vjoule_api/
lib.rs

1extern crate inotify;
2
3use std::time::{SystemTime};
4use std::fs;
5use std::fs::File;
6use std::io::Write;
7use subprocess::Exec;
8
9use inotify::{
10    Inotify,
11    WatchMask,
12};
13
14
15pub mod vjoule_api;
16use crate::vjoule_api::stamp::{ConsumptionStamp};
17use crate::vjoule_api::error::VJouleError;
18use crate::vjoule_api::constants::{VJOULE_RES_PATH, VJOULE_PATH};
19
20/**
21 * Main element of the vjoule api
22 */
23pub struct VJouleAPI {
24}
25
26impl VJouleAPI {    
27
28    /// Create and configure the vjoule api Create a this process_group refering to the pid of the
29    /// current process (and all its threads / forks) To work properly the vjoule service has to be
30    /// running
31    ///
32    /// # Example 
33    /// ```
34    /// let mut api = VJouleAPI::new ()?;
35    /// let machine_conso = api.get_current_machine_consumption ()?;
36    ///
37    /// println!("Current machine consumption : {}", machine_conso);
38    /// ```
39    ///
40    /// # Returns
41    /// 
42    /// - On success, the vjoule api
43    /// - On error, a message explaining the error
44    pub fn new ()-> Result <VJouleAPI, VJouleError> {
45	    let p = Exec::shell ("systemctl is-active --quiet vjoule_service.service").capture ().map_err (|_e| "Failed to execute bash command")?;
46	    match p.exit_status {
47	        subprocess::ExitStatus::Exited (0) => {}
48	        _ => {
49		        return Err ("vJoule service is not started. sudo systemctl start vjoule_service.service");
50	        }
51	    }
52
53	    Ok (VJouleAPI {})
54    }
55
56    ///
57    /// # Returns
58    /// The consumption of the machine since the start of the vjoule service
59    ///
60    /// # Information
61    /// 
62    /// This function make sure the vjoule service has written a consumption before returning. It
63    /// forces the service to perform an iteration to avoid waiting too long, but it can take a bit
64    /// of time (some milliseconds).
65    ///
66    pub fn get_current_machine_consumption (&mut self)-> Result<ConsumptionStamp, VJouleError> {
67	    self.force_signal ();
68	    self.get_current_machine_consumption_no_force ()
69    }
70
71    ///
72    /// # Returns
73    /// The consumption of the machine since the start of the vjoule service
74    ///
75    ///
76    pub fn get_current_machine_consumption_no_force (&mut self)-> Result<ConsumptionStamp, VJouleError> {
77	    let cpu_path = format!("{}/cpu", VJOULE_RES_PATH);
78	    let ram_path = format!("{}/ram", VJOULE_RES_PATH);
79	    let gpu_path = format!("{}/gpu", VJOULE_RES_PATH);
80        let pdu_e_path = format!("{}/pdu_energy", VJOULE_RES_PATH);
81        let pdu_p_path = format!("{}/pdu_power", VJOULE_RES_PATH);
82
83        let cpu = self.read_value (&cpu_path[..]);
84        let ram = self.read_value (&ram_path[..]);
85	    let gpu = self.read_value (&gpu_path[..]);
86        let pdu_e = self.read_value (&pdu_e_path[..]);
87        let pdu_p = self.read_value (&pdu_p_path[..]);
88
89	    Ok (ConsumptionStamp {
90	        time : SystemTime::now (),
91            pdu_watts : pdu_p,
92            pdu : pdu_e,
93	        cpu : cpu,
94	        ram : ram,
95	        gpu : gpu
96	    })
97    }
98
99    ///
100    /// # Returns
101    /// the value read in the file at path 'file' if exists and contains a double, 0 otherwise
102    ///
103    fn read_value (&mut self, file : &str)-> f64 {
104        match fs::read_to_string (&file) {
105            Ok (content) => {
106                match content[..content.len () - 1].parse::<f64> () {
107                    Ok (result) => {
108                        return result;
109                    }
110                    _ => {
111                        return 0.0;
112                    }
113                }
114            }
115            _ => {
116                return 0.0;
117            }
118        }
119
120    }
121
122    /// 
123    /// Force an iteration on the vjoule service. This private function make sure the service has
124    /// perform an iteration before reading the consumptions.
125    /// 
126    fn force_signal (&mut self) {
127	    let mut inotif = Inotify::init ().expect ("Error while initializing inotify instance");
128	    inotif.watches ().add (
129	        format!("{}/cpu", VJOULE_RES_PATH),
130	        WatchMask::MODIFY
131	    ).expect ("Failed to add watch");
132
133	    for _i in 0 .. 2 {
134	        let file = File::create (format!("{}/signal", VJOULE_PATH));
135	        match file {
136		        Ok (mut f) => {
137		            f.write_all (b"1").expect ("Failed to write in signal file");
138		            f.sync_all ().expect ("Failed to close signal file");
139		        }
140		        _ => {
141		        }
142	        }
143
144	        let mut buffer = [0 ; 1024];
145	        inotif.read_events_blocking (&mut buffer)
146		        .expect ("Error while reading events");
147	    }
148    }
149    
150}