1use std::fmt;
4use std::rc::Rc;
5
6use gwr_engine::engine::Engine;
7use gwr_engine::events::any_of::AnyOf;
8use gwr_engine::time::clock::Clock;
9use gwr_engine::traits::Event;
10
11use crate::config::RestaurantConfig;
12use crate::customer::CustomerOutcome;
13use crate::menu::ORDERS;
14use crate::sim::Restaurant;
15
16#[derive(Clone, Copy, Debug)]
17pub struct Staffing {
18 pub till: usize,
19 pub kitchen: usize,
20}
21
22impl fmt::Display for Staffing {
23 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
24 write!(f, "till {} / kitchen {}", self.till, self.kitchen)
25 }
26}
27
28pub fn spawn_till_worker(
29 engine: &Engine,
30 clock: &Clock,
31 config: RestaurantConfig,
32 worker_id: usize,
33 restaurant: Rc<Restaurant>,
34) {
35 let clock = clock.clone();
36 engine.spawn(async move {
37 loop {
38 if clock.tick_now().tick() >= config.day_ticks {
39 break;
40 }
41
42 if let Some(customer_id) = restaurant.pop_till() {
43 let customer = restaurant.customer(customer_id);
44 let till_start = restaurant.tick_now();
45 customer.mark_till_started(till_start);
46 restaurant.begin_till_service(
47 customer_id,
48 customer.joined_queue_tick(),
49 worker_id,
50 customer.order_index(),
51 );
52
53 let order_ticks = config.move_to_till_ticks
54 + config.order_overhead_ticks
55 + config.payment_ticks
56 + order_ordering_ticks(customer.order_index());
57 clock.wait_ticks(order_ticks).await;
58
59 let payment_tick = restaurant.tick_now();
60 customer.mark_payment_done(payment_tick);
61 restaurant.record_order_started(customer_id, worker_id);
62 restaurant.enqueue_kitchen(customer_id).await?;
63 restaurant.finish_till_service(customer_id, worker_id);
64 continue;
65 }
66
67 if restaurant.arrivals_complete.get() && restaurant.till_queue.is_empty() {
68 break;
69 }
70
71 AnyOf::new(vec![
72 Box::new(restaurant.till_queue_changed()),
73 Box::new(restaurant.closed.clone()),
74 ])
75 .listen()
76 .await;
77 }
78 Ok(())
79 });
80}
81
82pub fn spawn_kitchen_worker(
83 engine: &Engine,
84 clock: &Clock,
85 config: RestaurantConfig,
86 worker_id: usize,
87 restaurant: Rc<Restaurant>,
88) {
89 let clock = clock.clone();
90 engine.spawn(async move {
91 loop {
92 if let Some(customer_id) = restaurant.pop_kitchen() {
93 let customer = restaurant.customer(customer_id);
94 restaurant.begin_kitchen_service(customer_id, worker_id, customer.order_index());
95
96 let prep_ticks = config.pack_order_ticks + order_prep_ticks(customer.order_index());
97 clock.wait_ticks(prep_ticks).await;
98
99 let ready_tick = restaurant.tick_now();
100 customer.mark_food_ready(ready_tick);
101
102 restaurant.record_order_served(
103 customer_id,
104 worker_id,
105 customer.payment_done_tick(),
106 );
107
108 customer.notify_outcome(CustomerOutcome::Served);
109 restaurant.finish_kitchen_service(customer_id, worker_id);
110 continue;
111 }
112
113 if restaurant.can_kitchen_exit() {
114 break;
115 }
116
117 if restaurant.closed_seen.get() {
118 restaurant.kitchen_queue_changed().listen().await;
119 } else {
120 AnyOf::new(vec![
121 Box::new(restaurant.kitchen_queue_changed()),
122 Box::new(restaurant.closed.clone()),
123 ])
124 .listen()
125 .await;
126 }
127 }
128 Ok(())
129 });
130}
131
132fn order_ordering_ticks(order_index: usize) -> u64 {
133 ORDERS[order_index]
134 .items
135 .iter()
136 .map(|line| line.item.order_ticks() * line.count as u64)
137 .sum()
138}
139
140fn order_prep_ticks(order_index: usize) -> u64 {
141 ORDERS[order_index]
142 .items
143 .iter()
144 .map(|line| line.item.prep_ticks() * line.count as u64)
145 .sum()
146}