SMC 与有限状态机简单应用

系列文章目录

第一章 SMC 与有限状态机简单应用


前言

最近这段时间,受到项目上要求集成一份现有的模块代码,在收到的这份代码中,使用两套状态机配合完成既定功能。

源码包中包含两个后缀为sm的文本文件,刚开始根据文件内容和相关代码只能猜出这sm文件是一套状态机描述文件,随着慢慢摸索,了解到这是基于有限状态机与SMC(The State Machine Compiler)实现的。

SMC在sourceforge上有开源代码仓,有兴趣的可以自行去看看。

SMC - The State Machine Compiler download | SourceForge.net

引用官方介绍简单说明SMC是什么

SMC takes a state machine stored in a .sm file and generates a State pattern in 14 programming languages. Includes: default transitions, transition args, transition guards, push/pop transitions and Entry/Exit actions. See User Manual for more info.

SMC解析存储在.sm文件中的状态机,并生成14种编程语言的State模型。包括:默认转换、转换参数、转换保护、推送/弹出转换和进入/退出操作。有关更多信息,请参阅用户手册。


一、概述

既然我们都是天选牛马,那么就以一个简单的牛马状态机为例子,简单说明应用 SMC 状态机的步骤。

二、SMC 状态机应用步骤

1.创建状态机

牛马状态机 sm 文件(WorkerStateMachine.sm)如下:

%class Worker
%header Worker.h

%fsmclass WorkerStateMachine
%fsmfile WorkerStateMachine
%start WorkerStatus::Sleeping
%map WorkerStatus
%%

Eating
Entry {
    playAnimation("eating");
}
Exit {
    playAnimation("burp");
}
{
    timeToWork()
        Running {}

    timeToSleep()
        Sleeping {}
}

Running
Entry {
    playAnimation("running");
}
Exit {
    playAnimation("sweat");
}
{
    reachCompany()
        Working {}

    reachHome()
        Eating {}
}

Working
Entry {
    playAnimation("typewriting");
}
Exit {
    playAnimation("stretch");
}
{
    timeToHome()
        Running {}
}

Sleeping
Entry {
    playAnimation("snore");
}
Exit {
    playAnimation("yawn");
}
{
    timeToMeat()
        Eating {}
}

%%

2.翻译状态机

SMC支持将sm翻译为C++代码,这里将WorkerStateMachine.sm翻译为C++代码的命令如下:

java -jar ./Smc.jar -c++ -cast static_cast WorkerStateMachine.sm

3.集成状态机

在Worker类中集成WorkerStateMachine的代码如下:

// src/state_machine/Worker.h
#ifndef _WORKER_H_
#define _WORKER_H_

#include <string>
#include <map>
#include "WorkerStateMachine.h"

class Worker {
 public:
    Worker();
    virtual ~Worker();

    void startEveryDay();
    void playAnimation(std::string animationName);

 private:
    std::map<std::string, int32_t> m_animationMap;
    WorkerStateMachine m_workerFsm;
};

#endif  // WORKER_H_
//Worker.cpp
#include "Worker.h"

#include <chrono>
#include <thread>

Worker::Worker()
    : m_workerFsm(*this) {
    m_animationMap = {
        {"eating", 0},
        {"sweat", 1},
        {"typewriting", 2},
        {"stretch", 3},
        {"snore", 4},
        {"yawn", 5},
        {"running", 6},
         {"burp", 7},
    };
}

Worker::~Worker() {
    m_animationMap.clear();
}

void Worker::startEveryDay() {
    m_workerFsm.enterStartState();
}

void Worker::playAnimation(std::string animationName) {
    int32_t t_animationMode = -1;
    try {
        t_animationMode = m_animationMap.at(animationName);
    } catch (const std::out_of_range& e) {
        printf("[ERROR] Animation not found: %s\n", animationName.c_str());
        printf("[ERROR] %s\n", e.what());
    }

    if (t_animationMode != -1) {
        printf("Playing animation: %s\n", animationName.c_str());
        std::this_thread::sleep_for(std::chrono::seconds(5));
    }
    switch (t_animationMode) {
    case 0:  // 吃饭结束
        m_workerFsm.timeToWork();
        break;
    case 1:  // 跑步结束
        m_workerFsm.reachCompany();
        break;
    case 2:  // 打字结束
        m_workerFsm.timeToHome();
        break;
    case 3:  // 伸懒腰结束
        printf("1111111\n");
        break;
    case 4:  // 打呼噜结束
        m_workerFsm.timeToMeat();
        break;
    case 5:
        printf("2222222\n");
        break;
    default:
        break;
    }
}

在应用类中实例化一个 SMC 生成的状态机,并将应用类的引用传递给状态机实例作为状态机的上下文(Context);

上述代码中,在 Worker 类中声明一个 WorkerStateMachine 实例 m_workerFsm 作为数据成员,在 Worker 类构造函数的初始化列表中初始化 m_workerFsm,将 Worker 类的引用传入 m_workerFsm 作为 WorkerStateMachine 上下文;

在应用类的构造函数中实例化状态机是安全的,但在应用类构造函数中进入开始状态(enterStartState())是不安全的,因为,状态机开始状态的 Entry 动作中会调用应用类对象,而此时应用类的构造还没有完成,一些对象未初始化或者初始化函数还没执行;

Worker 类中声明一个成员函数 startEveryDay 作为启动状态机的入口,startEveryDay 函数中,通过 WorkerStateMachine 实例 m_workerFsm 执行 enterStartState()控制状态机进入开始状态。

WorkerStateMachine 中设计进入每个状态时播放状态的开始动画,退出每个状态时播放状态的结束动画。动画播放结束后,根据需要进入下一个状态。

4.运行实例

在 main 函数中创建 Worker 实例并开始牛马的每一天:

// main.cpp
#include "Worker.h"

int main() {
    Worker worker;
    worker.startEveryDay();
    return 0;
}

上述简易的代码运行后,输出内容如下:

ts@ts-OptiPlex-7070:~/work/testcase/ccpTest/build$ ./myapp 
Playing animation: snore
Playing animation: yawn
2222222
Playing animation: eating
Playing animation: burp
Playing animation: running

三、SMC 状态机相关

1.自动生成状态转换图

SMC支持根据sm自动化生成状态转换图,根据生成转换图的命令如下:

java -jar ./Smc.jar -graph WorkerStateMachine.sm
dot -Tpng ./WorkerStateMachine.dot -o ./WorkerStateMachine.png

2.完整工程源码

https://download.csdn.net/download/liangyunshan123/89738443

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值