summaryrefslogtreecommitdiffstats
path: root/src/canvas/widgets/network_graph.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/canvas/widgets/network_graph.rs')
-rw-r--r--src/canvas/widgets/network_graph.rs577
1 files changed, 463 insertions, 114 deletions
diff --git a/src/canvas/widgets/network_graph.rs b/src/canvas/widgets/network_graph.rs
index b6ee080b..74509224 100644
--- a/src/canvas/widgets/network_graph.rs
+++ b/src/canvas/widgets/network_graph.rs
@@ -3,9 +3,13 @@ use std::cmp::max;
use unicode_segmentation::UnicodeSegmentation;
use crate::{
- app::App,
- canvas::{drawing_utils::get_column_widths, Painter},
+ app::{App, AxisScaling},
+ canvas::{
+ drawing_utils::{get_column_widths, interpolate_points},
+ Painter,
+ },
constants::*,
+ units::data_units::DataUnit,
utils::gen_util::*,
};
@@ -82,103 +86,344 @@ impl NetworkGraphWidget for Painter {
/// Point is of time, data
type Point = (f64, f64);
- /// Returns the required max data point and labels.
- fn adjust_network_data_point(
- rx: &[Point], tx: &[Point], time_start: f64, time_end: f64,
- ) -> (f64, Vec<String>) {
- // First, filter and find the maximal rx or tx so we know how to scale
- let mut max_val_bytes = 0.0;
- let filtered_rx = rx
- .iter()
- .cloned()
- .filter(|(time, _data)| *time >= time_start && *time <= time_end);
-
- let filtered_tx = tx
- .iter()
- .cloned()
- .filter(|(time, _data)| *time >= time_start && *time <= time_end);
-
- for (_time, data) in filtered_rx.clone().chain(filtered_tx.clone()) {
- if data > max_val_bytes {
- max_val_bytes = data;
+ /// Returns the max data point and time given a time.
+ fn get_max_entry(
+ rx: &[Point], tx: &[Point], time_start: f64, network_scale_type: &AxisScaling,
+ network_use_binary_prefix: bool,
+ ) -> (f64, f64) {
+ /// Determines a "fake" max value in circumstances where we couldn't find one from the data.
+ fn calculate_missing_max(
+ network_scale_type: &AxisScaling, network_use_binary_prefix: bool,
+ ) -> f64 {
+ match network_scale_type {
+ AxisScaling::Log => {
+ if network_use_binary_prefix {
+ LOG_KIBI_LIMIT
+ } else {
+ LOG_KILO_LIMIT
+ }
+ }
+ AxisScaling::Linear => {
+ if network_use_binary_prefix {
+ KIBI_LIMIT_F64
+ } else {
+ KILO_LIMIT_F64
+ }
+ }
}
}
- // FIXME [NETWORKING]: Granularity. Just scale up the values.
- // FIXME [NETWORKING]: Ability to set fixed scale in config.
- // Currently we do 32 -> 33... which skips some gigabit values
- let true_max_val: f64;
- let mut labels = vec![];
- if max_val_bytes < LOG_KIBI_LIMIT {
- true_max_val = LOG_KIBI_LIMIT;
- labels = vec!["0B".to_string(), "1KiB".to_string()];
- } else if max_val_bytes < LOG_MEBI_LIMIT {
- true_max_val = LOG_MEBI_LIMIT;
- labels = vec!["0B".to_string(), "1KiB".to_string(), "1MiB".to_string()];
- } else if max_val_bytes < LOG_GIBI_LIMIT {
- true_max_val = LOG_GIBI_LIMIT;
- labels = vec![
- "0B".to_string(),
- "1KiB".to_string(),
- "1MiB".to_string(),
- "1GiB".to_string(),
- ];
- } else if max_val_bytes < LOG_TEBI_LIMIT {
- true_max_val = max_val_bytes.ceil() + 1.0;
- let cap_u32 = true_max_val as u32;
-
- for i in 0..=cap_u32 {
- match i {
- 0 => labels.push("0B".to_string()),
- LOG_KIBI_LIMIT_U32 => labels.push("1KiB".to_string()),
- LOG_MEBI_LIMIT_U32 => labels.push("1MiB".to_string()),
- LOG_GIBI_LIMIT_U32 => labels.push("1GiB".to_string()),
- _ if i == cap_u32 => {
- labels.push(format!("{}GiB", 2_u64.pow(cap_u32 - LOG_GIBI_LIMIT_U32)))
+ // First, let's shorten our ranges to actually look. We can abuse the fact that our rx and tx arrays
+ // are sorted, so we can short-circuit our search to filter out only the relevant data points...
+ let filtered_rx = if let (Some(rx_start), Some(rx_end)) = (
+ rx.iter().position(|(time, _data)| *time >= time_start),
+ rx.iter().rposition(|(time, _data)| *time <= 0.0),
+ ) {
+ Some(&rx[rx_start..=rx_end])
+ } else {
+ None
+ };
+
+ let filtered_tx = if let (Some(tx_start), Some(tx_end)) = (
+ tx.iter().position(|(time, _data)| *time >= time_start),
+ tx.iter().rposition(|(time, _data)| *time <= 0.0),
+ ) {
+ Some(&tx[tx_start..=tx_end])
+ } else {
+ None
+ };
+
+ // Then, find the maximal rx/tx so we know how to scale, and return it.
+ match (filtered_rx, filtered_tx) {
+ (None, None) => (
+ time_start,
+ calculate_missing_max(network_scale_type, network_use_binary_prefix),
+ ),
+ (None, Some(filtered_tx)) => {
+ match filtered_tx
+ .iter()
+ .max_by(|(_, data_a), (_, data_b)| get_ordering(data_a, data_b, false))
+ {
+ Some((best_time, max_val)) => {
+ if *max_val == 0.0 {
+ (
+ time_start,
+ calculate_missing_max(
+ network_scale_type,
+ network_use_binary_prefix,
+ ),
+ )
+ } else {
+ (*best_time, *max_val)
+ }
}
- _ if i == (LOG_GIBI_LIMIT_U32 + cap_u32) / 2 => labels.push(format!(
- "{}GiB",
- 2_u64.pow(cap_u32 - ((LOG_GIBI_LIMIT_U32 + cap_u32) / 2))
- )), // ~Halfway point
- _ => labels.push(String::default()),
+ None => (
+ time_start,
+ calculate_missing_max(network_scale_type, network_use_binary_prefix),
+ ),
}
}
- } else {
- true_max_val = max_val_bytes.ceil() + 1.0;
- let cap_u32 = true_max_val as u32;
-
- for i in 0..=cap_u32 {
- match i {
- 0 => labels.push("0B".to_string()),
- LOG_KIBI_LIMIT_U32 => labels.push("1KiB".to_string()),
- LOG_MEBI_LIMIT_U32 => labels.push("1MiB".to_string()),
- LOG_GIBI_LIMIT_U32 => labels.push("1GiB".to_string()),
- LOG_TEBI_LIMIT_U32 => labels.push("1TiB".to_string()),
- _ if i == cap_u32 => {
- labels.push(format!("{}GiB", 2_u64.pow(cap_u32 - LOG_TEBI_LIMIT_U32)))
+ (Some(filtered_rx), None) => {
+ match filtered_rx
+ .iter()
+ .max_by(|(_, data_a), (_, data_b)| get_ordering(data_a, data_b, false))
+ {
+ Some((best_time, max_val)) => {
+ if *max_val == 0.0 {
+ (
+ time_start,
+ calculate_missing_max(
+ network_scale_type,
+ network_use_binary_prefix,
+ ),
+ )
+ } else {
+ (*best_time, *max_val)
+ }
}
- _ if i == (LOG_TEBI_LIMIT_U32 + cap_u32) / 2 => labels.push(format!(
- "{}TiB",
- 2_u64.pow(cap_u32 - ((LOG_TEBI_LIMIT_U32 + cap_u32) / 2))
- )), // ~Halfway point
- _ => labels.push(String::default()),
+ None => (
+ time_start,
+ calculate_missing_max(network_scale_type, network_use_binary_prefix),
+ ),
+ }
+ }
+ (Some(filtered_rx), Some(filtered_tx)) => {
+ match filtered_rx
+ .iter()
+ .chain(filtered_tx)
+ .max_by(|(_, data_a), (_, data_b)| get_ordering(data_a, data_b, false))
+ {
+ Some((best_time, max_val)) => {
+ if *max_val == 0.0 {
+ (
+ *best_time,
+ calculate_missing_max(
+ network_scale_type,
+ network_use_binary_prefix,
+ ),
+ )
+ } else {
+ (*best_time, *max_val)
+ }
+ }
+ None => (
+ time_start,
+ calculate_missing_max(network_scale_type, network_use_binary_prefix),
+ ),
}
}
}
+ }
+
+ /// Returns the required max data point and labels.
+ fn adjust_network_data_point(
+ max_entry: f64, network_scale_type: &AxisScaling, network_unit_type: &DataUnit,
+ network_use_binary_prefix: bool,
+ ) -> (f64, Vec<String>) {
+ // So, we're going with an approach like this for linear data:
+ // - Main goal is to maximize the amount of information displayed given a specific height.
+ // We don't want to drown out some data if the ranges are too far though! Nor do we want to filter
+ // out too much data...
+ // - Change the y-axis unit (kilo/kibi, mega/mebi...) dynamically based on max load.
+ //
+ // The idea is we take the top value, build our scale such that each "point" is a scaled version of that.
+ // So for example, let's say I use 390 Mb/s. If I drew 4 segments, it would be 97.5, 195, 292.5, 390, and
+ // probably something like 438.75?
+ //
+ // So, how do we do this in tui-rs? Well, if we are using intervals that tie in perfectly to the max
+ // value we want... then it's actually not that hard. Since tui-rs accepts a vector as labels and will
+ // properly space them all out... we just work with that and space it out properly.
+ //
+ // Dynamic chart idea based off of FreeNAS's chart design.
+ //
+ // ===
+ //
+ // For log data, we just use the old method of log intervals (kilo/mega/giga/etc.). Keep it nice and simple.
+
+ // Now just check the largest unit we correspond to... then proceed to build some entries from there!
+
+ let unit_char = match network_unit_type {
+ DataUnit::Byte => "B",
+ DataUnit::Bit => "b",
+ };
+
+ match network_scale_type {
+ AxisScaling::Linear => {
+ let (k_limit, m_limit, g_limit, t_limit) = if network_use_binary_prefix {
+ (
+ KIBI_LIMIT_F64,
+ MEBI_LIMIT_F64,
+ GIBI_LIMIT_F64,
+ TEBI_LIMIT_F64,
+ )
+ } else {
+ (
+ KILO_LIMIT_F64,
+ MEGA_LIMIT_F64,
+ GIGA_LIMIT_F64,
+ TERA_LIMIT_F64,
+ )
+ };
+
+ let bumped_max_entry = max_entry * 1.5; // We use the bumped up version to calculate our unit type.
+ let (max_value_scaled, unit_prefix, unit_type): (f64, &str, &str) =
+ if bumped_max_entry < k_limit {
+ (max_entry, "", unit_char)
+ } else if bumped_max_entry < m_limit {
+ (
+ max_entry / k_limit,
+ if network_use_binary_prefix { "Ki" } else { "K" },
+ unit_char,
+ )
+ } else if bumped_max_entry < g_limit {
+ (
+ max_entry / m_limit,
+ if network_use_binary_prefix { "Mi" } else { "M" },
+ unit_char,
+ )
+ } else if bumped_max_entry < t_limit {
+ (
+ max_entry / g_limit,
+ if network_use_binary_prefix { "Gi" } else { "G" },
+ unit_char,
+ )
+ } else {
+ (
+ max_entry / t_limit,
+ if network_use_binary_prefix { "Ti" } else { "T" },
+ unit_char,
+ )
+ };
+
+ // Finally, build an acceptable range starting from there, using the given height!
+ // Note we try to put more of a weight on the bottom section vs. the top, since the top has less data.
+
+ let base_unit = max_value_scaled;
+ let labels: Vec<String> = vec![
+ format!("0{}{}", unit_prefix, unit_type),
+ format!("{:.1}", base_unit * 0.5),
+ format!("{:.1}", base_unit),
+ format!("{:.1}", base_unit * 1.5),
+ ]
+ .into_iter()
+ .map(|s| format!("{:>5}", s)) // Pull 5 as the longest legend value is generally going to be 5 digits (if they somehow hit over 5 terabits per second)
+ .collect();
+
+ (bumped_max_entry, labels)
+ }
+ AxisScaling::Log => {
+ let (m_limit, g_limit, t_limit) = if network_use_binary_prefix {
+ (LOG_MEBI_LIMIT, LOG_GIBI_LIMIT, LOG_TEBI_LIMIT)
+ } else {
+ (LOG_MEGA_LIMIT, LOG_GIGA_LIMIT, LOG_TERA_LIMIT)
+ };
+
+ fn get_zero(network_use_binary_prefix: bool, unit_char: &str) -> String {
+ format!(
+ "{}0{}",
+ if network_use_binary_prefix { " " } else { " " },
+ unit_char
+ )
+ }
+
+ fn get_k(network_use_binary_prefix: bool, unit_char: &str) -> String {
+ format!(
+ "1{}{}",
+ if network_use_binary_prefix { "Ki" } else { "K" },
+ unit_char
+ )
+ }
+
+ fn get_m(network_use_binary_prefix: bool, unit_char: &str) -> String {
+ format!(
+ "1{}{}",
+ if network_use_binary_prefix { "Mi" } else { "M" },
+ unit_char
+ )
+ }
+
+ fn get_g(network_use_binary_prefix: bool, unit_char: &str) -> String {
+ format!(
+ "1{}{}",
+ if network_use_binary_prefix { "Gi" } else { "G" },
+ unit_char
+ )
+ }
+
+ fn get_t(network_use_binary_prefix: bool, unit_char: &str) -> String {
+ format!(
+ "1{}{}",
+ if network_use_binary_prefix { "Ti" } else { "T" },
+ unit_char
+ )
+ }
- (true_max_val, labels)
+ fn get_p(network_use_binary_prefix: bool, unit_char: &str) -> String {
+ format!(
+ "1{}{}",
+ if network_use_binary_prefix { "Pi" } else { "P" },
+ unit_char
+ )
+ }
+
+ if max_entry < m_limit {
+ (
+ m_limit,
+ vec![
+ get_zero(network_use_binary_prefix, unit_char),
+ get_k(network_use_binary_prefix, unit_char),
+ get_m(network_use_binary_prefix, unit_char),
+ ],
+ )
+ } else if max_entry < g_limit {
+ (
+ g_limit,
+ vec![
+ get_zero(network_use_binary_prefix, unit_char),
+ get_k(network_use_binary_prefix, unit_char),
+ get_m(network_use_binary_prefix, unit_char),
+ get_g(network_use_binary_prefix, unit_char),
+ ],
+ )
+ } else if max_entry < t_limit {
+ (
+ t_limit,
+ vec![
+ get_zero(network_use_binary_prefix, unit_char),
+ get_k(network_use_binary_prefix, unit_char),
+ get_m(network_use_binary_prefix, unit_char),
+ get_g(network_use_binary_prefix, unit_char),
+ get_t(network_use_binary_prefix, unit_char),
+ ],
+ )
+ } else {
+ // I really doubt anyone's transferring beyond petabyte speeds...
+ (
+ if network_use_binary_prefix {
+ LOG_PEBI_LIMIT
+ } else {
+ LOG_PETA_LIMIT
+ },
+ vec![
+ get_zero(network_use_binary_prefix, unit_char),
+ get_k(network_use_binary_prefix, unit_char),
+ get_m(network_use_binary_prefix, unit_char),
+ get_g(network_use_binary_prefix, unit_char),
+ get_t(network_use_binary_prefix, unit_char),
+ get_p(network_use_binary_prefix, unit_char),
+ ],
+ )
+ }
+ }
+ }
}
if let Some(network_widget_state) = app_state.net_state.widget_states.get_mut(&widget_id) {
- let network_data_rx: &[(f64, f64)] = &app_state.canvas_data.network_data_rx;
- let network_data_tx: &[(f64, f64)] = &app_state.canvas_data.network_data_tx;
+ let network_data_rx: &mut [(f64, f64)] = &mut app_state.canvas_data.network_data_rx;
+ let network_data_tx: &mut [(f64, f64)] = &mut app_state.canvas_data.network_data_tx;
+
+ let time_start = -(network_widget_state.current_display_time as f64);
- let (max_range, labels) = adjust_network_data_point(
- network_data_rx,
- network_data_tx,
- -(network_widget_state.current_display_time as f64),
- 0.0,
- );
let display_time_labels = vec![
Span::styled(
format!("{}s", network_widget_state.current_display_time / 1000),
@@ -190,29 +435,138 @@ impl NetworkGraphWidget for Painter {
|| (app_state.app_config_fields.autohide_time
&& network_widget_state.autohide_timer.is_none())
{
- Axis::default().bounds([-(network_widget_state.current_display_time as f64), 0.0])
+ Axis::default().bounds([time_start, 0.0])
} else if let Some(time) = network_widget_state.autohide_timer {
if std::time::Instant::now().duration_since(time).as_millis()
< AUTOHIDE_TIMEOUT_MILLISECONDS as u128
{
Axis::default()
- .bounds([-(network_widget_state.current_display_time as f64), 0.0])
+ .bounds([time_start, 0.0])
.style(self.colours.graph_style)
.labels(display_time_labels)
} else {
network_widget_state.autohide_timer = None;
- Axis::default()
- .bounds([-(network_widget_state.current_display_time as f64), 0.0])
+ Axis::default().bounds([time_start, 0.0])
}
} else if draw_loc.height < TIME_LABEL_HEIGHT_LIMIT {
- Axis::default().bounds([-(network_widget_state.current_display_time as f64), 0.0])
+ Axis::default().bounds([time_start, 0.0])
} else {
Axis::default()
- .bounds([-(network_widget_state.current_display_time as f64), 0.0])
+ .bounds([time_start, 0.0])
.style(self.colours.graph_style)
.labels(display_time_labels)
};
+ // Interpolate a point for rx and tx between the last value outside of the left bounds and the first value
+ // inside it.
+ // Because we assume it is all in order for... basically all our code, we can't just append it,
+ // and insertion in the middle seems. So instead, we swap *out* the value that is outside with our
+ // interpolated point, draw and do whatever calculations, then swap back in the old value!
+ //
+ // Note there is some re-used work here! For potential optimizations, we could re-use some work here in/from
+ // get_max_entry...
+ let interpolated_rx_point = if let Some(rx_end_pos) = network_data_rx
+ .iter()
+ .position(|(time, _data)| *time >= time_start)
+ {
+ if rx_end_pos > 1 {
+ let rx_start_pos = rx_end_pos - 1;
+ let outside_rx_point = network_data_rx.get(rx_start_pos);
+ let inside_rx_point = network_data_rx.get(rx_end_pos);
+
+ if let (Some(outside_rx_point), Some(inside_rx_point)) =
+ (outside_rx_point, inside_rx_point)
+ {
+ let old = *outside_rx_point;
+
+ let new_point = (
+ time_start,
+ interpolate_points(outside_rx_point, inside_rx_point, time_start),
+ );
+
+ // debug!(
+ // "Interpolated between {:?} and {:?}, got rx for time {:?}: {:?}",
+ // outside_rx_point, inside_rx_point, time_start, new_point
+ // );
+
+ if let Some(to_replace) = network_data_rx.get_mut(rx_start_pos) {
+ *to_replace = new_point;
+ Some((rx_start_pos, old))
+ } else {
+ None // Failed to get mutable reference.
+ }
+ } else {
+ None // Point somehow doesn't exist in our network_data_rx
+ }
+ } else {
+ None // Point is already "leftmost", no need to interpolate.
+ }
+ } else {
+ None // There is no point.
+ };
+
+ let interpolated_tx_point = if let Some(tx_end_pos) = network_data_tx
+ .iter()
+ .position(|(time, _data)| *time >= time_start)
+ {
+ if tx_end_pos > 1 {
+ let tx_start_pos = tx_end_pos - 1;
+ let outside_tx_point = network_data_tx.get(tx_start_pos);
+ let inside_tx_point = network_data_tx.get(tx_end_pos);
+
+ if let (Some(outside_tx_point), Some(inside_tx_point)) =
+ (outside_tx_point, inside_tx_point)
+ {
+ let old = *outside_tx_point;
+
+ let new_point = (
+ time_start,
+ interpolate_points(outside_tx_point, inside_tx_point, time_start),
+ );
+
+ if let Some(to_replace) = network_data_tx.get_mut(tx_start_pos) {
+ *to_replace = new_point;
+ Some((tx_start_pos, old))
+ } else {
+ None // Failed to get mutable reference.
+ }
+ } else {
+ None // Point somehow doesn't exist in our network_data_tx
+ }
+ } else {
+ None // Point is already "leftmost", no need to interpolate.
+ }
+ } else {
+ None // There is no point.
+ };
+
+ // TODO: Cache network results: Only update if:
+ // - Force update (includes time interval change)
+ // - Old max time is off screen
+ // - A new time interval is better and does not fit (check from end of vector to last checked; we only want to update if it is TOO big!)
+
+ // Find the maximal rx/tx so we know how to scale, and return it.
+
+ let (_best_time, max_entry) = get_max_entry(
+ network_data_rx,
+ network_data_tx,
+ time_start,
+ &app_state.app_config_fields.network_scale_type,
+ app_state.app_config_fields.network_use_binary_prefix,
+ );
+
+ let (max_range, labels) = adjust_network_data_point(
+ max_entry,
+ &app_state.app_config_fields.network_scale_type,
+ &app_state.app_config_fields.network_unit_type,
+ app_state.app_config_fields.network_use_binary_prefix,
+ );
+
+ // Cache results.
+ // network_widget_state.draw_max_range_cache = max_range;
+ // network_widget_state.draw_time_start_cache = best_time;
+ // network_widget_state.draw_labels_cache = labels;
+
let y_axis_labels = labels
.iter()
.map(|label| Span::styled(label, self.colours.graph_style))
@@ -250,12 +604,12 @@ impl NetworkGraphWidget for Painter {
let legend_constraints = if hide_legend {
(Constraint::Ratio(0, 1), Constraint::Ratio(0, 1))
} else {
- (Constraint::Ratio(3, 4), Constraint::Ratio(3, 4))
+ (Constraint::Ratio(1, 1), Constraint::Ratio(3, 4))
};
+ // TODO: Add support for clicking on legend to only show that value on chart.
let dataset = if app_state.app_config_fields.use_old_network_legend && !hide_legend {
- let mut ret_val = vec![];
- ret_val.push(
+ vec![
Dataset::default()
.name(format!("RX: {:7}", app_state.canvas_data.rx_display))
.marker(if app_state.app_config_fields.use_dot {
@@ -266,9 +620,6 @@ impl NetworkGraphWidget for Painter {
.style(self.colours.rx_style)
.data(&network_data_rx)
.graph_type(tui::widgets::GraphType::Line),
- );
-
- ret_val.push(
Dataset::default()
.name(format!("TX: {:7}", app_state.canvas_data.tx_display))
.marker(if app_state.app_config_fields.use_dot {
@@ -279,30 +630,21 @@ impl NetworkGraphWidget for Painter {
.style(self.colours.tx_style)
.data(&network_data_tx)
.graph_type(tui::widgets::GraphType::Line),
- );
- ret_val.push(
Dataset::default()
.name(format!(
"Total RX: {:7}",
app_state.canvas_data.total_rx_display
))
.style(self.colours.total_rx_style),
- );
-
- ret_val.push(
Dataset::default()
.name(format!(
"Total TX: {:7}",
app_state.canvas_data.total_tx_display
))
.style(self.colours.total_tx_style),
- );
-
- ret_val
+ ]
} else {
- let mut ret_val = vec![];
-
- ret_val.push(
+ vec![
Dataset::default()
.name(&app_state.canvas_data.rx_display)
.marker(if app_state.app_config_fields.use_dot {
@@ -313,9 +655,6 @@ impl NetworkGraphWidget for Painter {
.style(self.colours.rx_style)
.data(&network_data_rx)
.graph_type(tui::widgets::GraphType::Line),
- );
-
- ret_val.push(
Dataset::default()
.name(&app_state.canvas_data.tx_display)
.marker(if app_state.app_config_fields.use_dot {
@@ -326,9 +665,7 @@ impl NetworkGraphWidget for Painter {
.style(self.colours.tx_style)
.data(&network_data_tx)
.graph_type(tui::widgets::GraphType::Line),
- );
-
- ret_val
+ ]
};
f.render_widget(
@@ -348,10 +685,22 @@ impl NetworkGraphWidget for Painter {
.hidden_legend_constraints(legend_constraints),
draw_loc,
);
+
+ // Now if you're done, reset any interpolated points!
+ if let Some((index, old_value)) = interpolated_rx_point {
+ if let Some(to_replace) = network_data_rx.get_mut(index) {
+ *to_replace = old_value;
+ }
+ }
+
+ if let Some((index, old_value)) = interpolated_tx_point {
+ if let Some(to_replace) = network_data_tx.get_mut(index) {
+ *to_replace = old_value;
+ }
+ }
}
}
- // TODO: [DEPRECATED] Get rid of this in, like, 0.6...?
fn draw_network_labels<B: Backend>(
&self, f: &mut Frame<'_, B>, app_state: &mut App, draw_loc: Rect, widget_id: u64,
) {