1use crate::blocks::{Block, Configure, Message, Sender};
26use crate::{ema, utils};
27use regex::Regex;
28use serde::Deserialize;
29use std::thread;
30
31const PATTERN: &str = r"cpu\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)";
32const PATH: &str = "/proc/stat";
33
34#[derive(Configure, Deserialize)]
35pub struct Cpu {
36 #[serde(default = "default_name")]
37 name: String,
38 #[serde(default = "default_period")]
39 period: f32,
40 #[serde(default = "default_alpha")]
41 alpha: f32,
42}
43
44fn default_name() -> String {
45 "cpu".to_string()
46}
47
48fn default_period() -> f32 {
49 1.0
50}
51
52fn default_alpha() -> f32 {
53 0.7
54}
55
56impl Sender for Cpu {
57 fn add_sender(&self, channel: crossbeam_channel::Sender<Message>) -> anyhow::Result<()> {
58 let name = self.get_name();
59 let monitor = utils::monitor_file(PATH.to_string(), self.period);
60 let mut perc = ema::Ema::new(self.alpha);
61 let mut cpu = Usage {
62 idle: 0.0,
63 total: 0.0,
64 };
65 let mut block = Block::new(name.clone(), true);
66
67 thread::spawn(move || {
68 for c in monitor {
69 let current_cpu = calc_cpu(match_proc(&c));
70 block.full_text = Some(format!(
71 " {:.1}%",
72 perc.push(calc_dcpu(¤t_cpu, &cpu))
73 ));
74 channel.send((name.clone(), block.to_string())).unwrap();
75 cpu = current_cpu;
76 }
77 });
78
79 Ok(())
80 }
81}
82
83struct Usage {
84 idle: f32,
85 total: f32,
86}
87
88fn calc_cpu(stat: regex::Captures) -> Usage {
89 let stats: Vec<f32> = stat
91 .iter()
92 .skip(1)
93 .map(|x| x.unwrap().as_str().parse().unwrap())
94 .collect();
95
96 Usage {
97 idle: (stats[3] + stats[4]),
98 total: stats.iter().sum(),
99 }
100}
101
102fn calc_dcpu(cpu: &Usage, prevcpu: &Usage) -> f32 {
103 (1.0 - (cpu.idle - prevcpu.idle) / (cpu.total - prevcpu.total)) * 100.0
104}
105
106fn match_proc(s: &str) -> regex::Captures {
107 lazy_static! {
108 static ref RE: Regex = Regex::new(PATTERN).unwrap();
109 }
110 RE.captures(s)
111 .expect(&format!("Failed to match /proc/stat contents '{}'", s))
112}
113
114#[cfg(test)]
115mod tests {
116 use super::*;
117 const STATFILE: &str = "cpu 237476 0 85111 17267319 2310 34402 4846 0 0 0\n";
118
119 #[test]
120 fn regex_matches() {
121 match_proc(&STATFILE);
122 }
123}