Mercurial > templog
diff py/utils.py @ 294:6bacd8ca9f8f
merge
author | Matt Johnston <matt@ucc.asn.au> |
---|---|
date | Sat, 06 Jul 2019 18:30:25 +0800 |
parents | af924d27140f |
children | 02aff9ff8d24 |
line wrap: on
line diff
--- a/py/utils.py Sat Jul 06 18:29:45 2019 +0800 +++ b/py/utils.py Sat Jul 06 18:30:25 2019 +0800 @@ -1,11 +1,13 @@ import os import sys -#import ctypes +import ctypes import time import select import logging import binascii import json +import datetime +import collections D = logging.debug L = logging.info @@ -138,3 +140,135 @@ def json_load_round_float(s, **args): return json.loads(s,parse_float = lambda f: round(float(f), 2), **args) + +class NotTooOften(object): + """ prevents things happening more than once per limit. + Isn't monotonic, good enough for logging. eg + self.logfailure = NotTooOften(180) # 3 minutes + ... + if self.logfailure(): + L("blah") + """ + def __init__(self, limit): + """ limit is a delay in seconds or TimeDelta """ + if type(limit) is datetime.timedelta: + self.limit = limit + else: + self.limit = datetime.timedelta(seconds=limit) + + # must be positive + assert self.limit > datetime.timedelta(0) + self.last = datetime.datetime(10, 1, 1) + + def __call__(self): + if datetime.datetime.now() - self.last > self.limit: + self.last = datetime.datetime.now() + return True + + def log(self, msg): + """ calls L(msg) if it isn't too often, otherwise D(msg) + """ + if self(): + L(msg + " (log interval %s)" % str(self.limit)) + else: + D(msg) + +Period = collections.namedtuple('Period', 'start end') +class StepIntegrator(object): + """ + Takes on/off events and a monotonically increasing timefn. Returns the integral + of (now-limittime, now) over those events. + + >>> s = StepIntegrator(lambda: t, 40) + >>> t = 1 + >>> s.turn(1) + >>> t = 10 + >>> s.turn(0) + >>> t = 20 + >>> s.turn(1) + >>> t = 30 + >>> print(s.integrate()) + 19 + >>> s.turn(0) + >>> print(s.integrate()) + 19 + >>> t = 35 + >>> print(s.integrate()) + 19 + >>> t = 42 + >>> print(s.integrate()) + 18 + >>> t = 52 + >>> print(s.integrate()) + 10 + >>> t = 69 + >>> print(s.integrate()) + 1 + >>> t = 70 + >>> print(s.integrate()) + 0 + >>> t = 170 + >>> print(s.integrate()) + 0 + """ + def __init__(self, timefn, limittime): + # _on_periods is a list of [period]. End is None if still on + self._on_periods = [] + self._timefn = timefn + self._limittime = limittime + + def set_limit(self, limittime): + if self._limittime == limittime: + return + self._limittime = limittime + self._trim() + + def turn(self, value): + if not self._on_periods: + if value: + self._on_periods.append(Period(self._timefn(), None)) + return + + # state hasn't changed + on_now = (self._on_periods[-1].end is None) + if value == on_now: + return + + if value: + self._on_periods.append(Period(self._timefn(), None)) + else: + self._on_periods[-1] = self._on_periods[-1]._replace(end = self._timefn()) + + def _trim(self): + begin = self._timefn() - self._limittime + # shortcut, first start is after begin + if not self._on_periods or self._on_periods[0].start >= begin: + return + + new_periods = [] + for s, e in self._on_periods: + if s == e: + continue + elif s >= begin: + new_periods.append(Period(s,e)) + elif e is not None and e < begin: + continue + else: + new_periods.append(Period(begin, e)) + self._on_periods = new_periods + + def integrate(self): + self._trim() + tot = 0 + for s, e in self._on_periods: + if e is None: + e = self._timefn() + tot += (e-s) + return tot + + + + + + +