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