pub mod request;
pub mod response;
pub use mbus_core::models::file_record::*;
mod apis;
mod service;
#[cfg(test)]
mod tests {
use heapless::Vec;
use crate::services::file_record::{
SubRequest, request::ReqPduCompiler, response::ResponseParser, service::ServiceBuilder,
service::ServiceDecompiler,
};
use mbus_core::{
data_unit::common::Pdu, errors::MbusError, function_codes::public::FunctionCode,
transport::TransportType,
};
#[test]
fn test_read_file_record_request_valid() {
let mut sub_req = SubRequest::new();
sub_req.add_read_sub_request(4, 1, 2).unwrap();
let pdu = ReqPduCompiler::read_file_record_request(&sub_req).unwrap();
assert_eq!(pdu.function_code(), FunctionCode::ReadFileRecord);
let expected_data = [0x07, 0x06, 0x00, 0x04, 0x00, 0x01, 0x00, 0x02];
assert_eq!(pdu.data().as_slice(), &expected_data);
}
#[test]
fn test_read_file_record_request_multiple_sub_requests() {
let mut sub_req = SubRequest::new();
sub_req.add_read_sub_request(4, 1, 2).unwrap(); sub_req.add_read_sub_request(5, 10, 1).unwrap();
let pdu = ReqPduCompiler::read_file_record_request(&sub_req).unwrap();
let expected_data = [
0x0E, 0x06, 0x00, 0x04, 0x00, 0x01, 0x00, 0x02, 0x06, 0x00, 0x05, 0x00, 0x0A, 0x00, 0x01, ];
assert_eq!(pdu.data().as_slice(), &expected_data);
}
#[test]
fn test_read_file_record_overflow_protection() {
let mut sub_req = SubRequest::new();
sub_req.add_read_sub_request(1, 0, 124).unwrap();
let err = sub_req.add_read_sub_request(1, 124, 1).unwrap_err();
assert_eq!(err, MbusError::FileReadPduOverflow);
}
#[test]
fn test_parse_read_file_record_response_valid() {
let data = [0x06, 0x05, 0x06, 0x12, 0x34, 0x56, 0x78];
let mut pdu_data = Vec::new();
pdu_data.extend_from_slice(&data).unwrap();
let pdu = Pdu::new(FunctionCode::ReadFileRecord, pdu_data, data.len() as u8);
let sub_reqs = ResponseParser::parse_read_file_record_response(&pdu).unwrap();
assert_eq!(sub_reqs.len(), 1);
let data = sub_reqs[0].record_data.as_ref().unwrap();
assert_eq!(data.as_slice(), &[0x1234, 0x5678]);
assert_eq!(sub_reqs[0].record_length, 2);
}
#[test]
fn test_parse_read_file_record_response_malformed() {
let data = [0x06, 0x05, 0x07, 0x12, 0x34, 0x56, 0x78];
let mut pdu_data = Vec::new();
pdu_data.extend_from_slice(&data).unwrap();
let pdu = Pdu::new(FunctionCode::ReadFileRecord, pdu_data, data.len() as u8);
let err = ResponseParser::parse_read_file_record_response(&pdu).unwrap_err();
assert_eq!(err, MbusError::ParseError);
}
#[test]
fn test_write_file_record_request_valid() {
let mut sub_req = SubRequest::new();
let mut data = Vec::new();
data.push(0x1234).unwrap();
data.push(0x5678).unwrap();
sub_req
.add_write_sub_request(4, 1, 2, data.clone())
.unwrap();
let pdu = ReqPduCompiler::write_file_record_request(&sub_req).unwrap();
assert_eq!(pdu.function_code(), FunctionCode::WriteFileRecord);
let expected_data = [
0x0B, 0x06, 0x00, 0x04, 0x00, 0x01, 0x00, 0x02, 0x12, 0x34, 0x56, 0x78,
];
assert_eq!(pdu.data().as_slice(), &expected_data);
}
#[test]
fn test_write_file_record_request_data_len_mismatch() {
let mut sub_req = SubRequest::new();
let mut data = Vec::new();
data.push(0x1234).unwrap();
let err = sub_req.add_write_sub_request(4, 1, 2, data).unwrap_err();
assert_eq!(err, MbusError::BufferLenMissmatch);
}
#[test]
fn test_write_file_record_overflow_protection() {
let mut sub_req = SubRequest::new();
let mut data = Vec::new();
for _ in 0..122 {
data.push(0xFFFF).unwrap();
}
sub_req
.add_write_sub_request(1, 1, 122, data.clone())
.unwrap();
let mut small_data = Vec::new();
small_data.push(0x0000).unwrap();
let err = sub_req
.add_write_sub_request(2, 2, 1, small_data)
.unwrap_err();
assert_eq!(err, MbusError::FileReadPduOverflow);
}
#[test]
fn test_parse_write_file_record_response_valid() {
let data = [
0x0B, 0x06, 0x00, 0x04, 0x00, 0x01, 0x00, 0x02, 0x12, 0x34, 0x56, 0x78,
];
let mut pdu_data = Vec::new();
pdu_data.extend_from_slice(&data).unwrap();
let pdu = Pdu::new(FunctionCode::WriteFileRecord, pdu_data, data.len() as u8);
let result = ResponseParser::parse_write_file_record_response(&pdu);
assert!(result.is_ok());
}
#[test]
fn test_parse_write_file_record_response_invalid_structure() {
let data = [0x0B, 0x06, 0x00, 0x04, 0x00, 0x01, 0x00, 0x02];
let mut pdu_data = Vec::new();
pdu_data.extend_from_slice(&data).unwrap();
let pdu = Pdu::new(FunctionCode::WriteFileRecord, pdu_data, data.len() as u8);
let err = ResponseParser::parse_write_file_record_response(&pdu).unwrap_err();
assert_eq!(err, MbusError::InvalidPduLength);
}
#[test]
fn test_file_record_service_read_request_tcp() {
let mut sub_req = SubRequest::new();
sub_req.add_read_sub_request(4, 1, 2).unwrap();
let txn_id = 0x1234;
let unit_id = 0x01;
let adu =
ServiceBuilder::read_file_record(txn_id, unit_id, &sub_req, TransportType::StdTcp)
.unwrap();
let expected = [
0x12, 0x34, 0x00, 0x00, 0x00, 0x0A, 0x01, 0x14, 0x07, 0x06, 0x00, 0x04, 0x00, 0x01,
0x00, 0x02,
];
assert_eq!(adu.as_slice(), &expected);
}
#[test]
fn test_file_record_service_write_request_tcp() {
let mut sub_req = SubRequest::new();
let mut data = Vec::new();
data.push(0x1122).unwrap();
sub_req.add_write_sub_request(4, 1, 1, data).unwrap();
let txn_id = 0x5678;
let unit_id = 0x02;
let adu =
ServiceBuilder::write_file_record(txn_id, unit_id, &sub_req, TransportType::StdTcp)
.unwrap();
let expected = [
0x56, 0x78, 0x00, 0x00, 0x00, 0x0C, 0x02, 0x15, 0x09, 0x06, 0x00, 0x04, 0x00, 0x01,
0x00, 0x01, 0x11, 0x22,
];
assert_eq!(adu.as_slice(), &expected);
}
#[test]
fn test_file_record_service_handle_read_response() {
let data = [0x04, 0x03, 0x06, 0xAA, 0xBB];
let mut pdu_data = Vec::new();
pdu_data.extend_from_slice(&data).unwrap();
let pdu = Pdu::new(FunctionCode::ReadFileRecord, pdu_data, 5);
let result =
ServiceDecompiler::handle_read_file_record_rsp(FunctionCode::ReadFileRecord, &pdu);
assert!(result.is_ok());
let sub_reqs = result.unwrap();
assert_eq!(sub_reqs.len(), 1);
assert_eq!(
sub_reqs[0].record_data.as_ref().unwrap().as_slice(),
&[0xAABB]
);
}
#[test]
fn test_file_record_service_handle_read_response_wrong_fc() {
let pdu = Pdu::new(FunctionCode::ReadCoils, Vec::new(), 0);
let result = ServiceDecompiler::handle_read_file_record_rsp(FunctionCode::ReadCoils, &pdu);
assert_eq!(result.unwrap_err(), MbusError::InvalidFunctionCode);
}
}