99 lines
3.5 KiB
Python
99 lines
3.5 KiB
Python
# 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()
|