C 语言实现简单有限状态机

2023-11-05

简介

常说的状态机是有限状态机 FSM,是表示有限个状态以及在这些状态之间的转移和动作等行为的数学计算模型。
三个特征:

  • 状态总数(state)是有限的。
  • 任一时刻,只处在一种状态之中。
  • 某种条件下,会从一种状态转变(transition)到另一种状态。

设计状态机的关键点:当前状态、外部输入、下一个状态。

状态机分类

Moore 型状态机

Moore 型状态机特点是:输出只与当前状态有关(与输入信号无关)。相对简单,考虑状态机的下一个状态时只需要考虑它的当前状态就行了。

Mealy 型状态机

Mealy 型状态机的特点是:输出不只和当前状态有关,还与输入信号有关。状态机接收到一个输入信号需要跳转到下一个状态时,状态机综合考虑 2 个条件(当前状态、输入值)后才决定跳转到哪个状态。

实现一个简单的状态机

代码参考AstarLight/FSM-framework

以小明的一天设计出一个状态机,下图为状态转移图:

首先,有限状态机的状态是有限的,我们可以定义一天中的状态:

enum
{
 GET_UP,
 GO_TO_SCHOOL,
 HAVE_LUNCH,
 DO_HOMEWORK,
 SLEEP,
};

状态机在没有事件的驱动下就是一潭死水,所以我们还需要定义出一些会发生的事件,去驱动状态机的运转:

enum
{
 EVENT1 = 1,
 EVENT2,
 EVENT3,
};

再定义一些在某个状态下需要处理的动作,也就是函数:


void GetUp()
{
 // do something
 printf("xiao ming gets up!\n");

}

void Go2School()
{
 // do something
 printf("xiao ming goes to school!\n");
}

void HaveLunch()
{
 // do something
 printf("xiao ming has lunch!\n");
}

void DoHomework()
{
 // do something
 printf("xiao ming does homework!\n");
}

void Go2Bed()
{
 // do something
 printf("xiao ming goes to bed!\n");
}

定义一个状态表结构,用来表示一个状态机的状态:

typedef struct FsmTable_s
{
 int event;              //事件
 int CurState;           //当前状态
 void (*eventActFun)();  //函数指针
 int NextState;          //下一个状态
}FsmTable_t;

接下来,我们就可以这个结构定义一个状态表,状态机根据这个表进行状态的流转:

FsmTable_t XiaoMingTable[] =
{
 //{到来的事件,当前的状态,将要要执行的函数,下一个状态}
 { EVENT1,  SLEEP,           GetUp,        GET_UP },
 { EVENT2,  GET_UP,          Go2School,    GO_TO_SCHOOL },
 { EVENT3,  GO_TO_SCHOOL,    HaveLunch,    HAVE_LUNCH },
 { EVENT1,  HAVE_LUNCH,      DoHomework,   DO_HOMEWORK },
 { EVENT2,  DO_HOMEWORK,     Go2Bed,       SLEEP },
};

定义一个状态机结构,表示一个状态机:

typedef struct FSM_s
{
 FsmTable_t* FsmTable;   //指向的状态表
 int curState;           //FSM当前所处的状态

}FSM_t;

有了这些基本的结构,就可以写主函数了:

int main()
{
 FSM_t fsm;                        // 实例化一个状态机
 InitFsm(&fsm);                    // 初始化状态机
 int event = EVENT1;               // 初始化事件,为了启动状态机流转,
                                      // 因为状态机只有在有时间发生时才会改变状态

 //小明的一天,周而复始的一天又一天,进行着相同的活动
 while (1)
 {
  printf("event %d is coming...\n", event);
  FSM_EventHandle(&fsm, event); // 有了初始事件,我们就需要处理这个事件,
                                      // 再写一个处理事件的函数
  printf("fsm current state %d\n", fsm.curState);
  test(&event); 
  Sleep(1);                     //休眠1秒,方便观察
 }

 return 0;
}

// 测试用的,模拟事件的发生
void test(int *event)
{
 if (*event == 3)
 {
  *event = 1;
 }
 else
 {
  (*event)++;
 }
 
}

