rust_snap7/utils/
setters.rs

1use regex;
2use std::time::Duration;
3
4pub fn set_bool(
5    bytearray: &mut [u8],
6    byte_index: usize,
7    bool_index: usize,
8    value: bool,
9) -> Result<(), String> {
10    if bool_index > 7 {
11        return Err(format!("bool_index {} out of range", bool_index));
12    }
13
14    let mask = 1 << bool_index;
15    if value {
16        bytearray[byte_index] |= mask;
17    } else {
18        bytearray[byte_index] &= !mask;
19    }
20    Ok(())
21}
22
23pub fn set_byte(bytearray: &mut [u8], byte_index: usize, value: u8) {
24    bytearray[byte_index] = value;
25}
26
27pub fn set_word(bytearray: &mut [u8], byte_index: usize, value: u16) {
28    bytearray[byte_index..byte_index + 2].copy_from_slice(&value.to_be_bytes());
29}
30
31pub fn set_int(bytearray: &mut [u8], byte_index: usize, value: i16) {
32    bytearray[byte_index..byte_index + 2].copy_from_slice(&value.to_be_bytes());
33}
34
35pub fn set_uint(bytearray: &mut [u8], byte_index: usize, value: u16) {
36    bytearray[byte_index..byte_index + 2].copy_from_slice(&value.to_be_bytes());
37}
38
39pub fn set_real(bytearray: &mut [u8], byte_index: usize, value: f32) {
40    bytearray[byte_index..byte_index + 4].copy_from_slice(&value.to_be_bytes());
41}
42
43pub fn set_dword(bytearray: &mut [u8], byte_index: usize, value: u32) {
44    bytearray[byte_index..byte_index + 4].copy_from_slice(&value.to_be_bytes());
45}
46
47pub fn set_dint(bytearray: &mut [u8], byte_index: usize, value: i32) {
48    bytearray[byte_index..byte_index + 4].copy_from_slice(&value.to_be_bytes());
49}
50
51pub fn set_udint(bytearray: &mut [u8], byte_index: usize, value: u32) {
52    bytearray[byte_index..byte_index + 4].copy_from_slice(&value.to_be_bytes());
53}
54
55pub fn set_time(bytearray: &mut [u8], byte_index: usize, time_string: &str) -> Result<(), String> {
56    let duration = parse_time_string(time_string)?;
57    let millis = duration.as_millis() as i32;
58    bytearray[byte_index..byte_index + 4].copy_from_slice(&millis.to_be_bytes());
59    Ok(())
60}
61
62pub fn parse_time_string(time_string: &str) -> Result<Duration, String> {
63    let re = regex::Regex::new(r"(-?)(\d+):(\d+):(\d+):(\d+).(\d+)").unwrap();
64    if let Some(caps) = re.captures(time_string) {
65        let sign = if &caps[1] == "-" { -1 } else { 1 };
66        let days: u64 = caps[2].parse().unwrap();
67        let hours: u64 = caps[3].parse().unwrap();
68        let minutes: u64 = caps[4].parse().unwrap();
69        let seconds: u64 = caps[5].parse().unwrap();
70        let millis: u64 = caps[6].parse().unwrap();
71        let total_millis = (((((days * 24 + hours) * 60 + minutes) * 60 + seconds) * 1000 + millis)
72            as i64
73            * sign) as u64;
74        Ok(Duration::from_millis(total_millis))
75    } else {
76        Err(format!("Invalid time string: {}", time_string))
77    }
78}
79
80pub fn set_usint(bytearray: &mut [u8], byte_index: usize, value: u8) {
81    bytearray[byte_index] = value;
82}
83
84pub fn set_sint(bytearray: &mut [u8], byte_index: usize, value: i8) {
85    bytearray[byte_index] = value as u8;
86}
87
88pub fn set_lreal(bytearray: &mut [u8], byte_index: usize, value: f64) {
89    bytearray[byte_index..byte_index + 8].copy_from_slice(&value.to_be_bytes());
90}
91
92pub fn set_char(bytearray: &mut [u8], byte_index: usize, value: char) -> Result<(), String> {
93    if value.is_ascii() {
94        bytearray[byte_index] = value as u8;
95        Ok(())
96    } else {
97        Err(format!("Non-ASCII character: {}", value))
98    }
99}
100
101pub fn set_date(
102    bytearray: &mut [u8],
103    byte_index: usize,
104    value: chrono::NaiveDate,
105) -> Result<(), String> {
106    let base_date = chrono::NaiveDate::from_ymd_opt(1990, 1, 1).expect("failed to get base date");
107    if value < base_date
108        || value > chrono::NaiveDate::from_ymd_opt(2168, 12, 31).expect("failed to get base date")
109    {
110        return Err(format!("Date out of range: {}", value));
111    }
112    let days = (value - base_date).num_days() as i16;
113    bytearray[byte_index..byte_index + 2].copy_from_slice(&days.to_be_bytes());
114    Ok(())
115}
116
117#[cfg(test)]
118mod setters_tests {
119    use super::*;
120    use chrono::NaiveDate;
121
122    #[test]
123    fn test_set_bool() {
124        let mut data = vec![0; 1];
125        set_bool(&mut data, 0, 0, true).unwrap();
126        assert_eq!(data, vec![1]);
127        set_bool(&mut data, 0, 0, false).unwrap();
128        assert_eq!(data, vec![0]);
129    }
130
131    #[test]
132    fn test_set_byte() {
133        let mut data = vec![0; 1];
134        set_byte(&mut data, 0, 255);
135        assert_eq!(data, vec![255]);
136    }
137
138    #[test]
139    fn test_set_word() {
140        let mut data = vec![0; 2];
141        set_word(&mut data, 0, 65535);
142        assert_eq!(data, vec![255, 255]);
143    }
144
145    #[test]
146    fn test_set_int() {
147        let mut data = vec![0; 2];
148        set_int(&mut data, 0, -32768);
149        assert_eq!(data, vec![128, 0]);
150    }
151
152    #[test]
153    fn test_set_date() {
154        let mut data = vec![0; 2];
155        let date = NaiveDate::from_ymd_opt(2024, 3, 27).unwrap();
156        set_date(&mut data, 0, date).unwrap();
157        assert_eq!(data, vec![48, 216]);
158    }
159    #[test]
160    fn test_set_uint() {
161        let mut bytearray = [0u8; 10];
162        set_uint(&mut bytearray, 2, 0x1234);
163        assert_eq!(bytearray[2], 0x12);
164        assert_eq!(bytearray[3], 0x34);
165    }
166
167    #[test]
168    fn test_set_real() {
169        let mut bytearray = [0u8; 10];
170        set_real(&mut bytearray, 2, 12.34);
171        assert_eq!(bytearray[2..6], 12.34f32.to_be_bytes());
172    }
173
174    #[test]
175    fn test_set_dword() {
176        let mut bytearray = [0u8; 10];
177        set_dword(&mut bytearray, 2, 0x12345678);
178        assert_eq!(bytearray[2], 0x12);
179        assert_eq!(bytearray[3], 0x34);
180        assert_eq!(bytearray[4], 0x56);
181        assert_eq!(bytearray[5], 0x78);
182    }
183
184    #[test]
185    fn test_set_dint() {
186        let mut bytearray = [0u8; 10];
187        set_dint(&mut bytearray, 2, -12345678);
188        assert_eq!(bytearray[2..6], (-12345678i32).to_be_bytes());
189    }
190
191    #[test]
192    fn test_set_udint() {
193        let mut bytearray = [0u8; 10];
194        set_udint(&mut bytearray, 2, 0x12345678);
195        assert_eq!(bytearray[2], 0x12);
196        assert_eq!(bytearray[3], 0x34);
197        assert_eq!(bytearray[4], 0x56);
198        assert_eq!(bytearray[5], 0x78);
199    }
200
201    #[test]
202    fn test_set_time() {
203        let mut bytearray = [0u8; 10];
204        set_time(&mut bytearray, 2, "0:0:0:1:0.0").unwrap();
205        assert_eq!(bytearray[2..6], 1000i32.to_be_bytes());
206    }
207
208    #[test]
209    fn test_set_usint() {
210        let mut bytearray = [0u8; 10];
211        set_usint(&mut bytearray, 2, 0x12);
212        assert_eq!(bytearray[2], 0x12);
213    }
214
215    #[test]
216    fn test_set_sint() {
217        let mut bytearray = [0u8; 10];
218        set_sint(&mut bytearray, 2, -5);
219        assert_eq!(bytearray[2], (-5i8) as u8);
220    }
221
222    #[test]
223    fn test_set_lreal() {
224        let mut bytearray = [0u8; 10];
225        set_lreal(&mut bytearray, 2, 12.34);
226        assert_eq!(bytearray[2..10], 12.34f64.to_be_bytes());
227    }
228
229    #[test]
230    fn test_set_char() {
231        let mut bytearray = [0u8; 10];
232        set_char(&mut bytearray, 2, 'A').unwrap();
233        assert_eq!(bytearray[2], b'A');
234    }
235
236    #[test]
237    fn test_set_char_non_ascii() {
238        let mut bytearray = [0u8; 10];
239        let result = set_char(&mut bytearray, 2, 'รง');
240        assert!(result.is_err());
241    }
242
243    #[test]
244    fn test_parse_time_string_valid() {
245        let duration = parse_time_string("0:0:0:1:0.0").unwrap();
246        assert_eq!(duration.as_millis(), 1000);
247    }
248
249    #[test]
250    fn test_parse_time_string_invalid() {
251        let result = parse_time_string("invalid time");
252        assert!(result.is_err());
253    }
254}