modbus_core/codec/rtu/
client.rs

1//! Modbus RTU client (master) specific functions.
2use super::*;
3
4/// Encode and RTU request.
5pub fn encode_request(adu: RequestAdu, buf: &mut [u8]) -> Result<usize> {
6    let RequestAdu { hdr, pdu } = adu;
7    if buf.len() < 2 {
8        return Err(Error::BufferSize);
9    }
10    let len = pdu.encode(&mut buf[1..])?;
11    if buf.len() < len + 3 {
12        return Err(Error::BufferSize);
13    }
14    buf[0] = hdr.slave;
15    let crc = crc16(&buf[0..=len]);
16    BigEndian::write_u16(&mut buf[len + 1..], crc);
17    Ok(len + 3)
18}
19
20/// Decode an RTU response.
21pub fn decode_response(buf: &[u8]) -> Result<Option<ResponseAdu>> {
22    if buf.is_empty() {
23        return Ok(None);
24    }
25    decode(DecoderType::Response, buf)
26        .and_then(|frame| {
27            let Some((DecodedFrame { slave, pdu }, _frame_pos)) = frame else {
28                return Ok(None);
29            };
30            let hdr = Header { slave };
31            // Decoding of the PDU should are unlikely to fail due
32            // to transmission errors, because the frame's bytes
33            // have already been verified with the CRC.
34
35            ExceptionResponse::try_from(pdu)
36                .map(|er| ResponsePdu(Err(er)))
37                .or_else(|_| Response::try_from(pdu).map(|r| ResponsePdu(Ok(r))))
38                .map(|pdu| Some(ResponseAdu { hdr, pdu }))
39                .map_err(|err| {
40                    // Unrecoverable error
41                    log::error!("Failed to decode Response PDU: {err}");
42                    err
43                })
44        })
45        .map_err(|_| {
46            // Decoding the transport frame is non-destructive and must
47            // never fail!
48            unreachable!();
49        })
50}
51
52#[cfg(test)]
53mod tests {
54    use super::*;
55
56    #[test]
57    fn decode_empty_response() {
58        let req = decode_response(&[]).unwrap();
59        assert!(req.is_none());
60    }
61
62    #[test]
63    fn decode_partly_received_response() {
64        let buf = &[
65            0x12, // slave address
66            0x16, // function code
67        ];
68        let req = decode_response(buf).unwrap();
69        assert!(req.is_none());
70    }
71
72    #[test]
73    fn encode_write_single_register_request() {
74        let mut buf = [0u8; 255];
75        let sz = encode_request(
76            RequestAdu {
77                hdr: Header { slave: 0x12 },
78                pdu: RequestPdu(Request::WriteSingleRegister(0x2222, 0xABCD)),
79            },
80            &mut buf,
81        )
82        .expect("Error encoding request");
83
84        let req = &buf[..sz];
85        assert_eq!(
86            req,
87            &[
88                0x12, // slave address
89                0x06, // function code
90                0x22, // addr
91                0x22, // addr
92                0xAB, // value
93                0xCD, // value
94                0x9F, // crc
95                0xBE, // crc
96            ]
97        );
98    }
99
100    #[test]
101    fn decode_write_single_register_response() {
102        use crate::frame::Response;
103        let rsp = &[0x12, 0x06, 0x22, 0x22, 0xAB, 0xCD, 0x9F, 0xBE];
104
105        assert!(matches!(
106            decode_response(rsp),
107            Ok(Some(ResponseAdu {
108                hdr: Header { slave: 0x12 },
109                pdu: ResponsePdu(Ok(Response::WriteSingleRegister(0x2222, 0xABCD)))
110            }))
111        ));
112    }
113
114    #[test]
115    fn decode_malformed_write_single_register_response() {
116        let rsp = &[0x12, 0x06, 0x22, 0x22, 0xAB, 0x65, 0x9E];
117
118        assert!(matches!(decode_response(rsp), Ok(None)));
119    }
120
121    #[test]
122    fn decode_bad_crc_write_single_register_response() {
123        let rsp = &[0x12, 0x06, 0x22, 0x22, 0xAB, 0xCD, 0x5F, 0xBE];
124
125        assert!(matches!(decode_response(rsp), Ok(None)));
126    }
127}