tokio_modbus/codec/
rtu.rs

1// SPDX-FileCopyrightText: Copyright (c) 2017-2025 slowtec GmbH <post@slowtec.de>
2// SPDX-License-Identifier: MIT OR Apache-2.0
3
4use std::io;
5
6use byteorder::{LittleEndian, ReadBytesExt as _};
7use smallvec::SmallVec;
8use tokio_util::codec::{Decoder, Encoder};
9
10use crate::{
11    bytes::{Buf, BufMut, Bytes, BytesMut},
12    frame::{rtu::*, MEI_TYPE_READ_DEVICE_IDENTIFICATION},
13    slave::SlaveId,
14};
15
16use super::{encode_request_pdu, request_pdu_size, RequestPdu};
17
18const MODBUS_CRC: crc::Crc<u16> = crc::Crc::<u16>::new(&crc::CRC_16_MODBUS);
19
20// [Modbus over Serial Line Specification and Implementation Guide V1.02](http://modbus.org/docs/Modbus_over_serial_line_V1_02.pdf), page 13
21// "The maximum size of a Modbus RTU frame is 256 bytes."
22const MAX_FRAME_LEN: usize = 256;
23
24type DroppedBytes = SmallVec<[u8; MAX_FRAME_LEN]>;
25
26#[derive(Debug)]
27pub(crate) struct FrameDecoder {
28    dropped_bytes: SmallVec<[u8; MAX_FRAME_LEN]>,
29}
30
31impl Default for FrameDecoder {
32    fn default() -> Self {
33        Self {
34            dropped_bytes: DroppedBytes::new(),
35        }
36    }
37}
38
39impl FrameDecoder {
40    pub(crate) fn decode(
41        &mut self,
42        buf: &mut BytesMut,
43        pdu_len: usize,
44    ) -> io::Result<Option<(SlaveId, Bytes)>> {
45        const CRC_BYTE_COUNT: usize = 2;
46
47        let adu_len = 1 + pdu_len;
48
49        if buf.len() < adu_len + CRC_BYTE_COUNT {
50            // Incomplete frame
51            return Ok(None);
52        }
53
54        let mut adu_buf = buf.split_to(adu_len);
55        let crc_buf = buf.split_to(CRC_BYTE_COUNT);
56
57        // Read trailing CRC and verify ADU
58        let crc_result =
59            read_crc(&mut io::Cursor::new(&crc_buf)).and_then(|crc| check_crc(&adu_buf, crc));
60
61        if let Err(err) = crc_result {
62            // CRC is invalid - restore the input buffer
63            let rem_buf = buf.split();
64            debug_assert!(buf.is_empty());
65            buf.unsplit(adu_buf);
66            buf.unsplit(crc_buf);
67            buf.unsplit(rem_buf);
68
69            return Err(err);
70        }
71
72        if !self.dropped_bytes.is_empty() {
73            log::warn!(
74                "Successfully decoded frame after dropping {} byte(s): {:X?}",
75                self.dropped_bytes.len(),
76                self.dropped_bytes
77            );
78            self.dropped_bytes.clear();
79        }
80        let slave_id = adu_buf.split_to(1)[0];
81        let pdu_data = adu_buf.freeze();
82
83        Ok(Some((slave_id, pdu_data)))
84    }
85
86    pub(crate) fn recover_on_error(&mut self, buf: &mut BytesMut) {
87        // If decoding failed the buffer cannot be empty
88        debug_assert!(!buf.is_empty());
89        // Skip and record the first byte of the buffer
90        {
91            let first = buf.first().unwrap();
92            log::debug!("Dropped first byte: {first:X?}");
93            if self.dropped_bytes.len() >= MAX_FRAME_LEN {
94                log::error!(
95                    "Giving up to decode frame after dropping {} byte(s): {:X?}",
96                    self.dropped_bytes.len(),
97                    self.dropped_bytes
98                );
99                self.dropped_bytes.clear();
100            }
101            self.dropped_bytes.push(*first);
102        }
103        buf.advance(1);
104    }
105}
106
107#[cfg(any(feature = "rtu-over-tcp-server", feature = "rtu-server"))]
108#[derive(Debug, Default)]
109pub(crate) struct RequestDecoder {
110    frame_decoder: FrameDecoder,
111}
112
113#[derive(Debug, Default)]
114pub(crate) struct ResponseDecoder {
115    frame_decoder: FrameDecoder,
116}
117
118#[derive(Debug, Default)]
119pub(crate) struct ClientCodec {
120    pub(crate) decoder: ResponseDecoder,
121}
122
123#[cfg(any(feature = "rtu-over-tcp-server", feature = "rtu-server"))]
124#[derive(Debug, Default)]
125pub(crate) struct ServerCodec {
126    pub(crate) decoder: RequestDecoder,
127}
128
129#[cfg(any(feature = "rtu-over-tcp-server", feature = "rtu-server"))]
130fn get_request_pdu_len(adu_buf: &BytesMut) -> io::Result<Option<usize>> {
131    if let Some(fn_code) = adu_buf.get(1) {
132        let len = match fn_code {
133            0x01..=0x06 => 5,
134            0x07 | 0x0B | 0x0C | 0x11 => 1,
135            0x0F | 0x10 => {
136                return Ok(adu_buf
137                    .get(6)
138                    .map(|&byte_count| 6 + usize::from(byte_count)));
139            }
140            0x16 => 7,
141            0x18 => 3,
142            0x17 => {
143                return Ok(adu_buf
144                    .get(10)
145                    .map(|&byte_count| 10 + usize::from(byte_count)));
146            }
147            0x2b if adu_buf
148                .get(2)
149                .is_some_and(|mei| *mei == MEI_TYPE_READ_DEVICE_IDENTIFICATION) =>
150            {
151                4
152            }
153            _ => {
154                return Err(io::Error::new(
155                    io::ErrorKind::InvalidData,
156                    format!("Invalid function code: 0x{fn_code:0>2X}"),
157                ));
158            }
159        };
160        Ok(Some(len))
161    } else {
162        Ok(None)
163    }
164}
165
166fn get_response_pdu_len(adu_buf: &BytesMut) -> io::Result<Option<usize>> {
167    if let Some(fn_code) = adu_buf.get(1) {
168        #[allow(clippy::match_same_arms)]
169        let len = match fn_code {
170            0x01..=0x04 | 0x0C | 0x11 | 0x17 => {
171                return Ok(adu_buf
172                    .get(2)
173                    .map(|&byte_count| 2 + usize::from(byte_count)));
174            }
175            0x05 | 0x06 | 0x0B | 0x0F | 0x10 => 5,
176            0x07 => 2,
177            0x16 => 7,
178            0x18 => {
179                if adu_buf.len() > 3 {
180                    3 + usize::from(u16::from_be_bytes([adu_buf[2], adu_buf[3]]))
181                } else {
182                    // incomplete frame
183                    return Ok(None);
184                }
185            }
186            0x81..=0xAB => 2,
187            0x2b if adu_buf
188                .get(2)
189                .is_some_and(|mei| *mei == MEI_TYPE_READ_DEVICE_IDENTIFICATION) =>
190            {
191                // 7-byte fixed header: function code, MEI type, device ID code,
192                // conformity level, more follows flag, next object ID, and object count.
193                if adu_buf.len() < 8 {
194                    return Ok(None);
195                }
196
197                let object_count = adu_buf[7] as usize;
198                let mut offset = 8;
199
200                for _ in 0..object_count {
201                    // Need at least 2 bytes: object ID and length
202                    if offset + 2 > adu_buf.len() {
203                        return Ok(None);
204                    }
205
206                    let object_len = adu_buf[offset + 1] as usize;
207                    offset += 2;
208
209                    // Check if full object value is present
210                    if offset + object_len > adu_buf.len() {
211                        return Ok(None);
212                    }
213
214                    offset += object_len;
215                }
216                offset - 1 // remove slave address byte
217            }
218            _ => {
219                if adu_buf.len() >= 3 {
220                    adu_buf.len() - 3
221                } else {
222                    return Err(std::io::Error::new(std::io::ErrorKind::InvalidData,
223                        format!("Incomplete ADU response {:#x?} for custom function code 0x{fn_code:0>2X}",
224                            adu_buf.to_vec())));
225                }
226            }
227        };
228        Ok(Some(len))
229    } else {
230        Ok(None)
231    }
232}
233
234fn calc_crc(data: &[u8]) -> u16 {
235    MODBUS_CRC.checksum(data)
236}
237
238fn check_crc(adu_data: &[u8], expected_crc: u16) -> io::Result<()> {
239    let actual_crc = calc_crc(adu_data);
240    if expected_crc != actual_crc {
241        return Err(io::Error::new(
242            io::ErrorKind::InvalidData,
243            format!("Invalid CRC: expected = 0x{expected_crc:0>4X}, actual = 0x{actual_crc:0>4X}"),
244        ));
245    }
246    Ok(())
247}
248
249fn write_crc(buf: &mut BytesMut, crc: u16) {
250    // The CRC is encoded with Little Endian byte order.
251    buf.put_u16_le(crc);
252}
253
254fn read_crc(read: &mut impl io::Read) -> io::Result<u16> {
255    // The CRC is encoded with Little Endian byte order.
256    read.read_u16::<LittleEndian>()
257}
258
259#[cfg(any(feature = "rtu-over-tcp-server", feature = "rtu-server"))]
260impl Decoder for RequestDecoder {
261    type Item = (SlaveId, Bytes);
262    type Error = io::Error;
263
264    fn decode(&mut self, buf: &mut BytesMut) -> io::Result<Option<(SlaveId, Bytes)>> {
265        decode("request", &mut self.frame_decoder, get_request_pdu_len, buf)
266    }
267}
268
269impl Decoder for ResponseDecoder {
270    type Item = (SlaveId, Bytes);
271    type Error = io::Error;
272
273    fn decode(&mut self, buf: &mut BytesMut) -> io::Result<Option<(SlaveId, Bytes)>> {
274        decode(
275            "response",
276            &mut self.frame_decoder,
277            get_response_pdu_len,
278            buf,
279        )
280    }
281}
282
283fn decode<F>(
284    pdu_type: &str,
285    frame_decoder: &mut FrameDecoder,
286    get_pdu_len: F,
287    buf: &mut BytesMut,
288) -> io::Result<Option<(SlaveId, Bytes)>>
289where
290    F: Fn(&BytesMut) -> io::Result<Option<usize>>,
291{
292    const MAX_RETRIES: usize = 20;
293
294    for _i in 0..MAX_RETRIES {
295        let result = get_pdu_len(buf).and_then(|pdu_len| {
296            let Some(pdu_len) = pdu_len else {
297                // Incomplete frame
298                return Ok(None);
299            };
300
301            frame_decoder.decode(buf, pdu_len)
302        });
303
304        if let Err(err) = result {
305            log::warn!("Failed to decode {pdu_type} frame: {err}");
306            frame_decoder.recover_on_error(buf);
307            continue;
308        }
309
310        return result;
311    }
312
313    // Maximum number of retries exceeded.
314    log::error!("Giving up to decode frame after {MAX_RETRIES} retries");
315    Err(io::Error::new(
316        io::ErrorKind::InvalidData,
317        "Too many retries",
318    ))
319}
320
321impl Decoder for ClientCodec {
322    type Item = ResponseAdu;
323    type Error = io::Error;
324
325    fn decode(&mut self, buf: &mut BytesMut) -> io::Result<Option<ResponseAdu>> {
326        let Some((slave_id, pdu_data)) = self.decoder.decode(buf)? else {
327            return Ok(None);
328        };
329
330        let hdr = Header { slave_id };
331
332        // Decoding of the PDU is unlikely to fail due
333        // to transmission errors, because the frame's bytes
334        // have already been verified with the CRC.
335        super::ResponsePdu::try_from(pdu_data)
336            .map(|pdu| Some(ResponseAdu { hdr, pdu }))
337            .map_err(|err| {
338                // Unrecoverable error
339                log::error!("Failed to decode response PDU: {err}");
340                err
341            })
342    }
343}
344
345#[cfg(any(feature = "rtu-over-tcp-server", feature = "rtu-server"))]
346impl Decoder for ServerCodec {
347    type Item = RequestAdu<'static>;
348    type Error = io::Error;
349
350    fn decode(&mut self, buf: &mut BytesMut) -> io::Result<Option<RequestAdu<'static>>> {
351        let Some((slave_id, pdu_data)) = self.decoder.decode(buf)? else {
352            return Ok(None);
353        };
354
355        let hdr = Header { slave_id };
356
357        // Decoding of the PDU is unlikely to fail due
358        // to transmission errors, because the frame's bytes
359        // have already been verified with the CRC.
360        super::RequestPdu::try_from(pdu_data)
361            .map(|pdu| Some(RequestAdu { hdr, pdu }))
362            .map_err(|err| {
363                // Unrecoverable error
364                log::error!("Failed to decode request PDU: {err}");
365                err
366            })
367    }
368}
369
370impl<'a> Encoder<RequestAdu<'a>> for ClientCodec {
371    type Error = io::Error;
372
373    fn encode(&mut self, adu: RequestAdu<'a>, buf: &mut BytesMut) -> io::Result<()> {
374        let RequestAdu {
375            hdr,
376            pdu: RequestPdu(request),
377        } = adu;
378        let buf_offset = buf.len();
379        let request_pdu_size = request_pdu_size(&request)?;
380        buf.reserve(request_pdu_size + 3);
381        buf.put_u8(hdr.slave_id);
382        encode_request_pdu(buf, &request);
383        let crc = calc_crc(&buf[buf_offset..]);
384        write_crc(buf, crc);
385        Ok(())
386    }
387}
388
389#[cfg(any(feature = "rtu-over-tcp-server", feature = "rtu-server"))]
390impl Encoder<ResponseAdu> for ServerCodec {
391    type Error = io::Error;
392
393    fn encode(&mut self, adu: ResponseAdu, buf: &mut BytesMut) -> io::Result<()> {
394        let ResponseAdu {
395            hdr,
396            pdu: super::ResponsePdu(pdu_res),
397        } = adu;
398        let buf_offset = buf.len();
399        let response_result_pdu_size = super::response_result_pdu_size(&pdu_res)?;
400        buf.reserve(response_result_pdu_size + 3);
401        buf.put_u8(hdr.slave_id);
402        super::encode_response_result_pdu(buf, &pdu_res);
403        let crc = calc_crc(&buf[buf_offset..]);
404        write_crc(buf, crc);
405        Ok(())
406    }
407}
408
409#[cfg(test)]
410mod tests {
411    use super::*;
412    use crate::bytes::Bytes;
413
414    #[test]
415    fn test_calc_crc() {
416        // See also: <https://crccalc.com/>
417
418        let msg = [0x01, 0x03, 0x08, 0x2B, 0x00, 0x02];
419        assert_eq!(calc_crc(&msg), 0x63B6);
420
421        let msg = [0x01, 0x03, 0x04, 0x00, 0x20, 0x00, 0x00];
422        assert_eq!(calc_crc(&msg), 0xF9FB);
423    }
424
425    #[test]
426    #[cfg(any(feature = "rtu-over-tcp-server", feature = "rtu-server"))]
427    fn test_get_request_pdu_len() {
428        let mut buf = BytesMut::new();
429
430        buf.extend_from_slice(&[0x66, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
431        assert!(get_request_pdu_len(&buf).is_err());
432
433        buf[1] = 0x01;
434        assert_eq!(get_request_pdu_len(&buf).unwrap(), Some(5));
435
436        buf[1] = 0x02;
437        assert_eq!(get_request_pdu_len(&buf).unwrap(), Some(5));
438
439        buf[1] = 0x03;
440        assert_eq!(get_request_pdu_len(&buf).unwrap(), Some(5));
441
442        buf[1] = 0x04;
443        assert_eq!(get_request_pdu_len(&buf).unwrap(), Some(5));
444
445        buf[1] = 0x05;
446        assert_eq!(get_request_pdu_len(&buf).unwrap(), Some(5));
447
448        buf[1] = 0x06;
449        assert_eq!(get_request_pdu_len(&buf).unwrap(), Some(5));
450
451        buf[1] = 0x07;
452        assert_eq!(get_request_pdu_len(&buf).unwrap(), Some(1));
453
454        // TODO: 0x08
455
456        buf[1] = 0x0B;
457        assert_eq!(get_request_pdu_len(&buf).unwrap(), Some(1));
458
459        buf[1] = 0x0C;
460        assert_eq!(get_request_pdu_len(&buf).unwrap(), Some(1));
461
462        buf[1] = 0x0F;
463        buf[6] = 99;
464        assert_eq!(get_request_pdu_len(&buf).unwrap(), Some(105));
465
466        buf[1] = 0x10;
467        buf[6] = 99;
468        assert_eq!(get_request_pdu_len(&buf).unwrap(), Some(105));
469
470        buf[1] = 0x11;
471        assert_eq!(get_request_pdu_len(&buf).unwrap(), Some(1));
472
473        // TODO: 0x14
474
475        // TODO: 0x15
476
477        buf[1] = 0x16;
478        assert_eq!(get_request_pdu_len(&buf).unwrap(), Some(7));
479
480        buf[1] = 0x17;
481        buf[10] = 99; // write byte count
482        assert_eq!(get_request_pdu_len(&buf).unwrap(), Some(109));
483
484        buf[1] = 0x18;
485        assert_eq!(get_request_pdu_len(&buf).unwrap(), Some(3));
486
487        buf[1] = 0x2B;
488        buf[2] = MEI_TYPE_READ_DEVICE_IDENTIFICATION;
489        buf[3] = 0x01;
490        buf[4] = 0x00;
491        assert_eq!(get_request_pdu_len(&buf).unwrap(), Some(4));
492    }
493
494    #[test]
495    fn test_get_response_pdu_len() {
496        let mut buf = BytesMut::new();
497        buf.extend_from_slice(&[0x66, 0x01, 99]);
498        assert_eq!(get_response_pdu_len(&buf).unwrap(), Some(101));
499
500        let mut buf = BytesMut::new();
501        buf.extend_from_slice(&[0x66, 0x00]);
502        assert!(get_response_pdu_len(&buf).is_err());
503
504        let mut buf = BytesMut::new();
505        buf.extend_from_slice(&[0x66, 0x00, 99, 0x00]);
506        assert_eq!(get_response_pdu_len(&buf).unwrap(), Some(1));
507
508        buf[1] = 0x01;
509        assert_eq!(get_response_pdu_len(&buf).unwrap(), Some(101));
510
511        buf[1] = 0x02;
512        assert_eq!(get_response_pdu_len(&buf).unwrap(), Some(101));
513
514        buf[1] = 0x03;
515        assert_eq!(get_response_pdu_len(&buf).unwrap(), Some(101));
516
517        buf[1] = 0x04;
518        assert_eq!(get_response_pdu_len(&buf).unwrap(), Some(101));
519
520        buf[1] = 0x05;
521        assert_eq!(get_response_pdu_len(&buf).unwrap(), Some(5));
522
523        buf[1] = 0x06;
524        assert_eq!(get_response_pdu_len(&buf).unwrap(), Some(5));
525
526        buf[1] = 0x07;
527        assert_eq!(get_response_pdu_len(&buf).unwrap(), Some(2));
528
529        // TODO: 0x08
530
531        buf[1] = 0x0B;
532        assert_eq!(get_response_pdu_len(&buf).unwrap(), Some(5));
533
534        buf[1] = 0x0C;
535        assert_eq!(get_response_pdu_len(&buf).unwrap(), Some(101));
536
537        buf[1] = 0x0F;
538        assert_eq!(get_response_pdu_len(&buf).unwrap(), Some(5));
539
540        buf[1] = 0x10;
541        assert_eq!(get_response_pdu_len(&buf).unwrap(), Some(5));
542
543        // TODO: 0x11
544
545        // TODO: 0x14
546
547        // TODO: 0x15
548
549        buf[1] = 0x16;
550        assert_eq!(get_response_pdu_len(&buf).unwrap(), Some(7));
551
552        buf[1] = 0x17;
553        assert_eq!(get_response_pdu_len(&buf).unwrap(), Some(101));
554
555        buf[1] = 0x18;
556        buf[2] = 0x01; // byte count Hi
557        buf[3] = 0x00; // byte count Lo
558        assert_eq!(get_response_pdu_len(&buf).unwrap(), Some(259));
559
560        // 0x2B - Read Device Identification response (MEI type 0x0E)
561        buf.clear();
562        buf.extend_from_slice(&[
563            0x42, // Slave address
564            0x2B, // Function code
565            MEI_TYPE_READ_DEVICE_IDENTIFICATION,
566            0x01, // Read Device ID code
567            0x01, // Conformity level
568            0x00, // More follows
569            0x00, // Next object ID
570            0x00, // Number of objects
571        ]);
572        assert_eq!(get_response_pdu_len(&buf).unwrap(), Some(7)); // 7 header
573
574        // With 1 object: ID = 0x01, len = 3, value = "ABC"
575        buf.clear();
576        buf.extend_from_slice(&[
577            0x42, // Slave address
578            0x2B, // Function code
579            MEI_TYPE_READ_DEVICE_IDENTIFICATION,
580            0x01, // Read Device ID code
581            0x01, // Conformity level
582            0x00, // More follows
583            0x00, // Next object ID
584            0x01, // Number of objects
585            0x01, // Object ID
586            0x03, // Object length
587            b'A',
588            b'B',
589            b'C',
590        ]);
591        assert_eq!(get_response_pdu_len(&buf).unwrap(), Some(12)); //  7 + 1 + 1 + 3
592
593        for i in 0x81..0xAB {
594            buf[1] = i;
595            assert_eq!(get_response_pdu_len(&buf).unwrap(), Some(2));
596        }
597    }
598
599    mod client {
600
601        use crate::{codec::ResponsePdu, Request, Response};
602
603        use super::*;
604
605        #[test]
606        fn decode_partly_received_client_message() {
607            let mut codec = ClientCodec::default();
608            let mut buf = BytesMut::from(
609                &[
610                    0x12, // slave address
611                    0x02, // function code
612                    0x03, // byte count
613                    0x00, // data
614                    0x00, // data
615                    0x00, // data
616                    0x00, // CRC first byte
617                          // missing crc second byte
618                ][..],
619            );
620            let res = codec.decode(&mut buf).unwrap();
621            assert!(res.is_none());
622            assert_eq!(buf.len(), 7);
623        }
624
625        #[test]
626        fn decode_empty_client_message() {
627            let mut codec = ClientCodec::default();
628            let mut buf = BytesMut::new();
629            assert_eq!(0, buf.len());
630
631            let res = codec.decode(&mut buf).unwrap();
632
633            assert!(res.is_none());
634            assert_eq!(0, buf.len());
635        }
636
637        #[test]
638        fn decode_single_byte_client_message() {
639            let mut codec = ClientCodec::default();
640            let mut buf = BytesMut::from(&[0x00][..]);
641            assert_eq!(1, buf.len());
642
643            let res = codec.decode(&mut buf).unwrap();
644
645            assert!(res.is_none());
646            assert_eq!(1, buf.len());
647        }
648
649        #[test]
650        #[cfg(any(feature = "rtu-over-tcp-server", feature = "rtu-server"))]
651        fn decode_empty_server_message() {
652            let mut codec = ServerCodec::default();
653            let mut buf = BytesMut::new();
654            assert_eq!(0, buf.len());
655
656            let res = codec.decode(&mut buf).unwrap();
657
658            assert!(res.is_none());
659            assert_eq!(0, buf.len());
660        }
661
662        #[test]
663        #[cfg(any(feature = "rtu-over-tcp-server", feature = "rtu-server"))]
664        fn decode_single_byte_server_message() {
665            let mut codec = ServerCodec::default();
666            let mut buf = BytesMut::from(&[0x00][..]);
667            assert_eq!(1, buf.len());
668
669            let res = codec.decode(&mut buf).unwrap();
670
671            assert!(res.is_none());
672            assert_eq!(1, buf.len());
673        }
674
675        #[test]
676        #[cfg(any(feature = "rtu-over-tcp-server", feature = "rtu-server"))]
677        fn decode_partly_received_server_message_0x16() {
678            let mut codec = ServerCodec::default();
679            let mut buf = BytesMut::from(
680                &[
681                    0x12, // slave address
682                    0x16, // function code
683                ][..],
684            );
685            assert_eq!(buf.len(), 2);
686
687            let res = codec.decode(&mut buf).unwrap();
688
689            assert!(res.is_none());
690            assert_eq!(buf.len(), 2);
691        }
692
693        #[test]
694        #[cfg(any(feature = "rtu-over-tcp-server", feature = "rtu-server"))]
695        fn decode_partly_received_server_message_0x0f() {
696            let mut codec = ServerCodec::default();
697            let mut buf = BytesMut::from(
698                &[
699                    0x12, // slave address
700                    0x0F, // function code
701                ][..],
702            );
703            assert_eq!(buf.len(), 2);
704
705            let res = codec.decode(&mut buf).unwrap();
706
707            assert!(res.is_none());
708            assert_eq!(buf.len(), 2);
709        }
710
711        #[test]
712        #[cfg(any(feature = "rtu-over-tcp-server", feature = "rtu-server"))]
713        fn decode_partly_received_server_message_0x10() {
714            let mut codec = ServerCodec::default();
715            let mut buf = BytesMut::from(
716                &[
717                    0x12, // slave address
718                    0x10, // function code
719                ][..],
720            );
721            assert_eq!(buf.len(), 2);
722
723            let res = codec.decode(&mut buf).unwrap();
724
725            assert!(res.is_none());
726            assert_eq!(buf.len(), 2);
727        }
728
729        #[test]
730        fn decode_rtu_message() {
731            let mut codec = ClientCodec::default();
732            let mut buf = BytesMut::from(
733                &[
734                    0x01, // slave address
735                    0x03, // function code
736                    0x04, // byte count
737                    0x89, //
738                    0x02, //
739                    0x42, //
740                    0xC7, //
741                    0x00, // crc
742                    0x9D, // crc
743                    0x00,
744                ][..],
745            );
746            let ResponseAdu { hdr, pdu } = codec.decode(&mut buf).unwrap().unwrap();
747            assert_eq!(buf.len(), 1);
748            assert_eq!(hdr.slave_id, 0x01);
749            if let Ok(Response::ReadHoldingRegisters(data)) = pdu.into() {
750                assert_eq!(data.len(), 2);
751                assert_eq!(data, vec![0x8902, 0x42C7]);
752            } else {
753                panic!("unexpected response")
754            }
755        }
756
757        #[test]
758        fn decode_rtu_response_drop_invalid_bytes() {
759            env_logger::init();
760            let mut codec = ClientCodec::default();
761            let mut buf = BytesMut::from(
762                &[
763                    0x42, // dropped byte
764                    0x43, // dropped byte
765                    0x01, // slave address
766                    0x03, // function code
767                    0x04, // byte count
768                    0x89, //
769                    0x02, //
770                    0x42, //
771                    0xC7, //
772                    0x00, // crc
773                    0x9D, // crc
774                    0x00,
775                ][..],
776            );
777            let ResponseAdu { hdr, pdu } = codec.decode(&mut buf).unwrap().unwrap();
778            assert_eq!(buf.len(), 1);
779            assert_eq!(hdr.slave_id, 0x01);
780            if let Ok(Response::ReadHoldingRegisters(data)) = pdu.into() {
781                assert_eq!(data.len(), 2);
782                assert_eq!(data, vec![0x8902, 0x42C7]);
783            } else {
784                panic!("unexpected response")
785            }
786        }
787
788        #[test]
789        fn decode_exception_message() {
790            let mut codec = ClientCodec::default();
791            let mut buf = BytesMut::from(
792                &[
793                    0x66, //
794                    0x82, // exception = 0x80 + 0x02
795                    0x03, //
796                    0xB1, // crc
797                    0x7E, // crc
798                ][..],
799            );
800
801            let ResponseAdu { pdu, .. } = codec.decode(&mut buf).unwrap().unwrap();
802            if let ResponsePdu(Err(err)) = pdu {
803                assert_eq!(format!("{err}"), "Modbus function 2: Illegal data value");
804                assert_eq!(buf.len(), 0);
805            } else {
806                panic!("unexpected response")
807            }
808        }
809
810        #[test]
811        fn encode_read_request() {
812            let mut codec = ClientCodec::default();
813            let mut buf = BytesMut::new();
814            let req = Request::ReadHoldingRegisters(0x082b, 2);
815            let pdu = req.into();
816            let slave_id = 0x01;
817            let hdr = Header { slave_id };
818            let adu = RequestAdu { hdr, pdu };
819            codec.encode(adu, &mut buf).unwrap();
820
821            assert_eq!(
822                buf,
823                Bytes::from_static(&[0x01, 0x03, 0x08, 0x2B, 0x00, 0x02, 0xB6, 0x63])
824            );
825        }
826
827        #[test]
828        fn encode_with_limited_buf_capacity() {
829            let mut codec = ClientCodec::default();
830            let req = Request::ReadHoldingRegisters(0x082b, 2);
831            let pdu = req.into();
832            let slave_id = 0x01;
833            let hdr = Header { slave_id };
834            let adu = RequestAdu { hdr, pdu };
835            let mut buf = BytesMut::with_capacity(40);
836            #[allow(unsafe_code)]
837            unsafe {
838                buf.set_len(33);
839            }
840            assert!(codec.encode(adu, &mut buf).is_ok());
841        }
842    }
843}