/** * 自动门点服务 * 提供自动门点的WebSocket数据处理功能 * 注:模拟数据功能已被注释,现在主要用于处理真实WebSocket推送数据 */ import { type MapPen, MapPointType } from '@api/map'; import type { EditorService } from './editor.service'; /** * 自动门点模拟数据配置 */ export interface AutoDoorSimulationConfig { /** 设备ID,需要与地图中自动门点的deviceId匹配 */ deviceId: string; /** 设备标签 */ label?: string; /** 状态切换间隔(毫秒),默认3000ms */ interval?: number; /** 初始状态,0=关门,1=开门,默认0 */ initialStatus?: 0 | 1; /** 是否启用控制台日志 */ enableLogging?: boolean; } /** * 自动门点状态数据结构(模拟WebSocket推送的数据格式) */ interface AutoDoorStatusData { gid: string; id: string; label: string; brand: null; type: 99; // 自动门点类型标识 ip: null; battery: number; isConnected: boolean; state: number; canOrder: boolean; canStop: null; canControl: boolean; targetPoint: null; deviceStatus: 0 | 1; // 设备状态:0=关门,1=开门 x: number; y: number; active: boolean; angle: number; isWaring: null; isFault: null; isLoading: null; path: []; } /** * WebSocket推送的自动门点数据接口 */ export interface AutoDoorWebSocketData { gid: string; id: string; // 设备ID label: string; brand: null; type: 99; // 自动门点类型标识 ip: null; battery: number; isConnected: boolean; state: number; canOrder: boolean; canStop: null; canControl: boolean; targetPoint: null; deviceStatus: 0 | 1; // 设备状态:0=关门,1=开门 x: number; y: number; active: boolean; angle: number; isWaring: null; isFault: null; isLoading: null; path: []; } /** * 自动门点服务 * 主要用于WebSocket数据处理,模拟功能保留但默认不使用 */ export class AutoDoorService { private timers = new Map(); private statusMap = new Map(); private editorService: EditorService | null = null; private latestAutoDoorData = new Map(); // 设备ID到自动门点ID的映射缓存,避免每次都遍历查找 private deviceIdToPointIdMap = new Map(); /** * 设置编辑器服务实例并初始化自动门点映射 * @param editor 编辑器服务实例 */ setEditorService(editor: EditorService): void { this.editorService = editor; // 初始化设备ID到点位ID的映射关系 this.initializeDeviceMapping(); } /** * 初始化设备ID到自动门点ID的映射关系 * 在场景加载后调用,建立高效的查找缓存 */ private initializeDeviceMapping(): void { if (!this.editorService) { console.warn('⚠️ 编辑器服务未设置,无法初始化自动门点映射'); return; } // 清空现有映射 this.deviceIdToPointIdMap.clear(); // 遍历所有点位,找出自动门点并建立映射 const pens = this.editorService.data().pens; let autoDoorCount = 0; pens.forEach((pen) => { if (pen.name === 'point' && (pen as MapPen).point?.type === MapPointType.自动门点) { const deviceId = (pen as MapPen).point?.deviceId; if (deviceId && pen.id) { this.deviceIdToPointIdMap.set(deviceId, pen.id); autoDoorCount++; } } }); console.log(`🚪 自动门点映射初始化完成: 共找到 ${autoDoorCount} 个自动门点`); } /** * 手动刷新设备映射(当场景发生变化时调用) */ refreshDeviceMapping(): void { this.initializeDeviceMapping(); } /** * 启动自动门点模拟(已停用,保留接口用于兼容性) * 注:当前使用真实WebSocket数据,此方法仅在测试时启用 * @param config 模拟配置 */ startSimulation(config: AutoDoorSimulationConfig): void { const { deviceId, interval = 3000, initialStatus = 0, enableLogging = true } = config; // 如果已经存在相同设备ID的模拟,先停止它 this.stopSimulation(deviceId); if (enableLogging) { console.log(`🚪 启动自动门点模拟: ${deviceId}`); } // 设置初始状态 this.statusMap.set(deviceId, initialStatus); // 创建定时器 const timer = setInterval(() => { const currentStatus = this.statusMap.get(deviceId) ?? 0; const newStatus = currentStatus === 0 ? 1 : 0; // 更新状态 this.statusMap.set(deviceId, newStatus); // 注意:这里不需要创建完整的模拟数据对象,只需要更新状态 if (enableLogging) { console.log(`🚪 自动门点状态更新: ${newStatus === 0 ? '关门(红色)' : '开门(蓝色)'} (deviceId: ${deviceId})`); } // 更新编辑器中的自动门点状态 if (this.editorService) { this.editorService.updateAutoDoorByDeviceId(deviceId, newStatus, true); } else { console.warn('⚠️ 编辑器服务未设置,无法更新自动门点状态'); } }, interval); // 保存定时器引用 this.timers.set(deviceId, timer); } /** * 停止指定设备的模拟(已停用,保留接口用于兼容性) * @param deviceId 设备ID */ stopSimulation(deviceId: string): void { const timer = this.timers.get(deviceId); if (timer) { clearInterval(timer); this.timers.delete(deviceId); this.statusMap.delete(deviceId); console.log(`🚪 停止自动门点模拟: ${deviceId}`); } } /** * 停止所有模拟(已停用,保留接口用于兼容性) */ stopAllSimulations(): void { for (const deviceId of this.timers.keys()) { this.stopSimulation(deviceId); } console.log('🚪 停止所有自动门点模拟'); } /** * 获取当前正在模拟的设备列表 */ getActiveSimulations(): string[] { return Array.from(this.timers.keys()); } /** * 获取指定设备的当前状态 * @param deviceId 设备ID */ getCurrentStatus(deviceId: string): 0 | 1 | undefined { return this.statusMap.get(deviceId); } /** * 手动设置设备状态 * @param deviceId 设备ID * @param status 设备状态 */ setDeviceStatus(deviceId: string, status: 0 | 1): void { this.statusMap.set(deviceId, status); if (this.editorService) { this.editorService.updateAutoDoorByDeviceId(deviceId, status, true); console.log(`🚪 手动设置自动门点状态: ${status === 0 ? '关门(红色)' : '开门(蓝色)'} (deviceId: ${deviceId})`); } } /** * 启动多个自动门点模拟 * @param configs 多个模拟配置 */ startMultipleSimulations(configs: AutoDoorSimulationConfig[]): void { configs.forEach((config) => this.startSimulation(config)); } /** * 处理WebSocket推送的自动门点数据 * @param data WebSocket推送的数据 */ handleWebSocketData(data: AutoDoorWebSocketData): void { const { label: deviceId, deviceStatus, active = true } = data; if (!deviceId || deviceStatus === undefined) { console.warn('⚠️ 自动门点数据格式不正确', data); return; } // 缓存最新数据 this.latestAutoDoorData.set(deviceId, { deviceId, deviceStatus, active }); console.log( `🚪 收到自动门点WebSocket数据: ${deviceStatus === 0 ? '关门(红色)' : '开门(蓝色)'} (deviceId: ${deviceId})`, ); } /** * 处理缓冲的自动门点数据(在渲染循环中调用) * @param frameBudget 帧预算(毫秒) * @param startTime 开始时间 * @returns 是否还有待处理的数据 */ processBufferedData(frameBudget: number, startTime: number): boolean { // 在时间预算内,持续处理自动门点数据 while (performance.now() - startTime < frameBudget && this.latestAutoDoorData.size > 0) { // 获取并移除 Map 中的第一条自动门点数据 const entry = this.latestAutoDoorData.entries().next().value; if (!entry) break; const [deviceId, data] = entry; this.latestAutoDoorData.delete(deviceId); // 使用映射缓存快速查找点位ID const pointId = this.deviceIdToPointIdMap.get(data.deviceId); if (!pointId) { console.warn(`⚠️ 未找到设备ID ${data.deviceId} 对应的自动门点,可能需要刷新映射`); continue; } // 更新自动门点状态(使用pointId直接更新,避免查找) if (this.editorService) { this.editorService.updateAutoDoorByDeviceId(data.deviceId, data.deviceStatus, data.active, pointId); } } // 返回是否还有待处理的数据 return this.latestAutoDoorData.size > 0; } /** * 获取当前缓冲数据数量 */ getBufferedDataCount(): number { return this.latestAutoDoorData.size; } /** * 清空缓冲数据 */ clearBufferedData(): void { this.latestAutoDoorData.clear(); } /** * 获取映射关系数量 */ getMappingCount(): number { return this.deviceIdToPointIdMap.size; } /** * 检查设备ID是否有对应的自动门点 */ hasDeviceMapping(deviceId: string): boolean { return this.deviceIdToPointIdMap.has(deviceId); } /** * 获取所有已映射的设备ID列表 */ getMappedDeviceIds(): string[] { return Array.from(this.deviceIdToPointIdMap.keys()); } /** * 创建模拟数据 * @param deviceId 设备ID * @param label 设备标签 * @param deviceStatus 设备状态 */ private createMockData(deviceId: string, label: string, deviceStatus: 0 | 1): AutoDoorStatusData { return { gid: '', id: deviceId, label, brand: null, type: 99, ip: null, battery: 0, isConnected: true, state: 0, canOrder: false, canStop: null, canControl: false, targetPoint: null, deviceStatus, x: 0, y: 0, active: true, angle: 0, isWaring: null, isFault: null, isLoading: null, path: [], }; } /** * 销毁服务,清理所有资源 */ destroy(): void { this.stopAllSimulations(); this.clearBufferedData(); this.deviceIdToPointIdMap.clear(); this.editorService = null; console.log('🚪 自动门点服务已销毁'); } } /** * 自动门点服务单例 */ export const autoDoorService = new AutoDoorService(); // 保持向后兼容 export const autoDoorSimulationService = autoDoorService;