DetectorGraph  2.0
trivialvendingmachine.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 <iostream>
16 
17 #include "graph.hpp"
18 #include "detector.hpp"
19 #include "processorcontainer.hpp"
20 #include "graphstatestore.hpp"
21 #include "dglogging.hpp"
22 #include "graphanalyzer.hpp"
23 
24 using std::cout;
25 using std::endl;
26 
27 /**
28  * @file trivialvendingmachine.cpp
29  * @brief Vending Machine with Named TopicStates and GraphAnalyzer
30  *
31  * @section ex-tvm-intro Introduction
32  * This example gives a single-detector solution for a hypothetical one-item,
33  * one-coin vending machine. It shows how a single detector can be used to
34  * synchronize combine signals - coins being inserted and the "Buy" button being
35  * clicked - to produce a third - SaleCompleted. It also introduces the concepts
36  * of _Trivial TopicStates_, _Named TopicStates_ and using GraphAnalyzer.
37  *
38  * @section ex-tvm-tts Trivial TopicStates
39  * A _Trivial TopicState_ has no data fields:
40  @snippetlineno trivialvendingmachine.cpp Trivial TopicState
41  * These are sometimes useful to represent parameter-less signals, timeouts etc.
42  *
43  * @section ex-tvm-nts Named TopicStates
44  * _Named TopicStates_, as opposed to Anonymous ones, are TopicStates that
45  * override the [TopicState::GetId](@ref DetectorGraph::TopicState::GetId)
46  * method. This allows for code that deals with sequences of different
47  * [TopicStates](@ref DetectorGraph::TopicState) in a general way. This is used
48  * primarily outside (or at the boundary) of the DetectorGraph portion of the
49  * application in applications that necessitate any need of generic/transparent
50  * treatment of TopicStates. An example of this is
51  * [GraphStateStore](@ref DetectorGraph::GraphStateStore) &
52  * [StateSnapshot](@ref DetectorGraph::StateSnapshot). They are often also
53  * called _Public_ TopicStates as they're what's exposed to the outside world
54  * from the point of view of a DetectorGraph graph.
55  *
56  * TopicState "names" come from an application-specific enumeration value
57  * for your named TopicStates - values must be cast-able to
58  * [TopicStateIdType](@ref DetectorGraph::TopicStateIdType) and be different than
59  * [TopicState::kAnonymousTopicState](@ref DetectorGraph::TopicState::kAnonymousTopicState)
60  * (-1). An example would be:
61  @snippetlineno trivialvendingmachine.cpp Application-Specific Enum for Named TopicStates
62  (`C++03` enums are fine too)
63  *
64  * Which then can be used in the overriding of TopicState::GetId for the desired
65  * named TopicState:
66  @snippetlineno trivialvendingmachine.cpp Named TopicState
67  *
68  * That then enables things like:
69  @snippetlineno trivialvendingmachine.cpp Inspecting Graph Output with Named TopicStates
70  *
71  * @section ex-tvm-dga GraphAnalyzer
72  * Finally, this example also shows how to use DetectorGraph::GraphAnalyzer to
73  * generate a GraphViz-compatible `.dot` representation of the graph:
74  @snippetlineno trivialvendingmachine.cpp Using GraphAnalyzer to Create dot file
75  *
76  * That .dot file when rendered generates the following graph:
77  @dot "Trivial Vending Machine"
78 digraph GraphAnalyzer {
79  rankdir = "LR";
80  node[fontname=Helvetica];
81  "BuyButtonClicked" [label="0:BuyButtonClicked",style=filled, shape=box, color=lightblue];
82  "BuyButtonClicked" -> "SaleDetector";
83  "CoinInserted" [label="1:CoinInserted",style=filled, shape=box, color=lightblue];
84  "CoinInserted" -> "SaleDetector";
85  "SaleDetector" [label="2:SaleDetector", color=blue];
86  "SaleDetector" -> "SaleCompleted";
87  "SaleDetector" -> "Balance";
88  "Balance" [label="3:Balance",style=filled, shape=box, peripheries=2, color=limegreen];
89  "SaleCompleted" [label="4:SaleCompleted",style=filled, shape=box, peripheries=2, color=limegreen];
90 }
91  @enddot
92  * Note that the _Named TopicStates_ are represented by outlined rectangles.
93  *
94  *
95  * For a more sophisticated solution to a similar problem, see @ref fancyvendingmachine.cpp .
96  */
97 
98 /// @cond DO_NOT_DOCUMENT
99 
100 //![Trivial TopicState]
101 struct CoinInserted : public DetectorGraph::TopicState
102 {
103 };
104 //![Trivial TopicState]
105 
106 struct BuyButtonClicked : public DetectorGraph::TopicState
107 {
108 };
109 
110 //![Application-Specific Enum for Named TopicStates]
111 enum class VendingMachineTopicStateIds{
112  kSaleCompleted = 0,
113  kBalance
114 };
115 //![Application-Specific Enum for Named TopicStates]
116 
117 //![Named TopicState]
118 struct SaleCompleted : public DetectorGraph::TopicState
119 {
120  DetectorGraph::TopicStateIdType GetId() const
121  {
122  return static_cast<DetectorGraph::TopicStateIdType>(
123  VendingMachineTopicStateIds::kSaleCompleted);
124  }
125 };
126 //![Named TopicState]
127 
128 struct Balance : public DetectorGraph::TopicState
129 {
130  Balance() : numberOfCoins(0) {}
131 
132  DetectorGraph::TopicStateIdType GetId() const
133  {
134  return static_cast<DetectorGraph::TopicStateIdType>(
135  VendingMachineTopicStateIds::kBalance);
136  }
137 
138  int numberOfCoins;
139 };
140 
141 class SaleDetector : public DetectorGraph::Detector
142 , public DetectorGraph::SubscriberInterface<CoinInserted>
143 , public DetectorGraph::SubscriberInterface<BuyButtonClicked>
144 , public DetectorGraph::Publisher<SaleCompleted>
145 , public DetectorGraph::Publisher<Balance>
146 {
147 public:
148  SaleDetector(DetectorGraph::Graph* graph) : DetectorGraph::Detector(graph), balance()
149  {
150  Subscribe<CoinInserted>(this);
151  Subscribe<BuyButtonClicked>(this);
152  SetupPublishing<SaleCompleted>(this);
153  SetupPublishing<Balance>(this);
154  }
155 
156  void Evaluate(const CoinInserted&)
157  {
158  balance.numberOfCoins++;
159  }
160  void Evaluate(const BuyButtonClicked&)
161  {
162  if (balance.numberOfCoins > 0)
163  {
164  DG_LOG("Making Sale");
165  balance.numberOfCoins--;
166  Publisher<SaleCompleted>::Publish(SaleCompleted());
167  }
168  else
169  {
170  DG_LOG("Funds not available; no sale made.");
171  }
172  }
173  void CompleteEvaluation()
174  {
175  Publisher<Balance>::Publish(balance);
176  }
177 
178 private:
179  Balance balance;
180 };
181 
182 //![VendingMachine ProcessorContainer]
183 class VendingMachine : public DetectorGraph::ProcessorContainer
184 {
185 public:
186  VendingMachine()
187  : mSaleDetector(&mGraph)
188  , mSaleTopic(mGraph.ResolveTopic<SaleCompleted>())
189  , mBalanceTopic(mGraph.ResolveTopic<Balance>())
190  , mStateStore()
191  {
192  }
193 
194  SaleDetector mSaleDetector;
196  DetectorGraph::Topic<Balance>* mBalanceTopic;
197  DetectorGraph::GraphStateStore mStateStore;
198 
199  //![Inspecting Graph Output with Named TopicStates]
200  void ProcessOutput()
201  {
202  // Now we can iterate exclusively through newly updated TopicStates
203  // instead of checking through every Topic of interest.
204  for (const auto topicState : mGraph.GetOutputList())
205  {
206  switch(static_cast<VendingMachineTopicStateIds>(
207  topicState->GetId()))
208  {
209  case VendingMachineTopicStateIds::kSaleCompleted:
210  cout << "Sale Made" << endl;
211  break;
212 
213  case VendingMachineTopicStateIds::kBalance:
214  {
215  const std::shared_ptr<const Balance> balance =
216  std::static_pointer_cast<const Balance>(topicState);
217 
218  cout << "Balance: " << balance->numberOfCoins;
219  cout << " coins" << endl;
220  }
221  break;
222  }
223  }
224 
225  // Or update a GraphStateStore that can be used for persistence or for
226  // a State querying API to be used from outside of the graph.
227  mStateStore.TakeNewSnapshot(mGraph.GetOutputList());
228  }
229  //![Inspecting Graph Output with Named TopicStates]
230 };
231 //![VendingMachine ProcessorContainer]
232 
233 int main()
234 {
235  VendingMachine vendingMachine;
236  vendingMachine.ProcessData(CoinInserted());
237  vendingMachine.ProcessData(BuyButtonClicked());
238 
239  vendingMachine.ProcessData(BuyButtonClicked());
240 
241 
242  vendingMachine.ProcessData(CoinInserted());
243  vendingMachine.ProcessData(CoinInserted());
244  vendingMachine.ProcessData(BuyButtonClicked());
245  vendingMachine.ProcessData(BuyButtonClicked());
246 
247 //![Using GraphAnalyzer to Create dot file]
248  DetectorGraph::GraphAnalyzer analyzer(vendingMachine.mGraph);
249  analyzer.GenerateDotFile("trivial_vending_machine.dot");
250 //![Using GraphAnalyzer to Create dot file]
251 }
252 
253 /// @endcond DO_NOT_DOCUMENT
Implements a graph of Topics & Detectors with Input/Output APIs.
Definition: graph.hpp:127
A Base class for a basic Graph container.
Manage data and its handler.
Definition: topic.hpp:84
void DG_LOG(const char *aLogString,...)
Definition: dglogging.cpp:22
A StateSnapshot keeper for DetectorGraph TopicStates.
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.
Class that provides debugging/diagnostics to a DetectorGraph detector graph.
void TakeNewSnapshot(const std::list< ptr::shared_ptr< const TopicState > > &arTopicStates)
Takes a new state snapshot and appends it to the look back queue.
int TopicStateIdType
Definition: topicstate.hpp:27