import type { DemoInspectionObject, PluginDemoTask, PluginManifest, } from './plugins/types.js'; export interface DemoTaskCatalogItem extends PluginDemoTask { pluginId: string; pluginName: string; enabled: boolean; disabledReason?: string; } export interface DemoTaskCatalog { inspectionObject: DemoInspectionObject | 'unknown'; inspectionObjectName?: string; total: number; items: DemoTaskCatalogItem[]; } export interface DemoSelectedPluginJob { pluginId: string; taskNos: number[]; params: Record; } function disabledReasonFor( task: PluginDemoTask, inspectionObject: DemoInspectionObject | 'unknown', inspectionObjectName?: string, ): string | undefined { if (!task.inspectionObjects?.length) return undefined; if (inspectionObject === 'unknown') { return inspectionObjectName ? `检测对象“${inspectionObjectName}”尚未配置任务适用规则` : '当前任务尚未确认头车或中车检测对象'; } if (!task.inspectionObjects.includes(inspectionObject)) { return `不适用于当前${inspectionObject === 'head' ? '头车' : '中车'}检测对象`; } return undefined; } /** 从插件 manifest 构建服务端权威任务目录。 */ export function buildDemoTaskCatalog( manifests: readonly PluginManifest[], inspectionObject: DemoInspectionObject | 'unknown', inspectionObjectName?: string, ): DemoTaskCatalog { const items: DemoTaskCatalogItem[] = []; const claimedTaskNos = new Map(); for (const manifest of manifests) { for (const task of manifest.demoTasks ?? []) { const previousPlugin = claimedTaskNos.get(task.taskNo); if (previousPlugin) { throw new Error(`演示任务 ${task.taskNo} 被插件 ${previousPlugin} 与 ${manifest.id} 重复声明`); } claimedTaskNos.set(task.taskNo, manifest.id); const disabledReason = disabledReasonFor(task, inspectionObject, inspectionObjectName); items.push({ ...task, pluginId: manifest.id, pluginName: manifest.name, enabled: disabledReason == null, ...(disabledReason ? { disabledReason } : {}), }); } } items.sort((a, b) => a.taskNo - b.taskNo); return { inspectionObject, ...(inspectionObjectName ? { inspectionObjectName } : {}), total: items.filter((item) => item.enabled).length, items, }; } /** 校验所选任务并转换为最小插件作业集合。 */ export function resolveSelectedDemoJobs( manifests: readonly PluginManifest[], catalog: DemoTaskCatalog, requestedTaskNos: readonly number[], ): { taskNos: number[]; jobs: DemoSelectedPluginJob[] } { const taskNos = Array.from(new Set(requestedTaskNos)).sort((a, b) => a - b); if (taskNos.length === 0) throw new Error('请至少选择一个任务'); const itemByTaskNo = new Map(catalog.items.map((item) => [item.taskNo, item])); for (const taskNo of taskNos) { const item = itemByTaskNo.get(taskNo); if (!item) throw new Error(`任务 ${taskNo} 不存在或当前算法未提供`); if (!item.enabled) throw new Error(`任务 ${taskNo} 当前不可执行:${item.disabledReason ?? '运行条件不满足'}`); } const taskNosByPlugin = new Map(); for (const taskNo of taskNos) { const pluginId = itemByTaskNo.get(taskNo)!.pluginId; const pluginTaskNos = taskNosByPlugin.get(pluginId) ?? []; pluginTaskNos.push(taskNo); taskNosByPlugin.set(pluginId, pluginTaskNos); } const manifestById = new Map(manifests.map((manifest) => [manifest.id, manifest])); const jobs: DemoSelectedPluginJob[] = []; for (const [pluginId, pluginTaskNos] of Array.from(taskNosByPlugin.entries())) { const manifest = manifestById.get(pluginId); if (!manifest) throw new Error(`未注册的算法插件:${pluginId}`); if (manifest.demoSelection?.kind !== 'task-nos' || !manifest.demoSelection.param) { throw new Error(`算法插件 ${pluginId} 未配置任务选择执行方式`); } jobs.push({ pluginId, taskNos: pluginTaskNos, params: { [manifest.demoSelection.param]: pluginTaskNos }, }); } return { taskNos, jobs }; }