/** * 算法插件(Route B)通用契约 * ------------------------------------------------------------ * 设计目标: * - 算法引擎层和 UI 层完全解耦:新增算法只需添加一个 JSON manifest + * 一个 Python 适配器,前端无需任何改动。 * - 输入是 JSON、输出是统一 `result.json`(误差统计 / 测量值 / 图片 / * 3D 叠加层),前端按 manifest 渲染表单、按 result.json 渲染结果。 * - 进度通过子进程 stdout 行 JSON `{"type":"progress","percent":...}` * 上报,由 runner 解析后写入 DB,前端 3s 一次轮询拿到。 */ /** 表单字段类型——前端 AlgorithmRunPanel 据此动态渲染表单 */ export type PluginInputType = | 'number' | 'integer' | 'string' | 'boolean' | 'select' | 'pointcloud' // 平台自动注入:任务下的点云路径数组 | 'cad'; // 平台自动注入:该任务所属车型的 CAD 文件 export interface PluginInputField { /** 参数名(写入 input.json `inputs.config[name]`) */ name: string; /** UI 显示标签 */ label: string; type: PluginInputType; /** 默认值 */ default?: unknown; /** number/integer 范围 */ min?: number; max?: number; step?: number; /** select 选项 */ options?: { value: string; label: string }[]; /** 是否必填(默认 false——使用 default 兜底) */ required?: boolean; /** 辅助说明,渲染在表单字段下方 */ description?: string; } /** 子进程运行时——真实算法 */ export interface PluginRuntimeProcess { kind: 'process'; /** 子进程命令模板,支持占位符: * {pythonBin} → process.env.PYTHON3_PATH || 'python3' * {inputJson} → runner 写到 workDir/input.json 的绝对路径 * {workDir} → workDir 绝对路径 * {adapterDir} → server/plugins/adapters 的绝对路径 * {algRoot} → 算法源码根目录(用户工作区下的 alg/) */ cmd: string[]; /** 额外环境变量;同样支持上面的占位符 */ envExtra?: Record; /** 超时(毫秒),默认 20 分钟 —— 工信部要求 20 分钟内出结果 */ timeoutMs?: number; } /** Mock 运行时——返回预设 fixture,用于未就绪算法保证系统完整运行 */ export interface PluginRuntimeMock { kind: 'mock'; /** 相对 server/plugins/fixtures/ 的 JSON 文件名 */ fixture: string; /** 模拟耗时(毫秒),用于演示进度条 */ simulateMs?: number; } 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) */ id: string; /** 中文名,UI 显示 */ name: string; /** 英文短名,日志 / 调试 */ shortName?: string; description: string; /** 算法版本,由算法团队维护 */ version: string; /** 结果 schema 版本,写入 task_analysis_results.schemaVersion */ schemaVersion: number; /** UI 分组标签(可选) */ category?: string; /** 表单字段(不含点云/CAD,这两类由平台自动注入) */ inputs?: PluginInputField[]; /** 是否需要 CAD 文件(true 时若车型下无 CAD 立即报错) */ requiresCad?: boolean; /** 是否需要点云(默认 true) */ requiresPointCloud?: boolean; /** “选择任务运行”模式的权威任务目录。 */ demoTasks?: PluginDemoTask[]; /** 选中任务传给适配器的方式。 */ demoSelection?: PluginDemoSelection; runtime: PluginRuntime; } // --------------------------------------------------------------------------- // 子进程 ↔ runner 消息协议 // --------------------------------------------------------------------------- export interface PluginProgressEvent { type: 'progress'; /** [0, 100] —— 我们对外用整数百分比;适配器允许 0~1 浮点,runner 自动转换 */ percent: number; message?: string; } /** 测量项(GenericResultView 渲染) */ export interface PluginMeasurement { /** 检验任务号;新协议与 metricCode 必须成对提供。 */ taskNo?: string; /** 跨版本稳定的测量项编码。 */ metricCode?: string; name: string; /** 同任务内的位置,例如左梁、右梁。 */ position?: string; /** 失败任务没有有效测量值时为 null,前端显示破折号。 */ value: number | null; unit?: string; tolerance?: number; /** 无法安全数值化的原始公差表达式。 */ toleranceLabel?: string; /** 斜率类测量的基准长度(mm)。 */ baseline?: number; /** pass / warning / fail / info */ status?: 'pass' | 'warning' | 'fail' | 'info'; passLabel?: string; warningLabel?: string; failLabel?: string; /** 兼容层无法确定任务号时置为 true。 */ legacyUnmapped?: boolean; /** 兼容转换前的原始项目名,用于追溯。 */ legacyOriginalName?: string; } /** 误差统计卡片(与原 task_analysis_results.errorStats 兼容) */ export interface PluginErrorStats { min: number; max: number; mean: number; stdDev: number; totalPoints?: number; withinTolerance?: number; outOfTolerance?: number; } /** 3D 叠加层——交给 PointCloudViewer.pluginOverlay 消费 */ export type PluginOverlay3D = | { kind: 'none' } | { /** 顶点色 Float32Array bin(与 smooth_heatmap 同格式); * file 是相对 outDir 的相对路径,前端通过 * /api/files/read/analysis/:taskId/:resultId/:file 拉取 */ kind: 'vertexColors'; file: string; vertexCount: number; dispMin: number; dispMax: number; } | { /** 着色 PLY 叠加文件(同样相对 outDir) */ kind: 'ply'; file: string; /** 批量结果可包含多个 PLY 叠加文件 */ files?: string[]; }; /** 算法写出的 result.json(与 INTEGRATION.md 描述对齐) */ export interface PluginResult { schemaVersion: number; status: 'completed' | 'error'; message?: string; measurements?: PluginMeasurement[]; errorStats?: PluginErrorStats; overlay3d?: PluginOverlay3D; /** 相对 outDir 的图片文件名(如 plot.png 或 images/foo.png)。 * 前端通过 /api/files/read/analysis/:taskId/:resultId/:filename 读取 */ images?: string[]; /** 相对 outDir 的可下载产物(如 report.xlsx)。 * 前端在结果面板展示下载入口。 */ files?: string[]; /** 配准算法专用:产出的衍生点云,runner 自动回写为任务点云记录。 * file 相对 outDir;name 是在任务点云列表中显示的名称。 */ derivedPointClouds?: { file: string; name: string }[]; /** 算法专属字段;前端折叠在「原始字段」里展示 */ extra?: Record; } /** 写入数据库 task_analysis_results.resultData 的运行时元数据。 * 与原有 smooth_heatmap/assembly 沿用同一字段,方便复用前端进度条渲染。 */ export interface PluginResultData extends Partial { kind: 'plugin'; pluginId: string; /** 0–1 实数,子进程上报后由 runner 转写 */ progress?: number; /** 当前阶段,UI 显示 */ stage?: string; /** 失败时的错误信息 */ error?: string; /** 子进程已结束时的退出码 */ exitCode?: number | null; /** 本次算法运行实际使用的任务点云文件 ID,用于前端叠加层匹配校验 */ inputPointCloudFileIds?: number[]; /** 本次算法运行前端选择的任务 CAD 文件 ID,用于后续匹配/追溯 */ inputCadFileIds?: number[]; }