add tactile_a codec

This commit is contained in:
lenn
2026-04-01 18:35:22 +08:00
parent a754656ea4
commit 380394b93a
17 changed files with 564 additions and 31 deletions

16
src-tauri/Cargo.lock generated
View File

@@ -558,6 +558,21 @@ dependencies = [
"libc",
]
[[package]]
name = "crc"
version = "3.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5eb8a2a1cd12ab0d987a5d5e825195d372001a4094a0376319d5a0ad71c1ba0d"
dependencies = [
"crc-catalog",
]
[[package]]
name = "crc-catalog"
version = "2.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5"
[[package]]
name = "crc32fast"
version = "1.5.0"
@@ -3854,6 +3869,7 @@ dependencies = [
"anyhow",
"async-trait",
"chrono",
"crc",
"csv",
"fern",
"humantime",

View File

@@ -32,3 +32,4 @@ log = "0.4.29"
humantime = "2.3.0"
csv = "1.4.0"
chrono = "0.4.44"
crc = "3.4.0"

View File

@@ -1,4 +1,5 @@
use crate::serial_core::{frame::TestFrame, record::Recording};
pub mod test;
pub mod tactile_a;
pub type TestRecording = Recording<TestFrame>;

View File

@@ -0,0 +1,142 @@
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}};
const FRAME_BUFFER_MIN_LENGTH: usize = 15;
pub struct TactileACodec {
buffer: Vec<u8>,
frame_nb: u64,
}
pub struct TactileACsvExporter;
pub struct TactileACsvImporter {
}
pub struct TactileAHandler;
#[derive(Clone)]
pub struct TactileADataPacket {
pub data: Vec<i32>,
pub dts_ms: u64,
}
impl From<u8> for TactileAFrameStatusCode {
fn from(value: u8) -> Self {
match value {
0 => TactileAFrameStatusCode::Success,
_ => TactileAFrameStatusCode::Failure
}
}
}
impl TactileACodec {
pub fn new() -> TactileACodec {
Self {
buffer: Vec::new(),
frame_nb: 0
}
}
pub fn parse_data_frame(data: &[u8]) -> Result<Vec<i32>> {
if data.len() % 2 == 0 {
return Err(CodecError::InvalidLength);
}
let vals: Vec<i32> = data
.chunks_exact(2)
.map(|chunk| u16::from_be_bytes(bytes))
}
}
impl Codec<TactileAFrame> for TactileACodec {
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();
loop {
if self.buffer.len() < FRAME_BUFFER_MIN_LENGTH {
break;
}
let header_pos = self.buffer
.windows(2)
.position(|w| w == [0xAA, 0x55]);
let Some(pos) = header_pos else {
self.buffer.clear();
break;
};
if pos > 0 {
self.buffer.drain(0..pos);
}
if self.buffer.len() < FRAME_BUFFER_MIN_LENGTH {
break;
}
let header = [self.buffer[0], self.buffer[1]];
let payload_len = u16::from_le_bytes([self.buffer[2], self.buffer[3]]) as usize;
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 status = TactileAFrameStatusCode::from(self.buffer[13]);
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;
if self.buffer[frame_length - 1] != checksum {
self.buffer.drain(0..1);
continue;
}
let dts_ms = elapsed_millis(session_started_at);
let meta: TactileAFrameMetaData = TactileAFrameMetaData {
header,
payload_len,
device_addr,
extend_code,
func_code,
start_addr,
except_data_len,
checksum,
};
frames.push(TactileAFrame::Rep({
TactileARepFrame {
meta,
status,
payload,
dts_ms
}
}));
self.buffer.drain(0..frame_length);
}
Ok(frames)
}
fn encode(&self, frame: &TactileAFrame) -> Result<Vec<u8>, crate::serial_core::error::CodecError> {
todo!()
}
}
#[async_trait]
impl FrameHandler<TactileAFrame, i32> for TactileAHandler {
async fn on_frame(&mut self, frame: &TactileAFrame) -> anyhow::Result<Option<Vec<i32>>> {
match frame {
TactileARepFrame => {
}
_ => {
}
}
}
}

View File

@@ -1,13 +1,15 @@
use std::io::Read;
use std::time::Instant;
use crate::serial_core::frame::{crc8, usize_to_u16_be_bytes, FrameHandler};
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;
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::*;
pub struct TestCodec {
buffer: Vec<u8>,
}
@@ -52,7 +54,9 @@ impl Codec<TestFrame> for TestCodec {
break;
}
let payload = self.buffer[5..5 + length].to_vec();
let checksum = crc8(payload.as_slice());
// let checksum = crc8(payload.as_slice());
let crc8_alg = crc::Crc::<u8>::new(&crc::CRC_8_SMBUS);
let checksum = crc8_alg.checksum(payload.as_slice());
if self.buffer[frame_length - 1] != checksum {
self.buffer.drain(0..1);
continue;
@@ -112,10 +116,6 @@ fn parse_data_frame(data: &[u8]) -> Result<Vec<i32>, CodecError> {
Ok(vals)
}
fn elapsed_millis(start_at: Instant) -> u64 {
start_at.elapsed().as_millis() as u64
}
pub struct TestCsvExporter;
pub struct TestCsvImporter {
channels: usize,

View File

@@ -10,34 +10,48 @@ pub struct TestFrame {
pub dts_ms: u64
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct TactileAFrameMetaData {
pub header: [u8; 2],
pub payload_len: usize,
pub device_addr: u8,
pub extend_code: u8,
pub func_code: u8,
pub start_addr: u32,
pub except_data_len: usize,
// pub status: u8,
// pub payload_data: Vec<u8>,
pub checksum: u8,
// pub dts_ms: u64,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct TactileAReqFrame {
pub meta: TactileAFrameMetaData,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct TactileARepFrame {
pub meta: TactileAFrameMetaData,
pub status: TactileAFrameStatusCode,
pub payload: Vec<u8>,
pub dts_ms: u64
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum TactileAFrameStatusCode {
Success,
Failure
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum TactileAFrame {
Req(TactileAReqFrame),
Rep(TactileARepFrame)
}
#[async_trait]
pub trait FrameHandler<F, T>: Send {
async fn on_frame(&mut self, frame: &F) -> Result<Option<Vec<T>>>;
}
pub fn usize_to_u16_be_bytes(n: usize) -> [u8; 2] {
(n as u16).to_be_bytes()
}
pub fn usize_to_u16_le_bytes(n: usize) -> [u8; 2] {
(n as u16).to_be_bytes()
}
pub fn crc8(data: &[u8]) -> u8 {
let mut crc: u8 = 0x00;
for &byte in data {
crc ^= byte;
for _ in 0..8 {
if (crc & 0x80) != 0 {
crc = (crc << 1) ^ 0x07;
} else {
crc <<= 1;
}
}
}
crc
}

View File

@@ -7,6 +7,7 @@ pub mod frame;
pub mod model;
pub mod serial;
pub mod record;
pub mod utils;
pub type TestRecording = Recording<TestFrame>;

View File

@@ -0,0 +1,38 @@
use std::time::Instant;
pub fn usize_to_u16_be_bytes(n: usize) -> [u8; 2] {
(n as u16).to_be_bytes()
}
pub fn usize_to_u16_le_bytes(n: usize) -> [u8; 2] {
(n as u16).to_be_bytes()
}
pub fn u16_to_hex_be_bytes(n: u16) -> [u8; 2] {
(n as u16).to_be_bytes()
}
pub fn u16_to_hex_le_bytes(n: u16) -> [u8; 2] {
(n as u16).to_le_bytes()
}
pub fn crc8(data: &[u8]) -> u8 {
let mut crc: u8 = 0x00;
for &byte in data {
crc ^= byte;
for _ in 0..8 {
if (crc & 0x80) != 0 {
crc = (crc << 1) ^ 0x07;
} else {
crc <<= 1;
}
}
}
crc
}
pub fn elapsed_millis(start_at: Instant) -> u64 {
start_at.elapsed().as_millis() as u64
}