diff --git a/app/database.py b/app/database.py index 1076069..78e3ed5 100644 --- a/app/database.py +++ b/app/database.py @@ -52,10 +52,11 @@ def get_user(u): u.password=row[2] u.longname=row[3] u.email=row[4] - u.isblack=row[6] - u.isbaron=row[7] - u.isshown=row[8] - u.autoblack=row[9] + u.isblack=row[5] + u.isbaron=row[6] + u.isshown=row[7] + u.autoblack=row[8] + u.onlyrfid=row[9] u.rfid_id = get_rfid_ids_by_userid(u.id) logging.info(u) return u @@ -70,10 +71,11 @@ def get_user_by_name(name): u.password=row[2] u.longname=row[3] u.email=row[4] - u.isblack=row[6] - u.isbaron=row[7] - u.isshown=row[8] - u.autoblack=row[9] + u.isblack=row[5] + u.isbaron=row[6] + u.isshown=row[7] + u.autoblack=row[8] + u.onlyrfid=row[9] logging.debug(u) u.rfid_id = get_rfid_ids_by_userid(u.id) return u @@ -88,10 +90,11 @@ def get_user_by_id(id): u.password=row[2] u.longname=row[3] u.email=row[4] - u.isblack=row[6] - u.isbaron=row[7] - u.isshown=row[8] - u.autoblack=row[9] + u.isblack=row[5] + u.isbaron=row[6] + u.isshown=row[7] + u.autoblack=row[8] + u.onlyrfid=row[9] u.rfid_id = get_rfid_ids_by_userid(u.id) logging.debug(u) return u @@ -114,10 +117,11 @@ def get_users(): u.password=row[2] u.longname=row[3] u.email=row[4] - u.isblack=row[6] - u.isbaron=row[7] - u.isshown=row[8] - u.autoblack=row[9] + u.isblack=row[5] + u.isbaron=row[6] + u.isshown=row[7] + u.autoblack=row[8] + u.onlyrfid=row[9] u.rfid_id = get_rfid_ids_by_userid(u.id) users.append(u) return users @@ -155,7 +159,7 @@ def add_rfid_id(rfid_id, userid): def update_user(u): #query_db("UPDATE users SET (NAME, LONGNAME, EMAIL, RFID_ID, ISBLACK, ISBARON, ISSHOWN) VALUES (?, ?, ?, ?, ?, ?, ?) WHERE ID=?", (u.name, u.longname, u.email, u.rfid_id, u.isblack, u.isbaron, u.isshown, u.id)) - query_db("UPDATE users SET NAME=?, LONGNAME=?, EMAIL=?, ISBLACK=?, ISBARON=?, ISSHOWN=?, AUTOBLACK=? WHERE ID=?", (u.name, u.longname, u.email, u.isblack, u.isbaron, u.isshown, u.autoblack, u.id)) + query_db("UPDATE users SET NAME=?, LONGNAME=?, EMAIL=?, ISBLACK=?, ISBARON=?, ISSHOWN=?, AUTOBLACK=?, ONLYRFID=? WHERE ID=?", (u.name, u.longname, u.email, u.isblack, u.isbaron, u.isshown, u.autoblack, u.onlyrfid ,u.id)) set_rfid_to_userid(u.rfid_id, u.id) get_db().commit() diff --git a/app/datalog.py b/app/datalog.py new file mode 100644 index 0000000..9ad41dc --- /dev/null +++ b/app/datalog.py @@ -0,0 +1,132 @@ +import numpy as np +import datetime as dt +import pandas as pa +import time +#import ipdb +import signal +import thread +import logging +import atexit +import settings +import os + +# i2c io pins +P_SCK = 3 +P_SDA = 2 +P_BAUD = 50000 + +# sensor data +SENSOR_BASEID = 0x48 +SENSOR_DATALEN = 2 + +# i2c commands for pigpio software i2c +I2C_END = 0 +I2C_START = 2 +I2C_STOP = 3 +I2C_SET_ADDR = 4 +I2C_READ = 6 + +if settings.settings.fridgeLogging: + try: + import pigpio + except: + logging.fatal("PIGPIO library could not be loaded, install PIGPIO to read sensor data or disable data logging in the settings!") + exit() + + +class DataLogger: + + def __init__(self): + self.ioif = None + self.logging = True + signal.signal(signal.SIGINT, self.cleanup) + logging.info("Data Logger created") + thread.start_new_thread(self.log_data, ()) + #self.log_data() + + def log_data(self): + + # pigpio + self.ioif = pigpio.pi() + + # software i2c + try: + self.ioif.bb_i2c_open(P_SDA, P_SCK, P_BAUD) + except: + logging.error("bus already open") + self.ioif.bb_i2c_close(P_SDA) + self.ioif.bb_i2c_open(P_SDA, P_SCK, P_BAUD) + + logging.info("Data Logger started") + + log = 0 + data_fast = np.zeros(8) + data_perm = np.zeros(8) + n_fast = 1 + n_perm = 1 + while logging: + logging.info("Data Logger logging") + columns = list(("time", )) + + for i in range(SENSOR_BASEID, SENSOR_BASEID + 8): + (count, data) = self.ioif.bb_i2c_zip(P_SDA, [I2C_SET_ADDR, i, I2C_START, I2C_READ, SENSOR_DATALEN, I2C_STOP, I2C_END]) + columns.append(str(i)) + if count > 1: + sign = 1 + if data[0] > 128: + print "sign" + data[0] = data[0]-128 + sign = -1 + data_fast[i-SENSOR_BASEID] += sign * (int(data[0]) + (int(data[1]) / 256.0)) + data_perm[i-SENSOR_BASEID] += sign * (int(data[0]) + (int(data[1]) / 256.0)) + else: + data_fast[i-SENSOR_BASEID] = np.nan + data_perm[i-SENSOR_BASEID] = np.nan + time.sleep(0.05) + + infoline = np.empty((1, 9), dtype=object) + + if n_fast % 10 == 0: + infoline[0, 1:9] = data_fast[0:8] / n_fast + n_fast = 0 + infoline[0, 0] = dt.datetime.now() + pdataline = pa.DataFrame.from_records(infoline, columns=columns) + self.savetofile(False, pdataline) + data_fast = np.zeros(8) + + if n_perm % 100 == 0: + infoline[0, 1:9] = data_perm[0:8] / n_perm + n_perm = 0 + infoline[0, 0] = dt.datetime.now() + pdataline = pa.DataFrame.from_records(infoline, columns=columns) + self.savetofile(True, pdataline) + data_perm = np.zeros(8) + + n_fast += 1 + n_perm += 1 + + + def savetofile(self, permanent, data): + + if permanent: + date = dt.datetime.now().strftime('%Y%m%d') + exists = os.path.isfile('app/static/logdata_' + date + '.csv') + with open('app/static/logdata_' + date + '.csv', 'a+') as f: + data.to_csv(f, header=not exists, index=False) + else: + exists = os.path.isfile('/tmp/baroness_logdata_fast.csv') + with open('/tmp/baroness_logdata_fast.csv', 'a+') as f: + data.to_csv(f, header=not exists, index=False) + + def cleanup(self): + self.logging = False + logging.info("Data logger closed") + self.ioif.bb_i2c_close(P_SDA) + +if __name__ == "__main__": + + #for standalone mode + print "creating datalogger" + logger = DataLogger() + while(1): + time.sleep(1) diff --git a/app/gui.py b/app/gui.py index 8f7f517..44523fa 100644 --- a/app/gui.py +++ b/app/gui.py @@ -231,7 +231,7 @@ class PanelUsers (wx.Panel): users = get_users() names = list() for user in users: - if user.isshown: + if user.isshown and not user.onlyrfid: names.append(user.longname) self.but_names = list() diff --git a/app/plot.py b/app/plot.py index 2fb634f..0109e94 100644 --- a/app/plot.py +++ b/app/plot.py @@ -1,15 +1,60 @@ -#import matplotlib #speed? -#matplotlib.use('GTKAgg') +import matplotlib #speed? +matplotlib.use('TkAgg') from matplotlib import pyplot as plt from matplotlib import units +from matplotlib import patheffects from matplotlib.dates import WeekdayLocator, DayLocator, HourLocator, DateFormatter, drange, MONDAY +from matplotlib import rcParams as rcp import numpy as np from user import User from database import * import multiprocessing as mp +import thread import pandas as pd import logging +import time +import glob + + +class Plotter: + + def __init__(self): + logging.info("Plotter started!") + self.joblist = list() + self.active = True + thread.start_new_thread(self.refresh, ()) + + def refresh(self): + ptime = 0 + while self.active: + logging.info("Plotter plotting stuff") + logfiles = list() + logfiles += glob.glob("app/static/logdata*.csv") + logfiles.append("/tmp/baroness_logdata_fast.csv") + print logfiles + if logfiles: + plot_log(logfiles, hours = 4) + + # plotter not fully integrated yet + #if ptime % 30 or self.joblist: + # plot_total() + # plot_list(4) + # for u in self.joblist: + # plot_total(u) + # self.joblist = list() + + ptime += 1 + time.sleep(10) + + def addplot(self, user): + logging.info("Plotter add user " + user.name) + self.joblist.append(user) + + def stop(self): + logging.info("Plotter stop!") + self.active = False + def plot_all_thread(user=None): if user is not None: @@ -93,31 +138,25 @@ def plot_total(user=None): fils = "app/static/total%03d.png" % user.id fill = "app/static/total%03d_big.png" % user.id plt.title(tit) - logging.info("plot plot_total " + str(datetime.datetime.now())) + logging.info("Plot plot_total " + str(datetime.datetime.now())) #480x320 fig.set_size_inches(4.8, 3.2) plt.savefig(fils, dpi=100) - logging.info("end plot_total " + str(datetime.datetime.now())) + logging.info("Plot: End plot_total " + str(datetime.datetime.now())) #fig.set_size_inches(4.8, 3.2) #plt.savefig(fill, dpi=400) -def plot_log(days, logfiles): - today = datetime.date.today() - delta = datetime.timedelta(days=1) - begin = today - datetime.timedelta(days=days) - dates = drange(begin, today + delta, delta) - - #print begin - #print today - #print dates +def plot_log(logfiles, hours): + end = datetime.datetime.now() + begin = end - datetime.timedelta(hours=hours) #load all logfiles data = None for logfile in logfiles: try: d = pd.read_csv(logfile, parse_dates=[0]) - if data: + if data is not None: data = pd.concat((data, d)) else: data = d @@ -126,53 +165,60 @@ def plot_log(days, logfiles): return 0 #ipdb.set_trace() + data = data.sort('time') + data = data[data.time > begin] data = data.set_index(pd.DatetimeIndex(data.time)) data = data.drop("time", axis=1) #ipdb.set_trace() plt.xkcd() + rcp['path.effects'] = [patheffects.withStroke(linewidth=0)] #all columns + for item, frame in data.iteritems(): + if frame is None or frame.empty or frame.isnull().values.all(): + continue f = plt.figure() #f.patch.set_facecolor("#ccefff") #f.patch.set_alpha("0.0") logging.debug("column " + str(item) + " " + frame.name) - plt.plot(data.index.to_pydatetime(), frame, "b") + plt.plot(data.index.to_pydatetime(), frame)#"b" ax = plt.gca() #ax.grid(True, linewidth=1.0) - plt.xlim(begin, datetime.datetime.now()) - ax.spines['right'].set_visible(False) - ax.spines['top'].set_visible(False) + #plt.xlim(begin, end) + #ax.spines['right'].set_visible(False) + #ax.spines['top'].set_visible(False) ax.yaxis.set_ticks_position('none')#('left') ax.xaxis.set_ticks_position('none')#('bottom') - f.autofmt_xdate() - - #plt.tick_params(which='minor', length=4) - #plt.tick_params(which='major', length=5) - #ipdb.set_trace() - ax.fmt_xdata = DateFormatter('%d.%m') - #ax.xaxis.set_major_locator(WeekdayLocator(MONDAY)) - #ax.xaxis.set_major_locator(DayLocator()) - ax.xaxis.set_major_formatter(DateFormatter('%d.%m')) - - + try: #might not work if few data available + f.autofmt_xdate() + #plt.tick_params(which='minor', length=4) + #plt.tick_params(which='major', length=5) + #ipdb.set_trace() + #ax.fmt_xdata = AutoDateFormatter('%d.%m/%H') + #ax.xaxis.set_major_locator(WeekdayLocator(MONDAY)) + ax.xaxis.set_major_locator(AutoDateLocator()) + ax.xaxis.set_major_formatter(AutoDateFormatter(defaultfmt='%d.%m')) + #f.autofmt_xdate() + except: + logging.warning("Plot: Few data!") #plt.xlabel('Datum') #plt.ylabel('Temperatur / C') outfile = "app/static/log_" + str(item) + ".png" - logging.info("plot plot_total " + str(datetime.datetime.now())) + logging.info("Plot plot_log " + str(datetime.datetime.now())) #480x320 plt.gcf().set_size_inches(5.3, 2.4) plt.tight_layout() plt.savefig(outfile, dpi=70, transparent=True, bbox_inches='tight') plt.close() - logging.info("end plot_list " + str(datetime.datetime.now())) + logging.info("Plot: End plot_log " + str(datetime.datetime.now())) def plot_list(duration): - logging.info("start plot_list " + str(datetime.datetime.now())) - today = datetime.datetime.today() - begin = today - datetime.timedelta(weeks=duration) + logging.info("Plot: Start plot_list " + str(datetime.datetime.now())) + end = datetime.datetime.now() + begin = end - datetime.timedelta(weeks=duration) users = get_users() products = get_products() @@ -246,11 +292,11 @@ def plot_list(duration): plt.title("Bierliste ("+ str(duration) + " Wochen)") - logging.info("plot plot_list " + str(datetime.datetime.now())) + logging.info("Plot plot_list " + str(datetime.datetime.now())) #800x600 fig.set_size_inches(15, 10) plt.savefig('app/static/bierliste_small.png', dpi=72, bbox_inches='tight') #1024x768 #fig.set_size_inches(10.24, 7.68) #plt.savefig('app/static/bierliste.png', dpi=100) - logging.info("end plot_list " + str(datetime.datetime.now())) + logging.info("Plot: End plot_list " + str(datetime.datetime.now())) diff --git a/app/rfid.py b/app/rfid.py index a143c2f..94c60f0 100644 --- a/app/rfid.py +++ b/app/rfid.py @@ -1,3 +1,4 @@ +import logging try: import MFRC522 except: @@ -6,14 +7,13 @@ except: import signal import thread import time -import logging class RFID: def __init__(self, callbackf): logging.info("RFID Reader initialized!") - self.reader = MFRC522.MFRC522() + self.reader = MFRC522.MFRC522(spd=500000) signal.signal(signal.SIGINT, self.stop) self.callback = callbackf self.loop = True diff --git a/app/templates/base.html b/app/templates/base.html index d0d5cc2..45ec444 100644 --- a/app/templates/base.html +++ b/app/templates/base.html @@ -6,6 +6,11 @@ + + + + + {% if title %} {{ title }} - Baroness diff --git a/app/templates/fridges.html b/app/templates/fridges.html index aebe61b..22d88d3 100644 --- a/app/templates/fridges.html +++ b/app/templates/fridges.html @@ -17,7 +17,7 @@ {% set fils = "log_%s.png" % sensor.id %}
- {% endfor %}b + {% endfor %} {% endfor %} diff --git a/app/templates/manage_users.html b/app/templates/manage_users.html index 000e445..97ca725 100644 --- a/app/templates/manage_users.html +++ b/app/templates/manage_users.html @@ -13,6 +13,7 @@ Baron Angezeigt Auto-Schwärzen + Nur RFID Kreuzen {% for user in users %} @@ -26,6 +27,7 @@ {% if user.isbaron %} ☑ {% else %} ☐ {% endif %} {% if user.isshown %} ☑ {% else %} ☐ {% endif %} {% if user.autoblack %} ☑ {% else %} ☐ {% endif %} + {% if user.onlyrfid %} ☑ {% else %} ☐ {% endif %} bearbeiten {% endfor %} diff --git a/app/templates/manage_users_edit.html b/app/templates/manage_users_edit.html index dd89fc2..18af959 100644 --- a/app/templates/manage_users_edit.html +++ b/app/templates/manage_users_edit.html @@ -18,6 +18,7 @@ Baron:
Angezeigt:
Auto-Schwärzen:
+ Nur mit RFID Kreuzen:
{% endif %} diff --git a/app/user.py b/app/user.py index b3080a3..2284eac 100644 --- a/app/user.py +++ b/app/user.py @@ -11,6 +11,7 @@ class User: self.isbaron=False self.isshown=False self.autoblack=False + self.onlyrfid=False def __str__(self): @@ -72,7 +73,15 @@ class User: if self.autoblack is None: s = "%s, None" % s else: - if self.isshown is 0 or self. isshown is False: + if self.autoblack is 0 or self. autoblack is False: + s = "%s, False" % s + else: + s = "%s, True" % s + + if self.onlyrfid is None: + s = "%s, None" % s + else: + if self.onlyrfid is 0 or self. onlyrfid is False: s = "%s, False" % s else: s = "%s, True" % s diff --git a/app/views.py b/app/views.py index 9724155..e329498 100644 --- a/app/views.py +++ b/app/views.py @@ -67,7 +67,6 @@ def logout(): @app.route('/fridges') def fridges(): - plot_log(30, ("/tmp/baroness_logdata_fast.csv",)) return render_template('fridges.html', fridges=settings.fridges, user=get_user_by_name(session.get('name'))) @@ -149,6 +148,10 @@ def manage_users_edit(name=None): else: u.autoblack = False + if 'onlyrfid' in request.form: + u.onlyrfid = True + else: + u.onlyrfid = False update_user(u) diff --git a/case/abstandh.fcstd b/case/abstandh.fcstd index 3540f81..1700f5b 100644 Binary files a/case/abstandh.fcstd and b/case/abstandh.fcstd differ diff --git a/case/abstandh.stl b/case/abstandh.stl new file mode 100644 index 0000000..fce052b Binary files /dev/null and b/case/abstandh.stl differ diff --git a/case/backside.fcstd b/case/backside.fcstd index 9cfbbe5..01df0de 100644 Binary files a/case/backside.fcstd and b/case/backside.fcstd differ diff --git a/case/backside.stl b/case/backside.stl new file mode 100644 index 0000000..c89c14c Binary files /dev/null and b/case/backside.stl differ diff --git a/case/baroness.fcstd b/case/baroness.fcstd index 5967b75..f9fd5fe 100644 Binary files a/case/baroness.fcstd and b/case/baroness.fcstd differ diff --git a/case/baroness.stl b/case/baroness.stl new file mode 100644 index 0000000..ed59d02 Binary files /dev/null and b/case/baroness.stl differ diff --git a/doc/database.sql b/doc/database.sql index 5ee4653..f02d95c 100644 --- a/doc/database.sql +++ b/doc/database.sql @@ -36,11 +36,11 @@ CREATE TABLE Users( password TEXT NOT NULL, longname TEXT NOT NULL, email TEXT, - rfid_id TEXT, isblack BOOLEAN DEFAULT 0, isbaron BOOLEAN DEFAULT 0, isshown BOOLEAN DEFAULT 1, - autoblack BOOLEAN DEFAULT 1 + autoblack BOOLEAN DEFAULT 1, + onlyrfid BOOLEAN DEFAULT 0 ); -- The table PRODUCTS contains information about the beverages available @@ -76,7 +76,7 @@ CREATE TABLE Deposits( ); CREATE TABLE Rfid( - di INTEGER PRIMARY KEY, + id INTEGER PRIMARY KEY, userid INTEGER NOT NULL, rfid_id TEXT, is_valid BOOLEAN DEFAULT 1, @@ -85,6 +85,11 @@ CREATE TABLE Rfid( ); +-- add one baron to login +INSERT INTO Users(name, password, longname, email, rfid_id, isblack, isbaron, isshown, onlyrfid) VALUES + (hindenburg, $2a$12$BGj0f9msFZiYqAN2DfKOme3lk1klMz1ea62IYNtLWz4q.zNXM0oR2, Paul von Hindenburg, hind@enburg.er, 0x6666666666, 0, 1, 1, 1, 0); + + -- The table Config stores basic config data, this is for the admins and the barons diff --git a/run.py b/run.py index 208c88c..376052c 100755 --- a/run.py +++ b/run.py @@ -5,6 +5,9 @@ from os import urandom from app import gui import thread import logging +from app import settings +from app import datalog +from app import plot if __name__ == '__main__': @@ -15,6 +18,12 @@ if __name__ == '__main__': logging.info("Baroness started!") print "Baroness started: logging to ", logfile + #start data logging + if settings.settings.fridgeLogging: + logger = datalog.DataLogger() + + #start plot thread + plotter = plot.Plotter() #start gui wx = wx.App() diff --git a/test/database.db b/test/database.db index 8b3a0ef..8c2e225 100644 Binary files a/test/database.db and b/test/database.db differ