#!/usr/bin/python3 # based on https://github.com/cnvogelg/ardu/blob/master/datalog/tools/plot_log.py # This script plots a log file of the format # "unix_timestamp key=value key2=value key3=value ..." # using rrdtool. Keys are arbitrary values of the format "type:number" # (e.g. t:1, t:2, h:1 for temperature and humidity) used to identify each sample. # Note that the keys currently ignored. # # The whole log is plotted into one graph. import sys import subprocess import os import math from string import Template if len(sys.argv) != 3: print("Usage: plot.py ", file=sys.stderr) sys.exit(1) log_file = sys.argv[1] png_file = sys.argv[2] rrd_file = "tmp.rrd" argmax = 50000 #### start of config # If you change this you also need to change the create and graph lists below. # Also note that samples have to be in the same order every time (the key is currently ignored). samples_per_line=2 template_values = { # 0 = everything from the input # otherwise = only the last n "total_samples": 0, } # The 4th field of the DS lines is the heartbeat. If you sample at an # interval < that value you will not see a graph. Simply adjust if necessary. create = [ # Index of the 0 after --start is used below. Adjust code if you # change the amount of values before it. 'rrdtool','create',rrd_file,'--step','30','--start',0, 'DS:temp1:GAUGE:50:U:U', 'DS:temp2:GAUGE:50:U:U', #'DS:hum1:GAUGE:50:U:U', 'DS:abshum1:GAUGE:50:U:U', 'RRA:AVERAGE:0.5:1:$total_samples', 'RRA:MIN:0.5:1:$total_samples', 'RRA:MAX:0.5:1:$total_samples', ] graph = [ # Indices of the two Nones are used below. Adjust the code if you # change the amount of values before them.. 'rrdtool','graph',png_file, '-s',None,'-e',None, '--height=500','--width=1000', '--color=BACK#FFFFFF', #'--title','Temperature', '--vertical-label', 'Temperature °C', '--right-axis-label', 'Humidity g/m³', '--right-axis', '0.5:0', #'--alt-autoscale', '-l','10','-u','35', '--rigid', '--left-axis-format', '%4.2lf', '--right-axis-format', '%4.2lf', 'DEF:temp1='+rrd_file+':temp1:AVERAGE', 'LINE1:temp1#339966:temp 1\l', 'DEF:temp2='+rrd_file+':temp2:AVERAGE', 'LINE1:temp2#663399:temp 2\l', #'DEF:hum1='+rrd_file+':hum1:AVERAGE', #'CDEF:hum11=hum1,2,/', #'LINE1:hum11#996633:hum 1\l', 'DEF:abshum1='+rrd_file+':abshum1:AVERAGE', 'CDEF:abshum11=abshum1,2,*', 'LINE1:abshum11#996633:abshum 1\l', ] #### end of config def chunker(seq, size): return (seq[pos:pos + size] for pos in range(0, len(seq), size)) calc_total_from_input = False if template_values["total_samples"] == 0: calc_total_from_input = True # load samples samples = [] with open(log_file,"r") as f: for line in f: if calc_total_from_input: template_values["total_samples"] += 1 elem = line.split(' ') values = [] # timestamp values.append(int(elem[0])) # values for i in range(1, samples_per_line + 1): values.append(float(elem[i].split("=")[1])) # absolute humidity hum=float(elem[3].split("=")[1]) temp=values[2] # Source: https://carnotcycle.wordpress.com/2012/08/04/how-to-convert-relative-humidity-to-absolute-humidity/ abshum=(6.112 * math.e**(17.67 * temp / (temp + 243.5)) * hum * 2.1674) / (273.15+temp) values.append(abshum) samples.append(values) # create rrd start_ts = samples[0][0] end_ts = samples[-1][0] create[6] = str(start_ts - 1) cmd = [] for elem in create: cmd.append(Template(elem).substitute(template_values)) #print("creating rrdb:",create) ret = subprocess.call(cmd) if ret != 0: #print("ERROR calling: {}".format(" ".join(create))) sys.exit(1) # update rrd for group in chunker(samples, argmax): cmd = ['rrdupdate', rrd_file, '--'] for sample in group: entry = ":".join(map(str, sample)) cmd.append(entry); ret = subprocess.call(cmd) if ret != 0: #print("ERROR calling: {}".format(" ".join(cmd))) sys.exit(1) # graph graph[4] = str(start_ts) graph[6] = str(end_ts) ret = subprocess.call(graph) if ret != 0: #print("ERROR calling: {}".format(" ".join(graph))) sys.exit(1) os.unlink(rrd_file) # vim: set noet: