Qt ModbusSlave多线程实践总结

发布于:2025-05-01 ⋅ 阅读:(14) ⋅ 点赞:(0)

最近项目中用到了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_OBJECT

public:
    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);
}


网站公告

今日签到

点亮在社区的每一天
去签到