100 Commits

Author SHA1 Message Date
369bb02391 优化:违停和拥堵 resolve 后清除冷却记录
车辆离开/拥堵消散确认后清冷却,新的违停/拥堵事件可正常触发。
15秒确认期+命中率阈值已能过滤路过车辆,不会误触发。
2026-03-19 15:47:01 +08:00
648606fd0d 优化:车辆违停和拥堵算法防频繁告警
违停:
- confirm_clear 30→120秒(持续2分钟无车才确认离开)
- cooldown 600→1800秒(30分钟冷却)
- ALARMED 清除阈值 ratio<0.3→ratio<0.15(更严格)

拥堵:
- confirm_clear 120→180秒
- cooldown 600→1800秒
- 消散阈值从 < threshold 改为 < threshold*0.5(降到一半才开始确认)
2026-03-19 15:19:27 +08:00
8da4ef9e93 修复:Redis 配置加载时传入 alarm_level 到算法实例
Redis 订阅路径初始化算法时漏传 alarm_level 参数,
导致前端配置的告警等级不生效,始终使用默认值。
2026-03-19 10:56:54 +08:00
13706bc55c 安全:移除 .env 跟踪,添加 .env.example 模板
.env 含真实密钥不应入库,已在 .gitignore 中忽略。
新增 .env.example 作为配置模板(占位符)。
2026-03-19 10:30:51 +08:00
2ea35ad5d3 功能:心跳同时发送到 vsp-service 和 WVP 平台
新增 WVP_API_URL 环境变量,心跳同时上报到两个地址,
解决前端从 WVP 读设备状态显示离线的问题。
2026-03-19 09:58:24 +08:00
9c39913a55 功能:告警级别支持前端配置下发 + 级别体系统一为 0-3
- 四种算法均支持通过 params.alarm_level 覆盖默认告警级别
- 级别体系统一:0紧急/1重要/2普通/3轻微
- 车辆拥堵默认阈值调整为 5 辆

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 16:39:58 +08:00
3d91aa1a67 功能:area_id 全链路支持 + 截图处理器独立 Redis 连接
- CameraInfo 模型添加 area_id 字段
- SQLite 表增加 area_id 列及迁移
- config_sync 同步 area_id 到本地
- 告警 ext_data 携带 area_id
- 截图处理器使用独立 Redis 连接,避免与配置同步阻塞冲突
- get_all_camera_configs 使用 cursor.description 动态获取列名

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 16:05:04 +08:00
0d88ed7fbb 功能:添加心跳守护线程,每30秒向 WVP 上报设备状态
- 新增 _start_heartbeat() 守护线程
- 每30秒 POST 到 WVP /api/ai/device/heartbeat
- 上报 uptime、帧数、告警数、活跃流数、配置版本
- 使用 stop_event.wait(30) 优雅退出

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-17 17:47:12 +08:00
ea992c6daa 功能:新增车辆违停和车辆拥堵检测算法
- IllegalParkingAlgorithm: 5状态机(IDLE→CONFIRMING_VEHICLE→PARKED_COUNTDOWN→ALARMED→CONFIRMING_CLEAR)
  禁停区域检测,15秒确认+5分钟倒计时,滑动窗口抗抖动,支持car/truck/bus/motorcycle
- VehicleCongestionAlgorithm: 4状态机(NORMAL→CONFIRMING_CONGESTION→CONGESTED→CONFIRMING_CLEAR)
  车辆计数≥阈值+持续60秒触发,滑动窗口平均值判断
