1use crate::encoding::{Reader, Writer};
2use crate::pdu::{ExceptionResponse, FunctionCode};
3use crate::{DecodeError, EncodeError};
4
5const MAX_READ_COIL_BYTES: usize = 250;
6const MAX_READ_REGISTERS: u16 = 125;
7const MAX_WRITE_COILS: u16 = 1968;
8const MAX_WRITE_REGISTERS: u16 = 123;
9
10fn validate_echo_quantity(quantity: u16, max: u16) -> Result<(), DecodeError> {
11 if quantity == 0 || quantity > max {
12 return Err(DecodeError::InvalidValue);
13 }
14 Ok(())
15}
16
17#[derive(Debug, Clone, Copy, PartialEq, Eq)]
19pub struct ReadCoilsResponse<'a> {
20 pub coil_status: &'a [u8],
22}
23
24impl<'a> ReadCoilsResponse<'a> {
25 fn decode_body(r: &mut Reader<'a>) -> Result<Self, DecodeError> {
26 let byte_count = usize::from(r.read_u8()?);
27 if byte_count == 0 || byte_count > MAX_READ_COIL_BYTES {
28 return Err(DecodeError::InvalidLength);
29 }
30 let data = r.read_exact(byte_count)?;
31 Ok(Self { coil_status: data })
32 }
33
34 pub fn encode(&self, w: &mut Writer<'_>) -> Result<(), EncodeError> {
35 let byte_count: u8 = self
36 .coil_status
37 .len()
38 .try_into()
39 .map_err(|_| EncodeError::ValueOutOfRange)?;
40 w.write_u8(FunctionCode::ReadCoils.as_u8())?;
41 w.write_u8(byte_count)?;
42 w.write_all(self.coil_status)?;
43 Ok(())
44 }
45
46 pub fn coil(&self, index: usize) -> Option<bool> {
47 let byte = self.coil_status.get(index / 8)?;
48 Some((byte & (1u8 << (index % 8))) != 0)
49 }
50}
51
52#[derive(Debug, Clone, Copy, PartialEq, Eq)]
54pub struct ReadDiscreteInputsResponse<'a> {
55 pub input_status: &'a [u8],
57}
58
59impl<'a> ReadDiscreteInputsResponse<'a> {
60 fn decode_body(r: &mut Reader<'a>) -> Result<Self, DecodeError> {
61 let byte_count = usize::from(r.read_u8()?);
62 if byte_count == 0 || byte_count > MAX_READ_COIL_BYTES {
63 return Err(DecodeError::InvalidLength);
64 }
65 let data = r.read_exact(byte_count)?;
66 Ok(Self { input_status: data })
67 }
68
69 pub fn encode(&self, w: &mut Writer<'_>) -> Result<(), EncodeError> {
70 let byte_count: u8 = self
71 .input_status
72 .len()
73 .try_into()
74 .map_err(|_| EncodeError::ValueOutOfRange)?;
75 w.write_u8(FunctionCode::ReadDiscreteInputs.as_u8())?;
76 w.write_u8(byte_count)?;
77 w.write_all(self.input_status)?;
78 Ok(())
79 }
80
81 pub fn coil(&self, index: usize) -> Option<bool> {
82 let byte = self.input_status.get(index / 8)?;
83 Some((byte & (1u8 << (index % 8))) != 0)
84 }
85}
86
87#[derive(Debug, Clone, Copy, PartialEq, Eq)]
89pub struct ReadHoldingRegistersResponse<'a> {
90 pub data: &'a [u8],
92}
93
94impl<'a> ReadHoldingRegistersResponse<'a> {
95 fn decode_body(r: &mut Reader<'a>) -> Result<Self, DecodeError> {
96 let byte_count = usize::from(r.read_u8()?);
97 if byte_count == 0 || (byte_count % 2) != 0 {
98 return Err(DecodeError::InvalidLength);
99 }
100 if byte_count > usize::from(MAX_READ_REGISTERS) * 2 {
101 return Err(DecodeError::InvalidLength);
102 }
103 let data = r.read_exact(byte_count)?;
104 Ok(Self { data })
105 }
106
107 pub fn encode(&self, w: &mut Writer<'_>) -> Result<(), EncodeError> {
108 if (self.data.len() % 2) != 0 {
109 return Err(EncodeError::InvalidLength);
110 }
111 let byte_count: u8 = self
112 .data
113 .len()
114 .try_into()
115 .map_err(|_| EncodeError::ValueOutOfRange)?;
116 w.write_u8(FunctionCode::ReadHoldingRegisters.as_u8())?;
117 w.write_u8(byte_count)?;
118 w.write_all(self.data)?;
119 Ok(())
120 }
121
122 pub fn register_count(&self) -> usize {
123 self.data.len() / 2
124 }
125
126 pub fn register(&self, index: usize) -> Option<u16> {
127 let offset = index.checked_mul(2)?;
128 let bytes = self.data.get(offset..offset + 2)?;
129 Some(u16::from_be_bytes([bytes[0], bytes[1]]))
130 }
131}
132
133#[derive(Debug, Clone, Copy, PartialEq, Eq)]
135pub struct ReadInputRegistersResponse<'a> {
136 pub data: &'a [u8],
138}
139
140impl<'a> ReadInputRegistersResponse<'a> {
141 fn decode_body(r: &mut Reader<'a>) -> Result<Self, DecodeError> {
142 let byte_count = usize::from(r.read_u8()?);
143 if byte_count == 0 || (byte_count % 2) != 0 {
144 return Err(DecodeError::InvalidLength);
145 }
146 if byte_count > usize::from(MAX_READ_REGISTERS) * 2 {
147 return Err(DecodeError::InvalidLength);
148 }
149 let data = r.read_exact(byte_count)?;
150 Ok(Self { data })
151 }
152
153 pub fn encode(&self, w: &mut Writer<'_>) -> Result<(), EncodeError> {
154 if (self.data.len() % 2) != 0 {
155 return Err(EncodeError::InvalidLength);
156 }
157 let byte_count: u8 = self
158 .data
159 .len()
160 .try_into()
161 .map_err(|_| EncodeError::ValueOutOfRange)?;
162 w.write_u8(FunctionCode::ReadInputRegisters.as_u8())?;
163 w.write_u8(byte_count)?;
164 w.write_all(self.data)?;
165 Ok(())
166 }
167
168 pub fn register_count(&self) -> usize {
169 self.data.len() / 2
170 }
171
172 pub fn register(&self, index: usize) -> Option<u16> {
173 let offset = index.checked_mul(2)?;
174 let bytes = self.data.get(offset..offset + 2)?;
175 Some(u16::from_be_bytes([bytes[0], bytes[1]]))
176 }
177}
178
179#[derive(Debug, Clone, Copy, PartialEq, Eq)]
181pub struct WriteSingleCoilResponse {
182 pub address: u16,
184 pub value: bool,
186}
187
188impl WriteSingleCoilResponse {
189 fn decode_body(r: &mut Reader<'_>) -> Result<Self, DecodeError> {
190 let address = r.read_be_u16()?;
191 let raw = r.read_be_u16()?;
192 let value = match raw {
193 0xFF00 => true,
194 0x0000 => false,
195 _ => return Err(DecodeError::InvalidValue),
196 };
197 Ok(Self { address, value })
198 }
199
200 pub fn encode(&self, w: &mut Writer<'_>) -> Result<(), EncodeError> {
201 w.write_u8(FunctionCode::WriteSingleCoil.as_u8())?;
202 w.write_be_u16(self.address)?;
203 w.write_be_u16(if self.value { 0xFF00 } else { 0x0000 })?;
204 Ok(())
205 }
206}
207
208#[derive(Debug, Clone, Copy, PartialEq, Eq)]
210pub struct WriteSingleRegisterResponse {
211 pub address: u16,
213 pub value: u16,
215}
216
217impl WriteSingleRegisterResponse {
218 fn decode_body(r: &mut Reader<'_>) -> Result<Self, DecodeError> {
219 Ok(Self {
220 address: r.read_be_u16()?,
221 value: r.read_be_u16()?,
222 })
223 }
224
225 pub fn encode(&self, w: &mut Writer<'_>) -> Result<(), EncodeError> {
226 w.write_u8(FunctionCode::WriteSingleRegister.as_u8())?;
227 w.write_be_u16(self.address)?;
228 w.write_be_u16(self.value)?;
229 Ok(())
230 }
231}
232
233#[derive(Debug, Clone, Copy, PartialEq, Eq)]
235pub struct WriteMultipleCoilsResponse {
236 pub start_address: u16,
238 pub quantity: u16,
240}
241
242impl WriteMultipleCoilsResponse {
243 fn decode_body(r: &mut Reader<'_>) -> Result<Self, DecodeError> {
244 let start_address = r.read_be_u16()?;
245 let quantity = r.read_be_u16()?;
246 validate_echo_quantity(quantity, MAX_WRITE_COILS)?;
247 Ok(Self {
248 start_address,
249 quantity,
250 })
251 }
252
253 pub fn encode(&self, w: &mut Writer<'_>) -> Result<(), EncodeError> {
254 if self.quantity == 0 || self.quantity > MAX_WRITE_COILS {
255 return Err(EncodeError::ValueOutOfRange);
256 }
257 w.write_u8(FunctionCode::WriteMultipleCoils.as_u8())?;
258 w.write_be_u16(self.start_address)?;
259 w.write_be_u16(self.quantity)?;
260 Ok(())
261 }
262}
263
264#[derive(Debug, Clone, Copy, PartialEq, Eq)]
266pub struct WriteMultipleRegistersResponse {
267 pub start_address: u16,
269 pub quantity: u16,
271}
272
273impl WriteMultipleRegistersResponse {
274 fn decode_body(r: &mut Reader<'_>) -> Result<Self, DecodeError> {
275 let start_address = r.read_be_u16()?;
276 let quantity = r.read_be_u16()?;
277 validate_echo_quantity(quantity, MAX_WRITE_REGISTERS)?;
278 Ok(Self {
279 start_address,
280 quantity,
281 })
282 }
283
284 pub fn encode(&self, w: &mut Writer<'_>) -> Result<(), EncodeError> {
285 if self.quantity == 0 || self.quantity > MAX_WRITE_REGISTERS {
286 return Err(EncodeError::ValueOutOfRange);
287 }
288 w.write_u8(FunctionCode::WriteMultipleRegisters.as_u8())?;
289 w.write_be_u16(self.start_address)?;
290 w.write_be_u16(self.quantity)?;
291 Ok(())
292 }
293}
294
295#[derive(Debug, Clone, Copy, PartialEq, Eq)]
299pub struct MaskWriteRegisterResponse {
300 pub address: u16,
302 pub and_mask: u16,
304 pub or_mask: u16,
306}
307
308impl MaskWriteRegisterResponse {
309 fn decode_body(r: &mut Reader<'_>) -> Result<Self, DecodeError> {
310 Ok(Self {
311 address: r.read_be_u16()?,
312 and_mask: r.read_be_u16()?,
313 or_mask: r.read_be_u16()?,
314 })
315 }
316
317 pub fn encode(&self, w: &mut Writer<'_>) -> Result<(), EncodeError> {
318 w.write_u8(FunctionCode::MaskWriteRegister.as_u8())?;
319 w.write_be_u16(self.address)?;
320 w.write_be_u16(self.and_mask)?;
321 w.write_be_u16(self.or_mask)?;
322 Ok(())
323 }
324}
325
326#[derive(Debug, Clone, Copy, PartialEq, Eq)]
328pub struct ReadWriteMultipleRegistersResponse<'a> {
329 pub data: &'a [u8],
331}
332
333impl<'a> ReadWriteMultipleRegistersResponse<'a> {
334 fn decode_body(r: &mut Reader<'a>) -> Result<Self, DecodeError> {
335 let byte_count = usize::from(r.read_u8()?);
336 if byte_count == 0 || (byte_count % 2) != 0 {
337 return Err(DecodeError::InvalidLength);
338 }
339 if byte_count > usize::from(MAX_READ_REGISTERS) * 2 {
340 return Err(DecodeError::InvalidLength);
341 }
342 let data = r.read_exact(byte_count)?;
343 Ok(Self { data })
344 }
345
346 pub fn encode(&self, w: &mut Writer<'_>) -> Result<(), EncodeError> {
347 if (self.data.len() % 2) != 0 {
348 return Err(EncodeError::InvalidLength);
349 }
350 let byte_count = u8::try_from(self.data.len()).map_err(|_| EncodeError::ValueOutOfRange)?;
351 w.write_u8(FunctionCode::ReadWriteMultipleRegisters.as_u8())?;
352 w.write_u8(byte_count)?;
353 w.write_all(self.data)?;
354 Ok(())
355 }
356
357 pub fn register_count(&self) -> usize {
358 self.data.len() / 2
359 }
360
361 pub fn register(&self, index: usize) -> Option<u16> {
362 let offset = index.checked_mul(2)?;
363 let bytes = self.data.get(offset..offset + 2)?;
364 Some(u16::from_be_bytes([bytes[0], bytes[1]]))
365 }
366}
367
368#[derive(Debug, Clone, Copy, PartialEq, Eq)]
370pub struct ReadExceptionStatusResponse {
371 pub data: u8,
373}
374
375impl ReadExceptionStatusResponse {
376 fn decode_body(r: &mut Reader<'_>) -> Result<Self, DecodeError> {
377 Ok(Self { data: r.read_u8()? })
378 }
379
380 pub fn encode(&self, w: &mut Writer<'_>) -> Result<(), EncodeError> {
381 w.write_u8(FunctionCode::ReadExceptionStatus.as_u8())?;
382 w.write_u8(self.data)?;
383 Ok(())
384 }
385}
386
387#[derive(Debug, Clone, Copy, PartialEq, Eq)]
389pub struct DiagnosticsResponse {
390 pub sub_function: u16,
392 pub data: u16,
394}
395
396impl DiagnosticsResponse {
397 fn decode_body(r: &mut Reader<'_>) -> Result<Self, DecodeError> {
398 Ok(Self {
399 sub_function: r.read_be_u16()?,
400 data: r.read_be_u16()?,
401 })
402 }
403
404 pub fn encode(&self, w: &mut Writer<'_>) -> Result<(), EncodeError> {
405 w.write_u8(FunctionCode::Diagnostics.as_u8())?;
406 w.write_be_u16(self.sub_function)?;
407 w.write_be_u16(self.data)?;
408 Ok(())
409 }
410}
411
412#[derive(Debug, Clone, Copy, PartialEq, Eq)]
414pub struct ReadFifoQueueResponse<'a> {
415 pub fifo_values: &'a [u8],
417}
418
419impl<'a> ReadFifoQueueResponse<'a> {
420 fn decode_body(r: &mut Reader<'a>) -> Result<Self, DecodeError> {
421 let byte_count = usize::from(r.read_be_u16()?);
422 let fifo_count = usize::from(r.read_be_u16()?);
423 let expected = fifo_count * 2;
424 if byte_count != expected + 2 {
425 return Err(DecodeError::InvalidLength);
426 }
427 let fifo_values = r.read_exact(expected)?;
428 Ok(Self { fifo_values })
429 }
430
431 pub fn encode(&self, w: &mut Writer<'_>) -> Result<(), EncodeError> {
432 if (self.fifo_values.len() % 2) != 0 {
433 return Err(EncodeError::InvalidLength);
434 }
435 let fifo_count = self.fifo_values.len() / 2;
436 let byte_count = self.fifo_values.len() + 2;
437 w.write_u8(FunctionCode::ReadFifoQueue.as_u8())?;
438 w.write_be_u16(
439 u16::try_from(byte_count).map_err(|_| EncodeError::ValueOutOfRange)?,
440 )?;
441 w.write_be_u16(u16::try_from(fifo_count).map_err(|_| EncodeError::ValueOutOfRange)?)?;
442 w.write_all(self.fifo_values)?;
443 Ok(())
444 }
445
446 pub fn fifo_count(&self) -> usize {
447 self.fifo_values.len() / 2
448 }
449
450 pub fn value(&self, index: usize) -> Option<u16> {
451 let offset = index.checked_mul(2)?;
452 let bytes = self.fifo_values.get(offset..offset + 2)?;
453 Some(u16::from_be_bytes([bytes[0], bytes[1]]))
454 }
455}
456
457#[derive(Debug, Clone, Copy, PartialEq, Eq)]
459pub struct CustomResponse<'a> {
460 pub function_code: u8,
462 pub data: &'a [u8],
464}
465
466impl<'a> CustomResponse<'a> {
467 pub fn encode(&self, w: &mut Writer<'_>) -> Result<(), EncodeError> {
468 if self.function_code == 0 || FunctionCode::is_exception(self.function_code) {
469 return Err(EncodeError::ValueOutOfRange);
470 }
471 w.write_u8(self.function_code)?;
472 w.write_all(self.data)?;
473 Ok(())
474 }
475}
476
477#[derive(Debug, Clone, Copy, PartialEq, Eq)]
482#[non_exhaustive]
483pub enum Response<'a> {
484 ReadCoils(ReadCoilsResponse<'a>),
485 ReadDiscreteInputs(ReadDiscreteInputsResponse<'a>),
486 ReadHoldingRegisters(ReadHoldingRegistersResponse<'a>),
487 ReadInputRegisters(ReadInputRegistersResponse<'a>),
488 WriteSingleCoil(WriteSingleCoilResponse),
489 WriteSingleRegister(WriteSingleRegisterResponse),
490 WriteMultipleCoils(WriteMultipleCoilsResponse),
491 WriteMultipleRegisters(WriteMultipleRegistersResponse),
492 MaskWriteRegister(MaskWriteRegisterResponse),
493 ReadWriteMultipleRegisters(ReadWriteMultipleRegistersResponse<'a>),
494 ReadExceptionStatus(ReadExceptionStatusResponse),
495 Diagnostics(DiagnosticsResponse),
496 ReadFifoQueue(ReadFifoQueueResponse<'a>),
497 Custom(CustomResponse<'a>),
498 Exception(ExceptionResponse),
499}
500
501impl<'a> Response<'a> {
502 pub fn decode(r: &mut Reader<'a>) -> Result<Self, DecodeError> {
503 let function_byte = r.read_u8()?;
504 if FunctionCode::is_exception(function_byte) {
505 return Ok(Self::Exception(ExceptionResponse::decode(function_byte, r)?));
506 }
507
508 let fc = FunctionCode::from_u8(function_byte)?;
509 match fc {
510 FunctionCode::ReadCoils => Ok(Self::ReadCoils(ReadCoilsResponse::decode_body(r)?)),
511 FunctionCode::ReadDiscreteInputs => {
512 Ok(Self::ReadDiscreteInputs(ReadDiscreteInputsResponse::decode_body(r)?))
513 }
514 FunctionCode::ReadHoldingRegisters => Ok(Self::ReadHoldingRegisters(
515 ReadHoldingRegistersResponse::decode_body(r)?,
516 )),
517 FunctionCode::ReadInputRegisters => Ok(Self::ReadInputRegisters(
518 ReadInputRegistersResponse::decode_body(r)?,
519 )),
520 FunctionCode::WriteSingleCoil => {
521 Ok(Self::WriteSingleCoil(WriteSingleCoilResponse::decode_body(r)?))
522 }
523 FunctionCode::WriteSingleRegister => Ok(Self::WriteSingleRegister(
524 WriteSingleRegisterResponse::decode_body(r)?,
525 )),
526 FunctionCode::WriteMultipleCoils => Ok(Self::WriteMultipleCoils(
527 WriteMultipleCoilsResponse::decode_body(r)?,
528 )),
529 FunctionCode::WriteMultipleRegisters => Ok(Self::WriteMultipleRegisters(
530 WriteMultipleRegistersResponse::decode_body(r)?,
531 )),
532 FunctionCode::MaskWriteRegister => Ok(Self::MaskWriteRegister(
533 MaskWriteRegisterResponse::decode_body(r)?,
534 )),
535 FunctionCode::ReadWriteMultipleRegisters => Ok(Self::ReadWriteMultipleRegisters(
536 ReadWriteMultipleRegistersResponse::decode_body(r)?,
537 )),
538 FunctionCode::ReadExceptionStatus => Ok(Self::ReadExceptionStatus(
539 ReadExceptionStatusResponse::decode_body(r)?,
540 )),
541 FunctionCode::Diagnostics => {
542 Ok(Self::Diagnostics(DiagnosticsResponse::decode_body(r)?))
543 }
544 FunctionCode::ReadFifoQueue => {
545 Ok(Self::ReadFifoQueue(ReadFifoQueueResponse::decode_body(r)?))
546 }
547 FunctionCode::Custom(function_code) => {
548 let data = r.read_exact(r.remaining())?;
549 Ok(Self::Custom(CustomResponse {
550 function_code,
551 data,
552 }))
553 }
554 }
555 }
556
557 pub fn encode(&self, w: &mut Writer<'_>) -> Result<(), EncodeError> {
558 match self {
559 Self::ReadCoils(resp) => resp.encode(w),
560 Self::ReadDiscreteInputs(resp) => resp.encode(w),
561 Self::ReadHoldingRegisters(resp) => resp.encode(w),
562 Self::ReadInputRegisters(resp) => resp.encode(w),
563 Self::WriteSingleCoil(resp) => resp.encode(w),
564 Self::WriteSingleRegister(resp) => resp.encode(w),
565 Self::WriteMultipleCoils(resp) => resp.encode(w),
566 Self::WriteMultipleRegisters(resp) => resp.encode(w),
567 Self::MaskWriteRegister(resp) => resp.encode(w),
568 Self::ReadWriteMultipleRegisters(resp) => resp.encode(w),
569 Self::ReadExceptionStatus(resp) => resp.encode(w),
570 Self::Diagnostics(resp) => resp.encode(w),
571 Self::ReadFifoQueue(resp) => resp.encode(w),
572 Self::Custom(resp) => resp.encode(w),
573 Self::Exception(resp) => resp.encode(w),
574 }
575 }
576}
577
578#[cfg(test)]
579mod tests {
580 use super::{
581 CustomResponse, MaskWriteRegisterResponse, ReadHoldingRegistersResponse,
582 ReadWriteMultipleRegistersResponse, Response, WriteSingleCoilResponse,
583 };
584 use crate::encoding::{Reader, Writer};
585 use crate::pdu::ExceptionCode;
586 use crate::DecodeError;
587
588 #[test]
589 fn register_helpers_work() {
590 let resp = ReadHoldingRegistersResponse {
591 data: &[0x12, 0x34, 0xAB, 0xCD],
592 };
593 assert_eq!(resp.register_count(), 2);
594 assert_eq!(resp.register(0), Some(0x1234));
595 assert_eq!(resp.register(1), Some(0xABCD));
596 assert_eq!(resp.register(2), None);
597 }
598
599 #[test]
600 fn response_decode_exception_unknown_code() {
601 let mut r = Reader::new(&[0x83, 0x19]);
602 match Response::decode(&mut r).unwrap() {
603 Response::Exception(ex) => {
604 assert_eq!(ex.function_code, 0x03);
605 assert_eq!(ex.exception_code, ExceptionCode::Unknown(0x19));
606 }
607 _ => panic!("expected exception"),
608 }
609 }
610
611 #[test]
612 fn write_single_coil_rejects_invalid_payload() {
613 let mut r = Reader::new(&[0x05, 0x00, 0x01, 0x12, 0x34]);
614 assert_eq!(Response::decode(&mut r).unwrap_err(), DecodeError::InvalidValue);
615 }
616
617 #[test]
618 fn enum_encode_roundtrip() {
619 let original = Response::WriteSingleCoil(WriteSingleCoilResponse {
620 address: 0x0007,
621 value: true,
622 });
623 let mut buf = [0u8; 8];
624 let mut w = Writer::new(&mut buf);
625 original.encode(&mut w).unwrap();
626
627 let mut r = Reader::new(w.as_written());
628 let decoded = Response::decode(&mut r).unwrap();
629 assert_eq!(decoded, original);
630 }
631
632 #[test]
633 fn custom_response_roundtrip() {
634 let original = Response::Custom(CustomResponse {
635 function_code: 0x41,
636 data: &[0xAA, 0x55],
637 });
638 let mut buf = [0u8; 8];
639 let mut w = Writer::new(&mut buf);
640 original.encode(&mut w).unwrap();
641 assert_eq!(w.as_written(), &[0x41, 0xAA, 0x55]);
642
643 let mut r = Reader::new(w.as_written());
644 let decoded = Response::decode(&mut r).unwrap();
645 assert_eq!(decoded, original);
646 }
647
648 #[test]
649 fn mask_write_response_roundtrip() {
650 let original = Response::MaskWriteRegister(MaskWriteRegisterResponse {
651 address: 0x0007,
652 and_mask: 0xFF00,
653 or_mask: 0x00A5,
654 });
655 let mut buf = [0u8; 16];
656 let mut w = Writer::new(&mut buf);
657 original.encode(&mut w).unwrap();
658 assert_eq!(w.as_written(), &[0x16, 0x00, 0x07, 0xFF, 0x00, 0x00, 0xA5]);
659
660 let mut r = Reader::new(w.as_written());
661 let decoded = Response::decode(&mut r).unwrap();
662 assert_eq!(decoded, original);
663 }
664
665 #[test]
666 fn read_write_multiple_registers_response_roundtrip() {
667 let original = Response::ReadWriteMultipleRegisters(ReadWriteMultipleRegistersResponse {
668 data: &[0x12, 0x34, 0xAB, 0xCD],
669 });
670 let mut buf = [0u8; 16];
671 let mut w = Writer::new(&mut buf);
672 original.encode(&mut w).unwrap();
673 assert_eq!(w.as_written(), &[0x17, 0x04, 0x12, 0x34, 0xAB, 0xCD]);
674
675 let mut r = Reader::new(w.as_written());
676 match Response::decode(&mut r).unwrap() {
677 Response::ReadWriteMultipleRegisters(resp) => {
678 assert_eq!(resp.register_count(), 2);
679 assert_eq!(resp.register(0), Some(0x1234));
680 assert_eq!(resp.register(1), Some(0xABCD));
681 }
682 other => panic!("unexpected response: {other:?}"),
683 }
684 }
685}