ruvector_memopt/dashboard/
server.rs1use std::sync::Arc;
6use tokio::sync::RwLock;
7use sysinfo::System;
8
9use super::data::{
10 DashboardCollector, DashboardData, SystemMetrics, ClusterInfo, SpectralState, SketchStats,
11 ProcessInfo,
12};
13use crate::algorithms::{MinCutClusterer, ProcessPageRank, CountMinSketch, SpectralAnalyzer};
14use crate::windows::memory::WindowsMemoryOptimizer;
15
16pub struct DashboardServer {
18 collector: Arc<RwLock<DashboardCollector>>,
19 mincut: Arc<RwLock<MinCutClusterer>>,
20 pagerank: Arc<RwLock<ProcessPageRank>>,
21 sketch: Arc<RwLock<CountMinSketch>>,
22 spectral: Arc<RwLock<SpectralAnalyzer>>,
23 system: Arc<RwLock<System>>,
24}
25
26impl DashboardServer {
27 pub fn new() -> Self {
28 Self {
29 collector: Arc::new(RwLock::new(DashboardCollector::new())),
30 mincut: Arc::new(RwLock::new(MinCutClusterer::new())),
31 pagerank: Arc::new(RwLock::new(ProcessPageRank::new())),
32 sketch: Arc::new(RwLock::new(CountMinSketch::new(0.01, 0.001))),
33 spectral: Arc::new(RwLock::new(SpectralAnalyzer::new(60))),
34 system: Arc::new(RwLock::new(System::new_all())),
35 }
36 }
37
38 pub async fn update(&self) -> Result<DashboardData, String> {
40 let mut system = self.system.write().await;
42 system.refresh_all();
43
44 let status = WindowsMemoryOptimizer::get_memory_status()?;
46
47 let spectral_state = {
49 let mut spectral = self.spectral.write().await;
50 spectral.add_sample(status.memory_load_percent as f64 / 100.0);
51 let class = spectral.classify();
52 let rec = spectral.get_recommendation();
53 let stats = spectral.stats();
54
55 SpectralState {
56 pattern_class: format!("{:?}", class),
57 trend: stats.trend,
58 variance: stats.variance,
59 recommendation: rec.reason,
60 confidence: rec.confidence,
61 predicted_relief_mb: rec.predicted_relief_mb,
62 }
63 };
64
65 let sketch_stats = {
67 let mut sketch = self.sketch.write().await;
68 sketch.add(status.memory_load_percent as u64);
69 let stats = sketch.stats();
70
71 SketchStats {
72 total_events: stats.total_count,
73 memory_bytes: stats.memory_bytes,
74 fill_ratio: stats.fill_ratio,
75 peak_hours: vec![], }
77 };
78
79 let clusters = {
81 let mut mincut = self.mincut.write().await;
82 mincut.build_graph(&system);
83 let raw_clusters = mincut.find_clusters(5);
84
85 let mut pagerank = self.pagerank.write().await;
86 pagerank.compute(&system);
87
88 raw_clusters
89 .into_iter()
90 .map(|c| {
91 let top_procs: Vec<ProcessInfo> = c
92 .processes
93 .iter()
94 .take(5)
95 .filter_map(|&pid| {
96 system.process(sysinfo::Pid::from_u32(pid)).map(|p| ProcessInfo {
97 pid,
98 name: p.name().to_string(),
99 memory_mb: p.memory() as f64 / (1024.0 * 1024.0),
100 pagerank_score: pagerank.get_score(pid),
101 trim_priority: 0.5,
102 })
103 })
104 .collect();
105
106 ClusterInfo {
107 id: c.id,
108 process_count: c.processes.len(),
109 total_memory_mb: c.total_memory_mb,
110 connectivity: c.connectivity,
111 top_processes: top_procs,
112 }
113 })
114 .collect()
115 };
116
117 let metrics = SystemMetrics {
119 timestamp_ms: chrono::Utc::now().timestamp_millis() as u64,
120 memory_used_mb: status.used_physical_mb() as u64,
121 memory_total_mb: status.total_physical_mb as u64,
122 memory_percent: status.memory_load_percent as f64,
123 swap_used_mb: (status.total_page_file_mb - status.available_page_file_mb) as u64,
124 swap_total_mb: status.total_page_file_mb as u64,
125 process_count: system.processes().len(),
126 optimization_count: 0,
127 total_freed_mb: 0.0,
128 };
129
130 let mut collector = self.collector.write().await;
132 collector.record_sample(metrics.memory_percent);
133
134 Ok(collector.get_data(metrics, clusters, spectral_state, sketch_stats))
135 }
136
137 pub async fn record_optimization(&self, freed_mb: f64) {
139 let mut collector = self.collector.write().await;
140 collector.record_optimization(freed_mb);
141 }
142
143 pub async fn get_json(&self) -> Result<String, String> {
145 let data = self.update().await?;
146 serde_json::to_string_pretty(&data).map_err(|e| e.to_string())
147 }
148
149 pub async fn serve(&self, port: u16) -> Result<(), String> {
151 println!("š Dashboard server starting on http://localhost:{}", port);
152 println!(" Press Ctrl+C to stop\n");
153
154 loop {
157 match self.update().await {
158 Ok(data) => {
159 println!("\nš Dashboard Update");
160 println!(" Memory: {:.1}% ({} MB / {} MB)",
161 data.metrics.memory_percent,
162 data.metrics.memory_used_mb,
163 data.metrics.memory_total_mb
164 );
165 println!(" Processes: {}", data.metrics.process_count);
166 println!(" Pattern: {} (confidence: {:.0}%)",
167 data.spectral.pattern_class,
168 data.spectral.confidence * 100.0
169 );
170 println!(" Clusters: {}", data.clusters.len());
171 println!(" Sketch events: {}", data.sketch.total_events);
172 }
173 Err(e) => {
174 eprintln!(" Error: {}", e);
175 }
176 }
177
178 tokio::time::sleep(std::time::Duration::from_secs(2)).await;
179 }
180 }
181}
182
183impl Default for DashboardServer {
184 fn default() -> Self {
185 Self::new()
186 }
187}
188
189#[cfg(test)]
190mod tests {
191 use super::*;
192
193 #[tokio::test]
194 async fn test_dashboard_server() {
195 let server = DashboardServer::new();
196 let result = server.update().await;
197 assert!(result.is_ok());
198
199 let data = result.unwrap();
200 assert!(data.metrics.memory_total_mb > 0);
201 }
202}