Menu

Tutorial

A. Chol

Tutorial

Simplest State Machine

using namespace ifsm;

StateMachine myMachine;

This is the simple one.

This kind of FSM is not going to be very helpful, but will start from there and expand.

Note: The StateMachine instance is a state itself, so anything that applies to State instances in the rest of this page also applies to the StateMachine instance, ie. transitions, properties, OnEntry, OnExit, ...

Validity and error management

Describing an FSM follows a set of rules. When on of these rule is broken, instantFSM throws a meaningfull exception to inform the developper of the problem. It is then good practice, during development, to surround your instantation with a try/catch block and display meaningfull error when an exception is caught:

using namespace ifsm;

try{
  StateMachine myMachine(

  );
}
catch(const StateMachineException& e){
  std::cout<<e.what()<<std::endl;
}

A detail of each exception that can be thrown will be given during the tutorial. You can also find it in the cheatsheet.

Events

States

The first interesting feature is to be able to add subStates to the root state (the StateMachine instance).
This is achieved by just calling the State() function inside the StateMachine constructor. A required parameter to the State function is the name of the State to create :

using namespace ifsm;

StateMachine myMachine(State("child"));

You can nest any number of States in any State :

using namespace ifsm;

StateMachine myMachine(

  State("Alpha", initialTag,
    State("Banana", initialTag),
    State("Apple")
  ),

  State("Bravo",
    State("Bird", initialTag),
    State("Fish")
  ),

  State("Charlie",
    State("Jupiter", initialTag),
    State("Saturn")
  )

);

Initial State

When instantiating multiple states alternatively to each other, you have to tell which one will be activated by default when entering the parent State.
This is achieved by passing the initialTag parameter to the initial State :

using namespace ifsm;

StateMachine myMachine(

  State("Alpha", initialTag), //Alpha will be entered when myMachine is activated

  State("Bravo"),

);

If you forget to set an initial state for each set of alternative nested states, a NoInitialState exception will be thrown.

Parallel States

Parallel States let you have orthogonal regions in your StateMachine, allowing for concurrent States to be active.

In order to declare a State parallel, pass the parallelTag to it. All of its nested States will be entered after the parallel State is entered.

using namespace ifsm;

StateMachine myMachine( parallelTag,

  State("Alpha"), 

  State("Bravo"),
  //Alpha and Bravo will be entered with the StateMachine entry
);

Note: The definition of an initial State amongst the nested States of a parallel State is not required.

Entry and Exit callbacks

Most of the time, the execution of your state machine drives properties of other systems. It can be sending some event or messages through a communication system or displaying and hiding some elements of a GUI. These action are executed relatively to the current configuration of the state machine, meaning that one action has to be undergone when entering the state and another, sometimes inserse action, when leaving it.
For instance, for a music player, I can have a state Playing and another state Stopped. In the Playing state, I need to turn the Play button into a Pause button and enable the Stop button, while in Stopped state, I need to turn the Pause button into Play and disable the Stop button.
To achieve this kind of behavior, you can setup entry and exit callbacks to each State you create, by calling respectively the OnEntry and OnExit functions inside the State function.
Each of these functions take a callable type as parameter, which will be called upon entry (respectively exit) of the State.
This callable can be of two forms :

  • void(void) : taking no parameter and returning nothing
  • void(StateMachine&) : taking a StateMachine& parameter and returning nothing

The former one is a simple callback with no parameter, while the latter one, receives the StateMachine instance as parameter, allowing the callback to issue an event or query the activity of a state.
This use of a generic callable as parameter allows for various types to be passed, be it pointer to function, std::bind results, std::function and especially lambda functions.

Here is a sample of displaying to the standard output when entering and exiting a state using simple functions:

using namespace ifsm;

void entering(){
  std::cout<<"Entering the root state!"<<std::endl;
}

void exiting(){
  std::cout<<"Exiting the root state!"<<std::endl;
}

StateMachine myMachine( 
  OnEntry([](){
    entering();
  }),
  OnExit([](){
    exiting();
  })
);

Here is the same sample using c++11's lambdas:

using namespace ifsm;

StateMachine myMachine( 
  OnEntry([](){
    std::cout<<"Entering the root state!"<<std::endl;
  }),
  OnExit([](){
    std::cout<<"Exiting the root state!"<<std::endl;
  })
);

The use of lambdas in the context of state machine definition is very powerfull, since the actions to undertake during specific states'entry and exit are embedded
in the definition. When the statemachine can drive orthogonal services (like a GUI in parallel with a physics simulation), avoiding state-specific function definitions outside of the state
machine provides a clearer view of what are the consequences of entering and exiting a state.

Have a look to the sample page to get a clearer view of the possibilities of such an architecture.

Transitions

Transitions model the connection between an event and a change of configuration of the state machine. A change of configuration implies leaving some of the active states of the state machine and entering some previously inactive states.
From there, the basic parameters for a transition are :

  • OnEvent(std::string) : the event that will trigger the transition
  • Target(std::string) : the target state that will become active during the transition.

Extended parameters are :
* Condition(callable_condition) : a callback returning a bool value that prevents the transition from executing if it return false.
* Action(callable) : a callback which is executed after exiting the active states that need to be exited and before entering candidate states for entry.

The following example demonstrate the use of transitions to go back and forth between two state following occuring events.

~~~~~~
:::cpp
using namespace ifsm;

StateMachine myMachine(
State("idle", initialTag,
Transition(
OnEvent("play"), Target("playing"),
Condition(songSelected{return songSelected;}),
Action({
std::cout<<"the player was kicked in"<<std::endl;
})
)
),
State("playing",
Transition(
OnEvent("stop", Target("idle"),
Action({
std::cout<<"the player was stopped"<<std::endl;
})
)
)
);

myMachine.pushEvent("play");
//the state machine enters the "playing" state after displaying the message
myMachine.pushEvent("stop");
//the state machine enters the "idle" state after displaying the stop message

Only transitions from active states are evaluated. This means that there is no need to check that the SM is in "idle" state when considering the event "play" for the first transition. The transition wouldn't be executed if it wasn't the case.

## Targetless transitions 

Sometimes you want to execute some actions (ie. trigger callbacks) when an event is received, without necessarily transitioning states. This is achieved with targetless transition. As the name suggests, these are defined like ordinary transitions, omitting the `Target()` parameter.

:::cpp
using namespace ifsm;

StateMachine myMachine(
Transition(
OnEvent("play"),
Action({std::cout<<"play event has been received"<<std::endl;})
)
);

Another way of defining a targetless transition is by using the `OnEvent` parameter, which behaves as a shorter synonym for `Transition`, but doesn't carry the falsified semantics.
The `OnEvent()` parameter, passed to a `State` or `StateMachine` instantiation, takes an `std::string` giving the event name as first parameter, and a `callable` as second parameter.

:::cpp
using namespace ifsm;

StateMachine myMachine(
OnEvent("play", {std::cout<<"play event has been received"<<std::endl;})
);
~~~~~~

The only drawback to the OnEvent shortcut for targetless transitions is the inability to set a condition callback like ordinary transitions. When a condition is required, you'd still have to define a proper Transition.

Starting the machine

When instantiated, the StateMachine is not considered active. Posting events to it won't trigger any callback. You can activate the StateMachine by calling StateMachine::enter().
Once active, you can query it to know whether a specific State is active.


Related

Wiki: CheatSheet
Wiki: Home
Wiki: Samples

Want the latest updates on software, tech news, and AI?
Get latest updates about software, tech news, and AI from SourceForge directly in your inbox once a month.