Commit ad0b9984 authored by Brice Gossin's avatar Brice Gossin

Upload New File

parent 6db92cb2
# -*- coding: utf-8 -*-
import sys
import csv
import requests
import json
import os
import re
import pandas as pd
import pyqtgraph as pg
import numpy as np
from PyQt5 import QtGui
from PyQt5.QtWidgets import * #QWebView
from PyQt5.QtCore import QTimer, QUrl, Qt, QDate, QFile
from PyQt5.QtGui import QDesktopServices, QColor, QTextDocumentWriter
from datetime import datetime
#from WebEngine import QWebEngineView
from influxdb import InfluxDBClient
class MainWindow(QMainWindow):
def __init__(self):
super(MainWindow, self).__init__()
self.graphs_window=None
# Configurer la connexion InfluxDB
self.influxdb_host = 'localhost'
self.influxdb_port = 8086
self.influxdb_database = 'LoRa_data'
self.influxdb_measurement = 'LoRa_sensors_data'
self.influxdb_client = InfluxDBClient(host=self.influxdb_host, port=self.influxdb_port, database=self.influxdb_database)
# Configurer l'IHM
self.setWindowTitle("InfluxDB Data Viewer")
screen_geometry=QApplication.desktop().screenGeometry()
self.setGeometry(0,70, 1920,1010)
# Créer un layout vertical
Vlayout = QVBoxLayout()
#Vlayout.setContentsMargins(20,20,20,20)
#layout horizontal
top_layout=QHBoxLayout()
#créer un menu
menubar=self.menuBar()
#créer un menu fichier
file_menu=menubar.addMenu("&File")
#créer l'action enregistrer
save_action=QAction("&Save the array", self)
save_action.setShortcut("Ctrl+S")
save_action.triggered.connect(self.saveFile)
#ajouter enregistrer à fichier
file_menu.addAction(save_action)
#Créer un bouton pour ouvrir une nouvelle page
graphs_button=QAction("Graphs", self)
graphs_button.triggered.connect(self.open_graphs_window)
menubar.addAction(graphs_button)
#graphs_button.setFixedSize(1000,1000)
#graphs_button.raise_()
#graphs_button.clicked.connect(self.open_graphs_window)
#Vlayout.addWidget(graphs_button)
#top_layout.addWidget(graphs_button)
#nouveau bouton pour ouvrir une autre page
photo_button=QAction("Pictures", self)
photo_button.triggered.connect(self.open_photo_window)
menubar.addAction(photo_button)
#graphs_button.setFixedSize(1000,1000)
#photo_button.raise_()
#photo_button.clicked.connect(self.open_photo_window)
#Vlayout.addWidget(photo_button)
#nouveau bouton load data pour importer les données de la carte sd
load_data_button=QAction("Load Data", self)
load_data_button.triggered.connect(self.import_program)
menubar.addAction(load_data_button)
#nouveau bouton "what to do in case of danger"
self.danger_button=QPushButton("What to do in case of danger")
self.danger_button.raise_()
self.danger_button.clicked.connect(self.open_danger_window)
Vlayout.addWidget(self.danger_button)
# Créer un tableau pour afficher les données
self.table_widget = QTableWidget(self)
self.table_widget.setColumnCount(11) # 9 colonnes pour toutes les valeurs importées + le temps
self.table_widget.setHorizontalHeaderLabels(['Time', 'Humidity', 'Luminosity','Mouvement', 'Outside temperature', 'RTC', 'Inside temperature', 'x', 'y', 'z', 'Photo?'])
self.table_widget.setColumnWidth(0, 300)
self.table_widget.setColumnWidth(4, 200)
self.table_widget.setColumnWidth(6, 200)
self.table_widget.setColumnWidth(5, 150)
Vlayout.addWidget(self.table_widget)
# Créer un widget central
central_widget = QWidget(self)
central_widget.setLayout(Vlayout)
self.setCentralWidget(central_widget)
#Créer un timer pour que la page s'actualise toutes les 0.5s
self.timer=QTimer(self)
self.timer.timeout.connect(self.load_influxdb_data)
self.timer.start(500)
# Charger et afficher les données InfluxDB
self.load_influxdb_data()
def load_influxdb_data(self):
self.table_widget.setRowCount(0)
# Exemple de requête InfluxDB pour obtenir les 100 dernières entrées
query = f'SELECT * FROM "{self.influxdb_measurement}" LIMIT 10000'
try:
result = self.influxdb_client.query(query)
points = result.get_points()
# Remplir le tableau avec les données
row_position = 0
for point in points:
timestamp = point['time'] #extrait le timestamp du point
temp=QTableWidgetItem(str(point['System_temperature'])) #variable pour stocker les valeurs de tempé interne et leur mettre une couleur
if point['System_temperature']>45: #on définit un seuil (ici 35°C)
temp.setBackground(QColor("red")) #si la tempé dépasse le seuil, on colore la case en rouge
clignote=True
else:
clignote=False
mouv=QTableWidgetItem(str(point['Mouvement'])) #variable pour stocker les valeurs de mouvement et leur mettre une couleur
if point['Mouvement'] ==1: #Si on a 1, le systeme a bougé
mouv.setBackground(QColor("red")) #on colore la case en rouge
clignote=True
else:
clignote=False
photo=QTableWidgetItem(str(point['Photo']))
if point['Photo'] == 1: #Si on a pas 0, une photo a été prise
photo.setBackground(QColor("green")) #on colore la case en vert
p = True
self.table_widget.insertRow(row_position) #insère une nouvelle ligne dans le tableau
self.table_widget.setItem(row_position, 0, QTableWidgetItem(str(timestamp))) #insère un point en fonction de sa position et du nom de son champ
self.table_widget.setItem(row_position, 1, QTableWidgetItem(str(point['Humidity'])))
self.table_widget.setItem(row_position, 2, QTableWidgetItem(str(point['Luminosity'])))
self.table_widget.setItem(row_position, 3, mouv)
self.table_widget.setItem(row_position, 4, QTableWidgetItem(str(point['Outside_temperature'])))
self.table_widget.setItem(row_position, 5, QTableWidgetItem(str(point['RTC'])))
self.table_widget.setItem(row_position, 6, temp) #ajout de la case éventuellement colorée
self.table_widget.setItem(row_position, 7, QTableWidgetItem(str(point['c_x'])))
self.table_widget.setItem(row_position, 8, QTableWidgetItem(str(point['c_y'])))
self.table_widget.setItem(row_position, 9, QTableWidgetItem(str(point['c_z'])))
self.table_widget.setItem(row_position, 10, photo)
row_position += 1
except Exception as e:
print(f"Erreur lors de la récupération des données InfluxDB : {e}")
#déplacer la vue vers le bas
self.table_widget.scrollToBottom()
if clignote is True :
#self.danger_button.setStyleSheet("background-color: red;")
for _ in range(150):
self.danger_button.setStyleSheet("background-color: white;")
QTimer.singleShot(1000, lambda: self.danger_button.setStyleSheet("background-color: red;"))
else:
self.danger_button.setStyleSheet("background-color: white;")
#if self.graphs_window is not None:
# timestamps=[self.table_widget.item(row,0).text() for row in range(self.table_widget.rowCount())] #exportation des données dans le tableau pour en faire des graphes
# humidity=[self.table_widget.item(row,1).text() for row in range(self.table_widget.rowCount())]
#self.graphs_window.create_graphs(timestamps, humidity) #exportation
def saveFile (self): #Fonction pour enregistrer en csv
file_path, _ = QFileDialog.getSaveFileName(self, "Save the array", "", "CSV File (*.csv)") # boite de dialogue
if file_path:
try:
with open(file_path, "w", newline="") as file: #ouvrir le fichier.csv en mode écriture
writer=csv.writer(file)
for row in range(self.table_widget.rowCount()): #parcourir les lignes du tableau
row_data=[]
for col in range (self.table_widget.columnCount()): #parcourir les colonnes
item=self.table_widget.item(row, col) #obtenir le texte dans chaque cellule
if item is not None:
row_data.append(item.text())
else:
row_data.append("")
writer.writerow(row_data) #écrire le texte dans le fichier.csv
except Exception as e:
print(f"Erreur lors de l'enregistrement du fichier : {e}")
def open_graphs_window(self): #ouverture de la nouvelle fenetre
if self.graphs_window is None:
self.graphs_window=GraphWindow()
self.graphs_window.setWindowTitle("Graphs window")
self.graphs_window.show()
def open_photo_window(self): #ouverture de la nouvelle fenetre
self.photo_window=PhotoWindow()
self.photo_window.setWindowTitle("Pictures window")
self.photo_window.show()
def open_danger_window(self): #ouverture de la nouvelle fenetre
self.danger_window=DangerWindow()
self.danger_window.setWindowTitle("What to do in case of danger")
self.danger_window.show()
def import_program(self):
# Configurer la connexion InfluxDB
influxdb_host = 'localhost'
influxdb_port = 8086
influxdb_database = 'LoRa_data'
influxdb_measurement = 'LoRa_sensors_data'
influxdb_client = InfluxDBClient(host=influxdb_host, port=influxdb_port, database=influxdb_database)
options = QFileDialog.Options()
options |= QFileDialog.DontUseNativeDialog
file_name, _ = QFileDialog.getOpenFileName(self, "Choisir un fichier CSV", "/media/pi/7.8 GB Volume", "Fichiers CSV (*.csv)", options=options)
#chemin du fichier csv
#csv_directory='/home/pi/Desktop/Load_SD_card_data'
#csv_filename='DATA.CSV'
#csv_path=os.path.join(csv_directory, csv_filename) #combine les deux variables pour créer un chemin d'accès complet
if file_name:
data=pd.read_csv(file_name) #lire le fichier.csv avec pandas
#parcourir les lignes du fichier.csv et créer la trame influxdb
points=[]
for index, row in data.iterrows():
timestamp=int(row['RTC'])*10**9
tags={'tag1':'SD'}
fields={'Mouvement': row['Movement'],
'RTC':row ['RTC'],
'System_temperature': row['Internal temperature'],
'Outside_temperature': row['External temperature'],
'Humidity': row['Humidity'],
'Luminosity': row['Luminosity'],
'c_x': row['x'],
'c_y': row['y'],
'c_z': row['z'],
'Photo': row['Photo']}
point={'measurement' : influxdb_measurement, 'tags':tags, 'time':timestamp, 'fields':fields}
points.append(point)
# LoRa_sensors_data,Photo=1 Mouvement=%s,RTC=%s,System_temperature=%s,Outside_temperature=%s,Humidity=%s,Luminosity=%s,c_x=%s,c_y=%s,c_z=%s"
try:
influxdb_client.write_points(points)
msg = QMessageBox()
msg.setWindowTitle('Success')
msg.setText('Success ! Data added to the database')
msg.exec_()
except Exception as e:
msg = QMessageBox()
msg.setWindowTitle('Fail')
msg.setText('Fail, retry the operation')
msg.exec_()
# self.import_program('/home/pi/Desktop/Load_SD_card_data', 'DATA.CSV')
class GraphWindow(QMainWindow):
def __init__(self):
super(GraphWindow, self).__init__() #config de la nouvelle fenetre
self.setWindowTitle("Graphs Window")
self.setGeometry(200,200,1000,1000)
central_widget=QWidget()
self.setCentralWidget(central_widget)
Vlayout = QVBoxLayout(central_widget)
#Config lien vers Grafana
self.link_label=QLabel('<a href="http://localhost:3000/d/cdfk1ap9jf8jkb/monitoring?orgId=1&refresh=5s&from=1710346802826&to=1710346822354">Lien vers les graphes</a>')
self.link_label.setOpenExternalLinks(True) #permet d'ouvrir le lien dans le navigateur
Vlayout.addWidget(self.link_label)
self.link_label.linkActivated.connect (self.openLink)
def openLink (self, url):
firefox_path="/usr/bin/firefox"
subprocess.Popen([firefox_path, url])
#Config graphe
#self.plot_widget=pg.PlotWidget()
#self.setCentralWidget(self.plot_widget)
#self.plot_widget.setBackground('w')
#self.plot_widget.setGeometry(0,0,500, 500)
#self.plot_widget.getAxis('bottom').setTickSpacing(60,60)
# def create_graphs(self, timestamps, humidity): #fonction pour créer un graphe (marche pas de fou mais affiche des points)
# x_data= [time.mktime(time.strptime(ts, "%Y-%m-%dT%H:%M:%S.%fZ"))for ts in timestamps]
# y_data=[float(hum) for hum in humidity]
#self.plot_widget.clear()
#self.plot_widget.plot(x_data, y_data, pen='b', symbol='o', symbolPen='b', symbolBrush='b')
class PhotoWindow(QMainWindow):
def __init__(self):
super(PhotoWindow, self).__init__() #config de la nouvelle fenetre
self.setWindowTitle("Pictures Window")
self.setGeometry(0,70, 1920,1010)
central_widget=QWidget()
self.setCentralWidget(central_widget)
Vlayout = QVBoxLayout(central_widget)
msg = QMessageBox() #Message box pour expliquer le fonctionnement de la page à l'utilisateur
msg.setWindowTitle('Pictures window')
msg.setText('Load the pictures SD card in the Raspberry and navigate in the calendar to see the pictures.')
msg.exec_()
menubar=self.menuBar()
#créer un menu fichier
file_menu=menubar.addMenu("&File")
#créer l'action enregistrer
save_action=QAction("&Save pictures", self)
save_action.setShortcut("Ctrl+S")
save_action.triggered.connect(self.savePictures)
#ajouter enregistrer à fichier
file_menu.addAction(save_action)
self.image_repository="/media/pi/3396-5C27/DCIM/105_PANA"
self.image_names=[]
self.url_image=[None]*1000
for fichier in os.listdir(self.image_repository): #on va chercher les noms des photos avec l'url du repertoire dans la carte sd
if fichier.endswith(".jpg"):
self.image_names.append(fichier)
i=0
for name in self.image_names:
self.url_image[i]=(self.image_repository + "/" + name) #on forme les url des photos avec leur repertoire et leurs noms puis on les stocke dans un tableau
i+=1
# Configurer la connexion InfluxDB
influxdb_host = 'localhost'
influxdb_port = 8086
influxdb_database = 'LoRa_data'
influxdb_measurement = 'LoRa_sensors_data'
influxdb_client = InfluxDBClient(host=influxdb_host, port=influxdb_port, database=influxdb_database)
influx_query=f"SELECT RTC, System_temperature, Outside_temperature, Humidity, Luminosity FROM {influxdb_measurement} WHERE Photo =1" #requete influxdb pour avoir les données quand photo=1
result=influxdb_client.query(influx_query)
first_series = None
previous_timestamp = None
formats = ['%Y-%m-%dT%H:%M:%S.%fZ', '%Y-%m-%dT%H:%M:%SZ']
self.data = [None] * 100000 # Initialisation d'une liste vide pour stocker les séries de données
self.first_data=None
self.data_str=[None]*100000
i = 0
for point in result.get_points():
for format in formats:
try: #choisir le bon format de date pour les données importées et reçues par LoRa (les formats sont différents à l'origine)
timestamp = datetime.strptime(point['time'], format).timestamp()
break
except ValueError:
continue
if previous_timestamp is None:
previous_timestamp = timestamp
self.first_data=point
if timestamp - previous_timestamp >= 18 * 3600:
first_series = point
self.data[i] = first_series
#self.data_str[self.i]=json.dumps(self.data[i])
# print("Première série de la journée :", first_series)
i += 1 # Incrémentation de l'indice pour stocker la prochaine série
previous_timestamp = timestamp
#print("Série de données 0 :", self.first_data)
# Affichage des données stockées dans la liste 'data'
# for j in range(self.i):
# print("Série de données", j+1, ":", self.data[j])
self.calendar=QCalendarWidget() #objet calendrier
Vlayout.addWidget(self.calendar)
self.calendar.clicked[QDate].connect(self.show_data)
# self.calendar.setStyleSheet("QCalendarWidget QAbstractItemView:item:selected {background-color: red;}""QCalendarWidget QAbstractItemView:item:hover {background-color: red;}")
self.data_textbox=QTextEdit()
self.data_textbox.setReadOnly(True)
Vlayout.addWidget(self.data_textbox)
self.prev_button=QPushButton('<')
self.next_button=QPushButton('>')
self.prev_button.clicked.connect(self.prev_day)
self.next_button.clicked.connect(self.next_day)
Vlayout.addWidget(self.prev_button)
Vlayout.addWidget(self.next_button)
# Vlayout.setAlignment(Qt.AlignTop)
start_date=QDate(2020,1,1)
end_date=QDate(2100,12,31)
current_date=start_date
while current_date<=end_date:
self.calendar.setDateTextFormat(current_date,self.get_black())
current_date=current_date.addDays(1)
def prev_day (self): #fonction pour passer au jour précédent
current_date=self.calendar.selectedDate()
new_date=current_date.addDays(-1)
self.calendar.setSelectedDate(new_date)
self.calendar.clicked.emit(new_date)
def next_day (self): #fonction pour passer au jour suivant
current_date=self.calendar.selectedDate()
new_date=current_date.addDays(1)
self.calendar.setSelectedDate(new_date)
self.calendar.clicked.emit(new_date)
def show_data (self,date): #Fonction qui affiche les photos et les données
self.data_textbox.clear()
formats = ['%Y-%m-%dT%H:%M:%S.%fZ', '%Y-%m-%dT%H:%M:%SZ']
date0_str=self.first_data['time']
if date0_str is not None: #On range la premiere série de données à la bonne date
for format in formats:
try: #choisir le bon format de date pour les données importées et reçues par LoRa (les formats sont différents à l'origine)
date0_obj= datetime.strptime(date0_str, format)
y0=date0_obj.year
m0=date0_obj.month
d0=date0_obj.day
if self.first_data:
if date == QDate(y0, m0, d0):
#self.calendar.setDateTextFormat(QDate(y0,m0,d0),self.get_special_format())
#self.data_textbox.append("Data: " + str(self.first_data))#self.url_image[i]
html="<div align=center>{}<br><img src='{}'></div>".format("Data: " + str(self.first_data), self.url_image[0])
self.data_textbox.setHtml(html)
self.data_textbox.show()
self.calendar.setDateTextFormat(QDate(y0,m0,d0),self.get_green())
else:
self.calendar.setDateTextFormat(QDate(y0,m0,d0),self.get_red())
self.data_textbox.append("No data available")
break
except ValueError:
continue
i=1
for item in self.data: #On range toutes les autres aux bonnes dates
if item is not None:
date_str=item['time']
for format in formats:
try: #choisir le bon format de date pour les données importées et reçues par LoRa (les formats sont différents à l'origine)
date_obj = datetime.strptime(date_str, format)
year=date_obj.year
month=date_obj.month
day=date_obj.day
if self.data is not None:
if date == QDate(year, month, day):
# self.data_textbox.append("Data: " + str(item))
html="<div align=center>{}<br><img src='{}'></div>".format("Data: " + str(item), self.url_image[i])
print (self.url_image[i])
self.data_textbox.setHtml(html)
self.data_textbox.show()
self.calendar.setDateTextFormat(QDate(year, month, day),self.get_green())
i+=1
else:
self.data_textbox.append("No data available")
self.calendar.setDateTextFormat(QDate(year, month, day),self.get_red())
break
except ValueError:
continue
def get_black(self):
special_format=self.calendar.dateTextFormat(QDate(2024,4,10))
special_format.setForeground(Qt.black)
return special_format
def get_green(self):
special_format=self.calendar.dateTextFormat(QDate(2024,4,10))
special_format.setForeground(Qt.green)
return special_format
def get_red(self):
special_format=self.calendar.dateTextFormat(QDate(2024,4,10))
special_format.setForeground(Qt.red)
return special_format
def savePictures(self): #Fonction pour sauvegarder les images+données
file_path, _ = QFileDialog.getSaveFileName(self, "Save the picture", "", "HTML File (*.html)") # boite de dialogue
if file_path:
document=self.data_textbox.document()
html_writer=QTextDocumentWriter(file_path)
success=html_writer.write(document)
if success:
msg = QMessageBox()
msg.setWindowTitle('Success')
msg.setText('Picture and data have been saved successfully.')
msg.exec_()
else:
msg = QMessageBox()
msg.setWindowTitle('Fail')
msg.setText('Saving process failed, retry the operation. Be certain that the name you chose is like "name.html".')
msg.exec_()
class DangerWindow(QMainWindow):
def __init__(self):
super(DangerWindow, self).__init__() #config de la nouvelle fenetre
self.setWindowTitle("What to do in case of danger")
self.setGeometry(200,200,250,250)
central_widget=QWidget()
self.setCentralWidget(central_widget)
Vlayout = QVBoxLayout(central_widget)
paragraph_mouv="""
<h1>Movement alert</h1>
<p>The system has moved over the limit. The pictures angle might be abnormal. You need to go to the system, collect the pictures SD card, and reset the system.</p>
<h1>Temperature alert</h1>
<p>The system's temperature exceeded the threshold, if the problem is persistent (3 alerts), you will need to check the system's functionning by collecting the pictures and data SD cards</p>
"""
label =QLabel(paragraph_mouv, self)
Vlayout.addWidget(label)
Vlayout.setContentsMargins(20,20,20,20)
container = QWidget()
container.setLayout(Vlayout)
self.setCentralWidget(container)
#Vlayout.addWidget(self.label)
#self.label.setText(
def import_program(self):
# Configurer la connexion InfluxDB
influxdb_host = 'localhost'
influxdb_port = 8086
influxdb_database = 'LoRa_data'
influxdb_measurement = 'LoRa_sensors_data'
influxdb_client = InfluxDBClient(host=influxdb_host, port=influxdb_port, database=influxdb_database)
options = QFileDialog.Options()
options |= QFileDialog.DontUseNativeDialog
file_name, _ = QFileDialog.getOpenFileName(self, "Choisir un fichier CSV", "/media/pi/7.8 GB Volume", "Fichiers CSV (*.csv)", options=options)
#chemin du fichier csv
#csv_directory='/home/pi/Desktop/Load_SD_card_data'
#csv_filename='DATA.CSV'
#csv_path=os.path.join(csv_directory, csv_filename) #combine les deux variables pour créer un chemin d'accès complet
if file_name:
data=pd.read_csv(file_name) #lire le fichier.csv avec pandas
#parcourir les lignes du fichier.csv et créer la trame influxdb
points=[]
for index, row in data.iterrows():
timestamp=int(row['RTC'])*10**9
tags={'tag1':'SD'}
fields={'Mouvement': row['Movement'],
'RTC':row ['RTC'],
'System_temperature': row['Internal temperature'],
'Outside_temperature': row['External temperature'],
'Humidity': row['Humidity'],
'Luminosity': row['Luminosity'],
'c_x': row['x'],
'c_y': row['y'],
'c_z': row['z']}
point={'measurement' : influxdb_measurement, 'tags':tags, 'time':timestamp, 'fields':fields}
points.append(point)
# LoRa_sensors_data,Photo=1 Mouvement=%s,RTC=%s,System_temperature=%s,Outside_temperature=%s,Humidity=%s,Luminosity=%s,c_x=%s,c_y=%s,c_z=%s"
try:
influxdb_client.write_points(points)
msg = QMessageBox()
msg.setWindowTitle('Success')
msg.setText('Success ! Data added to the database')
msg.exec_()
except Exception as e:
msg = QMessageBox()
msg.setWindowTitle('Fail')
msg.setText('Fail, retry the operation')
msg.exec_()
# self.import_program('/home/pi/Desktop/Load_SD_card_data', 'DATA.CSV')
def main():
app = QApplication(sys.argv)
main_window = MainWindow()
main_window.show()
#x_data=[point['time']for point in points]
#y_data=[point['Humidity']]
#graph_window = NewWindow(x_data, y_data)
sys.exit(app.exec_())
if __name__ == "__main__":
main()
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment