【开发语言】层次状态机(HSM)介绍

层次状态机(Hierarchical State Machine, HSM),从基本原理、结构设计、实现方法以及如何结合 Qt 进行具体实现等方面进行分析。

1. 层次状态机的基本原理

层次状态机是一种用于管理复杂系统行为的状态机模型,它通过将状态组织成层次结构来简化设计和维护。这种结构使得复杂的逻辑可以分解为更小、更易于管理的部分。

关键概念:
  • 状态(State): 系统在某一时刻所处的条件或模式。
  • 事件(Event): 触发状态转换的信息或信号。
  • 转换(Transition): 从一个状态到另一个状态的迁移过程。
  • 动作(Action): 在进入、离开状态或进行转换时执行的操作。
  • 父状态和子状态:
    • 父状态(Superstate): 包含多个子状态的状态。
    • 子状态(Substate): 属于某个父状态的更具体的状态。
特点:
  • 嵌套结构: 状态可以嵌套在其他状态中,形成层次结构。
  • 继承行为: 子状态可以继承父状态的行为和动作。
  • 事件委托: 事件可以从子状态传递到父状态进行处理。
  • 简化设计: 将复杂的状态逻辑分解为更小的、可管理的部分。

2. 层次状态机的设计

在设计层次状态机时,需要仔细规划状态之间的关系以及如何组织这些状态。以下是一些设计原则和步骤:

设计步骤:
  1. 识别顶级状态:

    • 确定系统的基本操作模式或主要功能。
    • 例如,在一个电梯系统中,顶级状态可能包括“待命”、“运行”和“维护”。
  2. 定义子状态:

    • 对于每个顶级状态,进一步分解为更具体的状态。
    • 例如,“运行”状态可以包含“上升”、“下降”和“停止”等子状态。
  3. 确定事件和转换:

    • 定义可能触发状态转换的事件。
    • 确定每个状态在接收到特定事件时应执行的操作以及如何进行转换。
    • 例如,“上升”状态在接收到“到达楼层”事件时,可以转换到“停止”状态。
  4. 实现继承和委托:

    • 设计父状态的行为,并让子状态继承这些行为。
    • 当子状态无法处理某个事件时,将该事件传递给其父状态进行处理。
  5. 编写动作函数:

    • 实现每个状态的进入(Entry)、离开(Exit)操作以及转换期间的动作(Action)。
    • 例如,在“上升”状态下进入时启动电机,在离开时停止电机。
  6. 定义初始状态和历史状态:

    • 指定每个复合状态的初始子状态。
    • 使用历史状态来记住上次活动的子状态,以便在返回该状态时恢复到之前的状态。
示例:

假设我们设计一个简单的电视遥控器状态机,包含以下状态:

  • 待命(Standby)
  • 运行(Running)
    • 频道选择(Channel Selection)
      • 浏览模式(Browse Mode)
      • 锁定模式(Lock Mode)
    • 音量控制(Volume Control)

事件包括:

  • POWER
  • CHANNEL_UP, CHANNEL_DOWN
  • VOLUME_UP, VOLUME_DOWN
  • MODE_SWITCH
状态图示例:
          +-------------------+|     Standby       |+--------+----------+|POWER|+----v-----+|  Running   |+--+-+------+| |CHANNEL_UP|VOLUME_DOWN/      |      \/       v       \+------------+     +------------+| Browse Mode|     |Volume Ctrl|+------------+     +------------+

3. 层次状态机的实现

在实际编程中,层次状态机可以通过多种方式实现。以下是一个使用 C++ 和 Qt 的具体示例。

使用结构体定义状态和事件

首先,我们定义状态、事件及其处理函数的类型:

#include <QObject>
#include <QVector>
#include <QDebug>namespace HSMUtilityDef {typedef uint32_t HSM_EVENT;// 定义常见事件const uint32_t MAX_DEPTH = 5;const HSM_EVENT HSME_NULL = 0;const HSM_EVENT HSME_START = 1;const HSM_EVENT HSME_INIT = static_cast<HSM_EVENT>(-3);const HSM_EVENT HSME_ENTRY = static_cast<HSM_EVENT>(-2);const HSM_EVENT HSME_EXIT = static_cast<HSM_EVENT>(-1);// 自定义事件const HSM_EVENT POWER = 100;const HSM_EVENT CHANNEL_UP = 101;const HSM_EVENT CHANNEL_DOWN = 102;const HSM_EVENT VOLUME_UP = 103;const HSM_EVENT VOLUME_DOWN = 104;const HSM_EVENT MODE_SWITCH = 105;
}
定义状态基类

创建一个抽象基类 HSMState,包含处理事件的虚函数:

class HSMState : public QObject {Q_OBJECTpublic:explicit HSMState(HSMState *parent = nullptr) : QObject(parent), m_parent(parent) {}virtual ~HSMState() {}// 处理事件的主要接口virtual HSMUtilityDef::HSM_EVENT handleEvent(HSMUtilityDef::HSM_EVENT event, void* param = nullptr) {switch (event) {case HSMUtilityDef::HSME_ENTRY:onEntry(param);break;case HSMUtilityDef::HSME_EXIT:onExit(param);break;default:return unhandledEvent(event, param);}return HSMUtilityDef::HSME_NULL;}protected:// 进入状态时执行的动作virtual void onEntry(void* param) {}// 离开状态时执行的动作virtual void onExit(void* param) {}// 处理未定义事件的方法virtual HSMUtilityDef::HSM_EVENT unhandledEvent(HSMUtilityDef::HSM_EVENT event, void* param) {qDebug() << "Unhandled event" << event;return event;}// 获取父状态HSMState* parent() const { return m_parent; }private:HSMState *m_parent;
};
定义具体状态类

创建具体的派生状态类,实现特定的逻辑:

// 待命状态(Standby)
class StandbyState : public HSMState {Q_OBJECTpublic:explicit StandbyState(HSMState* parent = nullptr) : HSMState(parent) {}protected:void onEntry(void* param) override {qDebug() << "Entering Standby State";}void onExit(void* param) override {qDebug() << "Exiting Standby State";}HSMUtilityDef::HSM_EVENT unhandledEvent(HSMUtilityDef::HSM_EVENT event, void* param) override {if (event == HSMUtilityDef::POWER) {return static_cast<HSMUtilityDef::HSM_EVENT>(HSMUtilityDef::HSME_INIT);}return HSMState::unhandledEvent(event, param);}
};// 运行状态(Running)
class RunningState : public HSMState {Q_OBJECTpublic:explicit RunningState(HSMState* parent = nullptr) : HSMState(parent) {}protected:void onEntry(void* param) override {qDebug() << "Entering Running State";}void onExit(void* param) override {qDebug() << "Exiting Running State";}HSMUtilityDef::HSM_EVENT unhandledEvent(HSMUtilityDef::HSM_EVENT event, void* param) override {if (event == HSMUtilityDef::POWER) {return static_cast<HSMUtilityDef::HSM_EVENT>(HSMUtilityDef::HSME_EXIT);}return HSMState::unhandledEvent(event, param);}
};// 频道选择状态(Channel Selection)
class ChannelSelectionState : public HSMState {Q_OBJECTpublic:explicit ChannelSelectionState(HSMState* parent = nullptr) : HSMState(parent), m_currentMode(BROWSE_MODE) {}protected:void onEntry(void* param) override {qDebug() << "Entering Channel Selection State";}void onExit(void* param) override {qDebug() << "Exiting Channel Selection State";}HSMUtilityDef::HSM_EVENT unhandledEvent(HSMUtilityDef::HSM_EVENT event, void* param) override {switch (event) {case HSMUtilityDef::CHANNEL_UP:channelUp();break;case HSMUtilityDef::CHANNEL_DOWN:channelDown();break;case HSMUtilityDef::MODE_SWITCH:modeSwitch();break;default:return HSMState::unhandledEvent(event, param);}return HSMUtilityDef::HSME_NULL;}private:enum Mode {BROWSE_MODE,LOCK_MODE};Mode m_currentMode;void channelUp() {if (m_currentMode == BROWSE_MODE) {qDebug() << "Channel Up in Browse Mode";} else if (m_currentMode == LOCK_MODE) {qDebug() << "Channel Up in Lock Mode";}}void channelDown() {if (m_currentMode == BROWSE_MODE) {qDebug() << "Channel Down in Browse Mode";} else if (m_currentMode == LOCK_MODE) {qDebug() << "Channel Down in Lock Mode";}}void modeSwitch() {if (m_currentMode == BROWSE_MODE) {m_currentMode = LOCK_MODE;qDebug() << "Switched to Lock Mode";} else {m_currentMode = BROWSE_MODE;qDebug() << "Switched to Browse Mode";}}
};// 音量控制状态(Volume Control)
class VolumeControlState : public HSMState {Q_OBJECTpublic:explicit VolumeControlState(HSMState* parent = nullptr) : HSMState(parent) {}protected:void onEntry(void* param) override {qDebug() << "Entering Volume Control State";}void onExit(void* param) override {qDebug() << "Exiting Volume Control State";}HSMUtilityDef::HSM_EVENT unhandledEvent(HSMUtilityDef::HSM_EVENT event, void* param) override {switch (event) {case HSMUtilityDef::VOLUME_UP:volumeUp();break;case HSMUtilityDef::VOLUME_DOWN:volumeDown();break;default:return HSMState::unhandledEvent(event, param);}return HSMUtilityDef::HSME_NULL;}private:void volumeUp() {qDebug() << "Volume Up";}void volumeDown() {qDebug() << "Volume Down";}
};
定义层次状态机类

