1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
use anyhow::Result;
use sysinfo::{DiskExt, CpuExt, System, SystemExt};
use nvml_wrapper::Nvml;
use nvml_wrapper::enum_wrappers::device::TemperatureSensor;
use log::{debug, info};
use crate::model::{SystemInfo, Processor, Disk, GraphicCard, GraphicsUsage, GraphicsProcessUtilization, SystemStatus, Process, Camera, NvidiaInfo};
use crate::monitor::Monitor;
use std::path::Path;

#[cfg(feature = "v4l")]
use crate::camera::list_cameras;

#[cfg(not(feature = "v4l"))]
fn list_cameras() -> Vec<Camera> {
    vec![]
}

/// Represents a machine. Currently you can monitor global CPU/Memory usage, processes CPU usage and the
/// Nvidia GPU usage. You can also retrieve information about CPU, disks...
pub struct Machine {
    monitor: Monitor,
    nvml: Option<nvml_wrapper::Nvml>,
}


impl Machine {
    /// Creates a new instance of Machine. If not graphic card it will warn about it but not an error
    /// Example
    /// ```
    /// use machine_info::Machine;
    /// let m = Machine::new();
    /// ```
    pub fn new() -> Machine{
        let nvml = match Nvml::init() {
            Ok(nvml) => {
                info!("Nvidia driver loaded");
                Some(nvml)
            },
            Err(error) => {
                debug!("Nvidia not available because {}", error);
                None
            }
        };
        Machine{
            monitor: Monitor::new(),
            nvml: nvml
        }
    }
    
    /// Retrieves full information about the computer
    /// Example
    /// ```
    /// use machine_info::Machine;
    /// let m = Machine::new();
    /// println!("{:?}", m.system_info())
    /// ```
    pub fn system_info(& mut self) -> SystemInfo {
        let sys = System::new_all();
        //let mut processors = Vec::new();
        let processor = sys.global_cpu_info();
        let processor = Processor{
            frequency: processor.frequency(),
            vendor: processor.vendor_id().to_string(),
            brand: processor.brand().to_string()
        };


        let mut disks = Vec::new();
        for disk in sys.disks() {
            disks.push(Disk{
                name: disk.name().to_str().unwrap().to_string(),
                fs: String::from_utf8(disk.file_system().to_vec()).unwrap(),
                storage_type: match disk.type_() {
                    sysinfo::DiskType::HDD => "HDD".to_string(),
                    sysinfo::DiskType::SSD => "SSD".to_string(),
                    _ => "Unknown".to_string()
                },
                available: disk.available_space(),
                size: disk.total_space(),
                mount_point: disk.mount_point().to_str().unwrap().to_string()
            })
        }

        let mut cards = Vec::new();
        let nvidia = if let Some(nvml) = &self.nvml {
            for n in 0..nvml.device_count().unwrap() {
                let device = nvml.device_by_index(n).unwrap();
                cards.push(GraphicCard{
                    id: device.uuid().unwrap(),
                    name: device.name().unwrap(),
                    brand: match device.brand().unwrap() {
                        nvml_wrapper::enum_wrappers::device::Brand::GeForce => "GeForce".to_string(),
                        nvml_wrapper::enum_wrappers::device::Brand::Quadro => "Quadro".to_string(),
                        nvml_wrapper::enum_wrappers::device::Brand::Tesla => "Tesla".to_string(),
                        nvml_wrapper::enum_wrappers::device::Brand::Titan => "Titan".to_string(),
                        nvml_wrapper::enum_wrappers::device::Brand::NVS => "NVS".to_string(),
                        nvml_wrapper::enum_wrappers::device::Brand::GRID => "GRID".to_string(),
                        nvml_wrapper::enum_wrappers::device::Brand::Unknown => "Unknown".to_string(),
                    },
                    memory: device.memory_info().unwrap().total,
                    temperature: device.temperature(nvml_wrapper::enum_wrappers::device::TemperatureSensor::Gpu).unwrap()
                });
            }
            Some(NvidiaInfo {
                driver_version: nvml.sys_driver_version().unwrap(),
                nvml_version: nvml.sys_nvml_version().unwrap(),
                cuda_version: nvml.sys_cuda_driver_version().unwrap()
            })
        } else {
            None
        };
        
        // Getting the model
        let model_path = Path::new("/sys/firmware/devicetree/base/model");
        let model = if model_path.exists() {
            Some(std::fs::read_to_string(model_path).unwrap())
        } else {
            None
        };
        
        let vaapi = Path::new("/dev/dri/renderD128").exists();

        SystemInfo {
            os_name: sys.name().unwrap(),
            kernel_version: sys.kernel_version().unwrap(),
            os_version: sys.os_version().unwrap(),
            distribution: sys.distribution_id(),
            hostname: sys.host_name().unwrap(),
            memory: sys.total_memory(),
            nvidia,
            vaapi,
            processor,
            total_processors: sys.cpus().len(),
            graphics: cards,
            disks,
            cameras: list_cameras(),
            model
        }
    }

