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(&'a [u8]),
404}
405#[cfg_attr(feature = "serde", derive(serde::Serialize))]
406#[derive(PartialEq, Debug, Clone)]
407#[cfg_attr(feature = "defmt", derive(defmt::Format))]
408pub struct Data<'a> {
409    pub value: Option<DataType<'a>>,
410    pub size: usize,
411}
412
413#[cfg(feature = "std")]
414impl<T: std::fmt::Display> std::fmt::Display for SingleEveryOrInvalid<T> {
415    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
416        match self {
417            SingleEveryOrInvalid::Single(value) => write!(f, "{}", value),
418            SingleEveryOrInvalid::Every() => write!(f, "Every"),
419            SingleEveryOrInvalid::Invalid() => write!(f, "Invalid"),
420        }
421    }
422}
423
424#[cfg(feature = "std")]
425impl std::fmt::Display for Data<'_> {
426    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
427        match &self.value {
428            Some(value) => match value {
429                DataType::Number(value) => write!(f, "{}", value),
430                DataType::LossyNumber(value) => write!(f, "{}", value),
431                DataType::Date(day, month, year) => write!(f, "{}/{}/{}", day, month, year),
432                DataType::DateTime(day, month, year, hour, minute) => {
433                    write!(f, "{}/{}/{} {}:{}:00", day, month, year, hour, minute)
434                }
435                DataType::DateTimeWithSeconds(day, month, year, hour, minute, second) => {
436                    write!(
437                        f,
438                        "{}/{}/{} {}:{}:{}",
439                        day, month, year, hour, minute, second
440                    )
441                }
442                DataType::Time(seconds, minutes, hours) => {
443                    write!(f, "{}:{}:{}", hours, minutes, seconds)
444                }
445                DataType::Text(text_unit) => {
446                    let text: String = (*text_unit).into();
447                    write!(f, "{}", text)
448                }
449                DataType::ManufacturerSpecific(data) => {
450                    write!(f, "Manufacturer Specific: {:?}", data)
451                }
452            },
453            None => write!(f, "No Data"),
454        }
455    }
456}
457
458impl Data<'_> {
459    #[must_use]
460    pub const fn get_size(&self) -> usize {
461        self.size
462    }
463}
464
465macro_rules! parse_single_or_every {
466    ($input:expr, $mask:expr, $all_value:expr, $shift:expr) => {
467        if $input & $mask == $all_value {
468            SingleEveryOrInvalid::Every()
469        } else {
470            SingleEveryOrInvalid::Single(($input & $mask) >> $shift)
471        }
472    };
473}
474
475macro_rules! parse_month {
476    ($input:expr) => {
477        match $input & 0xF {
478            0x1 => SingleEveryOrInvalid::Single(Month::January),
479            0x2 => SingleEveryOrInvalid::Single(Month::February),
480            0x3 => SingleEveryOrInvalid::Single(Month::March),
481            0x4 => SingleEveryOrInvalid::Single(Month::April),
482            0x5 => SingleEveryOrInvalid::Single(Month::May),
483            0x6 => SingleEveryOrInvalid::Single(Month::June),
484            0x7 => SingleEveryOrInvalid::Single(Month::July),
485            0x8 => SingleEveryOrInvalid::Single(Month::August),
486            0x9 => SingleEveryOrInvalid::Single(Month::September),
487            0xA => SingleEveryOrInvalid::Single(Month::October),
488            0xB => SingleEveryOrInvalid::Single(Month::November),
489            0xC => SingleEveryOrInvalid::Single(Month::December),
490            _ => SingleEveryOrInvalid::Invalid(),
491        }
492    };
493}
494
495macro_rules! parse_year {
496    ($input:expr, $mask_byte1:expr, $mask_byte2:expr, $all_value:expr) => {{
497        let byte1 = u16::from($input.get(1).copied().unwrap_or(0) & $mask_byte1);
498        let byte2 = u16::from($input.get(0).copied().unwrap_or(0) & $mask_byte2);
499        let year = byte1.wrapping_shr(1) | byte2.wrapping_shr(5);
500        if year == $all_value {
501            SingleEveryOrInvalid::Every()
502        } else {
503            SingleEveryOrInvalid::Single(year)
504        }
505    }};
506}
507fn bcd_to_value_internal(
508    data: &[u8],
509    num_digits: usize,
510    sign: i32,
511    lsb_order: bool,
512) -> Result<Data<'_>, DataRecordError> {
513    if data.len() < num_digits.div_ceil(2) {
514        return Err(DataRecordError::InsufficientData);
515    }
516
517    let mut data_value = 0.0;
518    let mut current_weight = 1.0;
519
520    for i in 0..num_digits {
521        let index = if lsb_order {
522            (num_digits - i - 1) / 2
523        } else {
524            i / 2
525        };
526        let byte = data.get(index).ok_or(DataRecordError::InsufficientData)?;
527
528        let digit = if i % 2 == 0 {
529            byte & 0x0F
530        } else {
531            (byte >> 4) & 0x0F
532        };
533
534        if digit > 9 {
535            return Err(DataRecordError::DataInformationError(
536                DataInformationError::InvalidValueInformation,
537            ));
538        }
539
540        data_value += f64::from(digit) * current_weight;
541        current_weight *= 10.0;
542    }
543
544    let signed_value = data_value * sign as f64;
545
546    Ok(Data {
547        value: Some(DataType::Number(signed_value)),
548        size: num_digits.div_ceil(2),
549    })
550}
551
552fn integer_to_value_internal(data: &[u8], byte_size: usize) -> Data<'_> {
553    let mut data_value = 0i64;
554    let mut shift = 0;
555    for byte in data.iter().take(if byte_size > 8 { 8 } else { byte_size }) {
556        data_value |= (*byte as i64) << shift;
557        shift += 8;
558    }
559
560    let msb = (data_value >> (shift - 1)) & 1;
561    let data_value = if byte_size < 8 && msb == 1 {
562        -((data_value ^ (2i64.pow(shift) - 1)) + 1)
563    } else {
564        data_value
565    };
566
567    let output = if byte_size > 8 {
568        DataType::LossyNumber(data_value as f64)
569    } else {
570        DataType::Number(data_value as f64)
571    };
572    Data {
573        value: Some(output),
574        size: byte_size,
575    }
576}
577
578impl DataFieldCoding {
579    pub fn parse<'a>(
580        &self,
581        input: &'a [u8],
582        fixed_data_header: Option<&'a LongTplHeader>,
583    ) -> Result<Data<'a>, DataRecordError> {
584        let lsb_order = fixed_data_header.map(|x| x.lsb_order).unwrap_or(false);
585
586        macro_rules! bcd_to_value {
587            ($data:expr, $num_digits:expr) => {{
588                bcd_to_value_internal($data, $num_digits, 1, lsb_order)
589            }};
590
591            ($data:expr, $num_digits:expr, $sign:expr) => {{
592                bcd_to_value_internal($data, $num_digits, $sign, lsb_order)
593            }};
594        }
595
596        macro_rules! integer_to_value {
597            ($data:expr, $byte_size:expr) => {{
598                if $data.len() < $byte_size {
599                    return Err(DataRecordError::InsufficientData);
600                }
601                Ok(integer_to_value_internal($data, $byte_size))
602            }};
603        }
604        match self {
605            Self::NoData => Ok(Data {
606                value: None,
607                size: 0,
608            }),
609            Self::Integer8Bit => integer_to_value!(input, 1),
610            Self::Integer16Bit => integer_to_value!(input, 2),
611            Self::Integer24Bit => integer_to_value!(input, 3),
612            Self::Integer32Bit => integer_to_value!(input, 4),
613            Self::Integer48Bit => integer_to_value!(input, 6),
614            Self::Integer64Bit => integer_to_value!(input, 8),
615
616            Self::Real32Bit => {
617                if input.len() < 4 {
618                    return Err(DataRecordError::InsufficientData);
619                }
620                if let Ok(x) = input
621                    .get(0..4)
622                    .ok_or(DataRecordError::InsufficientData)?
623                    .try_into()
624                {
625                    let x: [u8; 4] = x;
626                    Ok(Data {
627                        value: Some(DataType::Number(f64::from(f32::from_le_bytes(x)))),
628                        size: 4,
629                    })
630                } else {
631                    Err(DataRecordError::InsufficientData)
632                }
633            }
634
635            Self::SelectionForReadout => Ok(Data {
636                value: None,
637                size: 0,
638            }),
639
640            Self::BCD2Digit => bcd_to_value!(input, 2),
641            Self::BCD4Digit => bcd_to_value!(input, 4),
642            Self::BCD6Digit => bcd_to_value!(input, 6),
643            Self::BCD8Digit => bcd_to_value!(input, 8),
644            Self::BCDDigit12 => bcd_to_value!(input, 12),
645
646            Self::VariableLength => {
647                let mut length = *input.first().ok_or(DataRecordError::InsufficientData)?;
648                match length {
649                    0x00..=0xBF => Ok(Data {
650                        value: Some(DataType::Text(TextUnit::new(
651                            input
652                                .get(1..(1 + length as usize))
653                                .ok_or(DataRecordError::InsufficientData)?,
654                        ))),
655                        size: length as usize + 1,
656                    }),
657                    0xC0..=0xC9 => {
658                        length -= 0xC0;
659                        let bytes = input
660                            .get(1..(1 + length as usize))
661                            .ok_or(DataRecordError::InsufficientData)?;
662                        match bcd_to_value!(bytes, 2 * length as usize) {
663                            Ok(data) => Ok(Data {
664                                value: data.value,
665                                size: data.size + 1,
666                            }),
667                            Err(err) => Err(err),
668                        }
669                    }
670                    0xD0..=0xD9 => {
671                        length -= 0xD0;
672                        let bytes = input
673                            .get(1..(1 + length as usize))
674                            .ok_or(DataRecordError::InsufficientData)?;
675                        match bcd_to_value!(bytes, 2 * length as usize, -1) {
676                            Ok(data) => Ok(Data {
677                                value: data.value,
678                                size: data.size + 1,
679                            }),
680                            Err(err) => Err(err),
681                        }
682                    }
683                    0xE0..=0xEF => {
684                        length -= 0xE0;
685                        let bytes = input
686                            .get(1..(1 + length as usize))
687                            .ok_or(DataRecordError::InsufficientData)?;
688                        match integer_to_value!(bytes, length as usize) {
689                            Ok(data) => Ok(Data {
690                                value: data.value,
691                                size: data.size + 1,
692                            }),
693                            Err(err) => Err(err),
694                        }
695                    }
696                    0xF0..=0xF4 => {
697                        length -= 0xEC;
698                        let bytes = input
699                            .get(1..(1 + 4 * length as usize))
700                            .ok_or(DataRecordError::InsufficientData)?;
701                        match integer_to_value!(bytes, 4 * length as usize) {
702                            Ok(data) => Ok(Data {
703                                value: data.value,
704                                size: data.size + 1,
705                            }),
706                            Err(err) => Err(err),
707                        }
708                    }
709                    0xF5 => {
710                        let bytes = input
711                            .get(1..(1 + 48_usize))
712                            .ok_or(DataRecordError::InsufficientData)?;
713                        match integer_to_value!(bytes, 48_usize) {
714                            Ok(data) => Ok(Data {
715                                value: data.value,
716                                size: data.size + 1,
717                            }),
718                            Err(err) => Err(err),
719                        }
720                    }
721                    0xF6 => {
722                        let bytes = input
723                            .get(1..(1 + 64_usize))
724                            .ok_or(DataRecordError::InsufficientData)?;
725                        match integer_to_value!(bytes, 64_usize) {
726                            Ok(data) => Ok(Data {
727                                value: data.value,
728                                size: data.size + 1,
729                            }),
730                            Err(err) => Err(err),
731                        }
732                    }
733                    _ => Err(DataRecordError::DataInformationError(
734                        DataInformationError::Unimplemented {
735                            feature: "Variable length parsing for reserved length values",
736                        },
737                    )),
738                }
739            }
740
741            Self::SpecialFunctions(code) => match code {
742                SpecialFunctions::ManufacturerSpecific | SpecialFunctions::MoreRecordsFollow => {
743                    Ok(Data {
744                        value: Some(DataType::ManufacturerSpecific(input)),
745                        size: input.len(),
746                    })
747                }
748                SpecialFunctions::IdleFiller => Ok(Data {
749                    value: None,
750                    size: 0,
751                }),
752                SpecialFunctions::GlobalReadoutRequest => Ok(Data {
753                    value: None,
754                    size: 0,
755                }),
756                SpecialFunctions::Reserved => Err(DataRecordError::DataInformationError(
757                    DataInformationError::InvalidValueInformation,
758                )),
759            },
760
761            Self::DateTypeG => {
762                let day = parse_single_or_every!(
763                    input.first().ok_or(DataRecordError::InsufficientData)?,
764                    0x1F,
765                    0,
766                    0
767                );
768                let month = parse_month!(input.get(1).ok_or(DataRecordError::InsufficientData)?);
769                let year = parse_year!(input, 0xF0, 0xE0, 0x7F);
770
771                Ok(Data {
772                    value: Some(DataType::Date(day, month, year)),
773                    size: 2,
774                })
775            }
776            Self::DateTimeTypeF => {
777                let minutes = parse_single_or_every!(
778                    input.first().ok_or(DataRecordError::InsufficientData)?,
779                    0x3F,
780                    0x3F,
781                    0
782                );
783                let hour = parse_single_or_every!(
784                    input.get(1).ok_or(DataRecordError::InsufficientData)?,
785                    0x1F,
786                    0x1F,
787                    0
788                );
789                let day = parse_single_or_every!(
790                    input.get(2).ok_or(DataRecordError::InsufficientData)?,
791                    0x1F,
792                    0x1F,
793                    0
794                );
795                let month = parse_month!(input.get(3).ok_or(DataRecordError::InsufficientData)?);
796                let year = parse_year!(input, 0xF0, 0xE0, 0x7F);
797
798                Ok(Data {
799                    value: Some(DataType::DateTime(day, month, year, hour, minutes)),
800                    size: 4,
801                })
802            }
803            Self::DateTimeTypeJ => {
804                let seconds = parse_single_or_every!(
805                    input.first().ok_or(DataRecordError::InsufficientData)?,
806                    0x3F,
807                    0x3F,
808                    0
809                );
810                let minutes = parse_single_or_every!(
811                    input.get(1).ok_or(DataRecordError::InsufficientData)?,
812                    0x3F,
813                    0x3F,
814                    0
815                );
816                let hours = parse_single_or_every!(
817                    input.get(2).ok_or(DataRecordError::InsufficientData)?,
818                    0x1F,
819                    0x1F,
820                    0
821                );
822
823                Ok(Data {
824                    value: Some(DataType::Time(seconds, minutes, hours)),
825                    size: 4,
826                })
827            }
828            Self::DateTimeTypeI => {
829                // note: more information can be extracted from the data,
830                // however, because this data can be derived from the other data that is
831                // that is extracted, it is not necessary to extract it.
832
833                let seconds = parse_single_or_every!(
834                    input.first().ok_or(DataRecordError::InsufficientData)?,
835                    0x3F,
836                    0x3F,
837                    0
838                );
839
840                let minutes = parse_single_or_every!(
841                    input.get(1).ok_or(DataRecordError::InsufficientData)?,
842                    0x3F,
843                    0x3F,
844                    0
845                );
846
847                let hours = parse_single_or_every!(
848                    input.get(2).ok_or(DataRecordError::InsufficientData)?,
849                    0x1F,
850                    0x1F,
851                    0
852                );
853                let days = parse_single_or_every!(
854                    input.get(3).ok_or(DataRecordError::InsufficientData)?,
855                    0x1F,
856                    0x1F,
857                    0
858                );
859                let months = parse_month!(input.get(4).ok_or(DataRecordError::InsufficientData)?);
860                let year = parse_year!(input, 0xF0, 0xE0, 0x7F);
861
862                Ok(Data {
863                    value: Some(DataType::DateTimeWithSeconds(
864                        days, months, year, hours, minutes, seconds,
865                    )),
866                    size: 6,
867                })
868            }
869        }
870    }
871}
872
873impl DataInformation {
874    #[must_use]
875    pub const fn get_size(&self) -> usize {
876        self.size
877    }
878}
879#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
880#[derive(Debug, Clone, Copy, PartialEq)]
881#[cfg_attr(feature = "defmt", derive(defmt::Format))]
882#[non_exhaustive]
883pub enum FunctionField {
884    InstantaneousValue,
885    MaximumValue,
886    MinimumValue,
887    ValueDuringErrorState,
888}
889
890#[cfg(feature = "std")]
891impl std::fmt::Display for FunctionField {
892    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
893        match self {
894            FunctionField::InstantaneousValue => write!(f, "Inst"),
895            FunctionField::MaximumValue => write!(f, "Max"),
896            FunctionField::MinimumValue => write!(f, "Min"),
897            FunctionField::ValueDuringErrorState => write!(f, "Value During Error State"),
898        }
899    }
900}
901#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
902#[derive(Debug, Clone, Copy, PartialEq)]
903#[cfg_attr(feature = "defmt", derive(defmt::Format))]
904#[non_exhaustive]
905pub enum SpecialFunctions {
906    ManufacturerSpecific,
907    MoreRecordsFollow,
908    IdleFiller,
909    Reserved,
910    GlobalReadoutRequest,
911}
912
913#[cfg_attr(feature = "defmt", derive(defmt::Format))]
914pub struct Value {
915    pub data: f64,
916    pub byte_size: usize,
917}
918
919#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
920#[derive(Debug, Clone, Copy, PartialEq)]
921#[cfg_attr(feature = "defmt", derive(defmt::Format))]
922#[non_exhaustive]
923pub enum DataFieldCoding {
924    NoData,
925    Integer8Bit,
926    Integer16Bit,
927    Integer24Bit,
928    Integer32Bit,
929    Real32Bit,
930    Integer48Bit,
931    Integer64Bit,
932    SelectionForReadout,
933    BCD2Digit,
934    BCD4Digit,
935    BCD6Digit,
936    BCD8Digit,
937    VariableLength,
938    BCDDigit12,
939    SpecialFunctions(SpecialFunctions),
940    DateTypeG,
941    DateTimeTypeF,
942    DateTimeTypeJ,
943    DateTimeTypeI,
944}
945
946#[cfg(feature = "std")]
947impl std::fmt::Display for DataFieldCoding {
948    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
949        match self {
950            DataFieldCoding::NoData => write!(f, "No Data"),
951            DataFieldCoding::Integer8Bit => write!(f, "8-bit Integer"),
952            DataFieldCoding::Integer16Bit => write!(f, "16-bit Integer"),
953            DataFieldCoding::Integer24Bit => write!(f, "24-bit Integer"),
954            DataFieldCoding::Integer32Bit => write!(f, "32-bit Integer"),
955            DataFieldCoding::Real32Bit => write!(f, "32-bit Real"),
956            DataFieldCoding::Integer48Bit => write!(f, "48-bit Integer"),
957            DataFieldCoding::Integer64Bit => write!(f, "64-bit Integer"),
958            DataFieldCoding::SelectionForReadout => write!(f, "Selection for Readout"),
959            DataFieldCoding::BCD2Digit => write!(f, "BCD 2-digit"),
960            DataFieldCoding::BCD4Digit => write!(f, "BCD 4-digit"),
961            DataFieldCoding::BCD6Digit => write!(f, "BCD 6-digit"),
962            DataFieldCoding::BCD8Digit => write!(f, "BCD 8-digit"),
963            DataFieldCoding::VariableLength => write!(f, "Variable Length"),
964            DataFieldCoding::BCDDigit12 => write!(f, "BCD 12-digit"),
965            DataFieldCoding::DateTypeG => write!(f, "Date Type G"),
966            DataFieldCoding::DateTimeTypeF => write!(f, "Date Time Type F"),
967            DataFieldCoding::DateTimeTypeJ => write!(f, "Date Time Type J"),
968            DataFieldCoding::DateTimeTypeI => write!(f, "Date Time Type I"),
969            DataFieldCoding::SpecialFunctions(code) => write!(f, "Special Functions ({:?})", code),
970        }
971    }
972}
973
974#[cfg(test)]
975mod tests {
976
977    use super::*;
978    #[test]
979    fn test_data_information() {
980        let data = [0x13_u8];
981        let result = DataInformationBlock::try_from(data.as_slice());
982        let result = DataInformation::try_from(&result.unwrap());
983        assert_eq!(
984            result,
985            Ok(DataInformation {
986                storage_number: 0,
987                device: 0,
988                tariff: 0,
989                function_field: FunctionField::MaximumValue,
990                data_field_coding: DataFieldCoding::Integer24Bit,
991                data_information_extension: None,
992                size: 1,
993            })
994        );
995    }
996
997    #[test]
998    fn test_complex_data_information() {
999        let data = [0xc4, 0x80, 0x40];
1000        let result = DataInformationBlock::try_from(data.as_slice());
1001        let result = DataInformation::try_from(&result.unwrap());
1002        assert_eq!(
1003            result,
1004            Ok(DataInformation {
1005                storage_number: 1,
1006                device: 2,
1007                tariff: 0,
1008                function_field: FunctionField::InstantaneousValue,
1009                data_field_coding: DataFieldCoding::Integer32Bit,
1010                data_information_extension: None,
1011                size: 3,
1012            })
1013        );
1014    }
1015
1016    #[test]
1017    fn reverse_text_unit() {
1018        let original_value = [0x6c, 0x61, 0x67, 0x69];
1019        let parsed = TextUnit::new(&original_value);
1020        assert_eq!(&parsed, "igal");
1021    }
1022
1023    #[cfg(feature = "std")]
1024    #[test]
1025    fn text_unit_latin1_swedish_characters() {
1026        // "Malmö" in Latin-1 (reversed byte order per M-Bus)
1027        let bytes = [0xF6, 0x6D, 0x6C, 0x61, 0x4D]; // ö m l a M
1028        let text = TextUnit::new(&bytes);
1029        assert_eq!(String::from(text), "Malmö");
1030    }
1031
1032    #[cfg(feature = "std")]
1033    #[test]
1034    fn text_unit_latin1_superscript_three() {
1035        // "m³/h" in Latin-1 (reversed byte order per M-Bus)
1036        let bytes = [0x68, 0x2F, 0xB3, 0x6D]; // h / ³ m
1037        let text = TextUnit::new(&bytes);
1038        assert_eq!(String::from(text), "m³/h");
1039    }
1040
1041    #[cfg(feature = "std")]
1042    #[test]
1043    fn text_unit_utf8_superscript_three() {
1044        // "m³/h" in UTF-8 (reversed byte order per M-Bus)
1045        let bytes = [0x68, 0x2F, 0xB3, 0xC2, 0x6D]; // h / UTF-8(³) m
1046        let text = TextUnit::new(&bytes);
1047        assert_eq!(String::from(text), "m³/h");
1048    }
1049
1050    #[test]
1051    fn test_invalid_data_information() {
1052        let data = [
1053            0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
1054        ];
1055        let result = DataInformationBlock::try_from(data.as_slice());
1056        assert_eq!(result, Err(DataInformationError::DataTooLong));
1057    }
1058
1059    #[test]
1060    fn test_longest_data_information_not_too_long() {
1061        let data = [
1062            0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x01,
1063        ];
1064        let result = DataInformationBlock::try_from(data.as_slice());
1065        assert_ne!(result, Err(DataInformationError::DataTooLong));
1066    }
1067
1068    #[test]
1069    fn test_short_data_information() {
1070        let data = [0xFF];
1071        let result = DataInformationBlock::try_from(data.as_slice());
1072        assert_eq!(result, Err(DataInformationError::DataTooShort));
1073    }
1074
1075    #[test]
1076    fn test_data_inforamtion1() {
1077        let data = [178, 1];
1078        let result = DataInformationBlock::try_from(data.as_slice());
1079        assert!(result.is_ok());
1080        assert_eq!(result.unwrap().get_size(), 2);
1081    }
1082
1083    #[test]
1084    fn test_bcd_to_value_unsigned() {
1085        let data = [0x54, 0x76, 0x98];
1086        let result = bcd_to_value_internal(&data, 6, 1, false);
1087        assert_eq!(
1088            result.unwrap(),
1089            Data {
1090                value: Some(DataType::Number(987654.0)),
1091                size: 3
1092            }
1093        );
1094    }
1095
1096    #[test]
1097    fn test_bcd_to_value_invalid() {
1098        let data = [0x5A, 0x76, 0x98];
1099        let result = bcd_to_value_internal(&data, 6, 1, false);
1100        assert!(matches!(
1101            result,
1102            Err(DataRecordError::DataInformationError(
1103                DataInformationError::InvalidValueInformation
1104            ))
1105        ));
1106    }
1107
1108    #[test]
1109    fn test_integer_to_value_8_bit_positive() {
1110        let data = [0x7F];
1111        let result = integer_to_value_internal(&data, 1);
1112        assert_eq!(
1113            result,
1114            Data {
1115                value: Some(DataType::Number(127.0)),
1116                size: 1
1117            }
1118        );
1119    }
1120
1121    #[test]
1122    fn test_integer_to_value_8_bit_negative() {
1123        let data = [0xFF];
1124        let result = integer_to_value_internal(&data, 1);
1125        assert_eq!(
1126            result,
1127            Data {
1128                value: Some(DataType::Number(-1.0)),
1129                size: 1
1130            }
1131        );
1132    }
1133
1134    #[test]
1135    fn test_integer_to_value_64_bit_positive() {
1136        let data = [0xFA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00];
1137        let result = integer_to_value_internal(&data, 8);
1138        assert_eq!(
1139            result,
1140            Data {
1141                value: Some(DataType::Number(250.0)),
1142                size: 8
1143            }
1144        );
1145    }
1146
1147    #[test]
1148    fn test_integer_to_value_64_bit_negative() {
1149        let data = [0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF];
1150        let result = integer_to_value_internal(&data, 8);
1151        assert_eq!(
1152            result,
1153            Data {
1154                value: Some(DataType::Number(-1.0)),
1155                size: 8
1156            }
1157        );
1158    }
1159}