Compare commits
3
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b98d1001de | ||
|
|
8e4424764d | ||
|
|
1e61f4168d |
No files matched your search
@@ -26,7 +26,7 @@ use tokio::{
|
|||||||
time::{sleep, Instant},
|
time::{sleep, Instant},
|
||||||
};
|
};
|
||||||
|
|
||||||
const RENDER_INTERVAL: Duration = Duration::from_millis(250);
|
const RENDER_INTERVAL: Duration = Duration::from_millis(100);
|
||||||
|
|
||||||
pub async fn run_tui(
|
pub async fn run_tui(
|
||||||
args: TuiArgs,
|
args: TuiArgs,
|
||||||
|
|||||||
@@ -1256,7 +1256,6 @@ fn render_graph_area(frame: &mut Frame<'_>, area: Rect, state: &TuiState) {
|
|||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
struct ChartSeries {
|
struct ChartSeries {
|
||||||
name: String,
|
|
||||||
style: Style,
|
style: Style,
|
||||||
data: Vec<(f64, f64)>,
|
data: Vec<(f64, f64)>,
|
||||||
}
|
}
|
||||||
@@ -1273,31 +1272,65 @@ fn target_metric_series(
|
|||||||
viewport: GraphViewportRange,
|
viewport: GraphViewportRange,
|
||||||
metric: GraphMetric,
|
metric: GraphMetric,
|
||||||
) -> Option<ChartSeries> {
|
) -> Option<ChartSeries> {
|
||||||
let data = target
|
// Histories are monotonic. Skip old samples without converting every
|
||||||
|
// retained measurement on each repaint, then keep the nearest valid
|
||||||
|
// predecessor for interpolation across the left boundary.
|
||||||
|
let first = target
|
||||||
.graph_history
|
.graph_history
|
||||||
.iter()
|
.partition_point(|sample| sample.timestamp.mono < viewport.start);
|
||||||
.filter(|sample| {
|
let mut previous = target
|
||||||
sample.timestamp.mono >= viewport.start && sample.timestamp.mono <= viewport.end
|
.graph_history
|
||||||
})
|
.range(..first)
|
||||||
.filter_map(|sample| {
|
.rev()
|
||||||
|
.find_map(|sample| {
|
||||||
metric
|
metric
|
||||||
.value_ns(sample)
|
.value_ns(sample)
|
||||||
.map(|ns| (sample_x(sample, viewport), ns as f64 / 1_000_000.0))
|
.map(|ns| (sample.timestamp.mono, ns as f64 / 1_000_000.0))
|
||||||
})
|
});
|
||||||
.collect::<Vec<_>>();
|
let points = target.graph_history.range(first..).filter_map(|sample| {
|
||||||
|
metric
|
||||||
|
.value_ns(sample)
|
||||||
|
.map(|ns| (sample.timestamp.mono, ns as f64 / 1_000_000.0))
|
||||||
|
});
|
||||||
|
let mut data = Vec::new();
|
||||||
|
for point in points {
|
||||||
|
if let Some(before) = previous {
|
||||||
|
// Only genuine crossing segments contribute boundary points. In
|
||||||
|
// particular, never extend the latest sample to the live edge.
|
||||||
|
if before.0 < viewport.start && point.0 > viewport.start {
|
||||||
|
data.push((0.0, interpolate_graph_value(before, point, viewport.start)));
|
||||||
|
}
|
||||||
|
if before.0 < viewport.end && point.0 > viewport.end {
|
||||||
|
data.push((
|
||||||
|
viewport.window.as_secs_f64(),
|
||||||
|
interpolate_graph_value(before, point, viewport.end),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if point.0 > viewport.end {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if point.0 >= viewport.start {
|
||||||
|
data.push((
|
||||||
|
point.0.duration_since(viewport.start).as_secs_f64(),
|
||||||
|
point.1,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
previous = Some(point);
|
||||||
|
}
|
||||||
(!data.is_empty()).then_some(ChartSeries {
|
(!data.is_empty()).then_some(ChartSeries {
|
||||||
name: target.label.clone(),
|
|
||||||
style: target_style(target_idx),
|
style: target_style(target_idx),
|
||||||
data,
|
data,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn sample_x(sample: &GraphSample, viewport: GraphViewportRange) -> f64 {
|
fn interpolate_graph_value(a: (Instant, f64), b: (Instant, f64), at: Instant) -> f64 {
|
||||||
sample
|
let whole = b.0.duration_since(a.0).as_secs_f64();
|
||||||
.timestamp
|
if whole == 0.0 {
|
||||||
.mono
|
return b.1;
|
||||||
.saturating_duration_since(viewport.start)
|
}
|
||||||
.as_secs_f64()
|
let part = at.duration_since(a.0).as_secs_f64();
|
||||||
|
a.1 + (b.1 - a.1) * (part / whole)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn chart_datasets(series: &[ChartSeries]) -> Vec<Dataset<'_>> {
|
fn chart_datasets(series: &[ChartSeries]) -> Vec<Dataset<'_>> {
|
||||||
@@ -1305,7 +1338,6 @@ fn chart_datasets(series: &[ChartSeries]) -> Vec<Dataset<'_>> {
|
|||||||
.iter()
|
.iter()
|
||||||
.map(|series| {
|
.map(|series| {
|
||||||
Dataset::default()
|
Dataset::default()
|
||||||
.name(series.name.as_str())
|
|
||||||
.marker(symbols::Marker::Braille)
|
.marker(symbols::Marker::Braille)
|
||||||
.graph_type(GraphType::Line)
|
.graph_type(GraphType::Line)
|
||||||
.style(series.style)
|
.style(series.style)
|
||||||
@@ -1405,12 +1437,10 @@ fn graph_timestamp_anchor(state: &TuiState, end: Instant) -> Option<ClientTimest
|
|||||||
.targets
|
.targets
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(|target| {
|
.filter_map(|target| {
|
||||||
target
|
let after = target
|
||||||
.graph_history
|
.graph_history
|
||||||
.iter()
|
.partition_point(|sample| sample.timestamp.mono <= end);
|
||||||
.rev()
|
target.graph_history.get(after.saturating_sub(1))
|
||||||
.find(|sample| sample.timestamp.mono <= end)
|
|
||||||
.or_else(|| target.graph_history.front())
|
|
||||||
})
|
})
|
||||||
.map(|sample| sample.timestamp)
|
.map(|sample| sample.timestamp)
|
||||||
.min_by_key(|timestamp| {
|
.min_by_key(|timestamp| {
|
||||||
|
|||||||
Reference in new issue
Block a user