- AlgorithmManager: 新增default_params、register_algorithm、get_status支持两种新算法
- main.py: 泛化alarm_id回填和first_frame_time提取,ext_data新增vehicle_count字段

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-16 16:54:47 +08:00
9a1ac16f19 修复:截图回调禁用系统代理,解决502回调失败
requests.post回调WVP时被本地代理(127.0.0.1:7897)拦截导致502,
添加proxies=None绕过系统代理直连WVP。

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-11 09:40:51 +08:00
d132a50ae0 fix: 告警上传COS路径日期改为北京时间
alarm_upload_worker.py 中 COS object key 的日期和死信时间戳
从 UTC 改为北京时间(UTC+8),与服务端和日志时间一致。

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 17:22:50 +08:00
82d17a5266 fix: alarm_id 时间戳改为北京时间
generate_alarm_id() 中 datetime.now(timezone.utc) 改为
datetime.now(timezone(timedelta(hours=8))),与服务端时间一致。

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 17:20:50 +08:00
1211fc7207 feat: 离岗算法滑动窗口抗抖动优化
- 上岗确认阈值从 0.7 降为 0.6,降低漏确认
- 离岗触发从 ratio==0 改为 ratio<0.2,允许 20% 抖动
- 离岗中断从单帧判断改为 ratio>=0.5,避免偶尔一帧误检打断确认
- 入侵算法不变,通过调高 conf 到 0.6 解决误报

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-08 23:20:31 +08:00
7a5ddef2f6 feat: 支持按算法独立配置置信度阈值
通过 ALGO_CONF_{ALGO_CODE} 环境变量为每个算法设置独立的 conf_threshold,
未配置的算法回退到全局 CONF_THRESHOLD。推理过程零改动,仅后处理过滤阶段
按 bind.algo_code 使用对应阈值。

当前配置:离岗=0.4(降低漏检),入侵=0.5(减少误报)。

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-08 17:31:12 +08:00
3d88dfc1c6 修复:删除算法绑定后边缘端未停止对应算法推理
根本原因:config_sync.py 的 _cleanup_stale_records() 方法接收了
incoming_bind_ids 参数但从未使用,导致当 ROI 仍存在但其中某个
算法绑定被删除时,孤立的绑定记录继续留在 SQLite 中,推理循环
仍然从 SQLite 读取到已删除的绑定并继续生成告警。

修复内容:
1. config/database.py: 添加 get_all_bind_ids() 方法
2. core/config_sync.py: 在 _cleanup_stale_records() 中补全
   使用 incoming_bind_ids 清理孤立绑定的逻辑
3. algorithms.py: 在 reload_all_algorithms() 中添加清理内存中
   孤立算法实例的逻辑,防止内存泄漏
2026-03-05 17:12:15 +08:00
1d4eaf0174 文档:创建 CLAUDE.md 开发指南
新增内容:
- 项目概述(边缘 AI 推理服务功能说明)
- 常用命令(本地开发、测试、工具脚本、Docker 部署)
- 架构概览(核心模块详细说明)
  - 配置同步、视频流、推理引擎、告警上报
  - 算法模块(离岗、入侵检测)
- 数据流说明(配置下发、视频推理、告警上报、截图请求)
- Redis Key 设计(云端 + 本地)
- 配置文件说明(.env 环境变量、YAML 配置)
- 告警上报数据格式(触发和结束)
- 开发工作流(添加算法、修改推理流程、优化性能)
- 常见问题排查(引擎加载、告警失败、配置更新、GPU 内存)
- Git 提交规范

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-03-05 16:31:35 +08:00
bdbfca4252 fix(edge): 修复 batch 推理超过 MAX_BATCH_SIZE 导致缓冲区溢出
队列中 ROI 数量超过 8 时(多摄像头多 ROI 绑定场景),
一次性送入 TensorRT 引擎导致 np.copyto 溢出。
改为按 max_batch_size 分块推理。

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 16:49:20 +08:00
d9f78e0b48 refactor(edge): 截图不再保存本地,直接编码为base64上传COS
- ResultReporter: 截图通过 cv2.imencode 编码为 JPEG base64,
  直接放入 Redis 消息,不再调用 ImageStorageManager 保存本地文件
- AlarmUploadWorker: 从 Redis 读取 base64 解码为字节流,
  使用 put_object(Body=bytes) 直接上传 COS,移除 local: 回退逻辑
