use crate::Result;
use crate::{
adapters::{AdapterValidation, FrameAdapter},
types::{FramePacket, VarData, VariableInfo, VariableSchema},
};
use std::sync::Arc;
#[derive(Debug, Clone)]
pub struct DynamicFrame {
data: Arc<[u8]>,
tick_count: u32,
schema: Arc<VariableSchema>,
}
impl DynamicFrame {
pub fn variable_info(&self, name: &str) -> Option<&VariableInfo> {
self.schema.variables.get(name)
}
pub fn get<T: VarData>(&self, name: &str) -> Option<T> {
let info = self.variable_info(name)?;
T::from_bytes(self.data.as_ref(), info).ok()
}
pub fn f32(&self, name: &str) -> Option<f32> {
self.get(name)
}
pub fn i32(&self, name: &str) -> Option<i32> {
self.get(name)
}
pub fn u32(&self, name: &str) -> Option<u32> {
self.get(name)
}
pub fn bool(&self, name: &str) -> Option<bool> {
self.get(name)
}
pub fn tick_count(&self) -> u32 {
self.tick_count
}
}
impl FrameAdapter for DynamicFrame {
fn validate_schema(_schema: &VariableSchema) -> Result<AdapterValidation> {
Ok(AdapterValidation::new(Vec::new()))
}
fn adapt(packet: &FramePacket, _validation: &AdapterValidation) -> Self {
Self {
data: Arc::clone(&packet.data),
tick_count: packet.tick,
schema: Arc::clone(&packet.schema),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{VariableInfo, VariableSchema, types::VariableType};
use std::collections::HashMap;
#[test]
fn dynamic_frame_basic_lookup() {
let mut vars = HashMap::new();
vars.insert(
"RPM".to_string(),
VariableInfo {
name: "RPM".into(),
data_type: VariableType::Int32,
offset: 0,
count: 1,
count_as_time: false,
units: "rev/min".into(),
description: "Engine RPM".into(),
},
);
vars.insert(
"Speed".to_string(),
VariableInfo {
name: "Speed".into(),
data_type: VariableType::Float32,
offset: 4,
count: 1,
count_as_time: false,
units: "m/s".into(),
description: "Vehicle speed".into(),
},
);
vars.insert(
"CarIdxLapDistPct".to_string(),
VariableInfo {
name: "CarIdxLapDistPct".into(),
data_type: VariableType::Float32,
offset: 8,
count: 4,
count_as_time: false,
units: "%".into(),
description: "Per-car lap distance percentage".into(),
},
);
let schema = VariableSchema { variables: vars, frame_size: 24 };
let mut data = vec![0u8; 24];
data[0..4].copy_from_slice(&1234i32.to_le_bytes());
data[4..8].copy_from_slice(&42.5f32.to_le_bytes());
let lap_dist = [0.10f32, 0.20, 0.30, 0.40];
for (idx, value) in lap_dist.iter().enumerate() {
let start = 8 + idx * 4;
data[start..start + 4].copy_from_slice(&value.to_le_bytes());
}
let packet = FramePacket::new(data, 10, 0, Arc::new(schema));
let df = DynamicFrame::adapt(&packet, &AdapterValidation::new(vec![]));
assert_eq!(df.i32("RPM"), Some(1234));
assert!(df.f32("Speed").unwrap() - 42.5 < 1e-5);
let lap_dist_values: Vec<f32> = df.get("CarIdxLapDistPct").unwrap();
assert_eq!(lap_dist_values, lap_dist);
assert_eq!(df.u32("Missing"), None);
}
}