modbus_core/codec/
mod.rs

1// SPDX-FileCopyrightText: Copyright (c) 2018-2025 slowtec GmbH <post@slowtec.de>
2// SPDX-License-Identifier: MIT OR Apache-2.0
3
4use crate::{error::*, frame::*};
5use byteorder::{BigEndian, ByteOrder};
6
7pub mod rtu;
8pub mod tcp;
9
10/// The type of decoding
11#[cfg_attr(all(feature = "defmt", target_os = "none"), derive(defmt::Format))]
12#[derive(Debug, Clone, Copy, PartialEq, Eq)]
13pub enum DecoderType {
14    Request,
15    Response,
16}
17
18type Result<T> = core::result::Result<T, Error>;
19
20impl TryFrom<u8> for Exception {
21    type Error = Error;
22
23    fn try_from(code: u8) -> Result<Self> {
24        let ex = match code {
25            0x01 => Self::IllegalFunction,
26            0x02 => Self::IllegalDataAddress,
27            0x03 => Self::IllegalDataValue,
28            0x04 => Self::ServerDeviceFailure,
29            0x05 => Self::Acknowledge,
30            0x06 => Self::ServerDeviceBusy,
31            0x08 => Self::MemoryParityError,
32            0x0A => Self::GatewayPathUnavailable,
33            0x0B => Self::GatewayTargetDevice,
34            _ => {
35                return Err(Error::ExceptionCode(code));
36            }
37        };
38        Ok(ex)
39    }
40}
41
42impl From<ExceptionResponse> for [u8; 2] {
43    fn from(ex: ExceptionResponse) -> [u8; 2] {
44        let data = &mut [0; 2];
45        let fn_code: u8 = ex.function.value();
46        debug_assert!(fn_code < 0x80);
47        data[0] = fn_code + 0x80;
48        data[1] = ex.exception as u8;
49        *data
50    }
51}
52
53impl TryFrom<&[u8]> for ExceptionResponse {
54    type Error = Error;
55
56    fn try_from(bytes: &[u8]) -> Result<Self> {
57        if bytes.is_empty() {
58            return Err(Error::BufferSize);
59        }
60        let fn_err_code = bytes[0];
61        if fn_err_code < 0x80 {
62            return Err(Error::ExceptionFnCode(fn_err_code));
63        }
64        let function = FunctionCode::new(fn_err_code - 0x80);
65        let exception = Exception::try_from(bytes[1])?;
66        Ok(ExceptionResponse {
67            function,
68            exception,
69        })
70    }
71}
72
73impl<'r> TryFrom<&'r [u8]> for Request<'r> {
74    type Error = Error;
75
76    fn try_from(bytes: &'r [u8]) -> Result<Self> {
77        use FunctionCode as F;
78
79        if bytes.is_empty() {
80            return Err(Error::BufferSize);
81        }
82
83        let fn_code = bytes[0];
84
85        if bytes.len() < min_request_pdu_len(FunctionCode::new(fn_code)) {
86            return Err(Error::BufferSize);
87        }
88
89        let req = match FunctionCode::new(fn_code) {
90            F::ReadCoils
91            | F::ReadDiscreteInputs
92            | F::ReadInputRegisters
93            | F::ReadHoldingRegisters
94            | F::WriteSingleRegister => {
95                let addr = BigEndian::read_u16(&bytes[1..3]);
96                let quantity = BigEndian::read_u16(&bytes[3..5]);
97
98                match FunctionCode::new(fn_code) {
99                    F::ReadCoils => Self::ReadCoils(addr, quantity),
100                    F::ReadDiscreteInputs => Self::ReadDiscreteInputs(addr, quantity),
101                    F::ReadInputRegisters => Self::ReadInputRegisters(addr, quantity),
102                    F::ReadHoldingRegisters => Self::ReadHoldingRegisters(addr, quantity),
103                    F::WriteSingleRegister => Self::WriteSingleRegister(addr, quantity),
104                    _ => unreachable!(),
105                }
106            }
107            F::WriteSingleCoil => Self::WriteSingleCoil(
108                BigEndian::read_u16(&bytes[1..3]),
109                u16_coil_to_bool(BigEndian::read_u16(&bytes[3..5]))?,
110            ),
111            F::WriteMultipleCoils => {
112                let address = BigEndian::read_u16(&bytes[1..3]);
113                let quantity = BigEndian::read_u16(&bytes[3..5]) as usize;
114                let byte_count = bytes[5];
115                if bytes.len() < (6 + byte_count as usize) {
116                    return Err(Error::ByteCount(byte_count));
117                }
118                let data = &bytes[6..];
119                let coils = Coils { data, quantity };
120                Self::WriteMultipleCoils(address, coils)
121            }
122            F::WriteMultipleRegisters => {
123                let address = BigEndian::read_u16(&bytes[1..3]);
124                let quantity = BigEndian::read_u16(&bytes[3..5]) as usize;
125                let byte_count = bytes[5];
126                if bytes.len() < (6 + byte_count as usize) {
127                    return Err(Error::ByteCount(byte_count));
128                }
129                let data = Data {
130                    quantity,
131                    data: &bytes[6..6 + byte_count as usize],
132                };
133                Self::WriteMultipleRegisters(address, data)
134            }
135            F::ReadWriteMultipleRegisters => {
136                let read_address = BigEndian::read_u16(&bytes[1..3]);
137                let read_quantity = BigEndian::read_u16(&bytes[3..5]);
138                let write_address = BigEndian::read_u16(&bytes[5..7]);
139                let write_quantity = BigEndian::read_u16(&bytes[7..9]) as usize;
140                let write_count = bytes[9];
141                if bytes.len() < (10 + write_count as usize) {
142                    return Err(Error::ByteCount(write_count));
143                }
144                let data = Data {
145                    quantity: write_quantity,
146                    data: &bytes[10..10 + write_count as usize],
147                };
148                Self::ReadWriteMultipleRegisters(read_address, read_quantity, write_address, data)
149            }
150            _ => match fn_code {
151                fn_code if fn_code < 0x80 => {
152                    Self::Custom(FunctionCode::Custom(fn_code), &bytes[1..])
153                }
154                _ => return Err(Error::FnCode(fn_code)),
155            },
156        };
157        Ok(req)
158    }
159}
160
161impl<'r> TryFrom<&'r [u8]> for Response<'r> {
162    type Error = Error;
163
164    fn try_from(bytes: &'r [u8]) -> Result<Self> {
165        use FunctionCode as F;
166        if bytes.is_empty() {
167            return Err(Error::BufferSize);
168        }
169        let fn_code = bytes[0];
170        if bytes.len() < min_response_pdu_len(FunctionCode::new(fn_code)) {
171            return Err(Error::BufferSize);
172        }
173        let rsp = match FunctionCode::new(fn_code) {
174            F::ReadCoils | FunctionCode::ReadDiscreteInputs => {
175                let byte_count = bytes[1] as usize;
176                if byte_count + 2 > bytes.len() {
177                    return Err(Error::BufferSize);
178                }
179                let data = &bytes[2..byte_count + 2];
180                // Here we have not information about the exact requested quantity
181                // therefore we just assume that the whole byte is meant.
182                let quantity = byte_count * 8;
183
184                match FunctionCode::new(fn_code) {
185                    FunctionCode::ReadCoils => Self::ReadCoils(Coils { data, quantity }),
186                    FunctionCode::ReadDiscreteInputs => {
187                        Self::ReadDiscreteInputs(Coils { data, quantity })
188                    }
189                    _ => unreachable!(),
190                }
191            }
192            F::WriteSingleCoil => Self::WriteSingleCoil(BigEndian::read_u16(&bytes[1..])),
193
194            F::WriteMultipleCoils | F::WriteSingleRegister | F::WriteMultipleRegisters => {
195                let addr = BigEndian::read_u16(&bytes[1..]);
196                let payload = BigEndian::read_u16(&bytes[3..]);
197                match FunctionCode::new(fn_code) {
198                    F::WriteMultipleCoils => Self::WriteMultipleCoils(addr, payload),
199                    F::WriteSingleRegister => Self::WriteSingleRegister(addr, payload),
200                    F::WriteMultipleRegisters => Self::WriteMultipleRegisters(addr, payload),
201                    _ => unreachable!(),
202                }
203            }
204            F::ReadInputRegisters | F::ReadHoldingRegisters | F::ReadWriteMultipleRegisters => {
205                let byte_count = bytes[1] as usize;
206                let quantity = byte_count / 2;
207                if byte_count + 2 > bytes.len() {
208                    return Err(Error::BufferSize);
209                }
210                let data = &bytes[2..2 + byte_count];
211                let data = Data { data, quantity };
212
213                match FunctionCode::new(fn_code) {
214                    F::ReadInputRegisters => Self::ReadInputRegisters(data),
215                    F::ReadHoldingRegisters => Self::ReadHoldingRegisters(data),
216                    F::ReadWriteMultipleRegisters => Self::ReadWriteMultipleRegisters(data),
217                    _ => unreachable!(),
218                }
219            }
220            _ => Self::Custom(FunctionCode::new(fn_code), &bytes[1..]),
221        };
222        Ok(rsp)
223    }
224}
225
226/// Encode a struct into a buffer.
227pub trait Encode {
228    fn encode(&self, buf: &mut [u8]) -> Result<usize>;
229}
230
231impl Encode for Request<'_> {
232    fn encode(&self, buf: &mut [u8]) -> Result<usize> {
233        if buf.len() < self.pdu_len() {
234            return Err(Error::BufferSize);
235        }
236        buf[0] = FunctionCode::from(*self).value();
237        match self {
238            Self::ReadCoils(address, payload)
239            | Self::ReadDiscreteInputs(address, payload)
240            | Self::ReadInputRegisters(address, payload)
241            | Self::ReadHoldingRegisters(address, payload)
242            | Self::WriteSingleRegister(address, payload) => {
243                BigEndian::write_u16(&mut buf[1..], *address);
244                BigEndian::write_u16(&mut buf[3..], *payload);
245            }
246            Self::WriteSingleCoil(address, state) => {
247                BigEndian::write_u16(&mut buf[1..], *address);
248                BigEndian::write_u16(&mut buf[3..], bool_to_u16_coil(*state));
249            }
250            Self::WriteMultipleCoils(address, coils) => {
251                BigEndian::write_u16(&mut buf[1..], *address);
252                let len = coils.len();
253                BigEndian::write_u16(&mut buf[3..], len as u16);
254                buf[5] = coils.packed_len() as u8;
255                coils.copy_to(&mut buf[6..]);
256            }
257            Self::WriteMultipleRegisters(address, words) => {
258                BigEndian::write_u16(&mut buf[1..], *address);
259                let len = words.len();
260                BigEndian::write_u16(&mut buf[3..], len as u16);
261                buf[5] = len as u8 * 2;
262                for (idx, byte) in words.data.iter().enumerate() {
263                    buf[idx + 6] = *byte;
264                }
265            }
266            Self::ReadWriteMultipleRegisters(read_address, quantity, write_address, words) => {
267                BigEndian::write_u16(&mut buf[1..], *read_address);
268                BigEndian::write_u16(&mut buf[3..], *quantity);
269                BigEndian::write_u16(&mut buf[5..], *write_address);
270                let n = words.len();
271                BigEndian::write_u16(&mut buf[7..], n as u16);
272                buf[9] = n as u8 * 2;
273                for (idx, byte) in words.data.iter().enumerate() {
274                    buf[idx + 10] = *byte;
275                }
276            }
277            Self::Custom(_, custom_data) => {
278                custom_data.iter().enumerate().for_each(|(idx, d)| {
279                    buf[idx + 1] = *d;
280                });
281            }
282            #[cfg(feature = "rtu")]
283            _ => panic!(),
284        }
285        Ok(self.pdu_len())
286    }
287}
288
289impl Encode for Response<'_> {
290    fn encode(&self, buf: &mut [u8]) -> Result<usize> {
291        if buf.len() < self.pdu_len() {
292            return Err(Error::BufferSize);
293        }
294
295        buf[0] = FunctionCode::from(*self).value();
296        match self {
297            Self::ReadCoils(coils) | Self::ReadDiscreteInputs(coils) => {
298                buf[1] = coils.packed_len() as u8;
299                coils.copy_to(&mut buf[2..]);
300            }
301            Self::ReadInputRegisters(registers)
302            | Self::ReadHoldingRegisters(registers)
303            | Self::ReadWriteMultipleRegisters(registers) => {
304                buf[1] = (registers.len() * 2) as u8;
305                registers.copy_to(&mut buf[2..]);
306            }
307            Self::WriteSingleCoil(address) => {
308                BigEndian::write_u16(&mut buf[1..], *address);
309            }
310            Self::WriteMultipleCoils(address, payload)
311            | Self::WriteMultipleRegisters(address, payload)
312            | Self::WriteSingleRegister(address, payload) => {
313                BigEndian::write_u16(&mut buf[1..], *address);
314                BigEndian::write_u16(&mut buf[3..], *payload);
315            }
316            Self::Custom(_, custom_data) => {
317                for (idx, d) in custom_data.iter().enumerate() {
318                    buf[idx + 1] = *d;
319                }
320            }
321            #[cfg(feature = "rtu")]
322            Self::ReadExceptionStatus(error_code) => {
323                buf[1] = *error_code;
324            }
325            #[cfg(feature = "rtu")]
326            _ => {
327                // TODO:
328                unimplemented!()
329            }
330        }
331        Ok(self.pdu_len())
332    }
333}
334
335impl Encode for RequestPdu<'_> {
336    fn encode(&self, buf: &mut [u8]) -> Result<usize> {
337        self.0.encode(buf)
338    }
339}
340
341impl Encode for ResponsePdu<'_> {
342    fn encode(&self, buf: &mut [u8]) -> Result<usize> {
343        if buf.is_empty() {
344            return Err(Error::BufferSize);
345        }
346        match self.0 {
347            Ok(res) => res.encode(buf),
348            Err(e) => e.encode(buf),
349        }
350    }
351}
352
353impl Encode for ExceptionResponse {
354    fn encode(&self, buf: &mut [u8]) -> Result<usize> {
355        if buf.is_empty() {
356            return Err(Error::BufferSize);
357        }
358        let [code, ex]: [u8; 2] = (*self).into();
359        buf[0] = code;
360        buf[1] = ex;
361        Ok(2)
362    }
363}
364
365const fn min_request_pdu_len(fn_code: FunctionCode) -> usize {
366    use FunctionCode as F;
367    match fn_code {
368        F::ReadCoils
369        | F::ReadDiscreteInputs
370        | F::ReadInputRegisters
371        | F::WriteSingleCoil
372        | F::ReadHoldingRegisters
373        | F::WriteSingleRegister => 5,
374        F::WriteMultipleCoils | F::WriteMultipleRegisters => 6,
375        F::ReadWriteMultipleRegisters => 10,
376        _ => 1,
377    }
378}
379
380const fn min_response_pdu_len(fn_code: FunctionCode) -> usize {
381    use FunctionCode as F;
382    match fn_code {
383        F::ReadCoils
384        | F::ReadDiscreteInputs
385        | F::ReadInputRegisters
386        | F::ReadHoldingRegisters
387        | F::ReadWriteMultipleRegisters => 2,
388        F::WriteSingleCoil => 3,
389        F::WriteMultipleCoils | F::WriteSingleRegister | F::WriteMultipleRegisters => 5,
390        _ => 1,
391    }
392}
393
394#[cfg(test)]
395mod tests {
396    use super::*;
397
398    #[test]
399    fn exception_response_into_bytes() {
400        let bytes: [u8; 2] = ExceptionResponse {
401            function: FunctionCode::new(0x03),
402            exception: Exception::IllegalDataAddress,
403        }
404        .into();
405        assert_eq!(bytes[0], 0x83);
406        assert_eq!(bytes[1], 0x02);
407    }
408
409    #[test]
410    fn exception_response_from_bytes() {
411        let data: &[u8] = &[0x79, 0x02];
412        assert!(ExceptionResponse::try_from(data).is_err());
413
414        let bytes: &[u8] = &[0x83, 0x02];
415        let rsp = ExceptionResponse::try_from(bytes).unwrap();
416        assert_eq!(
417            rsp,
418            ExceptionResponse {
419                function: FunctionCode::new(0x03),
420                exception: Exception::IllegalDataAddress,
421            }
422        );
423    }
424
425    #[test]
426    fn test_min_request_pdu_len() {
427        use FunctionCode::*;
428
429        assert_eq!(min_request_pdu_len(ReadCoils), 5);
430        assert_eq!(min_request_pdu_len(ReadDiscreteInputs), 5);
431        assert_eq!(min_request_pdu_len(ReadInputRegisters), 5);
432        assert_eq!(min_request_pdu_len(WriteSingleCoil), 5);
433        assert_eq!(min_request_pdu_len(ReadHoldingRegisters), 5);
434        assert_eq!(min_request_pdu_len(WriteSingleRegister), 5);
435        assert_eq!(min_request_pdu_len(WriteMultipleCoils), 6);
436        assert_eq!(min_request_pdu_len(WriteMultipleRegisters), 6);
437        assert_eq!(min_request_pdu_len(ReadWriteMultipleRegisters), 10);
438    }
439
440    #[test]
441    fn test_min_response_pdu_len() {
442        use FunctionCode::*;
443
444        assert_eq!(min_response_pdu_len(ReadCoils), 2);
445        assert_eq!(min_response_pdu_len(ReadDiscreteInputs), 2);
446        assert_eq!(min_response_pdu_len(ReadInputRegisters), 2);
447        assert_eq!(min_response_pdu_len(WriteSingleCoil), 3);
448        assert_eq!(min_response_pdu_len(ReadHoldingRegisters), 2);
449        assert_eq!(min_response_pdu_len(WriteSingleRegister), 5);
450        assert_eq!(min_response_pdu_len(WriteMultipleCoils), 5);
451        assert_eq!(min_response_pdu_len(WriteMultipleRegisters), 5);
452        assert_eq!(min_response_pdu_len(ReadWriteMultipleRegisters), 2);
453    }
454
455    mod serialize_requests {
456        use super::*;
457
458        #[test]
459        fn read_coils() {
460            let bytes = &mut [0; 4];
461            assert!(Request::ReadCoils(0x12, 4).encode(bytes).is_err());
462            let bytes = &mut [0; 5];
463            Request::ReadCoils(0x12, 4).encode(bytes).unwrap();
464            assert_eq!(bytes[0], 1);
465            assert_eq!(bytes[1], 0x00);
466            assert_eq!(bytes[2], 0x12);
467            assert_eq!(bytes[3], 0x00);
468            assert_eq!(bytes[4], 0x04);
469        }
470
471        #[test]
472        fn read_discrete_inputs() {
473            let bytes = &mut [0; 5];
474            Request::ReadDiscreteInputs(0x03, 19).encode(bytes).unwrap();
475            assert_eq!(bytes[0], 2);
476            assert_eq!(bytes[1], 0x00);
477            assert_eq!(bytes[2], 0x03);
478            assert_eq!(bytes[3], 0x00);
479            assert_eq!(bytes[4], 19);
480        }
481
482        #[test]
483        fn write_single_coil() {
484            let bytes = &mut [0; 5];
485            Request::WriteSingleCoil(0x1234, true)
486                .encode(bytes)
487                .unwrap();
488            assert_eq!(bytes[0], 5);
489            assert_eq!(bytes[1], 0x12);
490            assert_eq!(bytes[2], 0x34);
491            assert_eq!(bytes[3], 0xFF);
492            assert_eq!(bytes[4], 0x00);
493        }
494
495        #[test]
496        fn write_multiple_coils() {
497            let states = &[true, false, true, true];
498            let buf = &mut [0];
499            let bytes = &mut [0; 7];
500            Request::WriteMultipleCoils(0x3311, Coils::from_bools(states, buf).unwrap())
501                .encode(bytes)
502                .unwrap();
503            assert_eq!(bytes[0], 0x0F);
504            assert_eq!(bytes[1], 0x33);
505            assert_eq!(bytes[2], 0x11);
506            assert_eq!(bytes[3], 0x00);
507            assert_eq!(bytes[4], 0x04);
508            assert_eq!(bytes[5], 0x01);
509            assert_eq!(bytes[6], 0b_0000_1101);
510        }
511
512        #[test]
513        fn read_input_registers() {
514            let bytes = &mut [0; 5];
515            Request::ReadInputRegisters(0x09, 77).encode(bytes).unwrap();
516            assert_eq!(bytes[0], 4);
517            assert_eq!(bytes[1], 0x00);
518            assert_eq!(bytes[2], 0x09);
519            assert_eq!(bytes[3], 0x00);
520            assert_eq!(bytes[4], 0x4D);
521        }
522
523        #[test]
524        fn read_holding_registers() {
525            let bytes = &mut [0; 5];
526            Request::ReadHoldingRegisters(0x09, 77)
527                .encode(bytes)
528                .unwrap();
529            assert_eq!(bytes[0], 3);
530            assert_eq!(bytes[1], 0x00);
531            assert_eq!(bytes[2], 0x09);
532            assert_eq!(bytes[3], 0x00);
533            assert_eq!(bytes[4], 0x4D);
534        }
535
536        #[test]
537        fn write_single_register() {
538            let bytes = &mut [0; 5];
539            Request::WriteSingleRegister(0x07, 0xABCD)
540                .encode(bytes)
541                .unwrap();
542            assert_eq!(bytes[0], 6);
543            assert_eq!(bytes[1], 0x00);
544            assert_eq!(bytes[2], 0x07);
545            assert_eq!(bytes[3], 0xAB);
546            assert_eq!(bytes[4], 0xCD);
547        }
548
549        #[test]
550        fn write_multiple_registers() {
551            let buf = &mut [0; 4];
552            let bytes = &mut [0; 10];
553
554            Request::WriteMultipleRegisters(
555                0x06,
556                Data::from_words(&[0xABCD, 0xEF12], buf).unwrap(),
557            )
558            .encode(bytes)
559            .unwrap();
560
561            // function code
562            assert_eq!(bytes[0], 0x10);
563
564            // write starting address
565            assert_eq!(bytes[1], 0x00);
566            assert_eq!(bytes[2], 0x06);
567
568            // quantity to write
569            assert_eq!(bytes[3], 0x00);
570            assert_eq!(bytes[4], 0x02);
571
572            // write byte count
573            assert_eq!(bytes[5], 0x04);
574
575            // values
576            assert_eq!(bytes[6], 0xAB);
577            assert_eq!(bytes[7], 0xCD);
578            assert_eq!(bytes[8], 0xEF);
579            assert_eq!(bytes[9], 0x12);
580        }
581
582        #[test]
583        fn read_write_multiple_registers() {
584            let buf = &mut [0; 4];
585            let bytes = &mut [0; 14];
586            let data = Data::from_words(&[0xABCD, 0xEF12], buf).unwrap();
587            Request::ReadWriteMultipleRegisters(0x05, 51, 0x03, data)
588                .encode(bytes)
589                .unwrap();
590
591            // function code
592            assert_eq!(bytes[0], 0x17);
593
594            // read starting address
595            assert_eq!(bytes[1], 0x00);
596            assert_eq!(bytes[2], 0x05);
597
598            // quantity to read
599            assert_eq!(bytes[3], 0x00);
600            assert_eq!(bytes[4], 0x33);
601
602            // write starting address
603            assert_eq!(bytes[5], 0x00);
604            assert_eq!(bytes[6], 0x03);
605
606            // quantity to write
607            assert_eq!(bytes[7], 0x00);
608            assert_eq!(bytes[8], 0x02);
609
610            // write byte count
611            assert_eq!(bytes[9], 0x04);
612
613            // values
614            assert_eq!(bytes[10], 0xAB);
615            assert_eq!(bytes[11], 0xCD);
616            assert_eq!(bytes[12], 0xEF);
617            assert_eq!(bytes[13], 0x12);
618        }
619
620        #[test]
621        fn custom() {
622            let bytes = &mut [0; 5];
623            Request::Custom(FunctionCode::Custom(0x55), &[0xCC, 0x88, 0xAA, 0xFF])
624                .encode(bytes)
625                .unwrap();
626            assert_eq!(bytes[0], 0x55);
627            assert_eq!(bytes[1], 0xCC);
628            assert_eq!(bytes[2], 0x88);
629            assert_eq!(bytes[3], 0xAA);
630            assert_eq!(bytes[4], 0xFF);
631        }
632    }
633
634    mod deserialize_requests {
635        use super::*;
636
637        #[test]
638        fn empty_request() {
639            let data: &[u8] = &[];
640            assert!(Request::try_from(data).is_err());
641        }
642
643        #[test]
644        fn read_coils() {
645            let data: &[u8] = &[0x01];
646            assert!(Request::try_from(data).is_err());
647            let data: &[u8] = &[0x01, 0x0, 0x0, 0x22];
648            assert!(Request::try_from(data).is_err());
649
650            let data: &[u8] = &[0x01, 0x00, 0x12, 0x0, 0x4];
651            let req = Request::try_from(data).unwrap();
652            assert_eq!(req, Request::ReadCoils(0x12, 4));
653        }
654
655        #[test]
656        fn read_discrete_inputs() {
657            let data: &[u8] = &[2, 0x00, 0x03, 0x00, 19];
658            let req = Request::try_from(data).unwrap();
659            assert_eq!(req, Request::ReadDiscreteInputs(0x03, 19));
660        }
661
662        #[test]
663        fn write_single_coil() {
664            let bytes: &[u8] = &[5, 0x12, 0x34, 0xFF, 0x00];
665            let req = Request::try_from(bytes).unwrap();
666            assert_eq!(req, Request::WriteSingleCoil(0x1234, true));
667        }
668
669        #[test]
670        fn write_multiple_coils() {
671            let data: &[u8] = &[0x0F, 0x33, 0x11, 0x00, 0x04, 0x02, 0b_0000_1101];
672            assert!(Request::try_from(data).is_err());
673
674            let data: &[u8] = &[
675                0x0F, 0x33, 0x11, 0x00, 0x04, 0x00, // byte count == 0
676            ];
677            assert!(Request::try_from(data).is_ok());
678
679            let bytes: &[u8] = &[0x0F, 0x33, 0x11, 0x00, 0x04, 0x01, 0b_0000_1101];
680            let req = Request::try_from(bytes).unwrap();
681            assert_eq!(
682                req,
683                Request::WriteMultipleCoils(
684                    0x3311,
685                    Coils {
686                        quantity: 4,
687                        data: &[0b1101]
688                    }
689                )
690            );
691        }
692
693        #[test]
694        fn read_input_registers() {
695            let bytes: &[u8] = &[4, 0x00, 0x09, 0x00, 0x4D];
696            let req = Request::try_from(bytes).unwrap();
697            assert_eq!(req, Request::ReadInputRegisters(0x09, 77));
698        }
699
700        #[test]
701        fn read_holding_registers() {
702            let bytes: &[u8] = &[3, 0x00, 0x09, 0x00, 0x4D];
703            let req = Request::try_from(bytes).unwrap();
704            assert_eq!(req, Request::ReadHoldingRegisters(0x09, 77));
705        }
706
707        #[test]
708        fn write_single_register() {
709            let bytes: &[u8] = &[6, 0x00, 0x07, 0xAB, 0xCD];
710            let req = Request::try_from(bytes).unwrap();
711            assert_eq!(req, Request::WriteSingleRegister(0x07, 0xABCD));
712        }
713
714        #[test]
715        fn write_multiple_registers() {
716            let data: &[u8] = &[0x10, 0x00, 0x06, 0x00, 0x02, 0x05, 0xAB, 0xCD, 0xEF, 0x12];
717            assert!(Request::try_from(data).is_err());
718
719            let bytes: &[u8] = &[0x10, 0x00, 0x06, 0x00, 0x02, 0x04, 0xAB, 0xCD, 0xEF, 0x12];
720            let req = Request::try_from(bytes).unwrap();
721            assert_eq!(
722                req,
723                Request::WriteMultipleRegisters(
724                    0x06,
725                    Data {
726                        quantity: 2,
727                        data: &[0xAB, 0xCD, 0xEF, 0x12]
728                    }
729                )
730            );
731            if let Request::WriteMultipleRegisters(_, data) = req {
732                assert_eq!(data.get(0), Some(0xABCD));
733                assert_eq!(data.get(1), Some(0xEF12));
734            } else {
735                unreachable!()
736            }
737        }
738
739        #[test]
740        fn read_write_multiple_registers() {
741            let data: &[u8] = &[
742                0x17, 0x00, 0x05, 0x00, 0x33, 0x00, 0x03, 0x00, 0x02, 0x05, 0xAB, 0xCD, 0xEF, 0x12,
743            ];
744            assert!(Request::try_from(data).is_err());
745            let bytes: &[u8] = &[
746                0x17, 0x00, 0x05, 0x00, 0x33, 0x00, 0x03, 0x00, 0x02, 0x04, 0xAB, 0xCD, 0xEF, 0x12,
747            ];
748            let req = Request::try_from(bytes).unwrap();
749            let data = Data {
750                quantity: 2,
751                data: &[0xAB, 0xCD, 0xEF, 0x12],
752            };
753            assert_eq!(
754                req,
755                Request::ReadWriteMultipleRegisters(0x05, 51, 0x03, data)
756            );
757            if let Request::ReadWriteMultipleRegisters(_, _, _, data) = req {
758                assert_eq!(data.get(0), Some(0xABCD));
759                assert_eq!(data.get(1), Some(0xEF12));
760            } else {
761                unreachable!()
762            }
763        }
764
765        #[test]
766        fn custom() {
767            let bytes: &[u8] = &[0x55, 0xCC, 0x88, 0xAA, 0xFF];
768            let req = Request::try_from(bytes).unwrap();
769            assert_eq!(
770                req,
771                Request::Custom(FunctionCode::Custom(0x55), &[0xCC, 0x88, 0xAA, 0xFF])
772            );
773        }
774    }
775
776    mod serialize_responses {
777        use super::*;
778
779        #[test]
780        fn read_coils() {
781            let buff: &mut [u8] = &mut [0];
782            let res = Response::ReadCoils(
783                Coils::from_bools(&[true, false, false, true, false], buff).unwrap(),
784            );
785            let bytes = &mut [0, 0];
786            assert!(res.encode(bytes).is_err());
787            let bytes = &mut [0, 0, 0];
788            res.encode(bytes).unwrap();
789            assert_eq!(bytes[0], 1);
790            assert_eq!(bytes[1], 1);
791            assert_eq!(bytes[2], 0b_0000_1001);
792        }
793
794        #[test]
795        fn read_discrete_inputs() {
796            let buff: &mut [u8] = &mut [0];
797            let res = Response::ReadDiscreteInputs(
798                Coils::from_bools(&[true, false, true, true], buff).unwrap(),
799            );
800            let bytes = &mut [0, 0, 0];
801            res.encode(bytes).unwrap();
802            assert_eq!(bytes[0], 2);
803            assert_eq!(bytes[1], 1);
804            assert_eq!(bytes[2], 0b_0000_1101);
805        }
806
807        #[test]
808        fn write_single_coil() {
809            let res = Response::WriteSingleCoil(0x33);
810            let bytes = &mut [0, 0, 0];
811            res.encode(bytes).unwrap();
812            assert_eq!(bytes[0], 5);
813            assert_eq!(bytes[1], 0x00);
814            assert_eq!(bytes[2], 0x33);
815        }
816
817        #[test]
818        fn write_multiple_coils() {
819            let res = Response::WriteMultipleCoils(0x3311, 5);
820            let bytes = &mut [0; 5];
821            res.encode(bytes).unwrap();
822            assert_eq!(bytes[0], 0x0F);
823            assert_eq!(bytes[1], 0x33);
824            assert_eq!(bytes[2], 0x11);
825            assert_eq!(bytes[3], 0x00);
826            assert_eq!(bytes[4], 0x05);
827        }
828
829        #[test]
830        fn read_input_registers() {
831            let buf: &mut [u8] = &mut [0; 6];
832            let res = Response::ReadInputRegisters(
833                Data::from_words(&[0xAA00, 0xCCBB, 0xEEDD], buf).unwrap(),
834            );
835            let bytes = &mut [0; 8];
836            res.encode(bytes).unwrap();
837            assert_eq!(bytes[0], 4);
838            assert_eq!(bytes[1], 0x06);
839            assert_eq!(bytes[2], 0xAA);
840            assert_eq!(bytes[3], 0x00);
841            assert_eq!(bytes[4], 0xCC);
842            assert_eq!(bytes[5], 0xBB);
843            assert_eq!(bytes[6], 0xEE);
844            assert_eq!(bytes[7], 0xDD);
845        }
846
847        #[test]
848        fn read_holding_registers() {
849            let buf: &mut [u8] = &mut [0; 4];
850            let res =
851                Response::ReadHoldingRegisters(Data::from_words(&[0xAA00, 0x1111], buf).unwrap());
852            let bytes = &mut [0; 6];
853            res.encode(bytes).unwrap();
854            assert_eq!(bytes[0], 3);
855            assert_eq!(bytes[1], 0x04);
856            assert_eq!(bytes[2], 0xAA);
857            assert_eq!(bytes[3], 0x00);
858            assert_eq!(bytes[4], 0x11);
859            assert_eq!(bytes[5], 0x11);
860        }
861
862        #[test]
863        fn write_single_register() {
864            let res = Response::WriteSingleRegister(0x07, 0xABCD);
865            let bytes = &mut [0; 5];
866            res.encode(bytes).unwrap();
867            assert_eq!(bytes[0], 6);
868            assert_eq!(bytes[1], 0x00);
869            assert_eq!(bytes[2], 0x07);
870            assert_eq!(bytes[3], 0xAB);
871            assert_eq!(bytes[4], 0xCD);
872        }
873
874        #[test]
875        fn write_multiple_registers() {
876            let res = Response::WriteMultipleRegisters(0x06, 2);
877            let bytes = &mut [0; 5];
878            res.encode(bytes).unwrap();
879            assert_eq!(bytes[0], 0x10);
880            assert_eq!(bytes[1], 0x00);
881            assert_eq!(bytes[2], 0x06);
882            assert_eq!(bytes[3], 0x00);
883            assert_eq!(bytes[4], 0x02);
884        }
885
886        #[test]
887        fn read_write_multiple_registers() {
888            let buf: &mut [u8] = &mut [0; 2];
889            let res =
890                Response::ReadWriteMultipleRegisters(Data::from_words(&[0x1234], buf).unwrap());
891            let bytes = &mut [0; 4];
892            res.encode(bytes).unwrap();
893            assert_eq!(bytes[0], 0x17);
894            assert_eq!(bytes[1], 0x02);
895            assert_eq!(bytes[2], 0x12);
896            assert_eq!(bytes[3], 0x34);
897        }
898
899        #[test]
900        fn custom() {
901            let res = Response::Custom(FunctionCode::Custom(0x55), &[0xCC, 0x88, 0xAA, 0xFF]);
902            let bytes = &mut [0; 5];
903            res.encode(bytes).unwrap();
904            assert_eq!(bytes[0], 0x55);
905            assert_eq!(bytes[1], 0xCC);
906            assert_eq!(bytes[2], 0x88);
907            assert_eq!(bytes[3], 0xAA);
908            assert_eq!(bytes[4], 0xFF);
909        }
910    }
911
912    mod deserialize_responses {
913        use super::*;
914
915        #[test]
916        fn read_coils() {
917            let bytes: &[u8] = &[1, 1, 0b_0000_1001];
918            let rsp = Response::try_from(bytes).unwrap();
919            assert_eq!(
920                rsp,
921                Response::ReadCoils(Coils {
922                    quantity: 8,
923                    data: &[0b_0000_1001]
924                })
925            );
926        }
927
928        #[test]
929        fn read_no_coils() {
930            let bytes: &[u8] = &[1, 0];
931            let rsp = Response::try_from(bytes).unwrap();
932            assert_eq!(
933                rsp,
934                Response::ReadCoils(Coils {
935                    quantity: 0,
936                    data: &[]
937                })
938            );
939        }
940
941        #[test]
942        fn read_coils_with_invalid_byte_count() {
943            let bytes: &[u8] = &[1, 2, 0x6];
944            assert!(Response::try_from(bytes).is_err());
945        }
946
947        #[test]
948        fn read_discrete_inputs() {
949            let bytes: &[u8] = &[2, 1, 0b_0000_1001];
950            let rsp = Response::try_from(bytes).unwrap();
951            assert_eq!(
952                rsp,
953                Response::ReadDiscreteInputs(Coils {
954                    quantity: 8,
955                    data: &[0b_0000_1001]
956                })
957            );
958        }
959
960        #[test]
961        fn write_single_coil() {
962            let bytes: &[u8] = &[5, 0x00, 0x33];
963            let rsp = Response::try_from(bytes).unwrap();
964            assert_eq!(rsp, Response::WriteSingleCoil(0x33));
965
966            let broken_bytes: &[u8] = &[5, 0x00];
967            assert!(Response::try_from(broken_bytes).is_err());
968        }
969
970        #[test]
971        fn write_multiple_coils() {
972            let bytes: &[u8] = &[0x0F, 0x33, 0x11, 0x00, 0x05];
973            let rsp = Response::try_from(bytes).unwrap();
974            assert_eq!(rsp, Response::WriteMultipleCoils(0x3311, 5));
975            let broken_bytes: &[u8] = &[0x0F, 0x33, 0x11, 0x00];
976            assert!(Response::try_from(broken_bytes).is_err());
977        }
978
979        #[test]
980        fn read_input_registers() {
981            let bytes: &[u8] = &[4, 0x06, 0xAA, 0x00, 0xCC, 0xBB, 0xEE, 0xDD];
982            let rsp = Response::try_from(bytes).unwrap();
983            assert_eq!(
984                rsp,
985                Response::ReadInputRegisters(Data {
986                    quantity: 3,
987                    data: &[0xAA, 0x00, 0xCC, 0xBB, 0xEE, 0xDD]
988                })
989            );
990        }
991
992        #[test]
993        fn read_holding_registers() {
994            let bytes: &[u8] = &[3, 0x04, 0xAA, 0x00, 0x11, 0x11];
995            let rsp = Response::try_from(bytes).unwrap();
996            assert_eq!(
997                rsp,
998                Response::ReadHoldingRegisters(Data {
999                    quantity: 2,
1000                    data: &[0xAA, 0x00, 0x11, 0x11]
1001                })
1002            );
1003        }
1004
1005        #[test]
1006        fn write_single_register() {
1007            let bytes: &[u8] = &[6, 0x00, 0x07, 0xAB, 0xCD];
1008            let rsp = Response::try_from(bytes).unwrap();
1009            assert_eq!(rsp, Response::WriteSingleRegister(0x07, 0xABCD));
1010            let broken_bytes: &[u8] = &[6, 0x00, 0x07, 0xAB];
1011            assert!(Response::try_from(broken_bytes).is_err());
1012        }
1013
1014        #[test]
1015        fn write_multiple_registers() {
1016            let bytes: &[u8] = &[0x10, 0x00, 0x06, 0x00, 0x02];
1017            let rsp = Response::try_from(bytes).unwrap();
1018            assert_eq!(rsp, Response::WriteMultipleRegisters(0x06, 2));
1019            let broken_bytes: &[u8] = &[0x10, 0x00, 0x06, 0x00];
1020            assert!(Response::try_from(broken_bytes).is_err());
1021        }
1022
1023        #[test]
1024        fn read_write_multiple_registers() {
1025            let bytes: &[u8] = &[0x17, 0x02, 0x12, 0x34];
1026            let rsp = Response::try_from(bytes).unwrap();
1027            assert_eq!(
1028                rsp,
1029                Response::ReadWriteMultipleRegisters(Data {
1030                    quantity: 1,
1031                    data: &[0x12, 0x34]
1032                })
1033            );
1034            let broken_bytes: &[u8] = &[0x17, 0x02, 0x12];
1035            assert!(Response::try_from(broken_bytes).is_err());
1036        }
1037
1038        #[test]
1039        fn custom() {
1040            let bytes: &[u8] = &[0x55, 0xCC, 0x88, 0xAA, 0xFF];
1041            let rsp = Response::try_from(bytes).unwrap();
1042            assert_eq!(
1043                rsp,
1044                Response::Custom(FunctionCode::Custom(0x55), &[0xCC, 0x88, 0xAA, 0xFF])
1045            );
1046            let bytes: &[u8] = &[0x66];
1047            let rsp = Response::try_from(bytes).unwrap();
1048            assert_eq!(rsp, Response::Custom(FunctionCode::Custom(0x66), &[]));
1049        }
1050    }
1051}