fix: protocol frame parsing, add debug feature, refactor read logic

This commit is contained in:
lenn
2026-05-06 17:16:51 +08:00
parent a7ef583d74
commit f984f330e8
7 changed files with 123 additions and 31 deletions

View File

@@ -7,6 +7,10 @@ edition = "2024"
name = "eskin_finger_sdk" name = "eskin_finger_sdk"
crate-type = ["lib", "cdylib", "staticlib"] crate-type = ["lib", "cdylib", "staticlib"]
[features]
default = []
debug = []
[dependencies] [dependencies]
chrono = "0.4.44" chrono = "0.4.44"
crc = "3.4.0" crc = "3.4.0"

View File

@@ -23,7 +23,7 @@ impl Default for DeviceConfig {
sample_capacity: 1024, sample_capacity: 1024,
command_capacity: 64, command_capacity: 64,
event_capacity: 128, event_capacity: 128,
read_timeout_ms: 100, read_timeout_ms: 1000,
} }
} }
} }

View File

@@ -6,7 +6,7 @@ use crate::{
config::{DeviceConfig, DeviceInfo}, config::{DeviceConfig, DeviceInfo},
error::SdkError, error::SdkError,
protocol::{ protocol::{
EskinProtocolCodec, FRAME_CRC_LEN, FRAME_START_RESPONSE, FRAME_STATUS_LEN, ProtocolCodec, EskinProtocolCodec, FRAME_START_RESPONSE, ProtocolCodec,
ReadRequest, WriteRequest, ReadRequest, WriteRequest,
}, },
stream::StreamRuntime, stream::StreamRuntime,
@@ -73,9 +73,19 @@ impl EskinDeviceInner {
buf: &mut [u8], buf: &mut [u8],
timeout: Duration, timeout: Duration,
) -> Result<(), SdkError> { ) -> Result<(), SdkError> {
let deadline = std::time::Instant::now()
+ timeout
.to_std()
.map_err(|_| SdkError::InvalidParameter("timeout must be non-negative".into()))?;
let mut offset = 0; let mut offset = 0;
while offset < buf.len() { while offset < buf.len() {
let n = transport.read(&mut buf[offset..], timeout)?; let remaining = deadline
.checked_duration_since(std::time::Instant::now())
.unwrap_or(std::time::Duration::from_millis(1));
debug_println!("[device] read_exact: need {} bytes, have {} so far, remaining timeout: {:?}", buf.len() - offset, offset, remaining);
let n = transport.read(&mut buf[offset..], Duration::from_std(remaining).unwrap())?;
debug_println!("[device] read_exact: got {} bytes: {:02X?}", n, &buf[offset..offset + n]);
if n == 0 { if n == 0 {
return Err(SdkError::Timeout); return Err(SdkError::Timeout);
@@ -94,19 +104,25 @@ impl EskinDeviceInner {
let timeout = Duration::milliseconds(self.config.read_timeout_ms as i64); let timeout = Duration::milliseconds(self.config.read_timeout_ms as i64);
let mut header = [0u8; 4]; let mut header = [0u8; 4];
Self::read_exact_from_transport(transport, &mut header, timeout)?; Self::read_exact_from_transport(transport, &mut header, timeout)?;
debug_println!("[device] recv header: {:02X?}", header);
let start = u16::from_be_bytes([header[0], header[1]]); // let start = u16::from_be_bytes([header[0], header[1]]);
let start = [header[0], header[1]];
if start != FRAME_START_RESPONSE { if start != FRAME_START_RESPONSE {
return Err(SdkError::FrameError(format!( return Err(SdkError::FrameError(format!(
"invalid response start: 0x{start:04X}" "invalid response start: 0x{start:02X?}"
))); )));
} }
debug_println!("h2: {:02X}, h3: {:02X}", header[2], header[3]);
let data_len = u16::from_le_bytes([header[2], header[3]]) as usize; let data_len = u16::from_le_bytes([header[2], header[3]]) as usize;
let total_len = 4 + data_len + FRAME_STATUS_LEN + FRAME_CRC_LEN; debug_println!("data_len: {data_len}");
let total_len = 5 + data_len;
// let total_len = data_len + 1;
let mut frame = vec![0u8; total_len]; let mut frame = vec![0u8; total_len];
frame[..4].copy_from_slice(&header); frame[..4].copy_from_slice(&header);
Self::read_exact_from_transport(transport, &mut frame[4..], timeout)?; Self::read_exact_from_transport(transport, &mut frame[4..], timeout)?;
debug_println!("[device] recv frame: {:02X?}", frame);
Ok(frame) Ok(frame)
} }

View File

@@ -1,3 +1,16 @@
/// Debug-only print macro. Only outputs when compiled with `--features debug`.
#[cfg(feature = "debug")]
#[macro_export]
macro_rules! debug_println {
($($arg:tt)*) => { println!($($arg)*) };
}
#[cfg(not(feature = "debug"))]
#[macro_export]
macro_rules! debug_println {
($($arg:tt)*) => {};
}
pub mod channel; pub mod channel;
pub mod config; pub mod config;
pub mod device; pub mod device;

View File

@@ -1,13 +1,60 @@
use eskin_finger_sdk::{config::DeviceConfig, device::{EskinDevice, EskinDeviceInner}, transport::SerialPortTransport}; use eskin_finger_sdk::{config::DeviceConfig, device::{EskinDevice, EskinDeviceInner}, transport::SerialPortTransport};
fn main() { fn main() {
let transport = SerialPortTransport::new("/dev/ttyUSB0", 921600); let transport = SerialPortTransport::new("COM10", 921600);
let config = DeviceConfig::default(); let config = DeviceConfig::default();
let mut device = EskinDeviceInner::new(config, Box::new(transport)); let mut device = EskinDeviceInner::new(config, Box::new(transport));
device.open().unwrap(); device.open().unwrap();
let data = device.read_register(0x1C00, 168).unwrap(); // let data = device.read_register(0x1C00, 168).unwrap();
println!("Serial: {:?}", data); // print_payload_data(&data);
read_hdv(&mut device);
read_check_group(&mut device);
read_row(&mut device);
write_col(&mut device, &[0x08]);
read_col(&mut device);
read_config(&mut device);
device.close().unwrap(); device.close().unwrap();
} }
fn read_hdv(device: &mut EskinDeviceInner) {
let series_id = device.read_register(0, 2).unwrap();
print_payload_data(&series_id);
}
fn read_check_group(device: &mut EskinDeviceInner) {
let group = device.read_register(0x000F, 1).unwrap();
print_payload_data(&group);
}
fn read_row(device: &mut EskinDeviceInner) {
let row = device.read_register(0x0015, 1).unwrap();
print_payload_data(&row);
}
fn write_col(device: &mut EskinDeviceInner, col: &[u8]) {
device.write_register(0x0014, col).unwrap();
}
fn read_col(device: &mut EskinDeviceInner) {
let col = device.read_register(0x0014, 1).unwrap();
print_payload_data(&col);
}
fn read_config(device: &mut EskinDeviceInner) {
let conf = device.read_register(0x0017, 1).unwrap();
print_payload_data(&conf);
}
fn print_payload_data(data: &[u8]) {
for (i, chunk) in data.chunks(2).enumerate() {
if chunk.len() == 2 {
let val = u16::from_le_bytes([chunk[0], chunk[1]]);
println!(" [{:3}] [{:02X}] [{:02X}] => {}", i, chunk[0], chunk[1], val);
} else {
println!(" [{:3}] [{:02X}] (odd byte)", i, chunk[0]);
}
}
}

View File

@@ -3,11 +3,11 @@ use serde::{Deserialize, Serialize};
use crate::error::SdkError; use crate::error::SdkError;
pub const FRAME_START_REQUEST: [u8; 2] = [0x55, 0xAA]; pub const FRAME_START_REQUEST: [u8; 2] = [0x55, 0xAA];
pub const FRAME_START_RESPONSE: u16 = 0xAA55; pub const FRAME_START_RESPONSE: [u8; 2] = [0xAA, 0x55];
pub const FUNC_READ: u8 = 0xFB; pub const FUNC_READ: u8 = 0xFB;
pub const FUNC_WRITE: u8 = 0x79; pub const FUNC_WRITE: u8 = 0x79;
pub const FUNC_RESPONSE_READ: u8 = 0xFF; pub const FUNC_RESPONSE_READ: u8 = 0xFB;
pub const FUNC_RESPONSE_WRITE: u8 = 0xF9; pub const FUNC_RESPONSE_WRITE: u8 = 0xF9;
pub const FRAME_HEADER_LEN: usize = 13; pub const FRAME_HEADER_LEN: usize = 13;
@@ -45,7 +45,7 @@ pub enum FrameFunction {
#[derive(Debug, Clone, Serialize, Deserialize)] #[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ProtocolFrame { pub struct ProtocolFrame {
pub start: u16, pub start: [u8; 2],
pub device_addr: u8, pub device_addr: u8,
pub function: u8, pub function: u8,
pub start_addr: u32, pub start_addr: u32,
@@ -154,7 +154,7 @@ impl ProtocolCodec for EskinProtocolCodec {
let crc = self.crc8(&frame); let crc = self.crc8(&frame);
frame.push(crc); frame.push(crc);
println!("send: {:02X?}", frame); debug_println!("send: {:02X?}", frame);
Ok(frame) Ok(frame)
} }
@@ -197,15 +197,17 @@ impl ProtocolCodec for EskinProtocolCodec {
return Err(SdkError::FrameError("read response too short".into())); return Err(SdkError::FrameError("read response too short".into()));
} }
let start = Self::read_u16_le(frame, 0)?; // let start = Self::read_u16_le(frame, 0)?;
let start = &frame[0..2];
if start != FRAME_START_RESPONSE { if start != FRAME_START_RESPONSE {
return Err(SdkError::FrameError(format!( return Err(SdkError::FrameError(format!(
"invalid response start: 0x{start:04X}" "invalid response start: 0x{start:02X?}"
))); )));
} }
debug_println!("get resp");
let data_len = Self::read_u16_le(frame, 2)? as usize; let data_len = Self::read_u16_le(frame, 2)? as usize;
let expected_len = 2 + 2 + data_len + FRAME_STATUS_LEN + FRAME_CRC_LEN; let expected_len = 2 + 2 + 1 + data_len;
if frame.len() != expected_len { if frame.len() != expected_len {
return Err(SdkError::FrameError(format!( return Err(SdkError::FrameError(format!(
@@ -234,13 +236,13 @@ impl ProtocolCodec for EskinProtocolCodec {
let start_addr = Self::read_u32_le(frame, 7)?; let start_addr = Self::read_u32_le(frame, 7)?;
let read_len = Self::read_u16_le(frame, 11)? as usize; let read_len = Self::read_u16_le(frame, 11)? as usize;
if data_len != 9 + read_len { if data_len != 10 + read_len {
return Err(SdkError::FrameError(format!( return Err(SdkError::FrameError(format!(
"read response data length mismatch: header data_len {data_len}, payload len {read_len}" "read response data length mismatch: header data_len {data_len}, payload len {read_len}"
))); )));
} }
let payload_start = 13; let payload_start = 14;
let payload_end = payload_start + read_len; let payload_end = payload_start + read_len;
let data = frame let data = frame
@@ -248,7 +250,7 @@ impl ProtocolCodec for EskinProtocolCodec {
.ok_or_else(|| SdkError::FrameError("read response payload missing".into()))? .ok_or_else(|| SdkError::FrameError("read response payload missing".into()))?
.to_vec(); .to_vec();
let status_offset = 4 + data_len; let status_offset = 13;
let status_raw = *frame let status_raw = *frame
.get(status_offset) .get(status_offset)
.ok_or_else(|| SdkError::FrameError("read response status missing".into()))?; .ok_or_else(|| SdkError::FrameError("read response status missing".into()))?;
@@ -271,15 +273,15 @@ impl ProtocolCodec for EskinProtocolCodec {
return Err(SdkError::FrameError("write response too short".into())); return Err(SdkError::FrameError("write response too short".into()));
} }
let start = Self::read_u16_le(frame, 0)?; let start = &frame[..2];
if start != FRAME_START_RESPONSE { if start != FRAME_START_RESPONSE {
return Err(SdkError::FrameError(format!( return Err(SdkError::FrameError(format!(
"invalid response start: 0x{start:04X}" "invalid response start: 0x{start:02X?}"
))); )));
} }
let data_len = Self::read_u16_le(frame, 2)? as usize; let data_len = Self::read_u16_le(frame, 2)? as usize;
let expected_len = 2 + 2 + data_len + FRAME_STATUS_LEN + FRAME_CRC_LEN; let expected_len = 2 + 2 + 1 + data_len;
if frame.len() != expected_len { if frame.len() != expected_len {
return Err(SdkError::FrameError(format!( return Err(SdkError::FrameError(format!(
@@ -307,13 +309,13 @@ impl ProtocolCodec for EskinProtocolCodec {
let start_addr = Self::read_u32_le(frame, 7)?; let start_addr = Self::read_u32_le(frame, 7)?;
let return_byte_count = Self::read_u16_le(frame, 11)?; let return_byte_count = Self::read_u16_le(frame, 11)?;
if data_len != 9 { if data_len != 10 {
return Err(SdkError::FrameError(format!( return Err(SdkError::FrameError(format!(
"write response data length mismatch: expected 9, got {data_len}" "write response data length mismatch: expected 9, got {data_len}"
))); )));
} }
let status_offset = 4 + data_len; let status_offset = 13;
let status_raw = *frame let status_raw = *frame
.get(status_offset) .get(status_offset)
.ok_or_else(|| SdkError::FrameError("write response status missing".into()))?; .ok_or_else(|| SdkError::FrameError("write response status missing".into()))?;
@@ -336,10 +338,10 @@ impl ProtocolCodec for EskinProtocolCodec {
return Err(SdkError::FrameError("stream frame too short".into())); return Err(SdkError::FrameError("stream frame too short".into()));
} }
let start = Self::read_u16_le(frame, 0)?; let start: [u8; 2] = frame[..2].try_into().unwrap();
if start != FRAME_START_RESPONSE { if start != FRAME_START_RESPONSE {
return Err(SdkError::FrameError(format!( return Err(SdkError::FrameError(format!(
"invalid stream frame start: 0x{start:04X}" "invalid stream frame start: 0x{start:02X?}"
))); )));
} }
@@ -373,7 +375,7 @@ impl ProtocolCodec for EskinProtocolCodec {
.ok_or_else(|| SdkError::FrameError("stream payload missing".into()))? .ok_or_else(|| SdkError::FrameError("stream payload missing".into()))?
.to_vec(); .to_vec();
let status_offset = 4 + data_len; let status_offset = 13;
let status_raw = *frame let status_raw = *frame
.get(status_offset) .get(status_offset)
.ok_or_else(|| SdkError::FrameError("stream status missing".into()))?; .ok_or_else(|| SdkError::FrameError("stream status missing".into()))?;

View File

@@ -238,9 +238,17 @@ impl PollingSampleCollector {
buf: &mut [u8], buf: &mut [u8],
timeout: Duration, timeout: Duration,
) -> Result<(), SdkError> { ) -> Result<(), SdkError> {
let deadline = std::time::Instant::now()
+ timeout
.to_std()
.map_err(|_| SdkError::InvalidParameter("timeout must be non-negative".into()))?;
let mut offset = 0; let mut offset = 0;
while offset < buf.len() { while offset < buf.len() {
let n = transport.read(&mut buf[offset..], timeout)?; let remaining = deadline
.checked_duration_since(std::time::Instant::now())
.unwrap_or(std::time::Duration::from_millis(1));
let n = transport.read(&mut buf[offset..], Duration::from_std(remaining).unwrap())?;
if n == 0 { if n == 0 {
return Err(SdkError::Timeout); return Err(SdkError::Timeout);
@@ -259,11 +267,12 @@ impl PollingSampleCollector {
let mut header = [0u8; 4]; let mut header = [0u8; 4];
Self::read_exact_from_transport(transport, &mut header, timeout)?; Self::read_exact_from_transport(transport, &mut header, timeout)?;
debug_println!("[stream] recv header: {:02X?}", header);
let start = u16::from_be_bytes([header[0], header[1]]); let start = [header[0], header[1]];
if start != FRAME_START_RESPONSE { if start != FRAME_START_RESPONSE {
return Err(SdkError::FrameError(format!( return Err(SdkError::FrameError(format!(
"invalid response start: 0x{start:04X}" "invalid response start: 0x{start:02X?}"
))); )));
} }
@@ -274,6 +283,7 @@ impl PollingSampleCollector {
frame[..4].copy_from_slice(&header); frame[..4].copy_from_slice(&header);
Self::read_exact_from_transport(transport, &mut frame[4..], timeout)?; Self::read_exact_from_transport(transport, &mut frame[4..], timeout)?;
debug_println!("[stream] recv frame: {:02X?}", frame);
Ok(frame) Ok(frame)
} }