Qtmodbus之TCP模式读操作

Posted 沧海一笑-dj

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Qtmodbus之TCP模式读操作相关的知识,希望对你有一定的参考价值。

00. 目录

01. 概述

Qt中几个常用的串口modbus类

QModbusRtuSerialSlave       //modbus串口通信方式下的服务器类
QModbusRtuSerialMaster      //串口通信方式下的客户端类
QModbusServer               // QModbusServer类接收和处理modbus的请求。
QModbusDataUnit             //存储接收和发送数据的类,数据类型为1bit和16bit
QModbusReply                //客户端访问服务器后得到的回复(如客户端读服务器数据时包含数据信息)

02. 开发环境

Windows系统:Windows10

Qt版本:Qt5.15或者Qt6

Pro配置文件如下

QT       += core gui serialbus serialport

greaterThan(QT_MAJOR_VERSION, 4): QT += widgets

CONFIG += c++11

# You can make your code fail to compile if it uses deprecated APIs.
# In order to do so, uncomment the following line.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000    # disables all the APIs deprecated before Qt 6.0.0

SOURCES += \\
    main.cpp \\
    widget.cpp

HEADERS += \\
    widget.h

# Default rules for deployment.
qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS += target

03. 读Coils程序示例

widget.h文件

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>

//前向声明
class QModbusClient;
class QModbusReply;


QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);
    ~Widget();

private:
    Ui::Widget *ui;

    QModbusClient *modbusDevice = nullptr;


private slots:
    void onReadReady();
    void onModbusStateChanged(int state);
    void on_btnConnect_clicked();
    void on_btnCoil_clicked();
    void on_btnDiscreteInputs_clicked();
    void on_btnInputRegisters_clicked();
    void on_btnHoldingRegisters_clicked();
};
#endif // WIDGET_H

widget.cpp文件

#include "widget.h"
#include "ui_widget.h"
#include <QDebug>
#include <QModbusTcpClient>


//构造函数
Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);

    //1. 创建QModbusDevice对象
    modbusDevice = new QModbusTcpClient(this);

    //禁用所有的读操作
    ui->btnCoil->setEnabled(false);
    ui->btnDiscreteInputs->setEnabled(false);
    ui->btnHoldingRegisters->setEnabled(false);
    ui->btnInputRegisters->setEnabled(false);

    connect(modbusDevice, &QModbusClient::errorOccurred, [this](QModbusDevice::Error) {
        qDebug() << "new Error: " << modbusDevice->errorString();
    });

    if (nullptr == modbusDevice)
    {
        qDebug() << "Cannot Create Modbus Client";
    }
    else
    {
        connect(modbusDevice, &QModbusClient::stateChanged,
                this, &Widget::onModbusStateChanged);
    }


}

//析构函数
Widget::~Widget()
{
    if (modbusDevice)
    {
        modbusDevice->disconnectDevice();
    }

    delete modbusDevice;

    delete ui;
}


void Widget::onReadReady()
{
    auto reply = qobject_cast<QModbusReply*>(sender());
    if (nullptr == reply)
    {
        return;
    }

    //判断是否出错
    if (reply->error() == QModbusDevice::NoError)
    {
        //读取响应数据
        const QModbusDataUnit responseData = reply->result();

        qDebug() << "读到数据为:" << responseData.values();

    }
    else if (reply->error() == QModbusDevice::ProtocolError)
    {
        qDebug() << "Read response Protocol error: " << reply->errorString();
    }
    else
    {
        qDebug() << "Read response Error: " << reply->errorString();
    }


    //删除reply
    reply->deleteLater();
}


void Widget::onModbusStateChanged(int state)
{
    //判断Modbus设备连接是否处于连接状态
    if (state == QModbusDevice::UnconnectedState)
    {
        qDebug() << "TCP Client连接到Server 未连接";
        ui->btnConnect->setText(tr("连接"));

        ui->btnCoil->setEnabled(false);
        ui->btnDiscreteInputs->setEnabled(false);
        ui->btnHoldingRegisters->setEnabled(false);
        ui->btnInputRegisters->setEnabled(false);
    }
    else if (state == QModbusDevice::ConnectingState)
    {
        qDebug() << "TCP Client正在连接Server";
    }
    else if (state == QModbusDevice::ConnectedState)
    {
        qDebug() << "TCP Client已经连接到Server";
        ui->btnConnect->setText(tr("断开连接"));

        ui->btnCoil->setEnabled(true);
        ui->btnDiscreteInputs->setEnabled(true);
        ui->btnHoldingRegisters->setEnabled(true);
        ui->btnInputRegisters->setEnabled(true);

    }
    else if (state == QModbusDevice::ClosingState)
    {
        qDebug() << "设备已经被关闭";
    }
}

