Scheduling Events in Boodler’s “distant” future
One of my original requests to Andrew was to extend Boodler’s scheduling time limitation (of just over one hour), which seemed too restrictive for those of us who “think big” and wanted something to happen further in the future than that limitation would allow — a perfectly reasonable request, especially when you listen for extended periods of time, and/or want to introduce a high level of randomness in your environments (including events’ overall duration).
I believe part of my misunderstanding of the problem stemmed from the ‘owstorm’ agent — after all, why would you want to restrict a thunderstorm to sixty minutes’ duration if you could make them longer ad lib?
After having mentioned this in an email (including about how I had altered agent.py to work around the limitation without ill effect, it seemed), Andrew explained the reason for the limitation — nothing to do with memory, as I had originally thought, incidentally — which was because of “computations of sound position overflow”, in Andrew’s words. There were more, but I won’t include them here.
He also roundly chastised me for my idiotic solution:
I cut [the scheduling limit] down to 1 hour to be safe, and to encourage people to solve their problems in agent code rather than in the scheduler.
(I see how well that worked.)
… Oops.
In response to the problem, I have penned a solution; an agent that I have tentatively dubbed ‘FutureAgent’, which looks like this:
from boodle.agent import Agent
class FutureAgent(Agent):
"""FutureAgent: subclass of Agent for scheduling events in the
future.
Methods to call:
sched_future_agent() -- schedule an event for the 'distant' future.
This is typically any value over 3600 seconds, although anything
under that will work as well.
Otherwise, treat it like any other Agent.
"""
def __init__(self):
Agent.__init__(self)
self.timeleft = None
# Don't set this value above 3600 or else you will defeat the
# purpose.
self.btimelimit = 3600
def sched_future_agent(self, ag, delay=0, chan=None):
"""sched_future_agent(agent [, delay=0, chan=self.channel])
Schedules an agent to run at some point in the distant future
(e.g. greater than 3600 seconds). Unlike Boodler's agent, this
scheduler will keep calling a 'delaying' internal class before
actually scheduling itself.
"""
if self.timeleft is None:
self.timeleft = delay
if self.timeleft > self.btimelimit:
# Time is greater than time limit, so sleep then try again.
self._sleep(self.btimelimit)
else:
# Schedule the agent normally, now.
self.sched_agent(ag, self.timeleft, chan)
def _sleep(self, delay=3600):
"""sleep([delay=3600])
Sleeps for the specified delay time. This would usually be 3600
seconds, so as to circumvent Boodler's built-in scheduling
limitation of a maximum of 3605 seconds.
"""
if self.timeleft is None:
errormsg = 'Time left for "%s" not yet set, can't sleep'
raise NotImplementedError, errormsg % (self.getname(), )
# Subtract the delay from the time left then reschedule myself.
self.timeleft = self.timeleft - delay
self.resched(delay)
Its use is straightforward — You import the module containing this class then use it (instead of Agent) to create your new agent. Then, when you need to schedule an event in the “distant” future — that is, further than 3600 seconds from ‘now’ — you call sched_future_agent() instead of sched_agent(), and it will handle the delay without error (or so my limited testing suggested, anyway).
I suspect that something like this might find its way into a future release of Boodler as part of the trunk, but I thought I’d share this now in case anyone else has run into a similar problem (and has to date found a similarly dull response to my own).
Thanks of course go to Andrew, who all but put the details in writing; I merely followed his suggestion and wrote the code to support them (any implementation errors or design flaws are therefore my own responsibility).