changeset 12:87d9d862ecc4

Avoid using pylab helper functions to reduce the magic level.
author darius
date Tue, 23 Oct 2007 01:01:48 +0000
parents 17449d52d5e5
children 3dad7161af87
files logplot.py
diffstat 1 files changed, 154 insertions(+), 55 deletions(-) [+]
line wrap: on
line diff
--- a/logplot.py	Tue Oct 23 01:00:19 2007 +0000
+++ b/logplot.py	Tue Oct 23 01:01:48 2007 +0000
@@ -1,74 +1,173 @@
 #!/usr/bin/env python
 
-import re, datetime, time, pylab, matplotlib
+import re, datetime, time, sys, cPickle, numpy
+import matplotlib
+import matplotlib.figure
+import matplotlib.backends.backend_agg
+import matplotlib.dates
+
 daterestr = '([0-9]{4}/[0-9]{2}/[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2}): '
 targtre = re.compile(daterestr + 'target temperature - (-?[0-9]+\.[0-9]+)')
-linere = re.compile(daterestr + '(-?[0-9]+\.[0-9]+)\s+(-?[0-9]+\.[0-9]+)\s+(-?[0-9]+\.[0-9]+)\s+([a-z-]+)\s+([a-z-]+)')
+templre = re.compile(daterestr + '(-?[0-9]+\.[0-9]+)\s+(-?[0-9]+\.[0-9]+)\s+(-?[0-9]+\.[0-9]+)\s+([a-z-]+)\s+([a-z-]+)')
 datefmt = '%Y/%m/%d %H:%M:%S'
 tz = matplotlib.pytz.timezone('Australia/Adelaide')
 
-targetTemp = 18.0
-logfile = '/tmp/beermon.log'
-start = time.mktime(time.strptime("2007/09/24 00:00:00", datefmt))
-#end = time.mktime(time.strptime("2007/09/28 00:00:00", datefmt))
+#start = time.mktime(time.strptime("2007/09/22 14:00:00", datefmt))
+start = None
+#end = time.mktime(time.strptime("2007/09/30 14:00:00", datefmt))
 end = None
 
-f = open(logfile)
+def parselog(f, start, end, times, fermTemps, fridgeTemps, ambTemps, targetTemps, states):
+    targetTemp = 18.0 # Have to guess this..
 
-times = []
-fermTemps = []
-fridgeTemps = []
-ambTemps = []
-states = []
-targetTemps = []
+    for line in f:
+        # Log line?
+        m = templre.match(line)
+        if (m == None):
+            # No, might be a target temp line
+            m = targtre.match(line)
+            if (m != None):
+                # Parse date portion
+                t = datetime.datetime(*time.strptime(m.group(1), datefmt)[0:6])
+                if ((start != None and t < start) or (end != None and t > end)):
+                    continue
 
-for line in f:
-    m = linere.match(line)
-    if (m == None):
-        m = targtre.match(line)
-        if (m != None):
-            t = time.mktime(time.strptime(m.group(1), datefmt))
-            if (t < start or t > end):
+                targetTemp = float(m.group(2))
+                continue
+            else:
+                # Isn't anything, next!
                 continue
 
-            targetTemp = float(m.group(2))
+        # Parse date portion
+        t = datetime.datetime(*time.strptime(m.group(1), datefmt)[0:6])
+        if ((start != None and t < start) or (end != None and t > end)):
             continue
+    
+        fermTemp = float(m.group(2))
+        fridgeTemp = float(m.group(3))
+        ambTemp = float(m.group(4))
+        state = m.group(5)
+
+        # Convert to Gregorian days (float) because that is what matplotlib uses
+        times.append(matplotlib.dates._to_ordinalf(t))
+        fermTemps.append(fermTemp)
+        fridgeTemps.append(fridgeTemp)
+        ambTemps.append(ambTemp)
+        targetTemps.append(targetTemp)
+        if (state == 'heat'):
+            states.append(10)
+        elif (state == 'idle'):
+            states.append(8)
         else:
-            continue
+            states.append(6)
+
+    return(times, fermTemps, fridgeTemps, ambTemps, targetTemps, states)
 
-    t = time.mktime(time.strptime(m.group(1), datefmt))
-    if (start != None and t < start):
-        continue
+def doplot(times, fermTemps, fridgeTemps, ambTemps, targetTemps, states, outfile):
+    assert(len(times) == len(fermTemps))
+    assert(len(times) == len(fridgeTemps))
+    assert(len(times) == len(ambTemps))
+    assert(len(times) == len(targetTemps))
+    assert(len(times) == len(states))
+    nrec = len(times)
+    print "nrec = %d" % (nrec)
+    newsz = [1000]
+    then = time.time()
+    times = rebin(numpy.array(times), newsz)
+    fermTemps = rebin(numpy.array(fermTemps), newsz)
+    fridgeTemps = rebin(numpy.array(fridgeTemps), newsz)
+    ambTemps = rebin(numpy.array(ambTemps), newsz)
+    targetTemps = rebin(numpy.array(targetTemps), newsz)
+    states = rebin(numpy.array(states, 'f'), newsz)
+    now = time.time()
+    print "Rebinning took %.2f msec" % ((now - then) * 1000)
+
+    title = "%s to %s (%d -> %d pts)" % (matplotlib.dates._from_ordinalf(times[0]).strftime(datefmt),
+                                         matplotlib.dates._from_ordinalf(times[-1]).strftime(datefmt),
+                                         nrec, len(times))
+    print title
+
+    then = time.time()
+    # A figure
+    fig = matplotlib.figure.Figure(figsize = (9.0, 5.0))
+
+    # Create a plot, get some axes
+    ax = fig.add_subplot(111)
+
+    # Plot the data onto the axes
+    p = ax.plot(times, fermTemps, times, fridgeTemps, times, ambTemps, times, targetTemps, times, states, '.')
+    ax.set_title(title, weight = 'bold')
+    ax.set_autoscale_on(True)
     
