DetectorGraph  2.0
helloworld.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 "graph.hpp"
16 #include "detector.hpp"
17 #include "sharedptr.hpp"
18 #include "processorcontainer.hpp"
19 
20 #include <iostream>
21 
22 using std::cout;
23 using std::endl;
24 
25 /**
26  * @file helloworld.cpp
27  * @brief The basics - A trivial Graph with a single Detector.
28  *
29  * @section ex-hw-intro Introduction
30  * This examples cover the basics of using the DetectorGraph framework - a "Hello
31  * World" of sorts.
32  * For the purposes of this example imagine a temperature sensor that produces
33  * samples regularly and that we're tasked to signal other parts of the system
34  * whenever that temperature crosses a threshold.
35  *
36  * We start by splitting the problem, within the scope of our task, in three
37  * parts:
38  * - What's our input data? _The input Topic_
39  * - What's our output data? _The output Topic_
40  * - How do we get from one to the other? _The Detector what will do the job_
41  *
42  * @section ex-hw-topics Topics
43  * Topics are defined by the DetectorGraph::TopicState type they carry. A
44  * TopicState is any C++ struct/class that inherits from
45  * DetectorGraph::TopicState.
46  *
47  * For the 'input' to our system we'll declare a `TemperatureSample` TopicState:
48  @snippetlineno helloworld.cpp Input Topic
49  * The important here is that this `struct` inherits TopicState and that it has
50  * data field(s).
51  *
52  * Next we define the 'output'; another struct that also inherits
53  * DetectorGraph::TopicState and has data fields:
54  @snippetlineno helloworld.cpp Output Topic
55  *
56  * @section ex-hw-detector Detector
57  * Now we just need to fill in the gap; the Detector that creates
58  * `OverheatingState` from `TemperatureSample`:
59  * two.
60  @snippetlineno helloworld.cpp Detector
61  *
62  * @section ex-hw-graph Graph
63  * The final plumbing is to add our newly created Detector to a
64  * DetectorGraph::Graph.
65  * Depending on the situation this can be done in different ways but here's the
66  * simplest:
67  @snippetlineno helloworld.cpp Adding to a Graph
68  *
69  * With that the Graph instance will internally create the following graph:
70  *
71  * @dot "HelloWorldGraph"
72 digraph GraphAnalyzer {
73  rankdir = "LR";
74  node[fontname=Helvetica];
75  size="12,5";
76  "TemperatureSample" [label="0:TemperatureSample",style=filled, shape=box, color=lightblue];
77  "TemperatureSample" -> "OverheatingDetector";
78  "OverheatingDetector" -> "OverheatingState";
79  "OverheatingState" [label="2:OverheatingState",style=filled, shape=box, color=limegreen];
80  "OverheatingDetector" [label="1:OverheatingDetector", color=blue];
81 }
82  * @enddot
83  *
84  * Topics are represented by rectangles, Detectors by the oval shapes and
85  * arrows follow the flow of data - this is the standard representation for
86  * DetectorGraph graphs. The numeric prefix to the names is each node's order
87  * in the Topological sort of the graph. Output topics are painted Lime Green
88  * and input ones Light Blue.
89  *
90  * @section ex-hw-usage Usage
91  * Finally, using the graph is done by Pushing data in, evaluating the graph
92  * and checking outputs:
93  @snippetlineno helloworld.cpp Basic Graph Usage
94  *
95  * Running the program then gives:
96  \verbatim
97 DetectorGraph: Graph Initialized
98 IsOverheating = true
99  \endverbatim
100  *
101  * An alternative way to do things is to subclass the
102  * DetectorGraph::ProcessorContainer utility that streamlines the
103  * DetectorGraph::Graph::PushData, DetectorGraph::Graph::EvaluateGraph and
104  * subsequent inspection steps.
105  * For the example above, the DetectorGraph::ProcessorContainer implementation
106  * would be:
107  @snippetlineno helloworld.cpp ProcessorContainer
108  *
109  * And then usage would be like:
110  @snippetlineno helloworld.cpp Using ProcessorContainer
111  *
112  *
113  \verbatim
114 DetectorGraph: Graph Initialized
115 OverheatingState.isOverheating = false
116 OverheatingState.isOverheating = false
117 OverheatingState.isOverheating = false
118 OverheatingState.isOverheating = true
119 OverheatingState.isOverheating = true
120  \endverbatim
121  *
122  * This example can be built with:
123  \code
124 g++ -std=c++11 -I./include/ -I./platform_standalone/ src/graph.cpp src/detector.cpp platform_standalone/dglogging.cpp examples/helloworld.cpp -o helloworld.out
125  \endcode
126  *
127  * @cond DO_NOT_DOCUMENT
128  */
129 
130 //! [Input Topic]
131 struct TemperatureSample : public DetectorGraph::TopicState
132 {
133  TemperatureSample(int aTemp = 0) : temperature(aTemp) {}
134  int temperature;
135 };
136 //! [Input Topic]
137 
138 //! [Output Topic]
139 struct OverheatingState : public DetectorGraph::TopicState
140 {
141  OverheatingState(bool aState = false) : isOverheating(aState) {}
142  bool isOverheating;
143 };
144 //! [Output Topic]
145 
146 //![Detector]
147 class OverheatingDetector : public DetectorGraph::Detector
148 , public DetectorGraph::SubscriberInterface<TemperatureSample>
149 , public DetectorGraph::Publisher<OverheatingState>
150 {
151 public:
152  OverheatingDetector(DetectorGraph::Graph* graph) : DetectorGraph::Detector(graph)
153  {
154  Subscribe<TemperatureSample>(this);
155  SetupPublishing<OverheatingState>(this);
156  }
157 
158  virtual void Evaluate(const TemperatureSample& sample)
159  {
160  if (sample.temperature > kThreshold)
161  {
162  Publish(OverheatingState(true));
163  }
164  else
165  {
166  Publish(OverheatingState(false));
167  }
168  }
169 
170  static const int kThreshold = 100;
171 };
172 //![Detector]
173 
174 //![ProcessorContainer]
175 class HelloWorldGraph : public DetectorGraph::ProcessorContainer
176 {
177 public:
178  HelloWorldGraph()
179  : mOverheatingDetector(&mGraph)
180  {
181  }
182 
183  OverheatingDetector mOverheatingDetector;
184 
185  virtual void ProcessOutput()
186  {
187  DetectorGraph::Topic<OverheatingState>* overheatingStateTopic = mGraph.ResolveTopic<OverheatingState>();
188  if (overheatingStateTopic->HasNewValue())
189  {
190  const OverheatingState& overheatingState = overheatingStateTopic->GetNewValue();
191  cout << "OverheatingState.isOverheating = " << ((overheatingState.isOverheating) ? "true" : "false") << endl;
192  }
193  }
194 };
195 //![ProcessorContainer]
196 
197 //![UnitTest-Above-1]
198 void Test_AboveThreshold() // Adapt to your Unit Test Framework
199 {
200  // Arrange
201  DetectorGraph::Graph graph;
202  OverheatingDetector detector(&graph);
203  auto outTopic = graph.ResolveTopic<OverheatingState>();
204  //![UnitTest-Above-1]
205  //![UnitTest-Above-2]
206 
207  // Act
208  graph.PushData(TemperatureSample(OverheatingDetector::kThreshold+1));
209  graph.EvaluateGraph();
210 
211  //![UnitTest-Above-2]
212  //![UnitTest-Above-3]
213  // Assert
214  DG_ASSERT(outTopic->HasNewValue()); // Adapt to your Unit Test Framework
215  DG_ASSERT(outTopic->GetNewValue().isOverheating == true); // Adapt to your Unit Test Framework
216 }
217 //![UnitTest-Above-3]
218 
219 void Test_BelowThreshold()
220 {
221  // Arrange
222  DetectorGraph::Graph graph;
223  OverheatingDetector detector(&graph);
224  auto outTopic = graph.ResolveTopic<OverheatingState>();
225 
226  // Act
227  graph.PushData(TemperatureSample(OverheatingDetector::kThreshold-1));
228  graph.EvaluateGraph();
229 
230  // Assert
231  DG_ASSERT(outTopic->HasNewValue());
232  DG_ASSERT(outTopic->GetNewValue().isOverheating == false);
233 }
234 
235 //![main]
236 int main()
237 {
238  // Below are examples of two different ways of using/composing
239  // DetectorGraph graphs.
240 
241  //![Adding to a Graph]
242  DetectorGraph::Graph graph;
243  OverheatingDetector detector(&graph);
244  //![Adding to a Graph]
245 
246  //![Basic Graph Usage]
247  graph.PushData(TemperatureSample(110));
248  graph.EvaluateGraph();
249 
250  const OverheatingState& output =
251  graph.ResolveTopic<OverheatingState>()->GetNewValue();
252  cout << "IsOverheating = " << ((output.isOverheating) ? "true" : "false") << endl;
253  //![Basic Graph Usage]
254 
255  //![Using ProcessorContainer]
256  HelloWorldGraph thermostat;
257  thermostat.ProcessData(TemperatureSample(70));
258  thermostat.ProcessData(TemperatureSample(90));
259  thermostat.ProcessData(TemperatureSample(100));
260  thermostat.ProcessData(TemperatureSample(110));
261  thermostat.ProcessData(TemperatureSample(120));
262  //![Using ProcessorContainer]
263 
264  // Normally your Unit test framework of choice would call this
265  // automatically. We do explicitly for demonstration purposes.
266  Test_AboveThreshold();
267  Test_BelowThreshold();
268 }
269 //![main]
270 
271 /// @endcond DO_NOT_DOCUMENT
Implements a graph of Topics & Detectors with Input/Output APIs.
Definition: graph.hpp:127
void PushData(const TTopicState &aTopicState)
Push data to a specific topic in the graph.
Definition: graph.hpp:187
bool HasNewValue() const
Returns true if the new Data is available.
Definition: topic.hpp:166
A Base class for a basic Graph container.
Manage data and its handler.
Definition: topic.hpp:84
Topic< TTopicState > * ResolveTopic()
Find/add a topic in the detector graph.
Definition: graph.hpp:160
const T & GetNewValue() const
Returns reference to the new/latest value in the topic.
Definition: topic.hpp:179
Base struct for topic data types.
Definition: topicstate.hpp:52
Base class that implements a Publisher behavior.
Definition: publisher.hpp:66
A unit of logic in a DetectorGraph.
Definition: detector.hpp:68
A Pure interface that declares the Subscriber behavior.
ErrorType EvaluateGraph()
Evaluate the whole graph.
Definition: graph.cpp:133
#define DG_ASSERT(condition)
Definition: dgassert.hpp:20