Mercurial > templog
comparison 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 |
comparison
equal
deleted
inserted
replaced
293:d15dda1b1f76 | 294:6bacd8ca9f8f |
---|---|
1 import os | 1 import os |
2 import sys | 2 import sys |
3 #import ctypes | 3 import ctypes |
4 import time | 4 import time |
5 import select | 5 import select |
6 import logging | 6 import logging |
7 import binascii | 7 import binascii |
8 import json | 8 import json |
9 import datetime | |
10 import collections | |
9 | 11 |
10 D = logging.debug | 12 D = logging.debug |
11 L = logging.info | 13 L = logging.info |
12 W = logging.warning | 14 W = logging.warning |
13 E = logging.error | 15 E = logging.error |
136 return -1 | 138 return -1 |
137 | 139 |
138 | 140 |
139 def json_load_round_float(s, **args): | 141 def json_load_round_float(s, **args): |
140 return json.loads(s,parse_float = lambda f: round(float(f), 2), **args) | 142 return json.loads(s,parse_float = lambda f: round(float(f), 2), **args) |
143 | |
144 class NotTooOften(object): | |
145 """ prevents things happening more than once per limit. | |
146 Isn't monotonic, good enough for logging. eg | |
147 self.logfailure = NotTooOften(180) # 3 minutes | |
148 ... | |
149 if self.logfailure(): | |
150 L("blah") | |
151 """ | |
152 def __init__(self, limit): | |
153 """ limit is a delay in seconds or TimeDelta """ | |
154 if type(limit) is datetime.timedelta: | |
155 self.limit = limit | |
156 else: | |
157 self.limit = datetime.timedelta(seconds=limit) | |
158 | |
159 # must be positive | |
160 assert self.limit > datetime.timedelta(0) | |
161 self.last = datetime.datetime(10, 1, 1) | |
162 | |
163 def __call__(self): | |
164 if datetime.datetime.now() - self.last > self.limit: | |
165 self.last = datetime.datetime.now() | |
166 return True | |
167 | |
168 def log(self, msg): | |
169 """ calls L(msg) if it isn't too often, otherwise D(msg) | |
170 """ | |
171 if self(): | |
172 L(msg + " (log interval %s)" % str(self.limit)) | |
173 else: | |
174 D(msg) | |
175 | |
176 Period = collections.namedtuple('Period', 'start end') | |
177 class StepIntegrator(object): | |
178 """ | |
179 Takes on/off events and a monotonically increasing timefn. Returns the integral | |
180 of (now-limittime, now) over those events. | |
181 | |
182 >>> s = StepIntegrator(lambda: t, 40) | |
183 >>> t = 1 | |
184 >>> s.turn(1) | |
185 >>> t = 10 | |
186 >>> s.turn(0) | |
187 >>> t = 20 | |
188 >>> s.turn(1) | |
189 >>> t = 30 | |
190 >>> print(s.integrate()) | |
191 19 | |
192 >>> s.turn(0) | |
193 >>> print(s.integrate()) | |
194 19 | |
195 >>> t = 35 | |
196 >>> print(s.integrate()) | |
197 19 | |
198 >>> t = 42 | |
199 >>> print(s.integrate()) | |
200 18 | |
201 >>> t = 52 | |
202 >>> print(s.integrate()) | |
203 10 | |
204 >>> t = 69 | |
205 >>> print(s.integrate()) | |
206 1 | |
207 >>> t = 70 | |
208 >>> print(s.integrate()) | |
209 0 | |
210 >>> t = 170 | |
211 >>> print(s.integrate()) | |
212 0 | |
213 """ | |
214 def __init__(self, timefn, limittime): | |
215 # _on_periods is a list of [period]. End is None if still on | |
216 self._on_periods = [] | |
217 self._timefn = timefn | |
218 self._limittime = limittime | |
219 | |
220 def set_limit(self, limittime): | |
221 if self._limittime == limittime: | |
222 return | |
223 self._limittime = limittime | |
224 self._trim() | |
225 | |
226 def turn(self, value): | |
227 if not self._on_periods: | |
228 if value: | |
229 self._on_periods.append(Period(self._timefn(), None)) | |
230 return | |
231 | |
232 # state hasn't changed | |
233 on_now = (self._on_periods[-1].end is None) | |
234 if value == on_now: | |
235 return | |
236 | |
237 if value: | |
238 self._on_periods.append(Period(self._timefn(), None)) | |
239 else: | |
240 self._on_periods[-1] = self._on_periods[-1]._replace(end = self._timefn()) | |
241 | |
242 def _trim(self): | |
243 begin = self._timefn() - self._limittime | |
244 # shortcut, first start is after begin | |
245 if not self._on_periods or self._on_periods[0].start >= begin: | |
246 return | |
247 | |
248 new_periods = [] | |
249 for s, e in self._on_periods: | |
250 if s == e: | |
251 continue | |
252 elif s >= begin: | |
253 new_periods.append(Period(s,e)) | |
254 elif e is not None and e < begin: | |
255 continue | |
256 else: | |
257 new_periods.append(Period(begin, e)) | |
258 self._on_periods = new_periods | |
259 | |
260 def integrate(self): | |
261 self._trim() | |
262 tot = 0 | |
263 for s, e in self._on_periods: | |
264 if e is None: | |
265 e = self._timefn() | |
266 tot += (e-s) | |
267 return tot | |
268 | |
269 | |
270 | |
271 | |
272 | |
273 | |
274 |