diff --git a/src/utils/robotDevice.js b/src/utils/robotDevice.js index 9fdac75..96c619b 100644 --- a/src/utils/robotDevice.js +++ b/src/utils/robotDevice.js @@ -1,3 +1,7 @@ +/** + * 平台设备类型枚举,供流程资源匹配、机器人设备筛选和界面展示共同使用。 + * Object.freeze 防止运行期间被改写,数值与后端 deviceKind 协议保持一致。 + */ export const DEVICE_KIND = Object.freeze({ AGV: 1, ARM: 2, @@ -8,6 +12,10 @@ export const DEVICE_KIND = Object.freeze({ SPEAKER: 13, }) +/** + * 流程动作到所需设备类型的全局映射。 + * 作用范围为所有根据 action 判断执行设备的流程模块;未列出的动作视为没有固定设备类型。 + */ export const DEVICE_KIND_BY_ACTION = Object.freeze({ AGV_MOVE_TO_POINT: DEVICE_KIND.AGV, AGV_MOVE_TO_STATION: DEVICE_KIND.AGV, @@ -28,8 +36,16 @@ export const DEVICE_KIND_BY_ACTION = Object.freeze({ VI_PLAY_CORPUS: DEVICE_KIND.SPEAKER, }) +/** + * 查询某个流程动作所需的设备类型。 + * 直接读取统一映射;未知或无需固定设备的动作返回 undefined,由调用方决定降级策略。 + */ export const getDeviceKindForAction = (action) => DEVICE_KIND_BY_ACTION[action] +/** + * 将机器人设备接口数据转换为 Element Plus 下拉框选项,供全局设备选择器复用。 + * 每项以 deviceId 作为值;设备名有效且不同于 ID 时展示“名称 (ID)”,否则仅展示 ID。 + */ export const toDeviceOptions = (devices = []) => devices.map((device) => ({ value: device.deviceId, label: device.deviceName && device.deviceName !== device.deviceId diff --git a/src/views/flow/components/ActionQueueSingleRun.vue b/src/views/flow/components/ActionQueueSingleRun.vue index cc23bf8..f2f9932 100644 --- a/src/views/flow/components/ActionQueueSingleRun.vue +++ b/src/views/flow/components/ActionQueueSingleRun.vue @@ -15,12 +15,9 @@
步骤 {{ index + 1 }} · {{ actionName(step.type) }}
- + :value="device.deviceId" />
{{ stepSummary(step) }}
@@ -34,31 +31,67 @@ import { ElMessage } from 'element-plus' import { getRobotDevicesByRobotId } from '@/api/inspection/robot' import { DEVICE_KIND } from '@/utils/robotDevice' +/** + * 组件输入配置,仅在当前单次动作队列运行表单内使用。 + * nodeParams 提供流程节点参数,robotOptions 提供可选机器人,initialRobotId 用于初始化默认机器人。 + */ const props = defineProps({ nodeParams: { type: Array, default: () => [] }, robotOptions: { type: Array, default: () => [] }, initialRobotId: { type: String, default: '' }, }) +/** 动作类型到中文名称的映射,作用范围为当前组件的步骤标题展示。 */ const labels = { DELAY: '等待', ARM_MOVE_J: '机械臂关节运动', ARM_MOVE_L: '机械臂直线运动', AGV_NAVIGATE_TO_POSE: '底盘导航到坐标', AGV_NAVIGATE_TO_STATION: '底盘导航到站点', AGV_FOLLOW_PATH: '底盘按路径导航', } +/** 当前选中的机器人 ID,作用于设备查询、校验和最终运行请求。 */ const robotId = ref('') +/** 本次动作队列标识;为空时由服务端自动生成,仅随当前运行请求提交。 */ const actionId = ref('') +/** 整个动作队列的超时时间(毫秒),作用于当前运行请求。 */ const totalTimeoutMs = ref(60000) +/** 当前节点解析出的动作步骤副本,组件内可补充 deviceId,不回写原始 props。 */ const steps = ref([]) +/** 当前机器人挂载的设备列表,用于为各动作步骤筛选可执行设备。 */ const devices = ref([]) +/** 设备接口请求状态,仅控制当前组件设备选择框的加载效果。 */ const deviceLoading = ref(false) + +/** + * 获取动作类型的展示名称。 + * 先查询本地中文映射,未配置的类型直接显示原值,避免未知动作显示为空。 + */ const actionName = (type) => labels[type] || type + +/** + * 根据动作名前缀判断所需设备类型。 + * 当前组件只处理机械臂和底盘队列动作;等待等无设备动作返回 null。 + */ const deviceKind = (type) => type?.startsWith('ARM_') ? DEVICE_KIND.ARM : (type?.startsWith('AGV_') ? DEVICE_KIND.AGV : null) + +/** + * 筛选单个步骤可使用的设备,范围限定为当前所选机器人的在线设备结果。 + * 将接口 deviceKind 与动作要求统一转为数值后比较,兼容字符串和数字类型。 + */ const devicesForStep = (step) => devices.value.filter((device) => Number(device.deviceKind) === deviceKind(step.type)) + +/** + * 生成步骤参数摘要,仅用于当前组件的只读预览。 + * 等待和站点导航使用易读文案,其余动作保留完整 JSON 信息。 + */ const stepSummary = (step) => { if (step.type === 'DELAY') return `等待 ${step.params?.durationMs || 0} 毫秒` if (step.type === 'AGV_NAVIGATE_TO_STATION') return `站点 ${step.params?.stationId || '-'}` return JSON.stringify(step.params || {}) } +/** + * 使用节点参数重新初始化表单,作用于首次渲染及输入属性变化。 + * 步骤:1. 将参数数组转成按名称索引的对象;2. 设置基本字段并深拷贝步骤; + * 3. 应用默认机器人;4. 若存在机器人,则加载设备并为步骤建立默认设备选择。 + */ const initData = async () => { const values = Object.fromEntries(props.nodeParams.map((item) => [item.name, item.input])) actionId.value = values.actionId || '' @@ -68,7 +101,13 @@ const initData = async () => { if (robotId.value) await loadDevices(robotId.value) } +/** + * 加载指定机器人的设备并重建各步骤设备选择,影响当前组件的 devices 和 steps。 + * 切换机器人后旧 deviceId 已不再可靠,因此先清空设备及所有步骤选择;无机器人时直接结束。 + * 接口返回后逐步按动作类型筛选设备,并默认选择第一个匹配项;finally 保证加载状态复位。 + */ const loadDevices = async (value) => { + // 机器人发生变化时,旧设备可能属于另一台机器人,必须整体清除以避免误提交。 devices.value = [] steps.value.forEach((step) => { step.deviceId = '' }) if (!value) return @@ -76,6 +115,7 @@ const loadDevices = async (value) => { try { const response = await getRobotDevicesByRobotId(value) devices.value = Array.isArray(response.data) ? response.data : [] + // 每个步骤独立按设备类型匹配;有候选项时预选首项,减少单一设备场景下的重复操作。 steps.value.forEach((step) => { const options = devicesForStep(step) if (options.length) step.deviceId = options[0].deviceId @@ -85,9 +125,14 @@ const loadDevices = async (value) => { } } +/** + * 校验当前动作队列是否具备运行条件,供父组件在提交前调用。 + * 依次检查机器人、队列非空及每个需要设备的步骤;首次失败即提示并终止,全部通过返回 true。 + */ const validate = () => { if (!robotId.value) return ElMessage.warning('请选择机器人'), false if (!steps.value.length) return ElMessage.warning('动作队列不能为空'), false + // 等待类步骤无需设备;机械臂或底盘步骤必须存在与当前机器人匹配的 deviceId。 for (let index = 0; index < steps.value.length; index += 1) { const step = steps.value[index] if (deviceKind(step.type) && !step.deviceId) { @@ -97,15 +142,41 @@ const validate = () => { } return true } + +/** 汇总当前表单数据为运行接口载荷;返回范围包含机器人、队列配置和全部步骤。 */ const getPayload = () => ({ robotId: robotId.value, actionId: actionId.value, totalTimeoutMs: totalTimeoutMs.value, steps: steps.value }) + +/** 返回当前选中的机器人 ID,供父组件处理兼容字段或运行上下文。 */ const getRobotId = () => robotId.value +/** 监听节点配置和默认机器人变化,深度变化时重建当前组件的表单及设备选项。 */ watch(() => [props.nodeParams, props.initialRobotId], initData, { immediate: true, deep: true }) + +/** 向父组件开放提交前校验、载荷读取和机器人 ID 读取能力。 */ defineExpose({ validate, getPayload, getRobotId })