新增灯条电量管理和灯条列表备份恢复功能

This commit is contained in:
zzh 2025-09-23 09:44:34 +08:00
parent 3e90c5f980
commit 5bf62c410a
3 changed files with 504 additions and 9 deletions

View File

@ -1,6 +1,12 @@
#include "lightstripmanager.h"
#include <QThread>
#include <QStandardPaths>
#include <QDir>
#include <QFile>
#include <QJsonArray>
#include <QFileDialog>
#include <QJsonDocument>
#include <QJsonObject>
// 如果不再需要可以删除: #include <QRandomGenerator>
LightStripManager::LightStripManager(QWidget *parent)
@ -11,6 +17,7 @@ LightStripManager::LightStripManager(QWidget *parent)
{
setupUI();
loadSnList();
loadBatteryInfo(); // 加载电量信息
// 设置窗口属性
setWindowTitle("灯条SN管理器");
@ -205,11 +212,42 @@ void LightStripManager::setupControlPanel()
);
connect(refreshBtn, &QPushButton::clicked, this, &LightStripManager::onRefreshClicked);
// 新增:备份按钮
backupBtn = new QPushButton("备份列表");
backupBtn->setMinimumHeight(35);
backupBtn->setStyleSheet(
"QPushButton { "
" background-color: #673AB7; "
" color: white; "
" font-weight: bold; "
" border-radius: 6px; "
" padding: 8px 16px; "
"} "
"QPushButton:hover { background-color: #5E35B1; }"
);
connect(backupBtn, &QPushButton::clicked, this, &LightStripManager::onBackupClicked);
// 恢复备份按钮
restoreBtn = new QPushButton("恢复备份");
restoreBtn->setStyleSheet(
"QPushButton { "
" background-color: #FF9800; "
" color: white; "
" font-weight: bold; "
" border-radius: 6px; "
" padding: 8px 16px; "
"} "
"QPushButton:hover { background-color: #F57C00; }"
);
connect(restoreBtn, &QPushButton::clicked, this, &LightStripManager::onRestoreClicked);
row1Layout->addWidget(selectAllBtn);
row1Layout->addWidget(deselectAllBtn);
row1Layout->addWidget(deleteSelectedBtn);
row1Layout->addWidget(clearAllBtn);
row1Layout->addWidget(refreshBtn);
row1Layout->addWidget(backupBtn);
row1Layout->addWidget(restoreBtn);
row1Layout->addStretch();
// 统计标签
@ -543,20 +581,31 @@ QWidget* LightStripManager::createSnWidget(const QString &sn)
"QCheckBox { "
" font-weight: bold; "
" font-size: 12px; "
" spacing: 8px; "
"} "
"QCheckBox::indicator { "
" width: 20px; "
" height: 20px; "
" width: 18px; "
" height: 18px; "
" border-radius: 9px; "
" border: 2px solid #d0d0d0; "
" background-color: #ffffff; "
"} "
"QCheckBox::indicator:unchecked { "
" border: 2px solid #ccc; "
" border-radius: 4px; "
" background-color: white; "
"QCheckBox::indicator:unchecked:hover { "
" border: 2px solid #4CAF50; "
" background-color: #f0f8f0; "
"} "
"QCheckBox::indicator:checked { "
" border: 2px solid #4CAF50; "
" border-radius: 4px; "
" background-color: #4CAF50; "
"} "
"QCheckBox::indicator:checked:hover { "
" background-color: #66BB6A; "
"} "
"QCheckBox::indicator:checked::after { "
" content: '✓'; "
" color: white; "
" font-size: 12px; "
" font-weight: bold; "
"}"
);
connect(checkBox, &QCheckBox::stateChanged, this, &LightStripManager::onCheckBoxStateChanged);
@ -580,9 +629,33 @@ QWidget* LightStripManager::createSnWidget(const QString &sn)
);
snLabel->setWordWrap(true);
// 创建电量标签
QLabel *batteryLabel = new QLabel("电量: --");
batteryLabel->setAlignment(Qt::AlignCenter);
batteryLabel->setStyleSheet(
"QLabel { "
" color: #666; "
" font-size: 10px; "
" background-color: #f5f5f5; "
" border: 1px solid #ddd; "
" border-radius: 3px; "
" padding: 2px 4px; "
" min-height: 8px; "
"}"
);
// 存储电量标签的引用
batteryLabels[sn] = batteryLabel;
// 如果已有电量信息,则显示并设置正确颜色
if (batteryInfoMap.contains(sn)) {
updateBatteryInfo(sn, batteryInfoMap[sn]);
}
// 添加到布局
itemLayout->addWidget(checkBox, 0, Qt::AlignCenter);
itemLayout->addWidget(snLabel);
itemLayout->addWidget(batteryLabel);
itemLayout->addStretch();
return lightStripWidget;
@ -660,12 +733,17 @@ void LightStripManager::onClearSnListClicked()
lightStripCheckBoxes.clear();
uniqueSnSet.clear();
// 清空电量相关数据
batteryInfoMap.clear();
batteryLabels.clear();
// 更新显示
updateSnCount();
updateControlButtons();
// 保存设置
saveSnList();
saveBatteryInfo(); // 保存电量信息
emit clearAllRequested();
}
@ -751,7 +829,12 @@ void LightStripManager::onDeleteSelectedClicked()
// 从集合中移除SN
QLabel *snLabel = lightStripWidgets[i]->findChild<QLabel*>();
if (snLabel) {
uniqueSnSet.remove(snLabel->text());
QString sn = snLabel->text();
uniqueSnSet.remove(sn);
// 清理电量相关数据
batteryInfoMap.remove(sn);
batteryLabels.remove(sn);
}
// 删除widget
@ -861,6 +944,129 @@ void LightStripManager::onAddSnManuallyClicked()
msgBox.exec();
}
void LightStripManager::onRestoreClicked()
{
QString backupDir = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/backups";
// 打开文件选择对话框
QString filePath = QFileDialog::getOpenFileName(
this,
"选择要恢复的备份文件",
backupDir,
"JSON备份文件 (*.json);;所有文件 (*)"
);
if (filePath.isEmpty()) {
return; // 用户取消了选择
}
// 加载备份文件
if (loadBackupFile(filePath)) {
// 恢复成功,刷新显示
applyResponsiveLayout();
updateSnCount();
updateControlButtons();
QMessageBox msgBox;
msgBox.setWindowTitle("恢复完成");
msgBox.setText("备份文件已成功恢复!");
msgBox.setIcon(QMessageBox::Information);
msgBox.setStyleSheet(
"QMessageBox { "
" color: #000000; "
"} "
"QPushButton { "
" color: #000000; "
" background-color: #f0f0f0; "
" border: 1px solid #ccc; "
" border-radius: 4px; "
" padding: 6px 12px; "
" min-width: 60px; "
"} "
"QPushButton:hover { "
" background-color: #e0e0e0; "
"}"
);
msgBox.exec();
} else {
QMessageBox msgBox;
msgBox.setWindowTitle("恢复失败");
msgBox.setText("无法读取或解析备份文件,请检查文件格式是否正确。");
msgBox.setIcon(QMessageBox::Warning);
msgBox.setStyleSheet(
"QMessageBox { "
" color: #000000; "
"} "
"QPushButton { "
" color: #000000; "
" background-color: #f0f0f0; "
" border: 1px solid #ccc; "
" border-radius: 4px; "
" padding: 6px 12px; "
" min-width: 60px; "
"} "
"QPushButton:hover { "
" background-color: #e0e0e0; "
"}"
);
msgBox.exec();
}
}
void LightStripManager::onBackupClicked()
{
if (uniqueSnSet.isEmpty()) {
QMessageBox msgBox;
msgBox.setWindowTitle("提示");
msgBox.setText("当前没有灯条SN数据无需备份。");
msgBox.setIcon(QMessageBox::Information);
msgBox.setStyleSheet(
"QMessageBox { "
" color: #000000; "
"} "
"QPushButton { "
" color: #000000; "
" background-color: #f0f0f0; "
" border: 1px solid #ccc; "
" border-radius: 4px; "
" padding: 6px 12px; "
" min-width: 60px; "
"} "
"QPushButton:hover { "
" background-color: #e0e0e0; "
"}"
);
msgBox.exec();
return;
}
// 执行备份
backupSnList();
// 显示备份成功消息
QMessageBox msgBox;
msgBox.setWindowTitle("备份完成");
msgBox.setText(QString("已成功备份 %1 个灯条SN到新文件。\n备份文件保存在应用数据目录的backups文件夹中。").arg(uniqueSnSet.size()));
msgBox.setIcon(QMessageBox::Information);
msgBox.setStyleSheet(
"QMessageBox { "
" color: #000000; "
"} "
"QPushButton { "
" color: #000000; "
" background-color: #f0f0f0; "
" border: 1px solid #ccc; "
" border-radius: 4px; "
" padding: 6px 12px; "
" min-width: 60px; "
"} "
"QPushButton:hover { "
" background-color: #e0e0e0; "
"}"
);
msgBox.exec();
}
void LightStripManager::onSearchTextChanged(const QString &text)
{
filterSnDisplay(text);
@ -1140,6 +1346,9 @@ void LightStripManager::saveSnList()
QStringList snList = uniqueSnSet.values();
settings->setValue("lightStripSnList", snList);
qDebug() << "已保存" << snList.size() << "个SN到lightStripSnList";
// 注释掉自动备份功能
// backupSnList();
}
// 强制同步到磁盘
@ -1479,6 +1688,9 @@ void LightStripManager::syncSnListFromMainWindow(const QStringList &snList)
lightStripCheckBoxes.clear();
uniqueSnSet.clear();
// 清理电量标签映射(但保留电量信息数据)
batteryLabels.clear();
// 批量添加但不保存
for (const QString &sn : snList) {
if (!uniqueSnSet.contains(sn)) {
@ -1734,4 +1946,258 @@ void LightStripManager::setupIdentityBindingArea()
// 加载保存的身份信息设置
loadIdentitySettings();
}
// 电量信息管理方法实现
void LightStripManager::updateBatteryInfo(const QString &sn, const QString &batteryLevel)
{
batteryInfoMap[sn] = batteryLevel;
// 添加调试信息
qDebug() << "[DEBUG] 更新电量信息 - SN:" << sn << "电量:" << batteryLevel;
// 更新对应的电量标签
if (batteryLabels.contains(sn)) {
batteryLabels[sn]->setText(QString("电量: %1").arg(batteryLevel));
// 根据电量设置不同颜色
QString color = "#666";
QString bgColor = "#fff3cd";
QString borderColor = "#ffeaa7";
bool ok;
int level = batteryLevel.toInt(&ok);
qDebug() << "[DEBUG] 电量转换 - 原值:" << batteryLevel << "转换后:" << level << "转换成功:" << ok;
if (ok) {
// 判断电量值是毫伏值还是百分比
if (level > 100) {
qDebug() << "[DEBUG] 检测为毫伏值:" << level;
// 毫伏值,一般锂电池范围 2500-4200mV
if (level < 2600) { // 低电量 (<2.8V)
color = "#d32f2f";
bgColor = "#ffebee";
borderColor = "#f8bbd9";
qDebug() << "[DEBUG] 设置为红色 - 低电量";
} else if (level < 2900) { // 中等电量 (2.8V-2.9V)
color = "#f57c00";
bgColor = "#fff3e0";
borderColor = "#ffcc02";
qDebug() << "[DEBUG] 设置为黄色 - 中等电量";
} else { // 健康电量 (≥2.9V)
color = "#388e3c";
bgColor = "#e8f5e8";
borderColor = "#4caf50";
qDebug() << "[DEBUG] 设置为绿色 - 健康电量";
}
} else {
// 百分比值 (0-100)
if (level <= 20) {
color = "#d32f2f";
bgColor = "#ffebee";
borderColor = "#f8bbd9";
} else if (level <= 50) {
color = "#f57c00";
bgColor = "#fff3e0";
borderColor = "#ffcc02";
} else {
color = "#388e3c";
bgColor = "#e8f5e8";
borderColor = "#4caf50";
}
}
}
batteryLabels[sn]->setStyleSheet(
QString("QLabel { "
" color: %1; "
" font-size: 10px; "
" background-color: %2; "
" border: 1px solid %3; "
" border-radius: 3px; "
" padding: 2px 4px; "
" min-height: 8px; "
"}").arg(color, bgColor, borderColor)
);
}
// 保存电量信息
saveBatteryInfo();
}
void LightStripManager::saveBatteryInfo()
{
settings->beginGroup("BatteryInfo");
settings->remove(""); // 清空组内所有键
for (auto it = batteryInfoMap.begin(); it != batteryInfoMap.end(); ++it) {
settings->setValue(it.key(), it.value());
}
settings->endGroup();
settings->sync();
qDebug() << "保存电量信息,共" << batteryInfoMap.size() << "条记录";
}
void LightStripManager::loadBatteryInfo()
{
batteryInfoMap.clear();
settings->beginGroup("BatteryInfo");
QStringList keys = settings->allKeys();
for (const QString &key : keys) {
QString value = settings->value(key).toString();
batteryInfoMap[key] = value;
// 如果对应的电量标签存在,更新显示和颜色
if (batteryLabels.contains(key)) {
updateBatteryInfo(key, value);
}
}
settings->endGroup();
qDebug() << "加载电量信息,共" << batteryInfoMap.size() << "条记录";
}
// 新增SN列表备份功能实现
void LightStripManager::backupSnList()
{
if (uniqueSnSet.isEmpty()) {
qDebug() << "SN列表为空无需备份";
return;
}
createBackupFile();
}
void LightStripManager::createBackupFile()
{
QString backupFileName = generateBackupFileName();
QString backupDir = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/backups";
// 创建备份目录
QDir dir;
if (!dir.exists(backupDir)) {
if (!dir.mkpath(backupDir)) {
qDebug() << "创建备份目录失败:" << backupDir;
return;
}
}
QString backupFilePath = backupDir + "/" + backupFileName;
// 创建备份文件内容
QJsonObject backupData;
backupData["timestamp"] = QDateTime::currentDateTime().toString(Qt::ISODate);
backupData["version"] = "1.0";
backupData["count"] = uniqueSnSet.size();
QJsonArray snArray;
QStringList snList = uniqueSnSet.values();
for (const QString &sn : snList) {
QJsonObject snObject;
snObject["sn"] = sn;
// 如果有电量信息,也一并备份
if (batteryInfoMap.contains(sn)) {
snObject["battery"] = batteryInfoMap[sn];
}
snArray.append(snObject);
}
backupData["lightstrips"] = snArray;
// 写入文件
QJsonDocument doc(backupData);
QFile file(backupFilePath);
if (file.open(QIODevice::WriteOnly)) {
file.write(doc.toJson());
file.close();
qDebug() << "SN列表备份成功:" << backupFilePath;
qDebug() << "备份了" << uniqueSnSet.size() << "个灯条SN";
} else {
qDebug() << "创建备份文件失败:" << backupFilePath;
}
}
QString LightStripManager::generateBackupFileName() const
{
QDateTime now = QDateTime::currentDateTime();
QString timestamp = now.toString("yyyyMMdd_hhmmss");
return QString("lightstrip_sn_backup_%1.json").arg(timestamp);
}
// 新增SN列表恢复功能实现
void LightStripManager::restoreSnList()
{
QString backupDir = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/backups";
// 打开文件选择对话框
QString filePath = QFileDialog::getOpenFileName(
this,
"选择要恢复的备份文件",
backupDir,
"JSON备份文件 (*.json);;所有文件 (*)"
);
if (!filePath.isEmpty()) {
loadBackupFile(filePath);
}
}
bool LightStripManager::loadBackupFile(const QString &filePath)
{
QFile file(filePath);
if (!file.open(QIODevice::ReadOnly)) {
qDebug() << "无法打开备份文件:" << filePath;
return false;
}
QByteArray data = file.readAll();
file.close();
QJsonParseError error;
QJsonDocument doc = QJsonDocument::fromJson(data, &error);
if (error.error != QJsonParseError::NoError) {
qDebug() << "解析JSON文件失败:" << error.errorString();
return false;
}
QJsonObject backupData = doc.object();
// 验证文件格式
if (!backupData.contains("lightstrips") || !backupData["lightstrips"].isArray()) {
qDebug() << "备份文件格式不正确缺少lightstrips数组";
return false;
}
// 清空当前数据
uniqueSnSet.clear();
batteryInfoMap.clear();
// 恢复SN列表和电量信息
QJsonArray snArray = backupData["lightstrips"].toArray();
for (const QJsonValue &value : snArray) {
QJsonObject snObject = value.toObject();
if (snObject.contains("sn")) {
QString sn = snObject["sn"].toString();
uniqueSnSet.insert(sn);
// 如果有电量信息,也一并恢复
if (snObject.contains("battery")) {
batteryInfoMap[sn] = snObject["battery"].toString();
}
}
}
// 保存恢复的数据
saveSnList();
saveBatteryInfo();
qDebug() << "从备份文件恢复了" << uniqueSnSet.size() << "个灯条SN";
qDebug() << "恢复了" << batteryInfoMap.size() << "条电量信息";
return true;
}

View File

@ -47,6 +47,11 @@ public:
// 新增:设置主窗口引用
void setMainWindow(MainWindow *mainWindow);
// 新增:电量信息管理方法
void updateBatteryInfo(const QString &sn, const QString &batteryLevel);
void saveBatteryInfo();
void loadBatteryInfo();
signals:
void snSelectionChanged(const QStringList &selectedSns);
@ -71,6 +76,8 @@ private slots:
void onSendLightAllClicked();
void onCheckBoxStateChanged();
void onRefreshClicked();
void onBackupClicked(); // 新增:备份按钮槽函数
void onRestoreClicked(); // 新增:恢复备份按钮槽函数
// 新增:身份信息绑定相关槽函数
void onBindIdentitySelectedClicked();
@ -97,6 +104,15 @@ private:
QWidget* createSnWidget(const QString &sn);
void applyResponsiveLayout();
// 新增SN列表备份功能
void backupSnList();
void createBackupFile();
QString generateBackupFileName() const;
// 新增SN列表恢复功能
void restoreSnList();
bool loadBackupFile(const QString &filePath);
// 新增MQTT发送功能
void sendLightControlMessage(const QStringList &sns);
@ -122,6 +138,8 @@ private:
QPushButton *deleteSelectedBtn;
QPushButton *clearAllBtn;
QPushButton *refreshBtn;
QPushButton *backupBtn; // 新增:备份按钮
QPushButton *restoreBtn; // 新增:恢复备份按钮
QLineEdit *searchEdit;
QLineEdit *manualSnEdit;
QPushButton *addSnBtn;
@ -175,6 +193,10 @@ private:
QString currentLabel1;
QString currentLabel2;
QString currentLabel3;
// 新增:电量信息相关数据
QMap<QString, QString> batteryInfoMap; // SN -> 电量信息的映射
QMap<QString, QLabel*> batteryLabels; // SN -> 电量标签的映射
public:
enum MatchRule {
EQUAL = 0, // = (00)

View File

@ -1012,7 +1012,8 @@ void MainWindow::processLightReportMessage(const QString &message) {
for (const QJsonValue &lightValue : lightsArray) {
QJsonObject lightObj = lightValue.toObject();
QString sn = lightObj["sn"].toString();
messageDisplay->append(QString("[DEBUG] 处理灯条SN: %1").arg(sn));
QString battery = lightObj["battery"].toString();
messageDisplay->append(QString("[DEBUG] 处理灯条SN: %1, 电量: %2").arg(sn, battery));
if (!sn.isEmpty() && !uniqueSnSet.contains(sn)) {
messageDisplay->append(QString("[DEBUG] 添加新SN到列表: %1").arg(sn));
@ -1023,6 +1024,12 @@ void MainWindow::processLightReportMessage(const QString &message) {
} else if (uniqueSnSet.contains(sn)) {
messageDisplay->append(QString("[DEBUG] SN已存在跳过: %1").arg(sn));
}
// 更新电量信息到LightStripManager
if (!sn.isEmpty() && !battery.isEmpty() && lightStripManager) {
lightStripManager->updateBatteryInfo(sn, battery);
messageDisplay->append(QString("[DEBUG] 更新电量信息: SN=%1, 电量=%2").arg(sn, battery));
}
}
if (newSnCount > 0) {