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