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