- 移除 AlarmInfo.snapshot_local_path,改为 snapshot_b64
- COS 未配置时返回 None 进入重试(不再静默回退本地路径)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 14:03:12 +08:00
d71d5da740 fix(edge): 告警上报改指 WVP 端点,修复连接失败
- CLOUD_API_URL 改为 WVP 地址 (http://124.221.55.225:18080)
- 告警路径从 /admin-api/aiot/alarm/edge/* 改为 /api/ai/alert/edge/*
- 适配 WVP 新增的 report/resolve 端点

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 09:58:34 +08:00
d9d58dfafa feat(edge): 截图支持临时 RTSP 连接,解决无 ROI 摄像头无法截图
_capture_frame 增加 rtsp_url 参数,优先走已有流,无流时降级
临时连接 RTSP 抓帧(_capture_ondemand),用完即释放。
提取 _encode_jpeg 公共方法。

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 09:39:14 +08:00
3ca6c93479 fix(edge): 配置同步后清理 SQLite 中的旧摄像头/ROI/绑定记录
- _sync_config_to_sqlite 增加旧数据清理逻辑
- 同步完成后删除不在本次推送列表中的摄像头、ROI 及关联算法绑定
- 仅在推送列表非空时执行清理,防止空配置误删所有数据
- 解决旧摄像头残留导致 Edge 加载过期配置的问题

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 19:58:58 +08:00
b2469c576c fix: 移除 config_sync 模块的日志抑制,恢复配置同步日志输出
core.config_sync 被错误地加入 _QUIET_LOGGERS 列表,导致所有 INFO 级别
日志被抑制,无法看到 Redis 连接、Stream 监听、配置同步等关键运行日志。

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-28 17:05:49 +08:00
a124edf8f9 fix(edge): EDGE_DEVICE_ID从edge-001改为edge,与WVP标准值一致
Edge的config_sync过滤Stream事件时要求device_id匹配,
WVP端标准device_id为"edge",此前不匹配导致Edge静默忽略
所有配置推送事件。

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-28 15:48:32 +08:00
a49a1be0eb fix(edge): 云端Redis默认db改为1,配置更新回调支持所有同步模式
- CloudRedisConfig.db 默认值从0改为1,与部署环境一致
- 配置更新回调不再限制为LOCAL模式,REDIS模式也支持动态热更新

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-28 15:48:18 +08:00
6d1e0e4a5e feat(edge): 截图响应改为HTTP回调,COS使用预签名URL
- 截图完成后优先通过HTTP回调WVP返回结果,回调失败降级写Redis
- COS上传后生成预签名URL(1小时有效期),不附加额外Params
- 移除Edge端缓存逻辑(缓存由WVP端统一管理)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-28 15:48:12 +08:00
9ec949ef02 fix(edge): LOCAL模式下独立连接云端Redis,确保截图处理器可用
CONFIG_SYNC_MODE=LOCAL 时 config_sync 不初始化云端 Redis,
截图处理器改为独立创建 Redis 连接。

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-28 09:04:26 +08:00
f70e6b6003 feat(edge): 新增截图处理模块,支持远程截图请求
- 新增 core/screenshot_handler.py:监听云端 Redis Stream 截图请求,
  抓帧后直传 COS,将结果 URL 写回 Redis
- main.py 集成 ScreenshotHandler 的初始化和停止
- requirements.txt 添加 cos-python-sdk-v5 依赖

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-27 17:22:49 +08:00
0b0e793785 fix(edge): 配置更新时保留算法状态,避免重复告警
- 新增 update_algorithm_params() 方法,仅更新参数不重置状态机
- 修改 reload_all_algorithms() 支持 preserve_state 参数
  - preserve_state=True: 保留状态机,仅更新参数(配置更新)
  - preserve_state=False: 完全重置(手动重启)
- 修改 main.py 配置更新回调使用 preserve_state=True

修复问题:配置更新导致算法状态机重置,周界入侵CONFIRMING_CLEAR状态丢失,重新生成新告警
现在配置更新时保留告警状态,不会产生重复告警

支持算法:
- leave_post: 更新 leave_countdown_sec, working_hours 等参数
- intrusion: 更新 confirm_intrusion_seconds, confirm_clear_seconds 等参数

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 11:24:41 +08:00
75ea66c452 feat(intrusion): 拆分参数,消失确认3分钟,持续有人5秒才重置
- 拆分confirm_seconds为confirm_intrusion_seconds(5秒)和confirm_clear_seconds(180秒)
- 入侵确认:持续检测到人5秒 → 触发告警
- 消失确认:持续无人180秒 → 自动resolve
- 消失确认期间逻辑:
  - 短暂有人(<5秒):继续倒计时
  - 持续有人(≥5秒):回到ALARMED状态
- 向后兼容:保留confirm_seconds参数
2026-02-25 09:12:56 +08:00
7b3265fe74 fix(edge): 配置推送后异步启动摄像头,不阻塞HTTP响应
问题:配置推送时同步启动摄像头,需等待13秒才返回响应,
导致云端10秒超时认为推送失败

修复:将_reload_cameras()改为异步执行:
1. 配置更新写入SQLite
2. 立即返回HTTP 200响应(<1秒)
3. 后台线程异步启动摄像头(10-15秒)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-14 22:36:56 +08:00
a38d599544 fix(config): 修改设备ID默认值为default - 匹配云端配置
- device_id: edge → default
- 修复云端推送空配置的问题
- 云端摄像头配置的device_id都是default,需要匹配

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-14 12:05:45 +08:00
db28773406 chore: 更新.gitignore - 排除测试和工具脚本
默认不提交:
- test_*.py, *_test.py
- diagnose_*.py, cleanup_*.py, restore_*.py
- 其他工具脚本 (*_tool.py, *_helper.py)

除非明确要求提交

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-14 11:39:08 +08:00
a1dbd6c55b tools(recovery): 添加摄像头配置恢复工具
用于恢复因config-sync bug丢失的摄像头配置
- 交互式输入RTSP URL
- 直接写入数据库
- 适用于云端配置也丢失的情况

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-14 11:36:07 +08:00
25851cdafc Revert "fix(config-sync): 修复全量同步时空配置不清理旧数据的bug"
This reverts commit 66c8039889.
2026-02-14 11:33:43 +08:00
29e67a2b80 tools(cleanup): 添加清理旧ROI配置的脚本
问题分析:
- 用户只配置了3个ROI (使用cam_xxx格式的camera_id)
- 数据库中有7个旧ROI (使用中文名称格式的camera_id)
- 旧ROI是残留数据,需要清理

功能:
- 自动识别cam_xxx格式的用户ROI
- 识别中文名称格式的旧ROI
- 删除旧ROI及其关联的算法绑定
- 保留用户实际配置的ROI

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-14 11:31:34 +08:00
2c074f064d revert(inference): 恢复置信度阈值到0.4
- conf_threshold: 0.5 → 0.4 (恢复原值)
- 未经用户授权的修改已回退

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-14 11:31:23 +08:00
66c8039889 fix(config-sync): 修复全量同步时空配置不清理旧数据的bug
问题描述:
- 当sync_mode=full且incoming_ids为空时,条件判断失败
- 导致旧的孤儿ROI配置残留在本地数据库
- 后续配置更新时尝试启动孤儿ROI对应的摄像头,产生警告

根本原因:
- line 889: if self._db_manager and incoming_ids:
- 当incoming_ids为空列表时,条件判断为False
- 跳过了清理旧配置的逻辑

修复方案:
- 移除incoming_ids的条件判断
- 全量同步时始终执行清理逻辑
- incoming_ids为空时,清除所有旧配置(符合全量同步语义)
- incoming_ids不为空时,清除不在列表中的旧配置

附加工具:
- cleanup_orphan_rois.py: 清理当前残留的孤儿ROI记录

影响:
- 修复配置同步逻辑bug
- 避免孤儿ROI警告
- 提高配置同步的可靠性

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-14 11:25:42 +08:00
4863d86f04 tools(diagnose): 添加摄像头配置诊断脚本
- 检测孤儿ROI记录(ROI存在但摄像头配置缺失)
- 诊断出3个有问题的摄像头ID
- 提供根本原因分析和解决方案

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-14 11:17:49 +08:00
0bb9f7ebd2 feat(inference): 提高置信度阈值到0.5 - 减少误检
- conf_threshold: 0.4 → 0.5
- 提高检测精度,减少误报
- 同时更新环境变量默认值

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-14 11:13:45 +08:00
6f9cb9d960 docs(intrusion): 添加自动告警处理机制说明文档
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-14 10:59:32 +08:00
4f755dc7ec feat(intrusion): main.py支持intrusion告警的alarm_id回填
- 在main.py中添加intrusion告警的alarm_id回填逻辑
- 遵循与leave_post相同的模式
- 在告警创建后调用IntrusionAlgorithm.set_last_alarm_id()
- 使能intrusion告警的自动解除功能

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-14 10:27:39 +08:00
37fc48e34d fix(intrusion): 修复代码质量问题 - 空值检查和常量提取
修复内容:
1. 添加 state_start_time 空值检查(CONFIRMING_INTRUSION 和 CONFIRMING_CLEAR 状态)
2. 移除 get_state() 未使用的 roi_id 参数
3. get_state() 接受 current_time 参数,避免直接调用 datetime.now()
4. 提取魔法数字:ALARM_LEVEL_INTRUSION = 3

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-14 10:23:14 +08:00
2d90f00b11 feat(intrusion): 添加自动告警处理功能 - 状态机和resolve事件
- 新增状态机: IDLE → CONFIRMING_INTRUSION → ALARMED → CONFIRMING_CLEAR → IDLE
- 入侵消失后,连续confirm_seconds秒无人,自动发送alarm_resolve
- 告警追踪: _last_alarm_id, _intrusion_start_time
- 冷却期逻辑保留,防止重复告警

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-14 10:14:46 +08:00
d6644a65f3 chore: 更新 .gitignore - 排除诊断文档和临时脚本
新增忽略规则:
- 诊断文档: *诊断*.md, *分析*.md, *报告*.md
- 临时脚本: *修复*.sh, *patch*.py

目的:防止临时调试文件和诊断文档被误提交

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-14 09:30:50 +08:00
4153efaae9 refactor(main): 优化摄像头视频流启动逻辑 - 只启动有ROI配置的摄像头
问题:
- 旧逻辑启动所有数据库中的摄像头视频流(7个)
- 实际只有3个摄像头有ROI配置
- 浪费带宽和计算资源

解决方案:
1. 新增 _get_camera_ids_with_roi() 方法
   - 从ROI配置中提取有配置的摄像头ID集合
   - 返回去重后的摄像头ID set

2. 新增 _get_camera_config_by_id() 方法
   - 根据ID获取摄像头配置对象
   - 提高代码复用性和可维护性

3. 重构 _load_cameras() 方法
   - 只启动有ROI配置的摄像头
   - 添加详细的成功/失败统计
   - 改进日志信息,便于排查问题

4. 重构 _reload_cameras() 方法
   - 配置更新时只添加有ROI且未启动的摄像头
   - 使用集合运算提高性能
   - 统一错误处理逻辑

5. 新增 _cleanup_cameras_without_roi() 方法
   - 清理已启动但没有ROI的摄像头流
   - 当用户删除所有ROI时自动停止视频流
   - 节省系统资源

6. 更新配置回调逻辑
   - 先清理无ROI的摄像头
   - 再添加新增的有ROI的摄像头
   - 保证视频流与ROI配置同步

优势:
- 资源利用率提升:只启动必要的视频流
- 代码模块化:提取公共逻辑,提高复用性
- 可维护性强:清晰的注释和文档字符串
- 日志完善:详细的统计信息便于监控

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-14 09:18:58 +08:00
6d408386bc test(aiot): 添加离岗告警完整流程集成测试
测试完整业务流程:
1. 人员上岗确认
2. 人员离开ROI
3. 离岗确认 → OFF_DUTY_COUNTDOWN
4. 倒计时结束 → ALARMED (验证告警无 duration_minutes,有 first_frame_time)
5. 创建告警记录 (验证 duration_ms=None)
6. 人员返回 → CONFIRMING_ON_DUTY
7. 回岗确认 → ON_DUTY (验证发送 resolve 事件)
8. 更新告警记录 (验证 duration_ms 已填充)

测试验证点:
- 告警触发时不包含 duration_minutes
- 告警创建时 duration_ms=NULL, last_frame_time=NULL
- resolve 事件包含正确的 duration_ms 和 last_frame_time
- 告警状态正确变更为 CLOSED/DONE

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-13 10:01:10 +08:00
690eb66277 fix(aiot): 告警触发时不发送持续时长,等待回岗后再计算
修改算法层告警触发逻辑:
- 移除 duration_minutes 字段(告警触发时不计算持续时长)
- 新增 first_frame_time 字段(记录离开时间)
- 修改消息为固定文本"人员离岗告警"

持续时长改为在人员回岗时由 alarm_resolve 事件计算

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-13 09:42:25 +08:00
5b2440c467 refactor(aiot): 离岗检测算法v2.0 - 全面重构
重构目标:
- 更清晰的状态机设计
- 滑动窗口平滑检测结果
- 更准确的告警判断逻辑

核心改进:
1. 状态机优化(6个状态 → 7个状态)
   - INIT: 初始化
   - CONFIRMING_ON_DUTY: 上岗确认中
   - ON_DUTY: 在岗
   - CONFIRMING_OFF_DUTY: 离岗确认中
   - OFF_DUTY_COUNTDOWN: 离岗倒计时
   - ALARMED: 已告警
   - NON_WORK_TIME: 非工作时间

2. 滑动窗口机制
   - 10秒滑动窗口,存储检测历史
   - 计算命中率(person_count / total_frames)
   - 上岗条件:命中率 ≥ 70%(允许30%漏检)
   - 离岗条件:命中率 = 0(窗口内完全没人)

3. 参数优化
   - confirm_on_duty_sec: 上岗确认(默认10秒)
   - confirm_off_duty_sec: 离岗确认(默认30秒)
   - confirm_return_sec: 回岗确认(默认10秒)
   - leave_countdown_sec: 离岗倒计时(默认300秒)
   - 向后兼容:confirm_leave_sec → confirm_off_duty_sec

4. 状态监控增强
   - get_state() 返回详细状态信息
   - 包含倒计时剩余时间、检测命中率等

5. 日志分级
   - INFO: 关键状态转换(确认上岗、确认离岗)
   - DEBUG: 次要状态转换(进入确认状态)
   - WARNING: 告警触发

技术细节:
- 使用deque实现O(1)滑动窗口更新
- 兼容旧参数名(confirm_leave_sec)
- 回岗自动发送resolve事件
- 非工作时间自动清理状态

影响范围:
- 告警判断更准确(抗漏检干扰)
- 状态转换更合理(细化确认流程)
- 调试更友好(详细状态信息)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-12 15:41:05 +08:00
7496a6fe04 feat(aiot): 实现离岗倒计时功能 - 修复持续时长过短问题
问题描述:
- 用户反馈告警持续时长只有48秒、103秒
- 预期:离岗倒计时5分钟后才告警,持续时长应>5.5分钟
- 根因:代码在"离岗确认"后立即触发告警,缺少倒计时环节

业务流程:
1. 上岗确认期:10秒(confirm_on_duty_sec)
2. 离岗确认期:30秒(confirm_leave_sec)
3. 离岗倒计时:300秒(leave_countdown_sec)← 新增
4. 告警冷却期:600秒(cooldown_sec)

修改内容:
1. LeavePostAlgorithm 构造函数
   - 新增 leave_countdown_sec 参数(默认300秒)
   - 新增 _off_duty_start_time 状态变量
   - 新增 _alarm_triggered 告警标志

2. LEAVING → OFF_DUTY 状态转换(Line 197-207)
   - 移除立即告警逻辑
   - 进入OFF_DUTY后仅记录时间,开始倒计时

3. OFF_DUTY 状态处理(Line 209-258)
   - 新增倒计时检查:off_duty_elapsed >= leave_countdown_sec
   - 倒计时结束才触发告警
   - 人员回岗时检查是否已告警,决定是否发送resolve事件

4. 算法实例创建(Line 600-607, 701-708)
   - 从配置读取 leave_countdown_sec(默认300秒)

5. reset() 方法
   - 清理新增状态变量

影响范围:
- 告警时机:从离岗确认后立即告警 → 倒计时结束后告警
- 持续时长:现在必然 >= 330秒(30s确认 + 300s倒计时)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-12 15:06:46 +08:00
4ebded3385 fix: 统一告警resolve事件的时间戳格式
问题描述:
- 告警结束时间使用 isoformat() 包含微秒
- 导致前端显示格式不一致(2026-02-12T14:23:42.331566)

修改内容:
- algorithms.py (2处)
  - 非工作时间resolve事件(line 135)
  - 人员回岗resolve事件(line 233)
  - 使用 strftime('%Y-%m-%d %H:%M:%S') 替代 isoformat()

影响范围:
- 告警resolve事件上报格式
- 与service端时间戳格式保持一致

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-12 14:57:22 +08:00