状态机技术在数据通讯协议栈中的编程应用
状态机技术在数据通讯协议栈中的编程应用
张腊明 ,佟 宇
(大连理工大学 机械工程学院 辽宁 大连 116024)
摘 要 :在数据通讯软件中 ,上位机 (PC 机) 数据通讯协议栈的编写工作通常由 Switch/ Case 分支语句来完成。分支语
句是一种简单初级的逻辑表达式 ,因此不易做到模块化 ,扩充性能和可移植性能都不理想 ,因而引入了状态机技术。通过将
通讯协议栈抽象为有序的不同状态和行为 ,再嵌入到 C + + 类中 ,可模拟状态机实现此通讯处理 ,在 Visual C + + 6. 0 平台上运
行可靠 ,稳定性好 ,并且状态行为具有可继承性。
关键词 :FSM ;数据采集 ;协议栈 ;分支语句
中图分类号 : TP311. 52 文献标识码 :B 文章编号 :1004 373X(2008) 03 146 03
FSM Methodology and Its Application in Communication Protocol Stack
ZHAN G Laming ,TON G Yu
(School of Mechanical & Engineering ,Dalian University of Technology ,Dalian ,116024 ,China)
Abstract :Switch/ Case syntax is the traditional method to program the communication protocol stack in the software of da2
ta communication. However ,Switch/ Case syntax as a simple logical expression neither have expansibility and transplantable
performance nor can be modularization. A new method based on FSM methodology is introduced in this paper. Dividing com2
munication protocol stack into a lot of states and actions ,which is embedded into the C + + class and can be inherited by other
classes ,making the software work reliably and stability on Visual C + + platform.
Keywords :FSM ;data acquisition ;protocol stack ;syntax
收稿日期 :2007 08 14
在计算机数字通讯中经常要对通讯数据进行打包、解
包和校验等多种顺序操作 ,相应的也要求协议软件能对接
收的数据依次进行相关处理 ,从而表现出数字通讯中的多
阶段性特征 ,为了满足分阶段处理通讯数据的需要 ,引进
了状态机技术。由于状态机能以模块化支撑协议 ,因而在
数据通讯协议栈处理技术方面扮演着越来越重要的角色。
1 状态机简介
状态机(有限状态机) 是一种具有离散输入输出系统
的模型。任何时刻他都处于一个特定的状态 ,状态的转换
依赖于系统所接受的事件。当在某状态下有事件发生时 ,
系统会根据输入的事件和当前的状态做出反映 ,从而决定
如何处理该事件以及是否转换到下一状态[1 ] 。
状态的触发事件通常由外部信号来完成 ,当有效的触
发事件发生时 ,便进入下一状态 (当然也可以不发生状态
转移) ,同时完成本状态的具体任务 ,直到所有状态完成 ,
再回到初始状态。当某一状态出现异常时 ,也返回初始状
态 ,等待下一触发事件的出现 ,如此反复循环。
状态机通常有两种表达方式 :状态表和状态图。
在图 1 所示简单状态图中 ,当状态机处于状态 1 时 ,
完成动作 1 ;事件 1 可以触发状态机跳转到状态 2 ,以执行
动作 2 ;同样 ,事件 2 也可以触发状态 2 的转移 ,从而又回
到状态 1 。图 1 所示状态和转移都很简单 ,真正的状态机
比他要复杂的多。
图 1 状态事件图
对应图 1 的状态表如表 1 所示。其中 ,括号内为当前
状态动作和下一状态 ,空网格表示在此状态下 ,此事件无
效 ,即不能触发状态的转移 ,当然也不执行任何动作。
表 1 状态事件表
状态 1 状态 2
事件 1 {动作 1 ,状态 2}
事件 2 {动作 2 ,状态 1}
2 状态机的实现
在高级编程语言(如 C ,C + + ) 中状态机的典型实现主
要有 :嵌套的 Switch 语句 ;状态表 ;面向对象状态设计模
641
工 控 技 术 张腊明等 :状态机技术在数据通讯协议栈中的编程应用
? 1994-2008 China Academic Journal Electronic Publishing House. All rights reserved. http://www.cnki.net
式。其他技术几乎是前面 3 种方式的组合[2 ] 。
Switch/ Case 语句是一种内联性很强、病态耦合的编
程技术 ,是一种简单初级的逻辑表达式 ,因此不易做到模
块化、灵活的可扩充性和可移植性 ,至于鲁棒性就更差了。
现代软件技术讲究松耦合、可移植、可快速扩充 ,并要求安
全可靠 ,而状态表的实现正是基于这一思想而发展的。当
要在状态机中增加新的状态与控制逻辑 ,只需在状态表中
修改即可 ,甚至可以动态修改 ,C + + 的指针完美地支持这
一点。在状态变化不是很复杂的情况下 ,这是一种非常可
取的方法。
3 数据采集系统 PC 端数据通讯协议栈的状态机实现
在笔者所编写的土壤渗流实验数据采集系统 PC 端
软件中 ,很好的利用了状态机技术来完成数据通讯协议栈
的处理。此实验数据的采集由下位机 (单片机) 和上位机
( PC 机) 共同来完成。对于 PC 端软件来说 ,正确的数据接
收是后续工作的前提和保障。因此 ,有必要对接收到的每
一帧消息进行检测和校验 ,即进行数据的预处理。只有符
合通讯协议的数据才进行后续处理 ,不符合通讯协议的数
据将丢弃掉 ,并且通知下位机数据出错。下面将结合笔者
参与开发的数据采集系统 ,论述状态机技术在 PC 端软件
中的编程应用 :
3. 1 上下位机软件的串行通讯协议
3. 1. 1 PDU 协议
PDU 数据单元 分为 两类 : 指 令类 PDU 和 数据 类
PDU 。指令 PDU 用于在通讯设备之间传输控制信息和状
态信息 ;数据 PDU 用于在通讯设备之间传输采集数据。
本协议 PDU 格式如下 :
功能代码 子功能代码 (可选) 参数数据
3. 1. 2 物理层(PH Y)
物理层接口的连接部件为 D 型 9 针连接器 ,其电平定
义为标准 RS 232 电平标准 ,上下位机通过 RS 232 串口实
现通讯。
3. 1. 3 逻辑链路子层(LLC)
逻辑链路子层的主要工作就是提供帧处理服务与差
错控制服务。
帧处理服务分为两种级别 ———数据帧服务和消息帧
服务。数据帧服务控制数据帧的比特数 ,停止位数 ;消息
帧服务提供消息帧头和消息帧尾来封装消息帧 ,以实现发
送与接收的消息同步 ,正确界定消息帧。在本通讯协议中
采用消息帧 ,用 ASCII 字符“:”标记消息帧头 ,以连续的
ASCII 字符 CR + LR 标记消息帧尾。
LLC 层还提供消息帧差错控制服务 :在本协议中采用
LRC 校验算法。通讯时 ,发送方按 LRC 算法生成 LRC 校
验码 ,按照高位在前低位在后的顺序附加在原始的消息帧
尾部 ,最后以“:”和 CR + L F 对消息进行封装。接收方按
LRC 算法将接收到的 LRC 校验码与在本地运算得到的校
验码进行对比 ,从而判断接收到的数据的正确性。
3. 1. 4 ADU 格式
上位机( PC 机) 与下位机通讯协议的 ADU 格式如下 :
: PDU LRC CR L R
3. 2 状态表的制定
按照本协议 ,上位机对接收到的每一帧消息进行校
验。其中功能代码 ,数据 ,LRC 码在一帧消息中处于一定
的先后位置 ,有相应的动作(如保存 ,校核等) 与之对应 ,可
以视为不同的状态 ,另外 ,消息帧头、帧尾等也视为不同的
状态。而对于每一个接收的字符 ,可以视其为触发信号。
如当接收的数据帧中出现数字时 ,视其为 DA TA SIG 信
号 ,当接收的数据帧中出现回车(CR) 字符时 ,视其为 CR
SIG 信号等 ,从而通过不断接收字符来触发状态的行为和
转移 ,使状态机循环工作下去。在分析数据通讯协议的基
础上 ,通过抽象 ,可将每帧消息分为如表 2 所示几个状态
及触发信号。
在表 2 中顶行列出了信号(触发或事件) ,最左边一列
是状态。各网格的内容是转换 ,他表示为{动作 ,下一状
态} 。例如 ,在 BEGIN 状态中 ,当接收到 COLON SIG 信
号时 ,将会调用 DoClear () 这一函数 ,从而完成一些数据复
位工作 ,同时 ,也触发状态到下一状态 ———MAINCODE
状态。而对于其他触发信号 ,状态机状态不变 ,仍在 BE2
GIN 状态 ,且什么动作也不做。COLON SIG 信号对应
数据帧的“:”字符 ,即当接收到“:”字符后 ,就向状态机发
出一 COLON SIG 信号 ,使状态机开始运行。而对接收
的其他字符 ,在没有收到“:”字符之前 ,协议要求不做处
理。表中的其他动作解释如下 :
RecordType() / / 记录功能代码 ,指令类代码或数据类
代码
DataAdd() / / 数据记录
DoLRC() / / 完成 LRC 校验工作
DoFinish() / / 数据检验完毕处理
ComRecord() / / 记录子功能代码
RecordState() / / 记录数据通道状态(正常或不正常)
DoError () / / 数据帧出错报告
3. 3 状态表的实现
上位机软件用 Visual C + + 6. 0 编写 ,C + + 语言基于面
向对象编程 (OOP) 思想 ,而状态机的核心机制是行为继
承 ,这与 OOP 模式很相似 ,超状态的行为能很容易地被子
状态所继承。鉴于此 ,可以利用 C + + 的类来表现状态行
为 ,即将状态行为嵌入到 C + + 类中 ,从而在 Visual C + + 平
台上可以很好地运行状态机。本状态表的实现就是利用
这一机制 ,父类封装了抽象的状态转换和当前状态行为 ,
子类实现具体的状态行为和状态转换。详见如下核心
代码。
741
《现代电子技术》2008 年第 3 期总第 266 期 t 电子技术应用 ü
? 1994-2008 China Academic Journal Electronic Publishing House. All rights reserved. http://www.cnki.net
表 2 数据通讯协议栈处理状态表
O THER SIG C SIG D SIG DA TA SIG CHAR SIG CR SIG L F SIG COLON SIG
BEGIN doNothing()
BEGIN
doNothing ()
BEGIN
doNothing()
BEGIN
doNothing()
BEGIN
doNothing()
BEGIN
doNothing()
BEGIN
doNothing()
BEGIN
DoClear ()
MAINCODE
MAINCODE Do Error ()
BEGIN
RecordType()
SUBCODE
RecordType()
DA TA
DoError ()
BEGIN
Do Error ()
BEGIN
doNothing()
MAINCODE
doNothing()
BEGIN
doNothing()
MAINCODE
DA TA Do Error ()
BEGIN
DataAdd()
DA TA
DataAdd()
DA TA
DataAdd()
DA TA
DoDataRecord()
DA TA
DoL RC()
DA TA
DoFinish()
BEGIN
Do Error ()
BEGIN
SUBCODE Do Error ()
BEGIN
ComRecord()
SUBCODE
ComRecord()
SUBCODE
ComRecord()
SUBCODE
RecordState()
PARAM
DoL RC()
SUBCODE
DoFinish()
BEGIN
Do Error ()
BEGIN
PARAM Do Error ()
BEGIN
ComRecord()
PARAM
ComRecord()
PARAM
ComRecord()
PARAM
Do Error ()
BEGIN
DoL RC()
PARAM
DoFinish()
BEGIN
Do Error ()
BEGIN
父类 StatusClass 类核心代码如下 :
/ / 定义成员函数指针
typedef void (StatusClass : : 3 Action) () ;
/ / 定义内层结构转换
struct Translate
{
Action action ; / / 抽象行为动作
unsigned nextStatus ; / / 抽象的下一动作
} ;
/ / 状态行为和转换
void StatusClass : :dispatch(unsigned const sig)
{
register Translate const 3 t = myTable + myState 3
myNsignals + sig ; / / 查状态表
(this - > 3 (t - > action) ) () ;
myState = t - > nextStatus ;
}
/ / 无状态行为的缺省动作
Void StatusClass : :doNothing()
{
??
}
子类 SubStatusClass 类核心代码如下 :
/ / 定义的信号
enum
Event{ O THER SIG, C SIG, D SIG, DA TA SIG,
CHAR SIG,CR SIG,L F SIG,COLON SIG,MAX SIG} ;
/ / 定义的状态
enum
State{BEGIN , MAINCODE ,DA TA , SUBCODE , PARAM ,
MAX STA TE} ;
/ / 初始化状态机
void SubStatusClass : :init ()
{
??
}
/ / 状态表 ,包含实际的状态行为和状态转换
(此状态表对应于表 2 所示状态和行为)
StatusClass : : Tran const SubStatusClassClass : : myTable
[ MAX STA TE][ MAX SIG] = {
{{ &StatusClass : :doNothing ,BEGIN} ,{ &StatusClass : :do2
Nothing , BEGIN } , { &StatusClass : : doNothing , BEGIN } ,
{ &StatusClass : : doNothing ,BEGIN} ,{ &StatusClass : : doNoth2
ing , BEGIN } , { &StatusClass : : doNothing , BEGIN } ,
{ &StatusClass : : doNothing , BEGIN } , { static cast < Status2
Class : : Action > ( &SubStatusClassClass : : DoClear ) , MAIN2
CODE}} ,
??
} ;
3. 4 状态类的调用
笔者已经将状态行为封装至 C + + 类当中 ,所以在程序
中调用时只须定义一个 StatusClass 类的对象即可运行状
态机了。同样 ,随着对象的销毁 ,状态机的生命周期也就
结束。在本软件中 ,数据的通讯协议栈处理是在单独一个
线程(预处理线程) 中完成的 ,而此线程的触发依赖于数据
的接收。故当有数据帧接收时便会使预处理线程恢复 ,从
而启动状态机 ,实现对数据帧的校验。状态类调用核心代
码如下 :
DWORD WINAPI
CMycomFirDlg : :Protocal Thread(L PVOID lp Parameter )
{
??
/ / 创建状态类对象
SubStatusClassClass myStatus ;
/ / 初始化状态机
myStatus. init () ;
??
/ / 状态机入口
myStatus. DealChar ()
??
/ / 挂起预处理线程
SuspendThread(Protocal handle) ;
}
4 结 语
实践证明 ,将状态机技术巧妙地运用于数据通讯协议
栈处理程序中 ,收到了很好的效果 ,不但可以避免大量的
Switch/ Case 语句 ,使程序简洁 ,而且提高了程序的扩展性
能 ,便于维护和修改。同时 ,由于状态机的行为嵌入到了
C + + 的类当中 ,可以视其为一个类 ,因而具有类的所有特
征 ,可以被继承 ,移植性能好 ,具有实用价值。
参 考 文 献
[1 ] 魏先民. 有限状态机在嵌入式软件中的应用[J ]. 潍坊学报 ,
2007 ,6 (4) :24 - 25.
[2 ] Samek Mico. Practical Statecharts in C/ C + + ———Quantem
Programming for Embedded Systems [ M ]. USA : CMP
Books (CMP Media LLC) ,2002.
作者简介 张腊明 男 ,1982 年出生 ,湖南长沙人 ,大连理工大学机械学院硕士研究生。主要研究方向为机电控制。
841
工 控 技 术 张腊明等 :状态机技术在数据通讯协议栈中的编程应用
? 1994-2008 China Academic Journal Electronic Publishing House. All rights reserved. http://www.cnki.net
状态机技术在数据通讯协议栈中的编程应用.pdf