1#![cfg_attr(not(feature = "std"), no_std)]
27
28#[cfg(feature = "std")]
29use std::fmt;
30
31pub use m_bus_core::decryption;
32
33use m_bus_core::{
34 bcd_hex_digits_to_u32, ConfigurationField, DeviceType, IdentificationNumber, ManufacturerCode,
35};
36pub use variable_user_data::DataRecordError;
37
38pub use self::data_record::DataRecord;
39#[cfg(feature = "decryption")]
40use m_bus_core::decryption::DecryptionError::{NotEncrypted, UnknownEncryptionState};
41pub use m_bus_core::ApplicationLayerError;
42
43pub mod data_information;
44pub mod data_record;
45pub mod extended_link_layer;
46pub mod value_information;
47pub mod variable_user_data;
48
49use extended_link_layer::ExtendedLinkLayer;
50
51pub fn parse_application_layer(data: &[u8]) -> Result<UserDataBlock<'_>, ApplicationLayerError> {
53 UserDataBlock::try_from(data)
54}
55
56#[must_use]
60pub const fn parse_data_records(data: &[u8]) -> DataRecords<'_> {
61 DataRecords::new(data, None)
62}
63
64#[must_use]
69pub const fn parse_data_records_with_header<'a>(
70 data: &'a [u8],
71 header: &'a LongTplHeader,
72) -> DataRecords<'a> {
73 DataRecords::new(data, Some(header))
74}
75
76#[cfg_attr(feature = "serde", derive(serde::Serialize))]
77#[cfg_attr(feature = "serde", serde(into = "Vec<DataRecord>"))]
78#[cfg_attr(feature = "defmt", derive(defmt::Format))]
79#[derive(Clone, Debug, PartialEq)]
80pub struct DataRecords<'a> {
81 offset: usize,
82 data: &'a [u8],
83 long_tpl_header: Option<&'a LongTplHeader>,
84}
85
86#[cfg(feature = "std")]
87impl<'a> From<DataRecords<'a>> for Vec<DataRecord<'a>> {
88 fn from(value: DataRecords<'a>) -> Self {
89 let value: Result<Vec<_>, _> = value.collect();
90 value.unwrap_or_default()
91 }
92}
93
94impl<'a> Iterator for DataRecords<'a> {
95 type Item = Result<DataRecord<'a>, DataRecordError>;
96
97 fn next(&mut self) -> Option<Self::Item> {
98 while self.offset < self.data.len() {
99 let dif = data_information::DataInformationField::from(*self.data.get(self.offset)?);
100
101 if dif.is_special_function() {
102 match dif.special_function() {
103 data_information::SpecialFunctions::IdleFiller => {
104 self.offset += 1;
105 }
106 data_information::SpecialFunctions::ManufacturerSpecific
107 | data_information::SpecialFunctions::MoreRecordsFollow => {
108 let remaining = self.data.get(self.offset..)?;
109 self.offset = self.data.len();
110 let record = if let Some(long_tpl_header) = self.long_tpl_header {
111 DataRecord::try_from((remaining, long_tpl_header))
112 } else {
113 DataRecord::try_from(remaining)
114 };
115 return Some(record);
116 }
117 data_information::SpecialFunctions::GlobalReadoutRequest => {
118 let remaining = self.data.get(self.offset..)?;
119 self.offset += 1;
120 let record = if let Some(long_tpl_header) = self.long_tpl_header {
121 DataRecord::try_from((remaining, long_tpl_header))
122 } else {
123 DataRecord::try_from(remaining)
124 };
125 return Some(record);
126 }
127 data_information::SpecialFunctions::Reserved => {
128 self.offset += 1;
129 }
130 }
131 } else {
132 let record = if let Some(long_tpl_header) = self.long_tpl_header {
133 DataRecord::try_from((self.data.get(self.offset..)?, long_tpl_header))
134 } else {
135 DataRecord::try_from(self.data.get(self.offset..)?)
136 };
137 match record {
138 Ok(record) => {
139 self.offset += record.get_size();
140 return Some(Ok(record));
141 }
142 Err(error) => {
143 match self.failed_record_size() {
149 Some(size) => self.offset += size,
150 None => self.offset = self.data.len(),
151 }
152 return Some(Err(error));
153 }
154 }
155 }
156 }
157 None
158 }
159}
160
161impl<'a> DataRecords<'a> {
162 fn failed_record_size(&self) -> Option<usize> {
169 let remaining = self.data.get(self.offset..)?;
170 let header = data_record::DataRecordHeader::try_from(remaining).ok()?;
171 let header_size = header.get_size();
172 let data_size = header
173 .processed_data_record_header
174 .data_information
175 .as_ref()?
176 .data_field_coding
177 .data_size(remaining.get(header_size..)?)?;
178
179 let size = header_size.checked_add(data_size)?;
180 if size == 0 || size > remaining.len() {
181 return None;
182 }
183 Some(size)
184 }
185
186 #[must_use]
187 pub const fn new(data: &'a [u8], long_tpl_header: Option<&'a LongTplHeader>) -> Self {
188 DataRecords {
189 offset: 0,
190 data,
191 long_tpl_header,
192 }
193 }
194}
195
196bitflags::bitflags! {
197 #[repr(transparent)]
198 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
199 #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
200 pub struct StatusField: u8 {
201 const COUNTER_BINARY_SIGNED = 0b0000_0001;
202 const COUNTER_FIXED_DATE = 0b0000_0010;
203 const POWER_LOW = 0b0000_0100;
204 const PERMANENT_ERROR = 0b0000_1000;
205 const TEMPORARY_ERROR = 0b0001_0000;
206 const MANUFACTURER_SPECIFIC_1 = 0b0010_0000;
207 const MANUFACTURER_SPECIFIC_2 = 0b0100_0000;
208 const MANUFACTURER_SPECIFIC_3 = 0b1000_0000;
209 }
210}
211
212#[cfg(feature = "defmt")]
213impl defmt::Format for StatusField {
214 fn format(&self, f: defmt::Formatter) {
215 defmt::write!(f, "{:?}", self);
216 }
217}
218
219#[cfg(feature = "std")]
220impl fmt::Display for StatusField {
221 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
222 let mut status = String::new();
223 if self.contains(StatusField::COUNTER_BINARY_SIGNED) {
224 status.push_str("Counter binary signed, ");
225 }
226 if self.contains(StatusField::COUNTER_FIXED_DATE) {
227 status.push_str("Counter fixed date, ");
228 }
229 if self.contains(StatusField::POWER_LOW) {
230 status.push_str("Power low, ");
231 }
232 if self.contains(StatusField::PERMANENT_ERROR) {
233 status.push_str("Permanent error, ");
234 }
235 if self.contains(StatusField::TEMPORARY_ERROR) {
236 status.push_str("Temporary error, ");
237 }
238 if self.contains(StatusField::MANUFACTURER_SPECIFIC_1) {
239 status.push_str("Manufacturer specific 1, ");
240 }
241 if self.contains(StatusField::MANUFACTURER_SPECIFIC_2) {
242 status.push_str("Manufacturer specific 2, ");
243 }
244 if self.contains(StatusField::MANUFACTURER_SPECIFIC_3) {
245 status.push_str("Manufacturer specific 3, ");
246 }
247 if status.is_empty() {
248 status.push_str("No Error(s)");
249 }
250 write!(f, "{}", status.trim_end_matches(", "))
251 }
252}
253
254#[derive(Debug, Clone, Copy, PartialEq)]
255#[cfg_attr(feature = "defmt", derive(defmt::Format))]
256#[non_exhaustive]
257pub enum Direction {
258 SlaveToMaster,
259 MasterToSlave,
260}
261
262impl From<ControlInformation> for Direction {
264 fn from(single_byte: ControlInformation) -> Self {
265 match single_byte {
266 ControlInformation::ResetAtApplicationLevel => Self::MasterToSlave,
267 ControlInformation::SendData => Self::MasterToSlave,
268 ControlInformation::SelectSlave => Self::MasterToSlave,
269 ControlInformation::SynchronizeSlave => Self::MasterToSlave,
270 ControlInformation::SetBaudRate300 => Self::MasterToSlave,
271 ControlInformation::SetBaudRate600 => Self::MasterToSlave,
272 ControlInformation::SetBaudRate1200 => Self::MasterToSlave,
273 ControlInformation::SetBaudRate2400 => Self::MasterToSlave,
274 ControlInformation::SetBaudRate4800 => Self::MasterToSlave,
275 ControlInformation::SetBaudRate9600 => Self::MasterToSlave,
276 ControlInformation::SetBaudRate19200 => Self::MasterToSlave,
277 ControlInformation::SetBaudRate38400 => Self::MasterToSlave,
278 ControlInformation::OutputRAMContent => Self::MasterToSlave,
279 ControlInformation::WriteRAMContent => Self::MasterToSlave,
280 ControlInformation::StartCalibrationTestMode => Self::MasterToSlave,
281 ControlInformation::ReadEEPROM => Self::MasterToSlave,
282 ControlInformation::StartSoftwareTest => Self::MasterToSlave,
283 ControlInformation::HashProcedure(_) => Self::MasterToSlave,
284 ControlInformation::SendErrorStatus => Self::SlaveToMaster,
285 ControlInformation::SendAlarmStatus => Self::SlaveToMaster,
286 ControlInformation::ResponseWithVariableDataStructure { lsb_order: _ } => {
287 Self::SlaveToMaster
288 }
289 ControlInformation::ResponseWithFixedDataStructure => Self::SlaveToMaster,
290 ControlInformation::DataSentWithShortTransportLayer => Self::MasterToSlave,
291 ControlInformation::DataSentWithLongTransportLayer => Self::MasterToSlave,
292 ControlInformation::CosemDataWithLongTransportLayer => Self::MasterToSlave,
293 ControlInformation::CosemDataWithShortTransportLayer => Self::MasterToSlave,
294 ControlInformation::ObisDataReservedLongTransportLayer => Self::MasterToSlave,
295 ControlInformation::ObisDataReservedShortTransportLayer => Self::MasterToSlave,
296 ControlInformation::ApplicationLayerFormatFrameNoTransport => Self::MasterToSlave,
297 ControlInformation::ApplicationLayerFormatFrameShortTransport => Self::MasterToSlave,
298 ControlInformation::ApplicationLayerFormatFrameLongTransport => Self::MasterToSlave,
299 ControlInformation::ClockSyncAbsolute => Self::MasterToSlave,
300 ControlInformation::ClockSyncRelative => Self::MasterToSlave,
301 ControlInformation::ApplicationErrorShortTransport => Self::SlaveToMaster,
302 ControlInformation::ApplicationErrorLongTransport => Self::SlaveToMaster,
303 ControlInformation::AlarmShortTransport => Self::SlaveToMaster,
304 ControlInformation::AlarmLongTransport => Self::SlaveToMaster,
305 ControlInformation::ApplicationLayerNoTransport => Self::SlaveToMaster,
306 ControlInformation::ApplicationLayerCompactFrameNoTransport => Self::SlaveToMaster,
307 ControlInformation::ApplicationLayerShortTransport => Self::SlaveToMaster,
308 ControlInformation::ApplicationLayerCompactFrameShortTransport => Self::SlaveToMaster,
309 ControlInformation::CosemApplicationLayerLongTransport => Self::SlaveToMaster,
310 ControlInformation::CosemApplicationLayerShortTransport => Self::SlaveToMaster,
311 ControlInformation::ObisApplicationLayerReservedLongTransport => Self::SlaveToMaster,
312 ControlInformation::ObisApplicationLayerReservedShortTransport => Self::SlaveToMaster,
313 ControlInformation::TransportLayerLongReadoutToMeter => Self::MasterToSlave,
314 ControlInformation::NetworkLayerData => Self::MasterToSlave,
315 ControlInformation::FutureUse => Self::MasterToSlave,
316 ControlInformation::NetworkManagementApplication => Self::MasterToSlave,
317 ControlInformation::TransportLayerCompactFrame => Self::MasterToSlave,
318 ControlInformation::TransportLayerFormatFrame => Self::MasterToSlave,
319 ControlInformation::NetworkManagementDataReserved => Self::MasterToSlave,
320 ControlInformation::TransportLayerShortMeterToReadout => Self::SlaveToMaster,
321 ControlInformation::TransportLayerLongMeterToReadout => Self::SlaveToMaster,
322 ControlInformation::ExtendedLinkLayerI => Self::SlaveToMaster,
323 ControlInformation::ExtendedLinkLayerII => Self::SlaveToMaster,
324 ControlInformation::ExtendedLinkLayerIII => Self::SlaveToMaster,
325 }
326 }
327}
328
329#[derive(Debug, Clone, Copy, PartialEq)]
330#[cfg_attr(feature = "defmt", derive(defmt::Format))]
331#[non_exhaustive]
332pub enum ControlInformation {
333 SendData,
334 SelectSlave,
335 ResetAtApplicationLevel,
336 SynchronizeSlave,
337 SetBaudRate300,
338 SetBaudRate600,
339 SetBaudRate1200,
340 SetBaudRate2400,
341 SetBaudRate4800,
342 SetBaudRate9600,
343 SetBaudRate19200,
344 SetBaudRate38400,
345 OutputRAMContent,
346 WriteRAMContent,
347 StartCalibrationTestMode,
348 ReadEEPROM,
349 StartSoftwareTest,
350 HashProcedure(u8),
351 SendErrorStatus,
352 SendAlarmStatus,
353 ResponseWithVariableDataStructure { lsb_order: bool },
354 ResponseWithFixedDataStructure,
355 DataSentWithShortTransportLayer,
357 DataSentWithLongTransportLayer,
358 CosemDataWithLongTransportLayer,
359 CosemDataWithShortTransportLayer,
360 ObisDataReservedLongTransportLayer,
361 ObisDataReservedShortTransportLayer,
362 ApplicationLayerFormatFrameNoTransport,
363 ApplicationLayerFormatFrameShortTransport,
364 ApplicationLayerFormatFrameLongTransport,
365 ClockSyncAbsolute,
366 ClockSyncRelative,
367 ApplicationErrorShortTransport,
368 ApplicationErrorLongTransport,
369 AlarmShortTransport,
370 AlarmLongTransport,
371 ApplicationLayerNoTransport,
372 ApplicationLayerCompactFrameNoTransport,
373 ApplicationLayerShortTransport,
374 ApplicationLayerCompactFrameShortTransport,
375 CosemApplicationLayerLongTransport,
376 CosemApplicationLayerShortTransport,
377 ObisApplicationLayerReservedLongTransport,
378 ObisApplicationLayerReservedShortTransport,
379 TransportLayerLongReadoutToMeter,
380 NetworkLayerData,
381 FutureUse,
382 NetworkManagementApplication,
383 TransportLayerCompactFrame,
384 TransportLayerFormatFrame,
385 NetworkManagementDataReserved,
386 TransportLayerShortMeterToReadout,
387 TransportLayerLongMeterToReadout,
388 ExtendedLinkLayerI,
389 ExtendedLinkLayerII,
390 ExtendedLinkLayerIII,
391}
392
393impl ControlInformation {
394 const fn from(byte: u8) -> Result<Self, ApplicationLayerError> {
395 match byte {
396 0x50 => Ok(Self::ResetAtApplicationLevel),
397 0x51 => Ok(Self::SendData),
398 0x52 => Ok(Self::SelectSlave),
399 0x54 => Ok(Self::SynchronizeSlave),
400 0x5A => Ok(Self::DataSentWithShortTransportLayer),
401 0x5B => Ok(Self::DataSentWithLongTransportLayer),
402 0x60 => Ok(Self::CosemDataWithLongTransportLayer),
403 0x61 => Ok(Self::CosemDataWithShortTransportLayer),
404 0x64 => Ok(Self::ObisDataReservedLongTransportLayer),
405 0x65 => Ok(Self::ObisDataReservedShortTransportLayer),
406 0x69 => Ok(Self::ApplicationLayerFormatFrameNoTransport),
407 0x6A => Ok(Self::ApplicationLayerFormatFrameShortTransport),
408 0x6B => Ok(Self::ApplicationLayerFormatFrameLongTransport),
409 0x6C => Ok(Self::ClockSyncAbsolute),
410 0x6D => Ok(Self::ClockSyncRelative),
411 0x6E => Ok(Self::ApplicationErrorShortTransport),
412 0x6F => Ok(Self::ApplicationErrorLongTransport),
413 0x70 => Ok(Self::SendErrorStatus),
414 0x71 => Ok(Self::SendAlarmStatus),
415 0x72 | 0x76 => Ok(Self::ResponseWithVariableDataStructure {
416 lsb_order: byte & 0x04 != 0,
417 }),
418 0x73 | 0x77 => Ok(Self::ResponseWithFixedDataStructure),
419 0x74 => Ok(Self::AlarmShortTransport),
420 0x75 => Ok(Self::AlarmLongTransport),
421 0x78 => Ok(Self::ApplicationLayerNoTransport),
422 0x79 => Ok(Self::ApplicationLayerCompactFrameNoTransport),
423 0x7A => Ok(Self::ApplicationLayerShortTransport),
424 0x7B => Ok(Self::ApplicationLayerCompactFrameShortTransport),
425 0x7C => Ok(Self::CosemApplicationLayerLongTransport),
426 0x7D => Ok(Self::CosemApplicationLayerShortTransport),
427 0x7E => Ok(Self::ObisApplicationLayerReservedLongTransport),
428 0x7F => Ok(Self::ObisApplicationLayerReservedShortTransport),
429 0x80 => Ok(Self::TransportLayerLongReadoutToMeter),
430 0x81 => Ok(Self::NetworkLayerData),
431 0x82 => Ok(Self::FutureUse),
432 0x83 => Ok(Self::NetworkManagementApplication),
433 0x84 => Ok(Self::TransportLayerCompactFrame),
434 0x85 => Ok(Self::TransportLayerFormatFrame),
435 0x89 => Ok(Self::NetworkManagementDataReserved),
436 0x8A => Ok(Self::TransportLayerShortMeterToReadout),
437 0x8B => Ok(Self::TransportLayerLongMeterToReadout),
438 0x8C => Ok(Self::ExtendedLinkLayerI),
439 0x8D => Ok(Self::ExtendedLinkLayerII),
440 0x8E => Ok(Self::ExtendedLinkLayerIII),
441 0x90..=0x97 => Ok(Self::HashProcedure(byte - 0x90)),
442 0xA0..=0xAF => Ok(Self::ApplicationLayerShortTransport),
449 0xB1 => Ok(Self::OutputRAMContent),
450 0xB2 => Ok(Self::WriteRAMContent),
451 0xB3 => Ok(Self::StartCalibrationTestMode),
452 0xB4 => Ok(Self::ReadEEPROM),
453 0xB6 => Ok(Self::StartSoftwareTest),
454 0xB8 => Ok(Self::SetBaudRate300),
455 0xB9 => Ok(Self::SetBaudRate600),
456 0xBA => Ok(Self::SetBaudRate1200),
457 0xBB => Ok(Self::SetBaudRate2400),
458 0xBC => Ok(Self::SetBaudRate4800),
459 0xBD => Ok(Self::SetBaudRate9600),
460 0xBE => Ok(Self::SetBaudRate19200),
461 0xBF => Ok(Self::SetBaudRate38400),
462 _ => Err(ApplicationLayerError::InvalidControlInformation { byte }),
463 }
464 }
465}
466
467#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
468#[derive(Debug, Clone, Copy, PartialEq)]
469#[cfg_attr(feature = "defmt", derive(defmt::Format))]
470#[non_exhaustive]
471pub enum ApplicationResetSubcode {
472 All(u8),
473 UserData(u8),
474 SimpleBilling(u8),
475 EnhancedBilling(u8),
476 MultiTariffBilling(u8),
477 InstantaneousValues(u8),
478 LoadManagementValues(u8),
479 Reserved1(u8),
480 InstallationStartup(u8),
481 Testing(u8),
482 Calibration(u8),
483 ConfigurationUpdates(u8),
484 Manufacturing(u8),
485 Development(u8),
486 Selftest(u8),
487 Reserved2(u8),
488}
489
490#[cfg(feature = "std")]
491impl fmt::Display for ApplicationResetSubcode {
492 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
493 let subcode = match self {
494 Self::All(_) => "All",
495 Self::UserData(_) => "User data",
496 Self::SimpleBilling(_) => "Simple billing",
497 Self::EnhancedBilling(_) => "Enhanced billing",
498 Self::MultiTariffBilling(_) => "Multi-tariff billing",
499 Self::InstantaneousValues(_) => "Instantaneous values",
500 Self::LoadManagementValues(_) => "Load management values",
501 Self::Reserved1(_) => "Reserved",
502 Self::InstallationStartup(_) => "Installation startup",
503 Self::Testing(_) => "Testing",
504 Self::Calibration(_) => "Calibration",
505 Self::ConfigurationUpdates(_) => "Configuration updates",
506 Self::Manufacturing(_) => "Manufacturing",
507 Self::Development(_) => "Development",
508 Self::Selftest(_) => "Self-test",
509 Self::Reserved2(_) => "Reserved",
510 };
511 write!(f, "{}", subcode)
512 }
513}
514
515impl ApplicationResetSubcode {
516 #[must_use]
517 pub const fn from(value: u8) -> Self {
518 match value & 0b1111 {
519 0b0000 => Self::All(value),
521 0b0001 => Self::UserData(value),
522 0b0010 => Self::SimpleBilling(value),
523 0b0011 => Self::EnhancedBilling(value),
524 0b0100 => Self::MultiTariffBilling(value),
525 0b0101 => Self::InstantaneousValues(value),
526 0b0110 => Self::LoadManagementValues(value),
527 0b0111 => Self::Reserved1(value),
528 0b1000 => Self::InstallationStartup(value),
529 0b1001 => Self::Testing(value),
530 0b1010 => Self::Calibration(value),
531 0b1011 => Self::ConfigurationUpdates(value),
532 0b1100 => Self::Manufacturing(value),
533 0b1101 => Self::Development(value),
534 0b1110 => Self::Selftest(value),
535 _ => Self::Reserved2(value),
536 }
537 }
538}
539
540#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
541#[derive(Debug, PartialEq)]
542#[cfg_attr(feature = "defmt", derive(defmt::Format))]
543pub struct Counter {
544 count: u32,
545}
546
547#[cfg(feature = "std")]
548impl fmt::Display for Counter {
549 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
550 write!(f, "{:08}", self.count)
551 }
552}
553
554impl Counter {
555 pub fn from_bcd_hex_digits(digits: [u8; 4]) -> Result<Self, ApplicationLayerError> {
556 let count = bcd_hex_digits_to_u32(digits)?;
557 Ok(Self { count })
558 }
559}
560
561#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
562#[allow(clippy::large_enum_variant)]
563#[derive(Debug, PartialEq)]
564#[cfg_attr(feature = "defmt", derive(defmt::Format))]
565#[non_exhaustive]
566pub enum UserDataBlock<'a> {
567 ResetAtApplicationLevel {
568 subcode: ApplicationResetSubcode,
569 },
570 FixedDataStructure {
571 identification_number: IdentificationNumber,
572 access_number: u8,
573 status: StatusField,
574 device_type_and_unit: u16,
575 counter1: Counter,
576 counter2: Counter,
577 },
578 VariableDataStructureWithLongTplHeader {
579 extended_link_layer: Option<ExtendedLinkLayer>,
580 long_tpl_header: LongTplHeader,
581 #[cfg_attr(feature = "serde", serde(skip_serializing))]
582 variable_data_block: &'a [u8],
583 },
584
585 VariableDataStructureWithShortTplHeader {
586 extended_link_layer: Option<ExtendedLinkLayer>,
587 short_tpl_header: ShortTplHeader,
588 #[cfg_attr(feature = "serde", serde(skip_serializing))]
589 variable_data_block: &'a [u8],
590 },
591
592 VariableDataStructureWithoutTplHeader {
593 extended_link_layer: Option<ExtendedLinkLayer>,
594 #[cfg_attr(feature = "serde", serde(skip_serializing))]
595 variable_data_block: &'a [u8],
596 },
597}
598
599impl<'a> UserDataBlock<'a> {
600 #[must_use]
605 pub fn data_records(&self) -> Option<DataRecords<'_>> {
606 match self {
607 Self::VariableDataStructureWithLongTplHeader {
608 long_tpl_header,
609 variable_data_block,
610 ..
611 } if !long_tpl_header.is_encrypted() => Some(parse_data_records_with_header(
612 variable_data_block,
613 long_tpl_header,
614 )),
615 Self::VariableDataStructureWithShortTplHeader {
616 short_tpl_header,
617 variable_data_block,
618 ..
619 } if !short_tpl_header.is_encrypted() => Some(parse_data_records(variable_data_block)),
620 Self::VariableDataStructureWithoutTplHeader {
621 variable_data_block,
622 ..
623 } => Some(parse_data_records(variable_data_block)),
624 _ => None,
625 }
626 }
627
628 #[must_use]
629 pub fn is_encrypted(&self) -> Option<bool> {
630 match self {
631 Self::VariableDataStructureWithLongTplHeader {
632 long_tpl_header, ..
633 } => Some(long_tpl_header.is_encrypted()),
634 _ => None,
635 }
636 }
637
638 #[must_use]
640 pub fn variable_data_len(&self) -> usize {
641 match self {
642 Self::VariableDataStructureWithLongTplHeader {
643 variable_data_block,
644 ..
645 } => variable_data_block.len(),
646 Self::VariableDataStructureWithShortTplHeader {
647 variable_data_block,
648 ..
649 } => variable_data_block.len(),
650 Self::VariableDataStructureWithoutTplHeader {
651 variable_data_block,
652 ..
653 } => variable_data_block.len(),
654 _ => 0,
655 }
656 }
657
658 #[cfg(feature = "decryption")]
659 pub fn decrypt_variable_data<K: crate::decryption::KeyProvider>(
660 &self,
661 provider: &K,
662 output: &mut [u8],
663 ) -> Result<usize, crate::decryption::DecryptionError> {
664 use crate::decryption::{DecryptionError, EncryptedPayload, KeyContext};
665
666 match self {
667 Self::VariableDataStructureWithLongTplHeader {
668 long_tpl_header,
669 variable_data_block,
670 ..
671 } => {
672 if !long_tpl_header.is_encrypted() {
673 return Err(NotEncrypted);
674 }
675
676 let security_mode = long_tpl_header
677 .short_tpl_header
678 .configuration_field
679 .security_mode();
680
681 let manufacturer = long_tpl_header
682 .manufacturer
683 .map_err(|_| DecryptionError::DecryptionFailed)?;
684
685 let context = KeyContext {
686 manufacturer,
687 identification_number: long_tpl_header.identification_number.number,
688 version: long_tpl_header.version,
689 device_type: long_tpl_header.device_type,
690 security_mode,
691 access_number: long_tpl_header.short_tpl_header.access_number,
692 };
693
694 let payload = EncryptedPayload::new(variable_data_block, context);
695 payload.decrypt_into(provider, output)
696 }
697 Self::VariableDataStructureWithShortTplHeader {
698 short_tpl_header, ..
699 } => {
700 if !short_tpl_header.is_encrypted() {
701 Err(NotEncrypted)
702 } else {
703 Err(UnknownEncryptionState)
706 }
707 }
708 _ => Err(DecryptionError::UnknownEncryptionState),
709 }
710 }
711
712 #[cfg(feature = "decryption")]
715 pub fn decrypt_variable_data_with_context<K: crate::decryption::KeyProvider>(
716 &self,
717 provider: &K,
718 manufacturer: ManufacturerCode,
719 identification_number: u32,
720 version: u8,
721 device_type: DeviceType,
722 output: &mut [u8],
723 ) -> Result<usize, crate::decryption::DecryptionError> {
724 use crate::decryption::{DecryptionError, EncryptedPayload, KeyContext};
725
726 match self {
727 Self::VariableDataStructureWithShortTplHeader {
728 short_tpl_header,
729 variable_data_block,
730 ..
731 } => {
732 if !short_tpl_header.is_encrypted() {
733 return Err(NotEncrypted);
734 }
735
736 let security_mode = short_tpl_header.configuration_field.security_mode();
737
738 let context = KeyContext {
739 manufacturer,
740 identification_number,
741 version,
742 device_type,
743 security_mode,
744 access_number: short_tpl_header.access_number,
745 };
746
747 let payload = EncryptedPayload::new(variable_data_block, context);
748 payload.decrypt_into(provider, output)
749 }
750 Self::VariableDataStructureWithLongTplHeader { .. } => {
751 self.decrypt_variable_data(provider, output)
753 }
754 _ => Err(DecryptionError::UnknownEncryptionState),
755 }
756 }
757}
758
759#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
760#[derive(Debug, PartialEq)]
761#[cfg_attr(feature = "defmt", derive(defmt::Format))]
762pub struct LongTplHeader {
763 pub identification_number: IdentificationNumber,
764 #[cfg_attr(
765 feature = "serde",
766 serde(skip_deserializing, default = "default_manufacturer_result")
767 )]
768 pub manufacturer: Result<ManufacturerCode, ApplicationLayerError>,
769 pub version: u8,
770 pub device_type: DeviceType,
771 pub short_tpl_header: ShortTplHeader,
772 pub lsb_order: bool,
773}
774
775#[cfg(feature = "serde")]
776fn default_manufacturer_result() -> Result<ManufacturerCode, ApplicationLayerError> {
777 Err(ApplicationLayerError::InsufficientData)
778}
779
780#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
781#[derive(Debug, PartialEq)]
782#[cfg_attr(feature = "defmt", derive(defmt::Format))]
783pub struct ShortTplHeader {
784 pub access_number: u8,
785 pub status: StatusField,
786 pub configuration_field: ConfigurationField,
787}
788
789impl LongTplHeader {
790 #[must_use]
791 pub fn is_encrypted(&self) -> bool {
792 use m_bus_core::SecurityMode;
793 !matches!(
794 self.short_tpl_header.configuration_field.security_mode(),
795 SecurityMode::NoEncryption
796 )
797 }
798}
799
800impl ShortTplHeader {
801 #[must_use]
802 pub fn is_encrypted(&self) -> bool {
803 use m_bus_core::SecurityMode;
804 !matches!(
805 self.configuration_field.security_mode(),
806 SecurityMode::NoEncryption
807 )
808 }
809}
810
811impl<'a> TryFrom<&'a [u8]> for UserDataBlock<'a> {
812 type Error = ApplicationLayerError;
813
814 fn try_from(data: &'a [u8]) -> Result<Self, ApplicationLayerError> {
815 if data.is_empty() {
816 return Err(ApplicationLayerError::MissingControlInformation);
817 }
818 let control_information = ControlInformation::from(
819 *data
820 .first()
821 .ok_or(ApplicationLayerError::InsufficientData)?,
822 )?;
823
824 match control_information {
825 ControlInformation::ResetAtApplicationLevel => {
826 let subcode = ApplicationResetSubcode::from(
827 *data.get(1).ok_or(ApplicationLayerError::InsufficientData)?,
828 );
829 Ok(UserDataBlock::ResetAtApplicationLevel { subcode })
830 }
831 ControlInformation::SendData => Err(ApplicationLayerError::Unimplemented {
832 feature: "SendData control information",
833 }),
834 ControlInformation::SelectSlave => Err(ApplicationLayerError::Unimplemented {
835 feature: "SelectSlave control information",
836 }),
837 ControlInformation::SynchronizeSlave => Err(ApplicationLayerError::Unimplemented {
838 feature: "SynchronizeSlave control information",
839 }),
840 ControlInformation::SetBaudRate300 => Err(ApplicationLayerError::Unimplemented {
841 feature: "SetBaudRate300 control information",
842 }),
843 ControlInformation::SetBaudRate600 => Err(ApplicationLayerError::Unimplemented {
844 feature: "SetBaudRate600 control information",
845 }),
846 ControlInformation::SetBaudRate1200 => Err(ApplicationLayerError::Unimplemented {
847 feature: "SetBaudRate1200 control information",
848 }),
849 ControlInformation::SetBaudRate2400 => Err(ApplicationLayerError::Unimplemented {
850 feature: "SetBaudRate2400 control information",
851 }),
852 ControlInformation::SetBaudRate4800 => Err(ApplicationLayerError::Unimplemented {
853 feature: "SetBaudRate4800 control information",
854 }),
855 ControlInformation::SetBaudRate9600 => Err(ApplicationLayerError::Unimplemented {
856 feature: "SetBaudRate9600 control information",
857 }),
858 ControlInformation::SetBaudRate19200 => Err(ApplicationLayerError::Unimplemented {
859 feature: "SetBaudRate19200 control information",
860 }),
861 ControlInformation::SetBaudRate38400 => Err(ApplicationLayerError::Unimplemented {
862 feature: "SetBaudRate38400 control information",
863 }),
864 ControlInformation::OutputRAMContent => Err(ApplicationLayerError::Unimplemented {
865 feature: "OutputRAMContent control information",
866 }),
867 ControlInformation::WriteRAMContent => Err(ApplicationLayerError::Unimplemented {
868 feature: "WriteRAMContent control information",
869 }),
870 ControlInformation::StartCalibrationTestMode => {
871 Err(ApplicationLayerError::Unimplemented {
872 feature: "StartCalibrationTestMode control information",
873 })
874 }
875 ControlInformation::ReadEEPROM => Err(ApplicationLayerError::Unimplemented {
876 feature: "ReadEEPROM control information",
877 }),
878 ControlInformation::StartSoftwareTest => Err(ApplicationLayerError::Unimplemented {
879 feature: "StartSoftwareTest control information",
880 }),
881 ControlInformation::HashProcedure(_) => Err(ApplicationLayerError::Unimplemented {
882 feature: "HashProcedure control information",
883 }),
884 ControlInformation::SendErrorStatus => Err(ApplicationLayerError::Unimplemented {
885 feature: "SendErrorStatus control information",
886 }),
887 ControlInformation::SendAlarmStatus => Err(ApplicationLayerError::Unimplemented {
888 feature: "SendAlarmStatus control information",
889 }),
890 ControlInformation::ResponseWithVariableDataStructure { lsb_order } => {
891 let mut iter = data.iter().skip(1);
892 let mut identification_number_bytes = [
893 *iter.next().ok_or(ApplicationLayerError::InsufficientData)?,
894 *iter.next().ok_or(ApplicationLayerError::InsufficientData)?,
895 *iter.next().ok_or(ApplicationLayerError::InsufficientData)?,
896 *iter.next().ok_or(ApplicationLayerError::InsufficientData)?,
897 ];
898 if lsb_order {
899 identification_number_bytes.reverse();
900 }
901
902 Ok(UserDataBlock::VariableDataStructureWithLongTplHeader {
903 long_tpl_header: LongTplHeader {
904 identification_number: IdentificationNumber::from_bcd_hex_digits(
905 identification_number_bytes,
906 )?,
907 manufacturer: ManufacturerCode::from_id(u16::from_le_bytes([
908 *iter.next().ok_or(ApplicationLayerError::InsufficientData)?,
909 *iter.next().ok_or(ApplicationLayerError::InsufficientData)?,
910 ])),
911 version: *iter.next().ok_or(ApplicationLayerError::InsufficientData)?,
912 device_type: DeviceType::from(
913 *iter.next().ok_or(ApplicationLayerError::InsufficientData)?,
914 ),
915 short_tpl_header: ShortTplHeader {
916 access_number: *iter
917 .next()
918 .ok_or(ApplicationLayerError::InsufficientData)?,
919 status: {
920 StatusField::from_bits_truncate(
921 *iter.next().ok_or(ApplicationLayerError::InsufficientData)?,
922 )
923 },
924 configuration_field: {
925 ConfigurationField::from_bytes(
926 *iter.next().ok_or(ApplicationLayerError::InsufficientData)?,
927 *iter.next().ok_or(ApplicationLayerError::InsufficientData)?,
928 )
929 },
930 },
931 lsb_order,
932 },
933 variable_data_block: data
934 .get(13..data.len())
935 .ok_or(ApplicationLayerError::InsufficientData)?,
936 extended_link_layer: None,
937 })
938 }
939 ControlInformation::ResponseWithFixedDataStructure => {
940 let mut iter = data.iter().skip(1);
941 let identification_number = IdentificationNumber::from_bcd_hex_digits([
942 *iter.next().ok_or(ApplicationLayerError::InsufficientData)?,
943 *iter.next().ok_or(ApplicationLayerError::InsufficientData)?,
944 *iter.next().ok_or(ApplicationLayerError::InsufficientData)?,
945 *iter.next().ok_or(ApplicationLayerError::InsufficientData)?,
946 ])?;
947
948 let access_number = *iter.next().ok_or(ApplicationLayerError::InsufficientData)?;
949
950 let status = StatusField::from_bits_truncate(
951 *iter.next().ok_or(ApplicationLayerError::InsufficientData)?,
952 );
953 let device_type_and_unit = u16::from_be_bytes([
954 *iter.next().ok_or(ApplicationLayerError::InsufficientData)?,
955 *iter.next().ok_or(ApplicationLayerError::InsufficientData)?,
956 ]);
957 let counter1 = Counter::from_bcd_hex_digits([
958 *iter.next().ok_or(ApplicationLayerError::InsufficientData)?,
959 *iter.next().ok_or(ApplicationLayerError::InsufficientData)?,
960 *iter.next().ok_or(ApplicationLayerError::InsufficientData)?,
961 *iter.next().ok_or(ApplicationLayerError::InsufficientData)?,
962 ])?;
963 let counter2 = Counter::from_bcd_hex_digits([
964 *iter.next().ok_or(ApplicationLayerError::InsufficientData)?,
965 *iter.next().ok_or(ApplicationLayerError::InsufficientData)?,
966 *iter.next().ok_or(ApplicationLayerError::InsufficientData)?,
967 *iter.next().ok_or(ApplicationLayerError::InsufficientData)?,
968 ])?;
969 Ok(UserDataBlock::FixedDataStructure {
970 identification_number,
971 access_number,
972 status,
973 device_type_and_unit,
974 counter1,
975 counter2,
976 })
977 }
978 ControlInformation::DataSentWithShortTransportLayer => {
979 Err(ApplicationLayerError::Unimplemented {
980 feature: "DataSentWithShortTransportLayer control information",
981 })
982 }
983 ControlInformation::DataSentWithLongTransportLayer => {
984 Err(ApplicationLayerError::Unimplemented {
985 feature: "DataSentWithLongTransportLayer control information",
986 })
987 }
988 ControlInformation::CosemDataWithLongTransportLayer => {
989 Err(ApplicationLayerError::Unimplemented {
990 feature: "CosemDataWithLongTransportLayer control information",
991 })
992 }
993 ControlInformation::CosemDataWithShortTransportLayer => {
994 Err(ApplicationLayerError::Unimplemented {
995 feature: "CosemDataWithShortTransportLayer control information",
996 })
997 }
998 ControlInformation::ObisDataReservedLongTransportLayer => {
999 Err(ApplicationLayerError::Unimplemented {
1000 feature: "ObisDataReservedLongTransportLayer control information",
1001 })
1002 }
1003 ControlInformation::ObisDataReservedShortTransportLayer => {
1004 Err(ApplicationLayerError::Unimplemented {
1005 feature: "ObisDataReservedShortTransportLayer control information",
1006 })
1007 }
1008 ControlInformation::ApplicationLayerFormatFrameNoTransport => {
1009 Err(ApplicationLayerError::Unimplemented {
1010 feature: "ApplicationLayerFormatFrameNoTransport control information",
1011 })
1012 }
1013 ControlInformation::ApplicationLayerFormatFrameShortTransport => {
1014 Err(ApplicationLayerError::Unimplemented {
1015 feature: "ApplicationLayerFormatFrameShortTransport control information",
1016 })
1017 }
1018 ControlInformation::ApplicationLayerFormatFrameLongTransport => {
1019 Err(ApplicationLayerError::Unimplemented {
1020 feature: "ApplicationLayerFormatFrameLongTransport control information",
1021 })
1022 }
1023 ControlInformation::ClockSyncAbsolute => Err(ApplicationLayerError::Unimplemented {
1024 feature: "ClockSyncAbsolute control information",
1025 }),
1026 ControlInformation::ClockSyncRelative => Err(ApplicationLayerError::Unimplemented {
1027 feature: "ClockSyncRelative control information",
1028 }),
1029 ControlInformation::ApplicationErrorShortTransport => {
1030 Err(ApplicationLayerError::Unimplemented {
1031 feature: "ApplicationErrorShortTransport control information",
1032 })
1033 }
1034 ControlInformation::ApplicationErrorLongTransport => {
1035 Err(ApplicationLayerError::Unimplemented {
1036 feature: "ApplicationErrorLongTransport control information",
1037 })
1038 }
1039 ControlInformation::AlarmShortTransport => Err(ApplicationLayerError::Unimplemented {
1040 feature: "AlarmShortTransport control information",
1041 }),
1042 ControlInformation::AlarmLongTransport => Err(ApplicationLayerError::Unimplemented {
1043 feature: "AlarmLongTransport control information",
1044 }),
1045 ControlInformation::ApplicationLayerNoTransport => {
1046 Ok(UserDataBlock::VariableDataStructureWithoutTplHeader {
1047 extended_link_layer: None,
1048 variable_data_block: data
1049 .get(1..data.len())
1050 .ok_or(ApplicationLayerError::InsufficientData)?,
1051 })
1052 }
1053 ControlInformation::ApplicationLayerCompactFrameNoTransport => {
1054 Err(ApplicationLayerError::Unimplemented {
1055 feature: "ApplicationLayerCompactFrameNoTransport control information",
1056 })
1057 }
1058 ControlInformation::ApplicationLayerShortTransport => {
1059 let has_encryption_config_byte = data[0] == 0xA0;
1062 let skip_count = if has_encryption_config_byte { 2 } else { 1 };
1063 let data_block_offset = if has_encryption_config_byte { 6 } else { 5 };
1064
1065 let mut iter = data.iter().skip(skip_count);
1066
1067 Ok(UserDataBlock::VariableDataStructureWithShortTplHeader {
1068 short_tpl_header: ShortTplHeader {
1069 access_number: *iter
1070 .next()
1071 .ok_or(ApplicationLayerError::InsufficientData)?,
1072 status: {
1073 StatusField::from_bits_truncate(
1074 *iter.next().ok_or(ApplicationLayerError::InsufficientData)?,
1075 )
1076 },
1077 configuration_field: {
1078 ConfigurationField::from_bytes(
1079 *iter.next().ok_or(ApplicationLayerError::InsufficientData)?,
1080 *iter.next().ok_or(ApplicationLayerError::InsufficientData)?,
1081 )
1082 },
1083 },
1084 variable_data_block: data
1085 .get(data_block_offset..data.len())
1086 .ok_or(ApplicationLayerError::InsufficientData)?,
1087 extended_link_layer: None,
1088 })
1089 }
1090 ControlInformation::ApplicationLayerCompactFrameShortTransport => {
1091 Err(ApplicationLayerError::Unimplemented {
1092 feature: "ApplicationLayerCompactFrameShortTransport control information",
1093 })
1094 }
1095 ControlInformation::CosemApplicationLayerLongTransport => {
1096 Err(ApplicationLayerError::Unimplemented {
1097 feature: "CosemApplicationLayerLongTransport control information",
1098 })
1099 }
1100 ControlInformation::CosemApplicationLayerShortTransport => {
1101 Err(ApplicationLayerError::Unimplemented {
1102 feature: "CosemApplicationLayerShortTransport control information",
1103 })
1104 }
1105 ControlInformation::ObisApplicationLayerReservedLongTransport => {
1106 Err(ApplicationLayerError::Unimplemented {
1107 feature: "ObisApplicationLayerReservedLongTransport control information",
1108 })
1109 }
1110 ControlInformation::ObisApplicationLayerReservedShortTransport => {
1111 Err(ApplicationLayerError::Unimplemented {
1112 feature: "ObisApplicationLayerReservedShortTransport control information",
1113 })
1114 }
1115 ControlInformation::TransportLayerLongReadoutToMeter => {
1116 Err(ApplicationLayerError::Unimplemented {
1117 feature: "TransportLayerLongReadoutToMeter control information",
1118 })
1119 }
1120 ControlInformation::NetworkLayerData => Err(ApplicationLayerError::Unimplemented {
1121 feature: "NetworkLayerData control information",
1122 }),
1123 ControlInformation::FutureUse => Err(ApplicationLayerError::Unimplemented {
1124 feature: "FutureUse control information",
1125 }),
1126 ControlInformation::NetworkManagementApplication => {
1127 Err(ApplicationLayerError::Unimplemented {
1128 feature: "NetworkManagementApplication control information",
1129 })
1130 }
1131 ControlInformation::TransportLayerCompactFrame => {
1132 Err(ApplicationLayerError::Unimplemented {
1133 feature: "TransportLayerCompactFrame control information",
1134 })
1135 }
1136 ControlInformation::TransportLayerFormatFrame => {
1137 Err(ApplicationLayerError::Unimplemented {
1138 feature: "TransportLayerFormatFrame control information",
1139 })
1140 }
1141 ControlInformation::NetworkManagementDataReserved => {
1142 Err(ApplicationLayerError::Unimplemented {
1143 feature: "NetworkManagementDataReserved control information",
1144 })
1145 }
1146 ControlInformation::TransportLayerShortMeterToReadout => {
1147 Err(ApplicationLayerError::Unimplemented {
1148 feature: "TransportLayerShortMeterToReadout control information",
1149 })
1150 }
1151 ControlInformation::TransportLayerLongMeterToReadout => {
1152 Err(ApplicationLayerError::Unimplemented {
1153 feature: "TransportLayerLongMeterToReadout control information",
1154 })
1155 }
1156 ControlInformation::ExtendedLinkLayerI => {
1157 let mut iter = data.iter();
1158 iter.next();
1159 let extended_link_layer = Some(ExtendedLinkLayer {
1160 communication_control: *iter
1161 .next()
1162 .ok_or(ApplicationLayerError::InsufficientData)?,
1163 access_number: *iter.next().ok_or(ApplicationLayerError::InsufficientData)?,
1164 receiver_address: None,
1165 encryption: None,
1166 });
1167 match UserDataBlock::try_from(iter.as_slice()) {
1168 Ok(UserDataBlock::VariableDataStructureWithShortTplHeader {
1169 short_tpl_header,
1170 variable_data_block,
1171 ..
1172 }) => Ok(UserDataBlock::VariableDataStructureWithShortTplHeader {
1173 extended_link_layer,
1174 short_tpl_header,
1175 variable_data_block,
1176 }),
1177 Ok(UserDataBlock::VariableDataStructureWithoutTplHeader {
1178 variable_data_block,
1179 ..
1180 }) => Ok(UserDataBlock::VariableDataStructureWithoutTplHeader {
1181 extended_link_layer,
1182 variable_data_block,
1183 }),
1184 _ => Err(ApplicationLayerError::MissingControlInformation),
1185 }
1186 }
1187 ControlInformation::ExtendedLinkLayerII => {
1188 let (ell, ell_size) = ExtendedLinkLayer::parse(
1190 data.get(1..)
1191 .ok_or(ApplicationLayerError::InsufficientData)?,
1192 extended_link_layer::EllFormat::FormatII,
1193 )?;
1194 let app_data_offset = 1 + ell_size;
1195
1196 let short_tpl_header = ShortTplHeader {
1199 access_number: ell.access_number,
1200 status: StatusField::from_bits_truncate(ell.communication_control),
1201 configuration_field: ConfigurationField::from_bytes(0x00, 0x00),
1202 };
1203
1204 Ok(UserDataBlock::VariableDataStructureWithShortTplHeader {
1205 extended_link_layer: Some(ell),
1206 short_tpl_header,
1207 variable_data_block: data
1208 .get(app_data_offset..)
1209 .ok_or(ApplicationLayerError::InsufficientData)?,
1210 })
1211 }
1212 ControlInformation::ExtendedLinkLayerIII => {
1213 let (ell, ell_size) = ExtendedLinkLayer::parse(
1215 data.get(1..)
1216 .ok_or(ApplicationLayerError::InsufficientData)?,
1217 extended_link_layer::EllFormat::FormatIII,
1218 )?;
1219 let app_data_offset = 1 + ell_size;
1220
1221 let short_tpl_header = ShortTplHeader {
1224 access_number: ell.access_number,
1225 status: StatusField::from_bits_truncate(ell.communication_control),
1226 configuration_field: ConfigurationField::from_bytes(0x00, 0x00),
1227 };
1228
1229 Ok(UserDataBlock::VariableDataStructureWithShortTplHeader {
1230 extended_link_layer: Some(ell),
1231 short_tpl_header,
1232 variable_data_block: data
1233 .get(app_data_offset..)
1234 .ok_or(ApplicationLayerError::InsufficientData)?,
1235 })
1236 }
1237 }
1238 }
1239}
1240
1241#[allow(clippy::unwrap_used, clippy::panic)]
1242#[cfg(all(test, feature = "std"))]
1243mod tests {
1244
1245 use super::*;
1246
1247 #[test]
1248 fn undecodable_record_does_not_discard_the_rest_of_the_frame() {
1249 let data = [
1253 0x0C, 0x12, 0x42, 0x07, 0x00, 0x00, 0x3C, 0x2A, 0xDD, 0xB4, 0xEB, 0xDD, 0x0A, 0x5A, 0x04, 0x02, ];
1257
1258 let results: Vec<_> = parse_data_records(&data).collect();
1259 assert_eq!(results.len(), 3);
1260 assert!(results[0].is_ok());
1261 assert!(results[1].is_err());
1262 assert!(results[2].is_ok());
1263
1264 let flow_temperature = results[2].as_ref().expect("third record parses");
1265 assert_eq!(
1266 flow_temperature.value(),
1267 Some(&data_information::DataType::Number(204.0))
1268 );
1269 }
1270
1271 #[test]
1272 fn unresynchronisable_record_stops_the_stream() {
1273 let data = [0x0D, 0x2A, 0x40, 0x01, 0x02];
1276
1277 let results: Vec<_> = parse_data_records(&data).collect();
1278 assert_eq!(results.len(), 1);
1279 assert!(results[0].is_err());
1280 }
1281
1282 #[test]
1283 fn test_control_information() {
1284 assert_eq!(
1285 ControlInformation::from(0x50),
1286 Ok(ControlInformation::ResetAtApplicationLevel)
1287 );
1288 assert_eq!(
1289 ControlInformation::from(0x51),
1290 Ok(ControlInformation::SendData)
1291 );
1292 assert_eq!(
1293 ControlInformation::from(0x52),
1294 Ok(ControlInformation::SelectSlave)
1295 );
1296 assert_eq!(
1297 ControlInformation::from(0x54),
1298 Ok(ControlInformation::SynchronizeSlave)
1299 );
1300 assert_eq!(
1301 ControlInformation::from(0xB8),
1302 Ok(ControlInformation::SetBaudRate300)
1303 );
1304 assert_eq!(
1305 ControlInformation::from(0xB9),
1306 Ok(ControlInformation::SetBaudRate600)
1307 );
1308 assert_eq!(
1309 ControlInformation::from(0xBA),
1310 Ok(ControlInformation::SetBaudRate1200)
1311 );
1312 assert_eq!(
1313 ControlInformation::from(0xBB),
1314 Ok(ControlInformation::SetBaudRate2400)
1315 );
1316 assert_eq!(
1317 ControlInformation::from(0xBC),
1318 Ok(ControlInformation::SetBaudRate4800)
1319 );
1320 assert_eq!(
1321 ControlInformation::from(0xBD),
1322 Ok(ControlInformation::SetBaudRate9600)
1323 );
1324 assert_eq!(
1325 ControlInformation::from(0xBE),
1326 Ok(ControlInformation::SetBaudRate19200)
1327 );
1328 assert_eq!(
1329 ControlInformation::from(0xBF),
1330 Ok(ControlInformation::SetBaudRate38400)
1331 );
1332 assert_eq!(
1333 ControlInformation::from(0xB1),
1334 Ok(ControlInformation::OutputRAMContent)
1335 );
1336 assert_eq!(
1337 ControlInformation::from(0xB2),
1338 Ok(ControlInformation::WriteRAMContent)
1339 );
1340 assert_eq!(
1341 ControlInformation::from(0xB3),
1342 Ok(ControlInformation::StartCalibrationTestMode)
1343 );
1344 assert_eq!(
1345 ControlInformation::from(0xB4),
1346 Ok(ControlInformation::ReadEEPROM)
1347 );
1348 assert_eq!(
1349 ControlInformation::from(0xB6),
1350 Ok(ControlInformation::StartSoftwareTest)
1351 );
1352 assert_eq!(
1353 ControlInformation::from(0x90),
1354 Ok(ControlInformation::HashProcedure(0,))
1355 );
1356 assert_eq!(
1357 ControlInformation::from(0x91),
1358 Ok(ControlInformation::HashProcedure(1,))
1359 );
1360 }
1361
1362 #[test]
1363 fn test_reset_subcode() {
1364 let data = [0x50, 0x10];
1366 let result = UserDataBlock::try_from(data.as_slice());
1367 assert_eq!(
1368 result,
1369 Ok(UserDataBlock::ResetAtApplicationLevel {
1370 subcode: ApplicationResetSubcode::All(0x10)
1371 })
1372 );
1373 }
1374
1375 #[test]
1376 fn test_application_layer_no_transport() {
1377 let data = [0x78, 0x0B, 0x13, 0x43, 0x65, 0x87];
1378 let result = UserDataBlock::try_from(data.as_slice());
1379
1380 assert_eq!(
1381 result,
1382 Ok(UserDataBlock::VariableDataStructureWithoutTplHeader {
1383 extended_link_layer: None,
1384 variable_data_block: &data[1..],
1385 })
1386 );
1387 }
1388
1389 #[test]
1390 fn test_ell_i_with_application_layer_no_transport() {
1391 let data = [0x8C, 0x20, 0x27, 0x78, 0x0B, 0x13, 0x43, 0x65, 0x87];
1392 let result = UserDataBlock::try_from(data.as_slice());
1393
1394 match result {
1395 Ok(UserDataBlock::VariableDataStructureWithoutTplHeader {
1396 extended_link_layer: Some(ell),
1397 variable_data_block,
1398 }) => {
1399 assert_eq!(ell.communication_control, 0x20);
1400 assert_eq!(ell.access_number, 0x27);
1401 assert_eq!(variable_data_block, &data[4..]);
1402 }
1403 other => panic!("expected no-TPL user data after ELL I, got {other:?}"),
1404 }
1405 }
1406
1407 #[test]
1408 fn test_device_type_roundtrip() {
1409 let test_cases = [
1411 (0x00, DeviceType::Other),
1412 (0x01, DeviceType::OilMeter),
1413 (0x02, DeviceType::ElectricityMeter),
1414 (0x03, DeviceType::GasMeter),
1415 (0x04, DeviceType::HeatMeterReturn),
1416 (0x05, DeviceType::SteamMeter),
1417 (0x06, DeviceType::WarmWaterMeter),
1418 (0x07, DeviceType::WaterMeter),
1419 (0x08, DeviceType::HeatCostAllocator),
1420 (0x09, DeviceType::CompressedAir),
1421 (0x0A, DeviceType::CoolingMeterReturn),
1422 (0x0B, DeviceType::CoolingMeterFlow),
1423 (0x0C, DeviceType::HeatMeterFlow),
1424 (0x0D, DeviceType::CombinedHeatCoolingMeter),
1425 (0x0E, DeviceType::BusSystemComponent),
1426 (0x0F, DeviceType::UnknownDevice),
1427 (0x10, DeviceType::IrrigationWaterMeter),
1428 (0x11, DeviceType::WaterDataLogger),
1429 (0x12, DeviceType::GasDataLogger),
1430 (0x13, DeviceType::GasConverter),
1431 (0x14, DeviceType::CalorificValue),
1432 (0x15, DeviceType::HotWaterMeter),
1433 (0x16, DeviceType::ColdWaterMeter),
1434 (0x17, DeviceType::DualRegisterWaterMeter),
1435 (0x18, DeviceType::PressureMeter),
1436 (0x19, DeviceType::AdConverter),
1437 (0x1A, DeviceType::SmokeDetector),
1438 (0x1B, DeviceType::RoomSensor),
1439 (0x1C, DeviceType::GasDetector),
1440 (0x20, DeviceType::ElectricityBreaker),
1441 (0x21, DeviceType::Valve),
1442 (0x25, DeviceType::CustomerUnit),
1443 (0x28, DeviceType::WasteWaterMeter),
1444 (0x29, DeviceType::Garbage),
1445 (0x30, DeviceType::ServiceTool),
1446 (0x31, DeviceType::CommunicationController),
1447 (0x32, DeviceType::UnidirectionalRepeater),
1448 (0x33, DeviceType::BidirectionalRepeater),
1449 (0x36, DeviceType::RadioConverterSystemSide),
1450 (0x37, DeviceType::RadioConverterMeterSide),
1451 (0x38, DeviceType::BusConverterMeterSide),
1452 (0xFF, DeviceType::Wildcard),
1453 (0x1D, DeviceType::ReservedSensor(0x1D)), (0x22, DeviceType::ReservedSwitch(0x22)), (0x40, DeviceType::Reserved(0x40)), ];
1458
1459 for (byte, expected_device_type) in test_cases {
1460 let device_type = DeviceType::from(byte);
1461 assert_eq!(device_type, expected_device_type);
1462 assert_eq!(u8::from(device_type), byte);
1463 }
1464
1465 assert_eq!(u8::from(DeviceType::Reserved(0x40)), 0x40);
1467 assert_eq!(u8::from(DeviceType::ReservedSensor(0x1D)), 0x1D);
1468 assert_eq!(u8::from(DeviceType::ReservedSwitch(0x22)), 0x22);
1469
1470 assert_eq!(u8::from(DeviceType::UnknownDevice), 0x0F);
1472 }
1473
1474 #[test]
1475 fn test_identification_number() -> Result<(), ApplicationLayerError> {
1476 let data = [0x78, 0x56, 0x34, 0x12];
1477 let result = IdentificationNumber::from_bcd_hex_digits(data)?;
1478 assert_eq!(result, IdentificationNumber { number: 12345678 });
1479 Ok(())
1480 }
1481
1482 #[test]
1483 fn test_fixed_data_structure() {
1484 let data = [
1485 0x73, 0x78, 0x56, 0x34, 0x12, 0x0A, 0x00, 0xE9, 0x7E, 0x01, 0x00, 0x00, 0x00, 0x35,
1486 0x01, 0x00, 0x00,
1487 ];
1488
1489 let result = UserDataBlock::try_from(data.as_slice());
1490
1491 assert_eq!(
1492 result,
1493 Ok(UserDataBlock::FixedDataStructure {
1494 identification_number: IdentificationNumber { number: 12345678 },
1495 access_number: 0x0A,
1496 status: StatusField::from_bits_truncate(0x00),
1497 device_type_and_unit: 0xE97E,
1498 counter1: Counter { count: 1 },
1499 counter2: Counter { count: 135 },
1500 })
1501 );
1502 }
1503
1504 #[test]
1505 fn test_manufacturer_code() -> Result<(), ApplicationLayerError> {
1506 let code = ManufacturerCode::from_id(0x1ee6)?;
1507 assert_eq!(
1508 code,
1509 ManufacturerCode {
1510 code: ['G', 'W', 'F']
1511 }
1512 );
1513 Ok(())
1514 }
1515
1516 #[test]
1517 fn global_readout_request_does_not_consume_following_records() {
1518 let data: &[u8] = &[0x7F, 0x01, 0x13, 0x05];
1520 let records: Vec<_> = DataRecords::new(data, None).flatten().collect();
1521 assert_eq!(records.len(), 2);
1522 }
1523
1524 #[test]
1525 fn parse_data_records_api_returns_record_values() {
1526 use crate::data_information::DataType;
1527
1528 let data = [0x03, 0x13, 0x15, 0x31, 0x00];
1529 let mut records = parse_data_records(&data);
1530 let record = records.next().unwrap().unwrap();
1531
1532 assert_eq!(record.value(), Some(&DataType::Number(12_565.0)));
1533 assert_eq!(record.raw_bytes(), &data);
1534 assert!(record.data_information().is_some());
1535 assert!(record.value_information().is_some());
1536 assert!(records.next().is_none());
1537 }
1538
1539 #[test]
1540 fn parse_application_layer_api_exposes_records() {
1541 let data = [0x78, 0x03, 0x13, 0x15, 0x31, 0x00];
1542 let application_layer = parse_application_layer(&data).unwrap();
1543 let records: Result<Vec<_>, _> = application_layer.data_records().unwrap().collect();
1544
1545 assert_eq!(records.unwrap().len(), 1);
1546 }
1547
1548 #[test]
1549 fn data_record_iterator_reports_parse_errors() {
1550 let mut records = parse_data_records(&[0x04]);
1551
1552 assert!(records.next().unwrap().is_err());
1553 assert!(records.next().is_none());
1554 }
1555}