【Linux】状态机

Linux 状态机

首先感谢阅读,作者是在工作中学习与积累,每一个笔记都是心得和积累,希望可以和大家一起交流学习。


学习咱们先定好计划,需要了解的都一样,有:

  1. (状态机)是什么?
  2. (状态机)为什么?
  3. (状态机)有什么?
  4. (状态机)怎么做?(实战)
  5. (状态机)扩展

1. (状态机)是什么?

官方定义:状态机是有限状态自动机的简称,是现实实物运行规则抽象而成的一个数学模型

其实,简而言之,就是设备根据不同的场景下,不同触发的条件,进行不同行为的一个运行机制。

举个栗子:

早晨是晴天,突然下雨了,天气变为雨天,我打开了伞;

下午雨停了,天气变为晴天,我收起了伞。

// 伪代码如下
// 两种状态
#define RAIN 0
#define SUN 1

uint_8 weather = SUN; //早晨是晴天

void main(void)
{
    while (1)
    {
        if (weather == SUN)
        {
            if (event == rain)      // 触发事件下雨了
            {
                open_umbrella();    // 打开伞
                weather = RAIN;     // 对应状态切位雨天
            }
            else
            {
                /* do nothing */    // 雨天状态下雨,伞已经开了,啥都不做
            }
        }
        else if (weather == RAIN)
        {
            if (event == rain_stop)      // 触发事件下雨了
            {
                close_umbrella();    // 打开伞
                weather = SUN;     // 对应状态切位雨天
            }
            else
            {
                /* do nothing */    // 雨天状态下雨,伞已经开了,啥都不做
            }
        }
        else
        {
            printf("天气出错了!");
        }
    }
}

可以看出去,这个状态机包含两种状态的切换(雨天和晴天),包含两种触发事件(下雨和雨停),使用if else或者switch便可以轻松实现,这就是最简单的状态机。


2. (状态机)为什么?

从上一节可以看出,状态机的优点:

具有有限数量的状态,任意时刻都处于有限状态集合中的某一状态。
(有事件(event)发生时,将从当前状态转换到另一个状态,或者仍然保持在当前状态。)
优点:编程快速简单。易于调试,很少的计算开销 ,灵活性

当设备的状态只有两个,触发事件和对应的操作只有两个时if else还绰绰有余。但是当状态不停增加时,触发事件也不停增加时,难道if else一直嵌套吗?

所以有了linux设备相对标准的状态机(至少博主目前的物联网设备跑的都是这个状态机)


3.(状态机)有什么?

也就是状态机的数据构造

3.1 状态(STATE)

首先判断自己项目包含几种运行状态,一定做到全面性覆盖设备的所有状态

此处博主用自己的蓝牙-GPS定位项目(使用LoRa通信)为例

typedef enum
{
    FW_STATE_BEGIN = 0,          // 起始状态
    FW_STATE_INIT,                      // 初始化状态
    FW_STATE_JOIN,                    // Join网络状态
    FW_STATE_WORKING,          // 工作状态
    FW_STATE_POWER_OFF,    // 下电状态
    FW_STATE_RESTRICT,          // 限制状态
    FW_STATE_MAX,                     // 用于STATE合法性判断
} FRAMEWORK_STATE;

为什么要有一个起始状态,状态机初始化也是需要一个默认状态的,就比如1.里的晴天SUN,一定得是设备已经处在一个状态中,才可以开启状态机运行。

3.2 事件(EVENT)

事件的设计,也要遵从全面性,事件可以将3.1的状态衔接起来

typedef enum
{
    EVENT_POWER_ON = 0,                 //开机
    EVENT_LOW_POWER,                     //关机
    EVENT_BUTTON_PUSH,                //短按按键
    EVENT_BUTTON_LONG_PUSH,  //长按按键
    EVENT_RESTRICT,                            //限制
    EVENT_INIT_OK,                               //初始化完成
    EVENT_JOIN_OK,                             //join网络完成
    EVENT_JOIN_FAILED,                     //join网络失败
    EVENT_HB_ACK_OK,                      //心跳应答接收成功,心跳上报成功
    EVENT_HB_ACK_LOST,                 //心跳应答丢失,心跳上报失败
    EVENT_CONNECTION_LOST,      //连接丢失
    EVENT_MAX,                                       //用于EVENT合法性判断
} FRAMEWORK_EVENT;

