feat(protocan): вынеси GUI-кадрирование в общий ABI
This commit is contained in:
@@ -28,6 +28,15 @@ class _AbiFrame(ctypes.Structure):
|
||||
]
|
||||
|
||||
|
||||
class _AbiGuiFrame(ctypes.Structure):
|
||||
_fields_ = [
|
||||
("sequence", ctypes.c_uint16),
|
||||
("size", ctypes.c_uint16),
|
||||
("message_type", ctypes.c_uint8),
|
||||
("payload", ctypes.c_uint8 * 512),
|
||||
]
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class NativeFrame:
|
||||
sequence: int
|
||||
@@ -36,6 +45,13 @@ class NativeFrame:
|
||||
data: bytes
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class NativeGuiFrame:
|
||||
message_type: int
|
||||
sequence: int
|
||||
payload: bytes
|
||||
|
||||
|
||||
def _library_candidates() -> Iterable[Path | str]:
|
||||
explicit = os.environ.get("SETCORE_LIBRARY")
|
||||
if explicit:
|
||||
@@ -108,6 +124,24 @@ class NativeCore:
|
||||
ctypes.POINTER(ctypes.c_uint32), ctypes.POINTER(ctypes.c_uint32),
|
||||
]
|
||||
lib.pcan_abi_parser_stats.restype = ctypes.c_int
|
||||
lib.pcan_abi_gui_crc32.argtypes = [ctypes.c_void_p, ctypes.c_size_t]
|
||||
lib.pcan_abi_gui_crc32.restype = ctypes.c_uint32
|
||||
lib.pcan_abi_gui_frame_encode.argtypes = [
|
||||
ctypes.c_uint8, ctypes.c_uint16, ctypes.c_void_p, ctypes.c_uint16,
|
||||
ctypes.c_void_p, ctypes.c_size_t,
|
||||
]
|
||||
lib.pcan_abi_gui_frame_encode.restype = ctypes.c_size_t
|
||||
lib.pcan_abi_gui_parser_size.restype = ctypes.c_size_t
|
||||
lib.pcan_abi_gui_parser_init.argtypes = [ctypes.c_void_p, ctypes.c_size_t]
|
||||
lib.pcan_abi_gui_parser_init.restype = ctypes.c_int
|
||||
lib.pcan_abi_gui_parser_push.argtypes = [
|
||||
ctypes.c_void_p, ctypes.c_uint8, ctypes.POINTER(_AbiGuiFrame),
|
||||
]
|
||||
lib.pcan_abi_gui_parser_push.restype = ctypes.c_int
|
||||
lib.pcan_abi_gui_parser_stats.argtypes = [
|
||||
ctypes.c_void_p, *(ctypes.POINTER(ctypes.c_uint32) for _ in range(5)),
|
||||
]
|
||||
lib.pcan_abi_gui_parser_stats.restype = ctypes.c_int
|
||||
|
||||
def id_pack(self, priority: int, route: int, device_type: int,
|
||||
device_id: int, message_type: int, body: int) -> int:
|
||||
@@ -146,6 +180,26 @@ class NativeCore:
|
||||
def parser(self) -> "NativeParser":
|
||||
return NativeParser(self)
|
||||
|
||||
def gui_crc32(self, data: bytes) -> int:
|
||||
source = (ctypes.c_uint8 * len(data)).from_buffer_copy(data) if data else None
|
||||
return int(self.lib.pcan_abi_gui_crc32(source, len(data)))
|
||||
|
||||
def gui_encode(self, message_type: int, sequence: int,
|
||||
payload: bytes) -> bytes:
|
||||
if len(payload) > 512:
|
||||
raise ValueError("GUI payload cannot exceed 512 bytes")
|
||||
source = ((ctypes.c_uint8 * len(payload)).from_buffer_copy(payload)
|
||||
if payload else None)
|
||||
output = (ctypes.c_uint8 * (8 + 512 + 4))()
|
||||
size = int(self.lib.pcan_abi_gui_frame_encode(
|
||||
message_type, sequence, source, len(payload), output, len(output)))
|
||||
if size == 0:
|
||||
raise ValueError("SETCore rejected the GUI frame")
|
||||
return bytes(output[:size])
|
||||
|
||||
def gui_parser(self) -> "NativeGuiParser":
|
||||
return NativeGuiParser(self)
|
||||
|
||||
|
||||
class NativeParser:
|
||||
def __init__(self, core: NativeCore) -> None:
|
||||
@@ -182,6 +236,42 @@ class NativeParser:
|
||||
}
|
||||
|
||||
|
||||
class NativeGuiParser:
|
||||
def __init__(self, core: NativeCore) -> None:
|
||||
self._core = core
|
||||
size = int(core.lib.pcan_abi_gui_parser_size())
|
||||
self._storage = ctypes.create_string_buffer(size)
|
||||
if not core.lib.pcan_abi_gui_parser_init(self._storage, size):
|
||||
raise NativeCoreUnavailable("SETCore GUI parser initialization failed")
|
||||
|
||||
def feed(self, chunk: bytes) -> list[NativeGuiFrame]:
|
||||
frames: list[NativeGuiFrame] = []
|
||||
raw = _AbiGuiFrame()
|
||||
for byte in chunk:
|
||||
result = self._core.lib.pcan_abi_gui_parser_push(
|
||||
self._storage, byte, ctypes.byref(raw))
|
||||
if result < 0:
|
||||
raise NativeCoreUnavailable("SETCore GUI parser rejected its context")
|
||||
if result > 0:
|
||||
frames.append(NativeGuiFrame(
|
||||
int(raw.message_type), int(raw.sequence),
|
||||
bytes(raw.payload[:raw.size])))
|
||||
return frames
|
||||
|
||||
def stats(self) -> dict[str, int]:
|
||||
values = [ctypes.c_uint32() for _ in range(5)]
|
||||
if not self._core.lib.pcan_abi_gui_parser_stats(
|
||||
self._storage, *(ctypes.byref(value) for value in values)):
|
||||
raise NativeCoreUnavailable("SETCore GUI parser stats unavailable")
|
||||
return {
|
||||
"frames": values[0].value,
|
||||
"crc_errors": values[1].value,
|
||||
"version_errors": values[2].value,
|
||||
"length_errors": values[3].value,
|
||||
"stray_bytes": values[4].value,
|
||||
}
|
||||
|
||||
|
||||
_default_core: NativeCore | None = None
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user