changeset 443:bca470d153fd

a bit more, and some tests
author Matt Johnston <matt@ucc.asn.au>
date Mon, 26 Nov 2012 23:21:03 +0800
parents 02318c9660cd
children c5629d79b4ac
files py/config.py py/fridge.py py/sensor_ds18b20.py py/tempserver.py py/test.py py/utils.py
diffstat 6 files changed, 119 insertions(+), 60 deletions(-) [+]
line wrap: on
line diff
--- a/py/config.py	Tue Nov 20 22:03:10 2012 +0800
+++ b/py/config.py	Mon Nov 26 23:21:03 2012 +0800
@@ -1,2 +1,6 @@
 FRIDGE_GPIO = '/sys/devices/virtual/gpio/gpio5'
 FRIDGE_SLEEP = 60
+
+SENSOR_SLEEP = 120
+
+UPLOAD_SLEEP = 300
--- a/py/fridge.py	Tue Nov 20 22:03:10 2012 +0800
+++ b/py/fridge.py	Mon Nov 26 23:21:03 2012 +0800
@@ -1,14 +1,17 @@
 # -*- coding: utf-8 -*-
-from utils import L,W,E
+from utils import L,W,E,EX
 import config
+import gevent
 
-class Fridge(object):
+class Fridge(gevent.Greenlet):
 
     OVERSHOOT_MAX_DIV = 1800.0 # 30 mins
     FRIDGE_AIR_MIN_RANGE = 4 # ÂșC
     FRIDGE_AIR_MAX_RANGE = 4
 
-    def __init__(self):
+    def __init__(self, server):
+        gevent.Greenlet.__init__(self)
+        self.server = server
         self.setup_gpio()
         self.wort_valid_clock = 0
         self.fridge_on_clock = 0
@@ -45,22 +48,22 @@
                 % (self.value_file.name, buf))
         return True
 
-    def run(self, server):
-
+    # greenlet subclassed
+    def _run(self):
         while True:
-            self.do(server)
+            self.do()
             gevent.sleep(config.FRIDGE_SLEEP)
 
-    def do(self, server):
+    def do(self)
         """ this is the main fridge control logic """
-        wort, fridge, ambient = server.current_temps()
+        wort, fridge = self.server.current_temps()
 
         fridge_min = params.fridge_setpoint - self.FRIDGE_AIR_MIN_RANGE
         fridge_max = params.fridge_setpoint + self.FRIDGE_AIR_MAX_RANGE
 
         wort_max = params.fridge_setpoint + params.fridge_difference
 
-        off_time = server.now() - self.fridge_off_clock
+        off_time = self.server.now() - self.fridge_off_clock
 
         if off_time < config.FRIDGE_DELAY:
             L("fridge skipping, too early")
@@ -68,10 +71,10 @@
 
         # handle broken wort sensor
         if wort is not None:
-            self.wort_valid_clock = server.now()
+            self.wort_valid_clock = self.server.now()
         else:
             W("Invalid wort sensor")
-            invalid_time = server.now() - self.wort_valid_clock
+            invalid_time = self.server.now() - self.wort_valid_clock
             if invalid_time < config.FRIDGE_WORT_INVALID_TIME:
                 W("Has only been invalid for %d, waiting" % invalid_time)
                 return
@@ -81,7 +84,7 @@
 
         if self.is_on():
             turn_off = False
-            on_time = server.now() - self.fridge_on_clock
+            on_time = self.server.now() - self.fridge_on_clock
 
             overshoot = 0
             if on_time > params.overshoot_delay:
@@ -96,14 +99,14 @@
                     turn_off = True
             else:
                 # wort sensor is broken
-                if fridge is not None and last_fridge < fridge_min:
+                if fridge is not None and fridge < fridge_min:
                     W("fridge off fallback")
                     turn_off = True
 
             if turn_off:
                 L("Turning fridge off")
                 self.off()
-                self.fridge_off_clock = server.now()
+                self.fridge_off_clock = self.server.now()
 
         else:
             # fridge is off
@@ -121,4 +124,4 @@
             if turn_on:
                 L("Turning fridge on")
                 self.on()
-                fridge_on_clock = server.now()
+                fridge_on_clock = self.server.now()
--- a/py/sensor_ds18b20.py	Tue Nov 20 22:03:10 2012 +0800
+++ b/py/sensor_ds18b20.py	Mon Nov 26 23:21:03 2012 +0800
@@ -1,21 +1,59 @@
 #!/usr/bin/env python2.7
 
-class DS18B20s(object):
+import gevent
+import config
+import re
+from utils import L,W,E,EX
+
+class DS18B20s(gevent.Greenlet):
+
+    THERM_RE = re.compile('.* YES\n.*t=(.*)\n', re.MULTILINE)
 
-    def __init__(self):
-        # query the bus 
-        pass
+    def __init__(self, server):
+        gevent.Greenlet.__init__(self)
+        self.server = server
+        # XXX set up paths
+        # XXX set up drain etc
+
+    def _run(self):
+        while True:
+            self.do()
+            gevent.sleep(config.SENSOR_SLEEP)
+
+    def sensor_path(self, s):
+        return os.path.join(self.master_dir, s)
 
