Refactor radar system
This commit is contained in:
@@ -1,102 +1,38 @@
|
||||
"""High-level orchestration service for configuring and querying LibreVNA."""
|
||||
"""Native single-device LibreVNA service used by acquisition and device-limit queries."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass, field
|
||||
import logging
|
||||
|
||||
from python_app.hardware_full.librevna_backends import LibreVnaBackend, MockLibreVnaBackend, NativeLibreVnaBackend
|
||||
from python_app.hardware_full.librevna_backends import NativeLibreVnaBackend
|
||||
from python_app.hardware_full.librevna_driver.models import SweepResult
|
||||
from python_app.models.run_config_model import RadarSweepModel
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@dataclass(slots=True)
|
||||
class LibreVnaService:
|
||||
"""Provide stable API for GUI/workflows while hiding backend details."""
|
||||
"""Provide the native single-device API; factories select mock acquisition."""
|
||||
|
||||
serial: str | None = None
|
||||
strict_protocol_version: int = 14
|
||||
backend_mode: str = "auto"
|
||||
_driver_available: bool = field(init=False, default=False, repr=False)
|
||||
_backend: LibreVnaBackend | None = field(init=False, default=None, repr=False)
|
||||
_using_mock_backend: bool = field(init=False, default=False, repr=False)
|
||||
_backend: NativeLibreVnaBackend = field(init=False, repr=False)
|
||||
|
||||
def __post_init__(self) -> None:
|
||||
"""Initialize selected backend and detect driver availability."""
|
||||
self._driver_available = False
|
||||
self._backend = None
|
||||
self._using_mock_backend = False
|
||||
|
||||
mode = self.backend_mode.strip().lower()
|
||||
if mode not in {"auto", "native", "mock"}:
|
||||
raise ValueError(f"Unsupported LibreVnaService backend mode: {self.backend_mode}")
|
||||
|
||||
if mode == "mock":
|
||||
logger.info("LibreVNA service using mock backend (mode=mock)")
|
||||
self._backend = MockLibreVnaBackend()
|
||||
self._using_mock_backend = True
|
||||
return
|
||||
|
||||
try:
|
||||
self._backend = NativeLibreVnaBackend(
|
||||
serial=self.serial,
|
||||
strict_protocol_version=self.strict_protocol_version,
|
||||
)
|
||||
self._driver_available = True
|
||||
logger.info("LibreVNA native backend initialized (serial=%s)", self.serial or "auto")
|
||||
except Exception as exc:
|
||||
# 'native' demands real hardware — never substitute synthetic data.
|
||||
if mode == "native":
|
||||
raise
|
||||
# 'auto' falls back to the mock backend ONLY when the native driver
|
||||
# library itself is unavailable (a dev host without the USB stack) —
|
||||
# this is not device-absence (that surfaces later from open()). Log it
|
||||
# loudly so it is never a silent surprise; a deployed appliance should
|
||||
# set driver_mode='native' to forbid the fallback entirely.
|
||||
logger.warning("LibreVNA native backend unavailable; falling back to mock (mode=auto): %s", exc)
|
||||
self._backend = MockLibreVnaBackend()
|
||||
self._using_mock_backend = True
|
||||
|
||||
@property
|
||||
def driver_available(self) -> bool:
|
||||
"""Return `True` when native Python LibreVNA driver is available."""
|
||||
return self._driver_available
|
||||
if self.backend_mode.strip().lower() not in {"auto", "native"}:
|
||||
raise ValueError(f"Unsupported native LibreVNA backend mode: {self.backend_mode}")
|
||||
self._backend = NativeLibreVnaBackend(self.serial, self.strict_protocol_version)
|
||||
|
||||
def open(self) -> None:
|
||||
"""Open backend resources."""
|
||||
if not self._driver_available and not self._using_mock_backend:
|
||||
return
|
||||
if self._backend is None:
|
||||
return
|
||||
logger.debug("Opening LibreVNA backend (mock=%s)", self._using_mock_backend)
|
||||
self._backend.open()
|
||||
|
||||
def close(self) -> None:
|
||||
"""Close backend resources."""
|
||||
if self._backend is None:
|
||||
return
|
||||
logger.debug("Closing LibreVNA backend")
|
||||
self._backend.close()
|
||||
|
||||
def configure(self, sweep: RadarSweepModel) -> None:
|
||||
"""Apply sweep settings to active backend."""
|
||||
if self._backend is None:
|
||||
raise RuntimeError("LibreVNA backend is not initialized")
|
||||
logger.debug(
|
||||
"Configuring LibreVNA sweep: %s-%s Hz, %s points",
|
||||
sweep.start_hz, sweep.stop_hz, sweep.points,
|
||||
)
|
||||
self._backend.configure(sweep)
|
||||
|
||||
def read_device_limits(self) -> dict[str, float | int]:
|
||||
"""Read native device limits from connected LibreVNA."""
|
||||
if not self._driver_available:
|
||||
raise RuntimeError("LibreVNA Python driver is not available")
|
||||
if self._backend is None:
|
||||
raise RuntimeError("LibreVNA backend is not initialized")
|
||||
|
||||
opened_here = not self._backend.is_open
|
||||
try:
|
||||
if opened_here:
|
||||
@@ -107,9 +43,4 @@ class LibreVnaService:
|
||||
self.close()
|
||||
|
||||
def acquire(self) -> SweepResult:
|
||||
"""Acquire one sweep with all available traces from active backend."""
|
||||
if self._backend is None:
|
||||
raise RuntimeError("LibreVNA backend is not initialized")
|
||||
if self._using_mock_backend and not self._driver_available and self.backend_mode != "mock":
|
||||
raise RuntimeError("Device not found")
|
||||
return self._backend.acquire()
|
||||
|
||||
Reference in New Issue
Block a user