changeset 259:26eee8591f61

long polling works
author Matt Johnston <matt@ucc.asn.au>
date Tue, 09 Jun 2015 23:27:44 +0800
parents 6d06795aefbb
children 6fb9d5f654ff
files py/config.py py/configwaiter.py py/fridge.py py/params.py py/requirements.txt py/sensor_ds18b20.py py/tempserver.py py/uploader.py py/utils.py
diffstat 9 files changed, 80 insertions(+), 51 deletions(-) [+]
line wrap: on
line diff
--- a/py/config.py	Mon Jun 08 22:33:04 2015 +0800
+++ b/py/config.py	Tue Jun 09 23:27:44 2015 +0800
@@ -20,9 +20,9 @@
 INTERNAL_TEMPERATURE = '/sys/class/thermal/thermal_zone0/temp'
 
 HMAC_KEY = "a key"
-SERVER_URL = 'https://evil.ucc.asn.au/~matt/templog/update'
+SERVER_URL = 'https://evil.ucc.asn.au/~matt/templog'
 UPDATE_URL = "%s/update" % SERVER_URL
-SETTINGS_URL = "%s/update" % SERVER_URL
+SETTINGS_URL = "%s/get_settings" % SERVER_URL
 
 # site-local values overridden in localconfig, eg WORT_NAME, HMAC_KEY
 try:
--- a/py/configwaiter.py	Mon Jun 08 22:33:04 2015 +0800
+++ b/py/configwaiter.py	Tue Jun 09 23:27:44 2015 +0800
@@ -1,42 +1,58 @@
-class ConfigWaiter(object):
-	""" Waits for config updates from the server. http long polling """
+import asyncio
+import aiohttp
 
-	def __init__(self, server):
-		self.server = server
-		self.epoch_tag = None
-		self.http_session = aiohttp.ClientSession()
+import utils
+from utils import L,D,EX,W,E
+import config
+
+class ConfigWaiter(object):
+    """ Waits for config updates from the server. http long polling """
 
-	@asyncio.coroutine
-	def run(self):
-		# wait until someting has been uploaded (the uploader itself waits 5 seconds)
-		yield from asyncio.sleep(10)
-		while True:
-			yield from self.do()
+    def __init__(self, server):
+        self.server = server
+        self.epoch_tag = None
+        self.http_session = aiohttp.ClientSession()
 
-			# avoid spinning too fast
-			yield from server.sleep(1)
+    @asyncio.coroutine
+    def run(self):
+        # wait until someting has been uploaded (the uploader itself waits 5 seconds)
+        yield from asyncio.sleep(10)
+        while True:
+            yield from self.do()
+
+            # avoid spinning too fast
+            yield from asyncio.sleep(1)
 
-	@asyncio.coroutine
-	def do(self):
-		try:
-			if self.epoch_tag:
-				headers = {'etag': self.epoch_tag}
-			else:
-				headers = None
+    @asyncio.coroutine
+    def do(self):
+        try:
+            if self.epoch_tag:
+                headers = {'etag': self.epoch_tag}
+            else:
+                headers = None
+
+            r = yield from asyncio.wait_for(
+                self.http_session.get(config.SETTINGS_URL, headers=headers), 
+                300)
+            D("waiter status %d" % r.status)
+            if r.status == 200:
+                rawresp = yield from asyncio.wait_for(r.text(), 300)
 
-	        r = yield from asyncio.wait_for(
-	        	self.http_session.get(config.SETTINGS_URL, headers=headers), 
-	        	300)
-	        if r.status == 200:
-		        resp = yield from asyncio.wait_for(r.json(), 300)
+                resp = utils.json_load_round_float(rawresp)
 
-		        self.epoch_tag = resp['epoch_tag']
-		        epoch = self.epoch_tag.split('-')[0]
-		        if self.server.params.receive(resp['params'], epoch):
-		        	self.server.reload_signal(True)
+                self.epoch_tag = resp['epoch_tag']
+                D("waiter got epoch tag %s" % self.epoch_tag)
+                epoch = self.epoch_tag.split('-')[0]
+                if self.server.params.receive(resp['params'], epoch):
+                    self.server.reload_signal(True)
+            elif r.status == 304:
+                pass
+            else:
+                # longer timeout to avoid spinning
+                yield from asyncio.sleep(30)
 
-		 except Exception as e:
-		 	E("Error watching config: %s" % str(e))
+        except Exception as e:
+            E("Error watching config: %s" % str(e))
 
 
 
--- a/py/fridge.py	Mon Jun 08 22:33:04 2015 +0800
+++ b/py/fridge.py	Tue Jun 09 23:27:44 2015 +0800
@@ -22,6 +22,7 @@
 
     def on(self):
         self.turn(True)
+        pass
 
     def off(self):
         self.turn(False)
--- a/py/params.py	Mon Jun 08 22:33:04 2015 +0800
+++ b/py/params.py	Tue Jun 09 23:27:44 2015 +0800
@@ -2,8 +2,9 @@
 import collections
 import json
 import signal
-import io
 import tempfile
+import os
+import binascii
 
 import config
 from utils import W,L,E,EX
@@ -26,7 +27,7 @@
 
     def __init__(self):
         self.update(_FIELD_DEFAULTS)
-        self._epoch = None
+        self._set_epoch(None)
 
     def __getattr__(self, k):
         return self[k]
@@ -36,9 +37,13 @@
         self[k]
         self[k] = v
 
