"""CAN-транспорт расширения регистратора periph_28335. Служебные запросы идут на штатный RX ID платы 0xBA0000 + Mode - 1. Ответы имеют отдельный ID 0xBA0020 + Mode - 1. Технологическая телеметрия не принимается за ответ логгера. Данные кадра следуют порядку MDL, MDH существующей прошивки; каждое слово передаётся старшим байтом вперёд. """ from dataclasses import dataclass, field import struct from set_devices.ump_logger import BASE, DATA, LIVE, EMU_CONTROL MARKER = 0x1F00 MAX_WORDS = 124 def request_ids(mode): if not 1 <= mode <= 16: raise ValueError('Номер CAN-платы должен быть 1…16 (Mode прошивки)') return 0xBA0000 + mode - 1, 0xBA0020 + mode - 1 def build_request(mode, token, function, address, value): if not 1 <= token <= 255: raise ValueError('Неверный номер CAN-запроса') valid = ((function == 3 and ((address == BASE and value == 16) or (address == DATA and value in (40, 80, 120)) or (address == LIVE and value == MAX_WORDS) or (address == EMU_CONTROL and value == 4))) or (function == 6 and ((BASE <= address <= BASE+3 and 0 <= value <= 65535) or (address == EMU_CONTROL+2 and value in (0, 1))))) if not valid: raise ValueError('Недопустимый запрос CAN-логгера') can_id, _ = request_ids(mode) return can_id, struct.pack('>4H', address, value, MARKER, token << 8 | function) @dataclass class Response: """Собирает один снимок. Дубликаты допустимы, противоречащие — ошибка. Номер фрагмента позволяет восстановить порядок при задержках адаптера. До получения всех фрагментов снимок не передаётся в график/архив. """ mode: int token: int function: int address: int value: int fragments: dict = field(default_factory=dict) def feed(self, can_id, data): if can_id != request_ids(self.mode)[1] or len(data) != 8: return None first, second, header, tag = struct.unpack('>4H', bytes(data)) if not MARKER <= header < MARKER + 62 or tag >> 8 != self.token: return None if tag & 255: raise ValueError(f'Плата отклонила CAN-запрос логгера: ошибка {tag & 255}') count = self.value if self.function == 3 else 2 index = header - MARKER if index >= count // 2: raise ValueError('Лишний фрагмент CAN-ответа логгера') words = (first, second) if index in self.fragments and self.fragments[index] != words: raise ValueError('Противоречащие фрагменты CAN-ответа') self.fragments[index] = words if len(self.fragments) != count // 2: return None result = tuple(word for n in range(count // 2) for word in self.fragments[n]) if self.function == 6: if result != (self.address, self.value): raise ValueError('CAN-плата подтвердила другую запись') return () return result