www.vakos.nl/programming/
P R O G R A M M I N G

VAKos's hobby
C/C++ programming, with focus on reusability
www.vakos.nl/programming/statemac/
STATEMAC


Statemachines are a well known way to implement software that reacts on events (see also Wikipedia Finite State Machines). They can be implemented in multiple ways, by boolean variables or by enum switch case.

The source code described here chooses a way to abstract the statemachine to a kind of language. This language is translated to a C implementation via macros.

You can download the under GPL 3 licensed (local copy) sources on this page. Any remarks, comments, bug-reports, improvement and/or suggestions are greatly appreciated via email to programming at vakos dot nl.

STATEMACH_END( sm )
  STATE( s1 )
    ONEVENT( e1 )
    TOSTATE( s2 )
    DOING( a1 )
  STATE_END( s1 )
STATEMACH_END( sm )

The language describes a STATEMACH, consisting of several STATEs. Every STATE can react on multiple events (ONEVENT) which trigger a transition to another state (TOSTATE) while DOING an action.

  ...
  STATE( s1 )
    ONEVENT( e1 )
    TOSTATE( s2 )
    DOING( a1 )

    ONLEAVINGSTATE()
    DOING( a1 )
  STATE_END( s1 )

A special transition is the ONLEAVINGSTATE transition. This transition is automatically triggered when another event in the same state has been triggered. The on-leaving-state can only trigger an action, since its state transition is already determined by its triggering event.

STATEMACH( sm )
  DOALWAYS( a1 )

  STATE( s1 )
    DOALWAYS( a2 )

    ONEVENT( e1 )
    TOSTATE( s2 )
    DOING( a1 )
    ...

The DOALWAYS is an extension to statemachines. It gives you "cyclic" control. Do-always can be implemented on statemachine-level (and is called every execution), or at state-level (and is called every execution when that state is active).

STATEMACH( sm )
STATEMACH_END( sm )

This language is "compiled", via the macros, to a function that must be called to execute the statemachine. The function is named after STATEMACH's argument, concatenated with "StateMachine". This function takes one argument, the this- pointer, which is a pointer to the statemachine-struct.

STATEMACH_ENUM( sm )
  s1,
  s2,
  s3
STATEMACH_ENUM_END( sm )

The most essential part of the statemachine-struct is an enum member. This enum knows all possible states of the statemachine. It is constructed with the STATEMACH_ENUM macro and creates an enum named after the argument, preceeded by "E" and concatenated with "State". Between these 'bracketed' macro, all the states of the statemachine are declared.

STATE( s1 )
  ONEVENT(
    STATEMACHINE_VAR_STATETIMEOUT(
      this->m_State, t1 ) )
  TOSTATE( s2 )
  DOING( a1 )
STATE( s1 )

The second part of the statemachine-struct is a timer, which is reset on every state transition. This timer is read by the STATEMACH_VAR_STATETIMEOUT macro.
statemac.h
timerfree.h
timerfree.c
timerfree-struct.h
The macro's to use such a statemachine are implemented in statemac.h. The timer it uses is implemented in the timerfree.c file, with public interface timerfree.h and internal definition timerfree-struct.h.
www.vakos.nl/programming/ This example shows how to operate a light from a momentary pushbutton. Assume a pushbutton that only contacts when it is pushed, like you find at most doorbells. Suppose that we want to switch a light on and off with this pushbutton. So push and release the first time will switch the light on, push and release a second time will switch the light off.
pushlight.h
pushlight.c
pushlight-struct.h
This pushbutton-light-controller consists of three files. File pushlight.h will declare the public interface, the prototype for the statemachine function that must be called cyclically. File pushlight.c will define the implementation, the statemachine function itself. File pushlight-struct.h will declare the private class definition.

STATEMACH_ENUM( PushLight )
  sOFF,
  sSWITCHING_ON,
  sON,
  sSWITCHING_OFF
STATEMACH_ENUM_END( PushLight )

In file pushlight-struct.h is the STATEMACH_ENUM macro used to define the states in the statemachine.

struct sPushLight
{
  STATEMACH_VAR( PushLight )
  int      m_PushButton;
  int      m_Light;
};

