Add shared waveform processing, generation and firmware image support

This commit is contained in:
2026-09-27 01:45:08 +03:00
parent 2ad29e7ffd
commit cc22c803d1
36 changed files with 2591 additions and 61 deletions

View File

@@ -0,0 +1,106 @@
"""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()