Skip to main content

m_bus_application_layer/
data_information.rs

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