node package manager

fluent-fsm

This awesome library provides an expressive way to specify, run and debug finite state machines in Javascript.

Here are the main features:

  • Express compact state transitions with regular expressions!
  • Debug your FSM on line with a mini-server (powered by socketio) with D3js rendering.

You can see above a live demonstration of what you'll see when you connect to your fsm with a browser [^1]. And yes, you can attach to different FSMs simultaneously by using different ports.

Note: The fsm runs server-side, on node! This is not compatible with browsers at the moment.

Installation

To install, use npm:

npm install fluent-fsm

Usage

Import the prototype in your program:

fsm = require('fsmexpress').fsm;

Create fsm and instantiate states

Create a finite state machine:

fs = new fsm()

Define states (livescript/coffeescript code):

fs.define-as-states([   'II' 'SI' 'PI' 'OI' 
                        'IS' 'SS' 'PS' 'OS' 
                        'IP' 'SP' 'PP' 'OP' 
                        'IC' 'SC' 'PC' 'OC' 'error' ])
                        
fs.define-as-initial('II')

Define transitions

Define a transition (optionally using a regular expression) from all states beginning with I excluding some states (IP, IC) on a specific event (an_event) and register function action_to_trigger to be triggered contextually:

fs.add_rule 
    from: 'I(.+)'
    excluding: 'IP IC' 
    at: 'an_event' 
    jumpTo: 'S-' 
    execute: action_to_trigger  

Note: the target state S- is a state beginning with S and ending with the matched text in (.+) in the from expression. So the above statement will generate only two different state transitions (because 'IP' 'IC' are not allowed from states):

II -> SI
IS -> SS

Unfold and optimize

After the state transitions have been setup, invoke unfold to generate actual state transition rules:

fs.unfold()

Prune states that are not reachable:

fs.optimize()

Linking to an event emitter

To register an event emitter:

fs.registerEventEmitter(the_event_emitter)

the_event_emitter should be a Node Emitter object. The FSM registers its own listeners to enable state transition internal methods. Practically, let's assume that we have the following event emitter:

class tester extends EventEmitter
    
    run_op: ~> 
        @emit 'anEvent'
        setTimeout(@run_tr, 300)
    
    run_tr: ~> 
        @emit 'anEvent2'
        setTimeout(@run_fl, 300)
    
    run_fl: ~> 
        @emit 'anotherEvent'
        setTimeout(@run_op, 300)

Let's register it and start the finite state machine:

tst = new tester()

# Register event emitter and start the fsm
fs.registerEventEmitter(tst)  
fs.start()   

GUI debug

You can have a visual representation of the FSM that is served through a small web service (screenshot above):

red = "#9d261d"
gre = "#46a546"
blu = "#049cdb"

# GUI related stuff..
fs.prepare-emit()
fs.mark transition: '.+',       with-color: 'lightgrey'
fs.mark transition: '.+Open',   with-color: "#gre"
fs.mark transition: '.+Close',  with-dashed-color: "#gre"
fs.mark transition: 'failed.+', with-color: "indianred"
fs.mark state:      '.+',       with-color: 'lightgrey'
fs.mark state:      'error',    with-color: 'indianred'
fs.mark state:      fs.initial, with-color: "#gre"
fs.mark state:      fs.final,   with-color: "lightsteelblue"
console.log fs.data

fs.serve(6970, 'my fsm')

You can see live state transitions (wherever the fsm is, even remotely, provided that the port can be accessed).

License

MIT

[^1]: Event generation is simulated in this page.