创建一个管理状态转换的主类 HSM

class HSM : public QObject {Q_OBJECTpublic:explicit HSM(QObject* parent = nullptr) : QObject(parent), m_currentState(nullptr) {}void start(HSMState* initialState) {if (m_currentState == nullptr) {initialize(initialState);}}void processEvent(HSMUtilityDef::HSM_EVENT event, void* param = nullptr) {if (m_currentState != nullptr) {HSMState* nextState = m_currentState;HSMUtilityDef::HSM_EVENT nextEvent = event;// 处理事件,直到返回 HSME_NULLwhile (nextEvent != HSMUtilityDef::HSME_NULL) {nextState = processSingleEvent(nextState, nextEvent, param);nextEvent = nextState->handleEvent(event, param);}}}private:HSMState* m_currentState;void initialize(HSMState* initialState) {if (initialState != nullptr) {// 初始化状态栈QVector<HSMState*> stateStack;while (initialState != nullptr) {stateStack.append(initialState);initialState = initialState->parent();}// 从顶层状态开始初始化for (int i = stateStack.size() - 1; i >= 0; --i) {HSMState* currentState = stateStack[i];currentState->handleEvent(HSMUtilityDef::HSME_ENTRY, nullptr);}m_currentState = stateStack.last();}}HSMState* processSingleEvent(HSMState* currentState, HSMUtilityDef::HSM_EVENT event, void* param) {switch (event) {case HSMUtilityDef::HSME_INIT:return initializeChildStates(currentState);case HSMUtilityDef::HSME_ENTRY:currentState->onEntry(param);break;case HSMUtilityDef::HSME_EXIT:currentState->onExit(param);return processSingleEvent(currentState->parent(), HSMUtilityDef::HSME_EXIT, param);}return currentState;}HSMState* initializeChildStates(HSMState* parentState) {if (parentState == nullptr) {return nullptr;}QVector<HSMState*> childStates = findInitialStates(parentState);for (HSMState* state : childStates) {processSingleEvent(state, HSMUtilityDef::HSME_ENTRY, nullptr);}return childStates.last();}QVector<HSMState*> findInitialStates(HSMState* parentState) const {// 在实际应用中,可能需要更复杂的逻辑来确定初始子状态// 这里简单地假设每个父状态只有一个直接的初始子状态QVector<HSMState*> children;QObjectList childObjects = parentState->children();for (QObject* obj : childObjects) {HSMState* state = qobject_cast<HSMState*>(obj);if (state != nullptr) {children.append(state);}}// 返回第一个子状态作为初始状态return children;}
};
构建和运行状态机

main 函数中构建并运行层次状态机:

#include <QCoreApplication>
#include <QDebug>int main(int argc, char *argv[]) {QCoreApplication a(argc, argv);// 创建状态对象StandbyState* standby = new StandbyState();RunningState* running = new RunningState(standby);ChannelSelectionState* channelSel = new ChannelSelectionState(running);VolumeControlState* volumeCtrl = new VolumeControlState(running);// 构建层次结构running->setParent(standby);channelSel->setParent(running);volumeCtrl->setParent(running);// 创建状态机并启动HSM hsm;hsm.start(standby);// 处理事件hsm.processEvent(HSMUtilityDef::POWER);          // 切换到运行模式hsm.processEvent(HSMUtilityDef::CHANNEL_UP);       // 选择频道向上hsm.processEvent(HSMUtilityDef::MODE_SWITCH);      // 切换到锁定模式hsm.processEvent(HSMUtilityDef::VOLUME_UP);        // 增加音量hsm.processEvent(HSMUtilityDef::POWER);          // 关闭电视return a.exec();
}

4. 使用 Qt 的信号和槽机制增强状态机

Qt 提供了强大的信号和槽机制,可以用来进一步简化状态机的设计和实现。以下是如何将 Qt 的信号和槽与层次状态机结合使用的方法。

修改 HSMState 类以支持信号和槽

HSMState 中添加信号来通知状态转换或动作执行:

#include <QObject>
#include <QVector>
#include <QDebug>namespace HSMUtilityDef {typedef uint32_t HSM_EVENT;// 定义常见事件const uint32_t MAX_DEPTH = 5;const HSM_EVENT HSME_NULL = 0;const HSM_EVENT HSME_START = 1;const HSM_EVENT HSME_INIT = static_cast<HSM_EVENT>(-3);const HSM_EVENT HSME_ENTRY = static_cast<HSM_EVENT>(-2);const HSM_EVENT HSME_EXIT = static_cast<HSM_EVENT>(-1);// 自定义事件const HSM_EVENT POWER = 100;const HSM_EVENT CHANNEL_UP = 101;const HSM_EVENT CHANNEL_DOWN = 102;const HSM_EVENT VOLUME_UP = 103;const HSM_EVENT VOLUME_DOWN = 104;const HSM_EVENT MODE_SWITCH = 105;
}class HSMState : public QObject {Q_OBJECTpublic:explicit HSMState(HSMState *parent = nullptr) : QObject(parent), m_parent(parent) {}virtual ~HSMState() {}// 处理事件的主要接口virtual HSMUtilityDef::HSM_EVENT handleEvent(HSMUtilityDef::HSM_EVENT event, void* param = nullptr) {switch (event) {case HSMUtilityDef::HSME_ENTRY:onEntry(param);break;case HSMUtilityDef::HSME_EXIT:onExit(param);break;default:return unhandledEvent(event, param);}return HSMUtilityDef::HSME_NULL;}signals:// 信号用于通知状态转换或动作执行void stateEntered(HSMState* state);void stateExited(HSMState* state);protected:// 进入状态时执行的动作virtual void onEntry(void* param) {emit stateEntered(this);}// 离开状态时执行的动作virtual void onExit(void* param) {emit stateExited(this);}// 处理未定义事件的方法virtual HSMUtilityDef::HSM_EVENT unhandledEvent(HSMUtilityDef::HSM_EVENT event, void* param) {qDebug() << "Unhandled event" << event;return event;}// 获取父状态HSMState* parent() const { return m_parent; }private:HSMState *m_parent;
};

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/62185.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

