core/include/state_machine.h File Reference

State Machine. More...

Functions

os_result_t os_sm_process_event (os_state_machine_t *sm)
 Process an event if there is any in state machine queue.
os_result_t os_sm_flush (os_state_machine_t *sm)
 Removes all pending events from the queue.

Detailed Description

State Machine.

Author:
Piotr Romaniuk, (c) ELESOFTROM
Version:
1.1 Nov. 8, 2013
state_machine_example.png
State Machine is an abstract object that is useful in behavioral description of the software. It is driven by incoming events distinguished by a type of signal that is carried in the event. Response to the event may vary and depends on the current state in which state the machine remains. The event can cause a change of internal state. The state machine may also generate an action in response to the event.

The DioneOS system defines a template for coding state machines - a set of programming rules that standardize translation from abstract state machine diagram to the C programming language. These measures help to write state machines in clear and well structured form.
The part of the DioneOS system that supports state machines consists of an infrastructure for common items and mechanisms for the state machines (i.e. event passing and storage, transitions, etc.).

The state machine interacts with other parts of the code by events (os_event_t) which are sent to the state machine. Due to different context and processing time between sender and state machine, it is equiped with its own queue. In the queue incomming events are serialized regarding to the reception time and wait for processing.
Because the state machine itself is passive, it must be supervised by active object - state machine manager. This active object has its own thread and is responsible for scheduling of the events processing. All events are processed in the context of that thread.
state_machines_in_dioneos.png
The state machine is described by a structural type os_state_machine_t and consists of (refer to the figure on the left):
1. table of handlers - a functions containing code that process events in each state,
2. queue implemented as ring buffer for pending event storage,
3. optional additional data attached during state machine creation.
It also has internal variable that represent current state of the state machine.

The state machine should be controlled by sending events, but not directly to state machine, events should be sent via the state machine manager (i.e. os_sm_manager_send_event() function).

In order to define your own state machine, you need (refer to listings below):
1. specify your signals (types of events) in .signals text file,
2. define states of your machine in .states text file,
3. create a header file for this type of state machine (this header will be included in .c files where the state machine will be used)
4. write a code of handlers, separated for each state. The handler (os_sm_handler_t()) is a function that will be called when event must be processed by the state machine. This function contains switch-case for splitting execution for different types of events (signals). Value returned from the handler determines if the transition to other state occurs. If zero is returned the state machine remains in current state.
This source file should contain definition of initialization function (created by generator - preprocessing macro) and other items (please refer to code below for details).

The state machine should be initialized and activated. Activation adds it to the state machine manager list and enables events flow from the queue to the handlers.

Warning:
Because all state machines work in context of one common thread, they should not block, otherwise all state machines are hold.

DioneOS system contains predefined macros that simplifies state machine definition. It provides useful and easy way for description of the state machine:
1. it requires entering the minimum of information,
2. only specific parts are entered, e.g. signal and states names,
3. standard naming style for state machine items are generated automatically,
4. the information is entered only once - eliminating possible error (e.g. list of states and handlers case),
5. structure of state machine is clear,

Note:
The space of signals (type of events) consists of two parts:
1. global (codes of signals are common for all state machines)
(i). system signal (sm_types.h),
(ii). user signals (sm_signals_user.h)
2. local (values are re-used for separated state machines) (state machine .h files)
Warning:
Do not send event with local signals from different state machine. Due to common value space, it will be missinterpretted in the handler.


Example of state machine definition and usage

Let's assume that the state machine will be named my_sm1
[1] my_sm1.signals - state machine signals definition

 DEF_SIGNAL( a )
 DEF_SIGNAL( b )
 DEF_SIGNAL( c )


[2] my_sm1.states - state machine states definition

 DEF_STATE( 1 )
 DEF_STATE( 2 )
 DEF_STATE( 3 )


[3] my_sm1.h

#ifndef __MY_SM1_H__
#define __MY_SM1_H__
#define SM_PREFIX( x ) my_sm1_##x // this will be used as prefix, this must be first, before system.h include 

#include "sm_macros_enums.h"
BEGIN_DEF_SIGNALS
#include "my_sm1.signals"           // will be expanded to signals enum 
END_DEF_SIGNALS


BEGIN_DEF_STATES
#include "my_sm1.states"            // will be expanded to states enum 
END_DEF_STATES

// Function for initialization of state machine
SM_INIT_DECL;   // is expanded to my_sm1_init(struct os_state_machine_s *sm, int queue_size, void * xdata); 



#endif

my_sm1.c

#include "my_sm1.h" // do not include system.h or state_machine.h before that 

#include "sm_macros_handlers.h"

// Define handlers first 
HANDLER( 1 )      //handler for state 1
// this is expanded to:
// int my_sm1_handler_state_1( os_state_machine_t * sm, os_event_t * event );

{
         switch( event->signal ){
                  case SIGNAL( a )://when signal a appears:
                           return STATE( 2 ); //transition to state 2
                  case SIGNAL( b ):
                           return 0; //do not change the state
                  default:
                           return 0;
         }
}

HANDLER( 2 )
{
         switch( event->signal ){
                  case SIGNAL( a ):
                           return STATE( 1 );
                  case SIGNAL( b ):
                           return STATE( 3 );
                  default:
                           return 0;
         }
}

HANDLER( 3 )
{
         switch( event->signal ){
                  case SIGNAL( a ):
                           return STATE( 1 );
                  case SIGNAL( b ):
                           return STATE( 2 );
                  default:
                           return 0;
         }
}

// here will be handlers list, this must be after handlers 
BEGIN_DEF
#include "my_sm1.states"
END_DEF

// Initialization of state machine, must be at the end of file, this function is generated and will setup all fields 
SM_INIT_DEF( STATE(1) )

When you want to use the state machine you need:

    #include "system.h"
    #include "my_sm1.h"
  
    struct os_state_machine_s my_sm1_aa; //define state machine descriptor
    
    ...
     os_sm_manager_init( 2, 256 );   //initialize state machine manager (only once)
     my_sm1_init( &my_sm1_aa, 10, (void*)xdata ); //initialize state machine, each instantion; xdata - some extra data
     os_sm_manager_sm_activate( &my_sm1_aa );
    ...
    //send event when it is required by:
     os_event_t ev;
     ev.signal = SIGNAL( my_sm1, a );
     os_sm_manager_send_event( &my_sm1_aa, ev );
  or from ISR
     os_sm_manager_send_event_intr( &my_sm1_aa, ev );



Function Documentation

os_result_t os_sm_flush ( os_state_machine_t sm)

Removes all pending events from the queue.

Parameters:
[in]smstate machine
Returns:
OS_STATUS_OK
os_result_t os_sm_process_event ( os_state_machine_t sm)

Process an event if there is any in state machine queue.

Parameters:
[in]smstate machine
Returns:
OS_EMPTY if no event was processed (pending), otherwise OS_STATUS_OK