@scion-scxml/scxml
Version:
An implementation of SCXML in JavaScript.
139 lines (122 loc) • 5.6 kB
JavaScript
//load statechart from scxml string; return initial state and token id
//send event to statechart with tokenid
//clean up statechart
const scxml = require('..'),
http = require('http');
var argv = require('optimist')
.alias('l', 'legacy-semantics')
.boolean('l')
.argv;
const interpreterConstructor = argv['legacy-semantics'] ?
require('@scion-scxml/core-legacy').Statechart :
scxml.scion.Statechart;
if(process.env.TEST_SOURCEMAPS_PLUGIN){
require('../plugins/sourcemaps/index'); //load the sourcemaps plugin
}
var sessionCounter = 1, sessions = {}, timeouts = {}, timeoutMs = 5000, sessionRegistry = new Map();
const PORT = process.env.PORT || 42000;
const TEST_ASYNC = typeof process.env.TEST_ASYNC !== 'undefined';
function cleanUp(sessionToken){
delete sessions[sessionToken];
}
http.createServer(function (req, res) {
//TODO: set a timeout to clean up if we don't hear back for a while
var s = "";
req.on("data",function(data){
s += data;
});
req.on("end",function(){
var sessionToken;
try{
var reqJson = JSON.parse(s);
if(reqJson.load){
scxml.urlToModel(reqJson.load,function(err,model){
if(err){
console.error(err.stack);
res.writeHead(500, {'Content-Type': 'text/plain'});
res.end(err.message);
}else{
try {
model.prepare(function(err, fnModel) {
if (err) {
console.error('model preparation error: ' + err, err.stack);
res.writeHead(500, {'Content-Type': 'text/plain'});
res.end(err.message);
return;
}
var interpreter = new interpreterConstructor(fnModel, {
sessionid: sessionCounter,
sessionRegistry : sessionRegistry,
sendAsync : TEST_ASYNC
});
var sessionToken = sessionCounter;
sessionCounter++;
sessions[sessionToken] = interpreter;
if(TEST_ASYNC){
interpreter.startAsync(startCb);
}else{
try {
let conf = interpreter.start();
startCb(null, conf);
} catch (err){
startCb(err);
}
}
function startCb(err, conf){
res.writeHead(200, {'Content-Type': 'application/json'});
res.end(JSON.stringify({
sessionToken : sessionToken,
nextConfiguration : conf
}));
// TODO: timeout should be kicked off before fetch/compilation/preparation
timeouts[sessionToken] = setTimeout(function(){
cleanUp(sessionToken);
},timeoutMs);
}
}, {console : console}, {writeModuleToDisk : true});
} catch(e) {
console.log(e.stack);
console.log(e);
res.writeHead(500, {'Content-Type': 'text/plain'});
res.end(e.message);
}
}
});
}else if(reqJson.event && (typeof reqJson.sessionToken === "number")){
sessionToken = reqJson.sessionToken;
let interpreter = sessions[sessionToken];
let event = reqJson.event;
if(TEST_ASYNC){
interpreter.genAsync(event, genCb);
}else{
try {
let conf = interpreter.gen(event);
genCb(null, conf);
} catch (err){
genCb(err);
}
}
function genCb(err, nextConfiguration){
res.writeHead(200, {'Content-Type': 'application/json'});
res.end(JSON.stringify({
nextConfiguration : nextConfiguration
}));
clearTimeout(timeouts[sessionToken]);
timeouts[sessionToken] = setTimeout(function(){cleanUp(sessionToken);},timeoutMs);
}
}else{
//unrecognized. send back an error
res.writeHead(400, {'Content-Type': 'text/plain'});
res.end("Unrecognized request.\n");
}
}catch(e){
console.error(e.stack);
console.error(e);
res.writeHead(500, {'Content-Type': 'text/plain'});
res.end(e.message);
}
});
}).listen(PORT, function(){
console.log("Express server listening on port " + PORT);
});
console.log('listening on port ' + PORT);