simpy

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)

Solution

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