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