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

refactor: points rework (v1) #1663

Merged
merged 46 commits into from
Feb 3, 2025
Merged
Changes from 1 commit
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
cb3f7a7
refactor: add new method of storing timeseries data
ClementTsang Dec 26, 2024
e6b1aaf
mostly finish adding data
ClementTsang Dec 26, 2024
6d63f9e
tmp
ClementTsang Jan 6, 2025
f697c8f
migrate over to separate lib
ClementTsang Jan 12, 2025
1490581
prepare to migrate over to new timeseries storage
ClementTsang Jan 12, 2025
6dc4946
prepare to migrate frozen state
ClementTsang Jan 13, 2025
e6a14fd
migrate frozen state
ClementTsang Jan 16, 2025
bf85e7e
name
ClementTsang Jan 16, 2025
9265319
migrate data collection
ClementTsang Jan 16, 2025
4385f57
migrate network
ClementTsang Jan 16, 2025
9d3c9ba
fix some stuff
ClementTsang Jan 16, 2025
25881f3
fix a panic from bad pruning
ClementTsang Jan 16, 2025
99ade9d
Fix pruning issues
ClementTsang Jan 17, 2025
3986175
migrate RAM
ClementTsang Jan 18, 2025
7675f6e
migrate swap
ClementTsang Jan 18, 2025
44bb8fb
migrate cache label
ClementTsang Jan 18, 2025
040a965
refactor out to function
ClementTsang Jan 18, 2025
e07d9ff
migrate ram points
ClementTsang Jan 18, 2025
6974b7b
migrate swap points
ClementTsang Jan 19, 2025
e4ff80a
migrate cache points
ClementTsang Jan 19, 2025
15aae70
migrate arc
ClementTsang Jan 19, 2025
9a8e224
migrate gpu, remove a bunch of state code around force update
ClementTsang Jan 19, 2025
88f4376
rename cache, also some comments
ClementTsang Jan 19, 2025
db80100
some temp cleanup
ClementTsang Jan 19, 2025
86853a0
migrate disk
ClementTsang Jan 19, 2025
e4c531f
comments to remind me above fixmes, fix bug around time graph spans
ClementTsang Jan 19, 2025
eb838f0
migrate load avg
ClementTsang Jan 19, 2025
4bee13b
port temps
ClementTsang Jan 19, 2025
e67806e
style
ClementTsang Jan 20, 2025
85549ca
fix bug wiwth left edge gap
ClementTsang Jan 20, 2025
40a43ee
partial migration of cpu, reorganize data file structure
ClementTsang Jan 20, 2025
9b8f33d
migrate cpu
ClementTsang Jan 20, 2025
ec562f2
some cleanup
ClementTsang Jan 20, 2025
7ee4878
fix bug with cpu widget + clippy
ClementTsang Jan 20, 2025
fbbd100
start some small optimization work
ClementTsang Jan 20, 2025
408e81c
fix some things for some platforms
ClementTsang Jan 20, 2025
a41142a
refactor: rename data_collection to collection
ClementTsang Jan 20, 2025
c3365be
refactor: only process temp type in data eat step
ClementTsang Jan 25, 2025
8e3c5da
flatten components folder a bit
ClementTsang Jan 26, 2025
12f831b
partially migrate to new graph system and fix cpu bug
ClementTsang Jan 26, 2025
0b7a689
driveby migration of process list to reduce allocs + more migration o…
ClementTsang Jan 29, 2025
9ffaebe
revert the collection change
ClementTsang Jan 29, 2025
3564f8b
port over network stuff...
ClementTsang Jan 31, 2025
c633dcd
fully migrate network, and fix some log bugs while we're at it
ClementTsang Jan 31, 2025
fb956e2
fix cpu colour in all mode
ClementTsang Feb 1, 2025
8aa3dd1
clean up some disk table stuff
ClementTsang Feb 3, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
partially migrate to new graph system and fix cpu bug
ClementTsang committed Jan 26, 2025
commit 12f831bb3c596f2709bf5a58a2e68f44146dae1e
64 changes: 48 additions & 16 deletions src/canvas/components/time_graph.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
mod time_chart;
pub use time_chart::*;

