基于事件型的有限状态机1.状态机简介2.状态机实现3.基于事件型的有限状态机实现
基于事件型的有限状态机
1.状态机简介
现态:状态机现在的状态;
次态:状态机下一个状态;
事件:指触发状态机下一状态的所满足的条件,相应事件到达后状态机自动执行动作,并切换状态;
动作:状态机切换状态时执行的操作;
以上四点就是状态机的基本原理,利用状态机可以进行较为复杂的状态条状或用于编解码等。
2.状态机实现
利用switch-case就可以构建出一个最简单的状态机,如下:
while(1)
{
switch(state)
{
case state1:
state = state2;
//todo
break;
case state1:
state = state2;
//todo
break;
/***/
default:
break;
}
}
这样的实现方式结构简单,易于理解,但是在代码量多的情况下就不是十分友好了,不便于修改,同时case语句还容易遗漏break,导致BUG出现。
3.基于事件型的有限状态机实现
利用函数指针及触发事件表,结构更加清晰明了,修改起来也更方便。
3.1状态及事件枚举
xfms_state_e状态枚举,这里状态机一共有4个状态,可以根据实际需要增加或者减少;
typedef enum
{
s1,
s2,
s3,
s4,
}xfms_state_e;
xfms_event_e事件枚举,简单理解就是有哪些事件会导致状态机的状态改变,同样根据实际修改;
typedef enum
{
event_a,
event_b,
event_c,
event_d,
event_e,
}xfms_event_e;
3.2状态机结构体
typedef struct
{
xfms_event_e event;
xfms_state_e state;
xfms_state_e next_state;
void (*process)();
}xfms_t;
用一个结构体把状态机的现态、次态、事件及动作联系起来;
3.3触发表
通过直接赋值的方法,将状态机的跳转关系存储起来。
static xfms_t fms_trigger_table[] =
{
/* event, current state, next state, process function */
{event_a, s1, s2, func_a},
{event_b, s2, s3, func_b},
{event_c, s2, s4, func_c},
{event_d, s3, s4, func_d},
{event_e, s4, s1, func_e},
};
这里完全可以在状态机结构体中不定义event,而把event作为触发表的index去存储,这样可以实现O(1)的查找效率,如下:
typedef struct
{
xfms_state_e state;
xfms_state_e next_state;
void (*process)();
}xfms_t;
static xfms_t fms_trigger_table[] =
{
/* current state, next state, process function */
{s1, s2, func_a}, //event_a,
{s2, s3, func_b}, //event_b,
{s2, s4, func_c},
{s3, s4, func_d},
{s4, s1, func_e},
};
这样做有一些局限性,因为状态机的跳转取决于2个条件:现态和事件,也就是说同一个事件,状态机要根据不同的现态进行不同的动作。这也是变量名用fms_trigger_table而非fms_event_table的原因,在fms_trigger_table中可以定义对同一个事件定义多个触发,提高了通用性。
例如,用一个按键实现开关机,按键作为一个事件,去触发改变状态机的状态,状态机通过现态(on或者off)执行不同的操作。
static xfms_t fms_trigger_table[] =
{
/* event, current state, next state, process function */
{event_key_press, on, off, shutdown},
{event_key_press, off, on, boot},
};
3.4状态机的状态更新
遍历触发表fms_trigger_table,找到对应event并且满足状态的触发事件,执行动作后更新状态机的状态,在需要状态机状态改变时调用,传入对应event;
int xfms_state_update(int event)
{
for (int i = 0; i < sizeof(fms_trigger_table)/sizeof(fms_trigger_table[0]); i++)
{
if (event == fms_trigger_table[i].event)
{
if (current_state == fms_trigger_table[i].state)
{
if (fms_trigger_table[i].process != NULL)
{
fms_trigger_table[i].process();
}
current_state = fms_trigger_table[i].next_state;
return 0;
}
}
}
return -1;
}