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