-    if (end != None and t > end):
-        continue
-    
-    fermTemp = float(m.group(2))
-    fridgeTemp = float(m.group(3))
-    ambTemp = float(m.group(4))
-    state = m.group(5)
+    # Add a formatter
+    hoursFmt = matplotlib.dates.DateFormatter('%d %b\n%H:%M')
+    ax.xaxis.set_major_formatter(hoursFmt)
     
-    times.append(t / (24 * 60 * 60)) # Is in float days
-    fermTemps.append(fermTemp)
-    fridgeTemps.append(fridgeTemp)
-    ambTemps.append(ambTemp)
-    states.append(state)
-    targetTemps.append(targetTemp)
+    # Prettify it
+    ax.grid(True)
+    ax.set_xlabel('Date')
+    ax.set_ylabel('Temperature(C)')
+    
+    # Set the legend
+    legend = ax.legend((p[0], p[1], p[2], p[3], p[4]), ('Fermenter', 'Fridge', 'Ambient', 'Target', 'State'), 'best')
+    
+    # Render the figure
+    canvas = matplotlib.backends.backend_agg.FigureCanvasAgg(fig)
+    canvas.print_figure(outfile, dpi = 96)
+    now = time.time()
+    print "Plotting took %.2f msec" % ((now - then) * 1000)
+
+def main():
+    logf = open(sys.argv[1], 'r')
+    outfile = sys.argv[1][:-3] + 'png'
 
-print "From %s to %s" % (time.asctime(time.localtime(times[0] * (24 * 60 * 60))),
-                         time.asctime(time.localtime(times[-1] * (24 * 60 * 60))))
-fig = pylab.figure()
-ax = fig.add_subplot(111)
-p = ax.plot(times, fermTemps, times, fridgeTemps, times, ambTemps, times, targetTemps)
-ax.set_autoscale_on(True)
-hoursFmt = pylab.DateFormatter('%d %b %H:%M', tz = tz)
-ax.xaxis.set_major_formatter(hoursFmt)
-labels = ax.get_xticklabels()
-pylab.setp(labels, 'rotation', 5.0)
-ax.grid(True)
-ax.set_xlabel('time(UTC)')
-ax.set_ylabel('Temperature(C)')
-pylab.legend((p[0], p[1], p[2], p[3]), ('Fermenter', 'Fridge', 'Ambient', 'Target'), 'best')
-#pylab.legend((p[0], p[1], p[2]), ('Fermenter', 'Fridge', 'Ambient'), 'best')
-#pylab.axhline(y = targetTemp)
-pylab.show()
+    try:
+        # Try reading the pickled data
+        p = cPickle.Unpickler(open("data.pck", "r"))
+        then = time.time()
+        times, fermTemps, fridgeTemps, ambTemps, targetTemps, states, offs = p.load()
+        now = time.time()
+        print "Unpickle took %.2f msec" % ((now - then) * 1000)
+        # Seek up to the last place we parsed
+        logf.seek(offs)
+        del p
+        print "Parsed pickle OK"
+        
+    except (IOError, cPickle.UnpicklingError, ValueError):
+        # Either the pickle doesn't exist, or it's invalid, start anew
+        print "Can't read pickle, starting from scratch"
+        times = []
+        fermTemps = []
+        fridgeTemps = []
+        ambTemps = []
+        states = []
+        targetTemps = []
+    
+    then = time.time()
+    parselog(logf, start, end, times, fermTemps, fridgeTemps, ambTemps, targetTemps, states)
+    now = time.time()
+    print "Parselog took %.2f msec" % ((now - then) * 1000)
+
+    # Write the pickle back out
+    p = cPickle.Pickler(open("data.pck", "w"), protocol = -1)
+    then = time.time()
+    p.dump((times, fermTemps, fridgeTemps, ambTemps, targetTemps, states, logf.tell()))
+    now = time.time()
+    print "Pickle took %.2f msec" % ((now - then) * 1000)
+    del p
+
+    # Do the plot
+    doplot(times, fermTemps, fridgeTemps, ambTemps, targetTemps, states, outfile)
+
+def rebin(a, newshape):
+    '''Rebin an array to a new shape.
+    '''
+    assert len(a.shape) == len(newshape)
+    
+    slices = [slice(0, old, float(old)/new) for old,new in zip(a.shape,newshape) ]
+    coordinates = numpy.mgrid[slices]
+    indices = coordinates.astype('i')   #choose the biggest smaller integer index
+    return a[tuple(indices)]
+
+if __name__ == "__main__":  
+    main()