Automata is a Deterministic Finite State Machine automata framework featuring: a JSON based automata creation, timed transitions, sub-states, guards, FSM registry, etc.
#Automata - A finite state machine framework.
Automata is a formal finite state machine (FSM) framework. Its aims at offering a totally decoupled management of logic and data storage. It features all the needed elements to have a modern and flexible finite state machine framework like
Automata is valid to be used directly as a node js module. Exposes an object with two functions:
First of all, one or more FSM must be registered in the system. A minimal state machine could be:
fsmContextregisterFSM// FSM registry namename : "Test"logic : constructor_func// Statesstate :name : "1"initial : truename : "2"name : "3"// transitionstransition :event : "12"from : "1"to : "2"event : "23"from : "2"to : "3";
To start using this machine, a FSM session must created out of a registered FSM. For example:
var session= fsmContextcreateSession"Test";
To send notification events to a session object, call either:
// asynchronous call ( setTimeout with 0 )sessiondispatch msgId: "12" ;// synchronous callsessionprocessMessage msgId: "12" ;
These methods accept as a valid message any object which contains a field called msgId. To trigger a transition, any message object's msgId value must be the value defined in the event attribute present in the transition FSM definition block.
The FSM logic and state is ketp apart on a custom object the developer supplies to the FSM via the logic value. This must be a constructor function and will create a new object per session. Methods on this object can be automatically invoked by the framework by assigning them to the activity hook values available on State and Transition objects. The hooks points can be either an string, identifying a function in the logic object, or a callback function of the form:
In either case, the calling this scope will be the logic object itself.
Automata offers many activy hooks on its activity. The following hooks are available:
Those hooks are defined in the FSM JSON definition as in the example:
/*** Define a logic constructor function.*/thiscount= 0;console.log"Enter state B";thiscount++;;console.log"Exit state A";;console.log"Transition fire code";return this;/*** Define a FSM*/fsmContextregisterFSM// FSM registry namename : "Test"logic : constructor_func// Statesstate :name : "A"initial : trueonExit : "A_onExit"name : "B"onEnter : "B_onEnter"name : "C"transition :event : "AB"from : "A"to : "B"onTransition: "TR_AB"event : "BC"from : "B"to : "C";var session= fsmContextcreateSession"Test";sessiondispatch msgId: "AB" ;// this will print:// Exit state A// Transition fire code// Enter state B
Guard prevent a transition from being fired. In Automata there're two available guard points out of the box. One on preTransitionFire and the otheron postTransitionFire. The difference is straight:
The way to instrument the engine that a guard veto has been launched, will be by throwing an exception from the pre/post-transition functions. Those functions are optional, and must be set in the "transition" block of the FSM definition as follows:
fsmContextregisterFSMtransition :event : "AB"from : "A"to : "B"onTransition : "TR_AB"onPreGuard : "pre_guard_function"onPostGuard : "post_guard_function"
Automata offers out of the box timed transitions by defining an onTimer block in a state definition. For example:
fsmContextregisterFSMstate :name : "1"initial : trueonTimer :timeout : 2000event : "12";
This instruments the engine that after 2 seconds of entering this state, a transition by a transition with an event id like "12" will be sent to the FSM session. The timer is handled automatically, and set/canceled on state enter/exit respectively.
Automata allows to nest as much as needed substates. In fact, by defining a single FSM, the engine stacks two levels, one for the FSM, and the other, initially for the FSM's initial state. To define different levels, you must register more than one FSM in the registry, and then reference one of them as a substate in the "state" section:
fsmContextregisterFSMstate :name : "a"initial : trueonEnter : "enter_a"onExit : "exit_a"subState: "STest";
Then, the transition section will identify this FSM as a substate by its name, STest. A "subState" can't have a regular name, nor onEnter/onExit functions.
The stacking of different subStates is done transparently, and they are handled by the "session" object. For each stacked level, a FSM.Context object is created. A context object is just a holder for the current state for each nesting level.
##Transition from Substates
The way in which Automata manages state changes is made hierarchycally. That means, the engine will try to find a suitable transition for a given incoming message regardless of its nesting level. So for any given FSM stacktrace, the engine will traverse upwards trying to find a suitable state to fire a transition for the dispatched event.
(Warning, offending ascii art. States between parenthesis, transitions between square brackets.)
(ROOT) | | v (S1) --[T_S1_S2]--> (SUB_STATE) --[T_SS_S3]--> (S3) | +---> (SS1) --[TSS1_SS2]--> (SS2)
For example, given the previous example,
sessiondispatch msgId : "T_S1_S2" ;
means the session is on state SS1, and the stackTrace will be the following:
ROOT, SUB_STATE, SS1
sessiondispatch msgId : "T_SS_S3" ;
on the session at state SS1, SS1 will be removed from the stack, and the session will transize to S3 state. Additionally, this session will be finished since S3 is a final State (this nesting level will be removed from the stack too), and ROOT is also a final state, causing the session to be emptied.
Any FSM session activity can be monitored by adding a listener. For example:
sessionaddListenerconsole.log"SessionListener contextCreated";console.log"SessionListener contextDestroyed";console.log"SessionListener finalStateReached";console.log"SessionListener stateChanged";console.log"SessionListener customEvent";;
The obj parameter for each listener object function contains the following parameters:
In all cases:
The preferred way for sending custom events will by calling:
sessionfireCustomEvent a_json_object ;
and have a listener/observer object attached to the sending FSM session. This method will be notified on the method