1. Include the C/C++ headers
#include <assert.h>
2. Configure optional HFSM2 functionality using #define
s
(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 susing FSM = M::PeerRoot<S(Off), // initial top-level stateM::Composite<S(On), // sub-machine region with a head state (On) and and 3 sub-statesS(Red), // initial sub-state of the regionS(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 transitionsif (control.context().powerOn) // access shared datacontrol.changeTo<On>(); // initiate a transition into 'On' region}};
struct On: FSM::State{void enter(PlanControl& control) { // called on state activationauto plan = control.plan(); // access the plan for the regionplan.change<Red, Yellow>(); // sequence plan steps, executed when the previous state succeedsplan.change<Yellow, Green>();plan.change<Green, Yellow>();plan.change<Yellow, Red>();}void exit(PlanControl& /*control*/) {} // called on state deactivationvoid planSucceeded(FullControl& control) { // called on the successful completion of all plan stepscontrol.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 updatescontrol.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 eventscontrol.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 planfsm.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 planfsm.update();assert(fsm.isActive<Done>()); // activated by On::planSucceeded()return 0;}