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