【状态机】基于QP框架实现的简单状态机说明
本介绍以RTOS进行对比进行,大量内容使用AI生成特性QP框架RTOS基本单元活动对象(Active Objects)线程(Threads)单元结构状态机 + 事件队列 + 私有数据执行上下文 + 栈空间设计哲学事件驱动计算模型过程式编程模型问题解决视角状态与行为建模任务与资源管理QP调度框架RTOS调度框架QP以事件驱动状态机为核心,专注于状态行为建模;
引言
简中网上对于QP的说明大部分挺停留在各种概念说明和概念对比,令人一头雾水,看了好像懂了,但是又不知道怎么做。因此本文不会从盘古开天辟地说起,也不会介绍状态机、RTOS、事件驱动啥的各种概念,仅仅介绍入门级别QP框架结合QM图形化工具的使用。
实现项目:密码门禁系统
1、用户输入(模拟按键):
- 0-9 : 数字输入
- *: 确认/开门 (模拟确认键)
- # : 清除输入/复位 (模拟清除键)
2、系统显示(控制台输出):
- 显示当前输入的密码(例如 **** 对应4位输入)
- 显示系统状态(Ready, Entering, Correct, Wrong, Locked)
3、核心逻辑:
- 预设一个简单密码(如 1234)。
- 用户输入数字:显示 Entering 状态,并显示当前输入密码
- 用户按 #:清除所有已输入数字,显示 Ready。
- 用户按 *:检查输入密码。
- 密码正确:显示 Correct 几秒,然后恢复 Ready。
- 密码错误:显示 Wrong 几秒,错误计数+1。
- 连续错误达到3次:系统 Locked(倒计时5秒),期间不接受任何输入。倒计时结束恢复 Ready,错误计数清零。
(其实这个项目用switch更简单)
分析项目
将项目抽象成外部控制输入(事件),项目状态即可,这部分需确保定义能够涵盖需求。
该项目仅有一个活动对象:DoorLock_AO,负责读取按键、处理逻辑、管理状态、控制显示
事件定义:
- 数字键按下
- 确认键按下
- 清除键按下
- 定时器超时
状态定义:
- 初始状态(Ready)
- 输入状态(Entering)
- 校验状态(Verifying)
- 正确状态(Correct)
- 错误状态(Wrong)
- 锁机状态(Locked)
QM工具使用

model就是project,包含活动对象、状态机、事件、层次等图形化设计元素。
package本质是一个分组,用于大型模型的分解,具有命名空间、复用性的特点,一个包可以包含子包、活动对象、状态机、事件、数据类型、端口
class遵循UML类的定义,包含属性、操作、状态机

选择为层次状态机

