978 lines
28 KiB
Markdown
978 lines
28 KiB
Markdown
# Qt前端客户端 / Qt Frontend Client
|
||
|
||
一个基于Qt框架开发的跨平台温湿度监控系统客户端应用程序,提供直观的图形用户界面用于实时监控和控制多个STM32设备。
|
||
|
||
A cross-platform temperature and humidity monitoring system client application developed with Qt framework, providing an intuitive graphical user interface for real-time monitoring and control of multiple STM32 devices.
|
||
|
||
## 📋 功能概览 / Features Overview
|
||
|
||
### 核心功能 / Core Features
|
||
- **实时数据监控** - 显示温度、湿度、蜂鸣器和LED状态
|
||
- **多设备管理** - 支持同时监控多个STM32设备
|
||
- **设备控制** - 远程控制设备的蜂鸣器和LED
|
||
- **自动重连** - 网络断开时自动重新连接
|
||
- **可配置刷新** - 自定义数据刷新间隔
|
||
- **跨平台支持** - 支持Linux、Windows和WSL
|
||
|
||
### 界面组件 / Interface Components
|
||
- **连接配置** - 服务器地址和端口设置
|
||
- **设备选择** - 下拉菜单选择当前监控设备
|
||
- **数据显示** - 实时显示传感器数据
|
||
- **控制面板** - 复选框控制设备输出
|
||
- **状态指示** - 连接状态和设备状态显示
|
||
|
||
## 🛠️ 技术特性 / Technical Features
|
||
|
||
### 兼容性设计 / Compatibility Design
|
||
- **Qt版本** - 兼容Qt 5.12及以上版本
|
||
- **C++标准** - 使用C++11标准
|
||
- **GLIBC兼容** - 支持GLIBC 2.31及更新版本
|
||
- **静态链接** - 减少运行时依赖
|
||
- **网络模块** - 基于QTcpSocket的TCP通信
|
||
|
||
### 架构特点 / Architecture Features
|
||
- **事件驱动** - 基于Qt信号槽机制
|
||
- **异步通信** - 非阻塞网络I/O
|
||
- **定时器管理** - 自动数据轮询和重连机制
|
||
- **JSON解析** - 结构化数据处理
|
||
- **内存管理** - Qt对象树自动内存管理
|
||
|
||
## 🚀 快速开始 / Quick Start
|
||
|
||
### 环境要求 / Prerequisites
|
||
|
||
#### 系统要求 / System Requirements
|
||
- **操作系统**: Linux 18.04+, Windows 10+, WSL2
|
||
- **处理器**: x86_64, ARM64
|
||
- **内存**: 最少512MB RAM
|
||
- **磁盘空间**: ~50MB
|
||
|
||
#### 软件依赖 / Software Dependencies
|
||
```bash
|
||
# Ubuntu/Debian
|
||
sudo apt update
|
||
sudo apt install -y qt5-default qtbase5-dev qtbase5-dev-tools
|
||
|
||
# CentOS/RHEL
|
||
sudo yum install -y qt5-qtbase-devel qt5-qttools-devel
|
||
|
||
# Windows
|
||
# 下载并安装Qt 5.12+ from https://www.qt.io/download
|
||
```
|
||
|
||
### 编译安装 / Build & Install
|
||
|
||
#### 方法一:使用qmake(推荐)
|
||
```bash
|
||
# 进入项目目录
|
||
cd qt_client
|
||
|
||
# 生成Makefile
|
||
qmake qt_client.pro
|
||
|
||
# 编译(使用所有CPU核心)
|
||
make -j$(nproc)
|
||
|
||
# 运行程序
|
||
./qt_client
|
||
```
|
||
|
||
#### 方法二:使用CMake(高级用户)
|
||
```bash
|
||
# 如果有CMakeLists.txt(需要创建)
|
||
mkdir build
|
||
cd build
|
||
cmake ..
|
||
make -j$(nproc)
|
||
./qt_client
|
||
```
|
||
|
||
#### Windows编译
|
||
```bash
|
||
# 使用Qt命令提示符
|
||
cd qt_client
|
||
qmake qt_client.pro
|
||
nmake
|
||
qt_client.exe
|
||
```
|
||
|
||
## 📖 使用指南 / User Guide
|
||
|
||
### 启动和连接 / Startup & Connection
|
||
|
||
1. **启动应用程序**
|
||
```bash
|
||
./qt_client
|
||
```
|
||
|
||
2. **配置服务器连接**
|
||
- 服务器地址:输入后端服务器IP地址(如:127.0.0.1)
|
||
- 端口:输入服务器端口(默认:5000)
|
||
- 点击"连接"按钮
|
||
|
||
3. **选择监控设备**
|
||
- 点击设备下拉菜单
|
||
- 选择要监控的设备ID
|
||
- 系统将自动开始显示该设备数据
|
||
|
||
### 界面操作 / Interface Operations
|
||
|
||
#### 连接管理 / Connection Management
|
||
- **连接按钮**:建立/断开与服务器的连接
|
||
- **重连间隔**:设置断线重连的时间间隔(秒)
|
||
- **数据刷新间隔**:设置数据轮询频率(毫秒)
|
||
|
||
#### 设备监控 / Device Monitoring
|
||
- **设备列表**:显示所有已连接的设备
|
||
- **刷新按钮**:手动刷新设备列表
|
||
- **当前设备**:显示正在监控的设备ID
|
||
|
||
#### 数据显示 / Data Display
|
||
- **温度**:显示当前温度值(°C)
|
||
- **湿度**:显示当前湿度值(%)
|
||
- **蜂鸣器状态**:显示蜂鸣器开关状态
|
||
- **LED状态**:显示三个LED的开关状态
|
||
|
||
#### 设备控制 / Device Control
|
||
- **蜂鸣器控制**:勾选复选框开启蜂鸣器
|
||
- **LED控制**:勾选对应LED的复选框控制开关
|
||
- **发送控制**:点击"发送控制"应用设置
|
||
|
||
### 高级功能 / Advanced Features
|
||
|
||
#### 自动重连 / Auto Reconnection
|
||
- 网络断开时程序会自动尝试重连
|
||
- 重连间隔可在界面中配置
|
||
- 支持手动禁用自动重连
|
||
|
||
#### 数据轮询 / Data Polling
|
||
- 定时向服务器请求最新数据
|
||
- 轮询间隔可自定义(100ms-60s)
|
||
- 支持暂停/恢复数据轮询
|
||
|
||
## 🔧 配置选项 / Configuration Options
|
||
|
||
### 项目配置 / Project Configuration
|
||
|
||
#### qt_client.pro 主要配置项
|
||
```pro
|
||
# Qt模块配置
|
||
QT += core widgets network
|
||
|
||
# C++标准
|
||
CONFIG += c++11
|
||
|
||
# 目标名称
|
||
TARGET = qt_client
|
||
TEMPLATE = app
|
||
|
||
# 兼容性定义
|
||
DEFINES += _GLIBCXX_USE_CXX11_ABI=0
|
||
DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x050C00
|
||
```
|
||
|
||
### 运行时配置 / Runtime Configuration
|
||
|
||
#### 网络配置 / Network Configuration
|
||
- **超时时间**:连接超时5秒
|
||
- **缓冲区大小**:接收缓冲区8KB
|
||
- **协议**:TCP/IP协议
|
||
- **数据格式**:JSON文本格式
|
||
|
||
#### 界面配置 / Interface Configuration
|
||
- **窗口大小**:800x600像素(可调整)
|
||
- **字体**:系统默认字体
|
||
- **主题**:系统原生主题
|
||
- **语言**:中文/英文(基于系统语言)
|
||
|
||
## 📊 数据协议 / Data Protocol
|
||
|
||
### 客户端发送 / Client to Server
|
||
|
||
#### 轮询请求 / Poll Request
|
||
```json
|
||
{
|
||
"type": "poll",
|
||
"device_id": "dev001"
|
||
}
|
||
```
|
||
|
||
#### 控制命令 / Control Command
|
||
```json
|
||
{
|
||
"type": "control",
|
||
"device_id": "dev001",
|
||
"buzzer": 1,
|
||
"led0": 0,
|
||
"led1": 1,
|
||
"led2": 0
|
||
}
|
||
```
|
||
|
||
### 服务器响应 / Server to Client
|
||
|
||
#### 传感器数据 / Sensor Data
|
||
```json
|
||
{
|
||
"device_id": "dev001",
|
||
"temp": 25.5,
|
||
"humidity": 60.0,
|
||
"buzzer": 0,
|
||
"led0": 1,
|
||
"led1": 0,
|
||
"led2": 0,
|
||
"timestamp": "2025-11-03 10:30:00"
|
||
}
|
||
```
|
||
|
||
#### 设备列表 / Device List
|
||
```json
|
||
{
|
||
"type": "device_list",
|
||
"devices": ["dev001", "dev002", "dev003"]
|
||
}
|
||
```
|
||
|
||
## 🐛 故障排除 / Troubleshooting
|
||
|
||
### 常见问题 / Common Issues
|
||
|
||
#### 编译问题 / Build Issues
|
||
1. **找不到qmake**
|
||
```bash
|
||
# Ubuntu/Debian
|
||
sudo apt install -y qt5-qmake
|
||
|
||
# 检查Qt安装
|
||
which qmake
|
||
qmake -version
|
||
```
|
||
|
||
2. **链接错误**
|
||
```bash
|
||
# 清理重新编译
|
||
make clean
|
||
qmake qt_client.pro
|
||
make
|
||
```
|
||
|
||
3. **GLIBC版本不兼容**
|
||
```bash
|
||
# 检查GLIBC版本
|
||
ldd --version
|
||
|
||
# 使用兼容模式运行
|
||
export LD_LIBRARY_PATH=/usr/lib/x86_64-linux-gnu
|
||
./qt_client
|
||
```
|
||
|
||
#### 运行时问题 / Runtime Issues
|
||
|
||
1. **无法连接服务器**
|
||
- 检查服务器是否运行:`netstat -tlnp | grep 5000`
|
||
- 验证IP地址和端口
|
||
- 检查防火墙设置
|
||
|
||
2. **设备列表为空**
|
||
- 确认STM32设备已连接到服务器
|
||
- 检查设备网络连接
|
||
- 查看服务器日志
|
||
|
||
3. **数据不更新**
|
||
- 检查轮询间隔设置
|
||
- 确认设备在线状态
|
||
- 重启应用程序
|
||
|
||
### 调试模式 / Debug Mode
|
||
|
||
#### 启用调试输出 / Enable Debug Output
|
||
```bash
|
||
# 编译时添加调试标志
|
||
qmake CONFIG+=debug qt_client.pro
|
||
make
|
||
|
||
# 使用调试器运行
|
||
gdb ./qt_client
|
||
```
|
||
|
||
#### 日志信息 / Log Information
|
||
- 网络连接状态
|
||
- 数据接收/发送记录
|
||
- 错误信息和异常
|
||
- 设备状态变化
|
||
|
||
## 🔬 核心逻辑实现详解 / Core Logic Implementation Details
|
||
|
||
### 网络通信架构 / Network Communication Architecture
|
||
|
||
#### TCP连接管理 / TCP Connection Management
|
||
程序采用Qt的QTcpSocket进行TCP通信,支持断线自动重连:
|
||
|
||
```cpp
|
||
// 连接状态机 (mainwindow.cpp:149-162)
|
||
void MainWindow::connectToServer() {
|
||
if (m_socket->state() == QAbstractSocket::ConnectedState) {
|
||
// 已连接状态 -> 断开连接
|
||
m_socket->disconnectFromHost();
|
||
return;
|
||
}
|
||
// 未连接状态 -> 尝试连接
|
||
QString ip = m_ipEdit->text().trimmed();
|
||
quint16 port = m_portEdit->text().toUShort(&ok);
|
||
m_socket->connectToHost(ip, port);
|
||
}
|
||
```
|
||
|
||
**连接生命周期管理**:
|
||
1. **连接建立** (mainwindow.cpp:164-172): 启动轮询定时器,停止重连定时器
|
||
2. **连接断开** (mainwindow.cpp:174-179): 停止轮询定时器,启动重连定时器
|
||
3. **自动重连** (mainwindow.cpp:321-331): 定时器触发时检查连接状态,自动重连
|
||
|
||
#### 数据接收处理逻辑 / Data Reception Processing Logic
|
||
实现智能数据解析,支持多种数据格式:
|
||
|
||
```cpp
|
||
// 分层数据处理 (mainwindow.cpp:181-306)
|
||
void MainWindow::onReadyRead() {
|
||
// 第一层:按行处理数据
|
||
while (m_socket->canReadLine()) {
|
||
QByteArray line = m_socket->readLine().trimmed();
|
||
// JSON解析和验证
|
||
QJsonDocument doc = QJsonDocument::fromJson(line, &err);
|
||
// 消息类型分发处理
|
||
}
|
||
|
||
// 第二层:处理无换行符的批量数据
|
||
if (m_socket->bytesAvailable() > 0) {
|
||
QByteArray raw = m_socket->readAll();
|
||
// 分割并处理多行JSON数据
|
||
}
|
||
}
|
||
```
|
||
|
||
**消息类型识别和处理**:
|
||
- **设备列表消息** (`type: "devices_list"`): 动态更新设备下拉框
|
||
- **设备状态消息** (`type: "device_status"`): 更新设备在线状态
|
||
- **传感器数据**: 自动识别并添加新设备,更新显示
|
||
- **控制状态响应**: 更新设备控制界面的状态
|
||
|
||
### 定时器管理系统 / Timer Management System
|
||
|
||
#### 双定时器架构 / Dual-Timer Architecture
|
||
程序使用两个独立的定时器实现不同功能:
|
||
|
||
```cpp
|
||
// 轮询定时器 - 数据采集 (mainwindow.cpp:125-129)
|
||
m_pollTimer = new QTimer(this);
|
||
m_pollTimer->setInterval(m_pollSpin->value()); // 默认100ms
|
||
connect(m_pollTimer, SIGNAL(timeout()), this, SLOT(pollData()));
|
||
|
||
// 重连定时器 - 连接管理 (mainwindow.cpp:132-136)
|
||
m_reconnectTimer = new QTimer(this);
|
||
m_reconnectTimer->setInterval(m_reconnectSpin->value() * 1000); // 默认5秒
|
||
m_reconnectTimer->setSingleShot(false); // 重复触发
|
||
```
|
||
|
||
**定时器协调机制**:
|
||
- **连接状态**: 轮询定时器启动,重连定时器停止
|
||
- **断开状态**: 轮询定时器停止,重连定时器启动
|
||
- **动态调整**: 用户可实时调整定时器间隔
|
||
|
||
### 设备管理系统 / Device Management System
|
||
|
||
#### 动态设备发现 / Dynamic Device Discovery
|
||
实现设备的自动发现和状态管理:
|
||
|
||
```cpp
|
||
// 设备自动添加逻辑 (mainwindow.cpp:226-244)
|
||
QString deviceId = obj.value("device_id").toString();
|
||
if (!deviceId.isEmpty()) {
|
||
// 检查设备是否已存在于下拉框
|
||
bool deviceExists = false;
|
||
for (int i = 0; i < m_deviceCombo->count(); ++i) {
|
||
if (m_deviceCombo->itemData(i).toString() == deviceId) {
|
||
deviceExists = true;
|
||
break;
|
||
}
|
||
}
|
||
|
||
// 自动添加新发现的设备
|
||
if (!deviceExists) {
|
||
QString displayText = QString("%1 (在线)").arg(deviceId);
|
||
m_deviceCombo->addItem(displayText, deviceId);
|
||
}
|
||
}
|
||
```
|
||
|
||
#### 设备数据缓存 / Device Data Caching
|
||
使用QMap实现设备数据的本地缓存:
|
||
|
||
```cpp
|
||
// 设备数据存储 (mainwindow.h:79)
|
||
QMap<QString, QString> m_deviceData; // device_id -> latest data
|
||
|
||
// 数据更新逻辑 (mainwindow.cpp:246-249)
|
||
m_deviceData[deviceId] = QString::fromUtf8(doc.toJson(QJsonDocument::Compact));
|
||
```
|
||
|
||
### 用户界面逻辑 / User Interface Logic
|
||
|
||
#### 信号槽连接模式 / Signal-Slot Connection Pattern
|
||
采用Qt5兼容的信号槽连接方式:
|
||
|
||
```cpp
|
||
// 兼容Qt 5.12的连接方式 (mainwindow.cpp:116-139)
|
||
connect(m_connectBtn, SIGNAL(clicked()), this, SLOT(connectToServer()));
|
||
connect(m_socket, SIGNAL(connected()), this, SLOT(onConnected()));
|
||
connect(m_deviceCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(onDeviceSelectionChanged()));
|
||
```
|
||
|
||
#### 设备切换逻辑 / Device Switching Logic
|
||
设备选择切换时的完整处理流程:
|
||
|
||
```cpp
|
||
// 设备选择处理 (mainwindow.cpp:333-368)
|
||
void MainWindow::onDeviceSelectionChanged() {
|
||
// 1. 更新当前设备ID
|
||
m_currentDeviceId = m_deviceCombo->currentData().toString();
|
||
|
||
// 2. 加载该设备的缓存数据
|
||
if (m_deviceData.contains(m_currentDeviceId)) {
|
||
QJsonDocument doc = QJsonDocument::fromJson(data.toUtf8(), &err);
|
||
updateDisplayFromJson(doc.object());
|
||
}
|
||
|
||
// 3. 请求设备的最新控制状态
|
||
if (m_socket->state() == QAbstractSocket::ConnectedState) {
|
||
QString request = QString("GET control %1\n").arg(m_currentDeviceId);
|
||
m_socket->write(request.toUtf8());
|
||
}
|
||
}
|
||
```
|
||
|
||
### 控制命令系统 / Control Command System
|
||
|
||
#### 命令构建和发送 / Command Construction and Sending
|
||
控制命令的JSON构建和发送流程:
|
||
|
||
```cpp
|
||
// 控制命令发送 (mainwindow.cpp:370-393)
|
||
void MainWindow::sendControlCommand() {
|
||
QJsonObject obj;
|
||
obj["device_id"] = m_currentDeviceId;
|
||
obj["buzzer"] = m_buzzerChk->isChecked() ? 1 : 0;
|
||
for (int i = 0; i < 3; ++i) {
|
||
obj[QString("led%1").arg(i)] = m_ledChk[i]->isChecked() ? 1 : 0;
|
||
}
|
||
|
||
QJsonDocument doc(obj);
|
||
QByteArray ba = doc.toJson(QJsonDocument::Compact) + '\n';
|
||
m_socket->write(ba);
|
||
m_socket->flush();
|
||
}
|
||
```
|
||
|
||
#### 本地状态同步 / Local State Synchronization
|
||
发送命令后立即更新本地显示状态:
|
||
|
||
```cpp
|
||
// 本地显示状态更新 (mainwindow.cpp:388-392)
|
||
QJsonObject displayObj;
|
||
displayObj["buzzer"] = m_buzzerChk->isChecked() ? 1 : 0;
|
||
for (int i = 0; i < 3; ++i) {
|
||
displayObj[QString("led%1").arg(i)] = m_ledChk[i]->isChecked() ? 1 : 0;
|
||
}
|
||
updateDisplayFromJson(displayObj);
|
||
```
|
||
|
||
### 兼容性设计模式 / Compatibility Design Patterns
|
||
|
||
#### GLIBC兼容性处理 / GLIBC Compatibility Handling
|
||
多层兼容性保障确保在不同系统上运行:
|
||
|
||
```cpp
|
||
// GLIBC版本限制 (main.cpp:6-11, mainwindow.h:9-14)
|
||
#if defined(__GLIBC__) && defined(__GLIBC_MINOR__)
|
||
# if __GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 31)
|
||
# define GLIBC_MAX_VERSION "2.31"
|
||
# endif
|
||
#endif
|
||
|
||
// 编译器标志设置 (qt_client.pro:21-25)
|
||
QMAKE_CXXFLAGS += -static-libgcc -static-libstdc++
|
||
QMAKE_CXXFLAGS += -D_GLIBCXX_USE_CXX11_ABI=0
|
||
QMAKE_CXXFLAGS += -std=c++11
|
||
```
|
||
|
||
#### Qt版本兼容 / Qt Version Compatibility
|
||
使用Qt 5.12兼容的API和语法:
|
||
|
||
```cpp
|
||
// 使用QString字面量而非QLatin1String (mainwindow.cpp:379)
|
||
obj["device_id"] = m_currentDeviceId; // 兼容Qt 5.12
|
||
|
||
// 使用SIGNAL/SLOT宏而非函数指针 (mainwindow.cpp:122)
|
||
connect(m_deviceCombo, SIGNAL(currentIndexChanged(int)),
|
||
this, SLOT(onDeviceSelectionChanged()));
|
||
```
|
||
|
||
### 错误处理机制 / Error Handling Mechanism
|
||
|
||
#### JSON解析容错 / JSON Parsing Fault Tolerance
|
||
多层JSON解析错误处理:
|
||
|
||
```cpp
|
||
// 分层JSON验证 (mainwindow.cpp:189-195, 277-282)
|
||
QJsonParseError err;
|
||
QJsonDocument doc = QJsonDocument::fromJson(line, &err);
|
||
if (err.error != QJsonParseError::NoError) {
|
||
qWarning() << "JSON parse error:" << err.errorString() << "line:" << line;
|
||
continue; // 跳过错误数据,继续处理
|
||
}
|
||
if (!doc.isObject()) continue; // 验证数据类型
|
||
```
|
||
|
||
#### 网络异常处理 / Network Exception Handling
|
||
网络通信中的异常情况处理:
|
||
|
||
```cpp
|
||
// 连接状态检查 (mainwindow.cpp:310-311)
|
||
if (m_socket->state() != QAbstractSocket::ConnectedState) return;
|
||
|
||
// 端口解析容错 (mainwindow.cpp:157-159)
|
||
quint16 port = m_portEdit->text().toUShort(&ok);
|
||
if (!ok) port = 5001; // 默认端口
|
||
```
|
||
|
||
## 🔮 扩展开发 / Extended Development
|
||
|
||
### 添加新功能 / Adding New Features
|
||
|
||
#### 1. 添加新的传感器支持
|
||
```cpp
|
||
// 在mainwindow.h中添加
|
||
QLabel *m_pressureLabel; // 气压显示
|
||
|
||
// 在updateDisplayFromJson中处理
|
||
if (obj.contains("pressure")) {
|
||
m_pressureLabel->setText(QString::number(obj["pressure"].toDouble(), 'f', 2));
|
||
}
|
||
```
|
||
|
||
#### 2. 添加历史数据图表
|
||
```pro
|
||
# 在.pro文件中添加
|
||
QT += charts
|
||
```
|
||
|
||
#### 3. 添加数据导出功能
|
||
```cpp
|
||
// 导出CSV格式
|
||
void exportToCSV(const QString &filename);
|
||
```
|
||
|
||
### 界面定制 / Interface Customization
|
||
|
||
#### 主题支持 / Theme Support
|
||
```cpp
|
||
// 应用暗色主题
|
||
QApplication::setStyle("Fusion");
|
||
QPalette darkPalette;
|
||
// 配置暗色调色板
|
||
qApp->setPalette(darkPalette);
|
||
```
|
||
|
||
#### 多语言支持 / Multi-language Support
|
||
```cpp
|
||
// 加载翻译文件
|
||
QTranslator translator;
|
||
translator.load("qt_client_zh_CN.qm");
|
||
qApp->installTranslator(&translator);
|
||
```
|
||
|
||
## 🔄 程序执行流程 / Program Execution Flow
|
||
|
||
### 应用程序启动流程 / Application Startup Flow
|
||
```cpp
|
||
// main.cpp 执行路径 (main.cpp:16-22)
|
||
int main(int argc, char *argv[]) {
|
||
QApplication a(argc, argv); // 1. 创建Qt应用程序实例
|
||
MainWindow w; // 2. 创建主窗口实例
|
||
w.show(); // 3. 显示主窗口
|
||
return a.exec(); // 4. 进入Qt事件循环
|
||
}
|
||
```
|
||
|
||
### 主窗口初始化流程 / Main Window Initialization Flow
|
||
```cpp
|
||
// MainWindow构造函数执行路径 (mainwindow.cpp:35-143)
|
||
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) {
|
||
// 阶段1: UI组件创建和布局设置 (35-114)
|
||
QWidget *central = new QWidget(this);
|
||
// 创建输入框、按钮、标签等UI控件
|
||
// 设置水平和垂直布局
|
||
|
||
// 阶段2: 信号槽连接 (116-139)
|
||
connect(m_connectBtn, SIGNAL(clicked()), this, SLOT(connectToServer()));
|
||
connect(m_socket, SIGNAL(connected()), this, SLOT(onConnected()));
|
||
// 连接所有UI事件和网络事件
|
||
|
||
// 阶段3: 定时器初始化和配置 (125-136)
|
||
m_pollTimer = new QTimer(this);
|
||
m_reconnectTimer = new QTimer(this);
|
||
// 配置定时器间隔和触发方式
|
||
|
||
// 阶段4: 窗口属性设置 (141-142)
|
||
setWindowTitle(tr("温湿度监控"));
|
||
resize(480, 200);
|
||
}
|
||
```
|
||
|
||
### 事件驱动处理流程 / Event-Driven Processing Flow
|
||
|
||
#### 网络事件处理链 / Network Event Processing Chain
|
||
```
|
||
用户点击"连接"按钮
|
||
↓
|
||
connectToServer() 被调用
|
||
↓
|
||
m_socket->connectToHost(ip, port)
|
||
↓
|
||
[网络连接建立]
|
||
↓
|
||
onConnected() 信号触发
|
||
↓
|
||
启动轮询定时器 + 停止重连定时器 + 自动刷新设备列表
|
||
↓
|
||
服务器发送数据
|
||
↓
|
||
onReadyRead() 信号触发
|
||
↓
|
||
JSON解析 + 消息类型分发 + UI更新
|
||
```
|
||
|
||
#### 用户交互处理链 / User Interaction Processing Chain
|
||
```
|
||
用户选择设备
|
||
↓
|
||
onDeviceSelectionChanged() 被调用
|
||
↓
|
||
更新当前设备ID + 加载缓存数据 + 请求控制状态
|
||
↓
|
||
用户调整控制选项
|
||
↓
|
||
sendControlCommand() 被调用
|
||
↓
|
||
构建JSON命令 + 发送到服务器 + 同步本地显示
|
||
```
|
||
|
||
## 📁 项目结构 / Project Structure
|
||
|
||
```
|
||
qt_client/ # Qt客户端根目录
|
||
├── main.cpp # 程序入口点
|
||
├── mainwindow.h # 主窗口类声明
|
||
├── mainwindow.cpp # 主窗口类实现
|
||
├── qt_client.pro # qmake项目文件
|
||
├── qt_client_compat.pro # 兼容性项目文件
|
||
├── qt_client # 编译后的可执行文件
|
||
├── README.md # 本文档
|
||
└── resources/ # 资源文件(如果存在)
|
||
├── icons/ # 图标文件
|
||
├── images/ # 图片文件
|
||
└── translations/ # 翻译文件
|
||
```
|
||
|
||
### 文件说明 / File Descriptions
|
||
|
||
| 文件 | 描述 | 主要功能 |
|
||
|------|------|----------|
|
||
| `main.cpp` | 程序入口 | GLIBC兼容性处理,Qt应用初始化,主窗口创建 |
|
||
| `mainwindow.h` | 主窗口头文件 | 类声明,UI组件定义,信号槽声明,成员变量定义 |
|
||
| `mainwindow.cpp` | 主窗口实现 | UI布局,网络通信,数据处理,事件处理,兼容性处理 |
|
||
| `qt_client.pro` | 项目配置 | Qt模块配置,编译标志,GLIBC兼容性设置,依赖管理 |
|
||
|
||
## 🧠 数据结构与算法 / Data Structures & Algorithms
|
||
|
||
### 核心数据结构 / Core Data Structures
|
||
|
||
#### 设备数据管理结构 / Device Data Management Structure
|
||
```cpp
|
||
// 设备数据缓存映射 (mainwindow.h:79)
|
||
QMap<QString, QString> m_deviceData; // device_id -> JSON数据字符串
|
||
|
||
// 设备选择组合框数据结构 (mainwindow.cpp:203-211)
|
||
m_deviceCombo->addItem(displayText, deviceId); // 显示文本 + 设备ID作为用户数据
|
||
```
|
||
|
||
**数据结构特点**:
|
||
- **键值映射**: 使用QString作为键,避免内存拷贝
|
||
- **JSON缓存**: 原始JSON字符串缓存,减少重复解析开销
|
||
- **双重存储**: ComboBox存储显示文本和设备ID,便于UI展示和数据查询
|
||
|
||
#### 网络消息处理结构 / Network Message Processing Structure
|
||
```cpp
|
||
// 分层消息处理 (mainwindow.cpp:181-306)
|
||
// 第一层:行式数据处理
|
||
while (m_socket->canReadLine()) {
|
||
QByteArray line = m_socket->readLine().trimmed();
|
||
// JSON解析和消息类型识别
|
||
}
|
||
|
||
// 第二层:批量数据处理
|
||
if (m_socket->bytesAvailable() > 0) {
|
||
QByteArray raw = m_socket->readAll();
|
||
QList<QByteArray> parts = raw.split('\n');
|
||
// 逐个处理每个JSON消息
|
||
}
|
||
```
|
||
|
||
### 关键算法实现 / Key Algorithm Implementations
|
||
|
||
#### 设备自动发现算法 / Device Auto-Discovery Algorithm
|
||
```cpp
|
||
// 设备存在性检查算法 (mainwindow.cpp:229-243)
|
||
bool deviceExists = false;
|
||
for (int i = 0; i < m_deviceCombo->count(); ++i) {
|
||
if (m_deviceCombo->itemData(i).toString() == deviceId) {
|
||
deviceExists = true;
|
||
break; // 早期退出优化
|
||
}
|
||
}
|
||
|
||
// 设备自动添加算法
|
||
if (!deviceExists) {
|
||
QString displayText = QString("%1 (在线)").arg(deviceId);
|
||
m_deviceCombo->addItem(displayText, deviceId);
|
||
}
|
||
```
|
||
|
||
**算法复杂度**: O(n) 时间复杂度,其中n为设备数量
|
||
**优化策略**:
|
||
- 早期退出减少不必要的遍历
|
||
- 设备ID直接比较,避免字符串解析
|
||
|
||
#### JSON消息解析算法 / JSON Message Parsing Algorithm
|
||
```cpp
|
||
// 容错JSON解析算法 (mainwindow.cpp:188-195)
|
||
QJsonParseError err;
|
||
QJsonDocument doc = QJsonDocument::fromJson(line, &err);
|
||
if (err.error != QJsonParseError::NoError) {
|
||
qWarning() << "JSON parse error:" << err.errorString();
|
||
continue; // 跳过错误数据,继续处理
|
||
}
|
||
|
||
// 消息类型分发算法 (mainwindow.cpp:198-224)
|
||
if (obj.contains("type")) {
|
||
QString type = obj.value("type").toString();
|
||
if (type == "devices_list") {
|
||
// 处理设备列表消息
|
||
} else if (type == "device_status") {
|
||
// 处理设备状态消息
|
||
}
|
||
} else {
|
||
// 处理传感器数据消息
|
||
}
|
||
```
|
||
|
||
**算法特点**:
|
||
- **错误隔离**: 单个JSON解析失败不影响其他消息处理
|
||
- **类型分发**: 基于消息类型的策略模式处理
|
||
- **向后兼容**: 支持无type字段的遗留消息格式
|
||
|
||
#### 定时器协调算法 / Timer Coordination Algorithm
|
||
```cpp
|
||
// 连接状态驱动的定时器管理 (mainwindow.cpp:164-179)
|
||
void MainWindow::onConnected() {
|
||
m_connectBtn->setText(tr("断开"));
|
||
m_pollTimer->start(); // 启动数据轮询
|
||
m_reconnectTimer->stop(); // 停止自动重连
|
||
}
|
||
|
||
void MainWindow::onDisconnected() {
|
||
m_connectBtn->setText(tr("连接"));
|
||
m_pollTimer->stop(); // 停止数据轮询
|
||
if (!m_reconnectTimer->isActive()) {
|
||
m_reconnectTimer->start(); // 启动自动重连
|
||
}
|
||
}
|
||
```
|
||
|
||
**协调机制**:
|
||
- **互斥控制**: 轮询定时器和重连定时器永远不会同时运行
|
||
- **状态驱动**: 定时器状态完全由网络连接状态决定
|
||
- **防重复**: 检查定时��状态,避免重复启动
|
||
|
||
### 内存管理策略 / Memory Management Strategy
|
||
|
||
#### Qt对象树管理 / Qt Object Tree Management
|
||
```cpp
|
||
// 自动内存管理 (mainwindow.cpp:36)
|
||
MainWindow::MainWindow(QWidget *parent)
|
||
: QMainWindow(parent), m_socket(new QTcpSocket(this)) {
|
||
// 所有UI组件以this为父对象,自动管理生命周期
|
||
m_pollTimer = new QTimer(this);
|
||
m_reconnectTimer = new QTimer(this);
|
||
}
|
||
```
|
||
|
||
**内存管理特点**:
|
||
- **父子关系**: 所有UI组件和定时器以主窗口为父对象
|
||
- **自动清理**: 主窗口销毁时自动清理所有子对象
|
||
- **无需手动delete**: Qt对象树自动管理内存分配和释放
|
||
|
||
#### 数据缓存优化 / Data Caching Optimization
|
||
```cpp
|
||
// JSON数据压缩缓存 (mainwindow.cpp:248)
|
||
m_deviceData[deviceId] = QString::fromUtf8(doc.toJson(QJsonDocument::Compact));
|
||
|
||
// 按需加载策略 (mainwindow.cpp:342-350)
|
||
if (m_deviceData.contains(m_currentDeviceId)) {
|
||
QString data = m_deviceData[m_currentDeviceId];
|
||
QJsonDocument doc = QJsonDocument::fromJson(data.toUtf8(), &err);
|
||
// 只在需要时才解析JSON数据
|
||
}
|
||
```
|
||
|
||
**优化策略**:
|
||
- **压缩存储**: 使用Compact格式减少内存占用
|
||
- **延迟解析**: 只在显示需要时才解析JSON
|
||
- **LRU潜力**: QMap天然支持最近最少使用访问模式
|
||
|
||
## 📚 API参考 / API Reference
|
||
|
||
### 主要类 / Main Classes
|
||
|
||
#### MainWindow类
|
||
```cpp
|
||
class MainWindow : public QMainWindow
|
||
{
|
||
Q_OBJECT
|
||
|
||
public:
|
||
explicit MainWindow(QWidget *parent = nullptr);
|
||
~MainWindow();
|
||
|
||
private slots:
|
||
void connectToServer(); // 连接服务器
|
||
void onConnected(); // 连接成功
|
||
void onDisconnected(); // 连接断开
|
||
void onReadyRead(); // 数据接收
|
||
void pollData(); // 轮询数据
|
||
void sendControlCommand(); // 发送控制命令
|
||
|
||
private:
|
||
void updateDisplayFromJson(const QJsonObject &obj);
|
||
|
||
// UI组件
|
||
QLineEdit *m_ipEdit; // IP地址输入
|
||
QLineEdit *m_portEdit; // 端口输入
|
||
QComboBox *m_deviceCombo; // 设备选择
|
||
QLabel *m_tempLabel; // 温度显示
|
||
QLabel *m_humLabel; // 湿度显示
|
||
QTcpSocket *m_socket; // TCP套接字
|
||
QTimer *m_pollTimer; // 轮询定时器
|
||
};
|
||
```
|
||
|
||
### 信号槽连接 / Signal-Slot Connections
|
||
```cpp
|
||
// 网络连接
|
||
connect(m_socket, &QTcpSocket::connected, this, &MainWindow::onConnected);
|
||
connect(m_socket, &QTcpSocket::disconnected, this, &MainWindow::onDisconnected);
|
||
connect(m_socket, &QTcpSocket::readyRead, this, &MainWindow::onReadyRead);
|
||
|
||
// UI交互
|
||
connect(m_connectBtn, &QPushButton::clicked, this, &MainWindow::connectToServer);
|
||
connect(m_sendControlBtn, &QPushButton::clicked, this, &MainWindow::sendControlCommand);
|
||
|
||
// 定时器
|
||
connect(m_pollTimer, &QTimer::timeout, this, &MainWindow::pollData);
|
||
```
|
||
|
||
## 📈 性能优化 / Performance Optimization
|
||
|
||
### 内存优化 / Memory Optimization
|
||
- 使用Qt对象树自动管理内存
|
||
- 避免内存泄漏,及时释放资源
|
||
- 合理设置缓冲区大小
|
||
|
||
### 网络优化 / Network Optimization
|
||
- 使用TCP_NODELAY减少延迟
|
||
- 批量处理网络数据
|
||
- 实现数据压缩传输
|
||
|
||
### 渲染优化 / Rendering Optimization
|
||
- 减少不必要的UI更新
|
||
- 使用双缓冲减少闪烁
|
||
- 优化重绘频率
|
||
|
||
## 🔒 安全考虑 / Security Considerations
|
||
|
||
### 网络安全 / Network Security
|
||
- 验证服务器身份证书
|
||
- 加密敏感数据传输
|
||
- 实现访问控制机制
|
||
|
||
### 数据安全 / Data Security
|
||
- 本地数据加密存储
|
||
- 定期清理临时文件
|
||
- 记录操作审计日志
|
||
|
||
## 📄 许可证 / License
|
||
|
||
本Qt客户端项目遵循MIT许可证:
|
||
|
||
```
|
||
MIT License
|
||
|
||
Copyright (c) 2025 Temperature & Humidity Monitoring System
|
||
|
||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||
of this software and associated documentation files (the "Software"), to deal
|
||
in the Software without restriction, including without limitation the rights
|
||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||
copies of the Software, and to permit persons to whom the Software is
|
||
furnished to do so, subject to the following conditions:
|
||
|
||
The above copyright notice and this permission notice shall be included in all
|
||
copies or substantial portions of the Software.
|
||
```
|
||
|
||
## 🤝 贡献指南 / Contributing Guide
|
||
|
||
### 开发流程 / Development Workflow
|
||
1. Fork项目仓库
|
||
2. 创建功能分支:`git checkout -b feature/new-feature`
|
||
3. 提交更改:`git commit -am 'Add new feature'`
|
||
4. 推送分支:`git push origin feature/new-feature`
|
||
5. 创建Pull Request
|
||
|
||
### 代码规范 / Code Style
|
||
- 遵循Qt官方编码规范
|
||
- 使用有意义的变量和函数名
|
||
- 添加适当的注释和文档
|
||
- 保持代码简洁和可读性
|
||
|
||
## 📞 技术支持 / Technical Support
|
||
|
||
### 联系方式 / Contact Information
|
||
- 📧 **邮箱**: [your-email@example.com]
|
||
- 🐛 **问题反馈**: [GitHub Issues Page]
|
||
- 📖 **项目文档**: [Project Wiki]
|
||
- 💬 **技术讨论**: [Discussion Forum]
|
||
|
||
### 版本历史 / Version History
|
||
|
||
| 版本 | 日期 | 更新内容 |
|
||
|------|------|----------|
|
||
| v1.0.0 | 2025-11-03 | 初始版本发布 |
|
||
| v1.1.0 | 计划中 | 添加历史数据图表 |
|
||
| v1.2.0 | 计划中 | 支持数据导出功能 |
|
||
| v2.0.0 | 计划中 | 全新UI设计和主题支持 |
|
||
|
||
---
|
||
|
||
**最后更新 / Last Updated**: 2025-11-03
|
||
|
||
**文档版本 / Documentation Version**: v1.0.0 |