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
508impl<'a> UserDataBlock<'a> {
509 #[must_use]
510 pub fn is_encrypted(&self) -> Option<bool> {
511 match self {
512 Self::VariableDataStructureWithLongTplHeader {
513 long_tpl_header, ..
514 } => Some(long_tpl_header.is_encrypted()),
515 _ => None,
516 }
517 }
518
519 #[must_use]
521 pub fn variable_data_len(&self) -> usize {
522 match self {
523 Self::VariableDataStructureWithLongTplHeader {
524 variable_data_block,
525 ..
526 } => variable_data_block.len(),
527 Self::VariableDataStructureWithShortTplHeader {
528 variable_data_block,
529 ..
530 } => variable_data_block.len(),
531 _ => 0,
532 }
533 }
534
535 #[cfg(feature = "decryption")]
536 pub fn decrypt_variable_data<K: crate::decryption::KeyProvider>(
537 &self,
538 provider: &K,
539 output: &mut [u8],
540 ) -> Result<usize, crate::decryption::DecryptionError> {
541 use crate::decryption::{DecryptionError, EncryptedPayload, KeyContext};
542
543 match self {
544 Self::VariableDataStructureWithLongTplHeader {
545 long_tpl_header,
546 variable_data_block,
547 ..
548 } => {
549 if !long_tpl_header.is_encrypted() {
550 return Err(NotEncrypted);
551 }
552
553 let security_mode = long_tpl_header
554 .short_tpl_header
555 .configuration_field
556 .security_mode();
557
558 let manufacturer = long_tpl_header
559 .manufacturer
560 .map_err(|_| DecryptionError::DecryptionFailed)?;
561
562 let context = KeyContext {
563 manufacturer,
564 identification_number: long_tpl_header.identification_number.number,
565 version: long_tpl_header.version,
566 device_type: long_tpl_header.device_type,
567 security_mode,
568 access_number: long_tpl_header.short_tpl_header.access_number,
569 };
570
571 let payload = EncryptedPayload::new(variable_data_block, context);
572 payload.decrypt_into(provider, output)
573 }
574 Self::VariableDataStructureWithShortTplHeader {
575 short_tpl_header, ..
576 } => {
577 if !short_tpl_header.is_encrypted() {
578 Err(NotEncrypted)
579 } else {
580 Err(UnknownEncryptionState)
583 }
584 }
585 _ => Err(DecryptionError::UnknownEncryptionState),
586 }
587 }
588
589 #[cfg(feature = "decryption")]
592 pub fn decrypt_variable_data_with_context<K: crate::decryption::KeyProvider>(
593 &self,
594 provider: &K,
595 manufacturer: ManufacturerCode,
596 identification_number: u32,
597 version: u8,
598 device_type: DeviceType,
599 output: &mut [u8],
600 ) -> Result<usize, crate::decryption::DecryptionError> {
601 use crate::decryption::{DecryptionError, EncryptedPayload, KeyContext};
602
603 match self {
604 Self::VariableDataStructureWithShortTplHeader {
605 short_tpl_header,
606 variable_data_block,
607 ..
608 } => {
609 if !short_tpl_header.is_encrypted() {
610 return Err(NotEncrypted);
611 }
612
613 let security_mode = short_tpl_header.configuration_field.security_mode();
614
615 let context = KeyContext {
616 manufacturer,
617 identification_number,
618 version,
619 device_type,
620 security_mode,
621 access_number: short_tpl_header.access_number,
622 };
623
624 let payload = EncryptedPayload::new(variable_data_block, context);
625 payload.decrypt_into(provider, output)
626 }
627 Self::VariableDataStructureWithLongTplHeader { .. } => {
628 self.decrypt_variable_data(provider, output)
630 }
631 _ => Err(DecryptionError::UnknownEncryptionState),
632 }
633 }
634}
635
636#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
637#[derive(Debug, PartialEq)]
638#[cfg_attr(feature = "defmt", derive(defmt::Format))]
639pub struct LongTplHeader {
640 pub identification_number: IdentificationNumber,
641 #[cfg_attr(
642 feature = "serde",
643 serde(skip_deserializing, default = "default_manufacturer_result")
644 )]
645 pub manufacturer: Result<ManufacturerCode, ApplicationLayerError>,
646 pub version: u8,
647 pub device_type: DeviceType,
648 pub short_tpl_header: ShortTplHeader,
649 pub lsb_order: bool,
650}
651
652#[cfg(feature = "serde")]
653fn default_manufacturer_result() -> Result<ManufacturerCode, ApplicationLayerError> {
654 Err(ApplicationLayerError::InsufficientData)
655}
656
657#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
658#[derive(Debug, PartialEq)]
659#[cfg_attr(feature = "defmt", derive(defmt::Format))]
660pub struct ShortTplHeader {
661 pub access_number: u8,
662 pub status: StatusField,
663 pub configuration_field: ConfigurationField,
664}
665
666impl LongTplHeader {
667 #[must_use]
668 pub fn is_encrypted(&self) -> bool {
669 use m_bus_core::SecurityMode;
670 !matches!(
671 self.short_tpl_header.configuration_field.security_mode(),
672 SecurityMode::NoEncryption
673 )
674 }
675}
676
677impl ShortTplHeader {
678 #[must_use]
679 pub fn is_encrypted(&self) -> bool {
680 use m_bus_core::SecurityMode;
681 !matches!(
682 self.configuration_field.security_mode(),
683 SecurityMode::NoEncryption
684 )
685 }
686}
687
688impl<'a> TryFrom<&'a [u8]> for UserDataBlock<'a> {
689 type Error = ApplicationLayerError;
690
691 fn try_from(data: &'a [u8]) -> Result<Self, ApplicationLayerError> {
692 if data.is_empty() {
693 return Err(ApplicationLayerError::MissingControlInformation);
694 }
695 let control_information = ControlInformation::from(
696 *data
697 .first()
698 .ok_or(ApplicationLayerError::InsufficientData)?,
699 )?;
700
701 match control_information {
702 ControlInformation::ResetAtApplicationLevel => {
703 let subcode = ApplicationResetSubcode::from(
704 *data.get(1).ok_or(ApplicationLayerError::InsufficientData)?,
705 );
706 Ok(UserDataBlock::ResetAtApplicationLevel { subcode })
707 }
708 ControlInformation::SendData => Err(ApplicationLayerError::Unimplemented {
709 feature: "SendData control information",
710 }),
711 ControlInformation::SelectSlave => Err(ApplicationLayerError::Unimplemented {
712 feature: "SelectSlave control information",
713 }),
714 ControlInformation::SynchronizeSlave => Err(ApplicationLayerError::Unimplemented {
715 feature: "SynchronizeSlave control information",
716 }),
717 ControlInformation::SetBaudRate300 => Err(ApplicationLayerError::Unimplemented {
718 feature: "SetBaudRate300 control information",
719 }),
720 ControlInformation::SetBaudRate600 => Err(ApplicationLayerError::Unimplemented {
721 feature: "SetBaudRate600 control information",
722 }),
723 ControlInformation::SetBaudRate1200 => Err(ApplicationLayerError::Unimplemented {
724 feature: "SetBaudRate1200 control information",
725 }),
726 ControlInformation::SetBaudRate2400 => Err(ApplicationLayerError::Unimplemented {
727 feature: "SetBaudRate2400 control information",
728 }),
729 ControlInformation::SetBaudRate4800 => Err(ApplicationLayerError::Unimplemented {
730 feature: "SetBaudRate4800 control information",
731 }),
732 ControlInformation::SetBaudRate9600 => Err(ApplicationLayerError::Unimplemented {
733 feature: "SetBaudRate9600 control information",
734 }),
735 ControlInformation::SetBaudRate19200 => Err(ApplicationLayerError::Unimplemented {
736 feature: "SetBaudRate19200 control information",
737 }),
738 ControlInformation::SetBaudRate38400 => Err(ApplicationLayerError::Unimplemented {
739 feature: "SetBaudRate38400 control information",
740 }),
741 ControlInformation::OutputRAMContent => Err(ApplicationLayerError::Unimplemented {
742 feature: "OutputRAMContent control information",
743 }),
744 ControlInformation::WriteRAMContent => Err(ApplicationLayerError::Unimplemented {
745 feature: "WriteRAMContent control information",
746 }),
747 ControlInformation::StartCalibrationTestMode => {
748 Err(ApplicationLayerError::Unimplemented {
749 feature: "StartCalibrationTestMode control information",
750 })
751 }
752 ControlInformation::ReadEEPROM => Err(ApplicationLayerError::Unimplemented {
753 feature: "ReadEEPROM control information",
754 }),
755 ControlInformation::StartSoftwareTest => Err(ApplicationLayerError::Unimplemented {
756 feature: "StartSoftwareTest control information",
757 }),
758 ControlInformation::HashProcedure(_) => Err(ApplicationLayerError::Unimplemented {
759 feature: "HashProcedure control information",
760 }),
761 ControlInformation::SendErrorStatus => Err(ApplicationLayerError::Unimplemented {
762 feature: "SendErrorStatus control information",
763 }),
764 ControlInformation::SendAlarmStatus => Err(ApplicationLayerError::Unimplemented {
765 feature: "SendAlarmStatus control information",
766 }),
767 ControlInformation::ResponseWithVariableDataStructure { lsb_order } => {
768 let mut iter = data.iter().skip(1);
769 let mut identification_number_bytes = [
770 *iter.next().ok_or(ApplicationLayerError::InsufficientData)?,
771 *iter.next().ok_or(ApplicationLayerError::InsufficientData)?,
772 *iter.next().ok_or(ApplicationLayerError::InsufficientData)?,
773 *iter.next().ok_or(ApplicationLayerError::InsufficientData)?,
774 ];
775 if lsb_order {
776 identification_number_bytes.reverse();
777 }
778
779 Ok(UserDataBlock::VariableDataStructureWithLongTplHeader {
780 long_tpl_header: LongTplHeader {
781 identification_number: IdentificationNumber::from_bcd_hex_digits(
782 identification_number_bytes,
783 )?,
784 manufacturer: ManufacturerCode::from_id(u16::from_le_bytes([
785 *iter.next().ok_or(ApplicationLayerError::InsufficientData)?,
786 *iter.next().ok_or(ApplicationLayerError::InsufficientData)?,
787 ])),
788 version: *iter.next().ok_or(ApplicationLayerError::InsufficientData)?,
789 device_type: DeviceType::from(
790 *iter.next().ok_or(ApplicationLayerError::InsufficientData)?,
791 ),
792 short_tpl_header: ShortTplHeader {
793 access_number: *iter
794 .next()
795 .ok_or(ApplicationLayerError::InsufficientData)?,
796 status: {
797 StatusField::from_bits_truncate(
798 *iter.next().ok_or(ApplicationLayerError::InsufficientData)?,
799 )
800 },
801 configuration_field: {
802 ConfigurationField::from_bytes(
803 *iter.next().ok_or(ApplicationLayerError::InsufficientData)?,
804 *iter.next().ok_or(ApplicationLayerError::InsufficientData)?,
805 )
806 },
807 },
808 lsb_order,
809 },
810 variable_data_block: data
811 .get(13..data.len())
812 .ok_or(ApplicationLayerError::InsufficientData)?,
813 extended_link_layer: None,
814 })
815 }
816 ControlInformation::ResponseWithFixedDataStructure => {
817 let mut iter = data.iter().skip(1);
818 let identification_number = IdentificationNumber::from_bcd_hex_digits([
819 *iter.next().ok_or(ApplicationLayerError::InsufficientData)?,
820 *iter.next().ok_or(ApplicationLayerError::InsufficientData)?,
821 *iter.next().ok_or(ApplicationLayerError::InsufficientData)?,
822 *iter.next().ok_or(ApplicationLayerError::InsufficientData)?,
823 ])?;
824
825 let access_number = *iter.next().ok_or(ApplicationLayerError::InsufficientData)?;
826
827 let status = StatusField::from_bits_truncate(
828 *iter.next().ok_or(ApplicationLayerError::InsufficientData)?,
829 );
830 let device_type_and_unit = u16::from_be_bytes([
831 *iter.next().ok_or(ApplicationLayerError::InsufficientData)?,
832 *iter.next().ok_or(ApplicationLayerError::InsufficientData)?,
833 ]);
834 let counter1 = Counter::from_bcd_hex_digits([
835 *iter.next().ok_or(ApplicationLayerError::InsufficientData)?,
836 *iter.next().ok_or(ApplicationLayerError::InsufficientData)?,
837 *iter.next().ok_or(ApplicationLayerError::InsufficientData)?,
838 *iter.next().ok_or(ApplicationLayerError::InsufficientData)?,
839 ])?;
840 let counter2 = Counter::from_bcd_hex_digits([
841 *iter.next().ok_or(ApplicationLayerError::InsufficientData)?,
842 *iter.next().ok_or(ApplicationLayerError::InsufficientData)?,
843 *iter.next().ok_or(ApplicationLayerError::InsufficientData)?,
844 *iter.next().ok_or(ApplicationLayerError::InsufficientData)?,
845 ])?;
846 Ok(UserDataBlock::FixedDataStructure {
847 identification_number,
848 access_number,
849 status,
850 device_type_and_unit,
851 counter1,
852 counter2,
853 })
854 }
855 ControlInformation::DataSentWithShortTransportLayer => {
856 Err(ApplicationLayerError::Unimplemented {
857 feature: "DataSentWithShortTransportLayer control information",
858 })
859 }
860 ControlInformation::DataSentWithLongTransportLayer => {
861 Err(ApplicationLayerError::Unimplemented {
862 feature: "DataSentWithLongTransportLayer control information",
863 })
864 }
865 ControlInformation::CosemDataWithLongTransportLayer => {
866 Err(ApplicationLayerError::Unimplemented {
867 feature: "CosemDataWithLongTransportLayer control information",
868 })
869 }
870 ControlInformation::CosemDataWithShortTransportLayer => {
871 Err(ApplicationLayerError::Unimplemented {
872 feature: "CosemDataWithShortTransportLayer control information",
873 })
874 }
875 ControlInformation::ObisDataReservedLongTransportLayer => {
876 Err(ApplicationLayerError::Unimplemented {
877 feature: "ObisDataReservedLongTransportLayer control information",
878 })
879 }
880 ControlInformation::ObisDataReservedShortTransportLayer => {
881 Err(ApplicationLayerError::Unimplemented {
882 feature: "ObisDataReservedShortTransportLayer control information",
883 })
884 }
885 ControlInformation::ApplicationLayerFormatFrameNoTransport => {
886 Err(ApplicationLayerError::Unimplemented {
887 feature: "ApplicationLayerFormatFrameNoTransport control information",
888 })
889 }
890 ControlInformation::ApplicationLayerFormatFrameShortTransport => {
891 Err(ApplicationLayerError::Unimplemented {
892 feature: "ApplicationLayerFormatFrameShortTransport control information",
893 })
894 }
895 ControlInformation::ApplicationLayerFormatFrameLongTransport => {
896 Err(ApplicationLayerError::Unimplemented {
897 feature: "ApplicationLayerFormatFrameLongTransport control information",
898 })
899 }
900 ControlInformation::ClockSyncAbsolute => Err(ApplicationLayerError::Unimplemented {
901 feature: "ClockSyncAbsolute control information",
902 }),
903 ControlInformation::ClockSyncRelative => Err(ApplicationLayerError::Unimplemented {
904 feature: "ClockSyncRelative control information",
905 }),
906 ControlInformation::ApplicationErrorShortTransport => {
907 Err(ApplicationLayerError::Unimplemented {
908 feature: "ApplicationErrorShortTransport control information",
909 })
910 }
911 ControlInformation::ApplicationErrorLongTransport => {
912 Err(ApplicationLayerError::Unimplemented {
913 feature: "ApplicationErrorLongTransport control information",
914 })
915 }
916 ControlInformation::AlarmShortTransport => Err(ApplicationLayerError::Unimplemented {
917 feature: "AlarmShortTransport control information",
918 }),
919 ControlInformation::AlarmLongTransport => Err(ApplicationLayerError::Unimplemented {
920 feature: "AlarmLongTransport control information",
921 }),
922 ControlInformation::ApplicationLayerNoTransport => {
923 Err(ApplicationLayerError::Unimplemented {
924 feature: "ApplicationLayerNoTransport control information",
925 })
926 }
927 ControlInformation::ApplicationLayerCompactFrameNoTransport => {
928 Err(ApplicationLayerError::Unimplemented {
929 feature: "ApplicationLayerCompactFrameNoTransport control information",
930 })
931 }
932 ControlInformation::ApplicationLayerShortTransport => {
933 let has_encryption_config_byte = data[0] == 0xA0;
936 let skip_count = if has_encryption_config_byte { 2 } else { 1 };
937 let data_block_offset = if has_encryption_config_byte { 6 } else { 5 };
938
939 let mut iter = data.iter().skip(skip_count);
940
941 Ok(UserDataBlock::VariableDataStructureWithShortTplHeader {
942 short_tpl_header: ShortTplHeader {
943 access_number: *iter
944 .next()
945 .ok_or(ApplicationLayerError::InsufficientData)?,
946 status: {
947 StatusField::from_bits_truncate(
948 *iter.next().ok_or(ApplicationLayerError::InsufficientData)?,
949 )
950 },
951 configuration_field: {
952 ConfigurationField::from_bytes(
953 *iter.next().ok_or(ApplicationLayerError::InsufficientData)?,
954 *iter.next().ok_or(ApplicationLayerError::InsufficientData)?,
955 )
956 },
957 },
958 variable_data_block: data
959 .get(data_block_offset..data.len())
960 .ok_or(ApplicationLayerError::InsufficientData)?,
961 extended_link_layer: None,
962 })
963 }
964 ControlInformation::ApplicationLayerCompactFrameShortTransport => {
965 Err(ApplicationLayerError::Unimplemented {
966 feature: "ApplicationLayerCompactFrameShortTransport control information",
967 })
968 }
969 ControlInformation::CosemApplicationLayerLongTransport => {
970 Err(ApplicationLayerError::Unimplemented {
971 feature: "CosemApplicationLayerLongTransport control information",
972 })
973 }
974 ControlInformation::CosemApplicationLayerShortTransport => {
975 Err(ApplicationLayerError::Unimplemented {
976 feature: "CosemApplicationLayerShortTransport control information",
977 })
978 }
979 ControlInformation::ObisApplicationLayerReservedLongTransport => {
980 Err(ApplicationLayerError::Unimplemented {
981 feature: "ObisApplicationLayerReservedLongTransport control information",
982 })
983 }
984 ControlInformation::ObisApplicationLayerReservedShortTransport => {
985 Err(ApplicationLayerError::Unimplemented {
986 feature: "ObisApplicationLayerReservedShortTransport control information",
987 })
988 }
989 ControlInformation::TransportLayerLongReadoutToMeter => {
990 Err(ApplicationLayerError::Unimplemented {
991 feature: "TransportLayerLongReadoutToMeter control information",
992 })
993 }
994 ControlInformation::NetworkLayerData => Err(ApplicationLayerError::Unimplemented {
995 feature: "NetworkLayerData control information",
996 }),
997 ControlInformation::FutureUse => Err(ApplicationLayerError::Unimplemented {
998 feature: "FutureUse control information",
999 }),
1000 ControlInformation::NetworkManagementApplication => {
1001 Err(ApplicationLayerError::Unimplemented {
1002 feature: "NetworkManagementApplication control information",
1003 })
1004 }
1005 ControlInformation::TransportLayerCompactFrame => {
1006 Err(ApplicationLayerError::Unimplemented {
1007 feature: "TransportLayerCompactFrame control information",
1008 })
1009 }
1010 ControlInformation::TransportLayerFormatFrame => {
1011 Err(ApplicationLayerError::Unimplemented {
1012 feature: "TransportLayerFormatFrame control information",
1013 })
1014 }
1015 ControlInformation::NetworkManagementDataReserved => {
1016 Err(ApplicationLayerError::Unimplemented {
1017 feature: "NetworkManagementDataReserved control information",
1018 })
1019 }
1020 ControlInformation::TransportLayerShortMeterToReadout => {
1021 Err(ApplicationLayerError::Unimplemented {
1022 feature: "TransportLayerShortMeterToReadout control information",
1023 })
1024 }
1025 ControlInformation::TransportLayerLongMeterToReadout => {
1026 Err(ApplicationLayerError::Unimplemented {
1027 feature: "TransportLayerLongMeterToReadout control information",
1028 })
1029 }
1030 ControlInformation::ExtendedLinkLayerI => {
1031 let mut iter = data.iter();
1032 iter.next();
1033 let extended_link_layer = Some(ExtendedLinkLayer {
1034 communication_control: *iter
1035 .next()
1036 .ok_or(ApplicationLayerError::InsufficientData)?,
1037 access_number: *iter.next().ok_or(ApplicationLayerError::InsufficientData)?,
1038 receiver_address: None,
1039 encryption: None,
1040 });
1041 let user_block = UserDataBlock::try_from(iter.as_slice());
1042 if let Ok(UserDataBlock::VariableDataStructureWithShortTplHeader {
1043 extended_link_layer: _,
1044 short_tpl_header,
1045 variable_data_block: _,
1046 }) = user_block
1047 {
1048 Ok(UserDataBlock::VariableDataStructureWithShortTplHeader {
1049 extended_link_layer,
1050 short_tpl_header,
1051 variable_data_block: {
1052 data.get(8..data.len())
1053 .ok_or(ApplicationLayerError::InsufficientData)?
1054 },
1055 })
1056 } else {
1057 Err(ApplicationLayerError::MissingControlInformation)
1058 }
1059 }
1060 ControlInformation::ExtendedLinkLayerII => {
1061 let (ell, ell_size) = ExtendedLinkLayer::parse(
1063 data.get(1..)
1064 .ok_or(ApplicationLayerError::InsufficientData)?,
1065 extended_link_layer::EllFormat::FormatII,
1066 )?;
1067 let app_data_offset = 1 + ell_size;
1068
1069 let short_tpl_header = ShortTplHeader {
1072 access_number: ell.access_number,
1073 status: StatusField::from_bits_truncate(ell.communication_control),
1074 configuration_field: ConfigurationField::from_bytes(0x00, 0x00),
1075 };
1076
1077 Ok(UserDataBlock::VariableDataStructureWithShortTplHeader {
1078 extended_link_layer: Some(ell),
1079 short_tpl_header,
1080 variable_data_block: data
1081 .get(app_data_offset..)
1082 .ok_or(ApplicationLayerError::InsufficientData)?,
1083 })
1084 }
1085 ControlInformation::ExtendedLinkLayerIII => {
1086 let (ell, ell_size) = ExtendedLinkLayer::parse(
1088 data.get(1..)
1089 .ok_or(ApplicationLayerError::InsufficientData)?,
1090 extended_link_layer::EllFormat::FormatIII,
1091 )?;
1092 let app_data_offset = 1 + ell_size;
1093
1094 let short_tpl_header = ShortTplHeader {
1097 access_number: ell.access_number,
1098 status: StatusField::from_bits_truncate(ell.communication_control),
1099 configuration_field: ConfigurationField::from_bytes(0x00, 0x00),
1100 };
1101
1102 Ok(UserDataBlock::VariableDataStructureWithShortTplHeader {
1103 extended_link_layer: Some(ell),
1104 short_tpl_header,
1105 variable_data_block: data
1106 .get(app_data_offset..)
1107 .ok_or(ApplicationLayerError::InsufficientData)?,
1108 })
1109 }
1110 }
1111 }
1112}
1113
1114#[allow(clippy::unwrap_used, clippy::panic)]
1115#[cfg(all(test, feature = "std"))]
1116mod tests {
1117
1118 use super::*;
1119
1120 #[test]
1121 fn test_control_information() {
1122 assert_eq!(
1123 ControlInformation::from(0x50),
1124 Ok(ControlInformation::ResetAtApplicationLevel)
1125 );
1126 assert_eq!(
1127 ControlInformation::from(0x51),
1128 Ok(ControlInformation::SendData)
1129 );
1130 assert_eq!(
1131 ControlInformation::from(0x52),
1132 Ok(ControlInformation::SelectSlave)
1133 );
1134 assert_eq!(
1135 ControlInformation::from(0x54),
1136 Ok(ControlInformation::SynchronizeSlave)
1137 );
1138 assert_eq!(
1139 ControlInformation::from(0xB8),
1140 Ok(ControlInformation::SetBaudRate300)
1141 );
1142 assert_eq!(
1143 ControlInformation::from(0xB9),
1144 Ok(ControlInformation::SetBaudRate600)
1145 );
1146 assert_eq!(
1147 ControlInformation::from(0xBA),
1148 Ok(ControlInformation::SetBaudRate1200)
1149 );
1150 assert_eq!(
1151 ControlInformation::from(0xBB),
1152 Ok(ControlInformation::SetBaudRate2400)
1153 );
1154 assert_eq!(
1155 ControlInformation::from(0xBC),
1156 Ok(ControlInformation::SetBaudRate4800)
1157 );
1158 assert_eq!(
1159 ControlInformation::from(0xBD),
1160 Ok(ControlInformation::SetBaudRate9600)
1161 );
1162 assert_eq!(
1163 ControlInformation::from(0xBE),
1164 Ok(ControlInformation::SetBaudRate19200)
1165 );
1166 assert_eq!(
1167 ControlInformation::from(0xBF),
1168 Ok(ControlInformation::SetBaudRate38400)
1169 );
1170 assert_eq!(
1171 ControlInformation::from(0xB1),
1172 Ok(ControlInformation::OutputRAMContent)
1173 );
1174 assert_eq!(
1175 ControlInformation::from(0xB2),
1176 Ok(ControlInformation::WriteRAMContent)
1177 );
1178 assert_eq!(
1179 ControlInformation::from(0xB3),
1180 Ok(ControlInformation::StartCalibrationTestMode)
1181 );
1182 assert_eq!(
1183 ControlInformation::from(0xB4),
1184 Ok(ControlInformation::ReadEEPROM)
1185 );
1186 assert_eq!(
1187 ControlInformation::from(0xB6),
1188 Ok(ControlInformation::StartSoftwareTest)
1189 );
1190 assert_eq!(
1191 ControlInformation::from(0x90),
1192 Ok(ControlInformation::HashProcedure(0,))
1193 );
1194 assert_eq!(
1195 ControlInformation::from(0x91),
1196 Ok(ControlInformation::HashProcedure(1,))
1197 );
1198 }
1199
1200 #[test]
1201 fn test_reset_subcode() {
1202 let data = [0x50, 0x10];
1204 let result = UserDataBlock::try_from(data.as_slice());
1205 assert_eq!(
1206 result,
1207 Ok(UserDataBlock::ResetAtApplicationLevel {
1208 subcode: ApplicationResetSubcode::All(0x10)
1209 })
1210 );
1211 }
1212
1213 #[test]
1214 fn test_device_type_roundtrip() {
1215 let test_cases = [
1217 (0x00, DeviceType::Other),
1218 (0x01, DeviceType::OilMeter),
1219 (0x02, DeviceType::ElectricityMeter),
1220 (0x03, DeviceType::GasMeter),
1221 (0x04, DeviceType::HeatMeterReturn),
1222 (0x05, DeviceType::SteamMeter),
1223 (0x06, DeviceType::WarmWaterMeter),
1224 (0x07, DeviceType::WaterMeter),
1225 (0x08, DeviceType::HeatCostAllocator),
1226 (0x09, DeviceType::CompressedAir),
1227 (0x0A, DeviceType::CoolingMeterReturn),
1228 (0x0B, DeviceType::CoolingMeterFlow),
1229 (0x0C, DeviceType::HeatMeterFlow),
1230 (0x0D, DeviceType::CombinedHeatCoolingMeter),
1231 (0x0E, DeviceType::BusSystemComponent),
1232 (0x0F, DeviceType::UnknownDevice),
1233 (0x10, DeviceType::IrrigationWaterMeter),
1234 (0x11, DeviceType::WaterDataLogger),
1235 (0x12, DeviceType::GasDataLogger),
1236 (0x13, DeviceType::GasConverter),
1237 (0x14, DeviceType::CalorificValue),
1238 (0x15, DeviceType::HotWaterMeter),
1239 (0x16, DeviceType::ColdWaterMeter),
1240 (0x17, DeviceType::DualRegisterWaterMeter),
1241 (0x18, DeviceType::PressureMeter),
1242 (0x19, DeviceType::AdConverter),
1243 (0x1A, DeviceType::SmokeDetector),
1244 (0x1B, DeviceType::RoomSensor),
1245 (0x1C, DeviceType::GasDetector),
1246 (0x20, DeviceType::ElectricityBreaker),
1247 (0x21, DeviceType::Valve),
1248 (0x25, DeviceType::CustomerUnit),
1249 (0x28, DeviceType::WasteWaterMeter),
1250 (0x29, DeviceType::Garbage),
1251 (0x30, DeviceType::ServiceTool),
1252 (0x31, DeviceType::CommunicationController),
1253 (0x32, DeviceType::UnidirectionalRepeater),
1254 (0x33, DeviceType::BidirectionalRepeater),
1255 (0x36, DeviceType::RadioConverterSystemSide),
1256 (0x37, DeviceType::RadioConverterMeterSide),
1257 (0x38, DeviceType::BusConverterMeterSide),
1258 (0xFF, DeviceType::Wildcard),
1259 (0x1D, DeviceType::ReservedSensor(0x1D)), (0x22, DeviceType::ReservedSwitch(0x22)), (0x40, DeviceType::Reserved(0x40)), ];
1264
1265 for (byte, expected_device_type) in test_cases {
1266 let device_type = DeviceType::from(byte);
1267 assert_eq!(device_type, expected_device_type);
1268 assert_eq!(u8::from(device_type), byte);
1269 }
1270
1271 assert_eq!(u8::from(DeviceType::Reserved(0x40)), 0x40);
1273 assert_eq!(u8::from(DeviceType::ReservedSensor(0x1D)), 0x1D);
1274 assert_eq!(u8::from(DeviceType::ReservedSwitch(0x22)), 0x22);
1275
1276 assert_eq!(u8::from(DeviceType::UnknownDevice), 0x0F);
1278 }
1279
1280 #[test]
1281 fn test_identification_number() -> Result<(), ApplicationLayerError> {
1282 let data = [0x78, 0x56, 0x34, 0x12];
1283 let result = IdentificationNumber::from_bcd_hex_digits(data)?;
1284 assert_eq!(result, IdentificationNumber { number: 12345678 });
1285 Ok(())
1286 }
1287
1288 #[test]
1289 fn test_fixed_data_structure() {
1290 let data = [
1291 0x73, 0x78, 0x56, 0x34, 0x12, 0x0A, 0x00, 0xE9, 0x7E, 0x01, 0x00, 0x00, 0x00, 0x35,
1292 0x01, 0x00, 0x00,
1293 ];
1294
1295 let result = UserDataBlock::try_from(data.as_slice());
1296
1297 assert_eq!(
1298 result,
1299 Ok(UserDataBlock::FixedDataStructure {
1300 identification_number: IdentificationNumber { number: 12345678 },
1301 access_number: 0x0A,
1302 status: StatusField::from_bits_truncate(0x00),
1303 device_type_and_unit: 0xE97E,
1304 counter1: Counter { count: 1 },
1305 counter2: Counter { count: 135 },
1306 })
1307 );
1308 }
1309
1310 #[test]
1311 fn test_manufacturer_code() -> Result<(), ApplicationLayerError> {
1312 let code = ManufacturerCode::from_id(0x1ee6)?;
1313 assert_eq!(
1314 code,
1315 ManufacturerCode {
1316 code: ['G', 'W', 'F']
1317 }
1318 );
1319 Ok(())
1320 }
1321
1322 #[test]
1323 fn test_lsb_frame() {
1324 use crate::data_information::DataType;
1325 use wired_mbus_link_layer::WiredFrame;
1326
1327 let lsb_frame: &[u8] = &[
1328 0x68, 0x64, 0x64, 0x68, 0x8, 0x7f, 0x76, 0x9, 0x67, 0x1, 0x6, 0x0, 0x0, 0x51, 0x4,
1329 0x50, 0x0, 0x0, 0x0, 0x2, 0x6c, 0x38, 0x1c, 0xc, 0xf, 0x0, 0x80, 0x87, 0x32, 0x8c,
1330 0x20, 0xf, 0x0, 0x0, 0x0, 0x0, 0xc, 0x14, 0x13, 0x32, 0x82, 0x58, 0xbc, 0x10, 0x15,
1331 0x0, 0x25, 0x81, 0x25, 0x8c, 0x20, 0x13, 0x0, 0x0, 0x0, 0x0, 0x8c, 0x30, 0x13, 0x0,
1332 0x0, 0x1, 0x61, 0x8c, 0x40, 0x13, 0x0, 0x0, 0x16, 0x88, 0xa, 0x3c, 0x1, 0x10, 0xa,
1333 0x2d, 0x0, 0x80, 0xa, 0x5a, 0x7, 0x18, 0xa, 0x5e, 0x6, 0x53, 0xc, 0x22, 0x0, 0x16, 0x7,
1334 0x26, 0x3c, 0x22, 0x0, 0x0, 0x33, 0x81, 0x4, 0x7e, 0x0, 0x0, 0x67, 0xc, 0xc, 0x16,
1335 ];
1336 let non_lsb_frame: &[u8] = &[
1337 0x68, 0xc7, 0xc7, 0x68, 0x8, 0x38, 0x72, 0x56, 0x73, 0x23, 0x72, 0x2d, 0x2c, 0x34, 0x4,
1338 0x87, 0x0, 0x0, 0x0, 0x4, 0xf, 0x7f, 0x1c, 0x1, 0x0, 0x4, 0xff, 0x7, 0x8a, 0xad, 0x8,
1339 0x0, 0x4, 0xff, 0x8, 0x6, 0xfe, 0x5, 0x0, 0x4, 0x14, 0x4e, 0x55, 0xb, 0x0, 0x84, 0x40,
1340 0x14, 0x0, 0x0, 0x0, 0x0, 0x84, 0x80, 0x40, 0x14, 0x0, 0x0, 0x0, 0x0, 0x4, 0x22, 0x76,
1341 0x7f, 0x0, 0x0, 0x34, 0x22, 0x8b, 0x2c, 0x0, 0x0, 0x2, 0x59, 0x61, 0x1b, 0x2, 0x5d,
1342 0x5f, 0x10, 0x2, 0x61, 0x2, 0xb, 0x4, 0x2d, 0x55, 0x0, 0x0, 0x0, 0x14, 0x2d, 0x83, 0x0,
1343 0x0, 0x0, 0x4, 0x3b, 0x6, 0x1, 0x0, 0x0, 0x14, 0x3b, 0xaa, 0x1, 0x0, 0x0, 0x4, 0xff,
1344 0x22, 0x0, 0x0, 0x0, 0x0, 0x4, 0x6d, 0x6, 0x2c, 0x1f, 0x3a, 0x44, 0xf, 0xcf, 0x11, 0x1,
1345 0x0, 0x44, 0xff, 0x7, 0xb, 0x69, 0x8, 0x0, 0x44, 0xff, 0x8, 0x54, 0xd3, 0x5, 0x0, 0x44,
1346 0x14, 0x11, 0xf3, 0xa, 0x0, 0xc4, 0x40, 0x14, 0x0, 0x0, 0x0, 0x0, 0xc4, 0x80, 0x40,
1347 0x14, 0x0, 0x0, 0x0, 0x0, 0x54, 0x2d, 0x3a, 0x0, 0x0, 0x0, 0x54, 0x3b, 0x28, 0x1, 0x0,
1348 0x0, 0x42, 0x6c, 0x1, 0x3a, 0x2, 0xff, 0x1a, 0x1, 0x1a, 0xc, 0x78, 0x56, 0x73, 0x23,
1349 0x72, 0x4, 0xff, 0x16, 0xe6, 0x84, 0x1e, 0x0, 0x4, 0xff, 0x17, 0xc1, 0xd5, 0xb4, 0x0,
1350 0x12, 0x16,
1351 ];
1352
1353 let frames = [
1354 (lsb_frame, 9670106, Some(DataType::Number(808732.0))),
1355 (non_lsb_frame, 72237356, Some(DataType::Number(568714.0))),
1356 ];
1357
1358 for (frame, expected_iden_nr, data_record_value) in frames {
1359 let frame = WiredFrame::try_from(frame).unwrap();
1360
1361 if let WiredFrame::LongFrame {
1362 function: _,
1363 address: _,
1364 data,
1365 } = frame
1366 {
1367 let user_data_block = UserDataBlock::try_from(data).unwrap();
1368 if let UserDataBlock::VariableDataStructureWithLongTplHeader {
1369 long_tpl_header: fixed_data_header,
1370 variable_data_block,
1371 ..
1372 } = user_data_block
1373 {
1374 assert_eq!(
1375 fixed_data_header.identification_number.number,
1376 expected_iden_nr
1377 );
1378
1379 let mut data_records =
1380 DataRecords::from((variable_data_block, &fixed_data_header)).flatten();
1381 data_records.next().unwrap();
1382 assert_eq!(data_records.next().unwrap().data.value, data_record_value);
1383 } else {
1384 panic!("UserDataBlock is not a variable data structure");
1385 }
1386 } else {
1387 panic!("Frame is not a long frame");
1388 }
1389 }
1390 }
1391
1392 #[test]
1393 fn test_manufacturer_specific_data() {
1394 use crate::data_information::DataType;
1395 use wired_mbus_link_layer::WiredFrame;
1396
1397 let manufacturer_specific_data_frame: &[u8] = &[
1398 0x68, 0x55, 0x55, 0x68, 0x8, 0x1e, 0x72, 0x34, 0x35, 0x58, 0x12, 0x92, 0x26, 0x18, 0x4,
1399 0x14, 0x0, 0x0, 0x0, 0xc, 0x78, 0x34, 0x35, 0x58, 0x12, 0x4, 0xe, 0x57, 0x64, 0x3, 0x0,
1400 0xc, 0x14, 0x73, 0x58, 0x44, 0x0, 0xb, 0x2d, 0x6, 0x0, 0x0, 0xb, 0x3b, 0x55, 0x0, 0x0,
1401 0xa, 0x5a, 0x87, 0x6, 0xa, 0x5e, 0x77, 0x5, 0xb, 0x61, 0x1, 0x11, 0x0, 0x4, 0x6d, 0x10,
1402 0x2, 0x4, 0x3c, 0x2, 0x27, 0x79, 0x11, 0x9, 0xfd, 0xe, 0x6, 0x9, 0xfd, 0xf, 0x6, 0x8c,
1403 0xc0, 0x0, 0x15, 0x71, 0x25, 0x0, 0x0, 0xf, 0x0, 0x0, 0x86, 0x16,
1404 ];
1405
1406 let frame = WiredFrame::try_from(manufacturer_specific_data_frame).unwrap();
1407
1408 if let WiredFrame::LongFrame {
1409 function: _,
1410 address: _,
1411 data,
1412 } = frame
1413 {
1414 let user_data_block = UserDataBlock::try_from(data).unwrap();
1415 if let UserDataBlock::VariableDataStructureWithLongTplHeader {
1416 long_tpl_header: fixed_data_header,
1417 variable_data_block,
1418 ..
1419 } = user_data_block
1420 {
1421 let mut data_records: Vec<_> =
1422 DataRecords::from((variable_data_block, &fixed_data_header))
1423 .flatten()
1424 .collect();
1425
1426 assert_eq!(data_records.len(), 14);
1427
1428 assert_eq!(
1429 data_records.pop().unwrap().data.value,
1430 Some(DataType::ManufacturerSpecific(&[15, 0, 0]))
1431 );
1432 assert_eq!(
1433 data_records.pop().unwrap().data.value,
1434 Some(DataType::Number(2571.0))
1435 );
1436 }
1437 }
1438 }
1439
1440 #[test]
1441 fn real32bit() {
1442 use crate::data_information::DataType;
1443 use crate::value_information::ValueLabel;
1444 use wired_mbus_link_layer::WiredFrame;
1445
1446 let real32bit: &[u8] = &[
1447 0x68, 0xa7, 0xa7, 0x68, 0x8, 0x4d, 0x72, 0x82, 0x4, 0x75, 0x30, 0xee, 0x4d, 0x19, 0x4,
1448 0xc2, 0x0, 0x0, 0x0, 0x4, 0xe, 0x1b, 0xe, 0x0, 0x0, 0x84, 0xa, 0xe, 0x4c, 0x6, 0x0,
1449 0x0, 0x4, 0x13, 0x7, 0x81, 0x0, 0x0, 0x84, 0xa, 0x13, 0x9d, 0x37, 0x0, 0x0, 0xb, 0xfd,
1450 0xf, 0x0, 0x7, 0x1, 0xa, 0xfd, 0xd, 0x0, 0x11, 0x8c, 0x40, 0x79, 0x1, 0x0, 0x0, 0x0,
1451 0x84, 0x40, 0x14, 0x31, 0x5, 0x0, 0x0, 0x84, 0x4a, 0x14, 0xfd, 0x4, 0x0, 0x0, 0x8c,
1452 0x80, 0x40, 0x79, 0x2, 0x0, 0x0, 0x0, 0x84, 0x80, 0x40, 0x14, 0x27, 0x50, 0x0, 0x0,
1453 0x84, 0x8a, 0x40, 0x14, 0x8, 0x31, 0x0, 0x0, 0x5, 0xff, 0x1, 0xdf, 0xa3, 0xb1, 0x3e,
1454 0x5, 0xff, 0x2, 0xa8, 0x59, 0x6b, 0x3f, 0xc, 0x78, 0x82, 0x4, 0x75, 0x30, 0x4, 0x6d,
1455 0x5, 0xb, 0x2f, 0x31, 0x82, 0xa, 0x6c, 0xe1, 0xf1, 0x5, 0x5b, 0x40, 0x7a, 0x63, 0x42,
1456 0x5, 0x5f, 0x80, 0xc3, 0x25, 0x42, 0x5, 0x3e, 0x0, 0x0, 0x0, 0x0, 0x5, 0x2b, 0x0, 0x0,
1457 0x0, 0x0, 0x1, 0xff, 0x2b, 0x0, 0x3, 0x22, 0x17, 0x3b, 0x0, 0x2, 0xff, 0x2c, 0x0, 0x0,
1458 0x1f, 0xa4, 0x16,
1459 ];
1460
1461 let frame = WiredFrame::try_from(real32bit).unwrap();
1462
1463 if let WiredFrame::LongFrame {
1464 function: _,
1465 address: _,
1466 data,
1467 } = frame
1468 {
1469 let user_data_block = UserDataBlock::try_from(data).unwrap();
1470 if let UserDataBlock::VariableDataStructureWithLongTplHeader {
1471 long_tpl_header: fixed_data_header,
1472 variable_data_block,
1473 ..
1474 } = user_data_block
1475 {
1476 let data_records: Vec<DataRecord> =
1477 DataRecords::from((variable_data_block, &fixed_data_header))
1478 .flatten()
1479 .collect();
1480
1481 assert_eq!(data_records.len(), 24);
1482
1483 for data_record in data_records {
1484 let labels = data_record
1485 .data_record_header
1486 .processed_data_record_header
1487 .value_information
1488 .as_ref()
1489 .unwrap()
1490 .labels
1491 .clone();
1492 if labels.contains(&ValueLabel::ReturnTemperature) {
1493 assert_eq!(
1494 data_record.data.value,
1495 Some(DataType::Number(41.44091796875))
1496 );
1497 }
1498 if labels.contains(&ValueLabel::FlowTemperature) {
1499 assert_eq!(
1500 data_record.data.value,
1501 Some(DataType::Number(56.869384765625))
1502 );
1503 }
1504 }
1505 }
1506 }
1507 }
1508
1509 #[test]
1510 fn global_readout_request_does_not_consume_following_records() {
1511 let data: &[u8] = &[0x7F, 0x01, 0x13, 0x05];
1513 let records: Vec<_> = DataRecords::new(data, None).flatten().collect();
1514 assert_eq!(records.len(), 2);
1515 }
1516}