3.3 行为(ACTION)

当3.1的状态+3.2的事件时,状态机不仅要做到状态的跃迁,还要执行对应的动作,这也是大家设计状态的最初原因,也就是handle啦~

// 这里先空着,下面一起讲

3.4 跃迁(TRANSITION;)

可以说这是状态机的主体,上面是3.1,3.2和3.3还不能只是原料,这个模块三种原料组合起来

先看下状态机的结构体构成

typedef void (*FRAMEWORK_ACTION)(void);  //预留一个动作

typedef struct
{
    FRAMEWORK_STATE     current;  //当前状态
    FRAMEWORK_EVENT    event;      //触发事件
    FRAMEWORK_STATE     next;         //要切换的状态
    FRAMEWORK_ACTION  action;     //对应的动作
} FRAMEWORK_TRANSITION;

这样可以说,一条状态机器(运行规则)就构造完成了~

3.5 状态机(FSM)

一个状态机包含哪些东西

  1. 状态机的运行规则(上一个刚说)
  2. 状态机当前状态 (对应触发事件,哪些状态可以切入)
  3. 状态机的条数(遍历时控制遍历次数)
typedef struct
{
    FRAMEWORK_STATE         state;
    FRAMEWORK_TRANSITION    *transitions;
    uint32_t                    transition_count;
} FRAMEWORK_FSM;

4. (状态机)怎么做?(实战)

4.1 构造完整的跃迁

根据3.4的单条跃迁,我们设置一个完整的运行跃迁

static FRAMEWORK_TRANSITION m_fw_transition[] =
{
    /* BEGIN */
    {FW_STATE_BEGIN, EVENT_POWER_ON,  FW_STATE_INIT,      proc_init},
    {FW_STATE_BEGIN, EVENT_LOW_POWER, FW_STATE_POWER_OFF, proc_power_off},

    /* INIT */
    {FW_STATE_INIT, EVENT_RESTRICT,         FW_STATE_RESTRICT,  proc_restrict},
    {FW_STATE_INIT, EVENT_INIT_OK,          FW_STATE_JOIN,      proc_join},
    {FW_STATE_INIT, EVENT_BUTTON_LONG_PUSH, FW_STATE_POWER_OFF, proc_power_off},

    /* JOIN */
    {FW_STATE_JOIN, EVENT_JOIN_FAILED,      FW_STATE_JOIN,      proc_join_retry},
    {FW_STATE_JOIN, EVENT_JOIN_OK,          FW_STATE_WORKING,   proc_working},

    {FW_STATE_JOIN, EVENT_LOW_POWER,        FW_STATE_POWER_OFF, proc_power_off},
    {FW_STATE_JOIN, EVENT_BUTTON_PUSH,      FW_STATE_JOIN,      proc_indicate_join},
    {FW_STATE_JOIN, EVENT_BUTTON_LONG_PUSH, FW_STATE_POWER_OFF, proc_power_off},

    /* WORKING */
    {FW_STATE_WORKING, EVENT_CONNECTION_LOST,  FW_STATE_JOIN,      proc_join_retry},
    {FW_STATE_WORKING, EVENT_HB_ACK_OK,        FW_STATE_WORKING,   heartbeat_ack_ok},
    {FW_STATE_WORKING, EVENT_HB_ACK_LOST,      FW_STATE_WORKING,   heartbeat_ack_lost},

    {FW_STATE_WORKING, EVENT_LOW_POWER,        FW_STATE_POWER_OFF, proc_power_off},
    {FW_STATE_WORKING, EVENT_BUTTON_PUSH,      FW_STATE_WORKING,   proc_indicate_join},
    {FW_STATE_WORKING, EVENT_BUTTON_LONG_PUSH, FW_STATE_POWER_OFF, proc_power_off},

    /* POWER_OFF */
    {FW_STATE_POWER_OFF, EVENT_BUTTON_LONG_PUSH, FW_STATE_BEGIN, proc_power_on},

    /* RESTRICT */
    {FW_STATE_RESTRICT, EVENT_BUTTON_PUSH, FW_STATE_RESTRICT, proc_indicate_restrict},
    {FW_STATE_RESTRICT, EVENT_BUTTON_LONG_PUSH, FW_STATE_POWER_OFF, proc_power_off},
};

