feat: initial commit - massive-cloud point cloud processing system

This commit is contained in:
2026-05-27 09:51:42 +00:00
commit 16811100d1
391 changed files with 78338 additions and 0 deletions

View File

@@ -0,0 +1,316 @@
# 算法集成规范(Route B · 通用接口接入)
本文是宏量点云处理平台对**多类算法批量接入**的对外规范,面向算法团队 +
验收方。一句话总结:
> 算法侧只交付 **`一个 CLI` + `一份 result.json`**,平台用统一插件机制
> 接入、并发执行、按 manifest 渲染表单、按 result.json 渲染结果。
满足工信部验收"算法在软件中集成运行"且 20 分钟内出结果的要求。
---
## 1. 总体架构
```
前端 (React)
└── AlgorithmRunPanel ← 由 manifest 自动生成插件列表 + 多选并发
└── GenericResultView ← 按 result.json 统一渲染(误差/测量/图片/3D 叠加)
└── PointCloudViewer ← pluginOverlay prop 接收插件生成的热力 / PLY
后端 (Node + tRPC)
└── algorithm.list ← 列出已注册插件
└── algorithm.run ← 单插件执行(入并发池)
└── algorithm.runBatch ← 多插件并发执行(入并发池)
└── /api/files/read/analysis/:taskId/:resultId/:filename ← 通用产物路由
算法层(按算法各自交付)
└── manifest/<id>.json ← 元数据 + UI 表单 + runtime
└── adapters/<id>_run.py ← 平台薄适配层
└── fixtures/<id>.json ← 未就绪时的 mock 预设
```
并发池:默认 4 路(环境变量 `ALGORITHM_JOB_CONCURRENCY` 可覆盖),
单算法超时默认 20 分钟(manifest 内 `runtime.timeoutMs` 可覆盖)。
---
## 2. Manifest JSON 协议
文件位置:`server/plugins/manifests/<plugin-id>.json`。完整示例见
`coupler-panel.json` 等。
```jsonc
{
"id": "coupler-panel", // 全局唯一 kebab-case ID
"name": "车钩面板检测", // UI 显示中文名
"description": "...",
"version": "1.0.0", // 算法版本
"schemaVersion": 1, // result.json schema 版本
"category": "几何测量", // UI 分组(可选)
"requiresPointCloud": true, // 默认 true
"requiresCad": false, // 默认 false;true 时需车型下有 CAD
"inputs": [
{
"name": "profile",
"label": "调参档位",
"type": "select", // number | integer | string | boolean | select
"default": "auto",
"options": [
{ "value": "auto", "label": "自动" },
{ "value": "high_density", "label": "高密度" }
],
"description": "辅助说明,渲染在字段下方"
}
],
"runtime": {
"kind": "process", // 真实算法
"cmd": ["{pythonBin}", "{adapterDir}/coupler_panel_run.py", "{inputJson}"],
"envExtra": {
"PYTHONPATH": "{algRoot}/挠度-偏转角/delivery/coupler_panel_release"
},
"timeoutMs": 1200000 // 默认 20 分钟
}
}
```
或 mock 模式(用于占位未就绪算法):
```jsonc
"runtime": {
"kind": "mock",
"fixture": "flatness-suite.json", // 相对 fixtures/ 的文件名
"simulateMs": 1500 // 模拟耗时,UI 演示进度条
}
```
### 2.1 cmd 占位符
| 占位符 | 含义 |
|--------|------|
| `{pythonBin}` | 容器内 Python 解释器(`$PYTHON3_PATH` 或 `python3`) |
| `{inputJson}` | runner 写到 workDir 的 `input.json` 绝对路径 |
| `{workDir}` | 任务工作目录(也是 outDir) |
| `{adapterDir}` | `server/plugins/adapters/` 绝对路径 |
| `{algRoot}` | 算法源码根目录(`$ALG_ROOT` 或 `/home/lipan/massive-cloud/alg`) |
---
## 3. 输入契约 — `input.json`
runner 在 workDir 写出 `input.json` 后调用算法 CLI,固定路径:
`/data/tasks/<taskId>/analysis/<resultId>/input.json`。
```jsonc
{
"schemaVersion": 1,
"pluginId": "coupler-panel",
"pluginVersion": "1.0.0",
"resultId": 123,
"taskId": 88,
"workDir": "/data/tasks/88/analysis/123",
"outDir": "/data/tasks/88/analysis/123", // 与 workDir 相同
"inputs": {
"pointCloudPaths": ["/data/tasks/88/pointcloud/scan.ply"],
"cadPath": null, // 或 "/data/models/55/cad/panel.stl"
"config": { // 来自 UI 表单
"profile": "auto",
"rightAxisSign": "1"
}
}
}
```
约束:
- 算法**不读全局环境**,所有产物落 `outDir`;
- 不修改 algorithm package 自身目录;
- CLI 必须**非交互**:禁用 GUI / 弹窗。
---
## 4. 输出契约 — `result.json`
算法在 `outDir/result.json` 写最终结果:
```jsonc
{
"schemaVersion": 1,
"status": "completed", // 或 "error"
"message": "可选简短说明",
"measurements": [
{ "name": "右偏转角", "value": 0.83, "unit": "°", "tolerance": 1.0, "status": "pass" },
{ "name": "最大 X 偏差", "value": 0.42, "unit": "mm", "tolerance": 1.0, "status": "pass" },
{ "name": "拟合内点率", "value": 0.92, "status": "info" }
],
"errorStats": { // 可选;用于 GenericResultView 误差卡片
"min": -1.83, "max": 1.28, "mean": 0.04, "stdDev": 0.32,
"totalPoints": 1500000,
"withinTolerance": 1450000, "outOfTolerance": 50000
},
"overlay3d": { // 可选;前端通过 PointCloudViewer.pluginOverlay 消费
"kind": "vertexColors", // 或 "ply" / "none"
"file": "heatmap.bin", // 相对 outDir
"vertexCount": 123456,
"dispMin": -0.5, "dispMax": 0.5
},
"images": ["plot.png", "images/diagnostic.png"], // 相对 outDir
"extra": { "...": "算法专属字段,前端折叠展示" }
}
```
`measurements[*].status` ∈ {`pass` | `warning` | `fail` | `info`},前端按 badge
颜色渲染。
---
## 5. 进度上报协议
子进程通过 **stdout 单行 JSON** 上报进度,runner 解析后写入数据库:
```text
{"type":"progress","percent":0.12,"message":"去噪"}
{"type":"progress","percent":0.45,"message":"平面拟合"}
{"type":"progress","percent":0.80,"message":"轮廓度计算"}
```
字段说明:
- `percent`:允许 0~1 或 0~100,runner 自动归一化为 0~1;
- `message`:当前阶段名,会写入 `resultData.stage`。
stderr 仅作日志,runner 会原样转发到平台日志。
---
## 6. 错误约定
| 情况 | 算法行为 | 平台行为 |
|------|----------|----------|
| 数据不合规 / 计算失败 | 写 `result.json` 且 `status="error"`,`message` 中文+英文摘要 | DB.status="error",前端红字提示 |
| 进程异常退出(无 result.json) | exit code 非 0 | runner 写一条带 `exitCode` 的 error 记录 |
| 超时 | runner 发 SIGKILL | DB.status="error",错误信息为「超时 N ms」 |
---
## 7. tRPC 接口
```ts
// 列出所有插件
trpc.algorithm.list.useQuery()
// → { plugins: PluginSummary[], scheduler: { running, queued, limit } }
// 单个算法
trpc.algorithm.run.useMutation()
// .mutate({ taskId, pluginId, params })
// → { id, pluginId, schemaVersion }
// 并发批量
trpc.algorithm.runBatch.useMutation()
// .mutate({ taskId, jobs: [{ pluginId, params }] })
// → { jobs: [{ id, pluginId }], scheduler }
```
每次 run 都会在 `task_analysis_results` 表插入一条 `type='plugin'` 的记录。
前端通过原有 `trpc.analysisResult.list` 轮询,按 `pluginId` 区分。
---
## 8. 数据库变更
迁移文件:`drizzle/0007_task_analysis_plugin_columns.sql`。
```sql
ALTER TABLE task_analysis_results
MODIFY COLUMN type ENUM(..., 'plugin') NOT NULL;
ALTER TABLE task_analysis_results
ADD COLUMN pluginId VARCHAR(64) NULL AFTER type,
ADD COLUMN schemaVersion INT NULL AFTER pluginId;
CREATE INDEX idx_task_analysis_plugin_id ON task_analysis_results (pluginId);
```
升级流程:
1. 应用 SQL 迁移(`pnpm db:push` 或手动执行)。
2. 重启 Node 服务。
3. 新增算法只需新增 manifest + adapter,无需再变更数据库。
---
## 9. 文件路由(产物)
```
GET /api/files/read/analysis/:taskId/:resultId/:filename
```
返回 outDir 下任意相对路径的文件(含 `images/foo.png` 等)。前端通过
`result.json.images` / `overlay3d.file` 直接拼接 URL。
> 与现存 `/read/analysis/heatmap/...`、`/read/analysis/assembly-vertex/...`
> 共存;具体路由优先匹配,最后由通用路由兜底。
---
## 10. 已接入的算法
全部 4 类算法都是真实 `process` 运行时,没有任何 mock:
| 插件 ID | 算法 | 来源 | 依赖 |
|---------|------|------|------|
| `coupler-panel` | 车钩面板检测 | `alg/挠度-偏转角/delivery/coupler_panel_release` | numpy / scipy / pyyaml |
| `underframe-deflection` | 构架挠度测量 | `alg/挠度-偏转角/delivery/underframe_deflection` | numpy / scipy |
| `train-measurement` | 列车点云测距 | `alg/260505列车点云测距v1/.../train_measurement_system` | numpy / pyyaml / open3d / openpyxl |
| `flatness-suite` | 平整度 / 平面度 / 轮廓 / 曲率 | `alg/平整度、平面度、轮廓测量/` 下的脚本 | numpy / open3d(contour 还需 torch + trimesh) |
> flatness-suite 的 contour 子模块依赖较重(~2GB torch)。adapter 在 torch
> 缺失时会自动跳过 contour,**plane / curvature 仍能正常出结果**。
### 10.1 容器内依赖安装
镜像构建阶段:
```bash
# 通用部分 — 已经在 Dockerfile 里自动执行
python3 -m pip install --break-system-packages \
--default-timeout=300 --retries 10 \
-i https://pypi.tuna.tsinghua.edu.cn/simple \
-r requirements-geometry.txt
# 可选:启用 flatness-suite 的 contour 子模块(CPU 版,~2GB)
python3 -m pip install --break-system-packages \
-r requirements-flatness-contour.txt
```
### 10.2 算法源码挂载
`docker-compose.yml` 已配置:
```yaml
app:
environment:
- ALG_ROOT=/alg
volumes:
- ${LOCAL_ALG_DIR:-./alg}:/alg:ro # 只读挂载,安全
```
宿主机上的 `alg/` 目录(与本仓库 `alg/` 结构一致)只读挂载到容器内 `/alg`。
manifest 里所有 `{algRoot}` 占位符在运行时都解析为该路径。算法源码通过卷挂载
而非打入镜像,避免 13.5GB 的构建上下文。
---
## 11. 验收检查清单
- [x] 系统中存在「算法运行(多选并发)」面板,能列出 4 类算法
- [x] 多选执行后所有算法**并行**调度(默认 4 路并发池)
- [x] 单次运行有进度条,20 分钟内出结果(manifest 默认超时 1200s)
- [x] 结果统一渲染:误差统计 / 测量值表 / 图片 / 3D 叠加按钮
- [x] 数据库记录算法 ID + schemaVersion,可回放、可追溯
- [x] 算法层与 UI 层完全解耦:新增算法不改前端 / 不改 tRPC / 不改数据库
---
## 12. 后续接入新算法的步骤
1. 把算法封装成一个 Python 入口:`python -m my_algo.run <input.json>`,
保证:读 `inputs.pointCloudPaths[0]` → 计算 → 写 `outDir/result.json`。
2. 复制一份 manifest(如 `coupler-panel.json`)改 `id`、`cmd`、`envExtra`、`inputs`。
3. (可选)写一个 adapter `server/plugins/adapters/my_algo_run.py` 做参数翻译,
或者算法 CLI 直接吃 input.json。
4. 重启服务,前端自动刷出新条目,**不需要任何前端 / SQL 改动**。