Skip to content

Commit f6dd808

Browse files
committed
cargo fmt and fixing tests
1 parent 7355850 commit f6dd808

5 files changed

Lines changed: 132 additions & 136 deletions

File tree

crates/m-bus-application-layer/src/lib.rs

Lines changed: 102 additions & 132 deletions
Original file line numberDiff line numberDiff line change
@@ -334,6 +334,13 @@ impl ControlInformation {
334334
0x8D => Ok(Self::ExtendedLinkLayerII),
335335
0x8E => Ok(Self::ExtendedLinkLayerIII),
336336
0x90..=0x97 => Ok(Self::HashProcedure(byte - 0x90)),
337+
// Encrypted CI codes (0xA0-0xAF) - these are encrypted variants of 0x7A (ApplicationLayerShortTransport)
338+
// When used with NOKEY, they should be parsed as unencrypted ApplicationLayerShortTransport
339+
// The lower 4 bits indicate the encryption mode:
340+
// 0xA0 = Mode 5 (AES-CBC-IV zero) or NOKEY
341+
// 0xA2 = Mode 7 (AES-CBC-IV non-zero) or NOKEY
342+
// 0xA4, 0xA6, etc. = other modes
343+
0xA0..=0xAF => Ok(Self::ApplicationLayerShortTransport),
337344
0xB1 => Ok(Self::OutputRAMContent),
338345
0xB2 => Ok(Self::WriteRAMContent),
339346
0xB3 => Ok(Self::StartCalibrationTestMode),
@@ -611,13 +618,22 @@ impl<'a> UserDataBlock<'a> {
611618
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
612619
pub struct LongTplHeader {
613620
pub identification_number: IdentificationNumber,
621+
#[cfg_attr(
622+
feature = "serde",
623+
serde(skip_deserializing, default = "default_manufacturer_result")
624+
)]
614625
pub manufacturer: Result<ManufacturerCode, ApplicationLayerError>,
615626
pub version: u8,
616627
pub device_type: DeviceType,
617628
pub short_tpl_header: ShortTplHeader,
618629
pub lsb_order: bool,
619630
}
620631

