From 1ba677273875e36031d19f7300eb6bd56b8aa9ad Mon Sep 17 00:00:00 2001 From: jess Date: Fri, 3 Apr 2026 07:02:50 -0700 Subject: [PATCH] decode esp_timestamp and meas_id in desktop Rust parser --- cue/src/app.rs | 10 +++++----- cue/src/protocol.rs | 47 +++++++++++++++++++++++++++++++++++++-------- 2 files changed, 44 insertions(+), 13 deletions(-) diff --git a/cue/src/app.rs b/cue/src/app.rs index f1a9a0d..e71b6c3 100644 --- a/cue/src/app.rs +++ b/cue/src/app.rs @@ -646,7 +646,7 @@ impl App { self.status = s; } Message::DeviceData(msg) => match msg { - EisMessage::SweepStart { num_points, freq_start, freq_stop } => { + EisMessage::SweepStart { num_points, freq_start, freq_stop, .. } => { if self.collecting_refs { /* ref collection: clear temp buffer */ self.eis_points.clear(); @@ -691,7 +691,7 @@ impl App { self.electrode = cfg.electrode; self.status = "Config received".into(); } - EisMessage::LsvStart { num_points, v_start, v_stop } => { + EisMessage::LsvStart { num_points, v_start, v_stop, .. } => { self.lsv_points.clear(); self.lsv_total = num_points; self.lsv_data = text_editor::Content::with_text(&fmt_lsv(&self.lsv_points)); @@ -739,7 +739,7 @@ impl App { ); } } - EisMessage::AmpStart { v_hold } => { + EisMessage::AmpStart { v_hold, .. } => { self.amp_points.clear(); self.amp_running = true; self.amp_data = text_editor::Content::with_text(&fmt_amp(&self.amp_points)); @@ -758,7 +758,7 @@ impl App { } self.status = format!("Amp complete: {} points", self.amp_points.len()); } - EisMessage::ClStart { num_points } => { + EisMessage::ClStart { num_points, .. } => { self.cl_points.clear(); self.cl_result = None; self.cl_total = num_points; @@ -798,7 +798,7 @@ impl App { self.status = format!("Chlorine complete: {} points", self.cl_points.len()); } } - EisMessage::PhResult(r) => { + EisMessage::PhResult(r, _, _) => { if self.collecting_refs { self.ph_ref = Some(r); } else { diff --git a/cue/src/protocol.rs b/cue/src/protocol.rs index a0c2548..23649b2 100644 --- a/cue/src/protocol.rs +++ b/cue/src/protocol.rs @@ -244,21 +244,23 @@ pub struct EisConfig { #[derive(Debug, Clone)] pub enum EisMessage { - SweepStart { num_points: u16, freq_start: f32, freq_stop: f32 }, + SweepStart { num_points: u16, freq_start: f32, freq_stop: f32, + esp_timestamp: Option, meas_id: Option }, DataPoint { _index: u16, point: EisPoint }, SweepEnd, Config(EisConfig), - LsvStart { num_points: u16, v_start: f32, v_stop: f32 }, + LsvStart { num_points: u16, v_start: f32, v_stop: f32, + esp_timestamp: Option, meas_id: Option }, LsvPoint { _index: u16, point: LsvPoint }, LsvEnd, - AmpStart { v_hold: f32 }, + AmpStart { v_hold: f32, esp_timestamp: Option, meas_id: Option }, AmpPoint { _index: u16, point: AmpPoint }, AmpEnd, - ClStart { num_points: u16 }, + ClStart { num_points: u16, esp_timestamp: Option, meas_id: Option }, ClPoint { _index: u16, point: ClPoint }, ClResult(ClResult), ClEnd, - PhResult(PhResult), + PhResult(PhResult, Option, Option), Temperature(f32), RefFrame { mode: u8, rtia_idx: u8 }, RefLpRange { mode: u8, low_idx: u8, high_idx: u8 }, @@ -285,6 +287,16 @@ fn decode_float(data: &[u8]) -> f32 { f32::from_le_bytes([b0, b1, b2, b3]) } +fn decode_u32(data: &[u8]) -> u32 { + let b = [ + data[1] | ((data[0] & 1) << 7), + data[2] | ((data[0] & 2) << 6), + data[3] | ((data[0] & 4) << 5), + data[4] | ((data[0] & 8) << 4), + ]; + u32::from_le_bytes(b) +} + fn encode_float(val: f32) -> [u8; 5] { let p = val.to_le_bytes(); [ @@ -303,10 +315,14 @@ pub fn parse_sysex(data: &[u8]) -> Option { match data[1] { RSP_SWEEP_START if data.len() >= 15 => { let p = &data[2..]; + let (ts, mid) = if p.len() >= 21 { + (Some(decode_u32(&p[13..18])), Some(decode_u16(&p[18..21]))) + } else { (None, None) }; Some(EisMessage::SweepStart { num_points: decode_u16(&p[0..3]), freq_start: decode_float(&p[3..8]), freq_stop: decode_float(&p[8..13]), + esp_timestamp: ts, meas_id: mid, }) } RSP_DATA_POINT if data.len() >= 30 => { @@ -342,10 +358,14 @@ pub fn parse_sysex(data: &[u8]) -> Option { } RSP_LSV_START if data.len() >= 15 => { let p = &data[2..]; + let (ts, mid) = if p.len() >= 21 { + (Some(decode_u32(&p[13..18])), Some(decode_u16(&p[18..21]))) + } else { (None, None) }; Some(EisMessage::LsvStart { num_points: decode_u16(&p[0..3]), v_start: decode_float(&p[3..8]), v_stop: decode_float(&p[8..13]), + esp_timestamp: ts, meas_id: mid, }) } RSP_LSV_POINT if data.len() >= 15 => { @@ -361,7 +381,11 @@ pub fn parse_sysex(data: &[u8]) -> Option { RSP_LSV_END => Some(EisMessage::LsvEnd), RSP_AMP_START if data.len() >= 7 => { let p = &data[2..]; - Some(EisMessage::AmpStart { v_hold: decode_float(&p[0..5]) }) + let (ts, mid) = if p.len() >= 13 { + (Some(decode_u32(&p[5..10])), Some(decode_u16(&p[10..13]))) + } else { (None, None) }; + Some(EisMessage::AmpStart { v_hold: decode_float(&p[0..5]), + esp_timestamp: ts, meas_id: mid }) } RSP_AMP_POINT if data.len() >= 15 => { let p = &data[2..]; @@ -376,7 +400,11 @@ pub fn parse_sysex(data: &[u8]) -> Option { RSP_AMP_END => Some(EisMessage::AmpEnd), RSP_CL_START if data.len() >= 5 => { let p = &data[2..]; - Some(EisMessage::ClStart { num_points: decode_u16(&p[0..3]) }) + let (ts, mid) = if p.len() >= 11 { + (Some(decode_u32(&p[3..8])), Some(decode_u16(&p[8..11]))) + } else { (None, None) }; + Some(EisMessage::ClStart { num_points: decode_u16(&p[0..3]), + esp_timestamp: ts, meas_id: mid }) } RSP_CL_POINT if data.len() >= 16 => { let p = &data[2..]; @@ -403,11 +431,14 @@ pub fn parse_sysex(data: &[u8]) -> Option { } RSP_PH_RESULT if data.len() >= 17 => { let p = &data[2..]; + let (ts, mid) = if p.len() >= 23 { + (Some(decode_u32(&p[15..20])), Some(decode_u16(&p[20..23]))) + } else { (None, None) }; Some(EisMessage::PhResult(PhResult { v_ocp_mv: decode_float(&p[0..5]), ph: decode_float(&p[5..10]), temp_c: decode_float(&p[10..15]), - })) + }, ts, mid)) } RSP_REF_FRAME if data.len() >= 4 => { Some(EisMessage::RefFrame { mode: data[2], rtia_idx: data[3] })