"""underframe-deflection 算法适配器 (v1.2.1)。 调用 alg/delivery_v1.2.1/underframe_deflection 的 run_both_beams: - 输入:整车配准点云 + 左梁 ROI JSON + 右梁 ROI JSON + CAD STP(溯源用) - 输出:left/ right/ 各一份 report.xlsx + report.json + deflection_curve.csv/png - measurements 展示左右梁最大挠度及检验结论 v1.2.1 变更: - 改用 run_both_beams 双梁并行入口,与算法方命令行一致 - leftRoiPath / rightRoiPath 参数默认指向 test_data ROI - skipRegistration 默认 True(点云已配准) """ 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" abs_val = abs(value) if abs_val <= tolerance: return "pass" if abs_val <= tolerance * 1.5: return "warning" return "fail" 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, "加载挠度算法包 v1.2.1") # ── 输入文件 ──────────────────────────────────────────────────────── pointcloud_path = first_pointcloud(payload) if not pointcloud_path: write_error_result(out_dir, "未提供点云文件") return 1 cad_path = inputs.get("cadPath") or None left_roi = config.get("leftRoiPath") or \ "/alg/delivery_v1.2.1/underframe_deflection/test_data/roi_left_beam.json" right_roi = config.get("rightRoiPath") or \ "/alg/delivery_v1.2.1/underframe_deflection/test_data/roi_right_beam.json" for p, label in [(left_roi, "左梁 ROI"), (right_roi, "右梁 ROI")]: if not os.path.isfile(p): write_error_result(out_dir, f"{label} 文件不存在:{p}") return 1 tolerance_mm = float(config.get("toleranceMm") or 3.0) left_tolerance_mm = float(config.get("leftToleranceMm") or tolerance_mm or 7.0) right_tolerance_mm = float(config.get("rightToleranceMm") or tolerance_mm or 12.0) skip_registration = bool(config.get("skipRegistration", True)) enable_bilateral = bool(config.get("enableBilateral", True)) emit_progress(0.10, f"开始左梁挠度测量(容差 {left_tolerance_mm} mm)") from underframe_deflection import run_single_beam_pipeline # type: ignore left_out = out_dir / "left" left_out.mkdir(exist_ok=True) left_report = run_single_beam_pipeline( input_cloud = pointcloud_path, roi_path = left_roi, out_dir = str(left_out), side = "left", tolerance_mm = left_tolerance_mm, cad_path = cad_path, enable_registration= not skip_registration, config = {"enable_bilateral": enable_bilateral}, ) emit_progress(0.55, f"左梁完成,开始右梁挠度测量(容差 {right_tolerance_mm} mm)") right_out = out_dir / "right" right_out.mkdir(exist_ok=True) right_report = run_single_beam_pipeline( input_cloud = pointcloud_path, roi_path = right_roi, out_dir = str(right_out), side = "right", tolerance_mm = right_tolerance_mm, cad_path = cad_path, enable_registration= not skip_registration, config = {"enable_bilateral": enable_bilateral}, ) result = {"left": left_report, "right": right_report} emit_progress(0.95, "整理结果") def _beam_measurements(report: dict, label: str) -> list: insp = report.get("inspection") or {} item_name = insp.get("item_name") or "纵向梁到直线的垂直偏差(挠度)" measured = insp.get("measured_mm") tol = insp.get("tolerance_mm") verdict = insp.get("verdict") or "" return [{ "name" : f"{label} · {item_name}", "value" : round(float(measured), 4) if measured is not None else 0.0, "unit" : "mm", "tolerance": float(tol) if tol is not None else None, "status" : "pass" if verdict == "合格" else "fail" if verdict == "不合格" else "info", }] measurements = ( _beam_measurements(left_report, "左梁") + _beam_measurements(right_report, "右梁") ) # 收集产物文件(xlsx/csv/png)- write_result 会自动扫 xlsx, # 这里额外加 csv/png 让前端也能下载 extra_files: list[str] = [] for subdir in ("left", "right"): sub = out_dir / subdir if not sub.exists(): continue for fname in sorted(sub.iterdir()): if fname.suffix in (".csv", ".png"): extra_files.append(str(fname.relative_to(out_dir))) # ── 汇总消息 ──────────────────────────────────────────────────────── left_v = (left_report.get("inspection") or {}).get("verdict") or "—" right_v = (right_report.get("inspection") or {}).get("verdict") or "—" left_d = (left_report.get("inspection") or {}).get("measured_mm") right_d = (right_report.get("inspection") or {}).get("measured_mm") msg = ( f"挠度测量完成。" f"左梁 {left_d:.3f} mm({left_v})," f"右梁 {right_d:.3f} mm({right_v})。" if left_d is not None and right_d is not None else "挠度测量完成。" ) write_result( out_dir, { "message": msg, "measurements": measurements, "files": extra_files, "extra": {"leftReport": left_report, "rightReport": right_report}, }, ) return 0 except Exception as exc: write_error_result( out_dir, f"underframe-deflection 计算失败:{exc}", extra={"traceback": traceback.format_exc()}, ) return 1 if __name__ == "__main__": sys.exit(main())