CGRA-ME
Visual.cpp
Go to the documentation of this file.
1 /*******************************************************************************
2  * The software programs comprising "CGRA-ME" and the documentation provided
3  * with them are copyright by its authors and the University of Toronto. Only
4  * non-commercial, not-for-profit use of this software is permitted without ex-
5  * plicit permission. This software is provided "as is" with no warranties or
6  * guarantees of support. See the LICENCE for more details. You should have re-
7  * ceived a copy of the full licence along with this software. If not, see
8  * <http://cgra-me.ece.utoronto.ca/license/>.
9  ******************************************************************************/
10 
11 #include <CGRA/Exception.h>
12 #include <CGRA/Visual.h>
13 #include <CGRA/Module.h>
14 
15 #include <string>
16 #include <set>
17 #include <regex>
18 #include <unordered_set>
19 #include <functional>
20 #include <iostream>
21 #include <fstream>
22 #include <sstream>
23 
24 #include <sys/stat.h>
25 #include <sys/types.h>
26 
27 #define NO_PREVIOUS_CYCLE_INPUT
28 
29 void genCGRAVisual(std::string exe_path, std::shared_ptr<CGRA> cgra, int II)
30 {
31  std::string par_js_path = exe_path + "/../../../src/visual/visual.partial.js";
32  std::ifstream par_js(par_js_path);
33  if(par_js.fail())
34  throw cgrame_visual_error("Failed creating ifstream " + par_js_path);
35  std::string context_f_path = exe_path + "/../../../output/context-div.js";
36  std::ofstream context_f(context_f_path, std::ios::trunc);
37  if(context_f.fail())
38  throw cgrame_visual_error("Failed creating ofstream on " + context_f_path);
39  std::string f_path = exe_path + "/../../../output/CGRA.js";
40  std::ofstream f(f_path, std::ios::trunc);
41  if(f.fail())
42  throw cgrame_visual_error("Failed creating ofstream on " + f_path);
43 
44  const auto& mrrg = cgra->getMRRG(II);;
45  if(!mrrg.initiationInterval())
46  throw cgrame_visual_error("MRRG Is Empty, Unable to Produce Visualization");
47 
48  // Fill context-div.js
49  context_f << "document.write(\"\n";
50  context_f << " <div class=\\\"tab\\\">\\\n";
51  context_f << " <button class=\\\"tablinks\\\" onclick=\\\"showContext(event, 'context_0')\\\" id=\\\"defaultOpen\\\">Context 0</button>\\\n";
52  for(int i = 1; i < mrrg.initiationInterval(); ++i)
53  context_f << " <button class=\\\"tablinks\\\" onclick=\\\"showContext(event, 'context_" << i << "')\\\">Context " << i << "</button>\\\n";
54  context_f << " </div>\\\n";
55  for(int i = 0; i < mrrg.initiationInterval(); ++i)
56  context_f << " <div id=\\\"context_" << i << "\\\" class=\\\"tabcontent\\\"></div>\\\n";
57  context_f << "\");\n";
58 
59  // Create top level function call
60  f << "function draw() {\n";
61  for(int i = 0; i < mrrg.initiationInterval(); ++i)
62  f << " draw_helper(" << i << ");\n";
63  f << "}\n";
64 
65  std::vector<std::set<std::pair<int, std::string>, std::greater<std::pair<int, std::string>>>> cluster_type;
66 
67  int max_module_depth = 0;
68  for(int i = 0; i < mrrg.initiationInterval(); ++i)
69  {
70  cluster_type.push_back(std::set<std::pair<int, std::string>, std::greater<std::pair<int, std::string>>>());
71  for(auto n = mrrg.allNodesByCycle().at(i).begin(); n != mrrg.allNodesByCycle().at(i).end(); n++)
72  {
73  std::string s = n->second->getFullName();
74  int count = 0;
75  std::string::size_type pre_pos = 0;
76  while(true)
77  {
78  auto pos = s.find('.', pre_pos);
79  pre_pos = pos + 1;
80  if(pos == std::string::npos)
81  break;
82  else
83  cluster_type[i].insert(std::make_pair(++count, s.substr(0, pos)));
84  }
85  if(count > max_module_depth)
86  max_module_depth = count;
87  }
88  }
89 
90  //if(max_module_depth <= 4)
91  f << "var clusterColor = [ '#6698FF', '#27CEC8', '#30FF89', '#EEFF99' ];\n";
92  //else
93  //throw cgrame_visual_error("CGRA Architecture Is Too Complicated");
94 
95  // Create cluster types
96  f << "var clusterTypes = [];\n";
97  for(int i = 0; i < mrrg.initiationInterval(); ++i)
98  {
99  f << "clusterTypes.push([\n";
100  for(auto & s : cluster_type[i])
101  {
102  f << "[\"" << s.second << "\", " << s.first << "],\n";
103  }
104  f << "]);\n";
105  }
106 
107 #ifndef NO_PREVIOUS_CYCLE_INPUT
108  std::multimap<MRRGNode*, MRRGNode*> input_nodes;
109 #endif
110 
111  // Create nodes
112  f << "var nodes = [];\n";
113  for(int i = 0; i < mrrg.initiationInterval(); ++i)
114  {
115  std::set<std::string> nodes;
116  f << "nodes.push(new vis.DataSet([\n";
117  for(auto n = mrrg.allNodesByCycle().at(i).begin(); n != mrrg.allNodesByCycle().at(i).end(); n++)
118  {
119  if(n->second->type == MRRG_NODE_FUNCTION)
120  f << "{ id: '" << n->second->getFullName() << "', label: '" << n->second->getFullName() <<"', shape: 'box'},\n";
121  else
122  f << "{ id: '" << n->second->getFullName() << "', label: '" << n->second->getFullName() <<"'},\n";
123  nodes.insert(n->second->getFullName());
124  }
125  // Find nodes not belong in this cycle
126  for(auto n = mrrg.allNodesByCycle().at(i).begin(); n != mrrg.allNodesByCycle().at(i).end(); ++n)
127  {
128  for(auto fanout = n->second->fanout.begin(); fanout != n->second->fanout.end(); fanout++)
129  {
130 #ifndef NO_PREVIOUS_CYCLE_INPUT
131  if((*fanout)->cycle != n->second->cycle) // Input node from previous cycle to the node found
132  input_nodes.insert(std::make_pair(*fanout, n->second));
133 #endif
134  if(nodes.find((*fanout)->getFullName()) == nodes.end())
135  {
136  f << "{ id: '" << **fanout << "' , label: '" << **fanout << "', color: '#B464FF'},\n";
137  nodes.insert((*fanout)->getFullName());
138  }
139  }
140  }
141  f << "]));\n";
142  }
143 
144  // Create edges
145  f << "var edges = [];\n";
146  for(int i = 0; i < mrrg.initiationInterval(); ++i)
147  {
148  f << "edges.push(new vis.DataSet([\n";
149  for(auto n = mrrg.allNodesByCycle().at(i).begin(); n != mrrg.allNodesByCycle().at(i).end(); ++n)
150  {
151  for(auto fanout = n->second->fanout.begin(); fanout != n->second->fanout.end(); fanout++)
152  {
153  f << "{ from: '" << (*(n->second)) << "', to: '" << (**fanout) << "', arrows: 'to' },\n";
154  }
155  }
156  f << "]));\n";
157  }
158 
159 #ifndef NO_PREVIOUS_CYCLE_INPUT
160  for(const auto & input_node : input_nodes)
161  {
162  f << "nodes[" << input_node.first->cycle << "].update([" << "{ id: '" << input_node.second->getFullName() << "', label: '" << input_node.second->getFullName() << "', color: 'grey'}]);\n";
163  f << "edges[" << input_node.first->cycle << "].add([" << "{ from: '" << input_node.second->getFullName() << "', to: '" << input_node.first->getFullName() << "', arrows: 'to' }]);\n";
164  }
165 #endif
166 
167  f << par_js.rdbuf();
168 }
169 
171  std::string exe_path, const MRRG& mrrg,
172  const Mapping& mapping, const MappingGraph& mg,
174 ) {
175  const auto vis_output_dir = exe_path + "/../../../output";
176  const auto vis_src_dir = exe_path + "/../../../src/visual";
177 
178  if (mkdir(vis_output_dir.c_str(), 0777) && errno != EEXIST) {
179  throw cgrame_visual_error(strerror(errno));
180  }
181 
182  std::string par_js_path = vis_src_dir + "/visual.partial.js";
183  std::ifstream par_js(par_js_path);
184  if(par_js.fail())
185  throw cgrame_visual_error("Failed creating ifstream " + par_js_path);
186  std::string context_f_path = vis_output_dir + "/context-div.js";
187  std::ofstream context_f(context_f_path, std::ios::trunc);
188  if(context_f.fail())
189  throw cgrame_visual_error("Failed creating ofstream on " + context_f_path);
190  std::string f_path = vis_output_dir + "/CGRA.js";
191  std::ofstream f(f_path, std::ios::trunc);
192  if(f.fail())
193  throw cgrame_visual_error("Failed creating ofstream on " + f_path);
194  std::string cgra_html_path_out = vis_output_dir + "/CGRA.html";
195  std::ofstream cgra_html_out(cgra_html_path_out, std::ios::trunc);
196  if(cgra_html_out.fail())
197  throw cgrame_visual_error("Failed creating ofstream on " + cgra_html_path_out);
198  std::string cgra_html_path_in = vis_src_dir + "/CGRA.html";
199  std::ifstream cgra_html_in(cgra_html_path_in);
200  if(cgra_html_in.fail())
201  throw cgrame_visual_error("Failed creating ifstream on " + cgra_html_path_in);
202 
203  if(!mrrg.initiationInterval())
204  throw cgrame_visual_error("MRRG Is Empty, Unable to Produce Visualization");
205 
206  // copy CGRA.html
207  cgra_html_out << cgra_html_in.rdbuf();
208 
209  // Fill context-div.js
210  context_f << "document.write(\"\\\n";
211  context_f << " <div class=\\\"tab\\\">\\\n";
212  context_f << " <button class=\\\"tablinks\\\" onclick=\\\"showContext(event, 'context_0')\\\" id=\\\"defaultOpen\\\">Context 0</button>\\\n";
213  for(int i = 1; i < mrrg.initiationInterval(); ++i)
214  context_f << " <button class=\\\"tablinks\\\" onclick=\\\"showContext(event, 'context_" << i << "')\\\">Context " << i << "</button>\\\n";
215  context_f << " <button class=\\\"tablinks\\\" onclick=\\\"showContext(event, 'context_" << mrrg.initiationInterval() << "')\\\">Mapping</button>\\\n";
216 
217  context_f << " </div>\\\n";
218 
219  for(int i = 0; i < mrrg.initiationInterval(); ++i)
220  context_f << " <div id=\\\"context_" << i << "\\\" class=\\\"tabcontent\\\"></div>\\\n";
221  context_f << " <div id=\\\"context_" << mrrg.initiationInterval() << "\\\" class=\\\"tabcontent\\\"></div>\\\n";
222 
223  context_f << "\");\n";
224 
225  // Create top level function call
226  f << "function draw() {\n";
227  for(int i = 0; i <= mrrg.initiationInterval(); ++i)
228  f << " draw_helper(" << i << ");\n";
229  f << "}\n";
230 
231  std::vector<std::set<std::pair<int, std::string>, std::greater<std::pair<int, std::string>>>> cluster_type;
232 
233  //Positions for the clusters
234  std::unordered_map<std::string, std::pair<double, double>> cluster_position;
235 
236  // Determine clusters present from mrrg node names
237  int max_module_depth = 0;
238  for(int i = 0; i < mrrg.initiationInterval(); ++i)
239  {
240  cluster_type.push_back(std::set<std::pair<int, std::string>, std::greater<std::pair<int, std::string>>>());
241  for(auto n = mrrg.allNodesByCycle().at(i).begin(); n != mrrg.allNodesByCycle().at(i).end(); ++n)
242  {
243  std::string s = n->second->getFullName();
244  int count = 0;
245  std::string::size_type pre_pos = 2;
246  while(true)
247  {
248  auto pos = s.find('.', pre_pos);
249  if(pos == std::string::npos)
250  break;
251  else {
252  cluster_type[i].insert(std::make_pair(++count, s.substr(0, pos)));
253 
254  // Get the position information for the submodule
255  auto posInfo = n->second->parent->getSubModulePosition(s.substr(pre_pos, pos-pre_pos));
256 
257  if (posInfo.first) {
258  cluster_position.emplace(s.substr(0, pos), posInfo.second);
259  }
260  }
261  pre_pos = pos + 1;
262  }
263  if(count > max_module_depth)
264  max_module_depth = count;
265  }
266  }
267 
268  //if(max_module_depth <= 4)
269  f << "var clusterColor = [ '#6698FF', '#27CEC8', '#30FF89', '#EEFF99' ];\n";
270  //else
271  //throw cgrame_visual_error("CGRA's Architecture Is Too Complicated");
272 
273  // Create cluster types for full MRRG and their contexts
274  f << "var clusterTypes = [];\n";
275  for(int i = 0; i < mrrg.initiationInterval(); ++i)
276  {
277  f << "clusterTypes.push([\n";
278  for(auto & s : cluster_type[i])
279  {
280  f << "[\"" << s.second << "\", " << s.first;
281 
282  // Position data
283  if (cluster_position.find(s.second) != cluster_position.end()) {
284  // Setting the position of clusters
285  int xPos, yPos;
286  std::tie(xPos, yPos) = getScaledPosition(cluster_position.at(s.second));
287 
288  f << ", " << xPos << ", " << yPos;
289  }
290 
291  f << "],\n";
292  }
293  f << "]);\n";
294  }
295 
296  // Mapping Graph Tab Submodules and Positions
297  f << "clusterTypes.push([\n";
298  for(int i = 0; i < mrrg.initiationInterval(); ++i)
299  {
300  for(auto & s: cluster_type[i])
301  {
302  f << "[\"" << s.second << "\", " << s.first;
303 
304  if (cluster_position.find(s.second) != cluster_position.end()) {
305  // Setting the position of clusters
306  int xPos, yPos;
307  std::tie(xPos, yPos) = getScaledPosition(cluster_position.at(s.second));
308 
309  // Offset for cycle
310  xPos += std::stoi(s.second)*10000;
311  f << ", " << xPos << ", " << yPos;
312  }
313  f << "],\n";
314  }
315  }
316  f << "]);\n";
317 
318 
319 #ifndef NO_PREVIOUS_CYCLE_INPUT
320  std::multimap<MRRGNode*, MRRGNode*> input_nodes;
321 #endif
322 
323  // Create nodes
324  f << "var nodes = [];\n";
325  for(int i = 0; i < mrrg.initiationInterval(); ++i)
326  {
327  std::set<std::string> nodes;
328  f << "nodes.push(new vis.DataSet([\n";
329  for(auto n = mrrg.allNodesByCycle().at(i).begin(); n != mrrg.allNodesByCycle().at(i).end(); ++n)
330  {
331  std::string name = n->second->getFullName();
332  if(n->second->type == MRRG_NODE_FUNCTION)
333  f << "{ id: '" << name << "', label: '" << name <<"', shape: 'box'";
334  else
335  f << "{ id: '" << name << "', label: '" << name << "'";
336  nodes.insert(name);
337 
338  auto dotIndex = name.rfind(".");
339  std::pair<bool, std::pair<double, double>> posInfo;
340  if (dotIndex != std::string::npos) {
341  posInfo = n->second->parent->getNodePosition(name.substr(dotIndex+1));
342  }
343  else {
344  auto colon_index = name.find(":");
345  posInfo = n->second->parent->getNodePosition(name.substr(colon_index+1));
346  }
347 
348  if (posInfo.first) {
349  int xPos, yPos;
350  std::tie(xPos, yPos) = getScaledPosition(posInfo.second);
351 
352  f << ", x:" << xPos << ", y:" << yPos << ", physics:false, fixed:true";
353  }
354 
355  f << "},\n";
356  }
357  for(auto n = mrrg.allNodesByCycle().at(i).begin(); n != mrrg.allNodesByCycle().at(i).end(); ++n)
358  {
359  for(auto fanout = n->second->fanout.begin(); fanout != n->second->fanout.end(); fanout++)
360  {
361 #ifndef NO_PREVIOUS_CYCLE_INPUT
362  if((*fanout)->cycle != n->second->cycle) // Input node from previous cycle to the node found
363  input_nodes.insert(std::make_pair(*fanout, n->second));
364 #endif
365  std::string name = (*fanout)->getFullName();
366  if(nodes.find(name) == nodes.end()) {
367  f << "{ id: '" << **fanout << "' , label: '" << **fanout << "', color: '#B464FF'";
368 
369  int xPos, yPos;
370  auto posInfo = (*fanout)->parent->getNodePosition(name.substr(name.rfind(".")+1));
371 
372  if (posInfo.first) {
373  std::tie(xPos, yPos) = getScaledPosition(posInfo.second);
374  // Offset for cycle, should probably match the screen width in the getScaledPosition function
375  if (std::stoi(name) != i) {
376  xPos += (i-std::stoi(name))*200;
377  }
378 
379  f << ", x:" << xPos << ", y:" << yPos << ", physics:false, fixed:true";
380  }
381  f << "},\n";
382  }
383  }
384  }
385  f << "]));\n";
386  }
387 
388  // Create nodes for mapping graph
389  f << "nodes.push(new vis.DataSet([\n";
390  for (const auto& mg_ndesc_and_node : mg.allNodes()) {
391  const auto& mg_node = mg.getNodeRef(mg_ndesc_and_node.first);
392  const auto& mrrg_node = mrrg.getNodeRef(toMRRG.at(mg_ndesc_and_node.first));
393  const auto& name = mrrg_node.getFullName();
394  // Nodes will have labels of: mrrg name | opgraph op
395  f << "{ id: '" << name << "|" << mg_node.op_node_desc->getName() << "', label: '" << name << "|" << mg_node.op_node_desc->getName() << "'";
396 
397  if (mrrg_node.parent->getName().find("mux") != std::string::npos) {
398  f << ", image: '../src/visual/Multiplexer.png', shape: 'image'";
399  }
400  else if (mrrg_node.parent->getName().find("ALU") != std::string::npos || mrrg_node.parent->getName().find("func") != std::string::npos) {
401  f << ", image: '../src/visual/ALU.png', shape: 'image'";
402  }
403  else if (mrrg_node.type == MRRG_NODE_FUNCTION) {
404  f <<", shape: 'box'";
405  }
406 
407  int xPos = -1, yPos = -1;
408  auto dotIndex = name.rfind(".");
409  std::pair<bool, std::pair<double, double>> posInfo;
410  if (dotIndex != std::string::npos) {
411  posInfo = mrrg_node.parent->getNodePosition(name.substr(dotIndex+1));
412  }
413  else {
414  posInfo = mrrg_node.parent->getNodePosition(name.substr(2));
415  }
416 
417  if (posInfo.first) {
418  std::tie(xPos, yPos) = getScaledPosition(posInfo.second);
419  // Offset for cycle, should probably match the screen width in the getScaledPosition function
420  xPos += std::stoi(name)*10000;
421 
422  f << ", x:" << xPos << ", y:" << yPos << ", physics:false, fixed:true";
423  }
424 
425  f << ", color:";
426  if (mrrg_node.type == MRRG_NODE_FUNCTION && xPos != -1) {
427  f << "'skyblue'";
428  }
429  else if (mrrg_node.type == MRRG_NODE_FUNCTION) {
430  f << "'coral'";
431  }
432  else {
433  f << "'cornsilk'";
434  }
435 
436  f <<"},\n";
437  }
438  f << "]));\n";
439 
440  // Create edges
441  f << "var edges = [];\n";
442  for(int i = 0; i < mrrg.initiationInterval(); ++i)
443  {
444  f << "edges.push(new vis.DataSet([\n";
445  for(auto n = mrrg.allNodesByCycle().at(i).begin(); n != mrrg.allNodesByCycle().at(i).end(); ++n)
446  {
447  for(auto fanout = n->second->fanout.begin(); fanout != n->second->fanout.end(); fanout++)
448  {
449  f << "{ from: '" << (*(n->second)) << "', to: '" << (**fanout) << "', arrows: 'to' },\n";
450  }
451  }
452  f << "]));\n";
453  }
454 
455  // Create edges for the mapping
456  f << "edges.push(new vis.DataSet([\n";
457  for (auto & node: mg.allNodes()) {
458  for (auto & fanout: mg.fanout(node.first)) {
459  f << "{ from: '" << mrrg.getNodeRef(toMRRG.at(node.first)).getFullName() << "|" << mg.getNodeRef(node.first).op_node_desc->getName() << "', to: '" << mrrg.getNodeRef(toMRRG.at(fanout)).getFullName() << "|" << mg.getNodeRef(fanout).op_node_desc->getName() << "', arrows: 'to' },\n";
460  }
461  }
462  f << "]));\n";
463 
464 #ifndef NO_PREVIOUS_CYCLE_INPUT
465  for(const auto & input_node : input_nodes)
466  {
467  f << "nodes[" << input_node.first->cycle << "].update([" << "{ id: '" << input_node.second->getFullName() << "', label: '" << input_node.second->getFullName() << "', color: 'grey'}]);\n";
468  f << "edges[" << input_node.first->cycle << "].add([" << "{ from: '" << input_node.second->getFullName() << "', to: '" << input_node.first->getFullName() << "', arrows: 'to' }]);\n";
469  }
470 #endif
471 
472  f << "var nodeMapped = []; while(nodeMapped.push([]) <= " << mrrg.initiationInterval() << ");\n";
473  for(const auto & map : mapping.getMapping())
474  {
475  for(const auto & mrrg_node : map.second)
476  {
477  f << "nodeMapped[" << mrrg.getNodeRef(mrrg_node).cycle << "].push({ id: '" << mrrg.getNodeRef(mrrg_node).getFullName() << "', color: 'red', title: '" << map.first->name << "' });\n";
478  }
479  }
480 
481  for (auto & node: mg.allNodes()) {
482  f << "nodeMapped[" << mrrg.initiationInterval() << "].push({ id: '" << mrrg.getNodeRef(toMRRG.at(node.first)).getFullName() << "|" << mg.getNodeRef(node.first).op_node_desc->getName() << "', title: '" << mrrg.getNodeRef(toMRRG.at(node.first)).getFullName() << "|" << mg.getNodeRef(node.first).op_node_desc->getName() << "' });\n";
483  }
484 
485  f << par_js.rdbuf();
486 }
487 
491 std::pair<double, double> getScaledPosition(const std::pair<double, double> pos) {
492  int width = 10000;
493  int height = 10000;
494  return {pos.first*width, pos.second*height};
495 }
496 
MRRGNode::getFullName
std::string getFullName() const
Definition: MRRG.cpp:569
Visual.h
MRRG::allNodesByCycle
const auto & allNodesByCycle() const
Definition: MRRG.h:357
MRRG
Definition: MRRG.h:216
getScaledPosition
std::pair< double, double > getScaledPosition(const std::pair< double, double > pos)
Definition: Visual.cpp:491
Mapping::getMapping
std::map< OpGraph::NodeDescriptor, std::vector< MRRG::NodeDescriptor > > & getMapping()
Definition: Mapping.h:47
CreateMappingGraphResult::ToMRRG
MappingGraph::ToMRRGVertexMap ToMRRG
Definition: Mapping.h:247
Module.h
MappingGraphNode::op_node_desc
OpGraph::NodeDescriptor op_node_desc
Definition: Mapping.h:126
MappingGraph
Definition: Mapping.h:129
set
dot tab cc set(dot_y_cc ${gen_dir}/dot.y.cc) if(BISON_FOUND) bison_target(dotparser_parser dot.y $
Definition: CMakeLists.txt:8
genMappingVisual
void genMappingVisual(std::string exe_path, const MRRG &mrrg, const Mapping &mapping, const MappingGraph &mg, const CreateMappingGraphResult::ToMRRG &toMRRG)
Definition: Visual.cpp:170
MappingGraph::allNodes
auto & allNodes() const
Definition: Mapping.h:234
Mapping
Definition: Mapping.h:31
Exception.h
MRRGNode::cycle
unsigned int cycle
Definition: MRRG.h:127
MRRG::getNodeRef
MRRGNode & getNodeRef(NodeDescriptor ndesc)
Definition: MRRG.h:273
MRRG_NODE_FUNCTION
@ MRRG_NODE_FUNCTION
Definition: MRRG.h:46
MappingGraph::fanout
auto & fanout(NodeDescriptor ndesc) const
Definition: Mapping.h:201
cgrame_visual_error
Definition: Exception.h:34
genCGRAVisual
void genCGRAVisual(std::string exe_path, std::shared_ptr< CGRA > cgra, int II)
Definition: Visual.cpp:29
MRRG::initiationInterval
int initiationInterval() const
Definition: MRRG.h:346
MappingGraph::getNodeRef
MappingGraphNode & getNodeRef(NodeDescriptor ndesc)
Definition: Mapping.cpp:359
OpGraphNode::getName
const std::string & getName() const
Definition: OpGraph.h:121