经典状态机的层次式实现
作者:
yugen 提交日期:2008-8-28 12:08:00
| 分类: | 访问量:2195
![正常](http://blog.tianya.cn/blogger/smilies/icon_confused.gif)
在Miro Samek Ph.D.所著的《嵌入式系统的微模块化程序设计――实用程序状态图C/C++实现》中提到了三种经典状态机的实现方法。这三种方法都是非层次式的。通过独立的子状态变量来记录每个状态的子状态和在进入/退出动作中实现子状态的初始化/清除来实现经典状态机的层次式。
例子,一个状态机的部分状态图:
在上面的状态图中,顶层有两个状态:STATE_MEASURE和STATE_PASSWD,状态机初始化为STATE_MEASURE状态。而STATE_MEASURE还有三个子状态STATE_MEASURE_NORMAL、STATE_MEASURE_MIN_SUCCESS、STATE_MEASURE_MIN。
使用FSMState变量来记录顶层状态的状态,而使用StateMeasureState来记录STATE_MEASURE_NORMAL状态的子状态。有多少个状态有子状态,就要有多少个变量还记录这些子状态。
状态之间的切换是通过事件来切换的。图中只有一个事件,即EVKey事件(按键事件)。EVKey事件有一个KeyValue参数,指示按键的键值。在有子状态的顶层状态的处理器中,先检查事件是不是发送给自己的,若是,则直接处理;不是的话,则转发到子状态处理器处理。例如STATE_MEASURE状态下按下KEY_SET键处理方法。进入和退出事件是隐含的,在进入和退出每个状态时,都会产生(但不一定会有实际动作函数执行),另一个用处就是,初始化子状态。
对于状态机的使用者来说,所要做的就是检测事件-->发送事件到状态机(调用函数FSMDispatch(FSMState, (EVENT*)&Event);)。不需要关心状态机的内部状态及子状态的问题。
实现代码:
/******************************************************************************
*事件类型代码定义,定义各种类型的事件代号
******************************************************************************/
typedef enum _EVENT_ID
{
EVENT_ID_ENTRY, //进入事件,必须要有,用来初始化子状态
EVENT_ID_EXIT, //退出事件,保留
EVENT_ID_KEY //按键事件
//添加其他事件类型
} EVENT_ID;
/******************************************************************************
*事件定义,用于定义具体的事件,EVENT是一个基类事件,其他事件结构的第一个
*成员必须为EVENT_ID EventID;以能实现事件之间的强制转换
******************************************************************************/
typedef struct _EVENT
{
EVENT_ID EventID; //事件类型
} EVENT;
/******************************************************************************
*常量事件定义,包括进入事件和退出事件
******************************************************************************/
///
/// 进入事件定义
///
const EVENT EventEntry =
{
EVENT_ID_ENTRY,
};
///
/// 退出事件定义
///
const EVENT EventExit =
{
EVENT_ID_EXIT,
};
/******************************************************************************
*按键事件定义,声明此类型的变量时,必须将EventID设置为EVENT_ID_KEY
******************************************************************************/
typedef struct _EVENT_KEY
{
EVENT_ID EventID; //EventID为第一个成员,以能实现和EVENT转换
uint8 KeyValue; //按键值
}EVENT_KEY;
/******************************************************************************
*状态处理器指针定义
******************************************************************************/
typedef void (*FSM_STATE)(const EVENT *e);
/******************************************************************************
*状态机变量定义
******************************************************************************/
FSM_STATE FSMState= NULL; //最高层状态机当前状态
FSM_STATE StateMeasureState = NULL; //用于记录STATE_MEASURE状态的子状态
/******************************************************************************
*状态机处理器函数声明,以State前缀
******************************************************************************/
void StateMeasure(const EVENT *e);
void StateMeasureNormal(const EVENT *e); //为STATE_MEASURE子状态STATE_MEASURE_NORMAL的子状态处理器
void StateMeasureMin(const EVENT *e);
void StateMeausreMinSuccess(const EVENT *e);
void StatePasswd(const EVENT *e);
/******************************************************************************
*状态机的动作函数声明,以Act前缀
******************************************************************************/
void ActDispMainClear(void);
void ActSetMeasureMode(uint32 Mode);
//下略
///
/// 状态机转换到新的状态
///
/// 要转换状态的状态机(即状态机变量)
/// 新的状态(传递的是新的状态处理器函数)
void FSMTran(FSM_STATE *FSM, FSM_STATE target)
{
(*FSM)(&EventExit); //发送退出事件到上个状态处理器,以执行一些清除动作
*FSM = target;
(*FSM)(&EventEntry); //发送进入事件到新的状态处理器,若状态有子状态的话,将执行子状态初始化
}
///
/// 将事件分发到状态机。因为高层状态和子层状态有不同的状态机变量,所以需要一个参数来指明事件分发到那个状态机。
///
/// 接收分发事件的目标状态机
/// 要分发的事件
void FSMDispatch(FSM_STATE FSM, const EVENT *e)
{
FSM(e);
}
///
/// 状态机初始化,顶层状态的初始化
///
void FSMInit(void)
{
FSMState = StateMeasure; //状态机的初始状态是STATE_MEASURE
FSMDispatch(FSMState, &EventEntry);
}
///
/// STATE_MEASURE状态处理
///
/// 当前状态下要处理的事件
void StateMeasure(const EVENT *e)
{
EVENT_KEY *EventKey;
switch(e->EventID)
{
case EVENT_ID_ENTRY:
ActSetMeasureMode(MEASURE_NORMAL);
StateMeasureState = StateMeasureNormal; //首次进入STATE_MEASURE时,初始化子状态为STATE_MEASURE_NORMAL;
FSMDispatch(StateMeasureState, e);
break;
case EVENT_ID_EXIT:
if(StateMeasureState != NULL) //转发到子状态处理器处理
{
FSMDispatch(StateMeasureState, e);
}
break;
case EVENT_ID_KEY:
EventKey = (EVENT_KEY*)e; //事件转换成按键事件
if(EventKey->KeyValue == KEY_SET) //这里处理关键,顶层状态处理器先捕获事件,判断是不是发送到顶层的,若是则直接处理,不是,则转发到子状态处理器处理。
{
ActSetMeasureMode(STOP_MEASURE);
ActDispMainClear();
ActResetPasswd();
ActDispPasswd(InputPasswd, PasswdPos);
FSMTran(&FSMState, StatePasswd); //顶层状态转换,转换到STATE_PASSWD状态
}
else
{
FSMDispatch(StateMeasureState, e); //交给子状态机处理
}
break;
}
}
///
/// STATE_MEASURE_NORMAL状态处理
///
/// 当前状态下要处理的事件
void StateMeasureNormal(const EVENT *e)
{
EVENT_KEY *EventKey;
switch(e->EventID)
{
case EVENT_ID_KEY:
EventKey = (EVENT_KEY*)e;
if(EventKey->KeyValue == KEY_SET)
{
ActSetMeasureMode(STOP_MEASURE);
ActResetPasswd();
ActDispPasswd(InputPasswd, 0);
FSMTran(&FSMState, StatePasswd);
}
else if(EventKey->KeyValue == KEY_MIN)
{
ActSetMeasureMode(MEASURE_MIN);
FSMTran(&StateMeasureState, StateMeasureMin);
}
break;
}
}
///
/// STATE_MEASURE_MIN_SUCCESS状态处理
///
/// 当前状态下要处理的事件
void StateMeasureMinSuccess(const EVENT *e)
{
//EVENT_KEY *EventKey;
switch(e->EventID)
{
case EVENT_ID_KEY: //任意键
ActSetMeasureMode(MEASURE_NORMAL);
FSMTran(&StateMeasureState, StateMeasureNormal);
break;
}
}
///
/// STATE_MEASURE_MIN状态处理
///
/// 当前状态下要处理的事件
void StateMeasureMin(const EVENT *e)
{
EVENT_KEY *EventKey;
switch(e->EventID)
{
case EVENT_ID_KEY:
EventKey = (EVENT_KEY*)e;
if(EventKey->KeyValue == KEY_CANCEL)
{
ActSetMeasureMode(MEASURE_NORMAL);
FSMTran(&StateMeasureState, StateMeasureNormal);
}
else if(EventKey->KeyValue == KEY_MIN)
{
ActSetMeasureMode(MEASURE_MIN_OK);
FSMTran(&StateMeasureState, StateMeasureMinSuccess);
}
break;
}
}
在《嵌入式系统的微模块化程序设计――实用程序状态图C/C++实现》中提到了Ian Horrocks提出的一种方法也是使用独立的变量来表示层次,我没看过那篇文章,不知道是不是与此类似。
例子,一个状态机的部分状态图:
![](http://img14.tianya.cn/photo/2008/8/28/9717740_1452456.jpg)
在上面的状态图中,顶层有两个状态:STATE_MEASURE和STATE_PASSWD,状态机初始化为STATE_MEASURE状态。而STATE_MEASURE还有三个子状态STATE_MEASURE_NORMAL、STATE_MEASURE_MIN_SUCCESS、STATE_MEASURE_MIN。
使用FSMState变量来记录顶层状态的状态,而使用StateMeasureState来记录STATE_MEASURE_NORMAL状态的子状态。有多少个状态有子状态,就要有多少个变量还记录这些子状态。
状态之间的切换是通过事件来切换的。图中只有一个事件,即EVKey事件(按键事件)。EVKey事件有一个KeyValue参数,指示按键的键值。在有子状态的顶层状态的处理器中,先检查事件是不是发送给自己的,若是,则直接处理;不是的话,则转发到子状态处理器处理。例如STATE_MEASURE状态下按下KEY_SET键处理方法。进入和退出事件是隐含的,在进入和退出每个状态时,都会产生(但不一定会有实际动作函数执行),另一个用处就是,初始化子状态。
对于状态机的使用者来说,所要做的就是检测事件-->发送事件到状态机(调用函数FSMDispatch(FSMState, (EVENT*)&Event);)。不需要关心状态机的内部状态及子状态的问题。
实现代码:
/******************************************************************************
*事件类型代码定义,定义各种类型的事件代号
******************************************************************************/
typedef enum _EVENT_ID
{
EVENT_ID_ENTRY, //进入事件,必须要有,用来初始化子状态
EVENT_ID_EXIT, //退出事件,保留
EVENT_ID_KEY //按键事件
//添加其他事件类型
} EVENT_ID;
/******************************************************************************
*事件定义,用于定义具体的事件,EVENT是一个基类事件,其他事件结构的第一个
*成员必须为EVENT_ID EventID;以能实现事件之间的强制转换
******************************************************************************/
typedef struct _EVENT
{
EVENT_ID EventID; //事件类型
} EVENT;
/******************************************************************************
*常量事件定义,包括进入事件和退出事件
******************************************************************************/
///
/// 进入事件定义
///
const EVENT EventEntry =
{
EVENT_ID_ENTRY,
};
///
/// 退出事件定义
///
const EVENT EventExit =
{
EVENT_ID_EXIT,
};
/******************************************************************************
*按键事件定义,声明此类型的变量时,必须将EventID设置为EVENT_ID_KEY
******************************************************************************/
typedef struct _EVENT_KEY
{
EVENT_ID EventID; //EventID为第一个成员,以能实现和EVENT转换
uint8 KeyValue; //按键值
}EVENT_KEY;
/******************************************************************************
*状态处理器指针定义
******************************************************************************/
typedef void (*FSM_STATE)(const EVENT *e);
/******************************************************************************
*状态机变量定义
******************************************************************************/
FSM_STATE FSMState= NULL; //最高层状态机当前状态
FSM_STATE StateMeasureState = NULL; //用于记录STATE_MEASURE状态的子状态
/******************************************************************************
*状态机处理器函数声明,以State前缀
******************************************************************************/
void StateMeasure(const EVENT *e);
void StateMeasureNormal(const EVENT *e); //为STATE_MEASURE子状态STATE_MEASURE_NORMAL的子状态处理器
void StateMeasureMin(const EVENT *e);
void StateMeausreMinSuccess(const EVENT *e);
void StatePasswd(const EVENT *e);
/******************************************************************************
*状态机的动作函数声明,以Act前缀
******************************************************************************/
void ActDispMainClear(void);
void ActSetMeasureMode(uint32 Mode);
//下略
///
/// 状态机转换到新的状态
///
/// 要转换状态的状态机(即状态机变量)
/// 新的状态(传递的是新的状态处理器函数)
void FSMTran(FSM_STATE *FSM, FSM_STATE target)
{
(*FSM)(&EventExit); //发送退出事件到上个状态处理器,以执行一些清除动作
*FSM = target;
(*FSM)(&EventEntry); //发送进入事件到新的状态处理器,若状态有子状态的话,将执行子状态初始化
}
///
/// 将事件分发到状态机。因为高层状态和子层状态有不同的状态机变量,所以需要一个参数来指明事件分发到那个状态机。
///
/// 接收分发事件的目标状态机
/// 要分发的事件
void FSMDispatch(FSM_STATE FSM, const EVENT *e)
{
FSM(e);
}
///
/// 状态机初始化,顶层状态的初始化
///
void FSMInit(void)
{
FSMState = StateMeasure; //状态机的初始状态是STATE_MEASURE
FSMDispatch(FSMState, &EventEntry);
}
///
/// STATE_MEASURE状态处理
///
/// 当前状态下要处理的事件
void StateMeasure(const EVENT *e)
{
EVENT_KEY *EventKey;
switch(e->EventID)
{
case EVENT_ID_ENTRY:
ActSetMeasureMode(MEASURE_NORMAL);
StateMeasureState = StateMeasureNormal; //首次进入STATE_MEASURE时,初始化子状态为STATE_MEASURE_NORMAL;
FSMDispatch(StateMeasureState, e);
break;
case EVENT_ID_EXIT:
if(StateMeasureState != NULL) //转发到子状态处理器处理
{
FSMDispatch(StateMeasureState, e);
}
break;
case EVENT_ID_KEY:
EventKey = (EVENT_KEY*)e; //事件转换成按键事件
if(EventKey->KeyValue == KEY_SET) //这里处理关键,顶层状态处理器先捕获事件,判断是不是发送到顶层的,若是则直接处理,不是,则转发到子状态处理器处理。
{
ActSetMeasureMode(STOP_MEASURE);
ActDispMainClear();
ActResetPasswd();
ActDispPasswd(InputPasswd, PasswdPos);
FSMTran(&FSMState, StatePasswd); //顶层状态转换,转换到STATE_PASSWD状态
}
else
{
FSMDispatch(StateMeasureState, e); //交给子状态机处理
}
break;
}
}
///
/// STATE_MEASURE_NORMAL状态处理
///
/// 当前状态下要处理的事件
void StateMeasureNormal(const EVENT *e)
{
EVENT_KEY *EventKey;
switch(e->EventID)
{
case EVENT_ID_KEY:
EventKey = (EVENT_KEY*)e;
if(EventKey->KeyValue == KEY_SET)
{
ActSetMeasureMode(STOP_MEASURE);
ActResetPasswd();
ActDispPasswd(InputPasswd, 0);
FSMTran(&FSMState, StatePasswd);
}
else if(EventKey->KeyValue == KEY_MIN)
{
ActSetMeasureMode(MEASURE_MIN);
FSMTran(&StateMeasureState, StateMeasureMin);
}
break;
}
}
///
/// STATE_MEASURE_MIN_SUCCESS状态处理
///
/// 当前状态下要处理的事件
void StateMeasureMinSuccess(const EVENT *e)
{
//EVENT_KEY *EventKey;
switch(e->EventID)
{
case EVENT_ID_KEY: //任意键
ActSetMeasureMode(MEASURE_NORMAL);
FSMTran(&StateMeasureState, StateMeasureNormal);
break;
}
}
///
/// STATE_MEASURE_MIN状态处理
///
/// 当前状态下要处理的事件
void StateMeasureMin(const EVENT *e)
{
EVENT_KEY *EventKey;
switch(e->EventID)
{
case EVENT_ID_KEY:
EventKey = (EVENT_KEY*)e;
if(EventKey->KeyValue == KEY_CANCEL)
{
ActSetMeasureMode(MEASURE_NORMAL);
FSMTran(&StateMeasureState, StateMeasureNormal);
}
else if(EventKey->KeyValue == KEY_MIN)
{
ActSetMeasureMode(MEASURE_MIN_OK);
FSMTran(&StateMeasureState, StateMeasureMinSuccess);
}
break;
}
}
在《嵌入式系统的微模块化程序设计――实用程序状态图C/C++实现》中提到了Ian Horrocks提出的一种方法也是使用独立的变量来表示层次,我没看过那篇文章,不知道是不是与此类似。