Question

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()
Was it helpful?

Solution 2

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()

OTHER TIPS

If you really can’t use a normal Resource with a capacity of 3, maybe Store would help you. The Store stores counter objects which you can request. Each counter has its own identity and can, for example, track its number of usages. After a customer is done, it has to put the counter back into the Store:

import simpy


class Counter:
    usages = 0


def customer(env, counters):
    counter = yield counters.get()
    yield env.timeout(1)
    counter.usages += 1
    yield counters.put(counter)


env = simpy.Environment()
counters = simpy.Store(env, capacity=3)
counters.items = [Counter() for i in range(counters.capacity)]
for i in range(10):
    env.process(customer(env, counters))
env.run()
for counter in counters.items:
    print(counter.usages)
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top