import os from dataclasses import dataclass from functools import lru_cache from dotenv import load_dotenv load_dotenv() def _get_bool(name: str, default: bool) -> bool: value = os.getenv(name) if value is None: return default return value.strip().lower() in {"1", "true", "yes", "on"} def _get_int(name: str, default: int) -> int: """从环境变量中读取整数配置。""" value = os.getenv(name) if value is None: return default try: return int(value) except ValueError: return default def _get_float(name: str, default: float) -> float: """从环境变量中读取浮点数配置。""" value = os.getenv(name) if value is None: return default try: return float(value) except ValueError: return default @dataclass(frozen=True) class AppSettings: """ FastAPI 服务本身的运行配置。 """ title: str version: str description: str host: str port: int @classmethod def from_env(cls) -> "AppSettings": return cls( title=os.getenv("APP_TITLE", "Raspberry Pi Camera API"), version=os.getenv("APP_VERSION", "0.1.0"), description=os.getenv( "APP_DESCRIPTION", "用于树莓派相机抓拍的 FastAPI 服务。", ), host=os.getenv("APP_HOST", "0.0.0.0"), port=_get_int("APP_PORT", 8002) ) @dataclass(frozen=True) class CameraSettings: """ 树莓派相机的集中配置。 """ camera_index: int width: int height: int frame_format: str jpeg_quality: int warmup_seconds: float enable_continuous_autofocus: bool enable_auto_white_balance: bool @classmethod def from_env(cls) -> "CameraSettings": return cls( camera_index=_get_int("CAMERA_INDEX", 0), width=_get_int("CAMERA_WIDTH", 2048), height=_get_int("CAMERA_HEIGHT", 2048), frame_format=os.getenv("CAMERA_FRAME_FORMAT", "RGB888"), jpeg_quality=_get_int("CAMERA_JPEG_QUALITY", 95), warmup_seconds=_get_float("CAMERA_WARMUP_SECONDS", 0.05), enable_continuous_autofocus=_get_bool( "CAMERA_ENABLE_CONTINUOUS_AUTOFOCUS", True, ), enable_auto_white_balance=_get_bool( "CAMERA_ENABLE_AUTO_WHITE_BALANCE", True, ), ) @dataclass(frozen=True) class Settings: """项目总配置入口,统一汇总服务配置与相机配置。""" app: AppSettings camera: CameraSettings @lru_cache(maxsize=1) def get_settings() -> Settings: """ 返回单例配置对象。 使用缓存的原因是: 1. 避免每次请求都重复读取环境变量。 2. 让整个项目里的配置来源保持一致。 """ return Settings( app=AppSettings.from_env(), camera=CameraSettings.from_env(), ) settings = get_settings()