changeset 328:46070aaf29ea

A bit of work on the server python
author Matt Johnston <matt@ucc.asn.au>
date Sat, 26 May 2012 10:17:27 +0800
parents 5639c74f2cbb
children 740438e21ea0
files main.c server/ts.py server/utils.py
diffstat 3 files changed, 191 insertions(+), 33 deletions(-) [+]
line wrap: on
line diff
--- a/main.c	Tue May 22 23:45:16 2012 +0800
+++ b/main.c	Sat May 26 10:17:27 2012 +0800
@@ -34,6 +34,7 @@
 #define VALUE_NOSENSOR -9000
 #define VALUE_BROKEN -8000
 
+// limited to uint16_t for now
 #define COMMS_WAKE 3600
 #define WAKE_SECS 30
 
@@ -69,6 +70,10 @@
 static uint16_t n_measurements;
 // stored as decidegrees
 static int16_t measurements[NUM_MEASUREMENTS][MAX_SENSORS];
+static uint32_t first_measurement_clock;
+// last_measurement_clock is redundant but checks that we're not missing
+// samples
+static uint32_t last_measurement_clock;
 
 // boolean flags
 static uint8_t need_measurement;
@@ -218,7 +223,9 @@
     eeprom_read(n_sensors, n_sensors);
 
     fprintf_P(crc_stdout, PSTR("START\n"));
-    fprintf_P(crc_stdout, PSTR("time=%lu\n"), clock_epoch);
+    fprintf_P(crc_stdout, PSTR("now=%lu\n"), clock_epoch);
+    fprintf_P(crc_stdout, PSTR("first_time=%lu\n"), first_measurement_clock);
+    fprintf_P(crc_stdout, PSTR("last_time=%lu\n"), last_measurement_clock);
     fprintf_P(crc_stdout, PSTR("sensors=%d\n"), n_measurements);
     for (uint8_t s = 0; s < n_sensors; s++)
     {
@@ -252,7 +259,8 @@
 static void
 cmd_btoff()
 {
-    printf_P(PSTR("Turning off\n"));
+    uint16_t next_wake = COMMS_WAKE - comms_count;
+    printf_P(PSTR("off:%d\n"), next_wake);
     _delay_ms(50);
     comms_timeout = 0;
 }
@@ -462,11 +470,15 @@
 {
     char c = UDR0;
     uart_putchar(c, NULL);
-    if (c == '\r')
+    // XXX move this out of interrupt handler
+    if (c == '\r' || c == '\n')
     {
-        readbuf[readpos] = '\0';
-        read_handler();
-        readpos = 0;
+        if (readpos > 0)
+        {
+            readbuf[readpos] = '\0';
+            read_handler();
+            readpos = 0;
+        }
     }
     else
     {
@@ -625,6 +637,13 @@
         }
         measurements[n_measurements][s] = decicelsius;
     }
+
+    if (n_measurements == 0)
+    {
+        first_measurement_clock = clock_epoch;
+    }
+    last_measurement_clock = clock_epoch;
+
     n_measurements++;
     //do_adc_335();
 }
--- a/server/ts.py	Tue May 22 23:45:16 2012 +0800
+++ b/server/ts.py	Sat May 26 10:17:27 2012 +0800
@@ -1,7 +1,14 @@
 #!/usr/bin/env python2.7
 
+BTADDR = "00:12:03:27:70:88"
+SLEEP_TIME = 180
+
 import sys
 import httplib
+import time
+import traceback
+
+from utils import monotonic_time, retry
 
 lightblue = None
 try:
@@ -9,8 +16,6 @@
 except ImportError:
     import bluetooth
 
-BTADDR = "00:12:03:27:70:88"
-
 def get_socket(addr):
     if lightblue:
         s = lightblue.socket()
@@ -19,40 +24,32 @@
         s = bluetooth.BluetoothSocket( bluetooth.RFCOMM )
         s.connect((addr, 1))
 
+    s.setnonblocking(True)
+
     return s
 
 # from http://blog.stalkr.net/2011/04/pctf-2011-32-thats-no-bluetooth.html
 def crc16(buff, crc = 0, poly = 0x8408):
-    l = len(buff)
-    i = 0
-    while i < l:
-        ch = ord(buff[i])
-        uc = 0
-        while uc < 8:
-            if (crc & 1) ^ (ch & 1):
-                crc = (crc >> 1) ^ poly
-            else:
-                crc >>= 1
-            ch >>= 1
-            uc += 1
-        i += 1
-    return crc
+    l = len(buff)
+    i = 0
+    while i < l:
+        ch = ord(buff[i])
+        uc = 0
+        while uc < 8:
+            if (crc & 1) ^ (ch & 1):
+                crc = (crc >> 1) ^ poly
+            else:
+                crc >>= 1
+            ch >>= 1
+            uc += 1
+        i += 1
+        return crc
 
 
+@retry()
 def fetch(sock):
     sock.send("fetch\n")
 
