from vidigi.animation import animate_activity_log
from vidigi.utils import EventPosition, create_event_position_df
import pandas as pd
import os
import random
import plotly.io as pio
= "notebook" pio.renderers.default
Examples from Outside of Healthcare: Carwash
This is one of the examples provided in the simpy documentation, with vidigi logging added.
https://simpy.readthedocs.io/en/latest/examples/carwash.html
View Imported Code, which has had logging steps added at the appropriate points in the ‘model’ class
"""
Carwash example.
Covers:
- Waiting for other processes
- Resources: Resource
Scenario:
A carwash has a limited number of washing machines and defines
a washing processes that takes some (random) time.
Car processes arrive at the carwash at a random time. If one washing
machine is available, they start the washing process and wait for it
to finish. If not, they wait until they can use one.
"""
import itertools
import random
from vidigi.resources import VidigiStore
from vidigi.animation import animate_activity_log
from vidigi.logging import EventLogger
from vidigi.utils import EventPosition, create_event_position_df
import simpy
import pandas as pd
# fmt: off
= 42
RANDOM_SEED = 2 # Number of machines in the carwash
NUM_MACHINES = 5 # Minutes it takes to clean a car
WASHTIME = 2 # Create a car every ~2 minutes
T_INTER = 60*8 # Simulation time in minutes
SIM_TIME # fmt: on
class Carwash:
"""A carwash has a limited number of machines (``NUM_MACHINES``) to
clean cars in parallel.
Cars have to request one of the machines. When they got one, they
can start the washing processes and wait for it to finish (which
takes ``washtime`` minutes).
"""
def __init__(self, env, num_machines, washtime):
self.env = env
self.machine = VidigiStore(env, num_resources=num_machines)
self.washtime = washtime
self.logger = EventLogger(env=self.env)
def wash(self, car):
"""The washing processes. It takes a ``car`` processes and tries
to clean it."""
yield self.env.timeout(self.washtime)
= random.randint(50, 99)
pct_dirt print(f"Carwash removed {pct_dirt}% of {car}'s dirt.")
def car(env, name, cw):
"""The car process (each car has a ``name``) arrives at the carwash
(``cw``) and requests a cleaning machine.
It then starts the washing process, waits for it to finish and
leaves to never come back ...
"""
print(f'{name} arrives at the carwash at {env.now:.2f}.')
=name)
cw.logger.log_arrival(entity_id=name, event='carwash_queue_wait_begins')
cw.logger.log_queue(entity_idwith cw.machine.request() as request:
= yield request
carwash_spot
print(f'{name} enters the carwash at {env.now:.2f}.')
=name, event="carwashing_begins",
cw.logger.log_resource_use_start(entity_id=carwash_spot.id_attribute)
resource_id
yield env.process(cw.wash(name))
=name, event="carwashing_ends",
cw.logger.log_resource_use_end(entity_id=carwash_spot.id_attribute)
resource_id
print(f'{name} leaves the carwash at {env.now:.2f}.')
=name)
cw.logger.log_departure(entity_id
def setup(env, num_machines, washtime, t_inter, duration):
"""Create a carwash, a number of initial cars and keep creating cars
approx. every ``t_inter`` minutes."""
# Create the carwash
= Carwash(env, num_machines, washtime)
carwash
= itertools.count()
car_count
# Create 4 initial cars
for _ in range(4):
f'Car {next(car_count)}', carwash))
env.process(car(env,
# Create more cars while the simulation is running
while env.now < duration:
yield env.timeout(random.randint(t_inter - 2, t_inter + 2))
f'Car {next(car_count)}', carwash))
env.process(car(env,
# Allow remaining events to finish before returning
yield env.timeout(0)
f"logs_{NUM_MACHINES}_machines_{T_INTER}_IAT.csv")
carwash.logger.to_dataframe().to_csv(
# Setup and start the simulation
print('Carwash')
print('Check out http://youtu.be/fXXmeP9TvBg while simulating ... ;-)')
# This helps to reproduce the results
random.seed(RANDOM_SEED)
def run_model():
# Create an environment and start the setup process
= simpy.Environment()
env = env.process(setup(env, NUM_MACHINES, WASHTIME, T_INTER, SIM_TIME))
carwash_process # Execute!
=carwash_process)
env.run(until
run_model()
= 7
T_INTER
run_model()
# Define positions for animation
= create_event_position_df([
event_positions ='arrival', x=0, y=350, label="Entrance"),
EventPosition(event='carwash_queue_wait_begins', x=350, y=200, label="Queue"),
EventPosition(event='carwashing_begins', x=340, y=100, resource='num_carwashes',
EventPosition(event="Being Washed"),
label='depart', x=250, y=50, label="Exit")
EventPosition(event
])
class Params:
def __init__(self):
self.num_carwashes = 2
= [ "🚗", "🚙", "🚓",
icon_list "🚗", "🚙", "🏎️",
"🚗", "🚙", "🚚",
"🚗", "🚙", "🛻",
"🚗", "🚙", "🚛",
"🚗", "🚙", "🚕",
"🚗", "🚙", "🚒",
"🚗", "🚙", "🚑"]
random.shuffle(icon_list)
Note that this animation uses several new parameters introduced in v1.1.0 of vidigi.
# Create animation
def generate_carwash_animation(event_log_df):
return animate_activity_log(
=event_log_df,
event_log=event_positions,
event_position_df=Params(),
scenario=1,
every_x_time_units=800,
plotly_height=800,
plotly_width=400,
override_x_max=400,
override_y_max=60*8,
limit_duration=50,
entity_icon_size=50,
gap_between_entities=180,
gap_between_resources=False,
display_stage_labels=7,
wrap_queues_at=7*3,
step_snapshot_max=60,
gap_between_queue_rows=icon_list,
custom_entity_icon_list=0,
resource_opacity=False,
setup_mode="https://raw.githubusercontent.com/hsma-tools/vidigi/refs/heads/main/examples/example_14_carwash/carwash_bg.png",
add_background_image=1, # New parameter in 1.1.0
background_image_opacity="white", # New parameter in 1.1.0
overflow_text_color="09:00:00",
start_time="day_clock"
time_display_units )
Run with a short inter-arrival time to force queues to build up
# Display log
"logs_2_machines_2_IAT.csv")) generate_carwash_animation(pd.read_csv(
Run with the standard inter-arrival time from the simpy documentation
# Display log
"logs_2_machines_7_IAT.csv")) generate_carwash_animation(pd.read_csv(