ruvector_memopt/dashboard/
data.rs

1//! Dashboard data structures
2//!
3//! These structures are designed to be serializable for WASM/JSON transport
4
5use serde::{Deserialize, Serialize};
6use std::collections::VecDeque;
7
8/// Real-time system metrics
9#[derive(Debug, Clone, Serialize, Deserialize)]
10pub struct SystemMetrics {
11    pub timestamp_ms: u64,
12    pub memory_used_mb: u64,
13    pub memory_total_mb: u64,
14    pub memory_percent: f64,
15    pub swap_used_mb: u64,
16    pub swap_total_mb: u64,
17    pub process_count: usize,
18    pub optimization_count: u32,
19    pub total_freed_mb: f64,
20}
21
22/// Algorithm performance metrics
23#[derive(Debug, Clone, Serialize, Deserialize)]
24pub struct AlgorithmMetrics {
25    pub name: String,
26    pub last_run_us: u64,
27    pub avg_run_us: f64,
28    pub calls: u64,
29    pub success_rate: f64,
30}
31
32/// Process cluster information
33#[derive(Debug, Clone, Serialize, Deserialize)]
34pub struct ClusterInfo {
35    pub id: usize,
36    pub process_count: usize,
37    pub total_memory_mb: f64,
38    pub connectivity: f64,
39    pub top_processes: Vec<ProcessInfo>,
40}
41
42/// Individual process information
43#[derive(Debug, Clone, Serialize, Deserialize)]
44pub struct ProcessInfo {
45    pub pid: u32,
46    pub name: String,
47    pub memory_mb: f64,
48    pub pagerank_score: f64,
49    pub trim_priority: f64,
50}
51
52/// Spectral analysis state
53#[derive(Debug, Clone, Serialize, Deserialize)]
54pub struct SpectralState {
55    pub pattern_class: String,
56    pub trend: f64,
57    pub variance: f64,
58    pub recommendation: String,
59    pub confidence: f64,
60    pub predicted_relief_mb: u64,
61}
62
63/// Count-Min Sketch statistics
64#[derive(Debug, Clone, Serialize, Deserialize)]
65pub struct SketchStats {
66    pub total_events: u64,
67    pub memory_bytes: usize,
68    pub fill_ratio: f64,
69    pub peak_hours: Vec<(u32, u64)>,
70}
71
72/// Complete dashboard data packet
73#[derive(Debug, Clone, Serialize, Deserialize)]
74pub struct DashboardData {
75    pub metrics: SystemMetrics,
76    pub algorithms: Vec<AlgorithmMetrics>,
77    pub clusters: Vec<ClusterInfo>,
78    pub spectral: SpectralState,
79    pub sketch: SketchStats,
80    pub history: Vec<HistoryPoint>,
81}
82
83/// Historical data point for charts
84#[derive(Debug, Clone, Serialize, Deserialize)]
85pub struct HistoryPoint {
86    pub timestamp_ms: u64,
87    pub memory_percent: f64,
88    pub freed_mb: f64,
89}
90
91/// Incremental update for websocket
92#[derive(Debug, Clone, Serialize, Deserialize)]
93pub struct DashboardUpdate {
94    pub update_type: UpdateType,
95    pub timestamp_ms: u64,
96    pub data: UpdateData,
97}
98
99#[derive(Debug, Clone, Serialize, Deserialize)]
100pub enum UpdateType {
101    Metrics,
102    Optimization,
103    Alert,
104    PatternChange,
105}
106
107#[derive(Debug, Clone, Serialize, Deserialize)]
108pub enum UpdateData {
109    Metrics(SystemMetrics),
110    Optimization { freed_mb: f64, duration_ms: u64 },
111    Alert { level: String, message: String },
112    Pattern { old: String, new: String },
113}
114
115/// Dashboard data collector
116pub struct DashboardCollector {
117    history: VecDeque<HistoryPoint>,
118    max_history: usize,
119    optimization_count: u32,
120    total_freed_mb: f64,
121    algorithm_stats: Vec<AlgorithmMetrics>,
122}
123
124impl DashboardCollector {
125    pub fn new() -> Self {
126        Self {
127            history: VecDeque::with_capacity(3600),
128            max_history: 3600,
129            optimization_count: 0,
130            total_freed_mb: 0.0,
131            algorithm_stats: vec![
132                AlgorithmMetrics {
133                    name: "MinCut".into(),
134                    last_run_us: 0,
135                    avg_run_us: 0.0,
136                    calls: 0,
137                    success_rate: 1.0,
138                },
139                AlgorithmMetrics {
140                    name: "PageRank".into(),
141                    last_run_us: 0,
142                    avg_run_us: 0.0,
143                    calls: 0,
144                    success_rate: 1.0,
145                },
146                AlgorithmMetrics {
147                    name: "Sketch".into(),
148                    last_run_us: 0,
149                    avg_run_us: 0.0,
150                    calls: 0,
151                    success_rate: 1.0,
152                },
153                AlgorithmMetrics {
154                    name: "Spectral".into(),
155                    last_run_us: 0,
156                    avg_run_us: 0.0,
157                    calls: 0,
158                    success_rate: 1.0,
159                },
160            ],
161        }
162    }
163
164    /// Record a memory sample
165    pub fn record_sample(&mut self, memory_percent: f64) {
166        let point = HistoryPoint {
167            timestamp_ms: chrono::Utc::now().timestamp_millis() as u64,
168            memory_percent,
169            freed_mb: 0.0,
170        };
171
172        if self.history.len() >= self.max_history {
173            self.history.pop_front();
174        }
175        self.history.push_back(point);
176    }
177
178    /// Record an optimization
179    pub fn record_optimization(&mut self, freed_mb: f64) {
180        self.optimization_count += 1;
181        self.total_freed_mb += freed_mb;
182
183        if let Some(last) = self.history.back_mut() {
184            last.freed_mb = freed_mb;
185        }
186    }
187
188    /// Record algorithm timing
189    pub fn record_algorithm(&mut self, name: &str, duration_us: u64, success: bool) {
190        if let Some(stats) = self.algorithm_stats.iter_mut().find(|s| s.name == name) {
191            stats.calls += 1;
192            stats.last_run_us = duration_us;
193            stats.avg_run_us =
194                (stats.avg_run_us * (stats.calls - 1) as f64 + duration_us as f64) / stats.calls as f64;
195            if !success {
196                stats.success_rate =
197                    (stats.success_rate * (stats.calls - 1) as f64) / stats.calls as f64;
198            }
199        }
200    }
201
202    /// Get full dashboard data
203    pub fn get_data(&self, metrics: SystemMetrics, clusters: Vec<ClusterInfo>, spectral: SpectralState, sketch: SketchStats) -> DashboardData {
204        DashboardData {
205            metrics,
206            algorithms: self.algorithm_stats.clone(),
207            clusters,
208            spectral,
209            sketch,
210            history: self.history.iter().cloned().collect(),
211        }
212    }
213
214    /// Get recent history
215    pub fn get_history(&self, count: usize) -> Vec<HistoryPoint> {
216        self.history.iter().rev().take(count).cloned().collect()
217    }
218
219    /// Get statistics
220    pub fn stats(&self) -> CollectorStats {
221        CollectorStats {
222            history_count: self.history.len(),
223            optimization_count: self.optimization_count,
224            total_freed_mb: self.total_freed_mb,
225        }
226    }
227}
228
229impl Default for DashboardCollector {
230    fn default() -> Self {
231        Self::new()
232    }
233}
234
235#[derive(Debug, Clone)]
236pub struct CollectorStats {
237    pub history_count: usize,
238    pub optimization_count: u32,
239    pub total_freed_mb: f64,
240}
241
242#[cfg(test)]
243mod tests {
244    use super::*;
245
246    #[test]
247    fn test_collector() {
248        let mut collector = DashboardCollector::new();
249
250        collector.record_sample(50.0);
251        collector.record_sample(55.0);
252        collector.record_optimization(100.0);
253        collector.record_algorithm("MinCut", 1000, true);
254
255        let stats = collector.stats();
256        assert_eq!(stats.history_count, 2);
257        assert_eq!(stats.optimization_count, 1);
258        assert_eq!(stats.total_freed_mb, 100.0);
259    }
260
261    #[test]
262    fn test_serialization() {
263        let metrics = SystemMetrics {
264            timestamp_ms: 0,
265            memory_used_mb: 8000,
266            memory_total_mb: 16000,
267            memory_percent: 50.0,
268            swap_used_mb: 0,
269            swap_total_mb: 0,
270            process_count: 100,
271            optimization_count: 5,
272            total_freed_mb: 500.0,
273        };
274
275        let json = serde_json::to_string(&metrics).unwrap();
276        assert!(json.contains("memory_used_mb"));
277    }
278}