632+
#[cfg(feature = "serde")]
633+
fn default_manufacturer_result() -> Result<ManufacturerCode, ApplicationLayerError> {
634+
Err(ApplicationLayerError::InsufficientData)
635+
}
636+
621637
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
622638
#[derive(Debug, PartialEq)]
623639
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
@@ -669,101 +685,65 @@ impl<'a> TryFrom<&'a [u8]> for UserDataBlock<'a> {
669685
);
670686
Ok(UserDataBlock::ResetAtApplicationLevel { subcode })
671687
}
672-
ControlInformation::SendData => {
673-
Err(ApplicationLayerError::Unimplemented {
674-
feature: "SendData control information",
675-
})
676-
}
677-
ControlInformation::SelectSlave => {
678-
Err(ApplicationLayerError::Unimplemented {
679-
feature: "SelectSlave control information",
680-
})
681-
}
682-
ControlInformation::SynchronizeSlave => {
683-
Err(ApplicationLayerError::Unimplemented {
684-
feature: "SynchronizeSlave control information",
685-
})
686-
}
687-
ControlInformation::SetBaudRate300 => {
688-
Err(ApplicationLayerError::Unimplemented {
689-
feature: "SetBaudRate300 control information",
690-
})
691-
}
692-
ControlInformation::SetBaudRate600 => {
693-
Err(ApplicationLayerError::Unimplemented {
694-
feature: "SetBaudRate600 control information",
695-
})
696-
}
697-
ControlInformation::SetBaudRate1200 => {
698-
Err(ApplicationLayerError::Unimplemented {
699-
feature: "SetBaudRate1200 control information",
700-
})
701-
}
702-
ControlInformation::SetBaudRate2400 => {
703-
Err(ApplicationLayerError::Unimplemented {
704-
feature: "SetBaudRate2400 control information",
705-
})
706-
}
707-
ControlInformation::SetBaudRate4800 => {
708-
Err(ApplicationLayerError::Unimplemented {
709-
feature: "SetBaudRate4800 control information",
710-
})
711-
}
712-
ControlInformation::SetBaudRate9600 => {
713-
Err(ApplicationLayerError::Unimplemented {
714-
feature: "SetBaudRate9600 control information",
715-
})
716-
}
717-
ControlInformation::SetBaudRate19200 => {
718-
Err(ApplicationLayerError::Unimplemented {
719-
feature: "SetBaudRate19200 control information",
720-
})
721-
}
722-
ControlInformation::SetBaudRate38400 => {
723-
Err(ApplicationLayerError::Unimplemented {
724-
feature: "SetBaudRate38400 control information",
725-
})
726-
}
727-
ControlInformation::OutputRAMContent => {
728-
Err(ApplicationLayerError::Unimplemented {
729-
feature: "OutputRAMContent control information",
730-
})
731-
}
732-
ControlInformation::WriteRAMContent => {
733-
Err(ApplicationLayerError::Unimplemented {
734-
feature: "WriteRAMContent control information",
735-
})
736-
}
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+
}),
737727
ControlInformation::StartCalibrationTestMode => {
738728
Err(ApplicationLayerError::Unimplemented {
739729
feature: "StartCalibrationTestMode control information",
740730
})
741731
}
742-
ControlInformation::ReadEEPROM => {
743-
Err(ApplicationLayerError::Unimplemented {
744-
feature: "ReadEEPROM control information",
745-
})
746-
}
747-
ControlInformation::StartSoftwareTest => {
748-
Err(ApplicationLayerError::Unimplemented {
749-
feature: "StartSoftwareTest control information",
750-
})
751-
}
752-
ControlInformation::HashProcedure(_) => {
753-
Err(ApplicationLayerError::Unimplemented {
754-
feature: "HashProcedure control information",
755-
})
756-
}
757-
ControlInformation::SendErrorStatus => {
758-
Err(ApplicationLayerError::Unimplemented {
759-
feature: "SendErrorStatus control information",
760-
})
761-
}
762-
ControlInformation::SendAlarmStatus => {
763-
Err(ApplicationLayerError::Unimplemented {
764-
feature: "SendAlarmStatus control information",
765-
})
766-
}
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+
}),
767747
ControlInformation::ResponseWithVariableDataStructure { lsb_order } => {
768748
let mut iter = data.iter().skip(1);
769749
let mut identification_number_bytes = [
@@ -897,16 +877,12 @@ impl<'a> TryFrom<&'a [u8]> for UserDataBlock<'a> {
897877
feature: "ApplicationLayerFormatFrameLongTransport control information",
898878
})
899879
}
900-
ControlInformation::ClockSyncAbsolute => {
901-
Err(ApplicationLayerError::Unimplemented {
902-
feature: "ClockSyncAbsolute control information",
903-
})
904-
}
905-
ControlInformation::ClockSyncRelative => {
906-
Err(ApplicationLayerError::Unimplemented {
907-
feature: "ClockSyncRelative control information",
908-
})
909-
}
880+
ControlInformation::ClockSyncAbsolute => Err(ApplicationLayerError::Unimplemented {
881+
feature: "ClockSyncAbsolute control information",
882+
}),
883+
ControlInformation::ClockSyncRelative => Err(ApplicationLayerError::Unimplemented {
884+
feature: "ClockSyncRelative control information",
885+
}),
910886
ControlInformation::ApplicationErrorShortTransport => {
911887
Err(ApplicationLayerError::Unimplemented {
912888
feature: "ApplicationErrorShortTransport control information",
@@ -917,16 +893,12 @@ impl<'a> TryFrom<&'a [u8]> for UserDataBlock<'a> {
917893
feature: "ApplicationErrorLongTransport control information",
918894
})
919895
}
920-
ControlInformation::AlarmShortTransport => {
921-
Err(ApplicationLayerError::Unimplemented {
922-
feature: "AlarmShortTransport control information",
923-
})
924-
}
925-
ControlInformation::AlarmLongTransport => {
926-
Err(ApplicationLayerError::Unimplemented {
927-
feature: "AlarmLongTransport control information",
928-
})
929-
}
896+
ControlInformation::AlarmShortTransport => Err(ApplicationLayerError::Unimplemented {
897+
feature: "AlarmShortTransport control information",
898+
}),
899+
ControlInformation::AlarmLongTransport => Err(ApplicationLayerError::Unimplemented {
900+
feature: "AlarmLongTransport control information",
901+
}),
930902
ControlInformation::ApplicationLayerNoTransport => {
931903
Err(ApplicationLayerError::Unimplemented {
932904
feature: "ApplicationLayerNoTransport control information",
@@ -938,7 +910,13 @@ impl<'a> TryFrom<&'a [u8]> for UserDataBlock<'a> {
938910
})
939911
}
940912
ControlInformation::ApplicationLayerShortTransport => {
941-
let mut iter = data.iter().skip(1);
913+
// CI=0xA0 (encryption mode 5) has an additional encryption configuration byte after CI
914+
// Other encrypted CI codes (0xA2, 0xA4, etc.) do not have this byte
915+
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);
942920

