1use crate::{HardwareInfo, ThermalInfo, PowerProfile, Result, HardwareQueryError};
7use async_trait::async_trait;
8use serde::{Deserialize, Serialize};
9use std::sync::Arc;
10use std::time::{Duration, Instant};
11use tokio::sync::{broadcast, RwLock, Mutex};
12use tokio::time::interval;
13
14#[derive(Debug, Clone, Serialize, Deserialize)]
16pub struct MonitoringConfig {
17 pub update_interval: Duration,
19 pub enable_thermal: bool,
21 pub enable_power: bool,
23 pub enable_hardware: bool,
25 pub thermal_threshold: f32,
27 pub power_threshold: Option<f32>,
29 pub background_monitoring: bool,
31}
32
33impl Default for MonitoringConfig {
34 fn default() -> Self {
35 Self {
36 update_interval: Duration::from_secs(5),
37 enable_thermal: true,
38 enable_power: true,
39 enable_hardware: true,
40 thermal_threshold: 80.0,
41 power_threshold: None,
42 background_monitoring: true,
43 }
44 }
45}
46
47#[derive(Debug, Clone, Serialize, Deserialize)]
49pub enum MonitoringEvent {
50 ThermalAlert {
52 sensor_name: String,
53 temperature: f32,
54 threshold: f32,
55 timestamp: std::time::SystemTime,
56 },
57 PowerAlert {
59 current_power: f32,
60 threshold: f32,
61 timestamp: std::time::SystemTime,
62 },
63 HardwareChanged {
65 change_type: HardwareChangeType,
66 description: String,
67 timestamp: std::time::SystemTime,
68 },
69 MonitoringError {
71 error: String,
72 timestamp: std::time::SystemTime,
73 },
74 MetricsUpdate {
76 hardware_info: Option<HardwareInfo>,
77 thermal_info: Option<ThermalInfo>,
78 power_profile: Option<PowerProfile>,
79 timestamp: std::time::SystemTime,
80 },
81}
82
83#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
85pub enum HardwareChangeType {
86 DeviceConnected,
88 DeviceDisconnected,
90 DriverChanged,
92 ConfigurationChanged,
94 PerformanceStateChanged,
96}
97
98#[derive(Debug, Clone, Serialize, Deserialize)]
100pub struct MonitoringStats {
101 pub total_events: u64,
103 pub thermal_alerts: u64,
105 pub power_alerts: u64,
107 pub hardware_changes: u64,
109 pub errors: u64,
111 pub uptime: Duration,
113 pub last_update: std::time::SystemTime,
115 pub average_update_interval: Duration,
117}
118
119#[async_trait]
121pub trait MonitoringCallback: Send + Sync {
122 async fn on_event(&self, event: &MonitoringEvent);
123}
124
125pub struct ClosureCallback<F>
127where
128 F: Fn(&MonitoringEvent) + Send + Sync,
129{
130 callback: F,
131}
132
133impl<F> ClosureCallback<F>
134where
135 F: Fn(&MonitoringEvent) + Send + Sync,
136{
137 pub fn new(callback: F) -> Self {
138 Self { callback }
139 }
140}
141
142#[async_trait]
143impl<F> MonitoringCallback for ClosureCallback<F>
144where
145 F: Fn(&MonitoringEvent) + Send + Sync,
146{
147 async fn on_event(&self, event: &MonitoringEvent) {
148 (self.callback)(event);
149 }
150}
151
152pub struct HardwareMonitor {
154 config: MonitoringConfig,
155 callbacks: Arc<Mutex<Vec<Box<dyn MonitoringCallback>>>>,
156 event_sender: broadcast::Sender<MonitoringEvent>,
157 stats: Arc<RwLock<MonitoringStats>>,
158 running: Arc<RwLock<bool>>,
159 start_time: Instant,
160 last_hardware_info: Arc<RwLock<Option<HardwareInfo>>>,
161 last_thermal_info: Arc<RwLock<Option<ThermalInfo>>>,
162 last_power_profile: Arc<RwLock<Option<PowerProfile>>>,
163}
164
165impl HardwareMonitor {
166 pub fn new() -> Self {
168 Self::with_config(MonitoringConfig::default())
169 }
170
171 pub fn with_config(config: MonitoringConfig) -> Self {
173 let (event_sender, _) = broadcast::channel(1000);
174
175 Self {
176 config,
177 callbacks: Arc::new(Mutex::new(Vec::new())),
178 event_sender,
179 stats: Arc::new(RwLock::new(MonitoringStats {
180 total_events: 0,
181 thermal_alerts: 0,
182 power_alerts: 0,
183 hardware_changes: 0,
184 errors: 0,
185 uptime: Duration::from_secs(0),
186 last_update: std::time::SystemTime::now(),
187 average_update_interval: Duration::from_secs(0),
188 })),
189 running: Arc::new(RwLock::new(false)),
190 start_time: Instant::now(),
191 last_hardware_info: Arc::new(RwLock::new(None)),
192 last_thermal_info: Arc::new(RwLock::new(None)),
193 last_power_profile: Arc::new(RwLock::new(None)),
194 }
195 }
196
197 pub async fn add_callback<T: MonitoringCallback + 'static>(&self, callback: T) {
199 let mut callbacks = self.callbacks.lock().await;
200 callbacks.push(Box::new(callback));
201 }
202
203 pub async fn on_event<F>(&self, callback: F)
205 where
206 F: Fn(&MonitoringEvent) + Send + Sync + 'static,
207 {
208 self.add_callback(ClosureCallback::new(callback)).await;
209 }
210
211 pub async fn on_thermal_threshold<F>(&self, threshold: f32, _callback: F)
213 where
214 F: Fn(&ThermalInfo) + Send + Sync + 'static,
215 {
216 self.on_event(move |event| {
217 if let MonitoringEvent::ThermalAlert { temperature, .. } = event {
218 if *temperature >= threshold {
219 }
222 }
223 }).await;
224 }
225
226 pub async fn on_power_threshold<F>(&self, threshold: f32, _callback: F)
228 where
229 F: Fn(&PowerProfile) + Send + Sync + 'static,
230 {
231 self.on_event(move |event| {
232 if let MonitoringEvent::PowerAlert { current_power, .. } = event {
233 if *current_power >= threshold {
234 }
236 }
237 }).await;
238 }
239
240 pub fn subscribe(&self) -> broadcast::Receiver<MonitoringEvent> {
242 self.event_sender.subscribe()
243 }
244
245 pub async fn start_monitoring(&self) -> Result<()> {
247 {
248 let mut running = self.running.write().await;
249 if *running {
250 return Err(HardwareQueryError::InvalidConfiguration(
251 "Monitoring is already running".to_string()
252 ));
253 }
254 *running = true;
255 }
256
257 let config = self.config.clone();
258 let callbacks = Arc::clone(&self.callbacks);
259 let event_sender = self.event_sender.clone();
260 let stats = Arc::clone(&self.stats);
261 let running = Arc::clone(&self.running);
262 let last_hardware_info = Arc::clone(&self.last_hardware_info);
263 let last_thermal_info = Arc::clone(&self.last_thermal_info);
264 let last_power_profile = Arc::clone(&self.last_power_profile);
265
266 tokio::spawn(async move {
267 let mut interval = interval(config.update_interval);
268 let mut update_times = Vec::new();
269
270 while *running.read().await {
271 interval.tick().await;
272 let update_start = Instant::now();
273
274 let mut hardware_info = None;
276 let mut thermal_info = None;
277 let mut power_profile = None;
278 let mut events = Vec::new();
279
280 if config.enable_hardware {
281 match HardwareInfo::query() {
282 Ok(info) => {
283 hardware_info = Some(info);
284 }
285 Err(e) => {
286 events.push(MonitoringEvent::MonitoringError {
287 error: format!("Failed to query hardware info: {}", e),
288 timestamp: std::time::SystemTime::now(),
289 });
290 }
291 }
292 }
293
294 if config.enable_thermal {
295 match ThermalInfo::query() {
296 Ok(info) => {
297 for sensor in info.sensors() {
299 if sensor.temperature >= config.thermal_threshold {
300 events.push(MonitoringEvent::ThermalAlert {
301 sensor_name: sensor.name.clone(),
302 temperature: sensor.temperature,
303 threshold: config.thermal_threshold,
304 timestamp: std::time::SystemTime::now(),
305 });
306 }
307 }
308 thermal_info = Some(info);
309 }
310 Err(e) => {
311 events.push(MonitoringEvent::MonitoringError {
312 error: format!("Failed to query thermal info: {}", e),
313 timestamp: std::time::SystemTime::now(),
314 });
315 }
316 }
317 }
318
319 if config.enable_power {
320 match PowerProfile::query() {
321 Ok(profile) => {
322 if let (Some(current_power), Some(threshold)) =
324 (profile.total_power_draw, config.power_threshold) {
325 if current_power >= threshold {
326 events.push(MonitoringEvent::PowerAlert {
327 current_power,
328 threshold,
329 timestamp: std::time::SystemTime::now(),
330 });
331 }
332 }
333 power_profile = Some(profile);
334 }
335 Err(e) => {
336 events.push(MonitoringEvent::MonitoringError {
337 error: format!("Failed to query power profile: {}", e),
338 timestamp: std::time::SystemTime::now(),
339 });
340 }
341 }
342 }
343
344 events.push(MonitoringEvent::MetricsUpdate {
346 hardware_info: hardware_info.clone(),
347 thermal_info: thermal_info.clone(),
348 power_profile: power_profile.clone(),
349 timestamp: std::time::SystemTime::now(),
350 });
351
352 if let Some(info) = hardware_info {
354 *last_hardware_info.write().await = Some(info);
355 }
356 if let Some(info) = thermal_info {
357 *last_thermal_info.write().await = Some(info);
358 }
359 if let Some(profile) = power_profile {
360 *last_power_profile.write().await = Some(profile);
361 }
362
363 for event in &events {
365 let _ = event_sender.send(event.clone());
367
368 let callbacks = callbacks.lock().await;
370 for callback in callbacks.iter() {
371 callback.on_event(event).await;
372 }
373 }
374
375 {
377 let mut stats = stats.write().await;
378 stats.total_events += events.len() as u64;
379
380 for event in &events {
381 match event {
382 MonitoringEvent::ThermalAlert { .. } => stats.thermal_alerts += 1,
383 MonitoringEvent::PowerAlert { .. } => stats.power_alerts += 1,
384 MonitoringEvent::HardwareChanged { .. } => stats.hardware_changes += 1,
385 MonitoringEvent::MonitoringError { .. } => stats.errors += 1,
386 _ => {}
387 }
388 }
389
390 stats.last_update = std::time::SystemTime::now();
391 let update_duration = update_start.elapsed();
392 update_times.push(update_duration);
393
394 if update_times.len() > 100 {
396 update_times.remove(0);
397 }
398
399 if !update_times.is_empty() {
400 let total_time: Duration = update_times.iter().sum();
401 stats.average_update_interval = total_time / update_times.len() as u32;
402 }
403 }
404 }
405 });
406
407 Ok(())
408 }
409
410 pub async fn stop_monitoring(&self) {
412 *self.running.write().await = false;
413 }
414
415 pub async fn is_monitoring(&self) -> bool {
417 *self.running.read().await
418 }
419
420 pub async fn get_stats(&self) -> MonitoringStats {
422 let mut stats = self.stats.read().await.clone();
423 stats.uptime = self.start_time.elapsed();
424 stats
425 }
426
427 pub async fn get_last_hardware_info(&self) -> Option<HardwareInfo> {
429 self.last_hardware_info.read().await.clone()
430 }
431
432 pub async fn get_last_thermal_info(&self) -> Option<ThermalInfo> {
434 self.last_thermal_info.read().await.clone()
435 }
436
437 pub async fn get_last_power_profile(&self) -> Option<PowerProfile> {
439 self.last_power_profile.read().await.clone()
440 }
441
442 pub async fn update_config(&mut self, new_config: MonitoringConfig) {
444 self.config = new_config;
445 }
446
447 pub async fn clear_callbacks(&self) {
449 let mut callbacks = self.callbacks.lock().await;
450 callbacks.clear();
451 }
452}
453
454impl Default for HardwareMonitor {
455 fn default() -> Self {
456 Self::new()
457 }
458}