科技始終來自人性。最近剛好看到別人寫的SM有點糟糕,於是想起自己之前寫的,感覺也是不夠直覺,於是改寫了一下。主要概念還是根據SM的定義。
An abstract state machine is a software component that defines a finite set of states:
- One state is defined as the initial state. When a machine starts to execute, it automatically enters this state.
- Each state can define actions that occur when a machine enters or exits that state.
- Each state can define events that trigger a transition.
- A transition defines how a machine would react to the event, by exiting one state and entering another state.
所以就從需求先定義API,再來實作內容。首先想到的是需要一個API來初始化這個SM,於是就有sm_alloc()誕生,並回傳sm這個抽象結構,sm_free()是用來釋放該SM的(destroy)。
typedef void * sm;
sm sm_alloc(char *name, void *data);
int sm_free(sm s);
從
Each state can define actions that occur when a machine enters or exits that state. 這句化,建立了API int sm_state_add(sm s, int state, sm_fp enter, sm_fp exit),用以建立sm中的"狀態",並且綁定enter action與exit action。
typedef int(*sm_fp)(void *data);
int sm_state_add(sm s, int state, sm_fp enter, sm_fp exit);
int sm_state_del(sm s, int state);
從
Each state can define events that trigger a transition與
A transition defines how a machine would react to the event, by exiting one state and entering another state.這句話中,建立了API int sm_event_add(sm s, int state, int event, int new_state, sm_fp action),用於建立狀態中的"事件",指定"新狀態",以及"事件對應的動作"。並且使用API int sm_run(sm s, int new_event)讓sm根據收到的"事件"執行。
int sm_event_add(sm s, int state, int event, int new_state, sm_fp action);
int sm_event_del(sm s, int state, int event);
int sm_run(sm s, int new_event);
根據
One state is defined as the initial state. When a machine starts to execute, it automatically enters this state.,我們定義了API int sm_init_state_set(sm s, int state)用以設定"初始狀態"。
int sm_current_state(sm s);
至此所有API都齊全了,以下是sm.h,其中我用marco稍微讓這個API多存一些資訊。
#ifndef _SM_H_
#define _SM_H_
typedef void * sm; /**< State machine handle */
typedef int(*sm_fp)(void *data); /**< State machine function pointer */
sm sm_alloc(char *name, void *data); /**< Allocate a state machine */
int sm_free(sm s); /**< Free a state machine */
#define sm_state_add(s, state, enter, exit) _sm_state_add(s, state, #state, (sm_fp)enter, #enter, (sm_fp)exit, #exit)
int _sm_state_add(sm s, int state, const char *st_name, sm_fp enter, const char * ent_fname, sm_fp exit, const char * exit_fname); /**< Add a state */
int sm_state_del(sm s, int state); /**< Delete a state */
int sm_run(sm s, int new_event); /**< Run the state machine */
#define sm_event_add(s, state, event, new_state, action) _sm_event_add(s, state, event, #event, new_state, (sm_fp)action, #action)
int _sm_event_add(sm s, int state, int event, const char *ev_name, int new_state, sm_fp action, const char *action_fname); /**< Add an event */
int sm_event_del(sm s, int state, int event); /**< Delete an event */
int sm_init_state_set(sm s, int state); /**< Set the initial state */
int sm_current_state(sm s); /**< Get the current state */
int sm_dump_state(sm s); /**< Dump the state machine */
#endif
接下來要解釋一下每個API的時作內容,首先是sm sm_alloc(char *name, void *data),我的想法是不限制有多少"狀態",所以要用link list去串SM中的每一個狀態,每個狀態都去串始於自己的"事件",為了在刪除"狀態"時,也能刪除指向該"狀態"的"事件",於於是我在每個"狀態"中多存了"被指到的事件(pointed_event_ll)"。
#include "sm.h"
#include "list.h"
struct sm_state {
struct list_head state_ll;
int state;
const char *st_name;
sm_fp enter;
const char *ent_fname;
sm_fp exit;
const char *exit_fname;
struct list_head event_ll;
struct list_head pointed_event_ll;
};
struct _sm {
void *v;
char *name;
struct list_head state_ll;
struct sm_state *cur_sm_st;
pthread_mutex_t mutex;
};
sm sm_alloc(char *name, void *data)
{
struct _sm *sm;
sm = (struct _sm *) malloc(sizeof(struct _sm));
if (!sm) {
sm_pr_err("malloc failed\n");
return NULL;
}
sm->v = data;
sm->name = strdup(name);
sm->cur_sm_st = NULL;
if (!sm->name) {
sm_pr_err("malloc name failed\n");
free(sm);
return NULL;
}
INIT_LIST_HEAD(&apm;sm->state_ll);
pthread_mutex_init(&sm->mutex, NULL);
return sm;
}
int sm_free(sm s)
{
struct _sm *sm = (struct _sm *) s;
struct sm_state *sm_st, *tmp_sm_st;
struct sm_event *sm_ev, *tmp_sm_ev;
if (!sm) {
return -1;
}
list_for_each_entry_safe(sm_st, tmp_sm_st, &sm->state_ll, state_ll) {
list_for_each_entry_safe(sm_ev, tmp_sm_ev, &st->event_ll, event_ll) {
list_del(&sm_ev->event_ll);
free(sm_ev);
}
list_del(&sm_st->state_ll);
free(sm_st);
}
pthread_mutex_destroy(&sm->mutex);
free(sm->name);
free(sm);
return 0;
}
接著要說一下int sm_state_add(sm s, int state, sm_fp enter, sm_fp exit),其實只要判斷不存在要建立的state,剩下就是把資訊存到struct sm_state *而已,而sm_state_del()就是把對應的event都刪除後,釋放對應的resource。
static struct sm_state *sm_get_sm_state(sm s, int state)
{
struct _sm *sm = (struct _sm *) s;
struct sm_state *sm_st;
list_for_each_entry(sm_st, &sm->state_ll, state_ll) {
if (sm_st->state == state) {
return st;
}
}
return NULL;
}
int _sm_state_add(sm s, int state, const char *st_name, sm_fp enter, const char * ent_fname, sm_fp exit, const char *exit_fname)
{
struct _sm *sm = (struct _sm *) s;
struct sm_state *sm_st;
if (!sm) {
sm_pr_err("invalid argument\n");
return -1;
}
// if state is already exist, return -1
sm_st = sm_get_sm_state(s, state);
if (sm_st) {
sm_pr_err("state exist\n");
return -1;
}
sm_st = (struct sm_state *) malloc(sizeof(struct sm_state));
if (!sm_st) {
sm_pr_err("malloc failed\n");
return -1;
}
sm_st->state = state;
sm_st->st_name = st_name;
sm_st->enter = enter;
sm_st->ent_fname = ent_fname;
sm_st->exit = exit;
sm_st->exit_fname = exit_fname;
INIT_LIST_HEAD(&sm_st->event_ll);
INIT_LIST_HEAD(&sm_st->pointed_event_ll);
list_add_tail(&sm_st->state_ll, &sm->state_ll);
return 0;
}
int sm_state_del(sm s, int state)
{
struct _sm *sm = (struct _sm *) s;
struct sm_state *sm_st;
struct sm_event *sm_ev, *tmp_sm_ev;
if (!sm) {
sm_pr_err("invalid argument\n");
return -1;
}
sm_st = sm_get_sm_state(s, state);
if (!sm_st) {
sm_pr_err("state is not exist\n");
return -1;
}
list_for_each_entry_safe(sm_ev, tmp_sm_ev, &sm_st->event_ll, event_ll) {
list_del(&sm_ev->event_ll);
free(sm_ev);
}
list_for_each_entry_safe(sm_ev, tmp_sm_ev, &sm_st->pointed_event_ll, pointed_event_ll) {
sm_st = sm_ev->sm_state;
list_del(&sm_ev->pointed_event_ll);
}
list_del(&sm_st->state_ll);
free(sm_st);
return 0;
}
int sm_event_add(sm s, int state, int event, int new_state, sm_fp action);要先判斷"狀態"與"新狀態"存在,且"事件"不存在,接著把該"事件"串到該"狀態"去。
static struct sm_event *sm_get_sm_event(struct sm_state *st, int event)
{
struct sm_event *sm_ev;
list_for_each_entry(sm_ev, &st->event_ll, event_ll) {
if (sm_ev->event == event) {
return sm_ev;
}
}
return NULL;
}
int _sm_event_add(sm s, int state, int event, const char *ev_name, int new_state, sm_fp action, const char *action_fname)
{
struct _sm *sm = (struct _sm *) s;
struct sm_state *sm_st, *new_sm_st;
struct sm_event *sm_ev;
if (!sm) {
sm_pr_err("invalid argument\n");
return -1;
}
sm_st = sm_get_sm_state(s, state);
if (!sm_st) {
sm_pr_err("state is not exist\n");
return -1;
}
sm_ev = sm_get_sm_event(sm_st, event);
if (sm_ev) {
sm_pr_err("event is already exist\n");
return -1;
}
new_sm_st = sm_get_sm_state(s, new_state);
if (!new_sm_st) {
sm_pr_err("new state is not exist\n");
return -1;
}
sm_ev = (struct sm_event *) malloc(sizeof(struct sm_event));
if (!sm_ev) {
sm_pr_err("malloc failed\n");
return -1;
}
sm_ev = (struct sm_event *) malloc(sizeof(struct sm_event));
if (!sm_ev) {
sm_pr_err("malloc failed\n");
return -1;
}
sm_ev->sm_state = sm_st;
sm_ev->event = event;
sm_ev->ev_name = ev_name;
sm_ev->new_sm_state = new_sm_st;
sm_ev->action = action;
sm_ev->action_fname = action_fname;
list_add_tail(&sm_ev->event_ll, &sm_st->event_ll);
list_add_tail(&sm_ev->pointed_event_ll, &new_sm_st->pointed_event_ll);
return 0;
}
int sm_event_del(sm s, int state, int event)
{
struct _sm *sm = (struct _sm *) s;
struct sm_state *sm_st;
struct sm_event *sm_ev;
if (!sm) {
sm_pr_err("invalid argument\n");
return -1;
}
sm_st = sm_get_sm_state(s, state);
if (!sm_st) {
sm_pr_err("state is not exist\n");
return -1;
}
sm_ev = sm_get_sm_event(sm_st, event);
if (!sm_ev) {
sm_pr_err("event is not exist\n");
return -1;
}
list_del(&sm_ev->event_ll);
free(sm_ev);
return 0;
}
int sm_init_state_set(sm s, int state),其實就是找到,該"狀態",然後把SM的cur_sm_st指向它
int sm_init_state_set(sm s, int state)
{
struct _sm *sm = (struct _sm *) s;
struct sm_state *sm_st;
if (!sm) {
sm_pr_err("invalid argument\n");
return -1;
}
sm_st = sm_get_sm_state(s, state);
if (!sm_st) {
sm_pr_err("state is not exist\n");
return -1;
}
sm->cur_sm_st = sm_st;
return 0;
}
最後是 int sm_run(sm s, int event),從cur_sm_st去找對應的"事件",如果找到,就執行離開該"狀態"的"動作",接著觸發該"事件"的"動作",最後設定"新狀態"為cur_sm_st,並執行新狀態的進入"動作"
int sm_run(sm s, int event)
{
struct _sm *sm = (struct _sm *) s;
struct sm_state *sm_st;
struct sm_event *sm_ev;
if (!sm) {
sm_pr_err("invalid argument\n");
return -1;
}
pthread_mutex_lock(&sm->mutex);
if (!sm->cur_sm_st) {
sm_pr_err("invalid stats\n");
pthread_mutex_unlock(&sm->mutex);
return -1;
}
sm_st = sm->cur_sm_st;
sm_ev = sm_get_sm_event(sm_st, event);
if (!sm_ev) {
sm_pr_err("event is not exist\n");
pthread_mutex_unlock(&sm->mutex);
return -1;
}
if (sm->cur_sm_st->exit) {
sm->cur_sm_st->exit(sm->v);
}
if (sm_ev->action) {
sm_ev->action(sm->v);
}
sm->cur_sm_st = sm_ev->new_sm_state;
if (sm->cur_sm_st->enter) {
sm->cur_sm_st->enter(sm->v);
}
pthread_mutex_unlock(&sm->mutex);
return 0;
}
剩下的僅是一些協助的API
int sm_current_state(sm s)
{
struct _sm *sm = (struct _sm *) s;
if (!sm) {
sm_pr_err("invalid argument\n");
return -1;
}
return sm->cur_sm_st->state;
}
int sm_dump_state(sm s)
{
struct _sm *sm = (struct _sm *) s;
struct sm_state *sm_st, *ori_sm_st;
struct sm_event *sm_ev;
if (!sm) {
sm_pr_err("invalid argument\n");
return -1;
}
list_for_each_entry(sm_st, &sm->state_ll, state_ll) {
printf("state: %d/%p/%s\n", sm_st->state, sm_st, sm_st->st_name);
printf("\tenter_fp: %p, enter_fname: %s\n", sm_st->enter, sm_st->enter?sm_st->ent_fname:"");
printf("\texit_fp: %p, exit_fname: %s\n", sm_st->exit, sm_st->exit?sm_st->exit_fname:"");
list_for_each_entry(sm_ev, &sm_st->event_ll, event_ll) {
printf("\tevent: %d/%p/%s, new_state: %d/%p/%s, ev_fp:%p/%s\n", sm_ev->event, sm_ev, sm_ev->ev_name,
sm_ev->new_sm_state->state, sm_ev->new_sm_state, sm_ev->new_sm_state->st_name,
sm_ev->action, sm_ev->action?sm_ev->action_fname:"");
}
// pointed event is the event that point to this state
list_for_each_entry(sm_ev, &sm_st->pointed_event_ll, pointed_event_ll) {
ori_sm_st = sm_ev->sm_state;
printf("\tpointed event: %d/%p/%s, from state: %d/%p/%s, ev_fp:%p/%s\n", sm_ev->event, sm_ev, sm_ev->ev_name,
ori_sm_st->state, ori_sm_st, ori_sm_st->st_name,
sm_ev->action, sm_ev->action?sm_ev->action_fname:"");
}
}
return 0;
}