943921
Ok(UserDataBlock::VariableDataStructureWithShortTplHeader {
944922
short_tpl_header: ShortTplHeader {
@@ -958,7 +936,7 @@ impl<'a> TryFrom<&'a [u8]> for UserDataBlock<'a> {
958936
},
959937
},
960938
variable_data_block: data
961-
.get(5..data.len())
939+
.get(data_block_offset..data.len())
962940
.ok_or(ApplicationLayerError::InsufficientData)?,
963941
extended_link_layer: None,
964942
})
@@ -993,16 +971,12 @@ impl<'a> TryFrom<&'a [u8]> for UserDataBlock<'a> {
993971
feature: "TransportLayerLongReadoutToMeter control information",
994972
})
995973
}
996-
ControlInformation::NetworkLayerData => {
997-
Err(ApplicationLayerError::Unimplemented {
998-
feature: "NetworkLayerData control information",
999-
})
1000-
}
1001-
ControlInformation::FutureUse => {
1002-
Err(ApplicationLayerError::Unimplemented {
1003-
feature: "FutureUse control information",
1004-
})
1005-
}
974+
ControlInformation::NetworkLayerData => Err(ApplicationLayerError::Unimplemented {
975+
feature: "NetworkLayerData control information",
976+
}),
977+
ControlInformation::FutureUse => Err(ApplicationLayerError::Unimplemented {
978+
feature: "FutureUse control information",
979+
}),
1006980
ControlInformation::NetworkManagementApplication => {
1007981
Err(ApplicationLayerError::Unimplemented {
1008982
feature: "NetworkManagementApplication control information",
@@ -1063,16 +1037,12 @@ impl<'a> TryFrom<&'a [u8]> for UserDataBlock<'a> {
10631037
Err(ApplicationLayerError::MissingControlInformation)
10641038
}
10651039
}
1066-
ControlInformation::ExtendedLinkLayerII => {
1067-
Err(ApplicationLayerError::Unimplemented {
1068-
feature: "ExtendedLinkLayerII control information",
1069-
})
1070-
}
1071-
ControlInformation::ExtendedLinkLayerIII => {
1072-
Err(ApplicationLayerError::Unimplemented {
1073-
feature: "ExtendedLinkLayerIII control information",
1074-
})
1075-
}
1040+
ControlInformation::ExtendedLinkLayerII => Err(ApplicationLayerError::Unimplemented {
1041+
feature: "ExtendedLinkLayerII control information",
1042+
}),
1043+
ControlInformation::ExtendedLinkLayerIII => Err(ApplicationLayerError::Unimplemented {
1044+
feature: "ExtendedLinkLayerIII control information",
1045+
}),
10761046
}
10771047
}
10781048
}