use std::borrow::Cow;
use std::{borrow::Cow, time::Instant};

use concat_string::concat_string;
use tui::{
@@ -13,13 +13,43 @@ use tui::{
Frame,
};

use crate::canvas::drawing_utils::widget_block;
use crate::{app::data::Values, canvas::drawing_utils::widget_block};

/// Represents the data required by the [`TimeGraph`].
///
/// TODO: We may be able to get rid of this intermediary data structure.
#[derive(Default)]
pub struct GraphData<'a> {
pub points: &'a [Point],
pub style: Style,
pub name: Option<Cow<'a, str>>,
time: &'a [Instant],
values: Option<&'a Values>,
style: Style,
name: Option<Cow<'a, str>>,
}

impl<'a> GraphData<'a> {
pub fn time(mut self, time: &'a [Instant]) -> Self {
self.time = time;

self
}

pub fn values(mut self, values: &'a Values) -> Self {
self.values = Some(values);

self
}

pub fn style(mut self, style: Style) -> Self {
self.style = style;

self
}

pub fn name(mut self, name: Cow<'a, str>) -> Self {
self.name = Some(name);

self
}
}

pub struct TimeGraph<'a> {
@@ -30,7 +60,7 @@ pub struct TimeGraph<'a> {
pub hide_x_labels: bool,

/// The min and max y boundaries.
pub y_bounds: [f64; 2],
pub y_bounds: AxisBound,

/// Any y-labels.
pub y_labels: &'a [Cow<'a, str>],
@@ -71,7 +101,7 @@ impl TimeGraph<'_> {
/// Generates the [`Axis`] for the x-axis.
fn generate_x_axis(&self) -> Axis<'_> {
// Due to how we display things, we need to adjust the time bound values.
let adjusted_x_bounds = [self.x_min, 0.0];
let adjusted_x_bounds = AxisBound::TimeMin(self.x_min);

