use heapless::Vec;
use mbus_core::data_unit::common::{MAX_PDU_DATA_LEN, Pdu};
use mbus_core::errors::MbusError;
use mbus_core::function_codes::public::FunctionCode;
use mbus_core::models::coil::Coils;
pub(super) struct ReqPduCompiler {}
impl ReqPduCompiler {
pub(super) fn read_coils_request(address: u16, quantity: u16) -> Result<Pdu, MbusError> {
if !(1..=2000).contains(&quantity) {
return Err(MbusError::InvalidQuantity); }
let mut data_vec: Vec<u8, MAX_PDU_DATA_LEN> = Vec::new();
data_vec
.extend_from_slice(&address.to_be_bytes())
.map_err(|_| MbusError::BufferLenMissmatch)?;
data_vec
.extend_from_slice(&quantity.to_be_bytes())
.map_err(|_| MbusError::BufferLenMissmatch)?;
Ok(Pdu::new(
FunctionCode::ReadCoils,
data_vec,
4, ))
}
pub(super) fn write_single_coil_request(address: u16, value: bool) -> Result<Pdu, MbusError> {
macro_rules! push_be {
($vec:expr, $val:expr) => {
$vec.extend_from_slice(&$val.to_be_bytes())
.map_err(|_| MbusError::BufferLenMissmatch)
};
}
let mut data_bytes: Vec<u8, MAX_PDU_DATA_LEN> = Vec::new();
push_be!(data_bytes, address)?;
let coil_value: u16 = if value { 0xFF00 } else { 0x0000 };
push_be!(data_bytes, coil_value)?;
Ok(Pdu::new(
FunctionCode::WriteSingleCoil,
data_bytes,
4, ))
}
pub(super) fn write_multiple_coils_request(
address: u16,
quantity: u16,
values: &Coils,
) -> Result<Pdu, MbusError> {
if !(1..=1968).contains(&quantity) {
return Err(MbusError::InvalidPduLength);
}
let byte_count = quantity.div_ceil(8) as u8;
let mut data_vec: Vec<u8, MAX_PDU_DATA_LEN> = Vec::new();
data_vec
.extend_from_slice(&address.to_be_bytes())
.map_err(|_| MbusError::BufferLenMissmatch)?;
data_vec
.extend_from_slice(&quantity.to_be_bytes())
.map_err(|_| MbusError::BufferLenMissmatch)?;
data_vec
.push(byte_count)
.map_err(|_| MbusError::BufferLenMissmatch)?;
let num_coil_bytes = byte_count as usize;
data_vec
.extend_from_slice(&values.values()[..num_coil_bytes])
.map_err(|_| MbusError::BufferLenMissmatch)?;
Ok(Pdu::new(
FunctionCode::WriteMultipleCoils,
data_vec,
5 + byte_count, ))
}
}