feo/
lib.rs

1/// A simple system resource monitoring CLI tool for Linux, with GPU monitoring
2/// feature for Raspberry Pi
3///
4/// Author: github.com/vcrn
5mod colors;
6
7use colors::Colors;
8use rounded_div::RoundedDiv;
9use sinfo::{Memory, SystemInfo};
10use std::{str, thread, time};
11use termion::color::{Fg, Reset, Rgb};
12
13/// Entry point into lib.rs
14pub fn run(delay: usize, gpu: bool, color: char) -> Result<(), anyhow::Error> {
15    let colors = Colors::new(color);
16
17    let delay_usize = match delay {
18        0 => 2,
19        _ => delay,
20    };
21
22    let delay_u64 = delay_usize as u64;
23
24    let mut mem_total = Memory::get_total()?;
25    mem_total.save_with_unit();
26
27    let mut cpu_time_prev = SystemInfo::new(gpu, SystemInfo::get_cpus()?)?.cpu_time; // Initial CPU time
28
29    loop {
30        let system_info = SystemInfo::new(gpu, cpu_time_prev.len())?;
31        update_monitor(
32            delay_usize,
33            &system_info,
34            &colors,
35            cpu_time_prev,
36            &mem_total,
37        );
38        cpu_time_prev = system_info.cpu_time;
39        thread::sleep(time::Duration::from_secs(delay_u64));
40    }
41}
42
43/// Updates the system information monitor, returns latest cpu_time
44fn update_monitor(
45    delay: usize,
46    system_info: &SystemInfo,
47    colors: &Colors,
48    cpu_time_prev: Vec<usize>,
49    mem_total: &Memory,
50) {
51    print!("{esc}c", esc = 27 as char); // Clears the terminal
52
53    println!("{}", "-".repeat(30)); // Line
54
55    print_comp_temp(colors.temp, system_info.cpu_temp, "CPU");
56
57    // TODO: Rewrite more efficiently to not do a check at every update_monitor()
58    if let Some(gpu_temp) = system_info.gpu_temp {
59        print_comp_temp(colors.temp, gpu_temp, "GPU");
60    }
61
62    print_cpu_load(delay, colors.cpu, &system_info.cpu_time, cpu_time_prev);
63    print_mem_use(colors.mem, &system_info.mem_free, mem_total);
64    print_uptime(colors.uptime, system_info.uptime);
65
66    println!("{}", "-".repeat(30)); // Line
67}
68
69/// Prints component temperature
70fn print_comp_temp(color: Rgb, temp: f32, component: &str) {
71    println!(
72        "{}{} temp{}:{:>18.1}\u{00B0} C",
73        Fg(color),
74        component,
75        Fg(Reset),
76        temp
77    );
78}
79
80/// Prints CPU load
81fn print_cpu_load(delay: usize, color: Rgb, cpu_time: &[usize], cpu_time_prev: Vec<usize>) {
82    for i in 0..cpu_time.len() {
83        let cpu_load_percent = (cpu_time[i] - cpu_time_prev[i]) / delay;
84        let cpu_load_bars = "|".repeat(cpu_load_percent.rounded_div(5)); // TODO: Add max limit, since cpu_load_percent can be > 100. match?
85        println!(
86            "{}CPU{}{}[{:<20}]{:>3}%",
87            Fg(color),
88            i + 1,
89            Fg(Reset),
90            cpu_load_bars,
91            cpu_load_percent
92        );
93    }
94}
95
96/// Prints memory usage
97fn print_mem_use(color: Rgb, mem_available: &Memory, mem_total: &Memory) {
98    let ram_usage = Memory::format(mem_total.ram - mem_available.ram);
99    let ram_fraction = match &mem_total.ram_with_unit {
100        Some(ram_with_unit) => format!("{ram_usage}/{}", ram_with_unit),
101        None => format!("{ram_usage}/{}", Memory::format(mem_total.ram)),
102    };
103
104    let swap_usage = Memory::format(mem_total.swap - mem_available.swap);
105    let swap_fraction = match &mem_total.swap_with_unit {
106        Some(swap_with_unit) => format!("{swap_usage}/{}", swap_with_unit),
107        None => format!("{swap_usage}/{}", Memory::format(mem_total.swap)),
108    };
109
110    println!("{}RAM{}:{:>26}", Fg(color), Fg(Reset), ram_fraction);
111    println!("{}Swap{}:{:>25}", Fg(color), Fg(Reset), swap_fraction);
112}
113
114/// Prints uptime since boot
115fn print_uptime(color: Rgb, uptime_sec: f64) {
116    let uptime_sec_int = uptime_sec as u64;
117    let uptime_hr = uptime_sec_int / (60 * 60);
118    let uptime_min = uptime_sec_int / (60) % (60);
119    let uptime_sec_remain = uptime_sec_int % 60;
120
121    println!(
122        "{}Uptime{}: {:>16}:{:02}:{:02}",
123        Fg(color),
124        Fg(Reset),
125        uptime_hr,
126        uptime_min,
127        uptime_sec_remain
128    );
129}
130
131#[cfg(test)]
132mod tests {
133    use super::*;
134
135    #[test]
136    #[ignore] // Fails on VM such as Github Actions, and clears screen
137    fn test_update_monitor() {
138        let colors = Colors::new('s');
139        let gpu = false;
140        let cpu_time_prev = SystemInfo::new(gpu, SystemInfo::get_cpus().unwrap())
141            .unwrap()
142            .cpu_time;
143        let system_info = SystemInfo::new(gpu, SystemInfo::get_cpus().unwrap()).unwrap();
144        let mut mem_total = Memory::get_total().unwrap();
145        mem_total.save_with_unit();
146        let delay = 3;
147
148        update_monitor(delay, &system_info, &colors, cpu_time_prev, &mem_total)
149    }
150}