"""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()