编写初始化状态机的函数:

int g_state_max_num = 0; // 状态机的状态最大数量,根据状态表的大小来计算
// 初始化FSM
void InitFsm(FSM_t* pFsm)
{
 g_state_max_num = sizeof(XiaoMingTable) / sizeof(FsmTable_t);
 pFsm->curState = SLEEP; // 初始状态为睡觉
    pFsm->FsmTable = XiaoMingTable;
}

编写事件处理函数:

/* 事件处理 */
void FSM_EventHandle(FSM_t* pFsm, int event)
{
 FsmTable_t* pActTable = pFsm->FsmTable;
 void (*eventActFun)() = NULL;  //函数指针初始化为空
 int NextState;
 int CurState = pFsm->curState;

 /* 获取当前动作函数 */
 for (int i = 0; i<g_max_num; i++)
 {
  //当且仅当当前状态下来个指定的事件,我才执行它
  if (event == pActTable[i].event && CurState == pActTable[i].CurState)
  {
   pActTable[i].eventActFun();                      // 执行动作函数
            FSM_StateTransfer(pFsm, pActTable[i].NextState); // 执行状态转移
   break;
  }
        else
        {
            // do nothing
        }
 }
}
/* 状态迁移 */
void FSM_StateTransfer(FSM_t* pFsm, int state)
{
 pFsm->curState = state;
}

参考资料

Linux 编程之有限状态机 FSM 的理解与实现 - Madcola - 博客园
JavaScript 与有限状态机 - 阮一峰的网络日志
有限状态机 - 维基百科,自由的百科全书

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C 语言实现简单有限状态机 的相关文章

