forked from SmartBench/CMVR-IOT-UI
fix(flow): 解决非机器人节点携带机器人执行目标导致的后端误判问题
- 非机器人节点不再携带 executionTarget 属性,避免后端将空对象误判为无效角色引用 - 添加 FLOW_NODE_RESOURCE_OWNER 和 USER_RESOURCE_OWNER 常量用于资源归属管理 - 为流程节点创建的设备插槽添加 managedBy 字段标识资源归属 - 移除未被任何节点引用的自动注册角色定义和设备使用 - 为用户手动创建的角色和设备插槽标记为 USER_RESOURCE_OWNER 归属 - 在节点更新时自动清理未使用的节点资源,优化资源管理
This commit is contained in:
parent
bbc937ce94
commit
e3a2761683
@ -2,6 +2,8 @@ import { getDeviceKindForAction } from '@/utils/robotDevice'
|
|||||||
|
|
||||||
export const FLOW_SCHEMA_VERSION = 2
|
export const FLOW_SCHEMA_VERSION = 2
|
||||||
export const DEFAULT_ROLE_KEY = 'default_executor'
|
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({
|
const DEVICE_KIND_META = Object.freeze({
|
||||||
1: { key: 'agv', name: '移动底盘' },
|
1: { key: 'agv', name: '移动底盘' },
|
||||||
@ -75,6 +77,7 @@ const findOrCreateLegacySlot = (role, node, legacySlotByIdentity, legacyDeviceId
|
|||||||
if (legacySlotByIdentity.has(identity)) return legacySlotByIdentity.get(identity)
|
if (legacySlotByIdentity.has(identity)) return legacySlotByIdentity.get(identity)
|
||||||
|
|
||||||
const slot = createDeviceSlot(kind, role.deviceSlots)
|
const slot = createDeviceSlot(kind, role.deviceSlots)
|
||||||
|
slot.managedBy = FLOW_NODE_RESOURCE_OWNER
|
||||||
if (normalizedDeviceId) slot.legacyDeviceId = normalizedDeviceId
|
if (normalizedDeviceId) slot.legacyDeviceId = normalizedDeviceId
|
||||||
role.deviceSlots.push(slot)
|
role.deviceSlots.push(slot)
|
||||||
legacySlotByIdentity.set(identity, slot)
|
legacySlotByIdentity.set(identity, slot)
|
||||||
@ -117,8 +120,11 @@ export const normalizeFlowResourceSchema = (sourceGraph) => {
|
|||||||
}))
|
}))
|
||||||
|
|
||||||
graph.nodes.forEach((node) => {
|
graph.nodes.forEach((node) => {
|
||||||
if (!isRobotNode(node)) return
|
|
||||||
const properties = node.properties || (node.properties = {})
|
const properties = node.properties || (node.properties = {})
|
||||||
|
if (!isRobotNode(node)) {
|
||||||
|
delete properties.executionTarget
|
||||||
|
return
|
||||||
|
}
|
||||||
const target = properties.executionTarget || {
|
const target = properties.executionTarget || {
|
||||||
roleKey: defaultRole.roleKey,
|
roleKey: defaultRole.roleKey,
|
||||||
dispatchPolicy: 'ONE',
|
dispatchPolicy: 'ONE',
|
||||||
@ -133,6 +139,10 @@ export const normalizeFlowResourceSchema = (sourceGraph) => {
|
|||||||
const slot = findOrCreateLegacySlot(targetRole, node, legacySlotByIdentity, deviceParam?.input)
|
const slot = findOrCreateLegacySlot(targetRole, node, legacySlotByIdentity, deviceParam?.input)
|
||||||
if (slot) target.deviceSlotKey = slot.slotKey
|
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
|
delete target.deviceBindings
|
||||||
properties.nodeParams = (properties.nodeParams || []).filter((param) => param.name !== 'deviceId')
|
properties.nodeParams = (properties.nodeParams || []).filter((param) => param.name !== 'deviceId')
|
||||||
properties.executionTarget = target
|
properties.executionTarget = target
|
||||||
@ -147,6 +157,53 @@ export const ensureNodeExecutionResources = (sourceGraph, nodeId) => {
|
|||||||
return { graph, node, start: getStartNode(graph) }
|
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) => {
|
export const validateFlowResources = (sourceGraph) => {
|
||||||
const graph = normalizeFlowResourceSchema(sourceGraph)
|
const graph = normalizeFlowResourceSchema(sourceGraph)
|
||||||
const roles = getResourceRoles(graph)
|
const roles = getResourceRoles(graph)
|
||||||
@ -212,8 +269,12 @@ export const mergeCopiedResourceRoles = (targetGraph, sourceRoles = [], copiedNo
|
|||||||
|
|
||||||
if (!targetRole && sourceRole) {
|
if (!targetRole && sourceRole) {
|
||||||
targetRole = clone(sourceRole)
|
targetRole = clone(sourceRole)
|
||||||
|
targetRole.managedBy = FLOW_NODE_RESOURCE_OWNER
|
||||||
delete targetRole.legacyRobotId
|
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)
|
targetRoles.push(targetRole)
|
||||||
addedRoles += 1
|
addedRoles += 1
|
||||||
addedSlots += targetRole.deviceSlots?.length || 0
|
addedSlots += targetRole.deviceSlots?.length || 0
|
||||||
@ -231,6 +292,7 @@ export const mergeCopiedResourceRoles = (targetGraph, sourceRoles = [], copiedNo
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
const newSlot = sourceSlot ? clone(sourceSlot) : createDeviceSlot(getDeviceKindForAction(node.properties?.action), targetRole.deviceSlots)
|
const newSlot = sourceSlot ? clone(sourceSlot) : createDeviceSlot(getDeviceKindForAction(node.properties?.action), targetRole.deviceSlots)
|
||||||
|
newSlot.managedBy = FLOW_NODE_RESOURCE_OWNER
|
||||||
delete newSlot.legacyDeviceId
|
delete newSlot.legacyDeviceId
|
||||||
targetRole.deviceSlots.push(newSlot)
|
targetRole.deviceSlots.push(newSlot)
|
||||||
target.deviceSlotKey = newSlot.slotKey
|
target.deviceSlotKey = newSlot.slotKey
|
||||||
|
|||||||
@ -237,11 +237,13 @@ const validateAndSave = async () => {
|
|||||||
ElMessage.warning('请选择该节点使用的设备用途')
|
ElMessage.warning('请选择该节点使用的设备用途')
|
||||||
}
|
}
|
||||||
|
|
||||||
// 保存到 lf
|
// 非机器人节点不应携带机器人执行目标,避免后端将空对象误判为无效角色引用。
|
||||||
lf.setProperties(props.data.id, {
|
const properties = {
|
||||||
...props.data.properties,
|
...props.data.properties,
|
||||||
...formData
|
...formData
|
||||||
})
|
}
|
||||||
|
if (!isRobotNode.value) delete properties.executionTarget
|
||||||
|
lf.setProperties(props.data.id, properties)
|
||||||
|
|
||||||
if (inputValid && outputValid) {
|
if (inputValid && outputValid) {
|
||||||
emit('save-success')
|
emit('save-success')
|
||||||
|
|||||||
@ -69,6 +69,7 @@ import {
|
|||||||
createDeviceSlot,
|
createDeviceSlot,
|
||||||
createResourceKey,
|
createResourceKey,
|
||||||
normalizeFlowResourceSchema,
|
normalizeFlowResourceSchema,
|
||||||
|
USER_RESOURCE_OWNER,
|
||||||
} from '@/utils/flowResources'
|
} from '@/utils/flowResources'
|
||||||
|
|
||||||
const props = defineProps({ data: Object })
|
const props = defineProps({ data: Object })
|
||||||
@ -93,11 +94,15 @@ const addRole = () => {
|
|||||||
...createDefaultRobotRole(),
|
...createDefaultRobotRole(),
|
||||||
roleKey: createResourceKey('robot_role'),
|
roleKey: createResourceKey('robot_role'),
|
||||||
displayName: `机器人角色${formData.resourceRoles.length + 1}`,
|
displayName: `机器人角色${formData.resourceRoles.length + 1}`,
|
||||||
|
managedBy: USER_RESOURCE_OWNER,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const addDeviceSlot = (role) => {
|
const addDeviceSlot = (role) => {
|
||||||
role.deviceSlots.push(createDeviceSlot(null, role.deviceSlots))
|
role.deviceSlots.push({
|
||||||
|
...createDeviceSlot(null, role.deviceSlots),
|
||||||
|
managedBy: USER_RESOURCE_OWNER,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const referencedNodes = (predicate) => globalThis.lf.getGraphData().nodes.filter((node) => predicate(node.properties?.executionTarget))
|
const referencedNodes = (predicate) => globalThis.lf.getGraphData().nodes.filter((node) => predicate(node.properties?.executionTarget))
|
||||||
|
|||||||
@ -275,6 +275,7 @@ import {
|
|||||||
getResourceRoles,
|
getResourceRoles,
|
||||||
mergeCopiedResourceRoles,
|
mergeCopiedResourceRoles,
|
||||||
normalizeFlowResourceSchema,
|
normalizeFlowResourceSchema,
|
||||||
|
pruneUnusedNodeResources,
|
||||||
validateFlowResources,
|
validateFlowResources,
|
||||||
} from "@/utils/flowResources";
|
} from "@/utils/flowResources";
|
||||||
import {
|
import {
|
||||||
@ -1702,6 +1703,10 @@ onMounted(() => {
|
|||||||
if (index !== -1) {
|
if (index !== -1) {
|
||||||
nodeErrorList.value.splice(index, 1);
|
nodeErrorList.value.splice(index, 1);
|
||||||
}
|
}
|
||||||
|
const cleanup = pruneUnusedNodeResources(lf.getGraphData());
|
||||||
|
if (cleanup.start && (cleanup.removedRoles || cleanup.removedSlots)) {
|
||||||
|
lf.setProperties(cleanup.start.id, cleanup.start.properties);
|
||||||
|
}
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user