mbus_core/models/fifo_queue/model.rs
1//! # Modbus FIFO Queue Models
2//!
3//! This module defines the data structures for handling **Read FIFO Queue** (Function Code 0x18).
4//!
5//! In Modbus, a FIFO (First-In-First-Out) queue is a specialized structure where a set of
6//! registers can be read from a single pointer address. When the client reads the FIFO
7//! pointer, the server returns the current count of registers in the queue followed by
8//! the register data itself.
9//!
10//! ## Key Components
11//! - [`FifoQueue`]: A container for the registers retrieved from the FIFO.
12//! - [`MAX_FIFO_QUEUE_COUNT_PER_PDU`]: The protocol limit for registers in one FIFO response.
13//!
14//! ## Protocol Limits
15//! According to the Modbus specification, the FIFO count can range from 0 to 31 registers.
16//! The response PDU includes a 2-byte byte count, a 2-byte FIFO count, and then the
17//! register data (up to 62 bytes).
18
19/// The maximum number of 16-bit registers that can be returned in a single Read FIFO Queue (FC 24) response.
20///
21/// The Modbus specification limits the FIFO count to 31 registers (62 bytes of data).
22pub const MAX_FIFO_QUEUE_COUNT_PER_PDU: usize = 31;
23
24/// A collection of register values retrieved from a Modbus FIFO queue.
25///
26/// This structure maintains the pointer address used for the request and stores the
27/// resulting register values in a fixed-size array, making it suitable for `no_std`
28/// and memory-constrained environments.
29///
30/// # Internal Representation
31/// The `queue` field is a fixed-size array (`[u16; MAX_FIFO_QUEUE_COUNT_PER_PDU]`)
32/// that stores the 16-bit register values. The `length` field tracks the actual
33/// number of valid registers currently present in the `queue`, allowing the struct
34/// to manage a variable number of registers within its fixed capacity.
35///
36/// # Examples
37///
38/// ```rust
39/// use mbus_core::models::fifo_queue::FifoQueue;
40/// use mbus_core::models::fifo_queue::MAX_FIFO_QUEUE_COUNT_PER_PDU;
41///
42/// // 1. Create a new FifoQueue instance for pointer address 0x1000.
43/// // Initially, it's empty.
44/// let mut fifo = FifoQueue::new(0x1000);
45/// assert_eq!(fifo.ptr_address(), 0x1000);
46/// assert_eq!(fifo.length(), 0);
47/// assert!(fifo.queue().is_empty());
48///
49/// // 2. Simulate receiving a Modbus response with FIFO data.
50/// // Let's say we read 3 registers: 0x1111, 0x2222, 0x3333.
51/// // The `values` array would typically come from parsing the PDU.
52/// let mut received_values = [0; MAX_FIFO_QUEUE_COUNT_PER_PDU];
53/// received_values[0] = 0x1111;
54/// received_values[1] = 0x2222;
55/// received_values[2] = 0x3333;
56/// let received_length = 3;
57///
58/// // 3. Populate the FifoQueue with the received data using `with_values`.
59/// fifo = fifo.with_values(received_values, received_length);
60///
61/// // 4. Verify the contents and properties of the FIFO queue.
62/// assert_eq!(fifo.length(), 3);
63/// assert_eq!(fifo.queue()[..3], [0x1111, 0x2222, 0x3333]);
64/// assert_eq!(fifo.queue()[..1], [0x1111]);
65/// assert_eq!(fifo.queue()[1..2], [0x2222]);
66/// assert_eq!(fifo.queue()[2..3], [0x3333]);
67/// ```
68#[derive(Debug, Clone)]
69pub struct FifoQueue {
70 /// The Modbus address of the FIFO pointer.
71 ptr_address: u16,
72 /// The register values read from the FIFO, stored as 16-bit unsigned integers.
73 queue: [u16; MAX_FIFO_QUEUE_COUNT_PER_PDU],
74 /// The actual number of valid registers currently stored in the `values` array.
75 length: usize,
76}
77
78impl FifoQueue {
79 /// Creates a new `FifoQueue` instance.
80 ///
81 /// # What happens:
82 /// A new `FifoQueue` is created with the specified `ptr_address`.
83 /// The internal `queue` array is initialized to all zeros, and the `length`
84 /// is set to 0, indicating an empty queue.
85 ///
86 /// # Arguments
87 /// * `ptr_address` - The Modbus address of the FIFO pointer that was queried.
88 pub fn new(ptr_address: u16) -> Self {
89 Self {
90 ptr_address,
91 // Initialize with zeros; the actual data will be loaded via `with_values`
92 queue: [0; MAX_FIFO_QUEUE_COUNT_PER_PDU],
93 length: 0,
94 }
95 }
96
97 /// Returns the Modbus address of the FIFO pointer.
98 ///
99 /// This is the address that was provided in the original Read FIFO Queue request.
100 ///
101 /// # Returns
102 /// The `u16` Modbus address of the FIFO pointer.
103 pub fn ptr_address(&self) -> u16 {
104 self.ptr_address
105 }
106
107 /// Returns a reference to the active values in the FIFO queue.
108 ///
109 /// This method provides a slice `&[u16]` containing only the `length`
110 /// valid registers, effectively hiding the unused capacity of the
111 /// internal fixed-size array.
112 ///
113 /// # Returns
114 /// A slice `&[u16]` of the register values.
115 pub fn queue(&self) -> &[u16] {
116 &self.queue[..self.length]
117 }
118
119 /// Returns the number of registers currently held in this FIFO response.
120 ///
121 /// # Returns
122 /// The `usize` number of registers in the queue.
123 pub fn length(&self) -> usize {
124 self.length
125 }
126
127 /// Loads the register values into the FIFO model and sets the active length.
128 ///
129 /// This method is typically used by the service layer after parsing a Modbus response PDU.
130 ///
131 /// # Arguments
132 /// * `values` - A fixed-size array containing the 16-bit register values.
133 /// This array should have a length of `MAX_FIFO_QUEUE_COUNT_PER_PDU`.
134 /// * `length` - The number of registers to be considered active.
135 ///
136 /// # What happens:
137 /// 1. The entire `values` array is copied into the internal `self.queue`.
138 /// 2. The `length` is set, but it is clamped to `MAX_FIFO_QUEUE_COUNT_PER_PDU`
139 /// to prevent exceeding the buffer's capacity.
140 ///
141 /// # Returns
142 /// The updated `FifoQueue` instance.
143 pub fn with_values(
144 mut self,
145 values: [u16; MAX_FIFO_QUEUE_COUNT_PER_PDU],
146 length: usize,
147 ) -> Self {
148 self.queue = values;
149 self.length = core::cmp::min(length, MAX_FIFO_QUEUE_COUNT_PER_PDU);
150 self
151 }
152}