1、简述状态机

在C语言编程中,可以使用状态机来实现复杂的控制流程和状态管理。
状态机通常由以下几个组成部分构成:

  • 状态(State):系统或程序可能处于的不同状态,例如"待机"、"运行"、"暂停"等。每个状态代表了系统或程序的一种行为或模式。
  • 事件(Event):触发状态转换的信号或条件,可以是外部输入、定时器到期、传感器触发等。事件发生时,状态机将根据当前状态和事件来确定下一个状态。
  • 转换(Transition):状态之间的切换过程,由事件触发。每个转换定义了从一个状态到另一个状态的条件和操作。
  • 动作(Action):状态转换时执行的操作或任务,例如更新变量、调用函数、发送消息等。动作可以在转换时执行,也可以在进入或离开某个状态时执行。

状态机.png

这张图阐述了一个洗衣机的状态机,主要分为六个子状态,分别是模式选择状态、进水状态、漂洗状态、甩干状态、完成状态与暂停状态,其中模式选择状态完成后可以进入洗衣状态(进水->漂洗->甩干->完成)也可以直接进入甩干状态,这就是状态转换,在洗衣服的过程中,可能会接收到暂停按键被按下的事件,因此要对暂停事件进行处理,这就是事件,在每种子状态下,又有些任务需要不断完成,这就是动作.

2、状态机实现

我们可以使用if else 或者switch case来实现,但是我们更希望将它设计成一个组件,来实现多种情况下的移植使用

状态机节点

状态机中的每个状态都有进入、退出、运行、事件四个元素,使用回调函数的方式实现:

typedef void (*FUN_FSM_WORK)(FSM_STATE *fsm);
typedef void (*FUN_FSM_EVENT)(FSM_STATE *fsm, uint32_t event, uint32_t param);
typedef struct
{
    void (*init)(FSM_STATE *fsm); 
    void (*exit)(FSM_STATE *fsm);
    FUN_FSM_WORK work;
    FUN_FSM_EVENT event;
} FSM_NODE;

一个状态机可以拥有多个状态,可以将子状态使用数组保存起来,并记录每个状态所对应的ID号

typedef struct __FSM_STATE
{
    const FSM_NODE **NODE; 
    const uint16_t num;    // 状态数量
    uint16_t state;        // 当前所在状态
    uintptr_t param;       // 传参
} FSM_STATE;

一个状态机包括初始化、状态转移、状态任务运行、事件响应四个函数:

void FSM_INIT(FSM_STATE *fsm, uint16_t initState, uintptr_t param);
void FSM_TRANS(FSM_STATE *fsm, uint16_t newState);
void FSM_WORK(FSM_STATE *fsm);
void FSM_EVENT(FSM_STATE *fsm, uint32_t event, uint32_t param);

状态机初始化实现如下,实质上是运行的子状态的进入函数

void FSM_INIT(FSM_STATE *fsm, uint16_t initState, uintptr_t param)
{
    ASSERT(fsm != NULL);
    ASSERT(initState < fsm->num);
    fsm->state = initState;
    fsm->param = param;
    if(fsm->NODE[fsm->state]->init)
    {
        fsm->NODE[fsm->state]->init(fsm);
    }
}

状态转换的实现如下,主要流程为:1、运行上一个状态的退出函数 2、状态编号改变 3、运行新状态的进入函数

void FSM_TRANS(FSM_STATE *fsm, uint16_t newState)
{
    ASSERT(fsm != NULL);
    ASSERT(newState < fsm->num);
    if (fsm->NODE[fsm->state]->exit)
    {
        fsm->NODE[fsm->state]->exit(fsm);
    }
    fsm->state = newState;
    if (fsm->NODE[fsm->state]->init)
    {
        fsm->NODE[fsm->state]->init(fsm);
    }
}

状态任务运行的实现如下,主要是循环运行当天状态的work函数

void FSM_WORK(FSM_STATE *fsm)
{
    ASSERT(fsm != NULL);
    if (fsm->NODE[fsm->state]->work)
    {
        fsm->NODE[fsm->state]->work(fsm);
    }
}

事件处理的实现如下,主要是函数回调到当前子状态下的event函数;

void FSM_EVENT(FSM_STATE *fsm, uint32_t event, uint32_t param)
{
    ASSERT(fsm != NULL);
    if (fsm->NODE[fsm->state]->event)
    {
        fsm->NODE[fsm->state]->event(fsm, event, param);
    }
}
文章目录