use crate::{Error, Result};
use bytes::{Buf, BufMut, Bytes, BytesMut};
use std::iter::zip;
use tokio_modbus::Address;
pub struct ModbusFeedbackFrame<'a> {
pub read_addresses: &'a [Address],
pub write_addresses: &'a [Address],
pub read_counts: &'a [u8],
pub write_counts: &'a [u8],
pub write_data: Bytes,
}
impl<'a> ModbusFeedbackFrame<'a> {
pub fn new(
read_addresses: &'a [Address],
write_addresses: &'a [Address],
read_counts: &'a [u8],
write_counts: &'a [u8],
write_data: Bytes,
) -> Self {
Self {
read_addresses,
write_addresses,
read_counts,
write_counts,
write_data,
}
}
pub fn new_read_frame(read_addresses: &'a [Address], read_counts: &'a [u8]) -> Self {
Self {
read_addresses,
write_addresses: &[],
read_counts,
write_counts: &[],
write_data: Bytes::new(),
}
}
pub fn new_write_frame(
write_addresses: &'a [Address],
write_counts: &'a [u8],
write_data: Bytes,
) -> Self {
Self {
read_addresses: &[],
write_addresses,
read_counts: &[],
write_counts,
write_data,
}
}
pub fn to_bytes_mut(&mut self) -> Result<Bytes> {
let mut bytes = BytesMut::with_capacity(
self.read_addresses.len() * 4 + self.write_addresses.len() * 4 + self.write_data.len(),
);
for (address, num_registers) in zip(self.write_addresses, self.write_counts) {
bytes.put_u8(1);
bytes.put_u16(*address);
bytes.put_u8(*num_registers);
match *num_registers {
1 => bytes.put_u16(self.write_data.get_u16()),
2 => bytes.put_u32(self.write_data.get_u32()),
_ => {
return Err(Error::Other(
"There should never be a writeable tag with a register count not equal to 1 or 2."
.into(),
))
}
}
}
for (address, count) in zip(self.read_addresses, self.read_counts) {
bytes.put_u8(0);
bytes.put_u16(*address);
bytes.put_u8(*count);
}
tracing::debug!("Raw bytes of mbfb: {bytes:?}");
Ok(bytes.freeze())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_mbfb_to_bytes_mut_read_only() {
let read_addresses = [1, 2, 3];
let read_counts = [1, 2, 4];
let mut mbfb = ModbusFeedbackFrame::new_read_frame(&read_addresses, &read_counts);
let bytes = mbfb.to_bytes_mut().unwrap();
let expected: Vec<u8> = vec![
0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0x03, 0x04,
];
assert_eq!(bytes, Bytes::from(expected));
}
#[test]
fn test_mbfb_to_bytes_mut_write_only() {
let write_addresses = [1, 2];
let write_counts = [1, 2];
let write_data = Bytes::from(vec![0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC]);
let mut mbfb =
ModbusFeedbackFrame::new_write_frame(&write_addresses, &write_counts, write_data);
let bytes = mbfb.to_bytes_mut().unwrap();
let expected: Vec<u8> = vec![
0x01, 0x00, 0x01, 0x01, 0x12, 0x34, 0x01, 0x00, 0x02, 0x02, 0x56, 0x78, 0x9A, 0xBC,
];
assert_eq!(bytes, Bytes::from(expected));
}
#[test]
fn test_mbfb_to_bytes_mut_read_write() {
let read_addresses = [1, 2];
let write_addresses = [3, 4];
let read_counts = [1, 2];
let write_counts = [2, 1];
let write_data = Bytes::from(vec![0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC]);
let mut mbfb = ModbusFeedbackFrame::new(
&read_addresses,
&write_addresses,
&read_counts,
&write_counts,
write_data,
);
let bytes = mbfb.to_bytes_mut().unwrap();
let expected: Vec<u8> = vec![
0x01, 0x00, 0x03, 0x02, 0x12, 0x34, 0x56, 0x78, 0x01, 0x00, 0x04, 0x01, 0x9A, 0xBC,
0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x02, 0x02,
];
assert_eq!(bytes, Bytes::from(expected));
}
#[test]
fn test_invalid_mbfb() {
let write_addresses = [1];
let write_counts = [3];
let write_data = Bytes::from(vec![0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC]);
let mut mbfb =
ModbusFeedbackFrame::new_write_frame(&write_addresses, &write_counts, write_data);
let res = mbfb.to_bytes_mut();
match res {
Err(e) => {
assert_eq!(e.to_string(), "There should never be a writeable tag with a register count not equal to 1 or 2.")
}
Ok(_) => {
panic!("Expected to receive an error result from to_bytes_mut!")
}
};
}
}