良许Linux教程网 干货合集 一款状态机自动生成工具

一款状态机自动生成工具

编写实用的软件系统状态机是一项具有挑战性的任务。尤其是当状态机本身较为复杂时,这一过程更是需要耗费大量的时间和精力。许多有着相关经验的程序员常常形容这一过程为缺乏创意,因为他们需要专注于管理各种状态,而非程序的运行逻辑。

作为一种常见的软件设计模式,不同软件系统的状态机之间会存在一些共性。因此,人们开始尝试开发一些工具,用于自动生成有限状态机的框架代码。在Linux下,FSME(Finite State Machine Editor)是一个不错的选择,提供了可视化的界面。

image-20240104194007034
image-20240104194007034

FSME是一个基于Qt的有限状态机工具,它能够让用户通过图形化的方式来对程序中所需要的状态机进行建模,并且还能够自动生成用C++或者Python实现的状态机框架代码。

下面就以下图中城门的状态机为例,来介绍如何利用FSME来自动生成程序中所需要的状态机代码。

控制城门的状态机

image-20240104194009982
image-20240104194009982

1状态机建模

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

  • 状态建模

在FSME界面左边的树形列表中选择”States”项,然后按下键盘上的Insert键来插入一个新的状态,接着在右下方的”Name”文本框中输入状态的名称,再在右上方的绘图区域单击该状态所要放置的位置,一个新的状态就创建好了。用同样的办法可以添加状态机所需要的所有状态,如下图所示。

状态建模

image-20240104194012889
image-20240104194012889
  • 事件建模

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

image-20240104194015685
image-20240104194015685
  • 转换建模

状态转换是整个建模过程中最重要的一个部分,它用来定义有限状态机中的一个状态是如何切换到另一个状态的。例如,当用来控制城门的状态机处于Opened状态时,如果此时有Close事件产生,那么状态机的当前状态将切换到Closed状态,这样一个完整的过程在状态机模型中可以用closeDoor这样一个转换来进行描述。

要在FSME中添加这样一个转换,首先需要在界面左边的树形列表中选择”States”下的”Opened”项,然后按下键盘上的Insert键来添加一个新的转换,接着在右下角的”Name”文本框中输入转换的名字”closeDoor”,在”Condition”文本框中输入”Close”表明触发该转换的条件是事件Close的产生,在”Target”下拉框中选择”Closed”项表明该转换发生后状态机将被切换到Closed状态,最后再单击”Apply”按钮,一个新的状态转换关系就定义好了,如下图所示。用同样的办法可以添加状态机所需要的所有转换。

转换建模

image-20240104194019534
image-20240104194019534

2 生成状态机框架

使用FSME不仅能够进行可视化的状态机建模,更重要的是它还可以根据得到的模型自动生成用C++或者Python实现的状态机框架。首先在FSME界面左边的树形列表中选择”Root”项,然后在右下角的”Name”文本框中输入状态机的名字”DoorFSM”,再从”Initial State”下拉列表中选择状态”Opened”作为状态机的初始化状态,如图6所示。

设置初始属性

image-20240104194022831
image-20240104194022831

在将状态机模型保存为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”按钮,一个新的输出就创建好了,如图所示。用同样的办法可以添加状态机所需要的所有输出。

添加输出

image-20240104194029001
image-20240104194029001

当所有的输出都定义好之后,接下来就可以为状态机中的每个状态绑定相应的输出。首先在FSME界面左侧的”States”项中选择相应的状态,然后从右下角的”Available”列表框中选择与该状态对应的输出,再单击”

图为状态设置输出

image-20240104194032446
image-20240104194032446

由于对状态机模型进行了修改,我们需要再次生成状态机的框架代码,不过这次不需要加上-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"
#include 

void DoorFSMLogic::enterOpened()
{
    std::cout "Enter Opened state." enterClosed()
{
    std::cout "Enter Closed state." enterLocked()
{
    std::cout "Enter Locked state." enterUnlocked()
{
    std::cout "Enter Unlocked state." 

同样,为了对生成的状态机进行验证,我们还需要手工编写一段测试代码:

/*
 * 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.

本文涉及代码下载: http://www.uml.org.cn/umlcode/code.zip

以上就是良许教程网为各位朋友分享的Linu系统相关内容。想要了解更多Linux相关知识记得关注公众号“良许Linux”,或扫描下方二维码进行关注,更多干货等着你 !

137e00002230ad9f26e78-265x300
本文由 良许Linux教程网 发布,可自由转载、引用,但需署名作者且注明文章出处。如转载至微信公众号,请在文末添加作者公众号二维码。
良许

作者: 良许

良许,世界500强企业Linux开发工程师,公众号【良许Linux】的作者,全网拥有超30W粉丝。个人标签:创业者,CSDN学院讲师,副业达人,流量玩家,摄影爱好者。
上一篇
下一篇

发表评论

联系我们

联系我们

公众号:良许Linux

在线咨询: QQ交谈

邮箱: yychuyu@163.com

关注微信
微信扫一扫关注我们

微信扫一扫关注我们

关注微博
返回顶部