使用状态机来实现状态切换,备忘。
#include <stdio.h>
#include <conio.h>
#pragma warning(disable:4996)
/*
四要素
state 状态
event 事件
action 动作
transition 变换
*/
typedef enum
{
sta_go = 0,
sta_down,
sta_right,
sta_light,
sta_stop
}state;
state now_state = sta_stop;//初始化目前状态为前进
state last_state = sta_stop;//初始化上一次状态为停止
typedef enum
{
evt_rl =0,
evt_yl,
evt_gl,
evt_people_pass,
evt_turn_right,
evt_turn_light
}eventid;
typedef void (*CallBack)(void);
typedef struct {
state curState;//当前状态
eventid eventId;//事件ID
state nextState;//下个状态
CallBack action;//回调函数,事件发生后,调用对应的回调函数
}StateTransform;
void callback_go(void)
{
printf("向前走\n");
}
void callback_stop(void)
{
printf("停止\n");
}
void callback_right(void)
{
printf("右转\n");
}
void callback_light(void)
{
printf("左转\n");
}
void callback_down(void)
{
printf("倒退特殊回调触发\n");
}
StateTransform stateTran_go[]{//顺序不能变
{sta_go,evt_rl, sta_stop,callback_stop},
{sta_go,evt_yl, sta_stop,callback_stop},
{sta_go,evt_gl, sta_stop,callback_stop},
{sta_go,evt_people_pass, sta_stop,callback_stop},
{sta_go,evt_turn_right, sta_right,callback_right},
{sta_go,evt_turn_light, sta_light,callback_light},
};
StateTransform stateTran_down[]{
{sta_down,evt_rl, sta_stop,callback_down},
{sta_down,evt_yl, sta_light,callback_down},
{sta_down,evt_gl, sta_light,callback_down},
{sta_down,evt_people_pass, sta_stop,callback_stop},
{sta_down,evt_turn_right, sta_right,callback_down},
{sta_down,evt_turn_light, sta_light,callback_down},
};
StateTransform stateTran_stop[]{
{sta_stop,evt_rl, sta_stop,callback_stop},
{sta_stop,evt_yl, sta_stop,callback_stop},
{sta_stop,evt_gl, sta_go,callback_go},
{sta_stop,evt_people_pass, sta_stop,callback_stop},
{sta_stop,evt_turn_right, sta_right,callback_right},
{sta_stop,evt_turn_light, sta_light,callback_light},
};
StateTransform stateTran_right[]{
{sta_right,evt_rl, sta_stop,callback_stop},
{sta_right,evt_yl, sta_right,callback_right},
{sta_right,evt_gl, sta_right,callback_right},
{sta_right,evt_people_pass, sta_stop,callback_stop},
{sta_right,evt_turn_right, sta_right,callback_right},
{sta_right,evt_turn_light, sta_light,callback_light},
};
StateTransform stateTran_light[]{
{sta_light,evt_rl, sta_stop,callback_go},
{sta_light,evt_yl, sta_light,callback_light},
{sta_light,evt_gl, sta_light,callback_light},
{sta_light,evt_people_pass, sta_stop,callback_stop},
{sta_light,evt_turn_right, sta_right,callback_right},
{sta_light,evt_turn_light, sta_light,callback_light},
};
void do_action(StateTransform* statTran)
{
printf("last_state = % d\n now_state = % d\n", last_state, now_state);
if (NULL == statTran)
{
perror("statTran is NULL\n");
return;
}
last_state= statTran->curState;//更新状态
now_state = statTran->nextState;
if (now_state != last_state)
{
if (statTran->action != NULL)
{
statTran->action();
}
}
else
{
printf("事件没有改变\n");
}
}
void event_action(unsigned int event)
{
switch (now_state)
{
case sta_go:
do_action(&stateTran_go[event]);
break;
case sta_down:
do_action(&stateTran_down[event]);
break;
case sta_right:
do_action(&stateTran_right[event]);
break;
case sta_light:
do_action(&stateTran_light[event]);
break;
case sta_stop:
do_action(&stateTran_stop[event]);
break;
default:
printf("事件不合法\n");
break;
}
}
void main()
{
int key;
printf("开始运行.....\n");
while (1)
{
key = getchar();
if (key=='r')
{
event_action(evt_rl);
}
if (key == 'y')
{
event_action(evt_yl);
}
if (key == 'g')
{
event_action(evt_gl);
}
if (key == 'p')
{
event_action(evt_people_pass);
}
if (key == 'R')
{
event_action(evt_turn_right);
}
if (key == 'L')
{
event_action(evt_turn_light);
}
}
}
运行结果
在除正在转弯的情况下,其余所有情况遇到黄灯都停止。同时所有状态遇到行人都停止。
举例不是很恰当,重要的是思想。
2023-7-31 新增
最近发送上边的状态机使用起来过于复杂,所以参考了一下另一位大佬的代码
所有代码如下
#include <stdio.h>
#include <conio.h> //获取键盘建值状态
enum SEvent {//所有事件的集合
event1 = 0x31,
event2,
event3,
event4,
event5,
event6,
event7,
event8
};
enum CState {//所有状态的集合
state1 = 1,
state2,
state3,
state4,
state5
};
typedef struct {
int Event;//触发的事件
int Curstate;//当前状态
void (*CallBack)();//触发之后的回调函数
int Nextstate;//下一个状态
}State_table;//状态表
typedef struct {
int Curstate;//当前状态
State_table* Stable;//状态表
int Stable_size;//状态表的项数
}FSM;//实际操作的状态机对象
/*状态机注册,给它一个状态表
pFsm 新建的状态机
pTable 传入的状态机对应表
state 初始默认状态
*/
void FSM_Regist(FSM* pFsm, State_table *pTable, CState state,int TableSize)
{
pFsm->Stable = pTable;//传入所给的状态表
pFsm->Stable_size= TableSize;//所给状态表的大小
pFsm->Curstate = state;//当前的默认状态
}
void FSM_EventHandle(FSM *pFsm, SEvent event) {//注意这里要传入的pFsm必须是一个指针,不然改不了状态
void (*ActionCallBack)();
ActionCallBack = 0;//默认置空
for (int i = 0; i < pFsm->Stable_size; i++) {
if (pFsm->Curstate == pFsm->Stable[i].Curstate && event == pFsm->Stable[i].Event) {
ActionCallBack = pFsm->Stable[i].CallBack;//找出要执行的函数
pFsm->Curstate = pFsm->Stable[i].Nextstate;//更改状态
}
}
if (ActionCallBack != 0) {
ActionCallBack();//动作执行
}
else
return;
}
void thing1() {
printf("这里是执行函数1 \r\n");
}
void thing2() {
printf("这里是执行函数2 \r\n");
}
void thing3() {
printf("这里是执行函数3 \r\n");
}
void thing4() {
printf("这里是执行函数4 \r\n");
}
void thing5() {
printf("这里是执行函数5 \r\n");
}
State_table STranTable[] = {
{ event1, state1, thing1, state2 },
{ event2, state2, thing2, state3 },
{ event3, state3, thing3, state4 },
{ event4, state4, thing4, state5 },
{ event5, state5, thing5, state5 },
{ event1, state5, thing1, state1 },
};
int main(void) {
FSM Mfsm;
FSM_Regist(&Mfsm, STranTable, state1,sizeof(STranTable)/sizeof(State_table));
while (1) {
while (_kbhit()) {
SEvent key = (SEvent)_getch();
FSM_EventHandle(&Mfsm, key);
}
}
}
使用方法:
1 定义所有事件的集合
enum SEvent {//所有事件的集合
event1 = 0x31,
event2,
event3,
event4,
event5,
event6,
event7,
event8
};
2 定义所有状态的集合
enum CState {//所有状态的集合
state1 = 1,
state2,
state3,
state4,
state5
};
3 初始化所有的回调函数和状态迁移表
void thing1() {
printf("这里是执行函数1 \r\n");
}
void thing2() {
printf("这里是执行函数2 \r\n");
}
void thing3() {
printf("这里是执行函数3 \r\n");
}
void thing4() {
printf("这里是执行函数4 \r\n");
}
void thing5() {
printf("这里是执行函数5 \r\n");
}
State_table STranTable[] = {//分别对应 触发的事件 当前状态 要触发的回调函数 下一个状态
{ event1, state1, thing1, state2 },
{ event2, state2, thing2, state3 },
{ event3, state3, thing3, state4 },
{ event4, state4, thing4, state5 },
{ event5, state5, thing5, state5 },
{ event1, state5, thing1, state1 },
};
4 开始使用
FSM Mfsm;//初始化一个状态机对象
FSM_Regist(&Mfsm, STranTable, state1,sizeof(STranTable)/sizeof(State_table));//注册状态机对象的状态迁移表
FSM_EventHandle(&Mfsm, key);//调用此函数和实现一次状态切换