C语言_有限状态机(Finite State Machine)

基本介绍

许多小型或复杂的应用程序都使用有限状态机 (FSM),C 语言中的有限状态机是嵌入式系统的流行设计模式之一,有限状态机使开发变得容易和顺利。

有很多设备使用事件基态,如咖啡机、自动售货机、POS 设备、门锁系统等。一些 POS 设备使用事件表,在事件表中使用事件处理程序注册事件,通过相关条件触发事件的执行。

本文中,使用C语言创建一个简易的ATM状态机。 ATM 机的状态可以通过即将发生的事件进行更改。ATM状态机包含以下几个状态:

  • Idle State
  • Card Inserted State
  • Pin entered State
  • Option Selected State
  • Amount Entered State

初始化时,ATM处于闲置状态,之后进入插卡状态,插卡处理完成之后需要用户输入密码,输入密码之后进行相关选项的操作,设置选项完成之后输入金额。创建一个状态机,按照以下步骤进行:

  • Gather the information which the user wants.
  • Analyze the all gather information and sketch the state transition diagram.
  • create a code skeleton of the state machine.
  • Make sure the transition (changing state) work properly
  • Implement all the required information in the code skeleton of the state machine.
  • Test the implemented state machine.

收集需求——>分析需求绘制状态图——>创建代码框架——>确认状态转换是否正确——>编写状态的具体动作——>测试状态机。

实现方法

在C语言中,有两种常用的方式实现基于事件的状态机:一种是通过嵌套的switch case(或者if else)实现,一种是look up table(查表)实现。

look up table 查表法实现有限状态机

结构体数组创建有限状态机是一种比较优雅的方式。状态机的状态和事件封装在一个结构中,并在适当的状态和事件上调用函数指针(事件处理程序),程序的可读性比较好。

