1use super::data_information::{self};
2use super::variable_user_data::DataRecordError;
3use super::LongTplHeader;
4
5#[cfg_attr(feature = "serde", derive(serde::Serialize))]
6#[derive(Debug, PartialEq, Clone)]
7#[cfg_attr(feature = "defmt", derive(defmt::Format))]
8pub struct DataInformationBlock<'a> {
9 pub data_information_field: DataInformationField,
10 pub data_information_field_extension: Option<DataInformationFieldExtensions<'a>>,
11}
12
13impl DataInformationBlock<'_> {
14 #[must_use]
15 pub fn get_size(&self) -> usize {
16 let mut size = 1;
17 if let Some(dife) = &self.data_information_field_extension {
18 size += dife.len();
19 }
20 size
21 }
22}
23#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
24#[derive(Debug, PartialEq, Clone)]
25#[cfg_attr(feature = "defmt", derive(defmt::Format))]
26pub struct DataInformationField {
27 pub data: u8,
28}
29
30impl From<data_information::DataInformationError> for DataRecordError {
31 fn from(error: data_information::DataInformationError) -> Self {
32 Self::DataInformationError(error)
33 }
34}
35
36impl From<u8> for DataInformationField {
37 fn from(data: u8) -> Self {
38 Self { data }
39 }
40}
41
42impl From<u8> for DataInformationFieldExtension {
43 fn from(data: u8) -> Self {
44 Self { data }
45 }
46}
47
48#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
49#[derive(Debug, PartialEq)]
50#[repr(transparent)]
51pub struct DataInformationFieldExtension {
52 pub data: u8,
53}
54
55#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
56#[derive(Clone, Debug, PartialEq)]
57#[cfg_attr(feature = "defmt", derive(defmt::Format))]
58pub struct DataInformationFieldExtensions<'a>(&'a [u8]);
59impl<'a> DataInformationFieldExtensions<'a> {
60 const fn new(data: &'a [u8]) -> Self {
61 Self(data)
62 }
63}
64
65impl Iterator for DataInformationFieldExtensions<'_> {
66 type Item = DataInformationFieldExtension;
67 fn next(&mut self) -> Option<Self::Item> {
68 let (head, tail) = self.0.split_first()?;
69 self.0 = tail;
70 Some(DataInformationFieldExtension { data: *head })
71 }
72 fn size_hint(&self) -> (usize, Option<usize>) {
73 (self.0.len(), Some(self.0.len()))
74 }
75}
76impl ExactSizeIterator for DataInformationFieldExtensions<'_> {}
77impl DoubleEndedIterator for DataInformationFieldExtensions<'_> {
78 fn next_back(&mut self) -> Option<Self::Item> {
79 let (end, start) = self.0.split_last()?;
80 self.0 = start;
81 Some(DataInformationFieldExtension { data: *end })
82 }
83}
84
85impl<'a> TryFrom<&'a [u8]> for DataInformationBlock<'a> {
86 type Error = DataInformationError;
87
88 fn try_from(data: &'a [u8]) -> Result<Self, DataInformationError> {
89 let Some((dif_byte, data)) = data.split_first() else {
90 return Err(DataInformationError::NoData);
91 };
92 let dif = DataInformationField::from(*dif_byte);
93
94 if !dif.has_extension() {
95 return Ok(DataInformationBlock {
96 data_information_field: dif,
97 data_information_field_extension: None,
98 });
99 }
100
101 let length = data.iter().take_while(|&&u8| u8 & 0x80 != 0).count();
102 let offset = length + 1;
103 if offset > MAXIMUM_DATA_INFORMATION_SIZE {
104 return Err(DataInformationError::DataTooLong);
105 }
106 Ok(DataInformationBlock {
107 data_information_field: dif,
108 data_information_field_extension: Some(DataInformationFieldExtensions::new(
109 data.get(..offset)
110 .ok_or(DataInformationError::DataTooShort)?,
111 )),
112 })
113 }
114}
115
116impl DataInformationField {
117 const fn has_extension(&self) -> bool {
118 self.data & 0x80 != 0
119 }
120
121 pub const fn is_special_function(&self) -> bool {
122 self.data & 0x0F == 0x0F
123 }
124
125 pub const fn special_function(&self) -> SpecialFunctions {
126 match self.data {
127 0x0F => SpecialFunctions::ManufacturerSpecific,
128 0x1F => SpecialFunctions::MoreRecordsFollow,
129 0x2F => SpecialFunctions::IdleFiller,
130 0x7F => SpecialFunctions::GlobalReadoutRequest,
131 _ => SpecialFunctions::Reserved,
132 }
133 }
134}
135#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
136#[derive(Debug, Clone, PartialEq)]
137#[cfg_attr(feature = "defmt", derive(defmt::Format))]
138pub struct DataInformation {
139 pub storage_number: u64,
140 pub tariff: u64,
141 pub device: u64,
142 pub function_field: FunctionField,
143 pub data_field_coding: DataFieldCoding,
144 pub data_information_extension: Option<DataInformationExtensionField>,
145 pub size: usize,
146}
147
148#[cfg(feature = "std")]
149impl std::fmt::Display for DataInformation {
150 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
151 write!(
152 f,
153 "{},{},{}",
154 self.storage_number, self.function_field, self.data_field_coding
155 )
156 }
157}
158
159const MAXIMUM_DATA_INFORMATION_SIZE: usize = 11;
160#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
161#[derive(Debug, Clone, PartialEq)]
162#[cfg_attr(feature = "defmt", derive(defmt::Format))]
163pub struct DataInformationExtensionField {}
164
165#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
166#[derive(Debug, Clone, Copy, PartialEq)]
167#[cfg_attr(feature = "defmt", derive(defmt::Format))]
168#[non_exhaustive]
169pub enum DataInformationError {
170 NoData,
171 DataTooLong,
172 DataTooShort,
173 InvalidValueInformation,
174 Unimplemented { feature: &'static str },
175}
176
177#[cfg(feature = "std")]
178impl std::fmt::Display for DataInformationError {
179 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
180 match self {
181 DataInformationError::NoData => write!(f, "No data available"),
182 DataInformationError::DataTooLong => write!(f, "Data too long"),
183 DataInformationError::DataTooShort => write!(f, "Data too short"),
184 DataInformationError::InvalidValueInformation => {
185 write!(f, "Invalid value information")
186 }
187 DataInformationError::Unimplemented { feature } => {
188 write!(f, "Unimplemented feature: {}", feature)
189 }
190 }
191 }
192}
193
194#[cfg(feature = "std")]
195impl std::error::Error for DataInformationError {}
196
197impl TryFrom<&DataInformationBlock<'_>> for DataInformation {
198 type Error = DataInformationError;
199
200 fn try_from(
201 data_information_block: &DataInformationBlock,
202 ) -> Result<Self, DataInformationError> {
203 let dif = data_information_block.data_information_field.data;
204 let possible_difes = &data_information_block.data_information_field_extension;
205 let mut storage_number = u64::from((dif & 0b0100_0000) >> 6);
206
207 let mut extension_bit = dif & 0x80 != 0;
208 let mut extension_index = 1;
209 let mut tariff = 0;
210 let mut device = 0;
211 if let Some(difes) = possible_difes {
212 let mut tariff_index = 0;
213 for (device_index, dife) in difes.clone().enumerate() {
214 if extension_index > MAXIMUM_DATA_INFORMATION_SIZE {
215 return Err(DataInformationError::DataTooLong);
216 }
217 let dife = dife.data;
218 storage_number += u64::from(dife & 0x0f) << ((extension_index * 4) + 1);
219 tariff |= u64::from((dife & 0x30) >> 4) << (tariff_index);
220 tariff_index += 2;
221 device |= u64::from((dife & 0x40) >> 6) << device_index;
222 extension_bit = dife & 0x80 != 0;
223 extension_index += 1;
224 }
225 }
226
227 let function_field = match (dif & 0b0011_0000) >> 4 {
228 0b00 => FunctionField::InstantaneousValue,
229 0b01 => FunctionField::MaximumValue,
230 0b10 => FunctionField::MinimumValue,
231 _ => FunctionField::ValueDuringErrorState,
232 };
233 let data_field_coding = match dif & 0b0000_1111 {
234 0b0000 => DataFieldCoding::NoData,
235 0b0001 => DataFieldCoding::Integer8Bit,
236 0b0010 => DataFieldCoding::Integer16Bit,
237 0b0011 => DataFieldCoding::Integer24Bit,
238 0b0100 => DataFieldCoding::Integer32Bit,
239 0b0101 => DataFieldCoding::Real32Bit,
240 0b0110 => DataFieldCoding::Integer48Bit,
241 0b0111 => DataFieldCoding::Integer64Bit,
242 0b1000 => DataFieldCoding::SelectionForReadout,
243 0b1001 => DataFieldCoding::BCD2Digit,
244 0b1010 => DataFieldCoding::BCD4Digit,
245 0b1011 => DataFieldCoding::BCD6Digit,
246 0b1100 => DataFieldCoding::BCD8Digit,
247 0b1101 => DataFieldCoding::VariableLength,
248 0b1110 => DataFieldCoding::BCDDigit12,
249 0b1111 => DataFieldCoding::SpecialFunctions(
250 data_information_block
251 .data_information_field
252 .special_function(),
253 ),
254 _ => unreachable!(), };
256
257 Ok(Self {
258 storage_number,
259 tariff,
260 device,
261 function_field,
262 data_field_coding,
263 data_information_extension: if extension_bit {
264 Some(DataInformationExtensionField {})
265 } else {
266 None
267 },
268 size: extension_index,
269 })
270 }
271}
272
273#[derive(Clone, Copy, Debug, PartialEq, Eq)]
274#[cfg_attr(feature = "serde", derive(serde::Serialize), serde(into = "String"))]
275#[cfg_attr(feature = "defmt", derive(defmt::Format))]
276pub struct TextUnit<'a>(&'a [u8]);
277impl<'a> TextUnit<'a> {
278 #[must_use]
279 pub const fn new(input: &'a [u8]) -> Self {
280 Self(input)
281 }
282}
283
284impl PartialEq<str> for TextUnit<'_> {
285 fn eq(&self, other: &str) -> bool {
286 self.0.iter().eq(other.as_bytes().iter().rev())
287 }
288}
289
290#[cfg(feature = "std")]
291impl std::fmt::Display for TextUnit<'_> {
292 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
293 write!(f, "{}", decode_text_unit(self.0))
294 }
295}
296
297#[cfg(feature = "std")]
298impl From<TextUnit<'_>> for String {
299 fn from(value: TextUnit<'_>) -> Self {
300 decode_text_unit(value.0)
301 }
302}
303
304#[cfg(feature = "std")]
305fn decode_text_unit(input: &[u8]) -> String {
306 let bytes: Vec<u8> = input.iter().copied().rev().collect();
307 match String::from_utf8(bytes) {
308 Ok(value) => value,
309 Err(error) => error.into_bytes().into_iter().map(char::from).collect(),
310 }
311}
312
313#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
314#[derive(Debug, PartialEq, Clone, Copy)]
315#[cfg_attr(feature = "defmt", derive(defmt::Format))]
316#[non_exhaustive]
317pub enum Month {
318 January,
319 February,
320 March,
321 April,
322 May,
323 June,
324 July,
325 August,
326 September,
327 October,
328 November,
329 December,
330}
331
332#[cfg(feature = "std")]
333impl std::fmt::Display for Month {
334 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
335 match self {
336 Month::January => write!(f, "Jan"),
337 Month::February => write!(f, "Feb"),
338 Month::March => write!(f, "Mar"),
339 Month::April => write!(f, "Apr"),
340 Month::May => write!(f, "May"),
341 Month::June => write!(f, "Jun"),
342 Month::July => write!(f, "Jul"),
343 Month::August => write!(f, "Aug"),
344 Month::September => write!(f, "Sep"),
345 Month::October => write!(f, "Oct"),
346 Month::November => write!(f, "Nov"),
347 Month::December => write!(f, "Dec"),
348 }
349 }
350}
351
352pub type Year = u16;
353pub type DayOfMonth = u8;
354pub type Hour = u8;
355pub type Minute = u8;
356pub type Second = u8;
357
358#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
359#[derive(Debug, PartialEq, Clone)]
360#[cfg_attr(feature = "defmt", derive(defmt::Format))]
361#[non_exhaustive]
362pub enum SingleEveryOrInvalid<T> {
363 Single(T),
364 Every(),
365 Invalid(),
366}
367
368#[cfg_attr(feature = "serde", derive(serde::Serialize))]
369#[derive(Debug, PartialEq, Clone)]
370#[cfg_attr(feature = "defmt", derive(defmt::Format))]
371#[non_exhaustive]
372pub enum DataType<'a> {
373 Text(TextUnit<'a>),
374 Number(f64),
375 LossyNumber(f64),
376 Date(
377 SingleEveryOrInvalid<DayOfMonth>,
378 SingleEveryOrInvalid<Month>,
379 SingleEveryOrInvalid<Year>,
380 ),
381 Time(
382 SingleEveryOrInvalid<Second>,
383 SingleEveryOrInvalid<Minute>,
384 SingleEveryOrInvalid<Hour>,
385 ),
386 DateTime(
387 SingleEveryOrInvalid<DayOfMonth>,
388 SingleEveryOrInvalid<Month>,
389 SingleEveryOrInvalid<Year>,
390 SingleEveryOrInvalid<Hour>,
391 SingleEveryOrInvalid<Minute>,
392 ),
393 DateTimeWithSeconds(
394 SingleEveryOrInvalid<DayOfMonth>,
395 SingleEveryOrInvalid<Month>,
396 SingleEveryOrInvalid<Year>,
397 SingleEveryOrInvalid<Hour>,
398 SingleEveryOrInvalid<Minute>,
399 SingleEveryOrInvalid<Second>,
400 ),
401 ManufacturerSpecific(
402 #[cfg_attr(
403 feature = "serde",
404 serde(serialize_with = "m_bus_core::serde_hex::serialize")
405 )]
406 &'a [u8],
407 ),
408}
409#[cfg_attr(feature = "serde", derive(serde::Serialize))]
410#[derive(PartialEq, Debug, Clone)]
411#[cfg_attr(feature = "defmt", derive(defmt::Format))]
412pub struct Data<'a> {
413 pub value: Option<DataType<'a>>,
414 pub size: usize,
415}
416
417#[cfg(feature = "std")]
418impl<T: std::fmt::Display> std::fmt::Display for SingleEveryOrInvalid<T> {
419 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
420 match self {
421 SingleEveryOrInvalid::Single(value) => write!(f, "{}", value),
422 SingleEveryOrInvalid::Every() => write!(f, "Every"),
423 SingleEveryOrInvalid::Invalid() => write!(f, "Invalid"),
424 }
425 }
426}
427
428#[cfg(feature = "std")]
429impl std::fmt::Display for Data<'_> {
430 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
431 match &self.value {
432 Some(value) => match value {
433 DataType::Number(value) => write!(f, "{}", value),
434 DataType::LossyNumber(value) => write!(f, "{}", value),
435 DataType::Date(day, month, year) => write!(f, "{}/{}/{}", day, month, year),
436 DataType::DateTime(day, month, year, hour, minute) => {
437 write!(f, "{}/{}/{} {}:{}:00", day, month, year, hour, minute)
438 }
439 DataType::DateTimeWithSeconds(day, month, year, hour, minute, second) => {
440 write!(
441 f,
442 "{}/{}/{} {}:{}:{}",
443 day, month, year, hour, minute, second
444 )
445 }
446 DataType::Time(seconds, minutes, hours) => {
447 write!(f, "{}:{}:{}", hours, minutes, seconds)
448 }
449 DataType::Text(text_unit) => {
450 let text: String = (*text_unit).into();
451 write!(f, "{}", text)
452 }
453 DataType::ManufacturerSpecific(data) => {
454 write!(f, "Manufacturer Specific: {:?}", data)
455 }
456 },
457 None => write!(f, "No Data"),
458 }
459 }
460}
461
462impl Data<'_> {
463 #[must_use]
464 pub const fn get_size(&self) -> usize {
465 self.size
466 }
467}
468
469macro_rules! parse_single_or_every {
470 ($input:expr, $mask:expr, $all_value:expr, $shift:expr) => {
471 if $input & $mask == $all_value {
472 SingleEveryOrInvalid::Every()
473 } else {
474 SingleEveryOrInvalid::Single(($input & $mask) >> $shift)
475 }
476 };
477}
478
479macro_rules! parse_month {
480 ($input:expr) => {
481 match $input & 0xF {
482 0x1 => SingleEveryOrInvalid::Single(Month::January),
483 0x2 => SingleEveryOrInvalid::Single(Month::February),
484 0x3 => SingleEveryOrInvalid::Single(Month::March),
485 0x4 => SingleEveryOrInvalid::Single(Month::April),
486 0x5 => SingleEveryOrInvalid::Single(Month::May),
487 0x6 => SingleEveryOrInvalid::Single(Month::June),
488 0x7 => SingleEveryOrInvalid::Single(Month::July),
489 0x8 => SingleEveryOrInvalid::Single(Month::August),
490 0x9 => SingleEveryOrInvalid::Single(Month::September),
491 0xA => SingleEveryOrInvalid::Single(Month::October),
492 0xB => SingleEveryOrInvalid::Single(Month::November),
493 0xC => SingleEveryOrInvalid::Single(Month::December),
494 _ => SingleEveryOrInvalid::Invalid(),
495 }
496 };
497}
498
499macro_rules! parse_year {
508 ($input:expr, $day_index:expr, $month_index:expr) => {{
509 let day_byte = u16::from($input.get($day_index).copied().unwrap_or(0) & 0xE0);
510 let month_byte = u16::from($input.get($month_index).copied().unwrap_or(0) & 0xF0);
511 let year = (day_byte >> 5) | (month_byte >> 1);
512 if year == 0x7F {
513 SingleEveryOrInvalid::Every()
514 } else {
515 SingleEveryOrInvalid::Single(2000 + year)
516 }
517 }};
518}
519fn bcd_to_value_internal(
520 data: &[u8],
521 num_digits: usize,
522 sign: i32,
523 lsb_order: bool,
524) -> Result<Data<'_>, DataRecordError> {
525 if data.len() < num_digits.div_ceil(2) {
526 return Err(DataRecordError::InsufficientData);
527 }
528
529 let mut data_value = 0.0;
530 let mut current_weight = 1.0;
531 let mut negative = false;
532
533 for i in 0..num_digits {
534 let index = if lsb_order {
535 (num_digits - i - 1) / 2
536 } else {
537 i / 2
538 };
539 let byte = data.get(index).ok_or(DataRecordError::InsufficientData)?;
540
541 let digit = if i % 2 == 0 {
542 byte & 0x0F
543 } else {
544 (byte >> 4) & 0x0F
545 };
546
547 if digit == 0x0F && i == num_digits - 1 {
551 negative = true;
552 break;
553 }
554
555 if digit > 9 {
556 return Err(DataRecordError::DataInformationError(
557 DataInformationError::InvalidValueInformation,
558 ));
559 }
560
561 data_value += f64::from(digit) * current_weight;
562 current_weight *= 10.0;
563 }
564
565 let sign = if negative { -sign } else { sign };
566 let signed_value = if data_value == 0.0 {
568 0.0
569 } else {
570 data_value * sign as f64
571 };
572
573 Ok(Data {
574 value: Some(DataType::Number(signed_value)),
575 size: num_digits.div_ceil(2),
576 })
577}
578
579fn integer_to_value_internal(data: &[u8], byte_size: usize) -> Data<'_> {
580 let mut data_value = 0i64;
581 let mut shift = 0;
582 for byte in data.iter().take(if byte_size > 8 { 8 } else { byte_size }) {
583 data_value |= (*byte as i64) << shift;
584 shift += 8;
585 }
586
587 let msb = (data_value >> (shift - 1)) & 1;
588 let data_value = if byte_size < 8 && msb == 1 {
589 -((data_value ^ (2i64.pow(shift) - 1)) + 1)
590 } else {
591 data_value
592 };
593
594 let output = if byte_size > 8 {
595 DataType::LossyNumber(data_value as f64)
596 } else {
597 DataType::Number(data_value as f64)
598 };
599 Data {
600 value: Some(output),
601 size: byte_size,
602 }
603}
604
605impl DataFieldCoding {
606 #[must_use]
618 pub fn data_size(&self, input: &[u8]) -> Option<usize> {
619 match self {
620 Self::NoData | Self::SelectionForReadout => Some(0),
621 Self::Integer8Bit | Self::BCD2Digit => Some(1),
622 Self::Integer16Bit | Self::BCD4Digit | Self::DateTypeG => Some(2),
623 Self::Integer24Bit | Self::BCD6Digit => Some(3),
624 Self::Integer32Bit
625 | Self::Real32Bit
626 | Self::BCD8Digit
627 | Self::DateTimeTypeF
628 | Self::DateTimeTypeJ => Some(4),
629 Self::Integer48Bit | Self::BCDDigit12 | Self::DateTimeTypeI => Some(6),
630 Self::Integer64Bit => Some(8),
631 Self::VariableLength => match *input.first()? {
632 length @ 0x00..=0xBF => Some(length as usize + 1),
633 length @ 0xC0..=0xC9 => Some((length - 0xC0) as usize + 1),
634 length @ 0xD0..=0xD9 => Some((length - 0xD0) as usize + 1),
635 length @ 0xE0..=0xEF => Some((length - 0xE0) as usize + 1),
636 length @ 0xF0..=0xF4 => Some(4 * (length - 0xEC) as usize + 1),
637 0xF5 => Some(48 + 1),
638 0xF6 => Some(64 + 1),
639 _ => None,
640 },
641 Self::SpecialFunctions(code) => match code {
642 SpecialFunctions::ManufacturerSpecific | SpecialFunctions::MoreRecordsFollow => {
643 Some(input.len())
644 }
645 SpecialFunctions::IdleFiller | SpecialFunctions::GlobalReadoutRequest => Some(0),
646 SpecialFunctions::Reserved => None,
647 },
648 }
649 }
650
651 pub fn parse<'a>(
652 &self,
653 input: &'a [u8],
654 fixed_data_header: Option<&'a LongTplHeader>,
655 ) -> Result<Data<'a>, DataRecordError> {
656 let lsb_order = fixed_data_header.map(|x| x.lsb_order).unwrap_or(false);
657
658 macro_rules! bcd_to_value {
659 ($data:expr, $num_digits:expr) => {{
660 bcd_to_value_internal($data, $num_digits, 1, lsb_order)
661 }};
662
663 ($data:expr, $num_digits:expr, $sign:expr) => {{
664 bcd_to_value_internal($data, $num_digits, $sign, lsb_order)
665 }};
666 }
667
668 macro_rules! integer_to_value {
669 ($data:expr, $byte_size:expr) => {{
670 if $data.len() < $byte_size {
671 return Err(DataRecordError::InsufficientData);
672 }
673 Ok(integer_to_value_internal($data, $byte_size))
674 }};
675 }
676 match self {
677 Self::NoData => Ok(Data {
678 value: None,
679 size: 0,
680 }),
681 Self::Integer8Bit => integer_to_value!(input, 1),
682 Self::Integer16Bit => integer_to_value!(input, 2),
683 Self::Integer24Bit => integer_to_value!(input, 3),
684 Self::Integer32Bit => integer_to_value!(input, 4),
685 Self::Integer48Bit => integer_to_value!(input, 6),
686 Self::Integer64Bit => integer_to_value!(input, 8),
687
688 Self::Real32Bit => {
689 if input.len() < 4 {
690 return Err(DataRecordError::InsufficientData);
691 }
692 if let Ok(x) = input
693 .get(0..4)
694 .ok_or(DataRecordError::InsufficientData)?
695 .try_into()
696 {
697 let x: [u8; 4] = x;
698 Ok(Data {
699 value: Some(DataType::Number(f64::from(f32::from_le_bytes(x)))),
700 size: 4,
701 })
702 } else {
703 Err(DataRecordError::InsufficientData)
704 }
705 }
706
707 Self::SelectionForReadout => Ok(Data {
708 value: None,
709 size: 0,
710 }),
711
712 Self::BCD2Digit => bcd_to_value!(input, 2),
713 Self::BCD4Digit => bcd_to_value!(input, 4),
714 Self::BCD6Digit => bcd_to_value!(input, 6),
715 Self::BCD8Digit => bcd_to_value!(input, 8),
716 Self::BCDDigit12 => bcd_to_value!(input, 12),
717
718 Self::VariableLength => {
719 let mut length = *input.first().ok_or(DataRecordError::InsufficientData)?;
720 match length {
721 0x00..=0xBF => Ok(Data {
722 value: Some(DataType::Text(TextUnit::new(
723 input
724 .get(1..(1 + length as usize))
725 .ok_or(DataRecordError::InsufficientData)?,
726 ))),
727 size: length as usize + 1,
728 }),
729 0xC0..=0xC9 => {
730 length -= 0xC0;
731 let bytes = input
732 .get(1..(1 + length as usize))
733 .ok_or(DataRecordError::InsufficientData)?;
734 match bcd_to_value!(bytes, 2 * length as usize) {
735 Ok(data) => Ok(Data {
736 value: data.value,
737 size: data.size + 1,
738 }),
739 Err(err) => Err(err),
740 }
741 }
742 0xD0..=0xD9 => {
743 length -= 0xD0;
744 let bytes = input
745 .get(1..(1 + length as usize))
746 .ok_or(DataRecordError::InsufficientData)?;
747 match bcd_to_value!(bytes, 2 * length as usize, -1) {
748 Ok(data) => Ok(Data {
749 value: data.value,
750 size: data.size + 1,
751 }),
752 Err(err) => Err(err),
753 }
754 }
755 0xE0..=0xEF => {
756 length -= 0xE0;
757 let bytes = input
758 .get(1..(1 + length as usize))
759 .ok_or(DataRecordError::InsufficientData)?;
760 match integer_to_value!(bytes, length as usize) {
761 Ok(data) => Ok(Data {
762 value: data.value,
763 size: data.size + 1,
764 }),
765 Err(err) => Err(err),
766 }
767 }
768 0xF0..=0xF4 => {
769 length -= 0xEC;
770 let bytes = input
771 .get(1..(1 + 4 * length as usize))
772 .ok_or(DataRecordError::InsufficientData)?;
773 match integer_to_value!(bytes, 4 * length as usize) {
774 Ok(data) => Ok(Data {
775 value: data.value,
776 size: data.size + 1,
777 }),
778 Err(err) => Err(err),
779 }
780 }
781 0xF5 => {
782 let bytes = input
783 .get(1..(1 + 48_usize))
784 .ok_or(DataRecordError::InsufficientData)?;
785 match integer_to_value!(bytes, 48_usize) {
786 Ok(data) => Ok(Data {
787 value: data.value,
788 size: data.size + 1,
789 }),
790 Err(err) => Err(err),
791 }
792 }
793 0xF6 => {
794 let bytes = input
795 .get(1..(1 + 64_usize))
796 .ok_or(DataRecordError::InsufficientData)?;
797 match integer_to_value!(bytes, 64_usize) {
798 Ok(data) => Ok(Data {
799 value: data.value,
800 size: data.size + 1,
801 }),
802 Err(err) => Err(err),
803 }
804 }
805 _ => Err(DataRecordError::DataInformationError(
806 DataInformationError::Unimplemented {
807 feature: "Variable length parsing for reserved length values",
808 },
809 )),
810 }
811 }
812
813 Self::SpecialFunctions(code) => match code {
814 SpecialFunctions::ManufacturerSpecific | SpecialFunctions::MoreRecordsFollow => {
815 Ok(Data {
816 value: Some(DataType::ManufacturerSpecific(input)),
817 size: input.len(),
818 })
819 }
820 SpecialFunctions::IdleFiller => Ok(Data {
821 value: None,
822 size: 0,
823 }),
824 SpecialFunctions::GlobalReadoutRequest => Ok(Data {
825 value: None,
826 size: 0,
827 }),
828 SpecialFunctions::Reserved => Err(DataRecordError::DataInformationError(
829 DataInformationError::InvalidValueInformation,
830 )),
831 },
832
833 Self::DateTypeG => {
834 let day = parse_single_or_every!(
835 input.first().ok_or(DataRecordError::InsufficientData)?,
836 0x1F,
837 0,
838 0
839 );
840 let month = parse_month!(input.get(1).ok_or(DataRecordError::InsufficientData)?);
841 let year = parse_year!(input, 0, 1);
842
843 Ok(Data {
844 value: Some(DataType::Date(day, month, year)),
845 size: 2,
846 })
847 }
848 Self::DateTimeTypeF => {
849 let minutes = parse_single_or_every!(
850 input.first().ok_or(DataRecordError::InsufficientData)?,
851 0x3F,
852 0x3F,
853 0
854 );
855 let hour = parse_single_or_every!(
856 input.get(1).ok_or(DataRecordError::InsufficientData)?,
857 0x1F,
858 0x1F,
859 0
860 );
861 let day = parse_single_or_every!(
862 input.get(2).ok_or(DataRecordError::InsufficientData)?,
863 0x1F,
864 0x1F,
865 0
866 );
867 let month = parse_month!(input.get(3).ok_or(DataRecordError::InsufficientData)?);
868 let year = parse_year!(input, 2, 3);
869
870 Ok(Data {
871 value: Some(DataType::DateTime(day, month, year, hour, minutes)),
872 size: 4,
873 })
874 }
875 Self::DateTimeTypeJ => {
876 let seconds = parse_single_or_every!(
877 input.first().ok_or(DataRecordError::InsufficientData)?,
878 0x3F,
879 0x3F,
880 0
881 );
882 let minutes = parse_single_or_every!(
883 input.get(1).ok_or(DataRecordError::InsufficientData)?,
884 0x3F,
885 0x3F,
886 0
887 );
888 let hours = parse_single_or_every!(
889 input.get(2).ok_or(DataRecordError::InsufficientData)?,
890 0x1F,
891 0x1F,
892 0
893 );
894
895 Ok(Data {
896 value: Some(DataType::Time(seconds, minutes, hours)),
897 size: 4,
898 })
899 }
900 Self::DateTimeTypeI => {
901 let seconds = parse_single_or_every!(
906 input.first().ok_or(DataRecordError::InsufficientData)?,
907 0x3F,
908 0x3F,
909 0
910 );
911
912 let minutes = parse_single_or_every!(
913 input.get(1).ok_or(DataRecordError::InsufficientData)?,
914 0x3F,
915 0x3F,
916 0
917 );
918
919 let hours = parse_single_or_every!(
920 input.get(2).ok_or(DataRecordError::InsufficientData)?,
921 0x1F,
922 0x1F,
923 0
924 );
925 let days = parse_single_or_every!(
926 input.get(3).ok_or(DataRecordError::InsufficientData)?,
927 0x1F,
928 0x1F,
929 0
930 );
931 let months = parse_month!(input.get(4).ok_or(DataRecordError::InsufficientData)?);
932 let year = parse_year!(input, 3, 4);
933
934 Ok(Data {
935 value: Some(DataType::DateTimeWithSeconds(
936 days, months, year, hours, minutes, seconds,
937 )),
938 size: 6,
939 })
940 }
941 }
942 }
943}
944
945impl DataInformation {
946 #[must_use]
947 pub const fn get_size(&self) -> usize {
948 self.size
949 }
950}
951#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
952#[derive(Debug, Clone, Copy, PartialEq)]
953#[cfg_attr(feature = "defmt", derive(defmt::Format))]
954#[non_exhaustive]
955pub enum FunctionField {
956 InstantaneousValue,
957 MaximumValue,
958 MinimumValue,
959 ValueDuringErrorState,
960}
961
962#[cfg(feature = "std")]
963impl std::fmt::Display for FunctionField {
964 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
965 match self {
966 FunctionField::InstantaneousValue => write!(f, "Inst"),
967 FunctionField::MaximumValue => write!(f, "Max"),
968 FunctionField::MinimumValue => write!(f, "Min"),
969 FunctionField::ValueDuringErrorState => write!(f, "Value During Error State"),
970 }
971 }
972}
973#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
974#[derive(Debug, Clone, Copy, PartialEq)]
975#[cfg_attr(feature = "defmt", derive(defmt::Format))]
976#[non_exhaustive]
977pub enum SpecialFunctions {
978 ManufacturerSpecific,
979 MoreRecordsFollow,
980 IdleFiller,
981 Reserved,
982 GlobalReadoutRequest,
983}
984
985#[cfg_attr(feature = "defmt", derive(defmt::Format))]
986pub struct Value {
987 pub data: f64,
988 pub byte_size: usize,
989}
990
991#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
992#[derive(Debug, Clone, Copy, PartialEq)]
993#[cfg_attr(feature = "defmt", derive(defmt::Format))]
994#[non_exhaustive]
995pub enum DataFieldCoding {
996 NoData,
997 Integer8Bit,
998 Integer16Bit,
999 Integer24Bit,
1000 Integer32Bit,
1001 Real32Bit,
1002 Integer48Bit,
1003 Integer64Bit,
1004 SelectionForReadout,
1005 BCD2Digit,
1006 BCD4Digit,
1007 BCD6Digit,
1008 BCD8Digit,
1009 VariableLength,
1010 BCDDigit12,
1011 SpecialFunctions(SpecialFunctions),
1012 DateTypeG,
1013 DateTimeTypeF,
1014 DateTimeTypeJ,
1015 DateTimeTypeI,
1016}
1017
1018#[cfg(feature = "std")]
1019impl std::fmt::Display for DataFieldCoding {
1020 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1021 match self {
1022 DataFieldCoding::NoData => write!(f, "No Data"),
1023 DataFieldCoding::Integer8Bit => write!(f, "8-bit Integer"),
1024 DataFieldCoding::Integer16Bit => write!(f, "16-bit Integer"),
1025 DataFieldCoding::Integer24Bit => write!(f, "24-bit Integer"),
1026 DataFieldCoding::Integer32Bit => write!(f, "32-bit Integer"),
1027 DataFieldCoding::Real32Bit => write!(f, "32-bit Real"),
1028 DataFieldCoding::Integer48Bit => write!(f, "48-bit Integer"),
1029 DataFieldCoding::Integer64Bit => write!(f, "64-bit Integer"),
1030 DataFieldCoding::SelectionForReadout => write!(f, "Selection for Readout"),
1031 DataFieldCoding::BCD2Digit => write!(f, "BCD 2-digit"),
1032 DataFieldCoding::BCD4Digit => write!(f, "BCD 4-digit"),
1033 DataFieldCoding::BCD6Digit => write!(f, "BCD 6-digit"),
1034 DataFieldCoding::BCD8Digit => write!(f, "BCD 8-digit"),
1035 DataFieldCoding::VariableLength => write!(f, "Variable Length"),
1036 DataFieldCoding::BCDDigit12 => write!(f, "BCD 12-digit"),
1037 DataFieldCoding::DateTypeG => write!(f, "Date Type G"),
1038 DataFieldCoding::DateTimeTypeF => write!(f, "Date Time Type F"),
1039 DataFieldCoding::DateTimeTypeJ => write!(f, "Date Time Type J"),
1040 DataFieldCoding::DateTimeTypeI => write!(f, "Date Time Type I"),
1041 DataFieldCoding::SpecialFunctions(code) => write!(f, "Special Functions ({:?})", code),
1042 }
1043 }
1044}
1045
1046#[cfg(test)]
1047mod tests {
1048
1049 use super::*;
1050 #[test]
1051 fn test_data_information() {
1052 let data = [0x13_u8];
1053 let result = DataInformationBlock::try_from(data.as_slice());
1054 let result = DataInformation::try_from(&result.unwrap());
1055 assert_eq!(
1056 result,
1057 Ok(DataInformation {
1058 storage_number: 0,
1059 device: 0,
1060 tariff: 0,
1061 function_field: FunctionField::MaximumValue,
1062 data_field_coding: DataFieldCoding::Integer24Bit,
1063 data_information_extension: None,
1064 size: 1,
1065 })
1066 );
1067 }
1068
1069 #[test]
1070 fn unextended_dif_does_not_interpret_following_bytes_as_extensions() {
1071 for dif in 0..=0x7f {
1072 let mut bytes = [0xff; MAXIMUM_DATA_INFORMATION_SIZE + 3];
1073 bytes[0] = dif;
1074 let block = DataInformationBlock::try_from(bytes.as_slice()).unwrap();
1075 assert_eq!(block.data_information_field.data, dif);
1076 assert_eq!(block.get_size(), 1);
1077 assert!(block.data_information_field_extension.is_none());
1078 }
1079 }
1080
1081 #[test]
1082 fn test_complex_data_information() {
1083 let data = [0xc4, 0x80, 0x40];
1084 let result = DataInformationBlock::try_from(data.as_slice());
1085 let result = DataInformation::try_from(&result.unwrap());
1086 assert_eq!(
1087 result,
1088 Ok(DataInformation {
1089 storage_number: 1,
1090 device: 2,
1091 tariff: 0,
1092 function_field: FunctionField::InstantaneousValue,
1093 data_field_coding: DataFieldCoding::Integer32Bit,
1094 data_information_extension: None,
1095 size: 3,
1096 })
1097 );
1098 }
1099
1100 #[test]
1101 fn reverse_text_unit() {
1102 let original_value = [0x6c, 0x61, 0x67, 0x69];
1103 let parsed = TextUnit::new(&original_value);
1104 assert_eq!(&parsed, "igal");
1105 }
1106
1107 #[cfg(feature = "std")]
1108 #[test]
1109 fn text_unit_latin1_swedish_characters() {
1110 let bytes = [0xF6, 0x6D, 0x6C, 0x61, 0x4D]; let text = TextUnit::new(&bytes);
1113 assert_eq!(String::from(text), "Malmö");
1114 }
1115
1116 #[cfg(feature = "std")]
1117 #[test]
1118 fn text_unit_latin1_superscript_three() {
1119 let bytes = [0x68, 0x2F, 0xB3, 0x6D]; let text = TextUnit::new(&bytes);
1122 assert_eq!(String::from(text), "m³/h");
1123 }
1124
1125 #[cfg(feature = "std")]
1126 #[test]
1127 fn text_unit_utf8_superscript_three() {
1128 let bytes = [0x68, 0x2F, 0xB3, 0xC2, 0x6D]; let text = TextUnit::new(&bytes);
1131 assert_eq!(String::from(text), "m³/h");
1132 }
1133
1134 #[test]
1135 fn test_invalid_data_information() {
1136 let data = [
1137 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
1138 ];
1139 let result = DataInformationBlock::try_from(data.as_slice());
1140 assert_eq!(result, Err(DataInformationError::DataTooLong));
1141 }
1142
1143 #[test]
1144 fn test_longest_data_information_not_too_long() {
1145 let data = [
1146 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x01,
1147 ];
1148 let result = DataInformationBlock::try_from(data.as_slice());
1149 assert_ne!(result, Err(DataInformationError::DataTooLong));
1150 }
1151
1152 #[test]
1153 fn test_short_data_information() {
1154 let data = [0xFF];
1155 let result = DataInformationBlock::try_from(data.as_slice());
1156 assert_eq!(result, Err(DataInformationError::DataTooShort));
1157 }
1158
1159 #[test]
1160 fn test_data_inforamtion1() {
1161 let data = [178, 1];
1162 let result = DataInformationBlock::try_from(data.as_slice());
1163 assert!(result.is_ok());
1164 assert_eq!(result.unwrap().get_size(), 2);
1165 }
1166
1167 #[test]
1168 fn test_bcd_to_value_unsigned() {
1169 let data = [0x54, 0x76, 0x98];
1170 let result = bcd_to_value_internal(&data, 6, 1, false);
1171 assert_eq!(
1172 result.unwrap(),
1173 Data {
1174 value: Some(DataType::Number(987654.0)),
1175 size: 3
1176 }
1177 );
1178 }
1179
1180 #[test]
1181 fn test_bcd_to_value_invalid() {
1182 let data = [0x5A, 0x76, 0x98];
1183 let result = bcd_to_value_internal(&data, 6, 1, false);
1184 assert!(matches!(
1185 result,
1186 Err(DataRecordError::DataInformationError(
1187 DataInformationError::InvalidValueInformation
1188 ))
1189 ));
1190 }
1191
1192 #[test]
1193 fn test_bcd_to_value_negative_sign_nibble() {
1194 let data = [0x18, 0x00, 0xF0];
1197 let result = bcd_to_value_internal(&data, 6, 1, false);
1198 assert_eq!(
1199 result.unwrap(),
1200 Data {
1201 value: Some(DataType::Number(-18.0)),
1202 size: 3
1203 }
1204 );
1205 }
1206
1207 #[test]
1208 fn test_bcd_to_value_negative_zero_is_positive_zero() {
1209 let data = [0x00, 0x00, 0xF0];
1210 let result = bcd_to_value_internal(&data, 6, 1, false);
1211 let Some(DataType::Number(value)) = result.unwrap().value else {
1212 panic!("expected a number");
1213 };
1214 assert!(value == 0.0 && value.is_sign_positive());
1215 }
1216
1217 #[test]
1218 fn test_bcd_to_value_sign_nibble_only_in_most_significant_digit() {
1219 let data = [0x18, 0xF0, 0x00];
1221 let result = bcd_to_value_internal(&data, 6, 1, false);
1222 assert!(matches!(
1223 result,
1224 Err(DataRecordError::DataInformationError(
1225 DataInformationError::InvalidValueInformation
1226 ))
1227 ));
1228 }
1229
1230 #[test]
1231 fn test_data_size_matches_parsed_size() {
1232 let payload = [0x01_u8; 80];
1235 let codings = [
1236 DataFieldCoding::NoData,
1237 DataFieldCoding::Integer8Bit,
1238 DataFieldCoding::Integer16Bit,
1239 DataFieldCoding::Integer24Bit,
1240 DataFieldCoding::Integer32Bit,
1241 DataFieldCoding::Real32Bit,
1242 DataFieldCoding::Integer48Bit,
1243 DataFieldCoding::Integer64Bit,
1244 DataFieldCoding::SelectionForReadout,
1245 DataFieldCoding::BCD2Digit,
1246 DataFieldCoding::BCD4Digit,
1247 DataFieldCoding::BCD6Digit,
1248 DataFieldCoding::BCD8Digit,
1249 DataFieldCoding::BCDDigit12,
1250 DataFieldCoding::DateTypeG,
1251 DataFieldCoding::DateTimeTypeF,
1252 DataFieldCoding::DateTimeTypeJ,
1253 DataFieldCoding::DateTimeTypeI,
1254 ];
1255
1256 for coding in codings {
1257 let parsed = coding.parse(&payload, None).expect("coding parses");
1258 assert_eq!(
1259 coding.data_size(&payload),
1260 Some(parsed.get_size()),
1261 "data_size disagrees with parse for {coding:?}"
1262 );
1263 }
1264
1265 for lvar in [0x03_u8, 0xC3, 0xD3, 0xE3, 0xF0, 0xF5, 0xF6] {
1267 let mut input = [0x00_u8; 80];
1268 input[0] = lvar;
1269 let parsed = DataFieldCoding::VariableLength
1270 .parse(&input, None)
1271 .expect("variable length parses");
1272 assert_eq!(
1273 DataFieldCoding::VariableLength.data_size(&input),
1274 Some(parsed.get_size()),
1275 "data_size disagrees with parse for LVAR {lvar:#04X}"
1276 );
1277 }
1278 }
1279
1280 #[test]
1281 fn test_date_type_g_reads_the_full_year() {
1282 assert_eq!(
1284 DataFieldCoding::DateTypeG.parse(&[0x8C, 0x11], None),
1285 Ok(Data {
1286 value: Some(DataType::Date(
1287 SingleEveryOrInvalid::Single(12),
1288 SingleEveryOrInvalid::Single(Month::January),
1289 SingleEveryOrInvalid::Single(2012),
1290 )),
1291 size: 2,
1292 })
1293 );
1294 }
1295
1296 #[test]
1297 fn test_date_time_type_f_reads_the_year_from_the_day_and_month_bytes() {
1298 assert_eq!(
1301 DataFieldCoding::DateTimeTypeF.parse(&[0x2D, 0x0D, 0x0F, 0x33], None),
1302 Ok(Data {
1303 value: Some(DataType::DateTime(
1304 SingleEveryOrInvalid::Single(15),
1305 SingleEveryOrInvalid::Single(Month::March),
1306 SingleEveryOrInvalid::Single(2024),
1307 SingleEveryOrInvalid::Single(13),
1308 SingleEveryOrInvalid::Single(45),
1309 )),
1310 size: 4,
1311 })
1312 );
1313 }
1314
1315 #[test]
1316 fn test_date_time_type_i_reads_the_year_from_the_day_and_month_bytes() {
1317 assert_eq!(
1319 DataFieldCoding::DateTimeTypeI.parse(&[0x36, 0x35, 0xC0, 0x53, 0x39, 0x00], None),
1320 Ok(Data {
1321 value: Some(DataType::DateTimeWithSeconds(
1322 SingleEveryOrInvalid::Single(19),
1323 SingleEveryOrInvalid::Single(Month::September),
1324 SingleEveryOrInvalid::Single(2026),
1325 SingleEveryOrInvalid::Single(0),
1326 SingleEveryOrInvalid::Single(53),
1327 SingleEveryOrInvalid::Single(54),
1328 )),
1329 size: 6,
1330 })
1331 );
1332 }
1333
1334 #[test]
1335 fn test_date_type_g_every_year_wildcard() {
1336 assert_eq!(
1338 DataFieldCoding::DateTypeG.parse(&[0xEC, 0xF1], None),
1339 Ok(Data {
1340 value: Some(DataType::Date(
1341 SingleEveryOrInvalid::Single(12),
1342 SingleEveryOrInvalid::Single(Month::January),
1343 SingleEveryOrInvalid::Every(),
1344 )),
1345 size: 2,
1346 })
1347 );
1348 }
1349
1350 #[test]
1351 fn test_integer_to_value_8_bit_positive() {
1352 let data = [0x7F];
1353 let result = integer_to_value_internal(&data, 1);
1354 assert_eq!(
1355 result,
1356 Data {
1357 value: Some(DataType::Number(127.0)),
1358 size: 1
1359 }
1360 );
1361 }
1362
1363 #[test]
1364 fn test_integer_to_value_8_bit_negative() {
1365 let data = [0xFF];
1366 let result = integer_to_value_internal(&data, 1);
1367 assert_eq!(
1368 result,
1369 Data {
1370 value: Some(DataType::Number(-1.0)),
1371 size: 1
1372 }
1373 );
1374 }
1375
1376 #[test]
1377 fn test_integer_to_value_64_bit_positive() {
1378 let data = [0xFA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00];
1379 let result = integer_to_value_internal(&data, 8);
1380 assert_eq!(
1381 result,
1382 Data {
1383 value: Some(DataType::Number(250.0)),
1384 size: 8
1385 }
1386 );
1387 }
1388
1389 #[test]
1390 fn test_integer_to_value_64_bit_negative() {
1391 let data = [0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF];
1392 let result = integer_to_value_internal(&data, 8);
1393 assert_eq!(
1394 result,
1395 Data {
1396 value: Some(DataType::Number(-1.0)),
1397 size: 8
1398 }
1399 );
1400 }
1401}