Skip to content

Instantly share code, notes, and snippets.

@sheenobu
Created November 3, 2012 23:00
Show Gist options
  • Save sheenobu/4009226 to your computer and use it in GitHub Desktop.
Save sheenobu/4009226 to your computer and use it in GitHub Desktop.
Finite State Machine learning with state-machine.js and nodejs
/*
* 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