添加状态机
打开状态绘制界面,画图(图中代码不重要,后期自己改动很大)
添加文件夹,这个文件夹用于存放生成的代码
其中需要手动实现构造函数,实现week定义的QF框架函数。
其中$declare${package1::DoorLock}和$define${package1::DoorLock}表示需要自动生成状态机的声明和定义。
代码
编写完成后生成代码。
完整代码(通过AI生成了注释)如下:
//$file${.::doorlock.c} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
//
// Model: homework1.qm
// File: ${.::doorlock.c}
//
// This code has been generated by QM 7.0.1 <www.state-machine.com/qm>.
// DO NOT EDIT THIS FILE MANUALLY. All your changes will be lost.
//
// Copyright (c) 2005 Quantum Leaps, LLC. All rights reserved.
//
// ____________________________________
// / /
// / GGGGGGG PPPPPPPP LL /
// / GG GG PP PP LL /
// / GG PP PP LL /
// / GG GGGGG PPPPPPPP LL /
// / GG GG PP LL /
// / GGGGGGG PP LLLLLLL /
// /___________________________________/
//
// SPDX-License-Identifier: GPL-3.0-or-later
//
// This generated code is open-source software licensed under the GNU
// General Public License (GPL) as published by the Free Software Foundation
// (see <https://www.gnu.org/licenses>).
//
// NOTE:
// The GPL does NOT permit the incorporation of this code into proprietary
// programs. Please contact Quantum Leaps for commercial licensing options,
// which expressly supersede the GPL and are designed explicitly for
// closed-source distribution.
//
// Quantum Leaps contact information:
// <www.state-machine.com/licensing>
// <info@state-machine.com>
//
//$endhead${.::doorlock.c} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
// 定义系统时钟节拍分辨率(每秒10个节拍,即100ms一个节拍)
#define QF_TICKS_PER_SEC 10
// 包含QP框架核心头文件
#include "qpc.h"
// 标准库头文件
#include <stdlib.h>
#include <stdio.h>
#include "safe_std.h" // 安全标准I/O函数
#include <time.h> // 时间函数
#include <conio.h> // 控制台输入函数(_kbhit, _getch)
#include <windows.h> // Windows API(Sleep函数)
/*
* 信号定义 - QP框架中的事件标识符
*
* QP框架使用信号(Signals)来标识不同类型的事件。
* 所有用户自定义信号必须从Q_USER_SIG开始定义,以避免与系统保留信号冲突。
*/
enum DoorLockSignals {
TIMEOUT_SIG = Q_USER_SIG, // 定时器超时信号(必须从Q_USER_SIG开始)
DIGIT_KEY_SIG, // 数字键按下事件
ENTER_KEY_SIG, // 确认键(*)按下事件
CLEAR_KEY_SIG, // 清除键(#)按下事件
MAX_PUB_SIG // 最后一个信号(用于边界检查)
};
//$declare${package1::DoorLock} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
/*
* DoorLock 结构体 - 状态机对象
*
* 在QP框架中,每个状态机都是一个对象,包含:
* 1. 继承自QHsm基类(层次状态机)
* 2. 私有数据成员(状态机内部使用的变量)
* 3. 公有数据成员(可被外部访问的变量)
*/
typedef struct {
// protected: (QP框架要求)
QHsm super; // 继承自QHsm基类(层次状态机)
// private: (状态机内部使用)
clock_t timer_start; // 定时器开始时间(clock_t类型)
uint32_t timer_duration; // 定时器持续时间(毫秒)
uint32_t input; // 当前输入的数字序列
// public: (可被外部访问)
uint8_t err_count; // 错误计数(连续错误次数)
uint32_t password; // 系统预设密码
} DoorLock;
/*
* 状态处理函数声明 - QP框架状态机核心
*
* 在QP框架中,每个状态对应一个处理函数,函数签名如下:
* static QState 函数名(DoorLock *const me, QEvt const *const e);
*
* 参数:
* me - 指向状态机实例的指针
* e - 指向事件的指针
*
* 返回值:
* QState - 状态处理结果(使用QP提供的宏)
*/
static QState DoorLock_initial(DoorLock *const me, void const *const par);
static QState DoorLock_Ready(DoorLock *const me, QEvt const *const e);
static QState DoorLock_Entering(DoorLock *const me, QEvt const *const e);
static QState DoorLock_Verifying(DoorLock *const me, QEvt const *const e);
static QState DoorLock_Correct(DoorLock *const me, QEvt const *const e);
static QState DoorLock_Wrong(DoorLock *const me, QEvt const *const e);
static QState DoorLock_Locked(DoorLock *const me, QEvt const *const e);
//$enddecl${package1::DoorLock} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/*
* DoorLock 构造函数
*
* 在QP框架中,构造函数用于:
* 1. 初始化状态机基类(QHsm)
* 2. 设置状态机的初始状态
* 3. 初始化状态机的数据成员
*/
void DoorLock_ctor(DoorLock *const me) {
/*
* QHsm_ctor - QP框架的层次状态机构造函数
*
* 参数:
* &me->super - 指向状态机基类的指针
* Q_STATE_CAST(&DoorLock_initial) - 初始状态函数(使用Q_STATE_CAST宏转换)
*/
QHsm_ctor(&me->super, Q_STATE_CAST(&DoorLock_initial));
// 初始化定时器字段
me->timer_start = 0;
me->timer_duration = 0;
// 初始化其他字段
me->input = 0;
me->err_count = 0;
me->password = 1234; // 设置默认密码
}
/*
* QF_onStartup - QP框架启动回调函数
*
* 当使用完整QP框架时,此函数在QF_run()之前调用。
* 在本简化实现中,我们不需要它,但保留它以满足编译要求。
*/
void QF_onStartup(void) {}
/*
* QF_onCleanup - QP框架清理回调函数
*
* 当使用完整QP框架时,此函数在QF_stop()之后调用。
* 在本简化实现中,我们不需要它,但保留它以满足编译要求。
*/
void QF_onCleanup(void) {}
/*
* QF_onClockTick - QP框架时钟节拍回调函数
*
* 当使用完整QP框架时,此函数由系统定时器中断调用。
* 在本简化实现中,我们不需要它,但保留它以满足编译要求。
*/
void QF_onClockTick(void) {}
/*
* Q_onError - QP框架错误处理函数
*
* 当QP框架检测到错误时调用此函数。
* 参数:
* module - 发生错误的模块名
* id - 错误ID
*/
Q_NORETURN Q_onError(char const *const module, int_t const id) {
FPRINTF_S(stderr, "ERROR in %s:%d", module, id);
exit(-1);
}
/*
* 主函数 - 程序入口点
*/
int main() {
DoorLock doorLock; // 创建状态机实例
/*
* QF_init - QP框架初始化函数
*
* 在完整QP框架中,此函数初始化框架内部数据结构。
* 在本简化实现中,我们调用它以满足某些QP组件的需求。
*/
QF_init();
// 调用构造函数初始化状态机
DoorLock_ctor(&doorLock);
/*
* QASM_INIT - QP框架状态机初始化宏
*
* 功能:初始化状态机并执行初始转换
* 参数:
* &doorLock.super - 指向状态机基类的指针
* (QEvt *)0 - 初始事件(通常为NULL)
* 0 - 状态机ID(通常为0)
*/
QASM_INIT(&doorLock.super, (QEvt *)0, 0);
// 主事件循环
while (1) {
// 1. 检查并处理定时器超时
if (doorLock.timer_duration > 0) {
clock_t current = clock();
// 计算已过时间(毫秒)
clock_t elapsed_ms = (current - doorLock.timer_start) * 1000 / CLOCKS_PER_SEC;
// 检查是否超时
if (elapsed_ms >= doorLock.timer_duration) {
/*
* 创建超时事件
*
* QEvt是QP框架的事件基类:
* struct QEvt {
* QSignal sig; // 事件信号
* // ... 其他框架内部成员
* };
*/
QEvt e = {TIMEOUT_SIG};
/*
* QASM_DISPATCH - QP框架事件分发宏
*
* 功能:将事件分发给状态机处理
* 参数:
* &doorLock.super - 指向状态机基类的指针
* &e - 事件指针
* 0 - 状态机ID(通常为0)
*/
QASM_DISPATCH(&doorLock.super, &e, 0);
// 重置定时器
doorLock.timer_duration = 0;
}
}
// 2. 非阻塞处理用户输入
if (_kbhit()) {
char input = _getch(); // 获取按键
QSignal sig = 0; // QP信号类型(本质是整数)
// 根据按键设置信号
switch (input) {
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
// 处理数字输入
doorLock.input = doorLock.input * 10 + (input - '0');
printf("Input: %lu\n", doorLock.input);
sig = DIGIT_KEY_SIG;
break;
case '*':
sig = ENTER_KEY_SIG;
break;
case '#':
sig = CLEAR_KEY_SIG;
break;
}
// 如果有有效信号,创建并分发事件
if (sig != 0) {
QEvt e = {sig};
QASM_DISPATCH(&doorLock.super, &e, 0);
}
}
// 3. 短暂休眠减少CPU占用
Sleep(10); // 休眠10ms
}
return 0;
}
//$skip${QP_VERSION} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
// 检查QP框架版本要求
#if (QP_VERSION < 730U) || (QP_VERSION != ((QP_RELEASE^4294967295U)%0x2710U))
#error qpc version 7.3.0 or higher required
#endif
//$endskip${QP_VERSION} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
//$define${package1::DoorLock} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
//${package1::DoorLock} ......................................................
//${package1::DoorLock::SM} ..................................................
/*
* DoorLock_initial - 状态机初始状态
*
* 在QP框架中,这是状态机的入口点。
* 当状态机初始化时,第一个调用的状态函数。
*/
static QState DoorLock_initial(DoorLock *const me, void const *const par) {
// 初始化错误计数
me->err_count = 0;
/*
* Q_TRAN - 状态转换宏
*
* 功能:请求转换到新状态
* 参数:目标状态的处理函数
* 返回值:Q_RET_TRAN(告诉框架需要状态转换)
*/
return Q_TRAN(&DoorLock_Ready);
}
//${package1::DoorLock::SM::Ready} ...........................................
/*
* Ready状态 - 系统就绪状态
*/
static QState DoorLock_Ready(DoorLock *const me, QEvt const *const e) {
QState status_; // 状态处理结果
switch (e->sig) {
// Q_ENTRY_SIG - 进入状态事件(由框架自动发送)
case Q_ENTRY_SIG: {
printf("ready\n");
// 重置输入和密码
me->input = 0;
me->password = 1234;
/*
* Q_HANDLED - 事件已处理宏
*
* 返回值:Q_RET_HANDLED(告诉框架事件已处理)
*/
status_ = Q_HANDLED();
break;
}
// DIGIT_KEY_SIG - 数字键按下事件
case DIGIT_KEY_SIG: {
// 转换到输入状态
status_ = Q_TRAN(&DoorLock_Entering);
break;
}
default: {
/*
* Q_SUPER - 传递给父状态宏
*
* 功能:将事件传递给父状态处理
* 参数:父状态的处理函数(QHsm_top是顶级状态)
* 返回值:Q_RET_SUPER(告诉框架将事件传递给父状态)
*/
status_ = Q_SUPER(&QHsm_top);
break;
}
}
return status_;
}
//${package1::DoorLock::SM::Entering} ........................................
/*
* Entering状态 - 密码输入状态
*/
static QState DoorLock_Entering(DoorLock *const me, QEvt const *const e) {
QState status_;
switch (e->sig) {
case Q_ENTRY_SIG: {
printf("entering\n");
status_ = Q_HANDLED();
break;
}
case DIGIT_KEY_SIG: {
// 数字键按下时保持在当前状态
status_ = Q_TRAN(&DoorLock_Entering);
break;
}
case CLEAR_KEY_SIG: {
// 清除键按下时返回就绪状态
status_ = Q_TRAN(&DoorLock_Ready);
break;
}
case ENTER_KEY_SIG: {
// 确认键按下时转换到验证状态
status_ = Q_TRAN(&DoorLock_Verifying);
break;
}
default: {
status_ = Q_SUPER(&QHsm_top);
break;
}
}
return status_;
}
//${package1::DoorLock::SM::Verifying} .......................................
/*
* Verifying状态 - 密码验证状态
*/
static QState DoorLock_Verifying(DoorLock *const me, QEvt const *const e) {
QState status_;
switch (e->sig) {
case Q_ENTRY_SIG: {
printf("verifying\n");
status_ = Q_HANDLED();
break;
}
case Q_INIT_SIG: {
/*
* Q_INIT_SIG - 状态初始化事件
*
* 在QP框架中,进入状态后自动发送此事件。
* 用于执行状态初始化逻辑。
*/
if (me->password == me->input) {
printf("-> c\n");
// 密码正确,转换到正确状态
status_ = Q_TRAN(&DoorLock_Correct);
} else {
printf("-> n \n");
// 密码错误,转换到错误状态
status_ = Q_TRAN(&DoorLock_Wrong);
}
break;
}
default: {
status_ = Q_SUPER(&QHsm_top);
break;
}
}
return status_;
}
//${package1::DoorLock::SM::Correct} .........................................
/*
* Correct状态 - 密码正确状态
*/
static QState DoorLock_Correct(DoorLock *const me, QEvt const *const e) {
QState status_;
switch (e->sig) {
case Q_ENTRY_SIG: {
printf("correct\n");
// 重置错误计数
me->err_count = 0;
// 启动2秒定时器
me->timer_start = clock();
me->timer_duration = 2000;
status_ = Q_HANDLED();
break;
}
case TIMEOUT_SIG: {
// 超时后返回就绪状态
status_ = Q_TRAN(&DoorLock_Ready);
break;
}
default: {
status_ = Q_SUPER(&QHsm_top);
break;
}
}
return status_;
}
//${package1::DoorLock::SM::Wrong} ...........................................
/*
* Wrong状态 - 密码错误状态
*/
static QState DoorLock_Wrong(DoorLock *const me, QEvt const *const e) {
QState status_;
switch (e->sig) {
case Q_ENTRY_SIG: {
printf("wrong\n");
me->err_count++;
me->timer_start = clock();
me->timer_duration = 2000;
status_ = Q_HANDLED();
break;
}
case Q_INIT_SIG: {
// 在初始化事件中检查错误计数
if (me->err_count >= 3) {
me->err_count = 0;
status_ = Q_TRAN(&DoorLock_Locked);
} else {
status_ = Q_HANDLED();
}
break;
}
case TIMEOUT_SIG: {
status_ = Q_TRAN(&DoorLock_Ready);
break;
}
default: {
status_ = Q_SUPER(&QHsm_top);
break;
}
}
return status_;
}
//${package1::DoorLock::SM::Locked} ..........................................
/*
* Locked状态 - 系统锁定状态
*/
static QState DoorLock_Locked(DoorLock *const me, QEvt const *const e) {
QState status_;
switch (e->sig) {
case Q_ENTRY_SIG: {
printf("Locked\n");
// 启动5秒定时器
me->timer_start = clock();
me->timer_duration = 5000;
status_ = Q_HANDLED();
break;
}
case TIMEOUT_SIG: {
// 超时后返回就绪状态
status_ = Q_TRAN(&DoorLock_Ready);
break;
}
default: {
status_ = Q_SUPER(&QHsm_top);
break;
}
}
return status_;
}
//$enddef${package1::DoorLock} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
QP框架介绍
本介绍以RTOS进行对比进行,大量内容使用AI生成
| 特性 | QP框架 | RTOS |
|---|---|---|
| 基本单元 | 活动对象(Active Objects) | 线程(Threads) |
| 单元结构 | 状态机 + 事件队列 + 私有数据 | 执行上下文 + 栈空间 |
| 设计哲学 | 事件驱动计算模型 | 过程式编程模型 |
| 问题解决视角 | 状态与行为建模 | 任务与资源管理 |
QP调度框架
RTOS调度框架
QP以事件驱动状态机为核心,专注于状态行为建模;RTOS以任务调度为核心,专注于执行流管理。理解这些差异有助于根据应用需求选择最佳方案,或在复杂系统中实现两者的协同应用。
QP状态机高级技术
- 层次状态嵌套
- 历史状态
- 正交区域
- 事件转换
- 事件发布\订阅
- 超时状态转换
- 状态模式扩展
- 延迟信号
- 状态机代理模式
- 状态机组合模式
- 。。。
在复杂逻辑场景的时候,通过更加高级的功能,实现花里胡哨的状态机功能,涵盖所有业务场景,并且运行起来较RTOS更安全,而且调试复杂度更低。
更多推荐



所有评论(0)