feat:add slave

This commit is contained in:
lenn
2026-04-02 17:54:10 +08:00
parent 380394b93a
commit a686d19e61
25 changed files with 763 additions and 101 deletions

View File

@@ -1,9 +1,16 @@
use std::time::Instant;
use crate::serial_core::error::CodecError;
use crate::serial_core::frame::{FrameHandler, TactileAFrameMetaData, TactileARepFrame, TestFrame};
use crate::serial_core::utils::elapsed_millis;
use crate::serial_core::{codec::Codec, codecs::test::TestCodec, frame::{TactileAFrame, TactileAFrameStatusCode}};
use crate::serial_core::frame::{
FrameHandler, TactileAFrameMetaData, TactileARepFrame, TactileAReqFrame, TestFrame,
};
use crate::serial_core::record::Recording;
use crate::serial_core::record::{self, CsvExporter};
use crate::serial_core::utils::{calc_crc8_itu, elapsed_millis, usize_to_u16_le_bytes};
use crate::serial_core::{
codec::Codec,
frame::{TactileAFrame, TactileAFrameStatusCode},
};
use async_trait::async_trait;
use std::time::Instant;
const FRAME_BUFFER_MIN_LENGTH: usize = 15;
pub struct TactileACodec {
@@ -11,10 +18,14 @@ pub struct TactileACodec {
frame_nb: u64,
}
pub struct TactileACsvExporter;
pub struct TactileACsvExporter {
channels: usize,
}
pub struct TactileACsvImporter {
channels: usize,
data_row: usize,
packets: Vec<TactileADataPacket>,
}
pub struct TactileAHandler;
@@ -29,32 +40,74 @@ impl From<u8> for TactileAFrameStatusCode {
fn from(value: u8) -> Self {
match value {
0 => TactileAFrameStatusCode::Success,
_ => TactileAFrameStatusCode::Failure
_ => TactileAFrameStatusCode::Failure,
}
}
}
impl TryFrom<&TactileARepFrame> for TactileADataPacket {
type Error = CodecError;
fn try_from(value: &TactileARepFrame) -> Result<TactileADataPacket, Self::Error> {
let data = TactileACodec::parse_data_frame(&value.payload)?;
let dts_ms = value.dts_ms;
Ok(TactileADataPacket {
data: data,
dts_ms: dts_ms,
})
}
}
impl TactileACodec {
pub fn new() -> TactileACodec {
pub fn new(cols: usize, rows: usize) -> TactileACodec {
Self {
buffer: Vec::new(),
frame_nb: 0
frame_nb: 0,
}
}
pub fn parse_data_frame(data: &[u8]) -> Result<Vec<i32>> {
if data.len() % 2 == 0 {
pub fn parse_data_frame(data: &[u8]) -> Result<Vec<i32>, CodecError> {
if data.len() % 2 != 0 {
return Err(CodecError::InvalidLength);
}
let vals: Vec<i32> = data
.chunks_exact(2)
.map(|chunk| u16::from_be_bytes(bytes))
}
.chunks_exact(2)
.map(|chunk| u16::from_le_bytes([chunk[0], chunk[1]]) as i32)
.collect::<Vec<i32>>();
Ok(vals)
}
pub fn build_req_frame(cols: usize, rows: usize) -> anyhow::Result<TactileAFrame> {
let header = [0x55, 0xAA];
let payload_len: usize = 9;
let device_addr: u8 = 0x34;
let extend_code: u8 = 0x00;
let func_code: u8 = 0xFB;
let start_addr: u32 = 7168;
let except_data_len: usize = cols * rows * 2;
let checksum: u8 = 0;
Ok(TactileAFrame::Req(TactileAReqFrame {
meta: TactileAFrameMetaData {
header,
payload_len,
device_addr,
extend_code,
func_code,
start_addr,
except_data_len,
checksum,
},
}))
}
}
impl Codec<TactileAFrame> for TactileACodec {
fn decode(&mut self, input: &[u8], session_started_at: std::time::Instant) -> Result<Vec<TactileAFrame>, CodecError> {
fn decode(
&mut self,
input: &[u8],
session_started_at: std::time::Instant,
) -> Result<Vec<TactileAFrame>, CodecError> {
self.buffer.extend_from_slice(input);
let mut frames: Vec<TactileAFrame> = Vec::new();
@@ -63,9 +116,7 @@ impl Codec<TactileAFrame> for TactileACodec {
break;
}
let header_pos = self.buffer
.windows(2)
.position(|w| w == [0xAA, 0x55]);
let header_pos = self.buffer.windows(2).position(|w| w == [0xAA, 0x55]);
let Some(pos) = header_pos else {
self.buffer.clear();
@@ -84,10 +135,15 @@ impl Codec<TactileAFrame> for TactileACodec {
let device_addr = self.buffer[4];
let extend_code = self.buffer[5];
let func_code = self.buffer[6];
let start_addr = u32::from_le_bytes([self.buffer[7],self.buffer[8],self.buffer[9],self.buffer[10]]);
let except_data_len = u16::from_le_bytes([self.buffer[11], self.buffer[12]]) as usize;
let start_addr = u32::from_le_bytes([
self.buffer[7],
self.buffer[8],
self.buffer[9],
self.buffer[10],
]);
let except_data_len = u16::from_le_bytes([self.buffer[11], self.buffer[12]]) as usize;
let status = TactileAFrameStatusCode::from(self.buffer[13]);
let payload = self.buffer[14..14+except_data_len].to_vec();
let payload = self.buffer[14..14 + except_data_len].to_vec();
let crc8_itu_alg = crc::Crc::<u8>::new(&crc::CRC_8_I_432_1);
let checksum = crc8_itu_alg.checksum(&payload.as_slice());
let frame_length = except_data_len + FRAME_BUFFER_MIN_LENGTH;
@@ -111,7 +167,7 @@ impl Codec<TactileAFrame> for TactileACodec {
meta,
status,
payload,
dts_ms
dts_ms,
}
}));
@@ -119,11 +175,29 @@ impl Codec<TactileAFrame> for TactileACodec {
}
Ok(frames)
}
fn encode(&self, frame: &TactileAFrame) -> Result<Vec<u8>, crate::serial_core::error::CodecError> {
todo!()
fn encode(
&self,
frame: &TactileAFrame,
) -> Result<Vec<u8>, crate::serial_core::error::CodecError> {
match frame {
TactileAFrame::Req(f) => {
let mut req_bytes: Vec<u8> = Vec::new();
req_bytes.extend_from_slice(f.meta.header.as_slice());
req_bytes.extend_from_slice(usize_to_u16_le_bytes(f.meta.payload_len).as_slice());
req_bytes.push(f.meta.device_addr);
req_bytes.push(f.meta.extend_code);
req_bytes.push(f.meta.func_code);
req_bytes.extend_from_slice(f.meta.start_addr.to_le_bytes().as_slice());
let checksum = calc_crc8_itu(req_bytes.as_slice());
req_bytes.push(checksum);
Ok(req_bytes)
}
_ => {
Err(CodecError::InvalidFrameType)
}
}
}
}
@@ -131,12 +205,43 @@ impl Codec<TactileAFrame> for TactileACodec {
impl FrameHandler<TactileAFrame, i32> for TactileAHandler {
async fn on_frame(&mut self, frame: &TactileAFrame) -> anyhow::Result<Option<Vec<i32>>> {
match frame {
TactileARepFrame => {
}
_ => {
TactileAFrame::Rep(rep) => {
let vals = TactileACodec::parse_data_frame(&rep.payload)?;
Ok(Some(vals))
}
_ => Ok(None),
}
}
}
}
impl TactileACsvExporter {
fn new(channels: usize) -> Self {
TactileACsvExporter { channels }
}
}
impl CsvExporter<TactileARepFrame> for TactileACsvExporter {
type Error = CodecError;
fn csv_header(&self, recording: &Recording<TactileARepFrame>) -> Vec<String> {
let mut header: Vec<String> = Vec::new();
for i in 0..self.channels {
header.push(format!("channel{}", i + 1));
}
header.push("dts".to_string());
header
}
fn csv_row(
&self,
item: &record::RecordedFrame<TactileARepFrame>,
) -> anyhow::Result<Vec<String>> {
let packet = TactileADataPacket::try_from(&item.frame)?;
let mut row: Vec<String> = packet.data.iter().map(|x| x.to_string()).collect();
row.push(packet.dts_ms.to_string());
Ok(row)
}
}
#[cfg(test)]
mod tests {}

View File

@@ -1,7 +1,6 @@
use std::io::Read;
use std::time::Instant;
use crate::serial_core::frame::{FrameHandler};
use crate::serial_core::utils::*;
use crate::serial_core::{codec::Codec, error::CodecError, frame::TestFrame};
use anyhow::anyhow;
use async_trait::async_trait;
@@ -9,7 +8,10 @@ use chrono::Local;
use csv::StringRecord;
use crate::serial_core::record::{write_csv, CsvExporter, CsvImporter, RecordedFrame, Recording};
use crc::{Crc, CRC_8_SMBUS};
use crate::serial_core::utils::*;
use crate::serial_core::utils::{
elapsed_millis,
usize_to_u16_be_bytes
};
pub struct TestCodec {
buffer: Vec<u8>,
}