107 lines
5.8 KiB
Python
107 lines
5.8 KiB
Python
"""USB CDC/COM worker for WG v1/v2/v3, with SET v2 block uploads. All waits run in a worker, never the GUI thread."""
|
||
import time
|
||
from PySide6.QtCore import QObject, QRunnable, Signal, QIODevice
|
||
from PySide6.QtSerialPort import QSerialPort
|
||
from set_devices.wavegen_protocol import WaveProtocol
|
||
|
||
class _Signals(QObject):
|
||
finished = Signal(object, str)
|
||
progress = Signal(int)
|
||
|
||
class WaveTransfer(QRunnable):
|
||
def __init__(self, port_name, address, action, waveform=None, port_factory=None):
|
||
super().__init__()
|
||
self.port_name, self.address, self.action, self.waveform = port_name, address, action, waveform
|
||
self.signals = _Signals()
|
||
self.port_factory = port_factory or QSerialPort
|
||
|
||
def run(self):
|
||
port = self.port_factory()
|
||
try:
|
||
protocol = WaveProtocol(self.address)
|
||
port.setPortName(self.port_name)
|
||
port.setBaudRate(115200)
|
||
port.setDataBits(QSerialPort.Data8)
|
||
port.setParity(QSerialPort.NoParity)
|
||
port.setStopBits(QSerialPort.OneStop)
|
||
port.setFlowControl(QSerialPort.NoFlowControl)
|
||
if not port.open(QIODevice.ReadWrite):
|
||
raise OSError(port.errorString())
|
||
port.clear()
|
||
|
||
def transfer(request, decode):
|
||
if port.write(request) != len(request):
|
||
raise OSError(port.errorString())
|
||
if port.bytesToWrite() and not port.waitForBytesWritten(1000):
|
||
raise OSError("Таймаут передачи USB")
|
||
reply = bytearray()
|
||
budget = getattr(port, 'response_timeout', lambda ms: ms)(1500)
|
||
deadline = time.monotonic() + budget / 1000
|
||
while time.monotonic() < deadline:
|
||
if port.bytesAvailable() or port.waitForReadyRead(50):
|
||
reply.extend(bytes(port.readAll()))
|
||
response = decode(request, reply)
|
||
if response is not None:
|
||
return response
|
||
raise OSError("Нет подтверждения USB. Состояние выхода неизвестно; проверьте связь и нажмите Стоп.")
|
||
|
||
def exchange(operation, index=0, value=0):
|
||
return transfer(protocol.request(operation, index, value), protocol.response)
|
||
|
||
def read_status():
|
||
words = exchange(0)
|
||
result = protocol.status(words)
|
||
if result['version'] >= 2:
|
||
result = protocol.status(words, exchange(8)[0])
|
||
return result
|
||
|
||
status = read_status() # Positive device identification before any write.
|
||
if self.action == "upload":
|
||
wave = self.waveform
|
||
if wave is None or len(wave.codes) > status["capacity"]:
|
||
raise ValueError("Таблица не помещается в память устройства")
|
||
if status['version'] == 1 and wave.sample_rate > 50000:
|
||
raise ValueError("Для частоты выше 50 000 отсчётов/с обновите прошивку МК: нужна карта генератора версии 2")
|
||
exchange(1)
|
||
if status['version'] >= 2:
|
||
exchange(9, value=wave.sample_rate >> 16)
|
||
exchange(2, value=wave.sample_rate & 0xffff)
|
||
exchange(3, value=len(wave.codes))
|
||
blocks = status['version'] >= 3 and getattr(port, 'supports_wave_blocks', False)
|
||
step = 120 if blocks else 1
|
||
for index in range(0, len(wave.codes), step):
|
||
values = wave.codes[index:index + step]
|
||
if blocks:
|
||
transfer(protocol.block_request(index, values), protocol.block_response)
|
||
else:
|
||
exchange(4, index, values[0])
|
||
self.signals.progress.emit(int((index + len(values)) * 50 / len(wave.codes)))
|
||
for index in range(0, len(wave.codes), step):
|
||
values = wave.codes[index:index + step]
|
||
actual = (transfer(protocol.block_request(index, count=len(values)), protocol.block_response)
|
||
if blocks else exchange(7, index))
|
||
if tuple(actual) != tuple(values):
|
||
raise ValueError(f"Проверка таблицы не пройдена: отсчёты с {index}")
|
||
self.signals.progress.emit(50 + int((index + len(values)) * 50 / len(wave.codes)))
|
||
exchange(5)
|
||
elif self.action == "start":
|
||
if not status["ready"]:
|
||
raise ValueError("Сначала загрузите и проверьте таблицу")
|
||
exchange(6)
|
||
elif self.action == "stop":
|
||
exchange(1)
|
||
elif self.action != "status":
|
||
raise ValueError("Неизвестное действие")
|
||
result = read_status()
|
||
if self.action == "upload" and (not result["ready"] or result["running"] or
|
||
result["count"] != len(self.waveform.codes) or result["rate"] != self.waveform.sample_rate):
|
||
raise ValueError("Устройство не подтвердило загруженную таблицу")
|
||
if self.action in ("start", "stop") and result["running"] != (self.action == "start"):
|
||
raise ValueError("Устройство не подтвердило состояние выхода")
|
||
self.signals.progress.emit(100)
|
||
self.signals.finished.emit(result, "")
|
||
except Exception as error:
|
||
self.signals.finished.emit(None, str(error))
|
||
finally:
|
||
port.close()
|