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

Qt ModbusSlave多线程实践总结

最近项目中用到了ModbusSlave,也就是Modbus从设备的功能,之前用的基本都是master设备,所以读取数据啥的用单线程就行了,用

 void WaitHelper::WaitImplByEventloop(int msec)
{QEventLoop loop;QTimer::singleShot(msec, &loop, &QEventLoop::quit);  // 设置定时器,在超时时退出事件循环loop.exec();  // 进入事件循
}

这种方法进行流程等待也没啥问题。但是在slave模式下,使用这种模式就会造成事件无法正常回调,在等待过程中没法回复请求,导致master设备卡死,最终使用多线程解决的该问题。
这里遵循Qt多线程最佳实践,使用worker->moveToThread(workerThread);实现多线程功能。核心方法就是创建ModbusWorker实例并且移动到QThread中。

bool ModbusTcpSlave::connectToHost()
{worker = new ModbusWorker();workerThread = new QThread(this);worker->moveToThread(workerThread);connect(workerThread, &QThread::started, [=]() {worker->startMonitoring(_obj["address"].toString());});connect(worker, &ModbusWorker::dataWritten, this, &ModbusTcpSlave::onDataWritten);connect(workerThread, &QThread::finished, worker, &QObject::deleteLater);connect(workerThread, &QThread::finished, workerThread, &QObject::deleteLater);workerThread->start();return true;
}

然后就是调用方法时使用QMetaObject::invokeMethod异步调用

void ModbusTcpSlave::WriteRegister(int address, int value)
{// 异步跨线程调用QMetaObject::invokeMethod(worker,"setData",Qt::QueuedConnection,Q_ARG(QModbusDataUnit::RegisterType, QModbusDataUnit::HoldingRegisters),Q_ARG(int, address),Q_ARG(quint16, value));
}

以下是源码:

#ifndef MODBUSWORKER_H
#define MODBUSWORKER_H#include <QObject>
#include <QModbusTcpServer>
#include <QModbusDataUnit>class ModbusWorker : public QObject
{Q_OBJECTpublic:explicit ModbusWorker(QObject *parent = nullptr);~ModbusWorker();bool connectToHost(const QString &address);void startMonitoring(const QString &address);void stopMonitoring();public slots:bool setData(QModbusDataUnit::RegisterType table, int address, quint16 value);signals:void dataWritten(QModbusDataUnit::RegisterType table, int address, quint16 value);private slots:void onDataWritten(QModbusDataUnit::RegisterType table, int address, int size);void onStateChanged(QModbusDevice::State state);private:QModbusTcpServer *_modbusServer;
};#endif // MODBUSWORKER_H
#include <QUrl>
#include <QDebug>
#include "modbusworker.h"ModbusWorker::ModbusWorker(QObject *parent) : QObject(parent), _modbusServer(new QModbusTcpServer(this))
{qRegisterMetaType<QModbusDataUnit::RegisterType>("QModbusDataUnit::RegisterType");QObject::connect(_modbusServer, &QModbusTcpServer::stateChanged, this, &ModbusWorker::onStateChanged);QObject::connect(_modbusServer, &QModbusTcpServer::dataWritten, this, &ModbusWorker::onDataWritten);
}ModbusWorker::~ModbusWorker()
{_modbusServer->disconnectDevice();
}bool ModbusWorker::setData(QModbusDataUnit::RegisterType table, int address, quint16 value)
{return _modbusServer->setData(table,address,value);
}void ModbusWorker::onDataWritten(QModbusDataUnit::RegisterType table, int address, int size)
{if(size==1){quint16 value;_modbusServer->data(table, address, &value);emit dataWritten(table,address,value);}
}void ModbusWorker::onStateChanged(QModbusDevice::State state)
{qDebug()<<__FUNCTION__<<__LINE__<<state;
}bool ModbusWorker::connectToHost(const QString &address)
{const QUrl url = QUrl::fromUserInput(address);_modbusServer->setConnectionParameter(QModbusDevice::NetworkPortParameter, url.port());_modbusServer->setConnectionParameter(QModbusDevice::NetworkAddressParameter, url.host());// 设置Modbus数据单元,类型为保持寄存器QModbusDataUnitMap reg;reg.insert(QModbusDataUnit::HoldingRegisters, {QModbusDataUnit::HoldingRegisters, 0, 30});reg.insert(QModbusDataUnit::InputRegisters, {QModbusDataUnit::InputRegisters, 0, 10});_modbusServer->setMap(reg);_modbusServer->setServerAddress(1);// 初始化保持寄存器的值for (int i = 0; i < 10; ++i) {_modbusServer->setData(QModbusDataUnit::HoldingRegisters, i, 0);}return _modbusServer->connectDevice();
}void ModbusWorker::startMonitoring(const QString &address)
{if(connectToHost(address)){return;}
}void ModbusWorker::stopMonitoring()
{_modbusServer->disconnectDevice();
}```cpp
#ifndef MODBUSTCPSLAVE_H
#define MODBUSTCPSLAVE_H#include <QObject>
#include <QModbusTcpServer>
#include "basecommunication.h"
#include "modbusworker.h"class ModbusTcpSlave : public BaseCommunication
{Q_OBJECT
public:Q_INVOKABLE explicit ModbusTcpSlave(QObject *parent = nullptr);signals:public slots:// BaseCommunication interface
public:virtual bool connectToHost() override;virtual bool isConnected() override;virtual bool disConnectFromHost() override;void WriteRegister(int address, int value);void WriteInputRegister(int address, int value);signals:void dataWritten(QModbusDataUnit::RegisterType,int address, quint16 val);private slots:void onDataWritten(QModbusDataUnit::RegisterType table, int address, int value);private:QThread *workerThread;ModbusWorker *worker;
};#endif // TCPMODBUSSERVER_H```cpp
#include <QUrl>
#include "modbustcpslave.h"ModbusTcpSlave::ModbusTcpSlave(QObject *parent) : BaseCommunication(parent)
{
}bool ModbusTcpSlave::connectToHost()
{worker = new ModbusWorker();workerThread = new QThread(this);worker->moveToThread(workerThread);connect(workerThread, &QThread::started, [=]() {worker->startMonitoring(_obj["address"].toString());});connect(worker, &ModbusWorker::dataWritten, this, &ModbusTcpSlave::onDataWritten);connect(workerThread, &QThread::finished, worker, &QObject::deleteLater);connect(workerThread, &QThread::finished, workerThread, &QObject::deleteLater);workerThread->start();return true;
}bool ModbusTcpSlave::isConnected()
{return true;
}bool ModbusTcpSlave::disConnectFromHost()
{worker->stopMonitoring();return true;
}void ModbusTcpSlave::WriteRegister(int address, int value)
{// 异步跨线程调用QMetaObject::invokeMethod(worker,"setData",Qt::QueuedConnection,Q_ARG(QModbusDataUnit::RegisterType, QModbusDataUnit::HoldingRegisters),Q_ARG(int, address),Q_ARG(quint16, value));
}void ModbusTcpSlave::WriteInputRegister(int address, int value)
{QMetaObject::invokeMethod(worker,"setData",Qt::QueuedConnection,Q_ARG(QModbusDataUnit::RegisterType, QModbusDataUnit::InputRegisters),Q_ARG(int, address),Q_ARG(quint16, value));
}void ModbusTcpSlave::onDataWritten(QModbusDataUnit::RegisterType table, int address, int value)
{emit dataWritten(table,address,value);
}
http://www.xdnf.cn/news/140113.html

