modbus_rtu/slave/data_model/
structure.rs

1/// Represents a statically defined set of Modbus RTU data addresses.
2///
3/// The addresses must be:
4/// - Known at compile time
5/// - Strictly increasing (ordered)
6/// - Unique
7///
8/// This structure is intended to be defined as a global constant/static value,
9/// and should not be instantiated dynamically.
10/// 
11/// ---
12/// # Examples
13/// ```
14/// use modbus_rtu::slave::DataStructure;
15/// 
16/// // Define data structure as const.
17/// const DATA_STRUCTURE: DataStructure<4> = DataStructure::new([
18///     0x0000,
19///     0x0001,
20///     0x1234,
21///     0x5678,
22/// ]);
23/// ```
24/// 
25#[derive(Debug)]
26pub struct DataStructure<const L: usize>([u16; L]);
27
28
29impl<const L: usize> DataStructure<L> {
30    /// Creates a new `DataStructure` after validating the input addresses.
31    ///
32    /// This function checks that the addresses are strictly ordered and unique at compile time.
33    /// It will panic if the addresses are not ordered or contain duplicates.
34    ///
35    /// ---
36    /// # Arguments
37    /// - `addresses`: The list of Modbus RTU register addresses to be stored in the structure.
38    ///
39    /// ---
40    /// # Returns
41    /// A new `DataStructure` instance if the validation passes.
42    /// 
43    /// ---
44    /// # Examples
45    /// ```
46    /// use modbus_rtu::slave::DataStructure;
47    /// 
48    /// const STRUCTURE: DataStructure<5> = DataStructure::new([
49    ///     0x0000,
50    ///     0x0001,
51    ///     0x0002,
52    ///     0x1234,
53    ///     0x5678,
54    /// ]);
55    /// ```
56    /// 
57    pub const fn new(addresses: [u16; L]) -> DataStructure<L> {
58        let _ = Self::validate(&addresses);
59        Self(addresses)
60    }
61
62    /// Get the index of the specified address using a manual binary search approach.
63    ///
64    /// This function performs a binary search on the statically defined address list.
65    /// It will panic if the address is not found in the list.
66    ///
67    /// ---
68    /// # Arguments
69    /// - `address`: The address to look for in the data structure.
70    ///
71    /// ---
72    /// # Returns
73    /// The index of the address in the list if it exists, or panics if not found.
74    /// 
75    /// ---
76    /// # Examples
77    /// ```
78    /// use modbus_rtu::slave::DataStructure;
79    /// 
80    /// const STRUCTURE: DataStructure<5> = DataStructure::new([
81    ///     0x0000,
82    ///     0x0001,
83    ///     0x0002,
84    ///     0x1234,
85    ///     0x5678,
86    /// ]);
87    /// 
88    /// assert_eq!(STRUCTURE.get(0x0001), 1);
89    /// ```
90    /// 
91    /// The code below will panic at complie time.
92    /// ```should_panic
93    /// # use modbus_rtu::slave::DataStructure;
94    /// # const STRUCTURE: DataStructure<5> = DataStructure::new([
95    /// #     0x0000,
96    /// #     0x0001,
97    /// #     0x0002,
98    /// #     0x1234,
99    /// #     0x5678,
100    /// # ]);
101    /// // Will panic!!
102    /// let index = STRUCTURE.get(0x0003);
103    /// ```
104    ///
105    /// ---
106    /// # Panics
107    /// This function will panic if the specified address is not found in the address list.
108    /// The panic message will indicate that the address is not registered and should be checked for validity.
109    /// 
110    #[inline(always)]
111    pub const fn get(&self, address: u16) -> usize {
112        let mut left = 0;
113        let mut right = self.0.len() - 1;
114        
115        while left <= right {
116            let mid = (left + right) / 2;
117            if self.0[mid] == address {
118                return mid;
119            } else if self.0[mid] < address {
120                left = mid + 1;
121            } else {
122                right = mid - 1;
123            }
124        }
125        
126        panic!("Address not found in the address list. Ensure the address is valid and registered.");
127    }
128
129    /// Finds the index of the specified address using the binary search algorithm.
130    ///
131    /// This function performs a binary search on the statically defined address list. 
132    /// It returns the index of the address if found.
133    ///
134    /// ---
135    /// # Arguments
136    /// - `address`: The address to search for in the data structure.
137    ///
138    /// ---
139    /// # Returns
140    /// `Some(index)` if the address exists in the list, or `None` if not found.
141    ///
142    /// ---
143    /// # Examples
144    /// ```
145    /// use modbus_rtu::slave::DataStructure;
146    /// 
147    /// const STRUCTURE: DataStructure<5> = DataStructure::new([
148    ///     0x0000,
149    ///     0x0001,
150    ///     0x0002,
151    ///     0x1234,
152    ///     0x5678,
153    /// ]);
154    /// 
155    /// assert_eq!(STRUCTURE.find(0x0001), Some(1));
156    /// assert_eq!(STRUCTURE.find(0x0003), None);
157    /// ```
158    /// 
159    pub fn find(&self, address: u16) -> Option<usize> {
160        self.0.binary_search(&address).ok()
161    }
162
163    /// Retrieves the address at the specified index.
164    ///
165    /// This function accesses the address stored at the given index in the data structure's array.
166    /// It is a simple getter function that does not perform any validation.
167    ///
168    /// ---
169    /// # Arguments
170    /// - `index`: The index of the address to retrieve. The number must be derived from the logical relationships defined within the data structure.
171    ///
172    /// ---
173    /// # Returns
174    /// The `u16` address located at the given index.
175    /// 
176    /// ---
177    /// # Examples
178    /// ```
179    /// use modbus_rtu::slave::DataStructure;
180    /// 
181    /// const STRUCTURE: DataStructure<5> = DataStructure::new([
182    ///     0x0000,
183    ///     0x0001,
184    ///     0x0002,
185    ///     0x1234,
186    ///     0x5678,
187    /// ]);
188    /// 
189    /// assert_eq!(STRUCTURE.get_address_by_index(3), 0x1234);
190    /// ```
191    /// 
192    /// The code below will panic at compile time.
193    /// ```should_panic
194    /// # use modbus_rtu::slave::DataStructure;
195    /// # const STRUCTURE: DataStructure<5> = DataStructure::new([
196    /// #     0x0000,
197    /// #     0x0001,
198    /// #     0x0002,
199    /// #     0x1234,
200    /// #     0x5678,
201    /// # ]);
202    /// // Will panic!!
203    /// let address: u16 = STRUCTURE.get_address_by_index(10);
204    /// ```
205    ///
206    /// ---
207    /// # Panics
208    /// This function will panic if the `index` is out of bounds for the address list.
209    /// The number must be derived from the logical relationships defined within the data structure.
210    ///
211    pub fn get_address_by_index(&self, index: usize) -> u16 {
212        self.0[index]
213    }
214
215    /// Returns the number of addresses stored in this data structure.
216    ///
217    /// This is equivalent to the compile-time constant length `L`.
218    /// 
219    /// ---
220    /// # Returns
221    /// Total count of valid(defined) addresses
222    /// 
223    /// ---
224    /// # Examples
225    /// ```
226    /// use modbus_rtu::slave::DataStructure;
227    /// 
228    /// const ADDRESSES: [u16; 4] = [
229    ///     0x0000,
230    ///     0x0001,
231    ///     0x1234,
232    ///     0x5678,
233    /// ];
234    /// 
235    /// const DATA_STRUCTURE: DataStructure<4> = DataStructure::new(ADDRESSES);
236    /// 
237    /// assert_eq!(ADDRESSES.len(), DATA_STRUCTURE.len());
238    /// ```
239    /// 
240    pub const fn len(&self) -> usize {
241        L
242    }
243
244    /// Validates that the address list is strictly increasing and unique.
245    ///
246    /// ---
247    /// # Arguments
248    /// - `addresses`: The list of addresses to validate.
249    ///
250    /// ---
251    /// # Returns
252    /// `true` if the addresses are valid (ordered and unique), otherwise panics.
253    ///
254    /// ---
255    /// # Panics
256    /// This function will panic if the addresses are not strictly increasing or contain duplicates.
257    /// 
258    const fn validate(addresses: &[u16; L]) -> bool {
259        if addresses.len() < 2 {
260            return true;
261        }
262
263        let mut i = 0;
264        while i < addresses.len() - 1 {
265            if addresses[i] >= addresses[i + 1] {
266                panic!("Addresses for data structure must be ordered and unique. Found invalid order at index.");
267            }
268            i += 1;
269        }
270        true
271    }
272}
273
274
275impl DataStructure<0> {
276    /// An empty data structure.
277    ///
278    /// This is used to define a data model that does not contain any data.
279    ///
280    pub const EMPTY: DataStructure<0> = DataStructure::new([]);
281}