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}