Commit Graph

14 Commits

Author SHA1 Message Date
ericwyuan
01bbb39750 [新增] NAS 运动监测通知服务 MotionNotifier - 轮询/接收 SS 事件后主动 POST 推送到甲骨文 /api/ss/motion(单向 NAS->Oracle):新增 motion_notifier 子包、/api/ss/webhook 实时转发蓝图、db_layer 运动游标、app.py 接线与 config 块 2026-08-22 10:26:03 +08:00
ericwyuan
e56d362d73 refactor(fam-core): 重构第二阶段 - img_proxy 收尾 + 稳定性小修 + 工程质量
img_proxy 收尾: 提交此前未提交的图片代理蓝图(app.py 注册),改为复用 oracle_sync
已解析好的 base_url/token/timeout,不再独立读一份配置(避免配置改动时两处不同步)。

稳定性/正确性: chat_handler 问答失败时不再把 Oracle 内部 HTTP 状态码等细节透传给
客户端,改为通用错误信息(详细原因仍记服务端日志); 删除 logger.py 里旧任务架构
遗留的死函数 log_task; 更新 config.yaml.example 到当前 v2 架构(原文件还是重构前
的 scheduler/dispatcher/video_server 旧结构,当前代码完全不读这些字段)。

工程质量: 新增 fam-core/tests(9 个单元测试,覆盖 _format_events 上下文格式化和
config_loader 的 ${ENV_VAR} 解析)。

部署时发现并修复一个和这次改动无关的运维问题: NAS .env 文件缺 export 关键字,
plain source 只在当前 shell 生效不会被子进程(gunicorn)继承,导致 Oracle-Sync
token 校验失败;用 set -a/set +a 强制导出重启,非代码改动。

已部署 NAS 并验证:/health、/api/status、/api/proxy/frame、/api/proxy/avatar
全部通过;浏览器实测事件时间轴、人物管理页正常渲染。

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-22 00:50:49 +08:00
ericwyuan
44026b65ea [阶段3] FAM-Core 瘦身为管理后台+Oracle-Sync 同步引擎,FAM-UI 改读同步镜像 - 删除 scheduler/dispatcher/poller/event_receiver/video_server,新增 oracle_sync 每30分钟拉增量写 sync_* 镜像表;member_manager/chat_handler 改走同步数据;UI 移除帧图改为事件时间线;DDL 新增 sync_videos/events/people/cursor 2026-08-21 10:38:26 +08:00
ericwyuan
0d5173f442 feat: UI 时间轴重构 + 关键帧图片全链路持久化
界面重做(深色监控面板主题):
- 事件时间轴页: 左侧事件列表卡片 + 右侧关键帧时间轴
  (时间点 + 视频帧 + 人物/动作/衣着摘要 + 关注标记)
- 全局 CSS: 深色主题、卡片化按钮、统计卡、对话气泡、成员卡片
- 侧边栏任务队列状态徽章; 历史帧无图时优雅降级占位

关键帧持久化链路:
- Edge orchestrator: process_push_task 成功后把 keyframes
  base64 注入 frame_details[i].frame_image (位置对齐视觉输入帧)
- NAS event_receiver: 落库前 base64 解码写盘到
  fam-ui/static/frames/event_{id}/frame_{idx}.jpg
- payload 增量 ~300KB/事件 (6帧 jpeg q80), 队列/拉取均无压力

配置: fam-core/fam-ui 新增 storage.frame_image_dir
2026-08-20 19:50:36 +08:00
ericwyuan
203b076783 feat: NAS端FFmpeg预压缩 — 根治360MB视频跨境上传超时
根因诊断(网络问题,非代码逻辑):
- NAS(中国移动) → Oracle(美国Phoenix) 跨境上行带宽实测仅 0.5-0.9MB/s 且波动大
- 下载方向 3.0MB/s(非对称,典型国际出口拥塞)
- Tailscale P2P: ping 可通(224ms)但持续数据流被阻断(0 B/s, 127s超时)
- 360MB 原始视频上传需 10-20min,任何大分块都会超时

NAS dispatcher:
- 新增 _compress_video: 480p/CRF28/veryfast,静态监控场景实测 ~36x 压缩比
  (360MB → ~12MB,上传时间 20min → ~30s)
