//========================================================================================================================================================================
// 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.
}
}