随机推荐

  • 万兆以太网选择6类线还是6A类线?

    在综合布线中 有些时候必不可免的需要用到万兆铜缆以太网 那么这个时候就会产生一个问题 就是线缆的选型 6类线和6A类线都可以支持到万兆以太网 那么究竟是选择成本较为低廉的6类线还是选择规格更高一些的6A类线缆 6类线与6A类线的区别 起初
  • Cordic算法

    Cordic算法可以利用简单的移位和加减来计算复杂的三角函数 双曲函数 对数 指数等 Cordic算法核心思想有两点 通过已知的角度来逼近输入的角度 用移位来代替tan 已知角度的cos 经过多次积累相乘趋于常数 具体原理如下 根据坐标旋转
  • 怎么委婉的拒绝别人

    其实拒绝别人最难的那个是自己 有心理负担 导致很多人不能说 不 多少次后悔答应别人了 但是也不能没有技巧的拒绝别人 如果没有思考过 那么也会让自己后悔自己做的不合适 方法不对 怎么去拒绝别人呢 需要掌握好技巧和心理分析然后坦然的拒绝别人是这
  • 关于Mybatis一对多查询以及返回一条记录的经验总结

    前人经验 关于一对多返回一条的问题 原因是在于多张表有列名相同的字段 如果在数据库中使用连接操作 如 INNER JOIN LEFT JOIN RIGHT JOIN 等 进行连接时 列名有相同的字段 则在连接结果集中 这些列名相同的字段会被
  • c:forEach status.index 行索引的使用

    使用seam做了个项目 展示一个自定义列表输出 分别定义表格标题List headerList和数据List
  • 浅谈云计算的三种服务模式:IaaS,PaaS和SaaS

    2008年 云计算的概念由Google率先提出 短时间内其核心理念在全球范围内迅速传播并发展 2010年在国内形成趋势 各大IT互联网商业巨头将目光聚焦在云计算 至目前 云计算在中国已经慢慢开始成熟起来 云计算指的是通过网络 云 将巨大的数
  • python从入门到精通——完整教程【转载】

    文章目录 一 pycharm下载安装 二 python下载安装 三 pycharm上配置python 四 配置镜像源让你下载嗖嗖的快 4 1 pycharm内部配置 4 2 手动添加镜像源 4 3 永久配置镜像源 五 插件安装 比如汉化 5
  • ios逆向(二)frida-ios-dump一键砸壳详细版

    写在前面 本教程为本人实际操作记录 在此感谢庆哥官方 一条命令完成砸壳 github frida ios dump ios端配置 打开cydia 添加源 https build frida re 打开刚刚添加的源 安装 frida 安装完成
  • 注册表关闭windows安全中心_关闭win10自动更新的三个小妙招,再也不用被自动下载更新打扰了...

    对于Windows 10操作系统 微软默认设置为自动下载并安装Windows 以确保系统正常运行并保持其安全性 但是在某些情况下 需要关闭Windows 10更新或在Windows 10上禁用自动更新安装 这该如何办呢 本文目录 关于Win
  • Linux系统中文件查找find函数用法

    find name april 在当前目录下查找以april开始的文件 find name april fprint file 在当前目录下查找以april开始的文件 并把结果输出到file中 find name ap o name may
  • JAVA导入txt文件并按行读取内容封装成实体以及导出下载

    业务背景 前台页面支持用户上传txt类型的文件 用做一些服务的配置 我们需求将改文件解析 读取里面的内容 并封装成接口参数 再调第三方接口 上代码 PostMapping uploadHost RequiresRoles admin pub
  • p9plus升级鸿蒙教程,华为P9 Plus(VIE-AL10 全网通 EMUI 5.0)一键ROOT图文详解教程

    伴随着安卓刷机越来越流行 很多安卓用户都喜欢上了这种可以自定个性系统的行为 那么华为P9 Plus VIE AL10 全网通 EMUI 5 0 怎么获取ROOT权限 华为P9 Plus VIE AL10 全网通 EMUI 5 0 一ROOT
  • IMEI、IMSI、ICCID、SN是什么?意义和区别?通信模组或手机的唯一识别码

    最近在做几个4G移动端的产品 初入行门有很多生涩的名词 想获取一个全球唯一ID作为设备后台管理编号 就扯出了 IMEI IMSI ICCID SN 这几个东西 IMEI IMEI 国际移动设备识别码 International Mobile
  • 如何正确理解三极管的放大区、饱和区、截止区

    作为电子初学者来说 模拟电路非常重要 模拟电路的三极管的应用是重中之重 能正确理解三极管的放大区 饱和区 截止区是理解三极管的标志 很多初学者都会认为三极管是两个 PN 结的简单凑合 如下图 这种想法是错误的 两个二极管的组合不能形成一个三
  • 多线程和高并发介绍

    多线程和高并发介绍 文章目录 多线程和高并发介绍 前言 一 什么是多线程 1 多线程介绍 2 多线程实现原理 3 白话文解释多线程 4 多线程存在的问题 二 什么是高并发 1 高并发介绍 2 如何提升系统的并发能力 三 多线程和高并发 总结
  • es6对象多层解构、数组解构

    对象类 基础对象解构 const obj a 1 b 2 c 3 const a b c obj console log a b c 1 2 3 多层对象解构 const obj a 1 b 2 c 3 d d1 4 const a b c
  • qt调用Linux脚本范例,QT下实现对Linux Shell调用的几种方法

    使用QProcess QThread include int main QProcess execute ls return 0 QProcess poc new QProcess poc gt start ping 222 207 53
  • [极客大挑战 2019]Knife

    极客大挑战 2019 Knife 主界面 很显然 题目已经内置了一个一句话木马 我们只需要用蚁剑连接即可 但是我在连接蚁剑时报错了 错误如下 经过搜索 原来是开启了手动代理模式 在菜单中关闭即可 更改后成功进入 在根目录下找到flag文件
  • 20050405:什么都要会啊

    为了要修补门户的页面 今天学会了三样 怎么用Photoshop切割图片并存入网页 怎么用DW在表格中平铺背景图片 在么用Tomcat部署网站 真的是什么都要会啊 今天下午在漫网论坛上发了封贴子 晚上却被删了 原贴如下 关于日本动漫中女性角色
  • C 语言实现简单有限状态机

    简介 常说的状态机是有限状态机 FSM 是表示有限个状态以及在这些状态之间的转移和动作等行为的数学计算模型 三个特征 状态总数 state 是有限的 任一时刻 只处在一种状态之中 某种条件下 会从一种状态转变 transition 到另一种