18#include <ChimeraTK/NDRegisterAccessor.h>
19#include <ChimeraTK/SystemTags.h>
30 debug(
"Checking network \"" + proxy.
getName() +
"\" consistency");
35 net.useReverseRecovery = proxy.
getTags().contains(ChimeraTK::SystemTags::reverseRecovery);
37 if(net.useReverseRecovery) {
38 debug(
" Network has reverse recovery");
41 debug(
" Network does not have reverse recovery");
44 int bidirectionalDeviceNodeCount = 0;
45 std::vector<std::shared_ptr<VariableNetworkNode>> unidirectionalDeviceNodes;
47 for(
const auto& node : proxy.
getNodes()) {
48 if(node->getDirection().withReturn) {
49 net.numberOfBidirectionalNodes++;
54 auto nodeDump = ss.str();
57 nodeDump.erase(nodeDump.length() - 1);
58 debug(
" Feeder: ", nodeDump);
64 std::stringstream ss1;
66 std::stringstream ss2;
69 " has more than one feeder:\n" + ss1.str() + ss2.str());
74 throw ChimeraTK::logic_error(
"Feeding a constant is not allowed (" + node->getQualifiedName() +
")");
78 if(node->getDirection().withReturn) {
79 net.numberOfBidirectionalConsumers++;
83 auto consumerDump = ss.str();
84 consumerDump.erase(consumerDump.length() - 1);
85 debug(
" Consumer: ", consumerDump);
86 net.consumers.push_back(*node);
88 net.numberOfPollingConsumers++;
92 if(node->getDirection().withReturn) {
93 bidirectionalDeviceNodeCount++;
96 unidirectionalDeviceNodes.push_back(node);
105 if(*net.valueType ==
typeid(
AnyType)) {
106 net.valueType = &node->getValueType();
107 firstNodeWithType = *node;
110 if(*net.valueType != node->getValueType() && node->getValueType() !=
typeid(
AnyType)) {
111 std::stringstream ss1;
112 firstNodeWithType.
dump(ss1);
113 std::stringstream ss2;
116 " contains nodes with different types: " + boost::core::demangle(net.valueType->name()) +
117 " != " + boost::core::demangle(node->getValueType().name()) +
"\n" + ss1.str() + ss2.str());
121 if(net.valueLength == 0) {
122 net.valueLength = node->getNumberOfElements();
125 if(net.valueLength != node->getNumberOfElements() && node->getNumberOfElements() != 0) {
126 throw ChimeraTK::logic_error(
132 if(net.description.empty()) {
133 net.description = node->getDescription();
136 if(net.unit.empty()) {
137 net.unit = node->getUnit();
141 if(bidirectionalDeviceNodeCount == 0 && net.useReverseRecovery) {
142 debug(
" Network has no bidirectional device nodes but uses reverse recovery, flagging device nodes as having "
144 if(unidirectionalDeviceNodes.size() > 1) {
145 throw ChimeraTK::logic_error(
146 "Invalid network " + proxy.
getFullyQualifiedPath() +
", reverse recovery causes initial value conflict");
148 for(
const auto& node : unidirectionalDeviceNodes) {
151 net.numberOfBidirectionalNodes++;
152 net.numberOfBidirectionalConsumers++;
158 auto* owner =
dynamic_cast<Module*
>(net.feeder.getOwningModule());
159 assert(owner !=
nullptr);
160 auto* feederApplicationModule = owner->findApplicationModule();
161 for(
const auto& consumer : net.consumers) {
166 auto*
module = dynamic_cast<Module*>(consumer.getOwningModule());
167 assert(module !=
nullptr);
168 if(feederApplicationModule == module->findApplicationModule()) {
169 throw ChimeraTK::logic_error(
170 std::string(
"Network for ") + consumer.getQualifiedName() +
"feeds itself in the same module");
177 if(*net.valueType ==
typeid(
AnyType)) {
178 net.valueType = &
typeid(ChimeraTK::Void);
182 if(net.valueLength == 0 && *net.valueType !=
typeid(ChimeraTK::Void)) {
187 throw ChimeraTK::logic_error(
210 bool isConstant{!net.
consumers.empty() &&
220 RegisterPath name(net.
consumers.front().getName());
221 auto components = name.getComponents();
222 assert(components.size() == 4);
223 std::string stringValue = components[3];
225 callForType(net.
consumers.front().getValueType(), [&](
auto t) {
226 using UserType = decltype(t);
227 net.feeder.setConstantValue(userTypeToUserType<UserType>(Utilities::unescapeName(stringValue)));
231 bool neededFeeder{
false};
245 if(not neededFeeder and not isConstant) {
252 debug(
" Network has a non-CS feeder, can create additional ControlSystem consumer");
253 debug(
" with" + std::string(needReturn ?
"" :
"out") +
" return");
261 callForType(*net.
valueType, [&](
auto t) {
262 using UserType = decltype(t);
264 for(auto& node : net.consumers) {
265 if(node.getType() != NodeType::ControlSystem) {
268 this->createProcessVariable<UserType>(
269 node, net.valueLength, net.unit, net.description, {AccessMode::wait_for_new_data});
273 AccessModeFlags flags = {AccessMode::wait_for_new_data};
286 catch(std::bad_cast& e) {
287 std::cerr <<
"Illegal value type " + boost::core::demangle(net.valueType->name()) +
" of variable network: "
288 << net.proxy->getFullyQualifiedPath() << std::endl;
296 template<
typename... Args>
297 void NetworkVisitor::debug(Args&&... args) {
299 (
logger(Logger::Severity::debug,
"ConnectionMaker") << ... << args) << std::endl;
308 debug(
"Network found: ", path);
310 auto triggerFinder = [&](
auto p) {
311 auto deviceTrigger = p.getTrigger();
313 if(deviceTrigger.isValid()) {
314 debug(
" Found Feeding device ", p.getAliasOrCdd(),
" with trigger ", p.getTrigger().getFullyQualifiedPath());
317 debug(
" Feeding from device ", p.getAliasOrCdd(),
" but without any trigger");
320 return std::make_pair(deviceTrigger, p);
323 Model::ProcessVariableProxy trigger{};
324 Model::DeviceModuleProxy device{};
328 if(_networks.at(path).feeder.getMode() == UpdateMode::poll && _networks.at(path).numberOfPollingConsumers != 1) {
329 _networks.at(path).useExternalTrigger =
true;
330 std::tie(trigger, device) =
331 proxy.
visit(triggerFinder, Model::adjacentInSearch, Model::keepPvAccess, Model::keepDeviceModules,
332 Model::returnFirstHit(std::make_pair(Model::ProcessVariableProxy{}, Model::DeviceModuleProxy{})));
333 if(!trigger.isValid()) {
334 throw ChimeraTK::logic_error(
335 "Poll-Type feeder " + _networks.at(path).feeder.getName() +
" needs trigger, but none provided");
339 auto constantFeeder = _networks.at(path).feeder.getType() == NodeType::Constant;
341 if(_networks.at(path).feeder.hasImplementation() && !constantFeeder) {
342 debug(
" Creating fixed implementation for feeder '", _networks.at(path).feeder.getName(),
"'...");
344 if(_networks.at(path).consumers.size() == 1 && !_networks.at(path).useExternalTrigger) {
345 debug(
" One consumer without external trigger, creating direct connection");
346 makeDirectConnectionForFeederWithImplementation(_networks.at(path));
349 debug(std::format(
" More than one consuming node ({}) or having external trigger ({}), setting up FanOut",
350 _networks.at(path).consumers.size(), _networks.at(path).useExternalTrigger));
351 makeFanOutConnectionForFeederWithImplementation(_networks.at(path), device, trigger);
354 else if(not constantFeeder) {
355 debug(
" Feeder '", _networks.at(path).feeder.getName(),
"' does not require a fixed implementation.");
356 assert(not trigger.isValid());
357 makeConnectionForFeederWithoutImplementation(_networks.at(path));
360 debug(
" Using constant feeder '", _networks.at(path).feeder.getName(),
"'.");
361 makeConnectionForConstantFeeder(_networks.at(path));
365 for(
auto& node : _networks.at(path).consumers) {
368 auto circularNetwork = node.scanForCircularDepencency();
369 if(not circularNetwork.empty()) {
370 auto circularNetworkHash = boost::hash_range(circularNetwork.begin(), circularNetwork.end());
371 _app._circularDependencyNetworks[circularNetworkHash] = circularNetwork;
372 _app._circularNetworkInvalidityCounters[circularNetworkHash] = 0;
375 std::to_string(circularNetworkHash));
383 void ConnectionMaker::finalise() {
384 debug(
"Calling finalise()...");
386 debug(
"Preparing trigger networks");
387 debug(
"Collecting triggers");
390 std::list<Model::DeviceModuleProxy> dmProxyList;
391 auto triggerCollector = [&](
auto proxy) { dmProxyList.push_back(proxy); };
392 _app.getModel().visit(triggerCollector, Model::depthFirstSearch, Model::keepDeviceModules);
393 for(
auto& proxy : dmProxyList) {
394 auto trigger = proxy.getTrigger();
395 if(not trigger.isValid()) {
398 _triggers.insert(trigger);
400 proxy.addVariable(trigger, placeholder);
402 debug(
" Found " + std::to_string(_triggers.size()) +
" trigger(s)");
404 debug(
"---------------------------");
405 debug(
"Finalising trigger networks");
406 debug(
"---------------------------");
407 for(
auto trigger : _triggers) {
408 auto info = checkAndFinaliseNetwork(trigger);
409 _triggerNetworks.insert(trigger.getFullyQualifiedPath());
410 _networks.insert({trigger.getFullyQualifiedPath(), info});
411 debug(
" trigger network: " + trigger.getFullyQualifiedPath());
414 debug(
"-------------------------");
415 debug(
"Finalising other networks");
416 debug(
"-------------------------");
417 auto connectingVisitor = [&](
auto proxy) {
426 _app.getModel().visit(connectingVisitor, ChimeraTK::Model::depthFirstSearch, ChimeraTK::Model::keepProcessVariables,
427 ChimeraTK::Model::keepParenthood);
432 void ConnectionMaker::connect() {
433 debug(
"Calling connect()...");
438 debug(
"---------------------------");
439 debug(
"Connecting trigger networks");
440 debug(
"---------------------------");
441 for(
auto trigger : _triggers) {
442 connectNetwork(trigger);
445 debug(
"-------------------------");
446 debug(
"Connecting other networks");
447 debug(
"-------------------------");
448 auto connectingVisitor = [&](
auto proxy) {
453 connectNetwork(proxy);
457 _app.getModel().visit(connectingVisitor, ChimeraTK::Model::depthFirstSearch, ChimeraTK::Model::keepProcessVariables,
458 ChimeraTK::Model::keepParenthood);
463 void ConnectionMaker::makeDirectConnectionForFeederWithImplementation(NetworkInformation& net) {
464 debug(
" Making direct connection for feeder with implementation");
466 callForType(*net.valueType, [&](
auto t) {
467 using UserType = decltype(t);
469 auto consumer = net.consumers.front();
470 boost::shared_ptr<ChimeraTK::NDRegisterAccessor<UserType>> feedingImpl;
472 if(net.feeder.getType() == NodeType::Device) {
473 feedingImpl = createDeviceVariable<UserType>(net.feeder);
475 else if(net.feeder.getType() == NodeType::ControlSystem) {
476 feedingImpl = getProcessVariable<UserType>(net.feeder);
479 throw ChimeraTK::logic_error(
"Unexpected node type!");
484 auto needsFanOut{true};
485 boost::shared_ptr<ChimeraTK::NDRegisterAccessor<UserType>> consumingImpl;
487 switch(consumer.getType()) {
488 case NodeType::Application:
489 debug(
" Node type is Application");
490 consumer.setAppAccessorImplementation(feedingImpl);
504 case NodeType::ControlSystem:
505 debug(
" Node type is ControlSystem");
506 consumingImpl = getProcessVariable<UserType>(consumer);
508 case NodeType::Device:
509 consumingImpl = createDeviceVariable<UserType>(consumer);
510 debug(
" Node type is Device");
512 case NodeType::TriggerReceiver: {
514 debug(
" Node type is TriggerReceiver (Alias = " + consumer.getDeviceAlias() +
")");
518 boost::make_shared<TriggerFanOut>(feedingImpl, *_app.getDeviceManager(consumer.getDeviceAlias()));
519 _app._internalModuleList.push_back(triggerFanOut);
520 net.triggerImpl[consumer.getDeviceAlias()] = triggerFanOut;
523 throw ChimeraTK::logic_error(
"Unexpected node type!");
527 debug(
" needing an additional fan-out");
528 assert(consumingImpl !=
nullptr);
530 auto consumerImplPair = ConsumerImplementationPairs<UserType>{{consumingImpl, consumer}};
531 boost::shared_ptr<ThreadedFanOut<UserType>> threadedFanOut;
532 if(not net.feeder.getDirection().withReturn) {
533 debug(
" No return channel");
534 threadedFanOut = boost::make_shared<ThreadedFanOut<UserType>>(feedingImpl, consumerImplPair);
537 debug(
" With return channel");
538 threadedFanOut = boost::make_shared<ThreadedFanOutWithReturn<UserType>>(feedingImpl, consumerImplPair);
540 _app._internalModuleList.push_back(threadedFanOut);
547 void ConnectionMaker::makeFanOutConnectionForFeederWithImplementation(
548 NetworkInformation& net,
const Model::DeviceModuleProxy& device,
const Model::ProcessVariableProxy& trigger) {
550 auto feederTrigger = !net.useExternalTrigger && net.feeder.getMode() == UpdateMode::push;
551 assert(feederTrigger || net.useExternalTrigger || net.numberOfPollingConsumers == 1);
553 callForType(*net.valueType, [&](
auto t) {
554 using UserType = decltype(t);
556 boost::shared_ptr<ChimeraTK::NDRegisterAccessor<UserType>> feedingImpl;
557 if(net.feeder.getType() == NodeType::Device) {
558 debug(
" Device feeder, creating Device variable");
559 feedingImpl = createDeviceVariable<UserType>(net.feeder);
561 else if(net.feeder.getType() == NodeType::ControlSystem) {
562 debug(
" CS feeder, creating CS variable");
563 feedingImpl = getProcessVariable<UserType>(net.feeder);
566 throw ChimeraTK::logic_error(
"Unexpected node type!");
569 boost::shared_ptr<FanOut<UserType>> fanOut;
570 boost::shared_ptr<ConsumingFanOut<UserType>> consumingFanOut;
573 auto consumerImplementationPairs = setConsumerImplementations<UserType>(net);
575 if(net.useExternalTrigger) {
576 assert(trigger.isValid());
578 debug(
" Using external trigger (Alias = " + device.getAliasOrCdd() +
")");
580 auto& triggerNet = _networks.at(trigger.getFullyQualifiedPath());
581 auto jt = triggerNet.triggerImpl.find(device.getAliasOrCdd());
582 assert(jt != triggerNet.triggerImpl.end());
587 jt->second->addNetwork(feedingImpl, consumerImplementationPairs);
589 else if(feederTrigger) {
590 debug(
" Using feeder trigger.");
595 boost::shared_ptr<ThreadedFanOut<UserType>> threadedFanOut;
596 if(not net.feeder.getDirection().withReturn) {
597 debug(
" No return channel");
598 threadedFanOut = boost::make_shared<ThreadedFanOut<UserType>>(feedingImpl, consumerImplementationPairs);
601 debug(
" With return channel");
603 boost::make_shared<ThreadedFanOutWithReturn<UserType>>(feedingImpl, consumerImplementationPairs);
605 _app._internalModuleList.push_back(threadedFanOut);
606 fanOut = threadedFanOut;
610 debug(
" No trigger, using consuming fanout.");
611 consumingFanOut = boost::make_shared<ConsumingFanOut<UserType>>(feedingImpl, consumerImplementationPairs);
615 for(
const auto& consumer : net.consumers) {
616 if(consumer.getMode() == UpdateMode::poll) {
617 consumer.setAppAccessorImplementation<UserType>(consumingFanOut);
624 auto* ehd =
dynamic_cast<ExceptionHandlingDecorator<UserType>*
>(feedingImpl.get());
626 ehd->setSkipInitialValueWait(
true);
638 template<
typename UserType>
639 void NetworkVisitor::createProcessVariable(
const VariableNetworkNode& node,
size_t length,
const std::string& unit,
640 const std::string& description, AccessModeFlags flags) {
649 if(!_app.getPVManager()) {
653 SynchronizationDirection dir;
655 dir = SynchronizationDirection::bidirectional;
658 dir = SynchronizationDirection::controlSystemToDevice;
661 dir = SynchronizationDirection::deviceToControlSystem;
664 debug(
" calling createProcessArray()");
666 auto pv = _app.getPVManager()->createProcessArray<UserType>(
667 dir, node.
getPublicName(), length, unit, description, {}, 3, flags);
669 boost::shared_ptr<ChimeraTK::NDRegisterAccessor<UserType>> pvImpl = pv;
673 if(flags.has(AccessMode::wait_for_new_data)) {
674 auto varId = detail::TestableMode::getNextVariableId();
675 _app._pvIdMap[pv->getUniqueId()] = varId;
676 pvImpl = _app.getTestableMode().decorate<UserType>(
677 pvImpl, detail::TestableMode::DecoratorType::READ,
"ControlSystem:" + node.
getPublicName(), varId);
681 else if(dir == SynchronizationDirection::bidirectional) {
683 auto varId = detail::TestableMode::getNextVariableId();
684 _app._pvIdMap[pv->getUniqueId()] = varId;
685 pvImpl = _app.getTestableMode().decorate<UserType>(
686 pvImpl, detail::TestableMode::DecoratorType::READ,
"ControlSystem:" + node.
getPublicName());
689 boost::fusion::at_key<UserType>(_decoratedPvImpls.table)[node.
getPublicName()] = pvImpl;
694 template<
typename UserType>
695 boost::shared_ptr<ChimeraTK::NDRegisterAccessor<UserType>> ConnectionMaker::getProcessVariable(
697 return boost::fusion::at_key<UserType>(_decoratedPvImpls.table).at(node.
getPublicName());
701 template<
typename UserType>
702 boost::shared_ptr<NDRegisterAccessor<UserType>> ConnectionMaker::createDeviceVariable(
710 auto dev = _app._deviceManagerMap.at(deviceAlias)->getDevice().getBackend();
714 AccessModeFlags flags{};
715 if(mode == UpdateMode::push && direction.dir == VariableDirection::feeding) {
716 flags = {AccessMode::wait_for_new_data};
720 auto accessor = dev->getRegisterAccessor<UserType>(registerName, nElements, 0, flags);
725 accessor->setDataValidity(DataValidity::faulty);
729 if(mode == UpdateMode::push && direction.dir == VariableDirection::feeding) {
730 accessor = _app.getTestableMode().decorate(accessor, detail::TestableMode::DecoratorType::READ);
733 auto recoveryHelper = boost::make_shared<RecoveryHelper>();
736 accessor = boost::make_shared<ReverseRecoveryDecorator<UserType>>(accessor, recoveryHelper);
739 return boost::make_shared<ExceptionHandlingDecorator<UserType>>(accessor, node, recoveryHelper);
744 template<
typename UserType>
746 debug(
" setConsumerImplementations");
749 for(
const auto& consumer : net.consumers) {
751 boost::shared_ptr<ChimeraTK::NDRegisterAccessor<UserType>>(), consumer};
753 if(consumer.getType() == NodeType::Application) {
754 debug(
" Node type is Application: " + consumer.getQualifiedName());
755 auto impls = createApplicationVariable<UserType>(consumer);
756 consumer.setAppAccessorImplementation<UserType>(impls.second);
757 pair = std::make_pair(impls.first, consumer);
759 else if(consumer.getType() == NodeType::ControlSystem) {
760 debug(
" Node type is ControlSystem");
761 auto impl = getProcessVariable<UserType>(consumer);
762 pair = std::make_pair(impl, consumer);
764 else if(consumer.getType() == NodeType::Device) {
765 debug(
" Node type is Device");
766 auto impl = createDeviceVariable<UserType>(consumer);
767 pair = std::make_pair(impl, consumer);
769 else if(consumer.getType() == NodeType::TriggerReceiver) {
770 debug(
" Node type is TriggerReceiver");
771 auto triggerConnection = createApplicationVariable<UserType>(net.feeder);
773 auto triggerFanOut = boost::make_shared<TriggerFanOut>(
774 triggerConnection.second, *_app.getDeviceManager(consumer.getDeviceAlias()));
775 _app._internalModuleList.push_back(triggerFanOut);
776 net.triggerImpl[consumer.getDeviceAlias()] = triggerFanOut;
778 pair = std::make_pair(triggerConnection.first, consumer);
781 throw ChimeraTK::logic_error(
"Unexpected node type!");
784 consumerImplPairs.push_back(pair);
787 return consumerImplPairs;
792 template<
typename UserType>
793 std::pair<boost::shared_ptr<ChimeraTK::NDRegisterAccessor<UserType>>,
794 boost::shared_ptr<ChimeraTK::NDRegisterAccessor<UserType>>>
795 ConnectionMaker::createApplicationVariable(VariableNetworkNode
const& node, VariableNetworkNode
const& consumer) {
797 size_t nElements = node.getNumberOfElements();
798 std::string name = node.getName();
799 assert(not name.empty());
800 AccessModeFlags flags = {};
801 if(consumer.isValid()) {
802 if(consumer.getMode() == UpdateMode::push) {
803 flags = {AccessMode::wait_for_new_data};
807 if(node.getMode() == UpdateMode::push) {
808 flags = {AccessMode::wait_for_new_data};
813 std::pair<boost::shared_ptr<ChimeraTK::NDRegisterAccessor<UserType>>,
814 boost::shared_ptr<ChimeraTK::NDRegisterAccessor<UserType>>>
816 if(consumer.isValid()) {
817 assert(node.getDirection().withReturn == consumer.getDirection().withReturn);
820 if(!node.getDirection().withReturn) {
821 pvarPair = createSynchronizedProcessArray<UserType>(
822 nElements, name, node.getUnit(), node.getDescription(), {}, 3, flags);
825 pvarPair = createBidirectionalSynchronizedProcessArray<UserType>(
826 nElements, name, node.getUnit(), node.getDescription(), {}, 3, flags);
828 assert(pvarPair.first->getName() !=
"");
829 assert(pvarPair.second->getName() !=
"");
831 if(flags.has(AccessMode::wait_for_new_data)) {
832 pvarPair = _app.getTestableMode().decorate(pvarPair, node, consumer);
836 if(_app._debugMode_variableList.count(node.getUniqueId()) ||
837 (consumer.getType() != NodeType::invalid && _app._debugMode_variableList.count(consumer.getUniqueId()))) {
838 if(consumer.getType() != NodeType::invalid) {
839 assert(node.getDirection().dir == VariableDirection::feeding);
840 assert(consumer.getDirection().dir == VariableDirection::consuming);
842 boost::make_shared<DebugPrintAccessorDecorator<UserType>>(pvarPair.first, node.getQualifiedName());
844 boost::make_shared<DebugPrintAccessorDecorator<UserType>>(pvarPair.second, consumer.getQualifiedName());
848 boost::make_shared<DebugPrintAccessorDecorator<UserType>>(pvarPair.first, node.getQualifiedName());
850 boost::make_shared<DebugPrintAccessorDecorator<UserType>>(pvarPair.second, node.getQualifiedName());
860 void ChimeraTK::ConnectionMaker::makeConnectionForFeederWithoutImplementation(NetworkInformation& net) {
863 throw ChimeraTK::logic_error(
"Unexpected node type!");
866 if(net.consumers.size() == 1) {
867 debug(
" Network of two nodes, connect directly");
869 const auto& consumer = net.consumers.front();
871 switch(consumer.getType()) {
873 debug(
" Node type is Application");
874 callForType(*net.valueType, [&](
auto t) {
875 using UserType = decltype(t);
876 auto impls = createApplicationVariable<UserType>(net.feeder, consumer);
877 net.feeder.setAppAccessorImplementation<UserType>(impls.first);
878 consumer.setAppAccessorImplementation<UserType>(impls.second);
882 debug(
" Node type is ControlSystem");
883 callForType(*net.valueType, [&](
auto t) {
884 using UserType = decltype(t);
885 auto impl = getProcessVariable<UserType>(consumer);
886 net.feeder.setAppAccessorImplementation(impl);
890 debug(
" Node type is Device");
891 callForType(*net.valueType, [&](
auto t) {
892 using UserType = decltype(t);
893 auto impl = createDeviceVariable<UserType>(consumer);
894 net.feeder.setAppAccessorImplementation(impl);
898 debug(
" Node type is TriggerReceiver");
902 boost::shared_ptr<TransferElement> consumingImpl;
903 callForType(*net.valueType, [&](
auto t) {
904 using UserType = decltype(t);
905 auto impls = createApplicationVariable<UserType>(net.feeder, consumer);
906 net.feeder.setAppAccessorImplementation<UserType>(impls.first);
907 consumingImpl = impls.second;
912 boost::make_shared<TriggerFanOut>(consumingImpl, *
_app.
getDeviceManager(consumer.getDeviceAlias()));
914 net.triggerImpl[consumer.getDeviceAlias()] = triggerFanOut;
919 debug(
" Node type is Constant");
920 net.feeder.setAppAccessorConstImplementation(net.feeder);
923 throw ChimeraTK::logic_error(
"Unexpected node type!");
926 else if(net.consumers.size() > 1) {
927 debug(std::format(
" More than one consumer, using fan-out as feeder impl (with return: {})",
928 net.feeder.getDirection().withReturn));
929 callForType(*net.valueType, [&](
auto t) {
930 using UserType = decltype(t);
931 auto consumerImplementationPairs = setConsumerImplementations<UserType>(net);
934 auto fanOut = boost::make_shared<FeedingFanOut<UserType>>(net.feeder.getName(), net.unit, net.description,
935 net.valueLength, net.feeder.getDirection().withReturn, consumerImplementationPairs);
936 net.feeder.setAppAccessorImplementation<UserType>(fanOut);
940 debug(
" No consumer (presumably optimised out)");
941 net.feeder.setAppAccessorConstImplementation(VariableNetworkNode(net.valueType,
true, net.valueLength));
947 void ConnectionMaker::makeConnectionForConstantFeeder(NetworkInformation& net) {
949 for(
const auto& consumer : net.consumers) {
950 AccessModeFlags flags{};
952 flags = {AccessMode::wait_for_new_data};
955 callForType(*net.valueType, [&](
auto t) {
956 using UserType = decltype(t);
958 if(consumer.getType() == NodeType::Application) {
959 consumer.setAppAccessorConstImplementation(net.feeder);
962 throw ChimeraTK::logic_error(
"Using constants as feeders for control system variables is not supported!");
967 auto deviceManager = _app.getDeviceManager(consumer.getDeviceAlias());
968 auto dev = deviceManager->getDevice().getBackend();
970 dev->getRegisterAccessor<UserType>(consumer.getRegisterName(), consumer.getNumberOfElements(), 0, {});
971 auto catalog = deviceManager->getDevice().getRegisterCatalogue();
972 auto tags = catalog.getRegister(consumer.getRegisterName()).getTags();
975 impl->accessChannel(0) =
976 std::vector<UserType>(consumer.getNumberOfElements(), net.feeder.getConstantValue<UserType>());
984 if(!tags.contains(ChimeraTK::SystemTags::reverseRecovery)) {
985 deviceManager->addRecoveryAccessor(
986 boost::make_shared<RecoveryHelper>(impl, VersionNumber(), deviceManager->writeOrder()));
990 throw ChimeraTK::logic_error(
"Using constants as triggers is not supported!");
993 throw ChimeraTK::logic_error(
"Unexpected node type!");
1002 debug(
"-----------------------------");
1003 debug(
"Optimising unmapped variables");
1004 debug(
"-----------------------------");
1006 for(
const auto& name : names) {
1007 debug(
"Looking at network " + name);
1011 if(network.useReverseRecovery) {
1015 auto reverseConsumer = std::ranges::find_if(network.consumers, [](
auto& consumer) {
1016 return consumer.getType() == NodeType::Device &&
1017 consumer.getTags().contains(ChimeraTK::SystemTags::reverseRecovery);
1019 if(reverseConsumer->isReadable()) {
1020 debug(std::format(
" Promoting reverse consumer {} to feeder", reverseConsumer->getName()));
1021 network.feeder = *reverseConsumer;
1022 network.consumers.remove(*reverseConsumer);
1026 " Reverse consumer {} is not readable, adding constant feeder instead", reverseConsumer->getName()));
1031 debug(
" Adding constant feeder");
1037 debug(
" Dropping CS consumer");
Pseudo type to identify nodes which can have arbitrary types.
std::list< boost::shared_ptr< InternalModule > > _internalModuleList
List of InternalModules.
boost::shared_ptr< DeviceManager > getDeviceManager(const std::string &aliasOrCDD)
Return the DeviceManager for the given alias name or CDD.
void optimiseUnmappedVariables(const std::set< std::string > &names)
Execute the optimisation request from the control system adapter (remove unused variables)
static constexpr std::string_view namePrefixConstant
Prefix for constants created by constant().
Decorator of the NDRegisterAccessor which facilitates tests of the application.
void setSkipInitialValueWait(bool skip)
const std::vector< std::shared_ptr< VariableNetworkNode > > & getNodes() const
Return all VariableNetworkNodes for this variable.
const std::string & getName() const
Get the name of the ProcessVariable.
const std::unordered_set< std::string > & getTags() const
Return all tags attached to this variable.
auto visit(VISITOR visitor, Args... args) const
Traverse the model using the specified filter and call the visitor functor for each ModuleGroup,...
std::string getFullyQualifiedPath() const
Return the fully qualified path.
Base class for ApplicationModule and DeviceModule, to have a common interface for these module types.
NetworkInformation checkAndFinaliseNetwork(Model::ProcessVariableProxy &proxy)
void finaliseNetwork(NetworkInformation &net)
std::map< std::string, NetworkInformation > _networks
NetworkInformation checkNetwork(Model::ProcessVariableProxy &proxy)
Class describing a node of a variable network.
void dump(std::ostream &stream=std::cout) const
Print node information to specified stream.
NodeType getType() const
Getter for the properties.
const std::string & getRegisterName() const
const std::string & getPublicName() const
size_t getNumberOfElements() const
UpdateMode getMode() const
VariableDirection getDirection() const
const std::string & getDeviceAlias() const
InvalidityTracer application module.
constexpr char noInitialValueReadTag[]
System tag to mark an accessor which should be excluded from the initial value read during applicatio...
std::list< std::pair< boost::shared_ptr< ChimeraTK::NDRegisterAccessor< UserType > >, VariableNetworkNode > > ConsumerImplementationPairs
Logger::StreamProxy logger(Logger::Severity severity, std::string context)
Convenience function to obtain the logger stream.
Struct to define the direction of variables.
enum ChimeraTK::VariableDirection::@0 dir
Enum to define directions of variables.
bool withReturn
Presence of return channel.