182 lines
7.1 KiB
Python
182 lines
7.1 KiB
Python
"""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")
|
||
|
||
selected_task_nos = config.get("demoTaskNos")
|
||
if isinstance(selected_task_nos, list) and 5 not in selected_task_nos:
|
||
write_error_result(out_dir, "构架挠度插件仅支持任务 5")
|
||
return 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())
|