'Simpy preempt interrupt isn't handled at very start of simulation
Currently picking up Simpy and trying to build a simulation which has a series of events to complete which need personnel, supplies, etc.
To emulate the personnel, I have a Container with the number of people and to handle the availability (like a shift pattern for when the people are able to work) I have a PreemptiveResource which events can grab when the shift is available but are all removed with a priority request at the end of a shift, leaving the events to wait.
All works well except for one circumstance when the priority request occurs at the start of the simulation (e.g. the simulation starts at midnight and first shift begins at 6am, so there's an immediate request to pull the shifts away from the events).
Here's the code:
import itertools
import random
import simpy
import simpy.util
import bisect
class PriorityFilterStore(simpy.FilterStore):
def _do_put(self, event):
if len(self.items) < self._capacity:
bisect.insort(self.items, event.item)
event.succeed()
def _do_get(self, event):
for i in range(len(self.items)):
item = self.items[i]
if event.filter(item):
del self.items[i]
event.succeed(item)
break
return True
RANDOM_SEED = 42
CONS_MAX = [100, 100, 100]
RESUPPLY_TIME = 24
def incident_1(name, env, schedule, manpool, manhours):
print('%s is created at %.1f' % (name, env.now))
done_in = manhours
while done_in > 0:
with schedule.request(priority=2) as req:
yield req
try:
mans = manpool.get(1)
yield mans
start = env.now
print('%s is being pre-planned starting at %.1f hours.' % (name, start))
yield env.timeout(done_in)
print('%s finished pre-planning at %.1f hours.' % (name, env.now))
manpool.put(1)
done_in = 0
except simpy.Interrupt:
done_in -= env.now - start
print('%s interrupted at %.1f, with %.1f hours left' % (name, env.now, done_in))
manpool.put(1)
def cons_control(env, cons_list):
"""Periodically check the level of the *fuel_pump* and call the tank
truck if the level falls below a threshold."""
while True:
resups = [0,0,0]
res_flag = False
for i in range(len(cons_list)):
if cons_list[i].capacity > cons_list[i].level:
# We need to call the tank truck now!
print('Calling resupply for cons %d at %d' % (i, env.now))
# Wait for the tank truck to arrive and refuel the station
res_flag = True
resups[i] = cons_list[i].capacity - cons_list[i].level
if res_flag:
yield env.process(resup_truck(env, cons_list, resups))
yield env.timeout(12)
def resup_truck(env, cons_list, resups):
"""Arrives at the gas station after a certain delay and refuels it."""
yield env.timeout(RESUPPLY_TIME)
print('Resupply arriving at time %d' % env.now)
for i in range(len(cons_list)):
if resups[i] > 0:
print('Resupplying cons %d with %d items.' % (i, resups[i]))
yield cons_list[i].put(resups[i])
def scheduler(env, shift_a, schedule_a):
#env.timeout(1)
env.process(shift_off(env, schedule_a, shift_a[0]))
simpy.util.start_delayed(env,shift_off(env, schedule_a, shift_a[2]-shift_a[1]),shift_a[1])
simpy.util.start_delayed(env,shift_off(env, schedule_a, 6), shift_a[3])
yield env.timeout(0)
def shift_off(env, this_schedule, time):
with this_schedule.request(priority=1) as req:
yield req
try:
print('Shift is stopping at %.1f until %.1f' % (env.now, env.now + time))
yield env.timeout(time)
print('Shift is starting at %.1f' % env.now)
except simpy.Interrupt:
print('Shift is somehow pulled back in play...')
def monitoring(env, time, man_a, man_b, man_c):
while True:
print('At time = %d: '% env.now)
print('%d of %d are allocated.' % (man_a.count, man_a.capacity))
print('%d of %d are allocated.' % (man_b.count, man_b.capacity))
print('%d of %d are allocated.' % (man_c.count, man_c.capacity))
yield env.timeout(0)
# Setup and start the simulation
print('Incident processing')
random.seed(RANDOM_SEED)
# Create environment and start processes
env = simpy.Environment()
schedule_a = simpy.PreemptiveResource(env, capacity=1)
man_a = simpy.Container(env, capacity=5, init=5)
shift_a = [6, 12, 13, 18]
man_b = simpy.Resource(env, capacity=1)
man_c = simpy.Resource(env, capacity=1)
cons_x = simpy.Container(env, CONS_MAX[0], init=CONS_MAX[0])
cons_y = simpy.Container(env, CONS_MAX[1], init=CONS_MAX[1])
cons_z = simpy.Container(env, CONS_MAX[2], init=CONS_MAX[2])
#env.process(monitoring(env,3,man_a,man_b,man_c))
env.process(scheduler(env,shift_a,schedule_a))
env.process(cons_control(env, [cons_x,cons_y,cons_z]))
env.process(incident_1('Incident 1', env, schedule_a, man_a, 24))
# Execute!
env.run(until=240)
If you comment out the line env.process(shift_off(env, schedule_a, shift_a[0])), then it works. Otherwise there is an "Exception Unhandled" error, which appears to happen within def shift_off at the line yield env.timeout(time)
Can anyone explain why this interrupt isn't being handled and how to correct this?
TIA
Edit 1:
I've got this working, but don't understand why.
Trying to follow some working examples, I managed to get the behaviour to work if I created a Scheduler object and had it initialise with the processes to stop/start the shifts (e.g. request the resource):
class Scheduler (object):
def __init__(self, env, this_shift, this_sched):
self.env = env
self.shift = this_shift
self.sched = this_sched
self.process = env.process(self.shift_off(env, self.sched, self.shift[0]))
def shift_off(self, env, this_schedule, time):
with this_schedule.request(priority=1) as req:
yield req
try:
print('Shift is stopping at %.1f until %.1f' % (env.now, env.now + time))
yield env.timeout(time)
print('Shift is starting at %.1f' % env.now)
except simpy.Interrupt:
print('Shift is somehow pulled back in play...')
I'm assuming this has something to do with removing the process from env to this object, but do not follow how or why this works.
Anyone explain the subtleties here?
Edit 2: This is still confusing me... Making further changes can still cause this error.
My current work-around is to create the activities to be worked on, then create the scheduler that blocks the shifts. The preemptive request goes in, kicks the activities out and it continues fine. If I init the scheduler before the activities then I get the interrupt error as before - but surely it's not interrupting anything if the resource is free and available at this point...
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|
