1use crate::encoding::{Reader, Writer};
2use crate::pdu::FunctionCode;
3use crate::{DecodeError, EncodeError};
4
5const MAX_READ_BITS: u16 = 2000;
6const MAX_READ_REGISTERS: u16 = 125;
7const MAX_WRITE_COILS: u16 = 1968;
8const MAX_WRITE_REGISTERS: u16 = 123;
9const MAX_RW_WRITE_REGISTERS: u16 = 121;
10
11fn validate_quantity(quantity: u16, max: u16) -> Result<(), EncodeError> {
12 if quantity == 0 || quantity > max {
13 return Err(EncodeError::ValueOutOfRange);
14 }
15 Ok(())
16}
17
18fn validate_quantity_decode(quantity: u16, max: u16) -> Result<(), DecodeError> {
19 if quantity == 0 || quantity > max {
20 return Err(DecodeError::InvalidValue);
21 }
22 Ok(())
23}
24
25fn write_header(
26 w: &mut Writer<'_>,
27 function: FunctionCode,
28 start_address: u16,
29 quantity: u16,
30) -> Result<(), EncodeError> {
31 w.write_u8(function.as_u8())?;
32 w.write_be_u16(start_address)?;
33 w.write_be_u16(quantity)?;
34 Ok(())
35}
36
37fn pack_coils(values: &[bool], out: &mut [u8]) {
38 out.fill(0);
39 for (i, value) in values.iter().enumerate() {
40 if *value {
41 out[i / 8] |= 1u8 << (i % 8);
42 }
43 }
44}
45
46#[derive(Debug, Clone, Copy, PartialEq, Eq)]
48pub struct ReadCoilsRequest {
49 pub start_address: u16,
50 pub quantity: u16,
51}
52
53impl ReadCoilsRequest {
54 pub fn encode(&self, w: &mut Writer<'_>) -> Result<(), EncodeError> {
55 validate_quantity(self.quantity, MAX_READ_BITS)?;
56 write_header(
57 w,
58 FunctionCode::ReadCoils,
59 self.start_address,
60 self.quantity,
61 )
62 }
63}
64
65#[derive(Debug, Clone, Copy, PartialEq, Eq)]
67pub struct ReadDiscreteInputsRequest {
68 pub start_address: u16,
69 pub quantity: u16,
70}
71
72impl ReadDiscreteInputsRequest {
73 pub fn encode(&self, w: &mut Writer<'_>) -> Result<(), EncodeError> {
74 validate_quantity(self.quantity, MAX_READ_BITS)?;
75 write_header(
76 w,
77 FunctionCode::ReadDiscreteInputs,
78 self.start_address,
79 self.quantity,
80 )
81 }
82}
83
84#[derive(Debug, Clone, Copy, PartialEq, Eq)]
86pub struct ReadHoldingRegistersRequest {
87 pub start_address: u16,
88 pub quantity: u16,
89}
90
91impl ReadHoldingRegistersRequest {
92 pub fn encode(&self, w: &mut Writer<'_>) -> Result<(), EncodeError> {
93 validate_quantity(self.quantity, MAX_READ_REGISTERS)?;
94 write_header(
95 w,
96 FunctionCode::ReadHoldingRegisters,
97 self.start_address,
98 self.quantity,
99 )
100 }
101}
102
103#[derive(Debug, Clone, Copy, PartialEq, Eq)]
105pub struct ReadInputRegistersRequest {
106 pub start_address: u16,
107 pub quantity: u16,
108}
109
110impl ReadInputRegistersRequest {
111 pub fn encode(&self, w: &mut Writer<'_>) -> Result<(), EncodeError> {
112 validate_quantity(self.quantity, MAX_READ_REGISTERS)?;
113 write_header(
114 w,
115 FunctionCode::ReadInputRegisters,
116 self.start_address,
117 self.quantity,
118 )
119 }
120}
121
122#[derive(Debug, Clone, Copy, PartialEq, Eq)]
124pub struct WriteSingleCoilRequest {
125 pub address: u16,
126 pub value: bool,
127}
128
129impl WriteSingleCoilRequest {
130 pub fn encode(&self, w: &mut Writer<'_>) -> Result<(), EncodeError> {
131 w.write_u8(FunctionCode::WriteSingleCoil.as_u8())?;
132 w.write_be_u16(self.address)?;
133 w.write_be_u16(if self.value { 0xFF00 } else { 0x0000 })?;
134 Ok(())
135 }
136}
137
138#[derive(Debug, Clone, Copy, PartialEq, Eq)]
140pub struct WriteSingleRegisterRequest {
141 pub address: u16,
142 pub value: u16,
143}
144
145impl WriteSingleRegisterRequest {
146 pub fn encode(&self, w: &mut Writer<'_>) -> Result<(), EncodeError> {
147 w.write_u8(FunctionCode::WriteSingleRegister.as_u8())?;
148 w.write_be_u16(self.address)?;
149 w.write_be_u16(self.value)?;
150 Ok(())
151 }
152}
153
154#[derive(Debug, Clone, Copy, PartialEq, Eq)]
156pub struct WriteMultipleCoilsRequest<'a> {
157 pub start_address: u16,
158 pub values: &'a [bool],
159}
160
161impl<'a> WriteMultipleCoilsRequest<'a> {
162 pub fn quantity(&self) -> Result<u16, EncodeError> {
163 let quantity: u16 = self
164 .values
165 .len()
166 .try_into()
167 .map_err(|_| EncodeError::ValueOutOfRange)?;
168 validate_quantity(quantity, MAX_WRITE_COILS)?;
169 Ok(quantity)
170 }
171
172 pub fn encode(&self, w: &mut Writer<'_>) -> Result<(), EncodeError> {
173 let quantity = self.quantity()?;
174 let byte_count_usize = self.values.len().div_ceil(8);
175 let byte_count: u8 = byte_count_usize
176 .try_into()
177 .map_err(|_| EncodeError::ValueOutOfRange)?;
178
179 w.write_u8(FunctionCode::WriteMultipleCoils.as_u8())?;
180 w.write_be_u16(self.start_address)?;
181 w.write_be_u16(quantity)?;
182 w.write_u8(byte_count)?;
183
184 let mut packed = [0u8; 246];
185 let used = usize::from(byte_count);
186 pack_coils(self.values, &mut packed[..used]);
187 w.write_all(&packed[..used])?;
188 Ok(())
189 }
190}
191
192#[derive(Debug, Clone, Copy, PartialEq, Eq)]
194pub struct WriteMultipleRegistersRequest<'a> {
195 pub start_address: u16,
196 pub values: &'a [u16],
197}
198
199impl<'a> WriteMultipleRegistersRequest<'a> {
200 pub fn quantity(&self) -> Result<u16, EncodeError> {
201 let quantity: u16 = self
202 .values
203 .len()
204 .try_into()
205 .map_err(|_| EncodeError::ValueOutOfRange)?;
206 validate_quantity(quantity, MAX_WRITE_REGISTERS)?;
207 Ok(quantity)
208 }
209
210 pub fn encode(&self, w: &mut Writer<'_>) -> Result<(), EncodeError> {
211 let quantity = self.quantity()?;
212 let byte_count_usize = self.values.len() * 2;
213 let byte_count: u8 = byte_count_usize
214 .try_into()
215 .map_err(|_| EncodeError::ValueOutOfRange)?;
216
217 w.write_u8(FunctionCode::WriteMultipleRegisters.as_u8())?;
218 w.write_be_u16(self.start_address)?;
219 w.write_be_u16(quantity)?;
220 w.write_u8(byte_count)?;
221 for value in self.values {
222 w.write_be_u16(*value)?;
223 }
224 Ok(())
225 }
226}
227
228#[derive(Debug, Clone, Copy, PartialEq, Eq)]
230pub struct MaskWriteRegisterRequest {
231 pub address: u16,
232 pub and_mask: u16,
233 pub or_mask: u16,
234}
235
236impl MaskWriteRegisterRequest {
237 pub fn encode(&self, w: &mut Writer<'_>) -> Result<(), EncodeError> {
238 w.write_u8(FunctionCode::MaskWriteRegister.as_u8())?;
239 w.write_be_u16(self.address)?;
240 w.write_be_u16(self.and_mask)?;
241 w.write_be_u16(self.or_mask)?;
242 Ok(())
243 }
244}
245
246#[derive(Debug, Clone, Copy, PartialEq, Eq)]
248pub struct ReadWriteMultipleRegistersRequest<'a> {
249 pub read_start_address: u16,
250 pub read_quantity: u16,
251 pub write_start_address: u16,
252 pub values: &'a [u16],
253}
254
255impl<'a> ReadWriteMultipleRegistersRequest<'a> {
256 pub fn write_quantity(&self) -> Result<u16, EncodeError> {
257 let quantity: u16 = self
258 .values
259 .len()
260 .try_into()
261 .map_err(|_| EncodeError::ValueOutOfRange)?;
262 validate_quantity(quantity, MAX_RW_WRITE_REGISTERS)?;
263 Ok(quantity)
264 }
265
266 pub fn encode(&self, w: &mut Writer<'_>) -> Result<(), EncodeError> {
267 validate_quantity(self.read_quantity, MAX_READ_REGISTERS)?;
268 let write_quantity = self.write_quantity()?;
269 let byte_count = u8::try_from(usize::from(write_quantity) * 2)
270 .map_err(|_| EncodeError::ValueOutOfRange)?;
271
272 w.write_u8(FunctionCode::ReadWriteMultipleRegisters.as_u8())?;
273 w.write_be_u16(self.read_start_address)?;
274 w.write_be_u16(self.read_quantity)?;
275 w.write_be_u16(self.write_start_address)?;
276 w.write_be_u16(write_quantity)?;
277 w.write_u8(byte_count)?;
278 for value in self.values {
279 w.write_be_u16(*value)?;
280 }
281 Ok(())
282 }
283}
284
285#[derive(Debug, Clone, Copy, PartialEq, Eq)]
287pub struct ReadExceptionStatusRequest;
288
289impl ReadExceptionStatusRequest {
290 pub fn encode(&self, w: &mut Writer<'_>) -> Result<(), EncodeError> {
291 w.write_u8(FunctionCode::ReadExceptionStatus.as_u8())?;
292 Ok(())
293 }
294}
295
296#[derive(Debug, Clone, Copy, PartialEq, Eq)]
298pub struct DiagnosticsRequest {
299 pub sub_function: u16,
300 pub data: u16,
301}
302
303impl DiagnosticsRequest {
304 pub fn encode(&self, w: &mut Writer<'_>) -> Result<(), EncodeError> {
305 w.write_u8(FunctionCode::Diagnostics.as_u8())?;
306 w.write_be_u16(self.sub_function)?;
307 w.write_be_u16(self.data)?;
308 Ok(())
309 }
310}
311
312#[derive(Debug, Clone, Copy, PartialEq, Eq)]
314pub struct ReadFifoQueueRequest {
315 pub fifo_pointer_address: u16,
316}
317
318impl ReadFifoQueueRequest {
319 pub fn encode(&self, w: &mut Writer<'_>) -> Result<(), EncodeError> {
320 w.write_u8(FunctionCode::ReadFifoQueue.as_u8())?;
321 w.write_be_u16(self.fifo_pointer_address)?;
322 Ok(())
323 }
324}
325
326#[derive(Debug, Clone, Copy, PartialEq, Eq)]
328pub struct CustomRequest<'a> {
329 pub function_code: u8,
330 pub data: &'a [u8],
331}
332
333impl<'a> CustomRequest<'a> {
334 pub fn encode(&self, w: &mut Writer<'_>) -> Result<(), EncodeError> {
335 if self.function_code == 0 || FunctionCode::is_exception(self.function_code) {
336 return Err(EncodeError::ValueOutOfRange);
337 }
338 w.write_u8(self.function_code)?;
339 w.write_all(self.data)?;
340 Ok(())
341 }
342}
343
344#[derive(Debug, Clone, Copy, PartialEq, Eq)]
346pub struct WriteMultipleCoilsRequestData<'a> {
347 pub start_address: u16,
348 pub quantity: u16,
349 pub values_packed: &'a [u8],
350}
351
352impl<'a> WriteMultipleCoilsRequestData<'a> {
353 pub fn coil(&self, index: usize) -> Option<bool> {
354 if index >= usize::from(self.quantity) {
355 return None;
356 }
357 let byte = self.values_packed.get(index / 8)?;
358 Some((byte & (1u8 << (index % 8))) != 0)
359 }
360}
361
362#[derive(Debug, Clone, Copy, PartialEq, Eq)]
364pub struct WriteMultipleRegistersRequestData<'a> {
365 pub start_address: u16,
366 pub values_bytes: &'a [u8],
367}
368
369impl<'a> WriteMultipleRegistersRequestData<'a> {
370 pub fn quantity(&self) -> usize {
371 self.values_bytes.len() / 2
372 }
373
374 pub fn register(&self, index: usize) -> Option<u16> {
375 let offset = index.checked_mul(2)?;
376 let bytes = self.values_bytes.get(offset..offset + 2)?;
377 Some(u16::from_be_bytes([bytes[0], bytes[1]]))
378 }
379}
380
381#[derive(Debug, Clone, Copy, PartialEq, Eq)]
383pub struct ReadWriteMultipleRegistersRequestData<'a> {
384 pub read_start_address: u16,
385 pub read_quantity: u16,
386 pub write_start_address: u16,
387 pub values_bytes: &'a [u8],
388}
389
390impl<'a> ReadWriteMultipleRegistersRequestData<'a> {
391 pub fn write_quantity(&self) -> usize {
392 self.values_bytes.len() / 2
393 }
394
395 pub fn register(&self, index: usize) -> Option<u16> {
396 let offset = index.checked_mul(2)?;
397 let bytes = self.values_bytes.get(offset..offset + 2)?;
398 Some(u16::from_be_bytes([bytes[0], bytes[1]]))
399 }
400}
401
402#[derive(Debug, Clone, Copy, PartialEq, Eq)]
404pub struct CustomRequestData<'a> {
405 pub function_code: u8,
406 pub data: &'a [u8],
407}
408
409#[cfg(feature = "alloc")]
410#[derive(Debug, Clone, PartialEq, Eq)]
411pub struct OwnedWriteMultipleCoilsRequest {
412 pub start_address: u16,
413 pub values: alloc::vec::Vec<bool>,
414}
415
416#[cfg(feature = "alloc")]
417impl OwnedWriteMultipleCoilsRequest {
418 pub fn as_borrowed(&self) -> WriteMultipleCoilsRequest<'_> {
419 WriteMultipleCoilsRequest {
420 start_address: self.start_address,
421 values: &self.values,
422 }
423 }
424}
425
426#[cfg(feature = "alloc")]
427#[derive(Debug, Clone, PartialEq, Eq)]
428pub struct OwnedWriteMultipleRegistersRequest {
429 pub start_address: u16,
430 pub values: alloc::vec::Vec<u16>,
431}
432
433#[cfg(feature = "alloc")]
434impl OwnedWriteMultipleRegistersRequest {
435 pub fn as_borrowed(&self) -> WriteMultipleRegistersRequest<'_> {
436 WriteMultipleRegistersRequest {
437 start_address: self.start_address,
438 values: &self.values,
439 }
440 }
441}
442
443#[derive(Debug, Clone, Copy, PartialEq, Eq)]
445#[non_exhaustive]
446pub enum Request<'a> {
447 ReadCoils(ReadCoilsRequest),
448 ReadDiscreteInputs(ReadDiscreteInputsRequest),
449 ReadHoldingRegisters(ReadHoldingRegistersRequest),
450 ReadInputRegisters(ReadInputRegistersRequest),
451 WriteSingleCoil(WriteSingleCoilRequest),
452 WriteSingleRegister(WriteSingleRegisterRequest),
453 WriteMultipleCoils(WriteMultipleCoilsRequest<'a>),
454 WriteMultipleRegisters(WriteMultipleRegistersRequest<'a>),
455 MaskWriteRegister(MaskWriteRegisterRequest),
456 ReadWriteMultipleRegisters(ReadWriteMultipleRegistersRequest<'a>),
457 ReadExceptionStatus(ReadExceptionStatusRequest),
458 Diagnostics(DiagnosticsRequest),
459 ReadFifoQueue(ReadFifoQueueRequest),
460 Custom(CustomRequest<'a>),
461}
462
463impl<'a> Request<'a> {
464 pub fn encode(&self, w: &mut Writer<'_>) -> Result<(), EncodeError> {
465 match self {
466 Self::ReadCoils(req) => req.encode(w),
467 Self::ReadDiscreteInputs(req) => req.encode(w),
468 Self::ReadHoldingRegisters(req) => req.encode(w),
469 Self::ReadInputRegisters(req) => req.encode(w),
470 Self::WriteSingleCoil(req) => req.encode(w),
471 Self::WriteSingleRegister(req) => req.encode(w),
472 Self::WriteMultipleCoils(req) => req.encode(w),
473 Self::WriteMultipleRegisters(req) => req.encode(w),
474 Self::MaskWriteRegister(req) => req.encode(w),
475 Self::ReadWriteMultipleRegisters(req) => req.encode(w),
476 Self::ReadExceptionStatus(req) => req.encode(w),
477 Self::Diagnostics(req) => req.encode(w),
478 Self::ReadFifoQueue(req) => req.encode(w),
479 Self::Custom(req) => req.encode(w),
480 }
481 }
482
483 pub fn function_code(&self) -> FunctionCode {
484 match self {
485 Self::ReadCoils(_) => FunctionCode::ReadCoils,
486 Self::ReadDiscreteInputs(_) => FunctionCode::ReadDiscreteInputs,
487 Self::ReadHoldingRegisters(_) => FunctionCode::ReadHoldingRegisters,
488 Self::ReadInputRegisters(_) => FunctionCode::ReadInputRegisters,
489 Self::WriteSingleCoil(_) => FunctionCode::WriteSingleCoil,
490 Self::WriteSingleRegister(_) => FunctionCode::WriteSingleRegister,
491 Self::WriteMultipleCoils(_) => FunctionCode::WriteMultipleCoils,
492 Self::WriteMultipleRegisters(_) => FunctionCode::WriteMultipleRegisters,
493 Self::MaskWriteRegister(_) => FunctionCode::MaskWriteRegister,
494 Self::ReadWriteMultipleRegisters(_) => FunctionCode::ReadWriteMultipleRegisters,
495 Self::ReadExceptionStatus(_) => FunctionCode::ReadExceptionStatus,
496 Self::Diagnostics(_) => FunctionCode::Diagnostics,
497 Self::ReadFifoQueue(_) => FunctionCode::ReadFifoQueue,
498 Self::Custom(req) => FunctionCode::Custom(req.function_code),
499 }
500 }
501}
502
503#[derive(Debug, Clone, Copy, PartialEq, Eq)]
505#[non_exhaustive]
506pub enum DecodedRequest<'a> {
507 ReadCoils(ReadCoilsRequest),
508 ReadDiscreteInputs(ReadDiscreteInputsRequest),
509 ReadHoldingRegisters(ReadHoldingRegistersRequest),
510 ReadInputRegisters(ReadInputRegistersRequest),
511 WriteSingleCoil(WriteSingleCoilRequest),
512 WriteSingleRegister(WriteSingleRegisterRequest),
513 WriteMultipleCoils(WriteMultipleCoilsRequestData<'a>),
514 WriteMultipleRegisters(WriteMultipleRegistersRequestData<'a>),
515 MaskWriteRegister(MaskWriteRegisterRequest),
516 ReadWriteMultipleRegisters(ReadWriteMultipleRegistersRequestData<'a>),
517 ReadExceptionStatus(ReadExceptionStatusRequest),
518 Diagnostics(DiagnosticsRequest),
519 ReadFifoQueue(ReadFifoQueueRequest),
520 Custom(CustomRequestData<'a>),
521}
522
523impl<'a> DecodedRequest<'a> {
524 pub fn function_code(&self) -> FunctionCode {
525 match self {
526 Self::ReadCoils(_) => FunctionCode::ReadCoils,
527 Self::ReadDiscreteInputs(_) => FunctionCode::ReadDiscreteInputs,
528 Self::ReadHoldingRegisters(_) => FunctionCode::ReadHoldingRegisters,
529 Self::ReadInputRegisters(_) => FunctionCode::ReadInputRegisters,
530 Self::WriteSingleCoil(_) => FunctionCode::WriteSingleCoil,
531 Self::WriteSingleRegister(_) => FunctionCode::WriteSingleRegister,
532 Self::WriteMultipleCoils(_) => FunctionCode::WriteMultipleCoils,
533 Self::WriteMultipleRegisters(_) => FunctionCode::WriteMultipleRegisters,
534 Self::MaskWriteRegister(_) => FunctionCode::MaskWriteRegister,
535 Self::ReadWriteMultipleRegisters(_) => FunctionCode::ReadWriteMultipleRegisters,
536 Self::ReadExceptionStatus(_) => FunctionCode::ReadExceptionStatus,
537 Self::Diagnostics(_) => FunctionCode::Diagnostics,
538 Self::ReadFifoQueue(_) => FunctionCode::ReadFifoQueue,
539 Self::Custom(req) => FunctionCode::Custom(req.function_code),
540 }
541 }
542
543 pub fn decode(r: &mut Reader<'a>) -> Result<Self, DecodeError> {
544 let function = FunctionCode::from_u8(r.read_u8()?)?;
545 match function {
546 FunctionCode::ReadCoils => {
547 let start_address = r.read_be_u16()?;
548 let quantity = r.read_be_u16()?;
549 validate_quantity_decode(quantity, MAX_READ_BITS)?;
550 Ok(Self::ReadCoils(ReadCoilsRequest {
551 start_address,
552 quantity,
553 }))
554 }
555 FunctionCode::ReadDiscreteInputs => {
556 let start_address = r.read_be_u16()?;
557 let quantity = r.read_be_u16()?;
558 validate_quantity_decode(quantity, MAX_READ_BITS)?;
559 Ok(Self::ReadDiscreteInputs(ReadDiscreteInputsRequest {
560 start_address,
561 quantity,
562 }))
563 }
564 FunctionCode::ReadHoldingRegisters => {
565 let start_address = r.read_be_u16()?;
566 let quantity = r.read_be_u16()?;
567 validate_quantity_decode(quantity, MAX_READ_REGISTERS)?;
568 Ok(Self::ReadHoldingRegisters(ReadHoldingRegistersRequest {
569 start_address,
570 quantity,
571 }))
572 }
573 FunctionCode::ReadInputRegisters => {
574 let start_address = r.read_be_u16()?;
575 let quantity = r.read_be_u16()?;
576 validate_quantity_decode(quantity, MAX_READ_REGISTERS)?;
577 Ok(Self::ReadInputRegisters(ReadInputRegistersRequest {
578 start_address,
579 quantity,
580 }))
581 }
582 FunctionCode::WriteSingleCoil => {
583 let address = r.read_be_u16()?;
584 let raw = r.read_be_u16()?;
585 let value = match raw {
586 0xFF00 => true,
587 0x0000 => false,
588 _ => return Err(DecodeError::InvalidValue),
589 };
590 Ok(Self::WriteSingleCoil(WriteSingleCoilRequest {
591 address,
592 value,
593 }))
594 }
595 FunctionCode::WriteSingleRegister => {
596 let address = r.read_be_u16()?;
597 let value = r.read_be_u16()?;
598 Ok(Self::WriteSingleRegister(WriteSingleRegisterRequest {
599 address,
600 value,
601 }))
602 }
603 FunctionCode::WriteMultipleCoils => {
604 let start_address = r.read_be_u16()?;
605 let quantity = r.read_be_u16()?;
606 validate_quantity_decode(quantity, MAX_WRITE_COILS)?;
607 let byte_count = usize::from(r.read_u8()?);
608 let expected = usize::from(quantity).div_ceil(8);
609 if byte_count != expected {
610 return Err(DecodeError::InvalidLength);
611 }
612 let values_packed = r.read_exact(byte_count)?;
613 Ok(Self::WriteMultipleCoils(WriteMultipleCoilsRequestData {
614 start_address,
615 quantity,
616 values_packed,
617 }))
618 }
619 FunctionCode::WriteMultipleRegisters => {
620 let start_address = r.read_be_u16()?;
621 let quantity = r.read_be_u16()?;
622 validate_quantity_decode(quantity, MAX_WRITE_REGISTERS)?;
623 let byte_count = usize::from(r.read_u8()?);
624 let expected = usize::from(quantity) * 2;
625 if byte_count != expected {
626 return Err(DecodeError::InvalidLength);
627 }
628 let values_bytes = r.read_exact(byte_count)?;
629 Ok(Self::WriteMultipleRegisters(WriteMultipleRegistersRequestData {
630 start_address,
631 values_bytes,
632 }))
633 }
634 FunctionCode::MaskWriteRegister => {
635 let address = r.read_be_u16()?;
636 let and_mask = r.read_be_u16()?;
637 let or_mask = r.read_be_u16()?;
638 Ok(Self::MaskWriteRegister(MaskWriteRegisterRequest {
639 address,
640 and_mask,
641 or_mask,
642 }))
643 }
644 FunctionCode::ReadWriteMultipleRegisters => {
645 let read_start_address = r.read_be_u16()?;
646 let read_quantity = r.read_be_u16()?;
647 validate_quantity_decode(read_quantity, MAX_READ_REGISTERS)?;
648 let write_start_address = r.read_be_u16()?;
649 let write_quantity = r.read_be_u16()?;
650 validate_quantity_decode(write_quantity, MAX_RW_WRITE_REGISTERS)?;
651 let byte_count = usize::from(r.read_u8()?);
652 let expected = usize::from(write_quantity) * 2;
653 if byte_count != expected {
654 return Err(DecodeError::InvalidLength);
655 }
656 let values_bytes = r.read_exact(byte_count)?;
657 Ok(Self::ReadWriteMultipleRegisters(
658 ReadWriteMultipleRegistersRequestData {
659 read_start_address,
660 read_quantity,
661 write_start_address,
662 values_bytes,
663 },
664 ))
665 }
666 FunctionCode::ReadExceptionStatus => {
667 Ok(Self::ReadExceptionStatus(ReadExceptionStatusRequest))
668 }
669 FunctionCode::Diagnostics => {
670 let sub_function = r.read_be_u16()?;
671 let data = r.read_be_u16()?;
672 Ok(Self::Diagnostics(DiagnosticsRequest { sub_function, data }))
673 }
674 FunctionCode::ReadFifoQueue => {
675 let fifo_pointer_address = r.read_be_u16()?;
676 Ok(Self::ReadFifoQueue(ReadFifoQueueRequest {
677 fifo_pointer_address,
678 }))
679 }
680 FunctionCode::Custom(function_code) => {
681 let data = r.read_exact(r.remaining())?;
682 Ok(Self::Custom(CustomRequestData {
683 function_code,
684 data,
685 }))
686 }
687 }
688 }
689}
690
691#[cfg(test)]
692mod tests {
693 use super::{
694 CustomRequest, DecodedRequest, MaskWriteRegisterRequest, ReadHoldingRegistersRequest,
695 ReadWriteMultipleRegistersRequest, Request, WriteMultipleCoilsRequest,
696 WriteMultipleRegistersRequest,
697 };
698 use crate::encoding::{Reader, Writer};
699 use crate::{DecodeError, EncodeError};
700
701 #[test]
702 fn read_holding_validates_quantity() {
703 let mut buf = [0u8; 8];
704 let mut w = Writer::new(&mut buf);
705 let req = ReadHoldingRegistersRequest {
706 start_address: 0,
707 quantity: 0,
708 };
709 assert_eq!(req.encode(&mut w).unwrap_err(), EncodeError::ValueOutOfRange);
710 }
711
712 #[test]
713 fn write_multiple_coils_packs_lsb_first() {
714 let req = WriteMultipleCoilsRequest {
715 start_address: 0x0013,
716 values: &[true, false, true, true, false, false, true, false, true],
717 };
718 let mut buf = [0u8; 32];
719 let mut w = Writer::new(&mut buf);
720 req.encode(&mut w).unwrap();
721 assert_eq!(
722 w.as_written(),
723 &[0x0F, 0x00, 0x13, 0x00, 0x09, 0x02, 0b0100_1101, 0b0000_0001]
724 );
725 }
726
727 #[test]
728 fn write_multiple_registers_rejects_too_many() {
729 let values = [0u16; 124];
730 let req = WriteMultipleRegistersRequest {
731 start_address: 0,
732 values: &values,
733 };
734 let mut buf = [0u8; 300];
735 let mut w = Writer::new(&mut buf);
736 assert_eq!(req.encode(&mut w).unwrap_err(), EncodeError::ValueOutOfRange);
737 }
738
739 #[test]
740 fn enum_dispatch_works() {
741 let req = Request::ReadHoldingRegisters(ReadHoldingRegistersRequest {
742 start_address: 0x006B,
743 quantity: 3,
744 });
745 let mut buf = [0u8; 8];
746 let mut w = Writer::new(&mut buf);
747 req.encode(&mut w).unwrap();
748 assert_eq!(w.as_written(), &[0x03, 0x00, 0x6B, 0x00, 0x03]);
749 }
750
751 #[test]
752 fn decode_fc03_request() {
753 let mut r = Reader::new(&[0x03, 0x00, 0x6B, 0x00, 0x03]);
754 let decoded = DecodedRequest::decode(&mut r).unwrap();
755 assert!(matches!(
756 decoded,
757 DecodedRequest::ReadHoldingRegisters(ReadHoldingRegistersRequest {
758 start_address: 0x006B,
759 quantity: 3
760 })
761 ));
762 assert!(r.is_empty());
763 }
764
765 #[test]
766 fn decode_fc15_request_and_bits() {
767 let mut r = Reader::new(&[0x0F, 0x00, 0x13, 0x00, 0x09, 0x02, 0b0100_1101, 0b0000_0001]);
768 let decoded = DecodedRequest::decode(&mut r).unwrap();
769 match decoded {
770 DecodedRequest::WriteMultipleCoils(req) => {
771 assert_eq!(req.start_address, 0x0013);
772 assert_eq!(req.quantity, 9);
773 assert_eq!(req.coil(0), Some(true));
774 assert_eq!(req.coil(1), Some(false));
775 assert_eq!(req.coil(8), Some(true));
776 assert_eq!(req.coil(9), None);
777 }
778 other => panic!("unexpected variant: {other:?}"),
779 }
780 }
781
782 #[test]
783 fn decode_rejects_invalid_fc16_byte_count() {
784 let mut r = Reader::new(&[0x10, 0x00, 0x00, 0x00, 0x02, 0x03, 0x12, 0x34, 0x56]);
785 assert_eq!(DecodedRequest::decode(&mut r).unwrap_err(), DecodeError::InvalidLength);
786 }
787
788 #[test]
789 fn decode_rejects_invalid_single_coil_value() {
790 let mut r = Reader::new(&[0x05, 0x00, 0x01, 0x12, 0x34]);
791 assert_eq!(DecodedRequest::decode(&mut r).unwrap_err(), DecodeError::InvalidValue);
792 }
793
794 #[test]
795 fn custom_request_roundtrip() {
796 let req = Request::Custom(CustomRequest {
797 function_code: 0x41,
798 data: &[0xAA, 0x55],
799 });
800 let mut buf = [0u8; 8];
801 let mut w = Writer::new(&mut buf);
802 req.encode(&mut w).unwrap();
803 assert_eq!(w.as_written(), &[0x41, 0xAA, 0x55]);
804
805 let mut r = Reader::new(w.as_written());
806 match DecodedRequest::decode(&mut r).unwrap() {
807 DecodedRequest::Custom(decoded) => {
808 assert_eq!(decoded.function_code, 0x41);
809 assert_eq!(decoded.data, &[0xAA, 0x55]);
810 }
811 other => panic!("unexpected variant: {other:?}"),
812 }
813 }
814
815 #[test]
816 fn mask_write_register_roundtrip() {
817 let req = Request::MaskWriteRegister(MaskWriteRegisterRequest {
818 address: 0x0004,
819 and_mask: 0xFF00,
820 or_mask: 0x0012,
821 });
822 let mut buf = [0u8; 16];
823 let mut w = Writer::new(&mut buf);
824 req.encode(&mut w).unwrap();
825 assert_eq!(w.as_written(), &[0x16, 0x00, 0x04, 0xFF, 0x00, 0x00, 0x12]);
826
827 let mut r = Reader::new(w.as_written());
828 match DecodedRequest::decode(&mut r).unwrap() {
829 DecodedRequest::MaskWriteRegister(decoded) => {
830 assert_eq!(decoded.address, 0x0004);
831 assert_eq!(decoded.and_mask, 0xFF00);
832 assert_eq!(decoded.or_mask, 0x0012);
833 }
834 other => panic!("unexpected variant: {other:?}"),
835 }
836 }
837
838 #[test]
839 fn read_write_multiple_registers_roundtrip() {
840 let req = Request::ReadWriteMultipleRegisters(ReadWriteMultipleRegistersRequest {
841 read_start_address: 0x0010,
842 read_quantity: 2,
843 write_start_address: 0x0020,
844 values: &[0x1111, 0x2222],
845 });
846
847 let mut buf = [0u8; 32];
848 let mut w = Writer::new(&mut buf);
849 req.encode(&mut w).unwrap();
850 assert_eq!(
851 w.as_written(),
852 &[0x17, 0x00, 0x10, 0x00, 0x02, 0x00, 0x20, 0x00, 0x02, 0x04, 0x11, 0x11, 0x22, 0x22]
853 );
854
855 let mut r = Reader::new(w.as_written());
856 match DecodedRequest::decode(&mut r).unwrap() {
857 DecodedRequest::ReadWriteMultipleRegisters(decoded) => {
858 assert_eq!(decoded.read_start_address, 0x0010);
859 assert_eq!(decoded.read_quantity, 2);
860 assert_eq!(decoded.write_start_address, 0x0020);
861 assert_eq!(decoded.write_quantity(), 2);
862 assert_eq!(decoded.register(0), Some(0x1111));
863 assert_eq!(decoded.register(1), Some(0x2222));
864 }
865 other => panic!("unexpected variant: {other:?}"),
866 }
867 }
868
869 #[test]
870 fn decode_rejects_invalid_fc23_byte_count() {
871 let mut r = Reader::new(&[0x17, 0x00, 0x10, 0x00, 0x01, 0x00, 0x20, 0x00, 0x01, 0x01, 0x12]);
872 assert_eq!(DecodedRequest::decode(&mut r).unwrap_err(), DecodeError::InvalidLength);
873 }
874}