-    def readline(self):
-        buf = ''
-        while true:
-            c = self.recv(1)
-            if c == '\r':
-                continue
-
-            buf.append(c)
-            if c == '\n':
-                return buf
-
     crc = 0
 
     lines = []
@@ -93,3 +90,70 @@
         return None
 
     return ''.join(lines)
+
+@retry()
+def turn_off(sock):
+    sock.send("btoff\n");
+    # read newline
+    l = readline(sock)
+    if not l:
+        print>>sys.stderr, "Bad response to btoff\n"
+        return None
+
+    off, next_wake = l.rstrip().split(':')
+    if off != 'Off':
+        print>>sys.stderr, "Bad response to btoff '%s'\n" % l
+
+    return int(next_wake)
+
+
+def do_comms(sock):
+    d = None
+    # serial could be unreliable, try a few times
+    for i in range(FETCH_TRIES):
+        d = fetch(sock)
+        if d:
+            break
+        time.sleep(1)
+    if not d:
+        return
+
+    res = send_results()
+    if not res:
+        return
+
+    clear_meas(sock)
+
+    next_wake = turn_off(sock)
+    sock.close()
+    return next_wake
+
+testcount = 0
+
+def sleep_for(secs):
+    until = monotonic_time + secs
+    while True:
+        length = until < monotonic_time()
+        if length <= 0:
+            return
+        time.sleep(length)
+
+def main():
+
+    while True:
+        sock = get_socket()
+        sleep_time = SLEEP_TIME
+        if sock:
+            next_wake = None
+            try:
+                next_wake = do_comms(sock)
+            except Exception, e:
+                print>>sys.stderr, "Error in do_comms:"
+                traceback.print_last(file=sys.stderr)
+            if next_wake:
+                sleep_time = min(next_wake, sleep_time)
+
+        sleep_for(sleep_time)
+
+if __name__ == '__main__':
+    main()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/server/utils.py	Sat May 26 10:17:27 2012 +0800
@@ -0,0 +1,75 @@
+import os
+import sys
+import ctypes
+import time
+import select
+
+DEFAULT_TRIES = 3
+
+__all__ = ('monotonic_time', 'retry')
+
+clock_gettime = None
+no_clock_gettime = False
+def monotonic_time():
+    global clock_gettime
+    global no_clock_gettime
+    if no_clock_gettime:
+        return time.time()
+
+    class timespec(ctypes.Structure):
+        _fields_ = [
+            ('tv_sec', ctypes.c_long),
+            ('tv_nsec', ctypes.c_long)
+        ]
+    if not clock_gettime:
+        try:
+            librt = ctypes.CDLL('librt.so.0', use_errno=True)
+            clock_gettime = librt.clock_gettime
+            clock_gettime.argtypes = [ctypes.c_int, ctypes.POINTER(timespec)]
+        except:
+            print>>sys.stderr, "No clock_gettime(), using fake fallback."
+            no_clock_gettime = True
+            return time.time()
+        
+    t = timespec()
+    CLOCK_MONOTONIC = 1 # see <linux/time.h>
+
+    if clock_gettime(CLOCK_MONOTONIC, ctypes.pointer(t)) != 0:
+        errno_ = ctypes.get_errno()
+        raise OSError(errno_, os.strerror(errno_))
+    return t.tv_sec + t.tv_nsec * 1e-9
+
+# decorator, tries a number of times, returns None on failure, sleeps between
+# Must be used as "@retry()" if arguments are defaulted
+def retry(retries=DEFAULT_TRIES, try_time = 1):
+    def inner(func):
+        print "inner"
+        def new_f(*args, **kwargs):
+            print "newf"
+            for i in range(retries):
+                print "retry %d" % i
+                d = func(*args, **kwargs)
+                if d:
+                    return d
+                time.sleep(try_time)
+        new_f.func_name = func.func_name
+        return new_f
+    return inner
+
+def readline(sock):
+    timeout = READLINE_SELECT_TIMEOUT
+    buf = ''
+    while true:
+        (rlist, wlist, xlist) = select.select([sock], [], [], timeout)
+        if sock not in rlist:
+            # hit timeout
+            return None
+
+        c = sock.recv(1)
+        if c == '\r':
+            continue
+
+        buf.append(c)
+        if c == '\n':
+            return buf
+