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

开源TinyFSM状态机适用于嵌入式工业平台吗?

文章目录

    • 引言
    • 基于传统 C++ 实现的状态机
    • TinyFSM 实现的对比
    • 现代 C++ 实现的状态机
    • 性能对比
      • TinyFSM 性能测试
      • 传统 C++ 性能测试
      • 现代 C++ 性能测试
    • 工业Misra C++编程标准
    • TinyFSM 的优缺点分析
    • 结论

引言

TinyFSM是一个为C++设计的轻量级有限状态机开源库库。
在嵌入式系统开发中,TinyFSM等状态机适用于控制系统和通信协议等场景,然而,开发者也需考虑该库的性能并考虑是否遵循工业C++标准。
传统 C++ 实现不仅能很容易的满足工业标准的要求,还能提供更高的性能和更低的内存开销。
现代 C++ 实现虽然引入了许多新特性,可以简化代码结构,但在性能上可能不如传统 C++ 实现高效。
反而TinyFSM本身很多地方设计不满足工业C++标准。

基于传统 C++ 实现的状态机

在嵌入式系统中,传统 C++ 实现的状态机通过显式管理状态变量和使用 switch 语句处理事件,可以有效控制内存和运行时开销,同时确保代码符合 MISRA C++ 规范。以下是一个简单的门状态机示例:

#include <iostream>enum class DoorState { Closed, Open, Locked };class DoorStateMachine {
public:DoorStateMachine() : state(DoorState::Closed) {}void open() {switch (state) {case DoorState::Closed:std::cout << "Door is opened\n";state = DoorState::Open;break;case DoorState::Open:std::cout << "Door is already open\n";break;case DoorState::Locked:std::cout << "Cannot open, door is locked\n";break;}}void close() {switch (state) {case DoorState::Closed:std::cout << "Door is already closed\n";break;case DoorState::Open:std::cout << "Door is closed\n";state = DoorState::Closed;break;case DoorState::Locked:std::cout << "Cannot close, door is locked\n";break;}}void lock() {switch (state) {case DoorState::Closed:std::cout << "Door is locked\n";state = DoorState::Locked;break;case DoorState::Open:std::cout << "Cannot lock, door is open\n";break;case DoorState::Locked:std::cout << "Door is already locked\n";break;}}private:DoorState state;
};int main() {DoorStateMachine door;door.open();door.close();door.lock();door.open();return 0;
}

TinyFSM 实现的对比

TinyFSM 是一个轻量级状态机库,通过继承 tinyfsm::Fsm 和定义状态类,能够直观地定义状态和事件处理函数。以下是使用 TinyFSM 实现的门状态机代码:

#include <tinyfsm.hpp>
#include <iostream>struct OpenEvent : tinyfsm::Event {};
struct CloseEvent : tinyfsm::Event {};
struct LockEvent : tinyfsm::Event {};class DoorState : public tinyfsm::Fsm<DoorState> {
public:virtual void react(OpenEvent const &) { std::cout << "Invalid transition\n"; }virtual void react(CloseEvent const &) { std::cout << "Invalid transition\n"; }virtual void react(LockEvent const &) { std::cout << "Invalid transition\n"; }virtual void entry() {}virtual void exit() {}
};class Closed : public DoorState {
public:void react(OpenEvent const &) override {std::cout << "Door is opened\n";transit<Open>();}void react(LockEvent const &) override {std::cout << "Door is locked\n";transit<Locked>();}
};class Open : public DoorState {
public:void react(CloseEvent const &) override {std::cout << "Door is closed\n";transit<Closed>();}
};class Locked : public DoorState {
public:void react(OpenEvent const &) override {std::cout << "Cannot open, door is locked\n";}
};FSM_INITIAL_STATE(DoorState, Closed)int main() {DoorState::start();DoorState::dispatch(OpenEvent());DoorState::dispatch(CloseEvent());DoorState::dispatch(LockEvent());DoorState::dispatch(OpenEvent());return 0;
}

现代 C++ 实现的状态机

