1#[cfg(feature = "std")]
3use std::fmt;
4
5use variable_user_data::DataRecordError;
6
7use self::data_record::DataRecord;
8
9pub mod data_information;
10pub mod data_record;
11pub mod value_information;
12pub mod variable_user_data;
13
14#[cfg_attr(feature = "serde", derive(serde::Serialize))]
15#[cfg_attr(feature = "serde", serde(into = "Vec<DataRecord>"))]
16#[cfg_attr(feature = "defmt", derive(defmt::Format))]
17#[derive(Clone, Debug, PartialEq)]
18pub struct DataRecords<'a> {
19 offset: usize,
20 data: &'a [u8],
21 fixed_data_header: Option<&'a FixedDataHeader>,
22}
23
24#[cfg(feature = "serde")]
25impl<'a> From<DataRecords<'a>> for Vec<DataRecord<'a>> {
26 fn from(value: DataRecords<'a>) -> Self {
27 let value: Result<Vec<_>, _> = value.collect();
28 value.unwrap_or_default()
29 }
30}
31
32impl<'a> Iterator for DataRecords<'a> {
33 type Item = Result<DataRecord<'a>, DataRecordError>;
34
35 fn next(&mut self) -> Option<Self::Item> {
36 let mut _more_records_follow = false;
37
38 while self.offset < self.data.len() {
39 match self.data.get(self.offset)? {
40 0x1F => {
41 _more_records_follow = true;
43 self.offset = self.data.len();
44 }
45 0x2F => {
46 self.offset += 1;
47 }
48 _ => {
49 let record = if let Some(fixed_data_header) = self.fixed_data_header {
50 DataRecord::try_from((self.data.get(self.offset..)?, fixed_data_header))
51 } else {
52 DataRecord::try_from(self.data.get(self.offset..)?)
53 };
54 if let Ok(record) = record {
55 self.offset += record.get_size();
56 return Some(Ok(record));
57 } else {
58 self.offset = self.data.len();
59 }
60 }
61 }
62 }
63 None
64 }
65}
66
67impl<'a> DataRecords<'a> {
68 #[must_use]
69 pub const fn new(data: &'a [u8], fixed_data_header: Option<&'a FixedDataHeader>) -> Self {
70 DataRecords {
71 offset: 0,
72 data,
73 fixed_data_header,
74 }
75 }
76}
77
78bitflags::bitflags! {
79 #[repr(transparent)]
80 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
81 #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
82 pub struct StatusField: u8 {
83 const COUNTER_BINARY_SIGNED = 0b0000_0001;
84 const COUNTER_FIXED_DATE = 0b0000_0010;
85 const POWER_LOW = 0b0000_0100;
86 const PERMANENT_ERROR = 0b0000_1000;
87 const TEMPORARY_ERROR = 0b0001_0000;
88 const MANUFACTURER_SPECIFIC_1 = 0b0010_0000;
89 const MANUFACTURER_SPECIFIC_2 = 0b0100_0000;
90 const MANUFACTURER_SPECIFIC_3 = 0b1000_0000;
91 }
92}
93
94#[cfg(feature = "defmt")]
95impl defmt::Format for StatusField {
96 fn format(&self, f: defmt::Formatter) {
97 defmt::write!(f, "{:?}", self);
98 }
99}
100
101#[cfg(feature = "std")]
102impl fmt::Display for StatusField {
103 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
104 let mut status = String::new();
105 if self.contains(StatusField::COUNTER_BINARY_SIGNED) {
106 status.push_str("Counter binary signed, ");
107 }
108 if self.contains(StatusField::COUNTER_FIXED_DATE) {
109 status.push_str("Counter fixed date, ");
110 }
111 if self.contains(StatusField::POWER_LOW) {
112 status.push_str("Power low, ");
113 }
114 if self.contains(StatusField::PERMANENT_ERROR) {
115 status.push_str("Permanent error, ");
116 }
117 if self.contains(StatusField::TEMPORARY_ERROR) {
118 status.push_str("Temporary error, ");
119 }
120 if self.contains(StatusField::MANUFACTURER_SPECIFIC_1) {
121 status.push_str("Manufacturer specific 1, ");
122 }
123 if self.contains(StatusField::MANUFACTURER_SPECIFIC_2) {
124 status.push_str("Manufacturer specific 2, ");
125 }
126 if self.contains(StatusField::MANUFACTURER_SPECIFIC_3) {
127 status.push_str("Manufacturer specific 3, ");
128 }
129 if status.is_empty() {
130 status.push_str("No Error(s)");
131 }
132 write!(f, "{}", status.trim_end_matches(", "))
133 }
134}
135
136#[derive(Debug, PartialEq)]
137#[cfg_attr(feature = "defmt", derive(defmt::Format))]
138pub enum Direction {
139 SlaveToMaster,
140 MasterToSlave,
141}
142
143impl From<ControlInformation> for Direction {
145 fn from(single_byte: ControlInformation) -> Self {
146 match single_byte {
147 ControlInformation::ResetAtApplicationLevel => Self::MasterToSlave,
148 ControlInformation::SendData => Self::MasterToSlave,
149 ControlInformation::SelectSlave => Self::MasterToSlave,
150 ControlInformation::SynchronizeSlave => Self::MasterToSlave,
151 ControlInformation::SetBaudRate300 => Self::MasterToSlave,
152 ControlInformation::SetBaudRate600 => Self::MasterToSlave,
153 ControlInformation::SetBaudRate1200 => Self::MasterToSlave,
154 ControlInformation::SetBaudRate2400 => Self::MasterToSlave,
155 ControlInformation::SetBaudRate4800 => Self::MasterToSlave,
156 ControlInformation::SetBaudRate9600 => Self::MasterToSlave,
157 ControlInformation::SetBaudRate19200 => Self::MasterToSlave,
158 ControlInformation::SetBaudRate38400 => Self::MasterToSlave,
159 ControlInformation::OutputRAMContent => Self::MasterToSlave,
160 ControlInformation::WriteRAMContent => Self::MasterToSlave,
161 ControlInformation::StartCalibrationTestMode => Self::MasterToSlave,
162 ControlInformation::ReadEEPROM => Self::MasterToSlave,
163 ControlInformation::StartSoftwareTest => Self::MasterToSlave,
164 ControlInformation::HashProcedure(_) => Self::MasterToSlave,
165 ControlInformation::SendErrorStatus => Self::SlaveToMaster,
166 ControlInformation::SendAlarmStatus => Self::SlaveToMaster,
167 ControlInformation::ResponseWithVariableDataStructure { lsb_order: _ } => {
168 Self::SlaveToMaster
169 }
170 ControlInformation::ResponseWithFixedDataStructure => Self::SlaveToMaster,
171 }
172 }
173}
174
175#[derive(Debug, PartialEq)]
176#[cfg_attr(feature = "defmt", derive(defmt::Format))]
177pub enum ControlInformation {
178 SendData,
179 SelectSlave,
180 ResetAtApplicationLevel,
181 SynchronizeSlave,
182 SetBaudRate300,
183 SetBaudRate600,
184 SetBaudRate1200,
185 SetBaudRate2400,
186 SetBaudRate4800,
187 SetBaudRate9600,
188 SetBaudRate19200,
189 SetBaudRate38400,
190 OutputRAMContent,
191 WriteRAMContent,
192 StartCalibrationTestMode,
193 ReadEEPROM,
194 StartSoftwareTest,
195 HashProcedure(u8),
196 SendErrorStatus,
197 SendAlarmStatus,
198 ResponseWithVariableDataStructure { lsb_order: bool },
199 ResponseWithFixedDataStructure,
200}
201
202impl ControlInformation {
203 const fn from(byte: u8) -> Result<Self, ApplicationLayerError> {
204 match byte {
205 0x50 => Ok(Self::ResetAtApplicationLevel),
206 0x51 => Ok(Self::SendData),
207 0x52 => Ok(Self::SelectSlave),
208 0x54 => Ok(Self::SynchronizeSlave),
209 0xB8 => Ok(Self::SetBaudRate300),
210 0xB9 => Ok(Self::SetBaudRate600),
211 0xBA => Ok(Self::SetBaudRate1200),
212 0xBB => Ok(Self::SetBaudRate2400),
213 0xBC => Ok(Self::SetBaudRate4800),
214 0xBD => Ok(Self::SetBaudRate9600),
215 0xBE => Ok(Self::SetBaudRate19200),
216 0xBF => Ok(Self::SetBaudRate38400),
217 0xB1 => Ok(Self::OutputRAMContent),
218 0xB2 => Ok(Self::WriteRAMContent),
219 0xB3 => Ok(Self::StartCalibrationTestMode),
220 0xB4 => Ok(Self::ReadEEPROM),
221 0xB6 => Ok(Self::StartSoftwareTest),
222 0x90..=0x97 => Ok(Self::HashProcedure(byte - 0x90)),
223 0x70 => Ok(Self::SendErrorStatus),
224 0x71 => Ok(Self::SendAlarmStatus),
225 0x72 | 0x76 => Ok(Self::ResponseWithVariableDataStructure {
226 lsb_order: byte & 0x04 != 0,
227 }),
228 0x73 | 0x77 => Ok(Self::ResponseWithFixedDataStructure),
229 _ => Err(ApplicationLayerError::InvalidControlInformation { byte }),
230 }
231 }
232}
233
234#[derive(Debug, PartialEq)]
235#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
236#[cfg_attr(feature = "defmt", derive(defmt::Format))]
237pub enum ApplicationLayerError {
238 MissingControlInformation,
239 InvalidControlInformation { byte: u8 },
240 IdentificationNumberError { digits: [u8; 4], number: u32 },
241 InvalidManufacturerCode { code: u16 },
242 InsufficientData,
243}
244
245#[cfg(feature = "std")]
246impl fmt::Display for ApplicationLayerError {
247 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
248 match self {
249 ApplicationLayerError::MissingControlInformation => {
250 write!(f, "Missing control information")
251 }
252 ApplicationLayerError::InvalidControlInformation { byte } => {
253 write!(f, "Invalid control information: {}", byte)
254 }
255 ApplicationLayerError::InvalidManufacturerCode { code } => {
256 write!(f, "Invalid manufacturer code: {}", code)
257 }
258 ApplicationLayerError::IdentificationNumberError { digits, number } => {
259 write!(
260 f,
261 "Invalid identification number: {:?}, number: {}",
262 digits, number
263 )
264 }
265 ApplicationLayerError::InsufficientData => {
266 write!(f, "Insufficient data")
267 }
268 }
269 }
270}
271
272#[cfg(feature = "std")]
273impl std::error::Error for ApplicationLayerError {}
274
275#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
276#[derive(Debug, PartialEq)]
277#[cfg_attr(feature = "defmt", derive(defmt::Format))]
278pub enum ApplicationResetSubcode {
279 All(u8),
280 UserData(u8),
281 SimpleBilling(u8),
282 EnhancedBilling(u8),
283 MultiTariffBilling(u8),
284 InstantaneousValues(u8),
285 LoadManagementValues(u8),
286 Reserved1(u8),
287 InstallationStartup(u8),
288 Testing(u8),
289 Calibration(u8),
290 ConfigurationUpdates(u8),
291 Manufacturing(u8),
292 Development(u8),
293 Selftest(u8),
294 Reserved2(u8),
295}
296
297#[cfg(feature = "std")]
298impl fmt::Display for ApplicationResetSubcode {
299 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
300 let subcode = match self {
301 Self::All(_) => "All",
302 Self::UserData(_) => "User data",
303 Self::SimpleBilling(_) => "Simple billing",
304 Self::EnhancedBilling(_) => "Enhanced billing",
305 Self::MultiTariffBilling(_) => "Multi-tariff billing",
306 Self::InstantaneousValues(_) => "Instantaneous values",
307 Self::LoadManagementValues(_) => "Load management values",
308 Self::Reserved1(_) => "Reserved",
309 Self::InstallationStartup(_) => "Installation startup",
310 Self::Testing(_) => "Testing",
311 Self::Calibration(_) => "Calibration",
312 Self::ConfigurationUpdates(_) => "Configuration updates",
313 Self::Manufacturing(_) => "Manufacturing",
314 Self::Development(_) => "Development",
315 Self::Selftest(_) => "Self-test",
316 Self::Reserved2(_) => "Reserved",
317 };
318 write!(f, "{}", subcode)
319 }
320}
321
322impl ApplicationResetSubcode {
323 #[must_use]
324 pub const fn from(value: u8) -> Self {
325 match value & 0b1111 {
326 0b0000 => Self::All(value),
328 0b0001 => Self::UserData(value),
329 0b0010 => Self::SimpleBilling(value),
330 0b0011 => Self::EnhancedBilling(value),
331 0b0100 => Self::MultiTariffBilling(value),
332 0b0101 => Self::InstantaneousValues(value),
333 0b0110 => Self::LoadManagementValues(value),
334 0b0111 => Self::Reserved1(value),
335 0b1000 => Self::InstallationStartup(value),
336 0b1001 => Self::Testing(value),
337 0b1010 => Self::Calibration(value),
338 0b1011 => Self::ConfigurationUpdates(value),
339 0b1100 => Self::Manufacturing(value),
340 0b1101 => Self::Development(value),
341 0b1110 => Self::Selftest(value),
342 _ => Self::Reserved2(value),
343 }
344 }
345}
346
347fn bcd_hex_digits_to_u32(digits: [u8; 4]) -> Result<u32, ApplicationLayerError> {
348 let mut number = 0u32;
349
350 for &digit in digits.iter().rev() {
351 let lower = digit & 0x0F;
352 let upper = digit >> 4;
353 if lower > 9 || upper > 9 {
354 return Err(ApplicationLayerError::IdentificationNumberError { digits, number });
355 }
356 number = number * 100 + (u32::from(upper) * 10) + u32::from(lower);
357 }
358
359 Ok(number)
360}
361#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
362#[derive(Debug, PartialEq)]
363#[cfg_attr(feature = "defmt", derive(defmt::Format))]
364pub struct Counter {
365 count: u32,
366}
367
368#[cfg(feature = "std")]
369impl fmt::Display for Counter {
370 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
371 write!(f, "{:08}", self.count)
372 }
373}
374#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
375#[derive(Debug, PartialEq)]
376#[cfg_attr(feature = "defmt", derive(defmt::Format))]
377pub struct IdentificationNumber {
378 pub number: u32,
379}
380
381#[cfg(feature = "std")]
382impl fmt::Display for IdentificationNumber {
383 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
384 write!(f, "{:08}", self.number)
385 }
386}
387
388impl From<IdentificationNumber> for u32 {
389 fn from(id: IdentificationNumber) -> Self {
390 id.number
391 }
392}
393
394impl IdentificationNumber {
395 pub fn from_bcd_hex_digits(digits: [u8; 4]) -> Result<Self, ApplicationLayerError> {
396 let number = bcd_hex_digits_to_u32(digits)?;
397 Ok(Self { number })
398 }
399}
400
401impl Counter {
402 pub fn from_bcd_hex_digits(digits: [u8; 4]) -> Result<Self, ApplicationLayerError> {
403 let count = bcd_hex_digits_to_u32(digits)?;
404 Ok(Self { count })
405 }
406}
407
408#[derive(Debug, PartialEq)]
409#[allow(dead_code)]
410#[cfg_attr(feature = "defmt", derive(defmt::Format))]
411pub struct FixedDataHeder {
412 identification_number: IdentificationNumber,
413 manufacturer_code: ManufacturerCode,
414 version: u8,
415 medium: Medium,
416 access_number: u8,
417 status: StatusField,
418 signature: u16,
419}
420#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
421#[allow(clippy::large_enum_variant)]
422#[derive(Debug, PartialEq)]
423#[cfg_attr(feature = "defmt", derive(defmt::Format))]
424pub enum UserDataBlock<'a> {
425 ResetAtApplicationLevel {
426 subcode: ApplicationResetSubcode,
427 },
428 FixedDataStructure {
429 identification_number: IdentificationNumber,
430 access_number: u8,
431 status: StatusField,
432 medium_ad_unit: u16,
433 counter1: Counter,
434 counter2: Counter,
435 },
436 VariableDataStructure {
437 fixed_data_header: FixedDataHeader,
438 #[cfg_attr(feature = "serde", serde(skip_serializing))]
439 variable_data_block: &'a [u8],
440 },
441}
442#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
443#[derive(Debug, PartialEq)]
444#[cfg_attr(feature = "defmt", derive(defmt::Format))]
445pub enum Medium {
446 Other,
447 Oil,
448 Electricity,
449 Gas,
450 Heat,
451 Steam,
452 HotWater,
453 Water,
454 HeatCostAllocator,
455 Reserved,
456 GasMode2,
457 HeatMode2,
458 HotWaterMode2,
459 WaterMode2,
460 HeatCostAllocator2,
461 ReservedMode2,
462 Unknown,
463 ColdWater,
464 DualWater,
465 Pressure,
466 ADConverter,
467}
468
469impl Medium {
470 #[must_use]
471 pub const fn from_byte(byte: u8) -> Self {
472 match byte {
473 0x00 => Self::Other,
474 0x01 => Self::Oil,
475 0x02 => Self::Electricity,
476 0x03 => Self::Gas,
477 0x04 => Self::Heat,
478 0x05 => Self::Steam,
479 0x06 => Self::HotWater,
480 0x07 => Self::Water,
481 0x08 => Self::HeatCostAllocator,
482 0x09 => Self::Reserved, 0x0A => Self::GasMode2,
484 0x0B => Self::HeatMode2,
485 0x0C => Self::HotWaterMode2,
486 0x0D => Self::WaterMode2,
487 0x0E => Self::HeatCostAllocator2,
488 0x0F => Self::ReservedMode2,
489 0x10 => Self::Reserved, 0x11 => Self::Reserved, 0x12 => Self::Reserved, 0x13 => Self::Reserved, 0x14 => Self::Reserved, 0x15 => Self::Reserved, 0x16 => Self::ColdWater,
497 0x17 => Self::DualWater,
498 0x18 => Self::Pressure,
499 0x19 => Self::ADConverter,
500 0x20..=0xFF => Self::Reserved,
502 _ => Self::Unknown,
503 }
504 }
505}
506
507#[cfg(feature = "std")]
508impl fmt::Display for Medium {
509 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
510 let medium = match self {
511 Self::Other => "Other",
512 Self::Oil => "Oil",
513 Self::Electricity => "Electricity",
514 Self::Gas => "Gas",
515 Self::Heat => "Heat",
516 Self::Steam => "Steam",
517 Self::HotWater => "Hot water",
518 Self::Water => "Water",
519 Self::HeatCostAllocator => "Heat Cost Allocator",
520 Self::Reserved => "Reserved",
521 Self::GasMode2 => "Gas Mode 2",
522 Self::HeatMode2 => "Heat Mode 2",
523 Self::HotWaterMode2 => "Hot Water Mode 2",
524 Self::WaterMode2 => "Water Mode 2",
525 Self::HeatCostAllocator2 => "Heat Cost Allocator 2",
526 Self::ReservedMode2 => "Reserved",
527 Self::Unknown => "Unknown",
528 Self::ColdWater => "Cold Water",
529 Self::DualWater => "Dual Water",
530 Self::Pressure => "Pressure",
531 Self::ADConverter => "AD Converter",
532 };
533 write!(f, "{}", medium)
534 }
535}
536#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
537#[derive(Debug, PartialEq)]
538#[cfg_attr(feature = "defmt", derive(defmt::Format))]
539pub struct FixedDataHeader {
540 pub identification_number: IdentificationNumber,
541 pub manufacturer: Result<ManufacturerCode, ApplicationLayerError>,
542 pub version: u8,
543 pub medium: Medium,
544 pub access_number: u8,
545 pub status: StatusField,
546 pub signature: u16,
547 pub lsb_order: bool,
548}
549#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
550#[derive(Debug, PartialEq)]
551#[cfg_attr(feature = "defmt", derive(defmt::Format))]
552pub struct ManufacturerCode {
553 pub code: [char; 3],
554}
555
556impl ManufacturerCode {
557 pub const fn from_id(id: u16) -> Result<Self, ApplicationLayerError> {
558 let first_letter = ((id / (32 * 32)) + 64) as u8 as char;
559 let second_letter = (((id % (32 * 32)) / 32) + 64) as u8 as char;
560 let third_letter = ((id % 32) + 64) as u8 as char;
561
562 if first_letter.is_ascii_uppercase()
563 && second_letter.is_ascii_uppercase()
564 && third_letter.is_ascii_uppercase()
565 {
566 Ok(Self {
567 code: [first_letter, second_letter, third_letter],
568 })
569 } else {
570 Err(ApplicationLayerError::InvalidManufacturerCode { code: id })
571 }
572 }
573}
574
575#[cfg(feature = "std")]
576impl fmt::Display for ManufacturerCode {
577 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
578 write!(f, "{}{}{}", self.code[0], self.code[1], self.code[2])
579 }
580}
581
582#[derive(Debug, PartialEq)]
583#[cfg_attr(feature = "defmt", derive(defmt::Format))]
584pub struct MeasuredMedium {
585 pub medium: Medium,
586}
587
588impl MeasuredMedium {
589 #[must_use]
590 pub const fn new(byte: u8) -> Self {
591 Self {
592 medium: Medium::from_byte(byte),
593 }
594 }
595}
596
597impl<'a> TryFrom<&'a [u8]> for UserDataBlock<'a> {
598 type Error = ApplicationLayerError;
599
600 fn try_from(data: &'a [u8]) -> Result<Self, ApplicationLayerError> {
601 if data.is_empty() {
602 return Err(ApplicationLayerError::MissingControlInformation);
603 }
604
605 let control_information = ControlInformation::from(
606 *data
607 .first()
608 .ok_or(ApplicationLayerError::InsufficientData)?,
609 )?;
610
611 match control_information {
612 ControlInformation::ResetAtApplicationLevel => {
613 let subcode = ApplicationResetSubcode::from(
614 *data.get(1).ok_or(ApplicationLayerError::InsufficientData)?,
615 );
616 Ok(UserDataBlock::ResetAtApplicationLevel { subcode })
617 }
618 ControlInformation::SendData => todo!(),
619 ControlInformation::SelectSlave => todo!(),
620 ControlInformation::SynchronizeSlave => todo!(),
621 ControlInformation::SetBaudRate300 => todo!(),
622 ControlInformation::SetBaudRate600 => todo!(),
623 ControlInformation::SetBaudRate1200 => todo!(),
624 ControlInformation::SetBaudRate2400 => todo!(),
625 ControlInformation::SetBaudRate4800 => todo!(),
626 ControlInformation::SetBaudRate9600 => todo!(),
627 ControlInformation::SetBaudRate19200 => todo!(),
628 ControlInformation::SetBaudRate38400 => todo!(),
629 ControlInformation::OutputRAMContent => todo!(),
630 ControlInformation::WriteRAMContent => todo!(),
631 ControlInformation::StartCalibrationTestMode => todo!(),
632 ControlInformation::ReadEEPROM => todo!(),
633 ControlInformation::StartSoftwareTest => todo!(),
634 ControlInformation::HashProcedure(_) => todo!(),
635 ControlInformation::SendErrorStatus => todo!(),
636 ControlInformation::SendAlarmStatus => todo!(),
637 ControlInformation::ResponseWithVariableDataStructure { lsb_order } => {
638 let mut iter = data.iter().skip(1);
639 let mut identification_number_bytes = [
640 *iter.next().ok_or(ApplicationLayerError::InsufficientData)?,
641 *iter.next().ok_or(ApplicationLayerError::InsufficientData)?,
642 *iter.next().ok_or(ApplicationLayerError::InsufficientData)?,
643 *iter.next().ok_or(ApplicationLayerError::InsufficientData)?,
644 ];
645 if lsb_order {
646 identification_number_bytes.reverse();
647 }
648
649 Ok(UserDataBlock::VariableDataStructure {
650 fixed_data_header: FixedDataHeader {
651 identification_number: IdentificationNumber::from_bcd_hex_digits(
652 identification_number_bytes,
653 )?,
654 manufacturer: ManufacturerCode::from_id(u16::from_le_bytes([
655 *iter.next().ok_or(ApplicationLayerError::InsufficientData)?,
656 *iter.next().ok_or(ApplicationLayerError::InsufficientData)?,
657 ])),
658 version: *iter.next().ok_or(ApplicationLayerError::InsufficientData)?,
659 medium: MeasuredMedium::new(
660 *iter.next().ok_or(ApplicationLayerError::InsufficientData)?,
661 )
662 .medium,
663 access_number: *iter
664 .next()
665 .ok_or(ApplicationLayerError::InsufficientData)?,
666 status: StatusField::from_bits_truncate(
667 *iter.next().ok_or(ApplicationLayerError::InsufficientData)?,
668 ),
669 signature: u16::from_le_bytes([
670 *iter.next().ok_or(ApplicationLayerError::InsufficientData)?,
671 *iter.next().ok_or(ApplicationLayerError::InsufficientData)?,
672 ]),
673 lsb_order,
674 },
675 variable_data_block: data
676 .get(13..data.len())
677 .ok_or(ApplicationLayerError::InsufficientData)?,
678 })
679 }
680 ControlInformation::ResponseWithFixedDataStructure => {
681 let mut iter = data.iter().skip(1);
682 let identification_number = IdentificationNumber::from_bcd_hex_digits([
683 *iter.next().ok_or(ApplicationLayerError::InsufficientData)?,
684 *iter.next().ok_or(ApplicationLayerError::InsufficientData)?,
685 *iter.next().ok_or(ApplicationLayerError::InsufficientData)?,
686 *iter.next().ok_or(ApplicationLayerError::InsufficientData)?,
687 ])?;
688
689 let access_number = *iter.next().ok_or(ApplicationLayerError::InsufficientData)?;
690
691 let status = StatusField::from_bits_truncate(
692 *iter.next().ok_or(ApplicationLayerError::InsufficientData)?,
693 );
694 let medium_and_unit = u16::from_be_bytes([
695 *iter.next().ok_or(ApplicationLayerError::InsufficientData)?,
696 *iter.next().ok_or(ApplicationLayerError::InsufficientData)?,
697 ]);
698 let counter1 = Counter::from_bcd_hex_digits([
699 *iter.next().ok_or(ApplicationLayerError::InsufficientData)?,
700 *iter.next().ok_or(ApplicationLayerError::InsufficientData)?,
701 *iter.next().ok_or(ApplicationLayerError::InsufficientData)?,
702 *iter.next().ok_or(ApplicationLayerError::InsufficientData)?,
703 ])?;
704 let counter2 = Counter::from_bcd_hex_digits([
705 *iter.next().ok_or(ApplicationLayerError::InsufficientData)?,
706 *iter.next().ok_or(ApplicationLayerError::InsufficientData)?,
707 *iter.next().ok_or(ApplicationLayerError::InsufficientData)?,
708 *iter.next().ok_or(ApplicationLayerError::InsufficientData)?,
709 ])?;
710 Ok(UserDataBlock::FixedDataStructure {
711 identification_number,
712 access_number,
713 status,
714 medium_ad_unit: medium_and_unit,
715 counter1,
716 counter2,
717 })
718 }
719 }
720 }
721}
722
723#[cfg(all(test, feature = "std"))]
724mod tests {
725
726 use super::*;
727
728 #[test]
729 fn test_control_information() {
730 assert_eq!(
731 ControlInformation::from(0x50),
732 Ok(ControlInformation::ResetAtApplicationLevel)
733 );
734 assert_eq!(
735 ControlInformation::from(0x51),
736 Ok(ControlInformation::SendData)
737 );
738 assert_eq!(
739 ControlInformation::from(0x52),
740 Ok(ControlInformation::SelectSlave)
741 );
742 assert_eq!(
743 ControlInformation::from(0x54),
744 Ok(ControlInformation::SynchronizeSlave)
745 );
746 assert_eq!(
747 ControlInformation::from(0xB8),
748 Ok(ControlInformation::SetBaudRate300)
749 );
750 assert_eq!(
751 ControlInformation::from(0xB9),
752 Ok(ControlInformation::SetBaudRate600)
753 );
754 assert_eq!(
755 ControlInformation::from(0xBA),
756 Ok(ControlInformation::SetBaudRate1200)
757 );
758 assert_eq!(
759 ControlInformation::from(0xBB),
760 Ok(ControlInformation::SetBaudRate2400)
761 );
762 assert_eq!(
763 ControlInformation::from(0xBC),
764 Ok(ControlInformation::SetBaudRate4800)
765 );
766 assert_eq!(
767 ControlInformation::from(0xBD),
768 Ok(ControlInformation::SetBaudRate9600)
769 );
770 assert_eq!(
771 ControlInformation::from(0xBE),
772 Ok(ControlInformation::SetBaudRate19200)
773 );
774 assert_eq!(
775 ControlInformation::from(0xBF),
776 Ok(ControlInformation::SetBaudRate38400)
777 );
778 assert_eq!(
779 ControlInformation::from(0xB1),
780 Ok(ControlInformation::OutputRAMContent)
781 );
782 assert_eq!(
783 ControlInformation::from(0xB2),
784 Ok(ControlInformation::WriteRAMContent)
785 );
786 assert_eq!(
787 ControlInformation::from(0xB3),
788 Ok(ControlInformation::StartCalibrationTestMode)
789 );
790 assert_eq!(
791 ControlInformation::from(0xB4),
792 Ok(ControlInformation::ReadEEPROM)
793 );
794 assert_eq!(
795 ControlInformation::from(0xB6),
796 Ok(ControlInformation::StartSoftwareTest)
797 );
798 assert_eq!(
799 ControlInformation::from(0x90),
800 Ok(ControlInformation::HashProcedure(0,))
801 );
802 assert_eq!(
803 ControlInformation::from(0x91),
804 Ok(ControlInformation::HashProcedure(1,))
805 );
806 }
807
808 #[test]
809 fn test_reset_subcode() {
810 let data = [0x50, 0x10];
812 let result = UserDataBlock::try_from(data.as_slice());
813 assert_eq!(
814 result,
815 Ok(UserDataBlock::ResetAtApplicationLevel {
816 subcode: ApplicationResetSubcode::All(0x10)
817 })
818 );
819 }
820
821 #[test]
822 fn test_identification_number() -> Result<(), ApplicationLayerError> {
823 let data = [0x78, 0x56, 0x34, 0x12];
824 let result = IdentificationNumber::from_bcd_hex_digits(data)?;
825 assert_eq!(result, IdentificationNumber { number: 12345678 });
826 Ok(())
827 }
828
829 #[test]
830 fn test_fixed_data_structure() {
831 let data = [
832 0x73, 0x78, 0x56, 0x34, 0x12, 0x0A, 0x00, 0xE9, 0x7E, 0x01, 0x00, 0x00, 0x00, 0x35,
833 0x01, 0x00, 0x00,
834 ];
835
836 let result = UserDataBlock::try_from(data.as_slice());
837
838 assert_eq!(
839 result,
840 Ok(UserDataBlock::FixedDataStructure {
841 identification_number: IdentificationNumber { number: 12345678 },
842 access_number: 0x0A,
843 status: StatusField::from_bits_truncate(0x00),
844 medium_ad_unit: 0xE97E,
845 counter1: Counter { count: 1 },
846 counter2: Counter { count: 135 },
847 })
848 );
849 }
850
851 #[test]
852 fn test_manufacturer_code() -> Result<(), ApplicationLayerError> {
853 let code = ManufacturerCode::from_id(0x1ee6)?;
854 assert_eq!(
855 code,
856 ManufacturerCode {
857 code: ['G', 'W', 'F']
858 }
859 );
860 Ok(())
861 }
862
863 #[test]
864 fn test_lsb_frame() {
865 use crate::frames::Frame;
866 use crate::user_data::data_information::DataType;
867
868 let lsb_frame: &[u8] = &[
869 0x68, 0x64, 0x64, 0x68, 0x8, 0x7f, 0x76, 0x9, 0x67, 0x1, 0x6, 0x0, 0x0, 0x51, 0x4,
870 0x50, 0x0, 0x0, 0x0, 0x2, 0x6c, 0x38, 0x1c, 0xc, 0xf, 0x0, 0x80, 0x87, 0x32, 0x8c,
871 0x20, 0xf, 0x0, 0x0, 0x0, 0x0, 0xc, 0x14, 0x13, 0x32, 0x82, 0x58, 0xbc, 0x10, 0x15,
872 0x0, 0x25, 0x81, 0x25, 0x8c, 0x20, 0x13, 0x0, 0x0, 0x0, 0x0, 0x8c, 0x30, 0x13, 0x0,
873 0x0, 0x1, 0x61, 0x8c, 0x40, 0x13, 0x0, 0x0, 0x16, 0x88, 0xa, 0x3c, 0x1, 0x10, 0xa,
874 0x2d, 0x0, 0x80, 0xa, 0x5a, 0x7, 0x18, 0xa, 0x5e, 0x6, 0x53, 0xc, 0x22, 0x0, 0x16, 0x7,
875 0x26, 0x3c, 0x22, 0x0, 0x0, 0x33, 0x81, 0x4, 0x7e, 0x0, 0x0, 0x67, 0xc, 0xc, 0x16,
876 ];
877 let non_lsb_frame: &[u8] = &[
878 0x68, 0xc7, 0xc7, 0x68, 0x8, 0x38, 0x72, 0x56, 0x73, 0x23, 0x72, 0x2d, 0x2c, 0x34, 0x4,
879 0x87, 0x0, 0x0, 0x0, 0x4, 0xf, 0x7f, 0x1c, 0x1, 0x0, 0x4, 0xff, 0x7, 0x8a, 0xad, 0x8,
880 0x0, 0x4, 0xff, 0x8, 0x6, 0xfe, 0x5, 0x0, 0x4, 0x14, 0x4e, 0x55, 0xb, 0x0, 0x84, 0x40,
881 0x14, 0x0, 0x0, 0x0, 0x0, 0x84, 0x80, 0x40, 0x14, 0x0, 0x0, 0x0, 0x0, 0x4, 0x22, 0x76,
882 0x7f, 0x0, 0x0, 0x34, 0x22, 0x8b, 0x2c, 0x0, 0x0, 0x2, 0x59, 0x61, 0x1b, 0x2, 0x5d,
883 0x5f, 0x10, 0x2, 0x61, 0x2, 0xb, 0x4, 0x2d, 0x55, 0x0, 0x0, 0x0, 0x14, 0x2d, 0x83, 0x0,
884 0x0, 0x0, 0x4, 0x3b, 0x6, 0x1, 0x0, 0x0, 0x14, 0x3b, 0xaa, 0x1, 0x0, 0x0, 0x4, 0xff,
885 0x22, 0x0, 0x0, 0x0, 0x0, 0x4, 0x6d, 0x6, 0x2c, 0x1f, 0x3a, 0x44, 0xf, 0xcf, 0x11, 0x1,
886 0x0, 0x44, 0xff, 0x7, 0xb, 0x69, 0x8, 0x0, 0x44, 0xff, 0x8, 0x54, 0xd3, 0x5, 0x0, 0x44,
887 0x14, 0x11, 0xf3, 0xa, 0x0, 0xc4, 0x40, 0x14, 0x0, 0x0, 0x0, 0x0, 0xc4, 0x80, 0x40,
888 0x14, 0x0, 0x0, 0x0, 0x0, 0x54, 0x2d, 0x3a, 0x0, 0x0, 0x0, 0x54, 0x3b, 0x28, 0x1, 0x0,
889 0x0, 0x42, 0x6c, 0x1, 0x3a, 0x2, 0xff, 0x1a, 0x1, 0x1a, 0xc, 0x78, 0x56, 0x73, 0x23,
890 0x72, 0x4, 0xff, 0x16, 0xe6, 0x84, 0x1e, 0x0, 0x4, 0xff, 0x17, 0xc1, 0xd5, 0xb4, 0x0,
891 0x12, 0x16,
892 ];
893
894 let frames = [
895 (lsb_frame, 9670106, Some(DataType::Number(808732.0))),
896 (non_lsb_frame, 72237356, Some(DataType::Number(568714.0))),
897 ];
898
899 for (frame, expected_iden_nr, data_record_value) in frames {
900 let frame = Frame::try_from(frame).unwrap();
901
902 if let Frame::LongFrame {
903 function: _,
904 address: _,
905 data,
906 } = frame
907 {
908 let user_data_block = UserDataBlock::try_from(data).unwrap();
909 if let UserDataBlock::VariableDataStructure {
910 fixed_data_header,
911 variable_data_block,
912 } = user_data_block
913 {
914 assert_eq!(
915 fixed_data_header.identification_number.number,
916 expected_iden_nr
917 );
918
919 let mut data_records =
920 DataRecords::try_from((variable_data_block, &fixed_data_header))
921 .unwrap()
922 .flatten();
923 data_records.next().unwrap();
924 assert_eq!(data_records.next().unwrap().data.value, data_record_value);
925 } else {
926 panic!("UserDataBlock is not a variable data structure");
927 }
928 } else {
929 panic!("Frame is not a long frame");
930 }
931 }
932 }
933
934 #[test]
935 fn test_manufacturer_specific_data() {
936 use crate::frames::Frame;
937 use crate::user_data::data_information::DataType;
938
939 let manufacturer_specific_data_frame: &[u8] = &[
940 0x68, 0x55, 0x55, 0x68, 0x8, 0x1e, 0x72, 0x34, 0x35, 0x58, 0x12, 0x92, 0x26, 0x18, 0x4,
941 0x14, 0x0, 0x0, 0x0, 0xc, 0x78, 0x34, 0x35, 0x58, 0x12, 0x4, 0xe, 0x57, 0x64, 0x3, 0x0,
942 0xc, 0x14, 0x73, 0x58, 0x44, 0x0, 0xb, 0x2d, 0x6, 0x0, 0x0, 0xb, 0x3b, 0x55, 0x0, 0x0,
943 0xa, 0x5a, 0x87, 0x6, 0xa, 0x5e, 0x77, 0x5, 0xb, 0x61, 0x1, 0x11, 0x0, 0x4, 0x6d, 0x10,
944 0x2, 0x4, 0x3c, 0x2, 0x27, 0x79, 0x11, 0x9, 0xfd, 0xe, 0x6, 0x9, 0xfd, 0xf, 0x6, 0x8c,
945 0xc0, 0x0, 0x15, 0x71, 0x25, 0x0, 0x0, 0xf, 0x0, 0x0, 0x86, 0x16,
946 ];
947
948 let frame = Frame::try_from(manufacturer_specific_data_frame).unwrap();
949
950 if let Frame::LongFrame {
951 function: _,
952 address: _,
953 data,
954 } = frame
955 {
956 let user_data_block = UserDataBlock::try_from(data).unwrap();
957 if let UserDataBlock::VariableDataStructure {
958 fixed_data_header,
959 variable_data_block,
960 } = user_data_block
961 {
962 let mut data_records: Vec<_> =
963 DataRecords::try_from((variable_data_block, &fixed_data_header))
964 .unwrap()
965 .flatten()
966 .collect();
967
968 assert_eq!(data_records.len(), 14);
969
970 assert_eq!(
971 data_records.pop().unwrap().data.value,
972 Some(DataType::ManufacturerSpecific(&[15, 0, 0]))
973 );
974 assert_eq!(
975 data_records.pop().unwrap().data.value,
976 Some(DataType::Number(2571.0))
977 );
978 }
979 }
980 }
981
982 #[test]
983 fn real32bit() {
984 use crate::frames::Frame;
985 use crate::user_data::data_information::DataType;
986 use crate::user_data::value_information::ValueLabel;
987
988 let real32bit: &[u8] = &[
989 0x68, 0xa7, 0xa7, 0x68, 0x8, 0x4d, 0x72, 0x82, 0x4, 0x75, 0x30, 0xee, 0x4d, 0x19, 0x4,
990 0xc2, 0x0, 0x0, 0x0, 0x4, 0xe, 0x1b, 0xe, 0x0, 0x0, 0x84, 0xa, 0xe, 0x4c, 0x6, 0x0,
991 0x0, 0x4, 0x13, 0x7, 0x81, 0x0, 0x0, 0x84, 0xa, 0x13, 0x9d, 0x37, 0x0, 0x0, 0xb, 0xfd,
992 0xf, 0x0, 0x7, 0x1, 0xa, 0xfd, 0xd, 0x0, 0x11, 0x8c, 0x40, 0x79, 0x1, 0x0, 0x0, 0x0,
993 0x84, 0x40, 0x14, 0x31, 0x5, 0x0, 0x0, 0x84, 0x4a, 0x14, 0xfd, 0x4, 0x0, 0x0, 0x8c,
994 0x80, 0x40, 0x79, 0x2, 0x0, 0x0, 0x0, 0x84, 0x80, 0x40, 0x14, 0x27, 0x50, 0x0, 0x0,
995 0x84, 0x8a, 0x40, 0x14, 0x8, 0x31, 0x0, 0x0, 0x5, 0xff, 0x1, 0xdf, 0xa3, 0xb1, 0x3e,
996 0x5, 0xff, 0x2, 0xa8, 0x59, 0x6b, 0x3f, 0xc, 0x78, 0x82, 0x4, 0x75, 0x30, 0x4, 0x6d,
997 0x5, 0xb, 0x2f, 0x31, 0x82, 0xa, 0x6c, 0xe1, 0xf1, 0x5, 0x5b, 0x40, 0x7a, 0x63, 0x42,
998 0x5, 0x5f, 0x80, 0xc3, 0x25, 0x42, 0x5, 0x3e, 0x0, 0x0, 0x0, 0x0, 0x5, 0x2b, 0x0, 0x0,
999 0x0, 0x0, 0x1, 0xff, 0x2b, 0x0, 0x3, 0x22, 0x17, 0x3b, 0x0, 0x2, 0xff, 0x2c, 0x0, 0x0,
1000 0x1f, 0xa4, 0x16,
1001 ];
1002
1003 let frame = Frame::try_from(real32bit).unwrap();
1004
1005 if let Frame::LongFrame {
1006 function: _,
1007 address: _,
1008 data,
1009 } = frame
1010 {
1011 let user_data_block = UserDataBlock::try_from(data).unwrap();
1012 if let UserDataBlock::VariableDataStructure {
1013 fixed_data_header,
1014 variable_data_block,
1015 } = user_data_block
1016 {
1017 let data_records: Vec<DataRecord> =
1018 DataRecords::try_from((variable_data_block, &fixed_data_header))
1019 .unwrap()
1020 .flatten()
1021 .collect();
1022
1023 assert_eq!(data_records.len(), 24);
1024
1025 for data_record in data_records {
1026 let labels = data_record
1027 .data_record_header
1028 .processed_data_record_header
1029 .value_information
1030 .as_ref()
1031 .unwrap()
1032 .labels
1033 .clone();
1034 if labels.contains(&ValueLabel::ReturnTemperature) {
1035 assert_eq!(
1036 data_record.data.value,
1037 Some(DataType::Number(41.44091796875))
1038 );
1039 }
1040 if labels.contains(&ValueLabel::FlowTemperature) {
1041 assert_eq!(
1042 data_record.data.value,
1043 Some(DataType::Number(56.869384765625))
1044 );
1045 }
1046 }
1047 }
1048 }
1049 }
1050}