Embedded Template Library  1.0
state_chart.h
1 /******************************************************************************
2 The MIT License(MIT)
3 
4 Embedded Template Library.
5 https://github.com/ETLCPP/etl
6 https://www.etlcpp.com
7 
8 Copyright(c) 2018 jwellbelove
9 
10 Permission is hereby granted, free of charge, to any person obtaining a copy
11 of this software and associated documentation files(the "Software"), to deal
12 in the Software without restriction, including without limitation the rights
13 to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
14 copies of the Software, and to permit persons to whom the Software is
15 furnished to do so, subject to the following conditions :
16 
17 The above copyright notice and this permission notice shall be included in all
18 copies or substantial portions of the Software.
19 
20 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
23 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26 SOFTWARE.
27 ******************************************************************************/
28 
29 #ifndef ETL_STATE_CHART_INCLUDED
30 #define ETL_STATE_CHART_INCLUDED
31 
32 #include <stdint.h>
33 
34 #include "etl/platform.h"
35 #include "etl/nullptr.h"
36 #include "etl/array.h"
37 #include "etl/array_view.h"
38 
39 namespace etl
40 {
41  //***************************************************************************
43  //***************************************************************************
45  {
46  public:
47 
48  typedef int state_id_t;
49  typedef int event_id_t;
50 
51  virtual void start(const bool on_entry_initial = true) = 0;
52  virtual void process_event(const event_id_t event_id) = 0;
53 
54  //*************************************************************************
57  //*************************************************************************
58  state_id_t get_state_id() const
59  {
60  return current_state_id;
61  }
62 
63  virtual ~istate_chart()
64  {
65  }
66 
67  protected:
68 
69  istate_chart(state_id_t current_state_id_)
70  : current_state_id(current_state_id_)
71  {
72  }
73 
74  state_id_t current_state_id;
75  };
76 
77  //***************************************************************************
79  //***************************************************************************
80  template <typename TObject>
81  class state_chart : public istate_chart
82  {
83  public:
84 
85  //*************************************************************************
87  //*************************************************************************
88  struct transition
89  {
90  ETL_CONSTEXPR transition(const state_id_t current_state_id_,
91  const event_id_t event_id_,
92  const state_id_t next_state_id_,
93  void (TObject::* const action_)() = ETL_NULLPTR,
94  bool (TObject::* const guard_)() = ETL_NULLPTR)
95  : from_any_state(false),
96  current_state_id(current_state_id_),
97  event_id(event_id_),
98  next_state_id(next_state_id_),
99  action(action_),
100  guard(guard_)
101  {
102  }
103 
104  ETL_CONSTEXPR transition(const event_id_t event_id_,
105  const state_id_t next_state_id_,
106  void (TObject::* const action_)() = ETL_NULLPTR,
107  bool (TObject::* const guard_)() = ETL_NULLPTR)
108  : from_any_state(true),
109  current_state_id(0),
110  event_id(event_id_),
111  next_state_id(next_state_id_),
112  action(action_),
113  guard(guard_)
114  {
115  }
116 
117  const bool from_any_state;
118  const state_id_t current_state_id;
119  const event_id_t event_id;
120  const state_id_t next_state_id;
121  void (TObject::* const action)();
122  bool (TObject::* const guard)();
123  };
124 
125  //*************************************************************************
127  //*************************************************************************
128  struct state
129  {
130  ETL_CONSTEXPR state(const state_id_t state_id_,
131  void (TObject::* const on_entry_)() = ETL_NULLPTR,
132  void (TObject::* const on_exit_)() = ETL_NULLPTR)
133  : state_id(state_id_),
134  on_entry(on_entry_),
135  on_exit(on_exit_)
136  {
137  }
138 
139  state_id_t state_id;
140  void (TObject::* const on_entry)();
141  void (TObject::* const on_exit)();
142  };
143 
144  //*************************************************************************
150  //*************************************************************************
151  ETL_CONSTEXPR state_chart(TObject& object_,
152  const transition* transition_table_begin_,
153  const transition* transition_table_end_,
154  const state_id_t state_id_)
155  : istate_chart(state_id_),
156  object(object_),
157  transition_table(transition_table_begin_, transition_table_end_),
158  started(false)
159  {
160  }
161 
162  //*************************************************************************
170  //*************************************************************************
171  ETL_CONSTEXPR state_chart(TObject& object_,
172  const transition* transition_table_begin_,
173  const transition* transition_table_end_,
174  const state* state_table_begin_,
175  const state* state_table_end_,
176  const state_id_t state_id_)
177  : istate_chart(state_id_),
178  object(object_),
179  transition_table(transition_table_begin_, transition_table_end_),
180  state_table(state_table_begin_, state_table_end_),
181  started(false)
182  {
183  }
184 
185  //*************************************************************************
189  //*************************************************************************
190  void set_transition_table(const transition* transition_table_begin_,
191  const transition* transition_table_end_)
192  {
193  transition_table.assign(transition_table_begin_, transition_table_end_);
194  }
195 
196  //*************************************************************************
200  //*************************************************************************
201  void set_state_table(const state* state_table_begin_,
202  const state* state_table_end_)
203  {
204  state_table.assign(state_table_begin_, state_table_end_);
205  }
206 
207  //*************************************************************************
210  //*************************************************************************
211  TObject& get_object()
212  {
213  return object;
214  }
215 
216  //*************************************************************************
219  //*************************************************************************
220  const TObject& get_object() const
221  {
222  return object;
223  }
224 
225  //*************************************************************************
228  //*************************************************************************
229  const state* find_state(state_id_t state_id)
230  {
231  if (state_table.empty())
232  {
233  return state_table.end();
234  }
235  else
236  {
237  return etl::find_if(state_table.begin(),
238  state_table.end(),
239  is_state(state_id));
240  }
241  }
242 
243  //*************************************************************************
245  //*************************************************************************
246  virtual void start(const bool on_entry_initial = true)
247  {
248  if (!started)
249  {
250  if (on_entry_initial)
251  {
252  // See if we have a state item for the initial state.
253  const state* s = find_state(current_state_id);
254 
255  // If the initial state has an 'on_entry' then call it.
256  if ((s != state_table.end()) && (s->on_entry != ETL_NULLPTR))
257  {
258  (object.*(s->on_entry))();
259  }
260  }
261 
262  started = true;
263  }
264  }
265 
266  //*************************************************************************
271  //*************************************************************************
272  virtual void process_event(const event_id_t event_id)
273  {
274  if (started)
275  {
276  const transition* t = transition_table.begin();
277 
278  // Keep looping until we execute a transition or reach the end of the table.
279  while (t != transition_table.end())
280  {
281  // Scan the transition table from the latest position.
282  t = etl::find_if(t,
283  transition_table.end(),
284  is_transition(event_id, current_state_id));
285 
286  // Found an entry?
287  if (t != transition_table.end())
288  {
289  // Shall we execute the transition?
290  if ((t->guard == ETL_NULLPTR) || ((object.*t->guard)()))
291  {
292  // Shall we execute the action?
293  if (t->action != ETL_NULLPTR)
294  {
295  (object.*t->action)();
296  }
297 
298  // Changing state?
299  if (current_state_id != t->next_state_id)
300  {
301  const state* s;
302 
303  // See if we have a state item for the current state.
305 
306  // If the current state has an 'on_exit' then call it.
307  if ((s != state_table.end()) && (s->on_exit != ETL_NULLPTR))
308  {
309  (object.*(s->on_exit))();
310  }
311 
312  current_state_id = t->next_state_id;
313 
314  // See if we have a state item for the new state.
316 
317  // If the new state has an 'on_entry' then call it.
318  if ((s != state_table.end()) && (s->on_entry != ETL_NULLPTR))
319  {
320  (object.*(s->on_entry))();
321  }
322  }
323 
324  t = transition_table.end();
325  }
326  else
327  {
328  // Start the search from the next item in the table.
329  ++t;
330  }
331  }
332  }
333  }
334  }
335 
336  private:
337 
338  //*************************************************************************
339  struct is_transition
340  {
341  is_transition(event_id_t event_id_, state_id_t state_id_)
342  : event_id(event_id_),
343  state_id(state_id_)
344  {
345  }
346 
347  bool operator()(const transition& t) const
348  {
349  return (t.event_id == event_id) && (t.from_any_state || (t.current_state_id == state_id));
350  }
351 
352  const event_id_t event_id;
353  const state_id_t state_id;
354  };
355 
356  //*************************************************************************
357  struct is_state
358  {
359  is_state(state_id_t state_id_)
360  : state_id(state_id_)
361  {
362  }
363 
364  bool operator()(const state& s) const
365  {
366  return (s.state_id == state_id);
367  }
368 
369  const state_id_t state_id;
370  };
371 
372  // Disabled
373  state_chart(const state_chart&) ETL_DELETE;
374  state_chart& operator =(const state_chart&) ETL_DELETE;
375 
376  TObject& object;
377  const etl::array_view<const transition> transition_table;
378  etl::array_view<const state> state_table;
379  bool started;
380  };
381 }
382 
383 #endif
Array view.
Definition: array_view.h:100
Simple Finite State Machine Interface.
Definition: state_chart.h:45
state_id_t get_state_id() const
Definition: state_chart.h:58
state_id_t current_state_id
The current state id.
Definition: state_chart.h:74
Simple Finite State Machine.
Definition: state_chart.h:82
const TObject & get_object() const
Definition: state_chart.h:220
void set_transition_table(const transition *transition_table_begin_, const transition *transition_table_end_)
Definition: state_chart.h:190
const state * find_state(state_id_t state_id)
Definition: state_chart.h:229
TObject & get_object()
Definition: state_chart.h:211
virtual void process_event(const event_id_t event_id)
Definition: state_chart.h:272
void set_state_table(const state *state_table_begin_, const state *state_table_end_)
Definition: state_chart.h:201
ETL_CONSTEXPR state_chart(TObject &object_, const transition *transition_table_begin_, const transition *transition_table_end_, const state_id_t state_id_)
Definition: state_chart.h:151
Definition: absolute.h:37
State definition.
Definition: state_chart.h:129
Transition definition.
Definition: state_chart.h:89