现代 C++(如 C++14 和 C++17)引入了许多新特性,使得开发高效、可维护的代码更加容易。在状态机实现中,现代 C++ 特性如 std::functionstd::unordered_map 可以显著简化代码结构。以下是一个基于现代 C++ 实现的状态机示例:

#include <iostream>
#include <functional>
#include <unordered_map>enum class DoorState { Closed, Open, Locked };
enum class DoorEvent { OpenEvent, CloseEvent, LockEvent };class DoorStateMachine {
public:DoorStateMachine() : state(DoorState::Closed) {stateHandlers[DoorState::Closed][DoorEvent::OpenEvent] = [this]() { handleOpenFromClosed(); };stateHandlers[DoorState::Closed][DoorEvent::LockEvent] = [this]() { handleLockFromClosed(); };stateHandlers[DoorState::Open][DoorEvent::CloseEvent] = [this]() { handleCloseFromOpen(); };stateHandlers[DoorState::Locked][DoorEvent::OpenEvent] = [this]() { handleOpenFromLocked(); };}void handleEvent(DoorEvent event) {auto eventHandler = stateHandlers[state].find(event);if (eventHandler != stateHandlers[state].end()) {eventHandler->second();} else {std::cout << "Invalid event\n";}}private:void handleOpenFromClosed() {std::cout << "Door is opened\n";state = DoorState::Open;}void handleLockFromClosed() {std::cout << "Door is locked\n";state = DoorState::Locked;}void handleCloseFromOpen() {std::cout << "Door is closed\n";state = DoorState::Closed;}void handleOpenFromLocked() {std::cout << "Cannot open, door is locked\n";}DoorState state;std::unordered_map<DoorState, std::unordered_map<DoorEvent, std::function<void()>>> stateHandlers;
};int main() {DoorStateMachine door;door.handleEvent(DoorEvent::OpenEvent);door.handleEvent(DoorEvent::CloseEvent);door.handleEvent(DoorEvent::LockEvent);door.handleEvent(DoorEvent::OpenEvent);return 0;
}

性能对比

  • 以下在树莓派5上测试,基本信息如下

    CPU:2.4GHz 四核 64位 Arm Cortex-A76
    内存:32位 LPDDR4X SDRAM,4267MT/s
    
  • 使用了benchmark多次深度压测。

  • 平均数据分别为:

    TinyFSM 传统C++ 现代C++
    5.91 ns 1.25 ns 413 ns

TinyFSM 性能测试

#include <benchmark/benchmark.h>
#include <tinyfsm.hpp>// 事件定义
struct OpenEvent : tinyfsm::Event {};
struct CloseEvent : tinyfsm::Event {};
struct LockEvent : tinyfsm::Event {};// 状态机基类
class DoorState : public tinyfsm::Fsm<DoorState> {public:virtual void react(OpenEvent const &) {}virtual void react(CloseEvent const &) {}virtual void react(LockEvent const &) {}virtual void entry() {}virtual void exit() {}
};// 定义具体状态类
class Closed : public DoorState {public:void react(OpenEvent const &) override { transit<Open>(); }void react(LockEvent const &) override { transit<Locked>(); }
};class Open : public DoorState {public:void react(CloseEvent const &) override { transit<Closed>(); }
};class Locked : public DoorState {public:void react(OpenEvent const &) override {}
};FSM_INITIAL_STATE(DoorState, Closed)static void BM_TinyFSM(benchmark::State &state) {for (auto _ : state) {DoorState::start();DoorState::dispatch(OpenEvent());DoorState::dispatch(CloseEvent());DoorState::dispatch(LockEvent());}
}BENCHMARK(BM_TinyFSM);
BENCHMARK_MAIN();

测试结果

Benchmark           Time             CPU   Iterations
-----------------------------------------------------
BM_TinyFSM       5.91 ns         5.91 ns    116692670

传统 C++ 性能测试

