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>
This commit is contained in:
2026-02-25 11:24:41 +08:00
parent 75ea66c452
commit 0b0e793785
2 changed files with 99 additions and 9 deletions

View File

@@ -908,8 +908,90 @@ class AlgorithmManager:
logger.error(f"重新加载ROI算法配置失败: {e}")
return False
def reload_all_algorithms(self) -> int:
"""重新加载所有算法配置"""
def update_algorithm_params(self, roi_id: str, bind_id: str, bind_config: dict) -> bool:
"""仅更新算法参数,保留状态机
Args:
roi_id: ROI ID
bind_id: 绑定ID
bind_config: 绑定配置字典包含algo_code和params
Returns:
是否成功更新
"""
try:
import json
key = f"{roi_id}_{bind_id}"
# 算法实例不存在,创建新的
if roi_id not in self.algorithms or key not in self.algorithms[roi_id]:
return self.load_bind_from_redis(bind_id)
# 提取参数
params_str = bind_config.get("params", "{}")
params = json.loads(params_str) if isinstance(params_str, str) else params_str
algo_code = bind_config.get("algo_code")
# 获取现有算法实例
existing_algo = self.algorithms[roi_id][key].get(algo_code)
if existing_algo is None:
# 算法类型不匹配,重新创建
return self.load_bind_from_redis(bind_id)
# 更新参数(根据算法类型调用不同的更新方法)
if algo_code == "leave_post":
# 更新离岗检测参数
leave_countdown_sec = params.get("leave_countdown_sec", 300)
working_hours = params.get("working_hours", [])
confirm_on_duty_sec = params.get("confirm_on_duty_sec", 10)
confirm_leave_sec = params.get("confirm_leave_sec", 30)
cooldown_sec = params.get("cooldown_sec", 600)
target_class = params.get("target_class", "person")
existing_algo.leave_countdown_sec = leave_countdown_sec
existing_algo.working_hours = working_hours
existing_algo.confirm_on_duty_sec = confirm_on_duty_sec
existing_algo.confirm_leave_sec = confirm_leave_sec
existing_algo.cooldown_sec = cooldown_sec
existing_algo.target_class = target_class
logger.info(f"[{roi_id}_{bind_id}] 更新离岗检测参数: countdown={leave_countdown_sec}s, hours={len(working_hours)}")
elif algo_code == "intrusion":
# 更新周界入侵参数
confirm_intrusion_sec = params.get("confirm_intrusion_seconds") or params.get("confirm_seconds", 5)
confirm_clear_sec = params.get("confirm_clear_seconds", 180)
cooldown_sec = params.get("cooldown_seconds", 300)
target_class = params.get("target_class")
existing_algo.confirm_intrusion_seconds = confirm_intrusion_sec
existing_algo.confirm_clear_seconds = confirm_clear_sec
existing_algo.cooldown_seconds = cooldown_sec
if target_class is not None:
existing_algo.target_class = target_class
logger.info(f"[{roi_id}_{bind_id}] 更新周界入侵参数: intrusion={confirm_intrusion_sec}s, clear={confirm_clear_sec}s")
# 其他算法类型可以在此添加
return True
except Exception as e:
logger.error(f"更新算法参数失败 {roi_id}_{bind_id}: {e}")
return False
def reload_all_algorithms(self, preserve_state: bool = True) -> int:
"""重新加载所有算法配置
Args:
preserve_state: 是否保留算法状态默认True
True - 仅更新参数,保留状态机(用于配置更新)
False - 完全重置(用于手动重启)
Returns:
成功加载的算法数量
"""
count = 0
try:
from core.config_sync import get_config_sync_manager
@@ -919,11 +1001,18 @@ class AlgorithmManager:
for bind in bindings:
bind_id = bind.get("bind_id")
roi_id = bind.get("roi_id")
if preserve_state:
# 仅更新参数,不重置状态
if self.update_algorithm_params(roi_id, bind_id, bind):
count += 1
else:
# 完全重置
self.reset_algorithm(roi_id, bind_id)
if self.load_bind_from_redis(bind_id):
count += 1
logger.info(f"已重新加载 {count} 个算法配置")
logger.info(f"已重新加载 {count} 个算法配置 (preserve_state={preserve_state})")
return count
except Exception as e:
logger.error(f"重新加载所有算法配置失败: {e}")

View File

@@ -97,7 +97,8 @@ class EdgeInferenceService:
if self._settings.config_sync_mode == "LOCAL" and self._config_manager:
def _on_config_update(topic, data):
if self._algorithm_manager:
self._algorithm_manager.reload_all_algorithms()
# 保留状态地更新参数,避免告警重复
self._algorithm_manager.reload_all_algorithms(preserve_state=True)
# 配置更新后清理无ROI的摄像头流
self._cleanup_cameras_without_roi()
# 配置更新后动态加载新摄像头流异步执行不阻塞HTTP响应