Skip to main content

m_bus_application_layer/
value_information.rs

1#[cfg(feature = "std")]
2use std::fmt;
3
4use super::data_information::DataInformationError;
5use arrayvec::ArrayVec;
6
7const MAX_VIFE_RECORDS: usize = 10;
8
9#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
10#[derive(Debug, PartialEq, Copy, Clone)]
11#[cfg_attr(feature = "defmt", derive(defmt::Format))]
12pub struct Unit {
13    pub name: UnitName,
14    pub exponent: i32,
15}
16macro_rules! unit {
17    ($name:ident) => {
18        Unit {
19            name: UnitName::$name,
20            exponent: 1,
21        }
22    };
23    ($name:ident ^ $exponent:literal) => {
24        Unit {
25            name: UnitName::$name,
26            exponent: $exponent,
27        }
28    };
29}
30
31impl TryFrom<&[u8]> for ValueInformationBlock {
32    type Error = DataInformationError;
33
34    fn try_from(data: &[u8]) -> Result<Self, DataInformationError> {
35        let mut vife = ArrayVec::<ValueInformationFieldExtension, MAX_VIFE_RECORDS>::new();
36        let vif =
37            ValueInformationField::from(*data.first().ok_or(DataInformationError::DataTooShort)?);
38        let mut plaintext_vife: Option<ArrayVec<char, 9>> = None;
39
40        #[cfg(not(feature = "plaintext-before-extension"))]
41        let standard_plaintex_vib = true;
42        #[cfg(feature = "plaintext-before-extension")]
43        let standard_plaintex_vib = false;
44
45        if !standard_plaintex_vib && vif.value_information_contains_ascii() {
46            plaintext_vife = Some(extract_plaintext_vife(
47                data.get(1..).ok_or(DataInformationError::DataTooShort)?,
48            )?);
49        }
50
51        if vif.has_extension() {
52            let mut offset = 1;
53            while offset < data.len() {
54                let vife_data = *data.get(offset).ok_or(DataInformationError::DataTooShort)?;
55                let current_vife = ValueInformationFieldExtension {
56                    data: vife_data,
57                    coding: match (offset, vife_data) {
58                        (0, 0xFB) => ValueInformationFieldExtensionCoding::MainVIFCodeExtension,
59                        (0, 0xFC) => {
60                            ValueInformationFieldExtensionCoding::AlternateVIFCodeExtension
61                        }
62                        (0, 0xEF) => {
63                            ValueInformationFieldExtensionCoding::ReservedAlternateVIFCodeExtension
64                        }
65                        _ => ValueInformationFieldExtensionCoding::ComninableOrthogonalVIFECodeExtension,
66                    },
67                };
68                let has_extension = current_vife.has_extension();
69                vife.push(current_vife);
70                offset += 1;
71                if !has_extension {
72                    break;
73                }
74                if vife.len() > MAX_VIFE_RECORDS {
75                    return Err(DataInformationError::InvalidValueInformation);
76                }
77            }
78            if standard_plaintex_vib && vif.value_information_contains_ascii() {
79                plaintext_vife = Some(extract_plaintext_vife(
80                    data.get(offset..)
81                        .ok_or(DataInformationError::DataTooShort)?,
82                )?);
83            }
84        }
85
86        Ok(Self {
87            value_information: vif,
88            value_information_extension: if vife.is_empty() { None } else { Some(vife) },
89            plaintext_vife,
90        })
91    }
92}
93
94fn extract_plaintext_vife(data: &[u8]) -> Result<ArrayVec<char, 9>, DataInformationError> {
95    let ascii_length = *data.first().ok_or(DataInformationError::DataTooShort)? as usize;
96    let mut ascii = ArrayVec::<char, 9>::new();
97    for item in data
98        .get(1..=ascii_length)
99        .ok_or(DataInformationError::DataTooShort)?
100    {
101        ascii.push(*item as char);
102    }
103    Ok(ascii)
104}
105
106#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
107#[derive(Debug, PartialEq, Clone)]
108pub struct ValueInformationBlock {
109    pub value_information: ValueInformationField,
110    pub value_information_extension:
111        Option<ArrayVec<ValueInformationFieldExtension, MAX_VIFE_RECORDS>>,
112    pub plaintext_vife: Option<ArrayVec<char, 9>>,
113}
114
115#[cfg(feature = "defmt")]
116impl defmt::Format for ValueInformationBlock {
117    fn format(&self, f: defmt::Formatter) {
118        defmt::write!(
119            f,
120            "ValueInformationBlock{{ value_information: {:?}",
121            self.value_information
122        );
123        if let Some(ext) = &self.value_information_extension {
124            defmt::write!(f, ", value_information_extension: [");
125            for (i, vife) in ext.iter().enumerate() {
126                if i != 0 {
127                    defmt::write!(f, ", ");
128                }
129                defmt::write!(f, "{:?}", vife);
130            }
131            defmt::write!(f, "]");
132        }
133        if let Some(text) = &self.plaintext_vife {
134            defmt::write!(f, ", plaintext_vife: ");
135            for c in text {
136                defmt::write!(f, "{}", c);
137            }
138        }
139        defmt::write!(f, " }}");
140    }
141}
142#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
143#[derive(Debug, PartialEq, Clone)]
144#[cfg_attr(feature = "defmt", derive(defmt::Format))]
145pub struct ValueInformationField {
146    pub data: u8,
147}
148
149impl ValueInformationField {
150    const fn value_information_contains_ascii(&self) -> bool {
151        self.data == 0x7C || self.data == 0xFC
152    }
153}
154#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
155#[derive(Debug, PartialEq, Clone)]
156#[cfg_attr(feature = "defmt", derive(defmt::Format))]
157pub struct ValueInformationFieldExtension {
158    pub data: u8,
159    pub coding: ValueInformationFieldExtensionCoding,
160}
161
162impl From<&ValueInformationField> for ValueInformationCoding {
163    fn from(value_information: &ValueInformationField) -> Self {
164        match value_information.data {
165            0x00..=0x7B | 0x80..=0xFA => Self::Primary,
166            0x7C | 0xFC => Self::PlainText,
167            0xFD => Self::MainVIFExtension,
168            0xFB => Self::AlternateVIFExtension,
169            0x7E => Self::ManufacturerSpecific,
170            0xFE => Self::ManufacturerSpecific,
171            0x7F => Self::ManufacturerSpecific,
172            0xFF => Self::ManufacturerSpecific,
173            _ => unreachable!("Invalid value information: {:X}", value_information.data),
174        }
175    }
176}
177
178impl ValueInformationField {
179    const fn has_extension(&self) -> bool {
180        self.data & 0x80 != 0
181    }
182}
183
184impl ValueInformationFieldExtension {
185    const fn has_extension(&self) -> bool {
186        self.data & 0x80 != 0
187    }
188}
189
190#[derive(Debug, Clone, Copy, PartialEq)]
191#[cfg_attr(feature = "defmt", derive(defmt::Format))]
192#[non_exhaustive]
193pub enum ValueInformationCoding {
194    Primary,
195    PlainText,
196    MainVIFExtension,
197    AlternateVIFExtension,
198    ManufacturerSpecific,
199}
200#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
201#[derive(Debug, PartialEq, Clone, Copy)]
202#[cfg_attr(feature = "defmt", derive(defmt::Format))]
203#[non_exhaustive]
204pub enum ValueInformationFieldExtensionCoding {
205    MainVIFCodeExtension,
206    AlternateVIFCodeExtension,
207    ReservedAlternateVIFCodeExtension,
208    ComninableOrthogonalVIFECodeExtension,
209}
210
211impl ValueInformationBlock {
212    #[must_use]
213    pub const fn get_size(&self) -> usize {
214        let mut size = 1;
215        if let Some(vife) = &self.value_information_extension {
216            size += vife.len();
217        }
218        if let Some(plaintext_vife) = &self.plaintext_vife {
219            // 1 byte for the length of the ASCII string
220            size += plaintext_vife.len() + 1;
221        }
222        size
223    }
224}
225
226impl TryFrom<&ValueInformationBlock> for ValueInformation {
227    type Error = DataInformationError;
228
229    fn try_from(
230        value_information_block: &ValueInformationBlock,
231    ) -> Result<Self, DataInformationError> {
232        let mut units = ArrayVec::<Unit, 10>::new();
233        let mut labels = ArrayVec::<ValueLabel, 10>::new();
234        let mut decimal_scale_exponent: isize = 0;
235        let mut decimal_offset_exponent = 0;
236        match ValueInformationCoding::from(&value_information_block.value_information) {
237            ValueInformationCoding::Primary => {
238                match value_information_block.value_information.data & 0x7F {
239                    0x00..=0x07 => {
240                        units.push(unit!(Watt));
241                        units.push(unit!(Hour));
242                        decimal_scale_exponent =
243                            (value_information_block.value_information.data & 0b111) as isize - 3;
244                    }
245                    0x08..=0x0F => {
246                        units.push(unit!(Joul));
247                        decimal_scale_exponent =
248                            (value_information_block.value_information.data & 0b111) as isize;
249                    }
250                    0x10..=0x17 => {
251                        units.push(unit!(Meter ^ 3));
252                        labels.push(ValueLabel::Volume);
253                        decimal_scale_exponent =
254                            (value_information_block.value_information.data & 0b111) as isize - 6;
255                    }
256                    0x18..=0x1F => {
257                        units.push(unit!(Kilogram));
258                        decimal_scale_exponent =
259                            (value_information_block.value_information.data & 0b111) as isize - 3;
260                    }
261                    0x20 | 0x24 => {
262                        units.push(unit!(Second));
263                    }
264                    0x21 | 0x25 => units.push(unit!(Meter)),
265                    0x22 | 0x26 => units.push(unit!(Hour)),
266                    0x23 | 0x27 => units.push(unit!(Day)),
267                    0x28..=0x2F => {
268                        units.push(unit!(Watt));
269                        decimal_scale_exponent +=
270                            (value_information_block.value_information.data & 0b111) as isize - 3;
271                    }
272                    0x30..=0x37 => {
273                        units.push(unit!(Joul));
274                        units.push(unit!(Hour ^ -1));
275                        decimal_scale_exponent +=
276                            (value_information_block.value_information.data & 0b111) as isize;
277                    }
278                    0x38..=0x3F => {
279                        units.push(unit!(Meter ^ 3));
280                        units.push(unit!(Hour ^ -1));
281                        decimal_scale_exponent +=
282                            (value_information_block.value_information.data & 0b111) as isize - 6;
283                    }
284                    0x40..=0x47 => {
285                        units.push(unit!(Meter ^ 3));
286                        units.push(unit!(Minute ^ -1));
287                        decimal_scale_exponent +=
288                            (value_information_block.value_information.data & 0b111) as isize - 7;
289                    }
290                    0x48..=0x4F => {
291                        units.push(unit!(Meter ^ 3));
292                        units.push(unit!(Second ^ -1));
293                        decimal_scale_exponent +=
294                            (value_information_block.value_information.data & 0b111) as isize - 9;
295                    }
296                    0x50..=0x57 => {
297                        units.push(unit!(Kilogram ^ 3));
298                        units.push(unit!(Hour ^ -1));
299                        decimal_scale_exponent +=
300                            (value_information_block.value_information.data & 0b111) as isize - 3;
301                    }
302                    0x58..=0x5F | 0x64..=0x67 => {
303                        units.push(unit!(Celsius));
304                        decimal_scale_exponent +=
305                            (value_information_block.value_information.data & 0b11) as isize - 3;
306                    }
307                    0x60..=0x63 => {
308                        units.push(unit!(Kelvin));
309                        decimal_scale_exponent +=
310                            (value_information_block.value_information.data & 0b11) as isize - 3;
311                    }
312                    0x68..=0x6B => {
313                        units.push(unit!(Bar));
314                        decimal_scale_exponent +=
315                            (value_information_block.value_information.data & 0b11) as isize - 3;
316                    }
317                    0x6C => labels.push(ValueLabel::Date),
318                    0x6D => labels.push(ValueLabel::DateTime),
319                    0x6E => labels.push(ValueLabel::DimensionlessHCA),
320                    0x70..=0x73 => labels.push(ValueLabel::AveragingDuration),
321                    0x74..=0x77 => labels.push(ValueLabel::ActualityDuration),
322                    0x78 => labels.push(ValueLabel::FabricationNumber),
323                    0x79 => labels.push(ValueLabel::EnhancedIdentification),
324                    0x7A => labels.push(ValueLabel::Address),
325                    0x7B => {}
326
327                    _ => {
328                        return Err(DataInformationError::Unimplemented {
329                            feature: "Primary value information unit codes (partial)",
330                        })
331                    }
332                };
333                /* consume orthogonal vife */
334                consume_orthhogonal_vife(
335                    value_information_block,
336                    &mut labels,
337                    &mut units,
338                    &mut decimal_scale_exponent,
339                    &mut decimal_offset_exponent,
340                );
341            }
342            ValueInformationCoding::MainVIFExtension => {
343                let vife = value_information_block
344                    .value_information_extension
345                    .as_ref()
346                    .ok_or(Self::Error::InvalidValueInformation)?;
347
348                let first_vife_data = vife.first().ok_or(DataInformationError::DataTooShort)?.data;
349                let second_vife_data = vife.get(1).map(|s| s.data);
350                match first_vife_data & 0x7F {
351                    0x00..=0x03 => {
352                        units.push(unit!(LocalMoneyCurrency));
353                        labels.push(ValueLabel::Credit);
354                        decimal_scale_exponent = (first_vife_data & 0b11) as isize - 3;
355                    }
356                    0x04..=0x07 => {
357                        units.push(unit!(LocalMoneyCurrency));
358                        labels.push(ValueLabel::Debit);
359                        decimal_scale_exponent = (first_vife_data & 0b11) as isize - 3;
360                    }
361                    0x08 => labels.push(ValueLabel::UniqueMessageIdentificationOrAccessNumber),
362                    0x09 => labels.push(ValueLabel::DeviceType),
363                    0x0A => labels.push(ValueLabel::Manufacturer),
364                    0x0B => labels.push(ValueLabel::ParameterSetIdentification),
365                    0x0C => labels.push(ValueLabel::ModelOrVersion),
366                    0x0D => labels.push(ValueLabel::HardwareVersion),
367                    0x0E => labels.push(ValueLabel::MetrologyFirmwareVersion),
368                    0x0F => labels.push(ValueLabel::OtherSoftwareVersion),
369                    0x10 => labels.push(ValueLabel::CustomerLocation),
370                    0x11 => labels.push(ValueLabel::Customer),
371                    0x12 => labels.push(ValueLabel::AccessCodeUser),
372                    0x13 => labels.push(ValueLabel::AccessCodeOperator),
373                    0x14 => labels.push(ValueLabel::AccessCodeSystemOperator),
374                    0x15 => labels.push(ValueLabel::AccessCodeDeveloper),
375                    0x16 => labels.push(ValueLabel::Password),
376                    0x17 => labels.push(ValueLabel::ErrorFlags),
377                    0x18 => labels.push(ValueLabel::ErrorMask),
378                    0x19 => labels.push(ValueLabel::SecurityKey),
379                    0x1A => {
380                        labels.push(ValueLabel::DigitalOutput);
381                        labels.push(ValueLabel::Binary);
382                    }
383                    0x1B => {
384                        labels.push(ValueLabel::DigitalInput);
385                        labels.push(ValueLabel::Binary);
386                    }
387                    0x1C => {
388                        units.push(unit!(Symbol));
389                        units.push(unit!(Second ^ -1));
390                        labels.push(ValueLabel::BaudRate);
391                    }
392                    0x1D => {
393                        units.push(unit!(BitTime));
394                        labels.push(ValueLabel::ResponseDelayTime);
395                    }
396                    0x1E => labels.push(ValueLabel::Retry),
397                    0x1F => labels.push(ValueLabel::RemoteControl),
398                    0x20 => labels.push(ValueLabel::FirstStorageForCycleStorage),
399                    0x21 => labels.push(ValueLabel::LastStorageForCycleStorage),
400                    0x22 => labels.push(ValueLabel::SizeOfStorageBlock),
401                    0x23 => labels.push(ValueLabel::DescripitonOfTariffAndSubunit),
402                    0x24 => {
403                        units.push(unit!(Second));
404                        labels.push(ValueLabel::StorageInterval);
405                    }
406                    0x25 => {
407                        units.push(unit!(Minute));
408                        labels.push(ValueLabel::StorageInterval);
409                    }
410                    0x26 => {
411                        units.push(unit!(Hour));
412                        labels.push(ValueLabel::StorageInterval);
413                    }
414                    0x27 => {
415                        units.push(unit!(Day));
416                        labels.push(ValueLabel::StorageInterval);
417                    }
418                    0x28 => {
419                        units.push(unit!(Month));
420                        labels.push(ValueLabel::StorageInterval);
421                    }
422                    0x29 => {
423                        units.push(unit!(Year));
424                        labels.push(ValueLabel::StorageInterval);
425                    }
426                    0x30 => labels.push(ValueLabel::DimensionlessHCA),
427                    0x31 => labels.push(ValueLabel::DataContainerForWmbusProtocol),
428                    0x32 => {
429                        units.push(unit!(Second));
430                        labels.push(ValueLabel::PeriodOfNormalDataTransmition);
431                    }
432                    0x33 => {
433                        units.push(unit!(Meter));
434                        labels.push(ValueLabel::PeriodOfNormalDataTransmition);
435                    }
436                    0x34 => {
437                        units.push(unit!(Hour));
438                        labels.push(ValueLabel::PeriodOfNormalDataTransmition);
439                    }
440                    0x35 => {
441                        units.push(unit!(Day));
442                        labels.push(ValueLabel::PeriodOfNormalDataTransmition);
443                    }
444                    0x50..=0x5F => {
445                        units.push(unit!(Volt));
446                        decimal_scale_exponent = (first_vife_data & 0b1111) as isize - 9;
447                    }
448                    0x60 => labels.push(ValueLabel::ResetCounter),
449                    0x61 => labels.push(ValueLabel::CumulationCounter),
450                    0x62 => labels.push(ValueLabel::ControlSignal),
451                    0x63 => labels.push(ValueLabel::DayOfWeek),
452                    0x64 => labels.push(ValueLabel::WeekNumber),
453                    0x65 => labels.push(ValueLabel::TimePointOfChangeOfTariff),
454                    0x66 => labels.push(ValueLabel::StateOfParameterActivation),
455                    0x67 => labels.push(ValueLabel::SpecialSupplierInformation),
456                    0x68 => {
457                        units.push(unit!(Hour));
458                        labels.push(ValueLabel::DurationSinceLastCumulation);
459                    }
460                    0x69 => {
461                        units.push(unit!(Day));
462                        labels.push(ValueLabel::DurationSinceLastCumulation);
463                    }
464                    0x6A => {
465                        units.push(unit!(Month));
466                        labels.push(ValueLabel::DurationSinceLastCumulation);
467                    }
468                    0x6B => {
469                        units.push(unit!(Year));
470                        labels.push(ValueLabel::DurationSinceLastCumulation);
471                    }
472                    0x6C => {
473                        units.push(unit!(Hour));
474                        labels.push(ValueLabel::OperatingTimeBattery);
475                    }
476                    0x6D => {
477                        units.push(unit!(Day));
478                        labels.push(ValueLabel::OperatingTimeBattery);
479                    }
480                    0x6E => {
481                        units.push(unit!(Month));
482                        labels.push(ValueLabel::OperatingTimeBattery);
483                    }
484                    0x6F => {
485                        units.push(unit!(Hour));
486                        labels.push(ValueLabel::OperatingTimeBattery);
487                    }
488                    0x70 => {
489                        units.push(unit!(Second));
490                        labels.push(ValueLabel::DateAndTimeOfBatteryChange);
491                    }
492                    0x71 => {
493                        units.push(unit!(DecibelMilliWatt));
494                        labels.push(ValueLabel::RFPowerLevel);
495                    }
496                    0x72 => labels.push(ValueLabel::DaylightSavingBeginningEndingDeviation),
497                    0x73 => labels.push(ValueLabel::ListeningWindowManagementData),
498                    0x74 => labels.push(ValueLabel::RemainingBatteryLifeTime),
499                    0x75 => labels.push(ValueLabel::NumberOfTimesTheMeterWasStopped),
500                    0x76 => labels.push(ValueLabel::DataContainerForManufacturerSpecificProtocol),
501                    0x7D => match second_vife_data.map(|s| s & 0x7F) {
502                        Some(0x00) => labels.push(ValueLabel::CurrentlySelectedApplication),
503                        Some(0x02) => {
504                            units.push(unit!(Month));
505                            labels.push(ValueLabel::RemainingBatteryLifeTime);
506                        }
507                        Some(0x03) => {
508                            units.push(unit!(Year));
509                            labels.push(ValueLabel::RemainingBatteryLifeTime);
510                        }
511                        _ => labels.push(ValueLabel::Reserved),
512                    },
513                    _ => labels.push(ValueLabel::Reserved),
514                }
515            }
516            ValueInformationCoding::AlternateVIFExtension => {
517                use UnitName::*;
518                use ValueLabel::*;
519                let mk_unit = |name, exponent| Unit { name, exponent };
520                macro_rules! populate {
521                    (@trd) => {};
522                    (@trd , $label:expr) => {{ labels.push($label); }};
523                    (@snd dec: $decimal:literal $($rem:tt)*) => {{
524                        decimal_scale_exponent = $decimal;
525                        populate!(@trd $($rem)*);
526                    }};
527                    ($name:ident / h, $exponent:expr, $($rem:tt)*) => {{
528                        units.push(mk_unit($name, $exponent));
529                        units.push(mk_unit(Hour, -1));
530                        populate!(@snd $($rem)*)
531                    }};
532                    ($name:ident * h, $exponent:expr, $($rem:tt)*) => {{
533                        units.push(mk_unit($name, $exponent));
534                        units.push(mk_unit(Hour, 1));
535                        populate!(@snd $($rem)*)
536                    }};
537                    ($name:ident, $exponent:expr, $($rem:tt)*) => {{
538                        units.push(mk_unit($name, $exponent));
539                        populate!(@snd $($rem)*)
540                    }};
541                }
542                let vife = value_information_block
543                    .value_information_extension
544                    .as_ref()
545                    .ok_or(Self::Error::InvalidValueInformation)?;
546                let first_vife_data = vife.first().ok_or(DataInformationError::DataTooShort)?.data;
547                match first_vife_data & 0x7F {
548                    0b0 => populate!(Watt / h, 3, dec: 5, Energy),
549                    0b000_0001 => populate!(Watt / h, 3, dec: 6, Energy),
550                    0b000_0010 => populate!(ReactiveWatt * h, 1, dec: 3, Energy),
551                    0b000_0011 => populate!(ReactiveWatt * h, 1, dec: 4, Energy),
552                    0b000_1000 => populate!(Joul, 1, dec: 8, Energy),
553                    0b000_1001 => populate!(Joul, 1, dec: 9, Energy),
554                    0b000_1100 => populate!(Joul, 1, dec: 5, Energy),
555                    0b000_1101 => populate!(Joul, 1, dec: 6, Energy),
556                    0b000_1110 => populate!(Joul, 1, dec: 7, Energy),
557                    0b000_1111 => populate!(Joul, 1, dec: 8, Energy),
558                    0b001_0000 => populate!(Meter, 3, dec: 2),
559                    0b001_0001 => populate!(Meter, 3, dec: 3),
560                    0b001_0100 => populate!(ReactiveWatt, 1, dec: -3),
561                    0b001_0101 => populate!(ReactiveWatt, 1, dec: -2),
562                    0b001_0110 => populate!(ReactiveWatt, 1, dec: -1),
563                    0b001_0111 => populate!(ReactiveWatt, 1, dec: 0),
564                    0b001_1000 => populate!(Tonne, 1, dec: 2),
565                    0b001_1001 => populate!(Tonne, 1, dec: 3),
566                    0b001_1010 => populate!(Percent, 1, dec: -1, RelativeHumidity),
567                    0b010_0000 => populate!(Feet, 3, dec: 0),
568                    0b010_0001 => populate!(Feet, 3, dec: 1),
569                    0b010_1000 => populate!(Watt, 1, dec: 5),
570                    0b010_1001 => populate!(Watt, 1, dec: 6),
571                    0b010_1010 => populate!(Degree, 1, dec: -1, PhaseUtoU),
572                    0b010_1011 => populate!(Degree, 1, dec: -1, PhaseUtoI),
573                    0b010_1100 => populate!(Hertz, 1, dec: -3),
574                    0b010_1101 => populate!(Hertz, 1, dec: -2),
575                    0b010_1110 => populate!(Hertz, 1, dec: -1),
576                    0b010_1111 => populate!(Hertz, 1, dec: 0),
577                    0b011_0000 => populate!(Joul / h, 1, dec: -8),
578                    0b011_0001 => populate!(Joul / h, 1, dec: -7),
579                    0b011_0100 => populate!(ApparentWatt / h, 1, dec: 0),
580                    0b011_0101 => populate!(ApparentWatt / h, 1, dec: 1),
581                    0b011_0110 => populate!(ApparentWatt / h, 1, dec: 2),
582                    0b011_0111 => populate!(ApparentWatt / h, 1, dec: 3),
583                    0b111_0100 => populate!(Celsius, 1, dec: -3, ColdWarmTemperatureLimit),
584                    0b111_0101 => populate!(Celsius, 1, dec: -2, ColdWarmTemperatureLimit),
585                    0b111_0110 => populate!(Celsius, 1, dec: -1, ColdWarmTemperatureLimit),
586                    0b111_0111 => populate!(Celsius, 1, dec: 0, ColdWarmTemperatureLimit),
587                    0b111_1000 => populate!(Watt, 1, dec: -3, CumaltiveMaximumOfActivePower),
588                    0b111_1001 => populate!(Watt, 1, dec: -2, CumaltiveMaximumOfActivePower),
589                    0b111_1010 => populate!(Watt, 1, dec: -1, CumaltiveMaximumOfActivePower),
590                    0b111_1011 => populate!(Watt, 1, dec: 0, CumaltiveMaximumOfActivePower),
591                    0b111_1100 => populate!(Watt, 1, dec: 1, CumaltiveMaximumOfActivePower),
592                    0b111_1101 => populate!(Watt, 1, dec: 2, CumaltiveMaximumOfActivePower),
593                    0b111_1110 => populate!(Watt, 1, dec: 3, CumaltiveMaximumOfActivePower),
594                    0b111_1111 => populate!(Watt, 1, dec: 4, CumaltiveMaximumOfActivePower),
595                    0b110_1000 => populate!(HCAUnit, 1,dec: 0, ResultingRatingFactor),
596                    0b110_1001 => populate!(HCAUnit, 1,dec: 0, ThermalOutputRatingFactor),
597                    0b110_1010 => populate!(HCAUnit, 1,dec: 0, ThermalCouplingRatingFactorOverall),
598                    0b110_1011 => populate!(HCAUnit, 1,dec: 0, ThermalCouplingRatingRoomSide),
599                    0b110_1100 => {
600                        populate!(HCAUnit, 1,dec: 0, ThermalCouplingRatingFactorHeatingSide)
601                    }
602                    0b110_1101 => populate!(HCAUnit, 1,dec: 0, LowTemperatureRatingFactor),
603                    0b110_1110 => populate!(HCAUnit, 1,dec: 0, DisplayOutputScalingFactor),
604
605                    _ => {
606                        return Err(DataInformationError::Unimplemented {
607                            feature: "Extended value information unit codes (partial)",
608                        })
609                    }
610                };
611            }
612            // we need to check if the next byte is equivalent to the length of the rest of the
613            // the data. In this case it is very likely that, this is how the payload is built up.
614            ValueInformationCoding::PlainText => labels.push(ValueLabel::PlainText),
615            ValueInformationCoding::ManufacturerSpecific => {
616                labels.push(ValueLabel::ManufacturerSpecific)
617            }
618        }
619
620        Ok(Self {
621            decimal_offset_exponent,
622            labels,
623            decimal_scale_exponent,
624            units,
625        })
626    }
627}
628
629fn consume_orthhogonal_vife(
630    value_information_block: &ValueInformationBlock,
631    labels: &mut ArrayVec<ValueLabel, 10>,
632    units: &mut ArrayVec<Unit, 10>,
633    decimal_scale_exponent: &mut isize,
634    decimal_offset_exponent: &mut isize,
635) {
636    if let Some(vife) = &value_information_block.value_information_extension {
637        let mut is_extension_of_combinable_orthogonal_vife = false;
638        for v in vife {
639            if v.data == 0xFC {
640                is_extension_of_combinable_orthogonal_vife = true;
641                continue;
642            }
643            if is_extension_of_combinable_orthogonal_vife {
644                is_extension_of_combinable_orthogonal_vife = false;
645                match v.data & 0x7F {
646                    0x00 => labels.push(ValueLabel::Reserved),
647                    0x01 => labels.push(ValueLabel::AtPhaseL1),
648                    0x02 => labels.push(ValueLabel::AtPhaseL2),
649                    0x03 => labels.push(ValueLabel::AtPhaseL3),
650                    0x04 => labels.push(ValueLabel::AtNeutral),
651                    0x05 => labels.push(ValueLabel::BetweenPhasesL1L2),
652                    0x06 => labels.push(ValueLabel::BetweenPhasesL2L3),
653                    0x07 => labels.push(ValueLabel::BetweenPhasesL3L1),
654                    0x08 => labels.push(ValueLabel::AtQuadrant1),
655                    0x09 => labels.push(ValueLabel::AtQuadrant2),
656                    0x0A => labels.push(ValueLabel::AtQuadrant3),
657                    0x0B => labels.push(ValueLabel::AtQuadrant4),
658                    0x0C => labels.push(ValueLabel::DeltaBetweenImportAndExport),
659                    0x0F => labels.push(
660                        ValueLabel::AccumulationOfAbsoluteValueBothPositiveAndNegativeContribution,
661                    ),
662                    0x11 => labels.push(ValueLabel::DataPresentedWithTypeC),
663                    0x12 => labels.push(ValueLabel::DataPresentedWithTypeD),
664                    0x13 => labels.push(ValueLabel::DirectionFromCommunicationPartnerToMeter),
665                    0x14 => labels.push(ValueLabel::DirectionFromMeterToCommunicationPartner),
666                    _ => labels.push(ValueLabel::Reserved),
667                }
668            } else {
669                match v.data & 0x7F {
670                    0x00..=0x0F => labels.push(ValueLabel::ReservedForObjectActions),
671                    0x10..=0x11 => labels.push(ValueLabel::Reserved),
672                    0x12 => labels.push(ValueLabel::Averaged),
673                    0x13 => labels.push(ValueLabel::InverseCompactProfile),
674                    0x14 => labels.push(ValueLabel::RelativeDeviation),
675                    0x15..=0x1C => labels.push(ValueLabel::RecoordErrorCodes),
676                    0x1D => labels.push(ValueLabel::StandardConformDataContent),
677                    0x1E => labels.push(ValueLabel::CompactProfileWithRegisterNumbers),
678                    0x1F => labels.push(ValueLabel::CompactProfile),
679                    0x20 => units.push(unit!(Second ^ -1)),
680                    0x21 => units.push(unit!(Minute ^ -1)),
681                    0x22 => units.push(unit!(Hour ^ -1)),
682                    0x23 => units.push(unit!(Day ^ -1)),
683                    0x24 => units.push(unit!(Week ^ -1)),
684                    0x25 => units.push(unit!(Month ^ -1)),
685                    0x26 => units.push(unit!(Year ^ -1)),
686                    0x27 => units.push(unit!(Revolution ^ -1)),
687                    0x28 => {
688                        units.push(unit!(Increment));
689                        units.push(unit!(InputPulseOnChannel0 ^ -1));
690                    }
691                    0x29 => {
692                        units.push(unit!(Increment));
693                        units.push(unit!(OutputPulseOnChannel0 ^ -1));
694                    }
695                    0x2A => {
696                        units.push(unit!(Increment));
697                        units.push(unit!(InputPulseOnChannel1 ^ -1));
698                    }
699                    0x2B => {
700                        units.push(unit!(Increment));
701                        units.push(unit!(OutputPulseOnChannel1 ^ -1));
702                    }
703                    0x2C => units.push(unit!(Liter)),
704                    0x2D => units.push(unit!(Meter ^ -3)),
705                    0x2E => units.push(unit!(Kilogram ^ -1)),
706                    0x2F => units.push(unit!(Kelvin ^ -1)),
707                    0x30 => {
708                        units.push(unit!(Watt ^ -1));
709                        units.push(unit!(Hour ^ -1));
710                        *decimal_scale_exponent -= 3;
711                    }
712                    0x31 => {
713                        units.push(unit!(Joul ^ -1));
714                        *decimal_scale_exponent += -9;
715                    }
716                    0x32 => {
717                        units.push(unit!(Watt ^ -1));
718                        *decimal_scale_exponent += -3;
719                    }
720                    0x33 => {
721                        units.push(unit!(Kelvin ^ -1));
722                        units.push(unit!(Liter ^ -1));
723                    }
724                    0x34 => units.push(unit!(Volt ^ -1)),
725                    0x35 => units.push(unit!(Ampere ^ -1)),
726                    0x36 => units.push(unit!(Second ^ 1)),
727                    0x37 => {
728                        units.push(unit!(Second ^ 1));
729                        units.push(unit!(Volt ^ -1));
730                    }
731                    0x38 => {
732                        units.push(unit!(Second ^ 1));
733                        units.push(unit!(Ampere ^ -1));
734                    }
735                    0x39 => labels.push(ValueLabel::StartDateOf),
736                    0x3A => labels.push(ValueLabel::VifContinsUncorrectedUnitOrValue),
737                    0x3B => labels.push(ValueLabel::AccumulationOnlyIfValueIsPositive),
738                    0x3C => labels.push(ValueLabel::AccumulationOnlyIfValueIsNegative),
739                    0x3D => labels.push(ValueLabel::NoneMetricUnits),
740                    0x3E => labels.push(ValueLabel::ValueAtBaseConditions),
741                    0x3F => labels.push(ValueLabel::ObisDecleration),
742                    0x40 => labels.push(ValueLabel::UpperLimitValue),
743                    0x48 => labels.push(ValueLabel::LowerLimitValue),
744                    0x41 => labels.push(ValueLabel::NumberOfExceedsOfUpperLimitValue),
745                    0x49 => labels.push(ValueLabel::NumberOfExceedsOfLowerLimitValue),
746                    0x42 => labels.push(ValueLabel::DateOfBeginFirstLowerLimitExceed),
747                    0x43 => labels.push(ValueLabel::DateOfBeginFirstUpperLimitExceed),
748                    0x46 => labels.push(ValueLabel::DateOfBeginLastLowerLimitExceed),
749                    0x47 => labels.push(ValueLabel::DateOfBeginLastUpperLimitExceed),
750                    0x4A => labels.push(ValueLabel::DateOfEndLastLowerLimitExceed),
751                    0x4B => labels.push(ValueLabel::DateOfEndLastUpperLimitExceed),
752                    0x4E => labels.push(ValueLabel::DateOfEndFirstLowerLimitExceed),
753                    0x4F => labels.push(ValueLabel::DateOfEndFirstUpperLimitExceed),
754                    0x50 => {
755                        labels.push(ValueLabel::DurationOfFirstLowerLimitExceed);
756                        units.push(unit!(Second));
757                    }
758                    0x51 => {
759                        labels.push(ValueLabel::DurationOfFirstLowerLimitExceed);
760                        units.push(unit!(Minute));
761                    }
762                    0x52 => {
763                        labels.push(ValueLabel::DurationOfFirstLowerLimitExceed);
764                        units.push(unit!(Hour));
765                    }
766                    0x53 => {
767                        labels.push(ValueLabel::DurationOfFirstLowerLimitExceed);
768                        units.push(unit!(Day));
769                    }
770                    0x54 => {
771                        labels.push(ValueLabel::DurationOfFirstUpperLimitExceed);
772                        units.push(unit!(Second));
773                    }
774                    0x55 => {
775                        labels.push(ValueLabel::DurationOfFirstUpperLimitExceed);
776                        units.push(unit!(Minute));
777                    }
778                    0x56 => {
779                        labels.push(ValueLabel::DurationOfFirstUpperLimitExceed);
780                        units.push(unit!(Hour));
781                    }
782                    0x57 => {
783                        labels.push(ValueLabel::DurationOfFirstUpperLimitExceed);
784                        units.push(unit!(Day));
785                    }
786                    0x58 => {
787                        labels.push(ValueLabel::DurationOfLastLowerLimitExceed);
788                        units.push(unit!(Second));
789                    }
790                    0x59 => {
791                        labels.push(ValueLabel::DurationOfLastLowerLimitExceed);
792                        units.push(unit!(Minute));
793                    }
794                    0x5A => {
795                        labels.push(ValueLabel::DurationOfLastLowerLimitExceed);
796                        units.push(unit!(Hour));
797                    }
798                    0x5B => {
799                        labels.push(ValueLabel::DurationOfLastLowerLimitExceed);
800                        units.push(unit!(Day));
801                    }
802                    0x5C => {
803                        labels.push(ValueLabel::DurationOfLastUpperLimitExceed);
804                        units.push(unit!(Second));
805                    }
806                    0x5D => {
807                        labels.push(ValueLabel::DurationOfLastUpperLimitExceed);
808                        units.push(unit!(Minute));
809                    }
810                    0x5E => {
811                        labels.push(ValueLabel::DurationOfLastUpperLimitExceed);
812                        units.push(unit!(Hour));
813                    }
814                    0x5F => {
815                        labels.push(ValueLabel::DurationOfLastUpperLimitExceed);
816                        units.push(unit!(Day));
817                    }
818                    0x60 => {
819                        labels.push(ValueLabel::DurationOfFirst);
820                        units.push(unit!(Second));
821                    }
822                    0x61 => {
823                        labels.push(ValueLabel::DurationOfFirst);
824                        units.push(unit!(Minute));
825                    }
826                    0x62 => {
827                        labels.push(ValueLabel::DurationOfFirst);
828                        units.push(unit!(Hour));
829                    }
830                    0x63 => {
831                        labels.push(ValueLabel::DurationOfFirst);
832                        units.push(unit!(Day));
833                    }
834                    0x64 => {
835                        labels.push(ValueLabel::DurationOfLast);
836                        units.push(unit!(Second));
837                    }
838                    0x65 => {
839                        labels.push(ValueLabel::DurationOfLast);
840                        units.push(unit!(Minute));
841                    }
842                    0x66 => {
843                        labels.push(ValueLabel::DurationOfLast);
844                        units.push(unit!(Day));
845                    }
846                    0x68 => labels.push(ValueLabel::ValueDuringLowerValueExeed),
847                    0x6C => labels.push(ValueLabel::ValueDuringUpperValueExceed),
848                    0x69 => labels.push(ValueLabel::LeakageValues),
849                    0x6D => labels.push(ValueLabel::OverflowValues),
850                    0x6A => labels.push(ValueLabel::DateOfBeginFirst),
851                    0x6B => labels.push(ValueLabel::DateOfBeginLast),
852                    0x6E => labels.push(ValueLabel::DateOfEndLast),
853                    0x6F => labels.push(ValueLabel::DateOfEndFirst),
854                    0x70..=0x77 => {
855                        *decimal_scale_exponent += (v.data & 0b111) as isize - 6;
856                    }
857                    0x78..=0x7B => {
858                        *decimal_offset_exponent += (v.data & 0b11) as isize - 3;
859                    }
860                    0x7D => labels.push(ValueLabel::MultiplicativeCorrectionFactor103),
861                    0x7E => labels.push(ValueLabel::FutureValue),
862                    0x7F => {
863                        labels.push(ValueLabel::NextVIFEAndDataOfThisBlockAreManufacturerSpecific)
864                    }
865                    _ => labels.push(ValueLabel::Reserved),
866                };
867            }
868        }
869    }
870}
871
872#[derive(Debug, Clone, Copy, PartialEq)]
873#[cfg_attr(feature = "defmt", derive(defmt::Format))]
874#[non_exhaustive]
875pub enum ValueInformationError {
876    InvalidValueInformation,
877}
878
879impl From<u8> for ValueInformationField {
880    fn from(data: u8) -> Self {
881        Self { data }
882    }
883}
884/// This is the most important type of the this file and represents
885/// the whole information inside the value information block
886/// value(x) = (multiplier * value + offset) * units
887#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
888#[derive(Debug, PartialEq, Clone)]
889pub struct ValueInformation {
890    pub decimal_offset_exponent: isize,
891    pub labels: ArrayVec<ValueLabel, 10>,
892    pub decimal_scale_exponent: isize,
893    pub units: ArrayVec<Unit, 10>,
894}
895
896#[cfg(feature = "defmt")]
897impl defmt::Format for ValueInformation {
898    fn format(&self, f: defmt::Formatter) {
899        defmt::write!(
900            f,
901            "ValueInformation{{ decimal_offset_exponent: {}, decimal_scale_exponent: {}",
902            self.decimal_offset_exponent,
903            self.decimal_scale_exponent
904        );
905        if !self.labels.is_empty() {
906            defmt::write!(f, ", labels: [");
907            for (i, label) in self.labels.iter().enumerate() {
908                if i != 0 {
909                    defmt::write!(f, ", ");
910                }
911                defmt::write!(f, "{:?}", label);
912            }
913            defmt::write!(f, "]");
914        }
915        if !self.units.is_empty() {
916            defmt::write!(f, ", units: [");
917            for (i, unit) in self.units.iter().enumerate() {
918                if i != 0 {
919                    defmt::write!(f, ", ");
920                }
921                defmt::write!(f, "{:?}", unit);
922            }
923            defmt::write!(f, "]");
924        }
925        defmt::write!(f, " }}");
926    }
927}
928
929#[cfg(feature = "std")]
930impl fmt::Display for ValueInformation {
931    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
932        if self.decimal_offset_exponent != 0 {
933            write!(f, "+{})", self.decimal_offset_exponent)?;
934        } else {
935            write!(f, ")")?;
936        }
937        if self.decimal_scale_exponent != 0 {
938            write!(f, "e{}", self.decimal_scale_exponent)?;
939        }
940        if !self.units.is_empty() {
941            write!(f, "[")?;
942            for unit in &self.units {
943                write!(f, "{}", unit)?;
944            }
945            write!(f, "]")?;
946        }
947        if !self.labels.is_empty() {
948            write!(f, "(")?;
949            for (i, label) in self.labels.iter().enumerate() {
950                write!(f, "{:?}", label)?;
951                if i != self.labels.len() - 1 {
952                    write!(f, ", ")?;
953                }
954            }
955
956            return write!(f, ")");
957        }
958        Ok(())
959    }
960}
961#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
962#[derive(Debug, Clone, Copy, PartialEq)]
963#[cfg_attr(feature = "defmt", derive(defmt::Format))]
964#[non_exhaustive]
965pub enum ValueLabel {
966    Instantaneous,
967    ReservedForObjectActions,
968    Reserved,
969    Averaged,
970    Integral,
971    Parameter,
972    InverseCompactProfile,
973    RelativeDeviation,
974    RecoordErrorCodes,
975    StandardConformDataContent,
976    CompactProfileWithRegisterNumbers,
977    CompactProfile,
978    ActualityDuration,
979    AveragingDuration,
980    Date,
981    Time,
982    DateTime,
983    DateTimeWithSeconds,
984    FabricationNumber,
985    EnhancedIdentification,
986    Address,
987    PlainText,
988    RevolutionOrMeasurement,
989    IncrementPerInputPulseOnChannelP,
990    IncrementPerOutputPulseOnChannelP,
991    HourMinuteSecond,
992    DayMonthYear,
993    StartDateOf,
994    VifContinsUncorrectedUnitOrValue,
995    AccumulationOnlyIfValueIsPositive,
996    AccumulationOnlyIfValueIsNegative,
997    NoneMetricUnits,
998    ValueAtBaseConditions,
999    ObisDecleration,
1000    UpperLimitValue,
1001    LowerLimitValue,
1002    NumberOfExceedsOfUpperLimitValue,
1003    NumberOfExceedsOfLowerLimitValue,
1004    DateOfBeginFirstLowerLimitExceed,
1005    DateOfBeginFirstUpperLimitExceed,
1006    DateOfBeginLastLowerLimitExceed,
1007    DateOfBeginLastUpperLimitExceed,
1008    DateOfEndLastLowerLimitExceed,
1009    DateOfEndLastUpperLimitExceed,
1010    DateOfEndFirstLowerLimitExceed,
1011    DateOfEndFirstUpperLimitExceed,
1012    DurationOfFirstLowerLimitExceed,
1013    DurationOfFirstUpperLimitExceed,
1014    DurationOfLastLowerLimitExceed,
1015    DurationOfLastUpperLimitExceed,
1016    DurationOfFirst,
1017    DurationOfLast,
1018    ValueDuringLowerValueExeed,
1019    ValueDuringUpperValueExceed,
1020    LeakageValues,
1021    OverflowValues,
1022    DateOfBeginLast,
1023    DateOfBeginFirst,
1024    DateOfEndLast,
1025    DateOfEndFirst,
1026    ExtensionOfCombinableOrthogonalVIFE,
1027    MultiplicativeCorrectionFactor103,
1028    FutureValue,
1029    NextVIFEAndDataOfThisBlockAreManufacturerSpecific,
1030    Credit,
1031    Debit,
1032    UniqueMessageIdentificationOrAccessNumber,
1033    DeviceType,
1034    Manufacturer,
1035    ParameterSetIdentification,
1036    ModelOrVersion,
1037    HardwareVersion,
1038    MetrologyFirmwareVersion,
1039    OtherSoftwareVersion,
1040    CustomerLocation,
1041    Customer,
1042    AccessCodeUser,
1043    AccessCodeOperator,
1044    AccessCodeSystemOperator,
1045    AccessCodeDeveloper,
1046    Password,
1047    ErrorFlags,
1048    ErrorMask,
1049    SecurityKey,
1050    DigitalInput,
1051    DigitalOutput,
1052    Binary,
1053    BaudRate,
1054    ResponseDelayTime,
1055    Retry,
1056    RemoteControl,
1057    FirstStorageForCycleStorage,
1058    LastStorageForCycleStorage,
1059    SizeOfStorageBlock,
1060    DescripitonOfTariffAndSubunit,
1061    StorageInterval,
1062    DimensionlessHCA,
1063    DataContainerForWmbusProtocol,
1064    PeriodOfNormalDataTransmition,
1065    ResetCounter,
1066    CumulationCounter,
1067    ControlSignal,
1068    DayOfWeek,
1069    WeekNumber,
1070    TimePointOfChangeOfTariff,
1071    StateOfParameterActivation,
1072    SpecialSupplierInformation,
1073    DurationSinceLastCumulation,
1074    OperatingTimeBattery,
1075    DateAndTimeOfBatteryChange,
1076    RFPowerLevel,
1077    DaylightSavingBeginningEndingDeviation,
1078    ListeningWindowManagementData,
1079    RemainingBatteryLifeTime,
1080    NumberOfTimesTheMeterWasStopped,
1081    DataContainerForManufacturerSpecificProtocol,
1082    CurrentlySelectedApplication,
1083    Energy,
1084    AtPhaseL1,
1085    AtPhaseL2,
1086    AtPhaseL3,
1087    AtNeutral,
1088    BetweenPhasesL1L2,
1089    BetweenPhasesL2L3,
1090    BetweenPhasesL3L1,
1091    AtQuadrant1,
1092    AtQuadrant2,
1093    AtQuadrant3,
1094    AtQuadrant4,
1095    DeltaBetweenImportAndExport,
1096    AccumulationOfAbsoluteValueBothPositiveAndNegativeContribution,
1097    DataPresentedWithTypeC,
1098    DataPresentedWithTypeD,
1099    DirectionFromCommunicationPartnerToMeter,
1100    DirectionFromMeterToCommunicationPartner,
1101    RelativeHumidity,
1102    PhaseUtoU,
1103    PhaseUtoI,
1104    ColdWarmTemperatureLimit,
1105    CumaltiveMaximumOfActivePower,
1106    ResultingRatingFactor,
1107    ThermalOutputRatingFactor,
1108    ThermalCouplingRatingFactorOverall,
1109    ThermalCouplingRatingRoomSide,
1110    ThermalCouplingRatingFactorHeatingSide,
1111    LowTemperatureRatingFactor,
1112    DisplayOutputScalingFactor,
1113    ManufacturerSpecific,
1114    Volume,
1115    FlowTemperature,
1116    ReturnTemperature,
1117}
1118
1119#[cfg(feature = "std")]
1120impl fmt::Display for Unit {
1121    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1122        let superscripts = ['⁰', '¹', '²', '³', '⁴', '⁵', '⁶', '⁷', '⁸', '⁹'];
1123        let invalid_superscript = '⁻';
1124        match self.exponent {
1125            1 => write!(f, "{}", self.name),
1126            0..=9 => write!(
1127                f,
1128                "{}{}",
1129                self.name,
1130                superscripts
1131                    .get(self.exponent as usize)
1132                    .unwrap_or(&invalid_superscript)
1133            ),
1134            10..=19 => write!(
1135                f,
1136                "{}{}{}",
1137                self.name,
1138                superscripts.get(1).unwrap_or(&invalid_superscript),
1139                superscripts
1140                    .get(self.exponent as usize - 10)
1141                    .unwrap_or(&invalid_superscript)
1142            ),
1143            x if (-9..0).contains(&x) => {
1144                write!(
1145                    f,
1146                    "{}⁻{}",
1147                    self.name,
1148                    superscripts
1149                        .get((-x) as usize)
1150                        .unwrap_or(&invalid_superscript)
1151                )
1152            }
1153            x if (-19..0).contains(&x) => write!(
1154                f,
1155                "{}⁻{}{}",
1156                self.name,
1157                superscripts.get(1).unwrap_or(&invalid_superscript),
1158                superscripts
1159                    .get((-x) as usize - 10)
1160                    .unwrap_or(&invalid_superscript)
1161            ),
1162            x => write!(f, "{}^{}", self.name, x),
1163        }
1164    }
1165}
1166#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1167#[derive(Debug, Clone, Copy, PartialEq)]
1168#[cfg_attr(feature = "defmt", derive(defmt::Format))]
1169#[non_exhaustive]
1170pub enum UnitName {
1171    Watt,
1172    ReactiveWatt,
1173    ApparentWatt,
1174    Joul,
1175    Kilogram,
1176    Tonne,
1177    Meter,
1178    Feet,
1179    Celsius,
1180    Kelvin,
1181    Bar,
1182    HCA,
1183    Reserved,
1184    WithoutUnits,
1185    Second,
1186    Minute,
1187    Hour,
1188    Day,
1189    Week,
1190    Month,
1191    Year,
1192    Revolution,
1193    Increment,
1194    InputPulseOnChannel0,
1195    OutputPulseOnChannel0,
1196    InputPulseOnChannel1,
1197    OutputPulseOnChannel1,
1198    Liter,
1199    Volt,
1200    Ampere,
1201    LocalMoneyCurrency,
1202    Symbol,
1203    BitTime,
1204    DecibelMilliWatt,
1205    Percent,
1206    Degree,
1207    Hertz,
1208    HCAUnit,
1209}
1210
1211#[cfg(feature = "std")]
1212impl fmt::Display for UnitName {
1213    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1214        match self {
1215            UnitName::Watt => write!(f, "W"),
1216            UnitName::ReactiveWatt => write!(f, "W (reactive)"),
1217            UnitName::ApparentWatt => write!(f, "W (apparent)"),
1218            UnitName::Joul => write!(f, "J"),
1219            UnitName::Kilogram => write!(f, "Kg"),
1220            UnitName::Tonne => write!(f, "t"),
1221            UnitName::Meter => write!(f, "m"),
1222            UnitName::Feet => write!(f, "ft"),
1223            UnitName::Celsius => write!(f, "°C"),
1224            UnitName::Kelvin => write!(f, "°K"),
1225            UnitName::Bar => write!(f, "Bar"),
1226            UnitName::HCA => write!(f, "HCA"),
1227            UnitName::Reserved => write!(f, "Reserved"),
1228            UnitName::WithoutUnits => write!(f, "-"),
1229            UnitName::Second => write!(f, "s"),
1230            UnitName::Minute => write!(f, "min"),
1231            UnitName::Hour => write!(f, "h"),
1232            UnitName::Day => write!(f, "day"),
1233            UnitName::Week => write!(f, "week"),
1234            UnitName::Month => write!(f, "month"),
1235            UnitName::Year => write!(f, "year"),
1236            UnitName::Revolution => write!(f, "revolution"),
1237            UnitName::Increment => write!(f, "increment"),
1238            UnitName::InputPulseOnChannel0 => write!(f, "InputPulseOnChannel0"),
1239            UnitName::OutputPulseOnChannel0 => write!(f, "OutputPulseOnChannel0"),
1240            UnitName::InputPulseOnChannel1 => write!(f, "InputPulseOnChannel1"),
1241            UnitName::OutputPulseOnChannel1 => write!(f, "OutputPulseOnChannel1"),
1242            UnitName::Liter => write!(f, "l"),
1243            UnitName::Volt => write!(f, "A"),
1244            UnitName::Ampere => write!(f, "A"),
1245            UnitName::LocalMoneyCurrency => write!(f, "$ (local)"),
1246            UnitName::Symbol => write!(f, "Symbol"),
1247            UnitName::BitTime => write!(f, "BitTime"),
1248            UnitName::DecibelMilliWatt => write!(f, "dBmW"),
1249            UnitName::Percent => write!(f, "%"),
1250            UnitName::Degree => write!(f, "°"),
1251            UnitName::Hertz => write!(f, "Hz"),
1252            UnitName::HCAUnit => write!(f, "HCAUnit"),
1253        }
1254    }
1255}
1256
1257mod tests {
1258
1259    #[test]
1260    fn test_single_byte_primary_value_information_parsing() {
1261        use crate::value_information::UnitName;
1262        use crate::value_information::{
1263            Unit, ValueInformation, ValueInformationBlock, ValueInformationField, ValueLabel,
1264        };
1265        use arrayvec::ArrayVec;
1266
1267        /* VIB = 0x13 => m3^3*1e-3 */
1268        let data = [0x13];
1269        let result = ValueInformationBlock::try_from(data.as_slice()).unwrap();
1270        assert_eq!(
1271            result,
1272            ValueInformationBlock {
1273                value_information: ValueInformationField::from(0x13),
1274                value_information_extension: None,
1275                plaintext_vife: None
1276            }
1277        );
1278        assert_eq!(result.get_size(), 1);
1279        assert_eq!(
1280            ValueInformation::try_from(&result).unwrap(),
1281            ValueInformation {
1282                decimal_offset_exponent: 0,
1283                decimal_scale_exponent: -3,
1284                units: {
1285                    let mut x = ArrayVec::<Unit, 10>::new();
1286                    x.push(unit!(Meter ^ 3));
1287                    x
1288                },
1289                labels: {
1290                    let mut x = ArrayVec::<ValueLabel, 10>::new();
1291                    x.push(ValueLabel::Volume);
1292                    x
1293                }
1294            }
1295        );
1296
1297        /* VIB = 0x14 => m3^-3*1e-2 */
1298        let data = [0x14];
1299        let result = ValueInformationBlock::try_from(data.as_slice()).unwrap();
1300        assert_eq!(
1301            result,
1302            ValueInformationBlock {
1303                value_information: ValueInformationField::from(0x14),
1304                value_information_extension: None,
1305                plaintext_vife: None
1306            }
1307        );
1308        assert_eq!(result.get_size(), 1);
1309        assert_eq!(
1310            ValueInformation::try_from(&result).unwrap(),
1311            ValueInformation {
1312                decimal_offset_exponent: 0,
1313                decimal_scale_exponent: -2,
1314                units: {
1315                    let mut x = ArrayVec::<Unit, 10>::new();
1316                    x.push(unit!(Meter ^ 3));
1317                    x
1318                },
1319
1320                labels: {
1321                    let mut x = ArrayVec::<ValueLabel, 10>::new();
1322                    x.push(ValueLabel::Volume);
1323                    x
1324                }
1325            }
1326        );
1327
1328        /* VIB = 0x15 => m3^3*1e-2 */
1329        let data = [0x15];
1330        let result = ValueInformationBlock::try_from(data.as_slice()).unwrap();
1331        assert_eq!(
1332            result,
1333            ValueInformationBlock {
1334                value_information: ValueInformationField::from(0x15),
1335                value_information_extension: None,
1336                plaintext_vife: None
1337            }
1338        );
1339        assert_eq!(result.get_size(), 1);
1340        assert_eq!(
1341            ValueInformation::try_from(&result).unwrap(),
1342            ValueInformation {
1343                decimal_offset_exponent: 0,
1344                decimal_scale_exponent: -1,
1345                units: {
1346                    let mut x = ArrayVec::<Unit, 10>::new();
1347                    x.push(unit!(Meter ^ 3));
1348                    x
1349                },
1350                labels: {
1351                    let mut x = ArrayVec::<ValueLabel, 10>::new();
1352                    x.push(ValueLabel::Volume);
1353                    x
1354                }
1355            }
1356        );
1357
1358        /* VIB = 0x16 => m3^-3*1e-1 */
1359        let data = [0x16];
1360        let result = ValueInformationBlock::try_from(data.as_slice()).unwrap();
1361        assert_eq!(
1362            result,
1363            ValueInformationBlock {
1364                value_information: ValueInformationField::from(0x16),
1365                value_information_extension: None,
1366                plaintext_vife: None
1367            },
1368        );
1369        assert_eq!(result.get_size(), 1);
1370    }
1371
1372    #[test]
1373    fn test_multibyte_primary_value_information() {
1374        use crate::value_information::UnitName;
1375        use crate::value_information::{
1376            Unit, ValueInformation, ValueInformationBlock, ValueInformationField, ValueLabel,
1377        };
1378        use arrayvec::ArrayVec;
1379        /* 1 VIF, 1 - 10 orthogonal VIFE */
1380
1381        /* VIF 0x96 = 0x16 | 0x80  => m3^-3*1e-1 with extension*/
1382        /* VIFE 0x12 => Combinable Orthogonal VIFE meaning "averaged" */
1383        /* VIB = 0x96, 0x12 */
1384        let data = [0x96, 0x12];
1385        let result = ValueInformationBlock::try_from(data.as_slice()).unwrap();
1386        assert_eq!(result.get_size(), 2);
1387        assert_eq!(result.value_information, ValueInformationField::from(0x96));
1388        assert_eq!(ValueInformation::try_from(&result).unwrap().labels, {
1389            let mut x = ArrayVec::<ValueLabel, 10>::new();
1390            x.push(ValueLabel::Volume);
1391            x.push(ValueLabel::Averaged);
1392            x
1393        });
1394
1395        /* VIF 0x96 = 0x16 | 0x80  => m3^-3*1e-1 with extension*/
1396        /* VIFE 0x92 = 0x12 | 0x80  => Combinable Orthogonal VIFE meaning "averaged" with extension */
1397        /* VIFE 0x20 => Combinable Orthogonal VIFE meaning "per second" */
1398        /* VIB = 0x96, 0x92,0x20 */
1399
1400        let data = [0x96, 0x92, 0x20];
1401        let result = ValueInformationBlock::try_from(data.as_slice()).unwrap();
1402        assert_eq!(result.get_size(), 3);
1403        assert_eq!(result.value_information, ValueInformationField::from(0x96));
1404        assert_eq!(
1405            ValueInformation::try_from(&result).unwrap(),
1406            ValueInformation {
1407                labels: {
1408                    let mut x = ArrayVec::<ValueLabel, 10>::new();
1409                    x.push(ValueLabel::Volume);
1410                    x.push(ValueLabel::Averaged);
1411                    x
1412                },
1413                decimal_offset_exponent: 0,
1414                decimal_scale_exponent: 0,
1415                units: {
1416                    let mut x = ArrayVec::<Unit, 10>::new();
1417                    x.push(unit!(Meter ^ 3));
1418                    x.push(unit!(Second ^ -1));
1419                    x
1420                }
1421            }
1422        );
1423
1424        /* VIF 0x96 = 0x16 | 0x80  => m3^-3*1e-1 with extension*/
1425        /* VIFE 0x92 = 0x12 | 0x80  => Combinable Orthogonal VIFE meaning "averaged" with extension */
1426        /* VIFE 0xA0= 0x20 | 0x80 => Combinable Orthogonal VIFE meaning "per second" */
1427        /* VIFE 0x2D => Combinable Orthogonal VIFE meaning "per m3". This cancels out the VIF m3, which is useless
1428        but till a valid VIB */
1429        /* VIB = 0x96, 0x92,0xA0, 0x2D */
1430        let data = [0x96, 0x92, 0xA0, 0x2D];
1431        let result = ValueInformationBlock::try_from(data.as_slice()).unwrap();
1432        assert_eq!(result.get_size(), 4);
1433        assert_eq!(result.value_information, ValueInformationField::from(0x96));
1434        assert_eq!(
1435            ValueInformation::try_from(&result).unwrap(),
1436            ValueInformation {
1437                labels: {
1438                    let mut x = ArrayVec::<ValueLabel, 10>::new();
1439                    x.push(ValueLabel::Volume);
1440                    x.push(ValueLabel::Averaged);
1441                    x
1442                },
1443                decimal_offset_exponent: 0,
1444                decimal_scale_exponent: 0,
1445                units: {
1446                    let mut x = ArrayVec::<Unit, 10>::new();
1447                    x.push(unit!(Meter ^ 3));
1448                    x.push(unit!(Second ^ -1));
1449                    x.push(unit!(Meter ^ -3));
1450                    x
1451                }
1452            }
1453        );
1454    }
1455
1456    #[cfg(not(feature = "plaintext-before-extension"))]
1457    #[test]
1458    fn test_plain_text_vif_norm_conform() {
1459        use arrayvec::ArrayVec;
1460
1461        use crate::value_information::{Unit, ValueInformation, ValueLabel};
1462
1463        use crate::value_information::ValueInformationBlock;
1464        // This is the ascii conform method of encoding the VIF
1465        // VIF  VIFE  LEN(3) 'R'   'H'  '%'
1466        // 0xFC, 0x74, 0x03, 0x52, 0x48, 0x25,
1467        // %RH
1468        // Combinable (orthogonal) VIFE-Code extension table
1469        // VIFE = 0x74 => E111 0nnn Multiplicative correction factor for value (not unit): 10nnn–6 => 10^-2
1470        //
1471        // according to the Norm the LEN and ASCII is not part tof the VIB however this makes parsing
1472        // cumbersome so we include it in the VIB
1473
1474        let data = [0xFC, 0x74, 0x03, 0x52, 0x48, 0x25];
1475        let result = ValueInformationBlock::try_from(data.as_slice()).unwrap();
1476        assert_eq!(result.get_size(), 6);
1477        assert_eq!(result.value_information.data, 0xFC);
1478        assert_eq!(
1479            ValueInformation::try_from(&result).unwrap(),
1480            ValueInformation {
1481                decimal_offset_exponent: 0,
1482                decimal_scale_exponent: 0,
1483                units: { ArrayVec::<Unit, 10>::new() },
1484                labels: {
1485                    let mut x = ArrayVec::<ValueLabel, 10>::new();
1486                    x.push(ValueLabel::PlainText);
1487                    x
1488                }
1489            }
1490        );
1491
1492        // This is how the VIF is encoded in the test vectors
1493        // VIF  LEN(3) 'R'   'H'  '%'    VIFE
1494        // 0xFC, 0x03, 0x48, 0x52, 0x25, 0x74,
1495        // %RH
1496        // VIFE = 0x74 => E111 0nnn Multiplicative correction factor for value (not unit): 10nnn–6 => 10^-2
1497        // when not following the norm the LEN and ASCII is part of the VIB
1498        // It is however none norm conform, see the next example which follows
1499        // the MBUS Norm which explicitly states that the VIIFE should be after the VIF
1500        // not aftter the ASCII plain text and its size
1501    }
1502
1503    #[test]
1504    fn test_short_vif_with_vife() {
1505        use crate::value_information::ValueInformationBlock;
1506        let data = [253, 27];
1507        let result = ValueInformationBlock::try_from(data.as_slice()).unwrap();
1508        assert_eq!(result.get_size(), 2);
1509    }
1510}