1use chrono::{DateTime, NaiveDate, Utc};
2use std::convert::TryInto;
3use std::time::Duration;
4
5pub fn get_bool(bytearray: &[u8], byte_index: usize, bool_index: usize) -> Result<bool, String> {
6 if bytearray.len() < byte_index + 2 || bool_index > 7 {
7 return Err("Buffer has no enough data to decoding".to_string());
8 }
9 let index_value = 1 << bool_index;
10 let byte_value = bytearray[byte_index];
11 let current_value = byte_value & index_value;
12 Ok(current_value == index_value)
13}
14
15pub fn get_byte(bytearray: &[u8], byte_index: usize) -> u8 {
16 bytearray[byte_index]
17}
18
19pub fn get_word(bytearray: &[u8], byte_index: usize) -> u16 {
20 let data: [u8; 2] = bytearray[byte_index..byte_index + 2].try_into().unwrap();
21 u16::from_be_bytes(data)
22}
23
24pub fn get_int(bytearray: &[u8], byte_index: usize) -> i16 {
25 let data: [u8; 2] = bytearray[byte_index..byte_index + 2].try_into().unwrap();
26 i16::from_be_bytes(data)
27}
28
29pub fn get_uint(bytearray: &[u8], byte_index: usize) -> u16 {
30 get_word(bytearray, byte_index)
31}
32
33pub fn get_real(bytearray: &[u8], byte_index: usize) -> f32 {
34 let data: [u8; 4] = bytearray[byte_index..byte_index + 4].try_into().unwrap();
35 f32::from_bits(u32::from_be_bytes(data))
36}
37
38pub fn get_fstring(
39 bytearray: &[u8],
40 byte_index: usize,
41 max_length: usize,
42 remove_padding: bool,
43) -> String {
44 let data = &bytearray[byte_index..byte_index + max_length];
45 let string = String::from_utf8(data.to_vec()).unwrap();
46
47 if remove_padding {
48 string.trim_end().to_string()
49 } else {
50 string
51 }
52}
53
54pub fn get_string(bytearray: &[u8], byte_index: usize) -> Result<String, String> {
55 let max_string_size = bytearray[byte_index] as usize;
56 let str_length = bytearray[byte_index + 1] as usize;
57
58 if str_length > max_string_size || max_string_size > 254 {
59 return Err("String length not match!".to_string());
60 }
61
62 if bytearray.len() < byte_index + str_length + 1 {
63 return Err("Buffer has no enough data to decoding".to_string());
64 }
65
66 let data = &bytearray[byte_index + 2..byte_index + 2 + str_length];
67 let str_data = String::from_utf8(data.to_vec()).map_err(|e| e.to_string())?;
68 Ok(str_data)
69}
70
71pub fn get_dword(bytearray: &[u8], byte_index: usize) -> u32 {
72 let data: [u8; 4] = bytearray[byte_index..byte_index + 4].try_into().unwrap();
73 u32::from_be_bytes(data)
74}
75
76pub fn get_dint(bytearray: &[u8], byte_index: usize) -> i32 {
77 let data: [u8; 4] = bytearray[byte_index..byte_index + 4].try_into().unwrap();
78 i32::from_be_bytes(data)
79}
80
81pub fn get_udint(bytearray: &[u8], byte_index: usize) -> u32 {
82 get_dword(bytearray, byte_index)
83}
84
85pub fn get_s5time(bytearray: &[u8], byte_index: usize) -> String {
86 let data_bytearray = &bytearray[byte_index..byte_index + 2];
87 let s5time_data_int_like = format!("{:02X}{:02X}", data_bytearray[0], data_bytearray[1]);
88
89 let time_base = match &s5time_data_int_like[0..1] {
90 "0" => 10,
91 "1" => 100,
92 "2" => 1000,
93 "3" => 10000,
94 _ => panic!("This value should not be greater than 3"),
95 };
96
97 let mut s5time_bcd: i32 = 0;
98
99 for (i, digit) in s5time_data_int_like.chars().enumerate() {
100 if i > 0 {
101 s5time_bcd *= 10;
102 s5time_bcd += digit.to_digit(10).unwrap() as i32;
103 }
104 }
105
106 let s5time_microseconds = time_base * s5time_bcd;
107 let s5time = Duration::from_micros(s5time_microseconds as u64 * 1000);
108 format!("{:?}", s5time)
109}
110
111pub fn get_dt(bytearray: &[u8], byte_index: usize) -> String {
112 get_date_time_object(bytearray, byte_index).to_string()
113}
114
115pub fn get_date_time_object(bytearray: &[u8], byte_index: usize) -> DateTime<Utc> {
116 fn bcd_to_byte(byte: u8) -> u8 {
117 (byte >> 4) * 10 + (byte & 0xF)
118 }
119 let year = bcd_to_byte(bytearray[byte_index]) as i32;
120 let year = if year < 90 { 2000 + year } else { 1900 + year };
121 let month = bcd_to_byte(bytearray[byte_index + 1]);
122 let day = bcd_to_byte(bytearray[byte_index + 2]);
123 let hour = bcd_to_byte(bytearray[byte_index + 3]);
124 let min = bcd_to_byte(bytearray[byte_index + 4]);
125 let sec = bcd_to_byte(bytearray[byte_index + 5]);
126 let microsec = (bcd_to_byte(bytearray[byte_index + 6]) as u32 * 10
127 + (bytearray[byte_index + 7] >> 4) as u32)
128 * 1000;
129
130 NaiveDate::from_ymd_opt(year, month.into(), day.into())
131 .expect("failed to parse date")
132 .and_hms_micro_opt(hour.into(), min.into(), sec.into(), microsec)
133 .expect("failed to parse time")
134 .and_utc()
135}
136
137pub fn get_time(bytearray: &[u8], byte_index: usize) -> String {
138 let data_bytearray = &bytearray[byte_index..byte_index + 4];
139 let mut val = i32::from_be_bytes(data_bytearray.try_into().unwrap());
140
141 let sign_str = if val < 0 {
142 val = -val;
143 "-"
144 } else {
145 ""
146 };
147
148 let milli_seconds = val % 1000;
149 let seconds = (val / 1000) % 60;
150 let minutes = (val / (1000 * 60)) % 60;
151 let hours = (val / (1000 * 60 * 60)) % 24;
152 let days = val / (1000 * 60 * 60 * 24);
153
154 format!(
155 "{}{}:{}:{}:{}.{}",
156 sign_str,
157 days,
158 hours % 24,
159 minutes % 60,
160 seconds % 60,
161 milli_seconds
162 )
163}
164
165pub fn get_usint(bytearray: &[u8], byte_index: usize) -> u8 {
166 bytearray[byte_index]
167}
168
169pub fn get_sint(bytearray: &[u8], byte_index: usize) -> i8 {
170 bytearray[byte_index] as i8
171}
172
173pub fn get_lint(bytearray: &[u8], byte_index: usize) -> i64 {
174 let data: [u8; 8] = bytearray[byte_index..byte_index + 8].try_into().unwrap();
175 i64::from_be_bytes(data)
176}
177
178pub fn get_lreal(bytearray: &[u8], byte_index: usize) -> f64 {
179 let data: [u8; 8] = bytearray[byte_index..byte_index + 8].try_into().unwrap();
180 f64::from_bits(u64::from_be_bytes(data))
181}
182
183pub fn get_lword(bytearray: &[u8], byte_index: usize) -> u64 {
184 let data: [u8; 8] = bytearray[byte_index..byte_index + 8].try_into().unwrap();
185 u64::from_be_bytes(data)
186}
187
188pub fn get_ulint(bytearray: &[u8], byte_index: usize) -> u64 {
189 get_lword(bytearray, byte_index)
190}
191
192pub fn get_tod(bytearray: &[u8], byte_index: usize) -> Duration {
193 let len_bytearray = bytearray.len();
194 let byte_range = byte_index + 4;
195 if len_bytearray < byte_range {
196 panic!("Date can't be extracted from bytearray. bytearray_[Index:Index+16] would cause overflow.");
197 }
198 let time_val = Duration::from_millis(u32::from_be_bytes(
199 bytearray[byte_index..byte_range].try_into().unwrap(),
200 ) as u64);
201 if time_val.as_secs() >= 86400 {
202 panic!(
203 "Time_Of_Date can't be extracted from bytearray. Bytearray contains unexpected values."
204 );
205 }
206 time_val
207}
208
209pub fn get_date(bytearray: &[u8], byte_index: usize) -> chrono::NaiveDate {
210 use chrono::NaiveDate;
211
212 let len_bytearray = bytearray.len();
213 let byte_range = byte_index + 2;
214 if len_bytearray < byte_range {
215 panic!("Date can't be extracted from bytearray. bytearray_[Index:Index+16] would cause overflow.");
216 }
217 let date_val = NaiveDate::from_ymd_opt(1990, 1, 1).expect("failed to parse date.")
218 + chrono::Duration::days(u16::from_be_bytes(
219 bytearray[byte_index..byte_range].try_into().unwrap(),
220 ) as i64);
221 if date_val > NaiveDate::from_ymd_opt(2168, 12, 31).expect("failed to parse date.") {
222 panic!("date_val is higher than specification allows.");
223 }
224 date_val
225}
226
227#[cfg(test)]
228mod getters_tests {
229 use super::*;
230 use chrono::NaiveDate;
231
232 #[test]
233 fn test_get_bool() {
234 let bytearray = [0b10101010];
235 assert!(get_bool(&bytearray, 0, 1));
236 assert!(!get_bool(&bytearray, 0, 0));
237 }
238
239 #[test]
240 fn test_get_byte() {
241 let bytearray = [0x12];
242 assert_eq!(get_byte(&bytearray, 0), 0x12);
243 }
244
245 #[test]
246 fn test_get_word() {
247 let bytearray = [0x12, 0x34];
248 assert_eq!(get_word(&bytearray, 0), 0x1234);
249 }
250
251 #[test]
252 fn test_get_int() {
253 let bytearray = [0xFF, 0xD6];
254 assert_eq!(get_int(&bytearray, 0), -42);
255 }
256
257 #[test]
258 fn test_get_uint() {
259 let bytearray = [0x12, 0x34];
260 assert_eq!(get_uint(&bytearray, 0), 0x1234);
261 }
262
263 #[test]
264 fn test_get_real() {
265 let bytearray = [0x41, 0x20, 0x00, 0x00];
266 assert_eq!(get_real(&bytearray, 0), 10.0);
267 }
268
269 #[test]
270 fn test_get_fstring() {
271 let bytearray = b"hello";
272 assert_eq!(get_fstring(bytearray, 0, 5, true), "hello");
273 }
274
275 #[test]
276 fn test_get_string() {
277 let bytearray = [5, 4, b'h', b'e', b'l', b'l', b'o'];
278 assert_eq!(get_string(&bytearray, 0), "hell");
279 }
280
281 #[test]
282 fn test_get_dword() {
283 let bytearray = [0x12, 0x34, 0x56, 0x78];
284 assert_eq!(get_dword(&bytearray, 0), 0x12345678);
285 }
286
287 #[test]
288 fn test_get_dint() {
289 let bytearray = [0xFF, 0xFF, 0xFF, 0xC6];
290 assert_eq!(get_dint(&bytearray, 0), -58);
291 }
292
293 #[test]
294 fn test_get_udint() {
295 let bytearray = [0x12, 0x34, 0x56, 0x78];
296 assert_eq!(get_udint(&bytearray, 0), 0x12345678);
297 }
298
299 #[test]
300 fn test_get_s5time() {
301 let bytearray = [0x12, 0x34];
302 assert_eq!(get_s5time(&bytearray, 0), "23.4s");
303 }
304
305 #[test]
306 fn test_get_dt() {
307 let bytearray = [0x24, 0x12, 0x12, 0x12, 0x30, 0x30, 0x30, 0x00];
308 assert_eq!(get_dt(&bytearray, 0), "2024-12-12 12:30:30.300 UTC");
309 }
310
311 #[test]
312
313 fn test_get_time() {
314 let bytearray = [0x7f, 0xff, 0xff, 0xff];
315 assert_eq!(get_time(&bytearray, 0), "24:20:31:23.647");
316 }
317
318 #[test]
319 fn test_get_usint() {
320 let bytearray = [0x12];
321 assert_eq!(get_usint(&bytearray, 0), 0x12);
322 }
323
324 #[test]
325 fn test_get_sint() {
326 let bytearray = [0xF6];
327 assert_eq!(get_sint(&bytearray, 0), -10);
328 }
329
330 #[test]
331 fn test_get_lint() {
332 let bytearray = [0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC6];
333 assert_eq!(get_lint(&bytearray, 0), -58);
334 }
335
336 #[test]
337 fn test_get_lreal() {
338 let bytearray = [0x40, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00];
339 assert_eq!(get_lreal(&bytearray, 0), 10.0);
340 }
341
342 #[test]
343 fn test_get_lword() {
344 let bytearray = [0x12, 0x34, 0x56, 0x78, 0x90, 0xAB, 0xCD, 0xEF];
345 assert_eq!(get_lword(&bytearray, 0), 0x1234567890ABCDEF);
346 }
347
348 #[test]
349 fn test_get_ulint() {
350 let bytearray = [0x12, 0x34, 0x56, 0x78, 0x90, 0xAB, 0xCD, 0xEF];
351 assert_eq!(get_ulint(&bytearray, 0), 0x1234567890ABCDEF);
352 }
353
354 #[test]
355 fn test_get_tod() {
356 let bytearray = [0x00, 0x01, 0x51, 0x80];
357 assert_eq!(get_tod(&bytearray, 0), Duration::from_millis(86400));
358 }
359
360 #[test]
361 fn test_get_date() {
362 let days_since_1990 = (NaiveDate::from_ymd_opt(2024, 1, 1).unwrap()
363 - NaiveDate::from_ymd_opt(1990, 1, 1).unwrap())
364 .num_days() as u16;
365 let bytearray = days_since_1990.to_be_bytes();
366 assert_eq!(
367 get_date(&bytearray, 0),
368 NaiveDate::from_ymd_opt(2024, 1, 1).expect("failed to parse date")
369 );
370 }
371}