crates/m-bus-application-layer/src/variable_user_data.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use super::data_information::{self};
22
use super::{DataRecords, LongTplHeader};
33

4-
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
4+
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
55
#[derive(Debug, Clone, Copy, PartialEq)]
66
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
77
#[non_exhaustive]
@@ -23,7 +23,7 @@ impl std::fmt::Display for DataRecordError {
2323
#[cfg(feature = "std")]
2424
impl std::error::Error for DataRecordError {}
2525

26-
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
26+
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
2727
#[derive(Debug, Clone, Copy, PartialEq)]
2828
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
2929
#[non_exhaustive]

crates/wireless-mbus-link-layer/src/lib.rs

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,23 @@ impl TryFrom<&[u8]> for ManufacturerId {
3636
])
3737
.map_err(|_| FrameError::TooShort)?,
3838
version: *iter.next().ok_or(FrameError::TooShort)?,
39-
device_type: DeviceType::from(*iter.next().ok_or(FrameError::TooShort)?),
39+
// In wireless M-Bus, device type encoding depends on the CI (Control Information) field:
40+
// - For unencrypted frames (CI=0x7A): use full device type byte
41+
// - For encrypted frames (CI=0xA0-0xAF): device type is in upper nibble,
42+
// lower nibble contains encryption mode information
43+
device_type: {
44+
let device_byte = *iter.next().ok_or(FrameError::TooShort)?;
45+
// Peek ahead at the CI field (at offset 8 from start of ManufacturerId data)
46+
let ci_byte = *data.get(8).ok_or(FrameError::TooShort)?;
47+
let device_type_code = if (0xA0..=0xAF).contains(&ci_byte) {
48+
// Encrypted frame: extract upper nibble only
49+
(device_byte >> 4) & 0x0F
50+
} else {
51+
// Unencrypted frame: use full byte
52+
device_byte
53+
};
54+
DeviceType::from(device_type_code)
55+
},
4056
is_unique_globally: false, /*todo not sure about this field*/
4157
})
4258
}

tests/test_wireless.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ struct ExpectedData {
2525
}
2626

2727
#[derive(Debug, Deserialize)]
28+
#[allow(dead_code)]
2829
struct ExpectedDataRecord {
2930
value: i64,
3031
unit: String,
@@ -40,10 +41,19 @@ fn device_type_from_str(device_type: &str) -> DeviceType {
4041
"Water" => DeviceType::WaterMeter,
4142
"WarmWater" => DeviceType::WarmWaterMeter,
4243
"Heat" => DeviceType::HeatMeterReturn,
44+
"HeatMeterFlow" => DeviceType::HeatMeterFlow,
4345
"HeatCostAllocator" => DeviceType::HeatCostAllocator,
4446
"HeatCoolingLoad" => DeviceType::CombinedHeatCoolingMeter,
4547
"Electricity" => DeviceType::ElectricityMeter,
4648
"RoomSensor" => DeviceType::RoomSensor,
49+
s if s.starts_with("Reserved(") => {
50+
// Parse "Reserved(128)" format
51+
let num_str = s.trim_start_matches("Reserved(").trim_end_matches(')');
52+
let code = num_str
53+
.parse::<u8>()
54+
.unwrap_or_else(|_| panic!("Invalid Reserved code: {}", s));
55+
DeviceType::Reserved(code)
56+
}
4757
_ => panic!("Unknown device type: {}", device_type),
4858
}
4959
}

tests/wmbusmeters/test_vectors.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,7 @@
151151
"identification_number": 58234965,
152152
"manufacturer": "TCH",
153153
"version": 39,
154-
"medium": "Heat",
154+
"medium": "HeatMeterFlow",
155155
"access_number": 18,
156156
"status": 159,
157157
"data_records": [

0 commit comments

Comments
 (0)