view web/log.py @ 363:99e7c2958a06

merge
author Matt Johnston <matt@ucc.asn.au>
date Sun, 24 Jun 2012 23:48:25 +0800
parents 8fea6144951b
children 62112fc2af21
line wrap: on
line source

#:vim:et:ts=4:sts=4:sw=4:
import rrdtool
import os
import os.path
import sys
import glob
import hashlib
import tempfile
import time
import syslog
import sqlite3
import traceback
from colorsys import hls_to_rgb

import config

def sensor_rrd_path(s):
    return '%s/sensor_%s.rrd' % (config.DATA_PATH, s)

# returns (path, sensor_name) tuples
def all_sensors():
    return [(r, os.path.basename(r[:-4])) 
        for r in glob.glob('%s/*.rrd' % config.DATA_PATH)]

def create_rrd(sensor_id):
    # start date of 10 seconds into 1970 is used so that we can
    # update with prior values straight away.
    args = [sensor_rrd_path(sensor_id), 
                    '--start', '10',
                    '--step', '300',
                    'DS:temp:GAUGE:600:-10:100',
                    'RRA:AVERAGE:0.5:1:1051200']

    rrdtool.create(*args)

# stolen from viewmtn, stolen from monotone-viz
def colour_from_string(str):
    def f(off):
        return ord(hashval[off]) / 256.0
    hashval = hashlib.sha1(str).digest()
    hue = f(5)
    li = f(1) * 0.15 + 0.55
    sat = f(2) * 0.5 + .5
    return ''.join(["%.2x" % int(x * 256) for x in hls_to_rgb(hue, li, sat)])

def graph_png(start, length):
    rrds = all_sensors()

    graph_args = []
    for n, (rrdfile, sensor) in enumerate(rrds):
        vname = 'temp%d' % n
        graph_args.append('DEF:%(vname)s=%(rrdfile)s:temp:AVERAGE' % locals())
        width = config.LINE_WIDTH
        legend = config.SENSOR_NAMES.get(sensor, sensor)
        colour = config.SENSOR_COLOURS.get(legend, colour_from_string(sensor))
        graph_args.append('LINE%(width)f:%(vname)s#%(colour)s:%(legend)s' % locals())

    tempf = tempfile.NamedTemporaryFile()
    args = [tempf.name, '-s', str(int(start)),
        '-e', str(int(start+length)),
        '-w', str(config.GRAPH_WIDTH),
        '-h', str(config.GRAPH_HEIGHT),
        '--slope-mode',
        '--border', '0',
        '--y-grid', '1:1',
        '--grid-dash', '1:0',
        '--color', 'GRID#00000000',
        '--color', 'MGRID#aaaaaa',
        '--color', 'BACK#ffffff',
        '--imgformat', 'PNG'] \
        + graph_args
    if config.GRAPH_FONT:
        args += ['--font', 'DEFAULT:11:%s' % config.GRAPH_FONT]
    print>>sys.stderr, args
    rrdtool.graph(*args)
    return tempf.read()

def sensor_update(sensor_id, measurements, first_real_time, time_step):
    try:
        open(sensor_rrd_path(sensor_id))
    except IOError, e:
        create_rrd(sensor_id)

    print>>sys.stderr, sensor_id, measurements, first_real_time, time_step

    if measurements:
        values = ['%d:%f' % p for p in 
            zip((first_real_time + time_step*t for t in xrange(len(measurements))),
                measurements)]

        rrdfile = sensor_rrd_path(sensor_id)
        print>>sys.stderr, values
        # XXX what to do here when it fails...
        for v in values:
            try:
                rrdtool.update(rrdfile, v)
            except Exception, e:
                print>>sys.stderr, "Bad rrdtool update '%s'" % v
                traceback.print_exc(file=sys.stderr)

        # be paranoid
        f = file(rrdfile)
        os.fsync(f.fileno())

def record_debug(lines):
    f = open('%s/debug.log' % config.DATA_PATH, 'a+')
    f.write('===== %s =====\n' % time.strftime('%a, %d %b %Y %H:%M:%S'))
    f.writelines(('%s\n' % s for s in lines))
    f.flush()
    return f

def parse(lines):
   
    debugf = record_debug(lines)

    entries = dict(l.split('=', 1) for l in lines)
    if len(entries) != len(lines):
        raise Exception("Keys are not unique")

    num_sensors = int(entries['sensors'])
    num_measurements = int(entries['measurements'])

    sensors = [entries['sensor_id%d' % n] for n in xrange(num_sensors)]

    meas = []
    for s in sensors:
        meas.append([])

    def val_scale(v):
        # convert decidegrees to degrees
        return 0.1 * v

    for n in xrange(num_measurements):
        vals = [val_scale(int(x)) for x in entries["meas%d" % n].strip().split()]
        if len(vals) != num_sensors:
            raise Exception("Wrong number of sensors for measurement %d" % n)
        # we make an array of values for each sensor
        for s in xrange(num_sensors):
            meas[s].append(vals[s])

    avr_now = float(entries['now'])
    avr_first_time = float(entries['first_time'])
    time_step = float(entries['time_step'])

    #sqlite 
    # - time
    # - voltage
    # - boot time

    first_real_time = time.time() - (avr_now - avr_first_time)

    for sensor_id, measurements in zip(sensors, meas):
        # XXX sqlite add
        sensor_update(sensor_id, measurements, first_real_time, time_step)

    debugf.write("Updated %d sensors\n" % len(sensors))
    debugf.flush()