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}