1use 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
20const 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 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 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 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 debug_assert!(!buf.is_empty());
89 {
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 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 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 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 if offset + object_len > adu_buf.len() {
211 return Ok(None);
212 }
213
214 offset += object_len;
215 }
216 offset - 1 }
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 buf.put_u16_le(crc);
252}
253
254fn read_crc(read: &mut impl io::Read) -> io::Result<u16> {
255 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 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 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 super::ResponsePdu::try_from(pdu_data)
336 .map(|pdu| Some(ResponseAdu { hdr, pdu }))
337 .map_err(|err| {
338 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 super::RequestPdu::try_from(pdu_data)
361 .map(|pdu| Some(RequestAdu { hdr, pdu }))
362 .map_err(|err| {
363 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 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 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 buf[1] = 0x16;
478 assert_eq!(get_request_pdu_len(&buf).unwrap(), Some(7));
479
480 buf[1] = 0x17;
481 buf[10] = 99; 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 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 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; buf[3] = 0x00; assert_eq!(get_response_pdu_len(&buf).unwrap(), Some(259));
559
560 buf.clear();
562 buf.extend_from_slice(&[
563 0x42, 0x2B, MEI_TYPE_READ_DEVICE_IDENTIFICATION,
566 0x01, 0x01, 0x00, 0x00, 0x00, ]);
572 assert_eq!(get_response_pdu_len(&buf).unwrap(), Some(7)); buf.clear();
576 buf.extend_from_slice(&[
577 0x42, 0x2B, MEI_TYPE_READ_DEVICE_IDENTIFICATION,
580 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x03, b'A',
588 b'B',
589 b'C',
590 ]);
591 assert_eq!(get_response_pdu_len(&buf).unwrap(), Some(12)); 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, 0x02, 0x03, 0x00, 0x00, 0x00, 0x00, ][..],
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, 0x16, ][..],
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, 0x0F, ][..],
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, 0x10, ][..],
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, 0x03, 0x04, 0x89, 0x02, 0x42, 0xC7, 0x00, 0x9D, 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, 0x43, 0x01, 0x03, 0x04, 0x89, 0x02, 0x42, 0xC7, 0x00, 0x9D, 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, 0x82, 0x03, 0xB1, 0x7E, ][..],
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}