1use std::collections::HashMap;
8use waremax_core::{EdgeId, NodeId, RobotId, SimTime};
9
10#[derive(Clone, Debug, PartialEq, Eq, Hash)]
12pub enum ReservableResource {
13 Edge(EdgeId),
15 Node(NodeId),
17}
18
19#[derive(Clone, Debug)]
21pub struct Reservation {
22 pub robot_id: RobotId,
24 pub resource: ReservableResource,
26 pub start_time: SimTime,
28 pub end_time: SimTime,
30}
31
32impl Reservation {
33 pub fn new(
35 robot_id: RobotId,
36 resource: ReservableResource,
37 start_time: SimTime,
38 end_time: SimTime,
39 ) -> Self {
40 Self {
41 robot_id,
42 resource,
43 start_time,
44 end_time,
45 }
46 }
47
48 pub fn overlaps(&self, start: SimTime, end: SimTime) -> bool {
50 self.start_time < end && self.end_time > start
51 }
52}
53
54#[derive(Clone, Debug)]
56pub struct ReservationConflict {
57 pub resource: ReservableResource,
59 pub conflicting_robot: RobotId,
61 pub conflict_start: SimTime,
63 pub conflict_end: SimTime,
65}
66
67#[derive(Clone, Default)]
72pub struct ReservationManager {
73 reservations: HashMap<ReservableResource, Vec<Reservation>>,
75 pub enabled: bool,
77}
78
79impl ReservationManager {
80 pub fn new() -> Self {
82 Self {
83 reservations: HashMap::new(),
84 enabled: false,
85 }
86 }
87
88 pub fn new_enabled() -> Self {
90 Self {
91 reservations: HashMap::new(),
92 enabled: true,
93 }
94 }
95
96 pub fn can_reserve(
101 &self,
102 resource: &ReservableResource,
103 robot: RobotId,
104 start: SimTime,
105 end: SimTime,
106 ) -> bool {
107 if !self.enabled {
108 return true;
109 }
110
111 if let Some(reservations) = self.reservations.get(resource) {
112 !reservations
113 .iter()
114 .any(|r| r.robot_id != robot && r.overlaps(start, end))
115 } else {
116 true
117 }
118 }
119
120 pub fn reserve(
125 &mut self,
126 resource: ReservableResource,
127 robot: RobotId,
128 start: SimTime,
129 end: SimTime,
130 ) -> Result<(), ReservationConflict> {
131 if !self.enabled {
132 return Ok(());
133 }
134
135 if let Some(reservations) = self.reservations.get(&resource) {
137 for r in reservations {
138 if r.robot_id != robot && r.overlaps(start, end) {
139 return Err(ReservationConflict {
140 resource: resource.clone(),
141 conflicting_robot: r.robot_id,
142 conflict_start: r.start_time,
143 conflict_end: r.end_time,
144 });
145 }
146 }
147 }
148
149 self.reservations
151 .entry(resource.clone())
152 .or_default()
153 .push(Reservation::new(robot, resource, start, end));
154 Ok(())
155 }
156
157 pub fn release_all(&mut self, robot: RobotId) {
161 for reservations in self.reservations.values_mut() {
162 reservations.retain(|r| r.robot_id != robot);
163 }
164 }
165
166 pub fn release(&mut self, resource: &ReservableResource, robot: RobotId) {
168 if let Some(reservations) = self.reservations.get_mut(resource) {
169 reservations.retain(|r| r.robot_id != robot);
170 }
171 }
172
173 pub fn cleanup_expired(&mut self, current_time: SimTime) {
177 for reservations in self.reservations.values_mut() {
178 reservations.retain(|r| r.end_time > current_time);
179 }
180 }
181
182 pub fn get_conflicts(
187 &self,
188 resource: &ReservableResource,
189 robot: RobotId,
190 start: SimTime,
191 end: SimTime,
192 ) -> Vec<&Reservation> {
193 if let Some(reservations) = self.reservations.get(resource) {
194 reservations
195 .iter()
196 .filter(|r| r.robot_id != robot && r.overlaps(start, end))
197 .collect()
198 } else {
199 Vec::new()
200 }
201 }
202
203 pub fn get_robot_reservations(&self, robot: RobotId) -> Vec<&Reservation> {
205 self.reservations
206 .values()
207 .flat_map(|v| v.iter())
208 .filter(|r| r.robot_id == robot)
209 .collect()
210 }
211
212 pub fn reservation_count(&self) -> usize {
214 self.reservations.values().map(|v| v.len()).sum()
215 }
216
217 pub fn has_reservations(
219 &self,
220 resource: &ReservableResource,
221 start: SimTime,
222 end: SimTime,
223 ) -> bool {
224 if let Some(reservations) = self.reservations.get(resource) {
225 reservations.iter().any(|r| r.overlaps(start, end))
226 } else {
227 false
228 }
229 }
230}
231
232#[cfg(test)]
233mod tests {
234 use super::*;
235
236 fn t(seconds: f64) -> SimTime {
237 SimTime::from_seconds(seconds)
238 }
239
240 #[test]
241 fn test_reservation_overlaps() {
242 let res = Reservation::new(
243 RobotId(1),
244 ReservableResource::Edge(EdgeId(1)),
245 t(10.0),
246 t(20.0),
247 );
248
249 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))); }
261
262 #[test]
263 fn test_reservation_manager_disabled() {
264 let mut mgr = ReservationManager::new();
265 assert!(!mgr.enabled);
266
267 assert!(mgr.can_reserve(
269 &ReservableResource::Edge(EdgeId(1)),
270 RobotId(1),
271 t(0.0),
272 t(10.0)
273 ));
274
275 mgr.reserve(
277 ReservableResource::Edge(EdgeId(1)),
278 RobotId(1),
279 t(0.0),
280 t(10.0),
281 )
282 .unwrap();
283
284 mgr.reserve(
286 ReservableResource::Edge(EdgeId(1)),
287 RobotId(2),
288 t(5.0),
289 t(15.0),
290 )
291 .unwrap();
292 }
293
294 #[test]
295 fn test_reservation_manager_enabled() {
296 let mut mgr = ReservationManager::new_enabled();
297 assert!(mgr.enabled);
298
299 mgr.reserve(
301 ReservableResource::Edge(EdgeId(1)),
302 RobotId(1),
303 t(0.0),
304 t(10.0),
305 )
306 .unwrap();
307
308 assert!(mgr.can_reserve(
310 &ReservableResource::Edge(EdgeId(1)),
311 RobotId(1),
312 t(5.0),
313 t(15.0)
314 ));
315
316 assert!(!mgr.can_reserve(
318 &ReservableResource::Edge(EdgeId(1)),
319 RobotId(2),
320 t(5.0),
321 t(15.0)
322 ));
323
324 assert!(mgr.can_reserve(
326 &ReservableResource::Edge(EdgeId(1)),
327 RobotId(2),
328 t(10.0),
329 t(20.0)
330 ));
331 }
332
333 #[test]
334 fn test_reservation_conflict() {
335 let mut mgr = ReservationManager::new_enabled();
336
337 mgr.reserve(
339 ReservableResource::Edge(EdgeId(1)),
340 RobotId(1),
341 t(0.0),
342 t(10.0),
343 )
344 .unwrap();
345
346 let result = mgr.reserve(
348 ReservableResource::Edge(EdgeId(1)),
349 RobotId(2),
350 t(5.0),
351 t(15.0),
352 );
353
354 assert!(result.is_err());
355 let conflict = result.unwrap_err();
356 assert_eq!(conflict.conflicting_robot, RobotId(1));
357 assert_eq!(conflict.conflict_start, t(0.0));
358 assert_eq!(conflict.conflict_end, t(10.0));
359 }
360
361 #[test]
362 fn test_release_all() {
363 let mut mgr = ReservationManager::new_enabled();
364
365 mgr.reserve(
367 ReservableResource::Edge(EdgeId(1)),
368 RobotId(1),
369 t(0.0),
370 t(10.0),
371 )
372 .unwrap();
373 mgr.reserve(
374 ReservableResource::Edge(EdgeId(2)),
375 RobotId(1),
376 t(10.0),
377 t(20.0),
378 )
379 .unwrap();
380 mgr.reserve(
381 ReservableResource::Node(NodeId(5)),
382 RobotId(1),
383 t(0.0),
384 t(5.0),
385 )
386 .unwrap();
387
388 assert_eq!(mgr.reservation_count(), 3);
389
390 mgr.release_all(RobotId(1));
392
393 assert_eq!(mgr.reservation_count(), 0);
394 }
395
396 #[test]
397 fn test_release_specific() {
398 let mut mgr = ReservationManager::new_enabled();
399
400 let edge1 = ReservableResource::Edge(EdgeId(1));
401 let edge2 = ReservableResource::Edge(EdgeId(2));
402
403 mgr.reserve(edge1.clone(), RobotId(1), t(0.0), t(10.0))
404 .unwrap();
405 mgr.reserve(edge2.clone(), RobotId(1), t(0.0), t(10.0))
406 .unwrap();
407
408 assert_eq!(mgr.reservation_count(), 2);
409
410 mgr.release(&edge1, RobotId(1));
412
413 assert_eq!(mgr.reservation_count(), 1);
414
415 assert!(mgr.can_reserve(&edge1, RobotId(2), t(0.0), t(10.0)));
417 assert!(!mgr.can_reserve(&edge2, RobotId(2), t(0.0), t(10.0)));
419 }
420
421 #[test]
422 fn test_cleanup_expired() {
423 let mut mgr = ReservationManager::new_enabled();
424
425 mgr.reserve(
427 ReservableResource::Edge(EdgeId(1)),
428 RobotId(1),
429 t(0.0),
430 t(10.0),
431 )
432 .unwrap();
433 mgr.reserve(
434 ReservableResource::Edge(EdgeId(2)),
435 RobotId(1),
436 t(5.0),
437 t(15.0),
438 )
439 .unwrap();
440 mgr.reserve(
441 ReservableResource::Edge(EdgeId(3)),
442 RobotId(1),
443 t(10.0),
444 t(20.0),
445 )
446 .unwrap();
447
448 assert_eq!(mgr.reservation_count(), 3);
449
450 mgr.cleanup_expired(t(10.0));
452
453 assert_eq!(mgr.reservation_count(), 2);
454
455 mgr.cleanup_expired(t(15.0));
457
458 assert_eq!(mgr.reservation_count(), 1);
459 }
460
461 #[test]
462 fn test_get_conflicts() {
463 let mut mgr = ReservationManager::new_enabled();
464
465 mgr.reserve(
466 ReservableResource::Edge(EdgeId(1)),
467 RobotId(1),
468 t(0.0),
469 t(10.0),
470 )
471 .unwrap();
472 mgr.reserve(
473 ReservableResource::Edge(EdgeId(1)),
474 RobotId(2),
475 t(15.0),
476 t(25.0),
477 )
478 .unwrap();
479
480 let edge1 = ReservableResource::Edge(EdgeId(1));
481
482 let conflicts = mgr.get_conflicts(&edge1, RobotId(3), t(5.0), t(12.0));
484 assert_eq!(conflicts.len(), 1);
485 assert_eq!(conflicts[0].robot_id, RobotId(1));
486
487 let conflicts = mgr.get_conflicts(&edge1, RobotId(3), t(5.0), t(20.0));
489 assert_eq!(conflicts.len(), 2);
490
491 let conflicts = mgr.get_conflicts(&edge1, RobotId(1), t(0.0), t(10.0));
493 assert_eq!(conflicts.len(), 0);
494 }
495
496 #[test]
497 fn test_node_reservations() {
498 let mut mgr = ReservationManager::new_enabled();
499
500 let node = ReservableResource::Node(NodeId(5));
501
502 mgr.reserve(node.clone(), RobotId(1), t(0.0), t(10.0))
503 .unwrap();
504
505 assert!(!mgr.can_reserve(&node, RobotId(2), t(5.0), t(15.0)));
507
508 assert!(mgr.can_reserve(&node, RobotId(2), t(10.0), t(20.0)));
510 }
511
512 #[test]
513 fn test_get_robot_reservations() {
514 let mut mgr = ReservationManager::new_enabled();
515
516 mgr.reserve(
517 ReservableResource::Edge(EdgeId(1)),
518 RobotId(1),
519 t(0.0),
520 t(10.0),
521 )
522 .unwrap();
523 mgr.reserve(
524 ReservableResource::Edge(EdgeId(2)),
525 RobotId(1),
526 t(10.0),
527 t(20.0),
528 )
529 .unwrap();
530 mgr.reserve(
531 ReservableResource::Edge(EdgeId(3)),
532 RobotId(2),
533 t(0.0),
534 t(10.0),
535 )
536 .unwrap();
537
538 let robot1_res = mgr.get_robot_reservations(RobotId(1));
539 assert_eq!(robot1_res.len(), 2);
540
541 let robot2_res = mgr.get_robot_reservations(RobotId(2));
542 assert_eq!(robot2_res.len(), 1);
543
544 let robot3_res = mgr.get_robot_reservations(RobotId(3));
545 assert_eq!(robot3_res.len(), 0);
546 }
547}