- FFI: eskin_open/close/read_register/write_register for C/C++/Python - Protocol: encode/decode tests with golden bytes verification - Stream: implement PollingSampleCollector producing FingerSample - Register: add parse_combined_forces/parse_module_errors - Transport: add MockSerialTransport for testing - Include: add C header file eskin_ffi.h - Examples: C++ and Python usage examples - README: full usage guide for Rust/C++/Python - Exclude docs/ from repo (internal only)
87 lines
2.5 KiB
Python
87 lines
2.5 KiB
Python
import ctypes
|
|
from ctypes import (
|
|
Structure, POINTER, c_void_p, c_char_p, c_uint8, c_uint16,
|
|
c_uint32, c_uint64, c_int16, c_bool
|
|
)
|
|
|
|
|
|
class EskinSdkVersion(Structure):
|
|
_fields_ = [
|
|
("major", c_uint16),
|
|
("minor", c_uint16),
|
|
("patch", c_uint16),
|
|
]
|
|
|
|
|
|
class EskinDevice:
|
|
def __init__(self, lib_path: str):
|
|
self._lib = ctypes.CDLL(lib_path)
|
|
self._setup_functions()
|
|
self._handle = None
|
|
|
|
def _setup_functions(self):
|
|
lib = self._lib
|
|
|
|
lib.eskin_version.restype = EskinSdkVersion
|
|
lib.eskin_version.argtypes = []
|
|
|
|
lib.eskin_open.restype = c_void_p
|
|
lib.eskin_open.argtypes = [c_char_p, c_void_p]
|
|
|
|
lib.eskin_close.restype = c_uint32
|
|
lib.eskin_close.argtypes = [c_void_p]
|
|
|
|
lib.eskin_read_register.restype = c_uint32
|
|
lib.eskin_read_register.argtypes = [
|
|
c_void_p, c_uint32, c_uint16,
|
|
POINTER(c_uint8), c_uint32, POINTER(c_uint32)
|
|
]
|
|
|
|
lib.eskin_write_register.restype = c_uint32
|
|
lib.eskin_write_register.argtypes = [
|
|
c_void_p, c_uint32, POINTER(c_uint8), c_uint16, POINTER(c_uint16)
|
|
]
|
|
|
|
def version(self) -> tuple:
|
|
v = self._lib.eskin_version()
|
|
return (v.major, v.minor, v.patch)
|
|
|
|
def open(self, path: str):
|
|
handle = self._lib.eskin_open(path.encode("utf-8"), None)
|
|
if not handle:
|
|
raise RuntimeError(f"Failed to open device: {path}")
|
|
self._handle = handle
|
|
|
|
def close(self):
|
|
if self._handle:
|
|
self._lib.eskin_close(self._handle)
|
|
self._handle = None
|
|
|
|
def read_register(self, addr: int, length: int) -> bytes:
|
|
"""读寄存器,返回原始字节"""
|
|
buf = (c_uint8 * 256)()
|
|
actual = c_uint32(0)
|
|
err = self._lib.eskin_read_register(
|
|
self._handle, addr, length, buf, len(buf), ctypes.byref(actual)
|
|
)
|
|
if err != 0:
|
|
raise RuntimeError(f"read_register failed: error={err}")
|
|
return bytes(buf[:actual.value])
|
|
|
|
def write_register(self, addr: int, data: bytes) -> int:
|
|
"""写寄存器,返回设备确认的字节数"""
|
|
arr = (c_uint8 * len(data))(*data)
|
|
ret = c_uint16(0)
|
|
err = self._lib.eskin_write_register(
|
|
self._handle, addr, arr, len(data), ctypes.byref(ret)
|
|
)
|
|
if err != 0:
|
|
raise RuntimeError(f"write_register failed: error={err}")
|
|
return ret.value
|
|
|
|
def __enter__(self):
|
|
return self
|
|
|
|
def __exit__(self, *args):
|
|
self.close()
|