Добавить протокол Altera Logic и общие клиенты прошивки
This commit is contained in:
114
python/altera_logic/qt_port.py
Normal file
114
python/altera_logic/qt_port.py
Normal file
@@ -0,0 +1,114 @@
|
||||
"""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)
|
||||
Reference in New Issue
Block a user