Mercurial > templog
annotate py/tempserver.py @ 569:d7575050e4be
StepIntegrator
author | Matt Johnston <matt@ucc.asn.au> |
---|---|
date | Wed, 11 Nov 2015 00:07:21 +0800 |
parents | 87d064493714 |
children | 02aff9ff8d24 |
rev | line source |
---|---|
447 | 1 #!/home/matt/templog/venv/bin/python |
439 | 2 |
3 import sys | |
4 import os | |
448
fe729664a5e6
working better. logging works properly, cleanup fridge.off() happens.
Matt Johnston <matt@ucc.asn.au>
parents:
447
diff
changeset
|
5 import logging |
498 | 6 import time |
7 import signal | |
527 | 8 import asyncio |
532
9b1d71310c83
better arg parser. seems close to ready
Matt Johnston <matt@ucc.asn.au>
parents:
531
diff
changeset
|
9 import argparse |
448
fe729664a5e6
working better. logging works properly, cleanup fridge.off() happens.
Matt Johnston <matt@ucc.asn.au>
parents:
447
diff
changeset
|
10 |
471 | 11 import lockfile.pidlockfile |
461
1eb68df9f8ab
Add daemon mode with locking, add "disabled" parameter
Matt Johnston <matt@ucc.asn.au>
parents:
459
diff
changeset
|
12 import daemon |
439 | 13 |
14 import utils | |
448
fe729664a5e6
working better. logging works properly, cleanup fridge.off() happens.
Matt Johnston <matt@ucc.asn.au>
parents:
447
diff
changeset
|
15 from utils import L,D,EX,W |
444 | 16 import fridge |
17 import config | |
530 | 18 import sensor |
444 | 19 import params |
459 | 20 import uploader |
556 | 21 import configwaiter |
444 | 22 |
439 | 23 |
24 class Tempserver(object): | |
565
26f20cee71be
emergency change sensor config
Matt Johnston <matt@ucc.asn.au>
parents:
556
diff
changeset
|
25 def __init__(self, test_mode, nowait): |
439 | 26 self.readings = [] |
444 | 27 self.current = (None, None) |
448
fe729664a5e6
working better. logging works properly, cleanup fridge.off() happens.
Matt Johnston <matt@ucc.asn.au>
parents:
447
diff
changeset
|
28 self.fridge = None |
532
9b1d71310c83
better arg parser. seems close to ready
Matt Johnston <matt@ucc.asn.au>
parents:
531
diff
changeset
|
29 self._wakeup = asyncio.Event() |
9b1d71310c83
better arg parser. seems close to ready
Matt Johnston <matt@ucc.asn.au>
parents:
531
diff
changeset
|
30 self._test_mode = test_mode |
565
26f20cee71be
emergency change sensor config
Matt Johnston <matt@ucc.asn.au>
parents:
556
diff
changeset
|
31 self._nowait = nowait |
439 | 32 |
448
fe729664a5e6
working better. logging works properly, cleanup fridge.off() happens.
Matt Johnston <matt@ucc.asn.au>
parents:
447
diff
changeset
|
33 def __enter__(self): |
444 | 34 self.params = params.Params() |
565
26f20cee71be
emergency change sensor config
Matt Johnston <matt@ucc.asn.au>
parents:
556
diff
changeset
|
35 self.fridge = fridge.Fridge(self, self._nowait) |
459 | 36 self.uploader = uploader.Uploader(self) |
551
9499bd2f344b
long polling config updates
Matt Johnston <matt@ucc.asn.au>
parents:
533
diff
changeset
|
37 self.configwaiter = configwaiter.ConfigWaiter(self) |
444 | 38 self.params.load() |
528 | 39 self.set_sensors(sensor.make_sensor(self)) |
551
9499bd2f344b
long polling config updates
Matt Johnston <matt@ucc.asn.au>
parents:
533
diff
changeset
|
40 asyncio.get_event_loop().add_signal_handler(signal.SIGHUP, self.reload_signal) |
448
fe729664a5e6
working better. logging works properly, cleanup fridge.off() happens.
Matt Johnston <matt@ucc.asn.au>
parents:
447
diff
changeset
|
41 return self |
439 | 42 |
448
fe729664a5e6
working better. logging works properly, cleanup fridge.off() happens.
Matt Johnston <matt@ucc.asn.au>
parents:
447
diff
changeset
|
43 def __exit__(self, exc_type, exc_value, traceback): |
fe729664a5e6
working better. logging works properly, cleanup fridge.off() happens.
Matt Johnston <matt@ucc.asn.au>
parents:
447
diff
changeset
|
44 L("Exiting, cleanup handler"); |
fe729664a5e6
working better. logging works properly, cleanup fridge.off() happens.
Matt Johnston <matt@ucc.asn.au>
parents:
447
diff
changeset
|
45 self.fridge.off() |
439 | 46 |
444 | 47 def run(self): |
448
fe729664a5e6
working better. logging works properly, cleanup fridge.off() happens.
Matt Johnston <matt@ucc.asn.au>
parents:
447
diff
changeset
|
48 |
fe729664a5e6
working better. logging works properly, cleanup fridge.off() happens.
Matt Johnston <matt@ucc.asn.au>
parents:
447
diff
changeset
|
49 if self.fridge is None: |
fe729664a5e6
working better. logging works properly, cleanup fridge.off() happens.
Matt Johnston <matt@ucc.asn.au>
parents:
447
diff
changeset
|
50 raise Exception("Tempserver.run() must be within 'with Tempserver() as server'") |
fe729664a5e6
working better. logging works properly, cleanup fridge.off() happens.
Matt Johnston <matt@ucc.asn.au>
parents:
447
diff
changeset
|
51 |
fe729664a5e6
working better. logging works properly, cleanup fridge.off() happens.
Matt Johnston <matt@ucc.asn.au>
parents:
447
diff
changeset
|
52 # XXX do these go here or in __enter_() ? |
fe729664a5e6
working better. logging works properly, cleanup fridge.off() happens.
Matt Johnston <matt@ucc.asn.au>
parents:
447
diff
changeset
|
53 self.start_time = self.now() |
533 | 54 runloops = [ |
527 | 55 self.fridge.run(), |
56 self.sensors.run(), | |
57 self.uploader.run(), | |
551
9499bd2f344b
long polling config updates
Matt Johnston <matt@ucc.asn.au>
parents:
533
diff
changeset
|
58 self.configwaiter.run(), |
533 | 59 ] |
531 | 60 |
527 | 61 loop = asyncio.get_event_loop() |
532
9b1d71310c83
better arg parser. seems close to ready
Matt Johnston <matt@ucc.asn.au>
parents:
531
diff
changeset
|
62 try: |
533 | 63 loop.run_until_complete(asyncio.gather(*runloops)) |
532
9b1d71310c83
better arg parser. seems close to ready
Matt Johnston <matt@ucc.asn.au>
parents:
531
diff
changeset
|
64 except KeyboardInterrupt: |
533 | 65 print('\nctrl-c') |
532
9b1d71310c83
better arg parser. seems close to ready
Matt Johnston <matt@ucc.asn.au>
parents:
531
diff
changeset
|
66 finally: |
533 | 67 # loop.close() seems necessary otherwise get warnings about signal handlers |
532
9b1d71310c83
better arg parser. seems close to ready
Matt Johnston <matt@ucc.asn.au>
parents:
531
diff
changeset
|
68 loop.close() |
444 | 69 |
70 def now(self): | |
71 return utils.monotonic_time() | |
72 | |
443 | 73 def set_sensors(self, sensors): |
447 | 74 if hasattr(self, 'sensors'): |
443 | 75 self.sensors.kill() |
76 self.sensors = sensors | |
77 self.wort_name = sensors.wort_name() | |
78 self.fridge_name = sensors.fridge_name() | |
439 | 79 |
80 def take_readings(self): | |
81 ret = self.readings | |
82 self.readings = [] | |
83 return ret | |
84 | |
85 def pushfront(self, readings): | |
86 """ used if a caller of take_readings() fails """ | |
459 | 87 self.readings = readings + self.readings |
439 | 88 |
443 | 89 # a reading is a map of {sensorname: value}. temperatures |
90 # are float degrees | |
439 | 91 def add_reading(self, reading): |
92 """ adds a reading at the current time """ | |
454 | 93 D("add_reading(%s)" % str(reading)) |
444 | 94 self.readings.append( (reading, self.now())) |
443 | 95 self.current = (reading.get(self.wort_name, None), |
96 reading.get(self.fridge_name, None)) | |
459 | 97 if len(self.readings) > config.MAX_READINGS: |
98 self.readings = self.readings[-config.MAX_READINGS:] | |
439 | 99 |
100 def current_temps(self): | |
443 | 101 """ returns (wort_temp, fridge_temp) tuple """ |
531 | 102 D("current: %s" % str(self.current)) |
447 | 103 return self.current |
444 | 104 |
531 | 105 @asyncio.coroutine |
518
0b5ff341d124
sleep on a semaphore so it can start/stop immediately when there's a SIGHUP
Matt Johnston <matt@ucc.asn.au>
parents:
514
diff
changeset
|
106 def sleep(self, timeout): |
0b5ff341d124
sleep on a semaphore so it can start/stop immediately when there's a SIGHUP
Matt Johnston <matt@ucc.asn.au>
parents:
514
diff
changeset
|
107 """ sleeps for timeout seconds, though wakes if the server's config is updated """ |
531 | 108 # XXX fixme - we should wake on _wakeup but asyncio Condition with wait_for is a bit broken? |
109 # https://groups.google.com/forum/#!topic/python-tulip/eSm7rZAe9LM | |
110 # For now we just sleep, ignore the _wakeup | |
532
9b1d71310c83
better arg parser. seems close to ready
Matt Johnston <matt@ucc.asn.au>
parents:
531
diff
changeset
|
111 try: |
9b1d71310c83
better arg parser. seems close to ready
Matt Johnston <matt@ucc.asn.au>
parents:
531
diff
changeset
|
112 yield from asyncio.wait_for(self._wakeup.wait(), timeout=timeout) |
9b1d71310c83
better arg parser. seems close to ready
Matt Johnston <matt@ucc.asn.au>
parents:
531
diff
changeset
|
113 except asyncio.TimeoutError: |
9b1d71310c83
better arg parser. seems close to ready
Matt Johnston <matt@ucc.asn.au>
parents:
531
diff
changeset
|
114 pass |
533 | 115 |
551
9499bd2f344b
long polling config updates
Matt Johnston <matt@ucc.asn.au>
parents:
533
diff
changeset
|
116 def reload_signal(self, no_file = False): |
518
0b5ff341d124
sleep on a semaphore so it can start/stop immediately when there's a SIGHUP
Matt Johnston <matt@ucc.asn.au>
parents:
514
diff
changeset
|
117 try: |
551
9499bd2f344b
long polling config updates
Matt Johnston <matt@ucc.asn.au>
parents:
533
diff
changeset
|
118 if not no_file: |
9499bd2f344b
long polling config updates
Matt Johnston <matt@ucc.asn.au>
parents:
533
diff
changeset
|
119 self.params.load() |
9499bd2f344b
long polling config updates
Matt Johnston <matt@ucc.asn.au>
parents:
533
diff
changeset
|
120 L("Reloaded.") |
532
9b1d71310c83
better arg parser. seems close to ready
Matt Johnston <matt@ucc.asn.au>
parents:
531
diff
changeset
|
121 self._wakeup.set() |
9b1d71310c83
better arg parser. seems close to ready
Matt Johnston <matt@ucc.asn.au>
parents:
531
diff
changeset
|
122 self._wakeup.clear() |
528 | 123 except Error as e: |
518
0b5ff341d124
sleep on a semaphore so it can start/stop immediately when there's a SIGHUP
Matt Johnston <matt@ucc.asn.au>
parents:
514
diff
changeset
|
124 W("Problem reloading: %s" % str(e)) |
0b5ff341d124
sleep on a semaphore so it can start/stop immediately when there's a SIGHUP
Matt Johnston <matt@ucc.asn.au>
parents:
514
diff
changeset
|
125 |
532
9b1d71310c83
better arg parser. seems close to ready
Matt Johnston <matt@ucc.asn.au>
parents:
531
diff
changeset
|
126 def test_mode(self): |
9b1d71310c83
better arg parser. seems close to ready
Matt Johnston <matt@ucc.asn.au>
parents:
531
diff
changeset
|
127 return self._test_mode |
9b1d71310c83
better arg parser. seems close to ready
Matt Johnston <matt@ucc.asn.au>
parents:
531
diff
changeset
|
128 |
9b1d71310c83
better arg parser. seems close to ready
Matt Johnston <matt@ucc.asn.au>
parents:
531
diff
changeset
|
129 def setup_logging(debug = False): |
9b1d71310c83
better arg parser. seems close to ready
Matt Johnston <matt@ucc.asn.au>
parents:
531
diff
changeset
|
130 level = logging.INFO |
9b1d71310c83
better arg parser. seems close to ready
Matt Johnston <matt@ucc.asn.au>
parents:
531
diff
changeset
|
131 if debug: |
9b1d71310c83
better arg parser. seems close to ready
Matt Johnston <matt@ucc.asn.au>
parents:
531
diff
changeset
|
132 level = logging.DEBUG |
444 | 133 logging.basicConfig(format='%(asctime)s %(message)s', |
566
87d064493714
fix wrong date format. how'd that slip in.
Matt Johnston <matt@ucc.asn.au>
parents:
565
diff
changeset
|
134 datefmt='%d/%m/%Y %I:%M:%S %p', |
532
9b1d71310c83
better arg parser. seems close to ready
Matt Johnston <matt@ucc.asn.au>
parents:
531
diff
changeset
|
135 level=level) |
9b1d71310c83
better arg parser. seems close to ready
Matt Johnston <matt@ucc.asn.au>
parents:
531
diff
changeset
|
136 #logging.getLogger("asyncio").setLevel(logging.DEBUG) |
444 | 137 |
565
26f20cee71be
emergency change sensor config
Matt Johnston <matt@ucc.asn.au>
parents:
556
diff
changeset
|
138 def start(test_mode, nowait): |
26f20cee71be
emergency change sensor config
Matt Johnston <matt@ucc.asn.au>
parents:
556
diff
changeset
|
139 with Tempserver(test_mode, nowait) as server: |
461
1eb68df9f8ab
Add daemon mode with locking, add "disabled" parameter
Matt Johnston <matt@ucc.asn.au>
parents:
459
diff
changeset
|
140 server.run() |
1eb68df9f8ab
Add daemon mode with locking, add "disabled" parameter
Matt Johnston <matt@ucc.asn.au>
parents:
459
diff
changeset
|
141 |
444 | 142 def main(): |
532
9b1d71310c83
better arg parser. seems close to ready
Matt Johnston <matt@ucc.asn.au>
parents:
531
diff
changeset
|
143 parser = argparse.ArgumentParser() |
9b1d71310c83
better arg parser. seems close to ready
Matt Johnston <matt@ucc.asn.au>
parents:
531
diff
changeset
|
144 parser.add_argument('--hup', action='store_true') |
9b1d71310c83
better arg parser. seems close to ready
Matt Johnston <matt@ucc.asn.au>
parents:
531
diff
changeset
|
145 parser.add_argument('--new', action='store_true') |
556 | 146 parser.add_argument('-D', '--daemon', action='store_true') |
532
9b1d71310c83
better arg parser. seems close to ready
Matt Johnston <matt@ucc.asn.au>
parents:
531
diff
changeset
|
147 parser.add_argument('-d', '--debug', action='store_true') |
9b1d71310c83
better arg parser. seems close to ready
Matt Johnston <matt@ucc.asn.au>
parents:
531
diff
changeset
|
148 parser.add_argument('-t', '--test', action='store_true') |
565
26f20cee71be
emergency change sensor config
Matt Johnston <matt@ucc.asn.au>
parents:
556
diff
changeset
|
149 parser.add_argument('--nowait', action='store_true') |
532
9b1d71310c83
better arg parser. seems close to ready
Matt Johnston <matt@ucc.asn.au>
parents:
531
diff
changeset
|
150 args = parser.parse_args() |
9b1d71310c83
better arg parser. seems close to ready
Matt Johnston <matt@ucc.asn.au>
parents:
531
diff
changeset
|
151 |
9b1d71310c83
better arg parser. seems close to ready
Matt Johnston <matt@ucc.asn.au>
parents:
531
diff
changeset
|
152 setup_logging(args.debug) |
444 | 153 |
473
bd29ddb360a5
fix to absolute path for lockfile, --daemon does chdir("/")
Matt Johnston <matt@ucc.asn.au>
parents:
471
diff
changeset
|
154 heredir = os.path.abspath(os.path.dirname(__file__)) |
bd29ddb360a5
fix to absolute path for lockfile, --daemon does chdir("/")
Matt Johnston <matt@ucc.asn.au>
parents:
471
diff
changeset
|
155 pidpath = os.path.join(heredir, 'tempserver.pid') |
471 | 156 pidf = lockfile.pidlockfile.PIDLockFile(pidpath, threaded=False) |
532
9b1d71310c83
better arg parser. seems close to ready
Matt Johnston <matt@ucc.asn.au>
parents:
531
diff
changeset
|
157 |
9b1d71310c83
better arg parser. seems close to ready
Matt Johnston <matt@ucc.asn.au>
parents:
531
diff
changeset
|
158 |
471 | 159 try: |
514 | 160 pidf.acquire(1) |
471 | 161 pidf.release() |
528 | 162 except (lockfile.AlreadyLocked, lockfile.LockTimeout) as e: |
471 | 163 pid = pidf.read_pid() |
532
9b1d71310c83
better arg parser. seems close to ready
Matt Johnston <matt@ucc.asn.au>
parents:
531
diff
changeset
|
164 if args.hup: |
499 | 165 try: |
166 os.kill(pid, signal.SIGHUP) | |
529 | 167 print("Sent SIGHUP to process %d" % pid, file=sys.stderr) |
499 | 168 sys.exit(0) |
169 except OSError: | |
529 | 170 print("Process %d isn't running?" % pid, file=sys.stderr) |
499 | 171 sys.exit(1) |
172 | |
529 | 173 print("Locked by PID %d" % pid, file=sys.stderr) |
499 | 174 |
498 | 175 stale = False |
471 | 176 if pid > 0: |
532
9b1d71310c83
better arg parser. seems close to ready
Matt Johnston <matt@ucc.asn.au>
parents:
531
diff
changeset
|
177 if args.new: |
498 | 178 try: |
179 os.kill(pid, 0) | |
180 except OSError: | |
181 stale = True | |
182 | |
183 if not stale: | |
529 | 184 print("Stopping old tempserver pid %d" % pid, file=sys.stderr) |
498 | 185 os.kill(pid, signal.SIGTERM) |
186 time.sleep(2) | |
187 pidf.acquire(0) | |
188 pidf.release() | |
189 else: | |
190 try: | |
191 os.kill(pid, 0) | |
192 # must still be running PID | |
193 raise e | |
194 except OSError: | |
195 stale = True | |
196 | |
197 if stale: | |
198 # isn't still running, steal the lock | |
529 | 199 print("Unlinking stale lockfile %s for pid %d" % (pidpath, pid), file=sys.stderr) |
498 | 200 pidf.break_lock() |
461
1eb68df9f8ab
Add daemon mode with locking, add "disabled" parameter
Matt Johnston <matt@ucc.asn.au>
parents:
459
diff
changeset
|
201 |
532
9b1d71310c83
better arg parser. seems close to ready
Matt Johnston <matt@ucc.asn.au>
parents:
531
diff
changeset
|
202 if args.hup: |
529 | 203 print("Doesn't seem to be running", file=sys.stderr) |
499 | 204 sys.exit(1) |
205 | |
532
9b1d71310c83
better arg parser. seems close to ready
Matt Johnston <matt@ucc.asn.au>
parents:
531
diff
changeset
|
206 if args.daemon: |
461
1eb68df9f8ab
Add daemon mode with locking, add "disabled" parameter
Matt Johnston <matt@ucc.asn.au>
parents:
459
diff
changeset
|
207 logpath = os.path.join(os.path.dirname(__file__), 'tempserver.log') |
1eb68df9f8ab
Add daemon mode with locking, add "disabled" parameter
Matt Johnston <matt@ucc.asn.au>
parents:
459
diff
changeset
|
208 logf = open(logpath, 'a+') |
1eb68df9f8ab
Add daemon mode with locking, add "disabled" parameter
Matt Johnston <matt@ucc.asn.au>
parents:
459
diff
changeset
|
209 with daemon.DaemonContext(pidfile=pidf, stdout=logf, stderr = logf): |
565
26f20cee71be
emergency change sensor config
Matt Johnston <matt@ucc.asn.au>
parents:
556
diff
changeset
|
210 start(args.test, args.nowait) |
461
1eb68df9f8ab
Add daemon mode with locking, add "disabled" parameter
Matt Johnston <matt@ucc.asn.au>
parents:
459
diff
changeset
|
211 else: |
1eb68df9f8ab
Add daemon mode with locking, add "disabled" parameter
Matt Johnston <matt@ucc.asn.au>
parents:
459
diff
changeset
|
212 with pidf: |
565
26f20cee71be
emergency change sensor config
Matt Johnston <matt@ucc.asn.au>
parents:
556
diff
changeset
|
213 start(args.test, args.nowait) |
444 | 214 |
215 if __name__ == '__main__': | |
216 main() |