From 690eb662778980acdd66173db28bc985939d9dea Mon Sep 17 00:00:00 2001 From: 16337 <1633794139@qq.com> Date: Fri, 13 Feb 2026 09:42:25 +0800 Subject: [PATCH] =?UTF-8?q?fix(aiot):=20=E5=91=8A=E8=AD=A6=E8=A7=A6?= =?UTF-8?q?=E5=8F=91=E6=97=B6=E4=B8=8D=E5=8F=91=E9=80=81=E6=8C=81=E7=BB=AD?= =?UTF-8?q?=E6=97=B6=E9=95=BF=EF=BC=8C=E7=AD=89=E5=BE=85=E5=9B=9E=E5=B2=97?= =?UTF-8?q?=E5=90=8E=E5=86=8D=E8=AE=A1=E7=AE=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 修改算法层告警触发逻辑: - 移除 duration_minutes 字段(告警触发时不计算持续时长) - 新增 first_frame_time 字段(记录离开时间) - 修改消息为固定文本"人员离岗告警" 持续时长改为在人员回岗时由 alarm_resolve 事件计算 Co-Authored-By: Claude Sonnet 4.5 --- algorithms.py | 6 ++-- test_leave_post_no_duration.py | 62 ++++++++++++++++++++++++++++++++++ 2 files changed, 64 insertions(+), 4 deletions(-) create mode 100644 test_leave_post_no_duration.py diff --git a/algorithms.py b/algorithms.py index 6f3ed7f..ae3e2d8 100644 --- a/algorithms.py +++ b/algorithms.py @@ -289,16 +289,14 @@ class LeavePostAlgorithm: (current_time - self.alert_cooldowns[cooldown_key]).total_seconds() > self.cooldown_sec: bbox = self._get_latest_bbox(tracks, roi_id) - total_off_duty_sec = (current_time - self._leave_start_time).total_seconds() - elapsed_minutes = int(total_off_duty_sec / 60) alerts.append({ "track_id": roi_id, "camera_id": camera_id, "bbox": bbox, - "duration_minutes": elapsed_minutes, "alert_type": "leave_post", - "message": f"离岗 {elapsed_minutes} 分钟", + "message": "人员离岗告警", + "first_frame_time": self._leave_start_time.strftime('%Y-%m-%d %H:%M:%S'), }) self.alert_cooldowns[cooldown_key] = current_time diff --git a/test_leave_post_no_duration.py b/test_leave_post_no_duration.py new file mode 100644 index 0000000..cedb67b --- /dev/null +++ b/test_leave_post_no_duration.py @@ -0,0 +1,62 @@ +import sys +sys.path.insert(0, 'C:/Users/16337/PycharmProjects/ai_edge') + +from algorithms import LeavePostAlgorithm +from datetime import datetime, timedelta + +def test_alarm_trigger_no_duration(): + """测试告警触发时不包含持续时长""" + algo = LeavePostAlgorithm( + confirm_on_duty_sec=1, + confirm_off_duty_sec=1, + leave_countdown_sec=2, + ) + + # 滑动窗口大小是10秒,我们需要在整个流程中考虑这一点 + t0 = datetime(2026, 2, 12, 14, 0, 0) + person_track = [{"matched_rois": [{"roi_id": "roi1"}], "class": "person"}] + + # 1. 上岗阶段:持续检测到人 (2秒,超过confirm_on_duty_sec=1秒) + for i in range(20): # 每100ms一帧,共2秒 + t = t0 + timedelta(milliseconds=i*100) + algo.process("roi1", "cam1", person_track, t) + + print(f"[1] 上岗后状态: {algo.state}") + assert algo.state == "ON_DUTY", f"Expected ON_DUTY, got {algo.state}" + + # 2. 离开阶段:等待11秒让窗口内的人检测完全过期,然后再发空帧 + # 关键:需要等待窗口过期(10秒) + 确认时间(1秒) + t_leave_start = t0 + timedelta(seconds=2) + + # 发送空帧超过窗口大小,确保detection_ratio变为0 + for i in range(120): # 每100ms一帧,共12秒 + t = t_leave_start + timedelta(milliseconds=i*100) + algo.process("roi1", "cam1", [], t) + + print(f"[2] 离开12秒后状态: {algo.state}") + + # 3. 继续发送空帧,等待倒计时结束 (leave_countdown_sec=2秒) + t_wait_alarm = t_leave_start + timedelta(seconds=12) + for i in range(30): # 每100ms一帧,共3秒 (超过倒计时) + t = t_wait_alarm + timedelta(milliseconds=i*100) + alerts = algo.process("roi1", "cam1", [], t) + if alerts: + break + + print(f"[3] 告警触发后状态: {algo.state}") + print(f" 告警数量: {len(alerts)}") + print(f" 告警内容: {alerts}") + + # ✅ 验证:告警不包含 duration_minutes + assert len(alerts) == 1, f"Expected 1 alert, got {len(alerts)}" + alarm = alerts[0] + assert alarm["alert_type"] == "leave_post" + assert "duration_minutes" not in alarm, "告警不应包含 duration_minutes" + assert "first_frame_time" in alarm, "告警应包含 first_frame_time" + assert alarm["message"] == "人员离岗告警", f"消息错误: {alarm['message']}" + + print(f"测试通过:告警不含持续时长") + print(f" first_frame_time: {alarm['first_frame_time']}") + +if __name__ == "__main__": + test_alarm_trigger_no_duration()