Skip to content

Tutorial: Vending machine FSM

Sergei Fedorov edited this page Nov 15, 2016 · 6 revisions

This tutorial uses most of features of afsm library such as state transitions, entry/exit actions, nested state machines, in-state event handling, default transitions and guards. Also it will expose some C++ template programming techniques used in afsm library.

Basic Machine

We will start with a state machine that can only turn on and off, it will have two states - on and off and will handle two events power_on and power_off

vending-starter

First of all we need to include a single header of afsm library

#include <afsm/fsm.hpp>

Then we start defining our state machine. All state machine definitions must derive from ::afsm::def::state_machine template, all states - from ::afsm::def::state template. If the machine contains a terminal state, it must derive from ::afsm::def::terminal_state template. There is no difference if we define nested states of nested states inside or outside state machine definition, but in this example we will define then inside the state machine class to follow the structure of the state machine and not to clutter the enclosing namespace. We will place all declarations inside a vending namespace.

namespace vending {
struct vending_def : ::afsm::def::state_machine<vending_def> {
    //@{
    /** @name Substates definition */
    struct off : state<off> {};
    struct on  : state<on> {};
    //@}

    /** Initial state machine state */
    using initial_state = off;
};
}  /* namespace vending */

Next we will add events and define a transition table. We will put events in an events namespace, to easily distinguish the events from other entities in code. An event is a structure that must be either a copy- or move- constructible. It's exact type is used for selecting transitions.

namespace vending {
namespace events {

struct power_on {};
struct power_off {};

}  /* namespace events */


struct vending_def : ::afsm::def::state_machine<vending_def> {
    // Skip ...
    /** State transition table */
    using transitions = transition_table <
        /*  Start   Event               Next    */
        tr< off,    events::power_on,   on      >,
        tr< on,     events::power_off,  off     >
    >;
};
}  /* namespace vending */

We are almost done with the basic state machine. All we are left to do - is to instantiate a state machine with a class template that will process events and handle state switching.

using vending_sm = ::afsm::state_machine<vending_def>;

Processing Events and Checking State

The ::afsm::state_machine template defines process_event member function template that accepts instances of events, so turning on our vending machine will look like

vending_sm vm;
vm.process_event(events::power_on{});

To check that a machine is in a given state there is a member function template is_in_state. Checks will look like this:

if (vm.is_in_state<vending_sm::on>()) { /**/ }
if (vm.is_in_state<vending_sm::off>()) { /**/ }

You can find a fully compilable and runnable source code here. The program defines all the above classes and makes some use of the machine - turns it on and off and outputs state checks in between.

Nested State Machines

As long as when on a vending machine can do different things, we will need to convert the on state to a nested state machine.

vending-basic

So we will replace struct on : state<on> {}; with:

    struct on  : state_machine<on> {
        //@{
        /** @name Substates definition */
        struct serving : state<serving> {};
        struct maintenance : state<maintenance> {};
        struct out_of_service : state<out_of_service> {};
        //@}

        /** Initial state machine state */
        using initial_state = serving;
        /** State transition table */
        using transitions = transition_table<
            /*  Start           Event                       Next            */
            tr< serving,        events::start_maintenance,  maintenance     >,
            tr< serving,        events::out_of_goods,       out_of_service  >,
            tr< out_of_service, events::start_maintenance,  maintenance     >,
            tr< maintenance,    events::end_maintenance,    serving         >
        >;
    };

Passing Arguments to State Machine Constructor

Adding Transition Actions

In-State Transitions

Adding Guards

Default Transitions

Preserving State`s State

Clone this wiki locally