Tuxi_Test_Qt/src/mainwindow.cpp
2025-09-11 10:12:19 +08:00

1237 lines
49 KiB
C++
Raw 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.

#include "mainwindow.h"
#include "lightstripmanager.h"
#include <QDateTime>
#include <QJsonObject>
#include <QJsonDocument>
//#include <QNetworkAccessManager>
//#include <QNetworkRequest>
//#include <QNetworkReply>
//#include <QFileInfo>
#include <QDir>
#include <QStandardPaths>
#include <QMessageBox>
#include <QUrl>
#include <QIcon>
#include <QDebug>
#include <QScreen>
#include <QGuiApplication>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent), otaOperationPending(false), isUpgradeOperation(true)
{
// 初始化设置对象
settings = new QSettings("TuxiApp", "LightStripSN", this);
setupUI();
// 创建MQTT客户端
mqttClient = new MqttClient(this);
// 连接信号和槽
connect(connectBtn, &QPushButton::clicked, this, &MainWindow::onConnectClicked);
connect(disconnectBtn, &QPushButton::clicked, this, &MainWindow::onDisconnectClicked);
connect(sendLightAllBtn, &QPushButton::clicked, this, &MainWindow::onSendLightAllClicked);
connect(clearSnBtn, &QPushButton::clicked, this, &MainWindow::onClearSnListClicked);
connect(searchLightStripBtn, &QPushButton::clicked, this, &MainWindow::onSearchLightStripClicked);
connect(mqttClient, &MqttClient::connected, this, &MainWindow::onMqttConnected);
connect(mqttClient, &MqttClient::disconnected, this, &MainWindow::onMqttDisconnected);
connect(mqttClient, &MqttClient::messageReceived, this, &MainWindow::onMessageReceived);
connect(mqttClient, &MqttClient::errorOccurred, this, &MainWindow::onMqttError);
connect(otaUpgradeBtn, &QPushButton::clicked, this, &MainWindow::onOtaUpgradeClicked);
connect(otaDowngradeBtn, &QPushButton::clicked, this, &MainWindow::onOtaDowngradeClicked);
connect(getVersionBtn, &QPushButton::clicked, this, &MainWindow::onGetVersionClicked);
// 初始化灯条管理器
lightStripManager = nullptr;
// 加载已保存的灯条SN列表
loadSnList();
// 初始化状态
updateConnectionStatus(false);
currentVersionEdit->setText("请点击获取版本");
}
MainWindow::~MainWindow()
{
}
void MainWindow::setupUI() {
setWindowTitle("兔喜Test Author:Zhangzhenghao Email:zzh9953477@gmail.com");
// 参考qt_bak的合理尺寸设置增加竖向高度
setMinimumSize(850, 720); // 增加最小高度
resize(900, 770); // 增加初始高度从750增加到850
// 获取屏幕尺寸并设置合适的窗口大小
QScreen *screen = QGuiApplication::primaryScreen();
if (screen) {
QRect screenGeometry = screen->geometry();
int screenWidth = screenGeometry.width();
int screenHeight = screenGeometry.height();
// 设置合理的窗口大小,增加竖向比例
int windowWidth = qMin(1000, static_cast<int>(screenWidth * 0.6));
int windowHeight = qMin(900, static_cast<int>(screenHeight * 0.7)); // 增加到75%
// 确保高度明显大于宽度
if (windowHeight < windowWidth * 0.95) {
windowHeight = static_cast<int>(windowWidth * 0.9); // 高度等于宽度
}
resize(windowWidth, windowHeight);
// 居中显示
move((screenWidth - windowWidth) / 2, (screenHeight - windowHeight) / 2 - 50);
}
setWindowIcon(QIcon(":/image/src/tuxi.ico"));
centralWidget = new QWidget(this);
setCentralWidget(centralWidget);
mainLayout = new QVBoxLayout(centralWidget);
mainLayout->setSpacing(10); // 减少布局间距
mainLayout->setContentsMargins(15, 10, 15, 10); // 减少边距
// MQTT连接区域
connectionGroup = new QGroupBox("MQTT连接设置", this);
connectionGroup->setStyleSheet("QGroupBox { font-weight: bold; font-size: 12px; padding-top: 10px; }");
QVBoxLayout *connectionLayout = new QVBoxLayout(connectionGroup);
connectionLayout->setSpacing(10);
// 服务器和端口 - 使用水平布局
QHBoxLayout *serverLayout = new QHBoxLayout();
serverLayout->addWidget(new QLabel("服务器:"));
brokerEdit = new QLineEdit("tx-mqtt.zt-express.com");
brokerEdit->setMinimumHeight(30);
serverLayout->addWidget(brokerEdit);
serverLayout->addWidget(new QLabel("端口:"));
portEdit = new QLineEdit("1883");
portEdit->setMinimumHeight(30);
portEdit->setMaximumWidth(100);
serverLayout->addWidget(portEdit);
connectionLayout->addLayout(serverLayout);
// 用户名和密码
QHBoxLayout *authLayout = new QHBoxLayout();
authLayout->addWidget(new QLabel("用户名:"));
usernameEdit = new QLineEdit("TJ251679787196");
usernameEdit->setMinimumHeight(30);
authLayout->addWidget(usernameEdit);
authLayout->addWidget(new QLabel("密码:"));
passwordEdit = new QLineEdit();
passwordEdit->setEchoMode(QLineEdit::Password);
passwordEdit->setMinimumHeight(30);
authLayout->addWidget(passwordEdit);
connectionLayout->addLayout(authLayout);
// 连接按钮
QHBoxLayout *btnLayout = new QHBoxLayout();
connectBtn = new QPushButton("连接");
connectBtn->setMinimumHeight(35);
connectBtn->setStyleSheet("QPushButton { background-color: #4CAF50; color: white; font-weight: bold; padding: 8px 16px; border-radius: 4px; }");
disconnectBtn = new QPushButton("断开");
disconnectBtn->setMinimumHeight(35);
disconnectBtn->setStyleSheet("QPushButton { background-color: #f44336; color: white; font-weight: bold; padding: 8px 16px; border-radius: 4px; }");
btnLayout->addWidget(connectBtn);
btnLayout->addWidget(disconnectBtn);
btnLayout->addStretch();
connectionLayout->addLayout(btnLayout);
mainLayout->addWidget(connectionGroup);
// 设备控制区域
deviceGroup = new QGroupBox("设备控制", this);
deviceGroup->setStyleSheet("QGroupBox { font-weight: bold; font-size: 12px; padding-top: 10px; }");
QHBoxLayout *deviceLayout = new QHBoxLayout(deviceGroup);
deviceLayout->setSpacing(10);
deviceLayout->addWidget(new QLabel("需要测试的设备SN:"));
deviceSnEdit = new QLineEdit("TJ251617198122");
deviceSnEdit->setMinimumHeight(30);
deviceLayout->addWidget(deviceSnEdit);
mainLayout->addWidget(deviceGroup);
// 点亮参数控制区域
lightGroup = new QGroupBox("点亮参数设置", this);
lightGroup->setStyleSheet("QGroupBox { font-weight: bold; font-size: 12px; padding-top: 10px; }");
QVBoxLayout *lightLayout = new QVBoxLayout(lightGroup);
lightLayout->setSpacing(10);
// 第一行:颜色和闪烁
QHBoxLayout *row1Layout = new QHBoxLayout();
row1Layout->addWidget(new QLabel("颜色:"));
colorCombo = new QComboBox();
colorCombo->addItems({"8-灭", "1-红", "2-黄", "3-蓝", "4-绿", "5-青", "6-白", "7-紫"});
colorCombo->setCurrentIndex(6);
colorCombo->setMinimumHeight(30);
row1Layout->addWidget(colorCombo);
row1Layout->addWidget(new QLabel("闪烁:"));
flashCombo = new QComboBox();
flashCombo->addItems({"0-关闭", "1-开启"});
flashCombo->setCurrentIndex(0);
flashCombo->setMinimumHeight(30);
row1Layout->addWidget(flashCombo);
row1Layout->addStretch();
lightLayout->addLayout(row1Layout);
// 第二行:闪烁间隔和点亮时长
QHBoxLayout *row2Layout = new QHBoxLayout();
row2Layout->addWidget(new QLabel("闪烁间隔(秒):"));
flashIntervalSpin = new QSpinBox();
flashIntervalSpin->setRange(1, 60);
flashIntervalSpin->setValue(4);
flashIntervalSpin->setMinimumHeight(30);
row2Layout->addWidget(flashIntervalSpin);
row2Layout->addWidget(new QLabel("点亮时长(秒):"));
lightDurationSpin = new QSpinBox();
lightDurationSpin->setRange(1, 300);
lightDurationSpin->setValue(30);
lightDurationSpin->setMinimumHeight(30);
row2Layout->addWidget(lightDurationSpin);
row2Layout->addStretch();
lightLayout->addLayout(row2Layout);
// 第三行:声音和发送按钮
QHBoxLayout *row3Layout = new QHBoxLayout();
row3Layout->addWidget(new QLabel("声音:"));
soundCombo = new QComboBox();
soundCombo->addItems({"0-关闭", "1-开启"});
soundCombo->setCurrentIndex(0);
soundCombo->setMinimumHeight(30);
row3Layout->addWidget(soundCombo);
row3Layout->addStretch();
sendLightAllBtn = new QPushButton("发送全部点亮");
sendLightAllBtn->setMinimumHeight(40);
sendLightAllBtn->setMinimumWidth(120);
sendLightAllBtn->setStyleSheet("QPushButton { background-color: #4CAF50; color: white; font-weight: bold; padding: 10px 20px; border-radius: 6px; font-size: 14px; }");
row3Layout->addWidget(sendLightAllBtn);
lightLayout->addLayout(row3Layout);
mainLayout->addWidget(lightGroup);
// 灯条SN管理区域 - 简化为一个按钮
snGroup = new QGroupBox("灯条SN管理", this);
snGroup->setStyleSheet(
"QGroupBox { "
" font-weight: bold; "
" font-size: 14px; "
" padding-top: 15px; "
" margin: 5px; "
" border: 2px solid #ddd; "
" border-radius: 8px; "
"}"
);
snGroup->setMinimumHeight(120); // 减少最小高度
snGroup->setMaximumHeight(140); // 设置最大高度限制
QVBoxLayout *snLayout = new QVBoxLayout(snGroup);
snLayout->setSpacing(8); // 减少组件间距
snLayout->setContentsMargins(10, 20, 10, 10); // 减少内边距
// 统计信息和搜索按钮布局
QHBoxLayout *snHeaderLayout = new QHBoxLayout();
snHeaderLayout->setSpacing(8);
snCountLabel = new QLabel("已发现灯条: 0 个");
snCountLabel->setStyleSheet(
"QLabel { "
" font-weight: bold; "
" font-size: 13px; "
" color: #2196F3; "
" padding: 3px 8px; "
" background-color: #f0f8ff; "
" border: 1px solid #ddd; "
" border-radius: 6px; "
" min-height: 12px; "
"}"
);
snHeaderLayout->addWidget(snCountLabel);
snHeaderLayout->addStretch();
searchLightStripBtn = new QPushButton("搜索灯条", this);
searchLightStripBtn->setMinimumHeight(26); // 减少按钮高度
searchLightStripBtn->setMinimumWidth(90);
searchLightStripBtn->setStyleSheet(
"QPushButton { "
" background-color: #2196F3; "
" color: white; "
" padding: 4px 10px; "
" font-weight: bold; "
" border-radius: 6px; "
" font-size: 12px; "
" border: none; "
"} "
"QPushButton:hover { "
" background-color: #1976D2; "
"}"
);
snHeaderLayout->addWidget(searchLightStripBtn);
snLayout->addLayout(snHeaderLayout);
// 打开管理器按钮
openManagerBtn = new QPushButton("打开灯条SN管理器");
openManagerBtn->setMinimumHeight(32); // 减少按钮高度
openManagerBtn->setStyleSheet(
"QPushButton { "
" background-color: #2196F3; "
" color: white; "
" font-weight: bold; "
" font-size: 14px; "
" padding: 6px 16px; "
" border-radius: 8px; "
" border: none; "
" margin: 1px; "
"} "
"QPushButton:hover { "
" background-color: #1976D2; "
"} "
"QPushButton:pressed { "
" background-color: #1565C0; "
"}"
);
connect(openManagerBtn, &QPushButton::clicked, this, &MainWindow::openLightStripManager);
snLayout->addWidget(openManagerBtn);
mainLayout->addWidget(snGroup);
// 消息显示区域
messageGroup = new QGroupBox("消息日志", this);
QVBoxLayout *msgLayout = new QVBoxLayout(messageGroup);
messageDisplay = new QTextEdit();
messageDisplay->setReadOnly(true);
msgLayout->addWidget(messageDisplay);
mainLayout->addWidget(messageGroup);
// 状态栏
statusLabel = new QLabel("未连接");
statusBar()->addWidget(statusLabel);
// OTA升级区域
otaGroup = new QGroupBox("OTA升降级", this);
QVBoxLayout *otaLayout = new QVBoxLayout(otaGroup);
// 当前版本显示
QHBoxLayout *versionLayout = new QHBoxLayout();
versionLayout->addWidget(new QLabel("当前版本:"));
currentVersionEdit = new QLineEdit(this);
currentVersionEdit->setReadOnly(true);
versionLayout->addWidget(currentVersionEdit);
// 创建获取版本按钮
getVersionBtn = new QPushButton("获取版本", this);
versionLayout->addWidget(getVersionBtn);
otaLayout->addLayout(versionLayout);
// OTA按钮
QHBoxLayout *otaButtonLayout = new QHBoxLayout();
otaUpgradeBtn = new QPushButton("升级", this);
otaDowngradeBtn = new QPushButton("降级", this);
otaButtonLayout->addWidget(otaUpgradeBtn);
otaButtonLayout->addWidget(otaDowngradeBtn);
otaLayout->addLayout(otaButtonLayout);
// 下载进度条
otaProgressBar = new QProgressBar(this); // 修正变量名
otaProgressBar->setVisible(false);
otaLayout->addWidget(otaProgressBar);
// OTA状态标签
otaStatusLabel = new QLabel("就绪", this);
otaLayout->addWidget(otaStatusLabel);
mainLayout->addWidget(otaGroup);
}
void MainWindow::onConnectClicked() {
QString broker = brokerEdit->text().trimmed();
int portNum = portEdit->text().toInt();
QString username = usernameEdit->text().trimmed();
QString password = passwordEdit->text();
QString deviceSn = deviceSnEdit->text().trimmed();
if (broker.isEmpty()) {
QMessageBox::warning(this, "警告", "请输入MQTT服务器地址");
return;
}
if (portNum <= 0 || portNum > 65535) {
QMessageBox::warning(this, "警告", "请输入有效的端口号 (1-65535)");
return;
}
// 设置用户名和密码(如果有的话)
if (!username.isEmpty()) {
mqttClient->setUsername(username);
}
if (!password.isEmpty()) {
mqttClient->setPassword(password);
}
// 连接到MQTT服务器
mqttClient->connectToHost(broker, static_cast<quint16>(portNum));
// 更新状态
statusLabel->setText("正在连接...");
connectBtn->setEnabled(false);
// 注意订阅操作应该在连接成功后进行在onMqttConnected()函数中处理
}
void MainWindow::onDisconnectClicked()
{
mqttClient->disconnectFromHost();
}
void MainWindow::onSendLightAllClicked()
{
QString deviceSn = deviceSnEdit->text().trimmed();
if (deviceSn.isEmpty()) {
messageDisplay->append(QString("[%1] 错误: 请输入设备SN")
.arg(QDateTime::currentDateTime().toString("hh:mm:ss")));
return;
}
if (!mqttClient->isConnected()) {
messageDisplay->append(QString("[%1] 错误: MQTT未连接")
.arg(QDateTime::currentDateTime().toString("hh:mm:ss")));
return;
}
// 获取参数值
QString color = QString::number(colorCombo->currentIndex());
QString flash = QString::number(flashCombo->currentIndex());
QString flashInterval = QString::number(flashIntervalSpin->value());
QString lightDuration = QString::number(lightDurationSpin->value());
QString sound = QString::number(soundCombo->currentIndex());
// 构造消息内容
QJsonObject dataObj;
dataObj["color"] = color;
dataObj["flash"] = flash;
dataObj["flashInterval"] = flashInterval;
dataObj["lightDuration"] = lightDuration;
dataObj["sound"] = sound;
QJsonObject msgObj;
msgObj["data"] = dataObj;
msgObj["msgType"] = "3027";
QJsonObject rootObj;
rootObj["deviceId"] = deviceSn;
rootObj["messageId"] = "1933039995430551552";
// 修复将QByteArray转换为QString
rootObj["msg"] = QString::fromUtf8(QJsonDocument(msgObj).toJson(QJsonDocument::Compact));
rootObj["timestamp"] = 2147483647;
QString message = QJsonDocument(rootObj).toJson(QJsonDocument::Compact);
// 发送到指定主题
QString topic = QString("iot/10045/%1/message/adviceDevice").arg(deviceSn);
if (mqttClient->publish(topic, message)) {
messageDisplay->append(QString("[%1] 发送全部点亮指令到设备 %2")
.arg(QDateTime::currentDateTime().toString("hh:mm:ss"))
.arg(deviceSn));
messageDisplay->append(QString("主题: %1").arg(topic));
messageDisplay->append(QString("参数: 颜色=%1, 闪烁=%2, 间隔=%3s, 时长=%4s, 声音=%5")
.arg(colorCombo->currentText())
.arg(flashCombo->currentText())
.arg(flashInterval)
.arg(lightDuration)
.arg(soundCombo->currentText()));
} else {
messageDisplay->append(QString("[%1] 发送全部点亮指令失败")
.arg(QDateTime::currentDateTime().toString("hh:mm:ss")));
}
}
void MainWindow::onMqttConnected() {
statusLabel->setText("MQTT连接成功");
statusLabel->setStyleSheet("QLabel { color: green; font-weight: bold; }");
updateConnectionStatus(true);
QString deviceSn = deviceSnEdit->text().trimmed();
if (!deviceSn.isEmpty()) {
// 订阅原有主题
QString responseTopic = QString("iot/10045/%1/response").arg(deviceSn);
QString stationTopic = QString("iot/10045/%1/station/report").arg(deviceSn);
// 订阅light report主题
QString lightReportTopic = QString("iot/10045/%1/light/report").arg(deviceSn);
// 订阅adviceDevice主题用于搜索灯条响应
QString adviceDeviceTopic = QString("iot/10045/%1/message/adviceDevice").arg(deviceSn);
QString resourceReportTopic = QString("iot/10045/%1/resource/report").arg(deviceSn);
mqttClient->subscribe(responseTopic);
mqttClient->subscribe(stationTopic);
mqttClient->subscribe(lightReportTopic);
mqttClient->subscribe(adviceDeviceTopic);
mqttClient->subscribe(resourceReportTopic);
messageDisplay->append(QString("[%1] 已订阅主题: %2")
.arg(QDateTime::currentDateTime().toString("hh:mm:ss"))
.arg(responseTopic));
messageDisplay->append(QString("[%1] 已订阅主题: %2")
.arg(QDateTime::currentDateTime().toString("hh:mm:ss"))
.arg(stationTopic));
messageDisplay->append(QString("[%1] 已订阅主题: %2")
.arg(QDateTime::currentDateTime().toString("hh:mm:ss"))
.arg(lightReportTopic));
messageDisplay->append(QString("[%1] 已订阅主题: %2")
.arg(QDateTime::currentDateTime().toString("hh:mm:ss"))
.arg(adviceDeviceTopic));
messageDisplay->append(QString("[%1] 已订阅主题: %2")
.arg(QDateTime::currentDateTime().toString("hh:mm:ss"))
.arg(resourceReportTopic));
}
}
void MainWindow::onMqttDisconnected()
{
messageDisplay->append(QString("[%1] MQTT连接断开")
.arg(QDateTime::currentDateTime().toString("hh:mm:ss")));
updateConnectionStatus(false);
}
void MainWindow::onMqttError(const QString &error)
{
messageDisplay->append(QString("[%1] MQTT错误: %2")
.arg(QDateTime::currentDateTime().toString("hh:mm:ss"))
.arg(error));
updateConnectionStatus(false);
}
void MainWindow::onMessageReceived(const QString &topic, const QString &message) {
// 显示接收到的消息
messageDisplay->append(QString("[%1] 收到消息").arg(QDateTime::currentDateTime().toString("hh:mm:ss")));
messageDisplay->append(QString("Topic: %1").arg(topic));
messageDisplay->append(QString("Message: %1").arg(message));
messageDisplay->append("---");
// 处理消息格式问题提取纯JSON部分
QString jsonMessage = message;
QString realTopic = topic;
// 如果topic是默认的incoming/topic尝试从消息中提取真实主题
if (topic == "incoming/topic" && message.contains("{")) {
// 查找消息中的主题信息(格式:%topic{json}
if (message.startsWith("%")) {
int jsonStart = message.indexOf("{");
if (jsonStart > 1) {
realTopic = message.mid(1, jsonStart - 1); // 去掉开头的%
messageDisplay->append(QString("[DEBUG] 从消息中提取到真实主题: %1").arg(realTopic));
}
}
}
// 去除开头的单引号
if (jsonMessage.startsWith("'")) {
jsonMessage = jsonMessage.mid(1);
}
// 去除结尾的单引号
if (jsonMessage.endsWith("'")) {
jsonMessage = jsonMessage.left(jsonMessage.length() - 1);
}
// 检查消息是否包含主题信息(格式:%topic{json}或topic{json}
if (jsonMessage.contains("{")) {
int jsonStart = jsonMessage.indexOf("{");
if (jsonStart != -1) {
jsonMessage = jsonMessage.mid(jsonStart);
}
}
// 解析JSON消息
QJsonParseError error;
QJsonDocument doc = QJsonDocument::fromJson(jsonMessage.toUtf8(), &error);
if (error.error != QJsonParseError::NoError) {
messageDisplay->append(QString("[%1] JSON解析错误: %2")
.arg(QDateTime::currentDateTime().toString("hh:mm:ss"))
.arg(error.errorString()));
return;
}
QJsonObject root = doc.object();
messageDisplay->append(QString("[%1] JSON解析成功").arg(QDateTime::currentDateTime().toString("hh:mm:ss")));
// 检查是否包含msg字段优先处理
if (root.contains("msg")) {
messageDisplay->append(QString("[%1] 找到msg字段").arg(QDateTime::currentDateTime().toString("hh:mm:ss")));
QJsonValue msgValue = root["msg"];
QJsonObject msgObj;
// msg可能是字符串或对象
if (msgValue.isString()) {
QJsonDocument msgDoc = QJsonDocument::fromJson(msgValue.toString().toUtf8());
if (!msgDoc.isNull()) {
msgObj = msgDoc.object();
}
} else if (msgValue.isObject()) {
msgObj = msgValue.toObject();
messageDisplay->append(QString("[%1] msg是对象类型").arg(QDateTime::currentDateTime().toString("hh:mm:ss")));
}
// 解析data中的baseVersion
if (msgObj.contains("data")) {
messageDisplay->append(QString("[%1] 找到data字段").arg(QDateTime::currentDateTime().toString("hh:mm:ss")));
QJsonObject dataObj = msgObj["data"].toObject();
if (dataObj.contains("baseVersion")) {
QString version = dataObj["baseVersion"].toString();
messageDisplay->append(QString("[%1] 找到baseVersion: %2")
.arg(QDateTime::currentDateTime().toString("hh:mm:ss"))
.arg(version));
// 确保currentVersionEdit存在
if (currentVersionEdit) {
currentVersionEdit->setText(version);
messageDisplay->append(QString("[%1] 版本已设置到输入框: %2")
.arg(QDateTime::currentDateTime().toString("hh:mm:ss"))
.arg(version));
} else {
messageDisplay->append(QString("[%1] 错误: currentVersionEdit为空")
.arg(QDateTime::currentDateTime().toString("hh:mm:ss")));
}
statusLabel->setText(QString("获取到当前版本: %1").arg(version));
return;
}
}
}
// 检查是否包含resource字段直接在根对象中
if (root.contains("resource")) {
QString resource = root["resource"].toString();
QString version = resource.trimmed();
messageDisplay->append(QString("[%1] 找到resource字段: %2")
.arg(QDateTime::currentDateTime().toString("hh:mm:ss"))
.arg(version));
if (currentVersionEdit) {
currentVersionEdit->setText(version);
messageDisplay->append(QString("[%1] 版本已设置到输入框: %2")
.arg(QDateTime::currentDateTime().toString("hh:mm:ss"))
.arg(version));
}
statusLabel->setText(QString("获取到当前版本: %1").arg(version));
return;
}
// 检查是否是light/report主题使用提取的真实主题
if (realTopic.contains("/light/report")) {
messageDisplay->append("[DEBUG] 检测到light/report消息开始解析SN");
processLightReportMessage(jsonMessage);
}
// 检查是否是station/report主题且有待处理的OTA操作
if (otaOperationPending && (realTopic.contains("/station/report") || realTopic.contains("/resource/report"))) {
QString operationType = isUpgradeOperation ? "升级" : "降级";
QString successMessage = QString("OTA%1成功 版本: %2")
.arg(operationType)
.arg(pendingOtaVersion);
messageDisplay->append(QString("[%1] %2")
.arg(QDateTime::currentDateTime().toString("hh:mm:ss"))
.arg(successMessage));
// 重置OTA状态
otaOperationPending = false;
pendingOtaVersion.clear();
isUpgradeOperation = false;
statusLabel->setText(successMessage);
}
}
void MainWindow::processOtaMessage(const QJsonObject &otaData)
{
QString version = otaData["version"].toString();
QString zipPath = otaData["zipPath"].toString();
int installType = otaData["installType"].toInt();
int immediately = otaData["immediately"].toInt();
otaStatusLabel->setText(QString("收到OTA任务: 版本 %1").arg(version));
}
void MainWindow::onOtaUpgradeClicked() {
// 获取设备SN
QString deviceSn = deviceSnEdit->text().trimmed();
if (deviceSn.isEmpty()) {
QMessageBox::warning(this, "警告", "请先输入设备SN");
return;
}
// 设置OTA状态跟踪
otaOperationPending = true;
pendingOtaVersion = "1.1.41";
isUpgradeOperation = true;
// 构造符合您格式的OTA升级消息
QJsonObject data;
data["msgType"] = "1014";
QJsonObject otaData;
otaData["needWifi"] = 2;
otaData["zipPath"] = "http://180.163.74.83:8000/tx_ota_1.1.41.zip";
otaData["installType"] = 2;
otaData["immediately"] = 0;
otaData["installTime"] = QDateTime::currentMSecsSinceEpoch() + 300000; // 5分钟后安装
otaData["version"] = "1.1.41";
data["data"] = otaData;
QJsonObject root;
root["deviceId"] = deviceSn; // 使用输入的设备SN
root["header"] = QJsonValue::Null;
root["messageId"] = QString::number(QDateTime::currentMSecsSinceEpoch());
root["msg"] = QString::fromUtf8(QJsonDocument(data).toJson(QJsonDocument::Compact));
root["productId"] = "";
root["timestamp"] = QDateTime::currentMSecsSinceEpoch();
QJsonDocument doc(root);
QString message = QString::fromUtf8(doc.toJson(QJsonDocument::Compact));
// 构建正确的MQTT主题
QString topic = QString("iot/10045/%1/message/adviceDevice").arg(deviceSn);
if (mqttClient && mqttClient->isConnected()) {
mqttClient->publish(topic, message);
statusLabel->setText(QString("已发送OTA升级命令到设备 %1 (版本 1.1.41)").arg(deviceSn));
// 显示发送的消息内容
QMessageBox::information(this, "OTA升级命令",
QString("已发送升级命令:\n设备SN: %1\n版本: 1.1.41\n主题: %2\n消息: %3")
.arg(deviceSn, topic, message));
} else {
statusLabel->setText("MQTT未连接无法发送OTA命令");
// 如果发送失败,重置状态
otaOperationPending = false;
}
}
void MainWindow::onOtaDowngradeClicked() {
// 获取设备SN
QString deviceSn = deviceSnEdit->text().trimmed();
if (deviceSn.isEmpty()) {
QMessageBox::warning(this, "警告", "请先输入设备SN");
return;
}
// 设置OTA状态跟踪
otaOperationPending = true;
pendingOtaVersion = "1.1.40";
isUpgradeOperation = false;
// 构造符合您格式的OTA降级消息
QJsonObject data;
data["msgType"] = "1014";
QJsonObject otaData;
otaData["needWifi"] = 2;
otaData["zipPath"] = "http://180.163.74.83:8000/tx_ota_1.1.40.zip";
otaData["installType"] = 2;
otaData["immediately"] = 0;
otaData["installTime"] = QDateTime::currentMSecsSinceEpoch() + 300000; // 5分钟后安装
otaData["version"] = "1.1.40";
data["data"] = otaData;
QJsonObject root;
root["deviceId"] = deviceSn; // 使用输入的设备SN
root["header"] = QJsonValue::Null;
root["messageId"] = QString::number(QDateTime::currentMSecsSinceEpoch());
root["msg"] = QString::fromUtf8(QJsonDocument(data).toJson(QJsonDocument::Compact));
root["productId"] = "";
root["timestamp"] = QDateTime::currentMSecsSinceEpoch();
QJsonDocument doc(root);
QString message = QString::fromUtf8(doc.toJson(QJsonDocument::Compact));
// 构建正确的MQTT主题
QString topic = QString("iot/10045/%1/message/adviceDevice").arg(deviceSn);
if (mqttClient && mqttClient->isConnected()) {
mqttClient->publish(topic, message);
statusLabel->setText(QString("已发送OTA降级命令到设备 %1 (版本 1.1.40)").arg(deviceSn));
// 显示发送的消息内容
QMessageBox::information(this, "OTA降级命令",
QString("已发送降级命令:\n设备SN: %1\n版本: 1.1.40\n主题: %2\n消息: %3")
.arg(deviceSn, topic, message));
} else {
statusLabel->setText("MQTT未连接无法发送OTA命令");
// 如果发送失败,重置状态
otaOperationPending = false;
}
}
void MainWindow::onSearchLightStripClicked() {
sendSearchLightStripCommand();
}
void MainWindow::sendSearchLightStripCommand() {
QString deviceSn = deviceSnEdit->text().trimmed();
if (deviceSn.isEmpty()) {
messageDisplay->append(QString("[%1] 错误: 设备SN不能为空")
.arg(QDateTime::currentDateTime().toString("hh:mm:ss")));
return;
}
QString topic = QString("iot/10045/%1/message/adviceDevice").arg(deviceSn);
// 构造消息
QJsonObject message;
message["deviceId"] = "";
message["header"] = QJsonValue::Null;
message["messageId"] = "1958474134031921152";
message["productId"] = "";
message["timestamp"] = QDateTime::currentMSecsSinceEpoch();
// 构造msg字段
QJsonObject msgData;
msgData["scene"] = "1";
msgData["timeout"] = "120";
QJsonObject msgObj;
msgObj["data"] = msgData;
msgObj["msgType"] = "5005";
QJsonDocument msgDoc(msgObj);
message["msg"] = QString::fromUtf8(msgDoc.toJson(QJsonDocument::Compact));
QJsonDocument doc(message);
QString jsonString = doc.toJson(QJsonDocument::Compact);
// 发送消息
mqttClient->publish(topic, jsonString);
messageDisplay->append(QString("[%1] 发送搜索灯条命令")
.arg(QDateTime::currentDateTime().toString("hh:mm:ss")));
messageDisplay->append(QString("Topic: %1").arg(topic));
messageDisplay->append(QString("Message: %1").arg(jsonString));
messageDisplay->append("---");
}
void MainWindow::sendOtaCommand(const QString& version, bool isUpgrade) {
QJsonObject msg;
msg["action"] = isUpgrade ? "upgrade" : "downgrade";
msg["version"] = version;
QJsonObject root;
root["type"] = "ota_command";
root["msg"] = QString::fromUtf8(QJsonDocument(msg).toJson(QJsonDocument::Compact));
QJsonDocument doc(root);
QString message = QString::fromUtf8(doc.toJson(QJsonDocument::Compact));
if (mqttClient && mqttClient->isConnected()) {
mqttClient->publish("device/ota/command", message);
statusLabel->setText(QString("已发送OTA%1命令 (版本 %2)").arg(isUpgrade ? "升级" : "降级", version));
} else {
statusLabel->setText("MQTT未连接无法发送OTA命令");
}
}
void MainWindow::onGetVersionClicked() {
// 获取设备SN
QString deviceSn = deviceSnEdit->text().trimmed();
if (deviceSn.isEmpty()) {
QMessageBox::warning(this, "警告", "请先输入设备SN");
return;
}
// 构造获取版本的消息
QJsonObject data;
data["msgType"] = "2335";
QJsonObject cmdData;
cmdData["cmd"] = "cat /userdata/tx_version";
data["data"] = cmdData;
QJsonObject root;
root["deviceId"] = deviceSn;
root["header"] = QJsonValue::Null;
root["messageId"] = QString::number(QDateTime::currentMSecsSinceEpoch());
root["msg"] = QString::fromUtf8(QJsonDocument(data).toJson(QJsonDocument::Compact));
root["productId"] = "";
root["timestamp"] = QDateTime::currentMSecsSinceEpoch();
QJsonDocument doc(root);
QString message = QString::fromUtf8(doc.toJson(QJsonDocument::Compact));
// 使用正确的发送主题
QString topic = QString("iot/10045/%1/message/adviceDevice").arg(deviceSn);
if (mqttClient && mqttClient->isConnected()) {
mqttClient->publish(topic, message);
statusLabel->setText(QString("已发送获取版本命令到设备 %1").arg(deviceSn));
} else {
statusLabel->setText("MQTT未连接无法发送命令");
}
}
void MainWindow::updateConnectionStatus(bool connected)
{
connectBtn->setEnabled(!connected);
disconnectBtn->setEnabled(connected);
sendLightAllBtn->setEnabled(connected);
// 新增:连接成功后禁用连接参数输入框,断开后重新启用
usernameEdit->setEnabled(!connected);
portEdit->setEnabled(!connected);
passwordEdit->setEnabled(!connected);
brokerEdit->setEnabled(!connected); // 同时也禁用服务器地址输入框
// 修改连接按钮样式:连接成功后设置灰色背景,断开后恢复绿色
if (connected) {
connectBtn->setStyleSheet("QPushButton { background-color: #cccccc; color: #666666; font-weight: bold; padding: 8px 16px; border-radius: 4px; }");
statusLabel->setText("已连接");
statusLabel->setStyleSheet("color: green;");
} else {
connectBtn->setStyleSheet("QPushButton { background-color: #4CAF50; color: white; font-weight: bold; padding: 8px 16px; border-radius: 4px; }");
statusLabel->setText("未连接");
statusLabel->setStyleSheet("color: red;");
}
}
void MainWindow::processLightReportMessage(const QString &message) {
messageDisplay->append(QString("[DEBUG] 开始处理light/report消息: %1").arg(message));
QJsonParseError error;
QJsonDocument doc = QJsonDocument::fromJson(message.toUtf8(), &error);
if (error.error != QJsonParseError::NoError) {
messageDisplay->append(QString("[ERROR] JSON解析失败: %1").arg(error.errorString()));
return;
}
QJsonObject rootObj = doc.object();
messageDisplay->append(QString("[DEBUG] 根对象键: %1").arg(QStringList(rootObj.keys()).join(", ")));
QJsonObject msgObj = rootObj["msg"].toObject();
messageDisplay->append(QString("[DEBUG] msg对象键: %1").arg(QStringList(msgObj.keys()).join(", ")));
QJsonObject dataObj = msgObj["data"].toObject();
messageDisplay->append(QString("[DEBUG] data对象键: %1").arg(QStringList(dataObj.keys()).join(", ")));
QJsonArray lightsArray = dataObj["lights"].toArray();
messageDisplay->append(QString("[DEBUG] lights数组长度: %1").arg(lightsArray.size()));
int newSnCount = 0;
for (const QJsonValue &lightValue : lightsArray) {
QJsonObject lightObj = lightValue.toObject();
QString sn = lightObj["sn"].toString();
messageDisplay->append(QString("[DEBUG] 处理灯条SN: %1").arg(sn));
if (!sn.isEmpty() && !uniqueSnSet.contains(sn)) {
messageDisplay->append(QString("[DEBUG] 添加新SN到列表: %1").arg(sn));
addSnToList(sn);
newSnCount++;
} else if (sn.isEmpty()) {
messageDisplay->append("[DEBUG] SN为空跳过");
} else if (uniqueSnSet.contains(sn)) {
messageDisplay->append(QString("[DEBUG] SN已存在跳过: %1").arg(sn));
}
}
if (newSnCount > 0) {
messageDisplay->append(QString("[INFO] 新发现 %1 个灯条SN").arg(newSnCount));
saveSnList();
} else {
messageDisplay->append("[DEBUG] 没有发现新的灯条SN");
}
}
void MainWindow::addSnToList(const QString &sn)
{
if (uniqueSnSet.contains(sn)) {
return;
}
uniqueSnSet.insert(sn);
// 更新主窗口的计数显示
snCountLabel->setText(QString("已发现灯条: %1 个").arg(uniqueSnSet.size()));
// 如果灯条管理器已打开同步添加但不重复更新MainWindow的计数
if (lightStripManager) {
// 临时断开信号连接,避免重复更新
disconnect(lightStripManager, &LightStripManager::snCountChanged, this, nullptr);
lightStripManager->addSnToList(sn);
// 重新连接信号
connect(lightStripManager, &LightStripManager::snCountChanged,
this, [this](int count) {
snCountLabel->setText(QString("已发现灯条: %1 个").arg(count));
});
}
// 移除这里的saveSnList()调用,让调用方决定何时保存
// saveSnList();
}
void MainWindow::saveSnList() {
QStringList snList = uniqueSnSet.values();
settings->setValue("lightStripSnList", snList);
settings->sync();
}
void MainWindow::loadSnList() {
QStringList snList = settings->value("lightStripSnList").toStringList();
for (const QString &sn : snList) {
addSnToList(sn);
}
}
void MainWindow::onClearSnListClicked()
{
// 清空所有灯条widget
QLayoutItem *item;
while ((item = snHorizontalLayout->takeAt(0)) != nullptr) {
delete item->widget();
delete item;
}
// 清空复选框列表和SN集合
lightStripCheckBoxes.clear();
uniqueSnSet.clear();
// 更新计数显示
snCountLabel->setText("已发现灯条: 0 个");
// 移除对lightStripManager的直接调用
// 让灯条管理器独立管理自己的数据
// 保存设置
saveSnList();
}
void MainWindow::openLightStripManager()
{
if (!lightStripManager) {
lightStripManager = new LightStripManager(this);
// 连接信号
connect(lightStripManager, &LightStripManager::lightControlRequested,
this, [this](const QStringList &sns, const QString &color, bool flash, int interval, int duration, bool sound) {
// 处理点亮控制请求
// 实现MQTT发送逻辑
// 检查MQTT连接状态
if (!mqttClient->isConnected()) {
qDebug() << "MQTT未连接无法发送灯条控制命令";
QMessageBox::warning(this, "警告", "MQTT未连接请先连接MQTT服务器");
return;
}
// 获取设备SN从界面获取
QString deviceSn = deviceSnEdit->text().trimmed();
if (deviceSn.isEmpty()) {
QMessageBox::warning(this, "警告", "请先输入需要测试的设备SN");
return;
}
// 构建内层msg数据
QJsonObject msgData;
QRegularExpression re("\\d+");
QRegularExpressionMatch match = re.match(color);
if (match.hasMatch()) {
msgData["color"] = match.captured(0); // 提取到的数字字符串
} else {
msgData["color"] = "0"; // 默认值
}
msgData["flash"] = flash ? "1" : "0";
msgData["flashInterval"] = QString::number(interval);
msgData["lightDuration"] = QString::number(duration);
msgData["scene"] = "3"; // 根据业务需求设置
msgData["sound"] = sound ? "1" : "0";
// 构建lights数组
QJsonArray lightsArray;
for (const QString &sn : sns) {
QJsonObject lightObj;
lightObj["sn"] = sn;
lightsArray.append(lightObj);
}
msgData["lights"] = lightsArray;
// 构建完整的msg对象
QJsonObject fullMsg;
fullMsg["data"] = msgData;
fullMsg["msgType"] = "3015"; // 根据业务协议设置
// 转换msg为JSON字符串
QJsonDocument msgDoc(fullMsg);
QString msgString = msgDoc.toJson(QJsonDocument::Compact);
// 构建最外层消息
QJsonObject outerMessage;
outerMessage["deviceId"] = ""; // 根据需要设置
outerMessage["messageId"] = QString::number(QDateTime::currentMSecsSinceEpoch()); // 生成唯一消息ID
outerMessage["msg"] = msgString;
outerMessage["timestamp"] = QDateTime::currentMSecsSinceEpoch();
// 转换为最终JSON字符串
QJsonDocument finalDoc(outerMessage);
QString finalMessage = finalDoc.toJson(QJsonDocument::Compact);
// 构建MQTT主题
QString topic = QString("iot/10045/%1/message/adviceDevice").arg(deviceSn);
// 发送MQTT消息
bool success = mqttClient->publish(topic, finalMessage);
if (success) {
qDebug() << "成功发送灯条控制命令";
qDebug() << "主题:" << topic;
qDebug() << "消息:" << finalMessage;
// 显示成功提示
QString resultMessage = QString("已向设备 %1 发送控制命令,控制 %2 个灯条")
.arg(deviceSn)
.arg(sns.size());
QMessageBox::information(this, "成功", resultMessage);
} else {
qDebug() << "发送灯条控制命令失败";
QMessageBox::warning(this, "错误", "发送MQTT消息失败请检查网络连接");
}
});
connect(lightStripManager, &LightStripManager::snSelectionChanged,
this, [this](const QStringList &selectedSns) {
// 处理选择变化
qDebug() << "Selected SNs changed:" << selectedSns;
});
// 新增:连接数量变化信号
connect(lightStripManager, &LightStripManager::snCountChanged,
this, [this](int count) {
// 同步更新MainWindow中的灯条数量显示
snCountLabel->setText(QString("已发现灯条: %1 个").arg(count));
});
// 新增:连接身份信息绑定信号
connect(lightStripManager, &LightStripManager::identityBindingRequested,
this, [this](const QString &sn, const QString &label1, const QString &label2, const QString &label3) {
// 处理身份信息绑定请求
// 检查MQTT连接状态
if (!mqttClient->isConnected()) {
qDebug() << "MQTT未连接无法发送身份信息绑定命令";
QMessageBox::warning(this, "警告", "MQTT未连接请先连接MQTT服务器");
return;
}
// 获取设备SN从界面获取
QString deviceSn = deviceSnEdit->text().trimmed();
if (deviceSn.isEmpty()) {
QMessageBox::warning(this, "警告", "请先输入需要测试的设备SN");
return;
}
// 构建身份信息绑定消息
QJsonObject msgData;
msgData["sn"] = sn;
msgData["label1"] = label1;
msgData["label2"] = label2;
msgData["label3"] = label3;
// 构建完整的msg对象
QJsonObject fullMsg;
fullMsg["data"] = msgData;
fullMsg["msgType"] = "3022"; // 身份信息绑定消息类型
// 转换msg为JSON字符串
QJsonDocument msgDoc(fullMsg);
QString msgString = msgDoc.toJson(QJsonDocument::Compact);
// 构建最外层消息
QJsonObject outerMessage;
outerMessage["deviceId"] = "";
outerMessage["messageId"] = QString::number(QDateTime::currentMSecsSinceEpoch());
outerMessage["msg"] = msgString;
outerMessage["timestamp"] = QDateTime::currentMSecsSinceEpoch();
// 转换为最终JSON字符串
QJsonDocument finalDoc(outerMessage);
QString finalMessage = finalDoc.toJson(QJsonDocument::Compact);
// 构建MQTT主题
QString topic = QString("iot/10045/%1/message/adviceDevice").arg(deviceSn);
// 发送MQTT消息
bool success = mqttClient->publish(topic, finalMessage);
if (success) {
qDebug() << "成功发送身份信息绑定命令";
qDebug() << "主题:" << topic;
qDebug() << "消息:" << finalMessage;
// 在消息显示区域显示发送的消息
messageDisplay->append(QString("[%1] 发送身份信息绑定到设备 %2")
.arg(QDateTime::currentDateTime().toString("hh:mm:ss"))
.arg(deviceSn));
messageDisplay->append(QString("主题: %1").arg(topic));
messageDisplay->append(QString("灯条SN: %1, Label1: %2, Label2: %3, Label3: %4")
.arg(sn).arg(label1).arg(label2).arg(label3));
} else {
qDebug() << "发送身份信息绑定命令失败";
QMessageBox::warning(this, "错误", "发送MQTT消息失败请检查网络连接");
}
});
// 连接关闭信号
connect(lightStripManager, &QWidget::destroyed,
this, &MainWindow::onLightStripManagerClosed);
// 同步当前的SN列表到管理器
for (const QString &sn : uniqueSnSet) {
lightStripManager->addSnToList(sn);
}
}
lightStripManager->show();
lightStripManager->raise();
lightStripManager->activateWindow();
}
void MainWindow::onLightStripManagerClosed()
{
// 灯条管理器关闭时的处理
lightStripManager = nullptr;
qDebug() << "Light strip manager closed";
}
QString MainWindow::getDeviceSn() const
{
return deviceSnEdit->text().trimmed();
}
bool MainWindow::isMqttConnected() const
{
return mqttClient && mqttClient->isConnected();
}
void MainWindow::publishMqttMessage(const QString &topic, const QString &message)
{
if (mqttClient && mqttClient->isConnected()) {
mqttClient->publish(topic, message);
messageDisplay->append(QString("[%1] 发送消息到主题: %2")
.arg(QDateTime::currentDateTime().toString("hh:mm:ss"))
.arg(topic));
} else {
QMessageBox::warning(this, "错误", "MQTT未连接无法发送消息");
}
}