#include <benchmark/benchmark.h>enum class DoorState { Closed, Open, Locked };
enum class DoorEvent { OpenEvent, CloseEvent, LockEvent };class DoorStateMachine {public:DoorStateMachine() : state(DoorState::Closed) {}void handleEvent(DoorEvent event) {switch (state) {case DoorState::Closed:if (event == DoorEvent::OpenEvent) {state = DoorState::Open;} else if (event == DoorEvent::LockEvent) {state = DoorState::Locked;}break;case DoorState::Open:if (event == DoorEvent::CloseEvent) {state = DoorState::Closed;}break;case DoorState::Locked:break;}}private:DoorState state;
};static void BM_TraditionalCPPStateMachine(benchmark::State& state) {DoorStateMachine door;for (auto _ : state) {benchmark::DoNotOptimize(door);door.handleEvent(DoorEvent::OpenEvent);door.handleEvent(DoorEvent::CloseEvent);door.handleEvent(DoorEvent::LockEvent);}
}BENCHMARK(BM_TraditionalCPPStateMachine);BENCHMARK_MAIN();

测试结果:

Benchmark                         Time             CPU   Iterations
-------------------------------------------------------------------
BM_TraditionalCPPStateMachine       1.25 ns         1.25 ns    558856294

现代 C++ 性能测试

#include <benchmark/benchmark.h>
#include <functional>
#include <unordered_map>enum class DoorState { Closed, Open, Locked };
enum class DoorEvent { OpenEvent, CloseEvent, LockEvent };class DoorStateMachine {public:DoorStateMachine() : state(DoorState::Closed) {stateHandlers[DoorState::Closed][DoorEvent::OpenEvent] = [this]() { state = DoorState::Open; };stateHandlers[DoorState::Closed][DoorEvent::LockEvent] = [this]() { state = DoorState::Locked; };stateHandlers[DoorState::Open][DoorEvent::CloseEvent] = [this]() { state = DoorState::Closed; };}void handleEvent(DoorEvent event) {auto eventHandler = stateHandlers[state].find(event);if (eventHandler != stateHandlers[state].end()) {eventHandler->second();}}private:DoorState state;std::unordered_map<DoorState, std::unordered_map<DoorEvent, std::function<void()>>> stateHandlers;
};static void BM_ModernCPPStateMachine(benchmark::State& state) {for (auto _ : state) {DoorStateMachine door;door.handleEvent(DoorEvent::OpenEvent);door.handleEvent(DoorEvent::CloseEvent);door.handleEvent(DoorEvent::LockEvent);}
}BENCHMARK(BM_ModernCPPStateMachine);BENCHMARK_MAIN();

测试结果:

Benchmark                         Time             CPU   Iterations
-------------------------------------------------------------------
BM_ModernCPPStateMachine        413 ns          413 ns      1694224

工业Misra C++编程标准

MISRA C++ 是工业领域的一个要求比较高的标准。以下是一些多态和继承的规则:

  • 规则 10-3-1:虚函数应有明确的用途,避免不必要的虚函数调用。
  • 规则 10-3-2:禁止多重继承。
  • 规则 10-3-3:尽量避免继承深度超过两个层次。
  • 规则 10-3-4:构造函数和析构函数中不应调用虚函数。
  • 规则 10-3-5:禁止多态对象的拷贝和赋值。

TinyFSM 的优缺点分析

优点

  1. 简洁和易用性:通过继承和定义状态类,TinyFSM 使状态和事件处理函数的定义更加直观。
  2. 代码可读性:每个状态独立成类,使状态转换逻辑清晰明了,便于理解和维护。
  3. 减少错误:提供了一个经过验证的框架,降低了手动管理状态转换时的出错风险。
  4. 可扩展性:可以轻松添加新状态和事件,只需定义新的状态类和事件类型。

缺点

  1. 内存使用:使用多态和虚函数增加了对象的内存开销,对于内存资源有限的嵌入式系统可能不太合适。
  2. 运行时开销:虚函数调用需要通过虚表查找实际的函数地址,增加了运行时开销。
  3. 不符合工业编码规范:TinyFSM 不符合严格的工业编码规范(如 MISRA C++)。

结论

总之,工业领域是否要选择TinyFSM还需要三思,尽管现代编程技术如 TinyFSM 对代码结构的简化带来了吸引力,但在需要遵循严格工业标准的环境中,推荐采用更传统的 C++ 编程方法。

