pub mod request;
pub mod response;
pub use mbus_core::models::coil::*;
mod apis;
mod service;
#[cfg(test)]
mod tests {
use heapless::Vec;
use mbus_core::models::coil::Coils;
use crate::services::coil::request::ReqPduCompiler;
use crate::services::coil::response::ResponseParser;
use crate::services::coil::{MAX_COIL_BYTES, MAX_COILS_PER_PDU};
use mbus_core::data_unit::common::Pdu;
use mbus_core::errors::MbusError;
use mbus_core::function_codes::public::FunctionCode;
#[test]
fn test_read_coils_request_valid() {
let address = 0x0001;
let quantity = 0x000A; let pdu = ReqPduCompiler::read_coils_request(address, quantity).unwrap();
assert_eq!(pdu.function_code(), FunctionCode::ReadCoils);
assert_eq!(pdu.data_len(), 4);
assert_eq!(pdu.data().as_slice(), &[0x00, 0x01, 0x00, 0x0A]);
}
#[test]
fn test_read_coils_request_invalid_quantity_low() {
let result = ReqPduCompiler::read_coils_request(0x0001, 0);
assert_eq!(result.unwrap_err(), MbusError::InvalidQuantity);
}
#[test]
fn test_read_coils_request_invalid_quantity_high() {
let result = ReqPduCompiler::read_coils_request(0x0001, 2001);
assert_eq!(result.unwrap_err(), MbusError::InvalidQuantity);
}
#[test]
fn test_parse_read_coils_response_valid_8_coils() {
let response_bytes = [0x01, 0x01, 0xB3];
let pdu = Pdu::from_bytes(&response_bytes).unwrap();
let coils_data = ResponseParser::parse_read_coils_response(&pdu, 8).unwrap();
assert_eq!(coils_data.as_slice(), &[0xB3]);
assert_eq!(coils_data.len(), 1); }
#[test]
fn test_parse_read_coils_response_valid_10_coils() {
let response_bytes = [0x01, 0x02, 0xB3, 0x03];
let pdu = Pdu::from_bytes(&response_bytes).unwrap();
let coils_data = ResponseParser::parse_read_coils_response(&pdu, 10).unwrap();
assert_eq!(coils_data.as_slice(), &[0xB3, 0x03]);
assert_eq!(coils_data.len(), 2); }
#[test]
fn test_parse_read_coils_response_valid_partial_last_byte() {
let response_bytes = [0x01, 0x01, 0x05];
let pdu = Pdu::from_bytes(&response_bytes).unwrap();
let coils_data = ResponseParser::parse_read_coils_response(&pdu, 3).unwrap();
assert_eq!(coils_data.as_slice(), &[0x05]);
assert_eq!(coils_data.len(), 1);
}
#[test]
fn test_parse_read_coils_response_wrong_fc() {
let response_bytes = [0x03, 0x01, 0xB3];
let pdu = Pdu::from_bytes(&response_bytes).unwrap();
let result = ResponseParser::parse_read_coils_response(&pdu, 8);
assert_eq!(result.unwrap_err(), MbusError::InvalidFunctionCode);
}
#[test]
fn test_parse_read_coils_response_empty_data() {
let pdu = Pdu::new(FunctionCode::ReadCoils, Vec::new(), 0);
let result = ResponseParser::parse_read_coils_response(&pdu, 8);
assert_eq!(result.unwrap_err(), MbusError::InvalidDataLen);
}
#[test]
fn test_parse_read_coils_response_byte_count_mismatch() {
let response_bytes = [0x01, 0x01, 0xB3, 0x00];
let pdu = Pdu::from_bytes(&response_bytes).unwrap();
let result = ResponseParser::parse_read_coils_response(&pdu, 8);
assert_eq!(result.unwrap_err(), MbusError::InvalidByteCount);
}
#[test]
fn test_parse_read_coils_response_expected_quantity_mismatch() {
let response_bytes = [0x01, 0x01, 0xB3];
let pdu = Pdu::from_bytes(&response_bytes).unwrap();
let result = ResponseParser::parse_read_coils_response(&pdu, 16);
assert_eq!(result.unwrap_err(), MbusError::InvalidQuantity);
}
#[test]
fn test_parse_read_coils_response_max_quantity() {
let max_quantity = MAX_COILS_PER_PDU as u16; let expected_byte_count = ((max_quantity + 7) / 8) as u8;
let mut response_bytes_vec: Vec<u8, 253> = Vec::new(); response_bytes_vec
.push(FunctionCode::ReadCoils as u8)
.unwrap();
response_bytes_vec.push(expected_byte_count).unwrap();
for i in 0..expected_byte_count as usize {
response_bytes_vec.push(i as u8).unwrap(); }
let pdu = Pdu::from_bytes(&response_bytes_vec).unwrap();
let coils_data = ResponseParser::parse_read_coils_response(&pdu, max_quantity).unwrap();
assert_eq!(coils_data.len(), expected_byte_count as usize);
assert_eq!(coils_data.as_slice(), &response_bytes_vec.as_slice()[2..]); }
#[test]
fn test_parse_read_coils_response_buffer_too_small_for_data() {
let expected_quantity = (MAX_COIL_BYTES * 8 + 1) as u16; let byte_count_in_pdu = ((expected_quantity + 7) / 8) as u8;
let mut response_bytes_vec: Vec<u8, 253> = Vec::new();
response_bytes_vec
.push(FunctionCode::ReadCoils as u8)
.unwrap();
response_bytes_vec.push(byte_count_in_pdu).unwrap(); for _i in 0..byte_count_in_pdu as usize {
response_bytes_vec.push(0x00).unwrap(); }
let pdu = Pdu::from_bytes(&response_bytes_vec).unwrap();
let result = ResponseParser::parse_read_coils_response(&pdu, expected_quantity);
assert_eq!(result.unwrap_err(), MbusError::BufferLenMissmatch);
}
#[test]
fn test_write_single_coil_request_on() {
let address = 0x0005;
let value = true;
let pdu = ReqPduCompiler::write_single_coil_request(address, value).unwrap();
assert_eq!(pdu.function_code(), FunctionCode::WriteSingleCoil);
assert_eq!(pdu.data_len(), 4);
assert_eq!(pdu.data().as_slice(), &[0x00, 0x05, 0xFF, 0x00]);
}
#[test]
fn test_write_single_coil_request_off() {
let address = 0x0005;
let value = false;
let pdu = ReqPduCompiler::write_single_coil_request(address, value).unwrap();
assert_eq!(pdu.function_code(), FunctionCode::WriteSingleCoil);
assert_eq!(pdu.data_len(), 4);
assert_eq!(pdu.data().as_slice(), &[0x00, 0x05, 0x00, 0x00]);
}
#[test]
fn test_parse_write_single_coil_response_valid() {
let response_bytes = [0x05, 0x00, 0x05, 0xFF, 0x00]; let pdu = Pdu::from_bytes(&response_bytes).unwrap();
let result = ResponseParser::parse_write_single_coil_response(&pdu, 0x0005, true);
assert!(result.is_ok());
}
#[test]
fn test_parse_write_single_coil_response_wrong_fc() {
let response_bytes = [0x03, 0x00, 0x05, 0xFF, 0x00]; let pdu = Pdu::from_bytes(&response_bytes).unwrap();
let result = ResponseParser::parse_write_single_coil_response(&pdu, 0x0005, true);
assert_eq!(result.unwrap_err(), MbusError::InvalidFunctionCode);
}
#[test]
fn test_parse_write_single_coil_response_address_mismatch() {
let response_bytes = [0x05, 0x00, 0x06, 0xFF, 0x00]; let pdu = Pdu::from_bytes(&response_bytes).unwrap();
let result = ResponseParser::parse_write_single_coil_response(&pdu, 0x0005, true);
assert_eq!(result.unwrap_err(), MbusError::InvalidAddress);
}
#[test]
fn test_parse_write_single_coil_response_value_mismatch() {
let response_bytes = [0x05, 0x00, 0x05, 0x00, 0x00]; let pdu = Pdu::from_bytes(&response_bytes).unwrap();
let result = ResponseParser::parse_write_single_coil_response(&pdu, 0x0005, true);
assert_eq!(result.unwrap_err(), MbusError::InvalidValue);
}
#[test]
fn test_parse_write_single_coil_response_invalid_len() {
let response_bytes = [0x05, 0x00, 0x05, 0xFF]; let pdu = Pdu::from_bytes(&response_bytes).unwrap();
let result = ResponseParser::parse_write_single_coil_response(&pdu, 0x0005, true);
assert_eq!(result.unwrap_err(), MbusError::InvalidDataLen);
}
#[test]
fn test_write_multiple_coils_request_valid() {
let address = 0x0001;
let quantity = 10;
let mut coils = Coils::new(address, quantity).unwrap();
for i in (0..quantity).step_by(2) {
coils.set_value(address + i, true).unwrap();
}
let pdu = ReqPduCompiler::write_multiple_coils_request(address, quantity, &coils).unwrap();
assert_eq!(pdu.function_code(), FunctionCode::WriteMultipleCoils);
assert_eq!(pdu.data_len(), 5 + 2); assert_eq!(
pdu.data().as_slice(),
&[0x00, 0x01, 0x00, 0x0A, 0x02, 0x55, 0x01]
);
}
#[test]
fn test_write_multiple_coils_request_invalid_quantity_low() {
let coils = Coils::new(0x0001, 1).unwrap();
let result = ReqPduCompiler::write_multiple_coils_request(0x0001, 0, &coils);
assert_eq!(result.unwrap_err(), MbusError::InvalidPduLength);
}
#[test]
fn test_write_multiple_coils_request_invalid_quantity_high() {
let coils = Coils::new(0x0001, 1968).unwrap();
let result = ReqPduCompiler::write_multiple_coils_request(0x0001, 1969, &coils);
assert_eq!(result.unwrap_err(), MbusError::InvalidPduLength);
}
#[test]
fn test_parse_write_multiple_coils_response_valid() {
let response_bytes = [0x0F, 0x00, 0x01, 0x00, 0x0A]; let pdu = Pdu::from_bytes(&response_bytes).unwrap();
let result = ResponseParser::parse_write_multiple_coils_response(&pdu, 0x0001, 10);
assert!(result.is_ok());
}
#[test]
fn test_parse_write_multiple_coils_response_wrong_fc() {
let response_bytes = [0x03, 0x00, 0x01, 0x00, 0x0A]; let pdu = Pdu::from_bytes(&response_bytes).unwrap();
let result = ResponseParser::parse_write_multiple_coils_response(&pdu, 0x0001, 10);
assert_eq!(result.unwrap_err(), MbusError::ParseError);
}
#[test]
fn test_parse_write_multiple_coils_response_address_mismatch() {
let response_bytes = [0x0F, 0x00, 0x02, 0x00, 0x0A]; let pdu = Pdu::from_bytes(&response_bytes).unwrap();
let result = ResponseParser::parse_write_multiple_coils_response(&pdu, 0x0001, 10);
assert_eq!(result.unwrap_err(), MbusError::InvalidAddress);
}
#[test]
fn test_parse_write_multiple_coils_response_quantity_mismatch() {
let response_bytes = [0x0F, 0x00, 0x01, 0x00, 0x0B]; let pdu = Pdu::from_bytes(&response_bytes).unwrap();
let result = ResponseParser::parse_write_multiple_coils_response(&pdu, 0x0001, 10);
assert_eq!(result.unwrap_err(), MbusError::InvalidQuantity);
}
#[test]
fn test_parse_write_multiple_coils_response_invalid_len() {
let response_bytes = [0x0F, 0x00, 0x01, 0x00]; let pdu = Pdu::from_bytes(&response_bytes).unwrap();
let result = ResponseParser::parse_write_multiple_coils_response(&pdu, 0x0001, 10);
assert_eq!(result.unwrap_err(), MbusError::InvalidDataLen);
}
}