"""Qt serial/lifecycle port. Protocol decisions are exclusively in C99.""" from __future__ import annotations from PySide6.QtCore import QObject, QTimer, QElapsedTimer, Signal from PySide6.QtSerialPort import QSerialPort, QSerialPortInfo from .native import NativeAnalyzer class AnalyzerPort(QObject): changed = Signal() completed = Signal(object) error = Signal(str) log = Signal(str) def __init__(self, parent=None): super().__init__(parent) self.core = None self.active = False self._last_done = False self.serial = QSerialPort(self) self.serial.readyRead.connect(self._read) self.serial.errorOccurred.connect(self._serial_error) self.timer = QTimer(self) self.timer.setInterval(10) self.timer.timeout.connect(self._tick) self.clock = QElapsedTimer() @staticmethod def ports(): return [(p.portName(), p.description()) for p in QSerialPortInfo.availablePorts()] def open(self, name, demo=False): self.close() try: self.core = NativeAnalyzer() self.core.reset(demo) except RuntimeError as exc: self.error.emit(str(exc)) return if not demo: self.serial.setPortName(name) self.serial.setBaudRate(921600) self.serial.setDataBits(QSerialPort.DataBits.Data8) self.serial.setParity(QSerialPort.Parity.NoParity) self.serial.setStopBits(QSerialPort.StopBits.OneStop) self.serial.setFlowControl(QSerialPort.FlowControl.NoFlowControl) if not self.serial.open(QSerialPort.OpenModeFlag.ReadWrite): self.error.emit(self.serial.errorString()) return self.active = True self._last_done = False self.clock.start() self.timer.start() self.log.emit("ДЕМО: синтетические данные, триггер не моделируется" if demo else "%s · 921600 8N1" % name) self._pump() def close(self): self.active = False self.timer.stop() self.serial.close() self.changed.emit() def start(self, *settings): if not self.active or not self.core: return try: self.core.start(*settings) except ValueError as exc: self.error.emit(str(exc)) return self._last_done = False self._pump() def _tick(self): if not self.active: return self.core.tick(self.clock.restart()) self._pump() def _read(self): data = bytes(self.serial.readAll()) if self.active and data: self.log.emit("RX " + data.hex(" ").upper()) self.core.feed(data) self._pump() def _pump(self): if not self.active: return if self.core.state == NativeAnalyzer.ERROR: message = self.core.error self.close() self.error.emit(message) return packet = self.core.next_request() if packet: self.log.emit("TX " + packet.hex(" ").upper()) if self.serial.write(packet) != len(packet): self.core.fail_io() self._pump() return self.clock.restart() if self.core.state == NativeAnalyzer.DONE and not self._last_done: self._last_done = True self.completed.emit(self.core.capture()) self.changed.emit() def _serial_error(self, code): if self.active and code != QSerialPort.SerialPortError.NoError: message = self.serial.errorString() self.core.fail_io() self.close() self.error.emit(message)