相关文章:

开源TinyFSM状态机适用于嵌入式工业平台吗?

文章目录 引言基于传统 C 实现的状态机TinyFSM 实现的对比现代 C 实现的状态机性能对比TinyFSM 性能测试传统 C 性能测试现代 C 性能测试 工业Misra C编程标准TinyFSM 的优缺点分析结论 引言 TinyFSM是一个为C设计的轻量级有限状态机开源库库。 在嵌入式系统开发中&#xff0c…...

EE trade:利弗莫尔三步建仓法

在股市投资领域&#xff0c;利弗莫尔这个名字代表着无数的智慧和经历。他的三步建仓法成为了投资者们趋之若鹜的学习对象。本文将详细解析利弗莫尔的著名买入法&#xff0c;通过分步进攻方式&#xff0c;有效掌控市场并实现盈利。 一、利弗莫尔的三步建仓法详解 利弗莫尔三步…...

Java中Callable的应用

在Java中&#xff0c;Callable接口是一种用于并发编程的接口&#xff0c;它与Runnable类似&#xff0c;但有一些重要的区别和优势。Callable接口提供了一种在多线程环境下执行任务并返回结果的方法。以下是一些Callable接口的常见应用场景和使用示例&#xff1a; Callable vs.…...

测试卡无法仪表注册问题分析

1、问题描述 00101测试卡无法注册LTE网络&#xff0c;modemlog中发现终端未发起Attach请求&#xff0c;对比正常注册非正常注册的版本&#xff0c;发现正常的多出了ims apn。可以通过ATCGDCONT?来查询modem APN参数。 2、问题分析 目前Modem是一套&#xff0c;没有相关修改。因…...

【扩散模型(一)】Stable Diffusion中的重建分支(reconstruction branch)和条件分支(condition branch)

Stable Diffusion 是一种基于扩散模型的生成模型&#xff0c;用于生成图像等数据。在解释 Stable Diffusion 的过程中&#xff0c;经常会提到两个主要的分支&#xff1a;重建分支&#xff08;reconstruction branch&#xff09;和条件分支&#xff08;condition branch&#xf…...

WPF——Binding

一、作用 将Window GUI的运行机理从 “事件驱动” 转变为 “数据驱动”。将UI界面与业务逻辑解耦&#xff0c;使得改动一个而无需改动另一个。数据逻辑层自成体系&#xff0c;使得无需借助UI也可进行单元测试。 二、基础 1. Binding源模板 Binding包括源与目标&#xff0c;源…...

linux与windows环境下qt程序打包教程

一、演示环境 qt5.14.2 二、Linux 2.1 关联依赖文件 2.1.1 下载打包工具 在Windows环境下可以使用 Qt Creator自带的官方工具进行打包&#xff0c;而Linux环境下没有官方工具&#xff0c;需要借助第三方工具才能打包。如&#xff1a;linuxdeployqt、CQtDeployer、AppImage…...

LeetCode21-合并两个有序链表

题目 将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。 示例 1&#xff1a; 输入&#xff1a;l1 [1,2,4], l2 [1,3,4] 输出&#xff1a;[1,1,2,3,4,4] 示例 2&#xff1a; 输入&#xff1a;l1 [], l2 [] 输出&#xf…...

嵌入式学习——数据结构(双向无头无环链表)——day47

1. makefile——&#xff08;注意&#xff1a;双向无头链表第一个节点的pre为空&#xff0c;最后一个节点的next为空&#xff09; 单向无头链表只能找到后一个节点、双向无头链表前后节点都能找到 OBJ:doulink OBJSmain.c doublelink.c CClgcc$(OBJ):$(OBJS)$(CC) $^ -o $ .PH…...

MYSQL 将某个字段赋值当前时间

如 我们需要将use_time 赋值为当前时间&#xff1a; 准备三条数据 &#xff1a; 执行sql &#xff0c;2种当前时间赋值函数&#xff0c;1种关键字赋值 &#xff1a; update test_info SET use_timeNOW() WHERE id 1; update test_info SET use_timeCURRENT_TIMESTAMP() …...

