DetectorGraph  2.0
graphanalyzer.cpp
Go to the documentation of this file.
1 // Copyright 2017 Nest Labs, Inc.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include "graphanalyzer.hpp"
16 
17 #include <fstream>
18 #include <iostream>
19 #include <sstream>
20 #include <list>
21 #include <typeinfo>
22 
23 #include "nodenameutils.hpp"
24 #include "dglogging.hpp"
25 
26 namespace DetectorGraph
27 {
28 
30 : mGraph(aGraph)
31 , mStringFilter(NodeNameUtils::GetMinimalName)
32 , mLabelWordWrapper(NodeNameUtils::WrapOnCommonEndings)
33 {
34 }
35 
36 void GraphAnalyzer::SetStringFilter(std::string (*aStringFilter)(const std::string&))
37 {
38  mStringFilter = aStringFilter;
39 }
40 
41 void GraphAnalyzer::SetLabelWordWrapper(std::string (*aStringFilter)(const std::string&))
42 {
43  mLabelWordWrapper = aStringFilter;
44 }
45 
46 void GraphAnalyzer::GenerateDotFile(const std::string& aOutFilePath) const
47 {
48  ofstream dotFile;
49  dotFile.open(aOutFilePath.c_str());
50 
51  unsigned int evaluationIndex = 0;
52  if (dotFile.is_open())
53  {
54  dotFile << "digraph GraphAnalyzer {" << endl;
55  dotFile << "\trankdir = \"LR\";" << endl;
56  dotFile << "\tnode[fontname=Helvetica];" << endl;
57  dotFile << "\t//ratio=fill; size=\"17,11\";" << endl;
58  dotFile << GetLegend() << endl;
59  for (std::list< Vertex* >::const_iterator it = mGraph.GetVertices().begin();
60  it != mGraph.GetVertices().end();
61  ++it)
62  {
63  std::string nodeName(GenerateNodeName((*it)->GetName()));
64  std::string nodeLabel(GenerateNodeLabel(nodeName, evaluationIndex++));
65 
66  if ( (*it)->GetVertexType() == Vertex::kTopicVertex)
67  {
68  std::string exposureStyleOverride;
69  BaseTopic* tTopic = static_cast<BaseTopic*>(*it);
70  if (tTopic->GetId() != TopicState::kAnonymousTopicState)
71  {
72  exposureStyleOverride = "peripheries=2, ";
73  }
74  else
75  {
76  exposureStyleOverride = "";
77  }
78 
79  /* If timer topic */
80  if ((*it)->GetFutureInEdges().size() > 0 && (*it)->GetFutureInEdges() == (*it)->GetOutEdges())
81  {
82  dotFile << "\t\"" << nodeName << "\" [label=\"" << nodeLabel << "\",style=filled, shape=box, " << exposureStyleOverride << "color=orange];" << endl;
83  }
84  /* if output topic */
85  else if ((*it)->GetOutEdges().size() == 0)
86  {
87  dotFile << "\t\"" << nodeName << "\" [label=\"" << nodeLabel << "\",style=filled, shape=box, " << exposureStyleOverride << "color=limegreen];" << endl;
88  }
89  /* if input topic */
90  else if ((*it)->GetInEdges().size() == 0)
91  {
92  dotFile << "\t\"" << nodeName << "\" [label=\"" << nodeLabel << "\",style=filled, shape=box, " << exposureStyleOverride << "color=lightblue];" << endl;
93  }
94  else
95  {
96  dotFile << "\t\"" << nodeName << "\" [label=\"" << nodeLabel << "\",style=filled, shape=box, " << exposureStyleOverride << "color=red];" << endl;
97  }
98  }
99  else if ( (*it)->GetVertexType() == Vertex::kDetectorVertex)
100  {
101  dotFile << "\t\"" << nodeName << "\" [label=\"" << nodeLabel << "\", color=blue];" << endl;
102  }
103 
104  for (std::list<Vertex*>::const_iterator outIt = (*it)->GetOutEdges().begin();
105  outIt != (*it)->GetOutEdges().end();
106  ++outIt)
107  {
108  dotFile << "\t\t\"" << nodeName << "\" -> \"" << GenerateNodeName((*outIt)->GetName()) << "\";" << endl;
109  }
110 
111  for (std::list<Vertex*>::const_iterator outIt = (*it)->GetFutureOutEdges().begin();
112  outIt != (*it)->GetFutureOutEdges().end();
113  ++outIt)
114  {
115  dotFile << "\t\t\"" << nodeName << "\" -> \"" << GenerateNodeName((*outIt)->GetName()) << "\" [style=dotted, color=red, constraint=false];" << endl;
116  }
117  }
118 
119  dotFile << "}" << endl;
120 
121  dotFile.close();
122 
123  DG_LOG("GraphViz DOT file created at: %s", aOutFilePath.c_str());
124  }
125 }
126 
128 {
129  DG_LOG("--- VERTICES START --- size = %d", mGraph.GetVertices().size());
130  for (std::list<Vertex*>::const_iterator vertexIt = mGraph.GetVertices().begin();
131  vertexIt != mGraph.GetVertices().end();
132  ++vertexIt)
133  {
134  DG_LOG("Vertex %s", GenerateNodeName((*vertexIt)->GetName()).c_str());
135  }
136  DG_LOG("--- VERTICES END ---");
137 }
138 
140 {
141  for (std::list< Vertex* >::const_iterator it = mGraph.GetVertices().begin();
142  it != mGraph.GetVertices().end();
143  ++it)
144  {
145  if ( (*it)->GetVertexType() == Vertex::kTopicVertex)
146  {
147  if ((*it)->GetInEdges().size() > 1)
148  {
149  BaseTopic* tTopic = static_cast<BaseTopic*>(*it);
150  if (tTopic->GetId() != TopicState::kAnonymousTopicState)
151  {
152  DG_LOG("Topic %s has two inputs and is public", (*it)->GetName());
153  return true;
154  }
155  } // LCOV_EXCL_LINE
156  }
157  }
158  return false;
159 }
160 
161 std::string GraphAnalyzer::GenerateNodeName(const char* aCompilerName) const
162 {
163  std::string retString = std::string(aCompilerName);
164  if (mStringFilter)
165  {
166  retString = mStringFilter(retString);
167  }
168 
169  return retString;
170 }
171 
172 std::string GraphAnalyzer::GenerateNodeLabel(const std::string& aNodeName, int aEvaluationIndex) const
173 {
174  std::string nodeLabel(aNodeName);
175 
176  if (mLabelWordWrapper)
177  {
178  nodeLabel = mLabelWordWrapper(nodeLabel);
179  }
180 
181  std::ostringstream nodeLabelStream;
182  nodeLabelStream << aEvaluationIndex << ":" << nodeLabel;
183 
184  return nodeLabelStream.str();
185 }
186 
187 std::string GraphAnalyzer::GetLegend() const
188 {
189  std::string legend = "\n"
190  "{\n"
191  "node [shape=plaintext]\n"
192  "subgraph cluster_02\n"
193  "{\n"
194  "label = \"Legend\";\n"
195  "\"Input Topic\" [label=\"[i] Input Topic\",style=filled, shape=box, color=lightblue];\n"
196  "\"Normal Topic\" [label=\"[i] Normal Topic\",style=filled, shape=box, color=red];\n"
197  "\"Public Topic\" [label=\"[i] Public Topic\",style=filled, shape=box, color=grey, peripheries=2];\n"
198  "\"Output Topic\" [label=\"[i] Output Topic\",style=filled, shape=box, color=limegreen];\n"
199  "\"Timeout Topic\" [label=\"[i] Timeout Topic\",style=filled, shape=box, color=orange];\n"
200  "\"Detector\" [label=\"[i] Detector\", shape=ellipse, color=blue];\n"
201  "\"hidden1\" [label=\"\"]\n"
202  "\"hidden2\" [label=\"\"]\n"
203  "\"Publish() dependency\" -> \"hidden1\";\n"
204  "\"FuturePublish() dependency\" -> \"hidden2\" [style=dotted, color=red];\n"
205  "}\n"
206  "}\n";
207 
208  return legend;
209 }
210 
211 }
const VertexPtrContainer & GetVertices() const
Definition: graph.hpp:221
Implements a graph of Topics & Detectors with Input/Output APIs.
Definition: graph.hpp:127
std::string WrapOnCommonEndings(const std::string &aNodeName)
Adds a \n to the input name before common suffixes.
virtual TopicStateIdType GetId() const =0
void DG_LOG(const char *aLogString,...)
Definition: dglogging.cpp:22
void SetLabelWordWrapper(std::string(*aStringFilter)(const std::string &))
Sets a filter to insert at specific points.
GraphAnalyzer(const Graph &aGraph)
bool HasPublicConflict() const
Prints whether two detectors post to the same public Topic.
void GenerateDotFile(const std::string &aOutFilePath) const
Print to aOutFilePath a graphviz visualization of the graph.
Provide interface for a topic.
Definition: topic.hpp:46
void SetStringFilter(std::string(*aStringFilter)(const std::string &))
Sets a filter to produce readable names from mangle C++ ones.
std::string GetMinimalName(const std::string &aMangledString)
Returns a readable name with redundant prefixes/suffixes removed.
void PrintVertexes() const
Prints to stdout the names of all vertices in the current order.