fix(playback): 增加库位数据支持,优化回放模式下的数据处理逻辑

This commit is contained in:
xudan 2025-10-28 15:39:40 +08:00
parent 85f7b94313
commit 1fbdb5efcb
3 changed files with 117 additions and 1 deletions

View File

@ -8,7 +8,7 @@ import ws from '../services/ws';
// Define the structure of WebSocket messages for playback // Define the structure of WebSocket messages for playback
type PlaybackMessage = { type PlaybackMessage = {
type: 'AMR' | 'SCENE'; type: 'AMR' | 'SCENE' | 'LOCATION';
timestamp: number; timestamp: number;
data: any; data: any;
}; };
@ -18,6 +18,7 @@ type PlaybackSceneData = {
totalDuration: number; totalDuration: number;
}; };
// Hook's return type definition
// Hook's return type definition // Hook's return type definition
export interface UsePlaybackWebSocketReturn { export interface UsePlaybackWebSocketReturn {
// Connection status // Connection status
@ -31,6 +32,7 @@ export interface UsePlaybackWebSocketReturn {
// Data for rendering // Data for rendering
currentSceneId: Ref<string | null>; currentSceneId: Ref<string | null>;
sceneJson: Ref<string | null>; sceneJson: Ref<string | null>;
locationData: Ref<any[]>; // 新增:用于存储库位数据
// Methods // Methods
connect: (historySceneId: string) => void; connect: (historySceneId: string) => void;
@ -50,6 +52,7 @@ export function usePlaybackWebSocket(editorService: ShallowRef<EditorService | u
const totalDuration = ref(0); const totalDuration = ref(0);
const currentSceneId = ref<string | null>(null); const currentSceneId = ref<string | null>(null);
const sceneJson = ref<string | null>(null); const sceneJson = ref<string | null>(null);
const locationData = ref<any[]>([]); // 新增:库位数据的响应式引用
const { calculateCenterPoint, jumpToPosition } = useViewState(); const { calculateCenterPoint, jumpToPosition } = useViewState();
@ -100,6 +103,9 @@ export function usePlaybackWebSocket(editorService: ShallowRef<EditorService | u
(msg.data as RobotRealtimeInfo[]).forEach((robot) => { (msg.data as RobotRealtimeInfo[]).forEach((robot) => {
latestRobotData.set(robot.id, robot); latestRobotData.set(robot.id, robot);
}); });
} else if (msg.type === 'LOCATION') {
// 新增:处理库位数据
locationData.value = msg.data;
} }
}; };
@ -230,6 +236,7 @@ export function usePlaybackWebSocket(editorService: ShallowRef<EditorService | u
totalDuration, totalDuration,
currentSceneId, currentSceneId,
sceneJson, sceneJson,
locationData, // 新增
connect, connect,
disconnect, disconnect,
play, play,

View File

@ -112,9 +112,12 @@ const playback = usePlaybackWebSocket(editor);
watch(mode, async (newMode) => { watch(mode, async (newMode) => {
if (newMode === 'live') { if (newMode === 'live') {
playback.disconnect(); playback.disconnect();
storageLocationService.value?.setPlaybackMode(false);
await monitorScene(); await monitorScene();
storageLocationService.value?.startMonitoring({ interval: 1 });
} else { } else {
client.value?.close(); client.value?.close();
storageLocationService.value?.setPlaybackMode(true);
playback.connect(props.sid); // Connect once using props.sid playback.connect(props.sid); // Connect once using props.sid
} }
}); });
@ -127,6 +130,44 @@ watch(playback.sceneJson, async (newJson) => {
} }
}); });
watch(playback.locationData, (newData) => {
if (newData) {
storageLocationService.value?.handlePlaybackData(newData);
}
});
let mockLocationInterval: NodeJS.Timeout;
onMounted(() => {
//
if (isPlaybackMode.value) {
let occupied = false;
mockLocationInterval = setInterval(() => {
const mockData = {
type: 'LOCATION',
timestamp: playback.currentTime.value,
data: [
{
id: '666',
label: '1-1',
stationName: 'AP190',
occupied: occupied,
locked: false,
disabled: false,
emptyTray: false,
},
],
};
//
storageLocationService.value?.handlePlaybackData(mockData.data);
occupied = !occupied; //
}, 2000); // 2
}
});
onUnmounted(() => {
clearInterval(mockLocationInterval);
});
watch(selectedDate, (date) => { watch(selectedDate, (date) => {
if (date) { if (date) {
const startOfDayTimestamp = date.startOf('day').valueOf(); const startOfDayTimestamp = date.startOf('day').valueOf();

View File

@ -115,6 +115,7 @@ export class StorageLocationService {
private config: StorageLocationConfig; private config: StorageLocationConfig;
// 渲染调度标记,避免在高频事件中重复 render // 渲染调度标记,避免在高频事件中重复 render
private renderScheduled = false; private renderScheduled = false;
private isPlaybackMode = false;
constructor(editor: EditorService, sceneId: string, config?: Partial<StorageLocationConfig>) { constructor(editor: EditorService, sceneId: string, config?: Partial<StorageLocationConfig>) {
this.editor = editor; this.editor = editor;
@ -472,6 +473,9 @@ export class StorageLocationService {
* @param options * @param options
*/ */
async startMonitoring(options: { interval?: number } = {}) { async startMonitoring(options: { interval?: number } = {}) {
if (this.isPlaybackMode) {
return;
}
this.stopMonitoring(); this.stopMonitoring();
// 监控库位状态 // 监控库位状态
@ -765,6 +769,70 @@ export class StorageLocationService {
this.storageLocations.value.clear(); this.storageLocations.value.clear();
this.editor = null; this.editor = null;
} }
setPlaybackMode(isPlayback: boolean) {
this.isPlaybackMode = isPlayback;
}
handlePlaybackData(data: any[]) {
const stationToPointIdMap = this.buildStationToPointIdMap();
const locationsByPointId = new Map<string, StorageLocationInfo[]>();
data.forEach((location) => {
const pointId = stationToPointIdMap.get(location.stationName);
if (pointId) {
if (!locationsByPointId.has(pointId)) {
locationsByPointId.set(pointId, []);
}
locationsByPointId.get(pointId)!.push({
...location,
is_occupied: location.occupied,
is_locked: location.locked,
is_disabled: location.disabled,
is_empty_tray: location.emptyTray,
layer_name: location.label,
station_name: location.stationName,
});
}
});
const changedPointIds = this.getChangedPointIds(locationsByPointId);
if (changedPointIds.size === 0) {
return;
}
this.storageLocations.value = locationsByPointId;
storageStateMap.clear();
for (const [pointId, list] of locationsByPointId.entries()) {
const inner = new Map<string, StorageState>();
list.forEach((loc) => {
inner.set(loc.layer_name, {
occupied: loc.is_occupied,
locked: loc.is_locked,
disabled: loc.is_disabled,
isEmptyTray: loc.is_empty_tray,
});
});
storageStateMap.set(pointId, inner);
}
this.updatePointBorderColorsOptimized(changedPointIds);
for (const pointId of changedPointIds) {
const pointPen = this.editor?.getPenById(pointId);
const storageNames = pointPen?.point?.associatedStorageLocations;
if (pointPen && Array.isArray(storageNames) && storageNames.length > 0) {
const existing = this.getStorageLocationPens(pointId);
if (existing.length > 0) {
this.update(pointId, storageNames);
} else {
this.create(pointId, storageNames);
}
}
}
this.scheduleRender();
}
} }
// ==================== 新增简化的库位更新API ==================== // ==================== 新增简化的库位更新API ====================