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