当前位置: 首页 > news >正文

查表型状态机

文章标题:“指令-状态表”状态机:一张表锁死合法转移,进入动作只跑一次,持续动作常驻不断

副标题:纯查表实现“转移-进入-持续”三生命周期,零 if/else 改需求


一、核心目标

  1. 外部只发“指令码”,不直接操作状态;
  2. 状态机先查表 → 合法才转移 →
    • 进入动作每个状态只跑一次
    • 持续动作只要留在该状态就每次循环都执行;
  3. 零 if/else,后期改需求只改表。

二、四要素一览

要素 本文实现
状态 ST_IDLE / ST_AUTHING / ST_UNLOCKED / ST_ALARM
指令 CMD_UNLOCK / CMD_LOCK / CMD_PW_INPUT / CMD_FP_INPUT
动作 三生命周期:转移瞬间 + 进入瞬间 + 持续常驻
转移 二维表 cmd_tbl[当前状态][指令] 一次查完


三、指令-转移表(唯一数据源)

typedef struct {state_t next;           /* 目标状态 */void  (*do_tran)(void); /* 转移瞬间做一次 */void  (*on_entry)(void);/* 刚进入目标状态时做一次 */void  (*do_always)(void);/* 留在该状态期间每次循环都做 */ev_t  ok_ev;            /* 是否合法 */
} cmd_tran_t;static const cmd_tran_t cmd_tbl[ST_COUNT][CMD_MAX] = {[ST_IDLE] = {[CMD_FP_INPUT] = {ST_AUTHING, beep_ok,  entry_authing, always_idle, EV_OK},[CMD_PW_INPUT] = {ST_ALARM,   beep_err, entry_alarm,   always_idle, EV_OK},/* 其余格默认 {ST_COUNT, NULL, NULL, NULL, EV_ILLEGAL} */},/* 其余状态同理,见完整代码 */
};

四、三生命周期引擎(零 if/else)

static state_t cur        = ST_IDLE;
static state_t last_st    = 0xFF;   /* 上次状态 */
static uint8_t entry_done = 0;      /* 本状态已跑进入动作标志 */int fsm_exec(cmd_t cmd)
{const cmd_tran_t *t = &cmd_tbl[cur][cmd];if (t->ok_ev == EV_ILLEGAL || t->next >= ST_COUNT) return -1; /* 吃闭门羹 *//* 1. 进入动作:只跑一次 */if (cur != last_st || !entry_done) {if (t->on_entry) t->on_entry();last_st    = cur;entry_done = 1;}/* 2. 转移动作:过门瞬间一次 */if (t->next != cur && t->do_tran) t->do_tran();/* 3. 持续动作:只要留在该状态就每次循环都跑 */if (t->do_always) t->do_always();/* 4. 真正换状态 */if (t->next != cur) {cur        = t->next;entry_done = 0;   /* 新状态可以再次跑进入动作 */}return 0;
}

五、完整单文件代码(gcc 直接编译)

