Qt ModbusTCP ModbusRTU 使用同步读和异步写

使用Qt自带的库开发,添加相关头文件

    #include <QModbusTcpClient>
    #include <QModbusReply>
    #include <QSerialPort>
    #include <QModbusDataUnit>
    #include <QModbusRtuSerialMaster>

一、寄存器说明

Modbus寄存器的操作包括读写和只读。具体如下:

        enum RegisterType {
            Invalid,
            DiscreteInputs,
            Coils,
            InputRegisters,
            HoldingRegisters
        };

分别叫做:

    离散输入寄存器(只读,通常为开关量输入)
    线圈寄存器(读写,一般为继电器的控制)
    输入寄存器(只读,一般为模拟量输入)
    保持寄存器(读写,一般状态参数控制)

二、同步读取

QT采用事件处理机制,由于Modbus读取过程通常有时延,Qt机制不适合采用while延时等待读取的方式。网上大多采用的是基于ModbusReply的Finished信号,做异步处理。导致在获取寄存器数据时候比较麻烦。因此,可以考虑使用事件循环做同步处理。

读寄存器数据的接口:

    QVector<quint16>
    MainWindow::readModbusTcpUnit(QModbusDataUnit::RegisterType type, int startAddr, int numbers, int serverID, bool *isOK)

    type:指明读取的类型,由RegisterType类型定义
    startAddr:起始地址
    numbers:读取的数量
    serverID:服务器的ID
    isOK:当前操作是否成功
    返回值:QVector<quint16> 类型,读取的数据放在向量中

以下是具体实现:

    QVector<quint16>
    MainWindow::readModbusTcpUnit(QModbusDataUnit::RegisterType type, int startAddr, int numbers, int serverID, bool *isOK)
    {
        QVector<quint16> results;
        results.clear();
     
        if (mModbusClient->state() != QModbusDevice::ConnectedState) {
            *isOK = false;
            return results;
        }
     
        QModbusDataUnit readUnit(type, startAddr, static_cast<quint16>(numbers));
     
        auto *reply = mModbusClient->sendReadRequest(readUnit, serverID);
     
        if (!reply->isFinished()) {
            QEventLoop loop;
            connect(reply, &QModbusReply::finished,&loop,&QEventLoop::quit);
            eventLoop.exec();
        }
     
        if (reply->error() == QModbusDevice::NoError) {
     
            const QModbusDataUnit unit = reply->result();
     
            QString strType;
            switch (type){
                case QModbusDataUnit::Coils: strType = "Coils"; break;
                case QModbusDataUnit::DiscreteInputs: strType = "DiscreteInputs"; break;
                case QModbusDataUnit::HoldingRegisters: strType = "HoldingRegisters"; break;
                case QModbusDataUnit::InputRegisters: strType = "InputRegisters"; break;
            }
     
            qDebug()<<"read "<<strType<< " startAddr = "<<startAddr<<" numbers = "<<numbers<<" values = " <<unit.values();
     
            *isOK = true;
            results = unit.values();
        } else {
            *isOK = false;
        }
     
        delete reply;
        return results;
    }

使用方法:

    /*
     * bool isOK;
     * QVector<quint16> readUnit = readModbusTcpUnit(QModbusDataUnit::Coils, 0, 10, SERVER_ID, &isOK);
     * QVector<quint16> readUnit = readModbusTcpUnit(QModbusDataUnit::DiscreteInputs, 0, 10, SERVER_ID, &isOK);
     * QVector<quint16> readUnit = readModbusTcpUnit(QModbusDataUnit::HoldingRegisters, 0, 10, SERVER_ID, &isOK);
     * QVector<quint16> readUnit = readModbusTcpUnit(QModbusDataUnit::InputRegisters, 0, 10, SERVER_ID, &isOK);
    */

三、异步写

写寄存器只有Coils和HoldingRegisters可以操作,写操作使用异步执行。

