exchange tast to tactilea

This commit is contained in:
lennlouisgeek
2026-04-03 00:47:36 +08:00
parent a686d19e61
commit 7688986ad7
15 changed files with 1842 additions and 147 deletions

View File

@@ -1,16 +1,18 @@
use crate::serial_core::error::CodecError;
use crate::serial_core::frame::{
FrameHandler, TactileAFrameMetaData, TactileARepFrame, TactileAReqFrame, TestFrame,
FrameHandler, TactileAFrameMetaData, TactileARepFrame, TactileAReqFrame,
};
use crate::serial_core::record::Recording;
use crate::serial_core::record::{self, CsvExporter};
use crate::serial_core::record::{write_csv, CsvExporter, CsvImporter, RecordedFrame, Recording};
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;
use csv::StringRecord;
use anyhow::anyhow;
use std::io::Read;
const FRAME_BUFFER_MIN_LENGTH: usize = 15;
pub struct TactileACodec {
@@ -234,7 +236,7 @@ impl CsvExporter<TactileARepFrame> for TactileACsvExporter {
fn csv_row(
&self,
item: &record::RecordedFrame<TactileARepFrame>,
item: &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();
@@ -243,5 +245,103 @@ impl CsvExporter<TactileARepFrame> for TactileACsvExporter {
}
}
#[cfg(test)]
mod tests {}
impl CsvExporter<TactileAFrame> for TactileACsvExporter {
type Error = CodecError;
fn csv_header(&self, _recording: &Recording<TactileAFrame>) -> 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: &RecordedFrame<TactileAFrame>,
) -> anyhow::Result<Vec<String>> {
let rep = match &item.frame {
TactileAFrame::Rep(rep) => rep,
TactileAFrame::Req(_) => return Err(anyhow!("request frame cannot be exported to csv row")),
};
let packet = TactileADataPacket::try_from(rep)?;
let mut row: Vec<String> = packet.data.iter().map(|x| x.to_string()).collect();
row.push(packet.dts_ms.to_string());
Ok(row)
}
}
impl TactileACsvImporter {
pub fn new(_path: &str) -> TactileACsvImporter {
Self {
channels: 0,
data_row: 0,
packets: Vec::new(),
}
}
fn parse_record(&mut self, record: StringRecord) -> anyhow::Result<TactileADataPacket> {
if self.channels == 0 {
return Err(anyhow!("csv header is missing channel columns"));
}
if record.len() < self.channels + 1 {
return Err(anyhow!("csv row has insufficient columns"));
}
let mut data = Vec::with_capacity(self.channels);
for index in 0..self.channels {
let cell = record.get(index).ok_or_else(|| anyhow!("missing channel cell"))?;
data.push(cell.parse::<i32>()?);
}
let dts_cell = record
.get(self.channels)
.ok_or_else(|| anyhow!("missing dts cell"))?;
let dts_ms = dts_cell.parse::<u64>()?;
Ok(TactileADataPacket {
data: data,
dts_ms: dts_ms,
})
}
}
impl CsvImporter<TactileADataPacket> for TactileACsvImporter {
fn load<R: Read>(&mut self, reader: R) -> anyhow::Result<Vec<TactileADataPacket>> {
let mut rdr = csv::Reader::from_reader(reader);
let headers = rdr.headers()?.clone();
self.channels = headers.len().saturating_sub(1);
self.data_row = 0;
self.packets.clear();
for record in rdr.records() {
let record = record?;
let packet = self.parse_record(record)?;
self.packets.push(packet);
self.data_row += 1;
}
Ok(self.packets.clone())
}
}
pub fn export_recording_csv<W>(recording: &Recording<TactileAFrame>, writer: W) -> anyhow::Result<()>
where
W: std::io::Write,
{
let channel_nb = recording
.frames
.iter()
.find_map(|frame| match &frame.frame {
TactileAFrame::Rep(rep) => Some(rep.payload.len() / 2),
TactileAFrame::Req(_) => None,
})
.unwrap_or(0);
let exporter = TactileACsvExporter::new(channel_nb);
write_csv(recording, &exporter, writer)
}

View File

@@ -4,7 +4,6 @@ use crate::serial_core::frame::{FrameHandler};
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};
@@ -233,9 +232,7 @@ pub fn export_recording_csv<W>(recording: &Recording<TestFrame>, writer: W) -> a
where
W: std::io::Write,
{
let now = Local::now();
let filename = format!("joyson_{}", now.format("%Y%m%d_%H%M%S"));
write_csv(recording, &TestCsvExporter, &filename)
write_csv(recording, &TestCsvExporter, writer)
}
#[cfg(test)]

View File

@@ -1,4 +1,7 @@
use crate::serial_core::{frame::TestFrame, record::Recording};
use crate::serial_core::{
frame::{TactileAFrame, TestFrame},
record::Recording,
};
pub mod codec;
pub mod codecs;
@@ -10,6 +13,7 @@ pub mod record;
pub mod utils;
pub type TestRecording = Recording<TestFrame>;
pub type TactileARecording = Recording<TactileAFrame>;
pub struct SerialConnection {
pub port: String,

View File

@@ -1,8 +1,3 @@
use std::fs::{write, File};
use std::io;
use anyhow::{Result, anyhow};
use csv::Reader;
#[derive(Clone)]
pub struct FrameTiming {
pub pts_ms: Option<u64>,
@@ -38,20 +33,17 @@ pub trait CsvImporter<P> {
fn load<R: std::io::Read>(&mut self, reader: R) -> anyhow::Result<Vec<P>>;
}
pub fn write_csv<F, E>(
pub fn write_csv<F, E, W>(
recording: &Recording<F>,
exporter: &E,
path: &str
// mut writer: W,
writer: W,
) -> anyhow::Result<()>
where
E: CsvExporter<F>,
// W: std::io::Write
W: std::io::Write,
{
let header = exporter.csv_header(&recording);
// let mut wrt = csv::Writer::from_writer(io::stdout());
let mut wrt = csv::Writer::from_path(format!("{}.csv", path))?;
let mut wrt = csv::Writer::from_writer(writer);
wrt.write_record(header)?;
for f in &recording.frames {
let row = exporter.csv_row(f)?;

View File

@@ -54,10 +54,24 @@ impl SerialFrame for TactileAFrame {
fn to_hud_packet(
&self,
_chart_state: &mut HudChartState,
_display_values: Option<&[i32]>,
chart_state: &mut HudChartState,
display_values: Option<&[i32]>,
) -> Option<HudPacket> {
None
match self {
TactileAFrame::Req(_) => None,
TactileAFrame::Rep(rep) => {
let proxy = TestFrame {
header: rep.meta.header,
cmd: rep.meta.func_code,
length: rep.meta.except_data_len,
payload: rep.payload.clone(),
checksum: rep.meta.checksum,
dts_ms: rep.dts_ms,
};
Some(chart_state.apply_frame(&proxy, display_values))
}
}
}
}