This statemachine-struct is embedded in a class definition for the pushbutton- light-controller. Next to a statemachine variable does it contain a handle to the pushbutton and the light.

struct sPushLight;
typedef struct sPushLight TPushLight;

File pushlight.h does a forward declaration of the 'class' defined in puslight-struct.h. In this way the users of the 'class' are not bothered with its private implementation.

void
PushLightInit(
  TPushLight  *this,
  int          a_PushButton,
  int          a_Light );

STATEMACH_DECL( PushLight )

Then it declares the prototypes of the public interface. An init function in which the object of our class can initialize its io-handles and statemachine variable.
And secondly, by means of the STATEMACH_DECL macro, the statemachine function that should be called cyclically.

void
PushLightInit(
  TPushLight *this,
  int         a_PushButton,
  int         a_Light )

STATEMACH( PushLight )
STATEMACH_END( PushLight )

File pushlight.c contains the function definitions. First there is the implementation of the init function and secondly the implementation of the statemachine function (via the macros STATEMACH()..STATEMACH_END()).

  STATE( sOFF )
    ONEVENT(
       ReadIo( this->m_PushButton ) )
    TOSTATE( sSWITCHING_ON )
    DOING(
      WriteIo( this->m_Light, 1 ); )
  STATE_END( sOFF )

The first state, sOFF, transitions to the sSWITCHING_ON state when the ReadIo() on the pushbutton handler returns true. During that transition it switches on the light (with WriteIo()).

  STATE( sSWITCHING_ON )
    ONEVENT( ! ReadIo( this->m_PushButton ) )
    TOSTATE( sON )
    DOING( /* nothing */ )
  STATE_END( sSWITCHING_ON )

The second state, sSWITCHING_ON, is a 'wait' state. It waits until the pushbutton is released. Upon release, the statemachine transits to state sON.

  STATE( sON )
    ONEVENT( ReadIo( this->m_PushButton ) )
    TOSTATE( sSWITCHING_OFF )
    DOING( /* nothing */ )

In the third state, sON, is the light on. When the pushbutton is pressed (command from the user), the light must be switched off. The pressing of the pushbutton is detected by the ONEVENT, which triggers a transition to state sSWITCHING_OFF.

Note that this transition does not excute any action, not even to switch off the light. This is explained below.

    ONEVENT(
      STATEMACH_VAR_STATETIMEOUT(
        this->m_State,
        900000 ) )
    TOSTATE( sSWITCHING_OFF )
    DOING( /* nothing */ )

The second part of this state sON is there to demonstrate the usefullness of the statemachine timer. Macro STATEMACH_VAR_STATETIMEOUT keeps track of the time this statemachine is in this state. When, in this example, this time exceeds 900k miliseconds (=900sec, =15min), it signals a true, which is then considered an event. This event brings the statemachine to state sSWITCHING_OFF.

This construction in this example reduces energy consumption. Whenever the user forgets to turn out the light, 15min after he/she swithced it on, it is automatically switched off.

Note that this transition does not trigger any action. This is explained below.

    ONLEAVINGSTATE()
    DOING(
      WriteIo( this->m_Light, 0 ); )
  STATE_END( sON )

The ONLEAVINGSTATE macro returns true, when the statemachine is transitioning. In this example is the transition caused by either the pressing of the pushbutton, or by the 900sec timeout.

No matter how caused, when leaving this state, the light must be switched off. The ONLEAVINGSTATE detects the leaving of the state and triggers the DOING to switch off the light (WriteIo).

  STATE( sSWITCHING_OFF )
    ONEVENT( ! ReadIo( this->m_PushButton ) )
    TOSTATE( sOFF )
    DOING( /* nothing */ )
  STATE_END( sSWITCHING_OFF )

The fourth and last state, sSWITCHING_OFF, is like sSWITCHING_ON a waiting state to wait for the pushbutton to be released. When the button is released, it transits to sOFF, doing /* nothing */.

www.vakos.nl/programming/
P R O G R A M M I N G

VAKos's hobby
C/C++ programming, with focus on reusability