-    def get_sensors(self):
-        """ Returns a sequence of sensorname """
-        pass
+    def do_sensor_name(self, s, contents = None):
+        try:
+            if contents is None:
+                fn = os.path.join(self.sensor_path(s), 'w1_slave')
+                f = open(fn, 'r')
+                contents = f.read()
+            match = self.THERM_RE.match(contents)
+            if match is None:
+                return None
+            temp = int(match.groups(1)[0])
+            return temp / 1000.0
+        except Exception, e:
+            EX("Problem reading sensor '%s': %s" % (s, str(e)))
+            return None
 
-    def read(self):
-        """ Returns a map of sensorname->temperature """
-        pass
+    def do(self):
+        vals = {}
+        for n in self.sensor_names():
+                value = do_sensor(n)
+                if value is not None:
+                    vals[n] = value
+
+        self.server.add_reading(vals)
+
+    def sensor_names(self):
+        """ Returns a sequence of sensorname """
+        return [d for d in os.listdir(self.master_dir) if
+            os.stat(sensor_path(d)).st_mode & stat.S_ISDIR]
 
     def wort_name(self):
-        pass
+        return config.WORT_NAME
 
     def fridge_name(self):
-        pass
+        return config.FRIDGE_NAME
--- a/py/tempserver.py	Tue Nov 20 22:03:10 2012 +0800
+++ b/py/tempserver.py	Mon Nov 26 23:21:03 2012 +0800
@@ -13,9 +13,18 @@
 
         self.start_time = utils.monotonic_time()
 
-        self.fridge = fridge.Fridge()
-        self.fridge.run(self)
+        self.fridge = fridge.Fridge(self)
+        self.fridge.start()
+
+        self.set_sensors(sensor_ds18b20.DS18B20s(self))
 
+    def set_sensors(self, sensors):
+        if self.hasattr(self, 'sensors'):
+            self.sensors.kill()
+        self.sensors = sensors
+        self.wort_name = sensors.wort_name()
+        self.fridge_name = sensors.fridge_name()
+        self.sensor_names = sensors.sensor_names()
 
     def take_readings(self):
         ret = self.readings
@@ -26,40 +35,14 @@
         """ used if a caller of take_readings() fails """
         self.readings = pushback + 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 """
         self.readings.append( (reading, utils.monotonic_time()))
+        self.current = (reading.get(self.wort_name, None),
+                    reading.get(self.fridge_name, None))
 
     def current_temps(self):
-        """ returns (wort_temp, fridge_temp, ambient_temp) tuple """
+        """ returns (wort_temp, fridge_temp) tuple """
         return current
-
-    def set_current(self, wort, fridge, ambient):
-        current = (wort, fridge, ambient)
-
-    def now(self):
-        return utils.monotonic_time()
-
-def spawn_timer(seconds, fn, *args, **kwargs):
-    def loop():
-        while True:
-            fn(*args, **kwargs)
-            gevent.sleep(seconds)
-    return gevent.spawn(loop)
-
-def setup():
-    pass
-
-def main():
-    setup()
-
-    def p(x):
-        print "hello %s" % x
-
-    spawn_timer(2, p, 'one')
-
-    gevent.sleep(20)
-
-
-if __name__ == '__main__':
-    main()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/py/test.py	Mon Nov 26 23:21:03 2012 +0800
@@ -0,0 +1,27 @@
+import unittest
+import sensor_ds18b20
+
+class TestSensors(unittest.TestCase):
+    def setUp(self):
+        self.sensors = sensor_ds18b20.DS18B20s(None)
+
+    def test_re(self):
+        f1 = """6e 01 4b 46 7f ff 02 10 71 : crc=71 YES
+6e 01 4b 46 7f ff 02 10 71 t=22875
+"""
+        val = self.sensors.do_sensor_name('blank', f1)
+        self.assertEqual(val, 22.875)
+
+        f2 = """6e 01 4b 46 7f ff 02 10 71 : crc=71 NO
+6e 01 4b 46 7f ff 02 10 71 t=22875
+"""
+        val = self.sensors.do_sensor_name('blank', f2)
+        self.assertEqual(val, None)
+
+        f3 = """6e 01 4b 46 7f ff 02 10 71 : crc=71 YES
+6e 01 4b 46 7f ff 02 10 71 t=-1
+"""
+        val = self.sensors.do_sensor_name('blank', f3)
+        self.assertEqual(val, -0.001)
+
+unittest.main()
--- a/py/utils.py	Tue Nov 20 22:03:10 2012 +0800
+++ b/py/utils.py	Mon Nov 26 23:21:03 2012 +0800
@@ -12,6 +12,10 @@
 DEFAULT_TRIES = 3
 READLINE_SELECT_TIMEOUT = 1
 
+def EX(msg, *args, **kwargs):
+    kwargs['exc_info'] = True
+    logging.error(msg, *args, **kwargs)
+
 clock_gettime = None
 no_clock_gettime = True
 def monotonic_time():