From a0e561ff05695e7196dd7990be63d2b6c15a4271 Mon Sep 17 00:00:00 2001 From: lipengpeng Date: Wed, 30 Jul 2025 09:28:03 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A4=87=E4=BB=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- modbus.pro.user | 2 +- widget.cpp | 272 ++++++++++++++++++++++++++++++------------------ widget.h | 46 +++++++- widget.ui | 74 ++++++------- 4 files changed, 251 insertions(+), 143 deletions(-) diff --git a/modbus.pro.user b/modbus.pro.user index 02776fd..c57183c 100644 --- a/modbus.pro.user +++ b/modbus.pro.user @@ -1,6 +1,6 @@ - + EnvironmentId diff --git a/widget.cpp b/widget.cpp index 250dfb5..2c2d57d 100644 --- a/widget.cpp +++ b/widget.cpp @@ -1,3 +1,13 @@ +/******************************* + * Copyright (C) 2025. + * + * File Name: widget.cpp + * Description: 显示与交互界面配置 + * Others: + * Version: 1.0 + * Author: lipengpeng + * Date: 2025-7-23 + *******************************/ #include "widget.h" #include "ui_widget.h" #include @@ -9,85 +19,104 @@ #include #include +/** + * @brief Widget 构造函数 + * @param parent 父窗口指针 + * + * 初始化用户界面、Modbus对象和串口通信对象。 + * 配置串口参数并连接信号槽。 + * 检查可用串口并填充到下拉框。 + */ Widget::Widget(QWidget *parent) : QWidget(parent), - ui(new Ui::Widget) + ui_(new Ui::Widget) { - modbus = new MyModbus(); - ui->setupUi(this); - setFixedSize(700,500); - serialComm = new SerialCommunicator(this); // 初始化串口通信类 + modbus_ = new MyModbus(); + ui_->setupUi(this); + setFixedSize(700,500); //设置窗口界面大小 + serialComm_ = new SerialCommunicator(this); // 初始化串口通信类 // 配置串口参数 - serialComm->setMaxRetry(3); // 最大重发3次 - serialComm->setRecvTimeout(50); // 接收超时50ms - serialComm->setResendTimeout(1000); // 重发间隔1s + serialComm_->setMaxRetry(3); // 最大重发3次 + serialComm_->setRecvTimeout(50); // 接收超时50ms + serialComm_->setResendTimeout(1000); // 重发间隔1s // 连接信号槽(串口->界面) //当串口接收到完整数据后,SerialCommunicator会发出dataReceived信号 - connect(serialComm, &SerialCommunicator::dataReceived, this, &Widget::onSerialDataReceived); + connect(serialComm_, &SerialCommunicator::dataReceived, this, &Widget::onSerialDataReceived); //当串口状态改变时,SerialCommunicator会发出statusChanged信号 - connect(serialComm, &SerialCommunicator::statusChanged, this, &Widget::onSerialStatusChanged); + connect(serialComm_, &SerialCommunicator::statusChanged, this, &Widget::onSerialStatusChanged); //当发送数据超过超时重发次数后仍未收到数据,SerialCommunicator会发出timeoutOccurred信号 - connect(serialComm, &SerialCommunicator::timeoutOccurred, this, &Widget::onSerialTimeout); + connect(serialComm_, &SerialCommunicator::timeoutOccurred, this, &Widget::onSerialTimeout); //当串口连接出现错误时(例如串口突然断开),SerialCommunicator会发出physicalDisconnected信号 - connect(serialComm, &SerialCommunicator::physicalDisconnected, this, [=](){ - ui->btnConnect->setText("连接"); - ui->btn_read->setEnabled(false); - ui->pushWrite->setEnabled(false); + connect(serialComm_, &SerialCommunicator::physicalDisconnected, this, [=](){ + ui_->btnConnect->setText("连接"); + ui_->btn_read->setEnabled(false); + ui_->pushWrite->setEnabled(false); QMessageBox::warning(this, "警告", "串口已断开连接"); }); //设置默认波特率、数据位、校验位 - ui->comboBox_baudRate->setCurrentIndex(3); - ui->comboBox_dataBit->setCurrentIndex(3); - ui->comboBox_xiaoyan->setCurrentIndex(2); + ui_->comboBox_baudRate->setCurrentIndex(3); + ui_->comboBox_dataBit->setCurrentIndex(3); + ui_->comboBox_xiaoyan->setCurrentIndex(2); //串口未连接前锁定读取和写入按钮 - ui->btn_read->setEnabled(0); - ui->pushWrite->setEnabled(0); + ui_->btn_read->setEnabled(0); + ui_->pushWrite->setEnabled(0); //检查当前可用串口 QList serialList = QSerialPortInfo::availablePorts(); for(QSerialPortInfo serialInfo : serialList) { - ui->comboBox_serialNum->addItem(serialInfo.portName()); + ui_->comboBox_serialNum->addItem(serialInfo.portName()); } } +/** + * @brief Widget 析构函数 + * + * 清理分配的资源:删除UI、Modbus和串口通信对象。 + */ Widget::~Widget() { - delete modbus; - delete serialComm; - delete ui; + delete modbus_; + delete serialComm_; + delete ui_; } -//串口连接 +/** + * @brief 串口连接/断开按钮点击处理 + * + * 根据当前按钮状态执行串口连接或断开操作。 + * 连接时读取界面配置的串口参数并尝试打开串口。 + * 成功连接后启用读写按钮,断开后禁用读写按钮。 + */ void Widget::on_btnConnect_clicked() { - if (ui->btnConnect->text() == "连接") + if (ui_->btnConnect->text() == "连接") { // 获取界面配置的串口参数 - QString portName = ui->comboBox_serialNum->currentText(); - qint32 baudRate = ui->comboBox_baudRate->currentText().toInt(); - QSerialPort::DataBits dataBits = QSerialPort::DataBits(ui->comboBox_dataBit->currentText().toInt()); + QString portName = ui_->comboBox_serialNum->currentText(); + qint32 baudRate = ui_->comboBox_baudRate->currentText().toInt(); + QSerialPort::DataBits dataBits = QSerialPort::DataBits(ui_->comboBox_dataBit->currentText().toInt()); QSerialPort::Parity parity; - switch (ui->comboBox_xiaoyan->currentIndex()) + switch (ui_->comboBox_xiaoyan->currentIndex()) { case 0: parity = QSerialPort::NoParity; break; case 1: parity = QSerialPort::OddParity; break; case 2: parity = QSerialPort::EvenParity; break; default: parity = QSerialPort::NoParity; } - QSerialPort::StopBits stopBits = QSerialPort::StopBits(ui->comboBox_stopBit->currentText().toInt()); + QSerialPort::StopBits stopBits = QSerialPort::StopBits(ui_->comboBox_stopBit->currentText().toInt()); // 配置并打开串口 - serialComm->setPortParams(portName, baudRate, dataBits, parity, stopBits); - if (serialComm->open()) + serialComm_->setPortParams(portName, baudRate, dataBits, parity, stopBits); + if (serialComm_->open()) { - ui->btnConnect->setText("断开"); - ui->btn_read->setEnabled(true); - ui->pushWrite->setEnabled(true); + ui_->btnConnect->setText("断开"); + ui_->btn_read->setEnabled(true); + ui_->pushWrite->setEnabled(true); } else { @@ -97,22 +126,28 @@ void Widget::on_btnConnect_clicked() else { // 断开串口 - serialComm->close(); - ui->btnConnect->setText("连接"); - ui->btn_read->setEnabled(false); - ui->pushWrite->setEnabled(false); + serialComm_->close(); + ui_->btnConnect->setText("连接"); + ui_->btn_read->setEnabled(false); + ui_->pushWrite->setEnabled(false); } } -//写线圈和写寄存器 +/** + * @brief 写数据按钮点击处理 + * + * 根据当前选择的功能码执行写线圈或写寄存器操作。 + * 验证输入数据格式,生成Modbus命令并发送。 + * 操作期间禁用读写按钮防止重复操作。 + */ void Widget::on_pushWrite_clicked() { - switch (ui->comboBox_gongnengma->currentIndex()) //判断当前功能码 + switch (ui_->comboBox_gongnengma->currentIndex()) //判断当前功能码 { case 2: //写多个线圈 { - QString sendData = ui->lineEdit->text().trimmed(); //读取输入框中的数据 + QString sendData = ui_->lineEdit->text().trimmed(); //读取输入框中的数据 //判断输入框中是否为空 if (sendData.isEmpty()) { @@ -138,10 +173,10 @@ void Widget::on_pushWrite_clicked() } //配置从站参数 - quint16 stationAddress = ui->lineEdit_stationAddress->text().toInt(); + quint16 stationAddress = ui_->lineEdit_stationAddress->text().toInt(); quint16 functionCode = 0x0f; - quint16 stratAddress = ui->lineEdit_stratAddress->text().toInt(); - quint16 length = ui->lineEdit_length->text().toInt(); + quint16 stratAddress = ui_->lineEdit_stratAddress->text().toInt(); + quint16 length = ui_->lineEdit_length->text().toInt(); //判断输入数据长度与设置的长度是否一致 if (coils.size() != length) @@ -150,20 +185,20 @@ void Widget::on_pushWrite_clicked() return; } //将从站参数传入modbus中 - modbus->Set(stationAddress,functionCode,stratAddress,length); - modbus->WriteCoil(coils); //使用WriteCoil方法生成命令报文 + modbus_->Set(stationAddress,functionCode,stratAddress,length); + modbus_->WriteCoil(coils); //使用WriteCoil方法生成命令报文 - serialComm->sendData(modbus->SendCommand()); //发送生成的命令报文 + serialComm_->sendData(modbus_->SendCommand()); //发送生成的命令报文 //锁定按钮 - ui->btn_read->setEnabled(0); - ui->pushWrite->setEnabled(0); + ui_->btn_read->setEnabled(0); + ui_->pushWrite->setEnabled(0); break; } case 3: //写多个寄存器 { - QString sendData = ui->lineEdit->text().trimmed(); //读取数据 + QString sendData = ui_->lineEdit->text().trimmed(); //读取数据 //判断输入框中是否为空 if (sendData.isEmpty()) { @@ -216,10 +251,10 @@ void Widget::on_pushWrite_clicked() } //配置从站参数 - quint16 stationAddress = ui->lineEdit_stationAddress->text().toInt(); + quint16 stationAddress = ui_->lineEdit_stationAddress->text().toInt(); quint16 functionCode = 0x10; - quint16 stratAddress = ui->lineEdit_stratAddress->text().toInt(); - quint16 length = ui->lineEdit_length->text().toInt(); + quint16 stratAddress = ui_->lineEdit_stratAddress->text().toInt(); + quint16 length = ui_->lineEdit_length->text().toInt(); //判断输入数据长度与设置的长度是否一致 if (values.size() != length) @@ -228,13 +263,13 @@ void Widget::on_pushWrite_clicked() return; } //将从站参数传入modbus中 - modbus->Set(stationAddress,functionCode,stratAddress,length); - modbus->WriteRegister(values); //使用WriteRegister方法生成命令报文 + modbus_->Set(stationAddress,functionCode,stratAddress,length); + modbus_->WriteRegister(values); //使用WriteRegister方法生成命令报文 - serialComm->sendData(modbus->SendCommand()); //发送生成的命令报文 + serialComm_->sendData(modbus_->SendCommand()); //发送生成的命令报文 //锁定按钮 - ui->btn_read->setEnabled(0); - ui->pushWrite->setEnabled(0); + ui_->btn_read->setEnabled(0); + ui_->pushWrite->setEnabled(0); break; } @@ -248,83 +283,107 @@ void Widget::on_pushWrite_clicked() } } -//发送读线圈和寄存器报文 +/** + * @brief 读数据按钮点击处理 + * + * 根据当前选择的功能码发送读线圈或读寄存器请求。 + * 生成Modbus命令并发送,操作期间禁用读写按钮。 + */ void Widget::on_btn_read_clicked() { //若当前操作设置错误则进行提示 - if (ui->comboBox_gongnengma->currentIndex() == 2 || - ui->comboBox_gongnengma->currentIndex() == 3) + if (ui_->comboBox_gongnengma->currentIndex() == 2 || + ui_->comboBox_gongnengma->currentIndex() == 3) { QMessageBox::warning(this, "提示", "请将“操作”切换为读线圈或读寄存器"); return; } //设置从站参数 - quint16 stationAddress = ui->lineEdit_stationAddress->text().toInt(); + quint16 stationAddress = ui_->lineEdit_stationAddress->text().toInt(); quint16 functionCode; - quint16 stratAddress = ui->lineEdit_stratAddress->text().toInt(); - quint16 length = ui->lineEdit_length->text().toInt(); - if (ui->comboBox_gongnengma->currentIndex() == 0) //读线圈 + quint16 stratAddress = ui_->lineEdit_stratAddress->text().toInt(); + quint16 length = ui_->lineEdit_length->text().toInt(); + if (ui_->comboBox_gongnengma->currentIndex() == 0) //读线圈 { functionCode = 0x01; } - else if(ui->comboBox_gongnengma->currentIndex() == 1) //读寄存器 + else if(ui_->comboBox_gongnengma->currentIndex() == 1) //读寄存器 { functionCode = 0x03; } //将从站参数传入modbus中 - modbus->Set(stationAddress,functionCode,stratAddress,length); - modbus->ReadCoilAndReg(); //使用ReadCoilAndReg方法生成命令报文 + modbus_->Set(stationAddress,functionCode,stratAddress,length); + modbus_->ReadCoilAndReg(); //使用ReadCoilAndReg方法生成命令报文 - serialComm->sendData(modbus->SendCommand()); //发送生成的命令报文 + serialComm_->sendData(modbus_->SendCommand()); //发送生成的命令报文 //锁定按钮 - ui->btn_read->setEnabled(0); - ui->pushWrite->setEnabled(0); + ui_->btn_read->setEnabled(0); + ui_->pushWrite->setEnabled(0); } -//保存通信历史数据 +/** + * @brief 保存通信历史数据 + * + * 调用外部函数保存通信历史到文件。 + */ void Widget::on_btn_SaveDate_clicked() { - SaveDate(this,ui->textEdit_2); + SaveDate(this,ui_->textEdit_2); } -//读取通信历史数据 +/** + * @brief 读取通信历史数据 + * + * 调用外部函数从文件加载通信历史。 + */ void Widget::on_btn_ReadDate_clicked() { - ReadDate(this,ui->textEdit_2); + ReadDate(this,ui_->textEdit_2); } -//清空状态通知文本框中的信息 +/** + * @brief 清空状态通知文本框 + */ void Widget::on_btn_ClearDate_clicked() { - ui->textEdit_2->clear(); + ui_->textEdit_2->clear(); } -//清空数据读取文本框中的信息 +/** + * @brief 清空数据读取文本框 + */ void Widget::on_btn_ClearRead_clicked() { - ui->textEdit->clear(); + ui_->textEdit->clear(); } -//处理接收到的返回报文 +/** + * @brief 串口数据接收处理 + * @param data 接收到的原始字节数据 + * + * 将接收到的数据交给Modbus解析,处理可能的错误, + * 并根据当前功能码解析和显示线圈或寄存器数据。 + */ void Widget::onSerialDataReceived(const QByteArray &data) { - QByteArray revMessage = modbus->Receive(data); // 接收的原始数据交给Modbus解析 + QByteArray revMessage = modbus_->Receive(data); // 接收的原始数据交给Modbus解析 // 启用操作按钮 - ui->btn_read->setEnabled(true); - ui->pushWrite->setEnabled(true); - if (revMessage.isEmpty()) //若modbus检测响应报文格式错误则丢弃数据,返回值为空 + ui_->btn_read->setEnabled(true); + ui_->pushWrite->setEnabled(true); + if (revMessage.isEmpty()) { + //若modbus检测响应报文格式错误则丢弃数据,返回值为空 QMessageBox::warning(this, "提示", "返回报文站地址或CRC校验错误,请重试"); return; } //显示接收到的响应报文 - ui->textEdit_2->append("接收报文:" + revMessage.toHex().toUpper()); + ui_->textEdit_2->append("接收报文:" + revMessage.toHex().toUpper()); // 检查响应报文中是否带有错误码 - int exCode = modbus->ErrorCheck(); + int exCode = modbus_->ErrorCheck(); if (exCode) { QString errorMsg; @@ -341,41 +400,50 @@ void Widget::onSerialDataReceived(const QByteArray &data) } // 解析并显示数据(根据功能码) - switch (ui->comboBox_gongnengma->currentIndex()) + switch (ui_->comboBox_gongnengma->currentIndex()) { case 0: { // 读线圈 - QVector coils = modbus->AnalReadCoil(); //使用AnalReadCoil方法解析线圈数据 - ui->textEdit->append("线圈状态:"); - for (int i = 0; i < coils.size(); i++) + QVector coils = modbus_->AnalReadCoil(); //使用AnalReadCoil方法解析线圈数据 + ui_->textEdit->append("线圈状态:"); + for (int i = 0; i < ui_->lineEdit_length->text().toInt(); i++) { - ui->textEdit->append(QString("线圈%1: %2").arg(i+1).arg(coils[i] ? "1" : "0")); + ui_->textEdit->append(QString("线圈%1: %2").arg(i+1).arg(coils[i] ? "1" : "0")); } break; } case 1: { // 读寄存器 - QVector regs = modbus->AnalReadReg(); //使用AnalReadReg方法解析寄存器数据 - ui->textEdit->append("寄存器值:"); - for (int i = 0; i < regs.size(); i++) + QVector regs = modbus_->AnalReadReg(); //使用AnalReadReg方法解析寄存器数据 + ui_->textEdit->append("寄存器值:"); + for (int i = 0; i < ui_->lineEdit_length->text().toInt(); i++) { - ui->textEdit->append(QString("寄存器%1: %2").arg(i+1).arg(regs[i])); + ui_->textEdit->append(QString("寄存器%1: %2").arg(i+1).arg(regs[i])); } break; } } } -//显示串口状态 +/** + * @brief 串口状态变化处理 + * @param status 状态描述字符串 + * + * 在状态通知文本框中显示串口状态信息。 + */ void Widget::onSerialStatusChanged(const QString &status) { - ui->textEdit_2->append(status); // 显示状态信息(如连接成功、超时重发等) + ui_->textEdit_2->append(status); // 显示状态信息(如连接成功、超时重发等) } -//超过超时重发次数后触发 +/** + * @brief 串口通信超时处理 + * + * 显示超时警告并重新启用操作按钮。 + */ void Widget::onSerialTimeout() { QMessageBox::warning(this, "提示", "等待响应超时,请检查设备"); - ui->btn_read->setEnabled(true); - ui->pushWrite->setEnabled(true); + ui_->btn_read->setEnabled(true); + ui_->pushWrite->setEnabled(true); } diff --git a/widget.h b/widget.h index 6e40a4b..2ea94ea 100644 --- a/widget.h +++ b/widget.h @@ -1,3 +1,13 @@ +/******************************* + * Copyright (C) 2025. + * + * File Name: widget.h + * Description: 显示与交互界面配置 + * Others: + * Version: 1.0 + * Author: lipengpeng + * Date: 2025-7-23 + *******************************/ #ifndef WIDGET_H #define WIDGET_H @@ -8,6 +18,13 @@ #include "communicationhistory.h" #include "serialcommunicator.h" +/** + * @class Widget + * @brief 主窗口类,负责用户界面和串口通信控制 + * + * 该类提供串口连接、数据读写、历史记录管理等功能。 + * 使用SerialCommunicator处理底层串口通信,MyModbus处理Modbus协议。 + */ namespace Ui { class Widget; } @@ -17,34 +34,57 @@ class Widget : public QWidget Q_OBJECT public: + /** + * @brief 构造函数,初始化界面和通信对象 + * @param parent 父窗口指针,默认为空 + */ explicit Widget(QWidget *parent = 0); + /** + * @brief 析构函数,清理资源 + */ ~Widget(); private slots: + //连接/断开串口按钮槽函数 void on_btnConnect_clicked(); + //发送写数据命令报文按钮槽函数 void on_pushWrite_clicked(); + //发送读数据命令报文按钮槽函数 void on_btn_read_clicked(); + //保存历史通信数据按钮槽函数 void on_btn_SaveDate_clicked(); + //读取历史通信数据按钮槽函数 void on_btn_ReadDate_clicked(); + //清空当前通信数据文本框按钮槽函数 void on_btn_ClearDate_clicked(); + //清空解析数据文本框按钮槽函数 void on_btn_ClearRead_clicked(); + /** + * @brief 串口数据接收槽函数 + * @param data 接收到的字节数据 + */ void onSerialDataReceived(const QByteArray &data); + /** + * @brief 串口状态变化槽函数 + * @param status 状态描述字符串 + */ void onSerialStatusChanged(const QString &status); + // 串口通信超时处理槽函数 void onSerialTimeout(); private: - Ui::Widget *ui; - SerialCommunicator *serialComm; - MyModbus *modbus; + Ui::Widget *ui_; // 指向用户界面的指针 + SerialCommunicator *serialComm_; // 串口通信处理对象 + MyModbus *modbus_; // Modbus协议处理对象 }; #endif // WIDGET_H diff --git a/widget.ui b/widget.ui index c170200..c5d8370 100644 --- a/widget.ui +++ b/widget.ui @@ -25,13 +25,39 @@ 从站配置 - + + + + 10 + 170 + 71 + 51 + + + + + + + + + + 90 + 170 + 71 + 51 + + + + + + + 10 - 30 - 159 - 141 + 20 + 151 + 151 @@ -42,6 +68,13 @@ + + + + 1 + + + @@ -101,41 +134,8 @@ - - - - 1 - - - - - - - 10 - 170 - 71 - 51 - - - - - - - - - - 90 - 170 - 71 - 51 - - - - - -