feat: 站点 JSON、语音终端 WebSocket 指派与客户端联调
- 用 OR_SITE_CONFIG_JSON_FILE 统一术间配置(video_rtsp_urls + voice_or_room_bindings) - VoiceTerminalHub:assignment、WS 推送与 HTTP 查询;开录/停录后 notify - 一键联调 orchestrate-and-start 与 /client/surgeries/start 共用指派逻辑,修复 demo 路径不发 WS - 语音桌面端:SIGINT 退出、shutdown 清理、仅 WS 指派、固定 pending 轮询间隔、界面仅保留录音时长 - 新增/调整契约与绑定测试,文档与示例配置同步 Made-with: Cursor
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import json
|
||||
from __future__ import annotations
|
||||
|
||||
from pathlib import Path
|
||||
from urllib.parse import quote_plus
|
||||
from typing import Any, Literal
|
||||
@@ -7,6 +8,7 @@ from pydantic import Field
|
||||
from pydantic_settings import BaseSettings, SettingsConfigDict
|
||||
|
||||
from app.baked import algorithm as baked_algorithm
|
||||
from app.or_site_config import OrSiteConfig
|
||||
|
||||
|
||||
class _SettingsGroup:
|
||||
@@ -39,8 +41,7 @@ class _VideoGroup(_SettingsGroup):
|
||||
"video_default_backend",
|
||||
"video_camera_backend_overrides_json",
|
||||
"video_rtsp_url_template",
|
||||
"video_rtsp_urls_json",
|
||||
"video_rtsp_urls_json_file",
|
||||
"or_site_config_json_file",
|
||||
)
|
||||
|
||||
|
||||
@@ -135,8 +136,8 @@ class Settings(BaseSettings):
|
||||
video_default_backend: Literal["rtsp", "hikvision_sdk", "auto"] = "rtsp"
|
||||
video_camera_backend_overrides_json: str = ""
|
||||
video_rtsp_url_template: str = ""
|
||||
video_rtsp_urls_json: str = ""
|
||||
video_rtsp_urls_json_file: str = ""
|
||||
#: 手术室站点配置(UTF-8 JSON):须含 video_rtsp_urls 与 voice_or_room_bindings,见 or_site_config.sample.json
|
||||
or_site_config_json_file: str = ""
|
||||
|
||||
hikvision_lib_dir: str = "/opt/hikvision/lib"
|
||||
hikvision_sdk_enabled: bool = False
|
||||
@@ -236,45 +237,27 @@ class Settings(BaseSettings):
|
||||
and self.minio_bucket.strip()
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def _parse_rtsp_urls_object(raw: str) -> dict[str, str]:
|
||||
raw = (raw or "").strip()
|
||||
if not raw:
|
||||
return {}
|
||||
try:
|
||||
data: Any = json.loads(raw)
|
||||
except json.JSONDecodeError as exc:
|
||||
raise ValueError(f"Invalid VIDEO_RTSP_URLS_JSON: {exc}") from exc
|
||||
if not isinstance(data, dict):
|
||||
raise ValueError("VIDEO_RTSP_URLS_JSON must be a JSON object")
|
||||
return {str(k): str(v) for k, v in data.items()}
|
||||
def load_or_site_config(self) -> OrSiteConfig | None:
|
||||
"""解析 ``or_site_config_json_file``;未配置路径时返回 ``None``。"""
|
||||
from app.or_site_config import load_or_site_config_from_path
|
||||
|
||||
path_raw = (self.or_site_config_json_file or "").strip()
|
||||
if not path_raw:
|
||||
return None
|
||||
path = Path(path_raw).expanduser()
|
||||
if not path.is_file():
|
||||
raise ValueError(f"OR_SITE_CONFIG_JSON_FILE is set but file not found: {path}")
|
||||
return load_or_site_config_from_path(path)
|
||||
|
||||
def video_rtsp_url_map(self) -> dict[str, str]:
|
||||
merged: dict[str, str] = {}
|
||||
path_raw = (self.video_rtsp_urls_json_file or "").strip()
|
||||
if path_raw:
|
||||
path = Path(path_raw).expanduser()
|
||||
if not path.is_file():
|
||||
raise ValueError(
|
||||
f"VIDEO_RTSP_URLS_JSON_FILE is set but file not found: {path}"
|
||||
)
|
||||
try:
|
||||
file_obj: Any = json.loads(path.read_text(encoding="utf-8"))
|
||||
except json.JSONDecodeError as exc:
|
||||
raise ValueError(
|
||||
f"Invalid JSON in VIDEO_RTSP_URLS_JSON_FILE {path}: {exc}"
|
||||
) from exc
|
||||
if not isinstance(file_obj, dict):
|
||||
raise ValueError(
|
||||
f"VIDEO_RTSP_URLS_JSON_FILE must contain a JSON object: {path}"
|
||||
)
|
||||
merged = {str(k): str(v) for k, v in file_obj.items()}
|
||||
merged.update(self._parse_rtsp_urls_object(self.video_rtsp_urls_json))
|
||||
return merged
|
||||
cfg = self.load_or_site_config()
|
||||
if cfg is None:
|
||||
return {}
|
||||
return dict(cfg.video_rtsp_urls)
|
||||
|
||||
@property
|
||||
def camera_rtsp_urls_sample_path(self) -> str:
|
||||
return baked_algorithm.default_camera_rtsp_urls_sample_path()
|
||||
def or_site_config_sample_path(self) -> str:
|
||||
return baked_algorithm.default_or_site_config_sample_path()
|
||||
|
||||
@property
|
||||
def video(self) -> _VideoGroup:
|
||||
|
||||
Reference in New Issue
Block a user