소스 검색

备份

master
鹏鹏 李 1 주 전
부모
커밋
a0e561ff05
4개의 변경된 파일251개의 추가작업 그리고 143개의 파일을 삭제
  1. +1
    -1
      modbus.pro.user
  2. +170
    -102
      widget.cpp
  3. +43
    -3
      widget.h
  4. +37
    -37
      widget.ui

+ 1
- 1
modbus.pro.user 파일 보기

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE QtCreatorProject>
<!-- Written by QtCreator 4.0.3, 2025-07-29T20:12:44. -->
<!-- Written by QtCreator 4.0.3, 2025-07-30T09:27:42. -->
<qtcreator>
<data>
<variable>EnvironmentId</variable>


+ 170
- 102
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 <QSerialPortInfo>
@@ -9,85 +19,104 @@
#include <QMessageBox>
#include <synchapi.h>

/**
* @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<QSerialPortInfo> 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<bool> coils = modbus->AnalReadCoil(); //使用AnalReadCoil方法解析线圈数据
ui->textEdit->append("线圈状态:");
for (int i = 0; i < coils.size(); i++)
QVector<bool> 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<quint16> regs = modbus->AnalReadReg(); //使用AnalReadReg方法解析寄存器数据
ui->textEdit->append("寄存器值:");
for (int i = 0; i < regs.size(); i++)
QVector<quint16> 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);
}

+ 43
- 3
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

+ 37
- 37
widget.ui 파일 보기

@@ -25,13 +25,39 @@
<property name="title">
<string>从站配置</string>
</property>
<widget class="QWidget" name="layoutWidget">
<widget class="QPushButton" name="btn_read">
<property name="geometry">
<rect>
<x>10</x>
<y>170</y>
<width>71</width>
<height>51</height>
</rect>
</property>
<property name="text">
<string>读</string>
</property>
</widget>
<widget class="QPushButton" name="pushWrite">
<property name="geometry">
<rect>
<x>90</x>
<y>170</y>
<width>71</width>
<height>51</height>
</rect>
</property>
<property name="text">
<string>写</string>
</property>
</widget>
<widget class="QWidget" name="">
<property name="geometry">
<rect>
<x>10</x>
<y>30</y>
<width>159</width>
<height>141</height>
<y>20</y>
<width>151</width>
<height>151</height>
</rect>
</property>
<layout class="QGridLayout" name="gridLayout_2">
@@ -42,6 +68,13 @@
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="lineEdit_stationAddress">
<property name="text">
<string>1</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_10">
<property name="text">
@@ -101,41 +134,8 @@
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="lineEdit_stationAddress">
<property name="text">
<string>1</string>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QPushButton" name="btn_read">
<property name="geometry">
<rect>
<x>10</x>
<y>170</y>
<width>71</width>
<height>51</height>
</rect>
</property>
<property name="text">
<string>读</string>
</property>
</widget>
<widget class="QPushButton" name="pushWrite">
<property name="geometry">
<rect>
<x>90</x>
<y>170</y>
<width>71</width>
<height>51</height>
</rect>
</property>
<property name="text">
<string>写</string>
</property>
</widget>
</widget>
<widget class="QGroupBox" name="groupBox_2">
<property name="geometry">


불러오는 중...
취소
저장