NRP Core  1.4.1
output_edge.h
Go to the documentation of this file.
1 /* * NRP Core - Backend infrastructure to synchronize simulations
2  *
3  * Copyright 2020-2023 NRP Team
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  * http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  *
17  * This project has received funding from the European Union’s Horizon 2020
18  * Framework Programme for Research and Innovation under the Specific Grant
19  * Agreement No. 945539 (Human Brain Project SGA3).
20  */
21 
22 #ifndef OUTPUT_EDGE_H
23 #define OUTPUT_EDGE_H
24 
28 
29 template<class T, class T_OUT>
30 concept OUTPUT_C = std::is_base_of_v<OutputNode<T_OUT>, T>;
31 
35 template <class T_IN, class T_OUT, OUTPUT_C<T_OUT> OUTPUT_CLASS>
36 class OutputEdge {
37 
38 public:
39 
40  OutputEdge() = delete;
41 
45  OutputEdge(std::string keyword, std::string id, std::string port,
46  bool publishFromCache,
47  unsigned int computePeriod) :
48  _keyword(std::move(keyword)), _id(std::move(id)), _port(std::move(port)),
49  _publishFromCache(publishFromCache),
50  _computePeriod(computePeriod)
51  {}
52 
59  boost::python::object pySetup(const boost::python::object& obj)
60  {
61  // Register output node
62  std::shared_ptr<ComputationalNode> node(makeNewNode());
64  OUTPUT_CLASS* oNode = dynamic_cast<OUTPUT_CLASS*>(node.get());
65  if(!oNode)
66  throw NRPException::logCreate("When creating Output Node \""+node->id()+"\": a node with the same name "
67  "was already registered with a different type");
68 
69  // Register edge
70  if(boost::python::extract<std::shared_ptr<PythonFunctionalNode>>(obj).check()) {
71  // PythonFunctionalNode case
72  std::shared_ptr<PythonFunctionalNode> pyFn = boost::python::extract<std::shared_ptr<PythonFunctionalNode>>(obj);
73  registerEdgePythonFN(oNode, pyFn);
74  }
75  else if(boost::python::extract<std::shared_ptr<FunctionalNodeBase>>(obj).check()) {
76  // FunctionalNodeBase case
77  std::shared_ptr<FunctionalNodeBase> pyFn = boost::python::extract<std::shared_ptr<FunctionalNodeBase>>(obj);
78  registerEdgeFNBase(oNode, pyFn);
79  }
80  else
81  throw NRPException::logCreate("OutputEdge received the wrong object type");
82 
83  // Returns FunctionalNode
84  return obj;
85  }
86 
87 protected:
88 
92  void registerEdgePythonFN(OUTPUT_CLASS* oNode, std::shared_ptr<PythonFunctionalNode>& pyFn)
93  {
94  try {
95  InputPort<boost::python::object, T_OUT> *iPort = oNode->template getOrRegisterInput<boost::python::object>(
96  _port);
97 
98  // Check that the edge can be created before trying
99  checkPortCanConnectOrThrow<boost::python::object>(iPort, oNode);
100 
101  // Register edge
102  ComputationalGraphManager::getInstance().registerEdge<boost::python::object, T_OUT>(
103  pyFn->getOutput(_keyword), iPort);
104  }
105  catch (const NRPException&) {
106  std::string error_msg = "An error occurred while creating graph edge to output node \"" + oNode->id() + "\"";
107  throw NRPException::logCreate(error_msg);
108  }
109  }
110 
114  void registerEdgeFNBase(OUTPUT_CLASS* oNode, const std::shared_ptr<FunctionalNodeBase>& pyFn)
115  {
116  try {
117  InputPort<T_IN, T_OUT> *iPort = oNode->template getOrRegisterInput<T_IN>(
118  _port);
119 
120  // Check that the edge can be created before trying
121  checkPortCanConnectOrThrow<T_IN>(iPort, oNode);
122 
123  // Register edge
124  OutputPort<T_IN>* oPort = dynamic_cast<OutputPort<T_IN>*>(pyFn->getOutputById(_keyword));
125  if(!oPort)
126  throw NRPException::logCreate("Port \"" + _keyword + "\" of Node \"" + pyFn->id() +
127  "\" can't be connected to port \"" + _port + "\" of Node " + oNode->id() +
128  "\". Probably there is a mismatch between the port types. Review your CG configuration.");
129 
131  oPort, iPort);
132  }
133  catch (const NRPException&) {
134  std::string error_msg = "An error occurred while creating graph edge to output node \"" + oNode->id() + "\"";
135  throw NRPException::logCreate(error_msg);
136  }
137  }
138 
139  // Check that the port can be connected or throw an exception
140  template<class T_INPUT>
141  void checkPortCanConnectOrThrow(InputPort<T_INPUT, T_OUT> *iPort,OUTPUT_CLASS* oNode)
142  {
143  std::string error_info = "";
144  if(!iPort)
145  error_info = "port couldn't be found";
146  else if(iPort->subscriptionsSize() == iPort->subscriptionsMax()) {
147  error_info = "the maximum limit of connections for this port has been reached";
148  if(oNode->getComputePeriod() != 1)
149  error_info= "the node has been configured with a compute period different than one and can only have one connection per port";
150  else if(oNode->publishFromCache())
151  error_info = "the node has been configured to publish from cache and can only have one connection per port";
152  }
153 
154  if(!error_info.empty()) {
155  std::string msg = "Error when creating graph edge to output node \"" + oNode->id() + "\", port \"" + _port + "\": " +
156  error_info + ". Check your graph definition.";
157 
158  throw NRPException::logCreate(msg);
159  }
160  }
161 
167  virtual OUTPUT_CLASS* makeNewNode() = 0;
168 
169  std::string _keyword;
170  std::string _id;
171  std::string _port;
173  unsigned int _computePeriod;
174 };
175 
176 template <class T_OUT, OUTPUT_C<T_OUT> OUTPUT_CLASS>
178 
179 
180 #endif //OUTPUT_EDGE_H
ComputationalGraphManager::registerEdge
void registerEdge(OutputPort< T_IN > *source, InputPort< T_IN, T_OUT > *target)
Connects an InputPort to an Output port and registers an edge in the graph between their parent nodes...
Definition: computational_graph_manager.h:107
NRPException
Base NRPException class.
Definition: nrp_exceptions.h:36
OutputEdge::_computePeriod
unsigned int _computePeriod
Definition: output_edge.h:173
OutputEdge::_publishFromCache
bool _publishFromCache
Definition: output_edge.h:172
OutputEdge::_id
std::string _id
Definition: output_edge.h:170
OutputEdge::OutputEdge
OutputEdge()=delete
ComputationalGraphManager::registerNode
void registerNode(std::shared_ptr< ComputationalNode > &obj)
Register a node in the graph.
Definition: computational_graph_manager.h:75
OutputEdge::pySetup
boost::python::object pySetup(const boost::python::object &obj)
call function in the decorator
Definition: output_edge.h:59
OutputEdge::OutputEdge
OutputEdge(std::string keyword, std::string id, std::string port, bool publishFromCache, unsigned int computePeriod)
Constructor.
Definition: output_edge.h:45
OutputEdge::checkPortCanConnectOrThrow
void checkPortCanConnectOrThrow(InputPort< T_INPUT, T_OUT > *iPort, OUTPUT_CLASS *oNode)
Definition: output_edge.h:141
OutputEdge::_port
std::string _port
Definition: output_edge.h:171
output_node.h
OutputEdge::registerEdgePythonFN
void registerEdgePythonFN(OUTPUT_CLASS *oNode, std::shared_ptr< PythonFunctionalNode > &pyFn)
registers an edge between oNode and pyFn. PythonFunctionalNode case
Definition: output_edge.h:92
OutputEdge
Helper class used to implement Python output edge decorators.
Definition: output_edge.h:36
ComputationalGraphManager::getInstance
static ComputationalGraphManager & getInstance()
Get singleton instance of ComputationalGraphManager.
Definition: computational_graph_manager.cpp:31
InputPort::subscriptionsMax
size_t subscriptionsMax()
Return the number ports this port is subscribed to.
Definition: input_port.h:81
OutputEdge::makeNewNode
virtual OUTPUT_CLASS * makeNewNode()=0
computational_graph_manager.h
NRPException::logCreate
static EXCEPTION logCreate(LOG_EXCEPTION_T &exception, const std::string &msg, NRPLogger::spdlog_out_fcn_t spdlogCall=NRPLogger::critical)
Definition: nrp_exceptions.h:73
OutputPort
Implementation of an output port in the computation graph.
Definition: output_port.h:36
InputPort
Implementation of an input port in the computation graph.
Definition: input_port.h:42
InputPort::subscriptionsSize
size_t subscriptionsSize() override
Return the number ports this port is subscribed to.
Definition: input_port.h:75
python_json_engine.port
port
Definition: python_json_engine.py:197
OutputEdge::registerEdgeFNBase
void registerEdgeFNBase(OUTPUT_CLASS *oNode, const std::shared_ptr< FunctionalNodeBase > &pyFn)
registers an edge between oNode and pyFn. FunctionalNodeBase case
Definition: output_edge.h:114
functional_node.h
OutputEdge::_keyword
std::string _keyword
Definition: output_edge.h:169
OUTPUT_C
concept OUTPUT_C
Definition: output_edge.h:30