1use serde::{Deserialize, Serialize};
6use std::collections::VecDeque;
7
8#[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#[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#[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#[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#[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#[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#[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#[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#[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
115pub 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 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 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 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 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 pub fn get_history(&self, count: usize) -> Vec<HistoryPoint> {
216 self.history.iter().rev().take(count).cloned().collect()
217 }
218
219 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}