Skip to main content

ri/device/
controller.rs

1//! Copyright © 2025-2026 Wenze Wei. All Rights Reserved.
2//!
3//! This file is part of Ri.
4//! The Ri project belongs to the Dunimd Team.
5//!
6//! Licensed under the Apache License, Version 2.0 (the "License");
7//! You may not use this file except in compliance with the License.
8//! You may obtain a copy of the License at
9//!
10//!     http://www.apache.org/licenses/LICENSE-2.0
11//!
12//! Unless required by applicable law or agreed to in writing, software
13//! distributed under the License is distributed on an "AS IS" BASIS,
14//! WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15//! See the License for the specific language governing permissions and
16//! limitations under the License.
17
18#![allow(non_snake_case)]
19
20//! # Device Controller
21//!
22//! This file implements the device controller for the Ri framework, responsible for managing the
23//! lifecycle and state of devices in the system. It provides functionality for device discovery,
24//! allocation, health monitoring, and state management.
25//!
26//! ## Key Components
27//!
28//! - **RiDeviceController**: Main device controller struct
29//! - **Device Discovery**: Scans the system/network for devices
30//! - **Device Allocation**: Manages device allocation and deallocation
31//! - **Health Monitoring**: Performs periodic health checks on devices
32//! - **Device State Management**: Tracks device status and capabilities
33//!
34//! ## Design Principles
35//!
36//! 1. **Centralized Management**: Single point of control for all devices
37//! 2. **Async-First**: All device operations are asynchronous
38//! 3. **Thread Safety**: Uses Arc and RwLock for safe concurrent access
39//! 4. **Indexing**: Maintains indexes for efficient device lookup by type
40//! 5. **Health Monitoring**: Periodic health checks to ensure device reliability
41//! 6. **Mock Support**: Built-in mock device generation for testing and demonstration
42//! 7. **State Tracking**: Tracks device allocation and status changes
43//! 8. **Scalability**: Efficiently handles large numbers of devices
44//! 9. **Fault Tolerance**: Handles device failures gracefully
45//! 10. **Resource Optimization**: Scores devices to find the best fit for requirements
46//!
47//! ## Usage
48//!
49//! ```rust
50//! use ri::device::{RiDeviceController, RiDeviceType, RiDeviceCapabilities};
51//! use ri::core::RiResult;
52//!
53//! async fn example() -> RiResult<()> {
54//!     // Create a new device controller
55//!     let mut controller = RiDeviceController::new();
56//!     
57//!     // Add mock devices for testing
58//!     controller.add_mock_devices()?;
59//!     
60//!     // Discover devices in the system
61//!     let discovery_result = controller.discover_devices().await?;
62//!     println!("Discovered {} devices", discovery_result.total_devices);
63//!     
64//!     // Find a suitable CPU device
65//!     let requirements = RiDeviceCapabilities::new()
66//!         .with_compute_units(8)
67//!         .with_memory_gb(16.0);
68//!     
69//!     if let Some(device) = controller.find_suitable_device(&RiDeviceType::CPU, &requirements).await? {
70//!         println!("Found suitable device: {}", device.id());
71//!         
72//!         // Allocate the device
73//!         controller.allocate_device(device.id(), "allocation-1").await?;
74//!         println!("Allocated device: {}", device.id());
75//!         
76//!         // Release the device
77//!         controller.release_device_by_allocation("allocation-1").await?;
78//!         println!("Released device: {}", device.id());
79//!     }
80//!     
81//!     // Perform health checks
82//!     let health_results = controller.perform_health_checks().await?;
83//!     for (device_id, health_score) in health_results {
84//!         println!("Device {} health score: {}", device_id, health_score);
85//!     }
86//!     
87//!     Ok(())
88//! }
89//! ```
90
91use chrono::Utc;
92use std::collections::HashMap as FxHashMap;
93use std::sync::Arc;
94use tokio::sync::RwLock;
95
96#[cfg(feature = "pyo3")]
97use pyo3::prelude::*;
98
99#[cfg(feature = "pyo3")]
100use pyo3::PyResult;
101
102use super::core::{RiDevice, RiDeviceCapabilities, RiDeviceStatus, RiDeviceType, RiDeviceControlConfig, RiNetworkDeviceInfo};
103use super::discovery::{RiDeviceDiscovery, DiscoveryConfig};
104use crate::core::RiResult;
105use crate::prelude::RiMetricsRegistry;
106#[cfg(not(target_os = "macos"))]
107use crate::prelude::RiError;
108// use super::scheduler::RiDeviceScheduler;
109
110/// Device controller - manages device lifecycle and state
111#[cfg_attr(feature = "pyo3", pyo3::prelude::pyclass)]
112pub struct RiDeviceController {
113    devices: FxHashMap<String, Arc<RwLock<RiDevice>>>,
114    device_type_index: FxHashMap<RiDeviceType, Vec<String>>,
115    allocation_map: FxHashMap<String, String>,
116    discovery: Option<Arc<RiDeviceDiscovery>>,
117}
118
119#[cfg(feature = "pyo3")]
120#[pymethods]
121impl RiDeviceController {
122    #[new]
123    fn py_new() -> Self {
124        Self::new()
125    }
126    
127    #[staticmethod]
128    fn default_controller() -> Self {
129        Self::default()
130    }
131    
132    #[pyo3(name = "discover_devices")]
133    fn discover_devices_impl(&mut self) -> PyResult<super::RiDiscoveryResult> {
134        let rt = crate::py_runtime::py_runtime();
135        
136        rt.block_on(self.discover_devices()).map_err(|e| {
137            pyo3::exceptions::PyRuntimeError::new_err(format!("Device discovery failed: {}", e))
138        })
139    }
140    
141    #[pyo3(name = "discover_system_devices")]
142    fn discover_system_devices_impl(&mut self, config: &RiDeviceControlConfig) -> PyResult<()> {
143        let rt = crate::py_runtime::py_runtime();
144        
145        rt.block_on(self.discover_system_devices(config)).map_err(|e| {
146            pyo3::exceptions::PyRuntimeError::new_err(format!("System device discovery failed: {}", e))
147        })
148    }
149    
150    #[pyo3(name = "find_suitable_device")]
151    fn find_suitable_device_impl(&self, device_type: &RiDeviceType, requirements: &RiDeviceCapabilities) -> PyResult<Option<RiDevice>> {
152        let rt = crate::py_runtime::py_runtime();
153        
154        rt.block_on(self.find_suitable_device(device_type, requirements)).map_err(|e| {
155            pyo3::exceptions::PyRuntimeError::new_err(format!("Failed to find suitable device: {}", e))
156        })
157    }
158    
159    #[pyo3(name = "allocate_device")]
160    fn allocate_device_impl(&mut self, device_id: &str, allocation_id: &str) -> PyResult<()> {
161        let rt = crate::py_runtime::py_runtime();
162        
163        rt.block_on(self.allocate_device(device_id, allocation_id)).map_err(|e| {
164            pyo3::exceptions::PyRuntimeError::new_err(format!("Device allocation failed: {}", e))
165        })
166    }
167    
168    #[pyo3(name = "release_device_by_allocation")]
169    fn release_device_by_allocation_impl(&mut self, allocation_id: &str) -> PyResult<()> {
170        let rt = crate::py_runtime::py_runtime();
171        
172        rt.block_on(self.release_device_by_allocation(allocation_id)).map_err(|e| {
173            pyo3::exceptions::PyRuntimeError::new_err(format!("Device release failed: {}", e))
174        })
175    }
176    
177    #[pyo3(name = "get_all_devices")]
178    fn get_all_devices_impl(&self) -> Vec<RiDevice> {
179        self.get_all_devices()
180    }
181    
182    #[pyo3(name = "release_all_devices")]
183    fn release_all_devices_impl(&mut self) -> PyResult<()> {
184        self.release_all_devices().map_err(|e| {
185            pyo3::exceptions::PyRuntimeError::new_err(format!("Failed to release all devices: {}", e))
186        })
187    }
188    
189    #[pyo3(name = "perform_health_checks")]
190    fn perform_health_checks_impl(&mut self) -> PyResult<Vec<(String, u8)>> {
191        let rt = crate::py_runtime::py_runtime();
192        
193        rt.block_on(self.perform_health_checks()).map_err(|e| {
194            pyo3::exceptions::PyRuntimeError::new_err(format!("Health checks failed: {}", e))
195        })
196    }
197    
198    #[pyo3(name = "get_device_health")]
199    fn get_device_health_impl(&self, device_id: &str) -> PyResult<super::core::RiDeviceHealthMetrics> {
200        let rt = crate::py_runtime::py_runtime();
201        
202        rt.block_on(self.get_device_health(device_id)).map_err(|e| {
203            pyo3::exceptions::PyRuntimeError::new_err(format!("Failed to get device health: {}", e))
204        })
205    }
206    
207    #[pyo3(name = "get_all_device_health")]
208    fn get_all_device_health_impl(&self) -> PyResult<FxHashMap<String, super::core::RiDeviceHealthMetrics>> {
209        let rt = crate::py_runtime::py_runtime();
210        
211        rt.block_on(self.get_all_device_health()).map_err(|e| {
212            pyo3::exceptions::PyRuntimeError::new_err(format!("Failed to get all device health: {}", e))
213        })
214    }
215    
216    #[pyo3(name = "device_count")]
217    fn device_count_impl(&self) -> usize {
218        self.devices.len()
219    }
220    
221    #[pyo3(name = "get_devices_by_type")]
222    fn get_devices_by_type_impl(&self, device_type: &RiDeviceType) -> Vec<RiDevice> {
223        let device_ids = match self.device_type_index.get(device_type) {
224            Some(ids) => ids.clone(),
225            None => return Vec::new(),
226        };
227        
228        let mut devices = Vec::with_capacity(4);
229        for device_id in device_ids {
230            if let Some(device_lock) = self.devices.get(&device_id) {
231                if let Ok(device) = device_lock.try_read() {
232                    devices.push(device.clone());
233                }
234            }
235        }
236        devices
237    }
238    
239    #[pyo3(name = "start_health_checks")]
240    fn start_health_checks_impl(&self, interval_secs: u64) -> PyResult<String> {
241        let _handle = self.start_health_checks(interval_secs);
242        Ok(format!("Health check task started with interval {} seconds", interval_secs))
243    }
244}
245
246impl Default for RiDeviceController {
247    fn default() -> Self {
248        Self::new()
249    }
250}
251
252impl RiDeviceController {
253    pub fn new() -> Self {
254        Self {
255            devices: FxHashMap::default(),
256            device_type_index: FxHashMap::default(),
257            allocation_map: FxHashMap::default(),
258            discovery: None,
259        }
260    }
261
262    /// Creates a new controller with the discovery engine
263    pub async fn with_discovery(discovery: Arc<RiDeviceDiscovery>) -> Self {
264        Self {
265            devices: FxHashMap::default(),
266            device_type_index: FxHashMap::default(),
267            allocation_map: FxHashMap::default(),
268            discovery: Some(discovery),
269        }
270    }
271
272    /// Initializes the discovery engine
273    pub async fn init_discovery(&mut self) -> RiResult<()> {
274        let config = DiscoveryConfig::default();
275        let discovery = Arc::new(RiDeviceDiscovery::new(config).await?);
276        self.discovery = Some(discovery);
277        Ok(())
278    }
279    
280    /// Discover devices in the system
281    pub async fn discover_devices(&mut self) -> RiResult<super::RiDiscoveryResult> {
282        // In a real implementation, this would scan the system/network for devices
283        // For now, we'll simulate discovery and update existing mock devices
284        
285        // Retry mechanism for device discovery
286        let max_retries = 3;
287        let retry_delay = std::time::Duration::from_millis(500);
288        
289        for attempt in 0..max_retries {
290            match self.perform_device_discovery().await {
291                Ok(result) => return Ok(result),
292                Err(e) => {
293                    if attempt == max_retries - 1 {
294                        // Last attempt failed, return error
295                        return Err(e);
296                    }
297                    
298                    // Log retry attempt
299                    let error_msg = format!("Device discovery attempt {} failed: {}, retrying in {}ms", 
300                                          attempt + 1, e, retry_delay.as_millis());
301                    log::warn!("{error_msg}");
302                    
303                    // Wait before retrying
304                    tokio::time::sleep(retry_delay).await;
305                }
306            }
307        }
308        
309        // This line should never be reached due to the retry loop
310        Err(crate::core::RiError::Other("Device discovery failed after maximum retries".to_string()))
311    }
312    
313    /// Performs the actual device discovery logic with proper error handling.
314    /// 
315    /// # Returns
316    /// 
317    /// A `RiResult<super::RiDiscoveryResult>` containing the discovery result if successful.
318    async fn perform_device_discovery(&mut self) -> RiResult<super::RiDiscoveryResult> {
319        let mut discovered_devices = Vec::with_capacity(4);
320        let mut updated_devices = Vec::with_capacity(4);
321        let mut removed_devices = Vec::with_capacity(4);
322
323        // Update existing devices with error handling
324        for device_lock in self.devices.values() {
325            match device_lock.try_write() {
326                Ok(mut device) => {
327                    device.update_last_seen();
328
329                    // Improved device status detection based on health metrics
330                    let health_metrics = device.health_metrics().clone();
331                    let device_type = device.device_type();
332                    
333                    // Realistic device status update based on health metrics
334                    match device_type {
335                        RiDeviceType::CPU => {
336                            // CPU devices are affected by high CPU usage and temperature
337                            if health_metrics.cpu_usage_percent > 95.0 || health_metrics.temperature_celsius > 90.0 {
338                                device.set_status(RiDeviceStatus::Degraded);
339                            } else if health_metrics.cpu_usage_percent > 80.0 || health_metrics.temperature_celsius > 80.0 {
340                                device.set_status(RiDeviceStatus::Busy);
341                            } else if device.status() != RiDeviceStatus::Allocated {
342                                device.set_status(RiDeviceStatus::Available);
343                            }
344                        },
345                        RiDeviceType::GPU => {
346                            // GPU devices are affected by high usage, temperature, and memory usage
347                            if health_metrics.cpu_usage_percent > 95.0 || health_metrics.temperature_celsius > 95.0 {
348                                device.set_status(RiDeviceStatus::Degraded);
349                            } else if health_metrics.cpu_usage_percent > 85.0 || health_metrics.temperature_celsius > 85.0 {
350                                device.set_status(RiDeviceStatus::Busy);
351                            } else if device.status() != RiDeviceStatus::Allocated {
352                                device.set_status(RiDeviceStatus::Available);
353                            }
354                        },
355                        RiDeviceType::Network => {
356                            // Network devices are affected by high latency
357                            if health_metrics.network_latency_ms > 200.0 {
358                                device.set_status(RiDeviceStatus::Degraded);
359                            } else if health_metrics.network_latency_ms > 100.0 {
360                                device.set_status(RiDeviceStatus::Busy);
361                            } else if device.status() != RiDeviceStatus::Allocated {
362                                device.set_status(RiDeviceStatus::Available);
363                            }
364                        },
365                        RiDeviceType::Storage => {
366                            // Storage devices are affected by high response time
367                            if health_metrics.response_time_ms > 100.0 {
368                                device.set_status(RiDeviceStatus::Degraded);
369                            } else if health_metrics.response_time_ms > 50.0 {
370                                device.set_status(RiDeviceStatus::Busy);
371                            } else if device.status() != RiDeviceStatus::Allocated {
372                                device.set_status(RiDeviceStatus::Available);
373                            }
374                        },
375                        _ => {
376                            // Default status update for other device types
377                            if health_metrics.error_count > 5 {
378                                device.set_status(RiDeviceStatus::Degraded);
379                            } else if device.status() != RiDeviceStatus::Allocated {
380                                device.set_status(RiDeviceStatus::Available);
381                            }
382                        }
383                    }
384
385                    updated_devices.push(device.clone());
386                },
387                Err(_) => {
388                    // Failed to acquire write lock, skip this device for now
389                    continue;
390                }
391            }
392        }
393
394        // Discover real hardware devices
395        let new_hardware_devices = self.discover_hardware_devices().await?;
396        
397        // Add discovered hardware devices
398        for device in new_hardware_devices {
399            let device_id = device.id().to_string();
400            
401            // Check if device already exists
402            if !self.devices.contains_key(&device_id) {
403                self.devices.insert(device_id.clone(), Arc::new(RwLock::new(device.clone())));
404                self.device_type_index
405                    .entry(device.device_type())
406                    .or_default()
407                    .push(device_id);
408                
409                discovered_devices.push(device);
410            }
411        }
412        
413        // Occasionally add new mock devices for testing and demonstration
414        if rand::random::<f64>() < 0.05 {
415            // 5% chance
416            let new_device = self.create_mock_device_for_discovery();
417            let device_id = new_device.id().to_string();
418
419            self.devices
420                .insert(device_id.clone(), Arc::new(RwLock::new(new_device.clone())));
421            self.device_type_index
422                .entry(new_device.device_type())
423                .or_default()
424                .push(device_id);
425
426            discovered_devices.push(new_device);
427        }
428
429        // Remove devices that haven't been seen for a while
430        let timeout = chrono::TimeDelta::minutes(5);
431        let now = Utc::now();
432
433        let mut to_remove = Vec::with_capacity(4);
434        for (device_id, device_lock) in &self.devices {
435            match device_lock.try_read() {
436                Ok(device) => {
437                    if now.signed_duration_since(device.last_seen()) > timeout {
438                        to_remove.push(device_id.clone());
439                    }
440                },
441                Err(_) => {
442                    // Failed to acquire read lock, skip this device for now
443                    continue;
444                }
445            }
446        }
447
448        for device_id in &to_remove {
449            self.remove_device(device_id).await?;
450            removed_devices.push(device_id.to_string());
451        }
452
453        Ok(super::RiDiscoveryResult {
454            discovered_devices,
455            updated_devices,
456            removed_devices,
457            total_devices: self.devices.len(),
458        })
459    }
460
461    /// Discover real system devices based on configuration
462    pub async fn discover_system_devices(&mut self, config: &RiDeviceControlConfig) -> RiResult<()> {
463        // Discover CPU devices
464        self.discover_cpu_devices(config).await?;
465        
466        // Discover GPU devices
467        self.discover_gpu_devices(config).await?;
468        
469        // Discover memory devices
470        self.discover_memory_devices(config).await?;
471        
472        // Discover storage devices
473        self.discover_storage_devices(config).await?;
474        
475        // Discover network devices
476        self.discover_network_devices(config).await?;
477        
478        Ok(())
479    }
480    
481    /// Discover GPU devices from system
482    async fn discover_gpu_devices(&mut self, _config: &RiDeviceControlConfig) -> RiResult<()> {
483        #[cfg(target_os = "windows")]
484        {
485            // Try NVIDIA GPU discovery first
486            if let Ok(nvidia_output) = std::process::Command::new("nvidia-smi")
487                .args(["--query-gpu=name,memory.total", "--format=csv,noheader"])
488                .output() 
489            {
490                let gpu_info = String::from_utf8_lossy(&nvidia_output.stdout);
491                
492                for (index, line) in gpu_info.lines().enumerate() {
493                    let parts: Vec<&str> = line.split(',').collect();
494                    if parts.len() >= 2 {
495                        let name = parts[0].trim();
496                        let memory_mb = parts[1].trim().replace(" MiB", "").parse::<f64>().unwrap_or(0.0);
497                        let memory_gb = memory_mb / 1024.0;
498                        
499                        let gpu_device = RiDevice::new(
500                            format!("GPU-{}-{}", index + 1, name), 
501                            RiDeviceType::GPU
502                        ).with_capabilities(
503                            RiDeviceCapabilities::new()
504                                .with_compute_units(1000) // Estimate
505                                .with_memory_gb(memory_gb)
506                        );
507                        
508                        self.add_device(gpu_device, "NVIDIA GPU".to_string()).await?;
509                    }
510                }
511            }
512        }
513        
514        #[cfg(target_os = "linux")]
515        {
516            // Try NVIDIA GPU discovery
517            if let Ok(nvidia_output) = std::process::Command::new("nvidia-smi")
518                .args(&["--query-gpu=name,memory.total", "--format=csv,noheader"])
519                .output()
520            {
521                let gpu_info = String::from_utf8_lossy(&nvidia_output.stdout);
522                
523                for (index, line) in gpu_info.lines().enumerate() {
524                    let parts: Vec<&str> = line.split(',').collect();
525                    if parts.len() >= 2 {
526                        let name = parts[0].trim();
527                        let memory_mb = parts[1].trim().replace(" MiB", "").parse::<f64>().unwrap_or(0.0);
528                        let memory_gb = memory_mb / 1024.0;
529                        
530                        let gpu_device = RiDevice::new(
531                            format!("GPU-{}-{}", index + 1, name), 
532                            RiDeviceType::GPU
533                        ).with_capabilities(
534                            RiDeviceCapabilities::new()
535                                .with_compute_units(1000) // Estimate
536                                .with_memory_gb(memory_gb)
537                        );
538                        
539                        self.add_device(gpu_device, "NVIDIA GPU".to_string()).await?;
540                    }
541                }
542            }
543        }
544        
545        Ok(())
546    }
547    
548    /// Discover storage devices from system
549    #[allow(dead_code)]
550    async fn discover_storage_devices_impl(&mut self, _config: &RiDeviceControlConfig) -> RiResult<Vec<RiDevice>> {
551        #[cfg(target_os = "windows")]
552        {
553            // Try NVIDIA GPU discovery first
554            if let Ok(nvidia_output) = std::process::Command::new("nvidia-smi")
555                .args(["--query-gpu=name,memory.total", "--format=csv,noheader"])
556                .output() 
557            {
558                let gpu_info = String::from_utf8_lossy(&nvidia_output.stdout);
559                
560                for (index, line) in gpu_info.lines().enumerate() {
561                    let parts: Vec<&str> = line.split(',').collect();
562                    if parts.len() >= 2 {
563                        let name = parts[0].trim();
564                        let memory_mb = parts[1].trim().replace(" MiB", "").parse::<f64>().unwrap_or(0.0);
565                        let memory_gb = memory_mb / 1024.0;
566                        
567                        let gpu_device = RiDevice::new(
568                            format!("GPU-{}-{}", index + 1, name), 
569                            RiDeviceType::GPU
570                        ).with_capabilities(
571                            RiDeviceCapabilities::new()
572                                .with_compute_units(1000) // Estimate
573                                .with_memory_gb(memory_gb)
574                        );
575                        
576                        self.add_device(gpu_device, "NVIDIA GPU".to_string()).await?;
577                    }
578                }
579            }
580        }
581        
582        #[cfg(target_os = "linux")]
583        {
584            // Try NVIDIA GPU discovery
585            if let Ok(nvidia_output) = std::process::Command::new("nvidia-smi")
586                .args(&["--query-gpu=name,memory.total", "--format=csv,noheader"])
587                .output()
588            {
589                let gpu_info = String::from_utf8_lossy(&nvidia_output.stdout);
590                
591                for (index, line) in gpu_info.lines().enumerate() {
592                    let parts: Vec<&str> = line.split(',').collect();
593                    if parts.len() >= 2 {
594                        let name = parts[0].trim();
595                        let memory_mb = parts[1].trim().replace(" MiB", "").parse::<f64>().unwrap_or(0.0);
596                        let memory_gb = memory_mb / 1024.0;
597                        
598                        let gpu_device = RiDevice::new(
599                            format!("GPU-{}-{}", index + 1, name), 
600                            RiDeviceType::GPU
601                        ).with_capabilities(
602                            RiDeviceCapabilities::new()
603                                .with_compute_units(1000) // Estimate
604                                .with_memory_gb(memory_gb)
605                        );
606                        
607                        self.add_device(gpu_device, "NVIDIA GPU".to_string()).await?;
608                    }
609                }
610            }
611        }
612        
613        Ok(vec![])
614    }
615    
616    /// Discover memory devices from system
617    async fn discover_memory_devices(&mut self, _config: &RiDeviceControlConfig) -> RiResult<()> {
618        #[cfg(target_os = "windows")]
619        {
620            let output = std::process::Command::new("powershell")
621                .args(["-NoProfile", "-Command", "Get-CimInstance Win32_PhysicalMemory | ForEach-Object { 'Capacity=' + $_.Capacity; 'Speed=' + $_.Speed }"])
622                .output()
623                .map_err(|e| RiError::DeviceError(format!("Failed to query memory info: {e}")))?;
624                
625            let memory_info = String::from_utf8_lossy(&output.stdout);
626            
627            let mut total_capacity_gb = 0.0;
628            let mut memory_modules = 0;
629            
630            for line in memory_info.lines() {
631                if line.starts_with("Capacity=") {
632                    if let Some(capacity_bytes) = line.split('=').nth(1).and_then(|s| s.trim().parse::<u64>().ok()) {
633                        total_capacity_gb += capacity_bytes as f64 / (1024.0 * 1024.0 * 1024.0);
634                        memory_modules += 1;
635                    }
636                }
637            }
638            
639            if memory_modules > 0 {
640                let memory_device = RiDevice::new(
641                    format!("Memory-{}GB-total", total_capacity_gb.round() as u32), 
642                    RiDeviceType::Memory
643                ).with_capabilities(
644                    RiDeviceCapabilities::new()
645                        .with_memory_gb(total_capacity_gb)
646                        .with_bandwidth_gbps(25.6) // Estimate for DDR4
647                );
648                
649                self.add_device(memory_device, "System Memory".to_string()).await?;
650            }
651        }
652        
653        #[cfg(target_os = "linux")]
654        {
655            if let Ok(meminfo) = std::fs::read_to_string("/proc/meminfo") {
656                for line in meminfo.lines() {
657                    if line.starts_with("MemTotal:") {
658                        if let Some(kb_str) = line.split_whitespace().nth(1) {
659                            if let Ok(kb) = kb_str.parse::<f64>() {
660                                let total_gb = kb / (1024.0 * 1024.0);
661                                
662                                let memory_device = RiDevice::new(
663                                    format!("Memory-{}GB-total", total_gb.round() as u32), 
664                                    RiDeviceType::Memory
665                                ).with_capabilities(
666                                    RiDeviceCapabilities::new()
667                                        .with_memory_gb(total_gb)
668                                        .with_bandwidth_gbps(25.6) // Estimate for DDR4
669                                );
670                                
671                                self.add_device(memory_device, "System Memory".to_string()).await?;
672                                break;
673                            }
674                        }
675                    }
676                }
677            }
678        }
679        
680        Ok(())
681    }
682    
683    /// Discover CPU devices from system
684    async fn discover_cpu_devices(&mut self, _config: &RiDeviceControlConfig) -> RiResult<()> {
685        #[cfg(target_os = "windows")]
686        {
687            let output = std::process::Command::new("powershell")
688                .args(["-NoProfile", "-Command", "Get-CimInstance Win32_Processor | ForEach-Object { 'Name=' + $_.Name; 'NumberOfCores=' + $_.NumberOfCores; 'NumberOfLogicalProcessors=' + $_.NumberOfLogicalProcessors }"])
689                .output()
690                .map_err(|e| RiError::DeviceError(format!("Failed to query CPU info: {e}")))?;
691                
692            let cpu_info = String::from_utf8_lossy(&output.stdout);
693            
694            // Query total physical memory (GB) so CPU devices advertise the
695            // host RAM they can actually address, instead of a hardcoded 0.0
696            // that fails any memory_gb requirement check.
697            let total_memory_gb: f64 = std::process::Command::new("powershell")
698                .args(["-NoProfile", "-Command", "[math]::Round((Get-CimInstance Win32_ComputerSystem).TotalPhysicalMemory / 1GB, 1)"])
699                .output()
700                .ok()
701                .and_then(|o| String::from_utf8_lossy(&o.stdout).trim().parse::<f64>().ok())
702                .unwrap_or(0.0);
703            
704            let mut cpu_count = 0;
705            let mut total_cores = 0;
706            let mut total_threads = 0;
707            
708            for line in cpu_info.lines() {
709                if line.starts_with("Name=") {
710                    cpu_count += 1;
711                } else if line.starts_with("NumberOfCores=") {
712                    if let Some(cores) = line.split('=').nth(1).and_then(|s| s.trim().parse::<usize>().ok()) {
713                        total_cores += cores;
714                    }
715                } else if line.starts_with("NumberOfLogicalProcessors=") {
716                    if let Some(threads) = line.split('=').nth(1).and_then(|s| s.trim().parse::<usize>().ok()) {
717                        total_threads += threads;
718                    }
719                }
720            }
721            
722            if cpu_count > 0 {
723                let cpu_device = RiDevice::new(
724                    format!("CPU-{total_cores}-cores-{total_threads}-threads"), 
725                    RiDeviceType::CPU
726                ).with_capabilities(
727                    RiDeviceCapabilities::new()
728                        .with_compute_units(total_cores)
729                        .with_memory_gb(total_memory_gb)
730                );
731                
732                self.add_device(cpu_device, "System Hardware".to_string()).await?;
733            }
734        }
735        
736        #[cfg(target_os = "linux")]
737        {
738            // Linux CPU discovery using /proc/cpuinfo
739            let cpu_info = std::fs::read_to_string("/proc/cpuinfo")
740                .map_err(|e| RiError::DeviceError(format!("Failed to read cpuinfo: {}", e)))?;
741                
742            let mut cpu_count = 0;
743            let mut total_cores = 0;
744            
745            for line in cpu_info.lines() {
746                if line.starts_with("processor\t") {
747                    cpu_count += 1;
748                } else if line.starts_with("cpu cores\t") {
749                    if let Some(cores) = line.split(':').nth(1).and_then(|s| s.trim().parse::<usize>().ok()) {
750                        total_cores = cores;
751                    }
752                }
753            }
754            
755            let total_threads = cpu_count; // In Linux, processor count equals thread count
756            
757            // Total physical memory from /proc/meminfo (MemTotal is in kB)
758            let total_memory_gb: f64 = std::fs::read_to_string("/proc/meminfo")
759                .ok()
760                .and_then(|mem| {
761                    mem.lines().find(|l| l.starts_with("MemTotal:")).and_then(|l| {
762                        l.split_whitespace().nth(1)
763                            .and_then(|kb| kb.parse::<f64>().ok())
764                            .map(|kb| (kb / 1024.0 / 1024.0 * 10.0).round() / 10.0)
765                    })
766                })
767                .unwrap_or(0.0);
768            
769            if cpu_count > 0 {
770                let cpu_device = RiDevice::new(
771                    format!("CPU-{}-cores-{}-threads", total_cores, total_threads), 
772                    RiDeviceType::CPU
773                ).with_capabilities(
774                    RiDeviceCapabilities::new()
775                        .with_compute_units(total_cores)
776                        .with_memory_gb(total_memory_gb)
777                );
778                
779                self.add_device(cpu_device, "System Hardware".to_string()).await?;
780            }
781        }
782        
783        Ok(())
784    }
785    
786    /// Discover storage devices from system
787    async fn discover_storage_devices(&mut self, config: &RiDeviceControlConfig) -> RiResult<()> {
788        // Call the implementation
789        self.discover_storage_devices_impl2(config).await
790    }
791    
792    /// Discover storage devices from system (implementation)
793    async fn discover_storage_devices_impl2(&mut self, _config: &RiDeviceControlConfig) -> RiResult<()> {
794        #[cfg(target_os = "windows")]
795        {
796            let output = std::process::Command::new("powershell")
797                .args(["-NoProfile", "-Command", "Get-CimInstance Win32_DiskDrive | ForEach-Object { 'Model=' + $_.Model; 'Size=' + $_.Size }"])
798                .output()
799                .map_err(|e| RiError::DeviceError(format!("Failed to query disk info: {e}")))?;
800                
801            let disk_info = String::from_utf8_lossy(&output.stdout);
802            
803            let mut disk_counter = 0;
804            // Store lines in a vector for easier access
805            let lines: Vec<&str> = disk_info.lines().collect();
806            
807            for (disk_index, line) in lines.iter().enumerate() {
808                if line.starts_with("Model=") {
809                    let model = line.split('=').nth(1).unwrap_or("Unknown").trim();
810                    disk_counter += 1;
811                    
812                    // Look for the size in the next line
813                    if disk_index + 1 < lines.len() && lines[disk_index + 1].starts_with("Size=") {
814                        let size_line = lines[disk_index + 1];
815                        if let Some(size_bytes) = size_line.split('=').nth(1).and_then(|s| s.trim().parse::<u64>().ok()) {
816                            let size_gb = size_bytes as f64 / (1024.0 * 1024.0 * 1024.0);
817                            
818                            let storage_device = RiDevice::new(
819                                format!("Storage-{disk_counter}-{model}"), 
820                                RiDeviceType::Storage
821                            ).with_capabilities(
822                                RiDeviceCapabilities::new()
823                                    .with_storage_gb(size_gb)
824                                    .with_bandwidth_gbps(6.0) // SATA III estimate
825                            );
826                            
827                            self.add_device(storage_device, "System Storage".to_string()).await?;
828                        }
829                    }
830                }
831            }
832        }
833        
834        #[cfg(target_os = "linux")]
835        {
836            // Read block devices from /sys/block
837            if let Ok(entries) = std::fs::read_dir("/sys/block") {
838                for (index, entry) in entries.enumerate() {
839                    if let Ok(entry) = entry {
840                        let device_name = entry.file_name().to_string_lossy().to_string();
841                        
842                        // Skip loop devices and ram disks
843                        if device_name.starts_with("loop") || device_name.starts_with("ram") {
844                            continue;
845                        }
846                        
847                        // Try to read size
848                        let size_path = entry.path().join("size");
849                        if let Ok(size_str) = std::fs::read_to_string(&size_path) {
850                            if let Ok(size_sectors) = size_str.trim().parse::<u64>() {
851                                let size_gb = (size_sectors * 512) as f64 / (1024.0 * 1024.0 * 1024.0);
852                                
853                                let storage_device = RiDevice::new(
854                                    format!("Storage-{}-{}", index + 1, device_name), 
855                                    RiDeviceType::Storage
856                                ).with_capabilities(
857                                    RiDeviceCapabilities::new()
858                                        .with_storage_gb(size_gb)
859                                        .with_bandwidth_gbps(6.0) // SATA III estimate
860                                );
861                                
862                                self.add_device(storage_device, "System Storage".to_string()).await?;
863                            }
864                        }
865                    }
866                }
867            }
868        }
869        
870        Ok(())
871    }
872    
873    /// Discover real hardware devices using the new discovery engine
874    async fn discover_hardware_devices(&mut self) -> RiResult<Vec<RiDevice>> {
875        // Use the new discovery engine if available
876        if let Some(discovery) = &self.discovery {
877            let devices = discovery.discover_all().await?;
878            return Ok(devices);
879        }
880
881        // Fallback to old discovery if no engine initialized
882        let mut temp_controller = RiDeviceController::new();
883        let config = RiDeviceControlConfig::default();
884        temp_controller.discover_system_devices(&config).await?;
885        Ok(temp_controller.get_all_devices())
886    }
887    
888    /// Discover network devices from system
889    async fn discover_network_devices(&mut self, _config: &RiDeviceControlConfig) -> RiResult<()> {
890        #[cfg(target_os = "windows")]
891        {
892            let output = std::process::Command::new("powershell")
893                .args(["-NoProfile", "-Command", "Get-CimInstance Win32_NetworkAdapter -Filter 'NetEnabled=true' | ForEach-Object { 'Name=' + $_.Name; 'Speed=' + $_.Speed }"])
894                .output()
895                .map_err(|e| RiError::DeviceError(format!("Failed to query network info: {e}")))?;
896                
897            let network_info = String::from_utf8_lossy(&output.stdout);
898            
899            let mut network_counter = 0;
900            // Store lines in a vector for easier access
901            let lines: Vec<&str> = network_info.lines().collect();
902            
903            for (network_index, line) in lines.iter().enumerate() {
904                if line.starts_with("Name=") {
905                    let name = line.split('=').nth(1).unwrap_or("Unknown").trim();
906                    network_counter += 1;
907                    
908                    // Look for speed in next lines
909                    if let Some(speed_line) = lines.iter().skip(network_index + 1).find(|l| l.starts_with("Speed=")) {
910                        if let Some(speed_bps) = speed_line.split('=').nth(1).and_then(|s| s.trim().parse::<u64>().ok()) {
911                            let speed_gbps = speed_bps as f64 / (1000.0 * 1000.0 * 1000.0);
912                            
913                            let network_device = RiDevice::new(
914                                format!("Network-{network_counter}-{name}"), 
915                                RiDeviceType::Network
916                            ).with_capabilities(
917                                RiDeviceCapabilities::new()
918                                    .with_bandwidth_gbps(speed_gbps)
919                            );
920                            
921                            self.add_device(network_device, "System Network".to_string()).await?;
922                        }
923                    }
924                }
925            }
926        }
927        
928        #[cfg(target_os = "linux")]
929        {
930            // Read network interfaces from /sys/class/net
931            if let Ok(entries) = std::fs::read_dir("/sys/class/net") {
932                for (index, entry) in entries.enumerate() {
933                    if let Ok(entry) = entry {
934                        let interface_name = entry.file_name().to_string_lossy().to_string();
935                        
936                        // Skip loopback interface
937                        if interface_name == "lo" {
938                            continue;
939                        }
940                        
941                        // Try to read speed
942                        let speed_path = entry.path().join("speed");
943                        if let Ok(speed_str) = std::fs::read_to_string(&speed_path) {
944                            if let Ok(speed_mbps) = speed_str.trim().parse::<f64>() {
945                                let speed_gbps = speed_mbps / 1000.0;
946                                
947                                let network_device = RiDevice::new(
948                                    format!("Network-{}-{}", index + 1, interface_name), 
949                                    RiDeviceType::Network
950                                ).with_capabilities(
951                                    RiDeviceCapabilities::new()
952                                        .with_bandwidth_gbps(speed_gbps)
953                                );
954                                
955                                self.add_device(network_device, "System Network".to_string()).await?;
956                            }
957                        }
958                    }
959                }
960            }
961        }
962        
963        Ok(())
964    }
965    
966    /// Helper method to add a discovered device
967    #[allow(dead_code)]
968    pub async fn add_device(&mut self, mut device: RiDevice, location: String) -> RiResult<()> {
969        device.set_status(RiDeviceStatus::Available);
970        device.set_location(location);
971        
972        let device_id = device.id().to_string();
973        let device_type = device.device_type();
974        
975        self.devices.insert(device_id.clone(), Arc::new(RwLock::new(device)));
976        self.device_type_index
977            .entry(device_type)
978            .or_default()
979            .push(device_id);
980            
981        Ok(())
982    }
983
984    /// Create a device from network discovery (for remote devices)
985    #[allow(dead_code)]
986    fn create_discovered_device(&self, device_info: &RiNetworkDeviceInfo) -> RiDevice {
987        let device_type_enum = match device_info.device_type.as_str() {
988            "CPU" => RiDeviceType::CPU,
989            "GPU" => RiDeviceType::GPU,
990            "Memory" => RiDeviceType::Memory,
991            "Storage" => RiDeviceType::Storage,
992            "Network" => RiDeviceType::Network,
993            _ => RiDeviceType::Custom,
994        };
995        
996        let name = format!("Discovered-{}-{}", device_info.device_type, device_info.id);
997        let mut device = RiDevice::new(name, device_type_enum);
998
999        // Add discovered capabilities
1000        let mut capabilities = RiDeviceCapabilities::new();
1001
1002        match device_type_enum {
1003            RiDeviceType::CPU => {
1004                capabilities = capabilities
1005                    .with_compute_units(device_info.compute_units.unwrap_or(8))
1006                    .with_memory_gb(device_info.memory_gb.unwrap_or(16.0));
1007            }
1008            RiDeviceType::GPU => {
1009                capabilities = capabilities
1010                    .with_compute_units(device_info.compute_units.unwrap_or(1000))
1011                    .with_memory_gb(device_info.memory_gb.unwrap_or(8.0));
1012            }
1013            RiDeviceType::Memory => {
1014                capabilities = capabilities
1015                    .with_memory_gb(device_info.memory_gb.unwrap_or(64.0))
1016                    .with_bandwidth_gbps(device_info.bandwidth_gbps.unwrap_or(25.6));
1017            }
1018            RiDeviceType::Storage => {
1019                capabilities = capabilities
1020                    .with_storage_gb(device_info.storage_gb.unwrap_or(1000.0))
1021                    .with_bandwidth_gbps(device_info.bandwidth_gbps.unwrap_or(6.0));
1022            }
1023            RiDeviceType::Network => {
1024                capabilities = capabilities
1025                    .with_bandwidth_gbps(device_info.bandwidth_gbps.unwrap_or(1.0));
1026            }
1027            _ => {}
1028        }
1029
1030        device = device.with_capabilities(capabilities);
1031        device.set_status(RiDeviceStatus::Available);
1032        device.set_location(format!("Network Discovery: {}", device_info.source));
1033
1034        device
1035    }
1036
1037    /// Find a suitable device for the given requirements
1038    pub async fn find_suitable_device(
1039        &self,
1040        device_type: &RiDeviceType,
1041        requirements: &RiDeviceCapabilities,
1042    ) -> RiResult<Option<RiDevice>> {
1043        let device_ids = match self.device_type_index.get(device_type) {
1044            Some(ids) => ids.clone(),
1045            None => return Ok(None),
1046        };
1047
1048        // Find the best available device
1049        let mut best_device: Option<RiDevice> = None;
1050        let mut best_score = 0u32;
1051
1052        for device_id in device_ids {
1053            if let Some(device_lock) = self.devices.get(&device_id) {
1054                let device = device_lock.read().await;
1055
1056                if device.is_available() && device.capabilities().meets_requirements(requirements)
1057                {
1058                    let score = self.calculate_device_score(&device);
1059
1060                    if score > best_score || best_device.is_none() {
1061                        best_device = Some(device.clone());
1062                        best_score = score;
1063                    }
1064                }
1065            }
1066        }
1067
1068        Ok(best_device)
1069    }
1070    
1071    /// Initialize metrics for device monitoring
1072    pub fn initialize_metrics(&mut self, metrics_registry: &RiMetricsRegistry) -> RiResult<()> {
1073        use crate::observability::{RiMetric, RiMetricConfig, RiMetricType};
1074        use std::sync::Arc;
1075        
1076        // Register device count metric
1077        let device_total_config = RiMetricConfig {
1078            metric_type: RiMetricType::Gauge,
1079            name: "dms_devices_total".to_string(),
1080            help: "Total number of discovered devices".to_string(),
1081            buckets: vec![],
1082            quantiles: vec![],
1083            max_age: std::time::Duration::from_secs(300),
1084            age_buckets: 5,
1085        };
1086        let device_total_metric = Arc::new(RiMetric::new(device_total_config));
1087        metrics_registry.register(device_total_metric.clone())?;
1088        
1089        // Register device type metrics
1090        for device_type in self.device_type_index.keys() {
1091            let device_type_config = RiMetricConfig {
1092                metric_type: RiMetricType::Gauge,
1093                name: format!("dms_devices_{}_total", device_type.to_string().to_lowercase()),
1094                help: format!("Total number of {device_type} devices"),
1095                buckets: vec![],
1096                quantiles: vec![],
1097                max_age: std::time::Duration::from_secs(300),
1098                age_buckets: 5,
1099            };
1100            let device_type_metric = Arc::new(RiMetric::new(device_type_config));
1101            metrics_registry.register(device_type_metric.clone())?;
1102        }
1103        
1104        Ok(())
1105    }
1106
1107    fn calculate_device_score(&self, device: &RiDevice) -> u32 {
1108        let mut score = device.health_score() as u32 * 100;
1109
1110        // Add capability-based scoring
1111        let capabilities = device.capabilities();
1112
1113        if let Some(compute_units) = capabilities.compute_units {
1114            score += compute_units as u32;
1115        }
1116
1117        if let Some(memory_gb) = capabilities.memory_gb {
1118            score += (memory_gb * 10.0) as u32;
1119        }
1120
1121        if let Some(storage_gb) = capabilities.storage_gb {
1122            score += (storage_gb * 5.0) as u32;
1123        }
1124
1125        if let Some(bandwidth_gbps) = capabilities.bandwidth_gbps {
1126            score += (bandwidth_gbps * 20.0) as u32;
1127        }
1128
1129        score
1130    }
1131
1132    /// Allocate a device
1133    pub async fn allocate_device(
1134        &mut self,
1135        device_id: &str,
1136        allocation_id: &str,
1137    ) -> RiResult<()> {
1138        if let Some(device_lock) = self.devices.get(device_id) {
1139            let mut device = device_lock.write().await;
1140
1141            if device.allocate(allocation_id) {
1142                self.allocation_map
1143                    .insert(allocation_id.to_string(), device_id.to_string());
1144                Ok(())
1145            } else {
1146                Err(crate::core::RiError::DeviceAllocationFailed {
1147                    device_id: device_id.to_string(),
1148                    reason: "Device not available".to_string(),
1149                })
1150            }
1151        } else {
1152            Err(crate::core::RiError::DeviceNotFound {
1153                device_id: device_id.to_string(),
1154            })
1155        }
1156    }
1157
1158    /// Release a device by allocation ID
1159    pub async fn release_device_by_allocation(&mut self, allocation_id: &str) -> RiResult<()> {
1160        if let Some(device_id) = self.allocation_map.remove(allocation_id) {
1161            if let Some(device_lock) = self.devices.get(&device_id) {
1162                let mut device = device_lock.write().await;
1163                device.release();
1164                Ok(())
1165            } else {
1166                Err(crate::core::RiError::DeviceNotFound { device_id })
1167            }
1168        } else {
1169            Err(crate::core::RiError::AllocationNotFound {
1170                allocation_id: allocation_id.to_string(),
1171            })
1172        }
1173    }
1174
1175    /// Remove a device
1176    pub async fn remove_device(&mut self, device_id: &str) -> RiResult<()> {
1177        if let Some(device_lock) = self.devices.remove(device_id) {
1178            let device = device_lock.read().await;
1179            let device_type = device.device_type();
1180
1181            // Remove from type index
1182            if let Some(type_devices) = self.device_type_index.get_mut(&device_type) {
1183                type_devices.retain(|id| id != device_id);
1184            }
1185
1186            // Remove any allocations
1187            if let Some(allocation_id) = device.get_allocation_id() {
1188                self.allocation_map.remove(allocation_id);
1189            }
1190        }
1191
1192        Ok(())
1193    }
1194
1195    /// Get a device by ID
1196    pub async fn get_device(&self, device_id: &str) -> Option<RiDevice> {
1197        if let Some(device_lock) = self.devices.get(device_id) {
1198            let device = device_lock.read().await;
1199            Some(device.clone())
1200        } else {
1201            None
1202        }
1203    }
1204
1205    /// Get all devices
1206    pub fn get_all_devices(&self) -> Vec<RiDevice> {
1207        let mut devices = Vec::with_capacity(4);
1208
1209        // This is a blocking operation - in a real implementation, we'd use async
1210        for device_lock in self.devices.values() {
1211            if let Ok(device) = device_lock.try_read() {
1212                devices.push(device.clone());
1213            }
1214        }
1215
1216        devices
1217    }
1218
1219    /// Release all devices (shutdown)
1220    pub fn release_all_devices(&mut self) -> RiResult<()> {
1221        // Clear all allocations
1222        self.allocation_map.clear();
1223
1224        // Release all devices
1225        for device_lock in self.devices.values() {
1226            if let Ok(mut device) = device_lock.try_write() {
1227                device.release();
1228            }
1229        }
1230
1231        Ok(())
1232    }
1233
1234    /// Perform health check on all devices
1235    pub async fn perform_health_checks(&mut self) -> RiResult<Vec<(String, u8)>> {
1236        let mut results = Vec::with_capacity(4);
1237
1238        for (device_id, device_lock) in &self.devices {
1239            let mut device = device_lock.write().await;
1240
1241            // Simulate health check by updating health metrics
1242            let mut health_metrics = device.health_metrics().clone();
1243
1244            // Simulate CPU and memory usage
1245            health_metrics.cpu_usage_percent = rand::random::<f64>() * 100.0;
1246            health_metrics.memory_usage_percent = rand::random::<f64>() * 100.0;
1247
1248            // Simulate temperature
1249            health_metrics.temperature_celsius = rand::random::<f64>() * 50.0 + 30.0;
1250
1251            // Simulate error count (occasionally increment)
1252            if rand::random::<f64>() < 0.01 {
1253                // 1% chance
1254                health_metrics.error_count += 1;
1255            }
1256
1257            // Simulate throughput
1258            health_metrics.throughput = rand::random::<u64>() % 1000;
1259            
1260            // Simulate network latency (for network devices)
1261            health_metrics.network_latency_ms = rand::random::<f64>() * 200.0;
1262            
1263            // Simulate disk IOPS (for storage devices)
1264            health_metrics.disk_iops = (rand::random::<f64>() * 500.0) as u64;
1265            
1266            // Simulate battery level
1267            health_metrics.battery_level_percent = rand::random::<f64>() * 100.0;
1268            
1269            // Simulate response time
1270            health_metrics.response_time_ms = rand::random::<f64>() * 150.0;
1271            
1272            // Simulate uptime (increment by 30 seconds each check)
1273            health_metrics.uptime_seconds += 30;
1274
1275            // Update device health metrics
1276            device.update_health_metrics(health_metrics);
1277
1278            // Calculate health score
1279            let health_score = device.dynamic_health_score(device.health_metrics());
1280
1281            // Update device status based on health score
1282            if health_score < 20 {
1283                device.set_status(RiDeviceStatus::Error);
1284            } else if health_score < 50 {
1285                device.set_status(RiDeviceStatus::Maintenance);
1286            } else if health_score < 70 {
1287                device.set_status(RiDeviceStatus::Degraded);
1288            } else if device.status() == RiDeviceStatus::Error
1289                || device.status() == RiDeviceStatus::Maintenance
1290                || device.status() == RiDeviceStatus::Degraded
1291            {
1292                device.set_status(RiDeviceStatus::Available);
1293            }
1294
1295            results.push((device_id.to_string(), health_score));
1296        }
1297
1298        Ok(results)
1299    }
1300
1301    /// Start periodic health checks
1302    pub async fn start_health_checks(&self, interval_secs: u64) -> tokio::task::JoinHandle<()> {
1303        let devices = self.devices.clone();
1304
1305        tokio::spawn(async move {
1306            let mut interval = tokio::time::interval(tokio::time::Duration::from_secs(interval_secs));
1307
1308            loop {
1309                interval.tick().await;
1310
1311                for device_lock in devices.values() {
1312                    let mut device = device_lock.write().await;
1313
1314                    // Simulate health check by updating health metrics
1315                    let mut health_metrics = device.health_metrics().clone();
1316
1317                    // Simulate CPU and memory usage
1318                    health_metrics.cpu_usage_percent = rand::random::<f64>() * 100.0;
1319                    health_metrics.memory_usage_percent = rand::random::<f64>() * 100.0;
1320
1321                    // Simulate temperature
1322                    health_metrics.temperature_celsius = rand::random::<f64>() * 50.0 + 30.0;
1323
1324                    // Simulate error count (occasionally increment)
1325                    if rand::random::<f64>() < 0.01 {
1326                        // 1% chance
1327                        health_metrics.error_count += 1;
1328                    }
1329
1330                    // Simulate throughput
1331                    health_metrics.throughput = rand::random::<u64>() % 1000;
1332                    
1333                    // Simulate network latency (for network devices)
1334                    health_metrics.network_latency_ms = rand::random::<f64>() * 200.0;
1335                    
1336                    // Simulate disk IOPS (for storage devices)
1337                    health_metrics.disk_iops = (rand::random::<f64>() * 500.0) as u64;
1338                    
1339                    // Simulate battery level
1340                    health_metrics.battery_level_percent = rand::random::<f64>() * 100.0;
1341                    
1342                    // Simulate response time
1343                    health_metrics.response_time_ms = rand::random::<f64>() * 150.0;
1344                    
1345                    // Simulate uptime (increment by the interval each check)
1346                    health_metrics.uptime_seconds += interval_secs as u64;
1347
1348                    // Update device health metrics
1349                    device.update_health_metrics(health_metrics);
1350
1351                    // Calculate health score
1352                    let health_score = device.dynamic_health_score(device.health_metrics());
1353
1354                    // Update device status based on health score
1355                    if health_score < 20 {
1356                        device.set_status(RiDeviceStatus::Error);
1357                    } else if health_score < 50 {
1358                        device.set_status(RiDeviceStatus::Maintenance);
1359                    } else if health_score < 70 {
1360                        device.set_status(RiDeviceStatus::Degraded);
1361                    } else if device.status() == RiDeviceStatus::Error
1362                        || device.status() == RiDeviceStatus::Maintenance
1363                        || device.status() == RiDeviceStatus::Degraded
1364                    {
1365                        device.set_status(RiDeviceStatus::Available);
1366                    }
1367                }
1368            }
1369        })
1370    }
1371
1372    /// Get device health metrics
1373    pub async fn get_device_health(
1374        &self,
1375        device_id: &str,
1376    ) -> RiResult<super::core::RiDeviceHealthMetrics> {
1377        if let Some(device_lock) = self.devices.get(device_id) {
1378            let device = device_lock.read().await;
1379            Ok(device.health_metrics().clone())
1380        } else {
1381            Err(crate::core::RiError::DeviceNotFound {
1382                device_id: device_id.to_string(),
1383            })
1384        }
1385    }
1386
1387    /// Get all device health metrics
1388    pub async fn get_all_device_health(
1389        &self,
1390    ) -> RiResult<FxHashMap<String, super::core::RiDeviceHealthMetrics>> {
1391        let mut health_map = FxHashMap::default();
1392
1393        for (device_id, device_lock) in &self.devices {
1394            let device = device_lock.read().await;
1395            health_map.insert(device_id.to_string(), device.health_metrics().clone());
1396        }
1397
1398        Ok(health_map)
1399    }
1400
1401    /// Create a mock device for discovery simulation
1402    fn create_mock_device_for_discovery(&self) -> RiDevice {
1403        use super::core::{RiDeviceCapabilities, RiDeviceType};
1404        
1405        let device_types = [RiDeviceType::CPU,
1406            RiDeviceType::GPU,
1407            RiDeviceType::Memory,
1408            RiDeviceType::Storage,
1409            RiDeviceType::Network];
1410        
1411        let device_type = device_types[rand::random::<usize>() % device_types.len()];
1412        
1413        let device_name = match device_type {
1414            RiDeviceType::CPU => format!("CPU-{}-cores", rand::random::<usize>() % 32 + 1),
1415            RiDeviceType::GPU => format!("GPU-{}-GB", rand::random::<usize>() % 24 + 1),
1416            RiDeviceType::Memory => format!("Memory-{}-GB", rand::random::<usize>() % 64 + 1),
1417            RiDeviceType::Storage => format!("Storage-{}-TB", rand::random::<usize>() % 10 + 1),
1418            RiDeviceType::Network => format!("Network-{}-Gbps", rand::random::<usize>() % 100 + 1),
1419            RiDeviceType::Sensor => format!("Sensor-{}-units", rand::random::<usize>() % 100 + 1),
1420            RiDeviceType::Actuator => format!("Actuator-{}-actions", rand::random::<usize>() % 50 + 1),
1421            RiDeviceType::Custom => format!("Custom-{}-device", rand::random::<usize>() % 1000 + 1),
1422        };
1423        
1424        let capabilities = RiDeviceCapabilities::new()
1425            .with_compute_units(rand::random::<usize>() % 1000 + 100)
1426            .with_memory_gb(rand::random::<f64>() * 64.0 + 1.0);
1427        
1428        RiDevice::new(device_name, device_type)
1429            .with_capabilities(capabilities)
1430    }
1431}