Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • Volkor/yams
1 result
Show changes
Commits on Source (3)
......@@ -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",
]
......
......@@ -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"
......@@ -29,6 +29,7 @@ This project serves as all that + a full historical backend of a meshtastic netw
## What it doesn't (currently)
Rough order of how much I want to add support.
- [ ] Working tiling UI (in progress!)
- [ ] Shows Historical Data as a nice graph
- [ ] SNR (both individual neighbours and overall)
- RSSI is done per packet, instead of in nodeinfo. this seems... clunky.
......@@ -54,6 +55,10 @@ Rough order of how much I want to add support.
- [ ] Lets you send messages/packets
- [ ] Supports multiple meshtastic nodes
- Runs meshtastic (you still need a meshtastic device)
- Runs ON the meshtastic device (you need a separate device)
- Maybe in the future we can have this compiled down to wasm, runs in browser and saves the db in browser. hosted on the esp32 itself.
- Is it small enough once compiled to wasm?
- Is there support for local database saving?
## Installation
......
......@@ -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
use longitude::DistanceUnit;
use serde::Deserialize;
/// Holds the individual config segments.
......
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()
......
File moved
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
use core::f32;
use egui::{text::TextWrapping, Color32, Label, RichText, Separator};
use egui_tiles::UiResponse;
use crate::{built_info, gui::Pane};
use egui::{Color32, Label, RichText};
use crate::built_info;
/// Shows information about the program.
/// you know, the normal boring stuff
......@@ -12,12 +9,11 @@ use crate::{built_info, gui::Pane};
/// - 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 ...
// double TODO: replace the logo with a cool ascii art of actual yams (vegetable) with text
let mut cool_logo = RichText::new("
let cool_logo = RichText::new("
▓██ ██▓ ▄▄▄ ███▄ ▄███▓ ██████
▒██ ██▒▒████▄ ▓██▒▀█▀ ██▒▒██ ▒
▒██ ██░▒██ ▀█▄ ▓██ ▓██░░ ▓██▄
......@@ -37,13 +33,6 @@ pub fn about_ui(ui: &mut egui::Ui) {
} else {
Color32::DARK_RED
};
// Although we need to disable some stuff to make it not look like crap most of the time...
let text_wrapping = TextWrapping {
max_width: f32::MAX,
overflow_character: None,
..Default::default()
};
ui.add(Label::new(cool_logo.color(color)));
......
/// 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
// 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
......@@ -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!(),
......
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
......@@ -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 {
......@@ -163,31 +163,4 @@ pub fn nodelist_ui(ui: &mut egui::Ui) {
});
}
});
}
// /// Initialise the raw GUI.
// /// We pass all tiling 'widgets' to their own files.
// pub(crate) async fn run_gui() {
// debug!("Running in GUI Mode!");
// use eframe::{App, egui};
// struct MyApp;
// impl App for MyApp {
// fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
// // Table Panel, showing nodes in a list.
// egui::CentralPanel::default().show(ctx, |ui| {
// });
// }
// }
// let native_options = eframe::NativeOptions::default();
// eframe::run_native(
// "Yet Another Meshtastic Server (GUI)",
// native_options,
// Box::new(|_cc| Ok(Box::new(MyApp))),
// ).unwrap();
// }
\ No newline at end of file
}
\ No newline at end of file
......@@ -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);
}
}
......
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;
......