更新:1、新增数据记录功能,将过程数据录入数据库;

2、增加输入点迹数据、凝聚后点迹数据记录;

Signed-off-by: waiwaylee <waiwaylee@foxmail.com>
This commit is contained in:
2026-09-30 09:02:28 +08:00
parent ff9b111d34
commit a4f32e4036
111 changed files with 321343 additions and 262 deletions
+155
View File
@@ -0,0 +1,155 @@
# 数据记录调试功能(rdp_record)
> 面向调试的数据记录框架:把内部结构体按「一个记录实体一张表」写入 SQLite,供事后查询分析。
> 本功能**默认只在 Debug 构建中启用**,Release/交付构建完全不编译(无依赖、无代码、无性能损失)。
> 相关代码:`data_process_class_dll/rdp_record.h`、`rdp_record_entity.h`、`rdp_record.cpp`;
> 第三方依赖来源见 `third_party/ormpp/UPSTREAM.md`。
---
## 1. 目的与范围
- **目的**:调试期把关键中间数据落库(点迹、航迹、关联结果……),用于事后 SQL 查询与对比分析;
- **框架性设计**:本功能**不预设记录哪些数据**。新增一类记录数据的成本固定为三步:
加记录实体结构体 → 注册 → 在需要的位置写一行记录点宏(配方见第 5 节);
- **非目标**:不做异步写入、不做查询/回放工具、不做数据裁剪与轮转、不追求多线程写入性能。
## 2. 开关与构建
| 项目 | 说明 |
|---|---|
| 开关 | CMake 缓存变量 `RDP_DATA_RECORD`,取值 `AUTO`(默认)/ `ON` / `OFF` |
| AUTO 语义 | `Debug` 构建启用、`Release`(及其它)构建关闭 |
| 生效方式 | 启用时构建系统定义 `RDP_RECORD_ENABLE` 并额外编译静态库 `rdp_record`(`rdp_record.cpp` + 内置 `sqlite3.c`)并链接进 DLL |
| 关闭时的保证 | 记录模块源码**不参与编译**;`RDP_RECORD(...)` / `RDP_RECORD_BATCH(...)` 展开为 `((void)0)`(实参不求值,也不会产生「变量已初始化但未使用」告警);DLL 不新增依赖与导出符号 |
```bat
:: 交付/Release:关闭(AUTO 的默认结果,也可显式 OFF)
.vscode\scripts\configure.bat build/X256_PS PHASE_SCANNING Release OFF
.vscode\scripts\build.bat build/X256_PS
:: Debug:启用(AUTO 的默认结果,也可显式 ON)
.vscode\scripts\configure.bat build/X256_PS PHASE_SCANNING Debug ON
.vscode\scripts\build.bat build/X256_PS
:: 直接用 cmake(需先进入 MSVC 2022 x64 环境)
cmake -S . -B build/debug_rec -G "NMake Makefiles" -DCMAKE_BUILD_TYPE=Debug ^
-DRADAR_SCAN_MODE=PHASE_SCANNING -DRDP_DATA_RECORD=ON
cmake --build build/debug_rec
```
配置阶段会打印开关状态:`数据记录调试功能:启用(C++20 静态库 rdp_record + 内置 SQLite)` 或 `...:关闭(不编译记录模块,RDP_RECORD 为空操作)`。
**语言标准与依赖**:记录模块以 **C++20** 独立编译(ormpp 要求 `CMAKE_CXX_STANDARD 20`),并编译内置的 SQLite amalgamation(因此启用时需要 C 编译器,随 VS 2022 工具集提供);**雷达处理代码与内置 Eigen 仍为 C++17**,二者通过 `rdp_record.h` 的「仅声明 + 显式实例化」跨标准边界:调用方(C++17)只把记录实体按 `const` 引用递给 `rdp_record::Write<T>()`,实现在 `rdp_record.cpp`(C++20)中显式实例化。
## 3. 运行期行为
| 项目 | 说明 |
|---|---|
| 存放目录 | `RDP_RECORD_DB_DIR`(`parameters.h`,默认 `.\rdp_records\`,相对宿主进程当前工作目录,不存在时自动创建) |
| 文件名 | `rdp_record_YYYYMMDD_HHMMSS.db`(**每次运行一个新文件**,与 `rdp_logs` 的日志命名风格一致) |
| 初始化 | `Data_Process_Factory::GetB()` 内调用 `rdp_record::Init()`(幂等):建目录 → 开库 → 打印记录库路径 |
| 关闭 | `Data_Process_Factory::Destroy()` 内调用 `rdp_record::Shutdown()`;宿主未调用时由静态析构兜底关库 |
| 建表 | 首次写入某记录实体时自动执行 `CREATE TABLE IF NOT EXISTS`(每实体只尝试一次) |
| PRAGMA | `synchronous=OFF`(调试取舍:提交时不再 fsync,崩溃可能丢失尾部数据;如需崩溃安全可改为 `NORMAL` 或自行在 sqlite3 层开启 WAL)。未设置 `journal_mode`——ormpp 的 `execute()` 只接受不返回结果行的语句 |
| 线程模型 | 单连接 + 内部互斥锁串行化写入;宿主多线程调用不会破坏数据,但**不保证高并发写入性能**(调试功能,按要求不做异步) |
| 失败降级 | 目录不可建/库打不开:`Init()` 返回 `false`,只打印一次日志,之后所有写入静默忽略;建表或插入失败:**每个实体只提示一次**,不重试、不抛出、不影响雷达数据处理 |
| 自增主键 | 每个记录实体的首字段固定 `int64_t id`:建表时登记自增主键,插入时自动跳过该列(写入时固定填 `0`,由 SQLite 分配) |
## 4. 记录实体(表)约定
1. 结构体定义在**全局命名空间**(勿放进 `namespace`),推荐以 `RdpRec` 开头;
2. **结构体名即表名**(ormpp 取「短结构体名」),无需任何建表代码;
3. 首字段固定为 `int64_t id`;
4. 字段类型仅限标量,映射关系:
| C++ 类型 | SQLite 类型 | 备注 |
|---|---|---|
| `int` | `INTEGER` | 枚举同理(按整数存) |
| `int64_t` / `long long` | `INTEGER` | 毫秒时间戳用它 |
| `float` | `FLOAT` | |
| `double` | `DOUBLE` | |
| `bool` | `INTEGER` | |
5. **禁止数组字段**(如 `float speed_dim[129]`、`char[N]`):ormpp 对 C 数组按 C 字符串语义(`strlen`)绑定,会读越界或写入错误数据。需要记录大批量数组时,另建「元素展开表」(一行一个元素)或改用 blob 字段;
6. 命名约定:字段名带单位后缀(`_ms` 毫秒、`_m` 米、`_deg` 度、`_mps` 米/秒、`_s` 秒),避免单位歧义;
7. **表结构变更不迁移旧表**:增删字段后不会修改已存在的表结构;由于每次运行生成新的 db 文件,正常调试流程天然规避该问题(若手工复用旧库,需自行建新表或删库)。
## 5. 新增记录点:四步配方
以「记录航迹号 + 高度」为例:
**第 1 步 — 声明实体**(`rdp_record_entity.h`「记录实体定义区」,紧随其后写一行 `RDP_RECORD_DECLARE`):
```cpp
struct RdpRecTrackHeight
{
int64_t id; //自增主键(写入时固定填 0)
int track_index; //航迹号
double height_m; //高度(m)
};
RDP_RECORD_DECLARE(RdpRecTrackHeight);
```
**第 2 步 — 注册实体**(`rdp_record.cpp`「记录实体注册区」,字段顺序必须与结构体声明顺序一致):
```cpp
YLT_REFL(RdpRecTrackHeight, id, track_index, height_m);
RDP_RECORD_DEFINE(RdpRecTrackHeight);
```
**第 3 步 — 埋记录点**(在需要记录的位置):
```cpp
//单条(就地构造;宏为可变参数,允许花括号内有逗号)
RDP_RECORD(RdpRecTrackHeight{0, trust_track[i].Track_Index, trust_track[i].Height});
//批量(需要语句/局部变量时,整体用 #ifdef 包裹,保证交付构建零代码)
#ifdef RDP_RECORD_ENABLE
std::vector<RdpRecTrackHeight> recs;
for (size_t i = 0; i < trust_track.size(); ++i)
{
recs.push_back(RdpRecTrackHeight{0, trust_track[i].Track_Index, trust_track[i].Height});
}
RDP_RECORD_BATCH(recs);
#endif
```
**第 4 步 — 无需建表**:首次写入该实体时自动 `CREATE TABLE IF NOT EXISTS`;不同实体自动落到不同表。
> 检查点:若忘记第 2 步(注册/实例化),链接期会报 **LNK2019**(`extern template` 已阻止隐式实例化),这正是配方的自检点。
## 6. 现有示例记录点
| 记录点 | 位置 | 表 | 说明 |
|---|---|---|---|
| ① TWS 输入点迹 | `data_process.cpp::data_preprocess()`(`point_type == 0` 分支逐点循环内) | `RdpRecPointTws` | 记录 `DataRev` 输入字段(距离 m、方位/俯仰 度、速度 m/s、SNR、RCS、波位/PRF/频点、CPI/GNSS 时间 ms 等 18 个字段) |
| ② 本圈输出航迹 | `data_process.cpp::track_process()`(`model == 1` 或 `model == 3` 处理帧结束后批量写入) | `RdpRecTrackOut` | TWS/TAS **共用一张表**,用 `track_mode` 区分;每行取输出数组的 `[i][0]`(该航迹的主记录);行数超过 `MAX_TRACK_NUM` 时钳位,避免越界读 |
两个示例的字段均为**示例**:按第 5 节增删字段即可,不影响框架。
## 7. 查询与分析
```python
# 取最近一次运行的记录库并查看表结构、行数与抽样数据(Python 自带 sqlite3 即可)
import glob, os, sqlite3
path = max(glob.glob(r'rdp_records\rdp_record_*.db'), key=os.path.getmtime)
con = sqlite3.connect(path)
print(path)
print(con.execute("select name from sqlite_master where type='table'").fetchall())
print(con.execute("pragma table_info(RdpRecPointTws)").fetchall())
print(con.execute("select count(*) from RdpRecPointTws").fetchone())
print(con.execute("select * from RdpRecPointTws order by id desc limit 3").fetchall())
print(con.execute("pragma integrity_check").fetchone())
```
也可用 DB Browser for SQLite 直接打开 db 文件;同一 db 内所有表一一对应各记录实体。
## 8. 注意事项与已知限制
- **交付构建不要开启**:`Release` + `RDP_DATA_RECORD=ON` 仅用于临时排查,会带来 sqlite3 编译与运行时写入开销;正式交付保持 `AUTO`/`OFF`;
- **Release 下记录点不做类型检查**:关闭时宏丢弃实参,因此记录点里的字段拼写/类型错误只在启用记录功能的构建中才会暴露——新增记录点后请至少构建一次开启记录功能的配置;
- `NaN`/`Inf` 会由 SQLite 存为 `NULL`(`sqlite3_bind_double` 行为),分析时需注意;
- 记录库是调试产物:进程异常退出(未调用 `Destroy()`)时,`synchronous=OFF` 下可能丢失最后若干条记录;
- IntelliSense 可能对 `rdp_record.cpp` 按 `c_cpp_properties.json` 的 C++17 提示(该文件实际按 C++20 编译,`build/<...>/compile_commands.json` 中带有 `/std:c++20`),以实际构建结果为准;
- `third_party/ormpp/` 为上游原样内置(ormpp 0.2.0 / iguana 1.0.5 / SQLite 3.49.1,Apache-2.0 与公有领域),升级方式与校验值见该目录 `UPSTREAM.md`。