view logplot.py @ 19:9264a9cacfde

Best isn't, use lower left.
author darius
date Tue, 29 Jan 2008 11:42:04 +0000
parents 87d9d862ecc4
children
line wrap: on
line source

#!/usr/bin/env python

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]+)')
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')

#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

def parselog(f, start, end, times, fermTemps, fridgeTemps, ambTemps, targetTemps, states):
    targetTemp = 18.0 # Have to guess this..

    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

                targetTemp = float(m.group(2))
                continue
            else:
                # Isn't anything, next!
                continue

        # 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:
            states.append(6)

    return(times, fermTemps, fridgeTemps, ambTemps, targetTemps, states)

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)
    
    # Add a formatter
    hoursFmt = matplotlib.dates.DateFormatter('%d %b\n%H:%M')
    ax.xaxis.set_major_formatter(hoursFmt)
    
    # 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'), 'lower left')
    
    # 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'

    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()