//连接按钮槽函数
void Widget::on_btnConnect_clicked()
{
    if (!modbusDevice)
    {
        return;
    }

    if (modbusDevice->state() != QModbusDevice::ConnectedState)
    {
        modbusDevice->setConnectionParameter(QModbusDevice::NetworkAddressParameter, "127.0.0.1");
        modbusDevice->setConnectionParameter(QModbusDevice::NetworkPortParameter, 10086);

        //设置超时时间
        modbusDevice->setTimeout(1000); //1秒
        //设置失败重试次数
        modbusDevice->setNumberOfRetries(3);

        //连接到服务端
        bool ok = modbusDevice->connectDevice();
        if (!ok)
        {
            qDebug() << "modbusDevice->connectDevice failed";
        }
    }
    else
    {
        //断开连接
        modbusDevice->disconnectDevice();
        ui->btnConnect->setText(tr("连接"));

        ui->btnCoil->setEnabled(false);
        ui->btnDiscreteInputs->setEnabled(false);
        ui->btnHoldingRegisters->setEnabled(false);
        ui->btnInputRegisters->setEnabled(false);
    }

}

//读线圈
void Widget::on_btnCoil_clicked()
{
    //QModbusDataUnit::Coils 从地址0开始读取10个线圈值
    QModbusDataUnit data(QModbusDataUnit::Coils, 0, 10);

    auto reply = modbusDevice->sendReadRequest(data, 0x1);
    if (nullptr == reply)
    {
        qDebug() << "发送请求数据失败: " << modbusDevice->errorString();
    }
    else
    {
        if (!reply->isFinished())
        {
            connect(reply, &QModbusReply::finished, this, &Widget::onReadReady);
        }
        else
        {
            //broadcast replies return immediately
            delete reply;
        }
    }
}

//读离散输入
void Widget::on_btnDiscreteInputs_clicked()
{

}

//读输入寄存器
void Widget::on_btnInputRegisters_clicked()
{

}

//读保持寄存器
void Widget::on_btnHoldingRegisters_clicked()
{

}

执行结果

15:18:15: Starting D:\\ProgramData\\Qt\\build-Test1-Desktop_Qt_5_15_2_MinGW_32_bit-Debug\\debug\\Test1.exe ...
TCP Client正在连接Server
TCP Client已经连接到Server
读到数据为: QVector(0, 0, 1, 0, 0, 0, 1, 0, 1, 0)
设备已经被关闭
TCP Client连接到Server 未连接
15:18:36: D:\\ProgramData\\Qt\\build-Test1-Desktop_Qt_5_15_2_MinGW_32_bit-Debug\\debug\\Test1.exe exited with code 0

04. 读DiscreteInputs程序示例

widget.h文件

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>

//前向声明
class QModbusClient;
class QModbusReply;


QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);
    ~Widget();

private:
    Ui::Widget *ui;

    QModbusClient *modbusDevice = nullptr;


private slots:
    void onReadReady();
    void onModbusStateChanged(int state);
    void on_btnConnect_clicked();
    void on_btnCoil_clicked();
    void on_btnDiscreteInputs_clicked();
    void on_btnInputRegisters_clicked();
    void on_btnHoldingRegisters_clicked();
};
#endif // WIDGET_H


widget.cpp文件

//读离散输入
void Widget::on_btnDiscreteInputs_clicked()
{
    //QModbusDataUnit::DiscreteInputs 从地址0开始读取10个离散输入值
    QModbusDataUnit data(QModbusDataUnit::DiscreteInputs, 0, 10);

    auto reply = modbusDevice->sendReadRequest(data, 0x1);
    if (nullptr == reply)
    {
        qDebug() << "发送请求数据失败: " << modbusDevice->errorString();
    }
    else
    {
        if (!reply->isFinished())
        {
            connect(reply, &QModbusReply::finished, this, &Widget::onReadReady);
        }
        else
        {
            //broadcast replies return immediately
            delete reply;
        }
    }
}

执行结果

15:25:14: Starting D:\\ProgramData\\Qt\\build-Test1-Desktop_Qt_5_15_2_MinGW_32_bit-Debug\\debug\\Test1.exe ...
TCP Client正在连接Server
TCP Client已经连接到Server
读到数据为: QVector(1, 1, 1, 1, 1, 0, 0, 0, 0, 0)
设备已经被关闭
TCP Client连接到Server 未连接

05. 读InputRegisters程序示例

widget.h文件

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>

//前向声明
class QModbusClient;
class QModbusReply;


QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);
    ~Widget();

private:
    Ui::Widget *ui;

    QModbusClient *modbusDevice = nullptr;


