Skip to main content

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}