2026-08-30 22:22:11 +08:00
|
|
|
|
"""分析事件桥 — 消费子进程 event_queue,在主进程写库"""
|
|
|
|
|
|
import logging
|
|
|
|
|
|
import queue
|
|
|
|
|
|
import threading
|
|
|
|
|
|
|
|
|
|
|
|
logger = logging.getLogger("analysis.event_bridge")
|
|
|
|
|
|
|
|
|
|
|
|
_BRIDGE = None
|
|
|
|
|
|
_BRIDGE_LOCK = threading.Lock()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class AnalysisEventBridge(object):
|
|
|
|
|
|
def __init__(self):
|
|
|
|
|
|
self._thread = None
|
|
|
|
|
|
self._running = False
|
|
|
|
|
|
self._queues = []
|
|
|
|
|
|
self._lock = threading.Lock()
|
2026-09-04 18:16:14 +08:00
|
|
|
|
self._latest = {}
|
2026-08-30 22:22:11 +08:00
|
|
|
|
|
|
|
|
|
|
def register_queue(self, q):
|
|
|
|
|
|
with self._lock:
|
|
|
|
|
|
if q not in self._queues:
|
|
|
|
|
|
self._queues.append(q)
|
|
|
|
|
|
|
|
|
|
|
|
def unregister_queue(self, q):
|
|
|
|
|
|
with self._lock:
|
|
|
|
|
|
if q in self._queues:
|
|
|
|
|
|
self._queues.remove(q)
|
|
|
|
|
|
|
|
|
|
|
|
def start(self):
|
|
|
|
|
|
if self._running:
|
|
|
|
|
|
return
|
|
|
|
|
|
self._running = True
|
|
|
|
|
|
self._thread = threading.Thread(target=self._loop, name="analysis-event-bridge", daemon=True)
|
|
|
|
|
|
self._thread.start()
|
|
|
|
|
|
logger.info("AnalysisEventBridge 已启动")
|
|
|
|
|
|
|
|
|
|
|
|
def _loop(self):
|
|
|
|
|
|
while self._running:
|
|
|
|
|
|
handled = False
|
|
|
|
|
|
with self._lock:
|
|
|
|
|
|
queues = list(self._queues)
|
|
|
|
|
|
for q in queues:
|
|
|
|
|
|
try:
|
|
|
|
|
|
while True:
|
|
|
|
|
|
msg = q.get_nowait()
|
|
|
|
|
|
self._handle(msg)
|
|
|
|
|
|
handled = True
|
|
|
|
|
|
except queue.Empty:
|
|
|
|
|
|
continue
|
|
|
|
|
|
if not handled:
|
|
|
|
|
|
threading.Event().wait(0.05)
|
|
|
|
|
|
|
|
|
|
|
|
def _handle(self, msg):
|
|
|
|
|
|
if not msg or len(msg) < 2:
|
|
|
|
|
|
return
|
|
|
|
|
|
kind, payload = msg[0], msg[1]
|
|
|
|
|
|
try:
|
|
|
|
|
|
if kind == "event":
|
|
|
|
|
|
self._on_event(payload)
|
|
|
|
|
|
elif kind == "touch":
|
|
|
|
|
|
self._on_touch(payload)
|
2026-09-04 18:16:14 +08:00
|
|
|
|
elif kind == "preview":
|
|
|
|
|
|
self._on_preview(payload)
|
2026-08-30 22:22:11 +08:00
|
|
|
|
except Exception as e:
|
|
|
|
|
|
logger.exception("事件桥处理失败: %s", e)
|
|
|
|
|
|
|
|
|
|
|
|
def _on_event(self, event):
|
|
|
|
|
|
from app.services.alarm_service import write_alarm, ALARM_EVENT_TYPES
|
|
|
|
|
|
etype = event.get("type", "")
|
|
|
|
|
|
if etype in ALARM_EVENT_TYPES:
|
|
|
|
|
|
write_alarm(event)
|
|
|
|
|
|
|
|
|
|
|
|
def _on_touch(self, payload):
|
|
|
|
|
|
# 已停用:不再写追踪快照
|
|
|
|
|
|
pass
|
|
|
|
|
|
|
2026-09-04 18:16:14 +08:00
|
|
|
|
def _on_preview(self, payload):
|
|
|
|
|
|
stream_id = payload.get("stream_id")
|
|
|
|
|
|
if stream_id is None:
|
|
|
|
|
|
return
|
|
|
|
|
|
with self._lock:
|
|
|
|
|
|
self._latest[int(stream_id)] = payload
|
|
|
|
|
|
|
|
|
|
|
|
def latest_preview(self, stream_id):
|
|
|
|
|
|
with self._lock:
|
|
|
|
|
|
value = self._latest.get(int(stream_id))
|
|
|
|
|
|
return dict(value) if value else None
|
|
|
|
|
|
|
|
|
|
|
|
def clear_preview(self, stream_id):
|
|
|
|
|
|
with self._lock:
|
|
|
|
|
|
self._latest.pop(int(stream_id), None)
|
|
|
|
|
|
|
2026-08-30 22:22:11 +08:00
|
|
|
|
|
|
|
|
|
|
def get_event_bridge():
|
|
|
|
|
|
global _BRIDGE
|
|
|
|
|
|
with _BRIDGE_LOCK:
|
|
|
|
|
|
if _BRIDGE is None:
|
|
|
|
|
|
_BRIDGE = AnalysisEventBridge()
|
|
|
|
|
|
_BRIDGE.start()
|
|
|
|
|
|
return _BRIDGE
|