if self.hide_x_labels {
Axis::default().bounds(adjusted_x_bounds)
@@ -116,9 +146,6 @@ impl TimeGraph<'_> {
pub fn draw_time_graph(&self, f: &mut Frame<'_>, draw_loc: Rect, graph_data: &[GraphData<'_>]) {
let x_axis = self.generate_x_axis();
let y_axis = self.generate_y_axis();

// This is some ugly manual loop unswitching. Maybe unnecessary.
// TODO: Optimize this step. Cut out unneeded points.
let data = graph_data.iter().map(create_dataset).collect();

let block = {
@@ -153,14 +180,19 @@ impl TimeGraph<'_> {
/// Creates a new [`Dataset`].
fn create_dataset<'a>(data: &'a GraphData<'a>) -> Dataset<'a> {
let GraphData {
points,
time,
values,
style,
name,
} = data;

let Some(values) = values else {
return Dataset::default();
};

let dataset = Dataset::default()
.style(*style)
.data(points)
.data(time, values)
.graph_type(GraphType::Line);

if let Some(name) = name {
@@ -181,7 +213,7 @@ mod test {
widgets::BorderType,
};

use super::TimeGraph;
use super::{AxisBound, TimeGraph};
use crate::canvas::components::time_graph::Axis;

const Y_LABELS: [Cow<'static, str>; 3] = [
@@ -195,7 +227,7 @@ mod test {
title: " Network ".into(),
x_min: -15000.0,
hide_x_labels: false,
y_bounds: [0.0, 100.5],
y_bounds: AxisBound::Max(100.5),
y_labels: &Y_LABELS,
graph_style: Style::default().fg(Color::Red),
border_style: Style::default().fg(Color::Blue),
@@ -216,7 +248,7 @@ mod test {
let x_axis = tg.generate_x_axis();

let actual = Axis::default()
.bounds([-15000.0, 0.0])
.bounds(AxisBound::TimeMin(-15000.0))
.labels(vec![Span::styled("15s", style), Span::styled("0s", style)])
.style(style);
assert_eq!(x_axis.bounds, actual.bounds);
@@ -231,7 +263,7 @@ mod test {
let y_axis = tg.generate_y_axis();

let actual = Axis::default()
.bounds([0.0, 100.5])
.bounds(AxisBound::Max(100.5))
.labels(vec![
Span::styled("0%", style),
Span::styled("50%", style),
100 changes: 68 additions & 32 deletions src/canvas/components/time_graph/time_chart.rs
Original file line number Diff line number Diff line change
@@ -7,7 +7,7 @@
mod canvas;
mod points;

use std::{cmp::max, str::FromStr};
use std::{cmp::max, str::FromStr, time::Instant};

use canvas::*;
use tui::{
@@ -20,20 +20,47 @@ use tui::{
};
use unicode_width::UnicodeWidthStr;

use crate::app::data::Values;

pub const DEFAULT_LEGEND_CONSTRAINTS: (Constraint, Constraint) =
(Constraint::Ratio(1, 4), Constraint::Length(4));

/// A single graph point.
pub type Point = (f64, f64);

/// An axis bound type.
#[derive(Debug, Default, Clone, Copy, PartialEq)]
pub enum AxisBound {
/// Just 0.
#[default]
Zero,
/// Bound by 0 and a dynamic max value.
DynamicMax { current_max: f64 },
/// Bound by a minimum value to 0.
TimeMin(f64),
/// Bound by 0 and a max value.
Max(f64),
}

impl AxisBound {
fn to_bounds(&self) -> [f64; 2] {
match self {
AxisBound::Zero => [0.0, 0.0],
AxisBound::DynamicMax { current_max } => [0.0, *current_max],
AxisBound::TimeMin(min) => [*min, 0.0],
AxisBound::Max(max) => [0.0, *max],
}
}
}

/// An X or Y axis for the [`TimeChart`] widget
#[derive(Debug, Default, Clone, PartialEq)]
pub struct Axis<'a> {
/// Title displayed next to axis end
pub(crate) title: Option<Line<'a>>,
/// Bounds for the axis (all data points outside these limits will not be
/// represented)
pub(crate) bounds: [f64; 2],
pub(crate) bounds: AxisBound,
/// A list of labels to put to the left or below the axis
pub(crate) labels: Option<Vec<Span<'a>>>,
/// The style used to draw the axis itself
@@ -47,9 +74,6 @@ impl<'a> Axis<'a> {
///
/// It will be displayed at the end of the axis. For an X axis this is the
/// right, for a Y axis, this is the top.
///
/// This is a fluent setter method which must be chained or used as it
/// consumes self
#[must_use = "method moves the value of self and returns the modified value"]
#[cfg_attr(not(test), expect(dead_code))]
pub fn title<T>(mut self, title: T) -> Axis<'a>
@@ -60,14 +84,9 @@ impl<'a> Axis<'a> {
self
}

/// Sets the bounds of this axis
///
/// In other words, sets the min and max value on this axis.
///
/// This is a fluent setter method which must be chained or used as it
/// consumes self
/// Sets the bounds of this axis.
#[must_use = "method moves the value of self and returns the modified value"]
pub fn bounds(mut self, bounds: [f64; 2]) -> Axis<'a> {
pub fn bounds(mut self, bounds: AxisBound) -> Axis<'a> {
self.bounds = bounds;
self
}
@@ -239,23 +258,28 @@ impl FromStr for LegendPosition {
}
}

#[derive(Debug, Default, Clone)]
enum Data<'a> {
Some {
times: &'a [Instant],
values: &'a Values,
},
#[default]
None,
}

/// A group of data points
///
/// This is the main element composing a [`TimeChart`].
///
/// A dataset can be [named](Dataset::name). Only named datasets will be
/// rendered in the legend.
///
/// After that, you can pass it data with [`Dataset::data`]. Data is an array of
/// `f64` tuples (`(f64, f64)`), the first element being X and the second Y.
/// It's also worth noting that, unlike the [`Rect`], here the Y axis is bottom
/// to top, as in math.
#[derive(Debug, Default, Clone, PartialEq)]
#[derive(Debug, Default, Clone)]
pub struct Dataset<'a> {
/// Name of the dataset (used in the legend if shown)
name: Option<Line<'a>>,
/// A reference to the actual data
data: &'a [(f64, f64)],
/// A reference to data.
data: Data<'a>,
/// Symbol used for each points of this dataset
marker: symbols::Marker,
/// Determines graph type used for drawing points
@@ -284,8 +308,8 @@ impl<'a> Dataset<'a> {
/// element being X and the second Y. It's also worth noting that,
/// unlike the [`Rect`], here the Y axis is bottom to top, as in math.
#[must_use = "method moves the value of self and returns the modified value"]
pub fn data(mut self, data: &'a [(f64, f64)]) -> Dataset<'a> {
self.data = data;
pub fn data(mut self, times: &'a [Instant], values: &'a Values) -> Dataset<'a> {
self.data = Data::Some { times, values };
self
}

@@ -297,9 +321,6 @@ impl<'a> Dataset<'a> {
///
/// Note [`Marker::Braille`] requires a font that supports Unicode Braille
/// Patterns.
///
/// This is a fluent setter method which must be chained or used as it
/// consumes self
#[must_use = "method moves the value of self and returns the modified value"]
#[expect(dead_code)]
pub fn marker(mut self, marker: symbols::Marker) -> Dataset<'a> {
@@ -367,7 +388,7 @@ struct ChartLayout {
/// - Automatic interpolation to points that fall *just* outside of the screen.
///
/// TODO: Support for putting the legend on the left side.
#[derive(Debug, Default, Clone, PartialEq)]
#[derive(Debug, Default, Clone)]
pub struct TimeChart<'a> {
/// A block to display around the widget eventually
block: Option<Block<'a>>,
@@ -383,7 +404,7 @@ pub struct TimeChart<'a> {
legend_style: Style,
/// Constraints used to determine whether the legend should be shown or not
hidden_legend_constraints: (Constraint, Constraint),
/// The position detnermine where the legenth is shown or hide regaurdless
/// The position determining whether the length is shown or hidden, regardless
/// of `hidden_legend_constraints`
legend_position: Option<LegendPosition>,
/// The marker type.
@@ -725,7 +746,7 @@ impl Widget for TimeChart<'_> {

// Sample the style of the entire widget. This sample will be used to reset the
// style of the cells that are part of the components put on top of the
// grah area (i.e legend and axis names).
// graph area (i.e legend and axis names).
let Some(original_style) = buf.cell((area.left(), area.top())).map(|cell| cell.style())
else {
return;
@@ -767,10 +788,13 @@ impl Widget for TimeChart<'_> {
}
}

let x_bounds = self.x_axis.bounds.to_bounds();
let y_bounds = self.y_axis.bounds.to_bounds();

Canvas::default()
.background_color(self.style.bg.unwrap_or(Color::Reset))
.x_bounds(self.x_axis.bounds)
.y_bounds(self.y_axis.bounds)
.x_bounds(x_bounds)
.y_bounds(y_bounds)
.marker(self.marker)
.paint(|ctx| {
self.draw_points(ctx);
@@ -930,6 +954,8 @@ mod tests {
};
}

use std::time::Duration;

use tui::style::{Modifier, Stylize};

use super::*;
@@ -942,7 +968,17 @@ mod tests {

#[test]
fn it_should_hide_the_legend() {
let data = [(0.0, 5.0), (1.0, 6.0), (3.0, 7.0)];
let now = Instant::now();
let times = [
now,
now.checked_add(Duration::from_secs(1)).unwrap(),
now.checked_add(Duration::from_secs(2)).unwrap(),
];
let mut values = Values::default();
values.push(5.0);
values.push(6.0);
values.push(7.0);

let cases = [
LegendTestCase {
chart_area: Rect::new(0, 0, 100, 100),
@@ -959,7 +995,7 @@ mod tests {
let datasets = (0..10)
.map(|i| {
let name = format!("Dataset #{i}");
Dataset::default().name(name).data(&data)
Dataset::default().name(name).data(&times, &values)
})
.collect::<Vec<_>>();
let chart = TimeChart::new(datasets)
Loading