//======================================================================================================================================================================== // RESERVOIR.QML // Ce fichier traite l'affichage des détails d'un réservoir. La page affiche notamment : // - le niveau actuel sous forme graphique (avec référence aux seuils haut et bas) // - la contenance actuelle (dans l'unit en cours) et son niveau relatif // - la liste des derniers événements, // - la courbe historique de niveau // La page est appelée depuis la page HOME, en cliquant sur la zone d'information du réservoir. //======================================================================================================================================================================== 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_reservoir // Mis en forme : readonly property double affichage_hauteur_utile: shipheart_hauteur_page - shipheart_hauteur_bandeau_haut - shipheart_hauteur_bandeau_bas - (2 * espace_lignes_principales) readonly property double pourcentage_hauteur_ligne_haut: 0.6 readonly property double pourcentage_hauteur_ligne_erreur: 0.3 // Part de 'pourcentage_hauteur_ligne_haut' readonly property double espace_lignes_principales: 20 readonly property double pourcentage_largeur_colonne_gauche: 0.5 readonly property double pourcentage_largeur_graphique: 0.85 readonly property double espace_colonnes_principales: 20 readonly property double espace_colonnes_secondaires: 50 readonly property int nombre_lignes_historique_visibles: 8 readonly property int nombre_lignes_historique_par_page: 30 readonly property double nb_boutons_periode: 4 readonly property int periode_init_raffraichissement_parametres: 20 // 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: 20 // En ms readonly property int periode_raffraichissement_graphiques: (15 * 1000) // en ms // Affichage : 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 int periode_analyse: 0 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: "--" // Historique : property string date_premiere_entree_historique: "" // Contient une chaine (format BDD) représentant la date/heure de l'entrée historique la plus ancienne. Permet de recharger de plus anciennes lors d'un MORE... property string date_derniere_entree_historique: "" // Contient une chaine (format BDD) représentant la date/heure de l'entrée historique la plus récente. Permet de ne recharger que les nouvelles... property bool flag_journal_full: false // est positionné à vrai quand il n'y a plus d'enregistrements supplémentaires disponibles pour ce filtre // Données : property bool flag_init: true // Permet de bloquer certains affichage tant que l'init n'est pas terminée property int id_reservoir: 0 property int id_variable: 0 property int etat_systeme: 0 property int etat: 0 property string nom_reservoir: "---" property double niveau: 0 property double niveau_seuil_bas: 75 property double niveau_seuil_haut: 25 property int capacite: 0 property int id_famille: 1 // Eau par défaut property int precision_affichage_mesures: 0 property int type_periode_graphique: 1 property string texte_alerte: "" property int nb_events: -1 // -1: aucun event n'est lié à ce capteur, 0: au moins 1 event est lié à ce capteur mais aucun n'a Etat_test>0, 1: au moins un event lié et au moins un Etat_test=1 // 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 du circuit : //=========================================================== 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; busy_indicator.running = true; init_page(); } else { mise_a_jour_valeurs_instantanees(); } } } //=========================================================== // 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 (!flag_zoom_en_cours) // On ne met pas à jour les courbes si on est en train de zoomer { mise_a_jour_courbes(); } } } } // Titre de la page : Shipheart_titre_1 {titre:nom_reservoir; hauteur: shipheart_hauteur_bandeau_haut} //------------------------------------------------------- // Ecran haut - gauche : //------------------------------------------------------- Rectangle { id: zone_affichage_haut_gauche x: 0 y: shipheart_hauteur_bandeau_haut; width: (shipheart_largeur_page * pourcentage_largeur_colonne_gauche) - (espace_colonnes_principales / 2); height: affichage_hauteur_utile * pourcentage_hauteur_ligne_haut; color: "transparent" visible: (etat_systeme === 0) //------------------------------------------------------- // Valeur si unité : Rectangle { anchors.fill: parent color: "transparent" visible: (symbole_unite !== "") Text { id: valeur_actuelle anchors { verticalCenter: parent.verticalCenter verticalCenterOffset: -80 horizontalCenter: parent.horizontalCenter horizontalCenterOffset: -20 } text: Fonctions.conversion_volume((capacite * niveau / 100), shipheart_unite_volume, "", precision_affichage_mesures); font.family: "Montserrat" font.pixelSize: 120 font.weight: Font.Bold // color: (nb_events > 0) ? couleur_rouge : couleur_blanc_casse color: couleur_blanc_casse } //------------------------------------------------------- // Unite : Text { anchors { baseline: valeur_actuelle.baseline left: valeur_actuelle.right leftMargin: 10 } text: shipheart_unite_volume_symbole font.pixelSize: 20 color: couleur_blanc_casse } //------------------------------------------------------- // Valeur en % de remplissage : Text { id: valeur_relative anchors { verticalCenter: parent.verticalCenter verticalCenterOffset: 50 horizontalCenter: parent.horizontalCenter horizontalCenterOffset: -10 } text: qsTr("[ %1 % ]").arg(niveau.toFixed(0)) font.family: "Montserrat" font.pixelSize: 50 color: couleur_blanc_casse } } //----------------------------------- // 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: nb_events >= 0 Image { source: (nb_events === 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.open(); } } } Component.onCompleted: { mise_a_jour_valeurs_instantanees(); } } //------------------------------------------------------- // Ecran haut - droite : //------------------------------------------------------- Rectangle { id: zone_affichage_haut_droite x: (shipheart_largeur_page * pourcentage_largeur_colonne_gauche) + (espace_colonnes_principales / 2); y: shipheart_hauteur_bandeau_haut width: ((shipheart_largeur_page * (1 - pourcentage_largeur_colonne_gauche)) - (espace_colonnes_principales / 2)); height: (affichage_hauteur_utile * pourcentage_hauteur_ligne_haut * pourcentage_hauteur_ligne_erreur) - (espace_lignes_principales / 2) color: "transparent" border.width: 3 border.color: couleur_rouge radius: shipheart_rayon_bouton visible: (etat_systeme === 0) && (texte_alerte !== "") Image { id: picto_alerte source: "/images/ak14.png" sourceSize.height: 30; anchors { left: parent.left leftMargin: 20 verticalCenter: parent.verticalCenter } } Text { // Affiche parfaitement 1, 2 ou 3 lignes de texte. A partir de 4 et 5, ça mort sur le contour du rectangle mais c'est encore lisible. width: parent.width - picto_alerte.width - picto_alerte.anchors.leftMargin - anchors.leftMargin; wrapMode: Text.Wrap horizontalAlignment: Text.AlignHCenter leftPadding: 10 // Dégage le pictogramme sur la gauche rightPadding: 10 // Evite de se caler sur le bord droit du rectangle text: texte_alerte; anchors { left: picto_alerte.right verticalCenter: parent.verticalCenter } font.pixelSize: 16 color: couleur_blanc_casse } } //------------------------------------------------------- // Ecran milieu - droite : //------------------------------------------------------- Rectangle { id: zone_affichage_milieu_droite x: zone_affichage_haut_droite.x y: zone_affichage_haut_droite.y + zone_affichage_haut_droite.height + espace_lignes_principales width: zone_affichage_haut_droite.width height: (affichage_hauteur_utile * pourcentage_hauteur_ligne_haut * (1 - pourcentage_hauteur_ligne_erreur)) - (espace_lignes_principales / 2) color: "transparent" visible: (etat_systeme === 0) Rectangle { id: visuel_niveau width: parent.width * 0.6 height: parent.height color: "transparent" border.width: 3 border.color: couleur_gris_moyen radius: 30 anchors { horizontalCenter: parent.horizontalCenter } Rectangle { width: parent.width - (2 * parent.border.width) height: (parent.height - (2 * parent.border.width)) * niveau / 100 radius: 2 color: couleur_bleu_koriolan_transparent anchors { bottom: parent.bottom bottomMargin: parent.border.width horizontalCenter: parent.horizontalCenter } } Rectangle { // Rectangle permettant de centrer tout le texte de capacité (valeur et unité) width: texte_capacite_reservoir.paintedWidth + texte_unite_capacite_reservoir.paintedWidth + texte_unite_capacite_reservoir.anchors.leftMargin height: texte_capacite_reservoir.paintedHeight anchors { verticalCenter: parent.verticalCenter horizontalCenter: parent.horizontalCenter } color: "transparent" Text { id: texte_capacite_reservoir text: Fonctions.conversion_volume(capacite, shipheart_unite_volume, "", 0); font.pixelSize: 50 font.bold: true color: couleur_gris_tres_pale_transparent } Text { id: texte_unite_capacite_reservoir text: shipheart_unite_volume_symbole anchors { left: texte_capacite_reservoir.right leftMargin: 20 baseline: texte_capacite_reservoir.baseline } font.pixelSize: 20 font.bold: true color: couleur_gris_tres_pale_transparent } } /* Rectangle { // Ligne de niveau haut width: parent.width * 0.9 height: 1 anchors { bottom: parent.bottom bottomMargin: (parent.height - (2 * parent.border.width)) * niveau_seuil_haut / 100 horizontalCenter: parent.horizontalCenter } color: couleur_vert_transparent } Rectangle { // Ligne de niveau bas width: parent.width * 0.9 height: 1 anchors { bottom: parent.bottom bottomMargin: (parent.height - (2 * parent.border.width)) * niveau_seuil_bas / 100 horizontalCenter: parent.horizontalCenter } color: couleur_rouge_transparent } */ } } //------------------------------------------------------- // Ecran bas : //------------------------------------------------------- Rectangle { id: zone_affichage_bas x: 0 y: zone_affichage_haut_gauche.y + zone_affichage_haut_gauche.height + espace_lignes_principales; width: shipheart_largeur_page; height: affichage_hauteur_utile * (1 - pourcentage_hauteur_ligne_haut) color: "transparent" visible: (etat_systeme === 0) //======================== // 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 !== 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: "" + symbole_unite + "" 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; 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 réservoir Absent // (le module sur lequel le réservoir est configuré 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 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 ce réservoir n'est pas accessible !

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.

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_reservoir type_source: shipheart_source_mesure_reservoir id_source: id_reservoir id_variable: page_reservoir.id_variable titre_widget: qsTr("Niveau") unite: "%" } //=========================================================== // 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; //==================================================================== // 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 }); //==================================================================== // Recherche de ID_Variable : req = ' SELECT Cle_primaire, Unite FROM TB_Variables WHERE Code="' + shipheart_reservoir_niveau + '" '; res = bdd.requete_unique(req); id_variable = Number(res[0]); symbole_unite = res[1]; //==================================================================== // Première mise à jour : mise_a_jour_valeurs_instantanees(); mise_a_jour_courbes(); //==================================================================== // Libération du flag : flag_init = false; } //============================================================================================================= // Mise à jour de la valeur instantanée //============================================================================================================= function mise_a_jour_valeurs_instantanees() { var req, res; var nb; var i; var type_entrees; var etat; //==================================================================== // Valeur numérique instantanée : req = 'SELECT Niveau, '; req += ' Capacite, '; req += ' ID_Famille, '; req += ' Etat_systeme, '; req += ' Type_entrees, '; req += ' Etat_entrees '; req += ' FROM TB_Reservoirs '; req += ' WHERE Cle_primaire="' + id_reservoir + '" '; res = bdd.requete_unique(req); niveau = Number(res[0]); capacite = Number(res[1]); id_famille = Number(res[2]); etat_systeme = Number(res[3]); type_entrees = Number(res[4]); etat = Number(res[5]); //console.log("Niveau = " + niveau); switch(shipheart_unite_volume) { case shipheart_unite_volume_litres: precision_affichage_mesures = 0; break; case shipheart_unite_volume_us_gallons: precision_affichage_mesures = 1; break; case shipheart_unite_volume_imp_gallons: precision_affichage_mesures = 1; break; default : precision_affichage_mesures = 1; break; } // Message d'alerte : if ((type_entrees === shipheart_reservoir_type_entrees_aux) && (etat !== shipheart_capteur_etat_normal)) { texte_alerte = qsTr("La valeur lue sur les electrodes plongées de ce réservoir est incohérente.
Vérifier l'ordre de cablage des électrodes sur les entrées auxiliaires à l'arrière de ShipBase. ") } else texte_alerte = ""; // Recherche des events liés à ce capteur et leur valeur : nb_events = -1; req = 'SELECT Etat_test '; req += ' FROM TB_Events '; req += ' WHERE Alarme_source>0 '; req += ' AND Type_source=' + shipheart_source_mesure_reservoir + ' '; req += ' AND ID_Source=' + id_reservoir + ' '; //console.log("Requête = [ " + req + "; ]"); nb = bdd.requete(req); for (i = 0; i < nb; i ++) { res = bdd.lecture_ligne(); if (!res.isEmpty) { if (nb_events < Number(res[0])) nb_events = Number(res[0]); } } // En sortant, on a donc nb_events=-1 si aucun event lié, 0 si au moins un event lié mais tous leurs Etat_test=0, et 1 si au moins un event et au moins un a Etat_test=1. } //============================================================================================================= // Mise à jour des données des courbes //============================================================================================================= function mise_a_jour_courbes() { var req, res; var nb; var courbe_principale_duree; var date_debut, date_fin; var delta_x, delta_y; var min, max; var mini_maxi; var gradient; var id_sb_in_i, id_sb_usb_i; //console.time('mise_a_jour_courbes') // Paramètres de la courbe dépendant de la période d'analyse : 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; } // Dates et durée : 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 //console.log("Date de début initiale UTC = " + date_debut + " et date de fin UTC = " + date_fin) // Mise à jour des données de la courbe principale : res = tbmesuresModel.maj_data(graphique.series("Courbe principale"), "TB_Mesures_reservoirs", id_reservoir, 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) { courbe_flag_vide = 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) // var mini_maxi = Fonctions.ajustement_mini_maxi(Number(res[6]), Number(res[8]), courbe_principale_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 yMin_valeur = 0 yMax_valeur = 100.4 // Le '.4' permet de libérer l'espace de l'épaisseur du trait haut quand on est à 100%... // precision_affichage_axe_y_courbe = Fonctions.precision_affichage_mini_maxi(Number(res[6]), Number(res[8]), courbe_principale_nombre_graduations_axe_y); precision_affichage_axe_y_courbe = 0 //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') } 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; } }