feat(aiot): 告警冷却时间调整 + 截图本地保留 + 中文路径修复

- 离岗检测冷却时间: 300s → 600s(10分钟)
- 入侵检测冷却时间: 120s → 300s(5分钟)
- 入侵告警级别改为高(alarm_level=3)
- COS 不可用时保留本地截图文件,不再上报后删除
- 修复 cv2.imwrite 中文路径失败,改用 imencode + write_bytes
- 配置订阅在 LOCAL 模式下跳过 Redis 连接

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-11 09:57:02 +08:00
parent e828f4e09b
commit 181623428a
8 changed files with 532 additions and 66 deletions

122
main.py
View File

@@ -14,6 +14,7 @@ from typing import Dict, Any, Optional, List, Tuple
from config.settings import get_settings, Settings
from core.config_sync import get_config_sync_manager, ConfigSyncManager
from core.debug_http_server import start_debug_http_server
from core.video_stream import MultiStreamManager, VideoFrame
from core.preprocessor import ImagePreprocessor
from core.tensorrt_engine import TensorRTEngine, EngineManager
@@ -48,6 +49,9 @@ class EdgeInferenceService:
self._reporter: Optional[ResultReporter] = None
self._alarm_worker: Optional[AlarmUploadWorker] = None
self._algorithm_manager: Optional[AlgorithmManager] = None
self._debug_reload_thread: Optional[threading.Thread] = None
self._debug_http_server = None
self._debug_http_thread: Optional[threading.Thread] = None
self._processing_threads: Dict[str, threading.Thread] = {}
self._stop_event = threading.Event()
@@ -90,6 +94,13 @@ class EdgeInferenceService:
try:
self._config_manager = get_config_sync_manager()
self._config_manager.start_config_subscription()
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._reload_cameras()
self._config_manager.register_callback("config_update", _on_config_update)
self._logger.info("配置管理器初始化成功")
except Exception as e:
self._logger.error(f"配置管理器初始化失败: {e}")
@@ -127,6 +138,9 @@ class EdgeInferenceService:
self._logger.info("后处理器初始化成功")
def _init_reporter(self):
if self._settings.config_sync_mode == "LOCAL" and not self._settings.alarm_upload_enabled:
self._logger.info("LOCAL 模式且 ALARM_UPLOAD_ENABLED=0跳过告警上报组件初始化")
return
"""初始化结果上报器"""
try:
self._reporter = ResultReporter()
@@ -157,6 +171,70 @@ class EdgeInferenceService:
except Exception as e:
self._logger.error(f"算法管理器初始化失败: {e}")
def _start_debug_reload_watcher(self):
"""本地调试:监听文件触发同步"""
if self._settings.config_sync_mode != "LOCAL":
return
if not getattr(self._settings, "debug", None) or not self._settings.debug.enabled:
return
if not self._config_manager:
return
signal_file = self._settings.debug.reload_signal_file
def worker():
last_mtime = None
self._logger.info(f"[DEBUG] 本地同步模式已启用,监听: {signal_file}")
while not self._stop_event.is_set():
try:
if os.path.exists(signal_file):
mtime = os.path.getmtime(signal_file)
if last_mtime is None:
last_mtime = mtime
elif mtime != last_mtime:
last_mtime = mtime
ok = self._config_manager.reload_local_config_from_file()
if self._algorithm_manager:
self._algorithm_manager.reload_all_algorithms()
self._logger.info(f"[DEBUG] 本地配置已重新加载: {ok}")
time.sleep(1.0)
except Exception as e:
self._logger.warning(f"[DEBUG] 监听本地配置失败: {e}")
time.sleep(1.0)
self._debug_reload_thread = threading.Thread(
target=worker,
name="LocalConfigReloadWatcher",
daemon=True,
)
self._debug_reload_thread.start()
def _start_debug_http_server(self):
"""本地调试:启动 HTTP 同步接口"""
if self._settings.config_sync_mode != "LOCAL":
return
if not getattr(self._settings, "debug", None) or not self._settings.debug.enabled:
return
if self._debug_http_server is not None:
return
host = self._settings.debug.host
port = self._settings.debug.port
self._debug_http_server = start_debug_http_server(host, port)
def worker():
try:
self._debug_http_server.serve_forever()
except Exception as e:
self._logger.warning(f"[DEBUG] HTTP 服务器异常: {e}")
self._debug_http_thread = threading.Thread(
target=worker,
name="DebugHttpServer",
daemon=True,
)
self._debug_http_thread.start()
def initialize(self):
"""初始化所有组件"""
self._logger.info("=" * 50)
@@ -171,6 +249,8 @@ class EdgeInferenceService:
self._init_postprocessor()
self._init_reporter()
self._init_algorithm_manager()
self._start_debug_reload_watcher()
self._start_debug_http_server()
self._performance_stats["start_time"] = datetime.now()
@@ -187,7 +267,7 @@ class EdgeInferenceService:
def _load_cameras(self):
"""加载摄像头配置"""
cameras = self._config_manager.get_cameras()
for camera in cameras:
try:
self._stream_manager.add_stream(
@@ -199,6 +279,37 @@ class EdgeInferenceService:
self._logger.info(f"已添加摄像头: {camera.camera_id}")
except Exception as e:
self._logger.error(f"添加摄像头失败 {camera.camera_id}: {e}")
def _reload_cameras(self):
"""配置更新后动态加载新摄像头(不重复添加已有的)"""
if not self._stream_manager or not self._config_manager:
return
try:
cameras = self._config_manager.get_cameras(force_refresh=True)
existing = set(self._stream_manager._streams.keys())
added = 0
for camera in cameras:
if camera.camera_id in existing:
continue
if not camera.rtsp_url:
self._logger.warning(f"摄像头 {camera.camera_id} 无 rtsp_url跳过")
continue
try:
self._stream_manager.add_stream(
camera_id=camera.camera_id,
rtsp_url=camera.rtsp_url,
target_fps=self._settings.video_stream.default_fps,
on_frame_callback=self._create_frame_callback(camera.camera_id)
)
self._stream_manager._streams[camera.camera_id].start()
added += 1
self._logger.info(f"动态添加并启动摄像头: {camera.camera_id}")
except Exception as e:
self._logger.error(f"动态添加摄像头失败 {camera.camera_id}: {e}")
if added > 0:
self._logger.info(f"配置更新后新增 {added} 个摄像头流")
except Exception as e:
self._logger.error(f"动态加载摄像头失败: {e}")
def _create_frame_callback(self, camera_id: str):
"""创建帧处理回调"""
@@ -358,6 +469,9 @@ class EdgeInferenceService:
self._logger.warning("算法管理器不可用,跳过算法处理")
return
if self._reporter is None:
self._logger.debug("ResultReporter 未启用,跳过告警上报")
return
roi_id = roi.roi_id
algo_code = bind.algo_code
algo_params = bind.params or {}
@@ -542,6 +656,12 @@ class EdgeInferenceService:
if self._reporter:
self._reporter.close()
if self._debug_http_server:
try:
self._debug_http_server.shutdown()
except Exception:
pass
self._performance_stats["uptime_seconds"] = (
(datetime.now() - self._performance_stats["start_time"]).total_seconds()