【Qt】modbus之串口模式写操作
生活随笔
收集整理的這篇文章主要介紹了
【Qt】modbus之串口模式写操作
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
00. 目錄
文章目錄
- 00. 目錄
- 01. 概述
- 02. 開發環境
- 03. 寫Coils程序示例
- 04. 寫HoldingRegisters程序示例
- 05. 綜合示例
- 06. 程序下載
- 07. 附錄
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 serialportgreaterThan(QT_MAJOR_VERSION, 4): QT += widgetsCONFIG += 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.0SOURCES += \main.cpp \widget.cppHEADERS += \widget.h# Default rules for deployment. qnx: target.path = /tmp/$${TARGET}/bin else: unix:!android: target.path = /opt/$${TARGET}/bin !isEmpty(target.path): INSTALLS += target03. 寫Coils程序示例
widget.h文件
#ifndef WIDGET_H #define WIDGET_H#include <QWidget>//前向聲明 class QModbusClient; class QModbusReply;class Widget : public QWidget {Q_OBJECTpublic:Widget(QWidget *parent = nullptr);~Widget();private:QModbusClient *modbusDevice = nullptr;private slots:void onReadReady(); }; #endif // WIDGET_Hwidget.cpp文件
#include "widget.h" #include <QModbusRtuSerialMaster> #include <QModbusDataUnit> #include <QModbusReply> #include <QVariant> #include <QSerialPort> #include <QDebug>//構造函數 Widget::Widget(QWidget *parent): QWidget(parent) {//1. 創建QModbusDevice對象modbusDevice = new QModbusRtuSerialMaster;//2. 如果處于連接狀態,則斷開連接if (modbusDevice->state() == QModbusDevice::ConnectedState){//斷開連接設備modbusDevice->disconnectDevice();}//3. 設置串口相關參數//設置串口信息modbusDevice->setConnectionParameter(QModbusDevice::SerialPortNameParameter, QVariant("COM3"));//設置校驗 無校驗modbusDevice->setConnectionParameter(QModbusDevice::SerialParityParameter, QSerialPort::NoParity);//設置波特率modbusDevice->setConnectionParameter(QModbusDevice::SerialBaudRateParameter, QSerialPort::Baud19200);//設置停止位modbusDevice->setConnectionParameter(QModbusDevice::SerialStopBitsParameter, QSerialPort::OneStop);//設置數據位modbusDevice->setConnectionParameter(QModbusDevice::SerialDataBitsParameter, QSerialPort::Data8);//4. 設置其他信息//設置超時時間modbusDevice->setTimeout(1000); //1秒//設置失敗重試次數modbusDevice->setNumberOfRetries(3);//5. 連接到設備bool ok = modbusDevice->connectDevice();if (!ok){qDebug() << "連接到串口失敗: " << modbusDevice->errorString();}else{qDebug() << "連接到串口成功";}//6. 發送寫請求//從地址0開始寫10個保持寄存器的值//QModbusDataUnit writeData(QModbusDataUnit::HoldingRegisters, 0, 10);//從地址0開始寫10個線圈的值QModbusDataUnit writeData(QModbusDataUnit::Coils, 0, 10);for (int i = 0; i < writeData.valueCount(); i++){writeData.setValue(i, (i * i) % 2);}qDebug() << "發送的數據為: " << writeData.values();QModbusReply* reply = modbusDevice->sendWriteRequest(writeData, 1);if (reply){if (!reply->isFinished()){//接收響應信息connect(reply, &QModbusReply::finished, this, [this, reply](){if (reply->error() == QModbusDevice::ProtocolError){//接收到的響應信息是協議錯誤qDebug() << "寫入數據錯誤:" << reply->errorString();}else if (reply->error() != QModbusDevice::NoError){//接收到的響應消息是其它錯誤qDebug() << "寫入數據錯誤: " << reply->errorString();}else{//接收到的消息沒有錯誤 一般沒有必要解析響應消息const QModbusDataUnit data = reply->result();qDebug() << "消息數據個數:" << data.valueCount() << " :" << data.values();}reply->deleteLater();});}else{//發送沒有響應數據//broadcast replies return immediatelyreply->deleteLater();}}else{qDebug() << "sendWriteRequest Error: " << reply->errorString();}//7. 發送讀取數據請求//從地址0開始讀取10個保持寄存器的值//QModbusDataUnit data(QModbusDataUnit::HoldingRegisters, 0, 10);//從地址0開始讀取10個離散輸入量的值//QModbusDataUnit data(QModbusDataUnit::DiscreteInputs, 0, 10);//QModbusDataUnit::Coils 從地址0開始讀取10個線圈值QModbusDataUnit data(QModbusDataUnit::Coils, 0, 10);//QModbusDataUnit::InputRegisters 從地址0開始讀取10個輸入寄存器的值//QModbusDataUnit data(QModbusDataUnit::InputRegisters, 0, 10);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 immediatelydelete reply;}}}//析構函數 Widget::~Widget() {if (modbusDevice){modbusDevice->disconnectDevice();}delete modbusDevice; }//準備讀取數據的槽函數 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();}//刪除replyreply->deleteLater(); }執行結果
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 exited with code 004. 寫HoldingRegisters程序示例
widget.h文件
#ifndef WIDGET_H #define WIDGET_H#include <QWidget>//前向聲明 class QModbusClient; class QModbusReply;class Widget : public QWidget {Q_OBJECTpublic:Widget(QWidget *parent = nullptr);~Widget();private:QModbusClient *modbusDevice = nullptr;private slots:void onReadReady(); }; #endif // WIDGET_Hwidget.cpp文件
#include "widget.h" #include <QModbusRtuSerialMaster> #include <QModbusDataUnit> #include <QModbusReply> #include <QVariant> #include <QSerialPort> #include <QDebug>//構造函數 Widget::Widget(QWidget *parent): QWidget(parent) {//1. 創建QModbusDevice對象modbusDevice = new QModbusRtuSerialMaster;//2. 如果處于連接狀態,則斷開連接if (modbusDevice->state() == QModbusDevice::ConnectedState){//斷開連接設備modbusDevice->disconnectDevice();}//3. 設置串口相關參數//設置串口信息modbusDevice->setConnectionParameter(QModbusDevice::SerialPortNameParameter, QVariant("COM3"));//設置校驗 無校驗modbusDevice->setConnectionParameter(QModbusDevice::SerialParityParameter, QSerialPort::NoParity);//設置波特率modbusDevice->setConnectionParameter(QModbusDevice::SerialBaudRateParameter, QSerialPort::Baud19200);//設置停止位modbusDevice->setConnectionParameter(QModbusDevice::SerialStopBitsParameter, QSerialPort::OneStop);//設置數據位modbusDevice->setConnectionParameter(QModbusDevice::SerialDataBitsParameter, QSerialPort::Data8);//4. 設置其他信息//設置超時時間modbusDevice->setTimeout(1000); //1秒//設置失敗重試次數modbusDevice->setNumberOfRetries(3);//5. 連接到設備bool ok = modbusDevice->connectDevice();if (!ok){qDebug() << "連接到串口失敗: " << modbusDevice->errorString();}else{qDebug() << "連接到串口成功";}//6. 發送寫請求//從地址0開始寫10個保持寄存器的值QModbusDataUnit writeData(QModbusDataUnit::HoldingRegisters, 0, 10);for (int i = 0; i < writeData.valueCount(); i++){writeData.setValue(i, i * i);}qDebug() << "發送的數據為: " << writeData.values();QModbusReply* reply = modbusDevice->sendWriteRequest(writeData, 1);if (reply){if (!reply->isFinished()){//接收響應信息connect(reply, &QModbusReply::finished, this, [this, reply](){if (reply->error() == QModbusDevice::ProtocolError){//接收到的響應信息是協議錯誤qDebug() << "寫入數據錯誤:" << reply->errorString();}else if (reply->error() != QModbusDevice::NoError){//接收到的響應消息是其它錯誤qDebug() << "寫入數據錯誤: " << reply->errorString();}else{//接收到的消息沒有錯誤 一般沒有必要解析響應消息const QModbusDataUnit data = reply->result();qDebug() << "消息數據個數:" << data.valueCount() << " :" << data.values();}reply->deleteLater();});}else{//發送沒有響應數據//broadcast replies return immediatelyreply->deleteLater();}}else{qDebug() << "sendWriteRequest Error: " << reply->errorString();}//7. 發送讀取數據請求//從地址0開始讀取10個保持寄存器的值QModbusDataUnit data(QModbusDataUnit::HoldingRegisters, 0, 10);//從地址0開始讀取10個離散輸入量的值//QModbusDataUnit data(QModbusDataUnit::DiscreteInputs, 0, 10);//QModbusDataUnit::Coils 從地址0開始讀取10個線圈值//QModbusDataUnit data(QModbusDataUnit::Coils, 0, 10);//QModbusDataUnit::InputRegisters 從地址0開始讀取10個輸入寄存器的值//QModbusDataUnit data(QModbusDataUnit::InputRegisters, 0, 10);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 immediatelydelete reply;}}}//析構函數 Widget::~Widget() {if (modbusDevice){modbusDevice->disconnectDevice();}delete modbusDevice; }//準備讀取數據的槽函數 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();}//刪除replyreply->deleteLater(); }執行結果
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 exited with code 005. 綜合示例
程序界面
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-J2Oiv0Co-1621479015968)(assets/image-20210520104421393.png)]
settingdialog.h文件
#ifndef SETTINGDIALOG_H #define SETTINGDIALOG_H#include <QDialog> #include <QtSerialPort>namespace Ui { class SettingDialog; }//串口設置相關類 class SettingDialog : public QDialog {Q_OBJECTpublic: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_Hsettingdialog.cpp文件
#include "settingdialog.h" #include "ui_settingdialog.h"//構造函數 SettingDialog::SettingDialog(QWidget *parent) :QDialog(parent),ui(new Ui::SettingDialog) {ui->setupUi(this);//設置默認參數信息ui->serialNameLineEdit->setText(tr("COM3"));ui->parityComboBox->setCurrentIndex(0);ui->baudComboBox->setCurrentText(QString::number(m_settings.baud));ui->dataBitComboBox->setCurrentText(QString::number(m_settings.dataBits));ui->stopBitComboBox->setCurrentText(QString::number(m_settings.stopBits));ui->spinBoxTimeOut->setValue(m_settings.responseTime);ui->spinBoxRetry->setValue(m_settings.numberOfRetries);}//析構函數 SettingDialog::~SettingDialog() {delete ui; }//返回參數信息 SettingDialog::Settings SettingDialog::settings() const {return m_settings; }//引用按鈕槽函數 void SettingDialog::on_btnApply_clicked() {m_settings.serialName = ui->serialNameLineEdit->text();m_settings.parity = ui->parityComboBox->currentText().toInt();m_settings.baud = ui->baudComboBox->currentText().toInt();m_settings.dataBits = ui->dataBitComboBox->currentText().toInt();m_settings.stopBits = ui->stopBitComboBox->currentText().toInt();m_settings.responseTime = ui->spinBoxTimeOut->value();m_settings.numberOfRetries = ui->spinBoxRetry->value();//隱藏參數設置對話框hide(); }mainwindow.h文件
#ifndef MAINWINDOW_H #define MAINWINDOW_H#include <QMainWindow> #include <QModbusDataUnit> #include "writeregistermodel.h"QT_BEGIN_NAMESPACEnamespace Ui { class MainWindow; }class SettingDialog; class QModbusClient; class QModbusReply;QT_END_NAMESPACEclass MainWindow : public QMainWindow {Q_OBJECTpublic:MainWindow(QWidget *parent = nullptr);~MainWindow();//信號與槽進行關聯void initActions();//讀請求數據包封裝QModbusDataUnit readRequest() const;//寫請求數據包封裝QModbusDataUnit writeRequest() const;private slots:void onConnectButtonClicked();void onConnectTypeChanged(int);void onModbusStateChanged(int state);void onReadButtonClicked();void onReadReady();void onWriteButtonClicked();void onReadWriteButtonClicked();void onWriteTableChanged(int);private:Ui::MainWindow *ui = nullptr;SettingDialog *m_settingDialog = nullptr;QModbusClient *modbusDevice = nullptr;QModbusReply *reply = nullptr;WriteRegisterModel *writeModel = nullptr;}; #endif // MAINWINDOW_Hmainwindow.cpp文件
#include "mainwindow.h" #include "ui_mainwindow.h" #include <QMessageBox> #include "settingdialog.h" #include <QModbusRtuSerialMaster> #include <QModbusReply> #include <QStandardItemModel> #include <QModbusDataUnit>//連接類型枚舉變量 enum ModbusConnection {Serial,Tcp };MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), ui(new Ui::MainWindow) {ui->setupUi(this);//創建對象m_settingDialog = new SettingDialog(this);//初始化信號與槽initActions();//創建寫模型writeModel = new WriteRegisterModel(this);writeModel->setStartAddress(ui->sbWriteStartAddr->value());writeModel->setNumberOfValues(ui->cbWriteCount->currentText());//MVCui->treeViewWrite->setModel(writeModel);//隱藏第二列ui->treeViewWrite->hideColumn(2);connect(writeModel, &WriteRegisterModel::updateViewport,ui->treeViewWrite->viewport(), QOverload<>::of(&QWidget::update));//默認為串口連接方式ui->cbConnType->setCurrentIndex(0);onConnectTypeChanged(0);auto model = new QStandardItemModel(10, 1, this);for (int i = 0; i < 10; i++){model->setItem(i, new QStandardItem(QStringLiteral("%1").arg(i + 1)));}ui->cbWriteCount->setModel(model);ui->cbWriteCount->setCurrentText("10");connect(ui->cbWriteCount, &QComboBox::currentTextChanged,writeModel, &WriteRegisterModel::setNumberOfValues);auto valueChanged = QOverload<int>::of(&QSpinBox::valueChanged);connect(ui->sbWriteStartAddr, valueChanged, writeModel, &WriteRegisterModel::setStartAddress);connect(ui->sbWriteStartAddr, valueChanged, this, [this, model](int i){int lastIndex = 0;const int curIndex = ui->cbWriteCount->currentIndex();for (int j = 0; j < 10; j++){//設置使能if (j < (10 - i)){lastIndex = j;model->item(j)->setEnabled(true);}else{//設置禁用model->item(j)->setEnabled(false);}}if (curIndex > lastIndex){ui->cbWriteCount->setCurrentIndex(lastIndex);}}); }//析構函數 MainWindow::~MainWindow() {if (modbusDevice){modbusDevice->disconnectDevice();delete modbusDevice;}delete ui; }//信號與槽進行關聯s void MainWindow::initActions() {//使能部分功能ui->actionConnect->setEnabled(true);ui->actionDisconnect->setEnabled(false);ui->actionQuit->setEnabled(true);ui->actionOption->setEnabled(true);//禁用讀寫操作ui->btnRead->setEnabled(false);ui->btnWrite->setEnabled(false);ui->btnReadWrite->setEnabled(false);//信號與槽關聯connect(ui->btnConnect, &QPushButton::clicked,this, &MainWindow::onConnectButtonClicked);connect(ui->actionConnect, &QAction::triggered,this, &MainWindow::onConnectButtonClicked);connect(ui->actionDisconnect, &QAction::triggered,this, &MainWindow::onConnectButtonClicked);//讀操作按鈕槽函數關聯connect(ui->btnRead, &QPushButton::clicked,this, &MainWindow::onReadButtonClicked);connect(ui->btnWrite, &QPushButton::clicked,this, &MainWindow::onWriteButtonClicked);connect(ui->btnReadWrite, &QPushButton::clicked,this, &MainWindow::onReadWriteButtonClicked);connect(ui->cbRegisterType, QOverload<int>::of(&QComboBox::currentIndexChanged),this, &MainWindow::onWriteTableChanged);connect(ui->cbConnType, QOverload<int>::of(&QComboBox::currentIndexChanged),this, &MainWindow::onConnectTypeChanged);//退出菜單connect(ui->actionQuit, &QAction::triggered, this, &MainWindow::close);//顯示參數設置對話框connect(ui->actionOption, &QAction::triggered, m_settingDialog, &QDialog::show);connect(ui->actionAbout, &QAction::triggered, [=]() {QMessageBox::aboutQt(this, "About Qt");}); }//構建請求報文 QModbusDataUnit MainWindow::readRequest() const {//const auto type = static_cast<QModbusDataUnit::RegisterType>(ui->cbRegisterType->currentData().toInt());QModbusDataUnit::RegisterType type = QModbusDataUnit::Invalid;qDebug() << ui->cbRegisterType->currentText();if (ui->cbRegisterType->currentText() == QString("線圈")){type = QModbusDataUnit::Coils;}else if (ui->cbRegisterType->currentText() == QString("離散輸入")){type = QModbusDataUnit::DiscreteInputs;}else if (ui->cbRegisterType->currentText() == QString("輸入寄存器")){type = QModbusDataUnit::InputRegisters;}else if (ui->cbRegisterType->currentText() == QString("保持寄存器")){type = QModbusDataUnit::HoldingRegisters;}qDebug() << "請求報文類型: " << type;//獲取int startAddress = ui->spReadStartAddr->value();Q_ASSERT(startAddress >= 0 && startAddress < 10);quint16 numberOfEntries = ui->cbReadCount->currentText().toUShort();return QModbusDataUnit(type, startAddress, numberOfEntries); }//寫請求數據包封裝 QModbusDataUnit MainWindow::writeRequest() const {QModbusDataUnit::RegisterType type = QModbusDataUnit::Invalid;qDebug() << ui->cbRegisterType->currentText();if (ui->cbRegisterType->currentText() == QString("線圈")){type = QModbusDataUnit::Coils;}else if (ui->cbRegisterType->currentText() == QString("離散輸入")){type = QModbusDataUnit::DiscreteInputs;}else if (ui->cbRegisterType->currentText() == QString("輸入寄存器")){type = QModbusDataUnit::InputRegisters;}else if (ui->cbRegisterType->currentText() == QString("保持寄存器")){type = QModbusDataUnit::HoldingRegisters;}qDebug() << "請求報文類型: " << type;//獲取int startAddress = ui->sbWriteStartAddr->value();Q_ASSERT(startAddress >= 0 && startAddress < 10);quint16 numberOfEntries = ui->cbWriteCount->currentText().toUShort();//qDebug() << "Test: " << startAddress << " " << numberOfEntries;return QModbusDataUnit(type, startAddress, numberOfEntries); }//連接和斷開連接的槽函數 void MainWindow::onConnectButtonClicked() {if (!modbusDevice){return;}//清空狀態欄消息statusBar()->clearMessage();if (modbusDevice->state() != QModbusDevice::ConnectedState){auto type = static_cast<ModbusConnection>(ui->cbConnType->currentIndex());if (type == Serial){//設置串口連接信息modbusDevice->setConnectionParameter(QModbusDevice::SerialPortNameParameter,m_settingDialog->settings().serialName);modbusDevice->setConnectionParameter(QModbusDevice::SerialParityParameter,m_settingDialog->settings().parity);modbusDevice->setConnectionParameter(QModbusDevice::SerialBaudRateParameter,m_settingDialog->settings().baud);modbusDevice->setConnectionParameter(QModbusDevice::SerialDataBitsParameter,m_settingDialog->settings().dataBits);modbusDevice->setConnectionParameter(QModbusDevice::SerialStopBitsParameter,m_settingDialog->settings().stopBits);}else{//TCP連接信息}modbusDevice->setTimeout(m_settingDialog->settings().responseTime);modbusDevice->setNumberOfRetries(m_settingDialog->settings().numberOfRetries);if (!modbusDevice->connectDevice()){statusBar()->showMessage(tr("Connect failed..") + modbusDevice->errorString(), 5000);}else{statusBar()->showMessage(tr("Connect Successfully"), 5000);qDebug() << "連接OK";ui->actionConnect->setEnabled(false);ui->actionDisconnect->setEnabled(true);//使能讀寫操作ui->btnRead->setEnabled(true);ui->btnWrite->setEnabled(true);ui->btnReadWrite->setEnabled(true);}}else{//斷開連接modbusDevice->disconnectDevice();ui->actionConnect->setEnabled(true);ui->actionDisconnect->setDisabled(true);qDebug() << "斷開連接成功";//禁用讀寫操作ui->btnRead->setEnabled(false);ui->btnWrite->setEnabled(false);ui->btnReadWrite->setEnabled(false);} }//連接類型槽函數 TCP Serial void MainWindow::onConnectTypeChanged(int index) {//如果之前存在連接,則斷開連接,然后釋放內存if(modbusDevice){modbusDevice->disconnectDevice();delete modbusDevice;modbusDevice = nullptr;}auto type = static_cast<ModbusConnection>(index);if (type == Serial){modbusDevice = new QModbusRtuSerialMaster(this);qDebug() << "new QModbusRtuSerialMaster Ok";statusBar()->showMessage("new QModbusRtuSerialMaster Ok", 3000);}else if (type == Tcp){}else{statusBar()->showMessage("連接類型非法", 5000);}connect(modbusDevice, &QModbusClient::errorOccurred, [this](QModbusDevice::Error){statusBar()->showMessage(modbusDevice->errorString(), 5000);});if (!modbusDevice){//分配空間失敗ui->btnConnect->setDisabled(true);if (type == Serial){statusBar()->showMessage(tr("創建Modbus Master失敗"), 5000);}else{statusBar()->showMessage(tr("創建Modbus Client失敗"), 5000);}}else{connect(modbusDevice, &QModbusClient::stateChanged,this, &MainWindow::onModbusStateChanged);}}//Modbus狀態改變槽函數 void MainWindow::onModbusStateChanged(int state) {//判斷Modbus設備連接是否處于連接狀態bool connected = (state != QModbusDevice::UnconnectedState);ui->actionConnect->setEnabled(!connected);ui->actionDisconnect->setEnabled(connected);if (QModbusDevice::UnconnectedState == state){ui->btnConnect->setText(tr("Connect"));}else{ui->btnConnect->setText(tr("Disconnect"));} }//讀操作槽函數 void MainWindow::onReadButtonClicked() {if (!modbusDevice){return;}ui->textEditRead->clear();statusBar()->clearMessage();//發送請求報文數據auto *reply = modbusDevice->sendReadRequest(readRequest(), ui->sbServerAddr->value());if (reply){if (!reply->isFinished()){//完畢之后 自動觸發槽函數connect(reply, &QModbusReply::finished, this, &MainWindow::onReadReady);}else{//廣播消息 不需要返回響應delete reply;}}else{statusBar()->showMessage(tr("Read Error: ") + modbusDevice->errorString(), 5000);}}//讀取數據 void MainWindow::onReadReady() {auto reply = qobject_cast<QModbusReply*>(sender());if (!reply){return;}if (reply->error() == QModbusDevice::NoError){const QModbusDataUnit data = reply->result();for (int i = 0, total = (int)data.valueCount(); i < total; i++){const QString str = tr("Address: %1 Value: %2").arg(data.startAddress() + i).arg(QString::number(data.value(i), data.registerType() <= QModbusDataUnit::Coils ? 10 : 16));ui->textEditRead->append(str);}}else if (reply->error() == QModbusDevice::ProtocolError){statusBar()->showMessage(tr("Read response error: %1 (Modbus exception: 0x%2)").arg(reply->errorString()).arg(reply->rawResult().exceptionCode(), -1, 16), 5000);}else{statusBar()->showMessage(tr("Read response error: %1 (Code: 0x%2)").arg(reply->errorString()).arg(reply->error(), -1, 16), 5000);}//釋放內存reply->deleteLater(); }void MainWindow::onWriteButtonClicked() {if (!modbusDevice){return;}statusBar()->clearMessage();QModbusDataUnit writeData = writeRequest();QModbusDataUnit::RegisterType type = writeData.registerType();//qDebug() << "test: " << writeData.valueCount();for (int i = 0, total = (int)(writeData.valueCount()); i < total; i++){if (type == QModbusDataUnit::Coils){writeData.setValue(i, writeModel->m_coils[i + writeData.startAddress()]);}else{//qDebug() << "test: " << writeModel->m_holdingRegisters[i + writeData.startAddress()];writeData.setValue(i, writeModel->m_holdingRegisters[i + writeData.startAddress()]);}}qDebug() << "寫數據內容為:" << writeData.values();//發送請求報文數據auto *reply = modbusDevice->sendWriteRequest(writeData, ui->sbServerAddr->value());if (reply){if (!reply->isFinished()){//完畢之后 自動觸發槽函數connect(reply, &QModbusReply::finished, this, [this, reply]{if (reply->error() == QModbusDevice::ProtocolError){statusBar()->showMessage(tr("Write Protocaol response error: %1").arg(reply->errorString()), 5000);}else if (reply->error() != QModbusDevice::NoError){statusBar()->showMessage(tr("Write response error: %1").arg(reply->errorString()), 5000);}else{qDebug() << "寫響應的數據: " << reply->result().values();}reply->deleteLater();});}else{//廣播消息 不需要返回響應reply->deleteLater();}}else{statusBar()->showMessage(tr("Write Error: ") + modbusDevice->errorString(), 5000);} }//讀寫按鈕槽函數 void MainWindow::onReadWriteButtonClicked() {if (!modbusDevice){return;}statusBar()->clearMessage();QModbusDataUnit writeData = writeRequest();QModbusDataUnit::RegisterType type = writeData.registerType();//qDebug() << "test: " << writeData.valueCount();for (int i = 0, total = (int)(writeData.valueCount()); i < total; i++){if (type == QModbusDataUnit::Coils){writeData.setValue(i, writeModel->m_coils[i + writeData.startAddress()]);}else{//qDebug() << "test: " << writeModel->m_holdingRegisters[i + writeData.startAddress()];writeData.setValue(i, writeModel->m_holdingRegisters[i + writeData.startAddress()]);}}qDebug() << "寫數據內容為:" << writeData.values();//發送請求報文數據auto *reply = modbusDevice->sendReadWriteRequest(readRequest(), writeData, ui->sbServerAddr->value());if (reply){if (!reply->isFinished()){connect(reply, &QModbusReply::finished, this, &MainWindow::onReadReady);//完畢之后 自動觸發槽函數connect(reply, &QModbusReply::finished, this, [this, reply]{if (reply->error() == QModbusDevice::ProtocolError){statusBar()->showMessage(tr("Write Protocaol response error: %1").arg(reply->errorString()), 5000);}else if (reply->error() != QModbusDevice::NoError){statusBar()->showMessage(tr("Write response error: %1").arg(reply->errorString()), 5000);}else{qDebug() << "寫響應的數據: " << reply->result().values();}reply->deleteLater();});}else{//廣播消息 不需要返回響應reply->deleteLater();}}else{statusBar()->showMessage(tr("Write Error: ") + modbusDevice->errorString(), 5000);} }void MainWindow::onWriteTableChanged(int index) {const bool coilsOrHolding = index == 0 || index == 3;if (coilsOrHolding){ui->treeViewWrite->setColumnHidden(1, index != 0);ui->treeViewWrite->setColumnHidden(2, index != 3);ui->treeViewWrite->resizeColumnToContents(0);}ui->btnReadWrite->setEnabled(index == 3);ui->btnWrite->setEnabled(coilsOrHolding);ui->groupBox_2->setEnabled(coilsOrHolding); }writeregistermodel.h文件
#ifndef WRITEREGISTERMODEL_H #define WRITEREGISTERMODEL_H#include <QAbstractItemModel> #include <QBitArray> #include <QObject>class WriteRegisterModel : public QAbstractTableModel {Q_OBJECTpublic:WriteRegisterModel(QObject *parent = nullptr);int rowCount(const QModelIndex &parent = QModelIndex()) const override;int columnCount(const QModelIndex &parent = QModelIndex()) const override;QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;QVariant headerData(int section, Qt::Orientation orientation, int role) const override;bool setData(const QModelIndex &index, const QVariant &value, int role) override;Qt::ItemFlags flags(const QModelIndex &index) const override;public slots:void setStartAddress(int address);void setNumberOfValues(const QString &number);signals:void updateViewport();public:int m_number = 0;int m_address = 0;QBitArray m_coils;QVector<quint16> m_holdingRegisters; };#endif // WRITEREGISTERMODEL_Hwriteregistermodel.cpp文件
#include "writeregistermodel.h"enum { NumColumn = 0, CoilsColumn = 1, HoldingColumn = 2, ColumnCount = 3, RowCount = 10 };WriteRegisterModel::WriteRegisterModel(QObject *parent): QAbstractTableModel(parent),m_coils(RowCount, false), m_holdingRegisters(RowCount, 0u) { }int WriteRegisterModel::rowCount(const QModelIndex &/*parent*/) const {return RowCount; }int WriteRegisterModel::columnCount(const QModelIndex &/*parent*/) const {return ColumnCount; }QVariant WriteRegisterModel::data(const QModelIndex &index, int role) const {if (!index.isValid() || index.row() >= RowCount || index.column() >= ColumnCount)return QVariant();Q_ASSERT(m_coils.count() == RowCount);Q_ASSERT(m_holdingRegisters.count() == RowCount);if (index.column() == NumColumn && role == Qt::DisplayRole)return QString::number(index.row());if (index.column() == CoilsColumn && role == Qt::CheckStateRole) // coilsreturn m_coils.at(index.row()) ? Qt::Checked : Qt::Unchecked;if (index.column() == HoldingColumn && role == Qt::DisplayRole) // holding registersreturn QString("0x%1").arg(QString::number(m_holdingRegisters.at(index.row()), 16));return QVariant();}QVariant WriteRegisterModel::headerData(int section, Qt::Orientation orientation, int role) const {if (role != Qt::DisplayRole)return QVariant();if (orientation == Qt::Horizontal) {switch (section) {case NumColumn:return QStringLiteral("#");case CoilsColumn:return QStringLiteral("Coils ");case HoldingColumn:return QStringLiteral("Holding Registers");default:break;}}return QVariant(); }bool WriteRegisterModel::setData(const QModelIndex &index, const QVariant &value, int role) {if (!index.isValid() || index.row() >= RowCount || index.column() >= ColumnCount)return false;Q_ASSERT(m_coils.count() == RowCount);Q_ASSERT(m_holdingRegisters.count() == RowCount);if (index.column() == CoilsColumn && role == Qt::CheckStateRole) { // coilsauto s = static_cast<Qt::CheckState>(value.toUInt());s == Qt::Checked ? m_coils.setBit(index.row()) : m_coils.clearBit(index.row());emit dataChanged(index, index);return true;}if (index.column() == HoldingColumn && role == Qt::EditRole) { // holding registersbool result = false;quint16 newValue = value.toString().toUShort(&result, 16);if (result)m_holdingRegisters[index.row()] = newValue;emit dataChanged(index, index);return result;}return false; }Qt::ItemFlags WriteRegisterModel::flags(const QModelIndex &index) const {if (!index.isValid() || index.row() >= RowCount || index.column() >= ColumnCount)return QAbstractTableModel::flags(index);Qt::ItemFlags flags = QAbstractTableModel::flags(index);if ((index.row() < m_address) || (index.row() >= (m_address + m_number)))flags &= ~Qt::ItemIsEnabled;if (index.column() == CoilsColumn) // coilsreturn flags | Qt::ItemIsUserCheckable;if (index.column() == HoldingColumn) // holding registersreturn flags | Qt::ItemIsEditable;return flags; }void WriteRegisterModel::setStartAddress(int address) {m_address = address;emit updateViewport(); }void WriteRegisterModel::setNumberOfValues(const QString &number) {m_number = number.toInt();emit updateViewport(); }06. 程序下載
6.1 RTUMaster寫操作示例(一).rar
6.2 RTUMasterTest寫操作(二).rar
07. 附錄
7.1 Qt教程匯總
網址:https://dengjin.blog.csdn.net/article/details/115174639
總結
以上是生活随笔為你收集整理的【Qt】modbus之串口模式写操作的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【Qt】modbus之串口模式读操作
- 下一篇: 【Qt】modbus之TCP模式读操作