CMVR-BID/src/utils/flowExecution.js

121 lines
5.0 KiB
JavaScript
Raw Normal View History

import { getFlowParamsApi } from '@/api/device/terminal'
import { getDetect } from '@/api/test/detect'
import {
getResourceRoles,
mergeResourceRoleDefinitions,
normalizeFlowResourceSchema,
validateFlowResources,
} from '@/utils/flowResources'
const clone = (value) => JSON.parse(JSON.stringify(value || []))
const parseFlowData = (itemId, flowData) => {
if (!flowData) throw new Error(`流程项[${itemId}]还没有配置工作流`)
try {
const graph = typeof flowData === 'string' ? JSON.parse(flowData) : flowData
const normalizedGraph = normalizeFlowResourceSchema(graph)
const validation = validateFlowResources(normalizedGraph)
if (validation.errors.length) {
throw new Error(validation.errors[0].message)
}
return validation.graph
} catch (error) {
throw new Error(`流程项[${itemId}]不可执行:${error.message || '工作流数据无法解析'}`)
}
}
const assertCompatibleDefinitions = (roleGroups) => {
const rolesByKey = new Map()
roleGroups.flat().forEach((role) => {
const existing = rolesByKey.get(role.roleKey)
if (!existing) {
rolesByKey.set(role.roleKey, clone([role])[0])
return
}
const slotsByKey = new Map((existing.deviceSlots || []).map((slot) => [slot.slotKey, slot]))
;(role.deviceSlots || []).forEach((slot) => {
const existingSlot = slotsByKey.get(slot.slotKey)
if (existingSlot && String(existingSlot.deviceKind ?? '') !== String(slot.deviceKind ?? '')) {
throw new Error(`机器人角色“${existing.displayName}”的设备用途“${slot.displayName}”在不同流程中类型不一致`)
}
if (!existingSlot) existing.deviceSlots.push(clone([slot])[0])
})
})
}
export const loadFlowItemRuntimeRoles = async (items = []) => {
if (items.some((item) => !item?.itemId)) throw new Error('任务编排中存在缺少流程项ID的数据')
const uniqueItems = Array.from(new Map(
items.filter((item) => item?.itemId).map((item) => [item.itemId, item]),
).values())
if (!uniqueItems.length) throw new Error('当前任务没有编排任何流程项')
const roleGroups = await Promise.all(uniqueItems.map(async (item) => {
const response = await getDetect(item.itemId)
const graph = parseFlowData(item.itemName || item.itemId, response.data?.flowData)
return getResourceRoles(graph)
}))
assertCompatibleDefinitions(roleGroups)
return mergeResourceRoleDefinitions(roleGroups)
}
2026-08-12 12:21:06 +08:00
/**
* Loads runtime roles without merging equal role keys across flow items.
* Task orchestration needs one independent assignment per detection item because
* two items may use different physical robots even when their workflows define
* the same logical role (for example, `default_robot`).
*/
export const loadFlowItemScopedRuntimeRoles = async (items = []) => {
if (items.some((item) => !item?.itemId)) throw new Error('任务编排中存在缺少流程项ID的数据')
if (!items.length) throw new Error('当前任务没有编排任何流程项')
const occurrences = new Map()
const roleGroups = await Promise.all(items.map(async (item) => {
const occurrence = (occurrences.get(String(item.itemId)) || 0) + 1
occurrences.set(String(item.itemId), occurrence)
const response = await getDetect(item.itemId)
const graph = parseFlowData(item.itemName || item.itemId, response.data?.flowData)
return getResourceRoles(graph).map((role) => ({
...clone([role])[0],
roleKey: `${item.itemId}:${occurrence}:${role.roleKey}`,
sourceRoleKey: role.roleKey,
itemId: item.itemId,
itemOccurrence: occurrence,
2026-08-12 12:21:06 +08:00
itemName: item.itemName || String(item.itemId),
displayName: `${item.itemName || item.itemId} / ${role.displayName}`,
}))
}))
return roleGroups.flat()
}
export const loadTaskRuntimeRoles = async (taskId) => {
if (!taskId) throw new Error('任务没有关联流程编排')
const response = await getFlowParamsApi(taskId)
return loadFlowItemRuntimeRoles(response.rows || [])
}
2026-08-12 12:21:06 +08:00
export const loadTaskScopedRuntimeRoles = async (taskId) => {
if (!taskId) throw new Error('任务没有关联流程编排')
const response = await getFlowParamsApi(taskId)
return loadFlowItemScopedRuntimeRoles(response.rows || [])
}
export const createRuntimeAssignments = (roles = [], primaryRobotId = '') => roles.map((role, index) => ({
roleKey: role.roleKey,
2026-08-12 12:21:06 +08:00
...(role.sourceRoleKey ? { sourceRoleKey: role.sourceRoleKey } : {}),
...(role.itemId != null ? { itemId: role.itemId } : {}),
...(role.itemOccurrence != null ? { itemOccurrence: role.itemOccurrence } : {}),
members: [{
memberKey: `${role.roleKey}_1`,
robotId: index === 0 ? primaryRobotId || role.legacyRobotId || '' : role.legacyRobotId || '',
devices: Object.fromEntries((role.deviceSlots || []).map((slot) => [
slot.slotKey,
slot.legacyDeviceId || '',
])),
}],
}))
export const firstAssignedRobotId = (assignments = []) => (
clone(assignments).flatMap((item) => item.members || []).find((member) => member.robotId)?.robotId || ''
)