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:
@@ -7,7 +7,7 @@
|
||||
## 环境
|
||||
|
||||
- `GET /health` 返回 `200`,`database: connected`
|
||||
- 环境变量:`VIDEO_RTSP_URLS_JSON` 或 `VIDEO_RTSP_URLS_JSON_FILE` 与客户端 `camera_ids` 一致
|
||||
- 环境变量:`OR_SITE_CONFIG_JSON_FILE` 中 `video_rtsp_urls` 与客户端 `camera_ids` 一致;`voice_or_room_bindings` 与现场术间一致
|
||||
- `MINIO_*`、`BAIDU_APP_ID` / `BAIDU_API_KEY` / `BAIDU_SECRET_KEY` 已配置(语音确认链路)
|
||||
- 模型权重在镜像/挂载中可读(默认路径见 `app/baked/algorithm.py` 与仓库 `app/resources/*.pt`)
|
||||
|
||||
|
||||
@@ -8,10 +8,9 @@
|
||||
|
||||
## RTSP 模式(默认)
|
||||
|
||||
1. 配置 `**camera_id` → RTSP URL** 映射,任选其一或组合使用:
|
||||
- `**VIDEO_RTSP_URLS_JSON_FILE`**:指向 UTF-8 JSON 文件(对象键为与请求一致的 `camera_id`)。仓库示例:`[app/resources/camera_rtsp_urls.sample.json](../app/resources/camera_rtsp_urls.sample.json)`(示例 ID:`or-cam-01`、`or-cam-02`)。
|
||||
- `**VIDEO_RTSP_URLS_JSON**`:内联 JSON 字符串;与文件合并时**覆盖同键**。
|
||||
- `**VIDEO_RTSP_URL_TEMPLATE`**:单模板,可用 `{camera_id}`。
|
||||
1. 配置 `**camera_id` → RTSP URL** 映射:
|
||||
- `**OR_SITE_CONFIG_JSON_FILE**`(推荐):UTF-8 JSON 文件,**仅支持**站点对象:`{"video_rtsp_urls":{...},"voice_or_room_bindings":[...]}`。根级只允许这两个键;`voice_or_room_bindings` 可为 `[]`。见 `[app/resources/or_site_config.sample.json](../app/resources/or_site_config.sample.json)`。服务每次解析映射时会重新读文件,便于联调覆盖 `video_rtsp_urls`。
|
||||
- `**VIDEO_RTSP_URL_TEMPLATE**`(可选):单模板字符串,可用 `{camera_id}`;在 `video_rtsp_urls` 未给出某路时使用。
|
||||
2. 调用 `POST /client/surgeries/start` 时,`camera_ids` 必须能在上述配置中解析出 RTSP 地址。
|
||||
3. **开录确认**:每路摄像头在超时内成功打开并读到**首帧**后,才认为该路已开录。
|
||||
|
||||
@@ -26,7 +25,7 @@ SDK **不作为构建期依赖**:将厂商提供的 Linux x86_64 动态库挂
|
||||
行为概要:
|
||||
|
||||
1. 进程内对 `NET_DVR_Init` 使用引用计数;每路使用 SDK 的工作线程在登录后 `NET_DVR_Logout`,线程结束时配对 `NET_DVR_Cleanup`。
|
||||
2. 若 `HIKVISION_SDK_FALLBACK_TO_RTSP=true`(默认),在**无法加载动态库**、**登录失败**或**未配置凭据**时,自动回退到 `VIDEO_RTSP_`* 映射拉流。
|
||||
2. 若 `HIKVISION_SDK_FALLBACK_TO_RTSP=true`(默认),在**无法加载动态库**、**登录失败**或**未配置凭据**时,自动回退到 **OR 站点配置**中的 `video_rtsp_urls` 或 `VIDEO_RTSP_URL_TEMPLATE` 拉流。
|
||||
|
||||
**注意**:`NET_DVR_Login_V30` 的设备信息结构体在不同 SDK 版本上可能存在差异;若登录异常,请优先使用 RTSP 回退或按厂商文档校对 ctypes 绑定。
|
||||
|
||||
|
||||
@@ -41,8 +41,18 @@ RTSP 地址、账号、口令等由客户端对接工程师提供给服务端运
|
||||
| 4 | `GET` | `/client/surgeries/{surgery_id}/result` | 查询手术结果 |
|
||||
| 5 | `GET` | `/client/surgeries/{surgery_id}/pending-confirmation` | 拉取待确认耗材 |
|
||||
| 6 | `POST` | `/client/surgeries/{surgery_id}/pending-confirmation/{confirmation_id}/resolve` | 提交医生答复 |
|
||||
| 7 | `GET` | `/client/voice-terminals/{terminal_id}/assignment` | 可选:查询当前指派(调试或简易集成;**官方桌面客户端仅用 WebSocket**) |
|
||||
| 8 | `WS` | `/client/voice-terminals/ws?terminal_id=...` | 语音桌面终端长连接,接收开录/停录指派(**推荐**) |
|
||||
| | | | |
|
||||
|
||||
**术间与语音终端绑定(服务端配置)**
|
||||
|
||||
- **唯一配置源**:环境变量 **`OR_SITE_CONFIG_JSON_FILE`** 指向手术室 **站点 JSON**(UTF-8),须同时包含 `video_rtsp_urls` 与 `voice_or_room_bindings`(见仓库 `app/resources/or_site_config.sample.json`)。`voice_or_room_bindings` 为数组,每项含 `or_room_id`、`camera_ids`、`voice_terminal_id`;`camera_ids` 在数组内须唯一,`voice_terminal_id` 全局唯一。
|
||||
- **`POST /client/surgeries/start`** 在 **HTTP 200 且开录已成功** 后:用请求体中的 `camera_ids` 在 `voice_or_room_bindings` 中解析终端(**精确匹配**术间 camera 集合,或 **开录路集为某术间 camera 集合的子集** 时匹配该术间);命中则向对应 `voice_terminal_id` 推送 **`action":"start"`**(并更新 assignment);未配置站点文件、或数组为空、或未命中则仅打日志,不影响 200。
|
||||
- **`POST /client/surgeries/end`** 在停录 **HTTP 200** 后:向该手术会话记录的终端推送 **`action":"end"`**(并清除 assignment)。
|
||||
- 推送 JSON 形如:`{"type":"voice_assignment","action":"start"|"end","surgery_id":"123456"}`。
|
||||
- **多 worker**:当前实现为进程内内存;多 Uvicorn worker 时需 sticky session 或 Redis 等另行同步。
|
||||
|
||||
## 4. 流程
|
||||
|
||||
### 4.1 时序图
|
||||
@@ -421,4 +431,27 @@ flowchart LR
|
||||
curl -sS -X POST \
|
||||
"http://<主机>:38080/client/surgeries/123456/pending-confirmation/<confirmation_id>/resolve" \
|
||||
-F "audio=@/path/to/voice.wav;type=audio/wav"
|
||||
```
|
||||
```
|
||||
|
||||
### 5.7 语音终端 assignment(HTTP,可选)
|
||||
|
||||
**路径** `GET /client/voice-terminals/{terminal_id}/assignment`
|
||||
|
||||
仓库内 **手术室耗材语音确认桌面客户端** 仅通过 **§5.8 WebSocket** 接收指派,**不调用**本接口。此处供运维脚本、未实现 WS 的第三方临时拉取 `active_surgery_id`。
|
||||
|
||||
**响应 200**
|
||||
|
||||
| **字段** | **类型** | **说明** |
|
||||
| -------- | -------- | -------- |
|
||||
| `voice_terminal_id` | `string` | 与路径一致 |
|
||||
| `active_surgery_id` | `string \| null` | 当前指派手术 6 位号;无指派时为 `null` |
|
||||
|
||||
### 5.8 语音终端 WebSocket
|
||||
|
||||
**路径** `GET ws://<主机>:<端口>/client/voice-terminals/ws?terminal_id=<终端ID>`(HTTPS 部署时使用 `wss://`)
|
||||
|
||||
**说明**
|
||||
|
||||
- 连接成功后,若服务端已有该终端的 assignment,会立即收到一条 **`action":"start"`** 的 JSON(与下文推送格式一致)。
|
||||
- 术中由服务端在 **`start` / `end` 成功后** 向已连接终端推送 JSON:`{"type":"voice_assignment","action":"start"|"end","surgery_id":"123456"}`。
|
||||
- 客户端可发送任意文本作心跳;服务端当前仅依赖 WebSocket 协议级 ping(由网关或客户端库实现)。
|
||||
Reference in New Issue
Block a user