use crate::blocks::common::{read_link, BlockHeader, ParseBlock, BLOCK_HEADER_SIZE};
use crate::error::{Mf4Error, Result};
use byteorder::ReadBytesExt;
use std::io::Cursor;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SourceType {
Other,
Ecu,
Bus,
Io,
Tool,
User,
Unknown(u8),
}
impl SourceType {
fn from_u8(value: u8) -> Self {
match value {
0 => SourceType::Other,
1 => SourceType::Ecu,
2 => SourceType::Bus,
3 => SourceType::Io,
4 => SourceType::Tool,
5 => SourceType::User,
v => SourceType::Unknown(v),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum BusType {
None,
Other,
Can,
Lin,
Most,
FlexRay,
KLine,
Ethernet,
Usb,
Unknown(u8),
}
impl BusType {
fn from_u8(value: u8) -> Self {
match value {
0 => BusType::None,
1 => BusType::Other,
2 => BusType::Can,
3 => BusType::Lin,
4 => BusType::Most,
5 => BusType::FlexRay,
6 => BusType::KLine,
7 => BusType::Ethernet,
8 => BusType::Usb,
v => BusType::Unknown(v),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub struct SiFlags {
pub simulated: bool,
}
impl SiFlags {
fn from_u8(value: u8) -> Self {
SiFlags {
simulated: (value & 0x01) != 0,
}
}
}
#[derive(Debug, Clone)]
pub struct SiBlock {
pub header: BlockHeader,
pub tx_name: u64,
pub tx_path: u64,
pub md_comment: u64,
pub source_type: SourceType,
pub bus_type: BusType,
pub flags: SiFlags,
pub reserved: [u8; 5],
}
impl SiBlock {
pub const MIN_SIZE: u64 = BLOCK_HEADER_SIZE as u64 + 3 * 8 + 8;
}
impl ParseBlock for SiBlock {
fn parse(data: &[u8], offset: u64) -> Result<Self> {
let header = BlockHeader::parse(data, offset)?;
header.validate_type(b"##SI", offset)?;
if header.length < Self::MIN_SIZE {
return Err(Mf4Error::invalid_block_size(
"SI",
header.length,
Self::MIN_SIZE,
));
}
let links_start = BLOCK_HEADER_SIZE;
let tx_name = read_link(data, links_start)?;
let tx_path = read_link(data, links_start + 8)?;
let md_comment = read_link(data, links_start + 16)?;
let data_start = header.data_offset();
let data_section = data
.get(data_start..)
.ok_or_else(|| Mf4Error::truncated(offset, data_start, data.len()))?;
let mut cursor = Cursor::new(data_section);
let source_type_raw = cursor.read_u8()?;
let source_type = SourceType::from_u8(source_type_raw);
let bus_type_raw = cursor.read_u8()?;
let bus_type = BusType::from_u8(bus_type_raw);
let flags_raw = cursor.read_u8()?;
let flags = SiFlags::from_u8(flags_raw);
let mut reserved = [0u8; 5];
std::io::Read::read_exact(&mut cursor, &mut reserved)?;
Ok(SiBlock {
header,
tx_name,
tx_path,
md_comment,
source_type,
bus_type,
flags,
reserved,
})
}
}
#[derive(Debug, Clone, Default)]
pub struct SourceInfo {
pub name: String,
pub path: String,
pub source_type: Option<SourceType>,
pub bus_type: Option<BusType>,
pub simulated: bool,
}
#[cfg(test)]
mod tests {
use super::*;
fn create_test_si_block() -> Vec<u8> {
let mut data = vec![0u8; 56];
data[0..4].copy_from_slice(b"##SI");
data[8..16].copy_from_slice(&56u64.to_le_bytes()); data[16..24].copy_from_slice(&3u64.to_le_bytes());
data[24..32].copy_from_slice(&1000u64.to_le_bytes()); data[32..40].copy_from_slice(&2000u64.to_le_bytes()); data[40..48].copy_from_slice(&0u64.to_le_bytes());
data[48] = 2; data[49] = 2; data[50] = 0;
data
}
#[test]
fn test_si_block_parse() {
let data = create_test_si_block();
let si = SiBlock::parse(&data, 5000).unwrap();
assert_eq!(si.header.block_type, *b"##SI");
assert_eq!(si.tx_name, 1000);
assert_eq!(si.tx_path, 2000);
assert_eq!(si.source_type, SourceType::Bus);
assert_eq!(si.bus_type, BusType::Can);
assert!(!si.flags.simulated);
}
#[test]
fn test_source_type() {
assert_eq!(SourceType::from_u8(0), SourceType::Other);
assert_eq!(SourceType::from_u8(1), SourceType::Ecu);
assert_eq!(SourceType::from_u8(2), SourceType::Bus);
assert!(matches!(SourceType::from_u8(99), SourceType::Unknown(99)));
}
#[test]
fn test_bus_type() {
assert_eq!(BusType::from_u8(2), BusType::Can);
assert_eq!(BusType::from_u8(3), BusType::Lin);
assert_eq!(BusType::from_u8(7), BusType::Ethernet);
}
}