Merge branch 'pull-request/#672-Added-HFSM-specific-start()-and-reset()' into development

This commit is contained in:
John Wellbelove 2023-02-19 13:56:10 +00:00
commit c5432e42ff
4 changed files with 168 additions and 7 deletions

View File

@ -346,7 +346,7 @@ namespace etl
/// Subsequent calls will do nothing.
///\param call_on_enter_state If true will call on_enter_state() for the first state. Default = true.
//*******************************************
void start(bool call_on_enter_state = true)
virtual void start(bool call_on_enter_state = true)
{
// Can only be started once.
if (p_state == ETL_NULLPTR)
@ -451,7 +451,7 @@ namespace etl
/// Reset the FSM to pre-started state.
///\param call_on_exit_state If true will call on_exit_state() for the current state. Default = false.
//*******************************************
void reset(bool call_on_exit_state = false)
virtual void reset(bool call_on_exit_state = false)
{
if ((p_state != ETL_NULLPTR) && call_on_exit_state)
{

View File

@ -372,7 +372,7 @@ namespace etl
/// Subsequent calls will do nothing.
///\param call_on_enter_state If true will call on_enter_state() for the first state. Default = true.
//*******************************************
void start(bool call_on_enter_state = true)
virtual void start(bool call_on_enter_state = true)
{
// Can only be started once.
if (p_state == ETL_NULLPTR)
@ -477,7 +477,7 @@ namespace etl
/// Reset the FSM to pre-started state.
///\param call_on_exit_state If true will call on_exit_state() for the current state. Default = false.
//*******************************************
void reset(bool call_on_exit_state = false)
virtual void reset(bool call_on_exit_state = false)
{
if ((p_state != ETL_NULLPTR) && call_on_exit_state)
{

View File

@ -50,6 +50,41 @@ namespace etl
{
}
//*******************************************
/// Starts the HFSM.
/// Can only be called once.
/// Subsequent calls will do nothing.
///\param call_on_enter_state If true will call on_enter_state() for the first state. Default = true.
//*******************************************
void start(bool call_on_enter_state = true) ETL_OVERRIDE
{
// Can only be started once.
if (p_state == ETL_NULLPTR)
{
p_state = state_list[0];
ETL_ASSERT(p_state != ETL_NULLPTR, ETL_ERROR(etl::fsm_null_state_exception));
if (call_on_enter_state)
{
do_enters(ETL_NULLPTR, p_state, true);
}
}
}
//*******************************************
/// Reset the HFSM to pre-started state.
///\param call_on_exit_state If true will call on_exit_state() for the current state. Default = false.
//*******************************************
virtual void reset(bool call_on_exit_state = false) ETL_OVERRIDE
{
if ((p_state != ETL_NULLPTR) && call_on_exit_state)
{
do_exits(ETL_NULLPTR, p_state);
}
p_state = ETL_NULLPTR;
}
using fsm::receive;
//*******************************************

View File

@ -33,6 +33,7 @@ SOFTWARE.
#include "etl/container.h"
#include "etl/packet.h"
#include "etl/queue.h"
#include "etl/circular_buffer.h"
#include <iostream>
@ -196,6 +197,9 @@ namespace
stoppedCount = 0;
isLampOn = false;
speed = 0;
stateEnterHistory.clear();
stateExitHistory.clear();
}
//***********************************
@ -238,6 +242,10 @@ namespace
int stoppedCount;
bool isLampOn;
int speed;
// A circular buffer is used so that data overflows won't assert in tests which don't use this data
etl::circular_buffer<StateId::enum_type, 10UL> stateEnterHistory{};
etl::circular_buffer<StateId::enum_type, 10UL> stateExitHistory{};
};
//***********************************
@ -271,9 +279,19 @@ namespace
//***********************************
etl::fsm_state_id_t on_enter_state()
{
get_fsm_context().TurnRunningLampOff();
auto& context = get_fsm_context();
context.stateEnterHistory.push(static_cast<StateId::enum_type>(get_state_id()));
context.TurnRunningLampOff();
return No_State_Change;
}
//***********************************
void on_exit_state()
{
auto& context = get_fsm_context();
context.stateExitHistory.push(static_cast<StateId::enum_type>(get_state_id()));
}
};
//***********************************
@ -301,10 +319,19 @@ namespace
//***********************************
etl::fsm_state_id_t on_enter_state()
{
get_fsm_context().TurnRunningLampOn();
auto& context = get_fsm_context();
context.stateEnterHistory.push(static_cast<StateId::enum_type>(get_state_id()));
context.TurnRunningLampOn();
return No_State_Change;
}
//***********************************
void on_exit_state()
{
auto& context = get_fsm_context();
context.stateExitHistory.push(static_cast<StateId::enum_type>(get_state_id()));
}
};
//***********************************
@ -336,11 +363,21 @@ namespace
return No_State_Change;
}
//***********************************
etl::fsm_state_id_t on_enter_state()
{
++get_fsm_context().windUpStartCount;
auto& context = get_fsm_context();
context.stateEnterHistory.push(static_cast<StateId::enum_type>(get_state_id()));
++context.windUpStartCount;
return No_State_Change;
}
//***********************************
void on_exit_state()
{
auto& context = get_fsm_context();
context.stateExitHistory.push(static_cast<StateId::enum_type>(get_state_id()));
}
};
//***********************************
@ -371,6 +408,13 @@ namespace
++get_fsm_context().unknownCount;
return No_State_Change;
}
//***********************************
void on_exit_state()
{
auto& context = get_fsm_context();
context.stateExitHistory.push(static_cast<StateId::enum_type>(get_state_id()));
}
};
//***********************************
@ -852,5 +896,87 @@ namespace
CHECK_THROW(mc.set_states(stateList, StateId::Number_Of_States), etl::fsm_state_list_order_exception);
}
//*************************************************************************
TEST(test_hfsm_start_enters)
{
MotorControl mc;
motorControl.Initialise(stateList, std::size(stateList));
motorControl.reset();
motorControl.ClearStatistics();
CHECK(!motorControl.is_started());
// Start the HFSM (with enters)
motorControl.start(true);
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()));
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.windUpCompleteCount);
CHECK_EQUAL(0, motorControl.windUpStartCount);
CHECK_EQUAL(0, motorControl.stoppedCount);
CHECK_EQUAL(0, motorControl.unknownCount);
etl::array<StateId::enum_type, 1UL> expectedEnters{StateId::Idle};
CHECK_EQUAL(expectedEnters.size(), motorControl.stateEnterHistory.size());
// No enters should have been performed
CHECK(motorControl.stateExitHistory.empty());
bool entersCorrect = std::equal(motorControl.stateEnterHistory.begin(), motorControl.stateEnterHistory.end(), expectedEnters.begin());
CHECK(entersCorrect);
}
//*************************************************************************
TEST(test_hfsm_reset_exits)
{
MotorControl mc;
motorControl.Initialise(stateList, std::size(stateList));
motorControl.reset();
motorControl.ClearStatistics();
CHECK(!motorControl.is_started());
// Start the HFSM (with enters)
motorControl.start(false);
CHECK(motorControl.is_started());
// Now in Idle state.
// Send Start event.
motorControl.receive(Start());
// Send Timeout event
motorControl.receive(Timeout());
// Send SetSpeed event.
motorControl.receive(SetSpeed(100));
CHECK_EQUAL(StateId::At_Speed, int(motorControl.get_state_id()));
CHECK_EQUAL(StateId::At_Speed, int(motorControl.get_state().get_state_id()));
// Clean statistics before reset for clean results
motorControl.ClearStatistics();
// Reset HFSM (with exits)
motorControl.reset(true);
etl::array<StateId::enum_type, 2UL> expectedExits{StateId::At_Speed, StateId::Running};
CHECK_EQUAL(expectedExits.size(), motorControl.stateExitHistory.size());
// No enters should have been performed
CHECK(motorControl.stateEnterHistory.empty());
bool exitsCorrect = std::equal(motorControl.stateExitHistory.begin(), motorControl.stateExitHistory.end(), expectedExits.begin());
CHECK(exitsCorrect);
}
};
}