Skip to main content

rusty_modbus_codec/request/
reg_read.rs

1//! Register-access read requests: FC 03 (Read Holding Registers) and FC 04 (Read Input Registers).
2
3use rusty_modbus_types::{Address, FunctionCode, Quantity};
4
5use crate::error::{DecodeError, EncodeError};
6use crate::request::Encode;
7
8/// FC 0x03 — Read Holding Registers request.
9///
10/// Reads 1..=125 contiguous holding registers starting at `address`.
11#[derive(Debug, Clone, Copy, PartialEq, Eq)]
12pub struct ReadHoldingRegistersRequest {
13    /// Starting address (0-indexed).
14    pub address: Address,
15    /// Number of registers to read (1..=125).
16    pub quantity: Quantity,
17}
18
19impl ReadHoldingRegistersRequest {
20    /// Maximum quantity for Read Holding Registers.
21    const MAX_QUANTITY: u16 = 125;
22
23    /// Decode from PDU data after the function code byte.
24    ///
25    /// # Errors
26    ///
27    /// Returns [`DecodeError::Truncated`] if `data` is shorter than 4 bytes.
28    /// Returns [`DecodeError::LengthMismatch`] if `data` has extra bytes.
29    /// Returns [`DecodeError::QuantityOutOfRange`] if the quantity is not in 1..=125.
30    pub fn decode(data: &[u8]) -> Result<Self, DecodeError> {
31        DecodeError::check_exact_len(data, 4)?;
32        let address = Address(u16::from_be_bytes([data[0], data[1]]));
33        let quantity = u16::from_be_bytes([data[2], data[3]]);
34        if quantity == 0 || quantity > Self::MAX_QUANTITY {
35            return Err(DecodeError::QuantityOutOfRange { quantity });
36        }
37        Ok(Self {
38            address,
39            quantity: Quantity(quantity),
40        })
41    }
42}
43
44impl Encode for ReadHoldingRegistersRequest {
45    fn encode_into(&self, buf: &mut [u8]) -> Result<usize, EncodeError> {
46        let len = self.encoded_len();
47        if buf.len() < len {
48            return Err(EncodeError::BufferTooSmall {
49                required: len,
50                available: buf.len(),
51            });
52        }
53        EncodeError::check_quantity(self.quantity.0, Self::MAX_QUANTITY)?;
54        EncodeError::check_pdu_len(len)?;
55        buf[0] = FunctionCode::ReadHoldingRegisters.code();
56        buf[1..3].copy_from_slice(&self.address.0.to_be_bytes());
57        buf[3..5].copy_from_slice(&self.quantity.0.to_be_bytes());
58        Ok(len)
59    }
60
61    fn encoded_len(&self) -> usize {
62        5
63    }
64}
65
66/// FC 0x04 — Read Input Registers request.
67///
68/// Reads 1..=125 contiguous input registers starting at `address`.
69#[derive(Debug, Clone, Copy, PartialEq, Eq)]
70pub struct ReadInputRegistersRequest {
71    /// Starting address (0-indexed).
72    pub address: Address,
73    /// Number of registers to read (1..=125).
74    pub quantity: Quantity,
75}
76
77impl ReadInputRegistersRequest {
78    /// Maximum quantity for Read Input Registers.
79    const MAX_QUANTITY: u16 = 125;
80
81    /// Decode from PDU data after the function code byte.
82    ///
83    /// # Errors
84    ///
85    /// Returns [`DecodeError::Truncated`] if `data` is shorter than 4 bytes.
86    /// Returns [`DecodeError::LengthMismatch`] if `data` has extra bytes.
87    /// Returns [`DecodeError::QuantityOutOfRange`] if the quantity is not in 1..=125.
88    pub fn decode(data: &[u8]) -> Result<Self, DecodeError> {
89        DecodeError::check_exact_len(data, 4)?;
90        let address = Address(u16::from_be_bytes([data[0], data[1]]));
91        let quantity = u16::from_be_bytes([data[2], data[3]]);
92        if quantity == 0 || quantity > Self::MAX_QUANTITY {
93            return Err(DecodeError::QuantityOutOfRange { quantity });
94        }
95        Ok(Self {
96            address,
97            quantity: Quantity(quantity),
98        })
99    }
100}
101
102impl Encode for ReadInputRegistersRequest {
103    fn encode_into(&self, buf: &mut [u8]) -> Result<usize, EncodeError> {
104        let len = self.encoded_len();
105        if buf.len() < len {
106            return Err(EncodeError::BufferTooSmall {
107                required: len,
108                available: buf.len(),
109            });
110        }
111        EncodeError::check_quantity(self.quantity.0, Self::MAX_QUANTITY)?;
112        EncodeError::check_pdu_len(len)?;
113        buf[0] = FunctionCode::ReadInputRegisters.code();
114        buf[1..3].copy_from_slice(&self.address.0.to_be_bytes());
115        buf[3..5].copy_from_slice(&self.quantity.0.to_be_bytes());
116        Ok(len)
117    }
118
119    fn encoded_len(&self) -> usize {
120        5
121    }
122}