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.0 Jun. 9, 2011
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 app_specific/include/sm_signals_user.h
2. define states of your machine (e.g. in my_state_machine.h)
3. provide initialization of the state machine structure, including ring buffer creation as the event queue
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.

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.


Example of state machine definition and usage

[1] app_specific/include/sm_signals_user.h

#ifndef USER_SM_SIGNALS_H_
#define USER_SM_SIGNALS_H_
 it is included into #sm_types.h , in signal enumeration type. 
  // example of user signals: 
sig_a,
sig_b,

#endif //USER_SM_SIGNALS_H_ 


[2] my_state_machine.h

 #ifndef MY_STATE_MACHINE_H_
 #define MY_STATE_MACHINE_H_

 // Enumerated type for definition of states of the machine  
typedef enum my_sm1_states{ 
    reserved=0, 
    my_sm1_state1, 
    my_sm1_state2, 
    my_sm1_state3,
    my_sm1_state_nb //used for determination number of states
    } my_sm1_states_t;
 
 // Function for initialization of state machine
 os_result_t my_sm1_init( struct os_state_machine_s *sm, int queue_size, void * xdata_a, int initial_state );
 #endif //MY_STATE_MACHINE_H_

my_state_machine.c

#include "sm_types.h"
#include "sm_manager.h"
#include "ring_buffer_ev.h"
#include "my_state_machine.h"

#include "stdio.h"
#include "stdlib.h"


//handlers - forward declaration
int my_sm1_handler1( os_state_machine_t * sm, os_event_t * event );
int my_sm1_handler2( os_state_machine_t * sm, os_event_t * event );
int my_sm1_handler3( os_state_machine_t * sm, os_event_t * event );

//table of handlers, will be attached to the state machine descriptor
os_sm_handler_t my_sm1_handlers[] = {
    my_sm1_handler1, 
    my_sm1_handler2, 
    my_sm1_handler3};
//number of handlers must be consistent with number of states

//initialization of state machine [3]
os_result_t my_sm1_init( struct os_state_machine_s *sm, int queue_size, void * xdata_a, int initial_state )
{
    os_result_t res;
    sm->handlers = my_sm1_handlers;
    sm->state = initial_state;
    sm->states_nb = my_sm1_state_nb;
    res = os_ringbuf_create_ev( &sm->ev_queue, queue_size );
    if( res != OS_STATUS_OK )
        return res;   
    sm->xdata = xdata_a;        
    os_sm_manager_sm_activate( sm );
    return OS_STATUS_OK;
}    

// the handlers that process events in each state [4]. In this simple example actions performed in the states are
// only printing a text, in a real state machine it can be any other code.
int my_sm1_handler1( os_state_machine_t * sm, os_event_t * event )
{
    printf("[%s]", sm->xdata );
    switch( event->signal ){
        case sig_a:
            printf("state 1: sig_a --> state2\n"); 
            return my_sm1_state2;
        case sig_b:
            printf("state 1: sig_b\n");
            return 0;
        default:
            printf("state 1: sig_?\n");
            return 0;
    }
}
int my_sm1_handler2( os_state_machine_t * sm, os_event_t * event )
{
    printf("[%s]", sm->xdata );
    switch( event->signal ){
        case sig_a:
            printf("state 2: sig_a --> state1\n"); 
            return my_sm1_state1;
        case sig_b:
            printf("state 2: sig_b --> state3\n"); 
            return my_sm1_state3;
        default:
            printf("state 2: sig_?\n");
            return 0;
    }
}

int my_sm1_handler3( os_state_machine_t * sm, os_event_t * event )
{
    printf("[%s]", sm->xdata );
    switch( event->signal ){
        case sig_a:
            printf("state 3: sig_a --> state1\n"); 
            return my_sm1_state1;
        case sig_b:
            printf("state 3: sig_b --> state2\n"); 
            return my_sm1_state2;
        default:
            printf("state 3: sig_?\n");
            return 0;
    }
}

When you want to use the state machine you need:

    #include "sm_manager.h"
    #include "sm_types.h"
    #include "my_state_machine.h"
  
    struct os_state_machine_s my_sm1; //define state machine descriptor
    
    ...
     os_sm_manager_init( 2, 256 );   //initialize state machine manager (only once)
     my_sm1_init( &my_sm1, 10, "some_extra_data 1", my_sm1_state1 ); //initialize state machine, each instantion
     
    ...
    //send event when it is required by:
     os_event_t ev;
     ev.signal = sig_a;
     os_sm_manager_send_event( &my_sm1, 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