读了阮哥的一篇关于JavaScript FSM库的介绍,然后就自己写了一个红绿灯过过瘾
demo链接: Demo - TrafLights
# 有限自动状态机(FSM) ↵
状态机是一种数学模型,可以特化为电子设计里的诸多奇技,也可以作为编程的淫巧使用。
它反映从系统开始到现在时刻的输入变化。转移指示状态变更,并且用必须满足确使转移发生的条件来描述它。动作是在给定时刻要进行的活动的描述。
这是 Wiki 上的简单描述 他有这样几个要点:
- 它是变化的,由
输入
和当前状态
确定 - 必须严格定义任意 A 状态转化到 B 状态的
条件
因此在写这个红绿灯的时候必须时刻注意:
- 状态转换是有方向的
- 注意库的使用
- 严格考虑所有情况
# 库 ↵
javascript-state-machine 是一个用于构造 FSM 的开源JavaScript库
Github上有它详细的教程
值得一提的是 Wiki上的这个描述:
- 进入动作(entry action):在进入状态时进行
- 退出动作:在退出状态时进行
- 输入动作:依赖于当前状态和输入条件进行
- 转移动作:在进行特定转移时进行
试把上面的
动作
改成 事件
或者 回调
- 进入事件:在进入状态时进行
- 退出事件:在退出状态时进行
- 输入事件:依赖于当前状态和输入条件进行
- 转移事件:在进行特定转移时进行
我选择的这个库也提供了类似的方法来提供回调来充当这些事件的响应函数,这是本库的核心思想
# 时钟信号 (红绿灯用的计时器) ↵
自写了一个 time.js 每+1s执行一次回调,每隔 30s 给回调传递 OF
用法类似这样:
00time.go(function(now, OF){ 01 if (OF){ 02 console.log('OVERFLOW') 03 } else { 04 console.log('now time: ' + now.toString()); 05 } 06}
# FSM核心 ↵
以下是用 JS 写的 状态描述 …
00var fsm = StateMachine.create({ 01 initial: 'green', // 初始状态 02 03 events: [ // 从 A 到 B 的事件名 04 // 正常情况下的红绿灯变化 05 { name: 'warn', from: 'green', to: 'yellow' }, 06 { name: 'panic', from: 'yellow', to: 'red' }, 07 { name: 'calm', from: 'red', to: 'yellow' }, 08 { name: 'clear', from: 'yellow', to: 'green' }, 09 // 以下三个是强制转换颜色 10 { name: 'toRed', from: ['green', 'red', 'yellow'], to: 'red' }, 11 { name: 'toYellow', from: ['green', 'red', 'yellow'], to: 'yellow' }, 12 { name: 'toGreen', from: ['green', 'red', 'yellow'], to: 'green' } 13 ] 14}); 15 16// 每次事件发生前 应该把灯一瞬间熄灭 17fsm.onbeforeevent = function(ev){ 18 $('.cir').attr('class', 'cir'); 19} 20 21// 然后再渲染上颜色 22fsm.onafterevent = function(ev){ 23 $('#' + fsm.current).addClass('color-'+fsm.current); 24}
# 实装实弹 ↵
00time.go(function(now, OF){ 01 // 剩余时间数字提示 (不足10前面补0) 02 $('time').html((now<10?('0'+now.toString()):now.toString()) + '<br />'); 03 04 if (OF){ // 这里跟 Verilog HDL 里 FSM 的 Three Always 写法很像 05 // toNextState(); 06 switch(fsm.current){ 07 case 'red': 08 fsm.calm(); 09 10 // 注意黄灯自己会一秒后转移到别的状态 11 setTimeout(function(){ 12 fsm.clear(); 13 }, 1000); 14 break; 15 case 'yellow': 16 // do nothing 17 break; 18 case 'green': 19 fsm.warn(); 20 // 注意黄灯自己会一秒后转移到别的状态 21 setTimeout(function(){ 22 fsm.panic() 23 }, 1000); 24 break; 25 default: 26 // reset 27 } 28 } else { 29 // do nothing 30 } 31});
# 事后 ↵
总觉得我这个 FSM 并没有很好的体现输出
并没有下面这种公式的优雅:
Output = Fsm(State);
花了些时间处理响应式问题,主要还是灯对于手机来说太大了… 于是写小一些…
Θ..Θ