From c1238445a76369d35a5d626e2513c6d12cf1aed8 Mon Sep 17 00:00:00 2001 From: Adam McQuilkin <46639306+ajmcquilkin@users.noreply.github.com> Date: Mon, 19 Feb 2024 11:22:49 -0800 Subject: [PATCH 1/4] Compiling removal of graph --- src-tauri/src/analytics/README.md | 125 - .../algorithms/articulation_point/mod.rs | 173 -- .../algorithms/articulation_point/results.rs | 19 - .../algorithms/diffusion_centrality/mod.rs | 118 - .../diffusion_centrality/results.rs | 28 - .../analytics/algorithms/karger_stein/mod.rs | 155 -- src-tauri/src/analytics/algorithms/mod.rs | 16 - .../algorithms/most_similar_timeline/mod.rs | 1 - .../most_similar_timeline/results.rs | 9 - .../algorithms/predicted_state/mod.rs | 1 - .../algorithms/predicted_state/results.rs | 9 - .../analytics/algorithms/stoer_wagner/mod.rs | 277 --- .../algorithms/stoer_wagner/results.rs | 19 - .../convert_to_graph_from_string.rs | 42 - .../analytics/aux_functions/edge_factory.rs | 128 - .../aux_functions/get_most_similar.rs | 161 -- src-tauri/src/analytics/aux_functions/mod.rs | 4 - .../analytics/aux_functions/take_snapshot.rs | 114 - .../analytics/data_structures/binary_heap.rs | 164 -- .../src/analytics/data_structures/cut.rs | 26 - .../src/analytics/data_structures/mod.rs | 6 - .../data_structures/neighbor_info.rs | 26 - .../data_structures/stoer_wagner_ds.rs | 113 - .../src/analytics/data_structures/timeline.rs | 255 -- .../analytics/data_structures/union_find.rs | 75 - src-tauri/src/analytics/mod.rs | 4 - .../src/analytics/state/configuration.rs | 273 --- src-tauri/src/analytics/state/controller.rs | 177 -- src-tauri/src/analytics/state/history.rs | 240 -- src-tauri/src/analytics/state/mod.rs | 215 -- src-tauri/src/analytics/state/store.rs | 81 - .../src/constructors/init/init_edge_map.rs | 354 --- src-tauri/src/constructors/init/init_graph.rs | 265 --- .../src/constructors/init/init_node_map.rs | 64 - src-tauri/src/constructors/init/mod.rs | 3 - src-tauri/src/constructors/mock/mocks.rs | 219 -- src-tauri/src/constructors/mock/mod.rs | 1 - src-tauri/src/constructors/mod.rs | 2 - .../src/data_conversion/distance_constants.rs | 12 - .../data_conversion/distance_conversion.rs | 77 - src-tauri/src/data_conversion/mod.rs | 2 - src-tauri/src/device/mod.rs | 21 - src-tauri/src/device/state.rs | 12 +- src-tauri/src/graph/edge.rs | 55 - src-tauri/src/graph/graph_ds.rs | 784 ------ src-tauri/src/graph/mod.rs | 3 - src-tauri/src/graph/node.rs | 80 - src-tauri/src/ipc/commands/connections.rs | 14 +- src-tauri/src/ipc/commands/graph.rs | 214 -- src-tauri/src/ipc/commands/mod.rs | 1 - src-tauri/src/ipc/events.rs | 21 - src-tauri/src/ipc/helpers.rs | 112 +- src-tauri/src/main.rs | 17 - src-tauri/src/state.rs | 15 +- src/bindings/index.ts | 2107 +++++------------ src/features/device/sagas.ts | 3 - 56 files changed, 564 insertions(+), 6948 deletions(-) delete mode 100644 src-tauri/src/analytics/README.md delete mode 100644 src-tauri/src/analytics/algorithms/articulation_point/mod.rs delete mode 100644 src-tauri/src/analytics/algorithms/articulation_point/results.rs delete mode 100644 src-tauri/src/analytics/algorithms/diffusion_centrality/mod.rs delete mode 100644 src-tauri/src/analytics/algorithms/diffusion_centrality/results.rs delete mode 100644 src-tauri/src/analytics/algorithms/karger_stein/mod.rs delete mode 100644 src-tauri/src/analytics/algorithms/mod.rs delete mode 100644 src-tauri/src/analytics/algorithms/most_similar_timeline/mod.rs delete mode 100644 src-tauri/src/analytics/algorithms/most_similar_timeline/results.rs delete mode 100644 src-tauri/src/analytics/algorithms/predicted_state/mod.rs delete mode 100644 src-tauri/src/analytics/algorithms/predicted_state/results.rs delete mode 100644 src-tauri/src/analytics/algorithms/stoer_wagner/mod.rs delete mode 100644 src-tauri/src/analytics/algorithms/stoer_wagner/results.rs delete mode 100644 src-tauri/src/analytics/aux_functions/convert_to_graph_from_string.rs delete mode 100644 src-tauri/src/analytics/aux_functions/edge_factory.rs delete mode 100644 src-tauri/src/analytics/aux_functions/get_most_similar.rs delete mode 100644 src-tauri/src/analytics/aux_functions/mod.rs delete mode 100644 src-tauri/src/analytics/aux_functions/take_snapshot.rs delete mode 100644 src-tauri/src/analytics/data_structures/binary_heap.rs delete mode 100644 src-tauri/src/analytics/data_structures/cut.rs delete mode 100644 src-tauri/src/analytics/data_structures/mod.rs delete mode 100644 src-tauri/src/analytics/data_structures/neighbor_info.rs delete mode 100644 src-tauri/src/analytics/data_structures/stoer_wagner_ds.rs delete mode 100644 src-tauri/src/analytics/data_structures/timeline.rs delete mode 100644 src-tauri/src/analytics/data_structures/union_find.rs delete mode 100644 src-tauri/src/analytics/mod.rs delete mode 100644 src-tauri/src/analytics/state/configuration.rs delete mode 100644 src-tauri/src/analytics/state/controller.rs delete mode 100644 src-tauri/src/analytics/state/history.rs delete mode 100644 src-tauri/src/analytics/state/mod.rs delete mode 100644 src-tauri/src/analytics/state/store.rs delete mode 100644 src-tauri/src/constructors/init/init_edge_map.rs delete mode 100644 src-tauri/src/constructors/init/init_graph.rs delete mode 100644 src-tauri/src/constructors/init/init_node_map.rs delete mode 100644 src-tauri/src/constructors/init/mod.rs delete mode 100644 src-tauri/src/constructors/mock/mocks.rs delete mode 100644 src-tauri/src/constructors/mock/mod.rs delete mode 100644 src-tauri/src/constructors/mod.rs delete mode 100644 src-tauri/src/data_conversion/distance_constants.rs delete mode 100644 src-tauri/src/data_conversion/distance_conversion.rs delete mode 100644 src-tauri/src/data_conversion/mod.rs delete mode 100644 src-tauri/src/graph/edge.rs delete mode 100644 src-tauri/src/graph/graph_ds.rs delete mode 100644 src-tauri/src/graph/mod.rs delete mode 100644 src-tauri/src/graph/node.rs delete mode 100644 src-tauri/src/ipc/commands/graph.rs diff --git a/src-tauri/src/analytics/README.md b/src-tauri/src/analytics/README.md deleted file mode 100644 index d8ccd970..00000000 --- a/src-tauri/src/analytics/README.md +++ /dev/null @@ -1,125 +0,0 @@ -## **Analytics** - -Module for interacting with the network / graph. - -  - -### **State** - -The entry-point of this module is `State` struct in [state.rs](./state.rs). `State` accepts two parameters: `config_fields: HashMap<&str, &str>` and `is_save: bool` both of which are passed down to [Timeline](./aux_data_structures/timeline.rs). Former is a hashmap with two keys "timeline_data_dir" and "timeline_label_dir" representing the directory locations in which the data will be saved if `is_save` is true. - -  - -The overall structure of `State` is as below: - -```rust -pub struct State { - timeline: Timeline, - history: History, - time: SystemTime, - algo_configs: AlgoConfig, - algo_run_mode_auto: bool, - algo_store: AlgoStore, - algo_controller: AlgoController, -}; -``` - -All fields of `State` are initialized as empty. When the backend centralizes the network information and creates a `Graph` object, it passes it to `State` as: - -```rust -state.add_graph(&graph); -``` - -which the `state` adds to `state.timeline`. - -Since the choice of algorithm to run is left to the user, frontend will send a bitfield of type `u8` to `state` to activate algorithms as: - -```rust -state.set_algos(algos_bitfield: u8); -``` - -`algos_bitfield` can be constructed in the following way: - -- To run the articulation point algorithm, the bitfield is 0b00001. -- To run the mincut algorithm, the bitfield is 0b00010. -- To run the diffusion centrality algorithm, the bitfield is 0b00100. -- To run the most similar timeline algorithm, the bitfield is 0b01000. -- To run the predicted state algorithm, the bitfield is 0b10000. -- To run all algorithms, the bitfield is 0b11111. - -All other combinations can be derived from above. - -  - -Running **'activated'** algorithms is as simple as calling: - -```rust -state.run_algos(); -``` - -In this function, `State` gets the current snapshot from `Timeline` and calls `AlgoController` as: - -```rust -self.algo_controller.run_algos( - &curr_graph, - &self.algo_configs, - &mut self.history, - &mut self.algo_store, -); -``` - -which itself calls the activated algorithms, stores their results as `Enum` in `AlgoStore` and logs the time in `History` object for each of the algorithms. - -To retrieve algorithm results: - -```rust -state.get_algo_results(); -let ap_algo_res = algo_results.get_aps(); -match ap_algo_res { - APResult::Success(aps) => { - println!("AP algorithm returned: {:?}", aps); - assert_eq!(aps.len(), 0); - } - APResult::Error(err_str) => { - panic!("Error running AP algorithm: {}", err_str); - } - APResult::Empty(b) => { - panic!("AP algorithm returned empty result: {}", b); - } -} -``` - -  - -### **AlgoStore** - -This `Struct` saves the result of activated algorithms represented by their respective `Enum`s. It should be noted that the `AlgoStore` saves the latest results and not all of them. So if an algorithm is run 5 times, `AlgoStore` will only save the last one. - -  - -### **AlgoController** - -This `Struct` doesn't have any field but contains a group of functions that run algorithms and save and log their results. Only `State` interacts with `AlgoController` and no other module should interact with it. - -  - -### **History** - -For each algorithm `History` stores the time and elapsed time since prior time each algorithm was run. - -  - -### **Enums** - -For each algorithm, we implement an `Enum` that can take on any one of the three values: Success: Any, Error: String, Empty: bool. For example, for articulation point algorithm result, we have the following `Enum`: - -```rust -#[derive(Debug)] -pub enum APResult { - Success(Vec), - Error(String), - Empty(bool), -} -``` - -The type of `APResult::Success` is `Vec` because that's what articulation_point algorithm returns if there is no problem. The same concept applies to all other algorithm results. diff --git a/src-tauri/src/analytics/algorithms/articulation_point/mod.rs b/src-tauri/src/analytics/algorithms/articulation_point/mod.rs deleted file mode 100644 index c0c3af35..00000000 --- a/src-tauri/src/analytics/algorithms/articulation_point/mod.rs +++ /dev/null @@ -1,173 +0,0 @@ -#![allow(dead_code)] -#![allow(clippy::too_many_arguments)] - -pub mod results; - -use super::AlgorithmRunner; -use crate::graph::graph_ds::Graph; -use defaultdict::DefaultHashMap; -use results::APResult; -use std::cmp::min; - -pub struct ArticulationPointRunner; - -pub struct ArticulationPointParams; - -impl AlgorithmRunner for ArticulationPointRunner { - type Parameters = ArticulationPointParams; - type Result = APResult; - - fn new(_params: Self::Parameters) -> Self { - ArticulationPointRunner - } - - fn run(&mut self, graph: &crate::graph::graph_ds::Graph) -> Self::Result { - let mut disc = DefaultHashMap::::new(); - let mut low = DefaultHashMap::::new(); - let mut visited = DefaultHashMap::::new(); - let mut ap = DefaultHashMap::::new(); - let mut time = 0; - let parent = "-1".to_string(); - - for node in graph.get_nodes() { - if !visited.get(&node.name) { - articulation_point_helper( - graph, - node.name, - &mut visited, - &mut disc, - &mut low, - &mut time, - parent.clone(), - &mut ap, - ); - } - } - - let mut articulation_points = Vec::new(); - for key in ap.keys() { - if *ap.get(key) { - articulation_points.push(graph.get_node_idx(key)); - } - } - - APResult::Success(articulation_points) - } -} - -fn articulation_point_helper( - graph: &Graph, - node_idx: String, - visited: &mut DefaultHashMap, - disc: &mut DefaultHashMap, - low: &mut DefaultHashMap, - time: &mut usize, - parent: String, - ap: &mut DefaultHashMap, -) { - let mut children = 0; - visited.insert(node_idx.clone(), true); - - *time += 1; - disc.insert(node_idx.clone(), *time as i32); - low.insert(node_idx.clone(), *time as i32); - - let neighbors = graph.get_neighbors(node_idx.clone()); - for neighbor in neighbors { - if !visited.get(&neighbor.name) { - children += 1; - articulation_point_helper( - graph, - neighbor.name.clone(), - visited, - disc, - low, - time, - node_idx.clone(), - ap, - ); - low.insert( - node_idx.clone(), - min(*low.get(&node_idx), *low.get(&neighbor.name)), - ); - - if !parent.eq("-1") && low.get(&neighbor.name) >= disc.get(&node_idx) { - ap.insert(node_idx.clone(), true); - } - } else if !neighbor.name.eq(&parent) { - low.insert( - node_idx.clone(), - min(*low.get(&node_idx), *disc.get(&neighbor.name)), - ); - } - } - - if parent.eq("-1") && children > 1 { - ap.insert(node_idx, true); - } -} - -// Create a unit test for the Graph struct -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn graph_with_articulation_point() { - // Create a graph - let mut g = Graph::new(); - - // Add nodes - let u: String = "u".to_string(); - let v: String = "v".to_string(); - let w: String = "w".to_string(); - let x: String = "x".to_string(); - let y: String = "y".to_string(); - let z: String = "z".to_string(); - let a: String = "a".to_string(); - let b: String = "b".to_string(); - - g.add_node(u.clone()); - g.add_node(v.clone()); - g.add_node(w.clone()); - g.add_node(x.clone()); - g.add_node(y.clone()); - g.add_node(z.clone()); - g.add_node(a.clone()); - g.add_node(b.clone()); - - // Add edges - g.add_edge(u.clone(), v.clone(), 1.0); - g.add_edge(u.clone(), w.clone(), 1.0); - g.add_edge(u.clone(), x.clone(), 1.0); - g.add_edge(w.clone(), x.clone(), 1.0); - g.add_edge(v, y.clone(), 1.0); - g.add_edge(x.clone(), y.clone(), 1.0); - g.add_edge(w, z, 1.0); - g.add_edge(x.clone(), a, 1.0); - g.add_edge(y, b, 1.0); - - println!("\n"); - - // Test the articulation point function - let mut runner = ArticulationPointRunner::new(ArticulationPointParams); - let articulation_points_res = runner.run(&g); - - match articulation_points_res { - APResult::Success(aps) => { - let len_articulation_points = aps.len(); - assert_eq!(len_articulation_points, 3); - // Check if node u is an articulation point - assert!(!aps.contains(&g.get_node_idx(&u))); - assert!(aps.contains(&g.get_node_idx(&x))); - println!("Articulation Points: {:?}", aps); - } - APResult::Error(aps) => { - println!("Articulation Points: {:?}", aps); - } - APResult::Empty(b) => { - println!("This won't happen. {:?}", b); - } - } - } -} diff --git a/src-tauri/src/analytics/algorithms/articulation_point/results.rs b/src-tauri/src/analytics/algorithms/articulation_point/results.rs deleted file mode 100644 index d32ed8ae..00000000 --- a/src-tauri/src/analytics/algorithms/articulation_point/results.rs +++ /dev/null @@ -1,19 +0,0 @@ -use petgraph::graph::NodeIndex; -use serde::{Deserialize, Serialize}; - -#[derive(Debug, Serialize, Deserialize)] -pub enum APResult { - Success(Vec), - Error(String), - Empty(bool), -} - -impl Clone for APResult { - fn clone(&self) -> Self { - match self { - APResult::Success(aps) => APResult::Success(aps.clone()), - APResult::Error(err) => APResult::Error(err.clone()), - APResult::Empty(empty) => APResult::Empty(*empty), - } - } -} diff --git a/src-tauri/src/analytics/algorithms/diffusion_centrality/mod.rs b/src-tauri/src/analytics/algorithms/diffusion_centrality/mod.rs deleted file mode 100644 index bc88c3c8..00000000 --- a/src-tauri/src/analytics/algorithms/diffusion_centrality/mod.rs +++ /dev/null @@ -1,118 +0,0 @@ -use nalgebra::DMatrix; -use std::collections::HashMap; - -pub mod results; - -use results::DiffCenError; - -use self::results::{DiffCenResult, EigenvalsResult}; - -use super::AlgorithmRunner; - -pub struct DiffusionCentralityRunner { - params: DiffusionCentralityParams, -} - -pub struct DiffusionCentralityParams { - pub t_param: u32, -} - -impl AlgorithmRunner for DiffusionCentralityRunner { - type Parameters = DiffusionCentralityParams; - type Result = DiffCenResult; - - fn new(params: Self::Parameters) -> Self { - DiffusionCentralityRunner { params } - } - - /// Calculates the diffusion centrality of each node in the graph. - /// Diffusion centrality is a measure of how much information a node - /// can diffuse to other nodes in the graph. - fn run(&mut self, graph: &crate::graph::graph_ds::Graph) -> Self::Result { - let t_param = &self.params.t_param; - - let n = graph.get_order(); - let (_, int_to_node_id, adj_matrix) = graph.convert_to_adj_matrix(); - let adj_matrix = &adj_matrix; - - let eigenvals = match graph.eigenvals(adj_matrix) { - EigenvalsResult::Success(eigenvals_vec) => eigenvals_vec, - EigenvalsResult::Error(err) => { - return DiffCenResult::Error(DiffCenError::EigenvalueError(err)) - } - EigenvalsResult::Empty(b) => return DiffCenResult::Empty(b), - }; - - let mut node_to_diffcen = HashMap::new(); - - let largest_eigenvalue = eigenvals - .iter() - .max_by(|a, b| a.total_cmp(b)) - .unwrap_or(&1.0); - - let q = 1.0 / largest_eigenvalue; - - let identity_matrix = DMatrix::::identity(n, n); - - let mut h_matrix = DMatrix::zeros(n, n); - - for t in 1..t_param + 1 { - h_matrix += (q * adj_matrix).pow(t) * &identity_matrix; - - let mut node_to_diffcen_at_t = HashMap::new(); - - for (i, row) in h_matrix.row_iter().enumerate() { - let row_sum = row.sum(); - - // divide the row by the sum of the row - let row_normalized = row.map(|x| x / row_sum); - - let mut node_to_diffcen_inner = HashMap::new(); - for (j, col) in row_normalized.iter().enumerate() { - let node_id_j = match query_node_id(j, &int_to_node_id) { - Ok(id) => id, - Err(_err) => { - return DiffCenResult::Error(DiffCenError::NodeIdLookupError( - j.try_into().expect("Failure to convert usize into u32"), - )) - } - }; - - if i == j { - let sum = row.sum(); - node_to_diffcen_inner.insert(node_id_j, sum); - continue; - } - - node_to_diffcen_inner.insert(node_id_j, *col); - } - - let node_id_i = match query_node_id(i, &int_to_node_id) { - Ok(id) => id, - Err(_err) => { - return DiffCenResult::Error(DiffCenError::NodeIdLookupError( - i.try_into().expect("Failure to convert usize into u32"), - )) - } - }; - - node_to_diffcen_at_t.insert(node_id_i, node_to_diffcen_inner); - } - - node_to_diffcen.insert(t.to_string(), node_to_diffcen_at_t); - } - - DiffCenResult::Success(node_to_diffcen) - } -} - -fn query_node_id( - id: usize, - int_to_node_id: &HashMap, -) -> Result { - let id = int_to_node_id - .get(&id) - .ok_or(DiffCenError::NodeIdLookupError(id as u32))?; - - Ok(id.to_owned()) -} diff --git a/src-tauri/src/analytics/algorithms/diffusion_centrality/results.rs b/src-tauri/src/analytics/algorithms/diffusion_centrality/results.rs deleted file mode 100644 index 9ae452d2..00000000 --- a/src-tauri/src/analytics/algorithms/diffusion_centrality/results.rs +++ /dev/null @@ -1,28 +0,0 @@ -#![allow(dead_code)] - -use serde::{Deserialize, Serialize}; -use std::collections::HashMap; - -pub type DiffCenMap = HashMap>>; - -#[derive(Clone, Debug, Serialize, Deserialize)] -pub enum DiffCenError { - NodeIdLookupError(u32), - EigenvalueError(String), -} - -#[derive(Clone, Debug, Serialize, Deserialize)] -pub enum DiffCenResult { - Success(DiffCenMap), - Error(DiffCenError), - Empty(bool), -} - -pub type EigenvalsError = String; - -#[derive(Clone, Debug, Serialize, Deserialize)] -pub enum EigenvalsResult { - Success(Vec), - Error(EigenvalsError), - Empty(bool), -} diff --git a/src-tauri/src/analytics/algorithms/karger_stein/mod.rs b/src-tauri/src/analytics/algorithms/karger_stein/mod.rs deleted file mode 100644 index 79fd86a1..00000000 --- a/src-tauri/src/analytics/algorithms/karger_stein/mod.rs +++ /dev/null @@ -1,155 +0,0 @@ -#![allow(dead_code)] - -use crate::graph::edge::Edge; -use crate::graph::graph_ds::Graph; - -/// Locate the insertion point for x in a to maintain sorted order. -/// If x is already present in a, the insertion point will be before -/// (to the left of) any existing entries. -/// -/// The returned insertion point i partitions the array a into two -/// halves so that all(val < x for val in a[lo : i]) for the left -/// side and all(val >= x for val in a[i : hi]) for the right side. -/// -/// * Arguments -/// -/// * `a` - The sorted slice to search. -/// * `x` - The value to search for. -pub fn bisect_left(a: &[T], x: T) -> usize { - let mut lo = 0; - let mut hi = a.len(); - - while lo < hi { - let mid = (lo + hi) / 2; - if a[mid] < x { - lo = mid + 1; - } else { - hi = mid; - } - } - lo -} - -/// Returns the edge picked randomly proportional to the weight of the edge -/// -/// * Arguments -/// -/// * `G` - The graph to pick the edge from -pub fn random_select(g: &Graph) -> Edge { - // Calculate the cumulative edge weights - let cumulative_edge_weights = g.get_cumulative_edge_weights(); - let total_weight = cumulative_edge_weights[cumulative_edge_weights.len() - 1]; - - let r = rand::random::() * total_weight; - let i = bisect_left(&cumulative_edge_weights, r); - let edges = g.get_edges(); - - edges[i].clone() -} - -pub fn contract(g: &Graph, k: usize) -> Graph { - let mut g = g.clone(); - - while g.get_order() > k { - let e = random_select(&g); - - let start_idx = e.u; - let finish_idx = e.v; - - let start = g - .get_node(start_idx) - .expect("Index from graph should exist"); - - let finish = g - .get_node(finish_idx) - .expect("Index from graph should exist"); - - for node in g.get_neighbors(finish.name.clone()) { - if !node.name.eq(&start.name) { - let weight = g.get_edge_weight(&finish.name, &node.name, None, Some(true)); - g.add_edge(start.name.clone(), node.name.clone(), weight); - } - } - g.remove_node(finish_idx); - } - - g.clone() -} - -pub fn karger_stein_gmincut(g: &Graph, n: i32) -> f64 { - if n <= 6 { - let g_prime = contract(g, 2); - let mut cut_sum = 0.0; - for edge in g_prime.get_edges() { - cut_sum += edge.weight; - } - - return cut_sum; - } - - let n = (n as f64 / 2.0f64.sqrt()) as i32 + 1; - let g_prime_1 = contract(g, n as usize); - let g_prime_1_min_cut = karger_stein_gmincut(&g_prime_1, n); - - let g_prime_2 = contract(g, n as usize); - let g_prime_2_min_cut = karger_stein_gmincut(&g_prime_2, n); - - if g_prime_1_min_cut > g_prime_2_min_cut { - return g_prime_2_min_cut; - } - - g_prime_1_min_cut -} - -// Create a unit test for the Graph struct -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn run_globalmincut() { - // Create a graph - let mut g = Graph::new(); - - // Add nodes - let u = "u".to_string(); - let v = "v".to_string(); - let w = "w".to_string(); - let x = "x".to_string(); - let y = "y".to_string(); - let z = "z".to_string(); - let a = "a".to_string(); - let b = "b".to_string(); - - g.add_node(u.clone()); - g.add_node(v.clone()); - g.add_node(w.clone()); - g.add_node(x.clone()); - g.add_node(y.clone()); - g.add_node(z.clone()); - g.add_node(a.clone()); - g.add_node(b.clone()); - - // Add edges - g.add_edge(u.clone(), v.clone(), 1.0); - g.add_edge(u.clone(), w.clone(), 1.0); - g.add_edge(u, x.clone(), 1.0); - g.add_edge(w.clone(), x.clone(), 1.0); - g.add_edge(v, y.clone(), 1.0); - g.add_edge(x.clone(), y.clone(), 1.0); - g.add_edge(w, z, 1.0); - g.add_edge(x, a, 1.0); - g.add_edge(y, b, 1.0); - - let mut mincut_2 = 0; - for _ in 0..1000 { - let order = g.get_order(); - let mincut = karger_stein_gmincut(&g.clone(), order as i32); - if mincut == 1.0 { - mincut_2 += 1; - } - } - - println!("{} out of 1000", mincut_2); - } -} diff --git a/src-tauri/src/analytics/algorithms/mod.rs b/src-tauri/src/analytics/algorithms/mod.rs deleted file mode 100644 index df88e3cd..00000000 --- a/src-tauri/src/analytics/algorithms/mod.rs +++ /dev/null @@ -1,16 +0,0 @@ -use crate::graph::graph_ds::Graph; - -pub mod articulation_point; -pub mod diffusion_centrality; -pub mod karger_stein; -pub mod most_similar_timeline; -pub mod predicted_state; -pub mod stoer_wagner; - -pub trait AlgorithmRunner { - type Parameters; - type Result; - - fn new(params: Self::Parameters) -> Self; - fn run(&mut self, graph: &Graph) -> Self::Result; -} diff --git a/src-tauri/src/analytics/algorithms/most_similar_timeline/mod.rs b/src-tauri/src/analytics/algorithms/most_similar_timeline/mod.rs deleted file mode 100644 index d8a1cc53..00000000 --- a/src-tauri/src/analytics/algorithms/most_similar_timeline/mod.rs +++ /dev/null @@ -1 +0,0 @@ -pub mod results; diff --git a/src-tauri/src/analytics/algorithms/most_similar_timeline/results.rs b/src-tauri/src/analytics/algorithms/most_similar_timeline/results.rs deleted file mode 100644 index 1c77d936..00000000 --- a/src-tauri/src/analytics/algorithms/most_similar_timeline/results.rs +++ /dev/null @@ -1,9 +0,0 @@ -use crate::graph::graph_ds::Graph; -use serde::{Deserialize, Serialize}; - -#[derive(Clone, Debug, Serialize, Deserialize)] -pub enum MostSimTResult { - Success(Graph), - Error(String), - Empty(bool), -} diff --git a/src-tauri/src/analytics/algorithms/predicted_state/mod.rs b/src-tauri/src/analytics/algorithms/predicted_state/mod.rs deleted file mode 100644 index d8a1cc53..00000000 --- a/src-tauri/src/analytics/algorithms/predicted_state/mod.rs +++ /dev/null @@ -1 +0,0 @@ -pub mod results; diff --git a/src-tauri/src/analytics/algorithms/predicted_state/results.rs b/src-tauri/src/analytics/algorithms/predicted_state/results.rs deleted file mode 100644 index 018bc785..00000000 --- a/src-tauri/src/analytics/algorithms/predicted_state/results.rs +++ /dev/null @@ -1,9 +0,0 @@ -use crate::graph::graph_ds::Graph; -use serde::{Deserialize, Serialize}; - -#[derive(Clone, Debug, Serialize, Deserialize)] -pub enum PredStateResult { - Success(Graph), - Error(String), - Empty(bool), -} diff --git a/src-tauri/src/analytics/algorithms/stoer_wagner/mod.rs b/src-tauri/src/analytics/algorithms/stoer_wagner/mod.rs deleted file mode 100644 index 53f2f505..00000000 --- a/src-tauri/src/analytics/algorithms/stoer_wagner/mod.rs +++ /dev/null @@ -1,277 +0,0 @@ -#![allow(dead_code)] - -pub mod results; - -use self::results::MinCutResult; - -use super::{ - super::data_structures::{ - binary_heap::BinaryHeap, cut::Cut, stoer_wagner_ds::StoerWagnerGraph, - }, - AlgorithmRunner, -}; -use results::SWCutResult; -use std::collections::HashMap; - -pub struct GlobalMinCutRunner; - -pub struct GlobalMinCutParams; - -impl AlgorithmRunner for GlobalMinCutRunner { - type Parameters = GlobalMinCutParams; - type Result = MinCutResult; - - fn new(_params: Self::Parameters) -> Self { - GlobalMinCutRunner - } - - fn run(&mut self, graph: &crate::graph::graph_ds::Graph) -> Self::Result { - let sw_graph = &mut StoerWagnerGraph::new(graph.clone()); - - match stoer_wagner(sw_graph) { - SWCutResult::Success(_) => {} - SWCutResult::Error(er_str) => return MinCutResult::Error(er_str), - SWCutResult::Empty(b) => return MinCutResult::Empty(b), - } - - let node_names = graph.get_nodes().iter().map(|n| n.name.clone()).collect(); - recover_mincut(sw_graph, node_names) - } -} - -pub fn st_mincut(g: &mut StoerWagnerGraph) -> SWCutResult { - let mut bheap = BinaryHeap::new(g); - - let uncontracted = g.uncontracted.clone(); - let mut a = Vec::new(); - - while a.len() < uncontracted.clone().len() { - let max = bheap.extract_max().unwrap(); - a.push(max.clone()); - - for node_id in g.uncontracted.clone() { - let w = g - .graph - .get_edge_weight(&node_id, &max.node.name, None, None); - bheap.update(node_id.clone(), w); - } - bheap.build_heap(); - } - if a.len() < 2 { - return SWCutResult::Error("Error in st_mincut".to_string()); - } - let weight = a[a.len() - 1].weight; - - let s = a[a.len() - 1].node.name.clone(); - let t = a[a.len() - 2].node.name.clone(); - - SWCutResult::Success(Cut::new(weight, s, t)) -} - -pub fn stoer_wagner(graph: &mut StoerWagnerGraph) -> SWCutResult { - if graph.uncontracted.len() == 2 { - return SWCutResult::Success(graph.get_cut()); - } - - let opt_cut = st_mincut(graph); - match opt_cut { - SWCutResult::Success(cut) => { - graph.contract_edge(cut.get_a(), cut.get_b()); - let opt_other_cut = stoer_wagner(graph); - match opt_other_cut { - SWCutResult::Success(other_cut) => { - if cut.get_weight() < other_cut.get_weight() { - return SWCutResult::Success(cut); - } - - SWCutResult::Success(other_cut) - } - SWCutResult::Error(other_err_str) => SWCutResult::Error(other_err_str), - SWCutResult::Empty(other_empty) => SWCutResult::Empty(other_empty), - } - } - SWCutResult::Error(err_str) => SWCutResult::Error(err_str), - SWCutResult::Empty(empty) => SWCutResult::Empty(empty), - } -} - -pub fn recover_mincut(graph: &mut StoerWagnerGraph, all_nodes: Vec) -> MinCutResult { - let mut parent_map = HashMap::new(); - - for node in all_nodes { - let parent = graph.uf.find(&node); - let children: &mut Vec = parent_map.entry(parent.clone()).or_default(); - children.push(node.clone()); - } - - // get the two keys in parent_map - let keys = Vec::from_iter(parent_map.keys()); - let s = keys[0]; - let t = keys[1]; - - let s_cut = parent_map.get(s).unwrap().clone(); - let t_cut = parent_map.get(t).unwrap().clone(); - - let mut st_cut_edges = Vec::new(); - - for node_s in s_cut { - for node_t in &t_cut { - let edge = graph.graph.get_edge(&node_s, node_t); - - if let Some(e) = edge { - st_cut_edges.push(e.clone()); - } - } - } - - MinCutResult::Success(st_cut_edges) -} - -// Create a unit test for the stoer-wagner algorithm -#[cfg(test)] -mod tests { - use crate::graph::graph_ds::Graph; - - use super::*; - - #[test] - fn test_stoer_wagner() { - // Create a graph - let mut g = Graph::new(); - - // Add nodes - let u: String = "u".to_string(); - let v: String = "v".to_string(); - let w: String = "w".to_string(); - let x: String = "x".to_string(); - let y: String = "y".to_string(); - let z: String = "z".to_string(); - let a: String = "a".to_string(); - let b: String = "b".to_string(); - - g.add_node(u.clone()); - g.add_node(v.clone()); - g.add_node(w.clone()); - g.add_node(x.clone()); - g.add_node(y.clone()); - g.add_node(z.clone()); - g.add_node(a.clone()); - g.add_node(b.clone()); - - // Add edges - g.add_edge(u.clone(), v.clone(), 1.0); - g.add_edge(u.clone(), w.clone(), 1.0); - g.add_edge(u.clone(), x.clone(), 1.0); - g.add_edge(w.clone(), x.clone(), 1.0); - g.add_edge(v.clone(), y.clone(), 1.0); - g.add_edge(x.clone(), y.clone(), 1.0); - g.add_edge(w.clone(), z.clone(), 1.0); - g.add_edge(x.clone(), a.clone(), 1.0); - g.add_edge(y.clone(), b.clone(), 1.0); - - let mut correct_mincut_weight_count = 0; - - let graph_sw = &mut StoerWagnerGraph::new(g.clone()); - - let mincut = stoer_wagner(graph_sw); - - match mincut { - SWCutResult::Success(cut) => { - assert_eq!(cut.get_weight(), 1.0); - let nodes = vec![u, v, w, x, y, z, a, b]; - let mincut_edges_res = recover_mincut(graph_sw, nodes); - - match mincut_edges_res { - MinCutResult::Success(mincut_edges) => { - assert_eq!(mincut_edges.len(), 1); - } - MinCutResult::Error(err_str) => { - panic!("Failed to find mincut from SWCut: {}", err_str); - } - MinCutResult::Empty(empty) => { - panic!("Empty MinCut result: {}", empty); - } - } - } - SWCutResult::Error(err_str) => { - panic!("Failed to find SWCut: {}", err_str); - } - SWCutResult::Empty(empty) => { - panic!("Empty SWCut result: {}", empty); - } - } - - for _ in 0..100 { - let graph_sw = &mut StoerWagnerGraph::new(g.clone()); - - let mincut_res = stoer_wagner(graph_sw); - match mincut_res { - SWCutResult::Success(cut) => { - if cut.get_weight() == 1.0 { - correct_mincut_weight_count += 1; - } - } - SWCutResult::Error(err_str) => { - panic!("Error: {}", err_str); - } - SWCutResult::Empty(empty) => { - panic!("Empty: {}", empty); - } - } - } - - assert_eq!(correct_mincut_weight_count, 100); - } - - #[test] - fn test_mincut_runner() { - // Create a graph - let mut g = Graph::new(); - - // Add nodes - let u: String = "u".to_string(); - let v: String = "v".to_string(); - let w: String = "w".to_string(); - let x: String = "x".to_string(); - let y: String = "y".to_string(); - let z: String = "z".to_string(); - let a: String = "a".to_string(); - let b: String = "b".to_string(); - - g.add_node(u.clone()); - g.add_node(v.clone()); - g.add_node(w.clone()); - g.add_node(x.clone()); - g.add_node(y.clone()); - g.add_node(z.clone()); - g.add_node(a.clone()); - g.add_node(b.clone()); - - // Add edges - g.add_edge(u.clone(), v.clone(), 1.0); - g.add_edge(u.clone(), w.clone(), 1.0); - g.add_edge(u, x.clone(), 1.0); - g.add_edge(w.clone(), x.clone(), 1.0); - g.add_edge(v, y.clone(), 1.0); - g.add_edge(x.clone(), y.clone(), 1.0); - g.add_edge(w, z, 1.0); - g.add_edge(x, a, 1.0); - g.add_edge(y, b, 1.0); - - let params = GlobalMinCutParams; - let mut runner = GlobalMinCutRunner::new(params); - let result = runner.run(&g); - - match result { - MinCutResult::Success(mincut_edges) => { - assert_eq!(mincut_edges.len(), 1); - } - MinCutResult::Error(err_str) => { - panic!("Runner failed to find minimum cut: {}", err_str); - } - MinCutResult::Empty(empty) => { - panic!("Runner returned empty cut: {}", empty); - } - } - } -} diff --git a/src-tauri/src/analytics/algorithms/stoer_wagner/results.rs b/src-tauri/src/analytics/algorithms/stoer_wagner/results.rs deleted file mode 100644 index 7e38f559..00000000 --- a/src-tauri/src/analytics/algorithms/stoer_wagner/results.rs +++ /dev/null @@ -1,19 +0,0 @@ -#![allow(dead_code)] - -use crate::analytics::data_structures::cut::Cut; -use crate::graph::edge::Edge; -use serde::{Deserialize, Serialize}; - -#[derive(Clone, Debug, Serialize, Deserialize)] -pub enum SWCutResult { - Success(Cut), - Error(String), - Empty(bool), -} - -#[derive(Clone, Debug, Serialize, Deserialize)] -pub enum MinCutResult { - Success(Vec), - Error(String), - Empty(bool), -} diff --git a/src-tauri/src/analytics/aux_functions/convert_to_graph_from_string.rs b/src-tauri/src/analytics/aux_functions/convert_to_graph_from_string.rs deleted file mode 100644 index 4205831f..00000000 --- a/src-tauri/src/analytics/aux_functions/convert_to_graph_from_string.rs +++ /dev/null @@ -1,42 +0,0 @@ -#![allow(dead_code)] - -use crate::graph::{edge::Edge, graph_ds::Graph, node::Node}; - -pub fn convert_to_graph(graph_string: Vec<&str>) -> Graph { - let mut graph = Graph::new(); - - let mut nodes: Vec = Vec::new(); - let mut edges: Vec = Vec::new(); - - for line in graph_string { - if line.starts_with("O:") { - let node = line.split(' ').collect::>()[1]; - nodes.push(node.to_string()); - } else if line.starts_with("E:") { - let edge = line.split(' ').collect::>()[1..].join(" "); - edges.push(edge); - } - } - - // add nodes - for node in nodes { - let node_struct = Node::new(node); - graph.add_node_from_struct(node_struct); - } - - // add edges - for edge in edges { - let edge_split = edge.split(' ').collect::>(); - let node1 = edge_split[0]; - let node2 = edge_split[1]; - let weight = edge_split[2].parse::().unwrap(); - - let node1_idx = graph.get_node_idx(&node1.to_string()); - let node2_idx = graph.get_node_idx(&node2.to_string()); - - let edge_struct = Edge::new(node1_idx, node2_idx, weight); - graph.add_edge_from_struct(edge_struct); - } - - graph -} diff --git a/src-tauri/src/analytics/aux_functions/edge_factory.rs b/src-tauri/src/analytics/aux_functions/edge_factory.rs deleted file mode 100644 index f68fad13..00000000 --- a/src-tauri/src/analytics/aux_functions/edge_factory.rs +++ /dev/null @@ -1,128 +0,0 @@ -use crate::graph::edge::Edge; -use petgraph::graph::NodeIndex; - -/// Returns a list of edges -/// -/// # Arguments -/// -/// * `a` - List of first endpoints of edges -/// * `b` - List of second endpoints of edges -/// * `distances` - List of distances between nodes -/// * `radio_s_quality` - List of radio signal qualities between nodes -/// * `distance_weight` - Weight of distance in edge weight -/// * `radio_s_quality_weight` - Weight of radio signal quality in edge weight -/// -/// a, b, distance and radio_s_quality must have the same length and provide -/// one-to-one mapping. -pub fn edge_factory( - a: Vec, - b: Vec, - distance: Vec, - radio_s_quality: Vec, - distance_weight: Option, - radio_s_quality_weight: Option, -) -> Vec { - let mut scaled_distances: Vec = Vec::new(); - - for dist in distance { - let scaled_dist = scale_distance(dist); - scaled_distances.push(scaled_dist); - } - - let distance_min = scaled_distances - .iter() - .fold(f64::INFINITY, |acc, &x| acc.min(x)); - let distance_max = scaled_distances - .iter() - .fold(f64::NEG_INFINITY, |acc, &x| acc.max(x)); - - let radio_s_quality_min = radio_s_quality - .iter() - .fold(f64::INFINITY, |acc, &x| acc.min(x)); - let radio_s_quality_max = radio_s_quality - .iter() - .fold(f64::NEG_INFINITY, |acc, &x| acc.max(x)); - - // create a vector of edges - let mut edges: Vec = Vec::new(); - - // unwrap the weights - let distance_weight = distance_weight.unwrap_or(0.5); - let radio_s_quality_weight = radio_s_quality_weight.unwrap_or(0.5); - - for i in 0..a.len() { - let a = a[i]; - let b = b[i]; - let distance = scaled_distances[i]; - let radio_s_quality = radio_s_quality[i]; - - // normalize the values - let mut weight = normalize_weight( - distance, - (distance_min, distance_max), - distance_weight, - radio_s_quality, - (radio_s_quality_min, radio_s_quality_max), - radio_s_quality_weight, - ); - - if weight.is_nan() { - weight = 0.0; - } - - let edge = Edge::new(a, b, weight); - - edges.push(edge); - } - - edges -} - -pub fn scale_distance(distance: f64) -> f64 { - 1.0 / (1.0 + distance + 2.0).ln() -} - -pub fn normalize_weight( - distance: f64, - distance_minmax: (f64, f64), - distance_weight: f64, - radio_s_quality: f64, - radio_s_quality_minmax: (f64, f64), - radio_s_quality_weight: f64, -) -> f64 { - let e = f64::EPSILON; - - let distance_norm = - (distance - distance_minmax.0 + e) / (distance_minmax.1 - distance_minmax.0 + e); - let radio_s_quality_norm = (radio_s_quality - radio_s_quality_minmax.0 + e) - / (radio_s_quality_minmax.1 + e - radio_s_quality_minmax.0); - - // Return weight - (distance_weight * distance_norm) + (radio_s_quality_weight * radio_s_quality_norm) -} - -// Create a unit test for the Graph struct -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_edge_factory() { - let u = NodeIndex::new(0); - let v = NodeIndex::new(1); - let w = NodeIndex::new(2); - let x = NodeIndex::new(3); - - let a = vec![u, u, w, v]; - let b = vec![v, w, x, x]; - - let distance = vec![0.45, 0.67, 0.23, 1.2]; - let radio_s_quality = vec![5.5, 3.12, 10.3, 2.7]; - - let edges = edge_factory(a, b, distance, radio_s_quality, None, None); - - for edge in edges { - assert!(edge.weight >= 0.0 && edge.weight <= 1.1); - } - } -} diff --git a/src-tauri/src/analytics/aux_functions/get_most_similar.rs b/src-tauri/src/analytics/aux_functions/get_most_similar.rs deleted file mode 100644 index c4bf13d8..00000000 --- a/src-tauri/src/analytics/aux_functions/get_most_similar.rs +++ /dev/null @@ -1,161 +0,0 @@ -#![allow(dead_code)] - -use super::convert_to_graph_from_string::convert_to_graph; -use super::take_snapshot::take_snapshot_of_graph; -use crate::graph::graph_ds::Graph; - -use reqwest::Client; -use serde_json::json; - -pub async fn request_most_similar(graphs: Vec<&Graph>) -> Result { - let client = Client::new(); - - let mut snapshots = "".to_owned(); - for graph in graphs { - let graph_string = take_snapshot_of_graph(graph); - snapshots.push_str(&graph_string); - snapshots.push_str("\n\n"); - } - - let snapshots_str: &str = &snapshots[..]; - - let graph_json = json!({ - "snapshots": snapshots_str, - }); - - let res = client - .post("http://127.0.0.1:5000/get_next_state") - .json(&graph_json) - .send() - .await; - - if let Ok(res_json) = res { - let graph_json = res_json.json::().await; - - if let Ok(g_json) = graph_json { - let next_statesnapshot = g_json["snapshot"].to_string(); - - let mut next_state_lines: Vec<&str> = next_statesnapshot.split('-').collect(); - - let size = next_state_lines.len(); - next_state_lines[0] = &next_state_lines[0][1..]; // remove the first character - next_state_lines[size - 1] = &next_state_lines[next_state_lines.len() - 1] - [..next_state_lines[next_state_lines.len() - 1].len() - 1]; // remove the last character - - let graph = convert_to_graph(next_state_lines.clone()); - let graph_string = graph.to_string(); - - println!("Next Graph State: \n{}", graph_string); - Ok(graph) - } else { - Err("Error: could not convert graph_json to a string".into()) - } - } else { - Err("Error: could not get response from server".into()) - } -} - -/* // test the function -#[cfg(test)] -mod tests { - use super::*; - use crate::graph::edge::Edge; - use crate::graph::node::Node; - - #[test] - fn test_api_call() { - // Create a graph - let mut g = Graph::new(); - - // Create a few nodes and edges and add to graph - let a = "a".to_string(); - let b = "b".to_string(); - let c = "c".to_string(); - let d = "d".to_string(); - - let mut a_node = Node::new(a.clone()); - a_node.set_gps(-72.28486, 43.71489, 1.0); - let a_idx = g.add_node_from_struct(a_node); - - let mut b_node = Node::new(b.clone()); - b_node.set_gps(-72.28239, 43.71584, 1.0); - let b_idx = g.add_node_from_struct(b_node); - - let mut c_node = Node::new(c.clone()); - c_node.set_gps(-72.28332, 43.7114, 1.0); - let c_idx = g.add_node_from_struct(c_node); - - let mut d_node = Node::new(d.clone()); - d_node.set_gps(-72.28085, 43.71235, 1.0); - let d_idx = g.add_node_from_struct(d_node); - - // 0: a, 1: b, 2: c, 3: d - let a_b = Edge::new(a_idx, b_idx, 0.51); - g.add_edge_from_struct(a_b); - - let a_c = Edge::new(a_idx, c_idx, 0.39); - g.add_edge_from_struct(a_c); - - let b_c = Edge::new(b_idx, c_idx, 0.4); - g.add_edge_from_struct(b_c); - - let b_d = Edge::new(b_idx, d_idx, 0.6); - g.add_edge_from_struct(b_d); - - let mut g_2 = Graph::new(); - - // Create a few nodes and edges and add to graph - let a = "a".to_string(); - let b = "b".to_string(); - let c = "c".to_string(); - let d = "d".to_string(); - - let mut a_node = Node::new(a.clone()); - a_node.set_gps(-72.28239, 43.71489, 1.0); - let a_idx = g_2.add_node_from_struct(a_node); - - let mut b_node = Node::new(b.clone()); - b_node.set_gps(-72.28486, 43.71584, 1.0); - let b_idx = g_2.add_node_from_struct(b_node); - - let mut c_node = Node::new(c.clone()); - c_node.set_gps(-72.28332, 43.7114, 1.0); - let c_idx = g_2.add_node_from_struct(c_node); - - let mut d_node = Node::new(d.clone()); - d_node.set_gps(-72.28085, 43.71235, 1.0); - let d_idx = g_2.add_node_from_struct(d_node); - - // 0: a, 1: b, 2: c, 3: d - let a_b = Edge::new(a_idx, b_idx, 0.6); - g_2.add_edge_from_struct(a_b); - - let a_c = Edge::new(a_idx, c_idx, 0.33); - g_2.add_edge_from_struct(a_c); - - let b_d = Edge::new(b_idx, d_idx, 0.65); - g_2.add_edge_from_struct(b_d); - - let b_c = Edge::new(b_idx, c_idx, 0.11); - g_2.add_edge_from_struct(b_c); - - let graphs = vec![&g, &g_2]; - - let rt = tokio::runtime::Runtime::new().unwrap(); - - // call get_most_similar and await the result - - let res = rt.block_on(get_most_similar(graphs)); - - // handle res - match res { - Ok(graph) => { - println!("Graph {:?}", graph); - } - Err(e) => { - println!("error: {}", e); - } - } - } -} - */ diff --git a/src-tauri/src/analytics/aux_functions/mod.rs b/src-tauri/src/analytics/aux_functions/mod.rs deleted file mode 100644 index 900a4231..00000000 --- a/src-tauri/src/analytics/aux_functions/mod.rs +++ /dev/null @@ -1,4 +0,0 @@ -pub mod convert_to_graph_from_string; -pub mod edge_factory; -pub mod get_most_similar; -pub mod take_snapshot; diff --git a/src-tauri/src/analytics/aux_functions/take_snapshot.rs b/src-tauri/src/analytics/aux_functions/take_snapshot.rs deleted file mode 100644 index 590ff714..00000000 --- a/src-tauri/src/analytics/aux_functions/take_snapshot.rs +++ /dev/null @@ -1,114 +0,0 @@ -use std::collections::HashMap; - -use crate::data_conversion::distance_conversion::total_distance; -use crate::graph::graph_ds::Graph; - -fn save_relative_ordering(graph: &Graph, graph_text: &mut String) { - let mut nodes = graph.get_nodes(); - let node_fts = take_snapshot_of_node_fts(graph); - - let smallest_longitude_node = nodes.iter().fold(nodes[0].clone(), |acc, node| { - if node.longitude < acc.longitude { - node.clone() - } else { - acc - } - }); - - // sort nodes by their haversine distance to the smallest longitude node - nodes.sort_by(|a, b| { - let a_distance = total_distance( - smallest_longitude_node.latitude, - smallest_longitude_node.longitude, - smallest_longitude_node.altitude, - a.latitude, - a.longitude, - a.altitude, - ); - let b_distance = total_distance( - smallest_longitude_node.latitude, - smallest_longitude_node.longitude, - smallest_longitude_node.altitude, - b.latitude, - b.longitude, - b.altitude, - ); - a_distance.partial_cmp(&b_distance).unwrap() - }); - - let order = graph.get_order(); - - graph_text.push_str(order.to_string().as_str()); - graph_text.push('\n'); - - for (idx, node) in nodes.into_iter().enumerate() { - graph_text.push_str("O: "); - graph_text.push_str(&node.name); - graph_text.push(' '); - graph_text.push_str(idx.to_string().as_str()); - graph_text.push(' '); - let node_fts_txt = node_fts.get(&node.name).unwrap(); - graph_text.push_str(node_fts_txt); - graph_text.push('\n'); - } -} - -/// Converts a graph to a string and returns it -/// -/// # Arguments -/// -/// * `graph` - the graph to convert -pub fn take_snapshot_of_graph(graph: &Graph) -> String { - let mut graph_string = "".to_owned(); - - save_relative_ordering(graph, &mut graph_string); - - for edge in graph.get_edges() { - graph_string.push_str("E: "); - let u_idx = edge.get_u(); - let node_u = graph.get_node(u_idx).expect("Index from edge should exist"); - graph_string.push_str(&node_u.name); - graph_string.push(' '); - - let b_idx = edge.get_v(); - let node_b = graph.get_node(b_idx).expect("Index from edge should exist"); - graph_string.push_str(&node_b.name); - graph_string.push(' '); - - let weight = edge.get_weight(); - graph_string.push_str(&weight.to_string()); - graph_string.push('\n'); - } - - graph_string.pop(); - graph_string -} - -pub fn take_snapshot_of_node_fts(graph: &Graph) -> HashMap { - let mut node_fts: HashMap = HashMap::new(); - - for node in graph.get_nodes() { - let mut node_ft_txt: String = "".to_owned(); - node_ft_txt.push_str("lat "); - node_ft_txt.push_str(&node.latitude.to_string()); - - node_ft_txt.push_str(" lon "); - node_ft_txt.push_str(&node.longitude.to_string()); - - node_ft_txt.push_str(" alt "); - node_ft_txt.push_str(&node.altitude.to_string()); - - node_ft_txt.push_str(" v "); - node_ft_txt.push_str(&node.speed.to_string()); - - node_ft_txt.push_str(" th "); - node_ft_txt.push_str(&node.direction.to_string()); - - node_ft_txt.push_str(" deg "); - node_ft_txt.push_str(&node.optimal_weighted_degree.to_string()); - - node_fts.insert(node.name.clone(), node_ft_txt); - } - - node_fts -} diff --git a/src-tauri/src/analytics/data_structures/binary_heap.rs b/src-tauri/src/analytics/data_structures/binary_heap.rs deleted file mode 100644 index ac16f125..00000000 --- a/src-tauri/src/analytics/data_structures/binary_heap.rs +++ /dev/null @@ -1,164 +0,0 @@ -#![allow(dead_code)] -#![allow(non_snake_case)] - -use std::collections::HashMap; - -use crate::graph::node::Node; - -use super::stoer_wagner_ds::StoerWagnerGraph; - -#[derive(Clone, Debug)] -pub struct Vertex { - pub node: Node, - pub weight: f64, -} - -impl Vertex { - pub fn new(node: Node, weight: f64) -> Vertex { - Vertex { node, weight } - } - - pub fn clone(&self) -> Vertex { - Vertex { - node: self.node.clone(), - weight: self.weight, - } - } -} - -#[derive(Clone, Debug)] -pub struct BinaryHeap { - heap: Vec, - size: i32, - node_to_idx: HashMap, -} - -impl BinaryHeap { - pub fn new(G: &StoerWagnerGraph) -> BinaryHeap { - let mut bheap = BinaryHeap { - heap: Vec::new(), - size: 0, - node_to_idx: HashMap::new(), - }; - - for node_id in G.uncontracted.clone() { - let node_idx = G.graph.get_node_idx(&node_id); - let node = G - .graph - .get_node(node_idx) - .expect("Index from graph should exist"); - - let vertex = Vertex { - node: node.clone(), - weight: 0.0, - }; - - bheap.heap.push(vertex); - bheap.node_to_idx.insert(node_id.clone(), bheap.size); - bheap.size += 1; - } - - bheap - } - - pub fn extract_max(&mut self) -> Option { - if self.size == 0 { - return None; - } - - let root = self.heap[0].clone(); - let leaf = self.heap[self.size as usize - 1].clone(); - self.node_to_idx.insert(root.node.name.clone(), -1); - self.node_to_idx.insert(leaf.node.name, 0); - self.heap[0] = self.heap[self.size as usize - 1].clone(); - self.size -= 1; - self.heapify(None); - - Some(root) - } - - pub fn left(&self, index: i32) -> Option { - if index * 2 >= self.size { - return None; - } - - Some(self.heap[index as usize * 2].weight) - } - - pub fn right(&self, index: i32) -> Option { - if index * 2 + 1 >= self.size { - return None; - } - - Some(self.heap[index as usize * 2 + 1].weight) - } - - pub fn swap(&mut self, ind1: i32, ind2: i32) { - let v1 = self.heap[ind1 as usize].clone(); - let v2 = self.heap[ind2 as usize].clone(); - let tmp = self.heap[ind1 as usize].clone(); - - self.heap[ind1 as usize] = self.heap[ind2 as usize].clone(); - self.heap[ind2 as usize] = tmp; - - self.node_to_idx.insert(v1.node.name, ind2); - self.node_to_idx.insert(v2.node.name, ind1); - } - - pub fn heapify(&mut self, root: Option) { - let mut children = Vec::>::new(); - - if let Some(left) = self.left(root.unwrap_or(0)) { - children.push(Some(left)); - } else { - children.push(None); - } - - if let Some(right) = self.right(root.unwrap_or(0)) { - children.push(Some(right)); - } else { - children.push(None); - } - - if children[0].is_none() { - return; - } - - let mut max = 2 * root.unwrap_or(0); - - if children[1].is_some() && children[1].unwrap() >= children[0].unwrap() { - max = 2 * root.unwrap_or(0) + 1; - } - - if self.heap[max as usize].weight > self.heap[root.unwrap_or(0) as usize].weight { - self.swap(max, root.unwrap_or(0)); - self.heapify(Some(max)); - } - } - - pub fn update(&mut self, node: String, weight: f64) { - let idx = self.node_to_idx.get(&node).unwrap(); - if *idx == -1 { - return; - } - - self.heap[*idx as usize].weight += weight; - } - - pub fn build_heap(&mut self) { - let start_idx = self.size / 2 - 1; - for i in (0..start_idx + 1).rev() { - self.heapify(Some(i)); - } - } - - // TODO this should be replaced with the `Display` trait - // pub fn print(&self) { - // for i in 0..self.size { - // println!( - // "Node: {}, Weight: {}", - // self.heap[i as usize].node.name, self.heap[i as usize].weight - // ); - // } - // } -} diff --git a/src-tauri/src/analytics/data_structures/cut.rs b/src-tauri/src/analytics/data_structures/cut.rs deleted file mode 100644 index 246f54ee..00000000 --- a/src-tauri/src/analytics/data_structures/cut.rs +++ /dev/null @@ -1,26 +0,0 @@ -use serde::{Deserialize, Serialize}; - -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct Cut { - weight: f64, - a: String, - b: String, -} - -impl Cut { - pub fn new(weight: f64, a: String, b: String) -> Cut { - Cut { weight, a, b } - } - - pub fn get_weight(&self) -> f64 { - self.weight - } - - pub fn get_a(&self) -> &String { - &self.a - } - - pub fn get_b(&self) -> &String { - &self.b - } -} diff --git a/src-tauri/src/analytics/data_structures/mod.rs b/src-tauri/src/analytics/data_structures/mod.rs deleted file mode 100644 index 483ecc80..00000000 --- a/src-tauri/src/analytics/data_structures/mod.rs +++ /dev/null @@ -1,6 +0,0 @@ -pub mod binary_heap; -pub mod cut; -pub mod neighbor_info; -pub mod stoer_wagner_ds; -pub mod timeline; -pub mod union_find; diff --git a/src-tauri/src/analytics/data_structures/neighbor_info.rs b/src-tauri/src/analytics/data_structures/neighbor_info.rs deleted file mode 100644 index 6920f5ed..00000000 --- a/src-tauri/src/analytics/data_structures/neighbor_info.rs +++ /dev/null @@ -1,26 +0,0 @@ -use serde::{Deserialize, Serialize}; -/* -* When we try to reconstruct graph topology, we need to know the neighbors of each node. -* To do that, each node maintains a record of past messages and senders (neighbors). -* This list is assembled in a NeighborInfo struct (or packet), and sent to the coordinator. -*/ - -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct Neighbor { - // ID of sender - pub id: u32, - // SNR (signal to noise ratio) of recieved message - pub snr: f64, - // Time message was recieved from the sender (neighbor) - pub timestamp: u64, -} - -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct NeighborInfo { - // ID of node (sender) - pub id: u32, - // Time NeighborInfo packet is sent to update coordinator - pub timestamp: u64, - // List of neighbors of the node and the SNR of their edges - pub neighbors: Vec, -} diff --git a/src-tauri/src/analytics/data_structures/stoer_wagner_ds.rs b/src-tauri/src/analytics/data_structures/stoer_wagner_ds.rs deleted file mode 100644 index 71be58bc..00000000 --- a/src-tauri/src/analytics/data_structures/stoer_wagner_ds.rs +++ /dev/null @@ -1,113 +0,0 @@ -#![allow(dead_code)] -#![allow(non_snake_case)] - -use super::super::data_structures::cut::Cut; -use super::super::data_structures::union_find::UnionFind; -use crate::graph::graph_ds::Graph; -use std::collections::HashSet; - -pub struct StoerWagnerGraph { - pub graph: Graph, - pub uf: UnionFind, - pub uncontracted: HashSet, -} - -impl StoerWagnerGraph { - pub fn new(g: Graph) -> StoerWagnerGraph { - let mut node_ids_list = Vec::new(); - for node_id in g.get_nodes() { - node_ids_list.push(node_id.name.clone()); - } - - let uf = UnionFind::new(node_ids_list.clone()); - let mut uncontracted = HashSet::new(); - for node in node_ids_list.clone() { - uncontracted.insert(node); - } - StoerWagnerGraph { - graph: g, - uf, - uncontracted, - } - } - - pub fn get_cut(&mut self) -> Cut { - let sets = Vec::from_iter(self.uf.get_sets()); - // if size of sets is not equal to 2, return error - if sets.len() != 2 { - panic!("Error: number of sets is not equal to 2"); - } - - let s = sets[0]; - let t = sets[1]; - - let t_idx = self.graph.get_node_idx(t); - let t_node = self - .graph - .get_node(t_idx) - .expect("Index from cut should exist"); - - let weight = t_node.optimal_weighted_degree; - - Cut::new(weight, s.to_string(), t.to_string()) - } - - pub fn contract_edge(&mut self, v1: &String, v2: &String) { - let start_idx = self.graph.get_node_idx(v1); - let finish_idx = self.graph.get_node_idx(v2); - - let mut start = self - .graph - .get_node(start_idx) - .expect("Index from edge should exist"); - - let finish = self - .graph - .get_node(finish_idx) - .expect("Index from edge should exist"); - - self.uf.union(v1, v2); - - for mut node in self.graph.get_neighbors(finish.name.clone()) { - if !node.name.eq(&start.name) { - let weight = self - .graph - .get_edge_weight(&finish.name, &node.name, None, None) - + self - .graph - .get_edge_weight(&start.name, &node.name, None, None); - - self.graph - .update_edge(start.name.clone(), node.name.clone(), weight, None, None); - - start.optimal_weighted_degree += - self.graph - .get_edge_weight(&finish.name, &node.name, None, None); - node.optimal_weighted_degree += - self.graph - .get_edge_weight(&finish.name, &node.name, None, None); - } - } - - self.uncontracted.remove(v2); - self.graph.remove_node(finish_idx); - } - - pub fn clone(&self) -> StoerWagnerGraph { - let mut node_ids_list = Vec::new(); - for node_id in self.graph.get_nodes() { - node_ids_list.push(node_id.name.clone()); - } - - let uf = UnionFind::new(node_ids_list.clone()); - let mut uncontracted = HashSet::new(); - for node in node_ids_list.clone() { - uncontracted.insert(node); - } - StoerWagnerGraph { - graph: self.graph.clone(), - uf, - uncontracted, - } - } -} diff --git a/src-tauri/src/analytics/data_structures/timeline.rs b/src-tauri/src/analytics/data_structures/timeline.rs deleted file mode 100644 index ce3517e3..00000000 --- a/src-tauri/src/analytics/data_structures/timeline.rs +++ /dev/null @@ -1,255 +0,0 @@ -#![allow(dead_code)] - -use log::error; -use std::collections::HashMap; -use std::fs; -use std::fs::OpenOptions; -use std::io::prelude::*; - -// A data structure to represent the timeline of graph snapshots. -use crate::analytics::aux_functions::take_snapshot::take_snapshot_of_graph; -use crate::graph::graph_ds::Graph; - -// Created at the beginning of each rescue attempt. -pub struct NetworkTimeline { - curr_snapshot: Option, - label: i32, - curr_timeline_id: i32, - curr_snapshot_id: i32, - label_dir: String, - data_dir: String, - save: bool, -} - -impl NetworkTimeline { - /// Creates a new timeline. - /// - /// # Arguments - /// - /// * `config_fields` - a HashMap containing the config fields: timeline_data_dir (location of data/timelines) and timeline_label_dir (location of data) - /// * `is_save` - a boolean indicating whether the timeline should be saved or not - /// - /// # Returns - /// - /// * `Timeline` - a new timeline - pub fn new(config_fields: HashMap<&str, &str>, is_save: bool) -> NetworkTimeline { - let mut curr_timeline_id = 0; - - let timeline_data_dir = config_fields.get("timeline_data_dir").unwrap_or(&""); - let timeline_label_dir = config_fields.get("timeline_label_dir").unwrap_or(&""); - - if is_save { - let paths = fs::read_dir(timeline_data_dir).unwrap(); - for res_path in paths { - let path = res_path.unwrap().path(); - let filename = path.file_name().unwrap().to_str().unwrap(); - let filename = filename.split('.').collect::>()[0]; - let prev_timeline_id = filename.parse::().unwrap(); - if prev_timeline_id > curr_timeline_id { - curr_timeline_id = prev_timeline_id; - }; - } - } - - curr_timeline_id += 1; - - NetworkTimeline { - curr_snapshot: None, - label: 1, - curr_timeline_id, - curr_snapshot_id: 0, - label_dir: timeline_label_dir.to_string(), - data_dir: timeline_data_dir.to_string(), - save: is_save, - } - } - - pub fn add_snapshot(&mut self, snapshot: &Graph) { - match &self.curr_snapshot { - None => { - self.curr_snapshot = Some(snapshot.clone()); - } - Some(_curr_snapshot) => { - let is_connected = self.check_connection(snapshot); - if self.save { - self.write_snapshot(); - } - self.curr_snapshot = Some(snapshot.clone()); - if !is_connected { - self.label = 0; - if self.save { - self.write_timeline_label(); - } - self.curr_timeline_id += 1; - } - } - } - self.curr_snapshot_id += 1; - } - - pub fn check_connection(&mut self, other_snapshot: &Graph) -> bool { - match &self.curr_snapshot { - None => true, - Some(curr_snapshot) => { - let g1_order = curr_snapshot.get_order(); - let g2_order = other_snapshot.get_order(); - let diff = (g1_order as i32 - g2_order as i32).abs(); - if diff >= 1 { - return false; - } - true - } - } - } - - pub fn get_curr_snapshot(&self) -> Option<&Graph> { - self.curr_snapshot.as_ref() - } - - /// Writes the current snapshot to a txt file. - pub fn write_snapshot(&mut self) { - let curr_snapshot = self.curr_snapshot.as_ref().expect("msg"); - let mut snapshot_string = take_snapshot_of_graph(curr_snapshot); - snapshot_string.push('\n'); - - let timeline_id = self.curr_timeline_id; - - let filename = format!("{}/{}.txt", self.data_dir, timeline_id); - let mut file = OpenOptions::new() - .append(true) - .create(true) - .open(filename.clone()) - .unwrap(); - - if let Err(e) = writeln!(file, "{}", snapshot_string) { - error!("Couldn't write snapshot to file {}: {}", filename, e); - } - } - - /// Writes the current label to a txt file. - pub fn write_timeline_label(&mut self) { - let label_text = format!("{},{}", self.curr_timeline_id, self.label); - let filename = format!("{}/labels.txt", self.label_dir); - - let mut labels_file = OpenOptions::new() - .append(true) - .create(true) - .open(filename.clone()) - .unwrap(); - - if let Err(e) = writeln!(labels_file, "{}", label_text) { - error!("Couldn't write label to file {}: {}", filename, e); - } - } - - pub fn clear(&mut self) { - self.curr_snapshot = None; - } - - pub fn conclude_timeline(&mut self) { - if self.save { - self.write_snapshot(); - self.label = 1; - self.write_timeline_label(); - } - } -} - -#[cfg(test)] -mod tests { - use crate::graph::{edge::Edge, node::Node}; - - use super::*; - - #[test] - fn test_timeline() { - let mut config_fields = HashMap::new(); - - config_fields.insert("timeline_data_dir", "data/timelines"); - config_fields.insert("timeline_label_dir", "data"); - - let mut timeline = NetworkTimeline::new(config_fields, false); - - let mut graph1 = Graph::new(); - - // Create a few nodes and edges and add to graph - let a: String = "a".to_string(); - let b: String = "b".to_string(); - let c: String = "c".to_string(); - let d: String = "d".to_string(); - - let mut a_node = Node::new(a); - a_node.set_gps(-72.28486, 43.71489, 1.0); - let a_idx = graph1.add_node_from_struct(a_node); - - let mut b_node = Node::new(b); - b_node.set_gps(-72.28239, 43.71584, 1.0); - let b_idx = graph1.add_node_from_struct(b_node); - - let mut c_node = Node::new(c); - c_node.set_gps(-72.28332, 43.7114, 1.0); - let c_idx = graph1.add_node_from_struct(c_node); - - let mut d_node = Node::new(d); - d_node.set_gps(-72.28085, 43.71235, 1.0); - let d_idx = graph1.add_node_from_struct(d_node); - - // 0: a, 1: b, 2: c, 3: d - let a_b = Edge::new(a_idx, b_idx, 0.51); - graph1.add_edge_from_struct(a_b); - - let a_c = Edge::new(a_idx, c_idx, 0.39); - graph1.add_edge_from_struct(a_c); - - let b_c = Edge::new(b_idx, c_idx, 0.4); - graph1.add_edge_from_struct(b_c); - - let b_d = Edge::new(b_idx, d_idx, 0.6); - graph1.add_edge_from_struct(b_d); - - let mut graph2 = Graph::new(); - - // Create a few nodes and edges and add to graph - let a: String = "a".to_string(); - let b: String = "b".to_string(); - let c: String = "c".to_string(); - let d: String = "d".to_string(); - - let mut a_node = Node::new(a); - a_node.set_gps(-72.28239, 43.71489, 1.0); - let a_idx = graph2.add_node_from_struct(a_node); - - let mut b_node = Node::new(b); - b_node.set_gps(-72.28486, 43.71584, 1.0); - let b_idx = graph2.add_node_from_struct(b_node); - - let mut c_node = Node::new(c); - c_node.set_gps(-72.28332, 43.7114, 1.0); - let c_idx = graph2.add_node_from_struct(c_node); - - let mut d_node = Node::new(d); - d_node.set_gps(-72.28085, 43.71235, 1.0); - let d_idx = graph2.add_node_from_struct(d_node); - - // 0: a, 1: b, 2: c, 3: d - let a_b = Edge::new(a_idx, b_idx, 0.6); - graph2.add_edge_from_struct(a_b); - - let a_c = Edge::new(a_idx, c_idx, 0.33); - graph2.add_edge_from_struct(a_c); - - let b_d = Edge::new(b_idx, d_idx, 0.65); - graph2.add_edge_from_struct(b_d); - - let b_c = Edge::new(b_idx, c_idx, 0.11); - graph2.add_edge_from_struct(b_c); - - let graphs = vec![graph1, graph2]; - - for graph in graphs { - timeline.add_snapshot(&graph); - } - - timeline.conclude_timeline(); - } -} diff --git a/src-tauri/src/analytics/data_structures/union_find.rs b/src-tauri/src/analytics/data_structures/union_find.rs deleted file mode 100644 index 95a5f4ca..00000000 --- a/src-tauri/src/analytics/data_structures/union_find.rs +++ /dev/null @@ -1,75 +0,0 @@ -#![allow(dead_code)] -#![allow(non_snake_case)] - -use std::collections::{HashMap, HashSet}; - -pub struct UnionFind { - pub sets: HashMap, - pub reps: HashSet, - pub size: i32, -} - -impl UnionFind { - pub fn new(v: Vec) -> UnionFind { - let mut uf_ds = UnionFind { - sets: HashMap::new(), - reps: HashSet::new(), - size: v.len() as i32, - }; - - for node_idx in v { - uf_ds.sets.insert(node_idx.clone(), node_idx.clone()); - uf_ds.reps.insert(node_idx.clone()); - } - - uf_ds - } - - pub fn find(&mut self, v: &String) -> String { - if self.sets.get(v).unwrap() == v { - return v.clone(); - } - - let set_find_result = self.sets.get(v).unwrap().clone(); - let parent = self.find(&set_find_result); - self.sets.insert(v.clone(), parent.to_string()); - parent - } - - pub fn union(&mut self, v1: &String, v2: &String) { - let v1_rep = self.find(v1); - self.sets.insert(v2.clone(), v1_rep); - self.reps.remove(v2); - } - - pub fn get_sets(&self) -> &HashSet { - &self.reps - } - - pub fn clone(&self) -> UnionFind { - UnionFind { - sets: self.sets.clone(), - reps: self.reps.clone(), - size: self.size, - } - } -} - -// Create a unit test for the Graph struct -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_unionfind() { - // Create a few nodes and edges and add to graph - let u: String = "u".to_string(); - let v: String = "v".to_string(); - let w: String = "w".to_string(); - - let mut uf = UnionFind::new(vec![u.clone(), v.clone(), w]); - uf.union(&u, &v); - - assert_eq!(uf.find(&u), uf.find(&v)); - } -} diff --git a/src-tauri/src/analytics/mod.rs b/src-tauri/src/analytics/mod.rs deleted file mode 100644 index fbfa97bd..00000000 --- a/src-tauri/src/analytics/mod.rs +++ /dev/null @@ -1,4 +0,0 @@ -pub mod algorithms; -pub mod aux_functions; -pub mod data_structures; -pub mod state; diff --git a/src-tauri/src/analytics/state/configuration.rs b/src-tauri/src/analytics/state/configuration.rs deleted file mode 100644 index 6cce6282..00000000 --- a/src-tauri/src/analytics/state/configuration.rs +++ /dev/null @@ -1,273 +0,0 @@ -#![allow(dead_code)] - -use std::{any::Any, collections::HashMap}; - -use serde::{Deserialize, Serialize}; - -/// The activation struct is used to determine whether an algorithm should be run. -/// -/// # Fields -/// -/// * `run` - A boolean that determines whether an algorithm should be run. -#[derive(Debug)] -struct Activation { - pub run: bool, -} - -pub struct Params { - pub params: HashMap>, -} - -impl Params { - pub fn new() -> Self { - Params { - params: HashMap::new(), - } - } - - pub fn add_param(&mut self, key: String, value: Box) { - self.params.insert(key, value); - } - - pub fn get(&self, key: &str) -> Option<&T> { - self.params.get(key).and_then(|v| v.downcast_ref::()) - } -} - -impl std::fmt::Debug for Params { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_struct("Params") - .field("params", &self.params) - .finish() - } -} - -impl Activation { - /// Creates a new Activation object. Defaults to false. - fn new() -> Self { - Activation { run: false } - } - - /// Sets whether an algorithm should be run. - fn set(&mut self, run: bool) { - self.run = run; - } - - /// Gets whether an algorithm should be run. - fn get(&self) -> bool { - self.run - } -} - -/// Configuration for articulation point algorithm. -/// Currently only contains the activation struct. -/// In the future, this may contain other parameters -/// for the algorithm (if any). -#[derive(Debug)] -pub struct APConf { - activation: Activation, - pub params: Params, -} - -/// Configuration for mincut algorithm. -/// Currently only contains the activation struct. -/// In the future, this may contain other parameters -/// for the algorithm (such as whether to include -/// the total weight of the mincut in the result). -#[derive(Debug)] -pub struct MinCutConf { - activation: Activation, - pub params: Params, -} - -/// Configuration for diffusion centrality algorithm. -/// Currently contains the activation struct and T. -#[derive(Debug)] -pub struct DiffCentConf { - activation: Activation, - pub params: Params, -} - -/// Configuration for most similar timeline algorithm. -/// Currently only contains the activation struct. -/// In the future, this **will** contain other parameters. -#[derive(Debug)] -pub struct MostSimTConf { - activation: Activation, - pub params: Params, -} - -/// Configuration for predicted state algorithm. -/// Currently only contains the activation struct. -/// In the future, this **will** contain other parameters. -#[derive(Debug)] -pub struct PredStateConf { - activation: Activation, - pub params: Params, -} - -#[derive(Debug)] -pub struct AlgorithmConfiguration { - pub ap: APConf, - pub mincut: MinCutConf, - pub diff_cent: DiffCentConf, - pub most_sim_t: MostSimTConf, - pub pred_state: PredStateConf, -} - -#[derive(Clone, Debug, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct AlgorithmConfigFlags { - pub articulation_point: Option, - pub diffusion_centrality: Option, - pub global_mincut: Option, - pub most_similar_timeline: Option, - pub predicted_state: Option, -} - -impl AlgorithmConfiguration { - pub fn new() -> Self { - AlgorithmConfiguration { - ap: APConf { - activation: Activation::new(), - params: Params::new(), - }, - mincut: MinCutConf { - activation: Activation::new(), - params: Params::new(), - }, - diff_cent: DiffCentConf { - activation: Activation::new(), - params: Params::new(), - }, - most_sim_t: MostSimTConf { - activation: Activation::new(), - params: Params::new(), - }, - pred_state: PredStateConf { - activation: Activation::new(), - params: Params::new(), - }, - } - } - - /// Sets which algos to run. - /// - /// # Arguments - /// - /// * `self` - The algorithm configuration object. - /// * `bitfield` - The bitfield of the algorithms. - /// To run the articulation point algorithm, the bitfield is 0b00001. - /// To run the mincut algorithm, the bitfield is 0b00010. - /// To run the diffusion centrality algorithm, the bitfield is 0b00100. - /// To run the most similar timeline algorithm, the bitfield is 0b01000. - /// To run the predicted state algorithm, the bitfield is 0b10000. - /// To run all algorithms, the bitfield is 0b11111. - pub fn set_algorithm_flags(&mut self, flags: AlgorithmConfigFlags) { - if let Some(f) = flags.articulation_point { - self.set_ap(f); - } - - if let Some(f) = flags.diffusion_centrality { - self.set_diff_cent(f); - } - - if let Some(f) = flags.global_mincut { - self.set_mincut(f); - } - - if let Some(f) = flags.most_similar_timeline { - self.set_most_sim_t(f); - } - - if let Some(f) = flags.predicted_state { - self.set_pred_state(f); - } - } - - pub fn set_ap(&mut self, ap: bool) { - self.ap.activation.set(ap); - } - - pub fn set_mincut(&mut self, mincut: bool) { - self.mincut.activation.set(mincut); - } - - pub fn set_diff_cent(&mut self, diff_cent: bool) { - self.diff_cent.activation.set(diff_cent); - } - - pub fn set_most_sim_t(&mut self, most_sim_t: bool) { - self.most_sim_t.activation.set(most_sim_t); - } - - pub fn set_pred_state(&mut self, pred_state: bool) { - self.pred_state.activation.set(pred_state); - } - - pub fn get_ap_activation(&self) -> bool { - self.ap.activation.get() - } - - pub fn get_mincut_activation(&self) -> bool { - self.mincut.activation.get() - } - - pub fn get_diff_cent_activation(&self) -> bool { - self.diff_cent.activation.get() - } - - pub fn get_most_sim_t_activation(&self) -> bool { - self.most_sim_t.activation.get() - } - - pub fn get_pred_state_activation(&self) -> bool { - self.pred_state.activation.get() - } - - pub fn get_ap_params(&self) -> &Params { - &self.ap.params - } - - pub fn get_mincut_params(&self) -> &Params { - &self.mincut.params - } - - pub fn get_diff_cent_params(&self) -> &Params { - &self.diff_cent.params - } - - pub fn get_most_sim_t_params(&self) -> &Params { - &self.most_sim_t.params - } - - pub fn get_pred_state_params(&self) -> &Params { - &self.pred_state.params - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_set_algos() { - let mut algo_config = AlgorithmConfiguration::new(); - - algo_config.set_algorithm_flags(AlgorithmConfigFlags { - articulation_point: Some(true), - diffusion_centrality: Some(true), - global_mincut: Some(true), - most_similar_timeline: None, - predicted_state: None, - }); - - assert!(algo_config.get_ap_activation()); - assert!(algo_config.get_mincut_activation()); - assert!(algo_config.get_diff_cent_activation()); - assert!(!algo_config.get_most_sim_t_activation()); - assert!(!algo_config.get_pred_state_activation()); - - println!("algo_config: {:?}", algo_config); - } -} diff --git a/src-tauri/src/analytics/state/controller.rs b/src-tauri/src/analytics/state/controller.rs deleted file mode 100644 index 37fda94a..00000000 --- a/src-tauri/src/analytics/state/controller.rs +++ /dev/null @@ -1,177 +0,0 @@ -#![allow(clippy::let_unit_value)] - -use crate::analytics::algorithms::articulation_point::results::APResult; -use crate::analytics::algorithms::articulation_point::{ - ArticulationPointParams, ArticulationPointRunner, -}; -use crate::analytics::algorithms::diffusion_centrality::results::DiffCenResult; -use crate::analytics::algorithms::diffusion_centrality::{ - DiffusionCentralityParams, DiffusionCentralityRunner, -}; -use crate::analytics::algorithms::stoer_wagner::results::MinCutResult; -use crate::analytics::algorithms::stoer_wagner::{GlobalMinCutParams, GlobalMinCutRunner}; -use crate::analytics::algorithms::AlgorithmRunner; -use crate::graph::graph_ds::Graph; - -use super::configuration::{AlgorithmConfiguration, Params}; -use super::history::AlgorithmRunHistory; -use super::store::ResultsStore; - -pub struct AlgoController; - -impl AlgoController { - pub fn new() -> Self { - Self {} - } - - /// Runs all algorithms that are activated in the AlgoConfig. - /// The results are stored in the AlgoStore and the History is updated. - /// - /// # Arguments - /// - /// * `graph` - The graph on which the algorithms are run. - /// * `algo_conf` - The configuration of the algorithms [`super::algos_config::AlgoConfig`]. - /// * `history` - The history of the algorithms [`super::history::History`]. - /// * `store` - The store of the algorithms [`super::algo_store::AlgoStore`]. - pub fn run_algos( - &mut self, - graph: &Graph, - algo_conf: &AlgorithmConfiguration, - history: &mut AlgorithmRunHistory, - store: &mut ResultsStore, - ) { - if algo_conf.get_ap_activation() { - let aps = self.run_ap(graph, algo_conf.get_ap_params()); - store.set_aps(aps); - history.log_ap(); - } - if algo_conf.get_mincut_activation() { - let mincut_edges = self.run_mincut(graph, algo_conf.get_mincut_params()); - store.set_mincut(mincut_edges); - history.log_mincut(); - } - if algo_conf.get_diff_cent_activation() { - let diff_cen_map = self.run_diff_cent(graph, algo_conf.get_diff_cent_params()); - store.set_diff_cent(diff_cen_map); - history.log_diff_cent(); - } - if algo_conf.get_most_sim_t_activation() { - // TODO - } - if algo_conf.get_pred_state_activation() { - // TODO - } - } - - /// Runs the articulation point algorithm. - /// - /// # Arguments - /// - /// * `graph` - The graph on which the algorithm is run. - /// * `params` - The parameters [`super::algos_config::Params`] of the algorithm. - /// - /// # Returns - /// - /// APResult - The result of the algorithm. Can be an error too. - pub fn run_ap(&mut self, graph: &Graph, _params: &Params) -> APResult { - let params = ArticulationPointParams; - let mut runner = ArticulationPointRunner::new(params); - runner.run(graph) - } - - /// Runs the mincut algorithm. - /// - /// # Arguments - /// - /// * `graph` - The graph on which the algorithm is run. - /// * `params` - The parameters [`super::algos_config::Params`] of the algorithm. - /// - /// # Returns - /// - /// MinCutResult - The result of the algorithm. Can be an error too. - pub fn run_mincut(&mut self, g: &Graph, _params: &Params) -> MinCutResult { - let params = GlobalMinCutParams; - let mut runner = GlobalMinCutRunner::new(params); - runner.run(g) - } - - /// Runs the diffusion centrality algorithm. - /// - /// # Arguments - /// - /// * `graph` - The graph on which the algorithm is run. - /// * `params` - The parameters [`super::algos_config::Params`] of the algorithm. - /// - /// # Returns - /// - /// DiffCenResult - The result of the algorithm. Can be an error too. - pub fn run_diff_cent(&mut self, g: &Graph, params: &Params) -> DiffCenResult { - let t_param = params.get("T").unwrap_or(&(5_u32)).to_owned(); - - let params = DiffusionCentralityParams { t_param }; - let mut runner = DiffusionCentralityRunner::new(params); - runner.run(g) - } - - // pub fn run_most_sim_t(&mut self, g: &Graph, params: &Params) { - // // TODO - // } - - // pub fn run_pred_state(&mut self, g: &Graph, params: &Params) { - // // TODO - // } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::analytics::state::configuration::AlgorithmConfigFlags; - use crate::graph::graph_ds::Graph; - - #[test] - fn test_run_controller() { - let mut algo_controller = AlgoController::new(); - let mut graph1 = Graph::new(); - - // Create a few nodes and edges and add to graph - let u: String = "u".to_string(); - let v: String = "v".to_string(); - let w: String = "w".to_string(); - - let _u_idx = graph1.add_node(u.clone()); - let _v_idx = graph1.add_node(v.clone()); - let _w_idx = graph1.add_node(w.clone()); - - graph1.add_edge(u.clone(), v.clone(), 1_f64); - graph1.add_edge(u, w.clone(), 7_f64); - graph1.add_edge(v, w, 35_f64); - - let mut algo_config = AlgorithmConfiguration::new(); - let history = &mut AlgorithmRunHistory::new(); - let store = &mut ResultsStore::new(); - - algo_config.set_algorithm_flags(AlgorithmConfigFlags { - articulation_point: None, - diffusion_centrality: Some(true), - global_mincut: None, - most_similar_timeline: None, - predicted_state: None, - }); - - algo_controller.run_algos(&graph1, &algo_config, history, store); - - let diff_cent_option = store.get_diff_cent(); - - match diff_cent_option { - DiffCenResult::Success(diff_cent) => { - println!("Diffusion centrality: {:?}", diff_cent); - } - DiffCenResult::Error(err) => { - panic!("Error in diffusion centrality: {:?}", err); - } - DiffCenResult::Empty(_b) => { - panic!("Empty graph"); - } - } - } -} diff --git a/src-tauri/src/analytics/state/history.rs b/src-tauri/src/analytics/state/history.rs deleted file mode 100644 index 24fa82d1..00000000 --- a/src-tauri/src/analytics/state/history.rs +++ /dev/null @@ -1,240 +0,0 @@ -#![allow(dead_code)] -use std::time::SystemTime; - -/// A struct that stores the history of the last calculation of each algorithm. -/// -/// # Fields -/// -/// * `ap_history` - A Vec of SystemTimes representing the history of the last articulation point calculation. -/// * `mincut_history` - A Vec of SystemTimes representing the history of the last mincut calculation. -/// * `diff_cent_history` - A Vec of SystemTimes representing the history of the last diffusion centrality calculation. -/// * `most_sim_t_history` - A Vec of SystemTimes representing the history of the last most similar timeline calculation. -/// * `pred_stt_history` - A Vec of SystemTimes representing the history of the last predicted state calculation. -pub struct AlgorithmRunHistory { - ap_history: Vec, - mincut_history: Vec, - diff_cent_history: Vec, - most_sim_t_history: Vec, - pred_stt_history: Vec, -} - -impl AlgorithmRunHistory { - pub fn new() -> Self { - AlgorithmRunHistory { - ap_history: Vec::new(), - mincut_history: Vec::new(), - diff_cent_history: Vec::new(), - most_sim_t_history: Vec::new(), - pred_stt_history: Vec::new(), - } - } - - fn get_last_time_entry(entries: &[SystemTime]) -> Option { - if let Some(last) = entries.last() { - return last.elapsed().ok(); - } - - None - } - - fn log_last_run_time(history: &mut Vec) -> SystemTime { - let time = SystemTime::now(); - history.push(time); - time - } - - /// Logs the time of the last articulation point calculation. - /// - /// # Arguments - /// - /// * `self` - The history object. - /// - /// # Returns - /// - /// * `SystemTime` - The time of the last articulation point calculation. - pub fn log_ap(&mut self) -> SystemTime { - Self::log_last_run_time(&mut self.ap_history) - } - - /// Logs the time of the last mincut calculation. - /// - /// # Arguments - /// - /// * `self` - The history object. - /// - /// # Returns - /// - /// * `SystemTime` - The time of the last mincut calculation. - pub fn log_mincut(&mut self) -> SystemTime { - Self::log_last_run_time(&mut self.mincut_history) - } - - /// Logs the time of the last diffusion centrality calculation. - /// - /// # Arguments - /// - /// * `self` - The history object. - /// - /// # Returns - /// - /// * `SystemTime` - The time of the last diffusion centrality calculation. - pub fn log_diff_cent(&mut self) -> SystemTime { - Self::log_last_run_time(&mut self.diff_cent_history) - } - - /// Logs the time of the last most similar timeline calculation. - /// - /// # Arguments - /// - /// * `self` - The history object. - /// - /// # Returns - /// - /// * `SystemTime` - The time of the last most similar timeline calculation. - pub fn log_most_sim_t(&mut self) -> SystemTime { - Self::log_last_run_time(&mut self.diff_cent_history) - } - - /// Logs the time of the last predicted state calculation. - /// - /// # Arguments - /// - /// * `self` - The history object. - /// - /// # Returns - /// - /// * `SystemTime` - The time of the last predicted state calculation. - pub fn log_pred_stt(&mut self) -> SystemTime { - Self::log_last_run_time(&mut self.pred_stt_history) - } - - /// Returns the history of the articulation point calculation. - /// - /// # Arguments - /// - /// * `self` - The history object. - /// - /// # Returns - /// - /// * `Vec` - The history of the articulation point calculation. - pub fn get_ap_history(&self) -> &Vec { - &self.ap_history - } - - /// Returns the history of the mincut calculation. - /// - /// # Arguments - /// - /// * `self` - The history object. - /// - /// # Returns - /// - /// * `Vec` - The history of the mincut calculation. - pub fn get_mincut_history(&self) -> &Vec { - &self.mincut_history - } - - /// Returns the history of the diffusion centrality calculation. - /// - /// # Arguments - /// - /// * `self` - The history object. - /// - /// # Returns - /// - /// * `Vec` - The history of the diffusion centrality calculation. - pub fn get_diffusion_centrality_history(&self) -> &Vec { - &self.diff_cent_history - } - - /// Returns the history of the most similar timeline calculation. - /// - /// # Arguments - /// - /// * `self` - The history object. - /// - /// # Returns - /// - /// * `Vec` - The history of the most similar timeline calculation. - pub fn get_most_sim_t_history(&self) -> &Vec { - &self.most_sim_t_history - } - - /// Returns the history of the predicted state calculation. - /// - /// # Arguments - /// - /// * `self` - The history object. - /// - /// # Returns - /// - /// * `Vec` - The history of the predicted state calculation. - pub fn get_pred_stt_history(&self) -> &Vec { - &self.pred_stt_history - } - - /// Returns the last elapsed time of the articulation point calculation. - /// - /// # Arguments - /// - /// * `self` - The history object. - /// - /// # Returns - /// - /// * `Option` - The last elapsed time of the articulation point calculation. - pub fn get_ap_history_last_elapsed(&self) -> Option { - Self::get_last_time_entry(&self.ap_history) - } - - /// Returns the last elapsed time of the mincut calculation. - /// - /// # Arguments - /// - /// * `self` - The history object. - /// - /// # Returns - /// - /// * `Option` - The last elapsed time of the mincut calculation. - pub fn get_mincut_history_last_elapsed(&self) -> Option { - Self::get_last_time_entry(&self.mincut_history) - } - - /// Returns the last elapsed time of the diffusion centrality calculation. - /// - /// # Arguments - /// - /// * `self` - The history object. - /// - /// # Returns - /// - /// * `Option` - The last elapsed time of the diffusion centrality calculation. - pub fn get_diffusion_centrality_history_last_elapsed(&self) -> Option { - Self::get_last_time_entry(&self.diff_cent_history) - } - - /// Returns the last elapsed time of the most similar timeline calculation. - /// - /// # Arguments - /// - /// * `self` - The history object. - /// - /// # Returns - /// - /// * `Option` - The last elapsed time of the most similar timeline calculation. - pub fn get_most_sim_timeline_history_last_elapsed(&self) -> Option { - Self::get_last_time_entry(&self.most_sim_t_history) - } - - /// Returns the last elapsed time of the predicted state calculation. - /// - /// # Arguments - /// - /// * `self` - The history object. - /// - /// # Returns - /// - /// * `Option` - The last elapsed time of the predicted state calculation. - pub fn get_predicted_state_history_last_elapsed(&self) -> Option { - Self::get_last_time_entry(&self.pred_stt_history) - } -} diff --git a/src-tauri/src/analytics/state/mod.rs b/src-tauri/src/analytics/state/mod.rs deleted file mode 100644 index efcc892a..00000000 --- a/src-tauri/src/analytics/state/mod.rs +++ /dev/null @@ -1,215 +0,0 @@ -#![allow(dead_code)] - -use std::collections::HashMap; -use std::time::SystemTime; - -use log::warn; - -use self::configuration::{AlgorithmConfigFlags, AlgorithmConfiguration}; -use self::controller::AlgoController; -use self::history::AlgorithmRunHistory; -use self::store::ResultsStore; -use super::data_structures::timeline::NetworkTimeline; - -use crate::graph::graph_ds::Graph; - -pub mod configuration; -pub mod controller; -pub mod history; -pub mod store; - -/// The AnalyticsState struct contains all the data that is not specific to a particular algorithm. -/// It is used to store the state of the application. -/// -/// # Fields -/// -/// * `timeline` - A Timeline object. -/// * `adj_matrix` - A Vec of Vecs of f64s representing the adjacency matrix. -/// * `int_to_node_id` - A HashMap from usize to String representing the mapping from integers to node IDs. -/// * `node_id_to_int` - A HashMap from String to usize representing the mapping from node IDs to integers. -/// * `history` - A History object. -/// * `time` - A SystemTime object. -/// * `auto_run_algos` - A boolean indicating whether the algorithms should be run automatically or not. -pub struct AnalyticsState { - timeline: NetworkTimeline, - history: AlgorithmRunHistory, - time: SystemTime, - algo_configs: AlgorithmConfiguration, - auto_run_algos: bool, - algo_store: ResultsStore, - algo_controller: AlgoController, -} - -impl AnalyticsState { - /// Creates a new AnalyticsState object. - /// - /// # Arguments - /// - /// * `config_fields` - A HashMap containing the configuration fields. - /// * `is_save` - A boolean indicating whether the timeline should be saved or not. - /// - /// # Returns - /// - /// * `AnalyticsState` - A new AnalyticsState object. - pub fn new(config_fields: HashMap<&str, &str>, is_save: bool) -> AnalyticsState { - AnalyticsState { - timeline: NetworkTimeline::new(config_fields, is_save), - history: AlgorithmRunHistory::new(), - time: SystemTime::now(), - algo_configs: AlgorithmConfiguration::new(), - auto_run_algos: true, - algo_store: ResultsStore::new(), - algo_controller: AlgoController::new(), - } - } - - /// Adds a graph snapshot to the timeline. - /// - /// # Arguments - /// - /// * `graph` - A Graph object. - pub fn add_graph_snapshot(&mut self, graph: &Graph) { - self.timeline.add_snapshot(graph); - } - - /// Sets which algorithms should be run. - /// - /// # Arguments - /// - /// * `algos_bitfield` - A u8 representing the algorithms to run. - pub fn set_algorithm_flags(&mut self, flags: AlgorithmConfigFlags) { - self.algo_configs.set_algorithm_flags(flags); - } - - pub fn run_algos(&mut self) { - let curr_graph_opt = self.timeline.get_curr_snapshot(); - match curr_graph_opt { - Some(curr_graph) => { - self.algo_controller.run_algos( - curr_graph, - &self.algo_configs, - &mut self.history, - &mut self.algo_store, - ); - } - None => { - warn!("No graph to run algorithms on."); - } - } - } - - /// Returns the algorithm result store. Frontend may use this to get the results of the algorithms. - /// - /// # Returns - /// - /// * `AlgoStore` - The algorithm result store. - pub fn get_algo_results(&self) -> &ResultsStore { - &self.algo_store - } -} - -/// Unit test -#[cfg(test)] -mod tests { - use super::super::algorithms::{ - articulation_point::results::APResult, diffusion_centrality::results::DiffCenResult, - }; - use super::*; - - #[test] - fn test_state() { - let mut state = AnalyticsState::new(HashMap::new(), false); - - let mut graph1 = Graph::new(); - - // Create a few nodes and edges and add to graph - let u: String = "u".to_string(); - let v: String = "v".to_string(); - let w: String = "w".to_string(); - - let _u_idx = graph1.add_node(u.clone()); - let _v_idx = graph1.add_node(v.clone()); - let _w_idx = graph1.add_node(w.clone()); - - graph1.add_edge(u.clone(), v.clone(), 1_f64); - graph1.add_edge(u, w.clone(), 7_f64); - graph1.add_edge(v, w, 35_f64); - - state.add_graph_snapshot(&graph1); - - state.set_algorithm_flags(AlgorithmConfigFlags { - articulation_point: Some(true), - diffusion_centrality: None, - global_mincut: None, - most_similar_timeline: None, - predicted_state: None, - }); - - state.run_algos(); - - let algo_results = state.get_algo_results(); - let ap_algo_res = algo_results.get_aps(); - match ap_algo_res { - APResult::Success(aps) => { - println!("AP algorithm returned: {:?}", aps); - assert_eq!(aps.len(), 0); - } - APResult::Error(err_str) => { - panic!("Error running AP algorithm: {}", err_str); - } - APResult::Empty(b) => { - panic!("AP algorithm returned empty result: {}", b); - } - } - } - - #[test] - fn test_diffusion_centrality() { - let mut state = AnalyticsState::new(HashMap::new(), false); - - let mut graph1 = Graph::new(); - - // Create a few nodes and edges and add to graph - let u: String = "u".to_string(); - let v: String = "v".to_string(); - let w: String = "w".to_string(); - - let _u_idx = graph1.add_node(u.clone()); - let _v_idx = graph1.add_node(v.clone()); - let _w_idx = graph1.add_node(w.clone()); - - graph1.add_edge(u.clone(), v.clone(), 1_f64); - graph1.add_edge(u, w.clone(), 7_f64); - graph1.add_edge(v, w, 35_f64); - - state.add_graph_snapshot(&graph1); - - state.set_algorithm_flags(AlgorithmConfigFlags { - articulation_point: None, - diffusion_centrality: Some(true), - global_mincut: None, - most_similar_timeline: None, - predicted_state: None, - }); - - state.run_algos(); - - let algo_results = state.get_algo_results(); - - let diff_cents_res = algo_results.get_diff_cent(); - match diff_cents_res { - DiffCenResult::Success(diff_cents) => { - println!("Diffusion centrality algorithm returned: {:?}", diff_cents); - } - DiffCenResult::Error(err) => { - panic!("Error running diffusion centrality algorithm: {:?}", err); - } - DiffCenResult::Empty(b) => { - panic!( - "Diffusion centrality algorithm returned empty result: {}", - b - ); - } - } - } -} diff --git a/src-tauri/src/analytics/state/store.rs b/src-tauri/src/analytics/state/store.rs deleted file mode 100644 index 7586eae7..00000000 --- a/src-tauri/src/analytics/state/store.rs +++ /dev/null @@ -1,81 +0,0 @@ -#![allow(dead_code)] - -use crate::analytics::algorithms::articulation_point::results::APResult; -use crate::analytics::algorithms::diffusion_centrality::results::DiffCenResult; -use crate::analytics::algorithms::most_similar_timeline::results::MostSimTResult; -use crate::analytics::algorithms::predicted_state::results::PredStateResult; -use crate::analytics::algorithms::stoer_wagner::results::MinCutResult; - -use crate::graph::graph_ds::Graph; -use serde::{Deserialize, Serialize}; - -/// Stores the results of the algorithms. -/// -/// # Fields -/// -/// * `aps` - [`crate::state_err_enums::ap::APResult`] that stores Success/Error/Empty states of articulation point algorithm. -/// * `mincut` - [`crate::state_err_enums::mincut::MinCutResult`] that stores Success/Error/Empty of minimum cut algorithm. -/// * `diff_cent` - [`crate::state_err_enums::diff_cen::DiffCenResult`] that stores Success/Error/Empty of diffusion centrality algorithm. -/// * `most_sim_t` - [`crate::state_err_enums::most_sim_timeline::MostSimTResult`] that stores Success/Error/Empty of most similar timeline algorithm. -/// * `pred_state` - [`crate::state_err_enums::pred_state::PredStateResult`] that stores Success/Error/Empty of state prediction algorithm. - -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct ResultsStore { - pub aps: APResult, - pub mincut: MinCutResult, - pub diff_cent: DiffCenResult, - pub most_sim_t: MostSimTResult, - pub pred_state: PredStateResult, -} - -impl ResultsStore { - pub fn new() -> Self { - ResultsStore { - aps: APResult::Empty(true), - mincut: MinCutResult::Empty(true), - diff_cent: DiffCenResult::Empty(true), - most_sim_t: MostSimTResult::Empty(true), - pred_state: PredStateResult::Empty(true), - } - } - - pub fn get_aps(&self) -> &APResult { - &self.aps - } - - pub fn get_mincut(&self) -> &MinCutResult { - &self.mincut - } - - pub fn get_diff_cent(&self) -> &DiffCenResult { - &self.diff_cent - } - - pub fn get_most_sim_t(&self) -> &MostSimTResult { - &self.most_sim_t - } - - pub fn get_pred_state(&self) -> &PredStateResult { - &self.pred_state - } - - pub fn set_aps(&mut self, aps: APResult) { - self.aps = aps; - } - - pub fn set_mincut(&mut self, mincut: MinCutResult) { - self.mincut = mincut; - } - - pub fn set_diff_cent(&mut self, diff_cent: DiffCenResult) { - self.diff_cent = diff_cent; - } - - pub fn set_most_sim_t(&mut self, most_sim_t: Graph) { - self.most_sim_t = MostSimTResult::Success(most_sim_t); - } - - pub fn set_pred_state(&mut self, pred_state: Graph) { - self.pred_state = PredStateResult::Success(pred_state); - } -} diff --git a/src-tauri/src/constructors/init/init_edge_map.rs b/src-tauri/src/constructors/init/init_edge_map.rs deleted file mode 100644 index bdb03932..00000000 --- a/src-tauri/src/constructors/init/init_edge_map.rs +++ /dev/null @@ -1,354 +0,0 @@ -use log::{info, warn}; -use meshtastic::protobufs::Neighbor; -use std::collections::HashMap; - -use crate::device::NeighborInfoPacket; - -#[derive(Clone, Debug)] -pub struct GraphEdgeMetadata { - pub snr: f64, - pub timestamp: u64, -} - -impl GraphEdgeMetadata { - fn new(snr: impl Into, timestamp: impl Into) -> Self { - Self { - snr: snr.into(), - timestamp: timestamp.into(), - } - } -} - -/// We're given a HashMap of NeighborInfo packets, each of which is considered to be the most up-to-date -/// edge info we have for a node. When we create edges, we want to keep this idea of freshness in mind. If -/// a node reports an edge, but a more recent packet from the other node does not report the edge, we want to -/// report the edge as dropped and not add it to our list. -/// -/// returns a hashmap is of form (node_id_1, node_id_2) -> (SNR, timestamp). -/// -/// This is an O(n^2) algorithm, but our graph is small. Should be reworked at some point. -pub fn init_edge_map( - neighbors: &HashMap, -) -> HashMap<(u32, u32), GraphEdgeMetadata> { - let mut snr_hashmap = HashMap::<(u32, u32), GraphEdgeMetadata>::new(); - - // For all stored neighbor info packets - for neighbor_packet in neighbors.values() { - let node_id_1 = neighbor_packet.data.node_id; - - // For all neighbors in each packet - for neighbor in &neighbor_packet.data.neighbors { - let node_id_2 = neighbor.node_id; - - // Insert each edge into the SNR map - snr_hashmap.insert( - as_key(node_id_1, node_id_2), - GraphEdgeMetadata::new(neighbor.snr, neighbor_packet.packet.rx_time), - ); - - // Check that the edge (node_id_2, node_id_1) has been heard from - // If the opposite neighbor is found on a recent packet, we take the most recent SNR - if let Some(opposite_neighbor) = - check_other_node_agrees(node_id_2, node_id_1, neighbors) - { - let opposite_packet = neighbors.get(&opposite_neighbor.node_id).unwrap(); - let own_packet = neighbors.get(&neighbor.node_id).unwrap(); - - let most_recent_data = if own_packet.packet.rx_time > opposite_packet.packet.rx_time - { - neighbor - } else { - &opposite_neighbor - }; - - snr_hashmap.insert( - as_key(node_id_1, node_id_2), - GraphEdgeMetadata::new(most_recent_data.snr, neighbor_packet.packet.rx_time), - ); - - continue; - } - - // If the opposite neighbor is not found on a recent packet, we check if our packet is most recent - let opt_opposite_node = neighbors.get(&node_id_2); - match opt_opposite_node { - Some(opposite_node) => { - // If the other is more recent, we drop the edge - if opposite_node.packet.rx_time > neighbor_packet.packet.rx_time { - info!("{} -> {} is a dropped edge", node_id_1, node_id_2); - if snr_hashmap.contains_key(&as_key(node_id_1, node_id_2)) { - snr_hashmap.remove(&as_key(node_id_1, node_id_2)); - } - } - } - _ => { - // If the other node is not found in the neighbors hashmap, we don't add the edge (drop it) - warn!("{} not found in neighbors", node_id_2); - } - } - } - } - snr_hashmap -} - -/// When we have one side of an edge, we need to check the other side to make sure -/// that the edge is still valid. If the other side of the edge is not found on a more -/// recent packet, we'll assume that the edge has dropped. -/// To do that, this function finds the corresponding Neighbor packet for the other node -pub fn check_other_node_agrees( - own_id: u32, - neighbor_id: u32, - neighbors: &HashMap, -) -> Option { - let own_packet = neighbors.get(&own_id)?; - let own_neighbors = &own_packet.data.neighbors; - - let found_neighbor = own_neighbors - .iter() - .find(|n| n.node_id == neighbor_id)? - .clone(); - - Some(found_neighbor) -} - -/// A helper function to prevent (A, B) (B, A) duplicates in the hashmap -/// by ensuring that A < B (assuming A != B) -pub fn as_key(node_1: u32, node_2: u32) -> (u32, u32) { - if node_1 < node_2 { - (node_1, node_2) - } else { - (node_2, node_1) - } -} - -#[cfg(test)] -mod tests { - use meshtastic::protobufs::{MeshPacket, Neighbor, NeighborInfo}; - - use super::*; - - #[test] - pub fn test_init_edge_map() { - let neighbor_1 = Neighbor { - node_id: 1, - snr: 1.0, - ..Default::default() - }; - - let neighbor_2 = Neighbor { - node_id: 2, - snr: 2.0, - ..Default::default() - }; - - let neighbor_3 = Neighbor { - node_id: 3, - snr: 3.0, - ..Default::default() - }; - - let neighbor_4 = Neighbor { - node_id: 4, - snr: 4.0, - ..Default::default() - }; - - let neighbor_info_1 = NeighborInfo { - node_id: 1, - neighbors: vec![neighbor_2.clone(), neighbor_3.clone(), neighbor_4.clone()], - ..Default::default() - }; - - let neighbor_info_2 = NeighborInfo { - node_id: 2, - neighbors: vec![neighbor_1.clone(), neighbor_3.clone(), neighbor_4.clone()], - ..Default::default() - }; - - let neighbor_info_3 = NeighborInfo { - node_id: 3, - neighbors: vec![neighbor_1.clone(), neighbor_2.clone(), neighbor_4], - ..Default::default() - }; - - let neighbor_info_4 = NeighborInfo { - node_id: 4, - neighbors: vec![neighbor_1, neighbor_2, neighbor_3], - ..Default::default() - }; - - let neighborinfo_vec = vec![ - neighbor_info_1, - neighbor_info_2, - neighbor_info_3, - neighbor_info_4, - ]; - - let mut neighborinfo_hashmap = HashMap::new(); - for neighborinfo in neighborinfo_vec { - neighborinfo_hashmap.insert( - neighborinfo.node_id, - NeighborInfoPacket { - data: neighborinfo, - ..Default::default() - }, - ); - } - - println!("neighborinfo_hashmap: {:?}", neighborinfo_hashmap); - let snr_hashmap = init_edge_map(&neighborinfo_hashmap); - println!("snr_hashmap: {:?}", snr_hashmap); - assert_eq!(snr_hashmap.len(), 6); - } - - #[test] - fn test_prioritize_recent_snr() { - let neighbor_1 = Neighbor { - node_id: 1, - snr: 1.0, - ..Default::default() - }; - - let neighbor_2 = Neighbor { - node_id: 2, - snr: 2.0, - ..Default::default() - }; - - let packet_1 = MeshPacket { - rx_time: 1, - ..Default::default() - }; - - let packet_2 = MeshPacket { - rx_time: 100, - ..Default::default() - }; - - let neighbor_info_1 = NeighborInfo { - node_id: 1, - neighbors: vec![neighbor_2], - ..Default::default() - }; - - let neighbor_info_2: NeighborInfo = NeighborInfo { - node_id: 2, - neighbors: vec![neighbor_1], - ..Default::default() - }; - - let neighborinfo_vec = vec![(neighbor_info_1, packet_1), (neighbor_info_2, packet_2)]; - - let mut neighborinfo_hashmap = HashMap::new(); - for (neighborinfo, packet) in neighborinfo_vec { - neighborinfo_hashmap.insert( - neighborinfo.node_id, - NeighborInfoPacket { - data: neighborinfo, - packet, - }, - ); - } - - let snr_hashmap = init_edge_map(&neighborinfo_hashmap); - println!("snr_hashmap: {:?}", snr_hashmap); - assert_eq!(snr_hashmap.len(), 1); - assert_eq!(snr_hashmap.get(&as_key(1, 2)).unwrap().snr, 2.0); - } - - #[test] - pub fn test_edge_drop_off() { - let neighbor_1 = Neighbor { - node_id: 1, - snr: 1.0, - ..Default::default() - }; - - let neighbor_2 = Neighbor { - node_id: 2, - snr: 2.0, - ..Default::default() - }; - - let neighbor_3 = Neighbor { - node_id: 3, - snr: 3.0, - ..Default::default() - }; - - let neighbor_4 = Neighbor { - node_id: 4, - snr: 4.0, - ..Default::default() - }; - - let packet_1 = MeshPacket { - rx_time: 1, - ..Default::default() - }; - - let packet_2 = MeshPacket { - rx_time: 2, - ..Default::default() - }; - - let packet_3 = MeshPacket { - rx_time: 3, - ..Default::default() - }; - - let packet_4 = MeshPacket { - rx_time: 4, - ..Default::default() - }; - - // Start with a full set of neighbors - let neighbor_info_1 = NeighborInfo { - node_id: 1, - neighbors: vec![neighbor_2.clone(), neighbor_3.clone(), neighbor_4.clone()], - ..Default::default() - }; - - let neighbor_info_2: NeighborInfo = NeighborInfo { - node_id: 2, - neighbors: vec![neighbor_1.clone(), neighbor_3.clone(), neighbor_4.clone()], - ..Default::default() - }; - - let neighbor_info_3: NeighborInfo = NeighborInfo { - node_id: 3, - neighbors: vec![neighbor_1, neighbor_2.clone(), neighbor_4], - ..Default::default() - }; - - // Drop a neighbor - let neighbor_info_4: NeighborInfo = NeighborInfo { - node_id: 4, - neighbors: vec![neighbor_2, neighbor_3], - ..Default::default() - }; - - let neighborinfo_vec = vec![ - (neighbor_info_1, packet_1), - (neighbor_info_2, packet_2), - (neighbor_info_3, packet_3), - (neighbor_info_4, packet_4), - ]; - - let mut neighborinfo_hashmap = HashMap::new(); - for (neighborinfo, packet) in neighborinfo_vec { - neighborinfo_hashmap.insert( - neighborinfo.node_id, - NeighborInfoPacket { - data: neighborinfo, - packet, - }, - ); - } - - println!("neighborinfo_hashmap: {:#?}", neighborinfo_hashmap); - let snr_hashmap = init_edge_map(&neighborinfo_hashmap); - println!("snr_hashmap: {:#?}", snr_hashmap); - assert_eq!(snr_hashmap.len(), 5); - } -} diff --git a/src-tauri/src/constructors/init/init_graph.rs b/src-tauri/src/constructors/init/init_graph.rs deleted file mode 100644 index 03a7bffb..00000000 --- a/src-tauri/src/constructors/init/init_graph.rs +++ /dev/null @@ -1,265 +0,0 @@ -use crate::analytics::aux_functions::edge_factory::edge_factory; -use crate::data_conversion::distance_constants::{ - ALT_CONVERSION_FACTOR, LAT_CONVERSION_FACTOR, LON_CONVERSION_FACTOR, SPEED_CONVERSION_FACTOR, -}; -use crate::data_conversion::distance_conversion::get_spherical_distance; -use crate::device::MeshNode; -use crate::graph::graph_ds::Graph; -use crate::graph::node::Node; -use log::warn; -use petgraph::graph::NodeIndex; -use std::collections::HashMap; - -use super::init_edge_map::GraphEdgeMetadata; - -// Create a graph from a hashmap of edge info and a hashmap of node location info -// Hashmaps will be stored in our `MeshDevice` struct -pub fn init_graph( - snr_hashmap: &HashMap<(u32, u32), GraphEdgeMetadata>, - loc_hashmap: &HashMap, -) -> Graph { - let mut graph = Graph::new(); - let mut edge_left_endpoints = Vec::::new(); - let mut edge_right_endpoints = Vec::::new(); - let mut edge_distances = Vec::::new(); - let mut edge_radio_quality = Vec::::new(); - - for neighbor_pair in snr_hashmap { - let node_id = neighbor_pair.0 .0; - let neighbor_id = neighbor_pair.0 .1; - let snr = neighbor_pair.1.snr; - let node_loc = loc_hashmap.get(&node_id); - let neighbor_loc = loc_hashmap.get(&neighbor_id); - - let node_idx = add_node_and_location_to_graph(node_id, &mut graph, node_loc); - let neighbor_idx = add_node_and_location_to_graph(neighbor_id, &mut graph, neighbor_loc); - let distance = get_spherical_distance(node_loc, neighbor_loc).unwrap(); - - edge_left_endpoints.push(node_idx); - edge_right_endpoints.push(neighbor_idx); - edge_distances.push(distance); - edge_radio_quality.push(snr); - } - - let edges = edge_factory( - edge_left_endpoints, - edge_right_endpoints, - edge_distances, - edge_radio_quality, - None, - None, - ); - - for edge in edges { - graph.add_edge_from_struct(edge); - } - - graph -} - -pub fn add_node_and_location_to_graph( - node_id: u32, - graph: &mut Graph, - node_loc: Option<&MeshNode>, -) -> NodeIndex { - let name: String = node_id.to_string(); - if !graph.contains_node(name.clone()) { - let mut node = Node::new(name.clone()); - if let Some(node_loc) = node_loc { - let node_pos = &node_loc.position_metrics.last(); - if let Some(node_pos) = node_pos { - node.latitude = node_pos.latitude as f64 * LAT_CONVERSION_FACTOR; - node.longitude = node_pos.longitude as f64 * LON_CONVERSION_FACTOR; - node.altitude = node_pos.altitude as f64 * ALT_CONVERSION_FACTOR; - node.speed = node_pos.ground_speed as f64 * SPEED_CONVERSION_FACTOR; - node.direction = node_pos.ground_track as f64; - } else { - warn!("We do not have position info for node {}", name); - } - } - - return graph.add_node_from_struct(node); - } - - let node_idx = graph.get_node_idx(&name); - let node = graph - .get_node_mut(node_idx) - .expect("Index from edge should exist"); - - if let Some(node_loc) = node_loc { - let node_pos = &node_loc.position_metrics.last(); - if let Some(node_pos) = node_pos { - let latitude = node_pos.latitude as f64 * LAT_CONVERSION_FACTOR; - let longitude = node_pos.longitude as f64 * LON_CONVERSION_FACTOR; - let altitude = node_pos.altitude as f64 * ALT_CONVERSION_FACTOR; - node.set_gps(longitude, latitude, altitude); - } - } - - graph.get_node_idx(&name) -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::analytics::data_structures::neighbor_info::Neighbor; - use crate::device::NormalizedPosition; - - #[test] - fn test_init_graph() { - let mut meshnode_1 = MeshNode::new(1); - meshnode_1 - .position_metrics - .push(NormalizedPosition::default()); - - let mut meshnode_2 = MeshNode::new(2); - meshnode_2 - .position_metrics - .push(NormalizedPosition::default()); - - let mut meshnode_3 = MeshNode::new(3); - meshnode_3 - .position_metrics - .push(NormalizedPosition::default()); - - let mut meshnode_4 = MeshNode::new(4); - meshnode_4 - .position_metrics - .push(NormalizedPosition::default()); - - let mut loc_hashmap: HashMap = HashMap::new(); - let mut snr_hashmap: HashMap<(u32, u32), GraphEdgeMetadata> = HashMap::new(); - - loc_hashmap.insert(1, meshnode_1); - loc_hashmap.insert(2, meshnode_2); - loc_hashmap.insert(3, meshnode_3); - loc_hashmap.insert(4, meshnode_4); - - snr_hashmap.insert( - (1, 2), - GraphEdgeMetadata { - snr: 0.9, - timestamp: 0, - }, - ); - - snr_hashmap.insert( - (1, 3), - GraphEdgeMetadata { - snr: 0.9, - timestamp: 0, - }, - ); - - snr_hashmap.insert( - (1, 4), - GraphEdgeMetadata { - snr: 0.9, - timestamp: 0, - }, - ); - - snr_hashmap.insert( - (2, 3), - GraphEdgeMetadata { - snr: 0.9, - timestamp: 0, - }, - ); - - snr_hashmap.insert( - (2, 4), - GraphEdgeMetadata { - snr: 0.9, - timestamp: 0, - }, - ); - - snr_hashmap.insert( - (3, 4), - GraphEdgeMetadata { - snr: 0.9, - timestamp: 0, - }, - ); - - let graph = init_graph(&snr_hashmap, &loc_hashmap); - // Check that the graph has the correct number of nodes - assert_eq!(graph.get_order(), 4); - // Check that the graph has the correct number of edges - assert_eq!(graph.get_size(), 6); - } - - #[test] - fn test_single_edge() { - let neighbor_1 = Neighbor { - id: 1, - timestamp: 0, - snr: 0.9, - }; - - let neighbor_2 = Neighbor { - id: 2, - timestamp: 100, - snr: 0.1, - }; - - let lat_1 = 43.7022; - let lng_1 = 72.2882; - let alt_1 = 0; - - let distance_1_info = NormalizedPosition { - latitude: lat_1, - longitude: lng_1, - altitude: alt_1, - ..Default::default() - }; - - let mut meshnode_1 = MeshNode::new(1); - meshnode_1.position_metrics.push(distance_1_info); - - let lat_2 = 43.7030; - let lng_2 = 72.2890; - let alt_2 = 0; - - let distance_2_info = NormalizedPosition { - latitude: lat_2, - longitude: lng_2, - altitude: alt_2, - ..Default::default() - }; - - let mut meshnode_2 = MeshNode::new(2); - meshnode_2.position_metrics.push(distance_2_info); - - let mut loc_hashmap: HashMap = HashMap::new(); - let mut snr_hashmap: HashMap<(u32, u32), GraphEdgeMetadata> = HashMap::new(); - - loc_hashmap.insert(1, meshnode_1); - loc_hashmap.insert(2, meshnode_2); - - snr_hashmap.insert( - (1, 2), - GraphEdgeMetadata { - snr: 0.1, - timestamp: 100, - }, - ); - - let mut graph = init_graph(&snr_hashmap, &loc_hashmap); - - // Check that the graph has the correct number of edges - assert_eq!(graph.get_size(), 1); - - // Check the edge weights to check that they are both the weight of the 1-2 edge, which has neighbor 2's SNR - // Assert that the 1-2 edge is the correct (smaller) SNR - let first_edge_weight = graph.get_edge_weight( - &neighbor_1.id.to_string(), - &neighbor_2.id.to_string(), - None, - Some(false), - ); - // The correct weight should a sum of the two distances normalized w 0.1 radio quality, which is this float - assert_eq!(first_edge_weight, 1.0); - } -} diff --git a/src-tauri/src/constructors/init/init_node_map.rs b/src-tauri/src/constructors/init/init_node_map.rs deleted file mode 100644 index 1b983502..00000000 --- a/src-tauri/src/constructors/init/init_node_map.rs +++ /dev/null @@ -1,64 +0,0 @@ -#![allow(dead_code)] - -use crate::device::MeshNode; -use std::collections::HashMap; - -// This function takes a vector of MeshNodes and returns a HashMap of MeshNodes, keyed by node id. -pub fn init_node_map(meshnode_vec: Vec) -> HashMap { - let mut loc_hashmap: HashMap = HashMap::new(); - for meshnode in meshnode_vec { - let node_id = meshnode.node_num; - loc_hashmap.insert(node_id, meshnode); - } - loc_hashmap -} - -#[cfg(test)] -mod tests { - use crate::device::NormalizedPosition; - - use super::*; - use meshtastic::protobufs; - - fn generate_test_user() -> protobufs::User { - protobufs::User { - id: "test".to_string(), - long_name: "test".to_string(), - short_name: "test".to_string(), - macaddr: Vec::new(), - hw_model: 0, - is_licensed: false, - } - } - - #[test] - pub fn test_init_node_map() { - let mut meshnode_1 = MeshNode::new(1); - meshnode_1.user = Some(generate_test_user()); - meshnode_1 - .position_metrics - .push(NormalizedPosition::default()); - - let mut meshnode_2 = MeshNode::new(2); - meshnode_2.user = Some(generate_test_user()); - meshnode_2 - .position_metrics - .push(NormalizedPosition::default()); - - let mut meshnode_3 = MeshNode::new(3); - meshnode_3.user = Some(generate_test_user()); - meshnode_3 - .position_metrics - .push(NormalizedPosition::default()); - - let mut meshnode_4 = MeshNode::new(4); - meshnode_4.user = Some(generate_test_user()); - meshnode_4 - .position_metrics - .push(NormalizedPosition::default()); - - let meshnode_vec = vec![meshnode_1, meshnode_2, meshnode_3, meshnode_4]; - let node_map = init_node_map(meshnode_vec); - assert_eq!(node_map.len(), 4); - } -} diff --git a/src-tauri/src/constructors/init/mod.rs b/src-tauri/src/constructors/init/mod.rs deleted file mode 100644 index 0a73f211..00000000 --- a/src-tauri/src/constructors/init/mod.rs +++ /dev/null @@ -1,3 +0,0 @@ -pub mod init_edge_map; -pub mod init_graph; -pub mod init_node_map; diff --git a/src-tauri/src/constructors/mock/mocks.rs b/src-tauri/src/constructors/mock/mocks.rs deleted file mode 100644 index 2c39b3ac..00000000 --- a/src-tauri/src/constructors/mock/mocks.rs +++ /dev/null @@ -1,219 +0,0 @@ -#![allow(dead_code)] - -use crate::analytics::data_structures::neighbor_info::{Neighbor, NeighborInfo}; -use crate::constructors::init::init_edge_map::as_key; -use crate::data_conversion::distance_conversion::{ - get_spherical_distance, gps_degrees_to_protobuf_field, -}; -use crate::device::helpers::get_current_time_u32; -use crate::device::MeshNode; -use meshtastic::protobufs; -use rand::distributions::Uniform; -use rand::prelude::*; -use rand::seq::SliceRandom; -use std::collections::HashMap; - -// Generate a list of neighborinfo packets for a given number of nodes and optional percent connectedness -pub fn mock_neighborinfo_packets( - num_nodes: i32, - opt_percent_connected: Option, -) -> Vec { - let percent_connected = opt_percent_connected.unwrap_or(1.0); - let mut neighborinfo_vec = Vec::new(); - for node_id in 0..num_nodes { - let new_neighbor = NeighborInfo { - // Assign sequential ids - id: node_id as u32, - // Assign zero time for now - timestamp: 0, - // Initialize empty neighbor lists - neighbors: Vec::new(), - }; - neighborinfo_vec.push(new_neighbor) - } - - // Add all the edges to a vector, then shuffle it and pick the first x edges to add to the graph - let mut all_edges_vec = Vec::new(); - for node_id in 0..num_nodes { - for neighbor_id in 0..num_nodes { - if node_id != neighbor_id { - all_edges_vec.push((node_id, neighbor_id)); - } - } - } - all_edges_vec.shuffle(&mut rand::thread_rng()); - let num_added_edges = (all_edges_vec.len() as f64 * percent_connected) as i32; - for edge_num in 0..num_added_edges { - let first_neighbor_id = all_edges_vec[edge_num as usize].0; - let second_neighbor_id = all_edges_vec[edge_num as usize].1; - let mut rng = rand::thread_rng(); - let rand_nbr_snr: f64 = rng.gen(); - // Push a copy of the second neighbor into the first neighbor's neighbor list - let edge_neighbor = Neighbor { - id: neighborinfo_vec[second_neighbor_id as usize].id, - snr: rand_nbr_snr, - timestamp: 0, - }; - neighborinfo_vec[first_neighbor_id as usize] - .neighbors - .push(edge_neighbor); - } - neighborinfo_vec -} - -// Generate a list of meshnode packets for a given number of nodes, within the Hanover area -pub fn mock_meshnode_packets(num_nodes: i32) -> Vec { - let mut meshnode_vec = Vec::new(); - for node_id in 0..num_nodes { - const HANOVER_LAT_PREFIX: f64 = 43.70; - const HANOVER_LON_PREFIX: f64 = 72.28; - - let latlngalt: (i32, i32, i32) = gps_degrees_to_protobuf_field( - HANOVER_LAT_PREFIX + rand::random::() * 0.01, - HANOVER_LON_PREFIX + rand::random::() * 0.01, - rand::random::() * 100.0, - ); - - let position = protobufs::Position { - latitude_i: latlngalt.0, - longitude_i: latlngalt.1, - altitude: latlngalt.2, - time: 0, - location_source: 0, - altitude_source: 0, - timestamp: 0, - timestamp_millis_adjust: 0, - altitude_hae: 0, - altitude_geoidal_separation: 0, - pdop: 0, - hdop: 0, - vdop: 0, - gps_accuracy: 0, - ground_speed: 0, - ground_track: 0, - fix_quality: 0, - fix_type: 0, - sats_in_view: 0, - sensor_id: 0, - next_update: 0, - seq_number: 0, - }; - - let user = protobufs::User { - id: "test".to_string(), - long_name: "test".to_string(), - short_name: "test".to_string(), - macaddr: Vec::new(), - hw_model: 0, - is_licensed: false, - }; - - let device_metrics = protobufs::DeviceMetrics { - battery_level: 0, - voltage: 0.0, - channel_utilization: 0.0, - air_util_tx: 0.0, - }; - - let node_info = protobufs::NodeInfo { - num: node_id as u32, - user: Some(user), - position: Some(position), - device_metrics: Some(device_metrics), - ..Default::default() - }; - - let mut meshnode = MeshNode::new(node_id.try_into().expect("Node ID cannot be negative")); - meshnode.update_from_node_info(node_info); - - meshnode_vec.push(meshnode); - } - meshnode_vec -} - -// Generate a map of edges and their weights from the current location info for mocking purposes -// Do not repeat edges. -pub fn mock_edge_map_from_loc_info( - nodes: HashMap, - radius: Option, -) -> HashMap<(u32, u32), (f64, u64)> { - // Connect nodes if their distance is less than a certain threshold radius, r, in km - let default_radius = Uniform::from(1..100).sample(&mut rand::thread_rng()) as f64; - let r = radius.unwrap_or(default_radius); - let mut edge_map = HashMap::new(); - for (node_id, node) in nodes.iter() { - for (neighbor_id, neighbor) in nodes.iter() { - if node_id != neighbor_id && !edge_map.contains_key(&as_key(*neighbor_id, *node_id)) { - let distance = get_spherical_distance(Some(node), Some(neighbor)).unwrap(); - if distance < r { - let snr = nodes - .get(neighbor_id) - .unwrap() - .last_heard - .as_ref() - .unwrap() - .snr; - let time = get_current_time_u32(); - edge_map.insert(as_key(*node_id, *neighbor_id), (snr as f64, time as u64)); - } - } - } - } - edge_map -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_init_neighborinfo_packets() { - let neighborinfo = mock_neighborinfo_packets(3, Some(0.8)); - println!("{:?}", neighborinfo); - assert_eq!(neighborinfo.len(), 3); - } - - #[test] - fn test_init_meshnode_packets() { - let meshnodes = mock_meshnode_packets(3); - println!("{:?}", meshnodes); - assert_eq!(meshnodes.len(), 3); - } - - #[test] - fn test_mock_edge_map_from_loc_info() { - let meshnodes = mock_meshnode_packets(3); - let mut nodes = HashMap::new(); - for node in meshnodes { - nodes.insert(node.node_num, node); - } - let edge_map = mock_edge_map_from_loc_info(nodes, None); - println!("{:?}", edge_map); - assert!(edge_map.len() <= 3); - } - - #[test] - fn test_mock_edge_map_with_single_node() { - let meshnodes = mock_meshnode_packets(1); - let mut nodes = HashMap::new(); - for node in meshnodes { - nodes.insert(node.node_num, node); - } - let edge_map = mock_edge_map_from_loc_info(nodes, None); - println!("{:?}", edge_map); - assert_eq!(edge_map.len(), 0); - } - - // Set radius to 1 cm. No edges should be generated, although statistically there is a tiny chance we get one - #[test] - fn test_mock_edge_map_with_small_radius() { - let meshnodes = mock_meshnode_packets(3); - let mut nodes = HashMap::new(); - for node in meshnodes { - nodes.insert(node.node_num, node); - } - let edge_map = mock_edge_map_from_loc_info(nodes, Some(0.00001)); - println!("{:?}", edge_map); - assert_eq!(edge_map.len(), 0); - } -} diff --git a/src-tauri/src/constructors/mock/mod.rs b/src-tauri/src/constructors/mock/mod.rs deleted file mode 100644 index 4c98279a..00000000 --- a/src-tauri/src/constructors/mock/mod.rs +++ /dev/null @@ -1 +0,0 @@ -pub mod mocks; diff --git a/src-tauri/src/constructors/mod.rs b/src-tauri/src/constructors/mod.rs deleted file mode 100644 index 22eea4fd..00000000 --- a/src-tauri/src/constructors/mod.rs +++ /dev/null @@ -1,2 +0,0 @@ -pub mod init; -pub mod mock; diff --git a/src-tauri/src/data_conversion/distance_constants.rs b/src-tauri/src/data_conversion/distance_constants.rs deleted file mode 100644 index 67a5cfa3..00000000 --- a/src-tauri/src/data_conversion/distance_constants.rs +++ /dev/null @@ -1,12 +0,0 @@ -// public protobuf conversion factors, used to load graph and mock data. See mesh.proto - -/* Lat: 1e-7 conversion from int to floating point degrees; see mesh.proto */ -pub const LAT_CONVERSION_FACTOR: f64 = 1e-7; -/* Longitude: 1e-7 conversion from int to floating point degrees; see mesh.proto */ -pub const LON_CONVERSION_FACTOR: f64 = 1e-7; -/* Altitude: in meters above sea level, no conversion needed */ -pub const ALT_CONVERSION_FACTOR: f64 = 1.0; -// radius of the earth in km -pub const RADIUS_EARTH_KM: f64 = 6371.0; -// speed in m/s -pub const SPEED_CONVERSION_FACTOR: f64 = 1.0; diff --git a/src-tauri/src/data_conversion/distance_conversion.rs b/src-tauri/src/data_conversion/distance_conversion.rs deleted file mode 100644 index c0de8e89..00000000 --- a/src-tauri/src/data_conversion/distance_conversion.rs +++ /dev/null @@ -1,77 +0,0 @@ -use crate::data_conversion::distance_constants::{ - ALT_CONVERSION_FACTOR, LAT_CONVERSION_FACTOR, LON_CONVERSION_FACTOR, RADIUS_EARTH_KM, -}; -use crate::device::MeshNode; - -/* -* Calculates the distance between two points on a sphere using helpers in graph snapshot -* Returns distance in kilometers -* -* Conversion function: -* Lat/Long: 1e-7 conversion from int to floating point degrees; see mesh.proto -* Altitude: in meters above sea level, no conversion needed -*/ -pub fn get_spherical_distance(node_1: Option<&MeshNode>, node_2: Option<&MeshNode>) -> Option { - match (node_1, node_2) { - (Some(node_1), Some(node_2)) => { - let node_1_pos = &node_1.position_metrics.last(); - let node_2_pos = &node_2.position_metrics.last(); - - match (node_1_pos, node_2_pos) { - (Some(node_1_pos), Some(node_2_pos)) => Some(total_distance( - node_1_pos.latitude as f64 * LAT_CONVERSION_FACTOR, - node_1_pos.longitude as f64 * LON_CONVERSION_FACTOR, - node_1_pos.altitude as f64 * ALT_CONVERSION_FACTOR, - node_2_pos.latitude as f64 * LAT_CONVERSION_FACTOR, - node_2_pos.longitude as f64 * LON_CONVERSION_FACTOR, - node_2_pos.altitude as f64 * ALT_CONVERSION_FACTOR, - )), - _ => None, - } - } - _ => None, - } -} - -/// Returns total distance between 2 nodes using euclidean of haversine and altitude difference. -/// -/// # Arguments -/// -/// * `lat1` - latitude of node 1 -/// * `lon1` - longitude of node 1 -/// * `alt1` - altitude of node 1 -/// * `lat2` - latitude of node 2 -/// * `lon2` - longitude of node 2 -/// * `alt2` - altitude of node 2 -pub fn total_distance(lat1: f64, lon1: f64, alt1: f64, lat2: f64, lon2: f64, alt2: f64) -> f64 { - let haversine_distance = haversine_distance(lat1, lon1, lat2, lon2).powi(2); - let alt_difference = (alt1 - alt2).powi(2); - (haversine_distance + alt_difference).sqrt() -} - -/// Returns Haversine distance between 2 nodes using their lat and long -/// https://en.wikipedia.org/wiki/Haversine_formula -/// -/// # Arguments -/// -/// * `lat1` - latitude of node 1 -/// * `lon1` - longitude of node 1 -/// * `lat2` - latitude of node 2 -/// * `lon2` - longitude of node 2 -fn haversine_distance(lat1: f64, lon1: f64, lat2: f64, lon2: f64) -> f64 { - let r = RADIUS_EARTH_KM; - let d_lat = (lat2 - lat1).to_radians(); - let d_lon = (lon2 - lon1).to_radians(); - let a = (d_lat / 2.0).sin().powi(2) - + (d_lon / 2.0).sin().powi(2) * lat1.to_radians().cos() * lat2.to_radians().cos(); - let c = 2.0 * a.sqrt().atan2((1.0 - a).sqrt()); - r * c -} - -// Convert gps degrees to protobuf position field -pub fn gps_degrees_to_protobuf_field(lat: f64, lon: f64, alt: f64) -> (i32, i32, i32) { - let lat_i = (lat / LAT_CONVERSION_FACTOR) as i32; - let lon_i = (lon / LON_CONVERSION_FACTOR) as i32; - let alt_i: i32 = (alt / ALT_CONVERSION_FACTOR) as i32; - (lat_i, lon_i, alt_i) -} diff --git a/src-tauri/src/data_conversion/mod.rs b/src-tauri/src/data_conversion/mod.rs deleted file mode 100644 index 8ddab3d3..00000000 --- a/src-tauri/src/data_conversion/mod.rs +++ /dev/null @@ -1,2 +0,0 @@ -pub mod distance_constants; -pub mod distance_conversion; diff --git a/src-tauri/src/device/mod.rs b/src-tauri/src/device/mod.rs index 921dbb38..ea8bf28f 100644 --- a/src-tauri/src/device/mod.rs +++ b/src-tauri/src/device/mod.rs @@ -9,7 +9,6 @@ use self::helpers::{ convert_location_field_to_protos, generate_rand_id, get_current_time_u32, normalize_location_field, }; -use crate::graph::graph_ds::Graph; pub mod handlers; pub mod helpers; @@ -364,23 +363,3 @@ impl MeshDevice { } } } - -/* - * Just as the MeshDevice struct contains all the information about a device (in raw packet form) - * the MeshGraph struct contains the network info in raw graph form. This is synchronized with - * the MeshDevice struct, and is used to generate the graph visualization/algorithm - * results (see analytics). - */ - -#[derive(Clone, Debug)] -pub struct MeshGraph { - pub graph: Graph, -} - -impl MeshGraph { - pub fn new() -> Self { - Self { - graph: Graph::new(), - } - } -} diff --git a/src-tauri/src/device/state.rs b/src-tauri/src/device/state.rs index f45a6487..af02f703 100644 --- a/src-tauri/src/device/state.rs +++ b/src-tauri/src/device/state.rs @@ -5,13 +5,11 @@ use meshtastic::protobufs; use super::helpers::get_current_time_u32; use super::{ - ChannelMessagePayload, ChannelMessageWithState, MeshChannel, MeshDevice, MeshGraph, MeshNode, + ChannelMessagePayload, ChannelMessageWithState, MeshChannel, MeshDevice, MeshNode, MeshNodeDeviceMetrics, MeshNodeEnvironmentMetrics, NeighborInfoPacket, NormalizedWaypoint, PositionPacket, SerialDeviceStatus, TelemetryPacket, TextPacket, UserPacket, WaypointPacket, }; -use crate::constructors::init::init_edge_map::init_edge_map; -use crate::constructors::init::init_graph::init_graph; use crate::device::{ChannelMessageState, LastHeardMetadata}; impl MeshDevice { @@ -357,11 +355,3 @@ impl MeshDevice { } } } - -impl MeshGraph { - pub fn regenerate_graph_from_device_info(&mut self, device: &MeshDevice) { - let edge_hashmap = init_edge_map(&device.neighbors); - self.graph = init_graph(&edge_hashmap, &device.nodes); - trace!("Graph: {:?}", self.graph); - } -} diff --git a/src-tauri/src/graph/edge.rs b/src-tauri/src/graph/edge.rs deleted file mode 100644 index 2a4684ca..00000000 --- a/src-tauri/src/graph/edge.rs +++ /dev/null @@ -1,55 +0,0 @@ -use serde::{Deserialize, Serialize}; - -#[derive(Debug, Serialize, Deserialize)] -pub struct Edge { - pub u: petgraph::graph::NodeIndex, - pub v: petgraph::graph::NodeIndex, - pub weight: f64, -} - -// Add hash operator to Edge -impl std::hash::Hash for Edge { - fn hash(&self, state: &mut H) { - self.u.hash(state); - self.v.hash(state); - } -} - -impl Edge { - pub fn new(u: petgraph::graph::NodeIndex, v: petgraph::graph::NodeIndex, weight: f64) -> Edge { - Edge { u, v, weight } - } - - pub fn get_u(&self) -> petgraph::graph::NodeIndex { - self.u - } - - pub fn get_v(&self) -> petgraph::graph::NodeIndex { - self.v - } - - pub fn get_weight(&self) -> f64 { - self.weight - } -} - -/// Add clone trait to Edge -impl Clone for Edge { - fn clone(&self) -> Self { - Edge { - u: self.u, - v: self.v, - weight: self.weight, - } - } -} - -/// Add eq operator to Edge -impl std::cmp::Eq for Edge {} - -/// Add equality operator to Edge -impl PartialEq for Edge { - fn eq(&self, other: &Self) -> bool { - self.u == other.u && self.v == other.v - } -} diff --git a/src-tauri/src/graph/graph_ds.rs b/src-tauri/src/graph/graph_ds.rs deleted file mode 100644 index d04e2bce..00000000 --- a/src-tauri/src/graph/graph_ds.rs +++ /dev/null @@ -1,784 +0,0 @@ -#![allow(dead_code)] -use crate::analytics::algorithms::diffusion_centrality::results::EigenvalsResult; -use crate::graph::edge::Edge; -use crate::graph::node::Node; - -use log::warn; -use nalgebra::DMatrix; -use petgraph::prelude::*; -use petgraph::stable_graph::StableUnGraph; -use serde::{Deserialize, Serialize}; -use std::{collections::HashMap, fmt::Display}; - -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct Graph { - pub g: StableGraph, - pub node_idx_map: HashMap, - pub edge_idx_map: HashMap< - (petgraph::graph::NodeIndex, petgraph::graph::NodeIndex), - Vec, - >, -} - -impl Graph { - /// Creates a new graph and returns it. - pub fn new() -> Graph { - Graph { - g: StableUnGraph::::default(), - node_idx_map: HashMap::new(), - edge_idx_map: HashMap::new(), - } - } - - /// Add a node to the graph. Returns the node index. - /// - /// # Arguments - /// - /// * `name` - String identifier of the node. - pub fn add_node(&mut self, name: String) -> petgraph::graph::NodeIndex { - let node = Node::new(name); - let node_idx = self.g.add_node(node.clone()); - self.node_idx_map.insert(node.name, node_idx); - node_idx - } - - /// Does the same thing as add_node but accepts a node struct as input. - /// - /// # Arguments - /// - /// * `node` - Node struct. - pub fn add_node_from_struct(&mut self, node: Node) -> petgraph::graph::NodeIndex { - let node_idx = self.g.add_node(node.clone()); - self.node_idx_map.insert(node.name, node_idx); - node_idx - } - - /// Removes node from the graph (and all edges connected to it). Does not return anything. - /// - /// # Arguments - /// - /// * `node_idx` - Node index of the node to be removed. - pub fn remove_node(&mut self, node: petgraph::graph::NodeIndex) { - let node_u = self.g.node_weight(node).unwrap().clone(); - - for neighbor_node in self.get_neighbors_idx(node_u.name.clone()) { - let node_v = self.g.node_weight(neighbor_node).unwrap(); - self.remove_edge(node_u.name.clone(), node_v.name.clone(), None, Some(false)); - } - - self.g.remove_node(node); - } - - /// Updates the weight of the node. Does not return anything. - /// - /// # Arguments - /// - /// * `idx` - Index of the node we want to update, represented as NodeIndex - /// * `new_weight` - New weight of the node - pub fn change_node_opt_weight(&mut self, idx: petgraph::graph::NodeIndex, new_weight: f64) { - let mut node = self.g.node_weight_mut(idx).unwrap(); - node.optimal_weighted_degree = new_weight; - } - - /// Returns a boolean signalling whether the graph contains a node - /// - /// # Arguments - /// - /// * `name` - Name of the node - pub fn contains_node(&mut self, name: String) -> bool { - self.node_idx_map.contains_key(&name) - } - - /// Returns whether the graph contains an edge - /// - /// # Arguments - /// - /// * `node1` - Name of the first node - /// * `node2` - Name of the second node - pub fn contains_edge(&mut self, node1: &String, node2: &String) -> bool { - let node1_idx = self.get_node_idx(node1); - let node2_idx = self.get_node_idx(node2); - - let contains_key = self.edge_idx_map.contains_key(&(node1_idx, node2_idx)); - let is_empty = self - .edge_idx_map - .get(&(node1_idx, node2_idx)) - .unwrap_or(&vec![]) - .is_empty(); - - contains_key && !is_empty - } - - /// Returns the edge between two nodes - /// - /// # Arguments - /// - /// * `node1` - Name of the first node - /// * `node2` - Name of the second node - /// - /// # Returns - /// - /// * `Option` - The edge between the two nodes if it exists - pub fn get_edge(&mut self, node1: &String, node2: &String) -> Option { - if self.contains_edge(node1, node2) { - let node1_idx = self.get_node_idx(node1); - let node2_idx = self.get_node_idx(node2); - - let edge_idx = self.edge_idx_map.get(&(node1_idx, node2_idx)).unwrap()[0]; - return Some(self.g.edge_weight(edge_idx).unwrap().clone()); - } - None - } - - /// Adds the edge to the graph and insert the edge index into the edge_idx_map - /// (where the key is the tuple of the node indices and the value is the list - /// of edges). We maintain a list because we allow parallel edges to exist. - /// Does not return anything. - /// - /// # Arguments - /// - /// * `u` - String identifier of the first node - /// * `v` - String identifier of the second node - /// * `weight` - Float weight of the edge - pub fn add_edge(&mut self, u: String, v: String, weight: f64) { - if !self.node_idx_map.contains_key(&u) { - let error_message = format!("Node {} does not exist", u); - return print_error_and_return(&error_message); - } - if !self.node_idx_map.contains_key(&v) { - let error_message = format!("Node {} does not exist", v); - return print_error_and_return(&error_message); - } - - let u_idx = *self.node_idx_map.get(&u).unwrap(); - let v_idx = *self.node_idx_map.get(&v).unwrap(); - - let node_u = self.g.node_weight(u_idx).unwrap().clone(); - let node_v = self.g.node_weight(v_idx).unwrap().clone(); - - let edge = Edge::new(u_idx, v_idx, weight); - - let edge_idx = self.g.add_edge(u_idx, v_idx, edge); - - // Insert new edge into edge_idx_map which maps (u, v) to a list of edge indices - let edge_idx_list: &mut Vec = - self.edge_idx_map.entry((u_idx, v_idx)).or_default(); - - edge_idx_list.push(edge_idx); - - // Insert new edge into edge_idx_map which maps (v, u) to a list of edge indices - let edge_idx_list: &mut Vec = - self.edge_idx_map.entry((v_idx, u_idx)).or_default(); - - edge_idx_list.push(edge_idx); - - let updated_weight_u = node_u.optimal_weighted_degree + weight; - let updated_weight_v = node_v.optimal_weighted_degree + weight; - - self.change_node_opt_weight(u_idx, updated_weight_u); - self.change_node_opt_weight(v_idx, updated_weight_v); - } - - /// Does the same thing as add_edge but accepts edge struct as input. - /// - /// # Arguments - /// - /// * `edge` - Edge struct - pub fn add_edge_from_struct(&mut self, edge: Edge) { - let u_idx = edge.get_u(); - let v_idx = edge.get_v(); - - let node_u = self.g.node_weight(u_idx).unwrap().clone(); - let node_v = self.g.node_weight(v_idx).unwrap().clone(); - - let weight = edge.get_weight(); - - let edge_idx = self.g.add_edge(edge.get_u(), edge.get_v(), edge); - - // Insert new edge into edge_idx_map which maps (u, v) to a list of edge indices - let edge_idx_list: &mut Vec = - self.edge_idx_map.entry((u_idx, v_idx)).or_default(); - - edge_idx_list.push(edge_idx); - - // Insert new edge into edge_idx_map which maps (v, u) to a list of edge indices - let edge_idx_list: &mut Vec = - self.edge_idx_map.entry((v_idx, u_idx)).or_default(); - - edge_idx_list.push(edge_idx); - - let updated_weight_u = node_u.optimal_weighted_degree + weight; - let updated_weight_v = node_v.optimal_weighted_degree + weight; - - self.change_node_opt_weight(u_idx, updated_weight_u); - self.change_node_opt_weight(v_idx, updated_weight_v); - } - - /// Updates the weight of the edge. Does not return anything. - /// - /// # Arguments - /// - /// * `u` - String identifier of the first node - /// * `v` - String identifier of the second node - /// * `weight` - Float weight of the edge - /// * `parallel_edge_idx` - Optional usize index of the parallel edge we want to update. - pub fn update_edge( - &mut self, - u: String, - v: String, - weight: f64, - parallel_edge_idx: Option, - update_all_parallel: Option, - ) { - if !self.node_idx_map.contains_key(&u) { - let error_message = format!("Node {} does not exist", u); - return print_error_and_return(&error_message); - } - if !self.node_idx_map.contains_key(&v) { - let error_message = format!("Node {} does not exist", v); - return print_error_and_return(&error_message); - } - - let u_idx = *self.node_idx_map.get(&u).unwrap(); - let v_idx = *self.node_idx_map.get(&v).unwrap(); - - // Check if edge does not exist - if !self.g.contains_edge(u_idx, v_idx) { - self.add_edge(u, v, weight); - return; - } - - let edge_idx_list = self.edge_idx_map.get(&(u_idx, v_idx)).unwrap().clone(); - - let old_weight = self - .g - .edge_weight(edge_idx_list[parallel_edge_idx.unwrap_or(0)]) - .unwrap() - .weight; - - let updated_weight_u = - self.g.node_weight(u_idx).unwrap().optimal_weighted_degree + weight - old_weight; - - let updated_weight_v = - self.g.node_weight(v_idx).unwrap().optimal_weighted_degree + weight - old_weight; - - self.change_node_opt_weight(u_idx, updated_weight_u); - self.change_node_opt_weight(v_idx, updated_weight_v); - - if !update_all_parallel.unwrap_or(false) { - let edge = Edge::new(u_idx, v_idx, weight); - - let edge_idx = self.g.update_edge(u_idx, v_idx, edge); - - // Update edge_idx_map to reflect the new edge index - let edge_idx_list: &mut Vec = - self.edge_idx_map.entry((u_idx, v_idx)).or_default(); - - edge_idx_list[parallel_edge_idx.unwrap_or(0)] = edge_idx; - - // Update edge_idx_map to reflect the new edge index - let edge_idx_list = self.edge_idx_map.entry((v_idx, u_idx)).or_default(); - - edge_idx_list[parallel_edge_idx.unwrap_or(0)] = edge_idx; - } else { - for parallel_edge_idx_iterator in 0..edge_idx_list.len() { - let edge = Edge::new(u_idx, v_idx, weight); - - let edge_idx = self.g.update_edge(u_idx, v_idx, edge); - - // Update edge_idx_map to reflect the new edge index - let edge_idx_list: &mut Vec = - self.edge_idx_map.entry((u_idx, v_idx)).or_default(); - - edge_idx_list[parallel_edge_idx_iterator] = edge_idx; - - // Update edge_idx_map to reflect the new edge index - let edge_idx_list: &mut Vec = - self.edge_idx_map.entry((v_idx, u_idx)).or_default(); - - edge_idx_list[parallel_edge_idx_iterator] = edge_idx; - } - } - } - - /// Returns the weight of the edge between the two nodes. - /// - /// # Arguments - /// - /// * `u` - String identifier of the first node - /// * `v` - String identifier of the second node - /// * `parallel_edge_idx` - Optional usize index of the parallel edge we want to update. - /// * `get_all_parallel` - Optional bool flag to get weight sum of all parallel edges. - pub fn get_edge_weight( - &mut self, - u: &String, - v: &String, - parallel_edge_idx: Option, - get_all_parallel: Option, - ) -> f64 { - if !self.node_idx_map.contains_key(u) { - warn!( - "{}", - format!("Node {} does not exist, returning empty edge weight", u) - ); - return 0.0; - } - - if !self.node_idx_map.contains_key(v) { - warn!( - "{}", - format!("Node {} does not exist, returning empty edge weight", v) - ); - return 0.0; - } - - if !self.contains_edge(u, v) { - warn!( - "Graph doesn't contain edge: ({}, {}), returning empty edge weight", - u, v - ); - return 0.0; - } - - let u_idx = self.node_idx_map.get(u).unwrap(); - let v_idx = self.node_idx_map.get(v).unwrap(); - - let edge_idx_list = self.edge_idx_map.get(&(*u_idx, *v_idx)).unwrap(); - - let mut weight = 0.0; - if get_all_parallel.unwrap_or(false) { - weight = self - .g - .edge_weight(edge_idx_list.clone()[parallel_edge_idx.unwrap_or(0)]) - .unwrap() - .weight; - } else { - for edge_idx in edge_idx_list { - weight += self.g.edge_weight(*edge_idx).unwrap().weight; - } - } - - weight - } - - /// Removes an edge from the graph. Does not return anything. - /// - /// # Arguments - /// - /// * `u` - String identifier of the first node - /// * `v` - String identifier of the second node - /// * `parallel_edge_idx` - Optional usize index of the parallel edge we want to remove. - /// * `remove_all_parallel` - Optional bool flag to remove all parallel edges. - pub fn remove_edge( - &mut self, - u: String, - v: String, - parallel_edge_idx: Option, - remove_all_parallel: Option, - ) { - if !self.node_idx_map.contains_key(&u) { - let error_message = format!("Node {} does not exist", u); - return print_error_and_return(&error_message); - } - if !self.node_idx_map.contains_key(&v) { - let error_message = format!("Node {} does not exist", v); - return print_error_and_return(&error_message); - } - - let u_idx = *self.node_idx_map.get(&u).unwrap(); - let v_idx = *self.node_idx_map.get(&v).unwrap(); - - // Check if edge does not exist - if !self.g.contains_edge(u_idx, v_idx) { - warn!("Edge: ({}, {}) does not exist", u, v); - return print_error_and_return("Edge does not exist"); - } - - let edge_idx_list = self.edge_idx_map.get(&(u_idx, v_idx)).unwrap().clone(); - - // If remove_all_parallel is false, then remove the single edge in the list whose - // index is parallel_edge_idx (default 0). - if !remove_all_parallel.unwrap_or(false) { - let weight = self - .g - .edge_weight(edge_idx_list[parallel_edge_idx.unwrap_or(0)]) - .unwrap_or(&Edge::new(u_idx, v_idx, 0.0)) - .weight; - - let node_u = self.g.node_weight(u_idx).unwrap(); - let node_v = self.g.node_weight(v_idx).unwrap(); - - let u_weight = node_u.optimal_weighted_degree - weight; - let v_weight = node_v.optimal_weighted_degree - weight; - - self.g - .remove_edge(edge_idx_list[parallel_edge_idx.unwrap_or(0)]); - - let edge_idx_list_mut: &mut Vec = - self.edge_idx_map.entry((v_idx, u_idx)).or_default(); - - edge_idx_list_mut.swap_remove(parallel_edge_idx.unwrap_or(0)); - - let edge_idx_list_mut: &mut Vec = - self.edge_idx_map.entry((u_idx, v_idx)).or_default(); - - edge_idx_list_mut.swap_remove(parallel_edge_idx.unwrap_or(0)); - - self.change_node_opt_weight(u_idx, u_weight); - self.change_node_opt_weight(v_idx, v_weight); - } else { - let node_u = self.g.node_weight(u_idx).unwrap(); - let node_v = self.g.node_weight(v_idx).unwrap(); - - let u_weight = node_u.optimal_weighted_degree; - let v_weight = node_v.optimal_weighted_degree; - - // If remove_all_parallel is true, then remove all edges in the list. - for edge_idx in edge_idx_list { - let weight = self.g.edge_weight(edge_idx).unwrap().weight; - - self.g.remove_edge(edge_idx); - - self.change_node_opt_weight(u_idx, u_weight - weight); - self.change_node_opt_weight(v_idx, v_weight - weight); - } - - self.edge_idx_map.remove(&(u_idx, v_idx)); - self.edge_idx_map.remove(&(v_idx, u_idx)); - } - } - - /// Converts a graph to an adjacency matrix. - /// - /// # Arguments - /// - /// * `graph` - a graph - /// - /// # Returns - /// - /// * `Vec>` - an adjacency matrix - pub fn convert_to_adj_matrix(&self) -> (Vec>, HashMap, DMatrix) { - let mut adj_matrix = Vec::new(); - - let nodes = self.get_nodes(); - let edges = self.get_edges(); - - let mut node_id_to_int = HashMap::new(); - let mut int_to_node_id = HashMap::new(); - - for (idx, node) in nodes.iter().enumerate() { - node_id_to_int.insert(node.name.clone(), idx); - int_to_node_id.insert(idx, node.name.clone()); - } - - for _ in 0..nodes.len() { - adj_matrix.push(vec![0.0; nodes.len()]); - } - - for edge in edges { - let u_name = self - .get_node(edge.get_u()) - .expect("Index from edge should exist") - .name; - - let v_name = self - .get_node(edge.get_v()) - .expect("Index from edge should exist") - .name; - - let u_id = node_id_to_int - .get(&u_name) - .expect("Name from edge should exist"); - - let v_id = node_id_to_int - .get(&v_name) - .expect("Name from edge should exist"); - - let weight = edge.get_weight(); - - adj_matrix[*u_id][*v_id] = weight; - adj_matrix[*v_id][*u_id] = weight; - } - - let n = self.get_order(); - let flattened_matrix = &adj_matrix - .iter() - .flat_map(|row| row.iter()) - .copied() - .collect::>(); - - let d_adj_matrix = DMatrix::from_row_slice(n, n, flattened_matrix); - - (adj_matrix, int_to_node_id, d_adj_matrix) - } - - pub fn eigenvals(&self, adj_matrix: &DMatrix) -> EigenvalsResult { - let schur = adj_matrix.clone().schur(); - - // Ensure eigenvalues are real - let eigenvalues = match schur.eigenvalues() { - Some(vals) => vals, - None => return EigenvalsResult::Error("Eigenvalues are not real.".to_string()), - }; - - let eigenvalues_vec = eigenvalues.data.as_vec().clone(); - EigenvalsResult::Success(eigenvalues_vec) - } - - /// Returns the number of nodes in the graph. - pub fn get_order(&self) -> usize { - self.g.node_count() - } - - /// Returns the number of edges in the graph. - pub fn get_size(&self) -> usize { - self.g.edge_count() - } - - /// Clones the graph and returns it. - pub fn clone(&self) -> Graph { - Graph { - g: self.g.clone(), - node_idx_map: self.node_idx_map.clone(), - edge_idx_map: self.edge_idx_map.clone(), - } - } - - /// Returns all the nodes in the graph. - pub fn get_nodes(&self) -> Vec { - let mut nodes = Vec::new(); - for node in self.g.node_weights() { - nodes.push(node.clone()); - } - nodes - } - - /// Returns the node associated with the given node index. - /// - /// # Arguments - /// - /// * `node_idx` - NodeIndex of the node we want to get. - pub fn get_node(&self, idx: petgraph::graph::NodeIndex) -> Option { - Some(self.g.node_weight(idx)?.clone()) - } - - /// Returns the node associated with the given node index. - /// - /// # Arguments - /// - /// * `node_idx` - NodeIndex of the node we want to get. - pub fn get_node_mut(&mut self, idx: petgraph::graph::NodeIndex) -> Option<&mut Node> { - self.g.node_weight_mut(idx) - } - - /// Returns the node index associated with the given node identifier. - /// - /// # Arguments - /// - /// * `node_id` - String identifier of the node we want to get. - pub fn get_node_idx(&self, node: &String) -> petgraph::graph::NodeIndex { - *self.node_idx_map.get(node).unwrap() - } - - /// Returns a list of all the edges in the graph. - pub fn get_edges(&self) -> Vec { - let mut edges = Vec::new(); - for edge in self.g.edge_weights() { - edges.push(edge.clone()); - } - edges - } - - /// Returns the nodes connected to the given node. - /// - /// # Arguments - /// - /// * `node` - String identifier of the node we want to get the neighbors of. - pub fn get_neighbors(&self, node: String) -> Vec { - let node_weight = self.node_idx_map.get(&node).unwrap(); - let mut neighbors = Vec::new(); - for neighbor in self.g.neighbors_undirected(*node_weight) { - neighbors.push(self.g.node_weight(neighbor).unwrap().clone()); - } - - neighbors - } - - /// Returns the indices of the nodes connected to the given node. - /// - /// # Arguments - /// - /// * `node` - String identifier of the node we want to get the neighbors of. - pub fn get_neighbors_idx(&self, node: String) -> Vec { - let node_weight = self.node_idx_map.get(&node).unwrap(); - let mut neighbors = Vec::new(); - for neighbor in self.g.neighbors_undirected(*node_weight) { - neighbors.push(neighbor); - } - - neighbors - } - - /// Returns the number of edges connected to the given node. - /// - /// # Arguments - /// - /// * `node` - String identifier of the node we want to get the degree of. - pub fn degree_of(&self, node: String) -> f64 { - if !self.node_idx_map.contains_key(&node) { - let error_message = format!("Node {} does not exist", node); - println!("{}", error_message); - return 0.0; - } - - let mut degree = 0.0; - let node_idx = self.node_idx_map.get(&node).unwrap(); - - for edge in self.g.edges(*node_idx) { - degree += self.g.edge_weight(edge.id()).unwrap().weight; - } - - degree - } - - /// Returns a list of cumulative edge weights. - pub fn get_cumulative_edge_weights(&self) -> Vec { - let mut cumulative_edge_weights = Vec::new(); - let mut total_weight = 0.0; - for edge in self.g.edge_weights() { - total_weight += edge.weight * 2.0; - cumulative_edge_weights.push(total_weight); - } - cumulative_edge_weights - } -} - -impl Display for Graph { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let mut s = String::new(); - - for edge in self.get_edges() { - let node_u = self.get_node(edge.u).expect("Index from edge should exist"); - let node_v = self.get_node(edge.v).expect("Index from edge should exist"); - - s.push_str(&format!( - "{} - {} {}\n", - node_u.name.clone(), - node_v.name.clone(), - edge.weight - )); - } - - write!(f, "{}", s) - } -} - -// Function to print given error and return -fn print_error_and_return(error: &str) { - eprintln!("{}", error); -} - -// Create a unit test for the Graph struct -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn initialize_graph() { - // Create a graph - let mut g = super::Graph::new(); - - // Create a few nodes and edges and add to graph - let u: String = "u".to_string(); - let v: String = "v".to_string(); - let w: String = "w".to_string(); - - let _u_idx = g.add_node(u.clone()); - let _v_idx = g.add_node(v.clone()); - let _w_idx = g.add_node(w.clone()); - - assert_eq!(g.get_order(), 3); - - g.add_edge(u.clone(), v.clone(), 1_f64); - g.add_edge(u.clone(), w.clone(), 1_f64); - g.add_edge(v.clone(), w.clone(), 35_f64); - - assert_eq!(g.get_size(), 3); - - g.update_edge(u.clone(), v, 11_f64, None, Some(false)); - g.remove_edge(u, w, None, Some(true)); - - assert_eq!(g.get_size(), 2); - } - - #[test] - fn test_parallel_edges() { - let mut g = super::Graph::new(); - - let u: String = "u".to_string(); - let v: String = "v".to_string(); - - let _u_idx = g.add_node(u.clone()); - let _v_idx = g.add_node(v.clone()); - - g.add_edge(u.clone(), v.clone(), 1_f64); - g.add_edge(u.clone(), v.clone(), 2_f64); - - assert_eq!(g.get_size(), 2); - - // update edge - g.update_edge(u, v, 11_f64, Some(0), None); - } - - #[test] - fn test_adj_matrix() { - let mut g1 = super::Graph::new(); - - // Create a few nodes and edges and add to graph - let a = "a".to_string(); - let b = "b".to_string(); - let c = "c".to_string(); - let d = "d".to_string(); - - let mut a_node = Node::new(a); - a_node.set_gps(-72.28486, 43.71489, 1.0); - let a_idx = g1.add_node_from_struct(a_node); - - let mut b_node = Node::new(b); - b_node.set_gps(-72.28239, 43.71584, 1.0); - let b_idx = g1.add_node_from_struct(b_node); - - let mut c_node = Node::new(c); - c_node.set_gps(-72.28332, 43.7114, 1.0); - let c_idx = g1.add_node_from_struct(c_node); - - let mut d_node = Node::new(d); - d_node.set_gps(-72.28085, 43.71235, 1.0); - let d_idx = g1.add_node_from_struct(d_node); - - let a_b = Edge::new(a_idx, b_idx, 0.51); - g1.add_edge_from_struct(a_b); - - let a_c = Edge::new(a_idx, c_idx, 0.39); - g1.add_edge_from_struct(a_c); - - let b_c = Edge::new(b_idx, c_idx, 0.4); - g1.add_edge_from_struct(b_c); - - let b_d = Edge::new(b_idx, d_idx, 0.6); - g1.add_edge_from_struct(b_d); - - let (adj_matrix, _int_to_node_id, _d_adj) = g1.convert_to_adj_matrix(); - - // assert that the adjacency matrix is correct - assert_eq!( - adj_matrix, - vec![ - vec![0.0, 0.51, 0.39, 0.0], - vec![0.51, 0.0, 0.4, 0.6], - vec![0.39, 0.4, 0.0, 0.0], - vec![0.0, 0.6, 0.0, 0.0] - ] - ); - } -} diff --git a/src-tauri/src/graph/mod.rs b/src-tauri/src/graph/mod.rs deleted file mode 100644 index 7f45555f..00000000 --- a/src-tauri/src/graph/mod.rs +++ /dev/null @@ -1,3 +0,0 @@ -pub mod edge; -pub mod graph_ds; -pub mod node; diff --git a/src-tauri/src/graph/node.rs b/src-tauri/src/graph/node.rs deleted file mode 100644 index 99e0d14b..00000000 --- a/src-tauri/src/graph/node.rs +++ /dev/null @@ -1,80 +0,0 @@ -use serde::{Deserialize, Serialize}; -#[derive(Debug, Serialize, Deserialize)] -pub struct Node { - pub name: String, - pub optimal_weighted_degree: f64, - pub longitude: f64, - pub latitude: f64, - pub altitude: f64, - pub speed: f64, - pub direction: f64, -} - -impl Node { - pub fn new(name: String) -> Node { - Node { - name, - optimal_weighted_degree: 0.0, - longitude: 0.0, - latitude: 0.0, - altitude: 0.0, - speed: 0.0, - direction: 0.0, - } - } - - pub fn set_gps(&mut self, longitude: f64, latitude: f64, altitude: f64) { - self.longitude = longitude; - self.latitude = latitude; - self.altitude = altitude; - } -} - -/// Add clone trait to Node -impl Clone for Node { - fn clone(&self) -> Self { - Node { - name: self.name.clone(), - optimal_weighted_degree: self.optimal_weighted_degree, - longitude: self.longitude, - latitude: self.latitude, - altitude: self.altitude, - speed: self.speed, - direction: self.direction, - } - } -} - -/// Add hash to Node so that we can use it as a key in a HashMap -impl std::hash::Hash for Node { - fn hash(&self, state: &mut H) { - self.name.hash(state); - } -} - -// Add equality operator to Node -impl std::cmp::Eq for Node {} - -// Add partial equality operator to Node -impl std::cmp::PartialEq for Node { - fn eq(&self, other: &Self) -> bool { - self.name == other.name - } -} - -// Add partial ordering operator to Node using optimal_weighted_degree -impl std::cmp::PartialOrd for Node { - fn partial_cmp(&self, other: &Self) -> Option { - self.optimal_weighted_degree - .partial_cmp(&other.optimal_weighted_degree) - } -} - -// Add Ord trait to Node using optimal_weighted_degree -impl std::cmp::Ord for Node { - fn cmp(&self, other: &Self) -> std::cmp::Ordering { - self.optimal_weighted_degree - .partial_cmp(&other.optimal_weighted_degree) - .unwrap() - } -} diff --git a/src-tauri/src/ipc/commands/connections.rs b/src-tauri/src/ipc/commands/connections.rs index 2639e705..ef3cda3f 100644 --- a/src-tauri/src/ipc/commands/connections.rs +++ b/src-tauri/src/ipc/commands/connections.rs @@ -49,7 +49,6 @@ async fn create_new_connection( app_handle: tauri::AppHandle, mesh_devices: tauri::State<'_, state::MeshDevices>, radio_connections: tauri::State<'_, state::RadioConnections>, - mesh_graph: tauri::State<'_, state::NetworkGraph>, ) -> Result<(), CommandError> where S: AsyncReadExt + AsyncWriteExt + Send + 'static, @@ -74,7 +73,6 @@ where let handle = app_handle.clone(); let mesh_devices_arc = mesh_devices.inner.clone(); let radio_connections_arc = radio_connections.inner.clone(); - let graph_arc = mesh_graph.inner.clone(); // Persist device struct in Tauri state { @@ -100,13 +98,7 @@ where // Spawn decoded packet handler to route decoded packets - spawn_decoded_handler( - handle, - decoded_listener, - mesh_devices_arc, - graph_arc, - device_key, - ); + spawn_decoded_handler(handle, decoded_listener, mesh_devices_arc, device_key); Ok(()) } @@ -120,7 +112,6 @@ pub async fn connect_to_serial_port( app_handle: tauri::AppHandle, mesh_devices: tauri::State<'_, state::MeshDevices>, radio_connections: tauri::State<'_, state::RadioConnections>, - mesh_graph: tauri::State<'_, state::NetworkGraph>, ) -> Result<(), CommandError> { debug!( "Called connect_to_serial_port command with port \"{}\"", @@ -140,7 +131,6 @@ pub async fn connect_to_serial_port( app_handle, mesh_devices, radio_connections, - mesh_graph, ) .await?; @@ -153,7 +143,6 @@ pub async fn connect_to_tcp_port( app_handle: tauri::AppHandle, mesh_devices: tauri::State<'_, state::MeshDevices>, radio_connections: tauri::State<'_, state::RadioConnections>, - mesh_graph: tauri::State<'_, state::NetworkGraph>, ) -> Result<(), CommandError> { debug!( "Called connect_to_tcp_port command with address \"{}\"", @@ -173,7 +162,6 @@ pub async fn connect_to_tcp_port( app_handle, mesh_devices, radio_connections, - mesh_graph, ) .await?; diff --git a/src-tauri/src/ipc/commands/graph.rs b/src-tauri/src/ipc/commands/graph.rs deleted file mode 100644 index ddb5e8fe..00000000 --- a/src-tauri/src/ipc/commands/graph.rs +++ /dev/null @@ -1,214 +0,0 @@ -use crate::analytics::algorithms::articulation_point::results::APResult; -use crate::analytics::algorithms::diffusion_centrality::results::DiffCenResult; -use crate::analytics::algorithms::stoer_wagner::results::MinCutResult; -use crate::analytics::state::configuration::AlgorithmConfigFlags; -use crate::device::NormalizedPosition; -use crate::ipc; -use crate::ipc::helpers::node_index_to_node_id; -use crate::ipc::APMincutStringResults; -use crate::ipc::CommandError; -use crate::state; - -use log::{debug, error, trace}; -use serde::Deserialize; -use serde::Serialize; -use serde_json::json; -use std::collections::HashMap; - -#[tauri::command] -pub async fn initialize_graph_state( - mesh_graph: tauri::State<'_, state::NetworkGraph>, - algo_state: tauri::State<'_, state::AnalyticsState>, -) -> Result<(), CommandError> { - debug!("Called initialize_graph_state command"); - ipc::helpers::initialize_graph_state(mesh_graph, algo_state).await -} - -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct GraphGeoJSONResult { - pub nodes: geojson::FeatureCollection, - pub edges: geojson::FeatureCollection, -} - -pub fn generate_node_properties(num: u32) -> serde_json::Map { - let mut properties = serde_json::Map::new(); - properties.insert("num".into(), json!(num)); - properties -} - -pub fn generate_edge_properties(snr: f64) -> serde_json::Map { - let mut properties = serde_json::Map::new(); - properties.insert("snr".into(), json!(snr)); - properties -} - -#[tauri::command] -pub async fn get_node_edges( - mesh_graph: tauri::State<'_, state::NetworkGraph>, - connected_devices: tauri::State<'_, state::MeshDevices>, -) -> Result { - trace!("Called get_node_edges command"); - - let mut graph_guard = mesh_graph.inner.lock().await; - let _graph = graph_guard.as_mut().ok_or("Graph edges not initialized")?; - - // Generate nodes and edges from connected devices - // * Note: this makes the false assumption that all nodes are fully connected - - let devices_guard = connected_devices.inner.lock().await; - let device_edge_info = devices_guard.values().fold(vec![], |accum, d| { - let filtered_nodes = d - .nodes - .iter() - .filter_map(|(num, node)| { - let NormalizedPosition { - latitude, - longitude, - .. - } = node.position_metrics.last()?; - - if latitude == &0.0 || longitude == &0.0 { - return None; - } - - Some(( - *num, - vec![longitude.clone() as f64, latitude.clone() as f64], - )) - }) - .collect::>(); - - let mut result = vec![]; - result.extend(accum); - result.extend(filtered_nodes); - result - }); - - let mut node_features = vec![]; - let mut edge_features = vec![]; - - for (index, (num, position)) in device_edge_info.iter().enumerate() { - node_features.push(geojson::Feature { - id: Some(geojson::feature::Id::String(num.to_string())), - geometry: Some(geojson::Geometry::new(geojson::Value::Point( - position.clone(), - ))), - properties: Some(generate_node_properties(num.to_owned())), - ..Default::default() - }); - - for (idx, (_n, pos)) in device_edge_info.iter().enumerate() { - edge_features.push(geojson::Feature { - id: Some(geojson::feature::Id::String( - (index * device_edge_info.len() + idx).to_string(), - )), - geometry: Some(geojson::Geometry::new(geojson::Value::LineString(vec![ - position.clone(), - pos.clone(), - ]))), - properties: Some(generate_edge_properties(1.)), - ..Default::default() - }); - } - } - - let nodes = geojson::FeatureCollection { - bbox: None, - foreign_members: None, - features: node_features, - }; - - let edges = geojson::FeatureCollection { - bbox: None, - foreign_members: None, - // features: vec![], - features: edge_features, // * enable to see fully-connected network - }; - - trace!("Found edges {:?}", edges); - - Ok(GraphGeoJSONResult { nodes, edges }) -} - -#[tauri::command] -pub async fn run_algorithms( - flags: AlgorithmConfigFlags, - mesh_graph: tauri::State<'_, state::NetworkGraph>, - algo_state: tauri::State<'_, state::AnalyticsState>, -) -> Result { - debug!("Called run_algorithms command"); - trace!("Running algorithms with flags {:?}", flags); - - let mut guard = mesh_graph.inner.lock().await; - let mut state_guard = algo_state.inner.lock().await; - - let graph_struct = guard.as_mut().ok_or("Graph not initialized")?; - let state = state_guard.as_mut().ok_or("State not initialized")?; - - state.add_graph_snapshot(&graph_struct.graph); - state.set_algorithm_flags(flags); - state.run_algos(); - - let algo_result = state.get_algo_results(); - - debug!("Received algorithm results: {:?}", algo_result); - - // convert AP from a vector of NodeIndexes to a vector of IDs (strings) - let ap_vec: Vec = match &algo_result.aps { - APResult::Success(aps) => aps - .iter() - .filter_map(|nodeindex| node_index_to_node_id(nodeindex, &graph_struct.graph)) - .collect(), - APResult::Error(err) => return Err(err.to_owned().into()), - APResult::Empty(_) => vec![], - }; - - // convert mincut from a vector of Edges to a vector of string pairs - let mincut_vec: Vec<(u32, u32)> = match &algo_result.mincut { - MinCutResult::Success(aps) => aps - .iter() - .filter_map(|edge| { - let u_res = node_index_to_node_id(&edge.get_u(), &graph_struct.graph)?; - let v_res = node_index_to_node_id(&edge.get_v(), &graph_struct.graph)?; - Some((u_res, v_res)) - }) - .collect(), - MinCutResult::Error(err) => return Err(err.to_owned().into()), - MinCutResult::Empty(_) => vec![], - }; - - let diffcen_maps: HashMap>> = match &algo_result.diff_cent { - DiffCenResult::Success(diff_cen_res) => diff_cen_res - .iter() - .map(|(key, val)| { - let key = key.parse::().unwrap_or(0); - let val = val - .iter() - .map(|(k, v)| { - let k = k.parse::().unwrap_or(0); - let v: HashMap = v - .iter() - .map(|(k1, v1)| { - let k1 = k1.parse::().unwrap_or(0); - (k1, *v1) - }) - .collect(); - (k, v) - }) - .collect(); - (key, val) - }) - .collect(), - DiffCenResult::Error(err) => { - error!("{:?}", err); - return Err("Diffusion centrality algorithm failed".into()); - } - DiffCenResult::Empty(_) => HashMap::new(), - }; - - Ok(APMincutStringResults { - ap_result: ap_vec, - mincut_result: mincut_vec, - diffcen_result: diffcen_maps, - }) -} diff --git a/src-tauri/src/ipc/commands/mod.rs b/src-tauri/src/ipc/commands/mod.rs index e2d0235e..0c4560c6 100644 --- a/src-tauri/src/ipc/commands/mod.rs +++ b/src-tauri/src/ipc/commands/mod.rs @@ -1,4 +1,3 @@ pub mod connections; -pub mod graph; pub mod mesh; pub mod radio; diff --git a/src-tauri/src/ipc/events.rs b/src-tauri/src/ipc/events.rs index 189366e3..9b97eae8 100644 --- a/src-tauri/src/ipc/events.rs +++ b/src-tauri/src/ipc/events.rs @@ -18,27 +18,6 @@ pub fn dispatch_updated_device( Ok(()) } -pub fn dispatch_updated_edges( - _handle: &tauri::AppHandle, - graph: &mut device::MeshGraph, -) -> tauri::Result<()> { - debug!("Dispatching updated edges"); - - let _edges = super::helpers::generate_graph_edges_geojson(graph); - let _nodes = geojson::FeatureCollection { - bbox: None, - features: vec![], - foreign_members: None, - }; - - // * This is temporarily disabled until we can figure out how to get the graph to update in-place - // handle.emit_all::("graph_update", GraphGeoJSONResult { nodes, edges })?; - - debug!("Dispatched updated edges"); - - Ok(()) -} - pub fn dispatch_configuration_status( handle: &tauri::AppHandle, status: ConfigurationStatus, diff --git a/src-tauri/src/ipc/helpers.rs b/src-tauri/src/ipc/helpers.rs index 9f03f25d..ace50611 100644 --- a/src-tauri/src/ipc/helpers.rs +++ b/src-tauri/src/ipc/helpers.rs @@ -1,4 +1,3 @@ -use std::collections::HashMap; use std::time::Duration; use log::{debug, error, trace, warn}; @@ -10,96 +9,7 @@ use tokio::sync::mpsc::UnboundedReceiver; use crate::device::SerialDeviceStatus; use crate::ipc::events::{dispatch_configuration_status, dispatch_rebooting_event}; use crate::ipc::{events, ConfigurationStatus}; -use crate::state::DeviceKey; -use crate::{analytics, device}; -use crate::{graph, state}; - -use super::CommandError; - -pub fn generate_graph_edges_geojson(graph: &mut device::MeshGraph) -> geojson::FeatureCollection { - let edge_features: Vec = graph - .graph - .get_edges() - .iter() - .filter(|e| { - let u = graph - .graph - .get_node(e.u) - .expect("Index from edge should exist"); - - let v = graph - .graph - .get_node(e.v) - .expect("Index from edge should exist"); - - u.longitude != 0.0 && u.latitude != 0.0 && v.latitude != 0.0 && v.longitude != 0.0 - }) - .map(|e| { - let u = graph - .graph - .get_node(e.u) - .expect("Index from edge should exist"); - - let v = graph - .graph - .get_node(e.v) - .expect("Index from edge should exist"); - - geojson::Feature { - id: Some(geojson::feature::Id::String(format!( - "{}-{}", - u.name, v.name - ))), - properties: None, - geometry: Some(geojson::Geometry::new(geojson::Value::LineString(vec![ - vec![u.longitude, u.latitude, u.altitude], - vec![v.longitude, v.latitude, v.altitude], - ]))), - ..Default::default() - } - }) - .collect(); - - geojson::FeatureCollection { - bbox: None, - foreign_members: None, - features: edge_features, - } -} - -pub fn node_index_to_node_id( - nodeindex: &petgraph::graph::NodeIndex, - graph: &graph::graph_ds::Graph, -) -> Option { - graph.node_idx_map.iter().find_map(|(key, &val)| { - if val == *nodeindex { - return key.parse::().ok(); - } - None - }) -} - -pub async fn initialize_graph_state( - mesh_graph: tauri::State<'_, state::NetworkGraph>, - algo_state: tauri::State<'_, state::AnalyticsState>, -) -> Result<(), CommandError> { - let new_graph = device::MeshGraph::new(); - let state = analytics::state::AnalyticsState::new(HashMap::new(), false); - let mesh_graph_arc = mesh_graph.inner.clone(); - let algo_state_arc = algo_state.inner.clone(); - - { - let mut new_graph_guard = mesh_graph_arc.lock().await; - *new_graph_guard = Some(new_graph); - } - - { - let mut new_state_guard = algo_state_arc.lock().await; - *new_state_guard = Some(state); - } - - Ok(()) -} +use crate::state::{self, DeviceKey}; pub fn spawn_configuration_timeout_handler( handle: tauri::AppHandle, @@ -159,7 +69,6 @@ pub fn spawn_decoded_handler( handle: tauri::AppHandle, mut decoded_listener: UnboundedReceiver, connected_devices_arc: state::MeshDevicesInner, - graph_arc: state::NetworkGraphInner, device_key: DeviceKey, ) { tauri::async_runtime::spawn(async move { @@ -180,15 +89,6 @@ pub fn spawn_decoded_handler( } }; - let mut graph_guard = graph_arc.lock().await; - let graph = match graph_guard.as_mut().ok_or("Graph not initialized") { - Ok(g) => g, - Err(e) => { - warn!("{}", e); - continue; - } - }; - let update_result = match device.handle_packet_from_radio(packet) { Ok(result) => result, Err(err) => { @@ -208,15 +108,7 @@ pub fn spawn_decoded_handler( } if update_result.regenerate_graph { - graph.regenerate_graph_from_device_info(device); - - match events::dispatch_updated_edges(&handle, graph) { - Ok(_) => (), - Err(e) => { - error!("Failed to dispatch edges to client:\n{}", e); - continue; - } - }; + log::warn!("Graph regeneration not implemented"); } if update_result.configuration_success diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index 5aae537b..fafc6a07 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -3,11 +3,7 @@ windows_subsystem = "windows" )] -mod analytics; -mod constructors; -mod data_conversion; mod device; -mod graph; mod ipc; mod state; @@ -100,14 +96,6 @@ fn main() { inner: Arc::new(async_runtime::Mutex::new(HashMap::new())), }; - let initial_graph_state = state::NetworkGraph { - inner: Arc::new(async_runtime::Mutex::new(None)), - }; - - let initial_analytics_state = state::AnalyticsState { - inner: Arc::new(async_runtime::Mutex::new(None)), - }; - let mut inital_autoconnect_state = state::AutoConnectState { inner: Arc::new(async_runtime::Mutex::new(None)), }; @@ -123,8 +111,6 @@ fn main() { // Manage application state app.app_handle().manage(initial_mesh_devices_state); app.app_handle().manage(initial_radio_connections_state); - app.app_handle().manage(initial_graph_state); - app.app_handle().manage(initial_analytics_state); // Autoconnect port state needs to be set after being mutated by CLI parser app.app_handle().manage(inital_autoconnect_state); @@ -138,9 +124,6 @@ fn main() { ipc::commands::connections::connect_to_tcp_port, ipc::commands::connections::drop_device_connection, ipc::commands::connections::drop_all_device_connections, - ipc::commands::graph::initialize_graph_state, - ipc::commands::graph::get_node_edges, - ipc::commands::graph::run_algorithms, ipc::commands::mesh::send_text, ipc::commands::mesh::send_waypoint, ipc::commands::mesh::delete_waypoint, diff --git a/src-tauri/src/state.rs b/src-tauri/src/state.rs index 01618272..c905c133 100644 --- a/src-tauri/src/state.rs +++ b/src-tauri/src/state.rs @@ -1,4 +1,4 @@ -use crate::{analytics, device}; +use crate::device; use meshtastic::connections::stream_api::{state::Configured, StreamApi}; use std::{collections::HashMap, sync::Arc}; use tauri::async_runtime; @@ -19,19 +19,6 @@ pub struct RadioConnections { pub inner: RadioConnectionsInner, } -pub type NetworkGraphInner = Arc>>; - -#[derive(Debug)] -pub struct NetworkGraph { - pub inner: NetworkGraphInner, -} - -pub type AnalyticsStateInner = Arc>>; - -pub struct AnalyticsState { - pub inner: AnalyticsStateInner, -} - pub type AutoConnectStateInner = Arc>>; #[derive(Debug)] diff --git a/src/bindings/index.ts b/src/bindings/index.ts index ea4fb101..76eac03c 100644 --- a/src/bindings/index.ts +++ b/src/bindings/index.ts @@ -1,762 +1,247 @@ // This file has been generated by Specta. DO NOT EDIT. /** - * - * TODO: REPLACE - */ -export type meshtastic_protobufs_module_config_CannedMessageConfig = { - rotary1Enabled: boolean; - inputbrokerPinA: number; - inputbrokerPinB: number; - inputbrokerPinPress: number; - inputbrokerEventCw: number; - inputbrokerEventCcw: number; - inputbrokerEventPress: number; - updown1Enabled: boolean; - enabled: boolean; - allowInputSource: string; - sendBell: boolean; -}; - -/** - * - * Packets from the radio to the phone will appear on the fromRadio characteristic. - * It will support READ and NOTIFY. When a new packet arrives the device will BLE notify? - * It will sit in that descriptor until consumed by the phone, - * at which point the next item in the FIFO will be populated. + * + * Store and Forward Module Config */ -export type meshtastic_protobufs_FromRadio = { - id: number; - payloadVariant: meshtastic_protobufs_from_radio_PayloadVariant | null; -}; +export type meshtastic_protobufs_module_config_StoreForwardConfig = { enabled: boolean; heartbeat: boolean; records: number; historyReturnMax: number; historyReturnWindow: number } /** - * - * A message used in our Dynamic Source Routing protocol (RFC 4728 based) + * + * Serial Config */ -export type meshtastic_protobufs_RouteDiscovery = { route: number[] }; +export type meshtastic_protobufs_module_config_SerialConfig = { enabled: boolean; echo: boolean; rxd: number; txd: number; baud: number; timeout: number; mode: number; overrideConsoleSerialPort: boolean } /** - * - * RemoteHardwarePins associated with a node + * + * Position with static location information only for NodeDBLite */ -export type meshtastic_protobufs_NodeRemoteHardwarePin = { - nodeNum: number; - pin: meshtastic_protobufs_RemoteHardwarePin | null; -}; - -export type meshtastic_protobufs_LocalConfig = { - device: meshtastic_protobufs_config_DeviceConfig | null; - position: meshtastic_protobufs_config_PositionConfig | null; - power: meshtastic_protobufs_config_PowerConfig | null; - network: meshtastic_protobufs_config_NetworkConfig | null; - display: meshtastic_protobufs_config_DisplayConfig | null; - lora: meshtastic_protobufs_config_LoRaConfig | null; - bluetooth: meshtastic_protobufs_config_BluetoothConfig | null; - version: number; -}; - -export type app_ipc_DeviceBulkConfig = { - radio: meshtastic_protobufs_LocalConfig | null; - module: meshtastic_protobufs_LocalModuleConfig | null; - channels: meshtastic_protobufs_Channel[] | null; -}; - -/** - * - * Full info on edges for a single node +export type meshtastic_protobufs_PositionLite = { latitudeI: number; longitudeI: number; altitude: number; time: number; locationSource: number } + +/** + * + * Canned message module configuration. */ -export type meshtastic_protobufs_NeighborInfo = { - nodeId: number; - lastSentById: number; - nodeBroadcastIntervalSecs: number; - neighbors: meshtastic_protobufs_Neighbor[]; -}; +export type meshtastic_protobufs_RtttlConfig = { ringtone: string } /** - * - * A failure in delivering a message (usually used for routing control messages, but might be provided in addition to ack.fail_id to provide - * details on the type of failure). + * + * Audio Config for codec2 voice */ -export type meshtastic_protobufs_routing_Error = - | "none" - | "noRoute" - | "gotNak" - | "timeout" - | "noInterface" - | "maxRetransmit" - | "noChannel" - | "tooLarge" - | "noResponse" - | "dutyCycleLimit" - | "badRequest" - | "notAuthorized"; - -/** - * - * Broadcast when a newly powered mesh node wants to find a node num it can use - * Sent from the phone over bluetooth to set the user id for the owner of this node. - * Also sent from nodes to each other when a new node signs on (so all clients can have this info) - * The algorithm is as follows: - * when a node starts up, it broadcasts their user and the normal flow is for all - * other nodes to reply with their User as well (so the new node can build its nodedb) - * If a node ever receives a User (not just the first broadcast) message where - * the sender node number equals our node number, that indicates a collision has - * occurred and the following steps should happen: - * If the receiving node (that was already in the mesh)'s macaddr is LOWER than the - * new User who just tried to sign in: it gets to keep its nodenum. - * We send a broadcast message of OUR User (we use a broadcast so that the other node can - * receive our message, considering we have the same id - it also serves to let - * observers correct their nodedb) - this case is rare so it should be okay. - * If any node receives a User where the macaddr is GTE than their local macaddr, - * they have been vetoed and should pick a new random nodenum (filtering against - * whatever it knows about the nodedb) and rebroadcast their User. - * A few nodenums are reserved and will never be requested: - * 0xff - broadcast - * 0 through 3 - for future use +export type meshtastic_protobufs_module_config_AudioConfig = { codec2Enabled: boolean; pttPin: number; bitrate: number; i2SWs: number; i2SSd: number; i2SDin: number; i2SSck: number } + +/** + * + * Weather station or other environmental metrics */ -export type meshtastic_protobufs_User = { - id: string; - longName: string; - shortName: string; - macaddr: number[]; - hwModel: number; - isLicensed: boolean; -}; +export type meshtastic_protobufs_EnvironmentMetrics = { temperature: number; relativeHumidity: number; barometricPressure: number; gasResistance: number; voltage: number; current: number } -export type app_device_MeshNodeDeviceMetrics = { - metrics: meshtastic_protobufs_DeviceMetrics; - timestamp: number; - snr: number; -}; +/** + * + * A packet envelope sent/received over the mesh + * only payload_variant is sent in the payload portion of the LORA packet. + * The other fields are either not sent at all, or sent in the special 16 byte LORA header. + */ +export type meshtastic_protobufs_MeshPacket = { from: number; to: number; channel: number; id: number; rxTime: number; rxSnr: number; hopLimit: number; wantAck: boolean; priority: number; rxRssi: number; delayed: number; payloadVariant: meshtastic_protobufs_mesh_packet_PayloadVariant | null } /** - * + * * TODO: REPLACE */ -export type meshtastic_protobufs_module_config_serial_config_SerialBaud = - | "baudDefault" - | "baud110" - | "baud300" - | "baud600" - | "baud1200" - | "baud2400" - | "baud4800" - | "baud9600" - | "baud19200" - | "baud38400" - | "baud57600" - | "baud115200" - | "baud230400" - | "baud460800" - | "baud576000" - | "baud921600"; - -/** - * - * An example app to show off the module system. This message is used for - * REMOTE_HARDWARE_APP PortNums. - * Also provides easy remote access to any GPIO. - * In the future other remote hardware operations can be added based on user interest - * (i.e. serial output, spi/i2c input/output). - * FIXME - currently this feature is turned on by default which is dangerous - * because no security yet (beyond the channel mechanism). - * It should be off by default and then protected based on some TBD mechanism - * (a special channel once multichannel support is included?) - */ -export type meshtastic_protobufs_HardwareMessage = { - type: number; - gpioMask: string; - gpioValue: string; -}; +export type meshtastic_protobufs_store_and_forward_Variant = { stats: meshtastic_protobufs_store_and_forward_Statistics } | { history: meshtastic_protobufs_store_and_forward_History } | { heartbeat: meshtastic_protobufs_store_and_forward_Heartbeat } | { empty: boolean } -export type app_device_NeighborInfoPacket = { - packet: meshtastic_protobufs_MeshPacket; - data: meshtastic_protobufs_NeighborInfo; -}; +/** + * + * Error codes for critical errors + * The device might report these fault codes on the screen. + * If you encounter a fault code, please post on the meshtastic.discourse.group + * and we'll try to help. + */ +export type meshtastic_protobufs_CriticalErrorCode = "none" | "txWatchdog" | "sleepEnterWait" | "noRadio" | "unspecified" | "ubloxUnitFailed" | "noAxp192" | "invalidRadioSetting" | "transmitFailed" | "brownout" | "sx1262Failure" | "radioSpiBug" /** - * - * Override OLED outo detect with this if it fails. + * + * A Routing control Data packet handled by the routing module */ -export type meshtastic_protobufs_config_display_config_OledType = - | "oledAuto" - | "oledSsd1306" - | "oledSh1106" - | "oledSh1107"; +export type meshtastic_protobufs_Routing = { variant: meshtastic_protobufs_routing_Variant | null } /** - * - * This message is handled by the Admin module and is responsible for all settings/channel read/write operations. - * This message is used to do settings operations to both remote AND local nodes. - * (Prior to 1.2 these operations were done via special ToRadio operations) + * + * A single edge in the mesh */ -export type meshtastic_protobufs_AdminMessage = { - payloadVariant: meshtastic_protobufs_admin_message_PayloadVariant | null; -}; +export type meshtastic_protobufs_Neighbor = { nodeId: number; snr: number; lastRxTime: number; nodeBroadcastIntervalSecs: number } /** - * - * Parameters for setting up Meshtastic for ameteur radio usage + * + * TODO: REPLACE */ -export type meshtastic_protobufs_HamParameters = { - callSign: string; - txPower: number; - frequency: number; - shortName: string; -}; +export type meshtastic_protobufs_ScreenFonts = "fontSmall" | "fontMedium" | "fontLarge" /** - * - * Identify if this is a delayed packet + * + * A pair of a channel number, mode and the (sharable) settings for that channel */ -export type meshtastic_protobufs_mesh_packet_Delayed = - | "noDelay" - | "broadcast" - | "direct"; +export type meshtastic_protobufs_Channel = { index: number; settings: meshtastic_protobufs_ChannelSettings | null; role: number } /** - * - * Position Config + * + * Ethernet connection status */ -export type meshtastic_protobufs_config_PositionConfig = { - positionBroadcastSecs: number; - positionBroadcastSmartEnabled: boolean; - fixedPosition: boolean; - gpsEnabled: boolean; - gpsUpdateInterval: number; - gpsAttemptTime: number; - positionFlags: number; - rxGpio: number; - txGpio: number; - broadcastSmartMinimumDistance: number; - broadcastSmartMinimumIntervalSecs: number; -}; - -/** - * - * Note: these enum names must EXACTLY match the string used in the device - * bin/build-all.sh script. - * Because they will be used to find firmware filenames in the android app for OTA updates. - * To match the old style filenames, _ is converted to -, p is converted to . +export type meshtastic_protobufs_EthernetConnectionStatus = { status: meshtastic_protobufs_NetworkConnectionStatus | null } + +/** + * + * TODO: REPLACE */ -export type meshtastic_protobufs_HardwareModel = - | "unset" - | "tloraV2" - | "tloraV1" - | "tloraV211P6" - | "tbeam" - | "heltecV20" - | "tbeamV0P7" - | "techo" - | "tloraV11P3" - | "rak4631" - | "heltecV21" - | "heltecV1" - | "lilygoTbeamS3Core" - | "rak11200" - | "nanoG1" - | "tloraV211P8" - | "tloraT3S3" - | "nanoG1Explorer" - | "nanoG2Ultra" - | "loraType" - | "stationG1" - | "rak11310" - | "loraRelayV1" - | "nrf52840Dk" - | "ppr" - | "genieblocks" - | "nrf52Unknown" - | "portduino" - | "androidSim" - | "diyV1" - | "nrf52840Pca10059" - | "drDev" - | "m5Stack" - | "heltecV3" - | "heltecWslV3" - | "betafpv2400Tx" - | "betafpv900NanoTx" - | "rpiPico" - | "heltecWirelessTracker" - | "heltecWirelessPaper" - | "tdeck" - | "twatchS3" - | "picomputerS3" - | "heltecHt62" - | "privateHw"; - -export type meshtastic_protobufs_LocalModuleConfig = { - mqtt: meshtastic_protobufs_module_config_MqttConfig | null; - serial: meshtastic_protobufs_module_config_SerialConfig | null; - externalNotification: meshtastic_protobufs_module_config_ExternalNotificationConfig | null; - storeForward: meshtastic_protobufs_module_config_StoreForwardConfig | null; - rangeTest: meshtastic_protobufs_module_config_RangeTestConfig | null; - telemetry: meshtastic_protobufs_module_config_TelemetryConfig | null; - cannedMessage: meshtastic_protobufs_module_config_CannedMessageConfig | null; - audio: meshtastic_protobufs_module_config_AudioConfig | null; - remoteHardware: meshtastic_protobufs_module_config_RemoteHardwareConfig | null; - neighborInfo: meshtastic_protobufs_module_config_NeighborInfoConfig | null; - ambientLighting: meshtastic_protobufs_module_config_AmbientLightingConfig | null; - detectionSensor: meshtastic_protobufs_module_config_DetectionSensorConfig | null; - version: number; -}; - -/** - * - * A single edge in the mesh +export type meshtastic_protobufs_module_config_CannedMessageConfig = { rotary1Enabled: boolean; inputbrokerPinA: number; inputbrokerPinB: number; inputbrokerPinPress: number; inputbrokerEventCw: number; inputbrokerEventCcw: number; inputbrokerEventPress: number; updown1Enabled: boolean; enabled: boolean; allowInputSource: string; sendBell: boolean } + +/** + * + * Unique local debugging info for this node + * Note: we don't include position or the user info, because that will come in the + * Sent to the phone in response to WantNodes. */ -export type meshtastic_protobufs_Neighbor = { - nodeId: number; - snr: number; - lastRxTime: number; - nodeBroadcastIntervalSecs: number; -}; +export type meshtastic_protobufs_MyNodeInfo = { myNodeNum: number; rebootCount: number; minAppVersion: number } -export type meshtastic_protobufs_config_bluetooth_config_PairingMode = - | "randomPin" - | "fixedPin" - | "noPin"; +export type meshtastic_protobufs_Config = { payloadVariant: meshtastic_protobufs_config_PayloadVariant | null } -export type app_device_ChannelMessageWithState = { - payload: app_device_ChannelMessagePayload; - state: app_device_ChannelMessageState; -}; +export type meshtastic_protobufs_config_bluetooth_config_PairingMode = "randomPin" | "fixedPin" | "noPin" /** - * - * This is the most compact possible representation for a set of channels. - * It includes only one PRIMARY channel (which must be first) and - * any SECONDARY channels. - * No DISABLED channels are included. - * This abstraction is used only on the the 'app side' of the world (ie python, javascript and android etc) to show a group of Channels as a (long) URL + * + * Power Config\ + * See [Power Config](/docs/settings/config/power) for additional power config details. */ -export type meshtastic_protobufs_ChannelSet = { - settings: meshtastic_protobufs_ChannelSettings[]; - loraConfig: meshtastic_protobufs_config_LoRaConfig | null; -}; +export type meshtastic_protobufs_config_PowerConfig = { isPowerSaving: boolean; onBatteryShutdownAfterSecs: number; adcMultiplierOverride: number; waitBluetoothSecs: number; sdsSecs: number; lsSecs: number; minWakeSecs: number; deviceBatteryInaAddress: number } /** - * - * This abstraction is used to contain any configuration for provisioning a node on any client. - * It is useful for importing and exporting configurations. + * + * A message used in our Dynamic Source Routing protocol (RFC 4728 based) */ -export type meshtastic_protobufs_DeviceProfile = { - longName: string | null; - shortName: string | null; - channelUrl: string | null; - config: meshtastic_protobufs_LocalConfig | null; - moduleConfig: meshtastic_protobufs_LocalModuleConfig | null; -}; +export type meshtastic_protobufs_RouteDiscovery = { route: number[] } + +export type meshtastic_protobufs_config_network_config_IpV4Config = { ip: number; gateway: number; subnet: number; dns: number } /** - * - * Display Config - */ -export type meshtastic_protobufs_config_DisplayConfig = { - screenOnSecs: number; - gpsFormat: number; - autoScreenCarouselSecs: number; - compassNorthTop: boolean; - flipScreen: boolean; - units: number; - oled: number; - displaymode: number; - headingBold: boolean; - wakeOnTapOrMotion: boolean; -}; - -/** - * - * TODO: REPLACE + * + * The on-disk saved channels */ -export type meshtastic_protobufs_module_config_canned_message_config_InputEventChar = - | "none" - | "up" - | "down" - | "left" - | "right" - | "select" - | "back" - | "cancel"; +export type meshtastic_protobufs_ChannelFile = { channels: meshtastic_protobufs_Channel[]; version: number } /** - * - * A GPIO pin definition for remote hardware module + * Currently a re-export of `protobufs::Position` with normalized lat, long */ -export type meshtastic_protobufs_RemoteHardwarePin = { - gpioPin: number; - name: string; - type: number; -}; +export type app_device_NormalizedPosition = { latitude: number; longitude: number; altitude: number; time: number; locationSource: meshtastic_protobufs_position_LocSource; altitudeSource: meshtastic_protobufs_position_AltSource; timestamp: number; timestampMillisAdjust: number; altitudeHae: number; altitudeGeoidalSeparation: number; pdop: number; hdop: number; vdop: number; gpsAccuracy: number; groundSpeed: number; groundTrack: number; fixQuality: number; fixType: number; satsInView: number; sensorId: number; nextUpdate: number; seqNumber: number } + +export type meshtastic_protobufs_config_BluetoothConfig = { enabled: boolean; mode: number; fixedPin: number } + +export type meshtastic_protobufs_DeviceConnectionStatus = { wifi: meshtastic_protobufs_WifiConnectionStatus | null; ethernet: meshtastic_protobufs_EthernetConnectionStatus | null; bluetooth: meshtastic_protobufs_BluetoothConnectionStatus | null; serial: meshtastic_protobufs_SerialConnectionStatus | null } /** - * - * Baudrate for codec2 voice + * + * Log levels, chosen to match python logging conventions. */ -export type meshtastic_protobufs_module_config_audio_config_AudioBaud = - | "codec2Default" - | "codec23200" - | "codec22400" - | "codec21600" - | "codec21400" - | "codec21300" - | "codec21200" - | "codec2700" - | "codec2700B"; +export type meshtastic_protobufs_log_record_Level = "unset" | "critical" | "error" | "warning" | "info" | "debug" | "trace" /** - * - * Unit display preference + * + * Network Config */ -export type meshtastic_protobufs_config_display_config_DisplayUnits = - | "metric" - | "imperial"; +export type meshtastic_protobufs_config_NetworkConfig = { wifiEnabled: boolean; wifiSsid: string; wifiPsk: string; ntpServer: string; ethEnabled: boolean; addressMode: number; ipv4Config: meshtastic_protobufs_config_network_config_IpV4Config | null; rsyslogServer: string } + +export type app_device_UserPacket = { packet: meshtastic_protobufs_MeshPacket; data: meshtastic_protobufs_User } + +export type app_device_TelemetryPacket = { packet: meshtastic_protobufs_MeshPacket; data: meshtastic_protobufs_Telemetry } -export type meshtastic_protobufs_Config = { - payloadVariant: meshtastic_protobufs_config_PayloadVariant | null; -}; +export type app_device_LastHeardMetadata = { timestamp: number; snr: number; channel: number } /** - * - * How the GPS coordinates are displayed on the OLED screen. + * + * Packets from the radio to the phone will appear on the fromRadio characteristic. + * It will support READ and NOTIFY. When a new packet arrives the device will BLE notify? + * It will sit in that descriptor until consumed by the phone, + * at which point the next item in the FIFO will be populated. */ -export type meshtastic_protobufs_config_display_config_GpsCoordinateFormat = - | "dec" - | "dms" - | "utm" - | "mgrs" - | "olc" - | "osgr"; +export type meshtastic_protobufs_FromRadio = { id: number; payloadVariant: meshtastic_protobufs_from_radio_PayloadVariant | null } /** - * - * Defines the device's role on the Mesh network + * + * How the altitude was acquired: manual, GPS int/ext, etc + * Default: same as location_source if present */ -export type meshtastic_protobufs_config_device_config_Role = - | "client" - | "clientMute" - | "router" - | "routerClient" - | "repeater" - | "tracker" - | "sensor"; +export type meshtastic_protobufs_position_AltSource = "altUnset" | "altManual" | "altInternal" | "altExternal" | "altBarometric" /** - * - * How the location was acquired: manual, onboard GPS, external (EUD) GPS + * + * Display Config */ -export type meshtastic_protobufs_position_LocSource = - | "locUnset" - | "locManual" - | "locInternal" - | "locExternal"; - -export type app_device_MeshDevice = { - configId: number; - ready: boolean; - status: app_device_SerialDeviceStatus; - channels: { [key: number]: app_device_MeshChannel }; - config: meshtastic_protobufs_LocalConfig; - moduleConfig: meshtastic_protobufs_LocalModuleConfig; - myNodeInfo: meshtastic_protobufs_MyNodeInfo; - nodes: { [key: number]: app_device_MeshNode }; - regionUnset: boolean; - deviceMetrics: meshtastic_protobufs_DeviceMetrics; - waypoints: { [key: number]: app_device_NormalizedWaypoint }; - neighbors: { [key: number]: app_device_NeighborInfoPacket }; - configInProgress: boolean; -}; +export type meshtastic_protobufs_config_DisplayConfig = { screenOnSecs: number; gpsFormat: number; autoScreenCarouselSecs: number; compassNorthTop: boolean; flipScreen: boolean; units: number; oled: number; displaymode: number; headingBold: boolean; wakeOnTapOrMotion: boolean } /** - * Currently a re-export of `protobufs::Position` with normalized lat, long - */ -export type app_device_NormalizedPosition = { - latitude: number; - longitude: number; - altitude: number; - time: number; - locationSource: meshtastic_protobufs_position_LocSource; - altitudeSource: meshtastic_protobufs_position_AltSource; - timestamp: number; - timestampMillisAdjust: number; - altitudeHae: number; - altitudeGeoidalSeparation: number; - pdop: number; - hdop: number; - vdop: number; - gpsAccuracy: number; - groundSpeed: number; - groundTrack: number; - fixQuality: number; - fixType: number; - satsInView: number; - sensorId: number; - nextUpdate: number; - seqNumber: number; -}; - -/** - * - * A pair of a channel number, mode and the (sharable) settings for that channel + * + * Ethernet or WiFi connection status */ -export type meshtastic_protobufs_Channel = { - index: number; - settings: meshtastic_protobufs_ChannelSettings | null; - role: number; -}; - -export type meshtastic_protobufs_x_modem_Control = - | "nul" - | "soh" - | "stx" - | "eot" - | "ack" - | "nak" - | "can" - | "ctrlz"; +export type meshtastic_protobufs_NetworkConnectionStatus = { ipAddress: number; isConnected: boolean; isMqttConnected: boolean; isSyslogConnected: boolean } /** - * - * TODO: REPLACE + * + * How the GPS coordinates are displayed on the OLED screen. */ -export type meshtastic_protobufs_admin_message_ModuleConfigType = - | "mqttConfig" - | "serialConfig" - | "extnotifConfig" - | "storeforwardConfig" - | "rangetestConfig" - | "telemetryConfig" - | "cannedmsgConfig" - | "audioConfig" - | "remotehardwareConfig" - | "neighborinfoConfig" - | "ambientlightingConfig" - | "detectionsensorConfig"; +export type meshtastic_protobufs_config_display_config_GpsCoordinateFormat = "dec" | "dms" | "utm" | "mgrs" | "olc" | "osgr" /** - * Ambient Lighting Module - Settings for control of onboard LEDs to allow users to adjust the brightness levels and respective color levels. - * Initially created for the RAK14001 RGB LED module. + * + * Standard predefined channel settings + * Note: these mappings must match ModemPreset Choice in the device code. */ -export type meshtastic_protobufs_module_config_AmbientLightingConfig = { - ledState: boolean; - current: number; - red: number; - green: number; - blue: number; -}; +export type meshtastic_protobufs_config_lo_ra_config_ModemPreset = "longFast" | "longSlow" | "veryLongSlow" | "mediumSlow" | "mediumFast" | "shortSlow" | "shortFast" | "longModerate" /** - * - * MQTT Client Config - */ -export type meshtastic_protobufs_module_config_MqttConfig = { - enabled: boolean; - address: string; - username: string; - password: string; - encryptionEnabled: boolean; - jsonEnabled: boolean; - tlsEnabled: boolean; - root: string; - proxyToClientEnabled: boolean; -}; - -export type meshtastic_protobufs_config_lo_ra_config_RegionCode = - | "unset" - | "us" - | "eu433" - | "eu868" - | "cn" - | "jp" - | "anz" - | "kr" - | "tw" - | "ru" - | "in" - | "nz865" - | "th" - | "lora24" - | "ua433" - | "ua868"; - -/** - * - * TODO: REPLACE + * + * Position Config */ -export type meshtastic_protobufs_admin_message_ConfigType = - | "deviceConfig" - | "positionConfig" - | "powerConfig" - | "networkConfig" - | "displayConfig" - | "loraConfig" - | "bluetoothConfig"; +export type meshtastic_protobufs_config_PositionConfig = { positionBroadcastSecs: number; positionBroadcastSmartEnabled: boolean; fixedPosition: boolean; gpsEnabled: boolean; gpsUpdateInterval: number; gpsAttemptTime: number; positionFlags: number; rxGpio: number; txGpio: number; broadcastSmartMinimumDistance: number; broadcastSmartMinimumIntervalSecs: number } -export type app_device_ChannelMessagePayload = - | ({ type: "text" } & app_device_TextPacket) - | ({ type: "waypoint" } & app_device_WaypointPacket); +export type app_device_SerialDeviceStatus = "restarting" | "disconnected" | "connecting" | "reconnecting" | "connected" | "configuring" | "configured" /** - * - * RemoteHardwareModule Config + * + * Unit display preference */ -export type meshtastic_protobufs_module_config_RemoteHardwareConfig = { - enabled: boolean; - allowUndefinedPinAccess: boolean; - availablePins: meshtastic_protobufs_RemoteHardwarePin[]; -}; +export type meshtastic_protobufs_config_display_config_DisplayUnits = "metric" | "imperial" /** - * - * A packet envelope sent/received over the mesh - * only payload_variant is sent in the payload portion of the LORA packet. - * The other fields are either not sent at all, or sent in the special 16 byte LORA header. - */ -export type meshtastic_protobufs_MeshPacket = { - from: number; - to: number; - channel: number; - id: number; - rxTime: number; - rxSnr: number; - hopLimit: number; - wantAck: boolean; - priority: number; - rxRssi: number; - delayed: number; - payloadVariant: meshtastic_protobufs_mesh_packet_PayloadVariant | null; -}; - -export type app_device_TelemetryPacket = { - packet: meshtastic_protobufs_MeshPacket; - data: meshtastic_protobufs_Telemetry; -}; - -/** - * - * Unique local debugging info for this node - * Note: we don't include position or the user info, because that will come in the - * Sent to the phone in response to WantNodes. + * + * 001 - 063 = From Router + * 064 - 127 = From Client */ -export type meshtastic_protobufs_MyNodeInfo = { - myNodeNum: number; - rebootCount: number; - minAppVersion: number; -}; +export type meshtastic_protobufs_store_and_forward_RequestResponse = "unset" | "routerError" | "routerHeartbeat" | "routerPing" | "routerPong" | "routerBusy" | "routerHistory" | "routerStats" | "clientError" | "clientHistory" | "clientStats" | "clientPing" | "clientPong" | "clientAbort" /** - * - * Bit field of boolean configuration options, indicating which optional - * fields to include when assembling POSITION messages - * Longitude and latitude are always included (also time if GPS-synced) - * NOTE: the more fields are included, the larger the message will be - - * leading to longer airtime and a higher risk of packet loss - */ -export type meshtastic_protobufs_config_position_config_PositionFlags = - | "unset" - | "altitude" - | "altitudeMsl" - | "geoidalSeparation" - | "dop" - | "hvdop" - | "satinview" - | "seqNo" - | "timestamp" - | "heading" - | "speed"; - -export type meshtastic_protobufs_config_BluetoothConfig = { - enabled: boolean; - mode: number; - fixedPin: number; -}; - -export type meshtastic_protobufs_telemetry_Variant = - | { deviceMetrics: meshtastic_protobufs_DeviceMetrics } - | { environmentMetrics: meshtastic_protobufs_EnvironmentMetrics } - | { airQualityMetrics: meshtastic_protobufs_AirQualityMetrics }; - -/** - * + * * TODO: REPLACE */ -export type meshtastic_protobufs_module_config_PayloadVariant = - | { mqtt: meshtastic_protobufs_module_config_MqttConfig } - | { serial: meshtastic_protobufs_module_config_SerialConfig } - | { - externalNotification: meshtastic_protobufs_module_config_ExternalNotificationConfig; - } - | { storeForward: meshtastic_protobufs_module_config_StoreForwardConfig } - | { rangeTest: meshtastic_protobufs_module_config_RangeTestConfig } - | { telemetry: meshtastic_protobufs_module_config_TelemetryConfig } - | { cannedMessage: meshtastic_protobufs_module_config_CannedMessageConfig } - | { audio: meshtastic_protobufs_module_config_AudioConfig } - | { remoteHardware: meshtastic_protobufs_module_config_RemoteHardwareConfig } - | { neighborInfo: meshtastic_protobufs_module_config_NeighborInfoConfig } - | { - ambientLighting: meshtastic_protobufs_module_config_AmbientLightingConfig; - } - | { - detectionSensor: meshtastic_protobufs_module_config_DetectionSensorConfig; - }; - -/** - * - * 001 - 063 = From Router - * 064 - 127 = From Client - */ -export type meshtastic_protobufs_store_and_forward_RequestResponse = - | "unset" - | "routerError" - | "routerHeartbeat" - | "routerPing" - | "routerPong" - | "routerBusy" - | "routerHistory" - | "routerStats" - | "clientError" - | "clientHistory" - | "clientStats" - | "clientPing" - | "clientPong" - | "clientAbort"; - -/** - * - * Power Config\ - * See [Power Config](/docs/settings/config/power) for additional power config details. +export type meshtastic_protobufs_admin_message_ModuleConfigType = "mqttConfig" | "serialConfig" | "extnotifConfig" | "storeforwardConfig" | "rangetestConfig" | "telemetryConfig" | "cannedmsgConfig" | "audioConfig" | "remotehardwareConfig" | "neighborinfoConfig" | "ambientlightingConfig" | "detectionsensorConfig" + +/** + * + * Packets/commands to the radio will be written (reliably) to the toRadio characteristic. + * Once the write completes the phone can assume it is handled. */ -export type meshtastic_protobufs_config_PowerConfig = { - isPowerSaving: boolean; - onBatteryShutdownAfterSecs: number; - adcMultiplierOverride: number; - waitBluetoothSecs: number; - sdsSecs: number; - lsSecs: number; - minWakeSecs: number; - deviceBatteryInaAddress: number; -}; +export type meshtastic_protobufs_ToRadio = { payloadVariant: meshtastic_protobufs_to_radio_PayloadVariant | null } + +export type app_device_MeshNodeDeviceMetrics = { metrics: meshtastic_protobufs_DeviceMetrics; timestamp: number; snr: number } /** - * - * This can be used for customizing the firmware distribution. If populated, - * show a secondary bootup screen with custom logo and text for 2.5 seconds. + * + * TODO: REPLACE */ -export type meshtastic_protobufs_OemStore = { - oemIconWidth: number; - oemIconHeight: number; - oemIconBits: number[]; - oemFont: number; - oemText: string; - oemAesKey: number[]; - oemLocalConfig: meshtastic_protobufs_LocalConfig | null; - oemLocalModuleConfig: meshtastic_protobufs_LocalModuleConfig | null; -}; +export type meshtastic_protobufs_admin_message_PayloadVariant = { getChannelRequest: number } | { getChannelResponse: meshtastic_protobufs_Channel } | { getOwnerRequest: boolean } | { getOwnerResponse: meshtastic_protobufs_User } | { getConfigRequest: number } | { getConfigResponse: meshtastic_protobufs_Config } | { getModuleConfigRequest: number } | { getModuleConfigResponse: meshtastic_protobufs_ModuleConfig } | { getCannedMessageModuleMessagesRequest: boolean } | { getCannedMessageModuleMessagesResponse: string } | { getDeviceMetadataRequest: boolean } | { getDeviceMetadataResponse: meshtastic_protobufs_DeviceMetadata } | { getRingtoneRequest: boolean } | { getRingtoneResponse: string } | { getDeviceConnectionStatusRequest: boolean } | { getDeviceConnectionStatusResponse: meshtastic_protobufs_DeviceConnectionStatus } | { setHamMode: meshtastic_protobufs_HamParameters } | { getNodeRemoteHardwarePinsRequest: boolean } | { getNodeRemoteHardwarePinsResponse: meshtastic_protobufs_NodeRemoteHardwarePinsResponse } | { setOwner: meshtastic_protobufs_User } | { setChannel: meshtastic_protobufs_Channel } | { setConfig: meshtastic_protobufs_Config } | { setModuleConfig: meshtastic_protobufs_ModuleConfig } | { setCannedMessageModuleMessages: string } | { setRingtoneMessage: string } | { beginEditSettings: boolean } | { commitEditSettings: boolean } | { rebootOtaSeconds: number } | { exitSimulator: boolean } | { rebootSeconds: number } | { shutdownSeconds: number } | { factoryReset: number } | { nodedbReset: number } + +export type app_device_ChannelMessageState = "pending" | "acknowledged" | { error: string } /** - * + * * For any new 'apps' that run on the device or via sister apps on phones/PCs they should pick and use a * unique 'portnum' for their application. * If you are making a new app using meshtastic, please send in a pull request to add your 'portnum' to this @@ -770,1046 +255,572 @@ export type meshtastic_protobufs_OemStore = { * We have change to this 'portnum' based scheme for specifying app handlers for particular payloads. * This change is backwards compatible by treating the legacy OPAQUE/CLEAR_TEXT values identically. */ -export type meshtastic_protobufs_PortNum = - | "unknownApp" - | "textMessageApp" - | "remoteHardwareApp" - | "positionApp" - | "nodeinfoApp" - | "routingApp" - | "adminApp" - | "textMessageCompressedApp" - | "waypointApp" - | "audioApp" - | "detectionSensorApp" - | "replyApp" - | "ipTunnelApp" - | "serialApp" - | "storeForwardApp" - | "rangeTestApp" - | "telemetryApp" - | "zpsApp" - | "simulatorApp" - | "tracerouteApp" - | "neighborinfoApp" - | "privateApp" - | "atakForwarder" - | "max"; - -/** - * - * Serial connection status - */ -export type meshtastic_protobufs_SerialConnectionStatus = { - baud: number; - isConnected: boolean; -}; - -export type meshtastic_protobufs_NodeInfoLite = { - num: number; - user: meshtastic_protobufs_User | null; - position: meshtastic_protobufs_PositionLite | null; - snr: number; - lastHeard: number; - deviceMetrics: meshtastic_protobufs_DeviceMetrics | null; - channel: number; -}; +export type meshtastic_protobufs_PortNum = "unknownApp" | "textMessageApp" | "remoteHardwareApp" | "positionApp" | "nodeinfoApp" | "routingApp" | "adminApp" | "textMessageCompressedApp" | "waypointApp" | "audioApp" | "detectionSensorApp" | "replyApp" | "ipTunnelApp" | "serialApp" | "storeForwardApp" | "rangeTestApp" | "telemetryApp" | "zpsApp" | "simulatorApp" | "tracerouteApp" | "neighborinfoApp" | "privateApp" | "atakForwarder" | "max" /** - * - * The bluetooth to device link: - * Old BTLE protocol docs from TODO, merge in above and make real docs... - * use protocol buffers, and NanoPB - * messages from device to phone: - * POSITION_UPDATE (..., time) - * TEXT_RECEIVED(from, text, time) - * OPAQUE_RECEIVED(from, payload, time) (for signal messages or other applications) - * messages from phone to device: - * SET_MYID(id, human readable long, human readable short) (send down the unique ID - * string used for this node, a human readable string shown for that id, and a very - * short human readable string suitable for oled screen) SEND_OPAQUE(dest, payload) - * (for signal messages or other applications) SEND_TEXT(dest, text) Get all - * nodes() (returns list of nodes, with full info, last time seen, loc, battery - * level etc) SET_CONFIG (switches device to a new set of radio params and - * preshared key, drops all existing nodes, force our node to rejoin this new group) - * Full information about a node on the mesh - */ -export type meshtastic_protobufs_NodeInfo = { - num: number; - user: meshtastic_protobufs_User | null; - position: meshtastic_protobufs_Position | null; - snr: number; - lastHeard: number; - deviceMetrics: meshtastic_protobufs_DeviceMetrics | null; - channel: number; -}; - -export type meshtastic_protobufs_config_display_config_DisplayMode = - | "default" - | "twocolor" - | "inverted" - | "color"; - -export type app_device_MeshNodePositionMetrics = { - metrics: app_device_NormalizedPosition; - timestamp: number; - snr: number; -}; - -export type app_device_MeshNodeEnvironmentMetrics = { - metrics: meshtastic_protobufs_EnvironmentMetrics; - timestamp: number; - snr: number; -}; - -/** - * - * Device metadata response - */ -export type meshtastic_protobufs_DeviceMetadata = { - firmwareVersion: string; - deviceStateVersion: number; - canShutdown: boolean; - hasWifi: boolean; - hasBluetooth: boolean; - hasEthernet: boolean; - role: number; - positionFlags: number; - hwModel: number; - hasRemoteHardware: boolean; -}; - -/** - * - * A Routing control Data packet handled by the routing module + * + * Module Config */ -export type meshtastic_protobufs_Routing = { - variant: meshtastic_protobufs_routing_Variant | null; -}; - -export type app_device_PositionPacket = { - packet: meshtastic_protobufs_MeshPacket; - data: meshtastic_protobufs_Position; -}; +export type meshtastic_protobufs_ModuleConfig = { payloadVariant: meshtastic_protobufs_module_config_PayloadVariant | null } /** - * - * TODO: REPLACE + * + * How this channel is being used (or not). + * Note: this field is an enum to give us options for the future. + * In particular, someday we might make a 'SCANNING' option. + * SCANNING channels could have different frequencies and the radio would + * occasionally check that freq to see if anything is being transmitted. + * For devices that have multiple physical radios attached, we could keep multiple PRIMARY/SCANNING channels active at once to allow + * cross band routing as needed. + * If a device has only a single radio (the common case) only one channel can be PRIMARY at a time + * (but any number of SECONDARY channels can't be sent received on that common frequency) */ -export type meshtastic_protobufs_module_config_serial_config_SerialMode = - | "default" - | "simple" - | "proto" - | "textmsg" - | "nmea" - | "caltopo"; +export type meshtastic_protobufs_channel_Role = "disabled" | "primary" | "secondary" + +export type app_device_MeshNodeEnvironmentMetrics = { metrics: meshtastic_protobufs_EnvironmentMetrics; timestamp: number; snr: number } /** - * - * Configuration + * + * This message wraps a MeshPacket with extra metadata about the sender and how it arrived. */ -export type meshtastic_protobufs_config_DeviceConfig = { - role: number; - serialEnabled: boolean; - debugLogEnabled: boolean; - buttonGpio: number; - buzzerGpio: number; - rebroadcastMode: number; - nodeInfoBroadcastSecs: number; - doubleTapAsButtonPress: boolean; - isManaged: boolean; -}; +export type meshtastic_protobufs_ServiceEnvelope = { packet: meshtastic_protobufs_MeshPacket | null; channelId: string; gatewayId: string } /** - * - * Detection Sensor Module Config - */ -export type meshtastic_protobufs_module_config_DetectionSensorConfig = { - enabled: boolean; - minimumBroadcastSecs: number; - stateBroadcastSecs: number; - sendBell: boolean; - name: string; - monitorPin: number; - detectionTriggeredHigh: boolean; - usePullup: boolean; -}; - -export type app_device_LastHeardMetadata = { - timestamp: number; - snr: number; - channel: number; -}; - -/** - * - * TODO: REPLACE + * + * This message is never sent over the wire, but it is used for serializing DB + * state to flash in the device code + * FIXME, since we write this each time we enter deep sleep (and have infinite + * flash) it would be better to use some sort of append only data structure for + * the receive queue and use the preferences store for the other stuff */ -export type meshtastic_protobufs_store_and_forward_Heartbeat = { - period: number; - secondary: number; -}; +export type meshtastic_protobufs_DeviceState = { myNode: meshtastic_protobufs_MyNodeInfo | null; owner: meshtastic_protobufs_User | null; receiveQueue: meshtastic_protobufs_MeshPacket[]; version: number; rxTextMessage: meshtastic_protobufs_MeshPacket | null; noSave: boolean; didGpsReset: boolean; rxWaypoint: meshtastic_protobufs_MeshPacket | null; nodeRemoteHardwarePins: meshtastic_protobufs_NodeRemoteHardwarePin[]; nodeDbLite: meshtastic_protobufs_NodeInfoLite[] } /** - * - * How the altitude was acquired: manual, GPS int/ext, etc - * Default: same as location_source if present + * + * Defines the device's role on the Mesh network */ -export type meshtastic_protobufs_position_AltSource = - | "altUnset" - | "altManual" - | "altInternal" - | "altExternal" - | "altBarometric"; - -export type meshtastic_protobufs_DeviceConnectionStatus = { - wifi: meshtastic_protobufs_WifiConnectionStatus | null; - ethernet: meshtastic_protobufs_EthernetConnectionStatus | null; - bluetooth: meshtastic_protobufs_BluetoothConnectionStatus | null; - serial: meshtastic_protobufs_SerialConnectionStatus | null; -}; +export type meshtastic_protobufs_config_device_config_Role = "client" | "clientMute" | "router" | "routerClient" | "repeater" | "tracker" | "sensor" /** - * - * TODO: REPLACE + * + * An example app to show off the module system. This message is used for + * REMOTE_HARDWARE_APP PortNums. + * Also provides easy remote access to any GPIO. + * In the future other remote hardware operations can be added based on user interest + * (i.e. serial output, spi/i2c input/output). + * FIXME - currently this feature is turned on by default which is dangerous + * because no security yet (beyond the channel mechanism). + * It should be off by default and then protected based on some TBD mechanism + * (a special channel once multichannel support is included?) */ -export type meshtastic_protobufs_StoreAndForward = { - rr: number; - variant: meshtastic_protobufs_store_and_forward_Variant | null; -}; +export type meshtastic_protobufs_HardwareMessage = { type: number; gpioMask: string; gpioValue: string } /** - * - * TODO: REPLACE + * + * Configuration */ -export type meshtastic_protobufs_store_and_forward_History = { - historyMessages: number; - window: number; - lastRequest: number; -}; +export type meshtastic_protobufs_config_DeviceConfig = { role: number; serialEnabled: boolean; debugLogEnabled: boolean; buttonGpio: number; buzzerGpio: number; rebroadcastMode: number; nodeInfoBroadcastSecs: number; doubleTapAsButtonPress: boolean; isManaged: boolean } -export type meshtastic_protobufs_mesh_packet_PayloadVariant = - | { decoded: meshtastic_protobufs_Data } - | { encrypted: number[] }; +export type meshtastic_protobufs_NodeInfoLite = { num: number; user: meshtastic_protobufs_User | null; position: meshtastic_protobufs_PositionLite | null; snr: number; lastHeard: number; deviceMetrics: meshtastic_protobufs_DeviceMetrics | null; channel: number } /** - * - * Store and Forward Module Config + * + * Lora Config */ -export type meshtastic_protobufs_module_config_StoreForwardConfig = { - enabled: boolean; - heartbeat: boolean; - records: number; - historyReturnMax: number; - historyReturnWindow: number; -}; +export type meshtastic_protobufs_config_LoRaConfig = { usePreset: boolean; modemPreset: number; bandwidth: number; spreadFactor: number; codingRate: number; frequencyOffset: number; region: number; hopLimit: number; txEnabled: boolean; txPower: number; channelNum: number; overrideDutyCycle: boolean; sx126XRxBoostedGain: boolean; overrideFrequency: number; ignoreIncoming: number[] } /** - * - * Standard predefined channel settings - * Note: these mappings must match ModemPreset Choice in the device code. + * + * Air quality metrics */ -export type meshtastic_protobufs_config_lo_ra_config_ModemPreset = - | "longFast" - | "longSlow" - | "veryLongSlow" - | "mediumSlow" - | "mediumFast" - | "shortSlow" - | "shortFast" - | "longModerate"; +export type meshtastic_protobufs_AirQualityMetrics = { pm10Standard: number; pm25Standard: number; pm100Standard: number; pm10Environmental: number; pm25Environmental: number; pm100Environmental: number; particles03Um: number; particles05Um: number; particles10Um: number; particles25Um: number; particles50Um: number; particles100Um: number } -/** - * - * TODO: REPLACE - */ -export type meshtastic_protobufs_store_and_forward_Statistics = { - messagesTotal: number; - messagesSaved: number; - messagesMax: number; - upTime: number; - requests: number; - requestsHistory: number; - heartbeat: boolean; - returnMax: number; - returnWindow: number; -}; +export type app_device_WaypointPacket = { packet: meshtastic_protobufs_MeshPacket; data: app_device_NormalizedWaypoint } -/** - * - * Log levels, chosen to match python logging conventions. - */ -export type meshtastic_protobufs_to_radio_PayloadVariant = - | { packet: meshtastic_protobufs_MeshPacket } - | { wantConfigId: number } - | { disconnect: boolean } - | { xmodemPacket: meshtastic_protobufs_XModem } - | { mqttClientProxyMessage: meshtastic_protobufs_MqttClientProxyMessage }; +export type meshtastic_protobufs_mesh_packet_PayloadVariant = { decoded: meshtastic_protobufs_Data } | { encrypted: number[] } /** - * - * Log levels, chosen to match python logging conventions. + * + * Key native device metrics such as battery level */ -export type meshtastic_protobufs_log_record_Level = - | "unset" - | "critical" - | "error" - | "warning" - | "info" - | "debug" - | "trace"; +export type meshtastic_protobufs_DeviceMetrics = { batteryLevel: number; voltage: number; channelUtilization: number; airUtilTx: number } /** - * - * Key native device metrics such as battery level + * + * Serial connection status */ -export type meshtastic_protobufs_DeviceMetrics = { - batteryLevel: number; - voltage: number; - channelUtilization: number; - airUtilTx: number; -}; +export type meshtastic_protobufs_SerialConnectionStatus = { baud: number; isConnected: boolean } /** - * - * Compressed message payload + * + * Bit field of boolean configuration options, indicating which optional + * fields to include when assembling POSITION messages + * Longitude and latitude are always included (also time if GPS-synced) + * NOTE: the more fields are included, the larger the message will be - + * leading to longer airtime and a higher risk of packet loss */ -export type meshtastic_protobufs_Compressed = { - portnum: number; - data: number[]; -}; +export type meshtastic_protobufs_config_position_config_PositionFlags = "unset" | "altitude" | "altitudeMsl" | "geoidalSeparation" | "dop" | "hvdop" | "satinview" | "seqNo" | "timestamp" | "heading" | "speed" /** - * - * Position with static location information only for NodeDBLite + * + * A GPIO pin definition for remote hardware module */ -export type meshtastic_protobufs_PositionLite = { - latitudeI: number; - longitudeI: number; - altitude: number; - time: number; - locationSource: number; -}; +export type meshtastic_protobufs_RemoteHardwarePin = { gpioPin: number; name: string; type: number } /** - * - * TODO: REPLACE + * + * RemoteHardwarePins associated with a node */ -export type meshtastic_protobufs_store_and_forward_Variant = - | { stats: meshtastic_protobufs_store_and_forward_Statistics } - | { history: meshtastic_protobufs_store_and_forward_History } - | { heartbeat: meshtastic_protobufs_store_and_forward_Heartbeat } - | { empty: boolean }; +export type meshtastic_protobufs_NodeRemoteHardwarePin = { nodeNum: number; pin: meshtastic_protobufs_RemoteHardwarePin | null } + +export type meshtastic_protobufs_routing_Variant = { routeRequest: meshtastic_protobufs_RouteDiscovery } | { routeReply: meshtastic_protobufs_RouteDiscovery } | { errorReason: number } /** - * - * Ethernet or WiFi connection status + * + * This is the most compact possible representation for a set of channels. + * It includes only one PRIMARY channel (which must be first) and + * any SECONDARY channels. + * No DISABLED channels are included. + * This abstraction is used only on the the 'app side' of the world (ie python, javascript and android etc) to show a group of Channels as a (long) URL */ -export type meshtastic_protobufs_NetworkConnectionStatus = { - ipAddress: number; - isConnected: boolean; - isMqttConnected: boolean; - isSyslogConnected: boolean; -}; +export type meshtastic_protobufs_ChannelSet = { settings: meshtastic_protobufs_ChannelSettings[]; loraConfig: meshtastic_protobufs_config_LoRaConfig | null } /** - * - * Types of Measurements the telemetry module is equipped to handle + * + * This can be used for customizing the firmware distribution. If populated, + * show a secondary bootup screen with custom logo and text for 2.5 seconds. */ -export type meshtastic_protobufs_Telemetry = { - time: number; - variant: meshtastic_protobufs_telemetry_Variant | null; -}; +export type meshtastic_protobufs_OemStore = { oemIconWidth: number; oemIconHeight: number; oemIconBits: number[]; oemFont: number; oemText: string; oemAesKey: number[]; oemLocalConfig: meshtastic_protobufs_LocalConfig | null; oemLocalModuleConfig: meshtastic_protobufs_LocalModuleConfig | null } + +export type app_device_NeighborInfoPacket = { packet: meshtastic_protobufs_MeshPacket; data: meshtastic_protobufs_NeighborInfo } /** - * - * Shared constants between device and phone + * + * Supported I2C Sensors for telemetry in Meshtastic */ -export type meshtastic_protobufs_Constants = "zero" | "dataPayloadLen"; +export type meshtastic_protobufs_TelemetrySensorType = "sensorUnset" | "bme280" | "bme680" | "mcp9808" | "ina260" | "ina219" | "bmp280" | "shtc3" | "lps22" | "qmc6310" | "qmi8658" | "qmc5883L" | "sht31" | "pmsa003I" + +export type meshtastic_protobufs_x_modem_Control = "nul" | "soh" | "stx" | "eot" | "ack" | "nak" | "can" | "ctrlz" + +export type meshtastic_protobufs_telemetry_Variant = { deviceMetrics: meshtastic_protobufs_DeviceMetrics } | { environmentMetrics: meshtastic_protobufs_EnvironmentMetrics } | { airQualityMetrics: meshtastic_protobufs_AirQualityMetrics } /** - * - * Canned message module configuration. + * + * Configuration for both device and environment metrics */ -export type meshtastic_protobufs_RtttlConfig = { ringtone: string }; +export type meshtastic_protobufs_module_config_TelemetryConfig = { deviceUpdateInterval: number; environmentUpdateInterval: number; environmentMeasurementEnabled: boolean; environmentScreenEnabled: boolean; environmentDisplayFahrenheit: boolean; airQualityEnabled: boolean; airQualityInterval: number } -export type app_ipc_APMincutStringResults = { - apResult: number[]; - mincutResult: [number, number][]; - diffcenResult: { - [key: number]: { [key: number]: { [key: number]: number } }; - }; -}; +export type meshtastic_protobufs_LocalModuleConfig = { mqtt: meshtastic_protobufs_module_config_MqttConfig | null; serial: meshtastic_protobufs_module_config_SerialConfig | null; externalNotification: meshtastic_protobufs_module_config_ExternalNotificationConfig | null; storeForward: meshtastic_protobufs_module_config_StoreForwardConfig | null; rangeTest: meshtastic_protobufs_module_config_RangeTestConfig | null; telemetry: meshtastic_protobufs_module_config_TelemetryConfig | null; cannedMessage: meshtastic_protobufs_module_config_CannedMessageConfig | null; audio: meshtastic_protobufs_module_config_AudioConfig | null; remoteHardware: meshtastic_protobufs_module_config_RemoteHardwareConfig | null; neighborInfo: meshtastic_protobufs_module_config_NeighborInfoConfig | null; ambientLighting: meshtastic_protobufs_module_config_AmbientLightingConfig | null; detectionSensor: meshtastic_protobufs_module_config_DetectionSensorConfig | null; version: number } /** - * - * Serial Config + * + * Override OLED outo detect with this if it fails. + */ +export type meshtastic_protobufs_config_display_config_OledType = "oledAuto" | "oledSsd1306" | "oledSh1106" | "oledSh1107" + +export type app_device_NormalizedWaypoint = { id: number; latitude: number; longitude: number; expire: number; lockedTo: number; name: string; description: string; icon: number } + +/** + * + * Device metadata response */ -export type meshtastic_protobufs_module_config_SerialConfig = { - enabled: boolean; - echo: boolean; - rxd: number; - txd: number; - baud: number; - timeout: number; - mode: number; - overrideConsoleSerialPort: boolean; -}; +export type meshtastic_protobufs_DeviceMetadata = { firmwareVersion: string; deviceStateVersion: number; canShutdown: boolean; hasWifi: boolean; hasBluetooth: boolean; hasEthernet: boolean; role: number; positionFlags: number; hwModel: number; hasRemoteHardware: boolean } -export type app_device_ChannelMessageState = - | "pending" - | "acknowledged" - | { error: string }; +/** + * + * Log levels, chosen to match python logging conventions. + */ +export type meshtastic_protobufs_from_radio_PayloadVariant = { packet: meshtastic_protobufs_MeshPacket } | { myInfo: meshtastic_protobufs_MyNodeInfo } | { nodeInfo: meshtastic_protobufs_NodeInfo } | { config: meshtastic_protobufs_Config } | { logRecord: meshtastic_protobufs_LogRecord } | { configCompleteId: number } | { rebooted: boolean } | { moduleConfig: meshtastic_protobufs_ModuleConfig } | { channel: meshtastic_protobufs_Channel } | { queueStatus: meshtastic_protobufs_QueueStatus } | { xmodemPacket: meshtastic_protobufs_XModem } | { metadata: meshtastic_protobufs_DeviceMetadata } | { mqttClientProxyMessage: meshtastic_protobufs_MqttClientProxyMessage } /** - * + * + * Compressed message payload + */ +export type meshtastic_protobufs_Compressed = { portnum: number; data: number[] } + +/** + * * TODO: REPLACE */ -export type meshtastic_protobufs_ScreenFonts = - | "fontSmall" - | "fontMedium" - | "fontLarge"; +export type meshtastic_protobufs_StoreAndForward = { rr: number; variant: meshtastic_protobufs_store_and_forward_Variant | null } /** - * - * Waypoint message, used to share arbitrary locations across the mesh + * + * This information can be encoded as a QRcode/url so that other users can configure + * their radio to join the same channel. + * A note about how channel names are shown to users: channelname-X + * poundsymbol is a prefix used to indicate this is a channel name (idea from @professr). + * Where X is a letter from A-Z (base 26) representing a hash of the PSK for this + * channel - so that if the user changes anything about the channel (which does + * force a new PSK) this letter will also change. Thus preventing user confusion if + * two friends try to type in a channel name of "BobsChan" and then can't talk + * because their PSKs will be different. + * The PSK is hashed into this letter by "0x41 + [xor all bytes of the psk ] modulo 26" + * This also allows the option of someday if people have the PSK off (zero), the + * users COULD type in a channel name and be able to talk. + * FIXME: Add description of multi-channel support and how primary vs secondary channels are used. + * FIXME: explain how apps use channels for security. + * explain how remote settings and remote gpio are managed as an example */ -export type meshtastic_protobufs_Waypoint = { - id: number; - latitudeI: number; - longitudeI: number; - expire: number; - lockedTo: number; - name: string; - description: string; - icon: number; -}; +export type meshtastic_protobufs_ChannelSettings = { channelNum: number; psk: number[]; name: string; id: number; uplinkEnabled: boolean; downlinkEnabled: boolean } -export type app_device_UserPacket = { - packet: meshtastic_protobufs_MeshPacket; - data: meshtastic_protobufs_User; -}; +export type app_device_MeshChannel = { config: meshtastic_protobufs_Channel; lastInteraction: number; messages: app_device_ChannelMessageWithState[] } /** - * - * Supported I2C Sensors for telemetry in Meshtastic + * + * Full info on edges for a single node */ -export type meshtastic_protobufs_TelemetrySensorType = - | "sensorUnset" - | "bme280" - | "bme680" - | "mcp9808" - | "ina260" - | "ina219" - | "bmp280" - | "shtc3" - | "lps22" - | "qmc6310" - | "qmi8658" - | "qmc5883L" - | "sht31" - | "pmsa003I"; - -/** - * - * Payload Variant +export type meshtastic_protobufs_NeighborInfo = { nodeId: number; lastSentById: number; nodeBroadcastIntervalSecs: number; neighbors: meshtastic_protobufs_Neighbor[] } + +/** + * + * Debug output from the device. + * To minimize the size of records inside the device code, if a time/source/level is not set + * on the message it is assumed to be a continuation of the previously sent message. + * This allows the device code to use fixed maxlen 64 byte strings for messages, + * and then extend as needed by emitting multiple records. */ -export type meshtastic_protobufs_config_PayloadVariant = - | { device: meshtastic_protobufs_config_DeviceConfig } - | { position: meshtastic_protobufs_config_PositionConfig } - | { power: meshtastic_protobufs_config_PowerConfig } - | { network: meshtastic_protobufs_config_NetworkConfig } - | { display: meshtastic_protobufs_config_DisplayConfig } - | { lora: meshtastic_protobufs_config_LoRaConfig } - | { bluetooth: meshtastic_protobufs_config_BluetoothConfig }; - -export type app_device_MeshNode = { - nodeNum: number; - lastHeard: app_device_LastHeardMetadata | null; - user: meshtastic_protobufs_User | null; - deviceMetrics: app_device_MeshNodeDeviceMetrics[]; - environmentMetrics: app_device_MeshNodeEnvironmentMetrics[]; - positionMetrics: app_device_NormalizedPosition[]; -}; - -/** - * - * Response envelope for node_remote_hardware_pins +export type meshtastic_protobufs_LogRecord = { message: string; time: number; source: string; level: number } + +export type app_device_PositionPacket = { packet: meshtastic_protobufs_MeshPacket; data: meshtastic_protobufs_Position } + +/** + * + * Bluetooth connection status */ -export type meshtastic_protobufs_NodeRemoteHardwarePinsResponse = { - nodeRemoteHardwarePins: meshtastic_protobufs_NodeRemoteHardwarePin[]; -}; +export type meshtastic_protobufs_BluetoothConnectionStatus = { pin: number; rssi: number; isConnected: boolean } /** - * - * Lora Config + * + * NeighborInfoModule Config */ -export type meshtastic_protobufs_config_LoRaConfig = { - usePreset: boolean; - modemPreset: number; - bandwidth: number; - spreadFactor: number; - codingRate: number; - frequencyOffset: number; - region: number; - hopLimit: number; - txEnabled: boolean; - txPower: number; - channelNum: number; - overrideDutyCycle: boolean; - sx126XRxBoostedGain: boolean; - overrideFrequency: number; - ignoreIncoming: number[]; -}; - -/** - * - * The priority of this message for sending. - * Higher priorities are sent first (when managing the transmit queue). - * This field is never sent over the air, it is only used internally inside of a local device node. - * API clients (either on the local node or connected directly to the node) - * can set this parameter if necessary. - * (values must be <= 127 to keep protobuf field to one byte in size. - * Detailed background on this field: - * I noticed a funny side effect of lora being so slow: Usually when making - * a protocol there isn’t much need to use message priority to change the order - * of transmission (because interfaces are fairly fast). - * But for lora where packets can take a few seconds each, it is very important - * to make sure that critical packets are sent ASAP. - * In the case of meshtastic that means we want to send protocol acks as soon as possible - * (to prevent unneeded retransmissions), we want routing messages to be sent next, - * then messages marked as reliable and finally 'background' packets like periodic position updates. - * So I bit the bullet and implemented a new (internal - not sent over the air) - * field in MeshPacket called 'priority'. - * And the transmission queue in the router object is now a priority queue. +export type meshtastic_protobufs_module_config_NeighborInfoConfig = { enabled: boolean; updateInterval: number } + +/** + * + * a gps position */ -export type meshtastic_protobufs_mesh_packet_Priority = - | "unset" - | "min" - | "background" - | "default" - | "reliable" - | "ack" - | "max"; +export type meshtastic_protobufs_Position = { latitudeI: number; longitudeI: number; altitude: number; time: number; locationSource: number; altitudeSource: number; timestamp: number; timestampMillisAdjust: number; altitudeHae: number; altitudeGeoidalSeparation: number; pdop: number; hdop: number; vdop: number; gpsAccuracy: number; groundSpeed: number; groundTrack: number; fixQuality: number; fixType: number; satsInView: number; sensorId: number; nextUpdate: number; seqNumber: number } + +export type meshtastic_protobufs_LocalConfig = { device: meshtastic_protobufs_config_DeviceConfig | null; position: meshtastic_protobufs_config_PositionConfig | null; power: meshtastic_protobufs_config_PowerConfig | null; network: meshtastic_protobufs_config_NetworkConfig | null; display: meshtastic_protobufs_config_DisplayConfig | null; lora: meshtastic_protobufs_config_LoRaConfig | null; bluetooth: meshtastic_protobufs_config_BluetoothConfig | null; version: number } /** - * - * Audio Config for codec2 voice + * + * This message is handled by the Admin module and is responsible for all settings/channel read/write operations. + * This message is used to do settings operations to both remote AND local nodes. + * (Prior to 1.2 these operations were done via special ToRadio operations) */ -export type meshtastic_protobufs_module_config_AudioConfig = { - codec2Enabled: boolean; - pttPin: number; - bitrate: number; - i2SWs: number; - i2SSd: number; - i2SDin: number; - i2SSck: number; -}; +export type meshtastic_protobufs_AdminMessage = { payloadVariant: meshtastic_protobufs_admin_message_PayloadVariant | null } + +export type app_ipc_DeviceBulkConfig = { radio: meshtastic_protobufs_LocalConfig | null; module: meshtastic_protobufs_LocalModuleConfig | null; channels: meshtastic_protobufs_Channel[] | null } /** - * - * Log levels, chosen to match python logging conventions. + * + * (Formerly called SubPacket) + * The payload portion fo a packet, this is the actual bytes that are sent + * inside a radio packet (because from/to are broken out by the comms library) */ -export type meshtastic_protobufs_from_radio_PayloadVariant = - | { packet: meshtastic_protobufs_MeshPacket } - | { myInfo: meshtastic_protobufs_MyNodeInfo } - | { nodeInfo: meshtastic_protobufs_NodeInfo } - | { config: meshtastic_protobufs_Config } - | { logRecord: meshtastic_protobufs_LogRecord } - | { configCompleteId: number } - | { rebooted: boolean } - | { moduleConfig: meshtastic_protobufs_ModuleConfig } - | { channel: meshtastic_protobufs_Channel } - | { queueStatus: meshtastic_protobufs_QueueStatus } - | { xmodemPacket: meshtastic_protobufs_XModem } - | { metadata: meshtastic_protobufs_DeviceMetadata } - | { mqttClientProxyMessage: meshtastic_protobufs_MqttClientProxyMessage }; - -export type app_device_MeshChannel = { - config: meshtastic_protobufs_Channel; - lastInteraction: number; - messages: app_device_ChannelMessageWithState[]; -}; - -/** - * - * This message wraps a MeshPacket with extra metadata about the sender and how it arrived. +export type meshtastic_protobufs_Data = { portnum: number; payload: number[]; wantResponse: boolean; dest: number; source: number; requestId: number; replyId: number; emoji: number } + +/** + * + * TODO: REPLACE */ -export type meshtastic_protobufs_ServiceEnvelope = { - packet: meshtastic_protobufs_MeshPacket | null; - channelId: string; - gatewayId: string; -}; +export type meshtastic_protobufs_module_config_serial_config_SerialMode = "default" | "simple" | "proto" | "textmsg" | "nmea" | "caltopo" /** - * - * How this channel is being used (or not). - * Note: this field is an enum to give us options for the future. - * In particular, someday we might make a 'SCANNING' option. - * SCANNING channels could have different frequencies and the radio would - * occasionally check that freq to see if anything is being transmitted. - * For devices that have multiple physical radios attached, we could keep multiple PRIMARY/SCANNING channels active at once to allow - * cross band routing as needed. - * If a device has only a single radio (the common case) only one channel can be PRIMARY at a time - * (but any number of SECONDARY channels can't be sent received on that common frequency) + * Ambient Lighting Module - Settings for control of onboard LEDs to allow users to adjust the brightness levels and respective color levels. + * Initially created for the RAK14001 RGB LED module. */ -export type meshtastic_protobufs_channel_Role = - | "disabled" - | "primary" - | "secondary"; +export type meshtastic_protobufs_module_config_AmbientLightingConfig = { ledState: boolean; current: number; red: number; green: number; blue: number } -export type meshtastic_protobufs_config_network_config_IpV4Config = { - ip: number; - gateway: number; - subnet: number; - dns: number; -}; +/** + * + * External Notifications Config + */ +export type meshtastic_protobufs_module_config_ExternalNotificationConfig = { enabled: boolean; outputMs: number; output: number; outputVibra: number; outputBuzzer: number; active: boolean; alertMessage: boolean; alertMessageVibra: boolean; alertMessageBuzzer: boolean; alertBell: boolean; alertBellVibra: boolean; alertBellBuzzer: boolean; usePwm: boolean; nagTimeout: number } -export type meshtastic_protobufs_RemoteHardwarePinType = - | "unknown" - | "digitalRead" - | "digitalWrite"; +export type app_device_ChannelMessageWithState = { payload: app_device_ChannelMessagePayload; state: app_device_ChannelMessageState } /** - * - * Preferences for the RangeTestModule + * + * Identify if this is a delayed packet */ -export type meshtastic_protobufs_module_config_RangeTestConfig = { - enabled: boolean; - sender: number; - save: boolean; -}; +export type meshtastic_protobufs_mesh_packet_Delayed = "noDelay" | "broadcast" | "direct" -export type meshtastic_protobufs_routing_Variant = - | { routeRequest: meshtastic_protobufs_RouteDiscovery } - | { routeReply: meshtastic_protobufs_RouteDiscovery } - | { errorReason: number }; +/** + * + * MQTT Client Config + */ +export type meshtastic_protobufs_module_config_MqttConfig = { enabled: boolean; address: string; username: string; password: string; encryptionEnabled: boolean; jsonEnabled: boolean; tlsEnabled: boolean; root: string; proxyToClientEnabled: boolean } /** - * - * This message is never sent over the wire, but it is used for serializing DB - * state to flash in the device code - * FIXME, since we write this each time we enter deep sleep (and have infinite - * flash) it would be better to use some sort of append only data structure for - * the receive queue and use the preferences store for the other stuff + * + * TODO: REPLACE */ -export type meshtastic_protobufs_DeviceState = { - myNode: meshtastic_protobufs_MyNodeInfo | null; - owner: meshtastic_protobufs_User | null; - receiveQueue: meshtastic_protobufs_MeshPacket[]; - version: number; - rxTextMessage: meshtastic_protobufs_MeshPacket | null; - noSave: boolean; - didGpsReset: boolean; - rxWaypoint: meshtastic_protobufs_MeshPacket | null; - nodeRemoteHardwarePins: meshtastic_protobufs_NodeRemoteHardwarePin[]; - nodeDbLite: meshtastic_protobufs_NodeInfoLite[]; -}; - -export type app_device_TextPacket = { - packet: meshtastic_protobufs_MeshPacket; - data: string; -}; - -/** - * - * Bluetooth connection status +export type meshtastic_protobufs_store_and_forward_Statistics = { messagesTotal: number; messagesSaved: number; messagesMax: number; upTime: number; requests: number; requestsHistory: number; heartbeat: boolean; returnMax: number; returnWindow: number } + +/** + * + * Types of Measurements the telemetry module is equipped to handle */ -export type meshtastic_protobufs_BluetoothConnectionStatus = { - pin: number; - rssi: number; - isConnected: boolean; -}; +export type meshtastic_protobufs_Telemetry = { time: number; variant: meshtastic_protobufs_telemetry_Variant | null } + +export type app_device_MeshNode = { nodeNum: number; lastHeard: app_device_LastHeardMetadata | null; user: meshtastic_protobufs_User | null; deviceMetrics: app_device_MeshNodeDeviceMetrics[]; environmentMetrics: app_device_MeshNodeEnvironmentMetrics[]; positionMetrics: app_device_NormalizedPosition[] } /** - * - * The on-disk saved channels + * + * TODO: REPLACE */ -export type meshtastic_protobufs_ChannelFile = { - channels: meshtastic_protobufs_Channel[]; - version: number; -}; +export type meshtastic_protobufs_hardware_message_Type = "unset" | "writeGpios" | "watchGpios" | "gpiosChanged" | "readGpios" | "readGpiosReply" -export type app_device_NormalizedWaypoint = { - id: number; - latitude: number; - longitude: number; - expire: number; - lockedTo: number; - name: string; - description: string; - icon: number; -}; +/** + * + * This message will be proxied over the PhoneAPI for the client to deliver to the MQTT server + */ +export type meshtastic_protobufs_MqttClientProxyMessage = { topic: string; retained: boolean; payloadVariant: meshtastic_protobufs_mqtt_client_proxy_message_PayloadVariant | null } /** - * - * Configuration for both device and environment metrics + * + * TODO: REPLACE + */ +export type meshtastic_protobufs_store_and_forward_History = { historyMessages: number; window: number; lastRequest: number } + +/** + * + * Response envelope for node_remote_hardware_pins + */ +export type meshtastic_protobufs_NodeRemoteHardwarePinsResponse = { nodeRemoteHardwarePins: meshtastic_protobufs_NodeRemoteHardwarePin[] } + +export type meshtastic_protobufs_config_display_config_DisplayMode = "default" | "twocolor" | "inverted" | "color" + +export type meshtastic_protobufs_config_network_config_AddressMode = "dhcp" | "static" + +/** + * + * Defines the device's behavior for how messages are rebroadcast + */ +export type meshtastic_protobufs_config_device_config_RebroadcastMode = "all" | "allSkipDecoding" | "localOnly" + +export type app_ipc_APMincutStringResults = { apResult: number[]; mincutResult: ([number, number])[]; diffcenResult: { [key: number]: { [key: number]: { [key: number]: number } } } } + +export type app_ipc_ConfigurationStatus = { deviceKey: string; successful: boolean; message: string | null } + +/** + * + * Baudrate for codec2 voice + */ +export type meshtastic_protobufs_module_config_audio_config_AudioBaud = "codec2Default" | "codec23200" | "codec22400" | "codec21600" | "codec21400" | "codec21300" | "codec21200" | "codec2700" | "codec2700B" + +/** + * + * Detection Sensor Module Config */ -export type meshtastic_protobufs_module_config_TelemetryConfig = { - deviceUpdateInterval: number; - environmentUpdateInterval: number; - environmentMeasurementEnabled: boolean; - environmentScreenEnabled: boolean; - environmentDisplayFahrenheit: boolean; - airQualityEnabled: boolean; - airQualityInterval: number; -}; +export type meshtastic_protobufs_module_config_DetectionSensorConfig = { enabled: boolean; minimumBroadcastSecs: number; stateBroadcastSecs: number; sendBell: boolean; name: string; monitorPin: number; detectionTriggeredHigh: boolean; usePullup: boolean } /** - * + * * TODO: REPLACE */ -export type meshtastic_protobufs_admin_message_PayloadVariant = - | { getChannelRequest: number } - | { getChannelResponse: meshtastic_protobufs_Channel } - | { getOwnerRequest: boolean } - | { getOwnerResponse: meshtastic_protobufs_User } - | { getConfigRequest: number } - | { getConfigResponse: meshtastic_protobufs_Config } - | { getModuleConfigRequest: number } - | { getModuleConfigResponse: meshtastic_protobufs_ModuleConfig } - | { getCannedMessageModuleMessagesRequest: boolean } - | { getCannedMessageModuleMessagesResponse: string } - | { getDeviceMetadataRequest: boolean } - | { getDeviceMetadataResponse: meshtastic_protobufs_DeviceMetadata } - | { getRingtoneRequest: boolean } - | { getRingtoneResponse: string } - | { getDeviceConnectionStatusRequest: boolean } - | { - getDeviceConnectionStatusResponse: meshtastic_protobufs_DeviceConnectionStatus; - } - | { setHamMode: meshtastic_protobufs_HamParameters } - | { getNodeRemoteHardwarePinsRequest: boolean } - | { - getNodeRemoteHardwarePinsResponse: meshtastic_protobufs_NodeRemoteHardwarePinsResponse; - } - | { setOwner: meshtastic_protobufs_User } - | { setChannel: meshtastic_protobufs_Channel } - | { setConfig: meshtastic_protobufs_Config } - | { setModuleConfig: meshtastic_protobufs_ModuleConfig } - | { setCannedMessageModuleMessages: string } - | { setRingtoneMessage: string } - | { beginEditSettings: boolean } - | { commitEditSettings: boolean } - | { rebootOtaSeconds: number } - | { exitSimulator: boolean } - | { rebootSeconds: number } - | { shutdownSeconds: number } - | { factoryReset: number } - | { nodedbReset: number }; - -export type app_device_NormalizedNodeInfo = { - num: number; - user: meshtastic_protobufs_User | null; - position: meshtastic_protobufs_Position | null; - snr: number; - lastHeard: number; - deviceMetrics: meshtastic_protobufs_DeviceMetrics | null; - channel: number; -}; - -/** - * - * (Formerly called SubPacket) - * The payload portion fo a packet, this is the actual bytes that are sent - * inside a radio packet (because from/to are broken out by the comms library) +export type meshtastic_protobufs_store_and_forward_Heartbeat = { period: number; secondary: number } + +/** + * + * Preferences for the RangeTestModule */ -export type meshtastic_protobufs_Data = { - portnum: number; - payload: number[]; - wantResponse: boolean; - dest: number; - source: number; - requestId: number; - replyId: number; - emoji: number; -}; +export type meshtastic_protobufs_module_config_RangeTestConfig = { enabled: boolean; sender: number; save: boolean } /** - * + * * TODO: REPLACE */ -export type meshtastic_protobufs_hardware_message_Type = - | "unset" - | "writeGpios" - | "watchGpios" - | "gpiosChanged" - | "readGpios" - | "readGpiosReply"; +export type meshtastic_protobufs_admin_message_ConfigType = "deviceConfig" | "positionConfig" | "powerConfig" | "networkConfig" | "displayConfig" | "loraConfig" | "bluetoothConfig" + +export type app_device_MeshDevice = { configId: number; ready: boolean; status: app_device_SerialDeviceStatus; channels: { [key: number]: app_device_MeshChannel }; config: meshtastic_protobufs_LocalConfig; moduleConfig: meshtastic_protobufs_LocalModuleConfig; myNodeInfo: meshtastic_protobufs_MyNodeInfo; nodes: { [key: number]: app_device_MeshNode }; regionUnset: boolean; deviceMetrics: meshtastic_protobufs_DeviceMetrics; waypoints: { [key: number]: app_device_NormalizedWaypoint }; neighbors: { [key: number]: app_device_NeighborInfoPacket }; configInProgress: boolean } /** - * - * Error codes for critical errors - * The device might report these fault codes on the screen. - * If you encounter a fault code, please post on the meshtastic.discourse.group - * and we'll try to help. + * + * This abstraction is used to contain any configuration for provisioning a node on any client. + * It is useful for importing and exporting configurations. */ -export type meshtastic_protobufs_CriticalErrorCode = - | "none" - | "txWatchdog" - | "sleepEnterWait" - | "noRadio" - | "unspecified" - | "ubloxUnitFailed" - | "noAxp192" - | "invalidRadioSetting" - | "transmitFailed" - | "brownout" - | "sx1262Failure" - | "radioSpiBug"; - -export type meshtastic_protobufs_QueueStatus = { - res: number; - free: number; - maxlen: number; - meshPacketId: number; -}; - -/** - * - * WiFi connection status +export type meshtastic_protobufs_DeviceProfile = { longName: string | null; shortName: string | null; channelUrl: string | null; config: meshtastic_protobufs_LocalConfig | null; moduleConfig: meshtastic_protobufs_LocalModuleConfig | null } + +/** + * + * Broadcast when a newly powered mesh node wants to find a node num it can use + * Sent from the phone over bluetooth to set the user id for the owner of this node. + * Also sent from nodes to each other when a new node signs on (so all clients can have this info) + * The algorithm is as follows: + * when a node starts up, it broadcasts their user and the normal flow is for all + * other nodes to reply with their User as well (so the new node can build its nodedb) + * If a node ever receives a User (not just the first broadcast) message where + * the sender node number equals our node number, that indicates a collision has + * occurred and the following steps should happen: + * If the receiving node (that was already in the mesh)'s macaddr is LOWER than the + * new User who just tried to sign in: it gets to keep its nodenum. + * We send a broadcast message of OUR User (we use a broadcast so that the other node can + * receive our message, considering we have the same id - it also serves to let + * observers correct their nodedb) - this case is rare so it should be okay. + * If any node receives a User where the macaddr is GTE than their local macaddr, + * they have been vetoed and should pick a new random nodenum (filtering against + * whatever it knows about the nodedb) and rebroadcast their User. + * A few nodenums are reserved and will never be requested: + * 0xff - broadcast + * 0 through 3 - for future use */ -export type meshtastic_protobufs_WifiConnectionStatus = { - status: meshtastic_protobufs_NetworkConnectionStatus | null; - ssid: string; - rssi: number; -}; +export type meshtastic_protobufs_User = { id: string; longName: string; shortName: string; macaddr: number[]; hwModel: number; isLicensed: boolean } + +export type app_device_NormalizedNodeInfo = { num: number; user: meshtastic_protobufs_User | null; position: meshtastic_protobufs_Position | null; snr: number; lastHeard: number; deviceMetrics: meshtastic_protobufs_DeviceMetrics | null; channel: number } + +export type meshtastic_protobufs_config_lo_ra_config_RegionCode = "unset" | "us" | "eu433" | "eu868" | "cn" | "jp" | "anz" | "kr" | "tw" | "ru" | "in" | "nz865" | "th" | "lora24" | "ua433" | "ua868" /** - * - * Weather station or other environmental metrics + * + * Shared constants between device and phone */ -export type meshtastic_protobufs_EnvironmentMetrics = { - temperature: number; - relativeHumidity: number; - barometricPressure: number; - gasResistance: number; - voltage: number; - current: number; -}; +export type meshtastic_protobufs_Constants = "zero" | "dataPayloadLen" + +export type app_device_MeshNodePositionMetrics = { metrics: app_device_NormalizedPosition; timestamp: number; snr: number } + +export type app_device_TextPacket = { packet: meshtastic_protobufs_MeshPacket; data: string } /** - * - * External Notifications Config + * + * RemoteHardwareModule Config */ -export type meshtastic_protobufs_module_config_ExternalNotificationConfig = { - enabled: boolean; - outputMs: number; - output: number; - outputVibra: number; - outputBuzzer: number; - active: boolean; - alertMessage: boolean; - alertMessageVibra: boolean; - alertMessageBuzzer: boolean; - alertBell: boolean; - alertBellVibra: boolean; - alertBellBuzzer: boolean; - usePwm: boolean; - nagTimeout: number; -}; - -/** - * - * Debug output from the device. - * To minimize the size of records inside the device code, if a time/source/level is not set - * on the message it is assumed to be a continuation of the previously sent message. - * This allows the device code to use fixed maxlen 64 byte strings for messages, - * and then extend as needed by emitting multiple records. +export type meshtastic_protobufs_module_config_RemoteHardwareConfig = { enabled: boolean; allowUndefinedPinAccess: boolean; availablePins: meshtastic_protobufs_RemoteHardwarePin[] } + +export type meshtastic_protobufs_QueueStatus = { res: number; free: number; maxlen: number; meshPacketId: number } + +/** + * + * Parameters for setting up Meshtastic for ameteur radio usage */ -export type meshtastic_protobufs_LogRecord = { - message: string; - time: number; - source: string; - level: number; -}; +export type meshtastic_protobufs_HamParameters = { callSign: string; txPower: number; frequency: number; shortName: string } /** - * - * This information can be encoded as a QRcode/url so that other users can configure - * their radio to join the same channel. - * A note about how channel names are shown to users: channelname-X - * poundsymbol is a prefix used to indicate this is a channel name (idea from @professr). - * Where X is a letter from A-Z (base 26) representing a hash of the PSK for this - * channel - so that if the user changes anything about the channel (which does - * force a new PSK) this letter will also change. Thus preventing user confusion if - * two friends try to type in a channel name of "BobsChan" and then can't talk - * because their PSKs will be different. - * The PSK is hashed into this letter by "0x41 + [xor all bytes of the psk ] modulo 26" - * This also allows the option of someday if people have the PSK off (zero), the - * users COULD type in a channel name and be able to talk. - * FIXME: Add description of multi-channel support and how primary vs secondary channels are used. - * FIXME: explain how apps use channels for security. - * explain how remote settings and remote gpio are managed as an example + * + * WiFi connection status + */ +export type meshtastic_protobufs_WifiConnectionStatus = { status: meshtastic_protobufs_NetworkConnectionStatus | null; ssid: string; rssi: number } + +/** + * + * TODO: REPLACE */ -export type meshtastic_protobufs_ChannelSettings = { - channelNum: number; - psk: number[]; - name: string; - id: number; - uplinkEnabled: boolean; - downlinkEnabled: boolean; -}; +export type meshtastic_protobufs_module_config_canned_message_config_InputEventChar = "none" | "up" | "down" | "left" | "right" | "select" | "back" | "cancel" -export type app_device_WaypointPacket = { - packet: meshtastic_protobufs_MeshPacket; - data: app_device_NormalizedWaypoint; -}; +/** + * + * The priority of this message for sending. + * Higher priorities are sent first (when managing the transmit queue). + * This field is never sent over the air, it is only used internally inside of a local device node. + * API clients (either on the local node or connected directly to the node) + * can set this parameter if necessary. + * (values must be <= 127 to keep protobuf field to one byte in size. + * Detailed background on this field: + * I noticed a funny side effect of lora being so slow: Usually when making + * a protocol there isn’t much need to use message priority to change the order + * of transmission (because interfaces are fairly fast). + * But for lora where packets can take a few seconds each, it is very important + * to make sure that critical packets are sent ASAP. + * In the case of meshtastic that means we want to send protocol acks as soon as possible + * (to prevent unneeded retransmissions), we want routing messages to be sent next, + * then messages marked as reliable and finally 'background' packets like periodic position updates. + * So I bit the bullet and implemented a new (internal - not sent over the air) + * field in MeshPacket called 'priority'. + * And the transmission queue in the router object is now a priority queue. + */ +export type meshtastic_protobufs_mesh_packet_Priority = "unset" | "min" | "background" | "default" | "reliable" | "ack" | "max" /** - * - * NeighborInfoModule Config + * + * A failure in delivering a message (usually used for routing control messages, but might be provided in addition to ack.fail_id to provide + * details on the type of failure). */ -export type meshtastic_protobufs_module_config_NeighborInfoConfig = { - enabled: boolean; - updateInterval: number; -}; +export type meshtastic_protobufs_routing_Error = "none" | "noRoute" | "gotNak" | "timeout" | "noInterface" | "maxRetransmit" | "noChannel" | "tooLarge" | "noResponse" | "dutyCycleLimit" | "badRequest" | "notAuthorized" /** - * - * Packets/commands to the radio will be written (reliably) to the toRadio characteristic. - * Once the write completes the phone can assume it is handled. + * + * Canned message module configuration. */ -export type meshtastic_protobufs_ToRadio = { - payloadVariant: meshtastic_protobufs_to_radio_PayloadVariant | null; -}; +export type meshtastic_protobufs_CannedMessageModuleConfig = { messages: string } /** - * - * Defines the device's behavior for how messages are rebroadcast + * + * Waypoint message, used to share arbitrary locations across the mesh */ -export type meshtastic_protobufs_config_device_config_RebroadcastMode = - | "all" - | "allSkipDecoding" - | "localOnly"; +export type meshtastic_protobufs_Waypoint = { id: number; latitudeI: number; longitudeI: number; expire: number; lockedTo: number; name: string; description: string; icon: number } /** - * - * Module Config + * + * TODO: REPLACE */ -export type meshtastic_protobufs_ModuleConfig = { - payloadVariant: meshtastic_protobufs_module_config_PayloadVariant | null; -}; +export type meshtastic_protobufs_module_config_serial_config_SerialBaud = "baudDefault" | "baud110" | "baud300" | "baud600" | "baud1200" | "baud2400" | "baud4800" | "baud9600" | "baud19200" | "baud38400" | "baud57600" | "baud115200" | "baud230400" | "baud460800" | "baud576000" | "baud921600" /** - * - * Air quality metrics + * + * Note: these enum names must EXACTLY match the string used in the device + * bin/build-all.sh script. + * Because they will be used to find firmware filenames in the android app for OTA updates. + * To match the old style filenames, _ is converted to -, p is converted to . */ -export type meshtastic_protobufs_AirQualityMetrics = { - pm10Standard: number; - pm25Standard: number; - pm100Standard: number; - pm10Environmental: number; - pm25Environmental: number; - pm100Environmental: number; - particles03Um: number; - particles05Um: number; - particles10Um: number; - particles25Um: number; - particles50Um: number; - particles100Um: number; -}; - -/** - * - * a gps position +export type meshtastic_protobufs_HardwareModel = "unset" | "tloraV2" | "tloraV1" | "tloraV211P6" | "tbeam" | "heltecV20" | "tbeamV0P7" | "techo" | "tloraV11P3" | "rak4631" | "heltecV21" | "heltecV1" | "lilygoTbeamS3Core" | "rak11200" | "nanoG1" | "tloraV211P8" | "tloraT3S3" | "nanoG1Explorer" | "nanoG2Ultra" | "loraType" | "stationG1" | "rak11310" | "loraRelayV1" | "nrf52840Dk" | "ppr" | "genieblocks" | "nrf52Unknown" | "portduino" | "androidSim" | "diyV1" | "nrf52840Pca10059" | "drDev" | "m5Stack" | "heltecV3" | "heltecWslV3" | "betafpv2400Tx" | "betafpv900NanoTx" | "rpiPico" | "heltecWirelessTracker" | "heltecWirelessPaper" | "tdeck" | "twatchS3" | "picomputerS3" | "heltecHt62" | "privateHw" + +/** + * + * The bluetooth to device link: + * Old BTLE protocol docs from TODO, merge in above and make real docs... + * use protocol buffers, and NanoPB + * messages from device to phone: + * POSITION_UPDATE (..., time) + * TEXT_RECEIVED(from, text, time) + * OPAQUE_RECEIVED(from, payload, time) (for signal messages or other applications) + * messages from phone to device: + * SET_MYID(id, human readable long, human readable short) (send down the unique ID + * string used for this node, a human readable string shown for that id, and a very + * short human readable string suitable for oled screen) SEND_OPAQUE(dest, payload) + * (for signal messages or other applications) SEND_TEXT(dest, text) Get all + * nodes() (returns list of nodes, with full info, last time seen, loc, battery + * level etc) SET_CONFIG (switches device to a new set of radio params and + * preshared key, drops all existing nodes, force our node to rejoin this new group) + * Full information about a node on the mesh */ -export type meshtastic_protobufs_Position = { - latitudeI: number; - longitudeI: number; - altitude: number; - time: number; - locationSource: number; - altitudeSource: number; - timestamp: number; - timestampMillisAdjust: number; - altitudeHae: number; - altitudeGeoidalSeparation: number; - pdop: number; - hdop: number; - vdop: number; - gpsAccuracy: number; - groundSpeed: number; - groundTrack: number; - fixQuality: number; - fixType: number; - satsInView: number; - sensorId: number; - nextUpdate: number; - seqNumber: number; -}; - -/** - * - * Canned message module configuration. +export type meshtastic_protobufs_NodeInfo = { num: number; user: meshtastic_protobufs_User | null; position: meshtastic_protobufs_Position | null; snr: number; lastHeard: number; deviceMetrics: meshtastic_protobufs_DeviceMetrics | null; channel: number } + +export type meshtastic_protobufs_XModem = { control: number; seq: number; crc16: number; buffer: number[] } + +/** + * + * Log levels, chosen to match python logging conventions. */ -export type meshtastic_protobufs_CannedMessageModuleConfig = { - messages: string; -}; +export type meshtastic_protobufs_to_radio_PayloadVariant = { packet: meshtastic_protobufs_MeshPacket } | { wantConfigId: number } | { disconnect: boolean } | { xmodemPacket: meshtastic_protobufs_XModem } | { mqttClientProxyMessage: meshtastic_protobufs_MqttClientProxyMessage } /** - * - * The actual service envelope payload or text for mqtt pub / sub + * + * How the location was acquired: manual, onboard GPS, external (EUD) GPS */ -export type meshtastic_protobufs_mqtt_client_proxy_message_PayloadVariant = - | { data: number[] } - | { text: string }; +export type meshtastic_protobufs_position_LocSource = "locUnset" | "locManual" | "locInternal" | "locExternal" /** - * - * Network Config + * + * Payload Variant */ -export type meshtastic_protobufs_config_NetworkConfig = { - wifiEnabled: boolean; - wifiSsid: string; - wifiPsk: string; - ntpServer: string; - ethEnabled: boolean; - addressMode: number; - ipv4Config: meshtastic_protobufs_config_network_config_IpV4Config | null; - rsyslogServer: string; -}; - -export type app_device_SerialDeviceStatus = - | "restarting" - | "disconnected" - | "connecting" - | "reconnecting" - | "connected" - | "configuring" - | "configured"; - -/** - * - * This message will be proxied over the PhoneAPI for the client to deliver to the MQTT server +export type meshtastic_protobufs_config_PayloadVariant = { device: meshtastic_protobufs_config_DeviceConfig } | { position: meshtastic_protobufs_config_PositionConfig } | { power: meshtastic_protobufs_config_PowerConfig } | { network: meshtastic_protobufs_config_NetworkConfig } | { display: meshtastic_protobufs_config_DisplayConfig } | { lora: meshtastic_protobufs_config_LoRaConfig } | { bluetooth: meshtastic_protobufs_config_BluetoothConfig } + +/** + * + * TODO: REPLACE */ -export type meshtastic_protobufs_MqttClientProxyMessage = { - topic: string; - retained: boolean; - payloadVariant: meshtastic_protobufs_mqtt_client_proxy_message_PayloadVariant | null; -}; +export type meshtastic_protobufs_module_config_PayloadVariant = { mqtt: meshtastic_protobufs_module_config_MqttConfig } | { serial: meshtastic_protobufs_module_config_SerialConfig } | { externalNotification: meshtastic_protobufs_module_config_ExternalNotificationConfig } | { storeForward: meshtastic_protobufs_module_config_StoreForwardConfig } | { rangeTest: meshtastic_protobufs_module_config_RangeTestConfig } | { telemetry: meshtastic_protobufs_module_config_TelemetryConfig } | { cannedMessage: meshtastic_protobufs_module_config_CannedMessageConfig } | { audio: meshtastic_protobufs_module_config_AudioConfig } | { remoteHardware: meshtastic_protobufs_module_config_RemoteHardwareConfig } | { neighborInfo: meshtastic_protobufs_module_config_NeighborInfoConfig } | { ambientLighting: meshtastic_protobufs_module_config_AmbientLightingConfig } | { detectionSensor: meshtastic_protobufs_module_config_DetectionSensorConfig } + +export type app_device_ChannelMessagePayload = ({ type: "text" } & app_device_TextPacket) | ({ type: "waypoint" } & app_device_WaypointPacket) /** - * - * Ethernet connection status + * + * The actual service envelope payload or text for mqtt pub / sub */ -export type meshtastic_protobufs_EthernetConnectionStatus = { - status: meshtastic_protobufs_NetworkConnectionStatus | null; -}; - -export type meshtastic_protobufs_XModem = { - control: number; - seq: number; - crc16: number; - buffer: number[]; -}; - -export type meshtastic_protobufs_config_network_config_AddressMode = - | "dhcp" - | "static"; - -export type app_ipc_ConfigurationStatus = { - deviceKey: string; - successful: boolean; - message: string | null; -}; +export type meshtastic_protobufs_mqtt_client_proxy_message_PayloadVariant = { data: number[] } | { text: string } + +export type meshtastic_protobufs_RemoteHardwarePinType = "unknown" | "digitalRead" | "digitalWrite" + diff --git a/src/features/device/sagas.ts b/src/features/device/sagas.ts index cad8cb3e..08aacf5f 100644 --- a/src/features/device/sagas.ts +++ b/src/features/device/sagas.ts @@ -132,7 +132,6 @@ function* initializeApplicationWorker( action: ReturnType, ) { try { - yield call(invoke, "initialize_graph_state"); yield call(subscribeAll); } catch (error) { yield put( @@ -170,8 +169,6 @@ function* connectToDeviceWorker( // * Can't block as these are infinite loops subscribeTask = yield fork(subscribeAll) as unknown as Task; - yield call(invoke, "initialize_graph_state"); - if (action.payload.params.type === ConnectionType.SERIAL) { yield call(invoke, "connect_to_serial_port", { portName: action.payload.params.portName, From cdb04e00b41e6d97214332ac3bf8135c0b323fc5 Mon Sep 17 00:00:00 2001 From: Adam McQuilkin <46639306+ajmcquilkin@users.noreply.github.com> Date: Mon, 19 Feb 2024 11:24:01 -0800 Subject: [PATCH 2/4] Removed unnecessary submodule --- .gitmodules | 3 --- src-tauri/protobufs | 1 - 2 files changed, 4 deletions(-) delete mode 160000 src-tauri/protobufs diff --git a/.gitmodules b/.gitmodules index 4692868a..e69de29b 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +0,0 @@ -[submodule "src-tauri/protobufs"] - path = src-tauri/protobufs - url = https://github.com/meshtastic/protobufs.git diff --git a/src-tauri/protobufs b/src-tauri/protobufs deleted file mode 160000 index 2ccf7342..00000000 --- a/src-tauri/protobufs +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 2ccf73428da8dac87aca12a1f91ac5cd76a7442c From 109c20336f07564aa7303e260c6fb7a6b0c55bc2 Mon Sep 17 00:00:00 2001 From: Adam McQuilkin <46639306+ajmcquilkin@users.noreply.github.com> Date: Mon, 19 Feb 2024 11:27:20 -0800 Subject: [PATCH 3/4] Clarified panic calls --- src-tauri/src/ipc/events.rs | 18 ++++++++++-------- src-tauri/src/ipc/helpers.rs | 9 ++++----- src-tauri/src/main.rs | 2 +- 3 files changed, 15 insertions(+), 14 deletions(-) diff --git a/src-tauri/src/ipc/events.rs b/src-tauri/src/ipc/events.rs index 9b97eae8..e3b45020 100644 --- a/src-tauri/src/ipc/events.rs +++ b/src-tauri/src/ipc/events.rs @@ -18,20 +18,22 @@ pub fn dispatch_updated_device( Ok(()) } -pub fn dispatch_configuration_status( - handle: &tauri::AppHandle, - status: ConfigurationStatus, -) -> tauri::Result<()> { +pub fn dispatch_configuration_status(handle: &tauri::AppHandle, status: ConfigurationStatus) -> () { debug!("Dispatching configuration status"); - handle.emit_all("configuration_status", status) + + handle + .emit_all("configuration_status", status) + .expect("Failed to dispatch configuration failure message"); } -pub fn dispatch_rebooting_event(handle: &tauri::AppHandle) -> tauri::Result<()> { +pub fn dispatch_rebooting_event(handle: &tauri::AppHandle) -> () { debug!("Dispatching rebooting event"); let current_time_sec = std::time::SystemTime::now() .duration_since(std::time::UNIX_EPOCH) - .unwrap() + .expect("Time went backwards") .as_secs(); - handle.emit_all("reboot", current_time_sec) + handle + .emit_all("reboot", current_time_sec) + .expect("Failed to dispatch rebooting event"); } diff --git a/src-tauri/src/ipc/helpers.rs b/src-tauri/src/ipc/helpers.rs index ace50611..04279b22 100644 --- a/src-tauri/src/ipc/helpers.rs +++ b/src-tauri/src/ipc/helpers.rs @@ -58,8 +58,7 @@ pub fn spawn_configuration_timeout_handler( "Configuration timed out. Are you sure this is a Meshtastic device?".into(), ), }, - ) - .expect("Failed to dispatch configuration failure message"); + ); trace!("Told UI to disconnect device"); }); @@ -126,8 +125,8 @@ pub fn spawn_decoded_handler( successful: true, message: None, }, - ) - .expect("Failed to dispatch configuration failure message"); + ); + device.set_status(SerialDeviceStatus::Connected); } @@ -147,7 +146,7 @@ pub fn spawn_decoded_handler( if update_result.rebooting { debug!("Device rebooting"); - dispatch_rebooting_event(&handle).expect("Failed to dispatch rebooting event"); + dispatch_rebooting_event(&handle); } } }); diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index fafc6a07..c51b45cb 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -84,7 +84,7 @@ fn main() { info!("Building TS types from Rust"); #[cfg(debug_assertions)] - export_ts_types("../src/bindings/index.ts").unwrap(); + export_ts_types("../src/bindings/index.ts").expect("Failed to export TS types"); info!("Application starting"); From ffc736702ce397eafbd3950a0825a70ed5c6a123 Mon Sep 17 00:00:00 2001 From: Adam McQuilkin <46639306+ajmcquilkin@users.noreply.github.com> Date: Mon, 19 Feb 2024 13:20:49 -0800 Subject: [PATCH 4/4] Updated UI to reflect backend code changes --- biome.json | 66 +++--- src/components/Map/AnalyticsPane.tsx | 213 ------------------ src/components/Map/CreateWaypointDialog.tsx | 1 - src/components/Map/MapEdgeTooltip.tsx | 22 -- src/components/Map/MapView.tsx | 117 +++++----- .../Map/algorithms/ArticulationPoints.tsx | 50 ---- src/components/Map/algorithms/Chart.tsx | 210 ----------------- src/components/Map/algorithms/CommonComps.tsx | 37 --- .../Map/algorithms/DiffusionSimulation.tsx | 35 --- src/components/Map/algorithms/MinCutEdges.tsx | 55 ----- src/features/device/connectionHandlerSagas.ts | 35 --- src/features/device/sagas.ts | 8 - src/features/map/slice.ts | 16 -- 13 files changed, 87 insertions(+), 778 deletions(-) delete mode 100644 src/components/Map/AnalyticsPane.tsx delete mode 100644 src/components/Map/MapEdgeTooltip.tsx delete mode 100644 src/components/Map/algorithms/ArticulationPoints.tsx delete mode 100644 src/components/Map/algorithms/Chart.tsx delete mode 100644 src/components/Map/algorithms/CommonComps.tsx delete mode 100644 src/components/Map/algorithms/DiffusionSimulation.tsx delete mode 100644 src/components/Map/algorithms/MinCutEdges.tsx diff --git a/biome.json b/biome.json index d0bb46c7..2b72e7ca 100644 --- a/biome.json +++ b/biome.json @@ -1,32 +1,38 @@ { - "$schema": "https://biomejs.dev/schemas/1.3.3/schema.json", - "formatter": { - "enabled": true, - "indentStyle": "space", - "indentWidth": 2, - "ignore": [ - "src/bindings/index.ts" - ] - }, - "organizeImports": { - "enabled": true - }, - "linter": { - "ignore": [ - "src/bindings/index.ts" - ], - "enabled": true, - "rules": { - "all": true, - "correctness": { - "useExhaustiveDependencies": "warn" - }, - "suspicious": { - "noDoubleEquals": "warn" - }, - "style": { - "noImplicitBoolean": "off" - } - } - } + "$schema": "https://biomejs.dev/schemas/1.3.3/schema.json", + "formatter": { + "enabled": true, + "indentStyle": "space", + "indentWidth": 2, + "ignore": [ + "src/bindings/index.ts" + ] + }, + "organizeImports": { + "enabled": true + }, + "linter": { + "ignore": [ + "src/bindings/index.ts" + ], + "enabled": true, + "rules": { + "all": true, + "complexity": { + "useSimplifiedLogicExpression": "off" + }, + "correctness": { + "useExhaustiveDependencies": "off" + }, + "performance": { + "noAccumulatingSpread": "off" + }, + "suspicious": { + "noDoubleEquals": "warn" + }, + "style": { + "noImplicitBoolean": "off" + } + } + } } \ No newline at end of file diff --git a/src/components/Map/AnalyticsPane.tsx b/src/components/Map/AnalyticsPane.tsx deleted file mode 100644 index 9e5578c9..00000000 --- a/src/components/Map/AnalyticsPane.tsx +++ /dev/null @@ -1,213 +0,0 @@ -import { ChevronDownIcon, XMarkIcon } from "@heroicons/react/24/outline"; -import * as Accordion from "@radix-ui/react-accordion"; -import { useState } from "react"; -import { useDispatch, useSelector } from "react-redux"; - -import { ArticulationPoints } from "@components/Map/algorithms/ArticulationPoints"; -import { DiffusionSimulation } from "@components/Map/algorithms/DiffusionSimulation"; -import { MincutEdges } from "@components/Map/algorithms/MinCutEdges"; - -import { - AlgorithmConfigFlags, - requestRunAllAlgorithms, -} from "@features/algorithms/actions"; -import { selectAlgorithmsResults } from "@features/algorithms/selectors"; -import { uiSliceActions } from "@features/ui/slice"; - -export const AnalyticsPane = () => { - const dispatch = useDispatch(); - const { apResult, mincutResult } = useSelector(selectAlgorithmsResults()); - - const handleClosePane = () => { - dispatch(uiSliceActions.setInfoPane(null)); - }; - - const diffcen = new Map(); - - const [isAPActive, setAPActive] = useState(false); - const [isMCEActive, setMCEActive] = useState(false); - const [isDiffusionActive, setDiffusionActive] = useState(false); - - const requestRunAlgorithms = () => { - const flags: AlgorithmConfigFlags = {}; - - if (isAPActive) flags.articulationPoint = true; - if (isDiffusionActive) flags.diffusionCentrality = true; - if (isMCEActive) flags.globalMincut = true; - - // Don't trigger IPC if no flags set - if (!Object.entries(flags).length) { - console.warn("No algorithms selected, not running..."); - return; - } - - dispatch(requestRunAllAlgorithms({ flags })); - }; - - return ( - -
-

