use crate::error::{Mf4Error, Result};
use crate::model::{Channel, ChannelGroup};
use crate::Mf4File;
const FLX_PREFIX: &str = "FLX_Frame";
const FRAME_ID_NAMES: &[&str] = &["ID", "FrameID"];
const STARTUP_NAMES: &[&str] = &["StartUpFrameFlag", "StartupFrameFlag", "StartupFlag"];
const FRAME_ID_MASK: u32 = 0x7FF;
const CYCLE_MASK: u32 = 0x3F;
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct FlexRayFrame<'a> {
pub timestamp: f64,
pub frame_id: u16,
pub cycle: u8,
pub bus_channel: u8,
pub null_frame: bool,
pub sync_frame: bool,
pub startup: bool,
pub data: &'a [u8],
}
pub struct FlexRayFrames {
pub(crate) timestamps: Vec<f64>,
pub(crate) frame_ids: Vec<u16>,
pub(crate) cycles: Vec<u8>,
pub(crate) bus_channels: Vec<u8>,
pub(crate) null_frames: Vec<bool>,
pub(crate) sync_frames: Vec<bool>,
pub(crate) startup_frames: Vec<bool>,
pub(crate) payloads: Vec<u8>,
pub(crate) payload_starts: Vec<usize>,
}
impl FlexRayFrames {
pub fn len(&self) -> usize {
self.frame_ids.len()
}
pub fn is_empty(&self) -> bool {
self.frame_ids.is_empty()
}
pub fn get(&self, index: usize) -> Option<FlexRayFrame<'_>> {
let frame_id = *self.frame_ids.get(index)?;
let cycle = *self.cycles.get(index)?;
let from = *self.payload_starts.get(index)?;
let to = *self.payload_starts.get(index + 1)?;
Some(FlexRayFrame {
timestamp: *self.timestamps.get(index)?,
frame_id,
cycle,
bus_channel: self.bus_channels.get(index).copied().unwrap_or(0),
null_frame: *self.null_frames.get(index)?,
sync_frame: *self.sync_frames.get(index)?,
startup: *self.startup_frames.get(index)?,
data: self.payloads.get(from..to)?,
})
}
pub fn iter(&self) -> impl Iterator<Item = FlexRayFrame<'_>> + '_ {
(0..self.len()).filter_map(|i| self.get(i))
}
}
fn field<'a>(group: &'a ChannelGroup, suffix: &str) -> Option<&'a Channel> {
let qualified = format!("{FLX_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!("{FLX_PREFIX}.{suffix}"),
})
}
fn field_any<'a>(group: &'a ChannelGroup, suffixes: &[&str]) -> Option<&'a Channel> {
suffixes.iter().find_map(|s| field(group, s))
}
fn scalars(file: &Mf4File, channel: &Channel) -> Result<Vec<f64>> {
Ok(file.signal(channel)?.values()?.to_f64())
}
pub(crate) fn read_flexray_frames(file: &Mf4File, group: &ChannelGroup) -> Result<FlexRayFrames> {
let frame_id_channel =
field_any(group, FRAME_ID_NAMES).ok_or_else(|| Mf4Error::ChannelNotFound {
name: format!("{FLX_PREFIX}.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 frame_ids: Vec<u16> = scalars(file, frame_id_channel)?
.into_iter()
.map(|id| (id as u32 & FRAME_ID_MASK) as u16)
.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() != frame_ids.len()
|| lengths.len() != frame_ids.len()
|| payload_values.len() != frame_ids.len()
{
return Err(Mf4Error::parse_error(format!(
"bus group '{}' has {} frame IDs, {} timestamps, {} lengths and {} payloads; \
frame fields must agree sample for sample",
group.acquisition_name,
frame_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 cycles = match field(group, "Cycle") {
Some(channel) => scalars(file, channel)?
.into_iter()
.map(|val| (val as u32 & CYCLE_MASK) as u8)
.collect(),
None => vec![0u8; frame_ids.len()],
};
let null_frames = match field(group, "NullFrameFlag") {
Some(channel) => scalars(file, channel)?
.into_iter()
.map(|val| val == 0.0)
.collect(),
None => vec![false; frame_ids.len()],
};
let sync_frames = match field(group, "SyncFrameFlag") {
Some(channel) => scalars(file, channel)?
.into_iter()
.map(|val| val != 0.0)
.collect(),
None => vec![false; frame_ids.len()],
};
let startup_frames = match field_any(group, STARTUP_NAMES) {
Some(channel) => scalars(file, channel)?
.into_iter()
.map(|val| val != 0.0)
.collect(),
None => vec![false; frame_ids.len()],
};
let mut payloads = Vec::new();
let mut payload_starts = Vec::with_capacity(frame_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 '{FLX_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(FlexRayFrames {
timestamps,
frame_ids,
cycles,
bus_channels,
null_frames,
sync_frames,
startup_frames,
payloads,
payload_starts,
})
}
pub(crate) fn is_flexray_frame_group(group: &ChannelGroup) -> bool {
field_any(group, FRAME_ID_NAMES).is_some() && field(group, "DataBytes").is_some()
}