Mercurial > templog
view py/tempserver.py @ 232:a01b7bccccd3
improve exception handling
author | Matt Johnston <matt@ucc.asn.au> |
---|---|
date | Sat, 11 Apr 2015 21:09:13 +0800 |
parents | e39ed85d87a5 |
children | 19569cb5ed46 |
line wrap: on
line source
#!/home/matt/templog/venv/bin/python import sys import os import logging import time import signal import asyncio import lockfile.pidlockfile import daemon import utils from utils import L,D,EX,W import fridge import config import sensor import params import uploader class Tempserver(object): def __init__(self): self.readings = [] self.current = (None, None) self.fridge = None self._wakeup = asyncio.Condition() def __enter__(self): self.params = params.Params() self.fridge = fridge.Fridge(self) self.uploader = uploader.Uploader(self) self.params.load() self.set_sensors(sensor.make_sensor(self)) asyncio.get_event_loop().add_signal_handler(signal.SIGHUP, self._reload_signal) return self def __exit__(self, exc_type, exc_value, traceback): L("Exiting, cleanup handler"); self.fridge.off() def run(self): if self.fridge is None: raise Exception("Tempserver.run() must be within 'with Tempserver() as server'") # XXX do these go here or in __enter_() ? self.start_time = self.now() tasks = ( self.fridge.run(), self.sensors.run(), self.uploader.run(), ) loop = asyncio.get_event_loop() result_tasks = loop.run_until_complete(asyncio.wait(tasks, return_when=asyncio.FIRST_EXCEPTION)) # use the results so that exceptions get thrown [t.result() for x in result_tasks for t in x] def now(self): return utils.monotonic_time() def set_sensors(self, sensors): if hasattr(self, 'sensors'): self.sensors.kill() self.sensors = sensors self.wort_name = sensors.wort_name() self.fridge_name = sensors.fridge_name() def take_readings(self): ret = self.readings self.readings = [] return ret def pushfront(self, readings): """ used if a caller of take_readings() fails """ self.readings = readings + self.readings # a reading is a map of {sensorname: value}. temperatures # are float degrees def add_reading(self, reading): """ adds a reading at the current time """ D("add_reading(%s)" % str(reading)) self.readings.append( (reading, self.now())) self.current = (reading.get(self.wort_name, None), reading.get(self.fridge_name, None)) if len(self.readings) > config.MAX_READINGS: self.readings = self.readings[-config.MAX_READINGS:] def current_temps(self): """ returns (wort_temp, fridge_temp) tuple """ D("current: %s" % str(self.current)) return self.current @asyncio.coroutine def sleep(self, timeout): """ sleeps for timeout seconds, though wakes if the server's config is updated """ # XXX fixme - we should wake on _wakeup but asyncio Condition with wait_for is a bit broken? # https://groups.google.com/forum/#!topic/python-tulip/eSm7rZAe9LM # For now we just sleep, ignore the _wakeup yield from asyncio.sleep(timeout) @asyncio.coroutine def _reload_signal(self): try: self.params.load() L("Reloaded.") yield from self._wakeup.acquire() self._wakeup.notify_all() self._wakeup.release() except Error as e: W("Problem reloading: %s" % str(e)) def setup_logging(): logging.basicConfig(format='%(asctime)s %(message)s', datefmt='%m/%d/%Y %I:%M:%S %p', level=logging.DEBUG) logging.getLogger("asyncio").setLevel(logging.DEBUG) def start(): with Tempserver() as server: server.run() def main(): setup_logging() heredir = os.path.abspath(os.path.dirname(__file__)) pidpath = os.path.join(heredir, 'tempserver.pid') pidf = lockfile.pidlockfile.PIDLockFile(pidpath, threaded=False) do_hup = '--hup' in sys.argv try: pidf.acquire(1) pidf.release() except (lockfile.AlreadyLocked, lockfile.LockTimeout) as e: pid = pidf.read_pid() if do_hup: try: os.kill(pid, signal.SIGHUP) print("Sent SIGHUP to process %d" % pid, file=sys.stderr) sys.exit(0) except OSError: print("Process %d isn't running?" % pid, file=sys.stderr) sys.exit(1) print("Locked by PID %d" % pid, file=sys.stderr) stale = False if pid > 0: if '--new' in sys.argv: try: os.kill(pid, 0) except OSError: stale = True if not stale: print("Stopping old tempserver pid %d" % pid, file=sys.stderr) os.kill(pid, signal.SIGTERM) time.sleep(2) pidf.acquire(0) pidf.release() else: try: os.kill(pid, 0) # must still be running PID raise e except OSError: stale = True if stale: # isn't still running, steal the lock print("Unlinking stale lockfile %s for pid %d" % (pidpath, pid), file=sys.stderr) pidf.break_lock() if do_hup: print("Doesn't seem to be running", file=sys.stderr) sys.exit(1) if '--daemon' in sys.argv: logpath = os.path.join(os.path.dirname(__file__), 'tempserver.log') logf = open(logpath, 'a+') with daemon.DaemonContext(pidfile=pidf, stdout=logf, stderr = logf): start() else: with pidf: start() if __name__ == '__main__': main()