- ffmpeg 自动探测: CodecPack ffmpeg41(带libx264) > /usr/local/bin > PATH
  (Synology 系统 ffmpeg 被裁剪,无 h264 编解码)
- scale 两级滤镜: force_original_aspect_ratio=decrease + trunc(iw/2)*2
  (h264 要求偶数尺寸,853x480 会报错)
- 压缩缓存 /tmp/fam_compressed/task_{id}/,源文件未变则重试复用,TTL 24h
- 压缩完成后重置 PROCESSING 状态(重置 stale 回收计时基准)
- 压缩失败回退原始文件分块上传
- _query_uploaded_chunks 失败时记录日志(原静默失败导致全量重传无感知)

Edge api_gateway:
- fix: total_chunks 变更清理旧分块后同步重写 meta.json
  (原 bug: meta.json 不更新导致每块上传都触发清理,删除同批新分块死循环)

config:
- stale_timeout 600→1800(覆盖压缩+上传+Edge队列积压总时长)
- 新增 ffmpeg_path / compress_timeout 配置项
2026-08-20 14:59:50 +08:00
ericwyuan
b6c13a9047 feat: 分块断点续传上传 — 20MB/块 + 分块级重试 + 断点查询
Edge 端新增 3 个端点:
- POST /api/edge/video/chunk: 接收单块,保存到 task_{id}/chunk_{index:04d}
- GET /api/edge/video/chunks: 查询已上传分块(断点续传)
- POST /api/edge/video/assemble: 合并全部分块入队

NAS Dispatcher 重写:
- 大文件(>50MB)自动分块上传(20MB/块)
- 每块最多重试 3 次(分块级重试,非整文件级)
- 上传前查询已上传分块,跳过已有的(断点续传)
- 全部上传后调 /assemble 合并入队
- 小文件(<=50MB)走直接上传路径
- max_retries 3→5(文件级重试次数)
- scheduler 切回生产目录

解决: 360MB 视频跨公网单次上传超时/断连问题
2026-08-20 12:27:30 +08:00
ericwyuan
4f0f19bccd fix: dispatcher/poller 超时改为分离模式,stale_timeout 缩短到 10 分钟
- dispatcher 上传 timeout 从 300s 改为 (10s connect, 60s read)
  避免大文件上传无限阻塞 dispatcher 线程
- poller 拉取 timeout 从 30s 改为 (10s connect, 15s read)
- stale_timeout 从 3600s 缩短到 600s(10 分钟)
  更快回收卡死的 PROCESSING 任务
2026-08-20 12:12:19 +08:00
ericwyuan
02da23ef42 feat: 异步任务队列架构 - SQLite队列 + 速率限制 + NAS Poller
Edge端:
- 新增 SQLite 异步任务队列 (queue_manager + consumer)
- 新增 TokenBucket 速率限制器 (Gemini 1000 RPM, NVIDIA 40 RPM, burst 2x)
- 新增 /api/edge/video/enqueue + /api/edge/results 端点
- 消费者线程从队列消费任务,按速率限制调用AI模型
- orchestrator 集成 rate_limiter,Gemini优先→NVIDIA兜底

NAS端:
- Dispatcher 重构为 enqueue 模式(上传后立即返回,不等结果)
- 新增 Poller 线程(定期从Edge拉取结果写 MariaDB)
- app.py 启动 Poller,config.yaml 新增 poller 配置
- db_layer 更新 valid_stages 添加 'process'
2026-08-20 12:07:09 +08:00
ericwyuan
babf5b09a9 [架构重构] 移除本地Ollama融合,云端直出JSON直存DB,Q&A三模型降级
1. 视频摘要链路:云端VLM直出结构化JSON → Edge format_cloud_result格式化校验 → 直存NAS DB(移除run_text_fusion本地融合)
2. 智能问答链路:Gemini→NVIDIA→Ollama降级,新增chat()纯文本问答方法
3. 适配器重构:base/gemini/nvidia/ollama adapter新增chat();gemini多图单请求结构化JSON;nvidia逐帧调用聚合
4. 端点变更:/api/edge/chat → /api/edge/chat/ask,调orchestrator.run_qa()
5. chat_handler改经Edge Q&A编排,不再直连Ollama
6. 配置更新:ollama_url → qa_url,Ollama role注释改为Q&A兜底
7. README同步更新架构描述、拓扑图、时序图、模块表
2026-08-20 10:21:09 +08:00
ericwyuan
727642d38b feat: adaptive keyframe count based on video duration
- Replace fixed 5-8 keyframe limit with duration-based adaptive sizing
- Candidate frames: clamp(duration_min × 2, 30, 120)
- Keyframe cap: clamp(duration / 150s, 8, 30)
- 30min video → 12 keyframes (was 8), 60min → 24, 120min → 30
- Short videos (<12min) still get floor of 8 keyframes
- Add Ollama keep-alive config doc to PROGRESS.md (OLLAMA_KEEP_ALIVE=-1)
- Update config.yaml and config.yaml.example with new video params
2026-08-20 01:29:14 +08:00
ericwyuan
40944428d1 feat: video analysis switched to push mode (upload whole video, sync response)
Rationale: Oracle cannot reach NAS (Tailscale userspace mode on NAS, no TUN),
the old pull+webhook design requires Edge to download video from NAS and
callback to NAS - both blocked. New design is one-way NAS -> Oracle:

