feat: add FFI layer, protocol tests, mock transport, README
- 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)
This commit is contained in:
49
example/cpp/main.cpp
Normal file
49
example/cpp/main.cpp
Normal file
@@ -0,0 +1,49 @@
|
||||
#include "../../include/eskin_ffi.h"
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
|
||||
int main() {
|
||||
printf("ESkin SDK version: %u.%u.%u\n",
|
||||
eskin_version().major, eskin_version().minor, eskin_version().patch);
|
||||
|
||||
EskinDeviceHandle dev = eskin_open("/dev/ttyUSB0", nullptr);
|
||||
if (!dev) {
|
||||
printf("Failed to open device\n");
|
||||
return 1;
|
||||
}
|
||||
printf("Device opened\n");
|
||||
|
||||
uint8_t buf[256];
|
||||
uint32_t actual = 0;
|
||||
EskinSdkErrorCode err = eskin_read_register(dev, 0x0000, 4, buf, sizeof(buf), &actual);
|
||||
if (err == ESkinSuccess) {
|
||||
printf("Serial number (%u bytes): ", actual);
|
||||
for (uint32_t i = 0; i < actual; i++) {
|
||||
printf("%02X ", buf[i]);
|
||||
}
|
||||
|
||||
printf("\n");
|
||||
}
|
||||
else {
|
||||
printf("read_register failed: %d\n", err);
|
||||
}
|
||||
|
||||
err = eskin_read_register(dev, 0x000F, 2, buf, sizeof(buf), &actual);
|
||||
if (err == ESkinSuccess) {
|
||||
printf("Firmware version (%u bytes): ", actual);
|
||||
for (uint32_t i = 0; i < actual; i++) {
|
||||
printf("%02X", buf[i]);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
err = eskin_read_register(dev, 0x0500, 168, buf, sizeof(buf), &actual);
|
||||
if (err == ESkinSuccess) {
|
||||
printf("Combined force raw (%u bytes)\n");
|
||||
}
|
||||
|
||||
eskin_close(dev);
|
||||
printf("Device closed\n");
|
||||
return 0;
|
||||
}
|
||||
86
example/python/eskin_ffi.py
Normal file
86
example/python/eskin_ffi.py
Normal file
@@ -0,0 +1,86 @@
|
||||
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()
|
||||
0
example/python/example.py
Normal file
0
example/python/example.py
Normal file
Reference in New Issue
Block a user