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