Created
October 24, 2011 20:00
-
-
Save AdamG/1309979 to your computer and use it in GitHub Desktop.
python tagtimed
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python | |
""" | |
TagTime daemon, in python. | |
Only trying to get to 'worksforme' here - the perl daemon just isn't | |
launching anything 3/4 of the time, and I have no idea how to debug | |
it. | |
""" | |
import argparse | |
import datetime | |
import logging | |
import math | |
import os | |
import random | |
import sys | |
import time | |
# logging stuff | |
logger = logging.getLogger("tagtimed") | |
logging.basicConfig(stream=sys.stderr, level=logging.DEBUG) | |
log = logger.debug | |
# This directory, needed to give path to ping.pl | |
dirname = os.path.dirname(__file__) | |
now = datetime.datetime.now | |
def main(): | |
parser = argparse.ArgumentParser( | |
description="start tagtimed.py") | |
parser.add_argument( | |
"interval", type=str, | |
help= | |
"Average interval between pings. If a number, the interval " | |
"is that number of seconds. Use 'm' or 'h' as suffixes to indicate " | |
"minutes or hours.") | |
args = parser.parse_args() | |
if args.interval == "test": | |
TagTimer(None).ping() | |
return | |
interval = _convert_to_seconds(args.interval) | |
TagTimer(interval).run() | |
class TagTimer(object): | |
def __init__(self, interval): | |
self.interval = interval | |
self.next_ping = None | |
def run(self): | |
"Initialize .next_ping, then sleep, run inner, and repeat." | |
# For the first ping, pretend we're in the middle of an | |
# existing period. The perl version walks forward from an | |
# epoch, but I'm OK with faking it. Note that the interval | |
# passed to .get_next_ping() is negative, so .next_ping will | |
# initially be a time in the past, and then .schedule_ping() | |
# prepares the future time. | |
# | |
# Note that this can mean a ping can happen immediately, but | |
# I'm not fussed about it. | |
self.next_ping = self.get_next_ping(now(), -self.interval/2.) | |
self.schedule_ping() | |
while True: | |
time.sleep(1) | |
# I don't like doing everything 3 levels of indentation | |
# in, OK? | |
self.run_inner() | |
def run_inner(self): | |
"Inner loop body of TagTimer.run()" | |
if now() >= self.next_ping: | |
self.ping() | |
self.schedule_ping() | |
def ping(self): | |
"Run a ping." | |
log("{0}:ping".format(now())) | |
# TODO: what a long, ugly line. And make it configurable. | |
cmd = "xterm -T 'TagTime' -fg white -bg red -cr MidnightBlue -bc -rw -e {dirname}/ping.pl {epochtime}".format( | |
dirname=dirname, epochtime=_epochtime(now())) | |
os.system(cmd) | |
def schedule_ping(self): | |
"Update .next_ping based on the previous value" | |
last_ping = self.next_ping | |
self.next_ping = self.get_next_ping(last_ping) | |
log("next ping at {0}".format(self.next_ping.strftime("%H:%M:%S"))) | |
def get_next_ping(self, after, interval=None): | |
"""Get a next ping after datetime 'after'. | |
Pass interval to override the default interval. | |
""" | |
return self.get_next_ping_poisson(after, interval) | |
def get_next_ping_poisson(self, after, interval=None): | |
"sub nextping { my($prev)=@_; return max($prev+1,round1($prev+exprand())); }" | |
if interval is None: interval = self.interval | |
value = -interval * math.log(random.random()) | |
return after + datetime.timedelta(seconds=max(1, value)) | |
def get_next_ping_uniform(self, after, interval=None): | |
if interval is None: interval = self.interval | |
return after + datetime.timedelta(seconds=random.uniform(0, interval*2)) | |
def _epochtime(dt): | |
"get epoch time integer of datetime 'dt'" | |
return int(time.mktime(dt.timetuple())) | |
def _convert_to_seconds(data): | |
"Convert string 'data' to an interval in seconds, interpreting m/h suffix" | |
# "3600" | |
if data.isdigit(): return float(data) | |
# "60m" | |
if data.lower()[-1] == "m": return float(data[:-1]) * 60 | |
# "1h" | |
if data.lower()[-1] == "h": return float(data[:-1]) * 3600 | |
if __name__ == "__main__": main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment