From a2a05d9e9d9245be4c6e4e9c70334b8ac5fc299b Mon Sep 17 00:00:00 2001 From: erg_samowzbudnik Date: Tue, 1 Jun 2021 15:11:52 +0200 Subject: Initial commit of a beta version. --- RPGH.py | 345 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 345 insertions(+) create mode 100644 RPGH.py (limited to 'RPGH.py') diff --git a/RPGH.py b/RPGH.py new file mode 100644 index 0000000..bfbad39 --- /dev/null +++ b/RPGH.py @@ -0,0 +1,345 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +__author__ = "Franek Łazarewicz-Muradyan" +__copyright__ = "Copyright 2021, Franek Łazarewicz-Muradyan" +__version__ = "0.1" +__status__ = "Beta" +__email__ = "uinarf@autistici.org" + +""" +This is the main program starting the GUI +""" + +import sys, os +from PyQt5 import QtWidgets +import matplotlib +matplotlib.use('qt5agg') +from matplotlib.figure import Figure +from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas +from matplotlib import dates +from datetime import datetime, timedelta +from time import sleep +import configparser +from RPGH_gui import Ui_MainWindow +#from RPGH_main_gui import Ui_MainWindow +import pandas as pd +from pandas.plotting import register_matplotlib_converters +register_matplotlib_converters() +import mysql.connector + +__location__ = os.path.realpath( + os.path.join(os.getcwd(), os.path.dirname(__file__))) + +# read and extract/format configuration data +config_f = 'settings_config_parser.cfg' +config_cl = 'settings_client.cfg' +config_abs = os.path.join(__location__, config_f) +config_cl_abs = os.path.join(__location__, config_cl) +cfg = configparser.ConfigParser(interpolation=None) +cfg_cl = configparser.ConfigParser(interpolation=None) +cfg.read(config_abs) +cfg_cl.read(config_cl_abs) + + +class Data: + + def fetch_table(self, table, _min, _max): + self.table = table + # limits of data to fetch from-to: + self._min = _min + self._max = _max + try: + conn = mysql.connector.connect(**cfg_cl['mysql']) + except mysql.connector.Error as e: + try: + conn = mysql.connector.connect( + host=cfg_cl['mysql']['host'], + user=cfg_cl['mysql']['user'], + password=cfg_cl['mysql']['password'], + database=cfg_cl['mysql']['database'] + ) + except mysql.connector.Error as e: + print(f"Couldn't connect to a database: {e}") + sys.exit() + cur = conn.cursor() + sql = ( + f"SELECT * FROM {self.table} WHERE timestamp BETWEEN %s AND %s" + ) + #sql = ( + # f"SELECT * FROM {self.table};" + #) + cur.execute(sql, (self._min, self._max)) + #cur.execute(sql) + rows = cur.fetchall() + fields = [i[0] for i in cur.description] + df = pd.DataFrame([[x for x in i] for i in rows]) + + return fields, df + +class DesignerMainWindow(QtWidgets.QMainWindow, Ui_MainWindow): + def __init__(self, parent = None): + + super(DesignerMainWindow, self).__init__(parent) + self.setupUi(self) + +# setting up mplwidget + self.mplwidget() + + def mplwidget(self): + + timestamp_format = cfg.get('various', 'timestamp_format') + _now_p = datetime.now() + _past_p = _now_p - timedelta(hours=12) + # time limits formated for mysql: + _now = _now_p.strftime(timestamp_format) + _past = _past_p.strftime(timestamp_format) + +# unpacking data settings + temp_max = cfg.getint('temperature', 'temp_max') + temp_min = cfg.getint('temperature', 'temp_min') + light_max = cfg.getint('light', 'light_max') + light_min = cfg.getint('light', 'light_min') + humid_max = cfg.getint('humidity', 'humidity_max') + humid_min = cfg.getint('humidity', 'humidity_min') + soil_max = cfg.getint('water', 'soil_moisture_max') + soil_min = cfg.getint('water', 'soil_moisture_min') + + # ax limits + now = _now_p.strftime("%H:%M:%S") + past = _past_p.strftime("%H:%M:%S") + +# unpacking temperature data + _temp_data = Data() + temp_fields, temp_data = _temp_data.fetch_table( + 'temperature', _past, _now + ) + timestamp_temp = pd.to_datetime( + pd.Series(temp_data[0]) + ) + temp_01 = ( + temp_data[1], + cfg['w1_mapping'][f'{temp_fields[1]}'] + ) + temp_02 = ( + temp_data[2], + cfg['w1_mapping'][f'{temp_fields[2]}'] + ) + temp_03 = ( + temp_data[3], + cfg['w1_mapping'][f'{temp_fields[3]}'] + ) + temp_04 = ( + temp_data[4], + cfg['w1_mapping'][f'{temp_fields[4]}'] + ) + +# unpacking light data + self.light_data = Data() + light_fields, light_data = self.light_data.fetch_table( + 'light', _past, _now) + timestamp_1 = pd.to_datetime( + pd.Series(light_data[0]), format=timestamp_format + ) + broadband = light_data[1] + infrared = light_data[2] + visible = light_data[3] + +# unpacking humidity data + self.hum_data = Data() + hum_fields, hum_data = self.hum_data.fetch_table( + 'humidity', _past, _now + ) + timestamp_2 = pd.to_datetime( + pd.Series(hum_data[0]), format=timestamp_format + ) + humid = hum_data[2] + +# unpacking rain data + self.rain_data = Data() + rain_fields, rain_data = self.hum_data.fetch_table( + 'rain', _past, _now + ) + timestamp_3 = pd.to_datetime( + pd.Series(rain_data[0]), format=timestamp_format + ) + rain = rain_data[1] + + days = dates.DayLocator() + hours = dates.HourLocator() + minutes = dates.MinuteLocator() + seconds = dates.SecondLocator() + dfmt = dates.DateFormatter('%b %d') + tmfd = dates.DateFormatter('%H %M') + +# ploting temperature data + self.mpl.canvas.ax.set_ylabel('Temperature ($^\circ$C)') + self.mpl.canvas.ax.grid(True) + self.mpl.canvas.ax.plot( + timestamp_temp, + temp_01[0], + c='brown', + ls=':', + label=f'{temp_01[1]}' + ) + self.mpl.canvas.ax.plot( + timestamp_temp, + temp_02[0], + c='orange', + ls='dotted', + label=f'{temp_02[1]}' + ) + self.mpl.canvas.ax.fill_between( + timestamp_temp, + temp_01[0], + temp_02[0] + ) + self.mpl.canvas.ax.fill_between( + timestamp_temp, + temp_01[0], + temp_max, + where=temp_01[0]>=temp_max, + edgecolor='red', + facecolor='none', + hatch='/', + interpolate=True + ) + self.mpl.canvas.ax.fill_between( + timestamp_temp, + temp_02[0], + temp_min, + where=temp_02[0]<=temp_min, + edgecolor='red', + facecolor='none', + hatch='/', + interpolate=True + ) + self.mpl.canvas.ax.legend(loc='upper left') + #self.mpl.canvas.ax.set_xlim([now, past]) +# adding light subplot + self.mpl.canvas.ax_1.set_ylabel('Light') + self.mpl.canvas.ax_1.grid(True) + self.mpl.canvas.ax_1.plot( + timestamp_1, + broadband, + c='cyan', + ls='-.', + label='broadband' + ) + self.mpl.canvas.ax_1.plot( + timestamp_1, + infrared, + c='red', + ls='-.', + label='infrared' + ) + self.mpl.canvas.ax_01 = self.mpl.canvas.ax_1.twinx() + self.mpl.canvas.ax_01.set_ylabel("light lm") + self.mpl.canvas.ax_01.plot( + timestamp_1, + visible, + c='orange',ls='-.', + label='visible' + ) + self.mpl.canvas.ax_1.legend(loc='upper left') + self.mpl.canvas.ax_01.legend(loc='lower left') + self.mpl.canvas.ax_1.fill_between( + timestamp_1, + broadband, + light_max, + where=broadband>=light_max, + edgecolor='red', + facecolor='none', + hatch="/", + interpolate=True + ) + self.mpl.canvas.ax_1.fill_between( + timestamp_1, + broadband, + light_min, + where=broadband<=light_min, + edgecolor='red', + facecolor='none', + hatch='/', + interpolate=True + ) + #self.mpl.canvas.ax_1.set_xlim([now, past]) +# adding humidity subplot + self.mpl.canvas.ax_2.set_ylabel('Humidity %') + self.mpl.canvas.ax_2.set_xlabel('time') + self.mpl.canvas.ax_2.grid(True) + self.mpl.canvas.ax_2.plot( + timestamp_2, + humid, + c='blue', + ls='--', + label='humidity' + ) + self.mpl.canvas.ax_2.legend(loc='upper left') + #self.mpl.canvas.ax_2.set_xlim([now, past]) + +# setting up weather station tab: +# adding temperature plot + self.mpl1.canvas.ax.set_ylabel('Temperature ($^\circ$C)') + self.mpl1.canvas.ax.grid(True) + self.mpl1.canvas.ax.plot( + timestamp_temp, + temp_03[0], + c='brown', + ls=':', + label=f'{temp_03[1]}' + ) + self.mpl1.canvas.ax.plot( + timestamp_temp, + temp_04[0], + c='orange', + ls='dotted', + label=f'{temp_04[1]}' + ) + self.mpl1.canvas.ax.legend(loc='upper left') + #self.mpl1.canvas.ax.set_xlim([now, past]) +# adding light plot + self.mpl1.canvas.ax_1.set_ylabel('Light lm') + self.mpl1.canvas.ax_1.grid(True) + self.mpl1.canvas.ax_1.plot( + timestamp_1, + visible, + c='yellow', + label='light' + ) + self.mpl1.canvas.ax_1.legend(loc='upper left') + #self.mpl1.canvas.ax_1.set_xlim([now, past]) +# adding humidity/rainfall graph +# mind you that hist() doesn't do what I'd like it to do. redo. + self.mpl1.canvas.ax_2.set_ylabel('Rainfall mm') + self.mpl1.canvas.ax_2.set_xlabel('time') + self.mpl1.canvas.ax_2.grid(True) +# this is a hack: unit for bar width is days hence hum_data.shape[0] = numbers +# of columns in dataframe allowing for dynamic sizing + self.mpl1.canvas.ax_2.bar( + timestamp_3, + rain, + width = .01*(1/rain_data.shape[0]), + edgecolor = 'black', + label='rainfall' + ) + self.mpl1.canvas.ax_2.legend(loc='upper left') + #self.mpl1.canvas.ax_2.set_xlim([now, past]) + + + def mpl_replot(self): + self.mpl.canvas.ax.clear() + self.mpl.canvas.ax_1.clear() + self.mpl.canvas.ax_2.clear() + self.mpl1.canvas.ax.clear() + self.mpl1.canvas.ax_1.clear() + self.mpl1.canvas.ax_2.clear() + self.mpl.canvas.draw_idle() + self.mpl1.canvas.draw_idle() + self.mplwidget() + +if __name__ == '__main__': + app = QtWidgets.QApplication([sys.argv]) + gui = DesignerMainWindow() + gui.show() + sys.exit(app.exec_()) -- cgit v1.2.3-65-gdbad