modbus_core/codec/rtu/
server.rs

1// SPDX-FileCopyrightText: Copyright (c) 2018-2025 slowtec GmbH <post@slowtec.de>
2// SPDX-License-Identifier: MIT OR Apache-2.0
3
4//! Modbus RTU server (slave) specific functions.
5use super::*;
6
7/// Decode an RTU request.
8pub fn decode_request(buf: &[u8]) -> Result<Option<RequestAdu<'_>>> {
9    if buf.is_empty() {
10        return Ok(None);
11    }
12    decode(DecoderType::Request, buf)
13        .and_then(|frame| {
14            let Some((DecodedFrame { slave, pdu }, _frame_pos)) = frame else {
15                return Ok(None);
16            };
17            let hdr = Header { slave };
18            // Decoding of the PDU should are unlikely to fail due
19            // to transmission errors, because the frame's bytes
20            // have already been verified with the CRC.
21            let request = Request::try_from(pdu)
22                .map(RequestPdu)
23                .map(|pdu| Some(RequestAdu { hdr, pdu }));
24
25            #[cfg(feature = "log")]
26            if let Err(error) = request {
27                // Unrecoverable error
28                log::error!("Failed to decode request PDU: {error}");
29            }
30            request
31        })
32        .map_err(|_| {
33            // Decoding the transport frame is non-destructive and must
34            // never fail!
35            unreachable!();
36        })
37}
38
39/// Encode an RTU response.
40pub fn encode_response(adu: ResponseAdu<'_>, buf: &mut [u8]) -> Result<usize> {
41    let ResponseAdu { hdr, pdu } = adu;
42    if buf.len() < 2 {
43        return Err(Error::BufferSize);
44    }
45    let len = pdu.encode(&mut buf[1..])?;
46    if buf.len() < len + 3 {
47        return Err(Error::BufferSize);
48    }
49    buf[0] = hdr.slave;
50    let crc = crc16(&buf[0..=len]);
51    BigEndian::write_u16(&mut buf[len + 1..], crc);
52    Ok(len + 3)
53}
54
55#[cfg(test)]
56mod tests {
57    use super::*;
58
59    #[test]
60    fn decode_empty_request() {
61        let req = decode_request(&[]).unwrap();
62        assert!(req.is_none());
63    }
64
65    #[test]
66    fn decode_partly_received_request() {
67        let buf = &[
68            0x12, // slave address
69            0x16, // function code
70        ];
71        let req = decode_request(buf).unwrap();
72        assert!(req.is_none());
73    }
74
75    #[test]
76    fn decode_write_single_register_request() {
77        let buf = &[
78            0x12, // slave address
79            0x06, // function code
80            0x22, // addr
81            0x22, // addr
82            0xAB, // value
83            0xCD, // value
84            0x9F, // crc
85            0xBE, // crc
86        ];
87        let adu = decode_request(buf).unwrap().unwrap();
88        let RequestAdu { hdr, pdu } = adu;
89        let RequestPdu(pdu) = pdu;
90        assert_eq!(hdr.slave, 0x12);
91        assert_eq!(FunctionCode::from(pdu), FunctionCode::WriteSingleRegister);
92    }
93
94    #[test]
95    fn encode_write_single_register_response() {
96        let adu = ResponseAdu {
97            hdr: Header { slave: 0x12 },
98            pdu: ResponsePdu(Ok(Response::WriteSingleRegister(0x2222, 0xABCD))),
99        };
100        let buf = &mut [0; 100];
101        let len = encode_response(adu, buf).unwrap();
102        assert_eq!(len, 8);
103        assert_eq!(buf[0], 0x12);
104        assert_eq!(buf[1], 0x06);
105        assert_eq!(buf[2], 0x22);
106        assert_eq!(buf[3], 0x22);
107        assert_eq!(buf[4], 0xAB);
108        assert_eq!(buf[5], 0xCD);
109        assert_eq!(buf[6], 0x9F);
110        assert_eq!(buf[7], 0xBE);
111    }
112}