modbus_rtu/slave/data_model/
mod.rs

1mod structure;      pub use structure::DataStructure;
2
3
4/// A data model for accessing values stored at unique 16-bit addresses.
5/// 
6/// ---
7/// # Examples
8/// ```
9/// use modbus_rtu::slave::{DataModel, DataStructure};
10/// 
11/// // Define data structure first.
12/// const DATA_STRUCTURE: DataStructure<4> = DataStructure::new([
13///     0x0001,
14///     0x0002,
15///     0x1234,
16///     0x5678,
17/// ]);
18/// 
19/// // And create a new data model instance with initial value array.
20/// let data_model = DataModel::new(&DATA_STRUCTURE, [0; 4]);
21/// ```
22/// 
23#[derive(Debug)]
24pub struct DataModel<const L: usize, T: Copy> {
25    /// The data structure defined as a constant at compile time.
26    /// The data model conforms to this structure.
27    /// 
28    structure: &'static DataStructure<L>,
29
30    /// The list of values being stored.
31    /// It is stored in the same form as the data structure.
32    values: [T; L],
33}
34
35
36impl<const L: usize, T: Copy> DataModel<L, T> {
37    /// Creates and initializes a new data model using the given data structure.
38    ///
39    /// ---
40    /// # Arguments
41    /// - `structure`: A reference to the constant data structure.
42    /// - `initial_values`: The initial values to populate the data model.
43    ///
44    /// ---
45    /// # Returns
46    /// A new `DataModel` instance initialized with the provided structure and values.
47    /// 
48    /// ---
49    /// # Examples
50    /// ```
51    /// use modbus_rtu::slave::{DataModel, DataStructure};
52    /// 
53    /// const STRUCTURE: DataStructure<5> = DataStructure::new([
54    ///     0x0000,
55    ///     0x0001,
56    ///     0x0002,
57    ///     0x1234,
58    ///     0x5678,
59    /// ]);
60    /// 
61    /// let data_model = DataModel::new(&STRUCTURE, [0; 5]);
62    /// ```
63    /// 
64    pub fn new(structure: &'static DataStructure<L>, initial_values: [T; L]) -> DataModel<L, T> {
65        Self { structure, values: initial_values }
66    }
67
68    /// Retrieves a value from the data model by using an address defined in the associated data structure.
69    ///
70    /// This method uses the address defined in the data structure to access the corresponding value
71    /// stored within the data model.
72    /// 
73    /// ---
74    /// # Arguments
75    /// - `address`: The address of the value to retrieve. Only addresses defined in the data structure are allowed.
76    /// 
77    /// ---
78    /// # Returns
79    /// The value stored at the specified address.
80    /// 
81    /// ---
82    /// # Examples
83    /// ```
84    /// use modbus_rtu::slave::{DataModel, DataStructure};
85    /// 
86    /// const STRUCTURE: DataStructure<5> = DataStructure::new([
87    ///     0x0000,
88    ///     0x0001,
89    ///     0x0002,
90    ///     0x1234,
91    ///     0x5678,
92    /// ]);
93    /// 
94    /// let data_model = DataModel::new(&STRUCTURE, [0; 5]);
95    /// 
96    /// assert_eq!(data_model.get_value(0x0001), 0);
97    /// ```
98    /// 
99    /// The code below will panic at compile time.
100    /// ```should_panic
101    /// # use modbus_rtu::slave::{DataModel, DataStructure};
102    /// # const STRUCTURE: DataStructure<5> = DataStructure::new([
103    /// #     0x0000,
104    /// #     0x0001,
105    /// #     0x0002,
106    /// #     0x1234,
107    /// #     0x5678,
108    /// # ]);
109    /// # let data_model = DataModel::new(&STRUCTURE, [0; 5]);
110    /// // Will panic!!
111    /// let value = data_model.get_value(0x0003);
112    /// ```
113    /// 
114    /// ---
115    /// # Panics
116    /// ...
117    /// 
118    pub const fn get_value(&self, address: u16) -> T {
119        let index = self.structure.get(address);
120        self.values[index]
121    }
122
123    /// Retrieves a value from the data model using a given address, if it exists in the structure.
124    ///
125    /// Unlike `get_value`, this method returns `None` instead of panicking if the address is not part of the structure.
126    /// This makes it suitable for use with dynamic or external input.
127    ///
128    /// ---
129    /// # Arguments
130    /// - `address`: The address of the value to look up. The address may or may not be defined in the structure.
131    ///
132    /// ---
133    /// # Returns
134    /// An `Option<T>` containing the value if the address is found, or `None` if it is not.
135    ///
136    /// ---
137    /// # Examples
138    /// ```
139    /// use modbus_rtu::slave::{DataModel, DataStructure};
140    ///
141    /// const STRUCTURE: DataStructure<5> = DataStructure::new([
142    ///     0x0000,
143    ///     0x0001,
144    ///     0x0002,
145    ///     0x1234,
146    ///     0x5678,
147    /// ]);
148    ///
149    /// let data_model = DataModel::new(&STRUCTURE, [10, 20, 30, 40, 50]);
150    ///
151    /// assert_eq!(data_model.find_value(0x0002), Some(30));
152    /// assert_eq!(data_model.find_value(0x9999), None);
153    /// ```
154    /// 
155    pub fn find_value(&self, address: u16) -> Option<T> {
156        let index = self.structure.find(address)?;
157        Some(self.values[index])
158    }
159
160    /// Sets a value in the data model at the specified index.
161    ///
162    /// This function assumes the index is already validated and corresponds to a valid entry in the model.
163    ///
164    /// ---
165    /// # Args
166    /// - `index`: The index at which to set the value. This must be valid, as obtained from `get_index`.
167    /// - `value`: The value to be set at the specified index.
168    ///
169    /// ---
170    /// # Examples
171    /// ```
172    /// # use modbus_rtu::slave::{DataModel, DataStructure};
173    /// # 
174    /// # const DATA_STRUCTURE: DataStructure<4> = DataStructure::new([
175    /// #     0x0000,
176    /// #     0x0001,
177    /// #     0x1234,
178    /// #     0x5678,
179    /// # ]);
180    /// # 
181    /// # let mut data_model = DataModel::new(&DATA_STRUCTURE, [0; 4]);
182    /// # 
183    /// // This is find.
184    /// let i = data_model.get_index(0x0000);
185    /// data_model.set_value(i, 5);
186    /// 
187    /// // This is fine too.
188    /// if let Some(i) = data_model.find_index(0x0008) {
189    ///     data_model.set_value(i, 5);
190    /// }
191    /// 
192    /// // But this might panic.
193    /// data_model.set_value(3, 5);
194    /// ```
195    /// 
196    /// ---
197    /// # Panics
198    /// Panics if the index is out of bounds.
199    ///
200    pub fn set_value(&mut self, index: usize, value: T) {
201        self.values[index] = value;
202    }
203
204    /// Retrieves the internal index for a given address defined in the data structure.
205    ///
206    /// This is useful when you want to perform index-based operations on the values array.
207    ///
208    /// ---
209    /// # Args
210    /// - `address`: The address whose index should be retrieved. Must be present in the structure.
211    ///
212    /// ---
213    /// # Returns
214    /// The index corresponding to the given address.
215    ///
216    /// ---
217    /// # Panics
218    /// Panics at compile time if the address is not found in the structure.
219    ///
220    pub const fn get_index(&self, address: u16) -> usize {
221        self.structure.get(address)
222    }
223
224    /// Attempts to find the index for a given address, if it exists in the data structure.
225    ///
226    /// This method is safe to use with dynamic or externally provided addresses.
227    ///
228    /// ---
229    /// # Args
230    /// - `address`: The address to search for.
231    ///
232    /// ---
233    /// # Returns
234    /// `Some(index)` if the address is present, or `None` otherwise.
235    ///
236    pub fn find_index(&self, address: u16) -> Option<usize> {
237        self.structure.find(address)
238    }
239
240    /// Checks whether the data model is empty.
241    ///
242    /// This returns `true` if the data model contains no entries, which occurs when its length `L` is zero.
243    ///
244    /// ---
245    /// # Returns
246    /// `true` if the data model is empty, `false` otherwise.
247    ///
248    /// ---
249    /// # Examples
250    /// ```
251    /// use modbus_rtu::slave::DataModel;
252    ///
253    /// let empty_model = DataModel::<0, u16>::empty();
254    /// assert!(empty_model.is_empty());
255    /// ```
256    ///
257    pub fn is_empty(&self) -> bool {
258        L == 0
259    }
260}
261
262
263impl<T: Copy> DataModel<0, T> {
264    /// Creates and returns an empty data model with no stored values.
265    ///
266    /// This is useful when a data model is required but no data is needed or used.
267    ///
268    /// ---
269    /// # Returns
270    /// An empty `DataModel` instance with no associated data.
271    ///
272    /// ---
273    /// # Examples
274    /// ```
275    /// use modbus_rtu::slave::{ModbusSlave, DataModel};
276    ///
277    /// let holding_registers = DataModel::empty();
278    /// let input_registers = DataModel::empty();
279    /// 
280    /// // Create modbus slave instance with zero registers
281    /// let modbus_slave = ModbusSlave::new(0x01, holding_registers, input_registers);
282    /// ```
283    ///
284    pub fn empty() -> DataModel<0, T> {
285        DataModel { structure: &DataStructure::EMPTY, values: [] }
286    }
287}