State Machine APIs
The state_machine component is a light wrapper around the webgme-hfsm static generated code. It is designed to be used as the component that one or more specific hfsms (manually written or generated from webgme-hfsm) can depend on.
Code examples for the state_machine API are provided in the state_machine example folder.
The example runs the generated code for the following example hsfm (which is provided and for which the code was generated from webgme-hfsm):
API Reference
Header File
Macros
-
MAGIC_ENUM_NO_CHECK_SUPPORT
Classes
-
class __state_machine_documentation__
State machine convenience wrapper for espp. Including
state_machine.hpp
provides access to all the base classes that the generated code relies on as well as what you would need to subclass yourself for a manually written hfsm. Please see https://github.com/finger563/webgme-hfsm for more information about modeling, generating, and developing HFSMs.State Machine / HFSM Example with the generated Complex example hfsm
const espp::state_machine::Complex::GeneratedEventBase *e = nullptr; bool handled = false; // create the HFSM espp::state_machine::Complex::Root complex_root; // set the log callback to print to stdout complex_root.set_log_callback([](std::string_view msg) { fmt::print("{}\n", msg); }); // initialize the HFSM complex_root.initialize(); // start a task to run the hfsm auto task_fn = [&complex_root](std::mutex &m, std::condition_variable &cv) { // execute the state machine complex_root.handle_all_events(); complex_root.tick(); // NOTE: if we call tick above, then we need to call handle_all_events() // again to handle any events that were spawned by the tick() complex_root.handle_all_events(); // get the active state period (the state may have changed from handling // events, so we always need to get the active leaf) auto current_hfsm_period = complex_root.getActiveLeaf()->getTimerPeriod(); // NOTE: sleeping in this way allows the sleep to exit early when the // task is being stopped / destroyed { std::unique_lock<std::mutex> lk(m); cv.wait_for(lk, std::chrono::duration<float>(current_hfsm_period)); } // stop the task if the hfsm has stopped (reached its end state) return complex_root.has_stopped(); }; auto task = espp::Task({.callback = task_fn, .task_config = {.name = "HFSM"}, .log_level = espp::Logger::Verbosity::DEBUG}); task.start(); // from other contexts you can spawn events into the HFSM. the functions are // generated based on the event names from the model, and the data // structures are generated into <state machine name>_event_data.hpp so that // you can enforce that events must have certain data. The sequence below // should transition the HFSM through a few states until it gets to a state // where the ENDEVENT will cause the whole HFSM to terminate. complex_root.spawn_EVENT4_event({}); complex_root.spawn_EVENT1_event({}); complex_root.spawn_EVENT2_event({}); complex_root.spawn_EVENT3_event({}); complex_root.spawn_EVENT1_event({}); complex_root.spawn_ENDEVENT_event({}); // give the hfsm some time to handle the events before we fully exit std::this_thread::sleep_for(1s);
Running the HFSM Test Bench on a Real Device:
// NOTE: we need to configure stdin/stdout to use std::cin for // get_user_selection() function. espp::Cli::configure_stdin_stdout(); const espp::state_machine::Complex::GeneratedEventBase *e = nullptr; // create the HFSM espp::state_machine::Complex::Root complex_root; // set the log callback to print to stdout complex_root.set_log_callback([](std::string_view msg) { fmt::print("{}\n", msg); }); // initialize the HFSM complex_root.initialize(); // start a task to run the hfsm auto task_fn = [&complex_root](std::mutex &m, std::condition_variable &cv) { // execute the state machine complex_root.handle_all_events(); // NOTE: we would normally call the tick() function here, but we want to // be able to manually tick the HFSM from the test bench and we // don't want to clutter the log with the tick() messages. // complex_root.tick(); // NOTE: if we call tick above, then we need to call handle_all_events() // again to handle any events that were spawned by the tick() // function // complex_root.handle_all_events(); // get the active state period (the state may have changed from handling // events, so we always need to get the active leaf) auto current_hfsm_period = complex_root.getActiveLeaf()->getTimerPeriod(); // NOTE: sleeping in this way allows the sleep to exit early when the // task is being stopped / destroyed { std::unique_lock<std::mutex> lk(m); cv.wait_for(lk, std::chrono::duration<float>(current_hfsm_period)); } // stop the task if the hfsm has stopped (reached its end state) return complex_root.has_stopped(); }; auto task = espp::Task({.callback = task_fn, .task_config = {.name = "HFSM"}, .log_level = espp::Logger::Verbosity::DEBUG}); task.start(); // NOTE: this is just a copy of the HFSM code from the generated test bench, // and is not intended to show how to actually run the HFSM in // production. while (!complex_root.has_stopped()) { do { std::this_thread::sleep_for(100ms); } while (complex_root.has_events()); display_event_menu(); int selection = get_user_selection(); if (selection == ExitSelection) { complex_root.terminate(); break; } else if (selection == RestartSelection) { complex_root.restart(); } else if (selection == TickSelection) { complex_root.tick(); } else { make_event(complex_root, selection); } }
Note
This class does not really exist or do anything, but it’s the only way I could figure out how to get this documentation built into the system :(
Header File
Classes
-
class EventBase
-
class StateBase
States contain other states and can consume generic EventBase objects if they have internal or external transitions on those events and if those transitions’ guards are satisfied. Only one transition can consume an event in a given state machine.
There is also a different kind of Event, the tick event, which is not consumed, but instead executes from the top-level state all the way to the curently active leaf state.
Entry and Exit actions also occur whenever a state is entered or exited, respectively.
Subclassed by espp::state_machine::DeepHistoryState, espp::state_machine::ShallowHistoryState
Public Functions
-
StateBase()
Default constructor.
-
explicit StateBase(StateBase *parent)
Constructor that sets the parent state.
- Parameters
parent – [in] Pointer to parent state
-
virtual ~StateBase(void)
Destructor.
-
virtual void initialize(void)
Will be generated to call entry() then handle any child initialization. Finally calls makeActive on the leaf.
-
virtual void entry(void)
Will be generated to run the entry() function defined in the model.
-
virtual void exit(void)
Will be generated to run the exit() function defined in the model.
-
virtual bool handleEvent(EventBase *event)
Calls handleEvent on the activeLeaf.
- Parameters
event – [in] Event needing to be handled
- Returns
true if event is consumed, false otherwise
-
virtual void tick(void)
Will be generated to run the tick() function defined in the model and then call _activeState->tick().
-
virtual double getTimerPeriod(void)
Returns the timer period for the state.
-
virtual StateBase *getInitial(void)
Will be known from the model so will be generated in derived classes to immediately return the correct initial state pointer for quickly transitioning to the proper state during external transition handling.
- Returns
Pointer to initial substate
-
void exitChildren(void)
Recurses down to the leaf state and calls the exit actions as it unwinds.
-
StateBase *getActiveChild(void)
Will return _activeState if it exists, otherwise will return nullptr.
- Returns
Pointer to last active substate
-
StateBase *getActiveLeaf(void)
Will return the active leaf state, otherwise will return nullptr.
- Returns
Pointer to last active leaf state.
-
virtual void makeActive(void)
Make this state the active substate of its parent and then recurse up through the tree to the root.
Note
Should only be called on leaf nodes!
-
void setShallowHistory(void)
Sets the currentlyActive state to the last active state and re-initializes them.
-
void setDeepHistory(void)
Go to the last active leaf of this state. If none exists, re-initialize.
-
StateBase()
Header File
Classes
-
class ShallowHistoryState : public espp::state_machine::StateBase
Shallow History Pseudostates exist purely to re-implement the makeActive() function to actually call _parentState->setShallowHistory()
Public Functions
-
inline ShallowHistoryState()
Default constructor.
-
inline explicit ShallowHistoryState(StateBase *_parent)
Constructor.
- Parameters
_parent – The parent state
-
inline virtual void makeActive() override
Calls _parentState->setShallowHistory().
-
virtual void initialize(void)
Will be generated to call entry() then handle any child initialization. Finally calls makeActive on the leaf.
-
virtual void entry(void)
Will be generated to run the entry() function defined in the model.
-
virtual void exit(void)
Will be generated to run the exit() function defined in the model.
-
virtual bool handleEvent(EventBase *event)
Calls handleEvent on the activeLeaf.
- Parameters
event – [in] Event needing to be handled
- Returns
true if event is consumed, false otherwise
-
virtual void tick(void)
Will be generated to run the tick() function defined in the model and then call _activeState->tick().
-
virtual double getTimerPeriod(void)
Returns the timer period for the state.
-
virtual StateBase *getInitial(void)
Will be known from the model so will be generated in derived classes to immediately return the correct initial state pointer for quickly transitioning to the proper state during external transition handling.
- Returns
Pointer to initial substate
-
void exitChildren(void)
Recurses down to the leaf state and calls the exit actions as it unwinds.
-
StateBase *getActiveChild(void)
Will return _activeState if it exists, otherwise will return nullptr.
- Returns
Pointer to last active substate
-
StateBase *getActiveLeaf(void)
Will return the active leaf state, otherwise will return nullptr.
- Returns
Pointer to last active leaf state.
-
void setShallowHistory(void)
Sets the currentlyActive state to the last active state and re-initializes them.
-
void setDeepHistory(void)
Go to the last active leaf of this state. If none exists, re-initialize.
-
inline ShallowHistoryState()
Header File
Classes
-
class DeepHistoryState : public espp::state_machine::StateBase
Deep History Pseudostates exist purely to re-implement the makeActive() function to actually call _parentState->setDeepHistory()
Public Functions
-
inline DeepHistoryState()
Construct a new Deep History State object.
-
inline explicit DeepHistoryState(StateBase *_parent)
Construct a new Deep History State object.
- Parameters
_parent – The parent state of this state
-
inline virtual void makeActive() override
Calls _parentState->setDeepHistory()
-
virtual void initialize(void)
Will be generated to call entry() then handle any child initialization. Finally calls makeActive on the leaf.
-
virtual void entry(void)
Will be generated to run the entry() function defined in the model.
-
virtual void exit(void)
Will be generated to run the exit() function defined in the model.
-
virtual bool handleEvent(EventBase *event)
Calls handleEvent on the activeLeaf.
- Parameters
event – [in] Event needing to be handled
- Returns
true if event is consumed, false otherwise
-
virtual void tick(void)
Will be generated to run the tick() function defined in the model and then call _activeState->tick().
-
virtual double getTimerPeriod(void)
Returns the timer period for the state.
-
virtual StateBase *getInitial(void)
Will be known from the model so will be generated in derived classes to immediately return the correct initial state pointer for quickly transitioning to the proper state during external transition handling.
- Returns
Pointer to initial substate
-
void exitChildren(void)
Recurses down to the leaf state and calls the exit actions as it unwinds.
-
StateBase *getActiveChild(void)
Will return _activeState if it exists, otherwise will return nullptr.
- Returns
Pointer to last active substate
-
StateBase *getActiveLeaf(void)
Will return the active leaf state, otherwise will return nullptr.
- Returns
Pointer to last active leaf state.
-
void setShallowHistory(void)
Sets the currentlyActive state to the last active state and re-initializes them.
-
void setDeepHistory(void)
Go to the last active leaf of this state. If none exists, re-initialize.
-
inline DeepHistoryState()