From 0b2a79e481d1107f406aad3582731ee32a95d660 Mon Sep 17 00:00:00 2001 From: Dman <71756356+xmt3061123@users.noreply.github.com> Date: Mon, 22 Jul 2024 09:33:26 +0800 Subject: [PATCH] Add files via upload --- main.cpp | 10 ++ serialassistant.cpp | 225 +++++++++++++++++++++++++++++++++++++++ serialassistant.h | 54 ++++++++++ serialassistant.pro | 24 +++++ serialassistant.ui | 251 ++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 564 insertions(+) create mode 100644 main.cpp create mode 100644 serialassistant.cpp create mode 100644 serialassistant.h create mode 100644 serialassistant.pro create mode 100644 serialassistant.ui diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..e1480b4 --- /dev/null +++ b/main.cpp @@ -0,0 +1,10 @@ +#include "serialassistant.h" +#include + +int main(int argc, char *argv[]) +{ + QApplication a(argc, argv); + SerialAssistant w; // 注意这里使用大写的 SerialAssistant + w.show(); + return a.exec(); +} diff --git a/serialassistant.cpp b/serialassistant.cpp new file mode 100644 index 0000000..d3df88d --- /dev/null +++ b/serialassistant.cpp @@ -0,0 +1,225 @@ +#include "serialassistant.h" +#include "ui_serialassistant.h" +#include +#include +#include + +// 定义静态成员:增加头尾效验 +const QByteArray SerialAssistant::START_DELIMITER = QByteArray::fromHex("AABB"); +const QByteArray SerialAssistant::END_DELIMITER = QByteArray::fromHex("CCDD"); + +// 在构造函数中打开日志文件 +SerialAssistant::SerialAssistant(QWidget *parent) + : QMainWindow(parent) + , ui(new Ui::SerialAssistant) + , totalSentBytes(0) + , totalReceivedBytes(0) + , totalPacketsSent(0) + , totalPacketsReceived(0) + , lostPackets(0) + , logFile("packet_log.txt") +{ + ui->setupUi(this); // 设置UI + serial = new QSerialPort(this); // 创建QSerialPort对象 + initSerialPort(); // 初始化串口设置 + + connect(serial, &QSerialPort::readyRead, this, &SerialAssistant::readData); // 连接readyRead信号到readData槽 + + // 添加状态标签到状态栏 + statusBar()->addPermanentWidget(ui->packetLossLabel); + statusBar()->addPermanentWidget(ui->totalSentLabel); + statusBar()->addPermanentWidget(ui->totalReceivedLabel); + + updateStatusBar(); // 更新状态栏 + + // 打开日志文件进行写入 + if (!logFile.open(QIODevice::WriteOnly | QIODevice::Append | QIODevice::Text)) { + QMessageBox::critical(this, tr("错误"), tr("无法打开日志文件!")); + } else { + logStream.setDevice(&logFile); // 设置日志流的设备为日志文件 + } +} + +// 析构函数,关闭日志文件 +SerialAssistant::~SerialAssistant() +{ + delete ui; + logFile.close(); +} + +// 初始化串口设置 +void SerialAssistant::initSerialPort() +{ + updatePortList(); // 更新可用的串口列表 + + // 添加波特率选项 + ui->baudRateCombo->addItem(QStringLiteral("115200"), QSerialPort::Baud115200); + ui->baudRateCombo->addItem(QStringLiteral("9600"), QSerialPort::Baud9600); + + + // 添加数据位选项 + ui->dataBitsCombo->addItem(QStringLiteral("8"), QSerialPort::Data8); + ui->dataBitsCombo->addItem(QStringLiteral("7"), QSerialPort::Data7); + + // 添加校验位选项 + ui->parityCombo->addItem(QStringLiteral("None"), QSerialPort::NoParity); + ui->parityCombo->addItem(QStringLiteral("Even"), QSerialPort::EvenParity); + ui->parityCombo->addItem(QStringLiteral("Odd"), QSerialPort::OddParity); + + // 添加停止位选项 + ui->stopBitsCombo->addItem(QStringLiteral("1"), QSerialPort::OneStop); + ui->stopBitsCombo->addItem(QStringLiteral("2"), QSerialPort::TwoStop); +} + +// 更新串口列表 +void SerialAssistant::updatePortList() +{ + ui->portCombo->clear(); // 清空串口下拉列表 + const auto infos = QSerialPortInfo::availablePorts(); // 获取可用串口信息 + for (const QSerialPortInfo &info : infos) { + ui->portCombo->addItem(info.portName()); // 将串口名称添加到下拉列表中 + } +} + +// 打开/关闭串口按钮点击事件处理函数 +void SerialAssistant::on_openCloseButton_clicked() +{ + if (serial->isOpen()) { + serial->close(); // 关闭串口 + ui->openCloseButton->setText("打开"); // 更新按钮文本 + statusBar()->showMessage("串口已关闭"); // 更新状态栏信息 + } else { + // 设置串口参数 + serial->setPortName(ui->portCombo->currentText()); + serial->setBaudRate(ui->baudRateCombo->currentData().toInt()); + serial->setDataBits(static_cast(ui->dataBitsCombo->currentData().toInt())); + serial->setParity(static_cast(ui->parityCombo->currentData().toInt())); + serial->setStopBits(static_cast(ui->stopBitsCombo->currentData().toInt())); + + if (serial->open(QIODevice::ReadWrite)) { + ui->openCloseButton->setText("关闭"); // 更新按钮文本 + statusBar()->showMessage("串口已打开"); // 更新状态栏信息 + } else { + QMessageBox::critical(this, tr("错误"), serial->errorString()); // 弹出错误信息 + statusBar()->showMessage("打开串口失败"); // 更新状态栏信息 + } + } +} + +// 发送按钮点击事件处理函数 +void SerialAssistant::on_sendButton_clicked() +{ + if (serial->isOpen()) { + QByteArray data = ui->sendTextEdit->toPlainText().toUtf8(); // 获取要发送的数据 + QByteArray packet = START_DELIMITER + data + END_DELIMITER; // 构建数据包 + quint16 crc = calculateCRC(packet); // 计算CRC校验码 + packet.append((char)(crc >> 8)); // 添加高位CRC + packet.append((char)(crc & 0xFF)); // 添加低位CRC + + qint64 bytesWritten = serial->write(packet); // 发送数据包 + if (bytesWritten == -1) { + QMessageBox::warning(this, tr("发送错误"), tr("数据发送失败!")); // 弹出警告信息 + } else { + totalSentBytes += bytesWritten; // 更新发送字节数 + totalPacketsSent++; // 更新发送包数 + updateStatusBar(); // 更新状态栏 + qDebug() << "Sent packet:" << packet.toHex(); // 调试输出发送的数据包 + } + } else { + QMessageBox::warning(this, tr("警告"), tr("串口未打开!")); // 弹出警告信息 + } +} + +// 读取数据 +void SerialAssistant::readData() +{ + receiveBuffer += serial->readAll(); // 读取所有可用数据到接收缓冲区 + qDebug() << "Current buffer:" << receiveBuffer.toHex(); // 调试输出当前缓冲区数据 + + while (receiveBuffer.contains(START_DELIMITER) && receiveBuffer.contains(END_DELIMITER)) { + int start = receiveBuffer.indexOf(START_DELIMITER); // 找到起始标志位置 + int end = receiveBuffer.indexOf(END_DELIMITER, start + START_DELIMITER.size()); // 找到结束标志位置 + + if (start != -1 && end != -1) { + QByteArray packet = receiveBuffer.mid(start, end - start + END_DELIMITER.size() + 2); // +2 for CRC + receiveBuffer = receiveBuffer.mid(end + END_DELIMITER.size() + 2); // 更新接收缓冲区 + + totalReceivedBytes += packet.size(); // 更新接收字节数 + totalPacketsReceived++; // 更新接收包数 + + if (verifyCRC(packet)) { + QByteArray data = packet.mid(START_DELIMITER.size(), packet.size() - START_DELIMITER.size() - END_DELIMITER.size() - 2); // 提取数据 + ui->receiveTextEdit->append(QString(data)); // 显示接收的数据 + qDebug() << "Received valid packet. Data:" << data; // 调试输出接收到的数据 + } else { + ui->receiveTextEdit->append("接收到损坏的数据!"); // 显示接收到的损坏数据 + lostPackets++; // 更新丢包数 + logLostPacket(packet); // 记录丢包信息 + qDebug() << "Received corrupted packet:" << packet.toHex(); // 调试输出接收到的损坏数据包 + } + } else { + break; // 如果没有完整的数据包,则退出循环 + } + } + + updateStatusBar(); // 更新状态栏 +} + +// 清空接收窗口按钮点击事件处理函数 +void SerialAssistant::on_clearReceiveButton_clicked() +{ + ui->receiveTextEdit->clear(); // 清空接收窗口 +} + +// 清空发送窗口按钮点击事件处理函数 +void SerialAssistant::on_clearSendButton_clicked() +{ + ui->sendTextEdit->clear(); // 清空发送窗口 +} + +// 计算CRC校验码 +quint16 SerialAssistant::calculateCRC(const QByteArray &data) +{ + quint16 crc = 0xFFFF; // 初始化CRC + for (int i = 0; i < data.size(); i++) { + crc ^= (quint8)data[i]; // 逐字节计算CRC + for (int j = 0; j < 8; j++) { + if (crc & 0x0001) { + crc = (crc >> 1) ^ 0xA001; // 如果最低位为1,则右移并异或多项式 + } else { + crc = crc >> 1; // 否则直接右移 + } + } + } + return crc; +} + +// 校验CRC +bool SerialAssistant::verifyCRC(const QByteArray &packet) +{ + if (packet.size() < 2) return false; // 如果数据包长度小于2,则返回false + QByteArray data = packet.left(packet.size() - 2); // 获取数据部分,不包括CRC + quint16 receivedCRC = ((quint8)packet[packet.size() - 2] << 8) | (quint8)packet[packet.size() - 1]; // 提取收到的CRC + quint16 calculatedCRC = calculateCRC(data); // 计算数据部分的CRC + return receivedCRC == calculatedCRC; // 返回校验结果 +} + +// 更新状态栏 +void SerialAssistant::updateStatusBar() +{ + ui->packetLossLabel->setText(QString("丢包个数: %1").arg(lostPackets)); // 更新丢包个数标签 + ui->totalSentLabel->setText(QString("发送总计: %1 字节").arg(totalSentBytes)); // 更新发送字节数标签 + ui->totalReceivedLabel->setText(QString("接收总计: %1 字节").arg(totalReceivedBytes)); // 更新接收字节数标签 +} + +// 记录丢包信息 +void SerialAssistant::logLostPacket(const QByteArray &packet) +{ + if (logStream.device() != nullptr) { + QString packetString = QString::fromUtf8(packet); // 将包数据转换为字符串 + logStream << QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss") + << " - 丢包: " << packet.toHex() << " \n(字符串: " << packetString << ")\n"; // 写入日志 + logStream.flush(); // 刷新日志流 + } +} + diff --git a/serialassistant.h b/serialassistant.h new file mode 100644 index 0000000..24a607f --- /dev/null +++ b/serialassistant.h @@ -0,0 +1,54 @@ +#ifndef SERIALASSISTANT_H +#define SERIALASSISTANT_H + +#include +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE +namespace Ui { class SerialAssistant; } +QT_END_NAMESPACE + +class SerialAssistant : public QMainWindow +{ + Q_OBJECT + +public: + SerialAssistant(QWidget *parent = nullptr); + ~SerialAssistant(); + +private slots: + void on_openCloseButton_clicked(); + void on_sendButton_clicked(); + void readData(); + void on_clearReceiveButton_clicked(); + void on_clearSendButton_clicked(); + +private: + Ui::SerialAssistant *ui; + QSerialPort *serial; + void initSerialPort(); + void updatePortList(); + quint16 calculateCRC(const QByteArray &data); + bool verifyCRC(const QByteArray &packet); + void updateStatusBar(); + void logLostPacket(const QByteArray &packet); + + qint64 totalSentBytes; + qint64 totalReceivedBytes; + int totalPacketsSent; + int totalPacketsReceived; + int lostPackets; + + QByteArray receiveBuffer; + + static const QByteArray START_DELIMITER; + static const QByteArray END_DELIMITER; + QFile logFile; + QTextStream logStream; +}; + +#endif // SERIALASSISTANT_H diff --git a/serialassistant.pro b/serialassistant.pro new file mode 100644 index 0000000..7ecfe87 --- /dev/null +++ b/serialassistant.pro @@ -0,0 +1,24 @@ +QT += core gui serialport + +greaterThan(QT_MAJOR_VERSION, 4): QT += widgets + +CONFIG += c++17 + +# 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.0 + +SOURCES += \ + main.cpp \ + serialassistant.cpp + +HEADERS += \ + serialassistant.h + +FORMS += \ + serialassistant.ui + +# Default rules for deployment. +qnx: target.path = /tmp/$${TARGET}/bin +else: unix:!android: target.path = /opt/$${TARGET}/bin +!isEmpty(target.path): INSTALLS += target diff --git a/serialassistant.ui b/serialassistant.ui new file mode 100644 index 0000000..5b92d41 --- /dev/null +++ b/serialassistant.ui @@ -0,0 +1,251 @@ + + + SerialAssistant + + + + 0 + 0 + 812 + 598 + + + + Serial Assistant + + + + + + + + 150 + 16777215 + + + + 丢包率: 0% + + + + + + + + 150 + 16777215 + + + + 接收总计: 0 字节 + + + + + + + + 150 + 16777215 + + + + 发送总计: 0 字节 + + + + + + + Qt::Horizontal + + + + + + + 串口设置 + + + + + + 端口: + + + + + + + + + + 波特率: + + + + + + + + + + 数据位: + + + + + + + + + + 校验位: + + + + + + + + + + 停止位: + + + + + + + + + + 打开串口 + + + + + + + + + + 发送区 + + + + + + + + + + + 发送 + + + + + + + 清除发送 + + + + + + + 十六进制发送 + + + + + + + + + + + + + + + + 接收区 + + + + + + true + + + + + + + + + 清除接收 + + + + + + + 保存接收 + + + + + + + 十六进制显示 + + + + + + + 自动滚动 + + + + + + + + + + + + + + + + + + 0 + 0 + 812 + 25 + + + + + 文件 + + + + + 帮助 + + + + + + + + + +