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):

"Complex" example HFSM showing the many of the UML formalisms supported.

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

Public Functions

inline virtual ~EventBase()

Default constructor.

virtual std::string to_string() const = 0

Returns a string representation of the event.

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 setActiveChild(StateBase *childState)

Update the active child 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.

void setParentState(StateBase *parent)

Will set the parent state.

Parameters

parent[in] Pointer to parent state

StateBase *getParentState(void)

Will return the parent state.

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 setActiveChild(StateBase *childState)

Update the active child 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.

void setParentState(StateBase *parent)

Will set the parent state.

Parameters

parent[in] Pointer to parent state

StateBase *getParentState(void)

Will return the parent state.

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 setActiveChild(StateBase *childState)

Update the active child 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.

void setParentState(StateBase *parent)

Will set the parent state.

Parameters

parent[in] Pointer to parent state

StateBase *getParentState(void)

Will return the parent state.