Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[pull] main from GyulyVGC:main #166

Merged
merged 11 commits into from
Feb 22, 2024
Prev Previous commit
Next Next commit
draw splines in traffic chart
  • Loading branch information
GyulyVGC committed Feb 18, 2024
commit 34fc10405e1f1d9891c7d4263a38e1f66d47b5a2
80 changes: 36 additions & 44 deletions src/chart/manage_chart_data.rs
Original file line number Diff line number Diff line change
@@ -1,77 +1,69 @@
use std::collections::VecDeque;
use splines::{Interpolation, Key, Spline};

use crate::{RunTimeData, TrafficChart};

/// This function is invoked every second by the application subscription
///
/// It updates data (packets and bytes per second) to be displayed in the chart of gui run page
pub fn update_charts_data(runtime_data: &mut RunTimeData, traffic_chart: &mut TrafficChart) {
let tot_seconds = traffic_chart.ticks;
let tot_seconds = traffic_chart.ticks as f32;
traffic_chart.ticks += 1;

let out_bytes_entry = runtime_data.tot_out_bytes - runtime_data.tot_out_bytes_prev;
let in_bytes_entry = runtime_data.tot_in_bytes - runtime_data.tot_in_bytes_prev;
let out_packets_entry = runtime_data.tot_out_packets - runtime_data.tot_out_packets_prev;
let in_packets_entry = runtime_data.tot_in_packets - runtime_data.tot_in_packets_prev;
let out_bytes_entry = -1.0 * (runtime_data.tot_out_bytes - runtime_data.tot_out_bytes_prev) as f32;
let in_bytes_entry = (runtime_data.tot_in_bytes - runtime_data.tot_in_bytes_prev) as f32;
let out_packets_entry = -1.0 * (runtime_data.tot_out_packets - runtime_data.tot_out_packets_prev) as f32;
let in_packets_entry = (runtime_data.tot_in_packets - runtime_data.tot_in_packets_prev) as f32;

let out_bytes_key = Key::new(tot_seconds, out_bytes_entry, Interpolation::Cosine);
let in_bytes_key = Key::new(tot_seconds, in_bytes_entry, Interpolation::Cosine);
let out_packets_key = Key::new(tot_seconds, out_packets_entry, Interpolation::Cosine);
let in_packets_key = Key::new(tot_seconds, in_packets_entry, Interpolation::Cosine);

// update sent bytes traffic data
if traffic_chart.out_bytes.len() >= 30 {
traffic_chart.out_bytes.pop_front();
}
traffic_chart.out_bytes.push_back((
tot_seconds,
-<u128 as TryInto<i64>>::try_into(out_bytes_entry).unwrap(),
));
update_spline(&mut traffic_chart.out_bytes, out_bytes_key);
traffic_chart.min_bytes = get_min(&traffic_chart.out_bytes);
runtime_data.tot_out_bytes_prev = runtime_data.tot_out_bytes;

// update received bytes traffic data
if traffic_chart.in_bytes.len() >= 30 {
traffic_chart.in_bytes.pop_front();
}
traffic_chart
.in_bytes
.push_back((tot_seconds, in_bytes_entry.try_into().unwrap()));
update_spline(&mut traffic_chart.in_bytes, in_bytes_key);
traffic_chart.max_bytes = get_max(&traffic_chart.in_bytes);
runtime_data.tot_in_bytes_prev = runtime_data.tot_in_bytes;

// update sent packets traffic data
if traffic_chart.out_packets.len() >= 30 {
traffic_chart.out_packets.pop_front();
}
traffic_chart.out_packets.push_back((
tot_seconds,
-<u128 as TryInto<i64>>::try_into(out_packets_entry).unwrap(),
));
update_spline(&mut traffic_chart.out_packets, out_packets_key);
traffic_chart.min_packets = get_min(&traffic_chart.out_packets);
runtime_data.tot_out_packets_prev = runtime_data.tot_out_packets;

// update received packets traffic data
if traffic_chart.in_packets.len() >= 30 {
traffic_chart.in_packets.pop_front();
}
traffic_chart
.in_packets
.push_back((tot_seconds, in_packets_entry.try_into().unwrap()));
update_spline(&mut traffic_chart.in_packets, in_packets_key);
traffic_chart.max_packets = get_max(&traffic_chart.in_packets);
runtime_data.tot_in_packets_prev = runtime_data.tot_in_packets;
}

