From 2d9d96de5652b64b30a4967bf9f20b0abff17f3a Mon Sep 17 00:00:00 2001 From: ericwyuan Date: Sat, 22 Aug 2026 12:08:00 +0800 Subject: [PATCH] =?UTF-8?q?[=E5=8A=9F=E8=83=BD]=20fam-edge=20=E8=BF=90?= =?UTF-8?q?=E5=8A=A8=E5=BF=83=E8=B7=B3=E6=94=AF=E6=8C=81=20-=20/api/ss/mot?= =?UTF-8?q?ion=20=E7=A9=BA=20events=20=E5=BF=83=E8=B7=B3=E5=88=B7=E6=96=B0?= =?UTF-8?q?,=20has=5Fmotion=5Fin=5Frange=5Flocal=20=E6=8C=89=20max=5Fheart?= =?UTF-8?q?beat=5Fage=5Fsec(900s)=20fail-open?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- fam-edge/config/config.yaml | 15 ++++++++------- fam-edge/src/fam_edge/api_gateway/api_gateway.py | 6 +++++- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/fam-edge/config/config.yaml b/fam-edge/config/config.yaml index 086cff6..19c768a 100644 --- a/fam-edge/config/config.yaml +++ b/fam-edge/config/config.yaml @@ -47,17 +47,18 @@ video_processing: # 降级顺序:先 gemini 整视频,失败再 nvidia 整视频;两者都失败 -> 标记 failed vision_order: ["gemini", "nvidia"] -# 运动侦测预过滤(2026-08-22 重构): +# 运动侦测预过滤(2026-08-22 重构,2026-08-22 补心跳): # - 甲骨文【不再反向访问 NAS】。原 dsm_motion_client(甲骨文主动查 SS API)已停用, -# 改由 NAS 端 fam-core 的 MotionNotifier 轮询/接收 SS 事件后,主动 POST 推送到 -# 甲骨文 /api/ss/motion,落库 ss_motion_events。 +# 改由 NAS 端 fam-core 的 MotionNotifier 接收 SS Webhook 后,主动 POST 推送到 +# 甲骨文 /api/ss/motion,落库 ss_motion_events;NAS 侧还会定期用空 events 单纯 +# 调一次这个接口当心跳(不要求真的有事件),证明推送链路还活着。 # - video_processor 分析前调用 db.has_motion_in_range_local()(本地运动事件表)做 # 预过滤:窗口内无运动事件则跳过云端分析(compute_provider=skipped_no_motion)。 -# - 本地运动事件表为空(冷启动/尚未收到推送)一律 fail-open(照常分析),不漏检。 -# 此 block 仅保留 min_motion_seconds 语义参考;host/account 等反向访问字段已弃用。 +# - 安全阀门: 距上次心跳超过 max_heartbeat_age_sec 没更新(NAS 服务挂了/网络断了/ +# DSM Webhook 规则被误关,历史数据表非空但推送链路已经死了),一律 fail-open +# (照常分析),不能仅凭"表里曾经有数据"就信任"这段时间确认无运动"的结论。 dsm_motion_prefilter: - enabled: false # 已停用:甲骨文不再主动访问 NAS SS - min_motion_seconds: 0 # 窗口内运动事件总时长需 ≥ 此值才算"有运动"(0=有事件就算) + max_heartbeat_age_sec: 900 # 心跳超过 15 分钟没更新 -> 判定推送链路可能已死,fail-open # 智能问答降级链(与视频分析独立):Gemini -> NVIDIA -> 本地 Ollama models: diff --git a/fam-edge/src/fam_edge/api_gateway/api_gateway.py b/fam-edge/src/fam_edge/api_gateway/api_gateway.py index 4d24e96..2050b70 100644 --- a/fam-edge/src/fam_edge/api_gateway/api_gateway.py +++ b/fam-edge/src/fam_edge/api_gateway/api_gateway.py @@ -205,7 +205,10 @@ def ss_motion(): {"event_id": 25349, "camera_id": 2, "event_type": 10, "start_time": 1787318023, "duration": 149, "thumbnail_url": "107471,12075"} ]} - start_time/duration 为 Unix epoch(与 SS 同源,时区无关)。 + start_time/duration 为 Unix epoch(与 SS 同源,时区无关)。events 可以是空数组 + ——NAS 侧也会定期用空 events 单纯调一次这个接口当心跳,证明推送链路还活着 + (鉴权通过就刷新心跳,不要求 stored>0),供 has_motion_in_range_local() 判断 + "无运动"结论是否可信。 落库 ss_motion_events(按 event_id 幂等),供 video_processor 本地预过滤使用。 """ if not _check_token(): @@ -218,6 +221,7 @@ def ss_motion(): return jsonify({"error": "events 必须是数组"}), 400 try: stored = state.get_db().record_motion_events(events) + state.get_db().record_motion_heartbeat() except Exception as e: logger.error(f"ss_motion 落库异常: {e}") return jsonify({"error": str(e)}), 500