diff --git a/include/etl/fsm.h b/include/etl/fsm.h index d350467d..3a837f39 100644 --- a/include/etl/fsm.h +++ b/include/etl/fsm.h @@ -238,21 +238,30 @@ namespace etl /// Starts the FSM. /// Can only be called once. /// Subsequent calls will do nothing. - ///\param call_on_enter_state If true will call on_enter_state() for the first state. Default = true. + ///\param call_on_enter_state If will call on_enter_state() for the first state. Default = true. //******************************************* void start(bool call_on_enter_state = true) { - // 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)); + // 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)); - if (call_on_enter_state) - { - p_state->on_enter_state(); - } - } + if (call_on_enter_state) + { + etl::fsm_state_id_t next_state_id; + etl::ifsm_state* p_last_state; + + do + { + p_last_state = p_state; + next_state_id = p_state->on_enter_state(); + p_state = state_list[next_state_id]; + + } while (p_last_state != p_state); + } + } } //******************************************* @@ -260,7 +269,7 @@ namespace etl //******************************************* void receive(const etl::imessage& message) { - etl::null_message_router nmr; + static etl::null_message_router nmr; receive(nmr, message); } @@ -269,26 +278,26 @@ namespace etl //******************************************* void receive(etl::imessage_router& source, const etl::imessage& message) { - etl::fsm_state_id_t next_state_id = p_state->process_event(source, message); - ETL_ASSERT(next_state_id < number_of_states, ETL_ERROR(etl::fsm_state_id_exception)); + etl::fsm_state_id_t next_state_id = p_state->process_event(source, message); + ETL_ASSERT(next_state_id < number_of_states, ETL_ERROR(etl::fsm_state_id_exception)); - etl::ifsm_state* p_next_state = state_list[next_state_id]; + etl::ifsm_state* p_next_state = state_list[next_state_id]; - // Have we changed state? - if (p_next_state != p_state) - { - do + // Have we changed state? + if (p_next_state != p_state) { - p_state->on_exit_state(); - p_state = p_next_state; + do + { + p_state->on_exit_state(); + p_state = p_next_state; - next_state_id = p_state->on_enter_state(); - ETL_ASSERT(next_state_id < number_of_states, ETL_ERROR(etl::fsm_state_id_exception)); + next_state_id = p_state->on_enter_state(); + ETL_ASSERT(next_state_id < number_of_states, ETL_ERROR(etl::fsm_state_id_exception)); - p_next_state = state_list[next_state_id]; + p_next_state = state_list[next_state_id]; - } while (p_next_state != p_state); // Have we changed state again? - } + } while (p_next_state != p_state); // Have we changed state again? + } } using imessage_router::accepts; diff --git a/include/etl/fsm_generator.h b/include/etl/fsm_generator.h index 37007900..a937542e 100644 --- a/include/etl/fsm_generator.h +++ b/include/etl/fsm_generator.h @@ -254,17 +254,26 @@ namespace etl //******************************************* void start(bool call_on_enter_state = true) { - // 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)); + // 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)); - if (call_on_enter_state) - { - p_state->on_enter_state(); - } - } + if (call_on_enter_state) + { + etl::fsm_state_id_t next_state_id; + etl::ifsm_state* p_last_state; + + do + { + p_last_state = p_state; + next_state_id = p_state->on_enter_state(); + p_state = state_list[next_state_id]; + + } while (p_last_state != p_state); + } + } } //******************************************* @@ -272,7 +281,7 @@ namespace etl //******************************************* void receive(const etl::imessage& message) { - etl::null_message_router nmr; + static etl::null_message_router nmr; receive(nmr, message); } @@ -281,26 +290,26 @@ namespace etl //******************************************* void receive(etl::imessage_router& source, const etl::imessage& message) { - etl::fsm_state_id_t next_state_id = p_state->process_event(source, message); - ETL_ASSERT(next_state_id < number_of_states, ETL_ERROR(etl::fsm_state_id_exception)); + etl::fsm_state_id_t next_state_id = p_state->process_event(source, message); + ETL_ASSERT(next_state_id < number_of_states, ETL_ERROR(etl::fsm_state_id_exception)); - etl::ifsm_state* p_next_state = state_list[next_state_id]; + etl::ifsm_state* p_next_state = state_list[next_state_id]; - // Have we changed state? - if (p_next_state != p_state) - { - do + // Have we changed state? + if (p_next_state != p_state) { - p_state->on_exit_state(); - p_state = p_next_state; + do + { + p_state->on_exit_state(); + p_state = p_next_state; - next_state_id = p_state->on_enter_state(); - ETL_ASSERT(next_state_id < number_of_states, ETL_ERROR(etl::fsm_state_id_exception)); + next_state_id = p_state->on_enter_state(); + ETL_ASSERT(next_state_id < number_of_states, ETL_ERROR(etl::fsm_state_id_exception)); - p_next_state = state_list[next_state_id]; + p_next_state = state_list[next_state_id]; - } while (p_next_state != p_state); // Have we changed state again? - } + } while (p_next_state != p_state); // Have we changed state again? + } } using imessage_router::accepts; diff --git a/include/etl/message.h b/include/etl/message.h index 64d1bb96..83714cb3 100644 --- a/include/etl/message.h +++ b/include/etl/message.h @@ -76,12 +76,10 @@ namespace etl const etl::message_id_t message_id; #if defined(ETL_MESSAGES_ARE_VIRTUAL) || defined(ETL_POLYMORPHIC_MESSAGES) - public: virtual ~imessage() { } #else - protected: ~imessage() { } diff --git a/include/etl/version.h b/include/etl/version.h index 1f59cc16..5b4b22be 100644 --- a/include/etl/version.h +++ b/include/etl/version.h @@ -37,10 +37,10 @@ SOFTWARE. /// Definitions of the ETL version ///\ingroup utilities -#define ETL_VERSION "11.11.1" +#define ETL_VERSION "11.11.2" #define ETL_VERSION_MAJOR 11 #define ETL_VERSION_MINOR 11 -#define ETL_VERSION_PATCH 1 +#define ETL_VERSION_PATCH 2 #endif diff --git a/test/test_fsm.cpp b/test/test_fsm.cpp index 37c2a8ec..74cb9b45 100644 --- a/test/test_fsm.cpp +++ b/test/test_fsm.cpp @@ -31,9 +31,12 @@ SOFTWARE. #include "fsm.h" #include "enum_type.h" #include "container.h" +#include "packet.h" +#include "queue.h" #include + namespace { const etl::message_router_id_t MOTOR_CONTROL = 0; @@ -49,6 +52,7 @@ namespace STOP, STOPPED, SET_SPEED, + RECURSIVE, UNSUPPORTED }; @@ -57,6 +61,7 @@ namespace ETL_ENUM_TYPE(STOP, "Stop") ETL_ENUM_TYPE(STOPPED, "Stopped") ETL_ENUM_TYPE(SET_SPEED, "Set Speed") + ETL_ENUM_TYPE(RECURSIVE, "Recursive") ETL_ENUM_TYPE(UNSUPPORTED, "Unsupported") ETL_END_ENUM_TYPE }; @@ -92,6 +97,11 @@ namespace { }; + //*********************************** + class Recursive : public etl::message + { + }; + //*********************************** class Unsupported : public etl::message { @@ -145,7 +155,7 @@ namespace } //*********************************** - void SetSpeed(int speed_) + void SetSpeedValue(int speed_) { speed = speed_; } @@ -162,6 +172,19 @@ namespace isLampOn = false; } + //*********************************** + template + void queue_recursive_message(const T& message) + { + messageQueue.emplace(message); + } + + typedef etl::largest Largest_t; + + typedef etl::packet Packet_t; + + etl::queue messageQueue; + int startCount; int stopCount; int setSpeedCount; @@ -174,7 +197,7 @@ namespace //*********************************** // The idle state. //*********************************** - class Idle : public etl::fsm_state + class Idle : public etl::fsm_state { public: @@ -185,6 +208,13 @@ namespace return StateId::RUNNING; } + //*********************************** + etl::fsm_state_id_t on_event(etl::imessage_router&, const Recursive&) + { + get_fsm_context().queue_recursive_message(Start()); + return StateId::IDLE; + } + //*********************************** etl::fsm_state_id_t on_event_unknown(etl::imessage_router&, const etl::imessage&) { @@ -226,7 +256,7 @@ namespace etl::fsm_state_id_t on_event(etl::imessage_router&, const SetSpeed& event) { ++get_fsm_context().setSpeedCount; - get_fsm_context().SetSpeed(event.speed); + get_fsm_context().SetSpeedValue(event.speed); return STATE_ID; } @@ -309,7 +339,7 @@ namespace CHECK(!motorControl.is_started()); // Start the FSM. - motorControl.start(); + motorControl.start(false); CHECK(motorControl.is_started()); // Now in Idle state. @@ -447,7 +477,7 @@ namespace CHECK(!motorControl.is_started()); // Start the FSM. - motorControl.start(); + motorControl.start(false); CHECK(motorControl.is_started()); // Now in Idle state. @@ -484,6 +514,43 @@ namespace CHECK_EQUAL(0, motorControl.unknownCount); } + //************************************************************************* + TEST(test_fsm_recursive_event) + { + etl::null_message_router nmr; + + motorControl.reset(); + motorControl.ClearStatistics(); + + motorControl.messageQueue.clear(); + + // Start the FSM. + motorControl.start(false); + + // Now in Idle state. + // Send Start event. + motorControl.receive(nmr, Recursive()); + + CHECK_EQUAL(1, motorControl.messageQueue.size()); + + // Send the queued message. + motorControl.receive(nmr, motorControl.messageQueue.front().get()); + motorControl.messageQueue.pop(); + + // 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.isLampOn); + CHECK_EQUAL(0, motorControl.setSpeedCount); + CHECK_EQUAL(0, motorControl.speed); + CHECK_EQUAL(1, motorControl.startCount); + CHECK_EQUAL(0, motorControl.stopCount); + CHECK_EQUAL(0, motorControl.stoppedCount); + CHECK_EQUAL(0, motorControl.unknownCount); + } + //************************************************************************* TEST(test_fsm_supported) {