Search code examples
simulationsimpy

Queue Simulation of sharing the server


I'm dealing with a queue simulation related to a Cafe counter. This counter has 6 chairs, thus serving at most 6 people at the same time. Customers will arrive in parties. The number of customers in each party can be equal to 1, 2, 3, 4, 5, or 6 with the same probability. The amount of time each party spends in the restaurant after being seated is equal to 0.5 units of time. Assume that multiple parties may share the counter. The order of service is first come first serve. Assume the next party has a size of x. The party will be seated when at least x empty seats become available. The restaurant will finish serving all parties that arrive before t=12 and then close. The parties that arrive after t=12 will be turned away. The below are the arrival times for each party.

arrival_times=np.array([ 0.0473792 ,  1.06007777,  2.02933004,  2.37675438,  2.69755975,
    3.50251282,  5.98208415,  6.14630716,  7.3503128 ,  7.60377882,
    8.22431782,  8.5749094 ,  8.98564659,  9.12636855,  9.75145154,
   11.01328947])

My original code looks like this way:

import numpy as np
import simpy

def arrival(arrival_times):
    i=0
    inter_arrivals = arrival_times
    for inter_arrival in inter_arrivals:
        yield env.timeout(inter_arrival)
        i+=1

        Outcomes["arrival"].append(env.now)
        env.process(service(i))

def service(i):

    #requesting the front desk

    rqt = resource.request()
    yield rqt
    service_time = 0.5
    yield env.timeout(service_time)
    resource.release(rqt)

    Outcomes["depart"].append(env.now)
    Outcomes["id"].append(i)
    Outcomes["customers"].append(np.np.random.randint(1,7))

Outcomes = {"arrival":[],"depart":[],"id":[],"customers":[]}
env = simpy.Environment()
resource = simpy.Resource(env)
env.process(arrival(arrival_times))
T = 12
env.run(until=T)

arrival_n = np.array(Outcomes["arrival"])
depart_n = np.array(Outcomes["depart"])
id_n = np.array(Outcomes["id"])
customer_n = np.array(Outcomes["customers"])

But my code requires that only one party can occupy the counter. And another party can get service after them. Could someone explain how to simulate the queue such that different parties can share the counter? Thank you very much. I see there is a function called Simpy.AllOf(), which might be useful and will process events when all of the events in the list have been triggered. But I still have no idea.


Solution

  • looks like you have a resource pool for the counter. You need a resource pool for the 6 counter stools. Everyone in the party needs to make a resource request for a stool and when everyone in the party has a stool, then they can be seated and served. After the party is severed, then each person will need to release their seized stool. Note there is a simpy.events.AllOf to make one yield for many events

    Here is my quick code example

    """
    Serving parties at a cafe couter
    
    parties are seated fifo
    there must be enough available seats to seat the 
    entire party befor the party is dequeued and seated
    
    more then one party can be at the cafe counter at a time
    
    Programmer: Michael R. Gibbs
    """
    
    import simpy
    import random
    
    def gen_arrivals(env, arrival_sched, partyQ, seats):
        """
        use a arrival schedule to create parties of size 1 to 6
        and put them in to a wait queue
        """
    
        i = 0
        for next_arrive in arrival_sched:
            gap = next_arrive - env.now
            yield env.timeout(gap)
    
            # next arrial time has been reacked
            # create a party
            i+= 1
            party = {
                "party_id": i,
                "arrive_time": env.now,
                "party_size": random.randrange(1,6)
            }
    
            # put party in queue
            yield partyQ.put(party)
            print(f"{env.now:06.3f}",f"party {party['party_id']} has arrived and needs {party['party_size']} seats, {6 -seats.count} seats avaliable")
    
    def seat_party(env, partyQ, counter_seats):
        """
        process the seatting queue, FIFO
        next party will block until there 
        are enough seats for the entire party, 
        even if a latter party
        can fit at the counter
        """
    
        while True:
            party = yield partyQ.get()
    
            # get seat request for each person in party
            seat_requests = []
            for _ in range(party['party_size']):
                rqst = counter_seats.request()
                seat_requests.append(rqst)
            
            # save the seats so we can release them lator
            party['seat_requests'] = seat_requests
    
            # yield until ALL the requests have been filled
            # since we are doing one party at a time, there
            # is not deadlock contention for seats between the parties
            yield simpy.events.AllOf(env, seat_requests)
    
            print(f"{env.now:06.3f}",f"party {party['party_id']} has been seated taking {party['party_size']} seats, {6 -counter_seats.count} seats avaliable")
    
            # once seated serve async so we can immediatly start
            # seating the next party, in case there still enough
            # seats to seat the next party imediatly
            env.process(serve_party(env, party, counter_seats))
    
    def serve_party(env, party, counter_seats):
        """
        serve the party then release the seats
        back to the counter seat resource pool
        """
    
        yield env.timeout(0.5)
    
        for seat_request in party['seat_requests']:
            yield counter_seats.release(seat_request)
    
        # and the party leaves
        print(f"{env.now:06.3f}",f"party {party['party_id']} has been served relasing {party['party_size']} seats, {6 -counter_seats.count} seats avaliable")
    
    """ schedule of arrial times for parties """
    arrival_times=[ 0.0473792 ,  1.06007777,  2.02933004,  2.37675438,  2.69755975,
        3.50251282,  5.98208415,  6.14630716,  7.3503128 ,  7.60377882,
        8.22431782,  8.5749094 ,  8.98564659,  9.12636855,  9.75145154,
       11.01328947]
    
    # start simulation and kick off arrival generation, and queue processing
    env = simpy.Environment()
    counter_seats = simpy.Resource(env, capacity=6)
    partyQ = simpy.Store(env,99)
    env.process(gen_arrivals(env, arrival_times, partyQ, counter_seats))
    env.process(seat_party(env, partyQ,counter_seats))
    
    env.run(24)