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