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) {
298 if(not _debugConnections) {
303 (
logger(Logger::Severity::debug,
"ConnectionMaker") << ... << args) << std::endl;
312 debug(
"Network found: ", path);
314 auto triggerFinder = [&](
auto p) {
315 auto deviceTrigger = p.getTrigger();
317 if(deviceTrigger.isValid()) {
318 debug(
" Found Feeding device ", p.getAliasOrCdd(),
" with trigger ", p.getTrigger().getFullyQualifiedPath());
321 debug(
" Feeding from device ", p.getAliasOrCdd(),
" but without any trigger");
324 return std::make_pair(deviceTrigger, p);
327 Model::ProcessVariableProxy trigger{};
328 Model::DeviceModuleProxy device{};
332 if(_networks.at(path).feeder.getMode() == UpdateMode::poll && _networks.at(path).numberOfPollingConsumers != 1) {
333 _networks.at(path).useExternalTrigger =
true;
334 std::tie(trigger, device) =
335 proxy.
visit(triggerFinder, Model::adjacentInSearch, Model::keepPvAccess, Model::keepDeviceModules,
336 Model::returnFirstHit(std::make_pair(Model::ProcessVariableProxy{}, Model::DeviceModuleProxy{})));
337 if(!trigger.isValid()) {
338 throw ChimeraTK::logic_error(
339 "Poll-Type feeder " + _networks.at(path).feeder.getName() +
" needs trigger, but none provided");
343 auto constantFeeder = _networks.at(path).feeder.getType() == NodeType::Constant;
345 if(_networks.at(path).feeder.hasImplementation() && !constantFeeder) {
346 debug(
" Creating fixed implementation for feeder '", _networks.at(path).feeder.getName(),
"'...");
348 if(_networks.at(path).consumers.size() == 1 && !_networks.at(path).useExternalTrigger) {
349 debug(
" One consumer without external trigger, creating direct connection");
350 makeDirectConnectionForFeederWithImplementation(_networks.at(path));
353 debug(std::format(
" More than one consuming node ({}) or having external trigger ({}), setting up FanOut",
354 _networks.at(path).consumers.size(), _networks.at(path).useExternalTrigger));
355 makeFanOutConnectionForFeederWithImplementation(_networks.at(path), device, trigger);
358 else if(not constantFeeder) {
359 debug(
" Feeder '", _networks.at(path).feeder.getName(),
"' does not require a fixed implementation.");
360 assert(not trigger.isValid());
361 makeConnectionForFeederWithoutImplementation(_networks.at(path));
364 debug(
" Using constant feeder '", _networks.at(path).feeder.getName(),
"'.");
365 makeConnectionForConstantFeeder(_networks.at(path));
369 for(
auto& node : _networks.at(path).consumers) {
372 auto circularNetwork = node.scanForCircularDepencency();
373 if(not circularNetwork.empty()) {
374 auto circularNetworkHash = boost::hash_range(circularNetwork.begin(), circularNetwork.end());
375 _app._circularDependencyNetworks[circularNetworkHash] = circularNetwork;
376 _app._circularNetworkInvalidityCounters[circularNetworkHash] = 0;
379 std::to_string(circularNetworkHash));
387 void ConnectionMaker::finalise() {
388 debug(
"Calling finalise()...");
390 _app.getTestableMode()._debugDecorating = _debugConnections;
392 debug(
"Preparing trigger networks");
393 debug(
"Collecting triggers");
396 std::list<Model::DeviceModuleProxy> dmProxyList;
397 auto triggerCollector = [&](
auto proxy) { dmProxyList.push_back(proxy); };
398 _app.getModel().visit(triggerCollector, Model::depthFirstSearch, Model::keepDeviceModules);
399 for(
auto& proxy : dmProxyList) {
400 auto trigger = proxy.getTrigger();
401 if(not trigger.isValid()) {
404 _triggers.insert(trigger);
406 proxy.addVariable(trigger, placeholder);
408 debug(
" Found " + std::to_string(_triggers.size()) +
" trigger(s)");
410 debug(
"---------------------------");
411 debug(
"Finalising trigger networks");
412 debug(
"---------------------------");
413 for(
auto trigger : _triggers) {
414 auto info = checkAndFinaliseNetwork(trigger);
415 _triggerNetworks.insert(trigger.getFullyQualifiedPath());
416 _networks.insert({trigger.getFullyQualifiedPath(), info});
417 debug(
" trigger network: " + trigger.getFullyQualifiedPath());
420 debug(
"-------------------------");
421 debug(
"Finalising other networks");
422 debug(
"-------------------------");
423 auto connectingVisitor = [&](
auto proxy) {
432 _app.getModel().visit(connectingVisitor, ChimeraTK::Model::depthFirstSearch, ChimeraTK::Model::keepProcessVariables,
433 ChimeraTK::Model::keepParenthood);
438 void ConnectionMaker::connect() {
439 debug(
"Calling connect()...");
441 _app.getTestableMode()._debugDecorating = _debugConnections;
446 debug(
"---------------------------");
447 debug(
"Connecting trigger networks");
448 debug(
"---------------------------");
449 for(
auto trigger : _triggers) {
450 connectNetwork(trigger);
453 debug(
"-------------------------");
454 debug(
"Connecting other networks");
455 debug(
"-------------------------");
456 auto connectingVisitor = [&](
auto proxy) {
461 connectNetwork(proxy);
465 _app.getModel().visit(connectingVisitor, ChimeraTK::Model::depthFirstSearch, ChimeraTK::Model::keepProcessVariables,
466 ChimeraTK::Model::keepParenthood);
471 void ConnectionMaker::makeDirectConnectionForFeederWithImplementation(NetworkInformation& net) {
472 debug(
" Making direct connection for feeder with implementation");
474 callForType(*net.valueType, [&](
auto t) {
475 using UserType = decltype(t);
477 auto consumer = net.consumers.front();
478 boost::shared_ptr<ChimeraTK::NDRegisterAccessor<UserType>> feedingImpl;
480 if(net.feeder.getType() == NodeType::Device) {
481 feedingImpl = createDeviceVariable<UserType>(net.feeder);
483 else if(net.feeder.getType() == NodeType::ControlSystem) {
484 feedingImpl = getProcessVariable<UserType>(net.feeder);
487 throw ChimeraTK::logic_error(
"Unexpected node type!");
492 auto needsFanOut{true};
493 boost::shared_ptr<ChimeraTK::NDRegisterAccessor<UserType>> consumingImpl;
495 switch(consumer.getType()) {
496 case NodeType::Application:
497 debug(
" Node type is Application");
498 consumer.setAppAccessorImplementation(feedingImpl);
501 case NodeType::ControlSystem:
502 debug(
" Node type is ControlSystem");
503 consumingImpl = getProcessVariable<UserType>(consumer);
505 case NodeType::Device:
506 consumingImpl = createDeviceVariable<UserType>(consumer);
507 debug(
" Node type is Device");
509 case NodeType::TriggerReceiver: {
511 debug(
" Node type is TriggerReceiver (Alias = " + consumer.getDeviceAlias() +
")");
515 boost::make_shared<TriggerFanOut>(feedingImpl, *_app.getDeviceManager(consumer.getDeviceAlias()));
516 _app._internalModuleList.push_back(triggerFanOut);
517 net.triggerImpl[consumer.getDeviceAlias()] = triggerFanOut;
520 throw ChimeraTK::logic_error(
"Unexpected node type!");
524 debug(
" needing an additional fan-out");
525 assert(consumingImpl !=
nullptr);
527 auto consumerImplPair = ConsumerImplementationPairs<UserType>{{consumingImpl, consumer}};
528 boost::shared_ptr<ThreadedFanOut<UserType>> threadedFanOut;
529 if(not net.feeder.getDirection().withReturn) {
530 debug(
" No return channel");
531 threadedFanOut = boost::make_shared<ThreadedFanOut<UserType>>(feedingImpl, consumerImplPair);
534 debug(
" With return channel");
535 threadedFanOut = boost::make_shared<ThreadedFanOutWithReturn<UserType>>(feedingImpl, consumerImplPair);
537 _app._internalModuleList.push_back(threadedFanOut);
544 void ConnectionMaker::makeFanOutConnectionForFeederWithImplementation(
545 NetworkInformation& net,
const Model::DeviceModuleProxy& device,
const Model::ProcessVariableProxy& trigger) {
547 auto feederTrigger = !net.useExternalTrigger && net.feeder.getMode() == UpdateMode::push;
548 assert(feederTrigger || net.useExternalTrigger || net.numberOfPollingConsumers == 1);
550 callForType(*net.valueType, [&](
auto t) {
551 using UserType = decltype(t);
553 boost::shared_ptr<ChimeraTK::NDRegisterAccessor<UserType>> feedingImpl;
554 if(net.feeder.getType() == NodeType::Device) {
555 debug(
" Device feeder, creating Device variable");
556 feedingImpl = createDeviceVariable<UserType>(net.feeder);
558 else if(net.feeder.getType() == NodeType::ControlSystem) {
559 debug(
" CS feeder, creating CS variable");
560 feedingImpl = getProcessVariable<UserType>(net.feeder);
563 throw ChimeraTK::logic_error(
"Unexpected node type!");
566 boost::shared_ptr<FanOut<UserType>> fanOut;
567 boost::shared_ptr<ConsumingFanOut<UserType>> consumingFanOut;
570 auto consumerImplementationPairs = setConsumerImplementations<UserType>(net);
572 if(net.useExternalTrigger) {
573 assert(trigger.isValid());
575 debug(
" Using external trigger (Alias = " + device.getAliasOrCdd() +
")");
577 auto& triggerNet = _networks.at(trigger.getFullyQualifiedPath());
578 auto jt = triggerNet.triggerImpl.find(device.getAliasOrCdd());
579 assert(jt != triggerNet.triggerImpl.end());
584 jt->second->addNetwork(feedingImpl, consumerImplementationPairs);
586 else if(feederTrigger) {
587 debug(
" Using feeder trigger.");
592 boost::shared_ptr<ThreadedFanOut<UserType>> threadedFanOut;
593 if(not net.feeder.getDirection().withReturn) {
594 debug(
" No return channel");
595 threadedFanOut = boost::make_shared<ThreadedFanOut<UserType>>(feedingImpl, consumerImplementationPairs);
598 debug(
" With return channel");
600 boost::make_shared<ThreadedFanOutWithReturn<UserType>>(feedingImpl, consumerImplementationPairs);
602 _app._internalModuleList.push_back(threadedFanOut);
603 fanOut = threadedFanOut;
607 debug(
" No trigger, using consuming fanout.");
608 consumingFanOut = boost::make_shared<ConsumingFanOut<UserType>>(feedingImpl, consumerImplementationPairs);
612 for(
const auto& consumer : net.consumers) {
613 if(consumer.getMode() == UpdateMode::poll) {
614 consumer.setAppAccessorImplementation<UserType>(consumingFanOut);
624 template<
typename UserType>
625 void NetworkVisitor::createProcessVariable(
const VariableNetworkNode& node,
size_t length,
const std::string& unit,
626 const std::string& description, AccessModeFlags flags) {
635 if(!_app.getPVManager()) {
639 SynchronizationDirection dir;
641 dir = SynchronizationDirection::bidirectional;
644 dir = SynchronizationDirection::controlSystemToDevice;
647 dir = SynchronizationDirection::deviceToControlSystem;
650 debug(
" calling createProcessArray()");
652 auto pv = _app.getPVManager()->createProcessArray<UserType>(
653 dir, node.
getPublicName(), length, unit, description, {}, 3, flags);
655 boost::shared_ptr<ChimeraTK::NDRegisterAccessor<UserType>> pvImpl = pv;
659 if(flags.has(AccessMode::wait_for_new_data)) {
660 auto varId = detail::TestableMode::getNextVariableId();
661 _app._pvIdMap[pv->getUniqueId()] = varId;
662 pvImpl = _app.getTestableMode().decorate<UserType>(
663 pvImpl, detail::TestableMode::DecoratorType::READ,
"ControlSystem:" + node.
getPublicName(), varId);
667 else if(dir == SynchronizationDirection::bidirectional) {
669 auto varId = detail::TestableMode::getNextVariableId();
670 _app._pvIdMap[pv->getUniqueId()] = varId;
671 pvImpl = _app.getTestableMode().decorate<UserType>(
672 pvImpl, detail::TestableMode::DecoratorType::READ,
"ControlSystem:" + node.
getPublicName());
675 boost::fusion::at_key<UserType>(_decoratedPvImpls.table)[node.
getPublicName()] = pvImpl;
680 template<
typename UserType>
681 boost::shared_ptr<ChimeraTK::NDRegisterAccessor<UserType>> ConnectionMaker::getProcessVariable(
683 return boost::fusion::at_key<UserType>(_decoratedPvImpls.table).at(node.
getPublicName());
687 template<
typename UserType>
688 boost::shared_ptr<NDRegisterAccessor<UserType>> ConnectionMaker::createDeviceVariable(
696 auto dev = _app._deviceManagerMap.at(deviceAlias)->getDevice().getBackend();
700 AccessModeFlags flags{};
701 if(mode == UpdateMode::push && direction.dir == VariableDirection::feeding) {
702 flags = {AccessMode::wait_for_new_data};
706 auto accessor = dev->getRegisterAccessor<UserType>(registerName, nElements, 0, flags);
711 accessor->setDataValidity(DataValidity::faulty);
715 if(mode == UpdateMode::push && direction.dir == VariableDirection::feeding) {
716 accessor = _app.getTestableMode().decorate(accessor, detail::TestableMode::DecoratorType::READ);
719 auto recoveryHelper = boost::make_shared<RecoveryHelper>();
722 accessor = boost::make_shared<ReverseRecoveryDecorator<UserType>>(accessor, recoveryHelper);
725 return boost::make_shared<ExceptionHandlingDecorator<UserType>>(accessor, node, recoveryHelper);
730 template<
typename UserType>
732 debug(
" setConsumerImplementations");
735 for(
const auto& consumer : net.consumers) {
737 boost::shared_ptr<ChimeraTK::NDRegisterAccessor<UserType>>(), consumer};
739 if(consumer.getType() == NodeType::Application) {
740 debug(
" Node type is Application: " + consumer.getQualifiedName());
741 auto impls = createApplicationVariable<UserType>(consumer);
742 consumer.setAppAccessorImplementation<UserType>(impls.second);
743 pair = std::make_pair(impls.first, consumer);
745 else if(consumer.getType() == NodeType::ControlSystem) {
746 debug(
" Node type is ControlSystem");
747 auto impl = getProcessVariable<UserType>(consumer);
748 pair = std::make_pair(impl, consumer);
750 else if(consumer.getType() == NodeType::Device) {
751 debug(
" Node type is Device");
752 auto impl = createDeviceVariable<UserType>(consumer);
753 pair = std::make_pair(impl, consumer);
755 else if(consumer.getType() == NodeType::TriggerReceiver) {
756 debug(
" Node type is TriggerReceiver");
757 auto triggerConnection = createApplicationVariable<UserType>(net.feeder);
759 auto triggerFanOut = boost::make_shared<TriggerFanOut>(
760 triggerConnection.second, *_app.getDeviceManager(consumer.getDeviceAlias()));
761 _app._internalModuleList.push_back(triggerFanOut);
762 net.triggerImpl[consumer.getDeviceAlias()] = triggerFanOut;
764 pair = std::make_pair(triggerConnection.first, consumer);
767 throw ChimeraTK::logic_error(
"Unexpected node type!");
770 consumerImplPairs.push_back(pair);
773 return consumerImplPairs;
778 template<
typename UserType>
779 std::pair<boost::shared_ptr<ChimeraTK::NDRegisterAccessor<UserType>>,
780 boost::shared_ptr<ChimeraTK::NDRegisterAccessor<UserType>>>
781 ConnectionMaker::createApplicationVariable(VariableNetworkNode
const& node, VariableNetworkNode
const& consumer) {
783 size_t nElements = node.getNumberOfElements();
784 std::string name = node.getName();
785 assert(not name.empty());
786 AccessModeFlags flags = {};
787 if(consumer.isValid()) {
788 if(consumer.getMode() == UpdateMode::push) {
789 flags = {AccessMode::wait_for_new_data};
793 if(node.getMode() == UpdateMode::push) {
794 flags = {AccessMode::wait_for_new_data};
799 std::pair<boost::shared_ptr<ChimeraTK::NDRegisterAccessor<UserType>>,
800 boost::shared_ptr<ChimeraTK::NDRegisterAccessor<UserType>>>
802 if(consumer.isValid()) {
803 assert(node.getDirection().withReturn == consumer.getDirection().withReturn);
806 if(!node.getDirection().withReturn) {
807 pvarPair = createSynchronizedProcessArray<UserType>(
808 nElements, name, node.getUnit(), node.getDescription(), {}, 3, flags);
811 pvarPair = createBidirectionalSynchronizedProcessArray<UserType>(
812 nElements, name, node.getUnit(), node.getDescription(), {}, 3, flags);
814 assert(pvarPair.first->getName() !=
"");
815 assert(pvarPair.second->getName() !=
"");
817 if(flags.has(AccessMode::wait_for_new_data)) {
818 pvarPair = _app.getTestableMode().decorate(pvarPair, node, consumer);
822 if(_app._debugMode_variableList.count(node.getUniqueId()) ||
823 (consumer.getType() != NodeType::invalid && _app._debugMode_variableList.count(consumer.getUniqueId()))) {
824 if(consumer.getType() != NodeType::invalid) {
825 assert(node.getDirection().dir == VariableDirection::feeding);
826 assert(consumer.getDirection().dir == VariableDirection::consuming);
828 boost::make_shared<DebugPrintAccessorDecorator<UserType>>(pvarPair.first, node.getQualifiedName());
830 boost::make_shared<DebugPrintAccessorDecorator<UserType>>(pvarPair.second, consumer.getQualifiedName());
834 boost::make_shared<DebugPrintAccessorDecorator<UserType>>(pvarPair.first, node.getQualifiedName());
836 boost::make_shared<DebugPrintAccessorDecorator<UserType>>(pvarPair.second, node.getQualifiedName());
846 void ChimeraTK::ConnectionMaker::makeConnectionForFeederWithoutImplementation(NetworkInformation& net) {
849 throw ChimeraTK::logic_error(
"Unexpected node type!");
852 if(net.consumers.size() == 1) {
853 debug(
" Network of two nodes, connect directly");
855 const auto& consumer = net.consumers.front();
857 switch(consumer.getType()) {
859 debug(
" Node type is Application");
860 callForType(*net.valueType, [&](
auto t) {
861 using UserType = decltype(t);
862 auto impls = createApplicationVariable<UserType>(net.feeder, consumer);
863 net.feeder.setAppAccessorImplementation<UserType>(impls.first);
864 consumer.setAppAccessorImplementation<UserType>(impls.second);
868 debug(
" Node type is ControlSystem");
869 callForType(*net.valueType, [&](
auto t) {
870 using UserType = decltype(t);
871 auto impl = getProcessVariable<UserType>(consumer);
872 net.feeder.setAppAccessorImplementation(impl);
876 debug(
" Node type is Device");
877 callForType(*net.valueType, [&](
auto t) {
878 using UserType = decltype(t);
879 auto impl = createDeviceVariable<UserType>(consumer);
880 net.feeder.setAppAccessorImplementation(impl);
884 debug(
" Node type is TriggerReceiver");
888 boost::shared_ptr<TransferElement> consumingImpl;
889 callForType(*net.valueType, [&](
auto t) {
890 using UserType = decltype(t);
891 auto impls = createApplicationVariable<UserType>(net.feeder, consumer);
892 net.feeder.setAppAccessorImplementation<UserType>(impls.first);
893 consumingImpl = impls.second;
898 boost::make_shared<TriggerFanOut>(consumingImpl, *
_app.
getDeviceManager(consumer.getDeviceAlias()));
900 net.triggerImpl[consumer.getDeviceAlias()] = triggerFanOut;
905 debug(
" Node type is Constant");
906 net.feeder.setAppAccessorConstImplementation(net.feeder);
909 throw ChimeraTK::logic_error(
"Unexpected node type!");
912 else if(net.consumers.size() > 1) {
913 debug(std::format(
" More than one consumer, using fan-out as feeder impl (with return: {})",
914 net.feeder.getDirection().withReturn));
915 callForType(*net.valueType, [&](
auto t) {
916 using UserType = decltype(t);
917 auto consumerImplementationPairs = setConsumerImplementations<UserType>(net);
920 auto fanOut = boost::make_shared<FeedingFanOut<UserType>>(net.feeder.getName(), net.unit, net.description,
921 net.valueLength, net.feeder.getDirection().withReturn, consumerImplementationPairs);
922 net.feeder.setAppAccessorImplementation<UserType>(fanOut);
926 debug(
" No consumer (presumably optimised out)");
927 net.feeder.setAppAccessorConstImplementation(VariableNetworkNode(net.valueType,
true, net.valueLength));
933 void ConnectionMaker::makeConnectionForConstantFeeder(NetworkInformation& net) {
935 for(
const auto& consumer : net.consumers) {
936 AccessModeFlags flags{};
938 flags = {AccessMode::wait_for_new_data};
941 callForType(*net.valueType, [&](
auto t) {
942 using UserType = decltype(t);
944 if(consumer.getType() == NodeType::Application) {
945 consumer.setAppAccessorConstImplementation(net.feeder);
948 throw ChimeraTK::logic_error(
"Using constants as feeders for control system variables is not supported!");
953 auto deviceManager = _app.getDeviceManager(consumer.getDeviceAlias());
954 auto dev = deviceManager->getDevice().getBackend();
956 dev->getRegisterAccessor<UserType>(consumer.getRegisterName(), consumer.getNumberOfElements(), 0, {});
957 auto catalog = deviceManager->getDevice().getRegisterCatalogue();
958 auto tags = catalog.getRegister(consumer.getRegisterName()).getTags();
961 impl->accessChannel(0) =
962 std::vector<UserType>(consumer.getNumberOfElements(), net.feeder.getConstantValue<UserType>());
970 if(!tags.contains(ChimeraTK::SystemTags::reverseRecovery)) {
971 deviceManager->addRecoveryAccessor(
972 boost::make_shared<RecoveryHelper>(impl, VersionNumber(), deviceManager->writeOrder()));
976 throw ChimeraTK::logic_error(
"Using constants as triggers is not supported!");
979 throw ChimeraTK::logic_error(
"Unexpected node type!");
988 debug(
"-----------------------------");
989 debug(
"Optimising unmapped variables");
990 debug(
"-----------------------------");
992 for(
const auto& name : names) {
993 debug(
"Looking at network " + name);
997 if(network.useReverseRecovery) {
1001 auto reverseConsumer = std::ranges::find_if(network.consumers, [](
auto& consumer) {
1002 return consumer.getType() == NodeType::Device &&
1003 consumer.getTags().contains(ChimeraTK::SystemTags::reverseRecovery);
1005 if(reverseConsumer->isReadable()) {
1006 debug(std::format(
" Promoting reverse consumer {} to feeder", reverseConsumer->getName()));
1007 network.feeder = *reverseConsumer;
1008 network.consumers.remove(*reverseConsumer);
1012 " Reverse consumer {} is not readable, adding constant feeder instead", reverseConsumer->getName()));
1017 debug(
" Adding constant feeder");
1023 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().
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.
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.