1use std::num::{ParseFloatError, ParseIntError};
4
5#[cfg(feature = "serde")]
6use serde::{Deserialize, Serialize};
7use subprocess::{Exec, PopenError, Redirection};
8
9use bitpat::bitpat;
10
11mod parsers;
12
13#[derive(Debug)]
14pub enum ExecutionError {
15 Popen(PopenError),
16 ParseInt(ParseIntError),
17 ParseFloat(ParseFloatError),
18}
19
20pub enum ClockSrc {
21 Arm,
22 Core,
23 Dpi,
24 Emmc,
25 H264,
26 Hdmi,
27 Isp,
28 Pixel,
29 Pwm,
30 Uart,
31 V3d,
32 Vec,
33}
34
35pub enum VoltSrc {
36 Core,
37 SdramC,
38 SdramI,
39 SdramP,
40}
41
42pub enum MemSrc {
43 Arm,
44 Gpu,
45}
46
47pub enum Src {
48 Clock(ClockSrc),
49 Mem(MemSrc),
50 Volt(VoltSrc),
51}
52
53pub enum Cmd {
54 GetMem,
55 GetThrottled,
56 MeasureClock,
57 MeasureTemp,
58 MeasureVolts,
59}
60
61#[derive(Debug, Default, PartialOrd, PartialEq)]
64#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
65pub struct ThrottledStatus {
66 pub arm_frequency_cap_occurred: bool,
67 pub arm_frequency_capped: bool,
68 pub currently_throttled: bool,
69 pub soft_temp_limit_active: bool,
70 pub soft_temp_limit_occurred: bool,
71 pub throttling_occurred: bool,
72 pub under_voltage: bool,
73 pub under_voltage_occurred: bool,
74}
75
76impl ThrottledStatus {
77 pub fn new(bit_pattern: isize) -> ThrottledStatus {
78 interpret_bit_pattern(bit_pattern)
79 }
80}
81
82pub fn exec_command(command: Cmd, src: Option<Src>) -> Result<String, PopenError> {
84 const VCGENCMD_INVOCATION: &str = "vcgencmd";
86
87 let vcgencmd_output = Exec::cmd("sudo")
88 .arg(VCGENCMD_INVOCATION)
89 .arg(resolve_command(command))
90 .arg(resolve_src(src).unwrap_or_default())
91 .stdout(Redirection::Pipe)
92 .capture()?
93 .stdout_str();
94
95 Ok(vcgencmd_output)
96}
97
98pub fn measure_clock(src: Src) -> Result<isize, ExecutionError> {
100 let output = exec_command(Cmd::MeasureClock, Some(src)).map_err(ExecutionError::Popen)?;
101 let frequency = parsers::frequency(&output).map_err(ExecutionError::ParseInt)?;
102
103 Ok(frequency)
104}
105
106pub fn measure_volts(src: Src) -> Result<f64, ExecutionError> {
107 let output = exec_command(Cmd::MeasureVolts, Some(src)).map_err(ExecutionError::Popen)?;
108 let volts = parsers::volts(&output).map_err(ExecutionError::ParseFloat)?;
109
110 Ok(volts)
111}
112
113pub fn measure_temp() -> Result<f64, ExecutionError> {
114 let output = exec_command(Cmd::MeasureTemp, None).map_err(ExecutionError::Popen)?;
115 let temperature = parsers::temp(&output).map_err(ExecutionError::ParseFloat)?;
116
117 Ok(temperature)
118}
119
120pub fn get_mem(src: Src) -> Result<isize, ExecutionError> {
121 let output = exec_command(Cmd::GetMem, Some(src)).map_err(ExecutionError::Popen)?;
122 let mem = parsers::mem(&output).map_err(ExecutionError::ParseInt)?;
123
124 Ok(mem)
125}
126
127pub fn get_throttled() -> Result<isize, ExecutionError> {
128 let output = exec_command(Cmd::GetThrottled, None).map_err(ExecutionError::Popen)?;
129 let bit_pattern = parsers::throttled(&output).map_err(ExecutionError::ParseInt)?;
130 Ok(bit_pattern)
131}
132
133pub fn interpret_bit_pattern(pattern: isize) -> ThrottledStatus {
170 let soft_temp_limit_occurred = bitpat!(1 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _)(pattern);
171 let arm_frequency_cap_occurred = bitpat!(_ 1 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _)(pattern);
172 let throttling_occurred = bitpat!(_ _ 1 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _)(pattern);
173 let under_voltage_occurred = bitpat!(_ _ _ 1 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _)(pattern);
174
175 let soft_temp_limit_active = bitpat!(_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 1 _ _ _)(pattern);
176 let arm_frequency_capped = bitpat!(_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 1 _ _)(pattern);
177 let currently_throttled = bitpat!(_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 1 _)(pattern);
178 let under_voltage = bitpat!(_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 1)(pattern);
179
180 ThrottledStatus {
181 arm_frequency_cap_occurred,
182 arm_frequency_capped,
183 currently_throttled,
184 soft_temp_limit_active,
185 soft_temp_limit_occurred,
186 throttling_occurred,
187 under_voltage,
188 under_voltage_occurred,
189 }
190}
191
192fn resolve_command(cmd: Cmd) -> String {
193 match cmd {
194 Cmd::GetMem => "get_mem",
195 Cmd::GetThrottled => "get_throttled",
196 Cmd::MeasureClock => "measure_clock",
197 Cmd::MeasureTemp => "measure_temp",
198 Cmd::MeasureVolts => "measure_volts",
199 }
200 .to_owned()
201}
202
203fn resolve_src(src: Option<Src>) -> Option<String> {
204 let src = src.as_ref()?;
206
207 match src {
208 Src::Clock(ClockSrc::Arm) => Some("arm".to_owned()),
209 Src::Clock(ClockSrc::Core) => Some("core".to_owned()),
210 Src::Clock(ClockSrc::Dpi) => Some("dpi".to_owned()),
211 Src::Clock(ClockSrc::Emmc) => Some("emmc".to_owned()),
212 Src::Clock(ClockSrc::H264) => Some("h264".to_owned()),
213 Src::Clock(ClockSrc::Hdmi) => Some("hdmi".to_owned()),
214 Src::Clock(ClockSrc::Isp) => Some("isp".to_owned()),
215 Src::Clock(ClockSrc::Pixel) => Some("pixel".to_owned()),
216 Src::Clock(ClockSrc::Pwm) => Some("pwm".to_owned()),
217 Src::Clock(ClockSrc::Uart) => Some("uart".to_owned()),
218 Src::Clock(ClockSrc::V3d) => Some("v3d".to_owned()),
219 Src::Clock(ClockSrc::Vec) => Some("vec".to_owned()),
220 Src::Mem(MemSrc::Arm) => Some("arm".to_owned()),
221 Src::Mem(MemSrc::Gpu) => Some("gpu".to_owned()),
222 Src::Volt(VoltSrc::Core) => Some("core".to_owned()),
223 Src::Volt(VoltSrc::SdramC) => Some("sdram_c".to_owned()),
224 Src::Volt(VoltSrc::SdramI) => Some("sdram_i".to_owned()),
225 Src::Volt(VoltSrc::SdramP) => Some("sdram_p".to_owned()),
226 }
227}
228
229#[cfg(test)]
230mod tests {
231 use super::*;
232
233 #[test]
234 fn test_resolve_src() {
235 assert_eq!(
236 Some(String::from("arm")),
237 resolve_src(Some(Src::Clock(ClockSrc::Arm)))
238 );
239
240 assert_eq!(None, resolve_src(None));
241 }
242
243 #[test]
244 fn test_resolve_command() {
245 assert_eq!("measure_temp", resolve_command(Cmd::MeasureTemp));
246 assert_eq!("measure_clock", resolve_command(Cmd::MeasureClock));
247 }
248
249 #[test]
250 fn test_throttled_status_methods() {
251 let throttled_status = ThrottledStatus::new(0b111100000000000001010);
252 assert_eq!(
253 throttled_status,
254 ThrottledStatus {
255 arm_frequency_cap_occurred: true,
256 arm_frequency_capped: false,
257 currently_throttled: true,
258 soft_temp_limit_active: true,
259 soft_temp_limit_occurred: true,
260 throttling_occurred: true,
261 under_voltage: false,
262 under_voltage_occurred: true,
263 }
264 )
265 }
266
267 #[test]
268 fn test_interpret_bit_pattern() {
269 let throttled_info = interpret_bit_pattern(0b111100000000000001010);
270 assert_eq!(
271 throttled_info,
272 ThrottledStatus {
273 arm_frequency_cap_occurred: true,
274 arm_frequency_capped: false,
275 currently_throttled: true,
276 soft_temp_limit_active: true,
277 soft_temp_limit_occurred: true,
278 throttling_occurred: true,
279 under_voltage: false,
280 under_voltage_occurred: true,
281 }
282 );
283
284 let throttled_info2 = interpret_bit_pattern(0b111100000000000001111);
285 assert_eq!(
286 throttled_info2,
287 ThrottledStatus {
288 arm_frequency_cap_occurred: true,
289 arm_frequency_capped: true,
290 currently_throttled: true,
291 soft_temp_limit_active: true,
292 soft_temp_limit_occurred: true,
293 throttling_occurred: true,
294 under_voltage: true,
295 under_voltage_occurred: true,
296 }
297 )
298 }
299
300 #[cfg(target_arch = "arm")]
301 #[test]
302 fn test_exec_command() {
303 let output = exec_command(Cmd::MeasureClock, Some(Src::Clock(ClockSrc::Core))).unwrap();
304 dbg!(&output);
305 assert!(output.contains("frequency"));
306 }
307
308 #[cfg(target_arch = "arm")]
309 #[test]
310 fn test_get_mem() {
311 let output = get_mem(Src::Mem(MemSrc::Arm));
312 dbg!(&output);
313 debug_assert_eq!(output.is_ok(), true)
314 }
315
316 #[cfg(target_arch = "arm")]
317 #[test]
318 fn test_measure_temp() {
319 let output = measure_temp();
320 dbg!(&output);
321 debug_assert_eq!(output.is_ok(), true)
322 }
323
324 #[cfg(target_arch = "arm")]
325 #[test]
326 fn test_measure_volts() {
327 let output = measure_volts(Src::Volt(VoltSrc::Core));
328 dbg!(&output);
329 debug_assert_eq!(output.is_ok(), true)
330 }
331
332 #[cfg(target_arch = "arm")]
333 #[test]
334 fn test_measure_frequency() {
335 let output = get_mem(Src::Mem(MemSrc::Arm));
336 dbg!(&output);
337 debug_assert_eq!(output.is_ok(), true)
338 }
339}