From 3f99eab26339a189300cc8889551223b5d74952c Mon Sep 17 00:00:00 2001 From: Ilya Zlobintsev Date: Thu, 6 Jun 2024 23:06:00 +0300 Subject: [PATCH] feat: add fan speed plot (#338) --- lact-gui/src/app/graphs_window/mod.rs | 24 +++++++ lact-gui/src/app/graphs_window/plot/imp.rs | 75 +++++++++++++++++++++- lact-gui/ui/graphs_window.blp | 20 +++++- 3 files changed, 115 insertions(+), 4 deletions(-) diff --git a/lact-gui/src/app/graphs_window/mod.rs b/lact-gui/src/app/graphs_window/mod.rs index dd14661..f7550db 100644 --- a/lact-gui/src/app/graphs_window/mod.rs +++ b/lact-gui/src/app/graphs_window/mod.rs @@ -27,6 +27,7 @@ impl GraphsWindow { let mut temperature_plot = imp.temperature_plot.data_mut(); let mut clockspeed_plot = imp.clockspeed_plot.data_mut(); let mut power_plot = imp.power_plot.data_mut(); + let mut fan_plot = imp.fan_plot.data_mut(); let throttling_plots = [&mut temperature_plot, &mut clockspeed_plot, &mut power_plot]; match &stats.throttle_info { @@ -81,13 +82,31 @@ impl GraphsWindow { clockspeed_plot.push_line_series("VRAM", point as f64); } + if let Some(max_speed) = stats.fan.speed_max { + fan_plot.push_line_series("Maximum", max_speed as f64); + } + if let Some(min_speed) = stats.fan.speed_min { + fan_plot.push_line_series("Minimum", min_speed as f64); + } + + if let Some(current_speed) = stats.fan.speed_current { + fan_plot.push_line_series("Current", current_speed as f64); + } + + if let Some(pwm) = stats.fan.pwm_current { + fan_plot + .push_secondary_line_series("Percentage", (pwm as f64 / u8::MAX as f64) * 100.0); + } + temperature_plot.trim_data(GRAPH_WIDTH_SECONDS); clockspeed_plot.trim_data(GRAPH_WIDTH_SECONDS); power_plot.trim_data(GRAPH_WIDTH_SECONDS); + fan_plot.trim_data(GRAPH_WIDTH_SECONDS); imp.temperature_plot.queue_draw(); imp.clockspeed_plot.queue_draw(); imp.power_plot.queue_draw(); + imp.fan_plot.queue_draw(); } pub fn clear(&self) { @@ -95,9 +114,12 @@ impl GraphsWindow { *imp.temperature_plot.data_mut() = PlotData::default(); *imp.clockspeed_plot.data_mut() = PlotData::default(); *imp.power_plot.data_mut() = PlotData::default(); + *imp.fan_plot.data_mut() = PlotData::default(); + imp.temperature_plot.queue_draw(); imp.clockspeed_plot.queue_draw(); imp.power_plot.queue_draw(); + imp.fan_plot.queue_draw(); } } @@ -128,6 +150,8 @@ mod imp { pub(super) clockspeed_plot: TemplateChild, #[template_child] pub(super) power_plot: TemplateChild, + #[template_child] + pub(super) fan_plot: TemplateChild, } #[glib::object_subclass] diff --git a/lact-gui/src/app/graphs_window/plot/imp.rs b/lact-gui/src/app/graphs_window/plot/imp.rs index e52915d..dacd707 100644 --- a/lact-gui/src/app/graphs_window/plot/imp.rs +++ b/lact-gui/src/app/graphs_window/plot/imp.rs @@ -21,7 +21,11 @@ pub struct Plot { #[property(get, set)] value_suffix: RefCell, #[property(get, set)] + secondary_value_suffix: RefCell, + #[property(get, set)] y_label_area_size: Cell, + #[property(get, set)] + secondary_y_label_area_size: Cell, pub(super) data: RefCell, } @@ -67,6 +71,7 @@ impl WidgetImpl for Plot { #[cfg_attr(feature = "bench", derive(Clone))] pub struct PlotData { line_series: BTreeMap>, + secondary_line_series: BTreeMap>, throttling: Vec<(i64, (String, bool))>, } @@ -75,6 +80,10 @@ impl PlotData { self.push_line_series_with_time(name, point, chrono::Local::now().naive_local()); } + pub fn push_secondary_line_series(&mut self, name: &str, point: f64) { + self.push_secondary_line_series_with_time(name, point, chrono::Local::now().naive_local()); + } + pub fn push_line_series_with_time(&mut self, name: &str, point: f64, time: NaiveDateTime) { self.line_series .entry(name.to_owned()) @@ -82,6 +91,18 @@ impl PlotData { .push((time.timestamp_millis(), point)); } + pub fn push_secondary_line_series_with_time( + &mut self, + name: &str, + point: f64, + time: NaiveDateTime, + ) { + self.secondary_line_series + .entry(name.to_owned()) + .or_default() + .push((time.timestamp_millis(), point)); + } + pub fn push_throttling(&mut self, name: &str, point: bool) { self.throttling.push(( chrono::Local::now().naive_local().timestamp_millis(), @@ -93,6 +114,10 @@ impl PlotData { self.line_series.iter() } + pub fn secondary_line_series_iter(&self) -> impl Iterator)> { + self.secondary_line_series.iter() + } + pub fn throttling_iter(&self) -> impl Iterator { self.throttling .iter() @@ -112,6 +137,18 @@ impl PlotData { self.line_series.retain(|_, data| !data.is_empty()); + for data in self.secondary_line_series.values_mut() { + let maximum_point = data + .last() + .map(|(date_time, _)| *date_time) + .unwrap_or_default(); + + data.retain(|(time_point, _)| ((maximum_point - *time_point) / 1000) < last_seconds); + } + + self.secondary_line_series + .retain(|_, data| !data.is_empty()); + // Limit data to N seconds let maximum_point = self .throttling @@ -162,12 +199,17 @@ impl Plot { let mut chart = ChartBuilder::on(&root) .x_label_area_size(40) .y_label_area_size(self.y_label_area_size.get()) + .right_y_label_area_size(self.secondary_y_label_area_size.get()) .margin(20) .caption(self.title.borrow().as_str(), ("sans-serif", 30)) .build_cartesian_2d( start_date..max(end_date, start_date + 60 * 1000), 0f64..maximum_value, - )?; + )? + .set_secondary_coord( + start_date..max(end_date, start_date + 60 * 1000), + 0.0..100.0, + ); chart .configure_mesh() @@ -182,6 +224,14 @@ impl Plot { .draw() .context("Failed to draw mesh")?; + chart + .configure_secondary_axes() + .y_label_formatter(&|x| format!("{x}{}", self.secondary_value_suffix.borrow())) + .y_labels(10) + .label_style(("sans-serif", 30)) + .draw() + .context("Failed to draw mesh")?; + // Draw the throttling histogram chart .draw_series( @@ -229,6 +279,29 @@ impl Plot { }); } + for (idx, (caption, data)) in (0..).zip(data.secondary_line_series_iter()) { + chart + .draw_secondary_series(LineSeries::new( + cubic_spline_interpolation(data.iter()) + .into_iter() + .flat_map(|((first_time, second_time), segment)| { + // Interpolate in intervals of one millisecond + (first_time..second_time).map(move |current_date| { + (current_date, segment.evaluate(current_date)) + }) + }), + Palette99::pick(idx + 10).stroke_width(1), // Offset the pallete pick compared to the main graph + )) + .context("Failed to draw series")? + .label(caption) + .legend(move |(x, y)| { + Rectangle::new( + [(x - 10, y - 10), (x + 10, y + 10)], + Palette99::pick(idx + 10), + ) + }); + } + chart .configure_series_labels() .margin(30) diff --git a/lact-gui/ui/graphs_window.blp b/lact-gui/ui/graphs_window.blp index c7466e1..1be6859 100644 --- a/lact-gui/ui/graphs_window.blp +++ b/lact-gui/ui/graphs_window.blp @@ -1,7 +1,7 @@ using Gtk 4.0; template $GraphsWindow: Window { - default-height: 600; + default-height: 800; default-width: 600; title: "Historical data"; hide-on-close: true; @@ -27,6 +27,20 @@ template $GraphsWindow: Window { } } + $Plot fan_plot { + title: "Fan speed"; + hexpand: true; + value-suffix: "PWM"; + secondary-value-suffix: "%"; + y-label-area-size: 140; + secondary-y-label-area-size: 80; + + layout { + column: 0; + row: 1; + } + } + $Plot clockspeed_plot { title: "Clockspeed"; hexpand: true; @@ -35,7 +49,7 @@ template $GraphsWindow: Window { layout { column: 0; - row: 1; + row: 2; } } @@ -47,7 +61,7 @@ template $GraphsWindow: Window { layout { column: 0; - row: 2; + row: 3; } } }