- Network Analytics -

-
- - -
-
- - {/* Articulation points tab */} - - - - Articulation Points - - - - - -
- -
-
-
- - {/* Mincut edges tab */} - - - - Mincut Edges - - - - - -
- -
-
-
- - {/* Diffusion simulation tab */} - - - - Diffusion Simulation - - - - - -
- -
-
-
- - {/* Predicted network state tab */} - - - - Predicted Network State - - - - - -
- When we have enough training data, we can use AI predict the network - state! -
-
-
-
- ); -}; diff --git a/src/components/Map/CreateWaypointDialog.tsx b/src/components/Map/CreateWaypointDialog.tsx index b756bec3..5cf4c559 100644 --- a/src/components/Map/CreateWaypointDialog.tsx +++ b/src/components/Map/CreateWaypointDialog.tsx @@ -71,7 +71,6 @@ export const CreateWaypointDialog = ({ lngLat, closeDialog, existingWaypoint, - // biome-ignore lint/complexity/noExcessiveCognitiveComplexity: Functional component }: ICreateWaypointDialogProps) => { const { t } = useTranslation(); diff --git a/src/components/Map/MapEdgeTooltip.tsx b/src/components/Map/MapEdgeTooltip.tsx deleted file mode 100644 index 60637a92..00000000 --- a/src/components/Map/MapEdgeTooltip.tsx +++ /dev/null @@ -1,22 +0,0 @@ -import type { PickingInfo } from "deck.gl/typed"; - -export interface IMapEdgeTooltip { - hoverInfo: PickingInfo; -} - -export const MapEdgeTooltip = ({ hoverInfo }: IMapEdgeTooltip) => { - const { object, x, y } = hoverInfo; - - const snr = object?.properties?.snr; - - if (!snr) return null; - - return ( -
-

SNR: {snr}

-
- ); -}; diff --git a/src/components/Map/MapView.tsx b/src/components/Map/MapView.tsx index e45d6812..e1a23600 100644 --- a/src/components/Map/MapView.tsx +++ b/src/components/Map/MapView.tsx @@ -1,15 +1,11 @@ -import { - CollisionFilterExtension, - PathStyleExtension, -} from "@deck.gl/extensions/typed"; +import { CollisionFilterExtension } from "@deck.gl/extensions/typed"; import { MapboxOverlay, MapboxOverlayProps } from "@deck.gl/mapbox/typed"; import * as Dialog from "@radix-ui/react-dialog"; import * as Separator from "@radix-ui/react-separator"; -import { invoke } from "@tauri-apps/api/tauri"; import { GeoJsonLayer, PickingInfo } from "deck.gl/typed"; import { MapPin, X } from "lucide-react"; import maplibregl from "maplibre-gl"; -import { useCallback, useEffect, useMemo, useState } from "react"; +import { useCallback, useMemo, useState } from "react"; import { useTranslation } from "react-i18next"; import { LngLat, @@ -26,23 +22,22 @@ import { import { useDispatch, useSelector } from "react-redux"; import { useDebounce } from "react-use"; -import type { app_device_NormalizedWaypoint } from "@bindings/index"; +import type { + app_device_MeshNode, + app_device_NormalizedWaypoint, +} from "@bindings/index"; import { MapSelectedNodeMenu } from "@components/Map/MapSelectedNodeMenu"; -// * Note: hiding until reimplementation -// import AnalyticsPane from "@components/Map/AnalyticsPane"; -// import MapInteractionPane from "@components/Map/MapInteractionPane"; import { NodeSearchDock } from "@components/NodeSearch/NodeSearchDock"; import { CreateWaypointDialog } from "@components/Map/CreateWaypointDialog"; import { MapContextOption } from "@components/Map/MapContextOption"; -import { MapEdgeTooltip } from "@components/Map/MapEdgeTooltip"; import { MapNodeTooltip } from "@components/Map/MapNodeTooltip"; import { MeshWaypoint } from "@components/Waypoints/MeshWaypoint"; import { WaypointMenu } from "@components/Waypoints/WaypointMenu"; import { selectMapConfigState } from "@features/appConfig/selectors"; -import { selectAllWaypoints } from "@features/device/selectors"; +import { selectAllNodes, selectAllWaypoints } from "@features/device/selectors"; import { selectMapState } from "@features/map/selectors"; import { mapSliceActions } from "@features/map/slice"; import { @@ -68,23 +63,60 @@ const DeckGLOverlay = (props: IDeckGLOverlayProps) => { return null; }; +const convertNodesToFeatureCollection = (nodes: app_device_MeshNode[]) => { + const features = nodes.reduce( + (accum, node) => { + const lastPositionMetric = + node.positionMetrics[node.positionMetrics.length - 1]; + + if (!lastPositionMetric) { + return accum; + } + + const { latitude, longitude } = lastPositionMetric; + + if (!latitude || !longitude) { + return accum; + } + + return [ + ...accum, + { + type: "Feature", + geometry: { + type: "Point", + coordinates: [longitude, latitude], + }, + properties: { + num: node.nodeNum, + }, + }, + ]; + }, + [] as GeoJSON.Feature[], + ); + + return { + type: "FeatureCollection", + features, + }; +}; + export const MapView = () => { const { t } = useTranslation(); const dispatch = useDispatch(); const activeNodeId = useSelector(selectActiveNodeId()); const showInfoPane = useSelector(selectInfoPane()); + const nodes = useSelector(selectAllNodes()); - const { nodesFeatureCollection, edgesFeatureCollection, viewState } = - useSelector(selectMapState()); - + const { viewState } = useSelector(selectMapState()); const { style } = useSelector(selectMapConfigState()); const waypoints = useSelector(selectAllWaypoints()); const activeWaypoint = useSelector(selectActiveWaypoint()); const [nodeHoverInfo, setNodeHoverInfo] = useState(null); - const [edgeHoverInfo, setEdgeHoverInfo] = useState(null); const [isWaypointDialogOpen, setWaypointDialogOpen] = useState(false); const [contextMenuEvent, setContextMenuEvent] = @@ -113,48 +145,12 @@ export const MapView = () => { [activeNodeId, dispatch], ); - useEffect(() => { - const timeout = setInterval(() => { - invoke("get_node_edges") - .then((c) => { - const { nodes, edges } = c as { - nodes: GeoJSON.FeatureCollection; - edges: GeoJSON.FeatureCollection; - }; - - dispatch(mapSliceActions.setNodesFeatureCollection(nodes)); - dispatch(mapSliceActions.setEdgesFeatureCollection(edges)); - }) - .catch(() => dispatch(mapSliceActions.setEdgesFeatureCollection(null))); - }, 5000); // 5s - - return () => clearInterval(timeout); - }, [dispatch]); - const layers = useMemo( () => [ - new GeoJsonLayer({ - id: "edges", - data: edgesFeatureCollection || {}, - pointType: "circle", - extensions: [new PathStyleExtension({ dash: true })], - getDashArray: [4, 4], - pickable: true, - dashJustified: true, - dashGapPickable: true, - filled: false, - lineWidthMinPixels: 2, - - onHover: setEdgeHoverInfo, - - getLineColor: () => { - return [55, 65, 81]; - }, - }), new GeoJsonLayer({ id: "nodes", pointType: "circle", - data: nodesFeatureCollection || {}, + data: convertNodesToFeatureCollection(nodes) || {}, pickable: true, stroked: false, @@ -190,12 +186,7 @@ export const MapView = () => { }, }), ], - [ - nodesFeatureCollection, - edgesFeatureCollection, - handleNodeClick, - activeNodeId, - ], + [nodes, handleNodeClick, activeNodeId], ); const handleClick = () => { @@ -236,7 +227,6 @@ export const MapView = () => { onContextMenu={(e) => e.preventDefault()} > {nodeHoverInfo && } - {edgeHoverInfo && } {/* Map translation is a pain, assuming users will use translated map tiles https://maplibre.org/maplibre-gl-js/docs/examples/language-switch/ */} { setEditingWaypoint(w); }} /> - ) : // : showInfoPane == "algos" ? ( - // - // ) - null} + ) : null} - {/* Note: hiding algos until reimplementation */} - {/* */} {(lastRightClickLngLat || editingWaypoint) && ( diff --git a/src/components/Map/algorithms/ArticulationPoints.tsx b/src/components/Map/algorithms/ArticulationPoints.tsx deleted file mode 100644 index c2cdc482..00000000 --- a/src/components/Map/algorithms/ArticulationPoints.tsx +++ /dev/null @@ -1,50 +0,0 @@ -import { - AlgorithmSelector, - LastRan, -} from "@components/Map/algorithms/CommonComps"; - -export interface ArticulationPointsProps { - articulationPoints: string[] | null; - isAPSet: boolean; - setAP: (checked: boolean) => void; -} - -// component ArticulationPoints receives a list of nodes that are articulation points -// and displays them in a list -export const ArticulationPoints = ({ - articulationPoints, - isAPSet, - setAP, -}: ArticulationPointsProps) => { - return ( -
- -

- Articulation points are nodes that, when removed, will disconnect the - graph. Consider messaging these nodes to inform them of the situation. -

-
    - {articulationPoints - ? articulationPoints.map((node, index) => ( - // alternating background colors for each node based on index's parity -
  • - - {node} - -
  • - )) - : "No results"} -
- -
- ); -}; diff --git a/src/components/Map/algorithms/Chart.tsx b/src/components/Map/algorithms/Chart.tsx deleted file mode 100644 index 1bce9633..00000000 --- a/src/components/Map/algorithms/Chart.tsx +++ /dev/null @@ -1,210 +0,0 @@ -import { - CategoryScale, - Chart as ChartJS, - Filler, - Legend, - LineElement, - LinearScale, - PointElement, - Title, - Tooltip, -} from "chart.js"; -import { Line } from "react-chartjs-2"; - -ChartJS.register( - CategoryScale, - LinearScale, - PointElement, - LineElement, - Title, - Tooltip, - Filler, - Legend, -); - -export const options = { - responsive: true, - plugins: { - legend: { - position: "top" as const, - }, - title: { - display: true, - text: "Diffusion Centrality per node over time", - }, - }, -}; - -// biome-ignore lint/complexity/noExcessiveCognitiveComplexity: Component needs to be rewritten -function convertToDataFormat() { - // data: Map>> - const diffcen = { - "1": { - u: { w: 0.875, u: 0.22285507026263682, v: 0.125 }, - w: { - v: 0.8333333333333333, - w: 1.899891188788434, - u: 0.16666666666666666, - }, - v: { - v: 1.0028478161818657, - u: 0.027777777777777776, - w: 0.9722222222222222, - }, - }, - "5": { - w: { - u: 0.11447557860207201, - v: 0.5253881911125526, - w: 7.548832973261137, - }, - u: { - u: 1.1971055926702734, - v: 0.3869554744587899, - w: 0.5306180750216268, - }, - v: { - u: 0.08736238508669167, - v: 5.302357097157161, - w: 0.5498104456545143, - }, - }, - "3": { - u: { - w: 0.5846156302145143, - u: 0.7100239287446046, - v: 0.3458195569108133, - }, - w: { - u: 0.12356102280699693, - v: 0.5789979029092013, - w: 3.3594015098012524, - }, - v: { v: 3.15260229690905, u: 0.07788491452768159, w: 0.6169780536897971 }, - }, - "4": { - w: { - v: 0.4474437258633965, - u: 0.10132558606505494, - w: 4.382201150288006, - }, - u: { - u: 0.9657539481298845, - v: 0.44933065647196085, - w: 0.459774563353038, - }, - v: { - u: 0.10096360051946618, - v: 4.298012880591784, - w: 0.45620812795185256, - }, - }, - "2": { - v: { - w: 0.4560563664334602, - u: 0.1013954341203142, - v: 2.1497846458869967, - }, - u: { - w: 0.46385720089360516, - v: 0.4551294396765634, - u: 0.4789370417123615, - }, - w: { - v: 0.4474339020518443, - w: 2.191212980781382, - u: 0.10138603482247471, - }, - }, - }; - const labels = Object.keys(diffcen); - - const node_to_time_values = new Map(); - - for (const [time, time_map] of Object.entries(diffcen)) { - for (const [source_node, node_map] of Object.entries(time_map)) { - for (const [destination_node, value] of Object.entries(node_map)) { - if (!node_to_time_values.has(source_node)) { - node_to_time_values.set(source_node, []); - } - if (source_node === destination_node) { - node_to_time_values.get(source_node)?.push(value); - } - } - } - } - - const datasets: { - fill: boolean; - label: string; - data: number[]; - borderColor: string; - backgroundColor: string; - }[] = []; - const colors = [ - "53B3AD", - "4146C2", - "E78B37", - "CD4B81", - "7F83F3", - "8EDE78", - "3878EB", - "6A2BCB", - "E2C742", - "BE6325", - ]; - - // sort by value and assign color from left to right - const sorted = new Map( - [...node_to_time_values.entries()].sort((a, b) => b[1][0] - a[1][0]), - ); - let i = 0; - for (const [node_name, values] of sorted) { - const color_rgb = hexToRgb(colors[i]); - if (color_rgb) { - colors[i] = `rgba(${color_rgb.r}, ${color_rgb.g}, ${color_rgb.b}, 0.9)`; - } - - datasets.push({ - fill: true, - label: node_name, - data: values, - borderColor: colors[i], - backgroundColor: colors[i], - }); - i++; - } - - // reverse the order of the datasets so that the legend is in the same order as the graph - datasets.reverse(); - - return { - labels, - datasets: datasets, - }; -} - -// Credit: https://stackoverflow.com/questions/5623838/rgb-to-hex-and-hex-to-rgb -function hexToRgb(hex: string): { r: number; g: number; b: number } | null { - // Expand shorthand form (e.g. "03F") to full form (e.g. "0033FF") - const shorthandRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i; - const updated_hex = hex.replace( - shorthandRegex, - (m: string, r: string, g: string, b: string): string => - r + r + g + g + b + b, - ); - - const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(updated_hex); - return result - ? { - r: parseInt(result[1], 16), - g: parseInt(result[2], 16), - b: parseInt(result[3], 16), - } - : null; -} - -// data : Map>> -export function DiffSimTotalChartTimeline() { - return ; -} diff --git a/src/components/Map/algorithms/CommonComps.tsx b/src/components/Map/algorithms/CommonComps.tsx deleted file mode 100644 index bac35066..00000000 --- a/src/components/Map/algorithms/CommonComps.tsx +++ /dev/null @@ -1,37 +0,0 @@ -import { CheckIcon } from "@heroicons/react/24/outline"; -import * as Checkbox from "@radix-ui/react-checkbox"; - -const LastRan = ({ lastRanMinutes }: { lastRanMinutes: number }) => { - return ( -
-

