feat: Service Registry + Bridge 解耦架构 + 全工程代码清理
## 架构升级:Service Registry + Bridge 模式 - 新增 PluginSDK/IPluginServices.h:10 个纯虚服务接口(IDataProvider/ILinkProvider/...) - 新增 MavLinkServiceBridge:单 QObject 实现全部服务,隔离 MavLinkNode 依赖 - 升级 PluginManifest:支持 plugin.json 的 provides/consumes 声明式依赖 - 实现 ExtensionHost::autoWire():元对象自省自动连接信号槽 - 集成到 AppController:initModules() 中创建桥接器并注册到 ServiceRegistry - CockpitPlugin 演示服务发现:initialize() 中通过 PluginContext 查找服务 ## 代码清理 - Plugins/opmap:~280 行死代码(waypointsetting 100行注释块/tilematrix 54行/等27个文件) - Plugins/MavLinkNode:~200 行 GBK 乱码注释翻译为 UTF-8 + 12 行注释死代码 - Plugins/ToolsUI:~222 行死代码(ECU.cpp 82行/INS.cpp 113行/Parse/ToolsUI 等) - StatusUI/Setting/MissionUI:~65 行注释死代码 - Cockpit/leftladder.cpp:10 处 GBK 乱码翻译为中文 - 清理头文件注释掉的 #include(19 处)、空 if-else 分支、注释变量声明 ## 编译验证 - [100%] Built target GCS 零错误 - 运行时 timeout 3s 正常退出,无崩溃
This commit is contained in:
@@ -0,0 +1,800 @@
|
||||
# C/C++ 编码规范
|
||||
|
||||
> **适用项目**: GCS 地面控制站
|
||||
> **语言标准**: C++17
|
||||
> **框架**: Qt 5/6
|
||||
> **构建工具**: CMake 3.16+
|
||||
> **格式化配置**: `.clang-format` (根目录)
|
||||
> **编码格式**: UTF-8
|
||||
---
|
||||
|
||||
## 1. 总则
|
||||
|
||||
### 1.1 核心原则
|
||||
|
||||
| 原则 | 说明 |
|
||||
|------|------|
|
||||
| **一致性优先** | 新增/修改的代码必须与所在文件或模块的现有风格保持一致 |
|
||||
| **最小改动** | 仅修改目标代码,不随意改写无关代码、不随意添加无关注释 |
|
||||
| **编译安全** | 修改后必须能通过编译;增删成员时检查所有引用 |
|
||||
| **头文件自足** | 每个 `.h` 应包含其所有依赖的前向声明或 include |
|
||||
|
||||
### 1.2 编码原则
|
||||
|
||||
- 代码优先考虑**可读性**和**可维护性**
|
||||
- 避免过度抽象,优先使用简单方案
|
||||
- 所有面向用户的字符串必须使用 `tr()` 包裹
|
||||
- 禁止在新代码中使用裸 `new` / `delete` 管理资源(除非 Qt 父子关系托管的 QObject 构造)
|
||||
|
||||
---
|
||||
|
||||
## 2. 文件组织
|
||||
|
||||
### 2.1 文件命名
|
||||
|
||||
| 类型 | 规范 | 示例 |
|
||||
|------|------|------|
|
||||
| 源文件 | `PascalCase.cpp` | `DataLink.cpp` |
|
||||
| 头文件 | `PascalCase.h` | `DataLink.h` |
|
||||
| UI 文件 | `PascalCase.ui` | `StatusUI.ui` |
|
||||
| 资源文件 | `snake_case.qrc` | `resources.qrc` |
|
||||
|
||||
### 2.2 目录结构
|
||||
|
||||
**当前项目实际结构**:
|
||||
|
||||
```
|
||||
gcs_nf/
|
||||
├── App/ # 主程序入口 + MainWindow + AppController
|
||||
├── PluginSDK/ # 插件开发包(静态库)
|
||||
├── Plugins/ # 所有功能插件(每个插件一个目录)
|
||||
│ ├── IPlugin.h # 插件接口 v2
|
||||
│ ├── IPlugin_v3.h # 插件接口 v3(向后兼容 v2)
|
||||
│ ├── Cockpit/ # 自绘飞行仪表盘
|
||||
│ ├── MavLinkNode/ # MAVLink 协议解析
|
||||
│ ├── dlink/ # 数据链路层
|
||||
│ ├── opmap/ # 离线地图引擎
|
||||
│ ├── Skin/ # 主题/皮肤
|
||||
│ ├── Setting/ # 系统设置
|
||||
│ └── ...
|
||||
├── mavlink/ # MAVLink C 库(git submodule)
|
||||
├── docs/ # 文档
|
||||
├── bin/ # 构建产物输出目录
|
||||
└── .clang-format # 代码格式化配置
|
||||
```
|
||||
|
||||
**文件组织约定**:
|
||||
|
||||
- 每个插件目录下至少有一个与插件同名的 `.h` / `.cpp` 主文件
|
||||
- 使用 `inc/` / `src/` 分离头文件和源文件的模块(如 Cockpit、MavLinkNode),新增文件也遵循此布局
|
||||
- UI 文件与使用它的类放在同一目录
|
||||
- 插件目录下必须有 `plugin.json` 清单文件
|
||||
|
||||
### 2.3 Include 顺序
|
||||
|
||||
遵循 **Google C++ Style** 的 include 顺序,各组之间空一行分隔:
|
||||
|
||||
```
|
||||
1. 自身对应的头文件(仅 .cpp 中): #include "MyClass.h"
|
||||
2. C 系统头文件: #include <cstdint>
|
||||
3. C++ 标准库头文件: #include <memory>
|
||||
4. 第三方库头文件 (Qt): #include <QObject>
|
||||
5. 本项目头文件: #include "PluginSDK/PluginContext.h"
|
||||
```
|
||||
|
||||
**示范**(`.cpp` 文件):
|
||||
|
||||
```cpp
|
||||
#include "PluginContext.h"
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include <QDebug>
|
||||
#include <QObject>
|
||||
|
||||
#include "ServiceRegistry.h"
|
||||
```
|
||||
|
||||
**示范**(`.h` 文件):
|
||||
|
||||
```cpp
|
||||
#ifndef PLUGINCONTEXT_H
|
||||
#define PLUGINCONTEXT_H
|
||||
|
||||
#include <QObject>
|
||||
|
||||
#include "MavLinkService.h"
|
||||
```
|
||||
|
||||
**禁止事项**:
|
||||
|
||||
- **禁止** 用 `""` 包含系统头文件或 Qt 头文件(如 `#include "QObject"`),一律使用 `<>`
|
||||
- **禁止** 在头文件中包含不必要的头文件,优先使用前向声明
|
||||
|
||||
---
|
||||
|
||||
## 3. 命名规范
|
||||
|
||||
### 3.1 命名总表
|
||||
|
||||
| 元素 | 规范 | 示例 |
|
||||
|------|------|------|
|
||||
| 类名 / 结构体名 | `PascalCase` | `MavLinkNode`, `PluginContext` |
|
||||
| 函数 / 方法 | `camelCase` | `setPortName()`, `onDataReceived()` |
|
||||
| 成员变量 | `m_camelCase` | `m_portName`, `m_isConnected` |
|
||||
| 静态常量 / 枚举值 | `kPascalCase` 或 `UPPER_SNAKE` | `kDefaultBaud` 或 `DEFAULT_TIMEOUT` |
|
||||
| 局部变量 | `camelCase` | `portName`, `resultCount` |
|
||||
| 全局变量 | `g_camelCase` | `g_appConfig` |
|
||||
| 命名空间 | `snake_case` | `mapcontrol`, `plugin_utils` |
|
||||
| 宏 | `UPPER_SNAKE` | `MAVLINK_START_UART_PORT` |
|
||||
| 信号 (signal) | camelCase(过去式/完成态) | `dataReceived`, `connectionStateChanged` |
|
||||
| 槽 (slot) | `on` + 过去式 | `onDataReceived`, `onStartClicked` |
|
||||
| 枚举类型 | `PascalCase` | `ConnectionState` |
|
||||
| 头文件守卫宏 | `FILENAME_H` | `MAVLINKSERVICE_H` |
|
||||
|
||||
### 3.2 命名细则
|
||||
|
||||
**类名**:
|
||||
```cpp
|
||||
class MavLinkNode : public QObject { }; // 正确 - PascalCase
|
||||
class mavlink_node : public QObject { }; // 错误 - snake_case
|
||||
```
|
||||
|
||||
**成员变量**:
|
||||
```cpp
|
||||
private:
|
||||
QString m_portName; // 正确 - m_camelCase
|
||||
bool m_isConnected = false; // 正确
|
||||
QString portName; // 错误 - 缺少 m_ 前缀
|
||||
```
|
||||
|
||||
**信号 / 槽**:
|
||||
```cpp
|
||||
signals:
|
||||
void dataReceived(const QByteArray &data); // 正确 - 过去式
|
||||
void connectionStateChanged(bool connected); // 正确 - 过去式
|
||||
void signal_heartbeat(); // 弃用 - 旧 MavLinkNode 风格,新代码禁止
|
||||
|
||||
public slots:
|
||||
void onDataReceived(const QByteArray &data); // 正确
|
||||
void onStartClicked(); // 正确
|
||||
void slot_data_received(); // 错误 - 弃用前缀
|
||||
```
|
||||
|
||||
**枚举**:
|
||||
```cpp
|
||||
enum class ConnectionState { // 正确 - enum class
|
||||
Disconnected,
|
||||
Connecting,
|
||||
Connected,
|
||||
Error
|
||||
};
|
||||
|
||||
enum ErrorCode { // 旧风格(已有代码兼容)
|
||||
ERR_NONE = 0,
|
||||
ERR_TIMEOUT,
|
||||
ERR_PARSE
|
||||
};
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. 格式规范
|
||||
|
||||
### 4.1 格式化工具
|
||||
|
||||
项目根目录有 `.clang-format`,**每次编辑后必须运行**:
|
||||
|
||||
```bash
|
||||
clang-format -style=file -i <修改的文件>
|
||||
```
|
||||
|
||||
### 4.2 核心格式规则
|
||||
|
||||
从 `.clang-format` 提取:
|
||||
|
||||
| 规则 | 值 |
|
||||
|------|-----|
|
||||
| 基础风格 | LLVM |
|
||||
| 缩进宽度 | 4 空格 |
|
||||
| Tab | 禁用(一律空格) |
|
||||
| 行宽上限 | 120 字符 |
|
||||
| 大括号风格 | **Allman**(大括号独立一行) |
|
||||
| 指针/引用对齐 | **靠左** (`int *p`, `QString &s`) |
|
||||
| 访问修饰符缩进 | -4(`public:` / `private:` 向外凸出) |
|
||||
| include 排序 | **不自动排序**(手动管理) |
|
||||
| 最大连续空行 | 1 行 |
|
||||
| 短函数单行 | 允许(如空构造/析构) |
|
||||
|
||||
### 4.3 Allman 大括号示范
|
||||
|
||||
```cpp
|
||||
void MyClass::doWork()
|
||||
{
|
||||
if (condition)
|
||||
{
|
||||
// ...
|
||||
}
|
||||
else
|
||||
{
|
||||
// ...
|
||||
}
|
||||
}
|
||||
|
||||
class MyClass : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit MyClass(QObject *parent = nullptr);
|
||||
~MyClass() override = default;
|
||||
|
||||
signals:
|
||||
void finished();
|
||||
|
||||
private:
|
||||
int m_count = 0;
|
||||
};
|
||||
```
|
||||
|
||||
### 4.4 空行与空格
|
||||
|
||||
- 类内各组之间空一行:`public:` / `signals:` / `public slots:` / `private:` 之间
|
||||
- 函数体之间空一行
|
||||
- 逻辑相关代码块可以内部不空行,不同逻辑块之间空一行
|
||||
- `if` / `for` / `while` 关键字后跟一个空格再跟 `(`
|
||||
|
||||
---
|
||||
|
||||
## 5. 注释规范
|
||||
|
||||
### 5.1 注释语言
|
||||
|
||||
- 代码注释使用**中文**
|
||||
- 面向用户的字符串使用 `tr()` 包裹中文
|
||||
|
||||
### 5.2 注释格式
|
||||
|
||||
**类/结构体注释** — 使用 `/* */` 或 `//` 块注释(重要类使用多行):
|
||||
|
||||
```cpp
|
||||
// ============================================================
|
||||
// AppController — 集中管理所有模块间的信号路由
|
||||
// ============================================================
|
||||
class AppController : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
// ...
|
||||
};
|
||||
```
|
||||
|
||||
**函数注释** — 对公开接口和复杂逻辑的函数使用 Doxygen 风格:
|
||||
|
||||
```cpp
|
||||
/**
|
||||
* @brief 将伺服器 PWM 值转换为角度
|
||||
* @param pwm 输入的 PWM 值
|
||||
* @param offset 修正偏移
|
||||
* @return 转换后的角度值(度)
|
||||
*/
|
||||
double pwmToDeg(int pwm, double offset);
|
||||
```
|
||||
|
||||
**内联注释** — 对关键算法或非直观逻辑使用行尾注释:
|
||||
|
||||
```cpp
|
||||
painter.translate(width() * H_pos / 100, height() * V_pos / 100); /* 坐标变换为窗体中心 */
|
||||
int side = qMin(width(), height()); /* 决定了模块只能是方形 */
|
||||
```
|
||||
|
||||
### 5.3 注释禁止事项
|
||||
|
||||
- **禁止** 保留被注释掉的"死代码"(`// old code ...`)。提交前必须删除
|
||||
- **禁止** 中文内容出现乱码(确保文件保存为 UTF-8 编码)
|
||||
- **禁止** 注释写"废话"(如 `// i 自增 1` 对 `i++` 的注释)
|
||||
|
||||
---
|
||||
|
||||
## 6. 头文件规范
|
||||
|
||||
### 6.1 头文件守卫
|
||||
|
||||
**统一使用 `#ifndef` / `#define` / `#endif`**,宏名使用 `文件名大写_H` 格式,结尾带宏名注释:
|
||||
|
||||
```cpp
|
||||
#ifndef PLUGINCONTEXT_H
|
||||
#define PLUGINCONTEXT_H
|
||||
|
||||
// ... 头文件内容 ...
|
||||
|
||||
#endif // PLUGINCONTEXT_H
|
||||
```
|
||||
|
||||
**禁止使用 `#pragma once`**(与项目现有代码保持一致)。
|
||||
|
||||
### 6.2 前向声明
|
||||
|
||||
在 `.h` 中,如果只需要类型的指针或引用,优先使用前向声明而非 include:
|
||||
|
||||
```cpp
|
||||
// MyPlugin.h
|
||||
|
||||
class QAction;
|
||||
class QWidget;
|
||||
namespace Ui { class MyPlugin; }
|
||||
namespace mapcontrol { class OPMapWidget; }
|
||||
class Config;
|
||||
```
|
||||
|
||||
### 6.3 头文件内容顺序
|
||||
|
||||
```cpp
|
||||
#ifndef MYCLASS_H
|
||||
#define MYCLASS_H
|
||||
|
||||
// 1. 必要的 Qt 头文件
|
||||
#include <QObject>
|
||||
|
||||
// 2. 前向声明
|
||||
class Config;
|
||||
namespace Ui { class MyClass; }
|
||||
|
||||
// 3. 类定义
|
||||
class MyClass : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
// 构造/析构
|
||||
// 公开方法
|
||||
signals:
|
||||
// 信号
|
||||
public slots:
|
||||
// 公开槽
|
||||
protected:
|
||||
// 保护成员
|
||||
private:
|
||||
// 私有成员
|
||||
};
|
||||
|
||||
#endif // MYCLASS_H
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. 类设计规范
|
||||
|
||||
### 7.1 类成员可见性
|
||||
|
||||
**从上到下**的顺序:
|
||||
|
||||
```
|
||||
public: 构造/析构 → 公开方法
|
||||
signals: Qt 信号
|
||||
public slots: Qt 公开槽
|
||||
protected: 保护成员(含虚函数)
|
||||
private: 私有成员变量和方法
|
||||
```
|
||||
|
||||
### 7.2 构造与析构
|
||||
|
||||
**使用 `explicit` 修饰单参数构造函数**:
|
||||
|
||||
```cpp
|
||||
class PluginContext : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit PluginContext(const QString &pluginId, QObject *parent = nullptr);
|
||||
~PluginContext() override = default;
|
||||
};
|
||||
```
|
||||
|
||||
- QObject 子类使用 `QObject *parent` 作为最后一个参数,利用 Qt 父子内存管理
|
||||
- 成员变量**就地初始化**(C++11 语法):
|
||||
|
||||
```cpp
|
||||
private:
|
||||
QString m_pluginId; // 不需要就地初始化(QString 默认已空)
|
||||
ServiceRegistry *m_serviceRegistry = nullptr;
|
||||
MavLinkService *m_mavlinkService = nullptr;
|
||||
bool m_isConnected = false;
|
||||
int m_retryCount = 3;
|
||||
```
|
||||
|
||||
- 析构函数中手动 delete 非 QObject 子类的裸指针,并设为 nullptr
|
||||
- 使用 `Q_UNUSED()` 标记未使用的参数:
|
||||
|
||||
```cpp
|
||||
void MainWindow::resizeEvent(QResizeEvent *event)
|
||||
{
|
||||
Q_UNUSED(event)
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
### 7.3 虚函数重写
|
||||
|
||||
**重写虚函数必须加 `override` 关键字**:
|
||||
|
||||
```cpp
|
||||
class MyPlugin : public IPlugin
|
||||
{
|
||||
public:
|
||||
QString name() const override;
|
||||
bool initialize(PluginContext *context) override;
|
||||
void shutdown() override;
|
||||
|
||||
protected:
|
||||
void paintEvent(QPaintEvent *event) override; // QWidget 虚函数
|
||||
};
|
||||
```
|
||||
|
||||
### 7.4 UI 文件使用
|
||||
|
||||
当类使用 `.ui` 文件时:
|
||||
|
||||
```cpp
|
||||
// StatusUI.h
|
||||
namespace Ui { class StatusUI; }
|
||||
|
||||
class StatusUI : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit StatusUI(QWidget *parent = nullptr);
|
||||
~StatusUI() override;
|
||||
|
||||
private:
|
||||
Ui::StatusUI *ui = nullptr; // 使用 m_ 前缀例外:此处用 ui
|
||||
};
|
||||
|
||||
// StatusUI.cpp
|
||||
#include "StatusUI.h"
|
||||
#include "ui_StatusUI.h"
|
||||
|
||||
StatusUI::StatusUI(QWidget *parent)
|
||||
: QWidget(parent)
|
||||
, ui(new Ui::StatusUI)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
|
||||
QFile file(":/qss/StatusUI.qss");
|
||||
file.open(QFile::ReadOnly);
|
||||
QTextStream filetext(&file);
|
||||
QString stylesheet = filetext.readAll();
|
||||
setStyleSheet(stylesheet);
|
||||
file.close();
|
||||
}
|
||||
|
||||
StatusUI::~StatusUI()
|
||||
{
|
||||
delete ui;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 8. Qt 特定规范
|
||||
|
||||
### 8.1 Signal / Slot 连接方式
|
||||
|
||||
**新代码优先使用函数指针语法**:
|
||||
|
||||
```cpp
|
||||
// 推荐 — 编译期检查
|
||||
connect(m_button, &QPushButton::clicked, this, &MyClass::onButtonClicked);
|
||||
|
||||
// 带 lambda
|
||||
connect(m_timer, &QTimer::timeout, this, [this]() {
|
||||
updateStatus();
|
||||
});
|
||||
|
||||
// 信号到信号
|
||||
connect(m_source, &Source::dataReady, m_target, &Target::onDataReady);
|
||||
```
|
||||
|
||||
**旧式 `SIGNAL()` / `SLOT()` 宏语法仅在维护旧代码时沿用,新代码禁止**:
|
||||
|
||||
```cpp
|
||||
// 弃用 — 仅维护旧代码时保留
|
||||
connect(sender, SIGNAL(valueChanged(int)), receiver, SLOT(onValueChanged(int)));
|
||||
```
|
||||
|
||||
### 8.2 QObject 内存管理
|
||||
|
||||
- QObject 子类通过 `parent` 参数加入 Qt 对象树,由 Qt 自动负责析构
|
||||
- 非 QObject 的成员指针需要在析构函数中手动 delete
|
||||
|
||||
```cpp
|
||||
// QObject 子类 — 不需要手动 delete
|
||||
m_button = new QPushButton(tr("开始"), this); // this 为 parent
|
||||
|
||||
// 非 QObject 裸指针 — 必须在析构函数中 delete
|
||||
MyClass::~MyClass()
|
||||
{
|
||||
delete m_rawData;
|
||||
m_rawData = nullptr;
|
||||
}
|
||||
```
|
||||
|
||||
### 8.3 QString 使用
|
||||
|
||||
- **所有**界面字符串用 `tr()` 包裹:
|
||||
|
||||
```cpp
|
||||
m_label->setText(tr("连接已建立"));
|
||||
m_button->setToolTip(tr("点击以断开连接"));
|
||||
```
|
||||
|
||||
- 数值格式化使用 `QString::number()`:
|
||||
|
||||
```cpp
|
||||
QString value = QString::number(pwm, 'f', 2); // 保留 2 位小数
|
||||
QString hex = QString::number(address, 16).toUpper(); // 十六进制大写
|
||||
```
|
||||
|
||||
- 多段拼接使用 `arg()` 或 `QStringLiteral`:
|
||||
|
||||
```cpp
|
||||
QString msg = tr("端口: %1, 波特率: %2").arg(portName).arg(baudRate);
|
||||
```
|
||||
|
||||
- **禁止** 在界面代码中使用 `std::string`,除非是纯算法库内部
|
||||
|
||||
### 8.4 自绘组件(无 .ui 文件)
|
||||
|
||||
对于 Cockpit 等纯自绘组件,遵循以下模式:
|
||||
|
||||
```cpp
|
||||
// Cockpit.h
|
||||
class Cockpit : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
protected:
|
||||
void paintEvent(QPaintEvent *event) override;
|
||||
void resizeEvent(QResizeEvent *event) override;
|
||||
|
||||
private:
|
||||
QColor m_groundColor = QColor(100, 100, 100);
|
||||
qreal m_roll = 0.0;
|
||||
};
|
||||
|
||||
// Cockpit.cpp
|
||||
void Cockpit::paintEvent(QPaintEvent *event)
|
||||
{
|
||||
Q_UNUSED(event)
|
||||
QPainter painter(this);
|
||||
painter.setRenderHint(QPainter::Antialiasing, true);
|
||||
// ... 绘制逻辑
|
||||
}
|
||||
```
|
||||
|
||||
### 8.5 qmake `.pro` / `.pri` 兼容
|
||||
|
||||
当前项目同时存在 CMake 和 qmake 构建系统。如果修改涉及 `.pro` 文件:
|
||||
- 保持 `.pri` 文件与 `CMakeLists.txt` 中的源文件列表同步
|
||||
- 新增文件需同时添加到两个构建文件中
|
||||
|
||||
---
|
||||
|
||||
## 9. 错误处理与日志
|
||||
|
||||
### 9.1 日志等级与使用
|
||||
|
||||
```cpp
|
||||
qDebug() << "调试信息"; // 开发期间输出,发布前删除或注释
|
||||
qInfo() << "关键操作日志"; // 应保留,用于运行时追踪
|
||||
qWarning() << "非致命异常"; // 如文件打开失败、配置缺失等
|
||||
qCritical() << "严重错误"; // 可能导致功能不可用的错误
|
||||
```
|
||||
|
||||
### 9.2 错误处理模式
|
||||
|
||||
**不抛出异常**(项目不使用 try/catch/throw):
|
||||
|
||||
```cpp
|
||||
// 检查条件,提前返回
|
||||
QFile file(path);
|
||||
if (!file.open(QIODevice::ReadOnly))
|
||||
{
|
||||
qWarning() << "无法打开文件:" << path << file.errorString();
|
||||
return false;
|
||||
}
|
||||
|
||||
// 检查空指针
|
||||
if (!config)
|
||||
{
|
||||
qWarning() << "Config 未初始化";
|
||||
return;
|
||||
}
|
||||
```
|
||||
|
||||
```cpp
|
||||
// JSON 解析错误处理
|
||||
QJsonParseError error;
|
||||
QJsonDocument doc = QJsonDocument::fromJson(data, &error);
|
||||
if (error.error != QJsonParseError::NoError)
|
||||
{
|
||||
qWarning() << "JSON 解析失败:" << error.errorString();
|
||||
return;
|
||||
}
|
||||
```
|
||||
|
||||
### 9.3 断言
|
||||
|
||||
- 对不应发生的逻辑错误使用 `Q_ASSERT`(Debug 模式生效,Release 模式移除)
|
||||
- 对 Release 也需要检查的条件使用普通 if 判断
|
||||
|
||||
```cpp
|
||||
Q_ASSERT(plugin != nullptr);
|
||||
Q_ASSERT(!m_pluginId.isEmpty());
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 10. 插件开发规范
|
||||
|
||||
### 10.1 插件接口
|
||||
|
||||
当前项目同时支持两版插件接口:
|
||||
|
||||
- **IPlugin v2** ([IPlugin.h](file:///d:/20_AI/gcs_nf/Plugins/IPlugin.h)) — 所有现有插件使用
|
||||
- **IPlugin v3** ([IPlugin_v3.h](file:///d:/20_AI/gcs_nf/Plugins/IPlugin_v3.h)) — 基于订阅模式,新功能开发优先使用
|
||||
|
||||
### 10.2 新插件模板
|
||||
|
||||
```cpp
|
||||
// MyPlugin.h
|
||||
#ifndef MYPLUGIN_H
|
||||
#define MYPLUGIN_H
|
||||
|
||||
#include "IPlugin_v3.h"
|
||||
|
||||
class MyPlugin : public IPlugin_v3
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit MyPlugin(QObject *parent = nullptr);
|
||||
~MyPlugin() override = default;
|
||||
|
||||
// IPlugin 接口
|
||||
QString name() const override;
|
||||
QString version() const override;
|
||||
bool initialize(PluginContext *context) override;
|
||||
void shutdown() override;
|
||||
|
||||
private:
|
||||
PluginContext *m_context = nullptr;
|
||||
};
|
||||
|
||||
#endif // MYPLUGIN_H
|
||||
```
|
||||
|
||||
### 10.3 plugin.json 清单
|
||||
|
||||
每个插件目录必须有 `plugin.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "MyPlugin",
|
||||
"version": "1.0.0",
|
||||
"description": "插件描述",
|
||||
"activationEvents": ["onStartup"],
|
||||
"contributes": {}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 11. CMake 构建规范
|
||||
|
||||
### 11.1 CMake 文件位置
|
||||
|
||||
- 顶层 `CMakeLists.txt` — 全局设置 + `add_subdirectory`
|
||||
- 每个模块目录下的 `CMakeLists.txt` — 模块自身的构建规则
|
||||
|
||||
### 11.2 新增文件时的操作
|
||||
|
||||
1. 将 `.h` / `.cpp` 文件加入对应模块的 `CMakeLists.txt` 中的 `add_library` 或 `add_executable` 源文件列表
|
||||
2. 如模块有 `.pri` 文件,同步更新
|
||||
3. 新增 `Q_OBJECT` 类时,无需额外操作(`AUTOMOC` 已开启)
|
||||
|
||||
---
|
||||
|
||||
## 12. AI 编辑指南
|
||||
|
||||
> 本章专门面向 AI 辅助编码场景,指导 AI 如何在此项目中安全地编辑代码。
|
||||
|
||||
### 12.1 编辑前的必读步骤
|
||||
|
||||
1. **读取目标文件** — 使用 Read 工具读取待编辑文件的完整内容
|
||||
2. **理解上下文** — 检查该文件用到的所有类型、成员、信号槽
|
||||
3. **确认风格** — 观察现有代码的命名、缩进、注释风格,严格模仿
|
||||
4. **检查依赖** — 查看 `#include` 列表,确保新增代码的依赖已包含
|
||||
|
||||
### 12.2 编辑操作规范
|
||||
|
||||
| 操作 | 规范 |
|
||||
|------|------|
|
||||
| 新增类 | 创建一个 `.h` + `.cpp`,遵循类设计规范(第 7 章) |
|
||||
| 新增成员函数 | 在 `.h` 中声明,在 `.cpp` 中实现;检查是否需要 `override` |
|
||||
| 新增成员变量 | 使用 `m_camelCase` 命名,放在 `private:` 区,作为最后手段才放 `public:` |
|
||||
| 新增信号 | 放在 `signals:` 区,使用过去式命名 |
|
||||
| 新增槽 | 放在 `public slots:` 或 `private slots:`,使用 `onXxx` 命名 |
|
||||
| 修改函数体 | 仅修改目标函数,不随意重构无关代码 |
|
||||
| 添加 include | 插入到 include 分组中的正确位置 |
|
||||
| 删除代码 | 彻底删除包括相关注释和空行,不留"死代码" |
|
||||
|
||||
### 12.3 编辑后的必做检查
|
||||
|
||||
- [ ] 新增/修改的类是否添加了 `Q_OBJECT` 宏
|
||||
- [ ] 是否使用了 `tr()` 包裹所有用户可见字符串
|
||||
- [ ] 重写的虚函数是否加了 `override`
|
||||
- [ ] 单参数构造函数是否加了 `explicit`
|
||||
- [ ] 是否修复了 `#include` 顺序
|
||||
- [ ] 是否有匹配的 `#ifndef` / `#define` / `#endif` 守卫(新文件)
|
||||
- [ ] 修改后是否运行了 `clang-format -style=file -i <文件>`
|
||||
- [ ] 是否同步更新了 `CMakeLists.txt`
|
||||
|
||||
### 12.4 常见编辑场景示范
|
||||
|
||||
#### 场景 A:给现有类添加一个槽函数
|
||||
|
||||
```
|
||||
步骤:
|
||||
1. 读取 MyClass.h,在 public slots: 或 private slots: 区添加声明
|
||||
2. 读取 MyClass.cpp,在文件末尾添加函数实现
|
||||
3. 使用 clang-format 格式化
|
||||
```
|
||||
|
||||
#### 场景 B:创建一个新的简单对话框
|
||||
|
||||
```
|
||||
步骤:
|
||||
1. 在某插件目录下创建 MyDialog.h / MyDialog.cpp / MyDialog.ui
|
||||
2. MyDialog.h:按照 7.4 节 UI 文件使用规范编写
|
||||
3. MyDialog.cpp:实现构造/析构函数
|
||||
4. 同步更新 CMakeLists.txt
|
||||
5. 使用 clang-format 格式化所有新文件
|
||||
```
|
||||
|
||||
#### 场景 C:在现有文件中添加一个信号
|
||||
|
||||
```
|
||||
步骤:
|
||||
1. 读取头文件,在 signals: 区添加信号声明
|
||||
2. 确认信号使用过去式名称(如 dataReady, finished)
|
||||
3. 查找所有用到信号的地方(通过 grep),确认连接方式
|
||||
```
|
||||
|
||||
### 12.5 代码样式速查卡片
|
||||
|
||||
```
|
||||
命名: 类 PascalCase | 成员 m_camelCase | 信号过去式 | 槽 onXxx
|
||||
格式: Allman 大括号 | 4 空格缩进 | 120 列宽 | 指针靠左 int *p
|
||||
头文件: #ifndef_H 守卫 | #include <> 系统头 | #include "" 项目头
|
||||
类结构: public → signals → public slots → protected → private
|
||||
字符串: tr("中文字符串") | QString::number(x, 'f', 2)
|
||||
调试: qDebug / qInfo / qWarning | 不用 try/catch
|
||||
构造: explicit 单参构造 | QObject *parent = nullptr 最后 | 成员就地初始化
|
||||
重写: 所有虚函数重写加 override
|
||||
插件: 实现 IPlugin_v3 | 提供 plugin.json | 在 CMakeLists.txt 注册
|
||||
编辑后: 运行 clang-format | 检查 tr() | 检查 CMakeLists.txt 同步
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 附录 A:与旧代码风格兼容说明
|
||||
|
||||
项目中有大量 2020-2022 年编写的旧代码,其风格与本文规范有所不同。AI 在修改旧文件时:
|
||||
|
||||
| 旧风格 | 新风格(本文规范) | 处理方式 |
|
||||
|--------|-------------------|---------|
|
||||
| `signal_heartbeat()` | `heartbeatReceived()` | **仅新增信号**用新风格;已有旧信号保持不变 |
|
||||
| `SIGNAL(...)` / `SLOT(...)` | `&Class::method` | **新增 connect** 用新语法;已有旧连接保持不变 |
|
||||
| 无 `override` | 添加 `override` | 修改到旧函数时可顺手补上 |
|
||||
| 无 `explicit` | 添加 `explicit` | 修改到旧类时可顺手补上 |
|
||||
| `#include "QObject"` | `#include <QObject>` | 修改到 include 时可顺手修正 |
|
||||
| public 成员变量 | private + m_ 前缀 | **仅新增成员**用新风格;已有 public 成员保持不变 |
|
||||
| 中文注释乱码 | UTF-8 中文注释 | 修改到乱码注释附近时修正 |
|
||||
Reference in New Issue
Block a user