Skip to content

Commit 6e8ff3e

Browse files
committed
Add priority queueing animation
1 parent 94be99c commit 6e8ff3e

2 files changed

Lines changed: 166 additions & 0 deletions

File tree

Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
from vidigi.logging import EventLogger
2+
from vidigi.utils import EventPosition, create_event_position_df
3+
from vidigi.animation import animate_activity_log, generate_animation
4+
from vidigi.prep import generate_animation_df, reshape_for_animations
5+
from vidigi.resources import VidigiPriorityStore
6+
import simpy
7+
import numpy as np
8+
9+
class g:
10+
n_cubicles = 1
11+
12+
class Patient:
13+
def __init__(self, p_id, priority):
14+
self.id = p_id
15+
self.priority = priority
16+
17+
class SimpleActivityModel:
18+
def __init__(self, master_seed=42):
19+
self.env = simpy.Environment()
20+
self.patient_counter = 0
21+
self.patient_inter = 3
22+
self.logger = EventLogger(env=self.env)
23+
24+
# Seed setup using numpy's SeedSequence
25+
self.master_seed = master_seed
26+
self.seed_seq = np.random.SeedSequence(master_seed)
27+
self.rng = np.random.default_rng(self.seed_seq)
28+
29+
self.cubicles = VidigiPriorityStore(self.env, num_resources=g.n_cubicles)
30+
31+
def generate_arrivals(self):
32+
while True:
33+
self.patient_counter += 1
34+
if self.rng.uniform(low=0, high=1) < 0.35:
35+
p = Patient(self.patient_counter, priority=1) # high priority
36+
else:
37+
p = Patient(self.patient_counter, priority=2) # others
38+
39+
self.logger.log_arrival(entity_id=p.id, priority=p.priority)
40+
41+
self.env.process(self.patient_journey(p))
42+
sampled_inter = self.rng.exponential(scale=self.patient_inter)
43+
yield self.env.timeout(sampled_inter)
44+
45+
def patient_journey(self, patient):
46+
self.logger.log_queue(entity_id=patient.id, event="wait_here", priority=patient.priority)
47+
48+
with self.cubicles.request(priority=patient.priority) as req:
49+
cubicle = yield req
50+
51+
self.logger.log_resource_use_start(
52+
entity_id=patient.id,
53+
event="start_treatment",
54+
resource_id=cubicle.id_attribute,
55+
priority=patient.priority
56+
)
57+
58+
yield self.env.timeout(self.rng.uniform(low=1, high=10))
59+
60+
self.logger.log_resource_use_end(
61+
entity_id=patient.id,
62+
event="end_treatment",
63+
resource_id=cubicle.id_attribute,
64+
priority=patient.priority
65+
)
66+
67+
self.logger.log_departure(entity_id=patient.id, priority=patient.priority)
68+
69+
def run(self):
70+
self.env.process(self.generate_arrivals())
71+
self.env.run(until=60)
72+
73+
model = SimpleActivityModel()
74+
model.run()
75+
event_log = model.logger.to_dataframe()
76+
# event_log.to_csv("test_log.csv")
77+
78+
event_position_df = create_event_position_df([
79+
EventPosition(event="wait_here", x=150 , y=150 , label="Wait Here!"),
80+
EventPosition(event="start_treatment", x=150 , y=75 , resource="n_cubicles", label="Be Treated"),
81+
EventPosition(event="depart", x=150, y=5, label="Exit")
82+
])
83+
84+
animation_df = generate_animation_df(
85+
full_entity_df=reshape_for_animations(
86+
event_log=event_log,
87+
every_x_time_units=1,
88+
limit_duration=60,
89+
step_snapshot_max=100
90+
),
91+
event_position_df=event_position_df,
92+
gap_between_entities=20,
93+
wrap_queues_at=5,
94+
gap_between_queue_rows=30
95+
)
96+
97+
def show_priority_icon(row):
98+
if "more" not in row["icon"]:
99+
if row["priority"] == 1:
100+
return "🚨"
101+
else:
102+
return row["icon"]
103+
else:
104+
return row["icon"]
105+
106+
animation_df = animation_df.assign(
107+
icon=animation_df.apply(show_priority_icon, axis=1)
108+
)
109+
110+
generate_animation(
111+
animation_df,
112+
event_position_df,
113+
scenario=g(),
114+
entity_icon_size=40,
115+
override_x_max=300,
116+
override_y_max=250,
117+
plotly_height=600,
118+
plotly_width=1100,
119+
).update_layout(
120+
plot_bgcolor='white',
121+
)
122+
123+
124+
# animate_activity_log(
125+
# event_log = event_log,
126+
# event_position_df = event_position_df,
127+
# scenario=g(),
128+
# every_x_time_units=1,
129+
# limit_duration=60,
130+
# override_x_max=300,
131+
# override_y_max=250,
132+
# plotly_height=600,
133+
# plotly_width=1100,
134+
# display_stage_labels=True,
135+
# gap_between_entities=20,
136+
# entity_icon_size=40,
137+
# wrap_queues_at=10,
138+
# gap_between_resources=30,
139+
# gap_between_queue_rows=30,
140+
# resource_icon_size=80,
141+
# simulation_time_unit="minutes",
142+
# custom_resource_icon="☐",
143+
# resource_opacity=0.7,
144+
# debug_write_intermediate_objects=True
145+
# ).update_layout(
146+
# plot_bgcolor='white',
147+
# )

lambda_des_presentation_part_2.qmd

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,18 +150,37 @@ And we might have multiple generators for different kinds of patients, if they a
150150
{{< include animation_examples/several_resource.py >}}
151151
```
152152

153+
153154
## FIFO Queues
154155

156+
In the queues we've seen so far, the sooner you arrive, the sooner you are seen.
157+
158+
```{python}
159+
#| echo: false
160+
{{< include animation_examples/single_resource.py >}}
161+
```
155162

156163
## Priority-based Queue
157164

165+
But it's also possible to have priority-based queueing.
166+
167+
```{python}
168+
#| echo: false
169+
{{< include animation_examples/single_resource_priority.py >}}
170+
```
158171

159172
## Reneging
160173

161174

162175
## Balking
163176

164177

178+
## Multiple Resource Pools
179+
180+
181+
## Multiple Resource Pools
182+
183+
165184
## Jockeying
166185

167186

0 commit comments

Comments
 (0)