comparison py/tempserver.py @ 293:d15dda1b1f76

merge
author Matt Johnston <matt@ucc.asn.au>
date Sat, 06 Jul 2019 18:29:45 +0800
parents 26eee8591f61
children 20c89630be6c
comparison
equal deleted inserted replaced
292:28eb733cb803 293:d15dda1b1f76
3 import sys 3 import sys
4 import os 4 import os
5 import logging 5 import logging
6 import time 6 import time
7 import signal 7 import signal
8 8 import asyncio
9 import gevent 9 import argparse
10 import gevent.monkey 10
11 import lockfile.pidlockfile 11 import lockfile.pidlockfile
12 import daemon 12 import daemon
13 13
14 import utils 14 import utils
15 from utils import L,D,EX,W 15 from utils import L,D,EX,W
16 import fridge 16 import fridge
17 import config 17 import config
18 import sensor_ds18b20 18 import sensor
19 import params 19 import params
20 import uploader 20 import uploader
21 import configwaiter
21 22
22 23
23 class Tempserver(object): 24 class Tempserver(object):
24 def __init__(self): 25 def __init__(self, test_mode):
25 self.readings = [] 26 self.readings = []
26 self.current = (None, None) 27 self.current = (None, None)
27 self.fridge = None 28 self.fridge = None
28 self._wakeup = gevent.event.Event() 29 self._wakeup = asyncio.Event()
29 30 self._test_mode = test_mode
30 # don't patch os, fork() is used by daemonize
31 gevent.monkey.patch_all(os=False, thread=False)
32 31
33 def __enter__(self): 32 def __enter__(self):
34 self.params = params.Params() 33 self.params = params.Params()
35 self.fridge = fridge.Fridge(self) 34 self.fridge = fridge.Fridge(self)
36 self.uploader = uploader.Uploader(self) 35 self.uploader = uploader.Uploader(self)
36 self.configwaiter = configwaiter.ConfigWaiter(self)
37 self.params.load() 37 self.params.load()
38 self.set_sensors(sensor_ds18b20.DS18B20s(self)) 38 self.set_sensors(sensor.make_sensor(self))
39 gevent.signal(signal.SIGHUP, self._reload_signal) 39 asyncio.get_event_loop().add_signal_handler(signal.SIGHUP, self.reload_signal)
40 return self 40 return self
41 41
42 def __exit__(self, exc_type, exc_value, traceback): 42 def __exit__(self, exc_type, exc_value, traceback):
43 L("Exiting, cleanup handler"); 43 L("Exiting, cleanup handler");
44 self.fridge.off() 44 self.fridge.off()
48 if self.fridge is None: 48 if self.fridge is None:
49 raise Exception("Tempserver.run() must be within 'with Tempserver() as server'") 49 raise Exception("Tempserver.run() must be within 'with Tempserver() as server'")
50 50
51 # XXX do these go here or in __enter_() ? 51 # XXX do these go here or in __enter_() ?
52 self.start_time = self.now() 52 self.start_time = self.now()
53 self.fridge.start() 53 runloops = [
54 self.sensors.start() 54 self.fridge.run(),
55 self.uploader.start() 55 self.sensors.run(),
56 56 self.uploader.run(),
57 # won't return. 57 self.configwaiter.run(),
58 while True: 58 ]
59 try: 59
60 gevent.sleep(60) 60 loop = asyncio.get_event_loop()
61 except KeyboardInterrupt: 61 try:
62 break 62 loop.run_until_complete(asyncio.gather(*runloops))
63 except KeyboardInterrupt:
64 print('\nctrl-c')
65 finally:
66 # loop.close() seems necessary otherwise get warnings about signal handlers
67 loop.close()
63 68
64 def now(self): 69 def now(self):
65 return utils.monotonic_time() 70 return utils.monotonic_time()
66 71
67 def set_sensors(self, sensors): 72 def set_sensors(self, sensors):
91 if len(self.readings) > config.MAX_READINGS: 96 if len(self.readings) > config.MAX_READINGS:
92 self.readings = self.readings[-config.MAX_READINGS:] 97 self.readings = self.readings[-config.MAX_READINGS:]
93 98
94 def current_temps(self): 99 def current_temps(self):
95 """ returns (wort_temp, fridge_temp) tuple """ 100 """ returns (wort_temp, fridge_temp) tuple """
101 D("current: %s" % str(self.current))
96 return self.current 102 return self.current
97 103
104 @asyncio.coroutine
98 def sleep(self, timeout): 105 def sleep(self, timeout):
99 """ sleeps for timeout seconds, though wakes if the server's config is updated """ 106 """ sleeps for timeout seconds, though wakes if the server's config is updated """
100 self._wakeup.wait(timeout) 107 # XXX fixme - we should wake on _wakeup but asyncio Condition with wait_for is a bit broken?
101 108 # https://groups.google.com/forum/#!topic/python-tulip/eSm7rZAe9LM
102 def _reload_signal(self): 109 # For now we just sleep, ignore the _wakeup
103 try: 110 try:
104 self.params.load() 111 yield from asyncio.wait_for(self._wakeup.wait(), timeout=timeout)
105 L("Reloaded.") 112 except asyncio.TimeoutError:
113 pass
114
115 def reload_signal(self, no_file = False):
116 try:
117 if not no_file:
118 self.params.load()
119 L("Reloaded.")
106 self._wakeup.set() 120 self._wakeup.set()
107 self._wakeup.clear() 121 self._wakeup.clear()
108 except self.Error, e: 122 except Error as e:
109 W("Problem reloading: %s" % str(e)) 123 W("Problem reloading: %s" % str(e))
110 124
111 def setup_logging(): 125 def test_mode(self):
126 return self._test_mode
127
128 def setup_logging(debug = False):
129 level = logging.INFO
130 if debug:
131 level = logging.DEBUG
112 logging.basicConfig(format='%(asctime)s %(message)s', 132 logging.basicConfig(format='%(asctime)s %(message)s',
113 datefmt='%m/%d/%Y %I:%M:%S %p', 133 datefmt='%m/%d/%Y %I:%M:%S %p',
114 level=logging.INFO) 134 level=level)
115 135 #logging.getLogger("asyncio").setLevel(logging.DEBUG)
116 def start(): 136
117 with Tempserver() as server: 137 def start(test_mode):
138 with Tempserver(test_mode) as server:
118 server.run() 139 server.run()
119 140
120 def main(): 141 def main():
121 setup_logging() 142 parser = argparse.ArgumentParser()
143 parser.add_argument('--hup', action='store_true')
144 parser.add_argument('--new', action='store_true')
145 parser.add_argument('-D', '--daemon', action='store_true')
146 parser.add_argument('-d', '--debug', action='store_true')
147 parser.add_argument('-t', '--test', action='store_true')
148 args = parser.parse_args()
149
150 setup_logging(args.debug)
122 151
123 heredir = os.path.abspath(os.path.dirname(__file__)) 152 heredir = os.path.abspath(os.path.dirname(__file__))
124 pidpath = os.path.join(heredir, 'tempserver.pid') 153 pidpath = os.path.join(heredir, 'tempserver.pid')
125 pidf = lockfile.pidlockfile.PIDLockFile(pidpath, threaded=False) 154 pidf = lockfile.pidlockfile.PIDLockFile(pidpath, threaded=False)
126 do_hup = '--hup' in sys.argv 155
156
127 try: 157 try:
128 pidf.acquire(1) 158 pidf.acquire(1)
129 pidf.release() 159 pidf.release()
130 except (lockfile.AlreadyLocked, lockfile.LockTimeout), e: 160 except (lockfile.AlreadyLocked, lockfile.LockTimeout) as e:
131 pid = pidf.read_pid() 161 pid = pidf.read_pid()
132 if do_hup: 162 if args.hup:
133 try: 163 try:
134 os.kill(pid, signal.SIGHUP) 164 os.kill(pid, signal.SIGHUP)
135 print>>sys.stderr, "Sent SIGHUP to process %d" % pid 165 print("Sent SIGHUP to process %d" % pid, file=sys.stderr)
136 sys.exit(0) 166 sys.exit(0)
137 except OSError: 167 except OSError:
138 print>>sys.stderr, "Process %d isn't running?" % pid 168 print("Process %d isn't running?" % pid, file=sys.stderr)
139 sys.exit(1) 169 sys.exit(1)
140 170
141 print>>sys.stderr, "Locked by PID %d" % pid 171 print("Locked by PID %d" % pid, file=sys.stderr)
142 172
143 stale = False 173 stale = False
144 if pid > 0: 174 if pid > 0:
145 if '--new' in sys.argv: 175 if args.new:
146 try: 176 try:
147 os.kill(pid, 0) 177 os.kill(pid, 0)
148 except OSError: 178 except OSError:
149 stale = True 179 stale = True
150 180
151 if not stale: 181 if not stale:
152 print>>sys.stderr, "Stopping old tempserver pid %d" % pid 182 print("Stopping old tempserver pid %d" % pid, file=sys.stderr)
153 os.kill(pid, signal.SIGTERM) 183 os.kill(pid, signal.SIGTERM)
154 time.sleep(2) 184 time.sleep(2)
155 pidf.acquire(0) 185 pidf.acquire(0)
156 pidf.release() 186 pidf.release()
157 else: 187 else:
162 except OSError: 192 except OSError:
163 stale = True 193 stale = True
164 194
165 if stale: 195 if stale:
166 # isn't still running, steal the lock 196 # isn't still running, steal the lock
167 print>>sys.stderr, "Unlinking stale lockfile %s for pid %d" % (pidpath, pid) 197 print("Unlinking stale lockfile %s for pid %d" % (pidpath, pid), file=sys.stderr)
168 pidf.break_lock() 198 pidf.break_lock()
169 199
170 if do_hup: 200 if args.hup:
171 print>>sys.stderr, "Doesn't seem to be running" 201 print("Doesn't seem to be running", file=sys.stderr)
172 sys.exit(1) 202 sys.exit(1)
173 203
174 if '--daemon' in sys.argv: 204 if args.daemon:
175 logpath = os.path.join(os.path.dirname(__file__), 'tempserver.log') 205 logpath = os.path.join(os.path.dirname(__file__), 'tempserver.log')
176 logf = open(logpath, 'a+') 206 logf = open(logpath, 'a+')
177 with daemon.DaemonContext(pidfile=pidf, stdout=logf, stderr = logf): 207 with daemon.DaemonContext(pidfile=pidf, stdout=logf, stderr = logf):
178 start() 208 start(args.test)
179 else: 209 else:
180 with pidf: 210 with pidf:
181 start() 211 start(args.test)
182 212
183 if __name__ == '__main__': 213 if __name__ == '__main__':
184 main() 214 main()