Mercurial > templog
annotate py/utils.py @ 586:87c20b8c5472 default master
port to python3
author | Matt Johnston <matt@ucc.asn.au> |
---|---|
date | Mon, 09 Sep 2019 22:24:10 +0800 |
parents | 02aff9ff8d24 |
children |
rev | line source |
---|---|
141 | 1 import os |
2 import sys | |
265 | 3 import ctypes |
141 | 4 import time |
5 import select | |
6 import logging | |
253
0a1b642e3086
long polling config updates
Matt Johnston <matt@ucc.asn.au>
parents:
230
diff
changeset
|
7 import binascii |
259 | 8 import json |
265 | 9 import datetime |
272
af924d27140f
scale integrate by delay time
Matt Johnston <matt@ucc.asn.au>
parents:
271
diff
changeset
|
10 import collections |
141 | 11 |
149
d686b111dab4
working better. logging works properly, cleanup fridge.off() happens.
Matt Johnston <matt@ucc.asn.au>
parents:
145
diff
changeset
|
12 D = logging.debug |
141 | 13 L = logging.info |
14 W = logging.warning | |
15 E = logging.error | |
16 | |
17 DEFAULT_TRIES = 3 | |
18 READLINE_SELECT_TIMEOUT = 1 | |
19 | |
144 | 20 def EX(msg, *args, **kwargs): |
21 kwargs['exc_info'] = True | |
22 logging.error(msg, *args, **kwargs) | |
23 | |
141 | 24 # decorator, tries a number of times, returns None on failure, sleeps between |
25 # Must be used as "@retry()" if arguments are defaulted | |
26 def retry(retries=DEFAULT_TRIES, try_time = 1): | |
27 def inner(func): | |
28 def new_f(*args, **kwargs): | |
29 for i in range(retries): | |
30 d = func(*args, **kwargs) | |
31 if d is not None: | |
32 return d | |
33 time.sleep(try_time) | |
34 return None | |
35 | |
230 | 36 new_f.__name__ = func.__name__ |
141 | 37 return new_f |
38 return inner | |
39 | |
40 def readline(sock): | |
41 timeout = READLINE_SELECT_TIMEOUT | |
42 buf = '' | |
43 while True: | |
44 (rlist, wlist, xlist) = select.select([sock], [], [], timeout) | |
45 if sock not in rlist: | |
46 # hit timeout | |
47 return None | |
48 | |
49 c = sock.recv(1) | |
50 if c == '': | |
51 # lightblue timeout | |
52 return None | |
53 if c == '\r': | |
54 continue | |
55 | |
56 buf += c | |
57 if c == '\n': | |
58 return buf | |
59 | |
60 # from http://blog.stalkr.net/2011/04/pctf-2011-32-thats-no-bluetooth.html | |
61 def crc16(buff, crc = 0, poly = 0x8408): | |
62 l = len(buff) | |
63 i = 0 | |
64 while i < l: | |
65 ch = ord(buff[i]) | |
66 uc = 0 | |
67 while uc < 8: | |
68 if (crc & 1) ^ (ch & 1): | |
69 crc = (crc >> 1) ^ poly | |
70 else: | |
71 crc >>= 1 | |
72 ch >>= 1 | |
73 uc += 1 | |
74 i += 1 | |
75 return crc | |
76 | |
77 def cheap_daemon(): | |
78 L("Daemonising.") | |
79 sys.stdout.flush() | |
80 sys.stderr.flush() | |
81 out = file('/dev/null', 'a+') | |
82 os.dup2(out.fileno(), sys.stdout.fileno()) | |
83 os.dup2(out.fileno(), sys.stderr.fileno()) | |
84 | |
85 try: | |
86 pid = os.fork() | |
87 if pid > 0: | |
88 sys.exit(0) | |
230 | 89 except OSError as e: |
141 | 90 E("Bad fork()") |
91 sys.exit(1) | |
92 | |
93 os.setsid() | |
94 | |
95 try: | |
96 pid = os.fork() | |
97 if pid > 0: | |
98 sys.exit(0) | |
230 | 99 except OSError as e: |
141 | 100 E("Bad fork()") |
101 sys.exit(1) | |
102 | |
145 | 103 def uptime(): |
104 try: | |
105 return float(open('/proc/uptime', 'r').read().split(' ', 1)[0]) | |
230 | 106 except Exception as e: |
145 | 107 return -1 |
141 | 108 |
259 | 109 |
110 def json_load_round_float(s, **args): | |
111 return json.loads(s,parse_float = lambda f: round(float(f), 2), **args) | |
265 | 112 |
113 class NotTooOften(object): | |
114 """ prevents things happening more than once per limit. | |
115 Isn't monotonic, good enough for logging. eg | |
116 self.logfailure = NotTooOften(180) # 3 minutes | |
117 ... | |
118 if self.logfailure(): | |
119 L("blah") | |
120 """ | |
121 def __init__(self, limit): | |
122 """ limit is a delay in seconds or TimeDelta """ | |
123 if type(limit) is datetime.timedelta: | |
124 self.limit = limit | |
125 else: | |
126 self.limit = datetime.timedelta(seconds=limit) | |
127 | |
128 # must be positive | |
129 assert self.limit > datetime.timedelta(0) | |
130 self.last = datetime.datetime(10, 1, 1) | |
131 | |
132 def __call__(self): | |
133 if datetime.datetime.now() - self.last > self.limit: | |
134 self.last = datetime.datetime.now() | |
135 return True | |
136 | |
137 def log(self, msg): | |
138 """ calls L(msg) if it isn't too often, otherwise D(msg) | |
139 """ | |
140 if self(): | |
141 L(msg + " (log interval %s)" % str(self.limit)) | |
142 else: | |
143 D(msg) | |
270 | 144 |
272
af924d27140f
scale integrate by delay time
Matt Johnston <matt@ucc.asn.au>
parents:
271
diff
changeset
|
145 Period = collections.namedtuple('Period', 'start end') |
270 | 146 class StepIntegrator(object): |
147 """ | |
271 | 148 Takes on/off events and a monotonically increasing timefn. Returns the integral |
149 of (now-limittime, now) over those events. | |
150 | |
270 | 151 >>> s = StepIntegrator(lambda: t, 40) |
152 >>> t = 1 | |
153 >>> s.turn(1) | |
154 >>> t = 10 | |
155 >>> s.turn(0) | |
156 >>> t = 20 | |
157 >>> s.turn(1) | |
158 >>> t = 30 | |
159 >>> print(s.integrate()) | |
160 19 | |
161 >>> s.turn(0) | |
162 >>> print(s.integrate()) | |
163 19 | |
164 >>> t = 35 | |
165 >>> print(s.integrate()) | |
166 19 | |
167 >>> t = 42 | |
168 >>> print(s.integrate()) | |
169 18 | |
170 >>> t = 52 | |
171 >>> print(s.integrate()) | |
172 10 | |
173 >>> t = 69 | |
174 >>> print(s.integrate()) | |
175 1 | |
176 >>> t = 70 | |
177 >>> print(s.integrate()) | |
178 0 | |
179 >>> t = 170 | |
180 >>> print(s.integrate()) | |
181 0 | |
182 """ | |
183 def __init__(self, timefn, limittime): | |
272
af924d27140f
scale integrate by delay time
Matt Johnston <matt@ucc.asn.au>
parents:
271
diff
changeset
|
184 # _on_periods is a list of [period]. End is None if still on |
270 | 185 self._on_periods = [] |
186 self._timefn = timefn | |
187 self._limittime = limittime | |
188 | |
271 | 189 def set_limit(self, limittime): |
190 if self._limittime == limittime: | |
191 return | |
192 self._limittime = limittime | |
272
af924d27140f
scale integrate by delay time
Matt Johnston <matt@ucc.asn.au>
parents:
271
diff
changeset
|
193 self._trim() |
271 | 194 |
270 | 195 def turn(self, value): |
196 if not self._on_periods: | |
197 if value: | |
272
af924d27140f
scale integrate by delay time
Matt Johnston <matt@ucc.asn.au>
parents:
271
diff
changeset
|
198 self._on_periods.append(Period(self._timefn(), None)) |
270 | 199 return |
200 | |
201 # state hasn't changed | |
272
af924d27140f
scale integrate by delay time
Matt Johnston <matt@ucc.asn.au>
parents:
271
diff
changeset
|
202 on_now = (self._on_periods[-1].end is None) |
270 | 203 if value == on_now: |
204 return | |
205 | |
206 if value: | |
272
af924d27140f
scale integrate by delay time
Matt Johnston <matt@ucc.asn.au>
parents:
271
diff
changeset
|
207 self._on_periods.append(Period(self._timefn(), None)) |
270 | 208 else: |
272
af924d27140f
scale integrate by delay time
Matt Johnston <matt@ucc.asn.au>
parents:
271
diff
changeset
|
209 self._on_periods[-1] = self._on_periods[-1]._replace(end = self._timefn()) |
270 | 210 |
211 def _trim(self): | |
212 begin = self._timefn() - self._limittime | |
213 # shortcut, first start is after begin | |
272
af924d27140f
scale integrate by delay time
Matt Johnston <matt@ucc.asn.au>
parents:
271
diff
changeset
|
214 if not self._on_periods or self._on_periods[0].start >= begin: |
270 | 215 return |
216 | |
217 new_periods = [] | |
218 for s, e in self._on_periods: | |
219 if s == e: | |
220 continue | |
221 elif s >= begin: | |
272
af924d27140f
scale integrate by delay time
Matt Johnston <matt@ucc.asn.au>
parents:
271
diff
changeset
|
222 new_periods.append(Period(s,e)) |
270 | 223 elif e is not None and e < begin: |
224 continue | |
225 else: | |
272
af924d27140f
scale integrate by delay time
Matt Johnston <matt@ucc.asn.au>
parents:
271
diff
changeset
|
226 new_periods.append(Period(begin, e)) |
270 | 227 self._on_periods = new_periods |
228 | |
229 def integrate(self): | |
230 self._trim() | |
231 tot = 0 | |
232 for s, e in self._on_periods: | |
233 if e is None: | |
234 e = self._timefn() | |
235 tot += (e-s) | |
236 return tot | |
237 | |
238 | |
239 | |
240 | |
241 | |
242 | |
243 |