Last Ran: {lastRanMinutes} minutes ago

-
- ); -}; - -export interface AlgorithmSelectorProps { - algorithm: string; - isSet: boolean; - setAlgorithm: (checked: boolean) => void; -} - -// component AlgorithmSelector receives the state and name of the algorithm and -// sets the state of the algorithm to true when user selects it -const AlgorithmSelector = ({ isSet, setAlgorithm }: AlgorithmSelectorProps) => { - return ( -
- - - - - -

{isSet ? "Active" : "Inactive"}

-
- ); -}; - -export { LastRan, AlgorithmSelector }; diff --git a/src/components/Map/algorithms/DiffusionSimulation.tsx b/src/components/Map/algorithms/DiffusionSimulation.tsx deleted file mode 100644 index 402985cb..00000000 --- a/src/components/Map/algorithms/DiffusionSimulation.tsx +++ /dev/null @@ -1,35 +0,0 @@ -import { DiffSimTotalChartTimeline } from "./Chart"; -import { AlgorithmSelector, LastRan } from "./CommonComps"; - -export interface DiffusionSimulationInterface { - diffusionCentrality: Map>>; - isDiffusionSet: boolean; - setDiffusion: (checked: boolean) => void; -} - -export const DiffusionSimulation = ({ - diffusionCentrality, - isDiffusionSet, - setDiffusion, -}: DiffusionSimulationInterface) => { - return ( -
- -

- This score between source node a and destination node b is the expected - number of times a will be able to communicate with b in selected T - periods. -

- - -
- {/*create a dropdown for source node, a dropdown for destination node, and a textfield for the time - use the values to extract value from diffusionCentrality hashmap and display it */} -
-
- ); -}; diff --git a/src/components/Map/algorithms/MinCutEdges.tsx b/src/components/Map/algorithms/MinCutEdges.tsx deleted file mode 100644 index 0f428c64..00000000 --- a/src/components/Map/algorithms/MinCutEdges.tsx +++ /dev/null @@ -1,55 +0,0 @@ -import { - AlgorithmSelector, - LastRan, -} from "@components/Map/algorithms/CommonComps"; - -export interface MincutEdgesInterface { - edges: string[][] | null; - isMincutSet: boolean; - setMinCut: (checked: boolean) => void; -} - -export const MincutEdges = ({ - edges, - isMincutSet, - setMinCut, -}: MincutEdgesInterface) => { - return ( -
- -

- Min cut edges are the smallest set of edges that, when removed, will - disconnect the graph. Consider messaging the nodes on these edges to - inform them of the situation. -

-
    - {edges - ? edges.map((edge, index) => ( - // edge is a tuple of two strings. We want to display the two strings with an arrow icon in between -
  • - - {edge[0]} - - - {"->"} - - - {edge[1]} - -
  • - )) - : "No results"} -
- -
- ); -}; diff --git a/src/features/device/connectionHandlerSagas.ts b/src/features/device/connectionHandlerSagas.ts index df1ea6be..93bd827c 100644 --- a/src/features/device/connectionHandlerSagas.ts +++ b/src/features/device/connectionHandlerSagas.ts @@ -7,18 +7,11 @@ import type { app_device_MeshDevice } from "@bindings/index"; import { connectionSliceActions } from "@features/connection/slice"; import { requestDisconnectFromDevice } from "@features/device/actions"; import { deviceSliceActions } from "@features/device/slice"; -import { mapSliceActions } from "@features/map/slice"; import type { DeviceKey } from "@utils/connections"; -export type GraphGeoJSONResult = { - nodes: GeoJSON.FeatureCollection; - edges: GeoJSON.FeatureCollection; -}; - export type DeviceUpdateChannel = EventChannel; export type DeviceDisconnectChannel = EventChannel; -export type GraphUpdateChannel = EventChannel; export type ConfigStatusChannel = EventChannel; export type RebootChannel = EventChannel; @@ -81,34 +74,6 @@ export function* handleDeviceDisconnectChannel( } } -export const createGraphUpdateChannel = (): GraphUpdateChannel => { - return eventChannel((emitter) => { - listen("graph_update", (event) => { - emitter(event.payload); - }) - // .then((unlisten) => { - // return unlisten; - // }) - .catch(console.error); - - // TODO UNLISTEN - return () => null; - }); -}; - -export function* handleGraphUpdateChannel(channel: GraphUpdateChannel) { - try { - while (true) { - const { nodes, edges }: GraphGeoJSONResult = yield take(channel); - - yield put(mapSliceActions.setNodesFeatureCollection(nodes)); - yield put(mapSliceActions.setEdgesFeatureCollection(edges)); - } - } catch (error) { - yield call(handleSagaError, error); - } -} - export const createConfigStatusChannel = (): ConfigStatusChannel => { return eventChannel((emitter) => { listen("configuration_status", (event) => { diff --git a/src/features/device/sagas.ts b/src/features/device/sagas.ts index 08aacf5f..3e911918 100644 --- a/src/features/device/sagas.ts +++ b/src/features/device/sagas.ts @@ -19,17 +19,14 @@ import { ConfigStatusChannel, DeviceDisconnectChannel, DeviceUpdateChannel, - GraphUpdateChannel, RebootChannel, createConfigStatusChannel, createDeviceDisconnectChannel, createDeviceUpdateChannel, - createGraphUpdateChannel, createRebootChannel, handleConfigStatusChannel, handleDeviceDisconnectChannel, handleDeviceUpdateChannel, - handleGraphUpdateChannel, handleRebootChannel, } from "@features/device/connectionHandlerSagas"; import { deviceSliceActions } from "@features/device/slice"; @@ -86,10 +83,6 @@ function* subscribeAll() { createDeviceDisconnectChannel, ); - const graphUpdateChannel: GraphUpdateChannel = yield call( - createGraphUpdateChannel, - ); - const configStatusChannel: ConfigStatusChannel = yield call( createConfigStatusChannel, ); @@ -99,7 +92,6 @@ function* subscribeAll() { yield all([ call(handleDeviceUpdateChannel, deviceUpdateChannel), call(handleDeviceDisconnectChannel, deviceDisconnectChannel), - call(handleGraphUpdateChannel, graphUpdateChannel), call(handleConfigStatusChannel, configStatusChannel), call(handleRebootChannel, rebootChannel), ]); diff --git a/src/features/map/slice.ts b/src/features/map/slice.ts index 01a4d4e9..d0a6ce24 100644 --- a/src/features/map/slice.ts +++ b/src/features/map/slice.ts @@ -7,8 +7,6 @@ export interface IMapUIState { export interface IMapState { viewState: Partial; - nodesFeatureCollection: GeoJSON.FeatureCollection | null; - edgesFeatureCollection: GeoJSON.FeatureCollection | null; mapUIState: IMapUIState; } @@ -18,8 +16,6 @@ export const initialMapState: IMapState = { longitude: 0.0, zoom: 0.0, }, - nodesFeatureCollection: null, - edgesFeatureCollection: null, mapUIState: { searchDockExpanded: true, }, @@ -42,18 +38,6 @@ export const mapSlice = createSlice({ setZoom: (state, action: PayloadAction) => { state.viewState.zoom = action.payload; }, - setNodesFeatureCollection: ( - state, - action: PayloadAction, - ) => { - state.nodesFeatureCollection = action.payload; - }, - setEdgesFeatureCollection: ( - state, - action: PayloadAction, - ) => { - state.edgesFeatureCollection = action.payload; - }, setMapUIState: (state, action: PayloadAction>) => { state.mapUIState = { ...state.mapUIState, ...action.payload }; },