"""High-level switch service selecting native/mock backend driver.""" from __future__ import annotations from dataclasses import dataclass, field from collections.abc import Callable import time from typing import TYPE_CHECKING from python_app.hardware_full.switch_drivers import ( H7992Driver, MockSwitchDriver, SwitchDriverProtocol, ) if TYPE_CHECKING: from python_app.models.run_config_schema import SwitchModel @dataclass(slots=True) class SwitchService: """Facade around concrete switch drivers.""" name: str positions: int mode: str = "mock" gpio_chip: str = "/dev/gpiochip0" pin_a: int = 17 pin_b: int = 27 default_position: int = 0 _driver: SwitchDriverProtocol = field(init=False, repr=False) def __post_init__(self) -> None: """Create underlying driver based on configured mode and type.""" self._driver = self._build_driver() @classmethod def from_model(cls, model: SwitchModel) -> SwitchService: """Build a switch service from a ``SwitchModel`` config section. The single construction path shared by every acquisition producer and capture workflow, so all devices drive switches identically. """ return cls( name=model.name, positions=model.positions, mode=model.driver_mode, gpio_chip=model.gpio_chip, pin_a=model.pin_a, pin_b=model.pin_b, default_position=model.default_position, ) def open(self) -> None: """Open underlying switch driver resources.""" self._driver.open() def close(self) -> None: """Close underlying switch driver resources.""" self._driver.close() def switch_to(self, position: int) -> None: """Switch to requested position.""" self._driver.switch_to(position) def position_count(self) -> int: """Return number of positions supported by the backend driver.""" return self._driver.position_count() @property def current_position(self) -> int: """Return current switch position reported by backend driver.""" return self._driver.current_position def _build_driver(self) -> SwitchDriverProtocol: """Instantiate concrete driver according to configured mode and driver kind.""" mode = self.mode.strip().lower() if mode == "mock": return MockSwitchDriver( name=self.name, positions=self.positions, default_position=self.default_position, ) if mode != "native": raise RuntimeError(f"Unsupported switch mode: {self.mode}") return H7992Driver( name=self.name, positions=self.positions, default_position=self.default_position, gpio_chip=self.gpio_chip, pin_a=self.pin_a, pin_b=self.pin_b, ) def select_and_settle( output_switch: SwitchDriverProtocol | None, input_switch: SwitchDriverProtocol | None, output: int, input: int, settling_ms: int, *, wait: Callable[[float], object] = time.sleep, ) -> bool: """Drive both switches and settle; return False when an interruptible wait stops.""" if output_switch is not None: output_switch.switch_to(output) if input_switch is not None: input_switch.switch_to(input) return not (settling_ms > 0 and wait(settling_ms / 1000.0))