#!/usr/bin/env python # -*- coding: utf-8 -*- """ WebSocket模块 提供WebSocket客户端通信相关功能 """ from typing import List from utils.logger import get_logger logger = get_logger("services.online_script.websocket_module") class VWEDWebSocketModule: """WebSocket模块""" def __init__(self, script_id: str): self.script_id = script_id self._manager = None def _get_manager(self): """获取WebSocket连接管理器(延迟导入)""" if self._manager is None: try: from routes.websocket_api import manager self._manager = manager except ImportError as e: logger.error(f"导入WebSocket管理器失败: {e}") raise Exception(f"WebSocket管理器不可用: {e}") return self._manager def send_msg_to_wsc_by_client_ip(self, msg: str, ip: str) -> None: """ 根据IP向客户端发送消息 Args: msg: 要发送的消息 ip: 接收消息的客户端IP Raises: Exception: 发送失败时抛出异常 """ try: manager = self._get_manager() # 从连接管理器中查找对应IP的WebSocket连接 found_connections = [] # 遍历所有活跃连接,查找匹配IP的连接 for _, connections in manager.active_connections.items(): for websocket in connections: try: # 获取WebSocket客户端IP client_host = websocket.client.host if hasattr(websocket, 'client') else None if client_host == ip: found_connections.append(websocket) except Exception: continue # 同样检查库位状态连接 for _, connections in manager.storage_location_connections.items(): for websocket in connections: try: # 获取WebSocket客户端IP client_host = websocket.client.host if hasattr(websocket, 'client') else None if client_host == ip: found_connections.append(websocket) except Exception: continue if not found_connections: raise Exception(f"未找到IP为 {ip} 的WebSocket客户端连接") # 向找到的所有连接发送消息 import asyncio async def send_async(): failed_count = 0 for websocket in found_connections: try: await manager.send_personal_message(msg, websocket) except Exception as e: failed_count += 1 logger.error(f"向IP {ip} 发送消息失败: {e}") if failed_count == len(found_connections): raise Exception(f"向IP {ip} 的所有连接发送消息都失败") # 检查是否在事件循环中 try: asyncio.get_running_loop() # 如果在事件循环中,创建任务 asyncio.create_task(send_async()) except RuntimeError: # 不在事件循环中,创建新的事件循环 asyncio.run(send_async()) logger.info(f"成功向IP {ip} 发送消息: {msg}") except Exception as e: logger.error(f"根据IP发送WebSocket消息失败: {e}") raise e def send_msg_to_wsc_by_client_name(self, msg: str, client_name: str) -> None: """ 根据客户端名称向客户端发送消息 Args: msg: 要发送的消息 client_name: 接收消息的客户端名称 Raises: Exception: 发送失败时抛出异常 """ try: manager = self._get_manager() # 从连接管理器中查找对应名称的WebSocket连接 found_connections = [] # 遍历所有活跃连接,查找匹配名称的连接 # 注意:当前的ConnectionManager没有存储客户端名称信息 # 这里使用task_record_id作为客户端标识符 for task_record_id, connections in manager.active_connections.items(): if task_record_id == client_name: found_connections.extend(connections) # 同样检查库位状态连接,使用scene_id作为客户端标识符 for scene_id, connections in manager.storage_location_connections.items(): if scene_id == client_name: found_connections.extend(connections) if not found_connections: raise Exception(f"未找到名称为 {client_name} 的WebSocket客户端连接") # 向找到的所有连接发送消息 import asyncio async def send_async(): failed_count = 0 for websocket in found_connections: try: await manager.send_personal_message(msg, websocket) except Exception as e: failed_count += 1 logger.error(f"向客户端 {client_name} 发送消息失败: {e}") if failed_count == len(found_connections): raise Exception(f"向客户端 {client_name} 的所有连接发送消息都失败") # 检查是否在事件循环中 try: asyncio.get_running_loop() # 如果在事件循环中,创建任务 asyncio.create_task(send_async()) except RuntimeError: # 不在事件循环中,创建新的事件循环 asyncio.run(send_async()) logger.info(f"成功向客户端 {client_name} 发送消息: {msg}") except Exception as e: logger.error(f"根据客户端名称发送WebSocket消息失败: {e}") raise e def get_websocket_client_ip(self) -> List[str]: """ 获取所有WebSocket客户端的IP Returns: 包含所有客户端IP的字符串列表 Raises: Exception: 获取失败时抛出异常 """ try: manager = self._get_manager() client_ips = [] # 遍历所有活跃连接,收集客户端IP for _, connections in manager.active_connections.items(): for websocket in connections: try: # 获取WebSocket客户端IP client_host = websocket.client.host if hasattr(websocket, 'client') else None if client_host and client_host not in client_ips: client_ips.append(client_host) except Exception: continue # 同样检查库位状态连接 for _, connections in manager.storage_location_connections.items(): for websocket in connections: try: # 获取WebSocket客户端IP client_host = websocket.client.host if hasattr(websocket, 'client') else None if client_host and client_host not in client_ips: client_ips.append(client_host) except Exception: continue logger.info(f"获取到 {len(client_ips)} 个WebSocket客户端IP") return client_ips except Exception as e: logger.error(f"获取WebSocket客户端IP失败: {e}") raise e def get_websocket_client_name(self) -> List[str]: """ 获取所有WebSocket客户端名称 Returns: 包含所有客户端名称的字符串列表 Raises: Exception: 获取失败时抛出异常 """ try: manager = self._get_manager() client_names = [] # 收集任务记录连接的客户端名称(使用task_record_id作为名称) client_names.extend(list(manager.active_connections.keys())) # 收集库位状态连接的客户端名称(使用scene_id作为名称) client_names.extend(list(manager.storage_location_connections.keys())) # 去重 client_names = list(set(client_names)) logger.info(f"获取到 {len(client_names)} 个WebSocket客户端名称") return client_names except Exception as e: logger.error(f"获取WebSocket客户端名称失败: {e}") raise e