Quick Tutorial

1. Include the C/C++ headers

#include <assert.h>

2. Configure optional HFSM2 functionality using #defines (in this case we're using Plans to make transition cycle more straightforward):

#define HFSM2_ENABLE_PLANS

3. Include HFSM2 header:

#include <hfsm2/machine.hpp>

4. Define interface class between the state machine and its host (also ok to use the host object itself):

struct Context {
    bool powerOn;
};

5. (Optional) Define type config:

using Config = hfsm2::Config::ContextT<Context>;

6. (Optional, recommended) Definehfsm2::Machine for convenience:

using M = hfsm2::MachineT<Config>;

7. Declare state machine structure. States need to be forward declared, e.g. with a magic macro:

#define S(s) struct s

using FSM = M::PeerRoot<
                S(Off),                                // initial top-level state
                M::Composite<S(On),                    // sub-machine region with a head state (On) and and 3 sub-states
                    S(Red),                            // initial sub-state of the region
                    S(Yellow),
                    S(Green)
                >,
                S(Done)
            >;

#undef S

8. (Optional) While HFSM2 transitions aren't event-based, events can be used to have FSM react to external stimuli:

struct Event {};

9. Define states and override required state methods:

struct Off
    : FSM::State
{
    void entryGuard(FullControl& control) {            // called before state activation, use to re-route transitions
        if (control.context().powerOn)                 // access shared data
            control.changeTo<On>();                    // initiate a transition into 'On' region
    }
};
struct On
    : FSM::State
{
    void enter(PlanControl& control) {                 // called on state activation
        auto plan = control.plan();                    // access the plan for the region

        plan.change<Red, Yellow>();                    // sequence plan steps, executed when the previous state succeeds
        plan.change<Yellow, Green>();
        plan.change<Green, Yellow>();
        plan.change<Yellow, Red>();
    }

    void exit(PlanControl& /*control*/) {}             // called on state deactivation

    void planSucceeded(FullControl& control) {         // called on the successful completion of all plan steps
        control.changeTo<Done>();
    }

    void planFailed(FullControl& /*control*/) {}       // called if any of the plan steps fails
};
struct Red
    : FSM::State
{
    void update(FullControl& control) {                // called on periodic state machine updates
        control.succeed();                             // notify successful completion of the plan step
    }                                                  // plan will advance to the 'Yellow' state
};
struct Yellow
    : FSM::State
{
    void update(FullControl& control) {
        control.succeed();                             // plan will advance to the 'Green' state on the first entry
                                                       // and 'Red' state on the second one
    }
};
struct Green
    : FSM::State
{
    void react(const Event&, FullControl& control) {   // called on external events
        control.succeed();                             // advance to the next plan step
    }
};
struct Done
    : FSM::State
{};

10. Write the client code to use your new state machine:

int main() {

11. Create context and state machine instances:

    Context context;
    context.powerOn = true;

    FSM::Instance fsm{context};
    assert(fsm.isActive<On>());                        // activated by Off::entryGuard()
    assert(fsm.isActive<Red>());                       // On's initial sub-state

12. Call FSM::update() for the FSM to process transitions:

    fsm.update();
    assert(fsm.isActive<Yellow>());                    // 1st setp of On's plan

    fsm.update();
    assert(fsm.isActive<Green>());                     // 2nd setp of On's plan

13. (Optional) Event reactions also cause transitions to be processed:

    fsm.react(Event{});
    assert(fsm.isActive<Yellow>());                    // 3rd setp of On's plan

14. Keep updating the FSM for as long as necessary:

    fsm.update();
    assert(fsm.isActive<Red>());                       // 4th setp of On's plan

    fsm.update();
    assert(fsm.isActive<Done>());                      // activated by On::planSucceeded()

    return 0;
}

See Also

snippets/wiki_tutorial.cpp

Last updated