相关文章:

  • macOS 更新后找不到钥匙串访问工具的解决方案
  • 手机打电话时电脑坐席同时收听对方说话并插入IVR预录声音片段
  • 使用Python脚本在Mac上彻底清除Chrome浏览历史:开发实战与隐私保护指南
  • 【2025最新面试操作系统八股】CPU利用率和load(负载)的区别,CPU利用率怎么算。
  • 边界凸台建模与实例
  • 电子学会—青少年软件编程 python一级等级考试真题—2025年03月
  • 时间复杂度分析
  • Linux学习笔记之环境变量
  • 住宅IP如何选择:长效VS短效,哪个更适合你的业务?
  • java排序算法-计数排序
  • OCR(Optical Character Recognition),光学字符识别
  • HashMap底层原理 什么是哈希表?哈希冲突?如何处理哈希冲突?
  • kotlin与MVVM结合使用总结(三)
  • (Go Gin)基于Go的WEB开发框架,GO Gin是什么?怎么启动?本文给你答案
  • 防火墙技术深度解析:从包过滤到云原生防火墙的部署与实战
  • 【1】GD32 系统架构、内核、中断系统、存储器系统
  • IDEA编写flinkSQL(快速体验版本,--无需配置环境)
  • Vue3后代组件多祖先通讯设计方案
  • OpenCV 图形API(63)图像结构分析和形状描述符------计算图像中非零像素的边界框函数boundingRect()
  • 52.[前端开发-JS实战框架应用]Day03-AJAX-插件开发-备课项目实战-Lodash
  • ubuntu20.04安装x11vnc远程桌面
  • AI数据分析的利器:解锁BI工具的无限潜力
  • android将打包文件的password和alias写入到本地文件
  • 区块链如何达成共识:PoW/PoS/DPoS的原理、争议与适用场景全解
  • 一些有关ffmpeg 使用(1)
  • LSPatch官方版:无Root Xposed框架,自由定制手机体验
  • MySQL的日志--Undo Log【学习笔记】
  • MCP认证考试技术难题实战破解:从IP冲突到PowerShell命令的深度指南
  • Hadoop进阶之路
  • 第100+39步 ChatGPT学习:R语言实现Xgboost SHAP