Integrated FSM and message router classes.

Fixed GCC compatibility issue.
Added on_enter_state() return tests.
This commit is contained in:
John Wellbelove 2017-07-03 23:30:52 +01:00
parent 48e016d208
commit 73da59ad17
5 changed files with 220 additions and 63 deletions

View File

@ -127,7 +127,7 @@ namespace etl
etl::fsm_state_id_t get_state_id() const
{
return state_id;
}
}
protected:
@ -143,7 +143,7 @@ namespace etl
virtual fsm_state_id_t process_event(etl::imessage_router& source, const etl::imessage& message) = 0;
virtual void on_enter_state() {}; // By default, do nothing.
virtual fsm_state_id_t on_enter_state() { return state_id; }; // By default, do nothing.
virtual void on_exit_state() {}; // By default, do nothing.
// The state id.
@ -171,13 +171,13 @@ namespace etl
}
//*******************************************
inline void on_enter_state(etl::ifsm_state &state)
inline fsm_state_id_t on_enter_state(etl::ifsm_state& state)
{
state.on_enter_state();
return state.on_enter_state();
}
//*******************************************
inline void on_exit_state(etl::ifsm_state &state)
inline void on_exit_state(etl::ifsm_state& state)
{
state.on_exit_state();
}
@ -259,26 +259,33 @@ namespace etl
//*******************************************
void receive(const etl::imessage& message)
{
receive(etl::null_message_router(), message);
etl::null_message_router nmr;
receive(nmr, message);
}
//*******************************************
void receive(etl::imessage_router& source, const etl::imessage& message)
{
{
etl::fsm_state_id_t next_state_id = fsm_helper::process_event(*p_state, 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];
// Have we changed state?
if (next_state_id != p_state->get_state_id())
if (p_next_state != p_state)
{
//p_state->on_exit_state();
fsm_helper::on_exit_state(*p_state);
do
{
fsm_helper::on_exit_state(*p_state);
p_state = state_list[next_state_id];
ETL_ASSERT(p_state != nullptr, ETL_ERROR(etl::fsm_null_state_exception));
p_state = p_next_state;
next_state_id = fsm_helper::on_enter_state(*p_state);
ETL_ASSERT(next_state_id < number_of_states, ETL_ERROR(etl::fsm_state_id_exception));
fsm_helper::on_enter_state(*p_state);
p_next_state = state_list[next_state_id];
} while (p_next_state != p_state); // Have we changed state again?
}
}
@ -373,12 +380,12 @@ namespace etl
// To generate to header file, run this at the command line.
// Note: You will need Python and COG installed.
//
// python -m cogapp -d -e -ofsm.h -DHandlers=<n> fsm_generator.h
// python -m cogapp -d -e -ofsm.h -DHandlers=<n> fsm_generator.h
// Where <n> is the number of messages to support.
//
// e.g.
// To generate handlers for up to 16 events...
// python -m cogapp -d -e -ofsm.h -DHandlers=16 fsm_generator.h
// python -m cogapp -d -e -ofsm.h -DHandlers=16 fsm_generator.h
//
// See CreateFSM.bat
//***************************************************************************
@ -409,6 +416,8 @@ namespace etl
{
}
private:
etl::fsm_state_id_t process_event(etl::imessage_router& source, const etl::imessage& message)
{
etl::fsm_state_id_t new_state_id;
@ -461,6 +470,8 @@ namespace etl
{
}
private:
etl::fsm_state_id_t process_event(etl::imessage_router& source, const etl::imessage& message)
{
etl::fsm_state_id_t new_state_id;
@ -512,6 +523,8 @@ namespace etl
{
}
private:
etl::fsm_state_id_t process_event(etl::imessage_router& source, const etl::imessage& message)
{
etl::fsm_state_id_t new_state_id;
@ -562,6 +575,8 @@ namespace etl
{
}
private:
etl::fsm_state_id_t process_event(etl::imessage_router& source, const etl::imessage& message)
{
etl::fsm_state_id_t new_state_id;
@ -610,6 +625,8 @@ namespace etl
{
}
private:
etl::fsm_state_id_t process_event(etl::imessage_router& source, const etl::imessage& message)
{
etl::fsm_state_id_t new_state_id;
@ -657,6 +674,8 @@ namespace etl
{
}
private:
etl::fsm_state_id_t process_event(etl::imessage_router& source, const etl::imessage& message)
{
etl::fsm_state_id_t new_state_id;
@ -703,6 +722,8 @@ namespace etl
{
}
private:
etl::fsm_state_id_t process_event(etl::imessage_router& source, const etl::imessage& message)
{
etl::fsm_state_id_t new_state_id;
@ -748,6 +769,8 @@ namespace etl
{
}
private:
etl::fsm_state_id_t process_event(etl::imessage_router& source, const etl::imessage& message)
{
etl::fsm_state_id_t new_state_id;
@ -791,6 +814,8 @@ namespace etl
{
}
private:
etl::fsm_state_id_t process_event(etl::imessage_router& source, const etl::imessage& message)
{
etl::fsm_state_id_t new_state_id;
@ -833,6 +858,8 @@ namespace etl
{
}
private:
etl::fsm_state_id_t process_event(etl::imessage_router& source, const etl::imessage& message)
{
etl::fsm_state_id_t new_state_id;
@ -874,6 +901,8 @@ namespace etl
{
}
private:
etl::fsm_state_id_t process_event(etl::imessage_router& source, const etl::imessage& message)
{
etl::fsm_state_id_t new_state_id;
@ -914,6 +943,8 @@ namespace etl
{
}
private:
etl::fsm_state_id_t process_event(etl::imessage_router& source, const etl::imessage& message)
{
etl::fsm_state_id_t new_state_id;
@ -952,6 +983,8 @@ namespace etl
{
}
private:
etl::fsm_state_id_t process_event(etl::imessage_router& source, const etl::imessage& message)
{
etl::fsm_state_id_t new_state_id;
@ -989,6 +1022,8 @@ namespace etl
{
}
private:
etl::fsm_state_id_t process_event(etl::imessage_router& source, const etl::imessage& message)
{
etl::fsm_state_id_t new_state_id;
@ -1025,6 +1060,8 @@ namespace etl
{
}
private:
etl::fsm_state_id_t process_event(etl::imessage_router& source, const etl::imessage& message)
{
etl::fsm_state_id_t new_state_id;
@ -1060,6 +1097,8 @@ namespace etl
{
}
private:
etl::fsm_state_id_t process_event(etl::imessage_router& source, const etl::imessage& message)
{
etl::fsm_state_id_t new_state_id;
@ -1074,6 +1113,32 @@ namespace etl
return new_state_id;
}
};
//***************************************************************************
// Specialisation for 0 message types.
//***************************************************************************
template <typename TState, const etl::fsm_state_id_t STATE_ID_>
class fsm_state<TState, STATE_ID_, void, void, void, void, void, void, void, void, void, void, void, void, void, void, void, void> : public ifsm_state
{
public:
enum
{
STATE_ID = STATE_ID_
};
fsm_state()
: ifsm_state(STATE_ID)
{
}
private:
etl::fsm_state_id_t process_event(etl::imessage_router& source, const etl::imessage& message)
{
return static_cast<TState*>(this)->on_event_unknown(source, message);
}
};
}
#undef ETL_FILE

View File

@ -171,13 +171,13 @@ namespace etl
}
//*******************************************
inline fsm_state_id_t on_enter_state(etl::ifsm_state &state)
inline fsm_state_id_t on_enter_state(etl::ifsm_state& state)
{
return state.on_enter_state();
}
//*******************************************
inline void on_exit_state(etl::ifsm_state &state)
inline void on_exit_state(etl::ifsm_state& state)
{
state.on_exit_state();
}
@ -253,7 +253,8 @@ namespace etl
//*******************************************
void receive(const etl::imessage& message)
{
receive(etl::null_message_router(), message);
etl::null_message_router nmr;
receive(nmr, message);
}
//*******************************************
@ -424,6 +425,8 @@ namespace etl
cog.outl(" {")
cog.outl(" }")
cog.outl("")
cog.outl("private:")
cog.outl("")
cog.outl(" etl::fsm_state_id_t process_event(etl::imessage_router& source, const etl::imessage& message)")
cog.outl(" {")
cog.outl(" etl::fsm_state_id_t new_state_id;")
@ -488,6 +491,8 @@ namespace etl
cog.outl(" {")
cog.outl(" }")
cog.outl("")
cog.outl("private:")
cog.outl("")
cog.outl(" etl::fsm_state_id_t process_event(etl::imessage_router& source, const etl::imessage& message)")
cog.outl(" {")
cog.outl(" etl::fsm_state_id_t new_state_id;")
@ -507,6 +512,41 @@ namespace etl
cog.outl(" return new_state_id;")
cog.outl(" }")
cog.outl("};")
####################################
# Specialisation for zero messages.
####################################
cog.outl("")
cog.outl("//***************************************************************************")
cog.outl("// Specialisation for 0 message types.")
cog.outl("//***************************************************************************")
cog.outl("template <typename TState, const etl::fsm_state_id_t STATE_ID_>")
cog.out("class fsm_state<TState, STATE_ID_, ")
for t in range(1, int(Handlers)):
cog.out("void, ")
if t % 16 == 0:
cog.outl("")
cog.out(" ")
cog.outl("void> : public ifsm_state")
cog.outl("{")
cog.outl("public:")
cog.outl("")
cog.outl(" enum")
cog.outl(" {")
cog.outl(" STATE_ID = STATE_ID_")
cog.outl(" };")
cog.outl("")
cog.outl(" fsm_state()")
cog.outl(" : ifsm_state(STATE_ID)")
cog.outl(" {")
cog.outl(" }")
cog.outl("")
cog.outl("private:")
cog.outl("")
cog.outl(" etl::fsm_state_id_t process_event(etl::imessage_router& source, const etl::imessage& message)")
cog.outl(" {")
cog.outl(" return static_cast<TState*>(this)->on_event_unknown(source, message);")
cog.outl(" }")
cog.outl("};")
]]]*/
/*[[[end]]]*/
}

View File

@ -162,7 +162,8 @@ namespace etl
void receive(const etl::imessage& msg)
{
receive(etl::null_message_router(), msg);
etl::null_message_router nmr;
receive(nmr, msg);
}
void receive(etl::imessage_router& source, const etl::imessage& msg)
@ -219,7 +220,8 @@ namespace etl
void receive(const etl::imessage& msg)
{
receive(etl::null_message_router(), msg);
etl::null_message_router nmr;
receive(nmr, msg);
}
void receive(etl::imessage_router& source, const etl::imessage& msg)
@ -275,7 +277,8 @@ namespace etl
void receive(const etl::imessage& msg)
{
receive(etl::null_message_router(), msg);
etl::null_message_router nmr;
receive(nmr, msg);
}
void receive(etl::imessage_router& source, const etl::imessage& msg)
@ -330,7 +333,8 @@ namespace etl
void receive(const etl::imessage& msg)
{
receive(etl::null_message_router(), msg);
etl::null_message_router nmr;
receive(nmr, msg);
}
void receive(etl::imessage_router& source, const etl::imessage& msg)
@ -383,7 +387,8 @@ namespace etl
void receive(const etl::imessage& msg)
{
receive(etl::null_message_router(), msg);
etl::null_message_router nmr;
receive(nmr, msg);
}
void receive(etl::imessage_router& source, const etl::imessage& msg)
@ -435,7 +440,8 @@ namespace etl
void receive(const etl::imessage& msg)
{
receive(etl::null_message_router(), msg);
etl::null_message_router nmr;
receive(nmr, msg);
}
void receive(etl::imessage_router& source, const etl::imessage& msg)
@ -486,7 +492,8 @@ namespace etl
void receive(const etl::imessage& msg)
{
receive(etl::null_message_router(), msg);
etl::null_message_router nmr;
receive(nmr, msg);
}
void receive(etl::imessage_router& source, const etl::imessage& msg)
@ -536,7 +543,8 @@ namespace etl
void receive(const etl::imessage& msg)
{
receive(etl::null_message_router(), msg);
etl::null_message_router nmr;
receive(nmr, msg);
}
void receive(etl::imessage_router& source, const etl::imessage& msg)
@ -584,7 +592,8 @@ namespace etl
void receive(const etl::imessage& msg)
{
receive(etl::null_message_router(), msg);
etl::null_message_router nmr;
receive(nmr, msg);
}
void receive(etl::imessage_router& source, const etl::imessage& msg)
@ -631,7 +640,8 @@ namespace etl
void receive(const etl::imessage& msg)
{
receive(etl::null_message_router(), msg);
etl::null_message_router nmr;
receive(nmr, msg);
}
void receive(etl::imessage_router& source, const etl::imessage& msg)
@ -676,7 +686,8 @@ namespace etl
void receive(const etl::imessage& msg)
{
receive(etl::null_message_router(), msg);
etl::null_message_router nmr;
receive(nmr, msg);
}
void receive(etl::imessage_router& source, const etl::imessage& msg)
@ -720,7 +731,8 @@ namespace etl
void receive(const etl::imessage& msg)
{
receive(etl::null_message_router(), msg);
etl::null_message_router nmr;
receive(nmr, msg);
}
void receive(etl::imessage_router& source, const etl::imessage& msg)
@ -762,7 +774,8 @@ namespace etl
void receive(const etl::imessage& msg)
{
receive(etl::null_message_router(), msg);
etl::null_message_router nmr;
receive(nmr, msg);
}
void receive(etl::imessage_router& source, const etl::imessage& msg)
@ -803,7 +816,8 @@ namespace etl
void receive(const etl::imessage& msg)
{
receive(etl::null_message_router(), msg);
etl::null_message_router nmr;
receive(nmr, msg);
}
void receive(etl::imessage_router& source, const etl::imessage& msg)
@ -843,7 +857,8 @@ namespace etl
void receive(const etl::imessage& msg)
{
receive(etl::null_message_router(), msg);
etl::null_message_router nmr;
receive(nmr, msg);
}
void receive(etl::imessage_router& source, const etl::imessage& msg)
@ -882,7 +897,8 @@ namespace etl
void receive(const etl::imessage& msg)
{
receive(etl::null_message_router(), msg);
etl::null_message_router nmr;
receive(nmr, msg);
}
void receive(etl::imessage_router& source, const etl::imessage& msg)

View File

@ -172,7 +172,8 @@ namespace etl
cog.outl("")
cog.outl(" void receive(const etl::imessage& msg)")
cog.outl(" {")
cog.outl(" receive(etl::null_message_router(), msg);")
cog.outl(" etl::null_message_router nmr;")
cog.outl(" receive(nmr, msg);")
cog.outl(" }")
cog.outl("")
cog.outl(" void receive(etl::imessage_router& source, const etl::imessage& msg)")
@ -245,7 +246,8 @@ namespace etl
cog.outl("")
cog.outl(" void receive(const etl::imessage& msg)")
cog.outl(" {")
cog.outl(" receive(etl::null_message_router(), msg);")
cog.outl(" etl::null_message_router nmr;")
cog.outl(" receive(nmr, msg);")
cog.outl(" }")
cog.outl("")
cog.outl(" void receive(etl::imessage_router& source, const etl::imessage& msg)")

View File

@ -103,6 +103,7 @@ namespace
IDLE,
RUNNING,
WINDING_DOWN,
LOCKED,
NUMBER_OF_STATES
};
@ -110,6 +111,7 @@ namespace
ETL_ENUM_TYPE(IDLE, "Idle")
ETL_ENUM_TYPE(RUNNING, "Running")
ETL_ENUM_TYPE(WINDING_DOWN, "Winding Down")
ETL_ENUM_TYPE(LOCKED, "Locked")
ETL_END_ENUM_TYPE
};
@ -195,9 +197,11 @@ namespace
}
//***********************************
void on_enter_state()
etl::fsm_state_id_t on_enter_state()
{
common.TurnRunningLampOff();
return StateId::LOCKED;
}
Common& common;
@ -246,9 +250,12 @@ namespace
return STATE_ID;
}
void on_enter_state()
//***********************************
etl::fsm_state_id_t on_enter_state()
{
common.TurnRunningLampOn();
return STATE_ID;
}
Common& common;
@ -284,6 +291,29 @@ namespace
Common& common;
};
//***********************************
// The locked state.
//***********************************
class Locked : public etl::fsm_state<Locked, StateId::LOCKED>
{
public:
//***********************************
Locked(Common& common)
: common(common)
{
}
//***********************************
etl::fsm_state_id_t on_event_unknown(etl::imessage_router& source, const etl::imessage& event)
{
++common.unknownCount;
return STATE_ID;
}
Common& common;
};
//***********************************
// The motor control FSM.
//***********************************
@ -294,7 +324,8 @@ namespace
MotorControl()
: idle(common),
running(common),
windingDown(common)
windingDown(common),
locked(common)
{
set_states(stateList, etl::size(stateList));
}
@ -307,10 +338,11 @@ namespace
Idle idle;
Running running;
WindingDown windingDown;
Locked locked;
etl::ifsm_state* stateList[StateId::NUMBER_OF_STATES] =
{
&idle, &running, &windingDown
&idle, &running, &windingDown, &locked
};
};
@ -321,6 +353,8 @@ namespace
//*************************************************************************
TEST(test_fsm)
{
etl::null_message_router nmr;
motorControl.reset();
motorControl.common.ClearStatistics();
@ -344,9 +378,9 @@ namespace
CHECK_EQUAL(0, motorControl.common.unknownCount);
// Send unhandled events.
motorControl.receive(etl::null_message_router(), Stop());
motorControl.receive(etl::null_message_router(), Stopped());
motorControl.receive(etl::null_message_router(), SetSpeed(10));
motorControl.receive(nmr, Stop());
motorControl.receive(nmr, Stopped());
motorControl.receive(nmr, SetSpeed(10));
CHECK_EQUAL(StateId::IDLE, motorControl.get_state_id());
CHECK_EQUAL(StateId::IDLE, motorControl.get_state().get_state_id());
@ -360,7 +394,7 @@ namespace
CHECK_EQUAL(3, motorControl.common.unknownCount);
// Send Start event.
motorControl.receive(etl::null_message_router(), Start());
motorControl.receive(nmr, Start());
// Now in Running state.
@ -376,8 +410,8 @@ namespace
CHECK_EQUAL(3, motorControl.common.unknownCount);
// Send unhandled events.
motorControl.receive(etl::null_message_router(), Start());
motorControl.receive(etl::null_message_router(), Stopped());
motorControl.receive(nmr, Start());
motorControl.receive(nmr, Stopped());
CHECK_EQUAL(StateId::RUNNING, int(motorControl.get_state_id()));
CHECK_EQUAL(StateId::RUNNING, int(motorControl.get_state().get_state_id()));
@ -391,7 +425,7 @@ namespace
CHECK_EQUAL(5, motorControl.common.unknownCount);
// Send SetSpeed event.
motorControl.receive(etl::null_message_router(), SetSpeed(100));
motorControl.receive(nmr, SetSpeed(100));
// Still in Running state.
@ -407,7 +441,7 @@ namespace
CHECK_EQUAL(5, motorControl.common.unknownCount);
// Send Stop event.
motorControl.receive(etl::null_message_router(), Stop());
motorControl.receive(nmr, Stop());
// Now in WindingDown state.
@ -423,9 +457,9 @@ namespace
CHECK_EQUAL(5, motorControl.common.unknownCount);
// Send unhandled events.
motorControl.receive(etl::null_message_router(), Start());
motorControl.receive(etl::null_message_router(), Stop());
motorControl.receive(etl::null_message_router(), SetSpeed(100));
motorControl.receive(nmr, Start());
motorControl.receive(nmr, Stop());
motorControl.receive(nmr, SetSpeed(100));
CHECK_EQUAL(StateId::WINDING_DOWN, int(motorControl.get_state_id()));
CHECK_EQUAL(StateId::WINDING_DOWN, int(motorControl.get_state().get_state_id()));
@ -439,12 +473,11 @@ namespace
CHECK_EQUAL(8, motorControl.common.unknownCount);
// Send Stopped event.
motorControl.receive(etl::null_message_router(), Stopped());
motorControl.receive(nmr, Stopped());
// Now in Idle state.
CHECK_EQUAL(StateId::IDLE, int(motorControl.get_state_id()));
CHECK_EQUAL(StateId::IDLE, int(motorControl.get_state().get_state_id()));
// Now in Locked state via Idle state.
CHECK_EQUAL(StateId::LOCKED, int(motorControl.get_state_id()));
CHECK_EQUAL(StateId::LOCKED, int(motorControl.get_state().get_state_id()));
CHECK_EQUAL(false, motorControl.common.isLampOn);
CHECK_EQUAL(1, motorControl.common.setSpeedCount);
@ -458,6 +491,8 @@ namespace
//*************************************************************************
TEST(test_fsm_emergency_stop)
{
etl::null_message_router nmr;
motorControl.reset();
motorControl.common.ClearStatistics();
@ -470,7 +505,7 @@ namespace
// Now in Idle state.
// Send Start event.
motorControl.receive(etl::null_message_router(), Start());
motorControl.receive(nmr, Start());
// Now in Running state.
@ -486,12 +521,11 @@ namespace
CHECK_EQUAL(0, motorControl.common.unknownCount);
// Send emergency Stop event.
motorControl.receive(etl::null_message_router(), Stop(true));
motorControl.receive(nmr, Stop(true));
// Now in Idle state.
CHECK_EQUAL(StateId::IDLE, int(motorControl.get_state_id()));
CHECK_EQUAL(StateId::IDLE, int(motorControl.get_state().get_state_id()));
// Now in Locked state via Idle state.
CHECK_EQUAL(StateId::LOCKED, int(motorControl.get_state_id()));
CHECK_EQUAL(StateId::LOCKED, int(motorControl.get_state().get_state_id()));
CHECK_EQUAL(false, motorControl.common.isLampOn);
CHECK_EQUAL(0, motorControl.common.setSpeedCount);