4.2 初始化函数

设置起始状态,运行哪个状态,状态机有多少条

//顺便在先创建一个状态机
static LST_FRAMEWORK_FSM m_fw_fsm;

void lst_framework_start(void)
{
    m_fw_fsm.state = LST_FW_STATE_BEGIN;
    m_fw_fsm.transitions = m_fw_transition;
    m_fw_fsm.transition_count = ARRAY_SIZE(m_fw_transition);
}

4.3 寻找状态机函数

static FRAMEWORK_TRANSITION *fw_find_transition(FRAMEWORK_FSM *fsm, FRAMEWORK_EVENT event)
{
    FRAMEWORK_TRANSITION *transform = NULL;
    uint32_t i;

    for (i = 0; i < fsm->transition_count; i++)
    {
        if ((fsm->transitions[i].current == fsm->state)
                && (fsm->transitions[i].event == event))
        {
            transform = &fsm->transitions[i];
        }
    }

    return transform;
}

4.4 事件发送函数

调用4.3状态机寻找函数

static void framework_event_send(FRAMEWORK_EVENT event)
{
    FRAMEWORK_TRANSITION *transform;

    transform = fw_find_transition(&m_fw_fsm, event);
    if (transform != NULL)
    {
        transform->action();
        m_fw_fsm.state = transform->next;
    }
}

只需要在业务代码判断条件适合时发送触发事件即可。

最后会贴上博主项目中的状态机源码


5. 扩展

当多层状态叠加需要考虑时(此处借用博主的另一个项目,汽车流媒体后视镜),如:


├── 关机                             
├── 开机
│   ├── 前进
│   │  ├──  左转
│   │  └──  右转
│   ├── 后退
│   │  ├──  左转
│   │  └──  右转
│   ├── 驻车
│   └── 空挡
└── 待机                

此时可以设计多层状态机

对应修改3.1

typedef struct
{
	/* data */
	SYSTEM_STATE 	system_state;
	MODE_STATE 	 	 mode_state;
	STEER_STATE     steer_state;
} FRAMEWORK_STATE;

单条跃迁不修改,因为上面状态已改为三层状态机

typedef struct
{
	FRAMEWORK_STATE current;  //要修改这个,见上一个代码块
	FRAMEWORK_EVENT event;
	FRAMEWORK_STATE next;
	FRAMEWORK_ACTION action;
} FRAMEWORK_TRANSITION;

对应修改4.2和4.3函数即可


源码:

已删除业务代码,只剩下框架,感谢理解~~

#include "nordic_common.h"
#include "nrf_log.h"

typedef enum
{
    FW_STATE_BEGIN = 0, // 起始状态
    FW_STATE_INIT,      // 初始化状态
    FW_STATE_JOIN,      // Join网络状态
    FW_STATE_WORKING,   // 工作状态
    FW_STATE_POWER_OFF, // 下电状态
    FW_STATE_RESTRICT,  // 限制状态
    FW_STATE_MAX,       // 用于STATE合法性判断
} FRAMEWORK_STATE;

typedef void (*FRAMEWORK_ACTION)(void);

typedef struct
{
    FRAMEWORK_STATE     current;
    FRAMEWORK_EVENT     event;
    FRAMEWORK_STATE     next;
    FRAMEWORK_ACTION    action;
} FRAMEWORK_TRANSITION;

typedef struct
{
    FRAMEWORK_STATE         state;
    FRAMEWORK_TRANSITION    *transitions;
    uint32_t                    transition_count;
} FRAMEWORK_FSM;

static void proc_init(void)
{

    return;
}
APP_TIMER_DEF(m_join_retry_timer);

static void join_retry_expire(void *p_context)
{
    return;
}

static void proc_join_retry(void)
{
    return;
}

static void proc_join(void)
{
    return;
}

static void proc_working(void)
{
    return;
}

