diff --git a/CHANGELOG.md b/CHANGELOG.md index fedb096..c34fd49 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,8 +6,9 @@ All notable changes to the `Serial Monitor` crate will be documented in this fil ### Added: -* Up to 4 Sentences highlightments using regex -* Groups settings in the side bar by category into collapsing menu. +* removed the custom implementation of `Print` and `ScrollArea` and implemented the `log` crate and `egui_logger` +* Up to 4 Sentences highlightings using regex (thanks [@simon0356](https://github.com/simon0356)) +* Groups settings in the side bar by category into collapsing menu. (thanks [@simon0356](https://github.com/simon0356)) ## 0.3.0 - 14.10.2024 - Automatic Reconnection diff --git a/Cargo.lock b/Cargo.lock index dd53c3d..6866120 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -174,6 +174,12 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc7eb209b1518d6bb87b283c20095f5228ecda460da70b44f0802523dea6da04" +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + [[package]] name = "android_system_properties" version = "0.1.5" @@ -802,6 +808,20 @@ dependencies = [ "libc", ] +[[package]] +name = "chrono" +version = "0.4.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "js-sys", + "num-traits", + "wasm-bindgen", + "windows-targets 0.52.6", +] + [[package]] name = "clang-sys" version = "1.8.1" @@ -1295,6 +1315,19 @@ dependencies = [ "winit", ] +[[package]] +name = "egui_logger" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e0d940e9a0f56a2fda347cafdd42513136ab946ca57febbc5e3c6faf77f7824" +dependencies = [ + "chrono", + "egui", + "hashbrown 0.15.0", + "log", + "regex", +] + [[package]] name = "egui_plot" version = "0.29.0" @@ -1505,6 +1538,12 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "foldhash" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f81ec6369c545a7d40e4589b5597581fa1c441fe1cce96dd1de43159910a36a2" + [[package]] name = "foreign-types" version = "0.5.0" @@ -1841,6 +1880,11 @@ name = "hashbrown" version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e087f84d4f86bf4b218b927129862374b72199ae7d8657835f1e89000eea4fb" +dependencies = [ + "allocator-api2", + "equivalent", + "foldhash", +] [[package]] name = "hassle-rs" @@ -1890,6 +1934,29 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "iana-time-zone" +version = "0.1.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "235e081f3925a06703c2d0117ea8b91f042756fd6e7a6e5d901e8ca1a996b220" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "windows-core 0.52.0", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + [[package]] name = "ident_case" version = "1.0.1" @@ -3226,11 +3293,13 @@ dependencies = [ "egui-phosphor", "egui-theme-switch", "egui_extras", + "egui_logger", "egui_plot", "hex", "image", "itertools-num", "keepawake", + "log", "preferences", "rand", "realfft", diff --git a/Cargo.toml b/Cargo.toml index cf5c24e..efcd64c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,6 +15,7 @@ egui_extras = { version = "0.29.0", features = ["serde"] } egui_plot = "0.29.0" egui-phosphor = { git = "https://github.com/hacknus/egui-phosphor" } egui-theme-switch = { version = "0.2.0", default-features = true } +egui_logger = "0.6.1" hex = "0.4" image = { version = "0.25", default-features = false, features = ["png"] } itertools-num = "0.1" @@ -27,6 +28,7 @@ rfd = "0.15.0" safe-transmute = "0.11" serde = { version = "1.0", features = ["derive"] } serialport = { git = "https://github.com/serialport/serialport-rs", features = ["serde"] } +log = "0.4.22" [package.metadata.bundle] name = "Serial Monitor" diff --git a/src/color_picker.rs b/src/color_picker.rs index 83b9cbb..e4fce27 100644 --- a/src/color_picker.rs +++ b/src/color_picker.rs @@ -165,7 +165,7 @@ fn color_slider_1d(ui: &mut Ui, value: &mut f32, color_at: impl Fn(f32) -> Color mesh.colored_vertex(pos2(x, rect.top()), color); mesh.colored_vertex(pos2(x, rect.bottom()), color); if i < N { - mesh.add_triangle((2 * i + 0) as u32, (2 * i + 1) as u32, (2 * i + 2) as u32); + mesh.add_triangle((2 * i) as u32, (2 * i + 1) as u32, (2 * i + 2) as u32); mesh.add_triangle((2 * i + 1) as u32, (2 * i + 2) as u32, (2 * i + 3) as u32); } } diff --git a/src/custom_highlighter.rs b/src/custom_highlighter.rs index 581d5a7..f005af1 100644 --- a/src/custom_highlighter.rs +++ b/src/custom_highlighter.rs @@ -1,29 +1,23 @@ - - -use eframe::egui::{FontFamily, FontId}; use eframe::egui::{self, text::LayoutJob, Color32, TextFormat}; - +use eframe::egui::{FontFamily, FontId}; extern crate regex; use regex::Regex; use regex::RegexSet; const DEFAULT_FONT_ID: FontId = FontId::new(14.0, FontFamily::Monospace); - -#[derive(Debug)] -#[derive(Clone, Copy)] -pub struct HighLightElement -{ - pos_start:usize, - pos_end:usize, - token_idx:usize +#[derive(Debug, Clone, Copy)] +pub struct HighLightElement { + pos_start: usize, + pos_end: usize, + token_idx: usize, } -impl HighLightElement{ - pub fn new(pos_start: usize, pos_end:usize,token_idx:usize) -> Self{ - Self{ +impl HighLightElement { + pub fn new(pos_start: usize, pos_end: usize, token_idx: usize) -> Self { + Self { pos_start, pos_end, - token_idx + token_idx, } } } @@ -31,16 +25,13 @@ pub fn highlight_impl( _ctx: &egui::Context, text: &str, tokens: Vec, - default_color:Color32 + default_color: Color32, ) -> Option { // Extremely simple syntax highlighter for when we compile without syntect - let mut my_tokens = tokens.clone(); - for token in my_tokens.clone() - { - if token.len() < 1 - { + for token in my_tokens.clone() { + if token.is_empty() { let index = my_tokens.iter().position(|x| *x == token).unwrap(); my_tokens.remove(index); } @@ -48,66 +39,77 @@ pub fn highlight_impl( let content_string = String::from(text); // let _ = file.read_to_string(&mut isi); - let mut regexs:Vec = Vec::new(); + let mut regexs: Vec = Vec::new(); for sentence in my_tokens.clone() { - match Regex::new(&sentence){ + match Regex::new(&sentence) { Ok(re) => { regexs.push(re); - }, - Err(_err) =>{ - - }, + } + Err(_err) => {} }; } - let mut highlight_list : Vec = Vec::::new(); - match RegexSet::new(my_tokens.clone()){ + let mut highlight_list: Vec = Vec::::new(); + match RegexSet::new(my_tokens.clone()) { Ok(set) => { for idx in set.matches(&content_string).into_iter() { for caps in regexs[idx].captures_iter(&content_string) { highlight_list.push(HighLightElement::new( - caps.get(0).unwrap().start(), + caps.get(0).unwrap().start(), caps.get(0).unwrap().end(), - idx)); + idx, + )); } } - }, - Err(_err) => { - } + Err(_err) => {} }; highlight_list.sort_by_key(|item| (item.pos_start, item.pos_end)); - let mut job = LayoutJob::default(); - let mut previous = HighLightElement::new(0,0, 0); - for matches in highlight_list - { - if previous.pos_end >= matches.pos_start - { - continue + let mut previous = HighLightElement::new(0, 0, 0); + for matches in highlight_list { + if previous.pos_end >= matches.pos_start { + continue; } - job.append(&text[previous.pos_end..(matches.pos_start)], 0.0, TextFormat::simple(DEFAULT_FONT_ID, default_color)); - if matches.token_idx == 0 - { - job.append(&text[matches.pos_start..matches.pos_end], 0.0, TextFormat::simple(DEFAULT_FONT_ID, Color32::from_rgb(255, 100, 100))); - }else if matches.token_idx == 1 - { - job.append(&text[matches.pos_start..matches.pos_end], 0.0, TextFormat::simple(DEFAULT_FONT_ID, Color32::from_rgb(225, 159, 0))); - - }else if matches.token_idx == 2 - { - job.append(&text[matches.pos_start..matches.pos_end], 0.0, TextFormat::simple(DEFAULT_FONT_ID, Color32::from_rgb(87, 165, 171))); - }else if matches.token_idx == 3 - { - job.append(&text[matches.pos_start..matches.pos_end], 0.0, TextFormat::simple(DEFAULT_FONT_ID, Color32::from_rgb(109, 147, 226))); + job.append( + &text[previous.pos_end..(matches.pos_start)], + 0.0, + TextFormat::simple(DEFAULT_FONT_ID, default_color), + ); + if matches.token_idx == 0 { + job.append( + &text[matches.pos_start..matches.pos_end], + 0.0, + TextFormat::simple(DEFAULT_FONT_ID, Color32::from_rgb(255, 100, 100)), + ); + } else if matches.token_idx == 1 { + job.append( + &text[matches.pos_start..matches.pos_end], + 0.0, + TextFormat::simple(DEFAULT_FONT_ID, Color32::from_rgb(225, 159, 0)), + ); + } else if matches.token_idx == 2 { + job.append( + &text[matches.pos_start..matches.pos_end], + 0.0, + TextFormat::simple(DEFAULT_FONT_ID, Color32::from_rgb(87, 165, 171)), + ); + } else if matches.token_idx == 3 { + job.append( + &text[matches.pos_start..matches.pos_end], + 0.0, + TextFormat::simple(DEFAULT_FONT_ID, Color32::from_rgb(109, 147, 226)), + ); } - previous = matches.clone(); + previous = matches; } - job.append(&text[previous.pos_end..], 0.0, TextFormat::simple(DEFAULT_FONT_ID, default_color)); - + job.append( + &text[previous.pos_end..], + 0.0, + TextFormat::simple(DEFAULT_FONT_ID, default_color), + ); Some(job) } - diff --git a/src/gui.rs b/src/gui.rs index a34c29c..409760a 100644 --- a/src/gui.rs +++ b/src/gui.rs @@ -20,12 +20,12 @@ use serde::{Deserialize, Serialize}; use serialport::{DataBits, FlowControl, Parity, StopBits}; use crate::color_picker::{color_picker_widget, color_picker_window, COLORS}; +use crate::custom_highlighter::highlight_impl; use crate::data::{DataContainer, SerialDirection}; use crate::serial::{clear_serial_settings, save_serial_settings, Device, SerialDevices}; use crate::toggle::toggle; use crate::FileOptions; use crate::{APP_INFO, PREFS_KEY}; -use crate::custom_highlighter::highlight_impl; const DEFAULT_FONT_ID: FontId = FontId::new(14.0, FontFamily::Monospace); pub const RIGHT_PANEL_WIDTH: f32 = 350.0; @@ -45,16 +45,6 @@ const SAVE_PLOT_SHORTCUT: KeyboardShortcut = KeyboardShortcut::new( const CLEAR_PLOT_SHORTCUT: KeyboardShortcut = KeyboardShortcut::new(egui::Modifiers::COMMAND, egui::Key::X); -#[derive(Clone)] -#[allow(unused)] -pub enum Print { - Empty, - Message(String), - Error(String), - Debug(String), - Ok(String), -} - #[derive(PartialEq)] pub enum WindowFeedback { None, @@ -63,75 +53,6 @@ pub enum WindowFeedback { Cancel, } -impl Print { - pub fn scroll_area_message( - &self, - gui_conf: &GuiSettingsContainer, - ) -> Option { - match self { - Print::Empty => None, - Print::Message(s) => { - let color = if gui_conf.dark_mode { - Color32::WHITE - } else { - Color32::BLACK - }; - Some(ScrollAreaMessage { - label: "[MSG] ".to_owned(), - content: s.to_owned(), - color, - }) - } - Print::Error(s) => { - let color = Color32::RED; - Some(ScrollAreaMessage { - label: "[ERR] ".to_owned(), - content: s.to_owned(), - color, - }) - } - Print::Debug(s) => { - let color = if gui_conf.dark_mode { - Color32::YELLOW - } else { - Color32::LIGHT_RED - }; - Some(ScrollAreaMessage { - label: "[DBG] ".to_owned(), - content: s.to_owned(), - color, - }) - } - Print::Ok(s) => { - let color = Color32::GREEN; - Some(ScrollAreaMessage { - label: "[OK] ".to_owned(), - content: s.to_owned(), - color, - }) - } - } - } -} - -#[allow(dead_code)] -pub struct ScrollAreaMessage { - label: String, - content: String, - color: Color32, -} - -pub fn print_to_console(print_lock: &Arc>>, message: Print) { - match print_lock.write() { - Ok(mut write_guard) => { - write_guard.push(message); - } - Err(e) => { - println!("Error while writing to print_lock: {}", e); - } - } -} - #[derive(Serialize, Deserialize, PartialEq, Debug, Clone)] pub struct GuiSettingsContainer { pub device: String, @@ -184,12 +105,10 @@ pub struct MyApp { serial_devices: SerialDevices, plotting_range: usize, plot_serial_display_ratio: f32, - console: Vec, picked_path: PathBuf, plot_location: Option, data: DataContainer, gui_conf: GuiSettingsContainer, - print_lock: Arc>>, device_lock: Arc>, devices_lock: Arc>>, connected_lock: Arc>, @@ -215,7 +134,6 @@ pub struct MyApp { #[allow(clippy::too_many_arguments)] impl MyApp { pub fn new( - print_lock: Arc>>, data_lock: Arc>, device_lock: Arc>, devices_lock: Arc>>, @@ -232,15 +150,11 @@ impl MyApp { device: "".to_string(), old_device: "".to_string(), data: DataContainer::default(), - console: vec![Print::Message( - "waiting for serial connection..,".to_owned(), - )], connected_lock, device_lock, devices_lock, device_idx: 0, serial_devices: devices, - print_lock, gui_conf, data_lock, save_tx, @@ -249,8 +163,8 @@ impl MyApp { plotting_range: usize::MAX, plot_serial_display_ratio: 0.45, command: "".to_string(), - show_sent_cmds: false, - show_timestamps: false, + show_sent_cmds: true, + show_timestamps: true, save_raw: false, eol: "\\r\\n".to_string(), colors: vec![COLORS[0]], @@ -323,18 +237,18 @@ impl MyApp { // Height let top_spacing = 5.0; let panel_height = ui.available_size().y; - let mut plot_height:f32 = 0.0; + let mut plot_height: f32 = 0.0; if self.serial_devices.number_of_plots[self.device_idx] > 0 { - let height:f32 ; - height = ui.available_size().y * self.plot_serial_display_ratio; + let height = ui.available_size().y * self.plot_serial_display_ratio; plot_height = height; // need to subtract 12.0, this seems to be the height of the separator of two adjacent plots - plot_height = - plot_height / (self.serial_devices.number_of_plots[self.device_idx] as f32) - 12.0; + plot_height = plot_height + / (self.serial_devices.number_of_plots[self.device_idx] as f32) + - 12.0; } - let mut plot_ui_heigh:f32 = 0.0; + let mut plot_ui_heigh: f32 = 0.0; ui.add_space(top_spacing); ui.horizontal(|ui| { @@ -344,7 +258,6 @@ impl MyApp { self.data = read_guard.clone(); } if self.serial_devices.number_of_plots[self.device_idx] > 0 { - if self.data.dataset.len() != self.labels.len() { self.labels = (0..max(self.data.dataset.len(), 1)) .map(|i| format!("Column {i}")) @@ -372,11 +285,13 @@ impl MyApp { } } - let t_fmt = - |x: GridMark, _range: &RangeInclusive| format!("{:4.2} s", x.value); + let t_fmt = |x: GridMark, _range: &RangeInclusive| { + format!("{:4.2} s", x.value) + }; let plots_ui = ui.vertical(|ui| { - for graph_idx in 0..self.serial_devices.number_of_plots[self.device_idx] { + for graph_idx in 0..self.serial_devices.number_of_plots[self.device_idx] + { if graph_idx != 0 { ui.separator(); } @@ -429,10 +344,8 @@ impl MyApp { plot_ui_heigh = 0.0; } - let serial_height = panel_height - - plot_ui_heigh - - left_border * 2.0 - - top_spacing; + let serial_height = + panel_height - plot_ui_heigh - left_border * 2.0 - top_spacing; let num_rows = self.data.raw_traffic.len(); let row_height = ui.text_style_height(&egui::TextStyle::Body); @@ -466,10 +379,13 @@ impl MyApp { .collect(); let mut layouter = |ui: &egui::Ui, string: &str, wrap_width: f32| { - let mut layout_job = highlight_impl(ui.ctx(), - string, - self.serial_devices.highlight_labels[self.device_idx].clone(), - Color32::from_rgb(155, 164, 167)).unwrap(); + let mut layout_job = highlight_impl( + ui.ctx(), + string, + self.serial_devices.highlight_labels[self.device_idx].clone(), + Color32::from_rgb(155, 164, 167), + ) + .unwrap(); layout_job.wrap.max_width = wrap_width; ui.fonts(|f| f.layout_job(layout_job)) }; @@ -480,7 +396,7 @@ impl MyApp { .lock_focus(true) .text_color(color) .desired_width(width) - .layouter(&mut layouter) + .layouter(&mut layouter), ); }); ui.horizontal(|ui| { @@ -498,10 +414,7 @@ impl MyApp { self.index = self.history.len() - 1; let eol = self.eol.replace("\\r", "\r").replace("\\n", "\n"); if let Err(err) = self.send_tx.send(self.command.clone() + &eol) { - print_to_console( - &self.print_lock, - Print::Error(format!("send_tx thread send failed: {:?}", err)), - ); + log::error!("send_tx thread send failed: {:?}", err); } // stay in focus! cmd_line.request_focus(); @@ -526,8 +439,7 @@ impl MyApp { }); } - fn draw_serial_settings(&mut self, ctx: &egui::Context, ui :&mut Ui) - { + fn draw_serial_settings(&mut self, ctx: &egui::Context, ui: &mut Ui) { ui.horizontal(|ui| { ui.heading("Serial Monitor"); self.paint_connection_indicator(ui); @@ -575,7 +487,8 @@ impl MyApp { let dev_text = dev.replace("/dev/tty.", ""); ui.selectable_value(&mut self.device, dev, dev_text); }); - }).response; + }) + .response; // let selected_new_device = response.changed(); //somehow this does not work // if selected_new_device { if old_name != self.device { @@ -611,12 +524,18 @@ impl MyApp { self.serial_devices.devices.push(device); self.serial_devices.number_of_plots.push(1); self.serial_devices.number_of_highlights.push(1); - self.serial_devices.highlight_labels.push(vec!["".to_string()]); - self.serial_devices.labels.push(vec!["Column 0".to_string()]); + self.serial_devices + .highlight_labels + .push(vec!["".to_string()]); + self.serial_devices + .labels + .push(vec!["Column 0".to_string()]); self.device_idx = self.serial_devices.devices.len() - 1; save_serial_settings(&self.serial_devices); } - self.clear_tx.send(true).expect("failed to send clear after choosing new device"); + self.clear_tx + .send(true) + .expect("failed to send clear after choosing new device"); // need to clear the data here such that we don't get errors in the gui (plot) self.data = DataContainer::default(); self.show_warning_window = WindowFeedback::None; @@ -627,7 +546,10 @@ impl MyApp { } } egui::ComboBox::from_id_salt("Baud Rate") - .selected_text(format!("{}", self.serial_devices.devices[self.device_idx].baud_rate)) + .selected_text(format!( + "{}", + self.serial_devices.devices[self.device_idx].baud_rate + )) .width(80.0) .show_ui(ui, |ui| { if self.connected_to_device { @@ -641,7 +563,11 @@ impl MyApp { ); }); }); - let connect_text = if self.connected_to_device { "Disconnect" } else { "Connect" }; + let connect_text = if self.connected_to_device { + "Disconnect" + } else { + "Connect" + }; if ui.button(connect_text).clicked() { if let Ok(mut device) = self.device_lock.write() { if self.connected_to_device { @@ -668,120 +594,184 @@ impl MyApp { ui.disable(); } egui::ComboBox::from_id_salt("Data Bits") - .selected_text(self.serial_devices.devices[self.device_idx].data_bits.to_string()) + .selected_text( + self.serial_devices.devices[self.device_idx] + .data_bits + .to_string(), + ) .width(30.0) .show_ui(ui, |ui| { - ui.selectable_value(&mut self.serial_devices.devices[self.device_idx].data_bits, DataBits::Eight, DataBits::Eight.to_string()); - ui.selectable_value(&mut self.serial_devices.devices[self.device_idx].data_bits, DataBits::Seven, DataBits::Seven.to_string()); - ui.selectable_value(&mut self.serial_devices.devices[self.device_idx].data_bits, DataBits::Six, DataBits::Six.to_string()); - ui.selectable_value(&mut self.serial_devices.devices[self.device_idx].data_bits, DataBits::Five, DataBits::Five.to_string()); - + ui.selectable_value( + &mut self.serial_devices.devices[self.device_idx].data_bits, + DataBits::Eight, + DataBits::Eight.to_string(), + ); + ui.selectable_value( + &mut self.serial_devices.devices[self.device_idx].data_bits, + DataBits::Seven, + DataBits::Seven.to_string(), + ); + ui.selectable_value( + &mut self.serial_devices.devices[self.device_idx].data_bits, + DataBits::Six, + DataBits::Six.to_string(), + ); + ui.selectable_value( + &mut self.serial_devices.devices[self.device_idx].data_bits, + DataBits::Five, + DataBits::Five.to_string(), + ); }); egui::ComboBox::from_id_salt("Parity") - .selected_text(self.serial_devices.devices[self.device_idx].parity.to_string()) + .selected_text( + self.serial_devices.devices[self.device_idx] + .parity + .to_string(), + ) .width(30.0) .show_ui(ui, |ui| { - ui.selectable_value(&mut self.serial_devices.devices[self.device_idx].parity, Parity::None, Parity::None.to_string()); - ui.selectable_value(&mut self.serial_devices.devices[self.device_idx].parity, Parity::Odd, Parity::Odd.to_string()); - ui.selectable_value(&mut self.serial_devices.devices[self.device_idx].parity, Parity::Even, Parity::Even.to_string()); + ui.selectable_value( + &mut self.serial_devices.devices[self.device_idx].parity, + Parity::None, + Parity::None.to_string(), + ); + ui.selectable_value( + &mut self.serial_devices.devices[self.device_idx].parity, + Parity::Odd, + Parity::Odd.to_string(), + ); + ui.selectable_value( + &mut self.serial_devices.devices[self.device_idx].parity, + Parity::Even, + Parity::Even.to_string(), + ); }); egui::ComboBox::from_id_salt("Stop Bits") - .selected_text(self.serial_devices.devices[self.device_idx].stop_bits.to_string()) + .selected_text( + self.serial_devices.devices[self.device_idx] + .stop_bits + .to_string(), + ) .width(30.0) .show_ui(ui, |ui| { - ui.selectable_value(&mut self.serial_devices.devices[self.device_idx].stop_bits, StopBits::One, StopBits::One.to_string()); - ui.selectable_value(&mut self.serial_devices.devices[self.device_idx].stop_bits, StopBits::Two, StopBits::Two.to_string()); + ui.selectable_value( + &mut self.serial_devices.devices[self.device_idx].stop_bits, + StopBits::One, + StopBits::One.to_string(), + ); + ui.selectable_value( + &mut self.serial_devices.devices[self.device_idx].stop_bits, + StopBits::Two, + StopBits::Two.to_string(), + ); }); egui::ComboBox::from_id_salt("Flow Control") - .selected_text(self.serial_devices.devices[self.device_idx].flow_control.to_string()) + .selected_text( + self.serial_devices.devices[self.device_idx] + .flow_control + .to_string(), + ) .width(75.0) .show_ui(ui, |ui| { - ui.selectable_value(&mut self.serial_devices.devices[self.device_idx].flow_control, FlowControl::None, FlowControl::None.to_string()); - ui.selectable_value(&mut self.serial_devices.devices[self.device_idx].flow_control, FlowControl::Hardware, FlowControl::Hardware.to_string()); - ui.selectable_value(&mut self.serial_devices.devices[self.device_idx].flow_control, FlowControl::Software, FlowControl::Software.to_string()); + ui.selectable_value( + &mut self.serial_devices.devices[self.device_idx].flow_control, + FlowControl::None, + FlowControl::None.to_string(), + ); + ui.selectable_value( + &mut self.serial_devices.devices[self.device_idx].flow_control, + FlowControl::Hardware, + FlowControl::Hardware.to_string(), + ); + ui.selectable_value( + &mut self.serial_devices.devices[self.device_idx].flow_control, + FlowControl::Software, + FlowControl::Software.to_string(), + ); }); egui::ComboBox::from_id_salt("Timeout") - .selected_text(self.serial_devices.devices[self.device_idx].timeout.as_millis().to_string()) + .selected_text( + self.serial_devices.devices[self.device_idx] + .timeout + .as_millis() + .to_string(), + ) .width(55.0) .show_ui(ui, |ui| { - ui.selectable_value(&mut self.serial_devices.devices[self.device_idx].timeout, Duration::from_millis(0), "0"); - ui.selectable_value(&mut self.serial_devices.devices[self.device_idx].timeout, Duration::from_millis(10), "10"); - ui.selectable_value(&mut self.serial_devices.devices[self.device_idx].timeout, Duration::from_millis(100), "100"); - ui.selectable_value(&mut self.serial_devices.devices[self.device_idx].timeout, Duration::from_millis(1000), "1000"); + ui.selectable_value( + &mut self.serial_devices.devices[self.device_idx].timeout, + Duration::from_millis(0), + "0", + ); + ui.selectable_value( + &mut self.serial_devices.devices[self.device_idx].timeout, + Duration::from_millis(10), + "10", + ); + ui.selectable_value( + &mut self.serial_devices.devices[self.device_idx].timeout, + Duration::from_millis(100), + "100", + ); + ui.selectable_value( + &mut self.serial_devices.devices[self.device_idx].timeout, + Duration::from_millis(1000), + "1000", + ); }); }); } fn draw_export_settings(&mut self, ctx: &egui::Context, ui: &mut Ui) { egui::Grid::new("export_settings") - .num_columns(2) - .spacing(Vec2 { x: 10.0, y: 10.0 }) - .striped(true) - .show(ui, |ui| { - if ui.button(egui::RichText::new(format!("{} Save CSV", egui_phosphor::regular::FLOPPY_DISK))) - .on_hover_text("Save Plot Data to CSV.") - .clicked() || ui.input_mut(|i| i.consume_shortcut(&SAVE_FILE_SHORTCUT)) - { - if let Some(path) = rfd::FileDialog::new().save_file() { - self.picked_path = path; - self.picked_path.set_extension("csv"); - if let Err(e) = self.save_tx.send(FileOptions { - file_path: self.picked_path.clone(), - save_absolute_time: self.gui_conf.save_absolute_time, - save_raw_traffic: self.save_raw, - names: self.serial_devices.labels[self.device_idx].clone(), - }) { - print_to_console( - &self.print_lock, - Print::Error(format!( - "save_tx thread send failed: {:?}", - e - )), - ); + .num_columns(2) + .spacing(Vec2 { x: 10.0, y: 10.0 }) + .striped(true) + .show(ui, |ui| { + if ui + .button(egui::RichText::new(format!( + "{} Save CSV", + egui_phosphor::regular::FLOPPY_DISK + ))) + .on_hover_text("Save Plot Data to CSV.") + .clicked() + || ui.input_mut(|i| i.consume_shortcut(&SAVE_FILE_SHORTCUT)) + { + if let Some(path) = rfd::FileDialog::new().save_file() { + self.picked_path = path; + self.picked_path.set_extension("csv"); + if let Err(e) = self.save_tx.send(FileOptions { + file_path: self.picked_path.clone(), + save_absolute_time: self.gui_conf.save_absolute_time, + save_raw_traffic: self.save_raw, + names: self.serial_devices.labels[self.device_idx].clone(), + }) { + log::error!("save_tx thread send failed: {:?}", e); + } } - } - }; - - if ui - .button(egui::RichText::new(format!("{} Save Plot", egui_phosphor::regular::FLOPPY_DISK))) - .on_hover_text("Save an image of the Plot.") - .clicked() || ui.input_mut(|i| i.consume_shortcut(&SAVE_PLOT_SHORTCUT)) + }; - { - ctx.send_viewport_cmd(egui::ViewportCommand::Screenshot); - } - ui.end_row(); - if ui.button(egui::RichText::new(format!("{} Clear Data", egui_phosphor::regular::X))) - .on_hover_text("Clear Data from Plot.") - .clicked() || ui.input_mut(|i| i.consume_shortcut(&CLEAR_PLOT_SHORTCUT)) { - print_to_console( - &self.print_lock, - Print::Ok("Cleared recorded Data".to_string()), - ); - if let Err(err) = self.clear_tx.send(true) { - print_to_console( - &self.print_lock, - Print::Error(format!( - "clear_tx thread send failed: {:?}", - err - )), - ); + if ui + .button(egui::RichText::new(format!( + "{} Save Plot", + egui_phosphor::regular::FLOPPY_DISK + ))) + .on_hover_text("Save an image of the Plot.") + .clicked() + || ui.input_mut(|i| i.consume_shortcut(&SAVE_PLOT_SHORTCUT)) + { + ctx.send_viewport_cmd(egui::ViewportCommand::Screenshot); } - // need to clear the data here in order to prevent errors in the gui (plot) - self.data = DataContainer::default(); - // self.names_tx.send(self.serial_devices.labels[self.device_idx].clone()).expect("Failed to send names"); - - } - ui.end_row(); - ui.label("Save Raw Traffic"); - ui.add(toggle(&mut self.save_raw)) - .on_hover_text("Save second CSV containing raw traffic.") - .changed(); - ui.end_row(); - ui.label("Save Absolute Time"); - ui.add(toggle(&mut self.gui_conf.save_absolute_time)) - .on_hover_text("Save absolute time in CSV."); - ui.end_row(); - }); + ui.end_row(); + ui.label("Save Raw Traffic"); + ui.add(toggle(&mut self.save_raw)) + .on_hover_text("Save second CSV containing raw traffic.") + .changed(); + ui.end_row(); + ui.label("Save Absolute Time"); + ui.add(toggle(&mut self.gui_conf.save_absolute_time)) + .on_hover_text("Save absolute time in CSV."); + ui.end_row(); + }); } fn draw_global_settings(&mut self, ui: &mut Ui) { @@ -804,19 +794,13 @@ impl MyApp { .clicked() || ui.input_mut(|i| i.consume_shortcut(&CLEAR_PLOT_SHORTCUT)) { - print_to_console( - &self.print_lock, - Print::Ok("Cleared recorded Data".to_string()), - ); + log::info!("Cleared recorded Data"); if let Err(err) = self.clear_tx.send(true) { - print_to_console( - &self.print_lock, - Print::Error(format!("clear_tx thread send failed: {:?}", err)), - ); + log::error!("clear_tx thread send failed: {:?}", err); } // need to clear the data here in order to prevent errors in the gui (plot) self.data = DataContainer::default(); - //self.names_tx.send(self.serial_devices.plot_labels[self.device_idx].clone()).expect("Failed to send names"); + // self.names_tx.send(self.serial_devices.labels[self.device_idx].clone()).expect("Failed to send names"); } ui.horizontal(|ui| { if ui.button("Clear Device History").clicked() { @@ -829,6 +813,19 @@ impl MyApp { // self.serial_devices.labels[self.device_idx] = self.serial_devices.labels.clone(); } }); + + ui.end_row(); + ui.label("Show Sent Commands"); + ui.add(toggle(&mut self.show_sent_cmds)) + .on_hover_text("Show sent commands in console."); + ui.end_row(); + ui.label("Show Timestamp"); + ui.add(toggle(&mut self.show_timestamps)) + .on_hover_text("Show timestamp in console."); + ui.end_row(); + ui.label("EOL character"); + ui.add(egui::TextEdit::singleline(&mut self.eol).desired_width(ui.available_width() * 0.9)) + .on_hover_text("Configure your EOL character for sent commands.."); } fn draw_plot_settings(&mut self, ui: &mut Ui) { @@ -898,23 +895,6 @@ impl MyApp { (self.serial_devices.number_of_plots[self.device_idx] + 1).clamp(0, 10); } }); - - ui.end_row(); - ui.label("Show Sent Commands"); - ui.add(toggle(&mut self.show_sent_cmds)) - .on_hover_text("Show sent commands in console."); - ui.end_row(); - ui.label("Show Timestamp"); - ui.add(toggle(&mut self.show_timestamps)) - .on_hover_text("Show timestamp in console."); - ui.end_row(); - ui.label("EOL character"); - ui.add( - egui::TextEdit::singleline(&mut self.eol) - .desired_width(ui.available_width() * 0.9), - ) - .on_hover_text("Configure your EOL character for sent commands.."); - // ui.checkbox(&mut self.gui_conf.debug, "Debug Mode"); ui.end_row(); }); ui.add_space(25.0); @@ -1032,9 +1012,12 @@ impl MyApp { ui.add_space(5.0); for i in 0..(self.serial_devices.number_of_highlights[self.device_idx]) { ui.add( - egui::TextEdit::singleline(&mut self.serial_devices.highlight_labels[self.device_idx][i]) - .desired_width(0.95 * RIGHT_PANEL_WIDTH) - ).on_hover_text("Sentence to highlight"); + egui::TextEdit::singleline( + &mut self.serial_devices.highlight_labels[self.device_idx][i], + ) + .desired_width(0.95 * RIGHT_PANEL_WIDTH), + ) + .on_hover_text("Sentence to highlight"); /* // Todo implement the color picker for each sentence let mut theme = @@ -1056,63 +1039,35 @@ impl MyApp { .show(ctx, |ui| { egui::ScrollArea::vertical().show(ui, |ui| { ui.add_enabled_ui(true, |ui| { - - self.draw_serial_settings(ctx,ui); + self.draw_serial_settings(ctx, ui); self.draw_global_settings(ui); ui.add_space(10.0); - CollapsingHeader::new("Plot") + CollapsingHeader::new("Plot Settings") .default_open(true) .show(ui, |ui| { self.draw_plot_settings(ui); }); - CollapsingHeader::new("Text Highlight") + CollapsingHeader::new("Text Highlight Settings") .default_open(true) .show(ui, |ui| { self.draw_highlight_settings(ctx, ui); }); - CollapsingHeader::new("Export") + CollapsingHeader::new("Export Settings") .default_open(true) .show(ui, |ui| { self.draw_export_settings(ctx, ui); }); - - if let Ok(read_guard) = self.print_lock.read() { - self.console = read_guard.clone(); - } - let num_rows = self.console.len(); - let row_height = ui.text_style_height(&egui::TextStyle::Body); - - ui.add_space(20.0); - ui.separator(); - ui.label("Debug Info:"); - ui.add_space(5.0); - egui::ScrollArea::vertical() - .id_salt("console_scroll_area") - .auto_shrink([false; 2]) - .stick_to_bottom(true) - .max_height(row_height * 15.5) - .show_rows(ui, row_height, num_rows, |ui, _row_range| { - let content: String = self - .console - .iter() - .flat_map(|row| row.scroll_area_message(&self.gui_conf)) - .map(|msg| msg.label + msg.content.as_str()) - .collect::>() - .join("\n"); - // we need to add it as one multiline object, such that we can select and copy - // text over multiple lines - ui.add( - egui::TextEdit::multiline(&mut content.as_str()) - .font(DEFAULT_FONT_ID) // for cursor height - .lock_focus(true), // TODO: add a layouter to highlight the labels - ); - }); }); + ui.add_space(20.0); + ui.separator(); + ui.collapsing("Debug logs:", |ui| { + egui_logger::logger_ui().show(ui); + }); + }); }); - }); } fn paint_connection_indicator(&self, ui: &mut egui::Ui) { @@ -1172,7 +1127,7 @@ impl eframe::App for MyApp { image::ColorType::Rgba8, ) .unwrap(); - eprintln!("Image saved to {path:?}."); + println!("Image saved to {path:?}."); } } } @@ -1183,4 +1138,4 @@ impl eframe::App for MyApp { println!("gui settings save failed: {:?}", err); } } -} \ No newline at end of file +} diff --git a/src/main.rs b/src/main.rs index d5fc4b4..3b04644 100644 --- a/src/main.rs +++ b/src/main.rs @@ -12,7 +12,7 @@ use std::thread; use std::time::Duration; use crate::data::{DataContainer, Packet}; -use crate::gui::{load_gui_settings, print_to_console, MyApp, Print, RIGHT_PANEL_WIDTH}; +use crate::gui::{load_gui_settings, MyApp, RIGHT_PANEL_WIDTH}; use crate::io::{save_to_csv, FileOptions}; use crate::serial::{load_serial_settings, serial_thread, Device}; use eframe::egui::{vec2, ViewportBuilder, Visuals}; @@ -49,7 +49,6 @@ fn split(payload: &str) -> Vec { fn main_thread( sync_tx: Sender, data_lock: Arc>, - print_lock: Arc>>, raw_data_rx: Receiver, save_rx: Receiver, clear_rx: Receiver, @@ -102,18 +101,13 @@ fn main_thread( if let Ok(csv_options) = save_rx.recv_timeout(Duration::from_millis(1)) { match save_to_csv(&data, &csv_options) { Ok(_) => { - print_to_console( - &print_lock, - Print::Ok(format!("saved data file to {:?} ", csv_options.file_path)), - ); + log::info!("saved data file to {:?} ", csv_options.file_path); } Err(e) => { - print_to_console( - &print_lock, - Print::Error(format!( - "failed to save file to {:?}: {:?}", - csv_options.file_path, e - )), + log::error!( + "failed to save file to {:?}: {:?}", + csv_options.file_path, + e ); } } @@ -124,13 +118,14 @@ fn main_thread( } fn main() { + egui_logger::builder().init().unwrap(); + let gui_settings = load_gui_settings(); let saved_serial_device_configs = load_serial_settings(); let device_lock = Arc::new(RwLock::new(Device::default())); let devices_lock = Arc::new(RwLock::new(vec![gui_settings.device.clone()])); let data_lock = Arc::new(RwLock::new(DataContainer::default())); - let print_lock = Arc::new(RwLock::new(vec![Print::Empty])); let connected_lock = Arc::new(RwLock::new(false)); let (save_tx, save_rx): (Sender, Receiver) = mpsc::channel(); @@ -141,7 +136,6 @@ fn main() { let serial_device_lock = device_lock.clone(); let serial_devices_lock = devices_lock.clone(); - let serial_print_lock = print_lock.clone(); let serial_connected_lock = connected_lock.clone(); println!("starting connection thread.."); @@ -151,24 +145,15 @@ fn main() { raw_data_tx, serial_device_lock, serial_devices_lock, - serial_print_lock, serial_connected_lock, ); }); let main_data_lock = data_lock.clone(); - let main_print_lock = print_lock.clone(); println!("starting main thread.."); let _main_thread_handler = thread::spawn(|| { - main_thread( - sync_tx, - main_data_lock, - main_print_lock, - raw_data_rx, - save_rx, - clear_rx, - ); + main_thread(sync_tx, main_data_lock, raw_data_rx, save_rx, clear_rx); }); let options = eframe::NativeOptions { @@ -186,7 +171,6 @@ fn main() { let gui_device_lock = device_lock; let gui_devices_lock = devices_lock; let gui_connected_lock = connected_lock; - let gui_print_lock = print_lock; if let Err(e) = eframe::run_native( "Serial Monitor", @@ -205,7 +189,6 @@ fn main() { }); Ok(Box::new(MyApp::new( - gui_print_lock, gui_data_lock, gui_device_lock, gui_devices_lock, diff --git a/src/serial.rs b/src/serial.rs index 567c005..74b6df6 100644 --- a/src/serial.rs +++ b/src/serial.rs @@ -9,7 +9,7 @@ use std::time::{Duration, Instant}; use crate::color_picker::COLORS; use crate::data::{get_epoch_ms, SerialDirection}; -use crate::{print_to_console, Packet, Print, APP_INFO, PREFS_KEY_SERIAL}; +use crate::{Packet, APP_INFO, PREFS_KEY_SERIAL}; #[derive(Debug, Clone, Serialize, Deserialize)] pub struct SerialDevices { @@ -104,7 +104,6 @@ pub fn serial_thread( raw_data_tx: Sender, device_lock: Arc>, devices_lock: Arc>>, - print_lock: Arc>>, connected_lock: Arc>, ) { let mut last_connected_device = Device::default(); @@ -131,23 +130,20 @@ pub fn serial_thread( if let Ok(mut connected) = connected_lock.write() { *connected = true; } - print_to_console( - &print_lock, - Print::Ok(format!( - "Connected to serial port: {} @ baud = {}", - device.name, device.baud_rate - )), + + log::info!( + "Connected to serial port: {} @ baud = {}", + device.name, + device.baud_rate ); + BufReader::new(p) } Err(err) => { if let Ok(mut write_guard) = device_lock.write() { write_guard.name.clear(); } - print_to_console( - &print_lock, - Print::Error(format!("Error connecting: {}", err)), - ); + log::error!("Error connecting: {}", err); continue; } }; @@ -167,10 +163,7 @@ pub fn serial_thread( *write_guard = devices.clone(); } - if let Some(message) = - disconnected(&device, &devices, &device_lock, &mut last_connected_device) - { - print_to_console(&print_lock, message); + if disconnected(&device, &devices, &device_lock, &mut last_connected_device) { break 'connected_loop; } @@ -225,15 +218,13 @@ fn disconnected( devices: &[String], device_lock: &Arc>, last_connected_device: &mut Device, -) -> Option { +) -> bool { // disconnection by button press if let Ok(read_guard) = device_lock.read() { if device.name != read_guard.name { *last_connected_device = Device::default(); - return Some(Print::Ok(format!( - "Disconnected from serial port: {}", - device.name - ))); + log::info!("Disconnected from serial port: {}", device.name); + return true; } } @@ -243,13 +234,10 @@ fn disconnected( write_guard.name.clear(); } *last_connected_device = device.clone(); - return Some(Print::Error(format!( - "Device has disconnected from serial port: {}", - device.name - ))); - } - - None + log::error!("Device has disconnected from serial port: {}", device.name); + return true; + }; + false } fn perform_writes(