Files
2026-02-01 22:23:06 +08:00

3731 lines
125 KiB
C++
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//#if _MSC_VER >= 1600
//#pragma execution_character_set("utf-8")
//#endif
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QMessageBox>
#include <QDebug>
#include <QPoint>
#include <QSettings>
#include <QFileDialog>
#include <QAbstractScrollArea>
#include <QScrollBar>
#include <QStyledItemDelegate>
#include <QSerialPortInfo>
#include <QScrollArea>
#include <QGuiApplication>
#include <QScreen>
#include <QRegularExpression>
#include <QRegularExpressionValidator>
#include <algorithm> // 添加算法库支持std::sort
#include <QProgressDialog>
#include <QtXlsx>
#include "commonhelper.h"
#include "frameless_helper.h"
#include "sendhistorydialog.h" // 添加发送历史对话框头文件
#include "xlsxdocument.h"
#include "xlsxworksheet.h"
#include "xlsxcellrange.h"
#include "xlsxsheetmodel.h"
using namespace QXlsx;
#define BUTTON_REPEAT_INTERVAL 400
#define COLUMN_MAX 23
#define COLUMN_VALID 21
#define COLUMN_SENDBUTTON 22
MainWindow::MainWindow(QWidget* parent, ToolkitBase_Interface* pBaseInterface) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
sendCount = 0;
receiveCount = 0;
dataSize = 0;
b_mqtt_connected = false;
bSendFile = false;
updateBuffer = NULL;
m_sendHistoryDialog = nullptr; // 初始化发送历史对话框指针
// 创建日志记录对象
m_logRecord = new LogRecord(this);
m_serialPort = new QSerialPort(this);
timer = new QTimer(this);
connect(timer, &QTimer::timeout, this, &MainWindow::timerUpdate);
sendFileTimer = new QTimer(this);
connect(sendFileTimer, &QTimer::timeout, this, &MainWindow::sendFileTimerUpdate);
PressTimer = new QTimer(this);
connect(PressTimer, &QTimer::timeout, this, &MainWindow::PressTimerUpdate);
// 初始化定时器 - 修改为每秒检查一次
regularTimer = new QTimer(this);
isRegularSending = false;
isPausedSending = false; // 初始化暂停状态变量
currentCommandIndex = 0;
elapsedSeconds = 0; // 添加累计秒数计数器
regularTimer->setInterval(1000); // 固定为1秒间隔
// 修改连接到新的发送方法
connect(regularTimer, SIGNAL(timeout()), this, SLOT(sendRegularCommandWithPort()));
initUi();
// 初始化主界面下拉框
updatePortsInComboBoxes();
connections();
QVector<QComboBox*>boxVec;
boxVec << ui->baudRateComboBox << ui->parityComboBox << ui->dataBitsComboBox
<< ui->stopBitsComboBox << ui->styleComboBox << ui->comComboBox;
// 设置代理
QStyledItemDelegate* delegate = new QStyledItemDelegate(this);
foreach (QComboBox* comboBox, boxVec)
{
comboBox->setItemDelegate(delegate);
}
// 发送文件功能已搭建完成,暂时不开放 2019/1/22
// ui->label_17->hide();
// ui->filePathLineEdit->hide();
// ui->openFileButton->hide();
// ui->sendFileButton->hide();
}
MainWindow::~MainWindow()
{
// 确保程序退出时停止日志记录
if (m_logRecord->isLogging())
{
m_logRecord->stopLog();
}
// 确保停止定时器
if (m_sendHistoryDialog && m_sendHistoryDialog->isTimingActive())
{
m_sendHistoryDialog->stopTimingCount();
}
// 释放发送历史对话框
if (m_sendHistoryDialog)
{
delete m_sendHistoryDialog;
m_sendHistoryDialog = nullptr;
}
// 关闭和释放所有串口
for (auto it = m_serialPorts.begin(); it != m_serialPorts.end(); ++it)
{
if (it.value()->isOpen())
{
it.value()->close();
}
delete it.value();
}
m_serialPorts.clear();
delete ui;
}
bool MainWindow::eventFilter(QObject* obj, QEvent* event)
{
if (obj == ui->lblTitle)
{
if (event->type() == QEvent::MouseButtonPress)
{
QMouseEvent* mouseEvent = dynamic_cast<QMouseEvent*>(event);
if (mouseEvent->button() == Qt::LeftButton)
{
mousePressed = true;
mousePoint = mouseEvent->globalPos() - pos();
mouseEvent->accept();
return true;
}
}
else if (event->type() == QEvent::MouseMove)
{
QMouseEvent* mouseEvent = dynamic_cast<QMouseEvent*>(event);
if (mousePressed && this->windowState() != Qt::WindowMaximized)
{
if (mouseEvent->globalY() <= QGuiApplication::primaryScreen()->availableGeometry().bottom())
{
move(mouseEvent->globalPos() - mousePoint);
mouseEvent->accept();
}
else
{
QCursor::setPos(
QCursor::pos().x(),
QGuiApplication::primaryScreen()->availableGeometry().bottom()
);
}
return true;
}
}
else if (event->type() == QEvent::MouseButtonRelease)
{
QMouseEvent* mouseEvent = dynamic_cast<QMouseEvent*>(event);
if (mouseEvent->button() == Qt::LeftButton)
{
mousePressed = false;
mouseEvent->accept();
return true;
}
}
}
return QObject::eventFilter(obj, event);
}
void MainWindow::on_btnCloseMenu_clicked()
{
close();
}
void MainWindow::on_btnMinMenu_clicked()
{
showMinimized();
}
// void MainWindow::openSerialPort()
// {
// // 先关闭可能已经打开的串口
// if (m_serialPort->isOpen()) {
// m_serialPort->close();
// QThread::msleep(100); // 等待串口完全关闭
// }
// // 更新串口配置
// updateComSettings();
// // 检查串口名称是否有效
// if (currentSettings.name.isEmpty()) {
// CommonHelper::ShowMessageBoxError(tr("串口名称无效,请重新选择串口!"));
// return;
// }
// // 设置串口参数
// m_serialPort->setPortName(currentSettings.name);
// m_serialPort->setBaudRate(currentSettings.baudRate);
// m_serialPort->setDataBits(currentSettings.dataBits);
// m_serialPort->setParity(currentSettings.parity);
// m_serialPort->setStopBits(currentSettings.stopBits);
// m_serialPort->setFlowControl(QSerialPort::NoFlowControl);
// // 尝试打开串口
// if (m_serialPort->open(QIODevice::ReadWrite))
// {
// // 更新UI状态
// ui->connectInfo->setPixmap(QPixmap(":/skin/image/info.png"));
// ui->connectInfo->setScaledContents(true);
// ui->connectBtn->setText("关闭串口");
// ui->NewOpenComButton->setText("关闭串口");
// ui->NewRegularSentButton->setEnabled(true);
// // 提示成功
// QString successMsg = tr("串口打开成功!\n串口: %1\n波特率: %2\n数据位: %3\n校验位: %4\n停止位: %5")
// .arg(currentSettings.name)
// .arg(currentSettings.baudRate)
// .arg(currentSettings.dataBits)
// .arg(currentSettings.parity)
// .arg(currentSettings.stopBits);
// CommonHelper::ShowMessageBoxInfo(successMsg);
// }
// else
// {
// // 获取详细错误信息
// QString errorString = m_serialPort->errorString();
// QSerialPort::SerialPortError error = m_serialPort->error();
// QString errorMsg;
// switch (error) {
// case QSerialPort::DeviceNotFoundError:
// errorMsg = tr("找不到指定的串口设备: %1").arg(currentSettings.name);
// break;
// case QSerialPort::PermissionError:
// errorMsg = tr("串口 %1 已被其他程序占用或权限不足").arg(currentSettings.name);
// break;
// case QSerialPort::OpenError:
// errorMsg = tr("无法打开串口 %1可能已被占用").arg(currentSettings.name);
// break;
// case QSerialPort::NotOpenError:
// errorMsg = tr("串口未打开");
// break;
// default:
// errorMsg = tr("打开串口 %1 失败: %2").arg(currentSettings.name).arg(errorString);
// break;
// }
// CommonHelper::ShowMessageBoxError(errorMsg + tr("\n\n建议\n1. 检查设备是否正确连接\n2. 确认串口未被其他程序占用\n3. 尝试刷新串口列表"));
// // 确保UI状态正确
// ui->connectBtn->setText("打开串口");
// ui->NewOpenComButton->setText("打开串口");
// ui->NewRegularSentButton->setEnabled(false);
// }
// }
void MainWindow::openSerialPort()
{
// 先关闭可能已经打开的串口
if (m_serialPort->isOpen())
{
m_serialPort->close();
QThread::msleep(100); // 等待串口完全关闭
}
// 更新串口配置
updateComSettings();
// 检查串口名称是否有效
if (currentSettings.name.isEmpty())
{
CommonHelper::ShowMessageBoxError(tr("串口名称无效,请重新选择串口!"));
return;
}
// 设置串口参数
m_serialPort->setPortName(currentSettings.name);
m_serialPort->setBaudRate(currentSettings.baudRate);
m_serialPort->setDataBits(currentSettings.dataBits);
m_serialPort->setParity(currentSettings.parity);
m_serialPort->setStopBits(currentSettings.stopBits);
m_serialPort->setFlowControl(QSerialPort::NoFlowControl);
// 尝试打开串口
if (m_serialPort->open(QIODevice::ReadWrite))
{
// 更新UI状态
ui->connectInfo->setPixmap(QPixmap(":/skin/image/info.png"));
ui->connectInfo->setScaledContents(true);
ui->connectBtn->setText("关闭串口");
ui->NewOpenComButton->setText("关闭串口");
// 删除这行,无论如何都不禁用定时发送按钮
// ui->NewRegularSentButton->setEnabled(true);
// 提示成功
QString successMsg = tr("串口打开成功!\n串口: %1\n波特率: %2\n数据位: %3\n校验位: %4\n停止位: %5")
.arg(currentSettings.name)
.arg(currentSettings.baudRate)
.arg(currentSettings.dataBits)
.arg(currentSettings.parity)
.arg(currentSettings.stopBits);
CommonHelper::ShowMessageBoxInfo(successMsg);
}
else
{
// 获取详细错误信息
QString errorString = m_serialPort->errorString();
QSerialPort::SerialPortError error = m_serialPort->error();
QString errorMsg;
switch (error)
{
case QSerialPort::DeviceNotFoundError:
errorMsg = tr("找不到指定的串口设备: %1").arg(currentSettings.name);
break;
case QSerialPort::PermissionError:
errorMsg = tr("串口 %1 已被其他程序占用或权限不足").arg(currentSettings.name);
break;
case QSerialPort::OpenError:
errorMsg = tr("无法打开串口 %1可能已被占用").arg(currentSettings.name);
break;
case QSerialPort::NotOpenError:
errorMsg = tr("串口未打开");
break;
default:
errorMsg = tr("打开串口 %1 失败: %2").arg(currentSettings.name).arg(errorString);
break;
}
CommonHelper::ShowMessageBoxError(errorMsg + tr("\n\n建议:\n1. 检查设备是否正确连接\n2. 确认串口未被其他程序占用\n3. 尝试刷新串口列表"));
// 确保UI状态正确
ui->connectBtn->setText("打开串口");
ui->NewOpenComButton->setText("打开串口");
// 删除这行,避免禁用定时发送按钮
// ui->NewRegularSentButton->setEnabled(false);
}
}
void MainWindow::closeSerialPort()
{
if (m_serialPort->isOpen())
{
m_serialPort->close();
}
// 关闭串口时停止定时发送和累计时间
if (isRegularSending)
{
regularTimer->stop();
isRegularSending = false;
isPausedSending = false; // 重置暂停状态
ui->RegularlySentButton->setText("定时发送");
ui->NewRegularSentButton->setText("定时发送");
// 停止计时器
if (m_sendHistoryDialog && m_sendHistoryDialog->isTimingActive())
{
m_sendHistoryDialog->stopTimingCount();
m_sendHistoryDialog->setPauseState(false); // 重置发送历史对话框的暂停状态
}
}
}
// void MainWindow::writeData(const QString &data)
// {
// QByteArray arr;
// if (ui->isHexCheckBox->isChecked())
// {
// arr = QString2Hex(data);
// m_serialPort->write(arr);
// // 如果没有发送数据,在已发送编辑框就什么也不显示
// if (!data.isEmpty())
// {
// ui->sendTextEdit->append(data.toUpper());
// // 记录发送的日志
// if (m_logRecord->isLogging()) {
// m_logRecord->logSend(data.toUpper());
// }
// // 添加到发送历史记录
// showSendHistoryDialog();
// if (m_sendHistoryDialog) {
// m_sendHistoryDialog->addSendRecord(data.toUpper(), true);
// }
// }
// }
// else
// {
// arr = data.toLatin1();
// m_serialPort->write(arr);
// // 如果没有发送数据,在已发送编辑框就什么也不显示
// if (!data.isEmpty())
// {
// ui->sendTextEdit->append(data);
// // 记录发送的日志
// if (m_logRecord->isLogging()) {
// m_logRecord->logSend(data);
// }
// // 添加到发送历史记录
// showSendHistoryDialog();
// if (m_sendHistoryDialog) {
// m_sendHistoryDialog->addSendRecord(data, false);
// }
// }
// }
// // 已发送计数
// sendCount += arr.size();
// ui->sendCountLabel->setNum(sendCount);
// // 当前文本50行时清除历史
// if ( 50 == ui->sendTextEdit->document()->lineCount())
// {
// // 读一行删一行
// QTextCursor txtcur= ui->sendTextEdit->textCursor();
// txtcur.setPosition(0);
// txtcur.movePosition(QTextCursor::EndOfLine,QTextCursor::KeepAnchor);
// QString qstr= txtcur.selectedText();
// txtcur.movePosition(QTextCursor::Down,QTextCursor::KeepAnchor);
// txtcur.movePosition(QTextCursor::StartOfLine,QTextCursor::KeepAnchor);
// txtcur.removeSelectedText();
// }
// }
// 在 writeData 方法中修改历史记录添加逻辑
void MainWindow::writeData(const QString& data)
{
QByteArray arr;
if (ui->isHexCheckBox->isChecked())
{
arr = QString2Hex(data);
m_serialPort->write(arr);
// 如果没有发送数据,在已发送编辑框就什么也不显示
if (!data.isEmpty())
{
ui->sendTextEdit->append(data.toUpper());
// 记录发送的日志
if (m_logRecord->isLogging())
{
m_logRecord->logSend(data.toUpper());
}
// 添加到发送历史记录 - 使用 true 表示HEX格式
showSendHistoryDialog();
if (m_sendHistoryDialog)
{
m_sendHistoryDialog->addSendRecord(data.toUpper(), true);
}
}
}
else
{
arr = data.toLatin1();
m_serialPort->write(arr);
// 如果没有发送数据,在已发送编辑框就什么也不显示
if (!data.isEmpty())
{
ui->sendTextEdit->append(data);
// 记录发送的日志
if (m_logRecord->isLogging())
{
m_logRecord->logSend(data);
}
// 添加到发送历史记录 - 使用 false 表示ASCII格式
showSendHistoryDialog();
if (m_sendHistoryDialog)
{
m_sendHistoryDialog->addSendRecord(data, false);
}
}
}
// 已发送计数
sendCount += arr.size();
ui->sendCountLabel->setNum(sendCount);
// 当前文本50行时清除历史
if (50 == ui->sendTextEdit->document()->lineCount())
{
// 读一行删一行
QTextCursor txtcur = ui->sendTextEdit->textCursor();
txtcur.setPosition(0);
txtcur.movePosition(QTextCursor::EndOfLine, QTextCursor::KeepAnchor);
QString qstr = txtcur.selectedText();
txtcur.movePosition(QTextCursor::Down, QTextCursor::KeepAnchor);
txtcur.movePosition(QTextCursor::StartOfLine, QTextCursor::KeepAnchor);
txtcur.removeSelectedText();
}
}
void MainWindow::readData()
{
// 读取串口缓冲区的所有数据给临时变量data
QByteArray data = m_serialPort->readAll();
QString str = ShowHex(data);
// 接收计数
receiveCount += data.count();
ui->receiveCountLabel->setNum(receiveCount);
// 将串口 读 的数据显示在 接收 数据文本框
if (!data.isEmpty())
{
ui->receiveTextEdit->append(str.toUpper());
// 记录接收的日志
if (m_logRecord->isLogging())
{
m_logRecord->logReceive(str.toUpper());
}
}
// 当前文本50行时清除历史
if (50 == ui->receiveTextEdit->document()->lineCount())
{
// 读一行删一行
QTextCursor txtcur = ui->receiveTextEdit->textCursor();
txtcur.setPosition(0);
txtcur.movePosition(QTextCursor::EndOfLine, QTextCursor::KeepAnchor);
QString qstr = txtcur.selectedText();
txtcur.movePosition(QTextCursor::Down, QTextCursor::KeepAnchor);
txtcur.movePosition(QTextCursor::StartOfLine, QTextCursor::KeepAnchor);
txtcur.removeSelectedText();
}
}
void MainWindow::sendDataSlot()
{
ui->stackedWidget->setCurrentIndex(1);
// 机器型号
ui->modelNumLabel->setText(ui->modelNumLineEdit->text());
if (ui->modelNumLineEdit->text().isEmpty())
{
ui->modelNumLabel->setText("无型号");
}
// 生成按钮
createButtons();
// 读串口数据
readData();
}
void MainWindow::backSlot()
{
// 返回到上一页
ui->stackedWidget->setCurrentIndex(0);
ui->tabWidget->clear();
// 清除界面上的按钮
clearButtons();
}
void MainWindow::addItemSlot()
{
ui->removeItemButton->setEnabled(true);
ui->clearAllBtn->setEnabled(true);
// 获取表单行数
int rowCount = ui->tableWidget->rowCount();
// 插入新行
ui->tableWidget->insertRow(rowCount);
// 创建COLUMN_MAX列的空项目
for (int col = 0; col < COLUMN_MAX; ++col)
{
QTableWidgetItem* item = new QTableWidgetItem();
if (col == COLUMN_VALID) // 有效列
{
item->setCheckState(Qt::Checked);
item->setFlags(item->flags() & ~Qt::ItemIsEditable);
}
ui->tableWidget->setItem(rowCount, col, item);
}
// 添加发送按钮
QPushButton* sendButton = new QPushButton("发送");
sendButton->setProperty("row", rowCount);
connect(sendButton, SIGNAL(clicked()), this, SLOT(onSendButtonClicked()));
ui->tableWidget->setCellWidget(rowCount, COLUMN_SENDBUTTON, sendButton);
update();
ui->tableWidget->verticalScrollBar()->setValue(ui->tableWidget->verticalScrollBar()->maximum());
}
void MainWindow::removeItemSlot()
{
// 判断是否选择了一行
if (ui->tableWidget->currentItem() && ui->tableWidget->currentItem()->isSelected())
{
// 调用自定义消息提示框
int result = CommonHelper::ShowMessageBoxQuesion("是否删除当前已选中项?");
if (result == 1)
{
// 将对应的表格清除
QModelIndexList indexList = ui->tableWidget->selectionModel()->selectedIndexes();
while (!indexList.isEmpty())
{
QModelIndex index = indexList.first();
ui->tableWidget->model()->removeRow(index.row());
indexList = ui->tableWidget->selectionModel()->selectedIndexes();
}
}
else
{
return;
}
}
// 如果一行都没有选中则提示
else
{
CommonHelper::ShowMessageBoxError("请选择需要删除的行!");
}
if (0 == ui->tableWidget->rowCount())
{
ui->removeItemButton->setEnabled(false);
ui->clearAllBtn->setEnabled(false);
}
else
{
ui->removeItemButton->setEnabled(true);
ui->clearAllBtn->setEnabled(true);
}
}
void MainWindow::clearAllSlot()
{
// 调用自定义消息提示框
int result = CommonHelper::ShowMessageBoxQuesion("是否清空全部数据?");
if (result == 1)
{
// 先将表格清除
ui->tableWidget->setRowCount(0);
// 再循环删除列表中的按钮对象
clearButtons();
}
else
{
return;
}
if (0 == ui->tableWidget->rowCount())
{
ui->removeItemButton->setEnabled(false);
ui->clearAllBtn->setEnabled(false);
}
else
{
ui->removeItemButton->setEnabled(true);
ui->clearAllBtn->setEnabled(true);
}
}
void MainWindow::loadFileSlot()
{
QFileDialog* fileDialog = new QFileDialog(this);
fileDialog->setWindowTitle(tr("打开文件"));
fileDialog->setDirectory("../config");
QStringList filters;
filters << "配置文件(*.ini *.xlsx)"
<< "INI配置文件(*.ini)"
<< "XLSX文件(*.xlsx)"
<< "所有文件(*)";
fileDialog->setNameFilters(filters);
if (fileDialog->exec() == QDialog::Accepted)
{
QString loadFilePath = fileDialog->selectedFiles()[0];
// 先将表格清除
ui->tableWidget->setRowCount(0);
Document* doc = new Document(loadFilePath);
CellRange cellRange = doc->dimension();
Worksheet* sheet = dynamic_cast<Worksheet*>(doc->workbook()->sheet(0));
m_sheetName = sheet->sheetName();
if (sheet != nullptr)
{
// 根据信息初始化QTabelWidget
int row = cellRange.rowCount();
int column = cellRange.columnCount();
// ui->tableWidget->setColumnCount(cellRange.columnCount());
ui->tableWidget->setRowCount(cellRange.rowCount() - 1); // 去除第一行
ui->tableWidget->resizeColumnsToContents();
ui->tableWidget->setEditTriggers(QAbstractItemView::AllEditTriggers);
for (int i = 2; i <= cellRange.lastRow(); i++)
{
bool hasValidCommand = false;
for (int j = 1; j <= cellRange.lastColumn(); j++)
{
QTableWidgetItem* item = new QTableWidgetItem();
item->setTextAlignment(Qt::AlignVCenter | Qt::AlignCenter);
QString value = sheet->read(i, j).toString();
item->setText(value);
ui->tableWidget->setItem(i - 2, j - 1, item);
if (!value.isEmpty())
{
hasValidCommand = true;
}
// qDebug("%d,%d:%s", i, j, sheet->read(i,j).toString().toStdString().c_str());
}
// 第22列有效性列 - 根据是否有设备指令判断
QTableWidgetItem* validItem = new QTableWidgetItem();
validItem->setFlags(validItem->flags() & ~Qt::ItemIsEditable);
validItem->setCheckState(hasValidCommand ? Qt::Checked : Qt::Unchecked);
ui->tableWidget->setItem(i, COLUMN_VALID, validItem);
// 第23列添加发送按钮
QPushButton* sendButton = new QPushButton("发送");
sendButton->setProperty("row", i);
connect(sendButton, SIGNAL(clicked()), this, SLOT(onMultiDeviceSendButtonClicked()));
ui->tableWidget->setCellWidget(i, COLUMN_SENDBUTTON, sendButton);
}
// 提示加载完成
CommonHelper::ShowMessageBoxInfo(tr("CSV文件加载完成共加载 %1 行数据").arg(row - 1));
}
}
if (0 == ui->tableWidget->rowCount())
{
ui->removeItemButton->setEnabled(false);
ui->clearAllBtn->setEnabled(false);
}
else
{
ui->removeItemButton->setEnabled(true);
ui->clearAllBtn->setEnabled(true);
}
}
//void MainWindow::loadFileSlot()
//{
// QFileDialog *fileDialog = new QFileDialog(this);
// fileDialog->setWindowTitle(tr("打开文件"));
// fileDialog->setDirectory("./");
// QStringList filters;
// filters << "配置文件(*.ini *.csv)"
// << "INI配置文件(*.ini)"
// << "CSV文件(*.csv)"
// << "所有文件(*)";
// fileDialog->setNameFilters(filters);
// if (fileDialog->exec() == QDialog::Accepted)
// {
// QString loadFilePath = fileDialog->selectedFiles()[0];
// // 先将表格清除
// ui->tableWidget->setRowCount(0);
// // 创建进度对话框
// QProgressDialog progress(tr("正在F..."), tr("取消"), 0, 100, this);
// progress.setWindowTitle(tr("请稍候"));
// progress.setWindowModality(Qt::WindowModal);
// progress.setMinimumDuration(0); // 立即显示
// progress.setValue(10);
// qApp->processEvents(); // 确保对话框显示
// // 获取文件信息
// QFileInfo fileInfo(loadFilePath);
// qint64 fileSize = fileInfo.size();
// // 更新进度对话框文本,显示文件信息
// progress.setLabelText(tr("正在读取文件: %1\n大小: %2 KB")
// .arg(fileInfo.fileName())
// .arg(fileSize / 1024.0, 0, 'f', 2));
// progress.setValue(15);
// qApp->processEvents();
// if (loadFilePath.endsWith(".csv", Qt::CaseInsensitive))
// {
// // 处理CSV文件
// QFile file(loadFilePath);
// if (!file.open(QIODevice::ReadOnly| QIODevice::Text))
// {
// progress.close();
// CommonHelper::ShowMessageBoxError(tr("无法打开CSV文件"));
// return;
// }
// QTextStream in(&file);
// int row = 0;
// int totalLines = 0;
// // 设置文件编码
// #if defined(Q_OS_WIN)
// #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
// in.setCodec("GBK");
// #else
// in.setEncoding(QStringConverter::System);
// #endif
// #else
// #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
// in.setCodec("UTF-8");
// #else
// in.setEncoding(QStringConverter::Utf8);
// #endif
// #endif
// // 先计算总行数用于进度显示
// QString fileContent = in.readAll();
// totalLines = fileContent.count('\n') + 1;
// in.seek(0); // 重置文件指针
// progress.setMaximum(totalLines);
// progress.setValue(20);
// progress.setLabelText(tr("正在读取文件: %1\n行数统计完成共 %2 行")
// .arg(fileInfo.fileName())
// .arg(totalLines));
// qApp->processEvents();
// // 强制跳过第一行(标题行),无论文件是否为空
// if (!in.atEnd()) {
// QString header = in.readLine();
// qDebug() << "已成功跳过CSV标题行: [" << header << "]";
// progress.setValue(25);
// qApp->processEvents();
// } else {
// qDebug() << "CSV文件为空或只有标题行";
// file.close();
// progress.close();
// CommonHelper::ShowMessageBoxError(tr("CSV文件为空或格式不正确"));
// return;
// }
// // 读取每一行数据
// while (!in.atEnd())
// {
// QString line = in.readLine().trimmed();
// if (line.isEmpty()) continue;
// QStringList fields;
// // 解析CSV行支持引号中的逗号
// bool inQuote = false;
// QString field;
// int num = line.length();
// for (int i = 0; i < num; ++i) {
// QChar c = line.at(i);
// if (c == "'") {
// inQuote = !inQuote;
// } else if (c == ',' && !inQuote) {
// fields.append(field.trimmed());
// field.clear();
// } else {
// field.append(c);
// }
// }
// fields.append(field.trimmed());
// // 确保有足够的字段数 - CSV实际有13列
// while (fields.size() < 2) {
// fields.append("");
// }
// // 插入新行
// ui->tableWidget->insertRow(row);
// // 创建23列的表格项全部初始化
// for (int col = 0; col < COLUMN_MAX; ++col) {
// QTableWidgetItem *item = new QTableWidgetItem();
// if (col == COLUMN_VALID) { // 有效列
// item->setCheckState(Qt::Checked);
// item->setFlags(item->flags() & ~Qt::ItemIsEditable);
// }
// ui->tableWidget->setItem(row, col, item);
// }
// // 填充CSV数据到表格 - 修正列索引映射
// // CSV格式0=序号, 1=时间轴, 2-13=12列设备数据
// // 第0列序号 (CSV第0列)
// if (fields.size() > 0) {
// ui->tableWidget->item(row, 0)->setText(fields[0]);
// }
// // 第1列时间轴转换为毫秒 (CSV第1列)
// if (fields.size() > 1) {
// QString timeStr = fields[1];
// int timeInSeconds = timeStr.toInt();
// ui->tableWidget->item(row, 1)->setText(QString::number(timeInSeconds));
// }
// // 第2-16列14列设备数据 (CSV第2-16列)
// for (int col = 2; col < COLUMN_VALID; ++col) {
// if (col < ui->tableWidget->columnCount() && col < fields.size()) {
// ui->tableWidget->item(row, col)->setText(fields[col]);
// } else if (col < ui->tableWidget->columnCount()) {
// ui->tableWidget->item(row, col)->setText(""); // 超出原CSV列数的部分设置为空
// }
// }
// // 第22列有效性列 - 根据是否有设备指令判断
// QTableWidgetItem *validItem = new QTableWidgetItem();
// validItem->setFlags(validItem->flags() & ~Qt::ItemIsEditable);
// ui->tableWidget->setItem(row, COLUMN_VALID, validItem);
// // 检查是否有任何设备有指令或协议
// bool hasValidCommand = false;
// // 检查设备1-4的指令和协议列每个设备占3列指令、协议、通讯
// // 设备1: 列2,3,4 设备2: 列5,6,7 设备3: 列8,9,10 设备4: 列11,12,13
// for (int devStart = 2; devStart < COLUMN_VALID; devStart += 3) {
// // 检查指令列(devStart)和协议列(devStart+1)
// if ((devStart < fields.size() && !fields[devStart].isEmpty()) ||
// (devStart + 1 < fields.size() && !fields[devStart + 1].isEmpty())) {
// hasValidCommand = true;
// break;
// }
// }
// ui->tableWidget->item(row, COLUMN_VALID)->setCheckState(hasValidCommand ? Qt::Checked : Qt::Unchecked);
// // 第18列添加发送按钮
// QPushButton *sendButton = new QPushButton("发送");
// sendButton->setProperty("row", row);
// connect(sendButton, SIGNAL(clicked()), this, SLOT(onMultiDeviceSendButtonClicked()));
// ui->tableWidget->setCellWidget(row, COLUMN_SENDBUTTON, sendButton);
// row++;
// // 更新进度条
// int currentProgress = 25 + (row * 70 / totalLines);
// if (currentProgress > 95) currentProgress = 95;
// progress.setValue(currentProgress);
// // 每处理10行更新一次提示信息避免频繁更新导致界面卡顿
// if (row % 10 == 0) {
// progress.setLabelText(tr("正在读取文件: %1\n已处理 %2/%3 行")
// .arg(fileInfo.fileName())
// .arg(row)
// .arg(totalLines - 1)); // 减1是因为跳过了标题行
// qApp->processEvents();
// }
// if (progress.wasCanceled()) {
// // 用户取消了操作,回滚已加载的内容
// ui->tableWidget->setRowCount(0);
// file.close();
// CommonHelper::ShowMessageBoxInfo(tr("文件加载已取消"));
// return;
// }
// }
// file.close();
// // 完成进度
// progress.setValue(100);
// progress.setLabelText(tr("文件加载完成: %1\n共处理 %2 行数据")
// .arg(fileInfo.fileName())
// .arg(row));
// qApp->processEvents();
// QThread::msleep(500); // 显示完成状态一小段时间
// // 提示加载完成
// CommonHelper::ShowMessageBoxInfo(tr("CSV文件加载完成共加载 %1 行数据").arg(row));
// }
// else
// {
// // INI文件处理
// progress.setValue(30);
// progress.setLabelText(tr("正在读取INI配置文件: %1").arg(fileInfo.fileName()));
// qApp->processEvents();
// QSettings *loadSettings = nullptr;
// loadSettings = new QSettings(loadFilePath, QSettings::IniFormat);
// // 设置本地编码 - Qt 6中不再需要setIniCodec
// #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
// // 使用Qt 5的方式
// #if defined(Q_OS_WIN)
// loadSettings->setIniCodec("GBK");
// #else
// loadSettings->setIniCodec("UTF-8");
// #endif
// #endif
// progress.setValue(40);
// qApp->processEvents();
// // 读按钮数据
// loadSettings->beginGroup("ButtonData");
// int nIndex = 0;
// int keyCount = 0;
// // 先获取键值数量,用于进度计算
// QStringList allKeys = loadSettings->childKeys();
// keyCount = allKeys.size();
// progress.setLabelText(tr("正在读取INI配置文件: %1\n共有 %2 个按钮配置")
// .arg(fileInfo.fileName())
// .arg(keyCount));
// qApp->processEvents();
// while (true)
// {
// // 没有找到key-value就跳出循环
// if (loadSettings->value(QString("%1").arg(nIndex)).isNull())
// {
// break;
// }
// // 更新进度
// int currentProgress = 40 + (nIndex * 50 / (keyCount > 0 ? keyCount : 1));
// if (currentProgress > 90) currentProgress = 90;
// progress.setValue(currentProgress);
// // 每处理5个配置项更新一次提示
// if (nIndex % 5 == 0) {
// progress.setLabelText(tr("正在读取INI配置: %1\n已处理 %2/%3 项")
// .arg(fileInfo.fileName())
// .arg(nIndex)
// .arg(keyCount));
// qApp->processEvents();
// }
// if (progress.wasCanceled()) {
// delete loadSettings;
// ui->tableWidget->setRowCount(0);
// CommonHelper::ShowMessageBoxInfo(tr("文件加载已取消"));
// return;
// }
// // 每循环一次就插入新一行
// ui->tableWidget->insertRow(nIndex);
// // 用于保存取出的临时数据字符串列表
// QStringList tempStrList = loadSettings->value(QString("%1").arg(nIndex)).toStringList();
// QString tempStr = tempStrList[0];
// for (int i = 1; i < tempStrList.size(); i++)
// {
// tempStr += ",";
// tempStr += tempStrList[i];
// }
// // 修改使用 QRegularExpression 而非 QRegExp
// QStringList buttonInfoList = tempStr.split(QRegularExpression("[|]"));
// // 按照新格式解析:时间|按钮分类|按钮名称|鼠标按下命令|鼠标松开命令|是否有效
// QString buttonTimeStr = buttonInfoList[0];
// QString buttonGroupStr = buttonInfoList[1];
// QString buttonNameStr = buttonInfoList[2];
// QString mouseDownStr = buttonInfoList[3];
// QString mouseUpStr = buttonInfoList[4];
// QVariant buttonAvailStr = buttonInfoList[5];
// QTableWidgetItem *buttonTimeItem = new QTableWidgetItem;
// QTableWidgetItem *buttonGroupItem = new QTableWidgetItem;
// QTableWidgetItem *buttonNameItem = new QTableWidgetItem;
// QTableWidgetItem *mouseDownItem = new QTableWidgetItem;
// QTableWidgetItem *mouseUpItem = new QTableWidgetItem;
// QTableWidgetItem *buttonAvailItem = new QTableWidgetItem;
// buttonAvailItem->setFlags(buttonAvailItem->flags() & ~Qt::ItemIsEditable);
// ui->tableWidget->setItem(nIndex, 0, buttonTimeItem);
// ui->tableWidget->setItem(nIndex, 1, buttonGroupItem);
// ui->tableWidget->setItem(nIndex, 2, buttonNameItem);
// ui->tableWidget->setItem(nIndex, 3, mouseDownItem);
// ui->tableWidget->setItem(nIndex, 4, mouseUpItem);
// ui->tableWidget->setItem(nIndex, 5, buttonAvailItem);
// // 添加发送按钮
// QPushButton *sendButton = new QPushButton("发送");
// sendButton->setProperty("row", nIndex); // 存储按钮所在的行号
// connect(sendButton, SIGNAL(clicked()), this, SLOT(onSendButtonClicked()));
// ui->tableWidget->setCellWidget(nIndex, 6, sendButton);
// buttonTimeItem->setText(buttonTimeStr);
// buttonGroupItem->setText(buttonGroupStr);
// buttonNameItem->setText(buttonNameStr);
// mouseDownItem->setText(mouseDownStr);
// mouseUpItem->setText(mouseUpStr);
// switch (buttonAvailStr.toBool())
// {
// case 0:
// buttonAvailItem->setCheckState(Qt::Unchecked);
// break;
// case 1:
// buttonAvailItem->setCheckState(Qt::Checked);
// break;
// }
// nIndex++;
// }
// loadSettings->endGroup();
// progress.setValue(92);
// progress.setLabelText(tr("正在读取串口配置..."));
// qApp->processEvents();
// // 读串口配置数据
// loadSettings->beginGroup("comm");
// ui->baudRateComboBox->setCurrentIndex(loadSettings->value("BaudRate").toInt());
// ui->parityComboBox->setCurrentIndex(loadSettings->value("Parity").toInt());
// ui->dataBitsComboBox->setCurrentIndex(loadSettings->value("Bytesize").toInt());
// ui->stopBitsComboBox->setCurrentIndex(loadSettings->value("StopBits").toInt());
// bool isHex = loadSettings->value("SendByHex").toBool();
// ui->isHexCheckBox->setChecked(isHex);
// ui->modelNumLineEdit->setText(loadSettings->value("Name").toString());
// loadSettings->endGroup();
// delete loadSettings;
// progress.setValue(100);
// progress.setLabelText(tr("文件加载完成: %1\n共加载 %2 项配置")
// .arg(fileInfo.fileName())
// .arg(nIndex));
// qApp->processEvents();
// QThread::msleep(500); // 显示完成状态一小段时间
// // 提示加载完成
// CommonHelper::ShowMessageBoxInfo(tr("INI文件加载完成共加载 %1 项配置").arg(nIndex));
// }
// }
// if (0 == ui->tableWidget->rowCount())
// {
// ui->removeItemButton->setEnabled(false);
// ui->clearAllBtn->setEnabled(false);
// }else {
// ui->removeItemButton->setEnabled(true);
// ui->clearAllBtn->setEnabled(true);
// }
//}
void MainWindow::saveFileSlot()
{
QString saveFilePath = QFileDialog::getSaveFileName(this,
tr("文件另存为"), "../config", tr("xlsx文件(*.xlsx);;INI配置文件(*.ini);;所有文件(*)"));
if (saveFilePath.isEmpty())
return;
QFile old_file(saveFilePath);
if (old_file.exists())
{
if (!old_file.remove())
{
return;
}
}
Document doc;
doc.addSheet(m_sheetName);
doc.selectSheet(m_sheetName);
if (saveFilePath.endsWith(".xlsx", Qt::CaseInsensitive))
{
int rowCount = ui->tableWidget->rowCount();
int columnCount = ui->tableWidget->columnCount();
// 写首行,从第一列开始
for (int i = 1; i <= m_headers.size(); i++)
{
// excel中起始行和列从1开始
doc.write(1, i, m_headers[i - 1]);
}
// 写数据
for (int i = 0; i < rowCount; i++)
{
for (int j = 0; j < COLUMN_VALID; j++)
{
QString itemStr = ui->tableWidget->item(i, j)->text();
doc.write(i + 2, j + 2, itemStr);
}
}
if (!doc.saveAs(saveFilePath))
{
CommonHelper::ShowMessageBoxError("文件保存失败!");
return;
}
doc.deleteLater();
}
else
{
// 原有的INI文件保存代码
QSettings* saveSettings = nullptr;
saveSettings = new QSettings(saveFilePath, QSettings::IniFormat);
// 设置本地编码 - Qt 6中不再需要setIniCodec
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
// 使用Qt 5的方式
#if defined(Q_OS_WIN)
saveSettings->setIniCodec("GBK");
#else
saveSettings->setIniCodec("UTF-8");
#endif
#endif
// 写按钮数据
saveSettings->beginGroup("ButtonData");
for (int nIndex = 0; nIndex < ui->tableWidget->rowCount(); nIndex++)
{
// // 表格中按钮名称没有填写,就跳出循环
// QString str = ui->tableWidget->item(nIndex, 0)->text();
// if (str.isEmpty())
// {
// break;
// }
QString buttonTimeStr = ui->tableWidget->item(nIndex, 0)->text();
QString buttonGroupStr = ui->tableWidget->item(nIndex, 1)->text();
QString buttonNameStr = ui->tableWidget->item(nIndex, 2)->text();
QString mouseDownStr = ui->tableWidget->item(nIndex, 3)->text();
QString mouseUpStr = ui->tableWidget->item(nIndex, 4)->text();
bool isAvail = ui->tableWidget->item(nIndex, 5)->checkState();
QString buttonAvailStr;
switch (isAvail)
{
case 0:
buttonAvailStr = "False";
break;
case 1:
buttonAvailStr = "True";
break;
default:
break;
}
QString tempStr = buttonGroupStr + "|" + buttonNameStr + "|"
+ mouseDownStr + "|" + mouseUpStr + "|" + buttonAvailStr;
// 修改使用 QRegularExpression 而非 QRegExp
QStringList tempStrList = tempStr.split(QRegularExpression("[,]"));
saveSettings->setValue(QString("%1").arg(nIndex), tempStrList);
}
saveSettings->endGroup();
// 写串口配置数据
saveSettings->beginGroup("comm");
saveSettings->setValue("BaudRate", ui->baudRateComboBox->currentIndex());
saveSettings->setValue("Parity", ui->parityComboBox->currentIndex());
saveSettings->setValue("Bytesize", ui->dataBitsComboBox->currentIndex());
saveSettings->setValue("StopBits", ui->stopBitsComboBox->currentIndex());
saveSettings->setValue("SendByHex", QString::number(ui->isHexCheckBox->isChecked()));
saveSettings->setValue("Name", ui->modelNumLineEdit->text());
saveSettings->endGroup();
delete saveSettings;
}
}
void MainWindow::createButtons()
{
QStringList groupNameList; // 存放生成的按钮分组标签名字
// 分组名获取
for (int i = 0; i < ui->tableWidget->rowCount(); i++)
{
groupNameList << ui->tableWidget->item(i, 1)->text();
}
removeListSame(&groupNameList);
// 创建 分类的组
for (int i = 0; i < groupNameList.size(); i++)
{
// 层叠关系(父->子)buttonFrame(gridLayout)->scrollArea->scrollWidget(gLayout)->pButton
QFrame* buttonFrame = new QFrame(this);
QScrollArea* scrollArea = new QScrollArea(this);
QGridLayout* gridLayout = new QGridLayout(this);
gridLayout->addWidget(scrollArea);
gridLayout->setContentsMargins(0, 0, 0, 0); // 替换setMargin(0)
gridLayout->setSpacing(0);
buttonFrame->setLayout(gridLayout);
QGridLayout* gLayout = new QGridLayout(this);
gLayout->setContentsMargins(30, 12, 30, 12);
gLayout->setSpacing(20);
gLayout->setSizeConstraint(QGridLayout::SetFixedSize);
QWidget* scrollWidget = new QWidget(this);
scrollWidget->setLayout(gLayout);
scrollArea->setWidget(scrollWidget);
scrollArea->setWidgetResizable(true);
gLayoutList << gLayout;
ui->tabWidget->addTab(buttonFrame, groupNameList[i]);
}
//遍历Tab页
for (int i = 0; i < ui->tabWidget->count(); i++)
{
QString currentTabText = ui->tabWidget->tabText(i);
// 设置生成的 按钮 行、列
int row = 0, col = 0;
// 循环创建 按钮
for (int nIndex = 0; nIndex < ui->tableWidget->rowCount(); nIndex++)
{
QString buttonTypeStr = ui->tableWidget->item(nIndex, 1)->text();
// 核对Tab页名字是否和按钮所分类相同
if (currentTabText == buttonTypeStr)
{
// 修改这里使用按钮名称第3列索引2而不是分类名称
QPushButton* pButton = new QPushButton(
QString("%1").arg(ui->tableWidget->item(nIndex, 2)->text()));
//按钮尺寸
pButton->setMinimumHeight(26);
pButton->setMinimumWidth(120);
// 按钮是否已勾选有效 已勾选则显示 未勾选则不显示
if (ui->tableWidget->item(nIndex, 5)->checkState() == Qt::Checked)
{
int alpha = ui->centralWidget->width() / 156;
gLayoutList[i]->addWidget(pButton, (row++) / alpha, (col++) % alpha, 1, 1);
pButton->show();
}
// 插入控件列表
buttonList.insert(nIndex, pButton);
// 信号槽连接
connect(pButton, SIGNAL(pressed()), this, SLOT(mouseDownSlot()));
connect(pButton, SIGNAL(released()), this, SLOT(mouseUpSlot()));
}
}
}
}
void MainWindow::clearButtons()
{
QList< QPushButton* >::iterator iter;
for (iter = buttonList.begin(); iter != buttonList.end(); iter++)
{
delete *iter;
}
buttonList.clear();
QList< QGridLayout* >::iterator iter2;
for (iter2 = gLayoutList.begin(); iter2 != gLayoutList.end(); iter2++)
{
delete *iter2;
}
gLayoutList.clear();
}
void MainWindow::mouseDownSlot()
{
// 通过sender找出点击的button
QPushButton* tmpButton = reinterpret_cast<QPushButton*>(sender());
for (int i = 0; i < buttonList.size(); i++)
{
if (tmpButton == buttonList.at(i)
&& ui->tableWidget->item(i, 5)->checkState() == Qt::Checked)
{
pressStr = ui->tableWidget->item(i, 3)->text();
// 替换逗号
pressStr.replace(QString(","), QString(" "));
// 1.单点发送命令(点击一次)
writeData(pressStr);
// 2.长按发送命令(按住不放)
PressTimer->start(BUTTON_REPEAT_INTERVAL);
tmpButton = NULL;
return;
}
}
}
void MainWindow::mouseUpSlot()
{
// 通过sender找出点击的button
QPushButton* tmpButton = reinterpret_cast<QPushButton*>(sender());
for (int i = 0; i < buttonList.size(); i++)
{
if (tmpButton == buttonList.at(i)
&& ui->tableWidget->item(i, 5)->checkState() == Qt::Checked)
{
// 按钮松开时 计时器停
PressTimer->stop();
QString str = ui->tableWidget->item(i, 4)->text();
// 替换逗号
str.replace(QString(","), QString(" "));
writeData(str);
tmpButton = NULL;
return;
}
}
}
void MainWindow::sendTextAutoScrollSlot()
{
// 设置发送框垂直滚动条的值
ui->sendTextEdit->setVerticalScrollBar(new QScrollBar);
ui->sendTextEdit->verticalScrollBar()->setValue(ui->sendTextEdit->verticalScrollBar()->maximum());
}
void MainWindow::receiveTextAutoScrollSlot()
{
// 设置接收框垂直滚动条的值
ui->receiveTextEdit->setVerticalScrollBar(new QScrollBar);
ui->receiveTextEdit->verticalScrollBar()->setValue(ui->receiveTextEdit->verticalScrollBar()->maximum());
}
void MainWindow::on_clearSendButton_clicked()
{
// 调用自定义消息提示框
int result = CommonHelper::ShowMessageBoxQuesion("是否清空发送数据?");
if (result == 1)
{
ui->sendTextEdit->clear();
sendCount = 0;
ui->sendCountLabel->setNum(sendCount);
}
else
{
return;
}
}
void MainWindow::on_clearReceiveButton_clicked()
{
// 调用自定义消息提示框
int result = CommonHelper::ShowMessageBoxQuesion("是否清空接收数据?");
if (result == 1)
{
ui->receiveTextEdit->clear();
receiveCount = 0;
ui->receiveCountLabel->setNum(receiveCount);
}
else
{
return;
}
}
void MainWindow::on_styleComboBox_currentIndexChanged(int index)
{
switch (index)
{
case 0:
getCurrentDate();
break;
case 1:
styleStr = "green";
break;
case 2:
styleStr = "dark";
break;
default:
break;
}
// 设置样式表
QFile file(QString(":/skin/stylesheet/%1.qss").arg(styleStr));
file.open(QFile::ReadOnly);
QString qss = QLatin1String(file.readAll());
qApp->setStyleSheet(qss);
}
void MainWindow::on_customSendButton_clicked()
{
if (! ui->customLineEdit->text().isEmpty())
{
QString customStr = ui->customLineEdit->text();
writeData(customStr);
// 已经在writeData中处理了历史记录的添加和显示
}
}
void MainWindow::on_customLineEdit_textChanged()
{
QRegularExpression regExp("^[A-Fa-f0-9\\s]+$"); // 匹配 0-9 A-F a-f 空格
bool match = regExp.match(ui->customLineEdit->text()).hasMatch();
if (!match && !ui->customLineEdit->text().isEmpty())
{
CommonHelper::ShowMessageBoxError(tr("请输入有效Hex字符组合\"0-9\"\"A-F\"\"a-f\"\" \"\n每两个字符之间空一个空格。"));
}
}
void MainWindow::on_intervalLineEdit_textChanged()
{
QRegularExpression reg("^[0-9]+$");
ui->intervalLineEdit->setValidator(new QRegularExpressionValidator(reg, this));
}
void MainWindow::on_timerCheckBox_stateChanged(int state)
{
switch (state)
{
case 0: // Unchecked
if (timer->isActive())
{
timer->stop();
}
break;
case 2: // Checked
if (! ui->intervalLineEdit->text().isEmpty())
{
int interval = ui->intervalLineEdit->text().toInt();
timer->start(interval);
}
break;
default:
break;
}
}
void MainWindow::timerUpdate()
{
on_customSendButton_clicked();
}
void MainWindow::on_checkResultBtn_clicked()
{
QRegularExpression regExp("^[A-Fa-f0-9\\s]+$"); // 匹配 0-9 A-F a-f 空格
bool match = regExp.match(ui->inputCommandTextEdit->toPlainText()).hasMatch();
if (!match && !ui->inputCommandTextEdit->toPlainText().isEmpty())
{
CommonHelper::ShowMessageBoxError(tr("请输入有效Hex字符组合\"0-9\"\"A-F\"\"a-f\"\" \"\n每两个字符之间空一个空格。"));
}
QString inputCommandStr = ui->inputCommandTextEdit->toPlainText();
QByteArray inputCommandArr = QString2Hex(inputCommandStr);
if (ui->xorRadioButton->isChecked())
{
char checkHex = xorCheck(inputCommandArr);
QString str = QString("%1").arg(checkHex & 0xFF, 2, 16, QLatin1Char('0')).toUpper();
ui->checkResultLineEdit->setText(str);
}
else if (ui->sumRadioButton->isChecked())
{
char checkHex = sumCheck(inputCommandArr);
QString str = QString("%1").arg(checkHex & 0xFF, 2, 16, QLatin1Char('0')).toUpper();
ui->checkResultLineEdit->setText(str);
}
}
void MainWindow::on_clearInputCommandBtn_clicked()
{
ui->inputCommandTextEdit->clear();
}
void MainWindow::on_settingsCheckBox_stateChanged(int state)
{
switch (state)
{
case 0: // Unchecked
ui->settingsGroupBox->hide();
break;
case 2: // Checked
ui->settingsGroupBox->show();
break;
default:
break;
}
}
void MainWindow::on_openFileButton_clicked()
{
QFileDialog* fileDialog = new QFileDialog(this);
fileDialog->setWindowTitle(tr("打开文件"));
fileDialog->setDirectory("./");
QStringList filters;
filters << "TXT文件(*.txt)"
<< "所有文件(*)";
fileDialog->setNameFilters(filters);
QStringList fileName;
if (fileDialog->exec() == QDialog::Accepted)
{
fileName = fileDialog->selectedFiles();
QString pathName = fileName.join(",");
ui->filePathLineEdit->setText(pathName);
}
}
void MainWindow::on_sendFileButton_clicked()
{
QString fileName = ui->filePathLineEdit->text();
if (fileName.isEmpty())
{
CommonHelper::ShowMessageBoxError(tr("请选择一个文件!"));
return;
}
QFileInfo* fileInfo = new QFileInfo(fileName);
int fileSize = fileInfo->size();
QFile file;
file.setFileName(fileName);
if (!file.open(QIODevice::ReadOnly))
{
// 文件打开失败
return;
}
QDataStream in(&file);
char* dataBuffer;
dataBuffer = new char[fileSize];
in.setVersion(QDataStream::Qt_5_6);
//读出文件到缓存
in.readRawData(dataBuffer, fileSize);
file.close();
if (!updateBuffer)
{
delete updateBuffer;
}
dataSize = fileSize;
updateBuffer = new char[dataSize];
int j = 0;
// 数据
for (int i = 0; i < fileSize; i++)
{
updateBuffer[j++] = dataBuffer[i];
}
delete dataBuffer;
bSendFile = true;
sendFile();
}
void MainWindow::sendFileTimerUpdate()
{
int packSize = 1; // 每次发128个字节
char* Temp;
Temp = new char[packSize];
if (m_lSendPoint * packSize < dataSize)
{
memcpy(Temp, updateBuffer + packSize * m_lSendPoint, packSize);
m_serialPort->write(Temp, packSize);
// 0QString str = QString(QLatin1String(Temp));
// ui->sendTextEdit->append(str);
}
// else
// {
// int itemt = dataSize - (m_lSendPoint - 1)*packSize;
// // 发送最后一条命令当前数据长度不能被128整除的部分
// memcpy(Temp, updateBuffer + 2 * (m_lSendPoint - 1), itemt);
// m_serialPort->write(Temp, itemt);
// sendFileTimer->stop();
// }
m_lSendPoint++;
if (m_lSendPoint * packSize == dataSize)
{
sendFileTimer->stop();
dataSize = 0;
}
delete Temp;
}
void MainWindow::PressTimerUpdate()
{
writeData(pressStr);
}
void MainWindow::connectCOMButtonSlot()
{
if ("打开串口" == ui->connectBtn->text())
{
openSerialPort();
// 同步新按钮状态
if ("关闭串口" == ui->connectBtn->text())
{
ui->NewRegularSentButton->setEnabled(true);
ui->NewOpenComButton->setText("关闭串口");
}
}
else if ("关闭串口" == ui->connectBtn->text())
{
ui->connectInfo->setPixmap(QPixmap(":/skin/image/error.png"));
ui->connectInfo->setScaledContents(true);
ui->connectBtn->setText("打开串口");
ui->NewOpenComButton->setText("打开串口");
// 停止定时发送并禁用相关按钮
if (isRegularSending)
{
regularTimer->stop();
isRegularSending = false;
ui->RegularlySentButton->setText("定时发送");
ui->NewRegularSentButton->setText("定时发送");
}
ui->NewRegularSentButton->setEnabled(false);
closeSerialPort();
}
}
// 实现主界面的打开串口按钮功能
// void MainWindow::on_NewOpenComButton_clicked()
// {
// if (ui->NewOpenComButton->text() == "打开串口")
// {
// // 检查是否选择了串口
// if (ui->NewComcomboBox->currentText().isEmpty()) {
// CommonHelper::ShowMessageBoxError(tr("请先选择串口!"));
// // 自动刷新串口列表
// updatePortsInComboBoxes();
// return;
// }
// // 调用统一的打开串口方法
// openSerialPort();
// }
// else // "关闭串口" 状态
// {
// closeSerialPort();
// // 更新UI状态
// ui->connectInfo->setPixmap(QPixmap(":/skin/image/error.png"));
// ui->connectInfo->setScaledContents(true);
// ui->connectBtn->setText("打开串口");
// ui->NewOpenComButton->setText("打开串口");
// // ui->NewRegularSentButton->setEnabled(false);
// CommonHelper::ShowMessageBoxInfo(tr("串口已关闭"));
// }
// }
void MainWindow::on_NewOpenComButton_clicked()
{
if (ui->NewOpenComButton->text() == "打开串口")
{
// 检查是否选择了串口
if (ui->NewComcomboBox->currentText().isEmpty())
{
CommonHelper::ShowMessageBoxError(tr("请先选择串口!"));
// 自动刷新串口列表
updatePortsInComboBoxes();
return;
}
// 调用统一的打开串口方法
openSerialPort();
}
else // "关闭串口" 状态
{
closeSerialPort();
// 更新UI状态
ui->connectInfo->setPixmap(QPixmap(":/skin/image/error.png"));
ui->connectInfo->setScaledContents(true);
ui->connectBtn->setText("打开串口");
ui->NewOpenComButton->setText("打开串口");
// 删除这行,避免禁用定时发送按钮
// ui->NewRegularSentButton->setEnabled(false);
CommonHelper::ShowMessageBoxInfo(tr("串口已关闭"));
}
}
void MainWindow::updatePortsInComboBoxes()
{
// 保存当前选中的串口
QString currentPort1 = ui->comComboBox->currentText();
QString currentPort2 = ui->NewComcomboBox->currentText();
// 清空串口列表
ui->comComboBox->clear();
ui->NewComcomboBox->clear();
// 获取系统中可用的串口
QList<QSerialPortInfo> ports = QSerialPortInfo::availablePorts();
// 填充串口选择框
bool foundPort1 = false, foundPort2 = false;
foreach (const QSerialPortInfo& info, ports)
{
QString portText = info.portName();
// 添加设备描述信息
if (!info.description().isEmpty() && info.description() != "n/a")
{
portText += " (" + info.description() + ")";
}
ui->comComboBox->addItem(portText, info.portName());
ui->NewComcomboBox->addItem(portText, info.portName());
// 检查是否是之前选中的串口
if (portText.contains(currentPort1.split("(").first().trimmed()))
{
ui->comComboBox->setCurrentIndex(ui->comComboBox->count() - 1);
foundPort1 = true;
}
if (portText.contains(currentPort2.split("(").first().trimmed()))
{
ui->NewComcomboBox->setCurrentIndex(ui->NewComcomboBox->count() - 1);
foundPort2 = true;
}
}
// 更新按钮状态
bool hasAvailablePorts = !ports.isEmpty();
ui->connectBtn->setEnabled(hasAvailablePorts);
ui->NewOpenComButton->setEnabled(hasAvailablePorts);
if (!hasAvailablePorts)
{
CommonHelper::ShowMessageBoxInfo(tr("未检测到可用的串口设备。\n\n请确认:\n1. 设备已正确连接\n2. 驱动程序已安装\n3. 设备未被其他程序占用"));
}
else
{
qDebug() << "发现" << ports.size() << "个可用串口";
}
// 如果之前选中的串口不存在了,给出提示
if (!currentPort1.isEmpty() && !foundPort1)
{
qDebug() << "之前选中的串口" << currentPort1 << "已断开";
}
if (!currentPort2.isEmpty() && !foundPort2)
{
qDebug() << "之前选中的串口" << currentPort2 << "已断开";
}
}
void MainWindow::showSendHistoryDialog()
{
// 如果对话框还没创建,则创建它
if (!m_sendHistoryDialog)
{
m_sendHistoryDialog = new SendHistoryDialog(this);
// 连接信号槽
connect(m_sendHistoryDialog, SIGNAL(sendCommand(QString)),
this, SLOT(writeData(QString)));
// 添加暂停/恢复定时发送的信号连接
connect(m_sendHistoryDialog, SIGNAL(pauseRegularSending(bool)),
this, SLOT(handlePauseRegularSending(bool)));
}
// 如果对话框尚未显示,则显示它
if (!m_sendHistoryDialog->isVisible())
{
m_sendHistoryDialog->show();
m_sendHistoryDialog->raise(); // 确保它在最前面
m_sendHistoryDialog->activateWindow(); // 让它获得焦点
}
}
// 处理暂停和恢复定时发送
void MainWindow::handlePauseRegularSending(bool pause)
{
if (isRegularSending)
{
if (pause && !isPausedSending)
{
// 暂停定时发送 - 停止定时器但不重置标志
regularTimer->stop();
isPausedSending = true;
// 暂停累计时间计时器 - 记录暂停时间点
if (m_sendHistoryDialog)
{
if (m_sendHistoryDialog->isTimingActive())
{
m_sendHistoryDialog->stopTimingCount();
}
m_sendHistoryDialog->setPauseState(true);
}
// 更新UI状态但保持isRegularSending标志
ui->RegularlySentButton->setText("恢复发送"); // 修改为"恢复发送"而非"定时发送(已暂停)"
ui->NewRegularSentButton->setText("恢复发送");
qDebug() << QString("定时发送已暂停,当前累计时间:%1秒").arg(elapsedSeconds);
}
else if (!pause && isPausedSending)
{
// 恢复定时发送 - 重新启动定时器
isPausedSending = false;
// 恢复累计时间计时器 - 从暂停点继续
if (m_sendHistoryDialog)
{
m_sendHistoryDialog->setPauseState(false);
if (!m_sendHistoryDialog->isTimingActive())
{
m_sendHistoryDialog->startTimingCount();
}
}
// 更新UI状态
ui->RegularlySentButton->setText("停止发送");
ui->NewRegularSentButton->setText("停止发送");
// 重新启动定时器
regularTimer->start();
qDebug() << QString("定时发送已恢复,当前累计时间:%1秒").arg(elapsedSeconds);
}
}
}
MainWindow::DeviceCommSettings MainWindow::parseCommSettingsString(const QString& commString)
{
// 默认通信设置
DeviceCommSettings settings;
settings.baudRate = 115200;
settings.dataBits = QSerialPort::Data8;
settings.parity = QSerialPort::NoParity;
settings.stopBits = QSerialPort::OneStop;
settings.portName = "";
// 解析格式示例: "115200 COM1" 或 "9600 COM3 8N1"
if (!commString.isEmpty())
{
QStringList parts = commString.split(" ", QString::SkipEmptyParts);
if (parts.size() >= 1)
{
// 尝试解析波特率
bool ok;
int baudRate = parts[0].toInt(&ok);
if (ok)
settings.baudRate = baudRate;
}
if (parts.size() >= 2)
{
// 解析端口名
if (parts[1].startsWith("COM", Qt::CaseInsensitive))
{
settings.portName = parts[1].trimmed();
}
}
if (parts.size() >= 3)
{
// 解析数据位、奇偶校验、停止位 (如 "8N1")
QString format = parts[2];
if (format.length() >= 3)
{
// 数据位
if (format[0] == '5')
settings.dataBits = QSerialPort::Data5;
else if (format[0] == '6')
settings.dataBits = QSerialPort::Data6;
else if (format[0] == '7')
settings.dataBits = QSerialPort::Data7;
else
settings.dataBits = QSerialPort::Data8;
// 校验位
if (format[1] == 'N' || format[1] == 'n')
settings.parity = QSerialPort::NoParity;
else if (format[1] == 'E' || format[1] == 'e')
settings.parity = QSerialPort::EvenParity;
else if (format[1] == 'O' || format[1] == 'o')
settings.parity = QSerialPort::OddParity;
// 停止位
if (format[2] == '2')
settings.stopBits = QSerialPort::TwoStop;
else
settings.stopBits = QSerialPort::OneStop;
}
}
}
return settings;
}
bool MainWindow::sendDataToDevice(const QString& portName, const QString& data)
{
// 如果是主串口直接使用writeData方法
if (portName == m_serialPort->portName())
{
writeData(data);
return true;
}
// 检查是否有该串口的实例
if (!m_serialPorts.contains(portName))
{
return false;
}
// 获取串口实例
QSerialPort* port = m_serialPorts[portName];
if (!port->isOpen())
{
return false;
}
// 发送数据
QByteArray arr;
if (ui->isHexCheckBox->isChecked())
{
arr = QString2Hex(data);
}
else
{
arr = data.toLatin1();
}
qint64 bytesWritten = port->write(arr);
return (bytesWritten == arr.size());
}
// void MainWindow::updateSendHistory(const QString &command, const QString &portName,
// int baudRate, const QString &cmdName)
// {
// // 显示发送历史对话框
// showSendHistoryDialog();
// if (m_sendHistoryDialog) {
// // 添加记录到历史对话框
// QString displayCommand;
// // 如果有指令名称且不为空,加上指令名称前缀
// if (!cmdName.isEmpty()) {
// if (portName != m_serialPort->portName()) {
// // 如果不是主串口,加上端口标识
// displayCommand = QString("[%1] %2 -> %3").arg(cmdName).arg(command).arg(portName);
// } else {
// displayCommand = QString("[%1] %2").arg(cmdName).arg(command);
// }
// } else {
// // 没有指令名称,只显示通讯协议
// if (portName != m_serialPort->portName()) {
// // 如果不是主串口,加上端口标识
// displayCommand = command + " -> " + portName;
// } else {
// displayCommand = command;
// }
// }
// bool isHex = ui->isHexCheckBox->isChecked();
// m_sendHistoryDialog->addSendRecord(displayCommand, isHex);
// }
// }
// 修改 updateSendHistory 方法,添加 isHex 参数并使用它
void MainWindow::updateSendHistory(const QString& command, const QString& portName,
int baudRate, const QString& cmdName, bool isHex)
{
// 显示发送历史对话框
showSendHistoryDialog();
if (m_sendHistoryDialog)
{
// 添加记录到历史对话框
QString displayCommand;
// 如果有指令名称且不为空,加上指令名称前缀
if (!cmdName.isEmpty())
{
displayCommand = QString("[%1] %2 -> %3").arg(cmdName).arg(command).arg(portName);
}
else
{
displayCommand = QString("%1 -> %2").arg(command).arg(portName);
}
// 使用传入的实际格式标志,而不是全局设置
m_sendHistoryDialog->addSendRecord(displayCommand, isHex);
}
}
void MainWindow::on_startLogButton_clicked()
{
QString logFilePath = QFileDialog::getSaveFileName(
this,
tr("保存日志文件"),
QDateTime::currentDateTime().toString("yyyy-MM-dd_hh-mm-ss") + ".log",
tr("日志文件 (*.log);;文本文件 (*.txt);;所有文件 (*.*)"));
if (logFilePath.isEmpty())
{
return; // 用户取消选择
}
if (m_logRecord->startLog(logFilePath))
{
CommonHelper::ShowMessageBoxInfo(tr("日志记录已开始"));
ui->startLogButton->setEnabled(false);
ui->stopLogButton->setEnabled(true);
}
else
{
CommonHelper::ShowMessageBoxError(tr("无法开始日志记录,请检查文件路径是否有效"));
}
}
void MainWindow::on_stopLogButton_clicked()
{
if (m_logRecord->isLogging())
{
m_logRecord->stopLog();
CommonHelper::ShowMessageBoxInfo(tr("日志记录已停止"));
ui->startLogButton->setEnabled(true);
ui->stopLogButton->setEnabled(false);
}
}
void MainWindow::on_refreshPortsButton_clicked()
{
updatePortsInComboBoxes();
CommonHelper::ShowMessageBoxInfo(tr("串口列表已更新"));
}
// void MainWindow::on_RegularlySentButton_clicked()
// {
// // 如果当前是暂停状态,重新启动定时发送
// if (isRegularSending && isPausedSending) {
// handlePauseRegularSending(false); // 恢复发送
// return;
// }
// if (!isRegularSending) {
// // 准备命令列表
// commandsWithComm.clear();
// currentCommandIndex = 0;
// elapsedSeconds = 0; // 重置累计秒数
// // 从表格中读取所有有效的命令及其时间点
// for (int i = 0; i < ui->tableWidget->rowCount(); i++) {
// if (ui->tableWidget->item(i, 17) && ui->tableWidget->item(i, 1) &&
// ui->tableWidget->item(i, 17)->checkState() == Qt::Checked) {
// int timePoint = ui->tableWidget->item(i, 1)->text().toInt();
// if (timePoint < 0) timePoint = 0; // 确保时间点不为负数
// checkAndAddDeviceCommand(i, 2, 3, 4, timePoint); // 设备1
// checkAndAddDeviceCommand(i, 5, 6, 7, timePoint); // 设备2
// checkAndAddDeviceCommand(i, 8, 9, 10, timePoint); // 设备3
// checkAndAddDeviceCommand(i, 11, 12, 13, timePoint); // 设备4
// checkAndAddDeviceCommand(i, 14, 15, 16, timePoint); // 设备5
// }
// }
// if (commandsWithComm.isEmpty()) {
// CommonHelper::ShowMessageBoxError(tr("没有找到有效的设备协议配置!"));
// return;
// }
// // 按时间点排序命令列表
// std::sort(commandsWithComm.begin(), commandsWithComm.end(),
// [](const DeviceCommand &a, const DeviceCommand &b) {
// return a.interval < b.interval; // interval现在表示时间点
// });
// if (!m_serialPort->isOpen()) {
// // if (ui->NewComcomboBox->currentText().isEmpty() && ui->comComboBox->currentText().isEmpty()) {
// // CommonHelper::ShowMessageBoxError(tr("请先选择串口!"));
// // return;
// // }
// // 如果串口未打开,尝试自动打开串口
// if (ui->NewComcomboBox->currentText().isEmpty() && ui->comComboBox->currentText().isEmpty()) {
// CommonHelper::ShowMessageBoxError(tr("请先选择串口!"));
// updatePortsInComboBoxes(); // 自动刷新串口列表
// return;
// }
// updateComSettings();
// m_serialPort->setPortName(currentSettings.name);
// m_serialPort->setBaudRate(currentSettings.baudRate);
// m_serialPort->setDataBits(currentSettings.dataBits);
// m_serialPort->setParity(currentSettings.parity);
// m_serialPort->setStopBits(currentSettings.stopBits);
// if (!m_serialPort->open(QIODevice::ReadWrite)) {
// CommonHelper::ShowMessageBoxError(tr("无法自动打开串口:%1").arg(m_serialPort->errorString()));
// return;
// }
// ui->connectInfo->setPixmap(QPixmap(":/skin/image/info.png"));
// ui->connectInfo->setScaledContents(true);
// ui->connectBtn->setText("关闭串口");
// ui->NewOpenComButton->setText("关闭串口");
// ui->NewRegularSentButton->setEnabled(true);
// CommonHelper::ShowMessageBoxInfo(tr("串口已自动打开,开始定时发送"));
// }
// regularTimer->start(); // 每秒检查一次
// isRegularSending = true;
// isPausedSending = false; // 确保重置暂停状态
// ui->RegularlySentButton->setText("停止发送");
// ui->NewRegularSentButton->setText("停止发送");
// showSendHistoryDialog();
// if (m_sendHistoryDialog) {
// m_sendHistoryDialog->setPauseState(false); // 确保重置历史对话框的暂停状态
// }
// // 启动累计时间计时器
// if (m_sendHistoryDialog && !m_sendHistoryDialog->isTimingActive()) {
// m_sendHistoryDialog->startTimingCount();
// }
// } else {
// // 无论是正在发送还是暂停状态,都停止定时发送
// regularTimer->stop();
// isRegularSending = false;
// isPausedSending = false; // 确保重置暂停状态
// elapsedSeconds = 0; // 重置累计秒数
// ui->RegularlySentButton->setText("定时发送");
// ui->NewRegularSentButton->setText("定时发送");
// if (m_sendHistoryDialog) {
// m_sendHistoryDialog->setPauseState(false); // 确保重置历史对话框的暂停状态
// }
// // 停止累计时间计时器
// if (m_sendHistoryDialog && m_sendHistoryDialog->isTimingActive()) {
// m_sendHistoryDialog->stopTimingCount();
// }
// CommonHelper::ShowMessageBoxInfo(tr("定时发送已停止"));
// }
// }
void MainWindow::on_RegularlySentButton_clicked()
{
// 如果当前是暂停状态,重新启动定时发送
if (isRegularSending && isPausedSending)
{
handlePauseRegularSending(false); // 恢复发送
return;
}
if (!isRegularSending)
{
// 准备命令列表
commandsWithComm.clear();
currentCommandIndex = 0;
elapsedSeconds = 0; // 重置累计秒数
// 从表格中读取所有有效的命令及其时间点
for (int i = 0; i < ui->tableWidget->rowCount(); i++)
{
if (ui->tableWidget->item(i, COLUMN_VALID) && ui->tableWidget->item(i, 1) &&
ui->tableWidget->item(i, COLUMN_VALID)->checkState() == Qt::Checked)
{
int timePoint = ui->tableWidget->item(i, 1)->text().toInt();
if (timePoint < 0)
timePoint = 0; // 确保时间点不为负数
checkAndAddDeviceCommand(i, 2, 3, 4, timePoint); // 设备1
checkAndAddDeviceCommand(i, 5, 6, 7, timePoint); // 设备2
checkAndAddDeviceCommand(i, 8, 9, 10, timePoint); // 设备3
checkAndAddDeviceCommand(i, 11, 12, 13, timePoint); // 设备4
checkAndAddDeviceCommand(i, 14, 15, 16, timePoint); // 设备5
}
}
if (commandsWithComm.isEmpty())
{
CommonHelper::ShowMessageBoxError(tr("没有找到有效的设备协议配置!"));
return;
}
// 按时间点排序命令列表
std::sort(commandsWithComm.begin(), commandsWithComm.end(),
[](const DeviceCommand & a, const DeviceCommand & b)
{
return a.interval < b.interval; // interval现在表示时间点
});
// 直接开始定时发送,不检查串口状态
regularTimer->start(); // 每秒检查一次
isRegularSending = true;
isPausedSending = false; // 确保重置暂停状态
ui->RegularlySentButton->setText("停止发送");
ui->NewRegularSentButton->setText("停止发送");
showSendHistoryDialog();
if (m_sendHistoryDialog)
{
m_sendHistoryDialog->setPauseState(false); // 确保重置历史对话框的暂停状态
}
// 启动累计时间计时器
if (m_sendHistoryDialog && !m_sendHistoryDialog->isTimingActive())
{
m_sendHistoryDialog->startTimingCount();
}
}
else
{
// 无论是正在发送还是暂停状态,都停止定时发送
regularTimer->stop();
isRegularSending = false;
isPausedSending = false; // 确保重置暂停状态
elapsedSeconds = 0; // 重置累计秒数
ui->RegularlySentButton->setText("定时发送");
ui->NewRegularSentButton->setText("定时发送");
if (m_sendHistoryDialog)
{
m_sendHistoryDialog->setPauseState(false); // 确保重置历史对话框的暂停状态
}
// 停止累计时间计时器
if (m_sendHistoryDialog && m_sendHistoryDialog->isTimingActive())
{
m_sendHistoryDialog->stopTimingCount();
}
CommonHelper::ShowMessageBoxInfo(tr("定时发送已停止"));
}
}
void MainWindow::on_NewRegularSentButton_clicked()
{
// 如果当前是暂停状态,重新启动定时发送
if (isRegularSending && isPausedSending)
{
handlePauseRegularSending(false); // 恢复发送
return;
}
// 调用主定时发送按钮的功能
on_RegularlySentButton_clicked();
}
void MainWindow::synchronizeComboBoxes()
{
QComboBox* senderBox = qobject_cast<QComboBox*>(QObject::sender());
if (senderBox == ui->comComboBox)
{
ui->NewComcomboBox->blockSignals(true);
ui->NewComcomboBox->setCurrentIndex(ui->comComboBox->currentIndex());
ui->NewComcomboBox->blockSignals(false);
}
else if (senderBox == ui->NewComcomboBox)
{
ui->comComboBox->blockSignals(true);
ui->comComboBox->setCurrentIndex(ui->NewComcomboBox->currentIndex());
ui->comComboBox->blockSignals(false);
}
}
void MainWindow::onSendButtonClicked()
{
QPushButton* button = qobject_cast<QPushButton*>(sender());
if (!button)
return;
int row = button->property("row").toInt();
if (row >= 0 && row < ui->tableWidget->rowCount() && m_serialPort->isOpen())
{
if (ui->tableWidget->item(row, 3) && !ui->tableWidget->item(row, 3)->text().isEmpty())
{
QString command = ui->tableWidget->item(row, 3)->text();
writeData(command);
}
else
{
CommonHelper::ShowMessageBoxError(tr("该行没有有效的发送命令!"));
}
}
else if (!m_serialPort->isOpen())
{
CommonHelper::ShowMessageBoxError(tr("请先打开串口!"));
}
}
void MainWindow::onMultiDeviceSendButtonClicked()
{
QPushButton* button = qobject_cast<QPushButton*>(sender());
if (!button)
return;
int row = button->property("row").toInt();
if (row >= 0 && row < ui->tableWidget->rowCount() /*&& m_serialPort->isOpen()*/)
{
bool anySent = false;
// if (trySendDeviceCommand(row, 2, 3, 4)) anySent = true;
// if (trySendDeviceCommand(row, 5, 6, 7)) anySent = true;
// if (trySendDeviceCommand(row, 8, 9, 10)) anySent = true;
// if (trySendDeviceCommand(row, 11, 12, 13)) anySent = true;
// if (trySendDeviceCommand(row, 14, 15, 16)) anySent = true;
if (trySendMQTTCommand(row, 17, 18, 19, 20))
anySent = true;
if (!anySent)
{
CommonHelper::ShowMessageBoxError(tr("该行没有配置任何设备命令!"));
}
}
else if (!m_serialPort->isOpen())
{
CommonHelper::ShowMessageBoxError(tr("请先打开串口!"));
}
}
// 修改 sendRegularCommandWithPort 方法中的数据发送部分
void MainWindow::sendRegularCommandWithPort()
{
if (commandsWithComm.isEmpty())
{
regularTimer->stop();
isRegularSending = false;
isPausedSending = false;
elapsedSeconds = 0;
ui->RegularlySentButton->setText("定时发送");
ui->NewRegularSentButton->setText("定时发送");
return;
}
// 增加累计秒数
elapsedSeconds++;
// 检查是否有需要在当前时间点发送的命令
for (int i = 0; i < commandsWithComm.size(); i++)
{
const DeviceCommand& cmd = commandsWithComm.at(i);
// 如果命令的时间点等于当前累计秒数,则发送
if (cmd.interval == elapsedSeconds)
{
// 解析通讯参数
DeviceCommSettings commSettings = parseCommSettingsString(cmd.commParam);
// 检查端口名是否有效
if (commSettings.portName.isEmpty())
{
qDebug() << "命令" << cmd.command << "的端口名称为空,跳过发送";
continue;
}
// 检查是否是主串口
bool isMainPort = false;
if (m_serialPort->isOpen() && m_serialPort->portName() == commSettings.portName)
{
isMainPort = true;
// 根据命令的格式发送数据,而不是根据全局设置
QByteArray arr;
if (cmd.isHex)
{
arr = QString2Hex(cmd.command);
m_serialPort->write(arr);
// 在发送编辑框中显示
ui->sendTextEdit->append(cmd.command.toUpper());
}
else
{
arr = cmd.command.toUtf8(); // 使用UTF-8编码ASCII文本
m_serialPort->write(arr);
// 在发送编辑框中显示
ui->sendTextEdit->append(cmd.command);
}
// 更新计数
sendCount += arr.size();
ui->sendCountLabel->setNum(sendCount);
// 记录日志
if (m_logRecord->isLogging())
{
if (cmd.isHex)
{
m_logRecord->logSend(cmd.command.toUpper());
}
else
{
m_logRecord->logSend(cmd.command);
}
}
// 添加到发送历史记录
updateSendHistory(cmd.command, commSettings.portName,
commSettings.baudRate, cmd.protocol, cmd.isHex);
qDebug() << "通过主串口" << commSettings.portName << "发送"
<< (cmd.isHex ? "HEX" : "ASCII") << "命令:" << cmd.command;
continue;
}
// 如果不是主串口,尝试找或创建特定串口
QSerialPort* targetPort = nullptr;
// 检查此端口是否已经创建和打开
if (m_serialPorts.contains(commSettings.portName))
{
targetPort = m_serialPorts[commSettings.portName];
// 如果端口已经打开但参数不同,需要重新配置
if (targetPort->isOpen() &&
(targetPort->baudRate() != commSettings.baudRate ||
targetPort->dataBits() != commSettings.dataBits ||
targetPort->parity() != commSettings.parity ||
targetPort->stopBits() != commSettings.stopBits))
{
targetPort->close();
}
}
// 如果串口不存在或已关闭,需要创建或打开
if (!targetPort || !targetPort->isOpen())
{
bool result = openSerialPortForDevice(
commSettings.portName,
commSettings.baudRate,
commSettings.dataBits,
commSettings.parity,
commSettings.stopBits
);
if (!result)
{
qDebug() << "无法打开串口" << commSettings.portName << ",跳过命令" << cmd.command;
continue;
}
targetPort = m_serialPorts[commSettings.portName];
}
// 根据命令的格式发送数据
QByteArray arr;
if (cmd.isHex)
{
arr = QString2Hex(cmd.command);
}
else
{
arr = cmd.command.toUtf8(); // 使用UTF-8编码ASCII文本
}
qint64 bytesWritten = targetPort->write(arr);
if (bytesWritten != arr.size())
{
qDebug() << "发送数据到串口" << commSettings.portName << "失败";
}
else
{
qDebug() << "成功发送" << (cmd.isHex ? "HEX" : "ASCII") << "命令到串口"
<< commSettings.portName << ":" << cmd.command;
// 在发送窗口添加发送记录
QString displayText;
if (!cmd.protocol.isEmpty())
{
displayText = QString("[%1] %2 -> %3").arg(cmd.protocol).arg(cmd.command).arg(commSettings.portName);
}
else
{
displayText = QString("%1 -> %2").arg(cmd.command).arg(commSettings.portName);
}
if (cmd.isHex)
{
ui->sendTextEdit->append(displayText.toUpper());
}
else
{
ui->sendTextEdit->append(displayText);
}
// 更新发送计数
sendCount += arr.size();
ui->sendCountLabel->setNum(sendCount);
// 添加到发送历史记录
updateSendHistory(cmd.command, commSettings.portName,
commSettings.baudRate, cmd.protocol, cmd.isHex);
// 记录发送的日志
if (m_logRecord->isLogging())
{
if (cmd.isHex)
{
m_logRecord->logSend(displayText.toUpper());
}
else
{
m_logRecord->logSend(displayText);
}
}
}
}
}
}
// void MainWindow::sendRegularCommandWithPort()
// {
// if (commandsWithComm.isEmpty()) {
// regularTimer->stop();
// isRegularSending = false;
// isPausedSending = false;
// elapsedSeconds = 0;
// ui->RegularlySentButton->setText("定时发送");
// ui->NewRegularSentButton->setText("定时发送");
// return;
// }
// // 增加累计秒数
// elapsedSeconds++;
// // 检查是否有需要在当前时间点发送的命令
// for (int i = 0; i < commandsWithComm.size(); i++) {
// const DeviceCommand &cmd = commandsWithComm.at(i);
// // 如果命令的时间点等于当前累计秒数,则发送
// if (cmd.interval == elapsedSeconds) {
// // 解析通讯参数
// DeviceCommSettings commSettings = parseCommSettingsString(cmd.commParam);
// // 检查端口名是否有效
// if (commSettings.portName.isEmpty()) {
// qDebug() << "命令" << cmd.command << "的端口名称为空,跳过发送";
// continue;
// }
// // 检查是否是主串口
// bool isMainPort = false;
// if (m_serialPort->isOpen() && m_serialPort->portName() == commSettings.portName) {
// isMainPort = true;
// writeData(cmd.command);
// qDebug() << "通过主串口" << commSettings.portName << "发送命令:" << cmd.command;
// // 添加到发送历史记录 - 包含指令名称
// updateSendHistory(cmd.command, commSettings.portName, commSettings.baudRate, cmd.protocol);
// continue;
// }
// // 如果不是主串口,尝试找或创建特定串口
// QSerialPort *targetPort = nullptr;
// // 检查此端口是否已经创建和打开
// if (m_serialPorts.contains(commSettings.portName)) {
// targetPort = m_serialPorts[commSettings.portName];
// // 如果端口已经打开但参数不同,需要重新配置
// if (targetPort->isOpen() &&
// (targetPort->baudRate() != commSettings.baudRate ||
// targetPort->dataBits() != commSettings.dataBits ||
// targetPort->parity() != commSettings.parity ||
// targetPort->stopBits() != commSettings.stopBits)) {
// targetPort->close();
// }
// }
// // 如果串口不存在或已关闭,需要创建或打开
// if (!targetPort || !targetPort->isOpen()) {
// bool result = openSerialPortForDevice(
// commSettings.portName,
// commSettings.baudRate,
// commSettings.dataBits,
// commSettings.parity,
// commSettings.stopBits
// );
// if (!result) {
// qDebug() << "无法打开串口" << commSettings.portName << ",跳过命令" << cmd.command;
// continue;
// }
// targetPort = m_serialPorts[commSettings.portName];
// }
// // 发送数据
// QByteArray arr;
// if (ui->isHexCheckBox->isChecked()) {
// arr = QString2Hex(cmd.command);
// } else {
// arr = cmd.command.toLatin1();
// }
// qint64 bytesWritten = targetPort->write(arr);
// if (bytesWritten != arr.size()) {
// qDebug() << "发送数据到串口" << commSettings.portName << "失败";
// } else {
// qDebug() << "成功发送到串口" << commSettings.portName << ":" << cmd.command;
// // 在发送窗口添加发送记录
// QString displayText;
// if (!cmd.protocol.isEmpty()) {
// displayText = QString("[%1] %2 -> %3").arg(cmd.protocol).arg(cmd.command).arg(commSettings.portName);
// } else {
// displayText = QString("%1 -> %2").arg(cmd.command).arg(commSettings.portName);
// }
// if (ui->isHexCheckBox->isChecked()) {
// ui->sendTextEdit->append(displayText.toUpper());
// } else {
// ui->sendTextEdit->append(displayText);
// }
// // 更新发送计数
// sendCount += arr.size();
// ui->sendCountLabel->setNum(sendCount);
// // 添加到发送历史记录 - 包含指令名称
// updateSendHistory(cmd.command, commSettings.portName, commSettings.baudRate, cmd.protocol);
// // 记录发送的日志 - 添加指令名称
// if (m_logRecord->isLogging()) {
// if (ui->isHexCheckBox->isChecked()) {
// m_logRecord->logSend(displayText.toUpper());
// } else {
// m_logRecord->logSend(displayText);
// }
// }
// }
// }
// }
// }
// void MainWindow::handleSerialPortError(QSerialPort::SerialPortError error)
// {
// QSerialPort *port = qobject_cast<QSerialPort*>(sender());
// if (!port) return;
// if (error != QSerialPort::NoError && error != QSerialPort::NotOpenError) {
// QString portName = port->portName();
// QString errorMsg = port->errorString();
// CommonHelper::ShowMessageBoxError(tr("串口 %1 发生错误: %2").arg(portName).arg(errorMsg));
// if (port == m_serialPort) {
// ui->connectInfo->setPixmap(QPixmap(":/skin/image/error.png"));
// ui->connectInfo->setScaledContents(true);
// ui->connectBtn->setText("打开串口");
// ui->NewOpenComButton->setText("打开串口");
// if (isRegularSending) {
// regularTimer->stop();
// isRegularSending = false;
// isPausedSending = false;
// ui->RegularlySentButton->setText("定时发送");
// ui->NewRegularSentButton->setText("定时发送");
// }
// ui->NewRegularSentButton->setEnabled(false);
// }
// }
// }
void MainWindow::handleSerialPortError(QSerialPort::SerialPortError error)
{
QSerialPort* port = qobject_cast<QSerialPort*>(sender());
if (!port)
return;
if (error != QSerialPort::NoError && error != QSerialPort::NotOpenError)
{
QString portName = port->portName();
QString errorMsg = port->errorString();
CommonHelper::ShowMessageBoxError(tr("串口 %1 发生错误: %2").arg(portName).arg(errorMsg));
if (port == m_serialPort)
{
ui->connectInfo->setPixmap(QPixmap(":/skin/image/error.png"));
ui->connectInfo->setScaledContents(true);
ui->connectBtn->setText("打开串口");
ui->NewOpenComButton->setText("打开串口");
if (isRegularSending)
{
regularTimer->stop();
isRegularSending = false;
isPausedSending = false;
ui->RegularlySentButton->setText("定时发送");
ui->NewRegularSentButton->setText("定时发送");
}
// 删除这行,避免禁用定时发送按钮
// ui->NewRegularSentButton->setEnabled(false);
}
}
}
void MainWindow::checkAndAddDeviceCommand(int row, int cmdCol, int protocolCol, int commCol, int timePoint)
{
// 确保列索引在有效范围内
if (row < 0 || row >= ui->tableWidget->rowCount() ||
protocolCol >= ui->tableWidget->columnCount() ||
cmdCol >= ui->tableWidget->columnCount() ||
commCol >= ui->tableWidget->columnCount())
{
return;
}
// 检查协议列是否有效且不为空 - 修改为发送协议而非命令
if (ui->tableWidget->item(row, protocolCol) && !ui->tableWidget->item(row, protocolCol)->text().isEmpty())
{
DeviceCommand cmd;
// 将要发送的内容设为协议字段而非指令名称
cmd.command = ui->tableWidget->item(row, protocolCol)->text();
cmd.row = row;
cmd.deviceCol = cmdCol; // 保留设备列位置以便可能的引用
cmd.interval = timePoint; // 使用传入的时间点
// 检测这是否是Hex格式数据 - 通常Hex格式数据只包含0-9和A-F字符以及空格
QString cmdText = cmd.command;
QRegularExpression hexRegex("^[A-Fa-f0-9\\s]+$");
cmd.isHex = hexRegex.match(cmdText).hasMatch();
// 获取指令名称(作为备注或记录,但不发送)
if (ui->tableWidget->item(row, cmdCol))
{
cmd.protocol = ui->tableWidget->item(row, cmdCol)->text();
}
else
{
cmd.protocol = ""; // 如果指令名称为空,则使用空字符串
}
// 获取通信参数
if (ui->tableWidget->item(row, commCol))
{
cmd.commParam = ui->tableWidget->item(row, commCol)->text();
// 确保通信参数有效
if (cmd.commParam.isEmpty())
{
// 尝试使用当前选中的串口
if (!ui->NewComcomboBox->currentText().isEmpty())
{
QString portName = ui->NewComcomboBox->currentData().toString();
cmd.commParam = QString("%1 %2").arg(currentSettings.baudRate).arg(portName);
}
else if (!ui->comComboBox->currentText().isEmpty())
{
QString portName = ui->comComboBox->currentData().toString();
cmd.commParam = QString("%1 %2").arg(currentSettings.baudRate).arg(portName);
}
else
{
// 默认使用 COM1
cmd.commParam = "115200 COM1";
}
}
}
else
{
// 使用默认通信参数
cmd.commParam = "115200 COM1";
}
// 添加到命令列表
commandsWithComm.append(cmd);
qDebug() << "添加命令:" << cmd.protocol << "协议:" << cmd.command
<< "通信:" << cmd.commParam << "时间点:" << cmd.interval
<< "格式:" << (cmd.isHex ? "HEX" : "ASCII");
}
}
// 修改 trySendDeviceCommand 方法兼容ASCII和HEX格式
bool MainWindow::trySendMQTTCommand(int row, int cmdCol, int protocolCol, int commCol, int topicCol)
{
// 检查协议列是否有效且不为空
if (ui->tableWidget->item(row, protocolCol) && !ui->tableWidget->item(row, protocolCol)->text().isEmpty())
{
// 获取要发送的协议
QString cmdName = ui->tableWidget->item(row, cmdCol)->text();
QString protocol = ui->tableWidget->item(row, protocolCol)->text();
// // 检测协议是否为HEX格式
// QRegularExpression hexRegex("^[A-Fa-f0-9\\s]+$");
// bool isHex = hexRegex.match(protocol).hasMatch();
// 获取指令名称(如果有)
QString topic;
if (ui->tableWidget->item(row, topicCol))
{
topic = ui->tableWidget->item(row, topicCol)->text();
}
// 获取通信参数(如果有)
QString commParam;
if (ui->tableWidget->item(row, commCol))
{
commParam = ui->tableWidget->item(row, commCol)->text();
}
// 如果有通信参数
MqttDealer* mqtt_s;
if (!commParam.isEmpty() && commParam.split(":").size() > 0)
{
// 检查此设备是否已经创建和打开
if (ConnectMQTTServer(commParam, mqtt_s))
{
// toDo 发送数据
m_mqttServers[commParam]->PublishDataByMQTT(ui->lineEdit_topic->text() + topic, protocol);
}
}
// 添加到发送历史记录
updateSendHistory(protocol, commParam, 0, cmdName, false);
QString displayCommand;
if (!cmdName.isEmpty())
{
displayCommand = QString("[%1] %2 -> %3").arg(cmdName).arg(protocol).arg(commParam);
}
else
{
displayCommand = QString("%1 -> %2").arg(protocol).arg(commParam);
}
// 记录发送的日志
if (m_logRecord->isLogging())
{
m_logRecord->logSend(displayCommand);
}
return true;
}
return false;
}
// 修改 trySendDeviceCommand 方法兼容ASCII和HEX格式
bool MainWindow::trySendDeviceCommand(int row, int cmdCol, int protocolCol, int commCol)
{
// 检查协议列是否有效且不为空
if (ui->tableWidget->item(row, protocolCol) && !ui->tableWidget->item(row, protocolCol)->text().isEmpty())
{
// 获取要发送的协议
QString protocol = ui->tableWidget->item(row, protocolCol)->text();
// 检测协议是否为HEX格式
QRegularExpression hexRegex("^[A-Fa-f0-9\\s]+$");
bool isHex = hexRegex.match(protocol).hasMatch();
// 获取指令名称(如果有)
QString cmdName;
if (ui->tableWidget->item(row, cmdCol))
{
cmdName = ui->tableWidget->item(row, cmdCol)->text();
}
// 获取通信参数(如果有)
QString commParam;
if (ui->tableWidget->item(row, commCol))
{
commParam = ui->tableWidget->item(row, commCol)->text();
}
// 如果有通信参数,尝试使用特定串口发送
if (!commParam.isEmpty())
{
// 解析通信参数
DeviceCommSettings commSettings = parseCommSettingsString(commParam);
// 如果指定了有效的串口名,且不是主串口,尝试使用特定串口发送
if (!commSettings.portName.isEmpty() &&
commSettings.portName != m_serialPort->portName())
{
QSerialPort* targetPort = nullptr;
// 检查此端口是否已经创建和打开
if (m_serialPorts.contains(commSettings.portName))
{
targetPort = m_serialPorts[commSettings.portName];
// 如果端口已经打开但参数不同,需要重新配置
if (targetPort->isOpen() &&
(targetPort->baudRate() != commSettings.baudRate ||
targetPort->dataBits() != commSettings.dataBits ||
targetPort->parity() != commSettings.parity ||
targetPort->stopBits() != commSettings.stopBits))
{
targetPort->close();
}
}
// 如果串口不存在或已关闭,需要创建或打开
if (!targetPort || !targetPort->isOpen())
{
bool result = openSerialPortForDevice(
commSettings.portName,
commSettings.baudRate,
commSettings.dataBits,
commSettings.parity,
commSettings.stopBits
);
if (!result)
{
qDebug() << "无法打开串口" << commSettings.portName << ",回退到主串口";
}
else
{
targetPort = m_serialPorts[commSettings.portName];
}
}
// 如果有可用的目标串口,发送数据
if (targetPort && targetPort->isOpen())
{
// 根据协议格式发送数据
QByteArray arr;
if (isHex)
{
arr = QString2Hex(protocol);
}
else
{
arr = protocol.toUtf8(); // 使用UTF-8编码ASCII文本
}
qint64 bytesWritten = targetPort->write(arr);
if (bytesWritten != arr.size())
{
qDebug() << "发送数据到串口" << commSettings.portName << "失败";
}
else
{
qDebug() << "成功发送" << (isHex ? "HEX" : "ASCII") << "命令到串口"
<< commSettings.portName << ":" << protocol;
// 在发送窗口添加发送记录
QString displayText;
if (!cmdName.isEmpty())
{
displayText = QString("[%1] %2 -> %3").arg(cmdName).arg(protocol).arg(commSettings.portName);
}
else
{
displayText = QString("%1 -> %2").arg(protocol).arg(m_serialPort->portName()); //arg(commSettings.portName);
}
if (isHex)
{
ui->sendTextEdit->append(displayText.toUpper());
}
else
{
ui->sendTextEdit->append(displayText);
}
// 更新发送计数
sendCount += arr.size();
ui->sendCountLabel->setNum(sendCount);
// 添加到发送历史记录
updateSendHistory(protocol, commSettings.portName, commSettings.baudRate, cmdName, isHex);
// 记录发送的日志
if (m_logRecord->isLogging())
{
if (isHex)
{
m_logRecord->logSend(displayText.toUpper());
}
else
{
m_logRecord->logSend(displayText);
}
}
return true;
}
}
}
}
// 回退到使用主串口发送
// 不再使用全局 isHexCheckBox而是根据协议自身判断
QByteArray arr;
if (isHex)
{
arr = QString2Hex(protocol);
m_serialPort->write(arr);
// 在发送编辑框中显示
QString displayText = protocol;
if (!cmdName.isEmpty())
{
displayText = QString("[%1] %2").arg(cmdName).arg(protocol);
}
ui->sendTextEdit->append(displayText.toUpper());
}
else
{
arr = protocol.toUtf8();
m_serialPort->write(arr);
// 在发送编辑框中显示
QString displayText = protocol;
if (!cmdName.isEmpty())
{
displayText = QString("[%1] %2").arg(cmdName).arg(protocol);
}
ui->sendTextEdit->append(displayText);
}
// 更新计数
sendCount += arr.size();
ui->sendCountLabel->setNum(sendCount);
// 记录日志
if (m_logRecord->isLogging())
{
QString displayText = protocol;
if (!cmdName.isEmpty())
{
displayText = QString("[%1] %2").arg(cmdName).arg(protocol);
}
if (isHex)
{
m_logRecord->logSend(displayText.toUpper());
}
else
{
m_logRecord->logSend(displayText);
}
}
// 添加到发送历史记录
updateSendHistory(protocol, m_serialPort->portName(), currentSettings.baudRate, cmdName, isHex);
return true;
}
return false;
}
bool MainWindow::ConnectMQTTServer(const QString& ipport, MqttDealer* mqtt_s)
{
if (ipport.isEmpty())
{
qDebug() << "服务器ip为空无法连接";
return false;
}
// 检查是否已有此端口实例
if (m_mqttServers.contains(ipport))
{
mqtt_s = m_mqttServers[ipport];
if (mqtt_s->IsOpen())
{
return true;
}
}
else
{
// 创建新服务器实例
mqtt_s = new MqttDealer();
}
// 设置参数
QStringList list = ipport.split(":");
if (list.size() <= 0)
{
return false;
}
if (!mqtt_s->Open(list.at(0).toStdString().c_str(), list[1].toInt()))
{
CommonHelper::ShowMessageBoxError(tr("当前MQTT通道%1连接失败\n请选择其他通道," \
"或者检查MQTT设置是否正确。").arg(list.at(0) + ":" + list.at(1)));
return false;
}
m_mqttServers[ipport] = mqtt_s;
return true;
}
void MainWindow::DisconnectMQTTServer(const QString& ipport)
{
if (m_mqttServers.contains(ipport))
{
MqttDealer* mqtt_server = m_mqttServers[ipport];
if (mqtt_server->IsOpen())
{
mqtt_server->Close();
}
}
}
bool MainWindow::openSerialPortForDevice(const QString& portName, int baudRate, QSerialPort::DataBits dataBits,
QSerialPort::Parity parity, QSerialPort::StopBits stopBits)
{
if (portName.isEmpty())
{
qDebug() << "串口名称为空,无法打开串口";
return false;
}
// 如果是主串口且已打开,直接返回成功
if (m_serialPort->isOpen() && m_serialPort->portName() == portName)
{
return true;
}
// 检查是否已有此端口实例
if (m_serialPorts.contains(portName))
{
QSerialPort* port = m_serialPorts[portName];
if (port->isOpen())
{
// 如果参数相同,不需要重新打开
if (port->baudRate() == baudRate &&
port->dataBits() == dataBits &&
port->parity() == parity &&
port->stopBits() == stopBits)
{
return true;
}
port->close(); // 关闭后重新设置参数
QThread::msleep(100); // 等待完全关闭
}
}
else
{
// 创建新串口实例
QSerialPort* port = new QSerialPort(this);
m_serialPorts[portName] = port;
// 连接错误信号
connect(port, SIGNAL(error(QSerialPort::SerialPortError)),
this, SLOT(handleSerialPortError(QSerialPort::SerialPortError)));
// 连接数据接收信号
connect(port, &QSerialPort::readyRead, [this, portName]()
{
if (m_serialPorts.contains(portName))
{
QSerialPort* port = m_serialPorts[portName];
QByteArray data = port->readAll();
if (!data.isEmpty())
{
QString str = ShowHex(data);
ui->receiveTextEdit->append("[" + portName + "] " + str.toUpper());
// 更新接收计数
receiveCount += data.count();
ui->receiveCountLabel->setNum(receiveCount);
// 记录日志
if (m_logRecord->isLogging())
{
m_logRecord->logReceive("[" + portName + "] " + str.toUpper());
}
}
}
});
}
// 设置串口参数
QSerialPort* port = m_serialPorts[portName];
port->setPortName(portName);
port->setBaudRate(baudRate);
port->setDataBits(dataBits);
port->setParity(parity);
port->setStopBits(stopBits);
port->setFlowControl(QSerialPort::NoFlowControl);
// 尝试打开串口
if (port->open(QIODevice::ReadWrite))
{
qDebug() << "成功打开串口" << portName << "波特率:" << baudRate;
return true;
}
else
{
qDebug() << "打开串口" << portName << "失败:" << port->errorString();
CommonHelper::ShowMessageBoxError(tr("无法打开串口 %1: %2").arg(portName).arg(port->errorString()));
return false;
}
}
void MainWindow::closeSerialPortForDevice(const QString& portName)
{
if (m_serialPorts.contains(portName))
{
QSerialPort* port = m_serialPorts[portName];
if (port->isOpen())
{
port->close();
}
}
}
// 添加缺失的基础函数实现
void MainWindow::initUi()
{
// 设置窗体标题栏隐藏
this->setWindowFlags(Qt::FramelessWindowHint | Qt::WindowSystemMenuHint | Qt::WindowMinimizeButtonHint);
// 显示在屏幕中间
CommonHelper::FormInCenter(this);
mousePressed = false;
//安装事件监听器,让标题栏识别鼠标双击
ui->lblTitle->installEventFilter(this);
FramelessHelper* pHelper = new FramelessHelper(this);
pHelper->activateOn(this); //激活当前窗体
pHelper->setTitleHeight(ui->wgtTitleBar->height()); //设置窗体的标题栏高度
// 初始化设置下拉框
ui->stackedWidget->setCurrentIndex(0);
ui->settingsGroupBox->hide();
// 添加波特率选项
ui->baudRateComboBox->addItem(QStringLiteral("1200"), 1200);
ui->baudRateComboBox->addItem(QStringLiteral("2400"), 2400);
ui->baudRateComboBox->addItem(QStringLiteral("4800"), 4800);
ui->baudRateComboBox->addItem(QStringLiteral("9600"), 9600);
ui->baudRateComboBox->addItem(QStringLiteral("19200"), 19200);
ui->baudRateComboBox->addItem(QStringLiteral("38400"), 38400);
ui->baudRateComboBox->addItem(QStringLiteral("57600"), 57600);
ui->baudRateComboBox->addItem(QStringLiteral("115200"), 115200);
ui->baudRateComboBox->setCurrentIndex(7); // 默认选择115200
ui->parityComboBox->addItem(tr("None"), QSerialPort::NoParity);
ui->parityComboBox->addItem(tr("Even"), QSerialPort::EvenParity);
ui->parityComboBox->addItem(tr("Odd"), QSerialPort::OddParity);
ui->dataBitsComboBox->addItem(QStringLiteral("8"), QSerialPort::Data8);
ui->dataBitsComboBox->addItem(QStringLiteral("7"), QSerialPort::Data7);
ui->dataBitsComboBox->addItem(QStringLiteral("6"), QSerialPort::Data6);
ui->dataBitsComboBox->addItem(QStringLiteral("5"), QSerialPort::Data5);
ui->dataBitsComboBox->setCurrentIndex(0);
ui->stopBitsComboBox->addItem(QStringLiteral("1"), QSerialPort::OneStop);
ui->stopBitsComboBox->addItem(QStringLiteral("2"), QSerialPort::TwoStop);
// 初始化单元表格
ui->tableWidget->horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents);
// 设置新的表格标题匹配CSV的13列格式 - 修改时间轴说明
// 设置新的表格标题匹配CSV的16列格式 - 添加了设备5
m_headers = QStringList{"序号", "时间点(S)", "设备1指令", "设备1协议", "设备1通讯",
"设备2指令", "设备2协议", "设备2通讯", "设备3指令", "设备3协议",
"设备3通讯", "设备4指令", "设备4协议", "设备4通讯", "设备5指令",
"设备5协议", "设备5通讯", "设备6指令",
"设备6协议", "设备6通讯", "设备6主题", "有效", "发送"};
ui->tableWidget->setColumnCount(COLUMN_MAX);
ui->tableWidget->setHorizontalHeaderLabels(m_headers);
// 设置列宽
for (int i = 0; i < COLUMN_MAX; ++i)
{
ui->tableWidget->setColumnWidth(i, i == 0 ? 60 : (i == 1 ? 80 : (i == COLUMN_VALID ? 60 : (i == COLUMN_SENDBUTTON ? 80 : 120))));
}
// 初始化串口连接图标
ui->connectInfo->setPixmap(QPixmap(":/skin/image/error.png"));
ui->connectInfo->setScaledContents(true);
// 获取系统日期时间
getCurrentDate();
// // 设置样式表
// QFile file(QString(":/skin/stylesheet/%1.qss").arg(styleStr));
// file.open(QFile::ReadOnly);
// QString qss = QLatin1String(file.readAll());
// qApp->setStyleSheet(qss);
// 添加翻译文件
QTranslator* translator = new QTranslator;
if (translator->load(":/qt_zh_CN.qm"))
{
qApp->installTranslator(translator);
}
// 获取并添加系统可用COM端口
foreach (const QSerialPortInfo& info, QSerialPortInfo::availablePorts())
{
ui->comComboBox->addItem(info.portName());
}
// 初始化日志按钮状态
ui->stopLogButton->setEnabled(false);
}
void MainWindow::connections()
{
// 串口数据接收
connect(m_serialPort, &QSerialPort::readyRead, this, &MainWindow::readData);
// 连接串口按钮
connect(ui->connectBtn, &QPushButton::clicked, this, &MainWindow::connectCOMButtonSlot);
// 发送数据相关 - 修正按钮名称
// connect(ui->sendDataBtn, &QPushButton::clicked, this, &MainWindow::sendDataSlot);
// connect(ui->backBtn, &QPushButton::clicked, this, &MainWindow::backSlot);
// 注释掉不存在的按钮连接或者根据实际UI文件中的按钮名称进行连接
// 表格操作
// connect(ui->addItemButton, &QPushButton::clicked, this, &MainWindow::addItemSlot);
connect(ui->removeItemButton, &QPushButton::clicked, this, &MainWindow::removeItemSlot);
connect(ui->clearAllBtn, &QPushButton::clicked, this, &MainWindow::clearAllSlot);
// 文件操作
connect(ui->loadFileBtn, &QPushButton::clicked, this, &MainWindow::loadFileSlot);
connect(ui->saveFileBtn, &QPushButton::clicked, this, &MainWindow::saveFileSlot);
// 滚动条自动滚动
connect(ui->sendTextEdit->verticalScrollBar(), &QScrollBar::valueChanged,
this, &MainWindow::sendTextAutoScrollSlot);
connect(ui->receiveTextEdit->verticalScrollBar(), &QScrollBar::valueChanged,
this, &MainWindow::receiveTextAutoScrollSlot);
// 同步下拉框
connect(ui->comComboBox, QOverload<int>::of(&QComboBox::currentIndexChanged),
this, &MainWindow::synchronizeComboBoxes);
connect(ui->NewComcomboBox, QOverload<int>::of(&QComboBox::currentIndexChanged),
this, &MainWindow::synchronizeComboBoxes);
}
void MainWindow::updateComSettings()
{
// 获取串口名称 - 优先使用主界面的选择
QString selectedPort;
if (!ui->NewComcomboBox->currentText().isEmpty())
{
selectedPort = ui->NewComcomboBox->currentText();
}
else if (!ui->comComboBox->currentText().isEmpty())
{
selectedPort = ui->comComboBox->currentText();
}
else
{
CommonHelper::ShowMessageBoxError(tr("请先选择串口!"));
return;
}
// 提取纯串口名(去掉可能的描述信息)
if (selectedPort.contains("("))
{
currentSettings.name = selectedPort.split("(").first().trimmed();
}
else
{
currentSettings.name = selectedPort.split(" ").first().trimmed();
}
// 获取波特率
bool ok;
int baudRate = ui->baudRateComboBox->currentData().toInt(&ok);
if (!ok)
{
baudRate = ui->baudRateComboBox->currentText().toInt(&ok);
if (!ok)
{
baudRate = 115200; // 默认波特率
}
}
currentSettings.baudRate = baudRate;
currentSettings.stringBaudRate = QString::number(baudRate);
// 获取数据位
currentSettings.dataBits = static_cast<QSerialPort::DataBits>(
ui->dataBitsComboBox->itemData(ui->dataBitsComboBox->currentIndex()).toInt());
currentSettings.stringDataBits = ui->dataBitsComboBox->currentText();
// 获取校验位
currentSettings.parity = static_cast<QSerialPort::Parity>(
ui->parityComboBox->itemData(ui->parityComboBox->currentIndex()).toInt());
currentSettings.stringParity = ui->parityComboBox->currentText();
// 获取停止位
currentSettings.stopBits = static_cast<QSerialPort::StopBits>(
ui->stopBitsComboBox->itemData(ui->stopBitsComboBox->currentIndex()).toInt());
currentSettings.stringStopBits = ui->stopBitsComboBox->currentText();
}
char MainWindow::ConvertHexChar(char ch)
{
if ((ch >= '0') && (ch <= '9'))
return ch - 0x30;
else if ((ch >= 'A') && (ch <= 'F'))
return ch - 'A' + 10;
else if ((ch >= 'a') && (ch <= 'f'))
return ch - 'a' + 10;
else
return (-1);
}
// QByteArray MainWindow::QString2Hex(QString str)
// {
// QByteArray senddata;
// int hexdata,lowhexdata;
// int hexdatalen = 0;
// int len = str.length();
// senddata.resize(len/2);
// char lstr,hstr;
// for(int i=0; i<len; )
// {
// hstr=str[i].toLatin1();
// if(hstr == ' ')
// {
// i++;
// continue;
// }
// i++;
// if(i >= len)
// break;
// lstr = str[i].toLatin1();
// hexdata = ConvertHexChar(hstr);
// lowhexdata = ConvertHexChar(lstr);
// if((hexdata == 16) || (lowhexdata == 16))
// break;
// else
// hexdata = hexdata*16+lowhexdata;
// i++;
// senddata[hexdatalen] = (char)hexdata;
// hexdatalen++;
// }
// senddata.resize(hexdatalen);
// return senddata;
// }
QByteArray MainWindow::QString2Hex(QString str)
{
QByteArray senddata;
int hexdata, lowhexdata;
int hexdatalen = 0;
int len = str.length();
// 预先计算可能的最大容量假设所有字符都是有效的hex字符
senddata.resize((len + 1) / 2);
char lstr, hstr;
for (int i = 0; i < len;)
{
hstr = str[i].toLatin1();
if (hstr == ' ' || hstr == '\t' || hstr == '\r' || hstr == '\n')
{
// 跳过所有空白字符
i++;
continue;
}
i++;
if (i >= len)
break;
lstr = str[i].toLatin1();
hexdata = ConvertHexChar(hstr);
lowhexdata = ConvertHexChar(lstr);
if ((hexdata == -1) || (lowhexdata == -1))
{
// 遇到无效的hex字符跳过这对字符
i++;
continue;
}
else
{
hexdata = hexdata * 16 + lowhexdata;
}
i++;
senddata[hexdatalen] = (char)hexdata;
hexdatalen++;
}
// 调整到实际使用的大小
senddata.resize(hexdatalen);
return senddata;
}
QString MainWindow::ShowHex(QByteArray data)
{
QString temp = "";
QString hex = data.toHex();
for (int i = 0; i < hex.length(); i += 2)
{
temp += hex.mid(i, 2) + " ";
}
return temp.toUpper();
}
void MainWindow::getCurrentDate()
{
currentDateTime = QDateTime::currentDateTime();
currentTime = QTime::currentTime();
// 根据时间设置样式
if (currentTime.hour() >= 6 && currentTime.hour() < COLUMN_SENDBUTTON)
{
styleStr = "green"; // 白天模式
}
else
{
styleStr = "dark"; // 夜间模式
}
}
void MainWindow::setWinterTime()
{
styleStr = "green";
}
void MainWindow::setSummerTime()
{
styleStr = "green";
}
char MainWindow::xorCheck(QByteArray array)
{
char xorData = 0;
for (int i = 0; i < array.size(); i++)
{
xorData ^= array.at(i);
}
return xorData;
}
char MainWindow::sumCheck(QByteArray array)
{
char sumData = 0;
for (int i = 0; i < array.size(); i++)
{
sumData += array.at(i);
}
return sumData;
}
void MainWindow::sendFile()
{
if (bSendFile)
{
m_lSendPoint = 0;
bSendFile = false;
sendFileTimer->start(1);
}
}
void MainWindow::removeListSame(QStringList* list)
{
for (int i = 0; i < list->size(); i++)
{
for (int j = i + 1; j < list->size();)
{
if (list->at(i) == list->at(j))
{
list->removeAt(j);
}
else
{
j++;
}
}
}
}
void MainWindow::on_pushButton_mqttBtn_clicked()
{
QString ip = ui->lineEdit_ip->text();
QString port = ui->lineEdit_port->text();
QString ipPort = ip + ":" + port;
// mqtt连接
if (!b_mqtt_connected)
{
if (!ipPort.isEmpty() && ipPort.split(":").size() > 0)
{
// 检查此设备是否已经创建和打开
if (!ConnectMQTTServer(ipPort, m_mqttClient))
{
return;
}
b_mqtt_connected = true;
ui->lineEdit_ip->setDisabled(true);
ui->lineEdit_port->setDisabled(true);
ui->lineEdit_topic->setDisabled(true);
ui->pushButton_mqttBtn->setText("已连接");
}
}
else
{
b_mqtt_connected = false;
DisconnectMQTTServer(ipPort);
ui->lineEdit_ip->setDisabled(false);
ui->lineEdit_port->setDisabled(false);
ui->lineEdit_topic->setDisabled(false);
ui->pushButton_mqttBtn->setText("连接");
}
}