Skip to main content

rusty_modbus_codec/response/
fifo.rs

1//! Response type for Read FIFO Queue (FC 18).
2
3use crate::error::{DecodeError, EncodeError};
4use crate::request::Encode;
5use rusty_modbus_types::{FunctionCode, MAX_FIFO_VALUES};
6
7/// Response to a Read FIFO Queue request (FC 0x18).
8#[derive(Debug)]
9pub struct ReadFifoQueueResponse<'buf> {
10    /// Total number of bytes following this field.
11    pub byte_count: u16,
12    /// Number of FIFO register values (0..=31).
13    pub fifo_count: u16,
14    /// Raw FIFO register data in big-endian byte order.
15    pub fifo_values: &'buf [u8],
16}
17
18impl<'buf> ReadFifoQueueResponse<'buf> {
19    /// Decode from the data bytes following the function code.
20    ///
21    /// # Errors
22    ///
23    /// Returns `DecodeError::Truncated` if `data` is too short.
24    /// Returns `DecodeError::ByteCountMismatch` if the declared byte count
25    /// does not match the remaining data length.
26    pub fn decode(data: &'buf [u8]) -> Result<Self, DecodeError> {
27        if data.len() < 4 {
28            return Err(DecodeError::Truncated {
29                expected: 4,
30                actual: data.len(),
31            });
32        }
33        let byte_count = u16::from_be_bytes([data[0], data[1]]);
34        let fifo_count = u16::from_be_bytes([data[2], data[3]]);
35        let fifo_values = &data[4..];
36        // byte_count includes the 2 bytes for fifo_count + the fifo data.
37        // Must be at least 2 to cover the fifo_count field.
38        if byte_count < 2 {
39            return Err(DecodeError::ByteCountMismatch {
40                declared: usize::from(byte_count),
41                actual: 2 + fifo_values.len(),
42            });
43        }
44        let expected_remaining = usize::from(byte_count) - 2;
45        if fifo_values.len() != expected_remaining {
46            return Err(DecodeError::ByteCountMismatch {
47                declared: expected_remaining,
48                actual: fifo_values.len(),
49            });
50        }
51        // Spec §6.18: fifo_count must be 0..=31.
52        if fifo_count > 31 {
53            return Err(DecodeError::QuantityOutOfRange {
54                quantity: fifo_count,
55            });
56        }
57        // Cross-check: fifo_values must hold exactly fifo_count × 2 bytes.
58        let expected_data_len = usize::from(fifo_count) * 2;
59        if fifo_values.len() != expected_data_len {
60            return Err(DecodeError::ByteCountMismatch {
61                declared: expected_data_len,
62                actual: fifo_values.len(),
63            });
64        }
65        Ok(Self {
66            byte_count,
67            fifo_count,
68            fifo_values,
69        })
70    }
71}
72
73impl Encode for ReadFifoQueueResponse<'_> {
74    fn encode_into(&self, buf: &mut [u8]) -> Result<usize, EncodeError> {
75        let len = self.encoded_len();
76        if buf.len() < len {
77            return Err(EncodeError::BufferTooSmall {
78                required: len,
79                available: buf.len(),
80            });
81        }
82        EncodeError::check_byte_count(usize::from(self.byte_count), 2 + self.fifo_values.len())?;
83        if self.fifo_count > MAX_FIFO_VALUES {
84            return Err(EncodeError::QuantityOutOfRange {
85                quantity: self.fifo_count,
86            });
87        }
88        let expected_data_len = usize::from(self.fifo_count) * 2;
89        EncodeError::check_byte_count(expected_data_len, self.fifo_values.len())?;
90        EncodeError::check_pdu_len(len)?;
91        buf[0] = FunctionCode::ReadFifoQueue.code();
92        let bc = self.byte_count.to_be_bytes();
93        buf[1] = bc[0];
94        buf[2] = bc[1];
95        let fc = self.fifo_count.to_be_bytes();
96        buf[3] = fc[0];
97        buf[4] = fc[1];
98        buf[5..5 + self.fifo_values.len()].copy_from_slice(self.fifo_values);
99        Ok(len)
100    }
101
102    fn encoded_len(&self) -> usize {
103        // FC + byte_count(2) + fifo_count(2) + fifo_values
104        1 + 2 + 2 + self.fifo_values.len()
105    }
106}