Qt技术|Mudbus TCP的使用示例

一、Mudbus TCP简介

        Modbus TCP 是 Modbus 协议的一个变种,它利用 TCP/IP 网络协议来传输 Modbus 消息。Modbus TCP 是一种广泛应用于工业自动化领域的通信协议,特别适用于远程监控和控制系统之间的数据交换。

  1. 基于 TCP/IP

    • Modbus TCP 基于标准的 TCP/IP 协议,可以在任何支持 TCP/IP 的网络上传输 Modbus 消息。
    • 使用 TCP/IP 协议提供了可靠的传输机制,包括错误检测和重传机制。
  2. 固定端口

    • Modbus TCP 通常使用固定的端口 502 进行通信。
    • 这使得 Modbus TCP 设备更容易在网络中被发现和配置。
  3. 简化协议

    • Modbus TCP 相比传统的 Modbus RTU 和 Modbus ASCII 协议更加简洁。
    • Modbus TCP 消息格式简化了原始的 Modbus 协议报头,仅保留了事务标识符、协议标识符、长度字段和单元标识符。
  4. 快速响应

    • Modbus TCP 的设计旨在减少网络延迟,提高响应速度。
    • 由于 Modbus TCP 消息格式更短,可以更快地进行处理。
  5. 易于集成

    • Modbus TCP 可以轻松地与其他基于 TCP/IP 的系统集成。
    • 支持多种操作系统和平台,便于开发和维护。

二、Mudbus TCP 在Qt中的使用

示例代码:

Modbus TCP Demo for Qt: 我的 Qt Modbus TCP Demo 当前存在一点问题,自动刷新和写入寄存器的效果冲突,但放在此处方便后续使用 (gitee.com)

 ***.pro

QT       += core gui  serialbus

slave.ui




slave.h

#ifndef SLAVE_H
#define SLAVE_H

#include <QWidget>
#include <QtSerialBus>
#include <QModbusDataUnit>
#include <QModbusClient>
#include <QString>
#include <QMessageBox>

namespace Ui {
class Slave;
}

class Slave : public QWidget
{
    Q_OBJECT

public:
    explicit Slave(QWidget *parent = nullptr);
    Slave(QString ip,int port);
    ~Slave();

    virtual void timerEvent(QTimerEvent *event);
signals:
private slots:

    void onReadyRead();
    void closeTCP();

    void on_btn_con_clicked();



    void on_btn_read_clicked();

    void on_btn_write_clicked();

private:
    QString ipNum;
    int portNum;
    QModbusTcpClient tcpClient;
    Ui::Slave *ui;
    void readU();
};

#endif // SLAVE_H

slave.cpp 

#include "slave.h"
#include "ui_slave.h"

Slave::Slave(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Slave)
{
    ui->setupUi(this);
//    this->ui->btn_read->setVisible(false);
//    this->ui->btn_write->hide();
}

Slave::Slave(QString ip, int port)
{
    this->ipNum = ip;
    this->portNum = port;
    this->setWindowTitle(" 从机- " + ip +" : "+ port);

}

Slave::~Slave()
{
    closeTCP();
    delete ui;
}

void Slave::timerEvent(QTimerEvent *event)
{
    on_btn_read_clicked();
}

void Slave::onReadyRead()
{
    auto reply = qobject_cast<QModbusReply *>(sender());
    if(reply->error()==QModbusDevice::NoError){
        qDebug()<<" 读取成功 ! ";
        reply->deleteLater();
    }else{
        qDebug()<<" 读取失败!  ";
        reply->deleteLater();
        return;
    }

    const QModbusDataUnit unit = reply->result();

    QString dataStr;
    for(int i=0;i<unit.valueCount();i++){
        dataStr += QString::number(unit.value(i))+" ";
        ui->tab_slave->setItem(i,0,new QTableWidgetItem(QString::number(unit.value(i))));
        ui->tab_master->setItem(i,0,new QTableWidgetItem(QString::number(unit.value(i))));
    }
    qDebug()<<dataStr;
}

