# server_context.py # -*- coding: utf-8 -*- import asyncio from typing import Dict, List, Set, Deque, Optional, Tuple, Any from collections import deque from concurrent.futures import ThreadPoolExecutor from fastapi import WebSocket class ServerContext: """ 单例模式的服务器全局上下文 用于统一管理状态、资源引用和客户端连接,解决 app_main.py 中 global 变量混乱的问题。 """ _instance = None _lock = asyncio.Lock() # 异步锁,主要用于保护关键状态切换 def __new__(cls): if cls._instance is None: cls._instance = super(ServerContext, cls).__new__(cls) cls._instance._initialized = False return cls._instance def __init__(self): if self._initialized: return self._initialized = True # ====== 1. WebSocket 客户端管理 ====== self.ui_clients: Dict[int, WebSocket] = {} self.camera_viewers: Set[WebSocket] = set() self.imu_ws_clients: Set[WebSocket] = set() self.esp32_audio_ws: Optional[WebSocket] = None self.esp32_camera_ws: Optional[WebSocket] = None # ====== 2. 媒体数据缓冲 ====== self.current_partial: str = "" self.recent_finals: List[str] = [] self.last_frames: Deque[Tuple[float, bytes]] = deque(maxlen=10) # ====== 3. 业务状态标志 (State Flags) ====== # 盲道导航状态 self.navigation_active: bool = False # 过马路导航状态 self.cross_street_active: bool = False # Omni 对话状态 self.omni_conversation_active: bool = False self.omni_previous_nav_state: Optional[str] = None # YOLO 媒体流状态 self.yolomedia_running: bool = False self.yolomedia_sending_frames: bool = False # ====== 4. 核心组件引用 (Resources) ====== # 导航器实例 self.blind_path_navigator = None self.cross_street_navigator = None self.indoor_navigator = None # 协调器 self.orchestrator = None # 模型实例 self.yolo_seg_model = None self.obstacle_detector = None self.indoor_seg_model = None # ====== 5. 异步处理资源 ====== # 帧处理线程池 self.frame_processing_executor = ThreadPoolExecutor(max_workers=3, thread_name_prefix="frame_proc") # 异步帧处理状态 self.nav_processing_task: Optional[asyncio.Task] = None self.nav_last_result_image: Any = None self.nav_last_result_jpeg: Optional[bytes] = None self.nav_pending_frame: Any = None self.nav_processing_lock = asyncio.Lock() self.nav_task_start_time: float = 0.0 def reset_navigation_state(self): """重置所有导航相关的状态标志""" self.navigation_active = False self.cross_street_active = False self.omni_conversation_active = False # 注意:这里不停止 orchestrator,只是重置标志位 def add_ui_client(self, ws: WebSocket): self.ui_clients[id(ws)] = ws def remove_ui_client(self, ws: WebSocket): self.ui_clients.pop(id(ws), None) def get_ui_client_count(self) -> int: return len(self.ui_clients) # 全局访问点 ctx = ServerContext()