"""Thin binding to the Balsam-167 physical EEPROM reader in the shared C core. No address conversion, endian/CRC codec or memory-size inference in this port. The caller owns serial scheduling, timeouts, cancellation and dump file storage. """ import ctypes as ct from .native import get_native_protocol, NativeProtocolUnavailable class _State(ct.Structure): _fields_ = [(name, ct.c_uint32) for name in ("controller", "start", "length", "received")] def _api(name, args, result): try: function = getattr(get_native_protocol().lib, "pcan_abi_periph28335_eeprom_" + name) except AttributeError as error: raise NativeProtocolUnavailable("Обновите templates/setprotocol.dll: нет API чтения EEPROM 167") from error function.argtypes, function.restype = args, result return function def capacity(m95m02=False): return int(_api("capacity_profile", [ct.c_uint], ct.c_uint32)(int(m95m02))) def write_request(controller, destination, source, length, *, m95m02=False): values = (controller, destination, source, length) if any(type(v) is not int or not 0 <= v <= 0xffffffff for v in values): raise ValueError("Адрес или длина вне диапазона uint32") output = ct.create_string_buffer(18) size = _api("write_profile", [ct.c_uint32]*4 + [ct.c_uint, ct.c_void_p, ct.c_size_t], ct.c_size_t)( *values, int(m95m02), output, len(output)) if not size: raise ValueError("LengthLoad или адрес выходит за область прошивки выбранной EEPROM либо пересекает область параметров. " "Предел от адреса 0: AT25256B — 32508 байт, M95M02 — 261884 байт.") return output.raw[:size] class Reader: def __init__(self, controller, start, length, *, block=False, m95m02=False): self.m95m02 = bool(m95m02) if m95m02 and not block: raise ValueError("Для M95M02 необходимо блочное чтение") self.block = block for value in (controller, start, length): if type(value) is not int or not 0 <= value <= 0xffffffff: raise ValueError("Адрес и длина должны быть неотрицательными целыми числами") self._state = _State() result = _api("begin_profile", [ct.c_void_p, ct.c_uint32, ct.c_uint32, ct.c_uint32, ct.c_uint], ct.c_int)( ct.byref(self._state), controller, start, length, int(self.m95m02)) if result: raise ValueError(f"Диапазон вне выбранной EEPROM: 0…0x{capacity(self.m95m02)-1:X}; длина должна быть больше нуля") if block: # Detect an old DLL before the GUI acquires ownership of the port. _api("block_request_profile", [ct.c_void_p, ct.c_void_p, ct.c_size_t, ct.c_uint], ct.c_size_t) _api("block_accept_profile", [ct.c_void_p, ct.c_void_p, ct.c_size_t, ct.c_void_p, ct.c_size_t, ct.c_uint], ct.c_int) self.expected @property def received(self): return int(self._state.received) @property def length(self): return int(self._state.length) @property def start(self): return int(self._state.start) @property def expected(self): if not self.block: return 8 return int(_api("block_expected_profile", [ct.c_void_p, ct.c_uint], ct.c_size_t)(ct.byref(self._state), int(self.m95m02))) def request(self): output = ct.create_string_buffer(8) args = [ct.c_void_p, ct.c_void_p, ct.c_size_t] + ([ct.c_uint] if self.block else []) extra = [int(self.m95m02)] if self.block else [] size = _api("block_request_profile" if self.block else "request", args, ct.c_size_t)( ct.byref(self._state), output, len(output), *extra) return output.raw[:size] def accept(self, reply): output = ct.create_string_buffer(240 if self.block else 2) reply = bytes(reply) args = [ct.c_void_p, ct.c_void_p, ct.c_size_t, ct.c_void_p, ct.c_size_t] + ([ct.c_uint] if self.block else []) extra = [int(self.m95m02)] if self.block else [] size = _api("block_accept_profile" if self.block else "accept", args, ct.c_int)( ct.byref(self._state), reply, len(reply), output, len(output), *extra) if size < 0: errors = {-3: "Неверная длина ответа EEPROM", -4: "Ошибка CRC ответа EEPROM", -5: "Неверный адрес контроллера или команда ответа EEPROM"} raise ValueError(errors.get(size, "Ответ EEPROM не соответствует активному чтению")) return output.raw[:size]