在很多协议层的实现中,本地状态的管理就可以采用有限状态机,比如TCP的本地连接状态,就构成了一个有限状态机。
本篇首先介绍有限状态机,然后以一个数字输入的例子介绍有限状态机的实现方式。
1. 有限状态机
有限状态机(Finite-State Machine, FSM),又称为有限状态自动机, 是表示某个系统的有限个状态以及在外部事件的作用下在这些状态之间迁移和动作等行为的数学模型。
最简单的有限状态机具有两个状态,比如灯,其两个状态为:开(On)和关(Off)。等也只有两个外部输入事件:开关拨上(Switch Up)和拨下(Switch Down)。
图 1 灯的有限状态机迁移
有限状态机可以用状态迁移表来完整描述。图 1灯的状态迁移表如表 1所示:
表 1 灯的有限状态机迁移表
关(Off) |
开(On) |
|
开关拨上(Switch Up) |
动作1(Action1) |
动作3(Action3) |
开关拨下(Switch Down) |
动作2(Action2) |
动作4(Action4) |
横轴表示灯的所有状态,纵轴表示所有可能的输入事件,其中的在相应的状态下,发生对应的外部事件输入时系统产生的动作。动作一般包括以下几个部分:内部相应的动作、内部状态变量的更新、产生相应的输出、迁移到新的状态。上表中的灯的动作1就包含:接通电源、设内部电源状态为通、产生光线的输出、迁移到开(On)的状态。但并不是在所有的状态收到所有的外部事件都会有这些相应的动作,如上表中的动作2,实际是并不发生具体的动作,也没有状态迁移,因此这是一个空的动作。
现实世界中存在大量具有有限个状态的系统:自动售货机、电梯、交通信号灯、通信协议等。而这些系统基本上都实现为嵌入式系统。
嵌入式系统中的很多行为可以用有限状态机来描述。比如通信子系统(包括串口通信、TCP/IP等从简单到复杂的各种通信子系统)、用户输入等。而且在前面的介绍中,我们知道,嵌入式系统的任务结构是一个接收和处理外部输入的无限循环。在等待外部输入时,任务处于某种状态,而其状态是有限的。因此广义地讲,所有的嵌入式任务都可以看作具有有限个状态,并根据外部输入,在其中迁移的系统,也就是有限状态机。但有些简单的任务只有一个状态,收到外部输入,进行处理,回到初始状态。当然这样的系统就没有必要特意用有限状态机来描述了。
但有限状态机的实现如果不进行仔细的规划,在测试或实际运行当中,就会产生各种问题,而且问题往往与特定的状态和事件有关,不易再现、定位和解决。下面以一个POS的输入为例,来说明有限状态机的实现。
2.POS输入处理的有限状态机实现
有限状态机用<状态、事件>对来分类描述一个系统或子系统的各种行为。系统总是处于有限个状态中的某一个,如果没有外部事件的输入,系统就一直处于该状态中。而事件可以看成是外部世界对该系统的一个触发,触发导致系统的一系列动作,并迁移到一个新的状态。
下面以一个POS机的金额输入窗口为例,描述有限状态机的实现。金额输入窗口需要满足以下条件:
- 一行液晶显示
- 金额起始不能有多个连续0,除非第一个0之后有小数点。00是非法的,0.0是合法的。
- 金额结尾不能有多个连续的0结尾,除非没有小数点。100、1.0、1.20是合法的,1.00是非法的
- 小数点后只能输入两位。1.23是合法的,1.234是非法的。
- 在所有状态支持退格删除。12退格后变为1,1.23退格后变为1.2,1.2退格后变为1.
- 限定总输入长度为20个数字,包括小数点
- 禁止0-9、小数点、退格键以外的任何输入
- 至多只能有一个小数点的输入
从上述要求可以看出,触发事件(外部输入)有以下几类:
- 输入0
- 输入1-9
- 输入小数点
- 输入退格
- 输入其他字符
状态有以下几个:
- 初始态。没有任何输入的状态
- 首字符0态。首字符输入,而且为0
- 首字符非0态。首字符输入,且不为0
- 整数态。首字符不为0,且有多字符(0,1-9)输入
- 小数态。有小数点输入
- 小数后0态。小数位最后字符为0
- 小数后非0态。小数位最后字符非0