78 lines
2.5 KiB
Python
78 lines
2.5 KiB
Python
from dataclasses import dataclass, field
|
|
from typing import List, Callable, Optional
|
|
import threading
|
|
import time
|
|
|
|
@dataclass
|
|
class MonDecoded:
|
|
cw_raw: int
|
|
sw: int
|
|
error_code: int
|
|
sa: int
|
|
tr: int
|
|
rt: int
|
|
wc: int
|
|
data: bytes
|
|
timetag: float
|
|
|
|
|
|
@dataclass
|
|
class MonitorInfo:
|
|
buffer: List[MonDecoded] = field(default_factory=list)
|
|
total_message: int = 0
|
|
processed_message: int = 0
|
|
size: int = 0
|
|
|
|
|
|
class MonitorBuffer:
|
|
"""Thread-safe monitor buffer providing driver-like API.
|
|
|
|
Methods:
|
|
- append(entry: MonDecoded): add one decoded message
|
|
- set_isr(callback): register ISR callback
|
|
- enable_isr(enable=True): enable/disable ISR invocation
|
|
- get_raw_buffer(): snapshot and clear internal buffer
|
|
"""
|
|
def __init__(self, buf_size: int = 512, isr_threshold: Optional[int] = None):
|
|
self._lock = threading.Lock()
|
|
self._buffer: List[MonDecoded] = []
|
|
self._buf_size = buf_size
|
|
self._isr_threshold = isr_threshold if isr_threshold is not None else max(1, buf_size // 2)
|
|
self._isr: Optional[Callable[[MonitorInfo], None]] = None
|
|
self._isr_enabled = False
|
|
|
|
def set_isr(self, callback: Callable[[MonitorInfo], None]):
|
|
self._isr = callback
|
|
|
|
def enable_isr(self, enable: bool = True):
|
|
self._isr_enabled = bool(enable)
|
|
|
|
def append(self, entry: MonDecoded):
|
|
with self._lock:
|
|
self._buffer.append(entry)
|
|
total = len(self._buffer)
|
|
# Optionally cap buffer to buf_size (drop oldest)
|
|
if len(self._buffer) > self._buf_size:
|
|
# drop oldest
|
|
self._buffer = self._buffer[-self._buf_size:]
|
|
# Check threshold outside lock for quick return
|
|
if total >= self._isr_threshold and self._isr_enabled and self._isr:
|
|
# build snapshot
|
|
with self._lock:
|
|
snapshot = MonitorInfo(buffer=list(self._buffer), total_message=len(self._buffer), processed_message=0, size=len(self._buffer))
|
|
# call ISR in daemon thread
|
|
try:
|
|
threading.Thread(target=self._isr, args=(snapshot,), daemon=True).start()
|
|
except Exception:
|
|
pass
|
|
|
|
def get_raw_buffer(self) -> MonitorInfo:
|
|
with self._lock:
|
|
snapshot = MonitorInfo(buffer=list(self._buffer), total_message=len(self._buffer), processed_message=0, size=len(self._buffer))
|
|
self._buffer = []
|
|
return snapshot
|
|
|
|
def size(self) -> int:
|
|
with self._lock:
|
|
return len(self._buffer)
|