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:
101
algorithms.py
101
algorithms.py
@@ -908,8 +908,90 @@ class AlgorithmManager:
|
|||||||
logger.error(f"重新加载ROI算法配置失败: {e}")
|
logger.error(f"重新加载ROI算法配置失败: {e}")
|
||||||
return False
|
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
|
count = 0
|
||||||
try:
|
try:
|
||||||
from core.config_sync import get_config_sync_manager
|
from core.config_sync import get_config_sync_manager
|
||||||
@@ -919,11 +1001,18 @@ class AlgorithmManager:
|
|||||||
for bind in bindings:
|
for bind in bindings:
|
||||||
bind_id = bind.get("bind_id")
|
bind_id = bind.get("bind_id")
|
||||||
roi_id = bind.get("roi_id")
|
roi_id = bind.get("roi_id")
|
||||||
self.reset_algorithm(roi_id, bind_id)
|
|
||||||
if self.load_bind_from_redis(bind_id):
|
|
||||||
count += 1
|
|
||||||
|
|
||||||
logger.info(f"已重新加载 {count} 个算法配置")
|
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} 个算法配置 (preserve_state={preserve_state})")
|
||||||
return count
|
return count
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"重新加载所有算法配置失败: {e}")
|
logger.error(f"重新加载所有算法配置失败: {e}")
|
||||||
|
|||||||
3
main.py
3
main.py
@@ -97,7 +97,8 @@ class EdgeInferenceService:
|
|||||||
if self._settings.config_sync_mode == "LOCAL" and self._config_manager:
|
if self._settings.config_sync_mode == "LOCAL" and self._config_manager:
|
||||||
def _on_config_update(topic, data):
|
def _on_config_update(topic, data):
|
||||||
if self._algorithm_manager:
|
if self._algorithm_manager:
|
||||||
self._algorithm_manager.reload_all_algorithms()
|
# 保留状态地更新参数,避免告警重复
|
||||||
|
self._algorithm_manager.reload_all_algorithms(preserve_state=True)
|
||||||
# 配置更新后清理无ROI的摄像头流
|
# 配置更新后清理无ROI的摄像头流
|
||||||
self._cleanup_cameras_without_roi()
|
self._cleanup_cameras_without_roi()
|
||||||
# 配置更新后动态加载新摄像头流(异步执行,不阻塞HTTP响应)
|
# 配置更新后动态加载新摄像头流(异步执行,不阻塞HTTP响应)
|
||||||
|
|||||||
Reference in New Issue
Block a user