ChimeraTK-ApplicationCore 04.06.00
Loading...
Searching...
No Matches
Model.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
3
4#include "Model.h"
5
6#include "ApplicationModule.h"
7#include "DeviceManager.h"
8#include "DeviceModule.h"
9#include "ModuleGroup.h"
10#include "Utilities.h"
11#include "VariableGroup.h"
12
13namespace ChimeraTK::Model {
14
15 /********************************************************************************************************************/
16 /********************************************************************************************************************/
18 /********************************************************************************************************************/
19 /********************************************************************************************************************/
20
21 Proxy::Proxy(std::shared_ptr<ProxyData> data) : _d(std::move(data)) {}
22
23 /********************************************************************************************************************/
24
25 std::string Proxy::getFullyQualifiedPath() const {
26 return _d->impl->getFullyQualifiedPath(_d->vertex);
27 }
28
29 /********************************************************************************************************************/
30
31 bool Proxy::isValid() const {
32 return _d != nullptr && _d->impl != nullptr;
33 }
34
35 /********************************************************************************************************************/
36
37 bool Proxy::operator==(const Proxy& other) const {
38 if(_d == other._d) {
39 return true;
40 }
41 if(_d == nullptr || other._d == nullptr) {
42 return false;
43 }
44 return _d->impl == other._d->impl && (other._d->impl == nullptr || _d->vertex == other._d->vertex);
45 }
46
47 /********************************************************************************************************************/
48 /********************************************************************************************************************/
50 /********************************************************************************************************************/
51 /********************************************************************************************************************/
52
54 // create ProxyData object
55 _d = std::make_shared<ProxyData>();
56 // create the graph
57 _d->impl = std::make_shared<Impl>();
58 // create root vertex
59 _d->vertex = boost::add_vertex(_d->impl->_graph);
61 _d->impl->_graph[_d->vertex].type = VertexProperties::Type::root;
62 _d->impl->_graph[_d->vertex].p.emplace<VertexProperties::RootProperties>(props);
63
64 // create neighbourhood edge to itself: the root represents both the Application ModuleGroup and the directory.
65 auto [neighbourhoodEdge, success] = boost::add_edge(_d->vertex, _d->vertex, _d->impl->_graph);
66 assert(success);
67 _d->impl->_graph[neighbourhoodEdge].type = EdgeProperties::Type::neighbourhood;
68 }
69
70 /********************************************************************************************************************/
71
73 return _d->impl->add(_d->vertex, module);
74 }
75
76 /********************************************************************************************************************/
77
79 return _d->impl->add(_d->vertex, module);
80 }
81
82 /********************************************************************************************************************/
83
85 return _d->impl->add(_d->vertex, module);
86 }
87
88 /********************************************************************************************************************/
89
90 DirectoryProxy RootProxy::addDirectory(const std::string& name) {
91 return _d->impl->addDirectory(_d->vertex, name);
92 }
93
94 /********************************************************************************************************************/
95
97 // The analyzer assumes that m_Size can be modified in boost::is_any_of so that there could be a mismatch between
98 // creating a dynamic entry and not freeing it later if m_size suddenly is different from what it was
99 // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks)
100 return _d->impl->addDirectoryRecursive(_d->vertex, name);
101 }
102
103 /********************************************************************************************************************/
104
106 return _d->impl->addVariable(_d->vertex, name);
107 }
108
109 /********************************************************************************************************************/
110
112 _d->impl->remove(module);
113 }
114
115 /********************************************************************************************************************/
116
118 _d->impl->remove(module);
119 }
120
121 /********************************************************************************************************************/
122
123 RootProxy::operator Model::ModuleGroupProxy() {
124 return _d->impl->_graph[_d->vertex].makeProxy<ModuleGroupProxy>(_d->vertex, _d->impl);
125 }
126
127 /********************************************************************************************************************/
128
129 RootProxy::operator Model::DirectoryProxy() {
130 return _d->impl->_graph[_d->vertex].makeProxy<DirectoryProxy>(_d->vertex, _d->impl);
131 }
132
133 /********************************************************************************************************************/
134
135 RootProxy RootProxy::makeRootProxy(const std::shared_ptr<Impl>& impl) {
136 auto graph = impl->_graph;
137 auto* vertex = *(boost::vertices(graph).first);
138
139 return graph[vertex].visitProxy(
140 [](auto proxy) {
141 if constexpr(isRoot(proxy)) {
142 return proxy;
143 }
144 else {
145 assert(false);
146 return RootProxy();
147 }
148 },
149 vertex, impl);
150 }
151
152 /********************************************************************************************************************/
153 /********************************************************************************************************************/
155 /********************************************************************************************************************/
156 /********************************************************************************************************************/
157
158 const std::string& ModuleGroupProxy::getName() const {
159 return std::get<VertexProperties::ModuleGroupProperties>(_d->impl->_graph[_d->vertex].p).name;
160 }
161
162 /********************************************************************************************************************/
163
165 return _d->impl->add(_d->vertex, module);
166 }
167
168 /********************************************************************************************************************/
169
171 return _d->impl->add(_d->vertex, module);
172 }
173
174 /********************************************************************************************************************/
175
177 return _d->impl->add(_d->vertex, module);
178 }
179
180 /********************************************************************************************************************/
181
183 _d->impl->remove(module);
184 }
185
186 /********************************************************************************************************************/
187
189 _d->impl->remove(module);
190 }
191
192 /********************************************************************************************************************/
193
195 return std::get<VertexProperties::ModuleGroupProperties>(_d->impl->_graph[_d->vertex].p).module;
196 }
197
198 /********************************************************************************************************************/
199
200 void ModuleGroupProxy::informMove(ModuleGroup& module) {
201 // Updating the reference works only through the constructor, hence we have to get all other data members and
202 // construct a new one in the std::variant.
203
204 auto currentProps = std::get<VertexProperties::ModuleGroupProperties>(_d->impl->_graph[_d->vertex].p);
205
206 _d->impl->_graph[_d->vertex].p.emplace<VertexProperties::ModuleGroupProperties>(
207 VertexProperties::ModuleGroupProperties{std::move(currentProps.name), module});
208 }
209
210 /********************************************************************************************************************/
211 /********************************************************************************************************************/
213 /********************************************************************************************************************/
214 /********************************************************************************************************************/
215
216 const std::string& ApplicationModuleProxy::getName() const {
217 return std::get<VertexProperties::ApplicationModuleProperties>(_d->impl->_graph[_d->vertex].p).name;
218 }
219
220 /********************************************************************************************************************/
221
223 return _d->impl->add(_d->vertex, module);
224 }
225
226 /********************************************************************************************************************/
227
229 return _d->impl->addVariableNode(*this, variable, node);
230 }
231
232 /********************************************************************************************************************/
233
235 _d->impl->remove(module);
236 }
237
238 /********************************************************************************************************************/
239
240 ApplicationModuleProxy::operator Model::VariableGroupProxy() {
241 return _d->impl->_graph[_d->vertex].makeProxy<VariableGroupProxy>(_d->vertex, _d->impl);
242 }
243
244 /********************************************************************************************************************/
245
247 return std::get<VertexProperties::ApplicationModuleProperties>(_d->impl->_graph[_d->vertex].p).module;
248 }
249
250 /********************************************************************************************************************/
251
252 void ApplicationModuleProxy::informMove(ApplicationModule& module) {
253 assert(_d->impl->_graph[_d->vertex].type == VertexProperties::Type::applicationModule);
254
255 // Updating the reference works only through the constructor, hence we have to get all other data members and
256 // construct a new one in the std::variant.
257
258 auto currentProps = std::get<VertexProperties::ApplicationModuleProperties>(_d->impl->_graph[_d->vertex].p);
259
260 _d->impl->_graph[_d->vertex].p.emplace<VertexProperties::ApplicationModuleProperties>(
261 VertexProperties::ApplicationModuleProperties{std::move(currentProps.name), module});
262 }
263
264 /********************************************************************************************************************/
265 /********************************************************************************************************************/
267 /********************************************************************************************************************/
268 /********************************************************************************************************************/
269
270 const std::string& VariableGroupProxy::getName() const {
271 return std::get<VertexProperties::VariableGroupProperties>(_d->impl->_graph[_d->vertex].p).name;
272 }
273
274 /********************************************************************************************************************/
275
277 return _d->impl->add(_d->vertex, module);
278 }
279
280 /********************************************************************************************************************/
281
283 return _d->impl->addVariableNode(*this, variable, node);
284 }
285
286 /********************************************************************************************************************/
287
289 _d->impl->remove(module);
290 }
291
292 /********************************************************************************************************************/
293
295 Vertex currentVertex{_d->vertex};
296
297 do {
298 // there must be exactly one owner
299 assert(boost::in_degree(currentVertex, _d->impl->_ownershipView) == 1);
300
301 // Update currentVertex to the owner of the previous currentVertex.
302 currentVertex =
303 boost::source(*boost::in_edges(currentVertex, _d->impl->_ownershipView).first, _d->impl->_ownershipView);
304
305 // repeat until the type of the currentVertex is no longer a VariablGroup
306 } while(_d->impl->_ownershipView[currentVertex].type == VertexProperties::Type::variableGroup);
307
308 // The type must now be a ApplicationModule
309 assert(_d->impl->_ownershipView[currentVertex].type == VertexProperties::Type::applicationModule);
310
311 return _d->impl->_graph[currentVertex].makeProxy<ApplicationModuleProxy>(currentVertex, _d->impl);
312 }
313
314 /********************************************************************************************************************/
315
317 return std::get<VertexProperties::VariableGroupProperties>(_d->impl->_graph[_d->vertex].p).module;
318 }
319
320 /********************************************************************************************************************/
321
322 void VariableGroupProxy::informMove(VariableGroup& group) {
323 assert(_d->impl->_graph[_d->vertex].type == VertexProperties::Type::variableGroup);
324
325 // Updating the reference works only through the constructor, hence we have to get all other data members and
326 // construct a new one in the std::variant.
327
328 auto currentProps = std::get<VertexProperties::VariableGroupProperties>(_d->impl->_graph[_d->vertex].p);
329
330 _d->impl->_graph[_d->vertex].p.emplace<VertexProperties::VariableGroupProperties>(
331 VertexProperties::VariableGroupProperties{std::move(currentProps.name), group});
332 }
333
334 /********************************************************************************************************************/
335 /********************************************************************************************************************/
337 /********************************************************************************************************************/
338 /********************************************************************************************************************/
339
340 const std::string& DeviceModuleProxy::getAliasOrCdd() const {
341 return std::get<VertexProperties::DeviceModuleProperties>(_d->impl->_graph[_d->vertex].p).aliasOrCdd;
342 }
343
344 /********************************************************************************************************************/
345
347 return std::get<VertexProperties::DeviceModuleProperties>(_d->impl->_graph[_d->vertex].p).trigger.lock();
348 }
349
350 /********************************************************************************************************************/
351
353 return _d->impl->addVariableNode(*this, variable, node);
354 }
355
356 /********************************************************************************************************************/
357
358 void DeviceModuleProxy::informMove(DeviceModule& module) {
359 // Updating the reference works only through the constructor, hence we have to get all other data members and
360 // construct a new one in the std::variant.
361
362 auto currentProps = std::get<VertexProperties::DeviceModuleProperties>(_d->impl->_graph[_d->vertex].p);
363
364 _d->impl->_graph[_d->vertex].p.emplace<VertexProperties::DeviceModuleProperties>(
366 std::move(currentProps.aliasOrCdd), std::move(currentProps.trigger), module});
367 }
368
369 /********************************************************************************************************************/
370 /********************************************************************************************************************/
372 /********************************************************************************************************************/
373 /********************************************************************************************************************/
374
375 const std::string& ProcessVariableProxy::getName() const {
376 return std::get<VertexProperties::ProcessVariableProperties>(_d->impl->_graph[_d->vertex].p).name;
377 }
378
379 /********************************************************************************************************************/
380
381 const std::vector<std::shared_ptr<VariableNetworkNode>>& ProcessVariableProxy::getNodes() const {
382 return std::get<VertexProperties::ProcessVariableProperties>(_d->impl->_graph[_d->vertex].p).nodes;
383 }
384
385 /********************************************************************************************************************/
386
387 const std::unordered_set<std::string>& ProcessVariableProxy::getTags() const {
388 return std::get<VertexProperties::ProcessVariableProperties>(_d->impl->_graph[_d->vertex].p).tags;
389 }
390
391 /********************************************************************************************************************/
392
393 void ProcessVariableProxy::addTag(const std::string& tag) {
394 auto& tags = std::get<VertexProperties::ProcessVariableProperties>(_d->impl->_graph[_d->vertex].p).tags;
395 tags.insert(tag);
396 // note, we do not erase negated tags here (in contrast to EntityOwner::addTag) since the model anyway
397 // collects tags from all associated Accessors. If one Accessor adds a tag and another one removes the same, the
398 // set of tags in the model contains both tag and negate tag, meaning it is undecided.
399 }
400
401 /********************************************************************************************************************/
402
404 // Safety check: We must not modify the model while iterating
405 if(_d->impl->_graphVisitingLevel != 0) {
406 throw ChimeraTK::logic_error("Must not alter the model while iterating/visiting!");
407 }
408
409 assert(node.getType() == NodeType::Application || node.getType() == NodeType::Device);
410 assert(node.getModel().isValid());
411 assert(isValid());
412 assert(node.getModel()._d->vertex == _d->vertex);
413 assert(node.getModel()._d->impl == _d->impl);
414
415 // remove node from the list of nodes in the PV's vertex properties
416 try {
417 auto& nodes = std::get<VertexProperties::ProcessVariableProperties>(_d->impl->_graph[_d->vertex].p).nodes;
418 auto it = std::find_if(nodes.begin(), nodes.end(), [&](auto pn) { return *pn == node; });
419 if(it == nodes.end()) {
420 return;
421 }
422 nodes.erase(it);
423
424 // remove model relationships between PV and module
425 bool ownershipDeleted{false};
426 if(node.getType() == NodeType::Application) {
427 auto* vg = dynamic_cast<VariableGroup*>(node.getOwningModule());
428 assert(vg != nullptr);
429
430 // remove ownership edge to variable group (if any)
431 auto vgm = vg->getModel();
432 if(vgm.isValid()) {
433 for(const auto& edge :
434 boost::make_iterator_range(boost::edge_range(vgm._d->vertex, _d->vertex, _d->impl->_graph))) {
435 if(_d->impl->_graph[edge].type == EdgeProperties::Type::ownership) {
436 boost::remove_edge(edge, _d->impl->_graph);
437 ownershipDeleted = true;
438 break;
439 }
440 }
441 }
442
443 // obtain the accessing module
444 auto* am = dynamic_cast<ApplicationModule*>(vg->findApplicationModule());
445 assert(am != nullptr);
446 auto amm = am->getModel();
447 // The owning ApplicationModule might be no longer in the model. This happens when the owning ApplicationModule
448 // has been removed from the model already. In this case, the direct ownership relation as well as the PV access
449 // relation have been removed already when removing the ApplicationModule.
450 if(amm.isValid()) {
451 // remove ownership edge to application module group (if directly owned)
452 if(!ownershipDeleted) {
453 for(const auto& edge :
454 boost::make_iterator_range(boost::edge_range(amm._d->vertex, _d->vertex, _d->impl->_graph))) {
455 if(_d->impl->_graph[edge].type == EdgeProperties::Type::ownership) {
456 boost::remove_edge(edge, _d->impl->_graph);
457 ownershipDeleted = true;
458 break;
459 }
460 }
461 }
462
463 // the ownership must be removed now
464 assert(ownershipDeleted);
465
466 // remove pv access edge
467#ifndef NDEBUG
468 bool pvAccessDeleted{false};
469#endif
471 for(const auto& edge :
472 boost::make_iterator_range(boost::edge_range(_d->vertex, amm._d->vertex, _d->impl->_graph))) {
473 if(_d->impl->_graph[edge].type == EdgeProperties::Type::pvAccess) {
474 boost::remove_edge(edge, _d->impl->_graph);
475#ifndef NDEBUG
476 pvAccessDeleted = true;
477#endif
478 break;
479 }
480 }
481 }
482 else {
484 for(const auto& edge :
485 boost::make_iterator_range(boost::edge_range(amm._d->vertex, _d->vertex, _d->impl->_graph))) {
486 if(_d->impl->_graph[edge].type == EdgeProperties::Type::pvAccess) {
487 boost::remove_edge(edge, _d->impl->_graph);
488#ifndef NDEBUG
489 pvAccessDeleted = true;
490#endif
491 break;
492 }
493 }
494 }
495 assert(pvAccessDeleted);
496 }
497 }
498 else if(node.getType() == NodeType::Device) {
499 auto* dm = dynamic_cast<DeviceModule*>(node.getOwningModule());
500 assert(dm != nullptr);
501
502 // remove ownership and pv access edges to device module
503 auto dmm = dm->getModel();
504 if(dmm.isValid()) {
505 boost::remove_edge(_d->vertex, dmm._d->vertex, _d->impl->_graph);
506 boost::remove_edge(dmm._d->vertex, _d->vertex, _d->impl->_graph);
507 }
508 }
509
510 // if only one incoming edge exists any more, remove the entire variable. the one incoming edge is the parenthood
511 // relation. ownership relations are also incoming, of which we must have zero.
512 if(boost::in_degree(_d->vertex, _d->impl->_graph) <= 1 && boost::out_degree(_d->vertex, _d->impl->_graph) == 0) {
513 assert(boost::in_degree(_d->vertex, _d->impl->_graph) == 0 ||
514 _d->impl->_graph[*boost::in_edges(_d->vertex, _d->impl->_graph).first].type ==
516
517 assert(nodes.empty());
518
519 boost::clear_vertex(_d->vertex, _d->impl->_graph);
520 boost::remove_vertex(_d->vertex, _d->impl->_graph);
521
522 _d->vertex = Model::Vertex();
523 _d->impl.reset();
524 }
525 }
526 catch(std::bad_variant_access&) {
527 assert(false);
528 }
529
530 node.setModel({});
531 }
532
533 /********************************************************************************************************************/
534 /********************************************************************************************************************/
536 /********************************************************************************************************************/
537 /********************************************************************************************************************/
538
539 const std::string& DirectoryProxy::getName() const {
540 return std::get<VertexProperties::DirectoryProperties>(_d->impl->_graph[_d->vertex].p).name;
541 }
542
543 /********************************************************************************************************************/
544
546 return _d->impl->addVariable(_d->vertex, name);
547 }
548
549 /********************************************************************************************************************/
550
552 return _d->impl->addDirectory(_d->vertex, name);
553 }
554
555 /********************************************************************************************************************/
556
558 return _d->impl->addDirectoryRecursive(_d->vertex, name);
559 }
560
561 /********************************************************************************************************************/
562 /********************************************************************************************************************/
564 /********************************************************************************************************************/
565 /********************************************************************************************************************/
566
567 ModuleGroupProxy Impl::add(Vertex owner, ModuleGroup& module) {
570 }
571
572 /********************************************************************************************************************/
573
574 ApplicationModuleProxy Impl::add(Vertex owner, ApplicationModule& module) {
575 return genericAdd<ApplicationModuleProxy, ApplicationModule, VertexProperties::ApplicationModuleProperties,
577 }
578
579 /********************************************************************************************************************/
580
581 VariableGroupProxy Impl::add(Vertex owner, VariableGroup& module) {
582 return genericAdd<VariableGroupProxy, VariableGroup, VertexProperties::VariableGroupProperties,
584 }
585
586 /********************************************************************************************************************/
587
588 DeviceModuleProxy Impl::add(Vertex owner, DeviceModule& module) {
589 return genericAdd<DeviceModuleProxy, DeviceModule, VertexProperties::DeviceModuleProperties,
591 }
592
593 /********************************************************************************************************************/
594
595 template<typename PROXY, typename MODULE, typename PROPS, VertexProperties::Type TYPE>
596 PROXY Impl::genericAdd(Vertex owner, MODULE& module) {
597 // Safety check: We must not modify the model while iterating
598 if(_graphVisitingLevel != 0) {
599 throw ChimeraTK::logic_error("Must not alter the model while iterating/visiting!");
600 }
601
602 auto parentDirectory = visit(owner, returnDirectory, getNeighbourDirectory, returnFirstHit(DirectoryProxy{}));
603
604 // create plain vertex first
605 auto* newVertex = boost::add_vertex(_graph);
606
607 // set vertex type and type-dependent properties
608 _graph[newVertex].type = TYPE;
609 if constexpr(!std::is_same<MODULE, DeviceModule>::value) {
610 _graph[newVertex].p.emplace<PROPS>(PROPS{module.getName(), module});
611 }
612 else {
613 auto alias = module.getDeviceManager().getDeviceAliasOrURI();
614 auto triggerPath = module.getTriggerPath();
615
616 ProcessVariableProxy trigger; // initially invalid, stays like that if triggerPath.empty()
617
618 if(!triggerPath.empty()) {
619 auto dir = parentDirectory.addDirectoryRecursive(Utilities::getPathName(triggerPath));
620 trigger = dir.addVariable(Utilities::getUnqualifiedName(triggerPath));
621
622 // connect trigger vertex with trigger edge
623 auto [triggerEdge, triggerSuccess] = boost::add_edge(trigger._d->vertex, newVertex, _graph);
624 assert(triggerSuccess);
625 _graph[triggerEdge].type = EdgeProperties::Type::trigger;
626 }
627
628 _graph[newVertex].p.emplace<PROPS>(PROPS{alias, trigger, module});
629 }
630
631 // connect the vertex with an ownership edge
632 auto [ownershipEdge, ownershipSuccess] = boost::add_edge(owner, newVertex, _graph);
633 assert(ownershipSuccess);
634 _graph[ownershipEdge].type = EdgeProperties::Type::ownership;
635
636 // obtain/create directory corresponding to the fully qualified path of the module
637 DirectoryProxy directory;
638 if constexpr(!std::is_same<MODULE, DeviceModule>::value) {
639 directory = addDirectoryRecursive(parentDirectory._d->vertex, module.getName());
640 }
641 else {
642 // the register content of DeviceModules is connected directly to the parent directory, since the alias/URI shall
643 // not be visible as a sub-directory.
644 directory = parentDirectory;
645 }
646
647 // connect the vertex with the directory with a neighbourhood edge
648 auto [neighbourhoodEdge, neighbourhoodSuccess] = boost::add_edge(newVertex, directory._d->vertex, _graph);
649 assert(neighbourhoodSuccess);
650 _graph[neighbourhoodEdge].type = EdgeProperties::Type::neighbourhood;
651
652 return _graph[newVertex].makeProxy<PROXY>(newVertex, shared_from_this());
653 }
654
655 /********************************************************************************************************************/
656
657 void Impl::remove(ModuleGroup& module) {
658 genericRemove(module);
659 }
660
661 /********************************************************************************************************************/
662
663 void Impl::remove(ApplicationModule& module) {
664 genericRemove(module);
665 }
666
667 /********************************************************************************************************************/
668
669 void Impl::remove(VariableGroup& module) {
670 genericRemove(module);
671 }
672
673 /********************************************************************************************************************/
674
675 void Impl::remove(DeviceModule& module) {
676 genericRemove(module);
677 }
678
679 /********************************************************************************************************************/
680
681 template<typename MODULE>
682 void Impl::genericRemove(MODULE& module) {
683 // Safety check: We must not modify the model while iterating
684 if(_graphVisitingLevel != 0) {
685 throw ChimeraTK::logic_error("Must not alter the model while iterating/visiting!");
686 }
687 auto modelToRemove = module.getModel();
688
689 // The model may be invalid e.g. in case of calls to unregisterModule() in move assignment operations. Nothing to be
690 // removed from the model in that case.
691 if(!modelToRemove.isValid()) {
692 return;
693 }
694
695 // Remove the vertex representing this module with all edges (ownership, PV access etc.)
696 auto vertexToRemove = modelToRemove._d->vertex;
697 boost::clear_vertex(vertexToRemove, _graph);
698 boost::remove_vertex(vertexToRemove, _graph);
699
700 module._model = {};
701 }
702
703 /********************************************************************************************************************/
704
705 template<typename PROXY>
706 void Impl::addVariableNode(PROXY module, ProcessVariableProxy& variable, VariableNetworkNode& node) {
707 // Safety check: We must not modify the model while iterating
708 if(_graphVisitingLevel != 0) {
709 throw ChimeraTK::logic_error("Must not alter the model while iterating/visiting!");
710 }
711 node.setModel(variable);
712
713 // get vertex of for variable
714 auto* vertex = variable._d->vertex;
715
716 // get owning vertex (VariableGroup or ApplicationModule)
717 auto owningVertex = module._d->vertex;
718
719 // create ownership edge
720 auto [newOwnershipEdge, success] = boost::add_edge(owningVertex, vertex, _graph);
721 assert(success);
722 _graph[newOwnershipEdge].type = EdgeProperties::Type::ownership;
723
724 // get accessing ApplicationModule or DeviceModule vertex
725 Vertex accessingVertex;
726 if constexpr(std::is_same<PROXY, ApplicationModuleProxy>::value || std::is_same<PROXY, DeviceModuleProxy>::value) {
727 accessingVertex = module._d->vertex;
728 }
729 else {
730 static_assert(std::is_same<PROXY, VariableGroupProxy>::value, "Proxy type cannot have variable nodes.");
731 accessingVertex = module.getOwningModule()._d->vertex;
732 }
733
734 // add node to variable
735 auto& props = std::get<VertexProperties::ProcessVariableProperties>(_graph[vertex].p);
736 props.nodes.emplace_back(std::make_shared<VariableNetworkNode>(node));
737
738 // add tags
739 const auto& tags = node.getTags();
740 props.tags.insert(tags.begin(), tags.end());
741
742 // connect the variable vertex with the accessing module, direction depends on access (read/write)
743 Edge newEdge;
744 if(node.getDirection().dir == VariableDirection::feeding) {
745 std::tie(newEdge, success) = boost::add_edge(accessingVertex, vertex, _graph);
746 }
747 else {
748 std::tie(newEdge, success) = boost::add_edge(vertex, accessingVertex, _graph);
749 }
750 assert(success);
751 _graph[newEdge].type = EdgeProperties::Type::pvAccess;
752 _graph[newEdge].pvAccessWithReturnChannel = node.getDirection().withReturn;
753 }
754
755 /********************************************************************************************************************/
756
757 ProcessVariableProxy Impl::addVariable(Vertex parent, const std::string& name) {
758 // Safety check: We must not modify the model while iterating
759 if(_graphVisitingLevel != 0) {
760 throw ChimeraTK::logic_error("Must not alter the model while iterating/visiting!");
761 }
762 if(!Utilities::checkName(name, false)) {
763 throw ChimeraTK::logic_error("Variable name '" + name + "' contains illegal characters.");
764 }
765
766 // first check if variable already exists
767 auto existing = visit(parent, returnProcessVariable, adjacentOutSearch, keepParenthood,
768 keepProcessVariables && keepName(name), returnFirstHit(ProcessVariableProxy{}));
769 if(existing.isValid()) {
770 return existing;
771 }
772
773 // create plain vertex first
774 auto* newVertex = boost::add_vertex(_graph);
775
776 // set vertex type and type-dependent properties
777 _graph[newVertex].type = VertexProperties::Type::processVariable;
778 VertexProperties::ProcessVariableProperties props{name, {}, {}};
779 _graph[newVertex].p.emplace<VertexProperties::ProcessVariableProperties>(props);
780
781 // connect the vertex with a parenthood edge
782 auto [newEdge, success] = boost::add_edge(parent, newVertex, _graph);
783 assert(success);
784 _graph[newEdge].type = EdgeProperties::Type::parenthood;
785
786 return _graph[newVertex].makeProxy<ProcessVariableProxy>(newVertex, shared_from_this());
787 }
788
789 /********************************************************************************************************************/
790
791 DirectoryProxy Impl::addDirectory(Vertex parent, const std::string& name) {
792 // Safety check: We must not modify the model while iterating
793 if(_graphVisitingLevel != 0) {
794 throw ChimeraTK::logic_error("Must not alter the model while iterating/visiting!");
795 }
796 if(!Utilities::checkName(name, false)) {
797 throw ChimeraTK::logic_error("Variable name '" + name + "' contains illegal characters.");
798 }
799
800 // first check if directory already exists
801 auto existing = visit(parent, returnDirectory, adjacentOutSearch, keepParenthood, keepDirectories && keepName(name),
803 if(existing.isValid()) {
804 return existing;
805 }
806
807 // create plain vertex first
808 auto* newVertex = boost::add_vertex(_graph);
809
810 // set vertex type and type-dependent properties
811 _graph[newVertex].type = VertexProperties::Type::directory;
812 VertexProperties::DirectoryProperties props{name};
813 _graph[newVertex].p.emplace<VertexProperties::DirectoryProperties>(props);
814
815 // connect the vertex with an hierarchy edge
816 auto [newEdge, success] = boost::add_edge(parent, newVertex, _graph);
817 assert(success);
818 _graph[newEdge].type = EdgeProperties::Type::parenthood;
819
820 return _graph[newVertex].makeProxy<DirectoryProxy>(newVertex, shared_from_this());
821 }
822
823 /********************************************************************************************************************/
824
825 DirectoryProxy Impl::addDirectoryRecursive(Vertex parent, const std::string& qualifiedPath) {
826 // separate the path into components
827 std::vector<std::string> components;
828 // The analyzer assumes that m_Size can be modified in boost::is_any_of so that there could be a mismatch between
829 // creating a dynamic entry and not freeing it later if m_size suddenly is different from what it was
830 // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks)
831 boost::split(components, qualifiedPath, boost::is_any_of("/"));
832
833 // Start at the parent
834 auto currentDirectory = _graph[parent].makeProxy<DirectoryProxy>(parent, shared_from_this());
835
836 for(const auto& component : components) {
837 // special treatment for "."
838 if(component == ".") {
839 continue;
840 }
841
842 // special treatment for ".."
843 if(component == "..") {
844 if(currentDirectory._d->vertex == *boost::vertices(currentDirectory._d->impl->_graph).first) {
845 throw ChimeraTK::logic_error("Path component '..' at root directory level found.");
846 }
847 currentDirectory = currentDirectory.visit(returnDirectory, getParent, returnFirstHit(DirectoryProxy{}));
848 assert(currentDirectory.isValid());
849 continue;
850 }
851
852 // Special treatment for an extra slash e.g. at the beginning or two consequtive slashes.
853 // Since the slash is the separator, the path component is just empty.
854 if(component.empty()) {
855 currentDirectory = Model::DirectoryProxy(RootProxy::makeRootProxy(shared_from_this())); // root directory
856 continue;
857 }
858
859 // no special treatment necessary: just add the new level (if not yet existing)
860 currentDirectory = currentDirectory.addDirectory(component);
861 }
862
863 // return the directory (either newly created or previously existing)
864 return currentDirectory;
865 }
866
867 /********************************************************************************************************************/
868
869 [[nodiscard]] std::string Impl::getFullyQualifiedPath(Vertex vertex) {
870 return _graph[vertex].visit([&](auto props) -> std::string {
871 constexpr bool useDirectoryHierarchy = isDirectory(props) || isVariable(props);
872
873 Vertex currentVertex = vertex;
874
875 RegisterPath path;
876
877 // Add name of current vertex (unless it's a DeviceModule or the Root)
878 if constexpr(hasName(props)) {
879 path = props.name;
880 }
881
882 // Loop until we have found the root vertex, add all names to the path
883 while(currentVertex != *boost::vertices(_graph).first) {
884 // define vistor to be executed for the owner of currentVertex
885 bool found = false;
886 auto vis = [&](auto proxy) {
887 if constexpr(hasName(proxy)) {
888 // prefix name of current object to path, since we are iterating towards the root
889 path = proxy.getName() / path;
890 }
891 currentVertex = proxy._d->vertex;
892 found = true;
893 };
894
895 // just call the lambda for the owner/parent of currentVertex
896 if constexpr(!useDirectoryHierarchy) {
897 visit(currentVertex, vis, getOwner);
898 }
899 else {
900 visit(currentVertex, vis, getParent);
901 }
902
903 // abort condition in case we try to get the qualified path for something not connected with the root any more
904 if(!found) {
905 path = "<disjunct>" / path;
906 break;
907 }
908 }
909
910 // resolve "/", ".." and "."
911 auto components = path.getComponents();
912 path = ""; // build path from components
913 for(const auto& component : components) {
914 if(component == ".") {
915 continue;
916 }
917 if(component == "..") {
918 path--; // remove last component
919 continue;
920 }
921 if(component == "/") {
922 path = ""; // clear entire path
923 continue;
924 }
925 path /= component;
926 }
927
928 return path;
929 });
930 }
931
932 /********************************************************************************************************************/
933
935 return std::get<VertexProperties::DeviceModuleProperties>(_d->impl->_graph[_d->vertex].p).module;
936 }
937 /********************************************************************************************************************/
938
939} // namespace ChimeraTK::Model
VariableGroupProxy add(VariableGroup &module)
Definition Model.cc:222
void remove(VariableGroup &module)
Definition Model.cc:234
ApplicationModule & getApplicationModule() const
Return the actual ApplicationModule.
Definition Model.cc:246
const std::string & getName() const
Get the name of the ApplicationModule.
Definition Model.cc:216
void addVariable(ProcessVariableProxy &variable, VariableNetworkNode &node)
Definition Model.cc:228
void addVariable(ProcessVariableProxy &variable, VariableNetworkNode &node)
Definition Model.cc:352
ProcessVariableProxy getTrigger() const
Get the ProcessVariableProxy for the trigger.
Definition Model.cc:346
DeviceModule & getDeviceModule() const
Definition Model.cc:934
const std::string & getAliasOrCdd() const
Get the alias or CDD of the device.
Definition Model.cc:340
ProcessVariableProxy addVariable(const std::string &name)
Definition Model.cc:545
const std::string & getName() const
Get the name of the Directory.
Definition Model.cc:539
DirectoryProxy addDirectory(const std::string &name)
Definition Model.cc:551
DirectoryProxy addDirectoryRecursive(const std::string &name)
Definition Model.cc:557
friend class ChimeraTK::ModuleGroup
Definition Model.h:295
ModuleGroupProxy add(ModuleGroup &module)
Definition Model.cc:164
const std::string & getName() const
Get the name of the ModuleGroup.
Definition Model.cc:158
ModuleGroup & getModuleGroup() const
Return the actual ModuleGroup.
Definition Model.cc:194
void remove(ApplicationModule &module)
Definition Model.cc:188
const std::vector< std::shared_ptr< VariableNetworkNode > > & getNodes() const
Return all VariableNetworkNodes for this variable.
Definition Model.cc:381
void addTag(const std::string &tag)
Add tag to this PV. Used by VariableNetworkNode to update the model when tags are added to PVs.
Definition Model.cc:393
const std::string & getName() const
Get the name of the ProcessVariable.
Definition Model.cc:375
void removeNode(const VariableNetworkNode &node)
Remove VariableNetworkNode from the list of nodes. Note: Will invalidate return value of getNodes()!
Definition Model.cc:403
const std::unordered_set< std::string > & getTags() const
Return all tags attached to this variable.
Definition Model.cc:387
Base class for the proxies representing objects in the model.
Definition Model.h:72
std::shared_ptr< ProxyData > _d
Definition Model.h:200
friend class ApplicationModuleProxy
Definition Model.h:184
bool isValid() const
Check if the model is valid.
Definition Model.cc:31
friend class VariableGroupProxy
Definition Model.h:185
friend class RootProxy
Definition Model.h:182
friend class DirectoryProxy
Definition Model.h:188
bool operator==(const Proxy &other) const
Definition Model.cc:37
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
friend class ModuleGroupProxy
Definition Model.h:183
friend class DeviceModuleProxy
Definition Model.h:186
Proxy()=default
The default constructor creates a dysfunctional, empty proxy.
friend class ProcessVariableProxy
Definition Model.h:187
std::string getFullyQualifiedPath() const
Return the fully qualified path.
Definition Model.cc:25
Proxy representing the root of the application model.
Definition Model.h:210
DirectoryProxy addDirectoryRecursive(const std::string &name)
Definition Model.cc:96
ModuleGroupProxy add(ModuleGroup &module)
Definition Model.cc:72
void remove(ApplicationModule &module)
Definition Model.cc:117
static RootProxy makeRootProxy(const std::shared_ptr< Impl > &impl)
Create RootProxy assuming the application has been created already (i.e.
Definition Model.cc:135
ProcessVariableProxy addVariable(const std::string &name)
Definition Model.cc:105
DirectoryProxy addDirectory(const std::string &name)
Definition Model.cc:90
void remove(VariableGroup &module)
Definition Model.cc:288
VariableGroupProxy add(VariableGroup &module)
Definition Model.cc:276
const std::string & getName() const
Get the name of the VariableGroup.
Definition Model.cc:270
ApplicationModuleProxy getOwningModule() const
Return the owning ApplicationModule (may be indirectly owned in case of nested VariableGroups).
Definition Model.cc:294
VariableGroup & getVariableGroup() const
Return the actual VariableGroup.
Definition Model.cc:316
void addVariable(ProcessVariableProxy &variable, VariableNetworkNode &node)
Definition Model.cc:282
Class describing a node of a variable network.
NodeType getType() const
Getter for the properties.
Model::ProcessVariableProxy getModel() const
void setModel(const Model::ProcessVariableProxy &model) const
VariableDirection getDirection() const
constexpr ReturnFirstHitWithValue< void > returnFirstHit()
Stop the search after the first hit and return.
Definition Model.h:790
constexpr bool isDirectory(const PROPERTY_OR_PROXY &)
Definition Model.h:669
constexpr bool hasName(const PROPERTY_OR_PROXY &)
Definition Model.h:677
Graph::edge_descriptor Edge
Definition Model.h:700
constexpr bool isRoot(const PROPERTY_OR_PROXY &)
Predicates to identify the type of a proxy or a properties struct.
Definition Model.h:621
boost::adjacency_list< detail::OutEdgeListType, detail::VertexListType, boost::bidirectionalS >::vertex_descriptor Vertex
Definition Model.h:65
constexpr bool isVariable(const PROPERTY_OR_PROXY &)
Definition Model.h:661
bool checkName(const std::string &name, bool allowDotsAndSlashes)
Check given name for characters which are not allowed in variable or module names.
Definition Utilities.cc:88
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
module_ module
@ neighbourhood
Edge represents the PV directory hierarchy. Arrow points towards the sub-directory or PV.
@ parenthood
Edge represents (C++) ownership. Arrow points towards the sub-module or PV.
@ ownership
Edge represents access of an module to a PV. Arrow shows data flow direction (read/write).
@ trigger
Edge points from a module to the directory where its PVs appear without hierarchy modification.
This is used to allow default construction of the std::variant.
Definition Model.h:523
enum ChimeraTK::VariableDirection::@0 dir
Enum to define directions of variables.