init commit
This commit is contained in:
@@ -0,0 +1,146 @@
|
||||
"""Dataclass schema for runtime configuration used by Python pipeline tools."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass, field
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
|
||||
@dataclass(slots=True)
|
||||
class ComboModel:
|
||||
"""One switch combination used for an acquisition sweep."""
|
||||
|
||||
input: int
|
||||
output: int
|
||||
|
||||
|
||||
@dataclass(slots=True)
|
||||
class RadarSweepModel:
|
||||
"""Sweep settings for LibreVNA acquisition."""
|
||||
|
||||
# Keep schema defaults minimal/safe; operational values come from run_config.json.
|
||||
start_hz: float = 0.0
|
||||
stop_hz: float = 0.0
|
||||
points: int = 1
|
||||
if_bandwidth_hz: float = 1.0
|
||||
power_dbm: float = -30.0
|
||||
|
||||
|
||||
@dataclass(slots=True)
|
||||
class RadarModel:
|
||||
"""Radar section of run configuration."""
|
||||
|
||||
model: str = ""
|
||||
serial: str = ""
|
||||
driver_mode: str = "mock"
|
||||
mock_signal_hz: float = 1_000_000.0
|
||||
sweep: RadarSweepModel = field(default_factory=RadarSweepModel)
|
||||
|
||||
|
||||
@dataclass(slots=True)
|
||||
class SwitchModel:
|
||||
"""Generic switch section of run configuration."""
|
||||
|
||||
name: str
|
||||
driver_mode: str = "mock"
|
||||
driver: str = ""
|
||||
radar_port: int = 0
|
||||
positions: int = 1
|
||||
default_position: int = 0
|
||||
gpio_chip: str = ""
|
||||
pin_a: int = -1
|
||||
pin_b: int = -1
|
||||
invert_logic: bool = False
|
||||
|
||||
|
||||
@dataclass(slots=True)
|
||||
class RingEndpointModel:
|
||||
"""Shared-memory ring endpoint description."""
|
||||
|
||||
name: str
|
||||
capacity: int = 1
|
||||
slot_size_bytes: int = 4096
|
||||
|
||||
|
||||
@dataclass(slots=True)
|
||||
class RingsModel:
|
||||
"""Ring endpoints used by orchestration pipeline."""
|
||||
|
||||
raw: RingEndpointModel = field(default_factory=lambda: RingEndpointModel(name=""))
|
||||
raw_tap: RingEndpointModel = field(default_factory=lambda: RingEndpointModel(name=""))
|
||||
preprocessed: RingEndpointModel = field(default_factory=lambda: RingEndpointModel(name=""))
|
||||
preprocessed_tap: RingEndpointModel = field(default_factory=lambda: RingEndpointModel(name=""))
|
||||
results: RingEndpointModel = field(default_factory=lambda: RingEndpointModel(name=""))
|
||||
|
||||
|
||||
@dataclass(slots=True)
|
||||
class RuntimeModel:
|
||||
"""Runtime process behavior and paths."""
|
||||
|
||||
settling_ms: int = 0
|
||||
idle_sleep_ms: int = 2
|
||||
continuous: bool = False
|
||||
processing_live_config_path: str = ""
|
||||
|
||||
|
||||
@dataclass(slots=True)
|
||||
class PreprocessModel:
|
||||
"""Selected preprocessing artifacts for live acquisition."""
|
||||
|
||||
calibration_set: str = ""
|
||||
reference_set: str = ""
|
||||
calibration_bundle_path: str = ""
|
||||
reference_bundle_path: str = ""
|
||||
|
||||
|
||||
@dataclass(slots=True)
|
||||
class RunConfigModel:
|
||||
"""Top-level runtime config model consumed by C++ processes and GUI."""
|
||||
|
||||
radar: RadarModel = field(default_factory=RadarModel)
|
||||
input_switch: SwitchModel = field(default_factory=lambda: SwitchModel(name=""))
|
||||
output_switch: SwitchModel = field(default_factory=lambda: SwitchModel(name=""))
|
||||
rings: RingsModel = field(default_factory=RingsModel)
|
||||
runtime: RuntimeModel = field(default_factory=RuntimeModel)
|
||||
preprocess: PreprocessModel = field(default_factory=PreprocessModel)
|
||||
combos: list[ComboModel] = field(default_factory=list)
|
||||
|
||||
@staticmethod
|
||||
def build_full_combos(input_positions: int, output_positions: int) -> list[ComboModel]:
|
||||
"""Build full Cartesian product of input/output switch positions."""
|
||||
return [
|
||||
ComboModel(input=input_pos, output=output_pos)
|
||||
for output_pos in range(output_positions)
|
||||
for input_pos in range(input_positions)
|
||||
]
|
||||
|
||||
def ensure_combos(self) -> None:
|
||||
"""Populate combos with full matrix when no explicit run combos are set."""
|
||||
if self.combos:
|
||||
return
|
||||
self.combos = self.build_full_combos(self.input_switch.positions, self.output_switch.positions)
|
||||
|
||||
@classmethod
|
||||
def from_dict(cls, payload: dict[str, Any]) -> RunConfigModel:
|
||||
"""Build model from JSON-like payload using codec layer."""
|
||||
from python_app.models.run_config_codec import run_config_from_dict
|
||||
|
||||
return run_config_from_dict(payload)
|
||||
|
||||
@classmethod
|
||||
def load_from_path(cls, path: Path) -> RunConfigModel:
|
||||
"""Load JSON file from disk and decode into model."""
|
||||
from python_app.models.run_config_codec import load_run_config
|
||||
|
||||
return load_run_config(path)
|
||||
|
||||
def clone(self) -> RunConfigModel:
|
||||
"""Create deep copy through codec round-trip."""
|
||||
return RunConfigModel.from_dict(self.to_dict())
|
||||
|
||||
def to_dict(self) -> dict[str, Any]:
|
||||
"""Encode model into JSON-serializable dictionary."""
|
||||
from python_app.models.run_config_codec import run_config_to_dict
|
||||
|
||||
return run_config_to_dict(self)
|
||||
Reference in New Issue
Block a user