MYSQL PARTITIONING分区操作和性能测试

PARTITION OR NOT PARTITION IN MYSQl Bill Karwin says “In most circumstances, you’re better off using indexes instead of partitioning as your main method of query optimization.” According to RICK JAMES: “It is so tempting to believe that PARTITIONing wi…

深入解析 Loss 减少方式:mean和sum的区别及其在大语言模型中的应用 (中英双语)

深入解析 Loss 减少方式&#xff1a;mean 和 sum 的区别及其在大语言模型中的应用 在训练大语言模型&#xff08;Large Language Models, LLM&#xff09;时&#xff0c;损失函数&#xff08;Loss Function&#xff09;的处理方式对模型的性能和优化过程有显著影响。本文以 re…

基于 AutoFlow 快速搭建基于 TiDB 向量搜索的本地知识库问答机器人

导读 本文将详细介绍如何通过 PingCAP 开源项目 AutoFlow 实现快速搭建基于 TiDB 的本地知识库问答机器人。如果提前准备好 Docker、TiDB 环境&#xff0c;整个搭建过程估计在 10 分钟左右即可完成&#xff0c;无须开发任何代码。 文中使用一篇 TiDB 文档作为本地数据源作为示…

生信技能63 - 构建gnomAD变异位点的SQLite查询数据库