+    def _set_epoch(self, epoch):
+        # since __setattr__ is overridden
+        object.__setattr__(self, '_epoch', epoch)
+
     def _do_load(self, f):
         try:
-            u = json.load(f)
+            u = utils.json_load_round_float(f.read())
         except Exception as e:
             raise self.Error(e)
 
@@ -48,7 +53,8 @@
             if k not in self:
                 raise self.Error("Unknown parameter %s=%s in file '%s'" % (str(k), str(u[k]), getattr(f, 'name', '???')))
         self.update(u)
-        self._epoch = utils.hexnonce()
+        # new epoch, 120 random bits
+        self._set_epoch(binascii.hexlify(os.urandom(15)).decode())
 
         L("Loaded parameters")
         L(self.save_string())
@@ -87,12 +93,12 @@
 
             return ta == tb
 
-        if self.keys() != new_params.keys():
-            diff = self.keys() ^ new_params.keys()
+        if self.keys() != params.keys():
+            diff = self.keys() ^ params.keys()
             E("Mismatching params, %s" % str(diff))
             return False
 
-        for k, v in new_params.items():
+        for k, v in params.items():
             if not same_type(v, self[k]):
                 E("Bad type for %s" % k)
                 return False
@@ -100,17 +106,19 @@
         dir = os.path.dirname(config.PARAMS_FILE)
         try:
             t = tempfile.NamedTemporaryFile(prefix='config',
+                mode='w+t',
                 dir = dir,
                 delete = False)
 
-            t.write(json.dumps(new_params, sort_keys=True, indent=4)+'\n')
+            out = json.dumps(params, sort_keys=True, indent=4)+'\n'
+            t.write(out)
             name = t.name
             t.close()
 
             os.rename(name, config.PARAMS_FILE)
             return True
         except Exception as e:
-            E("Problem: %s" % e)
+            EX("Problem: %s" % e)
             return False
 
     def save_string(self):
--- a/py/requirements.txt	Mon Jun 08 22:33:04 2015 +0800
+++ b/py/requirements.txt	Tue Jun 09 23:27:44 2015 +0800
@@ -4,6 +4,6 @@
 # sha256: 2zFqD89UuXAsr2ymGbdr4l1T9e4Hgbr_C7ni4DVfryQ
 python-daemon==2.0.5
 
-# sha256: IzjIUGznhTrC3377pzGj_QFafuJWGvqw1p3e-0NAP1o
-aiohttp==0.14.4
+# sha256: 6vR5rMmP_uCgKYgkZevyHzwwLhuUpBsWyKWmlbxhSQA
+aiohttp==0.16.3
 
--- a/py/sensor_ds18b20.py	Mon Jun 08 22:33:04 2015 +0800
+++ b/py/sensor_ds18b20.py	Tue Jun 09 23:27:44 2015 +0800
@@ -35,7 +35,7 @@
     def run(self):
         while True:
             yield from self.do()
-            yield from self.server.sleep(config.SENSOR_SLEEP)
+            yield from asyncio.sleep(config.SENSOR_SLEEP)
 
 
     @asyncio.coroutine
--- a/py/tempserver.py	Mon Jun 08 22:33:04 2015 +0800
+++ b/py/tempserver.py	Tue Jun 09 23:27:44 2015 +0800
@@ -18,6 +18,7 @@
 import sensor
 import params
 import uploader
+import configwaiter
 
 
 class Tempserver(object):
@@ -141,7 +142,7 @@
     parser = argparse.ArgumentParser()
     parser.add_argument('--hup', action='store_true')
     parser.add_argument('--new', action='store_true')
-    parser.add_argument('--daemon', action='store_true')
+    parser.add_argument('-D', '--daemon', action='store_true')
     parser.add_argument('-d', '--debug', action='store_true')
     parser.add_argument('-t', '--test', action='store_true')
     args = parser.parse_args()
--- a/py/uploader.py	Mon Jun 08 22:33:04 2015 +0800
+++ b/py/uploader.py	Tue Jun 09 23:27:44 2015 +0800
@@ -22,7 +22,7 @@
         yield from asyncio.sleep(5)
         while True:
             yield from self.do()
-            yield from self.server.sleep(config.UPLOAD_SLEEP)
+            yield from asyncio.sleep(config.UPLOAD_SLEEP)
 
     def get_tosend(self, readings):
         tosend = {}
@@ -62,12 +62,13 @@
         try:
             readings = self.server.take_readings()
             tosend = self.get_tosend(readings)
+            D("tosend >>>%s<<<" % str(tosend))
             nreadings = len(readings)
             yield from self.send(tosend)
             readings = None
             D("Sent updated %d readings" % nreadings)
         except Exception as e:
-            E("Error in uploader: %s" % str(e))
+            EX("Error in uploader: %s" % str(e))
         finally:
             if readings is not None:
                 self.server.pushfront(readings)
--- a/py/utils.py	Mon Jun 08 22:33:04 2015 +0800
+++ b/py/utils.py	Tue Jun 09 23:27:44 2015 +0800
@@ -5,6 +5,7 @@
 import select
 import logging
 import binascii
+import json
 
 D = logging.debug
 L = logging.info
@@ -134,5 +135,6 @@
     except Exception as e:
         return -1
 
-def hexnonce():
-    return binascii.hexlify(os.urandom(120))
+
+def json_load_round_float(s, **args):
+    return json.loads(s,parse_float = lambda f: round(float(f), 2), **args)