refactor(inspection): 统一AGV运行状态管理并优化机器人列表刷新逻辑
- 将多个组件中的硬编码机器人状态映射替换为统一的 agvRuntimeStatus 工具函数 - 在手动接管和地图概览组件中实现5秒定时刷新机器人列表功能 - 添加 onUnmounted 钩子清理定时器避免内存泄漏 - 重构 MapCanvas 中的机器人位置解析逻辑并添加数据验证 - 为地图概览组件的状态显示添加动态CSS类名支持
This commit is contained in:
parent
916e6f5756
commit
e18c4b88be
@ -11,7 +11,7 @@
|
||||
</div>
|
||||
<div>
|
||||
<div class="bot-name">{{ item.robotName }}</div>
|
||||
<div class="bot-local">{{ robotStatus[item.status] }}</div>
|
||||
<div class="bot-local">{{ agvRuntimeStatusLabel(item.status) }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -122,7 +122,7 @@
|
||||
</div>
|
||||
</template>
|
||||
<script setup>
|
||||
import { computed, onMounted, nextTick } from "vue";
|
||||
import { computed, onMounted, onUnmounted, nextTick } from "vue";
|
||||
import { ElMessage } from 'element-plus'
|
||||
import MapCanvas from "../MapCanvas.vue";
|
||||
import SvgIcon from "@/components/SvgIcon";
|
||||
@ -134,6 +134,7 @@ import { getJointStateApi, moveJApi, torqueOnApi, speedJApi, stopMotionApi, clea
|
||||
import { Remove, CirclePlus } from '@element-plus/icons-vue'
|
||||
import VoiceConversation from "./VoiceConversation.vue";
|
||||
import IPlayer from "@/components/IPlayer/index.vue";
|
||||
import { agvRuntimeStatusLabel } from '@/utils/agvRuntimeStatus'
|
||||
|
||||
const robotList = ref([])
|
||||
const robotDevices = ref([])
|
||||
@ -147,15 +148,18 @@ const loadRobotDevices = async (robotId) => {
|
||||
robotDevices.value = Array.isArray(response.data) ? response.data : []
|
||||
}
|
||||
|
||||
const fetchRobotList = async () => {
|
||||
const fetchRobotList = async (refreshDevices = true) => {
|
||||
try {
|
||||
const res = await getRobotList({
|
||||
robotType: 2,
|
||||
})
|
||||
if (res.code === 200) {
|
||||
const currentRobotId = activeBotData.value.robotId
|
||||
robotList.value = res.rows
|
||||
activeBotData.value = robotList.value[0] || {}
|
||||
await loadRobotDevices(activeBotData.value.robotId)
|
||||
const currentIndex = robotList.value.findIndex(item => item.robotId === currentRobotId)
|
||||
activeBot.value = currentIndex >= 0 ? currentIndex : 0
|
||||
activeBotData.value = robotList.value[activeBot.value] || {}
|
||||
if (refreshDevices) await loadRobotDevices(activeBotData.value.robotId)
|
||||
} else {
|
||||
console.error('获取机器人列表失败:', res.message)
|
||||
}
|
||||
@ -164,13 +168,6 @@ const fetchRobotList = async () => {
|
||||
}
|
||||
}
|
||||
|
||||
const robotStatus = {
|
||||
0: '在线',
|
||||
1: '离线',
|
||||
2: '充电中',
|
||||
3: '巡检中'
|
||||
}
|
||||
|
||||
const activeBot = ref(0)
|
||||
const activeBotData = ref({})
|
||||
const changeBot = async (index) => {
|
||||
@ -307,11 +304,17 @@ const stop = async () => {
|
||||
|
||||
const videoUrl = ref('https://test-streams.mux.dev/x36xhzz/x36xhzz.m3u8')
|
||||
|
||||
let robotRefreshTimer
|
||||
|
||||
onMounted(() => {
|
||||
fetchRobotList()
|
||||
robotRefreshTimer = window.setInterval(() => {
|
||||
if (!document.hidden) fetchRobotList(false)
|
||||
}, 5000)
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
window.clearInterval(robotRefreshTimer)
|
||||
if (animationId.value) {
|
||||
clearInterval(animationId.value)
|
||||
}
|
||||
|
||||
@ -379,6 +379,13 @@ const addRobot = (id, name,x, y, angleRad, color) => {
|
||||
applicationState.robots.push(robot);
|
||||
}
|
||||
|
||||
const parseRobotPosition = (currentPosition) => {
|
||||
if (!currentPosition) return null
|
||||
const values = String(currentPosition).split(',').map(Number)
|
||||
if (values.length < 3 || values.some(value => !Number.isFinite(value))) return null
|
||||
return values
|
||||
}
|
||||
|
||||
const moveRobot = (robot, dx, dy, dtheta = 0) => {
|
||||
robot.x += dx;
|
||||
robot.y += dy;
|
||||
@ -395,7 +402,9 @@ onMounted(() => {
|
||||
setTimeout(() => {
|
||||
bindEvent()
|
||||
props.robotList.forEach(item => {
|
||||
const [x, y, angle] = item.currentPosition.split(',')
|
||||
const position = parseRobotPosition(item.currentPosition)
|
||||
if (!position) return
|
||||
const [x, y, angle] = position
|
||||
addRobot(item.id, item.robotName, x, y, angle, colorList[applicationState.robots.length % colorList.length])
|
||||
})
|
||||
}, 300)
|
||||
@ -409,13 +418,14 @@ watch(() => props.robotList,
|
||||
if (newVal.length > 0) {
|
||||
const ids = applicationState.robots.map(r => r.id);
|
||||
props.robotList.forEach(item => {
|
||||
const position = parseRobotPosition(item.currentPosition)
|
||||
if (!position) return
|
||||
const [x, y, angle] = position
|
||||
if (!ids.includes(item.id)) {
|
||||
const [x, y, angle] = item.currentPosition.split(',')
|
||||
addRobot(item.id, item.robotName, x, y, angle, colorList[applicationState.robots.length % colorList.length])
|
||||
} else {
|
||||
applicationState.robots.forEach(robot => {
|
||||
if (robot.id === item.id) {
|
||||
const [x, y, angle] = item.currentPosition.split(',')
|
||||
// moveRobot(robot, parseFloat(x) - robot.x, parseFloat(y) - robot.y, parseFloat(angle) - robot.angle)
|
||||
robot.x = x
|
||||
robot.y = y
|
||||
@ -473,4 +483,4 @@ onUnmounted(() => {
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
|
||||
@ -7,7 +7,7 @@
|
||||
<div class="title-box">
|
||||
<div><SvgIcon name="bot" :color="index === 0 ? '#00D4FF' : '#FFB300'" /></div>
|
||||
<div class="title">{{ item.robotName }}</div>
|
||||
<div class="state online">{{ robotStatus[item.status] }}</div>
|
||||
<div class="state" :class="agvRuntimeStatusClass(item.status)">{{ agvRuntimeStatusLabel(item.status) }}</div>
|
||||
</div>
|
||||
<div class="info-box">
|
||||
<div class="item-box">
|
||||
@ -86,16 +86,9 @@ import MapCanvas from "./MapCanvas.vue";
|
||||
import { onMounted, onUnmounted } from "vue";
|
||||
import { getRobotList } from '@/api/inspection/robot'
|
||||
import { getRunTaskList } from '@/api/inspection/runTask'
|
||||
|
||||
console.log('sss', import.meta.env.VITE_INSPECTION_TYPE)
|
||||
import { agvRuntimeStatusClass, agvRuntimeStatusLabel } from '@/utils/agvRuntimeStatus'
|
||||
|
||||
const robotList = ref([])
|
||||
const robotStatus = {
|
||||
"0": "在线",
|
||||
"1": "离线",
|
||||
"2": "充电中",
|
||||
"3": "巡检中"
|
||||
}
|
||||
const getRobot = async () => {
|
||||
let robotType
|
||||
if (import.meta.env.VITE_INSPECTION_TYPE === 'inspection') {
|
||||
@ -125,7 +118,7 @@ const taskStatus = {
|
||||
}
|
||||
const getRunningTask = async () => {
|
||||
let taskType
|
||||
if (import.meta.env.VITE_INSPECTION_TYPE = 'inspection') {
|
||||
if (import.meta.env.VITE_INSPECTION_TYPE === 'inspection') {
|
||||
taskType = '1'
|
||||
} else {
|
||||
taskType = '2'
|
||||
@ -161,9 +154,18 @@ const centerCanvasView = () => {
|
||||
}
|
||||
}
|
||||
|
||||
let robotRefreshTimer
|
||||
|
||||
onMounted(() => {
|
||||
getRobot()
|
||||
getRunningTask()
|
||||
robotRefreshTimer = window.setInterval(() => {
|
||||
if (!document.hidden) getRobot()
|
||||
}, 5000)
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
window.clearInterval(robotRefreshTimer)
|
||||
})
|
||||
</script>
|
||||
|
||||
@ -224,6 +226,31 @@ onMounted(() => {
|
||||
background: #0f83;
|
||||
color: #0f8;
|
||||
}
|
||||
|
||||
.unknown,
|
||||
.offline,
|
||||
.stopped {
|
||||
background: rgba(148, 163, 184, 0.2);
|
||||
color: #cbd5e1;
|
||||
}
|
||||
|
||||
.manual,
|
||||
.running {
|
||||
background: rgba(0, 212, 255, 0.18);
|
||||
color: #00d4ff;
|
||||
}
|
||||
|
||||
.charging,
|
||||
.paused {
|
||||
background: rgba(255, 179, 0, 0.2);
|
||||
color: #ffb300;
|
||||
}
|
||||
|
||||
.fault,
|
||||
.emergency {
|
||||
background: rgba(255, 77, 79, 0.2);
|
||||
color: #ff6b6d;
|
||||
}
|
||||
}
|
||||
|
||||
.info-box {
|
||||
@ -403,4 +430,4 @@ onMounted(() => {
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
|
||||
@ -185,6 +185,7 @@ import { computed, getCurrentInstance, onBeforeUnmount, onMounted, reactive, ref
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
import { addRobot, deleteRobot, getRobotDevices, getRobotList, getRobotStatus, updateRobot } from '@/api/inspection/robot'
|
||||
import { getMapList } from '@/api/inspection/map'
|
||||
import { agvRuntimeStatusLabel, agvRuntimeStatusType } from '@/utils/agvRuntimeStatus'
|
||||
|
||||
const { proxy } = getCurrentInstance()
|
||||
const { robot_type } = proxy.useDict('robot_type')
|
||||
@ -256,8 +257,8 @@ async function loadDevices(showLoading = true) {
|
||||
try { const res = await getRobotDevices(activeRobot.value.id); devices.value = res.data || [] } finally { deviceLoading.value = false }
|
||||
}
|
||||
function endpointText(row) { return row.ipAddress && row.port ? `${row.ipAddress}:${row.port}` : '等待连接后自动获取' }
|
||||
function runtimeStatusLabel(value) { return ({ 0: '在线', 1: '离线', 2: '充电中', 3: '巡检中' })[value] || '未知' }
|
||||
function runtimeStatusType(value) { return ({ 0: 'success', 1: 'info', 2: 'warning', 3: 'primary' })[value] || 'info' }
|
||||
const runtimeStatusLabel = agvRuntimeStatusLabel
|
||||
const runtimeStatusType = agvRuntimeStatusType
|
||||
function hasBattery(row) { return row.batteryLevel !== null && row.batteryLevel !== undefined && row.batteryLevel !== '' }
|
||||
function normalizeBattery(value) { return Math.max(0, Math.min(100, Math.round(Number(value) || 0))) }
|
||||
function batteryColor(value) {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user