private slots:
    void onReadReady();
    void onModbusStateChanged(int state);
    void on_btnConnect_clicked();
    void on_btnCoil_clicked();
    void on_btnDiscreteInputs_clicked();
    void on_btnInputRegisters_clicked();
    void on_btnHoldingRegisters_clicked();
};
#endif // WIDGET_H


widget.cpp文件

//读输入寄存器
void Widget::on_btnInputRegisters_clicked()
{
    //QModbusDataUnit::InputRegisters 从地址0开始读取10个输入寄存器的值
    QModbusDataUnit data(QModbusDataUnit::InputRegisters, 0, 10);

    auto reply = modbusDevice->sendReadRequest(data, 0x1);
    if (nullptr == reply)
    {
        qDebug() << "发送请求数据失败: " << modbusDevice->errorString();
    }
    else
    {
        if (!reply->isFinished())
        {
            connect(reply, &QModbusReply::finished, this, &Widget::onReadReady);
        }
        else
        {
            //broadcast replies return immediately
            delete reply;
        }
    }
}

执行结果

15:28:06: Starting D:\\ProgramData\\Qt\\build-Test1-Desktop_Qt_5_15_2_MinGW_32_bit-Debug\\debug\\Test1.exe ...
TCP Client正在连接Server
TCP Client已经连接到Server
读到数据为: QVector(0, 12, 0, 11, 0, 0, 13, 0, 0, 0)
设备已经被关闭
TCP Client连接到Server 未连接

06. 读HoldingRegisters程序示例

widget.h文件

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>

//前向声明
class QModbusClient;
class QModbusReply;


QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);
    ~Widget();

private:
    Ui::Widget *ui;

    QModbusClient *modbusDevice = nullptr;


private slots:
    void onReadReady();
    void onModbusStateChanged(int state);
    void on_btnConnect_clicked();
    void on_btnCoil_clicked();
    void on_btnDiscreteInputs_clicked();
    void on_btnInputRegisters_clicked();
    void on_btnHoldingRegisters_clicked();
};
#endif // WIDGET_H

widget.cpp文件

//读保持寄存器
void Widget::on_btnHoldingRegisters_clicked()
{
    //从地址0开始读取10个保持寄存器的值
    QModbusDataUnit data(QModbusDataUnit::HoldingRegisters, 0, 10);

    auto reply = modbusDevice->sendReadRequest(data, 0x1);
    if (nullptr == reply)
    {
        qDebug() << "发送请求数据失败: " << modbusDevice->errorString();
    }
    else
    {
        if (!reply->isFinished())
        {
            connect(reply, &QModbusReply::finished, this, &Widget::onReadReady);
        }
        else
        {
            //broadcast replies return immediately
            delete reply;
        }
    }
}

执行结果

15:29:35: Starting D:\\ProgramData\\Qt\\build-Test1-Desktop_Qt_5_15_2_MinGW_32_bit-Debug\\debug\\Test1.exe ...
TCP Client正在连接Server
TCP Client已经连接到Server
读到数据为: QVector(0, 0, 11, 12, 13, 14, 15, 0, 0, 111)
设备已经被关闭
TCP Client连接到Server 未连接

07. 综合示例

程序界面

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-utd9gda4-1621496836516)(assets/image-20210520154331905.png)]

settingdialog.h文件

#ifndef SETTINGDIALOG_H
#define SETTINGDIALOG_H

#include <QDialog>
#include <QtSerialPort>

namespace Ui {
class SettingDialog;
}

//串口设置相关类
class SettingDialog : public QDialog
{
    Q_OBJECT

public:
    struct Settings
    {
        //串口名
        QString serialName = "COM3";
        //校验位
        int parity = QSerialPort::NoParity;
        //波特率
        int baud = QSerialPort::Baud19200;
        //数据位
        int dataBits = QSerialPort::Data8;
        //停止位
        int stopBits = QSerialPort::OneStop;

        //响应时间
        int responseTime = 1000;
        //重试次数
        int numberOfRetries = 3;
    };

    explicit SettingDialog(QWidget *parent = nullptr);
    ~SettingDialog();

    //返回参数设置信息
    Settings  settings() const;

private slots:
    void on_btnApply_clicked();

private:
    Ui::SettingDialog *ui;
    Settings m_settings;
};

#endif // SETTINGDIALOG_H

settingdialog.cpp文件

#include "settingdialog.h"
以上是关于Qtmodbus之TCP模式读操作的主要内容,如果未能解决你的问题,请参考以下文章

Qtmodbus之串口模式读操作

Qtmodbus之串口模式写操作

JUC并发编程 共享模式之工具 JUC 读写锁 ReentrantReadWriteLock -- ReentrantReadWriteLock(不可重入锁)使用 & 注意事项

Python 之 Socket编程(TCP/UDP)

Python基础之文件操作

读构建之法 第一章:概论