fix(aiot): 告警触发时不发送持续时长,等待回岗后再计算
修改算法层告警触发逻辑: - 移除 duration_minutes 字段(告警触发时不计算持续时长) - 新增 first_frame_time 字段(记录离开时间) - 修改消息为固定文本"人员离岗告警" 持续时长改为在人员回岗时由 alarm_resolve 事件计算 Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -289,16 +289,14 @@ class LeavePostAlgorithm:
|
|||||||
(current_time - self.alert_cooldowns[cooldown_key]).total_seconds() > self.cooldown_sec:
|
(current_time - self.alert_cooldowns[cooldown_key]).total_seconds() > self.cooldown_sec:
|
||||||
|
|
||||||
bbox = self._get_latest_bbox(tracks, roi_id)
|
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({
|
alerts.append({
|
||||||
"track_id": roi_id,
|
"track_id": roi_id,
|
||||||
"camera_id": camera_id,
|
"camera_id": camera_id,
|
||||||
"bbox": bbox,
|
"bbox": bbox,
|
||||||
"duration_minutes": elapsed_minutes,
|
|
||||||
"alert_type": "leave_post",
|
"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
|
self.alert_cooldowns[cooldown_key] = current_time
|
||||||
|
|||||||
62
test_leave_post_no_duration.py
Normal file
62
test_leave_post_no_duration.py
Normal file
@@ -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()
|
||||||
Reference in New Issue
Block a user