machine_info/
machine.rs

1use anyhow::Result;
2use sysinfo::{DiskExt, CpuExt, System, SystemExt};
3use nvml_wrapper::Nvml;
4use nvml_wrapper::enum_wrappers::device::TemperatureSensor;
5use log::{debug, info};
6use crate::model::{SystemInfo, Processor, Disk, GraphicCard, GraphicsUsage, GraphicsProcessUtilization, SystemStatus, Process, Camera, NvidiaInfo};
7use crate::monitor::Monitor;
8use std::path::Path;
9
10#[cfg(feature = "v4l")]
11use crate::camera::list_cameras;
12
13#[cfg(not(feature = "v4l"))]
14fn list_cameras() -> Vec<Camera> {
15    vec![]
16}
17
18/// Represents a machine. Currently you can monitor global CPU/Memory usage, processes CPU usage and the
19/// Nvidia GPU usage. You can also retrieve information about CPU, disks...
20pub struct Machine {
21    monitor: Monitor,
22    nvml: Option<nvml_wrapper::Nvml>,
23}
24
25
26impl Machine {
27    /// Creates a new instance of Machine. If not graphic card it will warn about it but not an error
28    /// Example
29    /// ```
30    /// use machine_info::Machine;
31    /// let m = Machine::new();
32    /// ```
33    pub fn new() -> Machine{
34        let nvml = match Nvml::init() {
35            Ok(nvml) => {
36                info!("Nvidia driver loaded");
37                Some(nvml)
38            },
39            Err(error) => {
40                debug!("Nvidia not available because {}", error);
41                None
42            }
43        };
44        Machine{
45            monitor: Monitor::new(),
46            nvml: nvml
47        }
48    }
49    
50    /// Retrieves full information about the computer
51    /// Example
52    /// ```
53    /// use machine_info::Machine;
54    /// let m = Machine::new();
55    /// println!("{:?}", m.system_info())
56    /// ```
57    pub fn system_info(& mut self) -> SystemInfo {
58        let sys = System::new_all();
59        //let mut processors = Vec::new();
60        let processor = sys.global_cpu_info();
61        let processor = Processor{
62            frequency: processor.frequency(),
63            vendor: processor.vendor_id().to_string(),
64            brand: processor.brand().to_string()
65        };
66
67
68        let mut disks = Vec::new();
69        for disk in sys.disks() {
70            disks.push(Disk{
71                name: disk.name().to_str().unwrap().to_string(),
72                fs: String::from_utf8(disk.file_system().to_vec()).unwrap(),
73                storage_type: match disk.type_() {
74                    sysinfo::DiskType::HDD => "HDD".to_string(),
75                    sysinfo::DiskType::SSD => "SSD".to_string(),
76                    _ => "Unknown".to_string()
77                },
78                available: disk.available_space(),
79                size: disk.total_space(),
80                mount_point: disk.mount_point().to_str().unwrap().to_string()
81            })
82        }
83
84        let mut cards = Vec::new();
85        let nvidia = if let Some(nvml) = &self.nvml {
86            for n in 0..nvml.device_count().unwrap() {
87                let device = nvml.device_by_index(n).unwrap();
88                cards.push(GraphicCard{
89                    id: device.uuid().unwrap(),
90                    name: device.name().unwrap(),
91                    brand: match device.brand().unwrap() {
92                        nvml_wrapper::enum_wrappers::device::Brand::GeForce => "GeForce".to_string(),
93                        nvml_wrapper::enum_wrappers::device::Brand::Quadro => "Quadro".to_string(),
94                        nvml_wrapper::enum_wrappers::device::Brand::Tesla => "Tesla".to_string(),
95                        nvml_wrapper::enum_wrappers::device::Brand::Titan => "Titan".to_string(),
96                        nvml_wrapper::enum_wrappers::device::Brand::NVS => "NVS".to_string(),
97                        nvml_wrapper::enum_wrappers::device::Brand::GRID => "GRID".to_string(),
98                        nvml_wrapper::enum_wrappers::device::Brand::Unknown => "Unknown".to_string(),
99                    },
100                    memory: device.memory_info().unwrap().total,
101                    temperature: device.temperature(nvml_wrapper::enum_wrappers::device::TemperatureSensor::Gpu).unwrap()
102                });
103            }
104            Some(NvidiaInfo {
105                driver_version: nvml.sys_driver_version().unwrap(),
106                nvml_version: nvml.sys_nvml_version().unwrap(),
107                cuda_version: nvml.sys_cuda_driver_version().unwrap()
108            })
109        } else {
110            None
111        };
112        
113        // Getting the model
114        let model_path = Path::new("/sys/firmware/devicetree/base/model");
115        let model = if model_path.exists() {
116            Some(std::fs::read_to_string(model_path).unwrap())
117        } else {
118            None
119        };
120        
121        let vaapi = Path::new("/dev/dri/renderD128").exists();
122
123        SystemInfo {
124            os_name: sys.name().unwrap(),
125            kernel_version: sys.kernel_version().unwrap(),
126            os_version: sys.os_version().unwrap(),
127            distribution: sys.distribution_id(),
128            hostname: sys.host_name().unwrap(),
129            memory: sys.total_memory(),
130            nvidia,
131            vaapi,
132            processor,
133            total_processors: sys.cpus().len(),
134            graphics: cards,
135            disks,
136            cameras: list_cameras(),
137            model
138        }
139    }
140
141    /*pub fn disks_status(&self) {
142        //TODO
143        /*
144        let mut disks = Vec::new();
145        for disk in self.sys.disks() {
146            disks.push(api::model::Disk{
147            })
148            */
149    }*/
150
151    /// The current usage of all graphic cards (if any)
152    /// Example
153    /// ```
154    /// use machine_info::Machine;
155    /// let m = Machine::new();
156    /// println!("{:?}", m.graphics_status())
157    /// ```
158    pub fn graphics_status(&self) -> Vec<GraphicsUsage> {
159        let mut cards = Vec::new();
160        if let Some(nvml) = &self.nvml {
161            for n in 0..nvml.device_count().unwrap() {
162                let device = nvml.device_by_index(n).unwrap();
163                let mut processes = Vec::new();
164                for p in device.process_utilization_stats(None).unwrap() {
165                    processes.push(GraphicsProcessUtilization{
166                         pid: p.pid,
167                        gpu: p.sm_util,
168                        memory: p.mem_util,
169                        encoder: p.enc_util,
170                        decoder: p.dec_util
171                    });
172                }
173    
174                cards.push(GraphicsUsage {
175                    id: device.uuid().unwrap(),
176                    memory_used: device.memory_info().unwrap().used,
177                    encoder: device.encoder_utilization().unwrap().utilization,
178                    decoder: device.decoder_utilization().unwrap().utilization,
179                    gpu: device.utilization_rates().unwrap().gpu,
180                    memory_usage: device.utilization_rates().unwrap().memory,
181                    temperature: device.temperature(TemperatureSensor::Gpu).unwrap(),
182                    processes
183                });
184            }
185        }
186        
187        cards
188        
189    }
190
191
192    /// To calculate the CPU usage of a process we have to keep track in time the process so first we have to register the process.
193    /// You need to know the PID of your process and use it as parameters. In case you provide an invalid PID it will return error
194    /// Example
195    /// ```
196    /// use machine_info::Machine;
197    /// let m = Machine::new();
198    /// let process_pid = 3218;
199    /// m.track_process(process_pid)
200    /// ```
201    pub fn track_process(&mut self, pid: i32) -> Result<()>{
202        self.monitor.track_process(pid)
203    }
204
205    /// Once we dont need to track a process it is recommended to not keep using resources on it. You should know the PID of your process.
206    /// If the PID was not registered before, it will just do nothing
207    /// Example
208    /// ```
209    /// use machine_info::Machine;
210    /// let m = Machine::new();
211    /// let process_pid = 3218;
212    /// m.track_process(process_pid)
213    /// m.untrack_process(process_pid)
214    /// ```
215    pub fn untrack_process(&mut self, pid: i32) {
216        self.monitor.untrack_process(pid);
217    }
218
219    /// The CPU usage of all tracked processes since the last call. So if you call it every 10 seconds, you will
220    /// get the CPU usage during the last 10 seconds. More calls will make the value more accurate but also more expensive
221    /// Example
222    /// ```
223    /// use machine_info::Machine;
224    /// use std::{thread, time};
225    /// 
226    /// let m = Machine::new();
227    /// m.track_process(3218)
228    /// m.track_process(4467)
229    /// loop {   
230    ///   let status = m.processes_status();
231    ///   println!("{:?}", status);
232    ///   thread::sleep(time::Duration::from_millis(1000));
233    /// }
234    /// 
235    /// ```
236    pub fn processes_status(& mut self) -> Vec<Process> {
237        self.monitor.next_processes().iter().map(|(pid, cpu)| Process{pid:*pid, cpu:*cpu}).collect::<Vec<Process>>()
238    }
239
240    /// The CPU and memory usage. For the CPU, it is the same as for `processes_status`. For the memory it returs the amount
241    /// a this moment
242    /// Example
243    /// ```
244    /// use machine_info::Machine;
245    /// use std::{thread, time};
246    /// 
247    /// let m = Machine::new();
248    /// m.track_process(3218)
249    /// m.track_process(4467)
250    /// loop {   
251    ///   let status = m.system_status();
252    ///   println!("{:?}", status);
253    ///   thread::sleep(time::Duration::from_millis(1000));
254    /// }
255    /// 
256    /// ```
257    pub fn system_status(& mut self) -> Result<SystemStatus> {
258        let (cpu, memory) = self.monitor.next()?;
259        Ok(SystemStatus {
260            memory,
261            cpu,
262        })
263    }
264
265}