Created
November 3, 2012 23:00
-
-
Save sheenobu/4009226 to your computer and use it in GitHub Desktop.
Finite State Machine learning with state-machine.js and nodejs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Modelling traffic lights as a persistent finite state machine using | |
* state-machine.js and nodejs. | |
*/ | |
/* | |
* This is a specialization of Object.extend that checks if the | |
* destination function exists, and if so, merges the two functions | |
* into one function. This allows two conflicting functions | |
* to become a series of procedural steps. | |
*/ | |
Object.join_function_table = function(destination, source) { | |
for (var property in source) { | |
if (source.hasOwnProperty(property)) { | |
if(destination.hasOwnProperty(property)) { | |
orig_dest = destination[property] | |
destination[property] = function() { | |
i = orig_dest.apply(this,arguments) | |
if(i == null || i == true) return source[property].apply(this,arguments) | |
} | |
}else{ | |
destination[property] = source[property]; | |
} | |
} | |
} | |
return destination; | |
}; | |
StateMachine = require('./state-machine').StateMachine | |
/* | |
* Our persistence function, implemented as a dummy SQL driver. | |
*/ | |
PersistentStateMachine = function(table, item, Constructor) { | |
return Constructor({ onchangestate: function(event, from, to ) { | |
console.log("[SQL DUMMY] UPDATE " + table + " SET state = '" + to + "' WHERE id = '" + item + "';") | |
}}) | |
} | |
/* | |
* Our traffic light class, built as | |
* a statemachine with additional functions for | |
* control. | |
*/ | |
function TrafficLight(cb) { | |
st = StateMachine.create({ | |
initial: 'stopped', | |
events: [ | |
{ name: 'break', to: 'broken' }, | |
{ name: 'fix', from: 'broken', to: 'red' }, | |
{ name: 'stop', to: 'stopped' }, | |
{ name: 'start', from: 'stopped', to: 'red' }, | |
{ name: 'next', from: 'red', to: 'green' }, | |
{ name: 'next', from: 'green', to: 'yellow' }, | |
{ name: 'next', from: 'yellow', to: 'red' }, | |
{ name: 'next', from: 'stopped', to: 'stopped' } | |
], | |
callbacks: Object.join_function_table( { | |
onafterstart:function(event,from,to) { | |
this.loop() | |
}, | |
onafternext:function(event,from,to,cb) { | |
cb() | |
}, | |
onchangestate: function(event,from,to) { | |
} | |
},cb) | |
} | |
) | |
/* The amount of time to wait between state changes is computed | |
* based on the current state. You can also pull from external state, such as | |
* amount of traffic, state of /other/ lights, etc | |
*/ | |
st.timeout = function() { | |
if(this.current == 'red') { | |
return 2000; | |
} | |
if(this.current == 'yellow') { | |
return 500; | |
} | |
if(this.current == 'green') { | |
return 4000; | |
} | |
if(this.current == 'broken' || this.current == 'stopped') { | |
return 0; | |
} | |
} | |
/* | |
* This is the recursive loop which sets up a heterogenous interval | |
* based on the state of the traffic light | |
*/ | |
st.loop = function() { | |
var self = this | |
setTimeout(function() { | |
self.next(function() { | |
self.loop() | |
}) | |
}, this.timeout()) | |
} | |
return st; | |
} | |
/* this is our traffic light instance */ | |
var traffic_light = PersistentStateMachine("traffic_lights","1",TrafficLight) | |
i = setInterval(function() { | |
console.log("another 5seconds (running at a higher speed)") | |
},500); | |
/* This stops the traffic light after 8 seconds and stops the debug counter */ | |
(function(dbg) { | |
setTimeout(function() { | |
traffic_light.stop() | |
clearInterval(dbg); | |
process.exit() // wwe shouldn't need this but it won't stop without it! | |
},8000) | |
})(i) | |
traffic_light.start() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment