update ignore

This commit is contained in:
lenn
2026-04-07 18:06:46 +08:00
parent 770d713d03
commit 0d3266c95a
344 changed files with 1900 additions and 296 deletions

View File

@@ -1,5 +1,5 @@
use crate::serial_core::frame::TactileAFrame;
use crate::serial_core::record::{RecordedFrame, Recording};
use crate::serial_core::record::RecordedFrame;
use serde::Serialize;
use std::sync::{Arc, Mutex};
@@ -8,8 +8,7 @@ use std::sync::{Arc, Mutex};
pub enum CalibrationState {
Idle,
CollectingData,
ExportingData,
WaitingForWeight,
WaitingForNextRound,
Completed,
}
@@ -17,21 +16,23 @@ pub enum CalibrationState {
#[serde(rename_all = "camelCase")]
pub struct CalibrationSession {
pub state: CalibrationState,
pub target_frame: usize,
pub target_frames: usize,
pub collected_frames: usize,
pub current_round: usize,
pub max_rounds: usize,
pub round_interval_ms: u64,
pub data: Vec<RecordedFrame<TactileAFrame>>,
}
impl CalibrationSession {
pub fn new(targt_frame: usize, max_round: usize) -> Self {
pub fn new(target_frames: usize, max_rounds: usize, round_interval_ms: u64) -> Self {
Self {
state: CalibrationState::Idle,
target_frame: targt_frame,
target_frames,
collected_frames: 0,
current_round: 1,
max_rounds: max_round,
max_rounds,
round_interval_ms,
data: Vec::new(),
}
}
@@ -40,10 +41,6 @@ impl CalibrationSession {
self.state = CalibrationState::CollectingData;
self.collected_frames = 0;
self.data.clear();
println!(
"标定第 {} 轮开始,目标收集 {} 个有效帧",
self.current_round, self.target_frame
);
}
pub fn add_frame(&mut self, frame: RecordedFrame<TactileAFrame>) -> bool {
@@ -53,24 +50,24 @@ impl CalibrationSession {
self.data.push(frame);
self.collected_frames += 1;
if self.collected_frames >= self.target_frame {
self.state = CalibrationState::ExportingData;
return true;
}
return false;
self.collected_frames >= self.target_frames
}
pub fn export_completed(&mut self) {
self.state = CalibrationState::WaitingForWeight;
println!("请修改配重,继续标定");
if self.current_round >= self.max_rounds {
self.state = CalibrationState::Completed;
} else {
self.state = CalibrationState::WaitingForNextRound;
}
}
pub fn weight_added(&mut self) -> Result<(), String> {
self.begin_next_round()
}
pub fn begin_next_round(&mut self) -> Result<(), String> {
if self.current_round >= self.max_rounds {
self.state = CalibrationState::Completed;
println!("标定完成,共 {}", self.current_round);
} else {
self.current_round += 1;
self.start();
@@ -79,15 +76,30 @@ impl CalibrationSession {
Ok(())
}
pub fn stop(&mut self) {
self.state = CalibrationState::Idle;
self.collected_frames = 0;
self.data.clear();
}
pub fn is_active(&self) -> bool {
matches!(
self.state,
CalibrationState::CollectingData | CalibrationState::WaitingForNextRound
)
}
pub fn get_progress(&self) -> CalibrationProgress {
CalibrationProgress {
state: self.state.clone(),
current_round: self.current_round,
max_rounds: self.max_rounds,
collected_frames: self.collected_frames,
target_frames: self.target_frame,
progress_percentage: if self.target_frame > 0 {
(self.collected_frames as f32 / self.target_frame as f32) * 100.0
target_frames: self.target_frames,
round_interval_ms: self.round_interval_ms,
is_active: self.is_active(),
progress_percentage: if self.target_frames > 0 {
(self.collected_frames as f32 / self.target_frames as f32) * 100.0
} else {
0.0
},
@@ -103,7 +115,9 @@ pub struct CalibrationProgress {
pub max_rounds: usize,
pub collected_frames: usize,
pub target_frames: usize,
pub round_interval_ms: u64,
pub is_active: bool,
pub progress_percentage: f32,
}
pub type SharedCalibrationSession = Arc<Mutex<Option<CalibrationSession>>>;
pub type SharedCalibrationSession = Arc<Mutex<CalibrationSession>>;

View File

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

View File

@@ -13,14 +13,12 @@ use async_trait::async_trait;
use csv::StringRecord;
use log::debug;
use std::io::Read;
use std::os::raw;
const FRAME_BUFFER_MIN_LENGTH: usize = 15;
const IGNOR_RAW_DATA_VAL: i32 = 10;
pub struct TactileACodec {
buffer: Vec<u8>,
frame_nb: u64,
expected_data_len: usize,
}
@@ -68,7 +66,6 @@ impl TactileACodec {
pub fn new(cols: usize, rows: usize) -> TactileACodec {
Self {
buffer: Vec::new(),
frame_nb: 0,
expected_data_len: cols * rows * 2,
}
}
@@ -82,7 +79,6 @@ impl TactileACodec {
.chunks_exact(2)
.map(|chunk| {
let mut raw_val = u16::from_le_bytes([chunk[0], chunk[1]]) as i32;
println!("raw_val: {}", raw_val);
if raw_val < IGNOR_RAW_DATA_VAL {
raw_val = 0;
}
@@ -245,7 +241,7 @@ impl FrameHandler<TactileAFrame, i32> for TactileAHandler {
match frame {
TactileAFrame::Rep(rep) => {
let vals = TactileACodec::parse_data_frame(&rep.payload)?;
debug!("vals is {:?}", vals);
Ok(Some(vals))
}
_ => Ok(None),
@@ -253,6 +249,15 @@ impl FrameHandler<TactileAFrame, i32> for TactileAHandler {
}
}
fn trans_data_2_n(val: i32) -> i32 {
if val <= 74602 {
val / 466
} else if val > 74602 && val <= 105503 {
}
}
impl TactileACsvExporter {
pub fn new(channels: usize) -> Self {
TactileACsvExporter {
@@ -271,7 +276,7 @@ impl TactileACsvExporter {
impl CsvExporter<TactileARepFrame> for TactileACsvExporter {
type Error = CodecError;
fn csv_header(&self, recording: &Recording<TactileARepFrame>) -> Vec<String> {
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));

View File

@@ -244,9 +244,7 @@ where
#[cfg(test)]
mod tests {
use super::*;
use csv::Reader;
use std::io::Cursor;
#[test]
fn test_read_csv_basic() -> anyhow::Result<()> {

View File

@@ -37,7 +37,7 @@ impl<F> Recording<F> {
pub fn push(&mut self, ite: RecordedFrame<F>) {
self.frames.push(ite);
}
pub fn check_frame_need_record(ite: RecordedFrame<F>) {}
pub fn check_frame_need_record(_ite: RecordedFrame<F>) {}
}
pub trait CsvExporter<F> {

View File

@@ -1,4 +1,4 @@
use crate::serial_core::calibration_session::*;
use crate::serial_core::calibration_session::*;
use crate::serial_core::codec::Codec;
use crate::serial_core::codecs::tactile_a::TactileACodec;
use crate::serial_core::frame::{FrameHandler, TactileAFrame, TestFrame};
@@ -164,9 +164,9 @@ impl PollRequester<TactileAFrame> for TactileAPollRequester {
pub async fn run_serial<C, H, T, F>(
app: AppHandle,
mut port: SerialStream,
mut codec: C,
mut handler: H,
port: SerialStream,
codec: C,
handler: H,
session_started_at: Instant,
recording: Arc<Mutex<Recording<F>>>,
cancel: CancellationToken,
@@ -294,111 +294,218 @@ where
Ok(())
}
// src-tauri/src/serial_core/serial.rs 中添加
// 鍦?src-tauri/src/serial_core/serial.rs 涓坊鍔?
pub async fn run_serial_with_calibration(
app: AppHandle,
mut port: SerialStream,
session_started_at: Instant,
cancel: CancellationToken,
mut calibration_session: CalibrationSession,
calibration_session: SharedCalibrationSession,
) -> Result<()> {
let mut codec = TactileACodec::new(DEFAULT_TACTILE_COLS, DEFAULT_TACTILE_ROWS);
let mut handler = TactileAHandler;
let mut requester = TactileAPollRequester::new(
Duration::from_millis(DEFAULT_TACTILE_POLL_INTERVAL_MS),
DEFAULT_TACTILE_COLS,
DEFAULT_TACTILE_ROWS,
Duration::from_millis(DEFAULT_TACTILE_REPLY_TIMEOUT_MS),
);
info!("run_serial_with_calibration begin");
emit_calibration_status(&app, &calibration_session)?;
let mut poll_interval = time::interval(Duration::from_millis(DEFAULT_TACTILE_POLL_INTERVAL_MS));
poll_interval.set_missed_tick_behavior(MissedTickBehavior::Skip);
let run_result = async {
let mut codec = TactileACodec::new(DEFAULT_TACTILE_COLS, DEFAULT_TACTILE_ROWS);
let mut handler = TactileAHandler;
let mut requester = TactileAPollRequester::new(
Duration::from_millis(DEFAULT_TACTILE_POLL_INTERVAL_MS),
DEFAULT_TACTILE_COLS,
DEFAULT_TACTILE_ROWS,
Duration::from_millis(DEFAULT_TACTILE_REPLY_TIMEOUT_MS),
);
let mut buffer = [0u8; 1024];
let recording = Arc::new(Mutex::new(Recording::new()));
let mut chart_state = HudChartState::new();
let mut prune_interval = time::interval(Duration::from_millis(450));
prune_interval.set_missed_tick_behavior(MissedTickBehavior::Delay);
let mut poll_interval =
time::interval(Duration::from_millis(DEFAULT_TACTILE_POLL_INTERVAL_MS));
poll_interval.set_missed_tick_behavior(MissedTickBehavior::Skip);
loop {
tokio::select! {
_ = cancel.cancelled() => break,
_ = poll_interval.tick() => {
if requester.should_request() {
if let Some(req) = requester.next_request()? {
let bytes = codec.encode(&req)?;
port.write_all(&bytes).await?;
let mut buffer = [0u8; 1024];
let recording = Arc::new(Mutex::new(Recording::new()));
let mut chart_state = HudChartState::new();
let mut prune_interval = time::interval(Duration::from_millis(450));
prune_interval.set_missed_tick_behavior(MissedTickBehavior::Delay);
let mut next_round_at: Option<Instant> = None;
loop {
tokio::select! {
_ = cancel.cancelled() => break,
_ = poll_interval.tick() => {
if let Some(deadline) = next_round_at {
if Instant::now() >= deadline {
next_round_at = None;
begin_next_calibration_round(&app, &calibration_session)?;
}
}
}
}
_ = prune_interval.tick() => {
if let Some(packet) = chart_state.prune_stale() {
app.emit("hud_stream", packet)?;
}
}
read_result = port.read(&mut buffer) => {
let n = read_result?;
if n == 0 {
tokio::task::yield_now().await;
continue;
}
let frames = codec.decode(&buffer[..n], session_started_at)?;
for frame in frames {
requester.on_rx_frame(&frame);
let decode_res = handler
.on_frame(&frame)
.await?
.map(|vals| vals.into_iter().map(Into::into).collect::<Vec<i32>>());
let recorded_frame = RecordedFrame {
timing: FrameTiming { pts_ms: None, dts_ms: frame.dts_ms() },
frame: frame.clone(),
};
if calibration_is_collecting(&calibration_session)?
&& requester.should_request()
{
let mut record = recording
.lock()
.map_err(|_| anyhow::anyhow!("recording state poisoned"))?;
record.push(recorded_frame.clone());
if let Some(req) = requester.next_request()? {
let bytes = codec.encode(&req)?;
port.write_all(&bytes).await?;
}
}
let display_values = if let Some(vals) = decode_res.as_ref() {
let summary = vals.iter().copied().sum::<i32>();
chart_state.record_summary(summary as f32);
chart_state.record_pressure_matrix(vals.as_slice());
Some(vec![summary])
} else {
None
};
if let Some(packet) = frame.to_hud_packet(&mut chart_state, display_values.as_deref()) {
}
_ = prune_interval.tick() => {
if let Some(packet) = chart_state.prune_stale() {
app.emit("hud_stream", packet)?;
}
}
read_result = port.read(&mut buffer) => {
let n = read_result?;
if n == 0 {
tokio::task::yield_now().await;
continue;
}
// 检查是否达到目标帧数
let should_export = calibration_session.add_frame(recorded_frame);
let frames = codec.decode(&buffer[..n], session_started_at)?;
for frame in frames {
requester.on_rx_frame(&frame);
if should_export {
// 导出数据
export_calibration_data(&app, &calibration_session, &recording).await?;
let decode_res = handler
.on_frame(&frame)
.await?
.map(|vals| vals.into_iter().map(Into::into).collect::<Vec<i32>>());
// 发送语音提示(这里用事件代替,前端可以播放语音)
app.emit("calibration_voice_prompt", "请添加配重")?;
let display_values = if let Some(vals) = decode_res.as_ref() {
let summary = vals.iter().copied().sum::<i32>();
let val_summary = summary - vals[vals.len() - 1];
if val_summary < 8400 {
continue;
}
chart_state.record_summary(summary as f32);
chart_state.record_pressure_matrix(vals.as_slice());
Some(vec![summary])
} else {
None
};
// 更新状态
calibration_session.export_completed();
let recorded_frame = RecordedFrame {
timing: FrameTiming { pts_ms: None, dts_ms: frame.dts_ms() },
frame: frame.clone(),
};
if let Ok(mut record) = recording.lock() {
record.frames.clear();
{
let mut record = recording
.lock()
.map_err(|_| anyhow::anyhow!("recording state poisoned"))?;
record.push(recorded_frame.clone());
}
if let Some(packet) = frame.to_hud_packet(&mut chart_state, display_values.as_deref()) {
app.emit("hud_stream", packet)?;
}
let should_export = {
let mut session = calibration_session
.lock()
.map_err(|_| anyhow::anyhow!("calibration session poisoned"))?;
session.add_frame(recorded_frame)
};
if should_export {
let current_round = {
let session = calibration_session
.lock()
.map_err(|_| anyhow::anyhow!("calibration session poisoned"))?;
session.current_round
};
export_calibration_data(&app, current_round, &recording).await?;
let (progress, round_interval_ms) = {
let mut session = calibration_session
.lock()
.map_err(|_| anyhow::anyhow!("calibration session poisoned"))?;
session.export_completed();
(session.get_progress(), session.round_interval_ms)
};
app.emit("calibration_status", progress.clone())?;
if let Ok(mut record) = recording.lock() {
record.frames.clear();
}
if progress.state == CalibrationState::Completed {
return Ok(());
}
if round_interval_ms == 0 {
begin_next_calibration_round(&app, &calibration_session)?;
} else {
next_round_at =
Some(Instant::now() + Duration::from_millis(round_interval_ms));
}
}
}
}
}
}
Ok(())
}
.await;
if cancel.is_cancelled() {
stop_calibration_session(&app, &calibration_session)?;
}
run_result
}
fn calibration_is_collecting(calibration_session: &SharedCalibrationSession) -> Result<bool> {
let session = calibration_session
.lock()
.map_err(|_| anyhow::anyhow!("calibration session poisoned"))?;
Ok(session.state == CalibrationState::CollectingData)
}
fn begin_next_calibration_round(
app: &AppHandle,
calibration_session: &SharedCalibrationSession,
) -> Result<()> {
{
let mut session = calibration_session
.lock()
.map_err(|_| anyhow::anyhow!("calibration session poisoned"))?;
if session.state == CalibrationState::WaitingForNextRound {
session
.begin_next_round()
.map_err(|error| anyhow::anyhow!(error))?;
}
}
emit_calibration_status(app, calibration_session)
}
fn stop_calibration_session(
app: &AppHandle,
calibration_session: &SharedCalibrationSession,
) -> Result<()> {
{
let mut session = calibration_session
.lock()
.map_err(|_| anyhow::anyhow!("calibration session poisoned"))?;
if session.state != CalibrationState::Completed {
session.stop();
}
}
emit_calibration_status(app, calibration_session)
}
fn emit_calibration_status(
app: &AppHandle,
calibration_session: &SharedCalibrationSession,
) -> Result<()> {
let progress = {
let session = calibration_session
.lock()
.map_err(|_| anyhow::anyhow!("calibration session poisoned"))?;
session.get_progress()
};
app.emit("calibration_status", progress)?;
Ok(())
}
use crate::serial_core::codecs::tactile_a::TactileACsvExporter;
@@ -407,7 +514,7 @@ use std::time::{SystemTime, UNIX_EPOCH};
use tauri::Manager;
async fn export_calibration_data(
app: &AppHandle,
calibration_session: &CalibrationSession,
current_round: usize,
recording: &Arc<Mutex<Recording<TactileAFrame>>>,
) -> Result<()> {
let timestamp = SystemTime::now()
@@ -415,12 +522,8 @@ async fn export_calibration_data(
.map(|duration| duration.as_millis())
.unwrap_or_default();
let filename = format!(
"calibration_round{}_{}.csv",
calibration_session.current_round, timestamp
);
let filename = format!("calibration_round{}_{}.csv", current_round, timestamp);
// 创建导出目录
let mut output_dir = match app.path().desktop_dir() {
Ok(path) => path,
Err(_) => std::env::current_dir()?,
@@ -431,7 +534,6 @@ async fn export_calibration_data(
let output_path = output_dir.join(&filename);
let file = File::create(&output_path)?;
// 使用现有的导出逻辑
let recording_lock = recording
.lock()
.map_err(|_| anyhow::anyhow!("Recording poisoned"))?;
@@ -442,6 +544,5 @@ async fn export_calibration_data(
write_csv(&recording_lock, &exporter, file)?;
info!("标定数据已导出到: {}", output_path.display());
Ok(())
}

View File

@@ -1,4 +1,3 @@
use std::time::Instant;
pub fn usize_to_u16_be_bytes(n: usize) -> [u8; 2] {
@@ -41,7 +40,9 @@ mod test {
#[test]
fn test_crc8_itu() -> anyhow::Result<()> {
let req_vec = vec![0x55, 0xAA, 0x09, 0x00, 0x34, 0x00, 0xFB, 0x00, 0x1C, 0x00, 0x00, 0x18, 0x00];
let req_vec = vec![
0x55, 0xAA, 0x09, 0x00, 0x34, 0x00, 0xFB, 0x00, 0x1C, 0x00, 0x00, 0x18, 0x00,
];
let checksum = calc_crc8_itu(req_vec.as_slice());
assert_eq!(checksum, 0x7A);
@@ -50,10 +51,12 @@ mod test {
#[test]
fn test_crc8_smbus() -> anyhow::Result<()> {
let req_vec = vec![0x55, 0xAA, 0x09, 0x00, 0x34, 0x00, 0xFB, 0x00, 0x1C, 0x00, 0x00, 0x18, 0x00];
let req_vec = vec![
0x55, 0xAA, 0x09, 0x00, 0x34, 0x00, 0xFB, 0x00, 0x1C, 0x00, 0x00, 0x18, 0x00,
];
let checksum = calc_crc8_smbus(req_vec.as_slice());
assert_eq!(checksum, 0x2F);
Ok(())
}
}
}