use crate::error::{Mf4Error, Result};
use crate::model::{Channel, ChannelGroup};
use crate::Mf4File;
const LIN_PREFIX: &str = "LIN_Frame";
const ID_MASK: u32 = 0x3F;
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct LinFrame<'a> {
pub timestamp: f64,
pub id: u8,
pub bus_channel: u8,
pub data: &'a [u8],
}
pub struct LinFrames {
pub(crate) timestamps: Vec<f64>,
pub(crate) ids: Vec<u8>,
pub(crate) bus_channels: Vec<u8>,
pub(crate) payloads: Vec<u8>,
pub(crate) payload_starts: Vec<usize>,
}
impl LinFrames {
pub fn len(&self) -> usize {
self.ids.len()
}
pub fn is_empty(&self) -> bool {
self.ids.is_empty()
}
pub fn get(&self, index: usize) -> Option<LinFrame<'_>> {
let id = *self.ids.get(index)?;
let from = *self.payload_starts.get(index)?;
let to = *self.payload_starts.get(index + 1)?;
Some(LinFrame {
timestamp: *self.timestamps.get(index)?,
id,
bus_channel: self.bus_channels.get(index).copied().unwrap_or(0),
data: self.payloads.get(from..to)?,
})
}
pub fn iter(&self) -> impl Iterator<Item = LinFrame<'_>> + '_ {
(0..self.len()).filter_map(|i| self.get(i))
}
}
fn field<'a>(group: &'a ChannelGroup, suffix: &str) -> Option<&'a Channel> {
let qualified = format!("{LIN_PREFIX}.{suffix}");
group.find_channel(&qualified)
}
fn require<'a>(group: &'a ChannelGroup, suffix: &str) -> Result<&'a Channel> {
field(group, suffix).ok_or_else(|| Mf4Error::ChannelNotFound {
name: format!("{LIN_PREFIX}.{suffix}"),
})
}
fn scalars(file: &Mf4File, channel: &Channel) -> Result<Vec<f64>> {
Ok(file.signal(channel)?.values()?.to_f64())
}
pub(crate) fn read_lin_frames(file: &Mf4File, group: &ChannelGroup) -> Result<LinFrames> {
let id_channel = require(group, "ID")?;
let length_channel = require(group, "DataLength")?;
let payload_channel = require(group, "DataBytes")?;
let master = group
.master_channel()
.ok_or_else(|| Mf4Error::ChannelNotFound {
name: format!("master channel of bus group '{}'", group.acquisition_name),
})?;
let ids: Vec<u8> = scalars(file, id_channel)?
.into_iter()
.map(|id| (id as u32 & ID_MASK) as u8)
.collect();
let timestamps = scalars(file, master)?;
let lengths: Vec<u8> = scalars(file, length_channel)?
.into_iter()
.map(|len| len as u8)
.collect();
let payload_values = file.signal(payload_channel)?.values()?;
if timestamps.len() != ids.len()
|| lengths.len() != ids.len()
|| payload_values.len() != ids.len()
{
return Err(Mf4Error::parse_error(format!(
"bus group '{}' has {} identifiers, {} timestamps, {} lengths and {} payloads; \
frame fields must agree sample for sample",
group.acquisition_name,
ids.len(),
timestamps.len(),
lengths.len(),
payload_values.len()
)));
}
let bus_channels = match field(group, "BusChannel") {
Some(channel) => scalars(file, channel)?
.into_iter()
.map(|bus| bus as u8)
.collect(),
None => Vec::new(),
};
let mut payloads = Vec::new();
let mut payload_starts = Vec::with_capacity(ids.len() + 1);
for (index, &len) in lengths.iter().enumerate() {
let sample = payload_values.bytes_at(index).ok_or_else(|| {
Mf4Error::parse_error(format!(
"payload channel '{LIN_PREFIX}.DataBytes' of bus group '{}' did not decode to bytes",
group.acquisition_name
))
})?;
payload_starts.push(payloads.len());
payloads.extend_from_slice(&sample[..usize::from(len).min(sample.len())]);
}
payload_starts.push(payloads.len());
Ok(LinFrames {
timestamps,
ids,
bus_channels,
payloads,
payload_starts,
})
}
pub(crate) fn is_lin_frame_group(group: &ChannelGroup) -> bool {
field(group, "ID").is_some() && field(group, "DataBytes").is_some()
}
pub(crate) fn decode_lin_signals<'a>(
file: &Mf4File,
database: &'a crate::candb::CanDatabase,
) -> Result<crate::bus::BusSignals<'a>> {
let mut accumulator = crate::bus::Accumulator::new(database);
for group in file.lin_frame_groups() {
for frame in file.lin_frames(group)?.iter() {
accumulator.push(crate::bus::CanFrame {
timestamp: frame.timestamp,
id: frame.id as u32,
extended: Some(false),
bus_channel: frame.bus_channel,
data: frame.data,
});
}
}
Ok(accumulator.finish())
}