Vuo  2.3.2
VuoCompilerDriver.cc
Go to the documentation of this file.
1 
10 #include "VuoCable.hh"
11 #include "VuoCompiler.hh"
12 #include "VuoCompilerDriver.hh"
13 #include "VuoCompilerCable.hh"
16 #include "VuoCompilerNode.hh"
18 #include "VuoCompilerType.hh"
19 #include "VuoComposition.hh"
20 #include "VuoNode.hh"
21 #include "VuoNodeClass.hh"
22 #include "VuoProtocol.hh"
23 #include "VuoPublishedPort.hh"
24 #include "VuoType.hh"
25 
32 VuoCompilerDriver::VuoCompilerDriver(VuoCompiler *compiler, const string &driverAsCompositionString)
33 {
34  this->compiler = compiler;
35  this->parser = VuoCompilerGraphvizParser::newParserFromCompositionString(driverAsCompositionString, compiler);
36  this->compositionString = driverAsCompositionString;
37 }
38 
47 {
48  // For each published output port in driver, make sure the protocol has a published
49  // input port with a matching name and type.
50  vector<VuoPublishedPort *> driverBridgeInputs = parser->getPublishedOutputPorts();
51  for (vector<VuoPublishedPort *>::iterator i = driverBridgeInputs.begin(); i != driverBridgeInputs.end(); ++i)
52  {
53  VuoPublishedPort *driverBridgeInput = (*i);
54  string driverBridgeInputName = driverBridgeInput->getClass()->getName();
55  VuoType *driverBridgeInputType = static_cast<VuoCompilerPublishedPort *>(driverBridgeInput->getCompiler())->getDataVuoType();
56 
57  if (!protocol->hasInputPort(driverBridgeInputName) ||
58  (protocol->getTypeForInputPort(driverBridgeInputName) != driverBridgeInputType->getModuleKey()))
59  {
60  return false;
61  }
62  }
63 
64  // For each published input port in driver, make sure the protocol has a published
65  // output port with a matching name and type.
66  vector<VuoPublishedPort *> driverBridgeOutputs = parser->getPublishedInputPorts();
67  for (vector<VuoPublishedPort *>::iterator i = driverBridgeOutputs.begin(); i != driverBridgeOutputs.end(); ++i)
68  {
69  VuoPublishedPort *driverBridgeOutput = (*i);
70  string driverBridgeOutputName = driverBridgeOutput->getClass()->getName();
71  VuoType *driverBridgeOutputType = static_cast<VuoCompilerPublishedPort *>(driverBridgeOutput->getCompiler())->getDataVuoType();
72 
73  if (!protocol->hasOutputPort(driverBridgeOutputName) ||
74  (protocol->getTypeForOutputPort(driverBridgeOutputName) != driverBridgeOutputType->getModuleKey()))
75  {
76  return false;
77  }
78  }
79 
80  return true;
81 }
82 
90 void VuoCompilerDriver::applyToComposition(VuoCompilerComposition *composition, VuoCompiler *compiler, bool canPublishedInputsBeEdited)
91 {
92  // Copy the driver's contents so the original driver won't be affected.
94 
95  // Add the (non-published) driver nodes to the composition.
96  vector<VuoNode *> nodes = parserCopy->getNodes();
97  for (vector<VuoNode *>::iterator node = nodes.begin(); node != nodes.end(); ++node)
98  {
99  // Give the node an identifier that is different from any nodes in the current composition
100  // and any `Make List` nodes that have just been replaced during a live-coding reload.
101  if ((*node)->hasCompiler())
102  {
103  string nodeIdentifierPrefix = (*node)->getCompiler()->getGraphvizIdentifierPrefix() + "_Driver";
104  composition->setUniqueGraphvizIdentifierForNode(*node, nodeIdentifierPrefix, nodeIdentifierPrefix);
105  }
106 
107  composition->getBase()->addNode(*node);
108  }
109 
110  // Add the (non-published) driver cables to the composition.
111  vector<VuoCable *> cables = parserCopy->getCables();
112  for (vector<VuoCable *>::iterator cable = cables.begin(); cable != cables.end(); ++cable)
113  if (! (*cable)->isPublished())
114  composition->getBase()->addCable(*cable);
115 
116  // Bridge each of the driver's published outputs with the matching composition published input.
117  vector<VuoPublishedPort *> driverBridgeInputs = parserCopy->getPublishedOutputPorts();
118  for (vector<VuoPublishedPort *>::iterator i = driverBridgeInputs.begin(); i != driverBridgeInputs.end(); ++i)
119  {
120  VuoPublishedPort *driverBridgeInput = (*i);
121  string driverBridgeInputName = driverBridgeInput->getClass()->getName();
122  VuoType *driverBridgeInputType = static_cast<VuoCompilerPublishedPort *>(driverBridgeInput->getCompiler())->getDataVuoType();
123 
124  VuoPublishedPort *compositionPublishedInput = composition->getBase()->getPublishedInputPortWithName(driverBridgeInputName);
125  if (compositionPublishedInput)
126  {
127  VuoType *compositionPublishedInputType = static_cast<VuoCompilerPublishedPort *>(compositionPublishedInput->getCompiler())->getDataVuoType();
128  if (compositionPublishedInputType == driverBridgeInputType)
129  {
130  vector<VuoCable *> publishedInputOutgoingCables = compositionPublishedInput->getConnectedCables();
131  set<pair<VuoPort *, VuoNode *> > compositionConnectedPorts;
132  map<pair<VuoPort *, VuoNode *>, bool> eventOnlyConnection;
133 
134  // Re-route the driver published output's incoming cables to each of the internal ports connected
135  // to the composition's corresponding published input.
136  for (vector<VuoCable *>::iterator i = publishedInputOutgoingCables.begin(); i != publishedInputOutgoingCables.end(); ++i)
137  {
138  VuoCable *cable = (*i);
139  VuoPort *toPort = cable->getToPort();
140  VuoNode *toNode = cable->getToNode();
141  compositionConnectedPorts.insert(make_pair(toPort, toNode));
142  eventOnlyConnection[make_pair(toPort, toNode)] = cable->getCompiler()->getAlwaysEventOnly();
143 
144  // Remove the original outgoing cables from the composition's published input.
145  composition->getBase()->removeCable(cable);
146  }
147 
148  // Remove the composition's published input.
149  int index = composition->getBase()->getIndexOfPublishedPort(compositionPublishedInput, true);
150  if (index != -1)
151  composition->getBase()->removePublishedInputPort(index);
152 
153  vector<VuoCable *> driverBridgeInputIncomingCables = driverBridgeInput->getConnectedCables();
154  for (vector<VuoCable *>::iterator i = driverBridgeInputIncomingCables.begin(); i != driverBridgeInputIncomingCables.end(); ++i)
155  {
156  VuoCable *cable = (*i);
157  VuoPort *fromPort = cable->getFromPort();
158  VuoNode *fromNode = cable->getFromNode();
159 
160  // For each of the driver published output's incoming cables, create new cables connecting
161  // that cable's 'From' port to each of the composition's internal ports originally connected
162  // to the composition published input.
163  for (set<pair<VuoPort *, VuoNode *> >::iterator i = compositionConnectedPorts.begin(); i != compositionConnectedPorts.end(); ++i)
164  {
165  VuoPort *toPort = i->first;
166  VuoNode *toNode = i->second;
167 
168  VuoCompilerPort *fromCompilerPort = static_cast<VuoCompilerPort *>(fromPort->getCompiler());
169  VuoCompilerPort *toCompilerPort = static_cast<VuoCompilerPort *>(toPort->getCompiler());
170  VuoCompilerCable *newCable = new VuoCompilerCable(fromNode->getCompiler(), fromCompilerPort, toNode->getCompiler(), toCompilerPort);
171  if (cable->getCompiler()->getAlwaysEventOnly() || eventOnlyConnection[(*i)])
172  newCable->setAlwaysEventOnly(true);
173 
174  composition->getBase()->addCable(newCable->getBase());
175  }
176  }
177  }
178  }
179  }
180 
181  // Bridge each of the driver's published inputs with the matching composition published output.
182  vector<VuoPublishedPort *> driverBridgeOutputs = parserCopy->getPublishedInputPorts();
183  for (vector<VuoPublishedPort *>::iterator i = driverBridgeOutputs.begin(); i != driverBridgeOutputs.end(); ++i)
184  {
185  VuoPublishedPort *driverBridgeOutput = (*i);
186  string driverBridgeOutputName = driverBridgeOutput->getClass()->getName();
187  VuoType *driverBridgeOutputType = static_cast<VuoCompilerPublishedPort *>(driverBridgeOutput->getCompiler())->getDataVuoType();
188 
189  VuoPublishedPort *compositionPublishedOutput = composition->getBase()->getPublishedOutputPortWithName(driverBridgeOutputName);
190  if (compositionPublishedOutput)
191  {
192  VuoType *compositionPublishedOutputType = static_cast<VuoCompilerPublishedPort *>(compositionPublishedOutput->getCompiler())->getDataVuoType();
193  if (compositionPublishedOutputType == driverBridgeOutputType)
194  {
195  vector<VuoCable *> publishedOutputIncomingCables = compositionPublishedOutput->getConnectedCables();
196  set<pair<VuoPort *, VuoNode *> > compositionConnectedPorts;
197  map<pair<VuoPort *, VuoNode *>, bool> eventOnlyConnection;
198 
199  // Re-route the composition published output's incoming cables to each of the internal ports connected
200  // to the driver's corresponding published input.
201  for (vector<VuoCable *>::iterator i = publishedOutputIncomingCables.begin(); i != publishedOutputIncomingCables.end(); ++i)
202  {
203  VuoCable *cable = (*i);
204  VuoPort *fromPort = cable->getFromPort();
205  VuoNode *fromNode = cable->getFromNode();
206  compositionConnectedPorts.insert(make_pair(fromPort, fromNode));
207  eventOnlyConnection[make_pair(fromPort, fromNode)] = cable->getCompiler()->getAlwaysEventOnly();
208 
209  // Remove the original outgoing cables from the composition's published input.
210  composition->getBase()->removeCable(cable);
211  }
212 
213  // Remove the composition's published output.
214  int index = composition->getBase()->getIndexOfPublishedPort(compositionPublishedOutput, false);
215  if (index != -1)
216  composition->getBase()->removePublishedOutputPort(index);
217 
218  vector<VuoCable *> driverBridgeOutputOutgoingCables = driverBridgeOutput->getConnectedCables();
219  for (vector<VuoCable *>::iterator i = driverBridgeOutputOutgoingCables.begin(); i != driverBridgeOutputOutgoingCables.end(); ++i)
220  {
221  VuoCable *cable = (*i);
222  VuoPort *toPort = cable->getToPort();
223  VuoNode *toNode = cable->getToNode();
224 
225  // For each of the driver published input's outgoing cables, create new cables connecting
226  // that cable's 'To' port to each of the composition's internal ports originally connected
227  // to the composition published output.
228  for (set<pair<VuoPort *, VuoNode *> >::iterator i = compositionConnectedPorts.begin(); i != compositionConnectedPorts.end(); ++i)
229  {
230  VuoPort *fromPort = i->first;
231  VuoNode *fromNode = i->second;
232 
233  VuoCompilerPort *fromCompilerPort = static_cast<VuoCompilerPort *>(fromPort->getCompiler());
234  VuoCompilerPort *toCompilerPort = static_cast<VuoCompilerPort *>(toPort->getCompiler());
235  VuoCompilerCable *newCable = new VuoCompilerCable(fromNode->getCompiler(), fromCompilerPort, toNode->getCompiler(), toCompilerPort);
236  if (cable->getCompiler()->getAlwaysEventOnly() || eventOnlyConnection[(*i)])
237  newCable->setAlwaysEventOnly(true);
238 
239  composition->getBase()->addCable(newCable->getBase());
240  }
241  }
242  }
243  }
244  }
245 
246  // For each data published input in the composition that does not have a corresponding published output in the driver,
247  // add an `Allow Changes` or `Allow First Value` node with an incoming data cable from the published input port,
248  // an incoming event cable from the driver's `time` published output port, and an outgoing event cable to each
249  // internal input port connected to the published input port.
250  auto driverBridgeInput = std::find_if(driverBridgeInputs.begin(), driverBridgeInputs.end(),
251  [] (VuoPublishedPort *p) { return p->getClass()->getName() == "time"; });
252  if (driverBridgeInput != driverBridgeInputs.end())
253  {
254  vector<VuoCable *> driverBridgeInputIncomingCables = (*driverBridgeInput)->getConnectedCables();
255 
256  vector<VuoPublishedPort *> compositionPublishedInputs = composition->getBase()->getPublishedInputPorts();
257  for (VuoPublishedPort *compositionPublishedInput : compositionPublishedInputs)
258  {
259  string name = compositionPublishedInput->getClass()->getName();
260  auto matchingDriverBridgeInput = std::find_if(driverBridgeInputs.begin(), driverBridgeInputs.end(),
261  [&name] (VuoPublishedPort *p) { return p->getClass()->getName() == name; });
262  if (matchingDriverBridgeInput != driverBridgeInputs.end())
263  continue;
264 
265  VuoType *type = static_cast<VuoCompilerPublishedPort *>(compositionPublishedInput->getCompiler())->getDataVuoType();
266  if (! type)
267  continue;
268 
269  vector<VuoCable *> publishedInputOutgoingCables = compositionPublishedInput->getConnectedCables();
270 
271  // Workaround for data types that don't support `Allow Changes`: Connect an event cable from the driver's `time`
272  // published output port directly to each internal input port connected to the published port.
273  if (canPublishedInputsBeEdited && ! type->getCompiler()->supportsComparison())
274  {
275  for (VuoCable *driverCable : driverBridgeInputIncomingCables)
276  {
277  for (VuoCable *compositionCable : publishedInputOutgoingCables)
278  {
279  // Event cable: driver output port -> composition input port
280  VuoCompilerNode *fromCompilerNode = driverCable->getFromNode()->getCompiler();
281  VuoCompilerPort *fromCompilerPort = static_cast<VuoCompilerPort *>(driverCable->getFromPort()->getCompiler());
282  VuoCompilerNode *toCompilerNode = compositionCable->getToNode()->getCompiler();
283  VuoCompilerPort *toCompilerPort = static_cast<VuoCompilerPort *>(compositionCable->getToPort()->getCompiler());
284  VuoCompilerCable *cable = new VuoCompilerCable(fromCompilerNode, fromCompilerPort, toCompilerNode, toCompilerPort);
285  cable->setAlwaysEventOnly(true);
286  composition->getBase()->addCable(cable->getBase());
287  }
288  }
289  continue;
290  }
291 
292  // Node: Allow Changes or Allow First Value
293  string allowNodeClassName = (canPublishedInputsBeEdited ? "vuo.event.allowChanges2" : "vuo.event.allowFirstValue");
294  VuoCompilerNodeClass *allowNodeClass = compiler->getNodeClass(allowNodeClassName + "." + type->getModuleKey());
295  VuoNode *allowNode = compiler->createNode(allowNodeClass);
296  string nodeIdentifierPrefix = allowNode->getCompiler()->getGraphvizIdentifierPrefix() + "_Driver";
297  composition->setUniqueGraphvizIdentifierForNode(allowNode, nodeIdentifierPrefix, nodeIdentifierPrefix);
298  composition->getBase()->addNode(allowNode);
299 
300  VuoCompilerPort *allowInputPort = static_cast<VuoCompilerPort *>(allowNode->getInputPorts().at(VuoNodeClass::unreservedInputPortStartIndex)->getCompiler());
301  VuoCompilerPort *allowOutputPort = static_cast<VuoCompilerPort *>(allowNode->getOutputPorts().at(VuoNodeClass::unreservedOutputPortStartIndex)->getCompiler());
302 
303  {
304  // Data cable: composition published input port -> Allow node
305  VuoCompilerPort *fromCompilerPort = static_cast<VuoCompilerPublishedPort *>(compositionPublishedInput->getCompiler());
306  VuoCompilerCable *cable = new VuoCompilerCable(nullptr, fromCompilerPort, allowNode->getCompiler(), allowInputPort);
307  composition->getBase()->addCable(cable->getBase());
308  }
309 
310  for (VuoCable *driverCable : driverBridgeInputIncomingCables)
311  {
312  // Event cable: driver output port -> Allow node
313  VuoCompilerNode *fromCompilerNode = driverCable->getFromNode()->getCompiler();
314  VuoCompilerPort *fromCompilerPort = static_cast<VuoCompilerPort *>(driverCable->getFromPort()->getCompiler());
315  VuoCompilerCable *cable = new VuoCompilerCable(fromCompilerNode, fromCompilerPort, allowNode->getCompiler(), allowInputPort);
316  cable->setAlwaysEventOnly(true);
317  composition->getBase()->addCable(cable->getBase());
318  }
319 
320  for (VuoCable *compositionCable : publishedInputOutgoingCables)
321  {
322  // Event cable: Allow node -> composition input port
323  VuoCompilerNode *toCompilerNode = compositionCable->getToNode()->getCompiler();
324  VuoCompilerPort *toCompilerPort = static_cast<VuoCompilerPort *>(compositionCable->getToPort()->getCompiler());
325  VuoCompilerCable *cable = new VuoCompilerCable(allowNode->getCompiler(), allowOutputPort, toCompilerNode, toCompilerPort);
326  cable->setAlwaysEventOnly(true);
327  composition->getBase()->addCable(cable->getBase());
328  }
329  }
330  }
331 }