|
| 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 | +# ) |
0 commit comments