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

工业软件架构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_OBJECT

public:
    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_OBJECT

public:
    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_OBJECT

public:
    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_OBJECT

public:
    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_OBJECT

public:
    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_OBJECT

public:
    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.kler.cn/a/282156.html

相关文章:

  • Spring Events在大型项目中的最佳实践
  • 力扣第 55 题 跳跃游戏
  • 使用YOLOv9进行图像与视频检测
  • 6.584-Lab1:MapReduce
  • Unity3D 完整直升机控制器(虚拟仿真级别)
  • AI行业动态:AGI预测、模型进化与工具革新
  • LLama3技术报告笔记(垂直能力)
  • js逆向——异步栈分析(上)
  • Faiss入门心得---向量数据库Faiss的搭建与使用
  • C#/WinForm实现炸弹人游戏
  • PaddleNLP 3.0 支持大语言模型开发
  • 新手学习打怪之编译安装LAMP和LNMP
  • 力扣850.矩形面积 II
  • Python的requests库详细介绍
  • 【持续更新】Mχ Plaayer Pro 1.86.0安卓知名播放器最新免费高级修改版
  • 深入浅出LangChain:从模型调用到Agents开发的全流程指南
  • 【React】跨域问题详解及解决方案
  • 手机三网状态实时查询分享
  • 软件设计模式 - 汇总
  • MyBatis的学习————下篇
  • SQL部分一
  • 【Docker】Docker学习01 | 什么是docker?
  • 【给女朋友讲C++】C++的调试之gdb
  • Wordpress 6.x 修改文件上传大小限制
  • 数学建模---论文写作
  • C# 数组,List,Stack,Dictionary,Queue,LinkedList 如何选择