From dfacd9645e4458f2eaedf9cbac60c0076792be4b Mon Sep 17 00:00:00 2001 From: Maxime Van Hees Date: Thu, 1 Aug 2024 17:09:42 +0200 Subject: [PATCH] tx and rx graph (to fix: circular dependency bug) --- mycelium-ui/Cargo.lock | 11 ++ mycelium-ui/Cargo.toml | 1 + mycelium-ui/src/components/peers.rs | 163 +++++++++++++++++++--------- 3 files changed, 122 insertions(+), 53 deletions(-) diff --git a/mycelium-ui/Cargo.lock b/mycelium-ui/Cargo.lock index 51bab3e..2792b20 100644 --- a/mycelium-ui/Cargo.lock +++ b/mycelium-ui/Cargo.lock @@ -1015,6 +1015,16 @@ dependencies = [ "dioxus-signals", ] +[[package]] +name = "dioxus-charts" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53ed46a5bf524783e4a3496a8ab38fbe129590d8a3e436c1297c14f96c559a1a" +dependencies = [ + "dioxus 0.5.1", + "log", +] + [[package]] name = "dioxus-cli-config" version = "0.5.0" @@ -3049,6 +3059,7 @@ name = "mycelium-ui" version = "0.1.0" dependencies = [ "dioxus 0.5.1", + "dioxus-charts", "dioxus-free-icons", "dioxus-logger", "dioxus-sortable", diff --git a/mycelium-ui/Cargo.toml b/mycelium-ui/Cargo.toml index 47abb06..f91089d 100644 --- a/mycelium-ui/Cargo.toml +++ b/mycelium-ui/Cargo.toml @@ -26,3 +26,4 @@ dioxus-free-icons = { version = "0.8.6", features = [ ] } human_bytes = { version = "0.4.3", features = ["fast"] } tokio = "1.39.2" +dioxus-charts = "0.2.0" diff --git a/mycelium-ui/src/components/peers.rs b/mycelium-ui/src/components/peers.rs index 413bee9..e72f47a 100644 --- a/mycelium-ui/src/components/peers.rs +++ b/mycelium-ui/src/components/peers.rs @@ -1,57 +1,30 @@ use crate::get_sort_indicator; use crate::{api, SearchState, ServerAddress, SortDirection}; use dioxus::prelude::*; +use dioxus_charts::LineChart; use human_bytes::human_bytes; -use mycelium::peer_manager::PeerType; -use std::{cmp::Ordering, collections::HashSet}; +use mycelium::peer_manager::{PeerStats, PeerType}; +use std::{ + cmp::Ordering, + collections::{HashSet, VecDeque}, +}; -#[component] -fn ExpandedPeerRow(peer: mycelium::peer_manager::PeerStats, on_close: EventHandler<()>) -> Element { - rsx! { - tr { class: "expanded-row", - td { colspan: "7", - div { class: "expanded-content", - p { "Tx bytes: {human_bytes(peer.tx_bytes as f64)}" } - p { "Rx bytes: {human_bytes(peer.rx_bytes as f64)}" } - button { class: "close-button", - onclick: move |_| on_close.call(()), - "Close" - } - } - } - } - } -} +const REFRESH_RATE_MS: u64 = 500; #[component] pub fn Peers() -> Element { let server_addr = use_context::>().read().0; - let mut peers = use_signal(|| Vec::new()); + let peers = use_signal(Vec::new); let mut error = use_signal(|| None::); - let _initial_fetch = use_resource(move || async move { - match api::get_peers(server_addr).await { - Ok(fetched_peers) => { - peers.set(fetched_peers); - error.set(None); - } - Err(e) => { - eprintln!("Error fetching peers: {}", e); - error.set(Some(format!( - "An error has occurred while fetching peers: {}", - e - ))); - } - } - }); - let _: Coroutine<()> = use_coroutine(|_rx: UnboundedReceiver<_>| { to_owned![peers, server_addr]; async move { loop { - tokio::time::sleep(tokio::time::Duration::from_millis(500)).await; + tokio::time::sleep(tokio::time::Duration::from_millis(REFRESH_RATE_MS)).await; match api::get_peers(server_addr).await { Ok(fetched_peers) => { + println!("fetching them peers bro"); peers.set(fetched_peers); error.set(None); } @@ -71,21 +44,18 @@ pub fn Peers() -> Element { if let Some(err) = error.read().as_ref() { div { class: "error-message", "{err}" } } else { - {PeersTable(peers)} + PeersTable { peers: peers } } } } -#[derive(Clone, PartialEq)] -// Rows that have been expaneded out to show additional information -struct ExpandedRows(HashSet); - -fn PeersTable(peers: Signal>) -> Element { +#[component] +fn PeersTable(peers: Signal>) -> Element { let mut current_page = use_signal(|| 0); let items_per_page = 20; let mut sort_column = use_signal(|| "Protocol".to_string()); let mut sort_direction = use_signal(|| SortDirection::Ascending); - let peers_len = peers.len(); + let peers_len = peers.read().len(); let mut change_page = move |delta: i32| { let cur_page = *current_page.read() as i32; @@ -98,12 +68,13 @@ fn PeersTable(peers: Signal>) -> Element let mut expanded_rows = use_signal(|| ExpandedRows(HashSet::new())); let mut toggle_row_expansion = move |peer_endpoint: String| { - let mut expanded = expanded_rows.write(); - if expanded.0.contains(&peer_endpoint) { - expanded.0.remove(&peer_endpoint); - } else { - expanded.0.insert(peer_endpoint); - } + expanded_rows.with_mut(|rows| { + if rows.0.contains(&peer_endpoint) { + rows.0.remove(&peer_endpoint); + } else { + rows.0.insert(peer_endpoint); + } + }); }; // Sorting @@ -172,7 +143,7 @@ fn PeersTable(peers: Signal>) -> Element _ => false, }) .cloned() - .collect::>() + .collect::>() }); let peers_len = filtered_peers.read().len(); @@ -250,15 +221,18 @@ fn PeersTable(peers: Signal>) -> Element } { let is_expanded = expanded_rows.read().0.contains(&peer.endpoint.to_string()); + let mut peer_signal = use_signal(|| peer.clone()); + // Update the peer_signal when the peers are fetched + peer_signal.set(peer.clone()); if is_expanded { rsx! { ExpandedPeerRow { - peer: peer.clone(), + peer: peer_signal, on_close: move |_| toggle_row_expansion(peer.endpoint.to_string()) } } } else { - rsx! { } + rsx! {} } } } @@ -282,6 +256,89 @@ fn PeersTable(peers: Signal>) -> Element } } +#[component] +fn ExpandedPeerRow( + peer: Signal, + on_close: EventHandler<()>, +) -> Element { + let tx_data_deque: VecDeque = VecDeque::with_capacity(20); + let rx_data_deque: VecDeque = VecDeque::with_capacity(20); + let mut tx_chart_data = use_signal(|| tx_data_deque); + let mut rx_chart_data = use_signal(|| rx_data_deque); + + let mut prev_tx = use_signal(|| 0f64); + let mut prev_rx = use_signal(|| 0f64); + + use_effect(move || { + println!("yeah..."); + let current_tx = peer.read().tx_bytes as f64; + let current_rx = peer.read().rx_bytes as f64; + + // Calculate data flow per second + let tx_per_second = (current_tx - *prev_tx.read()) as u64 * (1000 / REFRESH_RATE_MS); + let rx_per_second = (current_rx - *prev_rx.read()) as u64 * (1000 / REFRESH_RATE_MS); + + tx_chart_data.with_mut(|tx_data| { + if tx_data.len() >= 20 { + tx_data.pop_front(); + } + tx_data.push_back(tx_per_second as f32); + }); + + rx_chart_data.with_mut(|rx_data| { + if rx_data.len() >= 20 { + rx_data.pop_front(); + } + rx_data.push_back(rx_per_second as f32); + }); + }); + // Causes a circular dependency which causes the program to become unresponsive + // prev_tx.set(peer.read().tx_bytes as f64); + // prev_rx.set(peer.read().rx_bytes as f64); + + rsx! { + tr { class: "expanded-row", + td { colspan: "7", + div { class: "expanded-content", + // Tx chart + LineChart { + show_grid: false, + padding_top: 80, + padding_left: 100, + padding_right: 80, + padding_bottom: 80, + label_interpolation: (|v| human_bytes(v as f64).to_string()) as fn(f32)-> String, + series: vec![tx_chart_data.read().iter().cloned().collect::>()], + labels: (0..tx_chart_data.read().len()).map(|i| i.to_string()).collect::>(), + series_labels: vec!["Tx Bytes".into()], + } + // Rx chart + LineChart { + show_grid: false, + padding_top: 80, + padding_left: 100, + padding_right: 80, + padding_bottom: 80, + label_interpolation: (|v| human_bytes(v as f64).to_string()) as fn(f32)-> String, + series: vec![rx_chart_data.read().iter().cloned().collect::>()], + labels: (0..rx_chart_data.read().len()).map(|i| i.to_string()).collect::>(), + series_labels: vec!["Rx Bytes".into()], + } + // p { "Tx bytes: {human_bytes(peer.read().tx_bytes as f64)}" } + // p { "Rx bytes: {human_bytes(peer.read().rx_bytes as f64)}" } + button { class: "close-button", + onclick: move |_| on_close.call(()), + "Close" + } + } + } + } + } +} +#[derive(Clone, PartialEq)] +// Rows that have been expaneded out to show additional information +struct ExpandedRows(HashSet); + fn sort_peers( peers: &mut [mycelium::peer_manager::PeerStats], column: &str,