From f69f699f4a8f236879aec4892f7d036b8385ee71 Mon Sep 17 00:00:00 2001 From: xudan Date: Thu, 14 Aug 2025 17:55:22 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0AMR=20Redis=E7=8A=B6?= =?UTF-8?q?=E6=80=81=E6=8E=A5=E5=8F=A3=E5=8F=8A=E7=9B=B8=E5=85=B3=E5=8A=9F?= =?UTF-8?q?=E8=83=BD=EF=BC=8C=E6=94=AF=E6=8C=81=E5=9C=A8=E6=9C=BA=E5=99=A8?= =?UTF-8?q?=E4=BA=BA=E8=AF=A6=E7=BB=86=E5=8D=A1=E7=89=87=E4=B8=AD=E6=9F=A5?= =?UTF-8?q?=E7=9C=8BAMR=E7=8A=B6=E6=80=81=E4=BF=A1=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/apis/robot/api.ts | 14 +- src/apis/robot/type.ts | 88 +++++++ src/components/card/robot-detail-card.vue | 293 +++++++++++++++++++++- 3 files changed, 392 insertions(+), 3 deletions(-) diff --git a/src/apis/robot/api.ts b/src/apis/robot/api.ts index 7e8ad80..d587a54 100644 --- a/src/apis/robot/api.ts +++ b/src/apis/robot/api.ts @@ -1,6 +1,6 @@ import http from '@core/http'; -import type { RobotDetail, RobotGroup, RobotInfo } from './type'; +import type { AmrRedisState, RobotDetail, RobotGroup, RobotInfo } from './type'; const enum API { 获取所有机器人 = '/robot/getAll', @@ -9,6 +9,7 @@ const enum API { 批量抢占控制权 = '/robot/seizeByIds', 同步组文件 = '/robot/syncByGroupId', + 获取AMR状态 = '/amr/redis', } export async function getAllRobots(): Promise> { @@ -62,3 +63,14 @@ export async function syncGroupRobotsById(id: RobotGroup['id'], sid: RobotGroup[ return false; } } + +export async function getAmrRedisState(id: string): Promise { + type D = AmrRedisState; + try { + const data = await http.get(`${API.获取AMR状态}/${id}`); + return data ?? null; + } catch (error) { + console.debug(error); + return null; + } +} diff --git a/src/apis/robot/type.ts b/src/apis/robot/type.ts index e27ca2e..e9d8e26 100644 --- a/src/apis/robot/type.ts +++ b/src/apis/robot/type.ts @@ -46,3 +46,91 @@ export interface RobotRealtimeInfo extends RobotInfo { isWaring?: boolean; // 是否告警 isFault?: boolean; // 是否故障 } + +// AMR Redis状态接口 +export interface AmrRedisState { + headerid: number; + timestamp: string; + version: string; + manufacturer: string; + serialnumber: string; + orderid: string; + orderupdateid: number; + lastnodeid: string; + lastnodesequenceid: number; + driving: boolean; + waitingforinteractionzonerelease: boolean; + paused: boolean; + newbaserequest: boolean; + distancesincelastnode: number; + operatingmode: string; + nodestates: any[]; + edgestates: any[]; + agvposition: { + x: number; + y: number; + theta: number; + mapid: string; + mapdescription: string; + positioninitialized: boolean; + deviationrange: number; + localizationscore: number; + }; + velocity: { + vx: number; + vy: number; + omega: number; + }; + loads: Array<{ + loadid: string; + loadtype: string; + loadposition: string; + boundingboxreference: { + x: number; + y: number; + z: number; + theta: number; + }; + loaddimensions: { + length: number; + width: number; + height: number; + }; + }>; + actionstates: Array<{ + actionid: string; + actiontype: string; + actiondescription: string; + actionstatus: string; + resultdescription: string; + }>; + batterystate: { + batterycharge: number; + batteryvoltage: number; + batteryhealth: number; + charging: boolean; + reach: number; + }; + errors: Array<{ + errortype: string; + errorreferences: Array<{ + referencekey: string; + referencevalue: string; + }>; + errordescription: string; + errorlevel: string; + }>; + information: Array<{ + infotype: string; + inforeferences: Array<{ + referencekey: string; + referencevalue: string; + }>; + infodescription: string; + infolevel: string; + }>; + safetystate: { + estop: string; + fieldviolation: boolean; + }; +} diff --git a/src/components/card/robot-detail-card.vue b/src/components/card/robot-detail-card.vue index 2faead0..1ef9988 100644 --- a/src/components/card/robot-detail-card.vue +++ b/src/components/card/robot-detail-card.vue @@ -1,8 +1,8 @@ + + + + + +
+ +
+ + {{ $t('原始数据') }} + +
+ + + + + + {{ amrDetailData.serialnumber || '-' }} + {{ amrDetailData.manufacturer || '-' }} + {{ amrDetailData.version || '-' }} + {{ amrDetailData.orderid || '-' }} + {{ amrDetailData.lastnodeid || '-' }} + {{ + amrDetailData.operatingmode || '-' + }} + {{ + amrDetailData.driving ? $t('是') : $t('否') + }} + {{ + amrDetailData.paused ? $t('是') : $t('否') + }} + {{ + amrDetailData.timestamp || '-' + }} + {{ (amrDetailData.distancesincelastnode || 0).toFixed(2) }}m + {{ + amrDetailData.waitingforinteractionzonerelease ? $t('是') : $t('否') + }} + + + + + + {{ + amrDetailData.agvposition?.x?.toFixed(4) || '-' + }} + {{ + amrDetailData.agvposition?.y?.toFixed(4) || '-' + }} + {{ + amrDetailData.agvposition?.theta?.toFixed(4) || '-' + }} + {{ + amrDetailData.agvposition?.mapid || '-' + }} + {{ + amrDetailData.agvposition?.localizationscore?.toFixed(4) || '-' + }} + {{ + amrDetailData.agvposition?.deviationrange?.toFixed(4) || '-' + }} + + + + + + {{ amrDetailData.batterystate?.batterycharge?.toFixed(1) || '-' }}% + {{ amrDetailData.batterystate?.batteryvoltage?.toFixed(3) || '-' }}V + {{ amrDetailData.batterystate?.batteryhealth || '-' }}% + {{ + amrDetailData.batterystate?.charging ? $t('是') : $t('否') + }} + {{ amrDetailData.batterystate?.reach?.toFixed(1) || '-' }}km + + + + + + {{ amrDetailData.velocity?.vx?.toFixed(3) || '-' }}m/s + {{ amrDetailData.velocity?.vy?.toFixed(3) || '-' }}m/s + {{ amrDetailData.velocity?.omega?.toFixed(3) || '-' }}rad/s + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {{ amrDetailData.safetystate?.estop || '-' }} + + + + + {{ amrDetailData.safetystate?.fieldviolation ? $t('是') : $t('否') }} + + + + + + + + + +
+
+ +
+
@@ -112,4 +345,60 @@ const stateDot = computed(() => `state-${robot.value?.state}`); } } } + +.amr-detail-content { + .ant-tabs-content { + max-height: 600px; + overflow-y: auto; + } + + .ant-descriptions { + margin-bottom: 16px; + } + + .ant-table { + margin-bottom: 16px; + } + + .font-mono { + font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace; + font-size: 12px; + line-height: 1.5; + } + + .mt-16 { + margin-top: 16px; + } + + .icon { + margin-right: 4px; + } +} + +.error-content { + .mb-16 { + margin-bottom: 16px; + } +} + +// 弹窗标题样式 +:deep(.ant-modal-header) { + .flex { + display: flex; + align-items: center; + justify-content: space-between; + } + + .text-xs { + font-size: 12px; + } + + .text-gray-500 { + color: #6b7280; + } + + .mt-1 { + margin-top: 4px; + } +}