Reputation: 37
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
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