use byteorder::{ByteOrder, LittleEndian};
use chrono::{DateTime, Utc};
use crate::{
data::Data,
datagram::Datagram,
header::Header,
packet::Packet,
stream_blob_length::StreamBlobLength::{self, BlobLength, Malformed, Partial},
telegram::Telegram,
utils::{calc_and_compare_checksum_v0, copy_bytes_injecting_septett, has_msb_set},
};
pub fn length_from_bytes(buf: &[u8]) -> StreamBlobLength {
let len = buf.len();
if len < 1 {
Partial
} else if buf[0] != 0xAA {
Malformed
} else if len < 6 {
Partial
} else if has_msb_set(&buf[1..6]) {
Malformed
} else {
let protocol_version = buf[5];
let major = protocol_version & 0xF0;
if major == 0x10 {
if len < 10 {
Partial
} else if has_msb_set(&buf[6..10]) {
Malformed
} else if !calc_and_compare_checksum_v0(&buf[1..10]) {
Malformed
} else {
let frame_count = buf[8] as usize;
let expected_len = 10 + frame_count * 6;
if len < expected_len {
Partial
} else if has_msb_set(&buf[10..expected_len]) {
Malformed
} else {
let valid = (0..frame_count).all(|frame_idx| {
let frame_start = 10 + frame_idx * 6;
calc_and_compare_checksum_v0(&buf[frame_start..frame_start + 6])
});
if !valid {
Malformed
} else {
BlobLength(expected_len)
}
}
}
} else if major == 0x20 {
if len < 16 {
Partial
} else if has_msb_set(&buf[6..16]) {
Malformed
} else if !calc_and_compare_checksum_v0(&buf[1..16]) {
Malformed
} else {
BlobLength(16)
}
} else if major == 0x30 {
if len < 8 {
Partial
} else if has_msb_set(&buf[6..8]) {
Malformed
} else if !calc_and_compare_checksum_v0(&buf[1..8]) {
Malformed
} else {
let frame_count = Telegram::frame_count_from_command(buf[6]) as usize;
let expected_len = 8 + frame_count * 9;
if len < expected_len {
Partial
} else if has_msb_set(&buf[8..expected_len]) {
Malformed
} else {
let valid = (0..frame_count).all(|frame_idx| {
let frame_start = 8 + frame_idx * 9;
calc_and_compare_checksum_v0(&buf[frame_start..frame_start + 9])
});
if !valid {
Malformed
} else {
BlobLength(expected_len)
}
}
}
} else {
Malformed
}
}
}
pub fn data_from_checked_bytes(timestamp: DateTime<Utc>, channel: u8, buf: &[u8]) -> Data {
let protocol_version = buf[5];
let major = protocol_version & 0xF0;
let header = Header {
timestamp,
channel,
destination_address: LittleEndian::read_u16(&buf[1..]),
source_address: LittleEndian::read_u16(&buf[3..]),
protocol_version: buf[5],
};
if major == 0x10 {
let frame_count = buf[8] as usize;
let mut frame_data = [0u8; 508];
for frame_idx in 0..frame_count {
let src_start = 10 + frame_idx * 6;
let dst_start = frame_idx * 4;
copy_bytes_injecting_septett(
&mut frame_data[dst_start..dst_start + 4],
&buf[src_start..src_start + 5],
);
}
Data::Packet(Packet {
header,
command: LittleEndian::read_u16(&buf[6..]),
frame_count: buf[8],
frame_data,
})
} else if major == 0x20 {
let mut payload = [0u8; 6];
copy_bytes_injecting_septett(&mut payload, &buf[8..15]);
Data::Datagram(Datagram {
header,
command: LittleEndian::read_u16(&buf[6..]),
param16: LittleEndian::read_i16(&payload[0..]),
param32: LittleEndian::read_i32(&payload[2..]),
})
} else if major == 0x30 {
let command = buf[6];
let frame_count = Telegram::frame_count_from_command(command) as usize;
let mut frame_data = [0u8; 21];
for frame_idx in 0..frame_count {
let src_start = 8 + frame_idx * 9;
let dst_start = frame_idx * 7;
copy_bytes_injecting_septett(
&mut frame_data[dst_start..dst_start + 7],
&buf[src_start..src_start + 8],
);
}
Data::Telegram(Telegram {
header,
command,
frame_data,
})
} else {
unreachable!();
}
}
pub fn data_from_bytes(timestamp: DateTime<Utc>, channel: u8, buf: &[u8]) -> Option<Data> {
match length_from_bytes(buf) {
BlobLength(_) => Some(data_from_checked_bytes(timestamp, channel, buf)),
Partial | Malformed => None,
}
}
#[cfg(test)]
mod tests {
use super::*;
use chrono::TimeZone;
use crate::{
data::Data,
stream_blob_length::StreamBlobLength::{BlobLength, Malformed, Partial},
test_data::{LIVE_DATA_1, LIVE_TELEGRAM_1},
test_utils::to_hex_string,
};
#[test]
fn test_length_from_bytes() {
assert_eq!(Partial, length_from_bytes(&[]));
assert_eq!(Malformed, length_from_bytes(&[0x00]));
assert_eq!(Partial, length_from_bytes(&[0xAA, 0x10, 0x00, 0x11, 0x7E]));
assert_eq!(
Malformed,
length_from_bytes(&[0xAA, 0x10, 0x00, 0x11, 0x7E, 0xFF])
);
assert_eq!(
Partial,
length_from_bytes(&[0xAA, 0x10, 0x00, 0x22, 0x7E, 0x10, 0x00, 0x01, 0x01])
);
assert_eq!(
Malformed,
length_from_bytes(&[0xAA, 0x10, 0x00, 0x22, 0x7E, 0x10, 0x00, 0x01, 0x81, 0x3D])
);
assert_eq!(
Malformed,
length_from_bytes(&[0xAA, 0x10, 0x00, 0x22, 0x7E, 0x10, 0x00, 0x01, 0x01, 0x00])
);
assert_eq!(
BlobLength(10),
length_from_bytes(&[0xAA, 0x10, 0x00, 0x22, 0x7E, 0x10, 0x00, 0x01, 0x00, 0x3E])
);
assert_eq!(
Partial,
length_from_bytes(&[
0xAA, 0x10, 0x00, 0x22, 0x7E, 0x10, 0x00, 0x01, 0x01, 0x3D, 0x4B, 0x01, 0x0E, 0x00,
0x00
])
);
assert_eq!(
Malformed,
length_from_bytes(&[
0xAA, 0x10, 0x00, 0x22, 0x7E, 0x10, 0x00, 0x01, 0x01, 0x3D, 0x4B, 0x01, 0x0E, 0x00,
0x80, 0x25
])
);
assert_eq!(
Malformed,
length_from_bytes(&[
0xAA, 0x10, 0x00, 0x22, 0x7E, 0x10, 0x00, 0x01, 0x01, 0x3D, 0x4B, 0x01, 0x0E, 0x00,
0x00, 0x00
])
);
assert_eq!(
BlobLength(16),
length_from_bytes(&[
0xAA, 0x10, 0x00, 0x22, 0x7E, 0x10, 0x00, 0x01, 0x01, 0x3D, 0x4B, 0x01, 0x0E, 0x00,
0x00, 0x25
])
);
assert_eq!(
Partial,
length_from_bytes(&[
0xAA, 0x00, 0x00, 0x11, 0x7E, 0x20, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00
])
);
assert_eq!(
Malformed,
length_from_bytes(&[
0xAA, 0x00, 0x00, 0x11, 0x7E, 0x20, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x80, 0x4B
])
);
assert_eq!(
Malformed,
length_from_bytes(&[
0xAA, 0x00, 0x00, 0x11, 0x7E, 0x20, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00
])
);
assert_eq!(
BlobLength(16),
length_from_bytes(&[
0xAA, 0x00, 0x00, 0x11, 0x7E, 0x20, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x4B
])
);
assert_eq!(
Partial,
length_from_bytes(&[0xAA, 0x71, 0x77, 0x11, 0x20, 0x30, 0x25])
);
assert_eq!(
Malformed,
length_from_bytes(&[0xAA, 0x71, 0x77, 0x11, 0x20, 0x30, 0xA5, 0x11])
);
assert_eq!(
Malformed,
length_from_bytes(&[0xAA, 0x71, 0x77, 0x11, 0x20, 0x30, 0x25, 0x00])
);
assert_eq!(
BlobLength(8),
length_from_bytes(&[0xAA, 0x71, 0x77, 0x11, 0x20, 0x30, 0x05, 0x31])
);
assert_eq!(
Partial,
length_from_bytes(&[
0xAA, 0x71, 0x77, 0x11, 0x20, 0x30, 0x25, 0x11, 0x60, 0x18, 0x2B, 0x04, 0x00, 0x00,
0x00, 0x04
])
);
assert_eq!(
Malformed,
length_from_bytes(&[
0xAA, 0x71, 0x77, 0x11, 0x20, 0x30, 0x25, 0x11, 0x60, 0x18, 0x2B, 0x04, 0x00, 0x00,
0x00, 0x84, 0x54
])
);
assert_eq!(
Malformed,
length_from_bytes(&[
0xAA, 0x71, 0x77, 0x11, 0x20, 0x30, 0x25, 0x11, 0x60, 0x18, 0x2B, 0x04, 0x00, 0x00,
0x00, 0x04, 0x00
])
);
assert_eq!(
BlobLength(17),
length_from_bytes(&[
0xAA, 0x71, 0x77, 0x11, 0x20, 0x30, 0x25, 0x11, 0x60, 0x18, 0x2B, 0x04, 0x00, 0x00,
0x00, 0x04, 0x54
])
);
assert_eq!(BlobLength(172), length_from_bytes(&LIVE_DATA_1[0..]));
assert_eq!(BlobLength(70), length_from_bytes(&LIVE_DATA_1[172..]));
assert_eq!(BlobLength(16), length_from_bytes(&LIVE_DATA_1[242..]));
assert_eq!(BlobLength(94), length_from_bytes(&LIVE_DATA_1[258..]));
assert_eq!(BlobLength(16), length_from_bytes(&LIVE_DATA_1[352..]));
assert_eq!(BlobLength(17), length_from_bytes(&LIVE_TELEGRAM_1[0..]));
}
#[test]
fn test_data_from_checked_bytes() {
let timestamp = Utc.timestamp(1485688933, 0);
let channel = 0x11;
let data = data_from_checked_bytes(timestamp, channel, &LIVE_DATA_1[0..]);
if let Data::Packet(packet) = data {
assert_eq!(timestamp, packet.header.timestamp);
assert_eq!(channel, packet.header.channel);
assert_eq!(0x0010, packet.header.destination_address);
assert_eq!(0x7E11, packet.header.source_address);
assert_eq!(0x10, packet.header.protocol_version);
assert_eq!(0x0100, packet.command);
assert_eq!(0x1B, packet.frame_count);
assert_eq!(
to_hex_string(&[
0x37, 0x00, 0x1d, 0x01, 0x3d, 0x01, 0x24, 0x01, 0x07, 0x01, 0x09, 0x01, 0x02, 0x00, 0x37, 0x01, 0x13, 0x02, 0xb8, 0x22, 0xb8, 0x22, 0xb8, 0x22, 0x0f, 0x27, 0x0f, 0x27, 0x0f, 0x27, 0x46, 0x05, 0x0f, 0x27, 0x0f, 0x27, 0x0f, 0x27, 0x0f, 0x27, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x27, 0x0f, 0x27, 0x0f, 0x27, 0x0f, 0x27, 0x00, 0x00, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4c, 0xf2, 0x1f, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ]),
to_hex_string(&packet.frame_data[0..108])
);
} else {
panic!("Expected {:?} to be a Packet", data);
}
}
}