Skip to content

Commit

Permalink
Add files via upload
Browse files Browse the repository at this point in the history
  • Loading branch information
xmt3061123 committed Jul 22, 2024
1 parent b6d21cc commit 0b2a79e
Show file tree
Hide file tree
Showing 5 changed files with 564 additions and 0 deletions.
10 changes: 10 additions & 0 deletions main.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#include "serialassistant.h"
#include <QApplication>

int main(int argc, char *argv[])
{
QApplication a(argc, argv);
SerialAssistant w; // 注意这里使用大写的 SerialAssistant
w.show();
return a.exec();
}
225 changes: 225 additions & 0 deletions serialassistant.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,225 @@
#include "serialassistant.h"
#include "ui_serialassistant.h"
#include <QMessageBox>
#include <QDateTime>
#include <QDebug>

// 定义静态成员:增加头尾效验
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<QSerialPort::DataBits>(ui->dataBitsCombo->currentData().toInt()));
serial->setParity(static_cast<QSerialPort::Parity>(ui->parityCombo->currentData().toInt()));
serial->setStopBits(static_cast<QSerialPort::StopBits>(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(); // 刷新日志流
}
}

54 changes: 54 additions & 0 deletions serialassistant.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
#ifndef SERIALASSISTANT_H
#define SERIALASSISTANT_H

#include <QMainWindow>
#include <QtSerialPort/QSerialPort>
#include <QtSerialPort/QSerialPortInfo>
#include <QByteArray>
#include <QFile>
#include <QTextStream>

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
24 changes: 24 additions & 0 deletions serialassistant.pro
Original file line number Diff line number Diff line change
@@ -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
Loading

0 comments on commit 0b2a79e

Please sign in to comment.