feat: add selectable demo flow tasks

This commit is contained in:
2026-09-02 15:23:15 +08:00
parent f879328562
commit e3dc7ebe21
19 changed files with 871 additions and 110 deletions

View File

@@ -155,23 +155,53 @@ def main() -> int:
offset_right = 700.0 * _math.tan(_math.radians(raw_right)) if 'raw_right' in dir() else 700.0 * _math.tan(_math.radians(theta_right))
offset_ref = 375.0 * _math.tan(_math.radians(raw_ref)) if 'raw_ref' in dir() else 375.0 * _math.tan(_math.radians(theta_ref))
measurements = [
{
inspection_object = str((payload.get("context") or {}).get("inspectionObject") or "unknown")
longitudinal_task_no = "10" if inspection_object == "head" else "12" if inspection_object == "middle" else None
reference_task_no = "8" if inspection_object == "head" else "9" if inspection_object == "middle" else None
raw_selected_task_nos = config.get("demoTaskNos")
selected_task_nos = (
{str(int(value)) for value in raw_selected_task_nos if isinstance(value, (int, float)) and float(value).is_integer()}
if isinstance(raw_selected_task_nos, list)
else None
)
def _selected(task_no: str | None) -> bool:
return selected_task_nos is None or (task_no is not None and task_no in selected_task_nos)
measurements = []
if _selected(longitudinal_task_no):
longitudinal_measurement = {
"name": "车钩面板距纵向中心的右偏转角",
"value": round(offset_right, 6),
"unit": "mm",
"baseline": 700.0,
"tolerance": 1.0,
"status": _status_for(offset_right, 1.0),
},
{
}
if longitudinal_task_no:
longitudinal_measurement.update({
"taskNo": longitudinal_task_no,
"metricCode": "coupler.longitudinal_right_offset",
"name": "纵向中心右偏转",
})
measurements.append(longitudinal_measurement)
if _selected(reference_task_no):
reference_measurement = {
"name": "车钩面板距参考面的右偏转角",
"value": round(offset_ref, 6),
"unit": "mm",
"baseline": 375.0,
"tolerance": 0.8,
"status": _status_for(offset_ref, 0.8),
},
}
if reference_task_no:
reference_measurement.update({
"taskNo": reference_task_no,
"metricCode": "coupler.reference_right_offset",
"name": "参考面右偏转",
})
measurements.append(reference_measurement)
measurements.extend([
{
"name": "最大 x 偏差 dx_y",
"value": dx_y,
@@ -198,7 +228,7 @@ def main() -> int:
"unit": "mm",
"status": "info",
},
]
])
payload_out = {
"schemaVersion": getattr(result, "schema_version", "1.1"),

View File

@@ -226,8 +226,17 @@ def main() -> int:
result_template_path = alg_dir / "excel" / "测量结果.xlsx"
if not result_template_path.is_file():
raise RuntimeError(f"内置测量结果模板不存在: {result_template_path}")
selected_task_nos = config.get("demoTaskNos")
selected_task_ids = (
[str(int(value)) for value in selected_task_nos if isinstance(value, (int, float)) and float(value).is_integer()]
if isinstance(selected_task_nos, list)
else None
)
if selected_task_ids == []:
raise RuntimeError("请至少选择一个轮廓测量任务")
res = pipeline.run_contour_batch(
all_tasks=True,
task_ids=selected_task_ids,
all_tasks=selected_task_ids is None,
data_root=str(data_root),
pointcloud_kind="noise",
point_chunk_size=8192,

View File

@@ -0,0 +1,24 @@
import importlib.util
import unittest
from pathlib import Path
def _load_adapter(name: str):
path = Path(__file__).with_name(name)
spec = importlib.util.spec_from_file_location(path.stem, path)
module = importlib.util.module_from_spec(spec)
assert spec and spec.loader
spec.loader.exec_module(module)
return module
class DemoTaskSelectionTest(unittest.TestCase):
def test_train_task_number_parsing_and_selection(self):
adapter = _load_adapter("train_measurement_run.py")
self.assertEqual(adapter._task_no_from_name("任务46_车体横断面对角线差值"), "46")
self.assertEqual(adapter._selected_task_nos({"demoTaskNos": [2, 46, 2]}), {"2", "46"})
self.assertIsNone(adapter._selected_task_nos({}))
if __name__ == "__main__":
unittest.main()

View File

@@ -19,6 +19,7 @@ v2.2 变更:
from __future__ import annotations
import os
import re
import sys
import traceback
from pathlib import Path
@@ -33,6 +34,18 @@ from _plugin_io import (
)
def _task_no_from_name(name: object) -> str | None:
match = re.match(r"^任务\s*(\d+)", str(name or ""))
return match.group(1) if match else None
def _selected_task_nos(config: dict) -> set[str] | None:
raw = config.get("demoTaskNos")
if not isinstance(raw, list):
return None
return {str(int(value)) for value in raw if isinstance(value, (int, float)) and float(value).is_integer()}
def _to_float(value):
if value is None:
return None
@@ -155,6 +168,13 @@ def main() -> int:
if t.get("enabled", True)
}
tm.tasks = [t for t in tm.tasks if t.name in _enabled_names]
selected_task_nos = _selected_task_nos(config)
if selected_task_nos is not None:
available_task_nos = {_task_no_from_name(task.name) for task in tm.tasks}
missing_task_nos = selected_task_nos - available_task_nos
if missing_task_nos:
raise RuntimeError(f"测距配置中找不到所选任务:{','.join(sorted(missing_task_nos, key=int))}")
tm.tasks = [task for task in tm.tasks if _task_no_from_name(task.name) in selected_task_nos]
# 注入可视化输出目录
viz_dir = out_dir / "visualizations"

View File

@@ -49,6 +49,11 @@ def main() -> int:
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:

View File

@@ -8,6 +8,13 @@
"category": "几何测量",
"requiresPointCloud": true,
"requiresCad": false,
"demoSelection": { "kind": "task-nos", "param": "demoTaskNos" },
"demoTasks": [
{ "taskNo": 8, "name": "车钩面板到参考面的右偏转", "category": "车钩", "inspectionObjects": ["head"] },
{ "taskNo": 9, "name": "车钩面板到参考面的右偏转", "category": "车钩", "inspectionObjects": ["middle"] },
{ "taskNo": 10, "name": "车钩面板到纵向中心的右偏转", "category": "车钩", "inspectionObjects": ["head"] },
{ "taskNo": 12, "name": "车钩面板到纵向中心的右偏转", "category": "车钩", "inspectionObjects": ["middle"] }
],
"inputs": [
{
"name": "profile",

View File

@@ -8,6 +8,29 @@
"category": "几何测量",
"requiresPointCloud": false,
"requiresCad": false,
"demoSelection": { "kind": "task-nos", "param": "demoTaskNos" },
"demoTasks": [
{ "taskNo": 11, "name": "车钩面板的平整度", "category": "轮廓", "inspectionObjects": ["head", "middle"] },
{ "taskNo": 13, "name": "车长方向平面度", "category": "轮廓", "inspectionObjects": ["head", "middle"] },
{ "taskNo": 17, "name": "车高方向弧形轮廓测量", "category": "轮廓", "inspectionObjects": ["head", "middle"] },
{ "taskNo": 20, "name": "门柱平整度", "category": "轮廓", "inspectionObjects": ["head", "middle"] },
{ "taskNo": 22, "name": "从门口到外部轮廓的距离", "category": "轮廓", "inspectionObjects": ["head", "middle"] },
{ "taskNo": 23, "name": "从窗口到外部轮廓的距离", "category": "轮廓", "inspectionObjects": ["head", "middle"] },
{ "taskNo": 25, "name": "内风挡外圈平面度", "category": "轮廓", "inspectionObjects": ["head", "middle"] },
{ "taskNo": 26, "name": "内风挡区域平面度", "category": "轮廓", "inspectionObjects": ["head", "middle"] },
{ "taskNo": 27, "name": "外风挡区域平面度", "category": "轮廓", "inspectionObjects": ["head", "middle"] },
{ "taskNo": 31, "name": "车头前墙区域平面度", "category": "轮廓", "inspectionObjects": ["head"] },
{ "taskNo": 34, "name": "司机室门外弧形轮廓", "category": "轮廓", "inspectionObjects": ["head"] },
{ "taskNo": 35, "name": "前窗框侧轮廓", "category": "轮廓", "inspectionObjects": ["head"] },
{ "taskNo": 36, "name": "前窗框弧形轮廓", "category": "轮廓", "inspectionObjects": ["head"] },
{ "taskNo": 37, "name": "顶灯框侧轮廓", "category": "轮廓", "inspectionObjects": ["head"] },
{ "taskNo": 38, "name": "顶灯框弧形轮廓", "category": "轮廓", "inspectionObjects": ["head"] },
{ "taskNo": 39, "name": "逃生窗侧轮廓", "category": "轮廓", "inspectionObjects": ["head"] },
{ "taskNo": 40, "name": "逃生窗弧形轮廓", "category": "轮廓", "inspectionObjects": ["head"] },
{ "taskNo": 41, "name": "曲面窗侧轮廓", "category": "轮廓", "inspectionObjects": ["head"] },
{ "taskNo": 42, "name": "曲面窗弧形轮廓", "category": "轮廓", "inspectionObjects": ["head"] },
{ "taskNo": 45, "name": "地板平度", "category": "轮廓", "inspectionObjects": ["head", "middle"] }
],
"inputs": [
{
"name": "useBatchDatasets",

View File

@@ -8,6 +8,25 @@
"category": "尺寸测量",
"requiresPointCloud": true,
"requiresCad": false,
"demoSelection": { "kind": "task-nos", "param": "demoTaskNos" },
"demoTasks": [
{ "taskNo": 1, "name": "转向架距离", "category": "尺寸", "inspectionObjects": ["head", "middle"] },
{ "taskNo": 2, "name": "车体长度", "category": "尺寸", "inspectionObjects": ["head", "middle"] },
{ "taskNo": 7, "name": "边梁下平面到参考线的距离", "category": "尺寸", "inspectionObjects": ["head", "middle"] },
{ "taskNo": 15, "name": "侧墙最大点到参考平面距离", "category": "尺寸", "inspectionObjects": ["head", "middle"] },
{ "taskNo": 16, "name": "车体最大宽度", "category": "尺寸", "inspectionObjects": ["head", "middle"] },
{ "taskNo": 18, "name": "门口宽度", "category": "尺寸", "inspectionObjects": ["head", "middle"] },
{ "taskNo": 21, "name": "侧门高度", "category": "尺寸", "inspectionObjects": ["head", "middle"] },
{ "taskNo": 28, "name": "端门口宽度", "category": "尺寸", "inspectionObjects": ["head", "middle"] },
{ "taskNo": 29, "name": "端门口高度", "category": "尺寸", "inspectionObjects": ["head", "middle"] },
{ "taskNo": 30, "name": "参考线到司机室辅助孔距离", "category": "尺寸", "inspectionObjects": ["head"] },
{ "taskNo": 32, "name": "司机室登车门门口宽度", "category": "尺寸", "inspectionObjects": ["head"] },
{ "taskNo": 33, "name": "司机室登车门门口高度", "category": "尺寸", "inspectionObjects": ["head"] },
{ "taskNo": 43, "name": "窗口下沿高度", "category": "尺寸", "inspectionObjects": ["head", "middle"] },
{ "taskNo": 44, "name": "车顶高度", "category": "尺寸", "inspectionObjects": ["head", "middle"] },
{ "taskNo": 46, "name": "车体横断面对角线差值", "category": "尺寸", "inspectionObjects": ["head", "middle"] },
{ "taskNo": 47, "name": "内端墙门口高度", "category": "尺寸", "inspectionObjects": ["head", "middle"] }
],
"uiPanel": "train-measurement",
"inputs": [
{

View File

@@ -8,6 +8,10 @@
"category": "几何测量",
"requiresPointCloud": true,
"requiresCad": false,
"demoSelection": { "kind": "task-nos", "param": "demoTaskNos" },
"demoTasks": [
{ "taskNo": 5, "name": "纵向梁到直线的垂直偏差(挠度)", "category": "挠度", "inspectionObjects": ["head", "middle"] }
],
"inputs": [
{
"name": "leftRoiPath",

View File

@@ -68,6 +68,22 @@ export interface PluginRuntimeMock {
export type PluginRuntime = PluginRuntimeProcess | PluginRuntimeMock;
export type DemoInspectionObject = 'head' | 'middle';
/** 插件可在“全流程任务选择”中暴露的正式检验任务。 */
export interface PluginDemoTask {
taskNo: number;
name: string;
category: string;
inspectionObjects?: DemoInspectionObject[];
}
/** 任务选择如何转换为插件参数。 */
export interface PluginDemoSelection {
kind: 'task-nos';
param: string;
}
/** 插件 manifest(写在 server/plugins/manifests/*.json) */
export interface PluginManifest {
/** 全局唯一 ID(kebab-case,例如 coupler-panel) */
@@ -89,6 +105,10 @@ export interface PluginManifest {
requiresCad?: boolean;
/** 是否需要点云(默认 true) */
requiresPointCloud?: boolean;
/** “选择任务运行”模式的权威任务目录。 */
demoTasks?: PluginDemoTask[];
/** 选中任务传给适配器的方式。 */
demoSelection?: PluginDemoSelection;
runtime: PluginRuntime;
}