use std::collections::HashMap;
use waremax_core::{EdgeId, NodeId, RobotId, SimTime};
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub enum ReservableResource {
Edge(EdgeId),
Node(NodeId),
}
#[derive(Clone, Debug)]
pub struct Reservation {
pub robot_id: RobotId,
pub resource: ReservableResource,
pub start_time: SimTime,
pub end_time: SimTime,
}
impl Reservation {
pub fn new(
robot_id: RobotId,
resource: ReservableResource,
start_time: SimTime,
end_time: SimTime,
) -> Self {
Self {
robot_id,
resource,
start_time,
end_time,
}
}
pub fn overlaps(&self, start: SimTime, end: SimTime) -> bool {
self.start_time < end && self.end_time > start
}
}
#[derive(Clone, Debug)]
pub struct ReservationConflict {
pub resource: ReservableResource,
pub conflicting_robot: RobotId,
pub conflict_start: SimTime,
pub conflict_end: SimTime,
}
#[derive(Clone, Default)]
pub struct ReservationManager {
reservations: HashMap<ReservableResource, Vec<Reservation>>,
pub enabled: bool,
}
impl ReservationManager {
pub fn new() -> Self {
Self {
reservations: HashMap::new(),
enabled: false,
}
}
pub fn new_enabled() -> Self {
Self {
reservations: HashMap::new(),
enabled: true,
}
}
pub fn can_reserve(
&self,
resource: &ReservableResource,
robot: RobotId,
start: SimTime,
end: SimTime,
) -> bool {
if !self.enabled {
return true;
}
if let Some(reservations) = self.reservations.get(resource) {
!reservations
.iter()
.any(|r| r.robot_id != robot && r.overlaps(start, end))
} else {
true
}
}
pub fn reserve(
&mut self,
resource: ReservableResource,
robot: RobotId,
start: SimTime,
end: SimTime,
) -> Result<(), ReservationConflict> {
if !self.enabled {
return Ok(());
}
if let Some(reservations) = self.reservations.get(&resource) {
for r in reservations {
if r.robot_id != robot && r.overlaps(start, end) {
return Err(ReservationConflict {
resource: resource.clone(),
conflicting_robot: r.robot_id,
conflict_start: r.start_time,
conflict_end: r.end_time,
});
}
}
}
self.reservations
.entry(resource.clone())
.or_default()
.push(Reservation::new(robot, resource, start, end));
Ok(())
}
pub fn release_all(&mut self, robot: RobotId) {
for reservations in self.reservations.values_mut() {
reservations.retain(|r| r.robot_id != robot);
}
}
pub fn release(&mut self, resource: &ReservableResource, robot: RobotId) {
if let Some(reservations) = self.reservations.get_mut(resource) {
reservations.retain(|r| r.robot_id != robot);
}
}
pub fn cleanup_expired(&mut self, current_time: SimTime) {
for reservations in self.reservations.values_mut() {
reservations.retain(|r| r.end_time > current_time);
}
}
pub fn get_conflicts(
&self,
resource: &ReservableResource,
robot: RobotId,
start: SimTime,
end: SimTime,
) -> Vec<&Reservation> {
if let Some(reservations) = self.reservations.get(resource) {
reservations
.iter()
.filter(|r| r.robot_id != robot && r.overlaps(start, end))
.collect()
} else {
Vec::new()
}
}
pub fn get_robot_reservations(&self, robot: RobotId) -> Vec<&Reservation> {
self.reservations
.values()
.flat_map(|v| v.iter())
.filter(|r| r.robot_id == robot)
.collect()
}
pub fn reservation_count(&self) -> usize {
self.reservations.values().map(|v| v.len()).sum()
}
pub fn has_reservations(
&self,
resource: &ReservableResource,
start: SimTime,
end: SimTime,
) -> bool {
if let Some(reservations) = self.reservations.get(resource) {
reservations.iter().any(|r| r.overlaps(start, end))
} else {
false
}
}
}
#[cfg(test)]
mod tests {
use super::*;
fn t(seconds: f64) -> SimTime {
SimTime::from_seconds(seconds)
}
#[test]
fn test_reservation_overlaps() {
let res = Reservation::new(
RobotId(1),
ReservableResource::Edge(EdgeId(1)),
t(10.0),
t(20.0),
);
assert!(res.overlaps(t(15.0), t(25.0))); assert!(res.overlaps(t(5.0), t(15.0))); assert!(res.overlaps(t(12.0), t(18.0))); assert!(res.overlaps(t(5.0), t(25.0)));
assert!(!res.overlaps(t(0.0), t(10.0))); assert!(!res.overlaps(t(20.0), t(30.0))); assert!(!res.overlaps(t(0.0), t(5.0))); assert!(!res.overlaps(t(25.0), t(30.0))); }
#[test]
fn test_reservation_manager_disabled() {
let mut mgr = ReservationManager::new();
assert!(!mgr.enabled);
assert!(mgr.can_reserve(
&ReservableResource::Edge(EdgeId(1)),
RobotId(1),
t(0.0),
t(10.0)
));
mgr.reserve(
ReservableResource::Edge(EdgeId(1)),
RobotId(1),
t(0.0),
t(10.0),
)
.unwrap();
mgr.reserve(
ReservableResource::Edge(EdgeId(1)),
RobotId(2),
t(5.0),
t(15.0),
)
.unwrap();
}
#[test]
fn test_reservation_manager_enabled() {
let mut mgr = ReservationManager::new_enabled();
assert!(mgr.enabled);
mgr.reserve(
ReservableResource::Edge(EdgeId(1)),
RobotId(1),
t(0.0),
t(10.0),
)
.unwrap();
assert!(mgr.can_reserve(
&ReservableResource::Edge(EdgeId(1)),
RobotId(1),
t(5.0),
t(15.0)
));
assert!(!mgr.can_reserve(
&ReservableResource::Edge(EdgeId(1)),
RobotId(2),
t(5.0),
t(15.0)
));
assert!(mgr.can_reserve(
&ReservableResource::Edge(EdgeId(1)),
RobotId(2),
t(10.0),
t(20.0)
));
}
#[test]
fn test_reservation_conflict() {
let mut mgr = ReservationManager::new_enabled();
mgr.reserve(
ReservableResource::Edge(EdgeId(1)),
RobotId(1),
t(0.0),
t(10.0),
)
.unwrap();
let result = mgr.reserve(
ReservableResource::Edge(EdgeId(1)),
RobotId(2),
t(5.0),
t(15.0),
);
assert!(result.is_err());
let conflict = result.unwrap_err();
assert_eq!(conflict.conflicting_robot, RobotId(1));
assert_eq!(conflict.conflict_start, t(0.0));
assert_eq!(conflict.conflict_end, t(10.0));
}
#[test]
fn test_release_all() {
let mut mgr = ReservationManager::new_enabled();
mgr.reserve(
ReservableResource::Edge(EdgeId(1)),
RobotId(1),
t(0.0),
t(10.0),
)
.unwrap();
mgr.reserve(
ReservableResource::Edge(EdgeId(2)),
RobotId(1),
t(10.0),
t(20.0),
)
.unwrap();
mgr.reserve(
ReservableResource::Node(NodeId(5)),
RobotId(1),
t(0.0),
t(5.0),
)
.unwrap();
assert_eq!(mgr.reservation_count(), 3);
mgr.release_all(RobotId(1));
assert_eq!(mgr.reservation_count(), 0);
}
#[test]
fn test_release_specific() {
let mut mgr = ReservationManager::new_enabled();
let edge1 = ReservableResource::Edge(EdgeId(1));
let edge2 = ReservableResource::Edge(EdgeId(2));
mgr.reserve(edge1.clone(), RobotId(1), t(0.0), t(10.0))
.unwrap();
mgr.reserve(edge2.clone(), RobotId(1), t(0.0), t(10.0))
.unwrap();
assert_eq!(mgr.reservation_count(), 2);
mgr.release(&edge1, RobotId(1));
assert_eq!(mgr.reservation_count(), 1);
assert!(mgr.can_reserve(&edge1, RobotId(2), t(0.0), t(10.0)));
assert!(!mgr.can_reserve(&edge2, RobotId(2), t(0.0), t(10.0)));
}
#[test]
fn test_cleanup_expired() {
let mut mgr = ReservationManager::new_enabled();
mgr.reserve(
ReservableResource::Edge(EdgeId(1)),
RobotId(1),
t(0.0),
t(10.0),
)
.unwrap();
mgr.reserve(
ReservableResource::Edge(EdgeId(2)),
RobotId(1),
t(5.0),
t(15.0),
)
.unwrap();
mgr.reserve(
ReservableResource::Edge(EdgeId(3)),
RobotId(1),
t(10.0),
t(20.0),
)
.unwrap();
assert_eq!(mgr.reservation_count(), 3);
mgr.cleanup_expired(t(10.0));
assert_eq!(mgr.reservation_count(), 2);
mgr.cleanup_expired(t(15.0));
assert_eq!(mgr.reservation_count(), 1);
}
#[test]
fn test_get_conflicts() {
let mut mgr = ReservationManager::new_enabled();
mgr.reserve(
ReservableResource::Edge(EdgeId(1)),
RobotId(1),
t(0.0),
t(10.0),
)
.unwrap();
mgr.reserve(
ReservableResource::Edge(EdgeId(1)),
RobotId(2),
t(15.0),
t(25.0),
)
.unwrap();
let edge1 = ReservableResource::Edge(EdgeId(1));
let conflicts = mgr.get_conflicts(&edge1, RobotId(3), t(5.0), t(12.0));
assert_eq!(conflicts.len(), 1);
assert_eq!(conflicts[0].robot_id, RobotId(1));
let conflicts = mgr.get_conflicts(&edge1, RobotId(3), t(5.0), t(20.0));
assert_eq!(conflicts.len(), 2);
let conflicts = mgr.get_conflicts(&edge1, RobotId(1), t(0.0), t(10.0));
assert_eq!(conflicts.len(), 0);
}
#[test]
fn test_node_reservations() {
let mut mgr = ReservationManager::new_enabled();
let node = ReservableResource::Node(NodeId(5));
mgr.reserve(node.clone(), RobotId(1), t(0.0), t(10.0))
.unwrap();
assert!(!mgr.can_reserve(&node, RobotId(2), t(5.0), t(15.0)));
assert!(mgr.can_reserve(&node, RobotId(2), t(10.0), t(20.0)));
}
#[test]
fn test_get_robot_reservations() {
let mut mgr = ReservationManager::new_enabled();
mgr.reserve(
ReservableResource::Edge(EdgeId(1)),
RobotId(1),
t(0.0),
t(10.0),
)
.unwrap();
mgr.reserve(
ReservableResource::Edge(EdgeId(2)),
RobotId(1),
t(10.0),
t(20.0),
)
.unwrap();
mgr.reserve(
ReservableResource::Edge(EdgeId(3)),
RobotId(2),
t(0.0),
t(10.0),
)
.unwrap();
let robot1_res = mgr.get_robot_reservations(RobotId(1));
assert_eq!(robot1_res.len(), 2);
let robot2_res = mgr.get_robot_reservations(RobotId(2));
assert_eq!(robot2_res.len(), 1);
let robot3_res = mgr.get_robot_reservations(RobotId(3));
assert_eq!(robot3_res.len(), 0);
}
}