from __future__ import annotations from dataclasses import dataclass, field from pathlib import Path @dataclass class ServerConfig: """WebSocket服务器配置""" host: str = "0.0.0.0" port: int = 8765 max_ws_size: int = 10_485_760 @dataclass class VideoConfig: """视频帧处理配置""" process_every_n_frames: int = 1 @dataclass class ModelConfig: """姿态检测模型配置""" path: str = "" prefer_gpu: bool = True @property def resolved_path(self) -> str: """返回模型文件的绝对路径""" if self.path: return self.path return str(Path(__file__).resolve().parent.parent / "pose_models" / "pose_landmarker_full.task") @dataclass class DeadBugConfig: """死虫式(Dead Bug)运动检测配置""" visibility_threshold: float = 0.45 extension_confirm_frames: int = 4 reset_confirm_frames: int = 3 @dataclass class AudioConfig: """语音播报配置""" rep_announcer_enabled: bool = True rep_announcer_rate: int = 185 rep_announcer_volume: float = 1.0 @dataclass class LoggingConfig: """日志配置""" dir: str = "logs" rotation: str = "20 MB" retention: str = "14 days" @property def dir_path(self) -> Path: """返回日志目录的绝对路径""" return Path(__file__).resolve().parent.parent / self.dir @dataclass class AppConfig: """应用总配置,聚合所有子配置""" server: ServerConfig = field(default_factory=ServerConfig) video: VideoConfig = field(default_factory=VideoConfig) model: ModelConfig = field(default_factory=ModelConfig) dead_bug: DeadBugConfig = field(default_factory=DeadBugConfig) audio: AudioConfig = field(default_factory=AudioConfig) logging: LoggingConfig = field(default_factory=LoggingConfig)