"""Последовательный клиент CAN-логгера поверх уже подключённого Dima-адаптера.""" import secrets from .qt_compat import QObject, QTimer, Signal from set_devices.ump_logger_can import Response, build_request class UmpCanClient(QObject): completed = Signal(str, object) failed = Signal(str) def __init__(self, send, parent=None): super().__init__(parent) self._send = send self._token = secrets.randbelow(255) + 1 self._pending = None self._operation = '' self._timer = QTimer(self) self._timer.setSingleShot(True) self._timer.timeout.connect(self._timeout) @property def busy(self): return self._pending is not None def cancel(self): # Поздний ответ после переключения транспорта не должен продолжить # старую цепочку и отправить команду в новое подключение. self._timer.stop() self._pending = None self._operation = '' def request(self, operation, mode, function, address, value, timeout_ms=2000): if self.busy: self.failed.emit('CAN-логгер занят предыдущим запросом') return self._token = self._token % 255 + 1 try: can_id, data = build_request(mode, self._token, function, address, value) self._pending = Response(mode, self._token, function, address, value) self._operation = operation self._timer.start(timeout_ms) self._send(can_id, data) except (ValueError, RuntimeError, OSError) as error: self.cancel() self.failed.emit(str(error)) def receive(self, can_id, data): if self._pending is None: return try: words = self._pending.feed(can_id, data) except ValueError as error: self.cancel() self.failed.emit(str(error)) return if words is None: return operation = self._operation self.cancel() self.completed.emit(operation, words) def _timeout(self): writing = self._pending is not None and self._pending.function == 6 self.cancel() text = ('Таймаут CAN-логгера: проверьте адаптер, скорость, номер платы ' 'и прошивку с поддержкой CAN-логгера.') if writing: text += ' Подтверждение записи потеряно; команда могла выполниться.' self.failed.emit(text)