Embedded Template Library  1.0
fsm_generator.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) 2017 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 /*[[[cog
30 import cog
31 cog.outl("#if 0")
32 ]]]*/
33 /*[[[end]]]*/
34 #error THIS HEADER IS A GENERATOR. DO NOT INCLUDE.
35 /*[[[cog
36 import cog
37 cog.outl("#endif")
38 ]]]*/
39 /*[[[end]]]*/
40 
41 /*[[[cog
42 import cog
43 cog.outl("//***************************************************************************")
44 cog.outl("// THIS FILE HAS BEEN AUTO GENERATED. DO NOT EDIT THIS FILE.")
45 cog.outl("//***************************************************************************")
46 ]]]*/
47 /*[[[end]]]*/
48 
49 //***************************************************************************
50 // To generate to header file, run this at the command line.
51 // Note: You will need Python and COG installed.
52 //
53 // python -m cogapp -d -e -ofsm.h -DHandlers=<n> fsm_generator.h
54 // Where <n> is the number of messages to support.
55 //
56 // e.g.
57 // To generate handlers for up to 16 events...
58 // python -m cogapp -d -e -ofsm.h -DHandlers=16 fsm_generator.h
59 //
60 // See generate.bat
61 //***************************************************************************
62 
63 #ifndef ETL_FSM_INCLUDED
64 #define ETL_FSM_INCLUDED
65 
66 #include <stdint.h>
67 
68 #include "platform.h"
69 #include "array.h"
70 #include "nullptr.h"
71 #include "error_handler.h"
72 #include "exception.h"
73 #include "user_type.h"
74 #include "message_router.h"
75 #include "integral_limits.h"
76 #include "largest.h"
77 
78 #undef ETL_FILE
79 #define ETL_FILE "34"
80 
81 #include "private/minmax_push.h"
82 
83 namespace etl
84 {
85  class fsm;
86 
88 #if !defined(ETL_FSM_STATE_ID_TYPE)
89  typedef uint_least8_t fsm_state_id_t;
90 #else
91  typedef ETL_FSM_STATE_ID_TYPE fsm_state_id_t;
92 #endif
93 
94  // For internal FSM use.
95  typedef typename etl::larger_type<etl::message_id_t>::type fsm_internal_id_t;
96 
97  //***************************************************************************
99  //***************************************************************************
100  class fsm_exception : public etl::exception
101  {
102  public:
103 
104  fsm_exception(string_type reason_, string_type file_name_, numeric_type line_number_)
105  : etl::exception(reason_, file_name_, line_number_)
106  {
107  }
108  };
109 
110  //***************************************************************************
112  //***************************************************************************
113  class fsm_null_state_exception : public etl::fsm_exception
114  {
115  public:
116 
117  fsm_null_state_exception(string_type file_name_, numeric_type line_number_)
118  : etl::fsm_exception(ETL_ERROR_TEXT("fsm:null state", ETL_FILE"A"), file_name_, line_number_)
119  {
120  }
121  };
122 
123  //***************************************************************************
125  //***************************************************************************
126  class fsm_state_id_exception : public etl::fsm_exception
127  {
128  public:
129 
130  fsm_state_id_exception(string_type file_name_, numeric_type line_number_)
131  : etl::fsm_exception(ETL_ERROR_TEXT("fsm:state id", ETL_FILE"B"), file_name_, line_number_)
132  {
133  }
134  };
135 
136  //***************************************************************************
138  //***************************************************************************
139  class fsm_state_list_exception : public etl::fsm_exception
140  {
141  public:
142 
143  fsm_state_list_exception(string_type file_name_, numeric_type line_number_)
144  : etl::fsm_exception(ETL_ERROR_TEXT("fsm:state list", ETL_FILE"C"), file_name_, line_number_)
145  {
146  }
147  };
148 
149  //***************************************************************************
151  //***************************************************************************
152  class fsm_state_list_order_exception : public etl::fsm_exception
153  {
154  public:
155 
156  fsm_state_list_order_exception(string_type file_name_, numeric_type line_number_)
157  : etl::fsm_exception(ETL_ERROR_TEXT("fsm:state list order", ETL_FILE"D"), file_name_, line_number_)
158  {
159  }
160  };
161 
162  //***************************************************************************
164  //***************************************************************************
165  class ifsm_state
166  {
167  public:
168 
170  friend class etl::fsm;
171 
172  //*******************************************
174  //*******************************************
176  {
177  return state_id;
178  }
179 
180  protected:
181 
182  //*******************************************
184  //*******************************************
186  : state_id(state_id_),
187  p_context(ETL_NULLPTR)
188  {
189  }
190 
191  //*******************************************
193  //*******************************************
195  {
196  }
197 
198  //*******************************************
199  inline etl::fsm& get_fsm_context() const
200  {
201  return *p_context;
202  }
203 
204  private:
205 
206  virtual fsm_state_id_t process_event(etl::imessage_router& source, const etl::imessage& message) = 0;
207 
208  virtual fsm_state_id_t on_enter_state() { return state_id; } // By default, do nothing.
209  virtual void on_exit_state() {} // By default, do nothing.
210 
211  //*******************************************
212  void set_fsm_context(etl::fsm& context)
213  {
214  p_context = &context;
215  }
216 
217  // The state id.
218  const etl::fsm_state_id_t state_id;
219 
220  // A pointer to the FSM context.
221  etl::fsm* p_context;
222 
223  // Disabled.
224  ifsm_state(const ifsm_state&);
225  ifsm_state& operator =(const ifsm_state&);
226  };
227 
228  //***************************************************************************
230  //***************************************************************************
231  class fsm : public etl::imessage_router
232  {
233  public:
234 
235  //*******************************************
237  //*******************************************
238  fsm(etl::message_router_id_t id)
239  : imessage_router(id),
240  p_state(ETL_NULLPTR)
241  {
242  }
243 
244  //*******************************************
246  //*******************************************
247  template <typename TSize>
248  void set_states(etl::ifsm_state** p_states, TSize size)
249  {
250  state_list = p_states;
251  number_of_states = etl::fsm_state_id_t(size);
252 
253  ETL_ASSERT(number_of_states > 0, ETL_ERROR(etl::fsm_state_list_exception));
254 
255  for (etl::fsm_state_id_t i = 0; i < size; ++i)
256  {
257  ETL_ASSERT(state_list[i] != ETL_NULLPTR, ETL_ERROR(etl::fsm_null_state_exception));
258  ETL_ASSERT(state_list[i]->get_state_id() == i, ETL_ERROR(etl::fsm_state_list_order_exception));
259  state_list[i]->set_fsm_context(*this);
260  }
261  }
262 
263  //*******************************************
268  //*******************************************
269  void start(bool call_on_enter_state = true)
270  {
271  // Can only be started once.
272  if (p_state == ETL_NULLPTR)
273  {
274  p_state = state_list[0];
275  ETL_ASSERT(p_state != ETL_NULLPTR, ETL_ERROR(etl::fsm_null_state_exception));
276 
277  if (call_on_enter_state)
278  {
279  etl::fsm_state_id_t next_state_id;
280  etl::ifsm_state* p_last_state;
281 
282  do
283  {
284  p_last_state = p_state;
285  next_state_id = p_state->on_enter_state();
286  p_state = state_list[next_state_id];
287 
288  } while (p_last_state != p_state);
289  }
290  }
291  }
292 
293  //*******************************************
295  //*******************************************
296  void receive(const etl::imessage& message) ETL_OVERRIDE
297  {
298  static etl::null_message_router nmr;
299  receive(nmr, message);
300  }
301 
302  //*******************************************
304  //*******************************************
305  void receive(imessage_router& source, etl::message_router_id_t destination_router_id, const etl::imessage& message) ETL_OVERRIDE
306  {
307  if ((destination_router_id == get_message_router_id()) || (destination_router_id == imessage_router::ALL_MESSAGE_ROUTERS))
308  {
309  receive(source, message);
310  }
311  }
312 
313  //*******************************************
315  //*******************************************
316  void receive(etl::imessage_router& source, const etl::imessage& message) ETL_OVERRIDE
317  {
318  etl::fsm_state_id_t next_state_id = p_state->process_event(source, message);
319  ETL_ASSERT(next_state_id < number_of_states, ETL_ERROR(etl::fsm_state_id_exception));
320 
321  etl::ifsm_state* p_next_state = state_list[next_state_id];
322 
323  // Have we changed state?
324  if (p_next_state != p_state)
325  {
326  do
327  {
328  p_state->on_exit_state();
329  p_state = p_next_state;
330 
331  next_state_id = p_state->on_enter_state();
332  ETL_ASSERT(next_state_id < number_of_states, ETL_ERROR(etl::fsm_state_id_exception));
333 
334  p_next_state = state_list[next_state_id];
335 
336  } while (p_next_state != p_state); // Have we changed state again?
337  }
338  }
339 
340  using imessage_router::accepts;
341 
342  //*******************************************
345  //*******************************************
346  bool accepts(etl::message_id_t) const ETL_OVERRIDE
347  {
348  return true;
349  }
350 
351  //*******************************************
353  //*******************************************
355  {
356  ETL_ASSERT(p_state != ETL_NULLPTR, ETL_ERROR(etl::fsm_null_state_exception));
357  return p_state->get_state_id();
358  }
359 
360  //*******************************************
362  //*******************************************
364  {
365  ETL_ASSERT(p_state != ETL_NULLPTR, ETL_ERROR(etl::fsm_null_state_exception));
366  return *p_state;
367  }
368 
369  //*******************************************
371  //*******************************************
372  const ifsm_state& get_state() const
373  {
374  ETL_ASSERT(p_state != ETL_NULLPTR, ETL_ERROR(etl::fsm_null_state_exception));
375  return *p_state;
376  }
377 
378  //*******************************************
380  //*******************************************
381  bool is_started() const
382  {
383  return p_state != ETL_NULLPTR;
384  }
385 
386  //*******************************************
389  //*******************************************
390  void reset(bool call_on_exit_state = false)
391  {
392  if ((p_state != ETL_NULLPTR) && call_on_exit_state)
393  {
394  p_state->on_exit_state();
395  }
396 
397  p_state = ETL_NULLPTR;
398  }
399 
400  //********************************************
401  ETL_DEPRECATED bool is_null_router() const ETL_OVERRIDE
402  {
403  return false;
404  }
405 
406  //********************************************
407  bool is_producer() const ETL_OVERRIDE
408  {
409  return true;
410  }
411 
412  //********************************************
413  bool is_consumer() const ETL_OVERRIDE
414  {
415  return true;
416  }
417 
418  private:
419 
420  etl::ifsm_state* p_state;
421  etl::ifsm_state** state_list;
422  etl::fsm_state_id_t number_of_states;
423  };
424 
425  /*[[[cog
426  import cog
427  ################################################
428  # The first definition for all of the events.
429  ################################################
430  cog.outl("//***************************************************************************")
431  cog.outl("// The definition for all %s message types." % Handlers)
432  cog.outl("//***************************************************************************")
433  cog.outl("template <typename TContext, typename TDerived, const etl::fsm_state_id_t STATE_ID_, ")
434  cog.out(" ")
435  for n in range(1, int(Handlers)):
436  cog.out("typename T%s = void, " % n)
437  if n % 4 == 0:
438  cog.outl("")
439  cog.out(" ")
440  cog.outl("typename T%s = void>" % Handlers)
441  cog.outl("class fsm_state : public ifsm_state")
442  cog.outl("{")
443  cog.outl("public:")
444  cog.outl("")
445  cog.outl(" enum")
446  cog.outl(" {")
447  cog.outl(" STATE_ID = STATE_ID_")
448  cog.outl(" };")
449  cog.outl("")
450  cog.outl(" fsm_state()")
451  cog.outl(" : ifsm_state(STATE_ID)")
452  cog.outl(" {")
453  cog.outl(" }")
454  cog.outl("")
455  cog.outl("protected:")
456  cog.outl("")
457  cog.outl(" ~fsm_state()")
458  cog.outl(" {")
459  cog.outl(" }")
460  cog.outl("")
461  cog.outl(" inline TContext& get_fsm_context() const")
462  cog.outl(" {")
463  cog.outl(" return static_cast<TContext&>(ifsm_state::get_fsm_context());")
464  cog.outl(" }")
465  cog.outl("")
466  cog.outl("private:")
467  cog.outl("")
468  cog.outl(" etl::fsm_state_id_t process_event(etl::imessage_router& source, const etl::imessage& message)")
469  cog.outl(" {")
470  cog.outl(" etl::fsm_state_id_t new_state_id;")
471  cog.outl(" etl::message_id_t event_id = message.message_id;")
472  cog.outl("")
473  cog.outl(" switch (event_id)")
474  cog.outl(" {")
475  for n in range(1, int(Handlers) + 1):
476  cog.out(" case T%d::ID:" % n)
477  cog.out(" new_state_id = static_cast<TDerived*>(this)->on_event(source, static_cast<const T%d&>(message));" % n)
478  cog.outl(" break;")
479  cog.out(" default:")
480  cog.out(" new_state_id = static_cast<TDerived*>(this)->on_event_unknown(source, message);")
481  cog.outl(" break;")
482  cog.outl(" }")
483  cog.outl("")
484  cog.outl(" return new_state_id;")
485  cog.outl(" }")
486  cog.outl("};")
487 
488  ####################################
489  # All of the other specialisations.
490  ####################################
491  for n in range(int(Handlers) - 1, 0, -1):
492  cog.outl("")
493  cog.outl("//***************************************************************************")
494  if n == 1:
495  cog.outl("// Specialisation for %d message type." % n)
496  else:
497  cog.outl("// Specialisation for %d message types." % n)
498  cog.outl("//***************************************************************************")
499  cog.outl("template <typename TContext, typename TDerived, const etl::fsm_state_id_t STATE_ID_, ")
500  cog.out(" ")
501  for t in range(1, n):
502  cog.out("typename T%d, " % t)
503  if t % 4 == 0:
504  cog.outl("")
505  cog.out(" ")
506  cog.outl("typename T%d>" % n)
507  cog.out("class fsm_state<TContext, TDerived, STATE_ID_, ")
508  for t in range(1, n + 1):
509  cog.out("T%d, " % t)
510  if t % 16 == 0:
511  cog.outl("")
512  cog.out(" ")
513  for t in range(n + 1, int(Handlers)):
514  cog.out("void, ")
515  if t % 16 == 0:
516  cog.outl("")
517  cog.out(" ")
518  cog.outl("void> : public ifsm_state")
519  cog.outl("{")
520  cog.outl("public:")
521  cog.outl("")
522  cog.outl(" enum")
523  cog.outl(" {")
524  cog.outl(" STATE_ID = STATE_ID_")
525  cog.outl(" };")
526  cog.outl("")
527  cog.outl(" fsm_state()")
528  cog.outl(" : ifsm_state(STATE_ID)")
529  cog.outl(" {")
530  cog.outl(" }")
531  cog.outl("")
532  cog.outl("protected:")
533  cog.outl("")
534  cog.outl(" ~fsm_state()")
535  cog.outl(" {")
536  cog.outl(" }")
537  cog.outl("")
538  cog.outl(" inline TContext& get_fsm_context() const")
539  cog.outl(" {")
540  cog.outl(" return static_cast<TContext&>(ifsm_state::get_fsm_context());")
541  cog.outl(" }")
542  cog.outl("")
543  cog.outl("private:")
544  cog.outl("")
545  cog.outl(" etl::fsm_state_id_t process_event(etl::imessage_router& source, const etl::imessage& message)")
546  cog.outl(" {")
547  cog.outl(" etl::fsm_state_id_t new_state_id;")
548  cog.outl(" etl::message_id_t event_id = message.message_id;")
549  cog.outl("")
550  cog.outl(" switch (event_id)")
551  cog.outl(" {")
552  for n in range(1, n + 1):
553  cog.out(" case T%d::ID:" % n)
554  cog.out(" new_state_id = static_cast<TDerived*>(this)->on_event(source, static_cast<const T%d&>(message));" % n)
555  cog.outl(" break;")
556  cog.out(" default:")
557  cog.out(" new_state_id = static_cast<TDerived*>(this)->on_event_unknown(source, message);")
558  cog.outl(" break;")
559  cog.outl(" }")
560  cog.outl("")
561  cog.outl(" return new_state_id;")
562  cog.outl(" }")
563  cog.outl("};")
564  ####################################
565  # Specialisation for zero messages.
566  ####################################
567  cog.outl("")
568  cog.outl("//***************************************************************************")
569  cog.outl("// Specialisation for 0 message types.")
570  cog.outl("//***************************************************************************")
571  cog.outl("template <typename TContext, typename TDerived, const etl::fsm_state_id_t STATE_ID_>")
572  cog.out("class fsm_state<TContext, TDerived, STATE_ID_, ")
573  for t in range(1, int(Handlers)):
574  cog.out("void, ")
575  if t % 16 == 0:
576  cog.outl("")
577  cog.out(" ")
578  cog.outl("void> : public ifsm_state")
579  cog.outl("{")
580  cog.outl("public:")
581  cog.outl("")
582  cog.outl(" enum")
583  cog.outl(" {")
584  cog.outl(" STATE_ID = STATE_ID_")
585  cog.outl(" };")
586  cog.outl("")
587  cog.outl(" fsm_state()")
588  cog.outl(" : ifsm_state(STATE_ID)")
589  cog.outl(" {")
590  cog.outl(" }")
591  cog.outl("")
592  cog.outl("protected:")
593  cog.outl("")
594  cog.outl(" ~fsm_state()")
595  cog.outl(" {")
596  cog.outl(" }")
597  cog.outl("")
598  cog.outl(" inline TContext& get_fsm_context() const")
599  cog.outl(" {")
600  cog.outl(" return static_cast<TContext&>(ifsm_state::get_fsm_context());")
601  cog.outl(" }")
602  cog.outl("private:")
603  cog.outl("")
604  cog.outl(" etl::fsm_state_id_t process_event(etl::imessage_router& source, const etl::imessage& message)")
605  cog.outl(" {")
606  cog.outl(" return static_cast<TDerived*>(this)->on_event_unknown(source, message);")
607  cog.outl(" }")
608  cog.outl("};")
609  ]]]*/
610  /*[[[end]]]*/
611 }
612 
613 #undef ETL_FILE
614 
615 #include "private/minmax_pop.h"
616 
617 #endif
Base exception class for FSM.
Definition: fsm.h:89
Exception for null state pointer.
Definition: fsm.h:102
Exception for invalid state id.
Definition: fsm.h:115
Exception for incompatible state list.
Definition: fsm.h:128
Exception for incompatible order state list.
Definition: fsm.h:141
The FSM class.
Definition: fsm.h:220
etl::fsm_state_id_t get_state_id() const
Gets the current state id.
Definition: fsm.h:342
void receive(const etl::imessage &message) ETL_OVERRIDE
Top level message handler for the FSM.
Definition: fsm_generator.h:296
fsm(etl::message_router_id_t id)
Constructor.
Definition: fsm_generator.h:238
bool accepts(etl::message_id_t) const ETL_OVERRIDE
Does this FSM accept the message id? Yes, it accepts everything!
Definition: fsm_generator.h:346
void reset(bool call_on_exit_state=false)
Reset the FSM to pre-started state.
Definition: fsm_generator.h:390
ifsm_state & get_state()
Gets a reference to the current state interface.
Definition: fsm_generator.h:363
void set_states(etl::ifsm_state **p_states, TSize size)
Set the states for the FSM.
Definition: fsm_generator.h:248
const ifsm_state & get_state() const
Gets a const reference to the current state interface.
Definition: fsm_generator.h:372
void receive(etl::imessage_router &source, const etl::imessage &message) ETL_OVERRIDE
Top level message handler for the FSM.
Definition: fsm_generator.h:316
void start(bool call_on_enter_state=true)
Starts the FSM. Can only be called once. Subsequent calls will do nothing.
Definition: fsm_generator.h:269
void receive(imessage_router &source, etl::message_router_id_t destination_router_id, const etl::imessage &message) ETL_OVERRIDE
Top level message handler for the FSM.
Definition: fsm_generator.h:305
bool is_started() const
Checks if the FSM has been started.
Definition: fsm_generator.h:381
Interface class for FSM states.
Definition: fsm.h:154
~ifsm_state()
Destructor.
Definition: fsm_generator.h:194
etl::fsm_state_id_t get_state_id() const
Gets the id for this state.
Definition: fsm_generator.h:175
ifsm_state(etl::fsm_state_id_t state_id_)
Constructor.
Definition: fsm_generator.h:185
This is the base of all message routers.
Definition: message_router_generator.h:114
Definition: message.h:68
Definition: message.h:92
This router can be used as a sink for messages or a 'null source' router.
Definition: message_router_generator.h:194
#define ETL_ASSERT(b, e)
Definition: error_handler.h:290
exception(string_type reason_, string_type file_, numeric_type line_)
Constructor.
Definition: exception.h:67
Definition: exception.h:47
Defines a type that is as larger or larger than the specified type. Will return the specified type is...
Definition: largest_generator.h:352
Definition: absolute.h:37
uint_least8_t message_id_t
Allow alternative type for message id.
Definition: message_types.h:40
uint_least8_t fsm_state_id_t
Allow alternative type for state id.
Definition: fsm.h:73