fix: 更新开发环境token,优化机器人图元批量更新逻辑

This commit is contained in:
xudan 2025-10-30 09:39:45 +08:00
parent 8e7f00ec50
commit 149fcb5a37
4 changed files with 111 additions and 190 deletions

View File

@ -4,5 +4,5 @@ ENV_WEBSOCKET_BASE=/ws
ENV_STORAGE_WEBSOCKET_BASE=/vwedWs
# 开发环境token配置 - 可以手动设置或从另一个项目获取后填入
ENV_DEV_TOKEN=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE3NjE2NzY1NDksInVzZXJuYW1lIjoiYWRtaW4ifQ.q0SIqem1KTUfPm9wgjAG8YG6UmYiIcs_QV6AEJVmr8U
ENV_DEV_TOKEN=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE3NjE5MTYzNTIsInVzZXJuYW1lIjoiYWRtaW4ifQ.UEDfpkrB6y1xuNQt5AEKAnAyeEb4HKI8VC0hDXm4r9E
ENV_DEV_TENANT_ID=1000

View File

@ -1,4 +1,3 @@
import { LockState } from '@meta2d/core';
import { message } from 'ant-design-vue';
import { type Ref, ref, type ShallowRef, shallowRef } from 'vue';
@ -6,6 +5,7 @@ import { type RobotRealtimeInfo } from '../apis/robot';
import type { EditorService } from '../services/editor.service';
import { useViewState } from '../services/useViewState';
import ws from '../services/ws';
import { useRobotRenderer } from './useRobotRenderer';
// Define the structure of WebSocket messages for playback
type PlaybackMessage = {
@ -14,10 +14,6 @@ type PlaybackMessage = {
data: any;
};
type PlaybackSceneData = {
json: string;
totalDuration: number;
};
// Hook's return type definition
// Hook's return type definition
@ -60,6 +56,7 @@ export function usePlaybackWebSocket(
let hasReceivedAmrData = false; // 新增:用于确保 onFirstAmrData 只调用一次的标志
const { calculateCenterPoint, jumpToPosition } = useViewState();
const { batchUpdateRobots } = useRobotRenderer(editorService);
const locateCenter = () => {
if (editorService.value) {
@ -174,85 +171,6 @@ export function usePlaybackWebSocket(
console.warn(`播放速度控制 (${speed}x) 尚未实现`);
};
const batchUpdateRobots = (updates: Array<{ id: string; data: RobotRealtimeInfo }>) => {
const editor = editorService.value;
if (!editor || updates.length === 0) return;
const allPenUpdates: any[] = [];
updates.forEach(({ id, data }) => {
const robotExists = editor.checkRobotById(id);
if (robotExists) {
const { x, y, angle, active, path: points, isWaring, isFault, isCharging, isCarrying, canOrder, ...rest } =
data;
// 1. 更新机器人缓存的业务数据 (与实时模式对齐)
editor.updateRobot(id, {
...rest,
isCharging: (isCharging as any) ?? 0,
isCarrying: (isCarrying as any) ?? 0,
canOrder: false as any,
});
// 2. 准备图元更新负载对象
const penUpdatePayload: any = { id };
const robotState: any = {};
// 2.1 路径处理 (如果数据中包含)
if (points?.length) {
const cx = x || 37;
const cy = y || 37;
robotState.path = points.map((p) => ({ x: p.x - cx, y: p.y - cy }));
}
// 2.2 合并其他机器人状态
if (active !== undefined) robotState.active = active||true;
if (isWaring !== undefined) robotState.isWaring = isWaring;
if (isFault !== undefined) robotState.isFault = isFault;
if (isCharging !== undefined) robotState.isCharging = isCharging;
if (isCarrying !== undefined) robotState.isCarrying = isCarrying;
if (canOrder !== undefined) robotState.canOrder = canOrder;
if (Object.keys(robotState).length > 0) {
penUpdatePayload.robot = robotState;
}
// 2.3 合并位置、可见性和角度
if (x != null && y != null) {
penUpdatePayload.x = x - 60;
penUpdatePayload.y = y - 60;
penUpdatePayload.visible = true; // [核心修复] 明确设置 visible 为 true
penUpdatePayload.locked = LockState.None;
}
if (angle != null) {
penUpdatePayload.rotate = -angle + 180;
}
// 仅当有实际更新时才推入数组
if (Object.keys(penUpdatePayload).length > 1) {
allPenUpdates.push(penUpdatePayload);
}
}
});
// 4. 批量更新所有图元
if (allPenUpdates.length > 0) {
allPenUpdates.forEach((update) => {
editor.setValue(update, { render: false, history: false, doEvent: false });
});
// 5. [时序问题修复] 在所有图元属性更新后,再统一更新状态覆盖图标
// 这样可以确保 updateRobotStatusOverlay 读取到最新的 visible: true 状态
allPenUpdates.forEach((update) => {
const { id, x, y, rotate } = update;
const newPositionForOverlay = x !== undefined && y !== undefined ? { x, y, rotate } : undefined;
editor.updateRobotStatusOverlay?.(id, false, newPositionForOverlay);
});
}
// 5. 统一渲染
editor.render();
};
const renderLoop = () => {
try {

View File

@ -0,0 +1,105 @@
import { LockState } from '@meta2d/core';
import { isNil } from 'lodash-es';
import type { ShallowRef } from 'vue';
import type { RobotRealtimeInfo } from '../apis/robot';
import type { EditorService } from '../services/editor.service';
/**
*
* @param editorService - shallowRef
* @returns batchUpdateRobots
*/
export function useRobotRenderer(editorService: ShallowRef<EditorService | undefined>) {
/**
*
* @param updates
*/
const batchUpdateRobots = (updates: Array<{ id: string; data: RobotRealtimeInfo }>) => {
const editor = editorService.value;
if (!editor || updates.length === 0) return;
// 用于收集所有图元pen的更新数据
const allPenUpdates: any[] = [];
updates.forEach(({ id, data }) => {
const {
x,
y,
active,
angle,
path: points,
isWaring,
isFault,
isCharging = 0,
isCarrying = 0,
canOrder,
...rest
} = data;
// 1. 更新机器人缓存的业务数据
editor.updateRobot(id, {
...rest,
isCharging: isCharging as any,
isCarrying: isCarrying as any,
canOrder: canOrder as any,
});
// 2. 准备图元pen的更新负载对象将多个更新合并
const penUpdatePayload: any = { id };
const robotState: any = {};
// 2.1 处理路径并将其放入 robotState
if (points?.length) {
const cx = x ?? 37;
const cy = y ?? 37;
robotState.path = points.map((p) => ({ x: p.x - cx, y: p.y - cy }));
}
// 2.2 合并其他机器人状态
if (active !== undefined) robotState.active = active;
if (isWaring !== undefined) robotState.isWaring = isWaring;
if (isFault !== undefined) robotState.isFault = isFault;
if (isCharging !== undefined) robotState.isCharging = isCharging;
if (isCarrying !== undefined) robotState.isCarrying = isCarrying;
if (canOrder !== undefined) robotState.canOrder = canOrder;
if (Object.keys(robotState).length > 0) {
penUpdatePayload.robot = robotState;
}
// 2.3 合并位置、可见性和角度
if (!isNil(x) && !isNil(y)) {
penUpdatePayload.x = x - 60;
penUpdatePayload.y = y - 60;
penUpdatePayload.visible = true;
penUpdatePayload.locked = LockState.None;
}
if (angle != null) {
penUpdatePayload.rotate = -angle + 180;
}
if (Object.keys(penUpdatePayload).length > 1) {
allPenUpdates.push(penUpdatePayload);
}
const newPositionForOverlay =
penUpdatePayload.x !== undefined && penUpdatePayload.y !== undefined
? { x: penUpdatePayload.x, y: penUpdatePayload.y, rotate: penUpdatePayload.rotate }
: undefined;
editor.updateRobotStatusOverlay?.(id, false, newPositionForOverlay);
});
if (allPenUpdates.length > 0) {
allPenUpdates.forEach((update) => {
editor.setValue(update, { render: false, history: false, doEvent: false });
});
}
editor.render();
};
return {
batchUpdateRobots,
};
}

View File

@ -16,6 +16,7 @@ import FollowViewNotification from '../components/follow-view-notification.vue';
import PlaybackController from '../components/PlaybackController.vue';
import RobotLabels from '../components/robot-labels.vue';
import { usePlaybackWebSocket } from '../hooks/usePlaybackWebSocket';
import { useRobotRenderer } from '../hooks/useRobotRenderer';
import { autoDoorSimulationService, type AutoDoorWebSocketData } from '../services/auto-door-simulation.service';
import {
type ContextMenuState,
@ -111,6 +112,7 @@ const playback = usePlaybackWebSocket(editor, async () => {
// []
await editor.value?.initRobots();
});
const { batchUpdateRobots: batchUpdateRobotsRenderer } = useRobotRenderer(editor);
watch(mode, async (newMode) => {
if (newMode === 'live') {
@ -276,110 +278,6 @@ const monitorScene = async () => {
// requestAnimationFrame ID便 WebSocket
let animationFrameId: number;
/**
* 批量更新机器人数据减少渲染调用次数
* @param updates 需要更新的机器人数据数组
*/
const batchUpdateRobots = (updates: Array<{ id: string; data: RobotRealtimeInfo }>) => {
console.log('[实时模式] batchUpdateRobots 被调用,更新数量:', updates.length);
if (!editor.value || updates.length === 0) return;
// pen
const allPenUpdates: any[] = [];
updates.forEach(({ id, data }) => {
const {
x,
y,
active,
angle,
path: points,
isWaring,
isFault,
isCharging = 0,
isCarrying = 0,
canOrder,
...rest
} = data;
// 1.
console.log('[实时模式] 更新机器人业务数据:', id, { x, y, active, angle });
editor.value?.updateRobot(id, {
...rest,
isCharging: isCharging as any,
isCarrying: isCarrying as any,
canOrder: canOrder as any,
});
// 2. pen
const penUpdatePayload: any = { id };
const robotState: any = {};
// 2.1 robotState
// refreshRobot
if (points?.length && !isMonitorMode.value) {
//
const cx = x || 37; // X37
const cy = y || 37; // Y37
robotState.path = points.map((p) => ({ x: p.x - cx, y: p.y - cy }));
}
// 2.2
if (active !== undefined) robotState.active = active;
if (isWaring !== undefined) robotState.isWaring = isWaring;
if (isFault !== undefined) robotState.isFault = isFault;
if (isCharging !== undefined) robotState.isCharging = isCharging;
if (isCarrying !== undefined) robotState.isCarrying = isCarrying;
if (canOrder !== undefined) robotState.canOrder = canOrder;
// payload
if (Object.keys(robotState).length > 0) {
penUpdatePayload.robot = robotState;
}
// 2.3
if (!isNil(x) && !isNil(y)) {
penUpdatePayload.x = x - 60;
penUpdatePayload.y = y - 60;
penUpdatePayload.visible = true;
penUpdatePayload.locked = LockState.None;
console.log('[实时模式] 设置机器人可见性:', id, { x: x - 60, y: y - 60, visible: true });
}
if (angle != null) {
penUpdatePayload.rotate = -angle + 180;
}
//
if (Object.keys(penUpdatePayload).length > 1) {
allPenUpdates.push(penUpdatePayload);
}
// 3. (API)
// 使
const newPositionForOverlay =
penUpdatePayload.x !== undefined && penUpdatePayload.y !== undefined
? { x: penUpdatePayload.x, y: penUpdatePayload.y, rotate: penUpdatePayload.rotate }
: undefined;
editor.value?.updateRobotStatusOverlay?.(id, false, newPositionForOverlay);
});
// 4. 使Meta2D
if (allPenUpdates.length > 0) {
allPenUpdates.forEach((update) => {
editor.value?.setValue(update, { render: false, history: false, doEvent: false });
});
}
// 5.
/*
为了让机器人在地图上最基本地被绘制出来并能够移动后台推送的 WebSocket 数据最少需要 id, x, y 3
个字段
为了达到一个功能上比较完整的视觉效果带光圈能旋转则最少需要 id, x, y, angle, active 5
个字段
*/
editor.value?.render();
};
/**
* 渲染循环函数
@ -411,7 +309,7 @@ const monitorScene = async () => {
//
if (updates.length > 0) {
batchUpdateRobots(updates);
batchUpdateRobotsRenderer(updates);
}
//