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 /// Checks whether the data model is empty.
161 ///
162 /// This returns `true` if the data model contains no entries, which occurs when its length `L` is zero.
163 ///
164 /// ---
165 /// # Returns
166 /// `true` if the data model is empty, `false` otherwise.
167 ///
168 /// ---
169 /// # Examples
170 /// ```
171 /// use modbus_rtu::slave::DataModel;
172 ///
173 /// let empty_model = DataModel::<0, u16>::empty();
174 /// assert!(empty_model.is_empty());
175 /// ```
176 ///
177 pub fn is_empty(&self) -> bool {
178 L == 0
179 }
180}
181
182
183impl<T: Copy> DataModel<0, T> {
184 /// Creates and returns an empty data model with no stored values.
185 ///
186 /// This is useful when a data model is required but no data is needed or used.
187 ///
188 /// ---
189 /// # Returns
190 /// An empty `DataModel` instance with no associated data.
191 ///
192 /// ---
193 /// # Examples
194 /// ```
195 /// use modbus_rtu::slave::{ModbusSlave, DataModel};
196 ///
197 /// let holding_registers = DataModel::empty();
198 /// let input_registers = DataModel::empty();
199 ///
200 /// // Create modbus slave instance with zero registers
201 /// let modbus_slave = ModbusSlave::new(0x01, holding_registers, input_registers);
202 /// ```
203 ///
204 pub fn empty() -> DataModel<0, T> {
205 DataModel { structure: &DataStructure::EMPTY, values: [] }
206 }
207}