mbus_client/services/fifo_queue/mod.rs
1//! # Modbus FIFO Queue Service
2//!
3//! This module implements the client-side logic for **Function Code 24 (0x18): Read FIFO Queue**.
4//!
5//! The Read FIFO Queue function allows the client to read the contents of a First-In-First-Out (FIFO)
6//! queue of 16-bit registers in a remote device. The function returns a count of the registers
7//! in the queue, followed by the queue data.
8//!
9//! ## Module Structure
10//! - `apis`: High-level public API for triggering FIFO queue reads via `ClientServices`.
11//! - `request`: Handles the construction and serialization of the Read FIFO Queue request PDU.
12//! - `response`: Handles parsing, validation of byte counts, and register extraction from response PDUs.
13//! - `service`: Internal orchestration logic for building ADUs and handling de-encapsulation.
14//!
15//! ## Constraints
16//! - **Max Capacity**: The Modbus specification limits the FIFO queue to a maximum of 31 registers.
17//! - **Read-Only**: This function code is specifically for reading; writing to FIFOs is typically
18//! handled via standard register write functions (FC 06 or 16) depending on the server implementation.
19//! - **no_std**: Fully compatible with embedded environments using fixed-size buffers.
20
21pub mod request;
22pub mod response;
23
24pub use mbus_core::models::fifo_queue::*;
25
26mod apis;
27mod service;
28
29#[cfg(test)]
30mod tests {
31 use heapless::Vec;
32
33 use crate::services::fifo_queue::{request::ReqPduCompiler, response::ResponseParser};
34 use mbus_core::{
35 data_unit::common::Pdu, errors::MbusError, function_codes::public::FunctionCode,
36 };
37
38 // --- Read FIFO Queue (FC 0x18) ---
39
40 /// Test case: `read_fifo_queue_request` with valid data.
41 #[test]
42 fn test_read_fifo_queue_request_valid() {
43 let pdu = ReqPduCompiler::read_fifo_queue_request(0x0001).unwrap();
44 assert_eq!(pdu.function_code(), FunctionCode::ReadFifoQueue);
45 assert_eq!(pdu.data().as_slice(), &[0x00, 0x01]);
46 assert_eq!(pdu.data_len(), 2);
47 }
48
49 // --- Parse Read FIFO Queue Response Tests ---
50
51 /// Test case: `parse_read_fifo_queue_response` successfully parses a valid response with data.
52 #[test]
53 fn test_parse_read_fifo_queue_response_valid() {
54 // Response: FC(0x18), FIFO Byte Count(0x0004), FIFO Count(0x0001), FIFO Value(0x1234)
55 // PDU data: [0x00, 0x04, 0x00, 0x01, 0x12, 0x34]
56 let response_bytes = [0x18, 0x00, 0x04, 0x00, 0x01, 0x12, 0x34];
57 let pdu = Pdu::from_bytes(&response_bytes).unwrap();
58 let (values, count) = ResponseParser::parse_read_fifo_queue_response(&pdu).unwrap();
59 assert_eq!(&values[..count], &[0x1234]);
60 }
61
62 /// Test case: `parse_read_fifo_queue_response` successfully parses a valid response with multiple registers.
63 #[test]
64 fn test_parse_read_fifo_queue_response_multiple_registers() {
65 // Response: FC(0x18), FIFO Byte Count(0x0006), FIFO Count(0x0002), FIFO Value(0x1234, 0x5678)
66 // PDU data: [0x00, 0x06, 0x00, 0x02, 0x12, 0x34, 0x56, 0x78]
67 let response_bytes = [0x18, 0x00, 0x06, 0x00, 0x02, 0x12, 0x34, 0x56, 0x78];
68 let pdu = Pdu::from_bytes(&response_bytes).unwrap();
69 let (values, count) = ResponseParser::parse_read_fifo_queue_response(&pdu).unwrap();
70 assert_eq!(&values[..count], &[0x1234, 0x5678]);
71 }
72
73 /// Test case: `parse_read_fifo_queue_response` returns an error for wrong function code.
74 #[test]
75 fn test_parse_read_fifo_queue_response_wrong_fc() {
76 let response_bytes = [0x03, 0x00, 0x04, 0x00, 0x01, 0x12, 0x34]; // Wrong FC
77 let pdu = Pdu::from_bytes(&response_bytes).unwrap();
78 assert_eq!(
79 ResponseParser::parse_read_fifo_queue_response(&pdu).unwrap_err(),
80 MbusError::InvalidFunctionCode
81 );
82 }
83
84 /// Test case: `parse_read_fifo_queue_response` returns an error for PDU data too short.
85 #[test]
86 fn test_parse_read_fifo_queue_response_data_too_short() {
87 let response_bytes = [0x18, 0x00, 0x04, 0x00]; // Missing FIFO Count and values
88 let pdu = Pdu::from_bytes(&response_bytes).unwrap();
89 assert_eq!(
90 ResponseParser::parse_read_fifo_queue_response(&pdu).unwrap_err(),
91 MbusError::InvalidPduLength
92 );
93 }
94
95 /// Test case: `parse_read_fifo_queue_response` returns an error for FIFO byte count mismatch.
96 #[test]
97 fn test_parse_read_fifo_queue_response_fifo_byte_count_mismatch() {
98 // Total PDU data length is 7. Byte count is 5. 7 = 2 + 5. Length is correct.
99 // FIFO count is 1. Byte count should be 2 + 1*2 = 4. But it is 5. This is a ParseError.
100 let response_bytes = [0x18, 0x00, 0x05, 0x00, 0x01, 0x12, 0x34, 0x00];
101 let pdu = Pdu::from_bytes(&response_bytes).unwrap();
102 assert_eq!(
103 ResponseParser::parse_read_fifo_queue_response(&pdu).unwrap_err(),
104 MbusError::ParseError
105 );
106 }
107
108 /// Test case: `parse_read_fifo_queue_response` returns an error for FIFO count mismatch.
109 #[test]
110 fn test_parse_read_fifo_queue_response_fifo_count_mismatch() {
111 let response_bytes = [0x18, 0x00, 0x04, 0x00, 0x02, 0x12, 0x34]; // FIFO Count 2, but only 1 register value
112 let pdu = Pdu::from_bytes(&response_bytes).unwrap();
113 assert_eq!(
114 ResponseParser::parse_read_fifo_queue_response(&pdu).unwrap_err(),
115 MbusError::ParseError
116 );
117 }
118
119 /// Test case: `parse_read_fifo_queue_response` returns an error if the internal `Vec` capacity is exceeded.
120 #[test]
121 fn test_parse_read_fifo_queue_response_buffer_too_small_for_data() {
122 // This test checks that creating a PDU with more data than the spec allows will fail.
123 // A Read FIFO Queue response PDU's data section is:
124 // [FIFO Byte Count (2 bytes)] [FIFO Count (2 bytes)] [Values (N*2 bytes)]
125 // The total data length is `4 + N*2`. This must be <= MAX_PDU_DATA_LEN (252).
126 // This implies max N is 124.
127 //
128 // This test attempts to create a PDU with N=126, which would mean a data length of
129 // 4 + 126*2 = 256 bytes, which is > 252. `Pdu::from_bytes` should reject this.
130 let fifo_count = 126;
131 let fifo_byte_count = 2 + (fifo_count * 2); // 2 + 252 = 254
132
133 // Use a Vec for test data setup.
134 let mut response_pdu_bytes: Vec<u8, 512> = Vec::new();
135 for _ in 0..(1 + 2 + 2 + fifo_count * 2) {
136 response_pdu_bytes.push(0u8).unwrap();
137 }
138 response_pdu_bytes[0] = 0x18; // FC
139 response_pdu_bytes[1..3].copy_from_slice(&(fifo_byte_count as u16).to_be_bytes());
140 response_pdu_bytes[3..5].copy_from_slice(&(fifo_count as u16).to_be_bytes());
141
142 // The data part of the PDU (len 256) is > MAX_PDU_DATA_LEN (252).
143 let result = Pdu::from_bytes(&response_pdu_bytes);
144 assert_eq!(result.unwrap_err(), MbusError::InvalidPduLength);
145 }
146}