void Slave::closeTCP()
{
    tcpClient.disconnectDevice();
    qDebug()<<" 连接已断开 ";
}

void Slave::on_btn_con_clicked()
{

    if(ui->lt_ipNum->text().isEmpty()||ui->lt_portNum->text().isEmpty()){
        QMessageBox errorDialog;
        errorDialog.setIcon(QMessageBox::Critical); // 设置图标为错误图标
        errorDialog.setWindowTitle("   错误提示   "); // 设置窗口标题
        errorDialog.setText("  请检查ip和port,不可为空    " ); // 设置错误信息文本
        errorDialog.exec(); // 显示错误提示窗口并等待用户关闭
        return;
    }else{
        if(tcpClient.state()==QModbusClient::ConnectedState){
            tcpClient.disconnectDevice();
        }
        this->ipNum=ui->lt_ipNum->text();
        this->portNum = ui->lt_portNum->text().toInt();
        this->setWindowTitle(" 从机-" + ipNum +":"+ QString::number(portNum));
    }

    tcpClient.setConnectionParameter(QModbusDevice::NetworkAddressParameter,ipNum);
    tcpClient.setConnectionParameter(QModbusDevice::NetworkPortParameter,portNum);
    tcpClient.setTimeout(300);
    if(!tcpClient.connectDevice()){
        QMessageBox errorDialog2;
        errorDialog2.setIcon(QMessageBox::Critical); // 设置图标为错误图标
        errorDialog2.setWindowTitle(" 错误提示 "); // 设置窗口标题
        errorDialog2.setText(" 连接失败,请检查ip和port,慢点点击 "); // 设置错误信息文本
        errorDialog2.exec(); // 显示错误提示窗口并等待用户关闭
        return;
    }
    else {
        qDebug()<<" 连接成功 ";
//        startTimer(1000);
        //         ui->tab_slave->clear();
        //         ui->tab_master->clear();

    }



}

void Slave::on_btn_read_clicked()
{
    QModbusDataUnit readUnit(QModbusDataUnit::HoldingRegisters,0,10);
    auto *reply = tcpClient.sendReadRequest(readUnit,1);
    connect( reply,&QModbusReply::finished,this,&Slave::onReadyRead);
}

void Slave::on_btn_write_clicked()
{
    QModbusDataUnit writeUnit(QModbusDataUnit::HoldingRegisters,0,10); // 创建数据单元
    //    int emptyFlag = 0;
    for (int i = 0; i < ui->tab_master->rowCount(); ++i) {
        //        if(ui->tab_master->item(i,0)->text().isEmpty()){
        //           emptyFlag++;

        //        }
        writeUnit.setValue(i,ui->tab_master->item(i,0)->text().toInt());// 设置值
    }
    //    if(emptyFlag>0){
    //        QMessageBox::StandardButton reply;
    //        reply = QMessageBox::question(nullptr, "警告", "寄存器编辑器包含空选项是否要写入从机寄存器",
    //                                      QMessageBox::Yes | QMessageBox::No);

    //        if (reply == QMessageBox::No) {
    //            return;
    //        }
    //    }

    if (auto *reply = tcpClient.sendWriteRequest(writeUnit, 1)) { // 发送写请求
        if (!reply->isFinished()) {
            connect(reply, &QModbusReply::finished, this, [this, reply]() {
                if (reply->error() == QModbusDevice::NoError) {
                    qDebug() << " 修改成功 ";
                    on_btn_read_clicked();
                } else {
                    qDebug() << " 修改失败: " << reply->errorString();
                }
                reply->deleteLater();
            });
        } else {
            // 请求已完成但未完成
            delete reply; // 删除回复对象
        }
    } else {
        qDebug() << "发送请求失败:" << tcpClient.errorString();
    }
}

三、对Moudbus TCP的补充

