Cindy Philip
Cindy Philip

Reputation: 37

Simpy Simulation of queue and interruption

I'm trying to use PreemptiveResource of Simpy to simulate one queue. The server repeats the following cycle. During each cycle, the server is functional for 0.09 units of time, the server will shut down for 0.01 units of time. If the server is currently serving a customer when the server is shut down, the customer will immediately leave. When the service resumes, the next customer in line will be served. But my output seems that the customer will not leave after the interruption. Could someone explain how to fix this problem? Thank you very much.

    import numpy as np
    import simpy

    def arrival(lmbda):
    i=0
    while True:
        inter_arrival=-1/lmbda*np.log(np.random.rand())
        yield env.timeout(inter_arrival)
        i+=1
    s1=env.process(service(i))
    env.process(shutdown(i,s1))
    print(i,"arrival", env.now)

    def shutdown(i,s1):
    while True:

        #########the server is functional for 0.09 units of time

        yield env.timeout(0.09)

        #########if customer still in the queue

        if rqt_list[i-1].processed==False:
            s1.interrupt()   
        else:
            return

    def service(i ):

        ###########requesting the server

        rqt=desk.request()
        rqt_list.append(rqt)
        print(i, "request", env.now)

        while True:
            try:
                yield rqt
                break
            except simpy.Interrupt:

                #########leave [delete request from the queue]

                rqt.cancel()
                print(i, "server shuts down", env.now)

                #########the server will shut down for 0.01 units of time

                yield env.timeout(0.01)

                #return, generate a new request

                rqt=desk.request()
                rqt_list[i-1]=rqt
        print(i,  "start the service", env.now)
    
        yield env.timeout(0.2)
        print(i, "end the service", env.now)
        desk.release(rqt)

    env=simpy.Environment()
    env.process(arrival(lmbda=7))
    rqt_list=[]
    desk=simpy.PreemptiveResource(env)
    T=1
    env.run(until=T)

Upvotes: 0

Views: 789

Answers (1)

Michael
Michael

Reputation: 1914

I did not use interrupts. Instead I used a event to signal when the server's state becomes inactive. The service delay then yields on a timeout for the service time or the server state change event, which ever comes first. I think this makes the customer cleaner in the customer does not need any try / except. The customer can still check the server's state if it needs to do special stuff if the service was cut short because the server became inactive.

here is the code:

    """
    example of a server with breaks
    
    programmer: Michael R. Gibbs
    """
    
    import simpy
    import numpy as np 
    
    class Server():
        """
        Provides service to customers, but the service ends if the server goes on break
        """
    
        def __init__(self, env, serverId):
            self.env = env
            self.serverId = serverId
            self.state = "Active"               # is ther server Active or Inactive
            self.stateChange = self.env.event() # event to yeild on for when server state changes (go on/off break)
    
            self.activeTime = 0.9
            self.inactiveTime = 0.1
    
            self.serviceTime = 0.2
    
            # start the server
            env.process(self.lifeLoop())
    
        def lifeLoop(self):
            """
            Manages that state of the server (Active or Inactive)
            also uses a event to braodcast when the state changes
            """
    
            while True:
                # active state
                yield env.timeout(self.activeTime)
                print(self.env.now, f"server {self.serverId} is becoming inactive")
                self.state = "Inactive"
    
                # use event to braodcast state has change
                oldEvent = self.stateChange
                self.stateChange = self.env.event()
                oldEvent.succeed()
    
                # inactive state
                yield env.timeout(0.1)
                print(self.env.now, f"server {self.serverId} is becoming active")
                self.state = "Active"
    
                # use event to bradcast state has changed
                oldEvent = self.stateChange
                self.stateChange = self.env.event()
                oldEvent.succeed()
    
        def service(self):
            """
            The service delay
            ends when service time is up, or if the server becomes inactive
            """
            yield env.any_of([self.stateChange, env.timeout(self.serviceTime)])
    
    
    class ServiceDesk():
        """
        Manages the queue for getting a server
        """
    
        def __init__(self, env, serverCnt=2):
            self.env = env
            self.serverQ = simpy.Store(env, capacity=serverCnt)
    
            # create the servers and add to the queue
            for i in range(serverCnt):
                server = Server(env,i+1)
                self.serverQ.put(server)
    
        def getServer(self):
            """
            Gets a server
            
            Servers can become inactive waitting in the queue
            only return "Active" servers
            """
    
            server = None
    
            # keep searching the queue until a "Active" server is found
            while server is None:
                server = yield self.serverQ.get()
                
                if server.state == "Active":
                    return server
    
                else:
                    # to prevent a infinate loop, cache inative server in a break process
                    env.process(self.serverBreak(server))
                    server = None
    
        def serverBreak(self, server):
            """
            Wait for server to become "Active" before putting in back into the queue
            """
    
            yield server.stateChange
            self.freeServer(server)
    
        def freeServer(self, server):
            """
            puts the server back into the queue
            """
            self.serverQ.put(server)
    
    def customer(env, customerId, serviceDesk):
        """
        Customer arrives
        gets server
        gets service from server
        returns server
        leaves
        """
    
        # arrives
        print(env.now, f"customer {customerId} has arrived")
    
        # gets server
        server = yield env.process(serviceDesk.getServer())
        print(env.now, f"customer {customerId} got server {server.serverId}")
    
        # gets service
        yield env.process(server.service())
        print(env.now, f"customer {customerId} server {server.serverId} finished service")
    
        # return server
        serviceDesk.freeServer(server)
    
        # leaves
        print(env.now, f"customer {customerId} has left")
    
    def genCustomers(env, lmbda, serviceDest):
        """
        generates the arrival of customers
        """
    
        i=0 # customer id
    
        while True:
            inter_arrival=-1/lmbda*np.log(np.random.rand())
            yield env.timeout(inter_arrival)
            i+=1
            env.process(customer(env, i, serviceDesk))
    
    # start the simulation
    env=simpy.Environment()
    serviceDesk = ServiceDesk(env)
    env.process(genCustomers(env,7,serviceDesk))
    
    env.run(5)

Upvotes: 2

Related Questions