5#include <ChimeraTK/cppext/finally.hpp>
6#include <ChimeraTK/RegisterPath.h>
8#include <boost/algorithm/string/predicate.hpp>
9#include <boost/graph/adjacency_list.hpp>
10#include <boost/graph/breadth_first_search.hpp>
11#include <boost/graph/depth_first_search.hpp>
12#include <boost/graph/filtered_graph.hpp>
13#include <boost/graph/graphviz.hpp>
14#include <boost/graph/transpose_graph.hpp>
24 class ApplicationModule;
26 class VariableNetworkNode;
31 class InversionOfControlAccessor;
50 template<
typename ProxyType>
65 boost::adjacency_list<detail::OutEdgeListType, detail::VertexListType, boost::bidirectionalS>::vertex_descriptor;
157 template<
typename VISITOR,
typename... Args>
158 auto visit(VISITOR visitor, Args... args)
const;
176 [[nodiscard]]
bool isValid()
const;
191 template<
typename ProxyType>
198 explicit Proxy(std::shared_ptr<ProxyData> data);
200 std::shared_ptr<ProxyData>
_d;
238 template<
typename VISITOR>
239 bool visitByPath(std::string_view path, VISITOR visitor)
const;
241 template<
typename... Args>
242 void writeGraphViz(
const std::string& filename, Args... args)
const;
277 [[nodiscard]]
const std::string&
getName()
const;
303 [[nodiscard]]
const std::string&
getName()
const;
331 [[nodiscard]]
const std::string&
getName()
const;
381 [[nodiscard]]
const std::string&
getName()
const;
384 [[nodiscard]]
const std::vector<std::shared_ptr<VariableNetworkNode>>&
getNodes()
const;
387 [[nodiscard]]
const std::unordered_set<std::string>&
getTags()
const;
402 template<
typename VISITOR>
403 bool visitByPath(std::string_view path, VISITOR visitor)
const;
410 void addTag(
const std::string& tag);
429 [[nodiscard]]
const std::string&
getName()
const;
442 template<
typename VISITOR>
443 bool visitByPath(std::string_view path, VISITOR visitor)
const;
465 template<
typename ProxyType>
474 : _vertex(owningProxy.isValid() ? owningProxy._d->vertex : nullptr),
475 _impl(owningProxy.isValid() ? owningProxy._d->impl : nullptr) {}
479 if(owningProxy.isValid()) {
480 _vertex = owningProxy._d->vertex;
481 _impl = owningProxy._d->impl;
494 p._d = std::make_shared<Proxy::ProxyData>(_vertex, _impl.lock());
500 std::weak_ptr<Impl> _impl;
545 std::vector<std::shared_ptr<VariableNetworkNode>>
nodes;
546 std::unordered_set<std::string>
tags;
559 template<
typename VISITOR>
560 [[nodiscard]]
typename std::invoke_result<VISITOR, ApplicationModuleProperties&>::type
visit(VISITOR visitor)
const;
564 template<
typename VISITOR>
565 typename std::invoke_result<VISITOR, ApplicationModuleProxy>::type
visitProxy(
566 VISITOR visitor,
Vertex vertex,
const std::shared_ptr<Impl>& impl)
const;
581 template<
typename PROXY>
582 PROXY
makeProxy(
Vertex vertex,
const std::shared_ptr<Impl>& impl)
const;
590 mutable std::weak_ptr<Proxy::ProxyData> _proxy;
620 template<
typename PROPERTY_OR_PROXY>
621 constexpr bool isRoot(
const PROPERTY_OR_PROXY&) {
622 return std::is_same<PROPERTY_OR_PROXY, VertexProperties::RootProperties>::value ||
623 std::is_same<PROPERTY_OR_PROXY, RootProxy>::value;
628 template<
typename PROPERTY_OR_PROXY>
630 return std::is_same<PROPERTY_OR_PROXY, VertexProperties::ModuleGroupProperties>::value ||
631 std::is_same<PROPERTY_OR_PROXY, ModuleGroupProxy>::value;
636 template<
typename PROPERTY_OR_PROXY>
638 return std::is_same<PROPERTY_OR_PROXY, VertexProperties::ApplicationModuleProperties>::value ||
639 std::is_same<PROPERTY_OR_PROXY, ApplicationModuleProxy>::value;
644 template<
typename PROPERTY_OR_PROXY>
646 return std::is_same<PROPERTY_OR_PROXY, VertexProperties::VariableGroupProperties>::value ||
647 std::is_same<PROPERTY_OR_PROXY, VariableGroupProxy>::value;
652 template<
typename PROPERTY_OR_PROXY>
654 return std::is_same<PROPERTY_OR_PROXY, VertexProperties::DeviceModuleProperties>::value ||
655 std::is_same<PROPERTY_OR_PROXY, DeviceModuleProxy>::value;
660 template<
typename PROPERTY_OR_PROXY>
662 return std::is_same<PROPERTY_OR_PROXY, VertexProperties::ProcessVariableProperties>::value ||
663 std::is_same<PROPERTY_OR_PROXY, ProcessVariableProxy>::value;
668 template<
typename PROPERTY_OR_PROXY>
670 return std::is_same<PROPERTY_OR_PROXY, VertexProperties::DirectoryProperties>::value ||
671 std::is_same<PROPERTY_OR_PROXY, DirectoryProxy>::value;
676 template<
typename PROPERTY_OR_PROXY>
677 constexpr bool hasName(
const PROPERTY_OR_PROXY&) {
678 return std::is_same<PROPERTY_OR_PROXY, VertexProperties::ModuleGroupProperties>::value ||
679 std::is_same<PROPERTY_OR_PROXY, ModuleGroupProxy>::value ||
680 std::is_same<PROPERTY_OR_PROXY, VertexProperties::ApplicationModuleProperties>::value ||
681 std::is_same<PROPERTY_OR_PROXY, ApplicationModuleProxy>::value ||
682 std::is_same<PROPERTY_OR_PROXY, VertexProperties::VariableGroupProperties>::value ||
683 std::is_same<PROPERTY_OR_PROXY, VariableGroupProxy>::value ||
684 std::is_same<PROPERTY_OR_PROXY, VertexProperties::ProcessVariableProperties>::value ||
685 std::is_same<PROPERTY_OR_PROXY, ProcessVariableProxy>::value ||
686 std::is_same<PROPERTY_OR_PROXY, VertexProperties::DirectoryProperties>::value ||
687 std::is_same<PROPERTY_OR_PROXY, DirectoryProxy>::value;
698 using Vertex = Graph::vertex_descriptor;
700 using Edge = Graph::edge_descriptor;
803 template<
typename PROPERTIES>
811 template<
typename FILTER_LHS,
typename FILTER_RHS>
816 template<
typename FILTER_LHS,
typename FILTER_RHS>
817 struct OrSet : FILTER_LHS, FILTER_RHS {
818 explicit constexpr OrSet(FILTER_LHS lhs, FILTER_RHS rhs) : FILTER_LHS(lhs), FILTER_RHS(rhs) {}
821 return FILTER_LHS::evalVertexFilter(e) || FILTER_RHS::evalVertexFilter(e);
824 [[nodiscard]]
bool evalEdgeFilter(
const typename FILTER_LHS::PropertiesType& e)
const {
825 return FILTER_LHS::evalEdgeFilter(e) || FILTER_RHS::evalEdgeFilter(e);
828 template<
typename FILTER_NEXT_RHS>
829 constexpr auto operator||(
const FILTER_NEXT_RHS& rhs)
const {
833 template<
typename FILTER_NEXT_RHS>
834 constexpr auto operator&&(
const FILTER_NEXT_RHS& rhs)
const {
838 template<
typename PROXY>
840 return FILTER_LHS::template constevalObjecttype<PROXY>() || FILTER_RHS::template constevalObjecttype<PROXY>();
846 template<
typename FILTER_LHS,
typename FILTER_RHS>
848 explicit constexpr AndSet(FILTER_LHS lhs, FILTER_RHS rhs) : FILTER_LHS(lhs), FILTER_RHS(rhs) {}
851 return FILTER_LHS::evalVertexFilter(e) && FILTER_RHS::evalVertexFilter(e);
854 [[nodiscard]]
bool evalEdgeFilter(
const typename FILTER_LHS::PropertiesType& e)
const {
855 return FILTER_LHS::evalEdgeFilter(e) && FILTER_RHS::evalEdgeFilter(e);
858 template<
typename FILTER_NEXT_RHS>
859 constexpr auto operator||(
const FILTER_NEXT_RHS& rhs)
const {
863 template<
typename FILTER_NEXT_RHS>
864 constexpr auto operator&&(
const FILTER_NEXT_RHS& rhs)
const {
868 template<
typename PROXY>
870 return FILTER_LHS::template constevalObjecttype<PROXY>() && FILTER_RHS::template constevalObjecttype<PROXY>();
876 template<
typename FILTER>
878 explicit constexpr EdgeFilter(FILTER filter) : _filter(filter) {}
882 template<
typename FILTER_RHS>
884 static_assert(std::is_base_of<PropertyFilterTag<EdgeProperties>, FILTER_RHS>::value,
885 "Logical OR operator || cannot be used on different filter types.");
886 return OrSet(*
this, rhs);
889 template<
typename FILTER_RHS>
891 static_assert(std::is_base_of<PropertyFilterTag<EdgeProperties>, FILTER_RHS>::value,
892 "Logical AND operator && cannot be used on different filter types.");
893 return AndSet(*
this, rhs);
899 template<
typename FILTER_RHS>
907 template<
typename FILTER>
909 explicit constexpr VertexFilter(FILTER filter) : _filter(std::move(filter)) {}
913 template<
typename FILTER_RHS>
915 static_assert(std::is_base_of<PropertyFilterTag<VertexProperties>, FILTER_RHS>::value,
916 "Logical OR operator || cannot be used on different filter types.");
917 return OrSet(*
this, rhs);
920 template<
typename FILTER_RHS>
922 static_assert(std::is_base_of<PropertyFilterTag<VertexProperties>, FILTER_RHS>::value,
923 "Logical AND operator && cannot be used on different filter types.");
924 return AndSet(*
this, rhs);
930 template<
typename PROXY>
936 template<
typename FILTER_RHS>
944 [[maybe_unused]]
static auto keepAll = [](
const auto&) ->
bool {
return true; };
945 [[maybe_unused]]
static auto keepAllEdges = EdgeFilter(keepAll);
946 [[maybe_unused]]
static auto keepAllVertices = VertexFilter(keepAll);
951 template<EdgeProperties::Type RELATIONSHIP>
952 [[maybe_unused]]
static constexpr auto relationshipFilter =
953 EdgeFilter([](
const EdgeProperties& e) ->
bool {
return e.type == RELATIONSHIP; });
957 static constexpr auto keepPvAccess = relationshipFilter<EdgeProperties::Type::pvAccess>;
958 static constexpr auto keepPvAccesWithReturnChannel = Model::EdgeFilter([](
const Model::EdgeProperties& edge) ->
bool {
961 static constexpr auto keepOwnership = relationshipFilter<EdgeProperties::Type::ownership>;
962 static constexpr auto keepParenthood = relationshipFilter<EdgeProperties::Type::parenthood>;
963 static constexpr auto keepNeighbourhood = relationshipFilter<EdgeProperties::Type::neighbourhood>;
969 template<VertexProperties::Type OBJECTTYPE>
970 [[maybe_unused]]
static constexpr auto objecttypeFilterFunctor =
971 [](
const VertexProperties& e) ->
bool {
return e.type == OBJECTTYPE; };
982 template<VertexProperties::Type OBJECTTYPE,
typename PROXYTYPE>
983 struct ObjecttypeFilter : VertexFilter<decltype(detail::objecttypeFilterFunctor<OBJECTTYPE>)> {
984 constexpr ObjecttypeFilter()
985 : VertexFilter<decltype(
detail::objecttypeFilterFunctor<OBJECTTYPE>)>(
986 detail::objecttypeFilterFunctor<OBJECTTYPE>) {}
988 template<
typename FILTER_RHS>
989 constexpr auto operator||(
const FILTER_RHS& rhs)
const {
990 static_assert(std::is_base_of<PropertyFilterTag<VertexProperties>, FILTER_RHS>::value,
991 "Logical OR operator || cannot be used on different filter types.");
992 return OrSet(*
this, rhs);
995 template<
typename FILTER_RHS>
996 constexpr auto operator&&(
const FILTER_RHS& rhs)
const {
997 static_assert(std::is_base_of<PropertyFilterTag<VertexProperties>, FILTER_RHS>::value,
998 "Logical AND operator && cannot be used on different filter types.");
999 return AndSet(*
this, rhs);
1002 template<
typename PROXY>
1003 [[nodiscard]]
constexpr bool constevalObjecttype()
const {
1004 return std::is_same<PROXY, PROXYTYPE>::value;
1012 constexpr static auto keepModuleGroups = ObjecttypeFilter<VertexProperties::Type::moduleGroup, ModuleGroupProxy>();
1013 constexpr static auto keepApplicationModules =
1014 ObjecttypeFilter<VertexProperties::Type::applicationModule, ApplicationModuleProxy>();
1015 constexpr static auto keepVariableGroups =
1016 ObjecttypeFilter<VertexProperties::Type::variableGroup, VariableGroupProxy>();
1017 constexpr static auto keepDeviceModules = ObjecttypeFilter<VertexProperties::Type::deviceModule, DeviceModuleProxy>();
1018 constexpr static auto keepProcessVariables =
1019 ObjecttypeFilter<VertexProperties::Type::processVariable, ProcessVariableProxy>();
1020 constexpr static auto keepDirectories = ObjecttypeFilter<VertexProperties::Type::directory, DirectoryProxy>();
1026 template<
typename VERTEX_FILTER>
1028 if constexpr(vertexFilter.template constevalObjecttype<ModuleGroupProxy>()) {
1031 else if constexpr(vertexFilter.template constevalObjecttype<ApplicationModuleProxy>()) {
1034 else if constexpr(vertexFilter.template constevalObjecttype<VariableGroupProxy>()) {
1037 else if constexpr(vertexFilter.template constevalObjecttype<DeviceModuleProxy>()) {
1040 else if constexpr(vertexFilter.template constevalObjecttype<ProcessVariableProxy>()) {
1043 else if constexpr(vertexFilter.template constevalObjecttype<DirectoryProxy>()) {
1055 template<
typename VISITOR,
typename VERTEX_FILTER>
1057 typename std::invoke_result<
decltype(detail::findVertexFilterAcceptedProxyType<VERTEX_FILTER>),
1058 VERTEX_FILTER>::type>::type;
1065 [[maybe_unused]]
static auto keepName(
const std::string& name) {
1067 return e.
visit([name](
auto props) ->
bool {
1068 if constexpr(
hasName(props)) {
1069 return props.name == name;
1080 [[maybe_unused]]
static auto keepTag(std::string name) {
1081 return VertexFilter([name](
const VertexProperties& e) ->
bool {
1082 return e.visit([name](
auto props) ->
bool {
1084 return props.tags.count(name);
1096 template<
typename A,
typename B>
1103 template<
typename FIRST,
typename... MORE>
1105 if constexpr(
sizeof...(more) > 0) {
1116 ChimeraTK::Model::adjacentOutSearch, ChimeraTK::Model::keepOwnership, ChimeraTK::Model::keepModuleGroups);
1119 ChimeraTK::Model::adjacentOutSearch, ChimeraTK::Model::keepOwnership, ChimeraTK::Model::keepApplicationModules);
1122 ChimeraTK::Model::adjacentOutSearch, ChimeraTK::Model::keepOwnership, ChimeraTK::Model::keepVariableGroups);
1125 ChimeraTK::Model::adjacentOutSearch, ChimeraTK::Model::keepOwnership, ChimeraTK::Model::keepProcessVariables);
1128 ChimeraTK::Model::adjacentOutSearch, ChimeraTK::Model::keepParenthood, ChimeraTK::Model::keepDirectories);
1131 ChimeraTK::Model::adjacentOutSearch, ChimeraTK::Model::keepParenthood, ChimeraTK::Model::keepProcessVariables);
1133 static constexpr auto children =
1136 static constexpr auto getOwner =
1139 static constexpr auto getParent =
1142 static constexpr auto getNeighbourDirectory =
1143 combinedSearchConfig(ChimeraTK::Model::adjacentOutSearch, ChimeraTK::Model::keepNeighbourhood);
1145 static constexpr auto neighbourModules =
1157 static constexpr auto returnModuleGroup = [](
auto am) -> ModuleGroupProxy {
1162 throw ChimeraTK::logic_error(
"Model: ModuleGroupProxy expected, something else found.");
1174 static constexpr auto returnApplicationModule = [](
auto am) -> ApplicationModuleProxy {
1179 throw ChimeraTK::logic_error(
"Model: ApplicationModuleProxy expected, something else found.");
1191 static constexpr auto returnVariableGroup = [](
auto am) -> VariableGroupProxy {
1196 throw ChimeraTK::logic_error(
"Model: VariableGroupProxy expected, something else found.");
1208 static constexpr auto returnProcessVariable = [](
auto am) -> ProcessVariableProxy {
1213 throw ChimeraTK::logic_error(
"Model: ProcessVariableProxy expected, something else found.");
1225 static constexpr auto returnDirectory = [](
auto dir) -> DirectoryProxy {
1227 return DirectoryProxy(dir);
1230 throw ChimeraTK::logic_error(
"Model: DirectoryProxy expected, something else found.");
1237 template<
typename FIRST,
typename... ARGS>
1239 if constexpr(std::is_base_of<PropertyFilterTag<EdgeProperties>, FIRST>::value) {
1242 else if constexpr(
sizeof...(args) == 0) {
1243 return keepAllEdges;
1251 return keepAllEdges;
1256 template<
typename FIRST,
typename... ARGS>
1258 if constexpr(std::is_base_of<PropertyFilterTag<VertexProperties>, FIRST>::value) {
1261 else if constexpr(
sizeof...(args) == 0) {
1262 return keepAllVertices;
1270 return keepAllVertices;
1275 template<
typename SEARCH_TYPE>
1280 template<
typename FIRST,
typename... ARGS>
1282 if constexpr(std::is_base_of<SearchType, FIRST>::value) {
1285 else if constexpr(
sizeof...(ARGS) == 0) {
1286 return adjacentOutSearch;
1294 return adjacentOutSearch;
1299 template<
typename SEARCH_OPTION_TO_FIND,
typename FIRST,
typename... ARGS>
1301 if constexpr(std::is_base_of<SEARCH_OPTION_TO_FIND, FIRST>::value) {
1304 else if constexpr(
sizeof...(ARGS) == 0) {
1312 template<
typename SEARCH_OPTION_TO_FIND>
1319 template<
typename SEARCH_OPTION_TO_FIND,
typename FIRST,
typename... ARGS>
1321 if constexpr(std::is_base_of<SEARCH_OPTION_TO_FIND, FIRST>::value) {
1324 else if constexpr(
sizeof...(ARGS) == 0) {
1325 throw ChimeraTK::logic_error(
"Model::getSearchOption() called but search option not found!");
1332 template<
typename SEARCH_OPTION_TO_FIND>
1334 throw ChimeraTK::logic_error(
"Model::getSearchOption() called but search option not found!");
1339 template<
typename FIRST,
typename... ARGS>
1341 static_assert(std::is_base_of<PropertyFilterTag<EdgeProperties>, FIRST>::value ||
1342 std::is_base_of<PropertyFilterTag<VertexProperties>, FIRST>::value ||
1343 std::is_base_of<SearchType, FIRST>::value || std::is_base_of<SearchOption, FIRST>::value,
1344 "Wrong type passed in search configuration argument list. Must be ether an EdgeFilter, a VertexFilter, a "
1345 "SearchType or a SearchOption.");
1346 if constexpr(
sizeof...(args) > 0) {
1360 class Impl :
public std::enable_shared_from_this<Impl> {
1378 template<
typename PROXY,
typename MODULE,
typename PROPS, Model::VertexProperties::Type TYPE>
1389 template<
typename MODULE>
1390 void genericRemove(MODULE& module);
1407 template<
typename PROXY>
1410 template<
typename... Args>
1411 constexpr auto getFilteredGraph(Args... config)
const;
1413 template<
typename VISITOR,
typename... Args>
1414 auto visit(
Vertex startVertex, VISITOR visitor, Args... args);
1416 [[nodiscard]] std::string getFullyQualifiedPath(
Vertex vertex);
1418 template<
typename VISITOR,
typename PROXY>
1419 bool visitByPath(std::string_view path, VISITOR visitor, PROXY startProxy);
1425 std::atomic<size_t> _graphVisitingLevel{0};
1450 template<
typename VISITOR,
typename... Args>
1452 return _d->impl->visit(
_d->vertex, visitor, args...);
1461 template<
typename... Args>
1462 constexpr auto Impl::getFilteredGraph(Args... config)
const {
1470 [[maybe_unused]] std::function edgeFilterFunctor = [
this, edgeFilter](
const Model::Edge& e) ->
bool {
1472 return edgeFilter.evalEdgeFilter(props);
1476 constexpr bool filterEdges = !std::is_same<
decltype(edgeFilter),
decltype(keepAllEdges)>::value;
1477 if constexpr(filterEdges) {
1478 return boost::filtered_graph(_graph, edgeFilterFunctor);
1489 template<
typename BASE,
typename VISITOR,
typename FILTER,
bool RETURN_FIRST_HIT>
1500 template<
class Vertex,
class Graph>
1509 template<
class Vertex,
class Graph>
1520 template<
class Vertex,
class Graph>
1523 if(!
_filter.evalVertexFilter(g[v])) {
1527 if constexpr(RETURN_FIRST_HIT) {
1528 if constexpr(!std::is_same<detail::VisitorReturnType<VISITOR, FILTER>,
void>::value) {
1559 template<
typename VISITOR,
typename... Args>
1560 auto Impl::visit(
Vertex startVertex, VISITOR visitor, Args... args) {
1562 _graphVisitingLevel++;
1563 auto decrementVisitingLevel = cppext::finally([&] { _graphVisitingLevel--; });
1565 auto filteredGraph = getFilteredGraph(args...);
1571 using type =
typename decltype(typeHolder)::type;
1572 constexpr bool isAdjacent =
1573 std::is_same<AdjacentSearch, type>::value || std::is_base_of<AdjacentSearch, type>::value;
1574 constexpr bool isAdjacentIn =
1575 std::is_same<AdjacentInSearch, type>::value || std::is_base_of<AdjacentInSearch, type>::value;
1576 constexpr bool isAdjacentOut =
1577 std::is_same<AdjacentOutSearch, type>::value || std::is_base_of<AdjacentOutSearch, type>::value;
1578 constexpr bool isDFS =
1579 std::is_same<DepthFirstSearch, type>::value || std::is_base_of<DepthFirstSearch, type>::value;
1580 constexpr bool isBFS =
1581 std::is_same<BreadthFirstSearch, type>::value || std::is_base_of<BreadthFirstSearch, type>::value;
1589 auto visitorWrapper = [&](
auto proxy) -> VisitorReturnType {
1590 if constexpr(vertexFilter.template constevalObjecttype<decltype(proxy)>()) {
1591 return visitor(proxy);
1595 throw ChimeraTK::logic_error(
"We should never end up here...");
1598 if constexpr(isAdjacent || isAdjacentOut) {
1599 auto [start,
end] = boost::out_edges(startVertex, filteredGraph);
1600 for(
auto it = start; it !=
end; ++it) {
1602 auto vtx = target(*it, _graph);
1605 if(!vertexFilter.evalVertexFilter(_graph[vtx])) {
1609 if constexpr(returnFirst) {
1610 return filteredGraph[vtx].visitProxy(visitorWrapper, vtx, shared_from_this());
1613 filteredGraph[vtx].visitProxy(visitorWrapper, vtx, shared_from_this());
1617 if constexpr(isAdjacent || isAdjacentIn) {
1618 auto [start,
end] = boost::in_edges(startVertex, filteredGraph);
1619 for(
auto it = start; it !=
end; ++it) {
1621 auto vtx = source(*it, _graph);
1624 if(!vertexFilter.evalVertexFilter(_graph[vtx])) {
1628 if constexpr(returnFirst) {
1629 return filteredGraph[vtx].visitProxy(visitorWrapper, vtx, shared_from_this());
1632 filteredGraph[vtx].visitProxy(visitorWrapper, vtx, shared_from_this());
1636 if constexpr(isDFS || isBFS) {
1641 if constexpr(continueDisjunct || isBFS) {
1642 stopVertex = std::numeric_limits<Vertex>::max();
1645 stopVertex = startVertex;
1651 VisitOrder visitOrder;
1652 if constexpr(hasVisitOrderOption) {
1655 auto visitorObjectFactory = [&]() {
1656 if constexpr(isDFS) {
1657 return detail::VisitorHelper<boost::dfs_visitor<>,
decltype(visitorWrapper),
decltype(vertexFilter),
1658 returnFirst>(visitorWrapper, shared_from_this(), vertexFilter, stopVertex, rv, visitOrder);
1661 return detail::VisitorHelper<boost::bfs_visitor<>,
decltype(visitorWrapper),
decltype(vertexFilter),
1662 returnFirst>(visitorWrapper, shared_from_this(), vertexFilter, stopVertex, rv, visitOrder);
1665 auto visitorObject = visitorObjectFactory();
1681 std::map<Vertex, boost::default_color_type> colors;
1682 boost::associative_property_map color_map(colors);
1686 if constexpr(isDFS) {
1687 boost::depth_first_search(filteredGraph, visitorObject, color_map, startVertex);
1690 boost::queue<Vertex> buffer;
1691 boost::breadth_first_search(filteredGraph, startVertex, buffer, visitorObject, color_map);
1694 catch(
typename decltype(visitorObject)::StopException&) {
1698 if constexpr(returnFirst) {
1699 return visitorObject.getReturnValue();
1703 if constexpr(returnFirst) {
1711 template<
typename VISITOR,
typename PROXY>
1712 bool Impl::visitByPath(std::string_view path, VISITOR visitor, PROXY startProxy) {
1718 while(boost::starts_with(path,
"./")) {
1719 path = path.substr(2);
1723 if(path.empty() || path ==
".") {
1724 visitor(startProxy);
1729 if(boost::starts_with(path,
"../") || path ==
"..") {
1731 path = (path.size() > 2) ? path.substr(3) :
"";
1736 startProxy._d->vertex,
1738 if constexpr(isDirectory(parent) || isRoot(parent)) {
1739 found = parent.visitByPath(path, visitor);
1750 if(boost::starts_with(path,
"/")) {
1752 path = path.substr(1);
1760 auto slash = path.find_first_of(
'/');
1761 auto childName = path.substr(0, slash);
1762 path = (slash < path.length() - 1) ? path.substr(slash + 1) :
"";
1767 startProxy._d->vertex,
1769 if constexpr(isDirectory(child) || isVariable(child)) {
1770 if(child.getName() == childName) {
1771 found = child.visitByPath(path, visitor);
1788 template<
typename... Args>
1789 void RootProxy::writeGraphViz(
const std::string& filename, Args... args)
const {
1790 auto filteredGraph = _d->impl->getFilteredGraph(args...);
1794 std::ofstream of(filename);
1796 auto vertexPropWriter = [&](std::ostream& out,
const auto& vtx) {
1798 if(!vertexFilter.evalVertexFilter(_d->impl->_graph[vtx])) {
1802 _d->impl->_graph[vtx].visit([&](
auto prop) {
1807 out << R
"(label="/")";
1810 out << R
"(label=")" << prop.aliasOrCdd << R"(")";
1813 out << R
"(label=")" << prop.name << R"(")";
1816 throw ChimeraTK::logic_error(
"Unexpected VertexProperties type");
1823 out <<
"color=grey,style=filled";
1826 out <<
"color=lightskyblue,style=filled";
1829 out <<
"color=cyan,style=filled";
1832 out <<
"color=springgreen,style=filled";
1835 out <<
"color=yellow,style=filled";
1838 out <<
"color=black";
1841 out <<
"color=peachpuff,style=filled";
1844 throw ChimeraTK::logic_error(
"Unexpected VertexProperties type");
1850 auto edgePropWriter = [&](std::ostream& out,
const auto& edge) {
1852 switch(_d->impl->_graph[edge].type) {
1853 case EdgeProperties::Type::parenthood: {
1854 out <<
"color=red, arrowhead=diamond";
1857 case EdgeProperties::Type::ownership: {
1858 out <<
"color=blue, arrowhead=odot";
1861 case EdgeProperties::Type::pvAccess: {
1862 out <<
"color=black";
1865 case EdgeProperties::Type::neighbourhood: {
1866 out <<
"color=olive, arrowhead=tee";
1869 case EdgeProperties::Type::trigger: {
1870 out <<
"color=grey, arrowhead=crow";
1874 throw ChimeraTK::logic_error(
"Unexpected EdgeProperties type");
1881 std::map<Vertex, size_t> vertex_ids;
1882 for(
auto u : boost::make_iterator_range(vertices(filteredGraph))) {
1883 vertex_ids[u] = vertex_ids.size();
1888 boost::write_graphviz(of, filteredGraph, vertexPropWriter, edgePropWriter, boost::default_writer(),
1889 boost::make_assoc_property_map(vertex_ids));
1894 template<
typename VISITOR>
1895 bool RootProxy::visitByPath(std::string_view path, VISITOR visitor)
const {
1896 return _d->impl->visitByPath(path, visitor, *
this);
1905 template<
typename VISITOR>
1906 bool DirectoryProxy::visitByPath(std::string_view path, VISITOR visitor)
const {
1907 return _d->impl->visitByPath(path, visitor, *
this);
1916 template<
typename VISITOR>
1917 bool ProcessVariableProxy::visitByPath(std::string_view path, VISITOR visitor)
const {
1918 return _d->impl->visitByPath(path, visitor, *
this);
1927 template<
typename VISITOR>
1928 typename std::invoke_result<VISITOR, VertexProperties::ApplicationModuleProperties&>::type VertexProperties::visit(
1929 VISITOR visitor)
const {
1932 return visitor(std::get<RootProperties>(p));
1935 case Type::moduleGroup: {
1936 return visitor(std::get<ModuleGroupProperties>(p));
1939 case Type::applicationModule: {
1940 return visitor(std::get<ApplicationModuleProperties>(p));
1943 case Type::variableGroup: {
1944 return visitor(std::get<VariableGroupProperties>(p));
1947 case Type::deviceModule: {
1948 return visitor(std::get<DeviceModuleProperties>(p));
1951 case Type::processVariable: {
1952 return visitor(std::get<ProcessVariableProperties>(p));
1955 case Type::directory: {
1956 return visitor(std::get<DirectoryProperties>(p));
1962 throw ChimeraTK::logic_error(
"VertexProperties::visit() called for invalid-typed vertex.");
1967 template<
typename PROXY>
1968 PROXY VertexProperties::makeProxy(
Vertex vertex,
const std::shared_ptr<Impl>& impl)
const {
1969 auto proxyShared = _proxy.lock();
1971 proxyShared = std::make_shared<Proxy::ProxyData>(vertex, impl);
1972 _proxy = proxyShared;
1974 return PROXY(proxyShared);
1979 template<
typename VISITOR>
1980 typename std::invoke_result<VISITOR, ApplicationModuleProxy>::type VertexProperties::visitProxy(
1981 VISITOR visitor,
Vertex vertex,
const std::shared_ptr<Impl>& impl)
const {
1984 return visitor(makeProxy<RootProxy>(vertex, impl));
1987 case Type::moduleGroup: {
1988 return visitor(makeProxy<ModuleGroupProxy>(vertex, impl));
1991 case Type::applicationModule: {
1992 return visitor(makeProxy<ApplicationModuleProxy>(vertex, impl));
1995 case Type::variableGroup: {
1996 return visitor(makeProxy<VariableGroupProxy>(vertex, impl));
1999 case Type::deviceModule: {
2000 return visitor(makeProxy<DeviceModuleProxy>(vertex, impl));
2003 case Type::processVariable: {
2004 return visitor(makeProxy<ProcessVariableProxy>(vertex, impl));
2007 case Type::directory: {
2008 return visitor(makeProxy<DirectoryProxy>(vertex, impl));
2011 case Type::invalid: {
2014 throw ChimeraTK::logic_error(
"VertexProperties::visitProxy() called for invalid-typed vertex.");
Adds features required for inversion of control to an accessor.
VariableGroupProxy add(VariableGroup &module)
void remove(VariableGroup &module)
ApplicationModule & getApplicationModule() const
Return the actual ApplicationModule.
const std::string & getName() const
Get the name of the ApplicationModule.
void addVariable(ProcessVariableProxy &variable, VariableNetworkNode &node)
void addVariable(ProcessVariableProxy &variable, VariableNetworkNode &node)
ProcessVariableProxy getTrigger() const
Get the ProcessVariableProxy for the trigger.
DeviceModule & getDeviceModule() const
const std::string & getAliasOrCdd() const
Get the alias or CDD of the device.
ProcessVariableProxy addVariable(const std::string &name)
const std::string & getName() const
Get the name of the Directory.
DirectoryProxy addDirectory(const std::string &name)
bool visitByPath(std::string_view path, VISITOR visitor) const
Resolve the given path and call the visitor for the found object.
DirectoryProxy addDirectoryRecursive(const std::string &name)
Implementation class for the model.
ModuleGroupProxy add(ModuleGroup &module)
const std::string & getName() const
Get the name of the ModuleGroup.
ModuleGroup & getModuleGroup() const
Return the actual ModuleGroup.
void remove(ApplicationModule &module)
Proxy class which does not keep the ownership of the model.
NonOwningProxy & operator=(const ProxyType &owningProxy)
Assign non-owning proxy from (owning) proxy.
ProxyType lock()
Return owning proxy.
NonOwningProxy()=default
Default constructor creates "empty" non-owning proxy which does not contain a valid proxy.
NonOwningProxy(const ProxyType &owningProxy)
Construct non-owning proxy from (owning) proxy.
const std::vector< std::shared_ptr< VariableNetworkNode > > & getNodes() const
Return all VariableNetworkNodes for this variable.
void addTag(const std::string &tag)
Add tag to this PV. Used by VariableNetworkNode to update the model when tags are added to PVs.
const std::string & getName() const
Get the name of the ProcessVariable.
void removeNode(const VariableNetworkNode &node)
Remove VariableNetworkNode from the list of nodes. Note: Will invalidate return value of getNodes()!
const std::unordered_set< std::string > & getTags() const
Return all tags attached to this variable.
bool visitByPath(std::string_view path, VISITOR visitor) const
Resolve the given path and call the visitor for the found object.
Base class for the proxies representing objects in the model.
std::shared_ptr< ProxyData > _d
bool isValid() const
Check if the model is valid.
bool operator==(const Proxy &other) const
auto visit(VISITOR visitor, Args... args) const
Traverse the model using the specified filter and call the visitor functor for each ModuleGroup,...
Proxy()=default
The default constructor creates a dysfunctional, empty proxy.
std::string getFullyQualifiedPath() const
Return the fully qualified path.
Proxy representing the root of the application model.
DirectoryProxy addDirectoryRecursive(const std::string &name)
ModuleGroupProxy add(ModuleGroup &module)
void writeGraphViz(const std::string &filename, Args... args) const
Implementations of RootProxy.
void remove(ApplicationModule &module)
static RootProxy makeRootProxy(const std::shared_ptr< Impl > &impl)
Create RootProxy assuming the application has been created already (i.e.
ProcessVariableProxy addVariable(const std::string &name)
DirectoryProxy addDirectory(const std::string &name)
bool visitByPath(std::string_view path, VISITOR visitor) const
Resolve the given path and call the visitor for the found object.
void remove(VariableGroup &module)
VariableGroupProxy add(VariableGroup &module)
const std::string & getName() const
Get the name of the VariableGroup.
ApplicationModuleProxy getOwningModule() const
Return the owning ApplicationModule (may be indirectly owned in case of nested VariableGroups).
VariableGroup & getVariableGroup() const
Return the actual VariableGroup.
void addVariable(ProcessVariableProxy &variable, VariableNetworkNode &node)
Base class for ApplicationModule and DeviceModule, to have a common interface for these module types.
Class describing a node of a variable network.
constexpr auto findVertexFilterAcceptedProxyType(VERTEX_FILTER vertexFilter)
Helper function to find one of the Proxy types which will be let through by the given vertex filter.
typename std::invoke_result< VISITOR, typename std::invoke_result< decltype(detail::findVertexFilterAcceptedProxyType< VERTEX_FILTER >), VERTEX_FILTER >::type >::type VisitorReturnType
Helper to get the return type of a visitor functor, which accepts any proxy type which can pass the g...
boost::multisetS OutEdgeListType
boost::listS VertexListType
constexpr ReturnFirstHitWithValue< void > returnFirstHit()
Stop the search after the first hit and return.
constexpr bool isDirectory(const PROPERTY_OR_PROXY &)
constexpr bool hasSearchOption()
constexpr auto combinedSearchConfig(FIRST first, MORE... more)
constexpr bool isVariableGroup(const PROPERTY_OR_PROXY &)
constexpr bool isApplicationModule(const PROPERTY_OR_PROXY &)
constexpr bool isDeviceModule(const PROPERTY_OR_PROXY &)
constexpr bool isModuleGroup(const PROPERTY_OR_PROXY &)
constexpr bool hasName(const PROPERTY_OR_PROXY &)
boost::filtered_graph< Graph, std::function< bool(const Edge &)>, boost::keep_all > EdgeFilteredView
Graph::edge_descriptor Edge
constexpr auto getVertexFilter()
constexpr bool isRoot(const PROPERTY_OR_PROXY &)
Predicates to identify the type of a proxy or a properties struct.
constexpr auto getSearchOption()
constexpr auto getSearchType()
constexpr void checkConfigValidity()
boost::adjacency_list< detail::OutEdgeListType, detail::VertexListType, boost::bidirectionalS, VertexProperties, EdgeProperties > Graph
Graph type for the model.
boost::adjacency_list< detail::OutEdgeListType, detail::VertexListType, boost::bidirectionalS >::vertex_descriptor Vertex
constexpr auto getEdgeFilter()
constexpr bool isVariable(const PROPERTY_OR_PROXY &)
InvalidityTracer application module.
constexpr auto operator&&(const FILTER_NEXT_RHS &rhs) const
constexpr auto operator||(const FILTER_NEXT_RHS &rhs) const
bool evalEdgeFilter(const typename FILTER_LHS::PropertiesType &e) const
bool evalVertexFilter(const typename FILTER_LHS::PropertiesType &e) const
constexpr AndSet(FILTER_LHS lhs, FILTER_RHS rhs)
constexpr bool constevalObjecttype() const
constexpr CombinedSearchConfig(A a, B b)
constexpr EdgeFilter(const EdgeFilter< FILTER > &rhs) noexcept=default
constexpr EdgeFilter(EdgeFilter< FILTER > &&rhs) noexcept=default
constexpr auto operator||(const FILTER_RHS &rhs) const
bool evalEdgeFilter(const EdgeProperties &e) const
constexpr auto operator&&(const FILTER_RHS &rhs) const
constexpr EdgeFilter(FILTER filter)
Information to be stored with each edge.
@ 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.
bool pvAccessWithReturnChannel
Can be true only for Type::pvAccess, in which case it indicates the presence of a return channel.
constexpr OrSet(FILTER_LHS lhs, FILTER_RHS rhs)
constexpr auto operator&&(const FILTER_NEXT_RHS &rhs) const
bool evalVertexFilter(const typename FILTER_LHS::PropertiesType &e) const
constexpr auto operator||(const FILTER_NEXT_RHS &rhs) const
constexpr bool constevalObjecttype() const
bool evalEdgeFilter(const typename FILTER_LHS::PropertiesType &e) const
PROPERTIES PropertiesType
The data holding struct for the proxy classes.
std::shared_ptr< Impl > impl
ProxyData(Model::Vertex v, std::shared_ptr< Impl > i)
detail::ValueHolder< T > notFoundValue
Do not use these class definitions, instead use the static instances below.
Do not use these class definitions, instead use the static instances below.
bool evalVertexFilter(const VertexProperties &e) const
constexpr VertexFilter(FILTER filter)
constexpr VertexFilter(const VertexFilter< FILTER > &rhs)=default
constexpr auto operator||(const FILTER_RHS &rhs) const
constexpr bool constevalObjecttype() const
constexpr auto operator&&(const FILTER_RHS &rhs) const
constexpr VertexFilter(VertexFilter< FILTER > &&rhs) noexcept=default
NonOwningProxy< ProcessVariableProxy > trigger
std::vector< std::shared_ptr< VariableNetworkNode > > nodes
std::unordered_set< std::string > tags
This is used to allow default construction of the std::variant.
Information to be stored with each vertex.
VertexProperties & operator=(const VertexProperties &other)
std::variant< InvalidProperties, RootProperties, ModuleGroupProperties, ApplicationModuleProperties, VariableGroupProperties, DeviceModuleProperties, ProcessVariableProperties, DirectoryProperties > p
VertexProperties()=default
std::invoke_result< VISITOR, ApplicationModuleProperties & >::type visit(VISITOR visitor) const
Implementations of VertexProperties.
VertexProperties(const VertexProperties &other)=default
PROXY makeProxy(Vertex vertex, const std::shared_ptr< Impl > &impl) const
std::invoke_result< VISITOR, ApplicationModuleProxy >::type visitProxy(VISITOR visitor, Vertex vertex, const std::shared_ptr< Impl > &impl) const
constexpr VisitOrder()=default
constexpr VisitOrder(VisitOrderType t)
Helper to hold values of an arbitrary type including void (in which case no value is held,...
std::shared_ptr< Impl > _impl
void doVisit(Vertex v, Graph &g)
void discover_vertex(Vertex v, Graph &g)
void finish_vertex(Vertex v, Graph &g)
VisitorHelper(VISITOR &visitor, std::shared_ptr< Impl > impl, FILTER &filter, Vertex stopAfterVertex, ValueHolder< detail::VisitorReturnType< VISITOR, FILTER > > &rv, VisitOrder &visitOrder)
ValueHolder< detail::VisitorReturnType< VISITOR, FILTER > > & _rv