2017-10-29 14:59:48 +00:00
|
|
|
import matplotlib
|
|
|
|
|
matplotlib.use('Agg')
|
2017-10-20 12:23:02 +00:00
|
|
|
from flask import Flask, flash, redirect, render_template, request, make_response
|
2017-10-12 12:12:03 +00:00
|
|
|
import matplotlib.pyplot as plt
|
|
|
|
|
import io
|
2017-10-20 12:23:02 +00:00
|
|
|
import sqlite3
|
|
|
|
|
import matplotlib.dates as mdates
|
2017-10-20 14:19:13 +00:00
|
|
|
from flask_googlemaps import GoogleMaps
|
2017-11-12 16:28:59 +00:00
|
|
|
import csv
|
2017-11-12 16:48:26 +00:00
|
|
|
from flask_basicauth import BasicAuth
|
|
|
|
|
import configparser
|
2017-10-12 12:12:03 +00:00
|
|
|
|
2017-11-12 16:48:26 +00:00
|
|
|
|
|
|
|
|
# read settings
|
|
|
|
|
CONFIG_FILE = "../config/CosmicPi.config"
|
|
|
|
|
# read configuration
|
|
|
|
|
# Todo: Put the config parser into a propper class
|
|
|
|
|
# Todo: Implement proper error catching for configparser (e.g. non existent keys or file)
|
|
|
|
|
# read configuration
|
|
|
|
|
config = configparser.ConfigParser()
|
|
|
|
|
config.read(CONFIG_FILE)
|
|
|
|
|
SQLITE_LOCATION = config.get("Storage", "sqlite_location")
|
|
|
|
|
UI_USER = config.get("UI", "username")
|
|
|
|
|
UI_PASS = config.get("UI", "password")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# start flask
|
2017-10-12 12:12:03 +00:00
|
|
|
app = Flask(__name__)
|
2017-11-12 16:48:26 +00:00
|
|
|
app.config['BASIC_AUTH_USERNAME'] = UI_USER
|
|
|
|
|
app.config['BASIC_AUTH_PASSWORD'] = UI_PASS
|
|
|
|
|
|
|
|
|
|
basic_auth = BasicAuth(app)
|
2017-10-12 12:12:03 +00:00
|
|
|
|
2017-11-12 16:28:59 +00:00
|
|
|
|
2017-10-20 12:23:02 +00:00
|
|
|
def initDB():
|
2017-11-12 16:48:26 +00:00
|
|
|
conn = sqlite3.connect(SQLITE_LOCATION, timeout=60.0)
|
2017-10-20 12:23:02 +00:00
|
|
|
cursor = conn.cursor()
|
|
|
|
|
cursor.execute("SELECT name FROM sqlite_master WHERE type='table' AND name='Events'")
|
|
|
|
|
if cursor.fetchone() == None:
|
|
|
|
|
cursor.execute('''CREATE TABLE Events
|
|
|
|
|
(UTCUnixTime INTEGER, SubSeconds REAL, TempreatureC REAL, Humidity REAL, AccelX REAL,
|
|
|
|
|
AccelY REAL, AccelZ REAL, MagX REAL, MagY REAL, MagZ REAL, Pressure REAL, Longitude REAL,
|
|
|
|
|
Latitude REAL, DetectorName TEXT, DetectorVersion TEXT);''')
|
|
|
|
|
conn.commit()
|
2017-10-12 12:12:03 +00:00
|
|
|
|
|
|
|
|
@app.route("/base/")
|
|
|
|
|
def base():
|
|
|
|
|
return render_template(
|
|
|
|
|
'cosmic_base.html', **locals())
|
|
|
|
|
|
|
|
|
|
|
2017-10-20 13:25:51 +00:00
|
|
|
@app.route("/hw_serial.txt")
|
|
|
|
|
def getserial():
|
|
|
|
|
# Extract serial from cpuinfo file
|
|
|
|
|
cpuserial = "0000000000000000"
|
|
|
|
|
try:
|
|
|
|
|
f = open('/proc/cpuinfo','r')
|
|
|
|
|
for line in f:
|
|
|
|
|
if line[0:6]=='Serial':
|
|
|
|
|
cpuserial = line[10:26]
|
|
|
|
|
f.close()
|
|
|
|
|
except:
|
|
|
|
|
cpuserial = "ERROR000000000"
|
|
|
|
|
|
|
|
|
|
return cpuserial
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
icon_dict = {
|
|
|
|
|
'UTCUnixTime': "fa fa-clock-o fa-5x",
|
|
|
|
|
'TempreatureC': "fa fa-thermometer-half fa-5x",
|
|
|
|
|
'Humidity': "fa fa-tint fa-5x",
|
|
|
|
|
'AccelX': "fa fa-tachometer fa-5x",
|
|
|
|
|
'AccelY': "fa fa-tachometer fa-5x",
|
|
|
|
|
'AccelZ': "fa fa-tachometer fa-5x",
|
|
|
|
|
'MagX': "fa fa-compass fa-5x",
|
|
|
|
|
'MagY': "fa fa-compass fa-5x",
|
|
|
|
|
'MagZ': "fa fa-compass fa-5x",
|
|
|
|
|
'Pressure': "fa fa-thermometer-half fa-5x",
|
|
|
|
|
'Longitude': "fa fa-map-marker fa-5x",
|
|
|
|
|
'Latitude': "fa fa-map-marker fa-5x",
|
|
|
|
|
'DetectorName': "fa fa-info-circle fa-5x",
|
|
|
|
|
'DetectorVersion': "fa fa-info-circle fa-5x",
|
|
|
|
|
}
|
|
|
|
|
|
2017-10-12 12:12:03 +00:00
|
|
|
@app.route('/', methods=['GET', 'POST'])
|
|
|
|
|
@app.route('/dashboard/', methods=['GET', 'POST'])
|
2017-10-20 15:15:28 +00:00
|
|
|
def dashboard_page():
|
2017-10-20 13:25:51 +00:00
|
|
|
values_to_display =[{'name':'Hardware Serial', 'value':getserial(), 'icon':'fa fa-microchip fa-5x'}]
|
2017-10-20 14:19:13 +00:00
|
|
|
|
|
|
|
|
# get the latest datapoint
|
2017-11-12 16:48:26 +00:00
|
|
|
conn = sqlite3.connect(SQLITE_LOCATION, timeout=60.0)
|
2017-10-20 14:19:13 +00:00
|
|
|
cursor = conn.cursor()
|
|
|
|
|
cursor.execute("SELECT * FROM Events ORDER BY UTCUnixTime DESC, SubSeconds DESC;")
|
|
|
|
|
latest_datapoint = cursor.fetchone()
|
|
|
|
|
# get collumn names
|
|
|
|
|
cursor.execute("PRAGMA table_info(Events);")
|
|
|
|
|
col_names = cursor.fetchall()
|
2017-11-12 16:28:59 +00:00
|
|
|
conn.close()
|
2017-10-20 14:19:13 +00:00
|
|
|
|
|
|
|
|
# extract data
|
|
|
|
|
for i in range(0,len(col_names)):
|
|
|
|
|
# skip the subseconds
|
|
|
|
|
if i == 1:
|
|
|
|
|
continue;
|
|
|
|
|
# field name
|
|
|
|
|
f_name = col_names[i][1]
|
|
|
|
|
# fill in values
|
|
|
|
|
if f_name in icon_dict.keys():
|
|
|
|
|
values_to_display.append({'name':f_name, 'value':latest_datapoint[i], 'icon':icon_dict[f_name]})
|
|
|
|
|
|
2017-10-20 14:34:02 +00:00
|
|
|
return render_template('dashboard.html', values_to_display=values_to_display)
|
2017-10-20 14:19:13 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
@app.route('/plotting/', methods=['GET', 'POST'])
|
2017-10-20 15:15:28 +00:00
|
|
|
def plotting_page():
|
2017-10-20 14:19:13 +00:00
|
|
|
location_vars = {'Latitude': 0, 'Longitude': 0}
|
|
|
|
|
|
2017-10-20 13:25:51 +00:00
|
|
|
# get the latest datapoint
|
2017-11-12 16:48:26 +00:00
|
|
|
conn = sqlite3.connect(SQLITE_LOCATION, timeout=60.0)
|
2017-10-20 13:25:51 +00:00
|
|
|
cursor = conn.cursor()
|
|
|
|
|
cursor.execute("SELECT * FROM Events ORDER BY UTCUnixTime DESC, SubSeconds DESC;")
|
|
|
|
|
latest_datapoint = cursor.fetchone()
|
|
|
|
|
# get collumn names
|
|
|
|
|
cursor.execute("PRAGMA table_info(Events);")
|
|
|
|
|
col_names = cursor.fetchall()
|
2017-11-12 16:28:59 +00:00
|
|
|
conn.close()
|
2017-10-20 13:25:51 +00:00
|
|
|
|
2017-10-20 14:19:13 +00:00
|
|
|
# extract data
|
2017-10-20 13:25:51 +00:00
|
|
|
for i in range(0,len(col_names)):
|
|
|
|
|
# skip the subseconds
|
|
|
|
|
if i == 1:
|
|
|
|
|
continue;
|
2017-10-20 14:19:13 +00:00
|
|
|
# field name
|
|
|
|
|
f_name = col_names[i][1]
|
|
|
|
|
# fill in location
|
|
|
|
|
if f_name in location_vars.keys():
|
|
|
|
|
location_vars[f_name] = latest_datapoint[i]
|
|
|
|
|
|
|
|
|
|
|
2017-10-20 13:25:51 +00:00
|
|
|
|
2017-10-20 14:19:13 +00:00
|
|
|
return render_template('plotting.html', location_vars=location_vars)
|
2017-10-20 12:23:02 +00:00
|
|
|
|
2017-11-12 16:28:59 +00:00
|
|
|
@app.route('/settings/', methods=['GET', 'POST'])
|
2017-11-12 16:48:26 +00:00
|
|
|
@basic_auth.required
|
2017-11-12 16:28:59 +00:00
|
|
|
def settings_page():
|
|
|
|
|
return render_template('settings.html')
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@app.route('/CosmicPi_data.csv', methods=['GET'])
|
|
|
|
|
def csv_export():
|
2017-11-12 16:48:26 +00:00
|
|
|
conn = sqlite3.connect(SQLITE_LOCATION, timeout=60.0)
|
2017-11-12 16:28:59 +00:00
|
|
|
cursor = conn.cursor()
|
|
|
|
|
|
|
|
|
|
# get collumn names
|
|
|
|
|
cursor.execute("PRAGMA table_info(Events);")
|
|
|
|
|
col_data = cursor.fetchall()
|
|
|
|
|
col_names = []
|
|
|
|
|
for i in range(0, len(col_data)):
|
|
|
|
|
col_names.append(col_data[i][1])
|
|
|
|
|
|
|
|
|
|
# get data from db
|
|
|
|
|
cursor.execute("SELECT * FROM Events ORDER BY UTCUnixTime DESC, SubSeconds DESC;")
|
|
|
|
|
data = cursor.fetchall()
|
|
|
|
|
conn.close()
|
|
|
|
|
|
|
|
|
|
# write CSV export to memory
|
|
|
|
|
output = io.BytesIO()
|
|
|
|
|
writer = csv.writer(output)
|
|
|
|
|
writer.writerow(col_names)
|
|
|
|
|
writer.writerows(data)
|
|
|
|
|
|
|
|
|
|
response = make_response(output.getvalue())
|
|
|
|
|
response.headers['Content-Type'] = 'text/csv'
|
|
|
|
|
return response
|
|
|
|
|
|
2017-10-12 12:12:03 +00:00
|
|
|
|
2017-10-20 15:15:28 +00:00
|
|
|
@app.route('/about/', methods=['GET', 'POST'])
|
|
|
|
|
def about_page():
|
|
|
|
|
return render_template('about.html')
|
|
|
|
|
|
|
|
|
|
|
2017-10-20 12:23:02 +00:00
|
|
|
@app.route('/histogram.png')
|
2017-10-12 12:12:03 +00:00
|
|
|
def build_plot():
|
2017-10-20 12:23:02 +00:00
|
|
|
# get user set parameters
|
|
|
|
|
start_time = request.args.get('start_time', type=int)
|
|
|
|
|
end_time = request.args.get('end_time', type=int)
|
|
|
|
|
bin_size_seconds = request.args.get('bin_size_seconds', type=int)
|
|
|
|
|
plot_title = ''
|
2017-10-12 12:12:03 +00:00
|
|
|
|
2017-10-20 12:23:02 +00:00
|
|
|
# get some data
|
|
|
|
|
data = []
|
2017-11-12 16:48:26 +00:00
|
|
|
conn = sqlite3.connect(SQLITE_LOCATION, timeout=60.0)
|
2017-10-20 12:23:02 +00:00
|
|
|
cursor = conn.cursor()
|
|
|
|
|
# only get the last n seconds if the start time was negative
|
|
|
|
|
if start_time < 0:
|
2017-11-12 19:24:23 +00:00
|
|
|
plot_title += "Histogram of events over the last {0:.1f} minutes\nbin size: {1:d} [s]".format(-start_time/60., bin_size_seconds)
|
2017-10-20 12:23:02 +00:00
|
|
|
cursor.execute("SELECT * FROM Events ORDER BY UTCUnixTime DESC, SubSeconds DESC;")
|
|
|
|
|
start_time = cursor.fetchone()[0] + start_time
|
|
|
|
|
end_time = 9000000000
|
|
|
|
|
else:
|
|
|
|
|
plot_title += "Histogram of events over a set time\nbin size: " + str(bin_size_seconds) + " [s]"
|
|
|
|
|
cursor.execute("SELECT * FROM Events WHERE UTCUnixTime BETWEEN ? AND ? ORDER BY UTCUnixTime DESC, SubSeconds DESC;",
|
|
|
|
|
(start_time, end_time))
|
|
|
|
|
data = cursor.fetchall()
|
2017-11-12 16:28:59 +00:00
|
|
|
conn.close()
|
2017-10-20 12:23:02 +00:00
|
|
|
|
|
|
|
|
# massage data
|
|
|
|
|
if len(data) == 0:
|
|
|
|
|
plt.hist([])
|
|
|
|
|
plt.title("No data to display")
|
|
|
|
|
else:
|
|
|
|
|
event_time_list = [data[i][0] + data[i][1] for i in range(len(data))]
|
|
|
|
|
#event_time_list = [data[i][0] for i in range(len(data))]
|
|
|
|
|
bin_edges = range(int(event_time_list[len(event_time_list) - 1]), int(event_time_list[0]), bin_size_seconds)
|
2017-11-12 19:24:23 +00:00
|
|
|
x_axis_limits = (start_time, int(event_time_list[0])+1)
|
2017-10-20 12:23:02 +00:00
|
|
|
# convert our unix timestamps to Matplotlib format
|
|
|
|
|
event_time_list = mdates.epoch2num(event_time_list)
|
|
|
|
|
bin_edges = mdates.epoch2num(bin_edges)
|
|
|
|
|
x_axis_limits = mdates.epoch2num(x_axis_limits)
|
2017-10-12 12:12:03 +00:00
|
|
|
|
2017-10-20 12:23:02 +00:00
|
|
|
# make the plot
|
|
|
|
|
plt.hist(event_time_list,bins=bin_edges)
|
|
|
|
|
plt.title(plot_title)
|
|
|
|
|
plt.xlabel("Time [UTC]")
|
2017-11-12 19:24:23 +00:00
|
|
|
plt.ylabel("Number of Events per {0:d} second [1]".format(bin_size_seconds))
|
2017-10-20 12:23:02 +00:00
|
|
|
|
|
|
|
|
plt.subplots_adjust(bottom=0.2)
|
|
|
|
|
plt.xticks(rotation=25)
|
|
|
|
|
plt.tick_params(which='both', width=2, direction="out", top=False, right=False)
|
|
|
|
|
plt.tick_params(which='major', length=5)
|
|
|
|
|
plt.tick_params(which='minor', length=3, color='r')
|
|
|
|
|
|
|
|
|
|
# do the date formatting
|
|
|
|
|
ax = plt.gca()
|
|
|
|
|
locator = mdates.AutoDateLocator(minticks=7)
|
|
|
|
|
locator.intervald[mdates.SECONDLY] = [1,10,30]
|
|
|
|
|
formatter = mdates.AutoDateFormatter(locator)
|
|
|
|
|
formatter.scaled[1 / (24. * 60.)] = '%H:%M:%S'
|
|
|
|
|
ax.xaxis.set_major_locator(locator)
|
|
|
|
|
ax.xaxis.set_major_formatter(formatter)
|
|
|
|
|
ax.set_xlim(x_axis_limits)
|
|
|
|
|
|
|
|
|
|
# return the generated plot
|
|
|
|
|
img = io.BytesIO()
|
2017-10-12 12:12:03 +00:00
|
|
|
plt.savefig(img, format='png')
|
|
|
|
|
img.seek(0)
|
2017-10-20 12:23:02 +00:00
|
|
|
plt.close()
|
|
|
|
|
response = make_response(img.getvalue())
|
|
|
|
|
response.headers['Content-Type'] = 'image/png'
|
|
|
|
|
return response
|
2017-10-12 12:12:03 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if __name__ == '__main__':
|
2017-10-20 14:19:13 +00:00
|
|
|
# do necessary inits
|
2017-10-20 12:23:02 +00:00
|
|
|
initDB()
|
2017-10-20 14:19:13 +00:00
|
|
|
GoogleMaps(app, key="AIzaSyD_RgwMc6X6LpkAmskk4fWmafNFXtlB7_s")
|
2017-10-12 12:12:03 +00:00
|
|
|
app.run()
|