接口如下:

    bool
    MainWindow::writeModbusTcpCoils(QVector<quint16>  coilsValues, int startAddr, int numbers, int serverID)

    coilsValues:向量,需要写入的值;
    startAddr:起始地址
    numbers:需要写寄存器的数量
    serverID:服务器的ID

以写线圈为例,实现如下:

    void
    MainWindow::writeModbusTcpCoils(QVector<quint16>  coilsValues, int startAddr, int numbers, int serverID)
    {
        if (coilsValues.size() < numbers){
            qDebug()<<"error : coilsValue size < numbers";
            return;
        }
     
        if (mModbusClient->state() != QModbusDevice::ConnectedState) {
            qDebug()<<"error : disConnectedState";
            return;
        }
     
        QModbusDataUnit writeUnit(QModbusDataUnit::Coils, startAddr, static_cast<quint16>(numbers));
     
        for (int valueIdx = 0; valueIdx < writeUnit.valueCount(); ++valueIdx) {
            writeUnit.setValue(valueIdx, coilsValues.at(valueIdx));
        }
     
        auto *reply = mModbusClient->sendWriteRequest(writeUnit, serverID);
     
        if (!reply->isFinished()) {
            connect(reply, &QModbusReply::finished, this, [this, reply]() {
                if (reply->error() == QModbusDevice::ProtocolError) {
                    qDebug() << “ProtocolError”;
     
                } else if (reply->error() != QModbusDevice::NoError) {
                    qDebug() << “Error”;
                }
            });
        }
     
        reply->deleteLater();
    }

使用方法:

    /*
     * QVector<quint16> values;
     * values.push_back(1);
     * values.push_back(0);
     * values.push_back(1);
     * writeModbusTcpCoils(values, 0, 3, SERVER_ID);
     * writeModbusTcpCoils(values, 3, 2, SERVER_ID);
    */


 

  • 0
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Qt Modbus是一个用于与Modbus设备通信的库。同步异步是指在操作时采用同步方式,而在操作时采用异步方式。 在同步操作中,主线程会等待从Modbus设备取到数据后才会继续执行后续操作。这意味着主线程会被阻塞,直到取操作完成。同步取可以保证在取到数据后立即进行相应的处理,但如果取操作时间较长,可能会导致用户界面无响应或造成其他延迟。 异步操作是指主线程在发送请求后会立即继续执行后续代码,而不必等待操作完成。操作会在后台线程中进行,这样可以避免阻塞主线程,并允许主线程继续执行其他任务。异步操作提供了较好的响应性能和用户体验。 使用Qt Modbus进行同步异步时,可以通过在取操作时使用QModbusTcpClient的read函数,并在操作时使用QModbusTcpClient的sendWriteRequest函数来实现。 例如,可以使用类似以下的代码来实现同步异步: ```cpp // 创建Modbus Tcp Client对象 QModbusTcpClient *client = new QModbusTcpClient(this); // 连接到Modbus设备 client->setConnectionParameter(QModbusDevice::NetworkPortParameter, 502); client->setConnectionParameter(QModbusDevice::NetworkAddressParameter, "192.168.1.1"); client->connectDevice(); // 同步操作 if (auto *reply = client->sendReadRequest(readRequest, serverAddress)) { if (!reply->isFinished()) QEventLoop loop; connect(reply, &QModbusReply::finished, &loop, &QEventLoop::quit); loop.exec(); if (reply->error() == QModbusDevice::NoError) { // 根据取到的数据进行相应的处理 } else { // 处理取错误 } reply->deleteLater(); } else { // 处理取请求错误 reply->deleteLater(); } } // 异步操作 if (auto *reply = client->sendWriteRequest(writeRequest, serverAddress)) { connect(reply, &QModbusReply::finished, this, &MyClass::writeFinished); reply->deleteLater(); } ``` 通过以上代码,我们可以实现在操作时等待数据的同步取,而在操作时继续执行其他任务的异步入。这样可以提高程序的响应性和用户体验。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值