Stefan Scholz / ETL
Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers fsm_generator.h Source File

fsm_generator.h

00001 /******************************************************************************
00002 The MIT License(MIT)
00003 
00004 Embedded Template Library.
00005 https://github.com/ETLCPP/etl
00006 https://www.etlcpp.com
00007 
00008 Copyright(c) 2017 jwellbelove
00009 
00010 Permission is hereby granted, free of charge, to any person obtaining a copy
00011 of this software and associated documentation files(the "Software"), to deal
00012 in the Software without restriction, including without limitation the rights
00013 to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
00014 copies of the Software, and to permit persons to whom the Software is
00015 furnished to do so, subject to the following conditions :
00016 
00017 The above copyright notice and this permission notice shall be included in all
00018 copies or substantial portions of the Software.
00019 
00020 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
00021 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
00022 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
00023 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
00024 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
00025 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
00026 SOFTWARE.
00027 ******************************************************************************/
00028 
00029 /*[[[cog
00030 import cog
00031 cog.outl("#if 0")
00032 ]]]*/
00033 /*[[[end]]]*/
00034 #error THIS HEADER IS A GENERATOR. DO NOT INCLUDE.
00035 /*[[[cog
00036 import cog
00037 cog.outl("#endif")
00038 ]]]*/
00039 /*[[[end]]]*/
00040 
00041 /*[[[cog
00042 import cog
00043 cog.outl("//***************************************************************************")
00044 cog.outl("// This file has been auto generated. Do not edit this file.")
00045 cog.outl("//***************************************************************************")
00046 ]]]*/
00047 /*[[[end]]]*/
00048 
00049 //***************************************************************************
00050 // To generate to header file, run this at the command line.
00051 // Note: You will need Python and COG installed.
00052 //
00053 // python -m cogapp -d -e -ofsm.h -DHandlers=<n> fsm_generator.h
00054 // Where <n> is the number of messages to support.
00055 //
00056 // e.g.
00057 // To generate handlers for up to 16 events...
00058 // python -m cogapp -d -e -ofsm.h -DHandlers=16 fsm_generator.h
00059 //
00060 // See generate.bat
00061 //***************************************************************************
00062 
00063 #ifndef __ETL_FSM__
00064 #define __ETL_FSM__
00065 
00066 #include <stdint.h>
00067 
00068 #include "platform.h "
00069 #include "array.h "
00070 #include "nullptr.h "
00071 #include "error_handler.h "
00072 #include "exception.h "
00073 #include "user_type.h "
00074 #include "message_router.h"
00075 #include "integral_limits.h "
00076 #include "largest.h "
00077 
00078 #undef ETL_FILE
00079 #define ETL_FILE "34"
00080 
00081 #ifdef ETL_COMPILER_MICROSOFT
00082 #undef max
00083 #endif
00084 
00085 namespace etl
00086 {
00087   class fsm;
00088 
00089   /// Allow alternative type for state id.
00090 #if !defined(ETL_FSM_STATE_ID_TYPE)
00091     typedef uint_least8_t fsm_state_id_t;
00092 #else
00093     typedef ETL_FSM_STATE_ID_TYPE fsm_state_id_t;
00094 #endif
00095 
00096   // For internal FSM use.
00097   typedef typename etl::larger_type<etl::message_id_t>::type fsm_internal_id_t;
00098 
00099   //***************************************************************************
00100   /// Base exception class for FSM.
00101   //***************************************************************************
00102   class fsm_exception : public etl::exception
00103   {
00104   public:
00105 
00106     fsm_exception(string_type reason_, string_type file_name_, numeric_type line_number_)
00107       : etl::exception(reason_, file_name_, line_number_)
00108     {
00109     }
00110   };
00111 
00112   //***************************************************************************
00113   /// Exception for null state pointer.
00114   //***************************************************************************
00115   class fsm_null_state_exception : public etl::fsm_exception
00116   {
00117   public:
00118 
00119     fsm_null_state_exception(string_type file_name_, numeric_type line_number_)
00120       : etl::fsm_exception(ETL_ERROR_TEXT("fsm:null state", ETL_FILE"A"), file_name_, line_number_)
00121     {
00122     }
00123   };
00124 
00125   //***************************************************************************
00126   /// Exception for invalid state id.
00127   //***************************************************************************
00128   class fsm_state_id_exception : public etl::fsm_exception
00129   {
00130   public:
00131 
00132     fsm_state_id_exception(string_type file_name_, numeric_type line_number_)
00133       : etl::fsm_exception(ETL_ERROR_TEXT("fsm:state id", ETL_FILE"B"), file_name_, line_number_)
00134     {
00135     }
00136   };
00137 
00138   //***************************************************************************
00139   /// Exception for incompatible state list.
00140   //***************************************************************************
00141   class fsm_state_list_exception : public etl::fsm_exception
00142   {
00143   public:
00144 
00145     fsm_state_list_exception(string_type file_name_, numeric_type line_number_)
00146       : etl::fsm_exception(ETL_ERROR_TEXT("fsm:state list", ETL_FILE"C"), file_name_, line_number_)
00147     {
00148     }
00149   };
00150 
00151   //***************************************************************************
00152   /// Interface class for FSM states.
00153   //***************************************************************************
00154   class ifsm_state
00155   {
00156   public:
00157 
00158     /// Allows ifsm_state functions to be private.
00159     friend class etl::fsm;
00160 
00161     //*******************************************
00162     /// Gets the id for this state.
00163     //*******************************************
00164     etl::fsm_state_id_t get_state_id() const
00165     {
00166       return state_id;
00167     }
00168 
00169   protected:
00170 
00171     //*******************************************
00172     /// Constructor.
00173     //*******************************************
00174     ifsm_state(etl::fsm_state_id_t state_id_)
00175       : state_id(state_id_),
00176         p_context(std::nullptr)
00177     {
00178     }
00179 
00180     //*******************************************
00181     inline etl::fsm& get_fsm_context() const
00182     {
00183       return *p_context;
00184     }
00185 
00186   private:
00187 
00188     virtual fsm_state_id_t process_event(etl::imessage_router& source, const etl::imessage& message) = 0;
00189 
00190     virtual fsm_state_id_t on_enter_state() { return state_id; } // By default, do nothing.
00191     virtual void on_exit_state() {}  // By default, do nothing.
00192 
00193     //*******************************************
00194     void set_fsm_context(etl::fsm& context)
00195     {
00196       p_context = &context;
00197     }
00198 
00199     // The state id.
00200     const etl::fsm_state_id_t state_id;
00201 
00202     // A pointer to the FSM context.
00203     etl::fsm* p_context;
00204 
00205     // Disabled.
00206     ifsm_state(const ifsm_state&);
00207     ifsm_state& operator =(const ifsm_state&);
00208   };
00209 
00210   //***************************************************************************
00211   /// The FSM class.
00212   //***************************************************************************
00213   class fsm : public etl::imessage_router
00214   {
00215   public:
00216 
00217     //*******************************************
00218     /// Constructor.
00219     //*******************************************
00220     fsm(etl::message_router_id_t id)
00221       : imessage_router(id),
00222         p_state(std::nullptr)
00223     {
00224     }
00225 
00226     //*******************************************
00227     /// Set the states for the FSM
00228     //*******************************************
00229     template <typename TSize>
00230     void set_states(etl::ifsm_state** p_states, TSize size)
00231     {
00232       state_list       = p_states;
00233       number_of_states = etl::fsm_state_id_t(size);
00234 
00235       ETL_ASSERT((number_of_states > 0), ETL_ERROR(etl::fsm_state_list_exception));
00236 
00237       for (etl::fsm_state_id_t i = 0; i < size; ++i)
00238       {
00239         ETL_ASSERT((state_list[i] != std::nullptr), ETL_ERROR(etl::fsm_null_state_exception));
00240         state_list[i]->set_fsm_context(*this);
00241       }
00242     }
00243 
00244     //*******************************************
00245     /// Starts the FSM.
00246     /// Can only be called once.
00247     /// Subsequent calls will do nothing.
00248     //*******************************************
00249     void start()
00250     {
00251       // Can only be started once.
00252       if (p_state == std::nullptr)
00253       {
00254         p_state = state_list[0];
00255         ETL_ASSERT(p_state != std::nullptr, ETL_ERROR(etl::fsm_null_state_exception));
00256 
00257         p_state->on_enter_state();
00258       }
00259     }
00260 
00261     //*******************************************
00262     /// Top level message handler for the FSM.
00263     //*******************************************
00264     void receive(const etl::imessage& message)
00265     {
00266       etl::null_message_router nmr;
00267       receive(nmr, message);
00268     }
00269 
00270     //*******************************************
00271     /// Top level message handler for the FSM.
00272     //*******************************************
00273     void receive(etl::imessage_router& source, const etl::imessage& message)
00274     {
00275       etl::fsm_state_id_t next_state_id = p_state->process_event(source, message);
00276       ETL_ASSERT(next_state_id < number_of_states, ETL_ERROR(etl::fsm_state_id_exception));
00277 
00278       etl::ifsm_state* p_next_state = state_list[next_state_id];
00279 
00280       // Have we changed state?
00281       if (p_next_state != p_state)
00282       {
00283         do
00284         {
00285           p_state->on_exit_state();
00286           p_state = p_next_state;
00287 
00288           next_state_id = p_state->on_enter_state();
00289           ETL_ASSERT(next_state_id < number_of_states, ETL_ERROR(etl::fsm_state_id_exception));
00290 
00291           p_next_state = state_list[next_state_id];
00292 
00293         } while (p_next_state != p_state); // Have we changed state again?
00294       }
00295     }
00296 
00297     using imessage_router::accepts;
00298 
00299     //*******************************************
00300     /// Does this FSM accept the message id?
00301     /// Yes, it accepts everything!
00302     //*******************************************
00303     bool accepts(etl::message_id_t id) const
00304     {
00305       return true;
00306     }
00307 
00308     //*******************************************
00309     /// Gets the current state id.
00310     //*******************************************
00311     etl::fsm_state_id_t get_state_id() const
00312     {
00313       ETL_ASSERT(p_state != std::nullptr, ETL_ERROR(etl::fsm_null_state_exception));
00314       return p_state->get_state_id();
00315     }
00316 
00317     //*******************************************
00318     /// Gets a reference to the current state interface.
00319     //*******************************************
00320     ifsm_state& get_state()
00321     {
00322       ETL_ASSERT(p_state != std::nullptr, ETL_ERROR(etl::fsm_null_state_exception));
00323       return *p_state;
00324     }
00325 
00326     //*******************************************
00327     /// Gets a const reference to the current state interface.
00328     //*******************************************
00329     const ifsm_state& get_state() const
00330     {
00331       ETL_ASSERT(p_state != std::nullptr, ETL_ERROR(etl::fsm_null_state_exception));
00332       return *p_state;
00333     }
00334 
00335     //*******************************************
00336     /// Checks if the FSM has been started.
00337     //*******************************************
00338     bool is_started() const
00339     {
00340       return p_state != std::nullptr;
00341     }
00342 
00343     //*******************************************
00344     /// Reset the FSM to pre-started state.
00345     //*******************************************
00346     void reset()
00347     {
00348       p_state = std::nullptr;
00349     }
00350 
00351   private:
00352 
00353     etl::ifsm_state*    p_state;          ///< A pointer to the current state.
00354     etl::ifsm_state**   state_list;       ///< The list of added states.
00355     etl::fsm_state_id_t number_of_states; ///< The number of states.
00356   };
00357 
00358   /*[[[cog
00359   import cog
00360   ################################################
00361   # The first definition for all of the events.
00362   ################################################
00363   cog.outl("//***************************************************************************")
00364   cog.outl("// The definition for all %s message types." % Handlers)
00365   cog.outl("//***************************************************************************")
00366   cog.outl("template <typename TContext, typename TDerived, const etl::fsm_state_id_t STATE_ID_, ")
00367   cog.out("          ")
00368   for n in range(1, int(Handlers)):
00369       cog.out("typename T%s = void, " % n)
00370       if n % 4 == 0:
00371           cog.outl("")
00372           cog.out("          ")
00373   cog.outl("typename T%s = void>" % Handlers)
00374   cog.outl("class fsm_state : public ifsm_state")
00375   cog.outl("{")
00376   cog.outl("public:")
00377   cog.outl("")
00378   cog.outl("  enum")
00379   cog.outl("  {")
00380   cog.outl("    STATE_ID = STATE_ID_")
00381   cog.outl("  };")
00382   cog.outl("")
00383   cog.outl("  fsm_state()")
00384   cog.outl("    : ifsm_state(STATE_ID)")
00385   cog.outl("  {")
00386   cog.outl("  }")
00387   cog.outl("")
00388   cog.outl("protected:")
00389   cog.outl("")
00390   cog.outl("  inline TContext& get_fsm_context() const")
00391   cog.outl("  {")
00392   cog.outl("    return static_cast<TContext&>(ifsm_state::get_fsm_context());")
00393   cog.outl("  }")
00394   cog.outl("")
00395   cog.outl("private:")
00396   cog.outl("")
00397   cog.outl("  etl::fsm_state_id_t process_event(etl::imessage_router& source, const etl::imessage& message)")
00398   cog.outl("  {")
00399   cog.outl("    etl::fsm_state_id_t new_state_id;")
00400   cog.outl("    etl::message_id_t event_id = message.message_id;")
00401   cog.outl("")
00402   cog.outl("    switch (event_id)")
00403   cog.outl("    {")
00404   for n in range(1, int(Handlers) + 1):
00405       cog.out("      case T%d::ID:" % n)
00406       cog.out(" new_state_id = static_cast<TDerived*>(this)->on_event(source, static_cast<const T%d&>(message));" % n)
00407       cog.outl(" break;")
00408   cog.out("      default:")
00409   cog.out(" new_state_id = static_cast<TDerived*>(this)->on_event_unknown(source, message);")
00410   cog.outl(" break;")
00411   cog.outl("    }")
00412   cog.outl("")
00413   cog.outl("    return new_state_id;")
00414   cog.outl("  }")
00415   cog.outl("};")
00416 
00417   ####################################
00418   # All of the other specialisations.
00419   ####################################
00420   for n in range(int(Handlers) - 1, 0, -1):
00421       cog.outl("")
00422       cog.outl("//***************************************************************************")
00423       if n == 1:
00424           cog.outl("// Specialisation for %d message type." % n)
00425       else:
00426           cog.outl("// Specialisation for %d message types." % n)
00427       cog.outl("//***************************************************************************")
00428       cog.outl("template <typename TContext, typename TDerived, const etl::fsm_state_id_t STATE_ID_, ")
00429       cog.out("          ")
00430       for t in range(1, n):
00431           cog.out("typename T%d, " % t)
00432           if t % 4 == 0:
00433               cog.outl("")
00434               cog.out("          ")
00435       cog.outl("typename T%d>" % n)
00436       cog.out("class fsm_state<TContext, TDerived, STATE_ID_, ")
00437       for t in range(1, n + 1):
00438           cog.out("T%d, " % t)
00439       if t % 16 == 0:
00440           cog.outl("")
00441           cog.out("               ")
00442       for t in range(n + 1, int(Handlers)):
00443           cog.out("void, ")
00444       if t % 16 == 0:
00445           cog.outl("")
00446           cog.out("               ")
00447       cog.outl("void> : public ifsm_state")
00448       cog.outl("{")
00449       cog.outl("public:")
00450       cog.outl("")
00451       cog.outl("  enum")
00452       cog.outl("  {")
00453       cog.outl("    STATE_ID = STATE_ID_")
00454       cog.outl("  };")
00455       cog.outl("")
00456       cog.outl("  fsm_state()")
00457       cog.outl("    : ifsm_state(STATE_ID)")
00458       cog.outl("  {")
00459       cog.outl("  }")
00460       cog.outl("")
00461       cog.outl("protected:")
00462       cog.outl("")
00463       cog.outl("  inline TContext& get_fsm_context() const")
00464       cog.outl("  {")
00465       cog.outl("    return static_cast<TContext&>(ifsm_state::get_fsm_context());")
00466       cog.outl("  }")
00467       cog.outl("")
00468       cog.outl("private:")
00469       cog.outl("")
00470       cog.outl("  etl::fsm_state_id_t process_event(etl::imessage_router& source, const etl::imessage& message)")
00471       cog.outl("  {")
00472       cog.outl("    etl::fsm_state_id_t new_state_id;")
00473       cog.outl("    etl::message_id_t event_id = message.message_id;")
00474       cog.outl("")
00475       cog.outl("    switch (event_id)")
00476       cog.outl("    {")
00477       for n in range(1, n + 1):
00478           cog.out("      case T%d::ID:" % n)
00479           cog.out(" new_state_id = static_cast<TDerived*>(this)->on_event(source, static_cast<const T%d&>(message));" % n)
00480           cog.outl(" break;")
00481       cog.out("      default:")
00482       cog.out(" new_state_id = static_cast<TDerived*>(this)->on_event_unknown(source, message);")
00483       cog.outl(" break;")
00484       cog.outl("    }")
00485       cog.outl("")
00486       cog.outl("    return new_state_id;")
00487       cog.outl("  }")
00488       cog.outl("};")
00489   ####################################
00490   # Specialisation for zero messages.
00491   ####################################
00492   cog.outl("")
00493   cog.outl("//***************************************************************************")
00494   cog.outl("// Specialisation for 0 message types.")
00495   cog.outl("//***************************************************************************")
00496   cog.outl("template <typename TContext, typename TDerived, const etl::fsm_state_id_t STATE_ID_>")
00497   cog.out("class fsm_state<TContext, TDerived, STATE_ID_, ")
00498   for t in range(1, int(Handlers)):
00499       cog.out("void, ")
00500   if t % 16 == 0:
00501       cog.outl("")
00502       cog.out("               ")
00503   cog.outl("void> : public ifsm_state")
00504   cog.outl("{")
00505   cog.outl("public:")
00506   cog.outl("")
00507   cog.outl("  enum")
00508   cog.outl("  {")
00509   cog.outl("    STATE_ID = STATE_ID_")
00510   cog.outl("  };")
00511   cog.outl("")
00512   cog.outl("  fsm_state()")
00513   cog.outl("    : ifsm_state(STATE_ID)")
00514   cog.outl("  {")
00515   cog.outl("  }")
00516   cog.outl("")
00517   cog.outl("  inline TContext& get_fsm_context() const")
00518   cog.outl("  {")
00519   cog.outl("    return static_cast<TContext&>(ifsm_state::get_fsm_context());")
00520   cog.outl("  }")
00521   cog.outl("private:")
00522   cog.outl("")
00523   cog.outl("  etl::fsm_state_id_t process_event(etl::imessage_router& source, const etl::imessage& message)")
00524   cog.outl("  {")
00525   cog.outl("    return static_cast<TDerived*>(this)->on_event_unknown(source, message);")
00526   cog.outl("  }")
00527   cog.outl("};")
00528   ]]]*/
00529   /*[[[end]]]*/
00530 }
00531 
00532 #undef ETL_FILE
00533 
00534 #ifdef ETL_COMPILER_MICROSOFT
00535 #define max(a,b) (((a) > (b)) ? (a) : (b))
00536 #endif
00537 
00538 #endif
00539