Import initial du projet shipheart_cm5
This commit is contained in:
+982
@@ -0,0 +1,982 @@
|
||||
//========================================================================================================================================================================
|
||||
// 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: "<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;
|
||||
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 !<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_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.<br />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;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user