A framework for event driven FSM

建立一个通用事件驱动的状态机:

  1. 状态机的架构与具体的事件和状态的定义分开.
  2. 状态与事件是低耦合的, 即状态不用关心是什么事件导致状态机进入这个状态.
  3. 每个状态的进入或者退出, 会触发对应的处理函数.

Implement

以下代码为了结构清晰, 会省略一些细节和保护处理, 也有可能不符合C的强类型语法要求, 就权当成伪代码看待吧, 直接copy & paste, 编译器肯定会报错滴 -,-

基本数据类型定义
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/* 事件 */
typedef struct {
	unsigned id;  // 区别不同的事件
	void *user_data;  // 可以携带私有的数据, 传递给处理函数
} event_type;

/* 状态 */
typedef struct {
	unsigned id;  // 用于区别不同的事件
	sm_cb enter;  // 进入此状态调用的callback, 可为NULL
	sm_cb leave;  // 推出此状态调用的callback, 可为NULL
} state_type;

/* 状态机 */
// 为了简化模型, 一些必要的保护信息略去(比如表的大小限制等)
typedef struct {
	unsigned current_state;  // 当前状态机所处的状态
	const state_type *state_table;  // 以state_type的id为索引建立的状态表
	const unsigned **transition_table;  // 以state_id和event_id组成的状态转换矩阵
} fsm_type;
API

需要2个函数:

  • fsm_new: 由状态表和转换表(具体可以参考后面的Example部分), 以及初始状态(状态机总是处于某种状态当中), 创建一个状态机
  • fsm_event_handler: 状态机输入一个新的事件. 可以是某个线程监听外部事件的变化, 然后调用这个函数更新状态; 也可以是state_type的callback调用, 比如, 某个状态机进入A state后, 经过1min延时后, 会自动进入B state.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
/* 此处省去参数类型定义 */

/* fsm_new: 创建fsm
 * parameters:
 * 		state_stable
 *		transition_table
 *		init_state -- 状态机初始状态
 * return: fsm
 */
fsm_type *fsm_new(state_table, transition_table, init_state)
{
	fsm_type *fsm = calloc(sizeof(fsm_type));

	fsm->state_table = state_table;
	fsm->transition_table = transition_table;

	/* enter init_event */
	fsm->current_state = init_state;
	fsm->state_table[fsm->current_state].enter(); // invoke enter callback

	return fsm;
}

/* fsm_event_handler: 接收处理事件
 * parameters:
 * 		fsm
 *		event -- 发生的事件
 */
void fsm_event_handler(fsm, event)
{
	fsm->state_table[fsm->current_state].leave();  // invoke leave callback
	
	/* lookup for transitation table for next state if current event given */
	next_state = fsm->transition_table[fsm->current_state][event->id];
	/* prepare to enter next state */
	fsm->current_state = next_state;
	fsm->state_table[fsm->current_state].enter();
}

Example

一个栗子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
/* Event id 以及 State id 定义 */
enum {
	EVENT_INVALID = -1,
	EVENT_A,
	EVENT_B,
	...
	EVENT_MAX,
};

enum {
	STATE_INVALID = -1,
	STATE_1,
	STATE_2,
	...
	STATE_MAX,
};

/* state table */
state_type xxx_state_table[STATE_MAX] = {
	[STATE_1] = {
		.id = STATE_1;
		.enter = state_1_enter_cb,  /* called when enter this state */
		.leave = state_1_leave_cb,  /* called when leave this state */
	},
	[STATE_2] = {
		.id = STATE_2;
		.enter = state_2_enter_cb,
	},
	...
};

/* state transition matrix */
unsigned xxx_transition_table[STATE_MAX][EVENT_MAX] = {
	[STATE_1] = {
		[EVENT_2] = STATE_4,
		[EVENT_5] = STATE_2,
		...
	}
	[STATE_2] ...
	...
};

xxx_fsm = fsm_new(xxx_state_table, xxx_transition_table, STATE_1;)

可以看到, 这里的关键之处在于那个xxx_transition_table (其实就是状态转移表中的二维状态表), 假设状态机处于STATE_1, 而这时候有人调用fsm_event_handler输入一个EVENT_5事件:

  • 根据这个transition table, 下一个的状态会是STATE_2;
  • fsm会从STATE_1离开, 因此会运行leave callback: state_1_leave_cb
  • fsm会进入STATE_2, 因此会调用enter callback: state_2_enter_cb
  • fsm将当前状态保存为STATE_2

留言