import sys, os from threading import Thread from queue import Queue, Empty _root = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) _gui = os.path.dirname(os.path.abspath(__file__)) for _p in [_root, _gui]: if _p not in sys.path: sys.path.insert(0, _p) from a_backend import ABackend from a_transceiver import ATransceiver from msg.container import MsgContainer class SerialBackend(ABackend): """Backend that reads from a serial (COM / tty) port.""" def __init__(self, port: str, baud_rate: int = 115200): self._port = port self._baud = baud_rate self._serial = None self._xcvr = None self._running = False self._queue: Queue = Queue() self._thread = Thread(target=self._run, daemon=True) def register_xcvr(self, xcvr): self._xcvr = xcvr xcvr.on_register(self) def send(self, xcvr: ATransceiver, data: bytes): self._queue.put(data) def connect(self): import serial self._serial = serial.Serial(self._port, self._baud, timeout=0.1) self._running = True def start(self): self._thread.start() def stop(self): self._running = False if self._thread.is_alive(): self._thread.join(timeout=2.0) def disconnect(self): if self._serial and self._serial.is_open: self._serial.close() def _run(self): while self._running: try: data = self._serial.read(64) if data and self._xcvr: self._xcvr.on_recv(MsgContainer(data, self._xcvr)) # Drain outgoing queue while True: try: self._serial.write(self._queue.get_nowait()) except Empty: break except Exception: self._running = False break