Resource Monitor
RepositoryIn this project, I gained more experience using the modern Tauri framework. I built a resource monitoring app that tracks CPU, GPU, and RAM usage. It also displays general system information. The app is modern, lightweight, and performant, leveraging Rust and Tauri, both known for their high performance and small bundle sizes.
ResourceMonitor Rust Struct
This is a snippet of my ResourceMonitor Struct written in Rust. This struct helps me gather all data from the different monitors with a function to return all data at once and functions to only get certain data from each monitor.
1impl ResourceMonitor {
2 pub fn new() -> Result<Self, String> {
3 let sys = System::new_all();
4 let nvml = Nvml::init().map_err(|e| format!("Failed to init NVML: {}", e))?;
5 Ok(ResourceMonitor { sys, nvml })
6 }
7
8 pub fn collect_all_data(&mut self) -> ResourceData {
9 self.sys.refresh_all();
10
11 let cpu_info = CpuMonitor::get_cpu_info(&mut self.sys);
12 let system_info = SystemMonitor::get_system_info(&mut self.sys);
13 let memory_info = MemoryMonitor::get_memory_info(&mut self.sys);
14 let gpu_info = self.collect_gpu_data();
15
16 ResourceData {
17 cpu: cpu_info,
18 sys: system_info,
19 mem: memory_info,
20 gpu: gpu_info,
21 timestamp_ms: std::time::SystemTime::now()
22 .duration_since(std::time::UNIX_EPOCH)
23 .unwrap_or_default()
24 .as_millis() as u64,
25 }
26 }
27
28 pub fn collect_cpu_data(&mut self) -> CpuInfo {
29 CpuMonitor::get_cpu_info(&mut self.sys)
30 }
31
32 pub fn collect_system_data(&mut self) -> SystemInfo {
33 SystemMonitor::get_system_info(&mut self.sys)
34 }
35
36 pub fn collect_memory_data(&mut self) -> MemoryInfo {
37 MemoryMonitor::get_memory_info(&mut self.sys)
38 }
39
40 pub fn collect_gpu_data(&self) -> GpuInfo {
41 GpuMonitor::get_gpu_info(&self.nvml).unwrap_or_else(|_| GpuInfo {
42 name: "Unknown".to_string(),
43 temperature: 0,
44 memory_used_mb: 0,
45 memory_total_mb: 0,
46 utilization_percent: 0,
47 fan_speed_percent: None,
48 })
49 }
50}
lib.rs
The lib.rs file is the connection point between the NextJS frontend and Rust backend. This file exposes commands to the frontend which fetch the data gathered by the ResourceMonitor.
1#[tauri::command]
2fn get_all_resource_data(monitor: State<SharedResourceMonitor>) -> ResourceData {
3 let mut monitor = monitor.lock().expect("Failed to lock ResourceMonitor");
4 monitor.collect_all_data()
5}
6
7#[tauri::command]
8fn get_cpu_data(monitor: State<SharedResourceMonitor>) -> CpuInfo {
9 let mut monitor = monitor.lock().expect("Failed to lock ResourceMonitor");
10 monitor.collect_cpu_data()
11}
12
13#[tauri::command]
14fn get_system_data(monitor: State<SharedResourceMonitor>) -> SystemInfo {
15 let mut monitor = monitor.lock().expect("Failed to lock ResourceMonitor");
16 monitor.collect_system_data()
17}
18
19#[tauri::command]
20fn get_memory_data(monitor: State<SharedResourceMonitor>) -> MemoryInfo {
21 let mut monitor = monitor.lock().expect("Failed to lock ResourceMonitor");
22 monitor.collect_memory_data()
23}
24
25#[tauri::command]
26fn get_gpu_data(monitor: State<SharedResourceMonitor>) -> GpuInfo {
27 let monitor = monitor.lock().expect("Failed to lock ResourceMonitor");
28 monitor.collect_gpu_data()
29}
LineGraph Component
This component generates a line graph on the fly to represent some historical data about the usages from each monitor. This line graph is an SVG image which get displayed below the other component data.
1export default function LineGraph({dataPoints, label, maxPoints = 100, upperRange = 100}: LineGraphInterface) {
2 const data = dataPoints.slice(-maxPoints);
3
4 const width = 450;
5 const height = 200;
6 const padding = 12;
7
8 const graphHeight = height - (2 * padding);
9 const graphTopY = padding;
10 const graphBottomY = height - padding;
11
12 const points = data.length > 1
13 ? data
14 .map((value, i) => {
15 const x = padding + (i / (data.length - 1)) * (width - 2 * padding);
16 const y = graphBottomY - (value / upperRange) * graphHeight;
17 return `${x},${y}`;
18 })
19 .join(' ')
20 : data.length === 1
21 ? `${width / 2},${height / 2}`
22 : "";
23
24 const gridLineColor = "#666";
25 const textColor = "#444";
26 const fontSize = "11";
27
28 const getGridY = (percentage: number) => {
29 const valueAtPercentage = (percentage / 100) * upperRange;
30 return graphBottomY - (valueAtPercentage / upperRange) * graphHeight;
31 };
32
33 return (
34 <div className="line-graph" style={{maxWidth: width, width: '100%'}}>
35 {label && <h3 style={{marginBottom: 4}}>{label}</h3>}
36 <svg
37 viewBox={`0 0 ${width} ${height}`}
38 width="100%"
39 height={height}
40 style={{display: "block"}}
41 >
42 {[0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100].map((percentage) => {
43 const lineY = getGridY(percentage);
44 return (
45 <line
46 key={`grid-line-${percentage}`}
47 x1="0"
48 y1={lineY}
49 x2={width}
50 y2={lineY}
51 stroke={gridLineColor}
52 strokeWidth="1"
53 strokeDasharray="4 2"
54 />
55 );
56 })}
57
58 <text
59 x={padding + 3}
60 y={getGridY(100) - 3}
61 fill={textColor}
62 fontSize={fontSize}
63 dominantBaseline="auto"
64 >
65 {upperRange}
66 </text>
67 <text
68 x={width - padding - 3}
69 y={getGridY(100) - 3}
70 fill={textColor}
71 fontSize={fontSize}
72 dominantBaseline="auto"
73 textAnchor="end"
74 >
75 {upperRange}
76 </text>
77
78 <text
79 x={padding + 3}
80 y={getGridY(0) + 12}
81 fill={textColor}
82 fontSize={fontSize}
83 dominantBaseline="hanging"
84 >
85 0
86 </text>
87 <text
88 x={width - padding - 3}
89 y={getGridY(0) + 12}
90 fill={textColor}
91 fontSize={fontSize}
92 dominantBaseline="hanging"
93 textAnchor="end"
94 >
95 0
96 </text>
97
98 <polyline
99 fill="none"
100 stroke="#000"
101 strokeWidth="2"
102 points={points}
103 strokeLinecap="round"
104 strokeLinejoin="round"
105 />
106 </svg>
107 </div>
108 );
109}
Try it out yourself!
DownloadIf you want to try this app out for yourself, click on the download button and install the app using the MSI or EXE installer from GitHub.