I want to create a timer with a callback that can be interrupted or reset using SimPy. If interrupted, I do not want the callback to be executed, and if reset, I want the timer to restart with the same delay from env.now
. This seemed like an easy thing to do initially by simply using env.timeout
. However, the documentation notes:
To actually let time pass in a simulation, there is the timeout event. A timeout has two parameters: a delay and an optional value: Timeout(delay, value=None). It triggers itself during its creation and schedules itself at now + delay. Thus, the succeed() and fail() methods cannot be called again and you have to pass the event value to it when you create the timeout.
Because the simulation starts triggered, I can't add callbacks and because you can't call fail
, I can't interrupt the timeout.
I've considered just implementing a process that waits one timestep and checks a flag if it's been interrupted or reached the env.now
it was waiting for, but this seems horribly inefficient, and if I have a lot of timers (which I will), I'm worried that the number of generators will overwhelm the simulation. (The timeout function seems to work by scheduling itself in the future of the simulation, which is why you can have a ton of those running around).
So the spec is - create an event that triggers a callback after a specified amount of time, but that can be reset or interrupted before that time occurs. Any thoughts?
Well, if I understood your question correctly one thing you can do is create a Timer
class with a wait method which checks for simpy.Interrupt
. You can imlement stop()
so that when it's called, you also call interrupt()
. That way the callback will not be executed as long as interrupt()
has previously been called. A reset method would simply call stop()
(interrupt) and start()
again thus setting the action back to running()
and calling wait()
again, allowing the callback to be executed again after every timeout until interrupt is called again.
Here's an example implementation of such a Timer
class:
import simpy
class Timer(object):
def __init__(self, env, delay, callback):
self.env = env
self.delay = delay
self.action = None
self.callback = callback
self.running = False
self.canceled = False
def wait(self):
"""
Calls a callback after time has elapsed.
"""
try:
yield self.env.timeout(self.delay)
self.callback()
self.running = False
except simpy.Interrupt as i:
print "Interrupted!"
self.canceled = True
self.running = False
def start(self):
"""
Starts the timer
"""
if not self.running:
self.running = True
self.action = self.env.process(self.wait())
def stop(self):
"""
Stops the timer
"""
if self.running:
self.action.interrupt()
self.action = None
def reset(self):
"""
Interrupts the current timer and restarts.
"""
self.stop()
self.start()