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