ModelSim® SE Command Reference Manual : find命令的用法

该命令按类型和名称定位对象。命令的参数按对象类型分组。 1、语法 find nets | signals <object_name> … [-internal] [-nofilter] {[-in] [-inout] [-out] | [-ports]} [-recursive]find instances | blocks {<object_name> … | -bydu <design_unit> |…...

PHPMailer发送的中文内容乱码如何解决

一&#xff1a; PHPMailer sdk 文件中有个设置默认编码的位置&#xff1a; vendor/phpmailer/phpmailer/src/PHPMailer.php 二&#xff1a; 实际业务代码中&#xff1a; require /sdk/PHPMailer/vendor/autoload.php;$mail new PHPMailer(true);try {//Server settings$mai…...

.npmrc配置文件

.npmrc配置文件 .npmrc 是一个用于配置 npm 行为的文件。这个文件可以位于多个地方&#xff0c;但最常见的是位于项目目录或者你的用户主目录。npmrc文件由一系列键值对组成&#xff0c;用于配置npm在执行命令时的行为和参数。 一个 .npmrc 文件的例子可能包含以下内容&#…...

无线桥接两个路由器 实现全屋网络全覆盖

由于房屋结构、面积等因素&#xff0c;单个路由器的信号很难覆盖整个家。这时&#xff0c;我们可以通过无线桥接的方式&#xff0c;将两个路由器连接成一个网络&#xff0c;实现家庭网络的全面覆盖。 一、准备工作 在进行无线桥接之前&#xff0c;我们需要准备以下设备&#…...

qt开发-14_QListwidget 仿qq好友列表制作

QListWidget 继承 QListView。QListWidget 类提供了一个基于项的列表小部件。QListWidg et 是一个便捷的类&#xff0c;它提供了一个类似于 QListView&#xff08;下一小节将讲到&#xff09;提供的列表视图&#xff0c;但 是提供了一个用于添加和删除项目的基于项目的经典接口…...

基于hutool的sm2非对称加密使用示例

前言 现在在网上已经有很多同学对hutool的sm2使用进行了详细的介绍。但是在使用过程中不是很符合我个人的使用情况。我在这儿自己整理了一版&#xff0c;一方面希望能对有需要的同学有些许帮助&#xff0c;另一方面做个笔记&#xff0c;方便后期直接cv。 引入依赖 <dependen…...

深入Scala的变量声明与类型推断:语法糖下的智能推导

Scala是一种静态类型语言&#xff0c;以其强大的类型推断系统而闻名。变量声明和类型推断是Scala编程中的基础概念&#xff0c;它们共同简化了代码的编写并提高了开发效率。本文将深入探讨Scala中变量声明的语法规则和类型推断的工作原理。 1. Scala静态类型的优越性 静态类型…...

ATA-4052C高压功率放大器在新能源汽车安全测试中的应用

新能源汽车的崛起已经改变了汽车行业的格局&#xff0c;为环境友好型交通方式提供了更多的选择。为了确保这些新型汽车的安全性和可靠性&#xff0c;进行全面的安全测试是至关重要的。高压功率放大器在新能源汽车的安全测试中发挥着重要的作用&#xff0c;本文将介绍其应用以及…...

liunx打开谷歌报错

liunx打开谷歌报错[48526:48526:0624/173553.311113:ERROR:zygote_host_impl_linux.cc(99)] Running as root without --no-sandbox is not supported. See https://crbug.com/638180. 这个错误信息表明你尝试以root用户身份运行Chrome浏览器&#xff0c;但是没有使用–no-san…...

ICMAN液位检测大盘点

ICMAN液位检测原理&#xff1a; 基于双通道比较原理&#xff0c;一个通道检测当前无水状态下的环境电容&#xff0c;另一个通道通过传感电极去检测容器内液体的变化情况&#xff0c;通过两个通道电容的差值与芯片内部设定阈值做比较&#xff0c;来判断容器壁内部液位的变化或者…...

Chapter03-Authentication vulnerabilities

文章目录 1. 身份验证简介1.1 What is authentication1.2 difference between authentication and authorization1.3 身份验证机制失效的原因1.4 身份验证机制失效的影响 2. 基于登录功能的漏洞2.1 密码爆破2.2 用户名枚举2.3 有缺陷的暴力破解防护2.3.1 如果用户登录尝试失败次…...

Day131 | 灵神 | 回溯算法 | 子集型 子集

Day131 | 灵神 | 回溯算法 | 子集型 子集 78.子集 78. 子集 - 力扣&#xff08;LeetCode&#xff09; 思路&#xff1a; 笔者写过很多次这道题了&#xff0c;不想写题解了&#xff0c;大家看灵神讲解吧 回溯算法套路①子集型回溯【基础算法精讲 14】_哔哩哔哩_bilibili 完…...

Auto-Coder使用GPT-4o完成:在用TabPFN这个模型构建一个预测未来3天涨跌的分类任务

通过akshare库&#xff0c;获取股票数据&#xff0c;并生成TabPFN这个模型 可以识别、处理的格式&#xff0c;写一个完整的预处理示例&#xff0c;并构建一个预测未来 3 天股价涨跌的分类任务 用TabPFN这个模型构建一个预测未来 3 天股价涨跌的分类任务&#xff0c;进行预测并输…...

Qwen3-Embedding-0.6B深度解析:多语言语义检索的轻量级利器

第一章 引言&#xff1a;语义表示的新时代挑战与Qwen3的破局之路 1.1 文本嵌入的核心价值与技术演进 在人工智能领域&#xff0c;文本嵌入技术如同连接自然语言与机器理解的“神经突触”——它将人类语言转化为计算机可计算的语义向量&#xff0c;支撑着搜索引擎、推荐系统、…...

Module Federation 和 Native Federation 的比较

前言 Module Federation 是 Webpack 5 引入的微前端架构方案&#xff0c;允许不同独立构建的应用在运行时动态共享模块。 Native Federation 是 Angular 官方基于 Module Federation 理念实现的专为 Angular 优化的微前端方案。 概念解析 Module Federation (模块联邦) Modul…...

数据库分批入库

今天在工作中&#xff0c;遇到一个问题&#xff0c;就是分批查询的时候&#xff0c;由于批次过大导致出现了一些问题&#xff0c;一下是问题描述和解决方案&#xff1a; 示例&#xff1a; // 假设已有数据列表 dataList 和 PreparedStatement pstmt int batchSize 1000; // …...

聊一聊接口测试的意义有哪些?

目录 一、隔离性 & 早期测试 二、保障系统集成质量 三、验证业务逻辑的核心层 四、提升测试效率与覆盖度 五、系统稳定性的守护者 六、驱动团队协作与契约管理 七、性能与扩展性的前置评估 八、持续交付的核心支撑 接口测试的意义可以从四个维度展开&#xff0c;首…...

Java面试专项一-准备篇

一、企业简历筛选规则 一般企业的简历筛选流程&#xff1a;首先由HR先筛选一部分简历后&#xff0c;在将简历给到对应的项目负责人后再进行下一步的操作。 HR如何筛选简历 例如&#xff1a;Boss直聘&#xff08;招聘方平台&#xff09; 直接按照条件进行筛选 例如&#xff1a…...

【LeetCode】3309. 连接二进制表示可形成的最大数值(递归|回溯|位运算)

LeetCode 3309. 连接二进制表示可形成的最大数值&#xff08;中等&#xff09; 题目描述解题思路Java代码 题目描述 题目链接&#xff1a;LeetCode 3309. 连接二进制表示可形成的最大数值&#xff08;中等&#xff09; 给你一个长度为 3 的整数数组 nums。 现以某种顺序 连接…...

深入浅出Diffusion模型:从原理到实践的全方位教程

I. 引言&#xff1a;生成式AI的黎明 – Diffusion模型是什么&#xff1f; 近年来&#xff0c;生成式人工智能&#xff08;Generative AI&#xff09;领域取得了爆炸性的进展&#xff0c;模型能够根据简单的文本提示创作出逼真的图像、连贯的文本&#xff0c;乃至更多令人惊叹的…...