    /*pub fn disks_status(&self) {
        //TODO
        /*
        let mut disks = Vec::new();
        for disk in self.sys.disks() {
            disks.push(api::model::Disk{
            })
            */
    }*/

    /// The current usage of all graphic cards (if any)
    /// Example
    /// ```
    /// use machine_info::Machine;
    /// let m = Machine::new();
    /// println!("{:?}", m.graphics_status())
    /// ```
    pub fn graphics_status(&self) -> Vec<GraphicsUsage> {
        let mut cards = Vec::new();
        if let Some(nvml) = &self.nvml {
            for n in 0..nvml.device_count().unwrap() {
                let device = nvml.device_by_index(n).unwrap();
                let mut processes = Vec::new();
                for p in device.process_utilization_stats(None).unwrap() {
                    processes.push(GraphicsProcessUtilization{
                         pid: p.pid,
                        gpu: p.sm_util,
                        memory: p.mem_util,
                        encoder: p.enc_util,
                        decoder: p.dec_util
                    });
                }
    
                cards.push(GraphicsUsage {
                    id: device.uuid().unwrap(),
                    memory_used: device.memory_info().unwrap().used,
                    encoder: device.encoder_utilization().unwrap().utilization,
                    decoder: device.decoder_utilization().unwrap().utilization,
                    gpu: device.utilization_rates().unwrap().gpu,
                    memory_usage: device.utilization_rates().unwrap().memory,
                    temperature: device.temperature(TemperatureSensor::Gpu).unwrap(),
                    processes
                });
            }
        }
        
        cards
        
    }


    /// 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.
    /// 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
    /// Example
    /// ```
    /// use machine_info::Machine;
    /// let m = Machine::new();
    /// let process_pid = 3218;
    /// m.track_process(process_pid)
    /// ```
    pub fn track_process(&mut self, pid: i32) -> Result<()>{
        self.monitor.track_process(pid)
    }

    /// 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.
    /// If the PID was not registered before, it will just do nothing
    /// Example
    /// ```
    /// use machine_info::Machine;
    /// let m = Machine::new();
    /// let process_pid = 3218;
    /// m.track_process(process_pid)
    /// m.untrack_process(process_pid)
    /// ```
    pub fn untrack_process(&mut self, pid: i32) {
        self.monitor.untrack_process(pid);
    }

    /// The CPU usage of all tracked processes since the last call. So if you call it every 10 seconds, you will
    /// get the CPU usage during the last 10 seconds. More calls will make the value more accurate but also more expensive
    /// Example
    /// ```
    /// use machine_info::Machine;
    /// use std::{thread, time};
    /// 
    /// let m = Machine::new();
    /// m.track_process(3218)
    /// m.track_process(4467)
    /// loop {   
    ///   let status = m.processes_status();
    ///   println!("{:?}", status);
    ///   thread::sleep(time::Duration::from_millis(1000));
    /// }
    /// 
    /// ```
    pub fn processes_status(& mut self) -> Vec<Process> {
        self.monitor.next_processes().iter().map(|(pid, cpu)| Process{pid:*pid, cpu:*cpu}).collect::<Vec<Process>>()
    }

    /// The CPU and memory usage. For the CPU, it is the same as for `processes_status`. For the memory it returs the amount
    /// a this moment
    /// Example
    /// ```
    /// use machine_info::Machine;
    /// use std::{thread, time};
    /// 
    /// let m = Machine::new();
    /// m.track_process(3218)
    /// m.track_process(4467)
    /// loop {   
    ///   let status = m.system_status();
    ///   println!("{:?}", status);
    ///   thread::sleep(time::Duration::from_millis(1000));
    /// }
    /// 
    /// ```
    pub fn system_status(& mut self) -> Result<SystemStatus> {
        let (cpu, memory) = self.monitor.next()?;
        Ok(SystemStatus {
            memory,
            cpu,
        })
    }

}