`
chinamming
  • 浏览: 151230 次
  • 性别: Icon_minigender_1
  • 来自: 北京
文章分类
社区版块
存档分类
最新评论

状态机之C++解析

 
阅读更多

一、状态机描述

状态机理论最初的发展在数字电路设计领域。在数字电路方面,根据输出是否与输入信号有关,状态机可以划分为Mealy型和Moore型状态机;根据输出是否与输入信号同步,状态机可以划分为异步和同步状态机。而在软件设计领域,状态机设计的理论俨然已经自成一体。Moore型状态机的输出只和当前状态有关,和输入无关,如果在软件设计领域设计出这种类型的状态机,则该状态机接受的事件都是无内蕴信息的事件(输入)。Mealy型状态机的输入是由当前状态和输入共同决定,对应到软件设计领域,则该状态机接收的事件含有内蕴信息,并且影响状态机的输出。显然,这种划分在软件设计领域毫无意义。虽然软件设计领域的状态机也有同步和异步的划分,但和数字电路方面的同步异步已经不同。

除了《数字电路》,涉及到状态机的课程就是《编译原理》了(本人属计算机专业,其它专业是否涉及到状态机就不清楚了)。下面简单回顾一下《编译原理》里有关有限状态机的描述。在编译原理课程里面,对有限状态机的描述仅限在编译领域,特定状态,针对输入字符,发生状态改变,没有额外的行为,另编译原理里有限状态机的构成要素,还包含唯一的初始状态和一个终态集。数学语言描述如下:一个有限状态机M是一个五元组,M=(K,E,T,S,Z)。其中(1)K是一个有穷集,其中的每个元素称为状态(2)E是一个有穷字母表,它的每个元素称为一个输入字符(3)T是转换函数,是K×E->K上的映射(4)S是K中的元素,是唯一的一个初态(5) Z是K的一个子集,是一个终态集,或者叫结束集。很明显,状态机在编译原理里的讲解已经特化,输入被定位为字符集,状态改变的时候没有额外动作发生。

与编译原理中的状态机不同,软件设计领域中通用状态机的输入不是字符集,而是被称作事件的结构(可以是结构体,也可以是类对象),并且特定的状态下,针对发生的事件,不仅发生状态改变,而且产生动作。借鉴编译原理中状态机的初始状态和终态,通用状态机的数学语言描述如下:一个通用有限状态机M是一个七元组,M={K,E,T,M,F,S,Z}。其中(1)K是一个有穷集,其中的每个元素称为状态(2)E是一个有穷集,它的每个元素称为一个事件(3)T是转换函数,是K×E->K上的映射(4)M是一个有穷集,它的每个元素称为动作(5)F是动作映射函数,是K×E->M上的映射(6)S是K中的元素,是唯一的一个初态(7) Z是K的一个子集,是一个终态集,或者叫结束集。实用的状态机可以做进一步的优化,首先,可以把 (3)(5)整合在一起,做一个K×E->{K,M}的映射,其次从实用性的角度出发,禁止状态接收空事件(无输入的情况下,状态发生改变),作为弥补,为每个状态增加进入动作和离开动作,第三,鉴于定时器在系统中,尤其是在状态机中的重要性,可以为每个状态增加定时器以及超时后的状态转换。本文后面的讲述以及实现暂不考虑把定时器特化,如果需要,可以在状态的进入动作中初始化定时器(另:关于定时器,以后会写文章《系统设计之 定时器》)。

二、状态机分类(后文中如无特别说明,则状态机指软件设计领域的通用有限状态机)

依据状态之间是否有包含关系,分以下两种

(1)常规状态机。状态机中的所有状态是不相交的、互斥的。

(2)层次状态机。状态机中的状态之间要么是互斥的,要么是真包含的,可以用树性结构来描述这些状态集,包含其它状态的状态称为枝节点,不包含其它状态的状态称为叶节点,为方便单树描述,总是设计一个状态包含所有的状态节点,称为根节点。状态机的状态只能停留在叶节点,而不能停留在枝节点,每个枝节点需要指定一个子节点为它的默认子节点,以便状态机进入枝节点的时候能够停留到叶节点。

三、状态机实现

(1)switch/case if/else方式实现。用于少量状态(3个及其以下)的时候,不需要引入专门的状态机模块。这种方式不能编写通用的状态机模块,不再多说。

(2)面向过程方式:宏是实现面向过程方式的通用方式。虽然在状态机层面还是可以用面向对象的方式封装,这里还是把它称为面向过程的方式。
1.常规状态机模块实现。这个状态机涉及到机构由上而下为:

  • 顶层结构是状态机:当前状态id,缺省操作,状态表,
  • 状态表:状态数组
  • 状态结构:状态id,状态名,进入操作,退出操作,缺省操作,状态事件表(数组)
  • 状态事件结构:操作,事件,下一状态的id

状态机的算法是由状态机的结构决定的。实现如下:

#defineSINGLE_STATE_MAX_EVENT10
typedef
intFSM_EVENT_ID;
typedefstructevent_param_st
{
FSM_EVENT_IDid;
union
{
inti;
}
data;
}
FSM_EVENT;
typedef
intFSM_STATE_ID;
typedef
void(*FSM_FUNC)(FSM_EVENT*);
typedefstructstate_event_st
{
FSM_FUNCfunc;
FSM_EVENT_IDevent;
FSM_STATE_IDstate;
}
FSM_STATE_EVENT;
typedefstructstate_st
{
FSM_STATE_IDid;
char*name;
FSM_FUNCenter_func;
FSM_FUNCexit_func;
FSM_FUNCdefault_func;
FSM_STATE_EVENTevent_table[SINGLE_STATE_MAX_EVENT];
}
FSM_STATE;
typedefFSM_STATESTATE_TABLE[];
typedefFSM_STATE*PTR_STATE_TABLE;
#defineEND_EVENT_ID-1
#defineEND_STATE_ID-1
#defineBEGIN_FSM_STATE_TABLE(state_stable)
staticSTATE_TABLEstate_stable={
#defineBEGIN_STATE(id,name,enter_func,exit_func,default_func)
{id,name,enter_func,exit_func,default_func,{
#defineSTATE_EVENT_ITEM(func,event,state)
{func,event,state},
#defineEND_STATE(id)
{NULL,END_EVENT_ID,END_STATE_ID}}
}
,
#defineEND_FSM_STATE_TABLE(state_stable)
{END_STATE_ID,NULL,NULL,NULL,NULL,NULL}}
;

typedefstructfsm_st
{
FSM_STATE_IDstate_id;
FSM_FUNCdefault_func;
PTR_STATE_TABLEstate_tables;

}
FSM;

voidfsm_do_event(FSM&fsm,FSM_EVENT&event)
{
FSM_STATE*state=&(fsm.state_tables[fsm.state_id]);
inti=0;
while(state->event_table[i].event!=END_EVENT_ID)
{
if(state->event_table[i].event==event.id)
break;
i++;
}

if(state->event_table[i].event!=END_EVENT_ID)
{
if(state->id!=state->event_table[i].state)
{
if(state->exit_func)
state->exit_func(&event);
}

if(state->event_table[i].func)
state->event_table[i].func(&event);

if(state->id!=state->event_table[i].state)
{
if(fsm.state_tables[state->event_table[i].state].enter_func)
fsm.state_tables[state->event_table[i].state].enter_func(&event);
fsm.state_id=state->event_table[i].state;
}

}

else
{
if(state->default_func)
state->default_func(&event);
else
{
if(fsm.default_func)
fsm.default_func(&event);
}

}

}

以上说明实现原理,有特殊需要的话可以自己定制状态机,比如上面的状态事件表数组的上限取的是单个状态中事件项的最大值,也可以定义为所有事件的个数,这样的话事件也不需要查询,可以象状态样直接定位,只是状态事件表会浪费一些存储空间。上面的FSM_EVENT仅仅是个例子,实际开发根据需要定义不同的union。上面的算法也是假定状态表的状态定义是从0开始,顺序递增的。

对外部调用而言,最后的状态机结构和事件执行的方法可以封装为对象。下面举例说明状态机的定义(事件和状态都应该是enum类型,这里直接使用数字,仅为说明问题而已)。

BEGIN_FSM_STATE_TABLE(my_state_table)
BEGIN_STATE(0,"first",enter_fsm,exit_fsm,defualt_fsm)
STATE_EVENT_ITEM(func_fsm,1,1)
STATE_EVENT_ITEM(func_fsm,2,2)
END_STATE(0)

BEGIN_STATE(1,"second",enter_fsm,exit_fsm,defualt_fsm)
STATE_EVENT_ITEM(func_fsm,1,2)
STATE_EVENT_ITEM(func_fsm,2,0)
END_STATE(1)

BEGIN_STATE(2,"third",enter_fsm,exit_fsm,defualt_fsm)
STATE_EVENT_ITEM(func_fsm,1,0)
STATE_EVENT_ITEM(func_fsm,2,1)
END_STATE(2)
END_FSM_STATE_TABLE(my_state_table)

voidenter_fsm(FSM_EVENT*event)
{
printf("enterme\n");
}

voidexit_fsm(FSM_EVENT*event)
{
printf("exitme\n");
}

voiddefualt_fsm(FSM_EVENT*event)
{
printf("iamdefualt_fsm\n");
}

voidfunc_fsm(FSM_EVENT*event)
{
printf("iamfunc_fsm\n");
}

intmain()
{
printf("iammain\n");
FSMfsm=
{0,defualt_fsm,my_state_table};
printf("state[%d],name[%s]\n",fsm.state_id,fsm.state_tables[fsm.state_id].name);
FSM_EVENTevent;
event.id=1;
event.data.i=1;
fsm_do_event(fsm,event);
printf("state[%d],name[%s]\n",fsm.state_id,fsm.state_tables[fsm.state_id].name);
}

分享到:
评论

相关推荐

    C++实现的分层有限状态机v0.1

    本文将详细解析使用C++实现的分层有限状态机v0.1的相关知识。 首先,我们来看一下HFSM的基本概念。HFSM的核心思想是将状态分为多个层级,每个层级可以包含多个子状态。顶层状态通常代表较大的行为模式,而下层状态...

    youxianzhuangtaiji.rar_c++ 有限状态机_有限状态机_状态机_状态机 C

    有限状态机(Finite State Machine, FSM)是一种数学模型,在计算机科学和硬件设计中广泛应用,...理解并掌握有限状态机对于理解和设计复杂系统,尤其是在嵌入式系统、游戏编程和网络协议解析等领域,具有重要意义。

    自动机框架的SAEJ1939状态机_C++_下载.zip

    在这个"自动机框架的SAE J1939状态机_C++_下载.zip"压缩包中,我们可以期待找到一个用C++实现的SAE J1939状态机模型。状态机是一种建模工具,用于描述系统或程序在不同条件下的行为,特别是在处理具有多种操作模式和...

    基于c++的markdown解析器

    这可能需要设计一个状态机或者使用正则表达式库(如Boost.Regex或PCRE)。 4. **转换**:根据解析结果生成对应的HTML结构。例如,将一级标题转换为`<h1>`标签,将列表项转换为`<li>`标签等。 5. **输出HTML**:将...

    C语言实现状态机解析单词

    我们在看程序设计相关书籍的时候,经常会看见:...最近在写一个单词解析的练习题,刚好是使用了函数指针来达到隔离变化,降低耦合的目的,本文将是对学习知识的一个记录,同时分享给每一个需要学习函数指针的同学们。

    四种典型C语言状态机源代码

    4. **状态机模板:**C语言虽然不支持像C++那样的模板,但可以通过宏或者预处理技巧实现类似的效果。这种状态机模板允许开发者为不同类型的事件和状态创建通用的框架。源代码将展示如何使用这些技巧来提高代码复用性...

    json C++解析器

    你可以使用状态机或正则表达式来实现这个过程。 3. **语法分析**:接下来,词法符号表需要转换成抽象语法树(AST),这是一个表示JSON结构的数据结构。例如,键值对会转化为包含键和值的对象,数组会转化为包含元素...

    SIP协议解析与实现(c/c++)

    2. **状态机管理**:SIP协议基于事务,每个请求或响应都有对应的状态机。正确管理这些状态对于保证通信的可靠性至关重要。 3. **路由和重定向**:SIP支持通过代理服务器和重定向服务器进行消息转发,需要理解如何...

    用状态机原理进行软件设计

    同时,现代编程语言也提供了许多库和框架支持状态机的构建,例如在C++中的Boost.Statechart库,或者在Python中的transitions库。 总的来说,掌握状态机原理对于提升软件设计能力非常有益,无论是在嵌入式系统还是...

    AT指令解析,at指令解析框架,C,C++

    这些指令通常以ASCII文本形式发送,以“AT”开头,后面跟着具体的命令或参数,用于配置设备、检查状态或执行特定操作。"AT指令解析框架"则是为了简化处理这些指令而设计的一种软件结构。 在描述中提到的"AT指令解析...

    一个有限状态机的例子

    在计算机科学中,有限状态机被广泛应用于各种领域,包括编译器设计、网络协议解析、数据验证等。本例子中的有限状态机可能是用代码实现的,用于解决特定问题。 状态机的核心组成部分包括: 1. 状态(States):状态...

    大华相机Demo-C++.rar

    本文将围绕"大华相机Demo-C++.rar"这一压缩包,深入解析其中包含的C++案例,帮助读者理解和掌握如何使用大华工业相机进行程序开发。 首先,我们要明确“大华工业相机”是指大华公司推出的专用于工业自动化、检测、...

    zhuangtaiji.rar_状态机_状态机PPT

    本资料"zhuangtaiji.rar_状态机_状态机PPT"旨在指导如何有效地编写状态机,通过实例解析状态机的构建格式。 状态机主要由状态、事件、转换和动作四个基本要素构成。状态表示系统在某一时刻的行为或特性;事件是触发...

    基于C++实现的dlt634.5104协议解析源码-服务器端

    6. **状态机**:为了正确地处理异步通信和并发连接,可能需要实现一个状态机来跟踪每个连接的状态。 7. **日志记录**:为了调试和监控,源码中应包含对日志记录的支持,记录重要的通信事件和错误信息。 在开发过程...

    c++飞机大战+报告

    《C++实现的飞机大战游戏及技术解析》 在编程领域,C++是一种广泛应用的高级编程语言,以其高效性、灵活性和面向对象的特性而受到程序员的青睐。本项目名为“C++飞机大战”,它是一个基于C++实现的简单游戏,通过这...

    SCS舵机c++例程

    了解这些接口的工作原理和如何在C++中生成或解析这些信号至关重要。 3. **PWM控制**:PWM是控制舵机角度的主要方式,通过调整脉冲宽度来设定舵机的位置。C++中可以使用定时器库或自定义函数来生成PWM信号,理解脉宽...

    C++-代码解析(词法分析、语法分析).pdf

    在本文的示例代码中,词法分析是通过一个简单的状态机来实现的,该状态机使用 if-else 语句来判断当前字符是否为单词的开始或结束。 语法分析 语法分析是编译过程的第二步骤,负责对单词序列的语法正确性进行检查...

    状态机的应用 (c++编程或算法设计帮助)

    状态机在计算机科学和编程中扮演着至关重要的角色,特别是在处理模式识别、语言解析和算法设计等任务中。本文将深入探讨确定有限自动机(DFA)和非确定有限自动机(NFA),以及它们之间的转换和应用。 首先,确定...

    Hi3516板卡状态机示例

    状态机在嵌入式系统中广泛应用于处理各种序列化操作,如设备控制、协议解析等。本示例通过简单的事件封装,展示了如何在C++和C语言环境下构建高效且灵活的状态机。 在C++和C编程中,状态机的设计通常涉及以下几个...

Global site tag (gtag.js) - Google Analytics