1use crate::{
2 BatteryInfo, CPUInfo, GPUInfo, HardwareQueryError,
3 MemoryInfo, NetworkInfo, NPUInfo, PCIDevice, Result, StorageInfo, ThermalInfo, TPUInfo, USBDevice,
4 ARMHardwareInfo, FPGAInfo, PowerProfile, VirtualizationInfo,
5};
6use serde::{Deserialize, Serialize};
7use std::time::{SystemTime, UNIX_EPOCH};
8use std::collections::HashMap;
9
10#[derive(Debug, Clone, Serialize, Deserialize)]
12pub struct HardwareInfo {
13 pub timestamp: u64,
15 pub cpu: CPUInfo,
17 pub gpus: Vec<GPUInfo>,
19 pub npus: Vec<NPUInfo>,
21 pub tpus: Vec<TPUInfo>,
23 pub arm_hardware: Option<ARMHardwareInfo>,
25 pub fpgas: Vec<FPGAInfo>,
27 pub memory: MemoryInfo,
29 pub storage_devices: Vec<StorageInfo>,
31 pub network_interfaces: Vec<NetworkInfo>,
33 pub battery: Option<BatteryInfo>,
35 pub thermal: ThermalInfo,
37 pub pci_devices: Vec<PCIDevice>,
39 pub usb_devices: Vec<USBDevice>,
41 pub power_profile: Option<PowerProfile>,
43 pub virtualization: VirtualizationInfo,
45}
46
47impl HardwareInfo {
48 pub fn query() -> Result<Self> {
50 let timestamp = SystemTime::now()
51 .duration_since(UNIX_EPOCH)
52 .map_err(|e| HardwareQueryError::unknown(format!("Failed to get timestamp: {e}")))?
53 .as_secs();
54
55 Ok(Self {
56 timestamp,
57 cpu: CPUInfo::query()?,
58 gpus: GPUInfo::query_all()?,
59 npus: NPUInfo::query_all()?,
60 tpus: TPUInfo::query_all()?,
61 arm_hardware: ARMHardwareInfo::detect().ok().flatten(),
62 fpgas: FPGAInfo::detect_fpgas().unwrap_or_default(),
63 memory: MemoryInfo::query()?,
64 storage_devices: StorageInfo::query_all()?,
65 network_interfaces: NetworkInfo::query_all()?,
66 battery: BatteryInfo::query().ok(),
67 thermal: ThermalInfo::query()?,
68 pci_devices: PCIDevice::query_all()?,
69 usb_devices: USBDevice::query_all()?,
70 power_profile: PowerProfile::query().ok(),
71 virtualization: VirtualizationInfo::detect()?,
72 })
73 }
74
75 pub fn cpu(&self) -> &CPUInfo {
77 &self.cpu
78 }
79
80 pub fn gpus(&self) -> &[GPUInfo] {
82 &self.gpus
83 }
84
85 pub fn npus(&self) -> &[NPUInfo] {
87 &self.npus
88 }
89
90 pub fn tpus(&self) -> &[TPUInfo] {
92 &self.tpus
93 }
94
95 pub fn arm_hardware(&self) -> Option<&ARMHardwareInfo> {
97 self.arm_hardware.as_ref()
98 }
99
100 pub fn fpgas(&self) -> &[FPGAInfo] {
102 &self.fpgas
103 }
104
105 pub fn memory(&self) -> &MemoryInfo {
107 &self.memory
108 }
109
110 pub fn storage_devices(&self) -> &[StorageInfo] {
112 &self.storage_devices
113 }
114
115 pub fn network_interfaces(&self) -> &[NetworkInfo] {
117 &self.network_interfaces
118 }
119
120 pub fn battery(&self) -> Option<&BatteryInfo> {
122 self.battery.as_ref()
123 }
124
125 pub fn thermal(&self) -> &ThermalInfo {
127 &self.thermal
128 }
129
130 pub fn pci_devices(&self) -> &[PCIDevice] {
132 &self.pci_devices
133 }
134
135 pub fn usb_devices(&self) -> &[USBDevice] {
137 &self.usb_devices
138 }
139
140 pub fn power_profile(&self) -> Option<&PowerProfile> {
142 self.power_profile.as_ref()
143 }
144
145 pub fn virtualization(&self) -> &VirtualizationInfo {
147 &self.virtualization
148 }
149
150 pub fn is_arm_system(&self) -> bool {
152 self.arm_hardware.is_some()
153 }
154
155 pub fn has_fpgas(&self) -> bool {
157 !self.fpgas.is_empty()
158 }
159
160 pub fn is_virtualized(&self) -> bool {
162 self.virtualization.is_virtualized()
163 }
164
165 pub fn is_containerized(&self) -> bool {
167 self.virtualization.is_containerized()
168 }
169
170 pub fn virtualization_performance_impact(&self) -> f64 {
172 self.virtualization.get_performance_factor()
173 }
174
175 pub fn accelerator_count(&self) -> usize {
177 self.npus.len() + self.tpus.len() + self.fpgas.len()
178 }
179
180 pub fn accelerator_summary(&self) -> HashMap<String, usize> {
182 let mut summary = HashMap::new();
183
184 if !self.npus.is_empty() {
185 summary.insert("NPUs".to_string(), self.npus.len());
186 }
187 if !self.tpus.is_empty() {
188 summary.insert("TPUs".to_string(), self.tpus.len());
189 }
190 if !self.fpgas.is_empty() {
191 summary.insert("FPGAs".to_string(), self.fpgas.len());
192 }
193
194 summary
195 }
196
197 pub fn to_json(&self) -> Result<String> {
199 serde_json::to_string_pretty(self).map_err(Into::into)
200 }
201
202 pub fn from_json(json: &str) -> Result<Self> {
204 serde_json::from_str(json).map_err(Into::into)
205 }
206
207 pub fn summary(&self) -> HardwareSummary {
209 HardwareSummary {
210 cpu_model: format!("{} {}", self.cpu.vendor(), self.cpu.model_name()),
211 cpu_cores: self.cpu.physical_cores(),
212 cpu_threads: self.cpu.logical_cores(),
213 total_memory_gb: self.memory.total_gb(),
214 primary_gpu: self.gpus.first().map(|gpu| {
215 format!(
216 "{} {} ({} GB)",
217 gpu.vendor(),
218 gpu.model_name(),
219 gpu.memory_gb()
220 )
221 }),
222 storage_total_gb: self
223 .storage_devices
224 .iter()
225 .map(|storage| storage.capacity_gb())
226 .sum(),
227 }
228 }
229}
230
231#[derive(Debug, Clone, Serialize, Deserialize)]
233pub struct HardwareSummary {
234 pub cpu_model: String,
235 pub cpu_cores: u32,
236 pub cpu_threads: u32,
237 pub total_memory_gb: f64,
238 pub primary_gpu: Option<String>,
239 pub storage_total_gb: f64,
240}
241
242impl std::fmt::Display for HardwareSummary {
243 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
244 writeln!(f, "Hardware Summary:")?;
245 writeln!(
246 f,
247 " CPU: {} ({} cores, {} threads)",
248 self.cpu_model, self.cpu_cores, self.cpu_threads
249 )?;
250 writeln!(f, " Memory: {:.1} GB", self.total_memory_gb)?;
251 if let Some(gpu) = &self.primary_gpu {
252 writeln!(f, " Primary GPU: {gpu}")?;
253 }
254 writeln!(f, " Total Storage: {:.1} GB", self.storage_total_gb)?;
255 Ok(())
256 }
257}