工业软件架构1:(QT和C++实现)

工业软件架构 - 事件驱动 - 1

  • 0.事件总线(EventBus)
  • 1. 传感器模块(Sensor Module)
  • 2. 硬件控制模块(Hardware Control Module)
  • 3. 按键处理模块(Button Handler Module)
  • 4. 界面管理模块(UI Module)
  • 5. 参数管理模块(Parameter Manager Module)
  • 6. 主程序集成与初始化
  • 7. 总结
  • 8.在设计监控和轮询机制时,选择使用定时器(QTimer)而不是线程池
    • 1. Qt 事件驱动架构的优势
    • 2. 实时性和响应性
    • 3. 线程池的适用场景
    • 4. 定时器 vs. 线程池的性能和复杂度对比
    • 5. 实际应用中的折中
    • 6. 具体应用场景分析
      • 传感器监控
      • 按键状态监控
      • 界面更新
    • 总结

0.事件总线(EventBus)

class EventBus : public QObject 
{Q_OBJECTpublic:static EventBus* instance() {static EventBus bus;return &bus;}template<typename... Args>void publish(const QString &eventType, Args&&... args){emit eventOccurred(eventType, QVariant::fromValue(std::forward<Args>(args)...));}signals:void eventOccurred(const QString &eventType, const QVariant &data);private:EventBus() = default;~EventBus() = default;
};

1. 传感器模块(Sensor Module)

  • 功能: 采集传感器数据,并通过事件总线向其他模块发布传感器数据更新事件。
  • 实现: 使用一个管理类来管理所有传感器,并将采集的数据通过事件总线发布。
class SensorModule : public QObject
{Q_OBJECTpublic:SensorModule(QObject *parent = nullptr) : QObject(parent){startMonitoring();}signals:void sensorDataUpdated(int sensorId, double value);private slots:void startMonitoring() {// 模拟传感器数据采集QTimer *timer = new QTimer(this);connect(timer, &QTimer::timeout, this, &SensorModule::collectData);timer->start(100); // 100ms 更新一次}void collectData(){for (int sensorId = 0; sensorId < 100; ++sensorId) {double value = qrand() % 100 / 10.0; // 模拟传感器数据emit sensorDataUpdated(sensorId, value);EventBus::instance()->publish("SensorDataUpdated", sensorId, value);}}
};

2. 硬件控制模块(Hardware Control Module)

  • 功能: 监控硬件状态,处理硬件控制命令,并与界面进行同步。
  • 实现: 每个硬件设备由一个独立的类进行管理,通过事件总线接收控制命令。
class HardwareControlModule : public QObject{Q_OBJECTpublic:HardwareControlModule(QObject *parent = nullptr) : QObject(parent){connect(EventBus::instance(), &EventBus::eventOccurred, this, &HardwareControlModule::onEvent);}private slots:void onEvent(const QString &eventType, const QVariant &data){if (eventType.startsWith("ControlHardware")){int hardwareId = eventType.section('_', 1, 1).toInt();handleHardwareControl(hardwareId, data);}}void handleHardwareControl(int hardwareId, const QVariant &data){// 处理硬件控制逻辑qDebug() << "Controlling hardware" << hardwareId << "with data" << data;// 控制硬件逻辑EventBus::instance()->publish("HardwareStateUpdated", hardwareId, data);}
};

3. 按键处理模块(Button Handler Module)

  • 功能: 实时监控硬件按键状态,并触发相应的操作。
  • 实现: 通过中断或轮询方式检测按键状态,触发相应的硬件控制和界面切换。
class ButtonHandlerModule : public QObject {Q_OBJECTpublic:ButtonHandlerModule(QObject *parent = nullptr) : QObject(parent){startMonitoring();}private slots:void startMonitoring(){QTimer *timer = new QTimer(this);connect(timer, &QTimer::timeout, this, &ButtonHandlerModule::checkButtonStates);timer->start(50); // 50ms 检查一次按键状态}void checkButtonStates(){// 模拟按键状态检查for (int buttonId = 0; buttonId < 10; ++buttonId){bool pressed = qrand() % 2; // 模拟按键按下状态if (pressed){handleButtonPress(buttonId);}}}void handleButtonPress(int buttonId){// 根据按键ID发布不同的硬件控制命令或界面切换事件EventBus::instance()->publish(QString("ControlHardware_%1").arg(buttonId), QVariant::fromValue(buttonId));EventBus::instance()->publish(QString("NavigateToPage_%1").arg(buttonId), QVariant::fromValue(buttonId));}
};

4. 界面管理模块(UI Module)