//常用操作
#include <QCoreApplication>
#include <QModbusDataUnit>
#include <QModbusReply>
#include <QModbusTcpClient>
#include <QDebug>

// 定义 Modbus TCP 客户端
class ModbusTcpClient : public QModbusTcpClient {
public:
    ModbusTcpClient(QObject *parent = nullptr) : QModbusTcpClient(parent) {}

    // 读取保持寄存器
    bool readHoldingRegisters(quint16 startAddress, quint16 quantity, QModbusDataUnit &result) {
        QModbusDataUnit request(QModbusDataUnit::HoldingRegisters, startAddress, quantity);
        QModbusReply *reply = sendReadRequest(request, 1); // 设备ID为1

        if (!reply) {
            qDebug() << "Failed to send read request.";
            return false;
        }

        QObject::connect(reply, &QModbusReply::finished, this, [this, &result, reply]() {
            if (reply->error() == QModbusDevice::NoError) {
                result = reply->result();
            } else {
                qDebug() << "Error reading data from Modbus server:" << reply->errorString();
            }
            delete reply;
        });

        return true;
    }

    // 写入单个线圈
    bool writeSingleCoil(quint16 coilAddress, bool state) {
        QModbusDataUnit request(QModbusDataUnit:: Coil, coilAddress, 1);
        request.setValue(0, state ? 0xFF00 : 0x0000);
        QModbusReply *reply = sendWriteRequest(request, 1); // 设备ID为1

        if (!reply) {
            qDebug() << "Failed to send write request.";
            return false;
        }

        QObject::connect(reply, &QModbusReply::finished, this, [reply]() {
            if (reply->error() != QModbusDevice::NoError) {
                qDebug() << "Error writing data to Modbus server:" << reply->errorString();
            }
            delete reply;
        });

        return true;
    }

    // 写入多个保持寄存器
    bool writeMultipleHoldingRegisters(quint16 startAddress, const QList<quint16> &values) {
        QModbusDataUnit request(QModbusDataUnit::HoldingRegisters, startAddress, values.size());
        for (int i = 0; i < values.size(); ++i) {
            request.setValue(i, values[i]);
        }
        QModbusReply *reply = sendWriteRequest(request, 1); // 设备ID为1

        if (!reply) {
            qDebug() << "Failed to send write request.";
            return false;
        }

        QObject::connect(reply, &QModbusReply::finished, this, [reply]() {
            if (reply->error() != QModbusDevice::NoError) {
                qDebug() << "Error writing data to Modbus server:" << reply->errorString();
            }
            delete reply;
        });

        return true;
    }
};

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    // 创建 Modbus TCP 客户端
    ModbusTcpClient client;

    // 设置服务器地址和端口
    client.setServerAddress(QHostAddress("192.168.1.100"), 502);

    // 连接到 Modbus 服务器
    if (!client.connectDevice()) {
        qDebug() << "Failed to connect to Modbus server.";
        return -1;
    }

    // 定义要读取的寄存器的起始地址和数量
    quint16 startAddress = 100;
    quint16 quantity = 10;

    // 创建一个数据单元来请求读取保持寄存器
    QModbusDataUnit result;

    // 读取保持寄存器
    if (client.readHoldingRegisters(startAddress, quantity, result)) {
        qDebug() << "Received data from Modbus server:";
        for (quint16 i = 0; i < result.quantityOfValues(); ++i) {
            qDebug() << "Register" << startAddress + i << ":" << result.value(i);
        }
    }

    // 写入单个线圈
    if (client.writeSingleCoil(200, true)) {
        qDebug() << "Wrote single coil to Modbus server.";
    }

    // 写入多个保持寄存器
    QList<quint16> values = {1, 2, 3, 4, 5};
    if (client.writeMultipleHoldingRegisters(300, values)) {
        qDebug() << "Wrote multiple holding registers to Modbus server.";
    }

    // 主事件循环
    a.exec();

    return 0;
}

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值