Przeglądaj źródła

feat(event_pusher): add numpy image upload and tracking capture event

wenhongquan 1 dzień temu
rodzic
commit
1a907d2ea1

+ 84 - 0
dual_camera_system/event_pusher.py

@@ -8,11 +8,13 @@ import time
 import json
 import threading
 import queue
+import tempfile
 import requests
 import http.client
 import mimetypes
 from typing import Optional, Dict, Any, List
 from dataclasses import dataclass
+from datetime import datetime
 from enum import Enum
 from codecs import encode
 
@@ -80,6 +82,12 @@ class EventPusher:
         self.api_port = self.config.get('api_port', 8583)
         self.use_https = self.config.get('use_https', True)
         
+        # 基础 URL(优先使用配置中的 base_url)
+        self.base_url = self.config.get('base_url')
+        if not self.base_url:
+            protocol = 'https' if self.use_https else 'http'
+            self.base_url = f"{protocol}://{self.api_host}:{self.api_port}"
+        
         # 上传接口
         self.upload_url = self.config.get('upload_url', '/api/resource/oss/upload')
         self.event_url = self.config.get('event_url', '/api/system/event')
@@ -180,6 +188,82 @@ class EventPusher:
         self.push_event(event)
         return True
     
+    def upload_numpy_image(self, image: np.ndarray) -> Optional[str]:
+        """
+        将 numpy 图片上传到 OSS
+        
+        Args:
+            image: numpy 图像数组
+            
+        Returns:
+            图片URL或None
+        """
+        if image is None:
+            return None
+        
+        temp_path = None
+        try:
+            with tempfile.NamedTemporaryFile(suffix=".jpg", delete=False) as f:
+                temp_path = f.name
+            
+            cv2.imwrite(temp_path, image)
+            url = self._upload_image(temp_path)
+            return url
+        except Exception as e:
+            print(f"上传 numpy 图片失败: {e}")
+            return None
+        finally:
+            if temp_path:
+                try:
+                    os.remove(temp_path)
+                except Exception:
+                    pass
+    
+    def push_tracking_capture(self, batch_time: float, captures: List[dict]):
+        """
+        推送一轮多目标跟踪抓拍事件
+        
+        Args:
+            batch_time: 批次时间戳
+            captures: 抓拍记录列表
+            
+        Returns:
+            响应对象或None
+        """
+        payload = {
+            "eventType": "TRACKING_CAPTURE",
+            "eventTime": datetime.fromtimestamp(batch_time).isoformat(),
+            "deviceId": self.config.get("device_id"),
+            "data": {
+                "captureCount": len(captures),
+                "captures": captures,
+            }
+        }
+        url = f"{self.base_url}{self.event_url}"
+        return self._post(url, payload)
+    
+    def _post(self, url: str, json_data: dict):
+        """
+        发送 POST 请求
+        
+        Args:
+            url: 请求URL
+            json_data: JSON数据
+            
+        Returns:
+            响应对象或None
+        """
+        for attempt in range(self.retry_count):
+            try:
+                response = requests.post(url, json=json_data, verify=False, timeout=10)
+                return response
+            except Exception as e:
+                print(f"POST 请求异常 (尝试 {attempt + 1}/{self.retry_count}): {e}")
+                if attempt < self.retry_count - 1:
+                    time.sleep(self.retry_delay)
+        
+        return None
+    
     def _worker(self):
         """工作线程"""
         while self.running:

+ 52 - 0
dual_camera_system/tests/test_event_pusher_upload.py

@@ -0,0 +1,52 @@
+import sys
+import os
+sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
+
+import numpy as np
+import pytest
+from event_pusher import EventPusher
+
+
+def test_upload_numpy_image(monkeypatch):
+    config = {"device_id": "test-device", "base_url": "http://localhost"}
+    pusher = EventPusher(config)
+
+    called = {"path": None, "existed": False}
+
+    def fake_upload(path):
+        called["path"] = path
+        called["existed"] = os.path.exists(path)
+        return "http://example.com/image.jpg"
+
+    monkeypatch.setattr(pusher, "_upload_image", fake_upload)
+
+    img = np.zeros((100, 100, 3), dtype=np.uint8)
+    url = pusher.upload_numpy_image(img)
+
+    assert url == "http://example.com/image.jpg"
+    assert called["path"] is not None
+    assert called["existed"]
+
+
+def test_push_tracking_capture(monkeypatch):
+    config = {"device_id": "test-device", "base_url": "http://localhost"}
+    pusher = EventPusher(config)
+
+    captured = {}
+
+    def fake_post(url, json):
+        captured["url"] = url
+        captured["json"] = json
+        class Resp:
+            status_code = 200
+        return Resp()
+
+    monkeypatch.setattr(pusher, "_post", fake_post)
+
+    pusher.push_tracking_capture(
+        batch_time=1234567890.0,
+        captures=[{"track_id": 1, "ptz_image_url": "url1"}]
+    )
+
+    assert captured["json"]["eventType"] == "TRACKING_CAPTURE"
+    assert captured["json"]["data"]["captureCount"] == 1