use crate::{data_unit::common::MAX_PDU_DATA_LEN, errors::MbusError};
use heapless::Vec;
pub const MAX_SUB_REQUESTS_PER_PDU: usize = 35;
pub const SUB_REQ_PARAM_BYTE_LEN: usize = 6 + 1;
pub const FILE_RECORD_REF_TYPE: u8 = 0x06;
pub trait PduDataBytes {
fn to_sub_req_pdu_bytes(&self) -> Result<Vec<u8, MAX_PDU_DATA_LEN>, MbusError>;
}
#[derive(Debug, Clone, PartialEq)]
pub struct SubRequestParams {
pub file_number: u16,
pub record_number: u16,
pub record_length: u16,
pub record_data: Option<Vec<u16, MAX_PDU_DATA_LEN>>,
}
#[derive(Debug, Clone, Default)]
pub struct SubRequest {
params: Vec<SubRequestParams, MAX_SUB_REQUESTS_PER_PDU>,
total_read_bytes_length: u16,
}
impl SubRequest {
pub fn new() -> Self {
SubRequest {
params: Vec::new(),
total_read_bytes_length: 0,
}
}
pub fn add_read_sub_request(
&mut self,
file_number: u16,
record_number: u16,
record_length: u16,
) -> Result<(), MbusError> {
if self.params.len() >= MAX_SUB_REQUESTS_PER_PDU {
return Err(MbusError::TooManyFileReadSubRequests);
}
if (self.params.len() as u16 + 1) + (self.total_read_bytes_length + record_length) > 125 {
return Err(MbusError::FileReadPduOverflow);
}
self.params
.push(SubRequestParams {
file_number,
record_number,
record_length,
record_data: None,
})
.map_err(|_| MbusError::TooManyFileReadSubRequests)?;
self.total_read_bytes_length += record_length;
Ok(())
}
pub fn add_write_sub_request(
&mut self,
file_number: u16,
record_number: u16,
record_length: u16,
record_data: Vec<u16, MAX_PDU_DATA_LEN>,
) -> Result<(), MbusError> {
if self.params.len() >= MAX_SUB_REQUESTS_PER_PDU {
return Err(MbusError::TooManyFileReadSubRequests);
}
if record_data.len() != record_length as usize {
return Err(MbusError::BufferLenMissmatch);
}
let current_payload_size = self.byte_count();
let new_sub_req_size = SUB_REQ_PARAM_BYTE_LEN + (record_data.len() * 2);
if 1 + current_payload_size + new_sub_req_size > MAX_PDU_DATA_LEN {
return Err(MbusError::FileReadPduOverflow);
}
self.params
.push(SubRequestParams {
file_number,
record_number,
record_length,
record_data: Some(record_data),
})
.map_err(|_| MbusError::TooManyFileReadSubRequests)?;
self.total_read_bytes_length += record_length;
Ok(())
}
pub fn byte_count(&self) -> usize {
self.params
.iter()
.map(|p| {
7 + p.record_data.as_ref().map(|d| d.len() * 2).unwrap_or(0)
})
.sum()
}
pub fn clear_all(&mut self) {
self.total_read_bytes_length = 0;
self.params.clear();
}
}
impl PduDataBytes for SubRequest {
fn to_sub_req_pdu_bytes(&self) -> Result<Vec<u8, MAX_PDU_DATA_LEN>, MbusError> {
let mut bytes = Vec::new();
let byte_count = self.byte_count();
bytes
.push(byte_count as u8)
.map_err(|_| MbusError::BufferTooSmall)?;
for param in &self.params {
bytes
.push(FILE_RECORD_REF_TYPE)
.map_err(|_| MbusError::BufferTooSmall)?;
bytes
.extend_from_slice(¶m.file_number.to_be_bytes())
.map_err(|_| MbusError::BufferLenMissmatch)?;
bytes
.extend_from_slice(¶m.record_number.to_be_bytes())
.map_err(|_| MbusError::BufferLenMissmatch)?;
bytes
.extend_from_slice(¶m.record_length.to_be_bytes())
.map_err(|_| MbusError::BufferLenMissmatch)?;
if let Some(ref data) = param.record_data {
for val in data {
bytes
.extend_from_slice(&val.to_be_bytes())
.map_err(|_| MbusError::BufferLenMissmatch)?;
}
}
}
Ok(bytes)
}
}