use crate::FrameIdString;
use crate::velocity::VelocityTransform;
use cu29::clock::{CuTime, RobotClock};
use dashmap::DashMap;
use std::fmt::Debug;
#[derive(Clone)]
pub(crate) struct VelocityTransformCacheEntry<T: Copy + Debug + 'static> {
pub(crate) velocity: VelocityTransform<T>,
pub(crate) time: CuTime,
pub(crate) last_access: CuTime,
pub(crate) path_hash: u64,
}
pub(crate) struct VelocityTransformCache<T: Copy + Debug + 'static> {
entries: DashMap<(FrameIdString, FrameIdString), VelocityTransformCacheEntry<T>>,
max_size: usize,
max_age_nanos: u64,
cleanup_interval_nanos: u64,
last_cleanup_cell: std::sync::Mutex<CuTime>,
}
impl<T: Copy + Debug + 'static> VelocityTransformCache<T> {
pub(crate) fn new(max_size: usize, max_age_nanos: u64) -> Self {
Self {
entries: DashMap::with_capacity(max_size),
max_size,
max_age_nanos,
cleanup_interval_nanos: 5_000_000_000, last_cleanup_cell: std::sync::Mutex::new(CuTime::from(0u64)),
}
}
pub(crate) fn get(
&self,
from: &str,
to: &str,
time: CuTime,
path_hash: u64,
robot_clock: &RobotClock,
) -> Option<VelocityTransform<T>> {
let key = (
FrameIdString::from(from).expect("Frame name too long"),
FrameIdString::from(to).expect("Frame name too long"),
);
if let Some(mut entry) = self.entries.get_mut(&key) {
let now = robot_clock.now();
if entry.time == time && entry.path_hash == path_hash {
let age = now.as_nanos().saturating_sub(entry.last_access.as_nanos());
if age <= self.max_age_nanos {
entry.last_access = now;
return Some(entry.velocity.clone());
}
}
}
None
}
pub(crate) fn insert(
&self,
from: &str,
to: &str,
velocity: VelocityTransform<T>,
time: CuTime,
path_hash: u64,
robot_clock: &RobotClock,
) {
let now = robot_clock.now();
let key = (
FrameIdString::from(from).expect("Frame name too long"),
FrameIdString::from(to).expect("Frame name too long"),
);
if self.entries.len() >= self.max_size {
let mut oldest_key = None;
let mut oldest_time = now;
for entry in self.entries.iter() {
if entry.last_access < oldest_time {
oldest_time = entry.last_access;
oldest_key = Some(*entry.key());
}
}
if let Some(key_to_remove) = oldest_key {
self.entries.remove(&key_to_remove);
}
}
self.entries.insert(
key,
VelocityTransformCacheEntry {
velocity,
time,
last_access: now,
path_hash,
},
);
}
pub(crate) fn should_cleanup(&self, robot_clock: &RobotClock) -> bool {
let now = robot_clock.now();
let last_cleanup = *self.last_cleanup_cell.lock().unwrap();
let elapsed = now.as_nanos().saturating_sub(last_cleanup.as_nanos());
elapsed >= self.cleanup_interval_nanos
}
pub(crate) fn cleanup(&self, robot_clock: &RobotClock) {
let now = robot_clock.now();
let mut keys_to_remove = Vec::new();
for entry in self.entries.iter() {
let age = now.as_nanos().saturating_sub(entry.last_access.as_nanos());
if age > self.max_age_nanos {
keys_to_remove.push(*entry.key());
}
}
for key in keys_to_remove {
self.entries.remove(&key);
}
*self.last_cleanup_cell.lock().unwrap() = now;
}
pub(crate) fn clear(&self) {
self.entries.clear();
}
}