//======================================================================================================================================================================== // CONFIG_VALEURS_NIVEAUX.QML // Ce fichier traite la configuration de courbes de conversion valeurs <=> niveaux (batteries et réservoirs). La page permet de visualiser ou modifier // des points et d'afficher la courbe de conversion résultante. Il est aussi possible de recharger la config usine. // L'appel se fait en passant la propriété 'table' qui permet de connaitre le type d'éléments à traiter. // La page est appelée depuis la page CONFIG_XXXXXX, en cliquant sur le bouton "courbe de conversion" //======================================================================================================================================================================== import QtQuick 2.12 import QtQuick.Controls 2.12 import QtQml.Models 2.1 import QtCharts 2.3 import qt.koriolan.shipheart 1.0 import"fonctions.js" as Fonctions import"fonctions_batteries.js" as Fonctions_batteries import"fonctions_reservoirs.js" as Fonctions_reservoirs Shipheart_page { id: page_config_valeurs_niveaux // Mis en forme : readonly property double pourcentage_largeur_colonne_gauche: 0.25 readonly property double pourcentage_hauteur_ligne_haut: 0.70 readonly property double espace_colonnes_principales: 20 readonly property double espace_lignes_principales: 20 readonly property double affichage_hauteur_utile: shipheart_hauteur_page - shipheart_hauteur_bandeau_haut - shipheart_hauteur_bandeau_bas readonly property int nombre_lignes_points_visibles: 6 readonly property int diametre_points_deplacables: 200 readonly property double coeff_fleche_reglage_fin: 0.8 readonly property int periode_init_raffraichissement_parametres: 1 // En ms readonly property int periode_raffraichissement_parametres: 1000 // En ms readonly property int step_timer_appui_long: 30 // en ms // Affichage : property string texte_titre: "Courbe de conversion" property string texte_aucun_element: "Aucun élément n'est actuellement configuré" property bool courbe_flag_vide: true property bool flag_init: true // Permet de bloquer certains affichage tant qu el'init n'est pas terminée property bool flag_alignement_demande: false property bool flag_nouvelles_valeurs: false property bool flag_nombre_max_points_atteint: false property bool flag_affichage_reglage_fin: false property int point_en_cours: -1 property int point_a_supprimer: -1 // Données : property string table: "" property int id: 0 property string champ_liste: "" // Permet de conserver le nom du champ utilisé pour stocker la liste dans la table TB_Batteries ou TB_Reservoirs property int nb_points: 0 property string unite_x: "" property int precision_x: 3 property int precision_x_graphique: 2 property int tension_nominale: 12 property int type_entrees_reservoir: 0 property int mode_reservoir: 0 property int sous_mode_reservoir: 0 property int capacite: 0 property int valeur_timer_appui_long: 0 property real xMin property real xMax property real yMin property real yMax property bool flag_yMax_hors_limite: false property bool flag_valeurs_discretes: false // Indique que les capteurs en cours d'utilisation ne peuvent prendre que des valeurs discretes (a priori, uniquement le cas d'Entrées Aux) property int nb_intervalles_graphique: 10 // Nb : cela se traduit par N+1 valeurs d'absice property int etat_entrees: 0 // 0=Ok, 1=Electrodes plongées dans le désordre property double step_reglage_fin_vertical: 0.1 property double step_reglage_fin_horizontal: 0.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 : //=========================================================== 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_valeur_actuelle(); } } } //=========================================================== // Timer pour gérer les appuis longs : //=========================================================== Timer { id: timer_appui_long interval: step_timer_appui_long triggeredOnStart: false repeat: true onTriggered: { if (valeur_timer_appui_long < shipheart_mouse_press_and_hold_interval_tres_long) valeur_timer_appui_long+= step_timer_appui_long; else // Le fait de ne pas utilise le OnPressandHold permet d'être sût que la sortie se fait au même moment que la fin du comptage (sinon, on peut avoir de petits décalages) { timer_appui_long.running = false; valeur_timer_appui_long = 0; //--------------------------------------------- // Suppression de point : if (point_a_supprimer !== -1) supprimer_point(); // On ne peut pas utiliser 'mousearea_bouton_suppression_point.pressed' car il ya 1 bouton pour chaque délégué et cela génère une erreur (à juste raison car on ne saurait pas lequel) //--------------------------------------------- // Enregistrement : if (mousearea_bouton_enregistrer.pressed) { if (flag_nouvelles_valeurs) enregistrement(); } //--------------------------------------------- // Config usine : if (mousearea_bouton_courbe_usine.pressed) { flag_init = true; courbe_usine(); mise_a_jour_valeur_actuelle(); enregistrement(); flag_init = false; } } } } // Titre de la page : Shipheart_titre_1 {titre:texte_titre; 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) - (espace_lignes_principales / 2) clip:true // Evite que le ListView suivant ne puisse dépasser du rectangle color: "transparent" ListModel { id: modele_points } ListView { id: liste_points anchors.fill: parent model: modele_points spacing: 5 snapMode: GridView.SnapToRow // L'arret de l'affichage se fera sur une rangée entière highlightMoveDuration: 600 // défini la vitesse à laquelle on va se déplacer sur l'item sélectionné (utilisé avec l'affectation du 'currentIndex' quand on touche un rectangle/cercle) highlightMoveVelocity: -1 // -1 si le précedent est actif et réciproquement footerPositioning: ListView.InlineFooter // Positionne le footer à la fin des enregistrements (pratique pour le bouton MORE...) visible: flag_init ? false : true; opacity: Math.max(0.1, 1.0 - (Math.abs(verticalOvershoot) *3 / height)) // Permet de rendre transparent le contenu lorsqu'on dépasse la zone de défilement delegate: Rectangle { height: ((liste_points.height - ((nombre_lignes_points_visibles - 1) * liste_points.spacing)) / nombre_lignes_points_visibles) width:zone_affichage_haut_gauche.width color: model.actif ? shipheart_couleur_fond_bouton_actif : shipheart_couleur_fond_bouton radius: shipheart_rayon_bouton Rectangle { // Barre grandissante pour montrer durée d'appui vs tempo nécessaire : width: (valeur_timer_appui_long / shipheart_mouse_press_and_hold_interval_tres_long) * parent.width height: parent.height color: couleur_rouge_transparent radius: shipheart_rayon_bouton visible: mousearea_bouton_suppression_point.pressed } Text { id: no_ligne // Premier élément du bouton : n° de ligne width: parent.width * 0.2; anchors.verticalCenter: parent.verticalCenter horizontalAlignment: Text.AlignHCenter text: index + 1 font.pixelSize: 14 font.bold: true color: couleur_gris_moyen } Text { id: xs // Deuxième élément du bouton : la valeur de X x: no_ligne.width; width: parent.width * 0.4; anchors.verticalCenter: parent.verticalCenter horizontalAlignment: Text.AlignHCenter text: Number(model.x).toLocaleString(Qt.locale(),'f',precision_x) + " " + unite_x font.pixelSize: 14 color: couleur_blanc_casse } Text { id: ys_pc // Troisième élément du bouton : valeur de Y en % x: no_ligne.width + xs.width; width: parent.width - no_ligne.width - xs.width; anchors { verticalCenter: parent.verticalCenter verticalCenterOffset: (table === "TB_Reservoirs") ? -10 : 0 } horizontalAlignment: Text.AlignHCenter text: Number(model.y).toLocaleString(Qt.locale(),'f',1) + " %" font.pixelSize: 14 rightPadding: 20 color: couleur_blanc_casse } Text { id: ys_l // Troisième élément du bouton : valeur de Y en litres (ou unité de volume en cours) visible: (table === "TB_Reservoirs") x: no_ligne.width + xs.width; width: parent.width - no_ligne.width - xs.width; anchors { verticalCenter: parent.verticalCenter verticalCenterOffset: 10 } horizontalAlignment: Text.AlignHCenter text: Fonctions.conversion_volume((Number(model.y) * capacite / 100), shipheart_unite_volume, shipheart_unite_volume_symbole, 1); font.pixelSize: 14 rightPadding: 20 color: couleur_blanc_casse } Image { id: image_changed // Affichage du coin inférieur-droit en orange si le point a été modifié source: "/images/coin_bas_droite_orange.png" sourceSize.height: 20 anchors { bottom: parent.bottom right: parent.right } visible: model.changed ? true : false } MouseArea { id: mousearea_bouton_suppression_point anchors.fill: parent onPressed: { // Déclenché lors de l'appui inital if ((index === point_en_cours) && // On n'autorise la suppression d'un point que s'il est sélectionnés depuis le graphique (évite que les tentative de suppression se traduisent par un toggle de la sélection) (!flag_valeurs_discretes) && // On ne peut pas supprimer de points sur des électrodes plongées (nb_points > 2)) // On n'accepte pas de supprimer des points s'il n'y en a plus que 2 (ou moins)... { timer_appui_long.running = true; valeur_timer_appui_long = 0; point_a_supprimer = point_en_cours; } shipheart_clic(); } onClicked: { // Déclenché si l'appui n'a pas été suffisement long pour avoir un Press&Hold : timer_appui_long.running = false; valeur_timer_appui_long = 0; point_a_supprimer = -1; // Sélection du point : if (point_en_cours !== -1) modele_points.setProperty(point_en_cours, "actif", false); // Déselection du point en cours précédent modele_points.setProperty(index, "actif", true); // Sélection du nouveau point en cours point_en_cours = index; // Mémorisation du point en cours } onDoubleClicked: { flag_affichage_reglage_fin = !flag_affichage_reglage_fin; } onExited: { // Déclenché si le doigt sort de la zone du bouton (sinon, on continue à compter alors qu'on est sorti de la zone et même relaché le doigt !!) timer_appui_long.running = false; valeur_timer_appui_long = 0; point_a_supprimer = -1; } } } Label { anchors.fill: parent horizontalAlignment: Qt.AlignHCenter verticalAlignment: Qt.AlignVCenter text: qsTr("Aucun point de conversion n'est configuré pour le moment...") color: couleur_rouge_transparent font.pixelSize: 15 font.bold: true wrapMode: Label.Wrap visible: courbe_flag_vide } } } //------------------------------------------------------- // Ecran Bas - Gauche : //------------------------------------------------------- Rectangle { id: zone_affichage_bas_gauche x: 0 y: shipheart_hauteur_bandeau_haut + zone_affichage_haut_gauche.height + espace_lignes_principales; width: (shipheart_largeur_page * pourcentage_largeur_colonne_gauche) - (espace_colonnes_principales / 2) height: (affichage_hauteur_utile * (1 - pourcentage_hauteur_ligne_haut)) - (espace_lignes_principales * 3 / 2) color: "transparent" Column { anchors.fill: parent spacing: 5 //----------------------- // Ajout d'un point : Rectangle { width: parent.width height: (parent.height/3) - (parent.spacing/3) color: shipheart_couleur_fond_bouton radius: shipheart_rayon_bouton visible: !flag_valeurs_discretes // On ne peut pas ajouter de points sur des électrodes plongées Text { anchors { verticalCenter: parent.verticalCenter horizontalCenter: parent.horizontalCenter } text: "+" font.pixelSize: 40 font.bold: true color: !flag_nombre_max_points_atteint ? couleur_blanc_casse : couleur_gris_fonce } MouseArea{ id: mousearea_ajout_point anchors.fill : parent onClicked: { if (!flag_nombre_max_points_atteint) ajouter_point(); else { boite_nombre_max_points_atteint.imageTitreSource = "/images/ampoule.png"; boite_nombre_max_points_atteint.message = qsTr("Le nombre maximal de points d'étalonnage a été atteint (%1 points)
Vous ne pouvez pas en ajouter sans en avoir prélablement supprimé.").arg(courbe_etalonnage_max_points); boite_nombre_max_points_atteint.open(); } } onPressed: shipheart_clic(); enabled: point_en_cours !== -1 // Si aucun point n'est sélectionné, on n'autorise pas l'ajout (car on ne sait pas où insérer le nouveau point). NB : ce cas ne doit pas arriver car on fait en sorte d'avoir toujours un point actif... } } //----------------------- // Paramètres usine : Rectangle { width: parent.width height: (parent.height/3) - (parent.spacing/3) color: shipheart_couleur_fond_bouton radius: shipheart_rayon_bouton Rectangle { // Barre grandissante pour montrer durée d'appui vs tempo nécessaire : width: (valeur_timer_appui_long / shipheart_mouse_press_and_hold_interval_tres_long) * parent.width height: parent.height color: couleur_rouge_transparent radius: shipheart_rayon_bouton visible: mousearea_bouton_courbe_usine.pressed } Text { anchors { verticalCenter: parent.verticalCenter horizontalCenter: parent.horizontalCenter } font.pixelSize: 20 text: qsTr("Courbe usine") color: couleur_blanc_casse } MouseArea { id: mousearea_bouton_courbe_usine anchors.fill: parent onPressed: { // Déclenché lors de l'appui inital timer_appui_long.running = true; valeur_timer_appui_long = 0; shipheart_clic(); } onClicked: { // Déclenché si l'appui n'a pas été suffisement long pour avoir un Press&Hold : timer_appui_long.running = false; valeur_timer_appui_long = 0; } onExited: { // Déclenché si le doigt sort de la zone du bouton (sinon, on continue à compter alors qu'on est sorti de la zone et même relaché le doigt !!) timer_appui_long.running = false; valeur_timer_appui_long = 0; } } } //----------------------- // Bouton Enregistrer : Rectangle { width: parent.width height: (parent.height/3) - (parent.spacing/3) color: shipheart_couleur_fond_bouton radius: shipheart_rayon_bouton Rectangle { // Barre grandissante pour montrer durée d'appui vs tempo nécessaire : width: (valeur_timer_appui_long / shipheart_mouse_press_and_hold_interval_tres_long) * parent.width height: parent.height color: couleur_vert_transparent radius: shipheart_rayon_bouton visible: mousearea_bouton_enregistrer.pressed } Text { anchors { verticalCenter: parent.verticalCenter horizontalCenter: parent.horizontalCenter } font.pixelSize: 20 text: qsTr("Enregistrer") color: flag_nouvelles_valeurs ? couleur_blanc_casse : couleur_gris_fonce } MouseArea { id: mousearea_bouton_enregistrer anchors.fill: parent onPressed: { // Déclenché lors de l'appui inital timer_appui_long.running = true; valeur_timer_appui_long = 0; shipheart_clic(); } onClicked: { // Déclenché si l'appui n'a pas été suffisement long pour avoir un Press&Hold : timer_appui_long.running = false; valeur_timer_appui_long = 0; } onExited: { // Déclenché si le doigt sort de la zone du bouton (sinon, on continue à compter alors qu'on est sorti de la zone et même relaché le doigt !!) timer_appui_long.running = false; valeur_timer_appui_long = 0; } } } } } //------------------------------------------------------- // Ecran Droite : //------------------------------------------------------- Rectangle { id: zone_affichage_droite x: zone_affichage_haut_gauche.width + espace_colonnes_principales y: shipheart_hauteur_bandeau_haut; width: (shipheart_largeur_page * (1 - pourcentage_largeur_colonne_gauche)) - (espace_colonnes_principales / 2) height: affichage_hauteur_utile; color: "transparent" //======================== // Graphique : ChartView { id: graphique antialiasing: true anchors.fill: parent margins { // Attention : ces marges représentent l'espace entre le ChartView et sa plotArea (c'est donc plutôt un padding...) right: 50 bottom: 10 left: 0 top: 20 } width: parent.width backgroundColor: "transparent" legend { visible: false } ValueAxis { id: axisX min: xMin // Défini pendant l'init max: xMax // Idem tickCount: nb_intervalles_graphique + 1 // Il faut N+1 pour avoir N intervalles labelFormat: "%." + precision_x_graphique + "f" labelsFont:Qt.font({pointSize: 10}) color: couleur_gris_moyen shadesVisible: false gridVisible: true gridLineColor: couleur_gris_tres_fonce labelsColor: couleur_gris_tres_tres_pale titleText: "" + unite_x + "" } ValueAxis { id: axisY min: yMin // Défini pendant l'init max: yMax // idem tickCount: 11 labelFormat: "%.0f" labelsFont:Qt.font({pointSize: 10}) color: couleur_gris_moyen shadesColor: courbes_couleur_shades_fond shadesVisible: true gridVisible: true gridLineColor: couleur_gris_tres_fonce labelsColor: couleur_gris_tres_tres_pale titleText: "%" } LineSeries { id: courbe // Courbe fin bleue représentant la courbe finale (elle suit donc les modifications au fur et à mesure) axisX: axisX axisY: axisY capStyle: Qt.RoundCap style: Qt.SolidLine width: courbes_epaisseur_bordure // On reprend les valeurs standard des courbes de ShipHeart color: shipheart_couleur_bordure_courbes // Idem pour la couleur } ScatterSeries { id: points_originaux // Uniquement les points. De plus, ils ne bougent pas au fur et à mesure et représentent donc la version initiale. axisX: axisX axisY: axisY } ScatterSeries { id: points_nouveaux // Uniquement les points. Mais ceux-ci changent avec les modifications. Ils suivent donc la 'courbe' et sont utilisés juste pour la renforcer. axisX: axisX axisY: axisY borderColor: couleur_rouge borderWidth: 3 markerSize: 4 markerShape: ScatterSeries.MarkerShapeRectangle } LineSeries { id: croix_h // Trace horizontale de la croix de valeur actuelle axisX: axisX axisY: axisY style: Qt.DashLine width: 1 color: couleur_jaune_post_it visible: !etat_entrees && !courbe_flag_vide } LineSeries { id: croix_v // Trace verticale de la croix de valeur actuelle axisX: axisX axisY: axisY style: Qt.DashDotLine width: 1 color: couleur_jaune_post_it visible: !etat_entrees && !courbe_flag_vide } Text { // Contient la valeur actuelle sur l'axe X id: texte_valeur_x anchors.top: parent.top font.pixelSize: 18 font.weight: Font.Bold color: !flag_yMax_hors_limite ? couleur_jaune_post_it : couleur_rouge visible: !etat_entrees && !courbe_flag_vide } Text { // Contient la valeur actuelle sur l'axe Y id: texte_valeur_y anchors.right: parent.right font.pixelSize: 18 font.weight: Font.Bold color: couleur_jaune_post_it visible: !etat_entrees && !courbe_flag_vide } Rectangle { id: rectangle_remplissage x: graphique.plotArea.x width: graphique.plotArea.width color: couleur_bleu_koriolan_transparent visible: !etat_entrees && !courbe_flag_vide } // Ajout des points dragables : // On utilise un répéteur qui se base sur le même modèle que l'écran de gauche. // Chaque délégué est un rectangle retaillé en cercle (!) sur lequel on superpose un mouseArea. // C'est ce MouseArea qui est dragable avec comme target le rectangle/cercle sur lequel il est. // Quand on bouge la Mouserarea, elle bouge le rectangle/cercle. Et quand on détecte un changement // de coordonnées du rectangle/cercle, on lance une fonction de mise à jour du modèle de 'courbe' et 'points_nouveaux' // qui sont donc mis à jour en temps réel. // En plus, dès qu'on touche l'une des MouseArea, on met en actif le bouton correspondant de l'écran de gauche via OnPressed de MouseArea. Repeater { model: modele_points Rectangle { id: point_deplacable property bool flag_init_point_deplacable: flag_init // Recopie au niveau du delegate du flag général pour pouvoir utiliser onFlag_xxxChanged et déclencher l'appel à une fonction JS une seule fois en fin d'init property bool flag_align_point_deplacable: flag_alignement_demande // Idem lorsqu'une mise à jour directe des coordonnées a été fait et qu'il fait aligner le rectangle/cerlce sur ces nouvelles coordonnées. radius: diametre_points_deplacables // Défini dans l'entete width: radius / 2 // Rectangle => devient cercle height: width border { width: 5 color: (!flag_valeurs_discretes || (etat_entrees === 0)) ? "transparent" : (modele_points.get(index).etat_entree_discrete ? couleur_bleu_koriolan : couleur_bleu_koriolan_tres_tres_transparent) } color: modele_points.get(index).actif ? couleur_bleu_koriolan_transparent : couleur_bleu_koriolan_tres_tres_transparent Text { id: libelle_point // N° du point dans le modèle (qui est commun à l'écran de gauche et sur les points déplaçables anchors.top: parent.top anchors.left: parent.left text: (index + 1) font.pixelSize: 20 font.weight: Font.Bold color: couleur_gris_tres_pale } onXChanged: { // Si le rectangle/cercle a bougé (avec le doigt), on met à jour les valeurs de 'courbe', 'points_nouveaux' et 'modèle_points' (ce dernier correspondant à la liste gauche) if(mouse_area_point_deplacable.drag.active) { point_deplacable.adjustValue(this, index) } } onYChanged: { // Iem pour l'axe Y if(mouse_area_point_deplacable.drag.active) { point_deplacable.adjustValue(this, index) } } MouseArea { id: mouse_area_point_deplacable // Zone dragable enabled: !flag_affichage_reglage_fin || (index === point_en_cours) // On dévalide les zones dragables des autres points lorsqu'on est en mode 'réglage fin' car sinon ils interceptent les clicks sur les flèches de direction (pas trouvé comment gérer cela avec le paramètre Z...) anchors.fill: parent drag.target: point_deplacable drag.axis: flag_valeurs_discretes ? Drag.YAxis : Drag.XAndYAxis // Limitation des mouvements à la zone du graphique. Les valeurs de Min et Max du graphique sont donc importantes car on ne pourra pas aller au-delà... // Pour les mouvements X, on limite en plus aux positions X entre le point précédent et le point suivant (cela permet de n'avoir jamais une courbe qui "revient" sur elle même). /* drag.minimumX: Math.max( graphique.plotArea.x , Math.min( ((index > 0) ? (graphique.mapToPosition(courbe.at(index-1), courbe).x) : (graphique.plotArea.x + graphique.plotArea.width)), ((index < modele_points.count) ? (graphique.mapToPosition(courbe.at(index+1), courbe).x) : (graphique.plotArea.x + graphique.plotArea.width))) ) - (point_deplacable.width / 2) */ drag.minimumX: graphique.plotArea.x - (point_deplacable.width / 2) /* drag.maximumX: Math.min( (graphique.plotArea.x + graphique.plotArea.width) , Math.max( ((index > 0) ? (graphique.mapToPosition(courbe.at(index-1), courbe).x) : (graphique.plotArea.x)), ((index < modele_points.count) ? (graphique.mapToPosition(courbe.at(index+1), courbe).x) : (graphique.plotArea.x))) ) - (point_deplacable.width / 2) */ drag.maximumX: graphique.plotArea.x + graphique.plotArea.width - (point_deplacable.width / 2) drag.minimumY: graphique.plotArea.y - (point_deplacable.height / 2) // drag.maximumY: graphique.plotArea.y + graphique.plotArea.height - (point_deplacable.height / 2) // importantes car on ne pourra pas aller au-delà... onClicked: { if (point_en_cours !== -1) modele_points.setProperty(point_en_cours, "actif", false); modele_points.setProperty(index, "actif", true); point_en_cours = index; liste_points.currentIndex = index // Permet de ramener le bouton correspondant dans la zone visible du ListView de gauche } } onFlag_init_point_deplacableChanged: { adjustPosition(this, index) // Activé en fin d'init : Positionne le rectangle/cercle sur la valeur initiale du modèle. } onFlag_align_point_deplacableChanged: { if (flag_alignement_demande) // Permet de limiter le traitement au front montant de 'flag_alignement_demande' { // PS : ce traitement est fait pour tous les points de repeater, même si la plupart du temps, un seul point a été modifié... adjustPosition(this, index) // Ajuste la position du rectangle/cercle selon les coordonnées présentes dans 'modele_points' } } function adjustPosition(item, index) { let point = Qt.point(modele_points.get(index).x, modele_points.get(index).y) let position = graphique.mapToPosition(point, courbe) item.x = position.x - item.width / 2 item.y = position.y - item.height / 2 } function adjustValue(item, index) { let position = Qt.point(item.x + item.width / 2, item.y + item.height / 2) let point = graphique.mapToValue(position, courbe) modele_points.setProperty(index, "x", point.x) modele_points.setProperty(index, "y", point.y) modele_points.setProperty(index, "changed", true) courbe.replace(courbe.at(index).x, courbe.at(index).y, point.x, point.y) points_nouveaux.replace(points_nouveaux.at(index).x, points_nouveaux.at(index).y, point.x, point.y) flag_nouvelles_valeurs = true; } //------------------------------------------------------------ // Flèches de réglage fin : Rectangle { // Flèche vers le haut : id: bouton_reglage_fin_haut width: (diametre_points_deplacables / 2) height: (diametre_points_deplacables / 4) y: -(diametre_points_deplacables / 4) color: "transparent" visible: flag_affichage_reglage_fin && (index === point_en_cours) Image { anchors { horizontalCenter: parent.horizontalCenter verticalCenter: parent.verticalCenter } source: "/images/fleche_suite_haut.png" width: parent.width * coeff_fleche_reglage_fin height: parent.height * coeff_fleche_reglage_fin opacity: 0.5 } MouseArea { id: mouse_area_reglage_fin_haut anchors.fill: parent onClicked: { var new_val = modele_points.get(point_en_cours).y + step_reglage_fin_vertical; if (new_val > yMax) new_val = yMax; modele_points.setProperty(point_en_cours, "y", new_val); modele_points.setProperty(point_en_cours, "changed", true); courbe.replace(courbe.at(point_en_cours).x, courbe.at(point_en_cours).y, modele_points.get(point_en_cours).x, modele_points.get(point_en_cours).y); points_nouveaux.replace(points_nouveaux.at(point_en_cours).x, points_nouveaux.at(point_en_cours).y, modele_points.get(point_en_cours).x, modele_points.get(point_en_cours).y); point_deplacable.y = graphique.plotArea.y + (graphique.plotArea.height * (100 - modele_points.get(point_en_cours).y) / 100) - (diametre_points_deplacables / 4); flag_nouvelles_valeurs = true; } } } Rectangle { // Flèche vers le bas : id: bouton_reglage_fin_bas width: (diametre_points_deplacables / 2) height: (diametre_points_deplacables / 4) y: (diametre_points_deplacables / 2) color: "transparent" visible: flag_affichage_reglage_fin && (index === point_en_cours) Image { anchors { horizontalCenter: parent.horizontalCenter verticalCenter: parent.verticalCenter } source: "/images/fleche_suite_bas.png" width: parent.width * coeff_fleche_reglage_fin height: parent.height * coeff_fleche_reglage_fin opacity: 0.5 } MouseArea { id: mouse_area_reglage_fin_bas anchors.fill: parent onClicked: { var new_val = modele_points.get(point_en_cours).y - step_reglage_fin_vertical; if (new_val < yMin) new_val = yMin; modele_points.setProperty(point_en_cours, "y", new_val) modele_points.setProperty(point_en_cours, "changed", true) courbe.replace(courbe.at(point_en_cours).x, courbe.at(point_en_cours).y, modele_points.get(point_en_cours).x, modele_points.get(point_en_cours).y) points_nouveaux.replace(points_nouveaux.at(point_en_cours).x, points_nouveaux.at(point_en_cours).y, modele_points.get(point_en_cours).x, modele_points.get(point_en_cours).y) point_deplacable.y = graphique.plotArea.y + (graphique.plotArea.height * (100 - modele_points.get(point_en_cours).y) / 100) - (diametre_points_deplacables / 4) flag_nouvelles_valeurs = true; } } } Rectangle { // Flèche vers la gauche : id: bouton_reglage_fin_gauche width: (diametre_points_deplacables / 4) height: (diametre_points_deplacables / 2) x: -(diametre_points_deplacables / 4) color: "transparent" visible: flag_affichage_reglage_fin && (index === point_en_cours) && !flag_valeurs_discretes Image { anchors { horizontalCenter: parent.horizontalCenter verticalCenter: parent.verticalCenter } source: "/images/fleche_suite_gauche.png" width: parent.width * coeff_fleche_reglage_fin height: parent.height * coeff_fleche_reglage_fin opacity: 0.5 } MouseArea { id: mouse_area_reglage_fin_gauche anchors.fill: parent onClicked: { var new_val = modele_points.get(point_en_cours).x - step_reglage_fin_horizontal; if (new_val < xMin) new_val = xMin; modele_points.setProperty(point_en_cours, "x", new_val) modele_points.setProperty(point_en_cours, "changed", true) courbe.replace(courbe.at(point_en_cours).x, courbe.at(point_en_cours).y, modele_points.get(point_en_cours).x, modele_points.get(point_en_cours).y) points_nouveaux.replace(points_nouveaux.at(point_en_cours).x, points_nouveaux.at(point_en_cours).y, modele_points.get(point_en_cours).x, modele_points.get(point_en_cours).y) point_deplacable.x = graphique.plotArea.x + (graphique.plotArea.width * (modele_points.get(point_en_cours).x - xMin) / (xMax - xMin)) - (diametre_points_deplacables / 4) flag_nouvelles_valeurs = true; } } } Rectangle { // Flèche vers la droite : id: bouton_reglage_fin_droite width: (diametre_points_deplacables / 4) height: (diametre_points_deplacables / 2) x: (diametre_points_deplacables / 2) color: "transparent" visible: flag_affichage_reglage_fin && (index === point_en_cours) && !flag_valeurs_discretes Image { anchors { horizontalCenter: parent.horizontalCenter verticalCenter: parent.verticalCenter } source: "/images/fleche_suite_droite.png" width: parent.width * coeff_fleche_reglage_fin height: parent.height * coeff_fleche_reglage_fin opacity: 0.5 } MouseArea { id: mouse_area_reglage_fin_droite anchors.fill: parent onClicked: { var new_val = modele_points.get(point_en_cours).x + step_reglage_fin_horizontal; if (new_val > xMax) new_val = xMax; modele_points.setProperty(point_en_cours, "x", new_val) modele_points.setProperty(point_en_cours, "changed", true) courbe.replace(courbe.at(point_en_cours).x, courbe.at(point_en_cours).y, modele_points.get(point_en_cours).x, modele_points.get(point_en_cours).y) points_nouveaux.replace(points_nouveaux.at(point_en_cours).x, points_nouveaux.at(point_en_cours).y, modele_points.get(point_en_cours).x, modele_points.get(point_en_cours).y) point_deplacable.x = graphique.plotArea.x + (graphique.plotArea.width * (modele_points.get(point_en_cours).x - xMin) / (xMax - xMin)) - (diametre_points_deplacables / 4) flag_nouvelles_valeurs = true; } } } } } Text { id: texte_etat_entrees_invalide // Utilisé si aucune valeur n'est dispo, ce qui ne doit pas arriver trop souvent.... anchors.verticalCenter: parent.verticalCenter anchors.horizontalCenter: parent.horizontalCenter width: graphique.plotArea.width text: 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. ") font.pixelSize: 20 wrapMode: Text.Wrap horizontalAlignment: Text.AlignHCenter font.weight: Font.Bold color: couleur_rouge visible: etat_entrees } Text { id: texte_courbe_vide // Utilisé si aucune valeur n'est dispo, ce qui ne doit pas arriver trop souvent.... anchors.verticalCenter: parent.verticalCenter anchors.horizontalCenter: parent.horizontalCenter text: qsTr("Aucune donnée n'est disponible...") font.pixelSize: 20 font.weight: Font.Bold color: couleur_rouge visible: courbe_flag_vide } } // Fin du graphique } //------------------------------------------------------- // 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(); if (flag_nouvelles_valeurs) // Si on demande le retour alors que des points ont été modifiés et pas enregistrés : { boite_validation_sortie.imageTitreSource = "/images/attention.png"; boite_validation_sortie.message = qsTr("Vous avez effectué des modifications qui n'ont pes été enregistrées
Cliquer sur Annuler pour revenir en arrière et les enregistrer.
Cliquer sur OK pour sortir sans enregistrer.") boite_validation_sortie.open(); } else stackView.pop() // Si retour alors qu'il n'y a aucun point non enregistrés, on sort sans rien demander. } } } } //------------------------------------------------------- // Boite de validation : // NB : il est important que ce composant soit directement // rattaché à la page pour qu'elle s'affiche en plein écran. //------------------------------------------------------- Shipheart_boite_dialogue { id: boite_validation_sortie niveau: 1 boutonAnnuler: true onClicked: { switch(bouton) { case shipheart_boite_dialogue_bouton_1: // Si Ok, on sort sans chercher plus loin stackView.pop() break; case shipheart_boite_dialogue_bouton_2: // Si Annuler, on revient sur la page pour que l'on puisse enregistrer. break; default: } } } //------------------------------------------------------- // Boite d'e validation'info sur nombre max de points atteint : // NB : il est important que ce composant soit directement // rattaché à la page pour qu'elle s'affiche en plein écran. //------------------------------------------------------- Shipheart_boite_dialogue { id: boite_nombre_max_points_atteint niveau: 0 onClicked: { switch(bouton) { case shipheart_boite_dialogue_bouton_1: // Si Ok break; case shipheart_boite_dialogue_bouton_2: // Si Annuler break; default: } } } // Note : il est inutile de prévoir un Mousearea pour sortir de l'économiseur sur cette page puisqu'il // est systématiquement bloqué pour toutes les pages lancées depuis 'Paramètres Avancés'. //============================================================================================================= // Init de la page //============================================================================================================= function init_page() { var req, res; var i; var nb; var champs; var nom, liste; var chaines_entrees; flag_nouvelles_valeurs = false; // Paramètres spécifiques AVANT init : switch(table) { case "TB_Batteries": champ_liste = "Liste_tension_soc"; champs = "Nom , Tension_nominale, " + champ_liste; break; case "TB_Reservoirs": champ_liste = "Liste_valeurs_niveaux"; champs = "Nom , Type_entrees, Entrees, Mode_entrees, Type_sonde_entrees, Capacite, " + champ_liste; break; default: console.log("/!\\ Le type de table demandé n'est pas connu. La page est refermée..."); stackView.pop(); // Il est inutile de rester si le type d'objet n'est pas connu... break; } i = 0; // Récupération des infos correspondant à l'élément concerné : req = 'SELECT ' + champs + ' FROM ' + table + ' WHERE Cle_primaire=' + id; //console.log("Requête = [ " + req + " ]"); res = bdd.requete_unique(req); // Récupération des données selon le type d'objet : switch(table) { case "TB_Batteries": nom = res[i++]; tension_nominale = Number(res[i++]); liste = res[i++]; flag_valeurs_discretes = false; break; case "TB_Reservoirs": nom = res[i++]; type_entrees_reservoir = Number(res[i++]); chaines_entrees = res[i++].split(';'); mode_reservoir = Number(res[i++]); sous_mode_reservoir = Number(res[i++]); capacite = Number(res[i++]); liste = res[i++]; if (type_entrees_reservoir === shipheart_reservoir_type_entrees_aux) flag_valeurs_discretes = true; else flag_valeurs_discretes = false; break; // Pas de défaut car on a purgé ce cas au switch précédent... } // Remplissage des courbes et modèles : if (liste !== "") creation_courbes(liste.split(';')); else creation_courbes(""); // L'appel avec un array vide (cas du résultat de split sur une chaine vide) donne des résultats pas très clairs... // Sélection du premier point à l'ouverture : if (!courbe_flag_vide) { modele_points.setProperty(0, "actif", true); point_en_cours = 0; } // Paramètres spécifiques APRES init : switch(table) { case "TB_Batteries": texte_aucun_element = qsTr("Aucun point n'est actuellement configuré pour cette batterie...") texte_titre = qsTr("Courbe de conversion de [%1]").arg(nom) // Valeurs de min et de max du graphique : // Attention : ces valeurs sont utilisées pour limiter la zone dragable ! // Donc les valeurs au-delà de ces min/max ne sont pas atteignables...!! switch(tension_nominale) { default: case 12: xMin = 9.5; xMax = 14.5; break; case 24: xMin = 19; xMax = 29; break; case 48: xMin = 40; xMax = 56; break; } yMin = 0; yMax = 100; unite_x = "Volts"; precision_x = 2; precision_x_graphique = 2; step_reglage_fin_vertical = 0.1; // En %, donc 0.1% step_reglage_fin_horizontal = 0.01; // En V, donc 10mV break; case "TB_Reservoirs": texte_aucun_element = qsTr("Aucun réservoir n'est actuellement configuré...") texte_titre = qsTr("Courbe de conversion de [%1]").arg(nom) // Valeurs de min et de max du graphique : // Attention : ces valeurs sont utilisées pour limiter la zone dragable ! // Donc les valeurs au-delà de ces min/max ne sont pas atteignables...!! switch(type_entrees_reservoir) { case shipheart_reservoir_type_entrees_aux : xMin = 0; xMax = 0; for (i = 0; i < shipheart_reservoir_max_nombre_entrees_aux; i ++) { if (chaines_entrees[i]) xMax ++; else break; } mode_reservoir = xMax; // Est utilisé pouer appeler 'Fonctions_reservoirs.chaine_valeur_niveau_reservoir_usine()' sous_mode_reservoir = 0; nb_intervalles_graphique = xMax; // Permet d'ajuster le nombre d'intervalles au nombre d'entrées utilisé dans ce cas unite_x = ""; precision_x = 0; precision_x_graphique = 0; step_reglage_fin_vertical = (0.1) * 100 / capacite; step_reglage_fin_horizontal = 0.1; // Non utilisé avec ce type de sondes... break; case shipheart_reservoir_type_entrees_level_sensor: switch (mode_reservoir) // Mode : { case shipheart_reservoir_mode_entrees_level_sensor_0_10v: // 0-10V sous_mode_reservoir = 0; xMin = 0.0; xMax = 12.0; unite_x = "Volts"; precision_x = 2; precision_x_graphique = 1; step_reglage_fin_vertical = (0.1) * 100 / capacite; step_reglage_fin_horizontal = 0.01; break; case shipheart_reservoir_mode_entrees_level_sensor_500r: // 0-500R switch(sous_mode_reservoir) // Sous-mode : { case shipheart_reservoir_mode_entrees_level_sensor_500r_0_190r: // 0-190R xMin = 0; xMax = 200; break; case shipheart_reservoir_mode_entrees_level_sensor_500r_240_30r: // 240-30R xMin = 0; xMax = 260; break; case shipheart_reservoir_mode_entrees_level_sensor_500r_0_500r: // 0-500R default: xMin = 0; xMax = 700; break; } unite_x = String.fromCharCode(unicode_omega); //"Ohms"; precision_x = 1; precision_x_graphique = 0; step_reglage_fin_vertical = (0.1) * 100 / capacite; step_reglage_fin_horizontal = 0.1; break; case shipheart_reservoir_mode_entrees_level_sensor_4_20ma: // 4-20mA sous_mode_reservoir = 0; xMin = 0; xMax = 22.0; unite_x = "mA"; precision_x = 1; precision_x_graphique = 1; step_reglage_fin_vertical = (0.1) * 100 / capacite; step_reglage_fin_horizontal = 0.1; break; } break; default: console.log("/!\\ Le type d'entrée est incorrect. La page est refermée...") stackView.pop(); // Il est inutile de rester si le type d'objet n'est pas connu... break; } yMin = 0; yMax = 100; break; default: texte_aucun_element = qsTr("Aucun point n'est actuellement configuré pour cet élément...") texte_titre = qsTr("Courbe de conversion de type INCONNU") } // Mise à jour des données actuelles (forcément en fin de traitement car nécessite de connaitre les min et max) : mise_a_jour_valeur_actuelle(); flag_init = false; // Indique que l'init est terminée } //============================================================================================================= // Met à jour 'modele_points', 'courbe' et 'points_nouveaux' avec les paramètres contenu dans 'chaines_points' //============================================================================================================= function creation_courbes(chaines_points) { var i; var chaine_point; var point; var x,y; // RAZ des courbes : nb_points = 0; modele_points.clear(); courbe.clear(); points_originaux.clear(); points_nouveaux.clear(); courbe_flag_vide = true; point_en_cours = -1; point_a_supprimer = -1; if (chaines_points === "") { console.log("La liste de points est vide") return; // Si le tableau est vide (aucun point), il ne faut pas faire de traitement supplémentaire... } // Ajout des points dans les modèles : i = 0; for (chaine_point of chaines_points) { point = chaine_point.split('/'); x = Number(point[0]); y = Number(point[1]); if (flag_valeurs_discretes) x = i ++; modele_points.append({ "x": x, // Modèle pour la liste de gauche + les rectangle/cercles utilisés pour les bouger "y": y, "actif": false, "etat_entree_discrete": false, "changed": false}); courbe.append(x, y); // Courbe linéaire bleue points_originaux.append(x, y); // Points d'origine (qui ne bougeront plus) points_nouveaux.append(x, y); // Points nouveaux (qui bougeront dès qu'ils sont dragués) courbe_flag_vide = false; // Utilisé pour afficher le message "aucune données...' if (++nb_points >= courbe_etalonnage_max_points) { flag_nombre_max_points_atteint = true; break; } } flag_alignement_demande = true // Permet de déclencher un traitement au nivea du repeater pour mettre à jour la position du rectangle/cercle. flag_alignement_demande = false // La RAZ ne peut pas se faire dans le repeater. Donc on la fait ici car le front montant a déjà été détecté par le signe de changement. } //============================================================================================================= // Met à jour de la valeur actuelle (notamment, la croix H et V) //============================================================================================================= function mise_a_jour_valeur_actuelle() { var req, res; var champs; var val_x, val_y; var valeurs_brutes, valeur_brute; var i; // RAZ des deux modèles : croix_h.clear(); croix_v.clear(); switch(table) { //------------------------------- case "TB_Batteries": req = 'SELECT Tension, Soc FROM TB_Batteries WHERE Cle_primaire=' + id; //console.log("Requête = [ " + req + " ]"); res = bdd.requete_unique(req); val_x = Number(res[0]); val_y = Number(res[1]); texte_valeur_x.text = val_x.toFixed(precision_x) + " " + unite_x; texte_valeur_y.text = val_y.toFixed(0) + "%"; break; //------------------------------- case "TB_Reservoirs": req = 'SELECT Capacite, Valeur_brute, Niveau, Etat_entrees FROM TB_Reservoirs WHERE Cle_primaire=' + id; //console.log("Requête = [ " + req + " ]"); res = bdd.requete_unique(req); val_x = Number(res[1]); val_y = Number(res[2]); etat_entrees = Number(res[3]); // Mise à jour de l'état de chaque entrée AUX dans le cas d'électrodes plongées : switch(type_entrees_reservoir) { case shipheart_reservoir_type_entrees_aux : // Mise à jour de l'état réel de chaque entrée AUX : var masque = 1; modele_points.setProperty(0, "etat_entree_discrete", false); // La valeur '0' est toujours inactive (car c'est un état par absence des autres entrées) for (i = 1; i < nb_points; i++) { if ((val_x & masque) == 0) modele_points.setProperty(i, "etat_entree_discrete", false); else modele_points.setProperty(i, "etat_entree_discrete", true); masque <<= 1; } // Recalcul de val_x pour le ramener à des puissances de 2 : val_x = Math.log2(Number(res[1]) + 1); // Les valeurs de 0 à 15 par exemple, deviennent 1 à 16 donc 0-4 pour le log2(). NB : si la valeur est incohérente, le résultat ne sera pas entier mais cela ne pose pas de pb puisqu'il n'est pas affiché dans ce cas. break; case shipheart_reservoir_type_entrees_level_sensor: default: break; } // Texte de la valeur X : if (val_x > xMax) // Si la valeur est hors de l'intervalle affiché (notamment, une sonde résistive non branchée...) { texte_valeur_x.text = "[" + String.fromCharCode(unicode_fleche_demi_epaisse_discontinue_droite) + " " + val_x.toFixed(precision_x) + " " + unite_x + "]"; // La valeur est affichée entre [] val_x = xMax; // On cape la position en bout de graphique flag_yMax_hors_limite = true; } else { texte_valeur_x.text = val_x.toFixed(precision_x) + " " + unite_x; // Affichage normal flag_yMax_hors_limite = false; } // Texte de la valeur Y mis en forme avec conversion d'unité si nécessaire : texte_valeur_y.text = val_y.toFixed(0) + "%
" + Fonctions.conversion_volume((val_y * Number(res[0]) / 100), shipheart_unite_volume, shipheart_unite_volume_symbole, 0); break; default: // Ne devrait pas arriver car déjà testé à l'init... break; } // Points de la croix de valeur actuelle : croix_h.append(xMin, val_y); croix_h.append(xMax, val_y); croix_v.append(val_x, yMin); croix_v.append(val_x, yMax); // Positionnement des textes X et Y surle graphique (alignés sur la croix) : texte_valeur_x.leftPadding = graphique.mapToPosition(croix_v.at(0), croix_v).x - (texte_valeur_x.contentWidth / 2); texte_valeur_y.topPadding = graphique.mapToPosition(croix_h.at(0), croix_h).y - (texte_valeur_y.contentHeight / 2); // Paramètres de hauteur et position du rectangle de remplissage : rectangle_remplissage.height = graphique.plotArea.height * val_y / 100; rectangle_remplissage.y = graphique.plotArea.y + (graphique.plotArea.height * (100 - val_y) / 100) } //============================================================================================================= // Met à jour 'modele_points', 'courbe' et 'points_nouveaux' avec les paramètres usines. // NB : on ne met PAS à jour la BDD avec ces valeurs. Il faudra ensuite cliquer sur Enregistrer pour que ce soit fait. //============================================================================================================= function courbe_usine() { var i; var chaines_points; switch(table) { case "TB_Batteries": chaines_points = Fonctions_batteries.chaine_tension_niveau_batterie_usine(tension_nominale).split(';'); break; case "TB_Reservoirs": chaines_points = Fonctions_reservoirs.chaine_valeur_niveau_reservoir_usine(type_entrees_reservoir, mode_reservoir, sous_mode_reservoir).split(';'); break; default : return; } flag_nouvelles_valeurs = true; // En pratique, on n'est pas sûr que les valeurs seront différentes après la config usine c'est le principe de précaution... // Remplissage des courbes et modèles : creation_courbes(chaines_points); // Sélection du premier point à l'ouverture : if (!courbe_flag_vide) { modele_points.setProperty(0, "actif", true); point_en_cours = 0; } flag_affichage_reglage_fin = false; } //============================================================================================================= // Ajoute un point à la courbe existante //============================================================================================================= function ajouter_point() { var x, y; var nb; if (point_en_cours === (modele_points.count - 1)) // Cas où on est sur le dernier point { if (point_en_cours > 0) { modele_points.setProperty(point_en_cours, "actif", false); // Dévalidation du dernier point (le nouveau point créé sera validé après sa création) point_en_cours --; // S'il y a au moins 2 points, on se met sur le point précédent le dernier. } else return; // Ne doit pas arriver car on interdit la suppression dès qu'il ne rest plus que 2 points... } x = modele_points.get(point_en_cours).x + ((modele_points.get(point_en_cours + 1).x - modele_points.get(point_en_cours).x) / 2); y = modele_points.get(point_en_cours).y + ((modele_points.get(point_en_cours + 1).y - modele_points.get(point_en_cours).y) / 2); modele_points.insert(point_en_cours + 1, { "x": x, // Modèle pour la liste de gauche + les rectangle/cercles utilisés pour les bouger "y": y, "actif": true, "etat_entree_discrete": false, "changed": true}); courbe.insert((point_en_cours + 1), x, y); // Courbe linéaire bleue points_originaux.insert((point_en_cours + 1), x, y); // Points d'origine (qui ne bougeront plus) points_nouveaux.insert((point_en_cours + 1), x, y); // Points nouveaux (qui bougeront dès qu'ils sont dragués) modele_points.setProperty(point_en_cours, "actif", false); point_en_cours ++; // On se positionne sur le nouveau point flag_nouvelles_valeurs = true; // Le nouveau point, à ce stade, n'est pas enregistré ! if (++nb_points >= courbe_etalonnage_max_points) flag_nombre_max_points_atteint = true; courbe_flag_vide = false; // Utilisé pour afficher le message "aucune données...' flag_alignement_demande = true // Permet de déclencher un traitement au nivea du repeater pour mettre à jour la position du rectangle/cercle. flag_alignement_demande = false // La RAZ ne peut pas se faire dans le repeater. Donc on la fait ici car le front montant a déjà été détecté par le signe de changement. } //============================================================================================================= // Supprime le point de la courbe correspondant à l'index passé en paramètre //============================================================================================================= function supprimer_point() { var x, y; var nb; flag_init = true; if (point_a_supprimer === -1) return; modele_points.remove(point_a_supprimer, 1); // Modèle pour la liste de gauche + les rectangle/cercles utilisés pour les bouger courbe.remove(point_a_supprimer); // Courbe linéaire bleue points_originaux.remove(point_a_supprimer); // Points d'origine (qui ne bougeront plus) points_nouveaux.remove(point_a_supprimer); // Points nouveaux (qui bougeront dès qu'ils sont dragués) if (--nb_points < courbe_etalonnage_max_points) flag_nombre_max_points_atteint = false; // Maj du 'nb_points' et réaffichage du bouton '+' si on est passé en-dessous du max. if (nb_points < 1) { nb_points = 0; // Ne devrait pas être utile mais par précaution... courbe_flag_vide = true; // Utilisé pour afficher le message "aucune données...' } flag_nouvelles_valeurs = true; // Donne accès au bouton d'enregistrement et permet d'alerter si tentative de sortie sans avoir enregistré. flag_alignement_demande = true // Permet de déclencher un traitement au nivea du repeater pour mettre à jour la position du rectangle/cercle. flag_alignement_demande = false // La RAZ ne peut pas se faire dans le repeater. Donc on la fait ici car le front montant a déjà été détecté par le signe de changement. flag_affichage_reglage_fin = false; // Mise à jour du point en cours afin qu'il y en ait toujours un d'actif : if (point_a_supprimer >= 1) point_en_cours = point_a_supprimer - 1; else point_en_cours = 0; modele_points.setProperty(point_en_cours, "actif", true); // On sait que le 'point_en_cours' AVANT la suppression est celui qui a été supprimé ; il n'est donc pas utile d'essayer de le rendre inactif. point_a_supprimer = -1; flag_init = false; } //============================================================================================================= // Enregistre dans la BDD les nouvelles valeurs //============================================================================================================= function enregistrement() { var i; var req; var chaine = ""; var val, niv; // Génération de la chaine de valeurs/niveaux (exemple : "13800/10000;13400/9800;13150/9600;13000/9400;12900/9000;12800/8000;12443/4887;12100/2000;11800/1000;11500/500;11000/200;10500/0") for(i = 0; i < modele_points.count; i++) { if (chaine !== "") chaine += ";"; if (flag_valeurs_discretes) val = Math.pow(2, modele_points.get(i).x) - 1; else val = modele_points.get(i).x.toFixed(3); niv= modele_points.get(i).y.toFixed(2); chaine += val + "/" + niv; modele_points.setProperty(i, "changed", false) // On en profite pour RAZ tous les flags Changed points_originaux.replace(points_originaux.at(i).x, points_originaux.at(i).y, modele_points.get(i).x, modele_points.get(i).y) points_nouveaux.replace(points_nouveaux.at(i).x, points_nouveaux.at(i).y, modele_points.get(i).x, modele_points.get(i).y) } flag_nouvelles_valeurs = false; // ainsi que le flag de nouvelles valeurs //console.log("Chaine valeurs/niveaux = [" + chaine + " ]") flag_affichage_reglage_fin = false; // Mise à jour de l'entrée dans la table : req = 'UPDATE ' + table + ' SET ' + champ_liste + '="' + chaine + '" WHERE Cle_primaire=' + id + ' ' //console.log("Requête = " + req) bdd.requete_unique(req); // Demande de reconfiguration système via la BDD car on a modifié des valeurs de configuration : synchro_bdd(shipheart_type_synchro_config); // NB : on ne gère pas la fin de le reconfiguration comme sur la plupart des autres pages de ce type car on ne sort pas directement de la page (après l'enregistrement, // il faut cliquer sur BACK). De plus, même si on le fait pendant la reconfiguration, clea n'empèchera pas celle-ci de se poursuivre. } }