#include <stdio.h>
//Different state of ATM machine
typedef enum
{Idle_State,Card_Inserted_State,Pin_Eentered_State,Option_Selected_State,Amount_Entered_State,last_State
} eSystemState;
//Different type events
typedef enum
{Card_Insert_Event,Pin_Enter_Event,Option_Selection_Event,Amount_Enter_Event,Amount_Dispatch_Event,last_Event
} eSystemEvent;
//typedef of function pointer
typedef eSystemState(*pfEventHandler)(void);
//structure of state and event with event handler
typedef struct
{eSystemState eStateMachine;eSystemEvent eStateMachineEvent;pfEventHandler pfStateMachineEvnentHandler;
} sStateMachine;
//function call to dispatch the amount and return the ideal state
eSystemState AmountDispatchHandler(void)
{return Idle_State;
}
//function call to Enter amount and return amount entered state
eSystemState EnterAmountHandler(void)
{return Amount_Entered_State;
}
//function call to option select and return the option selected state
eSystemState OptionSelectionHandler(void)
{return Option_Selected_State;
}
//function call to enter the pin and return pin entered state
eSystemState EnterPinHandler(void)
{return Pin_Eentered_State;
}
//function call to processing track data and return card inserted state
eSystemState InsertCardHandler(void)
{return Card_Inserted_State;
}
//Initialize array of structure with states and event with proper handler
sStateMachine asStateMachine[] =
{{ Idle_State,              Card_Insert_Event,          InsertCardHandler },{ Card_Inserted_State,      Pin_Enter_Event,            EnterPinHandler },{ Pin_Eentered_State,     Option_Selection_Event,     OptionSelectionHandler },{ Option_Selected_State,   Amount_Enter_Event,         EnterAmountHandler },{ Amount_Entered_State,        Amount_Dispatch_Event,      AmountDispatchHandler }
};
//main function
int main(int argc, char *argv[])
{eSystemState eNextState = Idle_State;while (1){//Api read the eventeSystemEvent eNewEvent = read_event();if ((eNextState < last_State) && (eNewEvent < last_Event) &&      (asStateMachine[eNextState].eStateMachineEvent == eNewEvent) && (asStateMachine[eNextState].pfStateMachineEvnentHandler != NULL)){// function call as per the state and event and return the next state of the finite state machineeNextState = (*asStateMachine[eNextState].pfStateMachineEvnentHandler)();}else{//Invalid}}return 0;
}

状态机结构体中包含状态,事件编号,执行状态的动作。执行动作将返回对应的状态值。填写状态机的结构体数组,状态、触发事件和即将执行的动作。状态机运行时,初始化状态机,实践中,通常从外部API读入实践触发动作的执行。执行状态机中的动作,返回下一个状态的状态编号。

switch case实现有限状态机

这是实现状态机的最简单方法。使用 if-else 或 switch case 来检查状态并触发事件。如果状态和触发事件的组合匹配,则执行事件处理程序并更新下一个状态。这取决于检查第一个状态或事件的要求。 在下面的示例代码中,首先验证状态,然后检查触发的事件。

#include <stdio.h>
//Different state of ATM machine
typedef enum
{Idle_State,Card_Inserted_State,Pin_Eentered_State,Option_Selected_State,Amount_Entered_State,
} eSystemState;
//Different type events
typedef enum
{Card_Insert_Event,Pin_Enter_Event,Option_Selection_Event,Amount_Enter_Event,Amount_Dispatch_Event
} eSystemEvent;
//Prototype of eventhandlers
eSystemState AmountDispatchHandler(void)
{return Idle_State;
}
eSystemState EnterAmountHandler(void)
{return Amount_Entered_State;
}
eSystemState OptionSelectionHandler(void)
{return Option_Selected_State;
}
eSystemState EnterPinHandler(void)
{return Pin_Eentered_State;
}
eSystemState InsertCardHandler(void)
{return Card_Inserted_State;
}
int main(int argc, char *argv[])
{eSystemState eNextState = Idle_State;eSystemEvent eNewEvent;while (1){//Read system EventseSystemEvent eNewEvent = ReadEvent();switch (eNextState){case Idle_State:{if (Card_Insert_Event == eNewEvent){eNextState = InsertCardHandler();}}break;case Card_Inserted_State:{if (Pin_Enter_Event == eNewEvent){eNextState = EnterPinHandler();}}break;case Pin_Eentered_State:{if (Option_Selection_Event == eNewEvent){eNextState = OptionSelectionHandler();}}break;case Option_Selected_State:{if (Amount_Enter_Event == eNewEvent){eNextState = EnterAmountHandler();}}break;case Amount_Entered_State:{if (Amount_Dispatch_Event == eNewEvent){eNextState = AmountDispatchHandler();}}break;default:break;}}return 0;
}

使用switch case 和if else实现上面的状态切换和动作执行,导致程序很长,不利于后期的维护和修改。可否对上面的程序进行优化,避免这种嵌套的结构呢?

使用二维数组指针精简switch case嵌套结构

答案是肯定得,我们在上面的程序中,首先是用switch case进行了状态的判断,然后使用if else判断是否有事件触发动作。那么相当于这段代码包含两层判断,第一层大的状态判断后面跟随着一层判断。基于这个认知,自然就想到,可否使用一个二维数组,行代表一层判断,列代表这一层判断下面的子状态判断呢!

下面,使用一个二维数组替代以上switch case的嵌套,实现代码的精简:

# include <stdio.h>
typedef enum
{Idle_State,Card_Inserted_State,Pin_Eentered_State,Option_Selected_State,Amount_Entered_State,last_State
} eSystemState;
//Different type events
typedef enum
{Card_Insert_Event,Pin_Enter_Event,Option_Selection_Event,Amount_Enter_Event,Amount_Dispatch_Event,last_Event
} eSystemEvent;
//typedef of 2d array
typedef eSystemState (*const afEventHandler[last_State][last_Event])(void);
//function call to dispatch the amount and return the ideal state
eSystemState AmountDispatchHandler(void)
{return Idle_State;
}
//function call to Enter amount and return amount enetered state
eSystemState EnterAmountHandler(void)
{return Amount_Entered_State;
}
//function call to option select and return the option selected state
eSystemState OptionSelectionHandler(void)
{return Option_Selected_State;
}
//function call to enter the pin and return pin entered state
eSystemState EnterPinHandler(void)
{return Pin_Eentered_State;
}
//function call to processing track data and return card inserted state
eSystemState InsertCardHandler(void)
{return Card_Inserted_State;
}
int main(int argc, char *argv[])
{eSystemState eNextState = Idle_State;eSystemEvent eNewEvent;// Table to define valid states and event of finite state machinestatic afEventHandler StateMachine ={[Idle_State] = { [Card_Insert_Event] = InsertCardHandler },[Card_Inserted_State] = { [Pin_Enter_Event] = EnterPinHandler },[Pin_Eentered_State] = { [Option_Selection_Event] = OptionSelectionHandler },[Option_Selected_State] = { [Amount_Enter_Event] = EnterAmountHandler },[Amount_Entered_State] = { [Amount_Dispatch_Event] = AmountDispatchHandler },};while (1){// assume api to read the next eventeSystemEvent eNewEvent = ReadEvent();//Check NULL pointer and array boundaryif ((eNextState < last_State) && (eNewEvent < last_Event) && StateMachine[eNextState][eNewEvent] != NULL){// function call as per the state and event and return the next state of the finite state machineeNextState = (*StateMachine[eNextState][eNewEvent])();}else{//Invalid}}return 0;
}

在上面这段代码中,使用typedef定义函数指针,返回值正是执行动作返回的状态值。二维数组返回event,是一个执行动作的过程。这样的话,在有限状态机的实现中,定义行为state,列里面我们放置event,每一列对应一个action,就可以实现根据前一次的state和event,执行当前与之对应的action,而返回当前执行动作的state,使状态机正确转移到下一个状态。

在上面的实现方法中,一个嵌套的 switch case 替换为一个指向函数的指针数组。 精简了代码,但是同时也并降低代码的可读性。 当状态很多,触发动作的条件也很多时,会造成内存大量的浪费,因为在二维数组中,每一行里面并非所有的event都是这一行的state必须的。

以上是状态机的常见实现方法,针对嵌入式的手写代码编程,常常希望有一种简单又不需要占用太多资源的方法,并且能够有效复用。这里推荐一种基于事件驱动的有限状态机框架,QEP框架,使用函数指针映射为状态,将状态机的描述进行标准化处理,接口简单,占用资源极少。

QEP有限状态机框架

博主会长期更新关于有限状态机设计的方法与思想,欢迎关注。

reference:
https://aticleworld.com/state-machine-using-c/

C语言_有限状态机(FSM)相关推荐

  1. 《C专家编程》C语言实现有限状态机FSM

    用C语言实现有限状态机 在C语言中,有好几种方法可以用来表达FSM,但他们绝大多数都是基于函数指针数组 ---- 摘自 <C专家编程> 下面介绍如何使用函数指针数组实现FSM 直接上代码 ...

  2. c语言状态机_【C语言】有限状态机FSM

    有限状态状态机FSM(finite state machine)是为研究有限内存的计算过程和某些语言类而抽象出的一种计算模型.有限状态自动机拥有有限数量的状态,每个状态可以迁移到零个或多个状态,输入字 ...

  3. 有限状态自动机java实现_有限状态机FSM的几种简单实现

    标签: 有限状态机,Akka fsm,squirrel-foundation,java状态模式.责任链模式 1. 有限状态机的概念 有限状态机(英语:finite-state machine,缩写:F ...

  4. 状态模式 有限状态机_有限状态机解释

    状态模式 有限状态机 The finite state machine (FSM) is a software design pattern where a given model transitio ...

  5. iar环境下c语言编程,c语言_源代码-iar环境配置.pdf

    c语言_源代码-iar环境配置 欢迎光临我的博客:/mikehendry 其实,IAR 编译环境的配置是相当重要的,没配置正确或者不符合自己的习惯的话,使用起来就会很麻烦.下面 我根据网上的经验和资料 ...

  6. c语言while求a和b的和程序,数据结构实验1_C语言_输入集合A和B求并集、交集、差集(while +...

    数据结构实验1_C语言_输入集合A和B求并集.交集.差集(while + 数据结构实验1_C语言_输入集合A和B求并集.交集.差集(while + switch + 功能函数)) 实验1 (1)实验目 ...

  7. c语言考试排座位系统源程序,c语言_排考场座位问题:.doc

    c语言_排考场座位问题: ④排考场座位问题:假设考场有8×8的座位,每当一个考生进入考场就为他安排座位.主界面如下:请输入选项(1-4)1?设定考场座位2?取消考场座位3?显示某座位考生信息4?查找学 ...

  8. c语言游戏经典案例,C语言_编游戏案例精编.doc

    C语言_编游戏案例精编 C语言_编游戏案例精编 C语言 编游戏案例精编 案例一 贪吃蛇游戏 案例二 计算器 案例三 黑白棋游戏 案例四 迷宫问题 案例五 扫地雷游戏 案例六 速算24 案例七 数据结构 ...

  9. 不足100克按100克C语言,C语言_第3章.ppt

    C语言_第3章 C语言的语句可分为以下四类: 简单语句 空语句 复合语句 流程控制语句 三.复合语句用一对花括号,把若干条语句括起来,就形成了一条复合语句.形式如下: {[内部数据描述语句] 语句1: ...

最新文章

  1. 数据依赖症:当今AI领域的核心风险
  2. java.io 相关tips
  3. 大快人心!和P2P网贷彻底说再见
  4. ORM Model查询页生成
  5. EasySQLMAIL中企业微信的配置方法
  6. Atitit 群控云控资料索引index cyonkon yunkon cyonkonYonkon群控云控方面资料.rar C:\Users\ATI\Documents\cyonkonYonko
  7. IT战略规划项目方法论(德勤、埃森哲、IBM、凯捷)
  8. JUC之集合安全问题
  9. java调用万网域名接口
  10. linux 系统启动服务setup设置
  11. D3.js in action 笔记
  12. 漫步者蓝牙只有一边有声音_为什么我蓝牙耳机只有一边有声音?
  13. Win7 的70个使用技巧
  14. ArcGIS空间数据查询与处理
  15. Android多人视频聊天应用的开发(一)快速集成
  16. NBA球队中英文名对照大全附带各个球队的LOGO手机壁纸
  17. 双向链表的删除和插入
  18. pythonrandint用法_Python randint()用法及代码示例
  19. 7篇顶会论文带你梳理多任务学习建模方法
  20. 暗黑2浴火重生zclient注册账号

热门文章

  1. 抓取今日头条的个人收藏夹目录
  2. 随机森林分析金融数据
  3. Iconfot阿里妈妈-css高级应用
  4. MFC中设备描述符DC,CDC,HDC,CClientDC总结
  5. 将MinGW64注入右键菜单
  6. java 10进制转2进制递归算法_Java十进制转二进制,递归算法
  7. vue-cli 构建的项目如何加入骨架屏 skeleton
  8. docker容器日志采集EFK日志分析系统的搭建与应用
  9. 魔兽争霸微操教学(精华篇)
  10. 周鸿祎:360这个模式比较独特 全球就我们一家