I want to create a simulation model that simulates a bank with 3 number of counters. I want there to be one queue for customers, and if any one of the counters is available, it will service the counter for some amount of time. Each counter is its own simpy resource (I cant do one resource with a capacity of 3), and I need to know which counter serves the customer. I am having trouble implementing this.
I have found this article which seems to be about the same problem, but still don't know the exact implementation http://comments.gmane.org/gmane.comp.python.simpy.user/1754
I assume I need a yield simpy.AnyOf(env, list_of_resources) in there to request the first available counter, but I cant figure out how to set this up properly, and also if more than one resource becomes available at the same time, I want some way of checking this so that I can choose which counter is better for the customer to go to.
I am running python version 2.7, simpy version 3.0.4
EDIT Adding Code that show what Im trying to do and how it doesnt work right now.
import simpy
import random
LENGTH_SIM = 200.0
class Customer(object):
def __init__(self, arrive_time, num):
self.arrive_time = arrive_time
self.start_service_time = -1.0
self.finish_service_time = -1.0
self.served_by = -1.0
self.num = num
def print_attributes(self):
format_string = "#%3d Arrive: %6.1f Start Service: %6.1f "\
+ " End Service: %6.1f" + " Served By: %3d"
print format_string %(self.num, self.arrive_time,
self.start_service_time,
self.finish_service_time, self.served_by)
class Counter(object):
def __init__(self, number):
self.number = number
self.name = "Counter " + str(number)
self.start_times = []
self.end_times = []
class Bank(object):
def __init__(self, env, num_counters):
self.counters = [Counter(x+1) for x in range(num_counters)]
self.num_counters = num_counters
self.counters_resources = [simpy.Resource(env, capacity=1)
for x in range(num_counters)]
def generate_customers(env, customers):
count = 1
while 1:
wait_time = random.randint(8, 12)
yield env.timeout(wait_time)
customers.append(Customer(env.now, count))
print "Generated Customer: ", count
count += 1
def select_counter(env, bank):
'''choose a counter for the customer'''
for x in range(len(bank.counters)):
if bank.counters_resources[len(bank.counters) - 1 - x].count == 0:
print "Counter Selected: ", len(bank.counters) - 1 - x
print bank.counters[len(bank.counters) - 1 - x].number
print bank.counters_resources[len(bank.counters) - 1 - x].users
return len(bank.counters) - 1 - x
return -2.0
def gen_service_time():
return random.random() * 20.0 + 21.0
def handle_customer(env, bank, customer, customers, num_process):
'''handles customer'''
print "Process ", num_process, " started at:", env.now
counter = select_counter(env, bank)
print "Process ", num_process, " after counter select:", env.now
if counter != -2.0:
with bank.counters_resources[counter].request() as req:
yield req
print "Process ", num_process, "Time", env.now, "In if"
bank.counters[counter].start_times.append(env.now)
service_time = gen_service_time()
customer.start_service_time = env.now
customer.finish_service_time = env.now + service_time
bank.counters[counter].end_times.append(env.now + service_time)
customer.served_by = counter + 1
yield env.timeout(service_time)
else:
reqs = []
for x in range(len(bank.counters)):
reqs.append(bank.counters_resources[x].request())
counter_used = yield simpy.events.AnyOf(env, reqs)
for x in range(len(reqs)):
if counter_used.keys()[0] != reqs[x]:
print "True"
bank.counters_resources[x].release(reqs[x])
print "Process ", num_process, "Time", env.now, "In else"
bank.counters[0].start_times.append(env.now)
service_time = gen_service_time()
customer.start_service_time = env.now
customer.finish_service_time = env.now + service_time
bank.counters[0].end_times.append(env.now + service_time)
customer.served_by = 1
yield env.timeout(service_time)
for x in range(len(reqs)):
## if counter_used.keys()[0] == reqs[x]:
if 1:
bank.counters_resources[x].release(reqs[x])
def run_bank(env, bank, customers):
while 1:
min_time = -1.0
min_customer = -1.0
for x in xrange(len(customers)):
if customers[x].arrive_time < min_time or min_time == -1.0:
if customers[x].arrive_time > env.now:
min_time = customers[x].arrive_time
min_customer = x
else:
continue
if min_time == -1.0:
yield env.timeout(LENGTH_SIM - env.now)
yield env.timeout(min_time - env.now)
print "Calling Process: ", min_customer, "At time:", env.now
env.process(handle_customer(env,bank,customers[min_customer],customers,
min_customer + 1))
def run_sim():
env = simpy.Environment()
customers = []
env.process(generate_customers(env, customers))
env.run(until=LENGTH_SIM)
env2 = simpy.Environment()
bank = Bank(env2, 3)
env2.process(run_bank(env2, bank, customers))
env2.run(until = LENGTH_SIM)
for x in range(len(customers)):
customers[x].print_attributes()
if __name__ == '__main__':
run_sim()
I managed to solve it myself, the only thing I don't understand is what the different arguments to the cancel method do (I just set them all as None and it works, I'm not sure where there is documentation on what they are besides their name). The fixed code is as follows (the relevant part being the else clause in the handle_customer function:
import simpy
import random
LENGTH_SIM = 100.0
class Customer(object):
def __init__(self, arrive_time, num):
self.arrive_time = arrive_time
self.start_service_time = -1.0
self.finish_service_time = -1.0
self.served_by = -1.0
self.num = num
def print_attributes(self):
format_string = "#%3d Arrive: %6.1f Start Service: %6.1f "\
+ " End Service: %6.1f" + " Served By: %3d"
print format_string %(self.num, self.arrive_time,
self.start_service_time,
self.finish_service_time, self.served_by)
class Counter(object):
def __init__(self, number):
self.number = number
self.name = "Counter " + str(number)
self.start_times = []
self.end_times = []
class Bank(object):
def __init__(self, env, num_counters):
self.counters = [Counter(x+1) for x in range(num_counters)]
self.num_counters = num_counters
self.counters_resources = [simpy.Resource(env, capacity=1)
for x in range(num_counters)]
def generate_customers(env, customers):
count = 1
while 1:
## wait_time = random.randint(8, 12)
wait_time = 2
yield env.timeout(wait_time)
customers.append(Customer(env.now, count))
print "Generated Customer: ", count
count += 1
def select_counter(env, bank):
'''choose a counter for the customer'''
for x in range(len(bank.counters)):
if bank.counters_resources[len(bank.counters) - 1 - x].count == 0 and\
bank.counters_resources[len(bank.counters) - 1 - x].queue == []:
print "Counter Selected: ", len(bank.counters) - 1 - x
return len(bank.counters) - 1 - x
return -2.0
def gen_service_time():
## return random.random() * 20.0 + 19.0
return random.randint(4, 8)
def wait_til_available(env, bank, temp_list):
while 1:
yield env.timeout(0.0001)
for x in range(len(bank.counters)):
if bank.counters_resources[x].count == 0:
req = bank.counters_resources[x].request()
temp_list[0] = x
temp_list[1] = req
return
def handle_customer(env, bank, customer, customers, num_process):
'''handles customer'''
print "Process ", num_process, " started at:", env.now
counter = select_counter(env, bank)
if counter != -2.0:
with bank.counters_resources[counter].request() as req:
yield req
bank.counters[counter].start_times.append(env.now)
service_time = gen_service_time()
customer.start_service_time = env.now
customer.finish_service_time = env.now + service_time
bank.counters[counter].end_times.append(env.now + service_time)
customer.served_by = counter + 1
yield env.timeout(service_time)
else:
reqs = []
got_counter = 0
for x in range(len(bank.counters)):
reqs.append(bank.counters_resources[x].request())
good_req = yield simpy.events.AnyOf(env, reqs)
req = good_req.keys()[0]
for x in range(len(bank.counters)):
if req != reqs[x]:
bank.counters_resources[x].release(reqs[x])
reqs[x].cancel(None, None, None)
else:
got_counter = x
bank.counters[got_counter].start_times.append(env.now)
service_time = gen_service_time()
customer.start_service_time = env.now
customer.finish_service_time = env.now + service_time
bank.counters[got_counter].end_times.append(env.now + service_time)
customer.served_by = got_counter + 1
yield env.timeout(service_time)
print "Process ", num_process, " finished at:", env.now
bank.counters_resources[got_counter].release(req)
def run_bank(env, bank, customers):
while 1:
min_time = -1.0
min_customer = -1.0
for x in xrange(len(customers)):
if customers[x].arrive_time < min_time or min_time == -1.0:
if customers[x].arrive_time > env.now:
min_time = customers[x].arrive_time
min_customer = x
else:
continue
if min_time == -1.0:
yield env.timeout(LENGTH_SIM - env.now)
yield env.timeout(min_time - env.now)
print "Calling Process: ", min_customer+1, "At time:", env.now
env.process(handle_customer(env,bank,customers[min_customer],customers,
min_customer + 1))
def run_sim():
env = simpy.Environment()
customers = []
env.process(generate_customers(env, customers))
env.run(until=LENGTH_SIM)
env2 = simpy.Environment()
bank = Bank(env2, 3)
env2.process(run_bank(env2, bank, customers))
env2.run(until = LENGTH_SIM)
for x in range(len(customers)):
customers[x].print_attributes()
if __name__ == '__main__':
run_sim()