# openfsm **Repository Path**: liuzhuang111/openfsm ## Basic Information - **Project Name**: openfsm - **Description**: 简单易用、跨平台的有限状态机(Finite State Machine),支持在单片机上运行。 - **Primary Language**: C - **License**: MIT - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 24 - **Created**: 2024-04-24 - **Last Updated**: 2024-04-24 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # Open FSM ``` ____ ______ _____ __ __ / __ \ | ____/ ____| \/ | | | | |_ __ ___ _ __ | |__ | (___ | \ / | | | | | '_ \ / _ \ '_ \ | __| \___ \| |\/| | | |__| | |_) | __/ | | | | | ____) | | | | \____/| .__/ \___|_| |_| |_| |_____/|_| |_| | | |_| ``` ## 介绍 作为一个开源的FSM框架的c/c++实现,Open FSM可以解决过多的条件判断跳转等问题。 ## 特性 1、**使用难度小**,开发者在使用时,只需关心: - 状态机的初始化 - 各个状态的实现 - 各个状态之间应该如何切换 2、**支持嵌套**,开发者只需额外多做一点工作就可以实现基于Open FSM 的n阶状态机。 3、**跨平台**,Open FSM 在设计的时候便考虑了应用程序、内核环境以及单片机中的使用。 ## 函数接口 在使用`openfsm`之前,请先包含此头文件。 ```c #include "openfsm.h" ``` ### 定义状态值与状态处理函数 > 这两个宏定义是为了提高可读性而添加的。 ```c #define FSM_STATE(name) state_##name #define FSM_FUNCT(name) funct_##name ``` - `FSM_STATE`:定义状态值,后续切换时会使用 - `FSM_FUNCT`:定义状态处理函数,与状态对应。 例如: ```c /* FSM Functions */ void* FSM_FUNCT(init)(void * this_fsm); // 初始化状态 void* FSM_FUNCT(do_something)(void * this_fsm);//计数状态 void* FSM_FUNCT(done)(void * this_fsm);// 执行完成状态 void* FSM_FUNCT(err)(void* this_fsm); // 错误状态 /* FSM States */ enum procedure_state { FSM_STATE(init), FSM_STATE(count), FSM_STATE(done), FSM_STATE(err) }; ``` #### 参数 状态机处理函数要求指定一个`void *this_fsm`的参数。 `this_fsm`用于状态机处理函数在执行的时候,执行对应fsm接口函数。 例如: ```c void* FSM_FUNCT(init) (void* this_fsm) { SM_DATA *pd = get_data_entry(this_fsm); // 切换到下个状态 set_next_state(this_fsm, FSM_STATE(count)); // 数据处理 pd->cnt = 0; return NULL; } ``` #### 返回值 ##### 在状态中返回一个值 Open FSM 允许 在状态执行以后,返回对应的值。 将任何需要的值,强制转换为`AS_STEP_RETVAL`类型即可。 ```c void* FSM_FUNCT(xxx) (void* this_fsm) { SM_DATA *pd = get_data_entry(this_fsm); int *err_var; // 状态处理 set_next_state(this_fsm, FSM_STATE(count)); // 返回一个值 return (AS_STEP_RETVAL)-1; } ``` ##### 上层读出返回值 当调用者需要关心 状态机内部执行的状态时,可以通过下列函数得到返回值。 这个值通常是一个`void*`型的指针,具体交由调用者自行处理。 ```c void* get_step_retval(FSM* fsm); ``` - `fsm` :状态机 实例 例如: ```c step_ret ret = (step_ret) get_step_retval(&sub_fsm); ``` ### 定义状态机 为了跨平台的考虑,状态机的生命周期管理需要自行负责。 Open FSM不考虑动态内存申请。 例如: ```c // 静态申请 FSM fsm_1 = {0}; // 动态申请 FSM* p_fsm_1 = malloc(sizeof(FSM)); ``` ### 添加状态机跳转列表 在Open FSM中,状态机跳转列表代表了状态机中状态对应的处理函数。 ```c void set_procedures(FSM * fsm, Procedure *procedures); ``` - `fsm` :状态机 实例 - `procedures`: 一个事先声明好的状态机跳转列表。 例如: ```c /* 状态机跳转列表 */ static Procedure my_procedure_list[] = { FSM_FUNCT(init), FSM_FUNCT(do_something), FSM_FUNCT(done), FSM_FUNCT(err) }; set_procedures(&fsm_1, my_procedure_list); ``` ### 添加/获取数据域 Open FSM支持为状态机添加一个私有的数据域,用于灵活处理复杂的状态。 ```c void set_data_entry(FSM * fsm, void *data); void* get_data_entry(FSM * fsm); ``` - `fsm` :状态机 实例 - `data`:任意数据入口 `set_data_entry`传入的`data`可通过`get_data_entry`获取。 例如: ```c // 实际上使用到的数据 char my_data[128] = {0}; // 设置状态机数据域 set_data_entry(&fsm_1, my_data); // 获取状态机数据域 char *pd = get_data_entry(&fsm_1); ``` ### 设置默认状态 Open FSM规定,在运行状态机之前,需要明确指定一个初始状态。 ``` c void set_default_state(FSM * fsm, state st); ``` - `fsm` :状态机 实例 - `st`:状态值,是`FSM_STATE`定义的一个值。 例如: ```c set_default_state(&fsm_1, FSM_STATE(init)); ``` ### 错误处理 尽管 Open FSM 提供了`get_step_retval`用于获取状态机内部执行时的返回值。 如果对于错误处理有更高的要求,可以参考本节的内容以及对应的例程。 Open FSM 在节约内存开销的同时,也支持了一个用于错误处理的flag。 与 上述提及的 数据域 相似,Open FSM 也提供了一个错误域,调用者可自行指定任意类型的数据。 对应的接口如下: ```c typedef unsigned char state; // 告知调用者在执行过程中出现了错误。 set_fsm_error_flag(FSM* fsm); // 清除状态机错误 clr_fsm_error_flag(FSM* fsm); // 判断状态机在执行过程中是否出现错误。 state is_fsm_error(FSM* fsm); // 将某个容器设为状态机的错误域。 void set_err_var(FSM* fsm, void* err_var); // 获取状态机中的错误域,此后可以通过自己设计的方法将值读出。 void* get_err_var(FSM* fsm); ``` 例如: ```c void* FSM_FUNCT(xx)(void* this_fsm)// 错误状态 { int *err_var; // 通知 调用者 有错误发生 set_fsm_error_flag(this_fsm); // 把 错误值 设进 容器中(如果容器存在) err_var = get_err_var(this_fsm); if(err_var) *err_var = 0xff; return NULL; } // ... run_state_machine_once(&fsm_1); // 判断是否出错 if(is_fsm_error(&fsm_1)) { printf("Error when Stepping !\n"); got_err = get_err_var(&fsm_1); printf("Get Error Code :0x%x\n", *got_err); clr_fsm_error_flag(&fsm_1); } ``` ### 状态机执行与状态获取 #### 执行状态机 让 Open FSM 工作的真正接口是`run_state_machine_once`,原型如下: ```c // 执行状态机,使其步进一次 state run_state_machine_once(FSM * fsm); ``` - `fsm` :状态机 实例 执行完成以后,通过返回值可以获取到刚刚执行过的状态。 例如: ```c state cur_state; cur_state = run_state_machine_once(&fsm_1); printf("Ran :%d\n", cur_state); ``` #### 获取下一个状态 可以通过下列函数获取即将执行的状态。 ```c state get_next_state(FSM * fsm); ``` - `fsm` :状态机 实例 例如: ```c printf("Next :%d\n", get_next_state(&fsm_1)); ``` #### 重置状态机 ```c void init_state_machine(FSM *p); void reset_state_machine(FSM *p); ``` 上述的函数都会让状态机回到默认状态,但`reset_state_machine`会额外将错误状态清除。 ## 例程 本项目提供3个常见的例程,分别用于实现一阶状态机以及二阶状态机; 以及其中的错误处理。 详细的用法示例请参考 `src/example`中的具体实现 。