• 【Qt】modbus之串口模式写操作


    00. 目录

    文章目录

    ​ ​00. 目录​​
    ​ ​01. 概述​​
    ​ ​02. 开发环境​​
    ​ ​03. 写Coils程序示例​​
    ​ ​04. 写HoldingRegisters程序示例​​
    ​ ​05. 综合示例​​
     

    01. 概述
    Qt中几个常用的串口modbus类

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

    02. 开发环境
    ​Windows系统​:Windows10

    ​Qt版本​:Qt5.15或者Qt6

    Pro配置文件如下
     

    1. QT += core gui serialbus serialport
    2. greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
    3. CONFIG += c++11
    4. # You can make your code fail to compile if it uses deprecated APIs.
    5. # In order to do so, uncomment the following line.
    6. #DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
    7. SOURCES += \
    8. main.cpp \
    9. widget.cpp
    10. HEADERS += \
    11. widget.h
    12. # Default rules for deployment.
    13. qnx: target.path = /tmp/$${TARGET}/bin
    14. else: unix:!android: target.path = /opt/$${TARGET}/bin
    15. !isEmpty(target.path): INSTALLS += target

    03. 写Coils程序示例
    ​widget.h文件

    1. #ifndef WIDGET_H
    2. #define WIDGET_H
    3. #include
    4. //前向声明
    5. class QModbusClient;
    6. class QModbusReply;
    7. class Widget : public QWidget
    8. {
    9. Q_OBJECT
    10. public:
    11. Widget(QWidget *parent = nullptr);
    12. ~Widget();
    13. private:
    14. QModbusClient *modbusDevice = nullptr;
    15. private slots:
    16. void onReadReady();
    17. };
    18. #endif // WIDGET_H

    ​widget.cpp文件

    1. #include "widget.h"
    2. #include <QModbusRtuSerialMaster>
    3. #include <QModbusDataUnit>
    4. #include <QModbusReply>
    5. #include <QVariant>
    6. #include <QSerialPort>
    7. #include <QDebug>
    8. //构造函数
    9. Widget::Widget(QWidget *parent)
    10. : QWidget(parent)
    11. {
    12. //1. 创建QModbusDevice对象
    13. modbusDevice = new QModbusRtuSerialMaster;
    14. //2. 如果处于连接状态,则断开连接
    15. if (modbusDevice->state() == QModbusDevice::ConnectedState)
    16. {
    17. //断开连接设备
    18. modbusDevice->disconnectDevice();
    19. }
    20. //3. 设置串口相关参数
    21. //设置串口信息
    22. modbusDevice->setConnectionParameter(QModbusDevice::SerialPortNameParameter, QVariant("COM3"));
    23. //设置校验 无校验
    24. modbusDevice->setConnectionParameter(QModbusDevice::SerialParityParameter, QSerialPort::NoParity);
    25. //设置波特率
    26. modbusDevice->setConnectionParameter(QModbusDevice::SerialBaudRateParameter, QSerialPort::Baud19200);
    27. //设置停止位
    28. modbusDevice->setConnectionParameter(QModbusDevice::SerialStopBitsParameter, QSerialPort::OneStop);
    29. //设置数据位
    30. modbusDevice->setConnectionParameter(QModbusDevice::SerialDataBitsParameter, QSerialPort::Data8);
    31. //4. 设置其他信息
    32. //设置超时时间
    33. modbusDevice->setTimeout(1000); //1
    34. //设置失败重试次数
    35. modbusDevice->setNumberOfRetries(3);
    36. //5. 连接到设备
    37. bool ok = modbusDevice->connectDevice();
    38. if (!ok)
    39. {
    40. qDebug() << "连接到串口失败: " << modbusDevice->errorString();
    41. }
    42. else
    43. {
    44. qDebug() << "连接到串口成功";
    45. }
    46. //6. 发送写请求
    47. //从地址0开始写10个保持寄存器的值
    48. //QModbusDataUnit writeData(QModbusDataUnit::HoldingRegisters, 0, 10);
    49. //从地址0开始写10个线圈的值
    50. QModbusDataUnit writeData(QModbusDataUnit::Coils, 0, 10);
    51. for (int i = 0; i < writeData.valueCount(); i++)
    52. {
    53. writeData.setValue(i, (i * i) % 2);
    54. }
    55. qDebug() << "发送的数据为: " << writeData.values();
    56. QModbusReply* reply = modbusDevice->sendWriteRequest(writeData, 1);
    57. if (reply)
    58. {
    59. if (!reply->isFinished())
    60. {
    61. //接收响应信息
    62. connect(reply, &QModbusReply::finished, this, [this, reply](){
    63. if (reply->error() == QModbusDevice::ProtocolError)
    64. {
    65. //接收到的响应信息是协议错误
    66. qDebug() << "写入数据错误:" << reply->errorString();
    67. }
    68. else if (reply->error() != QModbusDevice::NoError)
    69. {
    70. //接收到的响应消息是其它错误
    71. qDebug() << "写入数据错误: " << reply->errorString();
    72. }
    73. else
    74. {
    75. //接收到的消息没有错误 一般没有必要解析响应消息
    76. const QModbusDataUnit data = reply->result();
    77. qDebug() << "消息数据个数:" << data.valueCount() << " :" << data.values();
    78. }
    79. reply->deleteLater();
    80. });
    81. }
    82. else
    83. {
    84. //发送没有响应数据
    85. //broadcast replies return immediately
    86. reply->deleteLater();
    87. }
    88. }
    89. else
    90. {
    91. qDebug() << "sendWriteRequest Error: " << reply->errorString();
    92. }
    93. //7. 发送读取数据请求
    94. //从地址0开始读取10个保持寄存器的值
    95. //QModbusDataUnit data(QModbusDataUnit::HoldingRegisters, 0, 10);
    96. //从地址0开始读取10个离散输入量的值
    97. //QModbusDataUnit data(QModbusDataUnit::DiscreteInputs, 0, 10);
    98. //QModbusDataUnit::Coils 从地址0开始读取10个线圈值
    99. QModbusDataUnit data(QModbusDataUnit::Coils, 0, 10);
    100. //QModbusDataUnit::InputRegisters 从地址0开始读取10个输入寄存器的值
    101. //QModbusDataUnit data(QModbusDataUnit::InputRegisters, 0, 10);
    102. reply = modbusDevice->sendReadRequest(data, 0x1);
    103. if (nullptr == reply)
    104. {
    105. qDebug() << "发送请求数据失败: " << modbusDevice->errorString();
    106. }
    107. else
    108. {
    109. if (!reply->isFinished())
    110. {
    111. connect(reply, &QModbusReply::finished, this, &Widget::onReadReady);
    112. }
    113. else
    114. {
    115. //broadcast replies return immediately
    116. delete reply;
    117. }
    118. }
    119. }
    120. //析构函数
    121. Widget::~Widget()
    122. {
    123. if (modbusDevice)
    124. {
    125. modbusDevice->disconnectDevice();
    126. }
    127. delete modbusDevice;
    128. }
    129. //准备读取数据的槽函数
    130. void Widget::onReadReady()
    131. {
    132. auto reply = qobject_cast<QModbusReply*>(sender());
    133. if (nullptr == reply)
    134. {
    135. return;
    136. }
    137. //判断是否出错
    138. if (reply->error() == QModbusDevice::NoError)
    139. {
    140. //读取响应数据
    141. const QModbusDataUnit responseData = reply->result();
    142. qDebug() << "读到数据为:" << responseData.values();
    143. }
    144. else if (reply->error() == QModbusDevice::ProtocolError)
    145. {
    146. qDebug() << "Read response Protocol error: " << reply->errorString();
    147. }
    148. else
    149. {
    150. qDebug() << "Read response Error: " << reply->errorString();
    151. }
    152. //删除reply
    153. reply->deleteLater();
    154. }

    执行结果​

    20:32:27: Starting D:\ProgramData\Qt\build-Test-Desktop_Qt_5_15_2_MinGW_32_bit-Debug\debug\Test.exe ...
    连接到串口成功
    发送的数据为:  QVector(0, 1, 0, 1, 0, 1, 0, 1, 0, 1)
    消息数据个数: 10  : QVector(0, 1, 0, 1, 0, 1, 0, 1, 0, 1)
    读到数据为: QVector(0, 1, 0, 1, 0, 1, 0, 1, 0, 1)
    20:34:02: D:\ProgramData\Qt\build-Test-Desktop_Qt_5_15_2_MinGW_32_bit-Debug\debug\Test.exe 
    04. 写HoldingRegisters程序示例
    ​widget.h文件

    1. #ifndef WIDGET_H
    2. #define WIDGET_H
    3. #include
    4. //前向声明
    5. class QModbusClient;
    6. class QModbusReply;
    7. class Widget : public QWidget
    8. {
    9. Q_OBJECT
    10. public:
    11. Widget(QWidget *parent = nullptr);
    12. ~Widget();
    13. private:
    14. QModbusClient *modbusDevice = nullptr;
    15. private slots:
    16. void onReadReady();
    17. };
    18. #endif // WIDGET_H

    ​widget.cpp文件

    1. #include "widget.h"
    2. #include <QModbusRtuSerialMaster>
    3. #include <QModbusDataUnit>
    4. #include <QModbusReply>
    5. #include <QVariant>
    6. #include <QSerialPort>
    7. #include <QDebug>
    8. //构造函数
    9. Widget::Widget(QWidget *parent)
    10. : QWidget(parent)
    11. {
    12. //1. 创建QModbusDevice对象
    13. modbusDevice = new QModbusRtuSerialMaster;
    14. //2. 如果处于连接状态,则断开连接
    15. if (modbusDevice->state() == QModbusDevice::ConnectedState)
    16. {
    17. //断开连接设备
    18. modbusDevice->disconnectDevice();
    19. }
    20. //3. 设置串口相关参数
    21. //设置串口信息
    22. modbusDevice->setConnectionParameter(QModbusDevice::SerialPortNameParameter, QVariant("COM3"));
    23. //设置校验 无校验
    24. modbusDevice->setConnectionParameter(QModbusDevice::SerialParityParameter, QSerialPort::NoParity);
    25. //设置波特率
    26. modbusDevice->setConnectionParameter(QModbusDevice::SerialBaudRateParameter, QSerialPort::Baud19200);
    27. //设置停止位
    28. modbusDevice->setConnectionParameter(QModbusDevice::SerialStopBitsParameter, QSerialPort::OneStop);
    29. //设置数据位
    30. modbusDevice->setConnectionParameter(QModbusDevice::SerialDataBitsParameter, QSerialPort::Data8);
    31. //4. 设置其他信息
    32. //设置超时时间
    33. modbusDevice->setTimeout(1000); //1
    34. //设置失败重试次数
    35. modbusDevice->setNumberOfRetries(3);
    36. //5. 连接到设备
    37. bool ok = modbusDevice->connectDevice();
    38. if (!ok)
    39. {
    40. qDebug() << "连接到串口失败: " << modbusDevice->errorString();
    41. }
    42. else
    43. {
    44. qDebug() << "连接到串口成功";
    45. }
    46. //6. 发送写请求
    47. //从地址0开始写10个保持寄存器的值
    48. QModbusDataUnit writeData(QModbusDataUnit::HoldingRegisters, 0, 10);
    49. for (int i = 0; i < writeData.valueCount(); i++)
    50. {
    51. writeData.setValue(i, i * i);
    52. }
    53. qDebug() << "发送的数据为: " << writeData.values();
    54. QModbusReply* reply = modbusDevice->sendWriteRequest(writeData, 1);
    55. if (reply)
    56. {
    57. if (!reply->isFinished())
    58. {
    59. //接收响应信息
    60. connect(reply, &QModbusReply::finished, this, [this, reply](){
    61. if (reply->error() == QModbusDevice::ProtocolError)
    62. {
    63. //接收到的响应信息是协议错误
    64. qDebug() << "写入数据错误:" << reply->errorString();
    65. }
    66. else if (reply->error() != QModbusDevice::NoError)
    67. {
    68. //接收到的响应消息是其它错误
    69. qDebug() << "写入数据错误: " << reply->errorString();
    70. }
    71. else
    72. {
    73. //接收到的消息没有错误 一般没有必要解析响应消息
    74. const QModbusDataUnit data = reply->result();
    75. qDebug() << "消息数据个数:" << data.valueCount() << " :" << data.values();
    76. }
    77. reply->deleteLater();
    78. });
    79. }
    80. else
    81. {
    82. //发送没有响应数据
    83. //broadcast replies return immediately
    84. reply->deleteLater();
    85. }
    86. }
    87. else
    88. {
    89. qDebug() << "sendWriteRequest Error: " << reply->errorString();
    90. }
    91. //7. 发送读取数据请求
    92. //从地址0开始读取10个保持寄存器的值
    93. QModbusDataUnit data(QModbusDataUnit::HoldingRegisters, 0, 10);
    94. //从地址0开始读取10个离散输入量的值
    95. //QModbusDataUnit data(QModbusDataUnit::DiscreteInputs, 0, 10);
    96. //QModbusDataUnit::Coils 从地址0开始读取10个线圈值
    97. //QModbusDataUnit data(QModbusDataUnit::Coils, 0, 10);
    98. //QModbusDataUnit::InputRegisters 从地址0开始读取10个输入寄存器的值
    99. //QModbusDataUnit data(QModbusDataUnit::InputRegisters, 0, 10);
    100. reply = modbusDevice->sendReadRequest(data, 0x1);
    101. if (nullptr == reply)
    102. {
    103. qDebug() << "发送请求数据失败: " << modbusDevice->errorString();
    104. }
    105. else
    106. {
    107. if (!reply->isFinished())
    108. {
    109. connect(reply, &QModbusReply::finished, this, &Widget::onReadReady);
    110. }
    111. else
    112. {
    113. //broadcast replies return immediately
    114. delete reply;
    115. }
    116. }
    117. }
    118. //析构函数
    119. Widget::~Widget()
    120. {
    121. if (modbusDevice)
    122. {
    123. modbusDevice->disconnectDevice();
    124. }
    125. delete modbusDevice;
    126. }
    127. //准备读取数据的槽函数
    128. void Widget::onReadReady()
    129. {
    130. auto reply = qobject_cast<QModbusReply*>(sender());
    131. if (nullptr == reply)
    132. {
    133. return;
    134. }
    135. //判断是否出错
    136. if (reply->error() == QModbusDevice::NoError)
    137. {
    138. //读取响应数据
    139. const QModbusDataUnit responseData = reply->result();
    140. qDebug() << "读到数据为:" << responseData.values();
    141. }
    142. else if (reply->error() == QModbusDevice::ProtocolError)
    143. {
    144. qDebug() << "Read response Protocol error: " << reply->errorString();
    145. }
    146. else
    147. {
    148. qDebug() << "Read response Error: " << reply->errorString();
    149. }
    150. //删除reply
    151. reply->deleteLater();
    152. }

    执行结果​

    20:23:23: Starting D:\ProgramData\Qt\build-Test-Desktop_Qt_5_15_2_MinGW_32_bit-Debug\debug\Test.exe ...
    连接到串口成功
    发送的数据为:  QVector(0, 1, 4, 9, 16, 25, 36, 49, 64, 81)
    消息数据个数: 10  : QVector(0, 1, 4, 9, 16, 25, 36, 49, 64, 81)
    读到数据为: QVector(0, 1, 4, 9, 16, 25, 36, 49, 64, 81)
    20:23:29: D:\ProgramData\Qt\build-Test-Desktop_Qt_5_15_2_MinGW_32_bit-Debug\debug\Test.exe ex

    05. 综合示例
    程序界面

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

    ​settingdialog.h文件

    1. #ifndef SETTINGDIALOG_H
    2. #define SETTINGDIALOG_H
    3. #include <QDialog>
    4. #include <QtSerialPort>
    5. namespace Ui {
    6. class SettingDialog;
    7. }
    8. //串口设置相关类
    9. class SettingDialog : public QDialog
    10. {
    11. Q_OBJECT
    12. public:
    13. struct Settings
    14. {
    15. //串口名
    16. QString serialName = "COM3";
    17. //校验位
    18. int parity = QSerialPort::NoParity;
    19. //波特率
    20. int baud = QSerialPort::Baud19200;
    21. //数据位
    22. int dataBits = QSerialPort::Data8;
    23. //停止位
    24. int stopBits = QSerialPort::OneStop;
    25. //响应时间
    26. int responseTime = 1000;
    27. //重试次数
    28. int numberOfRetries = 3;
    29. };
    30. explicit SettingDialog(QWidget *parent = nullptr);
    31. ~SettingDialog();
    32. //返回参数设置信息
    33. Settings settings() const;
    34. private slots:
    35. void on_btnApply_clicked();
    36. private:
    37. Ui::SettingDialog *ui;
    38. Settings m_settings;
    39. };
    40. #endif // SETTINGDIALOG_H

    ​settingdialog.cpp文件

    1. #include "settingdialog.h"
    2. #include "ui_settingdialog.h"
    3. //构造函数
    4. SettingDialog::SettingDialog(QWidget *parent) :
    5. QDialog(parent),
    6. ui(new Ui::SettingDialog)
    7. {
    8. ui->setupUi(this);
    9. //设置默认参数信息
    10. ui->serialNameLineEdit->setText(tr("COM3"));
    11. ui->parityComboBox->setCurrentIndex(0);
    12. ui->baudComboBox->setCurrentText(QString::number(m_settings.baud));
    13. ui->dataBitComboBox->setCurrentText(QString::number(m_settings.dataBits));
    14. ui->stopBitComboBox->setCurrentText(QString::number(m_settings.stopBits));
    15. ui->spinBoxTimeOut->setValue(m_settings.responseTime);
    16. ui->spinBoxRetry->setValue(m_settings.numberOfRetries);
    17. }
    18. //析构函数
    19. SettingDialog::~SettingDialog()
    20. {
    21. delete ui;
    22. }
    23. //返回参数信息
    24. SettingDialog::Settings SettingDialog::settings() const
    25. {
    26. return m_settings;
    27. }
    28. //引用按钮槽函数
    29. void SettingDialog::on_btnApply_clicked()
    30. {
    31. m_settings.serialName = ui->serialNameLineEdit->text();
    32. m_settings.parity = ui->parityComboBox->currentText().toInt();
    33. m_settings.baud = ui->baudComboBox->currentText().toInt();
    34. m_settings.dataBits = ui->dataBitComboBox->currentText().toInt();
    35. m_settings.stopBits = ui->stopBitComboBox->currentText().toInt();
    36. m_settings.responseTime = ui->spinBoxTimeOut->value();
    37. m_settings.numberOfRetries = ui->spinBoxRetry->value();
    38. //隐藏参数设置对话框
    39. hide();
    40. }

    ​mainwindow.h文件

    1. #ifndef MAINWINDOW_H
    2. #define MAINWINDOW_H
    3. #include
    4. #include
    5. #include "writeregistermodel.h"
    6. QT_BEGIN_NAMESPACE
    7. namespace Ui
    8. {
    9. class MainWindow;
    10. }
    11. class SettingDialog;
    12. class QModbusClient;
    13. class QModbusReply;
    14. QT_END_NAMESPACE
    15. class MainWindow : public QMainWindow
    16. {
    17. Q_OBJECT
    18. public:
    19. MainWindow(QWidget *parent = nullptr);
    20. ~MainWindow();
    21. //信号与槽进行关联
    22. void initActions();
    23. //读请求数据包封装
    24. QModbusDataUnit readRequest() const;
    25. //写请求数据包封装
    26. QModbusDataUnit writeRequest() const;
    27. private slots:
    28. void onConnectButtonClicked();
    29. void onConnectTypeChanged(int);
    30. void onModbusStateChanged(int state);
    31. void onReadButtonClicked();
    32. void onReadReady();
    33. void onWriteButtonClicked();
    34. void onReadWriteButtonClicked();
    35. void onWriteTableChanged(int);
    36. private:
    37. Ui::MainWindow *ui = nullptr;
    38. SettingDialog *m_settingDialog = nullptr;
    39. QModbusClient *modbusDevice = nullptr;
    40. QModbusReply *reply = nullptr;
    41. WriteRegisterModel *writeModel = nullptr;
    42. };
    43. #endif // MAINWINDOW_H

    mainwindow.cpp文件

    1. #include "mainwindow.h"
    2. #include "ui_mainwindow.h"
    3. #include <QMessageBox>
    4. #include "settingdialog.h"
    5. #include <QModbusRtuSerialMaster>
    6. #include <QModbusReply>
    7. #include <QStandardItemModel>
    8. #include <QModbusDataUnit>
    9. //连接类型枚举变量
    10. enum ModbusConnection {
    11. Serial,
    12. Tcp
    13. };
    14. MainWindow::MainWindow(QWidget *parent)
    15. : QMainWindow(parent)
    16. , ui(new Ui::MainWindow)
    17. {
    18. ui->setupUi(this);
    19. //创建对象
    20. m_settingDialog = new SettingDialog(this);
    21. //初始化信号与槽
    22. initActions();
    23. //创建写模型
    24. writeModel = new WriteRegisterModel(this);
    25. writeModel->setStartAddress(ui->sbWriteStartAddr->value());
    26. writeModel->setNumberOfValues(ui->cbWriteCount->currentText());
    27. //MVC
    28. ui->treeViewWrite->setModel(writeModel);
    29. //隐藏第二列
    30. ui->treeViewWrite->hideColumn(2);
    31. connect(writeModel, &WriteRegisterModel::updateViewport,
    32. ui->treeViewWrite->viewport(), QOverload<>::of(&QWidget::update));
    33. //默认为串口连接方式
    34. ui->cbConnType->setCurrentIndex(0);
    35. onConnectTypeChanged(0);
    36. auto model = new QStandardItemModel(10, 1, this);
    37. for (int i = 0; i < 10; i++)
    38. {
    39. model->setItem(i, new QStandardItem(QStringLiteral("%1").arg(i + 1)));
    40. }
    41. ui->cbWriteCount->setModel(model);
    42. ui->cbWriteCount->setCurrentText("10");
    43. connect(ui->cbWriteCount, &QComboBox::currentTextChanged,
    44. writeModel, &WriteRegisterModel::setNumberOfValues);
    45. auto valueChanged = QOverload<int>::of(&QSpinBox::valueChanged);
    46. connect(ui->sbWriteStartAddr, valueChanged, writeModel, &WriteRegisterModel::setStartAddress);
    47. connect(ui->sbWriteStartAddr, valueChanged, this, [this, model](int i){
    48. int lastIndex = 0;
    49. const int curIndex = ui->cbWriteCount->currentIndex();
    50. for (int j = 0; j < 10; j++)
    51. {
    52. //设置使能
    53. if (j < (10 - i))
    54. {
    55. lastIndex = j;
    56. model->item(j)->setEnabled(true);
    57. }
    58. else
    59. {
    60. //设置禁用
    61. model->item(j)->setEnabled(false);
    62. }
    63. }
    64. if (curIndex > lastIndex)
    65. {
    66. ui->cbWriteCount->setCurrentIndex(lastIndex);
    67. }
    68. });
    69. }
    70. //析构函数
    71. MainWindow::~MainWindow()
    72. {
    73. if (modbusDevice)
    74. {
    75. modbusDevice->disconnectDevice();
    76. delete modbusDevice;
    77. }
    78. delete ui;
    79. }
    80. //信号与槽进行关联s
    81. void MainWindow::initActions()
    82. {
    83. //使能部分功能
    84. ui->actionConnect->setEnabled(true);
    85. ui->actionDisconnect->setEnabled(false);
    86. ui->actionQuit->setEnabled(true);
    87. ui->actionOption->setEnabled(true);
    88. //禁用读写操作
    89. ui->btnRead->setEnabled(false);
    90. ui->btnWrite->setEnabled(false);
    91. ui->btnReadWrite->setEnabled(false);
    92. //信号与槽关联
    93. connect(ui->btnConnect, &QPushButton::clicked,
    94. this, &MainWindow::onConnectButtonClicked);
    95. connect(ui->actionConnect, &QAction::triggered,
    96. this, &MainWindow::onConnectButtonClicked);
    97. connect(ui->actionDisconnect, &QAction::triggered,
    98. this, &MainWindow::onConnectButtonClicked);
    99. //读操作按钮槽函数关联
    100. connect(ui->btnRead, &QPushButton::clicked,
    101. this, &MainWindow::onReadButtonClicked);
    102. connect(ui->btnWrite, &QPushButton::clicked,
    103. this, &MainWindow::onWriteButtonClicked);
    104. connect(ui->btnReadWrite, &QPushButton::clicked,
    105. this, &MainWindow::onReadWriteButtonClicked);
    106. connect(ui->cbRegisterType, QOverload<int>::of(&QComboBox::currentIndexChanged),
    107. this, &MainWindow::onWriteTableChanged);
    108. connect(ui->cbConnType, QOverload<int>::of(&QComboBox::currentIndexChanged),
    109. this, &MainWindow::onConnectTypeChanged);
    110. //退出菜单
    111. connect(ui->actionQuit, &QAction::triggered, this, &MainWindow::close);
    112. //显示参数设置对话框
    113. connect(ui->actionOption, &QAction::triggered, m_settingDialog, &QDialog::show);
    114. connect(ui->actionAbout, &QAction::triggered, [=]() {
    115. QMessageBox::aboutQt(this, "About Qt");
    116. });
    117. }
    118. //构建请求报文
    119. QModbusDataUnit MainWindow::readRequest() const
    120. {
    121. //const auto type = static_cast<QModbusDataUnit::RegisterType>(ui->cbRegisterType->currentData().toInt());
    122. QModbusDataUnit::RegisterType type = QModbusDataUnit::Invalid;
    123. qDebug() << ui->cbRegisterType->currentText();
    124. if (ui->cbRegisterType->currentText() == QString("线圈"))
    125. {
    126. type = QModbusDataUnit::Coils;
    127. }
    128. else if (ui->cbRegisterType->currentText() == QString("离散输入"))
    129. {
    130. type = QModbusDataUnit::DiscreteInputs;
    131. }
    132. else if (ui->cbRegisterType->currentText() == QString("输入寄存器"))
    133. {
    134. type = QModbusDataUnit::InputRegisters;
    135. }
    136. else if (ui->cbRegisterType->currentText() == QString("保持寄存器"))
    137. {
    138. type = QModbusDataUnit::HoldingRegisters;
    139. }
    140. qDebug() << "请求报文类型: " << type;
    141. //获取
    142. int startAddress = ui->spReadStartAddr->value();
    143. Q_ASSERT(startAddress >= 0 && startAddress < 10);
    144. quint16 numberOfEntries = ui->cbReadCount->currentText().toUShort();
    145. return QModbusDataUnit(type, startAddress, numberOfEntries);
    146. }
    147. //写请求数据包封装
    148. QModbusDataUnit MainWindow::writeRequest() const
    149. {
    150. QModbusDataUnit::RegisterType type = QModbusDataUnit::Invalid;
    151. qDebug() << ui->cbRegisterType->currentText();
    152. if (ui->cbRegisterType->currentText() == QString("线圈"))
    153. {
    154. type = QModbusDataUnit::Coils;
    155. }
    156. else if (ui->cbRegisterType->currentText() == QString("离散输入"))
    157. {
    158. type = QModbusDataUnit::DiscreteInputs;
    159. }
    160. else if (ui->cbRegisterType->currentText() == QString("输入寄存器"))
    161. {
    162. type = QModbusDataUnit::InputRegisters;
    163. }
    164. else if (ui->cbRegisterType->currentText() == QString("保持寄存器"))
    165. {
    166. type = QModbusDataUnit::HoldingRegisters;
    167. }
    168. qDebug() << "请求报文类型: " << type;
    169. //获取
    170. int startAddress = ui->sbWriteStartAddr->value();
    171. Q_ASSERT(startAddress >= 0 && startAddress < 10);
    172. quint16 numberOfEntries = ui->cbWriteCount->currentText().toUShort();
    173. //qDebug() << "Test: " << startAddress << " " << numberOfEntries;
    174. return QModbusDataUnit(type, startAddress, numberOfEntries);
    175. }
    176. //连接和断开连接的槽函数
    177. void MainWindow::onConnectButtonClicked()
    178. {
    179. if (!modbusDevice)
    180. {
    181. return;
    182. }
    183. //清空状态栏消息
    184. statusBar()->clearMessage();
    185. if (modbusDevice->state() != QModbusDevice::ConnectedState)
    186. {
    187. auto type = static_cast<ModbusConnection>(ui->cbConnType->currentIndex());
    188. if (type == Serial)
    189. {
    190. //设置串口连接信息
    191. modbusDevice->setConnectionParameter(QModbusDevice::SerialPortNameParameter,
    192. m_settingDialog->settings().serialName);
    193. modbusDevice->setConnectionParameter(QModbusDevice::SerialParityParameter,
    194. m_settingDialog->settings().parity);
    195. modbusDevice->setConnectionParameter(QModbusDevice::SerialBaudRateParameter,
    196. m_settingDialog->settings().baud);
    197. modbusDevice->setConnectionParameter(QModbusDevice::SerialDataBitsParameter,
    198. m_settingDialog->settings().dataBits);
    199. modbusDevice->setConnectionParameter(QModbusDevice::SerialStopBitsParameter,
    200. m_settingDialog->settings().stopBits);
    201. }
    202. else
    203. {
    204. //TCP连接信息
    205. }
    206. modbusDevice->setTimeout(m_settingDialog->settings().responseTime);
    207. modbusDevice->setNumberOfRetries(m_settingDialog->settings().numberOfRetries);
    208. if (!modbusDevice->connectDevice())
    209. {
    210. statusBar()->showMessage(tr("Connect failed..") + modbusDevice->errorString(), 5000);
    211. }
    212. else
    213. {
    214. statusBar()->showMessage(tr("Connect Successfully"), 5000);
    215. qDebug() << "连接OK";
    216. ui->actionConnect->setEnabled(false);
    217. ui->actionDisconnect->setEnabled(true);
    218. //使能读写操作
    219. ui->btnRead->setEnabled(true);
    220. ui->btnWrite->setEnabled(true);
    221. ui->btnReadWrite->setEnabled(true);
    222. }
    223. }
    224. else
    225. {
    226. //断开连接
    227. modbusDevice->disconnectDevice();
    228. ui->actionConnect->setEnabled(true);
    229. ui->actionDisconnect->setDisabled(true);
    230. qDebug() << "断开连接成功";
    231. //禁用读写操作
    232. ui->btnRead->setEnabled(false);
    233. ui->btnWrite->setEnabled(false);
    234. ui->btnReadWrite->setEnabled(false);
    235. }
    236. }
    237. //连接类型槽函数 TCP Serial
    238. void MainWindow::onConnectTypeChanged(int index)
    239. {
    240. //如果之前存在连接,则断开连接,然后释放内存
    241. if(modbusDevice)
    242. {
    243. modbusDevice->disconnectDevice();
    244. delete modbusDevice;
    245. modbusDevice = nullptr;
    246. }
    247. auto type = static_cast<ModbusConnection>(index);
    248. if (type == Serial)
    249. {
    250. modbusDevice = new QModbusRtuSerialMaster(this);
    251. qDebug() << "new QModbusRtuSerialMaster Ok";
    252. statusBar()->showMessage("new QModbusRtuSerialMaster Ok", 3000);
    253. }
    254. else if (type == Tcp)
    255. {
    256. }
    257. else
    258. {
    259. statusBar()->showMessage("连接类型非法", 5000);
    260. }
    261. connect(modbusDevice, &QModbusClient::errorOccurred, [this](QModbusDevice::Error){
    262. statusBar()->showMessage(modbusDevice->errorString(), 5000);
    263. });
    264. if (!modbusDevice)
    265. {
    266. //分配空间失败
    267. ui->btnConnect->setDisabled(true);
    268. if (type == Serial)
    269. {
    270. statusBar()->showMessage(tr("创建Modbus Master失败"), 5000);
    271. }
    272. else
    273. {
    274. statusBar()->showMessage(tr("创建Modbus Client失败"), 5000);
    275. }
    276. }
    277. else
    278. {
    279. connect(modbusDevice, &QModbusClient::stateChanged,
    280. this, &MainWindow::onModbusStateChanged);
    281. }
    282. }
    283. //Modbus状态改变槽函数
    284. void MainWindow::onModbusStateChanged(int state)
    285. {
    286. //判断Modbus设备连接是否处于连接状态
    287. bool connected = (state != QModbusDevice::UnconnectedState);
    288. ui->actionConnect->setEnabled(!connected);
    289. ui->actionDisconnect->setEnabled(connected);
    290. if (QModbusDevice::UnconnectedState == state)
    291. {
    292. ui->btnConnect->setText(tr("Connect"));
    293. }
    294. else
    295. {
    296. ui->btnConnect->setText(tr("Disconnect"));
    297. }
    298. }
    299. //读操作槽函数
    300. void MainWindow::onReadButtonClicked()
    301. {
    302. if (!modbusDevice)
    303. {
    304. return;
    305. }
    306. ui->textEditRead->clear();
    307. statusBar()->clearMessage();
    308. //发送请求报文数据
    309. auto *reply = modbusDevice->sendReadRequest(readRequest(), ui->sbServerAddr->value());
    310. if (reply)
    311. {
    312. if (!reply->isFinished())
    313. {
    314. //完毕之后 自动触发槽函数
    315. connect(reply, &QModbusReply::finished, this, &MainWindow::onReadReady);
    316. }
    317. else
    318. {
    319. //广播消息 不需要返回响应
    320. delete reply;
    321. }
    322. }
    323. else
    324. {
    325. statusBar()->showMessage(tr("Read Error: ") + modbusDevice->errorString(), 5000);
    326. }
    327. }
    328. //读取数据
    329. void MainWindow::onReadReady()
    330. {
    331. auto reply = qobject_cast<QModbusReply*>(sender());
    332. if (!reply)
    333. {
    334. return;
    335. }
    336. if (reply->error() == QModbusDevice::NoError)
    337. {
    338. const QModbusDataUnit data = reply->result();
    339. for (int i = 0, total = (int)data.valueCount(); i < total; i++)
    340. {
    341. const QString str = tr("Address: %1 Value: %2").arg(data.startAddress() + i)
    342. .arg(QString::number(data.value(i), data.registerType() <= QModbusDataUnit::Coils ? 10 : 16));
    343. ui->textEditRead->append(str);
    344. }
    345. }
    346. else if (reply->error() == QModbusDevice::ProtocolError)
    347. {
    348. statusBar()->showMessage(tr("Read response error: %1 (Modbus exception: 0x%2)").
    349. arg(reply->errorString()).
    350. arg(reply->rawResult().exceptionCode(), -1, 16), 5000);
    351. }
    352. else
    353. {
    354. statusBar()->showMessage(tr("Read response error: %1 (Code: 0x%2)").
    355. arg(reply->errorString()).
    356. arg(reply->error(), -1, 16), 5000);
    357. }
    358. //释放内存
    359. reply->deleteLater();
    360. }
    361. void MainWindow::onWriteButtonClicked()
    362. {
    363. if (!modbusDevice)
    364. {
    365. return;
    366. }
    367. statusBar()->clearMessage();
    368. QModbusDataUnit writeData = writeRequest();
    369. QModbusDataUnit::RegisterType type = writeData.registerType();
    370. //qDebug() << "test: " << writeData.valueCount();
    371. for (int i = 0, total = (int)(writeData.valueCount()); i < total; i++)
    372. {
    373. if (type == QModbusDataUnit::Coils)
    374. {
    375. writeData.setValue(i, writeModel->m_coils[i + writeData.startAddress()]);
    376. }
    377. else
    378. {
    379. //qDebug() << "test: " << writeModel->m_holdingRegisters[i + writeData.startAddress()];
    380. writeData.setValue(i, writeModel->m_holdingRegisters[i + writeData.startAddress()]);
    381. }
    382. }
    383. qDebug() << "写数据内容为:" << writeData.values();
    384. //发送请求报文数据
    385. auto *reply = modbusDevice->sendWriteRequest(writeData, ui->sbServerAddr->value());
    386. if (reply)
    387. {
    388. if (!reply->isFinished())
    389. {
    390. //完毕之后 自动触发槽函数
    391. connect(reply, &QModbusReply::finished, this, [this, reply]{
    392. if (reply->error() == QModbusDevice::ProtocolError)
    393. {
    394. statusBar()->showMessage(tr("Write Protocaol response error: %1").arg(reply->errorString()), 5000);
    395. }
    396. else if (reply->error() != QModbusDevice::NoError)
    397. {
    398. statusBar()->showMessage(tr("Write response error: %1").arg(reply->errorString()), 5000);
    399. }
    400. else
    401. {
    402. qDebug() << "写响应的数据: " << reply->result().values();
    403. }
    404. reply->deleteLater();
    405. });
    406. }
    407. else
    408. {
    409. //广播消息 不需要返回响应
    410. reply->deleteLater();
    411. }
    412. }
    413. else
    414. {
    415. statusBar()->showMessage(tr("Write Error: ") + modbusDevice->errorString(), 5000);
    416. }
    417. }
    418. //读写按钮槽函数
    419. void MainWindow::onReadWriteButtonClicked()
    420. {
    421. if (!modbusDevice)
    422. {
    423. return;
    424. }
    425. statusBar()->clearMessage();
    426. QModbusDataUnit writeData = writeRequest();
    427. QModbusDataUnit::RegisterType type = writeData.registerType();
    428. //qDebug() << "test: " << writeData.valueCount();
    429. for (int i = 0, total = (int)(writeData.valueCount()); i < total; i++)
    430. {
    431. if (type == QModbusDataUnit::Coils)
    432. {
    433. writeData.setValue(i, writeModel->m_coils[i + writeData.startAddress()]);
    434. }
    435. else
    436. {
    437. //qDebug() << "test: " << writeModel->m_holdingRegisters[i + writeData.startAddress()];
    438. writeData.setValue(i, writeModel->m_holdingRegisters[i + writeData.startAddress()]);
    439. }
    440. }
    441. qDebug() << "写数据内容为:" << writeData.values();
    442. //发送请求报文数据
    443. auto *reply = modbusDevice->sendReadWriteRequest(readRequest(), writeData, ui->sbServerAddr->value());
    444. if (reply)
    445. {
    446. if (!reply->isFinished())
    447. {
    448. connect(reply, &QModbusReply::finished, this, &MainWindow::onReadReady);
    449. //完毕之后 自动触发槽函数
    450. connect(reply, &QModbusReply::finished, this, [this, reply]{
    451. if (reply->error() == QModbusDevice::ProtocolError)
    452. {
    453. statusBar()->showMessage(tr("Write Protocaol response error: %1").arg(reply->errorString()), 5000);
    454. }
    455. else if (reply->error() != QModbusDevice::NoError)
    456. {
    457. statusBar()->showMessage(tr("Write response error: %1").arg(reply->errorString()), 5000);
    458. }
    459. else
    460. {
    461. qDebug() << "写响应的数据: " << reply->result().values();
    462. }
    463. reply->deleteLater();
    464. });
    465. }
    466. else
    467. {
    468. //广播消息 不需要返回响应
    469. reply->deleteLater();
    470. }
    471. }
    472. else
    473. {
    474. statusBar()->showMessage(tr("Write Error: ") + modbusDevice->errorString(), 5000);
    475. }
    476. }
    477. void MainWindow::onWriteTableChanged(int index)
    478. {
    479. const bool coilsOrHolding = index == 0 || index == 3;
    480. if (coilsOrHolding)
    481. {
    482. ui->treeViewWrite->setColumnHidden(1, index != 0);
    483. ui->treeViewWrite->setColumnHidden(2, index != 3);
    484. ui->treeViewWrite->resizeColumnToContents(0);
    485. }
    486. ui->btnReadWrite->setEnabled(index == 3);
    487. ui->btnWrite->setEnabled(coilsOrHolding);
    488. ui->groupBox_2->setEnabled(coilsOrHolding);
    489. }

    ​writeregistermodel.h文件

    1. #ifndef WRITEREGISTERMODEL_H
    2. #define WRITEREGISTERMODEL_H
    3. #include
    4. #include
    5. #include
    6. class WriteRegisterModel : public QAbstractTableModel
    7. {
    8. Q_OBJECT
    9. public:
    10. WriteRegisterModel(QObject *parent = nullptr);
    11. int rowCount(const QModelIndex &parent = QModelIndex()) const override;
    12. int columnCount(const QModelIndex &parent = QModelIndex()) const override;
    13. QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
    14. QVariant headerData(int section, Qt::Orientation orientation, int role) const override;
    15. bool setData(const QModelIndex &index, const QVariant &value, int role) override;
    16. Qt::ItemFlags flags(const QModelIndex &index) const override;
    17. public slots:
    18. void setStartAddress(int address);
    19. void setNumberOfValues(const QString &number);
    20. signals:
    21. void updateViewport();
    22. public:
    23. int m_number = 0;
    24. int m_address = 0;
    25. QBitArray m_coils;
    26. QVector m_holdingRegisters;
    27. };
    28. #endif // WRITEREGISTERMODEL_H

    ​writeregistermodel.cpp文件

    1. #include "writeregistermodel.h"
    2. enum { NumColumn = 0, CoilsColumn = 1, HoldingColumn = 2, ColumnCount = 3, RowCount = 10 };
    3. WriteRegisterModel::WriteRegisterModel(QObject *parent)
    4. : QAbstractTableModel(parent),
    5. m_coils(RowCount, false), m_holdingRegisters(RowCount, 0u)
    6. {
    7. }
    8. int WriteRegisterModel::rowCount(const QModelIndex &/*parent*/) const
    9. {
    10. return RowCount;
    11. }
    12. int WriteRegisterModel::columnCount(const QModelIndex &/*parent*/) const
    13. {
    14. return ColumnCount;
    15. }
    16. QVariant WriteRegisterModel::data(const QModelIndex &index, int role) const
    17. {
    18. if (!index.isValid() || index.row() >= RowCount || index.column() >= ColumnCount)
    19. return QVariant();
    20. Q_ASSERT(m_coils.count() == RowCount);
    21. Q_ASSERT(m_holdingRegisters.count() == RowCount);
    22. if (index.column() == NumColumn && role == Qt::DisplayRole)
    23. return QString::number(index.row());
    24. if (index.column() == CoilsColumn && role == Qt::CheckStateRole) // coils
    25. return m_coils.at(index.row()) ? Qt::Checked : Qt::Unchecked;
    26. if (index.column() == HoldingColumn && role == Qt::DisplayRole) // holding registers
    27. return QString("0x%1").arg(QString::number(m_holdingRegisters.at(index.row()), 16));
    28. return QVariant();
    29. }
    30. QVariant WriteRegisterModel::headerData(int section, Qt::Orientation orientation, int role) const
    31. {
    32. if (role != Qt::DisplayRole)
    33. return QVariant();
    34. if (orientation == Qt::Horizontal) {
    35. switch (section) {
    36. case NumColumn:
    37. return QStringLiteral("#");
    38. case CoilsColumn:
    39. return QStringLiteral("Coils ");
    40. case HoldingColumn:
    41. return QStringLiteral("Holding Registers");
    42. default:
    43. break;
    44. }
    45. }
    46. return QVariant();
    47. }
    48. bool WriteRegisterModel::setData(const QModelIndex &index, const QVariant &value, int role)
    49. {
    50. if (!index.isValid() || index.row() >= RowCount || index.column() >= ColumnCount)
    51. return false;
    52. Q_ASSERT(m_coils.count() == RowCount);
    53. Q_ASSERT(m_holdingRegisters.count() == RowCount);
    54. if (index.column() == CoilsColumn && role == Qt::CheckStateRole) { // coils
    55. auto s = static_cast<Qt::CheckState>(value.toUInt());
    56. s == Qt::Checked ? m_coils.setBit(index.row()) : m_coils.clearBit(index.row());
    57. emit dataChanged(index, index);
    58. return true;
    59. }
    60. if (index.column() == HoldingColumn && role == Qt::EditRole) { // holding registers
    61. bool result = false;
    62. quint16 newValue = value.toString().toUShort(&result, 16);
    63. if (result)
    64. m_holdingRegisters[index.row()] = newValue;
    65. emit dataChanged(index, index);
    66. return result;
    67. }
    68. return false;
    69. }
    70. Qt::ItemFlags WriteRegisterModel::flags(const QModelIndex &index) const
    71. {
    72. if (!index.isValid() || index.row() >= RowCount || index.column() >= ColumnCount)
    73. return QAbstractTableModel::flags(index);
    74. Qt::ItemFlags flags = QAbstractTableModel::flags(index);
    75. if ((index.row() < m_address) || (index.row() >= (m_address + m_number)))
    76. flags &= ~Qt::ItemIsEnabled;
    77. if (index.column() == CoilsColumn) // coils
    78. return flags | Qt::ItemIsUserCheckable;
    79. if (index.column() == HoldingColumn) // holding registers
    80. return flags | Qt::ItemIsEditable;
    81. return flags;
    82. }
    83. void WriteRegisterModel::setStartAddress(int address)
    84. {
    85. m_address = address;
    86. emit updateViewport();
    87. }
    88. void WriteRegisterModel::setNumberOfValues(const QString &number)
    89. {
    90. m_number = number.toInt();
    91. emit updateViewport();
    92. }

  • 相关阅读:
    Linux用户管理
    7、Jedis测试
    【图灵MySQL】Explain详解与索引最佳实践
    pt30redis数据类型
    Vue首屏优化方案
    【执行数据库操作时Tomcat报错但不影响使用】error testWhileIdle is true, validationQuery not set
    谷歌紧急更新,Chrome 今年第二个零日漏洞曝光
    (附源码)计算机毕业设计SSM教师职称资料管理系统
    基于.Net 的 AvaloniUI 多媒体播放器方案汇总
    【20221103】【每日一题】二叉搜索树中的众数
  • 原文地址:https://blog.csdn.net/delphigbg/article/details/127799464