use serde::{Deserialize, Serialize};
use super::Trend;
use crate::storage::MetricPoint;
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct MetricSnapshot {
pub key: String,
pub values: Vec<(u64, f64)>,
pub trend: Trend,
}
impl MetricSnapshot {
pub fn new(key: impl Into<String>, values: Vec<(u64, f64)>) -> Self {
let trend = Trend::from_values(&values.iter().map(|(_, v)| *v).collect::<Vec<_>>());
Self { key: key.into(), values, trend }
}
pub fn from_points(key: impl Into<String>, points: &[MetricPoint]) -> Self {
let values: Vec<(u64, f64)> = points
.iter()
.map(|p| {
let ts = p.timestamp.timestamp_millis() as u64;
(ts, p.value)
})
.collect();
Self::new(key, values)
}
pub fn latest(&self) -> Option<f64> {
self.values.last().map(|(_, v)| *v)
}
pub fn min(&self) -> Option<f64> {
self.values.iter().map(|(_, v)| *v).reduce(f64::min)
}
pub fn max(&self) -> Option<f64> {
self.values.iter().map(|(_, v)| *v).reduce(f64::max)
}
pub fn mean(&self) -> Option<f64> {
if self.values.is_empty() {
return None;
}
Some(self.values.iter().map(|(_, v)| *v).sum::<f64>() / self.values.len() as f64)
}
pub fn is_empty(&self) -> bool {
self.values.is_empty()
}
pub fn len(&self) -> usize {
self.values.len()
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ResourceSnapshot {
pub gpu_util: f64,
pub cpu_util: f64,
pub memory_used: u64,
pub memory_total: u64,
pub gpu_memory_used: Option<u64>,
pub gpu_memory_total: Option<u64>,
}
impl Default for ResourceSnapshot {
fn default() -> Self {
Self::new()
}
}
impl ResourceSnapshot {
pub fn new() -> Self {
Self {
gpu_util: 0.0,
cpu_util: 0.0,
memory_used: 0,
memory_total: 0,
gpu_memory_used: None,
gpu_memory_total: None,
}
}
pub fn with_gpu_util(mut self, util: f64) -> Self {
self.gpu_util = util.clamp(0.0, 1.0);
self
}
pub fn with_cpu_util(mut self, util: f64) -> Self {
self.cpu_util = util.clamp(0.0, 1.0);
self
}
pub fn with_memory(mut self, used: u64, total: u64) -> Self {
self.memory_used = used;
self.memory_total = total;
self
}
pub fn with_gpu_memory(mut self, used: u64, total: u64) -> Self {
self.gpu_memory_used = Some(used);
self.gpu_memory_total = Some(total);
self
}
pub fn memory_util(&self) -> f64 {
if self.memory_total == 0 {
return 0.0;
}
self.memory_used as f64 / self.memory_total as f64
}
pub fn gpu_memory_util(&self) -> Option<f64> {
match (self.gpu_memory_used, self.gpu_memory_total) {
(Some(used), Some(total)) if total > 0 => Some(used as f64 / total as f64),
_ => None,
}
}
}