mirror of
https://github.com/ETLCPP/etl.git
synced 2026-10-28 18:07:10 +08:00
Added transition_to member function to change state
Pulled out the core state change code to process_state_change
This commit is contained in:
parent
2ae0a422cd
commit
2de4057021
@ -508,30 +508,7 @@ namespace etl
|
||||
{
|
||||
etl::fsm_state_id_t next_state_id = p_state->process_event(message);
|
||||
|
||||
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];
|
||||
|
||||
do
|
||||
{
|
||||
p_state->on_exit_state();
|
||||
p_state = p_next_state;
|
||||
|
||||
next_state_id = p_state->on_enter_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));
|
||||
p_next_state = state_list[next_state_id];
|
||||
}
|
||||
} while (p_next_state != p_state); // Have we changed state again?
|
||||
}
|
||||
else if (is_self_transition(next_state_id))
|
||||
{
|
||||
p_state->on_exit_state();
|
||||
p_state->on_enter_state();
|
||||
}
|
||||
process_state_change(next_state_id);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -539,6 +516,21 @@ namespace etl
|
||||
}
|
||||
}
|
||||
|
||||
//*******************************************
|
||||
/// Invoke a state transition.
|
||||
//*******************************************
|
||||
etl::fsm_state_id_t transition_to(etl::fsm_state_id_t new_state_id)
|
||||
{
|
||||
if (is_started())
|
||||
{
|
||||
return process_state_change(new_state_id);
|
||||
}
|
||||
else
|
||||
{
|
||||
return ifsm_state::No_State_Change;
|
||||
}
|
||||
}
|
||||
|
||||
using imessage_router::accepts;
|
||||
|
||||
//*******************************************
|
||||
@ -633,6 +625,39 @@ namespace etl
|
||||
return (next_state_id == ifsm_state::Self_Transition);
|
||||
}
|
||||
|
||||
//*******************************************
|
||||
/// Core function to process a state change.
|
||||
//*******************************************
|
||||
virtual etl::fsm_state_id_t process_state_change(etl::fsm_state_id_t next_state_id)
|
||||
{
|
||||
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];
|
||||
|
||||
do
|
||||
{
|
||||
p_state->on_exit_state();
|
||||
p_state = p_next_state;
|
||||
|
||||
next_state_id = p_state->on_enter_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));
|
||||
p_next_state = state_list[next_state_id];
|
||||
}
|
||||
} while (p_next_state != p_state); // Have we changed state again?
|
||||
}
|
||||
else if (is_self_transition(next_state_id))
|
||||
{
|
||||
p_state->on_exit_state();
|
||||
p_state->on_enter_state();
|
||||
}
|
||||
|
||||
return p_state->get_state_id();
|
||||
}
|
||||
|
||||
etl::ifsm_state* p_state; ///< A pointer to the current state.
|
||||
etl::ifsm_state** state_list; ///< The list of added states.
|
||||
etl::fsm_state_id_t number_of_states; ///< The number of states.
|
||||
|
||||
@ -91,42 +91,6 @@ namespace etl
|
||||
p_state = ETL_NULLPTR;
|
||||
}
|
||||
|
||||
using fsm::receive;
|
||||
|
||||
//*******************************************
|
||||
/// Top level message handler for the HFSM.
|
||||
//*******************************************
|
||||
void receive(const etl::imessage& message) ETL_OVERRIDE
|
||||
{
|
||||
if (is_started())
|
||||
{
|
||||
etl::fsm_state_id_t next_state_id = p_state->process_event(message);
|
||||
|
||||
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);
|
||||
next_state_id = do_enters(p_root, p_next_state, true);
|
||||
|
||||
ETL_ASSERT_OR_RETURN(next_state_id < number_of_states, ETL_ERROR(etl::fsm_state_id_exception));
|
||||
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
|
||||
{
|
||||
ETL_ASSERT_FAIL(ETL_ERROR(etl::fsm_not_started));
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
//*******************************************
|
||||
@ -247,6 +211,33 @@ namespace etl
|
||||
p_current = p_current->p_parent;
|
||||
}
|
||||
}
|
||||
|
||||
//*******************************************
|
||||
/// Core function to process a state change.
|
||||
//*******************************************
|
||||
etl::fsm_state_id_t process_state_change(etl::fsm_state_id_t next_state_id) ETL_OVERRIDE
|
||||
{
|
||||
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);
|
||||
next_state_id = do_enters(p_root, p_next_state, true);
|
||||
|
||||
ETL_ASSERT_OR_RETURN(next_state_id < number_of_states, ETL_ERROR(etl::fsm_state_id_exception));
|
||||
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();
|
||||
}
|
||||
|
||||
return p_state->get_state_id();
|
||||
}
|
||||
};
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -730,5 +730,55 @@ namespace
|
||||
CHECK_EQUAL(StateId::Locked, int(motorControl.get_state_id()));
|
||||
CHECK_EQUAL(StateId::Locked, int(motorControl.get_state().get_state_id()));
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
TEST(test_fsm_force_state_changes)
|
||||
{
|
||||
MotorControl motorControl;
|
||||
|
||||
etl::fsm_state_pack<Idle, Running, WindingDown, Locked> statePack;
|
||||
|
||||
motorControl.Initialise(statePack);
|
||||
motorControl.reset();
|
||||
motorControl.ClearStatistics();
|
||||
|
||||
// Start the FSM.
|
||||
motorControl.start(false);
|
||||
CHECK(motorControl.is_started());
|
||||
|
||||
// Now in Idle state.
|
||||
CHECK_EQUAL(StateId::Idle, int(motorControl.get_state_id()));
|
||||
CHECK_EQUAL(StateId::Idle, int(motorControl.get_state().get_state_id()));
|
||||
|
||||
auto id1 = motorControl.transition_to(StateId::Running);
|
||||
|
||||
// Now in Running state.
|
||||
CHECK_EQUAL(StateId::Running, int(id1));
|
||||
CHECK_EQUAL(StateId::Running, int(motorControl.get_state_id()));
|
||||
CHECK_EQUAL(StateId::Running, int(motorControl.get_state().get_state_id()));
|
||||
|
||||
auto id2 = motorControl.transition_to(StateId::Idle);
|
||||
|
||||
// Now in Locked state.
|
||||
CHECK_EQUAL(StateId::Locked, int(id2));
|
||||
CHECK_EQUAL(StateId::Locked, int(motorControl.get_state_id()));
|
||||
CHECK_EQUAL(StateId::Locked, int(motorControl.get_state().get_state_id()));
|
||||
|
||||
auto id3 = motorControl.transition_to(StateId::Winding_Down);
|
||||
|
||||
// Now in Locked state.
|
||||
CHECK_EQUAL(StateId::Winding_Down, int(id3));
|
||||
CHECK_EQUAL(StateId::Winding_Down, int(motorControl.get_state_id()));
|
||||
CHECK_EQUAL(StateId::Winding_Down, int(motorControl.get_state().get_state_id()));
|
||||
|
||||
// Send a normal event message to make sure the FSM is still working.
|
||||
|
||||
// Now send a Stopped event message.
|
||||
motorControl.receive(Stopped());
|
||||
|
||||
// Now in Idle state.
|
||||
CHECK_EQUAL(StateId::Locked, int(motorControl.get_state_id()));
|
||||
CHECK_EQUAL(StateId::Locked, int(motorControl.get_state().get_state_id()));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@ -192,6 +192,14 @@ namespace
|
||||
ClearStatistics();
|
||||
}
|
||||
|
||||
//***********************************
|
||||
template <typename... TStates>
|
||||
void Initialise(etl::fsm_state_pack<TStates...>& state_pack)
|
||||
{
|
||||
set_states(state_pack);
|
||||
ClearStatistics();
|
||||
}
|
||||
|
||||
//***********************************
|
||||
void ClearStatistics()
|
||||
{
|
||||
@ -1028,5 +1036,56 @@ namespace
|
||||
|
||||
CHECK_EQUAL(1, motorControl.selfTransitionCount);
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
TEST(test_fsm_force_state_changes)
|
||||
{
|
||||
MotorControl motorControl;
|
||||
|
||||
etl::fsm_state_pack<Idle, Running, WindingUp, WindingDown, AtSpeed> statePack;
|
||||
|
||||
motorControl.Initialise(statePack);
|
||||
motorControl.reset();
|
||||
motorControl.ClearStatistics();
|
||||
|
||||
// Start the FSM.
|
||||
motorControl.start(false);
|
||||
CHECK(motorControl.is_started());
|
||||
|
||||
// Now in Idle state.
|
||||
CHECK_EQUAL(StateId::Idle, int(motorControl.get_state_id()));
|
||||
CHECK_EQUAL(StateId::Idle, int(motorControl.get_state().get_state_id()));
|
||||
|
||||
auto id1 = motorControl.transition_to(StateId::Winding_Up);
|
||||
|
||||
// Now in Winding_Up state.
|
||||
CHECK_EQUAL(StateId::Winding_Up, int(id1));
|
||||
CHECK_EQUAL(StateId::Winding_Up, int(motorControl.get_state_id()));
|
||||
CHECK_EQUAL(StateId::Winding_Up, int(motorControl.get_state().get_state_id()));
|
||||
|
||||
auto id2 = motorControl.transition_to(StateId::At_Speed);
|
||||
|
||||
// Now in At_Speed state.
|
||||
CHECK_EQUAL(StateId::At_Speed, int(id2));
|
||||
CHECK_EQUAL(StateId::At_Speed, int(motorControl.get_state_id()));
|
||||
CHECK_EQUAL(StateId::At_Speed, int(motorControl.get_state().get_state_id()));
|
||||
|
||||
// Send some normal event messages to make sure the HFSM is still working.
|
||||
|
||||
// Now send a Stop event message.
|
||||
motorControl.receive(Stop());
|
||||
|
||||
CHECK_EQUAL(StateId::Winding_Down, int(motorControl.get_state_id()));
|
||||
CHECK_EQUAL(StateId::Winding_Down, int(motorControl.get_state().get_state_id()));
|
||||
|
||||
// Now send a Stopped event message.
|
||||
motorControl.receive(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()));
|
||||
|
||||
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user