前言
之前介绍的状态机是有限状态机(FSM),这篇介绍一下分层状态机(HSM)的理解。
分层状态机的设计有助于降低系统复杂度,并且能够更好地组织系统的功能和行为。在分层状态机中,每个状态机负责处理一个特定的子任务或功能,这使得系统更易于理解和维护。在分层状态机中,高层状态机负责协调和控制下层状态机的行为。高层状态机可以通过向下层状态机发送命令或事件来触发状态转换,下层状态机可以将其处理结果返回给上层状态机,以便上层状态机作出相应的决策。
区别
分层状态机(HSM)和有限状态机(FSM)是两种不同的模型,虽然它们都用于描述系统的行为和控制流,但它们之间还是存在一些区别的。
(资料图片仅供参考)
层次结构不同:分层状态机是将系统划分为多个层级,并将每个层级表示为一个状态机,每个状态机有自己的输入和输出,可以与其他状态机进行交互,形成整个系统的行为。而有限状态机则是单层的结构,描述系统在特定条件下的状态和状态之间的转移。
状态数量不同:分层状态机中的状态数量通常比有限状态机更多。由于分层状态机的每个层级都有自己的状态集合,因此整个系统的状态集合是各层状态集合的并集。而有限状态机只有一个状态集合,状态数量相对较少。
状态之间的转移规则不同:在分层状态机中,状态之间的转移规则可以根据不同的层级进行定义,每个状态机有自己的转移规则。而有限状态机的状态之间的转移规则通常是全局统一的。
功能不同:分层状态机通常用于描述复杂系统的行为和控制流,其设计目的是为了降低系统的复杂度并更好地组织系统的功能和行为。而有限状态机通常用于描述简单的控制流程或者算法。
总之,分层状态机和有限状态机虽然都是描述系统行为和控制流的模型,但是它们的层次结构、状态数量、状态之间的转移规则和应用场景等方面存在较大的不同
举例说明
假设有一个自动售货机,它需要根据用户选择的按钮来售出对应的商品。我们可以使用有限状态机和分层状态机分别来实现这个自动售货机。
首先,我们来看一下如何使用有限状态机来实现自动售货机。假设我们有三个商品可以售卖,分别是饮料、糖果和薯片,对应的按钮分别为A、B和C。那么,我们可以定义以下状态:
根据这些状态,我们可以设计状态转移规则如下:
流程图:
接下来,我们来看一下如何使用分层状态机来实现自动售货机。首先,我们可以将自动售货机分解成三个层次:选择商品层、支付层和出货层。每个层次都有自己的状态和状态转移规则,如下所示:
然后,我们可以进一步细化每个层次的状态和状态转移规则,如下所示:
流程图:
需要注意的是,有限状态机和分层状态机都可以用于自动售货机的设计,选择哪种方法取决于具体的需求和复杂度。如果系统相对简单,可以使用有限状态机。如果需要管理多个子系统、处理多个事件和状态,可以考虑使用分层状态机。
最重要的是,在实际应用中,理解分层状态机的关键是正确地设计状态转移逻辑和事件处理函数。如果状态转移逻辑不正确,可能会导致状态机无法正确响应事件;如果事件处理函数不正确,可能会导致状态机无法正确处理事件。因此,在设计分层状态机时,需要认真考虑状态转移和事件处理的逻辑,并进行充分的测试和验证。
代码参考
有限状态机
有限状态机可以使用 switch
/case
或 if
/else
语句实现状态转移,并在每个状态转移时执行相应的操作。
#include #include enumState{ST_IDLE,ST_WAIT_SELECT,ST_WAIT_PAY,ST_DISPENSE};enumButton{BTN_NONE,BTN_A,BTN_B,BTN_C};enumStatecurrent_state=ST_IDLE;enumButtoncurrent_button=BTN_NONE;voidupdate_fsm(enumButtonbutton){switch(current_state){caseST_IDLE:if(button!=BTN_NONE){current_button=button;current_state=ST_WAIT_SELECT;printf("商品选择:%c\n",button);}break;caseST_WAIT_SELECT:if(button==BTN_NONE){current_state=ST_WAIT_PAY;printf("等待支付...\n");}break;caseST_WAIT_PAY:if(button==BTN_NONE){current_state=ST_DISPENSE;printf("出货中...\n");}break;caseST_DISPENSE:if(button==BTN_NONE){current_state=ST_IDLE;printf("出售完成\n");}break;}}intmain(){update_fsm(BTN_NONE);update_fsm(BTN_A);update_fsm(BTN_NONE);update_fsm(BTN_NONE);update_fsm(BTN_NONE);update_fsm(BTN_NONE);return0;}
分层状态机
而分层状态机中由于更加复杂,单纯地采用 switch
/case
或 if
/else
语句已经不太适合不同状态机及其子状态的转移,最好的方式是采用表驱动的方式去实现,然后在数组表中定义每个状态机及其子状态,并且有回调函数用来处理每个状态机及其子状态的处理等,提高系统的可维护性和可扩展性。
由于实现较为复杂,以下代码仅供参考,不具体实现
#include #include #include //状态机状态枚举typedefenum{STATE_IDLE,STATE_WAITING_FOR_COIN,STATE_WAITING_FOR_SELECTION,STATE_DISPENSING,STATE_COUNT}state_t;//事件枚举typedefenum{EVENT_INSERT_COIN,EVENT_MAKE_SELECTION,EVENT_DISPENSE,EVENT_COUNT}event_t;//状态转移条件函数类型定义typedefbool(*condition_func_t)(void);//状态机状态结构体定义typedefstruct{void(*enter_func)(void);//进入状态函数void(*exit_func)(void);//退出状态函数void(*poll_func)(void);//事件轮询处理函数condition_func_t(*cond_func)(void);//转移条件函数}state_info_t;//状态转移表staticconstinttransition_table[STATE_COUNT][EVENT_COUNT]={//EVENT_INSERT_COINEVENT_MAKE_SELECTIONEVENT_DISPENSE{STATE_WAITING_FOR_COIN,STATE_IDLE,STATE_IDLE},//STATE_IDLE{STATE_WAITING_FOR_COIN,STATE_WAITING_FOR_SELECTION,STATE_IDLE},//STATE_WAITING_FOR_COIN{STATE_WAITING_FOR_COIN,STATE_WAITING_FOR_SELECTION,STATE_DISPENSING},//STATE_WAITING_FOR_SELECTION{STATE_WAITING_FOR_COIN,STATE_WAITING_FOR_SELECTION,STATE_IDLE}//STATE_DISPENSING};//状态机状态数组staticconststate_info_tstate_table[STATE_COUNT]={//进入状态函数退出状态函数事件轮询处理函数转移条件函数{NULL,NULL,NULL,NULL},//STATE_IDLE{wait_for_coin_enter,wait_for_coin_exit,wait_for_coin_poll,wait_for_coin},//STATE_WAITING_FOR_COIN{wait_for_select_enter,wait_for_select_exit,wait_for_select_poll,waiting_for_select},//STATE_WAITING_FOR_SELECTION{dispensing_enter,dispensing_exit,dispensing_poll,dispensing}//STATE_DISPENSING};//当前状态staticstate_tcurrent_state=STATE_IDLE;