Skip to main content

m_bus_application_layer/
data_information.rs

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!(), // This case should never occur due to the 4-bit width
255        };
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        // EN 13757-3: Fh in the most significant digit marks a negative value.
540        // It is a sign marker, not a digit, and contributes nothing to the
541        // magnitude.
542        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    // Avoid -0.0, which would render as "-0.000000".
559    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    /// Returns how many bytes the data field occupies, without decoding it.
599    ///
600    /// The length of every coding but [`DataFieldCoding::VariableLength`] is
601    /// fixed by the DIF alone, so `input` is only read for the LVAR byte of a
602    /// variable-length field. Returns `None` when the length cannot be
603    /// determined, which is the case for a missing or reserved LVAR byte and
604    /// for a reserved special function.
605    ///
606    /// This is what [`DataFieldCoding::parse`] would report as
607    /// [`Data::get_size`] on success, so a record whose contents fail to decode
608    /// can still be stepped over.
609    #[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                // note: more information can be extracted from the data,
894                // however, because this data can be derived from the other data that is
895                // that is extracted, it is not necessary to extract it.
896
897                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        // "Malmö" in Latin-1 (reversed byte order per M-Bus)
1103        let bytes = [0xF6, 0x6D, 0x6C, 0x61, 0x4D]; // ö m l a M
1104        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        // "m³/h" in Latin-1 (reversed byte order per M-Bus)
1112        let bytes = [0x68, 0x2F, 0xB3, 0x6D]; // h / ³ m
1113        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        // "m³/h" in UTF-8 (reversed byte order per M-Bus)
1121        let bytes = [0x68, 0x2F, 0xB3, 0xC2, 0x6D]; // h / UTF-8(³) m
1122        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        // EN 13757-3: Fh in the most significant digit is a sign marker.
1187        // SLB_CF-Compact-Integral-MK-MaXX carries -18 as `18 00 F0`.
1188        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        // An Fh anywhere but the top digit is still invalid BCD.
1212        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        // `data_size` is the resynchronisation path for records that fail to
1225        // decode, so it must not drift from what `parse` consumes.
1226        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        // Variable length: the LVAR byte drives both.
1258        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}