ChimeraTK-ApplicationCore 04.06.00
Loading...
Searching...
No Matches
VariableNetworkNode.cc
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
4
5#include "Application.h"
6#include "ApplicationModule.h"
8#include "EntityOwner.h"
9#include "VariableGroup.h"
11#include "Visitor.h"
12
13#include <boost/container_hash/hash.hpp>
14
15#include <utility>
16
17namespace ChimeraTK {
18
19 /********************************************************************************************************************/
20
21 VariableNetworkNode::VariableNetworkNode(const VariableNetworkNode& other) = default;
22
23 /********************************************************************************************************************/
24
25 VariableNetworkNode& VariableNetworkNode::operator=(const VariableNetworkNode& rightHandSide) = default;
26
27 /********************************************************************************************************************/
28
29 VariableNetworkNode::VariableNetworkNode(EntityOwner* owner, ChimeraTK::TransferElementAbstractor* accessorBridge,
30 const std::string& name, VariableDirection direction, std::string unit, size_t nElements, UpdateMode mode,
31 const std::string& description, const std::type_info* valueType, const std::unordered_set<std::string>& tags)
32 : pdata(boost::make_shared<VariableNetworkNode_data>()) {
33 pdata->owningModule = owner;
35 pdata->appNode = accessorBridge;
36 pdata->name = name;
37 pdata->qualifiedName = owner->getQualifiedName() + "/" + name;
38 pdata->mode = mode;
39 pdata->direction = direction;
40 pdata->valueType = valueType;
41 pdata->unit = std::move(unit);
42 pdata->nElements = nElements;
43 pdata->description = description;
44 pdata->tags = tags;
45 }
46
47 /********************************************************************************************************************/
48
49 VariableNetworkNode::VariableNetworkNode(const std::string& name, const std::string& devAlias,
50 const std::string& regName, UpdateMode mode, VariableDirection dir, bool isReadable, const std::type_info& valTyp,
51 size_t nElements)
52 : pdata(boost::make_shared<VariableNetworkNode_data>()) {
53 pdata->name = name;
54 pdata->type = NodeType::Device;
55 pdata->mode = mode;
56 pdata->direction = dir;
57 pdata->valueType = &valTyp;
58 pdata->deviceAlias = devAlias;
59 pdata->registerName = regName;
60 pdata->nElements = nElements;
61 pdata->isReadable = isReadable;
62 }
63
64 /********************************************************************************************************************/
65
67 const std::string& pubName, VariableDirection dir, const std::type_info& valTyp, size_t nElements)
68 : pdata(boost::make_shared<VariableNetworkNode_data>()) {
69 pdata->name = pubName;
71 pdata->mode = UpdateMode::push;
72 pdata->direction = dir;
73 pdata->valueType = &valTyp;
74 pdata->publicName = pubName;
75 pdata->nElements = nElements;
76 }
77
78 /********************************************************************************************************************/
79
80 VariableNetworkNode::VariableNetworkNode(const std::string& deviceAliasOrCdd, int)
81 : pdata(boost::make_shared<VariableNetworkNode_data>()) {
83 pdata->mode = UpdateMode::push;
84 pdata->direction = {VariableDirection::consuming, false};
85 pdata->deviceAlias = deviceAliasOrCdd;
86 }
87
88 /********************************************************************************************************************/
89
90 VariableNetworkNode::VariableNetworkNode(const std::type_info* valTyp, bool makeFeeder, size_t length)
91 : pdata(boost::make_shared<VariableNetworkNode_data>()) {
92 pdata = boost::make_shared<VariableNetworkNode_data>();
94 pdata->valueType = valTyp;
95 pdata->nElements = length;
96 pdata->name = "*UNNAMED CONSTANT*";
97 if(makeFeeder) {
98 pdata->direction = {VariableDirection::feeding, false};
99 pdata->mode = UpdateMode::push;
100 }
101 else {
102 pdata->direction = {VariableDirection::consuming, false};
103 pdata->mode = UpdateMode::poll;
104 }
105 }
106
107 /********************************************************************************************************************/
108
109 VariableNetworkNode::VariableNetworkNode(boost::shared_ptr<VariableNetworkNode_data> _pdata)
110 : pdata(std::move(_pdata)) {}
111
112 /********************************************************************************************************************/
113
115
116 /********************************************************************************************************************/
117
119 return pdata->type == NodeType::Device || pdata->type == NodeType::ControlSystem ||
120 pdata->type == NodeType::Constant;
121 }
122
123 /********************************************************************************************************************/
124
126 visitor.dispatch(*this);
127 }
128
129 /********************************************************************************************************************/
130
132 return (other.pdata == pdata) || (pdata->type == NodeType::invalid && other.pdata->type == NodeType::invalid);
133 }
134
135 /********************************************************************************************************************/
136
138 return !operator==(other);
139 }
140
141 /********************************************************************************************************************/
142
144 if(pdata->type == NodeType::invalid && other.pdata->type == NodeType::invalid) {
145 return false;
146 }
147 return (other.pdata < pdata);
148 }
149
150 /********************************************************************************************************************/
151
152 void VariableNetworkNode::setValueType(const std::type_info& newType) const {
153 assert(*pdata->valueType == typeid(AnyType));
154 pdata->valueType = &newType;
155 }
156
157 /********************************************************************************************************************/
158
160 assert((pdata->type == NodeType::ControlSystem && pdata->direction.dir == VariableDirection::feeding) ||
161 (pdata->type == NodeType::Device && pdata->direction.dir == VariableDirection::consuming));
162 pdata->direction = newDirection;
163 }
164
165 /********************************************************************************************************************/
166
167 void VariableNetworkNode::dump(std::ostream& stream) const {
168 VariableNetworkNodeDumpingVisitor visitor(stream, " ");
169 visitor.dispatch(*this);
170 }
171
172 /********************************************************************************************************************/
173
175 if(!pdata) {
176 return NodeType::invalid;
177 }
178 return pdata->type;
179 }
180
181 /********************************************************************************************************************/
182
184 return pdata->mode;
185 }
186
187 /********************************************************************************************************************/
188
190 return pdata->direction;
191 }
192
193 /********************************************************************************************************************/
194
195 const std::type_info& VariableNetworkNode::getValueType() const {
196 return *(pdata->valueType);
197 }
198
199 /********************************************************************************************************************/
200
201 std::string VariableNetworkNode::getName() const {
202 return pdata->name;
203 }
204
205 /********************************************************************************************************************/
206
208 return pdata->qualifiedName;
209 }
210
211 /********************************************************************************************************************/
212
213 const std::string& VariableNetworkNode::getUnit() const {
214 return pdata->unit;
215 }
216
217 /********************************************************************************************************************/
218
219 const std::string& VariableNetworkNode::getDescription() const {
220 return pdata->description;
221 }
222
223 /********************************************************************************************************************/
224
226 assert(pdata->nodeToTrigger.getType() != NodeType::invalid);
227 return pdata->nodeToTrigger;
228 }
229
230 /********************************************************************************************************************/
231
232 const std::string& VariableNetworkNode::getPublicName() const {
233 assert(pdata->type == NodeType::ControlSystem);
234 return pdata->publicName;
235 }
236
237 /********************************************************************************************************************/
238
239 const std::string& VariableNetworkNode::getDeviceAlias() const {
240 assert(pdata->type == NodeType::Device || pdata->type == NodeType::TriggerReceiver);
241 return pdata->deviceAlias;
242 }
243
244 /********************************************************************************************************************/
245
246 const std::string& VariableNetworkNode::getRegisterName() const {
247 assert(pdata->type == NodeType::Device);
248 return pdata->registerName;
249 }
250
251 /********************************************************************************************************************/
252
253 void VariableNetworkNode::setNumberOfElements(size_t nElements) const {
254 pdata->nElements = nElements;
255 }
256
257 /********************************************************************************************************************/
258
260 return pdata->nElements;
261 }
262
263 /********************************************************************************************************************/
264
266 return pdata->isReadable;
267 }
268
269 /********************************************************************************************************************/
270
271 ChimeraTK::TransferElementAbstractor& VariableNetworkNode::getAppAccessorNoType() const {
272 return *(pdata->appNode);
273 }
274
275 /********************************************************************************************************************/
276
277 void VariableNetworkNode::setMetaData(const std::optional<std::string>& name, const std::optional<std::string>& unit,
278 const std::optional<std::string>& description, const std::optional<std::unordered_set<std::string>>& tags) {
280 throw ChimeraTK::logic_error("Calling VariableNetworkNode::updateMetaData() is not allowed for "
281 "non-application type nodes.");
282 }
283
284 bool needModelUpdate = getModel().isValid() && name.has_value();
285
286 if(needModelUpdate) {
287 getModel().removeNode(*this);
288 }
289
290 if(name.has_value()) {
291 pdata->name = name.value();
292 pdata->qualifiedName = pdata->owningModule->getQualifiedName() + "/" + name.value();
293 }
294 if(unit.has_value()) {
295 pdata->unit = unit.value();
296 }
297 if(description.has_value()) {
298 pdata->description = description.value();
299 }
300 if(tags.has_value()) {
301 pdata->tags = tags.value();
302 }
303
304 if(needModelUpdate) {
306 }
307 }
308
309 /********************************************************************************************************************/
310
311 void VariableNetworkNode::addTag(const std::string& tag) const {
312 if(pdata->tags.erase(negateTag(tag)) == 0) {
313 // negated tag was not found, so insert the tag
314 pdata->tags.insert(tag);
315 }
316 auto model = pdata->model.lock();
317 if(model.isValid()) {
318 model.addTag(tag);
319 }
320 }
321
322 /********************************************************************************************************************/
323
325 return pdata->circularNetworkHash != 0;
326 }
327
328 /********************************************************************************************************************/
329
330 std::list<EntityOwner*> VariableNetworkNode::scanForCircularDepencency() const {
331 // We are starting a new scan. Reset the indicator for already found circular dependencies.
332 detail::CircularDependencyDetectionRecursionStopper::startNewScan();
333
337 return {};
338 }
339
340 if(!getModel().isValid()) {
341 return {};
342 }
343
344 // find the feeder of the network
345 auto amProxy = getModel().visit(Model::returnApplicationModule, Model::keepPvAccess, Model::keepApplicationModules,
346 Model::adjacentInSearch, Model::returnFirstHit(Model::ApplicationModuleProxy{}));
347 // CS modules and device modules don't have an owning module. They stop the circle anyway. So if either the feeder
348 // or the receiver (this) don't have an owning module, there is nothing to do here.
349 if(!amProxy.isValid()) {
350 return {};
351 }
353
354 Module* owningModule = &amProxy.getApplicationModule();
355
356 // We do not put ourselves in the list right away. The called code will do this as well and detect a circle
357 // immediately, even if there is just a simple connection, we leave the marking of the already visited node
358 // to the recursive call.
359 auto inputModuleList = owningModule->getInputModulesRecursively({});
360
361 auto nInstancesFound = std::count(inputModuleList.begin(), inputModuleList.end(), owningModule);
362 assert(nInstancesFound >= 1); // the start list must not have been deleted in the call
363 // The owning module has been found again when scanning inputs recursively -> There is a circular dependency
364 if(nInstancesFound > 1) {
365 // clean up the circular network we found and return it.
366 inputModuleList.sort();
367 inputModuleList.unique();
368
369 // Remember that we are part of a circle, and of which circle
370 pdata->circularNetworkHash = boost::hash_range(inputModuleList.begin(), inputModuleList.end());
371 // we already did the assertion that the owning module is an application module above, so we can static cast here
372 auto* applicationModule = dynamic_cast<ApplicationModule*>(owningModule);
373 applicationModule->setCircularNetworkHash(pdata->circularNetworkHash);
374
375 // Find the MetaDataPropagatingRegisterDecorator which is involved and set the _isCirularInput flag
376 auto internalTargetElements = getAppAccessorNoType().getInternalElements();
377 // This is a list of all the nested decorators, so we will find the right point to cast
378 for(auto& elem : internalTargetElements) {
379 auto flagProvider = boost::dynamic_pointer_cast<MetaDataPropagationFlagProvider>(elem);
380 if(flagProvider) {
381 flagProvider->_isCircularInput = true;
382 }
383 }
384
385 return inputModuleList;
386 }
387
388 // No circular network. Return an empty list.
389 return {};
390 }
391
392 /********************************************************************************************************************/
393
394 const std::unordered_set<std::string>& VariableNetworkNode::getTags() const {
395 return pdata->tags;
396 }
397
398 /********************************************************************************************************************/
399
400 void VariableNetworkNode::setAppAccessorPointer(ChimeraTK::TransferElementAbstractor* accessor) const {
402 pdata->appNode = accessor;
403 }
404
405 /********************************************************************************************************************/
406
408 return pdata->owningModule;
409 }
410
411 /********************************************************************************************************************/
412
414 pdata->owningModule = newOwner;
415 }
416
417 /********************************************************************************************************************/
418
419 void VariableNetworkNode::setPublicName(const std::string& name) const {
420 pdata->publicName = name;
421 }
422
423 /********************************************************************************************************************/
424
426 return pdata->circularNetworkHash;
427 }
428
429 /********************************************************************************************************************/
430
432 return pdata->model.lock();
433 }
434
435 /********************************************************************************************************************/
436
438 pdata->model = model;
439 }
440
441 /********************************************************************************************************************/
442
444 callForType(getValueType(), [&](auto t) {
445 using UserType = decltype(t);
446 auto impl = boost::dynamic_pointer_cast<ChimeraTK::NDRegisterAccessor<UserType>>(
447 boost::make_shared<ConstantAccessor<UserType>>(feeder.getConstantValue<UserType>(), getNumberOfElements(),
448 getMode() == UpdateMode::push ? AccessModeFlags{AccessMode::wait_for_new_data} : AccessModeFlags{}));
449
451 setAppAccessorImplementation<UserType>(Application::getInstance().getTestableMode().decorate(
452 impl, detail::TestableMode::DecoratorType::READ, "Constant"));
453 }
454 else {
455 setAppAccessorImplementation<UserType>(impl);
456 }
457 });
458 }
459
460 /********************************************************************************************************************/
461
463 auto* owner = getOwningModule();
464 auto name = getName();
465
466 // Since we have to try out the possible owner types via dynamic_cast, the actual code is in this lambda:
467 auto addToOnwer = [&](auto& owner_casted) {
468 auto model = owner_casted.getModel();
469 if(!model.isValid()) {
470 // this happens e.g. for default-constructed owners and their sub-modules
471 return;
472 }
473 auto neighbourDir = model.visit(
474 Model::returnDirectory, Model::getNeighbourDirectory, Model::returnFirstHit(Model::DirectoryProxy{}));
475
476 auto dir = neighbourDir.addDirectoryRecursive(Utilities::getPathName(name));
477 auto var = dir.addVariable(Utilities::getUnqualifiedName(name));
478
479 model.addVariable(var, *this);
480 };
481
482 // Try for all possible module types
483 auto* owner_am = dynamic_cast<ApplicationModule*>(owner);
484 auto* owner_vg = dynamic_cast<VariableGroup*>(owner);
485 if(owner_am) {
486 addToOnwer(*owner_am);
487 }
488 else if(owner_vg) {
489 addToOnwer(*owner_vg);
490 }
491 else {
492 throw ChimeraTK::logic_error("Trying to add " + name + " to " + owner->getQualifiedName() +
493 " which is neither an ApplicationModule nor a VariableGroup, but a " +
494 boost::core::demangled_name(typeid(owner)));
495 }
496 }
497
498 /********************************************************************************************************************/
499
500} // namespace ChimeraTK
Pseudo type to identify nodes which can have arbitrary types.
static Application & getInstance()
Obtain instance of the application.
void setCircularNetworkHash(size_t circularNetworkHash)
Set the ID of the circular dependency network.
Base class for owners of other EntityOwners (e.g.
Definition EntityOwner.h:38
virtual std::string getQualifiedName() const =0
Get the fully qualified name of the module instance, i.e.
void removeNode(const VariableNetworkNode &node)
Remove VariableNetworkNode from the list of nodes. Note: Will invalidate return value of getNodes()!
Definition Model.cc:403
bool isValid() const
Check if the model is valid.
Definition Model.cc:31
auto visit(VISITOR visitor, Args... args) const
Traverse the model using the specified filter and call the visitor functor for each ModuleGroup,...
Definition Model.h:1451
Base class for ApplicationModule and DeviceModule, to have a common interface for these module types.
Definition Module.h:21
std::list< EntityOwner * > getInputModulesRecursively(std::list< EntityOwner * > startList) override
Use pointer to the module as unique identifier.
Definition Module.cc:241
The VariableNetworkNodeDumpingVisitor class.
void dispatch(const VariableNetworkNode &t) override
dispatch
Class describing a node of a variable network.
void dump(std::ostream &stream=std::cout) const
Print node information to specified stream.
void setValueType(const std::type_info &newType) const
Set the value type for this node.
boost::shared_ptr< VariableNetworkNode_data > pdata
ChimeraTK::TransferElementAbstractor & getAppAccessorNoType() const
NodeType getType() const
Getter for the properties.
const std::string & getRegisterName() const
const std::unordered_set< std::string > & getTags() const
Model::ProcessVariableProxy getModel() const
void setDirection(VariableDirection newDirection) const
Set the direction for this node.
const std::string & getPublicName() const
bool isCircularInput() const
Returns true if a circular dependency has been detected and the node is a consumer.
void setAppAccessorPointer(ChimeraTK::TransferElementAbstractor *accessor) const
Change pointer to the accessor.
const std::type_info & getValueType() const
bool operator<(const VariableNetworkNode &other) const
void setOwningModule(EntityOwner *newOwner) const
const std::string & getDescription() const
void setModel(const Model::ProcessVariableProxy &model) const
VariableNetworkNode & operator=(const VariableNetworkNode &rightHandSide)
Copy by assignment operator: Just copy the pointer to the data storage object.
void setNumberOfElements(size_t nElements) const
bool operator==(const VariableNetworkNode &other) const
Compare two nodes.
bool hasImplementation() const
Function checking if the node requires a fixed implementation.
VariableNetworkNode()
Default constructor for an invalid node.
size_t getCircularNetworkHash() const
Get the unique ID of the circular network.
void setPublicName(const std::string &name) const
VariableDirection getDirection() const
void setAppAccessorConstImplementation(const VariableNetworkNode &feeder) const
bool operator!=(const VariableNetworkNode &other) const
std::list< EntityOwner * > scanForCircularDepencency() const
Scan the networks and set the isCircularInput() flags if circular dependencies are detected.
void accept(Visitor< VariableNetworkNode > &visitor) const
void addTag(const std::string &tag) const
Add a tag.
VariableNetworkNode getNodeToTrigger() const
const std::string & getDeviceAlias() const
const std::string & getUnit() const
void setMetaData(const std::optional< std::string > &name, const std::optional< std::string > &unit={}, const std::optional< std::string > &description={}, const std::optional< std::unordered_set< std::string > > &tags={})
Change meta data (name, unit, description and optionally tags).
constexpr ReturnFirstHitWithValue< void > returnFirstHit()
Stop the search after the first hit and return.
Definition Model.h:790
std::string getPathName(const std::string &qualifiedName)
Return all but the last components of the given qualified name.
Definition Utilities.cc:29
std::string getUnqualifiedName(const std::string &qualifiedName)
Return the last component of the given qualified path name.
Definition Utilities.cc:19
InvalidityTracer application module.
std::string negateTag(const std::string &tag)
negate tag using prefix '!'
UpdateMode
Enum to define the update mode of variables.
Definition Flags.h:31
NodeType
Enum to define types of VariableNetworkNode.
Definition Flags.h:36
Struct to define the direction of variables.
Definition Flags.h:13
We use a pimpl pattern so copied instances of VariableNetworkNode refer to the same instance of the d...