State Machines

Warning

This feature is available only with a License - Contact us for more details

Struture

A Finite State Machine is defined in a JSON file with different sections :

  • A list of attributes,
  • A list of states,
  • A list of transitions.

A typical definition has the following structure:

{
    "scripts": string, /* path to where the JS Scripts are stored */
    "states": [
        // array of states
    ],
    "start" : string, /* this is the name of the initial state */
    "transitions" : [
        // array of transitions
    ]
}

Defining states

Each state definition requires the following to be set:

  • The state name, it must be unique in the same state machine,
  • Optional : A path to the JS Script to be executed when the state is active,
  • Optional : A path to the JS Script to be executed when the state will no longer be active because the State Machine is transitionning to another state.

Notes:

  • The script file is by default searched in the current storage (zip, tar, local disk etc.).
  • If the script path starts with http or https, then the system performs a http/https download with a GET method.
{
  "name": string, /* a name for this new state, must be unique */
  "on_enter": string,  /* optional : path to the JS script to be run */
  "on_leave": string  /* optional : path to the JS script to be run */
}

Example :

{
  "name": "init",
  "on_enter": "http://mysite.com/vm/enter.js",
  "on_leave": "leave.js" 
}

Defining Transitions

A transition says :
When the event 'event' is received then the new active state is 'new_active_state'.

{
  "from" : string, /* state unique name where the transition definition has to be added */
  "to"   : string, /* state unique name, new active state when the event is received */
  "on"   : string  /* event name or reg exp to match */
}

For a detailed review of the accepted syntax, see ECMAScript regular expression grammar

Example :

{
"from" : "one",
"to"   : "two",
"on"   : "tick[a-z_]+"
} 

About scripts

  • Any type of script is accepted here, no check is performed
  • A script to be run is passed a specific argument, structure is described below.

for 'on_enter' scripts

{
  "current_state" : string, /* the name of the state calling this script */
  "event_type"   : "enter",
  "fsm_name"   : string, /* the name used when the state machine was created */
  "fsm_uuid"   : string, /* unique ID for this state machine, can be used to call new StateMachine(id) */
  "state_uuid" : string, /* unique ID */
  "trigged_by_event" : string /* what was the event that triggered this enter */
} 

example :

{
  "current_state":"init",
  "event_type":"enter",
  "fsm_name":"test",
  "fsm_uuid":"bb414714-7e0e-4992-bf0e-caf0de486e3b",
  "state_uuid":"30ff07fb-c190-4e81-9e1f-2290010d02a0",
  "trigged_by_event":"reset"
}

for 'on_leave' scripts

{
  "current_state" : string, /* the name of the state calling this script */
  "event_type"   : "enter",
  "fsm_name"   : string, /* the name used when the state machine was created */
  "fsm_uuid"   : string, /* unique ID for this state machine, can be used to call new StateMachine(id) */
  "state_uuid" : string, /* unique ID */
  "trigged_by_event" : string, /* what was the event that triggered this 'leave' */
  "time_spent" : number /* time spent in seconds in this state since 'enter' */
} 

example :

{
 "current_state":"init",
 "event_type":"leave",
 "fsm_name":"test",
 "fsm_uuid":"bb414714-7e0e-4992-bf0e-caf0de486e3b",
 "state_uuid":"30ff07fb-c190-4e81-9e1f-2290010d02a0",
 "time_spent":0.002,
 "trigged_by_event":"ticka"
}

This parameter can be retrieved using the argv() function:

var fsmcall = JSON.stringify( argv(0)) ;
print('The trigger event is ' + fsmcall.trigged_by_event );

Full example

The following example has 3 states : init, one, two.
When the event tick is received, the new active state is one.
When the event tick<and at least one letter from 'a' to 'z' (for example : ticka, tickaa, tickabcd) is received, the new active state is two.

{
   "scripts": "/opt/app/fsm",
   "states": [
    {
        "name": "init",
        "on_enter": "enter.js",
        "on_leave": "leave.js" 
    },
    {
        "name": "one",
        "on_enter": "enter.js",
        "on_leave": "leave.js"
    },
    {
        "name": "two",
        "on_enter": "enter.js",
        "on_leave": "leave.js"
    }
   ],
   "start" : "init",
   "transitions" : [
    {
        "from" : "init",
        "to"   : "one",
        "on"   : "tick"
    },
    {
        "from" : "one",
        "to"   : "two",
        "on"   : "tick[a-z]+"
    }
   ]
}

The two scripts :
enter.js

print('enter !');

for( var i=0 ; i < argc() ; i++ ) {
    print( argv(i));
}

leave.js

print('leave !');

for( var i=0 ; i < argc() ; i++ ) {
    print( argv(i));
}

Using State Machines

The Machines modules must be used to transform a JSON description file to a StateMachine object.

Loading a definition file

Two parameters are required :

  • A string to name the object loaded,
  • The path to the definition file.

Notes:

  • The definition file is by default searched in the current storage (zip, tar, local disk etc.).
  • If the definition path starts with http or https, then the system performs a http/https download with a GET method.
  • Each instance of a loaded State Machine has a unique ID (uuid). It can be shared with other tasks running in the SDRVM to access the same FSM.
  • When loaded by the Machines module, the state machine is not in the initial state. A call to reset() must be performed.
var fsm = Machines.make(string , string);

Example :

var fsm = Machines.make('test','fsmdef.json');
print( 'new FSM loaded, unique ID is :' + fsm.getUUID() );

StateMachine

The StateMachine

Object StateMachine {
 constructor SateMachine( string uuid );
 bool isValid();
 bool reset();
 string getUUID();
 postEvent( string event_name );
};
var fsm = Machines.make('test','fsmdef.json');
print( 'new FSM loaded, unique ID is :' + fsm.getUUID() );
fsm.reset();
fsm.postEvent('xxxx'); // nothing happens 
fsm.postEvent('ticka');// goes to state 'one'
fsm.postEvent('tickb');// goes to state 'two'
Last update: January 8, 2022