diff --git a/algorithms.py b/algorithms.py index 1bf5af1..9846fe3 100644 --- a/algorithms.py +++ b/algorithms.py @@ -908,22 +908,111 @@ 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 config_manager = get_config_sync_manager() bindings = config_manager.get_bindings_from_redis("") - + for bind in bindings: bind_id = bind.get("bind_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 except Exception as e: logger.error(f"重新加载所有算法配置失败: {e}") diff --git a/main.py b/main.py index 199f907..5047693 100644 --- a/main.py +++ b/main.py @@ -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响应)