meterbus_wired_datalink/frame/field/
control.rs1use core::fmt;
92
93#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
95pub struct Control(u8);
96
97impl Control {
98 #[must_use]
100 pub const fn new(value: u8) -> Self {
101 Self(value)
102 }
103
104 #[must_use]
106 pub const fn snd_nke() -> Self {
107 Self(0x40)
108 }
109
110 #[must_use]
112 pub const fn req_ud1(fcb: bool) -> Self {
113 Self(0x5a | ((fcb as u8) << 5))
114 }
115
116 #[must_use]
118 pub const fn req_ud2(fcb: bool) -> Self {
119 Self(0x5b | ((fcb as u8) << 5))
120 }
121
122 #[must_use]
124 pub const fn snd_ud(bit5: bool) -> Self {
125 Self(0x53 | ((bit5 as u8) << 5))
126 }
127
128 #[must_use]
130 pub const fn snd_ud2() -> Self {
131 Self(0x43)
132 }
133
134 #[must_use]
136 pub const fn rsp_ud(acd: bool, dfc: bool) -> Self {
137 Self(0x08 | ((acd as u8) << 5) | ((dfc as u8) << 4))
138 }
139
140 #[must_use]
142 pub const fn value(self) -> u8 {
143 self.0
144 }
145
146 #[must_use]
148 pub const fn communication_type(self) -> CommunicationType {
149 match self.0 {
150 0x40 => CommunicationType::SndNke,
151 0x43 => CommunicationType::SndUd2,
152 0x53 | 0x73 => CommunicationType::SndUd,
153 0x5a | 0x7a => CommunicationType::ReqUd1,
154 0x5b | 0x7b => CommunicationType::ReqUd2,
155 0x08 | 0x18 | 0x28 | 0x38 => CommunicationType::RspUd,
156 _ => CommunicationType::Unsupported,
157 }
158 }
159
160 #[must_use]
162 pub const fn direction(self) -> Option<Direction> {
163 match self.communication_type() {
164 CommunicationType::SndNke
165 | CommunicationType::ReqUd1
166 | CommunicationType::ReqUd2
167 | CommunicationType::SndUd
168 | CommunicationType::SndUd2 => Some(Direction::MasterToSlave),
169 CommunicationType::RspUd => Some(Direction::SlaveToMaster),
170 CommunicationType::Unsupported => None,
171 }
172 }
173
174 #[must_use]
176 pub const fn frame_count_bit(self) -> Option<bool> {
177 if matches!(self.direction(), Some(Direction::MasterToSlave)) {
178 Some(self.0 & 0x20 != 0)
179 } else {
180 None
181 }
182 }
183
184 #[must_use]
186 pub const fn frame_count_valid(self) -> Option<bool> {
187 if matches!(self.direction(), Some(Direction::MasterToSlave)) {
188 Some(self.0 & 0x10 != 0)
189 } else {
190 None
191 }
192 }
193
194 #[must_use]
196 pub const fn access_demand(self) -> Option<bool> {
197 if matches!(self.direction(), Some(Direction::SlaveToMaster)) {
198 Some(self.0 & 0x20 != 0)
199 } else {
200 None
201 }
202 }
203
204 #[must_use]
206 pub const fn data_flow_control(self) -> Option<bool> {
207 if matches!(self.direction(), Some(Direction::SlaveToMaster)) {
208 Some(self.0 & 0x10 != 0)
209 } else {
210 None
211 }
212 }
213
214 pub(crate) const fn validate_short_frame(self) -> Result<(), ControlError> {
215 if matches!(
216 self.communication_type(),
217 CommunicationType::SndNke | CommunicationType::ReqUd1 | CommunicationType::ReqUd2
218 ) {
219 Ok(())
220 } else {
221 Err(ControlError::InvalidForShortFrame { value: self.0 })
222 }
223 }
224
225 pub(crate) const fn validate_variable_frame(self) -> Result<(), ControlError> {
226 if matches!(
227 self.communication_type(),
228 CommunicationType::SndUd | CommunicationType::SndUd2 | CommunicationType::RspUd
229 ) {
230 Ok(())
231 } else {
232 Err(ControlError::InvalidForVariableFrame { value: self.0 })
233 }
234 }
235}
236
237#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
239#[non_exhaustive]
240pub enum Direction {
241 MasterToSlave,
243 SlaveToMaster,
245}
246
247impl From<u8> for Control {
248 fn from(value: u8) -> Self {
249 Self::new(value)
250 }
251}
252
253#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
255pub enum CommunicationType {
256 SndNke,
258 ReqUd1,
260 ReqUd2,
262 SndUd,
264 SndUd2,
266 RspUd,
268 Unsupported,
270}
271
272#[derive(Clone, Copy, Debug, Eq, PartialEq)]
274pub enum ControlError {
275 InvalidForShortFrame {
277 value: u8,
279 },
280 InvalidForVariableFrame {
282 value: u8,
284 },
285}
286
287impl fmt::Display for ControlError {
288 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
289 match self {
290 Self::InvalidForShortFrame { value } => write!(
291 formatter,
292 "control value 0x{value:02x} is invalid for a short frame"
293 ),
294 Self::InvalidForVariableFrame { value } => write!(
295 formatter,
296 "control value 0x{value:02x} is invalid for a variable-format frame"
297 ),
298 }
299 }
300}
301
302impl core::error::Error for ControlError {}
303
304#[cfg(test)]
305#[cfg_attr(coverage_nightly, coverage(off))]
306mod tests {
307 use super::*;
308 #[cfg(feature = "alloc")]
309 use alloc::string::ToString;
310
311 #[test]
312 fn classifies_supported_communication_types() {
313 let cases = [
314 (0x40, CommunicationType::SndNke),
315 (0x43, CommunicationType::SndUd2),
316 (0x53, CommunicationType::SndUd),
317 (0x73, CommunicationType::SndUd),
318 (0x5a, CommunicationType::ReqUd1),
319 (0x7a, CommunicationType::ReqUd1),
320 (0x5b, CommunicationType::ReqUd2),
321 (0x7b, CommunicationType::ReqUd2),
322 (0x08, CommunicationType::RspUd),
323 (0x18, CommunicationType::RspUd),
324 (0x28, CommunicationType::RspUd),
325 (0x38, CommunicationType::RspUd),
326 (0xff, CommunicationType::Unsupported),
327 ];
328
329 for (value, communication_type) in cases {
330 let control = Control::from(value);
331 assert_eq!(control.value(), value);
332 assert_eq!(control.communication_type(), communication_type);
333 }
334 }
335
336 #[test]
337 fn exposes_master_and_slave_flags() {
338 let master = Control::new(0x7b);
339 assert_eq!(master.direction(), Some(Direction::MasterToSlave));
340 assert_eq!(master.frame_count_bit(), Some(true));
341 assert_eq!(master.frame_count_valid(), Some(true));
342 assert_eq!(master.access_demand(), None);
343 assert_eq!(master.data_flow_control(), None);
344
345 let slave = Control::new(0x38);
346 assert_eq!(slave.direction(), Some(Direction::SlaveToMaster));
347 assert_eq!(slave.frame_count_bit(), None);
348 assert_eq!(slave.frame_count_valid(), None);
349 assert_eq!(slave.access_demand(), Some(true));
350 assert_eq!(slave.data_flow_control(), Some(true));
351 }
352
353 #[test]
354 fn constructors_produce_named_control_values() {
355 assert_eq!(Control::snd_nke().value(), 0x40);
356 assert_eq!(Control::req_ud1(false).value(), 0x5a);
357 assert_eq!(Control::req_ud1(true).value(), 0x7a);
358 assert_eq!(Control::req_ud2(false).value(), 0x5b);
359 assert_eq!(Control::req_ud2(true).value(), 0x7b);
360 assert_eq!(Control::snd_ud(false).value(), 0x53);
361 assert_eq!(Control::snd_ud(true).value(), 0x73);
362 assert_eq!(Control::snd_ud2().value(), 0x43);
363 assert_eq!(Control::rsp_ud(false, false).value(), 0x08);
364 assert_eq!(Control::rsp_ud(false, true).value(), 0x18);
365 assert_eq!(Control::rsp_ud(true, false).value(), 0x28);
366 assert_eq!(Control::rsp_ud(true, true).value(), 0x38);
367 }
368
369 #[test]
370 fn all_control_bytes_expose_flags_only_for_supported_direction() {
371 for value in u8::MIN..=u8::MAX {
372 let control = Control::new(value);
373 match control.direction() {
374 Some(Direction::MasterToSlave) => {
375 assert!(control.frame_count_bit().is_some());
376 assert!(control.frame_count_valid().is_some());
377 assert_eq!(control.access_demand(), None);
378 assert_eq!(control.data_flow_control(), None);
379 }
380 Some(Direction::SlaveToMaster) => {
381 assert_eq!(control.frame_count_bit(), None);
382 assert_eq!(control.frame_count_valid(), None);
383 assert!(control.access_demand().is_some());
384 assert!(control.data_flow_control().is_some());
385 }
386 None => {
387 assert_eq!(control.frame_count_bit(), None);
388 assert_eq!(control.frame_count_valid(), None);
389 assert_eq!(control.access_demand(), None);
390 assert_eq!(control.data_flow_control(), None);
391 }
392 }
393 }
394 }
395
396 #[test]
397 fn validates_frame_compatibility() {
398 assert_eq!(Control::new(0x40).validate_short_frame(), Ok(()));
399 assert_eq!(Control::new(0x53).validate_variable_frame(), Ok(()));
400
401 let error = Control::new(0x53).validate_short_frame().unwrap_err();
402 assert_eq!(error, ControlError::InvalidForShortFrame { value: 0x53 });
403 #[cfg(feature = "alloc")]
404 assert_eq!(
405 error.to_string(),
406 "control value 0x53 is invalid for a short frame"
407 );
408 assert_eq!(
409 Control::new(0x40).validate_variable_frame(),
410 Err(ControlError::InvalidForVariableFrame { value: 0x40 })
411 );
412 }
413}