entrenar/dashboard/
snapshot.rs1use serde::{Deserialize, Serialize};
4
5use super::Trend;
6use crate::storage::MetricPoint;
7
8#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
10pub struct MetricSnapshot {
11 pub key: String,
13 pub values: Vec<(u64, f64)>,
15 pub trend: Trend,
17}
18
19impl MetricSnapshot {
20 pub fn new(key: impl Into<String>, values: Vec<(u64, f64)>) -> Self {
22 let trend = Trend::from_values(&values.iter().map(|(_, v)| *v).collect::<Vec<_>>());
23 Self { key: key.into(), values, trend }
24 }
25
26 pub fn from_points(key: impl Into<String>, points: &[MetricPoint]) -> Self {
28 let values: Vec<(u64, f64)> = points
29 .iter()
30 .map(|p| {
31 let ts = p.timestamp.timestamp_millis() as u64;
32 (ts, p.value)
33 })
34 .collect();
35 Self::new(key, values)
36 }
37
38 pub fn latest(&self) -> Option<f64> {
40 self.values.last().map(|(_, v)| *v)
41 }
42
43 pub fn min(&self) -> Option<f64> {
45 self.values.iter().map(|(_, v)| *v).reduce(f64::min)
46 }
47
48 pub fn max(&self) -> Option<f64> {
50 self.values.iter().map(|(_, v)| *v).reduce(f64::max)
51 }
52
53 pub fn mean(&self) -> Option<f64> {
55 if self.values.is_empty() {
56 return None;
57 }
58 Some(self.values.iter().map(|(_, v)| *v).sum::<f64>() / self.values.len() as f64)
59 }
60
61 pub fn is_empty(&self) -> bool {
63 self.values.is_empty()
64 }
65
66 pub fn len(&self) -> usize {
68 self.values.len()
69 }
70}
71
72#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
74pub struct ResourceSnapshot {
75 pub gpu_util: f64,
77 pub cpu_util: f64,
79 pub memory_used: u64,
81 pub memory_total: u64,
83 pub gpu_memory_used: Option<u64>,
85 pub gpu_memory_total: Option<u64>,
87}
88
89impl Default for ResourceSnapshot {
90 fn default() -> Self {
91 Self::new()
92 }
93}
94
95impl ResourceSnapshot {
96 pub fn new() -> Self {
98 Self {
99 gpu_util: 0.0,
100 cpu_util: 0.0,
101 memory_used: 0,
102 memory_total: 0,
103 gpu_memory_used: None,
104 gpu_memory_total: None,
105 }
106 }
107
108 pub fn with_gpu_util(mut self, util: f64) -> Self {
110 self.gpu_util = util.clamp(0.0, 1.0);
111 self
112 }
113
114 pub fn with_cpu_util(mut self, util: f64) -> Self {
116 self.cpu_util = util.clamp(0.0, 1.0);
117 self
118 }
119
120 pub fn with_memory(mut self, used: u64, total: u64) -> Self {
122 self.memory_used = used;
123 self.memory_total = total;
124 self
125 }
126
127 pub fn with_gpu_memory(mut self, used: u64, total: u64) -> Self {
129 self.gpu_memory_used = Some(used);
130 self.gpu_memory_total = Some(total);
131 self
132 }
133
134 pub fn memory_util(&self) -> f64 {
136 if self.memory_total == 0 {
137 return 0.0;
138 }
139 self.memory_used as f64 / self.memory_total as f64
140 }
141
142 pub fn gpu_memory_util(&self) -> Option<f64> {
144 match (self.gpu_memory_used, self.gpu_memory_total) {
145 (Some(used), Some(total)) if total > 0 => Some(used as f64 / total as f64),
146 _ => None,
147 }
148 }
149}