/// Finds the minimum y value to be displayed in chart
fn get_min(deque: &VecDeque<(u32, i64)>) -> i64 {
let mut min = 0;
for (_, x) in deque {
if *x < min {
min = *x;
fn update_spline(spline: &mut Spline<f32, f32>, new_key: Key<f32, f32>) {
if spline.len() >= 30 {
spline.remove(0);
}
spline.add(new_key);
}

/// Finds the minimum y value to be displayed in chart.
fn get_min(spline: &Spline<f32, f32>) -> f32 {
let mut min = 0.0;
for key in spline {
if key.value < min {
min = key.value;
}
}
min
}

/// Finds the maximum y value to be displayed in chart
fn get_max(deque: &VecDeque<(u32, i64)>) -> i64 {
let mut max = 0;
for (_, x) in deque {
if *x > max {
max = *x;
/// Finds the maximum y value to be displayed in chart.
fn get_max(spline: &Spline<f32, f32>) -> f32 {
let mut max = 0.0;
for key in spline {
if key.value > max {
max = key.value;
}
}
max
Expand Down
99 changes: 43 additions & 56 deletions src/chart/types/traffic_chart.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,11 @@
//! This module defines the behavior of the `TrafficChart` struct, used to display chart in GUI run page

use std::cmp::max;
use std::collections::VecDeque;

use iced::alignment::{Horizontal, Vertical};
use iced::widget::{Column, Container};
use iced::{Element, Renderer};
use plotters::prelude::*;
use plotters_iced::{Chart, ChartBuilder, ChartWidget, DrawingBackend};
use splines::{Interpolation, Key, Spline};
use splines::{Spline};

use crate::gui::app::FONT_FAMILY_NAME;
use crate::gui::styles::style_constants::CHARTS_LINE_BORDER;
Expand All @@ -23,21 +20,21 @@ pub struct TrafficChart {
/// Current time interval number
pub ticks: u32,
/// Sent bytes filtered and their time occurrence
pub out_bytes: VecDeque<(u32, i64)>,
pub out_bytes: Spline<f32, f32>,
/// Received bytes filtered and their time occurrence
pub in_bytes: VecDeque<(u32, i64)>,
pub in_bytes: Spline<f32, f32>,
/// Sent packets filtered and their time occurrence
pub out_packets: VecDeque<(u32, i64)>,
pub out_packets: Spline<f32, f32>,
/// Received packets filtered and their time occurrence
pub in_packets: VecDeque<(u32, i64)>,
pub in_packets: Spline<f32, f32>,
/// Minimum number of bytes per time interval (computed on last 30 intervals)
pub min_bytes: i64,
pub min_bytes: f32,
/// Maximum number of bytes per time interval (computed on last 30 intervals)
pub max_bytes: i64,
pub max_bytes: f32,
/// Minimum number of packets per time interval (computed on last 30 intervals)
pub min_packets: i64,
pub min_packets: f32,
/// Maximum number of packets per time interval (computed on last 30 intervals)
pub max_packets: i64,
pub max_packets: f32,
/// Language used for the chart legend
pub language: Language,
/// Packets or bytes
Expand All @@ -50,14 +47,14 @@ impl TrafficChart {
pub fn new(style: StyleType, language: Language) -> Self {
TrafficChart {
ticks: 0,
out_bytes: VecDeque::default(),
in_bytes: VecDeque::default(),
out_packets: VecDeque::default(),
in_packets: VecDeque::default(),
min_bytes: 0,
max_bytes: 0,
min_packets: 0,
max_packets: 0,
out_bytes: Spline::default(),
in_bytes: Spline::default(),
out_packets: Spline::default(),
in_packets: Spline::default(),
min_bytes: 0.0,
max_bytes: 0.0,
min_packets: 0.0,
max_packets: 0.0,
language,
chart_type: ChartType::Bytes,
style,
Expand All @@ -82,26 +79,6 @@ impl TrafficChart {
pub fn change_style(&mut self, style: StyleType) {
self.style = style;
}

fn interp(&self) -> VecDeque<(f32, f32)> {
let mut keys = Vec::new();
for (x, y) in &self.in_packets {
keys.push(Key::new(*x as f32, *y as f32, Interpolation::Cosine));
}
let spline = Spline::from_vec(keys);

let mut ret_val = VecDeque::new();
let len = self.in_packets.len();
let first = self.in_packets.get(0).unwrap().0;
let delta_x = max(30, len - first as usize) as f32;
let first = first as f32;
let d = delta_x / 1000.0;
for i in 0..1000 {
let p = spline.sample(first + i as f32 * d).unwrap_or_default();
ret_val.push_back((first + i as f32 * d, p));
}
ret_val
}
}

impl Chart<Message> for TrafficChart {
Expand Down Expand Up @@ -134,11 +111,11 @@ impl Chart<Message> for TrafficChart {
.set_label_area_size(LabelAreaPosition::Left, 60)
.set_label_area_size(LabelAreaPosition::Bottom, 50);

let get_y_axis_range = |min: i64, max: i64| {
let get_y_axis_range = |min: f32, max: f32| {
let fs = max - min;
#[allow(clippy::cast_possible_truncation, clippy::cast_precision_loss)]
let gap = (fs as f64 * 0.1) as i64;
min as f32 - gap as f32..max as f32 + gap as f32
let gap = fs * 0.05;
min - gap..max + gap
};

let y_axis_range = match self.chart_type {
Expand All @@ -157,9 +134,8 @@ impl Chart<Message> for TrafficChart {
chart
.configure_mesh()
.axis_style(buttons_color)
.bold_line_style(buttons_color.mix(0.4))
.light_line_style(buttons_color.mix(0.2))
.y_max_light_lines(1)
.bold_line_style(buttons_color.mix(0.3))
.light_line_style(buttons_color.mix(0.0))
.y_labels(7)
.label_style(
(FONT_FAMILY_NAME, 12.5)
Expand All @@ -176,20 +152,18 @@ impl Chart<Message> for TrafficChart {
.unwrap();

// Incoming series
let x = self.interp();
let y = VecDeque::new();
chart
.draw_series(
AreaSeries::new(
if self.chart_type.eq(&ChartType::Packets) {
x.iter().copied()
sample_spline(&self.in_packets)
} else {
y.iter().copied()
sample_spline(&self.in_bytes)
},
0.0,
color_incoming.mix(color_mix.into()),
)
.border_style(ShapeStyle::from(&color_incoming).stroke_width(CHARTS_LINE_BORDER)),
.border_style(ShapeStyle::from(&color_incoming).stroke_width(CHARTS_LINE_BORDER)),
)
.expect("Error drawing graph")
.label(incoming_translation(self.language))
Expand All @@ -198,20 +172,18 @@ impl Chart<Message> for TrafficChart {
});

// Outgoing series
let x = self.interp();
let y = VecDeque::new();
chart
.draw_series(
AreaSeries::new(
if self.chart_type.eq(&ChartType::Packets) {
x.iter().copied()
sample_spline(&self.out_packets)
} else {
y.iter().copied()
sample_spline(&self.out_bytes)
},
0.0,
color_outgoing.mix(color_mix.into()),
)
.border_style(ShapeStyle::from(&color_outgoing).stroke_width(CHARTS_LINE_BORDER)),
.border_style(ShapeStyle::from(&color_outgoing).stroke_width(CHARTS_LINE_BORDER)),
)
.expect("Error drawing graph")
.label(outgoing_translation(self.language))
Expand All @@ -235,3 +207,18 @@ impl Chart<Message> for TrafficChart {
.expect("Error drawing graph");
}
}

const PTS: f32 = 300.0;
fn sample_spline(spline: &Spline<f32, f32>) -> Vec<(f32, f32)> {
let mut ret_val = Vec::new();
let len = spline.len();
let first_x = spline.get(0).unwrap().t;
let last_x = spline.get(len-1).unwrap().t;
let delta = (last_x - first_x) / PTS;
for i in 0..=PTS as usize {
let x = first_x + i as f32 * delta;
let p = spline.clamped_sample(x).unwrap_or_default();
ret_val.push((x, p));
}
ret_val
}