算法更新: delivery v1.2.1、平整度v1.5、测距v2.2、整车配准v1.3 适配器: vehicle-registration 新增、各算法 manifest 和 adapter 更新 后端: taskCadFiles/source 字段迁移、derivedPointClouds 注册、取消任务联动 前端: ElapsedTime 时长显示、两步算法流程、canRun 逻辑修复 文档: 新增5份算法性能分析报告
184 lines
6.3 KiB
Python
184 lines
6.3 KiB
Python
"""underframe-deflection 算法适配器 (v1.2.1)。
|
||
|
||
v1.2.1 变更:
|
||
- 算法包升级至 delivery_v1.2.1(单梁结果新增项点检验字段:公差/测量/误差/结论)
|
||
- report.xlsx 等 Excel 产物通过 result.json 的 files 字段暴露给前端下载
|
||
- PYTHONPATH 指向 delivery_v1.2.1
|
||
|
||
v1.2 变更:
|
||
- 新增 single_beam 单梁管线
|
||
|
||
v1.1 变更:
|
||
- 入口改为 run_measurement_pipeline(返回 dict,同时落盘 report.json / xlsx / PLY)
|
||
- 新增 cad_path(CAD 基准 PLY/STL)和 roi_path(ROI JSON)可选输入
|
||
- 新增 skip_registration / enable_bilateral 配置项
|
||
- 结果从 report dict 中读取,而非 DeflectionResult 对象字段
|
||
"""
|
||
|
||
from __future__ import annotations
|
||
|
||
import os
|
||
import sys
|
||
import traceback
|
||
from pathlib import Path
|
||
|
||
sys.path.insert(0, str(Path(__file__).parent))
|
||
from _plugin_io import (
|
||
emit_progress,
|
||
first_pointcloud,
|
||
load_input,
|
||
write_error_result,
|
||
write_result,
|
||
)
|
||
|
||
|
||
def _status_for(value: float | None, tolerance: float | None) -> str:
|
||
if value is None or tolerance is None:
|
||
return "info"
|
||
if abs(value) <= tolerance:
|
||
return "pass"
|
||
if abs(value) <= tolerance * 1.5:
|
||
return "warning"
|
||
return "fail"
|
||
|
||
|
||
def _safe_float(v: object) -> float | None:
|
||
try:
|
||
return float(v) # type: ignore[arg-type]
|
||
except (TypeError, ValueError):
|
||
return None
|
||
|
||
|
||
def main() -> int:
|
||
payload = load_input(sys.argv)
|
||
inputs = payload.get("inputs") or {}
|
||
config = inputs.get("config") or {}
|
||
out_dir = Path(payload["outDir"])
|
||
out_dir.mkdir(parents=True, exist_ok=True)
|
||
|
||
try:
|
||
emit_progress(0.05, "加载 underframe_deflection v1.2.1 算法包")
|
||
from underframe_deflection.pipeline import run_measurement_pipeline # type: ignore
|
||
|
||
pc_path = first_pointcloud(payload)
|
||
emit_progress(0.15, f"读取点云:{os.path.basename(pc_path)}")
|
||
|
||
profile = str(config.get("profile") or "auto")
|
||
skip_registration = bool(config.get("skipRegistration") or False)
|
||
enable_bilateral = bool(config.get("enableBilateral") if "enableBilateral" in config else True)
|
||
|
||
# 可选 CAD 基准(PLY/STL)和 ROI JSON
|
||
cad_path = inputs.get("cadPath") or None
|
||
roi_path = inputs.get("roiPath") or None
|
||
|
||
emit_progress(0.20, "调用 run_measurement_pipeline")
|
||
|
||
# run_measurement_pipeline 内部会打印进度到 stdout,
|
||
# 我们在调用前后各报一次进度即可。
|
||
report: dict = run_measurement_pipeline(
|
||
input_cloud=pc_path,
|
||
out_dir=str(out_dir),
|
||
profile=profile,
|
||
cad_path=cad_path,
|
||
roi_path=roi_path,
|
||
skip_registration=skip_registration,
|
||
enable_bilateral=enable_bilateral,
|
||
skip_excel=False,
|
||
)
|
||
|
||
emit_progress(0.92, "整理结果")
|
||
|
||
# ── 从 report dict 中提取核心测量值 ──────────────────────────────────
|
||
deflection = report.get("deflection") or {}
|
||
left_sel = (deflection.get("left") or {}).get("selected_measurement") or {}
|
||
right_sel = (deflection.get("right") or {}).get("selected_measurement") or {}
|
||
|
||
left_mm = _safe_float(left_sel.get("signed_deflection_mm"))
|
||
right_mm = _safe_float(right_sel.get("signed_deflection_mm"))
|
||
|
||
registration = report.get("registration") or {}
|
||
reg_rms = _safe_float(registration.get("rms_mm"))
|
||
|
||
quality_flags: list[str] = report.get("quality_flags") or []
|
||
|
||
# 容差:高铁底架挠度通常要求 ≤ 3 mm(可在 manifest inputs 中暴露)
|
||
tol = _safe_float(config.get("toleranceMm")) or 3.0
|
||
|
||
measurements = [
|
||
{
|
||
"name": "左梁挠度",
|
||
"value": left_mm if left_mm is not None else float("nan"),
|
||
"unit": "mm",
|
||
"tolerance": tol,
|
||
"status": _status_for(left_mm, tol),
|
||
},
|
||
{
|
||
"name": "右梁挠度",
|
||
"value": right_mm if right_mm is not None else float("nan"),
|
||
"unit": "mm",
|
||
"tolerance": tol,
|
||
"status": _status_for(right_mm, tol),
|
||
},
|
||
]
|
||
|
||
if reg_rms is not None:
|
||
measurements.append(
|
||
{
|
||
"name": "配准 RMS",
|
||
"value": reg_rms,
|
||
"unit": "mm",
|
||
"status": "pass" if reg_rms <= 1.0 else "warning" if reg_rms <= 2.0 else "fail",
|
||
}
|
||
)
|
||
|
||
# ── 3D 叠加层:优先 out_dir/ply/ 子目录,回退到 out_dir 根目录 ──────────
|
||
overlay3d: dict = {"kind": "none"}
|
||
ply_files: list = []
|
||
ply_dir = out_dir / "ply"
|
||
if ply_dir.exists():
|
||
ply_files = sorted(ply_dir.glob("*.ply"))
|
||
if not ply_files:
|
||
# 算法直接输出到 out_dir 根目录;优先取 full overlay
|
||
for name in ("roi_measurement_overlay_full.ply", "roi_cropped_cloud.ply"):
|
||
candidate = out_dir / name
|
||
if candidate.exists():
|
||
ply_files = [candidate]
|
||
break
|
||
if not ply_files:
|
||
ply_files = sorted(out_dir.glob("*.ply"))
|
||
if ply_files:
|
||
rel = ply_files[0].relative_to(out_dir)
|
||
overlay3d = {
|
||
"kind": "ply",
|
||
"file": str(rel),
|
||
}
|
||
|
||
payload_out = {
|
||
"schemaVersion": report.get("schema_version", "1.1"),
|
||
"status": "completed",
|
||
"measurements": measurements,
|
||
"overlay3d": overlay3d,
|
||
"extra": {
|
||
"algorithmVersion": report.get("algorithm_version"),
|
||
"qualityFlags": quality_flags,
|
||
"registration": registration,
|
||
"deflectionFull": deflection,
|
||
},
|
||
}
|
||
write_result(out_dir, payload_out)
|
||
emit_progress(1.0, "完成")
|
||
return 0
|
||
|
||
except Exception as exc:
|
||
traceback.print_exc()
|
||
write_error_result(
|
||
out_dir,
|
||
f"underframe-deflection 计算失败:{exc}",
|
||
extra={"traceback": traceback.format_exc()},
|
||
)
|
||
return 1
|
||
|
||
|
||
if __name__ == "__main__":
|
||
sys.exit(main())
|