docs(radar): DS优先(dd_radar_3d)设计修订 + 实现计划(过3轮架构评审)

- spec 改 DS 优先:导入→登记 dd_radar_3d DS(非游离 dd_voxel vol-N)→按 DS 渲染。
  ddCode 对齐数据字典 DD0623(dd_radar_3d 体/dd_trajectory_data 轨迹/dd_voxel 仅反演)。
  关键发现:运行期体渲染只认 volumes_ 成员、不看 ddCode → 接入面极小。
- 实现计划 9 task(TDD):reader→共享 assembleRadarVolume(消DRY)→bridge→
  createRadarVolumeGrid→registerRadarDataset+loadVolume 懒加载后台建体→导入入口+
  dd_radar_3d 切片/色阶/详情 gate→双数据集互证。
- 3 轮 opus 架构评审已落修:HIGH-1 填体抽共享helper、HIGH-2 明星路in-app属首次、
  CRITICAL CategorySection切片gate、HIGH eager建体冻UI/MEDIUM spinner→改懒加载后台线程。
This commit is contained in:
gaozheng 2026-06-29 11:53:33 +08:00
parent 4da11f6185
commit 18b78a85d3
2 changed files with 1082 additions and 15 deletions

File diff suppressed because it is too large Load Diff

View File

@ -39,29 +39,44 @@
4. **数据类型**int16 小端(`.rd3`/`bits=16`) / int32 小端(`.rd7`/`bits=32`)。`-32768` 是直达波饱和真实值,**非空值哨兵**。
5. **`BITS` 公式 bug**:客户文档 §3.3 `BITS = 文件大小/LAST_TRACE/NUMBER_OF_CH×8` **漏了 SAMPLES 维、量纲错**
正确:`bytes = 文件大小/(LAST_TRACE×SAMPLES)`,并与扩展名交叉校验。**须同步后端**(见 `tools/radar_convert/README.md`)。
6. **ddCode数据字典 DD0623 权威)**:三维雷达体 DS = **`dd_radar_3d`**(本次新增,形态=三维插值模型);
轨迹 DS = **`dd_trajectory_data`**(保留,复用现成 `TrajectoryStrategy`/`TrajectoryMapView`,零改动);
`dd_voxel` **仅物探反演体(电阻率/速度)、不含雷达**`dd_slice` 切片共用;`dd_radar_rtk_trajectory` 已删除。
**雷达体不复用 `dd_voxel`。**
---
## 3. 架构论点:只换最内层 reader下游 100% 复用
## 3. 架构论点DS 优先 + 只换最内层 reader
### 3a. 数据层:只换最内层 reader下游建体链复用
现有真实雷达体链路是分层的,**只有最内层 reader 绑定厂商格式**
```
Api3dRepository::createGprVolume [api/Api3dRepository.cpp:128] 注册 dd_voxel/灰度色阶/预填 cachedGrid
└ data::createGprVolumeGrid [data/GprVolumeRepository.cpp:38]
├ io::gpr::buildLineVolumeFromGpr3dv ← 仅此层绑定厂商 .iprb/.ord读 Impulse 原始)
└ data::builtI16ToVolumeGrid [data/GprVolumeRepository.cpp:11] int16→float 稠密体
→ 勾选 → VtkSceneController.loadVolume → VtkSceneView.addVolume → render::buildVoxel(GPU RayCast)
→ SliceTool / InteractionManager / AnomalyDrawTool (切片 B/C-scan + 点线面异常,全现成)
data::createRadarVolumeGrid [data/GprVolumeRepository 新]
├ io::gpr::buildLineVolumeFromNormalized ← 新写:仅此层认规范化 .head/.data
└ data::builtI16ToVolumeGrid [data/GprVolumeRepository.cpp:11] int16→float 稠密体
```
(范本:现有 `createGprVolume`[`Api3dRepository.cpp:128`]→`createGprVolumeGrid`→`buildLineVolumeFromGpr3dv`(Impulse .iprb)。
我们换最内层 reader复用 `builtI16ToVolumeGrid` 及其下游。)**产 `core::BuiltI16`(轴 X=道/Y=通道/Z=采样)即可**
这正是 `GprVolumeRepository.cpp:14` 注释写明的"数据层方案 A"。
**新增一个产 `core::BuiltI16`(同轴映射 X=道/Y=通道/Z=采样)的规范化 reader**,则 `builtI16ToVolumeGrid`
往下的渲染/切片/异常链**零改动**。这正是 `GprVolumeRepository.cpp:14` 注释写明的"数据层方案 A"。
### 3b. DS 优先:运行期路由按 `volumes_` 成员,不看 ddCode关键发现已验真代码
桌面端体渲染的运行期分流**只认 `Api3dRepository::volumes_` map 是否含此 dsId**,与 ddCode/"vol-" 前缀无关:
- `isVolumeDataset(dsId)` = `volumes_.count(dsId)``Api3dRepository.cpp:105`
- `VtkSceneController::addDatasetAsync`(:182) 按 `isVolumeDataset` 分流体素/帘面;
- `loadVolume(dsId)`(:341) 唯一查找键 = `volumes_` 的 dsId命中 `cachedGrid` 直渲(:347)。
**所以 DS 优先落地 = 把一条"真实 radar DS id"为 key 的 `StoredVolume` 写进 `volumes_`**(携 `ddCode=dd_radar_3d` + `lineDir/prefix`
`isVolumeDataset/addDatasetAsync/loadVolume` **零改动**自动按体渲染。三维分析栏 voxel 段内容来自 `volumeRows()`
`section("voxel")->setDatasets``main.cpp:602`**绕过 `splitByCategory`**),故 `dimensionOf`/`CategoryConfig` **也无需改**——
唯一要改 `volumeRows()`(:170) 把硬编码 `dd_voxel` 改成输出该 DS 的真实 ddCode。
### 完全复用、不碰的部分
`render::buildVoxel`GPU 体绘制)、三级树 voxel 段(`CategoryConfig.hpp:23` `dd_voxel`)、勾选→`loadVolume`→`addVolume`、
`render::buildVoxel`GPU 体绘制)、勾选→`addDatasetAsync`→`loadVolume`→`addVolume` 路由、`isVolumeDataset`、
切片(`SliceTool` updown 深度 C-scan / leftright·frontback B-scan + `InteractionManager`)、
异常(`AnomalyDrawTool` 点/线/面 → `dd_anomaly` 挂体)、跨视图色阶联动、`builtI16ToVolumeGrid` 适配器。
异常(`AnomalyDrawTool` 点/线/面 → `dd_anomaly` 挂体)、跨视图色阶联动、`builtI16ToVolumeGrid` 适配器、
轨迹 `dd_trajectory_data` 详情视图。
---
@ -94,8 +109,8 @@ convert(lineDir, prefix, outDir) -> {head, data, cor} # 厂商原始 → 规
| ① | `src/io/gpr/NormalizedRadarReader.{hpp,cpp}` **新** | 纯函数:`parseHead(.head)→RadarHeader``readDataCube(.data,header)→cube`(按 `BITS`+`ENDIAN_TYPE` 解二进制,`reshape(K,M,N)` 主序已定);`parseCor(.cor)→vector<TracePos>`(先解析,世界配准后置) |
| ② | `src/io/gpr/NormalizedRadarVolumeBridge.{hpp,cpp}` **新** | `buildLineVolumeFromNormalized(lineDir,prefix,metrics,coarse,targetDy)→core::BuiltI16`。⚠️ **DRY**`Gpr3dvVolumeBridge.cpp:133-197` 的值域扫描/`Quant`构造/逐体素填值/spacing 当前内联且耦合 Impulse 模型——动工时**先把这段抽成共享 helper**(签名接受抽象立方体访问器 `(c,t,s)→short` + 通道偏移/几何),两个 bridge 同调;`planChannelInterpolation`/`depthOfSample` 已是共享纯函数。**不要复制填体循环**(否则两份独立漂移) |
| ③ | `src/data/GprVolumeRepository.{hpp,cpp}` 改 | 加 `createRadarVolumeGrid(lineDir,prefix,coarse,targetDy)`(调②,复用 `builtI16ToVolumeGrid`)。**不动**现有 `createGprVolumeGrid` |
| ④ | `src/data/api/Api3dRepository.{cpp,hpp}` 改 | `createRadarVolume(...)`(≈`createGprVolume` 复制换调③),仍注册 `dd_voxel` |
| ⑤ | `src/app/main.cpp` 改 | 本地导入入口「导入三维雷达测线目录」:选目录+前缀 → `createRadarVolume``refreshAnalysis` → 自动勾选渲染(后端就绪后换按 DS 下载) |
| ④ | `src/data/api/Api3dRepository.{cpp,hpp}` 改 | **DS 优先 + 懒加载后台建体**3 处):(a) `StoredVolume`(:139) 加 `lineDir/linePrefix/coarse/ddCode/structParentId` 字段(`ddCode` 默认 `"dd_voxel"`,存量行为不变);(b) 新 `registerRadarDataset(lineDir,prefix,name,structParentId,coarse)`——**只存元数据、不建体**,以 `ddCode="dd_radar_3d"` 写进 `volumes_`id=`"radar-"+(++counter)`,瞬时返回;(c) `loadVolume`(:347 后) 加 radar 分支:未命中 cachedGrid 且有 `linePrefix`**后台线程** `createRadarVolumeGrid`(仿 `finalizeVolume:268-335``std::thread`+`QMetaObject::invokeMethod(qApp,...)` 回主线程;无 `QCoreApplication` 时退化同步,可单测)→ 建体 + 灰度色阶 + 缓存 + async `onOk`(d) `volumeRows()`(:170) 输出 `sv.ddCode` + `sv.structParentId`。运行期 `isVolumeDataset/addDatasetAsync``volumes_` 成员 → **零改动**。**懒加载+后台建体同时消除 UI 冻结(评审 HIGH)与 spinner 永久转圈(评审 MEDIUM)**。⚠️ `dimensionOf`/`CategoryConfig`/`splitByCategory` **不需改**voxel 段走 `volumeRows` 注入、绕过分类,见 §3b |
| ⑤ | `src/app/main.cpp` + `src/app/panels/columns/CategorySection.cpp` 改 | (1) 导入入口「三维雷达→导入测线目录」:选目录+前缀 → `registerRadarDataset``refreshAnalysis``setItemChecked(true)`+`setItemBusy(true)`+`scrollItemToTop`(同 `generateVolume:978-980`渲染异步、spinner 由 `datasetRendered` 撤)。(2) **放开两处 ddCode gate**给 `dd_radar_3d``main.cpp:1011` `detailRequested`→体属性;**`CategorySection.cpp:397`** 列表右键 `if (ddCode=="dd_voxel")` 块(「生成切片」+「色阶」**同在此一个块内****只改 :397 这一处**即同时放开二者,无独立 :403 gate——**不改则 radar 体无切片入口、P0 验收②不可达 — 评审 CRITICAL**|
| ⑥ | 测试 | `tests/io/gpr/test_normalized_radar_reader.cpp`(喂 `tests/data/radar/` 截断样例,断言维度/字节序/通道插值/主序) |
### 几何映射(关键算法,纯函数易测)
@ -111,8 +126,8 @@ convert(lineDir, prefix, outDir) -> {head, data, cor} # 厂商原始 → 规
## 6. 分期
- **P0本期**:①–⑥,单线规范化体打通。**验收**:导入 `samples/radar/malamira_南同大道`(如 000 线)→
树出现 `dd_voxel` → 勾选渲染 → 切 updown/leftright/frontback → 画点/线/面异常并挂体下。
- **P1**`.cor` 逐道 GPS 世界配准 + 高程、`.index` 打标、多线合一场景。
三维体段出现一条 **`dd_radar_3d`** DS → 勾选 → `loadVolume` 懒建体渲染 → 切 updown/leftright/frontback → 画点/线/面异常并挂体下。
- **P1**`.cor` 逐道 GPS 世界配准 + 高程、`.index` 打标、多线合一场景、轨迹 DS(`dd_trajectory_data`) 一并登记、DS 进左侧数据集树(挂 TM)
- **P2**:大体量 → 把 `gpr_poc``ChunkedVolumeStore`+`*VolumeSource` 核外/LOD 接进 app碰 controller/view单独立项
---