将数据量巨大的gnomAD数据库,通过SQLite数据库寻找gnomAD中存在的各种变异注释信息(如等位基因计数,深度,次要等位基因频率等),查询300.000个变量的查询需要大约40秒,通过染色体编号+位置+REF+ALT即可进行快速查询。 1. gnomAD变异注释VCF文件字段 gnomAD VCF各版本包…

【前端】将vue的方法挂载到window上供全局使用,也方便跟原生js做交互

【前端】将vue的方法挂载到window上供全局使用&#xff0c;也方便跟原生js做交互 <template><div><el-button click"start">调用方法</el-button></div> </template> <script> // import { JScallbackProc } from ./JScal…

基于XML的AOP开发

AOP 为 Aspect Oriented Programming 的缩写&#xff0c;意思为面向切面编程。 AOP相关术语&#xff1a; 目标对象(Target)&#xff1a; 你要去代理的对象&#xff0c;可以理解为之前很单纯的那个对象。 代理对象(Proxy)&#xff1a; 你把你那个单纯的对象给我&#xff0c…

记录blender学习过程中遇到的问题

物体发射的方向不对 被发射物体&#xff08;例如一棵树&#xff09;n键看旋转归0 切换正视图 将被发射物体的局部坐标的Z轴 指向 全局方向的X轴时 并且把粒子系统设置的物体旋转勾选上 方向就对了 做倒角发现有问题 检查缩放应用、面朝向、有没有重合点&#xff08;融合点&am…

Ubuntu系统中Redis的安装步骤及服务配置

目录 内容概括 系统环境 安装方式 1、apt包管理器安装 &#xff08;1&#xff09;安装redis服务 &#xff08;2&#xff09;安装客户端&#xff08;进入命令行操作使用&#xff0c;包含redis-cli&#xff09; &#xff08;3&#xff09;安装检验 &#xff08;4&#xf…

半导体设备中的微型导轨应如何选择合适的润滑油?

微型导轨的润滑对于保证其高精度和高稳定性至关重要&#xff0c;尤其是在半导体设备中&#xff0c;微型导轨的润滑油选择需要考虑多个因素&#xff0c;以确保设备的最佳性能和寿命。以下是一些关键点&#xff1a; 1、黏度&#xff1a;润滑油的黏度是影响其流动性和润滑效果的重…

RocketMq详解:六、RocketMq的负载均衡机制

上一章&#xff1a;《SpringBootAop实现RocketMq的幂等》 文章目录 1.背景1.1 什么是负载均衡1.2 负载均衡的意义 2.RocketMQ消息消费2.1 消息的流转过程2.2 Consumer消费消息的流程 3.RocketMq的负载均衡策略3.1 Broker负载均衡3.2 Producer发送消息负载均衡3.3 消费端的负载均…

