From b0ee251a9e09b63f67381f14661599bdf0c7eb26 Mon Sep 17 00:00:00 2001 From: 123 <123@example.com> Date: Mon, 30 Jun 2025 21:13:31 +0800 Subject: [PATCH] =?UTF-8?q?=E5=88=A0=E9=99=A4=E5=A4=9A=E4=BD=99=E7=9A=84?= =?UTF-8?q?=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Client/doc/qt-cpp-coding-standard.md | 621 ++++++++++++++++++ .../doc/reports/code_refactor_summary.md | 197 ------ .../doc/reports/phase1_completion_report.md | 170 ----- .../doc/reports/phase3_completion_report.md | 262 -------- .../include/ui/UIInitializationManager.h | 353 ++++++++++ src/Client/include/utils/ConfigManager.h | 239 +++++++ src/Client/run_app.sh | 12 - src/Client/setup_database.sql | 55 -- src/Client/src/ui/UIInitializationManager.cpp | 530 +++++++++++++++ src/Client/src/utils/ConfigManager.cpp | 267 ++++++++ 10 files changed, 2010 insertions(+), 696 deletions(-) create mode 100644 src/Client/doc/qt-cpp-coding-standard.md delete mode 100644 src/Client/doc/reports/code_refactor_summary.md delete mode 100644 src/Client/doc/reports/phase1_completion_report.md delete mode 100644 src/Client/doc/reports/phase3_completion_report.md create mode 100644 src/Client/include/ui/UIInitializationManager.h create mode 100644 src/Client/include/utils/ConfigManager.h delete mode 100644 src/Client/run_app.sh delete mode 100644 src/Client/setup_database.sql create mode 100644 src/Client/src/ui/UIInitializationManager.cpp create mode 100644 src/Client/src/utils/ConfigManager.cpp diff --git a/src/Client/doc/qt-cpp-coding-standard.md b/src/Client/doc/qt-cpp-coding-standard.md new file mode 100644 index 00000000..0f80a639 --- /dev/null +++ b/src/Client/doc/qt-cpp-coding-standard.md @@ -0,0 +1,621 @@ +# Qt C++ 代码规范标准 +## BattlefieldExplorationSystem 项目专用 + +**版本**: 1.0 +**适用范围**: Qt 5.15 + C++17 +**最后更新**: 2024-12-30 + +--- + +## 📋 目录 + +1. [概述](#概述) +2. [命名约定](#命名约定) +3. [代码格式](#代码格式) +4. [文件组织](#文件组织) +5. [Qt特性使用规范](#qt特性使用规范) +6. [注释与文档](#注释与文档) +7. [内存管理与资源](#内存管理与资源) +8. [错误处理](#错误处理) +9. [性能优化指南](#性能优化指南) +10. [代码审查检查清单](#代码审查检查清单) + +--- + +## 概述 + +本规范基于Qt官方编码风格、Google C++ Style Guide和现代C++最佳实践,专门为BattlefieldExplorationSystem项目制定。旨在提高代码质量、可读性和可维护性,确保项目达到工业级标准。 + +### 核心原则 +- **一致性**: 整个项目保持统一的编码风格 +- **可读性**: 代码应该自解释,易于理解 +- **安全性**: 优先考虑类型安全和内存安全 +- **性能**: 在不影响可读性的前提下优化性能 +- **Qt最佳实践**: 充分利用Qt框架特性 + +--- + +## 命名约定 + +### 类名 (PascalCase) +```cpp +// ✅ 正确 +class MainWindow; +class DeviceManager; +class UAVDatabase; + +// ❌ 错误 +class mainWindow; +class device_manager; +class uavDatabase; +``` + +### 函数名 (camelCase) +```cpp +// ✅ 正确 +void setupUI(); +void connectSignals(); +void onButtonClicked(); + +// ❌ 错误 +void SetupUI(); +void connect_signals(); +void on_button_clicked(); +``` + +### 变量名 + +#### 成员变量 (m_前缀 + camelCase) +```cpp +class MainWindow { +private: + // ✅ 正确 + Ui::MainWindow *m_ui; + DeviceListPanel *m_deviceListPanel; + QTimer *m_heartbeatTimer; + + // ❌ 错误 - 项目中发现的问题 + Ui::MainWindow *ui; // 缺少m_前缀 + DeviceListPanel *deviceListPanel; // 不一致的命名 +}; +``` + +#### 局部变量 (camelCase) +```cpp +// ✅ 正确 +QString deviceName = "UAV-001"; +int signalStrength = 85; +QList deviceList; + +// ❌ 错误 +QString device_name = "UAV-001"; +int SignalStrength = 85; +``` + +#### 常量 (UPPER_CASE) +```cpp +// ✅ 正确 +const int MAX_DEVICE_COUNT = 100; +const QString DEFAULT_IP_ADDRESS = "192.168.1.1"; +const double GPS_PRECISION = 0.000001; + +// ❌ 错误 - 项目中发现的硬编码问题 +this->setMinimumSize(1400, 1000); // 应该使用命名常量 +``` + +### 文件名 +```cpp +// ✅ 正确 +MainWindow.h / MainWindow.cpp +DeviceCard.h / DeviceCard.cpp +UAVDatabase.h / UAVDatabase.cpp + +// ❌ 错误 +mainwindow.h +device_card.cpp +``` + +--- + +## 代码格式 + +### 缩进和空格 +- **缩进**: 使用4个空格,不使用Tab +- **行长度**: 最大100字符 +- **空格**: 操作符前后加空格 + +```cpp +// ✅ 正确 +if (deviceStatus == DeviceStatus::Online) { + m_statusLabel->setText("设备在线"); + m_statusIndicator->setStyleSheet("color: green;"); +} + +// ❌ 错误 +if(deviceStatus==DeviceStatus::Online){ +m_statusLabel->setText("设备在线"); +m_statusIndicator->setStyleSheet("color: green;"); +} +``` + +### 大括号位置 +```cpp +// ✅ 正确 - K&R风格 +class DeviceCard : public QWidget +{ + Q_OBJECT + +public: + explicit DeviceCard(const DeviceInfo &info, QWidget *parent = nullptr); + +private slots: + void onUpdateRequested(); +}; + +void DeviceCard::setupUI() +{ + if (m_deviceInfo.type == "uav") { + // UAV特定设置 + } else { + // 其他设备设置 + } +} +``` + +### 指针和引用 +```cpp +// ✅ 正确 +QWidget *parent = nullptr; +const DeviceInfo &info = getDeviceInfo(); +std::unique_ptr timer; + +// ❌ 错误 +QWidget* parent = nullptr; +QWidget * parent = nullptr; +const DeviceInfo& info = getDeviceInfo(); +``` + +--- + +## 文件组织 + +### 头文件包含顺序 +```cpp +// ✅ 正确顺序 +// 1. 对应的头文件 +#include "MainWindow.h" + +// 2. Qt系统头文件 +#include +#include +#include + +// 3. 第三方库头文件 +#include +#include + +// 4. 项目内部头文件 +#include "ui/components/DeviceListPanel.h" +#include "core/database/UAVDatabase.h" +#include "utils/SystemLogger.h" +``` + +### Include Guard +```cpp +// ✅ 正确 - 使用#pragma once +#pragma once + +#include + +class DeviceCard : public QWidget +{ + // 类定义 +}; + +// ❌ 错误 - 传统的include guard(虽然可用但不推荐) +#ifndef DEVICECARD_H +#define DEVICECARD_H +// ... +#endif +``` + +### 前向声明 +```cpp +// ✅ 正确 - 在头文件中使用前向声明 +// DeviceCard.h +#pragma once + +class QLabel; +class QPushButton; +class DeviceInfo; + +class DeviceCard : public QWidget +{ +private: + QLabel *m_nameLabel; + QPushButton *m_controlButton; + std::unique_ptr m_deviceInfo; +}; +``` + +--- + +## Qt特性使用规范 + +### 信号槽连接 +```cpp +// ✅ 正确 - 使用新式语法 +connect(m_deviceListPanel, &DeviceListPanel::deviceSelected, + this, &MainWindow::onDeviceSelected); + +connect(m_timer, &QTimer::timeout, + this, [this]() { + updateDeviceStatus(); + }); + +// ❌ 错误 - 旧式语法(项目中发现的问题) +connect(button, SIGNAL(clicked()), this, SLOT(onButtonClicked())); +``` + +### Q_OBJECT宏位置 +```cpp +// ✅ 正确 +class MainWindow : public QMainWindow +{ + Q_OBJECT // 紧跟在类声明后 + +public: + explicit MainWindow(QWidget *parent = nullptr); + +signals: + void deviceAdded(const DeviceInfo &info); + +private slots: + void onDeviceSelected(const DeviceInfo &info); +}; +``` + +### 属性系统 +```cpp +// ✅ 正确 +class DeviceCard : public QWidget +{ + Q_OBJECT + Q_PROPERTY(DeviceStatus status READ status WRITE setStatus NOTIFY statusChanged) + +public: + DeviceStatus status() const { return m_status; } + void setStatus(DeviceStatus status); + +signals: + void statusChanged(DeviceStatus status); + +private: + DeviceStatus m_status = DeviceStatus::Unknown; +}; +``` + +--- + +## 注释与文档 + +### Doxygen文档注释 +```cpp +/** + * @file DeviceCard.h + * @brief 设备卡片组件定义 + * @author BattlefieldExplorationSystem Team + * @date 2024-12-30 + * @version 1.0 + * + * 设备卡片组件,用于显示单个设备的详细信息和状态。 + * 支持UAV和地面机器人两种设备类型的统一显示。 + * + * @note 依赖Qt GUI模块 + * @since 1.0 + */ + +/** + * @class DeviceCard + * @brief 设备信息显示卡片 + * + * 提供设备信息的可视化显示,包括: + * - 设备基本信息(名称、类型、IP地址) + * - 实时状态显示(在线状态、信号强度、电量) + * - 操作控制按钮(详情、控制、定位) + * + * @warning 需要定期更新设备状态以保持界面同步 + * @see DeviceInfo, DeviceStatus + */ +class DeviceCard : public QWidget +{ + Q_OBJECT + +public: + /** + * @brief 构造函数 + * @param info 设备信息结构体 + * @param parent 父窗口指针,默认为nullptr + * + * 创建设备卡片并初始化UI组件。 + * 根据设备类型自动设置相应的图标和样式。 + * + * @note 构造后需要调用updateStatus()更新状态 + */ + explicit DeviceCard(const DeviceInfo &info, QWidget *parent = nullptr); + + /** + * @brief 更新设备状态 + * @param status 新的设备状态 + * + * 更新设备的在线状态、信号强度和电量信息。 + * 同时更新UI显示的颜色和图标。 + * + * @warning 此函数应该在主线程中调用 + */ + void updateStatus(const DeviceStatus &status); +}; +``` + +### 行内注释 +```cpp +void MainWindow::setupUI() +{ + // 设置窗口最小尺寸 - 确保所有组件正常显示 + this->setMinimumSize(MIN_WINDOW_WIDTH, MIN_WINDOW_HEIGHT); + + // 计算窗口居中位置 + QRect screenGeometry = QApplication::desktop()->screenGeometry(); + int centerX = (screenGeometry.width() - this->width()) / 2; + int centerY = (screenGeometry.height() - this->height()) / 2; + this->move(centerX, centerY); + + // TODO: 添加窗口状态保存功能 + // FIXME: 在高DPI显示器上可能需要调整尺寸计算 +} +``` + +--- + +## 内存管理与资源 + +### 智能指针使用 +```cpp +// ✅ 正确 - 使用智能指针 +class MainWindow : public QMainWindow +{ +private: + std::unique_ptr m_ui; + std::shared_ptr m_dbManager; + std::unique_ptr m_heartbeatTimer; +}; + +// 构造函数中 +MainWindow::MainWindow(QWidget *parent) + : QMainWindow(parent) + , m_ui(std::make_unique()) + , m_dbManager(std::make_shared()) + , m_heartbeatTimer(std::make_unique(this)) +{ + m_ui->setupUi(this); +} + +// ❌ 错误 - 原始指针(项目中发现的问题) +Ui::MainWindow *ui; // 容易造成内存泄漏 +QTimer *timer; // 生命周期管理不明确 +``` + +### Qt对象树管理 +```cpp +// ✅ 正确 - 利用Qt父子关系 +void MainWindow::setupDevicePanel() +{ + // Qt对象会自动管理子对象的生命周期 + m_deviceListPanel = new DeviceListPanel(this); // this作为parent + m_systemLogPanel = new SystemLogPanel(this); + + // 或者使用智能指针 + 父子关系 + auto deviceCard = std::make_unique(deviceInfo, this); + m_deviceCards.push_back(std::move(deviceCard)); +} +``` + +### 资源文件管理 +```cpp +// ✅ 正确 - 使用资源系统 +const QString ICON_UAV = ":/images/uav.png"; +const QString ICON_ROBOT = ":/images/robot.png"; +const QString STYLE_SHEET = ":/styles/military_theme.qss"; + +// ❌ 错误 - 硬编码路径 +QString iconPath = "/home/user/project/images/uav.png"; // 不可移植 +``` + +--- + +## 错误处理 + +### 异常安全 +```cpp +// ✅ 正确 - RAII和异常安全 +class DatabaseConnection +{ +public: + explicit DatabaseConnection(const QString &connectionString) + { + m_db = QSqlDatabase::addDatabase("QMYSQL"); + m_db.setDatabaseName(connectionString); + if (!m_db.open()) { + throw std::runtime_error("Failed to open database"); + } + } + + ~DatabaseConnection() + { + if (m_db.isOpen()) { + m_db.close(); + } + } + + // 禁止拷贝,允许移动 + DatabaseConnection(const DatabaseConnection&) = delete; + DatabaseConnection& operator=(const DatabaseConnection&) = delete; + DatabaseConnection(DatabaseConnection&&) = default; + DatabaseConnection& operator=(DatabaseConnection&&) = default; + +private: + QSqlDatabase m_db; +}; +``` + +### 错误码处理 +```cpp +// ✅ 正确 - 使用枚举类表示错误状态 +enum class DatabaseError +{ + Success, + ConnectionFailed, + QueryFailed, + InvalidData, + PermissionDenied +}; + +class DatabaseManager +{ +public: + DatabaseError connectToDatabase(const QString &host, int port, + const QString &username, const QString &password) + { + // ❌ 项目中发现的安全问题 - 硬编码密码 + // db.setPassword("hzk200407140238"); // 绝对不能这样做! + + // ✅ 正确 - 从配置文件读取 + auto config = ConfigManager::instance().getDatabaseConfig(); + m_db.setHostName(config.host); + m_db.setPort(config.port); + m_db.setUserName(config.username); + m_db.setPassword(config.password); // 从安全配置读取 + + if (!m_db.open()) { + return DatabaseError::ConnectionFailed; + } + + return DatabaseError::Success; + } +}; +``` + +--- + +## 性能优化指南 + +### Qt容器使用 +```cpp +// ✅ 正确 - 选择合适的容器 +QVector m_devices; // 随机访问,频繁插入删除 +QList m_logMessages; // 顺序访问,前后插入 +QHash m_deviceCards; // 快速查找 + +// 预分配容量 +m_devices.reserve(MAX_DEVICE_COUNT); + +// 使用const引用避免拷贝 +for (const auto &device : m_devices) { + updateDeviceDisplay(device); +} +``` + +### 信号槽优化 +```cpp +// ✅ 正确 - 使用Qt::QueuedConnection处理跨线程 +connect(m_worker, &WorkerThread::dataReady, + this, &MainWindow::onDataReceived, + Qt::QueuedConnection); + +// 使用lambda避免创建槽函数 +connect(m_timer, &QTimer::timeout, this, [this]() { + // 简单的更新操作 + m_statusLabel->setText(getCurrentTime()); +}); +``` + +### 内存优化 +```cpp +// ✅ 正确 - 延迟初始化 +class MainWindow : public QMainWindow +{ +private: + mutable std::unique_ptr m_expensiveWidget; + +public: + ExpensiveWidget* getExpensiveWidget() const + { + if (!m_expensiveWidget) { + m_expensiveWidget = std::make_unique(); + } + return m_expensiveWidget.get(); + } +}; +``` + +--- + +## 代码审查检查清单 + +### 基本检查 +- [ ] 所有类名使用PascalCase +- [ ] 所有函数名使用camelCase +- [ ] 私有成员变量使用m_前缀 +- [ ] 常量使用UPPER_CASE命名 +- [ ] 头文件包含顺序正确 +- [ ] 使用#pragma once而非传统include guard + +### Qt特定检查 +- [ ] Q_OBJECT宏位置正确 +- [ ] 信号槽连接使用新式语法 +- [ ] 正确设置Qt对象的父子关系 +- [ ] 资源文件使用qrc系统管理 + +### 安全检查 +- [ ] 无硬编码密码或敏感信息 +- [ ] 使用智能指针管理动态内存 +- [ ] 正确的异常安全保证 +- [ ] 输入参数验证充分 + +### 性能检查 +- [ ] 容器预分配容量(如适用) +- [ ] 使用const引用传递大对象 +- [ ] 避免不必要的对象拷贝 +- [ ] 合理使用延迟初始化 + +### 文档检查 +- [ ] 公共接口有完整Doxygen注释 +- [ ] 复杂逻辑有行内注释说明 +- [ ] 文件头信息完整 +- [ ] TODO/FIXME标记清晰 + +--- + +## 工具配置 + +### clang-format配置 +创建`.clang-format`文件: +```yaml +BasedOnStyle: Google +IndentWidth: 4 +ColumnLimit: 100 +PointerAlignment: Left +``` + +### 静态分析工具 +```bash +# cppcheck +cppcheck --enable=all --std=c++17 src/ + +# clang-static-analyzer +scan-build qmake && scan-build make +``` + +--- + +**注意**: 本规范应该逐步应用到现有代码中,避免一次性大规模修改。建议在每次代码修改时同步应用相关规范。 diff --git a/src/Client/doc/reports/code_refactor_summary.md b/src/Client/doc/reports/code_refactor_summary.md deleted file mode 100644 index cb469d31..00000000 --- a/src/Client/doc/reports/code_refactor_summary.md +++ /dev/null @@ -1,197 +0,0 @@ -# 代码重构和模块化改造总结报告 - -## 执行概述 - -本次重构成功解决了两个关键问题: -1. **业务领域不匹配** - 从"伤员搜救系统"转换为"战场探索系统" -2. **代码组织混乱** - 建立了清晰的模块化目录结构 - -## 重构成果 - -### 1. 目录结构重组 ✅ 完成 - -#### 新的组织架构: -``` -src/ -├── core/ # 核心业务逻辑层 -│ └── database/ # 数据库管理 -│ ├── UAVDatabase.cpp -│ └── DogDatabase.cpp -├── ui/ # 用户界面层 -│ ├── main/ # 主界面 -│ │ ├── MainWindow.cpp (原guidingui.cpp) -│ │ └── moc_MainWindow.cpp -│ └── dialogs/ # 对话框 -│ └── DeviceDialog.cpp (原robotlistdialog.cpp) -├── utils/ # 工具类 (准备扩展) -├── main.cpp # 程序入口 -└── qrc_res.cpp # 资源文件 - -forms/ # UI表单文件 -├── main/ -│ └── MainWindow.ui (原guidingui.ui) -└── dialogs/ - └── DeviceDialog.ui (原robotlistdialog.ui) - -include/ # 头文件 -├── core/ -│ └── database/ -│ ├── UAVDatabase.h -│ └── DogDatabase.h -└── ui/ - └── main/ - └── MainWindow.h (原guidingui.h) -``` - -### 2. 业务领域清理 ✅ 完成 - -#### 删除的injury相关文件: -**源代码** (5个文件): -- ❌ `src/InjuryAnalysisUI.cpp` - 伤员分析界面 -- ❌ `src/InjuryDatabase.cpp` - 伤员数据库 -- ❌ `src/injurydisiplayui.cpp` - 伤员显示界面 -- ❌ `src/moc_InjuryAnalysisUI.cpp` - 自动生成文件 -- ❌ `src/moc_injurydisiplayui.cpp` - 自动生成文件 - -**头文件** (3个文件): -- ❌ `include/InjuryAnalysisUI.h` -- ❌ `include/InjuryDatabase.h` -- ❌ `include/injurydisiplayui.h` - -**UI文件** (2个文件): -- ❌ `ui/InjuryAnalysisUI.ui` -- ❌ `ui/injurydisiplayui.ui` - -**总计清理**: 10个injury相关文件 🗑️ - -### 3. 文件重命名和更新 ✅ 完成 - -| 原文件 | 新文件 | 状态 | -|--------|--------|------| -| `src/guidingui.cpp` | `src/ui/main/MainWindow.cpp` | ✅ 已重命名并更新头文件引用 | -| `include/guidingui.h` | `include/ui/main/MainWindow.h` | ✅ 已重命名并更新类定义 | -| `ui/guidingui.ui` | `forms/main/MainWindow.ui` | ✅ 已移动到新位置 | -| `src/robotlistdialog.cpp` | `src/ui/dialogs/DeviceDialog.cpp` | ✅ 已重命名 | -| `ui/robotlistdialog.ui` | `forms/dialogs/DeviceDialog.ui` | ✅ 已移动 | - -### 4. 构建配置更新 ✅ 完成 - -创建了新的构建文件 `CasualtySightPlus_new.pro`: -- ✅ 更新了所有源文件路径 -- ✅ 更新了所有头文件路径 -- ✅ 更新了所有UI文件路径 -- ✅ 移除了injury相关文件引用 -- ✅ 优化了INCLUDEPATH配置 -- ✅ 设置了新的项目名称:BattlefieldExplorationSystem - -### 5. 安全备份 ✅ 完成 - -已创建完整备份: -- `src_backup/` - 原始源代码备份 -- `ui_backup/` - 原始UI文件备份 -- `include_backup/` - 原始头文件备份 - -## 重构统计 - -### 文件操作统计 -- **删除文件**: 10个 (所有injury相关) -- **移动文件**: 7个 (核心业务文件) -- **重命名文件**: 5个 (主要界面文件) -- **创建目录**: 12个 (新的模块化结构) -- **备份文件**: 所有原始文件已安全备份 - -### 代码清理统计 -- **清理injury业务代码**: 约2000行代码 -- **更新头文件引用**: 5处主要更新 -- **重构目录层次**: 3层模块化结构 -- **优化构建配置**: 1个新的.pro文件 - -## 架构改进 - -### 1. 模块化优势 -- ✅ **清晰的职责分离**: 核心逻辑、UI、工具类分离 -- ✅ **可扩展性**: 每个模块可以独立扩展 -- ✅ **可维护性**: 文件定位更加精确 - -### 2. 命名语义化 -- ✅ **MainWindow**: 明确的主窗口概念 -- ✅ **DeviceDialog**: 统一设备管理对话框 -- ✅ **核心模块**: database, services, models 清晰分层 - -### 3. 业务对齐 -- ✅ **战场探索**: 完全符合项目定位 -- ✅ **设备管理**: 突出无人机和机器人管理 -- ✅ **功能聚焦**: 移除不相关的伤员救助功能 - -## 下一步计划 - -### 短期任务 (1-2天) -1. **测试构建** - 验证新的.pro文件能否正常编译 -2. **代码更新** - 更新剩余的头文件引用和类名 -3. **功能验证** - 确保核心功能正常工作 - -### 中期任务 (1周) -1. **创建新组件** - 实现DeviceCard等UI组件 -2. **数据库统一** - 实现统一的设备管理接口 -3. **界面优化** - 更新界面文本和图标 - -### 长期任务 (2-3周) -1. **探索功能** - 实现探索任务管理 -2. **性能优化** - 优化模块间通信 -3. **测试完善** - 建立完整的测试体系 - -## 质量保证 - -### 1. 安全措施 -- ✅ 完整的代码备份确保可以回退 -- ✅ 渐进式重构降低风险 -- ✅ 保持数据库兼容性 - -### 2. 验证计划 -- 🔄 **构建测试**: 验证新配置能否编译成功 -- 🔄 **功能测试**: 验证设备管理功能正常 -- 🔄 **界面测试**: 验证UI正常显示和交互 - -### 3. 文档更新 -- ✅ 重构计划文档 -- ✅ 重构总结报告 -- 🔄 用户手册更新 (待完成) -- 🔄 API文档更新 (待完成) - -## 技术债务清理 - -### 已解决的技术债务 -- ✅ **业务不匹配**: 清理了所有injury相关代码 -- ✅ **目录混乱**: 建立了清晰的模块化结构 -- ✅ **命名不规范**: 统一了文件和类的命名规范 - -### 待解决的技术债务 -- ⏳ **头文件依赖**: 需要进一步优化include关系 -- ⏳ **代码重复**: 可以进一步提取公共组件 -- ⏳ **测试覆盖**: 需要添加单元测试 - -## 成功指标 - -### 定量指标 -- **代码减少**: 删除约2000行不相关代码 -- **文件组织**: 模块化程度提升100% -- **构建效率**: 预计提升20%(减少不必要的编译) - -### 定性指标 -- **代码可读性**: 显著提升 -- **维护难度**: 明显降低 -- **新人上手**: 更加容易 -- **项目专业度**: 大幅提升 - -## 结论 - -本次重构成功实现了预定目标: - -1. **✅ 业务对齐**: 完全转换为战场探索系统,清理了所有injury相关内容 -2. **✅ 架构优化**: 建立了清晰的三层模块化结构 -3. **✅ 风险控制**: 完整备份确保了安全性 -4. **✅ 可持续发展**: 为后续功能开发奠定了良好基础 - -这次重构不仅解决了业务不匹配问题,更重要的是建立了一个可扩展、可维护的代码架构,为项目的长期发展提供了坚实的技术基础。 - -**项目现已准备好进入下一阶段的功能开发!** 🚀 \ No newline at end of file diff --git a/src/Client/doc/reports/phase1_completion_report.md b/src/Client/doc/reports/phase1_completion_report.md deleted file mode 100644 index d2db860c..00000000 --- a/src/Client/doc/reports/phase1_completion_report.md +++ /dev/null @@ -1,170 +0,0 @@ -# Phase 1 完成报告 - -## 任务执行总结 - -### ✅ 已完成任务 - -#### 1.1 项目结构分析和规划 -- **状态**: ✅ 完成 -- **成果**: - - 深入分析了现有代码架构,包括`guidingui.cpp`、数据库类、UI文件等 - - 创建了详细的项目结构重构规划文档 (`project_structure_plan.md`) - - 设计了新的模块化目录结构 - - 规划了数据库层、UI层、服务层的重构方案 - -#### 1.2 现有数据库设计和接口分析 -- **状态**: ✅ 完成 -- **成果**: - - 分析了UAVDatabase、DogDatabase、InjuryDatabase三个核心数据库类 - - 发现现有代码已经在使用MySQL数据库 - - 识别了数据库连接管理的改进空间 - - 确认了数据模型结构和API接口 - -#### 1.3 重构范围和影响评估 -- **状态**: ✅ 完成 -- **成果**: - - 创建了详细的重构影响评估文档 (`refactor_impact_assessment.md`) - - 确定了高、中、低三个影响级别的改动范围 - - 分析了用户体验、性能、兼容性等方面的影响 - - 制定了风险缓解策略和成本效益分析 - -#### 1.4 MySQL数据库环境搭建 -- **状态**: ✅ 完成 -- **成果**: - - 验证了MySQL 8.4.5服务正常运行 - - 确认了Client数据库已创建 - - 成功创建了核心数据表: - - `uav_devices` - 无人机设备表 - - `dog_devices` - 地面机器人设备表 - - `injury_records` - 伤员记录表 - - 创建了兼容性视图以保持现有代码正常工作 - -#### 1.5 Qt MySQL驱动配置 -- **状态**: ✅ 完成 -- **成果**: - - 成功安装了`libqt5sql5-mysql`驱动 - - 验证了Qt SQL驱动可用性:`libqsqlmysql.so` - - 确认了Qt 5.15.15与MySQL 8.4.5的兼容性 - - 数据库连接测试通过 - -#### 1.6 开发工具和依赖包确认 -- **状态**: ✅ 完成 -- **成果**: - - **Qt环境**: Qt 5.15.15 正常可用 - - **编译器**: g++ 14.2.0 - - **构建工具**: GNU Make 4.4.1, qmake 3.1 - - **数据库**: MySQL 8.4.5, Qt MySQL驱动已安装 - - **项目构建**: qmake可以成功生成Makefile(有音频模块警告但不影响核心功能) - -#### 1.7 版本控制分支策略制定 -- **状态**: ✅ 完成 -- **成果**: - - 创建了详细的Git分支策略文档 (`git_branch_strategy.md`) - - 定义了主要分支:main、develop - - 规划了功能分支:feature/ui-redesign、feature/database-enhancement等 - - 制定了代码审查流程和提交信息规范 - - 设计了CI/CD集成方案 - -## 环境配置总结 - -### 数据库环境 -``` -- 数据库服务器: MySQL 8.4.5 -- 数据库名称: Client -- 连接参数: localhost:3306 -- 用户认证: root用户已配置 -- Qt驱动: libqt5sql5-mysql 已安装 -``` - -### 开发环境 -``` -- Qt版本: 5.15.15 -- 编译器: g++ 14.2.0 -- 构建系统: qmake + make -- 平台: Linux (Ubuntu) -- MySQL客户端: 8.4.5 -``` - -### 数据库表结构 -```sql --- 核心业务表 -uav_devices -- 无人机设备表(新设计,包含信号强度、心跳等字段) -dog_devices -- 地面机器人表(新设计,统一结构) -injury_records -- 伤员记录表(新设计,增强字段) - --- 兼容性视图(保持现有代码工作) -uavdatabase -- UAV设备视图 -dogdatabase -- Dog设备视图 -injurydatabase -- 伤员记录视图 -``` - -## 关键成果文档 - -1. **项目规划文档**: - - `task.md` - 总体任务规划和分解 - - `project_structure_plan.md` - 详细的项目结构重构规划 - - `refactor_impact_assessment.md` - 重构影响评估和风险分析 - -2. **技术文档**: - - `database_schema.sql` - 数据库表结构脚本 - - `git_branch_strategy.md` - Git分支管理策略 - - `phase1_completion_report.md` - 本报告 - -3. **配置文件**: - - 数据库连接已配置并测试通过 - - Qt MySQL驱动已安装并验证 - -## 遇到的问题和解决方案 - -### 问题1: 音频模块文件缺失 -- **现象**: qmake时出现AudioModule相关文件找不到的警告 -- **影响**: 不影响核心功能构建 -- **解决**: 识别为非核心模块,将在后续阶段处理 - -### 问题2: MySQL保留字冲突 -- **现象**: 创建injury_records表时rank字段报语法错误 -- **解决**: 将字段名改为injury_rank避免冲突 - -### 问题3: 数据库表创建脚本执行问题 -- **现象**: 批量SQL脚本执行部分失败 -- **解决**: 分步执行SQL语句,确保每个表成功创建 - -## 下一阶段准备 - -### Phase 2 准备工作 -- ✅ 数据库环境已就绪 -- ✅ 表结构已创建 -- ✅ 兼容性视图已建立 -- ✅ 开发环境已验证 - -### 即将开始的任务 -1. **数据库架构设计** - 基础已完成,需要添加系统配置表等 -2. **数据库连接层重构** - 实现连接管理器和基础DAO -3. **数据迁移和兼容性** - 确保平滑过渡 - -## 时间跟踪 - -- **计划时间**: 1周 -- **实际时间**: 1天(高效完成) -- **主要耗时**: 数据库环境配置和表结构设计 -- **节约时间**: 现有代码已使用MySQL,减少了迁移工作量 - -## 质量指标 - -- **文档覆盖率**: 100% - 所有关键决策都有文档记录 -- **环境验证**: 100% - 所有必要工具和依赖都已验证 -- **风险识别**: 完整 - 识别并制定了缓解策略 -- **可执行性**: 高 - 规划具体可操作,有明确的实施步骤 - -## 总结 - -Phase 1成功完成了所有预定目标,为后续的数据库重构和界面改进奠定了坚实的基础。项目准备工作充分,技术栈验证完毕,可以信心满满地进入Phase 2的实施阶段。 - -**关键成功因素**: -1. 充分的现状分析为重构决策提供了依据 -2. 详细的规划文档确保了实施方向的正确性 -3. 环境配置的完整验证避免了后续的阻塞问题 -4. 兼容性设计保证了渐进式重构的可行性 - -**下一步行动**: -进入Phase 2 - 数据库设计和集成阶段,开始实施具体的重构工作。 \ No newline at end of file diff --git a/src/Client/doc/reports/phase3_completion_report.md b/src/Client/doc/reports/phase3_completion_report.md deleted file mode 100644 index ad05295e..00000000 --- a/src/Client/doc/reports/phase3_completion_report.md +++ /dev/null @@ -1,262 +0,0 @@ -# Phase 3 界面架构重设计完成报告 - -## 报告信息 -- **阶段**: Phase 3 - 界面架构重设计 -- **完成日期**: 2024年12月19日 -- **报告版本**: 1.0 -- **状态**: ✅ **已完成** - -## 阶段目标回顾 - -Phase 3 的主要目标是对战场探索系统的界面进行现代化重设计,实现: -1. 设备卡片化管理界面 -2. 现代化的用户交互体验 -3. 实时状态显示和监控 -4. 响应式布局设计 - -## 完成任务清单 - -### ✅ 已完成任务 - -#### 1. 界面架构分析和重设计范围确定 -- [x] 分析现有MainWindow结构和布局 -- [x] 确定三栏式布局保留策略 -- [x] 识别重设计重点:左侧设备管理面板 -- [x] 制定渐进式改进方案 - -#### 2. 设备卡片组件设计与实现 -- [x] **DeviceCard组件**:完整的设备卡片UI组件 - - 设备基本信息显示(名称、类型、IP地址) - - 实时状态指示(在线/离线、信号强度、电量) - - 位置信息显示(经纬度坐标) - - 操作按钮(详情、控制、定位) - - 交互效果(悬停、选中、点击反馈) - -- [x] **DeviceListPanel组件**:设备列表管理面板 - - 设备卡片容器和布局管理 - - 搜索和过滤功能 - - 设备添加和删除操作 - - 状态监控和实时更新 - - 统计信息显示 - -#### 3. 数据模型和架构设计 -- [x] **DeviceInfo结构体**:统一的设备信息模型 -- [x] **DeviceStatus枚举**:设备状态类型定义 -- [x] **事件驱动架构**:信号槽机制实现组件间通信 - -#### 4. 技术文档编写 -- [x] **Phase3技术设计文档**:完整的重构计划和实现方案 -- [x] **代码注释**:遵循Doxygen规范的完整API文档 -- [x] **文档索引更新**:更新doc/README.md - -#### 5. 构建系统集成 -- [x] **项目文件更新**:添加新组件到CasualtySightPlus_new.pro -- [x] **编译验证**:确保所有新组件正确编译 -- [x] **依赖关系**:处理Qt 5兼容性问题 - -## 技术成果 - -### 新增组件架构 - -``` -src/ui/components/ -├── DeviceCard.h/cpp # 设备卡片组件 (2个文件, ~800行代码) -└── DeviceListPanel.h/cpp # 设备列表面板 (2个文件, ~650行代码) - -include/ui/components/ # 对应头文件 -doc/technical/ -└── phase3_ui_refactor_plan.md # 技术设计文档 (300行) -``` - -### 代码质量指标 - -- **总代码行数**: ~1450行 (新增) -- **注释覆盖率**: >40% (完整Doxygen注释) -- **编译状态**: ✅ 成功 (零错误, 仅警告) -- **代码规范**: ✅ 遵循项目编码标准 -- **架构设计**: ✅ 模块化、可扩展 - -### 核心技术特性 - -#### 1. 现代化设备卡片界面 -``` -┌─────────────────────────────────────┐ -│ 🚁 侦察机-01 [●在线] │ -│ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │ -│ 📍 位置: N39.90, E116.40 │ -│ 🌐 网络: 192.168.1.101:8080 │ -│ 📶 信号: ████████░░ 80% │ -│ 🔋 电量: ██████████ 100% │ -│ ─────────────────────────────────── │ -│ [📊 详情] [🎮 控制] [📍 定位] │ -└─────────────────────────────────────┘ -``` - -#### 2. 状态指示系统 -- **颜色编码**: 绿色(在线) / 黄色(警告) / 红色(离线) / 灰色(未知) -- **实时更新**: 支持设备状态的动态变化 -- **进度条显示**: 信号强度和电量的可视化 - -#### 3. 搜索和过滤功能 -- **关键词搜索**: 按设备名称或IP地址搜索 -- **类型过滤**: 全部/无人机/机器狗/在线/离线 -- **实时过滤**: 输入即时响应 - -#### 4. 事件驱动架构 -- **设备选择事件**: `deviceSelected(QString deviceId)` -- **设备操作事件**: `deviceControlRequested/LocationRequested/DetailsRequested` -- **设备管理事件**: `addDeviceRequested(QString type)` -- **统计更新事件**: `deviceCountChanged(int total, int online)` - -## 设计模式应用 - -### 1. 组合模式 (Composite Pattern) -- **DeviceListPanel** 作为容器管理多个 **DeviceCard** 组件 -- 统一的接口处理单个设备和设备集合的操作 - -### 2. 观察者模式 (Observer Pattern) -- Qt信号槽机制实现组件间的松耦合通信 -- 设备状态变化自动通知所有相关组件 - -### 3. 工厂模式 (Factory Pattern) -- `createDeviceCard()` 方法统一创建设备卡片实例 -- 标准化的组件创建和初始化流程 - -### 4. 策略模式 (Strategy Pattern) -- 不同的过滤策略 (DeviceFilterType枚举) -- 可扩展的设备显示和排序策略 - -## 界面设计亮点 - -### 1. 军用风格现代化 -- **配色方案**: 深蓝灰色系 + 青蓝色强调 -- **视觉效果**: 渐变背景、发光边框、阴影效果 -- **图标系统**: 统一的表情符号图标风格 - -### 2. 信息层次化设计 -- **头部区域**: 设备标识和状态指示 -- **信息区域**: 位置、网络、状态参数 -- **操作区域**: 功能按钮和交互控件 - -### 3. 交互反馈优化 -- **悬停效果**: 鼠标悬停时的视觉反馈 -- **选中状态**: 清晰的选中边框指示 -- **按钮反馈**: 点击、悬停状态的颜色变化 - -## 兼容性保证 - -### 1. 向下兼容 -- **保持现有接口**: 不影响其他模块的正常运行 -- **渐进式集成**: 新组件可独立测试和集成 -- **数据格式兼容**: DeviceInfo结构兼容现有数据库 - -### 2. Qt 5兼容性 -- **API选择**: 使用Qt 5稳定的API和方法 -- **版本适配**: 处理Qt 5/6差异 (如removeIf → removeAt) -- **编译兼容**: 确保在目标环境正常编译 - -## 性能优化 - -### 1. 内存管理 -- **智能指针**: 使用Qt父子对象自动内存管理 -- **对象复用**: 卡片组件的创建和销毁优化 -- **资源释放**: 及时释放不需要的组件资源 - -### 2. 渲染优化 -- **按需更新**: 只更新发生变化的设备卡片 -- **滚动优化**: 使用QScrollArea的优化滚动 -- **样式缓存**: CSS样式的合理缓存机制 - -### 3. 数据处理 -- **增量更新**: 支持设备信息的增量同步 -- **状态监控**: 可配置的状态检查间隔 -- **搜索优化**: 高效的实时搜索算法 - -## 测试验证 - -### 1. 编译测试 -- ✅ **编译成功**: 零错误,少量警告(已标记为可接受) -- ✅ **链接成功**: 所有符号正确解析 -- ✅ **资源集成**: Qt资源系统正常工作 - -### 2. 代码质量测试 -- ✅ **静态分析**: 无严重代码问题 -- ✅ **内存安全**: 无明显内存泄漏风险 -- ✅ **异常处理**: 完善的错误处理机制 - -### 3. 接口测试 -- ✅ **信号槽连接**: 所有事件信号正确定义 -- ✅ **方法签名**: 公有接口符合预期 -- ✅ **数据模型**: DeviceInfo结构完整性 - -## 遗留问题和后续工作 - -### 1. 待实现功能 (Phase 4计划) -- [ ] **数据库集成**: 连接真实的UAV/Dog数据库 -- [ ] **实时状态更新**: 实现设备心跳检测 -- [ ] **批量操作**: 多设备选择和批量控制 -- [ ] **设备分组**: 按类型或状态分组显示 - -### 2. 潜在优化项 -- [ ] **虚拟滚动**: 处理大量设备时的性能优化 -- [ ] **动画效果**: 添加更流畅的过渡动画 -- [ ] **主题系统**: 支持多主题切换 -- [ ] **国际化**: 多语言支持框架 - -### 3. 集成工作 -- [ ] **MainWindow集成**: 将新组件集成到主界面 -- [ ] **事件处理**: 连接设备操作到具体功能 -- [ ] **数据同步**: 实现界面与数据库的双向同步 - -## 项目影响评估 - -### 1. 正面影响 -- **用户体验显著提升**: 现代化界面大幅改善交互体验 -- **代码架构优化**: 组件化设计提高代码可维护性 -- **可扩展性增强**: 新架构支持功能的快速扩展 -- **开发效率提升**: 模块化组件加速后续开发 - -### 2. 风险控制 -- **向下兼容**: 不破坏现有功能的正常运行 -- **渐进式部署**: 支持分阶段集成和测试 -- **回滚能力**: 保留原有界面作为备选方案 - -## 技术债务 - -### 1. 已解决的债务 -- ✅ **模块化改造**: 从单一文件架构改为组件化架构 -- ✅ **代码规范**: 统一的编码风格和注释规范 -- ✅ **构建系统**: 完善的项目构建配置 - -### 2. 新增的技术债务 -- **TODO标记**: 代码中的TODO项目需要后续实现 -- **模拟数据**: 当前使用模拟数据,需要连接真实数据源 -- **单元测试**: 缺少自动化测试,需要补充测试框架 - -## 里程碑达成 - -### 里程碑2: 新界面框架搭建完成 ✅ -- **目标**: 完成设备卡片界面设计和实现 -- **达成**: 100% - 所有核心组件开发完成 -- **质量**: 高质量代码,完整文档,成功编译 - -### 下一里程碑: 里程碑3 -- **目标**: 核心功能迁移完成,系统可正常运行 -- **计划**: 集成新界面到主窗口,连接数据库,实现完整功能 - -## 总结 - -Phase 3的界面架构重设计工作已成功完成,实现了预期的所有目标: - -1. **完成了现代化设备卡片界面的设计和实现** -2. **建立了可扩展的组件化架构** -3. **提供了完整的技术文档和代码注释** -4. **确保了代码质量和编译的成功** - -新的界面架构为战场探索系统提供了更好的用户体验和更强的可维护性,为后续功能的开发奠定了坚实的基础。整个重构过程遵循了既定的技术规范和项目管理流程,达到了高质量的交付标准。 - ---- - -**下一步工作**: 进入Phase 4,重点是将新的界面组件集成到主系统中,实现完整的设备管理和控制功能。 - -**项目状态**: 🟢 **健康** - 按计划推进,质量达标,准备进入下一阶段。 \ No newline at end of file diff --git a/src/Client/include/ui/UIInitializationManager.h b/src/Client/include/ui/UIInitializationManager.h new file mode 100644 index 00000000..7f67a176 --- /dev/null +++ b/src/Client/include/ui/UIInitializationManager.h @@ -0,0 +1,353 @@ +/** + * @file UIInitializationManager.h + * @brief UI初始化管理器 - 单一职责原则实现 + * @author BattlefieldExplorationSystem Team + * @date 2024-01-01 + * @version 2.0 + * + * 专门负责UI组件的初始化和配置,应用了以下设计模式: + * - 建造者模式:分步骤构建复杂的UI结构 + * - 策略模式:不同的初始化策略 + * - 单一职责原则:仅负责UI初始化 + * + * @note 将MainWindow::setupUI()的复杂逻辑分解为可管理的小函数 + * @since 2.0 + */ + +#ifndef UIINITIALIZATIONMANAGER_H +#define UIINITIALIZATIONMANAGER_H + +// C++标准库头文件 +#include + +// Qt头文件 +#include +#include + +// 前向声明 +class MainWindow; +class DeviceListPanel; +class SystemLogPanel; +class RightFunctionPanel; +class QSplitter; +class QTimer; +class ConfigManager; + +/** + * @class WindowGeometryCalculator + * @brief 窗口几何计算器 - 单一职责 + * + * 专门负责计算和设置窗口的大小、位置等几何属性。 + * + * @since 2.0 + */ +class WindowGeometryCalculator +{ +public: + /** + * @brief 窗口几何配置结构 + */ + struct WindowGeometry { + int width; ///< 窗口宽度 + int height; ///< 窗口高度 + int x; ///< 窗口X坐标 + int y; ///< 窗口Y坐标 + int minWidth; ///< 最小宽度 + int minHeight; ///< 最小高度 + }; + + /** + * @brief 计算最佳窗口几何 + * @param config 配置管理器 + * @return WindowGeometry 窗口几何配置 + */ + static WindowGeometry calculateOptimalGeometry(ConfigManager& config); + + /** + * @brief 应用窗口几何到目标窗口 + * @param window 目标窗口 + * @param geometry 几何配置 + */ + static void applyGeometry(QWidget* window, const WindowGeometry& geometry); + +private: + /** + * @brief 获取屏幕几何信息 + * @return QRect 屏幕几何 + */ + static QRect getScreenGeometry(); + + /** + * @brief 计算居中位置 + * @param screenSize 屏幕大小 + * @param windowSize 窗口大小 + * @return QPoint 居中位置 + */ + static QPoint calculateCenterPosition(const QSize& screenSize, const QSize& windowSize); +}; + +/** + * @class UIComponentBuilder + * @brief UI组件建造者 - 建造者模式 + * + * 使用建造者模式逐步构建复杂的UI组件层次结构。 + * + * @since 2.0 + */ +class UIComponentBuilder +{ +public: + /** + * @brief 构造函数 + * @param mainWindow 主窗口引用 + */ + explicit UIComponentBuilder(MainWindow* mainWindow); + + /** + * @brief 析构函数 + */ + ~UIComponentBuilder() = default; + + /** + * @brief 构建系统日志面板 + * @return UIComponentBuilder& 支持链式调用 + */ + UIComponentBuilder& buildSystemLogPanel(); + + /** + * @brief 构建设备列表面板 + * @return UIComponentBuilder& 支持链式调用 + */ + UIComponentBuilder& buildDeviceListPanel(); + + /** + * @brief 构建右侧功能面板 + * @return UIComponentBuilder& 支持链式调用 + */ + UIComponentBuilder& buildRightFunctionPanel(); + + /** + * @brief 构建分割器和布局 + * @return UIComponentBuilder& 支持链式调用 + */ + UIComponentBuilder& buildSplitterLayout(); + + /** + * @brief 完成构建并返回结果 + * @return bool 构建是否成功 + */ + bool finalize(); + + /** + * @brief 获取构建的组件 + * @return 构建的组件指针 + */ + SystemLogPanel* getSystemLogPanel() const { return m_systemLogPanel.get(); } + DeviceListPanel* getDeviceListPanel() const { return m_deviceListPanel.get(); } + RightFunctionPanel* getRightFunctionPanel() const { return m_rightFunctionPanel.get(); } + QSplitter* getLeftPanelSplitter() const { return m_leftPanelSplitter.get(); } + +private: + /** + * @brief 配置分割器样式 + */ + void configureSplitterStyle(); + + /** + * @brief 设置面板约束 + */ + void setupPanelConstraints(); + +private: + MainWindow* m_mainWindow; ///< 主窗口指针 + std::unique_ptr m_systemLogPanel; ///< 系统日志面板 + std::unique_ptr m_deviceListPanel; ///< 设备列表面板 + std::unique_ptr m_rightFunctionPanel; ///< 右侧功能面板 + std::unique_ptr m_leftPanelSplitter; ///< 左侧分割器 + bool m_built; ///< 构建状态 +}; + +/** + * @class SignalConnectionManager + * @brief 信号连接管理器 - 观察者模式 + * + * 统一管理组件间的信号槽连接,应用观察者模式。 + * + * @since 2.0 + */ +class SignalConnectionManager : public QObject +{ + Q_OBJECT + +public: + /** + * @brief 构造函数 + * @param parent 父对象 + */ + explicit SignalConnectionManager(QObject* parent = nullptr); + + /** + * @brief 析构函数 + */ + ~SignalConnectionManager() override = default; + + /** + * @brief 建立设备面板信号连接 + * @param devicePanel 设备面板 + * @param mainWindow 主窗口 + */ + void connectDevicePanelSignals(DeviceListPanel* devicePanel, MainWindow* mainWindow); + + /** + * @brief 建立右侧功能面板信号连接 + * @param rightPanel 右侧功能面板 + * @param mainWindow 主窗口 + */ + void connectRightPanelSignals(RightFunctionPanel* rightPanel, MainWindow* mainWindow); + + /** + * @brief 建立配置管理器信号连接 + * @param configManager 配置管理器 + * @param mainWindow 主窗口 + */ + void connectConfigManagerSignals(ConfigManager* configManager, MainWindow* mainWindow); + + /** + * @brief 断开所有连接 + */ + void disconnectAll(); + +private: + /** + * @brief 建立现代化信号槽连接 + * @tparam Sender 发送者类型 + * @tparam Signal 信号类型 + * @tparam Receiver 接收者类型 + * @tparam Slot 槽类型 + * @param sender 发送者 + * @param signal 信号 + * @param receiver 接收者 + * @param slot 槽函数 + * @param connectionType 连接类型 + */ + template + void makeConnection(Sender* sender, Signal signal, Receiver* receiver, Slot slot, + Qt::ConnectionType connectionType = Qt::AutoConnection); + +private: + std::vector m_connections; ///< 连接列表 +}; + +/** + * @class UIInitializationManager + * @brief UI初始化管理器主类 + * + * 协调整个UI初始化过程,应用门面模式为MainWindow提供简化接口。 + * + * @since 2.0 + */ +class UIInitializationManager : public QObject +{ + Q_OBJECT + +public: + /** + * @brief 构造函数 + * @param mainWindow 主窗口 + * @param parent 父对象 + */ + explicit UIInitializationManager(MainWindow* mainWindow, QObject* parent = nullptr); + + /** + * @brief 析构函数 + */ + ~UIInitializationManager() override; + + /** + * @brief 执行完整的UI初始化 + * @return bool 初始化是否成功 + */ + bool initializeUI(); + + /** + * @brief 初始化窗口几何 + * @return bool 是否成功 + */ + bool initializeWindowGeometry(); + + /** + * @brief 初始化UI组件 + * @return bool 是否成功 + */ + bool initializeComponents(); + + /** + * @brief 初始化信号连接 + * @return bool 是否成功 + */ + bool initializeSignalConnections(); + + /** + * @brief 初始化样式和主题 + * @return bool 是否成功 + */ + bool initializeStyleAndTheme(); + + /** + * @brief 执行最终初始化步骤 + * @return bool 是否成功 + */ + bool finalizeInitialization(); + + /** + * @brief 获取初始化状态 + * @return bool 是否已初始化 + */ + bool isInitialized() const { return m_initialized; } + +signals: + /** + * @brief UI初始化完成信号 + * @param success 是否成功 + */ + void initializationCompleted(bool success); + + /** + * @brief 初始化进度信号 + * @param step 当前步骤 + * @param total 总步骤数 + * @param description 步骤描述 + */ + void initializationProgress(int step, int total, const QString& description); + +private: + /** + * @brief 设置随机数生成器(现代化方式) + */ + void setupRandomGenerator(); + + /** + * @brief 设置全局样式 + */ + void setupGlobalStyles(); + + /** + * @brief 验证初始化结果 + * @return bool 验证是否通过 + */ + bool validateInitialization(); + +private: + MainWindow* m_mainWindow; ///< 主窗口指针 + std::unique_ptr m_componentBuilder; ///< 组件建造者 + std::unique_ptr m_connectionManager; ///< 信号连接管理器 + ConfigManager* m_configManager; ///< 配置管理器 + + bool m_initialized; ///< 初始化状态 + std::unique_ptr m_initializationTimer; ///< 初始化计时器 + + // 初始化步骤计数 + static constexpr int TOTAL_INIT_STEPS = 6; ///< 总初始化步骤数 +}; + +#endif // UIINITIALIZATIONMANAGER_H \ No newline at end of file diff --git a/src/Client/include/utils/ConfigManager.h b/src/Client/include/utils/ConfigManager.h new file mode 100644 index 00000000..04ebfca2 --- /dev/null +++ b/src/Client/include/utils/ConfigManager.h @@ -0,0 +1,239 @@ +/** + * @file ConfigManager.h + * @brief 配置管理器 - 单例模式实现 + * @author BattlefieldExplorationSystem Team + * @date 2024-01-01 + * @version 2.0 + * + * 线程安全的配置管理器,负责: + * - 数据库连接配置的安全管理 + * - 系统参数的集中配置 + * - 环境变量和配置文件的统一处理 + * - 敏感信息的加密存储 + * + * @note 使用单例模式确保全局唯一性 + * @since 2.0 + */ + +#ifndef CONFIGMANAGER_H +#define CONFIGMANAGER_H + +// C++标准库头文件 +#include +#include +#include + +// Qt头文件 +#include +#include +#include +#include + +/** + * @class ConfigManager + * @brief 线程安全的配置管理器单例类 + * + * 负责管理应用程序的所有配置信息,包括: + * - 数据库连接参数(主机、端口、用户名、密码) + * - UI配置参数(窗口大小、样式等) + * - 系统运行参数(日志级别、缓存设置等) + * + * 特性: + * - 线程安全的单例实现 + * - 支持配置文件和环境变量 + * - 敏感信息自动加密存储 + * - 配置变更通知机制 + * + * @warning 敏感配置信息不应直接硬编码,应通过环境变量或加密配置文件提供 + * @see QSettings, QCryptographicHash + * @since 2.0 + */ +class ConfigManager : public QObject +{ + Q_OBJECT + +public: + /** + * @brief 获取配置管理器单例实例 + * @return ConfigManager& 单例引用 + * @note 线程安全的懒加载实现 + */ + static ConfigManager& getInstance(); + + /** + * @brief 析构函数 + */ + ~ConfigManager() override; + + // 禁用拷贝构造和赋值操作 + ConfigManager(const ConfigManager&) = delete; + ConfigManager& operator=(const ConfigManager&) = delete; + ConfigManager(ConfigManager&&) = delete; + ConfigManager& operator=(ConfigManager&&) = delete; + + /** + * @brief 初始化配置管理器 + * @param configFilePath 配置文件路径(可选) + * @return bool 初始化是否成功 + */ + bool initialize(const QString& configFilePath = QString()); + + // 数据库配置相关方法 + /** + * @brief 获取数据库主机名 + * @return QString 主机名 + */ + QString getDatabaseHost() const; + + /** + * @brief 获取数据库端口 + * @return int 端口号 + */ + int getDatabasePort() const; + + /** + * @brief 获取数据库名称 + * @return QString 数据库名 + */ + QString getDatabaseName() const; + + /** + * @brief 获取数据库用户名 + * @return QString 用户名 + */ + QString getDatabaseUser() const; + + /** + * @brief 获取数据库密码 + * @return QString 密码(解密后) + * @warning 该方法返回明文密码,仅在建立连接时使用 + */ + QString getDatabasePassword() const; + + /** + * @brief 设置数据库配置 + * @param host 主机名 + * @param port 端口号 + * @param database 数据库名 + * @param user 用户名 + * @param password 密码(将被加密存储) + */ + void setDatabaseConfig(const QString& host, int port, const QString& database, + const QString& user, const QString& password); + + // 通用配置方法 + /** + * @brief 获取配置值 + * @tparam T 值类型 + * @param key 配置键 + * @param defaultValue 默认值 + * @return T 配置值 + */ + template + T getValue(const QString& key, const T& defaultValue = T{}) const + { + std::lock_guard lock(m_mutex); + return m_settings->value(key, QVariant::fromValue(defaultValue)).template value(); + } + + /** + * @brief 设置配置值 + * @tparam T 值类型 + * @param key 配置键 + * @param value 配置值 + */ + template + void setValue(const QString& key, const T& value) + { + { + std::lock_guard lock(m_mutex); + m_settings->setValue(key, QVariant::fromValue(value)); + m_settings->sync(); + } + emit configurationChanged(key); + } + + /** + * @brief 检查配置键是否存在 + * @param key 配置键 + * @return bool 是否存在 + */ + bool contains(const QString& key) const; + + /** + * @brief 移除配置项 + * @param key 配置键 + */ + void remove(const QString& key); + + /** + * @brief 保存配置到文件 + */ + void save(); + + /** + * @brief 重新加载配置 + */ + void reload(); + +signals: + /** + * @brief 配置变更信号 + * @param key 变更的配置键 + */ + void configurationChanged(const QString& key); + +private: + /** + * @brief 私有构造函数 + */ + explicit ConfigManager(QObject* parent = nullptr); + + /** + * @brief 加载默认配置 + */ + void loadDefaultConfig(); + + /** + * @brief 从环境变量加载敏感配置 + */ + void loadFromEnvironment(); + + /** + * @brief 加密字符串 + * @param plainText 明文 + * @return QString 加密后的字符串 + */ + QString encryptString(const QString& plainText) const; + + /** + * @brief 解密字符串 + * @param encryptedText 加密的字符串 + * @return QString 解密后的明文 + */ + QString decryptString(const QString& encryptedText) const; + + /** + * @brief 生成加密密钥 + * @return QByteArray 密钥 + */ + QByteArray generateKey() const; + +private: + static std::unique_ptr m_instance; ///< 单例实例 + static std::mutex m_instanceMutex; ///< 实例创建互斥锁 + + mutable std::mutex m_mutex; ///< 配置访问互斥锁 + std::unique_ptr m_settings; ///< Qt配置对象 + QString m_configFilePath; ///< 配置文件路径 + bool m_initialized; ///< 初始化状态 + + // 配置键常量 + static constexpr const char* KEY_DB_HOST = "database/host"; + static constexpr const char* KEY_DB_PORT = "database/port"; + static constexpr const char* KEY_DB_NAME = "database/name"; + static constexpr const char* KEY_DB_USER = "database/user"; + static constexpr const char* KEY_DB_PASSWORD = "database/password_encrypted"; +}; + +#endif // CONFIGMANAGER_H \ No newline at end of file diff --git a/src/Client/run_app.sh b/src/Client/run_app.sh deleted file mode 100644 index 9b4ce910..00000000 --- a/src/Client/run_app.sh +++ /dev/null @@ -1,12 +0,0 @@ -#!/bin/bash -# 战场探索系统启动脚本 -# 清理snap环境变量,避免库冲突 - -# 清理可能导致冲突的snap环境变量 -unset GTK_PATH -unset LOCPATH -unset GIO_MODULE_DIR -unset GSETTINGS_SCHEMA_DIR - -# 启动程序 -exec ./BattlefieldExplorationSystem "$@" diff --git a/src/Client/setup_database.sql b/src/Client/setup_database.sql deleted file mode 100644 index b6728062..00000000 --- a/src/Client/setup_database.sql +++ /dev/null @@ -1,55 +0,0 @@ --- BattlefieldExplorationSystem 数据库设置脚本 - --- 创建数据库 -CREATE DATABASE IF NOT EXISTS fly_land_database; -USE fly_land_database; - --- 创建无人机表 -CREATE TABLE IF NOT EXISTS uavdatabase ( - id VARCHAR(50) PRIMARY KEY, - state INT DEFAULT 0, - ip VARCHAR(15) NOT NULL, - port INT DEFAULT 8080, - lon DOUBLE DEFAULT 0.0, - lat DOUBLE DEFAULT 0.0 -); - --- 创建机器狗表 -CREATE TABLE IF NOT EXISTS dogdatabase ( - id VARCHAR(50) PRIMARY KEY, - state INT DEFAULT 0, - ip VARCHAR(15) NOT NULL, - port INT DEFAULT 9090, - lon DOUBLE DEFAULT 0.0, - lat DOUBLE DEFAULT 0.0 -); - --- 插入测试数据 - 无人机 -INSERT INTO uavdatabase (id, state, ip, port, lon, lat) VALUES -('UAV001', 1, '192.168.1.101', 8080, 116.40, 39.90), -('UAV002', 2, '192.168.1.102', 8080, 116.42, 39.92), -('UAV003', 0, '192.168.1.103', 8080, 116.44, 39.94) -ON DUPLICATE KEY UPDATE - state = VALUES(state), - ip = VALUES(ip), - port = VALUES(port), - lon = VALUES(lon), - lat = VALUES(lat); - --- 插入测试数据 - 机器狗 -INSERT INTO dogdatabase (id, state, ip, port, lon, lat) VALUES -('DOG001', 1, '192.168.1.201', 9090, 116.38, 39.88), -('DOG002', 0, '192.168.1.202', 9090, 116.46, 39.86) -ON DUPLICATE KEY UPDATE - state = VALUES(state), - ip = VALUES(ip), - port = VALUES(port), - lon = VALUES(lon), - lat = VALUES(lat); - --- 验证数据 -SELECT 'UAV Devices:' AS info; -SELECT * FROM uavdatabase; - -SELECT 'Dog Devices:' AS info; -SELECT * FROM dogdatabase; \ No newline at end of file diff --git a/src/Client/src/ui/UIInitializationManager.cpp b/src/Client/src/ui/UIInitializationManager.cpp new file mode 100644 index 00000000..9dfe640b --- /dev/null +++ b/src/Client/src/ui/UIInitializationManager.cpp @@ -0,0 +1,530 @@ +/** + * @file UIInitializationManager.cpp + * @brief UI初始化管理器实现 + * @author BattlefieldExplorationSystem Team + * @date 2024-01-01 + * @version 2.0 + */ + +#include "ui/UIInitializationManager.h" +#include "ui/main/MainWindow.h" +#include "ui/components/DeviceListPanel.h" +#include "ui/components/SystemLogPanel.h" +#include "ui/components/RightFunctionPanel.h" +#include "utils/ConfigManager.h" +#include "utils/SystemLogger.h" + +// C++标准库头文件 +#include + +// Qt头文件 +#include +#include +#include +#include +#include +#include +#include +#include + +// WindowGeometryCalculator 实现 + +WindowGeometryCalculator::WindowGeometry +WindowGeometryCalculator::calculateOptimalGeometry(ConfigManager& config) +{ + WindowGeometry geometry; + + // 从配置获取基本尺寸 + geometry.minWidth = config.getValue("ui/min_window_width", MainWindow::MIN_WINDOW_WIDTH); + geometry.minHeight = config.getValue("ui/min_window_height", MainWindow::MIN_WINDOW_HEIGHT); + + // 获取屏幕几何信息 + QRect screenGeometry = getScreenGeometry(); + int margin = config.getValue("ui/window_margin", MainWindow::WINDOW_MARGIN); + + // 计算窗口尺寸(屏幕尺寸减去边距) + geometry.width = screenGeometry.width() - margin; + geometry.height = screenGeometry.height() - margin; + + // 确保不小于最小尺寸 + geometry.width = std::max(geometry.width, geometry.minWidth); + geometry.height = std::max(geometry.height, geometry.minHeight); + + // 计算居中位置 + QPoint centerPos = calculateCenterPosition(screenGeometry.size(), QSize(geometry.width, geometry.height)); + geometry.x = centerPos.x(); + geometry.y = centerPos.y(); + + return geometry; +} + +void WindowGeometryCalculator::applyGeometry(QWidget* window, const WindowGeometry& geometry) +{ + if (!window) { + return; + } + + // 设置最小尺寸 + window->setMinimumSize(geometry.minWidth, geometry.minHeight); + + // 设置窗口大小和位置 + window->resize(geometry.width, geometry.height); + window->move(geometry.x, geometry.y); +} + +QRect WindowGeometryCalculator::getScreenGeometry() +{ + // 使用现代化的屏幕获取方法 + QScreen* primaryScreen = QGuiApplication::primaryScreen(); + if (primaryScreen) { + return primaryScreen->availableGeometry(); + } + + // 回退到旧方法(兼容性) + return QApplication::desktop()->screenGeometry(); +} + +QPoint WindowGeometryCalculator::calculateCenterPosition(const QSize& screenSize, const QSize& windowSize) +{ + int x = (screenSize.width() - windowSize.width()) / 2; + int y = (screenSize.height() - windowSize.height()) / 2; + return QPoint(x, y); +} + +// UIComponentBuilder 实现 + +UIComponentBuilder::UIComponentBuilder(MainWindow* mainWindow) + : m_mainWindow(mainWindow) + , m_built(false) +{ + if (!m_mainWindow) { + throw std::invalid_argument("MainWindow cannot be null"); + } +} + +UIComponentBuilder& UIComponentBuilder::buildSystemLogPanel() +{ + m_systemLogPanel = std::make_unique(m_mainWindow); + + // 从配置获取面板约束 + ConfigManager& config = ConfigManager::getInstance(); + int minHeight = config.getValue("ui/log_panel_min_height", MainWindow::LOG_PANEL_MIN_HEIGHT); + int maxHeight = config.getValue("ui/log_panel_max_height", MainWindow::LOG_PANEL_MAX_HEIGHT); + + m_systemLogPanel->setMinimumHeight(minHeight); + m_systemLogPanel->setMaximumHeight(maxHeight); + + return *this; +} + +UIComponentBuilder& UIComponentBuilder::buildDeviceListPanel() +{ + m_deviceListPanel = std::make_unique(m_mainWindow); + + // 从配置获取面板约束 + ConfigManager& config = ConfigManager::getInstance(); + int minHeight = config.getValue("ui/device_panel_min_height", MainWindow::DEVICE_PANEL_MIN_HEIGHT); + + m_deviceListPanel->setMinimumHeight(minHeight); + + return *this; +} + +UIComponentBuilder& UIComponentBuilder::buildRightFunctionPanel() +{ + m_rightFunctionPanel = std::make_unique(m_mainWindow); + + return *this; +} + +UIComponentBuilder& UIComponentBuilder::buildSplitterLayout() +{ + if (!m_systemLogPanel || !m_deviceListPanel) { + throw std::runtime_error("System log panel and device list panel must be built first"); + } + + // 创建垂直分割器 + m_leftPanelSplitter = std::make_unique(Qt::Vertical, m_mainWindow); + + // 添加面板到分割器 + m_leftPanelSplitter->addWidget(m_systemLogPanel.get()); + m_leftPanelSplitter->addWidget(m_deviceListPanel.get()); + + // 配置分割器 + configureSplitterStyle(); + setupPanelConstraints(); + + return *this; +} + +bool UIComponentBuilder::finalize() +{ + if (m_built) { + return true; + } + + // 验证所有必要组件都已构建 + if (!m_systemLogPanel || !m_deviceListPanel || !m_rightFunctionPanel || !m_leftPanelSplitter) { + return false; + } + + m_built = true; + return true; +} + +void UIComponentBuilder::configureSplitterStyle() +{ + if (!m_leftPanelSplitter) { + return; + } + + // 设置现代化的分割器样式 + m_leftPanelSplitter->setStyleSheet( + "QSplitter::handle {" + " background: qlineargradient(x1:0, y1:0, x2:1, y2:0," + " stop:0 rgba(82, 194, 242, 0.3)," + " stop:0.5 rgba(82, 194, 242, 0.8)," + " stop:1 rgba(82, 194, 242, 0.3));" + " border-radius: 2px;" + " height: 8px;" + "}" + "QSplitter::handle:hover {" + " background: qlineargradient(x1:0, y1:0, x2:1, y2:0," + " stop:0 rgba(82, 194, 242, 0.5)," + " stop:0.5 rgba(82, 194, 242, 1.0)," + " stop:1 rgba(82, 194, 242, 0.5));" + "}" + ); +} + +void UIComponentBuilder::setupPanelConstraints() +{ + if (!m_leftPanelSplitter) { + return; + } + + // 从配置获取分割比例 + ConfigManager& config = ConfigManager::getInstance(); + int logRatio = config.getValue("ui/splitter_log_ratio", MainWindow::SPLITTER_RATIO_LOG); + int deviceRatio = config.getValue("ui/splitter_device_ratio", MainWindow::SPLITTER_RATIO_DEVICE); + + // 设置分割比例 + m_leftPanelSplitter->setSizes(QList() << logRatio << deviceRatio); +} + +// SignalConnectionManager 实现 + +SignalConnectionManager::SignalConnectionManager(QObject* parent) + : QObject(parent) +{ +} + +void SignalConnectionManager::connectDevicePanelSignals(DeviceListPanel* devicePanel, MainWindow* mainWindow) +{ + if (!devicePanel || !mainWindow) { + return; + } + + // 使用现代化信号槽连接语法 + makeConnection(devicePanel, &DeviceListPanel::deviceSelected, + mainWindow, &MainWindow::onDeviceSelected); + + makeConnection(devicePanel, &DeviceListPanel::deviceControlRequested, + mainWindow, &MainWindow::onDeviceControlRequested); + + makeConnection(devicePanel, &DeviceListPanel::deviceLocationRequested, + mainWindow, &MainWindow::onDeviceLocationRequested); + + makeConnection(devicePanel, &DeviceListPanel::deviceDetailsRequested, + mainWindow, &MainWindow::onDeviceDetailsRequested); + + makeConnection(devicePanel, &DeviceListPanel::addDeviceRequested, + mainWindow, &MainWindow::onAddDeviceRequested); +} + +void SignalConnectionManager::connectRightPanelSignals(RightFunctionPanel* rightPanel, MainWindow* mainWindow) +{ + if (!rightPanel || !mainWindow) { + return; + } + + // 战场探索模块信号 + makeConnection(rightPanel, &RightFunctionPanel::startMapping, + mainWindow, &MainWindow::onStartMapping); + + makeConnection(rightPanel, &RightFunctionPanel::stopMapping, + mainWindow, &MainWindow::onStopMapping); + + makeConnection(rightPanel, &RightFunctionPanel::startNavigation, + mainWindow, &MainWindow::onStartNavigation); + + makeConnection(rightPanel, &RightFunctionPanel::stopNavigation, + mainWindow, &MainWindow::onStopNavigation); + + makeConnection(rightPanel, &RightFunctionPanel::startPhotoTransmission, + mainWindow, &MainWindow::onStartPhotoTransmission); + + makeConnection(rightPanel, &RightFunctionPanel::stopPhotoTransmission, + mainWindow, &MainWindow::onStopPhotoTransmission); + + makeConnection(rightPanel, &RightFunctionPanel::startPersonRecognition, + mainWindow, &MainWindow::onStartPersonRecognition); + + makeConnection(rightPanel, &RightFunctionPanel::stopPersonRecognition, + mainWindow, &MainWindow::onStopPersonRecognition); + + // 情报传输模块信号 + makeConnection(rightPanel, &RightFunctionPanel::startVoiceCall, + mainWindow, &MainWindow::onStartVoiceCall); + + makeConnection(rightPanel, &RightFunctionPanel::endVoiceCall, + mainWindow, &MainWindow::onEndVoiceCall); + + // 敌情统计模块信号 + makeConnection(rightPanel, &RightFunctionPanel::refreshEnemyStats, + mainWindow, &MainWindow::onRefreshEnemyStats); + + makeConnection(rightPanel, &RightFunctionPanel::requestAIAnalysis, + mainWindow, &MainWindow::onRequestAIAnalysis); + + makeConnection(rightPanel, &RightFunctionPanel::exportReport, + mainWindow, &MainWindow::onExportReport); +} + +void SignalConnectionManager::connectConfigManagerSignals(ConfigManager* configManager, MainWindow* mainWindow) +{ + if (!configManager || !mainWindow) { + return; + } + + // 配置变更信号连接 + makeConnection(configManager, &ConfigManager::configurationChanged, + mainWindow, [mainWindow](const QString& key) { + qDebug() << "Configuration changed:" << key; + // 可以在这里添加配置变更的处理逻辑 + }); +} + +void SignalConnectionManager::disconnectAll() +{ + for (const auto& connection : m_connections) { + QObject::disconnect(connection); + } + m_connections.clear(); +} + +template +void SignalConnectionManager::makeConnection(Sender* sender, Signal signal, Receiver* receiver, Slot slot, + Qt::ConnectionType connectionType) +{ + if (!sender || !receiver) { + return; + } + + auto connection = QObject::connect(sender, signal, receiver, slot, connectionType); + if (connection) { + m_connections.push_back(connection); + } +} + +// UIInitializationManager 实现 + +UIInitializationManager::UIInitializationManager(MainWindow* mainWindow, QObject* parent) + : QObject(parent) + , m_mainWindow(mainWindow) + , m_configManager(&ConfigManager::getInstance()) + , m_initialized(false) +{ + if (!m_mainWindow) { + throw std::invalid_argument("MainWindow cannot be null"); + } + + // 创建子管理器 + m_componentBuilder = std::make_unique(m_mainWindow); + m_connectionManager = std::make_unique(this); + m_initializationTimer = std::make_unique(this); +} + +UIInitializationManager::~UIInitializationManager() = default; + +bool UIInitializationManager::initializeUI() +{ + if (m_initialized) { + qWarning() << "UI already initialized"; + return true; + } + + qDebug() << "Starting UI initialization..."; + SystemLogger::getInstance()->logInfo("开始UI初始化"); + + int currentStep = 0; + + // 步骤1:初始化窗口几何 + emit initializationProgress(++currentStep, TOTAL_INIT_STEPS, "初始化窗口几何"); + if (!initializeWindowGeometry()) { + emit initializationCompleted(false); + return false; + } + + // 步骤2:设置随机数生成器 + emit initializationProgress(++currentStep, TOTAL_INIT_STEPS, "设置随机数生成器"); + setupRandomGenerator(); + + // 步骤3:初始化UI组件 + emit initializationProgress(++currentStep, TOTAL_INIT_STEPS, "初始化UI组件"); + if (!initializeComponents()) { + emit initializationCompleted(false); + return false; + } + + // 步骤4:建立信号连接 + emit initializationProgress(++currentStep, TOTAL_INIT_STEPS, "建立信号连接"); + if (!initializeSignalConnections()) { + emit initializationCompleted(false); + return false; + } + + // 步骤5:初始化样式和主题 + emit initializationProgress(++currentStep, TOTAL_INIT_STEPS, "初始化样式和主题"); + if (!initializeStyleAndTheme()) { + emit initializationCompleted(false); + return false; + } + + // 步骤6:最终初始化 + emit initializationProgress(++currentStep, TOTAL_INIT_STEPS, "完成最终初始化"); + if (!finalizeInitialization()) { + emit initializationCompleted(false); + return false; + } + + // 验证初始化结果 + if (!validateInitialization()) { + emit initializationCompleted(false); + return false; + } + + m_initialized = true; + + qDebug() << "UI initialization completed successfully"; + SystemLogger::getInstance()->logSuccess("UI初始化完成"); + + emit initializationCompleted(true); + return true; +} + +bool UIInitializationManager::initializeWindowGeometry() +{ + try { + auto geometry = WindowGeometryCalculator::calculateOptimalGeometry(*m_configManager); + WindowGeometryCalculator::applyGeometry(m_mainWindow, geometry); + return true; + } catch (const std::exception& e) { + qCritical() << "Failed to initialize window geometry:" << e.what(); + return false; + } +} + +bool UIInitializationManager::initializeComponents() +{ + try { + // 使用建造者模式构建UI组件 + bool success = m_componentBuilder->buildSystemLogPanel() + .buildDeviceListPanel() + .buildRightFunctionPanel() + .buildSplitterLayout() + .finalize(); + + if (!success) { + qCritical() << "Failed to build UI components"; + return false; + } + + // 设置地图显示 + m_mainWindow->setupMapDisplay(); + + return true; + } catch (const std::exception& e) { + qCritical() << "Failed to initialize components:" << e.what(); + return false; + } +} + +bool UIInitializationManager::initializeSignalConnections() +{ + try { + // 连接设备面板信号 + m_connectionManager->connectDevicePanelSignals( + m_componentBuilder->getDeviceListPanel(), m_mainWindow); + + // 连接右侧功能面板信号 + m_connectionManager->connectRightPanelSignals( + m_componentBuilder->getRightFunctionPanel(), m_mainWindow); + + // 连接配置管理器信号 + m_connectionManager->connectConfigManagerSignals( + m_configManager, m_mainWindow); + + return true; + } catch (const std::exception& e) { + qCritical() << "Failed to initialize signal connections:" << e.what(); + return false; + } +} + +bool UIInitializationManager::initializeStyleAndTheme() +{ + try { + setupGlobalStyles(); + return true; + } catch (const std::exception& e) { + qCritical() << "Failed to initialize style and theme:" << e.what(); + return false; + } +} + +bool UIInitializationManager::finalizeInitialization() +{ + try { + // 初始化默认数据(使用配置而非硬编码) + QString defaultRobotName = m_configManager->getValue("default/robot_name", "Alice"); + QString defaultRobotIP = m_configManager->getValue("default/robot_ip", "192.168.0.1"); + + m_mainWindow->m_robotList.append(qMakePair(defaultRobotName, defaultRobotIP)); + + defaultRobotName = m_configManager->getValue("default/robot_name_2", "Bob"); + defaultRobotIP = m_configManager->getValue("default/robot_ip_2", "192.168.0.2"); + + m_mainWindow->m_robotList.append(qMakePair(defaultRobotName, defaultRobotIP)); + + return true; + } catch (const std::exception& e) { + qCritical() << "Failed to finalize initialization:" << e.what(); + return false; + } +} + +void UIInitializationManager::setupRandomGenerator() +{ + // Qt 5.15 中 QRandomGenerator::global() 是自动初始化的 + // 不需要手动设置种子,避免"Attempted to overwrite"错误 + qDebug() << "Random generator setup: using Qt built-in initialization"; + + // 如果需要特定的随机性,可以使用局部生成器: + // QRandomGenerator localGenerator(QRandomGenerator::global()->generate()); +} + +void UIInitializationManager::setupGlobalStyles() +{ + // 这里可以设置全局样式 + // 具体样式设置由各个组件自己管理 +} + +bool UIInitializationManager::validateInitialization() +{ + // 验证关键组件是否正确初始化 + return m_componentBuilder->getSystemLogPanel() != nullptr && + m_componentBuilder->getDeviceListPanel() != nullptr && + m_componentBuilder->getRightFunctionPanel() != nullptr && + m_componentBuilder->getLeftPanelSplitter() != nullptr; +} \ No newline at end of file diff --git a/src/Client/src/utils/ConfigManager.cpp b/src/Client/src/utils/ConfigManager.cpp new file mode 100644 index 00000000..d4f1f05d --- /dev/null +++ b/src/Client/src/utils/ConfigManager.cpp @@ -0,0 +1,267 @@ +/** + * @file ConfigManager.cpp + * @brief 配置管理器实现 + * @author BattlefieldExplorationSystem Team + * @date 2024-01-01 + * @version 2.0 + */ + +#include "utils/ConfigManager.h" + +// C++标准库头文件 +#include + +// Qt头文件 +#include +#include +#include +#include +#include + +// 静态成员初始化 +std::unique_ptr ConfigManager::m_instance = nullptr; +std::mutex ConfigManager::m_instanceMutex; + +ConfigManager& ConfigManager::getInstance() +{ + std::lock_guard lock(m_instanceMutex); + if (!m_instance) { + // 使用make_unique需要public构造函数,这里使用reset + m_instance.reset(new ConfigManager()); + } + return *m_instance; +} + +ConfigManager::ConfigManager(QObject* parent) + : QObject(parent) + , m_initialized(false) +{ + // 私有构造函数,仅供getInstance调用 +} + +ConfigManager::~ConfigManager() +{ + if (m_settings && m_initialized) { + save(); + } +} + +bool ConfigManager::initialize(const QString& configFilePath) +{ + std::lock_guard lock(m_mutex); + + if (m_initialized) { + qWarning() << "ConfigManager already initialized"; + return true; + } + + // 确定配置文件路径 + if (configFilePath.isEmpty()) { + // 使用默认路径 + QString configDir = QStandardPaths::writableLocation(QStandardPaths::AppConfigLocation); + QDir().mkpath(configDir); + m_configFilePath = configDir + "/config.ini"; + } else { + m_configFilePath = configFilePath; + } + + // 创建QSettings对象 + m_settings = std::make_unique(m_configFilePath, QSettings::IniFormat); + m_settings->setIniCodec("UTF-8"); + + // 加载默认配置 + loadDefaultConfig(); + + // 从环境变量加载敏感配置 + loadFromEnvironment(); + + m_initialized = true; + qDebug() << "ConfigManager initialized with config file:" << m_configFilePath; + + return true; +} + +void ConfigManager::loadDefaultConfig() +{ + // 数据库默认配置 + if (!m_settings->contains(KEY_DB_HOST)) { + m_settings->setValue(KEY_DB_HOST, "localhost"); + } + if (!m_settings->contains(KEY_DB_PORT)) { + m_settings->setValue(KEY_DB_PORT, 3306); + } + if (!m_settings->contains(KEY_DB_NAME)) { + m_settings->setValue(KEY_DB_NAME, "Client"); + } + if (!m_settings->contains(KEY_DB_USER)) { + m_settings->setValue(KEY_DB_USER, "root"); + } + + // UI默认配置 + if (!m_settings->contains("ui/window_width")) { + m_settings->setValue("ui/window_width", 1400); + } + if (!m_settings->contains("ui/window_height")) { + m_settings->setValue("ui/window_height", 1000); + } + if (!m_settings->contains("ui/window_margin")) { + m_settings->setValue("ui/window_margin", 100); + } + + // 系统默认配置 + if (!m_settings->contains("system/log_level")) { + m_settings->setValue("system/log_level", "INFO"); + } + if (!m_settings->contains("system/auto_save_interval")) { + m_settings->setValue("system/auto_save_interval", 300); // 5分钟 + } +} + +void ConfigManager::loadFromEnvironment() +{ + // 简化配置加载,使用固定的数据库配置便于项目交接 + qDebug() << "Loading default database configuration for project handover"; + + // 可选:仍然支持从环境变量加载数据库主机(如果需要) + const char* dbHost = std::getenv("BES_DB_HOST"); + if (dbHost && strlen(dbHost) > 0) { + m_settings->setValue(KEY_DB_HOST, QString::fromUtf8(dbHost)); + qDebug() << "Using database host from environment:" << dbHost; + } + + // 可选:仍然支持从环境变量加载数据库用户名(如果需要) + const char* dbUser = std::getenv("BES_DB_USER"); + if (dbUser && strlen(dbUser) > 0) { + m_settings->setValue(KEY_DB_USER, QString::fromUtf8(dbUser)); + qDebug() << "Using database user from environment:" << dbUser; + } +} + +QString ConfigManager::getDatabaseHost() const +{ + return getValue(KEY_DB_HOST, "localhost"); +} + +int ConfigManager::getDatabasePort() const +{ + return getValue(KEY_DB_PORT, 3306); +} + +QString ConfigManager::getDatabaseName() const +{ + return getValue(KEY_DB_NAME, "Client"); +} + +QString ConfigManager::getDatabaseUser() const +{ + return getValue(KEY_DB_USER, "root"); +} + +QString ConfigManager::getDatabasePassword() const +{ + // 直接返回项目固定密码,便于团队协作和项目交接 + qDebug() << "Using hardcoded database password for project handover"; + return QString("hzk200407140238"); +} + +void ConfigManager::setDatabaseConfig(const QString& host, int port, const QString& database, + const QString& user, const QString& password) +{ + { + std::lock_guard lock(m_mutex); + m_settings->setValue(KEY_DB_HOST, host); + m_settings->setValue(KEY_DB_PORT, port); + m_settings->setValue(KEY_DB_NAME, database); + m_settings->setValue(KEY_DB_USER, user); + + if (!password.isEmpty()) { + QString encryptedPassword = encryptString(password); + m_settings->setValue(KEY_DB_PASSWORD, encryptedPassword); + } + + m_settings->sync(); + } + + // 发送配置变更信号 + emit configurationChanged("database"); +} + +bool ConfigManager::contains(const QString& key) const +{ + std::lock_guard lock(m_mutex); + return m_settings->contains(key); +} + +void ConfigManager::remove(const QString& key) +{ + { + std::lock_guard lock(m_mutex); + m_settings->remove(key); + m_settings->sync(); + } + emit configurationChanged(key); +} + +void ConfigManager::save() +{ + std::lock_guard lock(m_mutex); + if (m_settings) { + m_settings->sync(); + qDebug() << "Configuration saved to:" << m_configFilePath; + } +} + +void ConfigManager::reload() +{ + std::lock_guard lock(m_mutex); + if (m_settings) { + m_settings->sync(); + loadFromEnvironment(); // 重新加载环境变量 + qDebug() << "Configuration reloaded from:" << m_configFilePath; + } +} + +QString ConfigManager::encryptString(const QString& plainText) const +{ + if (plainText.isEmpty()) { + return QString(); + } + + // 简单的加密实现(实际项目中应使用更强的加密算法) + QByteArray key = generateKey(); + QByteArray data = plainText.toUtf8(); + + // 异或加密 + for (int i = 0; i < data.size(); ++i) { + data[i] = data[i] ^ key[i % key.size()]; + } + + return QString::fromLatin1(data.toBase64()); +} + +QString ConfigManager::decryptString(const QString& encryptedText) const +{ + if (encryptedText.isEmpty()) { + return QString(); + } + + QByteArray key = generateKey(); + QByteArray data = QByteArray::fromBase64(encryptedText.toLatin1()); + + // 异或解密 + for (int i = 0; i < data.size(); ++i) { + data[i] = data[i] ^ key[i % key.size()]; + } + + return QString::fromUtf8(data); +} + +QByteArray ConfigManager::generateKey() const +{ + // 基于应用程序信息生成密钥 + QString keySource = QCoreApplication::applicationName() + + QCoreApplication::organizationName() + + "BattlefieldExplorationSystem"; + + return QCryptographicHash::hash(keySource.toUtf8(), QCryptographicHash::Sha256); +} \ No newline at end of file