Linux 状态机
首先感谢阅读,作者是在工作中学习与积累,每一个笔记都是心得和积累,希望可以和大家一起交流学习。
学习咱们先定好计划,需要了解的都一样,有:
- (状态机)是什么?
- (状态机)为什么?
- (状态机)有什么?
- (状态机)怎么做?(实战)
- (状态机)扩展
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
{
}
}
else if (weather == RAIN)
{
if (event == rain_stop)
{
close_umbrella();
weather = SUN;
}
else
{
}
}
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,
FW_STATE_WORKING,
FW_STATE_POWER_OFF,
FW_STATE_RESTRICT,
FW_STATE_MAX,
} 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,
EVENT_JOIN_FAILED,
EVENT_HB_ACK_OK,
EVENT_HB_ACK_LOST,
EVENT_CONNECTION_LOST,
EVENT_MAX,
} 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)
一个状态机包含哪些东西
- 状态机的运行规则(上一个刚说)
- 状态机当前状态 (对应触发事件,哪些状态可以切入)
- 状态机的条数(遍历时控制遍历次数)
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[] =
{
{FW_STATE_BEGIN, EVENT_POWER_ON, FW_STATE_INIT, proc_init},
{FW_STATE_BEGIN, EVENT_LOW_POWER, FW_STATE_POWER_OFF, proc_power_off},
{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},
{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},
{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},
{FW_STATE_POWER_OFF, EVENT_BUTTON_LONG_PUSH, FW_STATE_BEGIN, proc_power_on},
{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
{
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,
FW_STATE_WORKING,
FW_STATE_POWER_OFF,
FW_STATE_RESTRICT,
FW_STATE_MAX,
} 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[] =
{
{FW_STATE_BEGIN, EVENT_POWER_ON, FW_STATE_INIT, proc_init},
{FW_STATE_BEGIN, EVENT_LOW_POWER, FW_STATE_POWER_OFF, proc_power_off},
{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},
{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},
{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},
{FW_STATE_POWER_OFF, EVENT_BUTTON_LONG_PUSH, FW_STATE_BEGIN, proc_power_on},
{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);
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)