From ffc17160da2b12e8ea9f604ae744acb0a1ed59bc Mon Sep 17 00:00:00 2001 From: John Wellbelove Date: Tue, 27 May 2025 16:05:46 +0100 Subject: [PATCH] Added support for self transition to etl::hfsm Changed all instances of 'return STATE_ID' to 'return No_State_Change' --- include/etl/hfsm.h | 17 +++++--- test/test_fsm.cpp | 10 ++--- test/test_hfsm.cpp | 102 ++++++++++++++++++++++++++++++++------------- 3 files changed, 88 insertions(+), 41 deletions(-) diff --git a/include/etl/hfsm.h b/include/etl/hfsm.h index 8afe94f4..1a1cfcd0 100644 --- a/include/etl/hfsm.h +++ b/include/etl/hfsm.h @@ -103,13 +103,13 @@ namespace etl etl::fsm_state_id_t next_state_id = p_state->process_event(message); if (next_state_id != ifsm_state::No_State_Change) - { - ETL_ASSERT_OR_RETURN(next_state_id < number_of_states, ETL_ERROR(etl::fsm_state_id_exception)); - etl::ifsm_state* p_next_state = state_list[next_state_id]; - - // Have we changed state? - if (p_next_state != p_state) + { + if (have_changed_state(next_state_id)) { + ETL_ASSERT_OR_RETURN(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_root = common_ancestor(p_state, p_next_state); do_exits(p_root, p_state); @@ -123,6 +123,11 @@ namespace etl p_state = state_list[next_state_id]; } } + else if (is_self_transition(next_state_id)) + { + p_state->on_exit_state(); + p_state->on_enter_state(); + } } } else diff --git a/test/test_fsm.cpp b/test/test_fsm.cpp index d3481dd3..a1b29b04 100644 --- a/test/test_fsm.cpp +++ b/test/test_fsm.cpp @@ -293,14 +293,14 @@ namespace { ++get_fsm_context().setSpeedCount; get_fsm_context().SetSpeedValue(event.speed); - return STATE_ID; + return No_State_Change; } //*********************************** etl::fsm_state_id_t on_event_unknown(const etl::imessage&) { ++get_fsm_context().unknownCount; - return STATE_ID; + return No_State_Change; } //*********************************** @@ -308,7 +308,7 @@ namespace { get_fsm_context().TurnRunningLampOn(); - return STATE_ID; + return No_State_Change; } }; @@ -330,7 +330,7 @@ namespace etl::fsm_state_id_t on_event_unknown(const etl::imessage&) { ++get_fsm_context().unknownCount; - return STATE_ID; + return No_State_Change; } }; @@ -345,7 +345,7 @@ namespace etl::fsm_state_id_t on_event_unknown(const etl::imessage&) { ++get_fsm_context().unknownCount; - return STATE_ID; + return No_State_Change; } }; diff --git a/test/test_hfsm.cpp b/test/test_hfsm.cpp index 4ea762ab..adc794ac 100644 --- a/test/test_hfsm.cpp +++ b/test/test_hfsm.cpp @@ -82,19 +82,21 @@ namespace Stopped, Set_Speed, Recursive, + Self_Transition, Timeout, Unsupported }; ETL_DECLARE_ENUM_TYPE(EventId, etl::message_id_t) - ETL_ENUM_TYPE(Start, "Start") - ETL_ENUM_TYPE(Stop, "Stop") - ETL_ENUM_TYPE(EStop, "E-Stop") - ETL_ENUM_TYPE(Stopped, "Stopped") - ETL_ENUM_TYPE(Set_Speed, "Set Speed") - ETL_ENUM_TYPE(Recursive, "Recursive") - ETL_ENUM_TYPE(Timeout, "Timeout") - ETL_ENUM_TYPE(Unsupported, "Unsupported") + ETL_ENUM_TYPE(Start, "Start") + ETL_ENUM_TYPE(Stop, "Stop") + ETL_ENUM_TYPE(EStop, "E-Stop") + ETL_ENUM_TYPE(Stopped, "Stopped") + ETL_ENUM_TYPE(Set_Speed, "Set Speed") + ETL_ENUM_TYPE(Recursive, "Recursive") + ETL_ENUM_TYPE(Self_Transition, "Self_Transition") + ETL_ENUM_TYPE(Timeout, "Timeout") + ETL_ENUM_TYPE(Unsupported, "Unsupported") ETL_END_ENUM_TYPE }; @@ -133,6 +135,11 @@ namespace { }; + //*********************************** + class SelfTransition : public etl::message + { + }; + //*********************************** class Timeout : public etl::message { @@ -195,6 +202,7 @@ namespace windUpStartCount = 0; unknownCount = 0; stoppedCount = 0; + selfTransitionCount = 0; isLampOn = false; speed = 0; @@ -240,6 +248,7 @@ namespace int setSpeedCount; int unknownCount; int stoppedCount; + int selfTransitionCount; bool isLampOn; int speed; @@ -297,7 +306,7 @@ namespace //*********************************** // The running state. //*********************************** - class Running : public etl::fsm_state + class Running : public etl::fsm_state { public: @@ -326,6 +335,22 @@ namespace return No_State_Change; } + //*********************************** + etl::fsm_state_id_t on_event(const SetSpeed& event) + { + ++get_fsm_context().setSpeedCount; + get_fsm_context().SetSpeedValue(event.speed); + + return No_State_Change; + } + + //*********************************** + etl::fsm_state_id_t on_event(const SelfTransition&) + { + ++get_fsm_context().selfTransitionCount; + return Self_Transition; + } + //*********************************** void on_exit_state() { @@ -383,7 +408,7 @@ namespace //*********************************** // The at speed state. //*********************************** - class AtSpeed : public etl::fsm_state + class AtSpeed : public etl::fsm_state { public: //*********************************** @@ -393,15 +418,6 @@ namespace return StateId::Winding_Down; } - //*********************************** - etl::fsm_state_id_t on_event(const SetSpeed& event) - { - ++get_fsm_context().setSpeedCount; - get_fsm_context().SetSpeedValue(event.speed); - //return No_State_Change; - return this->get_state_id(); - } - //*********************************** etl::fsm_state_id_t on_event_unknown(const etl::imessage&) { @@ -504,7 +520,6 @@ namespace // Send unhandled events. motorControl.receive(Stop()); motorControl.receive(Stopped()); - motorControl.receive(SetSpeed(10)); CHECK_EQUAL(StateId::Idle, motorControl.get_state_id()); CHECK_EQUAL(StateId::Idle, motorControl.get_state().get_state_id()); @@ -515,7 +530,7 @@ namespace CHECK_EQUAL(0, motorControl.startCount); CHECK_EQUAL(0, motorControl.stopCount); CHECK_EQUAL(0, motorControl.stoppedCount); - CHECK_EQUAL(3, motorControl.unknownCount); + CHECK_EQUAL(2, motorControl.unknownCount); CHECK_EQUAL(0, motorControl.windUpCompleteCount); CHECK_EQUAL(0, motorControl.windUpStartCount); @@ -533,7 +548,7 @@ namespace CHECK_EQUAL(1, motorControl.startCount); CHECK_EQUAL(0, motorControl.stopCount); CHECK_EQUAL(0, motorControl.stoppedCount); - CHECK_EQUAL(3, motorControl.unknownCount); + CHECK_EQUAL(2, motorControl.unknownCount); CHECK_EQUAL(0, motorControl.windUpCompleteCount); CHECK_EQUAL(1, motorControl.windUpStartCount); @@ -550,7 +565,7 @@ namespace CHECK_EQUAL(1, motorControl.startCount); CHECK_EQUAL(0, motorControl.stopCount); CHECK_EQUAL(0, motorControl.stoppedCount); - CHECK_EQUAL(5, motorControl.unknownCount); + CHECK_EQUAL(4, motorControl.unknownCount); CHECK_EQUAL(0, motorControl.windUpCompleteCount); CHECK_EQUAL(1, motorControl.windUpStartCount); @@ -566,7 +581,7 @@ namespace CHECK_EQUAL(1, motorControl.startCount); CHECK_EQUAL(0, motorControl.stopCount); CHECK_EQUAL(0, motorControl.stoppedCount); - CHECK_EQUAL(5, motorControl.unknownCount); + CHECK_EQUAL(4, motorControl.unknownCount); CHECK_EQUAL(1, motorControl.windUpCompleteCount); CHECK_EQUAL(1, motorControl.windUpStartCount); @@ -584,7 +599,7 @@ namespace CHECK_EQUAL(1, motorControl.startCount); CHECK_EQUAL(0, motorControl.stopCount); CHECK_EQUAL(0, motorControl.stoppedCount); - CHECK_EQUAL(5, motorControl.unknownCount); + CHECK_EQUAL(4, motorControl.unknownCount); CHECK_EQUAL(1, motorControl.windUpCompleteCount); CHECK_EQUAL(1, motorControl.windUpStartCount); @@ -602,14 +617,13 @@ namespace CHECK_EQUAL(1, motorControl.startCount); CHECK_EQUAL(1, motorControl.stopCount); CHECK_EQUAL(0, motorControl.stoppedCount); - CHECK_EQUAL(5, motorControl.unknownCount); + CHECK_EQUAL(4, motorControl.unknownCount); CHECK_EQUAL(1, motorControl.windUpCompleteCount); CHECK_EQUAL(1, motorControl.windUpStartCount); // Send unhandled events. motorControl.receive(Start()); motorControl.receive(Stop()); - motorControl.receive(SetSpeed(100)); CHECK_EQUAL(StateId::Winding_Down, int(motorControl.get_state_id())); CHECK_EQUAL(StateId::Winding_Down, int(motorControl.get_state().get_state_id())); @@ -620,7 +634,7 @@ namespace CHECK_EQUAL(1, motorControl.startCount); CHECK_EQUAL(1, motorControl.stopCount); CHECK_EQUAL(0, motorControl.stoppedCount); - CHECK_EQUAL(8, motorControl.unknownCount); + CHECK_EQUAL(6, motorControl.unknownCount); CHECK_EQUAL(1, motorControl.windUpCompleteCount); CHECK_EQUAL(1, motorControl.windUpStartCount); @@ -637,7 +651,7 @@ namespace CHECK_EQUAL(1, motorControl.startCount); CHECK_EQUAL(1, motorControl.stopCount); CHECK_EQUAL(1, motorControl.stoppedCount); - CHECK_EQUAL(8, motorControl.unknownCount); + CHECK_EQUAL(6, motorControl.unknownCount); CHECK_EQUAL(1, motorControl.windUpCompleteCount); CHECK_EQUAL(1, motorControl.windUpStartCount); } @@ -980,11 +994,39 @@ namespace } //************************************************************************* - TEST(test_fsm_no_states_and_no_start) + TEST(test_hfsm_no_states_and_no_start) { MotorControl mc; CHECK_THROW(mc.receive(Start()), etl::fsm_not_started); } + + //************************************************************************* + TEST(test_hfsm_self_transition) + { + MotorControl motorControl; + + motorControl.Initialise(stateList, ETL_OR_STD17::size(stateList)); + motorControl.reset(); + motorControl.ClearStatistics(); + + // Start the FSM. + motorControl.start(false); + + // Send Start event. + motorControl.receive(Start()); + + // Send Timeout event + motorControl.receive(Timeout()); + // Now in AtSpeed state. + + CHECK_EQUAL(0, motorControl.selfTransitionCount); + + // Execute self transition. + // Handled by the parent Running state. + motorControl.receive(SelfTransition()); + + CHECK_EQUAL(1, motorControl.selfTransitionCount); + } }; }