最近項目中用到了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
#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);
}