From 8ac2db0dbe9b9f69e88b24028203a1b52cc9227e Mon Sep 17 00:00:00 2001 From: Jeremy Overesch <10420943+jovere@users.noreply.github.com> Date: Thu, 28 Apr 2022 09:29:11 -0500 Subject: [PATCH] Feature/pass partial handled events to parent (#541) * Pass partial handled events to parent. * Fix the generator file for C++17. --- include/etl/fsm.h | 36 ++++++++------- include/etl/generators/fsm_generator.h | 8 ++-- test/test_hfsm.cpp | 63 +++++++++++++++++++++++++- 3 files changed, 86 insertions(+), 21 deletions(-) diff --git a/include/etl/fsm.h b/include/etl/fsm.h index 3f7cdbff..1a6d0bc1 100644 --- a/include/etl/fsm.h +++ b/include/etl/fsm.h @@ -179,6 +179,8 @@ namespace etl // Pass this whenever no state change is desired. // The highest unsigned value of fsm_state_id_t. static ETL_CONSTANT fsm_state_id_t No_State_Change = etl::integral_limits::max; + // Pass this when this event also needs to be passed to the parent. + static ETL_CONSTANT fsm_state_id_t Pass_To_Parent = No_State_Change - 1U; /// Allows ifsm_state functions to be private. friend class etl::fsm; @@ -542,7 +544,7 @@ namespace etl const bool was_handled = (process_event_type(message, new_state_id) || ...); - if (!was_handled) + if (!was_handled || new_state_id == Pass_To_Parent) { new_state_id = (p_parent != nullptr) ? p_parent->process_event(message) : static_cast(this)->on_event_unknown(message); } @@ -630,7 +632,7 @@ namespace etl default: new_state_id = p_parent ? p_parent->process_event(message) : static_cast(this)->on_event_unknown(message); break; } - return new_state_id; + return (new_state_id != Pass_To_Parent) ? new_state_id : (p_parent ? p_parent->process_event(message) : No_State_Change); } }; @@ -694,7 +696,7 @@ namespace etl default: new_state_id = p_parent ? p_parent->process_event(message) : static_cast(this)->on_event_unknown(message); break; } - return new_state_id; + return (new_state_id != Pass_To_Parent) ? new_state_id : (p_parent ? p_parent->process_event(message) : No_State_Change); } }; @@ -757,7 +759,7 @@ namespace etl default: new_state_id = p_parent ? p_parent->process_event(message) : static_cast(this)->on_event_unknown(message); break; } - return new_state_id; + return (new_state_id != Pass_To_Parent) ? new_state_id : (p_parent ? p_parent->process_event(message) : No_State_Change); } }; @@ -819,7 +821,7 @@ namespace etl default: new_state_id = p_parent ? p_parent->process_event(message) : static_cast(this)->on_event_unknown(message); break; } - return new_state_id; + return (new_state_id != Pass_To_Parent) ? new_state_id : (p_parent ? p_parent->process_event(message) : No_State_Change); } }; @@ -879,7 +881,7 @@ namespace etl default: new_state_id = p_parent ? p_parent->process_event(message) : static_cast(this)->on_event_unknown(message); break; } - return new_state_id; + return (new_state_id != Pass_To_Parent) ? new_state_id : (p_parent ? p_parent->process_event(message) : No_State_Change); } }; @@ -938,7 +940,7 @@ namespace etl default: new_state_id = p_parent ? p_parent->process_event(message) : static_cast(this)->on_event_unknown(message); break; } - return new_state_id; + return (new_state_id != Pass_To_Parent) ? new_state_id : (p_parent ? p_parent->process_event(message) : No_State_Change); } }; @@ -996,7 +998,7 @@ namespace etl default: new_state_id = p_parent ? p_parent->process_event(message) : static_cast(this)->on_event_unknown(message); break; } - return new_state_id; + return (new_state_id != Pass_To_Parent) ? new_state_id : (p_parent ? p_parent->process_event(message) : No_State_Change); } }; @@ -1053,7 +1055,7 @@ namespace etl default: new_state_id = p_parent ? p_parent->process_event(message) : static_cast(this)->on_event_unknown(message); break; } - return new_state_id; + return (new_state_id != Pass_To_Parent) ? new_state_id : (p_parent ? p_parent->process_event(message) : No_State_Change); } }; @@ -1108,7 +1110,7 @@ namespace etl default: new_state_id = p_parent ? p_parent->process_event(message) : static_cast(this)->on_event_unknown(message); break; } - return new_state_id; + return (new_state_id != Pass_To_Parent) ? new_state_id : (p_parent ? p_parent->process_event(message) : No_State_Change); } }; @@ -1162,7 +1164,7 @@ namespace etl default: new_state_id = p_parent ? p_parent->process_event(message) : static_cast(this)->on_event_unknown(message); break; } - return new_state_id; + return (new_state_id != Pass_To_Parent) ? new_state_id : (p_parent ? p_parent->process_event(message) : No_State_Change); } }; @@ -1215,7 +1217,7 @@ namespace etl default: new_state_id = p_parent ? p_parent->process_event(message) : static_cast(this)->on_event_unknown(message); break; } - return new_state_id; + return (new_state_id != Pass_To_Parent) ? new_state_id : (p_parent ? p_parent->process_event(message) : No_State_Change); } }; @@ -1267,7 +1269,7 @@ namespace etl default: new_state_id = p_parent ? p_parent->process_event(message) : static_cast(this)->on_event_unknown(message); break; } - return new_state_id; + return (new_state_id != Pass_To_Parent) ? new_state_id : (p_parent ? p_parent->process_event(message) : No_State_Change); } }; @@ -1317,7 +1319,7 @@ namespace etl default: new_state_id = p_parent ? p_parent->process_event(message) : static_cast(this)->on_event_unknown(message); break; } - return new_state_id; + return (new_state_id != Pass_To_Parent) ? new_state_id : (p_parent ? p_parent->process_event(message) : No_State_Change); } }; @@ -1366,7 +1368,7 @@ namespace etl default: new_state_id = p_parent ? p_parent->process_event(message) : static_cast(this)->on_event_unknown(message); break; } - return new_state_id; + return (new_state_id != Pass_To_Parent) ? new_state_id : (p_parent ? p_parent->process_event(message) : No_State_Change); } }; @@ -1414,7 +1416,7 @@ namespace etl default: new_state_id = p_parent ? p_parent->process_event(message) : static_cast(this)->on_event_unknown(message); break; } - return new_state_id; + return (new_state_id != Pass_To_Parent) ? new_state_id : (p_parent ? p_parent->process_event(message) : No_State_Change); } }; @@ -1461,7 +1463,7 @@ namespace etl default: new_state_id = p_parent ? p_parent->process_event(message) : static_cast(this)->on_event_unknown(message); break; } - return new_state_id; + return (new_state_id != Pass_To_Parent) ? new_state_id : (p_parent ? p_parent->process_event(message) : No_State_Change); } }; diff --git a/include/etl/generators/fsm_generator.h b/include/etl/generators/fsm_generator.h index bb83be9c..a9d38643 100644 --- a/include/etl/generators/fsm_generator.h +++ b/include/etl/generators/fsm_generator.h @@ -198,6 +198,8 @@ namespace etl // Pass this whenever no state change is desired. // The highest unsigned value of fsm_state_id_t. static ETL_CONSTANT fsm_state_id_t No_State_Change = etl::integral_limits::max; + // Pass this when this event also needs to be passed to the parent. + static ETL_CONSTANT fsm_state_id_t Pass_To_Parent = No_State_Change - 1U; /// Allows ifsm_state functions to be private. friend class etl::fsm; @@ -568,7 +570,7 @@ namespace etl const bool was_handled = (process_event_type(message, new_state_id) || ...); - if (!was_handled) + if (!was_handled || new_state_id == Pass_To_Parent) { new_state_id = (p_parent != nullptr) ? p_parent->process_event(message) : static_cast(this)->on_event_unknown(message); } @@ -654,7 +656,7 @@ namespace etl cog.outl(" break;") cog.outl(" }") cog.outl("") - cog.outl(" return new_state_id;") + cog.outl(" return (new_state_id != Pass_To_Parent) ? new_state_id : (p_parent ? p_parent->process_event(message) : No_State_Change);") cog.outl(" }") cog.outl("};") @@ -731,7 +733,7 @@ namespace etl cog.outl(" break;") cog.outl(" }") cog.outl("") - cog.outl(" return new_state_id;") + cog.outl(" return (new_state_id != Pass_To_Parent) ? new_state_id : (p_parent ? p_parent->process_event(message) : No_State_Change);") cog.outl(" }") cog.outl("};") #################################### diff --git a/test/test_hfsm.cpp b/test/test_hfsm.cpp index b95c1779..375d1e98 100644 --- a/test/test_hfsm.cpp +++ b/test/test_hfsm.cpp @@ -376,10 +376,16 @@ namespace //*********************************** // The winding down state. //*********************************** - class WindingDown : public etl::fsm_state + class WindingDown : public etl::fsm_state { public: + etl::fsm_state_id_t on_event(const EStop&) + { + ++get_fsm_context().stopCount; + return Pass_To_Parent; + } + //*********************************** etl::fsm_state_id_t on_event(const Stopped&) { @@ -699,6 +705,61 @@ namespace CHECK_EQUAL(1, motorControl.windUpStartCount); } + //************************************************************************* + TEST(test_hfsm_emergency_stop_from_winding_down) + { + etl::null_message_router nmr; + + motorControl.Initialise(stateList, std::size(stateList)); + motorControl.reset(); + motorControl.ClearStatistics(); + + CHECK(!motorControl.is_started()); + + // Start the FSM. + motorControl.start(false); + CHECK(motorControl.is_started()); + + // Now in Idle state. + + // Send Start event. + motorControl.receive(Start()); + motorControl.receive(Timeout()); + motorControl.receive(Stop()); + + // Now in at speed 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.isLampOn); + CHECK_EQUAL(0, motorControl.setSpeedCount); + CHECK_EQUAL(0, motorControl.speed); + CHECK_EQUAL(1, motorControl.startCount); + CHECK_EQUAL(1, motorControl.stopCount); + CHECK_EQUAL(0, motorControl.stoppedCount); + CHECK_EQUAL(0, motorControl.unknownCount); + CHECK_EQUAL(1, motorControl.windUpCompleteCount); + CHECK_EQUAL(1, motorControl.windUpStartCount); + + // Send emergency Stop event. + motorControl.receive(EStop()); + + // 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.isLampOn); + CHECK_EQUAL(0, motorControl.setSpeedCount); + CHECK_EQUAL(0, motorControl.speed); + CHECK_EQUAL(1, motorControl.startCount); + CHECK_EQUAL(3, motorControl.stopCount); + CHECK_EQUAL(0, motorControl.stoppedCount); + CHECK_EQUAL(0, motorControl.unknownCount); + CHECK_EQUAL(1, motorControl.windUpCompleteCount); + CHECK_EQUAL(1, motorControl.windUpStartCount); + } + //************************************************************************* TEST(test_hfsm_recursive_event) {