sim_restaurant/
staff.rs

1// Copyright (c) 2026 Graphcore Ltd. All rights reserved.
2
3use 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}