diff --git a/include/etl/state_chart.h b/include/etl/state_chart.h index b37f6578..94667279 100644 --- a/include/etl/state_chart.h +++ b/include/etl/state_chart.h @@ -223,48 +223,61 @@ namespace etl { if (started) { - // Scan the transition table. - const transition* t = std::find_if(transition_table.begin(), - transition_table.end(), - is_transition(event_id, current_state_id)); + const transition* t = transition_table.begin(); - // Found an entry? - if (t != transition_table.end()) + // Keep looping until we execute a transition or reach the end of the table. + while (t != transition_table.end()) { - // Shall we execute the transition? - if ((t->guard == nullptr) || ((object.*t->guard)())) + // Scan the transition table from the latest position. + t = std::find_if(t, + transition_table.end(), + is_transition(event_id, current_state_id)); + + // Found an entry? + if (t != transition_table.end()) { - // Shall we execute the action? - if (t->action != nullptr) + // Shall we execute the transition? + if ((t->guard == nullptr) || ((object.*t->guard)())) { - (object.*t->action)(); - } - - // Changing state? - if (current_state_id != t->next_state_id) - { - const state* s; - - // See if we have a state item for the current state. - s = find_state(current_state_id); - - // If the current state has an 'on_exit' then call it. - if ((s != state_table.end()) && (s->on_exit != nullptr)) + // Shall we execute the action? + if (t->action != nullptr) { - (object.*(s->on_exit))(); + (object.*t->action)(); } - // See if we have a state item for the next state. - s = find_state(t->next_state_id); - - // If the new state has an 'on_entry' then call it. - if ((s != state_table.end()) && (s->on_entry != nullptr)) + // Changing state? + if (current_state_id != t->next_state_id) { - (object.*(s->on_entry))(); - } - } + const state* s; - current_state_id = t->next_state_id; + // See if we have a state item for the current state. + s = find_state(current_state_id); + + // If the current state has an 'on_exit' then call it. + if ((s != state_table.end()) && (s->on_exit != nullptr)) + { + (object.*(s->on_exit))(); + } + + // See if we have a state item for the next state. + s = find_state(t->next_state_id); + + // If the new state has an 'on_entry' then call it. + if ((s != state_table.end()) && (s->on_entry != nullptr)) + { + (object.*(s->on_entry))(); + } + + current_state_id = t->next_state_id; + } + + t = transition_table.end(); + } + else + { + // Start the search from the next item in the table. + ++t; + } } } } diff --git a/test/test_state_chart.cpp b/test/test_state_chart.cpp index 61f4bf07..2134ed6b 100644 --- a/test/test_state_chart.cpp +++ b/test/test_state_chart.cpp @@ -103,6 +103,7 @@ namespace speed = 0; windingDown = 0; entered_idle = false; + null = 0; } //*********************************** @@ -167,6 +168,12 @@ namespace return guard; } + //*********************************** + bool NotGuard() + { + return !guard; + } + //*********************************** void TurnRunningLampOn() { @@ -179,6 +186,12 @@ namespace isLampOn = false; } + //*********************************** + void Null() + { + ++null; + } + int startCount; int stopCount; int setSpeedCount; @@ -187,17 +200,19 @@ namespace int speed; int windingDown; bool entered_idle; + int null; bool guard; - static const etl::array transitionTable; + static const etl::array transitionTable; static const etl::array stateTable; }; //*************************************************************************** - const etl::array MotorControl::transitionTable = + const etl::array MotorControl::transitionTable = { MotorControl::transition(EventId::START, StateId::IDLE, StateId::RUNNING, &MotorControl::OnStart, &MotorControl::Guard), + MotorControl::transition(EventId::START, StateId::IDLE, StateId::IDLE, &MotorControl::Null, &MotorControl::NotGuard), MotorControl::transition(EventId::STOP, StateId::RUNNING, StateId::WINDING_DOWN, &MotorControl::OnStop), MotorControl::transition(EventId::STOPPED, StateId::WINDING_DOWN, StateId::IDLE, &MotorControl::OnStopped), MotorControl::transition(EventId::EMERGENCY_STOP, StateId::RUNNING, StateId::IDLE, &MotorControl::OnStop), @@ -283,6 +298,7 @@ namespace CHECK_EQUAL(0, motorControl.stopCount); CHECK_EQUAL(0, motorControl.stoppedCount); CHECK_EQUAL(0, motorControl.windingDown); + CHECK_EQUAL(1, motorControl.null); // Send Start event. motorControl.guard = true; @@ -299,6 +315,7 @@ namespace CHECK_EQUAL(0, motorControl.stopCount); CHECK_EQUAL(0, motorControl.stoppedCount); CHECK_EQUAL(0, motorControl.windingDown); + CHECK_EQUAL(1, motorControl.null); // Send unhandled events. motorControl.process_event(EventId::START); @@ -313,6 +330,7 @@ namespace CHECK_EQUAL(0, motorControl.stopCount); CHECK_EQUAL(0, motorControl.stoppedCount); CHECK_EQUAL(0, motorControl.windingDown); + CHECK_EQUAL(1, motorControl.null); // Send SetSpeed event. motorControl.process_event(EventId::SET_SPEED); @@ -328,6 +346,7 @@ namespace CHECK_EQUAL(0, motorControl.stopCount); CHECK_EQUAL(0, motorControl.stoppedCount); CHECK_EQUAL(0, motorControl.windingDown); + CHECK_EQUAL(1, motorControl.null); // Send Stop event. motorControl.process_event(EventId::STOP); @@ -343,6 +362,7 @@ namespace CHECK_EQUAL(1, motorControl.stopCount); CHECK_EQUAL(0, motorControl.stoppedCount); CHECK_EQUAL(1, motorControl.windingDown); + CHECK_EQUAL(1, motorControl.null); // Send unhandled events. motorControl.process_event(EventId::START); @@ -357,6 +377,7 @@ namespace CHECK_EQUAL(1, motorControl.stopCount); CHECK_EQUAL(0, motorControl.stoppedCount); CHECK_EQUAL(1, motorControl.windingDown); + CHECK_EQUAL(1, motorControl.null); // Send Stopped event. motorControl.process_event(EventId::STOPPED); @@ -371,6 +392,7 @@ namespace CHECK_EQUAL(1, motorControl.stopCount); CHECK_EQUAL(1, motorControl.stoppedCount); CHECK_EQUAL(0, motorControl.windingDown); + CHECK_EQUAL(1, motorControl.null); } //*************************************************************************