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