config.py 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124
  1. import os
  2. from dataclasses import dataclass
  3. from functools import lru_cache
  4. from dotenv import load_dotenv
  5. load_dotenv()
  6. def _get_bool(name: str, default: bool) -> bool:
  7. value = os.getenv(name)
  8. if value is None:
  9. return default
  10. return value.strip().lower() in {"1", "true", "yes", "on"}
  11. def _get_int(name: str, default: int) -> int:
  12. """从环境变量中读取整数配置。"""
  13. value = os.getenv(name)
  14. if value is None:
  15. return default
  16. try:
  17. return int(value)
  18. except ValueError:
  19. return default
  20. def _get_float(name: str, default: float) -> float:
  21. """从环境变量中读取浮点数配置。"""
  22. value = os.getenv(name)
  23. if value is None:
  24. return default
  25. try:
  26. return float(value)
  27. except ValueError:
  28. return default
  29. @dataclass(frozen=True)
  30. class AppSettings:
  31. """
  32. FastAPI 服务本身的运行配置。
  33. """
  34. title: str
  35. version: str
  36. description: str
  37. host: str
  38. port: int
  39. @classmethod
  40. def from_env(cls) -> "AppSettings":
  41. return cls(
  42. title=os.getenv("APP_TITLE", "Raspberry Pi Camera API"),
  43. version=os.getenv("APP_VERSION", "0.1.0"),
  44. description=os.getenv(
  45. "APP_DESCRIPTION",
  46. "用于树莓派相机抓拍的 FastAPI 服务。",
  47. ),
  48. host=os.getenv("APP_HOST", "0.0.0.0"),
  49. port=_get_int("APP_PORT", 8002)
  50. )
  51. @dataclass(frozen=True)
  52. class CameraSettings:
  53. """
  54. 树莓派相机的集中配置。
  55. """
  56. camera_index: int
  57. width: int
  58. height: int
  59. frame_format: str
  60. jpeg_quality: int
  61. warmup_seconds: float
  62. enable_continuous_autofocus: bool
  63. enable_auto_white_balance: bool
  64. @classmethod
  65. def from_env(cls) -> "CameraSettings":
  66. return cls(
  67. camera_index=_get_int("CAMERA_INDEX", 0),
  68. width=_get_int("CAMERA_WIDTH", 2048),
  69. height=_get_int("CAMERA_HEIGHT", 2048),
  70. frame_format=os.getenv("CAMERA_FRAME_FORMAT", "RGB888"),
  71. jpeg_quality=_get_int("CAMERA_JPEG_QUALITY", 95),
  72. warmup_seconds=_get_float("CAMERA_WARMUP_SECONDS", 0.05),
  73. enable_continuous_autofocus=_get_bool(
  74. "CAMERA_ENABLE_CONTINUOUS_AUTOFOCUS",
  75. True,
  76. ),
  77. enable_auto_white_balance=_get_bool(
  78. "CAMERA_ENABLE_AUTO_WHITE_BALANCE",
  79. True,
  80. ),
  81. )
  82. @dataclass(frozen=True)
  83. class Settings:
  84. """项目总配置入口,统一汇总服务配置与相机配置。"""
  85. app: AppSettings
  86. camera: CameraSettings
  87. @lru_cache(maxsize=1)
  88. def get_settings() -> Settings:
  89. """
  90. 返回单例配置对象。
  91. 使用缓存的原因是:
  92. 1. 避免每次请求都重复读取环境变量。
  93. 2. 让整个项目里的配置来源保持一致。
  94. """
  95. return Settings(
  96. app=AppSettings.from_env(),
  97. camera=CameraSettings.from_env(),
  98. )
  99. settings = get_settings()