From fa600586ae519f996500113457847b7718106b45 Mon Sep 17 00:00:00 2001 From: LongHairedHacker Date: Fri, 27 Apr 2018 02:03:42 +0200 Subject: [PATCH] Added database creation and updates --- .gitignore | 2 ++ config.py | 43 ++++++++++++++++++++++++++++++--------- configutils.py | 5 +++++ mockup.py | 32 +++++++++++++++++++++-------- update.py | 55 ++++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 119 insertions(+), 18 deletions(-) create mode 100644 configutils.py diff --git a/.gitignore b/.gitignore index 5b1c2c1..7281708 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ *.pyc __pycache__ virtenv +*.rrd +*.png diff --git a/config.py b/config.py index acd539f..2689832 100644 --- a/config.py +++ b/config.py @@ -1,29 +1,52 @@ #!/usr/bin/env python3 -import serial # for parity constants + +from configutils import * SERIAL = '/dev/ttyUSB0' BAUD_RATE = 4800 STOP_BITS = 1 PARITY = None +DATA_FILE = './test.rrd' +ARCHIVE_DATA_FILE = './test_archive.rrd' DATA_INTERVAL = 60 ARCHIVE_INTERVAL = 60 * 60 # 1h -KEEP_INTERVAL = 365 * 24 * 60 * 60 # 1 year +ARCHIVE_KEEP_INTERVAL = 365 * 24 * 60 * 60 # 1 year +MAX_MISSING = 0.5 # Manual claims poly should be 0x8404, internet says xmodem CRC_TYPE = 'xmodem' + STORED_VALUES = [ - 'U_bat', - 'U_mod1', - 'U_mod2', - 'I_pv_in', - 'I_load_total', - 'load_switch', - 'max_charge_bat_day', - 'max_charge_load_day' + StoredValue('U_bat', 0.0, 20.0), + StoredValue('U_mod1', 0.0, 20.0), + StoredValue('U_mod2', 0.0, 20.0), + StoredValue('I_pv_in', 0.0, 100.0), + StoredValue('I_load_total', 0.0, 100.0), + StoredValue('load_switch', 0, 1), + StoredValue('max_charge_bat_day', 0.0, 1000.0), + StoredValue('max_charge_load_day', 0.0, 1000.0), ] +GRAPHS = { + 'voltages.png' : [ + GraphLine('U_bat', 'Spannung Batterie [V]', '#ff0000'), + GraphLine('U_mod1', 'Spannung Modul 1 [V]', '#00ff00'), + GraphLine('U_mod2', 'Spannung Modul 2 [V]', '#0000ff'), + ], + 'currents.png' : [ + GraphLine('I_pv_in', 'Strom PV [A]', '#ff0000'), + GraphLine('I_load_total', 'Strom Last [A]', '#00ff00'), + ], + 'realy.png' : [ + GraphLine('load_switch', 'Relay', '#0000ff'), + ], + 'charges.png' : [ + GraphLine('max_charge_bat_day', 'Max. Batterie Landung [Ah]', '#0000ff'), + GraphLine('max_charge_load_day', 'Max. Last Entladung [Ah]', '#ff0000'), + ] + } FORMAT = ['Version', 'Date', diff --git a/configutils.py b/configutils.py new file mode 100644 index 0000000..20d2cc5 --- /dev/null +++ b/configutils.py @@ -0,0 +1,5 @@ +#!/usr/bin/env python3 +from collections import namedtuple + +StoredValue = namedtuple('StoredValue', ['name', 'min', 'max']) +GraphLine = namedtuple('GraphLine', ['name', 'label', 'color']) diff --git a/mockup.py b/mockup.py index 7412331..0ee72e4 100644 --- a/mockup.py +++ b/mockup.py @@ -1,15 +1,27 @@ #!/usr/bin/env python3 +import os import crcmod from random import randint +from time import sleep from config import * -from update import parse_line +from update import parse_line, create_database, update_database, update_graphs calc_crc = crcmod.predefined.mkCrcFun(CRC_TYPE) def gen_line(): - values = [str(randint(0,1000)) for _ in range(0,len(FORMAT))] + values = [] + + stored = {name : (minval, maxval) for name, minval, maxval in STORED_VALUES} + for name in FORMAT: + minval = 0 + maxval = 1000 + if name in stored.keys(): + minval, maxval = stored[name] + + values += [str(randint(minval, maxval))] + payload = ";".join(values) + ";" crc = calc_crc(payload.encode('ascii')) crc_str = chr((crc & 0xFF00) >> 8) + chr(crc & 0xFF) @@ -19,13 +31,17 @@ def gen_line(): def main(): - line = gen_line() - result = parse_line(line) + if not os.path.exists(DATA_FILE) or not os.path.exists(ARCHIVE_DATA_FILE): + create_database() + sleep(60) - print(result) - - - + while True: + print("Adding Line...") + line = gen_line() + parsed = parse_line(line) + update_database(parsed) + update_graphs() + sleep(60) if __name__ == '__main__': main() diff --git a/update.py b/update.py index 33db62e..4fd6cd0 100644 --- a/update.py +++ b/update.py @@ -1,6 +1,8 @@ #!/usr/bin/env python3 +import time import serial import crcmod +import rrdtool from config import * @@ -24,6 +26,59 @@ def parse_line(line): return dict(data) +def create_database(): + sources = [] + for name, minval, maxval in STORED_VALUES: + sources += ["DS:%s:GAUGE:%d:%f:%f" % (name, DATA_INTERVAL * 2, minval, maxval)] + + now = time.time() + now = now - (now % DATA_INTERVAL) + + rrd_params = [DATA_FILE, + "--start", "%d" % now, + "--step", str(DATA_INTERVAL)] + rrd_params += sources + rows = ARCHIVE_INTERVAL / DATA_INTERVAL + rrd_params += ["RRA:LAST:0.1:1:%d" % (rows,)] + rrdtool.create(*rrd_params) + + + rrd_archive_params = [ARCHIVE_DATA_FILE, + "--start", "now", + "--step", str(DATA_INTERVAL)] + steps = ARCHIVE_INTERVAL / DATA_INTERVAL + rows = ARCHIVE_KEEP_INTERVAL / ARCHIVE_INTERVAL + rrd_archive_params += sources + rrd_archive_params += ["RRA:AVERAGE:%f:%d:%d" % (MAX_MISSING, steps, rows)] + rrd_archive_params += ["RRA:MAX:%f:%d:%d" % (MAX_MISSING, steps, rows)] + rrd_archive_params += ["RRA:MIN:%f:%d:%d" % (MAX_MISSING, steps, rows)] + rrdtool.create(*rrd_archive_params) + + + +def update_database(line): + update_values = [] + for name, _, _ in STORED_VALUES: + update_values += ["%f" % float(line[name])] + + now = time.time() + now = now - (now % DATA_INTERVAL) + line = ("%d:" % now) + ":".join(update_values) + print(line) + rrdtool.update(DATA_FILE, line) + rrdtool.update(ARCHIVE_DATA_FILE, line) + +def update_graphs(): + for name, lines in GRAPHS.items(): + graph_params = [name, '-a', 'PNG', '-s', 'n-%d' % ARCHIVE_INTERVAL] + + for name, lable, color in lines: + graph_params += ['DEF:%s=%s:%s:LAST' % (name, DATA_FILE, name)] + graph_params += ['LINE1:%s%s:%s' % (name, color, lable)] + + rrdtool.graph(*graph_params) + + def main(): pass