yocto的xxx.bb文件在什么时候会拷贝文件到build目录

在 Yocto 中&#xff0c;.bb 文件用于描述如何构建和安装一个软件包&#xff0c;而文件在构建过程中的拷贝操作通常会在某些特定的步骤中进行。具体来说&#xff0c;文件会在以下几个阶段被拷贝到 build 目录&#xff08;或者更准确地说&#xff0c;拷贝到目标目录 ${D}&#x…

主打极致性价比,AMD RX 8600/8800显卡定了

*以下内容仅为网络爆料及传闻&#xff0c;一切以官方消息为准。 这谁能想到&#xff0c;率先掏出下一代桌面独立显卡的不是老大哥 NVIDIA&#xff0c;也不是 AMD&#xff0c;反而是三家中存在感最弱的 Intel&#xff01; 就在 12 月 3 日&#xff0c;Intel 正式发布了自家第二…

数组哪些方法会触发Vue监听,哪些不会触发监听

发现宝藏 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。【宝藏入口】。 在 Vue 中&#xff0c;数组的变化是通过 响应式 系统来监听的。Vue 使用 getter 和 setter 来追踪数组的变化&#xff0c;并在数…

npm, yarn, pnpm之间的区别

前言 在现代化的开发中&#xff0c;一个人可能同时开发多个项目&#xff0c;安装的项目越来越多&#xff0c;所随之安装的依赖包也越来越臃肿&#xff0c;而且有时候所安装的速度也很慢&#xff0c;甚至会安装失败。 因此我们就需要去了解一下&#xff0c;我们的包管理器&#…

工业检测基础-工业相机选型及应用场景

以下是一些常见的工业检测相机种类、检测原理、应用场景及选型依据&#xff1a; 2D相机 检测原理&#xff1a;基于二维图像捕获&#xff0c;通过分析图像的明暗、纹理、颜色等信息来检测物体的特征和缺陷.应用场景&#xff1a;广泛应用于平面工件的外观检测&#xff0c;如检测…

C语言连接数据库

文章目录 一、初始化数据库二、创建数据库连接三、执行增删改查语句1、增删改2、查 四、执行增删改查语句 接下来我简单的介绍一下怎么用C语言连接数据库。 初始化数据库创建数据库连接执行增删改查语句关闭数据库连接 一、初始化数据库 // 数据库初始化 MYSQL mysql; MYSQL* r…

优化LabVIEW数据运算效率的方法

在LabVIEW中进行大量数据运算时&#xff0c;提升计算效率并减少时间占用是开发过程中常遇到的挑战。为此&#xff0c;可以从多个角度着手优化&#xff0c;包括合理选择数据结构与算法、并行处理、多线程技术、硬件加速、内存管理和界面优化等。通过采用这些策略&#xff0c;可以…

开源模型应用落地-安全合规篇-用户输入价值观判断(四)

一、前言 在深度合规功能中,对用户输入内容的价值观判断具有重要意义。这一功能不仅仅是对信息合法性和合规性的简单审核,更是对信息背后隐含的伦理道德和社会责任的深刻洞察。通过对价值观的判断,系统能够识别可能引发不当影响或冲突的内容,从而为用户提供更安全、更和谐的…

计算机的错误计算(一百七十六)

摘要 利用某一大语言模型计算 的值&#xff0c;输出为 0 . 例1. 在某一大语言模型下&#xff0c;计算 的值。其中sin中值取弧度。结果保留16位有效数字。 直接贴图吧&#xff1a; 点评&#xff1a; &#xff08;1&#xff09;以上为一个大模型给的答案。从其回答可知&…

数据结构与算法——1204—递归分治法

1、斐波那契数列优化 使用滚动变量&#xff0c;保存当前计算结果和前两项值 (1)RAB (2)更新计算对象&#xff0c;AB&#xff0c;BR #include<iostream> using namespace std;int fun(int n) {if (n 0)return 0;if (n 1 || n 2)return 1;int num11;int num21;int su…