chigraph  master
Systems programming language written for beginners in LLVM
NodeInstance.cpp
Go to the documentation of this file.
1 
3 #include "chi/NodeInstance.hpp"
4 #include "chi/DataType.hpp"
6 #include "chi/GraphFunction.hpp"
7 #include "chi/GraphModule.hpp"
8 #include "chi/NodeType.hpp"
9 #include "chi/Support/Result.hpp"
10 
11 #include <cassert>
12 
13 namespace chi {
14 NodeInstance::NodeInstance(GraphFunction* func, std::unique_ptr<NodeType> nodeType, float posX,
15  float posY, boost::uuids::uuid nodeID)
16  : mType{std::move(nodeType)},
17  mX{posX},
18  mY{posY},
19  mId{nodeID},
20  mContext{&mType->context()},
21  mFunction{func},
22  mGraphModule{&func->module()} {
23  assert(mType != nullptr && mFunction != nullptr);
24 
25  mType->mNodeInstance = this;
26 
27  inputDataConnections.resize(type().dataInputs().size(), {nullptr, ~0ull});
28  outputDataConnections.resize(type().dataOutputs().size(), {});
29 
30  if (!type().pure()) {
31  inputExecConnections.resize(type().execInputs().size(), {});
32  outputExecConnections.resize(type().execOutputs().size(), {nullptr, ~0ull});
33  }
34 }
35 
36 NodeInstance::NodeInstance(const NodeInstance& other, boost::uuids::uuid id)
37  : mType(other.type().clone()),
38  mX{other.x()},
39  mY{other.y()},
40  mId{id},
41  mContext{&other.context()},
42  mFunction{&other.function()} {
43  assert(mType != nullptr && mFunction != nullptr);
44 
45  mType->mNodeInstance = this;
46 
47  inputDataConnections.resize(type().dataInputs().size(), {nullptr, ~0ull});
48  outputDataConnections.resize(type().dataOutputs().size(), {});
49 
50  inputExecConnections.resize(type().execInputs().size(), {});
51  outputExecConnections.resize(type().execOutputs().size(), {nullptr, ~0ull});
52 }
53 
54 NodeInstance::~NodeInstance() = default;
55 
56 void NodeInstance::setType(std::unique_ptr<NodeType> newType) {
58 
59  // delete exec connections that are out of range
60  // start at one past the end
61  for (size_t id = newType->execInputs().size(); id < inputExecConnections.size(); ++id) {
62  while (!inputExecConnections[id].empty()) {
63  assert(inputExecConnections[id][0].first); // should never fail...
64  auto res = disconnectExec(*inputExecConnections[id][0].first,
65  inputExecConnections[id][0].second);
66 
67  assert(res.mSuccess);
68  }
69  }
70  inputExecConnections.resize(newType->execInputs().size());
71 
72  for (size_t id = newType->execOutputs().size(); id < outputExecConnections.size(); ++id) {
73  auto& conn = outputExecConnections[id];
74  if (conn.first != nullptr) { disconnectExec(*this, id); }
75  }
76  outputExecConnections.resize(newType->execOutputs().size(), std::make_pair(nullptr, ~0ull));
77 
78  auto id = 0ull;
79  for (const auto& conn : inputDataConnections) {
80  // if there is no connection, then skip
81  if (conn.first == nullptr) {
82  ++id;
83  continue;
84  }
85 
86  // if we get here, then we have to deal with the connection.
87 
88  // see if we can keep it
89  if (newType->dataInputs().size() > id &&
90  type().dataInputs()[id].type == newType->dataInputs()[id].type) {
91  ++id;
92  continue;
93  }
94 
95  // disconnect if there's a connection and we can't keep it
96  auto res = disconnectData(*conn.first, conn.second, *this);
97 
98  assert(res.mSuccess);
99 
100  ++id;
101  }
102  inputDataConnections.resize(newType->dataInputs().size(), std::make_pair(nullptr, ~0ull));
103 
104  id = 0ull;
105  for (const auto& connSlot : outputDataConnections) {
106  // keep the connections if they're still good
107  if (newType->dataOutputs().size() > id &&
108  type().dataOutputs()[id].type == newType->dataOutputs()[id].type) {
109  ++id;
110  continue;
111  }
112 
113  while (!connSlot.empty()) {
114  assert(connSlot[0].first);
115 
116  auto res = disconnectData(*this, id, *connSlot[0].first);
117 
118  assert(res.mSuccess);
119  }
120  ++id;
121  }
122  outputDataConnections.resize(newType->dataOutputs().size());
123 
124  mType = std::move(newType);
125  mType->mNodeInstance = this;
126 }
127 
128 Result connectData(NodeInstance& lhs, size_t lhsConnID, NodeInstance& rhs, size_t rhsConnID) {
129  Result res = {};
130 
131  assert(&lhs.function() == &rhs.function());
132 
133  rhs.module().updateLastEditTime();
134 
135  // make sure the connection exists
136  // the input to the connection is the output to the node
137  if (lhsConnID >= lhs.outputDataConnections.size()) {
138  auto dataOutputs = nlohmann::json::array();
139  for (auto& output : lhs.type().dataOutputs()) {
140  dataOutputs.push_back({{output.name, output.type.qualifiedName()}});
141  }
142 
143  res.addEntry("E22", "Output Data connection doesn't exist in node",
144  {{"Requested ID", lhsConnID},
145  {"Node Type", lhs.type().qualifiedName()},
146  {"Node JSON", rhs.type().toJSON()},
147  {"Node Output Data Connections", dataOutputs}});
148  }
149  if (rhsConnID >= rhs.inputDataConnections.size()) {
150  auto dataInputs = nlohmann::json::array();
151  for (auto& output : rhs.type().dataInputs()) {
152  dataInputs.push_back({{output.name, output.type.qualifiedName()}});
153  }
154 
155  res.addEntry("E23", "Input Data connection doesn't exist in node",
156  {{"Requested ID", rhsConnID},
157  {"Node Type", rhs.type().qualifiedName()},
158  {"Node JSON", rhs.type().toJSON()},
159  {"Node Input Data Connections", dataInputs}});
160  }
161 
162  // if there are errors, back out
163  if (!res) { return res; }
164  // make sure the connection is of the right type
165  if (lhs.type().dataOutputs()[lhsConnID].type != rhs.type().dataInputs()[rhsConnID].type) {
166  res.addEntry("E24", "Connecting data nodes with different types is invalid",
167  {{"Left Hand Type", lhs.type().dataOutputs()[lhsConnID].type.qualifiedName()},
168  {"Right Hand Type", rhs.type().dataInputs()[rhsConnID].type.qualifiedName()},
169  {"Left Node JSON", rhs.type().toJSON()},
170  {"Right Node JSON", rhs.type().toJSON()}});
171  return res;
172  }
173 
174  // if we are replacing a connection, disconnect it
175  if (rhs.inputDataConnections[rhsConnID].first != nullptr) {
176  res += disconnectData(lhs, lhsConnID, rhs);
177  if (!res) { return res; }
178  }
179 
180  lhs.outputDataConnections[lhsConnID].emplace_back(&rhs, rhsConnID);
181  rhs.inputDataConnections[rhsConnID] = {&lhs, lhsConnID};
182 
183  return res;
184 }
185 
186 Result connectExec(NodeInstance& lhs, size_t lhsConnID, NodeInstance& rhs, size_t rhsConnID) {
187  Result res = {};
188 
189  assert(&lhs.function() == &rhs.function());
190 
191  lhs.module().updateLastEditTime();
192 
193  // make sure the connection exists
194  if (lhsConnID >= lhs.outputExecConnections.size()) {
195  auto execOutputs = nlohmann::json::array();
196  for (auto& output : lhs.type().execOutputs()) { execOutputs.push_back(output); }
197 
198  res.addEntry("E22", "Output exec connection doesn't exist in node",
199  {{"Requested ID", lhsConnID},
200  {"Node Type", lhs.type().qualifiedName()},
201  {"Node Output Exec Connections", execOutputs}});
202  }
203  if (rhsConnID >= rhs.inputExecConnections.size()) {
204  auto execInputs = nlohmann::json::array();
205  for (auto& output : rhs.type().execInputs()) { execInputs.push_back(output); }
206 
207  res.addEntry("E23", "Input exec connection doesn't exist in node",
208  {{"Requested ID", lhsConnID},
209  {"Node Type", rhs.type().qualifiedName()},
210  {"Node Input Exec Connections", execInputs}
211 
212  });
213  }
214 
215  if (!res) { return res; }
216  // if we are replacing a connection, disconnect it
217  if (lhs.outputExecConnections[lhsConnID].first != nullptr) {
218  res += disconnectExec(lhs, lhsConnID);
219  if (!res) { return res; }
220  }
221 
222  // connect it!
223  lhs.outputExecConnections[lhsConnID] = {&rhs, rhsConnID};
224  rhs.inputExecConnections[rhsConnID].emplace_back(&lhs, lhsConnID);
225 
226  return res;
227 }
228 
229 Result disconnectData(NodeInstance& lhs, size_t lhsConnID, NodeInstance& rhs) {
230  assert(&lhs.function() == &rhs.function());
231 
232  lhs.module().updateLastEditTime();
233 
234  Result res = {};
235 
236  if (lhsConnID >= lhs.outputDataConnections.size()) {
237  auto dataOutputs = nlohmann::json::array();
238  for (auto& output : lhs.type().dataOutputs()) {
239  dataOutputs.push_back({{output.name, output.type.qualifiedName()}});
240  }
241 
242  res.addEntry("E22", "Output data connection in node doesn't exist",
243  {{"Requested ID", lhsConnID},
244  {"Node Type", lhs.type().qualifiedName()},
245  {"Node JSON", rhs.type().toJSON()},
246  {"Node Output Data Connections", dataOutputs}});
247 
248  return res;
249  }
250 
251  // find the connection
252  auto iter = std::find_if(lhs.outputDataConnections[lhsConnID].begin(),
253  lhs.outputDataConnections[lhsConnID].end(),
254  [&](auto& pair) { return pair.first == &rhs; });
255 
256  if (iter == lhs.outputDataConnections[lhsConnID].end()) {
257  res.addEntry("EUKN", "Cannot disconnect from connection that doesn't exist",
258  {{"Left node ID", lhs.stringId()},
259  {"Right node ID", rhs.stringId()},
260  {"Left dock ID", lhsConnID}});
261 
262  return res;
263  }
264 
265  if (rhs.inputDataConnections.size() <= iter->second) {
266  auto dataInputs = nlohmann::json::array();
267  for (auto& output : rhs.type().dataInputs()) {
268  dataInputs.push_back({{output.name, output.type.qualifiedName()}});
269  }
270 
271  res.addEntry("E23", "Input Data connection doesn't exist in node",
272  {{"Requested ID", iter->second},
273  {"Node Type", rhs.type().qualifiedName()},
274  {"Node JSON", rhs.type().toJSON()},
275  {"Node Input Data Connections", dataInputs}});
276 
277  return res;
278  }
279 
280  if (rhs.inputDataConnections[iter->second] != std::make_pair(&lhs, lhsConnID)) {
281  res.addEntry("EUKN", "Cannot disconnect from connection that doesn't exist",
282  {{"Left node ID", lhs.stringId()}, {"Right node ID", rhs.stringId()}});
283 
284  return res;
285  }
286 
287  // finally actually disconnect it
288  rhs.inputDataConnections[iter->second] = {nullptr, ~0ull};
289  lhs.outputDataConnections[lhsConnID].erase(iter);
290 
291  return res;
292 }
293 
294 Result disconnectExec(NodeInstance& lhs, size_t lhsConnID) {
295  Result res = {};
296 
297  lhs.module().updateLastEditTime();
298 
299  if (lhsConnID >= lhs.outputExecConnections.size()) {
300  auto execOutputs = nlohmann::json::array();
301  for (auto& output : lhs.type().execOutputs()) { execOutputs.push_back(output); }
302 
303  res.addEntry("E22", "Output exec connection doesn't exist in node",
304  {{"Requested ID", lhsConnID},
305  {"Node Type", lhs.type().qualifiedName()},
306  {"Node Output Exec Connections", execOutputs}});
307  }
308 
309  auto& lhsconn = lhs.outputExecConnections[lhsConnID];
310  auto& rhsconns = lhsconn.first->inputExecConnections[lhsconn.second];
311 
312  auto iter = std::find_if(rhsconns.begin(), rhsconns.end(),
313  [&](auto pair) { return pair == std::make_pair(&lhs, lhsConnID); });
314 
315  if (iter == rhsconns.end()) {
316  res.addEntry("EUKN", "Cannot disconnect an exec connection that doesn't connect back",
317  {{"Left node ID", lhs.stringId()}, {"Left node dock id", lhsConnID}});
318  return res;
319  }
320 
321  rhsconns.erase(iter);
322  lhsconn = {nullptr, ~0ull};
323 
324  return res;
325 }
326 
327 } // namespace chi
const std::vector< std::string > & execInputs() const
Get the execution inputs for the node.
Definition: NodeType.hpp:79
std::vector< std::vector< std::pair< NodeInstance *, size_t > > > inputExecConnections
The connections that lead to this node, exec.
Defines functions for validating GraphFunction objects.
this is an AST-like representation of a function in a graph It is used for IDE-like behavior...
void addEntry(const char *ec, const char *overview, nlohmann::json data)
Add a entry to the result, either a warning or an error.
Definition: Result.cpp:52
void setType(std::unique_ptr< NodeType > newType)
Set the type of the node instance.
std::string stringId() const
Get the ID as a string.
Defines the Result class and related functions.
std::vector< std::vector< std::pair< NodeInstance *, size_t > > > outputDataConnections
The connections that lead out of this node, data.
const std::vector< NamedDataType > & dataOutputs() const
Get the data outputs for the node.
Definition: NodeType.hpp:76
NodeType & type()
Get the type of the instance.
An instance of a node.
std::vector< std::pair< NodeInstance *, size_t > > inputDataConnections
The connections that go into this node, data.
GraphModule & module() const
Get the containing GraphModule.
NodeInstance(GraphFunction *func, std::unique_ptr< NodeType > nodeType, float posX, float posY, boost::uuids::uuid nodeID=boost::uuids::random_generator()())
Construct a nodeinstace with a type location and an ID, usually called from GraphFunction::insertNode...
virtual nlohmann::json toJSON() const
Create the JSON necessary to store the object.
Definition: NodeType.hpp:54
const std::vector< std::string > & execOutputs() const
Get the execution outputs for the node.
Definition: NodeType.hpp:82
~NodeInstance()
Destructor.
Result disconnectData(NodeInstance &lhs, size_t lhsConnID, NodeInstance &rhs)
Disconnect a data connection.
Result connectData(NodeInstance &lhs, size_t lhsConnID, NodeInstance &rhs, size_t rhsConnID)
Connects two nodes&#39; data connections.
Result connectExec(NodeInstance &lhs, size_t lhsConnID, NodeInstance &rhs, size_t rhsConnID)
Connects two nodes&#39; exec connections.
GraphFunction & function() const
Get the containing GraphFunction.
Defines the NodeType class.
Defines the DataType class.
void updateLastEditTime(std::time_t newLastEditTime=std::time(nullptr))
Update the last edit time, signifying that it&#39;s been edited.
Definition: ChiModule.hpp:105
std::vector< std::pair< NodeInstance *, size_t > > outputExecConnections
The connections that go out of this node, exec.
const std::vector< NamedDataType > & dataInputs() const
Get the data inputs for the node.
Definition: NodeType.hpp:73
Result disconnectExec(NodeInstance &lhs, size_t lhsConnID)
Disconnect a exec connection.
Defines the GraphModule class.
float x() const
Get the X location of the instance.
The namespace where chigraph lives.
std::string qualifiedName() const
Get the qualified name of the node type, like module.name():name()
Definition: NodeType.cpp:17
boost::uuids::uuid id() const
Get the ID of the instance, unique to the graph.
The result object, used for identifiying errors with good diagnostics.
Definition: Result.hpp:72
Declares the GraphFunction class.
Defines the NodeInstance class and related functions.