控制城门的状态机
1状态机建模
首先运行fsme命令来启动状态机编辑器,然后单击工具栏上的"New"按钮来创建一个新的状态机。FSME中用于构建状态机的基本元素一共有五种:事件(Event)、输入(Input)、输出(Output)、状态(State)和转换(Transition),在界面左边的树形列表中可以找到其中的四种。- 状态建模

-
事件建模
在FSME界面左边的树形列表中选择"Events"项,然后按下键盘上的Insert键来添加一个新的事件,接着在右下方的"Name"文本框中输入事件的名称,再单击"Apply"按钮,一个新的事件就创建好了。
用同样的办法可以添加状态机所需要的所有事件,如下图所示。

-
转换建模

2 生成状态机框架
使用FSME不仅能够进行可视化的状态机建模,更重要的是它还可以根据得到的模型自动生成用C++或者Python实现的状态机框架。首先在FSME界面左边的树形列表中选择"Root"项,然后在右下角的"Name"文本框中输入状态机的名字"DoorFSM",再从"Initial State"下拉列表中选择状态"Opened"作为状态机的初始化状态,如图6所示。设置初始属性
在将状态机模型保存为door.fsm文件之后,使用下面的命令可以生成包含有状态机定义的头文件:
[xiaowp@linuxgam code]$ fsmc door.fsm -d -o DoorFSM.h进一步还可以生成包含有状态机实现的框架代码:
[xiaowp@linuxgam code]$ fsmc door.fsm -d -impl DoorFSM.h -o DoorFSM.cpp如果想对生成的状态机进行验证,只需要再手工编写一段用于测试的代码就可以了:
/* * TestFSM.cpp * 测试生成的状态机框架 */ #include "DoorFSM.h" int main() { DoorFSM door; door.A(DoorFSM::Close); door.A(DoorFSM::Lock); door.A(DoorFSM::Unlock); door.A(DoorFSM::Open); }有限状态机是由事件来进行驱动的,在FSME生成的状态机框架代码中,方法A()可以被用来向状态机发送相应的事件,从而提供状态机正常运转所需要的"动力"。状态机负责在其内部维护一个事件队列,所有到达的事件都会先被放到事件队列中进行等候,从而能够保证它们将按照到达的先后顺序被依次处理。在处理每一个到达的事件时,状态机都会根据自己当前所处的状态,检查与该状态对应的转换条件是否已经被满足,如果满足的话则激活相应的状态转换过程。使用下面的命令能够将生成的状态机框架和测试代码编译成一个可执行文件:
[xiaowp@linuxgam code]$ g++ DoorFSM.cpp TestFSM.cpp -o fsm由于之前在用fsmc命令生成状态机代码时使用了-d选项,生成的状态机框架中会包含一定的调试信息,包括状态机中每次状态转换时的激活事件、转换前的状态、所经历的转换、转换后的状态等,如下所示:
[xiaowp@linuxgam code]$ ./fsm DoorFSM:event:'Close' DoorFSM:state:'Opened' DoorFSM:transition:'closeDoor' DoorFSM:new state:'Closed' DoorFSM:event:'Lock' DoorFSM:state:'Closed' DoorFSM:transition:'lockDoor' DoorFSM:new state:'Locked' DoorFSM:event:'Unlock' DoorFSM:state:'Locked' DoorFSM:transition:'unlockDoor' DoorFSM:new state:'Unlocked' DoorFSM:event:'Open' DoorFSM:state:'Unlocked' DoorFSM:transition:'openDoor' DoorFSM:new state:'Opened'
3定制状态机
目前得到的状态机已经能够响应来自外部的各种事件,并适当地调整自己当前所处的状态,也就是说已经实现了状态机引擎的功能,接下来要做的就是根据应用的具体需求来进行定制,为状态机加入与软件系统本身相关的那些处理逻辑。在FSME中,与具体应用相关的操作称为输出(Output),它们实际上就是一些需要用户给出具体实现的虚函数,自动生成的状态机引擎负责在进入或者退出某个状态时调用它们。仍然以控制城门的那个状态机为例,假设我们希望在进入每个状态时都添加一部分处理逻辑。首在FSME界面左边的树形列表选择"Outputs"项,然后按下键盘上的Insert键来添加一个新的输出,接着在右下方的"Name"文本框中输入相应的名称,再单击"Apply"按钮,一个新的输出就创建好了,如图所示。用同样的办法可以添加状态机所需要的所有输出。添加输出

由于对状态机模型进行了修改,我们需要再次生成状态机的框架代码,不过这次不需要加上-d参数:
[xiaowp@linuxgam code]$ fsmc door.fsm -o DoorFSM.h [xiaowp@linuxgam code]$ fsmc door.fsm -d -impl DoorFSM.h -o DoorFSM.cpp我们在新的状态机模型中添加了enterOpend、enterClosed、enterLocked和enterUnlocked四个输出,因此生成的类DoorFSM中会包含如下几个纯虚函数:
virtual void enterOpened() = 0; virtual void enterLocked() = 0; virtual void enterUnlocked() = 0; virtual void enterClosed() = 0;显然,此时生成的状态机框架不能够再被直接编译了,我们必须从类DoorFSM派生出一个子类,并提供对这几个纯虚函数的具体实现:
/* * DoorFSMLogic.h * 状态机控制逻辑的头文件 */ #include "DoorFSM.h" class DoorFSMLogic : public DoorFSM { protected: virtual void enterOpened(); virtual void enterLocked(); virtual void enterUnlocked(); virtual void enterClosed(); };正如前面所提到过的,这几个函数实际上代表的正是应用系统的处理逻辑,作为例子我们只是简单地输出一些提示信息:
/* * DoorFSMLogic.cpp * 状态机控制逻辑的实现文件 */ #include "DoorFSMLogic.h" #includevoid DoorFSMLogic::enterOpened() { std::cout << "Enter Opened state." << std::endl; } void DoorFSMLogic::enterClosed() { std::cout << "Enter Closed state." << std::endl; } void DoorFSMLogic::enterLocked() { std::cout << "Enter Locked state." << std::endl; } void DoorFSMLogic::enterUnlocked() { std::cout << "Enter Unlocked state." << std::endl; }同样,为了对生成的状态机进行验证,我们还需要手工编写一段测试代码:
/* * TestFSM.cpp * 测试状态机逻辑 */ #include "DoorFSMLogic.h" int main() { DoorFSMLogic door; door.A(DoorFSM::Close); door.A(DoorFSM::Lock); door.A(DoorFSM::Unlock); door.A(DoorFSM::Open); }使用下面的命令能够将生成的状态机框架和测试代码编译成一个可执行文件:
[xiaowp@linuxgam code]$ g++ DoorFSM.cpp DoorFSMLogic.cpp TestLogic.cpp -o logic运行结果如下所示:
[xiaowp@linuxgam code]$ ./logic Enter Closed state. Enter Locked state. Enter Unlocked state. Enter Opened state.