ruvector_memopt/dashboard/
server.rs

1//! Simple HTTP server for dashboard data
2//!
3//! Serves JSON API for dashboard frontend
4
5use 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
16/// Dashboard server state
17pub 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    /// Update all data and return dashboard state
39    pub async fn update(&self) -> Result<DashboardData, String> {
40        // Refresh system info
41        let mut system = self.system.write().await;
42        system.refresh_all();
43
44        // Get memory status
45        let status = WindowsMemoryOptimizer::get_memory_status()?;
46
47        // Update spectral analyzer
48        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        // Update sketch
66        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![], // Would need pressure tracker for this
76            }
77        };
78
79        // Update MinCut and PageRank
80        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        // Build metrics
118        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        // Record sample
131        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    /// Record an optimization result
138    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    /// Get JSON data
144    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    /// Run HTTP server (simplified - prints to console)
150    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        // In a real implementation, this would use a web framework like axum or warp
155        // For now, we'll just print updates periodically
156        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}