2001 lines
102 KiB
QML
2001 lines
102 KiB
QML
import QtQuick 2.12
|
|
import QtQuick.Controls 2.12
|
|
import QtQuick.Layouts 1.12
|
|
import QtCharts 2.3
|
|
|
|
import qt.koriolan.shipheart 1.0
|
|
import"fonctions.js" as Fonctions
|
|
import"fonctions_date_heure.js" as Fonctions_date_heure
|
|
|
|
|
|
Shipheart_page {
|
|
id: page_conso_globale
|
|
|
|
|
|
// Mis en forme :
|
|
readonly property int espace_colonnes_secondaires: 10
|
|
readonly property double pourcentage_hauteur_ligne_haut: 0.6
|
|
readonly property int espace_lignes_principales: 20
|
|
readonly property int espace_lignes_secondaires: 10
|
|
readonly property int nombre_widgets_par_ligne: 4
|
|
readonly property double pourcentage_largeur_graphique: 0.85
|
|
readonly property int nb_boutons_periode: 4
|
|
readonly property int largeur_bouton_suite: 15
|
|
|
|
readonly property int periode_init_raffraichissement_parametres: 1 // En ms ATtention : The Timer type is synchronized with the animation timer. Since the animation timer is usually set to 60fps, the resolution of Timer will be at best 16ms.
|
|
readonly property int periode_raffraichissement_parametres: 1000 // En ms
|
|
readonly property int periode_init_raffraichissement_graphiques: 1 // En ms
|
|
readonly property int periode_raffraichissement_graphiques: 15 * 1000 // en ms
|
|
|
|
|
|
// Affichage :
|
|
property int periode_analyse: 0
|
|
|
|
property bool courbe_flag_vide: false // Flag indiquant s'il y a des données à afficher ou pas
|
|
property int courbe_nombre_principal_graduations_axe_x: 13
|
|
property int courbe_nombre_zoom_1_graduations_axe_x: 8
|
|
property int courbe_nombre_graduations_axe_y: 5
|
|
property string courbe_format_principal_axe_x: "HH:mm" // exemple : "yyyy-MM-dd HH:mm:ss"
|
|
property string courbe_format_zoom_1_axe_x: "HH:mm" // exemple : "yyyy-MM-dd HH:mm:ss"
|
|
|
|
property bool flag_zoom_en_cours: false
|
|
property real zoomx_en_cours: 1
|
|
|
|
property date xMin_date
|
|
property date xMax_date
|
|
property real yMin_valeur: 0
|
|
property real yMax_valeur: 1
|
|
property int precision_affichage_axe_y_courbe: 1
|
|
property string symbole_unite: "W"
|
|
|
|
property int etat_systeme: 0 // Le '0' permet simplement d'aviter l'affichage du message d'erreur pendant un très court instant au démarrage
|
|
|
|
|
|
// Données :
|
|
property bool flag_init: true // Permet de bloquer certains affichage tant que l'init n'est pas terminée
|
|
|
|
property int id_batterie: 0
|
|
property int id_variable: 0
|
|
property string nom: ""
|
|
property int nombre_cycles: 0
|
|
property int nombre_decharges_completes: 0
|
|
property int type_entrees: 0
|
|
property string temperature: ""
|
|
property int temperature_id_variable: 0
|
|
property string temperature_unite_variable: ""
|
|
property string tension: ""
|
|
property int tension_id_variable: 0
|
|
property string tension_unite_variable: ""
|
|
property string courant_differentiel: ""
|
|
property int courant_differentiel_id_variable: 0
|
|
property string courant_differentiel_unite_variable: ""
|
|
property string courant_charge: ""
|
|
property int courant_charge_id_variable: 0
|
|
property string courant_charge_unite_variable: ""
|
|
property string courant_decharge:""
|
|
property int courant_decharge_id_variable: 0
|
|
property string courant_decharge_unite_variable: ""
|
|
property string puissance: ""
|
|
property int puissance_id_variable: 0
|
|
property string puissance_unite_variable: ""
|
|
property string soh: ""
|
|
property int soh_id_variable: 0
|
|
property string soh_unite_variable: ""
|
|
property string soc: ""
|
|
property int soc_id_variable: 0
|
|
property string soc_unite_variable: ""
|
|
property string autonomie: ""
|
|
property int autonomie_id_variable: 0
|
|
property string autonomie_unite_variable: ""
|
|
property string energie: ""
|
|
property int energie_id_variable: 0
|
|
property string energie_unite_variable: ""
|
|
|
|
property int id_variable_energie_circuit: 0
|
|
property int id_variable_puissance_circuit: 0
|
|
|
|
property double pourcentage_maxi: 0
|
|
|
|
|
|
|
|
|
|
// Modèles :
|
|
// Note : les modèles chargés dans 'main.qml' n'ont pas à être rechargés dans les pages suivantes
|
|
|
|
|
|
//===========================================================
|
|
// Timer periode raffraichissement parametres capteur :
|
|
//===========================================================
|
|
Timer {
|
|
id: timer_raffraichissement_parametres
|
|
interval: periode_init_raffraichissement_parametres
|
|
triggeredOnStart: false
|
|
running: true
|
|
repeat: true
|
|
onTriggered: {
|
|
if (interval == periode_init_raffraichissement_parametres) { // Premier passage, 1ms après le premier chargement
|
|
interval = periode_raffraichissement_parametres;
|
|
init_page();
|
|
}
|
|
else {
|
|
mise_a_jour_valeurs_instantanées();
|
|
}
|
|
}
|
|
}
|
|
|
|
//===========================================================
|
|
// Timer periode raffraichissement graphiques :
|
|
//===========================================================
|
|
Timer {
|
|
id: timer_raffraichissement_graphiques
|
|
interval: periode_raffraichissement_graphiques
|
|
triggeredOnStart: false
|
|
running: true
|
|
repeat: true
|
|
onTriggered: {
|
|
if (interval === periode_init_raffraichissement_graphiques) interval = periode_raffraichissement_graphiques;
|
|
|
|
if (!flag_init) // La gestion du premier calcul des courbes est géré directement par 'init()' car le séquençage des 2 timers au démarrage n'est pas garanti !!
|
|
{
|
|
if (id_variable === 0) // Affichage du bilan d'énergie :
|
|
{
|
|
comparatif_conso_energie();
|
|
}
|
|
else // Affichage d'une courbe spécifique :
|
|
{
|
|
if (!flag_zoom_en_cours)
|
|
{
|
|
mise_a_jour_courbes(); // On ne met pas à jour les courbes si on est en train de zoomer
|
|
mise_a_jour_valeur_energie(); // On utilise ce timer "lent" pour mettre à jour cette valeur qui évolue peu
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// Titre de la page :
|
|
Shipheart_titre_1 {titre: nom; hauteur: shipheart_hauteur_bandeau_haut}
|
|
|
|
|
|
|
|
//-------------------------------------------------------
|
|
// Ecran haut :
|
|
//-------------------------------------------------------
|
|
Rectangle {
|
|
id: zone_affichage_haut
|
|
x: 0
|
|
y: shipheart_hauteur_bandeau_haut;
|
|
width: shipheart_largeur_page
|
|
height: (shipheart_hauteur_page - shipheart_hauteur_bandeau_haut - shipheart_hauteur_bandeau_bas - (2 * espace_lignes_principales)) * pourcentage_hauteur_ligne_haut;
|
|
color: "transparent"
|
|
visible: (etat_systeme === 0)
|
|
|
|
//--------------------
|
|
// Grille principale:
|
|
GridView {
|
|
id : grille_donnees_principale
|
|
|
|
property int index_base_en_cours: 0 // Utilsé pour stocker l'index du premier widget affiché à gauche
|
|
|
|
highlightRangeMode: GridView.StrictlyEnforceRange // Nécessaire pour que 'index_base_en_cours' soit mis à jour. Au passage, autorise de faire défiler les widgets jusqu'à ne garder que le dernier visible (pas génant et même plutôt sympa...)
|
|
width: parent.width - (2 * largeur_bouton_suite)
|
|
height: (type_entrees === shipheart_batteries_type_entrees_ibs) ? parent.height : (parent.height / 2)
|
|
cellWidth: width / nombre_widgets_par_ligne
|
|
cellHeight: (type_entrees === shipheart_batteries_type_entrees_ibs) ? (height / 2) : height
|
|
flow: GridView.FlowTopToBottom // Items are laid out from top to bottom, and the view scrolls horizontally
|
|
snapMode: GridView.SnapToRow // L'arret de l'affichage se fera sur une rangée entière
|
|
anchors.verticalCenter: parent.verticalCenter // Utile pour les batteries sans IBS (une seule ligne de widgets)
|
|
anchors.horizontalCenter: parent.horizontalCenter
|
|
clip: true // Nécessaire pour que les Delegates s'arretent à l'intérieur de la GridView
|
|
model: ListModel { }
|
|
|
|
onMovementEnded: {
|
|
index_base_en_cours = currentIndex; // Capte la valeur de currentIndex aà la fin du mouvement (la valeur vaut alors l'index du widget le plus à gauche). Si on ne le capte pas, la valeur de CurrentIndex repasse à 0 après l'arrêt du flicker (pourquoi...???)
|
|
//console.log("index_base_en_cours = "+ index_base_en_cours)
|
|
}
|
|
|
|
delegate: Rectangle { // Permet d'occuper toute la cellule de la grille
|
|
width: grille_donnees_principale.cellWidth
|
|
height: grille_donnees_principale.cellHeight
|
|
color: "transparent"
|
|
|
|
Rectangle { // Permet d'avoir un bouton plus petit que la cellule de la grille et de le centrer sur la cellule (=> espace entre boutons)
|
|
width: parent.width - espace_colonnes_secondaires;
|
|
height: parent.height - espace_lignes_secondaires;
|
|
color: (id_variable === model.id_var) ? shipheart_couleur_fond_bouton_actif : shipheart_couleur_fond_bouton
|
|
radius: shipheart_rayon_bouton
|
|
anchors {
|
|
verticalCenter: parent.verticalCenter
|
|
horizontalCenter: parent.horizontalCenter
|
|
}
|
|
|
|
//-----------------------------------
|
|
// Titre :
|
|
Text {
|
|
id: titre
|
|
text: model.titre
|
|
padding: 10
|
|
font.pixelSize: 18
|
|
color: couleur_blanc_casse
|
|
}
|
|
|
|
//-----------------------------------
|
|
// Valeur courante :
|
|
Text {
|
|
id: valeur
|
|
text: model.valeur
|
|
anchors {
|
|
verticalCenter: parent.verticalCenter
|
|
horizontalCenter: parent.horizontalCenter
|
|
horizontalCenterOffset: soh_icone.visible ? 0 : -20 // Permet de garder l'ensemble 'valeur + unité) centré sans utiliser un conteneur spécial
|
|
}
|
|
font.family: "Montserrat"
|
|
font.pixelSize: 60
|
|
font.weight: Font.Black
|
|
color: (model.etat <= 0) ?
|
|
( ((model.type_couleur !== 0) && (model.valeur < 0)) ?
|
|
couleur_orange :
|
|
couleur_blanc_casse ) :
|
|
couleur_rouge
|
|
}
|
|
Text {
|
|
id: unite
|
|
text: model.unite
|
|
anchors {
|
|
baseline: valeur.baseline
|
|
left: valeur.right
|
|
}
|
|
leftPadding: 10
|
|
font.pixelSize: 20
|
|
// color: (model.etat === 0) ? couleur_blanc_casse : couleur_rouge
|
|
color: couleur_blanc_casse
|
|
}
|
|
|
|
//-----------------------------------
|
|
// SOH :
|
|
Image {
|
|
id: soh_icone
|
|
source: (id_variable === model.id_var) ? "/images/soh.png" : "/images/soh_sombre.png"
|
|
sourceSize.width: 20
|
|
anchors {
|
|
top: parent.top
|
|
topMargin: 50
|
|
left: parent.left
|
|
leftMargin: 20
|
|
}
|
|
visible: (type_entrees === shipheart_batteries_type_entrees_ibs) && flag_affichage_complementaire
|
|
}
|
|
Text {
|
|
id: soh_valeur
|
|
text: soh + "%"
|
|
anchors {
|
|
verticalCenter: soh_icone.verticalCenter
|
|
left: soh_icone.right
|
|
leftMargin: 5
|
|
}
|
|
font.pixelSize: 10
|
|
color: (id_variable === model.id_var) ? couleur_blanc_casse : couleur_gris_moyen
|
|
visible: soh_icone.visible
|
|
}
|
|
|
|
//-----------------------------------
|
|
// Cycles :
|
|
Image {
|
|
id: cycles_icone
|
|
source: (id_variable === model.id_var) ? "/images/cycle_clair.png" : "/images/cycle_sombre.png"
|
|
sourceSize.width: soh_icone.sourceSize.width
|
|
anchors {
|
|
top: parent.top
|
|
topMargin: soh_icone.anchors.topMargin + 30
|
|
left: parent.left
|
|
leftMargin: soh_icone.anchors.leftMargin
|
|
}
|
|
visible: (type_entrees === shipheart_batteries_type_entrees_ibs) && flag_affichage_complementaire
|
|
}
|
|
Text {
|
|
id: cycles_valeur
|
|
text: nombre_cycles
|
|
anchors {
|
|
verticalCenter: cycles_icone.verticalCenter
|
|
left: cycles_icone.right
|
|
leftMargin: soh_valeur.anchors.leftMargin
|
|
}
|
|
font.pixelSize: soh_valeur.font.pixelSize
|
|
color: (id_variable === model.id_var) ? couleur_blanc_casse : couleur_gris_moyen
|
|
visible: cycles_icone.visible
|
|
}
|
|
|
|
//-----------------------------------
|
|
// Cycles :
|
|
Image {
|
|
id: decharges_completes_icone
|
|
source: (id_variable === model.id_var) ? "/images/cycle_100pc.png" : "/images/cycle_100pc_sombre.png"
|
|
sourceSize.width: soh_icone.sourceSize.width
|
|
anchors {
|
|
top: parent.top
|
|
topMargin: soh_icone.anchors.topMargin + 60
|
|
left: parent.left
|
|
leftMargin: soh_icone.anchors.leftMargin
|
|
}
|
|
visible: (type_entrees === shipheart_batteries_type_entrees_ibs) && flag_affichage_complementaire
|
|
}
|
|
Text {
|
|
id: decharges_completes_valeur
|
|
text: nombre_decharges_completes
|
|
anchors {
|
|
verticalCenter: decharges_completes_icone.verticalCenter
|
|
left: decharges_completes_icone.right
|
|
leftMargin: soh_valeur.anchors.leftMargin
|
|
}
|
|
font.pixelSize: soh_valeur.font.pixelSize
|
|
color: (id_variable === model.id_var) ? couleur_blanc_casse : couleur_gris_moyen
|
|
visible: decharges_completes_icone.visible
|
|
}
|
|
|
|
//-----------------------------------
|
|
// Zone de clic principale :
|
|
MouseArea{
|
|
width: parent.width
|
|
height: parent.height - zone_alarmes.height
|
|
anchors {
|
|
left: parent.left
|
|
top: parent.top
|
|
}
|
|
|
|
onClicked: {
|
|
shipheart_clic();
|
|
raz_zoom(); // Au-delà de la fonctionnalité, c'est obligatoire sinon, le raffraichissement des la courbe est bloqué au niveau du timer
|
|
if (id_variable === model.id_var) id_variable = 0;
|
|
else
|
|
{
|
|
id_variable = model.id_var;
|
|
symbole_unite = model.unite;
|
|
}
|
|
console.log("ID Variable = " + id_variable)
|
|
busy_indicator.running = true;
|
|
timer_raffraichissement_graphiques.interval = periode_init_raffraichissement_graphiques; // Cela permet de lancer le "busy_indicator' avant de faire la mise à jour de la courbe
|
|
}
|
|
}
|
|
|
|
//-----------------------------------
|
|
// Alarmes :
|
|
Rectangle {
|
|
id: zone_alarmes
|
|
width: parent.width
|
|
height: 50
|
|
color: shipheart_couleur_fond_bouton
|
|
radius: shipheart_rayon_bouton
|
|
anchors {
|
|
left: parent.left
|
|
bottom: parent.bottom
|
|
}
|
|
visible: model.etat >= 0
|
|
|
|
Image {
|
|
source: (model.etat === 0) ? "/images/cloche_sombre.png" : "/images/cloche_rouge.png"
|
|
sourceSize.height: 20
|
|
anchors {
|
|
verticalCenter: zone_alarmes.verticalCenter
|
|
horizontalCenter: zone_alarmes.horizontalCenter
|
|
}
|
|
}
|
|
|
|
//-----------------------------------
|
|
// Zone de clics dédiée aux alarmes :
|
|
MouseArea{
|
|
anchors.fill: zone_alarmes
|
|
|
|
onClicked: {
|
|
shipheart_clic();
|
|
boite_dialogue_liste_alarmes.id_variable = model.id_var;
|
|
boite_dialogue_liste_alarmes.titre_widget = model.titre;
|
|
boite_dialogue_liste_alarmes.unite = model.unite;
|
|
boite_dialogue_liste_alarmes.open();
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
}
|
|
}
|
|
} // Fin de Gridview
|
|
|
|
// Flèche de suite gauche :
|
|
Image {
|
|
anchors.verticalCenter: grille_donnees_principale.verticalCenter
|
|
anchors.right: grille_donnees_principale.left
|
|
source: "/images/fleche_suite_gauche_sombre.png"
|
|
width: largeur_bouton_suite
|
|
height: grille_donnees_principale.height - espace_lignes_secondaires
|
|
opacity: 0.5
|
|
visible: (!grille_donnees_principale.moving) && // Permet de ne pas afficher la flèche pendant le mouvement (surtout génant à cause du lag généré par la fin du mouvement qui est assez lent).
|
|
(grille_donnees_principale.index_base_en_cours > 0)
|
|
}
|
|
|
|
// Flèche de suite droite :
|
|
Image {
|
|
anchors.verticalCenter: grille_donnees_principale.verticalCenter
|
|
anchors.left: grille_donnees_principale.right
|
|
source: "/images/fleche_suite_droite_sombre.png"
|
|
width: largeur_bouton_suite
|
|
height: grille_donnees_principale.height - espace_lignes_secondaires
|
|
opacity: 0.5
|
|
visible: (!grille_donnees_principale.moving) && // Permet de ne pas afficher la flèche pendant le mouvement (surtout génant à cause du lag généré par la fin du mouvement qui est assez lent).
|
|
((grille_donnees_principale.count - grille_donnees_principale.index_base_en_cours) > (nombre_widgets_par_ligne * (grille_donnees_principale.height / grille_donnees_principale.cellHeight))) // le calcul 'height/cellheight' donne le nombre de lignes de widget (généralement, 1 ou 2)
|
|
}
|
|
|
|
}
|
|
|
|
|
|
//-------------------------------------------------------
|
|
// Ecran bas :
|
|
//-------------------------------------------------------
|
|
Rectangle {
|
|
id: zone_affichage_bas
|
|
x: 0
|
|
y: zone_affichage_haut.y + zone_affichage_haut.height + espace_lignes_principales;
|
|
width: shipheart_largeur_page;
|
|
height: (shipheart_hauteur_page - shipheart_hauteur_bandeau_haut - shipheart_hauteur_bandeau_bas - (2 * espace_lignes_principales)) * (1 - pourcentage_hauteur_ligne_haut) + 5; // +5 pour compenser margin-top du bandeau bas
|
|
color: "transparent"
|
|
visible: (etat_systeme === 0)
|
|
|
|
//===================================
|
|
// Comparatif consommation d'énergie
|
|
Rectangle {
|
|
id: zone_affichage_consommateurs
|
|
x: 0
|
|
y: 0
|
|
width: (parent.width * pourcentage_largeur_graphique) - espace_colonnes_secondaires;
|
|
height: parent.height
|
|
color: "transparent"
|
|
visible: (id_variable === 0)
|
|
|
|
GridView {
|
|
id : grille_consommateurs
|
|
width: parent.width
|
|
height: parent.height
|
|
cellWidth: parent.width / 8
|
|
cellHeight: parent.height
|
|
flow: GridView.FlowTopToBottom // Items are laid out from top to bottom, and the view scrolls horizontally
|
|
snapMode: GridView.SnapToRow // L'arret de l'affichage se fera sur une rangée entière
|
|
clip: true // Nécessaire pour que les Delegates s'arretent à l'intérieur de la GridView
|
|
model: ListModel { }
|
|
|
|
delegate: Rectangle { // Permet d'occuper toute la cellule de la grille
|
|
width: grille_consommateurs.cellWidth
|
|
height: grille_consommateurs.cellHeight
|
|
color: "transparent"
|
|
visible: !busy_indicator.running
|
|
|
|
Rectangle { // Permet d'avoir un bouton plus petit que la cellule de la grille et de le centrer sur la cellule (=> espace entre boutons)
|
|
id: bouton_consommateur
|
|
width: parent.width - espace_colonnes_secondaires;
|
|
height: parent.height;
|
|
color: "transparent"
|
|
anchors {
|
|
verticalCenter: parent.verticalCenter
|
|
horizontalCenter: parent.horizontalCenter
|
|
}
|
|
|
|
// Nom du circuit :
|
|
Text {
|
|
id: nom_consommateur
|
|
text: model.nom
|
|
width: parent.height - 75 // le '-75' permet de limiter le texte en partie haute. ATTENTION : la valeur influe sur le '60' ci-dessous ! Pourquoi ????
|
|
height: parent.width
|
|
anchors {
|
|
bottom: parent.bottom // calage du texte en bas
|
|
bottomMargin: 60 // Permet de remonter le texte qui, sinon, part d'en-dessous du rectangle (provient de la rotation mais pas clair du pourquoi...!!!)
|
|
horizontalCenter: parent.horizontalCenter
|
|
horizontalCenterOffset: 20 // Petit espace à gauche
|
|
}
|
|
wrapMode: Text.Wrap
|
|
font.pixelSize: 15
|
|
color: couleur_blanc_casse
|
|
rotation: 270 // Affichage vertical
|
|
}
|
|
|
|
// Icone de circuit On :
|
|
Image {
|
|
source: (model.consigne !== 0) ? "/images/circuit_on.png" : "/images/icone_consommation_electrique.png"
|
|
sourceSize.width: 15
|
|
opacity: (model.consigne !== 0) ? 1.0 : 0.2
|
|
anchors {
|
|
bottom: parent.bottom
|
|
bottomMargin: 5
|
|
horizontalCenter: parent.horizontalCenter
|
|
horizontalCenterOffset: -35
|
|
}
|
|
rotation: 270
|
|
}
|
|
|
|
// Valeur d'énergie (yc unité) :
|
|
Text {
|
|
id: energie_consommateur
|
|
text: model.energie.toFixed(0) + " Wh"
|
|
anchors {
|
|
top: parent.top
|
|
topMargin: 5
|
|
horizontalCenter: parent.horizontalCenter
|
|
// horizontalCenterOffset: 20
|
|
}
|
|
font.weight: Font.Bold
|
|
font.pixelSize: 15
|
|
color: couleur_blanc_casse
|
|
}
|
|
|
|
// Pourcentage :
|
|
Text {
|
|
id: pourcentage
|
|
text: model.pourcentage.toFixed(2) + " %"
|
|
anchors {
|
|
top: energie_consommateur.bottom
|
|
topMargin: 5
|
|
horizontalCenter: parent.horizontalCenter
|
|
// horizontalCenterOffset: 20
|
|
}
|
|
font.weight: Font.Thin
|
|
font.pixelSize: 12
|
|
color: couleur_blanc_casse
|
|
}
|
|
|
|
// Bargraph :
|
|
Rectangle {
|
|
id: bargraph_consommateur
|
|
width: 40
|
|
// height: (parent.height - 60) * model.pourcentage / (100 * pourcentage_maxi);
|
|
color: couleur_orange
|
|
radius: shipheart_rayon_bouton
|
|
anchors {
|
|
bottom: parent.bottom
|
|
horizontalCenter: parent.horizontalCenter
|
|
horizontalCenterOffset: 20
|
|
}
|
|
PropertyAnimation on height {
|
|
to: (bouton_consommateur.height - 60) * model.pourcentage / (100 * pourcentage_maxi); // Le '-60' permet de laisser la place à l'énergie et au pourcentage
|
|
duration: 500;
|
|
}
|
|
}
|
|
|
|
// Fond du bouton :
|
|
Rectangle { // Permet d'avoir un fond et de pouvoir régler son opacité sans changer celle des textes au-dessus
|
|
// width: parent.width
|
|
// height: parent.height
|
|
anchors.fill : parent
|
|
color: shipheart_couleur_fond_bouton
|
|
opacity: 0.5
|
|
radius: shipheart_rayon_bouton
|
|
}
|
|
|
|
//-----------------------------------
|
|
MouseArea{
|
|
anchors.fill : parent
|
|
onClicked: {
|
|
shipheart_clic();
|
|
stackView.push("circuit.qml", {"id_circuit": model.id })
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
Label {
|
|
anchors.fill: parent
|
|
horizontalAlignment: Qt.AlignHCenter
|
|
verticalAlignment: Qt.AlignVCenter
|
|
visible: grille_consommateurs.model.count === 0
|
|
text: qsTr("Aucune consommation d'énergie sur cette période...")
|
|
color: couleur_gris_moyen
|
|
font.pixelSize: 15
|
|
font.bold: true
|
|
wrapMode: Label.Wrap
|
|
}
|
|
}
|
|
|
|
|
|
|
|
//===================================
|
|
// Consommation d'énergie
|
|
Rectangle {
|
|
id: zone_affichage_energie
|
|
x: 0
|
|
y: 0
|
|
width: (parent.width * pourcentage_largeur_graphique) - espace_colonnes_secondaires;
|
|
height: parent.height
|
|
color: "transparent"
|
|
visible: (id_variable === energie_id_variable)
|
|
|
|
|
|
GridView {
|
|
id : grille_energie
|
|
width: parent.width
|
|
height: parent.height
|
|
cellWidth: parent.width / grille_energie.model.count
|
|
cellHeight: parent.height
|
|
flow: GridView.FlowTopToBottom // Items are laid out from top to bottom, and the view scrolls horizontally
|
|
snapMode: GridView.SnapToRow // L'arret de l'affichage se fera sur une rangée entière
|
|
clip: true // Nécessaire pour que les Delegates s'arretent à l'intérieur de la GridView
|
|
model: ListModel { }
|
|
|
|
delegate: Rectangle { // Permet d'occuper toute la cellule de la grille
|
|
id: barre_energie
|
|
width: grille_energie.cellWidth
|
|
height: grille_energie.cellHeight
|
|
color: "transparent"
|
|
visible: !busy_indicator.running
|
|
|
|
// Valeur d'énergie (yc unité) :
|
|
Text {
|
|
id: valeur_energie
|
|
text: (model.valeur !== 0) ? model.valeur.toFixed(model.precision) : "--"
|
|
anchors {
|
|
top: parent.top
|
|
horizontalCenter: parent.horizontalCenter
|
|
}
|
|
font.weight: (model.hauteur === 1) ? Font.Bold : Font.Normal
|
|
font.pixelSize: 13
|
|
color: (model.hauteur === 1) ?
|
|
(!flag_periode_en_cours ? couleur_orange : couleur_jaune_post_it) :
|
|
((model.valeur !== 0) ? couleur_blanc_casse : couleur_gris_moyen)
|
|
}
|
|
Text {
|
|
id: unite_energie
|
|
text: model.unite
|
|
anchors {
|
|
top: valeur_energie.bottom
|
|
horizontalCenter: parent.horizontalCenter
|
|
}
|
|
font.pixelSize: 10
|
|
color: (model.valeur !== 0) ? couleur_blanc_casse : couleur_gris_moyen
|
|
}
|
|
// Bargraph :
|
|
Rectangle {
|
|
width: model.largeur
|
|
color: !flag_periode_en_cours ? couleur_orange : couleur_jaune_post_it
|
|
radius: shipheart_rayon_bouton
|
|
anchors {
|
|
bottom: parent.bottom
|
|
bottomMargin: 20
|
|
horizontalCenter: parent.horizontalCenter
|
|
}
|
|
PropertyAnimation on height {
|
|
to: (barre_energie.height - 70) * model.hauteur; // Le '-60' permet de laisser la place à l'énergie et au pourcentage
|
|
duration: 500;
|
|
}
|
|
}
|
|
// Date de début principale :
|
|
Text {
|
|
text: model.debut_principal
|
|
anchors {
|
|
bottom: parent.bottom
|
|
horizontalCenter: parent.horizontalCenter
|
|
}
|
|
font.pixelSize: 12
|
|
font.weight: Font.Bold
|
|
color: couleur_blanc_casse
|
|
}
|
|
// Date de début secondaire :
|
|
Text {
|
|
text: model.debut_secondaire
|
|
anchors {
|
|
bottom: parent.bottom
|
|
horizontalCenter: parent.horizontalCenter
|
|
}
|
|
font.pixelSize: 10
|
|
color: couleur_blanc_casse
|
|
}
|
|
}
|
|
}
|
|
|
|
Label {
|
|
anchors.fill: parent
|
|
horizontalAlignment: Qt.AlignHCenter
|
|
verticalAlignment: Qt.AlignVCenter
|
|
visible: grille_energie.model.count === 0
|
|
text: qsTr("Aucune consommation d'énergie sur cette période...")
|
|
color: couleur_gris_moyen
|
|
font.pixelSize: 15
|
|
font.bold: true
|
|
wrapMode: Label.Wrap
|
|
}
|
|
}
|
|
|
|
|
|
|
|
//========================
|
|
// Graphique :
|
|
ChartView {
|
|
id: graphique
|
|
antialiasing: true
|
|
anchors {
|
|
top: parent.top;
|
|
left:parent.left;
|
|
bottom:parent.bottom;
|
|
margins: -15 // Un peu compliqué mais permet, avec la ligne suivante, ...
|
|
}
|
|
margins { right: 0; bottom: 0; left: 5; top: 0 } // ... d'annuler les marges.
|
|
width: parent.width * pourcentage_largeur_graphique;
|
|
backgroundColor: "transparent"
|
|
// animationDuration: 10000
|
|
// animationOptions: ChartView.SeriesAnimations
|
|
legend {visible: false }
|
|
visible: (id_variable !== energie_id_variable) && (id_variable !== 0)
|
|
|
|
DateTimeAxis {
|
|
id: axisX
|
|
format: (zoomx_en_cours > 0.5) ? courbe_format_principal_axe_x : courbe_format_zoom_1_axe_x
|
|
min: xMin_date
|
|
max: xMax_date
|
|
tickCount: (zoomx_en_cours > 0.5) ? courbe_nombre_principal_graduations_axe_x : courbe_nombre_zoom_1_graduations_axe_x
|
|
//labelsColor: (!flag_zoom_en_cours) ? couleur_blanc_casse : couleur_green_yellow
|
|
labelsColor: couleur_blanc_casse
|
|
labelsFont: Qt.font({pointSize: 10})
|
|
color: couleur_gris_moyen
|
|
shadesColor: courbes_couleur_shades_fond
|
|
shadesVisible: true
|
|
gridVisible: false
|
|
// gridLineColor: "green"
|
|
}
|
|
ValueAxis {
|
|
id: axisY1
|
|
min: yMin_valeur
|
|
max: yMax_valeur
|
|
tickCount: courbe_nombre_graduations_axe_y
|
|
labelFormat: "%." + precision_affichage_axe_y_courbe + "f"
|
|
// labelsColor: (!flag_zoom_en_cours) ? couleur_blanc_casse : couleur_green_yellow
|
|
labelsColor: couleur_blanc_casse
|
|
labelsFont:Qt.font({pointSize: 10})
|
|
color: couleur_gris_moyen
|
|
// shadesColor: courbes_couleur_shades_fond
|
|
shadesVisible: false
|
|
gridVisible: true
|
|
gridLineColor: couleur_gris_tres_fonce
|
|
titleText: "<font color='" + couleur_blanc_casse + "'>" + symbole_unite + "</font>"
|
|
titleFont {
|
|
pointSize: 20
|
|
bold: true
|
|
}
|
|
}
|
|
LineSeries { // Utilisé si on veut renforcer la courbe mais sans avoir tout le tour de l'area renfocée (qui se fait dans AreaSeries pour le coup)
|
|
id: courbe_principale
|
|
name: "Courbe principale"
|
|
axisX: axisX
|
|
axisY: axisY1
|
|
capStyle: Qt.RoundCap
|
|
style: Qt.SolidLine
|
|
width: courbes_epaisseur_bordure
|
|
color: shipheart_couleur_bordure_courbes
|
|
|
|
}
|
|
Text {
|
|
id: texte_courbe_vide
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
anchors.horizontalCenter: parent.horizontalCenter
|
|
text: qsTr("Aucune donnée n'est disponible pour cette période")
|
|
font.pixelSize: 20
|
|
font.weight: Font.Bold
|
|
color: "red"
|
|
visible: courbe_flag_vide ? true : false;
|
|
}
|
|
|
|
//---------------------------------
|
|
// Rectangle de sélection de zoom :
|
|
Rectangle {
|
|
id: rectangle_zoom
|
|
x: pinch_area.zoom_x
|
|
y: pinch_area.zoom_y
|
|
width: pinch_area.zoom_w
|
|
height: pinch_area.zoom_h
|
|
radius: shipheart_rayon_zoom
|
|
|
|
color: couleur_bleu_koriolan_tres_transparent
|
|
}
|
|
|
|
//---------------------------------
|
|
// Zone de pinch :
|
|
PinchArea{
|
|
id: pinch_area
|
|
|
|
// Propriété de la eone de zoom pendant toute la phase de sélection :
|
|
property real zoom_x: 0
|
|
property real zoom_y: 0
|
|
property real zoom_w: 0
|
|
property real zoom_h: 0
|
|
|
|
anchors.fill: parent // Calé sur 'graphique'.
|
|
// Attention : la zone de traçage à l'intérieur de 'graphique' est 'graphique.plotArea'.
|
|
// Toutes les coordonnées sont données dans 'graphique' mais on limite à l'usage les coordonnées intérieures à 'graphique.plotArea'.
|
|
|
|
//-----------------------------------------------
|
|
// Mis à jour en cours de déplacement du pinch :
|
|
onPinchUpdated: {
|
|
|
|
if ((pinch.point1.x !== pinch.point2.x) || // Il arrive, en fin de pinch, que les 2 points renvoyés soient les mêmes...
|
|
(pinch.point1.y !== pinch.point2.y)) // ... Il faut éviter de les traiter sous peine de ramener W et H à 0.
|
|
{
|
|
// Récupération des données en s'affranchissant de la position des 2 points :
|
|
zoom_x = Math.min(pinch.point1.x, pinch.point2.x)
|
|
zoom_y = Math.min(pinch.point1.y, pinch.point2.y)
|
|
zoom_w = Math.abs(pinch.point1.x - pinch.point2.x)
|
|
zoom_h = Math.abs(pinch.point1.y - pinch.point2.y)
|
|
|
|
// Limitation de 'x' et 'w' à la largeur de la zone de traçage :
|
|
if (zoom_x < graphique.plotArea.x) zoom_x = graphique.plotArea.x;
|
|
if (zoom_x > (graphique.plotArea.x + graphique.plotArea.width)) zoom_x = graphique.plotArea.x + graphique.plotArea.width
|
|
if ((zoom_x + zoom_w) > (graphique.plotArea.x + graphique.plotArea.width)) zoom_w = (graphique.plotArea.x + graphique.plotArea.width) - zoom_x;
|
|
|
|
// Limitation de 'y' et 'h' à la hauteur de la zone de traçage :
|
|
if (zoom_y < graphique.plotArea.y) zoom_y = graphique.plotArea.y;
|
|
if (zoom_y > (graphique.plotArea.y + graphique.plotArea.height)) zoom_y = graphique.plotArea.y + graphique.plotArea.height
|
|
if ((zoom_y + zoom_h) > (graphique.plotArea.y + graphique.plotArea.height)) zoom_h = (graphique.plotArea.y + graphique.plotArea.height) - zoom_y;
|
|
}
|
|
}
|
|
|
|
//-------------------------
|
|
// Valeur de fin du pinch :
|
|
onPinchFinished: {
|
|
// Préparation du zoom sur le rectangle sélectionné :
|
|
var r = Qt.rect(zoom_x, zoom_y, zoom_w, zoom_h);
|
|
graphique.zoomIn(r);
|
|
flag_zoom_en_cours = true;
|
|
|
|
zoomx_en_cours *= (zoom_w / graphique.plotArea.width);
|
|
|
|
// Effacement du rectangle :
|
|
zoom_x = 0
|
|
zoom_y = 0
|
|
zoom_w = 0
|
|
zoom_h = 0
|
|
}
|
|
|
|
// Déplacement de la courbe :
|
|
MouseArea{
|
|
anchors.fill: parent
|
|
drag.target: dragTarget
|
|
drag.axis: Drag.XAndYAxis
|
|
onClicked: {
|
|
shipheart_clic();
|
|
if (!flag_zoom_en_cours) // Si le zoom est en cours, il ne faut PAS lancer le raffraichissement...!
|
|
{
|
|
busy_indicator.running = true;
|
|
timer_raffraichissement_graphiques.interval = periode_init_raffraichissement_graphiques; // Cela permet de lancer le "busy_indicator' avant de faire la mise à jour de la courbe
|
|
}
|
|
}
|
|
onPressAndHold: {
|
|
raz_zoom();
|
|
}
|
|
}
|
|
Item {
|
|
id: dragTarget
|
|
property real oldX : x // Permet de mémoriser la dernière position d'arrivée
|
|
property real oldY : y
|
|
|
|
property real cumulX : 0 // Permet de revenir à un déclage nul lorsque nécessaire
|
|
property real cumulY : 0
|
|
|
|
onXChanged: {
|
|
graphique.scrollLeft( x - oldX );
|
|
cumulX -= (x - oldX); // Le signe '-' permettra ensuite de revenir à le position de départ
|
|
oldX = x;
|
|
}
|
|
|
|
onYChanged: {
|
|
graphique.scrollUp( y - oldY );
|
|
cumulY -= (y - oldY); // Le signe '-' permettra ensuite de revenir à le position de départ
|
|
oldY = y;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//========================
|
|
// Busy Indicator :
|
|
Shipheart_busy_indicator {
|
|
id: busy_indicator
|
|
}
|
|
|
|
//========================
|
|
// Boutons de pếriode :
|
|
ColumnLayout {
|
|
spacing: 10
|
|
anchors {
|
|
top: parent.top;
|
|
right:parent.right;
|
|
rightMargin: 5;
|
|
bottom:parent.bottom;
|
|
}
|
|
width: (parent.width * (1 - pourcentage_largeur_graphique)) - anchors.rightMargin;
|
|
|
|
ListModel {
|
|
id: modele_periodes_graphique
|
|
}
|
|
|
|
Repeater {
|
|
model: modele_periodes_graphique
|
|
|
|
Rectangle {
|
|
color: "transparent"
|
|
Layout.preferredWidth: parent.width;
|
|
Layout.preferredHeight: (parent.height / nb_boutons_periode) - parent.spacing;
|
|
|
|
Text {
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
anchors.horizontalCenter: parent.horizontalCenter
|
|
text: model.libelle
|
|
font.pixelSize: 18
|
|
font.weight: (model.index === periode_analyse) ? Font.Bold : Font.Normal
|
|
color: couleur_blanc_casse
|
|
}
|
|
MouseArea { // Zone d'interaction. On est obligé de la mettre dans la grille pour que Rectangle et MouseArea soient frères ou parent/enfant ET qu'on n'occupe qu'un espace dans la grille.
|
|
anchors.fill: parent
|
|
onClicked: {
|
|
shipheart_clic();
|
|
raz_zoom(); // Au-delà de la fonctionnalité, c'est obligatoire sinon, le raffraichissement des la courbe est bloqué au niveau du timer
|
|
periode_analyse = model.index;
|
|
busy_indicator.running = true;
|
|
mise_a_jour_valeur_energie(); // Permet que la mise à jour du widget soit quasi instantanée après l'appui.
|
|
timer_raffraichissement_graphiques.interval = periode_init_raffraichissement_graphiques; // Cela permet de lancer le "busy_indicator' avant de faire la mise à jour de la courbe
|
|
}
|
|
}
|
|
// Fond du bouton :
|
|
Rectangle { // Permet d'avoir un fond et de pouvoir régler son opacité sans changer celle des textes au-dessus
|
|
width: parent.width
|
|
height: parent.height
|
|
color: (model.index === periode_analyse) ? (flag_zoom_en_cours ? couleur_green_yellow_transparent : shipheart_couleur_fond_bouton_actif) : shipheart_couleur_fond_bouton;
|
|
opacity: 0.5
|
|
radius: shipheart_rayon_bouton
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//-------------------------------------------------------
|
|
// Message d'erreur si batterie Absente
|
|
// (le module sur lequel la batterie est configurée n'est pas physiquement présent)
|
|
//-------------------------------------------------------
|
|
Rectangle {
|
|
id: zone_message_absence
|
|
x: 0
|
|
y: shipheart_hauteur_bandeau_haut + espace_lignes_principales
|
|
width: shipheart_largeur_page
|
|
height: shipheart_hauteur_page - shipheart_hauteur_bandeau_haut - shipheart_hauteur_bandeau_bas - espace_lignes_principales
|
|
color: "transparent"
|
|
visible: (etat_systeme !== 0)
|
|
|
|
Rectangle { // Rectangle entouré de rouge
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
anchors.verticalCenterOffset: -50
|
|
anchors.horizontalCenter: parent.horizontalCenter
|
|
width: parent.width
|
|
height: parent.height / 2
|
|
radius: shipheart_rayon_bouton
|
|
border.width: 3
|
|
border.color: couleur_rouge
|
|
color: couleur_rouge_transparent//"transparent"
|
|
|
|
Image { // Pictogramme DANGER
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
anchors.verticalCenterOffset: -100
|
|
anchors.horizontalCenter: parent.horizontalCenter
|
|
source: "/images/ak14.png"
|
|
sourceSize.height: 70;
|
|
}
|
|
|
|
Text { // Texte d'info
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
anchors.verticalCenterOffset: 50
|
|
anchors.horizontalCenter: parent.horizontalCenter
|
|
width: parent.width
|
|
height : parent.height / 2
|
|
horizontalAlignment: Qt.AlignCenter
|
|
verticalAlignment: Qt.AlignVCenter
|
|
padding: 50
|
|
text: qsTr("Le module assurant la supervision de cette batterie n'est pas accessible !<br /><br />Si des ajouts/suppression/déplacements de modules ont eu lieu récemment, mettez à jour la configuration correspondant dans le menu MODULES de PARAMETRES AVANCES.<br /><br />Dans le cas où aucune modification n'a été effectuée récemment, vérifier les connexions de tous les modules.")
|
|
color: couleur_blanc_casse
|
|
font.pixelSize: 18
|
|
wrapMode: Label.Wrap
|
|
}
|
|
}
|
|
}
|
|
|
|
//-------------------------------------------------------
|
|
// Bandeau bas :
|
|
//-------------------------------------------------------
|
|
Row {
|
|
x: 0
|
|
y: shipheart_hauteur_page - shipheart_hauteur_bandeau_bas;
|
|
width: shipheart_largeur_page;
|
|
height: shipheart_hauteur_bandeau_bas;
|
|
spacing: 5
|
|
padding: 5
|
|
|
|
|
|
//.................................................
|
|
// Bouton retour
|
|
//.................................................
|
|
Rectangle {
|
|
id: bandeau_bas_zone_retour
|
|
width: parent.width - (2 * parent.padding)
|
|
height: shipheart_hauteur_bandeau_bas - parent.topPadding - parent.bottomPadding;
|
|
radius: 5
|
|
color: shipheart_couleur_fond_bouton
|
|
|
|
Image {
|
|
source: "/images/icone_retour.png"
|
|
sourceSize.height: 30
|
|
anchors {
|
|
verticalCenter: parent.verticalCenter
|
|
horizontalCenter: parent.horizontalCenter
|
|
}
|
|
}
|
|
|
|
MouseArea { // Zone d'interaction. On est obligé de la mettre dans la grille pour que Rectangle et MouseArea soient frères ou parent/enfant ET qu'on n'occupe qu'un espace dans la grille.
|
|
id: mouse_area_bandeau_bas_zone_retour
|
|
anchors.fill: parent
|
|
onClicked: {
|
|
shipheart_clic();
|
|
stackView.pop()
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//-------------------------------------------------------
|
|
// Boite d'affichage de la liste des alarmes :
|
|
//-------------------------------------------------------
|
|
Shipheart_boite_dialogue_liste_alarmes {
|
|
id: boite_dialogue_liste_alarmes
|
|
|
|
titre: nom
|
|
type_source: shipheart_source_mesure_batterie
|
|
id_source: id_batterie
|
|
}
|
|
|
|
//===========================================================
|
|
// Economiseur : Doit être placé en fin de page pour fonctionner
|
|
//===========================================================
|
|
MouseArea {
|
|
anchors.fill: parent
|
|
preventStealing: true // Permet que cela fonctionne même si un élément flickable (notamment) soit derrière et "vole" le click
|
|
enabled: flag_economiseur_actif
|
|
onClicked: {
|
|
mise_en_veille(0);
|
|
}
|
|
}
|
|
|
|
|
|
//=============================================================================================================
|
|
// Init de la page
|
|
//=============================================================================================================
|
|
function init_page()
|
|
{
|
|
var req, res;
|
|
var nb;
|
|
var i;
|
|
|
|
id_variable = 0; // Par défaut, on affiche le comparatif d'énergie
|
|
|
|
//====================================================================
|
|
// Init du nom des périodes du graphique :
|
|
modele_periodes_graphique.append({ "libelle": Fonctions_date_heure.nom_periode_analyse(0), "actif": true });
|
|
modele_periodes_graphique.append({ "libelle": Fonctions_date_heure.nom_periode_analyse(1), "actif": true });
|
|
modele_periodes_graphique.append({ "libelle": Fonctions_date_heure.nom_periode_analyse(2), "actif": true });
|
|
modele_periodes_graphique.append({ "libelle": Fonctions_date_heure.nom_periode_analyse(3), "actif": true });
|
|
|
|
//====================================================================
|
|
// Lecture des infos générales concernant la batterie :
|
|
req = 'SELECT Nom, Type_entrees FROM TB_Batteries WHERE Cle_primaire="' + id_batterie + '" ';
|
|
res = bdd.requete_unique(req);
|
|
nom = res[0];
|
|
type_entrees = Number(res[1]);
|
|
|
|
//====================================================================
|
|
// Recherche de ID_Variable_puissance_circuit :
|
|
req = ' SELECT Cle_primaire FROM TB_Variables WHERE Code="' + shipheart_circuit_puissance + '" ';
|
|
res = bdd.requete_unique(req);
|
|
id_variable_puissance_circuit = Number(res[0]);
|
|
//console.log("ID_Variable_puissance_circuit = " + id_variable_puissance_circuit + " ");
|
|
|
|
//====================================================================
|
|
// Recherche de ID_Variable_energie_circuit :
|
|
req = ' SELECT Cle_primaire FROM TB_Variables WHERE Code="' + shipheart_circuit_energie + '" ';
|
|
res = bdd.requete_unique(req);
|
|
id_variable_energie_circuit = Number(res[0]);
|
|
//console.log("ID_Variable_energie_circuit = " + id_variable_energie_circuit + " ");
|
|
|
|
//====================================================================
|
|
// Recherche des ID_Variable batterie :
|
|
req = ' SELECT Code, Cle_primaire, Unite FROM TB_Variables WHERE Code LIKE "%Batt_%" ';
|
|
nb = bdd.requete(req);
|
|
for (i = 0; i < nb; i ++)
|
|
{
|
|
res = bdd.lecture_ligne();
|
|
if (!res.isEmpty)
|
|
{
|
|
switch(res[0])
|
|
{
|
|
case shipheart_batterie_temperature:
|
|
temperature_id_variable = Number(res[1]);
|
|
temperature_unite_variable = res[2];
|
|
break;
|
|
case shipheart_batterie_tension:
|
|
tension_id_variable = Number(res[1]);
|
|
tension_unite_variable = res[2];
|
|
break;
|
|
case shipheart_batterie_courant_differentiel:
|
|
courant_differentiel_id_variable = Number(res[1]);
|
|
courant_differentiel_unite_variable = res[2];
|
|
break;
|
|
case shipheart_batterie_courant_charge:
|
|
courant_charge_id_variable = Number(res[1]);
|
|
courant_charge_unite_variable = res[2];
|
|
break;
|
|
case shipheart_batterie_courant_decharge:
|
|
courant_decharge_id_variable = Number(res[1]);
|
|
courant_decharge_unite_variable = res[2];
|
|
break;
|
|
case shipheart_batterie_soh:
|
|
soh_id_variable = Number(res[1]);
|
|
soh_unite_variable = res[2];
|
|
break;
|
|
case shipheart_batterie_soc:
|
|
soc_id_variable = Number(res[1]);
|
|
soc_unite_variable = res[2];
|
|
break;
|
|
case shipheart_batterie_autonomie:
|
|
autonomie_id_variable = Number(res[1]);
|
|
autonomie_unite_variable = res[2];
|
|
break;
|
|
case shipheart_batterie_puissance:
|
|
puissance_id_variable = Number(res[1]);
|
|
puissance_unite_variable = res[2];
|
|
break;
|
|
case shipheart_batterie_energie:
|
|
energie_id_variable = Number(res[1]);
|
|
energie_unite_variable = res[2];
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
else break; // Ne devrait pas arriver....
|
|
}
|
|
|
|
//====================================================================
|
|
// Mise à jour intiale :
|
|
mise_a_jour_valeurs_instantanées();
|
|
mise_a_jour_valeur_energie();
|
|
|
|
if (id_variable === 0) comparatif_conso_energie();
|
|
else mise_a_jour_courbes();
|
|
|
|
|
|
|
|
flag_init = false; // Libération du flag
|
|
}
|
|
|
|
|
|
//=============================================================================================================
|
|
// Mise à jour de la valeur instantanée
|
|
//=============================================================================================================
|
|
function mise_a_jour_valeurs_instantanées()
|
|
{
|
|
var req, res;
|
|
var nb;
|
|
var i;
|
|
var valeur;
|
|
|
|
// Requete :
|
|
req = 'SELECT ';
|
|
req += ' Etat_systeme, ';
|
|
req += ' Nombre_cycles, ';
|
|
req += ' Nombre_decharges_completes, ';
|
|
req += ' Temperature, ';
|
|
req += ' Tension, ';
|
|
req += ' Courant_differentiel, ';
|
|
req += ' Courant_charge, ';
|
|
req += ' Courant_decharge, ';
|
|
req += ' Tension * Courant_decharge, '; // Puissance consommée
|
|
req += ' Soh, ';
|
|
req += ' Soc, ';
|
|
req += ' Autonomie ';
|
|
req += ' FROM TB_Batteries WHERE Cle_primaire="' + id_batterie + '" ';
|
|
//console.log("Requête = [ " + req + "; ]");
|
|
res = bdd.requete_unique(req);
|
|
|
|
// Récupération des données :
|
|
i = 0;
|
|
etat_systeme = Number(res[i++]);
|
|
nombre_cycles = Number(res[i++]).toFixed(0);
|
|
nombre_decharges_completes = Number(res[i++]).toFixed(0);
|
|
temperature = Number(res[i++]).toFixed(1);
|
|
tension = Number(res[i++]).toFixed(2);
|
|
courant_differentiel = Number(res[i++]).toFixed(1);
|
|
courant_charge = Number(res[i++]).toFixed(1);
|
|
courant_decharge = Number(res[i++]).toFixed(1);
|
|
|
|
valeur = Number(res[i++]);
|
|
if (Math.abs(valeur) > 99.90) puissance = valeur.toFixed(0); // Le seuil de 99.90 permet que 99.91 qui est affiché 100 soit affiché '100%' et non '100.0%' (valable pour 99.92 et jusqu'à 99.99....)
|
|
else puissance = valeur.toFixed(1);
|
|
|
|
valeur = Number(res[i++]);
|
|
if (valeur > 99.90) soh = valeur.toFixed(0); // Le seuil de 99.90 permet que 99.91 qui est affiché 100 soit affiché '100%' et non '100.0%' (valable pour 99.92 et jusqu'à 99.99....)
|
|
else soh = valeur.toFixed(1);
|
|
|
|
valeur = Number(res[i++]);
|
|
if (valeur > 99.90) soc = valeur.toFixed(0); // Le seuil de 99.90 permet que 99.91 qui est affiché 100 soit affiché '100%' et non '100.0%' (valable pour 99.92 et jusqu'à 99.99....)
|
|
else soc = valeur.toFixed(1);
|
|
|
|
autonomie = Number(res[i++]).toFixed(1);
|
|
|
|
|
|
|
|
// Recherche des éventuelles alarmes provenant des évènements :
|
|
var temperature_etat = -1;
|
|
var tension_etat = -1;
|
|
var courant_differentiel_etat = -1;
|
|
var courant_charge_etat = -1;
|
|
var courant_decharge_etat = -1;
|
|
var soh_etat = -1;
|
|
var soc_etat = -1;
|
|
var autonomie_etat = -1;
|
|
var puissance_etat = -1;
|
|
var energie_etat = -1;
|
|
|
|
req = 'SELECT ID_Variable, Etat_test ';
|
|
req += ' FROM TB_Events ';
|
|
req += ' WHERE Validite=1 ';
|
|
req += ' AND Alarme_source>0 ';
|
|
req += ' AND Type_source=' + shipheart_source_mesure_batterie + ' ';
|
|
req += ' AND ID_Source=' + id_batterie + ' ';
|
|
//console.log("Requête = [ " + req + "; ]");
|
|
nb = bdd.requete(req);
|
|
for (i = 0; i < nb; i ++)
|
|
{
|
|
res = bdd.lecture_ligne();
|
|
if (!res.isEmpty)
|
|
{
|
|
switch(Number(res[0]))
|
|
{
|
|
case temperature_id_variable:
|
|
if (temperature_etat < Number(res[1])) temperature_etat = Number(res[1]);
|
|
break;
|
|
case tension_id_variable:
|
|
if (tension_etat < Number(res[1])) tension_etat = Number(res[1]);
|
|
break;
|
|
case courant_differentiel_id_variable:
|
|
if (courant_differentiel_etat < Number(res[1])) courant_differentiel_etat = Number(res[1]);
|
|
break;
|
|
case courant_charge_id_variable:
|
|
if (courant_charge_etat < Number(res[1])) courant_charge_etat = Number(res[1]);
|
|
break;
|
|
case courant_decharge_id_variable:
|
|
if (courant_decharge_etat < Number(res[1])) courant_decharge_etat = Number(res[1]);
|
|
break;
|
|
case soh_id_variable:
|
|
if (soh_etat < Number(res[1])) soh_etat = Number(res[1]);
|
|
break;
|
|
case soc_id_variable:
|
|
if (soc_etat < Number(res[1])) soc_etat = Number(res[1]);
|
|
break;
|
|
case autonomie_id_variable:
|
|
if (autonomie_etat < Number(res[1])) autonomie_etat = Number(res[1]);
|
|
break;
|
|
case puissance_id_variable:
|
|
if (puissance_etat < Number(res[1])) puissance_etat = Number(res[1]);
|
|
break;
|
|
case energie_id_variable:
|
|
if (energie_etat < Number(res[1])) energie_etat = Number(res[1]);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// Mise à jour du modèle de la grille principale :
|
|
grille_donnees_principale.model.clear();
|
|
|
|
grille_donnees_principale.model.append({
|
|
"titre": Fonctions.nom_variable("Batt_soc"),
|
|
"id_var":soc_id_variable,
|
|
"valeur": soc,
|
|
"unite": soc_unite_variable,
|
|
"etat": soc_etat,
|
|
"type_couleur": 0,
|
|
"flag_affichage_complementaire": true })
|
|
grille_donnees_principale.model.append({
|
|
"titre": Fonctions.nom_variable("Batt_tension"),
|
|
"id_var":tension_id_variable,
|
|
"valeur": tension,
|
|
"unite": tension_unite_variable,
|
|
"etat": tension_etat,
|
|
"type_couleur": 0,
|
|
"flag_affichage_complementaire": false })
|
|
|
|
if (type_entrees === shipheart_batteries_type_entrees_ibs)
|
|
{
|
|
grille_donnees_principale.model.append({
|
|
"titre": Fonctions.nom_variable("Batt_courant_differentiel"),
|
|
"id_var":courant_differentiel_id_variable,
|
|
"valeur": courant_differentiel,
|
|
"unite": courant_differentiel_unite_variable,
|
|
"etat": courant_differentiel_etat,
|
|
"type_couleur": 1,
|
|
"flag_affichage_complementaire": false })
|
|
grille_donnees_principale.model.append({
|
|
"titre": Fonctions.nom_variable("Batt_puissance"),
|
|
"id_var":puissance_id_variable,
|
|
"valeur": puissance,
|
|
"unite": puissance_unite_variable,
|
|
"etat": puissance_etat,
|
|
"type_couleur": 0,
|
|
"flag_affichage_complementaire": false })
|
|
|
|
grille_donnees_principale.model.append({
|
|
"titre": Fonctions.nom_variable("Batt_courant_charge"),
|
|
"id_var":courant_charge_id_variable,
|
|
"valeur": courant_charge,
|
|
"unite": courant_charge_unite_variable,
|
|
"etat": courant_charge_etat,
|
|
"type_couleur": 0,
|
|
"flag_affichage_complementaire": false })
|
|
grille_donnees_principale.model.append({
|
|
"titre": Fonctions.nom_variable("Batt_courant_decharge"),
|
|
"id_var":courant_decharge_id_variable,
|
|
"valeur": courant_decharge,
|
|
"unite": courant_decharge_unite_variable,
|
|
"etat": courant_decharge_etat,
|
|
"type_couleur": 0,
|
|
"flag_affichage_complementaire": false })
|
|
|
|
grille_donnees_principale.model.append({
|
|
"titre": Fonctions.nom_variable("Batt_temperature"),
|
|
"id_var":temperature_id_variable,
|
|
"valeur": temperature,
|
|
"unite": temperature_unite_variable,
|
|
"etat": temperature_etat,
|
|
"type_couleur": 0,
|
|
"flag_affichage_complementaire": false })
|
|
grille_donnees_principale.model.append({
|
|
"titre": Fonctions.nom_variable("Batt_energie"),
|
|
"id_var":energie_id_variable,
|
|
"valeur": energie,
|
|
"unite": energie_unite_variable,
|
|
"etat": energie_etat,
|
|
"type_couleur": 0,
|
|
"flag_affichage_complementaire": false })
|
|
}
|
|
}
|
|
|
|
|
|
//=============================================================================================================
|
|
// Mise à jour de la valeur globale de l'énergie consommée sur la période en cours
|
|
//=============================================================================================================
|
|
function mise_a_jour_valeur_energie()
|
|
{
|
|
var req, res;
|
|
var i;
|
|
var date_debut, date_fin;
|
|
var type_mesure;
|
|
var total;
|
|
|
|
|
|
console.time('calcul_valeur_energie')
|
|
|
|
// Dates et durée :
|
|
date_fin = new Date(); // En UTC
|
|
date_debut = Fonctions_date_heure.date_add_secondes(date_fin, -(Fonctions_date_heure.duree_periode_analyse(periode_analyse))); // En UTC
|
|
//console.log("Date de début initiale UTC = " + date_debut + " et date de fin UTC = " + date_fin)
|
|
|
|
// Ajustements à l'intervalle fini d'analyse :
|
|
var debut_fin = Fonctions_date_heure.ajustement_periode_analyse(periode_analyse, date_debut, date_fin, shipheart_fuseau_horaire, shipheart_heure_ete);
|
|
switch(periode_analyse)
|
|
{
|
|
default:
|
|
case 0: // 1 heure
|
|
type_mesure=3;
|
|
break;
|
|
case 1: // 24 heures
|
|
case 2: // 7 jours
|
|
case 3: // 30 jours
|
|
type_mesure=2;
|
|
break;
|
|
}
|
|
|
|
// Requete :
|
|
req = ' SELECT';
|
|
req += ' SUM(Valeur)';
|
|
req += ' FROM TB_Mesures_circuits as tb_mes ';
|
|
req += ' WHERE tb_mes.Type=' + type_mesure + ' ';
|
|
req += ' AND tb_mes.ID_Variable=' + id_variable_energie_circuit + ' ';
|
|
req += ' AND tb_mes.ID IN ';
|
|
req += ' (SELECT Cle_primaire FROM TB_Circuits WHERE ';
|
|
req += ' Adresse_module IN ';
|
|
req += ' (SELECT Adresse_k_bus FROM TB_Modules_config WHERE ';
|
|
req += ' ID_Batterie=' + id_batterie + ' ';
|
|
req += ' AND (Type=' + shipheart_type_module_shipbase + ' OR Type=' + shipheart_type_module_shippower812 + ' OR Type=' + shipheart_type_module_shippower8DC + ') ';
|
|
req += ' ) ';
|
|
req += ' ) ';
|
|
req += ' AND tb_mes.Date>="' + Fonctions_date_heure.chaine_date_heure_bdd(debut_fin.debut) + '" ';
|
|
req += ' AND tb_mes.Date<="' + Fonctions_date_heure.chaine_date_heure_bdd(debut_fin.fin) + '" ';
|
|
req += ' ORDER BY tb_mes.Date ';
|
|
//console.log("Requête = [" + req + "]");
|
|
res = bdd.requete_unique(req);
|
|
total = Number(res[0]);
|
|
|
|
// Mise en forme du résultat :
|
|
energie_unite_variable = "Wh"; // Par défaut
|
|
if (total < 100) energie = total.toFixed(1);
|
|
else if (total < 1000) energie = total.toFixed(0);
|
|
else
|
|
{
|
|
total /= 1000;
|
|
energie_unite_variable = "kWh"
|
|
if (total < 10) energie = total.toFixed(1);
|
|
else energie = total.toFixed(0);
|
|
}
|
|
|
|
// Mise à jour immédiate du modele (sinon, il faudra attendre mise_a_jour_valeurs_instantanées()) :
|
|
for (i = 0; i < grille_donnees_principale.model.count; i ++)
|
|
{
|
|
if (grille_donnees_principale.model.get(i).id_var === energie_id_variable)
|
|
{
|
|
grille_donnees_principale.model.setProperty(i, "valeur", energie);
|
|
grille_donnees_principale.model.setProperty(i, "unite", energie_unite_variable);
|
|
}
|
|
}
|
|
|
|
console.timeEnd('calcul_valeur_energie')
|
|
}
|
|
|
|
//=============================================================================================================
|
|
// Consommation d'énergie de la batterie entre dates
|
|
//=============================================================================================================
|
|
function conso_energie_intervalle(date_debut, date_fin)
|
|
{
|
|
var req, res;
|
|
var nb;
|
|
var i;
|
|
var id_circuits = [];
|
|
var chaine_date_debut = Fonctions_date_heure.chaine_date_heure_bdd(date_debut);
|
|
var chaine_date_fin = Fonctions_date_heure.chaine_date_heure_bdd(date_fin);
|
|
var energie;
|
|
|
|
|
|
//console.time('maj_energie');
|
|
|
|
energie = 0;
|
|
|
|
// Ancienne requête très longue à cause de la sous-requete [SELECT ID, max(Date) AS date_prev FROM TB_Mesures_circuits WHERE ID_Variable=24 AND Date<="2024-02-05 05:00:00" GROUP BY ID ; ]
|
|
// qui prend près de 5 secondes sur une V2 chargée normalement. La solution créée en 02/2024 utilise le fait que la table des mesures est indexée sur Date et qu'une mesure d'energie
|
|
// sur un circuit est quasi instantanée. On est donc passé à lister les circuits concerné (1 requête) puis l'énegrie de chaque circuit sur l'intervalle (N requete) avec la somme de tous les résultats.
|
|
// La fonction qui prenait 5s donne un résultat strictement identique en 92ms...
|
|
// ATTENTION : cette ancienne requête n'a PAS été mise à jour lors de la suppression du champ 'TB_Circuits.Type_circuit'.
|
|
|
|
/* req = 'SELECT (SUM(Duree * Puissance) / 3600) AS Energie '; // 3600 secondes. NB : la division a intérêt à être faite sur la somme (et non la somme des divisions...)
|
|
req += ' FROM ( ';
|
|
req += ' SELECT ';
|
|
req += ' IFNULL'; // Return the specified value IF the expression is NULL, otherwise return the expression ie on convertit les valeurs NULL en 0.
|
|
req += ' (mesures.Valeur, 0) as Puissance, '; // Contrairement aux calculs usuels d'intégrale, la forme de stockage des mesures ne nécessite de connaitre que la valeur du point de début d'intervalle.
|
|
req += ' ( UNIX_TIMESTAMP(';
|
|
req += ' COALESCE('; // Returns the first non-null value in a list
|
|
req += ' LEAST(LEAD(mesures.Date) OVER (PARTITION BY mesures.ID ORDER BY mesures.Date), "' + chaine_date_fin + '"),'; // "Plus petit" entre [date de la prochaine mesure] et [date de fin] ou NULL si l'un au moins est NULL (cas du dernier point de l'intervalle avec aucun point au-delà).
|
|
req += ' LEAD(mesures.Date) OVER (PARTITION BY mesures.ID ORDER BY mesures.Date),'; // Date de la prochaine mesure (qui peut être NULL si on est sur le dernier point et qu'aucun point ultérieur n'existe)
|
|
req += ' "' + chaine_date_fin + '")) '; // Date de fin qui est forcément non NULL...
|
|
req += ' - ';
|
|
req += ' UNIX_TIMESTAMP(';
|
|
req += ' GREATEST(mesures.Date, "' + chaine_date_debut + '"))'; // "Plus grand" entre [date de mesure] et [date de début]. NB : contrairement à la fin de la période, aucune de ces deux valeurs ne peut être nulle...
|
|
req += ' ) as Duree'; // en secondes, entre le point en cours (ou la date de début si on est sur le point "ajouté") et le point suivant (ou la date de fin si on est sur le dernier point de l'intervalle)
|
|
req += ' FROM (';
|
|
req += ' (SELECT ID, Date, Valeur FROM TB_Mesures_circuits WHERE ID_Variable=' + id_variable_puissance_circuit + ' AND Date>="' + chaine_date_debut + '" AND Date<="' + chaine_date_fin + '") '; // Tous les points dans l'intervalle (y compris ceux avec la valeur NULL)
|
|
req += ' UNION ';
|
|
req += ' (SELECT tb_mes.ID, tb_mes.Date, tb_mes.Valeur'; // On ne peut malheureusement pas récupérer directement ces valeurs dans le SELECT suivant car le MAX l'en empêche...
|
|
req += ' FROM ';
|
|
req += ' TB_Mesures_circuits AS tb_mes, ';
|
|
req += ' (SELECT ID, max(Date) AS date_prev FROM TB_Mesures_circuits WHERE ID_Variable=' + id_variable_puissance_circuit + ' AND Date<="' + chaine_date_debut + '" GROUP BY ID) AS tb_prev '; // Renvoit pour chaque ID (via GROUP BY) la date la plus récente < date de début et l'ID correspondant. NB : la récup d'ID est possible à l'issue du GROUP BY mais pas les autres champs (qui varient)
|
|
req += ' WHERE tb_mes.ID=tb_prev.ID AND tb_mes.ID_Variable=' + id_variable_puissance_circuit + ' AND tb_mes.Date=tb_prev.date_prev)'; // Filtre pour récupérer un seul enregistrement par ID qui corresponde à la date la plus récente trouvée pour cet ID. Pour chaque ID, on a donc un point "ajouté" qui est le dernier point juste avant la période en cours (ou aucun point "ajouté" s'il n'y en pas avant pour cet ID !)
|
|
req += ' ) AS mesures ';
|
|
req += ' WHERE '; // On filtre les enregistrements concernant un circuit :
|
|
req += ' mesures.ID IN (SELECT Cle_primaire FROM TB_Circuits WHERE (Type_circuit=0 OR Type_circuit=1 OR Type_circuit=4) '; // ... affectés à un ShipBase, à un SP812 ou un SP8DC
|
|
req += ' AND ';
|
|
req += ' Adresse_module IN (SELECT Adresse_k_bus FROM TB_Modules_config WHERE ID_Batterie="' + id_batterie + '"))'; // ... lui-même (le module) affecté à la batterie en cours. NB : on ne filtre pas sur les modules "présents" car on considère que si le module a été retiré, on afffiche quand même l'historique
|
|
req += ' ) AS valeurs ';
|
|
//console.log("Requête = [" + req + "]");
|
|
res = bdd.requete_unique(req);
|
|
energie = Number(res[0]); */
|
|
|
|
req = 'SELECT Cle_primaire FROM TB_Circuits WHERE ';
|
|
req += ' Adresse_module IN ';
|
|
req += ' (SELECT Adresse_k_bus FROM TB_Modules_config WHERE ';
|
|
req += ' ID_Batterie=' + id_batterie + ' ';
|
|
req += ' AND (Type=' + shipheart_type_module_shipbase + ' OR Type=' + shipheart_type_module_shippower812 + ' OR Type=' + shipheart_type_module_shippower8DC + ') )';
|
|
//console.log("Requête = [" + req + "]");
|
|
nb = bdd.requete(req);
|
|
|
|
for (i = 0; i < nb; i ++)
|
|
{
|
|
res = bdd.lecture_ligne();
|
|
if (!res.isEmpty)
|
|
{
|
|
id_circuits.push(Number(res[0]));
|
|
}
|
|
else break; // Ne devrait pas arriver....
|
|
}
|
|
|
|
for (let i = 0; i < id_circuits.length; i++)
|
|
{
|
|
//console.log(" ID # " + i + " = " + id_circuits[i]);
|
|
|
|
// NB : cette requete est la copie exacte de celle utilisée dans 'conso_energie_intervalle()' de 'circuit.qml'. Seul le nom des variables 'id_variable_puissance_circuit' et 'id_circuit' sont adaptées à cet environnement.
|
|
req = 'SELECT (SUM(Duree * Puissance) / 3600) AS Energie '; // 3600 secondes. NB : la division a intérêt à être faite sur la somme (et non la somme des divisions...)
|
|
req += ' FROM ( ';
|
|
req += ' SELECT ';
|
|
req += ' IFNULL'; // Return the specified value IF the expression is NULL, otherwise return the expression ie on convertit les valeurs NULL en 0.
|
|
req += ' (mesures.Valeur, 0) as Puissance, '; // Contrairement aux calculs usuels d'intégrale, la forme de stockage des mesures ne nécessite de connaitre que la valeur du point de début d'intervalle.
|
|
req += ' ( UNIX_TIMESTAMP(';
|
|
req += ' COALESCE('; // Returns the first non-null value in a list
|
|
req += ' LEAST(LEAD(mesures.Date) OVER (ORDER BY mesures.Date), "' + chaine_date_fin + '"),'; // "Plus petit" entre [date de la prochaine mesure] et [date de fin] ou NULL si l'un au moins est NULL (cas du dernier point de l'intervalle avec aucun point au-delà).
|
|
req += ' LEAD(mesures.Date) OVER (ORDER BY mesures.Date),'; // Date de la prochaine mesure (qui peut être NULL si on est sur le dernier point et qu'aucun point ultérieur n'existe)
|
|
req += ' "' + chaine_date_fin + '")) '; // Date de fin qui est forcément non NULL...
|
|
req += ' - ';
|
|
req += ' UNIX_TIMESTAMP(';
|
|
req += ' GREATEST(mesures.Date, "' + chaine_date_debut + '"))'; // "Plus grand" entre [date de mesure] et [date de début]. NB : contrairement à la fin de la période, aucune de ces deux valeurs ne peut être nulle...
|
|
req += ' ) as Duree'; // en secondes, entre le point en cours (ou la date de début si on est sur le point "ajouté") et le point suivant (ou la date de fin si on est sur le dernier point de l'intervalle)
|
|
req += ' FROM (';
|
|
req += ' (SELECT Date, Valeur FROM TB_Mesures_circuits WHERE ID_Variable=' + id_variable_puissance_circuit + ' AND Date>="' + chaine_date_debut + '" AND Date<="' + chaine_date_fin + '" AND ID=' + id_circuits[i] + ') '; // Tous les points dans l'intervalle (y compris ceux avec la valeur NULL)
|
|
req += ' UNION ';
|
|
req += ' (SELECT Date, Valeur FROM TB_Mesures_circuits WHERE ID_Variable=' + id_variable_puissance_circuit + ' AND Date<="' + chaine_date_debut + '" AND ID=' + id_circuits[i] + ' ORDER BY Date DESC LIMIT 1) ';
|
|
req += ' ) AS mesures ';
|
|
req += ' ) AS valeurs ';
|
|
//console.log("Requête = [" + req + "]");
|
|
res = bdd.requete_unique(req);
|
|
|
|
energie += Number(res[0]);
|
|
}
|
|
|
|
//console.log(" Energie = " + energie);
|
|
|
|
|
|
//console.timeEnd('maj_energie')
|
|
|
|
return(energie);
|
|
}
|
|
|
|
|
|
//=============================================================================================================
|
|
// Mise à jour du comparatif de consommation d'énergie par circuits
|
|
//=============================================================================================================
|
|
function comparatif_conso_energie()
|
|
{
|
|
var req, res;
|
|
var nb;
|
|
var i;
|
|
var date_debut, date_fin;
|
|
var type_mesure;
|
|
var total;
|
|
|
|
|
|
//console.time('maj_energie')
|
|
|
|
// Dates et durée :
|
|
date_fin = new Date(); // En UTC
|
|
date_debut = Fonctions_date_heure.date_add_secondes(date_fin, -(Fonctions_date_heure.duree_periode_analyse(periode_analyse))); // En UTC
|
|
//console.log("Date de début initiale UTC = " + date_debut + " et date de fin UTC = " + date_fin)
|
|
|
|
// Ajustements à l'intervalle fini d'analyse :
|
|
var debut_fin = Fonctions_date_heure.ajustement_periode_analyse(periode_analyse, date_debut, date_fin, shipheart_fuseau_horaire, shipheart_heure_ete);
|
|
switch(periode_analyse)
|
|
{
|
|
default:
|
|
case 0: // 1 heure
|
|
type_mesure=3;
|
|
break;
|
|
case 1: // 24 heures
|
|
case 2: // 7 jours
|
|
case 3: // 30 jours
|
|
type_mesure=2;
|
|
break;
|
|
}
|
|
|
|
// Requete :
|
|
req = 'SELECT ID, SUM(Valeur) AS Energie ';
|
|
req += ' FROM TB_Mesures_circuits AS tb_mes ';
|
|
req += ' WHERE tb_mes.Type=' + type_mesure + ' ';
|
|
req += ' AND tb_mes.ID_Variable=' + id_variable_energie_circuit + ' ';
|
|
req += ' AND tb_mes.ID IN ';
|
|
req += ' ( SELECT Cle_primaire FROM TB_Circuits WHERE ';
|
|
req += ' Adresse_module IN ';
|
|
req += ' (SELECT Adresse_k_bus FROM TB_Modules_config WHERE ';
|
|
req += ' ID_Batterie=' + id_batterie + ' ';
|
|
req += ' AND (Type=' + shipheart_type_module_shipbase + ' OR Type=' + shipheart_type_module_shippower812 + ' OR Type=' + shipheart_type_module_shippower8DC + ') )';
|
|
req += ' ) ';
|
|
req += ' AND tb_mes.Date>="' + Fonctions_date_heure.chaine_date_heure_bdd(debut_fin.debut) + '" ';
|
|
req += ' AND tb_mes.Date<="' + Fonctions_date_heure.chaine_date_heure_bdd(debut_fin.fin) + '" ';
|
|
req += ' GROUP BY tb_mes.ID ';
|
|
req += ' ORDER BY Energie DESC; ';
|
|
//console.log("Requête = [" + req + "]");
|
|
nb = bdd.requete(req);
|
|
|
|
// Mise à jour du modèle, trié par ordre décroissant d'énergie consommée par la reqête :
|
|
total = 0;
|
|
var nb_consommateurs = 0;
|
|
|
|
grille_consommateurs.model.clear();
|
|
|
|
//---------------------------------------------------
|
|
// renseignerment de l'ID et de l'énergie consommée :
|
|
for (i = 0; i < nb; i ++)
|
|
{
|
|
res = bdd.lecture_ligne();
|
|
if (!res.isEmpty)
|
|
{
|
|
total += Number(res[1]);
|
|
nb_consommateurs ++;
|
|
grille_consommateurs.model.append({"id": Number(res[0]), "nom": "", "consigne": 0, "energie": Number(res[1]), "pourcentage": 0 });
|
|
}
|
|
else break; // Ne devrait pas arriver....
|
|
}
|
|
|
|
//---------------------------------------------------
|
|
// Mise à jour des autres informations du circuit :
|
|
// NB : on est obligé de faire cette mise à jour en deuxième passe car on doit dabord avoir le TOTAL calculé lors de la précédente...
|
|
if (total !== 0)
|
|
{
|
|
for(i = 0; i < nb_consommateurs; i++)
|
|
{
|
|
// Mise à jour du pourcentage maxi (afin de normaliser ensuite toutes les barres sur la plus grande) :
|
|
if (i === 0) pourcentage_maxi = grille_consommateurs.model.get(i).energie / total;
|
|
|
|
// Sous-requête de recherche des infos sur le circuit :
|
|
req = 'SELECT Nom, Consigne from TB_Circuits WHERE Cle_primaire=' + grille_consommateurs.model.get(i).id + ' ';
|
|
res = bdd.requete_unique(req);
|
|
|
|
// Mise à jour des informations dans le modèle :
|
|
grille_consommateurs.model.setProperty(i, "pourcentage", (grille_consommateurs.model.get(i).energie * 100 / total));
|
|
grille_consommateurs.model.setProperty(i, "nom", res[0]);
|
|
grille_consommateurs.model.setProperty(i, "consigne", Number(res[1]));
|
|
}
|
|
}
|
|
else grille_consommateurs.model.clear(); // Si le total vaut 0Wh, on peut supprimer toutes les entrées car elles sont toutes nulles...
|
|
|
|
|
|
// Arrêt du sablier:
|
|
busy_indicator.running = false;
|
|
|
|
|
|
//console.timeEnd('maj_energie')
|
|
}
|
|
|
|
|
|
//=============================================================================================================
|
|
// Mise à jour des données des courbes
|
|
//=============================================================================================================
|
|
function mise_a_jour_courbes()
|
|
{
|
|
var req, res;
|
|
var nb;
|
|
var i;
|
|
var courbe_principale_duree;
|
|
var date_debut, date_fin;
|
|
var date_debut_intervalle, date_fin_intervalle;
|
|
var date_debut_intervalle_locale;
|
|
var mini_maxi;
|
|
var type_mesure;
|
|
var valeur;
|
|
var precision;
|
|
var unite;
|
|
var max;
|
|
var largeur_bargraph;
|
|
var texte_debut_principal;
|
|
var texte_debut_secondaire;
|
|
var regroupement;
|
|
|
|
|
|
console.time('mise_a_jour_courbes')
|
|
|
|
|
|
// Préparation des paramètres du graphique :
|
|
|
|
courbe_principale_duree = Fonctions_date_heure.duree_periode_analyse(periode_analyse); // En secondes
|
|
date_fin = new Date(); // En UTC
|
|
date_debut = Fonctions_date_heure.date_add_secondes(date_fin, -(courbe_principale_duree)); // En UTC
|
|
|
|
//=======================================================
|
|
// Barres d'énergie :
|
|
if (id_variable === energie_id_variable)
|
|
{
|
|
max = 0;
|
|
|
|
grille_energie.model.clear();
|
|
|
|
// Ajustements à l'intervalle fini d'analyse :
|
|
var debut_fin = Fonctions_date_heure.ajustement_periode_analyse(periode_analyse, date_debut, date_fin, shipheart_fuseau_horaire, shipheart_heure_ete);
|
|
|
|
switch(periode_analyse)
|
|
{
|
|
default:
|
|
case 0: // 1 heure
|
|
type_mesure=3;
|
|
courbe_nombre_principal_graduations_axe_x = 13;
|
|
largeur_bargraph = 40;
|
|
regroupement = "(1)";
|
|
break;
|
|
|
|
case 1: // 24 heures
|
|
type_mesure=2;
|
|
courbe_nombre_principal_graduations_axe_x = 25;
|
|
largeur_bargraph = 35;
|
|
regroupement = "(1)";
|
|
break;
|
|
|
|
case 2: // 7 jours
|
|
type_mesure=2;
|
|
courbe_nombre_principal_graduations_axe_x = 29;
|
|
largeur_bargraph = 30;
|
|
regroupement = "(6*60*60)";
|
|
break;
|
|
|
|
case 3: // 30 jours
|
|
type_mesure=2;
|
|
courbe_nombre_principal_graduations_axe_x = 31;
|
|
largeur_bargraph = 28;
|
|
regroupement = "(24*60*60)";
|
|
break;
|
|
}
|
|
|
|
// Recherche des data dans la BDD :
|
|
req = ' SELECT';
|
|
req += ' SUM(Valeur),';
|
|
req += ' MAX(Date)';
|
|
req += ' FROM TB_Mesures_circuits as tb_mes ';
|
|
req += ' WHERE tb_mes.Type=' + type_mesure + ' ';
|
|
req += ' AND tb_mes.ID_Variable=' + id_variable_energie_circuit + ' ';
|
|
req += ' AND tb_mes.ID IN ';
|
|
req += ' (';
|
|
req += ' SELECT Cle_primaire FROM TB_Circuits WHERE ';
|
|
req += ' Adresse_module IN ';
|
|
req += ' (';
|
|
req += ' SELECT Adresse_k_bus FROM TB_Modules_config WHERE ';
|
|
req += ' ID_Batterie=' + id_batterie + ' ';
|
|
req += ' AND (Type=' + shipheart_type_module_shipbase + ' OR Type=' + shipheart_type_module_shippower812 + ' OR Type=' + shipheart_type_module_shippower8DC + ') ';
|
|
req += ' )';
|
|
req += ' ) ';
|
|
req += ' AND tb_mes.Date>="' + Fonctions_date_heure.chaine_date_heure_bdd(debut_fin.debut) + '" ';
|
|
req += ' AND tb_mes.Date<="' + Fonctions_date_heure.chaine_date_heure_bdd(debut_fin.fin) + '" ';
|
|
req += ' GROUP BY (UNIX_TIMESTAMP(Date) - UNIX_TIMESTAMP("' + Fonctions_date_heure.chaine_date_heure_bdd(debut_fin.debut) + '")) DIV ' + regroupement + ' ';
|
|
req += ' ORDER BY tb_mes.Date ';
|
|
//console.log("Requête = [" + req + "]");
|
|
nb = bdd.requete(req);
|
|
for (i = 0; i < nb; i ++)
|
|
{
|
|
res = bdd.lecture_ligne();
|
|
if (!res.isEmpty)
|
|
{
|
|
// Récupération des données de l'intervalle :
|
|
valeur = Number(res[0]);
|
|
date_fin_intervalle = Fonctions_date_heure.chaine_bdd_date_heure(res[1]);
|
|
date_debut_intervalle = Fonctions_date_heure.date_add_secondes(date_fin_intervalle, -((courbe_principale_duree / (courbe_nombre_principal_graduations_axe_x - 1)) - 1) );
|
|
|
|
// Calcul du 'max' (en Wh) :
|
|
if (max < valeur) max = valeur;
|
|
|
|
// Ajout de l'intervale au modèle :
|
|
grille_energie.model.append({"debut_intervalle": date_debut_intervalle, "debut_principal": "", "debut_secondaire": "", "valeur": valeur, "precision": 0, "unite": "", "hauteur": 0.0, "largeur": largeur_bargraph, "flag_periode_en_cours": false });
|
|
}
|
|
else break; // Ne devrait pas arriver....
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
// Calcul de l'échantillon "période en cours" (qui se termine MAINTENANT) :
|
|
// NOTA : la ligne suivnte représente environ 200-250ms, soit 90% du temps total de traitement...!
|
|
valeur = conso_energie_intervalle(debut_fin.fin, date_fin);
|
|
//console.log("DERNIER INTERVALLE : Début intervalle UTC = " + debut_fin.fin + " et fin d'intervalle UTC = " + date_fin);
|
|
|
|
// Calcul du 'max' (en Wh) avec le dernier intervalle :
|
|
if (max < valeur) max = valeur;
|
|
|
|
// Ajout du dernier intervalle au modèle :
|
|
grille_energie.model.append({"debut_intervalle": debut_fin.fin, "debut_principal": "", "debut_secondaire": "", "valeur": valeur, "precision": 0, "unite": "", "hauteur": 0.0, "largeur": largeur_bargraph, "flag_periode_en_cours": true });
|
|
|
|
|
|
//---------------------------------------------------
|
|
// Mise à jour des autres informations du circuit :
|
|
// NB : on est obligé de faire cette mise à jour en deuxième passe car on doit d'abord avoir le MAX calculé lors de la précédente...
|
|
if (max > 0)
|
|
{
|
|
for(i = 0; i < grille_energie.model.count; i++)
|
|
{
|
|
// Récupéartion de la valeur brute de l'échantillon :
|
|
valeur = grille_energie.model.get(i).valeur;
|
|
|
|
// Calcul de la hauteur de la barre :
|
|
// NB : à faire avant ajustement de la valeur en kWh !!
|
|
grille_energie.model.setProperty(i, "hauteur", (valeur / max));
|
|
|
|
// Calcul de l'unité et de la précision d'affichage en fonction de la valeur :
|
|
unite = "Wh"; // Par défaut
|
|
if (valeur < 100) precision = 1;
|
|
else if (valeur < 1000) precision = 0;
|
|
else
|
|
{
|
|
valeur /= 1000;
|
|
grille_energie.model.setProperty(i, "valeur", valeur); // Dans ce cas, il faut mettre à jour la valeur dans le modèle...
|
|
unite = "kWh"
|
|
if (valeur < 10) precision = 1;
|
|
else precision = 0;
|
|
}
|
|
grille_energie.model.setProperty(i, "precision", precision);
|
|
grille_energie.model.setProperty(i, "unite", unite);
|
|
|
|
// Calcul des dates de début principales et secondaires de l'échantillon avec le format associé :
|
|
date_debut_intervalle = grille_energie.model.get(i).debut_intervalle;
|
|
date_debut_intervalle_locale = Fonctions_date_heure.date_courante(date_debut_intervalle, shipheart_fuseau_horaire, shipheart_heure_ete);
|
|
|
|
// Détermination des légendes de temps :
|
|
switch(periode_analyse)
|
|
{
|
|
default:
|
|
case 0: // 1 heure
|
|
texte_debut_principal = Qt.formatDateTime(date_debut_intervalle_locale, "HH:mm");
|
|
texte_debut_secondaire = "";
|
|
break;
|
|
|
|
case 1: // 24 heures
|
|
if (Math.trunc(date_debut_intervalle_locale.getHours() % 6) === 0)
|
|
{
|
|
texte_debut_principal = Qt.formatDateTime(date_debut_intervalle_locale, "HH:mm");
|
|
texte_debut_secondaire = "";
|
|
}
|
|
else
|
|
{
|
|
texte_debut_principal = "";
|
|
texte_debut_secondaire = Qt.formatDateTime(date_debut_intervalle_locale, "HH:mm");
|
|
}
|
|
break;
|
|
|
|
case 2: // 7 jours
|
|
if (date_debut_intervalle_locale.getHours() === 0)
|
|
{
|
|
texte_debut_principal = Qt.formatDateTime(date_debut_intervalle_locale, "dd/MM");
|
|
texte_debut_secondaire = "";
|
|
}
|
|
else
|
|
{
|
|
texte_debut_principal = "";
|
|
texte_debut_secondaire = Qt.formatDateTime(date_debut_intervalle_locale, "HH:mm");
|
|
}
|
|
break;
|
|
|
|
case 3: // 30 jours
|
|
texte_debut_principal = Qt.formatDateTime(date_debut_intervalle_locale, "dd");
|
|
texte_debut_secondaire = "";
|
|
break;
|
|
}
|
|
grille_energie.model.setProperty(i, "debut_principal", texte_debut_principal);
|
|
grille_energie.model.setProperty(i, "debut_secondaire", texte_debut_secondaire);
|
|
}
|
|
}
|
|
else grille_energie.model.clear(); // Si le MAX vaut 0Wh, on peut supprimer toutes les entrées car elles sont toutes nulles...
|
|
|
|
}
|
|
|
|
//=======================================================
|
|
// Courbes des variables (autre qu'énergie) :
|
|
else
|
|
{
|
|
|
|
switch(periode_analyse)
|
|
{
|
|
default:
|
|
case 0: // 1 heure
|
|
courbe_nombre_principal_graduations_axe_x = 7 // Nombre de bandes verticales + 1
|
|
courbe_nombre_zoom_1_graduations_axe_x = 7 // Utilisé lorsque le taux de zoom passe en-dessous de 0.5
|
|
courbe_format_principal_axe_x = "HH:mm" // exemple : "yyyy-MM-dd HH:mm:ss"
|
|
courbe_format_zoom_1_axe_x = "HH:mm" // Utilisé lorsque le taux de zoom passe en-dessous de 0.5
|
|
break;
|
|
|
|
case 1: // 24 heures
|
|
courbe_nombre_principal_graduations_axe_x = 13 // Nombre de bandes verticales + 1
|
|
courbe_nombre_zoom_1_graduations_axe_x = 13 // Utilisé lorsque le taux de zoom passe en-dessous de 0.5
|
|
courbe_format_principal_axe_x = "HH:mm" // exemple : "yyyy-MM-dd HH:mm:ss"
|
|
courbe_format_zoom_1_axe_x = "HH:mm" // Utilisé lorsque le taux de zoom passe en-dessous de 0.5
|
|
break;
|
|
|
|
case 2: // 7 jours
|
|
courbe_nombre_principal_graduations_axe_x = 8 // Nombre de bandes verticales + 1
|
|
courbe_nombre_zoom_1_graduations_axe_x = 8 // Utilisé lorsque le taux de zoom passe en-dessous de 0.5
|
|
courbe_format_principal_axe_x = "dd/MM HH:mm" // exemple : "yyyy-MM-dd HH:mm:ss"
|
|
courbe_format_zoom_1_axe_x = "HH:mm" // Utilisé lorsque le taux de zoom passe en-dessous de 0.5
|
|
break;
|
|
|
|
case 3: // 30 jours
|
|
courbe_nombre_principal_graduations_axe_x = 31 // Nombre de bandes verticales + 1
|
|
courbe_nombre_zoom_1_graduations_axe_x = 13 // Utilisé lorsque le taux de zoom passe en-dessous de 0.5
|
|
courbe_format_principal_axe_x = "dd" // exemple : "yyyy-MM-dd HH:mm:ss"
|
|
courbe_format_zoom_1_axe_x = "HH:mm" // Utilisé lorsque le taux de zoom passe en-dessous de 0.5
|
|
break;
|
|
}
|
|
|
|
// Mise à jour des données de la courbe principale :
|
|
res = tbmesuresModel.maj_data(graphique.series("Courbe principale"), "TB_Mesures_batteries", id_batterie, id_variable, date_debut, date_fin, 1, 1, (shipheart_fuseau_horaire + shipheart_heure_ete), 0, 0);
|
|
|
|
//-----------------------------------------------------------------------------------------------------
|
|
// Cas où aucune donnée n'est disponible sur la période :
|
|
if (Number(res[0]) === 0)
|
|
{
|
|
id_circuit = true; // Permet d'afficher le texte "Aucune donnée disponible..." sur la zone graphique
|
|
}
|
|
//-----------------------------------------------------------------------------------------------------
|
|
// Cas où des données sont disponibles sur la période (cas normal donc...) :
|
|
else
|
|
{
|
|
courbe_flag_vide = false;
|
|
|
|
xMin_date = new Date(Number(res[1])); // Nécessaire pour la Min de l'axe X (NB : les dates retournées par 'maj_data' sont en heure locale)
|
|
xMax_date = new Date(Number(res[3])); // Nécessaire pour la Max de l'axe X (NB : les dates retournées par 'maj_data' sont en heure locale)
|
|
mini_maxi = Fonctions.ajustement_mini_maxi(Number(res[6]), Number(res[8]), courbe_nombre_graduations_axe_y);
|
|
yMin_valeur = mini_maxi.mini // Nécessaire pour le Min de l'axe Y
|
|
yMax_valeur = mini_maxi.maxi // Nécessaire pour le Max de l'axe Y
|
|
precision_affichage_axe_y_courbe = Fonctions.precision_affichage_mini_maxi(Number(res[6]), Number(res[8]), courbe_nombre_graduations_axe_y);
|
|
//console.log("Min val=" + Number(res[6]) + " et mini=" + yMin_valeur + " - Max val=" + Number(res[8]) + " et maxi=" + yMax_valeur);
|
|
}
|
|
|
|
}
|
|
|
|
console.timeEnd('mise_a_jour_courbes')
|
|
|
|
// Arrêt du sablier :
|
|
busy_indicator.running = false; // Arrêt du tourniquet s'il est en marche
|
|
}
|
|
|
|
|
|
|
|
//=============================================================================================================
|
|
// RAZ du zoom
|
|
//=============================================================================================================
|
|
function raz_zoom()
|
|
{
|
|
|
|
graphique.zoomReset(); // RAZ du zoom proprement dit
|
|
graphique.scrollLeft(dragTarget.cumulX); // RAZ du déplacement horizontal par annulation du cumul
|
|
graphique.scrollUp(dragTarget.cumulY); // RAZ du déplacement vertical par annulation du cumul
|
|
|
|
dragTarget.cumulX = 0; // RAZ du cumul après retour à la position d'origine
|
|
dragTarget.cumulY = 0;
|
|
|
|
flag_zoom_en_cours = false;
|
|
zoomx_en_cours = 1;
|
|
}
|
|
|
|
|
|
//=============================================================================================================
|
|
// Recherche de l'index d'une certaine valeur dans un modele
|
|
//=============================================================================================================
|
|
function recherche_index_valeur_modele(cible, modele, nom_modele)
|
|
{
|
|
var i;
|
|
|
|
for (i=0; i<modele.count; i ++)
|
|
{
|
|
if (modele.get(i).valeur === cible) break; // Cas où la valeur cible a été trouvée parmi les valeurs du modèle
|
|
}
|
|
if (i === modele.count)
|
|
{
|
|
console.log("Aucune valeur trouvée pour cible = [" + cible + "] du modèle = " + nom_modele);
|
|
i = -1; // Cas où la valeur n'a pas été trouvée parmi les valeurs du modèle
|
|
}
|
|
//else console.log("Valeur trouvée : [" + i + "] pour cible = [" + cible + "] soit texte = [" + modele.get(i).texte + "]");
|
|
return(i);
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|