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 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_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 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#[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 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 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 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 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 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 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 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 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 }
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}