diff --git a/src/CreateFSM.bat b/src/CreateFSM.bat new file mode 100644 index 00000000..1cb113c4 --- /dev/null +++ b/src/CreateFSM.bat @@ -0,0 +1 @@ +python -m cogapp -d -e -ofsm.h -DHandlers=16 fsm_generator.h \ No newline at end of file diff --git a/src/CreateMessageRouter.bat b/src/CreateMessageRouter.bat new file mode 100644 index 00000000..710d838d --- /dev/null +++ b/src/CreateMessageRouter.bat @@ -0,0 +1 @@ +python -m cogapp -d -e -omessage_router.h -DHandlers=16 message_router_generator.h \ No newline at end of file diff --git a/src/experimental/fsm.h b/src/experimental/fsm.h deleted file mode 100644 index 6c65c20a..00000000 --- a/src/experimental/fsm.h +++ /dev/null @@ -1,339 +0,0 @@ - -#ifndef __ETL_FSM__ -#define __ETL_FSM__ - -#include - -#include "../array.h" -#include "../nullptr.h" -#include "../error_handler.h" -#include "../exception.h" - -#undef ETL_FILE -#define ETL_FILE "34" - -namespace etl -{ -#if !defined(ETL_FSM_STATE_ID_TYPE) - typedef uint_least8_t fsm_state_id_t; -#else - typedef ETL_FSM_STATE_ID_TYPE fsm_state_id_t; -#endif - -#if !defined(ETL_FSM_EVENT_ID_TYPE) - typedef uint_least8_t fsm_event_id_t; -#else - typedef ETL_FSM_STATE_ID_TYPE fsm_event_id_t; -#endif - - //*************************************************************************** - /// Base exception class for FSM. - //*************************************************************************** - class fsm_exception : public etl::exception - { - public: - - fsm_exception(string_type what, string_type file_name, numeric_type line_number) - : etl::exception(what, file_name, line_number) - { - } - }; - - //*************************************************************************** - /// Exception for null state pointer. - //*************************************************************************** - class fsm_nullstate_exception : public etl::fsm_exception - { - public: - - fsm_nullstate_exception(string_type file_name, numeric_type line_number) - : etl::fsm_exception(ETL_ERROR_TEXT("fsm:null state", ETL_FILE"A"), file_name, line_number) - { - } - }; - - //*************************************************************************** - /// Exception for invalid state id. - //*************************************************************************** - class fsm_state_id_exception : public etl::fsm_exception - { - public: - - fsm_state_id_exception(string_type file_name, numeric_type line_number) - : etl::fsm_exception(ETL_ERROR_TEXT("fsm:state id", ETL_FILE"B"), file_name, line_number) - { - } - }; - - //*************************************************************************** - /// Interface class for FSM events. - //*************************************************************************** - class ifsm_event - { - public: - - //******************************************* - /// Gets the id for this event. - //******************************************* - etl::fsm_event_id_t get_event_id() const - { - return event_id; - } - - protected: - - //******************************************* - /// Constructor. - //******************************************* - ifsm_event(etl::fsm_event_id_t event_id_) - : event_id(event_id_) - { - } - - private: - - // The event id. - const etl::fsm_event_id_t event_id; - }; - - //*************************************************************************** - /// Base class for FSM events. - //*************************************************************************** - template - class fsm_event : public etl::ifsm_event - { - public: - - enum - { - EVENT_ID = EVENT_ID_ - }; - - //******************************************* - /// Constructor. - //******************************************* - fsm_event() - : ifsm_event(EVENT_ID) - { - } - }; - - //*************************************************************************** - /// Interface class for FSM states. - //*************************************************************************** - class ifsm_state - { - public: - - //******************************************* - /// Gets the id for this state. - //******************************************* - etl::fsm_state_id_t get_state_id() const - { - return state_id; - } - - virtual etl::fsm_state_id_t on_event(const etl::ifsm_event& event) = 0; - - protected: - - //******************************************* - /// Constructor. - //******************************************* - ifsm_state(etl::fsm_state_id_t state_id_) - : state_id(state_id_) - { - } - - virtual void on_enter_state() {}; // By default, do nothing. - virtual void on_exit_state() {}; // By default, do nothing. - - private: - - // The state id. - const etl::fsm_state_id_t state_id; - - // Disabled. - ifsm_state(const ifsm_state&); - ifsm_state& operator =(const ifsm_state&); - }; - - //*************************************************************************** - // To be COG generated. - /// Base class for FSM states. - //*************************************************************************** - template - class fsm_state : public ifsm_state - { - public: - - enum - { - STATE_ID = STATE_ID_ - }; - - //******************************************* - /// Constructor. - //******************************************* - fsm_state() - : ifsm_state(STATE_ID) - { - } - - //******************************************* - /// Top level event handler for the state. - //******************************************* - etl::fsm_state_id_t on_event(const etl::ifsm_event& event) - { - etl::fsm_state_id_t new_state_id; - etl::fsm_event_id_t id = event.get_event_id(); - - switch (id) - { - case T1::EVENT_ID: new_state_id = static_cast(*this).on_event(static_cast(event)); break; - case T2::EVENT_ID: new_state_id = static_cast(*this).on_event(static_cast(event)); break; - default: new_state_id = static_cast(*this).on_unknown_event(event); break; - } - - return new_state_id; - } - - private: - - // Disabled. - fsm_state(const fsm_state&); - fsm_state& operator =(const fsm_state&); - }; - - //*************************************************************************** - template - class fsm - { - public: - - //******************************************* - /// Constructor. - //******************************************* - fsm() - : p_state(nullptr) - { - state_list.fill(nullptr); - } - - //******************************************* - /// Starts the FSM. - /// Can only be called once. - /// Subsequent calls will do nothing. - //******************************************* - void start() - { - // Can only be started once. - if (p_state == nullptr) - { - p_state = state_list[0]; - ETL_ASSERT(p_state != nullptr, ETL_ERROR(etl::fsm_nullstate_exception)); - - p_state->on_enter_state(); - } - } - - //******************************************* - /// Adds a state to the FSM. - /// If the state has the same id as one already added - /// then the current state will be overwritten. - //******************************************* - void add_state(etl::ifsm_state& state) - { - ETL_ASSERT(state.get_state_id() < MAX_STATES, ETL_ERROR(etl::fsm_state_id_exception)); - state_list[state.get_state_id()] = &state; - } - - //******************************************* - /// Top level event handler for the FSM. - //******************************************* - void on_event(const etl::ifsm_event& event) - { - etl::fsm_state_id_t next_state_id = p_state->on_event(event); - ETL_ASSERT(next_state_id < MAX_STATES, ETL_ERROR(etl::fsm_state_id_exception)); - - // Have we changed state? - if (next_state_id != p_state->get_state_id()) - { - p_state->on_exit_state(); - - p_state = state_list[next_state_id]; - ETL_ASSERT(p_state != nullptr, ETL_ERROR(etl::fsm_nullstate_exception)); - - p_state->on_enter_state(); - } - } - - //******************************************* - /// Gets the current state id. - //******************************************* - etl::fsm_state_id_t get_state_id() const - { - return p_state->get_state_id(); - } - - //******************************************* - /// Gets a reference to the current state interface. - //******************************************* - ifsm_state& get_state() - { - return *p_state; - } - - //******************************************* - /// Gets a const reference to the current state interface. - //******************************************* - const ifsm_state& get_state() const - { - return *p_state; - } - - private: - - etl::ifsm_state* p_state; ///< A pointer to the current state. - etl::array state_list; ///< The list of added states. - - // Disabled. - fsm(const fsm&); - fsm& operator =(const fsm&); - }; -} - -#undef ETL_FILE - -// class Start : public etl::fsm_event<0> -// { - -// }; - -// class Idle : public etl::fsm_state -// { -// public: - -// etl::fsm_state_id_t on_event(const Start& event) -// { -// return 1; -// } - -// etl::fsm_state_id_t on_unknown_event(const etl::ifsm_event& event) -// { -// return THIS_STATE_ID; -// } -// }; - -// etl::fsm<3> machine; - -// void F() -// { -// Idle idle; - -// machine.add_state(idle); -// machine.start(); -// machine.on_event(Start()); -// } - -#endif \ No newline at end of file diff --git a/src/fsm.h b/src/fsm.h new file mode 100644 index 00000000..9b049dde --- /dev/null +++ b/src/fsm.h @@ -0,0 +1,1073 @@ +/****************************************************************************** +The MIT License(MIT) + +Embedded Template Library. +https://github.com/ETLCPP/etl +http://www.etlcpp.com + +Copyright(c) 2017 jwellbelove + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files(the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions : + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +******************************************************************************/ + +#ifndef __ETL_FSM__ +#define __ETL_FSM__ + +#include + +#include "array.h" +#include "nullptr.h" +#include "error_handler.h" +#include "exception.h" +#include "user_type.h" + +#undef ETL_FILE +#define ETL_FILE "34" + +namespace etl +{ +#if !defined(ETL_FSM_STATE_ID_TYPE) + typedef uint_least8_t fsm_state_id_t; +#else + typedef ETL_FSM_STATE_ID_TYPE fsm_state_id_t; +#endif + +#if !defined(ETL_FSM_EVENT_ID_TYPE) + typedef uint_least8_t fsm_event_id_t; +#else + typedef ETL_FSM_STATE_ID_TYPE fsm_event_id_t; +#endif + + //*************************************************************************** + /// Base exception class for FSM. + //*************************************************************************** + class fsm_exception : public etl::exception + { + public: + + fsm_exception(string_type what, string_type file_name, numeric_type line_number) + : etl::exception(what, file_name, line_number) + { + } + }; + + //*************************************************************************** + /// Exception for null state pointer. + //*************************************************************************** + class fsm_null_state_exception : public etl::fsm_exception + { + public: + + fsm_null_state_exception(string_type file_name, numeric_type line_number) + : etl::fsm_exception(ETL_ERROR_TEXT("fsm:null state", ETL_FILE"A"), file_name, line_number) + { + } + }; + + //*************************************************************************** + /// Exception for invalid state id. + //*************************************************************************** + class fsm_state_id_exception : public etl::fsm_exception + { + public: + + fsm_state_id_exception(string_type file_name, numeric_type line_number) + : etl::fsm_exception(ETL_ERROR_TEXT("fsm:state id", ETL_FILE"B"), file_name, line_number) + { + } + }; + + //*************************************************************************** + /// Exception for incompatible state list. + //*************************************************************************** + class fsm_state_list_exception : public etl::fsm_exception + { + public: + + fsm_state_list_exception(string_type file_name, numeric_type line_number) + : etl::fsm_exception(ETL_ERROR_TEXT("fsm:state list", ETL_FILE"C"), file_name, line_number) + { + } + }; + + //*************************************************************************** + /// Interface class for FSM events. + //*************************************************************************** + class ifsm_event + { + public: + + //******************************************* + /// Gets the id for this event. + //******************************************* + etl::fsm_event_id_t get_event_id() const + { + return event_id; + } + + protected: + + //******************************************* + /// Constructor. + //******************************************* + ifsm_event(etl::fsm_event_id_t event_id_) + : event_id(event_id_) + { + } + + private: + + // The event id. + const etl::fsm_event_id_t event_id; + }; + + //*************************************************************************** + /// Base class for FSM events. + //*************************************************************************** + template + class fsm_event : public etl::ifsm_event + { + public: + + enum + { + EVENT_ID = EVENT_ID_ + }; + + //******************************************* + /// Constructor. + //******************************************* + fsm_event() + : ifsm_event(EVENT_ID) + { + } + }; + + //*************************************************************************** + /// Interface class for FSM states. + //*************************************************************************** + class ifsm_state + { + public: + + friend class fsm; + + //******************************************* + /// Gets the id for this state. + //******************************************* + etl::fsm_state_id_t get_state_id() const + { + return state_id; + } + + virtual etl::fsm_state_id_t on_event(const etl::ifsm_event& event) = 0; + + protected: + + //******************************************* + /// Constructor. + //******************************************* + ifsm_state(etl::fsm_state_id_t state_id_) + : state_id(state_id_) + { + } + + virtual void on_enter_state() {}; // By default, do nothing. + virtual void on_exit_state() {}; // By default, do nothing. + + private: + + // The state id. + const etl::fsm_state_id_t state_id; + + // Disabled. + ifsm_state(const ifsm_state&); + ifsm_state& operator =(const ifsm_state&); + }; + + //*************************************************************************** + class fsm + { + public: + + //******************************************* + /// Constructor. + //******************************************* + fsm() + : p_state(nullptr) + { + } + + //******************************************* + /// Set the states for the FSM + //******************************************* + template + void set_states(etl::ifsm_state** p_states, TSize size) + { + state_list = p_states; + number_of_states = etl::fsm_state_id_t(size); + + for (etl::fsm_state_id_t i = 0; i < size; ++i) + { + ETL_ASSERT((state_list[i] != nullptr), ETL_ERROR(etl::fsm_null_state_exception)); + } + + bool ok = (number_of_states > 0) && + etl::is_sorted(state_list, state_list + number_of_states, fsm::CompareStateId()); + + ETL_ASSERT(ok, ETL_ERROR(etl::fsm_state_list_exception)); + } + + //******************************************* + /// Starts the FSM. + /// Can only be called once. + /// Subsequent calls will do nothing. + //******************************************* + void start() + { + // Can only be started once. + if (p_state == nullptr) + { + p_state = state_list[0]; + ETL_ASSERT(p_state != nullptr, ETL_ERROR(etl::fsm_null_state_exception)); + + p_state->on_enter_state(); + } + } + + //******************************************* + /// Top level event handler for the FSM. + //******************************************* + void on_event(const etl::ifsm_event& event) + { + etl::fsm_state_id_t next_state_id = p_state->on_event(event); + ETL_ASSERT(next_state_id < number_of_states, ETL_ERROR(etl::fsm_state_id_exception)); + + // Have we changed state? + if (next_state_id != p_state->get_state_id()) + { + p_state->on_exit_state(); + + p_state = state_list[next_state_id]; + ETL_ASSERT(p_state != nullptr, ETL_ERROR(etl::fsm_null_state_exception)); + + p_state->on_enter_state(); + } + } + + //******************************************* + /// Gets the current state id. + //******************************************* + etl::fsm_state_id_t get_state_id() const + { + ETL_ASSERT(p_state != nullptr, ETL_ERROR(etl::fsm_null_state_exception)); + return p_state->get_state_id(); + } + + //******************************************* + /// Gets a reference to the current state interface. + //******************************************* + ifsm_state& get_state() + { + ETL_ASSERT(p_state != nullptr, ETL_ERROR(etl::fsm_null_state_exception)); + return *p_state; + } + + //******************************************* + /// Gets a const reference to the current state interface. + //******************************************* + const ifsm_state& get_state() const + { + ETL_ASSERT(p_state != nullptr, ETL_ERROR(etl::fsm_null_state_exception)); + return *p_state; + } + + //******************************************* + /// Checks if the FSM has been started. + //******************************************* + bool is_started() const + { + return p_state != nullptr; + } + + //******************************************* + /// Reset the FSM to pre-started state. + //******************************************* + void reset() + { + p_state = nullptr; + } + + private: + + struct CompareStateId + { + bool operator()(etl::ifsm_state* lhs, etl::ifsm_state* rhs) + { + return lhs->get_state_id() < rhs->get_state_id(); + } + }; + + etl::ifsm_state* p_state; ///< A pointer to the current state. + etl::ifsm_state** state_list; ///< The list of added states. + etl::fsm_state_id_t number_of_states; ///< The number of states. + }; + + //*************************************************************************** + // To generate to header file, run this at the command line. + // Note: You will need Python and COG installed. + // + // python -m cogapp -d -e -ofsm.h -DHandlers= fsm_generator.h + // Where is the number of messages to support. + // + // e.g. + // To generate handlers for up to 16 events... + // python -m cogapp -d -e -ofsm.h -DHandlers=16 fsm_generator.h + // + // See CreateFSM.bat + //*************************************************************************** + + //*************************************************************************** + // The code below has been auto generated. Do not manually edit. + //*************************************************************************** + + //*************************************************************************** + // The definition for all 16 message types. + //*************************************************************************** + template + class fsm_state : public ifsm_state + { + public: + + friend class fsm; + + enum + { + STATE_ID = STATE_ID_ + }; + + fsm_state() + : ifsm_state(STATE_ID) + { + } + + etl::fsm_state_id_t on_event(const etl::ifsm_event& event) + { + etl::fsm_state_id_t new_state_id; + etl::fsm_event_id_t event_id = event.get_event_id(); + + switch (event_id) + { + case T1::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T2::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T3::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T4::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T5::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T6::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T7::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T8::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T9::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T10::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T11::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T12::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T13::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T14::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T15::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T16::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + default: new_state_id = static_cast(this)->on_event_unknown(event); break; + } + + return new_state_id; + } + }; + + //*************************************************************************** + // Specialisation for 15 event types. + //*************************************************************************** + template + class fsm_state : public ifsm_state + { + public: + friend class fsm; + + + enum + { + STATE_ID = STATE_ID_ + }; + + fsm_state() + : ifsm_state(STATE_ID) + { + } + + etl::fsm_state_id_t on_event(const etl::ifsm_event& event) + { + etl::fsm_state_id_t new_state_id; + etl::fsm_event_id_t event_id = event.get_event_id(); + + switch (event_id) + { + case T1::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T2::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T3::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T4::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T5::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T6::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T7::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T8::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T9::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T10::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T11::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T12::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T13::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T14::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T15::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + default: new_state_id = static_cast(this)->on_event_unknown(event); break; + } + + return new_state_id; + } + }; + + //*************************************************************************** + // Specialisation for 14 event types. + //*************************************************************************** + template + class fsm_state : public ifsm_state + { + public: + friend class fsm; + + + enum + { + STATE_ID = STATE_ID_ + }; + + fsm_state() + : ifsm_state(STATE_ID) + { + } + + etl::fsm_state_id_t on_event(const etl::ifsm_event& event) + { + etl::fsm_state_id_t new_state_id; + etl::fsm_event_id_t event_id = event.get_event_id(); + + switch (event_id) + { + case T1::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T2::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T3::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T4::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T5::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T6::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T7::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T8::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T9::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T10::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T11::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T12::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T13::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T14::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + default: new_state_id = static_cast(this)->on_event_unknown(event); break; + } + + return new_state_id; + } + }; + + //*************************************************************************** + // Specialisation for 13 event types. + //*************************************************************************** + template + class fsm_state : public ifsm_state + { + public: + friend class fsm; + + + enum + { + STATE_ID = STATE_ID_ + }; + + fsm_state() + : ifsm_state(STATE_ID) + { + } + + etl::fsm_state_id_t on_event(const etl::ifsm_event& event) + { + etl::fsm_state_id_t new_state_id; + etl::fsm_event_id_t event_id = event.get_event_id(); + + switch (event_id) + { + case T1::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T2::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T3::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T4::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T5::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T6::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T7::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T8::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T9::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T10::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T11::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T12::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T13::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + default: new_state_id = static_cast(this)->on_event_unknown(event); break; + } + + return new_state_id; + } + }; + + //*************************************************************************** + // Specialisation for 12 event types. + //*************************************************************************** + template + class fsm_state : public ifsm_state + { + public: + friend class fsm; + + + enum + { + STATE_ID = STATE_ID_ + }; + + fsm_state() + : ifsm_state(STATE_ID) + { + } + + etl::fsm_state_id_t on_event(const etl::ifsm_event& event) + { + etl::fsm_state_id_t new_state_id; + etl::fsm_event_id_t event_id = event.get_event_id(); + + switch (event_id) + { + case T1::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T2::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T3::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T4::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T5::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T6::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T7::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T8::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T9::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T10::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T11::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T12::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + default: new_state_id = static_cast(this)->on_event_unknown(event); break; + } + + return new_state_id; + } + }; + + //*************************************************************************** + // Specialisation for 11 event types. + //*************************************************************************** + template + class fsm_state : public ifsm_state + { + public: + friend class fsm; + + + enum + { + STATE_ID = STATE_ID_ + }; + + fsm_state() + : ifsm_state(STATE_ID) + { + } + + etl::fsm_state_id_t on_event(const etl::ifsm_event& event) + { + etl::fsm_state_id_t new_state_id; + etl::fsm_event_id_t event_id = event.get_event_id(); + + switch (event_id) + { + case T1::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T2::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T3::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T4::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T5::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T6::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T7::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T8::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T9::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T10::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T11::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + default: new_state_id = static_cast(this)->on_event_unknown(event); break; + } + + return new_state_id; + } + }; + + //*************************************************************************** + // Specialisation for 10 event types. + //*************************************************************************** + template + class fsm_state : public ifsm_state + { + public: + friend class fsm; + + + enum + { + STATE_ID = STATE_ID_ + }; + + fsm_state() + : ifsm_state(STATE_ID) + { + } + + etl::fsm_state_id_t on_event(const etl::ifsm_event& event) + { + etl::fsm_state_id_t new_state_id; + etl::fsm_event_id_t event_id = event.get_event_id(); + + switch (event_id) + { + case T1::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T2::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T3::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T4::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T5::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T6::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T7::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T8::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T9::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T10::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + default: new_state_id = static_cast(this)->on_event_unknown(event); break; + } + + return new_state_id; + } + }; + + //*************************************************************************** + // Specialisation for 9 event types. + //*************************************************************************** + template + class fsm_state : public ifsm_state + { + public: + friend class fsm; + + + enum + { + STATE_ID = STATE_ID_ + }; + + fsm_state() + : ifsm_state(STATE_ID) + { + } + + etl::fsm_state_id_t on_event(const etl::ifsm_event& event) + { + etl::fsm_state_id_t new_state_id; + etl::fsm_event_id_t event_id = event.get_event_id(); + + switch (event_id) + { + case T1::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T2::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T3::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T4::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T5::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T6::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T7::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T8::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T9::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + default: new_state_id = static_cast(this)->on_event_unknown(event); break; + } + + return new_state_id; + } + }; + + //*************************************************************************** + // Specialisation for 8 event types. + //*************************************************************************** + template + class fsm_state : public ifsm_state + { + public: + friend class fsm; + + + enum + { + STATE_ID = STATE_ID_ + }; + + fsm_state() + : ifsm_state(STATE_ID) + { + } + + etl::fsm_state_id_t on_event(const etl::ifsm_event& event) + { + etl::fsm_state_id_t new_state_id; + etl::fsm_event_id_t event_id = event.get_event_id(); + + switch (event_id) + { + case T1::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T2::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T3::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T4::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T5::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T6::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T7::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T8::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + default: new_state_id = static_cast(this)->on_event_unknown(event); break; + } + + return new_state_id; + } + }; + + //*************************************************************************** + // Specialisation for 7 event types. + //*************************************************************************** + template + class fsm_state : public ifsm_state + { + public: + friend class fsm; + + + enum + { + STATE_ID = STATE_ID_ + }; + + fsm_state() + : ifsm_state(STATE_ID) + { + } + + etl::fsm_state_id_t on_event(const etl::ifsm_event& event) + { + etl::fsm_state_id_t new_state_id; + etl::fsm_event_id_t event_id = event.get_event_id(); + + switch (event_id) + { + case T1::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T2::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T3::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T4::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T5::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T6::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T7::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + default: new_state_id = static_cast(this)->on_event_unknown(event); break; + } + + return new_state_id; + } + }; + + //*************************************************************************** + // Specialisation for 6 event types. + //*************************************************************************** + template + class fsm_state : public ifsm_state + { + public: + friend class fsm; + + + enum + { + STATE_ID = STATE_ID_ + }; + + fsm_state() + : ifsm_state(STATE_ID) + { + } + + etl::fsm_state_id_t on_event(const etl::ifsm_event& event) + { + etl::fsm_state_id_t new_state_id; + etl::fsm_event_id_t event_id = event.get_event_id(); + + switch (event_id) + { + case T1::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T2::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T3::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T4::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T5::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T6::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + default: new_state_id = static_cast(this)->on_event_unknown(event); break; + } + + return new_state_id; + } + }; + + //*************************************************************************** + // Specialisation for 5 event types. + //*************************************************************************** + template + class fsm_state : public ifsm_state + { + public: + friend class fsm; + + + enum + { + STATE_ID = STATE_ID_ + }; + + fsm_state() + : ifsm_state(STATE_ID) + { + } + + etl::fsm_state_id_t on_event(const etl::ifsm_event& event) + { + etl::fsm_state_id_t new_state_id; + etl::fsm_event_id_t event_id = event.get_event_id(); + + switch (event_id) + { + case T1::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T2::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T3::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T4::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T5::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + default: new_state_id = static_cast(this)->on_event_unknown(event); break; + } + + return new_state_id; + } + }; + + //*************************************************************************** + // Specialisation for 4 event types. + //*************************************************************************** + template + class fsm_state : public ifsm_state + { + public: + friend class fsm; + + + enum + { + STATE_ID = STATE_ID_ + }; + + fsm_state() + : ifsm_state(STATE_ID) + { + } + + etl::fsm_state_id_t on_event(const etl::ifsm_event& event) + { + etl::fsm_state_id_t new_state_id; + etl::fsm_event_id_t event_id = event.get_event_id(); + + switch (event_id) + { + case T1::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T2::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T3::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T4::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + default: new_state_id = static_cast(this)->on_event_unknown(event); break; + } + + return new_state_id; + } + }; + + //*************************************************************************** + // Specialisation for 3 event types. + //*************************************************************************** + template + class fsm_state : public ifsm_state + { + public: + friend class fsm; + + + enum + { + STATE_ID = STATE_ID_ + }; + + fsm_state() + : ifsm_state(STATE_ID) + { + } + + etl::fsm_state_id_t on_event(const etl::ifsm_event& event) + { + etl::fsm_state_id_t new_state_id; + etl::fsm_event_id_t event_id = event.get_event_id(); + + switch (event_id) + { + case T1::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T2::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T3::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + default: new_state_id = static_cast(this)->on_event_unknown(event); break; + } + + return new_state_id; + } + }; + + //*************************************************************************** + // Specialisation for 2 event types. + //*************************************************************************** + template + class fsm_state : public ifsm_state + { + public: + friend class fsm; + + + enum + { + STATE_ID = STATE_ID_ + }; + + fsm_state() + : ifsm_state(STATE_ID) + { + } + + etl::fsm_state_id_t on_event(const etl::ifsm_event& event) + { + etl::fsm_state_id_t new_state_id; + etl::fsm_event_id_t event_id = event.get_event_id(); + + switch (event_id) + { + case T1::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + case T2::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + default: new_state_id = static_cast(this)->on_event_unknown(event); break; + } + + return new_state_id; + } + }; + + //*************************************************************************** + // Specialisation for 1 event type. + //*************************************************************************** + template + class fsm_state : public ifsm_state + { + public: + friend class fsm; + + + enum + { + STATE_ID = STATE_ID_ + }; + + fsm_state() + : ifsm_state(STATE_ID) + { + } + + etl::fsm_state_id_t on_event(const etl::ifsm_event& event) + { + etl::fsm_state_id_t new_state_id; + etl::fsm_event_id_t event_id = event.get_event_id(); + + switch (event_id) + { + case T1::EVENT_ID: new_state_id = static_cast(this)->on_event(static_cast(event)); break; + default: new_state_id = static_cast(this)->on_event_unknown(event); break; + } + + return new_state_id; + } + }; +} + +#undef ETL_FILE + +#endif \ No newline at end of file diff --git a/src/fsm_generator.h b/src/fsm_generator.h new file mode 100644 index 00000000..4888e91a --- /dev/null +++ b/src/fsm_generator.h @@ -0,0 +1,477 @@ +/****************************************************************************** +The MIT License(MIT) + +Embedded Template Library. +https://github.com/ETLCPP/etl +http://www.etlcpp.com + +Copyright(c) 2017 jwellbelove + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files(the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions : + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +******************************************************************************/ + +#ifndef __ETL_FSM__ +#define __ETL_FSM__ + +#include + +#include "array.h" +#include "nullptr.h" +#include "error_handler.h" +#include "exception.h" +#include "user_type.h" + +#undef ETL_FILE +#define ETL_FILE "34" + +namespace etl +{ +#if !defined(ETL_FSM_STATE_ID_TYPE) + typedef uint_least8_t fsm_state_id_t; +#else + typedef ETL_FSM_STATE_ID_TYPE fsm_state_id_t; +#endif + +#if !defined(ETL_FSM_EVENT_ID_TYPE) + typedef uint_least8_t fsm_event_id_t; +#else + typedef ETL_FSM_STATE_ID_TYPE fsm_event_id_t; +#endif + + //*************************************************************************** + /// Base exception class for FSM. + //*************************************************************************** + class fsm_exception : public etl::exception + { + public: + + fsm_exception(string_type what, string_type file_name, numeric_type line_number) + : etl::exception(what, file_name, line_number) + { + } + }; + + //*************************************************************************** + /// Exception for null state pointer. + //*************************************************************************** + class fsm_null_state_exception : public etl::fsm_exception + { + public: + + fsm_null_state_exception(string_type file_name, numeric_type line_number) + : etl::fsm_exception(ETL_ERROR_TEXT("fsm:null state", ETL_FILE"A"), file_name, line_number) + { + } + }; + + //*************************************************************************** + /// Exception for invalid state id. + //*************************************************************************** + class fsm_state_id_exception : public etl::fsm_exception + { + public: + + fsm_state_id_exception(string_type file_name, numeric_type line_number) + : etl::fsm_exception(ETL_ERROR_TEXT("fsm:state id", ETL_FILE"B"), file_name, line_number) + { + } + }; + + //*************************************************************************** + /// Exception for incompatible state list. + //*************************************************************************** + class fsm_state_list_exception : public etl::fsm_exception + { + public: + + fsm_state_list_exception(string_type file_name, numeric_type line_number) + : etl::fsm_exception(ETL_ERROR_TEXT("fsm:state list", ETL_FILE"C"), file_name, line_number) + { + } + }; + + //*************************************************************************** + /// Interface class for FSM events. + //*************************************************************************** + class ifsm_event + { + public: + + //******************************************* + /// Gets the id for this event. + //******************************************* + etl::fsm_event_id_t get_event_id() const + { + return event_id; + } + + protected: + + //******************************************* + /// Constructor. + //******************************************* + ifsm_event(etl::fsm_event_id_t event_id_) + : event_id(event_id_) + { + } + + private: + + // The event id. + const etl::fsm_event_id_t event_id; + }; + + //*************************************************************************** + /// Base class for FSM events. + //*************************************************************************** + template + class fsm_event : public etl::ifsm_event + { + public: + + enum + { + EVENT_ID = EVENT_ID_ + }; + + //******************************************* + /// Constructor. + //******************************************* + fsm_event() + : ifsm_event(EVENT_ID) + { + } + }; + + //*************************************************************************** + /// Interface class for FSM states. + //*************************************************************************** + class ifsm_state + { + public: + + friend class fsm; + + //******************************************* + /// Gets the id for this state. + //******************************************* + etl::fsm_state_id_t get_state_id() const + { + return state_id; + } + + virtual etl::fsm_state_id_t on_event(const etl::ifsm_event& event) = 0; + + protected: + + //******************************************* + /// Constructor. + //******************************************* + ifsm_state(etl::fsm_state_id_t state_id_) + : state_id(state_id_) + { + } + + virtual void on_enter_state() {}; // By default, do nothing. + virtual void on_exit_state() {}; // By default, do nothing. + + private: + + // The state id. + const etl::fsm_state_id_t state_id; + + // Disabled. + ifsm_state(const ifsm_state&); + ifsm_state& operator =(const ifsm_state&); + }; + + //*************************************************************************** + class fsm + { + public: + + //******************************************* + /// Constructor. + //******************************************* + fsm() + : p_state(nullptr) + { + } + + //******************************************* + /// Set the states for the FSM + //******************************************* + template + void set_states(etl::ifsm_state** p_states, TSize size) + { + state_list = p_states; + number_of_states = etl::fsm_state_id_t(size); + + for (etl::fsm_state_id_t i = 0; i < size; ++i) + { + ETL_ASSERT((state_list[i] != nullptr), ETL_ERROR(etl::fsm_null_state_exception)); + } + + bool ok = (number_of_states > 0) && + etl::is_sorted(state_list, state_list + number_of_states, fsm::CompareStateId()); + + ETL_ASSERT(ok, ETL_ERROR(etl::fsm_state_list_exception)); + } + + //******************************************* + /// Starts the FSM. + /// Can only be called once. + /// Subsequent calls will do nothing. + //******************************************* + void start() + { + // Can only be started once. + if (p_state == nullptr) + { + p_state = state_list[0]; + ETL_ASSERT(p_state != nullptr, ETL_ERROR(etl::fsm_null_state_exception)); + + p_state->on_enter_state(); + } + } + + //******************************************* + /// Top level event handler for the FSM. + //******************************************* + void on_event(const etl::ifsm_event& event) + { + etl::fsm_state_id_t next_state_id = p_state->on_event(event); + ETL_ASSERT(next_state_id < number_of_states, ETL_ERROR(etl::fsm_state_id_exception)); + + // Have we changed state? + if (next_state_id != p_state->get_state_id()) + { + p_state->on_exit_state(); + + p_state = state_list[next_state_id]; + ETL_ASSERT(p_state != nullptr, ETL_ERROR(etl::fsm_null_state_exception)); + + p_state->on_enter_state(); + } + } + + //******************************************* + /// Gets the current state id. + //******************************************* + etl::fsm_state_id_t get_state_id() const + { + ETL_ASSERT(p_state != nullptr, ETL_ERROR(etl::fsm_null_state_exception)); + return p_state->get_state_id(); + } + + //******************************************* + /// Gets a reference to the current state interface. + //******************************************* + ifsm_state& get_state() + { + ETL_ASSERT(p_state != nullptr, ETL_ERROR(etl::fsm_null_state_exception)); + return *p_state; + } + + //******************************************* + /// Gets a const reference to the current state interface. + //******************************************* + const ifsm_state& get_state() const + { + ETL_ASSERT(p_state != nullptr, ETL_ERROR(etl::fsm_null_state_exception)); + return *p_state; + } + + //******************************************* + /// Checks if the FSM has been started. + //******************************************* + bool is_started() const + { + return p_state != nullptr; + } + + //******************************************* + /// Reset the FSM to pre-started state. + //******************************************* + void reset() + { + p_state = nullptr; + } + + private: + + struct CompareStateId + { + bool operator()(etl::ifsm_state* lhs, etl::ifsm_state* rhs) + { + return lhs->get_state_id() < rhs->get_state_id(); + } + }; + + etl::ifsm_state* p_state; ///< A pointer to the current state. + etl::ifsm_state** state_list; ///< The list of added states. + etl::fsm_state_id_t number_of_states; ///< The number of states. + }; + + //*************************************************************************** + // To generate to header file, run this at the command line. + // Note: You will need Python and COG installed. + // + // python -m cogapp -d -e -ofsm.h -DHandlers= fsm_generator.h + // Where is the number of messages to support. + // + // e.g. + // To generate handlers for up to 16 events... + // python -m cogapp -d -e -ofsm.h -DHandlers=16 fsm_generator.h + // + // See CreateFSM.bat + //*************************************************************************** + + /*[[[cog + import cog + cog.outl("//***************************************************************************") + cog.outl("// The code below has been auto generated. Do not manually edit.") + cog.outl("//***************************************************************************") + cog.outl("") + ################################################ + # The first definition for all of the events. + ################################################ + cog.outl("//***************************************************************************") + cog.outl("// The definition for all %s message types." % Handlers) + cog.outl("//***************************************************************************") + cog.outl("template " % Handlers) + cog.outl("class fsm_state : public ifsm_state") + cog.outl("{") + cog.outl("public:") + cog.outl("") + + cog.outl(" friend class fsm;") + cog.outl("") + + cog.outl(" enum") + cog.outl(" {") + cog.outl(" STATE_ID = STATE_ID_") + cog.outl(" };") + cog.outl("") + cog.outl(" fsm_state()") + cog.outl(" : ifsm_state(STATE_ID)") + cog.outl(" {") + cog.outl(" }") + cog.outl("") + cog.outl(" etl::fsm_state_id_t on_event(const etl::ifsm_event& event)") + cog.outl(" {") + cog.outl(" etl::fsm_state_id_t new_state_id;") + cog.outl(" etl::fsm_event_id_t event_id = event.get_event_id();") + cog.outl("") + cog.outl(" switch (event_id)") + cog.outl(" {") + for n in range(1, int(Handlers) + 1): + cog.out(" case T%d::EVENT_ID:" % n) + cog.out(" new_state_id = static_cast(this)->on_event(static_cast(event));" % n) + cog.outl(" break;") + cog.out(" default:") + cog.out(" new_state_id = static_cast(this)->on_event_unknown(event);") + cog.outl(" break;") + cog.outl(" }") + cog.outl("") + cog.outl(" return new_state_id;") + cog.outl(" }") + cog.outl("};") + + #################################### + # All of the other specialisations. + #################################### + for n in range(int(Handlers) - 1, 0, -1): + cog.outl("") + cog.outl("//***************************************************************************") + if n == 1: + cog.outl("// Specialisation for %d event type." % n) + else: + cog.outl("// Specialisation for %d event types." % n) + cog.outl("//***************************************************************************") + cog.outl("template " % n) + cog.out("class fsm_state : public ifsm_state") + cog.outl("{") + cog.outl("public:") + + cog.outl(" friend class fsm;") + cog.outl("") + + cog.outl("") + cog.outl(" enum") + cog.outl(" {") + cog.outl(" STATE_ID = STATE_ID_") + cog.outl(" };") + cog.outl("") + cog.outl(" fsm_state()") + cog.outl(" : ifsm_state(STATE_ID)") + cog.outl(" {") + cog.outl(" }") + cog.outl("") + cog.outl(" etl::fsm_state_id_t on_event(const etl::ifsm_event& event)") + cog.outl(" {") + cog.outl(" etl::fsm_state_id_t new_state_id;") + cog.outl(" etl::fsm_event_id_t event_id = event.get_event_id();") + cog.outl("") + cog.outl(" switch (event_id)") + cog.outl(" {") + for n in range(1, n + 1): + cog.out(" case T%d::EVENT_ID:" % n) + cog.out(" new_state_id = static_cast(this)->on_event(static_cast(event));" % n) + cog.outl(" break;") + cog.out(" default:") + cog.out(" new_state_id = static_cast(this)->on_event_unknown(event);") + cog.outl(" break;") + cog.outl(" }") + cog.outl("") + cog.outl(" return new_state_id;") + cog.outl(" }") + cog.outl("};") + ]]]*/ + /*[[[end]]]*/ +} + +#undef ETL_FILE + +#endif \ No newline at end of file diff --git a/src/message_router.h b/src/message_router.h new file mode 100644 index 00000000..7911e04d --- /dev/null +++ b/src/message_router.h @@ -0,0 +1,1132 @@ +/****************************************************************************** +The MIT License(MIT) + +Embedded Template Library. +https://github.com/ETLCPP/etl +http://www.etlcpp.com + +Copyright(c) 2017 jwellbelove + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files(the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions : + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +******************************************************************************/ + +#ifndef __ETL_MESSAGE_ROUTER__ +#define __ETL_MESSAGE_ROUTER__ + +#include + +namespace etl +{ + //*************************************************************************** + class imessage + { + public: + + typedef size_t id_t; + + virtual ~imessage() {} + virtual id_t get_message_id() const = 0; + }; + + //*************************************************************************** + template + class message : public imessage + { + public: + + enum + { + ID = ID_ + }; + + id_t get_message_id() const + { + return ID; + } + }; + + //*************************************************************************** + template + class imessage_router + { + public: + + typedef TReturn return_type; + + virtual ~imessage_router() {} + virtual TReturn receive(imessage_router& source, const imessage& message) = 0; + + TReturn send_to(imessage_router& destination, const imessage& message) + { + return destination.receive(*this, message); + } + }; + + //*************************************************************************** + template <> + class imessage_router + { + public: + + typedef void return_type; + + virtual ~imessage_router() {} + virtual void receive(imessage_router& source, const imessage& message) = 0; + + void send_to(imessage_router& destination, const imessage& message) + { + destination.receive(*this, message); + } + }; + + //*************************************************************************** + template + class null_message_router : public imessage_router + { + public: + + TReturn receive(imessage_router& source, const imessage& message) + { + return TReturn(); + } + }; + + //*************************************************************************** + template <> + class null_message_router : public imessage_router + { + public: + + void receive(imessage_router& source, const imessage& message) + { + } + }; + + //*************************************************************************** + // To generate to header file, run this at the command line. + // Note: You will need Python and COG installed. + // + // python -m cogapp -d -e -omessage_router.h -DHandlers= message_router_generator.h + // Where is the number of messages to support. + // + // e.g. + // To generate handlers for up to 16 messages... + // python -m cogapp -d -e -omessage_router.h -DHandlers=16 message_router_generator.h + // + // See CreateMessageProcessor.bat + //*************************************************************************** + + //*************************************************************************** + // The code below has been auto generated. Do not manually edit. + //*************************************************************************** + + //*************************************************************************** + // The definition for all 16 message types, returning TReturn. + //*************************************************************************** + template + class message_router + : public imessage_router + { + public: + + TReturn receive(imessage_router& source, const imessage& msg) + { + const id_t id = msg.get_message_id(); + + switch (id) + { + case T1::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T2::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T3::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T4::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T5::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T6::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T7::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T8::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T9::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T10::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T11::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T12::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T13::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T14::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T15::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T16::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + default: return static_cast(this)->on_receive_unknown(source, msg); break; + } + } + }; + + //*************************************************************************** + // The definition for all 16 message types, returning void. + //*************************************************************************** + template + class message_router + : public imessage_router + { + public: + + void receive(imessage_router& source, const imessage& msg) + { + const id_t id = msg.get_message_id(); + + switch (id) + { + case T1::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T2::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T3::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T4::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T5::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T6::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T7::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T8::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T9::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T10::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T11::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T12::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T13::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T14::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T15::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T16::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + default: static_cast(this)->on_receive_unknown(source, msg); break; + } + } + }; + + //*************************************************************************** + // Specialisation for 15 message types, returning TReturn. + //*************************************************************************** + template + class message_router + : public imessage_router + { + public: + + return_type receive(imessage_router& source, const imessage& msg) + { + const size_t id = msg.get_message_id(); + + switch (id) + { + case T1::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T2::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T3::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T4::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T5::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T6::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T7::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T8::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T9::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T10::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T11::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T12::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T13::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T14::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T15::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + default: return static_cast(this)->on_receive_unknown(source, msg); break; + } + } + }; + + //*************************************************************************** + // Specialisation for 15 message types, returning void. + //*************************************************************************** + template + class message_router + : public imessage_router + { + public: + + void receive(imessage_router& source, const imessage& msg) + { + const size_t id = msg.get_message_id(); + + switch (id) + { + case T1::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T2::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T3::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T4::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T5::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T6::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T7::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T8::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T9::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T10::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T11::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T12::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T13::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T14::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T15::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + default: static_cast(this)->on_receive_unknown(source, msg); break; + } + } + }; + + //*************************************************************************** + // Specialisation for 14 message types, returning TReturn. + //*************************************************************************** + template + class message_router + : public imessage_router + { + public: + + return_type receive(imessage_router& source, const imessage& msg) + { + const size_t id = msg.get_message_id(); + + switch (id) + { + case T1::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T2::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T3::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T4::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T5::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T6::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T7::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T8::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T9::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T10::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T11::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T12::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T13::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T14::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + default: return static_cast(this)->on_receive_unknown(source, msg); break; + } + } + }; + + //*************************************************************************** + // Specialisation for 14 message types, returning void. + //*************************************************************************** + template + class message_router + : public imessage_router + { + public: + + void receive(imessage_router& source, const imessage& msg) + { + const size_t id = msg.get_message_id(); + + switch (id) + { + case T1::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T2::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T3::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T4::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T5::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T6::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T7::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T8::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T9::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T10::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T11::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T12::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T13::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T14::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + default: static_cast(this)->on_receive_unknown(source, msg); break; + } + } + }; + + //*************************************************************************** + // Specialisation for 13 message types, returning TReturn. + //*************************************************************************** + template + class message_router + : public imessage_router + { + public: + + return_type receive(imessage_router& source, const imessage& msg) + { + const size_t id = msg.get_message_id(); + + switch (id) + { + case T1::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T2::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T3::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T4::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T5::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T6::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T7::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T8::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T9::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T10::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T11::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T12::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T13::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + default: return static_cast(this)->on_receive_unknown(source, msg); break; + } + } + }; + + //*************************************************************************** + // Specialisation for 13 message types, returning void. + //*************************************************************************** + template + class message_router + : public imessage_router + { + public: + + void receive(imessage_router& source, const imessage& msg) + { + const size_t id = msg.get_message_id(); + + switch (id) + { + case T1::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T2::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T3::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T4::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T5::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T6::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T7::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T8::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T9::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T10::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T11::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T12::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T13::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + default: static_cast(this)->on_receive_unknown(source, msg); break; + } + } + }; + + //*************************************************************************** + // Specialisation for 12 message types, returning TReturn. + //*************************************************************************** + template + class message_router + : public imessage_router + { + public: + + return_type receive(imessage_router& source, const imessage& msg) + { + const size_t id = msg.get_message_id(); + + switch (id) + { + case T1::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T2::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T3::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T4::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T5::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T6::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T7::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T8::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T9::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T10::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T11::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T12::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + default: return static_cast(this)->on_receive_unknown(source, msg); break; + } + } + }; + + //*************************************************************************** + // Specialisation for 12 message types, returning void. + //*************************************************************************** + template + class message_router + : public imessage_router + { + public: + + void receive(imessage_router& source, const imessage& msg) + { + const size_t id = msg.get_message_id(); + + switch (id) + { + case T1::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T2::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T3::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T4::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T5::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T6::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T7::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T8::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T9::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T10::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T11::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T12::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + default: static_cast(this)->on_receive_unknown(source, msg); break; + } + } + }; + + //*************************************************************************** + // Specialisation for 11 message types, returning TReturn. + //*************************************************************************** + template + class message_router + : public imessage_router + { + public: + + return_type receive(imessage_router& source, const imessage& msg) + { + const size_t id = msg.get_message_id(); + + switch (id) + { + case T1::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T2::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T3::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T4::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T5::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T6::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T7::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T8::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T9::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T10::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T11::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + default: return static_cast(this)->on_receive_unknown(source, msg); break; + } + } + }; + + //*************************************************************************** + // Specialisation for 11 message types, returning void. + //*************************************************************************** + template + class message_router + : public imessage_router + { + public: + + void receive(imessage_router& source, const imessage& msg) + { + const size_t id = msg.get_message_id(); + + switch (id) + { + case T1::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T2::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T3::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T4::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T5::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T6::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T7::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T8::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T9::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T10::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T11::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + default: static_cast(this)->on_receive_unknown(source, msg); break; + } + } + }; + + //*************************************************************************** + // Specialisation for 10 message types, returning TReturn. + //*************************************************************************** + template + class message_router + : public imessage_router + { + public: + + return_type receive(imessage_router& source, const imessage& msg) + { + const size_t id = msg.get_message_id(); + + switch (id) + { + case T1::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T2::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T3::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T4::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T5::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T6::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T7::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T8::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T9::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T10::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + default: return static_cast(this)->on_receive_unknown(source, msg); break; + } + } + }; + + //*************************************************************************** + // Specialisation for 10 message types, returning void. + //*************************************************************************** + template + class message_router + : public imessage_router + { + public: + + void receive(imessage_router& source, const imessage& msg) + { + const size_t id = msg.get_message_id(); + + switch (id) + { + case T1::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T2::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T3::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T4::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T5::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T6::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T7::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T8::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T9::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T10::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + default: static_cast(this)->on_receive_unknown(source, msg); break; + } + } + }; + + //*************************************************************************** + // Specialisation for 9 message types, returning TReturn. + //*************************************************************************** + template + class message_router + : public imessage_router + { + public: + + return_type receive(imessage_router& source, const imessage& msg) + { + const size_t id = msg.get_message_id(); + + switch (id) + { + case T1::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T2::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T3::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T4::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T5::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T6::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T7::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T8::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T9::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + default: return static_cast(this)->on_receive_unknown(source, msg); break; + } + } + }; + + //*************************************************************************** + // Specialisation for 9 message types, returning void. + //*************************************************************************** + template + class message_router + : public imessage_router + { + public: + + void receive(imessage_router& source, const imessage& msg) + { + const size_t id = msg.get_message_id(); + + switch (id) + { + case T1::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T2::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T3::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T4::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T5::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T6::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T7::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T8::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T9::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + default: static_cast(this)->on_receive_unknown(source, msg); break; + } + } + }; + + //*************************************************************************** + // Specialisation for 8 message types, returning TReturn. + //*************************************************************************** + template + class message_router + : public imessage_router + { + public: + + return_type receive(imessage_router& source, const imessage& msg) + { + const size_t id = msg.get_message_id(); + + switch (id) + { + case T1::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T2::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T3::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T4::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T5::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T6::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T7::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T8::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + default: return static_cast(this)->on_receive_unknown(source, msg); break; + } + } + }; + + //*************************************************************************** + // Specialisation for 8 message types, returning void. + //*************************************************************************** + template + class message_router + : public imessage_router + { + public: + + void receive(imessage_router& source, const imessage& msg) + { + const size_t id = msg.get_message_id(); + + switch (id) + { + case T1::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T2::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T3::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T4::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T5::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T6::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T7::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T8::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + default: static_cast(this)->on_receive_unknown(source, msg); break; + } + } + }; + + //*************************************************************************** + // Specialisation for 7 message types, returning TReturn. + //*************************************************************************** + template + class message_router + : public imessage_router + { + public: + + return_type receive(imessage_router& source, const imessage& msg) + { + const size_t id = msg.get_message_id(); + + switch (id) + { + case T1::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T2::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T3::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T4::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T5::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T6::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T7::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + default: return static_cast(this)->on_receive_unknown(source, msg); break; + } + } + }; + + //*************************************************************************** + // Specialisation for 7 message types, returning void. + //*************************************************************************** + template + class message_router + : public imessage_router + { + public: + + void receive(imessage_router& source, const imessage& msg) + { + const size_t id = msg.get_message_id(); + + switch (id) + { + case T1::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T2::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T3::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T4::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T5::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T6::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T7::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + default: static_cast(this)->on_receive_unknown(source, msg); break; + } + } + }; + + //*************************************************************************** + // Specialisation for 6 message types, returning TReturn. + //*************************************************************************** + template + class message_router + : public imessage_router + { + public: + + return_type receive(imessage_router& source, const imessage& msg) + { + const size_t id = msg.get_message_id(); + + switch (id) + { + case T1::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T2::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T3::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T4::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T5::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T6::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + default: return static_cast(this)->on_receive_unknown(source, msg); break; + } + } + }; + + //*************************************************************************** + // Specialisation for 6 message types, returning void. + //*************************************************************************** + template + class message_router + : public imessage_router + { + public: + + void receive(imessage_router& source, const imessage& msg) + { + const size_t id = msg.get_message_id(); + + switch (id) + { + case T1::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T2::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T3::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T4::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T5::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T6::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + default: static_cast(this)->on_receive_unknown(source, msg); break; + } + } + }; + + //*************************************************************************** + // Specialisation for 5 message types, returning TReturn. + //*************************************************************************** + template + class message_router + : public imessage_router + { + public: + + return_type receive(imessage_router& source, const imessage& msg) + { + const size_t id = msg.get_message_id(); + + switch (id) + { + case T1::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T2::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T3::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T4::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T5::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + default: return static_cast(this)->on_receive_unknown(source, msg); break; + } + } + }; + + //*************************************************************************** + // Specialisation for 5 message types, returning void. + //*************************************************************************** + template + class message_router + : public imessage_router + { + public: + + void receive(imessage_router& source, const imessage& msg) + { + const size_t id = msg.get_message_id(); + + switch (id) + { + case T1::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T2::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T3::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T4::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T5::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + default: static_cast(this)->on_receive_unknown(source, msg); break; + } + } + }; + + //*************************************************************************** + // Specialisation for 4 message types, returning TReturn. + //*************************************************************************** + template + class message_router + : public imessage_router + { + public: + + return_type receive(imessage_router& source, const imessage& msg) + { + const size_t id = msg.get_message_id(); + + switch (id) + { + case T1::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T2::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T3::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T4::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + default: return static_cast(this)->on_receive_unknown(source, msg); break; + } + } + }; + + //*************************************************************************** + // Specialisation for 4 message types, returning void. + //*************************************************************************** + template + class message_router + : public imessage_router + { + public: + + void receive(imessage_router& source, const imessage& msg) + { + const size_t id = msg.get_message_id(); + + switch (id) + { + case T1::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T2::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T3::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T4::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + default: static_cast(this)->on_receive_unknown(source, msg); break; + } + } + }; + + //*************************************************************************** + // Specialisation for 3 message types, returning TReturn. + //*************************************************************************** + template + class message_router + : public imessage_router + { + public: + + return_type receive(imessage_router& source, const imessage& msg) + { + const size_t id = msg.get_message_id(); + + switch (id) + { + case T1::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T2::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T3::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + default: return static_cast(this)->on_receive_unknown(source, msg); break; + } + } + }; + + //*************************************************************************** + // Specialisation for 3 message types, returning void. + //*************************************************************************** + template + class message_router + : public imessage_router + { + public: + + void receive(imessage_router& source, const imessage& msg) + { + const size_t id = msg.get_message_id(); + + switch (id) + { + case T1::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T2::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T3::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + default: static_cast(this)->on_receive_unknown(source, msg); break; + } + } + }; + + //*************************************************************************** + // Specialisation for 2 message types, returning TReturn. + //*************************************************************************** + template + class message_router + : public imessage_router + { + public: + + return_type receive(imessage_router& source, const imessage& msg) + { + const size_t id = msg.get_message_id(); + + switch (id) + { + case T1::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + case T2::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + default: return static_cast(this)->on_receive_unknown(source, msg); break; + } + } + }; + + //*************************************************************************** + // Specialisation for 2 message types, returning void. + //*************************************************************************** + template + class message_router + : public imessage_router + { + public: + + void receive(imessage_router& source, const imessage& msg) + { + const size_t id = msg.get_message_id(); + + switch (id) + { + case T1::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + case T2::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + default: static_cast(this)->on_receive_unknown(source, msg); break; + } + } + }; + + //*************************************************************************** + // Specialisation for 1 message type, returning TReturn. + //*************************************************************************** + template + class message_router + : public imessage_router + { + public: + + return_type receive(imessage_router& source, const imessage& msg) + { + const size_t id = msg.get_message_id(); + + switch (id) + { + case T1::ID: return static_cast(this)->on_receive(source, static_cast(msg)); break; + default: return static_cast(this)->on_receive_unknown(source, msg); break; + } + } + }; + + //*************************************************************************** + // Specialisation for 1 message type, returning void. + //*************************************************************************** + template + class message_router + : public imessage_router + { + public: + + void receive(imessage_router& source, const imessage& msg) + { + const size_t id = msg.get_message_id(); + + switch (id) + { + case T1::ID: static_cast(this)->on_receive(source, static_cast(msg)); break; + default: static_cast(this)->on_receive_unknown(source, msg); break; + } + } + }; +} + +#endif diff --git a/src/message_router_generator.h b/src/message_router_generator.h new file mode 100644 index 00000000..57879607 --- /dev/null +++ b/src/message_router_generator.h @@ -0,0 +1,347 @@ +/****************************************************************************** +The MIT License(MIT) + +Embedded Template Library. +https://github.com/ETLCPP/etl +http://www.etlcpp.com + +Copyright(c) 2017 jwellbelove + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files(the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions : + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +******************************************************************************/ + +#ifndef __ETL_MESSAGE_ROUTER__ +#define __ETL_MESSAGE_ROUTER__ + +#include + +namespace etl +{ + //*************************************************************************** + class imessage + { + public: + + typedef size_t id_t; + + virtual ~imessage() {} + virtual id_t get_message_id() const = 0; + }; + + //*************************************************************************** + template + class message : public imessage + { + public: + + enum + { + ID = ID_ + }; + + id_t get_message_id() const + { + return ID; + } + }; + + //*************************************************************************** + template + class imessage_router + { + public: + + typedef TReturn return_type; + + virtual ~imessage_router() {} + virtual TReturn receive(imessage_router& source, const imessage& message) = 0; + + TReturn send_to(imessage_router& destination, const imessage& message) + { + return destination.receive(*this, message); + } + }; + + //*************************************************************************** + template <> + class imessage_router + { + public: + + typedef void return_type; + + virtual ~imessage_router() {} + virtual void receive(imessage_router& source, const imessage& message) = 0; + + void send_to(imessage_router& destination, const imessage& message) + { + destination.receive(*this, message); + } + }; + + //*************************************************************************** + template + class null_message_router : public imessage_router + { + public: + + null_message_router() + : value(TReturn()) + { + } + + null_message_router(const TReturn& value) + : value(value) + { + } + + null_message_router(const null_message_router& other) + : value(other.value) + { + } + + null_message_router& operator ==(const null_message_router& other) + : value(other.value) + { + } + + TReturn receive(imessage_router& source, const imessage& message) + { + return value; + } + + private: + + TReturn value; + }; + + //*************************************************************************** + template <> + class null_message_router : public imessage_router + { + public: + + void receive(imessage_router& source, const imessage& message) + { + } + }; + + //*************************************************************************** + // To generate to header file, run this at the command line. + // Note: You will need Python and COG installed. + // + // python -m cogapp -d -e -omessage_router.h -DHandlers= message_router_generator.h + // Where is the number of messages to support. + // + // e.g. + // To generate handlers for up to 16 messages... + // python -m cogapp -d -e -omessage_router.h -DHandlers=16 message_router_generator.h + // + // See CreateMessageProcessor.bat + //*************************************************************************** + + /*[[[cog + import cog + cog.outl("//***************************************************************************") + cog.outl("// The code below has been auto generated. Do not manually edit.") + cog.outl("//***************************************************************************") + cog.outl("") + ################################################ + # The first definition for all of the messages. + ################################################ + cog.outl("//***************************************************************************") + cog.outl("// The definition for all %s message types, returning TReturn." % Handlers) + cog.outl("//***************************************************************************") + cog.outl("template " % Handlers) + cog.outl("class message_router") + cog.outl(" : public imessage_router") + cog.outl("{") + cog.outl("public:") + cog.outl("") + cog.outl(" TReturn receive(imessage_router& source, const imessage& msg)") + cog.outl(" {") + cog.outl(" const id_t id = msg.get_message_id();") + cog.outl("") + cog.outl(" switch (id)") + cog.outl(" {") + for n in range(1, int(Handlers) + 1): + cog.out(" case T%d::ID:" % n) + cog.out(" return static_cast(this)->on_receive(source, static_cast(msg));" % n) + cog.outl(" break;") + cog.out(" default:") + cog.out(" return static_cast(this)->on_receive_unknown(source, msg);") + cog.outl(" break;") + cog.outl(" }") + cog.outl(" }") + cog.outl("};") + cog.outl("") + + ################################################ + # The first definition for all of the messages, with void return type. + ################################################ + cog.outl("//***************************************************************************") + cog.outl("// The definition for all %s message types, returning void." % Handlers) + cog.outl("//***************************************************************************") + cog.outl("template " % Handlers) + cog.out("class message_router" % Handlers) + cog.outl(" : public imessage_router") + cog.outl("{") + cog.outl("public:") + cog.outl("") + cog.outl(" void receive(imessage_router& source, const imessage& msg)") + cog.outl(" {") + cog.outl(" const id_t id = msg.get_message_id();") + cog.outl("") + cog.outl(" switch (id)") + cog.outl(" {") + for n in range(1, int(Handlers) + 1): + cog.out(" case T%d::ID:" % n) + cog.out(" static_cast(this)->on_receive(source, static_cast(msg));" % n) + cog.outl(" break;") + cog.out(" default:") + cog.out(" static_cast(this)->on_receive_unknown(source, msg);") + cog.outl(" break;") + cog.outl(" }") + cog.outl(" }") + cog.outl("};") + + #################################### + # All of the other specialisations. + #################################### + for n in range(int(Handlers) - 1, 0, -1): + cog.outl("") + cog.outl("//***************************************************************************") + if n == 1: + cog.outl("// Specialisation for %d message type, returning TReturn." % n) + else: + cog.outl("// Specialisation for %d message types, returning TReturn." % n) + cog.outl("//***************************************************************************") + cog.outl("template " % n) + cog.out("class message_router") + cog.outl(" : public imessage_router") + cog.outl("{") + cog.outl("public:") + cog.outl("") + cog.outl(" return_type receive(imessage_router& source, const imessage& msg)") + cog.outl(" {") + cog.outl(" const size_t id = msg.get_message_id();") + cog.outl("") + cog.outl(" switch (id)") + cog.outl(" {") + for t in range(1, n + 1): + cog.out(" case T%d::ID:" % t) + cog.out(" return static_cast(this)->on_receive(source, static_cast(msg));" % t) + cog.outl(" break;") + cog.out(" default:") + cog.out(" return static_cast(this)->on_receive_unknown(source, msg);") + cog.outl(" break;") + cog.outl(" }") + cog.outl(" }") + cog.outl("};") + + cog.outl("") + cog.outl("//***************************************************************************") + if n == 1: + cog.outl("// Specialisation for %d message type, returning void." % n) + else: + cog.outl("// Specialisation for %d message types, returning void." % n) + cog.outl("//***************************************************************************") + cog.outl("template " % n) + cog.out("class message_router") + cog.outl(" : public imessage_router") + cog.outl("{") + cog.outl("public:") + cog.outl("") + cog.outl(" void receive(imessage_router& source, const imessage& msg)") + cog.outl(" {") + cog.outl(" const size_t id = msg.get_message_id();") + cog.outl("") + cog.outl(" switch (id)") + cog.outl(" {") + for t in range(1, n + 1): + cog.out(" case T%d::ID:" % t) + cog.out(" static_cast(this)->on_receive(source, static_cast(msg));" % t) + cog.outl(" break;") + cog.out(" default:") + cog.out(" static_cast(this)->on_receive_unknown(source, msg);") + cog.outl(" break;") + cog.outl(" }") + cog.outl(" }") + cog.outl("};") + ]]]*/ + /*[[[end]]]*/ +} + +#endif diff --git a/test/test_fsm.cpp b/test/test_fsm.cpp new file mode 100644 index 00000000..edcd1678 --- /dev/null +++ b/test/test_fsm.cpp @@ -0,0 +1,497 @@ +/****************************************************************************** +The MIT License(MIT) + +Embedded Template Library. +https://github.com/ETLCPP/etl +https://www.etlcpp.com + +Copyright(c) 2017 jwellbelove + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files(the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions : + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +******************************************************************************/ + +#include "UnitTest++.h" + +#include "fsm.h" +#include "enum_type.h" +#include "container.h" + +#include + +namespace +{ + //*************************************************************************** + // Events + struct EventId + { + enum enum_type + { + START, + STOP, + STOPPED, + SET_SPEED + }; + + ETL_DECLARE_ENUM_TYPE(EventId, etl::fsm_event_id_t) + ETL_ENUM_TYPE(START, "Start") + ETL_ENUM_TYPE(STOP, "Stop") + ETL_ENUM_TYPE(STOPPED, "Stopped") + ETL_ENUM_TYPE(SET_SPEED, "Set Speed") + ETL_END_ENUM_TYPE + }; + + //*********************************** + class Start : public etl::fsm_event + { + }; + + //*********************************** + class Stop : public etl::fsm_event + { + public: + + Stop() : isEmergencyStop(false) {} + Stop(bool emergency) : isEmergencyStop(emergency) {} + + const bool isEmergencyStop; + }; + + //*********************************** + class SetSpeed : public etl::fsm_event + { + public: + + SetSpeed(int speed) : speed(speed) {} + + const int speed; + }; + + //*********************************** + class Stopped : public etl::fsm_event + { + }; + + //*************************************************************************** + // States + struct StateId + { + enum enum_type + { + IDLE, + RUNNING, + WINDING_DOWN, + NUMBER_OF_STATES + }; + + ETL_DECLARE_ENUM_TYPE(StateId, etl::fsm_state_id_t) + ETL_ENUM_TYPE(IDLE, "Idle") + ETL_ENUM_TYPE(RUNNING, "Running") + ETL_ENUM_TYPE(WINDING_DOWN, "Winding Down") + ETL_END_ENUM_TYPE + }; + + class MotorControl; + + //*********************************** + // Common functionality + //*********************************** + class Common + { + public: + + //*********************************** + Common() + { + ClearStatistics(); + } + + //*********************************** + void ClearStatistics() + { + startCount = 0; + stopCount = 0; + setSpeedCount = 0; + unknownCount = 0; + stoppedCount = 0; + isLampOn = false; + speed = 0; + } + + //*********************************** + void SetSpeed(int speed_) + { + speed = speed_; + } + + //*********************************** + void TurnRunningLampOn() + { + isLampOn = true; + } + + //*********************************** + void TurnRunningLampOff() + { + isLampOn = false; + } + + int startCount; + int stopCount; + int setSpeedCount; + int unknownCount; + int stoppedCount; + bool isLampOn; + int speed; + }; + + //*********************************** + // The idle state. + //*********************************** + class Idle : public etl::fsm_state + { + public: + + //*********************************** + Idle(Common& common) + : common(common) + { + } + + //*********************************** + etl::fsm_state_id_t on_event(const Start& event) + { + ++common.startCount; + return StateId::RUNNING; + } + + //*********************************** + etl::fsm_state_id_t on_event_unknown(const etl::ifsm_event& event) + { + ++common.unknownCount; + return STATE_ID; + } + + //*********************************** + void on_enter_state() + { + common.TurnRunningLampOff(); + } + + Common& common; + }; + + //*********************************** + // The running state. + //*********************************** + class Running : public etl::fsm_state + { + public: + + //*********************************** + Running(Common& common) + : common(common) + { + } + + //*********************************** + etl::fsm_state_id_t on_event(const Stop& event) + { + ++common.stopCount; + + if (event.isEmergencyStop) + { + return StateId::IDLE; + } + else + { + return StateId::WINDING_DOWN; + } + } + + //*********************************** + etl::fsm_state_id_t on_event(const SetSpeed& event) + { + ++common.setSpeedCount; + common.SetSpeed(event.speed); + return STATE_ID; + } + + //*********************************** + etl::fsm_state_id_t on_event_unknown(const etl::ifsm_event& event) + { + ++common.unknownCount; + return STATE_ID; + } + + void on_enter_state() + { + common.TurnRunningLampOn(); + } + + Common& common; + }; + + //*********************************** + // The winding down state. + //*********************************** + class WindingDown : public etl::fsm_state + { + public: + + //*********************************** + WindingDown(Common& common) + : common(common) + { + } + + //*********************************** + etl::fsm_state_id_t on_event(const Stopped& event) + { + ++common.stoppedCount; + return StateId::IDLE; + } + + //*********************************** + etl::fsm_state_id_t on_event_unknown(const etl::ifsm_event& event) + { + ++common.unknownCount; + return STATE_ID; + } + + Common& common; + }; + + //*********************************** + // The motor control FSM. + //*********************************** + class MotorControl : public etl::fsm + { + public: + + MotorControl() + : idle(common), + running(common), + windingDown(common) + { + set_states(stateList, etl::size(stateList)); + } + + Common common; + + private: + + Idle idle; + Running running; + WindingDown windingDown; + + etl::ifsm_state* stateList[StateId::NUMBER_OF_STATES] = + { + &idle, &running, &windingDown + }; + }; + + MotorControl motorControl; + + SUITE(test_map) + { + //************************************************************************* + TEST(test_fsm) + { + motorControl.reset(); + motorControl.common.ClearStatistics(); + + CHECK(!motorControl.is_started()); + + // Start the FSM. + motorControl.start(); + CHECK(motorControl.is_started()); + + // Now in Idle state. + + CHECK_EQUAL(StateId::IDLE, int(motorControl.get_state_id())); + CHECK_EQUAL(StateId::IDLE, int(motorControl.get_state().get_state_id())); + + CHECK_EQUAL(false, motorControl.common.isLampOn); + CHECK_EQUAL(0, motorControl.common.setSpeedCount); + CHECK_EQUAL(0, motorControl.common.speed); + CHECK_EQUAL(0, motorControl.common.startCount); + CHECK_EQUAL(0, motorControl.common.stopCount); + CHECK_EQUAL(0, motorControl.common.stoppedCount); + CHECK_EQUAL(0, motorControl.common.unknownCount); + + // Send unhandled events. + motorControl.on_event(Stop()); + motorControl.on_event(Stopped()); + motorControl.on_event(SetSpeed(10)); + + CHECK_EQUAL(StateId::IDLE, motorControl.get_state_id()); + CHECK_EQUAL(StateId::IDLE, motorControl.get_state().get_state_id()); + + CHECK_EQUAL(false, motorControl.common.isLampOn); + CHECK_EQUAL(0, motorControl.common.setSpeedCount); + CHECK_EQUAL(0, motorControl.common.speed); + CHECK_EQUAL(0, motorControl.common.startCount); + CHECK_EQUAL(0, motorControl.common.stopCount); + CHECK_EQUAL(0, motorControl.common.stoppedCount); + CHECK_EQUAL(3, motorControl.common.unknownCount); + + // Send Start event. + motorControl.on_event(Start()); + + // Now in Running state. + + CHECK_EQUAL(StateId::RUNNING, int(motorControl.get_state_id())); + CHECK_EQUAL(StateId::RUNNING, int(motorControl.get_state().get_state_id())); + + CHECK_EQUAL(true, motorControl.common.isLampOn); + CHECK_EQUAL(0, motorControl.common.setSpeedCount); + CHECK_EQUAL(0, motorControl.common.speed); + CHECK_EQUAL(1, motorControl.common.startCount); + CHECK_EQUAL(0, motorControl.common.stopCount); + CHECK_EQUAL(0, motorControl.common.stoppedCount); + CHECK_EQUAL(3, motorControl.common.unknownCount); + + // Send unhandled events. + motorControl.on_event(Start()); + motorControl.on_event(Stopped()); + + CHECK_EQUAL(StateId::RUNNING, int(motorControl.get_state_id())); + CHECK_EQUAL(StateId::RUNNING, int(motorControl.get_state().get_state_id())); + + CHECK_EQUAL(true, motorControl.common.isLampOn); + CHECK_EQUAL(0, motorControl.common.setSpeedCount); + CHECK_EQUAL(0, motorControl.common.speed); + CHECK_EQUAL(1, motorControl.common.startCount); + CHECK_EQUAL(0, motorControl.common.stopCount); + CHECK_EQUAL(0, motorControl.common.stoppedCount); + CHECK_EQUAL(5, motorControl.common.unknownCount); + + // Send SetSpeed event. + motorControl.on_event(SetSpeed(100)); + + // Still in Running state. + + CHECK_EQUAL(StateId::RUNNING, int(motorControl.get_state_id())); + CHECK_EQUAL(StateId::RUNNING, int(motorControl.get_state().get_state_id())); + + CHECK_EQUAL(true, motorControl.common.isLampOn); + CHECK_EQUAL(1, motorControl.common.setSpeedCount); + CHECK_EQUAL(100, motorControl.common.speed); + CHECK_EQUAL(1, motorControl.common.startCount); + CHECK_EQUAL(0, motorControl.common.stopCount); + CHECK_EQUAL(0, motorControl.common.stoppedCount); + CHECK_EQUAL(5, motorControl.common.unknownCount); + + // Send Stop event. + motorControl.on_event(Stop()); + + // Now in WindingDown state. + + CHECK_EQUAL(StateId::WINDING_DOWN, int(motorControl.get_state_id())); + CHECK_EQUAL(StateId::WINDING_DOWN, int(motorControl.get_state().get_state_id())); + + CHECK_EQUAL(true, motorControl.common.isLampOn); + CHECK_EQUAL(1, motorControl.common.setSpeedCount); + CHECK_EQUAL(100, motorControl.common.speed); + CHECK_EQUAL(1, motorControl.common.startCount); + CHECK_EQUAL(1, motorControl.common.stopCount); + CHECK_EQUAL(0, motorControl.common.stoppedCount); + CHECK_EQUAL(5, motorControl.common.unknownCount); + + // Send unhandled events. + motorControl.on_event(Start()); + motorControl.on_event(Stop()); + motorControl.on_event(SetSpeed(100)); + + CHECK_EQUAL(StateId::WINDING_DOWN, int(motorControl.get_state_id())); + CHECK_EQUAL(StateId::WINDING_DOWN, int(motorControl.get_state().get_state_id())); + + CHECK_EQUAL(true, motorControl.common.isLampOn); + CHECK_EQUAL(1, motorControl.common.setSpeedCount); + CHECK_EQUAL(100, motorControl.common.speed); + CHECK_EQUAL(1, motorControl.common.startCount); + CHECK_EQUAL(1, motorControl.common.stopCount); + CHECK_EQUAL(0, motorControl.common.stoppedCount); + CHECK_EQUAL(8, motorControl.common.unknownCount); + + // Send Stopped event. + motorControl.on_event(Stopped()); + + // Now in Idle state. + + CHECK_EQUAL(StateId::IDLE, int(motorControl.get_state_id())); + CHECK_EQUAL(StateId::IDLE, int(motorControl.get_state().get_state_id())); + + CHECK_EQUAL(false, motorControl.common.isLampOn); + CHECK_EQUAL(1, motorControl.common.setSpeedCount); + CHECK_EQUAL(100, motorControl.common.speed); + CHECK_EQUAL(1, motorControl.common.startCount); + CHECK_EQUAL(1, motorControl.common.stopCount); + CHECK_EQUAL(1, motorControl.common.stoppedCount); + CHECK_EQUAL(8, motorControl.common.unknownCount); + } + + //************************************************************************* + TEST(test_fsm_emergency_stop) + { + motorControl.reset(); + motorControl.common.ClearStatistics(); + + CHECK(!motorControl.is_started()); + + // Start the FSM. + motorControl.start(); + CHECK(motorControl.is_started()); + + // Now in Idle state. + + // Send Start event. + motorControl.on_event(Start()); + + // Now in Running state. + + CHECK_EQUAL(StateId::RUNNING, int(motorControl.get_state_id())); + CHECK_EQUAL(StateId::RUNNING, int(motorControl.get_state().get_state_id())); + + CHECK_EQUAL(true, motorControl.common.isLampOn); + CHECK_EQUAL(0, motorControl.common.setSpeedCount); + CHECK_EQUAL(0, motorControl.common.speed); + CHECK_EQUAL(1, motorControl.common.startCount); + CHECK_EQUAL(0, motorControl.common.stopCount); + CHECK_EQUAL(0, motorControl.common.stoppedCount); + CHECK_EQUAL(0, motorControl.common.unknownCount); + + // Send emergency Stop event. + motorControl.on_event(Stop(true)); + + // Now in Idle state. + + CHECK_EQUAL(StateId::IDLE, int(motorControl.get_state_id())); + CHECK_EQUAL(StateId::IDLE, int(motorControl.get_state().get_state_id())); + + CHECK_EQUAL(false, motorControl.common.isLampOn); + CHECK_EQUAL(0, motorControl.common.setSpeedCount); + CHECK_EQUAL(0, motorControl.common.speed); + CHECK_EQUAL(1, motorControl.common.startCount); + CHECK_EQUAL(1, motorControl.common.stopCount); + CHECK_EQUAL(0, motorControl.common.stoppedCount); + CHECK_EQUAL(0, motorControl.common.unknownCount); + } + }; +} diff --git a/test/test_message_router.cpp b/test/test_message_router.cpp new file mode 100644 index 00000000..b8620502 --- /dev/null +++ b/test/test_message_router.cpp @@ -0,0 +1,528 @@ +/****************************************************************************** +The MIT License(MIT) + +Embedded Template Library. +https://github.com/ETLCPP/etl +http://www.etlcpp.com + +Copyright(c) 2017 jwellbelove + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files(the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions : + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +******************************************************************************/ + +#include "UnitTest++.h" +#include "ExtraCheckMacros.h" + +#include "message_router.h" + +//*************************************************************************** +// The set of messages. +//*************************************************************************** +namespace +{ + enum + { + MESSAGE1, + MESSAGE2, + MESSAGE3, + MESSAGE4, + MESSAGE5 + }; + + struct Message1 : public etl::message + { + + }; + + struct Message2 : public etl::message + { + + }; + + struct Message3 : public etl::message + { + + }; + + struct Message4 : public etl::message + { + + }; + + struct Message5 : public etl::message + { + + }; + + Message1 message1; + Message2 message2; + Message3 message3; + Message4 message4; + Message5 message5; + + // Router typedefs. + typedef etl::imessage_router int_router_t; + typedef etl::imessage_router void_router_t; + + //*************************************************************************** + // Router that handles messages 1, 2, 3, 4 and 5 and returns nothing. + //*************************************************************************** + class RouterVoid1 : public etl::message_router + { + public: + + RouterVoid1() + : message1_count(0), + message2_count(0), + message3_count(0), + message4_count(0), + message_unknown_count(0), + callback_count(0) + { + + } + + void on_receive(void_router_t& sender, const Message1& msg) + { + ++message1_count; + send_to(sender, message5); + } + + void on_receive(void_router_t& sender, const Message2& msg) + { + ++message2_count; + send_to(sender, message5); + } + + void on_receive(void_router_t& sender, const Message3& msg) + { + ++message3_count; + send_to(sender, message5); + } + + void on_receive(void_router_t& sender, const Message4& msg) + { + ++message4_count; + send_to(sender, message5); + } + + void on_receive(void_router_t& sender, const Message5& msg) + { + ++callback_count; + } + + void on_receive_unknown(void_router_t& sender, const etl::imessage& msg) + { + ++message_unknown_count; + } + + int message1_count; + int message2_count; + int message3_count; + int message4_count; + int message_unknown_count; + int callback_count; + }; + + //*************************************************************************** + // Router that handles messages 1, 2, 4 and 5 and returns nothing. + //*************************************************************************** + class RouterVoid2 : public etl::message_router + { + public: + + RouterVoid2() + : message1_count(0), + message2_count(0), + message4_count(0), + message_unknown_count(0), + callback_count(0) + { + + } + + void on_receive(void_router_t& sender, const Message1& msg) + { + ++message1_count; + send_to(sender, message5); + } + + void on_receive(void_router_t& sender, const Message2& msg) + { + ++message2_count; + send_to(sender, message5); + } + + void on_receive(void_router_t& sender, const Message4& msg) + { + ++message4_count; + send_to(sender, message5); + } + + void on_receive(void_router_t& sender, const Message5& msg) + { + ++callback_count; + } + + void on_receive_unknown(void_router_t& sender, const etl::imessage& msg) + { + ++message_unknown_count; + send_to(sender, message5); + } + + int message1_count; + int message2_count; + int message4_count; + int message_unknown_count; + int callback_count; + }; + + //*************************************************************************** + // Router that handles messages 1, 2, 3, 4 and 5 and returns an int. + //*************************************************************************** + class RouterInt1 : public etl::message_router + { + public: + + RouterInt1() + : message1_count(0), + message2_count(0), + message3_count(0), + message4_count(0), + message_unknown_count(0), + callback_count(0) + { + + } + + int on_receive(int_router_t& sender, const Message1& msg) + { + ++message1_count; + send_to(sender, message5); + return msg.ID; + } + + int on_receive(int_router_t& sender, const Message2& msg) + { + ++message2_count; + send_to(sender, message5); + return msg.ID; + } + + int on_receive(int_router_t& sender, const Message3& msg) + { + ++message3_count; + send_to(sender, message5); + return msg.ID; + } + + int on_receive(int_router_t& sender, const Message4& msg) + { + ++message4_count; + send_to(sender, message5); + return msg.ID; + } + + int on_receive(int_router_t& sender, const Message5& msg) + { + ++callback_count; + return msg.ID; + } + + int on_receive_unknown(int_router_t& sender, const etl::imessage& msg) + { + ++message_unknown_count; + return msg.get_message_id(); + } + + int message1_count; + int message2_count; + int message3_count; + int message4_count; + int message_unknown_count; + int callback_count; + }; + + //*************************************************************************** + // Router that handles messages 1, 2, 4 and 5 and returns an int. + //*************************************************************************** + class RouterInt2 : public etl::message_router + { + public: + + RouterInt2() + : message1_count(0), + message2_count(0), + message4_count(0), + message_unknown_count(0), + callback_count(0) + { + + } + + int on_receive(int_router_t& sender, const Message1& msg) + { + ++message1_count; + send_to(sender, message5); + return msg.ID; + } + + int on_receive(int_router_t& sender, const Message2& msg) + { + ++message2_count; + send_to(sender, message5); + return msg.ID; + } + + int on_receive(int_router_t& sender, const Message4& msg) + { + ++message4_count; + send_to(sender, message5); + return msg.ID; + } + + int on_receive(int_router_t& sender, const Message5& msg) + { + ++callback_count; + return msg.ID; + } + + int on_receive_unknown(int_router_t& sender, const etl::imessage& msg) + { + ++message_unknown_count; + send_to(sender, message5); + return msg.get_message_id(); + } + + int message1_count; + int message2_count; + int message4_count; + int message_unknown_count; + int callback_count; + }; + + // Null router types. + typedef etl::null_message_router int_null_router_t; + typedef etl::null_message_router void_null_router_t; + + void_router_t* p_void_router; + int_router_t* p_int_router; + + SUITE(test_message_router) + { + //========================================================================= + TEST(message_router_void) + { + RouterVoid1 r1; + RouterVoid2 r2; + + p_void_router = &r1; + + p_void_router->receive(r2, message1); + CHECK_EQUAL(1, r1.message1_count); + CHECK_EQUAL(0, r1.message2_count); + CHECK_EQUAL(0, r1.message3_count); + CHECK_EQUAL(0, r1.message4_count); + CHECK_EQUAL(0, r1.message_unknown_count); + CHECK_EQUAL(1, r2.callback_count); + + p_void_router->receive(r2, message2); + CHECK_EQUAL(1, r1.message1_count); + CHECK_EQUAL(1, r1.message2_count); + CHECK_EQUAL(0, r1.message3_count); + CHECK_EQUAL(0, r1.message4_count); + CHECK_EQUAL(0, r1.message_unknown_count); + CHECK_EQUAL(2, r2.callback_count); + + p_void_router->receive(r2, message3); + CHECK_EQUAL(1, r1.message1_count); + CHECK_EQUAL(1, r1.message2_count); + CHECK_EQUAL(1, r1.message3_count); + CHECK_EQUAL(0, r1.message4_count); + CHECK_EQUAL(0, r1.message_unknown_count); + CHECK_EQUAL(3, r2.callback_count); + + p_void_router->receive(r2, message4); + CHECK_EQUAL(1, r1.message1_count); + CHECK_EQUAL(1, r1.message2_count); + CHECK_EQUAL(1, r1.message3_count); + CHECK_EQUAL(1, r1.message4_count); + CHECK_EQUAL(0, r1.message_unknown_count); + CHECK_EQUAL(4, r2.callback_count); + + p_void_router = &r2; + + p_void_router->receive(r1, message1); + CHECK_EQUAL(1, r2.message1_count); + CHECK_EQUAL(0, r2.message2_count); + CHECK_EQUAL(0, r2.message4_count); + CHECK_EQUAL(0, r2.message_unknown_count); + CHECK_EQUAL(1, r1.callback_count); + + p_void_router->receive(r1, message2); + CHECK_EQUAL(1, r2.message1_count); + CHECK_EQUAL(1, r2.message2_count); + CHECK_EQUAL(0, r2.message4_count); + CHECK_EQUAL(0, r2.message_unknown_count); + CHECK_EQUAL(2, r1.callback_count); + + p_void_router->receive(r1, message3); + CHECK_EQUAL(1, r2.message1_count); + CHECK_EQUAL(1, r2.message2_count); + CHECK_EQUAL(0, r2.message4_count); + CHECK_EQUAL(1, r2.message_unknown_count); + CHECK_EQUAL(3, r1.callback_count); + + p_void_router->receive(r1, message4); + CHECK_EQUAL(1, r2.message1_count); + CHECK_EQUAL(1, r2.message2_count); + CHECK_EQUAL(1, r2.message4_count); + CHECK_EQUAL(1, r2.message_unknown_count); + CHECK_EQUAL(4, r1.callback_count); + } + + //========================================================================= + TEST(message_router_int) + { + RouterInt1 r1; + RouterInt2 r2; + + p_int_router = &r1; + + CHECK_EQUAL(int(message1.ID), (p_int_router->receive(r2, message1))); + CHECK_EQUAL(1, r1.message1_count); + CHECK_EQUAL(0, r1.message2_count); + CHECK_EQUAL(0, r1.message3_count); + CHECK_EQUAL(0, r1.message4_count); + CHECK_EQUAL(0, r1.message_unknown_count); + CHECK_EQUAL(1, r2.callback_count); + + CHECK_EQUAL(int(message2.ID), (p_int_router->receive(r2, message2))); + CHECK_EQUAL(1, r1.message1_count); + CHECK_EQUAL(1, r1.message2_count); + CHECK_EQUAL(0, r1.message3_count); + CHECK_EQUAL(0, r1.message4_count); + CHECK_EQUAL(0, r1.message_unknown_count); + CHECK_EQUAL(2, r2.callback_count); + + CHECK_EQUAL(int(message3.ID), (p_int_router->receive(r2, message3))); + CHECK_EQUAL(1, r1.message1_count); + CHECK_EQUAL(1, r1.message2_count); + CHECK_EQUAL(1, r1.message3_count); + CHECK_EQUAL(0, r1.message4_count); + CHECK_EQUAL(0, r1.message_unknown_count); + CHECK_EQUAL(3, r2.callback_count); + + CHECK_EQUAL(int(message4.ID), (p_int_router->receive(r2, message4))); + CHECK_EQUAL(1, r1.message1_count); + CHECK_EQUAL(1, r1.message2_count); + CHECK_EQUAL(1, r1.message3_count); + CHECK_EQUAL(1, r1.message4_count); + CHECK_EQUAL(0, r1.message_unknown_count); + CHECK_EQUAL(4, r2.callback_count); + + p_int_router = &r2; + + CHECK_EQUAL(int(message1.ID), (p_int_router->receive(r1, message1))); + CHECK_EQUAL(1, r2.message1_count); + CHECK_EQUAL(0, r2.message2_count); + CHECK_EQUAL(0, r2.message4_count); + CHECK_EQUAL(0, r2.message_unknown_count); + CHECK_EQUAL(1, r1.callback_count); + + CHECK_EQUAL(int(message2.ID), (p_int_router->receive(r1, message2))); + CHECK_EQUAL(1, r2.message1_count); + CHECK_EQUAL(1, r2.message2_count); + CHECK_EQUAL(0, r2.message4_count); + CHECK_EQUAL(0, r2.message_unknown_count); + CHECK_EQUAL(2, r1.callback_count); + + CHECK_EQUAL(int(message3.ID), (p_int_router->receive(r1, message3))); + CHECK_EQUAL(1, r2.message1_count); + CHECK_EQUAL(1, r2.message2_count); + CHECK_EQUAL(0, r2.message4_count); + CHECK_EQUAL(1, r2.message_unknown_count); + CHECK_EQUAL(3, r1.callback_count); + + CHECK_EQUAL(int(message4.ID), (p_int_router->receive(r1, message4))); + CHECK_EQUAL(1, r2.message1_count); + CHECK_EQUAL(1, r2.message2_count); + CHECK_EQUAL(1, r2.message4_count); + CHECK_EQUAL(1, r2.message_unknown_count); + CHECK_EQUAL(4, r1.callback_count); + } + + //========================================================================= + TEST(message_null_router_void) + { + RouterVoid2 router; + void_null_router_t null_router; + + // Send from the null router. + null_router.send_to(router, message1); + CHECK_EQUAL(1, router.message1_count); + CHECK_EQUAL(0, router.message2_count); + CHECK_EQUAL(0, router.message4_count); + CHECK_EQUAL(0, router.message_unknown_count); + + null_router.send_to(router, message2); + CHECK_EQUAL(1, router.message1_count); + CHECK_EQUAL(1, router.message2_count); + CHECK_EQUAL(0, router.message4_count); + CHECK_EQUAL(0, router.message_unknown_count); + + null_router.send_to(router, message3); + CHECK_EQUAL(1, router.message1_count); + CHECK_EQUAL(1, router.message2_count); + CHECK_EQUAL(0, router.message4_count); + CHECK_EQUAL(1, router.message_unknown_count); + + null_router.send_to(router, message4); + CHECK_EQUAL(1, router.message1_count); + CHECK_EQUAL(1, router.message2_count); + CHECK_EQUAL(1, router.message4_count); + CHECK_EQUAL(1, router.message_unknown_count); + + // Send to the null router. + router.send_to(null_router, message1); + CHECK_EQUAL(1, router.message1_count); + CHECK_EQUAL(1, router.message2_count); + CHECK_EQUAL(1, router.message4_count); + CHECK_EQUAL(1, router.message_unknown_count); + + router.send_to(null_router, message2); + CHECK_EQUAL(1, router.message1_count); + CHECK_EQUAL(1, router.message2_count); + CHECK_EQUAL(1, router.message4_count); + CHECK_EQUAL(1, router.message_unknown_count); + + router.send_to(null_router, message3); + CHECK_EQUAL(1, router.message1_count); + CHECK_EQUAL(1, router.message2_count); + CHECK_EQUAL(1, router.message4_count); + CHECK_EQUAL(1, router.message_unknown_count); + + router.send_to(null_router, message4); + CHECK_EQUAL(1, router.message1_count); + CHECK_EQUAL(1, router.message2_count); + CHECK_EQUAL(1, router.message4_count); + CHECK_EQUAL(1, router.message_unknown_count); + } + }; +}