diff --git a/src/views/inspection/cockpit/components/ManualTakeover/index.vue b/src/views/inspection/cockpit/components/ManualTakeover/index.vue index d82338a..d78e55e 100644 --- a/src/views/inspection/cockpit/components/ManualTakeover/index.vue +++ b/src/views/inspection/cockpit/components/ManualTakeover/index.vue @@ -100,7 +100,7 @@
- +
diff --git a/src/views/inspection/cockpit/components/MapCanvas.vue b/src/views/inspection/cockpit/components/MapCanvas.vue index 7d630e4..fc29cc2 100644 --- a/src/views/inspection/cockpit/components/MapCanvas.vue +++ b/src/views/inspection/cockpit/components/MapCanvas.vue @@ -28,16 +28,11 @@ import { import { getMapJson } from '@/api/inspection/robot' import { useInspectionStore } from "@/store/modules/inspection"; import { ElMessage } from 'element-plus'; -import { clear } from 'ace-builds/src-noconflict/ext-code_lens'; const props = defineProps({ robotList: { type: Array, default: () => [] - }, - clearMap: { - type: Boolean, - default: false } }) @@ -78,6 +73,9 @@ const applicationState = { let mapCanvas; // 主渲染画布 DOM 元素 let canvasCtx; // 2D 渲染上下文 +let animationFrameId; +let mapLoadVersion = 0; + const init = () => { mapCanvas = document.getElementById('map-canvas'); canvasCtx = mapCanvas.getContext('2d'); @@ -191,7 +189,7 @@ function renderFrame(timestamp) { // 如果地图未加载,跳过绘制 if (!applicationState.isMapLoaded) { - requestAnimationFrame(renderFrame); // 继续请求下一帧 + animationFrameId = requestAnimationFrame(renderFrame); // 继续请求下一帧 return; } @@ -241,7 +239,7 @@ function renderFrame(timestamp) { drawMapBoundary(canvasCtx, map); // 请求下一帧渲染 - requestAnimationFrame(renderFrame); + animationFrameId = requestAnimationFrame(renderFrame); } const drawAllRobots = (ctx) => { @@ -354,14 +352,25 @@ const centerCanvasView = () => { inspectionStore.camera.centerY = (map.originY + map.maxY) / 2; } -const loadSourceMap = async () => { - const connectedRobot = props.robotList.find(item => item.connectStatus == 1); +const getConnectedRobot = (robotList = props.robotList) => { + return robotList.find(item => item?.connectStatus == 1) +} + +const getMapIdentity = (robotList = props.robotList) => { + const robot = getConnectedRobot(robotList) + return robot ? `${robot.id ?? ''}:${robot.robotMapName ?? ''}` : '' +} + +const loadSourceMap = async (connectedRobot = getConnectedRobot()) => { if (!connectedRobot) return; + const currentLoadVersion = ++mapLoadVersion; + const res = await getMapJson({ robotId: connectedRobot.id, mapName: connectedRobot.robotMapName }); + if (currentLoadVersion !== mapLoadVersion) return; if (res.code === 200) { try { const rawJsonObject = JSON.parse(res.data); // 解析 JSON @@ -373,20 +382,6 @@ const loadSourceMap = async () => { } }; -/** - * - * @param id 机器人编号 - * @param name 机器人名称 - * @param angleRad 角度 - * @param color 颜色 - */ -const addRobot = (id, name,x, y, angleRad, color) => { - const map = applicationState.parsedMap; - // 放置于地图中央附近 - const robot = new MobileRobot(id, name, x, y, angleRad, color); - applicationState.robots.push(robot); -} - const parseRobotPosition = (currentPosition) => { if (!currentPosition) return null const values = String(currentPosition).split(',').map(Number) @@ -394,60 +389,58 @@ const parseRobotPosition = (currentPosition) => { return values } -const moveRobot = (robot, dx, dy, dtheta = 0) => { - robot.x += dx; - robot.y += dy; - robot.angle += dtheta; +const syncRobots = (robotList) => { + const previousRobots = new Map(applicationState.robots.map(robot => [robot.id, robot])); + const nextRobots = []; + + robotList.forEach(item => { + const position = parseRobotPosition(item.currentPosition) + if (!position) return + + const [x, y, angle] = position + const robot = previousRobots.get(item.id) + if (robot) { + robot.name = item.robotName + robot.x = x + robot.y = y + robot.angle = angle + nextRobots.push(robot) + return + } + + nextRobots.push(new MobileRobot( + item.id, + item.robotName, + x, + y, + angle, + colorList[nextRobots.length % colorList.length] + )) + }) + + applicationState.robots = nextRobots } onMounted(() => { init() - if (props.robotList.length > 0) { - loadSourceMap() - requestAnimationFrame(renderFrame); - window.addEventListener('resize', resizeCanvasToContainer); // 监听窗口大小变化 - resizeCanvasToContainer(); // 初始调用一次 - setTimeout(() => { - bindEvent() - props.robotList.forEach(item => { - 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) - } + window.addEventListener('resize', resizeCanvasToContainer); + resizeCanvasToContainer(); + bindEvent() + syncRobots(props.robotList) + loadSourceMap() + animationFrameId = requestAnimationFrame(renderFrame); }) const colorList = ['#00D4FF', '#FFB300', '#00FF88', '#ff5100', '#fff', '#15ff00', '#ff00d4', '#00d4ff'] -watch(() => props.robotList, - (newVal) => { - if (newVal.length > 0) { - if (props.clearMap) { - applicationState.robots = [] - } - loadSourceMap() +// 轮询只同步机器人实例,不重新请求、解析或重置地图。 +watch(() => props.robotList, syncRobots) - 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)) { - addRobot(item.id, item.robotName, x, y, angle, colorList[applicationState.robots.length % colorList.length]) - } else { - applicationState.robots.forEach(robot => { - if (robot.id === item.id) { - // moveRobot(robot, parseFloat(x) - robot.x, parseFloat(y) - robot.y, parseFloat(angle) - robot.angle) - robot.x = x - robot.y = y - robot.angle = angle - } - }) - } - }) - } +// 只有用于加载地图的机器人或地图名称改变时,才重新加载底图。 +watch( + () => getMapIdentity(props.robotList), + (newIdentity, oldIdentity) => { + if (newIdentity && newIdentity !== oldIdentity) loadSourceMap() } ) @@ -458,6 +451,8 @@ defineExpose({ onUnmounted(() => { window.removeEventListener('resize', resizeCanvasToContainer) + cancelAnimationFrame(animationFrameId) + mapLoadVersion += 1 canvasCtx = null })