- FAM-Edge: new POST /api/edge/video/push endpoint accepts multipart video
  upload, reuses existing OpenCV scene-change keyframe selection, analyzes
  synchronously and returns the result payload directly in the HTTP response
  (no webhook callback). Old /api/edge/video/analyze kept for compatibility.
- FAM-Edge: VideoPreprocessor.save_upload() saves the uploaded file
- FAM-Edge: AIOrchestrator.process_push_task() runs the full pipeline
  (health check -> extract -> select -> compress -> VLM -> fusion) and
  returns callback-style payload dict
- FAM-Core: Dispatcher rewritten to push mode - reads local video file,
  uploads with task metadata (camera_name, event_start_time from file mtime,
  known_members_context), applies the result to DB via shared
  event_receiver.apply_success_event()
- FAM-Core: event_receiver success logic extracted into reusable
  apply_success_event() (used by both webhook route and dispatcher)
- config: edge_url -> /api/edge/video/push, push_timeout 1800s, gunicorn
  Edge timeout raised to 1800s for long synchronous analysis
2026-08-20 01:03:48 +08:00
ericwyuan
94a8805045 feat: FAM-Edge chat proxy + PyMySQL migration for FAM-UI + config update
- FAM-Edge: add /api/edge/chat proxy endpoint forwarding to local Ollama
  (Ollama port 11434 not exposed externally, FAM-Edge acts as reverse proxy)
- FAM-Core config: edge_url and ollama_url switched from Tailscale IP to
  Oracle public IP (Tailscale firewall blocking between NAS and Oracle)
- FAM-UI: migrate mysql.connector to PyMySQL (same as FAM-Core)
- FAM-UI: cursor(dictionary=True) replaced with cursorclass=DictCursor
- End-to-end chat verified: FAM-Core -> FAM-Edge proxy -> Ollama -> response
  Answer: 今天没有观察到张三 (no events in DB yet, expected)
2026-08-19 23:59:28 +08:00
ericwyuan
47658fa068 [deploy] PyMySQL 替换 + Tailscale 组网 + config.yaml + 路径修复
- db_layer.py: mysql-connector-python(19MB) → PyMySQL(45KB), 去掉连接池
- config_loader.py: 修复路径解析 (2级→3级 dirname), FAM-Core + FAM-Edge 均修复
- Tailscale 组网完成: Oracle=100.74.137.126, NAS=100.70.234.39, 互通验证通过
- NAS Python 依赖: flask/gunicorn/pymysql/PyYAML 全部安装成功
- NAS 代码部署: /volume1/web/sentinel-home-ai/, FAM-Core 7 模块导入 PASS
- NAS DB 连接验证: PyMySQL → MariaDB 10.11.11, 6 张表可见, PASS
- config.yaml 创建: FAM-Core + FAM-Edge + FAM-UI (Tailscale IP + 密码)
- requirements.txt: mysql-connector-python → PyMySQL
2026-08-19 23:27:28 +08:00
ericwyuan
da6b1c8d39 [2.1-2.5] FAM-Core 六个子模块 - Scheduler/Dispatcher/Event-Receiver/Chat-Handler/Member-Manager/Video-Server + DB层 + 配置 2026-08-19 22:21:31 +08:00