wireless_mbus_link_layer/
lib.rs1use m_bus_core::{DeviceType, Function, IdentificationNumber, ManufacturerCode};
2
3fn crc16_en13757(data: &[u8]) -> u16 {
6 let mut crc: u16 = 0x0000;
7 for &byte in data {
8 crc ^= (byte as u16) << 8;
9 for _ in 0..8 {
10 if crc & 0x8000 != 0 {
11 crc = (crc << 1) ^ 0x3D65;
12 } else {
13 crc <<= 1;
14 }
15 }
16 }
17 crc ^ 0xFFFF
18}
19
20pub fn trailing_frame_crc_start(data: &[u8]) -> Option<usize> {
23 let crc_start = data.len().checked_sub(2)?;
24 if crc_start < 10 {
25 return None;
26 }
27
28 let expected = u16::from_be_bytes([data[crc_start], data[crc_start + 1]]);
29 (crc16_en13757(&data[..crc_start]) == expected).then_some(crc_start)
30}
31
32pub fn strip_format_a_crcs<'a>(data: &[u8], output: &'a mut [u8]) -> Option<&'a [u8]> {
42 if data.len() < 12 || output.len() < data.len() {
43 return None;
44 }
45
46 if crc16_en13757(&data[0..10]) != u16::from_be_bytes([data[10], data[11]]) {
48 return None;
49 }
50
51 let mut out_pos = 10;
52 output[..10].copy_from_slice(&data[..10]);
53
54 let mut pos = 12;
55 while pos < data.len() {
56 let remaining = data.len() - pos;
57 if remaining < 3 {
58 output[out_pos..out_pos + remaining].copy_from_slice(&data[pos..pos + remaining]);
59 out_pos += remaining;
60 break;
61 }
62
63 let max_data_len = 16.min(remaining - 2);
64 let mut found = false;
65
66 for data_len in (1..=max_data_len).rev() {
67 let crc_start = pos + data_len;
68 if crc_start + 2 > data.len() {
69 continue;
70 }
71 if crc16_en13757(&data[pos..crc_start])
72 == u16::from_be_bytes([data[crc_start], data[crc_start + 1]])
73 {
74 output[out_pos..out_pos + data_len].copy_from_slice(&data[pos..crc_start]);
75 out_pos += data_len;
76 pos = crc_start + 2;
77 found = true;
78 break;
79 }
80 }
81
82 if !found {
83 let remaining = data.len() - pos;
84 output[out_pos..out_pos + remaining].copy_from_slice(&data[pos..]);
85 out_pos += remaining;
86 break;
87 }
88 }
89
90 output[0] = (out_pos - 1) as u8;
91 Some(&output[..out_pos])
92}
93
94#[derive(Debug, Clone, Copy, PartialEq)]
95#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
96pub struct WirelessFrame<'a> {
97 pub function: Function,
98 pub manufacturer_id: ManufacturerId,
99 pub data: &'a [u8],
100}
101
102#[derive(Debug, Clone, Copy, PartialEq)]
103#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
104pub struct ManufacturerId {
105 pub manufacturer_code: ManufacturerCode,
106 pub identification_number: IdentificationNumber,
107 pub device_type: DeviceType,
108 pub version: u8,
109 pub is_unique_globally: bool,
110}
111
112impl TryFrom<&[u8]> for ManufacturerId {
113 type Error = FrameError;
114 fn try_from(data: &[u8]) -> Result<Self, FrameError> {
115 let mut iter = data.iter();
116 Ok(ManufacturerId {
117 manufacturer_code: ManufacturerCode::from_id(u16::from_le_bytes([
118 *iter.next().ok_or(FrameError::TooShort)?,
119 *iter.next().ok_or(FrameError::TooShort)?,
120 ]))
121 .map_err(|_| FrameError::TooShort)?,
122 identification_number: IdentificationNumber::from_bcd_hex_digits([
123 *iter.next().ok_or(FrameError::TooShort)?,
124 *iter.next().ok_or(FrameError::TooShort)?,
125 *iter.next().ok_or(FrameError::TooShort)?,
126 *iter.next().ok_or(FrameError::TooShort)?,
127 ])
128 .map_err(|_| FrameError::TooShort)?,
129 version: *iter.next().ok_or(FrameError::TooShort)?,
130 device_type: {
135 let device_byte = *iter.next().ok_or(FrameError::TooShort)?;
136 let ci_byte = *data.get(8).ok_or(FrameError::TooShort)?;
138 let device_type_code = if (0xA0..=0xAF).contains(&ci_byte) {
139 (device_byte >> 4) & 0x0F
141 } else {
142 device_byte
144 };
145 DeviceType::from(device_type_code)
146 },
147 is_unique_globally: false, })
149 }
150}
151
152#[derive(Debug, Copy, Clone, PartialEq)]
153#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
154pub enum FrameError {
155 EmptyData,
156 TooShort,
157 WrongLength { expected: usize, actual: usize },
158}
159
160impl<'a> TryFrom<&'a [u8]> for WirelessFrame<'a> {
161 type Error = FrameError;
162
163 fn try_from(data: &'a [u8]) -> Result<Self, FrameError> {
164 let length = data.len();
165 let length_byte = *data.first().ok_or(FrameError::EmptyData)? as usize;
166 let _c_field = *data.get(1).ok_or(FrameError::TooShort)? as usize;
167 let manufacturer_id = ManufacturerId::try_from(&data[2..])?;
168
169 if length_byte + 1 == length {
171 let data_end = trailing_frame_crc_start(data).unwrap_or(length);
172 return Ok(WirelessFrame {
173 function: Function::SndNk { prm: false },
174 manufacturer_id,
175 data: &data[10..data_end],
176 });
177 }
178
179 Err(FrameError::WrongLength {
180 expected: length_byte + 1,
181 actual: data.len(),
182 })
183 }
184}
185
186#[cfg(test)]
187mod test {
188 use super::*;
189
190 #[test]
191 fn test_dummy() {
192 let _id = 33225544;
193 let _medium = 7; let _man = "SEN";
195 let _version = 104;
196 let frame: &[u8] = &[
197 0x18, 0x44, 0xAE, 0x4C, 0x44, 0x55, 0x22, 0x33, 0x68, 0x07, 0x7A, 0x55, 0x00, 0x00,
198 0x00, 0x00, 0x04, 0x13, 0x89, 0xE2, 0x01, 0x00, 0x02, 0x3B, 0x00, 0x00,
199 ];
200 let parsed = WirelessFrame::try_from(frame);
201 println!("{:#?}", parsed);
202 }
203
204 #[test]
205 fn test_trailing_frame_crc_is_not_payload() {
206 let frame = [
207 0x14, 0x44, 0xAE, 0x0C, 0x78, 0x56, 0x34, 0x12, 0x01, 0x07, 0x8C, 0x20, 0x27, 0x78,
208 0x0B, 0x13, 0x43, 0x65, 0x87, 0x7A, 0xC5,
209 ];
210
211 assert_eq!(trailing_frame_crc_start(&frame), Some(19));
212
213 let parsed = WirelessFrame::try_from(frame.as_slice()).expect("valid wireless frame");
214 assert_eq!(
215 parsed.data,
216 &[0x8C, 0x20, 0x27, 0x78, 0x0B, 0x13, 0x43, 0x65, 0x87]
217 );
218 }
219}