Skip to main content

fennec_modbus/protocol/function/
read_multiple.rs

1//! Codes for functions that read multiple coils or registers.
2
3use core::marker::PhantomData;
4
5use bytes::{Buf, BufMut};
6
7use crate::{
8    Error,
9    protocol::{
10        Address,
11        codec::{BitSize, Decode, Encode, adapters::DropRemaining},
12        function,
13        function::size_argument::SizeArgument,
14    },
15};
16
17/// Address range for reading operations.
18///
19/// # Example
20///
21/// ```rust
22/// use fennec_modbus::protocol::{
23///     codec::Encode,
24///     function::{read_multiple::Args, size_argument},
25/// };
26///
27/// // Read holding registers 108–110 (Modbus spec §6.3 example).
28/// assert_eq!(
29///     Args::<_, [u16; 3], size_argument::Words>::new(0x006B_u16).to_bytes(),
30///     [
31///         0x00, 0x6B, // starting address
32///         0x00, 0x03, // quantity of registers
33///     ]
34/// );
35/// ```
36#[must_use]
37pub struct Args<A, V, S>(
38    /// Bare starting address.
39    A,
40    /// Binding to the value type. This is needed to know the number of registers or coils.
41    PhantomData<V>,
42    /// Binding to the size type, normally [`size_argument::Bits`] or [`size_argument::Words`].
43    PhantomData<S>,
44);
45
46impl<A, V: BitSize, S> From<A> for Args<A, V, S> {
47    /// Wrap the address into [`Args`].
48    fn from(address: A) -> Self {
49        Self::new(address)
50    }
51}
52
53impl<A, V: BitSize, S> Args<A, V, S> {
54    /// Create the address range from the starting address.
55    pub const fn new(starting_address: A) -> Self {
56        Self(starting_address, PhantomData, PhantomData)
57    }
58}
59
60impl<A: Address, V: BitSize, S: SizeArgument> Encode for Args<A, V, S> {
61    /// Encode the address and number of bits to read.
62    fn encode_to(&self, buf: &mut impl BufMut) {
63        S::assert_valid_size::<V, 250>();
64        self.0.encode_to(buf);
65        buf.put_u16(S::quantity_for::<V>());
66    }
67}
68
69/// Output decoder for the read operations.
70///
71/// # Example
72///
73/// ```rust
74/// use fennec_modbus::protocol::{
75///     codec::Decode,
76///     function::{IntoValue, read_multiple::Output},
77/// };
78///
79/// const BYTES: &[u8] = &[
80///     0x04, // byte count
81///     0x02, 0x2B, // register: high, low
82///     0x00, 0x00, // register: high, low
83/// ];
84///
85/// #[expect(const_item_mutation)]
86/// let value = Output::<u32>::decode_from(&mut BYTES).unwrap().into_value();
87/// assert_eq!(value, 0x022B0000);
88/// ```
89pub struct Output<V>(V);
90
91impl<V: Decode> Decode for Output<V> {
92    fn decode_from(buf: &mut impl Buf) -> Result<Self, Error> {
93        let n_bytes = buf.try_get_u8()?;
94        let mut from = DropRemaining(buf).take(usize::from(n_bytes));
95        V::decode_from(&mut from).map(Self)
96    }
97}
98
99impl<V> function::IntoValue for Output<V> {
100    type Value = V;
101
102    fn into_value(self) -> Self::Value {
103        self.0
104    }
105}