1use crate::{
4 BinId, ChargingStationId, EdgeId, EventId, MaintenanceStationId, NodeId, OrderId, RobotId,
5 ShipmentId, SimTime, SkuId, StationId, TaskId,
6};
7use rkyv::{Archive, Deserialize, Serialize};
8use std::cmp::Ordering;
9
10#[derive(Archive, Deserialize, Serialize, Clone, Debug)]
12pub enum SimEvent {
13 OrderArrival { order_id: OrderId },
15
16 TaskAssignment { task_id: TaskId, robot_id: RobotId },
18
19 RobotDepartNode {
21 robot_id: RobotId,
22 from_node: NodeId,
23 to_node: NodeId,
24 edge_id: EdgeId,
25 },
26
27 RobotArriveNode {
29 robot_id: RobotId,
30 node_id: NodeId,
31 from_node: NodeId,
32 },
33
34 StationServiceStart {
36 robot_id: RobotId,
37 station_id: StationId,
38 task_id: TaskId,
39 },
40
41 StationServiceEnd {
43 robot_id: RobotId,
44 station_id: StationId,
45 task_id: TaskId,
46 },
47
48 InventoryUpdate {
50 sku_id: SkuId,
51 bin_id: BinId,
52 delta: i32,
53 task_id: TaskId,
54 },
55
56 RobotWaitStart {
58 robot_id: RobotId,
59 at_node: NodeId,
60 waiting_for_edge: EdgeId,
61 },
62
63 RobotWaitEnd { robot_id: RobotId, at_node: NodeId },
65
66 RobotPickup {
68 robot_id: RobotId,
69 task_id: TaskId,
70 node_id: NodeId,
71 },
72
73 DispatchTasks,
75
76 InboundArrival {
79 shipment_id: ShipmentId,
80 station_id: StationId,
81 },
82
83 PutawayTaskCreated {
85 task_id: TaskId,
86 shipment_id: ShipmentId,
87 },
88
89 OutboundReady { order_id: OrderId },
91
92 ShipmentDeparture {
94 shipment_id: ShipmentId,
95 station_id: StationId,
96 },
97
98 ReplenishmentTrigger {
100 sku_id: SkuId,
101 bin_id: BinId,
102 current_qty: u32,
103 threshold: u32,
104 },
105
106 RobotChargingStart {
109 robot_id: RobotId,
110 station_id: ChargingStationId,
111 },
112
113 RobotChargingEnd {
115 robot_id: RobotId,
116 station_id: ChargingStationId,
117 energy_charged_wh: f64,
118 },
119
120 RobotLowBattery { robot_id: RobotId, soc: f64 },
122
123 MetricsSampleTick,
126
127 DeadlockDetected {
130 robots: Vec<RobotId>,
132 },
133
134 DeadlockResolved {
136 robots: Vec<RobotId>,
138 resolver_robot: RobotId,
140 },
141
142 RobotFailure {
145 robot_id: RobotId,
146 interrupted_task: Option<TaskId>,
148 },
149
150 RobotMaintenanceDue {
152 robot_id: RobotId,
153 operating_hours: f64,
155 },
156
157 MaintenanceStart {
159 robot_id: RobotId,
160 station_id: MaintenanceStationId,
161 is_repair: bool,
163 },
164
165 MaintenanceEnd {
167 robot_id: RobotId,
168 station_id: MaintenanceStationId,
169 is_repair: bool,
171 duration_s: f64,
173 },
174}
175
176impl SimEvent {
177 pub fn event_type_name(&self) -> &'static str {
179 match self {
180 SimEvent::OrderArrival { .. } => "order_arrival",
181 SimEvent::TaskAssignment { .. } => "task_assignment",
182 SimEvent::RobotDepartNode { .. } => "robot_depart_node",
183 SimEvent::RobotArriveNode { .. } => "robot_arrive_node",
184 SimEvent::StationServiceStart { .. } => "station_service_start",
185 SimEvent::StationServiceEnd { .. } => "station_service_end",
186 SimEvent::InventoryUpdate { .. } => "inventory_update",
187 SimEvent::RobotWaitStart { .. } => "robot_wait_start",
188 SimEvent::RobotWaitEnd { .. } => "robot_wait_end",
189 SimEvent::RobotPickup { .. } => "robot_pickup",
190 SimEvent::DispatchTasks => "dispatch_tasks",
191 SimEvent::InboundArrival { .. } => "inbound_arrival",
193 SimEvent::PutawayTaskCreated { .. } => "putaway_task_created",
194 SimEvent::OutboundReady { .. } => "outbound_ready",
195 SimEvent::ShipmentDeparture { .. } => "shipment_departure",
196 SimEvent::ReplenishmentTrigger { .. } => "replenishment_trigger",
197 SimEvent::RobotChargingStart { .. } => "robot_charging_start",
199 SimEvent::RobotChargingEnd { .. } => "robot_charging_end",
200 SimEvent::RobotLowBattery { .. } => "robot_low_battery",
201 SimEvent::MetricsSampleTick => "metrics_sample_tick",
203 SimEvent::DeadlockDetected { .. } => "deadlock_detected",
205 SimEvent::DeadlockResolved { .. } => "deadlock_resolved",
206 SimEvent::RobotFailure { .. } => "robot_failure",
208 SimEvent::RobotMaintenanceDue { .. } => "robot_maintenance_due",
209 SimEvent::MaintenanceStart { .. } => "maintenance_start",
210 SimEvent::MaintenanceEnd { .. } => "maintenance_end",
211 }
212 }
213
214 pub fn robot_id(&self) -> Option<RobotId> {
216 match self {
217 SimEvent::TaskAssignment { robot_id, .. } => Some(*robot_id),
218 SimEvent::RobotDepartNode { robot_id, .. } => Some(*robot_id),
219 SimEvent::RobotArriveNode { robot_id, .. } => Some(*robot_id),
220 SimEvent::StationServiceStart { robot_id, .. } => Some(*robot_id),
221 SimEvent::StationServiceEnd { robot_id, .. } => Some(*robot_id),
222 SimEvent::RobotWaitStart { robot_id, .. } => Some(*robot_id),
223 SimEvent::RobotWaitEnd { robot_id, .. } => Some(*robot_id),
224 SimEvent::RobotPickup { robot_id, .. } => Some(*robot_id),
225 SimEvent::RobotChargingStart { robot_id, .. } => Some(*robot_id),
226 SimEvent::RobotChargingEnd { robot_id, .. } => Some(*robot_id),
227 SimEvent::RobotLowBattery { robot_id, .. } => Some(*robot_id),
228 SimEvent::DeadlockResolved { resolver_robot, .. } => Some(*resolver_robot),
229 SimEvent::RobotFailure { robot_id, .. } => Some(*robot_id),
230 SimEvent::RobotMaintenanceDue { robot_id, .. } => Some(*robot_id),
231 SimEvent::MaintenanceStart { robot_id, .. } => Some(*robot_id),
232 SimEvent::MaintenanceEnd { robot_id, .. } => Some(*robot_id),
233 _ => None,
234 }
235 }
236
237 pub fn task_id(&self) -> Option<TaskId> {
239 match self {
240 SimEvent::TaskAssignment { task_id, .. } => Some(*task_id),
241 SimEvent::StationServiceStart { task_id, .. } => Some(*task_id),
242 SimEvent::StationServiceEnd { task_id, .. } => Some(*task_id),
243 SimEvent::InventoryUpdate { task_id, .. } => Some(*task_id),
244 SimEvent::RobotPickup { task_id, .. } => Some(*task_id),
245 SimEvent::PutawayTaskCreated { task_id, .. } => Some(*task_id),
246 _ => None,
247 }
248 }
249
250 pub fn shipment_id(&self) -> Option<ShipmentId> {
252 match self {
253 SimEvent::InboundArrival { shipment_id, .. } => Some(*shipment_id),
254 SimEvent::PutawayTaskCreated { shipment_id, .. } => Some(*shipment_id),
255 SimEvent::ShipmentDeparture { shipment_id, .. } => Some(*shipment_id),
256 _ => None,
257 }
258 }
259
260 pub fn charging_station_id(&self) -> Option<ChargingStationId> {
262 match self {
263 SimEvent::RobotChargingStart { station_id, .. } => Some(*station_id),
264 SimEvent::RobotChargingEnd { station_id, .. } => Some(*station_id),
265 _ => None,
266 }
267 }
268
269 pub fn maintenance_station_id(&self) -> Option<MaintenanceStationId> {
271 match self {
272 SimEvent::MaintenanceStart { station_id, .. } => Some(*station_id),
273 SimEvent::MaintenanceEnd { station_id, .. } => Some(*station_id),
274 _ => None,
275 }
276 }
277}
278
279#[derive(Archive, Deserialize, Serialize, Clone, Debug)]
281pub struct ScheduledEvent {
282 pub id: EventId,
283 pub time: SimTime,
284 pub event: SimEvent,
285}
286
287impl ScheduledEvent {
288 pub fn new(id: EventId, time: SimTime, event: SimEvent) -> Self {
290 Self { id, time, event }
291 }
292}
293
294impl PartialEq for ScheduledEvent {
295 fn eq(&self, other: &Self) -> bool {
296 self.id == other.id
297 }
298}
299
300impl Eq for ScheduledEvent {}
301
302impl PartialOrd for ScheduledEvent {
303 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
304 Some(self.cmp(other))
305 }
306}
307
308impl Ord for ScheduledEvent {
309 fn cmp(&self, other: &Self) -> Ordering {
310 match other.time.0.partial_cmp(&self.time.0) {
313 Some(Ordering::Equal) | None => other.id.0.cmp(&self.id.0),
314 Some(ord) => ord,
315 }
316 }
317}
318
319#[cfg(test)]
320mod tests {
321 use super::*;
322
323 #[test]
324 fn test_event_ordering() {
325 let e1 = ScheduledEvent::new(
326 EventId(1),
327 SimTime::from_seconds(10.0),
328 SimEvent::DispatchTasks,
329 );
330 let e2 = ScheduledEvent::new(
331 EventId(2),
332 SimTime::from_seconds(5.0),
333 SimEvent::DispatchTasks,
334 );
335
336 assert!(e2 > e1);
338 }
339
340 #[test]
341 fn test_event_type_name() {
342 let event = SimEvent::OrderArrival {
343 order_id: OrderId(1),
344 };
345 assert_eq!(event.event_type_name(), "order_arrival");
346 }
347}