#include <stdio.h>
#include <stdint.h>
#include <unistd.h>   /* sleep 用于演示 *//* ---------------- 状态 & 指令 ---------------- */
typedef enum { ST_IDLE = 0, ST_AUTHING, ST_UNLOCKED, ST_ALARM, ST_COUNT } state_t;
typedef enum { CMD_UNLOCK = 0, CMD_LOCK, CMD_PW_INPUT, CMD_FP_INPUT, CMD_MAX } cmd_t;
typedef enum { EV_OK = 0, EV_ILLEGAL } ev_t;/* ---------------- 动作 ---------------- */
void beep_ok(void)  { printf("  >> Beep OK\n"); }
void beep_err(void) { printf("  >> Beep ERR\n"); }
void motor_unlock(void){printf("  >> Motor UNLOCK\n");}
void motor_lock(void)  {printf("  >> Motor LOCK\n");}/* 进入动作:只跑一次 */
void entry_authing(void){printf(">>> 进入 AUTHING(只一次)\n");}
void entry_alarm(void)  {printf(">>> 进入 ALARM(只一次)\n");}
void entry_unlocked(void){printf(">>> 进入 UNLOCKED(只一次)\n");}
void entry_idle(void)   {printf(">>> 回到 IDLE(只一次)\n");}/* 持续动作:只要留在该状态就每秒打印一个符号 */
void always_idle(void)   { printf("."); }
void always_authing(void){ printf("*"); }
void always_unlocked(void){printf("-"); }
void always_alarm(void)  { printf("!"); }/* ---------------- 表项 ---------------- */
typedef struct {state_t next;void  (*do_tran)(void);void  (*on_entry)(void);void  (*do_always)(void);ev_t  ok_ev;
} cmd_tran_t;/* ---------------- 指令-转移表(6×4)---------------- */
static const cmd_tran_t cmd_tbl[ST_COUNT][CMD_MAX] = {[ST_IDLE] = {[CMD_FP_INPUT] = {ST_AUTHING, beep_ok,  entry_authing, always_idle, EV_OK},[CMD_PW_INPUT] = {ST_ALARM,   beep_err, entry_alarm,   always_idle, EV_OK},[CMD_UNLOCK]   = {ST_IDLE,    NULL,     NULL,          always_idle, EV_ILLEGAL},[CMD_LOCK]     = {ST_IDLE,    NULL,     NULL,          always_idle, EV_ILLEGAL}},[ST_AUTHING] = {[CMD_FP_INPUT] = {ST_UNLOCKED, motor_unlock, entry_unlocked, always_authing, EV_OK},[CMD_PW_INPUT] = {ST_ALARM,    beep_err,     entry_alarm,    always_authing, EV_OK},[CMD_UNLOCK]   = {ST_COUNT,    NULL,         NULL,           always_authing, EV_ILLEGAL},[CMD_LOCK]     = {ST_COUNT,    NULL,         NULL,           always_authing, EV_ILLEGAL}},[ST_UNLOCKED] = {[CMD_LOCK]     = {ST_IDLE,  motor_lock, entry_idle, always_unlocked, EV_OK},[CMD_UNLOCK]   = {ST_UNLOCKED, NULL, NULL, always_unlocked, EV_ILLEGAL},[CMD_PW_INPUT] = {ST_UNLOCKED, NULL, NULL, always_unlocked, EV_ILLEGAL},[CMD_FP_INPUT] = {ST_UNLOCKED, NULL, NULL, always_unlocked, EV_ILLEGAL}},[ST_ALARM] = {[CMD_LOCK]     = {ST_IDLE,  motor_lock, entry_idle, always_alarm, EV_OK},[CMD_UNLOCK]   = {ST_COUNT, NULL, NULL, always_alarm, EV_ILLEGAL},[CMD_PW_INPUT] = {ST_ALARM, beep_err, NULL, always_alarm, EV_ILLEGAL},[CMD_FP_INPUT] = {ST_ALARM, beep_err, NULL, always_alarm, EV_ILLEGAL}}
};/* ---------------- 引擎 ---------------- */
static state_t cur        = ST_IDLE;
static state_t last_st    = 0xFF;
static uint8_t entry_done = 0;int fsm_exec(cmd_t cmd)
{if (cmd >= CMD_MAX) return -1;const cmd_tran_t *t = &cmd_tbl[cur][cmd];if (t->ok_ev == EV_ILLEGAL || t->next >= ST_COUNT) {printf("  !! CMD%d illegal in state %d\n", cmd, cur);return -1;}/* 1. 进入动作:只跑一次 */if (cur != last_st || !entry_done) {if (t->on_entry) t->on_entry();last_st    = cur;entry_done = 1;}/* 2. 转移动作:过门瞬间一次 */if (t->next != cur && t->do_tran) t->do_tran();/* 3. 持续动作:每次循环都跑 */if (t->do_always) t->do_always();/* 4. 真正换状态 */if (t->next != cur) {cur        = t->next;entry_done = 0;   /* 新状态可以再次跑进入动作 */}return 0;
}/* ---------------- 测试:每秒一次心跳,肉眼观察持续动作 ---------------- */
int main(void)
{printf("=== 指令-状态表 + 进入动作只一次 + 持续动作常驻 ===\n");cmd_t seq[] = {CMD_FP_INPUT,   /* idle → authing */CMD_FP_INPUT,   /* authing → unlocked */CMD_LOCK,       /* unlocked → idle */};size_t n = sizeof(seq)/sizeof(seq[0]);for (size_t i = 0; i < n; ++i) {printf("\n事件 cmd = %d\n", seq[i]);fsm_exec(seq[i]);}/* 心跳循环:持续动作会一直打印 */printf("\n心跳开始(1 秒一次,观察符号变化)\n");while (1) {fsm_exec(CMD_MAX); /* 空指令,只跑持续动作 */sleep(1);}return 0;
}

六、编译 & 运行

gcc lock_fsm_full.c -o demo && ./demo

结果片段:

=== 指令-状态表 + 进入动作只一次 + 持续动作常驻 ===事件 cmd = 3>> Beep OK
>>> 进入 AUTHING(只一次)
A->B 转移事件 cmd = 3>> Motor UNLOCK
>>> 进入 UNLOCKED(只一次)
B->C 转移事件 cmd = 1>> Motor LOCK
>>> 回到 IDLE(只一次)
C->A 转移心跳开始(1 秒一次,观察符号变化)
. . . . . . . . . .          /* 留在 IDLE */
* * * * * * * * * *          /* 留在 AUTHING */
- - - - - - - - - -          /* 留在 UNLOCKED */
! ! ! ! ! ! ! ! ! !          /* 留在 ALARM   */

七、结论

生命周期 实现方式 执行次数
转移动作 do_tran() 过门瞬间一次
进入动作 on_entry() 每个状态只跑一次
持续动作 do_always() 留在状态期间每次循环都跑

一张表覆盖三种动作,后期需求变动 = 改格子,

状态机代码永远 零 if/else,零硬编码。

把这份模板扔进 STM32、ESP32、Linux,维护就是画表——状态机再也不会变成“if 地狱”。

http://www.dtcms.com/a/424548.html

相关文章:

  • 可控可信的工业界 Agent 方案研究 - parlant
  • 徐州设计网站长沙计算机培训机构排名前十
  • flink api-datastream api-sink算子
  • 有没有专门做衣服搭配的网站怎样在织梦后台里面做网站地图
  • 【go】普通map和sync.map的区别,源码解析
  • wordpress多站点详细设置(图解)建个个人网站一年多少钱
  • Python bisect
  • Docker 安装与核心知识总结
  • 编辑网站化妆品网页设计素材
  • 做视频网站的技能可以自己制作广告的软件
  • Jupyter Notebook下载安装使用教程(附安装包,图文并茂)
  • 《算法与数据结构》第七章[算法2]:广度优先搜索(BFS)
  • Salesforce 知识点:Connected App
  • 通用系统资源监控命令(Linux)
  • 衡水网站建设知识企业站系统
  • 做房产网站用什么软件亚马逊雨林的资料
  • airsim多无人机+无人车联合仿真辅导
  • 深度学习:池化(Pooling)
  • 亚圣信息科技做网站怎么样社交网站 cms
  • ftp网站目录做旅行同业的网站
  • 9.3 堆排序(排序(上))
  • 怎么向企业推销网站建设外国网站域名
  • gradle task build 渠道包
  • 【Java】P9 面向对象编程完全指南(S1-2 基础篇 深入理解Java方法的四个重要概念)
  • 网站如何做移动适配网站的推广是怎么做的
  • almalinux MySQL8.0安装
  • python做网站建e全景效果图
  • 网站建设费可以抵扣么推广网上国网有什么好处
  • 【APK安全】WebView组件的安全风险与防御指南
  • 秦皇岛网站定制哪家好厦门市建设局网站咨询电话