diff --git a/Cargo.lock b/Cargo.lock index 883d2ab8b80f374050c8c6298cda0f4be28dc2f9..75780af4db63c8076b956c6be3704f0fcca6f968 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -206,6 +206,56 @@ dependencies = [ "libc", ] +[[package]] +name = "anstream" +version = "0.6.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" + +[[package]] +name = "anstyle-parse" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c" +dependencies = [ + "windows-sys 0.59.0", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3534e77181a9cc07539ad51f2141fe32f6c3ffd4df76db8ad92346b003ae4e" +dependencies = [ + "anstyle", + "once_cell", + "windows-sys 0.59.0", +] + [[package]] name = "anyhow" version = "1.0.95" @@ -889,12 +939,39 @@ dependencies = [ "unicode-width", ] +[[package]] +name = "colog" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c426b7af8d5e0ad79de6713996632ce31f0d68ba84068fb0d654b396e519df0" +dependencies = [ + "colored", + "env_logger", + "log", +] + [[package]] name = "color_quant" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" +[[package]] +name = "colorchoice" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" + +[[package]] +name = "colored" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "117725a109d387c937a1533ce01b450cbde6b88abceea8473c4d7a85853cda3c" +dependencies = [ + "lazy_static", + "windows-sys 0.59.0", +] + [[package]] name = "combine" version = "4.6.7" @@ -1293,6 +1370,18 @@ dependencies = [ "winit", ] +[[package]] +name = "egui_logger" +version = "0.6.2" +source = "git+https://github.com/rnd-ash/egui_logger?branch=main#f274d8da2322b3d1bd02a7ff8f8ef025c33f24a9" +dependencies = [ + "chrono", + "egui", + "hashbrown 0.15.2", + "log", + "regex", +] + [[package]] name = "egui_tiles" version = "0.12.0" @@ -1404,6 +1493,29 @@ dependencies = [ "syn 2.0.95", ] +[[package]] +name = "env_filter" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "186e05a59d4c50738528153b83b0b0194d3a29507dfec16eccd4b342903397d0" +dependencies = [ + "log", + "regex", +] + +[[package]] +name = "env_logger" +version = "0.11.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcaee3d8e3cfc3fd92428d477bc97fc29ec8716d180c0d74c643bb26166660e0" +dependencies = [ + "anstream", + "anstyle", + "env_filter", + "humantime", + "log", +] + [[package]] name = "epaint" version = "0.31.0" @@ -2097,6 +2209,12 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" +[[package]] +name = "humantime" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" + [[package]] name = "hyper" version = "0.14.32" @@ -2394,6 +2512,12 @@ version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" +[[package]] +name = "is_terminal_polyfill" +version = "1.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" + [[package]] name = "itertools" version = "0.10.5" @@ -2674,9 +2798,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.22" +version = "0.4.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" +checksum = "04cbf5b083de1c7e0222a7a51dbfdba1cbe1c6ab0b15e29fff3f6c077fd9cd9f" [[package]] name = "longitude" @@ -3014,16 +3138,6 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0676bb32a98c1a483ce53e500a81ad9c3d5b3f7c920c28c24e9cb0980d0b5bc8" -[[package]] -name = "nu-ansi-term" -version = "0.46.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" -dependencies = [ - "overload", - "winapi", -] - [[package]] name = "num-bigint" version = "0.4.6" @@ -3386,12 +3500,6 @@ dependencies = [ "pin-project-lite", ] -[[package]] -name = "overload" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" - [[package]] name = "owned_ttf_parser" version = "0.25.0" @@ -4366,15 +4474,6 @@ dependencies = [ "digest", ] -[[package]] -name = "sharded-slab" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" -dependencies = [ - "lazy_static", -] - [[package]] name = "shlex" version = "1.3.0" @@ -4741,16 +4840,6 @@ dependencies = [ "syn 2.0.95", ] -[[package]] -name = "thread_local" -version = "1.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" -dependencies = [ - "cfg-if", - "once_cell", -] - [[package]] name = "tiff" version = "0.9.1" @@ -4976,32 +5065,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" dependencies = [ "once_cell", - "valuable", -] - -[[package]] -name = "tracing-log" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" -dependencies = [ - "log", - "once_cell", - "tracing-core", -] - -[[package]] -name = "tracing-subscriber" -version = "0.3.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008" -dependencies = [ - "nu-ansi-term", - "sharded-slab", - "smallvec", - "thread_local", - "tracing-core", - "tracing-log", ] [[package]] @@ -5180,6 +5243,12 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + [[package]] name = "uuid" version = "1.11.0" @@ -5197,12 +5266,6 @@ dependencies = [ "wasm-bindgen", ] -[[package]] -name = "valuable" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" - [[package]] name = "vcpkg" version = "0.2.15" @@ -6297,9 +6360,11 @@ dependencies = [ "built", "chrono", "chrono-humanize", + "colog", "eframe", "egui", "egui_extras", + "egui_logger", "egui_tiles", "fern", "figment", @@ -6313,8 +6378,6 @@ dependencies = [ "rusqlite_migration", "serde", "tokio", - "tracing", - "tracing-subscriber", "walkers", ] diff --git a/Cargo.toml b/Cargo.toml index 6e3c3676f0bd1fe431ffceadad5d72edf3497fdf..9a0d36061cd968a5a691b676b8e944ec9ce74d7c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,11 +7,9 @@ build = "build.rs" [dependencies] fern = "0.7.1" -log = "0.4.22" +log = "0.4.25" meshtastic = "0.1.6" tokio = "1.42.0" -tracing = "0.1.41" -tracing-subscriber = "0.3.19" rusqlite = { version = "0.32.1", features = ["bundled"] } rusqlite_migration = "1.3.1" lazy_static = "1.5.0" @@ -23,13 +21,15 @@ chrono-humanize = "0.2.3" # gui eframe = { version = "0.31.0", features = ["wgpu"] } egui = "0.31.0" -walkers = "0.34.0" egui_extras = { version = "0.31.0", features = ["all_loaders"] } +egui_tiles = "0.12.0" +egui_logger = { git = "https://github.com/rnd-ash/egui_logger", branch = "main" } +walkers = "0.34.0" image = { version = "0.25", features = ["jpeg", "png", "webp"] } chrono = "0.4.39" longitude = "0.2.1" -egui_tiles = "0.12.0" +colog = "1.3.0" [build-dependencies] built = { version = "0.7.6", features = ["git2", "chrono"] } -git2 = "0.20.0" \ No newline at end of file +git2 = "0.20.0" diff --git a/docs/gui.md b/docs/gui.md index a114ee8ab03978c6bedf4eecb793ae7a4002e7de..59aa0080364db6e37153b615688656c39a8e24d5 100644 --- a/docs/gui.md +++ b/docs/gui.md @@ -11,4 +11,6 @@ - 'Tiling' UI, make it piss easy to change the UI to each user's liking. - Pop out windows to new ones? Can we also tile them?? - List of Presets, with naming? - - One single context/alt/top bar for file, edit, whatever. \ No newline at end of file + - One single context/alt/top bar for file, edit, whatever. +- Logging view + - https://github.com/RegenJacob/egui_logger \ No newline at end of file diff --git a/src/config.rs b/src/config.rs index 1ec16916530e651dca3a66119aae1288234f6f31..7deb82fc96bf3a525d48f5d0ee5e275c7abf989c 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,4 +1,3 @@ -use longitude::DistanceUnit; use serde::Deserialize; /// Holds the individual config segments. diff --git a/src/db.rs b/src/db.rs index 0acdbed01a6f8a07f544c551d9842eb47e757531..35fab6ac9f21dcfa9ff55884993c6be87be0ee8e 100644 --- a/src/db.rs +++ b/src/db.rs @@ -1,15 +1,14 @@ -use std::path::{Path, PathBuf}; +use std::path::PathBuf; use std::fs::File; -use std::io; -use meshtastic::protobufs::config::{BluetoothConfig, DeviceConfig, DisplayConfig, LoRaConfig, NetworkConfig, PayloadVariant, PositionConfig, PowerConfig}; +use meshtastic::protobufs::config::{BluetoothConfig, DeviceConfig, DisplayConfig, LoRaConfig, NetworkConfig, PositionConfig, PowerConfig}; use meshtastic::protobufs::module_config::{AmbientLightingConfig, AudioConfig, CannedMessageConfig, DetectionSensorConfig, ExternalNotificationConfig, MqttConfig, NeighborInfoConfig, PaxcounterConfig, RangeTestConfig, RemoteHardwareConfig, SerialConfig, StoreForwardConfig, TelemetryConfig}; use rusqlite::{Connection, Result}; use rusqlite_migration::{Migrations, M}; use serde::{Deserialize, Serialize}; -use tracing::{debug, error, info}; +use log::{error, warn, info, debug, trace}; use lazy_static::lazy_static; -use meshtastic::protobufs::{Channel, Data, DeviceMetrics, MeshPacket, MyNodeInfo, NodeInfo, Position, User}; +use meshtastic::protobufs::{Channel, DeviceMetrics, MeshPacket, MyNodeInfo, NodeInfo, Position, User}; use crate::CONFIG; @@ -25,7 +24,7 @@ lazy_static! { ]); } -fn u32_vec_to_u8_vec(vec: &Vec<u32>) -> Vec<u8> { +fn u32_vec_to_u8_vec(vec: &[u32]) -> Vec<u8> { vec.iter() .flat_map(|x| x.to_be_bytes().to_vec()) .collect() diff --git a/src/gui/context_menu.rs b/src/gui/menus.rs similarity index 100% rename from src/gui/context_menu.rs rename to src/gui/menus.rs diff --git a/src/gui/mod.rs b/src/gui/mod.rs index abc0769f328873a3bd40c3843ab0852016cc4be0..f77847c1d7655d1bc53b4b89d071010ad46f95c7 100644 --- a/src/gui/mod.rs +++ b/src/gui/mod.rs @@ -1,13 +1,16 @@ pub mod widgets; use crate::gui::widgets::WidgetType::About; -use egui::{menu, Color32, FontDefinitions}; -use egui_tiles::{SimplificationOptions, Tile, TileId, Tiles}; -use tracing::debug; +use egui::{Ui, menu, Color32, FontDefinitions}; +use egui_tiles::{Container, SimplificationOptions, Tile, TileId, Tiles}; +use log::{error, warn, info, debug, trace}; +use widgets::about; /// Runs the actual GUI. /// We contruct the empty tree, headers and basic window stuff here. /// Widgets are all opened via this, or loaded from a saved state. pub(crate) async fn run_gui() -> Result<(), eframe::Error> { + // Start Logging + egui_logger::builder().init().expect("Error initialising egui_logger"); debug!("Running in GUI Mode!"); // run actual ui let options = eframe::NativeOptions { @@ -34,13 +37,29 @@ pub(crate) async fn run_gui() -> Result<(), eframe::Error> { /// This allows us to add new widgets into the UI. impl YamsApp { // Add a new pane to the tree, depending on the widget_type. - fn add_about(&mut self, widget_type: widgets::WidgetType,) { - // Find the next free id. (maybe?) + fn add_widget(&mut self, widget_type: widgets::WidgetType,) { + // Find the next view number we can use let next_view_num = self.tree.tiles.next_free_id().0 as usize; + // Create a new pane with said view number. - let pane = Pane::with_nr(next_view_num, widget_type); - // Insert it! - let _ = self.tree.tiles.insert_pane(pane); + let pane = Pane::with_nr(next_view_num, widget_type.clone()); + + // Insert it into the tree + let pane_id = self.tree.tiles.insert_pane(pane); + // debug!("Inserted pane: {:#?}", pane); + + + // Add the new pane to the root tab tile if possible. + if let Some(root_id) = self.tree.root() { + // Modify the tile if it's a container and supports children. + if let Some(root_tile) = self.tree.tiles.get_mut(root_id) { + if let Tile::Container(container) = root_tile { + if let Container::Tabs(tabs) = container { + tabs.children.push(pane_id); + } + } + } + } } } @@ -51,13 +70,16 @@ impl Default for YamsApp { fn default() -> Self { // Initialize a variable to keep track of the next view number let mut next_view_nr = 0; - // Define a closure that will create a new Pane with a unique view number - // and increment the next_view_nr variable for the next time it's called - let mut gen_view = || { - let view = Pane::with_nr(next_view_nr, widgets::WidgetType::NodeList); - next_view_nr += 1; - view - }; + + // Closure (fancy function?) to create new Panes + // let mut gen_view = || { + // let view = Pane::with_nr(next_view_nr, widgets::WidgetType::About); + // next_view_nr += 1; + // view + // }; + // Create new panes for default + // let about_view = Pane::with_nr(next_view_nr+1, widgets::WidgetType::About); + let nodelist_view = Pane::with_nr(next_view_nr+1, widgets::WidgetType::NodeList); // Create an empty tiles structure to store a tile (which can contain more tiles) let mut tiles = egui_tiles::Tiles::default(); @@ -65,33 +87,9 @@ impl Default for YamsApp { // Vector to hold the ids of our tabs let mut tabs = vec![]; - // Insert a tab tile with 7 children into our tiles and push its id onto the tabs vector - let tab_tile = { - let children = (0..7).map(|_| tiles.insert_pane(gen_view())).collect(); - tiles.insert_tab_tile(children) - }; - tabs.push(tab_tile); - - // Insert a horizontal tile with 7 children into our tiles and push its id onto the tabs vector - tabs.push({ - let children = (0..7).map(|_| tiles.insert_pane(gen_view())).collect(); - tiles.insert_horizontal_tile(children) - }); - - // Insert a vertical tile with 7 children into our tiles and push its id onto the tabs vector - tabs.push({ - let children = (0..7).map(|_| tiles.insert_pane(gen_view())).collect(); - tiles.insert_vertical_tile(children) - }); - - // Insert a grid tile with 11 cells into our tiles and push its id onto the tabs vector - tabs.push({ - let cells = (0..11).map(|_| tiles.insert_pane(gen_view())).collect(); - tiles.insert_grid_tile(cells) - }); - // Insert a single pane tile into our tiles and push its id onto the tabs vector - tabs.push(tiles.insert_pane(gen_view())); + // tabs.push(tiles.insert_pane(about_view)); + tabs.push(tiles.insert_pane(nodelist_view)); // Insert a tab tile with all of our tabs as children into our tiles let root = tiles.insert_tab_tile(tabs); @@ -100,10 +98,12 @@ impl Default for YamsApp { let tree = egui_tiles::Tree::new("my_tree", root, tiles); // Return a new YamsApp instance with our tree and default behavior - Self { + let self_state = Self { tree, behavior: Default::default(), - } + }; + debug!("Full YamsApp default state: {:#?}", &self_state); + self_state } } @@ -144,6 +144,7 @@ impl Pane { } // The storage for the Tree Behaviour config options +#[derive(Debug)] struct TreeBehavior { simplification_options: egui_tiles::SimplificationOptions, tab_bar_height: f32, @@ -290,16 +291,16 @@ impl egui_tiles::Behavior<Pane> for TreeBehavior { Tile::Pane(pane) => { // Single pane removal let tab_title = self.tab_title_for_pane(pane); - log::debug!("Closing tab: {}, tile ID: {tile_id:?}", tab_title.text()); + debug!("Closing tab: {}, tile ID: {tile_id:?}", tab_title.text()); } Tile::Container(container) => { // Container removal - log::debug!("Closing container: {:?}", container.kind()); + debug!("Closing container: {:?}", container.kind()); let children_ids = container.children(); for child_id in children_ids { if let Some(Tile::Pane(pane)) = tiles.get(*child_id) { let tab_title = self.tab_title_for_pane(pane); - log::debug!("Closing tab: {}, tile ID: {tile_id:?}", tab_title.text()); + debug!("Closing tab: {}, tile ID: {tile_id:?}", tab_title.text()); } } } @@ -311,7 +312,7 @@ impl egui_tiles::Behavior<Pane> for TreeBehavior { } } -#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] +#[derive(Debug)] struct YamsApp { tree: egui_tiles::Tree<Pane>, @@ -336,8 +337,30 @@ impl eframe::App for YamsApp { // List of widgets here, we can add new widgets into the panel from here! ui.menu_button("Widgets", |ui| { - // Nested menus here - // Add Widget -> Widget Selector + // make a add widget button for every single widget type + ui.menu_button("Add Widget", |ui| { + if ui.button("About YAMS").clicked() { + self.add_widget(widgets::WidgetType::About); + } + if ui.button("Node List").clicked() { + self.add_widget(widgets::WidgetType::NodeList); + } + if ui.button("Node Info").clicked() { + self.add_widget(widgets::WidgetType::NodeInfo); + } + if ui.button("Logs").clicked() { + self.add_widget(widgets::WidgetType::Log); + } + if ui.button("Map View").clicked() { + self.add_widget(widgets::WidgetType::MapView); + } + if ui.button("Settings").clicked() { + self.add_widget(widgets::WidgetType::Settings); + } + if ui.button("Signal Strength").clicked() { + self.add_widget(widgets::WidgetType::SignalStrengthHistorical); + } + }) }); // UI Tools (reset, split? layouts?) @@ -353,8 +376,7 @@ impl eframe::App for YamsApp { } if ui.button("About").clicked() { // Create a new about widget (don't show it in the widgets menu?) - // TODO: THIS DOES NOT WORK! - self.add_about(widgets::WidgetType::About); + self.add_widget(widgets::WidgetType::About); } }); @@ -403,7 +425,7 @@ fn tree_ui( // Temporarily remove the tile to circumvent the borrowchecker let Some(mut tile) = tiles.remove(tile_id) else { - log::debug!("Missing tile {tile_id:?}"); + debug!("Missing tile {tile_id:?}"); return; }; @@ -443,4 +465,4 @@ fn tree_ui( // Put the tile back tiles.insert(tile_id, tile); -} +} \ No newline at end of file diff --git a/src/gui/widgets/about.rs b/src/gui/widgets/about.rs index 3d34b741f8427e63cb8c25a7a69ae22bfa613c1d..3ee4b8ee9487fc2a8b6180640efd6b9261b5e631 100644 --- a/src/gui/widgets/about.rs +++ b/src/gui/widgets/about.rs @@ -9,7 +9,6 @@ use crate::built_info; /// - link to repo /// - contributors /// - random shit - pub fn about_ui(ui: &mut egui::Ui) { // Show cool ascii logo // TODO: make this not wrap or truncate with ... diff --git a/src/gui/widgets/log.rs b/src/gui/widgets/log.rs deleted file mode 100644 index 67d043278fc397c5c6e0c6986cb7a40c2936d996..0000000000000000000000000000000000000000 --- a/src/gui/widgets/log.rs +++ /dev/null @@ -1,3 +0,0 @@ -/// This widget shows all log output from the program. -/// You should be able to open multiple of these, so we can have "Serial Log" and "YAMS Log" as two separate instances of this widget. -/// Filtering by log level? \ No newline at end of file diff --git a/src/gui/widgets/logging.rs b/src/gui/widgets/logging.rs new file mode 100644 index 0000000000000000000000000000000000000000..b9ed7482eb03ec34ec6f73a359e82a72ea6414d0 --- /dev/null +++ b/src/gui/widgets/logging.rs @@ -0,0 +1,10 @@ +// This widget shows all log output from the program. +// You should be able to open multiple of these, so we can have "Serial Log" and "YAMS Log" as two separate instances of this widget. +// Filtering by log level? + +use egui; + +pub fn log_ui(ui: &mut egui::Ui) { + // Ensure that `egui_logger::logger_ui()` returns an object compatible with `&mut egui::Ui` + egui_logger::logger_ui().enable_ctx_menu(true).show(ui); +} \ No newline at end of file diff --git a/src/gui/widgets/mod.rs b/src/gui/widgets/mod.rs index e1d6d30c2c2ddaf0f62b828cafc451c2edf79564..f048c5894a291642f4bdbc0d15ca323caa1215eb 100644 --- a/src/gui/widgets/mod.rs +++ b/src/gui/widgets/mod.rs @@ -3,6 +3,8 @@ use std::fmt; pub mod about; pub mod node_list; +pub mod node_info; +pub mod logging; #[derive(Clone)] pub(crate) enum WidgetType { @@ -39,7 +41,9 @@ pub fn render_widget(widget_type: &WidgetType, ui: &mut egui::Ui) { node_list::nodelist_ui(ui); }, WidgetType::NodeInfo => todo!(), - WidgetType::Log => todo!(), + WidgetType::Log => { + logging::log_ui(ui); + }, WidgetType::MapView => todo!(), WidgetType::Settings => todo!(), WidgetType::SignalStrengthHistorical => todo!(), diff --git a/src/gui/widgets/node_info.rs b/src/gui/widgets/node_info.rs new file mode 100644 index 0000000000000000000000000000000000000000..b8d10d3e2248f47313a3481d6bf33e83e5b91d20 --- /dev/null +++ b/src/gui/widgets/node_info.rs @@ -0,0 +1,143 @@ +use chrono::DateTime; +use chrono_humanize::HumanTime; +use egui_extras::{Column, TableBuilder}; +use longitude::{DistanceUnit, Location}; +use meshtastic::protobufs::{channel::Role, HardwareModel, Position}; + +use crate::{db::Database, CONFIG}; + +use super::node_list; + +// This handles viewing information about a specific node. +// Shows: +// - All the normal info from a node +// - Location History on a map? +// - Recent Messages? +// - Neighbours? +// - Battery Graph +// - Environmental Graph +// - First Seen / Last Heard +// - Distance + +/// UI Elements for the Node List +pub fn nodeinfo_ui(ui: &mut egui::Ui) { + // Get nodes from DB + let mut nodes = Database::read_node_list().expect("Failed to read nodes"); + + // Sort nodes by `last_heard` in descending order + nodes.sort_by(|a, b| b.last_heard.cmp(&a.last_heard)); + + TableBuilder::new(ui) + // TODO for the table: + // - Clickable Table Sorting + // - Add more info + // - Auto hide/show scrollbar on smaller devices + // - Add to a panel that we can move around / shitty windowing. + .column(Column::remainder()) // node id + .column(Column::remainder().clip(true)) // node name + .column(Column::remainder()) // snr + .column(Column::remainder()) // dist + .column(Column::remainder()) // hops + .column(Column::remainder().clip(true)) // hw model + .column(Column::remainder()) // role + .column(Column::remainder()) // battery + .column(Column::remainder().clip(true)) // last heard + .striped(true) + .header(15.0, |mut header| { + header.col(|ui| { + ui.heading("Node ID"); + }); + header.col(|ui| { + ui.heading("Name"); + }); + header.col(|ui| { + ui.heading("SNR"); + }); + header.col(|ui| { + + // Get distance units + let unit = match CONFIG.gui.units.as_ref() { + "Centimeters" => "cm", + "Meters" => "m", + "Kilometers" => "km", + "Inches" => "in", + "Feet" => "ft", + "Yards" => "yd", + "Miles" => "mi", + _ => "km", // Default to km if no match found. + }; + + ui.heading(format!("Distance ({})", unit)); + }); + header.col(|ui| { + ui.heading("Hops Away"); + }); + header.col(|ui| { + ui.heading("HW Model"); + }); + header.col(|ui| { + ui.heading("Role"); + }); + header.col(|ui| { + ui.heading("Battery"); + }); + header.col(|ui| { + ui.heading("Last Heard"); + }); + }) + .body(|mut body| { + for node in nodes { + let user = node.user.unwrap(); + let device = node.device_metrics.unwrap(); + // Convert the last heard to a time, then make it nice to read. + let now = chrono::Local::now().to_utc(); + let humanised = if node.last_heard == 0 { + String::from("Unknown") + } else { + let last_heard = DateTime::from_timestamp(node.last_heard.into(), 0).unwrap(); + let duration = last_heard - now; + HumanTime::from(duration).to_string() + }; + + body.row(15.0, |mut row| { + row.col(|ui| { + ui.label(user.id); + }); + row.col(|ui| { + ui.label(format!("{} ({})", user.long_name, user.short_name)); + }); + row.col(|ui| { + ui.label(node.snr.to_string()); + }); + row.col(|ui| { + if let Some(distance) = node_list::calc_distance_to_node(node.position.unwrap()) { + if distance.is_nan() { + ui.label("-"); + } else { + ui.label(format!("{:.2}", distance)); + } + } else { + ui.label("-"); + } + }); + row.col(|ui| { + ui.label(node.hops_away.to_string()); + }); + row.col(|ui| { + // Convert the i32 to a HardwareModel object, then convert to string + ui.label(HardwareModel::from_i32(user.hw_model).unwrap_or_default().as_str_name()); + }); + row.col(|ui| { + // Convert the i32 to a Role object, convert to string. + ui.label(Role::from_i32(user.role).unwrap_or_default().as_str_name()); + }); + row.col(|ui| { + ui.label(device.battery_level.to_string()); + }); + row.col(|ui| { + ui.label(humanised); + }); + }); + } + }); +} \ No newline at end of file diff --git a/src/gui/widgets/node_list.rs b/src/gui/widgets/node_list.rs index 0206c341d7b7e109c82583cdd2e368c095f74a18..ef020318253f4f866e724d54ccf5be2b2db9be97 100644 --- a/src/gui/widgets/node_list.rs +++ b/src/gui/widgets/node_list.rs @@ -17,7 +17,7 @@ use crate::{db::Database, CONFIG}; // - Location (has/doesn't have gps location) /// Calculates the distance betwen 2 coordinate points. -fn calc_distance_to_node(away: Position) -> Option<f64> { +pub(crate) fn calc_distance_to_node(away: Position) -> Option<f64> { let home_pos = Database::read_home_node().expect("Failed to find Home Node").position.unwrap(); let distance = Location { diff --git a/src/main.rs b/src/main.rs index 5d6934eed3621cdec8ecb6a0e1bb84bf99604250..bc12fa3a0b55c772ebf33e3af2b0d7ba6e986c13 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,14 +3,11 @@ use std::{env, process}; use figment::{Figment, providers::{Format, Toml, Env}}; use lazy_static::lazy_static; -use meshtastic::api::{StreamApi, StreamHandle}; +use meshtastic::api::StreamApi; use meshtastic::utils; -use meshtastic::protobufs::*; use packets::parse; -use tokio::{net::TcpStream, sync::mpsc::UnboundedReceiver}; -use tracing::{debug, error, info, Level}; -use tracing_subscriber::FmtSubscriber; +use log::{error, warn, info, debug, trace}; mod nodes; mod packets; @@ -30,19 +27,20 @@ lazy_static! { .merge(Env::prefixed("yamm_")) .extract() .unwrap_or_default(); - tracing::info!("Loaded Config!"); - tracing::debug!("full config: {:?}", config); + info!("Loaded Config!"); + debug!("full config: {:?}", config); config }; } async fn run_server() { + // Start Logging + colog::init(); + // set up new connection let stream_api = StreamApi::new(); // Different connection types have different setups. - let mut tcp_stream: StreamHandle<TcpStream>; - //let mut serial_stream: StreamHandle<SerialStream>; // SerialStream isn't a valid type??? match &CONFIG.meshconfig.connection_type { config::ConnectionType::Tcp => { // Build the tcp stream object from config. @@ -83,25 +81,13 @@ async fn run_server() { #[tokio::main] async fn main() -> Result<(), Box<dyn std::error::Error>> { - // Initialise Logging - this isn't final. - let subscriber = FmtSubscriber::builder() - // all events higher than debug are shown - .with_max_level(Level::DEBUG) - .finish(); - tracing::subscriber::set_global_default(subscriber) - .expect("setting default subscriber failed"); - - // Hello World! - info!("Y(et) A(nother) M(eshtastic) S(erver) - v0.1.0"); - - // Do we run a server or gui? let args: Vec<String> = env::args().collect(); // display help on no args, exit. if args.len() < 2 { - info!("Usage: yams <mode>"); - info!("Modes: 'server', 'gui'"); + println!("Usage: yams <mode>"); + println!("Modes: 'server', 'gui'"); process::exit(1); } @@ -110,9 +96,9 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> { "server" => run_server().await, "gui" => gui::run_gui().await.expect("uh oh"), _ => { - error!("Unknown mode: {}", args[1]); - info!("Usage: yams <mode>"); - info!("Modes: 'server', 'gui'"); + eprintln!("Unknown mode: {}", args[1]); + println!("Usage: yams <mode>"); + println!("Modes: 'server', 'gui'"); process::exit(1); } } diff --git a/src/packets.rs b/src/packets.rs index d5fd3a0c1f3b51ed9530ea6b56a5e209b1bc18ed..9d5aad7174cc466bb4e6b09fb6bc90076e5b0668 100644 --- a/src/packets.rs +++ b/src/packets.rs @@ -1,10 +1,6 @@ -use std::path::Path; - -use config::device_config; use from_radio::PayloadVariant; -use log::info; -use meshtastic::protobufs::{config::device_config::Role, *}; -use tracing::{debug, error}; +use meshtastic::protobufs::*; +use log::{error, warn, info, debug, trace}; use crate::db::Database;