[文档] 同步 v3 运动监测架构(Webhook 驱动/去轮询) - README 补充 2.2.1 链路/API 表/模块表,PROGRESS 更新运行状态与变更记录 51-53

This commit is contained in:
ericwyuan
2026-08-22 11:11:15 +08:00
parent e52d8e9103
commit b2b537dde5
2 changed files with 42 additions and 7 deletions

View File

@@ -1,12 +1,12 @@
# 项目进度追踪 # 项目进度追踪
> 最后更新: 2026-08-20 18:50 > 最后更新: 2026-08-22 11:08
## 服务运行状态 ## 服务运行状态
| 服务 | 节点 | 地址 | 状态 | 验证结果 | | 服务 | 节点 | 地址 | 状态 | 验证结果 |
|------|------|------|------|---------| |------|------|------|------|---------|
| FAM-Core | NAS | 0.0.0.0:8000 | ✅ 运行中 | health=ok, gunicorn --threads 4, scheduler+dispatcher+poller 全部 running | | FAM-Core | NAS | 0.0.0.0:8000 | ✅ 运行中 | health=ok, gunicorn 单 workerOracle-Sync + 运动监测 Webhook轮询已关闭SS→NAS→Oracle 推送) |
| FAM-Edge | Oracle | 0.0.0.0:5000 | ✅ 运行中 | v2.0, SQLite 异步队列 + 消费者线程 + TokenBucket 速率限制 | | FAM-Edge | Oracle | 0.0.0.0:5000 | ✅ 运行中 | v2.0, SQLite 异步队列 + 消费者线程 + TokenBucket 速率限制 |
| MariaDB | NAS | 127.0.0.1:3306 | ✅ 运行中 | 10.11.11, 6 张表, utf8mb4 | | MariaDB | NAS | 127.0.0.1:3306 | ✅ 运行中 | 10.11.11, 6 张表, utf8mb4 |
| Ollama | Oracle | 127.0.0.1:11434 | ✅ 运行中 | qwen2.5:7b仅智能问答兜底不参与视觉/融合) | | Ollama | Oracle | 127.0.0.1:11434 | ✅ 运行中 | qwen2.5:7b仅智能问答兜底不参与视觉/融合) |
@@ -129,6 +129,10 @@
| 49 | **串行上传 + 看门狗** — Dispatcher limit=1 避免带宽争抢 + 线程存活检测(is_alive) + 60s 看门狗自动重启 + 退避缩短至 30×(n+1)s | `881ea3f` | 2026-08-20 | | 49 | **串行上传 + 看门狗** — Dispatcher limit=1 避免带宽争抢 + 线程存活检测(is_alive) + 60s 看门狗自动重启 + 退避缩短至 30×(n+1)s | `881ea3f` | 2026-08-20 |
| 50 | **fix: 分块大小 5MB + chunk_size 变更防护** — Edge 端 total_chunks 变更自动清理旧分块NAS 端断点续传检测 total_chunks 不匹配时从头上传timeout (60,180) | `5915cf4` | 2026-08-20 | | 50 | **fix: 分块大小 5MB + chunk_size 变更防护** — Edge 端 total_chunks 变更自动清理旧分块NAS 端断点续传检测 total_chunks 不匹配时从头上传timeout (60,180) | `5915cf4` | 2026-08-20 |
| 51 | **运动监测重构为 Webhook 驱动(去轮询)** — 关闭 MotionNotifier 轮询SS 事件经「行動規則/Webhook」`POST :8000/api/ss/webhook` 推送FAM-Core 映射(%DEVICE_NAME%→camera_id、%EVENT_TIME%→epoch、合成稳定 event_id`POST /api/ss/motion` 推甲骨文;甲骨文 `ss_motion_events` 落库、video_processor 改本地运动预过滤 | `ca6d425` | 2026-08-22 |
| 52 | **fix: MotionNotifier 补回 camera_ids 属性** — 避免重新开启轮询时 `_fetch_events` 引用缺失属性崩溃 | `e52d8e9` | 2026-08-22 |
| 53 | **文档同步** — README 架构章节2.2.1 运动监测链路)+ API 表 + 模块表补充 v3 Webhook 架构PROGRESS 状态/变更记录更新 | - | 2026-08-22 |
### 待完成 ### 待完成
| # | 任务 | 依赖 | 优先级 | | # | 任务 | 依赖 | 优先级 |

View File

