1use anyhow::anyhow;
2use anyhow::Result;
3use std::mem;
4use std::time::SystemTime;
5
6use crate::utils::system_time_to_unix_seconds;
7
8#[derive(Debug, Copy, Clone, PartialEq, Eq)]
9#[repr(u8)]
10pub enum HoldType {
11 Current = 0,
12 Maximum = 1,
13 Minimum = 2,
14 Average = 3,
15}
16
17impl TryFrom<u8> for HoldType {
18 type Error = ();
19
20 fn try_from(value: u8) -> Result<Self, Self::Error> {
21 match value {
22 0 => Ok(Self::Current),
23 1 => Ok(Self::Maximum),
24 2 => Ok(Self::Minimum),
25 3 => Ok(Self::Average),
26 _ => Err(()),
27 }
28 }
29}
30
31#[derive(Debug, Copy, Clone)]
33pub struct Reading {
34 pub timestamp: SystemTime,
35 pub current_temps_c: [f32; 4],
36 pub held_temps_c: [f32; 4],
37 pub hold_type: HoldType,
38 pub meter_temp_c: f32,
39}
40
41impl Reading {
42 pub const N_BYTES: usize = 56;
43 pub const SYNC: [u8; 5] = [0xaa, 0x55, 0x00, 0x34, 0x01];
44 pub const N_SYNC_BYTES: usize = Self::SYNC.len();
45
46 fn unpack_f32(buf: &[u8], offset: &mut usize) -> Result<f32> {
47 let size = mem::size_of::<f32>();
48 if *offset + size > buf.len() {
49 return Err(anyhow!("Read beyond buffer"));
50 }
51 let bytes = &buf[*offset..*offset + size];
52 let value = f32::from_le_bytes(bytes.try_into().unwrap());
53 *offset += size;
54 Ok(value)
55 }
56
57 fn unpack_u8(buf: &[u8], offset: &mut usize) -> Result<u8> {
58 let size = mem::size_of::<u8>();
59 if *offset + size > buf.len() {
60 return Err(anyhow!("Read beyond buffer"));
61 }
62 let value = buf[*offset];
63 *offset += size;
64 Ok(value)
65 }
66
67 fn unpack_u16(buf: &[u8], offset: &mut usize) -> Result<u16> {
68 let size = mem::size_of::<u16>();
69 if *offset + size > buf.len() {
70 return Err(anyhow!("Read beyond buffer"));
71 }
72 let bytes = &buf[*offset..*offset + size];
73 let value = u16::from_le_bytes(bytes.try_into().unwrap());
74 *offset += size;
75 Ok(value)
76 }
77
78 fn unpack_u32(buf: &[u8], offset: &mut usize) -> Result<u32> {
79 let size = mem::size_of::<u32>();
80 if *offset + size > buf.len() {
81 return Err(anyhow!("Read beyond buffer"));
82 }
83 let bytes = &buf[*offset..*offset + size];
84 let value = u32::from_le_bytes(bytes.try_into().unwrap());
85 *offset += size;
86 Ok(value)
87 }
88
89 pub fn parse(buf: &[u8; Self::N_BYTES]) -> Result<Self> {
90 if buf.len() != Self::N_BYTES {
91 return Err(anyhow!("Incorrect buffer size"));
92 }
93 if buf[..Self::N_SYNC_BYTES] != Self::SYNC {
94 return Err(anyhow!("Bad sync header"));
95 }
96
97 let mut offset = Self::N_SYNC_BYTES;
98 let timestamp = SystemTime::now();
99 let mut current_temps_c = [0.0; 4];
100 for temp in current_temps_c.iter_mut() {
101 *temp = Self::unpack_f32(buf, &mut offset)?;
102 }
103 for temp in current_temps_c.iter_mut() {
104 let error = Self::unpack_u8(buf, &mut offset)?;
105 if error != 0 {
106 *temp = f32::NAN;
107 }
108 }
109 let mut held_temps_c = [0.0; 4];
110 for temp in held_temps_c.iter_mut() {
111 *temp = Self::unpack_f32(buf, &mut offset)?;
112 }
113 for temp in held_temps_c.iter_mut() {
114 let error = Self::unpack_u8(buf, &mut offset)?;
115 if error != 0 {
116 *temp = f32::NAN;
117 }
118 }
119 let meter_temp_c = Self::unpack_f32(buf, &mut offset)?;
120 Self::unpack_u32(buf, &mut offset)?; let hold_type_raw = Self::unpack_u8(buf, &mut offset)?;
122 let hold_type =
123 HoldType::try_from(hold_type_raw).map_err(|_| anyhow!("Invalid HoldType"))?;
124 Self::unpack_u16(buf, &mut offset)?; if offset == Self::N_BYTES {
127 Ok(Self {
128 timestamp,
129 current_temps_c,
130 held_temps_c,
131 hold_type,
132 meter_temp_c,
133 })
134 } else {
135 Err(anyhow!("Failed to parse all bytes"))
136 }
137 }
138
139 pub fn print_current_temps(&self) {
140 print!(
141 "{:.3}",
142 system_time_to_unix_seconds(self.timestamp).unwrap()
143 );
144 for temp in self.current_temps_c.iter() {
145 print!(" {:7.3}", temp);
146 }
147 println!();
148 }
149
150 pub fn print_all_temps(&self) {
151 print!(
152 "{:.3}",
153 system_time_to_unix_seconds(self.timestamp).unwrap()
154 );
155 for temp in &self.current_temps_c {
156 print!(" {:7.3}", temp);
157 }
158 print!(" {:?}", self.hold_type);
159 for temp in &self.held_temps_c {
160 print!(" {:7.3}", temp);
161 }
162 println!();
163 }
164}
165
166#[cfg(test)]
167mod tests {
168 use super::*;
169
170 #[test]
171 fn test_parse_reading_from_bytes() -> Result<()> {
172 #[rustfmt::skip]
173 let test_bytes: [u8; Reading::N_BYTES] = [
174 0xaa, 0x55, 0x00, 0x34, 0x01, 0x98, 0x94, 0xd5, 0x41, 0x00, 0x00, 0x00, 0x00, 0x2d, 0x02, 0xd5, 0x41, 0x6c, 0x25, 0x85, 0x42, 0x00, 0x30, 0x30, 0x30, 0x98, 0x94, 0xd5, 0x41, 0x00, 0x00, 0x00, 0x00, 0x2d, 0x02, 0xd5, 0x41, 0x6c, 0x25, 0x85, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xd2, 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x15, ];
190
191 let reading_result = Reading::parse(&test_bytes)?;
192
193 assert_eq!(reading_result.current_temps_c[0], 26.697556);
194 assert!(reading_result.current_temps_c[1].is_nan());
195 assert!(reading_result.current_temps_c[2].is_nan());
196 assert!(reading_result.current_temps_c[3].is_nan());
197
198 assert_eq!(reading_result.held_temps_c[0], 26.697556);
199 assert_eq!(reading_result.held_temps_c[1], 0.0);
200 assert_eq!(reading_result.held_temps_c[2], 26.626062);
201 assert_eq!(reading_result.held_temps_c[3], 66.57309);
202
203 assert_eq!(reading_result.meter_temp_c, 26.3125);
204 assert_eq!(reading_result.hold_type, HoldType::Current);
205
206 Ok(())
207 }
208
209 #[test]
210 fn test_parse_bad_sync() -> Result<()> {
211 let mut buffer = [0u8; Reading::N_BYTES];
212 buffer[0] = 0x00; let reading_result = Reading::parse(&buffer);
214 assert!(reading_result.is_err());
215 assert_eq!(reading_result.unwrap_err().to_string(), "Bad sync header");
216 Ok(())
217 }
218
219 #[test]
220 fn test_parse_invalid_hold_type() -> Result<()> {
221 let mut buffer = [0u8; Reading::N_BYTES];
222 buffer[..Reading::N_SYNC_BYTES].copy_from_slice(&Reading::SYNC);
223 buffer[Reading::N_BYTES - 3] = 0xff; let reading_result = Reading::parse(&buffer);
225 assert!(reading_result.is_err());
226 assert_eq!(reading_result.unwrap_err().to_string(), "Invalid HoldType");
227 Ok(())
228 }
229}