fix: 修复告警上报字段缺失和 MQTT 连接不稳定

- AlertInfo.to_dict() 补充 bind_id、device_id、algorithm 字段
- AlertInfo 新增 device_id 和 algorithm 属性
- MQTTConfig 新增 device_id 配置项(环境变量 EDGE_DEVICE_ID)
- main.py 创建 AlertInfo 时传入 device_id 和 algorithm
- 心跳上报使用配置的 device_id 代替硬编码字符串
- MQTT 协议从 MQTTv5 降级为 MQTTv311 提高兼容性
- MQTT client_id 添加随机后缀防止冲突

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-09 14:13:49 +08:00
parent 20ba192e7f
commit ff3d6e2653
3 changed files with 38 additions and 7 deletions

View File

@@ -49,6 +49,7 @@ class MQTTConfig:
broker_host: str = "localhost"
broker_port: int = 1883
client_id: str = "edge_inference_service"
device_id: str = "edge-001"
username: Optional[str] = None
password: Optional[str] = None
keepalive: int = 60
@@ -82,6 +83,21 @@ class InferenceConfig:
fp16_mode: bool = True
# COCO 数据集类别名称YOLO 模型使用)
COCO_CLASS_NAMES = [
"person", "bicycle", "car", "motorcycle", "airplane", "bus", "train", "truck", "boat",
"traffic light", "fire hydrant", "stop sign", "parking meter", "bench", "bird", "cat",
"dog", "horse", "sheep", "cow", "elephant", "bear", "zebra", "giraffe", "backpack",
"umbrella", "handbag", "tie", "suitcase", "frisbee", "skis", "snowboard", "sports ball",
"kite", "baseball bat", "baseball glove", "skateboard", "surfboard", "tennis racket",
"bottle", "wine glass", "cup", "fork", "knife", "spoon", "bowl", "banana", "apple",
"sandwich", "orange", "broccoli", "carrot", "hot dog", "pizza", "donut", "cake",
"chair", "couch", "potted plant", "bed", "dining table", "toilet", "tv", "laptop",
"mouse", "remote", "keyboard", "cell phone", "microwave", "oven", "toaster", "sink",
"refrigerator", "book", "clock", "vase", "scissors", "teddy bear", "hair drier", "toothbrush"
]
@dataclass
class CameraConfig:
"""单个摄像头配置"""
@@ -146,6 +162,7 @@ class Settings:
broker_host=os.getenv("MQTT_BROKER_HOST", "localhost"),
broker_port=int(os.getenv("MQTT_BROKER_PORT", "1883")),
client_id=os.getenv("MQTT_CLIENT_ID", "edge_inference_service"),
device_id=os.getenv("EDGE_DEVICE_ID", "edge-001"),
username=os.getenv("MQTT_USERNAME"),
password=os.getenv("MQTT_PASSWORD"),
)
@@ -170,6 +187,9 @@ class Settings:
self.log_file_backup_count = int(os.getenv("LOG_FILE_BACKUP_COUNT", "5"))
self.working_hours = self._parse_working_hours()
# 使用 COCO 类别名称
self.class_names = COCO_CLASS_NAMES
def _parse_working_hours(self) -> List[dict]:
"""解析工作时间配置"""

View File

@@ -12,6 +12,7 @@ import json
import logging
import threading
import time
import uuid
from datetime import datetime
from typing import Any, Dict, List, Optional, Callable
from dataclasses import dataclass, field
@@ -32,6 +33,8 @@ class AlertInfo:
roi_id: str
alert_type: str
bind_id: Optional[str] = None
device_id: Optional[str] = None
algorithm: Optional[str] = None
target_class: Optional[str] = None
confidence: Optional[float] = None
bbox: Optional[List[float]] = field(default_factory=list)
@@ -39,14 +42,17 @@ class AlertInfo:
screenshot: Optional[np.ndarray] = None
timestamp: datetime = field(default_factory=datetime.now)
duration_minutes: Optional[float] = None
def to_dict(self) -> Dict[str, Any]:
"""转换为字典"""
"""转换为字典(发送到告警平台的 MQTT 消息格式)"""
return {
"alert_id": self.alert_id,
"camera_id": self.camera_id,
"roi_id": self.roi_id,
"bind_id": self.bind_id,
"device_id": self.device_id,
"alert_type": self.alert_type,
"algorithm": self.algorithm,
"target_class": self.target_class,
"confidence": self.confidence,
"bbox": self.bbox,
@@ -110,19 +116,22 @@ class ResultReporter:
"""初始化MQTT客户端"""
self._logger.info(f"正在连接 MQTT: {self._mqtt_broker}:{self._mqtt_port}")
try:
# 给 client_id 添加随机后缀,防止冲突
unique_client_id = f"{self._mqtt_client_id}_{uuid.uuid4().hex[:8]}"
# 兼容不同版本的 paho-mqtt
try:
# paho-mqtt 2.0+ 版本
self._client = mqtt.Client(
client_id=self._mqtt_client_id,
protocol=mqtt.MQTTv5,
client_id=unique_client_id,
protocol=mqtt.MQTTv311,
callback_api_version=mqtt.CallbackAPIVersion.VERSION2
)
except (AttributeError, TypeError):
# paho-mqtt 1.x 版本
self._client = mqtt.Client(
client_id=self._mqtt_client_id,
protocol=mqtt.MQTTv5
client_id=unique_client_id,
protocol=mqtt.MQTTv311
)
self._client.on_connect = self._on_connect

View File

@@ -385,7 +385,9 @@ class EdgeInferenceService:
camera_id=camera_id,
roi_id=roi_id,
bind_id=bind.bind_id,
device_id=self._settings.mqtt.device_id,
alert_type=alert.get("alert_type", "detection"),
algorithm=algo_code,
target_class=alert.get("class", bind.target_class or "unknown"),
confidence=alert.get("confidence", 1.0),
bbox=alert.get("bbox", []),
@@ -472,7 +474,7 @@ class EdgeInferenceService:
}
if self._reporter:
self._reporter.report_heartbeat("edge_inference_device", status)
self._reporter.report_heartbeat(self._settings.mqtt.device_id, status)
except Exception as e:
self._logger.error(f"心跳上报失败: {e}")