use alloc::vec::Vec;
use binrw::{BinRead, BinWrite, io::Cursor};
use bon::bon;
use crate::{protocol, protocol::r#struct::Writable};
#[must_use]
#[derive(Clone, Debug, BinWrite)]
#[bw(big)]
pub struct Args {
starting_address: u16,
n_coils: u16,
n_bytes: u8,
coil_bytes: Vec<u8>,
}
#[bon]
impl Args {
#[builder]
pub fn new<S: Writable>(
starting_address: u16,
n_coils: u16,
coils: S,
) -> Result<Self, protocol::Error> {
if (1..=0x07B0).contains(&n_coils) {
let n_bytes = u8::try_from(n_coils.div_ceil(8)).unwrap();
let coil_bytes = {
let mut buffer = Cursor::new(Vec::new());
coils.write_be(&mut buffer)?;
buffer.into_inner()
};
if coil_bytes.len() == n_bytes.into() {
Ok(Self { starting_address, n_coils, n_bytes, coil_bytes })
} else {
Err(protocol::Error::CoilNumberMismatch {
n_expected_bytes: n_bytes.into(),
n_actual_bytes: coil_bytes.len(),
})
}
} else {
Err(protocol::Error::InvalidCount(n_coils.into()))
}
}
}
#[must_use]
#[derive(Copy, Clone, derive_more::Debug, BinRead)]
#[br(big)]
pub struct Output {
pub starting_address: u16,
pub n_coils: u16,
}
#[cfg(test)]
mod tests {
use alloc::vec;
use binrw::{BinRead, io::Cursor};
use modular_bitfield::prelude::*;
use super::*;
#[bitfield]
#[derive(Copy, Clone, BinWrite)]
#[bw(map = |&it| Self::into_bytes(it))]
struct PackedData {
#[allow(dead_code)]
status_1: B8,
#[allow(dead_code)]
status_2: B2,
#[skip]
__: B6,
}
#[test]
fn request_example_ok() {
const EXPECTED: &[u8] = &[
0x00, 0x13, 0x00, 0x0A, 0x02, 0xCD, 0x01, ];
let mut output = Cursor::new(vec![]);
Args::builder()
.starting_address(19)
.n_coils(10)
.coils(PackedData::new().with_status_1(0xCD).with_status_2(1))
.build()
.unwrap()
.write(&mut output)
.unwrap();
assert_eq!(output.into_inner(), EXPECTED);
}
#[test]
fn response_example_ok() {
const RESPONSE: &[u8] = &[
0x00, 0x13, 0x00, 0x0A, ];
let response = Output::read(&mut Cursor::new(RESPONSE)).unwrap();
assert_eq!(response.starting_address, 19);
assert_eq!(response.n_coils, 10);
}
}