statemachine.js 5.83 KB
/******************************************************************************
 * Statemachine implementation
 * ***************************************************************************/

import Component from './component.js';
import TransitionNetwork from './transitionnetwork.js';
import EventLogicParser from './eventlogicparser.js';

/**
 * Describes a statemachine
 * @param obj {object} an object describing a state machine. If obj is empty then the statemachine is empty
 * @class
 */
let Statemachine = new Component.create(function (obj) {
    // dynamic construction: properties are initial state that have properties 
    // that are tokens and value that are the final state
    var initial = "", final = "", transitions = {}, currentState = "", self= this;
    var astTokens = {}; // it keeps AST from event logic expressions 
    var tokenEvents = {}; // it keeps promises for tokens.
    var network = {};
    

    var addToken = function(t) {        
        if ( self.slots.indexOf(t) < 0 ) {
            self.slots.push(t);
            self[t] = function( s ) {
                return function() {
                    self.setToken(s);
                };
            } (t);
        }
    };
    
    /* this function aims at preparing transition networks for a given state*/    
    var setSheet = function(s) {
        // we build promise trees using ast
        var t;
                
        if (transitions.hasOwnProperty(s)) {
            tokenEvents = {};
            for (t in transitions[s]) {
                if (transitions[s].hasOwnProperty(t)) {
                    network = TransitionNetwork.build(astTokens[t],tokenEvents).promise;
                    network.then(
                        function() {
                            var token;
                            // clean up remaining promises
                            for (token in tokenEvents) {
                                if (tokenEvents.hasOwnProperty(token)) tokenEvents[token].abort();                                                                                   
                            }
                            // then activate next sheet 
                            setSheet(transitions[s][t]);
                        }                    
                    );
                }
            }
        }
        
        currentState = s;
        self.emit('requestSheet', currentState);
        if (currentState === final) {
            self.emit('requestTermination');
        }
        
    };
    
    /**
     * Sets the initial state of the statemachine
     * @param string {string} name of the initial state
     */
    this.setInitialState = function (string) {
        initial = string;
        currentState = initial;
    };
    /**
     * Sets the final state of the statemachine
     * @param string {string} name of the final state
     */
    this.setFinalState = function (string) { final = string; };
    /**
     * Adds a transition to the state machine
     * @param start {string} name of the state at the beginning of the transition
     * @param token {string} name of the token triggering the transition
     * @param end {string} name of the state reached at the end of the transition
     */
    this.addTransition = function (start, token, end) {
        var re = /([A-Za-z_]\w*)/g;
        var t, tsd, ts, tsc;
        try {
            
            var tsd = ARCS.EventLogicParser.parse(token);
            if (typeof tsd === "string") {
                addToken(tsd);
            } else {
                while( (t = re.exec(token)) !== null) {
                    addToken(t[0]);
                }                
            }

            astTokens[token] = tsd;
            
            if (transitions[start] === undefined) {
                transitions[start] = {};
            }
            transitions[start][token] = end;
        } catch (e) {  }
    };
    /**
     * Gives a token to the statemachine. According to its list of transitions
     * and the current state, it may trigger a transition
     * @param token {string} name of the token
     */
    this.setToken = function (token) {
        if (tokenEvents.hasOwnProperty(token)) {
            tokenEvents[token].accept();
        }
    };
    /**
     * Sets transitions from a list of transitions
     * @param obj {object[]} list of transitions
     */
    this.setTransitions = function (obj) {
        // this function is no longuer a simple affectation
        // transitions = obj;         
        var p, t, i;
        for (p in obj) {
            if (obj.hasOwnProperty(p)) {
                for (t in obj[p]) {
                    if (obj[p].hasOwnProperty(t)) {
                        this.addTransition(p, t, obj[p][t]);
                    }
                }
            }            
        }
        
        
        // we will temporay dump properties in order to understand how the statemachine is built
        /*
        for (p in transitions) {
            if (transitions.hasOwnProperty(p)) {
                for (t in transitions[p]) {
                    if (transitions[p].hasOwnProperty(t)) {
                        console.log("\t" + p + "\t----\t" + t + "\t--->\t" + transitions[p][t]);
                    }                    
                }
            }
        }*/
    };
    /**
     * Initialize and starts the statemachine, setting its current state to 
     * the initial state (by default, it is the departure of the first transition
     */
    this.start = function () {
        console.log("statemachine", this, initial,obj);
        setSheet(initial);
    };
    

    // INIT CODE
    if (obj !== undefined) {
        initial = obj.initial;
        final = obj.final;
        this.setTransitions(obj.transitions);
        currentState = "";
    }

},
['setToken'],
['requestSheet', 'requestTermination']
);
                                       
export default { StateMachine: StateMachine};