modbius_core/requests/
read.rs

1use crate::functions::PublicModbusFunction;
2
3// This could have been a trait but extracting the behavior into a trait doesn't make sense. They wouldn't
4// make sense with generics or trait objects because they are completely different requests in theory.
5// Also it is inacceptable to require all users to include a (definitely weirdly named) 
6// trait in order to work with these request structures.
7macro_rules! read_req {
8    ($name:ident, $fcode:expr, $entity:literal, $test:ident) => {
9        #[doc=concat!("The request structure to read ", $entity)]
10        #[doc=concat!("\n")]
11        /// used to parse and build modbus data to read quantity entities starting from addr.
12        #[derive(Debug, Clone, Copy, Hash, Default, PartialEq, Eq, PartialOrd, Ord)]
13        pub struct $name {
14            pub addr: u16,
15            pub quantity: u16,
16        }
17
18        impl $name {
19            /// The Modbus function this read requests corresponds to.
20            pub const MODBUS_FUNCTION_CODE: PublicModbusFunction = $fcode;
21
22            #[doc=concat!("Create a new request to read quantity ", $entity)]
23            pub const fn new(addr: u16, quantity: u16) -> Self {
24                Self { addr, quantity }
25            }
26
27            /// Parse this request from the given modbus data
28            ///
29            /// The data should only consist out of the address and quantity as the slave id function
30            /// will be already read through other means.
31            pub fn from_data(
32                data: &[u8],
33            ) -> Result<(Self, &[u8]), $crate::ModbusSerializationError> {
34                if data.len() < 4 {
35                    Err($crate::ModbusSerializationError::UnexpectedEOF {
36                        expected: 4,
37                        got: data.len(),
38                    })
39                } else {
40                    Ok(unsafe { Self::from_data_unchecked(data) })
41                }
42            }
43
44            /// Parse this request from the given modbus data without bounds checks.
45            ///
46            /// The data should only consist out of the address and quantity as the slave id function
47            /// will be already read through other means.
48            ///
49            /// # Safety
50            /// This function causes undefined behavior if the len of data is smaller than 4
51            pub unsafe fn from_data_unchecked(data: &[u8]) -> (Self, &[u8]) {
52                let (addr, data) = $crate::util::read_u16_unchecked(data);
53                let (quantity, data) = $crate::util::read_u16_unchecked(data);
54
55                (Self::new(addr, quantity), data)
56            }
57
58            /// Create modbus data of the correct size from this request
59            ///
60            /// The format of the array will be [addrhi, addrlo, quantityhi, quantitylo] in big endian
61            pub fn into_data(self) -> [u8; 5] {
62                let addr = self.addr.to_be_bytes();
63                let quantity = self.quantity.to_be_bytes();
64                [Self::MODBUS_FUNCTION_CODE as u8, addr[0], addr[1], quantity[0], quantity[1]]
65            }
66
67            /// Write this request to the slice as modbus data
68            pub fn write_to_slice(
69                self,
70                out: &mut [u8],
71            ) -> Result<(), $crate::ModbusSerializationError> {
72                if out.len() < 5 {
73                    return Err($crate::ModbusSerializationError::InsufficientBuffer {
74                        expected: 5,
75                        got: out.len(),
76                    });
77                }
78
79                unsafe { self.write_to_slice_unchecked(out) };
80                Ok(())
81            }
82
83            /// Write this request to the slice as modbus data without bounds checking.
84            ///
85            /// # Safety
86            /// This function invokes undefined behavior if the len of data is less than 5
87            pub unsafe fn write_to_slice_unchecked(self, out: &mut [u8]) {
88                out.get_unchecked_mut(0..5).copy_from_slice(&self.into_data());
89            }
90        }
91
92        #[cfg(test)]
93        mod $test {
94            use super::*;
95
96            #[test]
97            fn create() {
98                let req = $name {
99                    addr: 10,
100                    quantity: 20,
101                };
102
103                let req_new = $name::new(10, 20);
104
105                assert_eq!(req, req_new);
106                assert_eq!(req.addr, req_new.addr);
107                assert_eq!(req.quantity, req_new.quantity);
108            }
109
110            #[test]
111            fn from_data0() {
112                let data = [0, 10, 0, 20];
113                let (req, tail) = $name::from_data(&data).unwrap();
114
115                assert_eq!(req.addr, 10);
116                assert_eq!(req.quantity, 20);
117                assert!(tail.is_empty());
118            }
119
120            #[test]
121            fn from_data1() {
122                let data = [1, 10, 2, 20];
123                let (req, tail) = $name::from_data(&data).unwrap();
124
125                assert_eq!(req.addr, 266);
126                assert_eq!(req.quantity, 532);
127                assert!(tail.is_empty());
128            }
129
130            #[test]
131            fn from_data2() {
132                let data = [0, 255, 0, 255];
133                let (req, tail) = $name::from_data(&data).unwrap();
134
135                assert_eq!(req.addr, 255);
136                assert_eq!(req.quantity, 255);
137                assert!(tail.is_empty());
138            }
139
140            #[test]
141            fn from_data3() {
142                let data = [255, 0, 255, 0];
143                let (req, tail) = $name::from_data(&data).unwrap();
144
145                assert_eq!(req.addr, 65280);
146                assert_eq!(req.quantity, 65280);
147                assert!(tail.is_empty());
148            }
149
150            #[test]
151            fn from_data4() {
152                let data = [255, 255, 255, 255];
153                let (req, tail) = $name::from_data(&data).unwrap();
154
155                assert_eq!(req.addr, u16::MAX);
156                assert_eq!(req.quantity, u16::MAX);
157                assert!(tail.is_empty());
158            }
159
160            #[test]
161            fn from_data_unchecked0() {
162                let data = [0, 10, 0, 20];
163                let (req, tail) = $name::from_data(&data).unwrap();
164
165                assert_eq!(req.addr, 10);
166                assert_eq!(req.quantity, 20);
167                assert!(tail.is_empty());
168            }
169
170            #[test]
171            fn from_data_unchecked1() {
172                let data = [1, 10, 2, 20];
173                let (req, tail) = unsafe { $name::from_data_unchecked(&data) };
174
175                assert_eq!(req.addr, 266);
176                assert_eq!(req.quantity, 532);
177                assert!(tail.is_empty());
178            }
179
180            #[test]
181            fn from_data_unchecked2() {
182                let data = [0, 255, 0, 255];
183                let (req, tail) = unsafe { $name::from_data_unchecked(&data) };
184
185                assert_eq!(req.addr, 255);
186                assert_eq!(req.quantity, 255);
187                assert!(tail.is_empty());
188            }
189
190            #[test]
191            fn from_data_unchecked3() {
192                let data = [255, 0, 255, 0];
193                let (req, tail) = unsafe { $name::from_data_unchecked(&data) };
194
195                assert_eq!(req.addr, 65280);
196                assert_eq!(req.quantity, 65280);
197                assert!(tail.is_empty());
198            }
199
200            #[test]
201            fn from_data_unchecked4() {
202                let data = [255, 255, 255, 255];
203                let (req, tail) = unsafe { $name::from_data_unchecked(&data) };
204
205                assert_eq!(req.addr, u16::MAX);
206                assert_eq!(req.quantity, u16::MAX);
207                assert!(tail.is_empty());
208            }
209
210            #[test]
211            fn from_data_fail0() {
212                let data = [255, 255];
213                let res = $name::from_data(&data);
214                assert_eq!(
215                    res.unwrap_err(),
216                    $crate::ModbusSerializationError::UnexpectedEOF {
217                        expected: 4,
218                        got: 2
219                    }
220                );
221            }
222
223            #[test]
224            fn from_data_fail1() {
225                let data = [];
226                let res = $name::from_data(&data);
227                assert_eq!(
228                    res.unwrap_err(),
229                    $crate::ModbusSerializationError::UnexpectedEOF {
230                        expected: 4,
231                        got: 0
232                    }
233                );
234            }
235
236            #[test]
237            fn from_data_fail2() {
238                let data = [255, 255, 0];
239                let res = $name::from_data(&data);
240                assert_eq!(
241                    res.unwrap_err(),
242                    $crate::ModbusSerializationError::UnexpectedEOF {
243                        expected: 4,
244                        got: 3
245                    }
246                );
247            }
248
249            #[test]
250            fn from_data_tail0() {
251                let data = [255, 255, 255, 255, 1, 2, 3, 4];
252                let (req, tail) = $name::from_data(&data).unwrap();
253
254                assert_eq!(req.addr, u16::MAX);
255                assert_eq!(req.quantity, u16::MAX);
256                assert_eq!(tail, &[1, 2, 3, 4]);
257            }
258
259            #[test]
260            fn from_data_tail1() {
261                let data = [255, 255, 255, 255, 1];
262                let (req, tail) = $name::from_data(&data).unwrap();
263
264                assert_eq!(req.addr, u16::MAX);
265                assert_eq!(req.quantity, u16::MAX);
266                assert_eq!(tail, &[1]);
267            }
268
269            #[test]
270            fn from_data_unchecked_tail0() {
271                let data = [255, 255, 255, 255, 1, 2, 3, 4];
272                let (req, tail) = unsafe { $name::from_data_unchecked(&data) };
273
274                assert_eq!(req.addr, u16::MAX);
275                assert_eq!(req.quantity, u16::MAX);
276                assert_eq!(tail, &[1, 2, 3, 4]);
277            }
278
279            #[test]
280            fn from_data_unchecked_tail1() {
281                let data = [255, 255, 255, 255, 1];
282                let (req, tail) = unsafe { $name::from_data_unchecked(&data) };
283
284                assert_eq!(req.addr, u16::MAX);
285                assert_eq!(req.quantity, u16::MAX);
286                assert_eq!(tail, &[1]);
287            }
288
289            #[test]
290            fn from_data_eq_from_data_unecked() {
291                let data = [255, 255, 255, 255];
292                let (req_unchecked, tail_unchecked) = unsafe { $name::from_data_unchecked(&data) };
293                let (req, tail) = $name::from_data(&data).unwrap();
294
295                assert_eq!(req.addr, u16::MAX);
296                assert_eq!(req.quantity, u16::MAX);
297
298                assert_eq!(req_unchecked.addr, u16::MAX);
299                assert_eq!(req_unchecked.quantity, u16::MAX);
300
301                assert_eq!(req.addr, req_unchecked.addr);
302                assert_eq!(req.quantity, req_unchecked.quantity);
303
304                assert!(tail.is_empty());
305                assert_eq!(tail, tail_unchecked);
306            }
307
308            #[test]
309            fn into_data0() {
310                let data = [255, 255, 255, 255];
311                let (req, _tail) = $name::from_data(&data).unwrap();
312                assert_eq!(req.addr, u16::MAX);
313                assert_eq!(req.quantity, u16::MAX);
314                assert_eq!(data, req.into_data()[1..]);
315            }
316
317            #[test]
318            fn into_data1() {
319                let data = [0, 255, 0, 255];
320                let (req, _tail) = $name::from_data(&data).unwrap();
321                assert_eq!(req.addr, 255);
322                assert_eq!(req.quantity, 255);
323                assert_eq!(data, req.into_data()[1..]);
324            }
325
326            #[test]
327            fn into_data2() {
328                let req = $name::new(10, 20);
329                assert_eq!(req.addr, 10);
330                assert_eq!(req.quantity, 20);
331                assert_eq!([0, 10, 0, 20], req.into_data()[1..]);
332            }
333
334            #[test]
335            fn write_to_slice0() {
336                let req = $name::new(10, 20);
337                let mut slice = [0; 5];
338
339                req.write_to_slice(&mut slice).unwrap();
340                assert_eq!(slice, [$name::MODBUS_FUNCTION_CODE as u8, 0, 10, 0, 20]);
341            }
342
343            #[test]
344            fn write_to_slice1() {
345                let req = $name::new(256, 255);
346                let mut slice = [0, 1, 2, 3, 4];
347
348                req.write_to_slice(&mut slice).unwrap();
349                assert_eq!(slice, [$name::MODBUS_FUNCTION_CODE as u8, 1, 0, 0, 255]);
350            }
351
352            #[test]
353            fn write_to_slice2() {
354                let req = $name::new(u16::MAX, u16::MAX);
355                let mut slice = [1, 1, 1, 1, 1, 0, 0, 0, 0];
356
357                req.write_to_slice(&mut slice).unwrap();
358                assert_eq!(
359                    slice,
360                    [$name::MODBUS_FUNCTION_CODE as u8, 255, 255, 255, 255, 0, 0, 0, 0]
361                );
362            }
363
364            #[test]
365            fn write_to_slice_fail0() {
366                let req = $name::new(u16::MAX, u16::MAX);
367                let mut slice = [1, 1, 1, 1];
368
369                let err = req.write_to_slice(&mut slice).unwrap_err();
370
371                assert_eq!(err, $crate::ModbusSerializationError::InsufficientBuffer { got: 4, expected: 5 });
372            }
373
374            #[test]
375            fn write_to_slice_fail1() {
376                let req = $name::new(u16::MAX, u16::MAX);
377                let mut slice = [];
378
379                let err = req.write_to_slice(&mut slice).unwrap_err();
380
381                assert_eq!(err, $crate::ModbusSerializationError::InsufficientBuffer { got: 0, expected: 5 });
382            }
383
384            #[test]
385            fn write_to_slice_eq_write_to_slice_unchecked() {
386                let req = $name::new(u16::MAX, u16::MAX);
387                let mut slice = [1,2,3,4,5];
388                let mut slice_unchecked = [1,2,3,4,5];
389
390                req.write_to_slice(&mut slice).unwrap();
391                unsafe { req.write_to_slice_unchecked(&mut slice_unchecked) };
392
393                assert_eq!(slice, slice_unchecked);
394                assert_eq!(slice, [$name::MODBUS_FUNCTION_CODE as u8, 255, 255, 255, 255]);
395                assert_eq!(slice, [$name::MODBUS_FUNCTION_CODE as u8, 255, 255, 255, 255]);
396            }
397        }
398    };
399}
400
401read_req!(ReadCoils, PublicModbusFunction::ReadCoils, "Coils", coils);
402read_req!(
403    ReadDiscreteInputs,
404    PublicModbusFunction::ReadDiscreteInputs,
405    "DiscreteInputs",
406    discrete_inputs
407);
408read_req!(
409    ReadHoldingRegisters,
410    PublicModbusFunction::ReadHoldingRegisters,
411    "HoldingRegisters",
412    holding_registers
413);
414read_req!(
415    ReadInputRegisters,
416    PublicModbusFunction::ReadInputRegisters,
417    "InputRegisters",
418    input_registers
419);