1#![allow(non_snake_case)]
19
20use chrono::Utc;
92use std::collections::HashMap;
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::{DMSCDevice, DMSCDeviceCapabilities, DMSCDeviceStatus, DMSCDeviceType, DMSCDeviceControlConfig, DMSCNetworkDeviceInfo};
103use super::discovery::{DMSCDeviceDiscovery, DiscoveryConfig};
104use crate::core::DMSCResult;
105use crate::prelude::DMSCMetricsRegistry;
106#[cfg(not(target_os = "macos"))]
107use crate::prelude::DMSCError;
108#[cfg_attr(feature = "pyo3", pyo3::prelude::pyclass)]
112pub struct DMSCDeviceController {
113 devices: HashMap<String, Arc<RwLock<DMSCDevice>>>,
114 device_type_index: HashMap<DMSCDeviceType, Vec<String>>,
115 allocation_map: HashMap<String, String>,
116 discovery: Option<Arc<DMSCDeviceDiscovery>>,
117}
118
119#[cfg(feature = "pyo3")]
120#[pymethods]
121impl DMSCDeviceController {
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::DMSCDiscoveryResult> {
134 let rt = tokio::runtime::Runtime::new().map_err(|e| {
135 pyo3::exceptions::PyRuntimeError::new_err(format!("Failed to create runtime: {}", e))
136 })?;
137
138 rt.block_on(self.discover_devices()).map_err(|e| {
139 pyo3::exceptions::PyRuntimeError::new_err(format!("Device discovery failed: {}", e))
140 })
141 }
142
143 #[pyo3(name = "discover_system_devices")]
144 fn discover_system_devices_impl(&mut self, config: &DMSCDeviceControlConfig) -> PyResult<()> {
145 let rt = tokio::runtime::Runtime::new().map_err(|e| {
146 pyo3::exceptions::PyRuntimeError::new_err(format!("Failed to create runtime: {}", e))
147 })?;
148
149 rt.block_on(self.discover_system_devices(config)).map_err(|e| {
150 pyo3::exceptions::PyRuntimeError::new_err(format!("System device discovery failed: {}", e))
151 })
152 }
153
154 #[pyo3(name = "find_suitable_device")]
155 fn find_suitable_device_impl(&self, device_type: &DMSCDeviceType, requirements: &DMSCDeviceCapabilities) -> PyResult<Option<DMSCDevice>> {
156 let rt = tokio::runtime::Runtime::new().map_err(|e| {
157 pyo3::exceptions::PyRuntimeError::new_err(format!("Failed to create runtime: {}", e))
158 })?;
159
160 rt.block_on(self.find_suitable_device(device_type, requirements)).map_err(|e| {
161 pyo3::exceptions::PyRuntimeError::new_err(format!("Failed to find suitable device: {}", e))
162 })
163 }
164
165 #[pyo3(name = "allocate_device")]
166 fn allocate_device_impl(&mut self, device_id: &str, allocation_id: &str) -> PyResult<()> {
167 let rt = tokio::runtime::Runtime::new().map_err(|e| {
168 pyo3::exceptions::PyRuntimeError::new_err(format!("Failed to create runtime: {}", e))
169 })?;
170
171 rt.block_on(self.allocate_device(device_id, allocation_id)).map_err(|e| {
172 pyo3::exceptions::PyRuntimeError::new_err(format!("Device allocation failed: {}", e))
173 })
174 }
175
176 #[pyo3(name = "release_device_by_allocation")]
177 fn release_device_by_allocation_impl(&mut self, allocation_id: &str) -> PyResult<()> {
178 let rt = tokio::runtime::Runtime::new().map_err(|e| {
179 pyo3::exceptions::PyRuntimeError::new_err(format!("Failed to create runtime: {}", e))
180 })?;
181
182 rt.block_on(self.release_device_by_allocation(allocation_id)).map_err(|e| {
183 pyo3::exceptions::PyRuntimeError::new_err(format!("Device release failed: {}", e))
184 })
185 }
186
187 #[pyo3(name = "get_all_devices")]
188 fn get_all_devices_impl(&self) -> Vec<DMSCDevice> {
189 self.get_all_devices()
190 }
191
192 #[pyo3(name = "release_all_devices")]
193 fn release_all_devices_impl(&mut self) -> PyResult<()> {
194 self.release_all_devices().map_err(|e| {
195 pyo3::exceptions::PyRuntimeError::new_err(format!("Failed to release all devices: {}", e))
196 })
197 }
198
199 #[pyo3(name = "perform_health_checks")]
200 fn perform_health_checks_impl(&mut self) -> PyResult<Vec<(String, u8)>> {
201 let rt = tokio::runtime::Runtime::new().map_err(|e| {
202 pyo3::exceptions::PyRuntimeError::new_err(format!("Failed to create runtime: {}", e))
203 })?;
204
205 rt.block_on(self.perform_health_checks()).map_err(|e| {
206 pyo3::exceptions::PyRuntimeError::new_err(format!("Health checks failed: {}", e))
207 })
208 }
209
210 #[pyo3(name = "get_device_health")]
211 fn get_device_health_impl(&self, device_id: &str) -> PyResult<super::core::DMSCDeviceHealthMetrics> {
212 let rt = tokio::runtime::Runtime::new().map_err(|e| {
213 pyo3::exceptions::PyRuntimeError::new_err(format!("Failed to create runtime: {}", e))
214 })?;
215
216 rt.block_on(self.get_device_health(device_id)).map_err(|e| {
217 pyo3::exceptions::PyRuntimeError::new_err(format!("Failed to get device health: {}", e))
218 })
219 }
220
221 #[pyo3(name = "get_all_device_health")]
222 fn get_all_device_health_impl(&self) -> PyResult<std::collections::HashMap<String, super::core::DMSCDeviceHealthMetrics>> {
223 let rt = tokio::runtime::Runtime::new().map_err(|e| {
224 pyo3::exceptions::PyRuntimeError::new_err(format!("Failed to create runtime: {}", e))
225 })?;
226
227 rt.block_on(self.get_all_device_health()).map_err(|e| {
228 pyo3::exceptions::PyRuntimeError::new_err(format!("Failed to get all device health: {}", e))
229 })
230 }
231
232 #[pyo3(name = "device_count")]
233 fn device_count_impl(&self) -> usize {
234 self.devices.len()
235 }
236
237 #[pyo3(name = "get_devices_by_type")]
238 fn get_devices_by_type_impl(&self, device_type: &DMSCDeviceType) -> Vec<DMSCDevice> {
239 let device_ids = match self.device_type_index.get(device_type) {
240 Some(ids) => ids.clone(),
241 None => return Vec::new(),
242 };
243
244 let mut devices = Vec::new();
245 for device_id in device_ids {
246 if let Some(device_lock) = self.devices.get(&device_id) {
247 if let Ok(device) = device_lock.try_read() {
248 devices.push(device.clone());
249 }
250 }
251 }
252 devices
253 }
254
255 #[pyo3(name = "start_health_checks")]
256 fn start_health_checks_impl(&self, interval_secs: u64) -> PyResult<String> {
257 let _handle = self.start_health_checks(interval_secs);
258 Ok(format!("Health check task started with interval {} seconds", interval_secs))
259 }
260}
261
262impl Default for DMSCDeviceController {
263 fn default() -> Self {
264 Self::new()
265 }
266}
267
268impl DMSCDeviceController {
269 pub fn new() -> Self {
270 Self {
271 devices: HashMap::new(),
272 device_type_index: HashMap::new(),
273 allocation_map: HashMap::new(),
274 discovery: None,
275 }
276 }
277
278 pub async fn with_discovery(discovery: Arc<DMSCDeviceDiscovery>) -> Self {
280 Self {
281 devices: HashMap::new(),
282 device_type_index: HashMap::new(),
283 allocation_map: HashMap::new(),
284 discovery: Some(discovery),
285 }
286 }
287
288 pub async fn init_discovery(&mut self) -> DMSCResult<()> {
290 let config = DiscoveryConfig::default();
291 let discovery = Arc::new(DMSCDeviceDiscovery::new(config).await?);
292 self.discovery = Some(discovery);
293 Ok(())
294 }
295
296 pub async fn discover_devices(&mut self) -> DMSCResult<super::DMSCDiscoveryResult> {
298 let max_retries = 3;
303 let retry_delay = std::time::Duration::from_millis(500);
304
305 for attempt in 0..max_retries {
306 match self.perform_device_discovery().await {
307 Ok(result) => return Ok(result),
308 Err(e) => {
309 if attempt == max_retries - 1 {
310 return Err(e);
312 }
313
314 let error_msg = format!("Device discovery attempt {} failed: {}, retrying in {}ms",
316 attempt + 1, e, retry_delay.as_millis());
317 log::warn!("{error_msg}");
318
319 tokio::time::sleep(retry_delay).await;
321 }
322 }
323 }
324
325 Err(crate::core::DMSCError::Other("Device discovery failed after maximum retries".to_string()))
327 }
328
329 async fn perform_device_discovery(&mut self) -> DMSCResult<super::DMSCDiscoveryResult> {
335 let mut discovered_devices = Vec::new();
336 let mut updated_devices = Vec::new();
337 let mut removed_devices = Vec::new();
338
339 for device_lock in self.devices.values() {
341 match device_lock.try_write() {
342 Ok(mut device) => {
343 device.update_last_seen();
344
345 let health_metrics = device.health_metrics().clone();
347 let device_type = device.device_type();
348
349 match device_type {
351 DMSCDeviceType::CPU => {
352 if health_metrics.cpu_usage_percent > 95.0 || health_metrics.temperature_celsius > 90.0 {
354 device.set_status(DMSCDeviceStatus::Degraded);
355 } else if health_metrics.cpu_usage_percent > 80.0 || health_metrics.temperature_celsius > 80.0 {
356 device.set_status(DMSCDeviceStatus::Busy);
357 } else if device.status() != DMSCDeviceStatus::Allocated {
358 device.set_status(DMSCDeviceStatus::Available);
359 }
360 },
361 DMSCDeviceType::GPU => {
362 if health_metrics.cpu_usage_percent > 95.0 || health_metrics.temperature_celsius > 95.0 {
364 device.set_status(DMSCDeviceStatus::Degraded);
365 } else if health_metrics.cpu_usage_percent > 85.0 || health_metrics.temperature_celsius > 85.0 {
366 device.set_status(DMSCDeviceStatus::Busy);
367 } else if device.status() != DMSCDeviceStatus::Allocated {
368 device.set_status(DMSCDeviceStatus::Available);
369 }
370 },
371 DMSCDeviceType::Network => {
372 if health_metrics.network_latency_ms > 200.0 {
374 device.set_status(DMSCDeviceStatus::Degraded);
375 } else if health_metrics.network_latency_ms > 100.0 {
376 device.set_status(DMSCDeviceStatus::Busy);
377 } else if device.status() != DMSCDeviceStatus::Allocated {
378 device.set_status(DMSCDeviceStatus::Available);
379 }
380 },
381 DMSCDeviceType::Storage => {
382 if health_metrics.response_time_ms > 100.0 {
384 device.set_status(DMSCDeviceStatus::Degraded);
385 } else if health_metrics.response_time_ms > 50.0 {
386 device.set_status(DMSCDeviceStatus::Busy);
387 } else if device.status() != DMSCDeviceStatus::Allocated {
388 device.set_status(DMSCDeviceStatus::Available);
389 }
390 },
391 _ => {
392 if health_metrics.error_count > 5 {
394 device.set_status(DMSCDeviceStatus::Degraded);
395 } else if device.status() != DMSCDeviceStatus::Allocated {
396 device.set_status(DMSCDeviceStatus::Available);
397 }
398 }
399 }
400
401 updated_devices.push(device.clone());
402 },
403 Err(_) => {
404 continue;
406 }
407 }
408 }
409
410 let new_hardware_devices = self.discover_hardware_devices().await?;
412
413 for device in new_hardware_devices {
415 let device_id = device.id().to_string();
416
417 if !self.devices.contains_key(&device_id) {
419 self.devices.insert(device_id.clone(), Arc::new(RwLock::new(device.clone())));
420 self.device_type_index
421 .entry(device.device_type())
422 .or_default()
423 .push(device_id);
424
425 discovered_devices.push(device);
426 }
427 }
428
429 if rand::random::<f64>() < 0.05 {
431 let new_device = self.create_mock_device_for_discovery();
433 let device_id = new_device.id().to_string();
434
435 self.devices
436 .insert(device_id.clone(), Arc::new(RwLock::new(new_device.clone())));
437 self.device_type_index
438 .entry(new_device.device_type())
439 .or_default()
440 .push(device_id);
441
442 discovered_devices.push(new_device);
443 }
444
445 let timeout = chrono::TimeDelta::minutes(5);
447 let now = Utc::now();
448
449 let mut to_remove = Vec::new();
450 for (device_id, device_lock) in &self.devices {
451 match device_lock.try_read() {
452 Ok(device) => {
453 if now.signed_duration_since(device.last_seen()) > timeout {
454 to_remove.push(device_id.clone());
455 }
456 },
457 Err(_) => {
458 continue;
460 }
461 }
462 }
463
464 for device_id in &to_remove {
465 self.remove_device(device_id).await?;
466 removed_devices.push(device_id.to_string());
467 }
468
469 Ok(super::DMSCDiscoveryResult {
470 discovered_devices,
471 updated_devices,
472 removed_devices,
473 total_devices: self.devices.len(),
474 })
475 }
476
477 pub async fn discover_system_devices(&mut self, config: &DMSCDeviceControlConfig) -> DMSCResult<()> {
479 self.discover_cpu_devices(config).await?;
481
482 self.discover_gpu_devices(config).await?;
484
485 self.discover_memory_devices(config).await?;
487
488 self.discover_storage_devices(config).await?;
490
491 self.discover_network_devices(config).await?;
493
494 Ok(())
495 }
496
497 async fn discover_gpu_devices(&mut self, _config: &DMSCDeviceControlConfig) -> DMSCResult<()> {
499 #[cfg(target_os = "windows")]
500 {
501 if let Ok(nvidia_output) = std::process::Command::new("nvidia-smi")
503 .args(["--query-gpu=name,memory.total", "--format=csv,noheader"])
504 .output()
505 {
506 let gpu_info = String::from_utf8_lossy(&nvidia_output.stdout);
507
508 for (index, line) in gpu_info.lines().enumerate() {
509 let parts: Vec<&str> = line.split(',').collect();
510 if parts.len() >= 2 {
511 let name = parts[0].trim();
512 let memory_mb = parts[1].trim().replace(" MiB", "").parse::<f64>().unwrap_or(0.0);
513 let memory_gb = memory_mb / 1024.0;
514
515 let gpu_device = DMSCDevice::new(
516 format!("GPU-{}-{}", index + 1, name),
517 DMSCDeviceType::GPU
518 ).with_capabilities(
519 DMSCDeviceCapabilities::new()
520 .with_compute_units(1000) .with_memory_gb(memory_gb)
522 );
523
524 self.add_device(gpu_device, "NVIDIA GPU".to_string()).await?;
525 }
526 }
527 }
528 }
529
530 #[cfg(target_os = "linux")]
531 {
532 if let Ok(nvidia_output) = std::process::Command::new("nvidia-smi")
534 .args(&["--query-gpu=name,memory.total", "--format=csv,noheader"])
535 .output()
536 {
537 let gpu_info = String::from_utf8_lossy(&nvidia_output.stdout);
538
539 for (index, line) in gpu_info.lines().enumerate() {
540 let parts: Vec<&str> = line.split(',').collect();
541 if parts.len() >= 2 {
542 let name = parts[0].trim();
543 let memory_mb = parts[1].trim().replace(" MiB", "").parse::<f64>().unwrap_or(0.0);
544 let memory_gb = memory_mb / 1024.0;
545
546 let gpu_device = DMSCDevice::new(
547 format!("GPU-{}-{}", index + 1, name),
548 DMSCDeviceType::GPU
549 ).with_capabilities(
550 DMSCDeviceCapabilities::new()
551 .with_compute_units(1000) .with_memory_gb(memory_gb)
553 );
554
555 self.add_device(gpu_device, "NVIDIA GPU".to_string()).await?;
556 }
557 }
558 }
559 }
560
561 Ok(())
562 }
563
564 #[allow(dead_code)]
566 async fn discover_storage_devices_impl(&mut self, _config: &DMSCDeviceControlConfig) -> DMSCResult<Vec<DMSCDevice>> {
567 #[cfg(target_os = "windows")]
568 {
569 if let Ok(nvidia_output) = std::process::Command::new("nvidia-smi")
571 .args(["--query-gpu=name,memory.total", "--format=csv,noheader"])
572 .output()
573 {
574 let gpu_info = String::from_utf8_lossy(&nvidia_output.stdout);
575
576 for (index, line) in gpu_info.lines().enumerate() {
577 let parts: Vec<&str> = line.split(',').collect();
578 if parts.len() >= 2 {
579 let name = parts[0].trim();
580 let memory_mb = parts[1].trim().replace(" MiB", "").parse::<f64>().unwrap_or(0.0);
581 let memory_gb = memory_mb / 1024.0;
582
583 let gpu_device = DMSCDevice::new(
584 format!("GPU-{}-{}", index + 1, name),
585 DMSCDeviceType::GPU
586 ).with_capabilities(
587 DMSCDeviceCapabilities::new()
588 .with_compute_units(1000) .with_memory_gb(memory_gb)
590 );
591
592 self.add_device(gpu_device, "NVIDIA GPU".to_string()).await?;
593 }
594 }
595 }
596 }
597
598 #[cfg(target_os = "linux")]
599 {
600 if let Ok(nvidia_output) = std::process::Command::new("nvidia-smi")
602 .args(&["--query-gpu=name,memory.total", "--format=csv,noheader"])
603 .output()
604 {
605 let gpu_info = String::from_utf8_lossy(&nvidia_output.stdout);
606
607 for (index, line) in gpu_info.lines().enumerate() {
608 let parts: Vec<&str> = line.split(',').collect();
609 if parts.len() >= 2 {
610 let name = parts[0].trim();
611 let memory_mb = parts[1].trim().replace(" MiB", "").parse::<f64>().unwrap_or(0.0);
612 let memory_gb = memory_mb / 1024.0;
613
614 let gpu_device = DMSCDevice::new(
615 format!("GPU-{}-{}", index + 1, name),
616 DMSCDeviceType::GPU
617 ).with_capabilities(
618 DMSCDeviceCapabilities::new()
619 .with_compute_units(1000) .with_memory_gb(memory_gb)
621 );
622
623 self.add_device(gpu_device, "NVIDIA GPU".to_string()).await?;
624 }
625 }
626 }
627 }
628
629 Ok(vec![])
630 }
631
632 async fn discover_memory_devices(&mut self, _config: &DMSCDeviceControlConfig) -> DMSCResult<()> {
634 #[cfg(target_os = "windows")]
635 {
636 let output = std::process::Command::new("wmic")
637 .args(["memorychip", "get", "Capacity,Speed", "/format:list"])
638 .output()
639 .map_err(|e| DMSCError::DeviceError(format!("Failed to query memory info: {e}")))?;
640
641 let memory_info = String::from_utf8_lossy(&output.stdout);
642
643 let mut total_capacity_gb = 0.0;
644 let mut memory_modules = 0;
645
646 for line in memory_info.lines() {
647 if line.starts_with("Capacity=") {
648 if let Some(capacity_bytes) = line.split('=').nth(1).and_then(|s| s.trim().parse::<u64>().ok()) {
649 total_capacity_gb += capacity_bytes as f64 / (1024.0 * 1024.0 * 1024.0);
650 memory_modules += 1;
651 }
652 }
653 }
654
655 if memory_modules > 0 {
656 let memory_device = DMSCDevice::new(
657 format!("Memory-{}GB-total", total_capacity_gb.round() as u32),
658 DMSCDeviceType::Memory
659 ).with_capabilities(
660 DMSCDeviceCapabilities::new()
661 .with_memory_gb(total_capacity_gb)
662 .with_bandwidth_gbps(25.6) );
664
665 self.add_device(memory_device, "System Memory".to_string()).await?;
666 }
667 }
668
669 #[cfg(target_os = "linux")]
670 {
671 if let Ok(meminfo) = std::fs::read_to_string("/proc/meminfo") {
672 for line in meminfo.lines() {
673 if line.starts_with("MemTotal:") {
674 if let Some(kb_str) = line.split_whitespace().nth(1) {
675 if let Ok(kb) = kb_str.parse::<f64>() {
676 let total_gb = kb / (1024.0 * 1024.0);
677
678 let memory_device = DMSCDevice::new(
679 format!("Memory-{}GB-total", total_gb.round() as u32),
680 DMSCDeviceType::Memory
681 ).with_capabilities(
682 DMSCDeviceCapabilities::new()
683 .with_memory_gb(total_gb)
684 .with_bandwidth_gbps(25.6) );
686
687 self.add_device(memory_device, "System Memory".to_string()).await?;
688 break;
689 }
690 }
691 }
692 }
693 }
694 }
695
696 Ok(())
697 }
698
699 async fn discover_cpu_devices(&mut self, _config: &DMSCDeviceControlConfig) -> DMSCResult<()> {
701 #[cfg(target_os = "windows")]
702 {
703 let output = std::process::Command::new("wmic")
704 .args(["cpu", "get", "Name,NumberOfCores,NumberOfLogicalProcessors", "/format:list"])
705 .output()
706 .map_err(|e| DMSCError::DeviceError(format!("Failed to query CPU info: {e}")))?;
707
708 let cpu_info = String::from_utf8_lossy(&output.stdout);
709
710 let mut cpu_count = 0;
711 let mut total_cores = 0;
712 let mut total_threads = 0;
713
714 for line in cpu_info.lines() {
715 if line.starts_with("Name=") {
716 cpu_count += 1;
717 } else if line.starts_with("NumberOfCores=") {
718 if let Some(cores) = line.split('=').nth(1).and_then(|s| s.trim().parse::<usize>().ok()) {
719 total_cores += cores;
720 }
721 } else if line.starts_with("NumberOfLogicalProcessors=") {
722 if let Some(threads) = line.split('=').nth(1).and_then(|s| s.trim().parse::<usize>().ok()) {
723 total_threads += threads;
724 }
725 }
726 }
727
728 if cpu_count > 0 {
729 let cpu_device = DMSCDevice::new(
730 format!("CPU-{total_cores}-cores-{total_threads}-threads"),
731 DMSCDeviceType::CPU
732 ).with_capabilities(
733 DMSCDeviceCapabilities::new()
734 .with_compute_units(total_cores)
735 .with_memory_gb(0.0)
736 );
737
738 self.add_device(cpu_device, "System Hardware".to_string()).await?;
739 }
740 }
741
742 #[cfg(target_os = "linux")]
743 {
744 let cpu_info = std::fs::read_to_string("/proc/cpuinfo")
746 .map_err(|e| DMSCError::DeviceError(format!("Failed to read cpuinfo: {}", e)))?;
747
748 let mut cpu_count = 0;
749 let mut total_cores = 0;
750
751 for line in cpu_info.lines() {
752 if line.starts_with("processor\t") {
753 cpu_count += 1;
754 } else if line.starts_with("cpu cores\t") {
755 if let Some(cores) = line.split(':').nth(1).and_then(|s| s.trim().parse::<usize>().ok()) {
756 total_cores = cores;
757 }
758 }
759 }
760
761 let total_threads = cpu_count; if cpu_count > 0 {
764 let cpu_device = DMSCDevice::new(
765 format!("CPU-{}-cores-{}-threads", total_cores, total_threads),
766 DMSCDeviceType::CPU
767 ).with_capabilities(
768 DMSCDeviceCapabilities::new()
769 .with_compute_units(total_cores)
770 .with_memory_gb(0.0)
771 );
772
773 self.add_device(cpu_device, "System Hardware".to_string()).await?;
774 }
775 }
776
777 Ok(())
778 }
779
780 async fn discover_storage_devices(&mut self, config: &DMSCDeviceControlConfig) -> DMSCResult<()> {
782 self.discover_storage_devices_impl2(config).await
784 }
785
786 async fn discover_storage_devices_impl2(&mut self, _config: &DMSCDeviceControlConfig) -> DMSCResult<()> {
788 #[cfg(target_os = "windows")]
789 {
790 let output = std::process::Command::new("wmic")
791 .args(["diskdrive", "get", "Model,Size", "/format:list"])
792 .output()
793 .map_err(|e| DMSCError::DeviceError(format!("Failed to query disk info: {e}")))?;
794
795 let disk_info = String::from_utf8_lossy(&output.stdout);
796
797 let mut disk_counter = 0;
798 let lines: Vec<&str> = disk_info.lines().collect();
800
801 for (disk_index, line) in lines.iter().enumerate() {
802 if line.starts_with("Model=") {
803 let model = line.split('=').nth(1).unwrap_or("Unknown").trim();
804 disk_counter += 1;
805
806 if disk_index + 1 < lines.len() && lines[disk_index + 1].starts_with("Size=") {
808 let size_line = lines[disk_index + 1];
809 if let Some(size_bytes) = size_line.split('=').nth(1).and_then(|s| s.trim().parse::<u64>().ok()) {
810 let size_gb = size_bytes as f64 / (1024.0 * 1024.0 * 1024.0);
811
812 let storage_device = DMSCDevice::new(
813 format!("Storage-{disk_counter}-{model}"),
814 DMSCDeviceType::Storage
815 ).with_capabilities(
816 DMSCDeviceCapabilities::new()
817 .with_storage_gb(size_gb)
818 .with_bandwidth_gbps(6.0) );
820
821 self.add_device(storage_device, "System Storage".to_string()).await?;
822 }
823 }
824 }
825 }
826 }
827
828 #[cfg(target_os = "linux")]
829 {
830 if let Ok(entries) = std::fs::read_dir("/sys/block") {
832 for (index, entry) in entries.enumerate() {
833 if let Ok(entry) = entry {
834 let device_name = entry.file_name().to_string_lossy().to_string();
835
836 if device_name.starts_with("loop") || device_name.starts_with("ram") {
838 continue;
839 }
840
841 let size_path = entry.path().join("size");
843 if let Ok(size_str) = std::fs::read_to_string(&size_path) {
844 if let Ok(size_sectors) = size_str.trim().parse::<u64>() {
845 let size_gb = (size_sectors * 512) as f64 / (1024.0 * 1024.0 * 1024.0);
846
847 let storage_device = DMSCDevice::new(
848 format!("Storage-{}-{}", index + 1, device_name),
849 DMSCDeviceType::Storage
850 ).with_capabilities(
851 DMSCDeviceCapabilities::new()
852 .with_storage_gb(size_gb)
853 .with_bandwidth_gbps(6.0) );
855
856 self.add_device(storage_device, "System Storage".to_string()).await?;
857 }
858 }
859 }
860 }
861 }
862 }
863
864 Ok(())
865 }
866
867 async fn discover_hardware_devices(&mut self) -> DMSCResult<Vec<DMSCDevice>> {
869 if let Some(discovery) = &self.discovery {
871 let devices = discovery.discover_all().await?;
872 return Ok(devices);
873 }
874
875 let mut temp_controller = DMSCDeviceController::new();
877 let config = DMSCDeviceControlConfig::default();
878 temp_controller.discover_system_devices(&config).await?;
879 Ok(temp_controller.get_all_devices())
880 }
881
882 async fn discover_network_devices(&mut self, _config: &DMSCDeviceControlConfig) -> DMSCResult<()> {
884 #[cfg(target_os = "windows")]
885 {
886 let output = std::process::Command::new("wmic")
887 .args(["nic", "where", "NetEnabled=true", "get", "Name,Speed", "/format:list"])
888 .output()
889 .map_err(|e| DMSCError::DeviceError(format!("Failed to query network info: {e}")))?;
890
891 let network_info = String::from_utf8_lossy(&output.stdout);
892
893 let mut network_counter = 0;
894 let lines: Vec<&str> = network_info.lines().collect();
896
897 for (network_index, line) in lines.iter().enumerate() {
898 if line.starts_with("Name=") {
899 let name = line.split('=').nth(1).unwrap_or("Unknown").trim();
900 network_counter += 1;
901
902 if let Some(speed_line) = lines.iter().skip(network_index + 1).find(|l| l.starts_with("Speed=")) {
904 if let Some(speed_bps) = speed_line.split('=').nth(1).and_then(|s| s.trim().parse::<u64>().ok()) {
905 let speed_gbps = speed_bps as f64 / (1000.0 * 1000.0 * 1000.0);
906
907 let network_device = DMSCDevice::new(
908 format!("Network-{network_counter}-{name}"),
909 DMSCDeviceType::Network
910 ).with_capabilities(
911 DMSCDeviceCapabilities::new()
912 .with_bandwidth_gbps(speed_gbps)
913 );
914
915 self.add_device(network_device, "System Network".to_string()).await?;
916 }
917 }
918 }
919 }
920 }
921
922 #[cfg(target_os = "linux")]
923 {
924 if let Ok(entries) = std::fs::read_dir("/sys/class/net") {
926 for (index, entry) in entries.enumerate() {
927 if let Ok(entry) = entry {
928 let interface_name = entry.file_name().to_string_lossy().to_string();
929
930 if interface_name == "lo" {
932 continue;
933 }
934
935 let speed_path = entry.path().join("speed");
937 if let Ok(speed_str) = std::fs::read_to_string(&speed_path) {
938 if let Ok(speed_mbps) = speed_str.trim().parse::<f64>() {
939 let speed_gbps = speed_mbps / 1000.0;
940
941 let network_device = DMSCDevice::new(
942 format!("Network-{}-{}", index + 1, interface_name),
943 DMSCDeviceType::Network
944 ).with_capabilities(
945 DMSCDeviceCapabilities::new()
946 .with_bandwidth_gbps(speed_gbps)
947 );
948
949 self.add_device(network_device, "System Network".to_string()).await?;
950 }
951 }
952 }
953 }
954 }
955 }
956
957 Ok(())
958 }
959
960 #[allow(dead_code)]
962 async fn add_device(&mut self, mut device: DMSCDevice, location: String) -> DMSCResult<()> {
963 device.set_status(DMSCDeviceStatus::Available);
964 device.set_location(location);
965
966 let device_id = device.id().to_string();
967 let device_type = device.device_type();
968
969 self.devices.insert(device_id.clone(), Arc::new(RwLock::new(device)));
970 self.device_type_index
971 .entry(device_type)
972 .or_default()
973 .push(device_id);
974
975 Ok(())
976 }
977
978 #[allow(dead_code)]
980 fn create_discovered_device(&self, device_info: &DMSCNetworkDeviceInfo) -> DMSCDevice {
981 let device_type_enum = match device_info.device_type.as_str() {
982 "CPU" => DMSCDeviceType::CPU,
983 "GPU" => DMSCDeviceType::GPU,
984 "Memory" => DMSCDeviceType::Memory,
985 "Storage" => DMSCDeviceType::Storage,
986 "Network" => DMSCDeviceType::Network,
987 _ => DMSCDeviceType::Custom,
988 };
989
990 let name = format!("Discovered-{}-{}", device_info.device_type, device_info.id);
991 let mut device = DMSCDevice::new(name, device_type_enum);
992
993 let mut capabilities = DMSCDeviceCapabilities::new();
995
996 match device_type_enum {
997 DMSCDeviceType::CPU => {
998 capabilities = capabilities
999 .with_compute_units(device_info.compute_units.unwrap_or(8))
1000 .with_memory_gb(device_info.memory_gb.unwrap_or(16.0));
1001 }
1002 DMSCDeviceType::GPU => {
1003 capabilities = capabilities
1004 .with_compute_units(device_info.compute_units.unwrap_or(1000))
1005 .with_memory_gb(device_info.memory_gb.unwrap_or(8.0));
1006 }
1007 DMSCDeviceType::Memory => {
1008 capabilities = capabilities
1009 .with_memory_gb(device_info.memory_gb.unwrap_or(64.0))
1010 .with_bandwidth_gbps(device_info.bandwidth_gbps.unwrap_or(25.6));
1011 }
1012 DMSCDeviceType::Storage => {
1013 capabilities = capabilities
1014 .with_storage_gb(device_info.storage_gb.unwrap_or(1000.0))
1015 .with_bandwidth_gbps(device_info.bandwidth_gbps.unwrap_or(6.0));
1016 }
1017 DMSCDeviceType::Network => {
1018 capabilities = capabilities
1019 .with_bandwidth_gbps(device_info.bandwidth_gbps.unwrap_or(1.0));
1020 }
1021 _ => {}
1022 }
1023
1024 device = device.with_capabilities(capabilities);
1025 device.set_status(DMSCDeviceStatus::Available);
1026 device.set_location(format!("Network Discovery: {}", device_info.source));
1027
1028 device
1029 }
1030
1031 pub async fn find_suitable_device(
1033 &self,
1034 device_type: &DMSCDeviceType,
1035 requirements: &DMSCDeviceCapabilities,
1036 ) -> DMSCResult<Option<DMSCDevice>> {
1037 let device_ids = match self.device_type_index.get(device_type) {
1038 Some(ids) => ids.clone(),
1039 None => return Ok(None),
1040 };
1041
1042 let mut best_device: Option<DMSCDevice> = None;
1044 let mut best_score = 0u32;
1045
1046 for device_id in device_ids {
1047 if let Some(device_lock) = self.devices.get(&device_id) {
1048 let device = device_lock.read().await;
1049
1050 if device.is_available() && device.capabilities().meets_requirements(requirements)
1051 {
1052 let score = self.calculate_device_score(&device);
1053
1054 if score > best_score || best_device.is_none() {
1055 best_device = Some(device.clone());
1056 best_score = score;
1057 }
1058 }
1059 }
1060 }
1061
1062 Ok(best_device)
1063 }
1064
1065 pub fn initialize_metrics(&mut self, metrics_registry: &DMSCMetricsRegistry) -> DMSCResult<()> {
1067 use crate::observability::{DMSCMetric, DMSCMetricConfig, DMSCMetricType};
1068 use std::sync::Arc;
1069
1070 let device_total_config = DMSCMetricConfig {
1072 metric_type: DMSCMetricType::Gauge,
1073 name: "dms_devices_total".to_string(),
1074 help: "Total number of discovered devices".to_string(),
1075 buckets: vec![],
1076 quantiles: vec![],
1077 max_age: std::time::Duration::from_secs(300),
1078 age_buckets: 5,
1079 };
1080 let device_total_metric = Arc::new(DMSCMetric::new(device_total_config));
1081 metrics_registry.register(device_total_metric.clone())?;
1082
1083 for device_type in self.device_type_index.keys() {
1085 let device_type_config = DMSCMetricConfig {
1086 metric_type: DMSCMetricType::Gauge,
1087 name: format!("dms_devices_{}_total", device_type.to_string().to_lowercase()),
1088 help: format!("Total number of {device_type} devices"),
1089 buckets: vec![],
1090 quantiles: vec![],
1091 max_age: std::time::Duration::from_secs(300),
1092 age_buckets: 5,
1093 };
1094 let device_type_metric = Arc::new(DMSCMetric::new(device_type_config));
1095 metrics_registry.register(device_type_metric.clone())?;
1096 }
1097
1098 Ok(())
1099 }
1100
1101 fn calculate_device_score(&self, device: &DMSCDevice) -> u32 {
1102 let mut score = device.health_score() as u32 * 100;
1103
1104 let capabilities = device.capabilities();
1106
1107 if let Some(compute_units) = capabilities.compute_units {
1108 score += compute_units as u32;
1109 }
1110
1111 if let Some(memory_gb) = capabilities.memory_gb {
1112 score += (memory_gb * 10.0) as u32;
1113 }
1114
1115 if let Some(storage_gb) = capabilities.storage_gb {
1116 score += (storage_gb * 5.0) as u32;
1117 }
1118
1119 if let Some(bandwidth_gbps) = capabilities.bandwidth_gbps {
1120 score += (bandwidth_gbps * 20.0) as u32;
1121 }
1122
1123 score
1124 }
1125
1126 pub async fn allocate_device(
1128 &mut self,
1129 device_id: &str,
1130 allocation_id: &str,
1131 ) -> DMSCResult<()> {
1132 if let Some(device_lock) = self.devices.get(device_id) {
1133 let mut device = device_lock.write().await;
1134
1135 if device.allocate(allocation_id) {
1136 self.allocation_map
1137 .insert(allocation_id.to_string(), device_id.to_string());
1138 Ok(())
1139 } else {
1140 Err(crate::core::DMSCError::DeviceAllocationFailed {
1141 device_id: device_id.to_string(),
1142 reason: "Device not available".to_string(),
1143 })
1144 }
1145 } else {
1146 Err(crate::core::DMSCError::DeviceNotFound {
1147 device_id: device_id.to_string(),
1148 })
1149 }
1150 }
1151
1152 pub async fn release_device_by_allocation(&mut self, allocation_id: &str) -> DMSCResult<()> {
1154 if let Some(device_id) = self.allocation_map.remove(allocation_id) {
1155 if let Some(device_lock) = self.devices.get(&device_id) {
1156 let mut device = device_lock.write().await;
1157 device.release();
1158 Ok(())
1159 } else {
1160 Err(crate::core::DMSCError::DeviceNotFound { device_id })
1161 }
1162 } else {
1163 Err(crate::core::DMSCError::AllocationNotFound {
1164 allocation_id: allocation_id.to_string(),
1165 })
1166 }
1167 }
1168
1169 async fn remove_device(&mut self, device_id: &str) -> DMSCResult<()> {
1171 if let Some(device_lock) = self.devices.remove(device_id) {
1172 let device = device_lock.read().await;
1173 let device_type = device.device_type();
1174
1175 if let Some(type_devices) = self.device_type_index.get_mut(&device_type) {
1177 type_devices.retain(|id| id != device_id);
1178 }
1179
1180 if let Some(allocation_id) = device.get_allocation_id() {
1182 self.allocation_map.remove(allocation_id);
1183 }
1184 }
1185
1186 Ok(())
1187 }
1188
1189 pub fn get_all_devices(&self) -> Vec<DMSCDevice> {
1191 let mut devices = Vec::new();
1192
1193 for device_lock in self.devices.values() {
1195 if let Ok(device) = device_lock.try_read() {
1196 devices.push(device.clone());
1197 }
1198 }
1199
1200 devices
1201 }
1202
1203 pub fn release_all_devices(&mut self) -> DMSCResult<()> {
1205 self.allocation_map.clear();
1207
1208 for device_lock in self.devices.values() {
1210 if let Ok(mut device) = device_lock.try_write() {
1211 device.release();
1212 }
1213 }
1214
1215 Ok(())
1216 }
1217
1218 pub async fn perform_health_checks(&mut self) -> DMSCResult<Vec<(String, u8)>> {
1220 let mut results = Vec::new();
1221
1222 for (device_id, device_lock) in &self.devices {
1223 let mut device = device_lock.write().await;
1224
1225 let mut health_metrics = device.health_metrics().clone();
1227
1228 health_metrics.cpu_usage_percent = rand::random::<f64>() * 100.0;
1230 health_metrics.memory_usage_percent = rand::random::<f64>() * 100.0;
1231
1232 health_metrics.temperature_celsius = rand::random::<f64>() * 50.0 + 30.0;
1234
1235 if rand::random::<f64>() < 0.01 {
1237 health_metrics.error_count += 1;
1239 }
1240
1241 health_metrics.throughput = rand::random::<u64>() % 1000;
1243
1244 health_metrics.network_latency_ms = rand::random::<f64>() * 200.0;
1246
1247 health_metrics.disk_iops = (rand::random::<f64>() * 500.0) as u64;
1249
1250 health_metrics.battery_level_percent = rand::random::<f64>() * 100.0;
1252
1253 health_metrics.response_time_ms = rand::random::<f64>() * 150.0;
1255
1256 health_metrics.uptime_seconds += 30;
1258
1259 device.update_health_metrics(health_metrics);
1261
1262 let health_score = device.dynamic_health_score(device.health_metrics());
1264
1265 if health_score < 20 {
1267 device.set_status(DMSCDeviceStatus::Error);
1268 } else if health_score < 50 {
1269 device.set_status(DMSCDeviceStatus::Maintenance);
1270 } else if health_score < 70 {
1271 device.set_status(DMSCDeviceStatus::Degraded);
1272 } else if device.status() == DMSCDeviceStatus::Error
1273 || device.status() == DMSCDeviceStatus::Maintenance
1274 || device.status() == DMSCDeviceStatus::Degraded
1275 {
1276 device.set_status(DMSCDeviceStatus::Available);
1277 }
1278
1279 results.push((device_id.to_string(), health_score));
1280 }
1281
1282 Ok(results)
1283 }
1284
1285 pub async fn start_health_checks(&self, interval_secs: u64) -> tokio::task::JoinHandle<()> {
1287 let devices = self.devices.clone();
1288
1289 tokio::spawn(async move {
1290 let mut interval = tokio::time::interval(tokio::time::Duration::from_secs(interval_secs));
1291
1292 loop {
1293 interval.tick().await;
1294
1295 for device_lock in devices.values() {
1296 let mut device = device_lock.write().await;
1297
1298 let mut health_metrics = device.health_metrics().clone();
1300
1301 health_metrics.cpu_usage_percent = rand::random::<f64>() * 100.0;
1303 health_metrics.memory_usage_percent = rand::random::<f64>() * 100.0;
1304
1305 health_metrics.temperature_celsius = rand::random::<f64>() * 50.0 + 30.0;
1307
1308 if rand::random::<f64>() < 0.01 {
1310 health_metrics.error_count += 1;
1312 }
1313
1314 health_metrics.throughput = rand::random::<u64>() % 1000;
1316
1317 health_metrics.network_latency_ms = rand::random::<f64>() * 200.0;
1319
1320 health_metrics.disk_iops = (rand::random::<f64>() * 500.0) as u64;
1322
1323 health_metrics.battery_level_percent = rand::random::<f64>() * 100.0;
1325
1326 health_metrics.response_time_ms = rand::random::<f64>() * 150.0;
1328
1329 health_metrics.uptime_seconds += interval_secs as u64;
1331
1332 device.update_health_metrics(health_metrics);
1334
1335 let health_score = device.dynamic_health_score(device.health_metrics());
1337
1338 if health_score < 20 {
1340 device.set_status(DMSCDeviceStatus::Error);
1341 } else if health_score < 50 {
1342 device.set_status(DMSCDeviceStatus::Maintenance);
1343 } else if health_score < 70 {
1344 device.set_status(DMSCDeviceStatus::Degraded);
1345 } else if device.status() == DMSCDeviceStatus::Error
1346 || device.status() == DMSCDeviceStatus::Maintenance
1347 || device.status() == DMSCDeviceStatus::Degraded
1348 {
1349 device.set_status(DMSCDeviceStatus::Available);
1350 }
1351 }
1352 }
1353 })
1354 }
1355
1356 pub async fn get_device_health(
1358 &self,
1359 device_id: &str,
1360 ) -> DMSCResult<super::core::DMSCDeviceHealthMetrics> {
1361 if let Some(device_lock) = self.devices.get(device_id) {
1362 let device = device_lock.read().await;
1363 Ok(device.health_metrics().clone())
1364 } else {
1365 Err(crate::core::DMSCError::DeviceNotFound {
1366 device_id: device_id.to_string(),
1367 })
1368 }
1369 }
1370
1371 pub async fn get_all_device_health(
1373 &self,
1374 ) -> DMSCResult<HashMap<String, super::core::DMSCDeviceHealthMetrics>> {
1375 let mut health_map = HashMap::new();
1376
1377 for (device_id, device_lock) in &self.devices {
1378 let device = device_lock.read().await;
1379 health_map.insert(device_id.to_string(), device.health_metrics().clone());
1380 }
1381
1382 Ok(health_map)
1383 }
1384
1385 fn create_mock_device_for_discovery(&self) -> DMSCDevice {
1387 use super::core::{DMSCDeviceCapabilities, DMSCDeviceType};
1388
1389 let device_types = [DMSCDeviceType::CPU,
1390 DMSCDeviceType::GPU,
1391 DMSCDeviceType::Memory,
1392 DMSCDeviceType::Storage,
1393 DMSCDeviceType::Network];
1394
1395 let device_type = device_types[rand::random::<usize>() % device_types.len()];
1396
1397 let device_name = match device_type {
1398 DMSCDeviceType::CPU => format!("CPU-{}-cores", rand::random::<usize>() % 32 + 1),
1399 DMSCDeviceType::GPU => format!("GPU-{}-GB", rand::random::<usize>() % 24 + 1),
1400 DMSCDeviceType::Memory => format!("Memory-{}-GB", rand::random::<usize>() % 64 + 1),
1401 DMSCDeviceType::Storage => format!("Storage-{}-TB", rand::random::<usize>() % 10 + 1),
1402 DMSCDeviceType::Network => format!("Network-{}-Gbps", rand::random::<usize>() % 100 + 1),
1403 DMSCDeviceType::Sensor => format!("Sensor-{}-units", rand::random::<usize>() % 100 + 1),
1404 DMSCDeviceType::Actuator => format!("Actuator-{}-actions", rand::random::<usize>() % 50 + 1),
1405 DMSCDeviceType::Custom => format!("Custom-{}-device", rand::random::<usize>() % 1000 + 1),
1406 };
1407
1408 let capabilities = DMSCDeviceCapabilities::new()
1409 .with_compute_units(rand::random::<usize>() % 1000 + 100)
1410 .with_memory_gb(rand::random::<f64>() * 64.0 + 1.0);
1411
1412 DMSCDevice::new(device_name, device_type)
1413 .with_capabilities(capabilities)
1414 }
1415}