Evaluate remaining red/green time for upcoming intersection
This commit is contained in:
parent
933106b7ea
commit
7463adb038
12 changed files with 2979 additions and 103 deletions
221
src/cache.rs
Normal file
221
src/cache.rs
Normal file
|
|
@ -0,0 +1,221 @@
|
|||
//! Data Cache
|
||||
//!
|
||||
//! Store items for some time
|
||||
|
||||
use alloc::rc::Rc;
|
||||
use alloc::vec::Vec;
|
||||
use core::ops::Sub;
|
||||
|
||||
pub trait Cachable<K>
|
||||
where
|
||||
K: core::cmp::PartialEq,
|
||||
{
|
||||
fn key(&self) -> K;
|
||||
}
|
||||
|
||||
struct CacheItem<K, T>
|
||||
where
|
||||
T: Cachable<K>,
|
||||
K: core::cmp::PartialEq,
|
||||
{
|
||||
timestamp: chrono::NaiveDateTime,
|
||||
data: Rc<core::cell::RefCell<T>>,
|
||||
phantom: core::marker::PhantomData<K>, // rust wants this
|
||||
}
|
||||
|
||||
impl<K, T> core::fmt::Display for CacheItem<K, T>
|
||||
where
|
||||
T: Cachable<K>,
|
||||
K: core::cmp::PartialEq + core::fmt::Display,
|
||||
{
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"CacheItem({}, {:?})",
|
||||
self.data.borrow().key(),
|
||||
self.timestamp
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<K, T> CacheItem<K, T>
|
||||
where
|
||||
T: Cachable<K>,
|
||||
K: core::cmp::PartialEq,
|
||||
{
|
||||
fn new(time_now: chrono::NaiveDateTime, data: T) -> Self {
|
||||
Self {
|
||||
timestamp: time_now,
|
||||
data: Rc::new(core::cell::RefCell::new(data)),
|
||||
phantom: core::marker::PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum CacheError<K>
|
||||
where
|
||||
K: core::fmt::Display,
|
||||
{
|
||||
KeyNotFound(K),
|
||||
InsertKeyAlreadyPresent(K),
|
||||
}
|
||||
|
||||
impl<K> core::fmt::Display for CacheError<K>
|
||||
where
|
||||
K: core::fmt::Display,
|
||||
{
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
match self {
|
||||
CacheError::KeyNotFound(key) => write!(f, "Key '{key}' doesn't exist in cache"),
|
||||
CacheError::InsertKeyAlreadyPresent(key) => {
|
||||
write!(f, "Insert failed b/c key '{key}' already exists in cache")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Item cache
|
||||
///
|
||||
/// Stores items for a certain time.
|
||||
/// Items need to implement `Cachable` so that an itentifier (key) can be retreived from them.
|
||||
pub struct Cache<K, T>
|
||||
where
|
||||
T: Cachable<K>,
|
||||
K: core::cmp::PartialEq,
|
||||
{
|
||||
lifetime: chrono::Duration,
|
||||
items: Vec<CacheItem<K, T>>,
|
||||
}
|
||||
|
||||
impl<K, T> core::fmt::Display for Cache<K, T>
|
||||
where
|
||||
T: Cachable<K>,
|
||||
K: core::cmp::PartialEq + core::fmt::Display,
|
||||
{
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
write!(f, "Cache({}, {} items)", self.lifetime, self.items.len())
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
impl<K, T> Cache<K, T>
|
||||
where
|
||||
T: Cachable<K> + Clone,
|
||||
K: core::cmp::PartialEq + core::fmt::Display,
|
||||
{
|
||||
/// Creates a new cache with a certain item lifetime
|
||||
pub fn new(lifetime: chrono::Duration) -> Self {
|
||||
Self {
|
||||
lifetime,
|
||||
items: alloc::vec![],
|
||||
}
|
||||
}
|
||||
|
||||
/// Determines if a key is in the cache
|
||||
pub fn contains(&mut self, key: &K) -> bool {
|
||||
self.items
|
||||
.iter()
|
||||
.find(|i| i.data.borrow().key() == *key)
|
||||
.is_some()
|
||||
}
|
||||
|
||||
/// Retrieves all known keys
|
||||
pub fn keys(&self) -> Vec<K> {
|
||||
self.items.iter().map(|i| i.data.borrow().key()).collect()
|
||||
}
|
||||
|
||||
/// Adds a new item to the cache
|
||||
///
|
||||
/// Call [`Self::update`] to update the contents of the cached item
|
||||
/// or just to bump the timestamp.
|
||||
///
|
||||
/// # Errors
|
||||
/// Fails if the specified key already exist
|
||||
pub fn insert(
|
||||
&mut self,
|
||||
time_now: chrono::NaiveDateTime,
|
||||
data: T,
|
||||
) -> Result<(), CacheError<K>> {
|
||||
let key = data.key();
|
||||
if self.contains(&key) {
|
||||
Err(CacheError::InsertKeyAlreadyPresent(key))
|
||||
} else {
|
||||
self.items.push(CacheItem::new(time_now, data));
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Retrieves an item from the cache
|
||||
///
|
||||
/// Returns `None` if no item exists for this key
|
||||
pub fn get(&mut self, key: &K) -> Option<Rc<core::cell::RefCell<T>>> {
|
||||
self.items.iter().find_map(|i| {
|
||||
if i.data.borrow().key() == *key {
|
||||
Some(i.data.clone())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Retrieves items from the cache which match the predicate
|
||||
pub fn get_filtered<P>(&self, predicate: P) -> Vec<Rc<core::cell::RefCell<T>>>
|
||||
where
|
||||
P: Fn(&T) -> bool,
|
||||
{
|
||||
self.items
|
||||
.iter()
|
||||
.filter_map(|i| {
|
||||
let data = i.data.borrow();
|
||||
if predicate(&data) {
|
||||
Some(i.data.clone())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Updates an item in the cache using the provided function
|
||||
///
|
||||
/// This can also be used to just bump the timestamp of the cached item by providing an empty closure.
|
||||
///
|
||||
/// # Errors
|
||||
/// Fails if the specified key does not exist in the cache
|
||||
pub fn update<F>(
|
||||
&mut self,
|
||||
time_now: chrono::NaiveDateTime,
|
||||
key: K,
|
||||
func: F,
|
||||
) -> Result<(), CacheError<K>>
|
||||
where
|
||||
F: Fn(&mut T),
|
||||
K: core::fmt::Display,
|
||||
{
|
||||
// find item
|
||||
let Some(pos) = self.items.iter().position(|i| i.data.borrow().key() == key) else {
|
||||
return Err(CacheError::KeyNotFound(key));
|
||||
};
|
||||
|
||||
// unwrap is fine since we fetched the position before
|
||||
let item = self.items.get_mut(pos).unwrap();
|
||||
|
||||
let mut data = item.data.borrow_mut();
|
||||
func(&mut data);
|
||||
|
||||
item.timestamp = time_now;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Cleans items which timed out from the cache
|
||||
///
|
||||
/// Call this regularly to prune old items.
|
||||
pub fn prune(&mut self, time_now: chrono::NaiveDateTime) {
|
||||
self.items.retain(|i| {
|
||||
let age = time_now.sub(i.timestamp);
|
||||
age < self.lifetime
|
||||
});
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue