use m_bus_core::{DeviceType, Function, IdentificationNumber, ManufacturerCode};
fn crc16_en13757(data: &[u8]) -> u16 {
let mut crc: u16 = 0x0000;
for &byte in data {
crc ^= (byte as u16) << 8;
for _ in 0..8 {
if crc & 0x8000 != 0 {
crc = (crc << 1) ^ 0x3D65;
} else {
crc <<= 1;
}
}
}
crc ^ 0xFFFF
}
pub fn strip_format_a_crcs<'a>(data: &[u8], output: &'a mut [u8]) -> Option<&'a [u8]> {
if data.len() < 12 || output.len() < data.len() {
return None;
}
if crc16_en13757(&data[0..10]) != u16::from_be_bytes([data[10], data[11]]) {
return None;
}
let mut out_pos = 10;
output[..10].copy_from_slice(&data[..10]);
let mut pos = 12;
while pos < data.len() {
let remaining = data.len() - pos;
if remaining < 3 {
output[out_pos..out_pos + remaining].copy_from_slice(&data[pos..pos + remaining]);
out_pos += remaining;
break;
}
let max_data_len = 16.min(remaining - 2);
let mut found = false;
for data_len in (1..=max_data_len).rev() {
let crc_start = pos + data_len;
if crc_start + 2 > data.len() {
continue;
}
if crc16_en13757(&data[pos..crc_start])
== u16::from_be_bytes([data[crc_start], data[crc_start + 1]])
{
output[out_pos..out_pos + data_len].copy_from_slice(&data[pos..crc_start]);
out_pos += data_len;
pos = crc_start + 2;
found = true;
break;
}
}
if !found {
let remaining = data.len() - pos;
output[out_pos..out_pos + remaining].copy_from_slice(&data[pos..]);
out_pos += remaining;
break;
}
}
output[0] = (out_pos - 1) as u8;
Some(&output[..out_pos])
}
#[derive(Debug, Clone, Copy, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct WirelessFrame<'a> {
pub function: Function,
pub manufacturer_id: ManufacturerId,
pub data: &'a [u8],
}
#[derive(Debug, Clone, Copy, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct ManufacturerId {
pub manufacturer_code: ManufacturerCode,
pub identification_number: IdentificationNumber,
pub device_type: DeviceType,
pub version: u8,
pub is_unique_globally: bool,
}
impl TryFrom<&[u8]> for ManufacturerId {
type Error = FrameError;
fn try_from(data: &[u8]) -> Result<Self, FrameError> {
let mut iter = data.iter();
Ok(ManufacturerId {
manufacturer_code: ManufacturerCode::from_id(u16::from_le_bytes([
*iter.next().ok_or(FrameError::TooShort)?,
*iter.next().ok_or(FrameError::TooShort)?,
]))
.map_err(|_| FrameError::TooShort)?,
identification_number: IdentificationNumber::from_bcd_hex_digits([
*iter.next().ok_or(FrameError::TooShort)?,
*iter.next().ok_or(FrameError::TooShort)?,
*iter.next().ok_or(FrameError::TooShort)?,
*iter.next().ok_or(FrameError::TooShort)?,
])
.map_err(|_| FrameError::TooShort)?,
version: *iter.next().ok_or(FrameError::TooShort)?,
device_type: {
let device_byte = *iter.next().ok_or(FrameError::TooShort)?;
let ci_byte = *data.get(8).ok_or(FrameError::TooShort)?;
let device_type_code = if (0xA0..=0xAF).contains(&ci_byte) {
(device_byte >> 4) & 0x0F
} else {
device_byte
};
DeviceType::from(device_type_code)
},
is_unique_globally: false,
})
}
}
#[derive(Debug, Copy, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum FrameError {
EmptyData,
TooShort,
WrongLength { expected: usize, actual: usize },
}
impl<'a> TryFrom<&'a [u8]> for WirelessFrame<'a> {
type Error = FrameError;
fn try_from(data: &'a [u8]) -> Result<Self, FrameError> {
let length = data.len();
let length_byte = *data.first().ok_or(FrameError::EmptyData)? as usize;
let _c_field = *data.get(1).ok_or(FrameError::TooShort)? as usize;
let manufacturer_id = ManufacturerId::try_from(&data[2..])?;
if length_byte + 1 == length {
return Ok(WirelessFrame {
function: Function::SndNk { prm: false },
manufacturer_id,
data: &data[10..],
});
}
Err(FrameError::WrongLength {
expected: length_byte + 1,
actual: data.len(),
})
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_dummy() {
let _id = 33225544;
let _medium = 7; let _man = "SEN";
let _version = 104;
let frame: &[u8] = &[
0x18, 0x44, 0xAE, 0x4C, 0x44, 0x55, 0x22, 0x33, 0x68, 0x07, 0x7A, 0x55, 0x00, 0x00,
0x00, 0x00, 0x04, 0x13, 0x89, 0xE2, 0x01, 0x00, 0x02, 0x3B, 0x00, 0x00,
];
let parsed = WirelessFrame::try_from(frame);
println!("{:#?}", parsed);
}
}