use crate::{Error, Result, subsystems::memory::{MemoryBackend, memory_types}};
use memory_types::{FromMemoryBackend, MemoryType};
fn encode_spatial(coordinate: f32) -> Result<i16> {
let scaled = coordinate * 1000.0;
if scaled < i16::MIN as f32 || scaled > i16::MAX as f32 {
return Err(Error::InvalidArgument(
format!("Spatial coordinate {:.3}m out of representable range ({:.3}m to {:.3}m)",
coordinate, i16::MIN as f32 / 1000.0, i16::MAX as f32 / 1000.0)
));
}
Ok(scaled as i16)
}
fn encode_yaw(angle_rad: f32) -> Result<i16> {
let scaled = angle_rad.to_degrees() * 10.0;
if scaled < i16::MIN as f32 || scaled > i16::MAX as f32 {
return Err(Error::InvalidArgument(
format!("Yaw angle {:.3} rad out of representable range", angle_rad)
));
}
Ok(scaled as i16)
}
#[derive(Debug, Clone)]
pub struct Poly {
pub values: [f32; 8],
}
impl Default for Poly {
fn default() -> Self {
Self { values: [0.0; 8] }
}
}
impl Poly {
pub fn new(values: [f32; 8]) -> Self {
Self { values }
}
pub fn from_slice(values: &[f32]) -> Self {
let mut poly = Self::default();
let len = values.len().min(8);
poly.values[..len].copy_from_slice(&values[..len]);
poly
}
}
#[derive(Debug, Clone)]
pub struct Poly4D {
pub duration: f32,
pub x: Poly,
pub y: Poly,
pub z: Poly,
pub yaw: Poly,
}
impl Poly4D {
pub fn new(duration: f32, x: Poly, y: Poly, z: Poly, yaw: Poly) -> Self {
Self { duration, x, y, z, yaw }
}
pub fn pack(&self) -> Vec<u8> {
let mut data = Vec::with_capacity(132);
for &v in &self.x.values {
data.extend_from_slice(&v.to_le_bytes());
}
for &v in &self.y.values {
data.extend_from_slice(&v.to_le_bytes());
}
for &v in &self.z.values {
data.extend_from_slice(&v.to_le_bytes());
}
for &v in &self.yaw.values {
data.extend_from_slice(&v.to_le_bytes());
}
data.extend_from_slice(&self.duration.to_le_bytes());
data
}
}
#[derive(Debug, Clone)]
pub struct CompressedStart {
pub x: f32,
pub y: f32,
pub z: f32,
pub yaw: f32,
}
impl CompressedStart {
pub fn new(x: f32, y: f32, z: f32, yaw: f32) -> Self {
Self { x, y, z, yaw }
}
pub fn pack(&self) -> Result<Vec<u8>> {
let mut data = Vec::with_capacity(8);
data.extend_from_slice(&encode_spatial(self.x)?.to_le_bytes());
data.extend_from_slice(&encode_spatial(self.y)?.to_le_bytes());
data.extend_from_slice(&encode_spatial(self.z)?.to_le_bytes());
data.extend_from_slice(&encode_yaw(self.yaw)?.to_le_bytes());
Ok(data)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum ElementType {
Constant = 0,
Linear = 1,
Quadratic = 2,
Full = 3,
}
impl ElementType {
fn from_len(len: usize) -> Option<Self> {
match len {
0 => Some(ElementType::Constant),
1 => Some(ElementType::Linear),
3 => Some(ElementType::Quadratic),
7 => Some(ElementType::Full),
_ => None,
}
}
}
#[derive(Debug, Clone)]
pub struct CompressedSegment {
duration: f32,
x: Vec<f32>,
y: Vec<f32>,
z: Vec<f32>,
yaw: Vec<f32>,
}
impl CompressedSegment {
pub fn new(duration: f32, x: Vec<f32>, y: Vec<f32>, z: Vec<f32>, yaw: Vec<f32>) -> Result<Self> {
Self::validate(&x)?;
Self::validate(&y)?;
Self::validate(&z)?;
Self::validate(&yaw)?;
Ok(Self { duration, x, y, z, yaw })
}
fn validate(element: &[f32]) -> Result<()> {
let len = element.len();
if len != 0 && len != 1 && len != 3 && len != 7 {
return Err(Error::InvalidArgument(
"Element length must be 0, 1, 3, or 7".to_owned()
));
}
Ok(())
}
fn encode_type(element: &[f32]) -> u8 {
ElementType::from_len(element.len()).unwrap() as u8
}
fn pack_spatial_element(element: &[f32]) -> Result<Vec<u8>> {
let mut data = Vec::new();
for &v in element {
data.extend_from_slice(&encode_spatial(v)?.to_le_bytes());
}
Ok(data)
}
fn pack_yaw_element(element: &[f32]) -> Result<Vec<u8>> {
let mut data = Vec::new();
for &v in element {
data.extend_from_slice(&encode_yaw(v)?.to_le_bytes());
}
Ok(data)
}
pub fn pack(&self) -> Result<Vec<u8>> {
let element_types = Self::encode_type(&self.x)
| (Self::encode_type(&self.y) << 2)
| (Self::encode_type(&self.z) << 4)
| (Self::encode_type(&self.yaw) << 6);
let duration_ms = (self.duration * 1000.0) as u16;
let mut data = Vec::new();
data.push(element_types);
data.extend_from_slice(&duration_ms.to_le_bytes());
data.extend(Self::pack_spatial_element(&self.x)?);
data.extend(Self::pack_spatial_element(&self.y)?);
data.extend(Self::pack_spatial_element(&self.z)?);
data.extend(Self::pack_yaw_element(&self.yaw)?);
Ok(data)
}
}
#[derive(Debug)]
pub struct TrajectoryMemory {
memory: MemoryBackend,
}
impl FromMemoryBackend for TrajectoryMemory {
async fn from_memory_backend(memory: MemoryBackend) -> Result<Self> {
if memory.memory_type == MemoryType::Trajectory {
Ok(Self { memory })
} else {
Err(Error::MemoryError("Wrong type of memory!".to_owned()))
}
}
async fn initialize_memory_backend(memory: MemoryBackend) -> Result<Self> {
if memory.memory_type == MemoryType::Trajectory {
Ok(Self { memory })
} else {
Err(Error::MemoryError("Wrong type of memory!".to_owned()))
}
}
fn close_memory(self) -> MemoryBackend {
self.memory
}
}
impl TrajectoryMemory {
pub async fn write_uncompressed(
&self,
segments: &[Poly4D],
start_addr: usize,
) -> Result<usize> {
let mut data = Vec::new();
for segment in segments {
data.extend(segment.pack());
}
self.memory.write::<fn(usize, usize)>(start_addr, &data, None).await?;
Ok(data.len())
}
pub async fn write_uncompressed_with_progress<F>(
&self,
segments: &[Poly4D],
start_addr: usize,
progress_callback: F,
) -> Result<usize>
where
F: FnMut(usize, usize),
{
let mut data = Vec::new();
for segment in segments {
data.extend(segment.pack());
}
self.memory.write(start_addr, &data, Some(progress_callback)).await?;
Ok(data.len())
}
pub async fn write_compressed(
&self,
start: &CompressedStart,
segments: &[CompressedSegment],
start_addr: usize,
) -> Result<usize> {
let mut data = start.pack()?;
for segment in segments {
data.extend(segment.pack()?);
}
self.memory.write::<fn(usize, usize)>(start_addr, &data, None).await?;
Ok(data.len())
}
pub async fn write_compressed_with_progress<F>(
&self,
start: &CompressedStart,
segments: &[CompressedSegment],
start_addr: usize,
progress_callback: F,
) -> Result<usize>
where
F: FnMut(usize, usize),
{
let mut data = start.pack()?;
for segment in segments {
data.extend(segment.pack()?);
}
self.memory.write(start_addr, &data, Some(progress_callback)).await?;
Ok(data.len())
}
}