Jean-Yves Didier

changed the statemachine system in order to use promises with event logic

......@@ -84,7 +84,8 @@ module.exports = function (grunt) {
'src/connection.js',
'src/sheet.js',
'src/eventlogicparser.js',
'src/transitionsystem.js',
'src/tokenevent.js',
'src/transitionnetwork.js',
'src/statemachine.js',
'src/application.js',
'src/arcs_module.js',
......
......@@ -1859,6 +1859,81 @@ ARCS.TransitionSystem.build = function( tree, d) {
return res;
};
// this class creates a promise that can be deferred as long as necessary.
// calls to accept or reject will solve the promise.
ARCS.TokenEvent = function() {
var refResolve;
var refReject;
this.promise = new Promise(function(resolve, reject) {
refResolve = resolve;
refReject = reject;
});
this.accept = function() {
refResolve();
};
this.abort = function() {
refReject();
};
};
/* the aim of the transition network is to build a network of promises */
ARCS.TransitionNetwork = function() {
// object storing token events (that is to say references to promises)
this.promise = {};
this.and = function(tn) {
this.promise = Promise.all([this.promise, tn.promise]);
return this;
};
this.or = function(tn) {
this.promise = Promise.race([this.promise, tn.promise]);
return this;
};
};
ARCS.TransitionNetwork.build = function(tree, tokenEvents) {
var res;
var tmpTN;
var rightTN;
if (typeof tree === "string") {
// here we have a terminal string i.e. a token event
var tokenEvent;
if (tokenEvents.hasOwnProperty(tree)) {
tokenEvent = tokenEvents[tree];
} else {
tokenEvents[tree] = tokenEvent = new ARCS.TokenEvent();
}
var tn = new ARCS.TransitionNetwork();
tn.promise = tokenEvent.promise;
return tn;
}
res = ARCS.TransitionNetwork.build(tree[0],tokenEvents);
for (i=1; i < tree.length; i++) {
if (tree[i].hasOwnProperty('and')) {
rightTN = ARCS.TransitionSystem.build(tree[i]['and'], tokenEvents);
tmpTN = res.and(rightTN);
} else {
if (tree[i].hasOwnProperty('or')) {
rightTN = ARCS.TransitionSystem.build(tree[i]['or'], tokenEvents);
tmpTN = res.or(rightTN);
} else {
console.warn('[ARCS] Illegal tree');
}
}
res = tmpTN;
}
return res;
};
/******************************************************************************
* Statemachine implementation
* ***************************************************************************/
......@@ -1871,6 +1946,9 @@ ARCS.Statemachine = new ARCS.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) {
......@@ -1884,6 +1962,39 @@ ARCS.Statemachine = new ARCS.Component.create(function (obj) {
}
};
/* 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 = ARCS.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
......@@ -1907,39 +2018,22 @@ ARCS.Statemachine = new ARCS.Component.create(function (obj) {
var re = /([A-Za-z_]\w*)/g;
var t, tsd, ts, tsc;
try {
var tsd = ARCS.EventLogicParser.parse(token);
if (typeof tsd === "string") {
if (transitions[start] === undefined) {
transitions[start] = {};
}
transitions[start][tsd] = end;
addToken(tsd);
} else {
// we will start to build here a transition system corresponding to
// the "token" object.
// first we look for tokens
while( (t = re.exec(token)) !== null) {
addToken(t[0]);
}
// then we build the transition system.
ts = ARCS.TransitionSystem.build(tsd);
tsc = ts.clone(start + '_' + end + '_');
tsc.renameState(tsc.initial, start);
tsc.renameState(tsc.final, end);
// then, let's fuse the transition systems !
tsc.visitTransitions( function ( s, t, e) {
if (transitions[s] === undefined) {
transitions[s] = {};
}
transitions[s][t] = e;
});
}
}
astTokens[token] = tsd;
if (transitions[start] === undefined) {
transitions[start] = {};
}
transitions[start][token] = end;
} catch (e) { }
};
/**
......@@ -1948,14 +2042,8 @@ ARCS.Statemachine = new ARCS.Component.create(function (obj) {
* @param token {string} name of the token
*/
this.setToken = function (token) {
if (transitions[currentState] !== undefined) {
if (transitions[currentState][token] !== undefined) {
currentState = transitions[currentState][token];
this.emit('requestSheet', currentState);
if (currentState === final) {
this.emit('requestTermination');
}
}
if (tokenEvents.hasOwnProperty(token)) {
tokenEvents[token].accept();
}
};
/**
......@@ -1994,8 +2082,7 @@ ARCS.Statemachine = new ARCS.Component.create(function (obj) {
* the initial state (by default, it is the departure of the first transition
*/
this.start = function () {
currentState = initial;
this.emit('requestSheet', currentState);
setSheet(initial);
};
......@@ -2003,7 +2090,6 @@ ARCS.Statemachine = new ARCS.Component.create(function (obj) {
if (obj !== undefined) {
initial = obj.initial;
final = obj.final;
//transitions = obj.transitions;
this.setTransitions(obj.transitions);
currentState = "";
}
......@@ -2262,7 +2348,7 @@ arcs_module = function(moduleDefinition, deps) {
moduleDefinition.apply(this, deps) : moduleDefinition;
if (mdef === undefined) {
throw new Error("[ARCS] Your module is undefined. Did you forget to export components?\nCode of module follows:\n"+moduleDefinition);
throw new Error("[ARCS] Your module is undefined. Did you forget to export components?\nCode of module follows:\n" +moduleDefinition);
}
for (p in mdef) {
......@@ -2316,7 +2402,7 @@ arcs_module = function(moduleDefinition, deps) {
ARCS.Context.currentContext.addLibraryPromise(
Promise.all(depResolves).then(storeComponents,
function(reason) { console.error("[ARCS] Failed to load dependency " + reason ); })
function(reason) { console.error("[ARCS] Failed to load dependency ", reason ); })
);
......
This diff is collapsed. Click to expand it.
......@@ -33,7 +33,7 @@ arcs_module(function (ARCS) {
console.log("Loop : emitting ", i);
this.emit("newIteration", i);
}
this.emit("sendToken", "end");
this.emit("endLoop");
};
/** @function Loop#newIteration
......@@ -48,7 +48,7 @@ arcs_module(function (ARCS) {
},
"setIterations", //slotList
["sendToken", "newIteration"] // signalList
["endLoop", "newIteration"] // signalList
);
......
......@@ -10,6 +10,9 @@ ARCS.Statemachine = new ARCS.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) {
......@@ -23,6 +26,39 @@ ARCS.Statemachine = new ARCS.Component.create(function (obj) {
}
};
/* 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 = ARCS.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
......@@ -46,39 +82,22 @@ ARCS.Statemachine = new ARCS.Component.create(function (obj) {
var re = /([A-Za-z_]\w*)/g;
var t, tsd, ts, tsc;
try {
var tsd = ARCS.EventLogicParser.parse(token);
if (typeof tsd === "string") {
if (transitions[start] === undefined) {
transitions[start] = {};
}
transitions[start][tsd] = end;
addToken(tsd);
} else {
// we will start to build here a transition system corresponding to
// the "token" object.
// first we look for tokens
while( (t = re.exec(token)) !== null) {
addToken(t[0]);
}
// then we build the transition system.
ts = ARCS.TransitionSystem.build(tsd);
tsc = ts.clone(start + '_' + end + '_');
tsc.renameState(tsc.initial, start);
tsc.renameState(tsc.final, end);
// then, let's fuse the transition systems !
tsc.visitTransitions( function ( s, t, e) {
if (transitions[s] === undefined) {
transitions[s] = {};
}
transitions[s][t] = e;
});
}
}
astTokens[token] = tsd;
if (transitions[start] === undefined) {
transitions[start] = {};
}
transitions[start][token] = end;
} catch (e) { }
};
/**
......@@ -87,14 +106,8 @@ ARCS.Statemachine = new ARCS.Component.create(function (obj) {
* @param token {string} name of the token
*/
this.setToken = function (token) {
if (transitions[currentState] !== undefined) {
if (transitions[currentState][token] !== undefined) {
currentState = transitions[currentState][token];
this.emit('requestSheet', currentState);
if (currentState === final) {
this.emit('requestTermination');
}
}
if (tokenEvents.hasOwnProperty(token)) {
tokenEvents[token].accept();
}
};
/**
......@@ -133,8 +146,7 @@ ARCS.Statemachine = new ARCS.Component.create(function (obj) {
* the initial state (by default, it is the departure of the first transition
*/
this.start = function () {
currentState = initial;
this.emit('requestSheet', currentState);
setSheet(initial);
};
......@@ -142,7 +154,6 @@ ARCS.Statemachine = new ARCS.Component.create(function (obj) {
if (obj !== undefined) {
initial = obj.initial;
final = obj.final;
//transitions = obj.transitions;
this.setTransitions(obj.transitions);
currentState = "";
}
......
// this class creates a promise that can be deferred as long as necessary.
// calls to accept or reject will solve the promise.
ARCS.TokenEvent = function() {
var refResolve;
var refReject;
this.promise = new Promise(function(resolve, reject) {
refResolve = resolve;
refReject = reject;
});
this.accept = function() {
refResolve();
};
this.abort = function() {
refReject();
};
};
\ No newline at end of file
ARCS.TokenManager = function() {
var expressions = {};
var collectedTokens = {};
var TokenNode = function() {
var child = null;
var next = null;
var token = null;
var op = null;
this.or = function() {
};
this.and = function() {
};
this.eval = function() {
if (op !== null) {
} else {
}
};
};
this.addExpression = function (expr) {
try {
var tsd = ARCS.EventLogicParser.parse(expr);
} catch (e) { }
};
this.collect = function (token) {
collectedTokens[token] = true;
};
this.clearTokens = function() {
collectedTokens = {};
};
this.checkExpression = function(expr) {
};
};
\ No newline at end of file
/* the aim of the transition network is to build a network of promises */
ARCS.TransitionNetwork = function() {
// object storing token events (that is to say references to promises)
this.promise = {};
this.and = function(tn) {
this.promise = Promise.all([this.promise, tn.promise]);
return this;
};
this.or = function(tn) {
this.promise = Promise.race([this.promise, tn.promise]);
return this;
};
};
ARCS.TransitionNetwork.build = function(tree, tokenEvents) {
var res;
var tmpTN;
var rightTN;
if (typeof tree === "string") {
// here we have a terminal string i.e. a token event
var tokenEvent;
if (tokenEvents.hasOwnProperty(tree)) {
tokenEvent = tokenEvents[tree];
} else {
tokenEvents[tree] = tokenEvent = new ARCS.TokenEvent();
}
var tn = new ARCS.TransitionNetwork();
tn.promise = tokenEvent.promise;
return tn;
}
res = ARCS.TransitionNetwork.build(tree[0],tokenEvents);
for (i=1; i < tree.length; i++) {
if (tree[i].hasOwnProperty('and')) {
rightTN = ARCS.TransitionSystem.build(tree[i]['and'], tokenEvents);
tmpTN = res.and(rightTN);
} else {
if (tree[i].hasOwnProperty('or')) {
rightTN = ARCS.TransitionSystem.build(tree[i]['or'], tokenEvents);
tmpTN = res.or(rightTN);
} else {
console.warn('[ARCS] Illegal tree');
}
}
res = tmpTN;
}
return res;
};
\ No newline at end of file
/*
* The purpose of this class is to build a transition system in order to fully
* ascertain some "event" logic
*/
ARCS.TransitionSystem = function(a, b, c) {
this.initial = "" ;
this.final = "";
var transitions = {};
if ( a !== undefined) {
if ( (b === undefined || c === undefined)) {
this.initial = "a$";
this.final = "z$";
transitions[this.initial] = {};
transitions[this.initial][a] = this.final;
} else {
this.initial = a;
this.final = c;
transitions[this.initial] = {};
transitions[this.initial][b] = this.final;
}
}
this.visitTransitions = function( closure ) {
var p,t;
for (p in transitions) {
if (transitions.hasOwnProperty(p)) {
for (t in transitions[p]) {
if (transitions[p].hasOwnProperty(t)) {
closure(p,t,transitions[p][t]);
}
}
}
}
}
this.clone = function(pf) {
// checked
var prefix = pf || "";
var res = new ARCS.TransitionSystem();
res.initial = prefix + this.initial;
res.final = prefix + this.final;
this.visitTransitions( function(s, t, e) {
res.addTransition(prefix + s, t, prefix + e);
});
return res;
};
this.fuse = function(ts, i, f) {
// checked
var res = ts.clone();
this.visitTransitions( function(s, t, e) {
res.addTransition(s, t, e);
});
if (i === undefined) {
res.initial = this.initial;
} else {
res.initial = i;
}
if (f === undefined) {
res.final = this.final;
} else {
res.final = f;
}
return res;
};
this.hasState = function(s) {
if (this.initial === s || this.final === s || transitions.hasOwnProperty(s)) {
return true;
}
try {
this.visitTransitions( function (s1, t, e) {
if ( e === s) throw '!';
});
} catch ( e ) {
if (e === '!')
return true;
}
return false;
};
this.renameState = function (currentName, newName) {
if (this.hasState(newName)) {
// this is bad !
console.error("This transition system is corrupt !");
}
if (this.initial === currentName) {
this.initial = newName;
}
if (this.final === currentName) {
this.final = newName;
}
if (transitions.hasOwnProperty(currentName)) {
transitions[newName] = transitions[currentName];
delete transitions[currentName];
}
this.visitTransitions( function (s, t, e) {
if ( e === currentName ) {
transitions[s][t] = newName;
}
});
};
this.dump = function () {
console.log('@ = ' + this.initial + ', + = ' + this.final);
this.visitTransitions( function(s, t, e) {
console.log(' ' + s + '\t---\t'+ t +'\t-->\t' + e);
});
};
this.getStates = function() {
// check
var states = [];
// then we start looking for states
this.visitTransitions(function (s, t, e) {
if (states.indexOf(s) < 0) {
states.push(s);
}
if (states.indexOf(e) < 0) {
states.push(e);
}
});
return states;
}
this.addTransition = function( start, token, end) {
if ( ! transitions.hasOwnProperty(start)) {
transitions[start] = {};
}
transitions[start][token] = end;
};
// let's make a AND operation
this.and = function(ts) {
//check
// letters will create disambiguation
var i;
// first, let's create two identical trees
var leftTS1 = this.clone('i');
var leftTS2 = this.clone('j');
var rightTS;
// then, we fuse the trees together.
var res = leftTS1.fuse(leftTS2, leftTS1.initial, leftTS2.final);
var tmpFuse;
var states = this.getStates();
for( i = 0; i < states.length; i++) {
rightTS = ts.clone('k'+i);
rightTS.renameState(rightTS.initial, 'i'+ states[i]);
rightTS.renameState(rightTS.final, 'j'+ states[i]);
tmpFuse = res.fuse(rightTS);
res = tmpFuse;
}
return res;
};
// let's make a OR operation
this.or = function(ts) {
// checked
var leftTS = this.clone('s');
var rightTS = ts.clone('t');
rightTS.renameState(rightTS.initial, leftTS.initial);
rightTS.renameState(rightTS.final, leftTS.final);
return leftTS.fuse(rightTS);
};
};
// tree is the one obtained by parsing expressions
ARCS.TransitionSystem.build = function( tree, d) {
var depth = d || 1;
var counter = 1;
var i;
var res;
var tmpTS;
var rightTS;
if (typeof tree === "string") {
return new ARCS.TransitionSystem(tree).clone('w0d' + depth);
}
// TODO: depth is not handled in subexpression prefixes.
// This may results in name clashes for intermediate transformations (or not)
res = ARCS.TransitionSystem.build(tree[0], depth + 1);
for (i=1; i < tree.length; i++) {
if (tree[i].hasOwnProperty('and')) {
rightTS = ARCS.TransitionSystem.build(tree[i]['and'], depth + 1).clone('w' + counter++);
tmpTS = res.and(rightTS);
} else {
if (tree[i].hasOwnProperty('or')) {
rightTS = ARCS.TransitionSystem.build(tree[i]['or'], depth + 1).clone('w' + counter++);
tmpTS = res.or(rightTS);
} else {
console.warn('[ARCS] Illegal tree');
}
}
res = tmpTS;
}
return res;
};
\ No newline at end of file
......@@ -25,7 +25,7 @@
],
"connections" : [
{ "source":"loop", "signal":"newIteration", "destination":"dint", "slot":"display" },
{ "source":"loop", "signal":"sendToken", "destination":"statemachine", "slot":"end" }
{ "source":"loop", "signal":"endLoop", "destination":"statemachine", "slot":"end" }
],
"cleanups" : []
},
......@@ -36,7 +36,7 @@
],
"connections" : [
{ "source":"loop", "signal":"newIteration", "destination":"dint", "slot":"display" },
{ "source":"loop", "signal":"sendToken", "destination":"statemachine", "slot":"end" }
{ "source":"loop", "signal":"endLoop", "destination":"statemachine", "slot":"end" }
],
"cleanups" : []
}
......