@@ -68,7 +68,8 @@ Orchestrator 视觉阶段按 `fallback` 模式顺序降级Gemini → NVIDIA N
| 家庭网络 | 用户入口 | 普通终端 | 192.168.50.0/24 | 浏览器访问 `http://192.168.50.64:8501` | | 家庭网络 | 用户入口 | 普通终端 | 192.168.50.0/24 | 浏览器访问 `http://192.168.50.64:8501` |
**网络要点(推送模式)** **网络要点(推送模式)**
- 服务间通信只有一条**NAS → Oracle 公网 IP:5000**HTTP 上传视频。Edge 不需要反向访问 NAS webhook、无视频拉取) - 服务间通信只有两条出站**NAS → Oracle 公网 IP:5000**HTTP 上传视频`POST /api/ss/motion` 推送运动侦测事件。Edge 不需要反向访问 NAS无视频拉取
- **Surveillance Station → NAS 本机 Webhook**:摄像头动作经 SS「行動規則/Webhook」实时 `POST :8000/api/ss/webhook`(同机 127.0.0.1,无需外网),由 FAM-Core 映射后推送 Oracle。这是 NAS 入站,非 Edge 反向访问
- Oracle 端 Ollama 端口 11434 不对外暴露,聊天请求经 FAM-Edge `/api/edge/chat` 代理转发 - Oracle 端 Ollama 端口 11434 不对外暴露,聊天请求经 FAM-Edge `/api/edge/chat` 代理转发
- Tailscale 两节点已安装在线,但 NAS tailscaled 为 userspace 模式且防火墙端口不通,暂走公网 IP - Tailscale 两节点已安装在线,但 NAS tailscaled 为 userspace 模式且防火墙端口不通,暂走公网 IP
@@ -105,8 +106,29 @@ Orchestrator 视觉阶段按 `fallback` 模式顺序降级Gemini → NVIDIA N
└─────────────────────────────────────────────────────────────────────────────────┘ └─────────────────────────────────────────────────────────────────────────────────┘
``` ```
#### 2.2.1 运动监测链路v3Webhook 驱动 · 去轮询)
摄像头动作事件由 **Surveillance Station 主动推送**,不经过任何轮询:
Surveillance Station (NAS 本机)
│ 事件=偵測到動作 → 行動規則「Webhook」动作event_time=%EVENT_TIME% 等
│ HTTP POST同机 127.0.0.1:8000无需外网
NAS FAM-Core :8000 /api/ss/webhook (motion_bp.py)
│ ├─ 解析 JSON / 表单(兼容单条与数组)
│ ├─ 字段映射:%DEVICE_NAME% → camera_id查 SS 名→id 映射)、%EVENT_TIME% → epoch
│ └─ 合成稳定 event_id = hash(device|time|thumb)(供甲骨文 UNIQUE 幂等去重)
│ HTTP POST /api/ss/motion?token=ORACLE_SYNC_TOKEN事件触发即推
Oracle FAM-Edge :5000 /api/ss/motion (api_gateway.py)
│ → OracleDB.ss_motion_eventsevent_id UNIQUE自动去重
│ → video_processor.has_motion_in_range_local() 查本地事件做运动预过滤
> 设计要点:甲骨文**不反向访问** NASSS 推送是 NAS 入站(同机 127.0.0.1)。
> 失败-open`ss_motion_events` 为空时预过滤返回 None不误杀视频
**网络要点(新架构)** **网络要点(新架构)**
- NAS → Oracle 仅一条出站 HTTPS/HTTP`GET /api/oracle/sync`(拉取)与 `POST /api/oracle/people/correct`(命名回推)均走 Oracle 公网 IP:5000token 鉴权 - NAS → Oracle 出站共两条:① `GET /api/oracle/sync`(拉取增量)+ `POST /api/oracle/people/correct`(命名回推);② `POST /api/ss/motion`(运动侦测事件推送)。均走 Oracle 公网 IP:5000token 鉴权
- Oracle Ollama :11434 不对外暴露,问答经 FAM-Edge `/api/edge/chat/ask` 代理 - Oracle Ollama :11434 不对外暴露,问答经 FAM-Edge `/api/edge/chat/ask` 代理
- Tailscale 两节点在线但 NAS 无法反向访问 Oracle故全部走 NAS 主动出站拉取模式 - Tailscale 两节点在线但 NAS 无法反向访问 Oracle故全部走 NAS 主动出站拉取模式
@@ -119,6 +141,11 @@ Orchestrator 视觉阶段按 `fallback` 模式顺序降级Gemini → NVIDIA N
5. NAS Oracle-Sync 每 30 分钟 `GET /api/oracle/sync?since=<cursor>` → upsert 到本地 `sync_*` 镜像表 → 推进 `sync_cursor` 5. NAS Oracle-Sync 每 30 分钟 `GET /api/oracle/sync?since=<cursor>` → upsert 到本地 `sync_*` 镜像表 → 推进 `sync_cursor`
6. FAM-UI 读本地镜像展示;用户命名 → `POST /api/oracle/people/correct` 回推 Oracle下一周期同步生效 6. FAM-UI 读本地镜像展示;用户命名 → `POST /api/oracle/people/correct` 回推 Oracle下一周期同步生效
**运动监测支流v3Webhook 驱动 · 去轮询)**
- Surveillance Station 侦测到动作 → 行動規則「Webhook」动作 `POST :8000/api/ss/webhook`(参数 `event_time=%EVENT_TIME%``device_name=%DEVICE_NAME%``event_name`/`server_name`/`thumbnail_url`
- FAM-Core `motion_bp` 解析并映射(`%DEVICE_NAME%`→camera_id、`%EVENT_TIME%`→epoch、合成稳定 event_id`POST /api/ss/motion` 推送 Oracle
- Oracle `api_gateway` 落库 `ss_motion_events`UNIQUE event_id 幂等),`video_processor.has_motion_in_range_local()` 据此做运动预过滤(空表 fail-open 返回 None
**容错设计** **容错设计**
- Oracle 单视频串行(`max_concurrent=1`)避免多视频抢占云端配额 - Oracle 单视频串行(`max_concurrent=1`)避免多视频抢占云端配额
- 视频分析失败(两云端均不可用)标记 `failed`,下一周期 cursor 仍包含它会被重试 - 视频分析失败(两云端均不可用)标记 `failed`,下一周期 cursor 仍包含它会被重试
@@ -138,6 +165,8 @@ Orchestrator 视觉阶段按 `fallback` 模式顺序降级Gemini → NVIDIA N
| Oracle-Sync | `oracle_sync/oracle_sync.py` | 唯一后台线程:每 30 分钟 `GET /api/oracle/sync?since=<cursor>&token=` 拉增量 → upsert 到 `sync_videos`/`sync_events`/`sync_people` → 推进 `sync_cursor``push_name_correct()` 回推命名校正;`trigger_now()` 立即同步 | | Oracle-Sync | `oracle_sync/oracle_sync.py` | 唯一后台线程:每 30 分钟 `GET /api/oracle/sync?since=<cursor>&token=` 拉增量 → upsert 到 `sync_videos`/`sync_events`/`sync_people` → 推进 `sync_cursor``push_name_correct()` 回推命名校正;`trigger_now()` 立即同步 |
| Chat-Handler | `chat_handler/chat_handler.py` | `/api/chat/ask``sync_events` 拼上下文 → 经 Oracle `/api/edge/chat/ask` 问答编排Gemini→NVIDIA→本地 Ollama 兜底)→ 写 chat_history | | Chat-Handler | `chat_handler/chat_handler.py` | `/api/chat/ask``sync_events` 拼上下文 → 经 Oracle `/api/edge/chat/ask` 问答编排Gemini→NVIDIA→本地 Ollama 兜底)→ 写 chat_history |
| Member-Manager | `member_manager/member_manager.py` | `/api/member/unnamed` / `/api/member/list` / `/api/member/name` / `/api/member/merge`;命名/合并回推 Oracle 并即时拉回本地镜像 | | Member-Manager | `member_manager/member_manager.py` | `/api/member/unnamed` / `/api/member/list` / `/api/member/name` / `/api/member/merge`;命名/合并回推 Oracle 并即时拉回本地镜像 |
| MotionNotifier | `motion_notifier/motion_notifier.py` | 运动事件映射/发送服务:**轮询已关闭**`poll_enabled=false`);启动时一次性拉取 SS 摄像头「名→id」映射并以 config 兜底;提供 `build_event_from_webhook()` 将 SS Webhook 字段归一化为事件 |
| Motion-Webhook | `motion_bp.py` | `POST /api/ss/webhook` 接收 SS Webhook兼容 JSON/表单/单条/数组)→ 映射 → 推送 Oracle`GET /api/ss/status` 查询状态 |
| 公共层 | `db_layer.py` / `config_loader.py` / `logger.py` | PyMySQL 连接unix_socket同步镜像 CRUD文件日志 | | 公共层 | `db_layer.py` / `config_loader.py` / `logger.py` | PyMySQL 连接unix_socket同步镜像 CRUD文件日志 |
> 已删除Task-Scheduler / Dispatcher / Poller / Event-Receiver / Video-Server视频上传、切片、抽帧、关键帧落盘等职责全部迁移至 Oracle 端NAS CPU 占用大幅降低)。 > 已删除Task-Scheduler / Dispatcher / Poller / Event-Receiver / Video-Server视频上传、切片、抽帧、关键帧落盘等职责全部迁移至 Oracle 端NAS CPU 占用大幅降低)。
@@ -148,11 +177,11 @@ Orchestrator 视觉阶段按 `fallback` 模式顺序降级Gemini → NVIDIA N
| 模块 | 文件 | 职责 | | 模块 | 文件 | 职责 |
|------|------|------| |------|------|------|
| API-Gateway | `api_gateway/api_gateway.py` | `GET /api/oracle/sync`增量拉取since+token 校验);`POST /api/oracle/people/correct`(命名校正);`POST /api/edge/chat/ask`(问答编排);`GET /health` | | API-Gateway | `api_gateway/api_gateway.py` | `GET /api/oracle/sync`增量拉取since+token 校验);`POST /api/oracle/people/correct`(命名校正);`POST /api/edge/chat/ask`(问答编排);`POST /api/ss/motion`运动事件接收token 校验,落库 `ss_motion_events``GET /health` |
| Video-Queue | `video_queue.py` | 生产-消费队列:生产者 30s 轮询 rclone 同步落地目录登记新视频入队(含重启恢复);消费者(`max_concurrent` 个线程)取队列调 Video-Processor模型超时 = 原配置 ×`timeout_multiplier`;失败重试上限 `max_retries` | | Video-Queue | `video_queue.py` | 生产-消费队列:生产者 30s 轮询 rclone 同步落地目录登记新视频入队(含重启恢复);消费者(`max_concurrent` 个线程)取队列调 Video-Processor模型超时 = 原配置 ×`timeout_multiplier`;失败重试上限 `max_retries` |
| Video-Processor | `video_processor.py` | 按 `vision_order` 调适配器 `analyze_video`(整视频);首个成功即落库 Oracle `videos`+`events`+`people`;全失败标 `failed` | | Video-Processor | `video_processor.py` | 按 `vision_order` 调适配器 `analyze_video`(整视频);首个成功即落库 Oracle `videos`+`events`+`people`;全失败标 `failed`;运动预过滤改查本地 `ss_motion_events``has_motion_in_range_local`),不再反向访问 NAS |
| Person-Service | `person_service.py` | 汇总全量人物 → LLM 合并为规范名 → `set_canonical`;生成 `known_members_context` 回灌视频提示manual 命名优先不被覆盖 | | Person-Service | `person_service.py` | 汇总全量人物 → LLM 合并为规范名 → `set_canonical`;生成 `known_members_context` 回灌视频提示manual 命名优先不被覆盖 |
| OracleDB | `oracle_db.py` | SQLitevideos / events / people / sync_cursor`get_sync_delta(since)` 增量导出 | | OracleDB | `oracle_db.py` | SQLitevideos / events / people / sync_cursor / ss_motion_events`get_sync_delta(since)` 增量导出`record_motion_events` / `has_motion_in_range_local`(运动事件幂等落库与窗口查询) |
| Model-Adapters | `model_adapters/` | `BaseModelAdapter.analyze_video(video_path, known_members_context, event_start_time)`GeminiFiles API 整视频)/ NVIDIA整视频 `video_url``num_frames=128`/ Ollama纯文本不参与视频 | | Model-Adapters | `model_adapters/` | `BaseModelAdapter.analyze_video(video_path, known_members_context, event_start_time)`GeminiFiles API 整视频)/ NVIDIA整视频 `video_url``num_frames=128`/ Ollama纯文本不参与视频 |
| QA-Orchestrator | `qa.py` | 遍历所有适配器 `chat()`Gemini→NVIDIA→Ollama 三级降级(仅问答) | | QA-Orchestrator | `qa.py` | 遍历所有适配器 `chat()`Gemini→NVIDIA→Ollama 三级降级(仅问答) |
@@ -266,6 +295,8 @@ chat_history 独立表(问答上下文摘要留存)
| `/api/member/list` | GET | 全部人物label + canonical_name + 是否命名) | | `/api/member/list` | GET | 全部人物label + canonical_name + 是否命名) |
| `/api/member/name` | POST | 命名:`{"label","canonical_name"}` → 回推 Oracle 并即时拉回本地镜像 | | `/api/member/name` | POST | 命名:`{"label","canonical_name"}` → 回推 Oracle 并即时拉回本地镜像 |
| `/api/member/merge` | POST | 合并:`{"source","target"}` → 将 source 并入 target 身份(统一 canonical_name | | `/api/member/merge` | POST | 合并:`{"source","target"}` → 将 source 并入 target 身份(统一 canonical_name |
| `/api/ss/webhook` | POST | 接收 Surveillance Station Webhook参数 `event_time`/`device_name`/`event_name`/`server_name`/`thumbnail_url`);映射后推送 Oracle `/api/ss/motion` |
| `/api/ss/status` | GET | 运动监测状态:`poll_enabled` / `camera_loaded` / `camera_map` / `pushed_total` / `last_error` |
> 已删除:`/api/core/callback/event`、`/media/<path>`(视频处理职责已迁移至 Oracle > 已删除:`/api/core/callback/event`、`/media/<path>`(视频处理职责已迁移至 Oracle