static void proc_power_off(void)
{
    return;
}

static void proc_restrict(void)
{
    return;
}

static void proc_power_on(void)
{
    return;
}

static void proc_indicate_join(void)
{
    return;
}

static void proc_indicate_restrict(void)
{
    return;
}

static FRAMEWORK_FSM m_fw_fsm;

static FRAMEWORK_TRANSITION m_fw_transition[] =
{
    /* BEGIN */
    {FW_STATE_BEGIN, EVENT_POWER_ON,  FW_STATE_INIT,      proc_init},
    {FW_STATE_BEGIN, EVENT_LOW_POWER, FW_STATE_POWER_OFF, proc_power_off},

    /* INIT */
    {FW_STATE_INIT, EVENT_RESTRICT,         FW_STATE_RESTRICT,  proc_restrict},
    {FW_STATE_INIT, EVENT_INIT_OK,          FW_STATE_JOIN,      proc_join},
    {FW_STATE_INIT, EVENT_BUTTON_LONG_PUSH, FW_STATE_POWER_OFF, proc_power_off},

    /* JOIN */
    {FW_STATE_JOIN, EVENT_JOIN_FAILED,      FW_STATE_JOIN,      proc_join_retry},
    {FW_STATE_JOIN, EVENT_JOIN_OK,          FW_STATE_WORKING,   proc_working},

    {FW_STATE_JOIN, EVENT_LOW_POWER,        FW_STATE_POWER_OFF, proc_power_off},
    {FW_STATE_JOIN, EVENT_BUTTON_PUSH,      FW_STATE_JOIN,      proc_indicate_join},
    {FW_STATE_JOIN, EVENT_BUTTON_LONG_PUSH, FW_STATE_POWER_OFF, proc_power_off},

    /* WORKING */
    {FW_STATE_WORKING, EVENT_CONNECTION_LOST,  FW_STATE_JOIN,      proc_join_retry},
    {FW_STATE_WORKING, EVENT_HB_ACK_OK,        FW_STATE_WORKING,   heartbeat_ack_ok},
    {FW_STATE_WORKING, EVENT_HB_ACK_LOST,      FW_STATE_WORKING,   heartbeat_ack_lost},

    {FW_STATE_WORKING, EVENT_LOW_POWER,        FW_STATE_POWER_OFF, proc_power_off},
    {FW_STATE_WORKING, EVENT_BUTTON_PUSH,      FW_STATE_WORKING,   proc_indicate_join},
    {FW_STATE_WORKING, EVENT_BUTTON_LONG_PUSH, FW_STATE_POWER_OFF, proc_power_off},

    /* POWER_OFF */
    {FW_STATE_POWER_OFF, EVENT_BUTTON_LONG_PUSH, FW_STATE_BEGIN, proc_power_on},

    /* RESTRICT */
    {FW_STATE_RESTRICT, EVENT_BUTTON_PUSH, FW_STATE_RESTRICT, proc_indicate_restrict},
    {FW_STATE_RESTRICT, EVENT_BUTTON_LONG_PUSH, FW_STATE_POWER_OFF, proc_power_off},
};

static FRAMEWORK_TRANSITION *fw_find_transition(FRAMEWORK_FSM *fsm, FRAMEWORK_EVENT event)
{
    FRAMEWORK_TRANSITION *transform = NULL;
    uint32_t i;

    for (i = 0; i < fsm->transition_count; i++)
    {
        if ((fsm->transitions[i].current == fsm->state)
                && (fsm->transitions[i].event == event))
        {
            transform = &fsm->transitions[i];
        }
    }

    return transform;
}

static void fw_event_handler(FRAMEWORK_EVENT event)
{
    FRAMEWORK_TRANSITION *transform;

    transform = fw_find_transition(&m_fw_fsm, *event);
    if (transform != NULL)
    {
        transform->action();
        m_fw_fsm.state = transform->next;
    }
}

void framework_start(void)
{
    m_fw_fsm.state = FW_STATE_BEGIN;
    m_fw_fsm.transitions = m_fw_transition;
    m_fw_fsm.transition_count = ARRAY_SIZE(m_fw_transition);
}
  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

八个大柚子

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值