diff --git a/include/etl/state_chart.h b/include/etl/state_chart.h
index 1657e174..b37f6578 100644
--- a/include/etl/state_chart.h
+++ b/include/etl/state_chart.h
@@ -138,7 +138,8 @@ namespace etl
const state_id_t state_id_)
: istate_chart(state_id_),
object(object_),
- transition_table(transition_table_.begin(), transition_table_.end())
+ transition_table(transition_table_.begin(), transition_table_.end()),
+ started(false)
{
}
@@ -189,6 +190,29 @@ namespace etl
}
}
+ //*************************************************************************
+ ///
+ //*************************************************************************
+ void start(const bool on_entry_initial = true)
+ {
+ if (!started)
+ {
+ if (on_entry_initial)
+ {
+ // See if we have a state item for the initial state.
+ const state* s = find_state(current_state_id);
+
+ // If the initial state has an 'on_entry' then call it.
+ if ((s != state_table.end()) && (s->on_entry != nullptr))
+ {
+ (object.*(s->on_entry))();
+ }
+ }
+
+ started = true;
+ }
+ }
+
//*************************************************************************
/// Processes the specified event.
/// The state machine will action the first item in the transition table
@@ -197,52 +221,51 @@ namespace etl
//*************************************************************************
void process_event(const event_id_t event_id)
{
- // Scan the transition table.
- const transition* t = std::find_if(transition_table.begin(),
- transition_table.end(),
- is_transition(event_id, current_state_id));
-
- // Found an entry?
- if (t != transition_table.end())
+ if (started)
{
- // Shall we execute the transition?
- if ((t->guard == nullptr) || ((object.*t->guard)()))
+ // Scan the transition table.
+ const transition* t = std::find_if(transition_table.begin(),
+ transition_table.end(),
+ is_transition(event_id, current_state_id));
+
+ // Found an entry?
+ if (t != transition_table.end())
{
- const bool to_new_state = (current_state_id != t->next_state_id);
-
- // Execute the state exit if necessary.
- if (to_new_state)
+ // Shall we execute the transition?
+ if ((t->guard == nullptr) || ((object.*t->guard)()))
{
- // See if we have a state item for the current state.
- const 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)();
}
- }
- // Shall we execute the action?
- if (t->action != nullptr)
- {
- (object.*t->action)();
- }
-
- // Execute the state entry if necessary.
- if (to_new_state)
- {
- // See if we have a state item for the next state.
- const 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;
+ }
}
}
}
@@ -290,6 +313,7 @@ namespace etl
TObject& object; ///< The object that supplies guard and action member functions.
const etl::array_view transition_table; ///< The table of transitions.
etl::array_view state_table; ///< The table of states.
+ bool started; ///< Set if the state chart has been started.
};
}
diff --git a/test/test_state_chart.cpp b/test/test_state_chart.cpp
index a7730ce8..61f4bf07 100644
--- a/test/test_state_chart.cpp
+++ b/test/test_state_chart.cpp
@@ -102,6 +102,7 @@ namespace
isLampOn = false;
speed = 0;
windingDown = 0;
+ entered_idle = false;
}
//***********************************
@@ -133,6 +134,7 @@ namespace
void OnEnterIdle()
{
TurnRunningLampOff();
+ entered_idle = true;
}
//***********************************
@@ -184,6 +186,7 @@ namespace
bool isLampOn;
int speed;
int windingDown;
+ bool entered_idle;
bool guard;
@@ -218,7 +221,7 @@ namespace
{
motorControl.ClearStatistics();
- // Now in Idle state.
+ // In Idle state.
CHECK_EQUAL(StateId::IDLE, int(motorControl.get_state_id()));
CHECK_EQUAL(false, motorControl.isLampOn);
@@ -228,12 +231,13 @@ namespace
CHECK_EQUAL(0, motorControl.stopCount);
CHECK_EQUAL(0, motorControl.stoppedCount);
CHECK_EQUAL(0, motorControl.windingDown);
+ CHECK_EQUAL(false, motorControl.entered_idle);
- // Send unhandled events.
- motorControl.process_event(EventId::STOP);
- motorControl.process_event(EventId::STOPPED);
+ // Send Start event (state chart not started).
+ motorControl.guard = true;
+ motorControl.process_event(EventId::START);
- CHECK_EQUAL(StateId::IDLE, motorControl.get_state_id());
+ CHECK_EQUAL(StateId::IDLE, int(motorControl.get_state_id()));
CHECK_EQUAL(false, motorControl.isLampOn);
CHECK_EQUAL(0, motorControl.setSpeedCount);
@@ -241,7 +245,29 @@ namespace
CHECK_EQUAL(0, motorControl.startCount);
CHECK_EQUAL(0, motorControl.stopCount);
CHECK_EQUAL(0, motorControl.stoppedCount);
+ CHECK_EQUAL(0, motorControl.windingDown);
+ CHECK_EQUAL(false, motorControl.entered_idle);
+ // Start the state chart
+ motorControl.guard = true;
+ motorControl.start();
+
+ CHECK_EQUAL(true, motorControl.entered_idle);
+
+ // Send unhandled events.
+ motorControl.process_event(EventId::STOP);
+ motorControl.process_event(EventId::STOPPED);
+
+ CHECK_EQUAL(StateId::IDLE, int(motorControl.get_state_id()));
+
+ CHECK_EQUAL(false, motorControl.isLampOn);
+ CHECK_EQUAL(0, motorControl.setSpeedCount);
+ CHECK_EQUAL(0, motorControl.speed);
+ CHECK_EQUAL(0, motorControl.startCount);
+ CHECK_EQUAL(0, motorControl.stopCount);
+ CHECK_EQUAL(0, motorControl.stoppedCount);
+ CHECK_EQUAL(0, motorControl.windingDown);
+
// Send Start event.
motorControl.guard = false;
motorControl.process_event(EventId::START);