ChimeraTK-ApplicationCore 04.06.00
Loading...
Searching...
No Matches
FanIn.h
Go to the documentation of this file.
1// SPDX-FileCopyrightText: Deutsches Elektronen-Synchrotron DESY, MSK, ChimeraTK Project <chimeratk-support@desy.de>
2// SPDX-License-Identifier: LGPL-3.0-or-later
3#pragma once
4
5#include "AccessorConcepts.h"
6#include "ArrayAccessor.h"
7#include "ScalarAccessor.h"
9#include "VariableGroup.h"
10
11#include <boost/range/join.hpp>
12
13#include <ranges>
14
15namespace ChimeraTK {
16
17 /********************************************************************************************************************/
18 /********************************************************************************************************************/
19
20 namespace detail::FanIn {
21 template<class AccessorType>
22 struct AccessorTypeHelper {};
23
24 template<scalar_accessor AccessorType>
25 struct AccessorTypeHelper<AccessorType> {
26 using type = ScalarRegisterAccessor<typename AccessorType::value_type>;
27 using out_type = ScalarOutput<typename AccessorType::value_type>;
28 using acc_type = ScalarAccessor<typename AccessorType::value_type>;
29 };
30 template<array_accessor AccessorType>
31 struct AccessorTypeHelper<AccessorType> {
32 using type = OneDRegisterAccessor<typename AccessorType::value_type>;
33 using out_type = ArrayOutput<typename AccessorType::value_type>;
34 using acc_type = ArrayAccessor<typename AccessorType::value_type>;
35 };
36
37 } // namespace detail::FanIn
38
39 /********************************************************************************************************************/
40 /********************************************************************************************************************/
41
55 template<push_input AccessorType>
56 class FanIn : public detail::FanIn::AccessorTypeHelper<AccessorType>::out_type {
57 public:
58 using AbstractorType = detail::FanIn::AccessorTypeHelper<AccessorType>::type;
59 using value_type = typename AccessorType::value_type;
61 std::function<value_type(TransferElementID, const std::map<TransferElementID, AbstractorType>&)>;
62 using out_type = detail::FanIn::AccessorTypeHelper<AccessorType>::out_type;
63
81 FanIn(VariableGroup* owner, std::string name, std::string unit, const std::string& description,
82 AggregatorType aggregator, const std::unordered_set<std::string>& tags = {});
83
97 FanIn(VariableGroup* owner, std::string name, std::initializer_list<std::string> additionalNames, std::string unit,
98 const std::string& description, AggregatorType aggregator, const std::unordered_set<std::string>& tags = {});
99
100 FanIn(FanIn&& other) noexcept { *this = std::move(other); }
101 FanIn& operator=(FanIn&& other) noexcept;
102 FanIn(const FanIn& other) = delete;
103 FanIn& operator=(const FanIn& other) = delete;
104 FanIn() = default;
105
109 const AbstractorType& input(const TransferElementID& id) const { return _inputs.get(id); }
110
114 bool hasInput(const TransferElementID& id) const { return _inputs.has(id); }
115
119 auto inputs() const;
120
124 auto inputs();
125
126 void replace(FanIn&& other) { *this = std::move(other); }
127
128 protected:
129 // Change read/write functions etc. to protected, because they are not for the user. Using them might be
130 // confusing since the FanIn is technically an output.
131 using out_type::write;
132 using out_type::writeDestructively;
133 using out_type::writeIfDifferent;
134 using out_type::setAndWrite;
135 using out_type::read;
136 using out_type::readNonBlocking;
137 using out_type::readLatest;
138 using out_type::isReadOnly;
139 using out_type::isReadable;
140 using out_type::isWriteable;
141
142 class Inputs : public VariableGroup {
143 public:
145
146 Inputs(VariableGroup* owner, FanIn& output, std::string name, auto additionalNames, std::string unit,
147 const std::string& description, FanIn::AggregatorType aggregator,
148 const std::unordered_set<std::string>& tags = {});
149
150 const AccessorType& get(const TransferElementID& id) const;
151
152 bool has(const TransferElementID& id) const;
153
154 protected:
155 void postConstruct() override;
156 void prepare() override;
157
159
160 void processUpdate(const TransferElementID& change, UpdateType type);
161
162 std::string _name;
163 std::string _unit;
164 std::vector<std::string> _additionalNames;
165
167
168 out_type* _output{nullptr};
169
170 std::vector<AccessorType> _inputs;
171 std::map<TransferElementID, AbstractorType> _abstractorMap;
172 std::map<TransferElementID, AccessorType*> _accessorMap;
173 TransferElementID _lastUpdate;
174 bool _hasValidator{false};
176
177 friend class FanIn;
178
184 template<user_type U>
185 class TrackingDecorator : public NDRegisterAccessorDecorator<U>, public UserInputValidator::AccessorHook {
186 public:
187 TrackingDecorator(const boost::shared_ptr<ChimeraTK::NDRegisterAccessor<U>>& target, FanIn::Inputs& fanIn)
188 : NDRegisterAccessorDecorator<U>(target), _fanIn(fanIn) {}
189
190 void doPostRead(TransferType type, bool updateDataBuffer) override;
191
192 void onReject() override;
193
194 void onAccept() override;
195
196 void onAddValidator(UserInputValidator&) override;
197
198 private:
199 Inputs& _fanIn;
200 };
201 };
202
204 };
205
206 /********************************************************************************************************************/
207
208 template<user_type UserType>
210
211 template<user_type UserType>
213
214 template<user_type UserType>
216
217 template<user_type UserType>
219
220 static constexpr auto fanInKeepLastValue = [](auto id, const auto& map) { return map.at(id); };
221
222 /********************************************************************************************************************/
223 /********************************************************************************************************************/
224
225 template<push_input AccessorType>
226 FanIn<AccessorType>::FanIn(VariableGroup* owner, std::string name, std::string unit, const std::string& description,
227 AggregatorType aggregator, const std::unordered_set<std::string>& tags)
228 : detail::FanIn::AccessorTypeHelper<AccessorType>::out_type(owner, name, unit, description, tags),
229 _inputs(owner, *this, name, std::span<std::string>{}, unit, description, aggregator, tags) {}
230
231 /********************************************************************************************************************/
232
233 template<push_input AccessorType>
234 FanIn<AccessorType>::FanIn(VariableGroup* owner, std::string name, std::initializer_list<std::string> additionalNames,
235 std::string unit, const std::string& description, AggregatorType aggregator,
236 const std::unordered_set<std::string>& tags)
237 : detail::FanIn::AccessorTypeHelper<AccessorType>::out_type(owner, name, unit, description, tags),
238 _inputs(owner, *this, name, additionalNames, unit, description, aggregator, tags) {}
239
240 /********************************************************************************************************************/
241
242 template<push_input AccessorType>
244 // we have to re-create the inputs VariableGroup so it is properly owned by the new owner
245 _inputs = Inputs{static_cast<VariableGroup*>(other.getOwner()), *this, other._inputs._name,
246 other._inputs._additionalNames, other._inputs._unit, "", other._inputs._aggregator};
247
249 std::move(other));
250 return *this;
251 }
252
253 /********************************************************************************************************************/
254
255 template<push_input AccessorType>
257 _inputs.prepare(); // make sure map is filled, noop if it already is
258 return _inputs._accessorMap | std::views::values |
259 std::views::transform([](const auto* p) -> const auto& { return *p; });
260 }
261
262 /********************************************************************************************************************/
263
264 template<push_input AccessorType>
266 _inputs.prepare(); // make sure map is filled, noop if it already is
267 return _inputs._accessorMap | std::views::values | std::views::transform([](auto* p) -> auto& { return *p; });
268 }
269
270 /********************************************************************************************************************/
271 /********************************************************************************************************************/
272
273 template<push_input AccessorType>
274 FanIn<AccessorType>::Inputs::Inputs(VariableGroup* owner, FanIn& output, std::string name, auto additionalNames,
275 std::string unit, const std::string& description, AggregatorType aggregator,
276 const std::unordered_set<std::string>& tags)
277 : VariableGroup(owner, ".", description, tags), _name(std::move(name)), _unit(std::move(unit)),
278 _additionalNames(additionalNames.begin(), additionalNames.end()), _aggregator(aggregator), _output(&output) {}
279
280 /********************************************************************************************************************/
281
282 template<push_input AccessorType>
284 assert(_output != nullptr);
285
286 std::list<std::string> inputNames;
287
288 auto nodes = _output->getModel().getNodes();
289 size_t index{0};
290
291 // add one input for each incoming connection
292 for(auto& node : nodes) {
293 if(node->getDirection().dir != VariableDirection::feeding) {
294 continue;
295 }
296 if(*node == VariableNetworkNode(*_output)) {
297 continue;
298 }
299 auto qualifiedName = node->getModel().getFullyQualifiedPath();
300 inputNames.emplace_back(qualifiedName + "/__FanInNode_" + std::to_string(index) + "__");
301 node->setMetaData(inputNames.back());
302 ++index;
303 }
304
305 for(auto& name : boost::join(inputNames, _additionalNames)) {
306 _inputs.emplace_back(this, name, "", "");
307 }
308 }
309
310 /********************************************************************************************************************/
311
312 template<push_input AccessorType>
314 if(!_accessorMap.empty()) {
315 // prepare() is also called by inputs() to make sure the map is already filled, e.g. when needed by the owning
316 // module's prepare() function, which might be called first.
317 return;
318 }
319
320 for(auto& input : _inputs) {
321 auto deco = boost::make_shared<TrackingDecorator<typename AccessorType::value_type>>(input.getImpl(), *this);
322 input.NDRegisterAccessorAbstractor<typename AccessorType::value_type>::replace(deco);
323
324 _accessorMap[input.getId()] = &input;
325 _abstractorMap[input.getId()].replace(input);
326 }
327 }
328
329 /********************************************************************************************************************/
330
331 template<push_input AccessorType>
332 const AccessorType& FanIn<AccessorType>::Inputs::get(const TransferElementID& id) const {
333 if(_accessorMap.empty()) {
334 throw ChimeraTK::logic_error("FanIn::get() called too early, prepare() has not yet been called.");
335 }
336
337 return *(_accessorMap.at(id));
338 }
339
340 /********************************************************************************************************************/
341
342 template<push_input AccessorType>
343 bool FanIn<AccessorType>::Inputs::has(const TransferElementID& id) const {
344 if(_accessorMap.empty()) {
345 throw ChimeraTK::logic_error("FanIn::get() called too early, prepare() has not yet been called.");
346 }
347
348 return _accessorMap.contains(id);
349 }
350
351 /********************************************************************************************************************/
352
353 template<push_input AccessorType>
354 void FanIn<AccessorType>::Inputs::processUpdate(const TransferElementID& change, UpdateType type) {
355 assert(_output != nullptr);
356
357 _lastUpdate = change;
358
359 // Only send initial value once all inputs have seen their initial value, so we send out only one single initial
360 // value with the aggregator having access to all initial values.
361 if(_output->getVersionNumber() == VersionNumber{nullptr}) {
362 for(const auto& [id, inp] : _abstractorMap) {
363 if(inp.getVersionNumber() == VersionNumber{nullptr}) {
364 return;
365 }
366 }
367 }
368
369 if(type != UpdateType::ACCEPT) {
370 *_output = _aggregator(change, _abstractorMap);
371 }
372
373 if(_output->getVersionNumber() == VersionNumber{nullptr} && _hasValidator && type != UpdateType::POST_READ) {
374 ++_nInitialValuesValidated;
375 if(_nInitialValuesValidated < _inputs.size()) {
376 return;
377 }
378 }
379
380 // If a UserInputValidator is added, delay writing the output until after the validation took place (see the hook
381 // functions onAccept() and onReject() of the TrackingDecorator).
382 if(!_hasValidator || type != UpdateType::POST_READ) {
383 _output->write();
384 }
385 }
386
387 /********************************************************************************************************************/
388 /********************************************************************************************************************/
389
390 template<push_input AccessorType>
391 template<user_type U>
392 void FanIn<AccessorType>::Inputs::TrackingDecorator<U>::doPostRead(TransferType type, bool updateDataBuffer) {
393 NDRegisterAccessorDecorator<U>::doPostRead(type, updateDataBuffer);
394 if(updateDataBuffer) {
395 _fanIn.processUpdate(this->getId(), UpdateType::POST_READ);
396 }
397 }
398
399 /********************************************************************************************************************/
400
401 template<push_input AccessorType>
402 template<user_type U>
404 assert(_fanIn._hasValidator);
405 _fanIn.processUpdate(this->getId(), UpdateType::REJECT);
406 }
407
408 /********************************************************************************************************************/
409
410 template<push_input AccessorType>
411 template<user_type U>
413 assert(_fanIn._hasValidator);
414 _fanIn.processUpdate(this->getId(), UpdateType::ACCEPT);
415 }
416 /********************************************************************************************************************/
417
418 template<push_input AccessorType>
419 template<user_type U>
423
424 /********************************************************************************************************************/
425
426} // namespace ChimeraTK
std::string _name
The name of this instance.
Helper decorator which keeps track of the last update received by the FanIn.
Definition FanIn.h:185
void onAddValidator(UserInputValidator &) override
Called when the accessor is added to the validator, i.e.
Definition FanIn.h:420
void doPostRead(TransferType type, bool updateDataBuffer) override
Definition FanIn.h:392
void onReject() override
Called when UserInputValidator::validate() (or validateAll()) rejects an incoming or initial value.
Definition FanIn.h:403
void onAccept() override
Called when UserInputValidator::validate() (or validateAll()) accepts an incoming or initial value,...
Definition FanIn.h:412
TrackingDecorator(const boost::shared_ptr< ChimeraTK::NDRegisterAccessor< U > > &target, FanIn::Inputs &fanIn)
Definition FanIn.h:187
std::vector< std::string > _additionalNames
Definition FanIn.h:164
void postConstruct() override
Hook function called on all Modules of an Application after the Application constructor is complete b...
Definition FanIn.h:283
bool has(const TransferElementID &id) const
Definition FanIn.h:343
const AccessorType & get(const TransferElementID &id) const
Definition FanIn.h:332
size_t _nInitialValuesValidated
Definition FanIn.h:175
void prepare() override
Prepare the execution of the module.
Definition FanIn.h:313
TransferElementID _lastUpdate
Definition FanIn.h:173
std::string _name
Definition FanIn.h:162
AggregatorType _aggregator
Definition FanIn.h:166
std::map< TransferElementID, AbstractorType > _abstractorMap
Definition FanIn.h:171
Inputs(VariableGroup *owner, FanIn &output, std::string name, auto additionalNames, std::string unit, const std::string &description, FanIn::AggregatorType aggregator, const std::unordered_set< std::string > &tags={})
Definition FanIn.h:274
std::string _unit
Definition FanIn.h:163
std::map< TransferElementID, AccessorType * > _accessorMap
Definition FanIn.h:172
std::vector< AccessorType > _inputs
Definition FanIn.h:170
void processUpdate(const TransferElementID &change, UpdateType type)
Definition FanIn.h:354
Special accessor allows multiple incoming connections to the same logical process variable.
Definition FanIn.h:56
bool hasInput(const TransferElementID &id) const
Check whether the given TransferElementID identifies an internal input.
Definition FanIn.h:114
detail::FanIn::AccessorTypeHelper< AccessorType >::out_type out_type
Definition FanIn.h:62
FanIn(FanIn &&other) noexcept
Definition FanIn.h:100
FanIn(VariableGroup *owner, std::string name, std::string unit, const std::string &description, AggregatorType aggregator, const std::unordered_set< std::string > &tags={})
Construct FanIn.
Definition FanIn.h:226
Inputs _inputs
Definition FanIn.h:203
FanIn(const FanIn &other)=delete
typename AccessorType::value_type value_type
Definition FanIn.h:59
const AbstractorType & input(const TransferElementID &id) const
Return the internal input accessor for the given TransferElementID.
Definition FanIn.h:109
auto inputs()
Return iterable range of all internal input accessors.
Definition FanIn.h:265
FanIn(VariableGroup *owner, std::string name, std::initializer_list< std::string > additionalNames, std::string unit, const std::string &description, AggregatorType aggregator, const std::unordered_set< std::string > &tags={})
Construct FanIn with additional inputs.
Definition FanIn.h:234
std::function< value_type(TransferElementID, const std::map< TransferElementID, AbstractorType > &)> AggregatorType
Definition FanIn.h:61
void replace(FanIn &&other)
Definition FanIn.h:126
FanIn & operator=(const FanIn &other)=delete
detail::FanIn::AccessorTypeHelper< AccessorType >::type AbstractorType
Definition FanIn.h:58
FanIn & operator=(FanIn &&other) noexcept
Definition FanIn.h:243
auto inputs() const
Return iterable range of all internal input accessors.
Definition FanIn.h:256
Adds features required for inversion of control to an accessor.
Accessors inheriting from this class (in addition to their accessor base class) can get informed abou...
VariableGroup()=default
Default constructor: Allows late initialisation of VariableGroups (e.g.
Class describing a node of a variable network.
InvalidityTracer application module.
Class to realise the validation of user input values.