2026-01-29 18:43:19 +08:00
|
|
|
|
"""
|
|
|
|
|
|
主入口模块
|
|
|
|
|
|
整合所有模块,启动推理服务
|
|
|
|
|
|
"""
|
2026-01-29 18:33:12 +08:00
|
|
|
|
|
2026-01-29 18:43:19 +08:00
|
|
|
|
import logging
|
|
|
|
|
|
import os
|
|
|
|
|
|
import sys
|
|
|
|
|
|
import threading
|
|
|
|
|
|
import signal
|
|
|
|
|
|
import time
|
|
|
|
|
|
from datetime import datetime
|
|
|
|
|
|
from typing import Dict, Any, Optional
|
2026-01-29 18:33:12 +08:00
|
|
|
|
|
2026-01-29 18:43:19 +08:00
|
|
|
|
from config.settings import get_settings, Settings
|
|
|
|
|
|
from config.database import init_database
|
|
|
|
|
|
from core.config_sync import get_config_sync_manager, ConfigSyncManager
|
|
|
|
|
|
from core.video_stream import MultiStreamManager, VideoFrame
|
|
|
|
|
|
from core.preprocessor import ImagePreprocessor
|
|
|
|
|
|
from core.tensorrt_engine import TensorRTEngine, EngineManager
|
|
|
|
|
|
from core.postprocessor import PostProcessor
|
|
|
|
|
|
from core.result_reporter import ResultReporter
|
|
|
|
|
|
from utils.logger import get_logger, StructuredLogger
|
|
|
|
|
|
from utils.version_control import get_version_control
|
2026-01-29 18:33:12 +08:00
|
|
|
|
|
2026-01-29 18:43:19 +08:00
|
|
|
|
logger = logging.getLogger(__name__)
|
2026-01-29 18:33:12 +08:00
|
|
|
|
|
|
|
|
|
|
|
2026-01-29 18:43:19 +08:00
|
|
|
|
class EdgeInferenceService:
|
|
|
|
|
|
"""边缘推理服务主类
|
|
|
|
|
|
|
|
|
|
|
|
整合所有模块,提供完整的推理服务
|
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
|
|
def __init__(self):
|
|
|
|
|
|
self._running = False
|
|
|
|
|
|
self._settings = get_settings()
|
|
|
|
|
|
self._logger = get_logger("main")
|
|
|
|
|
|
self._version_control = get_version_control()
|
|
|
|
|
|
|
|
|
|
|
|
self._db_manager = None
|
|
|
|
|
|
self._config_manager: Optional[ConfigSyncManager] = None
|
|
|
|
|
|
self._stream_manager: Optional[MultiStreamManager] = None
|
|
|
|
|
|
self._preprocessor: Optional[ImagePreprocessor] = None
|
|
|
|
|
|
self._engine_manager: Optional[EngineManager] = None
|
|
|
|
|
|
self._postprocessor: Optional[PostProcessor] = None
|
|
|
|
|
|
self._reporter: Optional[ResultReporter] = None
|
|
|
|
|
|
|
|
|
|
|
|
self._processing_threads: Dict[str, threading.Thread] = {}
|
|
|
|
|
|
self._stop_event = threading.Event()
|
|
|
|
|
|
|
|
|
|
|
|
self._performance_stats = {
|
|
|
|
|
|
"start_time": None,
|
|
|
|
|
|
"total_frames_processed": 0,
|
|
|
|
|
|
"total_alerts_generated": 0,
|
|
|
|
|
|
"uptime_seconds": 0,
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
self._logger.info("Edge_Inference_Service 初始化开始")
|
|
|
|
|
|
|
|
|
|
|
|
def _init_database(self):
|
|
|
|
|
|
"""初始化数据库"""
|
|
|
|
|
|
try:
|
|
|
|
|
|
from config.database import DatabaseManager
|
|
|
|
|
|
self._db_manager = DatabaseManager()
|
|
|
|
|
|
if self._db_manager.is_available:
|
|
|
|
|
|
self._logger.info("数据库初始化成功")
|
|
|
|
|
|
else:
|
|
|
|
|
|
self._logger.warning("数据库不可见,服务将在无数据库模式下运行")
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
self._logger.warning(f"数据库初始化失败,服务将在无数据库模式下运行: {e}")
|
|
|
|
|
|
self._db_manager = None
|
|
|
|
|
|
|
|
|
|
|
|
def _init_config_manager(self):
|
|
|
|
|
|
"""初始化配置管理器"""
|
|
|
|
|
|
try:
|
|
|
|
|
|
self._config_manager = get_config_sync_manager()
|
|
|
|
|
|
self._config_manager.start_config_subscription()
|
|
|
|
|
|
self._logger.info("配置管理器初始化成功")
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
self._logger.error(f"配置管理器初始化失败: {e}")
|
|
|
|
|
|
raise
|
|
|
|
|
|
|
|
|
|
|
|
def _init_stream_manager(self):
|
|
|
|
|
|
"""初始化流管理器"""
|
|
|
|
|
|
self._stream_manager = MultiStreamManager()
|
|
|
|
|
|
self._logger.info("流管理器初始化成功")
|
|
|
|
|
|
|
|
|
|
|
|
def _init_preprocessor(self):
|
|
|
|
|
|
"""初始化预处理器"""
|
|
|
|
|
|
self._preprocessor = ImagePreprocessor()
|
|
|
|
|
|
self._logger.info("预处理器初始化成功")
|
|
|
|
|
|
|
|
|
|
|
|
def _init_engine(self):
|
|
|
|
|
|
"""初始化推理引擎"""
|
|
|
|
|
|
try:
|
|
|
|
|
|
self._engine_manager = EngineManager()
|
|
|
|
|
|
|
|
|
|
|
|
engine_path = self._settings.inference.model_path
|
|
|
|
|
|
if os.path.exists(engine_path):
|
|
|
|
|
|
self._engine_manager.load_engine("default", engine_path)
|
|
|
|
|
|
self._logger.info(f"推理引擎加载成功: {engine_path}")
|
|
|
|
|
|
else:
|
|
|
|
|
|
self._logger.warning(f"引擎文件不存在: {engine_path}")
|
|
|
|
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
self._logger.error(f"推理引擎初始化失败: {e}")
|
|
|
|
|
|
raise
|
|
|
|
|
|
|
|
|
|
|
|
def _init_postprocessor(self):
|
|
|
|
|
|
"""初始化后处理器"""
|
|
|
|
|
|
self._postprocessor = PostProcessor()
|
|
|
|
|
|
self._logger.info("后处理器初始化成功")
|
|
|
|
|
|
|
|
|
|
|
|
def _init_reporter(self):
|
|
|
|
|
|
"""初始化结果上报器"""
|
|
|
|
|
|
self._reporter = ResultReporter()
|
|
|
|
|
|
self._logger.info("结果上报器初始化成功")
|
|
|
|
|
|
|
|
|
|
|
|
def initialize(self):
|
|
|
|
|
|
"""初始化所有组件"""
|
|
|
|
|
|
self._logger.info("=" * 50)
|
|
|
|
|
|
self._logger.info("Edge_Inference_Service 启动")
|
|
|
|
|
|
self._logger.info("=" * 50)
|
|
|
|
|
|
|
|
|
|
|
|
self._init_database()
|
|
|
|
|
|
self._init_config_manager()
|
|
|
|
|
|
self._init_stream_manager()
|
|
|
|
|
|
self._init_preprocessor()
|
|
|
|
|
|
self._init_engine()
|
|
|
|
|
|
self._init_postprocessor()
|
|
|
|
|
|
self._init_reporter()
|
|
|
|
|
|
|
|
|
|
|
|
self._performance_stats["start_time"] = datetime.now()
|
|
|
|
|
|
|
|
|
|
|
|
self._version_control.record_update(
|
|
|
|
|
|
version="1.0.0",
|
|
|
|
|
|
update_type="启动",
|
|
|
|
|
|
description="Edge_Inference_Service 启动运行",
|
|
|
|
|
|
updated_by="系统",
|
|
|
|
|
|
affected_items=["全局"],
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
self._logger.info("所有组件初始化完成")
|
|
|
|
|
|
|
|
|
|
|
|
def _load_cameras(self):
|
|
|
|
|
|
"""加载摄像头配置"""
|
|
|
|
|
|
cameras = self._config_manager.get_cameras()
|
|
|
|
|
|
|
|
|
|
|
|
for camera in cameras:
|
|
|
|
|
|
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._logger.info(f"已添加摄像头: {camera.camera_id}")
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
self._logger.error(f"添加摄像头失败 {camera.camera_id}: {e}")
|
|
|
|
|
|
|
|
|
|
|
|
def _create_frame_callback(self, camera_id: str):
|
|
|
|
|
|
"""创建帧处理回调"""
|
|
|
|
|
|
def callback(frame):
|
|
|
|
|
|
self._process_frame(camera_id, frame)
|
|
|
|
|
|
return callback
|
|
|
|
|
|
|
|
|
|
|
|
def _process_frame(self, camera_id: str, frame: VideoFrame):
|
|
|
|
|
|
"""处理视频帧"""
|
|
|
|
|
|
try:
|
|
|
|
|
|
start_time = time.perf_counter()
|
|
|
|
|
|
|
|
|
|
|
|
roi_configs = self._config_manager.get_roi_configs(camera_id)
|
|
|
|
|
|
|
|
|
|
|
|
for roi in roi_configs:
|
|
|
|
|
|
self._process_roi_frame(camera_id, frame, roi)
|
|
|
|
|
|
|
|
|
|
|
|
processing_time_ms = (time.perf_counter() - start_time) * 1000
|
|
|
|
|
|
|
|
|
|
|
|
self._performance_stats["total_frames_processed"] += 1
|
|
|
|
|
|
|
|
|
|
|
|
self._logger.log_inference_latency(
|
|
|
|
|
|
processing_time_ms,
|
|
|
|
|
|
batch_size=1
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
self._logger.error(f"处理帧失败 {camera_id}: {e}")
|
|
|
|
|
|
|
|
|
|
|
|
def _process_roi_frame(
|
|
|
|
|
|
self,
|
|
|
|
|
|
camera_id: str,
|
|
|
|
|
|
frame: VideoFrame,
|
|
|
|
|
|
roi
|
|
|
|
|
|
):
|
|
|
|
|
|
"""处理ROI帧"""
|
|
|
|
|
|
try:
|
|
|
|
|
|
cropped = self._preprocessor.preprocess_single(frame.image, roi)
|
|
|
|
|
|
|
|
|
|
|
|
processed_image, scale_info = cropped
|
|
|
|
|
|
|
|
|
|
|
|
batch_data = self._preprocessor._batch_preprocessor._stack_and_normalize(
|
|
|
|
|
|
[processed_image]
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
engine = self._engine_manager.get_engine("default")
|
|
|
|
|
|
if engine is None:
|
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
|
|
outputs, inference_time_ms = engine.infer(batch_data)
|
|
|
|
|
|
|
|
|
|
|
|
boxes, scores, class_ids = self._postprocessor.process_detections(
|
|
|
|
|
|
outputs,
|
|
|
|
|
|
conf_threshold=self._settings.inference.conf_threshold
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
if len(boxes) > 0:
|
|
|
|
|
|
self._handle_detections(
|
|
|
|
|
|
camera_id, roi.roi_id, frame,
|
|
|
|
|
|
boxes, scores, class_ids,
|
|
|
|
|
|
scale_info
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
self._logger.error(f"处理ROI帧失败: {e}")
|
|
|
|
|
|
|
|
|
|
|
|
def _handle_detections(
|
|
|
|
|
|
self,
|
|
|
|
|
|
camera_id: str,
|
|
|
|
|
|
roi_id: str,
|
|
|
|
|
|
frame: VideoFrame,
|
|
|
|
|
|
boxes: any,
|
|
|
|
|
|
scores: any,
|
|
|
|
|
|
class_ids: any,
|
|
|
|
|
|
scale_info: tuple
|
|
|
|
|
|
):
|
|
|
|
|
|
"""处理检测结果"""
|
|
|
|
|
|
try:
|
|
|
|
|
|
from config.config_models import AlgorithmType
|
|
|
|
|
|
|
|
|
|
|
|
mapped_boxes = self._postprocessor.map_coordinates(
|
|
|
|
|
|
boxes, scale_info,
|
|
|
|
|
|
(frame.width, frame.height)
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
for i, box in enumerate(mapped_boxes):
|
|
|
|
|
|
detection_result = {
|
|
|
|
|
|
"class_id": int(class_ids[i]) if len(class_ids) > 0 else 0,
|
|
|
|
|
|
"confidence": float(scores[i]),
|
|
|
|
|
|
"bbox": box,
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
alert_result = self._postprocessor.check_alarm_condition(
|
|
|
|
|
|
roi_id, True, frame.timestamp
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
if alert_result["should_alert"]:
|
|
|
|
|
|
self._performance_stats["total_alerts_generated"] += 1
|
|
|
|
|
|
|
|
|
|
|
|
screenshot = frame.image
|
|
|
|
|
|
|
|
|
|
|
|
self._reporter.report_detection_alert(
|
|
|
|
|
|
camera_id=camera_id,
|
|
|
|
|
|
roi_id=roi_id,
|
|
|
|
|
|
alert_type="detection",
|
|
|
|
|
|
detection={
|
|
|
|
|
|
"class_name": f"class_{detection_result['class_id']}",
|
|
|
|
|
|
"confidence": detection_result["confidence"],
|
|
|
|
|
|
"bbox": detection_result["bbox"],
|
|
|
|
|
|
"message": f"检测到目标"
|
|
|
|
|
|
},
|
|
|
|
|
|
screenshot=screenshot
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
self._logger.log_alert(
|
|
|
|
|
|
"detection",
|
|
|
|
|
|
camera_id,
|
|
|
|
|
|
roi_id,
|
|
|
|
|
|
detection_result["confidence"]
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
self._logger.error(f"处理检测结果失败: {e}")
|
|
|
|
|
|
|
|
|
|
|
|
def start(self):
|
|
|
|
|
|
"""启动服务"""
|
|
|
|
|
|
if self._running:
|
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
|
|
self._running = True
|
|
|
|
|
|
self._stop_event.clear()
|
|
|
|
|
|
|
|
|
|
|
|
self._load_cameras()
|
|
|
|
|
|
|
|
|
|
|
|
self._stream_manager.start_all()
|
|
|
|
|
|
|
|
|
|
|
|
self._logger.info("Edge_Inference_Service 已启动")
|
|
|
|
|
|
|
|
|
|
|
|
self._start_heartbeat_thread()
|
|
|
|
|
|
|
|
|
|
|
|
self._register_signal_handlers()
|
|
|
|
|
|
|
|
|
|
|
|
self._wait_for_shutdown()
|
|
|
|
|
|
|
|
|
|
|
|
def _start_heartbeat_thread(self):
|
|
|
|
|
|
"""启动心跳线程"""
|
|
|
|
|
|
def heartbeat():
|
|
|
|
|
|
while not self._stop_event.is_set():
|
|
|
|
|
|
try:
|
|
|
|
|
|
uptime = (datetime.now() - self._performance_stats["start_time"]).total_seconds()
|
|
|
|
|
|
self._performance_stats["uptime_seconds"] = uptime
|
|
|
|
|
|
|
|
|
|
|
|
status = {
|
|
|
|
|
|
"running": True,
|
|
|
|
|
|
"uptime_seconds": uptime,
|
|
|
|
|
|
"frames_processed": self._performance_stats["total_frames_processed"],
|
|
|
|
|
|
"alerts_generated": self._performance_stats["total_alerts_generated"],
|
|
|
|
|
|
"stream_stats": self._stream_manager.get_statistics() if self._stream_manager else {},
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if self._reporter:
|
|
|
|
|
|
self._reporter.report_heartbeat("edge_inference_device", status)
|
|
|
|
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
self._logger.error(f"心跳上报失败: {e}")
|
|
|
|
|
|
|
|
|
|
|
|
time.sleep(30)
|
|
|
|
|
|
|
|
|
|
|
|
thread = threading.Thread(target=heartbeat, name="Heartbeat", daemon=True)
|
|
|
|
|
|
thread.start()
|
|
|
|
|
|
|
|
|
|
|
|
def _register_signal_handlers(self):
|
|
|
|
|
|
"""注册信号处理器"""
|
|
|
|
|
|
def handle_signal(signum, frame):
|
|
|
|
|
|
self._logger.info(f"收到信号 {signum}, 正在停止服务...")
|
|
|
|
|
|
self.stop()
|
|
|
|
|
|
|
|
|
|
|
|
signal.signal(signal.SIGINT, handle_signal)
|
|
|
|
|
|
signal.signal(signal.SIGTERM, handle_signal)
|
|
|
|
|
|
|
|
|
|
|
|
def _wait_for_shutdown(self):
|
|
|
|
|
|
"""等待关闭信号"""
|
|
|
|
|
|
while not self._stop_event.is_set():
|
|
|
|
|
|
time.sleep(1)
|
|
|
|
|
|
|
|
|
|
|
|
def stop(self):
|
|
|
|
|
|
"""停止服务"""
|
|
|
|
|
|
if not self._running:
|
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
|
|
self._running = False
|
|
|
|
|
|
self._stop_event.set()
|
|
|
|
|
|
|
|
|
|
|
|
if self._stream_manager:
|
|
|
|
|
|
self._stream_manager.stop_all()
|
|
|
|
|
|
self._stream_manager.close()
|
|
|
|
|
|
|
|
|
|
|
|
if self._engine_manager:
|
|
|
|
|
|
self._engine_manager.release_all()
|
|
|
|
|
|
|
|
|
|
|
|
if self._config_manager:
|
|
|
|
|
|
self._config_manager.stop_config_subscription()
|
|
|
|
|
|
self._config_manager.close()
|
|
|
|
|
|
|
|
|
|
|
|
if self._reporter:
|
|
|
|
|
|
self._reporter.close()
|
|
|
|
|
|
|
|
|
|
|
|
self._performance_stats["uptime_seconds"] = (
|
|
|
|
|
|
(datetime.now() - self._performance_stats["start_time"]).total_seconds()
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
self._logger.info("Edge_Inference_Service 已停止")
|
|
|
|
|
|
self._logger.info(f"运行统计: {self._performance_stats}")
|
|
|
|
|
|
|
|
|
|
|
|
def get_status(self) -> Dict[str, Any]:
|
|
|
|
|
|
"""获取服务状态"""
|
|
|
|
|
|
return {
|
|
|
|
|
|
"running": self._running,
|
|
|
|
|
|
"start_time": (
|
|
|
|
|
|
self._performance_stats["start_time"].isoformat()
|
|
|
|
|
|
if self._performance_stats["start_time"] else None
|
|
|
|
|
|
),
|
|
|
|
|
|
"uptime_seconds": self._performance_stats["uptime_seconds"],
|
|
|
|
|
|
"total_frames_processed": self._performance_stats["total_frames_processed"],
|
|
|
|
|
|
"total_alerts_generated": self._performance_stats["total_alerts_generated"],
|
|
|
|
|
|
"stream_manager": (
|
|
|
|
|
|
self._stream_manager.get_statistics()
|
|
|
|
|
|
if self._stream_manager else {}
|
|
|
|
|
|
),
|
|
|
|
|
|
"config_version": (
|
|
|
|
|
|
self._config_manager.config_version
|
|
|
|
|
|
if self._config_manager else None
|
|
|
|
|
|
),
|
|
|
|
|
|
}
|
2026-01-29 18:33:12 +08:00
|
|
|
|
|
2026-01-29 18:43:19 +08:00
|
|
|
|
|
|
|
|
|
|
def main():
|
|
|
|
|
|
"""主函数入口"""
|
|
|
|
|
|
service = EdgeInferenceService()
|
|
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
|
service.initialize()
|
|
|
|
|
|
service.start()
|
|
|
|
|
|
except KeyboardInterrupt:
|
|
|
|
|
|
service.stop()
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
logger.error(f"服务异常: {e}")
|
|
|
|
|
|
raise
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
|
|
main()
|