Files
massive-cloud/server/demoFlow.ts

121 lines
4.1 KiB
TypeScript

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<string, unknown>;
}
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<number, string>();
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<string, number[]>();
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 };
}