  • 功能: 管理 10 个左右的页面,并根据按键操作或硬件状态进行切换和更新。
  • 实现: 使用 QStackedWidget 或 QStackedLayout 管理多个页面,并通过事件总线进行页面切换。
class UIManager : public QObject 
{Q_OBJECTpublic:UIManager(QStackedWidget *stack, QObject *parent = nullptr) : QObject(parent), stackedWidget(stack) {connect(EventBus::instance(), &EventBus::eventOccurred, this, &UIManager::onEvent);}void registerPage(int pageId, QWidget *page){if (!pageRegistry.contains(pageId)) {pageRegistry[pageId] = page;stackedWidget->addWidget(page);}}private slots:void onEvent(const QString &eventType, const QVariant &data) {if (eventType.startsWith("NavigateToPage")){int pageId = eventType.section('_', 1, 1).toInt();stackedWidget->setCurrentWidget(pageRegistry[pageId]);} else if (eventType == "HardwareStateUpdated"){// 根据硬件状态更新当前页面}}private:QStackedWidget *stackedWidget;QHash<int, QWidget*> pageRegistry;
};

5. 参数管理模块(Parameter Manager Module)

  • 功能: 从文件加载初始化参数,修改后保存,并与硬件和界面同步。
  • 实现: 使用一个管理类来加载和保存参数,并通过事件总线发布参数更新事件。
class ParameterManager : public QObject {Q_OBJECTpublic:ParameterManager(QObject *parent = nullptr) : QObject(parent){loadParameters();}void loadParameters(){// 从文件加载参数QFile file("parameters.json");if (file.open(QIODevice::ReadOnly)) {QByteArray data = file.readAll();QJsonDocument doc = QJsonDocument::fromJson(data);parameters = doc.object().toVariantMap();file.close();// 发布加载完成的参数EventBus::instance()->publish("ParametersLoaded", parameters);}}void saveParameters(){QFile file("parameters.json");if (file.open(QIODevice::WriteOnly)) {QJsonDocument doc(QJsonObject::fromVariantMap(parameters));file.write(doc.toJson());file.close();}}QVariant getParameter(const QString &key) const {return parameters.value(key);}void setParameter(const QString &key, const QVariant &value) {parameters[key] = value;EventBus::instance()->publish("ParameterUpdated", key, value);saveParameters();}private:QVariantMap parameters;
};

6. 主程序集成与初始化

主程序将初始化所有模块,并通过事件总线连接各个模块,实现实时监控和响应。

int main(int argc, char *argv[]) 
{QApplication app(argc, argv);// 初始化堆叠界面QStackedWidget stackedWidget;// 初始化模块SensorModule sensorModule;HardwareControlModule hardwareControlModule;ButtonHandlerModule buttonHandlerModule;ParameterManager parameterManager;UIManager uiManager(&stackedWidget);// 注册页面for (int i = 0; i < 10; ++i){QWidget *page = new QWidget();QLabel *label = new QLabel(QString("Page %1").arg(i + 1), page);QVBoxLayout *layout = new QVBoxLayout(page);layout->addWidget(label);uiManager.registerPage(i, page);}// 显示堆叠窗口stackedWidget.show();return app.exec();
}

7. 总结

这个架构充分考虑了实时性、模块化、和可扩展性,适应了复杂硬件环境下的多功能需求。关键点如下:

  • 模块化设计:各个功能通过模块化设计进行管理,减少耦合度,增加系统的灵活性。
  • 事件驱动:事件总线在各模块间传递事件,实现模块间的松耦合和实时通信。
  • 实时响应:传感器数据、硬件状态和按键操作均通过事件总线实时更新,确保系统的及时响应。
  • 界面管理:使用 QStackedWidget 管理多个页面,并通过事件总线实现页面的动态切换和更新。
  • 参数管理:文件系统与应用程序参数保持同步,确保参数的一致性和持久性。
  • 通过这种架构,软件可以在复杂硬件环境中高效、可靠地运行,并提供实时的监控和控制功能。

8.在设计监控和轮询机制时,选择使用定时器(QTimer)而不是线程池

1. Qt 事件驱动架构的优势

Qt 框架本身是基于事件驱动的,这意味着它特别适合处理异步事件和响应用户交互。定时器是 Qt 提供的一种简单且高效的机制,可以在主线程的事件循环中定期执行任务,而不必创建额外的线程。

  • 轻量级: QTimer 在主线程中运行,不需要创建和管理多个线程,因此它的资源消耗非常低,特别是对于短时间间隔的任务,它非常高效。
  • 线程安全: 由于 QTimer 在主线程的事件循环中运行,所以你不必担心线程同步的问题。所有的事件处理都在同一个线程中进行,避免了跨线程访问共享资源时的复杂性。

2. 实时性和响应性

在需要实时监控传感器数据、按键状态等的场景下,使用 QTimer 进行定时轮询可以确保操作在主线程中快速执行,并立即响应结果。

  • 快速响应: 因为操作在主线程中执行,所以响应时间非常短。事件循环能够确保每次定时器触发时立即处理任务,而不用等待线程上下文切换。
  • 适合频繁的短时任务: 对于频繁且短时间的任务,如每 50ms 检查按键状态,QTimer 可以提供非常稳定和精确的定时机制,而不需要启动或销毁线程。

3. 线程池的适用场景

线程池(QThreadPool)和 QRunnable 更适合用于处理较长时间运行的任务或需要并行执行的任务。

  • 并行计算: 线程池适用于计算密集型任务或者需要并行执行的任务,而非简单的轮询或状态检查。
  • 避免主线程阻塞: 当任务可能会阻塞主线程时,使用线程池可以将这些任务移到后台线程中执行,而不影响主线程的界面响应。
    监控传感器数据和按键状态的操作都是非常频繁的、耗时非常短的任务,使用 QTimer 在主线程中处理这些任务更为合适。

4. 定时器 vs. 线程池的性能和复杂度对比

  • 资源消耗: 线程池会创建多个线程,并需要进行线程管理(如上下文切换、线程同步),这对资源的消耗较大,且管理复杂。QTimer 则没有这种开销。
  • 开发复杂度: 使用线程池需要考虑线程间同步和资源共享的复杂性。相较之下,QTimer 提供了更简洁的实现方式,避免了多线程编程的复杂性。

5. 实际应用中的折中

当然,如果你的监控任务变得非常复杂,或者涉及到长时间运行的操作,那么可以考虑结合使用 QTimer 和线程池:

  • 定时器触发线程池任务: 使用 QTimer 定期触发任务,然后将较重的任务委托给线程池处理,主线程继续保持响应性。
  • 多线程监控: 如果传感器数据的采集或按键监控变得非常复杂,且需要并行处理,可以将不同的任务分配给不同的线程池实例。

6. 具体应用场景分析

传感器监控

  • 使用 QTimer: 传感器状态的监控通常是频繁且快速的操作。例如,每隔 100ms 轮询一次传感器状态。在这种情况下,使用 QTimer 在主线程中定期轮询传感器是一个合理的选择,因为轮询操作通常是轻量级的。
    void SensorModule::startMonitoring(){QTimer *timer = new QTimer(this);connect(timer, &QTimer::timeout, this, &SensorModule::collectData);timer->start(100); // 100ms 更新一次
    }
    
  • 使用线程池: 如果传感器的数据处理需要耗时的计算,或者多个传感器的数据需要并行处理,那么可以使用线程池来处理这些任务,将数据采集和处理分离开来。
    void SensorModule::collectData() 
    {for (int sensorId = 0; sensorId < 100; ++sensorId){QtConcurrent::run([this, sensorId](){double value = getSensorData(sensorId); // 假设这是一个耗时操作emit sensorDataUpdated(sensorId, value);});}
    }
    

按键状态监控

  • 使用 QTimer: 按键状态的监控通常也是频繁且快速的操作(例如每 50ms 轮询一次按键状态)。按键检测通常不会耗费大量时间,因此使用 QTimer 进行轮询是合理的。

    void ButtonHandlerModule::startMonitoring() 
    {QTimer *timer = new QTimer(this);connect(timer, &QTimer::timeout, this, &ButtonHandlerModule::checkButtonStates);timer->start(50); // 50ms 检查一次按键状态
    }
    
  • 使用线程池: 如果按键按下后会触发一些耗时操作,比如复杂的硬件控制或计算任务,这些操作应该委托给线程池处理,而不是直接在 QTimer 的槽函数中执行,以避免阻塞主线程。

    void ButtonHandlerModule::handleButtonPress(int buttonId) 
    {Command *command = createCommandForButton(buttonId);if (command) {QtConcurrent::run([command]() {command->execute(); // 在后台线程中执行命令delete command;});}
    }
    

界面更新

  • 使用 QTimer: 如果有需要定期刷新界面的操作,比如显示传感器数据或硬件状态,那么 QTimer 是一个合适的工具。
    void UIManager::startAutoRefresh() 
    {QTimer *timer = new QTimer(this);connect(timer, &QTimer::timeout, this, &UIManager::refreshDisplay);timer->start(1000); // 每秒刷新一次
    }
    
  • 使用线程池: 当界面更新依赖于复杂的后台计算结果时,可以将计算部分放到线程池中,计算完成后通过 QMetaObject::invokeMethod 或 QTimer::singleShot 在主线程中更新界面。
    void SomeViewModel::performComplexCalculation(){QtConcurrent::run([this]() {QVariant result = performCalculation();QMetaObject::invokeMethod(this, [this, result]() {this->updateUI(result); // 确保在主线程中更新 UI});});
    }
    

总结

在这种实时监控和响应的场景下,QTimer 是一种更轻量级和合适的选择,它充分利用了 Qt 的事件驱动机制,简化了代码的实现,并保证了系统的响应性。如果监控任务较简单且需要频繁执行,使用 QTimer 是最佳选择。而线程池更适合需要并行执行和长时间运行的任务。

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

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

相关文章

单位的一位干部去世了,该单位领导参加他的追悼会致辞是这样写的?

单位的一位干部去世了&#xff0c;该单位领导参加他的追悼会致辞是这样写的&#xff1f; 这是一篇单位工会领导参加本单位一位普通干部追悼会的致词 &#xff08;范文点评&#xff09; 胡某晔同志追悼会悼词 各位来宾、各位亲友&#xff1a; 今天&#xff0c;我们怀着十分沉痛…

基于深度学习的单目标跟踪系统

基于深度学习的单目标跟踪&#xff0c;效果吊打传统算法&#xff0c;3060显卡上达到实时&#xff0c;代码python和c两个版本都有。 基于深度学习的单目标跟踪系统是一种先进的计算机视觉技术&#xff0c;它可以实现实时的、高精度的目标跟踪。与传统的基于特征匹配或模板匹配的…

【宝马中国-注册/登录安全分析报告】

前言 由于网站注册入口容易被黑客攻击&#xff0c;存在如下安全问题&#xff1a; 暴力破解密码&#xff0c;造成用户信息泄露短信盗刷的安全问题&#xff0c;影响业务及导致用户投诉带来经济损失&#xff0c;尤其是后付费客户&#xff0c;风险巨大&#xff0c;造成亏损无底洞…

一些零碎的关于合约测试,ERC20调用的知识

文章目录 前言一、vm.startPrank&#xff08;user&#xff09;是什么&#xff1f;二、approve 方法场景设定步骤 1: 用户授权步骤 2: 合约使用授权步骤 3: 检查授权状态示例代码 ERC20 approve的源代码代码解释代码解释应用场景示例注意事项代码解释可能的重载版本 _approve事件…

关于武汉芯景科技有限公司的实时时钟芯片XJ8337开发指南(兼容DS1337)

一、芯片引脚介绍 1.芯片引脚 2.引脚描述 二、系统结构图 三、功能描述 1.时钟功能 2.闹钟功能&#xff08;两个闹钟&#xff09; 3.振荡器停止控制 4.频率输出&#xff08;1HZ、1.096KHZ、8.192KHZ、32.768KHZ&#xff09; 5.振荡器停止检测 6.闹钟中断标志 四、程序代…

MySQL 查询优化详解

在使用 MySQL 数据库时&#xff0c;查询性能往往是影响应用程序整体性能的关键因素。通过对查询进行优化&#xff0c;可以显著提升数据库的响应速度和处理能力。本文将深入探讨 MySQL 查询优化的几个重要方面&#xff0c;包括查询分析与执行计划&#xff08;EXPLAIN&#xff09…

#C++ 笔记三

七、异常处理 1.概念 异常是程序在执行期间产生的问题。 C异常是指在程序运行时发生的特殊情况&#xff0c;比如下标越界等。 异常提供了一种转移程序控制权的方式。 2.抛出异常 throw语句的操作数可以是任意表达式&#xff0c;表达式结果的类型决定了抛出异常的类型。 …

k8s-pod 实战六 (如何在不同的部署环境中调整startupprobe的参数?)

在不同的部署环境中(如开发、测试、生产环境),你可能希望对 startupProbe 的参数进行调整,以适应不同的需求和条件。以下是几种常见的方法和实践: 方法一:使用 Kustomize 1. 目录结构 假设你的项目目录结构如下: my-app/ ├── base/ │ └── deployment.yaml …

进程和线程(操作系统八股文part2)

一个操作系统的进程和线程部分的笔记&#xff0c;大部分来源于&#xff1a;小林coding和Javaguide&#xff0c;以及操作系统黑书。 进程和线程 什么是进程 运行中的程序叫进程**&#xff08;Process&#xff09;**。 进程是资源分配的最小单位&#xff0c;线程是执行的最小…

redis分布式是如何实现的(面试版)

需要结合项目中的业务进行回答&#xff0c;通常情况下&#xff0c;分布式锁使用的场景&#xff1a;集群情况下的定时任务、抢单、幂等性场景。 下面先来看一个抢卷场景&#xff1a; 以下情况会出现超卖情况&#xff1a; 因为线程会交替执行&#xff0c;所以线程查询优惠价的数…

Socket编程---UDP篇

目录 一. UDP协议 二. Socket编程 2.1 sockaddr家族 2.2 接口介绍 三. 服务端实现 四. 服务端调用实现 五. 客户端实现 六. 效果展示 一. UDP协议 何为UDP协议的含义&#xff0c;上篇粗略提及了一下TCP与UDP的区别&#xff1a; TCP&#xff1a; •…

SpringBoot集成kafka-消息转发@sendTo()注解

SpringBoot集成kafka-消息转发sendTo 1、消费者2、生产者3、实体类对象4、JSON工具类5、配置文件application.yml6、测试类7、测试 1、消费者 启动消费者进行消息监听&#xff0c;消费者A监听到生产者发送的消息使用sendTo()注解将消息转发给消费者B package com.power.consu…

Django+Vue二手交易平台的设计与实现

目录 1 项目介绍2 项目截图3 核心代码3.1 需要的环境3.2 Django接口层3.3 实体类3.4 config.ini3.5 启动类3.5 Vue 4 数据库表设计5 文档参考6 计算机毕设选题推荐7 源码获取 1 项目介绍 博主个人介绍&#xff1a;CSDN认证博客专家&#xff0c;CSDN平台Java领域优质创作者&…

react中关于token的两个场景

场景一 在react项目中&#xff0c;路由跳转前需判断是否存在token&#xff0c;有则正常跳转&#xff0c;没有则去登录页面。 实现 这里使用的是localstorage存储token&#xff08;也可以使用redux管理token&#xff09; // src\components\AuthRoute.js // 封装高阶组件 //…

cesium 地形获取和生成

1.先从网上下载12.5m精度的地形,然后叠加无人机的地形数据 2.使用global mapper pro合并并导出完整的tiff 3.使用cesiumLab进行tiff的文件数据切片生成terrain格式的数据

cocos发布unity平台试玩广告失败问题

前言 关于如何输出试玩广告和平台文档中的说明就不赘述了&#xff0c;下面主要介绍在发布过程中遇到的问题。 检测不到mraid.open()或应用商店链接 提示&#xff1a; Creative pack validation failed: Your responsive playable is missing mraid.open() Your responsive p…

防御Nginx负载均衡中的拒绝服务攻击:策略与实践

拒绝服务攻击&#xff08;DoS&#xff09;和分布式拒绝服务攻击&#xff08;DDoS&#xff09;是网络安全的主要威胁之一&#xff0c;它们通过过载服务器资源或网络带宽&#xff0c;使得合法用户无法访问服务。Nginx作为一种流行的负载均衡器&#xff0c;提供了多种机制来帮助防…

第二讲__提炼

1.多态 1.定义 常见的两种情况&#xff1a; 1.继承 一个类A里面定义了一些方法&#xff0c;另一个类B继承了这个类并重写了A类的部分方法&#xff0c;主函数中用A类(父类)接收了一个B类(子类)对象&#xff0c;此时即时多态。 2.接口 一个接口A里面定义类很多抽象方法&#xff0…

从零开始学习Spring Cloud Consul:服务治理的完整解决方案

从零开始学习Spring Cloud Consul&#xff1a;服务治理的完整解决方案 在微服务架构中&#xff0c;服务的注册与发现、配置管理、负载均衡、健康检查等服务治理功能是保障系统稳定性和可扩展性的关键。Spring Cloud Consul作为Spring Cloud生态系统中与Consul集成的模块&#…

.NET HandyControl 深度解析:一个现代化的UI控件库

文章目录 前言一、选择HandyControl的原因二、如何使用HandyControl1.安装HandyControl2.使用代码例子 总结 前言 在.NET开发领域&#xff0c;UI&#xff08;用户界面&#xff09;设计的美观性和易用性对于应用程序的成功至关重要。为了帮助开发者快速构建现代化、美观且用户友…