From a8ec0fe24fe23995bf9333dbb563a7dd0a2ccbf1 Mon Sep 17 00:00:00 2001 From: lipengpeng Date: Wed, 30 Jul 2025 17:14:58 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AF=B9=E5=8F=98=E9=87=8F=E5=90=8D=E5=92=8C?= =?UTF-8?q?=E5=87=BD=E6=95=B0=E5=90=8D=E8=BF=9B=E8=A1=8C=E4=BA=86=E4=BF=AE?= =?UTF-8?q?=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- modbus.pro.user | 2 +- mymodbus.cpp | 6 +- mymodbus.h | 16 +-- serialcommunicator.cpp | 28 ++-- serialcommunicator.h | 2 +- widget.cpp | 75 +++++++---- widget.h | 16 ++- widget.ui | 289 +++++++++++++++++++++-------------------- 8 files changed, 242 insertions(+), 192 deletions(-) diff --git a/modbus.pro.user b/modbus.pro.user index c57183c..ff0a176 100644 --- a/modbus.pro.user +++ b/modbus.pro.user @@ -1,6 +1,6 @@ - + EnvironmentId diff --git a/mymodbus.cpp b/mymodbus.cpp index 7395122..f326702 100644 --- a/mymodbus.cpp +++ b/mymodbus.cpp @@ -20,9 +20,9 @@ MyModbus::MyModbus() { this->stationAddress_ = 1; // 默认站地址 - this->functionCode_ = 0x01; // 默认功能码(读线圈) - this->startAdress_ = 256; // 默认起始地址 - this->length_ = 1; // 默认数据长度 + this->functionCode_ = 0x01; // 默认功能码(读线圈) + this->startAdress_ = 256; // 默认起始地址 + this->length_ = 1; // 默认数据长度 } /** diff --git a/mymodbus.h b/mymodbus.h index 7a995e2..afc12c0 100644 --- a/mymodbus.h +++ b/mymodbus.h @@ -35,14 +35,6 @@ class MyModbus { -private: - quint16 stationAddress_; // 从站地址 - quint16 functionCode_; // 功能码 - quint16 startAdress_; // 起始地址 - quint16 length_; // 数据长度 - QByteArray sendCommand_; // 待发送的命令报文 - QByteArray receive_; // 接收到的响应报文 - public: /** * @brief 默认构造函数 @@ -123,6 +115,14 @@ public: */ bool CrcCheck(const QByteArray &data); +private: + quint16 stationAddress_; // 从站地址 + quint16 functionCode_; // 功能码 + quint16 startAdress_; // 起始地址 + quint16 length_; // 数据长度 + QByteArray sendCommand_; // 待发送的命令报文 + QByteArray receive_; // 接收到的响应报文 + }; #endif // MYMODBUS_H diff --git a/serialcommunicator.cpp b/serialcommunicator.cpp index 1a70b12..80bf77b 100644 --- a/serialcommunicator.cpp +++ b/serialcommunicator.cpp @@ -48,7 +48,19 @@ SerialCommunicator::SerialCommunicator(QObject *parent) : QObject(parent) */ SerialCommunicator::~SerialCommunicator() { + //停止所有定时器,避免在对象销毁后触发定时器事件 + if (recvTimer_->isActive()) + { + recvTimer_->stop(); + } + if (resendTimer_->isActive()) + { + resendTimer_->stop(); + } close(); + delete recvTimer_; + delete resendTimer_; + delete serialPort_; } /** @@ -61,10 +73,10 @@ SerialCommunicator::~SerialCommunicator() * * @note 此方法应在打开串口前调用,设置值将在下次open()时生效 */ -void SerialCommunicator::setPortParams(const QString &portName, qint32 baudRate, - QSerialPort::DataBits dataBits, - QSerialPort::Parity parity, - QSerialPort::StopBits stopBits) +void SerialCommunicator::setPortParams(QString portName, qint32 baudRate, + QSerialPort::DataBits dataBits, + QSerialPort::Parity parity, + QSerialPort::StopBits stopBits) { this->portName_ = portName; this->baudRate_ = baudRate; @@ -228,12 +240,12 @@ void SerialCommunicator::onReadyRead() } /** - * @brief 接收完成超时处理 + * @brief 数据接收完成处理 * * @details 执行流程: - * 1. 发送dataReceived信号包含完整数据 - * 2. 清空接收缓冲区 - * 3. 停止重发定时器(表示已收到响应) + * 1. 停止重发定时器(表示已收到响应) + * 2. 发送dataReceived信号包含完整数据 + * 3. 清空接收缓冲区 */ void SerialCommunicator::onRecvTimeout() { diff --git a/serialcommunicator.h b/serialcommunicator.h index 1730669..7aaedd2 100644 --- a/serialcommunicator.h +++ b/serialcommunicator.h @@ -60,7 +60,7 @@ public: * @param stopBits 停止位(通常为QSerialPort::OneStop) * @note 此方法应在打开串口前调用 */ - void setPortParams(const QString &portName_, qint32 baudRate_, + void setPortParams(QString portName_, qint32 baudRate_, QSerialPort::DataBits dataBits_, QSerialPort::Parity parity_, QSerialPort::StopBits stopBits_); diff --git a/widget.cpp b/widget.cpp index 2c2d57d..9f7df4d 100644 --- a/widget.cpp +++ b/widget.cpp @@ -42,6 +42,14 @@ Widget::Widget(QWidget *parent) : serialComm_->setResendTimeout(1000); // 重发间隔1s // 连接信号槽(串口->界面) + connect(ui_->btn_connect, &QPushButton::clicked, this, &Widget::btnConnectClicked); + connect(ui_->btn_write, &QPushButton::clicked, this, &Widget::onBtnWriteClicked); + connect(ui_->btn_read, &QPushButton::clicked, this, &Widget::onBtnReadClicked); + connect(ui_->btn_clear_read, &QPushButton::clicked, this, &Widget::onBtnClearreadClicked); + connect(ui_->btn_clear_date, &QPushButton::clicked, this, &Widget::onBtnCleardateClicked); + connect(ui_->btn_save_date, &QPushButton::clicked, this, &Widget::onBtnSavedateClicked); + connect(ui_->btn_read_date, &QPushButton::clicked, this, &Widget::onBtnReaddateClicked); + connect(ui_->btn_refresh, &QPushButton::clicked, this, &Widget::onBtnRefreshClicked); //当串口接收到完整数据后,SerialCommunicator会发出dataReceived信号 connect(serialComm_, &SerialCommunicator::dataReceived, this, &Widget::onSerialDataReceived); //当串口状态改变时,SerialCommunicator会发出statusChanged信号 @@ -50,9 +58,9 @@ Widget::Widget(QWidget *parent) : connect(serialComm_, &SerialCommunicator::timeoutOccurred, this, &Widget::onSerialTimeout); //当串口连接出现错误时(例如串口突然断开),SerialCommunicator会发出physicalDisconnected信号 connect(serialComm_, &SerialCommunicator::physicalDisconnected, this, [=](){ - ui_->btnConnect->setText("连接"); + ui_->btn_connect->setText("连接"); ui_->btn_read->setEnabled(false); - ui_->pushWrite->setEnabled(false); + ui_->btn_write->setEnabled(false); QMessageBox::warning(this, "警告", "串口已断开连接"); }); @@ -62,8 +70,8 @@ Widget::Widget(QWidget *parent) : ui_->comboBox_xiaoyan->setCurrentIndex(2); //串口未连接前锁定读取和写入按钮 - ui_->btn_read->setEnabled(0); - ui_->pushWrite->setEnabled(0); + ui_->btn_read->setEnabled(false); + ui_->btn_write->setEnabled(false); //检查当前可用串口 QList serialList = QSerialPortInfo::availablePorts(); @@ -92,9 +100,9 @@ Widget::~Widget() * 连接时读取界面配置的串口参数并尝试打开串口。 * 成功连接后启用读写按钮,断开后禁用读写按钮。 */ -void Widget::on_btnConnect_clicked() +void Widget::btnConnectClicked() { - if (ui_->btnConnect->text() == "连接") + if (ui_->btn_connect->text() == "连接") { // 获取界面配置的串口参数 QString portName = ui_->comboBox_serialNum->currentText(); @@ -114,9 +122,9 @@ void Widget::on_btnConnect_clicked() serialComm_->setPortParams(portName, baudRate, dataBits, parity, stopBits); if (serialComm_->open()) { - ui_->btnConnect->setText("断开"); + ui_->btn_connect->setText("断开"); ui_->btn_read->setEnabled(true); - ui_->pushWrite->setEnabled(true); + ui_->btn_write->setEnabled(true); } else { @@ -127,9 +135,9 @@ void Widget::on_btnConnect_clicked() { // 断开串口 serialComm_->close(); - ui_->btnConnect->setText("连接"); + ui_->btn_connect->setText("连接"); ui_->btn_read->setEnabled(false); - ui_->pushWrite->setEnabled(false); + ui_->btn_write->setEnabled(false); } } @@ -141,7 +149,7 @@ void Widget::on_btnConnect_clicked() * 验证输入数据格式,生成Modbus命令并发送。 * 操作期间禁用读写按钮防止重复操作。 */ -void Widget::on_pushWrite_clicked() +void Widget::onBtnWriteClicked() { switch (ui_->comboBox_gongnengma->currentIndex()) //判断当前功能码 { @@ -190,8 +198,8 @@ void Widget::on_pushWrite_clicked() serialComm_->sendData(modbus_->SendCommand()); //发送生成的命令报文 //锁定按钮 - ui_->btn_read->setEnabled(0); - ui_->pushWrite->setEnabled(0); + ui_->btn_read->setEnabled(false); + ui_->btn_write->setEnabled(false); break; } @@ -239,11 +247,11 @@ void Widget::on_pushWrite_clicked() } else { - // 默认按十六进制处理 + // 若输入数值不是十进制则按十六进制处理 v = s.toUShort(&ok, 16); if (!ok) { - QMessageBox::warning(this, "提示", "请输入正确的十六进制值(0-FFFF),或检查逗号格式"); + QMessageBox::warning(this, "提示", "请输入正确的10进制或16进制值(0-FFFF),或检查逗号格式"); return; } } @@ -268,8 +276,8 @@ void Widget::on_pushWrite_clicked() serialComm_->sendData(modbus_->SendCommand()); //发送生成的命令报文 //锁定按钮 - ui_->btn_read->setEnabled(0); - ui_->pushWrite->setEnabled(0); + ui_->btn_read->setEnabled(false); + ui_->btn_write->setEnabled(false); break; } @@ -289,7 +297,7 @@ void Widget::on_pushWrite_clicked() * 根据当前选择的功能码发送读线圈或读寄存器请求。 * 生成Modbus命令并发送,操作期间禁用读写按钮。 */ -void Widget::on_btn_read_clicked() +void Widget::onBtnReadClicked() { //若当前操作设置错误则进行提示 if (ui_->comboBox_gongnengma->currentIndex() == 2 || @@ -318,8 +326,8 @@ void Widget::on_btn_read_clicked() serialComm_->sendData(modbus_->SendCommand()); //发送生成的命令报文 //锁定按钮 - ui_->btn_read->setEnabled(0); - ui_->pushWrite->setEnabled(0); + ui_->btn_read->setEnabled(false); + ui_->btn_write->setEnabled(false); } @@ -328,7 +336,7 @@ void Widget::on_btn_read_clicked() * * 调用外部函数保存通信历史到文件。 */ -void Widget::on_btn_SaveDate_clicked() +void Widget::onBtnSavedateClicked() { SaveDate(this,ui_->textEdit_2); } @@ -338,7 +346,7 @@ void Widget::on_btn_SaveDate_clicked() * * 调用外部函数从文件加载通信历史。 */ -void Widget::on_btn_ReadDate_clicked() +void Widget::onBtnReaddateClicked() { ReadDate(this,ui_->textEdit_2); } @@ -346,7 +354,7 @@ void Widget::on_btn_ReadDate_clicked() /** * @brief 清空状态通知文本框 */ -void Widget::on_btn_ClearDate_clicked() +void Widget::onBtnCleardateClicked() { ui_->textEdit_2->clear(); } @@ -354,7 +362,7 @@ void Widget::on_btn_ClearDate_clicked() /** * @brief 清空数据读取文本框 */ -void Widget::on_btn_ClearRead_clicked() +void Widget::onBtnClearreadClicked() { ui_->textEdit->clear(); } @@ -371,7 +379,7 @@ void Widget::onSerialDataReceived(const QByteArray &data) QByteArray revMessage = modbus_->Receive(data); // 接收的原始数据交给Modbus解析 // 启用操作按钮 ui_->btn_read->setEnabled(true); - ui_->pushWrite->setEnabled(true); + ui_->btn_write->setEnabled(true); if (revMessage.isEmpty()) { //若modbus检测响应报文格式错误则丢弃数据,返回值为空 @@ -445,5 +453,20 @@ void Widget::onSerialTimeout() { QMessageBox::warning(this, "提示", "等待响应超时,请检查设备"); ui_->btn_read->setEnabled(true); - ui_->pushWrite->setEnabled(true); + ui_->btn_write->setEnabled(true); +} + +/** + * @brief 检测当前可用串口 + * + * 检查可用串口并填充到下拉框。 + */ +void Widget::onBtnRefreshClicked() +{ + ui_->comboBox_serialNum->clear(); + QList serialList = QSerialPortInfo::availablePorts(); + for(QSerialPortInfo serialInfo : serialList) + { + ui_->comboBox_serialNum->addItem(serialInfo.portName()); + } } diff --git a/widget.h b/widget.h index 2ea94ea..8748771 100644 --- a/widget.h +++ b/widget.h @@ -46,25 +46,25 @@ public: private slots: //连接/断开串口按钮槽函数 - void on_btnConnect_clicked(); + void btnConnectClicked(); //发送写数据命令报文按钮槽函数 - void on_pushWrite_clicked(); + void onBtnWriteClicked(); //发送读数据命令报文按钮槽函数 - void on_btn_read_clicked(); + void onBtnReadClicked(); //保存历史通信数据按钮槽函数 - void on_btn_SaveDate_clicked(); + void onBtnSavedateClicked(); //读取历史通信数据按钮槽函数 - void on_btn_ReadDate_clicked(); + void onBtnReaddateClicked(); //清空当前通信数据文本框按钮槽函数 - void on_btn_ClearDate_clicked(); + void onBtnCleardateClicked(); //清空解析数据文本框按钮槽函数 - void on_btn_ClearRead_clicked(); + void onBtnClearreadClicked(); /** * @brief 串口数据接收槽函数 @@ -81,6 +81,8 @@ private slots: // 串口通信超时处理槽函数 void onSerialTimeout(); + void onBtnRefreshClicked(); + private: Ui::Widget *ui_; // 指向用户界面的指针 SerialCommunicator *serialComm_; // 串口通信处理对象 diff --git a/widget.ui b/widget.ui index c5d8370..f3cf060 100644 --- a/widget.ui +++ b/widget.ui @@ -38,7 +38,7 @@ - + 90 @@ -51,12 +51,12 @@ - + 10 20 - 151 + 159 151 @@ -143,154 +143,167 @@ 30 19 181 - 221 + 231 串口配置 - - - - - + + + + 10 + 20 + 161 + 205 + + + + + + + 串口 + + + + + + + + + + 波特率 + + + + + + - 串口 + 1200 - - - - - - - + + - 波特率 + 2400 - - - - - - - 1200 - - - - - 2400 - - - - - 4800 - - - - - 9600 - - - - - 19200 - - - - - 38400 - - - - - - + + - 数据位 + 4800 - - - - - - - 5 - - - - - 6 - - - - - 7 - - - - - 8 - - - - - - + + - 校验位 + 9600 - - - - - - - 无校验(N) - - - - - 奇校验(O) - - - - - 偶校验(E) - - - - - - + + - 停止位 + 19200 - - - - - - - 1 - - - - - 2 - - - - - - + + - 连接 + 38400 - - - - - + + + + + + + 数据位 + + + + + + + + 5 + + + + + 6 + + + + + 7 + + + + + 8 + + + + + + + + 校验位 + + + + + + + + 无校验(N) + + + + + 奇校验(O) + + + + + 偶校验(E) + + + + + + + + 停止位 + + + + + + + + 1 + + + + + 2 + + + + + + + + 连接 + + + + + + + 检测可用串口 + + + + + @@ -394,28 +407,28 @@ - + 清空读取信息 - + 清空收发信息 - + 读取历史收发信息 - + 保存收发信息