diff --git a/include/etl/state_chart.h b/include/etl/state_chart.h index a126db0f..18ead85a 100644 --- a/include/etl/state_chart.h +++ b/include/etl/state_chart.h @@ -35,6 +35,7 @@ SOFTWARE. #include "etl/nullptr.h" #include "etl/array.h" #include "etl/array_view.h" +#include "etl/type_traits.h" namespace etl { @@ -74,11 +75,332 @@ namespace etl state_id_t current_state_id; ///< The current state id. }; + //*************************************************************************** + /// Simple Finite State Machine + /// Data parameter for events. + //*************************************************************************** + template + class state_chart : public istate_chart + { + public: + + typedef TDataParameter data_parameter_type; + typedef typename etl::types::type data_type; + + //************************************************************************* + /// Transition definition + //************************************************************************* + struct transition + { + ETL_CONSTEXPR transition(const state_id_t current_state_id_, + const event_id_t event_id_, + const state_id_t next_state_id_, + void (TObject::* const action_)(data_parameter_type) = ETL_NULLPTR, + bool (TObject::* const guard_)() = ETL_NULLPTR) + : from_any_state(false), + current_state_id(current_state_id_), + event_id(event_id_), + next_state_id(next_state_id_), + action(action_), + guard(guard_) + { + } + + ETL_CONSTEXPR transition(const event_id_t event_id_, + const state_id_t next_state_id_, + void (TObject::* const action_)(data_parameter_type) = ETL_NULLPTR, + bool (TObject::* const guard_)() = ETL_NULLPTR) + : from_any_state(true), + current_state_id(0), + event_id(event_id_), + next_state_id(next_state_id_), + action(action_), + guard(guard_) + { + } + + const bool from_any_state; + const state_id_t current_state_id; + const event_id_t event_id; + const state_id_t next_state_id; + void (TObject::* const action)(data_parameter_type); + bool (TObject::* const guard)(); + }; + + //************************************************************************* + /// State definition + //************************************************************************* + struct state + { + ETL_CONSTEXPR state(const state_id_t state_id_, + void (TObject::* const on_entry_)() = ETL_NULLPTR, + void (TObject::* const on_exit_)() = ETL_NULLPTR) + : state_id(state_id_), + on_entry(on_entry_), + on_exit(on_exit_) + { + } + + state_id_t state_id; + void (TObject::* const on_entry)(); + void (TObject::* const on_exit)(); + }; + + //************************************************************************* + /// Constructor. + /// \param object_ A reference to the implementation object. + /// \param transition_table_begin_ The start of the table of transitions. + /// \param transition_table_end_ The end of the table of transitions. + /// \param state_id_ The initial state id. + //************************************************************************* + ETL_CONSTEXPR state_chart(TObject& object_, + const transition* transition_table_begin_, + const transition* transition_table_end_, + const state_id_t state_id_) + : istate_chart(state_id_), + object(object_), + transition_table(transition_table_begin_, transition_table_end_), + started(false) + { + } + + //************************************************************************* + /// Constructor. + /// \param object_ A reference to the implementation object. + /// \param transition_table_begin_ The start of the table of transitions. + /// \param transition_table_end_ The end of the table of transitions. + /// \param state_table_begin_ The start of the state table. + /// \param state_table_end_ The end of the state table. + /// \param state_id_ The initial state id. + //************************************************************************* + ETL_CONSTEXPR state_chart(TObject& object_, + const transition* transition_table_begin_, + const transition* transition_table_end_, + const state* state_table_begin_, + const state* state_table_end_, + const state_id_t state_id_) + : istate_chart(state_id_), + object(object_), + transition_table(transition_table_begin_, transition_table_end_), + state_table(state_table_begin_, state_table_end_), + started(false) + { + } + + //************************************************************************* + /// Sets the transition table. + /// \param state_table_begin_ The start of the state table. + /// \param state_table_end_ The end of the state table. + //************************************************************************* + void set_transition_table(const transition* transition_table_begin_, + const transition* transition_table_end_) + { + transition_table.assign(transition_table_begin_, transition_table_end_); + } + + //************************************************************************* + /// Sets the state table. + /// \param state_table_begin_ The start of the state table. + /// \param state_table_end_ The end of the state table. + //************************************************************************* + void set_state_table(const state* state_table_begin_, + const state* state_table_end_) + { + state_table.assign(state_table_begin_, state_table_end_); + } + + //************************************************************************* + /// Gets a reference to the implementation object. + /// \return Reference to the implementation object. + //************************************************************************* + TObject& get_object() + { + return object; + } + + //************************************************************************* + /// Gets a const reference to the implementation object. + /// \return Const reference to the implementation object. + //************************************************************************* + const TObject& get_object() const + { + return object; + } + + //************************************************************************* + /// Gets the current state id. + /// \return The current state id. + //************************************************************************* + const state* find_state(state_id_t state_id) + { + if (state_table.empty()) + { + return state_table.end(); + } + else + { + return etl::find_if(state_table.begin(), + state_table.end(), + is_state(state_id)); + } + } + + //************************************************************************* + /// + //************************************************************************* + virtual void start(const bool on_entry_initial = true) ETL_OVERRIDE + { + 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 != ETL_NULLPTR)) + { + (object.*(s->on_entry))(); + } + } + + started = true; + } + } + + //************************************************************************* + /// Processes the specified event. + /// The state machine will action the first item in the transition table + /// that satisfies the conditions for executing the action. + /// \param event_id The id of the event to process. + //************************************************************************* + virtual void process_event(const event_id_t event_id) ETL_OVERRIDE + { + process_event(event_id, data_type()); + } + + //************************************************************************* + /// Processes the specified event. + /// The state machine will action the first item in the transition table + /// that satisfies the conditions for executing the action. + /// \param event_id The id of the event to process. + /// \param data The data to pass to the action. + //************************************************************************* + void process_event(const event_id_t event_id, data_parameter_type data) + { + if (started) + { + const transition* t = transition_table.begin(); + + // Keep looping until we execute a transition or reach the end of the table. + while (t != transition_table.end()) + { + // Scan the transition table from the latest position. + t = etl::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 transition? + if ((t->guard == ETL_NULLPTR) || ((object.*t->guard)())) + { + // Shall we execute the action? + if (t->action != ETL_NULLPTR) + { + (object.*t->action)(data); + } + + // 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 != ETL_NULLPTR)) + { + (object.*(s->on_exit))(); + } + + current_state_id = t->next_state_id; + + // See if we have a state item for the new state. + s = find_state(current_state_id); + + // If the new state has an 'on_entry' then call it. + if ((s != state_table.end()) && (s->on_entry != ETL_NULLPTR)) + { + (object.*(s->on_entry))(); + } + } + + t = transition_table.end(); + } + else + { + // Start the search from the next item in the table. + ++t; + } + } + } + } + } + + private: + + //************************************************************************* + struct is_transition + { + is_transition(event_id_t event_id_, state_id_t state_id_) + : event_id(event_id_), + state_id(state_id_) + { + } + + bool operator()(const transition& t) const + { + return (t.event_id == event_id) && (t.from_any_state || (t.current_state_id == state_id)); + } + + const event_id_t event_id; + const state_id_t state_id; + }; + + //************************************************************************* + struct is_state + { + is_state(state_id_t state_id_) + : state_id(state_id_) + { + } + + bool operator()(const state& s) const + { + return (s.state_id == state_id); + } + + const state_id_t state_id; + }; + + // Disabled + state_chart(const state_chart&) ETL_DELETE; + state_chart& operator =(const state_chart&) ETL_DELETE; + + 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. + }; + //*************************************************************************** /// Simple Finite State Machine //*************************************************************************** template - class state_chart : public istate_chart + class state_chart : public istate_chart { public: @@ -243,7 +565,7 @@ namespace etl //************************************************************************* /// //************************************************************************* - virtual void start(const bool on_entry_initial = true) + virtual void start(const bool on_entry_initial = true) ETL_OVERRIDE { if (!started) { @@ -269,7 +591,7 @@ namespace etl /// that satisfies the conditions for executing the action. /// \param event_id The id of the event to process. //************************************************************************* - virtual void process_event(const event_id_t event_id) + virtual void process_event(const event_id_t event_id) ETL_OVERRIDE { if (started) { diff --git a/test/test_state_chart_with_data_parameter.cpp b/test/test_state_chart_with_data_parameter.cpp new file mode 100644 index 00000000..c90f85b4 --- /dev/null +++ b/test/test_state_chart_with_data_parameter.cpp @@ -0,0 +1,484 @@ +/****************************************************************************** +The MIT License(MIT) + +Embedded Template Library. +https://github.com/ETLCPP/etl +https://www.etlcpp.com + +Copyright(c) 2018 jwellbelove + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files(the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions : + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +******************************************************************************/ + +#include "UnitTest++/UnitTest++.h" + +#include "etl/state_chart.h" +#include "etl/enum_type.h" +#include "etl/queue.h" +#include "etl/array.h" + +#include + +namespace +{ + //*************************************************************************** + // Events + struct EventId + { + enum enum_type + { + START, + STOP, + EMERGENCY_STOP, + STOPPED, + SET_SPEED, + ABORT + }; + + ETL_DECLARE_ENUM_TYPE(EventId, etl::istate_chart::event_id_t) + ETL_ENUM_TYPE(START, "Start") + ETL_ENUM_TYPE(STOP, "Stop") + ETL_ENUM_TYPE(EMERGENCY_STOP, "Emergency Stop") + ETL_ENUM_TYPE(STOPPED, "Stopped") + ETL_ENUM_TYPE(SET_SPEED, "Set Speed") + ETL_ENUM_TYPE(ABORT, "Abort") + ETL_END_ENUM_TYPE + }; + + //*************************************************************************** + // States + struct StateId + { + enum enum_type + { + IDLE, + RUNNING, + WINDING_DOWN, + NUMBER_OF_STATES + }; + + ETL_DECLARE_ENUM_TYPE(StateId, etl::istate_chart::state_id_t) + ETL_ENUM_TYPE(IDLE, "Idle") + ETL_ENUM_TYPE(RUNNING, "Running") + ETL_ENUM_TYPE(WINDING_DOWN, "Winding Down") + ETL_END_ENUM_TYPE + }; + + //*********************************** + // The motor control FSM. + //*********************************** + class MotorControl : public etl::state_chart + { + public: + + MotorControl() + : state_chart(*this, transitionTable.begin(), transitionTable.end(), StateId::IDLE) + { + this->set_state_table(stateTable.begin(), stateTable.end()); + ClearStatistics(); + } + + //*********************************** + void ClearStatistics() + { + startCount = 0; + stopCount = 0; + setSpeedCount = 0; + stoppedCount = 0; + isLampOn = false; + speed = 0; + windingDown = 0; + entered_idle = false; + null = 0; + data = 0; + } + + //*********************************** + void OnStart(data_parameter_type d) + { + data = d; + ++startCount; + } + + //*********************************** + void OnStop(data_parameter_type d) + { + data = d; + ++stopCount; + } + + //*********************************** + void OnStopped(data_parameter_type d) + { + data = d; + ++stoppedCount; + } + + //*********************************** + void OnSetSpeed(data_parameter_type d) + { + data = d; + ++setSpeedCount; + SetSpeedValue(100); + } + + //*********************************** + void OnEnterIdle() + { + TurnRunningLampOff(); + entered_idle = true; + } + + //*********************************** + void OnEnterRunning() + { + TurnRunningLampOn(); + } + + //*********************************** + void OnEnterWindingDown() + { + ++windingDown; + } + + //*********************************** + void OnExitWindingDown() + { + --windingDown; + } + + //*********************************** + void SetSpeedValue(int speed_) + { + speed = speed_; + } + + //*********************************** + bool Guard() + { + return guard; + } + + //*********************************** + bool NotGuard() + { + return !guard; + } + + //*********************************** + void TurnRunningLampOn() + { + isLampOn = true; + } + + //*********************************** + void TurnRunningLampOff() + { + isLampOn = false; + } + + //*********************************** + void Null(data_parameter_type d) + { + data = d; + ++null; + } + + int startCount; + int stopCount; + int setSpeedCount; + int stoppedCount; + bool isLampOn; + int speed; + int windingDown; + bool entered_idle; + int null; + int data; + + bool guard; + + static const etl::array transitionTable; + static const etl::array stateTable; + }; + + //*************************************************************************** + constexpr etl::array MotorControl::transitionTable = + { + MotorControl::transition(StateId::IDLE, EventId::START, StateId::RUNNING, &MotorControl::OnStart, &MotorControl::Guard), + MotorControl::transition(StateId::IDLE, EventId::START, StateId::IDLE, &MotorControl::Null, &MotorControl::NotGuard), + MotorControl::transition(StateId::RUNNING, EventId::STOP, StateId::WINDING_DOWN, &MotorControl::OnStop), + MotorControl::transition(StateId::RUNNING, EventId::EMERGENCY_STOP, StateId::IDLE, &MotorControl::OnStop), + MotorControl::transition(StateId::RUNNING, EventId::SET_SPEED, StateId::RUNNING, &MotorControl::OnSetSpeed), + MotorControl::transition(StateId::WINDING_DOWN, EventId::STOPPED, StateId::IDLE, &MotorControl::OnStopped), + MotorControl::transition( EventId::ABORT, StateId::IDLE) + }; + + //*************************************************************************** + constexpr etl::array MotorControl::stateTable = + { + MotorControl::state(StateId::IDLE, &MotorControl::OnEnterIdle, nullptr), + MotorControl::state(StateId::RUNNING, &MotorControl::OnEnterRunning, nullptr), + MotorControl::state(StateId::WINDING_DOWN, &MotorControl::OnEnterWindingDown, &MotorControl::OnExitWindingDown) + }; + + MotorControl motorControl; + + SUITE(test_state_chart_class) + { + //************************************************************************* + TEST(test_state_chart) + { + motorControl.ClearStatistics(); + + // In Idle state. + 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); + CHECK_EQUAL(false, motorControl.entered_idle); + + // Send Start event (state chart not started). + motorControl.guard = true; + motorControl.process_event(EventId::START, 1); + + CHECK_EQUAL(StateId::IDLE, int(motorControl.get_state_id())); + + CHECK_EQUAL(0, motorControl.data); + 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); + 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, 2); + motorControl.process_event(EventId::STOPPED, 3); + + CHECK_EQUAL(StateId::IDLE, int(motorControl.get_state_id())); + + CHECK_EQUAL(0, motorControl.data); + 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, 4); + + // Still in Idle state. + + CHECK_EQUAL(StateId::IDLE, int(motorControl.get_state_id())); + + CHECK_EQUAL(4, motorControl.data); + 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); + CHECK_EQUAL(1, motorControl.null); + + // Send Start event. + motorControl.guard = true; + motorControl.process_event(EventId::START, 5); + + // Now in Running state. + + CHECK_EQUAL(StateId::RUNNING, int(motorControl.get_state_id())); + + CHECK_EQUAL(5, motorControl.data); + CHECK_EQUAL(true, motorControl.isLampOn); + CHECK_EQUAL(0, motorControl.setSpeedCount); + CHECK_EQUAL(0, motorControl.speed); + CHECK_EQUAL(1, motorControl.startCount); + 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, 6); + motorControl.process_event(EventId::STOPPED, 7); + + CHECK_EQUAL(StateId::RUNNING, int(motorControl.get_state_id())); + + CHECK_EQUAL(5, motorControl.data); + CHECK_EQUAL(true, motorControl.isLampOn); + CHECK_EQUAL(0, motorControl.setSpeedCount); + CHECK_EQUAL(0, motorControl.speed); + CHECK_EQUAL(1, motorControl.startCount); + 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, 8); + + // Still in Running state. + + CHECK_EQUAL(StateId::RUNNING, int(motorControl.get_state_id())); + + CHECK_EQUAL(8, motorControl.data); + CHECK_EQUAL(true, motorControl.isLampOn); + CHECK_EQUAL(1, motorControl.setSpeedCount); + CHECK_EQUAL(100, motorControl.speed); + CHECK_EQUAL(1, motorControl.startCount); + 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, 9); + + // Now in WindingDown state. + + CHECK_EQUAL(StateId::WINDING_DOWN, int(motorControl.get_state_id())); + + CHECK_EQUAL(9, motorControl.data); + CHECK_EQUAL(true, motorControl.isLampOn); + CHECK_EQUAL(1, motorControl.setSpeedCount); + CHECK_EQUAL(100, motorControl.speed); + CHECK_EQUAL(1, motorControl.startCount); + 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, 10); + motorControl.process_event(EventId::STOP, 11); + + CHECK_EQUAL(StateId::WINDING_DOWN, int(motorControl.get_state_id())); + + CHECK_EQUAL(9, motorControl.data); + CHECK_EQUAL(true, motorControl.isLampOn); + CHECK_EQUAL(1, motorControl.setSpeedCount); + CHECK_EQUAL(100, motorControl.speed); + CHECK_EQUAL(1, motorControl.startCount); + 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, 12); + + // Now in Idle state. + CHECK_EQUAL(StateId::IDLE, int(motorControl.get_state_id())); + + CHECK_EQUAL(12, motorControl.data); + CHECK_EQUAL(false, motorControl.isLampOn); + CHECK_EQUAL(1, motorControl.setSpeedCount); + CHECK_EQUAL(100, motorControl.speed); + CHECK_EQUAL(1, motorControl.startCount); + CHECK_EQUAL(1, motorControl.stopCount); + CHECK_EQUAL(1, motorControl.stoppedCount); + CHECK_EQUAL(0, motorControl.windingDown); + CHECK_EQUAL(1, motorControl.null); + } + + //************************************************************************* + TEST(test_fsm_emergency_stop) + { + motorControl.ClearStatistics(); + + // Now in Idle state. + + // Send Start event. + motorControl.process_event(EventId::START, 1); + + // Now in Running state. + + CHECK_EQUAL(StateId::RUNNING, int(motorControl.get_state_id())); + + CHECK_EQUAL(1, motorControl.data); + CHECK_EQUAL(true, motorControl.isLampOn); + CHECK_EQUAL(0, motorControl.setSpeedCount); + CHECK_EQUAL(0, motorControl.speed); + CHECK_EQUAL(1, motorControl.startCount); + CHECK_EQUAL(0, motorControl.stopCount); + CHECK_EQUAL(0, motorControl.stoppedCount); + CHECK_EQUAL(0, motorControl.windingDown); + + // Send emergency Stop event. + motorControl.process_event(EventId::EMERGENCY_STOP, 2); + + // Now in Idle state. + CHECK_EQUAL(StateId::IDLE, int(motorControl.get_state_id())); + + CHECK_EQUAL(2, motorControl.data); + CHECK_EQUAL(false, motorControl.isLampOn); + CHECK_EQUAL(0, motorControl.setSpeedCount); + CHECK_EQUAL(0, motorControl.speed); + CHECK_EQUAL(1, motorControl.startCount); + CHECK_EQUAL(1, motorControl.stopCount); + CHECK_EQUAL(0, motorControl.stoppedCount); + CHECK_EQUAL(0, motorControl.windingDown); + } + + //************************************************************************* + TEST(test_fsm_abort) + { + motorControl.ClearStatistics(); + + // Now in Idle state. + + // Send Start event. + motorControl.process_event(EventId::START, 1); + + // Now in Running state. + + motorControl.process_event(EventId::ABORT, 2); + CHECK_EQUAL(StateId::IDLE, int(motorControl.get_state_id())); + + // Send Start event. + motorControl.process_event(EventId::START, 3); + + // Now in Running state. + + // Send Stop event. + motorControl.process_event(EventId::STOP, 4); + + // Now in WindingDown state. + motorControl.process_event(EventId::ABORT, 5); + CHECK_EQUAL(StateId::IDLE, int(motorControl.get_state_id())); + } + }; +} diff --git a/test/vs2019/etl.vcxproj b/test/vs2019/etl.vcxproj index 3aa2fa84..3d5b214a 100644 --- a/test/vs2019/etl.vcxproj +++ b/test/vs2019/etl.vcxproj @@ -1950,6 +1950,7 @@ + diff --git a/test/vs2019/etl.vcxproj.filters b/test/vs2019/etl.vcxproj.filters index cda71eb1..52aedd20 100644 --- a/test/vs2019/etl.vcxproj.filters +++ b/test/vs2019/etl.vcxproj.filters @@ -1496,6 +1496,9 @@ Source Files + + Source Files +