From 3dbe4b119612b280e0e4069bf4d4f94465916c97 Mon Sep 17 00:00:00 2001 From: lutgaru Date: Thu, 24 Jul 2025 15:19:20 -0700 Subject: [PATCH 1/2] write raw values to log file --- src/app.rs | 2 +- src/gui/file_log_panel.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/app.rs b/src/app.rs index 29546cd..2e9cf59 100644 --- a/src/app.rs +++ b/src/app.rs @@ -127,6 +127,7 @@ impl TemplateApp { String::from_utf8_lossy(&data).to_string() }; self.write_log(&message); + self.file_log_panel.write_to_file(&data); ctx.request_repaint(); } CommunicationEvent::ConnectionClosed => { @@ -143,7 +144,6 @@ impl TemplateApp { fn write_log(&mut self, message: &str) { self.rx_panel .append_log(message, self.settings.max_log_string_length); - self.file_log_panel.write_to_file(message); } } diff --git a/src/gui/file_log_panel.rs b/src/gui/file_log_panel.rs index 1188fa1..5884dd9 100644 --- a/src/gui/file_log_panel.rs +++ b/src/gui/file_log_panel.rs @@ -80,9 +80,9 @@ impl FileLogPanel { } } - pub fn write_to_file(&mut self, message: &str) { + pub fn write_to_file(&mut self, message: &[u8]) { if let Some(ref mut file) = self.log_file { - if let Err(e) = file.write_all(message.as_bytes()) { + if let Err(e) = file.write_all(message) { eprintln!("Error writing to log file: {e:?}"); } } From b6dab09450dc86fbfee7cd0d4b0c94fd2680b4c8 Mon Sep 17 00:00:00 2001 From: lutgaru Date: Thu, 24 Jul 2025 15:22:08 -0700 Subject: [PATCH 2/2] fix: resolve UTF-8 character boundary panic - Replace String::from_utf8_lossy().to_string() with into_owned() for better performance - Refactor RxPanel to use VecDeque for efficient character-based log management - Eliminates UTF-8 character boundary issues when truncating logs - Implements circular buffer pattern for O(1) character insertion/removal - Updates serialization to work with VecDeque - Add graceful error handling in serial communication thread - Handle serial read errors without panicking - Detect GUI channel disconnection and stop thread cleanly - Prevent cascade failures when GUI thread crashes Fixes crashes caused by assertion failed: self.is_char_boundary(end) when processing serial data containing invalid UTF-8 sequences. --- src/app.rs | 2 +- src/gui/rx_panel.rs | 31 +++++++++---------------- src/serial_impl/serial_communication.rs | 19 +++++++++++---- 3 files changed, 27 insertions(+), 25 deletions(-) diff --git a/src/app.rs b/src/app.rs index 2e9cf59..87c3701 100644 --- a/src/app.rs +++ b/src/app.rs @@ -124,7 +124,7 @@ impl TemplateApp { .join(" "); format!("{hex_string} ") } else { - String::from_utf8_lossy(&data).to_string() + String::from_utf8_lossy(&data).into_owned() }; self.write_log(&message); self.file_log_panel.write_to_file(&data); diff --git a/src/gui/rx_panel.rs b/src/gui/rx_panel.rs index 87bd6c7..c3afa8f 100644 --- a/src/gui/rx_panel.rs +++ b/src/gui/rx_panel.rs @@ -1,14 +1,16 @@ use egui::Vec2; +use std::collections::VecDeque; +#[derive(Default)] pub struct RxPanel { - pub content: String, + pub content: VecDeque, should_scroll_to_bottom: bool, } impl RxPanel { pub fn new(_max_length: usize) -> Self { Self { - content: "Starting app\n".to_string(), + content: VecDeque::new(), should_scroll_to_bottom: false, } } @@ -23,7 +25,7 @@ impl RxPanel { scroll_area.show(ui, |ui| { ui.add_sized( text_size, - egui::TextEdit::multiline(&mut self.content) + egui::TextEdit::multiline(&mut self.content.iter().collect::()) .font(egui::TextStyle::Monospace) .code_editor() .desired_rows(10) @@ -41,14 +43,12 @@ impl RxPanel { } pub fn append_log(&mut self, message: &str, max_length: usize) { - eprintln!("{message}"); - self.content += message; - - if self.content.len() > max_length { - let excess_len = self.content.len() - max_length; - self.content.drain(0..excess_len); + for ch in message.chars() { + if self.content.len() == max_length { + self.content.pop_front(); // Elimina el carácter más viejo + } + self.content.push_back(ch); // Agrega el nuevo carácter } - // Mark that we need to scroll to bottom self.should_scroll_to_bottom = true; } @@ -72,19 +72,10 @@ impl<'de> serde::Deserialize<'de> for RxPanel { where D: serde::Deserializer<'de>, { - let content = String::deserialize(deserializer)?; + let content = VecDeque::deserialize(deserializer)?; Ok(Self { content, should_scroll_to_bottom: false, }) } } - -impl Default for RxPanel { - fn default() -> Self { - Self { - content: "Starting app\n".to_string(), - should_scroll_to_bottom: false, - } - } -} diff --git a/src/serial_impl/serial_communication.rs b/src/serial_impl/serial_communication.rs index c5f4ee8..b5a9042 100644 --- a/src/serial_impl/serial_communication.rs +++ b/src/serial_impl/serial_communication.rs @@ -71,10 +71,21 @@ impl CommunicationManager for SerialCommunication { let size = port_instance.bytes_to_read().unwrap_or(0); if size > 0 { let mut serial_buf: Vec = vec![0; size as usize]; - port_instance.read_exact(&mut serial_buf).unwrap(); - // self.write_log(message.unwrap_or(String::from("")).as_str()); - tx.send(CommunicationEvent::DataReceived(serial_buf)) - .expect("Failed to send data to GUI"); + match port_instance.read_exact(&mut serial_buf) { + Ok(_) => { + // Handle channel send errors gracefully + if tx + .send(CommunicationEvent::DataReceived(serial_buf)) + .is_err() + { + eprintln!("GUI channel disconnected, stopping serial thread"); + break; // Exit the loop if GUI is gone + } + } + Err(e) => { + eprintln!("Serial read error: {e}"); + } + } } }