|
|
|
|
@ -2,6 +2,8 @@ import { getDeviceKindForAction } from '@/utils/robotDevice'
|
|
|
|
|
|
|
|
|
|
export const FLOW_SCHEMA_VERSION = 2
|
|
|
|
|
export const DEFAULT_ROLE_KEY = 'default_executor'
|
|
|
|
|
export const FLOW_NODE_RESOURCE_OWNER = 'FLOW_NODE'
|
|
|
|
|
export const USER_RESOURCE_OWNER = 'USER'
|
|
|
|
|
|
|
|
|
|
const DEVICE_KIND_META = Object.freeze({
|
|
|
|
|
1: { key: 'agv', name: '移动底盘' },
|
|
|
|
|
@ -75,6 +77,7 @@ const findOrCreateLegacySlot = (role, node, legacySlotByIdentity, legacyDeviceId
|
|
|
|
|
if (legacySlotByIdentity.has(identity)) return legacySlotByIdentity.get(identity)
|
|
|
|
|
|
|
|
|
|
const slot = createDeviceSlot(kind, role.deviceSlots)
|
|
|
|
|
slot.managedBy = FLOW_NODE_RESOURCE_OWNER
|
|
|
|
|
if (normalizedDeviceId) slot.legacyDeviceId = normalizedDeviceId
|
|
|
|
|
role.deviceSlots.push(slot)
|
|
|
|
|
legacySlotByIdentity.set(identity, slot)
|
|
|
|
|
@ -117,8 +120,11 @@ export const normalizeFlowResourceSchema = (sourceGraph) => {
|
|
|
|
|
}))
|
|
|
|
|
|
|
|
|
|
graph.nodes.forEach((node) => {
|
|
|
|
|
if (!isRobotNode(node)) return
|
|
|
|
|
const properties = node.properties || (node.properties = {})
|
|
|
|
|
if (!isRobotNode(node)) {
|
|
|
|
|
delete properties.executionTarget
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
const target = properties.executionTarget || {
|
|
|
|
|
roleKey: defaultRole.roleKey,
|
|
|
|
|
dispatchPolicy: 'ONE',
|
|
|
|
|
@ -133,6 +139,10 @@ export const normalizeFlowResourceSchema = (sourceGraph) => {
|
|
|
|
|
const slot = findOrCreateLegacySlot(targetRole, node, legacySlotByIdentity, deviceParam?.input)
|
|
|
|
|
if (slot) target.deviceSlotKey = slot.slotKey
|
|
|
|
|
}
|
|
|
|
|
const referencedSlot = targetRole.deviceSlots.find((slot) => slot.slotKey === target.deviceSlotKey)
|
|
|
|
|
if (referencedSlot && !referencedSlot.managedBy) {
|
|
|
|
|
referencedSlot.managedBy = FLOW_NODE_RESOURCE_OWNER
|
|
|
|
|
}
|
|
|
|
|
delete target.deviceBindings
|
|
|
|
|
properties.nodeParams = (properties.nodeParams || []).filter((param) => param.name !== 'deviceId')
|
|
|
|
|
properties.executionTarget = target
|
|
|
|
|
@ -147,6 +157,53 @@ export const ensureNodeExecutionResources = (sourceGraph, nodeId) => {
|
|
|
|
|
return { graph, node, start: getStartNode(graph) }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Removes role definitions and device usages that were registered automatically
|
|
|
|
|
* for flow nodes and are no longer referenced by any remaining node.
|
|
|
|
|
*/
|
|
|
|
|
export const pruneUnusedNodeResources = (sourceGraph) => {
|
|
|
|
|
const graph = clone(sourceGraph || { nodes: [], edges: [] })
|
|
|
|
|
const start = getStartNode(graph)
|
|
|
|
|
if (!start?.properties?.resourceRoles) {
|
|
|
|
|
return { graph, start, removedRoles: 0, removedSlots: 0 }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const usedRoleKeys = new Set()
|
|
|
|
|
const usedSlotsByRole = new Map()
|
|
|
|
|
graph.nodes.filter(isRobotNode).forEach((node) => {
|
|
|
|
|
const target = node.properties?.executionTarget
|
|
|
|
|
if (!target?.roleKey) return
|
|
|
|
|
usedRoleKeys.add(target.roleKey)
|
|
|
|
|
if (!target.deviceSlotKey) return
|
|
|
|
|
if (!usedSlotsByRole.has(target.roleKey)) usedSlotsByRole.set(target.roleKey, new Set())
|
|
|
|
|
usedSlotsByRole.get(target.roleKey).add(target.deviceSlotKey)
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
let removedSlots = 0
|
|
|
|
|
start.properties.resourceRoles.forEach((role) => {
|
|
|
|
|
const usedSlotKeys = usedSlotsByRole.get(role.roleKey) || new Set()
|
|
|
|
|
role.deviceSlots = (role.deviceSlots || []).filter((slot) => {
|
|
|
|
|
const shouldRemove = slot.managedBy === FLOW_NODE_RESOURCE_OWNER && !usedSlotKeys.has(slot.slotKey)
|
|
|
|
|
if (shouldRemove) removedSlots += 1
|
|
|
|
|
return !shouldRemove
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
const beforeRoleCount = start.properties.resourceRoles.length
|
|
|
|
|
start.properties.resourceRoles = start.properties.resourceRoles.filter((role, index) => (
|
|
|
|
|
index === 0
|
|
|
|
|
|| role.managedBy !== FLOW_NODE_RESOURCE_OWNER
|
|
|
|
|
|| usedRoleKeys.has(role.roleKey)
|
|
|
|
|
))
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
graph,
|
|
|
|
|
start,
|
|
|
|
|
removedRoles: beforeRoleCount - start.properties.resourceRoles.length,
|
|
|
|
|
removedSlots,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export const validateFlowResources = (sourceGraph) => {
|
|
|
|
|
const graph = normalizeFlowResourceSchema(sourceGraph)
|
|
|
|
|
const roles = getResourceRoles(graph)
|
|
|
|
|
@ -212,8 +269,12 @@ export const mergeCopiedResourceRoles = (targetGraph, sourceRoles = [], copiedNo
|
|
|
|
|
|
|
|
|
|
if (!targetRole && sourceRole) {
|
|
|
|
|
targetRole = clone(sourceRole)
|
|
|
|
|
targetRole.managedBy = FLOW_NODE_RESOURCE_OWNER
|
|
|
|
|
delete targetRole.legacyRobotId
|
|
|
|
|
;(targetRole.deviceSlots || []).forEach((slot) => delete slot.legacyDeviceId)
|
|
|
|
|
;(targetRole.deviceSlots || []).forEach((slot) => {
|
|
|
|
|
slot.managedBy = FLOW_NODE_RESOURCE_OWNER
|
|
|
|
|
delete slot.legacyDeviceId
|
|
|
|
|
})
|
|
|
|
|
targetRoles.push(targetRole)
|
|
|
|
|
addedRoles += 1
|
|
|
|
|
addedSlots += targetRole.deviceSlots?.length || 0
|
|
|
|
|
@ -231,6 +292,7 @@ export const mergeCopiedResourceRoles = (targetGraph, sourceRoles = [], copiedNo
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
const newSlot = sourceSlot ? clone(sourceSlot) : createDeviceSlot(getDeviceKindForAction(node.properties?.action), targetRole.deviceSlots)
|
|
|
|
|
newSlot.managedBy = FLOW_NODE_RESOURCE_OWNER
|
|
|
|
|
delete newSlot.legacyDeviceId
|
|
|
|
|
targetRole.deviceSlots.push(newSlot)
|
|
|
|
|
target.deviceSlotKey = newSlot.slotKey
|
|
|
|
|
|