Просмотр исходного кода

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

wenhongquan 1 день назад
Родитель
Сommit
1a907d2ea1
2 измененных файлов с 136 добавлено и 0 удалено
  1. 84 0
      dual_camera_system/event_pusher.py
  2. 52 0
      dual_camera_system/tests/test_event_pusher_upload.py

+ 84 - 0
dual_camera_system/event_pusher.py

@@ -8,11 +8,13 @@ import time
 import json
 import json
 import threading
 import threading
 import queue
 import queue
+import tempfile
 import requests
 import requests
 import http.client
 import http.client
 import mimetypes
 import mimetypes
 from typing import Optional, Dict, Any, List
 from typing import Optional, Dict, Any, List
 from dataclasses import dataclass
 from dataclasses import dataclass
+from datetime import datetime
 from enum import Enum
 from enum import Enum
 from codecs import encode
 from codecs import encode
 
 
@@ -80,6 +82,12 @@ class EventPusher:
         self.api_port = self.config.get('api_port', 8583)
         self.api_port = self.config.get('api_port', 8583)
         self.use_https = self.config.get('use_https', True)
         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.upload_url = self.config.get('upload_url', '/api/resource/oss/upload')
         self.event_url = self.config.get('event_url', '/api/system/event')
         self.event_url = self.config.get('event_url', '/api/system/event')
@@ -180,6 +188,82 @@ class EventPusher:
         self.push_event(event)
         self.push_event(event)
         return True
         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):
     def _worker(self):
         """工作线程"""
         """工作线程"""
         while self.running:
         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