Skip to main content

rusty_modbus_codec/response/
reg_read.rs

1//! Response types for register-read function codes (FC 03, 04).
2
3use crate::error::{DecodeError, EncodeError};
4use crate::request::Encode;
5use rusty_modbus_types::FunctionCode;
6
7/// Response to a Read Holding Registers request (FC 0x03).
8#[derive(Debug)]
9pub struct ReadHoldingRegistersResponse<'buf> {
10    /// Number of data bytes that follow (should be 2 * register count).
11    pub byte_count: u8,
12    /// Raw register data in big-endian byte order.
13    pub register_data: &'buf [u8],
14}
15
16impl<'buf> ReadHoldingRegistersResponse<'buf> {
17    /// Returns the number of registers in this response.
18    #[must_use]
19    pub fn count(&self) -> usize {
20        self.register_data.len() / 2
21    }
22
23    /// Returns the register value at the given zero-based index.
24    ///
25    /// # Panics
26    ///
27    /// Panics if `index` is out of range.
28    #[must_use]
29    pub fn register(&self, index: usize) -> u16 {
30        let off = index * 2;
31        u16::from_be_bytes([self.register_data[off], self.register_data[off + 1]])
32    }
33
34    /// Returns an iterator over all register values.
35    pub fn registers(&self) -> impl Iterator<Item = u16> + '_ {
36        self.register_data
37            .chunks_exact(2)
38            .map(|c| u16::from_be_bytes([c[0], c[1]]))
39    }
40
41    /// Returns the raw register data bytes.
42    #[must_use]
43    pub fn raw(&self) -> &'buf [u8] {
44        self.register_data
45    }
46
47    /// Decode from the data bytes following the function code.
48    ///
49    /// # Errors
50    ///
51    /// Returns `DecodeError::Truncated` if `data` is too short.
52    /// Returns `DecodeError::ByteCountMismatch` if the declared byte count
53    /// does not match the remaining data length.
54    pub fn decode(data: &'buf [u8]) -> Result<Self, DecodeError> {
55        if data.is_empty() {
56            return Err(DecodeError::Truncated {
57                expected: 1,
58                actual: 0,
59            });
60        }
61        let byte_count = data[0];
62        let register_data = &data[1..];
63        if register_data.len() != usize::from(byte_count) {
64            return Err(DecodeError::ByteCountMismatch {
65                declared: usize::from(byte_count),
66                actual: register_data.len(),
67            });
68        }
69        Ok(Self {
70            byte_count,
71            register_data,
72        })
73    }
74}
75
76impl Encode for ReadHoldingRegistersResponse<'_> {
77    fn encode_into(&self, buf: &mut [u8]) -> Result<usize, EncodeError> {
78        let len = self.encoded_len();
79        if buf.len() < len {
80            return Err(EncodeError::BufferTooSmall {
81                required: len,
82                available: buf.len(),
83            });
84        }
85        EncodeError::check_byte_count(usize::from(self.byte_count), self.register_data.len())?;
86        EncodeError::check_pdu_len(len)?;
87        buf[0] = FunctionCode::ReadHoldingRegisters.code();
88        buf[1] = self.byte_count;
89        buf[2..2 + usize::from(self.byte_count)].copy_from_slice(self.register_data);
90        Ok(len)
91    }
92
93    fn encoded_len(&self) -> usize {
94        1 + 1 + usize::from(self.byte_count)
95    }
96}
97
98/// Response to a Read Input Registers request (FC 0x04).
99#[derive(Debug)]
100pub struct ReadInputRegistersResponse<'buf> {
101    /// Number of data bytes that follow (should be 2 * register count).
102    pub byte_count: u8,
103    /// Raw register data in big-endian byte order.
104    pub register_data: &'buf [u8],
105}
106
107impl<'buf> ReadInputRegistersResponse<'buf> {
108    /// Returns the number of registers in this response.
109    #[must_use]
110    pub fn count(&self) -> usize {
111        self.register_data.len() / 2
112    }
113
114    /// Returns the register value at the given zero-based index.
115    ///
116    /// # Panics
117    ///
118    /// Panics if `index` is out of range.
119    #[must_use]
120    pub fn register(&self, index: usize) -> u16 {
121        let off = index * 2;
122        u16::from_be_bytes([self.register_data[off], self.register_data[off + 1]])
123    }
124
125    /// Returns an iterator over all register values.
126    pub fn registers(&self) -> impl Iterator<Item = u16> + '_ {
127        self.register_data
128            .chunks_exact(2)
129            .map(|c| u16::from_be_bytes([c[0], c[1]]))
130    }
131
132    /// Returns the raw register data bytes.
133    #[must_use]
134    pub fn raw(&self) -> &'buf [u8] {
135        self.register_data
136    }
137
138    /// Decode from the data bytes following the function code.
139    ///
140    /// # Errors
141    ///
142    /// Returns `DecodeError::Truncated` if `data` is too short.
143    /// Returns `DecodeError::ByteCountMismatch` if the declared byte count
144    /// does not match the remaining data length.
145    pub fn decode(data: &'buf [u8]) -> Result<Self, DecodeError> {
146        if data.is_empty() {
147            return Err(DecodeError::Truncated {
148                expected: 1,
149                actual: 0,
150            });
151        }
152        let byte_count = data[0];
153        let register_data = &data[1..];
154        if register_data.len() != usize::from(byte_count) {
155            return Err(DecodeError::ByteCountMismatch {
156                declared: usize::from(byte_count),
157                actual: register_data.len(),
158            });
159        }
160        Ok(Self {
161            byte_count,
162            register_data,
163        })
164    }
165}
166
167impl Encode for ReadInputRegistersResponse<'_> {
168    fn encode_into(&self, buf: &mut [u8]) -> Result<usize, EncodeError> {
169        let len = self.encoded_len();
170        if buf.len() < len {
171            return Err(EncodeError::BufferTooSmall {
172                required: len,
173                available: buf.len(),
174            });
175        }
176        EncodeError::check_byte_count(usize::from(self.byte_count), self.register_data.len())?;
177        EncodeError::check_pdu_len(len)?;
178        buf[0] = FunctionCode::ReadInputRegisters.code();
179        buf[1] = self.byte_count;
180        buf[2..2 + usize::from(self.byte_count)].copy_from_slice(self.register_data);
181        Ok(len)
182    }
183
184    fn encoded_len(&self) -> usize {
185        1 + 1 + usize::from(self.byte_count)
186    }
187}