rusty_modbus_codec/request/
bit_write.rs1use rusty_modbus_types::{Address, CoilValue, FunctionCode, Quantity};
4
5use crate::error::{DecodeError, EncodeError};
6use crate::request::Encode;
7
8#[derive(Debug, Clone, Copy, PartialEq, Eq)]
12pub struct WriteSingleCoilRequest {
13 pub address: Address,
15 pub value: CoilValue,
17}
18
19impl WriteSingleCoilRequest {
20 pub fn decode(data: &[u8]) -> Result<Self, DecodeError> {
28 DecodeError::check_exact_len(data, 4)?;
29 let address = Address(u16::from_be_bytes([data[0], data[1]]));
30 let raw_value = u16::from_be_bytes([data[2], data[3]]);
31 let value =
32 CoilValue::from_wire(raw_value).ok_or(DecodeError::InvalidCoilValue(raw_value))?;
33 Ok(Self { address, value })
34 }
35}
36
37impl Encode for WriteSingleCoilRequest {
38 fn encode_into(&self, buf: &mut [u8]) -> Result<usize, EncodeError> {
39 let len = self.encoded_len();
40 if buf.len() < len {
41 return Err(EncodeError::BufferTooSmall {
42 required: len,
43 available: buf.len(),
44 });
45 }
46 EncodeError::check_pdu_len(len)?;
47 buf[0] = FunctionCode::WriteSingleCoil.code();
48 buf[1..3].copy_from_slice(&self.address.0.to_be_bytes());
49 buf[3..5].copy_from_slice(&self.value.to_wire().to_be_bytes());
50 Ok(len)
51 }
52
53 fn encoded_len(&self) -> usize {
54 5
55 }
56}
57
58#[derive(Debug, Clone, Copy, PartialEq, Eq)]
63pub struct WriteMultipleCoilsRequest<'buf> {
64 pub address: Address,
66 pub quantity: Quantity,
68 pub byte_count: u8,
70 pub coil_values: &'buf [u8],
72}
73
74impl<'buf> WriteMultipleCoilsRequest<'buf> {
75 const MAX_QUANTITY: u16 = 1968;
77
78 pub fn decode(data: &'buf [u8]) -> Result<Self, DecodeError> {
87 if data.len() < 5 {
88 return Err(DecodeError::Truncated {
89 expected: 5,
90 actual: data.len(),
91 });
92 }
93 let address = Address(u16::from_be_bytes([data[0], data[1]]));
94 let quantity = u16::from_be_bytes([data[2], data[3]]);
95 if quantity == 0 || quantity > Self::MAX_QUANTITY {
96 return Err(DecodeError::QuantityOutOfRange { quantity });
97 }
98 let byte_count = data[4];
99 let expected_bytes = quantity.div_ceil(8);
101 if u16::from(byte_count) != expected_bytes {
102 return Err(DecodeError::ByteCountMismatch {
103 declared: usize::from(byte_count),
104 actual: expected_bytes as usize,
105 });
106 }
107 let remaining = data.len() - 5;
109 if byte_count as usize != remaining {
110 return Err(DecodeError::ByteCountMismatch {
111 declared: byte_count as usize,
112 actual: remaining,
113 });
114 }
115 let coil_values = &data[5..];
116 Ok(Self {
117 address,
118 quantity: Quantity(quantity),
119 byte_count,
120 coil_values,
121 })
122 }
123}
124
125impl Encode for WriteMultipleCoilsRequest<'_> {
126 fn encode_into(&self, buf: &mut [u8]) -> Result<usize, EncodeError> {
127 let len = self.encoded_len();
128 if buf.len() < len {
129 return Err(EncodeError::BufferTooSmall {
130 required: len,
131 available: buf.len(),
132 });
133 }
134 EncodeError::check_quantity(self.quantity.0, Self::MAX_QUANTITY)?;
135 let expected_bytes = usize::from(self.quantity.0.div_ceil(8));
136 EncodeError::check_byte_count(usize::from(self.byte_count), expected_bytes)?;
137 EncodeError::check_byte_count(expected_bytes, self.coil_values.len())?;
138 EncodeError::check_pdu_len(len)?;
139 buf[0] = FunctionCode::WriteMultipleCoils.code();
140 buf[1..3].copy_from_slice(&self.address.0.to_be_bytes());
141 buf[3..5].copy_from_slice(&self.quantity.0.to_be_bytes());
142 buf[5] = self.byte_count;
143 buf[6..6 + self.coil_values.len()].copy_from_slice(self.coil_values);
144 Ok(len)
145 }
146
147 fn encoded_len(&self) -> usize {
148 6 + self.coil_values.len()
150 }
151}