1use super::data_information::{self};
2use super::variable_user_data::DataRecordError;
3use super::LongTplHeader;
4
5#[cfg_attr(feature = "serde", derive(serde::Serialize))]
6#[derive(Debug, PartialEq, Clone)]
7#[cfg_attr(feature = "defmt", derive(defmt::Format))]
8pub struct DataInformationBlock<'a> {
9 pub data_information_field: DataInformationField,
10 pub data_information_field_extension: Option<DataInformationFieldExtensions<'a>>,
11}
12
13impl DataInformationBlock<'_> {
14 #[must_use]
15 pub fn get_size(&self) -> usize {
16 let mut size = 1;
17 if let Some(dife) = &self.data_information_field_extension {
18 size += dife.len();
19 }
20 size
21 }
22}
23#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
24#[derive(Debug, PartialEq, Clone)]
25#[cfg_attr(feature = "defmt", derive(defmt::Format))]
26pub struct DataInformationField {
27 pub data: u8,
28}
29
30impl From<data_information::DataInformationError> for DataRecordError {
31 fn from(error: data_information::DataInformationError) -> Self {
32 Self::DataInformationError(error)
33 }
34}
35
36impl From<u8> for DataInformationField {
37 fn from(data: u8) -> Self {
38 Self { data }
39 }
40}
41
42impl From<u8> for DataInformationFieldExtension {
43 fn from(data: u8) -> Self {
44 Self { data }
45 }
46}
47
48#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
49#[derive(Debug, PartialEq)]
50#[repr(transparent)]
51pub struct DataInformationFieldExtension {
52 pub data: u8,
53}
54
55#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
56#[derive(Clone, Debug, PartialEq)]
57#[cfg_attr(feature = "defmt", derive(defmt::Format))]
58pub struct DataInformationFieldExtensions<'a>(&'a [u8]);
59impl<'a> DataInformationFieldExtensions<'a> {
60 const fn new(data: &'a [u8]) -> Self {
61 Self(data)
62 }
63}
64
65impl Iterator for DataInformationFieldExtensions<'_> {
66 type Item = DataInformationFieldExtension;
67 fn next(&mut self) -> Option<Self::Item> {
68 let (head, tail) = self.0.split_first()?;
69 self.0 = tail;
70 Some(DataInformationFieldExtension { data: *head })
71 }
72 fn size_hint(&self) -> (usize, Option<usize>) {
73 (self.0.len(), Some(self.0.len()))
74 }
75}
76impl ExactSizeIterator for DataInformationFieldExtensions<'_> {}
77impl DoubleEndedIterator for DataInformationFieldExtensions<'_> {
78 fn next_back(&mut self) -> Option<Self::Item> {
79 let (end, start) = self.0.split_last()?;
80 self.0 = start;
81 Some(DataInformationFieldExtension { data: *end })
82 }
83}
84
85impl<'a> TryFrom<&'a [u8]> for DataInformationBlock<'a> {
86 type Error = DataInformationError;
87
88 fn try_from(data: &'a [u8]) -> Result<Self, DataInformationError> {
89 let Some((dif_byte, data)) = data.split_first() else {
90 return Err(DataInformationError::NoData);
91 };
92 let dif = DataInformationField::from(*dif_byte);
93
94 if !dif.has_extension() {
95 return Ok(DataInformationBlock {
96 data_information_field: dif,
97 data_information_field_extension: None,
98 });
99 }
100
101 let length = data.iter().take_while(|&&u8| u8 & 0x80 != 0).count();
102 let offset = length + 1;
103 if offset > MAXIMUM_DATA_INFORMATION_SIZE {
104 return Err(DataInformationError::DataTooLong);
105 }
106 Ok(DataInformationBlock {
107 data_information_field: dif,
108 data_information_field_extension: Some(DataInformationFieldExtensions::new(
109 data.get(..offset)
110 .ok_or(DataInformationError::DataTooShort)?,
111 )),
112 })
113 }
114}
115
116impl DataInformationField {
117 const fn has_extension(&self) -> bool {
118 self.data & 0x80 != 0
119 }
120
121 pub const fn is_special_function(&self) -> bool {
122 self.data & 0x0F == 0x0F
123 }
124
125 pub const fn special_function(&self) -> SpecialFunctions {
126 match self.data {
127 0x0F => SpecialFunctions::ManufacturerSpecific,
128 0x1F => SpecialFunctions::MoreRecordsFollow,
129 0x2F => SpecialFunctions::IdleFiller,
130 0x7F => SpecialFunctions::GlobalReadoutRequest,
131 _ => SpecialFunctions::Reserved,
132 }
133 }
134}
135#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
136#[derive(Debug, Clone, PartialEq)]
137#[cfg_attr(feature = "defmt", derive(defmt::Format))]
138pub struct DataInformation {
139 pub storage_number: u64,
140 pub tariff: u64,
141 pub device: u64,
142 pub function_field: FunctionField,
143 pub data_field_coding: DataFieldCoding,
144 pub data_information_extension: Option<DataInformationExtensionField>,
145 pub size: usize,
146}
147
148#[cfg(feature = "std")]
149impl std::fmt::Display for DataInformation {
150 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
151 write!(
152 f,
153 "{},{},{}",
154 self.storage_number, self.function_field, self.data_field_coding
155 )
156 }
157}
158
159const MAXIMUM_DATA_INFORMATION_SIZE: usize = 11;
160#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
161#[derive(Debug, Clone, PartialEq)]
162#[cfg_attr(feature = "defmt", derive(defmt::Format))]
163pub struct DataInformationExtensionField {}
164
165#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
166#[derive(Debug, Clone, Copy, PartialEq)]
167#[cfg_attr(feature = "defmt", derive(defmt::Format))]
168#[non_exhaustive]
169pub enum DataInformationError {
170 NoData,
171 DataTooLong,
172 DataTooShort,
173 InvalidValueInformation,
174 Unimplemented { feature: &'static str },
175}
176
177#[cfg(feature = "std")]
178impl std::fmt::Display for DataInformationError {
179 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
180 match self {
181 DataInformationError::NoData => write!(f, "No data available"),
182 DataInformationError::DataTooLong => write!(f, "Data too long"),
183 DataInformationError::DataTooShort => write!(f, "Data too short"),
184 DataInformationError::InvalidValueInformation => {
185 write!(f, "Invalid value information")
186 }
187 DataInformationError::Unimplemented { feature } => {
188 write!(f, "Unimplemented feature: {}", feature)
189 }
190 }
191 }
192}
193
194#[cfg(feature = "std")]
195impl std::error::Error for DataInformationError {}
196
197impl TryFrom<&DataInformationBlock<'_>> for DataInformation {
198 type Error = DataInformationError;
199
200 fn try_from(
201 data_information_block: &DataInformationBlock,
202 ) -> Result<Self, DataInformationError> {
203 let dif = data_information_block.data_information_field.data;
204 let possible_difes = &data_information_block.data_information_field_extension;
205 let mut storage_number = u64::from((dif & 0b0100_0000) >> 6);
206
207 let mut extension_bit = dif & 0x80 != 0;
208 let mut extension_index = 1;
209 let mut tariff = 0;
210 let mut device = 0;
211 if let Some(difes) = possible_difes {
212 let mut tariff_index = 0;
213 for (device_index, dife) in difes.clone().enumerate() {
214 if extension_index > MAXIMUM_DATA_INFORMATION_SIZE {
215 return Err(DataInformationError::DataTooLong);
216 }
217 let dife = dife.data;
218 storage_number += u64::from(dife & 0x0f) << ((extension_index * 4) + 1);
219 tariff |= u64::from((dife & 0x30) >> 4) << (tariff_index);
220 tariff_index += 2;
221 device |= u64::from((dife & 0x40) >> 6) << device_index;
222 extension_bit = dife & 0x80 != 0;
223 extension_index += 1;
224 }
225 }
226
227 let function_field = match (dif & 0b0011_0000) >> 4 {
228 0b00 => FunctionField::InstantaneousValue,
229 0b01 => FunctionField::MaximumValue,
230 0b10 => FunctionField::MinimumValue,
231 _ => FunctionField::ValueDuringErrorState,
232 };
233 let data_field_coding = match dif & 0b0000_1111 {
234 0b0000 => DataFieldCoding::NoData,
235 0b0001 => DataFieldCoding::Integer8Bit,
236 0b0010 => DataFieldCoding::Integer16Bit,
237 0b0011 => DataFieldCoding::Integer24Bit,
238 0b0100 => DataFieldCoding::Integer32Bit,
239 0b0101 => DataFieldCoding::Real32Bit,
240 0b0110 => DataFieldCoding::Integer48Bit,
241 0b0111 => DataFieldCoding::Integer64Bit,
242 0b1000 => DataFieldCoding::SelectionForReadout,
243 0b1001 => DataFieldCoding::BCD2Digit,
244 0b1010 => DataFieldCoding::BCD4Digit,
245 0b1011 => DataFieldCoding::BCD6Digit,
246 0b1100 => DataFieldCoding::BCD8Digit,
247 0b1101 => DataFieldCoding::VariableLength,
248 0b1110 => DataFieldCoding::BCDDigit12,
249 0b1111 => DataFieldCoding::SpecialFunctions(
250 data_information_block
251 .data_information_field
252 .special_function(),
253 ),
254 _ => unreachable!(), };
256
257 Ok(Self {
258 storage_number,
259 tariff,
260 device,
261 function_field,
262 data_field_coding,
263 data_information_extension: if extension_bit {
264 Some(DataInformationExtensionField {})
265 } else {
266 None
267 },
268 size: extension_index,
269 })
270 }
271}
272
273#[derive(Clone, Copy, Debug, PartialEq, Eq)]
274#[cfg_attr(feature = "serde", derive(serde::Serialize), serde(into = "String"))]
275#[cfg_attr(feature = "defmt", derive(defmt::Format))]
276pub struct TextUnit<'a>(&'a [u8]);
277impl<'a> TextUnit<'a> {
278 #[must_use]
279 pub const fn new(input: &'a [u8]) -> Self {
280 Self(input)
281 }
282}
283
284impl PartialEq<str> for TextUnit<'_> {
285 fn eq(&self, other: &str) -> bool {
286 self.0.iter().eq(other.as_bytes().iter().rev())
287 }
288}
289
290#[cfg(feature = "std")]
291impl std::fmt::Display for TextUnit<'_> {
292 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
293 write!(f, "{}", decode_text_unit(self.0))
294 }
295}
296
297#[cfg(feature = "std")]
298impl From<TextUnit<'_>> for String {
299 fn from(value: TextUnit<'_>) -> Self {
300 decode_text_unit(value.0)
301 }
302}
303
304#[cfg(feature = "std")]
305fn decode_text_unit(input: &[u8]) -> String {
306 let bytes: Vec<u8> = input.iter().copied().rev().collect();
307 match String::from_utf8(bytes) {
308 Ok(value) => value,
309 Err(error) => error.into_bytes().into_iter().map(char::from).collect(),
310 }
311}
312
313#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
314#[derive(Debug, PartialEq, Clone, Copy)]
315#[cfg_attr(feature = "defmt", derive(defmt::Format))]
316#[non_exhaustive]
317pub enum Month {
318 January,
319 February,
320 March,
321 April,
322 May,
323 June,
324 July,
325 August,
326 September,
327 October,
328 November,
329 December,
330}
331
332#[cfg(feature = "std")]
333impl std::fmt::Display for Month {
334 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
335 match self {
336 Month::January => write!(f, "Jan"),
337 Month::February => write!(f, "Feb"),
338 Month::March => write!(f, "Mar"),
339 Month::April => write!(f, "Apr"),
340 Month::May => write!(f, "May"),
341 Month::June => write!(f, "Jun"),
342 Month::July => write!(f, "Jul"),
343 Month::August => write!(f, "Aug"),
344 Month::September => write!(f, "Sep"),
345 Month::October => write!(f, "Oct"),
346 Month::November => write!(f, "Nov"),
347 Month::December => write!(f, "Dec"),
348 }
349 }
350}
351
352pub type Year = u16;
353pub type DayOfMonth = u8;
354pub type Hour = u8;
355pub type Minute = u8;
356pub type Second = u8;
357
358#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
359#[derive(Debug, PartialEq, Clone)]
360#[cfg_attr(feature = "defmt", derive(defmt::Format))]
361#[non_exhaustive]
362pub enum SingleEveryOrInvalid<T> {
363 Single(T),
364 Every(),
365 Invalid(),
366}
367
368#[cfg_attr(feature = "serde", derive(serde::Serialize))]
369#[derive(Debug, PartialEq, Clone)]
370#[cfg_attr(feature = "defmt", derive(defmt::Format))]
371#[non_exhaustive]
372pub enum DataType<'a> {
373 Text(TextUnit<'a>),
374 Number(f64),
375 LossyNumber(f64),
376 Date(
377 SingleEveryOrInvalid<DayOfMonth>,
378 SingleEveryOrInvalid<Month>,
379 SingleEveryOrInvalid<Year>,
380 ),
381 Time(
382 SingleEveryOrInvalid<Second>,
383 SingleEveryOrInvalid<Minute>,
384 SingleEveryOrInvalid<Hour>,
385 ),
386 DateTime(
387 SingleEveryOrInvalid<DayOfMonth>,
388 SingleEveryOrInvalid<Month>,
389 SingleEveryOrInvalid<Year>,
390 SingleEveryOrInvalid<Hour>,
391 SingleEveryOrInvalid<Minute>,
392 ),
393 DateTimeWithSeconds(
394 SingleEveryOrInvalid<DayOfMonth>,
395 SingleEveryOrInvalid<Month>,
396 SingleEveryOrInvalid<Year>,
397 SingleEveryOrInvalid<Hour>,
398 SingleEveryOrInvalid<Minute>,
399 SingleEveryOrInvalid<Second>,
400 ),
401 ManufacturerSpecific(
402 #[cfg_attr(
403 feature = "serde",
404 serde(serialize_with = "m_bus_core::serde_hex::serialize")
405 )]
406 &'a [u8],
407 ),
408}
409#[cfg_attr(feature = "serde", derive(serde::Serialize))]
410#[derive(PartialEq, Debug, Clone)]
411#[cfg_attr(feature = "defmt", derive(defmt::Format))]
412pub struct Data<'a> {
413 pub value: Option<DataType<'a>>,
414 pub size: usize,
415}
416
417#[cfg(feature = "std")]
418impl<T: std::fmt::Display> std::fmt::Display for SingleEveryOrInvalid<T> {
419 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
420 match self {
421 SingleEveryOrInvalid::Single(value) => write!(f, "{}", value),
422 SingleEveryOrInvalid::Every() => write!(f, "Every"),
423 SingleEveryOrInvalid::Invalid() => write!(f, "Invalid"),
424 }
425 }
426}
427
428#[cfg(feature = "std")]
429impl std::fmt::Display for Data<'_> {
430 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
431 match &self.value {
432 Some(value) => match value {
433 DataType::Number(value) => write!(f, "{}", value),
434 DataType::LossyNumber(value) => write!(f, "{}", value),
435 DataType::Date(day, month, year) => write!(f, "{}/{}/{}", day, month, year),
436 DataType::DateTime(day, month, year, hour, minute) => {
437 write!(f, "{}/{}/{} {}:{}:00", day, month, year, hour, minute)
438 }
439 DataType::DateTimeWithSeconds(day, month, year, hour, minute, second) => {
440 write!(
441 f,
442 "{}/{}/{} {}:{}:{}",
443 day, month, year, hour, minute, second
444 )
445 }
446 DataType::Time(seconds, minutes, hours) => {
447 write!(f, "{}:{}:{}", hours, minutes, seconds)
448 }
449 DataType::Text(text_unit) => {
450 let text: String = (*text_unit).into();
451 write!(f, "{}", text)
452 }
453 DataType::ManufacturerSpecific(data) => {
454 write!(f, "Manufacturer Specific: {:?}", data)
455 }
456 },
457 None => write!(f, "No Data"),
458 }
459 }
460}
461
462impl Data<'_> {
463 #[must_use]
464 pub const fn get_size(&self) -> usize {
465 self.size
466 }
467}
468
469macro_rules! parse_single_or_every {
470 ($input:expr, $mask:expr, $all_value:expr, $shift:expr) => {
471 if $input & $mask == $all_value {
472 SingleEveryOrInvalid::Every()
473 } else {
474 SingleEveryOrInvalid::Single(($input & $mask) >> $shift)
475 }
476 };
477}
478
479macro_rules! parse_month {
480 ($input:expr) => {
481 match $input & 0xF {
482 0x1 => SingleEveryOrInvalid::Single(Month::January),
483 0x2 => SingleEveryOrInvalid::Single(Month::February),
484 0x3 => SingleEveryOrInvalid::Single(Month::March),
485 0x4 => SingleEveryOrInvalid::Single(Month::April),
486 0x5 => SingleEveryOrInvalid::Single(Month::May),
487 0x6 => SingleEveryOrInvalid::Single(Month::June),
488 0x7 => SingleEveryOrInvalid::Single(Month::July),
489 0x8 => SingleEveryOrInvalid::Single(Month::August),
490 0x9 => SingleEveryOrInvalid::Single(Month::September),
491 0xA => SingleEveryOrInvalid::Single(Month::October),
492 0xB => SingleEveryOrInvalid::Single(Month::November),
493 0xC => SingleEveryOrInvalid::Single(Month::December),
494 _ => SingleEveryOrInvalid::Invalid(),
495 }
496 };
497}
498
499macro_rules! parse_year {
500 ($input:expr, $mask_byte1:expr, $mask_byte2:expr, $all_value:expr) => {{
501 let byte1 = u16::from($input.get(1).copied().unwrap_or(0) & $mask_byte1);
502 let byte2 = u16::from($input.get(0).copied().unwrap_or(0) & $mask_byte2);
503 let year = byte1.wrapping_shr(1) | byte2.wrapping_shr(5);
504 if year == $all_value {
505 SingleEveryOrInvalid::Every()
506 } else {
507 SingleEveryOrInvalid::Single(year)
508 }
509 }};
510}
511fn bcd_to_value_internal(
512 data: &[u8],
513 num_digits: usize,
514 sign: i32,
515 lsb_order: bool,
516) -> Result<Data<'_>, DataRecordError> {
517 if data.len() < num_digits.div_ceil(2) {
518 return Err(DataRecordError::InsufficientData);
519 }
520
521 let mut data_value = 0.0;
522 let mut current_weight = 1.0;
523 let mut negative = false;
524
525 for i in 0..num_digits {
526 let index = if lsb_order {
527 (num_digits - i - 1) / 2
528 } else {
529 i / 2
530 };
531 let byte = data.get(index).ok_or(DataRecordError::InsufficientData)?;
532
533 let digit = if i % 2 == 0 {
534 byte & 0x0F
535 } else {
536 (byte >> 4) & 0x0F
537 };
538
539 if digit == 0x0F && i == num_digits - 1 {
543 negative = true;
544 break;
545 }
546
547 if digit > 9 {
548 return Err(DataRecordError::DataInformationError(
549 DataInformationError::InvalidValueInformation,
550 ));
551 }
552
553 data_value += f64::from(digit) * current_weight;
554 current_weight *= 10.0;
555 }
556
557 let sign = if negative { -sign } else { sign };
558 let signed_value = if data_value == 0.0 {
560 0.0
561 } else {
562 data_value * sign as f64
563 };
564
565 Ok(Data {
566 value: Some(DataType::Number(signed_value)),
567 size: num_digits.div_ceil(2),
568 })
569}
570
571fn integer_to_value_internal(data: &[u8], byte_size: usize) -> Data<'_> {
572 let mut data_value = 0i64;
573 let mut shift = 0;
574 for byte in data.iter().take(if byte_size > 8 { 8 } else { byte_size }) {
575 data_value |= (*byte as i64) << shift;
576 shift += 8;
577 }
578
579 let msb = (data_value >> (shift - 1)) & 1;
580 let data_value = if byte_size < 8 && msb == 1 {
581 -((data_value ^ (2i64.pow(shift) - 1)) + 1)
582 } else {
583 data_value
584 };
585
586 let output = if byte_size > 8 {
587 DataType::LossyNumber(data_value as f64)
588 } else {
589 DataType::Number(data_value as f64)
590 };
591 Data {
592 value: Some(output),
593 size: byte_size,
594 }
595}
596
597impl DataFieldCoding {
598 #[must_use]
610 pub fn data_size(&self, input: &[u8]) -> Option<usize> {
611 match self {
612 Self::NoData | Self::SelectionForReadout => Some(0),
613 Self::Integer8Bit | Self::BCD2Digit => Some(1),
614 Self::Integer16Bit | Self::BCD4Digit | Self::DateTypeG => Some(2),
615 Self::Integer24Bit | Self::BCD6Digit => Some(3),
616 Self::Integer32Bit
617 | Self::Real32Bit
618 | Self::BCD8Digit
619 | Self::DateTimeTypeF
620 | Self::DateTimeTypeJ => Some(4),
621 Self::Integer48Bit | Self::BCDDigit12 | Self::DateTimeTypeI => Some(6),
622 Self::Integer64Bit => Some(8),
623 Self::VariableLength => match *input.first()? {
624 length @ 0x00..=0xBF => Some(length as usize + 1),
625 length @ 0xC0..=0xC9 => Some((length - 0xC0) as usize + 1),
626 length @ 0xD0..=0xD9 => Some((length - 0xD0) as usize + 1),
627 length @ 0xE0..=0xEF => Some((length - 0xE0) as usize + 1),
628 length @ 0xF0..=0xF4 => Some(4 * (length - 0xEC) as usize + 1),
629 0xF5 => Some(48 + 1),
630 0xF6 => Some(64 + 1),
631 _ => None,
632 },
633 Self::SpecialFunctions(code) => match code {
634 SpecialFunctions::ManufacturerSpecific | SpecialFunctions::MoreRecordsFollow => {
635 Some(input.len())
636 }
637 SpecialFunctions::IdleFiller | SpecialFunctions::GlobalReadoutRequest => Some(0),
638 SpecialFunctions::Reserved => None,
639 },
640 }
641 }
642
643 pub fn parse<'a>(
644 &self,
645 input: &'a [u8],
646 fixed_data_header: Option<&'a LongTplHeader>,
647 ) -> Result<Data<'a>, DataRecordError> {
648 let lsb_order = fixed_data_header.map(|x| x.lsb_order).unwrap_or(false);
649
650 macro_rules! bcd_to_value {
651 ($data:expr, $num_digits:expr) => {{
652 bcd_to_value_internal($data, $num_digits, 1, lsb_order)
653 }};
654
655 ($data:expr, $num_digits:expr, $sign:expr) => {{
656 bcd_to_value_internal($data, $num_digits, $sign, lsb_order)
657 }};
658 }
659
660 macro_rules! integer_to_value {
661 ($data:expr, $byte_size:expr) => {{
662 if $data.len() < $byte_size {
663 return Err(DataRecordError::InsufficientData);
664 }
665 Ok(integer_to_value_internal($data, $byte_size))
666 }};
667 }
668 match self {
669 Self::NoData => Ok(Data {
670 value: None,
671 size: 0,
672 }),
673 Self::Integer8Bit => integer_to_value!(input, 1),
674 Self::Integer16Bit => integer_to_value!(input, 2),
675 Self::Integer24Bit => integer_to_value!(input, 3),
676 Self::Integer32Bit => integer_to_value!(input, 4),
677 Self::Integer48Bit => integer_to_value!(input, 6),
678 Self::Integer64Bit => integer_to_value!(input, 8),
679
680 Self::Real32Bit => {
681 if input.len() < 4 {
682 return Err(DataRecordError::InsufficientData);
683 }
684 if let Ok(x) = input
685 .get(0..4)
686 .ok_or(DataRecordError::InsufficientData)?
687 .try_into()
688 {
689 let x: [u8; 4] = x;
690 Ok(Data {
691 value: Some(DataType::Number(f64::from(f32::from_le_bytes(x)))),
692 size: 4,
693 })
694 } else {
695 Err(DataRecordError::InsufficientData)
696 }
697 }
698
699 Self::SelectionForReadout => Ok(Data {
700 value: None,
701 size: 0,
702 }),
703
704 Self::BCD2Digit => bcd_to_value!(input, 2),
705 Self::BCD4Digit => bcd_to_value!(input, 4),
706 Self::BCD6Digit => bcd_to_value!(input, 6),
707 Self::BCD8Digit => bcd_to_value!(input, 8),
708 Self::BCDDigit12 => bcd_to_value!(input, 12),
709
710 Self::VariableLength => {
711 let mut length = *input.first().ok_or(DataRecordError::InsufficientData)?;
712 match length {
713 0x00..=0xBF => Ok(Data {
714 value: Some(DataType::Text(TextUnit::new(
715 input
716 .get(1..(1 + length as usize))
717 .ok_or(DataRecordError::InsufficientData)?,
718 ))),
719 size: length as usize + 1,
720 }),
721 0xC0..=0xC9 => {
722 length -= 0xC0;
723 let bytes = input
724 .get(1..(1 + length as usize))
725 .ok_or(DataRecordError::InsufficientData)?;
726 match bcd_to_value!(bytes, 2 * length as usize) {
727 Ok(data) => Ok(Data {
728 value: data.value,
729 size: data.size + 1,
730 }),
731 Err(err) => Err(err),
732 }
733 }
734 0xD0..=0xD9 => {
735 length -= 0xD0;
736 let bytes = input
737 .get(1..(1 + length as usize))
738 .ok_or(DataRecordError::InsufficientData)?;
739 match bcd_to_value!(bytes, 2 * length as usize, -1) {
740 Ok(data) => Ok(Data {
741 value: data.value,
742 size: data.size + 1,
743 }),
744 Err(err) => Err(err),
745 }
746 }
747 0xE0..=0xEF => {
748 length -= 0xE0;
749 let bytes = input
750 .get(1..(1 + length as usize))
751 .ok_or(DataRecordError::InsufficientData)?;
752 match integer_to_value!(bytes, length as usize) {
753 Ok(data) => Ok(Data {
754 value: data.value,
755 size: data.size + 1,
756 }),
757 Err(err) => Err(err),
758 }
759 }
760 0xF0..=0xF4 => {
761 length -= 0xEC;
762 let bytes = input
763 .get(1..(1 + 4 * length as usize))
764 .ok_or(DataRecordError::InsufficientData)?;
765 match integer_to_value!(bytes, 4 * length as usize) {
766 Ok(data) => Ok(Data {
767 value: data.value,
768 size: data.size + 1,
769 }),
770 Err(err) => Err(err),
771 }
772 }
773 0xF5 => {
774 let bytes = input
775 .get(1..(1 + 48_usize))
776 .ok_or(DataRecordError::InsufficientData)?;
777 match integer_to_value!(bytes, 48_usize) {
778 Ok(data) => Ok(Data {
779 value: data.value,
780 size: data.size + 1,
781 }),
782 Err(err) => Err(err),
783 }
784 }
785 0xF6 => {
786 let bytes = input
787 .get(1..(1 + 64_usize))
788 .ok_or(DataRecordError::InsufficientData)?;
789 match integer_to_value!(bytes, 64_usize) {
790 Ok(data) => Ok(Data {
791 value: data.value,
792 size: data.size + 1,
793 }),
794 Err(err) => Err(err),
795 }
796 }
797 _ => Err(DataRecordError::DataInformationError(
798 DataInformationError::Unimplemented {
799 feature: "Variable length parsing for reserved length values",
800 },
801 )),
802 }
803 }
804
805 Self::SpecialFunctions(code) => match code {
806 SpecialFunctions::ManufacturerSpecific | SpecialFunctions::MoreRecordsFollow => {
807 Ok(Data {
808 value: Some(DataType::ManufacturerSpecific(input)),
809 size: input.len(),
810 })
811 }
812 SpecialFunctions::IdleFiller => Ok(Data {
813 value: None,
814 size: 0,
815 }),
816 SpecialFunctions::GlobalReadoutRequest => Ok(Data {
817 value: None,
818 size: 0,
819 }),
820 SpecialFunctions::Reserved => Err(DataRecordError::DataInformationError(
821 DataInformationError::InvalidValueInformation,
822 )),
823 },
824
825 Self::DateTypeG => {
826 let day = parse_single_or_every!(
827 input.first().ok_or(DataRecordError::InsufficientData)?,
828 0x1F,
829 0,
830 0
831 );
832 let month = parse_month!(input.get(1).ok_or(DataRecordError::InsufficientData)?);
833 let year = parse_year!(input, 0xF0, 0xE0, 0x7F);
834
835 Ok(Data {
836 value: Some(DataType::Date(day, month, year)),
837 size: 2,
838 })
839 }
840 Self::DateTimeTypeF => {
841 let minutes = parse_single_or_every!(
842 input.first().ok_or(DataRecordError::InsufficientData)?,
843 0x3F,
844 0x3F,
845 0
846 );
847 let hour = parse_single_or_every!(
848 input.get(1).ok_or(DataRecordError::InsufficientData)?,
849 0x1F,
850 0x1F,
851 0
852 );
853 let day = parse_single_or_every!(
854 input.get(2).ok_or(DataRecordError::InsufficientData)?,
855 0x1F,
856 0x1F,
857 0
858 );
859 let month = parse_month!(input.get(3).ok_or(DataRecordError::InsufficientData)?);
860 let year = parse_year!(input, 0xF0, 0xE0, 0x7F);
861
862 Ok(Data {
863 value: Some(DataType::DateTime(day, month, year, hour, minutes)),
864 size: 4,
865 })
866 }
867 Self::DateTimeTypeJ => {
868 let seconds = parse_single_or_every!(
869 input.first().ok_or(DataRecordError::InsufficientData)?,
870 0x3F,
871 0x3F,
872 0
873 );
874 let minutes = parse_single_or_every!(
875 input.get(1).ok_or(DataRecordError::InsufficientData)?,
876 0x3F,
877 0x3F,
878 0
879 );
880 let hours = parse_single_or_every!(
881 input.get(2).ok_or(DataRecordError::InsufficientData)?,
882 0x1F,
883 0x1F,
884 0
885 );
886
887 Ok(Data {
888 value: Some(DataType::Time(seconds, minutes, hours)),
889 size: 4,
890 })
891 }
892 Self::DateTimeTypeI => {
893 let seconds = parse_single_or_every!(
898 input.first().ok_or(DataRecordError::InsufficientData)?,
899 0x3F,
900 0x3F,
901 0
902 );
903
904 let minutes = parse_single_or_every!(
905 input.get(1).ok_or(DataRecordError::InsufficientData)?,
906 0x3F,
907 0x3F,
908 0
909 );
910
911 let hours = parse_single_or_every!(
912 input.get(2).ok_or(DataRecordError::InsufficientData)?,
913 0x1F,
914 0x1F,
915 0
916 );
917 let days = parse_single_or_every!(
918 input.get(3).ok_or(DataRecordError::InsufficientData)?,
919 0x1F,
920 0x1F,
921 0
922 );
923 let months = parse_month!(input.get(4).ok_or(DataRecordError::InsufficientData)?);
924 let year = parse_year!(input, 0xF0, 0xE0, 0x7F);
925
926 Ok(Data {
927 value: Some(DataType::DateTimeWithSeconds(
928 days, months, year, hours, minutes, seconds,
929 )),
930 size: 6,
931 })
932 }
933 }
934 }
935}
936
937impl DataInformation {
938 #[must_use]
939 pub const fn get_size(&self) -> usize {
940 self.size
941 }
942}
943#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
944#[derive(Debug, Clone, Copy, PartialEq)]
945#[cfg_attr(feature = "defmt", derive(defmt::Format))]
946#[non_exhaustive]
947pub enum FunctionField {
948 InstantaneousValue,
949 MaximumValue,
950 MinimumValue,
951 ValueDuringErrorState,
952}
953
954#[cfg(feature = "std")]
955impl std::fmt::Display for FunctionField {
956 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
957 match self {
958 FunctionField::InstantaneousValue => write!(f, "Inst"),
959 FunctionField::MaximumValue => write!(f, "Max"),
960 FunctionField::MinimumValue => write!(f, "Min"),
961 FunctionField::ValueDuringErrorState => write!(f, "Value During Error State"),
962 }
963 }
964}
965#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
966#[derive(Debug, Clone, Copy, PartialEq)]
967#[cfg_attr(feature = "defmt", derive(defmt::Format))]
968#[non_exhaustive]
969pub enum SpecialFunctions {
970 ManufacturerSpecific,
971 MoreRecordsFollow,
972 IdleFiller,
973 Reserved,
974 GlobalReadoutRequest,
975}
976
977#[cfg_attr(feature = "defmt", derive(defmt::Format))]
978pub struct Value {
979 pub data: f64,
980 pub byte_size: usize,
981}
982
983#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
984#[derive(Debug, Clone, Copy, PartialEq)]
985#[cfg_attr(feature = "defmt", derive(defmt::Format))]
986#[non_exhaustive]
987pub enum DataFieldCoding {
988 NoData,
989 Integer8Bit,
990 Integer16Bit,
991 Integer24Bit,
992 Integer32Bit,
993 Real32Bit,
994 Integer48Bit,
995 Integer64Bit,
996 SelectionForReadout,
997 BCD2Digit,
998 BCD4Digit,
999 BCD6Digit,
1000 BCD8Digit,
1001 VariableLength,
1002 BCDDigit12,
1003 SpecialFunctions(SpecialFunctions),
1004 DateTypeG,
1005 DateTimeTypeF,
1006 DateTimeTypeJ,
1007 DateTimeTypeI,
1008}
1009
1010#[cfg(feature = "std")]
1011impl std::fmt::Display for DataFieldCoding {
1012 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1013 match self {
1014 DataFieldCoding::NoData => write!(f, "No Data"),
1015 DataFieldCoding::Integer8Bit => write!(f, "8-bit Integer"),
1016 DataFieldCoding::Integer16Bit => write!(f, "16-bit Integer"),
1017 DataFieldCoding::Integer24Bit => write!(f, "24-bit Integer"),
1018 DataFieldCoding::Integer32Bit => write!(f, "32-bit Integer"),
1019 DataFieldCoding::Real32Bit => write!(f, "32-bit Real"),
1020 DataFieldCoding::Integer48Bit => write!(f, "48-bit Integer"),
1021 DataFieldCoding::Integer64Bit => write!(f, "64-bit Integer"),
1022 DataFieldCoding::SelectionForReadout => write!(f, "Selection for Readout"),
1023 DataFieldCoding::BCD2Digit => write!(f, "BCD 2-digit"),
1024 DataFieldCoding::BCD4Digit => write!(f, "BCD 4-digit"),
1025 DataFieldCoding::BCD6Digit => write!(f, "BCD 6-digit"),
1026 DataFieldCoding::BCD8Digit => write!(f, "BCD 8-digit"),
1027 DataFieldCoding::VariableLength => write!(f, "Variable Length"),
1028 DataFieldCoding::BCDDigit12 => write!(f, "BCD 12-digit"),
1029 DataFieldCoding::DateTypeG => write!(f, "Date Type G"),
1030 DataFieldCoding::DateTimeTypeF => write!(f, "Date Time Type F"),
1031 DataFieldCoding::DateTimeTypeJ => write!(f, "Date Time Type J"),
1032 DataFieldCoding::DateTimeTypeI => write!(f, "Date Time Type I"),
1033 DataFieldCoding::SpecialFunctions(code) => write!(f, "Special Functions ({:?})", code),
1034 }
1035 }
1036}
1037
1038#[cfg(test)]
1039mod tests {
1040
1041 use super::*;
1042 #[test]
1043 fn test_data_information() {
1044 let data = [0x13_u8];
1045 let result = DataInformationBlock::try_from(data.as_slice());
1046 let result = DataInformation::try_from(&result.unwrap());
1047 assert_eq!(
1048 result,
1049 Ok(DataInformation {
1050 storage_number: 0,
1051 device: 0,
1052 tariff: 0,
1053 function_field: FunctionField::MaximumValue,
1054 data_field_coding: DataFieldCoding::Integer24Bit,
1055 data_information_extension: None,
1056 size: 1,
1057 })
1058 );
1059 }
1060
1061 #[test]
1062 fn unextended_dif_does_not_interpret_following_bytes_as_extensions() {
1063 for dif in 0..=0x7f {
1064 let mut bytes = [0xff; MAXIMUM_DATA_INFORMATION_SIZE + 3];
1065 bytes[0] = dif;
1066 let block = DataInformationBlock::try_from(bytes.as_slice()).unwrap();
1067 assert_eq!(block.data_information_field.data, dif);
1068 assert_eq!(block.get_size(), 1);
1069 assert!(block.data_information_field_extension.is_none());
1070 }
1071 }
1072
1073 #[test]
1074 fn test_complex_data_information() {
1075 let data = [0xc4, 0x80, 0x40];
1076 let result = DataInformationBlock::try_from(data.as_slice());
1077 let result = DataInformation::try_from(&result.unwrap());
1078 assert_eq!(
1079 result,
1080 Ok(DataInformation {
1081 storage_number: 1,
1082 device: 2,
1083 tariff: 0,
1084 function_field: FunctionField::InstantaneousValue,
1085 data_field_coding: DataFieldCoding::Integer32Bit,
1086 data_information_extension: None,
1087 size: 3,
1088 })
1089 );
1090 }
1091
1092 #[test]
1093 fn reverse_text_unit() {
1094 let original_value = [0x6c, 0x61, 0x67, 0x69];
1095 let parsed = TextUnit::new(&original_value);
1096 assert_eq!(&parsed, "igal");
1097 }
1098
1099 #[cfg(feature = "std")]
1100 #[test]
1101 fn text_unit_latin1_swedish_characters() {
1102 let bytes = [0xF6, 0x6D, 0x6C, 0x61, 0x4D]; let text = TextUnit::new(&bytes);
1105 assert_eq!(String::from(text), "Malmö");
1106 }
1107
1108 #[cfg(feature = "std")]
1109 #[test]
1110 fn text_unit_latin1_superscript_three() {
1111 let bytes = [0x68, 0x2F, 0xB3, 0x6D]; let text = TextUnit::new(&bytes);
1114 assert_eq!(String::from(text), "m³/h");
1115 }
1116
1117 #[cfg(feature = "std")]
1118 #[test]
1119 fn text_unit_utf8_superscript_three() {
1120 let bytes = [0x68, 0x2F, 0xB3, 0xC2, 0x6D]; let text = TextUnit::new(&bytes);
1123 assert_eq!(String::from(text), "m³/h");
1124 }
1125
1126 #[test]
1127 fn test_invalid_data_information() {
1128 let data = [
1129 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
1130 ];
1131 let result = DataInformationBlock::try_from(data.as_slice());
1132 assert_eq!(result, Err(DataInformationError::DataTooLong));
1133 }
1134
1135 #[test]
1136 fn test_longest_data_information_not_too_long() {
1137 let data = [
1138 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x01,
1139 ];
1140 let result = DataInformationBlock::try_from(data.as_slice());
1141 assert_ne!(result, Err(DataInformationError::DataTooLong));
1142 }
1143
1144 #[test]
1145 fn test_short_data_information() {
1146 let data = [0xFF];
1147 let result = DataInformationBlock::try_from(data.as_slice());
1148 assert_eq!(result, Err(DataInformationError::DataTooShort));
1149 }
1150
1151 #[test]
1152 fn test_data_inforamtion1() {
1153 let data = [178, 1];
1154 let result = DataInformationBlock::try_from(data.as_slice());
1155 assert!(result.is_ok());
1156 assert_eq!(result.unwrap().get_size(), 2);
1157 }
1158
1159 #[test]
1160 fn test_bcd_to_value_unsigned() {
1161 let data = [0x54, 0x76, 0x98];
1162 let result = bcd_to_value_internal(&data, 6, 1, false);
1163 assert_eq!(
1164 result.unwrap(),
1165 Data {
1166 value: Some(DataType::Number(987654.0)),
1167 size: 3
1168 }
1169 );
1170 }
1171
1172 #[test]
1173 fn test_bcd_to_value_invalid() {
1174 let data = [0x5A, 0x76, 0x98];
1175 let result = bcd_to_value_internal(&data, 6, 1, false);
1176 assert!(matches!(
1177 result,
1178 Err(DataRecordError::DataInformationError(
1179 DataInformationError::InvalidValueInformation
1180 ))
1181 ));
1182 }
1183
1184 #[test]
1185 fn test_bcd_to_value_negative_sign_nibble() {
1186 let data = [0x18, 0x00, 0xF0];
1189 let result = bcd_to_value_internal(&data, 6, 1, false);
1190 assert_eq!(
1191 result.unwrap(),
1192 Data {
1193 value: Some(DataType::Number(-18.0)),
1194 size: 3
1195 }
1196 );
1197 }
1198
1199 #[test]
1200 fn test_bcd_to_value_negative_zero_is_positive_zero() {
1201 let data = [0x00, 0x00, 0xF0];
1202 let result = bcd_to_value_internal(&data, 6, 1, false);
1203 let Some(DataType::Number(value)) = result.unwrap().value else {
1204 panic!("expected a number");
1205 };
1206 assert!(value == 0.0 && value.is_sign_positive());
1207 }
1208
1209 #[test]
1210 fn test_bcd_to_value_sign_nibble_only_in_most_significant_digit() {
1211 let data = [0x18, 0xF0, 0x00];
1213 let result = bcd_to_value_internal(&data, 6, 1, false);
1214 assert!(matches!(
1215 result,
1216 Err(DataRecordError::DataInformationError(
1217 DataInformationError::InvalidValueInformation
1218 ))
1219 ));
1220 }
1221
1222 #[test]
1223 fn test_data_size_matches_parsed_size() {
1224 let payload = [0x01_u8; 80];
1227 let codings = [
1228 DataFieldCoding::NoData,
1229 DataFieldCoding::Integer8Bit,
1230 DataFieldCoding::Integer16Bit,
1231 DataFieldCoding::Integer24Bit,
1232 DataFieldCoding::Integer32Bit,
1233 DataFieldCoding::Real32Bit,
1234 DataFieldCoding::Integer48Bit,
1235 DataFieldCoding::Integer64Bit,
1236 DataFieldCoding::SelectionForReadout,
1237 DataFieldCoding::BCD2Digit,
1238 DataFieldCoding::BCD4Digit,
1239 DataFieldCoding::BCD6Digit,
1240 DataFieldCoding::BCD8Digit,
1241 DataFieldCoding::BCDDigit12,
1242 DataFieldCoding::DateTypeG,
1243 DataFieldCoding::DateTimeTypeF,
1244 DataFieldCoding::DateTimeTypeJ,
1245 DataFieldCoding::DateTimeTypeI,
1246 ];
1247
1248 for coding in codings {
1249 let parsed = coding.parse(&payload, None).expect("coding parses");
1250 assert_eq!(
1251 coding.data_size(&payload),
1252 Some(parsed.get_size()),
1253 "data_size disagrees with parse for {coding:?}"
1254 );
1255 }
1256
1257 for lvar in [0x03_u8, 0xC3, 0xD3, 0xE3, 0xF0, 0xF5, 0xF6] {
1259 let mut input = [0x00_u8; 80];
1260 input[0] = lvar;
1261 let parsed = DataFieldCoding::VariableLength
1262 .parse(&input, None)
1263 .expect("variable length parses");
1264 assert_eq!(
1265 DataFieldCoding::VariableLength.data_size(&input),
1266 Some(parsed.get_size()),
1267 "data_size disagrees with parse for LVAR {lvar:#04X}"
1268 );
1269 }
1270 }
1271
1272 #[test]
1273 fn test_integer_to_value_8_bit_positive() {
1274 let data = [0x7F];
1275 let result = integer_to_value_internal(&data, 1);
1276 assert_eq!(
1277 result,
1278 Data {
1279 value: Some(DataType::Number(127.0)),
1280 size: 1
1281 }
1282 );
1283 }
1284
1285 #[test]
1286 fn test_integer_to_value_8_bit_negative() {
1287 let data = [0xFF];
1288 let result = integer_to_value_internal(&data, 1);
1289 assert_eq!(
1290 result,
1291 Data {
1292 value: Some(DataType::Number(-1.0)),
1293 size: 1
1294 }
1295 );
1296 }
1297
1298 #[test]
1299 fn test_integer_to_value_64_bit_positive() {
1300 let data = [0xFA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00];
1301 let result = integer_to_value_internal(&data, 8);
1302 assert_eq!(
1303 result,
1304 Data {
1305 value: Some(DataType::Number(250.0)),
1306 size: 8
1307 }
1308 );
1309 }
1310
1311 #[test]
1312 fn test_integer_to_value_64_bit_negative() {
1313 let data = [0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF];
1314 let result = integer_to_value_internal(&data, 8);
1315 assert_eq!(
1316 result,
1317 Data {
1318 value: Some(DataType::Number(-1.0)),
1319 size: 8
1320 }
1321 );
1322 }
1323}