Import initial du projet shipheart_cm5

This commit is contained in:
2026-08-13 09:19:38 +02:00
commit 53e47729fb
533 changed files with 127946 additions and 0 deletions
+21
View File
@@ -0,0 +1,21 @@
# Dossiers de build CMake/Qt Creator
build/
CMakeFiles/
CMakeCache.txt
cmake_install.cmake
Makefile
Testing/
.qt/
.qtc_clangd/
.qtcreator/
# Fichier de version régénéré à chaque build (cf generate_version.cmake)
_version.h
# Objets compilés
*.o
*.obj
# Fichiers de session/éditeur
*.autosave
*~
+130
View File
@@ -0,0 +1,130 @@
cmake_minimum_required(VERSION 3.16)
project(shipheart_cm5 LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
set(CMAKE_AUTOUIC ON)
find_package(Qt6 REQUIRED COMPONENTS
Quick
VirtualKeyboard
Qml
Sql
QuickControls2
Charts
LinguistTools
)
set_target_properties(Qt6::lrelease PROPERTIES
IMPORTED_LOCATION /usr/local/bin/lrelease-host
IMPORTED_LOCATION_RELEASE /usr/local/bin/lrelease-host
IMPORTED_LOCATION_NONE /usr/local/bin/lrelease-host
)
# ---------------------------------------------------------------------------
# Version dynamique, injectée dans _version.h à CHAQUE build (pas seulement
# à chaque reconfiguration CMake), au même format que le .pro d'origine
# (yymmdd_HHMMSS, ex: 260807_133409) et avec la même garantie : deux
# exécutables différents obtiennent toujours un horodatage différent.
#
# Mécanisme : une cible personnalisée sans suivi de fichier (add_custom_target
# ... ALL, sans dépendance de fichier source) est donc TOUJOURS considérée
# périmée par Ninja/Make et se ré-exécute avant chaque build, contrairement à
# string(TIMESTAMP ...) seul qui ne s'évalue qu'à la configuration du projet.
#
# NB IMPORTANT - modification de main.cpp nécessaire : ce mécanisme régénère
# le fichier source _version.h - il faut donc que main.cpp fasse
# #include "_version.h"
# (actuellement absent : VERSION n'y est utilisé que comme macro fournie par
# le compilateur). Cet ajout est neutre pour la compilation CM4/qmake (qui
# continue de fournir VERSION via DEFINES +=, sans jamais regénérer ni lire
# le contenu de _version.h).
# ---------------------------------------------------------------------------
add_custom_target(generate_version ALL
COMMAND ${CMAKE_COMMAND}
-DOUTPUT_FILE=${CMAKE_CURRENT_SOURCE_DIR}/_version.h
-P ${CMAKE_CURRENT_SOURCE_DIR}/generate_version.cmake
COMMENT "Régénération de _version.h (horodatage)"
BYPRODUCTS ${CMAKE_CURRENT_SOURCE_DIR}/_version.h
)
add_executable(shipheart_qt
backend.cpp
bdd.cpp
main.cpp
systeme.cpp
tb_mesures.cpp
# Headers listés pour visibilité dans l'IDE (non obligatoire pour la compilation) :
_version.h
backend.h
bdd.h
shipheart.h
systeme.h
tb_mesures.h
# Ressources :
qml.qrc
traductions.qrc
)
add_dependencies(shipheart_qt generate_version)
target_link_libraries(shipheart_qt PRIVATE
Qt6::Quick
Qt6::VirtualKeyboard
Qt6::Qml
Qt6::Sql
Qt6::QuickControls2
Qt6::Charts
)
# ---------------------------------------------------------------------------
# Traductions (équivalent de TRANSLATIONS + lupdate/lrelease du .pro)
# ---------------------------------------------------------------------------
set(QT_LRELEASE_EXECUTABLE /usr/local/bin/lrelease-host)
set(TS_FILES
shipheart_fr_FR.ts
shipheart_en_EN.ts
shipheart_de_DE.ts
shipheart_es_ES.ts
shipheart_pt_PT.ts
shipheart_nl_NL.ts
shipheart_it_IT.ts
shipheart_lv_LV.ts
shipheart_ca_ES.ts
)
qt_add_translations(shipheart_qt TS_FILES ${TS_FILES})
# ---------------------------------------------------------------------------
# Dispositions de clavier virtuel personnalisées (virtualkeyboard_layouts_koriolan)
#
# RÉSOLU : ces fichiers sont déjà intégralement référencés dans qml.qrc
# (confirmé), et chargés au runtime via une ressource Qt embarquée
# (qputenv("QT_VIRTUALKEYBOARD_LAYOUT_PATH", ":/virtualkeyboard_layouts_koriolan/layouts")
# dans main.cpp). Rien à ajouter ici : qml.qrc est déjà dans la liste des
# sources de add_executable() ci-dessus, donc ces fichiers sont embarqués
# dans le binaire comme sur CM4.
#
# Dette technique notée pour plus tard (hors scope de ce portage) : la
# duplication complète de tous les layouts Qt (toutes langues, tous modes)
# pour la seule modification d'une touche (Espace -> Moins) dans
# fallback/digits.qml est disproportionnée. Une solution plus ciblée
# existe probablement (héritage partiel des layouts standards), à explorer
# séparément - à tester sur CM4 ET CM5 si entreprise, code source commun.
# ---------------------------------------------------------------------------
# ---------------------------------------------------------------------------
# Installation (équivalent target.path du .pro)
# ---------------------------------------------------------------------------
install(TARGETS shipheart_qt
DESTINATION /home/koriolan/shipheart/versions/current
)
# NB : le mécanisme QMAKE_POST_LINK du .pro d'origine copiait l'exécutable
# vers un chemin LOCAL à l'ancien poste de développement Ubuntu
# (/home/koriolan/SharePoint_Koriolan_Documents/...) - obsolète dans le
# nouveau contexte (compilation croisée WSL2 + déploiement SSH piloté par
# Qt Creator). Non repris ici ; à remplacer, si un historique des versions
# compilées est toujours souhaité, par un mécanisme côté CM5 ou côté WSL2,
# à définir séparément.
+325
View File
@@ -0,0 +1,325 @@
import QtQuick 2.12
import QtQuick.Controls 2.12
Popup {
id: boite_dialogue
// Propriétés fixes :
readonly property double pourcentage_largeur_page_popup: 0.6
readonly property double pourcentage_hauteur_page_popup: 0.7
readonly property int padding_popup: 50
readonly property double pourcentage_boite_dialogue_titre: 0.25
readonly property double pourcentage_boite_dialogue_message: 1 - pourcentage_boite_dialogue_titre - pourcentage_boite_dialogue_ligne_boutons
readonly property double pourcentage_boite_dialogue_ligne_boutons: 0.15
readonly property int hauteur_progress_bar: 30
readonly property int bouton_largeur: 200
// Propriétés :
property int niveau: 0
property string message: "--"
property int type: 0 // Utilisé uniquement par la page appelante pour distinguer les différents appels dans la même page
// Propriétés alias :
property alias boutonOk: bouton_ok.visible
property alias boutonAnnuler: bouton_annuler.visible
property alias boutonPlusTard: bouton_plus_tard.visible
property alias boutonOkTexte: bouton_ok_label.text
property alias boutonAnnulerTexte: bouton_annuler_label.text
property alias boutonPlusTardTexte: bouton_plus_tard_label.text
property alias alignementMessage: texte_message.horizontalAlignment
property alias progressBarValid: progress_bar.visible
property alias progressBarValue: progress_bar.value
property alias imageTitreSource: image_titre.source
property alias affichageAscenseur: ascenseur.visible
// Signaux :
signal clicked(int bouton) // Valeur à lire lorsque le "OnClicked' est activé
x: (shipheart_largeur_page * (1 - pourcentage_largeur_page_popup)) / 2;
y: (shipheart_hauteur_page * (1 - pourcentage_hauteur_page_popup)) / 2;
width: shipheart_largeur_page * pourcentage_largeur_page_popup;
height: shipheart_hauteur_page * pourcentage_hauteur_page_popup;
padding: padding_popup
modal: true
// focus: true // SDIR # 224 : Il ne faut PAS activer le focus. Sinon, à la fermeture, le focus précédent est réactivé (comme le dernier TextField par exemple)
closePolicy: Popup.NoAutoClose //Popup.CloseOnPressOutside
background: Rectangle {
radius: 20
color: {
switch(boite_dialogue.niveau)
{
default:
case 0: color: shipheart_couleur_fond_boite_dialogue_niveau_0; break;
case 1: color: shipheart_couleur_fond_boite_dialogue_niveau_1; break;
case 2: color: shipheart_couleur_fond_boite_dialogue_niveau_2; break;
}
}
}
Column {
id: affichage_boite_dialogue
spacing: 20
//=========================================================================
// Titre :
//=========================================================================
Rectangle {
id: titre_boite_dialogue
width: boite_dialogue.width - (2 * padding_popup)
height: (boite_dialogue.height - (2 * padding_popup) - (2 * affichage_boite_dialogue.spacing)) * pourcentage_boite_dialogue_titre
color: "transparent"
Image {
id: image_titre
source: ""
sourceSize.height: parent.height
anchors {
verticalCenter: parent.verticalCenter
horizontalCenter: parent.horizontalCenter
}
visible: true
}
}
//=========================================================================
// Message :
//=========================================================================
Rectangle {
id: message_boite_dialogue
width: boite_dialogue.width - (2 * padding_popup)
height: (boite_dialogue.height - (2 * padding_popup) - (2 * affichage_boite_dialogue.spacing)) * pourcentage_boite_dialogue_message
color: "transparent"
Flickable {
anchors.fill: parent
anchors {
verticalCenter: parent.verticalCenter
}
contentWidth: texte_message.width
contentHeight: texte_message.height
flickableDirection: Flickable.VerticalFlick
clip: true
ScrollBar.vertical: ScrollBar { // Important car permet de voir que la partie affichée n'est pas complète dans le message...
id: ascenseur
policy: ScrollBar.AlwaysOn
width: 10
visible: false
}
TextEdit {
id: texte_message
width: message_boite_dialogue.width - ascenseur.width - 5 // Le '-5' permet de détacher le texte de l'ascenseur
height: Math.max(message_boite_dialogue.height, implicitHeight) // Permet d'avoir la hauteur de la zone si le texte est "court" (et donc de centrer verticalement) puis la hauteur totale si le texte est "long" (et pouvoir se déplacer vers la fin et pas centré sur le milieu !)
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
readOnly:true
wrapMode: Text.Wrap
textFormat: Text.RichText
text: boite_dialogue.message
color: {
switch(boite_dialogue.niveau)
{
default:
case 0: shipheart_couleur_texte_boite_dialogue_niveau_0; break;
case 1: shipheart_couleur_texte_boite_dialogue_niveau_1; break;
case 2: shipheart_couleur_texte_boite_dialogue_niveau_2; break;
}
}
font.pixelSize:18
}
}
}
//=========================================================================
// Barre d'avancement :
//=========================================================================
ProgressBar {
id: progress_bar
width: parent.width
visible: false // Par défaut
background: Rectangle {
implicitWidth: parent.width
implicitHeight: hauteur_progress_bar
color: "transparent"
border.color: couleur_gris_moyen
border.width: 2
radius: shipheart_rayon_bouton
Text {
text: (progress_bar.value * 100).toFixed(0) + "%"
anchors.fill: parent
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
color: {
switch(boite_dialogue.niveau)
{
default:
case 0: couleur_blanc_casse; break;
case 1: couleur_blanc_casse; break;
case 2: couleur_blanc_casse; break;
}
}
font.pixelSize:18
font.weight: Font.Bold
}
}
contentItem: Item {
implicitWidth: parent.width
implicitHeight: hauteur_progress_bar
Rectangle {
width: progress_bar.value * parent.width
height: parent.height
radius: shipheart_rayon_bouton
color: shipheart_couleur_fond_bouton_actif
}
}
}
//=========================================================================
// Boutons :
//=========================================================================
Row {
height: (boite_dialogue.height - (2 * padding_popup) - (2 * affichage_boite_dialogue.spacing)) * pourcentage_boite_dialogue_ligne_boutons
anchors.horizontalCenter: parent.horizontalCenter
spacing: 20
//--------------------------------------------------------------------
// Bouton Ok :
Rectangle {
id: bouton_ok
width: bouton_largeur
height: parent.height
anchors.verticalCenter: parent.verticalCenter
radius: shipheart_rayon_bouton
color: couleur_gris_tres_fonce
visible: true
Label {
id: bouton_ok_label
anchors.centerIn: parent
text: qsTr("OK")
color: couleur_blanc_casse
font.pixelSize:20
}
MouseArea {
anchors.fill: parent
onClicked: {
shipheart_clic();
boite_dialogue.clicked(shipheart_boite_dialogue_bouton_1);
boite_dialogue.close();
}
}
}
//--------------------------------------------------------------------
// Bouton Annuler :
Rectangle {
id: bouton_annuler
width: bouton_largeur
height: parent.height
anchors.verticalCenter: parent.verticalCenter
radius: shipheart_rayon_bouton
color: couleur_gris_tres_fonce
visible: false
Label {
id: bouton_annuler_label
anchors.centerIn: parent
text: qsTr("Annuler")
color: couleur_blanc_casse
font.pixelSize:20
}
MouseArea {
anchors.fill: parent
onClicked: {
shipheart_clic();
boite_dialogue.clicked(shipheart_boite_dialogue_bouton_2);
boite_dialogue.close();
}
}
}
//--------------------------------------------------------------------
// Bouton Plus Tard :
Rectangle {
id: bouton_plus_tard
width: bouton_largeur
height: parent.height
anchors.verticalCenter: parent.verticalCenter
radius: shipheart_rayon_bouton
color: couleur_gris_tres_fonce
visible: false
Label {
id: bouton_plus_tard_label
anchors.centerIn: parent
text: qsTr("Plus tard")
color: couleur_blanc_casse
font.pixelSize:20
}
MouseArea {
anchors.fill: parent
onClicked: {
shipheart_clic();
boite_dialogue.clicked(shipheart_boite_dialogue_bouton_3);
boite_dialogue.close();
}
}
}
}
}
enter: Transition {
ParallelAnimation {
NumberAnimation {
property: "opacity";
from: 0.0;
to: 1.0;
//easing.type: Easing.InElastic;
duration: 200
}
NumberAnimation {
property: "scale";
from: 0.0;
to: 1.0;
// easing.type: Easing.OutBack
duration: 200
}
}
}
exit: Transition {
ParallelAnimation {
NumberAnimation {
property: "opacity";
from: 1.0
to: 0.0;
duration: 200
}
NumberAnimation {
property: "scale";
from: 1.0
to: 0.0;
duration: 200
}
}
}
}
+411
View File
@@ -0,0 +1,411 @@
import QtQuick 2.12
import QtQuick.Controls 2.12
Popup {
id: boite_dialogue_action_circuit
// Propriétés fixes :
readonly property double pourcentage_largeur_page_popup: 0.8
readonly property double pourcentage_hauteur_page_popup: 0.7
readonly property int padding_popup: 20
readonly property double pourcentage_boite_dialogue_titre: 0.3
readonly property double pourcentage_boite_dialogue_message: 1 - pourcentage_boite_dialogue_titre - pourcentage_boite_dialogue_ligne_boutons
readonly property double pourcentage_boite_dialogue_ligne_boutons: 0.3
readonly property int bouton_largeur: 200
readonly property int largeur_grand_bouton_action: (largeur_petit_bouton_action * 2) + espace_boutons
readonly property int largeur_petit_bouton_action: 140
readonly property int espace_boutons: 5
// Propriétés :
property string nom_circuit: ""
property int action_en_cours: -2
property int mode_cde: 0
// Propriétés alias :
property alias boutonAnnuler: bouton_annuler.visible
property alias boutonAnnulerTexte: bouton_annuler_label.text
// Signaux :
signal clicked(int bouton, int action) // Valeur à lire lorsque le "OnClicked' est activé
x: (shipheart_largeur_page * (1 - pourcentage_largeur_page_popup)) / 2;
y: (shipheart_hauteur_page * (1 - pourcentage_hauteur_page_popup)) / 2;
width: shipheart_largeur_page * pourcentage_largeur_page_popup;
height: shipheart_hauteur_page * pourcentage_hauteur_page_popup;
padding: padding_popup
modal: true
// focus: true // SDIR # 224 : Il ne faut PAS activer le focus. Sinon, à la fermeture, le focus précédent est réactivé (comme le dernier TextField par exemple)
closePolicy: Popup.NoAutoClose //Popup.CloseOnPressOutside
background: Rectangle {
color: shipheart_couleur_fond_boite_dialogue_niveau_0
radius: 20
}
Column {
id: affichage_boite_dialogue
spacing: 20
//=========================================================================
// Titre :
//=========================================================================
Rectangle {
id: titre_boite_dialogue
width: boite_dialogue_action_circuit.width - (2 * padding_popup)
height: (boite_dialogue_action_circuit.height - (2 * padding_popup) - (2 * affichage_boite_dialogue.spacing)) * pourcentage_boite_dialogue_titre
color: "transparent"
Label {
text: nom_circuit
anchors.fill: parent
verticalAlignment: Text.AlignTop
horizontalAlignment: Text.AlignHCenter
topPadding: 20
wrapMode: Text.Wrap
color: couleur_blanc_casse
font.pixelSize:40
font.weight: Font.Bold
}
Label {
text: qsTr("Action à appliquer au circuit lorsque l'évènement se déclenche :")
anchors.fill: parent
verticalAlignment: Text.AlignBottom
horizontalAlignment: Text.AlignHCenter
bottomPadding: 20
wrapMode: Text.Wrap
color: couleur_blanc_casse
font.pixelSize:18
}
}
//=========================================================================
// Choix de l'action :
//=========================================================================
Rectangle {
width: boite_dialogue_action_circuit.width - (2 * padding_popup)
height: (boite_dialogue_action_circuit.height - (2 * padding_popup) - (2 * affichage_boite_dialogue.spacing)) * pourcentage_boite_dialogue_message
color: "transparent"
//------------------------------------
// Ligne des actions générales :
Row {
anchors {
top: parent.top
horizontalCenter: parent.horizontalCenter
}
height: (parent.height / 2) - (spacing / 2)
spacing: espace_boutons
// Bouton Aucune action :
Rectangle {
property int val: -2
width: largeur_grand_bouton_action
height: parent.height
radius: shipheart_rayon_bouton
color: (action_en_cours === val) ? couleur_gris_fonce : couleur_gris_tres_fonce
Label {
anchors.fill: parent
horizontalAlignment: Qt.AlignHCenter
verticalAlignment: Qt.AlignVCenter
text: qsTr("Aucune")
font.pixelSize: 18
color: (action_en_cours === parent.val) ? couleur_green_yellow : couleur_blanc_casse
font.weight: (action_en_cours === parent.val) ? Font.Bold : Font.Normal
}
MouseArea {
anchors.fill: parent
onClicked: {
shipheart_clic();
boite_dialogue_action_circuit.clicked(shipheart_boite_dialogue_bouton_1, parent.val);
}
}
}
// Bouton Bascule :
Rectangle {
property int val: -1
width: largeur_grand_bouton_action
height: parent.height
radius: shipheart_rayon_bouton
color: (action_en_cours === val) ? couleur_gris_fonce : couleur_gris_tres_fonce
Label {
anchors.fill: parent
horizontalAlignment: Qt.AlignHCenter
verticalAlignment: Qt.AlignVCenter
topPadding: -(parent.height * 0.3)
text: qsTr("Le circuit <b>bascule</b>")
font.pixelSize: 18
color: (action_en_cours === parent.val) ? couleur_green_yellow : couleur_blanc_casse
font.weight: (action_en_cours === parent.val) ? Font.Bold : Font.Normal
}
Label { // Complment de texte en dessous du titre de bouton proprement-dit.
anchors.fill: parent
horizontalAlignment: Qt.AlignHCenter
verticalAlignment: Qt.AlignVCenter
topPadding: (parent.height * 0.3)
text: qsTr("[passe à <b>ON</b> s'il est <b>OFF</b><br /> ou à <b>OFF</b> s'il est <b>ON</b>]")
font.pixelSize: 15
// color: (action_en_cours === parent.val) ? couleur_green_yellow : couleur_gris_pale
color: couleur_gris_pale
}
MouseArea {
anchors.fill: parent
onClicked: {
shipheart_clic();
boite_dialogue_action_circuit.clicked(shipheart_boite_dialogue_bouton_1, parent.val);
}
}
}
}
//------------------------------------
// Ligne des consignes spécifiques :
Row {
anchors {
bottom: parent.bottom
horizontalCenter: parent.horizontalCenter
}
height: (parent.height / 2) - (spacing / 2)
spacing: espace_boutons
// Bouton OFF :
Rectangle {
property int val: 0
width: (mode_cde === shipheart_mode_commande_circuit_soft_variable) ? largeur_petit_bouton_action : largeur_grand_bouton_action
height: parent.height
radius: shipheart_rayon_bouton
color: (action_en_cours === 0) ? couleur_gris_fonce : couleur_gris_tres_fonce
Label {
anchors.fill: parent
horizontalAlignment: Qt.AlignHCenter
verticalAlignment: Qt.AlignVCenter
text: qsTr("OFF")
font.pixelSize: 18
color: (action_en_cours === parent.val) ? couleur_green_yellow : couleur_blanc_casse
font.weight: (action_en_cours === parent.val) ? Font.Bold : Font.Normal
}
MouseArea {
anchors.fill: parent
onClicked: {
shipheart_clic();
boite_dialogue_action_circuit.clicked(shipheart_boite_dialogue_bouton_1, parent.val);
}
}
}
// Bouton 10% :
Rectangle {
property int val: 10
width: largeur_petit_bouton_action
height: parent.height
radius: shipheart_rayon_bouton
color: (action_en_cours === val) ? couleur_gris_fonce : couleur_gris_tres_fonce
visible: (mode_cde === shipheart_mode_commande_circuit_soft_variable)
Label {
anchors.fill: parent
horizontalAlignment: Qt.AlignHCenter
verticalAlignment: Qt.AlignVCenter
text: parent.val + "%"
font.pixelSize: 18
color: (action_en_cours === parent.val) ? couleur_green_yellow : couleur_blanc_casse
font.weight: (action_en_cours === parent.val) ? Font.Bold : Font.Normal
}
MouseArea {
anchors.fill: parent
onClicked: {
shipheart_clic();
boite_dialogue_action_circuit.clicked(shipheart_boite_dialogue_bouton_1, parent.val);
}
}
}
// Bouton 25% :
Rectangle {
property int val: 25
width: largeur_petit_bouton_action
height: parent.height
radius: shipheart_rayon_bouton
color: (action_en_cours === val) ? couleur_gris_fonce : couleur_gris_tres_fonce
visible: (mode_cde === shipheart_mode_commande_circuit_soft_variable)
Label {
anchors.fill: parent
horizontalAlignment: Qt.AlignHCenter
verticalAlignment: Qt.AlignVCenter
text: parent.val + "%"
font.pixelSize: 18
color: (action_en_cours === parent.val) ? couleur_green_yellow : couleur_blanc_casse
font.weight: (action_en_cours === parent.val) ? Font.Bold : Font.Normal
}
MouseArea {
anchors.fill: parent
onClicked: {
shipheart_clic();
boite_dialogue_action_circuit.clicked(shipheart_boite_dialogue_bouton_1, parent.val);
}
}
}
// Bouton 50% :
Rectangle {
property int val: 50
width: largeur_petit_bouton_action
height: parent.height
radius: shipheart_rayon_bouton
color: (action_en_cours === val) ? couleur_gris_fonce : couleur_gris_tres_fonce
visible: (mode_cde === shipheart_mode_commande_circuit_soft_variable)
Label {
anchors.fill: parent
horizontalAlignment: Qt.AlignHCenter
verticalAlignment: Qt.AlignVCenter
text: parent.val + "%"
font.pixelSize: 18
color: (action_en_cours === parent.val) ? couleur_green_yellow : couleur_blanc_casse
font.weight: (action_en_cours === parent.val) ? Font.Bold : Font.Normal
}
MouseArea {
anchors.fill: parent
onClicked: {
shipheart_clic();
boite_dialogue_action_circuit.clicked(shipheart_boite_dialogue_bouton_1, parent.val);
}
}
}
// Bouton 75% :
Rectangle {
property int val: 75
width: largeur_petit_bouton_action
height: parent.height
radius: shipheart_rayon_bouton
color: (action_en_cours === val) ? couleur_gris_fonce : couleur_gris_tres_fonce
visible: (mode_cde === shipheart_mode_commande_circuit_soft_variable)
Label {
anchors.fill: parent
horizontalAlignment: Qt.AlignHCenter
verticalAlignment: Qt.AlignVCenter
text: parent.val + "%"
font.pixelSize: 18
color: (action_en_cours === parent.val) ? couleur_green_yellow : couleur_blanc_casse
font.weight: (action_en_cours === parent.val) ? Font.Bold : Font.Normal
}
MouseArea {
anchors.fill: parent
onClicked: {
shipheart_clic();
boite_dialogue_action_circuit.clicked(shipheart_boite_dialogue_bouton_1, parent.val);
}
}
}
// Bouton ON :
Rectangle {
property int val: 100
width: (mode_cde === shipheart_mode_commande_circuit_soft_variable) ? largeur_petit_bouton_action : largeur_grand_bouton_action
height: parent.height
radius: shipheart_rayon_bouton
color: (action_en_cours === 100) ? couleur_gris_fonce : couleur_gris_tres_fonce
Label {
anchors.fill: parent
horizontalAlignment: Qt.AlignHCenter
verticalAlignment: Qt.AlignVCenter
// text: qsTr("ON") + ((mode_cde === shipheart_mode_commande_circuit_soft_variable) ? "<br />100%" : "")
text: qsTr("ON")
font.pixelSize: 18
color: (action_en_cours === parent.val) ? couleur_green_yellow : couleur_blanc_casse
font.weight: (action_en_cours === parent.val) ? Font.Bold : Font.Normal
}
MouseArea {
anchors.fill: parent
onClicked: {
shipheart_clic();
boite_dialogue_action_circuit.clicked(shipheart_boite_dialogue_bouton_1, parent.val);
}
}
}
}
}
//=========================================================================
// Boutons :
//=========================================================================
Row {
height: (boite_dialogue_action_circuit.height - (2 * padding_popup) - (2 * affichage_boite_dialogue.spacing)) * pourcentage_boite_dialogue_ligne_boutons
anchors.horizontalCenter: parent.horizontalCenter
spacing: 20
//--------------------------------------------------------------------
// Bouton Annuler :
Rectangle {
id: bouton_annuler
width: bouton_largeur
height: parent.height * 0.5
anchors.verticalCenter: parent.verticalCenter
radius: shipheart_rayon_bouton
color: couleur_gris_tres_fonce
Label {
id: bouton_annuler_label
anchors.centerIn: parent
text: qsTr("Annuler")
color: couleur_blanc_casse
font.pixelSize:20
}
MouseArea {
anchors.fill: parent
onClicked: {
shipheart_clic();
boite_dialogue_action_circuit.clicked(shipheart_boite_dialogue_bouton_2, 0);
}
}
}
}
}
//=============================================================================================================
// Transitions d'ouverture et de fermeture de la boite de dialogue
//=============================================================================================================
enter: Transition {
ParallelAnimation {
NumberAnimation {
property: "opacity"
from: 0.0
to: 1.0
//easing.type: Easing.InElastic
duration: 200
}
NumberAnimation {
property: "scale"
from: 0.0
to: 1.0
// easing.type: Easing.OutBack
duration: 200
}
}
}
exit: Transition {
ParallelAnimation {
NumberAnimation {
property: "opacity"
from: 1.0
to: 0.0
duration: 200
}
NumberAnimation {
property: "scale"
from: 1.0
to: 0.0
duration: 200
}
}
}
}
@@ -0,0 +1,292 @@
import QtQuick 2.12
import QtQuick.Controls 2.12
Popup {
id: boite_dialogue_affichage_duree
// Propriétés fixes :
readonly property double pourcentage_hauteur_page_popup: 0.7
readonly property double pourcentage_hauteur_titre: 0.3
readonly property double pourcentage_hauteur_compteur: 1 - pourcentage_hauteur_titre - pourcentage_hauteur_bargraph - pourcentage_hauteur_ligne_boutons
readonly property double pourcentage_hauteur_bargraph: 0.07
readonly property double pourcentage_hauteur_ligne_boutons: 0.25
readonly property int bouton_hauteur: 60
readonly property int hauteur_progress_bar: 30
readonly property double pourcentage_largeur_page_popup: 0.8
readonly property int bouton_largeur: 200
readonly property int padding_popup: 20
readonly property int periode_raffraichissement_parametres: 100 // En ms
readonly property int step_timer_appui_long: 30 // en ms
// Propriétés :
property int compteur: 0
property int diviseur: 1
property string libelle_compteur_initial: ""
property string type: "minuteur" // Soit "chrono", soit "minuteur"
// Propriétés alias :
//-------------------------------------------------
// Signaux :
//-------------------------------------------------
signal clicked(int bouton) // Valeurs à lire lorsque le "OnClicked' est activé
//-------------------------------------------------
// Ouverture :
//-------------------------------------------------
onAboutToShow: { // Activé une fois quand le popup va être affiché
init(); // L'init n'a d'utilité que lorsqu'on va l'afficher...
timer_raffraichissement_parametres.start();
}
//-------------------------------------------------
// Fermeture :
//-------------------------------------------------
onAboutToHide: {
timer_raffraichissement_parametres.stop();
}
//===========================================================
// Timer periode raffraichissement parametres :
//===========================================================
Timer {
id: timer_raffraichissement_parametres
interval: periode_raffraichissement_parametres
triggeredOnStart: false
running: false
repeat: true
onTriggered: {
mise_a_jour()
}
}
x: (shipheart_largeur_page * (1 - pourcentage_largeur_page_popup)) / 2;
y: (shipheart_hauteur_page * (1 - pourcentage_hauteur_page_popup)) / 2;
width: shipheart_largeur_page * pourcentage_largeur_page_popup;
height: shipheart_hauteur_page * pourcentage_hauteur_page_popup;
padding: padding_popup
modal: true
// focus: true // SDIR # 224 : Il ne faut PAS activer le focus. Sinon, à la fermeture, le focus précédent est réactivé (comme le dernier TextField par exemple)
closePolicy: Popup.NoAutoClose //Popup.CloseOnPressOutside
background: Rectangle {
color: shipheart_couleur_fond_boite_dialogue_niveau_0
radius: 20
}
Column {
id: affichage_boite_dialogue
spacing: 20
//=========================================================================
// Titre :
//=========================================================================
Rectangle {
id: titre_boite_dialogue
width: boite_dialogue_affichage_duree.width - (2 * padding_popup)
height: (boite_dialogue_affichage_duree.height - (2 * padding_popup) - (2 * affichage_boite_dialogue.spacing)) * pourcentage_hauteur_titre
color: "transparent"
Image {
id: image_titre
source: ""
sourceSize.height: 120
anchors {
verticalCenter: parent.verticalCenter
horizontalCenter: parent.horizontalCenter
}
visible: true
}
}
//=========================================================================
// Zone principale :
//=========================================================================
Rectangle {
id: zone_principale
width: boite_dialogue_affichage_duree.width - (2 * padding_popup)
height: (boite_dialogue_affichage_duree.height - (2 * padding_popup) - (3 * affichage_boite_dialogue.spacing)) * pourcentage_hauteur_compteur
color: "transparent"
Label {
text: Math.trunc(compteur / (60 * 60 * diviseur)).toString().padStart(2, '0') + ":" + Math.trunc((compteur / (60 * diviseur)) % 60).toString().padStart(2, '0') + ":" + Math.trunc((compteur / diviseur) % 60).toString().padStart(2, '0') + ((diviseur > 1) ? ("," + (compteur % diviseur)) : "")
anchors.fill: parent
verticalAlignment: Text.AlignBottom
horizontalAlignment: Text.AlignHCenter
wrapMode: Text.Wrap
color: couleur_blanc_casse
font.pixelSize:150
font.weight: Font.Bold
}
}
//=========================================================================
// Barre d'avancement :
//=========================================================================
ProgressBar {
id: progress_bar
width: parent.width
height: (boite_dialogue_affichage_duree.height - (2 * padding_popup) - (3 * affichage_boite_dialogue.spacing)) * pourcentage_hauteur_bargraph
value: (page_accueil.compteur_initial_minuteur !== 0) ? (compteur_minuteur / page_accueil.compteur_initial_minuteur) : 0
visible: type === "minuteur"
background: Rectangle {
implicitWidth: parent.width
implicitHeight: hauteur_progress_bar
color: shipheart_couleur_fond_bouton //"transparent"
radius: shipheart_rayon_bouton
Text {
// text: (progress_bar.value * 100).toFixed(0) + "%"
text: "[ " + libelle_compteur_initial + " ]"
anchors.fill: parent
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
color: shipheart_couleur_texte_slider
font.pixelSize:18
font.weight: Font.Bold
}
}
contentItem: Item {
implicitWidth: parent.width
implicitHeight: hauteur_progress_bar
Rectangle {
width: progress_bar.value * parent.width
height: parent.height
radius: shipheart_rayon_bouton
color: shipheart_couleur_fond_bouton_actif
}
}
}
//=========================================================================
// Boutons de bas de fenêtre :
//=========================================================================
Row {
height: (boite_dialogue_affichage_duree.height - (2 * padding_popup) - (3 * affichage_boite_dialogue.spacing)) * pourcentage_hauteur_ligne_boutons
anchors.horizontalCenter: parent.horizontalCenter
spacing: 20
//.......................................................
// Bouton Ok :
//.......................................................
Rectangle {
id: bouton_ok
width: bouton_largeur
height: bouton_hauteur
anchors.verticalCenter: parent.verticalCenter
radius: shipheart_rayon_bouton
color: couleur_gris_tres_fonce
Label {
anchors.centerIn: parent
text: qsTr("Ok")
color: couleur_blanc_casse
font.pixelSize:20
}
MouseArea {
anchors.fill: parent
onClicked: {
shipheart_clic();
boite_dialogue_affichage_duree.clicked(shipheart_boite_dialogue_bouton_1);
}
}
}
}
}
//=============================================================================================================
// Transitions d'ouverture et de fermeture de la boite de dialogue
//=============================================================================================================
enter: Transition {
ParallelAnimation {
NumberAnimation {
property: "opacity"
from: 0.0
to: 1.0
//easing.type: Easing.InElastic
duration: 200
}
NumberAnimation {
property: "scale"
from: 0.0
to: 1.0
// easing.type: Easing.OutBack
duration: 200
}
}
}
exit: Transition {
ParallelAnimation {
NumberAnimation {
property: "opacity"
from: 1.0
to: 0.0
duration: 200
}
NumberAnimation {
property: "scale"
from: 1.0
to: 0.0
duration: 200
}
}
}
//=============================================================================================================
// Init des données de la boite de dialogue
//=============================================================================================================
function init()
{
if (type === "chrono")
{
image_titre.source = "/images/chronometre.png"
diviseur = 10;
}
else if (type === "minuteur")
{
image_titre.source = "/images/sablier.png"
diviseur = 1;
libelle_compteur_initial = Math.trunc(page_accueil.compteur_initial_minuteur / (60 * 60 * diviseur)).toString().padStart(2, '0') + ":" + Math.trunc((page_accueil.compteur_initial_minuteur / (60 * diviseur)) % 60).toString().padStart(2, '0') + ":" + Math.trunc((page_accueil.compteur_initial_minuteur / diviseur) % 60).toString().padStart(2, '0')
}
mise_a_jour();
}
//=============================================================================================================
// Mise à jour des données de la boite de dialogue
//=============================================================================================================
function mise_a_jour()
{
if (type === "chrono")
{
compteur = page_accueil.compteur_chrono;
}
else if (type === "minuteur")
{
compteur = page_accueil.compteur_minuteur;
}
if (compteur <= 0) boite_dialogue_affichage_duree.close(); // A l'expiration du compteur, on ferme la boite dans tous les cas.
}
}
+577
View File
@@ -0,0 +1,577 @@
import QtQuick 2.12
import QtQuick.Controls 2.12
import"fonctions_date_heure.js" as Fonctions_date_heure
Popup {
id: boite_dialogue_condition_horaire
// Propriétés fixes :
readonly property double pourcentage_largeur_page_popup: 0.7
readonly property double pourcentage_hauteur_page_popup: 0.7
readonly property int padding_popup: 20
readonly property double pourcentage_boite_dialogue_titre: 0.20
readonly property double pourcentage_boite_dialogue_message: 1 - pourcentage_boite_dialogue_titre - pourcentage_boite_dialogue_ligne_boutons
readonly property double pourcentage_boite_dialogue_ligne_boutons: 0.2
readonly property int hauteur_icones_tumblers: 35
readonly property int bouton_largeur: 200
readonly property int espace_tumblers: 20
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
// Propriétés :
property int valeur_timer_appui_long: 0
property int jour_en_cours: 0
property int valeur_tumbler_jour: 0
property int valeur_tumbler_heure: 0
property int valeur_tumbler_minute: 0
// Propriétés alias :
property alias boutonOk: bouton_ok.visible
property alias boutonAnnuler: bouton_annuler.visible
property alias boutonSupprimer: bouton_supprimer.visible
//-------------------------------------------------
// Signaux :
//-------------------------------------------------
signal clicked(int bouton, int jour, int heure, int minute) // Valeurs à lire lorsque le "OnClicked' est activé
//-------------------------------------------------
// Init :
//-------------------------------------------------
onOpened: { // Activé une fois que le popup est complètement affiché
mise_a_jour(); // Met à jour les valeurs courantes des 3 tumbler en fonction des paramètres passés
}
//===========================================================
// 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(); // Charge le modèle des 3 tumblers (dès l'ouverture de "config_event.qml" !)
}
}
}
//===========================================================
// 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;
if (mousearea_bouton_supprimer.pressed)
{
boite_dialogue_condition_horaire.clicked(shipheart_boite_dialogue_bouton_3, 0, 0, 0);
}
}
}
}
x: (shipheart_largeur_page * (1 - pourcentage_largeur_page_popup)) / 2;
y: (shipheart_hauteur_page * (1 - pourcentage_hauteur_page_popup)) / 2;
width: shipheart_largeur_page * pourcentage_largeur_page_popup;
height: shipheart_hauteur_page * pourcentage_hauteur_page_popup;
padding: padding_popup
modal: true
// focus: true // SDIR # 224 : Il ne faut PAS activer le focus. Sinon, à la fermeture, le focus précédent est réactivé (comme le dernier TextField par exemple)
closePolicy: Popup.NoAutoClose //Popup.CloseOnPressOutside
background: Rectangle {
color: shipheart_couleur_fond_boite_dialogue_niveau_0
radius: 20
}
Column {
id: affichage_boite_dialogue
spacing: 20
//=========================================================================
// Titre :
//=========================================================================
Rectangle {
id: titre_boite_dialogue
width: boite_dialogue_condition_horaire.width - (2 * padding_popup)
height: (boite_dialogue_condition_horaire.height - (2 * padding_popup) - (2 * affichage_boite_dialogue.spacing)) * pourcentage_boite_dialogue_titre
color: "transparent"
Image {
id: image_titre
source: "/images/condition_horaire.png"
sourceSize.height: 80
anchors {
verticalCenter: parent.verticalCenter
horizontalCenter: parent.horizontalCenter
}
}
}
//=========================================================================
// Choix :
//=========================================================================
Rectangle {
id: zone_choix
width: boite_dialogue_condition_horaire.width - (2 * padding_popup)
height: (boite_dialogue_condition_horaire.height - (2 * padding_popup) - (2 * affichage_boite_dialogue.spacing)) * pourcentage_boite_dialogue_message
color: "transparent"
Row {
anchors.horizontalCenter: parent.horizontalCenter
spacing: espace_tumblers
//-----------------------------------
// Jour de la semaine :
//-----------------------------------
Rectangle {
width: zone_choix.width * 0.3
height: zone_choix.height
color: "transparent"
Image {
source: "/images/calendrier.png"
sourceSize.height: hauteur_icones_tumblers
anchors {
top: parent.top
horizontalCenter: parent.horizontalCenter
}
}
Tumbler {
id: tumbler_jour
width: parent.width
height: parent.height * 0.85
anchors {
bottom: parent.bottom
}
visibleItemCount: 5
model: ListModel { }
delegate: bouton_jour
Component {
id: bouton_jour
Rectangle {
color: couleur_gris_fonce
opacity: 1.0 - Math.abs(Tumbler.displacement) / (Tumbler.tumbler.visibleItemCount / 2)
Text {
anchors.fill: parent
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
opacity: 1.0 - Math.abs(Tumbler.displacement) / (Tumbler.tumbler.visibleItemCount / 2)
text: model.libelle
color: (index === tumbler_jour.currentIndex) ? couleur_blanc_casse : couleur_gris_pale
font.pixelSize: 18
font.bold: (index === tumbler_jour.currentIndex)
}
}
}
background: Rectangle {
color: couleur_gris_tres_fonce
radius: shipheart_grand_rayon_bouton
}
}
}
//-----------------------------------
// Heure :
//-----------------------------------
Rectangle {
width: zone_choix.width * 0.3
height: zone_choix.height
color: "transparent"
Image {
source: "/images/heure.png"
sourceSize.height: hauteur_icones_tumblers
anchors {
top: parent.top
horizontalCenter: parent.horizontalCenter
}
}
Tumbler {
id: tumbler_heure
width: parent.width
height: parent.height * 0.85
anchors {
bottom: parent.bottom
}
visibleItemCount: 5
model: ListModel { }
delegate: bouton_heure
Component {
id: bouton_heure
Rectangle {
color: couleur_gris_fonce
opacity: 1.0 - Math.abs(Tumbler.displacement) / (Tumbler.tumbler.visibleItemCount / 2)
Text {
anchors.fill: parent
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
opacity: 1.0 - Math.abs(Tumbler.displacement) / (Tumbler.tumbler.visibleItemCount / 2)
text: model.libelle
color: (index === tumbler_heure.currentIndex) ? couleur_blanc_casse : couleur_gris_pale
font.pixelSize: 18
font.bold: (index === tumbler_heure.currentIndex)
}
}
}
background: Rectangle {
color: couleur_gris_tres_fonce
radius: shipheart_grand_rayon_bouton
}
}
}
//-----------------------------------
// Minute :
//-----------------------------------
Rectangle {
width: zone_choix.width * 0.3
height: zone_choix.height
color: "transparent"
Image {
source: "/images/minute.png"
sourceSize.height: hauteur_icones_tumblers
anchors {
top: parent.top
horizontalCenter: parent.horizontalCenter
}
visible: (tumbler_minute.opacity > 0)
}
Tumbler {
id: tumbler_minute
width: parent.width
height: parent.height * 0.85
anchors {
bottom: parent.bottom
}
visibleItemCount: 5
model: ListModel { }
delegate: bouton_minute
opacity : (tumbler_heure.model.get(tumbler_heure.currentIndex) !== undefined) ? // Sinon, génère un warning au démarrage...
((tumbler_heure.model.get(tumbler_heure.currentIndex).valeur <= 23) ? 1.0 : 0) :
1.0
Behavior on opacity {
NumberAnimation { duration: 300 }
}
Component {
id: bouton_minute
Rectangle {
color: couleur_gris_fonce
opacity: 1.0 - Math.abs(Tumbler.displacement) / (Tumbler.tumbler.visibleItemCount / 2)
Text {
anchors.fill: parent
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
opacity: 1.0 - Math.abs(Tumbler.displacement) / (Tumbler.tumbler.visibleItemCount / 2)
text: model.libelle
color: (index === tumbler_minute.currentIndex) ? couleur_blanc_casse : couleur_gris_pale
font.pixelSize: 18
font.bold: (index === tumbler_minute.currentIndex)
}
}
}
background: Rectangle {
color: couleur_gris_tres_fonce
radius: shipheart_grand_rayon_bouton
}
}
}
}
}
//=========================================================================
// Boutons :
//=========================================================================
Row {
height: (boite_dialogue_condition_horaire.height - (2 * padding_popup) - (2 * affichage_boite_dialogue.spacing)) * pourcentage_boite_dialogue_ligne_boutons
anchors.horizontalCenter: parent.horizontalCenter
spacing: 20
//.......................................................
// Bouton Ok :
//.......................................................
Rectangle {
id: bouton_ok
width: bouton_largeur
height: parent.height * 0.75
anchors.verticalCenter: parent.verticalCenter
radius: shipheart_rayon_bouton
color: couleur_gris_tres_fonce
Label {
id: bouton_ok_label
anchors.centerIn: parent
text: qsTr("OK")
color: couleur_blanc_casse
font.pixelSize:20
}
MouseArea {
anchors.fill: parent
onClicked: {
shipheart_clic();
boite_dialogue_condition_horaire.clicked(shipheart_boite_dialogue_bouton_1,
tumbler_jour.model.get(tumbler_jour.currentIndex).valeur,
tumbler_heure.model.get(tumbler_heure.currentIndex).valeur,
tumbler_minute.model.get(tumbler_minute.currentIndex).valeur);
}
}
}
//.......................................................
// Bouton Annuler :
//.......................................................
Rectangle {
id: bouton_annuler
width: bouton_largeur
height: parent.height * 0.75
anchors.verticalCenter: parent.verticalCenter
radius: shipheart_rayon_bouton
color: couleur_gris_tres_fonce
Label {
id: bouton_annuler_label
anchors.centerIn: parent
text: qsTr("Annuler")
color: couleur_blanc_casse
font.pixelSize:20
}
MouseArea {
anchors.fill: parent
onClicked: {
shipheart_clic();
boite_dialogue_condition_horaire.clicked(shipheart_boite_dialogue_bouton_2, 0, 0, 0);
}
}
}
//.......................................................
// Bouton de suppression
//.......................................................
Rectangle {
id: bouton_supprimer
width: bouton_largeur
height: parent.height * 0.75
anchors.verticalCenter: parent.verticalCenter
radius: shipheart_rayon_bouton
color: couleur_gris_tres_fonce
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_supprimer.pressed
}
Text {
anchors {
verticalCenter: parent.verticalCenter
horizontalCenter: parent.horizontalCenter
}
font.pixelSize: 20
text: qsTr("Supprimer")
color: couleur_blanc_casse
}
MouseArea {
id: mousearea_bouton_supprimer
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;
}
}
}
}
}
//=============================================================================================================
// Transitions d'ouverture et de fermeture de la boite de dialogue
//=============================================================================================================
enter: Transition {
ParallelAnimation {
NumberAnimation {
property: "opacity"
from: 0.0
to: 1.0
//easing.type: Easing.InElastic
duration: 200
}
NumberAnimation {
property: "scale"
from: 0.0
to: 1.0
// easing.type: Easing.OutBack
duration: 200
}
}
}
exit: Transition {
ParallelAnimation {
NumberAnimation {
property: "opacity"
from: 1.0
to: 0.0
duration: 200
}
NumberAnimation {
property: "scale"
from: 1.0
to: 0.0
duration: 200
}
}
}
//=============================================================================================================
// Init des données de la boite de dialogue
//=============================================================================================================
function init()
{
var i;
//-------------------------------------
// Jours de la semaine :
//-------------------------------------
tumbler_jour.model.clear();
// "Tous les jours" en premier :
tumbler_jour.model.append( { "valeur": 8, "libelle": Fonctions_date_heure.nom_jour_semaine(8) } );
// Ajout de lundi à dimanche :
for (i = 0; i < 7; i ++)
{
tumbler_jour.model.append( { "valeur": (i+1), "libelle": Fonctions_date_heure.nom_jour_semaine(i+1) } );
}
//-------------------------------------
// Heures :
//-------------------------------------
tumbler_heure.model.clear();
// Heures normales :
for (i = 0; i < 24; i ++)
{
tumbler_heure.model.append( { "valeur": i, "libelle": ((i < 10) ? "0"+i : ""+i)} );
}
// Heures spéciales
// tumbler_heure.model.append( { "valeur": shipheart_heure_speciale_fin_nuit, "libelle": qsTr("Fin de nuit") } );
tumbler_heure.model.append( { "valeur": shipheart_heure_speciale_aube_nautique, "libelle": qsTr("Aube nautique") } );
// tumbler_heure.model.append( { "valeur": shipheart_heure_speciale_aube, "libelle": qsTr("Aube") } );
tumbler_heure.model.append( { "valeur": shipheart_heure_speciale_lever_soleil, "libelle": qsTr("Lever du soleil") } );
tumbler_heure.model.append( { "valeur": shipheart_heure_speciale_zenith, "libelle": qsTr("Zénith") } );
tumbler_heure.model.append( { "valeur": shipheart_heure_speciale_coucher_soleil, "libelle": qsTr("Coucher du soleil") } );
// tumbler_heure.model.append( { "valeur": shipheart_heure_speciale_crepuscule, "libelle": qsTr("Crépuscule") } );
tumbler_heure.model.append( { "valeur": shipheart_heure_speciale_crepuscule_nautique, "libelle": qsTr("Crépuscule nautique") } );
// tumbler_heure.model.append( { "valeur": shipheart_heure_speciale_debut_nuit, "libelle": qsTr("Début de nuit") } );
//-------------------------------------
// Minutes :
//-------------------------------------
tumbler_minute.model.clear();
// Heures normales :
for (i = 0; i < 60; i ++)
{
tumbler_minute.model.append( { "valeur": i, "libelle": ((i < 10) ? "0"+i : ""+i) } );
}
}
//=============================================================================================================
// Mise à jour des données de la boite de dialogue
//=============================================================================================================
function mise_a_jour()
{
var i;
//-------------------------------------
// Jours de la semaine :
//-------------------------------------
// Rercherche de la valeur courante :
for (i = 0; i < tumbler_jour.model.count; i ++)
{
if (tumbler_jour.model.get(i).valeur === valeur_tumbler_jour)
{
tumbler_jour.currentIndex = i;
break;
}
}
//-------------------------------------
// Heures :
//-------------------------------------
// Rercherche de la valeur courante :
for (i = 0; i < tumbler_heure.model.count; i ++)
{
if (tumbler_heure.model.get(i).valeur === valeur_tumbler_heure)
{
tumbler_heure.currentIndex = i;
break;
}
}
//-------------------------------------
// Minutes :
//-------------------------------------
// Rercherche de la valeur courante :
for (i = 0; i < tumbler_minute.model.count; i ++)
{
if (tumbler_minute.model.get(i).valeur === valeur_tumbler_minute)
{
tumbler_minute.currentIndex = i;
break;
}
}
}
}
+234
View File
@@ -0,0 +1,234 @@
import QtQuick 2.12
import QtQuick.Controls 2.12
Popup {
id: boite_dialogue_entrees_aux
// Propriétés fixes :
readonly property double pourcentage_largeur_page_popup: 0.95
readonly property double pourcentage_hauteur_page_popup: 0.7
readonly property int padding_popup: 20
readonly property double pourcentage_boite_dialogue_titre: 0.3
readonly property double pourcentage_boite_dialogue_message: 1 - pourcentage_boite_dialogue_titre - pourcentage_boite_dialogue_ligne_boutons
readonly property double pourcentage_boite_dialogue_ligne_boutons: 0.3
readonly property int bouton_largeur: 200
// Propriétés :
// Propriétés alias :
property alias boutonAnnuler: bouton_annuler.visible
property alias boutonAnnulerTexte: bouton_annuler_label.text
property string nom_equipement: ""
// Signaux :
signal clicked(int bouton, int no_entree) // Valeur à lire lorsque le "OnClicked' est activé
x: (shipheart_largeur_page * (1 - pourcentage_largeur_page_popup)) / 2;
y: (shipheart_hauteur_page * (1 - pourcentage_hauteur_page_popup)) / 2;
width: shipheart_largeur_page * pourcentage_largeur_page_popup;
height: shipheart_hauteur_page * pourcentage_hauteur_page_popup;
padding: padding_popup
modal: true
// focus: true // SDIR # 224 : Il ne faut PAS activer le focus. Sinon, à la fermeture, le focus précédent est réactivé (comme le dernier TextField par exemple)
closePolicy: Popup.NoAutoClose //Popup.CloseOnPressOutside
background: Rectangle {
color: shipheart_couleur_fond_boite_dialogue_niveau_0
radius: 20
}
Column {
id: affichage_boite_dialogue
spacing: 20
//=========================================================================
// Titre :
//=========================================================================
Rectangle {
id: titre_boite_dialogue
width: boite_dialogue_entrees_aux.width - (2 * padding_popup)
height: (boite_dialogue_entrees_aux.height - (2 * padding_popup) - (2 * affichage_boite_dialogue.spacing)) * pourcentage_boite_dialogue_titre
color: "transparent"
Label {
text: qsTr("Entrées Auxiliaires")
anchors.fill: parent
verticalAlignment: Text.AlignTop
horizontalAlignment: Text.AlignHCenter
topPadding: 20
wrapMode: Text.Wrap
color: couleur_blanc_casse
font.pixelSize:40
font.weight: Font.Bold
}
Label {
text: qsTr("Affectation de l'entrée n° %1 du %2").arg(entree_en_cours).arg(nom_equipement)
anchors.fill: parent
verticalAlignment: Text.AlignBottom
horizontalAlignment: Text.AlignHCenter
bottomPadding: 20
wrapMode: Text.Wrap
color: couleur_blanc_casse
font.pixelSize:18
}
}
//=========================================================================
// Choix de l'entrée :
//=========================================================================
Rectangle {
width: boite_dialogue_entrees_aux.width - (2 * padding_popup)
height: (boite_dialogue_entrees_aux.height - (2 * padding_popup) - (2 * affichage_boite_dialogue.spacing)) * pourcentage_boite_dialogue_message
color: "transparent"
Grid {
id: grille_entrees_aux
width: parent.width
height: parent.height
columns: 4
spacing: 20
Repeater { // Constitue les blocs d'entrées, telq qu'ils sont physiquement (4 entrées + espace)
model: modele_entrees_aux
delegate: Row {
width: (parent.width - (3 * grille_entrees_aux.spacing)) / 4
height: (parent.height - grille_entrees_aux.spacing) / 2
Repeater { // Constitue les 4 entrées par bloc
model: valeurs
delegate: Rectangle { // Ce rectangle réserve la place pour chaque bouton mais n'est pas visible
width: parent.width / 4
height: parent.height
color: "transparent"
Rectangle { // Ce rectangle est le bouton visible, légèrement plus petit que son père pour réserver un espace entre chaque entrée au sein du même bloc
width: parent.width * 0.98
height: parent.height
radius: shipheart_rayon_bouton
color: ((model.used === 1) && (model.no_entree !== entree_en_cours)) ? couleur_gris_fonce : couleur_gris_tres_fonce
Label {
anchors.fill: parent
horizontalAlignment: Qt.AlignHCenter
verticalAlignment: Qt.AlignTop
topPadding: 20
text: no
color: (model.used === 0) ? couleur_blanc_casse : couleur_gris_moyen
font.pixelSize: 18
font.bold: true
}
Label {
anchors.fill: parent
horizontalAlignment: Qt.AlignHCenter
verticalAlignment: Qt.AlignBottom
bottomPadding: 20
visible: model.no_entree !== 0
text: model.no_entree
color: couleur_green_yellow
font.pixelSize: 18
font.bold: true
}
}
MouseArea {
anchors.fill: parent
enabled: (model.used === 0) || (model.no_entree === entree_en_cours)
onClicked: {
shipheart_clic();
boite_dialogue_entrees_aux.clicked(shipheart_boite_dialogue_bouton_1, no);
}
}
}
}
}
}
}
}
//=========================================================================
// Boutons :
//=========================================================================
Row {
height: (boite_dialogue_entrees_aux.height - (2 * padding_popup) - (2 * affichage_boite_dialogue.spacing)) * pourcentage_boite_dialogue_ligne_boutons
anchors.horizontalCenter: parent.horizontalCenter
spacing: 20
//--------------------------------------------------------------------
// Bouton Annuler :
Rectangle {
id: bouton_annuler
width: bouton_largeur
height: parent.height * 0.5
anchors.verticalCenter: parent.verticalCenter
radius: shipheart_rayon_bouton
color: couleur_gris_tres_fonce
Label {
id: bouton_annuler_label
anchors.centerIn: parent
text: qsTr("Annuler")
color: couleur_blanc_casse
font.pixelSize:20
}
MouseArea {
anchors.fill: parent
onClicked: {
shipheart_clic();
boite_dialogue_entrees_aux.clicked(shipheart_boite_dialogue_bouton_2, 0);
boite_dialogue_entrees_aux.close();
}
}
}
}
}
//=============================================================================================================
// Transitions d'ouverture et de fermeture de la boite de dialogue
//=============================================================================================================
enter: Transition {
ParallelAnimation {
NumberAnimation {
property: "opacity"
from: 0.0
to: 1.0
//easing.type: Easing.InElastic
duration: 200
}
NumberAnimation {
property: "scale"
from: 0.0
to: 1.0
// easing.type: Easing.OutBack
duration: 200
}
}
}
exit: Transition {
ParallelAnimation {
NumberAnimation {
property: "opacity"
from: 1.0
to: 0.0
duration: 200
}
NumberAnimation {
property: "scale"
from: 1.0
to: 0.0
duration: 200
}
}
}
}
+229
View File
@@ -0,0 +1,229 @@
import QtQuick 2.12
import QtQuick.Controls 2.12
Popup {
id: boite_dialogue_entrees_level_sensor
// Propriétés fixes :
readonly property double pourcentage_largeur_page_popup: 0.8
readonly property double pourcentage_hauteur_page_popup: 0.7
readonly property int padding_popup: 20
readonly property double pourcentage_boite_dialogue_titre: 0.3
readonly property double pourcentage_boite_dialogue_message: 1 - pourcentage_boite_dialogue_titre - pourcentage_boite_dialogue_ligne_boutons
readonly property double pourcentage_boite_dialogue_ligne_boutons: 0.3
readonly property int bouton_largeur: 200
// Propriétés :
// Propriétés alias :
property alias boutonAnnuler: bouton_annuler.visible
property alias boutonAnnulerTexte: bouton_annuler_label.text
// Signaux :
signal clicked(int bouton, int no_entree) // Valeur à lire lorsque le "OnClicked' est activé
x: (shipheart_largeur_page * (1 - pourcentage_largeur_page_popup)) / 2;
y: (shipheart_hauteur_page * (1 - pourcentage_hauteur_page_popup)) / 2;
width: shipheart_largeur_page * pourcentage_largeur_page_popup;
height: shipheart_hauteur_page * pourcentage_hauteur_page_popup;
padding: padding_popup
modal: true
// focus: true // SDIR # 224 : Il ne faut PAS activer le focus. Sinon, à la fermeture, le focus précédent est réactivé (comme le dernier TextField par exemple)
closePolicy: Popup.NoAutoClose //Popup.CloseOnPressOutside
background: Rectangle {
color: shipheart_couleur_fond_boite_dialogue_niveau_0
radius: 20
}
Column {
id: affichage_boite_dialogue
spacing: 20
//=========================================================================
// Titre :
//=========================================================================
Rectangle {
id: titre_boite_dialogue
width: boite_dialogue_entrees_level_sensor.width - (2 * padding_popup)
height: (boite_dialogue_entrees_level_sensor.height - (2 * padding_popup) - (2 * affichage_boite_dialogue.spacing)) * pourcentage_boite_dialogue_titre
color: "transparent"
Label {
text: qsTr("Entrées Level Sensor")
anchors.fill: parent
verticalAlignment: Text.AlignTop
horizontalAlignment: Text.AlignHCenter
topPadding: 20
wrapMode: Text.Wrap
color: couleur_blanc_casse
font.pixelSize:40
font.weight: Font.Bold
}
Label {
text: qsTr("Affectation de l'entrée du réservoir")
anchors.fill: parent
verticalAlignment: Text.AlignBottom
horizontalAlignment: Text.AlignHCenter
bottomPadding: 20
wrapMode: Text.Wrap
color: couleur_blanc_casse
font.pixelSize:18
}
}
//=========================================================================
// Choix de l'entrée :
//=========================================================================
Rectangle {
width: boite_dialogue_entrees_level_sensor.width - (2 * padding_popup)
height: (boite_dialogue_entrees_level_sensor.height - (2 * padding_popup) - (2 * affichage_boite_dialogue.spacing)) * pourcentage_boite_dialogue_message
color: "transparent"
Grid {
id: grille_entrees_level_sensor
width: parent.width
height: parent.height
columns: 1
spacing: 20
Repeater { // Constitue les blocs d'entrées, telq qu'ils sont physiquement (4 entrées + espace)
model: modele_entrees_level_sensor
delegate: Row {
width: (parent.width - ((grille_entrees_level_sensor.columns - 1) * grille_entrees_level_sensor.spacing)) / grille_entrees_level_sensor.columns
height: (parent.height - grille_entrees_level_sensor.spacing) / 2
Repeater { // Constitue les 4 entrées par bloc
model: valeurs
delegate: Rectangle { // Ce rectangle réserve la place pour chaque bouton mais n'est pas visible
width: parent.width / 4
height: parent.height
color: "transparent"
Rectangle { // Ce rectangle est le bouton visible, légèrement plus petit que son père pour réserver un espace entre chaque entrée au sein du même bloc
width: parent.width * 0.98
height: parent.height
radius: shipheart_rayon_bouton
color: ((model.used === 1) && (model.no_entree !== entree_en_cours)) ? couleur_gris_fonce : couleur_gris_tres_fonce
Label {
anchors.fill: parent
horizontalAlignment: Qt.AlignHCenter
verticalAlignment: Qt.AlignTop
topPadding: 20
text: no
color: (model.used === 0) ? couleur_blanc_casse : couleur_gris_moyen
font.pixelSize: 18
font.bold: true
}
Label {
anchors.fill: parent
horizontalAlignment: Qt.AlignHCenter
verticalAlignment: Qt.AlignBottom
bottomPadding: 20
visible: model.no_entree !== 0
text: "X"
color: couleur_green_yellow
font.pixelSize: 18
font.bold: true
}
}
MouseArea {
anchors.fill: parent
enabled: (model.used === 0) || (model.no_entree === entree_en_cours)
onClicked: {
shipheart_clic();
boite_dialogue_entrees_level_sensor.clicked(shipheart_boite_dialogue_bouton_1, no);
}
}
}
}
}
}
}
}
//=========================================================================
// Boutons :
//=========================================================================
Row {
height: (boite_dialogue_entrees_level_sensor.height - (2 * padding_popup) - (2 * affichage_boite_dialogue.spacing)) * pourcentage_boite_dialogue_ligne_boutons
anchors.horizontalCenter: parent.horizontalCenter
spacing: 20
//--------------------------------------------------------------------
// Bouton Annuler :
Rectangle {
id: bouton_annuler
width: bouton_largeur
height: parent.height * 0.5
anchors.verticalCenter: parent.verticalCenter
radius: shipheart_rayon_bouton
color: couleur_gris_tres_fonce
Label {
id: bouton_annuler_label
anchors.centerIn: parent
text: qsTr("Annuler")
color: couleur_blanc_casse
font.pixelSize:20
}
MouseArea {
anchors.fill: parent
onClicked: {
shipheart_clic();
boite_dialogue_entrees_level_sensor.clicked(shipheart_boite_dialogue_bouton_2, 0);
boite_dialogue_entrees_level_sensor.close();
}
}
}
}
}
//=============================================================================================================
// Transitions d'ouverture et de fermeture de la boite de dialogue
//=============================================================================================================
enter: Transition {
ParallelAnimation {
NumberAnimation {
property: "opacity"
from: 0.0
to: 1.0
//easing.type: Easing.InElastic
duration: 200
}
NumberAnimation {
property: "scale"
from: 0.0
to: 1.0
// easing.type: Easing.OutBack
duration: 200
}
}
}
exit: Transition {
ParallelAnimation {
NumberAnimation {
property: "opacity"
from: 1.0
to: 0.0
duration: 200
}
NumberAnimation {
property: "scale"
from: 1.0
to: 0.0
duration: 200
}
}
}
}
+380
View File
@@ -0,0 +1,380 @@
import QtQuick 2.12
import QtQuick.Controls 2.12
Popup {
id: boite_dialogue_liste_alarmes
// Propriétés fixes :
readonly property double pourcentage_largeur_page_popup: 0.8
readonly property double pourcentage_hauteur_page_popup: 0.7
readonly property int padding_popup: 20
readonly property double pourcentage_zone_titre: 0.3
readonly property double pourcentage_zone_principale: 1 - pourcentage_zone_titre - pourcentage_zone_boutons
readonly property double pourcentage_zone_boutons: 0.15
readonly property int bouton_hauteur: 60
readonly property int hauteur_ligne_formulaire: 60
readonly property double pourcentage_zone_validite: 0.15
readonly property int bouton_largeur: 200
// Propriétés :
property bool flag_init: true
property int type_source: 0
property int id_source: 0
property int id_variable: 0
property string titre: ""
property string titre_widget: ""
property string unite: ""
property int index_condition_horaire_en_cours: -1
// Propriétés alias :
//-------------------------------------------------
// Signaux :
//-------------------------------------------------
signal clicked(int bouton) // Valeurs à lire lorsque le "OnClicked' est activé
//-------------------------------------------------
// Ouverture :
//-------------------------------------------------
onAboutToShow: { // Activé une fois quand le popup va être affiché
init(); // L'init n'a d'utilité que lorsqu'on va l'afficher...
}
onOpened: { // Quand la page est affichée
}
//-------------------------------------------------
// Fermeture :
//-------------------------------------------------
onAboutToHide: {
timer_raffraichissement_parametres.stop();
}
//===========================================================
// Timer periode raffraichissement parametres :
//===========================================================
Timer {
id: timer_raffraichissement_parametres
interval: periode_raffraichissement_parametres
triggeredOnStart: false
running: false
repeat: true
onTriggered: {
mise_a_jour()
}
}
x: (shipheart_largeur_page * (1 - pourcentage_largeur_page_popup)) / 2;
y: (shipheart_hauteur_page * (1 - pourcentage_hauteur_page_popup)) / 2;
width: shipheart_largeur_page * pourcentage_largeur_page_popup;
height: shipheart_hauteur_page * pourcentage_hauteur_page_popup;
padding: padding_popup
modal: true
// focus: true // SDIR # 224 : Il ne faut PAS activer le focus. Sinon, à la fermeture, le focus précédent est réactivé (comme le dernier TextField par exemple)
closePolicy: Popup.NoAutoClose //Popup.CloseOnPressOutside
background: Rectangle {
color: shipheart_couleur_fond_boite_dialogue_niveau_0
radius: 20
}
Column {
id: affichage_boite_dialogue
spacing: 20
//=========================================================================
// Titre :
//=========================================================================
Rectangle {
id: titre_boite_dialogue
width: boite_dialogue_liste_alarmes.width - (2 * padding_popup)
height: (boite_dialogue_liste_alarmes.height - (2 * padding_popup) - (2 * affichage_boite_dialogue.spacing)) * pourcentage_zone_titre
color: "transparent"
Image {
id: image_titre
source: "/images/cloche.png"
sourceSize.height: parent.height * 0.4
anchors {
verticalCenter: parent.verticalCenter
verticalCenterOffset: -parent.height / 4
horizontalCenter: parent.horizontalCenter
}
visible: true
}
Label {
text: titre
anchors {
verticalCenter: parent.verticalCenter
verticalCenterOffset: parent.height / 4
horizontalCenter: parent.horizontalCenter
}
wrapMode: Text.Wrap
color: couleur_blanc_casse
font.pixelSize:32
font.weight: Font.Bold
}
}
//=========================================================================
// Zone principale :
//=========================================================================
Rectangle {
id: zone_principale
width: boite_dialogue_liste_alarmes.width - (2 * padding_popup)
height: (boite_dialogue_liste_alarmes.height - (2 * padding_popup) - (2 * affichage_boite_dialogue.spacing)) * pourcentage_zone_principale
color: "transparent"
GridView {
id: grille_alarmes
width: parent.width
height: parent.height
cellWidth: parent.width;
cellHeight: hauteur_ligne_formulaire
snapMode: GridView.SnapToRow // L'arret de l'affichage se fera sur une rangée entière
clip: true // Nécessaire pour que les Delegates s'arretent à l'intérieur de la GridView
model: ListModel { }
delegate: Rectangle {
width: grille_alarmes.cellWidth
height: grille_alarmes.cellHeight
color: "transparent"
//.......................................................
// Bouton principal :
//.......................................................
Rectangle {
height: parent.height - 10
width: parent.width
color: (model.etat_test === 0) ? shipheart_couleur_fond_bouton : couleur_rouge
radius: shipheart_rayon_bouton
anchors {
left: parent.left
verticalCenter: parent.verticalCenter
}
Text {
anchors.fill: parent
verticalAlignment: Qt.AlignVCenter
horizontalAlignment: Qt.AlignLeft
leftPadding: 20
text: "<b>" + model.nom_event + "</b> [" + model.libelle_test + "]"
wrapMode: Text.Wrap
textFormat: Text.RichText
color: couleur_blanc_casse
font.pixelSize: 18
}
/* MouseArea{
anchors.fill : parent
onClicked: {
shipheart_clic();
}
}
*/
}
}
} // Fin de Gridview
}
//=========================================================================
// Boutons de bas de fenêtre :
//=========================================================================
Row {
height: (boite_dialogue_liste_alarmes.height - (2 * padding_popup) - (2 * affichage_boite_dialogue.spacing)) * pourcentage_zone_boutons
anchors.horizontalCenter: parent.horizontalCenter
spacing: 20
//.......................................................
// Bouton Ok :
//.......................................................
Rectangle {
id: bouton_ok
width: bouton_largeur
height: bouton_hauteur
anchors.verticalCenter: parent.verticalCenter
radius: shipheart_rayon_bouton
color: couleur_gris_tres_fonce
Label {
anchors.centerIn: parent
text: qsTr("Ok")
color: couleur_blanc_casse
font.pixelSize:20
}
MouseArea {
anchors.fill: parent
onClicked: {
shipheart_clic();
boite_dialogue_liste_alarmes.clicked(shipheart_boite_dialogue_bouton_1);
boite_dialogue_liste_alarmes.close();
}
}
}
}
}
//=============================================================================================================
// Transitions d'ouverture et de fermeture de la boite de dialogue
//=============================================================================================================
enter: Transition {
ParallelAnimation {
NumberAnimation {
property: "opacity";
from: 0.0;
to: 1.0
//easing.type: Easing.InElastic;
duration: 200
}
NumberAnimation {
property: "scale";
from: 0.0;
to: 1.0;
// easing.type: Easing.OutBack
duration: 200
}
}
}
exit: Transition {
ParallelAnimation {
NumberAnimation {
property: "opacity";
from: 1.0
to: 0.0;
duration: 200
}
NumberAnimation {
property: "scale";
from: 1.0
to: 0.0;
duration: 200
}
}
}
//=============================================================================================================
// Init des données de la boite de dialogue
//=============================================================================================================
function init()
{
var req, res;
var nb;
var i;
var libelle_test;
flag_init = true; // On est obligé de le renseigne manuellement car la fonction 'init()' est appelée à chaque ouverture (et la valeur par défaut, qu'à l'ouverture initiale...)
console.log("id_variable="+id_variable)
grille_alarmes.model.clear();
req = 'SELECT Cle_primaire, '
req += ' Nom, ';
req += ' Test, ';
req += ' Seuil, ';
req += ' Etat_test ';
req += ' FROM TB_Events ';
req += ' WHERE Validite=1 ';
req += ' AND Alarme_source>0 ';
req += ' AND Type_source=' + type_source + ' ';
req += ' AND ID_Source=' + id_source + ' ';
req += ' AND ID_Variable=' + id_variable + ' ';
req += ' ORDER BY Cle_primaire ';
//console.log("Requête = [ " + req + "; ]");
nb = bdd.requete(req);
for (i = 0; i < nb; i ++)
{
res = bdd.lecture_ligne();
if (!res.isEmpty)
{
libelle_test = titre_widget;
switch(Number(res[2]))
{
case shipheart_test_non_defini:
default:
libelle_test += " ? "
break;
case shipheart_test_strictement_inferieur:
libelle_test += " &lt; "
break;
case shipheart_test_inferieur_ou_egal:
libelle_test += " &le; "
break;
case shipheart_test_egal:
libelle_test += " = "
break;
case shipheart_test_superieur_ou_egal:
libelle_test += " &ge; "
break;
case shipheart_test_strictement_superieur:
libelle_test += " &gt; "
break;
}
libelle_test += Number(res[3]) + " " + unite;
grille_alarmes.model.append({
"cle_primaire": Number(res[0]),
"nom_event": res[1],
"libelle_test": libelle_test,
"etat_test": Number(res[4])
})
}
}
timer_raffraichissement_parametres.start();
flag_init = false;
}
//=============================================================================================================
// Mise à jour des données de la boite de dialogue
//=============================================================================================================
function mise_a_jour()
{
var req, res;
var nb;
var i, j;
req = 'SELECT Cle_primaire, '
req += ' Etat_test ';
req += ' FROM TB_Events ';
req += ' WHERE Validite=1 ';
req += ' AND Alarme_source>0 ';
req += ' AND Type_source=' + type_source + ' ';
req += ' AND ID_Source=' + id_source + ' ';
req += ' AND ID_Variable=' + id_variable + ' ';
req += ' ORDER BY Cle_primaire ';
//console.log("Requête = [ " + req + "; ]");
nb = bdd.requete(req);
for (i = 0; i < nb; i ++)
{
res = bdd.lecture_ligne();
if (!res.isEmpty)
{
// Recherche dans le modèle la clé primaire de la ligne lue :
for (j = 0; j < grille_alarmes.model.count; j ++)
{
if (grille_alarmes.model.get(i).cle_primaire === Number(res[0]))
{
grille_alarmes.model.set(i, {"etat_test": Number(res[1]) }); // Mise à jour de l'état du test
break;
}
}
}
}
}
}
+551
View File
@@ -0,0 +1,551 @@
import QtQuick 2.12
import QtQuick.Controls 2.12
Popup {
id: boite_dialogue_minuteur
// Propriétés fixes :
readonly property double pourcentage_largeur_page_popup: 0.8
readonly property double pourcentage_hauteur_page_popup: 0.8
readonly property int padding_popup: 20
readonly property double pourcentage_boite_dialogue_titre: 0.2
readonly property double pourcentage_boite_dialogue_message: 1 - pourcentage_boite_dialogue_titre - pourcentage_boite_dialogue_ligne_boutons
readonly property double pourcentage_boite_dialogue_ligne_boutons: 0.15
readonly property int bouton_hauteur: 60
readonly property double pourcentage_zone_tumblers: 0.75
readonly property int bouton_largeur: 200
readonly property int espace_tumblers: 20
// Propriétés :
property bool flag_init: true
property int valeur_tumbler_heure: 0
property int valeur_tumbler_minute: 0
property int valeur_tumbler_seconde: 0
// Propriétés alias :
//-------------------------------------------------
// Signaux :
//-------------------------------------------------
signal clicked(int bouton, int heure, int minute, int seconde) // Valeurs à lire lorsque le "OnClicked' est activé
//-------------------------------------------------
// Ouverture :
//-------------------------------------------------
onAboutToShow: { // Activé une fois quand le popup va être affiché
init(); // L'init n'a d'utilité que lorsqu'on va l'afficher...
}
onOpened: { // Quand la page est affichée
mise_a_jour(); // Nb : si on le fait à la suite de l'init, la première maj ne se fait pas car la page n'est pas encore ouverte...
}
//-------------------------------------------------
// Fermeture :
//-------------------------------------------------
onAboutToHide: {
}
x: (shipheart_largeur_page * (1 - pourcentage_largeur_page_popup)) / 2;
y: (shipheart_hauteur_page * (1 - pourcentage_hauteur_page_popup)) / 2;
width: shipheart_largeur_page * pourcentage_largeur_page_popup;
height: shipheart_hauteur_page * pourcentage_hauteur_page_popup;
padding: padding_popup
modal: true
// focus: true // SDIR # 224 : Il ne faut PAS activer le focus. Sinon, à la fermeture, le focus précédent est réactivé (comme le dernier TextField par exemple)
closePolicy: Popup.NoAutoClose //Popup.CloseOnPressOutside
background: Rectangle {
color: shipheart_couleur_fond_boite_dialogue_niveau_0
radius: 20
}
Column {
id: affichage_boite_dialogue
spacing: 20
//=========================================================================
// Titre :
//=========================================================================
Rectangle {
id: titre_boite_dialogue
width: boite_dialogue_minuteur.width - (2 * padding_popup)
height: (boite_dialogue_minuteur.height - (2 * padding_popup) - (2 * affichage_boite_dialogue.spacing)) * pourcentage_boite_dialogue_titre
color: "transparent"
Image {
id: image_titre
source: "/images/sablier.png"
sourceSize.height: 100
anchors {
verticalCenter: parent.verticalCenter
horizontalCenter: parent.horizontalCenter
}
}
}
//=========================================================================
// Zone principale :
//=========================================================================
Rectangle {
id: zone_principale
width: boite_dialogue_minuteur.width - (2 * padding_popup)
height: (boite_dialogue_minuteur.height - (2 * padding_popup) - (2 * affichage_boite_dialogue.spacing)) * pourcentage_boite_dialogue_message
color: "transparent"
//------------------------------------------
// Tumblers de sélection manuelle :
//------------------------------------------
Row {
id: zone_tumblers
width: parent.width * pourcentage_zone_tumblers
height: parent.height
spacing: espace_tumblers
//-----------------------------------
// Heure :
//-----------------------------------
Rectangle {
width: zone_tumblers.width * 0.3
height: zone_tumblers.height
color: "transparent"
Text {
anchors.top: parent.top
width: parent.width
height: parent.height - tumbler_heure.height
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
text: qsTr("Heures")
color: couleur_blanc_casse
font.pixelSize: 18
}
Tumbler {
id: tumbler_heure
width: parent.width
height: parent.height * 0.85
anchors {
bottom: parent.bottom
}
visibleItemCount: 5
model: ListModel { }
delegate: bouton_heure
Component {
id: bouton_heure
Rectangle {
color: couleur_gris_fonce
opacity: 1.0 - Math.abs(Tumbler.displacement) / (Tumbler.tumbler.visibleItemCount / 2)
Text {
anchors.fill: parent
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
opacity: 1.0 - Math.abs(Tumbler.displacement) / (Tumbler.tumbler.visibleItemCount / 2)
text: model.libelle
color: (index === tumbler_heure.currentIndex) ? couleur_blanc_casse : couleur_gris_pale
font.family: "Montserrat"
font.pixelSize: 26
font.bold: (index === tumbler_heure.currentIndex)
}
}
}
background: Rectangle {
color: couleur_gris_tres_fonce
radius: shipheart_grand_rayon_bouton
}
}
}
//-----------------------------------
// Minute :
//-----------------------------------
Rectangle {
width: zone_tumblers.width * 0.3
height: zone_tumblers.height
color: "transparent"
Text {
anchors.top: parent.top
width: parent.width
height: parent.height - tumbler_minute.height
visible: (tumbler_heure.model.get(tumbler_heure.currentIndex) !== undefined) ? // Sinon, génère un warning au démarrage...
(tumbler_heure.model.get(tumbler_heure.currentIndex).valeur <= 23) :
true
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
text: qsTr("Minutes")
color: couleur_blanc_casse
font.pixelSize: 18
}
Tumbler {
id: tumbler_minute
width: parent.width
height: parent.height * 0.85
anchors {
bottom: parent.bottom
}
visibleItemCount: 5
model: ListModel { }
delegate: bouton_minute
opacity : (tumbler_heure.model.get(tumbler_heure.currentIndex) !== undefined) ? // Sinon, génère un warning au démarrage...
((tumbler_heure.model.get(tumbler_heure.currentIndex).valeur <= 23) ? 1.0 : 0) :
1.0
Component {
id: bouton_minute
Rectangle {
color: couleur_gris_fonce
opacity: 1.0 - Math.abs(Tumbler.displacement) / (Tumbler.tumbler.visibleItemCount / 2)
Text {
anchors.fill: parent
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
opacity: 1.0 - Math.abs(Tumbler.displacement) / (Tumbler.tumbler.visibleItemCount / 2)
text: model.libelle
color: (index === tumbler_minute.currentIndex) ? couleur_blanc_casse : couleur_gris_pale
font.family: "Montserrat"
font.pixelSize: 26
font.bold: (index === tumbler_minute.currentIndex)
}
}
}
background: Rectangle {
color: couleur_gris_tres_fonce
radius: shipheart_grand_rayon_bouton
}
}
}
//-----------------------------------
// Secondes :
//-----------------------------------
Rectangle {
width: zone_tumblers.width * 0.3
height: zone_tumblers.height
color: "transparent"
Text {
anchors.top: parent.top
width: parent.width
height: parent.height - tumbler_minute.height
visible: (tumbler_heure.model.get(tumbler_heure.currentIndex) !== undefined) ? // Sinon, génère un warning au démarrage...
(tumbler_heure.model.get(tumbler_heure.currentIndex).valeur <= 23) :
true
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
text: qsTr("Secondes")
color: couleur_blanc_casse
font.pixelSize: 18
}
Tumbler {
id: tumbler_seconde
width: parent.width
height: parent.height * 0.85
anchors {
bottom: parent.bottom
}
visibleItemCount: 5
model: ListModel { }
delegate: bouton_seconde
opacity : (tumbler_heure.model.get(tumbler_heure.currentIndex) !== undefined) ? // Sinon, génère un warning au démarrage...
((tumbler_heure.model.get(tumbler_heure.currentIndex).valeur <= 23) ? 1.0 : 0) :
1.0
Component {
id: bouton_seconde
Rectangle {
color: couleur_gris_fonce
opacity: 1.0 - Math.abs(Tumbler.displacement) / (Tumbler.tumbler.visibleItemCount / 2)
Text {
anchors.fill: parent
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
opacity: 1.0 - Math.abs(Tumbler.displacement) / (Tumbler.tumbler.visibleItemCount / 2)
text: model.libelle
color: (index === tumbler_seconde.currentIndex) ? couleur_blanc_casse : couleur_gris_pale
font.family: "Montserrat"
font.pixelSize: 26
font.bold: (index === tumbler_seconde.currentIndex)
}
}
}
background: Rectangle {
color: couleur_gris_tres_fonce
radius: shipheart_grand_rayon_bouton
}
}
}
}
//------------------------------------------
// Boutons de droite :
//------------------------------------------
Column {
id: colonne_boutons
height: parent.height
width: parent.width * (1 - pourcentage_zone_tumblers)
anchors.right: parent.right
spacing: 5
Repeater {
model: ListModel { id: modele_boutons_preprogrammes }
// Bouton proprement dit :
Rectangle {
width: parent.width
height: (colonne_boutons.height - ((modele_boutons_preprogrammes.count - 1) * colonne_boutons.spacing)) / modele_boutons_preprogrammes.count
radius: shipheart_rayon_bouton
color: couleur_gris_tres_fonce
Label {
anchors.centerIn: parent
text: model.libelle
color: couleur_blanc_casse
font.pixelSize:18
}
MouseArea {
anchors.fill: parent
onClicked: {
shipheart_clic();
valeur_tumbler_heure = model.heure;
valeur_tumbler_minute = model.minute;
valeur_tumbler_seconde = model.seconde;
mise_a_jour();
}
}
}
}
}
}
//=========================================================================
// Boutons de bas de fenêtre :
//=========================================================================
Row {
height: (boite_dialogue_minuteur.height - (2 * padding_popup) - (2 * affichage_boite_dialogue.spacing)) * pourcentage_boite_dialogue_ligne_boutons
anchors.horizontalCenter: parent.horizontalCenter
spacing: 20
//.......................................................
// Bouton Annuler :
//.......................................................
Rectangle {
id: bouton_annuler
width: bouton_largeur
height: bouton_hauteur
anchors.verticalCenter: parent.verticalCenter
radius: shipheart_rayon_bouton
color: couleur_gris_tres_fonce
Label {
id: bouton_annuler_label
anchors.centerIn: parent
text: qsTr("Annuler")
color: couleur_blanc_casse
font.pixelSize:20
}
MouseArea {
anchors.fill: parent
onClicked: {
shipheart_clic();
boite_dialogue_minuteur.clicked(shipheart_boite_dialogue_bouton_2, 0, 0, 0);
}
}
}
//.......................................................
// Bouton Ok / Démarrer :
//.......................................................
Rectangle {
id: bouton_ok
width: bouton_largeur
height: bouton_hauteur
anchors.verticalCenter: parent.verticalCenter
radius: shipheart_rayon_bouton
color: couleur_gris_tres_fonce
Label {
anchors.centerIn: parent
text: qsTr("Démarrer")
color: mousearea_demarrer.enabled ? couleur_blanc_casse : couleur_gris_moyen
font.pixelSize:20
}
MouseArea {
id: mousearea_demarrer
anchors.fill: parent
enabled: (!flag_init) &&
( (tumbler_heure.model.get(tumbler_heure.currentIndex).valeur !== 0)
|| (tumbler_minute.model.get(tumbler_minute.currentIndex).valeur !== 0)
|| (tumbler_seconde.model.get(tumbler_seconde.currentIndex).valeur !== 0) )
onClicked: {
shipheart_clic();
boite_dialogue_minuteur.clicked(shipheart_boite_dialogue_bouton_1,
tumbler_heure.model.get(tumbler_heure.currentIndex).valeur,
tumbler_minute.model.get(tumbler_minute.currentIndex).valeur,
tumbler_seconde.model.get(tumbler_seconde.currentIndex).valeur);
}
}
}
}
}
//=============================================================================================================
// Transitions d'ouverture et de fermeture de la boite de dialogue
//=============================================================================================================
enter: Transition {
ParallelAnimation {
NumberAnimation {
property: "opacity"
from: 0.0
to: 1.0
//easing.type: Easing.InElastic
duration: 200
}
NumberAnimation {
property: "scale"
from: 0.0
to: 1.0
// easing.type: Easing.OutBack
duration: 200
}
}
}
exit: Transition {
ParallelAnimation {
NumberAnimation {
property: "opacity"
from: 1.0
to: 0.0
duration: 200
}
NumberAnimation {
property: "scale"
from: 1.0
to: 0.0
duration: 200
}
}
}
//=============================================================================================================
// Init des données de la boite de dialogue
//=============================================================================================================
function init()
{
var i;
flag_init = true; // On est obligé de le renseigne manuellement car la fonction 'init()' est appelée à chaque ouverture (et la valeur par défaut, qu'à l'ouverture initiale...)
//-------------------------------------
// Heures :
//-------------------------------------
tumbler_heure.model.clear();
for (i = 0; i < 24; i ++)
{
tumbler_heure.model.append( { "valeur": i, "libelle": ((i < 10) ? "0"+i : ""+i)} );
}
//-------------------------------------
// Minutes :
//-------------------------------------
tumbler_minute.model.clear();
for (i = 0; i < 60; i ++)
{
tumbler_minute.model.append( { "valeur": i, "libelle": ((i < 10) ? "0"+i : ""+i) } );
}
//-------------------------------------
// secondes :
//-------------------------------------
tumbler_seconde.model.clear();
for (i = 0; i < 60; i ++)
{
tumbler_seconde.model.append( { "valeur": i, "libelle": ((i < 10) ? "0"+i : ""+i) } );
}
modele_boutons_preprogrammes.clear();
modele_boutons_preprogrammes.append( {"libelle": "00:01:00", "heure": 0, "minute": 1, "seconde": 0 } );
modele_boutons_preprogrammes.append( {"libelle": "00:03:00", "heure": 0, "minute": 3, "seconde": 0 } );
modele_boutons_preprogrammes.append( {"libelle": "00:05:00", "heure": 0, "minute": 5, "seconde": 0 } );
modele_boutons_preprogrammes.append( {"libelle": "00:15:00", "heure": 0, "minute": 15, "seconde": 0 } );
modele_boutons_preprogrammes.append( {"libelle": "00:30:00", "heure": 0, "minute": 30, "seconde": 0 } );
modele_boutons_preprogrammes.append( {"libelle": "01:00:00", "heure": 1, "minute": 0, "seconde": 0 } );
flag_init = false;
}
//=============================================================================================================
// Mise à jour des données de la boite de dialogue
//=============================================================================================================
function mise_a_jour()
{
var i;
//-------------------------------------
// Heures :
//-------------------------------------
// Rercherche de la valeur courante :
for (i = 0; i < tumbler_heure.model.count; i ++)
{
if (tumbler_heure.model.get(i).valeur === valeur_tumbler_heure)
{
tumbler_heure.currentIndex = i;
break;
}
}
//-------------------------------------
// Minutes :
//-------------------------------------
// Rercherche de la valeur courante :
for (i = 0; i < tumbler_minute.model.count; i ++)
{
if (tumbler_minute.model.get(i).valeur === valeur_tumbler_minute)
{
tumbler_minute.currentIndex = i;
break;
}
}
//-------------------------------------
// Secondes :
//-------------------------------------
// Rercherche de la valeur courante :
for (i = 0; i < tumbler_seconde.model.count; i ++)
{
if (tumbler_seconde.model.get(i).valeur === valeur_tumbler_seconde)
{
tumbler_seconde.currentIndex = i;
break;
}
}
}
}
+202
View File
@@ -0,0 +1,202 @@
import QtQuick 2.12
import QtQuick.Controls 2.12
Popup {
id: boite_dialogue_nettoyage_ecran
// Propriétés fixes :
readonly property double pourcentage_largeur_page_popup: 0.8
readonly property double pourcentage_hauteur_page_popup: 0.7
readonly property int padding_popup: 50
readonly property double pourcentage_boite_dialogue_titre: 0.2
readonly property double pourcentage_boite_dialogue_message: 1 - pourcentage_boite_dialogue_titre - pourcentage_boite_dialogue_ligne_boutons
readonly property double pourcentage_boite_dialogue_ligne_boutons: 0.30
readonly property int bouton_largeur: 500
readonly property int step_timer_sortie: 10 // en ms
// Propriétés :
property int valeur_timer_sortie: 0
// Propriétés alias :
// Signaux :
x: (shipheart_largeur_page * (1 - pourcentage_largeur_page_popup)) / 2;
y: (shipheart_hauteur_page * (1 - pourcentage_hauteur_page_popup)) / 2;
width: shipheart_largeur_page * pourcentage_largeur_page_popup;
height: shipheart_hauteur_page * pourcentage_hauteur_page_popup;
padding: padding_popup
modal: true
// focus: true // SDIR # 224 : Il ne faut PAS activer le focus. Sinon, à la fermeture, le focus précédent est réactivé (comme le dernier TextField par exemple)
closePolicy: Popup.NoAutoClose //Popup.CloseOnPressOutside
background: Rectangle {
color: shipheart_couleur_fond_boite_dialogue_niveau_0
radius: 20
}
Timer {
id: timer_sortie
interval: step_timer_sortie
triggeredOnStart: false
repeat: true
onTriggered: {
if (valeur_timer_sortie < shipheart_mouse_press_and_hold_interval_tres_long) valeur_timer_sortie+= step_timer_sortie;
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_sortie.running = false;
valeur_timer_sortie = 0;
shipheart_clic();
boite_dialogue_nettoyage_ecran.close();
}
}
}
Column {
id: affichage_boite_dialogue_nettoyage_ecran
spacing: 20
//=========================================================================
// Titre :
//=========================================================================
Rectangle {
id: titre_boite_dialogue_nettoyage_ecran
width: boite_dialogue_nettoyage_ecran.width - (2 * padding_popup)
height: (boite_dialogue_nettoyage_ecran.height - (2 * padding_popup) - (2 * affichage_boite_dialogue_nettoyage_ecran.spacing)) * pourcentage_boite_dialogue_titre
color: "transparent"
Text {
text: qsTr("Nettoyage de l'écran")
anchors.fill: parent
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
wrapMode: Text.Wrap
color: couleur_blanc_casse
font.pixelSize:40
font.weight: Font.Bold
}
}
//=========================================================================
// Message :
//=========================================================================
Rectangle {
id: message_boite_dialogue_nettoyage_ecran
width: boite_dialogue_nettoyage_ecran.width - (2 * padding_popup)
height: (boite_dialogue_nettoyage_ecran.height - (2 * padding_popup) - (2 * affichage_boite_dialogue_nettoyage_ecran.spacing)) * pourcentage_boite_dialogue_message
color: "transparent"
Text {
anchors.fill: parent
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
wrapMode: Text.Wrap
text: qsTr("L'écran est actuellement figé afin de vous permettre de le nettoyer sans risque de déclencher des actions non désirées.<br /><br />Maintenez le bouton ci-dessous pendant plus de %1 secondes pour sortir de ce mode.").arg(shipheart_mouse_press_and_hold_interval_tres_long / 1000)
color: couleur_blanc_casse
font.pixelSize:18
}
}
//=========================================================================
// Boutons :
//=========================================================================
Row {
height: (boite_dialogue_nettoyage_ecran.height - (2 * padding_popup) - (2 * affichage_boite_dialogue_nettoyage_ecran.spacing)) * pourcentage_boite_dialogue_ligne_boutons
anchors.horizontalCenter: parent.horizontalCenter
spacing: 20
//--------------------------------------------------------------------
// Bouton Sortie :
Rectangle {
id: bouton_sortie
width: bouton_largeur
height: parent.height * 0.5
anchors.verticalCenter: parent.verticalCenter
radius: shipheart_rayon_bouton
color: couleur_gris_tres_fonce
Label {
id: bouton_sortie_label
anchors.centerIn: parent
text: qsTr("Sortie")
color: couleur_blanc_casse
font.pixelSize:20
}
// Barre grandissante pour montrer durée d'appui vs tempo nécessaire :
Rectangle {
width: (valeur_timer_sortie / shipheart_mouse_press_and_hold_interval_tres_long) * parent.width
height: parent.height
color: couleur_gris_tres_pale_transparent
radius: shipheart_rayon_bouton
}
MouseArea {
anchors.fill: parent
onPressed: { // Déclenché lors de l'appui inital
valeur_timer_sortie = 0;
timer_sortie.running = true;
}
onClicked: { // Déclenché si l'appui n'a pas été suffisement long pour avoir un Press&Hold :
timer_sortie.running = false;
valeur_timer_sortie = 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_sortie.running = false;
valeur_timer_sortie = 0;
}
}
}
}
}
//=============================================================================================================
// Transitions d'ouverture et de fermeture de la boite de dialogue
//=============================================================================================================
enter: Transition {
ParallelAnimation {
NumberAnimation {
property: "opacity"
from: 0.0
to: 1.0
//easing.type: Easing.InElastic
duration: 200
}
NumberAnimation {
property: "scale"
from: 0.0
to: 1.0
// easing.type: Easing.OutBack
duration: 200
}
}
}
exit: Transition {
ParallelAnimation {
NumberAnimation {
property: "opacity"
from: 1.0
to: 0.0
duration: 200
}
NumberAnimation {
property: "scale"
from: 1.0
to: 0.0
duration: 200
}
}
}
}
+470
View File
@@ -0,0 +1,470 @@
import QtQuick 2.12
import QtQuick.Controls 2.12
import"fonctions_date_heure.js" as Fonctions_date_heure
Popup {
id: boite_dialogue_reveils
// Propriétés fixes :
readonly property double pourcentage_largeur_page_popup: 0.7
readonly property double pourcentage_hauteur_page_popup: 0.8
readonly property int padding_popup: 20
readonly property double pourcentage_zone_titre: 0.20
readonly property double pourcentage_zone_principale: 1 - pourcentage_zone_titre - pourcentage_zone_boutons
readonly property double pourcentage_zone_boutons: 0.15
readonly property int bouton_hauteur: 60
readonly property int hauteur_ligne_formulaire: 60
readonly property double pourcentage_zone_validite: 0.15
readonly property int bouton_largeur: 200
// Propriétés :
property bool flag_init: true
property int index_condition_horaire_en_cours: -1
// Propriétés alias :
//-------------------------------------------------
// Signaux :
//-------------------------------------------------
signal clicked(int bouton) // Valeurs à lire lorsque le "OnClicked' est activé
//-------------------------------------------------
// Ouverture :
//-------------------------------------------------
onAboutToShow: { // Activé une fois quand le popup va être affiché
init(); // L'init n'a d'utilité que lorsqu'on va l'afficher...
}
onOpened: { // Quand la page est affichée
}
//-------------------------------------------------
// Fermeture :
//-------------------------------------------------
onAboutToHide: {
}
x: (shipheart_largeur_page * (1 - pourcentage_largeur_page_popup)) / 2;
y: (shipheart_hauteur_page * (1 - pourcentage_hauteur_page_popup)) / 2;
width: shipheart_largeur_page * pourcentage_largeur_page_popup;
height: shipheart_hauteur_page * pourcentage_hauteur_page_popup;
padding: padding_popup
modal: true
// focus: true // SDIR # 224 : Il ne faut PAS activer le focus. Sinon, à la fermeture, le focus précédent est réactivé (comme le dernier TextField par exemple)
closePolicy: Popup.NoAutoClose //Popup.CloseOnPressOutside
background: Rectangle {
color: shipheart_couleur_fond_boite_dialogue_niveau_0
radius: 20
}
Column {
id: affichage_boite_dialogue
spacing: 20
//=========================================================================
// Titre :
//=========================================================================
Rectangle {
id: titre_boite_dialogue
width: boite_dialogue_reveils.width - (2 * padding_popup)
height: (boite_dialogue_reveils.height - (2 * padding_popup) - (2 * affichage_boite_dialogue.spacing)) * pourcentage_zone_titre
color: "transparent"
Image {
id: image_titre
source: "/images/reveil.png"
sourceSize.height: 100
anchors {
verticalCenter: parent.verticalCenter
horizontalCenter: parent.horizontalCenter
}
visible: true
}
}
//=========================================================================
// Zone principale :
//=========================================================================
Rectangle {
id: zone_principale
width: boite_dialogue_reveils.width - (2 * padding_popup)
height: (boite_dialogue_reveils.height - (2 * padding_popup) - (2 * affichage_boite_dialogue.spacing)) * pourcentage_zone_principale
color: "transparent"
GridView {
id: grille_conditions_horaires
width: parent.width
height: parent.height
cellWidth: parent.width;
cellHeight: hauteur_ligne_formulaire
snapMode: GridView.SnapToRow // L'arret de l'affichage se fera sur une rangée entière
clip: true // Nécessaire pour que les Delegates s'arretent à l'intérieur de la GridView
model: ListModel { }
delegate: Rectangle {
width: grille_conditions_horaires.cellWidth
height: grille_conditions_horaires.cellHeight
color: "transparent"
//.......................................................
// Bouton principal :
//.......................................................
Rectangle {
height: parent.height - 10
width: (!model.bouton) ? ((parent.width * (1 - pourcentage_zone_validite)) - 10) : (parent.width)
// visible: (!model.bouton)
color: shipheart_couleur_fond_bouton
radius: shipheart_rayon_bouton
anchors {
left: parent.left
verticalCenter: parent.verticalCenter
}
Text {
anchors.fill: parent
verticalAlignment: Qt.AlignVCenter
horizontalAlignment: (!model.bouton) ? Qt.AlignLeft : Qt.AlignHCenter
leftPadding: (!model.bouton) ? 20 : 0
text: model.libelle
color: couleur_blanc_casse
font.pixelSize: (!model.bouton) ? 18 : (hauteur_ligne_formulaire * 0.8)
}
MouseArea{
anchors.fill : parent
onClicked: {
shipheart_clic();
if (model.bouton) // Cas du bouton '+'
{
index_condition_horaire_en_cours = -1; // Marqueur pour indiquer l'ajout d'un élément
boite_selection_condition_horaire.valeur_tumbler_jour = 8; // Tous les jours par défaut
boite_selection_condition_horaire.valeur_tumbler_heure = 0;
boite_selection_condition_horaire.valeur_tumbler_minute = 0;
boite_selection_condition_horaire.boutonSupprimer = false;
}
else // Cas d'une modification de condition existante :
{
index_condition_horaire_en_cours = index;
boite_selection_condition_horaire.valeur_tumbler_jour = model.jour;
boite_selection_condition_horaire.valeur_tumbler_heure = model.heure;
boite_selection_condition_horaire.valeur_tumbler_minute = model.minute;
boite_selection_condition_horaire.boutonSupprimer = true;
}
boite_selection_condition_horaire.x = 0; // Bloque le déplacement latéral classique de la boite de dialogue puisqu'on l'appelle d'une boite déjà ouverte...
boite_selection_condition_horaire.y = titre_boite_dialogue.height; // Idem en Y mais avec un décalage permettant de toujours voir le titre de la boite appelante....
boite_selection_condition_horaire.open();
}
}
}
//.......................................................
// Bouton de validite :
//.......................................................
Rectangle {
height: parent.height - 10
width: (parent.width * pourcentage_zone_validite)
visible: (!model.bouton)
color: shipheart_couleur_fond_bouton
radius: shipheart_rayon_bouton
anchors {
right: parent.right
verticalCenter: parent.verticalCenter
}
Image {
id: image_info_paramètres
source: (model.validite === 0) ? "/images/cloche_barree.png" : "/images/cloche_verte.png"
sourceSize.height: parent.height * 0.5
anchors {
verticalCenter: parent.verticalCenter
horizontalCenter: parent.horizontalCenter
}
}
MouseArea{
anchors.fill : parent
onClicked: {
shipheart_clic();
if (grille_conditions_horaires.model.get(index).validite === 0)
{
grille_conditions_horaires.model.set(index, {"validite": 1} );
}
else grille_conditions_horaires.model.set(index, {"validite": 0} );
}
}
}
}
} // Fin de Gridview
}
//=========================================================================
// Boutons de bas de fenêtre :
//=========================================================================
Row {
height: (boite_dialogue_reveils.height - (2 * padding_popup) - (2 * affichage_boite_dialogue.spacing)) * pourcentage_zone_boutons
anchors.horizontalCenter: parent.horizontalCenter
spacing: 20
//.......................................................
// Bouton Annuler :
//.......................................................
Rectangle {
id: bouton_annuler
width: bouton_largeur
height: bouton_hauteur
anchors.verticalCenter: parent.verticalCenter
radius: shipheart_rayon_bouton
color: couleur_gris_tres_fonce
Label {
id: bouton_annuler_label
anchors.centerIn: parent
text: qsTr("Annuler")
color: couleur_blanc_casse
font.pixelSize:20
}
MouseArea {
anchors.fill: parent
onClicked: {
shipheart_clic();
boite_dialogue_reveils.clicked(shipheart_boite_dialogue_bouton_2);
}
}
}
//.......................................................
// Bouton Ok / Enregistrer :
//.......................................................
Rectangle {
id: bouton_ok
width: bouton_largeur
height: bouton_hauteur
anchors.verticalCenter: parent.verticalCenter
radius: shipheart_rayon_bouton
color: couleur_gris_tres_fonce
Label {
anchors.centerIn: parent
text: qsTr("Enregistrer")
color: couleur_blanc_casse
font.pixelSize:20
}
MouseArea {
anchors.fill: parent
onClicked: {
shipheart_clic();
mise_a_jour();
}
}
}
}
}
//-------------------------------------------------------
// Boite de sélection d'une condition horaire :
//-------------------------------------------------------
Shipheart_boite_dialogue_condition_horaire {
id: boite_selection_condition_horaire
boutonOk: true
boutonAnnuler: true
onClicked: {
//console.log("Le Bouton [" + bouton + "] est appuyé");
//-------------------
// Bouton Ok :
//-------------------
if (bouton === shipheart_boite_dialogue_bouton_1)
{
//console.log("Jour = " + jour + ", Heure="+heure+" et Minute="+minute);
//----------------------------------------
// Cas d'une mise à jour :
if (index_condition_horaire_en_cours !== -1)
{
// Mise à jour de l'entrée du modèle avec les données saisies :
grille_conditions_horaires.model.set(index_condition_horaire_en_cours, {
"libelle": Fonctions_date_heure.nom_jour_semaine(jour) + " @ " + Fonctions_date_heure.nom_heure(heure, minute),
"jour": jour,
"heure": heure,
"minute": minute} );
}
//----------------------------------------
// Cas de l'ajout d'une condition :
else
{
grille_conditions_horaires.model.insert( (grille_conditions_horaires.model.count - 1), { // Insertion juste avant le '+'
"libelle": Fonctions_date_heure.nom_jour_semaine(jour) + " @ " + Fonctions_date_heure.nom_heure(heure, minute),
"jour": jour,
"heure": heure,
"minute": minute,
"validite": 1,
"bouton": false });
}
}
//-------------------
// Bouton Annuler :
//-------------------
if (bouton === shipheart_boite_dialogue_bouton_2) ; // Aucune action...
//-------------------
// Bouton Supprimer :
//-------------------
if (bouton === shipheart_boite_dialogue_bouton_3)
{
grille_conditions_horaires.model.remove(index_condition_horaire_en_cours, 1);
}
boite_selection_condition_horaire.close(); // Dans tous les cas... (notamment si ANNULER)
}
}
//=============================================================================================================
// Transitions d'ouverture et de fermeture de la boite de dialogue
//=============================================================================================================
enter: Transition {
ParallelAnimation {
NumberAnimation {
property: "opacity"
from: 0.0
to: 1.0
//easing.type: Easing.InElastic
duration: 200
}
NumberAnimation {
property: "scale"
from: 0.0
to: 1.0
// easing.type: Easing.OutBack
duration: 200
}
}
}
exit: Transition {
ParallelAnimation {
NumberAnimation {
property: "opacity"
from: 1.0
to: 0.0
duration: 200
}
NumberAnimation {
property: "scale"
from: 1.0
to: 0.0
duration: 200
}
}
}
//=============================================================================================================
// Init des données de la boite de dialogue
//=============================================================================================================
function init()
{
var req, res;
var i;
var conditions;
var condition
var jour;
var heure;
var minute;
var validite;
flag_init = true; // On est obligé de le renseigne manuellement car la fonction 'init()' est appelée à chaque ouverture (et la valeur par défaut, qu'à l'ouverture initiale...)
grille_conditions_horaires.model.clear();
req = 'SELECT Valeur FROM TB_Configuration WHERE Nom="Reveils" ';
//console.log("Requête = [ " + req + '; ]')
res = bdd.requete_unique(req);
var conditions_horaires = res[0];
if (conditions_horaires !== "")
{
conditions = conditions_horaires.split(';');
for (i = 0; i < conditions.length; i ++)
{
condition = conditions[i].split('/');
if (condition.length === 4)
{
jour = Number(condition[0]);
heure = Number(condition[1]);
minute = Number(condition[2]);
validite = Number(condition[3]);
//console.log("Condition #"+(i+1)+" : jour="+Fonctions_date_heure.nom_jour_semaine(jour)+" à "+ Fonctions_date_heure.nom_heure(heure, minute) );
grille_conditions_horaires.model.append( {
"libelle": Fonctions_date_heure.nom_jour_semaine(jour) + " @ " + Fonctions_date_heure.nom_heure(heure, minute),
"jour": jour,
"heure": heure,
"minute": minute,
"validite": validite,
"bouton": false });
}
}
}
// Ajout du bouton "+" :
grille_conditions_horaires.model.append( { "libelle": "+", "jour": 0, "heure": 0, "minute": 0, "validite": 0, "bouton": true });
flag_init = false;
}
//=============================================================================================================
// Mise à jour des données de la boite de dialogue
//=============================================================================================================
function mise_a_jour()
{
var req;
var conditions_horaires;
var flag_first;
var i;
conditions_horaires = "";
if ((grille_conditions_horaires.model.count - 1) > 0) // Le '-1' permet de neutraliser le bouton '+' qui est toujours présents
{
flag_first = true;
for (i = 0; i < grille_conditions_horaires.model.count; i ++) // Parcours du modèle existant
{
if (!grille_conditions_horaires.model.get(i).bouton) // Ne prend pas en compte le bouton '+'
{
if (flag_first) flag_first = false;
else conditions_horaires += ";"
conditions_horaires += grille_conditions_horaires.model.get(i).jour;
conditions_horaires += "/";
conditions_horaires += grille_conditions_horaires.model.get(i).heure;
conditions_horaires += "/";
conditions_horaires += grille_conditions_horaires.model.get(i).minute;
conditions_horaires += "/";
conditions_horaires += grille_conditions_horaires.model.get(i).validite;
}
}
}
else console.log("Aucune condition horaire n'a été configurée. La chaine 'Reveils' est laissée vide.")
//console.log("Conditions horaires = [ " + conditions_horaires + " ]")
req = 'UPDATE TB_Configuration SET Valeur="' + conditions_horaires + '" WHERE Nom="Reveils" ';
console.log("Requête = [ " + req + '; ]')
bdd.requete_unique(req);
boite_dialogue_reveils.clicked(shipheart_boite_dialogue_bouton_1);
}
}
+316
View File
@@ -0,0 +1,316 @@
import QtQuick 2.12
import QtQuick.Controls 2.12
Popup {
id: boite_dialogue_validation_securisee
// Propriétés fixes :
readonly property double pourcentage_largeur_page_popup: 0.6
readonly property double pourcentage_hauteur_page_popup: 0.7
readonly property int padding_popup: 50
readonly property double pourcentage_boite_dialogue_titre: 0.25
readonly property double pourcentage_boite_dialogue_message: 1 - pourcentage_boite_dialogue_titre - pourcentage_boite_dialogue_ligne_boutons
readonly property double pourcentage_boite_dialogue_ligne_boutons: 0.15
readonly property int bouton_largeur: 200
readonly property int step_timer_appui_long: 30 // en ms
// Propriétés :
property int valeur_timer_appui_long: 0
property int niveau: 0
property string message: "--"
property int type: 0 // Utilisé uniquement par la page appelante pour distinguer les différents appels dans la même page
// Propriétés alias :
property alias boutonOk: bouton_ok.visible
property alias boutonAnnuler: bouton_annuler.visible
property alias boutonSecurise: bouton_securise.visible
property alias boutonOkTexte: bouton_ok_label.text
property alias boutonAnnulerTexte: bouton_annuler_label.text
property alias boutonSecuriseTexte: bouton_securise_label.text
property alias alignementMessage: texte_message.horizontalAlignment
property alias imageTitreSource: image_titre.source
property alias affichageAscenseur: ascenseur.visible
// Signaux :
signal clicked(int bouton) // Valeur à lire lorsque le "OnClicked' est activé
//===========================================================
// 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;
if (mousearea_bouton_securise.pressed)
{
boite_dialogue_validation_securisee.clicked(shipheart_boite_dialogue_bouton_4);
}
}
}
}
x: (shipheart_largeur_page * (1 - pourcentage_largeur_page_popup)) / 2;
y: (shipheart_hauteur_page * (1 - pourcentage_hauteur_page_popup)) / 2;
width: shipheart_largeur_page * pourcentage_largeur_page_popup;
height: shipheart_hauteur_page * pourcentage_hauteur_page_popup;
padding: padding_popup
modal: true
// focus: true // SDIR # 224 : Il ne faut PAS activer le focus. Sinon, à la fermeture, le focus précédent est réactivé (comme le dernier TextField par exemple)
closePolicy: Popup.NoAutoClose //Popup.CloseOnPressOutside
background: Rectangle {
radius: 20
color: {
switch(boite_dialogue_validation_securisee.niveau)
{
default:
case 0: color: shipheart_couleur_fond_boite_dialogue_niveau_0; break;
case 1: color: shipheart_couleur_fond_boite_dialogue_niveau_1; break;
case 2: color: shipheart_couleur_fond_boite_dialogue_niveau_2; break;
}
}
}
Column {
id: affichage_boite_dialogue
spacing: 20
//=========================================================================
// Titre :
//=========================================================================
Rectangle {
id: titre_boite_dialogue
width: boite_dialogue_validation_securisee.width - (2 * padding_popup)
height: (boite_dialogue_validation_securisee.height - (2 * padding_popup) - (2 * affichage_boite_dialogue.spacing)) * pourcentage_boite_dialogue_titre
color: "transparent"
Image {
id: image_titre
source: ""
sourceSize.height: parent.height
anchors {
verticalCenter: parent.verticalCenter
horizontalCenter: parent.horizontalCenter
}
visible: true
}
}
//=========================================================================
// Message :
//=========================================================================
Rectangle {
id: message_boite_dialogue
width: boite_dialogue_validation_securisee.width - (2 * padding_popup)
height: (boite_dialogue_validation_securisee.height - (2 * padding_popup) - (2 * affichage_boite_dialogue.spacing)) * pourcentage_boite_dialogue_message
color: "transparent"
Flickable {
anchors.fill: parent
anchors {
verticalCenter: parent.verticalCenter
}
contentWidth: texte_message.width
contentHeight: texte_message.height
flickableDirection: Flickable.VerticalFlick
clip: true
ScrollBar.vertical: ScrollBar { // Important car permet de voir que la partie affichée n'est pas complète dans le message...
id: ascenseur
policy: ScrollBar.AlwaysOn
width: 10
visible: false
}
TextEdit {
id: texte_message
width: message_boite_dialogue.width - ascenseur.width - 5 // Le '-5' permet de détacher le texte de l'ascenseur
height: Math.max(message_boite_dialogue.height, implicitHeight) // Permet d'avoir la hauteur de la zone si le texte est "court" (et donc de centrer verticalement) puis la hauteur totale si le texte est "long" (et pouvoir se déplacer vers la fin et pas centré sur le milieu !)
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
readOnly:true
wrapMode: Text.Wrap
textFormat: Text.RichText
text: boite_dialogue_validation_securisee.message
color: {
switch(boite_dialogue_validation_securisee.niveau)
{
default:
case 0: shipheart_couleur_texte_boite_dialogue_niveau_0; break;
case 1: shipheart_couleur_texte_boite_dialogue_niveau_1; break;
case 2: shipheart_couleur_texte_boite_dialogue_niveau_2; break;
}
}
font.pixelSize:18
}
}
}
//=========================================================================
// Boutons :
//=========================================================================
Row {
height: (boite_dialogue_validation_securisee.height - (2 * padding_popup) - (2 * affichage_boite_dialogue.spacing)) * pourcentage_boite_dialogue_ligne_boutons
anchors.horizontalCenter: parent.horizontalCenter
spacing: 20
//--------------------------------------------------------------------
// Bouton Ok :
Rectangle {
id: bouton_ok
width: bouton_largeur
height: parent.height
anchors.verticalCenter: parent.verticalCenter
radius: shipheart_rayon_bouton
color: couleur_gris_tres_fonce
visible: true
Label {
id: bouton_ok_label
anchors.centerIn: parent
text: qsTr("OK")
color: couleur_blanc_casse
font.pixelSize:20
}
MouseArea {
anchors.fill: parent
onClicked: {
shipheart_clic();
boite_dialogue_validation_securisee.clicked(shipheart_boite_dialogue_bouton_1);
}
}
}
//--------------------------------------------------------------------
// Bouton Annuler :
Rectangle {
id: bouton_annuler
width: bouton_largeur
height: parent.height
anchors.verticalCenter: parent.verticalCenter
radius: shipheart_rayon_bouton
color: couleur_gris_tres_fonce
visible: false
Label {
id: bouton_annuler_label
anchors.centerIn: parent
text: qsTr("Annuler")
color: couleur_blanc_casse
font.pixelSize:20
}
MouseArea {
anchors.fill: parent
onClicked: {
shipheart_clic();
boite_dialogue_validation_securisee.clicked(shipheart_boite_dialogue_bouton_2);
}
}
}
//--------------------------------------------------------------------
// Bouton Sécurisé :
Rectangle {
id: bouton_securise
width: bouton_largeur
height: parent.height
anchors.verticalCenter: parent.verticalCenter
radius: shipheart_rayon_bouton
color: couleur_gris_tres_fonce
visible: false
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_securise.pressed
}
Label {
id: bouton_securise_label
anchors.centerIn: parent
text: qsTr("---")
color: couleur_blanc_casse
font.pixelSize:20
}
MouseArea {
id: mousearea_bouton_securise
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;
}
}
}
}
}
enter: Transition {
ParallelAnimation {
NumberAnimation {
property: "opacity";
from: 0.0;
to: 1.0;
//easing.type: Easing.InElastic;
duration: 200
}
NumberAnimation {
property: "scale";
from: 0.0;
to: 1.0;
// easing.type: Easing.OutBack
duration: 200
}
}
}
exit: Transition {
ParallelAnimation {
NumberAnimation {
property: "opacity";
from: 1.0
to: 0.0;
duration: 200
}
NumberAnimation {
property: "scale";
from: 1.0
to: 0.0;
duration: 200
}
}
}
}
+177
View File
@@ -0,0 +1,177 @@
//-----------------------------------------------------------------------------------------------------
// Fichier définissant un bouton de type Batterie sur la page d'accueil
// A priori, appelé depuis "home.qml"
//-----------------------------------------------------------------------------------------------------
import QtQuick 2.12
//=======================================================
// Ensemble du bouton
//=======================================================
Rectangle {
id: bouton_batterie
readonly property int largeur_bouton: batteries_grid.cellWidth - espace_colonnes_secondaires;
readonly property int hauteur_bouton: batteries_grid.cellHeight - espace_lignes_secondaires;
readonly property int rayon_bouton: 5
readonly property double pourcentage_largeur_zone_affichage_nom: 0.85
readonly property int seuil_haut: 75 // utilisé pour changer de couleur orange/vert
readonly property int seuil_bas: 25 // utilisé pour changer de couleur rouge/orange
width: largeur_bouton
height: hauteur_bouton
color: "transparent" // On ne peut pas mettre l'opacité sur celui-ci, sinon tous ses enfants sont aussi semi-transparents...
//---------------------------------------------------------------------------------------
// Bandeau latéral avec tension nominale :
//---------------------------------------------------------------------------------------
Image {
id: bandeau_tension_batterie
source: (model.Tension_nominale === 12) ? "/images/batterie_12v.png" : "/images/batterie_24v.png"
sourceSize.height: parent.height
}
//---------------------------------------------------------------------------------------
// Bouton dans son ensemble :
//---------------------------------------------------------------------------------------
Rectangle {
anchors.fill: parent
radius: rayon_bouton
border.color: couleur_rouge
border.width: (model.Etat_systeme === 0) ? 0 : 3
color: shipheart_couleur_fond_bouton
MouseArea {
id: mouse_area_zone_parametres
anchors.fill: parent
width: largeur_bouton
height: hauteur_bouton
onClicked: {
shipheart_clic();
stackView.push("batterie.qml",
{
"id_batterie": model.Cle_primaire,
"flag_detail_batterie": true,
"nom":model.Nom
})
}
}
}
//-------------------------------------------------------
// Zone du nom (en haut à gauche) :
//-------------------------------------------------------
Rectangle {
id: zone_nom
x: bandeau_tension_batterie.width
width: (largeur_bouton * pourcentage_largeur_zone_affichage_nom) - bandeau_tension_batterie.width;
height: hauteur_bouton / 2;
color: "transparent"
Text {
text: model.Nom
anchors.fill: parent // Permet que le texte occupe tout l'espace du parent (et que le centrage vertical qui suit fonctionne...)
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignLeft
leftPadding: 10
font.pixelSize: 15
elide: Text.ElideRight
font.weight: Font.Bold
color: couleur_blanc_casse
}
}
//-------------------------------------------------------
// Zone de valeur absolue :
//-------------------------------------------------------
Rectangle {
id: zone_valeur
x: bandeau_tension_batterie.width
y: hauteur_bouton / 2;
width: (largeur_bouton * pourcentage_largeur_zone_affichage_nom) - bandeau_tension_batterie.width;
height: hauteur_bouton / 2;
color: "transparent"
visible: (model.Etat_systeme === 0)
Image {
source: "/images/coin_bas_droite_rouge.png"
sourceSize.height: 20
anchors {
bottom: parent.bottom
right: parent.right
}
visible: (model.Etat_systeme === 0) // SDIR #285
&& (model.Etat !== 0) // SDIR #285
}
Text {
text: (model.Tension).toFixed(2) + " V";
anchors {
verticalCenter: parent.verticalCenter
horizontalCenter: parent.horizontalCenter
}
font.pixelSize: 18
font.bold: true
color: couleur_blanc_casse
}
}
//-------------------------------------------------------
// Zone niveau relatif :
//-------------------------------------------------------
Rectangle {
id: zone_niveau_relatif
color: "transparent" // Sinon, le fond du rectangle est blanc...
x: largeur_bouton * pourcentage_largeur_zone_affichage_nom
width: largeur_bouton * (1 - pourcentage_largeur_zone_affichage_nom);
height: hauteur_bouton;
radius: shipheart_rayon_bouton
border.color: couleur_fond_bargraph_niveau_relatif()
border.width: 1
visible: (model.Etat_systeme === 0)
Text {
id:zone_texte_niveau_relatif
text: (model.Niveau).toFixed(0) + " %";
anchors.fill: parent // Permet que le texte occupe tout l'espace du parent (et que le centrage vertical qui suit fonctionne...)
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
font.pixelSize: 12
font.bold: (model.Niveau < 10) // soit < 10%
color: couleur_texte_niveau_relatif()
}
Rectangle {
id:zone_bargraph_niveau_relatif
color: couleur_fond_bargraph_niveau_relatif()
y: parent.height * (100 - model.Niveau) / 100
width: parent.width;
height: parent.height * model.Niveau / 100;
radius: shipheart_rayon_bouton
}
}
//----------------------------------------------------------
function couleur_texte_niveau_relatif()
{
switch(model.Logique)
{
case 1: return((model.Niveau >= seuil_haut) ? couleur_rouge : ((model.Niveau >= seuil_bas) ? couleur_orange : couleur_green_yellow))
case 0: return((model.Niveau >= seuil_haut) ? couleur_rouge : ((model.Niveau >= seuil_bas) ? couleur_green_yellow : couleur_rouge))
default: return((model.Niveau >= seuil_haut) ? couleur_green_yellow : ((model.Niveau >= seuil_bas) ? couleur_orange : couleur_rouge))
}
}
//----------------------------------------------------------
function couleur_fond_bargraph_niveau_relatif()
{
switch(model.Logique)
{
case 1: return((model.Niveau >= seuil_haut) ? couleur_rouge_transparent : ((model.Niveau >= seuil_bas) ? couleur_orange_transparent : couleur_vert_transparent))
case 0: return((model.Niveau >= seuil_haut) ? couleur_rouge_transparent : ((model.Niveau >= seuil_bas) ? couleur_vert_transparent : couleur_rouge_transparent))
default: return((model.Niveau >= seuil_haut) ? couleur_vert_transparent : ((model.Niveau >= seuil_bas) ? couleur_orange_transparent : couleur_rouge_transparent))
}
}
}
+162
View File
@@ -0,0 +1,162 @@
//-----------------------------------------------------------------------------------------------------
// Fichier définissant un bouton de type Capteur sur la page d'accueil
// A priori, appelé depuis "home.qml"
//-----------------------------------------------------------------------------------------------------
import QtQuick 2.12
//=======================================================
// Ensemble du bouton
//=======================================================
Rectangle {
id: bouton_capteur
readonly property int largeur_bouton: capteurs_grid.cellWidth - espace_colonnes_secondaires;
readonly property int hauteur_bouton: capteurs_grid.cellHeight - espace_lignes_secondaires;
readonly property int rayon_bouton: 5
width: largeur_bouton
height: hauteur_bouton
radius: rayon_bouton
color: "transparent" // On ne peut pas mettre l'opacité sur celui-ci, sinon tous ses enfants sont aussi semi-transparents...
//-------------------------------------------------------
// Fond de bouton :
Rectangle {
anchors.fill: parent
color: shipheart_couleur_fond_bouton
radius: rayon_bouton
border.color: couleur_rouge
border.width: (model.Etat_systeme === 0) ? 0 : 3
//-------------------------
// Triangle orange d'alerte :
Image {
source: "/images/coin_bas_droite_orange.png"
sourceSize.height: 20
anchors {
bottom: parent.bottom
right: parent.right
}
visible: (model.Etat_systeme === 0) && // SDIR #285
((model.Etat_capteur === shipheart_capteur_etat_alerte) ||
(model.Etat > 0) || // Nb alertes d'Events > 0
(model.Obsolete) )
}
//-------------------------
// Triangle rouge d'alarme :
Image {
source: "/images/coin_bas_droite_rouge.png"
sourceSize.height: 20
anchors {
bottom: parent.bottom
right: parent.right
}
visible: (model.Etat_systeme === 0) && // SDIR #285
(model.Etat_capteur === shipheart_capteur_etat_alarme)
}
}
//-------------------------------------------------------
// Nom du capteur :
Rectangle {
id: nom_capteur
width: parent.width
height: parent.height * 0.25;
color: "transparent"
Text {
text: model.Nom
anchors.fill: parent
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignLeft
leftPadding: 10
font.pixelSize: 15
font.weight: Font.Bold
color: couleur_blanc_casse
elide: Text.ElideRight // Pas de wrap car on ne peut afficher qu'une seule ligne quoi qu'il arrive...
}
}
//-------------------------------------------------------
// Valeur si aucune unité :
Rectangle {
id: valeur_capteur_sans_unite
anchors.top: nom_capteur.bottom
width: parent.width
height: parent.height - nom_capteur.height
color: "transparent"
visible: (model.Etat_systeme === 0) && (model.Unite === "")
Text {
anchors.fill: parent
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
padding: 10
text: model.Valeur
font.family: "Montserrat"
fontSizeMode: Text.HorizontalFit;
minimumPixelSize: 20;
font.pixelSize: 50
elide: Text.ElideRight
wrapMode: Text.Wrap
font.weight: Font.Bold
// color: (model.Etat === 0) ? couleur_blanc_casse : couleur_rouge
color: couleur_blanc_casse
}
}
//-------------------------------------------------------
// Valeur si unité :
Rectangle {
id: valeur_capteur_avec_unite
anchors.top: nom_capteur.bottom
width: parent.width
height: parent.height - nom_capteur.height
color: "transparent"
visible: (model.Etat_systeme === 0) && (model.Unite !== "")
Text {
id: valeur_actuelle
anchors {
verticalCenter: parent.verticalCenter
horizontalCenter: parent.horizontalCenter
horizontalCenterOffset: -20
}
text: model.Valeur
font.family: "Montserrat"
font.pixelSize: 50
font.weight: Font.Bold
// color: (model.Etat === 0) ? couleur_blanc_casse : couleur_rouge
color: couleur_blanc_casse
}
//-------------------------------------------------------
// Unite :
Text {
anchors {
baseline: valeur_actuelle.baseline
left: valeur_actuelle.right
leftMargin: 10
}
text: model.Unite
font.pixelSize: 15
color: couleur_gris_pale
}
}
//-------------------------------------------------------
// Zone de clic :
MouseArea { // Zone d'interaction Paramètres du reservoir
id: mouse_area_zone_parametres
anchors.fill: parent
onClicked: {
shipheart_clic();
stackView.push("capteur.qml", {"id_capteur": model.Cle_primaire})
}
}
}
+339
View File
@@ -0,0 +1,339 @@
//-----------------------------------------------------------------------------------------------------
// Fichier définissant un bouton de type Circuit sur la page d'accueil
// A priori, appelé depuis "home.qml"
//-----------------------------------------------------------------------------------------------------
import QtQuick 2.12
//=======================================================
// Ensemble du bouton
//=======================================================
Item {
id: bouton_circuit
// Paramètres d'affichage :
readonly property int largeur_bouton: circuits_grid.cellWidth - espace_colonnes_secondaires;
readonly property int hauteur_bouton: circuits_grid.cellHeight - espace_lignes_secondaires;
readonly property double pourcentage_largeur_zone_affichage_nom: 0.60
readonly property double pourcentage_largeur_zone_affichage_consigne: 0.25
readonly property int espace_interne: 5
// | zone affichage nom | cdes auto | consigne |
// | zone affichage intensité | | |
width: largeur_bouton
height: hauteur_bouton
Rectangle {
width: parent.width
height: parent.height
color: (model.Etat_systeme === 0) ? "transparent" : shipheart_couleur_fond_bouton // On ne peut pas mettre l'opacité sur celui-ci, sinon tous ses enfants sont aussi semi-transparents...
radius: shipheart_rayon_bouton
border.color: couleur_rouge
border.width: (model.Etat_systeme === 0) ? 0 : 3
//------------------------------------------------------------------------------------------
// Zone de parametres (toute la partie gauche, hors consigne et hors cde AUTO s'il y en a :
//------------------------------------------------------------------------------------------
Rectangle {
id: zone_parametres
x: 0
y: 0
width: ((model.Type_circuit === shipheart_type_module_shipbase) && (model.No_circuit === 0)) ?
parent.width : // Cas du circuit #0 de ShipBase
(model.Validation_cdes_events >= 0) ? // Cas général :
(parent.width * pourcentage_largeur_zone_affichage_nom) - espace_interne : // Avec commandes AUTO
parent.width * (1 - pourcentage_largeur_zone_affichage_consigne) // Sans commandes AUTO
height: parent.height;
color: (model.Etat_systeme === 0) ? shipheart_couleur_fond_bouton : "transparent"
radius: shipheart_rayon_bouton
Image { // Affichage du coin inférieur-droit en rouge ou orange si alarme ou alerte
source: ( (Etat_circuit === shipheart_etat_circuit_limitation) ||
(Etat_circuit === shipheart_etat_circuit_protection) ||
(Etat_circuit === shipheart_etat_circuit_disjonte) ||
(Etat_circuit === shipheart_etat_circuit_absence_terre) ||
(Etat_circuit === shipheart_etat_circuit_absence_masse) )
? "/images/coin_bas_droite_rouge.png"
: "/images/coin_bas_droite_orange.png"
// (Etat_commande_carte === shipheart_etat_commande_module_test) ||
// (Etat_commande_carte === shipheart_etat_commande_module_undervoltage) ||
// (Etat_circuit === shipheart_etat_circuit_cc_batterie) ||
// (Etat_circuit === shipheart_etat_circuit_deleste) ||
// (Etat_circuit === shipheart_etat_circuit_inversion_phase_neutre) ||
// (Etat_circuit === shipheart_etat_circuit_undervoltage) ||
// (Etat_events > 0)
sourceSize.height: 20
anchors {
bottom: parent.bottom
right: parent.right
}
visible: ( (Etat_commande_carte !== shipheart_etat_commande_module_ok)
|| (Etat_circuit !== shipheart_etat_circuit_ok)
|| (Etat_events > 0) )
&& (model.Etat_systeme === 0) // SDIR #285
}
MouseArea { // Zone d'interaction Paramètres du circuit
id: mouse_area_zone_parametres
x: 0
y: 0
anchors.fill: parent
onClicked: {
shipheart_clic();
stackView.push("circuit.qml", {"id_circuit": model.Cle_primaire, "consigne": model.Consigne })
}
}
}
//-------------------------------------------------------
// Zone du nom (en haut à gauche et au milieu) :
//-------------------------------------------------------
Rectangle {
id: nom
color: "transparent"
x: 0
y: 0
width: (parent.width * pourcentage_largeur_zone_affichage_nom) - espace_interne
height: parent.height / 2;
Text {
text: model.Nom
anchors.fill: parent // Permet que le texte occupe tout l'espace du parent (et que le centrage vertical qui suit fonctionne...)
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignLeft
leftPadding: 10
font.pixelSize: 15
font.weight: Font.Bold
color: couleur_blanc_casse
}
}
//-------------------------------------------------------
// Zone de cdes auto (en haut au centre) :
//-------------------------------------------------------
Rectangle {
id: cdes_auto
x: parent.width * pourcentage_largeur_zone_affichage_nom
y: 0;
width: (parent.width * (1 - pourcentage_largeur_zone_affichage_nom - pourcentage_largeur_zone_affichage_consigne));
height: parent.height;
radius: shipheart_rayon_bouton
color: shipheart_couleur_fond_bouton
visible: (model.Etat_systeme === 0) && (model.Validation_cdes_events >= 0)
Text {
text: "Auto" // Attention : Non traduit...
anchors.fill: parent // Permet que le texte occupe tout l'espace du parent (et que le centrage vertical qui suit fonctionne...)
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
font.pixelSize: 15
font.weight: Font.Bold
color : (model.Validation_cdes_events === 0) ? couleur_gris_moyen : couleur_green_yellow
}
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_cdes_auto
x: 0
y: 0
width: parent.width
height: parent.height
pressAndHoldInterval: shipheart_mouse_press_and_hold_interval_long
onPressAndHold: {
shipheart_clic();
toggle_cdes_auto();
timer_home.restart(); // Permet de réarmer le timer (qui ne doit PAS être 'triggeredOnStart' !) afin de reporter la prochaine mise à jour à une période entière de timer. Cela évite qu'un raffraichissement arrive trop tôt, avant que la nouvelle consigne ne soit prise en compte => On>Off>On ou Off>On>Off...
}
}
// Halo autour du bouton pendant l'appui :
Rectangle {
anchors {
verticalCenter: parent.verticalCenter
horizontalCenter: parent.horizontalCenter
}
width: Math.max(parent.width, parent.height) + 100
height: width
radius: width / 2
color : (model.Validation_cdes_events === 0) ? couleur_gris_moyen : couleur_green_yellow
opacity: 0.2
visible: mouse_area_cdes_auto.pressed
}
}
//-------------------------------------------------------
// Zone du courant instantané (en bas à gauche) :
//-------------------------------------------------------
Rectangle {
id: courant_instantane
color: "transparent" // Sinon, le fond du rectangle est blanc...
x: 0
y: hauteur_bouton / 2;
width: parent.width * pourcentage_largeur_zone_affichage_nom;
height: parent.height / 2;
visible: (model.Etat_systeme === 0) ? (consigne_principale.text !== qsTr("Off")) : 0
Image {
source: "/images/icone_consommation_electrique.png"
sourceSize.height: 20
anchors.left: courant_instantane.left
anchors.leftMargin: 5
anchors.verticalCenter: courant_instantane.verticalCenter
}
Text {
text: (model.Type_circuit !== shipheart_type_module_shippower4AC ?
"DC " + model.Tension.toFixed(2) + " V " + (model.Intensite > shipheart_plancher_intensite_circuit_sp8xx ? " | " + model.Intensite.toFixed(2) + " A | " + (model.Tension * model.Intensite).toFixed(0) + " W" : "") :
"AC " + model.Tension.toFixed(0) + " V | " + model.Frequence.toFixed(1) + " Hz " + (model.Intensite > shipheart_plancher_intensite_circuit_sp4ac ? " | " + model.Intensite.toFixed(1) + " A | " + (model.Tension * model.Intensite).toFixed(0) + " VA" : ""))
anchors.fill: parent // Permet que le texte occupe tout l'espace du parent (et que le centrage vertical qui suit fonctionne...)
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignLeft
leftPadding: 20
font.pixelSize: 12
color: couleur_gris_tres_tres_pale
}
}
//-------------------------------------------------------
// Zone de consigne (au milieu, à droite) :
//-------------------------------------------------------
Rectangle {
id: consigne
x: (parent.width * (1 - pourcentage_largeur_zone_affichage_consigne)) + espace_interne
width: (parent.width * pourcentage_largeur_zone_affichage_consigne) - espace_interne
height: parent.height;
//color: (model.Etat_commande_carte === shipheart_etat_commande_module_ok) ? ((model.Consigne === 0) ? couleur_rouge_transparent : couleur_vert_transparent) : "transparent"
color: (model.Etat_commande_carte === shipheart_etat_commande_module_ok) ? ((model.Consigne === 0) ? shipheart_couleur_fond_bouton : couleur_vert_transparent) : "transparent" // SDIR #207
radius: shipheart_rayon_bouton
border.width: (model.Etat_commande_carte === shipheart_etat_commande_module_ok) ? 0 : 3
border.color: (model.Etat_commande_carte === shipheart_etat_commande_module_ok) ? // SDIR #182
((model.Consigne === 0) ? couleur_rouge : couleur_green_yellow) : // SDIR #159. En pratique, inutile car dans ce cas, border.width=0
// ((model.Mode_test === 0) ? couleur_rouge : couleur_green_yellow) // SDIR #182
((model.Mode_test === 0) ? shipheart_couleur_fond_bouton : couleur_green_yellow) // SDIR #182 & SDIR #207
visible: (model.Etat_systeme === 0) && ((model.Type_circuit !== shipheart_type_module_shipbase) || (model.No_circuit !== 0))
Text {
id: consigne_principale
text: (model.Etat_commande_carte === shipheart_etat_commande_module_undervoltage) ?
"Off" :
((model.Etat_commande_carte === shipheart_etat_commande_module_ok) ?
((model.Consigne === 0) ? qsTr("Off") : qsTr("On")) :
((model.Mode_test === 0) ? qsTr("Off") : qsTr("On")))
anchors.verticalCenter: consigne.verticalCenter
anchors.verticalCenterOffset: ((model.Etat_commande_carte === shipheart_etat_commande_module_ok) && (model.Consigne > 0) && (model.Consigne < 100)) ? -10 : 0
anchors.horizontalCenter: consigne.horizontalCenter
font.pixelSize: 20
font.weight: Font.Bold
color: (model.Etat_commande_carte === shipheart_etat_commande_module_ok) ? // SDIR #182
((model.Consigne === 0) ? couleur_gris_pale : couleur_green_yellow) : // SDIR #159 & SDIR #207
((model.Mode_test === 0) ? couleur_gris_pale : couleur_green_yellow) // SDIR #182 & SDIR #207
}
Text {
id: consigne_secondaire
text: (model.Etat_commande_carte === shipheart_etat_commande_module_undervoltage) ?
"" :
((model.Etat_commande_carte === shipheart_etat_commande_module_ok) ?
((model.Consigne === 0) ? "" : ((model.Consigne === 100) ? "" : (model.Consigne + "%"))) :
"" )
anchors.top: consigne_principale.bottom
anchors.horizontalCenter: consigne.horizontalCenter
font.pixelSize: 16
color: (model.Consigne === 0) ? couleur_gris_pale : couleur_green_yellow // SDIR #159 & SDIR #207
}
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_consigne
anchors.fill: parent
enabled: (model.Etat_commande_carte === shipheart_etat_commande_module_ok) // Si on est en Undelvoltage ou en Test, on rend le bouton inoppérant...
pressAndHoldInterval: shipheart_mouse_press_and_hold_interval_long
onClicked: {
shipheart_clic();
var new_consigne;
if (model.Consigne === 0)
{
if (model.Old_consigne === 0) new_consigne = 100; // Si OLD = 0, on passe la consigne à 100%
else new_consigne = model.Old_consigne; // Sinon, on applique OLD à NEW
}
else new_consigne = 0;
// Mise à jour de la nouvelle consigne :
changement_consigne(new_consigne, -1);
timer_home.restart(); // Permet de réarmer le timer (qui ne doit PAS être 'triggeredOnStart' !) afin de reporter la prochaine mise à jour à une période entière de timer. Cela évite qu'un raffraichissement arrive trop tôt, avant que la nouvelle consigne ne soit prise en compte => On>Off>On ou Off>On>Off...
}
onPressAndHold: {
shipheart_clic();
if ((model.Consigne > 0) && (model.Consigne <100)) // On ne traite l'appui long que si on est ON [ET] en gradateur
{
// Mise à jour des nouvelles Consigne et Old_consigne :
changement_consigne(100, 100);
timer_home.restart(); // Permet de réarmer le timer (qui ne doit PAS être 'triggeredOnStart' !) afin de reporter la prochaine mise à jour à une période entière de timer. Cela évite qu'un raffraichissement arrive trop tôt, avant que la nouvelle consigne ne soit prise en compte => On>Off>On ou Off>On>Off...
}
}
}
}
// Fin de 'Zone de consigne'
//-------------------------------------------------------
//-------------------------------------------------------
// Zone d'interaction du circuit lorsque le circuit est ABSENT :
//-------------------------------------------------------
MouseArea {
id: mouse_area_absence
anchors.fill: parent
width: largeur_bouton * pourcentage_largeur_zone_affichage_nom;
height: hauteur_bouton;
enabled: (model.Etat_systeme !== 0)
onClicked: {
shipheart_clic();
stackView.push("circuit.qml", {"id_circuit": model.Cle_primaire })
}
}
// Fin de 'mouse_area_absence'
//-------------------------------------------------------
}
//=============================================================================================================
// Mise à jour de la valeuer de consigne
//=============================================================================================================
function changement_consigne(new_consigne, old_consigne)
{
var req;
// Mise à jour immédiate du modèle pour que l'affichage réagisse immédiatement :
modele_circuits.setProperty(index , "Consigne", new_consigne);
// Mise à jour dans la BDD :
req = "UPDATE TB_Circuits SET New_consigne=" + new_consigne + " ";
if (old_consigne !== -1) req += ", Old_consigne=" + old_consigne + " ";
req += " WHERE Cle_primaire='" + model.Cle_primaire + "'";
//console.log("Requête = [" + req + "]");
bdd.requete_unique(req);
synchro_bdd(shipheart_type_synchro_data);
}
//=============================================================================================================
// Mise à jour de Cdes Auto
//=============================================================================================================
function toggle_cdes_auto()
{
var req;
var mode;
if (model.Validation_cdes_events === 0) mode = 1;
else mode = 0;
// Mise à jour immédiate du modèle pour que l'affichage réagisse immédiatement :
modele_circuits.setProperty(index , "Validation_cdes_events", mode);
// Mise à jour dans la BDD :
bdd.requete_unique("UPDATE TB_Circuits SET Validation_cdes_events=" + mode + " WHERE Cle_primaire='" + model.Cle_primaire + "'")
// Note : le modèle sera ensuite remis à jour par la routine périodique dans HOME. en l'occurence, le modèle sera déjà à jour sur ce champ...
}
}
+200
View File
@@ -0,0 +1,200 @@
//-----------------------------------------------------------------------------------------------------
// Fichier définissant un bouton de type Réservoir sur la page d'accueil
// A priori, appelé depuis "home.qml"
//-----------------------------------------------------------------------------------------------------
import QtQuick 2.12
import"fonctions.js" as Fonctions
//=======================================================
// Ensemble du bouton
//=======================================================
Rectangle {
id: bouton_reservoir
readonly property int largeur_bouton: reservoirs_grid.cellWidth - espace_colonnes_secondaires;
readonly property int hauteur_bouton: reservoirs_grid.cellHeight - espace_lignes_secondaires;
readonly property double pourcentage_largeur_zone_affichage_nom: 0.85
readonly property int seuil_haut: 75 // utilisé pour changer de couleur orange/vert
readonly property int seuil_bas: 25 // utilisé pour changer de couleur rouge/orange
width: largeur_bouton
height: hauteur_bouton
radius: shipheart_rayon_bouton
color: "transparent" // On ne peut pas mettre l'opacité sur celui-ci, sinon tous ses enfants sont aussi semi-transparents...
//-------------------------------------------------------
// Bouton dans son ensemble :
//-------------------------------------------------------
Rectangle {
width: largeur_bouton;
height: hauteur_bouton;
color: shipheart_couleur_fond_bouton
radius: shipheart_rayon_bouton
border.color: couleur_rouge
border.width: (model.Etat_systeme === 0) ? 0 : 3
MouseArea {
anchors.fill: parent
width: largeur_bouton;
height: hauteur_bouton;
onClicked: {
shipheart_clic();
stackView.push("reservoir.qml",
{
"id_reservoir": model.Cle_primaire,
"nom_reservoir": model.Nom,
"niveau": model.Niveau,
"capacite": model.Capacite,
"id_famille": model.ID_Famille
})
}
}
}
//-------------------------------------------------------
// Zone du nom (en haut à gauche) :
//-------------------------------------------------------
Rectangle {
id: zone_nom
width: largeur_bouton * pourcentage_largeur_zone_affichage_nom;
height: hauteur_bouton / 2;
color: "transparent"
Text {
text: model.Nom
anchors.fill: parent // Permet que le texte occupe tout l'espace du parent (et que le centrage vertical qui suit fonctionne...)
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignLeft
leftPadding: 10
font.pixelSize: 15
elide: Text.ElideRight
font.weight: Font.Bold
color: couleur_blanc_casse
}
}
//-------------------------------------------------------
// Zone niveau absolu :
//-------------------------------------------------------
Rectangle {
id: zone_niveau_absolu
y: hauteur_bouton / 2;
width: largeur_bouton * pourcentage_largeur_zone_affichage_nom;
height: hauteur_bouton / 2;
color: "transparent"
visible: (model.Etat_systeme === 0)
//-------------------------
// Triangle orange d'alerte :
Image {
source: "/images/coin_bas_droite_orange.png"
sourceSize.height: 20
anchors {
bottom: parent.bottom
right: parent.right
}
visible: (model.Etat_systeme === 0) // SDIR #285
&& (model.Etat === 1) // SDIR #285
}
//-------------------------
// Triangle rouge d'alarme :
Image {
source: "/images/coin_bas_droite_rouge.png"
sourceSize.height: 20
anchors {
bottom: parent.bottom
right: parent.right
}
visible: (model.Etat_systeme === 0) // SDIR #285
&& (model.Etat === 2) // SDIR #285
}
//-------------------------
// Contenance actuelle :
Text {
id: niveau_absolu
text: Fonctions.conversion_volume(((model.Niveau / 100) * model.Capacite), shipheart_unite_volume, shipheart_unite_volume_symbole, 0);
anchors {
verticalCenter: parent.verticalCenter
horizontalCenter: parent.horizontalCenter
}
font.pixelSize: 18
font.bold: true
color: couleur_blanc_casse
}
//-------------------------
// Capacité :
Text {
id: capacite
text: "/ " + Fonctions.conversion_volume(model.Capacite, shipheart_unite_volume, shipheart_unite_volume_symbole, 0);
anchors {
left: niveau_absolu.right
leftMargin: 20
bottom: niveau_absolu.bottom
}
font.pixelSize: 15
color: couleur_gris_moyen
}
}
//-------------------------------------------------------
// Zone niveau relatif :
//-------------------------------------------------------
Rectangle {
id: zone_niveau_relatif
color: "transparent" // Sinon, le fond du rectangle est blanc...
x: largeur_bouton * pourcentage_largeur_zone_affichage_nom
width: largeur_bouton * (1 - pourcentage_largeur_zone_affichage_nom);
height: hauteur_bouton;
radius: shipheart_rayon_bouton
border.color: couleur_fond_bargraph_niveau_relatif()
border.width: 1
visible: (model.Etat_systeme === 0)
Text {
id:zone_texte_niveau_relatif
text: (model.Niveau).toFixed(0) + " %";
anchors.fill: parent // Permet que le texte occupe tout l'espace du parent (et que le centrage vertical qui suit fonctionne...)
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
font.pixelSize: 12
font.bold: (model.Niveau < 10) // soit < 10%
color: couleur_texte_niveau_relatif()
}
Rectangle {
id:zone_bargraph_niveau_relatif
color: couleur_fond_bargraph_niveau_relatif()
y: parent.height * (100 - model.Niveau) / 100
width: parent.width;
height: parent.height * model.Niveau / 100;
radius: shipheart_rayon_bouton
}
}
//----------------------------------------------------------
function couleur_texte_niveau_relatif()
{
switch(model.Logique)
{
case 1: return((model.Niveau >= seuil_haut) ? couleur_rouge : ((model.Niveau >= seuil_bas) ? couleur_orange : couleur_green_yellow))
case 0: return((model.Niveau >= seuil_haut) ? couleur_rouge : ((model.Niveau >= seuil_bas) ? couleur_green_yellow : couleur_rouge))
default: return((model.Niveau >= seuil_haut) ? couleur_green_yellow : ((model.Niveau >= seuil_bas) ? couleur_orange : couleur_rouge))
}
}
//----------------------------------------------------------
function couleur_fond_bargraph_niveau_relatif()
{
switch(model.Logique)
{
case 1: return((model.Niveau >= seuil_haut) ? couleur_rouge_transparent : ((model.Niveau >= seuil_bas) ? couleur_orange_transparent : couleur_vert_transparent))
case 0: return((model.Niveau >= seuil_haut) ? couleur_rouge_transparent : ((model.Niveau >= seuil_bas) ? couleur_vert_transparent : couleur_rouge_transparent))
default: return((model.Niveau >= seuil_haut) ? couleur_vert_transparent : ((model.Niveau >= seuil_bas) ? couleur_orange_transparent : couleur_rouge_transparent))
}
}
}
+68
View File
@@ -0,0 +1,68 @@
import QtQuick 2.12
import QtQuick.Controls 2.12
BusyIndicator {
id: busy_indicator
running: false
anchors.verticalCenter: parent.verticalCenter
anchors.horizontalCenter: parent.horizontalCenter
width: 120
height: width // Par principe, la forme est carrée...
contentItem: Item {
x: parent.width / 2
y: parent.height / 2
width: parent.width
height: parent.height
Item {
id: item
width: parent.width
height: parent.height
opacity: busy_indicator.running ? 1 : 0
Behavior on opacity { // Permet l'apparition et la disparition progressive du BI
OpacityAnimator {
duration: 250
}
}
RotationAnimator { // Donne le mouvement de rotation de l'ensemble des points
target: item
running: true
from: 0
to: 360
loops: Animation.Infinite
duration: 1000 // Durée d'un tour complet
}
Repeater {
id: repeater
model: 7 // Nombre de points formant le BI
Rectangle {
x: (item.width / 2) - (width / 2)
y: (item.height / 2) - (height / 2)
implicitWidth: item.width / 5 // Largeur du point
implicitHeight: item.height / 5 // Hauteur du point
radius: implicitWidth / 2 // Donne la forme circulaire du point
color: shipheart_couleur_fond_boite_dialogue_niveau_0 // Couleur du point
opacity: (index + 1) / (repeater.count + 1) // Donne l'opacité variable des points
transform: [
Translate {
y: -(Math.min(item.width, item.height) / 2) + (width / 2) // Permet de positionner le premier point en haut et au centre de la zone
},
Rotation {
angle: (index / repeater.count) * 360 // Tourne de x° en fonction de l'index
origin.x: width / 2 // Le centre de rotation est le centre de la zone
origin.y: height / 2
}
]
}
}
}
}
}
+55
View File
@@ -0,0 +1,55 @@
import QtQuick 2.12
Rectangle {
id: formulaire_bouton
property bool actif: false
property bool affichage_fleche: false // Indique si l'on affiche les flèches haut/bas, ou pas.
property bool fleche_haut: false // Utilisé uniquement si 'affichage_fleche' est TRUE, indique si la flèche à utiliser est vers le haut (true) ou vers le bas (false)
property string titre: ""
property color couleur_titre: couleur_blanc_casse
property bool gras_titre: false
color: actif ? shipheart_couleur_fond_bouton_actif : shipheart_couleur_fond_bouton
radius: shipheart_rayon_bouton
anchors.verticalCenter: parent.verticalCenter
height: parent.height - 10 // Nb : la Width est donnée au moment de l'appel de Combobox
Image {
source: "/images/up.png"
sourceSize.height: 15
anchors {
verticalCenter: parent.verticalCenter
right: parent.right
rightMargin: 20
}
visible: affichage_fleche && fleche_haut
}
Image {
source: "/images/down.png"
sourceSize.height: 15
anchors {
verticalCenter: parent.verticalCenter
right: parent.right
rightMargin: 20
}
visible: affichage_fleche && !fleche_haut
}
Text {
anchors {
verticalCenter: parent.verticalCenter
horizontalCenter: parent.horizontalCenter
}
text: titre
font.pixelSize: 18
font.bold: gras_titre
color: couleur_titre
}
}
+49
View File
@@ -0,0 +1,49 @@
import QtQuick 2.12
import QtQuick.Controls 2.12
CheckBox {
id: formulaire_checkbox
property bool flag_num_only: false
property bool password_type: false
anchors {
right: parent.right
verticalCenter: parent.verticalCenter
}
width: parent.width - 50
height: parent.height - 10
tristate: false
checked: false
text: ""
font.pixelSize: 18
indicator: Rectangle {
anchors {
horizontalCenter: parent.horizontalCenter
verticalCenter: parent.verticalCenter
}
implicitWidth: 40
implicitHeight: 40
radius: 5
color: "transparent"
border.color: couleur_gris_tres_fonce //couleur_bleu_koriolan_clair
border.width: formulaire_checkbox.checked ? 0 : 1
Rectangle {
visible: formulaire_checkbox.checked
color: couleur_gris_tres_tres_pale //couleur_bleu_koriolan_clair
radius: 5
anchors.fill: parent
}
Text {
visible: formulaire_checkbox.checked
text: "\u2713" // U+2714 est la coche épaisse, U+2713 est la coche normale
color: couleur_bleu_koriolan
font.pixelSize: parent.height
anchors {
horizontalCenter: parent.horizontalCenter
verticalCenter: parent.verticalCenter
}
}
}
}
+121
View File
@@ -0,0 +1,121 @@
import QtQuick 2.12
import QtQuick.Controls 2.12
ComboBox {
id: formulaire_combobox
property bool groupe: false
property bool actif: true
property string prefixe_repos: ""
property string suffixe_repos: ""
//=============================================================================================
// Partie au repos :
//=============================================================================================
anchors {
right: parent.right
leftMargin: 20
verticalCenter: parent.verticalCenter
}
// height: parent.height - 10 // Nb : la Width est donnée au moment de l'appel de Combobox
height: Math.max( (combobox_text.contentHeight + 10), (hauteur_ligne_formulaire - 10) )
textRole: "texte"
//---------------------------------------------------------------------------------------------
// Petite image dans la version repos :
indicator: Image {
id: icone_combo_box
source: "/images/down.png"
sourceSize.height: 15
anchors {
verticalCenter: parent.verticalCenter
right: parent.right
rightMargin: 20
}
}
//---------------------------------------------------------------------------------------------
// Texte au repos :
contentItem: Text {
id: combobox_text
leftPadding: 10
rightPadding: formulaire_combobox.indicator.width + formulaire_combobox.spacing
text: prefixe_repos + formulaire_combobox.currentText + suffixe_repos
font.pixelSize: 18
color: couleur_blanc_casse
wrapMode: Text.Wrap
verticalAlignment: Text.AlignVCenter
textFormat: Text.RichText
}
//---------------------------------------------------------------------------------------------
// Fond au repos
background: Rectangle {
radius: shipheart_rayon_bouton
color: shipheart_couleur_fond_bouton
}
//=============================================================================================
// Partie dépliée :
//=============================================================================================
//---------------------------------------------------------------------------------------------
// Fond :
popup: Popup {
y: formulaire_combobox.height - 1
width: formulaire_combobox.width
implicitHeight: contentItem.implicitHeight
padding: 0
// Note : si une un warning gest émis sur cette ligne, du genre 'ListView: Binding loop detected for property "implicitHeight"', c'est que la hauteur des lignes
// de la comboxbox (lorsqu'elle est ouverte) est trop importante. Une solution est de fixer la hauteur des lignes (cf la ligne commentée (92 environ) ci-dessous)
// L'inconvénient est que toutes les lignes ont alors la même hauteur... Il semble que ce warning ne soit pas grave donc laissé en l'état pour le moment... (02/2023)
contentItem: ListView { // Listview listant les éléments dépliés
clip: true
implicitHeight: contentHeight
model: formulaire_combobox.popup.visible ? formulaire_combobox.delegateModel : null
currentIndex: formulaire_combobox.highlightedIndex
ScrollIndicator.vertical: ScrollIndicator { } // Scrollbar quand il y a beaucoup d'éléments dépliés
}
background: Rectangle { // Fond de la partie dépliée
color: "transparent"
radius: shipheart_rayon_bouton
}
}
//---------------------------------------------------------------------------------------------
// Eléments dépliés :
delegate: ItemDelegate {
width: formulaire_combobox.width
//height: 100
enabled: groupe ? false : (actif ? true : false)
contentItem: Text { // Texte des éléments dépliés
text: model.texte
color: groupe ? couleur_bleu_koriolan : (actif ? couleur_blanc_casse : couleur_gris_tres_pale_transparent)
font.pixelSize: 18
font.weight: groupe ? Font.Bold : Font.Normal
topPadding: 5
bottomPadding: 5
leftPadding: groupe ? 0 : 30
rightPadding: formulaire_combobox.indicator.width + formulaire_combobox.spacing
wrapMode: Text.Wrap
verticalAlignment: Text.AlignVCenter
textFormat: Text.RichText
}
background: Rectangle { // Fond de chaque entrée dépliée
border.width: model.erreur ? 2 : 1
border.color: model.erreur ? couleur_rouge : couleur_gris_fonce
radius: shipheart_rayon_bouton
color: (highlightedIndex === index) ? couleur_bleu_koriolan : couleur_gris_tres_fonce
}
highlighted: highlightedIndex === index
}
}
+168
View File
@@ -0,0 +1,168 @@
import QtQuick 2.12
import QtQuick.Controls 2.12
ComboBox {
id: formulaire_combobox
property bool groupe: false
property bool actif: true
property string prefixe_repos: ""
property string suffixe_repos: ""
//=============================================================================================
// Partie au repos :
//=============================================================================================
anchors {
right: parent.right
leftMargin: 20
verticalCenter: parent.verticalCenter
}
height: parent.height - 10 // Nb : la Width est donnée au moment de l'appel de Combobox
textRole: "texte"
//---------------------------------------------------------------------------------------------
// Petite image dans la version repos :
indicator: Image {
id: icone_combo_box
source: "/images/down.png"
sourceSize.height: 15
anchors {
verticalCenter: parent.verticalCenter
right: parent.right
rightMargin: 20
}
}
//---------------------------------------------------------------------------------------------
// Texte au repos :
contentItem: Rectangle {
color: "transparent"
anchors.fill: parent
//-------------------------------------
// Icone :
Image {
source: (formulaire_combobox.currentIndex >= 0) && (formulaire_combobox.model.get(formulaire_combobox.currentIndex).icone !== "")
? "/images/" + formulaire_combobox.model.get(formulaire_combobox.currentIndex).icone
: ""
fillMode: Image.PreserveAspectFit
height: parent.height * 0.6
anchors {
verticalCenter: parent.verticalCenter
left: parent.left
leftMargin: 10
}
visible: (formulaire_combobox.currentIndex >= 0)
&& (formulaire_combobox.model.get(formulaire_combobox.currentIndex).icone !== "")
}
//-------------------------------------
// Texte :
Text {
leftPadding: 10
rightPadding: formulaire_combobox.indicator.width + formulaire_combobox.spacing
text: prefixe_repos + formulaire_combobox.currentText + suffixe_repos
font.pixelSize: 18
color: couleur_blanc_casse
wrapMode: Text.Wrap
anchors.fill: parent
verticalAlignment: Text.AlignVCenter
textFormat: Text.RichText
}
}
//---------------------------------------------------------------------------------------------
// Fond au repos
background: Rectangle {
radius: shipheart_rayon_bouton
color: shipheart_couleur_fond_bouton
}
//=============================================================================================
// Partie dépliée :
//=============================================================================================
//---------------------------------------------------------------------------------------------
// Fond :
popup: Popup {
y: formulaire_combobox.height - 1
width: formulaire_combobox.width
implicitHeight: contentItem.implicitHeight
padding: 0
// Note : si une un warning gest émis sur cette ligne, du genre 'ListView: Binding loop detected for property "implicitHeight"', c'est que la hauteur des lignes
// de la comboxbox (lorsqu'elle est ouverte) est trop importante. Une solution est de fixer la hauteur des lignes (cf la ligne commentée (95 environ) ci-dessous)
// L'inconvénient est que toutes les lignes ont alors la même hauteur... Il semble que ce warning ne soit pas grave donc laissé en l'état pour le moment... (02/2023)
contentItem: ListView { // Listview listant les éléments dépliés
clip: true
implicitHeight: contentHeight
model: formulaire_combobox.popup.visible ? formulaire_combobox.delegateModel : null
currentIndex: formulaire_combobox.highlightedIndex
ScrollIndicator.vertical: ScrollIndicator { } // Scrollbar quand il y a beaucoup d'éléments dépliés
}
background: Rectangle { // Fond de la partie dépliée
color: "transparent"
radius: shipheart_rayon_bouton
}
}
//---------------------------------------------------------------------------------------------
// Eléments dépliés :
delegate: ItemDelegate {
width: formulaire_combobox.width
height: 50
enabled: groupe ? false : (actif ? true : false)
contentItem: Rectangle {
color: "transparent"
anchors.fill: parent
//-------------------------------------
// Icone :
Image {
source: (model.icone !== "")
? "/images/" + model.icone
: ""
fillMode: Image.PreserveAspectFit
height: parent.height * 0.6
anchors {
verticalCenter: parent.verticalCenter
left: parent.left
leftMargin: 10
}
visible: (model.icone !== "")
}
//-------------------------------------
// Texte :
Text { // Texte des éléments dépliés
text: model.texte
color: groupe ? couleur_bleu_koriolan : (actif ? couleur_blanc_casse : couleur_gris_tres_pale_transparent)
font.pixelSize: 18
font.weight: groupe ? Font.Bold : Font.Normal
topPadding: 5
bottomPadding: 5
leftPadding: groupe ? 5 : 35
rightPadding: formulaire_combobox.indicator.width + formulaire_combobox.spacing
wrapMode: Text.Wrap
verticalAlignment: Text.AlignVCenter
textFormat: Text.RichText
anchors.fill: parent
}
}
background: Rectangle { // Fond de chaque entrée dépliée
border.width: model.erreur ? 2 : 1
border.color: model.erreur ? couleur_rouge : couleur_gris_fonce
radius: shipheart_rayon_bouton
color: (highlightedIndex === index) ? couleur_bleu_koriolan : couleur_gris_tres_fonce
}
highlighted: highlightedIndex === index
}
}
+17
View File
@@ -0,0 +1,17 @@
import QtQuick 2.12
import QtQuick.Controls 2.12
Label {
property color couleur: shipheart_formulaire_couleur_texte_par_defaut
anchors.verticalCenter: parent.verticalCenter
// horizontalAlignment: Text.AlignHCenter
leftPadding: 20
width: parent.width
font.pixelSize: 18
color: couleur
text: "--"
}
+27
View File
@@ -0,0 +1,27 @@
import QtQuick 2.12
Item {
id: ligne_separation
property int haut_bas_hautbas: shipheart_formulaire_ligne_separation_haute_basse // 1= haut, 2= bas, 3= haut et bas
property int force: shipheart_formulaire_ligne_separation_force_faible // 1=très clair, 2=moyen, 3=très vif
property double largeur_parent: parent.width
property double hauteur_parent: parent.height
property int epaisseur_ligne: 1
Rectangle { // Trait du haut :
width: largeur_parent
height: epaisseur_ligne
color: force === shipheart_formulaire_ligne_separation_force_faible ? couleur_gris_tres_fonce_transparent : (force === shipheart_formulaire_ligne_separation_force_moyenne ? couleur_gris_fonce : couleur_gris_pale)
visible: ((haut_bas_hautbas & 1) === 1) ? true : false
}
Rectangle { // Trait du bas :
y: hauteur_parent - epaisseur_ligne
width: largeur_parent
height: epaisseur_ligne
color: force === shipheart_formulaire_ligne_separation_force_faible ? couleur_gris_tres_fonce_transparent : (force === shipheart_formulaire_ligne_separation_force_moyenne ? couleur_gris_fonce : couleur_gris_pale)
visible: ((haut_bas_hautbas & 2) === 2) ? true : false
}
}
+41
View File
@@ -0,0 +1,41 @@
import QtQuick 2.12
import QtQuick.Controls 2.12
// Toutes les valeurs ci-dessous sont des valeurs par défaut qui passent à peu près partout.
// Mais elles peuvent toutes êtes overridées lors de l'appel de la forme :
//
// Shipheart_formulaire_textfield {
// text: libelle
// placeholderText: qsTr("truc bidule")
// anchors.left: label.right
// width: parent.width - label.width - anchors.leftMargin
// ... etc...
// }
TextArea {
id: formulaire_textarea
anchors {
right: parent.right
top: parent.top
}
padding: 10
placeholderText: ""
placeholderTextColor: couleur_gris_tres_pale_transparent
font.pixelSize: 18
color: couleur_blanc_casse
font.family: shipheart_font_saisie_textarea
text: ""
background: Rectangle {
color: shipheart_couleur_fond_bouton
radius: shipheart_rayon_bouton
}
}
+53
View File
@@ -0,0 +1,53 @@
import QtQuick 2.12
import QtQuick.Controls 2.12
// Toutes les valeurs ci-dessous sont des valeurs par défaut qui passent à peu près partout.
// Mais elles peuvent toutes êtes overridées lors de l'appel de la forme :
//
// Shipheart_formulaire_textfield {
// text: libelle
// placeholderText: qsTr("truc bidule")
// anchors.left: label.right
// width: parent.width - label.width - anchors.leftMargin
// ... etc...
// }
TextField {
id: formulaire_textfield
property bool flag_num_only: false
property bool password_type: false
anchors {
verticalCenter: parent.verticalCenter
}
leftPadding: 10
horizontalAlignment: Text.AlignLeft
width: parent.width
height: parent.height - 10
placeholderText: ""
inputMethodHints: flag_num_only ? Qt.ImhDigitsOnly : Qt.ImhNone // Affiche les chiffres et le '.' mais pas les signes (notamment, impossible d'entrer '-')
// inputMethodHints: flag_num_only ? Qt.ImhFormattedNumbersOnly : Qt.ImhNone
// inputMethodHints: flag_num_only ? Qt.ImhPreferNumbers : Qt.ImhNone // Affiche les chiffres et les signes, y compris '-'
font.pixelSize: 18
color: couleur_blanc_casse
font.family: shipheart_font_saisie_textfield
font.letterSpacing: password_type ? 5.0 : 1.0 // Evite que les points (en mode pwd) soient collés les uns aux autres
echoMode: password_type ? TextInput.Password : TextInput.Normal
passwordMaskDelay: 1000
// passwordCharacter: "="
text: ""
background: Rectangle {
color: shipheart_couleur_fond_bouton
radius: shipheart_rayon_bouton
}
onAccepted: {
// console.log("Saisie du Textfield avec valeur = [" + text + "]");
}
}
+15
View File
@@ -0,0 +1,15 @@
import QtQuick 2.12
import QtQuick.Controls 2.12
Label {
x: parent.width - width
anchors.verticalCenter: parent.verticalCenter
horizontalAlignment: Text.AlignHCenter
width: parent.width
font.pixelSize: 18
color: couleur_blanc_casse
text: "--"
}
+12
View File
@@ -0,0 +1,12 @@
import QtQuick.Controls
Label {
anchors {
verticalCenter: parent.verticalCenter
}
leftPadding: 10
width: parent.width
font.pixelSize: 18
color: couleur_blanc_casse
text: "--"
}
+171
View File
@@ -0,0 +1,171 @@
import QtQuick 2.12
import QtQuick.Controls 2.12
// Shipheart_page est un composant au sens QML. Il commence donc par une majuscule.
// Il peut être utilisé partout dans le projet.
Page {
id: page
background: Rectangle { // Attention : Background ne peut avoir qu'un seul élément [a priori, pas complètement sûr...] => on créé un Rectangle de plus haut niveau et on met ensuite dedans ce qu'on veut...
anchors.fill: parent
color: shipheart_theme_couleur_fond_ecran
Image {
anchors.fill: parent
fillMode: Image.Tile
// fillMode: Image.PreserveAspectCrop
source: shipheart_theme_fichier_fond_ecran
visible: (shipheart_theme_fichier_fond_ecran !== "")
}
}
//=================================================================================================
// Fonction : Shipheart_clic
// Cette fonction est normalement appelé à chaque appel d'un 'MouseArea.OnClick'
//=================================================================================================
function shipheart_clic()
{
emission_bip();
reload_economiseur();
}
//=====================================================================================
// Fonction : Vérifie si la configuration en cours nécessite l'émission d'un bip.
// Si oui, met à jour le champ correspondant dans la BDD et fait une demande
// de synchro pour que celle-ci soit prise en compte rapidmeent.
// Reçoit : Rien
// Retour : Rien
//=====================================================================================
function emission_bip()
{
if ((shipheart_bips !== "") && // Si shipheart_bips = "", il n'y a aucun bip à émettre...
(shipheart_type_installation === shipheart_type_installation_shipbase)) // SDIR 317 : ... et si on est sur ShipRS, on n'émet pas de bips
{
bdd.requete_unique("UPDATE TB_Systeme SET Valeur='" + shipheart_bips + "/0/0/0' WHERE Nom='New_bip'");
synchro_bdd(shipheart_type_synchro_data);
}
return;
}
//=================================================================================================
// Fonction : Réarmement économiseur
//=================================================================================================
function reload_economiseur()
{
var req, res;
/* // La valeur est relue à chaque fois dans la BDD pour être certain d'avoir la dernière valeur :
req = 'SELECT Valeur FROM TB_Configuration WHERE Nom LIKE "%Time_out_economiseur%" ';
res = bdd.requete_unique(req);
shipheart_compteur_economiseur = Number(res[0]);
*/
shipheart_compteur_economiseur = shipheart_time_out_economiseur;
}
//=================================================================================================
// Fonction : Demande de mise en veille (on ou off)
//=================================================================================================
function mise_en_veille(veille)
{
if (shipheart_type_installation !== shipheart_type_installation_shipbase) return; // SDIR #251
if (veille === 0) // On sort toujours du mode veille si c'est demandé
{
bdd.requete_unique('UPDATE TB_Systeme SET Valeur=' + veille + ' WHERE Nom="Veille"');
synchro_bdd(shipheart_type_synchro_data);
//console.log("Sortie de veille");
flag_economiseur_actif = false;
}
else
{
if ((veille === 1) && (get_validation_economiseur() === 1)) // On ne rentre en veille que si c'est demandé (le time-out a expiré ET l'économiseur est valide)
{
bdd.requete_unique('UPDATE TB_Systeme SET Valeur=' + veille + ' WHERE Nom="Veille"');
synchro_bdd(shipheart_type_synchro_data);
flag_economiseur_actif = true;
}
//else console.log("Demande d'activation de l'économiseur alors qu'il est dévalidé")
}
}
//=================================================================================================
// Fonction : Change l'état de la validation économiseur
//=================================================================================================
function changer_validation_economiseur()
{
var req, res;
var new_val;
req = 'SELECT Valeur FROM TB_Configuration WHERE Nom LIKE "%Validation_economiseur%" ';
res = bdd.requete_unique(req);
if (Number(res[0]) === 0) new_val = '1';
else new_val = '0';
bdd.requete_unique('UPDATE TB_Configuration SET Valeur=' + new_val + ' WHERE Nom LIKE "%Validation_economiseur%" ');
}
//=================================================================================================
// Fonction : Retourne l'état actuel de la validation de l'économiseur
//=================================================================================================
function get_validation_economiseur()
{
var req, res;
req = 'SELECT Valeur FROM TB_Configuration WHERE Nom LIKE "%Validation_economiseur%" ';
res = bdd.requete_unique(req);
return(Number(res[0]));
}
//=================================================================================================
// Fonction : Charge les paramètres du thème en cours
//=================================================================================================
function chargement_theme()
{
var req, res;
// Récupération du paramètre 'TB_Configuration.Theme'
req = 'SELECT Valeur FROM TB_Configuration WHERE Nom="Theme" ';
res = bdd.requete_unique(req);
// Vérifie que la n° de thème sélectionné est bien compatible avec les thèmes présents sur cette version (on peut imaginer qu'on en ait supprimé...) :
if (Number(res[0]) >= shipheart_config_themes.length)
{
shipheart_theme = 0; // Valeur par défaut
req = 'UPDATE TB_Configuration '; // Mise à jour dans la BDD pour les prochains démarrages
req += ' SET Valeur="' + shipheart_theme + '" ';
req += ' WHERE Nom="Theme" ';
//console.log("Requête = " + req)
bdd.requete_unique(req);
}
else shipheart_theme = Number(res[0]); // Cas normal
// Mise à jour des paramètres d'affcihage liés au thème choisit :
shipheart_theme_fichier_fond_ecran = shipheart_config_themes[shipheart_theme][shipheart_index_theme_fichier_fond_ecran];
shipheart_theme_couleur_fond_ecran = shipheart_config_themes[shipheart_theme][shipheart_index_theme_couleur_fond_ecran];
shipheart_couleur_fond_bouton_actif = shipheart_config_themes[shipheart_theme][shipheart_index_theme_couleur_fond_bouton_actif];
shipheart_couleur_texte_titre_1 = shipheart_config_themes[shipheart_theme][shipheart_index_theme_couleur_texte_titre_1];
shipheart_couleur_texte_titre_2 = shipheart_config_themes[shipheart_theme][shipheart_index_theme_couleur_texte_titre_2];
shipheart_couleur_texte_titre_3 = shipheart_config_themes[shipheart_theme][shipheart_index_theme_couleur_texte_titre_3];
shipheart_couleur_fond_boite_dialogue_niveau_0 = shipheart_config_themes[shipheart_theme][shipheart_index_theme_couleur_fond_boite_dialogue_niveau_0];
shipheart_couleur_fond_boite_dialogue_niveau_1 = shipheart_config_themes[shipheart_theme][shipheart_index_theme_couleur_fond_boite_dialogue_niveau_1];
shipheart_couleur_fond_boite_dialogue_niveau_2 = shipheart_config_themes[shipheart_theme][shipheart_index_theme_couleur_fond_boite_dialogue_niveau_2];
shipheart_couleur_texte_boite_dialogue_niveau_0 = shipheart_config_themes[shipheart_theme][shipheart_index_theme_couleur_texte_boite_dialogue_niveau_0];
shipheart_couleur_texte_boite_dialogue_niveau_1 = shipheart_config_themes[shipheart_theme][shipheart_index_theme_couleur_texte_boite_dialogue_niveau_1];
shipheart_couleur_texte_boite_dialogue_niveau_2 = shipheart_config_themes[shipheart_theme][shipheart_index_theme_couleur_texte_boite_dialogue_niveau_2];
shipheart_couleur_texte_slider = shipheart_config_themes[shipheart_theme][shipheart_index_theme_couleur_texte_slider];
shipheart_couleur_bordure_courbes = shipheart_config_themes[shipheart_theme][shipheart_index_theme_couleur_bordure_courbes];
//console.log("Thème d'affichage à utiliser = " + shipheart_theme + " [fichier = '" + shipheart_theme_fichier_fond_ecran + "' et couleur = '" + shipheart_theme_couleur_fond_ecran + "']");
}
}
+26
View File
@@ -0,0 +1,26 @@
import QtQuick 2.12
Rectangle {
property string titre
property int hauteur
id: titre_1
width: parent.width
height: hauteur
radius: 5
color: "transparent"
// color: couleur_green_yellow_transparent
Text {
verticalAlignment: Text.AlignVCenter
text: titre
color: shipheart_couleur_texte_titre_1
font.pixelSize : 30
font.weight: Font.Black
font.capitalization: Font.SmallCaps
anchors.verticalCenter: parent.verticalCenter // Centrage vertical sur la hauteur du titre reçu en paramètre
anchors.left: parent.left // Nécessaire pour que le leftMargin suivant fonctionne
anchors.leftMargin: 10
}
}
+45
View File
@@ -0,0 +1,45 @@
import QtQuick 2.12
Rectangle {
property string titre: ""
property string icone: ""
property int hauteur: visible ? shipheart_hauteur_titre_2 : 0
width: parent.width
height: hauteur
radius: 5
color: "transparent"
// color: couleur_gris_tres_fonce_transparent
Image {
id: icone_titre_2
source: (icone !== "") ? "/images/" + icone : "" // Si on laisse '/images/' seule, cela génère une erreur si le nom du fichier est vide...
sourceSize.height: parent.height * 0.8
anchors {
verticalCenter: parent.verticalCenter
left: parent.left
}
visible: icone !== ""
}
Text {
anchors {
verticalCenter: parent.verticalCenter // Centrage vertical sur la hauteur du titre reçu en paramètre
left: (icone !== "") ? icone_titre_2.right : parent.left
leftMargin: 10
}
verticalAlignment: Text.AlignVCenter
text: titre
color: shipheart_couleur_texte_titre_2
font.pixelSize : 24
// font.weight: Font.Black
visible: titre !== ""
}
}
+44
View File
@@ -0,0 +1,44 @@
import QtQuick 2.12
Rectangle {
property string titre: ""
property string icone: ""
property int hauteur: visible ? shipheart_hauteur_titre_2 : 0
width: parent.width
height: hauteur
radius: 5
color: "transparent"
// color: couleur_gris_tres_fonce_transparent
Image {
id: icone_titre_3
source: (icone !== "") ? "/images/" + icone : "" // Si on laisse '/images/' seule, cela génère une erreur si le nom du fichier est vide...
sourceSize.height: parent.height * 0.8
anchors {
verticalCenter: parent.verticalCenter
left: parent.left
}
visible: icone !== ""
}
Text {
anchors {
verticalCenter: parent.verticalCenter // Centrage vertical sur la hauteur du titre reçu en paramètre
left: (icone !== "") ? icone_titre_3.right : parent.left
leftMargin: 50
}
verticalAlignment: Text.AlignVCenter
text: titre
color: shipheart_couleur_texte_titre_3
font.pixelSize : 21
font.weight: Font.Bold
visible: titre !== ""
}
}
+38
View File
@@ -0,0 +1,38 @@
#include "backend.h"
#include <QDebug>
#include <QApplication>
#include <QQmlApplicationEngine>
#include <QTranslator>
extern QTranslator translator;
Backend::Backend() : QObject()
{
}
//===================================================================================================
// Fonction : Reçoit le code de la langue (par exemple 'en_EN') sélectionnée par l'utilisateur,
// supprime le fichier de langue en cours et charge le nouveau.
//===================================================================================================
void Backend::change_langue(QString lang)
{
if (!translator.isEmpty()) QApplication::instance()->removeTranslator(&translator);
if (translator.load(":/shipheart_"+ lang))
{
if (QApplication::instance()->installTranslator(&translator))
{
QQmlApplicationEngine *engine = QApplication::instance()->findChild<QQmlApplicationEngine *>("currentEngine");
engine->retranslate();
}
else qDebug() << "\tL'installation de " << lang << " a rencontré une erreur. Aucune langue n'est désormais installée (retour au FR)";
}
else qDebug() << "\tLe chargement de " << lang << " a rencontré une erreur. Aucune langue n'est désormais chargée (retour au FR)";
QQmlApplicationEngine *engine = QApplication::instance()->findChild<QQmlApplicationEngine *>("currentEngine");
engine->retranslate();
qDebug() << "L'activation de la langue [" << lang << "] a été effectuée avec succès.";
}
Executable
+20
View File
@@ -0,0 +1,20 @@
#ifndef BACKEND_H
#define BACKEND_H
#include <QObject>
#include <QProcess>
class Backend : public QObject
{
Q_OBJECT
public:
Backend();
// explicit Backend(QObject *parent = 0);
public slots:
void change_langue(QString lang);
};
#endif // BACKEND_H
+2000
View File
File diff suppressed because it is too large Load Diff
+179
View File
@@ -0,0 +1,179 @@
#include "bdd.h"
#include <QDebug>
#include <QSqlRecord>
#include <QSqlQuery>
//#include <QDateTime>
#include <QCryptographicHash>
//============================================================================
// Création du modèle
//============================================================================
Bdd::Bdd() :
QObject() {
m_lignes.clear();
m_ptr_ligne = 0;
}
//================================================================================================================================
// Fonction : Lit dans TB_Systeme l'état de 'Etat_cm3p' et le retourne.
// Reçoit : Rien
// Retour : 0=En cours d'initialisation, 1=Démarré Ok ou -1=Démarrage impossible
//================================================================================================================================
qint16 Bdd::check_fin_configuration()
{
QSqlQuery query;
bool retour;
retour = false;
if (query.exec("SELECT Valeur FROM TB_Systeme WHERE Nom='Etat_cm3p' ")) // Exécution de la requête (sans erreur)
{
if (query.next()) // Cas normal où on a trouvé une valeur :
{
retour = query.record().value(0).toInt();
}
else qDebug() << "La requête n'a renvoyé aucune valeur...";
}
else qDebug() << "/!\\ Erreur d'exécution de la requête !";
return(retour);
}
//================================================================================================================================
// Fonction : Exécute la requête SQL reçue en paramètre puis va récupérer les résultats. Seule le premier enregistrement est
// retourné car je ne sais pas renvoyer un vecteur de StringList (seul les vecteurs de String fonctionnent côté QML).
// Juste avant le retour, on appelle la fonction de mise à jour du modèle. Cela permet de s'assurer que toutes les
// valeurs utilisées dans QML sont à jour immédiatement (utile notamment après un UPDATE).
// Reçoit : la requête à exécuter
// Retour : un tableau de String avec autant d'entrées que de colonnes renvoyées par MariaDB. (Important : si elle renvoit
// plus d'une ligne, seule la première est retournée)
//================================================================================================================================
QStringList Bdd::requete_unique(QString requete)
{
int i;
QStringList resultats;
QSqlQuery query(requete);
while (query.next())
{
QSqlRecord resultat = query.record();
for(i = 0; i < resultat.count(); i++)
{
resultats << resultat.value(i).toString();
}
break; // On ne renvoie que le premier enregistrement quoi qu'il arrive
}
return(resultats);
}
//================================================================================================================================
// Fonction : Exécute la requête SQL reçue en paramètre puis va récupérer les résultats. Les résultats sont stockés dans la
// variable 'm_lignes' et la fonction renvoit soit -1 si erreur, soit le nombre d'enregistrements récupérés dans la BDD.
// Reçoit : la requête à exécuter
// Retour : -1 si erreur, n>=0 sinon (n = nombre de lignes récupérés dans la BDD par la requête)
//================================================================================================================================
qint64 Bdd::requete(QString requete)
{
QSqlQuery query;
QStringList ligne; // Ligne temporaire
qint64 nb_lignes = 0; // Nombre de lignes en retour de requête
qint16 nb_champs; // Nombre de champs de la ligne
qint16 i;
//QTime myTimer;
//myTimer.start();
m_lignes.clear(); // RAZ de la liste
m_ptr_ligne = 0; // RAZ du pointeur sur la liste
//qDebug() << "REQUETE : Temps écoulé avant exec : " << myTimer.elapsed() << "ms";
query.setForwardOnly(true); // Permet d'améliorer l'efficacité (mais oblige ensuite à accéder dans l'ordre aux enregistrements)
if (!query.exec(requete)) return(-1); // Exécution de la requête
//qDebug() << "REQUETE : Temps écoulé après exec : " << myTimer.elapsed() << "ms";
while (query.next()) // Boucle sur tous les enregistrements
{
QSqlRecord resultat = query.record(); // Récupération de enregistrement
nb_champs = resultat.count(); // Récupération du nombre de champs
//ligne = QStringList(); // Ligne vide
ligne.clear();
for(i = 0; i < nb_champs; i++) // Boucle sur tous les champs
{
ligne << query.value(i).toString(); // Chargement du champs dans la ligne temporaire ('query.value' est réputé plus rapide que 'record.value'
}
m_lignes << ligne; // Stockage de la ligne temporaire dans la liste de lignes
nb_lignes ++; // Maj du nombre de lignes
}
//qDebug() << "REQUETE : Temps écoulé après chargement lignes : " << myTimer.elapsed() << "ms";
//qDebug() << "Taille de 'm_lignes' = " hash.toHex()<< m_lignes.count();
return(nb_lignes);
}
//================================================================================================================================
// Fonction : Lit la ligne suivante dans la liste 'm_lignes'
// Reçoit : Rien
// Retour : un tableau de String avec autant d'entrées que de colonnes renvoyées par MariaDB.
//================================================================================================================================
QStringList Bdd::lecture_ligne()
{
QStringList ligne = QStringList(); // Ligne temporaire
ligne = m_lignes.at(m_ptr_ligne);
m_ptr_ligne ++; // Maj du pointeur sur la liste
return(ligne);
}
//================================================================================================================================
// Fonction : Calcule le hash de la chaine reçue en entrée et le retourne
// Reçoit : La chaine à hacher
// Retour : La chaine hachée
//================================================================================================================================
QString Bdd::sha256(QString entree)
{
QByteArray hash = QCryptographicHash::hash(entree.toUtf8(), QCryptographicHash::Sha256);
return(hash.toHex());
}
//================================================================================================================================
// Fonction : Convertit un entier signé sur 16 bits en une chaine de 4 caractères hexa
// Reçoit : La valeur à convertir
// Retour : La chaine en hexa
//================================================================================================================================
QString Bdd::int16_to_chaine_hexa(qint16 entree)
{
char chaine[20] = "";
char retour[5] = "";
uint16_t lg;
sprintf(chaine, "%04X", (int16_t) entree);
lg = strlen(chaine);
if (lg > 4) sprintf(retour, "%s", (chaine + lg - 4)); // Nécessaire pour les valeurs négatives qui renvoient FFFF avant. Par exemple : 'FFFFFEC8' au lieu de 'FEC8'
else sprintf(retour, "%s", chaine); // Cas normal de chaines déjà sur 4 octets
//qDebug() << "Chaine = " << chaine << " et Retour = [" << retour << "] et longueur = " << strlen(retour) ;
return(retour);
}
Executable
+71
View File
@@ -0,0 +1,71 @@
#ifndef BDD_H
#define BDD_H
#include <QObject>
#include <QString>
#include <QVector>
//==============================================================
class Bdd : public QObject
{
Q_OBJECT
QList<QStringList> m_lignes;
qint64 m_ptr_ligne;
public:
Bdd();
Q_INVOKABLE qint16 check_fin_configuration();
Q_INVOKABLE QStringList requete_unique(QString requete);
Q_INVOKABLE qint64 requete(QString requete);
Q_INVOKABLE QStringList lecture_ligne();
Q_INVOKABLE QString sha256(QString entree);
Q_INVOKABLE QString int16_to_chaine_hexa(qint16 entree);
};
#endif // BDD_H
+1168
View File
File diff suppressed because it is too large Load Diff
+2156
View File
File diff suppressed because it is too large Load Diff
+454
View File
@@ -0,0 +1,454 @@
//========================================================================================================================================================================
// CONFIG_ACCES_PARAMETRES.QML
// Ce fichier traite la configuration des accès aux pages de paramètres.
// La page est appelée depuis la page parametres_avances.qml, en cliquant sur "Sécurité" > "Accès aux paramètres"
//========================================================================================================================================================================
import QtQuick 2.12
import QtQml.Models
import qt.koriolan.shipheart 1.0
Shipheart_page {
id: page_config_acces_parametres
// Mis en forme :
readonly property double pourcentage_largeur_colonne_gauche: 0.75
readonly property double espace_colonnes_principales: 20
readonly property double pourcentage_hauteur_ligne_haut: 1
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 hauteur_ligne_formulaire: 60
readonly property double pourcentage_largeur_labels_formulaire: 0.4
readonly property int hauteur_bouton_droit: 80
readonly property double pourcentage_bandeau_bas_largeur_zone_retour: 1
readonly property int periode_init_raffraichissement_parametres: 1 // En ms
readonly property int periode_raffraichissement_parametres: 1000 // En ms
// Affichage :
property bool bouton_bandeau_bas_zone_retour_actif: false
property int protection_mode: 0
// Données :
property int min_taille_code: 6
property int max_taille_code: 8
// 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 if (!bouton_bandeau_bas_zone_retour_actif) {
}
}
}
// Titre de la page :
Shipheart_titre_1 {titre:qsTr("Configuration de l'accès aux paramètres avancés"); 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);
color: "transparent"
ListView {
id: grille_principale
width: parent.width
height: parent.height
snapMode: GridView.SnapToRow // L'arret de l'affichage se fera sur une rangée entière
clip: true // Nécessaire pour que les Delegates s'arretent à l'intérieur de la GridView
model: modele_grille_principale
footer: Rectangle { // Permet de dégager la place suffisante en fin de GridView pour accéder même au dernier élément lorsque que clavier est déplié
height: shipheart_hauteur_clavier
color: "transparent"
}
ObjectModel {
id: modele_grille_principale
//-------------------------------------------------------------------------------
// Ligne de formulaire : Time-out accès
Rectangle {
width: parent.width
height: visible ? hauteur_ligne_formulaire : 0
color: "transparent"
Shipheart_formulaire_label {
text: qsTr("Durée de maintien de l'accès")
width: parent.width * pourcentage_largeur_labels_formulaire
}
Shipheart_formulaire_combobox {
id: champ_formulaire_Time_out_niveau_acces
model: ListModel { }
width: parent.width * (1 - pourcentage_largeur_labels_formulaire)
onActivated: {
shipheart_clic();
}
}
// Shipheart_formulaire_ligne_separation {
// haut_bas_hautbas: shipheart_formulaire_ligne_separation_basse
// }
}
// Espace :
Rectangle {
width: parent.width
height: 50
color: "transparent"
}
// Sous-titre :
Shipheart_titre_2 {titre:qsTr("Nouveau code"); hauteur: shipheart_hauteur_bandeau_haut}
//-------------------------------------------------------------------------------
// Ligne de formulaire : Code
Rectangle {
width: (shipheart_largeur_page * pourcentage_largeur_colonne_gauche) - (espace_colonnes_principales / 2)
height: visible ? hauteur_ligne_formulaire : 0
color: "transparent"
Shipheart_formulaire_label {
text: qsTr("Code d'accès")
width: parent.width * pourcentage_largeur_labels_formulaire
}
Shipheart_formulaire_textfield {
id: champ_formulaire_Code
width: parent.width * (1 - pourcentage_largeur_labels_formulaire)
x: parent.width * pourcentage_largeur_labels_formulaire
flag_num_only: true
password_type: true
maximumLength: max_taille_code_pattern
}
}
//-------------------------------------------------------------------------------
// Ligne de formulaire : Répéter Code
Rectangle {
width: (shipheart_largeur_page * pourcentage_largeur_colonne_gauche) - (espace_colonnes_principales / 2)
height: visible ? hauteur_ligne_formulaire : 0
color: "transparent"
Shipheart_formulaire_label {
text: qsTr("Répéter le code d'accès")
width: parent.width * pourcentage_largeur_labels_formulaire
}
Shipheart_formulaire_textfield {
id: champ_formulaire_Code_repetition
width: parent.width * (1 - pourcentage_largeur_labels_formulaire)
x: parent.width * pourcentage_largeur_labels_formulaire
flag_num_only: true
password_type: true
maximumLength: max_taille_code_pattern
}
}
}
}
}
//-------------------------------------------------------
// 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 * pourcentage_hauteur_ligne_haut) - (espace_lignes_principales / 2);
color: "transparent"
Column {
anchors.fill: parent
spacing: 10
// Bouton Enregistrer :
Rectangle {
width: parent.width
height: hauteur_bouton_droit
color: shipheart_couleur_fond_bouton
radius: shipheart_rayon_bouton
// visible: !clavier.active // SDIR #255
Text {
anchors {
verticalCenter: parent.verticalCenter
horizontalCenter: parent.horizontalCenter
}
font.pixelSize: 20
text: qsTr("Enregistrer")
color: couleur_blanc_casse
}
MouseArea {
anchors.fill: parent
onClicked: {
shipheart_clic();
if (mise_a_jour() !== -1) stackView.pop(); // On ne revient à la page précédente que si la saisie est valide...
}
}
}
}
}
//-------------------------------------------------------
// 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 * pourcentage_bandeau_bas_largeur_zone_retour) - (2 * parent.padding)
height: shipheart_hauteur_bandeau_bas - parent.topPadding - parent.bottomPadding;
radius: shipheart_rayon_bouton
color: shipheart_couleur_fond_bouton
Image {
source: "/images/icone_retour.png"
sourceSize.height: 30
anchors {
verticalCenter: parent.verticalCenter
horizontalCenter: parent.horizontalCenter
}
}
MouseArea { // Zone d'interaction. On est obligé de la mettre dans la grille pour que Rectangle et MouseArea soient frères ou parent/enfant ET qu'on n'occupe qu'un espace dans la grille.
id: mouse_area_bandeau_bas_zone_retour
anchors.fill: parent
onClicked: {
shipheart_clic();
stackView.pop()
}
}
}
}
//-------------------------------------------------------
// Boite d'information :
//-------------------------------------------------------
Shipheart_boite_dialogue {
id: boite_information
}
//===========================================================
// Economiseur : Doit être placé en fin de page pour fonctionner
//===========================================================
MouseArea {
anchors.fill: parent
preventStealing: true // Permet que cela fonctionne même si un élément flickable (notamment) soit derrière et "vole" le click
enabled: flag_economiseur_actif
onClicked: {
mise_en_veille(0);
}
}
//=============================================================================================================
// Init de la page
//=============================================================================================================
function init_page()
{
var req, res;
var nb;
var i;
var time_out_niveau_acces, defaut_time_out_niveau_acces = (5 * 60);
//---------------------------------------------------------------------------------------
// Taille du code :
req = 'SELECT Valeur FROM TB_Configuration WHERE Nom="Min_taille_code" ';
res = bdd.requete_unique(req);
min_taille_code = Number(res[0]);
req = 'SELECT Valeur FROM TB_Configuration WHERE Nom="Max_taille_code" ';
res = bdd.requete_unique(req);
max_taille_code = Number(res[0]);
//---------------------------------------------------------------------------------------
// Time-out (en x 1s) :
req = 'SELECT Valeur FROM TB_Configuration WHERE Nom="Time_out_acces" ';
res = bdd.requete_unique(req);
time_out_niveau_acces = Number(res[0]);
champ_formulaire_Time_out_niveau_acces.model.clear();
champ_formulaire_Time_out_niveau_acces.model.append({"valeur": (0), "texte": qsTr("Aucune (saisie du code à chaque accès)") });
champ_formulaire_Time_out_niveau_acces.model.append({"valeur": (1*60), "texte": qsTr("1 minute") });
champ_formulaire_Time_out_niveau_acces.model.append({"valeur": defaut_time_out_niveau_acces, "texte": qsTr("5 minutes") });
champ_formulaire_Time_out_niveau_acces.model.append({"valeur": (15*60), "texte": qsTr("15 minutes") });
champ_formulaire_Time_out_niveau_acces.model.append({"valeur": (60*60), "texte": qsTr("1 heure") });
champ_formulaire_Time_out_niveau_acces.model.append({"valeur": -1, "texte": qsTr("Infinie (aucune protection des paramètres avancés)") });
// Positionnement de l'index sur la valeur en cours :
i = recherche_index_valeur_modele(time_out_niveau_acces, champ_formulaire_Time_out_niveau_acces.model, "Time_out_economiseur");
if (i === -1) champ_formulaire_Time_out_niveau_acces.currentIndex = recherche_index_valeur_modele(defaut_time_out_niveau_acces, champ_formulaire_Time_out_niveau_acces.model, "Time_out_economiseur");
else champ_formulaire_Time_out_niveau_acces.currentIndex = i;
}
//=============================================================================================================
// Mise à jour des valeurs saisies
//=============================================================================================================
function mise_a_jour()
{
var req;
var val;
//-------------------------------------------
// Configuration du time-out :
val = champ_formulaire_Time_out_niveau_acces.model.get(champ_formulaire_Time_out_niveau_acces.currentIndex).valeur;
//console.log("Valeur = " + val)
switch(val)
{
case 0 :
shipheart_niveau_acces_en_cours = shipheart_niveau_acces_aucun;
break;
case -1 :
if (shipheart_niveau_acces_en_cours === shipheart_niveau_acces_aucun) shipheart_niveau_acces_en_cours = shipheart_niveau_acces_user;
break;
default :
if (shipheart_niveau_acces_en_cours === shipheart_niveau_acces_aucun) shipheart_niveau_acces_en_cours = shipheart_niveau_acces_user;
break;
}
req = 'UPDATE TB_Configuration ';
req += ' SET Valeur="' + val + '" ';
req += ' WHERE Nom ="Time_out_acces" ';
//console.log("Requête = " + req)
bdd.requete_unique(req);
shipheart_time_out_niveau_acces = val;
shipheart_compteur_time_out_niveau_acces = shipheart_time_out_niveau_acces;
//-------------------------------------------
// Configuration du code d'accès :
var nouveau_code_saisi, nouveau_code_repetition_saisi;
var nouveau_code_saisi_encrypte;
var erreur = false;
nouveau_code_saisi = champ_formulaire_Code.text
nouveau_code_repetition_saisi = champ_formulaire_Code_repetition.text
do { // Fake loop
//.........................................
// Si aucune valeur de code n'est saisie :
if ((nouveau_code_saisi === "") && (nouveau_code_repetition_saisi === "")) break;
//.................................................
// Si la répétition n'est pas égale à l'original :
if (nouveau_code_repetition_saisi !== nouveau_code_saisi)
{
boite_information.niveau = 1
boite_information.imageTitreSource = "/images/attention.png"
boite_information.message = qsTr("Les deux valeurs saisies doivent être identiques !\n\nDe plus, le code doit contenir :\n- %1 chiffres au minimum\n- %2 chiffres au maximum.").arg(min_taille_code).arg(max_taille_code);
boite_information.open();
erreur = true;
break;
}
//.................................................
// Si le format du nouveau code n'est pas correct :
if ((nouveau_code_saisi.length < min_taille_code) || (nouveau_code_saisi.length > max_taille_code))
{
//console.log("Le code saisie a une longueur anormale [" + nouveau_code_saisi.length + "]")
boite_information.niveau = 1
boite_information.imageTitreSource = "/images/attention.png"
boite_information.message = qsTr("Le nouveau code doit contenir :\n- %1 chiffres au minimum\n- %2 chiffres au maximum.\n\nLe code saisi ne répond pas à ces critères et sa mise à jour n'est donc pas effectuée.\n\nMerci de saisir un code au format valide si vous souhaitez le modifier.").arg(min_taille_code).arg(max_taille_code);
boite_information.open();
erreur = true;
break;
}
//.................................................
// Si tout est Ok :
nouveau_code_saisi_encrypte = bdd.sha256(nouveau_code_saisi);
req = 'UPDATE TB_Configuration ';
req += ' SET Valeur="' + nouveau_code_saisi_encrypte + '" ';
req += ' WHERE Nom="Code" ';
//console.log("Requête = " + req)
bdd.requete_unique(req);
} while (0);
if (erreur === false) return(0);
else return(-1);
}
//=============================================================================================================
// Recherche de l'index d'une certaine valeur dans un modele
//=============================================================================================================
function recherche_index_valeur_modele(cible, modele, nom_modele)
{
var i;
for (i=0; i<modele.count; i ++)
{
if (modele.get(i).valeur === cible) break; // Cas où la valeur cible a été trouvée parmi les valeurs du modèle
}
if (i === modele.count)
{
console.log("Aucune valeur trouvée pour cible = [" + cible + "] du modèle = " + nom_modele);
i = -1; // Cas où la valeur n'a pas été trouvée parmi les valeurs du modèle
}
//else console.log("Valeur trouvée : [" + i + "] pour cible = [" + cible + "] soit texte = [" + modele.get(i).texte + "]");
return(i);
}
}
+484
View File
@@ -0,0 +1,484 @@
//========================================================================================================================================================================
// CONFIG_AFFICHAGE.QML
// Ce fichier traite la configuration des paramètres d'affichage.
// La page est appelée depuis la page parametres_avances.qml, en cliquant sur "Vos paramètres" > "Affichage"
//========================================================================================================================================================================
import QtQuick 2.12
import qt.koriolan.shipheart 1.0
Shipheart_page {
id: page_config_affichage
// Mis en forme :
readonly property double pourcentage_largeur_colonne_gauche: 0.75
readonly property double espace_colonnes_principales: 20
readonly property double pourcentage_hauteur_ligne_haut: 1
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 hauteur_ligne_formulaire: 60
readonly property double pourcentage_largeur_labels_formulaire: 0.4
readonly property int hauteur_bouton_droit: 80
readonly property double pourcentage_bandeau_bas_largeur_zone_retour: 1
readonly property int periode_init_raffraichissement_parametres: 1 // En ms
readonly property int periode_raffraichissement_parametres: 1000 // En ms
// Affichage :
property bool bouton_bandeau_bas_zone_retour_actif: false
property int protection_mode: 0
// Données :
// 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 if (!bouton_bandeau_bas_zone_retour_actif) {
}
}
}
// Titre de la page :
Shipheart_titre_1 {titre:qsTr("Configuration de l'affichage"); 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);
color: "transparent"
ListView {
id: grille_principale
width: parent.width
height: parent.height
snapMode: GridView.SnapToRow // L'arret de l'affichage se fera sur une rangée entière
clip: true // Nécessaire pour que les Delegates s'arretent à l'intérieur de la GridView
model: modele_grille_principale
footer: Rectangle { // Permet de dégager la place suffisante en fin de GridView pour accéder même au dernier élément lorsque que clavier est déplié
height: shipheart_hauteur_clavier
color: "transparent"
}
ObjectModel {
id: modele_grille_principale
//-------------------------------------------------------------------------------
// Ligne de formulaire : Nom du bateau
Rectangle {
width: (shipheart_largeur_page * pourcentage_largeur_colonne_gauche) - (espace_colonnes_principales / 2)
height: visible ? hauteur_ligne_formulaire : 0
color: "transparent"
Shipheart_formulaire_label {
text: qsTr("Nom du bateau")
width: parent.width * pourcentage_largeur_labels_formulaire
}
Shipheart_formulaire_textfield {
id: champ_formulaire_Nom_bateau
width: parent.width * (1 - pourcentage_largeur_labels_formulaire)
x: parent.width * pourcentage_largeur_labels_formulaire
}
Shipheart_formulaire_ligne_separation {
haut_bas_hautbas: shipheart_formulaire_ligne_separation_basse
}
}
//-------------------------------------------------------------------------------
// Ligne de formulaire : Fuseau horaire
Rectangle {
width: parent.width
height: visible ? hauteur_ligne_formulaire : 0
color: "transparent"
Shipheart_formulaire_label {
text: qsTr("Fuseau horaire")
width: parent.width * pourcentage_largeur_labels_formulaire
}
Shipheart_formulaire_combobox {
id: champ_formulaire_Fuseau_horaire
model: ListModel { }
width: parent.width * (1 - pourcentage_largeur_labels_formulaire)
onActivated: {
shipheart_clic();
}
}
Shipheart_formulaire_ligne_separation {
haut_bas_hautbas: shipheart_formulaire_ligne_separation_basse
}
}
//-------------------------------------------------------------------------------
// Ligne de formulaire : Time-out économiseur d'écran
Rectangle {
width: parent.width
height: visible ? hauteur_ligne_formulaire : 0
color: "transparent"
Shipheart_formulaire_label {
text: qsTr("Temporisaton économiseur d'écran")
width: parent.width * pourcentage_largeur_labels_formulaire
}
Shipheart_formulaire_combobox {
id: champ_formulaire_Time_out_economiseur
model: ListModel { }
width: parent.width * (1 - pourcentage_largeur_labels_formulaire)
onActivated: {
shipheart_clic();
}
}
Shipheart_formulaire_ligne_separation {
haut_bas_hautbas: shipheart_formulaire_ligne_separation_basse
}
}
//-------------------------------------------------------------------------------
// Ligne de formulaire : Thème d'affichage
Rectangle {
width: parent.width
height: visible ? hauteur_ligne_formulaire : 0
color: "transparent"
Shipheart_formulaire_label {
text: qsTr("Thème d'affichage")
width: parent.width * pourcentage_largeur_labels_formulaire
}
Shipheart_formulaire_combobox {
id: champ_formulaire_Theme
model: ListModel { }
width: parent.width * (1 - pourcentage_largeur_labels_formulaire)
onActivated: {
shipheart_clic();
}
}
Shipheart_formulaire_ligne_separation {
haut_bas_hautbas: shipheart_formulaire_ligne_separation_basse
}
}
}
}
}
//-------------------------------------------------------
// 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 * pourcentage_hauteur_ligne_haut) - (espace_lignes_principales / 2);
color: "transparent"
Column {
anchors.fill: parent
spacing: 10
//---------------------------
// Bouton Enregistrer :
//---------------------------
Rectangle {
width: parent.width
height: hauteur_bouton_droit
color: shipheart_couleur_fond_bouton
radius: shipheart_rayon_bouton
// visible: !clavier.active // SDIR #255
Text {
anchors {
verticalCenter: parent.verticalCenter
horizontalCenter: parent.horizontalCenter
}
font.pixelSize: 20
text: qsTr("Enregistrer")
color: couleur_blanc_casse
}
MouseArea {
anchors.fill: parent
onClicked: {
shipheart_clic();
mise_a_jour(); // Mise à jour des données saisies dans la BDD et reconfiguration par l'application pour prise en compte.
stackView.pop();
}
}
}
}
}
//-------------------------------------------------------
// 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 * pourcentage_bandeau_bas_largeur_zone_retour) - (2 * parent.padding)
height: shipheart_hauteur_bandeau_bas - parent.topPadding - parent.bottomPadding;
radius: shipheart_rayon_bouton
color: shipheart_couleur_fond_bouton
Image {
source: "/images/icone_retour.png"
sourceSize.height: 30
anchors {
verticalCenter: parent.verticalCenter
horizontalCenter: parent.horizontalCenter
}
}
MouseArea { // Zone d'interaction. On est obligé de la mettre dans la grille pour que Rectangle et MouseArea soient frères ou parent/enfant ET qu'on n'occupe qu'un espace dans la grille.
id: mouse_area_bandeau_bas_zone_retour
anchors.fill: parent
onClicked: {
shipheart_clic();
stackView.pop()
}
}
}
}
// 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 nb;
var i;
var defaut_time_out_economiseur = 0;
var capteur_temperature_accueil, defaut_capteur_temperature_accueil = 0;
var theme, defaut_theme = 0;
//---------------------------------------------------------------------------------------
// Nom du bateau :
//---------------------------------------------------------------------------------------
req = 'SELECT Valeur FROM TB_Configuration WHERE Nom="Nom_du_bateau" ';
res = bdd.requete_unique(req);
champ_formulaire_Nom_bateau.text = res[0];
//---------------------------------------------------------------------------------------
// Fuseau horaire :
//---------------------------------------------------------------------------------------
// Config actuelle :
req = 'SELECT Valeur FROM TB_Configuration WHERE Nom="Fuseau_horaire" ';
res = bdd.requete_unique(req);
shipheart_fuseau_horaire = Number(res[0]);
// Liste des fuseaux possibles :
champ_formulaire_Fuseau_horaire.model.clear();
champ_formulaire_Fuseau_horaire.model.append({"valeur": (-12), "texte": qsTr("UTC-12 (Yankee)") });
champ_formulaire_Fuseau_horaire.model.append({"valeur": (-11), "texte": qsTr("UTC-11 (X-Ray)") });
champ_formulaire_Fuseau_horaire.model.append({"valeur": (-10), "texte": qsTr("UTC-10 (Whisky)") });
champ_formulaire_Fuseau_horaire.model.append({"valeur": (-9), "texte": qsTr("UTC-9 (Victor)") });
champ_formulaire_Fuseau_horaire.model.append({"valeur": (-8), "texte": qsTr("UTC-8 (Uniform)") });
champ_formulaire_Fuseau_horaire.model.append({"valeur": (-7), "texte": qsTr("UTC-7 (Tango)") });
champ_formulaire_Fuseau_horaire.model.append({"valeur": (-6), "texte": qsTr("UTC-6 (Sierra)") });
champ_formulaire_Fuseau_horaire.model.append({"valeur": (-5), "texte": qsTr("UTC-5 (Romeo)") });
champ_formulaire_Fuseau_horaire.model.append({"valeur": (-4), "texte": qsTr("UTC-4 (Quebec)") });
champ_formulaire_Fuseau_horaire.model.append({"valeur": (-3), "texte": qsTr("UTC-3 (Papa)") });
champ_formulaire_Fuseau_horaire.model.append({"valeur": (-2), "texte": qsTr("UTC-2 (Oscar)") });
champ_formulaire_Fuseau_horaire.model.append({"valeur": (-1), "texte": qsTr("UTC-1 (November)") });
champ_formulaire_Fuseau_horaire.model.append({"valeur": (0), "texte": qsTr("UTC (Zoulou)") });
champ_formulaire_Fuseau_horaire.model.append({"valeur": (1), "texte": qsTr("UTC+1 (Alpha)") });
champ_formulaire_Fuseau_horaire.model.append({"valeur": (2), "texte": qsTr("UTC+2 (Bravo)") });
champ_formulaire_Fuseau_horaire.model.append({"valeur": (3), "texte": qsTr("UTC+3 (Charlie)") });
champ_formulaire_Fuseau_horaire.model.append({"valeur": (4), "texte": qsTr("UTC+4 (Delta)") });
champ_formulaire_Fuseau_horaire.model.append({"valeur": (5), "texte": qsTr("UTC+5 (Echo)") });
champ_formulaire_Fuseau_horaire.model.append({"valeur": (6), "texte": qsTr("UTC+6 (Foxtrot)") });
champ_formulaire_Fuseau_horaire.model.append({"valeur": (7), "texte": qsTr("UTC+7 (Golf)") });
champ_formulaire_Fuseau_horaire.model.append({"valeur": (8), "texte": qsTr("UTC+8 (Hotel)") });
champ_formulaire_Fuseau_horaire.model.append({"valeur": (9), "texte": qsTr("UTC+9 (India)") });
champ_formulaire_Fuseau_horaire.model.append({"valeur": (10), "texte": qsTr("UTC+10 (Kilo)") });
champ_formulaire_Fuseau_horaire.model.append({"valeur": (11), "texte": qsTr("UTC+11 (Lima)") });
champ_formulaire_Fuseau_horaire.model.append({"valeur": (12), "texte": qsTr("UTC+12 (Mike)") });
// Positionnement de l'index sur la valeur en cours :
i = recherche_index_valeur_modele(shipheart_fuseau_horaire, champ_formulaire_Fuseau_horaire.model, "Fuseau_horaire");
if (i === -1) champ_formulaire_Fuseau_horaire.currentIndex = recherche_index_valeur_modele(defaut_fuseau_horaire, champ_formulaire_Fuseau_horaire.model, "Fuseau_horaire");
else champ_formulaire_Fuseau_horaire.currentIndex = i;
//---------------------------------------------------------------------------------------
// Time-out économiseur :
//---------------------------------------------------------------------------------------
// Liste des time-outs possibles (en x 1s) :
champ_formulaire_Time_out_economiseur.model.clear();
champ_formulaire_Time_out_economiseur.model.append({"valeur": (15), "texte": qsTr("15 secondes") });
champ_formulaire_Time_out_economiseur.model.append({"valeur": (30), "texte": qsTr("30 secondes") });
champ_formulaire_Time_out_economiseur.model.append({"valeur": (1*60), "texte": qsTr("1 minute") });
champ_formulaire_Time_out_economiseur.model.append({"valeur": (3*60), "texte": qsTr("3 minutes") });
champ_formulaire_Time_out_economiseur.model.append({"valeur": (10*60), "texte": qsTr("10 minutes") });
champ_formulaire_Time_out_economiseur.model.append({"valeur": defaut_time_out_economiseur, "texte": qsTr("Aucune limite") });
// Positionnement de l'index sur la valeur en cours :
console.log("shipheart_time_out_economiseur AVANT recherche = " + shipheart_time_out_economiseur);
i = recherche_index_valeur_modele(shipheart_time_out_economiseur, champ_formulaire_Time_out_economiseur.model, "Time_out_economiseur");
if (i === -1) champ_formulaire_Time_out_economiseur.currentIndex = recherche_index_valeur_modele(defaut_time_out_economiseur, champ_formulaire_Time_out_economiseur.model, "Time_out_economiseur");
else champ_formulaire_Time_out_economiseur.currentIndex = i;
console.log("shipheart_time_out_economiseur : valeur de i après recherche = " + i);
//---------------------------------------------------------------------------------------
// Theme d'affichage :
//---------------------------------------------------------------------------------------
// Récupération de la valeur actuelle :
theme = shipheart_theme;
// Liste des time-outs possibles (en x 1s) :
champ_formulaire_Theme.model.clear();
for (i = 0; i < shipheart_config_themes.length; i++)
{
champ_formulaire_Theme.model.append({"valeur": i, "texte": shipheart_config_themes[i][shipheart_index_theme_nom] });
}
// Positionnement de l'index sur la valeur en cours :
i = recherche_index_valeur_modele(theme, champ_formulaire_Theme.model, "Theme");
if (i === -1) champ_formulaire_Theme.currentIndex = recherche_index_valeur_modele(defaut_theme, champ_formulaire_Theme.model, "Theme");
else champ_formulaire_Theme.currentIndex = i;
}
//=============================================================================================================
// Mise à jour des valeurs saisies
//=============================================================================================================
function mise_a_jour()
{
var req;
//---------------------------------------------------------------------------------------
// Nom du bateau :
//---------------------------------------------------------------------------------------
req = 'UPDATE TB_Configuration ';
req += ' SET Valeur="' + champ_formulaire_Nom_bateau.text + '" ';
req += ' WHERE Nom="Nom_du_bateau" ';
//console.log("Requête = " + req)
bdd.requete_unique(req);
//---------------------------------------------------------------------------------------
// Fuseau horaire :
//---------------------------------------------------------------------------------------
shipheart_fuseau_horaire = champ_formulaire_Fuseau_horaire.model.get(champ_formulaire_Fuseau_horaire.currentIndex).valeur;
req = 'UPDATE TB_Configuration ';
req += ' SET Valeur="' + shipheart_fuseau_horaire + '" ';
req += ' WHERE Nom="Fuseau_horaire" ';
//console.log("Requête = " + req)
bdd.requete_unique(req);
//---------------------------------------------------------------------------------------
// Time-out économiseur :
//---------------------------------------------------------------------------------------
shipheart_time_out_economiseur = champ_formulaire_Time_out_economiseur.model.get(champ_formulaire_Time_out_economiseur.currentIndex).valeur;
req = 'UPDATE TB_Configuration ';
req += ' SET Valeur="' + shipheart_time_out_economiseur + '" ';
req += ' WHERE Nom="Time_out_economiseur" ';
//console.log("Requête = " + req)
bdd.requete_unique(req);
// Mise à jour de la nouvelle valeur de time-out dans le compteur en cours :
shipheart_compteur_economiseur = shipheart_time_out_economiseur;
//---------------------------------------------------------------------------------------
// Thème :
//---------------------------------------------------------------------------------------
// Mise à jour dans la BDD :
req = 'UPDATE TB_Configuration ';
req += ' SET Valeur="' + champ_formulaire_Theme.model.get(champ_formulaire_Theme.currentIndex).valeur + '" ';
req += ' WHERE Nom="Theme" ';
//console.log("Requête = " + req)
bdd.requete_unique(req);
chargement_theme(); // Permet de faire les vérifications de cohérence (a priori, peu utile ici) et, surtout, de charger toutes les variables liées au thème
}
//=============================================================================================================
// Recherche de l'index d'une certaine valeur dans un modele
//=============================================================================================================
function recherche_index_valeur_modele(cible, modele, nom_modele)
{
var i;
for (i=0; i<modele.count; i ++)
{
if (modele.get(i).valeur === cible) break; // Cas où la valeur cible a été trouvée parmi les valeurs du modèle
}
if (i === modele.count)
{
console.log("Aucune valeur trouvée pour cible = [" + cible + "] du modèle = " + nom_modele);
i = -1; // Cas où la valeur n'a pas été trouvée parmi les valeurs du modèle
}
//else console.log("Valeur trouvée : [" + i + "] pour cible = [" + cible + "] soit texte = [" + modele.get(i).texte + "]");
return(i);
}
}
+481
View File
@@ -0,0 +1,481 @@
//========================================================================================================================================================================
// CONFIG_AUDIO.QML
// Ce fichier traite les paramètres de configuration liés à l'audio
// La page est appelée depuis la page parametres_avances.qml, en cliquant sur "Audio" depuis la colonne "Vos paramètres"
//========================================================================================================================================================================
import QtQuick 2.12
import QtQml.Models
import qt.koriolan.shipheart 1.0
Shipheart_page {
id: page_config_versions
// Mis en forme :
readonly property double pourcentage_largeur_colonne_gauche: 0.75
readonly property double espace_colonnes_principales: 20
readonly property double pourcentage_hauteur_ligne_haut: 0.3
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 hauteur_ligne_formulaire: 60
readonly property double pourcentage_largeur_labels_formulaire: 0.4
readonly property int hauteur_bouton_droit: 80
readonly property double pourcentage_bandeau_bas_largeur_zone_retour: 1
readonly property int periode_init_raffraichissement_parametres: 1 // En ms
readonly property int periode_raffraichissement_parametres: 1000 // En ms
// Affichage :
property bool bouton_bandeau_bas_zone_retour_actif: false
property int protection_mode: 0
// Données :
// 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 if (!bouton_bandeau_bas_zone_retour_actif) {
mise_a_jour_periodique();
}
}
}
// Titre de la page :
Shipheart_titre_1 {titre:qsTr("Configuration audio"); 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;
color: "transparent"
ListView {
id: grille_principale
width: parent.width
height: parent.height
snapMode: GridView.SnapToRow // L'arret de l'affichage se fera sur une rangée entière
clip: true // Nécessaire pour que les Delegates s'arretent à l'intérieur de la GridView
model: modele_grille_principale
footer: Rectangle { // Permet de dégager la place suffisante en fin de GridView pour accéder même au dernier élément lorsque que clavier est déplié
height: shipheart_hauteur_clavier
color: "transparent"
}
ObjectModel {
id: modele_grille_principale
//-------------------------------------------------------------------------------
// Ligne de formulaire : Bips
Rectangle {
width: parent.width
height: visible ? hauteur_ligne_formulaire : 0
color: "transparent"
Shipheart_formulaire_label {
text: qsTr("Bips de clic")
width: parent.width * pourcentage_largeur_labels_formulaire
}
Shipheart_formulaire_combobox {
id: champ_formulaire_Bips
model: ListModel { }
width: parent.width * (1 - pourcentage_largeur_labels_formulaire)
onActivated: {
shipheart_clic();
test_bip(champ_formulaire_Bips.model.get(champ_formulaire_Bips.currentIndex).valeur);
}
}
Shipheart_formulaire_ligne_separation {
haut_bas_hautbas: shipheart_formulaire_ligne_separation_basse
}
}
//-------------------------------------------------------------------------------
// Ligne de formulaire : Fonction_jack
Rectangle {
width: parent.width
height: visible ? (1.6 * hauteur_ligne_formulaire) : 0
color: "transparent"
Shipheart_formulaire_label {
text: qsTr("Fonctionnement de la sortie Audio")
width: parent.width * pourcentage_largeur_labels_formulaire
}
Shipheart_formulaire_combobox {
id: champ_formulaire_Fonction_jack
model: ListModel { }
width: parent.width * (1 - pourcentage_largeur_labels_formulaire)
onActivated: {
shipheart_clic();
}
}
Shipheart_formulaire_ligne_separation {
haut_bas_hautbas: shipheart_formulaire_ligne_separation_basse
}
}
}
}
}
//-------------------------------------------------------
// Ecran Milieu - Gauche :
//-------------------------------------------------------
Rectangle {
id: zone_affichage_milieu_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;
color: "transparent"
//-------------------------------------------------------------------
// Grille pour afficher les valeurs des paramètres non configurables :
GridView {
id: grille_informations
width: parent.width
height: parent.height
cellWidth: parent.width;
cellHeight: hauteur_ligne_formulaire
snapMode: GridView.SnapToRow // L'arret de l'affichage se fera sur une rangée entière
clip: true // Nécessaire pour que les Delegates s'arretent à l'intérieur de la GridView
model: modele_grille_informations
delegate: Rectangle {
width: zone_affichage_milieu_gauche.width // L'utilisation de 'parent' génère un warning au moment de la MAJ périodique (clear du modèle)
height: visible ? hauteur_ligne_formulaire : 0
color: "transparent"
Shipheart_formulaire_label {
text: model.nom_champ
width: parent.width * pourcentage_largeur_labels_formulaire
}
Shipheart_formulaire_valeur_fixe {
text: model.valeur_champ
width: parent.width * (1 - pourcentage_largeur_labels_formulaire)
x: parent.width * pourcentage_largeur_labels_formulaire
}
Shipheart_formulaire_ligne_separation {
haut_bas_hautbas: shipheart_formulaire_ligne_separation_basse
}
}
ListModel {
id: modele_grille_informations
}
}
}
//-------------------------------------------------------
// 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 * pourcentage_hauteur_ligne_haut) - (espace_lignes_principales / 2);
color: "transparent"
Column {
anchors.fill: parent
spacing: 10
//.......................................................
// Bouton Enregistrer :
//.......................................................
Rectangle {
width: parent.width
height: hauteur_bouton_droit
color: shipheart_couleur_fond_bouton
radius: shipheart_rayon_bouton
// visible: !clavier.active // SDIR #255
Text {
anchors {
verticalCenter: parent.verticalCenter
horizontalCenter: parent.horizontalCenter
}
font.pixelSize: 20
text: qsTr("Enregistrer")
color: couleur_blanc_casse
}
MouseArea {
anchors.fill: parent
onClicked: {
shipheart_clic();
mise_a_jour(); // Mise à jour des données saisies dans la BDD et reconfiguration par l'application pour prise en compte.
timer_tempo_reconfiguration.restart();
}
}
}
//.......................................................
// Timer de reconfiguration :
//.......................................................
Timer {
id: timer_tempo_reconfiguration
interval: 250 // Cette tempo n'est utilisée que pour le premier cycle
triggeredOnStart: false
running: false
repeat: true
onTriggered: {
if (bdd.check_fin_configuration())
{
timer_tempo_reconfiguration.stop();
stackView.pop(); // Retour à la page de config des capteurs
}
else timer_tempo_reconfiguration.interval = 100 // Tempo plus courte pour les lectures suivantes
}
}
}
}
//-------------------------------------------------------
// 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 * pourcentage_bandeau_bas_largeur_zone_retour) - (2 * parent.padding)
height: shipheart_hauteur_bandeau_bas - parent.topPadding - parent.bottomPadding;
radius: shipheart_rayon_bouton
color: shipheart_couleur_fond_bouton
Image {
source: "/images/icone_retour.png"
sourceSize.height: 30
anchors {
verticalCenter: parent.verticalCenter
horizontalCenter: parent.horizontalCenter
}
}
MouseArea { // Zone d'interaction. On est obligé de la mettre dans la grille pour que Rectangle et MouseArea soient frères ou parent/enfant ET qu'on n'occupe qu'un espace dans la grille.
id: mouse_area_bandeau_bas_zone_retour
anchors.fill: parent
onClicked: {
shipheart_clic();
stackView.pop()
}
}
}
}
// 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 nb;
var i;
var bips;
var fonction_jack;
mise_a_jour_periodique();
//---------------------------------------------------------------------------------------
// Bips :
req = 'SELECT Valeur FROM TB_Configuration WHERE Nom="Bips" ';
res = bdd.requete_unique(req);
bips = res[0];
champ_formulaire_Bips.model.clear();
champ_formulaire_Bips.model.append({"valeur": "", "texte": qsTr("Aucun bip") });
champ_formulaire_Bips.model.append({"valeur": "beep-28.mp3", "texte": qsTr("Bip par défaut") });
champ_formulaire_Bips.model.append({"valeur": "beep-027.mp3", "texte": qsTr("Bip 2") });
champ_formulaire_Bips.model.append({"valeur": "beep-07.mp3", "texte": qsTr("Bip 3") });
champ_formulaire_Bips.model.append({"valeur": "beep-25.mp3", "texte": qsTr("Bip 4") });
champ_formulaire_Bips.model.append({"valeur": "beep-28.mp3", "texte": qsTr("Bip 5") });
champ_formulaire_Bips.model.append({"valeur": "beep-30b.mp3", "texte": qsTr("Bip 6") });
champ_formulaire_Bips.model.append({"valeur": "button-17.mp3", "texte": qsTr("Bip 7") });
champ_formulaire_Bips.model.append({"valeur": "button-21.mp3", "texte": qsTr("Bip 8") });
champ_formulaire_Bips.model.append({"valeur": "button-27.mp3", "texte": qsTr("Bip 9") });
champ_formulaire_Bips.model.append({"valeur": "button-28.mp3", "texte": qsTr("Bip 10") });
champ_formulaire_Bips.model.append({"valeur": "button-32.mp3", "texte": qsTr("Bip 11") });
champ_formulaire_Bips.model.append({"valeur": "button-33a.mp3", "texte": qsTr("Bip 12") });
champ_formulaire_Bips.model.append({"valeur": "button-35.mp3", "texte": qsTr("Bip 13") });
champ_formulaire_Bips.model.append({"valeur": "button-42.mp3", "texte": qsTr("Bip 14") });
// Positionnement de l'index sur la valeur en cours :
i = recherche_index_valeur_modele(bips, champ_formulaire_Bips.model, "bips");
if (i === -1) champ_formulaire_Bips.currentIndex = recherche_index_valeur_modele("beep-28.mp3", champ_formulaire_Bips.model, "bips");
else champ_formulaire_Bips.currentIndex = i;
//---------------------------------------------------------------------------------------
// Fonction Jack :
req = 'SELECT Valeur FROM TB_Configuration WHERE Nom="Fonction_jack" ';
res = bdd.requete_unique(req);
fonction_jack = Number(res[0]);
champ_formulaire_Fonction_jack.model.clear();
// Mise à jour des textes pour tenir compte du fait que l'état de la prise jack ne peut pas être remonté correctement (cf SDIR #232).
// champ_formulaire_Fonction_jack.model.append({"valeur": 0, "texte": qsTr("Le son de ShipBase est coupé lorsqu'une prise jack est présente sur la sortie AUDIO") });
// champ_formulaire_Fonction_jack.model.append({"valeur": 1, "texte": qsTr("Le son de ShipBase est toujours diffusé, même lorsqu'une prise jack est présente sur la sortie AUDIO") });
champ_formulaire_Fonction_jack.model.append({"valeur": 0, "texte": qsTr("Le son de ShipBase est coupé. Le son est uniquement diffusé sur la sortie AUDIO") });
champ_formulaire_Fonction_jack.model.append({"valeur": 1, "texte": qsTr("Le son est diffusé sur ShipBase et sur la sortie AUDIO") });
// Positionnement de l'index sur la valeur en cours :
i = recherche_index_valeur_modele(fonction_jack, champ_formulaire_Fonction_jack.model, "fonction_jack");
if (i === -1) champ_formulaire_Fonction_jack.currentIndex = recherche_index_valeur_modele(2, champ_formulaire_Fonction_jack.model, "fonction_jack");
else champ_formulaire_Fonction_jack.currentIndex = i;
}
//=============================================================================================================
// Mise à jour périodique
//=============================================================================================================
function mise_a_jour_periodique()
{
var req, res;
var nb;
var i;
var nom_champ, valeur_champ;
modele_grille_informations.clear();
req = 'SELECT Nom, Valeur FROM TB_Systeme ORDER BY Cle_primaire';
//console.log("req =" + req);
nb = bdd.requete(req);
for (i = 0; i < nb; i ++)
{
res = bdd.lecture_ligne();
if (!res.isEmpty)
{
switch(res[0])
{
// L'affichage de l'état de la prise Jack a été supprimé car sa détection ne fonctionne pas, malgré le traitement du SDIR #232.
// case "Presence_jack":
// nom_champ = qsTr("Prise jack sur la sortie AUDIO");
// valeur_champ = (Number(res[1]) ? qsTr("Présente") : qsTr("Absente"));
// break;
default: continue;
}
modele_grille_informations.append({"commande": false, "nom_champ": nom_champ, "valeur_champ": valeur_champ});
}
else break; // Ne devrait pas arriver....
}
}
//=============================================================================================================
// Mise à jour des valeurs saisies
//=============================================================================================================
function mise_a_jour()
{
var req;
// Maj de la variable globale pour prise en compte immédiate :
shipheart_bips = champ_formulaire_Bips.model.get(champ_formulaire_Bips.currentIndex).valeur;
// Mise à jour de la nouvelle valeur Bips dans la BDD :
req = 'UPDATE TB_Configuration SET Valeur="' + shipheart_bips + '" WHERE Nom="Bips" '
//console.log("Requête = " + req)
bdd.requete_unique(req);
// Mise à jour dans la BDD de la valeur du bip déjà positionnée par 'shipheart_clic() > emission_bip()' (mais pas encore traitée) :
req = "UPDATE TB_Systeme SET Valeur='" + shipheart_bips + "/0/0/0' WHERE Nom='New_bip' ";
//console.log("Requête = " + req)
bdd.requete_unique(req);
// Mise à jour de la nouvelle valeur Fonction_jack dans la BDD :
req = 'UPDATE TB_Configuration SET Valeur="' + champ_formulaire_Fonction_jack.model.get(champ_formulaire_Fonction_jack.currentIndex).valeur + '" WHERE Nom="Fonction_jack" '
//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);
}
//=============================================================================================================
// Test de bip
//=============================================================================================================
function test_bip(type_bip)
{
var req;
req = "UPDATE TB_Systeme SET Valeur='" + type_bip + "/0/0/0' WHERE Nom='New_bip'";
bdd.requete_unique(req);
//console.log("Requête = " + req)
synchro_bdd(shipheart_type_synchro_data);
}
//=============================================================================================================
// Recherche de l'index d'une certaine valeur dans un modele
//=============================================================================================================
function recherche_index_valeur_modele(cible, modele, nom_modele)
{
var i;
for (i=0; i<modele.count; i ++)
{
if (modele.get(i).valeur === cible) break; // Cas où la valeur cible a été trouvée parmi les valeurs du modèle
}
if (i === modele.count)
{
console.log("Aucune valeur trouvée pour cible = [" + cible + "] du modèle = " + nom_modele);
i = -1; // Cas où la valeur n'a pas été trouvée parmi les valeurs du modèle
}
//else console.log("Valeur trouvée : [" + i + "] pour cible = [" + cible + "] soit texte = [" + modele.get(i).texte + "]");
return(i);
}
}
+1998
View File
File diff suppressed because it is too large Load Diff
+309
View File
@@ -0,0 +1,309 @@
//========================================================================================================================================================================
// CONFIG_BATTERIES.QML
// Ce fichier traite la configuration des batteries. La page permet notamment de :
// - Afficher, pour toutes les batteries déjà configurées (et donc d'accéder à la configuration détaillée de l'une d'elle ; ou sa création)
// - Accéder au menu de changement de l'ordre d'affichage sur la page d'accueil
// La page est appelée depuis la page PARAMETRES_AVANCES, en cliquant sur "Batteries"
//========================================================================================================================================================================
import QtQuick 2.12
import QtQuick.Controls 2.12
import qt.koriolan.shipheart 1.0
Shipheart_page {
id: page_config_batteries
// Mis en forme :
readonly property double affichage_hauteur_utile: shipheart_hauteur_page - shipheart_hauteur_bandeau_haut - shipheart_hauteur_bandeau_bas
readonly property int hauteur_bouton_droit: 80
readonly property double pourcentage_largeur_colonne_gauche: 0.75
readonly property double pourcentage_bandeau_bas_largeur_zone_retour: 1.0
readonly property int espace_colonnes_principales: 20
readonly property int nombre_batteries_affiches_sur_une_colonne: 8
readonly property int periode_init_raffraichissement_parametres: 1 // En ms
readonly property int periode_raffraichissement_parametres: 100 // En ms
// Affichage :
property int niveau_page: 0
property bool bouton_bandeau_bas_zone_retour_actif: false
// 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();
}
if (stackView.depth === (niveau_page + 1)) // Désactive le raffraichissement quand la page n'est pas activée dans la pile
{
if (!bouton_bandeau_bas_zone_retour_actif)
{
}
}
}
}
//===========================================================
// Lorsque la page est sur le point d'être affichée dans la pile :
//===========================================================
StackView.onActivating: {
mise_a_jour_donnees(); // Permet que lors du retour sur la page, l'état des différentes variables soit mise à jour avant l'affichage proprement dit.
}
// Titre de la page :
Shipheart_titre_1 {titre:qsTr("Configuration des batteries"); hauteur: shipheart_hauteur_bandeau_haut}
//-------------------------------------------------------
// Ecran de gauche (liste des batteries) :
//-------------------------------------------------------
Rectangle {
id: zone_affichage_gauche
x: 0
y: shipheart_hauteur_bandeau_haut;
width: (shipheart_largeur_page * pourcentage_largeur_colonne_gauche) - (espace_colonnes_principales / 2)
height: affichage_hauteur_utile
color: "transparent"
//-------------------------------------------------------------------
// Grille pour afficher les batteries :
GridView {
id: grille_principale
width: parent.width
height: parent.height
cellWidth: parent.width;
cellHeight: height / nombre_batteries_affiches_sur_une_colonne
snapMode: GridView.SnapToRow // L'arret de l'affichage se fera sur une rangée entière
clip: true // Nécessaire pour que les Delegates s'arretent à l'intérieur de la GridView
model: modele_grille_principale
delegate: Rectangle {
width: grille_principale.cellWidth
height: grille_principale.cellHeight
color: "transparent"
Rectangle { // c'est le bouton proprement dit...
height: parent.height - 10
width:parent.width - 10
color: shipheart_couleur_fond_bouton
border.color: couleur_rouge
border.width: (model.etat_systeme === 0) ? 0 : 3
radius: shipheart_rayon_bouton
anchors.horizontalCenter: parent.horizontalCenter
anchors.verticalCenter: parent.verticalCenter
Text {
anchors.fill: parent
horizontalAlignment: Qt.AlignLeft
verticalAlignment: Qt.AlignVCenter
leftPadding: 20
text: model.nom
color: couleur_blanc_casse
font.pixelSize: 18
wrapMode: Label.Wrap
}
MouseArea{
anchors.fill : parent
enabled : !grille_principale.moving // Permet de bloquer le Mousearea pendant le déplacement de la grille
onReleased: { // Permet déclencher à la fin d'un clic (court) sans mouvement
shipheart_clic();
stackView.push("config_batterie.qml", {"id_batterie": model.id})
}
}
}
}
ListModel {
id: modele_grille_principale
}
Label {
anchors.fill: parent
horizontalAlignment: Qt.AlignHCenter
verticalAlignment: Qt.AlignVCenter
visible: parent.count === 0
text: qsTr("Aucune batterie n'est encore configurée...")
color: couleur_rouge_transparent
font.pixelSize: 15
font.bold: true
wrapMode: Label.Wrap
}
}
}
//-------------------------------------------------------
// Ecran Droite :
//-------------------------------------------------------
Rectangle {
id: zone_affichage_droite
x: zone_affichage_gauche.width + espace_colonnes_principales
y: shipheart_hauteur_bandeau_haut + 5; // Le +5 permet d'aligner le haut du premier bouton de droite avec le haut du premier bouton de la liste de gauche (
width: (shipheart_largeur_page * (1 - pourcentage_largeur_colonne_gauche)) - (espace_colonnes_principales / 2)
height: affichage_hauteur_utile
color: "transparent"
Column {
anchors.fill: parent
spacing: 10
//----------------------
// Bouton + :
Rectangle {
width: parent.width
height: hauteur_bouton_droit
color: shipheart_couleur_fond_bouton
radius: shipheart_rayon_bouton
visible: !clavier.active
Text {
anchors {
verticalCenter: parent.verticalCenter
horizontalCenter: parent.horizontalCenter
}
font.pixelSize: 40
text: qsTr("+")
color: couleur_blanc_casse
}
MouseArea {
anchors.fill: parent
onClicked: {
shipheart_clic();
stackView.push("config_batterie.qml");
}
}
}
//----------------------
// Bouton tri :
Rectangle {
width: parent.width
height: hauteur_bouton_droit
color: shipheart_couleur_fond_bouton
radius: shipheart_rayon_bouton
visible: !clavier.active // && (modele_grille_principale.count > 1) // SDIR #258
Image {
id: image_parametres
source: "/images/tri.png"
sourceSize.height: 25
anchors {
verticalCenter: parent.verticalCenter
horizontalCenter: parent.horizontalCenter
}
}
MouseArea {
anchors.fill: parent
onClicked: {
shipheart_clic();
stackView.push("config_ordre_elements.qml", {"table": "TB_Batteries"})
}
}
}
}
}
//-------------------------------------------------------
// Bandeau bas :
//-------------------------------------------------------
Row {
x: 0
y: shipheart_hauteur_page - shipheart_hauteur_bandeau_bas;
width: shipheart_largeur_page;
height: shipheart_hauteur_bandeau_bas;
padding: 5
spacing: 5
//.................................................
// Bouton retour
//.................................................
Rectangle {
id: bandeau_bas_zone_retour
width: (parent.width * pourcentage_bandeau_bas_largeur_zone_retour) - parent.leftPadding - (parent.spacing / 2)
height: shipheart_hauteur_bandeau_bas - parent.topPadding - parent.bottomPadding;
radius: shipheart_rayon_bouton
color: shipheart_couleur_fond_bouton
Image {
source: "/images/icone_retour.png"
sourceSize.height: 30
anchors {
verticalCenter: parent.verticalCenter
horizontalCenter: parent.horizontalCenter
}
}
MouseArea { // Zone d'interaction. On est obligé de la mettre dans la grille pour que Rectangle et MouseArea soient frères ou parent/enfant ET qu'on n'occupe qu'un espace dans la grille.
id: mouse_area_bandeau_bas_zone_retour
anchors.fill: parent
onClicked: {
shipheart_clic();
stackView.pop()
}
}
}
}
// 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()
{
niveau_page = stackView.depth - 1; // Permet de stocker le niveau de la page dans la Stackview
mise_a_jour_donnees();
}
//=============================================================================================================
// Mise à jour des données
//=============================================================================================================
function mise_a_jour_donnees()
{
var req, res;
var i;
var nb_batteries;
modele_grille_principale.clear(); // RAZ des valeurs existantes
req = 'SELECT Cle_primaire, ';
req += ' Etat_systeme, ';
req += ' Nom ';
req += ' FROM TB_Batteries ORDER BY Priorite_affichage_accueil';
//console.log("req = [ " + req + '; ]');
nb_batteries = bdd.requete(req);
for (i = 0; i < nb_batteries; i ++)
{
res = bdd.lecture_ligne();
if (!res.isEmpty)
{
modele_grille_principale.append({"id": Number(res[0]), "etat_systeme": Number(res[1]), "nom": res[2] });
}
else break; // Ne devrait pas arriver....
}
}
}
+2555
View File
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,874 @@
//========================================================================================================================================================================
// CONFIG_CIRCUIT_CONDITION_COMMANDE_AUTO.QML
// Ce fichier traite la configuration d'une condition d'une commande Auto d'un circuit. La page permet notamment de :
// - Modifier la config de cette condition (ou de la créer la première fois),
// La page est appelée depuis la page config_circuit_commande_auto
//========================================================================================================================================================================
import QtQuick 2.12
import QtQml.Models
import qt.koriolan.shipheart 1.0
import"fonctions.js" as Fonctions
Shipheart_page {
id: page_config_capteur_calcule_booleen_condition
// Mis en forme :
readonly property double pourcentage_largeur_colonne_gauche: 0.75
readonly property double espace_colonnes_principales: 20
readonly property double pourcentage_hauteur_ligne_haut: 1
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 hauteur_ligne_formulaire: 60
readonly property double pourcentage_largeur_labels_formulaire: 0.4
readonly property double pourcentage_largeur_unites_formulaire: 0.1
readonly property int hauteur_bouton_droit: 80
readonly property double pourcentage_bandeau_bas_largeur_zone_retour: 1
readonly property int periode_init_raffraichissement_parametres: 1 // En ms
readonly property int periode_raffraichissement_parametres: 1000 // En ms
// Données :
property bool flag_init: true // Permet de bloquer certains affichage tant que l'init n'est pas terminée
property string nom_capteur: ""
property bool flag_new;
property int type_source: 0
property int id_source: 0
property int id_variable: 0
property int test: 0
property double seuil: 0
property string seuil_unite: ""
property bool flag_test_binaire: false // Indique si l'évènement porte sur une condition binaire ou non (exemple: Capteur>Entrée AUX)
// Modèles :
// Note : les modèles chargés dans 'main.qml' n'ont pas à être rechargés dans les pages suivantes
//-------------------------------------------------
// Signaux :
//-------------------------------------------------
signal enregistrer_config_capteur_calcule_booleen_condition(bool flag_new, int type_source, int id_source, int id_variable, int test, double seuil) // Valeurs à lire lorsque le "OnEnregistrer' est activé
//===========================================================
// 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 {
}
}
}
// Titre de la page :
Shipheart_titre_1 {titre: qsTr("Création d'une condition du capteur [%1]").arg(nom_capteur); 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);
color: "transparent"
ListView {
id: grille_principale
width: parent.width
height: parent.height
snapMode: GridView.SnapToRow // L'arret de l'affichage se fera sur une rangée entière
clip: true // Nécessaire pour que les Delegates s'arretent à l'intérieur de la GridView
model: modele_grille_principale
footer: Rectangle { // Permet de dégager la place suffisante en fin de GridView pour accéder même au dernier élément lorsque que clavier est déplié
height: shipheart_hauteur_clavier
color: "transparent"
}
ObjectModel {
id: modele_grille_principale
//-------------------------------------------------------------------------------
// Ligne de formulaire : Type_source
Rectangle {
width: (shipheart_largeur_page * pourcentage_largeur_colonne_gauche) - (espace_colonnes_principales / 2)
height: visible ? hauteur_ligne_formulaire : 0 // Permet que la ligne n'occupe aucune place quand elle n'est pas visible
color: "transparent"
visible: (!flag_init)
Shipheart_formulaire_label {
text: qsTr("Type")
width: parent.width * pourcentage_largeur_labels_formulaire
couleur: (champ_formulaire_Type_source.currentIndex !== 0)
? shipheart_formulaire_couleur_texte_par_defaut
: shipheart_formulaire_couleur_texte_en_erreur
}
Shipheart_formulaire_combobox {
id: champ_formulaire_Type_source
model: ListModel { }
width: parent.width * (1 - pourcentage_largeur_labels_formulaire)
onActivated: {
shipheart_clic();
if (type_source !== champ_formulaire_Type_source.model.get(champ_formulaire_Type_source.currentIndex).valeur)
{
type_source = champ_formulaire_Type_source.model.get(champ_formulaire_Type_source.currentIndex).valeur;
id_source = 0;
id_variable = 0;
test = 0;
mise_a_jour_source();
}
}
}
Shipheart_formulaire_ligne_separation {
haut_bas_hautbas: shipheart_formulaire_ligne_separation_basse
}
}
//-------------------------------------------------------------------------------
// Ligne de formulaire : Source
Rectangle {
width: (shipheart_largeur_page * pourcentage_largeur_colonne_gauche) - (espace_colonnes_principales / 2)
height: visible ? hauteur_ligne_formulaire : 0 // Permet que la ligne n'occupe aucune place quand elle n'est pas visible
color: "transparent"
visible: (!flag_init)
&& (type_source > 0)
Shipheart_formulaire_label {
text: qsTr("Source")
width: parent.width * pourcentage_largeur_labels_formulaire
couleur: (champ_formulaire_Source.currentIndex !== 0)
? shipheart_formulaire_couleur_texte_par_defaut
: shipheart_formulaire_couleur_texte_en_erreur
}
Shipheart_formulaire_combobox {
id: champ_formulaire_Source
model: ListModel { }
width: parent.width * (1 - pourcentage_largeur_labels_formulaire)
onActivated: {
shipheart_clic();
if (id_source !== champ_formulaire_Source.model.get(champ_formulaire_Source.currentIndex).valeur)
{
id_source = champ_formulaire_Source.model.get(champ_formulaire_Source.currentIndex).valeur;
id_variable = 0;
test = 0;
mise_a_jour_variable();
}
}
}
Shipheart_formulaire_ligne_separation {
haut_bas_hautbas: shipheart_formulaire_ligne_separation_basse
}
}
//-------------------------------------------------------------------------------
// Ligne de formulaire : Variable
Rectangle {
width: parent.width
height: visible ? hauteur_ligne_formulaire : 0 // Permet que la ligne n'occupe aucune place quand elle n'est pas visible
color: "transparent"
visible: (!flag_init)
&& (type_source > 0)
&& (id_source > 0)
&& (champ_formulaire_Variable.model.count > 2) // Comme on a toujours "Non défini", si Count<=2, c'est qu'on n'a qu'un seul choix qu'il n'est pas nécessaire d'afficher...
Shipheart_formulaire_label {
text: qsTr("Variable")
width: parent.width * pourcentage_largeur_labels_formulaire
couleur: (champ_formulaire_Variable.currentIndex !== 0)
? shipheart_formulaire_couleur_texte_par_defaut
: shipheart_formulaire_couleur_texte_en_erreur
}
Shipheart_formulaire_combobox {
id: champ_formulaire_Variable
model: ListModel { }
width: parent.width * (1 - pourcentage_largeur_labels_formulaire)
onActivated: {
shipheart_clic();
if (id_variable !== champ_formulaire_Variable.model.get(champ_formulaire_Variable.currentIndex).valeur)
{
id_variable = champ_formulaire_Variable.model.get(champ_formulaire_Variable.currentIndex).valeur;
seuil_unite = champ_formulaire_Variable.model.get(champ_formulaire_Variable.currentIndex).unite;
mise_a_jour_test();
}
}
}
Shipheart_formulaire_ligne_separation {
haut_bas_hautbas: shipheart_formulaire_ligne_separation_basse
}
}
//-------------------------------------------------------------------------------
// Ligne de formulaire : Test
Rectangle {
width: parent.width
height: visible ? hauteur_ligne_formulaire : 0 // Permet que la ligne n'occupe aucune place quand elle n'est pas visible
color: "transparent"
visible: (!flag_init)
&& (type_source > 0)
&& (id_source > 0)
&& (id_variable > 0)
Shipheart_formulaire_label {
text: qsTr("Test")
width: parent.width * pourcentage_largeur_labels_formulaire
couleur: (champ_formulaire_Test.currentIndex !== 0)
? shipheart_formulaire_couleur_texte_par_defaut
: shipheart_formulaire_couleur_texte_en_erreur
}
Shipheart_formulaire_combobox {
id: champ_formulaire_Test
model: ListModel { }
width: parent.width * (1 - pourcentage_largeur_labels_formulaire)
onActivated: {
shipheart_clic();
test = champ_formulaire_Test.model.get(champ_formulaire_Test.currentIndex).valeur;
}
}
Shipheart_formulaire_ligne_separation {
haut_bas_hautbas: shipheart_formulaire_ligne_separation_basse
}
}
//-------------------------------------------------------------------------------
// Ligne de formulaire : Seuil
Rectangle {
width: (shipheart_largeur_page * pourcentage_largeur_colonne_gauche) - (espace_colonnes_principales / 2)
height: visible ? hauteur_ligne_formulaire : 0 // Permet que la ligne n'occupe aucune place quand elle n'est pas visible
color: "transparent"
visible: (!flag_init)
&& (type_source > 0)
&& (id_source > 0)
&& (id_variable > 0)
&& (!flag_test_binaire) // Si on est sur un test binaire, il est inutile d'afficher le seuil...
Shipheart_formulaire_label {
text: qsTr("Seuil")
width: parent.width * pourcentage_largeur_labels_formulaire
}
Shipheart_formulaire_textfield {
id: champ_formulaire_Seuil
width: parent.width * (1 - pourcentage_largeur_labels_formulaire - pourcentage_largeur_unites_formulaire)
x: parent.width * pourcentage_largeur_labels_formulaire
flag_num_only: true
}
Shipheart_formulaire_unite { // Unités :
text: seuil_unite
width: parent.width * pourcentage_largeur_unites_formulaire
}
Shipheart_formulaire_ligne_separation {
haut_bas_hautbas: shipheart_formulaire_ligne_separation_basse
}
}
} // Fin de modele grille principale (ObjectModel )
}
}
//-------------------------------------------------------
// 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 * pourcentage_hauteur_ligne_haut) - (espace_lignes_principales / 2);
color: "transparent"
Column {
anchors.fill: parent
spacing: 10
//.......................................................
// Bouton Enregistrer/créer :
//.......................................................
Rectangle {
width: parent.width
height: hauteur_bouton_droit
color: shipheart_couleur_fond_bouton
radius: shipheart_rayon_bouton
visible:
// (!clavier.active) && // SDIR #255
(type_source > 0) &&
(id_source > 0) &&
(id_variable > 0) &&
(test > 0)
Text {
anchors {
verticalCenter: parent.verticalCenter
horizontalCenter: parent.horizontalCenter
}
font.pixelSize: 20
text: flag_new ? qsTr("Créer la condition") : qsTr("Enregistrer")
color: couleur_blanc_casse
}
MouseArea {
anchors.fill: parent
onClicked: {
shipheart_clic();
seuil = champ_formulaire_Seuil.text;
// Renvoi des nouvelles valeurs vers la page d'appel (qu'on soit en Création ou Modification) :
page_config_capteur_calcule_booleen_condition.enregistrer_config_capteur_calcule_booleen_condition(flag_new, type_source, id_source, id_variable, test, seuil);
// Fermeture de la page dès que les données ont été remontées :
stackView.pop();
}
}
}
//-------------------------------------------------------
// Timer de reconfiguration :
//-------------------------------------------------------
Timer {
id: timer_tempo_reconfiguration
interval: 250 // Cette tempo n'est utilisée que pour le premier cycle
triggeredOnStart: false
running: false
repeat: true
onTriggered: {
if (bdd.check_fin_configuration())
{
timer_tempo_reconfiguration.stop();
stackView.pop();
}
else timer_tempo_reconfiguration.interval = 100 // Tempo plus courte pour les lectures suivantes")
}
}
}
}
//-------------------------------------------------------
// 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 * pourcentage_bandeau_bas_largeur_zone_retour) - (2 * parent.padding)
height: shipheart_hauteur_bandeau_bas - parent.topPadding - parent.bottomPadding;
radius: shipheart_rayon_bouton
color: shipheart_couleur_fond_bouton
Image {
source: "/images/icone_retour.png"
sourceSize.height: 30
anchors {
verticalCenter: parent.verticalCenter
horizontalCenter: parent.horizontalCenter
}
}
MouseArea { // Zone d'interaction. On est obligé de la mettre dans la grille pour que Rectangle et MouseArea soient frères ou parent/enfant ET qu'on n'occupe qu'un espace dans la grille.
id: mouse_area_bandeau_bas_zone_retour
anchors.fill: parent
onClicked: {
shipheart_clic();
stackView.pop()
}
}
}
}
//===========================================================
// Busy Indicator :
//===========================================================
Shipheart_busy_indicator {
id: busy_indicator
running: timer_tempo_reconfiguration.running
}
// 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 i;
// Détection d'une création vs une modification :
if ((type_source > 0) && (id_source> 0) && (id_variable> 0)) flag_new = false;
else flag_new = true;
// Mise à jour des champs texte :
champ_formulaire_Seuil.text = seuil;
// Mise à jour du modèle de Type de source :
champ_formulaire_Type_source.model.clear();
champ_formulaire_Type_source.model.append( {
"valeur": 0,
"texte": qsTr("Non défini") });
champ_formulaire_Type_source.model.append( {
"valeur": shipheart_source_mesure_circuit,
"texte": qsTr("Circuit") });
champ_formulaire_Type_source.model.append( {
"valeur": shipheart_source_mesure_capteur,
"texte": qsTr("Capteur") });
champ_formulaire_Type_source.model.append( {
"valeur": shipheart_source_mesure_reservoir,
"texte": qsTr("Réservoir") });
champ_formulaire_Type_source.model.append( {
"valeur": shipheart_source_mesure_batterie,
"texte": qsTr("Batterie") });
// Type source : positionnement de l'index sur la valeur en cours :
i = recherche_index_valeur_modele(type_source, champ_formulaire_Type_source.model, "Type_source");
if (i === -1) champ_formulaire_Type_source.currentIndex = recherche_index_valeur_modele(-1, champ_formulaire_Type_source.model, "Type_source");
else champ_formulaire_Type_source.currentIndex = i;
// Mise à jour en cascade :
if (type_source > 0) mise_a_jour_source();
flag_init = false; // Indique que l'init est terminée
}
//=============================================================================================================
// Mise à jour du modèle de source
//=============================================================================================================
function mise_a_jour_source()
{
var req, res;
var nb;
var i;
var flag_familles;
champ_formulaire_Source.model.clear();
champ_formulaire_Source.model.append({"valeur": 0, "texte": qsTr("Non défini") , "groupe": false });
// Choix de le requete en fonction du type :
switch(type_source)
{
case shipheart_source_mesure_circuit:
req = 'SELECT Cle_primaire, Nom, Nom_famille, Priorite_affichage_accueil FROM TB_Circuits, '
req += ' (SELECT Cle_primaire AS Cle_famille, Nom AS Nom_famille, Priorite_affichage AS Priorite_famille FROM TB_Familles WHERE Type_source=' + shipheart_type_source_circuits + ') AS TB_Familles '
req += ' WHERE ID_Famille=Cle_famille '
req += ' ORDER BY Priorite_famille, ABS(Priorite_affichage_accueil) ';
flag_familles = true;
break;
case shipheart_source_mesure_capteur:
req = 'SELECT Cle_primaire, Nom, Nom_famille, Priorite_affichage_accueil, Type_entrees FROM TB_Capteurs, '
req += ' (SELECT Cle_primaire AS Cle_famille, Nom AS Nom_famille, Priorite_affichage AS Priorite_famille FROM TB_Familles WHERE Type_source=' + shipheart_type_source_capteurs + ') AS TB_Familles ';
req += ' WHERE ID_Famille=Cle_famille ';
req += ' ORDER BY Priorite_famille, ABS(Priorite_affichage_accueil) ';
flag_familles = true;
break;
case shipheart_source_mesure_reservoir:
req = 'SELECT Cle_primaire, Nom, Nom_famille, Priorite_affichage_accueil FROM TB_Reservoirs, '
req += ' (SELECT Cle_primaire AS Cle_famille, Nom AS Nom_famille, Priorite_affichage AS Priorite_famille FROM TB_Familles WHERE Type_source=' + shipheart_type_source_reservoirs + ') AS TB_Familles '
req += ' WHERE ID_Famille=Cle_famille '
req += ' ORDER BY Priorite_famille, ABS(Priorite_affichage_accueil) ';
flag_familles = true;
break;
case shipheart_source_mesure_batterie:
req = 'SELECT Cle_primaire, Nom FROM TB_Batteries '
req += 'WHERE 1 '
req += 'ORDER BY Priorite_affichage_accueil, Nom';
flag_familles = false;
break;
}
//console.log("req = [ " + req + "; ]");
nb = bdd.requete(req);
var prec_famille = "";
var flag_aine_famille;
var flag_capteur_calcule;
for (i = 0; i < nb; i ++)
{
res = bdd.lecture_ligne();
if (!res.isEmpty)
{
// Ajout d'une ligne par famille (si le type l'autorise) :
if (flag_familles)
{
if (prec_famille !== res[2])
{
champ_formulaire_Source.model.append({"valeur": Number(res[0]), "texte": res[2], "groupe": true, "actif": true, "capteur_calcule": false });
prec_famille = res[2];
}
}
// Détection d'un capteur calculé :
flag_capteur_calcule = false;
if (type_source === shipheart_source_mesure_capteur)
{
if (Number(res[4]) >= shipheart_capteur_type_entrees_calculee_booleen) flag_capteur_calcule = true;
}
// Ajout de la ligne "normale" dans le modèle :
champ_formulaire_Source.model.append({"valeur": Number(res[0]), "texte": res[1], "groupe": false, "actif": true, "capteur_calcule": flag_capteur_calcule });
}
else break; // Ne devrait pas arriver....
}
//-------------------------------------------------------------------------------
// Dévalidation des entrées du modèle qui créeraient des références circulaires :
if (type_source === shipheart_source_mesure_capteur) // Cas d'une condition sur capteur uniquement
{
// Parcours de tout le modèle :
for (i = 0; i < champ_formulaire_Source.model.count; i ++)
{
if (champ_formulaire_Source.model.get(i).capteur_calcule) // On ne traite que les capteurs calculés (les autres ne présentent pas de risque de réf circulaires)
{
var cle_capteur = champ_formulaire_Source.model.get(i).valeur;
// Dévalidation de tous les capteurs calculés dans le doute :
champ_formulaire_Source.model.set(i, {"actif": false });
/*
// Dévalidation du capteur auquel appartient la condition :
if (cle_capteur === id_capteur) champ_formulaire_Source.model.set(i, {"actif": false });
else
{
// A TRAITER !!!!!
}
*/
}
}
}
//------------------------------------------------------------------------------------------------------------------------
// Source : positionnement de l'index sur la valeur en cours :
i = recherche_index_valeur_modele(id_source, champ_formulaire_Source.model, "Source");
if (i === -1) champ_formulaire_Source.currentIndex = recherche_index_valeur_modele(0, champ_formulaire_Source.model, "Source");
else champ_formulaire_Source.currentIndex = i;
//------------------------------------------------------------------------------------------------------------------------
// Mise à jour en cascade :
if (id_source > 0) mise_a_jour_variable();
}
//=============================================================================================================
// Mise à jour du modèle de variable
//=============================================================================================================
function mise_a_jour_variable()
{
var req, res;
var nb;
var i;
var libelle;
// Effacement du modèle et premier enregistrement avec 'Non défini' ::
champ_formulaire_Variable.model.clear();
champ_formulaire_Variable.model.append( {
"valeur": 0,
"texte": qsTr("Non défini"),
"unite": "",
"actif": true });
switch(type_source)
{
//----------------------------------------
case shipheart_source_mesure_circuit:
// Recherche du type de circuit :
req = 'SELECT Type FROM TB_Modules_config WHERE Adresse_k_bus IN (SELECT Adresse_module FROM TB_Circuits WHERE Cle_primaire=' + id_source + ')';
//console.log("Requête = [ " + req + "; ]");
res = bdd.requete_unique(req);
var type_circuit = Number(res[0]);
// Recherche des entrées de TB_Variables applicables à la source sélectionnée :
req = "SELECT Cle_primaire, Unite, Code FROM TB_Variables WHERE Type=" + type_source + "; ";
//console.log("Requête = [ " + req + "; ]");
nb = bdd.requete(req);
for (i = 0; i < nb; i ++)
{
res = bdd.lecture_ligne();
if (!res.isEmpty)
{
switch(res[2])
{
//---------------------
case "Circ_tension":
if (type_circuit === shipheart_type_module_shippower4AC) continue;
break;
//---------------------
case "Circ_frequence":
if (type_circuit !== shipheart_type_module_shippower4AC) continue;
break;
//---------------------
case "Circ_tension_secteur":
if (type_circuit !== shipheart_type_module_shippower4AC) continue;
break;
}
libelle = Fonctions.nom_variable(res[2]);
champ_formulaire_Variable.model.append( {
"valeur": Number(res[0]),
"texte": libelle,
"unite": res[1],
"actif": true });
}
}
// Mise à jour du flag de test binaire :
flag_test_binaire = false;
break;
//----------------------------------------
case shipheart_source_mesure_capteur:
// Recherche de la variable du capteur :
req = "SELECT ID_Variable, Type_entrees FROM TB_Capteurs WHERE Cle_primaire=" + id_source + "; ";
//console.log("Requête = [ " + req + "; ]");
res = bdd.requete_unique(req);
var type = Number(res[1]);
if (type < shipheart_capteur_type_entrees_calculee_booleen) // Si le capteur est calculé, on ne peut pas chercher (et trouver d'ID_Variable)
{
// Recherche des informations de la variable du capteur dans TB_Variables :
req = "SELECT Cle_primaire, Nom, Unite FROM TB_Variables WHERE Cle_primaire=" + Number(res[0]) + "; ";
//console.log("Requête = [ " + req + "; ]");
res = bdd.requete_unique(req);
champ_formulaire_Variable.model.append( {
"valeur": Number(res[0]),
"texte": res[1],
"unite": res[2],
"actif": true });
// Mise à jour de la variable car on n'a pas le choix :
id_variable = Number(res[0]);
}
// Mise à jour du flag de test binaire :
if ((type == shipheart_capteur_type_entrees_aux) ||
(type == shipheart_capteur_type_entrees_calculee_booleen) ) flag_test_binaire = true;
else flag_test_binaire = false;
break;
//----------------------------------------
case shipheart_source_mesure_reservoir:
// Recherche des entrées de TB_Variables applicables à la source sélectionnée :
req = "SELECT Cle_primaire, Nom, Unite FROM TB_Variables WHERE Code='Res_niveau'; ";
//console.log("Requête = [ " + req + "; ]");
res = bdd.requete_unique(req);
champ_formulaire_Variable.model.append( {
"valeur": Number(res[0]),
"texte": res[1],
"unite": res[2],
"actif": true });
// Mise à jour de la variable car on n'a pas le choix :
id_variable = Number(res[0]);
// Mise à jour du flag de test binaire :
flag_test_binaire = false;
break;
//----------------------------------------
case shipheart_source_mesure_batterie:
// Recherche du type de capteur de batterie :
req = "SELECT Type_entrees FROM TB_Batteries WHERE Cle_primaire=" + id_source + "; ";
//console.log("Requête = [ " + req + "; ]");
res = bdd.requete_unique(req);
var type_entrees = Number(res[0]);
// Recherche des entrées de TB_Variables applicables à la source sélectionnée :
req = "SELECT Cle_primaire, Unite, Code FROM TB_Variables WHERE Type=" + type_source + "; ";
//console.log("Requête = [ " + req + "; ]");
nb = bdd.requete(req);
for (i = 0; i < nb; i ++)
{
res = bdd.lecture_ligne();
if (!res.isEmpty)
{
switch(res[2])
{
//---------------------
case "Batt_temperature":
if (type_entrees !== shipheart_capteur_type_entrees_ibs) continue;
break;
//---------------------
case "Batt_courant_differentiel":
if (type_entrees !== shipheart_capteur_type_entrees_ibs) continue;
break;
//---------------------
case "Batt_courant_charge":
if (type_entrees !== shipheart_capteur_type_entrees_ibs) continue;
break;
//---------------------
case "Batt_soh":
if (type_entrees !== shipheart_capteur_type_entrees_ibs) continue;
break;
//---------------------
/* case "Batt_autonomie":
if (type_entrees !== shipheart_capteur_type_entrees_ibs) continue;
break;
*/
}
libelle = Fonctions.nom_variable(res[2]);
champ_formulaire_Variable.model.append( {
"valeur": Number(res[0]),
"texte": libelle,
"unite": res[1],
"actif": true });
}
}
// Mise à jour du flag de test binaire :
flag_test_binaire = false;
break;
//----------------------------------------
default:
break;
}
// Mise à jour de l'index courant du modèle :
i = recherche_index_valeur_modele(id_variable, champ_formulaire_Variable.model, "id_variable");
if (i === -1) champ_formulaire_Variable.currentIndex = recherche_index_valeur_modele(0, champ_formulaire_Variable.model, "id_variable");
else champ_formulaire_Variable.currentIndex = i;
// Mise à jour de l'unité correspondant à la variable en cours :
seuil_unite = champ_formulaire_Variable.model.get(champ_formulaire_Variable.currentIndex).unite;
// Mise à jour en cascade :
if (id_variable > 0) mise_a_jour_test();
}
//=============================================================================================================
// Mise à jour du modèle
//=============================================================================================================
function mise_a_jour_test()
{
var i;
champ_formulaire_Test.model.clear();
champ_formulaire_Test.model.append( {
"valeur": shipheart_test_non_defini,
"texte": qsTr("Non défini"),
"actif": true });
if (flag_test_binaire)
{
champ_formulaire_Test.model.append( {
"valeur": shipheart_test_strictement_superieur,
"texte": qsTr("Actif"),
"actif": true });
champ_formulaire_Test.model.append( {
"valeur": shipheart_test_strictement_inferieur,
"texte": qsTr("Inactif"),
"actif": true });
}
else
{
champ_formulaire_Test.model.append( {
"valeur": shipheart_test_strictement_inferieur,
"texte": qsTr("&lt; au seuil"),
"actif": true });
champ_formulaire_Test.model.append( {
"valeur": shipheart_test_inferieur_ou_egal,
"texte": qsTr("&le; au seuil"),
"actif": true });
champ_formulaire_Test.model.append( {
"valeur": shipheart_test_egal,
"texte": qsTr("= au seuil"),
"actif": true });
champ_formulaire_Test.model.append( {
"valeur": shipheart_test_superieur_ou_egal,
"texte": qsTr("&ge; au seuil"),
"actif": true });
champ_formulaire_Test.model.append( {
"valeur": shipheart_test_strictement_superieur,
"texte": qsTr("&gt; au seuil"),
"actif": true });
}
// Mise à jour de l'index courant du modèle :
i = recherche_index_valeur_modele(test, champ_formulaire_Test.model, "Test");
if (i === -1) champ_formulaire_Test.currentIndex = recherche_index_valeur_modele(0, champ_formulaire_Test.model, "Test");
else champ_formulaire_Test.currentIndex = i;
}
//=============================================================================================================
// Recherche de l'index d'une certaine valeur dans un modele
//=============================================================================================================
function recherche_index_valeur_modele(cible, modele, nom_modele)
{
var i;
for (i=0; i<modele.count; i ++)
{
if (modele.get(i).valeur === cible) break; // Cas où la valeur cible a été trouvée parmi les valeurs du modèle
}
if (i === modele.count)
{
console.log("Aucune valeur trouvée pour cible = [" + cible + "] du modèle = " + nom_modele);
i = -1; // Cas où la valeur n'a pas été trouvée parmi les valeurs du modèle
}
//else console.log("Valeur trouvée : [" + i + "] pour cible = [" + cible + "] soit texte = [" + modele.get(i).texte + "]");
return(i);
}
}
+478
View File
@@ -0,0 +1,478 @@
//========================================================================================================================================================================
// CONFIG_CAPTEURS.QML
// Ce fichier traite la configuration des capteurs. La page permet notamment de :
// - Afficher, pour tous les capteurs déjà configurés (et donc d'accéder à la configuration détaillée de l'un d'eux ; ou sa création)
// - Déclencher la recherche de nouveaux capteurs OW
// La page est appelée depuis la page PARAMETRES_AVANCES, en cliquant sur "Capteurs"
//========================================================================================================================================================================
import QtQuick 2.12
import QtQuick.Controls 2.12
import qt.koriolan.shipheart 1.0
import"fonctions.js" as Fonctions
Shipheart_page {
id: page_config_capteurs
// Mis en forme :
readonly property double affichage_hauteur_utile: shipheart_hauteur_page - shipheart_hauteur_bandeau_haut - shipheart_hauteur_bandeau_bas
readonly property int hauteur_bouton_droit: 80
readonly property double pourcentage_largeur_colonne_gauche: 0.75
readonly property int espace_colonnes_principales: 20
readonly property double pourcentage_bandeau_bas_largeur_zone_retour: 1.0
readonly property int nombre_capteurs_affiches_sur_une_colonne: 8
readonly property int periode_init_raffraichissement_parametres: 1 // En ms
readonly property int periode_raffraichissement_parametres: 1000 // En ms /!\ Laisser à 1s afin que le compteur secondes fonctionne correctement...
// Affichage :
// 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(); // Fait via StackView.onActivating
}
else
{
mise_a_jour_donnees();
}
}
}
//===========================================================
// Lorsque la page est sur le point d'être affichée dans la pile :
//===========================================================
StackView.onActivating: {
mise_a_jour_donnees(); // Permet que lors du retour sur la page, l'état des différentes variables soit mise à jour avant l'affichage proprement dit.
}
// Titre de la page :
Shipheart_titre_1 {titre:qsTr("Configuration des capteurs"); hauteur: shipheart_hauteur_bandeau_haut}
//-------------------------------------------------------
// Ecran de gauche (liste des capteurs) :
//-------------------------------------------------------
Rectangle {
id: zone_affichage_gauche
x: 0
y: shipheart_hauteur_bandeau_haut;
width: (shipheart_largeur_page * pourcentage_largeur_colonne_gauche) - (espace_colonnes_principales / 2)
height: affichage_hauteur_utile
color: "transparent"
//-------------------------------------------------------------------
// Grille pour afficher les capteurs :
GridView {
id: grille_principale
width: parent.width
height: parent.height
cellWidth: parent.width;
cellHeight: height / nombre_capteurs_affiches_sur_une_colonne
snapMode: GridView.SnapToRow // L'arret de l'affichage se fera sur une rangée entière
clip: true // Nécessaire pour que les Delegates s'arretent à l'intérieur de la GridView
model: modele_grille_principale
delegate: Rectangle {
width: grille_principale.cellWidth
height: grille_principale.cellHeight
color: "transparent"
// Ajout d'une ligne de sous-titre avec le nom du famille de capteurs si c'est le premier capteur de la famille :
Shipheart_titre_3 {titre:model.nom_famille; hauteur: (affichage_hauteur_utile / nombre_capteurs_affiches_sur_une_colonne); visible: (model.nom_famille !== "")}
Rectangle { // c'est le bouton proprement dit...
height: parent.height - 10
width:parent.width - 10
color: shipheart_couleur_fond_bouton
radius: shipheart_rayon_bouton
border.color: couleur_rouge
border.width: (model.etat_systeme === 0) ? 0 : 3
anchors.horizontalCenter: parent.horizontalCenter
anchors.verticalCenter: parent.verticalCenter
visible: (model.nom_famille === "")
// Nom du capteur :
Text {
anchors.fill: parent
horizontalAlignment: Qt.AlignLeft
verticalAlignment: Qt.AlignVCenter
leftPadding: 20
text: model.nom
color: couleur_blanc_casse
font.pixelSize: 18
wrapMode: Label.Wrap
}
// Valeur actuelle du capteur :
Text {
anchors.fill: parent
horizontalAlignment: Qt.AlignRight
verticalAlignment: Qt.AlignVCenter
rightPadding: 20
text: model.valeur
color: couleur_gris_moyen
font.pixelSize: 18
}
// Triangle bleu indiquant un capteur masqué :
Image {
id: image_capteur_masque // Affichage du coin inférieur-droit en couleur si le capteur est masqué
source: "/images/coin_bas_droite_bleu_koriolan.png"
sourceSize.height: 20
anchors {
bottom: parent.bottom
right: parent.right
}
visible: model.flag_masque ? true : false
}
MouseArea{
anchors.fill : parent
enabled : !grille_principale.moving // Permet de bloquer le Mousearea pendant le déplacement de la grille
onReleased: { // Permet déclencher à la fin d'un clic (court) sans mouvement
shipheart_clic();
stackView.push("config_capteur.qml", {"id_capteur": model.id})
}
}
}
}
ListModel {
id: modele_grille_principale
}
Label {
anchors.fill: parent
horizontalAlignment: Qt.AlignHCenter
verticalAlignment: Qt.AlignVCenter
visible: parent.count === 0
text: qsTr("Aucun capteur n'est encore configuré...")
color: couleur_rouge_transparent
font.pixelSize: 15
font.bold: true
wrapMode: Label.Wrap
}
}
}
//-------------------------------------------------------
// Ecran Droite :
//-------------------------------------------------------
Rectangle {
id: zone_affichage_droite
x: zone_affichage_gauche.width + espace_colonnes_principales
y: shipheart_hauteur_bandeau_haut + 5; // Le +5 permet d'aligner le haut du premier bouton de droite avec le haut du premier bouton de la liste de gauche (
width: (shipheart_largeur_page * (1 - pourcentage_largeur_colonne_gauche)) - (espace_colonnes_principales / 2)
height: affichage_hauteur_utile
color: "transparent"
Column {
anchors.fill: parent
spacing: 10
//----------------------
// Bouton + :
Rectangle {
width: parent.width
height: hauteur_bouton_droit
color: shipheart_couleur_fond_bouton
radius: shipheart_rayon_bouton
visible: !clavier.active
Text {
anchors {
verticalCenter: parent.verticalCenter
horizontalCenter: parent.horizontalCenter
}
font.pixelSize: 40
text: qsTr("+")
color: couleur_blanc_casse
}
MouseArea {
anchors.fill: parent
onClicked: {
shipheart_clic();
stackView.push("config_capteur.qml");
}
}
}
//----------------------
// Bouton tri :
Rectangle {
width: parent.width
height: hauteur_bouton_droit
color: shipheart_couleur_fond_bouton
radius: shipheart_rayon_bouton
visible: !clavier.active // && (modele_grille_principale.count > 1) // SDIR #258
Image {
id: image_parametres
source: "/images/tri.png"
sourceSize.height: 25
anchors {
verticalCenter: parent.verticalCenter
horizontalCenter: parent.horizontalCenter
}
}
MouseArea {
anchors.fill: parent
onClicked: {
shipheart_clic();
stackView.push("config_ordre_elements.qml", {"table": "TB_Capteurs"})
}
}
}
}
}
//-------------------------------------------------------
// Bandeau bas :
//-------------------------------------------------------
Row {
x: 0
y: shipheart_hauteur_page - shipheart_hauteur_bandeau_bas;
width: shipheart_largeur_page;
height: shipheart_hauteur_bandeau_bas;
padding: 5
spacing: 5
//.................................................
// Bouton retour
//.................................................
Rectangle {
id: bandeau_bas_zone_retour
width: (parent.width * pourcentage_bandeau_bas_largeur_zone_retour) - parent.leftPadding - (parent.spacing / 2)
height: shipheart_hauteur_bandeau_bas - parent.topPadding - parent.bottomPadding;
radius: shipheart_rayon_bouton
color: shipheart_couleur_fond_bouton
Image {
source: "/images/icone_retour.png"
sourceSize.height: 30
anchors {
verticalCenter: parent.verticalCenter
horizontalCenter: parent.horizontalCenter
}
}
MouseArea { // Zone d'interaction. On est obligé de la mettre dans la grille pour que Rectangle et MouseArea soient frères ou parent/enfant ET qu'on n'occupe qu'un espace dans la grille.
id: mouse_area_bandeau_bas_zone_retour
anchors.fill: parent
onClicked: {
shipheart_clic();
stackView.pop()
}
}
}
}
// 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()
{
mise_a_jour_donnees();
}
//=============================================================================================================
// Mise à jour des données
//=============================================================================================================
function mise_a_jour_donnees()
{
var req, res;
var sous_req, sous_res;
var i, j;
var nb_capteurs;
var prec_famille;
// Grille principale (liste des capteurs configurés) :
modele_grille_principale.clear();
// Recherche des valeurs en BDD :
req = ' SELECT Cle_primaire, ';
req += ' Etat_systeme, '
req += ' Nom, ';
req += ' Type_entrees, ';
req += ' Entrees, ';
req += ' ID_Variable, ';
req += ' Valeur, ';
req += ' Precision_affichage_accueil, ';
req += ' Liste_valeurs_textes, ';
req += ' Nom_famille, ';
req += ' Priorite_affichage_accueil ';
req += ' FROM TB_Capteurs, ';
req += ' (SELECT Cle_primaire AS Cle_famille, Nom AS Nom_famille, Priorite_affichage AS Priorite_famille ';
req += ' FROM TB_Familles WHERE Type_source=' + shipheart_type_source_capteurs + ') AS TB_Familles ';
req += ' WHERE ID_Famille=Cle_famille ';
req += ' ORDER BY Priorite_famille, ABS(Priorite_affichage_accueil)';
//console.log("Requête = [ " + req + "; ]");
nb_capteurs = bdd.requete(req);
prec_famille = "";
// Boucle sur tous les capteurs :
for (i = 0; i < nb_capteurs; i ++)
{
res = bdd.lecture_ligne();
if (!res.isEmpty)
{
// Récup de toutes les données du capteur (pas forcément super efficace mais BEAUCOUP plus simple à maintenir...) :
j = 0;
var cle_primaire = Number(res[j++]);
var etat_systeme = Number(res[j++]);
var nom = res[j++];
var type_entrees = Number(res[j++]);
var entrees = res[j++];
var id_variable = Number(res[j++]);
var valeur = Number(res[j++]);
var precision_affichage = Number(res[j++]);
var liste_valeurs_textes = res[j++];
var nom_famille = res[j++];
var priorite_affichage_accueil = Number(res[j++]);
// Recherche des infos stockées dans la Variable pointée par ID_Variable :
sous_res = bdd.requete_unique('SELECT Unite, Conversion_unite FROM TB_Variables WHERE Cle_primaire="' + id_variable + '" ');
j = 0;
var symbole_unite = sous_res[j++];
var conversion_unite = Number(sous_res[j++]);
var nouvelle_unite;
var type_unite;
// Traitement des conversions d'unités :
switch(conversion_unite)
{
case shipheart_type_unite_volume:
nouvelle_unite = shipheart_unite_volume;
symbole_unite = shipheart_unite_volume_symbole;
type_unite = shipheart_type_unite_volume;
break;
case shipheart_type_unite_temperature:
nouvelle_unite = shipheart_unite_temperature;
symbole_unite = shipheart_unite_temperature_symbole;
type_unite = shipheart_type_unite_temperature;
break;
case shipheart_type_unite_latitude:
nouvelle_unite = 0;
symbole_unite = "";
type_unite = shipheart_type_unite_latitude;
break;
case shipheart_type_unite_longitude:
nouvelle_unite = 0;
symbole_unite = "";
type_unite = shipheart_type_unite_longitude;
break;
default:
nouvelle_unite = 0;
type_unite = 0;
symbole_unite = sous_res[0];
break;
}
// Cas spécifiques en fonction du type de capteur :
var chaine_valeur = Fonctions.conversion_unites( valeur, nouvelle_unite, "", precision_affichage, type_unite);
switch(type_entrees)
{
//-----------------------------------------------------------------
case shipheart_capteur_type_entrees_aux:
case shipheart_capteur_type_entrees_calculee_booleen:
// Traitement des listes :
var chaines_valeurs_textes = liste_valeurs_textes.split(';');
for (let chaine_valeur_texte of chaines_valeurs_textes)
{
var chaine = chaine_valeur_texte.split('/');
if (chaine.length === 2) // Evite de traiter les cas invalides (notamment, une chaine totalement vide qui arrive ici avec un length de 1...)
{
if (valeur === Number(chaine [0])) // Si l'une des valeurs de la liste correspond à la valeur actuelle :
{
chaine_valeur = chaine[1]; // La valeur affichée sera le texte correspondant.
break; // Et il est inutile de chercher d'autres valeurs...
}
}
}
break;
//-----------------------------------------------------------------
case shipheart_capteur_type_entrees_level_sensor:
break;
//-----------------------------------------------------------------
case shipheart_capteur_type_entrees_ow:
break;
//-----------------------------------------------------------------
case shipheart_capteur_type_entrees_predefinie:
break;
//-----------------------------------------------------------------
default :
break;
}
// Cas particulier d'un capteur absent :
if (etat_systeme !== 0) chaine_valeur = "---"
// Ajout de le ligne de famille :
if (prec_famille !== nom_famille)
{
modele_grille_principale.append({
"nom_famille": nom_famille,
"id": 0,
"etat_systeme": 0,
"nom": "",
"flag_masque": false,
"valeur": ""} );
prec_famille = nom_famille;
}
// Entrée normale :
modele_grille_principale.append({
"nom_famille": "",
"id": cle_primaire,
"etat_systeme": etat_systeme,
"nom": nom,
"flag_masque": (priorite_affichage_accueil < 1) ? true : false,
"valeur": chaine_valeur + " " + symbole_unite } );
}
else break; // Ne devrait pas arriver....
}
}
}
+1714
View File
File diff suppressed because it is too large Load Diff
+376
View File
@@ -0,0 +1,376 @@
//========================================================================================================================================================================
// CONFIG_CIRCUITS.QML
// Ce fichier traite la configuration des circuits. La page permet notamment de :
// - Afficher, pour tous les circuits déjà configurés (et donc d'accéder à la configuration détaillée de l'un d'eux ; ou sa création)
// - Accéder au menu de changement de l'ordre d'affichage sur la page d'accueil
// La page est appelée depuis la page PARAMETRES_AVANCES, en cliquant sur "Circuits"
//========================================================================================================================================================================
import QtQuick 2.12
import QtQuick.Controls 2.12
import qt.koriolan.shipheart 1.0
Shipheart_page {
id: page_config_circuits
// Mis en forme :
readonly property double affichage_hauteur_utile: shipheart_hauteur_page - shipheart_hauteur_bandeau_haut - shipheart_hauteur_bandeau_bas
readonly property int hauteur_bouton_droit: 80
readonly property double pourcentage_largeur_colonne_gauche: 0.75
readonly property int espace_colonnes_principales: 20
readonly property double pourcentage_bandeau_bas_largeur_zone_retour: 1.0
readonly property int nombre_circuits_affiches_sur_une_colonne: 8
readonly property int periode_init_raffraichissement_parametres: 1 // En ms
readonly property int periode_raffraichissement_parametres: 100 // En ms
// Affichage :
property int niveau_page: 0
property bool bouton_bandeau_bas_zone_retour_actif: false
// 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();
}
if (stackView.depth === (niveau_page + 1)) // Désactive le raffraichissement quand la page n'est pas activée dans la pile
{
if (!bouton_bandeau_bas_zone_retour_actif)
{
}
}
}
}
//===========================================================
// Lorsque la page est sur le point d'être affichée dans la pile :
//===========================================================
StackView.onActivating: {
mise_a_jour_donnees(); // Permet que lors du retour sur la page, l'état des différentes variables soit mise à jour avant l'affichage proprement dit.
}
// Titre de la page :
Shipheart_titre_1 {titre:qsTr("Configuration des circuits"); hauteur: shipheart_hauteur_bandeau_haut}
//-------------------------------------------------------
// Ecran de gauche (liste des circuits) :
//-------------------------------------------------------
Rectangle {
id: zone_affichage_gauche
x: 0
y: shipheart_hauteur_bandeau_haut;
width: (shipheart_largeur_page * pourcentage_largeur_colonne_gauche) - (espace_colonnes_principales / 2)
height: affichage_hauteur_utile
color: "transparent"
//-------------------------------------------------------------------
// Grille pour afficher les circuits :
GridView {
id: grille_principale
width: parent.width
height: parent.height
cellWidth: parent.width;
cellHeight: height / nombre_circuits_affiches_sur_une_colonne
snapMode: GridView.SnapToRow // L'arret de l'affichage se fera sur une rangée entière
clip: true // Nécessaire pour que les Delegates s'arretent à l'intérieur de la GridView
model: modele_grille_principale
delegate: Rectangle {
width: grille_principale.cellWidth
height: grille_principale.cellHeight
color: "transparent"
// Ajout d'une ligne de sous-titre avec le nom du famille de capteurs si c'est le premier capteur de la famille :
Shipheart_titre_3 {titre:model.nom_famille; hauteur: (affichage_hauteur_utile / nombre_circuits_affiches_sur_une_colonne); visible: (model.nom_famille !== "")}
Rectangle { // c'est le bouton proprement dit...
height: parent.height - 10
width:parent.width - 10
color: (model.adresse_module >= 0) ? shipheart_couleur_fond_bouton : couleur_rouge_transparent
radius: shipheart_rayon_bouton
border.color: couleur_rouge
border.width: ((model.etat_systeme === 0) && (model.adresse_module >= 0)) ? 0 : 3
anchors.horizontalCenter: parent.horizontalCenter
anchors.verticalCenter: parent.verticalCenter
visible: (model.nom_famille === "")
// Nom du circuit :
Text {
anchors.fill: parent
horizontalAlignment: Qt.AlignLeft
verticalAlignment: Qt.AlignVCenter
leftPadding: 20
text: model.nom
color: couleur_blanc_casse
font.pixelSize: 18
wrapMode: Label.Wrap
}
// Triangle bleu indiquant un circuit masqué :
Image {
id: image_circuit_masque // Affichage du coin inférieur-droit en couleur si le circuit est masqué
source: "/images/coin_bas_droite_bleu_koriolan.png"
sourceSize.height: 20
anchors {
bottom: parent.bottom
right: parent.right
}
visible: model.flag_masque ? true : false
}
MouseArea{
anchors.fill : parent
enabled : !grille_principale.moving // Permet de bloquer le Mousearea pendant le déplacement de la grille
onReleased: { // Permet déclencher à la fin d'un clic (court) sans mouvement
shipheart_clic();
stackView.push("config_circuit.qml", {"id_circuit": model.id})
}
}
}
}
ListModel {
id: modele_grille_principale
}
Label {
anchors.fill: parent
horizontalAlignment: Qt.AlignHCenter
verticalAlignment: Qt.AlignVCenter
visible: parent.count === 0
text: qsTr("Aucun circuit n'est encore configuré...")
color: couleur_rouge_transparent
font.pixelSize: 15
font.bold: true
wrapMode: Label.Wrap
}
}
}
//-------------------------------------------------------
// Ecran Droite :
//-------------------------------------------------------
Rectangle {
id: zone_affichage_droite
x: zone_affichage_gauche.width + espace_colonnes_principales
y: shipheart_hauteur_bandeau_haut + 5; // Le +5 permet d'aligner le haut du premier bouton de droite avec le haut du premier bouton de la liste de gauche (
width: (shipheart_largeur_page * (1 - pourcentage_largeur_colonne_gauche)) - (espace_colonnes_principales / 2)
height: affichage_hauteur_utile
color: "transparent"
Column {
anchors.fill: parent
spacing: 10
//----------------------
// Bouton + :
Rectangle {
width: parent.width
height: hauteur_bouton_droit
color: shipheart_couleur_fond_bouton
radius: shipheart_rayon_bouton
visible: !clavier.active
Text {
anchors {
verticalCenter: parent.verticalCenter
horizontalCenter: parent.horizontalCenter
}
font.pixelSize: 40
text: qsTr("+")
color: couleur_blanc_casse
}
MouseArea {
anchors.fill: parent
onClicked: {
shipheart_clic();
stackView.push("config_circuit.qml");
}
}
}
//----------------------
// Bouton tri :
Rectangle {
width: parent.width
height: hauteur_bouton_droit
color: shipheart_couleur_fond_bouton
radius: shipheart_rayon_bouton
visible: !clavier.active // && (modele_grille_principale.count > 1) // SDIR #258
Image {
id: image_parametres
source: "/images/tri.png"
sourceSize.height: 25
anchors {
verticalCenter: parent.verticalCenter
horizontalCenter: parent.horizontalCenter
}
}
MouseArea {
anchors.fill: parent
onClicked: {
shipheart_clic();
stackView.push("config_ordre_elements.qml", {"table": "TB_Circuits"})
}
}
}
}
}
//-------------------------------------------------------
// Bandeau bas :
//-------------------------------------------------------
Row {
x: 0
y: shipheart_hauteur_page - shipheart_hauteur_bandeau_bas;
width: shipheart_largeur_page;
height: shipheart_hauteur_bandeau_bas;
padding: 5
spacing: 5
//.................................................
// Bouton retour
//.................................................
Rectangle {
id: bandeau_bas_zone_retour
width: (parent.width * pourcentage_bandeau_bas_largeur_zone_retour) - (2 * parent.padding)
height: shipheart_hauteur_bandeau_bas - parent.topPadding - parent.bottomPadding;
radius: shipheart_rayon_bouton
color: shipheart_couleur_fond_bouton
Image {
source: "/images/icone_retour.png"
sourceSize.height: 30
anchors {
verticalCenter: parent.verticalCenter
horizontalCenter: parent.horizontalCenter
}
}
MouseArea { // Zone d'interaction. On est obligé de la mettre dans la grille pour que Rectangle et MouseArea soient frères ou parent/enfant ET qu'on n'occupe qu'un espace dans la grille.
id: mouse_area_bandeau_bas_zone_retour
anchors.fill: parent
onClicked: {
shipheart_clic();
stackView.pop()
}
}
}
}
// 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()
{
niveau_page = stackView.depth - 1; // Permet de stocker le niveau de la page dans la Stackview
mise_a_jour_donnees();
}
//=============================================================================================================
// Mise à jour des données
//=============================================================================================================
function mise_a_jour_donnees()
{
var req, res;
var sous_req, sous_res;
var i, j;
var nb_circuits;
var prec_famille;
// Grille principale (liste des capteurs configurés) :
modele_grille_principale.clear();
// Recherche des valeurs en BDD :
req = ' SELECT Cle_primaire, ';
req += ' Adresse_module, '
req += ' Etat_systeme, '
req += ' Nom, ';
req += ' Nom_famille, ';
req += ' Priorite_affichage_accueil ';
req += ' FROM TB_Circuits, ';
req += ' (SELECT Cle_primaire AS Cle_famille, Nom AS Nom_famille, Priorite_affichage AS Priorite_famille ';
req += ' FROM TB_Familles WHERE Type_source=' + shipheart_type_source_circuits + ') AS TB_Familles ';
req += ' WHERE ID_Famille=Cle_famille ';
req += ' AND ID_Circuit_esclave>=0 '; // On n'affiche tout simplement pas les circuits esclaves...
req += ' ORDER BY Priorite_famille, ABS(Priorite_affichage_accueil)';
//console.log("Requête = [ " + req + "; ]");
nb_circuits = bdd.requete(req);
prec_famille = "";
// Boucle sur tous les capteurs :
for (i = 0; i < nb_circuits; i ++)
{
res = bdd.lecture_ligne();
if (!res.isEmpty)
{
// Récup de toutes les données du capteur (pas forcément super efficace mais BEAUCOUP plus simple à maintenir...) :
j = 0;
var cle_primaire = Number(res[j++]);
var adresse_module = Number(res[j++]);
var etat_systeme = Number(res[j++]);
var nom = res[j++];
var nom_famille = res[j++];
var priorite_affichage_accueil = Number(res[j++]);
// Ajout de le ligne de famille :
if (prec_famille !== nom_famille)
{
modele_grille_principale.append({
"nom_famille": nom_famille,
"id": 0,
"adresse_module": 0,
"etat_systeme": 0,
"nom": "",
"flag_masque": false} );
prec_famille = nom_famille;
}
// Entrée normale :
modele_grille_principale.append({
"nom_famille": "",
"id": cle_primaire,
"adresse_module": adresse_module,
"etat_systeme": etat_systeme,
"nom": nom,
"flag_masque": (priorite_affichage_accueil < 1) ? true : false } );
}
else break; // Ne devrait pas arriver....
}
}
}
+2164
View File
File diff suppressed because it is too large Load Diff
+428
View File
@@ -0,0 +1,428 @@
//========================================================================================================================================================================
// CONFIG_EVENTS.QML
// Ce fichier traite la configuration des évènements. La page permet notamment de :
// - Afficher tous les évènements déjà configurés (et donc d'accéder à la configuration détaillée de l'un d'eux ; ou sa création)
// La page est appelée depuis la page PARAMETRES_AVANCES, en cliquant sur "Evènements"
//========================================================================================================================================================================
import QtQuick 2.12
import QtQuick.Controls 2.12
import qt.koriolan.shipheart 1.0
Shipheart_page {
id: page_config_evenements
// Mis en forme :
readonly property double affichage_hauteur_utile: shipheart_hauteur_page - shipheart_hauteur_bandeau_haut - shipheart_hauteur_bandeau_bas
readonly property int hauteur_bouton_droit: 80
readonly property double pourcentage_largeur_colonne_gauche: 0.75
readonly property double pourcentage_largeur_bouton_auto_dans_colonne_gauche: 0.15
readonly property int espace_colonnes_principales: 20
readonly property double pourcentage_bandeau_bas_largeur_zone_retour: 1.0
readonly property int nombre_capteurs_affiches_sur_une_colonne: 8
readonly property int periode_init_raffraichissement_parametres: 1 // En ms
readonly property int periode_raffraichissement_parametres: 1000 // En ms /!\ Laisser à 1s afin que le compteur secondes fonctionne correctement...
// Données :
property int nombre_events: 0
// Modèles :
// Note : les modèles chargés dans 'main.qml' n'ont pas à être rechargés dans les pages suivantes
//===========================================================
// Timer periode raffraichissement parametres :
//===========================================================
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();
}
}
}
//===========================================================
// Lorsque la page est sur le point d'être affichée dans la pile :
//===========================================================
StackView.onActivating: {
mise_a_jour_donnees(); // Permet que lors du retour sur la page, l'état des différentes variables soit mise à jour avant l'affichage proprement dit.
}
// Titre de la page :
Shipheart_titre_1 {titre:qsTr("Configuration des évènements"); hauteur: shipheart_hauteur_bandeau_haut}
//-------------------------------------------------------
// Ecran de gauche (liste des capteurs) :
//-------------------------------------------------------
Rectangle {
id: zone_affichage_gauche
x: 0
y: shipheart_hauteur_bandeau_haut;
width: (shipheart_largeur_page * pourcentage_largeur_colonne_gauche) - (espace_colonnes_principales / 2)
height: affichage_hauteur_utile
color: "transparent"
//-------------------------------------------------------------------
// Grille pour afficher les évènements :
GridView {
id: grille_principale
width: parent.width
height: parent.height
cellWidth: parent.width;
cellHeight: height / nombre_capteurs_affiches_sur_une_colonne
snapMode: GridView.SnapToRow // L'arret de l'affichage se fera sur une rangée entière
clip: true // Nécessaire pour que les Delegates s'arretent à l'intérieur de la GridView
highlightRangeMode: ListView.StrictlyEnforceRange // Permet de faire défiler la liste jusqu'à ne garder que le dernier élément affiché (et comme la liste des modules physiques est obligée d'utiliser ce paramètre, on fait pareil sur cette liste pour que les deux soient synchro)
model: modele_grille_principale
delegate: Rectangle {
width: grille_principale.cellWidth
height: grille_principale.cellHeight
color: "transparent"
// Ajout d'une ligne de sous-titre avec le nom du type d'event si c'est le premier event du type :
Shipheart_titre_3 {titre:model.nom_famille; hauteur: (affichage_hauteur_utile / nombre_capteurs_affiches_sur_une_colonne); visible: model.famille}
//------------------------------------
// Bouton avec le nom de l'event :
//------------------------------------
Rectangle {
height: parent.height - 10
width: (parent.width * (1 - pourcentage_largeur_bouton_auto_dans_colonne_gauche)) - 10
color: shipheart_couleur_fond_bouton
radius: shipheart_rayon_bouton
anchors {
left: parent.left
verticalCenter: parent.verticalCenter
}
visible: !model.famille
Text {
anchors.fill: parent
horizontalAlignment: Qt.AlignLeft
verticalAlignment: Qt.AlignVCenter
leftPadding: 20
text: model.nom
color: couleur_blanc_casse
font.pixelSize: 18
wrapMode: Label.Wrap
}
MouseArea{
anchors.fill : parent
onClicked: {
shipheart_clic();
stackView.push("config_event.qml", {"id_event": model.id})
}
}
}
//------------------------------------
// Bouton AUTO :
//------------------------------------
Rectangle {
x: (parent.width * (1 - pourcentage_largeur_bouton_auto_dans_colonne_gauche))
height: parent.height - 10
width:(parent.width * pourcentage_largeur_bouton_auto_dans_colonne_gauche)
anchors {
right: parent.right
verticalCenter: parent.verticalCenter
}
radius: shipheart_rayon_bouton
color: shipheart_couleur_fond_bouton
visible: !model.famille
Image {
source: (model.validite === 0) ? "/images/faux.png" : "/images/vrai.png"
sourceSize.height: parent.height * 0.5
anchors {
verticalCenter: parent.verticalCenter
horizontalCenter: parent.horizontalCenter
}
}
MouseArea {
id: mouse_area_cdes_auto
x: 0
y: 0
width: parent.width
height: parent.height
pressAndHoldInterval: shipheart_mouse_press_and_hold_interval_long
onPressed: {
}
onReleased: {
}
onPressAndHold: {
shipheart_clic();
toggle_validite(model.index);
}
}
}
}
ListModel {
id: modele_grille_principale
}
Label {
anchors.fill: parent
horizontalAlignment: Qt.AlignHCenter
verticalAlignment: Qt.AlignVCenter
visible: parent.count === 0
text: qsTr("Aucun évènement n'est encore configuré...")
color: couleur_rouge_transparent
font.pixelSize: 15
font.bold: true
wrapMode: Label.Wrap
}
}
}
//-------------------------------------------------------
// Ecran Droite :
//-------------------------------------------------------
Rectangle {
id: zone_affichage_droite
x: zone_affichage_gauche.width + espace_colonnes_principales
y: shipheart_hauteur_bandeau_haut + 5; // Le +5 permet d'aligner le haut du premier bouton de droite avec le haut du premier bouton de la liste de gauche (
width: (shipheart_largeur_page * (1 - pourcentage_largeur_colonne_gauche)) - (espace_colonnes_principales / 2)
height: affichage_hauteur_utile
color: "transparent"
Column {
anchors.fill: parent
spacing: 10
//----------------------
// Bouton + :
Rectangle {
width: parent.width
height: hauteur_bouton_droit
color: shipheart_couleur_fond_bouton
radius: shipheart_rayon_bouton
visible: !clavier.active
Text {
anchors {
verticalCenter: parent.verticalCenter
horizontalCenter: parent.horizontalCenter
}
font.pixelSize: 40
text: "+"
color: couleur_blanc_casse
}
MouseArea {
anchors.fill: parent
onClicked: {
shipheart_clic();
if (nombre_events < shipheart_max_nombre_events) stackView.push("config_event.qml");
else
{
boite_information.niveau = 1;
boite_information.imageTitreSource = "/images/ampoule.png"
boite_information.message = qsTr("Le nombre total d'évènements est limité à %1.<br /><br />Vous ne pouvez pas ajouter de nouvel évènement avant d'en avoir supprimé préalablement.").arg(shipheart_max_nombre_events);
boite_information.open();
}
}
}
}
}
}
//-------------------------------------------------------
// Bandeau bas :
//-------------------------------------------------------
Row {
x: 0
y: shipheart_hauteur_page - shipheart_hauteur_bandeau_bas;
width: shipheart_largeur_page;
height: shipheart_hauteur_bandeau_bas;
padding: 5
spacing: 5
//.................................................
// Bouton retour
//.................................................
Rectangle {
id: bandeau_bas_zone_retour
width: (parent.width * pourcentage_bandeau_bas_largeur_zone_retour) - parent.leftPadding - (parent.spacing / 2)
height: shipheart_hauteur_bandeau_bas - parent.topPadding - parent.bottomPadding;
radius: shipheart_rayon_bouton
color: shipheart_couleur_fond_bouton
Image {
source: "/images/icone_retour.png"
sourceSize.height: 30
anchors {
verticalCenter: parent.verticalCenter
horizontalCenter: parent.horizontalCenter
}
}
MouseArea { // Zone d'interaction. On est obligé de la mettre dans la grille pour que Rectangle et MouseArea soient frères ou parent/enfant ET qu'on n'occupe qu'un espace dans la grille.
id: mouse_area_bandeau_bas_zone_retour
anchors.fill: parent
onClicked: {
shipheart_clic();
stackView.pop()
}
}
}
}
//-------------------------------------------------------
// Boite de validation :
//-------------------------------------------------------
Shipheart_boite_dialogue {
id: boite_information
}
// 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()
{
mise_a_jour_donnees();
}
//=============================================================================================================
// Mise à jour des données
//=============================================================================================================
function mise_a_jour_donnees()
{
var req, res;
var sous_req, sous_res;
var i;
var nb_events;
//=======================================================================
// Grille principale (liste des évènements configurés) :
modele_grille_principale.clear(); // RAZ des valeurs existantes
nombre_events = 0;
req = "SELECT TB_Events.Cle_primaire AS cle_event, TB_Events.Nom AS nom_event, Type_source, TB_Events.Validite";
req += " FROM TB_Events";
req += " WHERE Type_source=0";
req += " UNION";
req += " SELECT TB_Events.Cle_primaire AS cle_event, CONCAT(tb_source.Nom, ' - ', TB_Events.Nom) AS nom_event, Type_source, TB_Events.Validite";
req += " FROM TB_Events";
req += " INNER JOIN TB_Circuits AS tb_source ON TB_Events.ID_Source=tb_source.Cle_primaire";
req += " WHERE Type_source=1";
req += " UNION";
req += " SELECT TB_Events.Cle_primaire AS cle_event, CONCAT(tb_source.Nom, ' - ', TB_Events.Nom) AS nom_event, Type_source, TB_Events.Validite";
req += " FROM TB_Events";
req += " INNER JOIN TB_Capteurs AS tb_source ON TB_Events.ID_Source=tb_source.Cle_primaire";
req += " WHERE Type_source=2";
req += " UNION";
req += " SELECT TB_Events.Cle_primaire AS cle_event, CONCAT(tb_source.Nom, ' - ', TB_Events.Nom) AS nom_event, Type_source, TB_Events.Validite";
req += " FROM TB_Events";
req += " INNER JOIN TB_Reservoirs AS tb_source ON TB_Events.ID_Source=tb_source.Cle_primaire";
req += " WHERE Type_source=3";
req += " UNION";
req += " SELECT TB_Events.Cle_primaire AS cle_event, CONCAT(tb_source.Nom, ' - ', TB_Events.Nom) AS nom_event, Type_source, TB_Events.Validite";
req += " FROM TB_Events";
req += " INNER JOIN TB_Batteries AS tb_source ON TB_Events.ID_Source=tb_source.Cle_primaire";
req += " WHERE Type_source=4 ORDER BY Type_source, nom_event ";
//console.log("req = [ " + req + "; ]");
nb_events = bdd.requete(req);
var prec_type = -1;
for (i = 0; i < nb_events; i ++)
{
res = bdd.lecture_ligne();
if (!res.isEmpty)
{
// Ajout de le ligne de Type :
if (prec_type !== Number(res[2]))
{
modele_grille_principale.append({"famille":true, "nom": "", "id": 0, "nom_famille": nom_type_source(Number(res[2])), "validite": 0 });
prec_type = Number(res[2]);
}
// Entrée normale :
modele_grille_principale.append({"famille":false, "nom": res[1], "id": Number(res[0]), "nom_famille": nom_type_source(Number(res[2])), "validite": Number(res[3]) });
nombre_events ++;
}
else break; // Ne devrait pas arriver....
}
}
//=============================================================================================================
// Nom type de source
//=============================================================================================================
function nom_type_source(source)
{
switch(source)
{
case shipheart_source_mesure_circuit:
return(qsTr("Sur circuits"));
case shipheart_source_mesure_capteur:
return(qsTr("Sur capteurs"));
case shipheart_source_mesure_reservoir:
return(qsTr("Sur réservoirs"));
case shipheart_source_mesure_batterie:
return(qsTr("Sur batteries"));
default:
return(qsTr("Horaire seulement"));
}
}
//=============================================================================================================
// Bascule de la visibilité d'un évènement
//=============================================================================================================
function toggle_validite(index)
{
var req;
var validite;
// Recherche de la valeur actuelle et calcul de la nouvelle :
validite = modele_grille_principale.get(index).validite;
if (validite === 0) validite = 1;
else validite = 0;
// Mise à jour de la nouvelle valeur dans le modèle :
modele_grille_principale.set(index, {"validite": validite});
// Mis eà jour de la nouvelle valeur dans la BDD :
req = 'UPDATE TB_Events SET Validite=' + validite + ' WHERE Cle_primaire=' + modele_grille_principale.get(index).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);
}
}
+2148
View File
File diff suppressed because it is too large Load Diff
+406
View File
@@ -0,0 +1,406 @@
//========================================================================================================================================================================
// CONFIG_GROUPE_ELEMENTS.QML
// Ce fichier traite la configuration des groupes d'éléments (circuits notamment)
// La page est appelée depuis la page config_ordre_elements.qml, en cliquant sur "+" ou par pression longue sur un groupe existant
//========================================================================================================================================================================
import QtQuick 2.12
import QtQml.Models
import qt.koriolan.shipheart 1.0
Shipheart_page {
id: page_config_groupe_elements
// Mis en forme :
readonly property double pourcentage_largeur_colonne_gauche: 0.75
readonly property double espace_colonnes_principales: 20
readonly property double pourcentage_hauteur_ligne_haut: 1
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 hauteur_ligne_formulaire: 60
readonly property double pourcentage_largeur_labels_formulaire: 0.4
readonly property int hauteur_bouton_droit: 80
readonly property double pourcentage_bandeau_bas_largeur_zone_retour: 1
readonly property int periode_init_raffraichissement_parametres: 1 // En ms
readonly property int periode_raffraichissement_parametres: 1000 // En ms
// Affichage :
property bool bouton_bandeau_bas_zone_retour_actif: false
property int protection_mode: 0
// Données :
property int type_source: shipheart_type_source_inconnu
property int id_groupe: 0
// Modèles :
// Note : les modèles chargés dans 'main.qml' n'ont pas à être rechargés dans les pages suivantes
//===========================================================
// Timer periode raffraichissement parametres :
//===========================================================
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 if (!bouton_bandeau_bas_zone_retour_actif) {
}
}
}
// Titre de la page :
Shipheart_titre_1 {titre:(id_groupe === 0) ? qsTr("Création d'un groupe") : qsTr("Modification d'un groupe"); 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;
color: "transparent"
ListView {
id: grille_principale
width: parent.width
height: parent.height
snapMode: GridView.SnapToRow // L'arret de l'affichage se fera sur une rangée entière
clip: true // Nécessaire pour que les Delegates s'arretent à l'intérieur de la GridView
model: modele_grille_principale
footer: Rectangle { // Permet de dégager la place suffisante en fin de GridView pour accéder même au dernier élément lorsque que clavier est déplié
height: shipheart_hauteur_clavier
color: "transparent"
}
ObjectModel {
id: modele_grille_principale
//-------------------------------------------------------------------------------
// Ligne de formulaire : Nom du groupe
Rectangle {
width: (shipheart_largeur_page * pourcentage_largeur_colonne_gauche) - (espace_colonnes_principales / 2)
height: visible ? hauteur_ligne_formulaire : 0
color: "transparent"
visible: true
Shipheart_formulaire_label {
text: qsTr("Nom du groupe")
width: parent.width * pourcentage_largeur_labels_formulaire
}
Shipheart_formulaire_textfield {
id: champ_formulaire_Nom
width: parent.width * (1 - pourcentage_largeur_labels_formulaire)
x: parent.width * pourcentage_largeur_labels_formulaire
}
Shipheart_formulaire_ligne_separation {
haut_bas_hautbas: shipheart_formulaire_ligne_separation_basse
}
}
}
}
}
//-------------------------------------------------------
// 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 * pourcentage_hauteur_ligne_haut) - (espace_lignes_principales / 2);
color: "transparent"
Column {
anchors.fill: parent
spacing: 10
// Bouton Enregistrer :
Rectangle {
width: parent.width
height: hauteur_bouton_droit
color: shipheart_couleur_fond_bouton
radius: shipheart_rayon_bouton
visible:
// (!clavier.active) && // Si le clavier est replié... // SDIR #255
(champ_formulaire_Nom.text !== "") // Si le nom n'est pas vide...
Text {
anchors {
verticalCenter: parent.verticalCenter
horizontalCenter: parent.horizontalCenter
}
font.pixelSize: 20
text: (id_groupe === 0) ? qsTr("Créer le groupe") : qsTr("Enregistrer")
color: couleur_blanc_casse
}
MouseArea {
anchors.fill: parent
onClicked: {
shipheart_clic();
mise_a_jour(); // Mise à jour des données saisies dans la BDD
stackView.pop(); // Retour
}
}
}
// Bouton Supprimer :
Rectangle {
width: parent.width
height: hauteur_bouton_droit
color: shipheart_couleur_fond_bouton
radius: shipheart_rayon_bouton
visible: ((clavier.active) || // Si le clavier est déplié...
(id_groupe === 0)) // Si le groupe n'existe pas encore...
? false // ...on ne propose pas le bouton
: true
Text {
anchors {
verticalCenter: parent.verticalCenter
horizontalCenter: parent.horizontalCenter
}
font.pixelSize: 20
text: qsTr("Supprimer")
color: couleur_blanc_casse
}
MouseArea {
anchors.fill: parent
onClicked: {
shipheart_clic();
if (suppression() === 0) stackView.pop(); // Retour
}
}
}
}
}
//-------------------------------------------------------
// 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 * pourcentage_bandeau_bas_largeur_zone_retour) - (2 * parent.padding)
height: shipheart_hauteur_bandeau_bas - parent.topPadding - parent.bottomPadding;
radius: shipheart_rayon_bouton
color: shipheart_couleur_fond_bouton
Image {
source: "/images/icone_retour.png"
sourceSize.height: 30
anchors {
verticalCenter: parent.verticalCenter
horizontalCenter: parent.horizontalCenter
}
}
MouseArea { // Zone d'interaction. On est obligé de la mettre dans la grille pour que Rectangle et MouseArea soient frères ou parent/enfant ET qu'on n'occupe qu'un espace dans la grille.
id: mouse_area_bandeau_bas_zone_retour
anchors.fill: parent
onClicked: {
shipheart_clic();
stackView.pop()
}
}
}
}
//-------------------------------------------------------
// Boite d'info :
//-------------------------------------------------------
Shipheart_boite_dialogue {
id: boite_information
}
// 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 nb;
//---------------------------------------------------------------------------------------
// Nom du groupe :
if (id_groupe > 0)
{
req = 'SELECT Nom FROM TB_Familles WHERE Cle_primaire=' + id_groupe + ' ';
//console.log("Requête = " + req)
res = bdd.requete_unique(req);
champ_formulaire_Nom.text = res[0];
}
else champ_formulaire_Nom.text = "";
}
//=============================================================================================================
// Mise à jour des valeurs saisies
//=============================================================================================================
function mise_a_jour()
{
var req;
if (id_groupe === 0) // Création :
{
// Insertion d'une nouvelle entrée dans la BDD :
req = 'INSERT INTO TB_Familles (Nom, Priorite_affichage, Type_source) VALUES ("' + champ_formulaire_Nom.text + '", 99, ' + type_source + ')';
//console.log("Requête = " + req)
bdd.requete_unique(req);
}
else // Modification
{
// Mise à jour des nouvelles valeurs dans la BDD :
req = 'UPDATE TB_Familles SET Nom="' + champ_formulaire_Nom.text + '" WHERE Cle_primaire=' + id_groupe + ' ';
//console.log("Requête = " + req)
bdd.requete_unique(req);
}
}
//=============================================================================================================
// Suppression d'un groupe
//=============================================================================================================
function suppression()
{
var req, res;
var nb, nb_masques;
var retour = 0;
var table_elements;
var nom_elements;
// Vérification que le groupe est vide :
switch(type_source)
{
//----------------------------------------------------
case shipheart_type_source_batteries:
table_elements = "TB_Batteries";
nom_elements = qsTr("batteries");
break;
//----------------------------------------------------
case shipheart_type_source_capteurs:
table_elements = "TB_Capteurs";
nom_elements = qsTr("capteurs");
break;
//----------------------------------------------------
case shipheart_type_source_circuits:
table_elements = "TB_Circuits";
nom_elements = qsTr("circuits");
break;
//----------------------------------------------------
case shipheart_type_source_reservoirs:
table_elements = "TB_Reservoirs";
nom_elements = qsTr("réservoirs");
break;
//----------------------------------------------------
default:
table_elements = "";
break;
}
if (table_elements !== "")
{
nb = 0;
req = 'SELECT COUNT(*) AS Nb FROM ' + table_elements + ' WHERE ID_Famille=' + id_groupe + ' ';
//console.log("Requête = " + req)
res = bdd.requete_unique(req);
nb = Number(res[0]);
if (nb === 0) // La table ne contient aucun élément lié à cette famille.
{
// Vérification que ce groupe n'est pas le dernier (on n'accepte pas de n'avoir AUCUN groupe) :
req = 'SELECT COUNT(*) AS Nb FROM TB_Familles WHERE Type_source=' + type_source + ' ';
//console.log("Requête = " + req)
res = bdd.requete_unique(req);
if (Number(res[0]) > 1) // Il reste d'autres groupes valides. On peut donc supprimer celle-ci :
{
req = 'DELETE FROM TB_Familles WHERE Cle_primaire=' + id_groupe + ' ';
//console.log("Requête = " + req)
bdd.requete_unique(req);
}
else // C'est le dernier groupe valide, on interdit donc sa suppression.
{
// Définition des paramètres de la boite de dialogue :
boite_information.niveau = 1;
boite_information.imageTitreSource = "/images/attention.png"
boite_information.message = qsTr("Ce groupe est le dernier encore valide.<br /><br />Vous ne pourrez le supprimer qu'après en avoir préalablement créé au moins un autre (il n'est pas possible de fonctionner sans aucun groupe de %1).").arg(nom_elements)
boite_information.open();
retour = -1;
}
}
else // La table contient encore des éléments liés à cette famille
{
// Recherche des éléments masqués :
req = 'SELECT COUNT(*) AS Nb FROM ' + table_elements + ' WHERE ID_Famille=' + id_groupe + ' AND Priorite_affichage_accueil<=0 ';
//console.log("Requête = " + req)
res = bdd.requete_unique(req);
nb_masques = Number(res[0]);
// Définition des paramètres de la boite de dialogue :
boite_information.niveau = 1;
boite_information.imageTitreSource = "/images/attention.png"
if (nb_masques === 0) boite_information.message = qsTr("Ce groupe comprend encore %1 %2.<br /><br />Il est nécessaire d'affecter ces %2 à un autre groupe avant de pouvoir le supprimer.").arg(nb).arg(nom_elements).arg(nb_masques);
else boite_information.message = qsTr("Ce groupe comprend encore %1 %2,<br />dont %3 configuré(s) comme \"masqué(s)\".<br /><br />Il est nécessaire d'affecter ces %2 à un autre groupe avant de pouvoir le supprimer.").arg(nb).arg(nom_elements).arg(nb_masques);
boite_information.open();
retour = -1;
}
}
return (retour);
}
}
+520
View File
@@ -0,0 +1,520 @@
//========================================================================================================================================================================
// CONFIG_INSTALLATION.QML
// Ce fichier traite la configuration des paramètres d'installation.
// La page est appelée depuis la page parametres_avances.qml, en cliquant sur "Système" > "Installation"
//========================================================================================================================================================================
import QtQuick 2.12
import QtQml.Models
import qt.koriolan.shipheart 1.0
Shipheart_page {
id: page_config_installation
// Mis en forme :
readonly property double pourcentage_largeur_colonne_gauche: 0.75
readonly property double espace_colonnes_principales: 20
readonly property double pourcentage_hauteur_ligne_haut: 1
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 hauteur_ligne_formulaire: 60
readonly property double pourcentage_largeur_labels_formulaire: 0.4
readonly property int hauteur_bouton_droit: 80
readonly property double pourcentage_bandeau_bas_largeur_zone_retour: 1
readonly property int periode_init_raffraichissement_parametres: 1 // En ms
readonly property int periode_raffraichissement_parametres: 1000 // En ms
// Affichage :
property bool bouton_bandeau_bas_zone_retour_actif: false
property int protection_mode: 0
// Données :
// 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 if (!bouton_bandeau_bas_zone_retour_actif) {
}
}
}
// Titre de la page :
Shipheart_titre_1 {titre:qsTr("Coordonnées de l'installateur"); 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);
color: "transparent"
ListView {
id: grille_principale
width: parent.width
height: parent.height
snapMode: GridView.SnapToRow // L'arret de l'affichage se fera sur une rangée entière
clip: true // Nécessaire pour que les Delegates s'arretent à l'intérieur de la GridView
model: modele_grille_principale
footer: Rectangle { // Permet de dégager la place suffisante en fin de GridView pour accéder même au dernier élément lorsque que clavier est déplié
height: shipheart_hauteur_clavier
color: "transparent"
}
ObjectModel {
id: modele_grille_principale
//-------------------------------------------------------------------------------
// Ligne de formulaire : Raison sociale
Rectangle {
width: (shipheart_largeur_page * pourcentage_largeur_colonne_gauche) - (espace_colonnes_principales / 2)
height: visible ? hauteur_ligne_formulaire : 0
color: "transparent"
Shipheart_formulaire_label {
text: qsTr("Raison sociale")
width: parent.width * pourcentage_largeur_labels_formulaire
}
Shipheart_formulaire_textfield {
id: champ_formulaire_Raison_sociale
width: parent.width * (1 - pourcentage_largeur_labels_formulaire)
x: parent.width * pourcentage_largeur_labels_formulaire
}
Shipheart_formulaire_ligne_separation {
haut_bas_hautbas: shipheart_formulaire_ligne_separation_basse
}
}
//-------------------------------------------------------------------------------
// Ligne de formulaire : Rue_1
Rectangle {
width: (shipheart_largeur_page * pourcentage_largeur_colonne_gauche) - (espace_colonnes_principales / 2)
height: visible ? hauteur_ligne_formulaire : 0
color: "transparent"
Shipheart_formulaire_label {
text: qsTr("N° & Rue")
width: parent.width * pourcentage_largeur_labels_formulaire
}
Shipheart_formulaire_textfield {
id: champ_formulaire_Rue_1
width: parent.width * (1 - pourcentage_largeur_labels_formulaire)
x: parent.width * pourcentage_largeur_labels_formulaire
}
Shipheart_formulaire_ligne_separation {
haut_bas_hautbas: shipheart_formulaire_ligne_separation_basse
}
}
//-------------------------------------------------------------------------------
// Ligne de formulaire : Rue_2
Rectangle {
width: (shipheart_largeur_page * pourcentage_largeur_colonne_gauche) - (espace_colonnes_principales / 2)
height: visible ? hauteur_ligne_formulaire : 0
color: "transparent"
Shipheart_formulaire_label {
text: qsTr("Complément d'adresse")
width: parent.width * pourcentage_largeur_labels_formulaire
}
Shipheart_formulaire_textfield {
id: champ_formulaire_Rue_2
width: parent.width * (1 - pourcentage_largeur_labels_formulaire)
x: parent.width * pourcentage_largeur_labels_formulaire
}
Shipheart_formulaire_ligne_separation {
haut_bas_hautbas: shipheart_formulaire_ligne_separation_basse
}
}
//-------------------------------------------------------------------------------
// Ligne de formulaire : Code_postal
Rectangle {
width: (shipheart_largeur_page * pourcentage_largeur_colonne_gauche) - (espace_colonnes_principales / 2)
height: visible ? hauteur_ligne_formulaire : 0
color: "transparent"
Shipheart_formulaire_label {
text: qsTr("Code postal")
width: parent.width * pourcentage_largeur_labels_formulaire
}
Shipheart_formulaire_textfield {
id: champ_formulaire_Code_postal
width: parent.width * (1 - pourcentage_largeur_labels_formulaire)
x: parent.width * pourcentage_largeur_labels_formulaire
}
Shipheart_formulaire_ligne_separation {
haut_bas_hautbas: shipheart_formulaire_ligne_separation_basse
}
}
//-------------------------------------------------------------------------------
// Ligne de formulaire : Ville
Rectangle {
width: (shipheart_largeur_page * pourcentage_largeur_colonne_gauche) - (espace_colonnes_principales / 2)
height: visible ? hauteur_ligne_formulaire : 0
color: "transparent"
Shipheart_formulaire_label {
text: qsTr("Ville")
width: parent.width * pourcentage_largeur_labels_formulaire
}
Shipheart_formulaire_textfield {
id: champ_formulaire_Ville
width: parent.width * (1 - pourcentage_largeur_labels_formulaire)
x: parent.width * pourcentage_largeur_labels_formulaire
}
Shipheart_formulaire_ligne_separation {
haut_bas_hautbas: shipheart_formulaire_ligne_separation_basse
}
}
//-------------------------------------------------------------------------------
// Ligne de formulaire : Nom_contact
Rectangle {
width: (shipheart_largeur_page * pourcentage_largeur_colonne_gauche) - (espace_colonnes_principales / 2)
height: visible ? hauteur_ligne_formulaire : 0
color: "transparent"
Shipheart_formulaire_label {
text: qsTr("Nom du contact")
width: parent.width * pourcentage_largeur_labels_formulaire
}
Shipheart_formulaire_textfield {
id: champ_formulaire_Nom_contact
width: parent.width * (1 - pourcentage_largeur_labels_formulaire)
x: parent.width * pourcentage_largeur_labels_formulaire
}
Shipheart_formulaire_ligne_separation {
haut_bas_hautbas: shipheart_formulaire_ligne_separation_basse
}
}
//-------------------------------------------------------------------------------
// Ligne de formulaire : Téléphone
Rectangle {
width: (shipheart_largeur_page * pourcentage_largeur_colonne_gauche) - (espace_colonnes_principales / 2)
height: visible ? hauteur_ligne_formulaire : 0
color: "transparent"
Shipheart_formulaire_label {
text: qsTr("Téléphone")
width: parent.width * pourcentage_largeur_labels_formulaire
}
Shipheart_formulaire_textfield {
id: champ_formulaire_Telephone
width: parent.width * (1 - pourcentage_largeur_labels_formulaire)
x: parent.width * pourcentage_largeur_labels_formulaire
inputMethodHints: Qt.ImhDialableCharactersOnly // N'affiche que les caractères destinés à un n° de téléphone
}
Shipheart_formulaire_ligne_separation {
haut_bas_hautbas: shipheart_formulaire_ligne_separation_basse
}
}
//-------------------------------------------------------------------------------
// Ligne de formulaire : Email
Rectangle {
width: (shipheart_largeur_page * pourcentage_largeur_colonne_gauche) - (espace_colonnes_principales / 2)
height: visible ? hauteur_ligne_formulaire : 0
color: "transparent"
Shipheart_formulaire_label {
text: qsTr("Email")
width: parent.width * pourcentage_largeur_labels_formulaire
}
Shipheart_formulaire_textfield {
id: champ_formulaire_Email
width: parent.width * (1 - pourcentage_largeur_labels_formulaire)
x: parent.width * pourcentage_largeur_labels_formulaire
inputMethodHints: Qt.ImhEmailCharactersOnly // N'affiche que les caractères destinés à un Email
}
Shipheart_formulaire_ligne_separation {
haut_bas_hautbas: shipheart_formulaire_ligne_separation_basse
}
}
//-------------------------------------------------------------------------------
// Ligne de formulaire : Site web
Rectangle {
width: (shipheart_largeur_page * pourcentage_largeur_colonne_gauche) - (espace_colonnes_principales / 2)
height: visible ? hauteur_ligne_formulaire : 0
color: "transparent"
Shipheart_formulaire_label {
text: qsTr("Site web")
width: parent.width * pourcentage_largeur_labels_formulaire
}
Shipheart_formulaire_textfield {
id: champ_formulaire_Site_web
width: parent.width * (1 - pourcentage_largeur_labels_formulaire)
x: parent.width * pourcentage_largeur_labels_formulaire
inputMethodHints: Qt.ImhUrlCharactersOnly // N'affiche que les caractères destinés à une URL
}
Shipheart_formulaire_ligne_separation {
haut_bas_hautbas: shipheart_formulaire_ligne_separation_basse
}
}
}
}
}
//-------------------------------------------------------
// 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 * pourcentage_hauteur_ligne_haut) - (espace_lignes_principales / 2);
color: "transparent"
Column {
anchors.fill: parent
spacing: 10
//---------------------------
// Bouton Enregistrer :
//---------------------------
Rectangle {
width: parent.width
height: hauteur_bouton_droit
color: shipheart_couleur_fond_bouton
radius: shipheart_rayon_bouton
// visible: !clavier.active // SDIR #255
Text {
anchors {
verticalCenter: parent.verticalCenter
horizontalCenter: parent.horizontalCenter
}
font.pixelSize: 20
text: qsTr("Enregistrer")
color: couleur_blanc_casse
}
MouseArea {
anchors.fill: parent
onClicked: {
shipheart_clic();
mise_a_jour(); // Mise à jour des données saisies dans la BDD et reconfiguration par l'application pour prise en compte.
stackView.pop();
}
}
}
}
}
//-------------------------------------------------------
// 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 * pourcentage_bandeau_bas_largeur_zone_retour) - (2 * parent.padding)
height: shipheart_hauteur_bandeau_bas - parent.topPadding - parent.bottomPadding;
radius: shipheart_rayon_bouton
color: shipheart_couleur_fond_bouton
Image {
source: "/images/icone_retour.png"
sourceSize.height: 30
anchors {
verticalCenter: parent.verticalCenter
horizontalCenter: parent.horizontalCenter
}
}
MouseArea { // Zone d'interaction. On est obligé de la mettre dans la grille pour que Rectangle et MouseArea soient frères ou parent/enfant ET qu'on n'occupe qu'un espace dans la grille.
id: mouse_area_bandeau_bas_zone_retour
anchors.fill: parent
onClicked: {
shipheart_clic();
stackView.pop()
}
}
}
}
// 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 nb;
req = 'SELECT Valeur FROM TB_Configuration WHERE Nom="Raison_sociale_installateur" ';
res = bdd.requete_unique(req);
champ_formulaire_Raison_sociale.text = res[0];
req = 'SELECT Valeur FROM TB_Configuration WHERE Nom="Rue_1_installateur" ';
res = bdd.requete_unique(req);
champ_formulaire_Rue_1.text = res[0];
req = 'SELECT Valeur FROM TB_Configuration WHERE Nom="Rue_2_installateur" ';
res = bdd.requete_unique(req);
champ_formulaire_Rue_2.text = res[0];
req = 'SELECT Valeur FROM TB_Configuration WHERE Nom="Code_postal_installateur" ';
res = bdd.requete_unique(req);
champ_formulaire_Code_postal.text = res[0];
req = 'SELECT Valeur FROM TB_Configuration WHERE Nom="Ville_installateur" ';
res = bdd.requete_unique(req);
champ_formulaire_Ville.text = res[0];
req = 'SELECT Valeur FROM TB_Configuration WHERE Nom="Nom_contact_installateur" ';
res = bdd.requete_unique(req);
champ_formulaire_Nom_contact.text = res[0];
req = 'SELECT Valeur FROM TB_Configuration WHERE Nom="Telephone_installateur" ';
res = bdd.requete_unique(req);
champ_formulaire_Telephone.text = res[0];
req = 'SELECT Valeur FROM TB_Configuration WHERE Nom="Adresse_email_installateur" ';
res = bdd.requete_unique(req);
champ_formulaire_Email.text = res[0];
req = 'SELECT Valeur FROM TB_Configuration WHERE Nom="Site_web_installateur" ';
res = bdd.requete_unique(req);
champ_formulaire_Site_web.text = res[0];
}
//=============================================================================================================
// Mise à jour des valeurs saisies
//=============================================================================================================
function mise_a_jour()
{
var req;
req = 'UPDATE TB_Configuration ';
req += ' SET Valeur="' + champ_formulaire_Raison_sociale.text + '" ';
req += ' WHERE Nom="Raison_sociale_installateur" ';
//console.log("Requête = [ " + req + "; ]")
bdd.requete_unique(req);
req = 'UPDATE TB_Configuration ';
req += ' SET Valeur="' + champ_formulaire_Rue_1.text + '" ';
req += ' WHERE Nom="Rue_1_installateur" ';
//console.log("Requête = [ " + req + "; ]")
bdd.requete_unique(req);
req = 'UPDATE TB_Configuration ';
req += ' SET Valeur="' + champ_formulaire_Rue_2.text + '" ';
req += ' WHERE Nom="Rue_2_installateur" ';
//console.log("Requête = [ " + req + "; ]")
bdd.requete_unique(req);
req = 'UPDATE TB_Configuration ';
req += ' SET Valeur="' + champ_formulaire_Code_postal.text + '" ';
req += ' WHERE Nom="Code_postal_installateur" ';
//console.log("Requête = [ " + req + "; ]")
bdd.requete_unique(req);
req = 'UPDATE TB_Configuration ';
req += ' SET Valeur="' + champ_formulaire_Ville.text + '" ';
req += ' WHERE Nom="Ville_installateur" ';
//console.log("Requête = [ " + req + "; ]")
bdd.requete_unique(req);
req = 'UPDATE TB_Configuration ';
req += ' SET Valeur="' + champ_formulaire_Nom_contact.text + '" ';
req += ' WHERE Nom="Nom_contact_installateur" ';
//console.log("Requête = [ " + req + "; ]")
bdd.requete_unique(req);
req = 'UPDATE TB_Configuration ';
req += ' SET Valeur="' + champ_formulaire_Telephone.text + '" ';
req += ' WHERE Nom="Telephone_installateur" ';
//console.log("Requête = [ " + req + "; ]")
bdd.requete_unique(req);
req = 'UPDATE TB_Configuration ';
req += ' SET Valeur="' + champ_formulaire_Email.text + '" ';
req += ' WHERE Nom="Adresse_email_installateur" ';
//console.log("Requête = [ " + req + "; ]")
bdd.requete_unique(req);
req = 'UPDATE TB_Configuration ';
req += ' SET Valeur="' + champ_formulaire_Site_web.text + '" ';
req += ' WHERE Nom="Site_web_installateur" ';
//console.log("Requête = [ " + req + "; ]")
bdd.requete_unique(req);
}
}
+514
View File
@@ -0,0 +1,514 @@
//========================================================================================================================================================================
// CONFIG_KORIOLAN.QML
// Ce fichier traite les paramètres de configuration qui sont réservés à Koriolan
// La page est appelée depuis la page parametres_avances.qml, en cliquant sur "Accès réservé Koriolan" depuis la colonne "Sécurité"
// Note importante : ce menu n'est accéssible que si l'accés à été fait avec un code au moins de niveau Administrateur.
//========================================================================================================================================================================
import QtQuick 2.12
import QtQml.Models
import qt.koriolan.shipheart 1.0
Shipheart_page {
id: page_config_koriolan
// Mis en forme :
readonly property double pourcentage_largeur_colonne_gauche: 0.75
readonly property double espace_colonnes_principales: 20
readonly property double pourcentage_hauteur_ligne_haut: 0.2
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 hauteur_ligne_formulaire: 60
readonly property double pourcentage_largeur_labels_formulaire: 0.6
readonly property int hauteur_bouton_droit: 80
readonly property double pourcentage_bandeau_bas_largeur_zone_retour: 1
readonly property int periode_init_raffraichissement_parametres: 1 // En ms
readonly property int periode_raffraichissement_parametres: 1000 // En ms
// Affichage :
property bool bouton_bandeau_bas_zone_retour_actif: false
property int mode_maj_firmwares: 0
// Données :
// 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 if (!bouton_bandeau_bas_zone_retour_actif) {
}
}
}
// Titre de la page :
Shipheart_titre_1 {titre:qsTr("Accès réservé Koriolan"); 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;
color: "transparent"
ListView {
id: grille_principale
width: parent.width
height: parent.height
snapMode: GridView.SnapToRow // L'arret de l'affichage se fera sur une rangée entière
clip: true // Nécessaire pour que les Delegates s'arretent à l'intérieur de la GridView
model: modele_grille_principale
footer: Rectangle { // Permet de dégager la place suffisante en fin de GridView pour accéder même au dernier élément lorsque que clavier est déplié
height: shipheart_hauteur_clavier
color: "transparent"
}
ObjectModel {
id: modele_grille_principale
//-------------------------------------------------------------------------------
// Ligne de formulaire : Client spécifique
Rectangle {
width: parent.width
height: visible ? hauteur_ligne_formulaire : 0
color: "transparent"
Shipheart_formulaire_label {
text: qsTr("Client spécifique")
width: parent.width * pourcentage_largeur_labels_formulaire
}
Shipheart_formulaire_combobox {
id: champ_formulaire_Specifique_client
model: ListModel { }
width: parent.width * (1 - pourcentage_largeur_labels_formulaire)
onActivated: {
shipheart_clic();
}
}
Shipheart_formulaire_ligne_separation {
haut_bas_hautbas: shipheart_formulaire_ligne_separation_basse
}
}
//-------------------------------------------------------------------------------
// Ligne de formulaire : Mode maj firmwares
Rectangle {
width: parent.width
height: visible ? hauteur_ligne_formulaire : 0
color: "transparent"
Shipheart_formulaire_label {
text: qsTr("Mode de mise à jour des firmwares")
width: parent.width * pourcentage_largeur_labels_formulaire
}
Shipheart_formulaire_combobox {
id: champ_formulaire_Mode_maj_firmwares
model: ListModel { }
width: parent.width * (1 - pourcentage_largeur_labels_formulaire)
onActivated: {
shipheart_clic();
}
}
Shipheart_formulaire_ligne_separation {
haut_bas_hautbas: shipheart_formulaire_ligne_separation_basse
}
}
}
}
}
//-------------------------------------------------------
// Ecran Milieu - Gauche :
//-------------------------------------------------------
Rectangle {
id: zone_affichage_milieu_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;
color: "transparent"
//-------------------------------------------------------------------
// Grille pour afficher les valeurs des paramètres non configurables :
GridView {
id: grille_informations
width: parent.width
height: parent.height
cellWidth: parent.width;
cellHeight: hauteur_ligne_formulaire
snapMode: GridView.SnapToRow // L'arret de l'affichage se fera sur une rangée entière
clip: true // Nécessaire pour que les Delegates s'arretent à l'intérieur de la GridView
model: modele_grille_informations
delegate: Rectangle {
width: parent.width
height: visible ? hauteur_ligne_formulaire : 0
color: "transparent"
Shipheart_formulaire_label {
text: model.nom_champ
width: parent.width * pourcentage_largeur_labels_formulaire
}
Shipheart_formulaire_valeur_fixe {
text: model.valeur_champ
width: parent.width * (1 - pourcentage_largeur_labels_formulaire)
x: parent.width * pourcentage_largeur_labels_formulaire
}
Shipheart_formulaire_ligne_separation {
haut_bas_hautbas: shipheart_formulaire_ligne_separation_basse
}
}
ListModel {
id: modele_grille_informations
}
}
}
//-------------------------------------------------------
// 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 * pourcentage_hauteur_ligne_haut) - (espace_lignes_principales / 2);
color: "transparent"
Column {
anchors.fill: parent
spacing: 10
// Bouton Enregistrer :
Rectangle {
width: parent.width
height: hauteur_bouton_droit
color: shipheart_couleur_fond_bouton
radius: shipheart_rayon_bouton
// visible: !clavier.active // SDIR #255
Text {
anchors {
verticalCenter: parent.verticalCenter
horizontalCenter: parent.horizontalCenter
}
font.pixelSize: 20
text: qsTr("Enregistrer")
color: couleur_blanc_casse
}
MouseArea {
anchors.fill: parent
onClicked: {
shipheart_clic();
mise_a_jour(); // Mise à jour des données saisies dans la BDD et reconfiguration par l'application pour prise en compte.
stackView.pop();
}
}
}
// Bouton Test matériel :
Rectangle {
width: parent.width
height: hauteur_bouton_droit
color: shipheart_couleur_fond_bouton
radius: shipheart_rayon_bouton
visible: !clavier.active
Text {
anchors {
verticalCenter: parent.verticalCenter
horizontalCenter: parent.horizontalCenter
}
font.pixelSize: 20
text: qsTr("Tests matériel")
color: couleur_blanc_casse
}
MouseArea {
anchors.fill: parent
onClicked: {
shipheart_clic();
stackView.push("shipheart_test_home.qml")
}
}
}
}
}
//-------------------------------------------------------
// 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 * pourcentage_bandeau_bas_largeur_zone_retour) - (2 * parent.padding)
height: shipheart_hauteur_bandeau_bas - parent.topPadding - parent.bottomPadding;
radius: shipheart_rayon_bouton
color: shipheart_couleur_fond_bouton
Image {
source: "/images/icone_retour.png"
sourceSize.height: 30
anchors {
verticalCenter: parent.verticalCenter
horizontalCenter: parent.horizontalCenter
}
}
MouseArea { // Zone d'interaction. On est obligé de la mettre dans la grille pour que Rectangle et MouseArea soient frères ou parent/enfant ET qu'on n'occupe qu'un espace dans la grille.
id: mouse_area_bandeau_bas_zone_retour
anchors.fill: parent
onClicked: {
shipheart_clic();
stackView.pop()
}
}
}
}
// 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 nb;
var i;
var nom_champ, valeur_champ;
var specifique_client;
modele_grille_informations.clear();
req = 'SELECT Nom, Valeur FROM TB_Configuration ORDER BY Cle_primaire';
//console.log("req =" + req);
nb = bdd.requete(req);
for (i = 0; i < nb; i ++)
{
res = bdd.lecture_ligne();
if (!res.isEmpty)
{
switch(res[0])
{
case "Nb_jours_avant_suppression_historique": nom_champ = qsTr("Nombre jours avant suppression historique"); valeur_champ = res[1]; break;
case "Nb_jours_avant_suppression_mesures": nom_champ = qsTr("Nombre jours avant suppression mesures"); valeur_champ = res[1]; break;
case "Periode_sauvegarde_bdd": nom_champ = qsTr("Période de sauvegarde BDD"); valeur_champ = res[1]; break;
case "Periode_check_download": nom_champ = qsTr("Période de vérification download"); valeur_champ = res[1]; break;
default: continue;
}
modele_grille_informations.append({"commande": false, "nom_champ": nom_champ, "valeur_champ": valeur_champ});
}
else break; // Ne devrait pas arriver....
}
req = 'SELECT Nom, Valeur FROM TB_Systeme ORDER BY Cle_primaire';
//console.log("req =" + req);
nb = bdd.requete(req);
for (i = 0; i < nb; i ++)
{
res = bdd.lecture_ligne();
if (!res.isEmpty)
{
switch(res[0])
{
case "Date_dernier_archivage_mesures": nom_champ = qsTr("Date de dernier archivage mesures"); valeur_champ = res[1]; break;
default: continue;
}
modele_grille_informations.append({"commande": false, "nom_champ": nom_champ, "valeur_champ": valeur_champ});
}
else break; // Ne devrait pas arriver....
}
//---------------------------------------------------------------------------------------
// Specifique_client :
// Mise à jour depuis la BDD au cas où... :
req = 'SELECT Valeur FROM TB_Configuration WHERE Nom="Specifique_client" ';
res = bdd.requete_unique(req);
shipheart_specifique_client = res[0].split(';') // Mémorisation de l'ensemble du tableau
specifique_client = Number(shipheart_specifique_client[0]);
champ_formulaire_Specifique_client.model.clear();
champ_formulaire_Specifique_client.model.append({"valeur": shipheart_specifique_client_tous, "texte": qsTr("Aucune spécificité") });
champ_formulaire_Specifique_client.model.append({"valeur": shipheart_specifique_client_suncy, "texte": qsTr("Client Suncy Concept") });
// Positionnement de l'index sur la valeur en cours :
i = recherche_index_valeur_modele(specifique_client, champ_formulaire_Specifique_client.model, "specifique_client");
if (i === -1) champ_formulaire_Specifique_client.currentIndex = recherche_index_valeur_modele(shipheart_specifique_client_tous, champ_formulaire_Specifique_client.model, "specifique_client");
else champ_formulaire_Specifique_client.currentIndex = i;
//---------------------------------------------------------------------------------------
// Mode de mise à jour des firmwares :
req = 'SELECT Valeur FROM TB_Configuration WHERE Nom="Mode_maj_firmwares" ';
res = bdd.requete_unique(req);
mode_maj_firmwares = Number(res[0]);
champ_formulaire_Mode_maj_firmwares.model.clear();
champ_formulaire_Mode_maj_firmwares.model.append({"valeur": 0, "texte": qsTr("Normal (si version installée est antérieure à la version en cours)") });
champ_formulaire_Mode_maj_firmwares.model.append({"valeur": 1, "texte": qsTr("Différente (si version installée est différente de version en cours)") });
champ_formulaire_Mode_maj_firmwares.model.append({"valeur": 2, "texte": qsTr("Toujours (à utiliser avec modération...)") });
// Positionnement de l'index sur la valeur en cours :
i = recherche_index_valeur_modele(mode_maj_firmwares, champ_formulaire_Mode_maj_firmwares.model, "mode_maj_firmwares");
if (i === -1) champ_formulaire_Mode_maj_firmwares.currentIndex = recherche_index_valeur_modele(0, champ_formulaire_Mode_maj_firmwares.model, "mode_maj_firmwares");
else champ_formulaire_Mode_maj_firmwares.currentIndex = i;
}
//=============================================================================================================
// Mise à jour des valeurs saisies
//=============================================================================================================
function mise_a_jour()
{
var req;
var specifique_client;
var chaine_specifique_client = "";
//-------------------------------------------------------------------------------------------------------------
// Spécifique client :
specifique_client = champ_formulaire_Specifique_client.model.get(champ_formulaire_Specifique_client.currentIndex).valeur;
if (Number(shipheart_specifique_client[0]) !== specifique_client) // Si le code client a changé :
{
switch(specifique_client)
{
default:
case shipheart_specifique_client_tous:
chaine_specifique_client = shipheart_specifique_client_tous;
break;
case shipheart_specifique_client_suncy:
chaine_specifique_client = shipheart_specifique_client_suncy;
chaine_specifique_client += ";id_circuit_activation_moteur=0"
break;
}
// Mise à jour de la nouvelle valeur dans la BDD :
req = 'UPDATE TB_Configuration SET Valeur="' + chaine_specifique_client + '" ';
req += ' WHERE Nom="Specifique_client" '
//console.log("Requête = [ " + req + "; ]");
bdd.requete_unique(req);
}
//-------------------------------------------------------------------------------------------------------------
// Mode de mise à jour des firmwares :
if (mode_maj_firmwares !== champ_formulaire_Mode_maj_firmwares.model.get(champ_formulaire_Mode_maj_firmwares.currentIndex).valeur)
{
mode_maj_firmwares = champ_formulaire_Mode_maj_firmwares.model.get(champ_formulaire_Mode_maj_firmwares.currentIndex).valeur;
switch(mode_maj_firmwares)
{
case 0:
case 1:
case 2:
// Mise à jour de la nouvelle valeur dans la BDD :
req = 'UPDATE TB_Configuration SET Valeur="' + mode_maj_firmwares + '" ';
req += ' WHERE Nom="Mode_maj_firmwares" '
//console.log("Requête = [ " + req + "; ]");
bdd.requete_unique(req);
break;
default:
break;
}
}
//------------------------------------------------------------------------------------------------------------------
// ATTENTION : le rechargement de la config ne suffira pas la plupart du temps. Il faudra procéder à un reset de ShipHeart...
// Demande de reconfiguration système via la BDD car on a modifié des valeurs de configuration :
synchro_bdd(shipheart_type_synchro_config);
}
//=============================================================================================================
// Recherche de l'index d'une certaine valeur dans un modele
//=============================================================================================================
function recherche_index_valeur_modele(cible, modele, nom_modele)
{
var i;
for (i=0; i<modele.count; i ++)
{
if (modele.get(i).valeur === cible) break; // Cas où la valeur cible a été trouvée parmi les valeurs du modèle
}
if (i === modele.count)
{
console.log("Aucune valeur trouvée pour cible = [" + cible + "] du modèle = " + nom_modele);
i = -1; // Cas où la valeur n'a pas été trouvée parmi les valeurs du modèle
}
//else console.log("Valeur trouvée : [" + i + "] pour cible = [" + cible + "] soit texte = [" + modele.get(i).texte + "]");
return(i);
}
}
+1085
View File
File diff suppressed because it is too large Load Diff
+422
View File
@@ -0,0 +1,422 @@
//========================================================================================================================================================================
// CONFIG_MODULE_CREATION.QML
// Ce fichier traite la création d'un module dans la table TB_Modules_config
// La page est appelée depuis la page config_réseau_k_bus.qml, en cliquant sur "+"
//========================================================================================================================================================================
import QtQuick 2.12
import QtQuick.Controls 2.12
import QtQml.Models
import qt.koriolan.shipheart 1.0
import"fonctions.js" as Fonctions
Shipheart_page {
id: page_config_module_modification
// Mis en forme :
readonly property double pourcentage_largeur_colonne_gauche: 0.75
readonly property double pourcentage_largeur_unites_formulaire: 0.1
readonly property double espace_colonnes_principales: 20
readonly property double pourcentage_hauteur_ligne_haut: 1
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 hauteur_ligne_formulaire: 60
readonly property double pourcentage_largeur_labels_formulaire: 0.4
readonly property int hauteur_bouton_droit: 80
readonly property double pourcentage_bandeau_bas_largeur_zone_retour: 1
readonly property int periode_init_raffraichissement_parametres: 1 // En ms
readonly property int periode_raffraichissement_parametres: 1000 // En ms
// Affichage :
// Données :
property int type_module: -1
property string nom_module: ""
property int adresse_module: 0
// Modèles :
// Note : les modèles chargés dans 'main.qml' n'ont pas à être rechargés dans les pages suivantes
//===========================================================
// Timer periode raffraichissement parametres :
//===========================================================
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 {
}
}
}
// Titre de la page :
Shipheart_titre_1 {titre:qsTr("Création d'un module à l'adresse [%1]").arg(adresse_module); 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);
color: "transparent"
ListView {
id: grille_principale
width: parent.width
height: parent.height
snapMode: GridView.SnapToRow // L'arret de l'affichage se fera sur une rangée entière
clip: true // Nécessaire pour que les Delegates s'arretent à l'intérieur de la GridView
model: modele_grille_principale
footer: Rectangle { // Permet de dégager la place suffisante en fin de GridView pour accéder même au dernier élément lorsque que clavier est déplié
height: shipheart_hauteur_clavier
color: "transparent"
}
ObjectModel {
id: modele_grille_principale
//-------------------------------------------------------------------------------
// Ligne de formulaire : Type de module
Rectangle {
width: (shipheart_largeur_page * pourcentage_largeur_colonne_gauche) - (espace_colonnes_principales / 2)
height: visible ? hauteur_ligne_formulaire : 0
color: "transparent"
Shipheart_formulaire_label {
text: qsTr("Type de module")
width: parent.width * pourcentage_largeur_labels_formulaire
couleur: (champ_formulaire_Type_module.currentIndex !== 0)
? shipheart_formulaire_couleur_texte_par_defaut
: shipheart_formulaire_couleur_texte_en_erreur
}
Shipheart_formulaire_combobox {
id: champ_formulaire_Type_module
model: ListModel { }
width: parent.width * (1 - pourcentage_largeur_labels_formulaire)
onActivated: {
shipheart_clic();
type_module = champ_formulaire_Type_module.model.get(champ_formulaire_Type_module.currentIndex).valeur;
// champ_formulaire_Nom.text = Fonctions.nom_type_module(champ_formulaire_Type_module.model.get(champ_formulaire_Type_module.currentIndex).valeur) + " @" + adresse_module;
}
}
Shipheart_formulaire_ligne_separation {
haut_bas_hautbas: shipheart_formulaire_ligne_separation_basse
}
}
//-------------------------------------------------------------------------------
// Ligne de séparation :
Rectangle {
width: parent.width
height: 5
color: "transparent"
}
//-------------------------------------------------------------------------------
// Ligne de formulaire : Nom
Rectangle {
width: (shipheart_largeur_page * pourcentage_largeur_colonne_gauche) - (espace_colonnes_principales / 2)
implicitHeight: champ_formulaire_Nom.implicitHeight + 10
color: "transparent"
visible: (champ_formulaire_Type_module.currentIndex !== 0)
Shipheart_formulaire_label {
text: qsTr("Nom du module")
width: parent.width * pourcentage_largeur_labels_formulaire
anchors.top: parent.top // Ces deux lignes permettent...
verticalAlignment: Text.AlignTop // ... d'aligner le label avec le haut du Textarea qui suit (utile loesque le textarea est très long)
}
Shipheart_formulaire_textarea {
id: champ_formulaire_Nom
width: parent.width * (1 - pourcentage_largeur_labels_formulaire)
horizontalAlignment: Text.AlignJustify
wrapMode: Label.Wrap
}
Shipheart_formulaire_ligne_separation {
haut_bas_hautbas: shipheart_formulaire_ligne_separation_basse
}
}
//-------------------------------------------------------------------------------
// Ligne de formulaire : Emplacement
Rectangle {
width: (shipheart_largeur_page * pourcentage_largeur_colonne_gauche) - (espace_colonnes_principales / 2)
implicitHeight: champ_formulaire_Emplacement.implicitHeight + 10
color: "transparent"
visible: (champ_formulaire_Type_module.currentIndex !== 0) &&
(champ_formulaire_Nom.text !== "")
Shipheart_formulaire_label {
text: qsTr("Emplacement du module à bord")
width: parent.width * pourcentage_largeur_labels_formulaire
anchors.top: parent.top // Ces deux lignes permettent...
verticalAlignment: Text.AlignTop // ... d'aligner le label avec le haut du Textarea qui suit (utile loesque le textarea est très long)
}
Shipheart_formulaire_textarea {
id: champ_formulaire_Emplacement
width: parent.width * (1 - pourcentage_largeur_labels_formulaire)
horizontalAlignment: Text.AlignJustify
wrapMode: Label.Wrap
}
Shipheart_formulaire_ligne_separation {
haut_bas_hautbas: shipheart_formulaire_ligne_separation_basse
}
}
//-------------------------------------------------------------------------------
// Ligne de séparation :
Rectangle {
width: parent.width
height: 5
color: "transparent"
}
//-------------------------------------------------------------------------------
// Ligne de formulaire : Commentaires
Rectangle {
width: (shipheart_largeur_page * pourcentage_largeur_colonne_gauche) - (espace_colonnes_principales / 2)
implicitHeight: champ_formulaire_Commentaires.implicitHeight + 10
color: "transparent"
visible: (champ_formulaire_Type_module.currentIndex !== 0) &&
(champ_formulaire_Nom.text !== "")
Shipheart_formulaire_label {
text: qsTr("Commentaires")
width: parent.width * pourcentage_largeur_labels_formulaire
anchors.top: parent.top // Ces deux lignes permettent...
verticalAlignment: Text.AlignTop // ... d'aligner le label avec le haut du Textarea qui suit (utile loesque le textarea est très long)
}
Shipheart_formulaire_textarea {
id: champ_formulaire_Commentaires
width: parent.width * (1 - pourcentage_largeur_labels_formulaire)
horizontalAlignment: Text.AlignJustify
wrapMode: Label.Wrap
}
Shipheart_formulaire_ligne_separation {
haut_bas_hautbas: shipheart_formulaire_ligne_separation_basse
}
}
//-------------------------------------------------------------------------------
// Ligne d'extension pour laisser la liste remonter au-dessus de l'écran si besoin :
Rectangle {
width: parent.width
height: 100
color: "transparent"
}
}
}
}
//-------------------------------------------------------
// 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 * pourcentage_hauteur_ligne_haut) - (espace_lignes_principales / 2);
color: "transparent"
Column {
anchors.fill: parent
spacing: 10
// Bouton Enregistrer :
Rectangle {
width: parent.width
height: hauteur_bouton_droit
color: shipheart_couleur_fond_bouton
radius: shipheart_rayon_bouton
// visible: !clavier.active // SDIR #255
Text {
anchors {
verticalCenter: parent.verticalCenter
horizontalCenter: parent.horizontalCenter
}
font.pixelSize: 20
text: qsTr("Enregistrer")
color: couleur_blanc_casse
}
MouseArea {
anchors.fill: parent
onClicked: {
shipheart_clic();
creation();
stackView.pop();
}
}
}
}
}
//-------------------------------------------------------
// 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 * pourcentage_bandeau_bas_largeur_zone_retour) - (2 * parent.padding)
height: shipheart_hauteur_bandeau_bas - parent.topPadding - parent.bottomPadding;
radius: shipheart_rayon_bouton
color: shipheart_couleur_fond_bouton
Image {
source: "/images/icone_retour.png"
sourceSize.height: 30
anchors {
verticalCenter: parent.verticalCenter
horizontalCenter: parent.horizontalCenter
}
}
MouseArea { // Zone d'interaction. On est obligé de la mettre dans la grille pour que Rectangle et MouseArea soient frères ou parent/enfant ET qu'on n'occupe qu'un espace dans la grille.
id: mouse_area_bandeau_bas_zone_retour
anchors.fill: parent
onClicked: {
shipheart_clic();
stackView.pop()
}
}
}
}
// 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;
//---------------------------------------------------------------------------------------
// Type de module :
champ_formulaire_Type_module.model.clear();
champ_formulaire_Type_module.model.append({"valeur": shipheart_type_module_inconnu, "texte": Fonctions.nom_type_module(shipheart_type_module_inconnu) });
// champ_formulaire_Type_module.model.append({"valeur": shipheart_type_module_shipbase, "texte": Fonctions.nom_type_module(shipheart_type_module_shipbase) });
champ_formulaire_Type_module.model.append({"valeur": shipheart_type_module_shippower8DC, "texte": Fonctions.nom_type_module(shipheart_type_module_shippower8DC) });
champ_formulaire_Type_module.model.append({"valeur": shipheart_type_module_shippower812, "texte": Fonctions.nom_type_module(shipheart_type_module_shippower812) });
champ_formulaire_Type_module.model.append({"valeur": shipheart_type_module_shippower4AC, "texte": Fonctions.nom_type_module(shipheart_type_module_shippower4AC) });
champ_formulaire_Type_module.model.append({"valeur": shipheart_type_module_shipBS, "texte": Fonctions.nom_type_module(shipheart_type_module_shipBS) });
// Positionnement de l'index sur la valeur en cours :
i = recherche_index_valeur_modele(type_module, champ_formulaire_Type_module.model, "Type_module");
champ_formulaire_Type_module.currentIndex = i; // La valeur de retour d'erreur de la fonction étant '-1', comme la valeur 'Non-définie', on peut l'affecter directement, sans vérifier la valeir de retour. Dans ces 2 cas, on sera en NON DEFINI...
}
//=============================================================================================================
// Création du module
//=============================================================================================================
function creation()
{
var req;
var val;
var texte_nom;
var texte_emplacement;
var texte_commentaires;
// Mise à jour des textes :
texte_nom = champ_formulaire_Nom.text.replace(/\"/g, "\\\"");
//console.log("Nom = [" + champ_formulaire_Nom.text + "] et texte modifié = [" + texte_nom + "]")
texte_emplacement = champ_formulaire_Emplacement.text.replace(/\"/g, "\\\"");
//console.log("Emplacement = [" + champ_formulaire_Emplacement.text + "] et texte modifié = [" + texte_emplacement + "]")
texte_commentaires = champ_formulaire_Commentaires.text.replace(/\"/g, "\\\"");
//console.log("Commentaires = [" + champ_formulaire_Commentaires.text + "] et texte modifié = [" + texte_commentaires + "]")
req = 'INSERT INTO TB_Modules_config ';
req += '(Adresse_k_bus, ';
req += 'Nom, ';
req += 'Type, ';
req += 'Emplacement, ';
req += 'Commentaires) ';
req += ' VALUES (';
req += adresse_module + ' ';
req += ', "' + texte_nom + '" ';
req += ', ' + type_module + ' ';
req += ', "' + texte_emplacement + '" ';
req += ', "' + texte_commentaires + '" ';
req += ') ';
console.log("Requête = [ " + req + "; ]")
bdd.requete_unique(req);
}
//=============================================================================================================
// Recherche de l'index d'une certaine valeur dans un modele
//=============================================================================================================
function recherche_index_valeur_modele(cible, modele, nom_modele)
{
var i;
for (i=0; i<modele.count; i ++)
{
if (modele.get(i).valeur === cible) break; // Cas où la valeur cible a été trouvée parmi les valeurs du modèle
}
if (i === modele.count)
{
console.log("Aucune valeur trouvée pour cible = [" + cible + "] du modèle = " + nom_modele);
i = -1; // Cas où la valeur n'a pas été trouvée parmi les valeurs du modèle
}
//else console.log("Valeur trouvée : [" + i + "] pour cible = [" + cible + "] soit texte = [" + modele.get(i).texte + "]");
return(i);
}
}
+398
View File
@@ -0,0 +1,398 @@
//========================================================================================================================================================================
// CONFIG_MODULE.QML
// Ce fichier traite la configuration des paramètres d'un module en particulier
// La page est appelée depuis la page config_modules.qml, en cliquant sur "Modifier les informations du module"
//========================================================================================================================================================================
import QtQuick 2.12
import QtQuick.Controls 2.12
import QtQml.Models
import qt.koriolan.shipheart 1.0
import"fonctions.js" as Fonctions
Shipheart_page {
id: page_config_module_modification
// Mis en forme :
readonly property double pourcentage_largeur_colonne_gauche: 0.75
readonly property double pourcentage_largeur_unites_formulaire: 0.1
readonly property double espace_colonnes_principales: 20
readonly property double pourcentage_hauteur_ligne_haut: 1
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 hauteur_ligne_formulaire: 60
readonly property double pourcentage_largeur_labels_formulaire: 0.4
readonly property int hauteur_bouton_droit: 80
readonly property double pourcentage_bandeau_bas_largeur_zone_retour: 1
readonly property int periode_init_raffraichissement_parametres: 1 // En ms
readonly property int periode_raffraichissement_parametres: 1000 // En ms
// Affichage :
// Données :
// property int adresse: 0
property int type_module: 0
property string nom_module: ""
property int adresse_module: 0
// Modèles :
// Note : les modèles chargés dans 'main.qml' n'ont pas à être rechargés dans les pages suivantes
//===========================================================
// Timer periode raffraichissement parametres :
//===========================================================
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 {
}
}
}
// Titre de la page :
Shipheart_titre_1 {titre:qsTr("Configuration du module [%1] à l'adresse [%2]").arg(nom_module).arg(adresse_module); 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);
color: "transparent"
ListView {
id: grille_principale
width: parent.width
height: parent.height
snapMode: GridView.SnapToRow // L'arret de l'affichage se fera sur une rangée entière
clip: true // Nécessaire pour que les Delegates s'arretent à l'intérieur de la GridView
model: modele_grille_principale
footer: Rectangle { // Permet de dégager la place suffisante en fin de GridView pour accéder même au dernier élément lorsque que clavier est déplié
height: shipheart_hauteur_clavier
color: "transparent"
}
ObjectModel {
id: modele_grille_principale
//-------------------------------------------------------------------------------
// Ligne de formulaire : Nom
Rectangle {
width: (shipheart_largeur_page * pourcentage_largeur_colonne_gauche) - (espace_colonnes_principales / 2)
implicitHeight: champ_formulaire_Nom.implicitHeight + 10
color: "transparent"
Shipheart_formulaire_label {
text: qsTr("Nom du module")
width: parent.width * pourcentage_largeur_labels_formulaire
anchors.top: parent.top // Ces deux lignes permettent...
verticalAlignment: Text.AlignTop // ... d'aligner le label avec le haut du Textarea qui suit (utile loesque le textarea est très long)
}
Shipheart_formulaire_textarea {
id: champ_formulaire_Nom
width: parent.width * (1 - pourcentage_largeur_labels_formulaire)
horizontalAlignment: Text.AlignJustify
wrapMode: Label.Wrap
}
Shipheart_formulaire_ligne_separation {
haut_bas_hautbas: shipheart_formulaire_ligne_separation_basse
}
}
//-------------------------------------------------------------------------------
// Ligne de séparation :
Rectangle {
width: parent.width
height: 5
color: "transparent"
}
//-------------------------------------------------------------------------------
// Ligne de formulaire : Emplacement
Rectangle {
width: (shipheart_largeur_page * pourcentage_largeur_colonne_gauche) - (espace_colonnes_principales / 2)
implicitHeight: champ_formulaire_Emplacement.implicitHeight + 10
color: "transparent"
Shipheart_formulaire_label {
text: qsTr("Emplacement du module à bord")
width: parent.width * pourcentage_largeur_labels_formulaire
anchors.top: parent.top // Ces deux lignes permettent...
verticalAlignment: Text.AlignTop // ... d'aligner le label avec le haut du Textarea qui suit (utile loesque le textarea est très long)
}
Shipheart_formulaire_textarea {
id: champ_formulaire_Emplacement
width: parent.width * (1 - pourcentage_largeur_labels_formulaire)
horizontalAlignment: Text.AlignJustify
wrapMode: Label.Wrap
}
Shipheart_formulaire_ligne_separation {
haut_bas_hautbas: shipheart_formulaire_ligne_separation_basse
}
}
//-------------------------------------------------------------------------------
// Ligne de séparation :
Rectangle {
width: parent.width
height: 5
color: "transparent"
}
//-------------------------------------------------------------------------------
// Ligne de formulaire : Commentaires
Rectangle {
width: (shipheart_largeur_page * pourcentage_largeur_colonne_gauche) - (espace_colonnes_principales / 2)
implicitHeight: champ_formulaire_Commentaires.implicitHeight + 10
color: "transparent"
Shipheart_formulaire_label {
text: qsTr("Commentaires")
width: parent.width * pourcentage_largeur_labels_formulaire
anchors.top: parent.top // Ces deux lignes permettent...
verticalAlignment: Text.AlignTop // ... d'aligner le label avec le haut du Textarea qui suit (utile loesque le textarea est très long)
}
Shipheart_formulaire_textarea {
id: champ_formulaire_Commentaires
width: parent.width * (1 - pourcentage_largeur_labels_formulaire)
horizontalAlignment: Text.AlignJustify
wrapMode: Label.Wrap
}
Shipheart_formulaire_ligne_separation {
haut_bas_hautbas: shipheart_formulaire_ligne_separation_basse
}
}
//-------------------------------------------------------------------------------
// Ligne de formulaire : Courant de masses maxi
Rectangle {
width: (shipheart_largeur_page * pourcentage_largeur_colonne_gauche) - (espace_colonnes_principales / 2)
height: visible ? hauteur_ligne_formulaire : 0
color: "transparent"
visible: (type_module === shipheart_type_module_shippower8DC)
Shipheart_formulaire_label { // Label :
text: qsTr("Courant de masses max. sur réseau")
width: parent.width * pourcentage_largeur_labels_formulaire
}
Shipheart_formulaire_textfield { // Champ de saisie :
id: champ_formulaire_Courant_masses_maxi
width: parent.width * (1 - pourcentage_largeur_labels_formulaire - pourcentage_largeur_unites_formulaire)
x: parent.width * pourcentage_largeur_labels_formulaire
flag_num_only: true
horizontalAlignment: Text.AlignRight
rightPadding: 10
}
Shipheart_formulaire_unite { // Unités :
text: qsTr("A")
width: parent.width * pourcentage_largeur_unites_formulaire
}
Shipheart_formulaire_ligne_separation {
haut_bas_hautbas: shipheart_formulaire_ligne_separation_basse
}
}
//-------------------------------------------------------------------------------
// Ligne d'extension pour laisser la liste remonter au-dessus de l'écran si besoin :
Rectangle {
width: parent.width
height: 100
color: "transparent"
}
}
}
}
//-------------------------------------------------------
// 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 * pourcentage_hauteur_ligne_haut) - (espace_lignes_principales / 2);
color: "transparent"
Column {
anchors.fill: parent
spacing: 10
// Bouton Enregistrer :
Rectangle {
width: parent.width
height: hauteur_bouton_droit
color: shipheart_couleur_fond_bouton
radius: shipheart_rayon_bouton
// visible: !clavier.active // SDIR #255
Text {
anchors {
verticalCenter: parent.verticalCenter
horizontalCenter: parent.horizontalCenter
}
font.pixelSize: 20
text: qsTr("Enregistrer")
color: couleur_blanc_casse
}
MouseArea {
anchors.fill: parent
onClicked: {
shipheart_clic();
mise_a_jour();
stackView.pop();
}
}
}
}
}
//-------------------------------------------------------
// 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 * pourcentage_bandeau_bas_largeur_zone_retour) - (2 * parent.padding)
height: shipheart_hauteur_bandeau_bas - parent.topPadding - parent.bottomPadding;
radius: shipheart_rayon_bouton
color: shipheart_couleur_fond_bouton
Image {
source: "/images/icone_retour.png"
sourceSize.height: 30
anchors {
verticalCenter: parent.verticalCenter
horizontalCenter: parent.horizontalCenter
}
}
MouseArea { // Zone d'interaction. On est obligé de la mettre dans la grille pour que Rectangle et MouseArea soient frères ou parent/enfant ET qu'on n'occupe qu'un espace dans la grille.
id: mouse_area_bandeau_bas_zone_retour
anchors.fill: parent
onClicked: {
shipheart_clic();
stackView.pop()
}
}
}
}
// 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;
req = 'SELECT Type, Adresse_k_bus, Nom, Emplacement, Commentaires FROM TB_Modules_config WHERE Adresse_k_bus=' + adresse_module + ' ';
res = bdd.requete_unique(req);
type_module = Number(res[0]);
nom_module = Fonctions.nom_type_module(type_module);
adresse_module = Number(res[1]);
champ_formulaire_Nom.text = res[2];
champ_formulaire_Emplacement.text = res[3];
champ_formulaire_Commentaires.text = res[4];
req = 'SELECT Valeur FROM TB_Configuration WHERE Nom="Courant_masses_maxi_sp8dc" ';
res = bdd.requete_unique(req);
champ_formulaire_Courant_masses_maxi.text = res[0];
}
//=============================================================================================================
// Mise à jour des valeurs saisies
//=============================================================================================================
function mise_a_jour()
{
var req;
var val;
var texte_nom;
var texte_emplacement;
var texte_commentaires;
var courant_masses;
// Mise à jour des textes :
texte_nom = champ_formulaire_Nom.text.replace(/\"/g, "\\\"");
//console.log("Nom = [" + champ_formulaire_Nom.text + "] et texte modifié = [" + texte_nom + "]")
texte_emplacement = champ_formulaire_Emplacement.text.replace(/\"/g, "\\\"");
//console.log("Emplacement = [" + champ_formulaire_Emplacement.text + "] et texte modifié = [" + texte_emplacement + "]")
texte_commentaires = champ_formulaire_Commentaires.text.replace(/\"/g, "\\\"");
//console.log("Commentaires = [" + champ_formulaire_Commentaires.text + "] et texte modifié = [" + texte_commentaires + "]")
req = 'UPDATE TB_Modules_config SET Nom="' + texte_nom + '", Emplacement="' + texte_emplacement + '", Commentaires="' + texte_commentaires + '" WHERE Adresse_k_bus=' + adresse_module + ' ';
//console.log("Requête = " + req)
bdd.requete_unique(req);
// Mise à jour de la valeur de 'Courant_masses_maxi_sp8dc' :
courant_masses = Number(champ_formulaire_Courant_masses_maxi.text);
if ((courant_masses >= 0) && (courant_masses <= shipheart_max_courant_masses_sp8dc))
{
req = 'UPDATE TB_Configuration SET Valeur=' + courant_masses + ' WHERE Nom="Courant_masses_maxi_sp8dc" ';
//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);
}
}
}
+416
View File
@@ -0,0 +1,416 @@
//========================================================================================================================================================================
// CONFIG_MODULE.QML
// Ce fichier traite la configuration d'un module (à la fois sur l'aspect physique et config)
// La page est appelée depuis la page config_reseau_j_bus.qml, en cliquant sur un module dans la colonne 'config'
//========================================================================================================================================================================
import QtQuick 2.12
import QtQuick.Controls 2.12
import QtQml.Models
import qt.koriolan.shipheart 1.0
import"fonctions.js" as Fonctions
Shipheart_page {
id: page_config_module
// Mis en forme :
readonly property double pourcentage_largeur_colonne_gauche: 0.75
readonly property double espace_colonnes_principales: 20
readonly property double pourcentage_hauteur_ligne_haut: 0
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 hauteur_ligne_formulaire: 50
readonly property double pourcentage_largeur_labels_formulaire: 0.45
readonly property int hauteur_bouton_droit: 80
readonly property double pourcentage_bandeau_bas_largeur_zone_retour: 1
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
// Données :
property bool flag_init: true // Permet de bloquer certains affichage tant que l'init n'est pas terminée
property int valeur_timer_appui_long: 0
property int adresse_module;
property int type_carte: 0
property string titre_page: ""
property int consigne_en_cours: 0 // Contient la commande en cours sur le module
property int index_courant_masses: -1 // SDIR #321
// 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();
}
}
}
//===========================================================
// Lorsque la page est sur le point d'être affichée dans la pile :
//===========================================================
StackView.onActivating: {
init_page(); // Permet un rafraichissement des champs qui ont pu être modifiés dans les pages en-dessous (Emplacement du module, Commentaires,...)
}
//===========================================================
// Titre de la page :
//===========================================================
Shipheart_titre_1 {titre:titre_page; 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;
color: "transparent"
ListView {
id: grille_principale
width: parent.width
height: parent.height
snapMode: GridView.SnapToRow // L'arret de l'affichage se fera sur une rangée entière
clip: true // Nécessaire pour que les Delegates s'arretent à l'intérieur de la GridView
model: modele_grille_principale
footer: Rectangle { // Permet de dégager la place suffisante en fin de GridView pour accéder même au dernier élément lorsque que clavier est déplié
height: shipheart_hauteur_clavier
color: "transparent"
}
ObjectModel {
id: modele_grille_principale
// Cf Ship2net.qml pour ajouter ici des champs de saisie...
}
}
}
//-------------------------------------------------------
// Ecran Milieu - Gauche :
//-------------------------------------------------------
Rectangle {
id: zone_affichage_milieu_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;
color: "transparent"
//-------------------------------------------------------------------
// Grille pour afficher les valeurs des paramètres non configurables :
ListView {
id: grille_informations
width: parent.width
height: parent.height
spacing: 5
snapMode: GridView.SnapToRow // L'arret de l'affichage se fera sur une rangée entière
clip: true // Nécessaire pour que les Delegates s'arretent à l'intérieur de la GridView
model: modele_grille_informations
delegate: ligne_champ
Component {
id: ligne_champ
Rectangle {
width: grille_informations.width
height: undefined // Permet de faire fonctionner le 'implicitHeight' qui suit
implicitHeight: (texte_valeur_champ.implicitHeight > hauteur_ligne_formulaire) ?
texte_valeur_champ.implicitHeight :
hauteur_ligne_formulaire
color: "transparent"
// visible: !flag_init
Shipheart_formulaire_label {
text: nom_champ
width: parent.width * pourcentage_largeur_labels_formulaire
}
Shipheart_formulaire_valeur_fixe {
id: texte_valeur_champ
text: valeur_champ
width: parent.width * (1 - pourcentage_largeur_labels_formulaire)
x: parent.width * pourcentage_largeur_labels_formulaire
padding: 10
}
Shipheart_formulaire_ligne_separation {
haut_bas_hautbas: shipheart_formulaire_ligne_separation_basse
}
}
}
ListModel {
id: modele_grille_informations
}
}
}
//-------------------------------------------------------
// 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 * pourcentage_hauteur_ligne_haut) - (espace_lignes_principales / 2);
color: "transparent"
Column {
anchors.fill: parent
spacing: 10
//------------------------------------------------
// Bouton localiser le module :
Rectangle {
width: parent.width
height: hauteur_bouton_droit
color: (consigne_en_cours !== 0) ? shipheart_couleur_fond_bouton_actif : shipheart_couleur_fond_bouton
radius: shipheart_rayon_bouton
visible: !clavier.active && (type_carte !== shipheart_type_module_shipbase)
Text {
anchors {
verticalCenter: parent.verticalCenter
horizontalCenter: parent.horizontalCenter
}
font.pixelSize: 20
text: qsTr("Localiser le module")
color: couleur_blanc_casse
}
MouseArea {
anchors.fill: parent
onClicked: {
shipheart_clic();
if (consigne_en_cours === 0) consigne_en_cours = 1;
else consigne_en_cours = 0;
commande_localisation_module(adresse_module, consigne_en_cours);
}
}
}
}
}
//-------------------------------------------------------
// 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 * pourcentage_bandeau_bas_largeur_zone_retour) - (2 * parent.padding)
height: shipheart_hauteur_bandeau_bas - parent.topPadding - parent.bottomPadding;
radius: shipheart_rayon_bouton
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 (consigne_en_cours !== 0) commande_localisation_module(adresse_module, 0); // Coupure de la localisation du module si on quitte la page et que la localisation est en cours
stackView.pop()
}
}
}
}
// 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()
{
mise_a_jour();
flag_init = false;
}
//=============================================================================================================
// Mise à jour des informations variables
// NB : le choix est fait de relancer une init complète chaque seconde. Un peu lourd mais permet de raffraichir
// automatiquement toutes les infos chaque seconde (utile en tests de cartes...) [SDIR #309]
//=============================================================================================================
function mise_a_jour()
{
var req, res;
var i;
var nom_champ, valeur_champ, flag_valide;
var index = 0;
var nom_champ_emplacement, valeur_champ_emplacement;
var nom_champ_commentaires, valeur_champ_commentaires;
modele_grille_informations.clear(); // RAZ des valeurs existantes
// La liste ci-dessous permet de définir les champs qui seront affichés ainsi que leur ordre d'affichage :
// Attention : 'Type' est traité hors boucle et doit donc rester en première position !!
var param = ["Type",
"Adresse_k_bus",
"Adresse_mac",
// "Software_type",
"Software_version",
"Software_crc",
"Hardware_version",
"Flag_r_term",
// "Cpu_unique_id",
// "Cpu_flash_size",
// "Cpu_package",
"Date_maj"]; // Laissé actif en dernière position pour gérer les ',' lors de commentaires... Mais supprimé dans le switch...
req = 'SELECT ';
req += param[0];
for (i=1; i<(param.length - 1); i++) req += ', ' + param[i];
req += ' FROM TB_Modules_physiques WHERE Adresse_k_bus=' + adresse_module + ' ';
//console.log("req =[" + req + "]");
res = bdd.requete_unique(req);
//=======================================================
// Cas particulier du 'Type' qui est traité hors boucle :
// NB : il doit donc rester en première position de la liste des paramètres !!
type_carte = Number(res[0]);
//==================================================
// Traitement de tous les autres paramètres :
for (i=1; i<(param.length - 1); i++)
{
flag_valide = true;
switch(param[i]) // NB : l'ordre de traitement dans le switch n'a aucun impact sur l'affichage....
{
case "Adresse_k_bus" :
if (type_carte === shipheart_type_module_shipbase) titre_page = qsTr("Paramètres du module [%1]").arg(Fonctions.nom_type_module(type_carte))
else titre_page = qsTr("Paramètres du module [%1] à l'adresse [%2]").arg(Fonctions.nom_type_module(type_carte)).arg(Number(res[i]));
continue;
case "Adresse_mac" : nom_champ = qsTr("N° de série"); valeur_champ = res[i]; break;
case "Software_type" : nom_champ = qsTr("Nom du firmware"); valeur_champ = res[i]; break;
case "Software_version" : nom_champ = qsTr("Version du firmware"); valeur_champ = res[i]; break;
case "Hardware_version": nom_champ = qsTr("Version matérielle"); valeur_champ = res[i]; break;
case "Flag_r_term" : if (adresse_module === 0) continue; nom_champ = qsTr("Résistance de terminaison"); valeur_champ = Number(res[i]) ? qsTr("Oui") : qsTr("Non"); break;
// case "Cpu_unique_id": nom_champ = qsTr("ID unique du processeur"); valeur_champ = res[i]; break;
// case "Cpu_flash_size": nom_champ = qsTr("Taille mémoire flash"); valeur_champ = res[i] + " kB"; break;
// case "Cpu_package": nom_champ = qsTr("Code package processeur"); valeur_champ = res[i]; break;
case "Software_crc": nom_champ = qsTr("CRC du firmware"); valeur_champ = res[i]; break;
case "Date_maj":
flag_valide = false;
break;
default: nom_champ = param[i]; valeur_champ = res[i]; break;
}
if (flag_valide)
{
valeur_champ = valeur_champ.replace(/\n/g, "<br />"); // Remplace les '\n' par '<br />'. Sinon, les '\n' ne sont pas pris en compte par le RichText d'affichage.
modele_grille_informations.append({"nom_champ": nom_champ, "valeur_champ": valeur_champ});
}
}
//==================================================
// Cas particulier des SP8DC avec leur courant de masse :
if (type_carte === shipheart_type_module_shippower8DC)
{
// Ajout de l'entrée dans le modèle :
modele_grille_informations.append({"nom_champ": qsTr("Courant de masses sur réseau"), "valeur_champ": "0"});
index_courant_masses = modele_grille_informations.count - 1; // Permet d'accéder ensuite à ce champ particulier pour mettre sa valeur à jour. Le '-1' permet d'avoir une valeur entre 0 et N car l'append précédent a positionné le COUNT à >= 1.
}
//=========================================================
// Cas particulier du champ Version Processeur de ShipBase :
if (adresse_module === 0) // Pour ShipBase, la version de Compute Module n'est pas dans TB_Modules_physiques mais dans TB_Systeme...
{
res = bdd.requete_unique('SELECT Valeur FROM TB_Systeme WHERE Nom="Version_cm" ');
modele_grille_informations.append({"nom_champ": qsTr("Version processeur"), "valeur_champ": res[0]});
}
//=========================================================
// Courant de masse des SP8DC :
if (index_courant_masses >= 0) // SDIR #321 : si l'index est toujours égal à -1, c'est que le champ n'est pas utilisé sur ce module et on ne fait pas de mises à jour.
{
req = 'SELECT Courant_masses FROM TB_Modules_config WHERE Adresse_k_bus=' + adresse_module + ' ';
//console.log("req = [" + req + "]");
res = bdd.requete_unique(req);
modele_grille_informations.setProperty(index_courant_masses, "valeur_champ", Number(res[0]).toFixed(3).toString() + " A");
}
}
//=============================================================================================================
// Emission d'une commande à destination d'une carte
//=============================================================================================================
function commande_localisation_module(id, consigne)
{
var req;
consigne_en_cours = consigne;
req = "UPDATE TB_Modules_config SET New_cde='" + shipheart_commande_module_localisation + "/" + consigne + "' WHERE Adresse_k_bus=" + adresse_module + " ";
bdd.requete_unique(req);
}
}
+709
View File
@@ -0,0 +1,709 @@
//========================================================================================================================================================================
// CONFIG_ORDRE_ELEMENTS.QML
// Ce fichier traite l'ordre d'affichage d'éléments sur la page d'accueil (batteries, circuits, réservoirs, capteurs). La page permet modifier l'ordre d'affichage
// en déplaçant (glisser/déposer) éléments configurés.
// 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_1, en cliquant sur l'icone des deux flèches inversés en bas de l'écran
//========================================================================================================================================================================
import QtQuick 2.12
import QtQuick.Controls 2.12
import QtQml.Models 2.1
import qt.koriolan.shipheart 1.0
Shipheart_page {
id: page_config_ordre_elements
// Mis en forme :
readonly property double affichage_hauteur_utile: shipheart_hauteur_page - shipheart_hauteur_bandeau_haut - shipheart_hauteur_bandeau_bas
readonly property int periode_init_raffraichissement_parametres: 1 // En ms
readonly property int periode_raffraichissement_parametres: 1000 // En ms
// Affichage :
property string texte_titre: ""
property string texte_aucun_groupe: ""
property string texte_aucun_element: ""
property bool bouton_bandeau_bas_zone_retour_actif: false
property bool flag_affichage_zone_groupe: false
property double pourcentage_largeur_zone_groupes: 0.4
property int nombre_groupes_affichees_sur_une_colonne: 8
property int espace_zones: 10
property int nombre_colonnes_affichage: 1
property int nombre_elements_affichees_sur_une_colonne: 8
property int espace_lignes_affichage: 10
property int espace_colonnes_affichage: 300
// Données :
property string table: ""
property int type_source_groupe: shipheart_type_source_inconnu // 0
property int id_groupe: 0 // Cle_primaire du groupe en cours (par défaut, 0 = sans famille)
// 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 if (!bouton_bandeau_bas_zone_retour_actif)
{
}
}
}
//===========================================================
// Lorsque la page est sur le point d'être affichée dans la pile :
//===========================================================
StackView.onActivating: {
mise_a_jour_liste_groupes();
mise_a_jour_liste_elements_groupe(id_groupe);
}
// Titre de la page :
Shipheart_titre_1 {titre:texte_titre; hauteur: shipheart_hauteur_bandeau_haut}
//-------------------------------------------------------
// Ecran Principal :
//-------------------------------------------------------
Rectangle {
id: zone_affichage_principale
x: 0
y: shipheart_hauteur_bandeau_haut
width: shipheart_largeur_page
height: affichage_hauteur_utile
color: "transparent"
//-------------------------------------------------
// Liste des groupes
Rectangle {
id: zone_affichage_groupes
x: 0
y: 0
width: (shipheart_largeur_page * pourcentage_largeur_zone_groupes) - (espace_zones / 2)
height: affichage_hauteur_utile
visible: flag_affichage_zone_groupe
color: "transparent"
//-------------------------------------------------------------------
// Liste :
ListView {
property bool deplacement_en_cours: false // Indique si un déplacement (au global) est en cours ou pas
property int save_current_index: 0 // Permet de garder le Current_index du début du déplacement et de compenser les changements qui sont fait automatiquement, notamment en début et fin de liste. Le principe est qu'une fois le mouvement déclenché, ON NE CHANGE PAS de CurrentIndex !!
id: grille_groupes
x: 0
y: 0
width: parent.width
height: parent.height
clip: true // Limite l'affichage des délégués dans la zone de ListView (sinon, on affiche au moins un délégué au-dessus et un en dessous...)
highlightRangeMode: ListView.StrictlyEnforceRange // Permet de faire défiler la liste jusqu'à ne garder que le dernier élément affiché
model: modele_liste_groupes
delegate: bouton_groupe
displaced: Transition { // Gère la transition lors d'un déplacement
NumberAnimation{
properties: "x,y" // Ici, on devrait pouvoir limiter à Y
duration: 500 // Vitesse à laquelle se déplacent les boutons lorsqu'on les déplace
}
}
Component {
id: bouton_groupe
MouseArea {
id: dragArea_groupe // Zone correspondant à un seul bouton (on est dans le Delegate)
width: grille_groupes.width
height: grille_groupes.height / nombre_groupes_affichees_sur_une_colonne
property bool bouton_appuye: false // On ne peut pas utiliser la propriété globale "deplacement_en_cours" car il faut une propriété par délégué
pressAndHoldInterval: 500; // Temporise avant de passer en mode "déplacement"
drag.target: bouton_appuye ? contenu_bouton_groupe : undefined
drag.axis: Drag.YAxis // On ne se déplace que sur l'axe Y
onClicked: {
shipheart_clic();
mise_a_jour_ordre();
id_groupe = modele_liste_groupes.get(index).id;
mise_a_jour_liste_groupes();
mise_a_jour_liste_elements_groupe(id_groupe);
}
onDoubleClicked: {
shipheart_clic();
stackView.push("config_groupe_elements.qml", {"table": type_source_groupe, "id_groupe":modele_liste_groupes.get(index).id})
}
onPressAndHold: {
shipheart_clic();
bouton_appuye = true // Indication (au niveau du bouton seul) du début de l'appui long donc du déplacement
grille_groupes.deplacement_en_cours = true // Indication (au niveau de ListView) du début du déplacement
grille_groupes.save_current_index = grille_groupes.currentIndex; // Sauvegarde du CurrentIndex au début du déplacement
}
onReleased: {
bouton_appuye = false // Indication (au niveau du bouton seul) de la fin de l'appui long donc du déplacement
grille_groupes.deplacement_en_cours = false // Indication (au niveau de ListView) de la fin du déplacement
grille_groupes.currentIndex = grille_groupes.save_current_index; // Restitution de la valeur initiale du CurrentIndex (a priori, pas nécessaire car mis à jour au fur et à mesure des changements dans "onCurrentIndexChanged")
}
Rectangle {
id: contenu_bouton_groupe
anchors {
horizontalCenter: parent.horizontalCenter
verticalCenter: parent.verticalCenter
}
width: dragArea_groupe.width - 10
height: dragArea_groupe.height - 10
radius: shipheart_rayon_bouton
color: (id_groupe === model.id) ? shipheart_couleur_fond_bouton_actif : shipheart_couleur_fond_bouton // SDIR #245
opacity: dragArea_groupe.bouton_appuye ? 0.6 : 1.0
Text {
anchors.verticalCenter: contenu_bouton_groupe.verticalCenter
anchors.left: contenu_bouton_groupe.left
leftPadding: 20
rightPadding: 20
text: model.nom
color: couleur_blanc_casse
font.pixelSize: 18
}
Drag.active: dragArea_groupe.bouton_appuye
Drag.source: dragArea_groupe
Drag.hotSpot.x: width / 2
Drag.hotSpot.y: height / 2
states: State{
when: dragArea_groupe.bouton_appuye
ParentChange {
target: contenu_bouton_groupe
parent: grille_groupes
}
AnchorChanges {
target: contenu_bouton_groupe
anchors { // Necessaire pour autoriser le mouvement
horizontalCenter: undefined
verticalCenter: undefined
}
}
}
}
DropArea {
anchors.fill: parent
onEntered: {
if (drag.source.DelegateModel.itemsIndex !== dragArea_groupe.DelegateModel.itemsIndex)
{
//console.log("\tMouvement de Source index = " + drag.source.DelegateModel.itemsIndex + " vers Dest index = " + dragArea.DelegateModel.itemsIndex)
modele_liste_groupes.move(drag.source.DelegateModel.itemsIndex, dragArea_groupe.DelegateModel.itemsIndex, 1)
}
}
}
}
}
footer: Rectangle { // Ce premier rectangle sert juste à créer un espace un peu plus grand pour le bouton '+' et le décaler de 'spacing' vers le bas...
width: grille_groupes.width
height: grille_groupes.height / nombre_groupes_affichees_sur_une_colonne
color: "transparent"
Rectangle { // c'est le bouton proprement dit...
id: bouton_footer_plus
width: parent.width - 10
height: parent.height - 10
x: 5
color: shipheart_couleur_fond_bouton
radius: shipheart_rayon_bouton
anchors.verticalCenter: parent.verticalCenter
Text {
anchors {
verticalCenter: parent.verticalCenter
horizontalCenter: parent.horizontalCenter
}
text: "+"
font.pixelSize: 40
font.bold: true
color: couleur_gris_moyen
}
MouseArea{
anchors.fill : parent
onClicked: {
shipheart_clic();
stackView.push("config_groupe_elements.qml", {"table": type_source_groupe, "id_groupe":0})
}
}
}
Rectangle { // Texte d'info en-dessous du '+'
width: parent.width - 10
height: parent.height - 10
x: 5
y: bouton_footer_plus.height
color: "transparent"
anchors.top: parent.bottom
anchors.topMargin: 30
Text {
anchors.top: parent.top
anchors.horizontalCenter: parent.horizontalCenter
horizontalAlignment: Text.AlignHCenter
font.pixelSize: 12
color: couleur_gris_moyen
text: qsTr("Pour déplacer un groupe, le maintenir appuyé puis le faire glisser.<br />Pour modifier ou supprimer un groupe, double-cliquer sur le groupe.<br />Pour ajouter un groupe, cliquer sur '+'.<br /><br />Note : si un seul groupe, aucun onglet n'est affiché sur la page concernée...")
}
}
} // Fin de footer
ListModel {
id: modele_liste_groupes
}
onCurrentIndexChanged: {
if (grille_groupes.deplacement_en_cours === false)
{
// grille_modules_physiques.positionViewAtIndex(currentIndex, ListView.Beginning); // Permet de faire défiler la liste des modules physiques "en face" des modules configurés. Mais uniquement en dehors de déplacements puisque, pendant les déplacements, on force CurrentIndex à rester fixe.
}
else
{
grille_groupes.currentIndex = grille_groupes.save_current_index; // Permet de compenser les changements de CurrentIndex 'imposés' par le ListView. Notamment lors des déplacements en début et fin de liste.
}
}
} // Fin de Listview des Groupes
// Texte si liste de groupes est vide :
Label {
anchors.fill: parent
horizontalAlignment: Qt.AlignHCenter
verticalAlignment: Qt.AlignVCenter
visible: parent.count === 0
text: texte_aucun_groupe
color: couleur_rouge_transparent
font.pixelSize: 15
font.bold: true
wrapMode: Label.Wrap
}
}
//---------------------------------------------------------------------
// Ligne verticale de séparation entre groupes et éléments du groupe :
Rectangle {
x: zone_affichage_groupes.width + (espace_zones/2)
y: 0
width: 3
height: affichage_hauteur_utile
color: couleur_gris_tres_pale_transparent
visible: flag_affichage_zone_groupe
}
//-------------------------------------------------
// Liste des éléments du groupe :
Rectangle {
id: zone_affichage_elements
x: flag_affichage_zone_groupe ? (zone_affichage_groupes.width + espace_zones) : 0
y: 0
width: flag_affichage_zone_groupe ? (shipheart_largeur_page * (1 - pourcentage_largeur_zone_groupes)) - (espace_zones / 2) : shipheart_largeur_page
height: affichage_hauteur_utile
color: "transparent"
GridView {
id: grille_elements
property int save_current_index: 0 // Permet de garder le Current_index du début du déplacement et de compenser les changements qui sont fait automatiquement, notamment en début et fin de liste. Le principe est qu'une fois le mouvement déclenché, ON NE CHANGE PAS de CurrentIndex !!
anchors.fill: parent
cellWidth: (width / nombre_colonnes_affichage)
cellHeight: parent.height / nombre_elements_affichees_sur_une_colonne
displaced: Transition { // Gère la transition lors d'un déplacement
NumberAnimation{
properties: "x,y" // Ici, on devrait pouvoir limiter à Y
duration: 500 // Vitesse à laquelle se déplacent les boutons lorsqu'on les déplace
}
}
model: modele_liste_elements
delegate: bouton_element
Component {
id: bouton_element
MouseArea {
id: dragArea_element // Zone correspondant à un seul bouton (on est dans le Delegate)
property bool bouton_appuye: false // On ne peut pas utiliser la propriété globale "deplacement_en_cours" car il faut une propriété par délégué
width: grille_elements.cellWidth
height: grille_elements.cellHeight
pressAndHoldInterval: 500; // Temporise avant de passer en mode "déplacement"
drag.target: bouton_appuye ? contenu_bouton_element : undefined
drag.axis: Drag.XandYAxis // On se déplace sur les 2 axes
onPressAndHold: {
bouton_appuye = true // Indication (au niveau du bouton seul) du début de l'appui long donc du déplacement
grille_elements.save_current_index = grille_elements.currentIndex; // Sauvegarde du CurrentIndex au début du déplacement
}
onReleased: {
bouton_appuye = false // Indication (au niveau du bouton seul) de la fin de l'appui long donc du déplacement
grille_elements.currentIndex = grille_elements.save_current_index; // Restitution de la valeur initiale du CurrentIndex (a priori, pas nécessaire car mis à jour au fur et à mesure des changements dans "onCurrentIndexChanged")
}
Rectangle {
id: contenu_bouton_element
anchors {
horizontalCenter: parent.horizontalCenter
verticalCenter: parent.verticalCenter
}
width: dragArea_element.width - 10
height: dragArea_element.height - 10
radius: shipheart_rayon_bouton
color: shipheart_couleur_fond_bouton
opacity: dragArea_element.bouton_appuye ? 0.6 : 1.0
Text {
anchors.verticalCenter: contenu_bouton_element.verticalCenter
anchors.left: contenu_bouton_element.left
leftPadding: 20
rightPadding: 20
text: model.nom
color: couleur_blanc_casse
font.pixelSize: 18
}
Drag.active: dragArea_element.bouton_appuye
Drag.source: dragArea_element
Drag.hotSpot.x: width / 2
Drag.hotSpot.y: height / 2
states: State{
when: dragArea_element.bouton_appuye
ParentChange {
target: contenu_bouton_element
parent: grille_elements
}
AnchorChanges {
target: contenu_bouton_element
anchors { // Necessaire pour autoriser le mouvement
horizontalCenter: undefined
verticalCenter: undefined
}
}
}
}
DropArea {
anchors.fill: parent
onEntered: {
if (drag.source.DelegateModel.itemsIndex !== dragArea_element.DelegateModel.itemsIndex)
{
//console.log("\tMouvement de Source index = " + drag.source.DelegateModel.itemsIndex + " vers Dest index = " + dragArea_element.DelegateModel.itemsIndex)
modele_liste_elements.move(drag.source.DelegateModel.itemsIndex, dragArea_element.DelegateModel.itemsIndex, 1)
}
}
}
}
}
ListModel {
id: modele_liste_elements
}
onCurrentIndexChanged: {
grille_elements.currentIndex = grille_elements.save_current_index; // Permet de compenser les changements de CurrentIndex 'imposés' par le ListView. Notamment lors des déplacements en début et fin de liste.
}
} // Fin de Gridview
// Texte si groupe vide :
Label {
anchors.fill: parent
horizontalAlignment: Qt.AlignHCenter
verticalAlignment: Qt.AlignVCenter
visible: modele_liste_elements.count === 0
text: texte_aucun_element
color: couleur_rouge_transparent
font.pixelSize: 15
font.bold: true
wrapMode: Label.Wrap
}
} // Fin de zone affichage elements
} // Fin de Ecran Principal
//-------------------------------------------------------
// 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();
mise_a_jour_ordre();
stackView.pop()
}
}
}
}
// 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;
//console.log("Table = "+ table);
// Paramètres par défaut :
flag_affichage_zone_groupe = false;
nombre_colonnes_affichage = 1;
nombre_elements_affichees_sur_une_colonne = 8;
espace_lignes_affichage = 8;
espace_colonnes_affichage = 300;
type_source_groupe = shipheart_type_source_inconnu;
texte_aucun_groupe = qsTr("Aucun groupe n'est actuellement configuré...");
texte_aucun_element = qsTr("Aucun élément n'est actuellement configuré...");
// Paramètres spécifiques :
switch(table)
{
//----------------------------------------------------------------------------------
case "TB_Batteries":
nombre_colonnes_affichage = 1;
espace_colonnes_affichage = 20;
texte_aucun_groupe = qsTr("Aucun groupe de batteries n'est actuellement configuré...");
texte_aucun_element = qsTr("Aucune batterie n'est actuellement affectée à ce groupe...");
texte_titre = qsTr("Affichage des batteries sur la page d'accueil");
type_source_groupe = shipheart_type_source_batteries;
break;
//----------------------------------------------------------------------------------
case "TB_Capteurs":
nombre_colonnes_affichage = 3;
espace_colonnes_affichage = 20;
texte_aucun_groupe = qsTr("Aucun groupe de capteurs n'est actuellement configuré...");
texte_aucun_element = qsTr("Aucun capteur n'est actuellement affecté ce groupe...");
texte_titre = qsTr("Affichage des capteurs sur la page d'accueil");
type_source_groupe = shipheart_type_source_capteurs;
break;
//----------------------------------------------------------------------------------
case "TB_Circuits":
nombre_colonnes_affichage = 2;
espace_colonnes_affichage = 80;
texte_aucun_groupe = qsTr("Aucun groupe de circuits n'est actuellement configuré...");
texte_aucun_element = qsTr("Aucun circuit n'est actuellement affecté à ce groupe...");
texte_titre = qsTr("Affichage des circuits sur la page d'accueil");
type_source_groupe = shipheart_type_source_circuits;
break;
//----------------------------------------------------------------------------------
case "TB_Reservoirs":
nombre_colonnes_affichage = 1;
nombre_elements_affichees_sur_une_colonne = 8;
espace_colonnes_affichage = 20;
texte_aucun_groupe = qsTr("Aucun groupe de réservoirs n'est actuellement configuré...");
texte_aucun_element = qsTr("Aucun réservoir n'est actuellement affecté à ce groupe...");
texte_titre = qsTr("Affichage des réservoirs sur la page d'accueil");
type_source_groupe = shipheart_type_source_reservoirs;
break;
//----------------------------------------------------------------------------------
default:
}
// Traitement spécifique quand on traite les groupes :
if (type_source_groupe !== shipheart_type_source_inconnu)
{
flag_affichage_zone_groupe = true; // Plus facile pour gérer l'affichage
mise_a_jour_liste_groupes(); // Après la rcherche du premier groupe sinon il n'est pas sélectionné...
}
mise_a_jour_liste_elements_groupe(id_groupe);
}
//=============================================================================================================
// Mise à jour des groupes
//=============================================================================================================
function mise_a_jour_liste_groupes()
{
var req, res, nb;
var i;
var actif;
//console.log("Table groupe = " +table_groupe);
modele_liste_groupes.clear();
// Vérif que l'id froupe existe bien :
req = 'SELECT Cle_primaire FROM TB_Familles WHERE Type_source=' + type_source_groupe + ' AND Cle_primaire=' + id_groupe + ' ';
//console.log("Requête = [ " + req + "; ]")
nb = bdd.requete(req);
if (nb === 0) id_groupe = 0; // Sinon, on positionne l'ID à 0 pour que la première valeur soit enregistrée dans la boucle suivante.
// Recherche des groupes :
if (type_source_groupe !== shipheart_type_source_inconnu)
{
req = 'SELECT Nom, Cle_primaire, Priorite_affichage FROM TB_Familles WHERE Type_source=' + type_source_groupe + ' ORDER BY Priorite_affichage';
//console.log("Requête = [ " + req + "; ]")
nb = bdd.requete(req);
for (i = 0; i < nb; i ++)
{
res = bdd.lecture_ligne();
if (!res.isEmpty)
{
if (id_groupe === 0) id_groupe = Number(res[1]); // Utile en retour de suppression d'un groupe qui n'est plus disponible => on prend le premier
if (id_groupe === Number(res[1])) actif = true;
else actif = false;
modele_liste_groupes.append({"nom": res[0], "id": Number(res[1]), "actif": actif});
}
}
}
}
//=============================================================================================================
// Mise à jour des éléments d'un groupe
//=============================================================================================================
function mise_a_jour_liste_elements_groupe(id_groupe)
{
var req, res, nb;
var i;
var filtre;
modele_liste_elements.clear();
// Filtre de sélection en fonction du fait qu'on gère les groupes ou pas :
if (type_source_groupe !== shipheart_type_source_inconnu) filtre = ' AND ID_Famille=' + id_groupe + ' ';
else filtre = "";
// Récupération des données correspondant au type dans la BDD + mise à jour du modèle :
req = 'SELECT Nom, Cle_primaire FROM TB_Familles WHERE Type_source=' + type_source_groupe + ' ' + filtre + ' AND Priorite_affichage_accueil>0 ORDER BY Priorite_affichage_accueil';
//console.log("Requête = [ " + req + "; ]")
nb = bdd.requete(req);
for (i = 0; i < nb; i ++)
{
res = bdd.lecture_ligne();
if (!res.isEmpty)
{
modele_liste_elements.append({"nom": res[0], "id_element": Number(res[1])});
}
}
}
//=============================================================================================================
// Enregistrement des modifications dans la BDD
//=============================================================================================================
function mise_a_jour_ordre()
{
var req, res;
var i;
var nb;
//------------------------------------------------------------
// Mise à jour de l'ordre d'affichage des familles (groupes) :
if (type_source_groupe !== shipheart_type_source_inconnu)
{
for(i = 0; i < modele_liste_groupes.count; i++)
{
if (modele_liste_groupes.get(i).id === 0) continue; // C'est le groupe par défaut qui n'est pas stocké dans la BDD...
req = 'UPDATE TB_Familles SET Priorite_affichage=' + (i+1) + ' WHERE Type_source=' + type_source_groupe + ' AND Cle_primaire=' + modele_liste_groupes.get(i).id + ' ';
//console.log("Requête = " + req)
bdd.requete_unique(req);
}
}
//--------------------------------------------------------------------------
// Mise à jour de l'ordre des éléments dans le groupe actuellement affiché :
for(i = 0; i < modele_liste_elements.count; i++)
{
// Important : il faut bien commencer la numérotation à 1 car si 0 (ou négatif), l'élément est considéré comme masqué sur la page d'accueil (il faut alors le démasquer dans sa page de config).
req = 'UPDATE ' + table + ' SET Priorite_affichage_accueil=' + (i+1) + ' WHERE Cle_primaire=' + modele_liste_elements.get(i).id_element + ' ';
//console.log("Requête = [ " + req + "; ]")
bdd.requete_unique(req);
}
// NB : il n'est pas utile de demander une synchro (2) car les éléments modifiés ne concernent que des sujets d'affichage qui n'influent en rien le fonctionnement en dehors de QT.
}
}
+686
View File
@@ -0,0 +1,686 @@
//========================================================================================================================================================================
// CONFIG_PAGE.QML
// Ce fichier traite la configuration d'une page. La page permet notamment de :
// - Modifier la config de la page (ou de le créer la première fois),
// - Supprimer la page
// La page est appelée depuis la page config_pages.qml, en cliquant sur la page à modifier (ou sur le '+' en vue d'une création)
//========================================================================================================================================================================
import QtQuick 2.12
import QtQuick.Controls 2.12
import QtQml.Models
import qt.koriolan.shipheart 1.0
Shipheart_page {
id: page_config_page
// Mis en forme :
readonly property double pourcentage_largeur_colonne_gauche: 0.75
readonly property int espace_colonnes_principales: 20
readonly property double pourcentage_largeur_labels_formulaire: 0.5
readonly property double pourcentage_largeur_unites_formulaire: 0.1
readonly property double pourcentage_hauteur_ligne_haut: 1
readonly property int affichage_hauteur_utile: shipheart_hauteur_page - shipheart_hauteur_bandeau_haut - shipheart_hauteur_bandeau_bas
readonly property int espace_lignes_principales: 20
readonly property int hauteur_ligne_formulaire: 60
readonly property int hauteur_bouton_droit: 80
readonly property double pourcentage_bandeau_bas_largeur_zone_retour: 1.0
// Timings :
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 bool flag_init: true // Permet de bloquer certains affichage tant que l'init n'est pas terminée
// Données :
property int valeur_timer_appui_long: 0
property int id_page: 0
property string fichier: ""
property int opacite: 0
// Modèles :
// Note : les modèles chargés dans 'main.qml' n'ont pas à être rechargés dans les pages suivantes
//-------------------------------------------------
// Signaux :
//-------------------------------------------------
signal enregistrer_config_page(int new_id_page) // Valeurs à lire lorsque le "OnEnregistrer' est activé
//===========================================================
// 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;
}
else {
}
}
}
//===========================================================
// 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 la page :
if (mousearea_bouton_supprimer.pressed)
{
if (check_avant_suppression() !== -1)
{
shipheart_clic();
suppression_page(); // Mise à jour des données saisies dans la BDD et reconfiguration par l'application pour prise en compte.
timer_tempo_reconfiguration.restart(); // Lancement du timer pour attendre la fin de la reconfiguration
}
}
}
}
}
//===========================================================
// Lorsque la page est sur le point d'être affichée dans la pile :
//===========================================================
StackView.onActivating: {
init_page();
}
// Titre de la page :
Shipheart_titre_1 {titre: (id_page === 0) ? qsTr("Création d'une page") : qsTr("Configuration de la page"); 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);
color: "transparent"
//=========================
// Liste des champs :
//=========================
ListView {
id: grille_principale
width: parent.width
height: parent.height
snapMode: GridView.SnapToRow // L'arret de l'affichage se fera sur une rangée entière
clip: true // Nécessaire pour que les Delegates s'arretent à l'intérieur de la GridView
model: modele_grille_principale
footer: Rectangle { // Permet de dégager la place suffisante en fin de GridView pour accéder même au dernier élément lorsque que clavier est déplié
height: shipheart_hauteur_clavier
color: "transparent"
}
ObjectModel {
id: modele_grille_principale
//-------------------------------------------------------------------------------
// Ligne de formulaire : Nom
Rectangle {
id: ligne_nom
width: (shipheart_largeur_page * pourcentage_largeur_colonne_gauche) - (espace_colonnes_principales / 2)
height: visible ? hauteur_ligne_formulaire : 0
color: "transparent"
Shipheart_formulaire_label {
text: qsTr("Nom de la page")
width: parent.width * pourcentage_largeur_labels_formulaire
couleur: (champ_formulaire_Nom.text !== "")
? shipheart_formulaire_couleur_texte_par_defaut
: shipheart_formulaire_couleur_texte_en_erreur
}
Shipheart_formulaire_textfield {
id: champ_formulaire_Nom
width: parent.width * (1 - pourcentage_largeur_labels_formulaire)
x: parent.width * pourcentage_largeur_labels_formulaire
}
Shipheart_formulaire_ligne_separation {
haut_bas_hautbas: shipheart_formulaire_ligne_separation_basse
}
}
//-------------------------------------------------------------------------------
// Ligne de formulaire : Opacité
Rectangle {
id: ligne_opacite
width: (shipheart_largeur_page * pourcentage_largeur_colonne_gauche) - (espace_colonnes_principales / 2)
height: visible ? hauteur_ligne_formulaire : 0
color: "transparent"
visible: (!flag_init)
// && (champ_formulaire_Nom.text !== "")
Shipheart_formulaire_label {
text: qsTr("Opacité de la silhouette")
width: parent.width * pourcentage_largeur_labels_formulaire
couleur: ((opacite > 0) && (opacite <= 100))
? shipheart_formulaire_couleur_texte_par_defaut
: shipheart_formulaire_couleur_texte_en_erreur
}
Shipheart_formulaire_combobox {
id: champ_formulaire_Opacite
model: ListModel { }
width: parent.width * (1 - pourcentage_largeur_labels_formulaire)
onActivated: {
shipheart_clic();
opacite = champ_formulaire_Opacite.model.get(champ_formulaire_Opacite.currentIndex).valeur;
}
}
Shipheart_formulaire_ligne_separation {
haut_bas_hautbas: shipheart_formulaire_ligne_separation_basse
}
}
//-------------------------------------------------------------------------------
// Ligne de formulaire : Fichier
Rectangle {
id: ligne_fichier
width: (shipheart_largeur_page * pourcentage_largeur_colonne_gauche) - (espace_colonnes_principales / 2)
height: visible ? hauteur_ligne_formulaire : 0
color: "transparent"
visible: ligne_opacite.visible
Shipheart_formulaire_label { // Label :
text: qsTr("Fichier de silhouette")
width: parent.width * pourcentage_largeur_labels_formulaire
couleur: shipheart_formulaire_couleur_texte_par_defaut
}
Shipheart_formulaire_valeur_fixe {
id: champ_formulaire_Fichier
width: parent.width * (1 - pourcentage_largeur_labels_formulaire)
x: parent.width * pourcentage_largeur_labels_formulaire
}
Shipheart_formulaire_ligne_separation {
haut_bas_hautbas: shipheart_formulaire_ligne_separation_basse
}
}
//---------------------------------------
// Miniature :
Rectangle {
width: parent.width * (1 - pourcentage_largeur_labels_formulaire)
height: (6 * hauteur_ligne_formulaire)
x: parent.width * pourcentage_largeur_labels_formulaire
color: "transparent"
visible: !flag_init
Image {
source: (fichier !== "")
? "file://" + shipheart_repertoire_config + shipheart_sous_repertoire_images_silhouette + "/" + fichier
: ""
anchors.fill: parent
fillMode: Image.PreserveAspectFit
anchors {
verticalCenter: parent.verticalCenter
horizontalCenter: parent.horizontalCenter
}
opacity: (opacite / 100)
}
}
}
}
}
//-------------------------------------------------------
// 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 * pourcentage_hauteur_ligne_haut) - (espace_lignes_principales / 2);
color: "transparent"
Column {
anchors.fill: parent
spacing: 10
//-------------------------------------------------------
// Bouton Enregistrer/créer :
//-------------------------------------------------------
Rectangle {
id: bouton_enregistrer_creer
width: parent.width
height: hauteur_bouton_droit
color: shipheart_couleur_fond_bouton
radius: shipheart_rayon_bouton
visible:
// !clavier.active && // Si le clavier est déplié, // SDIR #255
(champ_formulaire_Nom.text !== "") // ... ou si le nom n'est pas vide,
&& (opacite > 0) // ... ou si l'opacité est > 0%,
&& (opacite <= 100) // ... ou si l'opacité est <= 100%,
Text {
anchors {
verticalCenter: parent.verticalCenter
horizontalCenter: parent.horizontalCenter
}
font.pixelSize: 20
text: (id_page === 0) ? qsTr("Créer la page") : qsTr("Enregistrer")
color: couleur_blanc_casse
}
MouseArea {
anchors.fill: parent
onClicked: {
shipheart_clic();
if (id_page !== 0) mise_a_jour_page(); // Mise à jour des données saisies dans la BDD et reconfiguration par l'application pour prise en compte.
else creation_page();
timer_tempo_reconfiguration.restart();
}
}
}
//-------------------------------------------------------
// Bouton d'accès à l'éditeur
// NB : On donne l'accès à l'édition même si la page n'est pas encore créé MAIS que toutes les conditions de création sont réunies.
// Dans ce cas, on effectue la création automatiquement avant de donner l'accès à l'édition.
//-------------------------------------------------------
Rectangle {
width: parent.width
height: hauteur_bouton_droit
color: shipheart_couleur_fond_bouton
radius: shipheart_rayon_bouton
visible: bouton_enregistrer_creer.visible
Text {
anchors {
verticalCenter: parent.verticalCenter
horizontalCenter: parent.horizontalCenter
}
font.pixelSize: 20
text: qsTr("Silhouette")
color: couleur_blanc_casse
}
MouseArea {
anchors.fill: parent
onClicked: {
shipheart_clic();
if (id_page === 0) creation_page(); // Si la page n'a pas encore été créé et qu'on demande l'accès à la silhouette, on commence par créer la page
stackView.push("config_fichiers.qml", {"mode_config_fichiers": shipheart_mode_config_fichiers_silhouette,"id_page": id_page})
}
}
}
//-------------------------------------------------------
// Bouton de suppression
//-------------------------------------------------------
Rectangle {
width: parent.width
height: hauteur_bouton_droit
color: shipheart_couleur_fond_bouton
radius: shipheart_rayon_bouton
visible: bouton_enregistrer_creer.visible && (id_page !== 0)
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_supprimer.pressed
}
Text {
anchors {
verticalCenter: parent.verticalCenter
horizontalCenter: parent.horizontalCenter
}
font.pixelSize: 20
text: qsTr("Supprimer")
color: couleur_blanc_casse
}
MouseArea {
id: mousearea_bouton_supprimer
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;
}
}
}
//-------------------------------------------------------
// Timer de reconfiguration :
//-------------------------------------------------------
Timer {
id: timer_tempo_reconfiguration
interval: 250 // Cette tempo n'est utilisée que pour le premier cycle
triggeredOnStart: false
running: false
repeat: true
onTriggered: {
if (bdd.check_fin_configuration())
{
timer_tempo_reconfiguration.stop();
if (id_page !== 0) {
page_config_page.enregistrer_config_page(id_page); // Renvoi la nouvelle valeur de 'id_page' si elle a été modifiée (création ou modification)
stackView.pop(); // Remonte d'une vue, classique.
}
else stackView.pop(stackView.get(stackView.depth - 3)); // Remonte de 2 vues, jusqu'à la 'config_pages' car la création a été annulée ou la page supprimée
}
else timer_tempo_reconfiguration.interval = 100 // Tempo plus courte pour les lectures suivantes
}
}
}
}
//-------------------------------------------------------
// 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 * pourcentage_bandeau_bas_largeur_zone_retour) - parent.leftPadding - (parent.spacing / 2)
height: shipheart_hauteur_bandeau_bas - parent.topPadding - parent.bottomPadding;
radius: shipheart_rayon_bouton
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 (id_page !== 0) stackView.pop(); // Remonte d'une vue, classique.
else stackView.pop(stackView.get(stackView.depth - 3)); // Remonte de 2 vues, jusqu'à la 'config_pages' car la création a été annulée
}
}
}
}
//-----------------------------------------------espace_lignes_principales--------
// Boite d'info :
//-------------------------------------------------------
Shipheart_boite_dialogue {
id: boite_information
}
//===========================================================
// Busy Indicator :
//===========================================================
Shipheart_busy_indicator {
id: busy_indicator
running: timer_tempo_reconfiguration.running
}
// 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 liste_valeurs_textes;
//console.log("Id de la page = " + id_page);
//---------------------------------------------------------------------------------------
// Valeurs par défaut :
champ_formulaire_Nom.text = "";
fichier = "";
champ_formulaire_Fichier.text = qsTr("Aucun");
opacite = 50;
//---------------------------------------------------------------------------------------
// Caractéristiques de la page en cours :
if (id_page > 0)
{
req = 'SELECT Nom, ';
// req += ' Type, ';
req += ' Fichier, ';
req += ' Opacite, ';
req += ' Taille, ';
req += ' Offset_x, ';
req += ' Offset_y ';
req += ' FROM TB_Pages ';
req += ' WHERE Cle_primaire=' + id_page + ' ';
//console.log("Requête = [" + req + "]")
res = bdd.requete_unique(req);
// Récupération des données :
i = 0;
champ_formulaire_Nom.text = res[i ++];
// i ++; // Type
fichier = res[i ++];
opacite = Number(res[i ++]);
}
//---------------------------------------------------------------------------------------
// Fichier :
if (fichier === "") champ_formulaire_Fichier.text = qsTr("Aucun");
else champ_formulaire_Fichier.text = fichier;
//---------------------------------------------------------------------------------------
// Opacité :
champ_formulaire_Opacite.model.clear();
for (i = 1; i <= 20; i ++)
{
champ_formulaire_Opacite.model.append({"valeur": (5*i), "texte": ((5*i) + " %") });
}
// Positionnement de l'index sur la valeur en cours :
i = recherche_index_valeur_modele(opacite, champ_formulaire_Opacite.model, "Opacité");
if (i === -1) champ_formulaire_Opacite.currentIndex = 10; // Soit la valeur 50%
else champ_formulaire_Opacite.currentIndex = i;
//---------------------------------------------------------------------------------------
// Fin d'init :
flag_init = false; // Indique que l'init est terminée
}
//=============================================================================================================
// Mise à jour des valeurs saisies de la page
//=============================================================================================================
function mise_a_jour_page()
{
var req, res;
var nom;
nom = champ_formulaire_Nom.text;
// Requete de mise à jour :
req = 'UPDATE TB_Pages SET Nom="' + nom + '" ';
req += ', Opacite=' + opacite + ' ';
req += ' WHERE Cle_primaire=' + id_page + ' '
//console.log("Requête = [" + req + "]")
bdd.requete_unique(req);
}
//=============================================================================================================
// Création de la page
//=============================================================================================================
function creation_page()
{
var req, res;
var nom;
nom = champ_formulaire_Nom.text;
// Requete de création :
req = 'INSERT INTO TB_Pages (Nom';
req += ', Type';
req += ', Opacite';
req += ') ';
req += ' VALUES ("' + nom + '"';
req += ', ' + shipheart_type_page_personnalisee_masquable;
req += ', ' + opacite;
req += ') ';
//console.log("Requête = [" + req + "]")
bdd.requete_unique(req);
// Recherche de l'ID_Page correspondant à la page créée :
req = ' SELECT Cle_primaire FROM TB_Pages ORDER BY Cle_primaire DESC LIMIT 1 ';
//console.log("Requête = [" + req + "]")
res = bdd.requete_unique(req);
id_page = Number(res[0]);
}
//=============================================================================================================
// Check suppression du capteur
//=============================================================================================================
function check_avant_suppression()
{
var message = "";
//=========================================================================
// Recherche de l'impact de cette suppression sur les spécifiques clients :
switch(Number(shipheart_specifique_client[0]))
{
//------------------------------------------------------
default:
case shipheart_specifique_client_tous:
//console.log("Tous les clients")
break;
//------------------------------------------------------
case shipheart_specifique_client_suncy:
//console.log("Client Suncy")
break;
}
//=========================================================================
// Recherche de l'impact de cette suppression en général :
//=========================================================================
// Conclusion :
if (message === "") return(0); // Aucune réserve à supprimer le message....
else // Affichage de la boite de dialogue. La suppression n'est pas autorisée...
{
boite_information.niveau = 2;
boite_information.imageTitreSource = "/images/attention.png"
boite_information.message = message;
boite_information.alignementMessage = Text.AlignLeft
boite_information.open();
return(-1);
}
}
//=============================================================================================================
// Suppression de la page
//=============================================================================================================
function suppression_page()
{
var req;
// Suppression de la page :
req = 'DELETE FROM TB_Pages WHERE Cle_primaire=' + id_page + ' ';
bdd.requete_unique(req);
id_page = 0;
}
//=============================================================================================================
// Recherche de l'index d'une certaine valeur dans un modele
//=============================================================================================================
function recherche_index_valeur_modele(cible, modele, nom_modele)
{
var i;
for (i=0; i<modele.count; i ++)
{
if (modele.get(i).valeur === cible) break; // Cas où la valeur cible a été trouvée parmi les valeurs du modèle
}
if (i === modele.count)
{
console.log("Aucune valeur trouvée pour cible = [" + cible + "] du modèle = " + nom_modele);
i = -1; // Cas où la valeur n'a pas été trouvée parmi les valeurs du modèle
}
//else console.log("Valeur trouvée : [" + i + "] pour cible = [" + cible + "] soit texte = [" + modele.get(i).texte + "]");
return(i);
}
}
+856
View File
@@ -0,0 +1,856 @@
//========================================================================================================================================================================
// CONFIG_PAGE_EDITION.QML
// Ce fichier traite la configuration d'une page personnalisée.
// La page est appelée depuis la page CONFIG_PAGE, en cliquant sur le bouton "Edition"
//========================================================================================================================================================================
import QtQuick 2.12
import QtQuick.Controls 2.12
import QtQml.Models 2.1
import qt.koriolan.shipheart 1.0
import"fonctions.js" as Fonctions
Shipheart_page {
id: page_config_page_edition
// Mis en forme :
readonly property double pourcentage_largeur_colonne_gauche: 0.20
readonly property double pourcentage_bandeau_bas_largeur_zone_retour: 1.0
readonly property double espace_colonnes_principales: 20
readonly property double pourcentage_hauteur_ligne_haut: 0.75
readonly property double affichage_hauteur_utile: shipheart_hauteur_page - shipheart_hauteur_bandeau_haut - shipheart_hauteur_bandeau_bas
readonly property double espace_lignes_principales: 20
readonly property int nombre_lignes_points_visibles: 6
readonly property int diametre_points_deplacables: 100
readonly property int periode_init_raffraichissement_parametres: 1 // En ms
readonly property int periode_raffraichissement_parametres: 1000 // En ms
// Affichage :
property bool flag_aucun_points: 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_affichage_ajout_point: true
// Données :
property int id_page: 0
property string nom_page: ""
property string fichier: ""
property int opacite: 0
property int taille: 0
property int offset_x: 0
property int offset_y: 0
property string chaine_points: ""
property int nb_points: 0
property int index_point_en_cours: -1
property double ecart_maxi_detectable: 0.0001
// Modèles :
// Note : les modèles chargés dans 'main.qml' n'ont pas à être rechargés dans les pages suivantes
//===========================================================
// Signaux :
//===========================================================
//----------------------------------------------------------------------------------
// Connexion au signaux de pages créées depuis celle-ci :
Connections {
target: stackView.currentItem // Pointe sur la page active de StackView (qui sera créée... bientôt, ou pas)
ignoreUnknownSignals: true // Permet de ne pas déclencher d'erreur AVANT que la page ne soit créée par un StackView.push()
// ----------------------------------------------------------------------------
// Fonction d'enregistrement des nouvelles data provenant de 'config_page' :
// ----------------------------------------------------------------------------
function onEnregistrer_config_page(new_id_page)
{
//console.log("Mise à jour de l'ID de la page : new_id_page = "+new_id_page);
id_page = new_id_page;
}
// ---------------------------------------------------------------------------------
// Fonction d'enregistrement des nouvelles data provenant de 'config_page_point' :
// ---------------------------------------------------------------------------------
function onEnregistrer_config_page_point(new_flag_point_existant, new_type_source, new_id_source, new_type_point, new_taille_point)
{
//console.log("index_point_en_cours = " + index_point_en_cours + " | Mise à jour des nouvelles valeur : new_flag_point_existant = " + new_flag_point_existant + ", new_type_source = " + new_type_source + ", new_id_source = " + new_id_source + ", new_type_point = " + new_type_point + ", new_taille_point = " + new_taille_point);
//----------------------------------------
// Cas d'une mise à jour :
if (index_point_en_cours !== -1) // Le point existait avant l'appel :
{
if (new_flag_point_existant) // Il existe toujours = mise à jour des données du point sélectionné :
{
modele_liste_points.set(index_point_en_cours, {
"type_source": new_type_source,
"id_source": new_id_source,
"type_point": new_type_point,
"taille_point": new_taille_point } );
}
else // Il n'existe plus = suppression du point :
{
modele_liste_points.remove(index_point_en_cours, 1);
index_point_en_cours = -1;
}
}
//----------------------------------------
// Cas de l'ajout d'un point :
else // Le point n'existait pas avant l'appel :
{
if (new_flag_point_existant) // Il existe maintenant = insertion du nouveau point :
{
modele_liste_points.append({
"validite": 1,
"type_source": new_type_source,
"id_source": new_id_source,
"x": -0.5, // Insertion sur le bord gauche...
"y": -0.5, // .... et en haut de la page pour être visible
"x_initial": -0.5,
"y_initial": -0.5,
"type_point": new_type_point,
"taille_point": new_taille_point,
"actif": false,
"changed": true });
flag_alignement_demande = true // Permet de déclencher un traitement au niveau 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.
}
else ; // Il n'existe toujours pas (création abandonnée)
}
enregistrer_points(); // Mémorise dans la BDD les éventuels changements faits dans 'config_page_point'. Obligatoire car, ensuite, une 'init_page()' sera fait et rechargera les points depuis la BDD... (sans ça, les modifs faites sont perdues !)
}
}
//===========================================================
// 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;
}
else
{
// Recherche s'il y a au moins un point qui a été modifié :
let flag = false;
for(let i = 0; i < modele_liste_points.count; i++) {
if (modele_liste_points.get(i).changed) { flag = true; break; }
}
flag_nouvelles_valeurs = flag;
}
}
}
//===========================================================
// Lorsque la page est sur le point d'être affichée dans la pile :
//===========================================================
StackView.onActivating: {
if (id_page !== 0) init_page(); // Permet que lors du retour sur la page, l'état des différentes variables soit mise à jour avant l'affichage proprement dit. Y compris lors de l'ouverture initiale...
}
//===========================================================
// Lorsque la page est affichée dans la pile :
//===========================================================
StackView.onActivated: {
if (id_page === 0) init_page(); // Permet, lorsque l'ouverture est terminée, de rediriger si besoin vers una autre page ('config_page' lors de la création)
}
// Titre de la page :
Shipheart_titre_1 {titre: qsTr("Edition de la page [%1]").arg(nom_page); 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_liste_points
}
ListView {
id: liste_points
anchors.fill: parent
model: modele_liste_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
//--------------------------------------------------------------------
// N° du point :
Text {
id: no_point // Premier élément du bouton : n° de ligne
width: parent.width * 0.1;
anchors.verticalCenter: parent.verticalCenter
horizontalAlignment: Text.AlignRight
rightPadding: 5
text: index + 1
font.pixelSize: 18
font.bold: true
color: couleur_blanc_casse
}
//--------------------------------------------------------------------
// Icone du point :
Rectangle { // Le passage par un rectangle avant l'image permet de centrer l'image sur le rectangle et pas juste caler les images à gaiche (elles n'ont pas la même largeur donc elles n'étaient pas centrées les unes par rapport aux autres)
id: icone_point
width: parent.width * 0.1;
height: parent.height
x: no_point.width
color: "transparent"
Image {
source: "/images/" + model.icone
sourceSize.height: parent.height * 0.4
sourceSize.width: parent.width
anchors {
verticalCenter: parent.verticalCenter
horizontalCenter: parent.horizontalCenter
}
visible: (model.icone !== "")
}
}
//--------------------------------------------------------------------
// Nom du point :
Text {
id: nom_point
x: no_point.width + icone_point.width;
width: parent.width - no_point.width - icone_point.width;
anchors.verticalCenter: parent.verticalCenter
horizontalAlignment: Text.AlignLeft
leftPadding: 5
text: model.nom
font.pixelSize: 15
color: couleur_blanc_casse
}
//--------------------------------------------------------------------
// Triangle orange de modification :
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
}
//--------------------------------------------------------------------
MouseArea{
anchors.fill : parent
onClicked: {
shipheart_clic();
index_point_en_cours = index;
stackView.push("config_page_point.qml", {
"flag_point_existant" : true,
"type_source": model.type_source,
"id_source": model.id_source,
"type_point": model.type_point,
"taille_point": model.taille_point })
}
pressAndHoldInterval: shipheart_mouse_press_and_hold_interval_long;
onPressAndHold: {
shipheart_clic();
// Appui long = Remise en position initiale du point :
modele_liste_points.setProperty(index, "x", model.x_initial) // Recopie des coordonnées originelles dans les coordonnées actuelles
modele_liste_points.setProperty(index, "y", model.y_initial)
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.
}
}
}
footer: Rectangle { // Ce premier rectangle sert juste à créer un espace un peu plu sgrand pour le bouton '+' et le décaler de 'spacing' vers le bas...
height: ((liste_points.height - ((nombre_lignes_points_visibles - 1) * liste_points.spacing)) / nombre_lignes_points_visibles) + liste_points.spacing
width:parent.width
color: "transparent"
visible: flag_affichage_ajout_point
Rectangle { // c'est le bouton proprement dit...
height: ((liste_points.height - ((nombre_lignes_points_visibles - 1) * liste_points.spacing)) / nombre_lignes_points_visibles)
width:parent.width
color: shipheart_couleur_fond_bouton
radius: shipheart_rayon_bouton
anchors.bottom: parent.bottom
Text {
anchors {
verticalCenter: parent.verticalCenter
horizontalCenter: parent.horizontalCenter
}
text: "+"
font.pixelSize: 40
font.bold: true
color: couleur_gris_moyen
}
MouseArea{
anchors.fill : parent
onClicked: {
shipheart_clic();
index_point_en_cours = -1;
stackView.push("config_page_point.qml", {"flag_point_existant" : false, "type_source": 0, "id_source": 0, "type_point": 0 })
}
onPressed: shipheart_clic();
}
}
}
Label {
anchors.fill: parent
horizontalAlignment: Qt.AlignHCenter
verticalAlignment: Qt.AlignVCenter
visible: parent.count === 0
text: qsTr("Aucun équipement n'est configuré sur cette page pour le moment...")
color: couleur_rouge_transparent
font.pixelSize: 15
font.bold: true
wrapMode: Label.Wrap
}
}
}
//-------------------------------------------------------
// 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
//-----------------------------
// Config de la page :
//-----------------------------
Rectangle {
width: parent.width
height: (parent.height/2) - (parent.spacing/2)
color: shipheart_couleur_fond_bouton
radius: shipheart_rayon_bouton
Text {
anchors {
verticalCenter: parent.verticalCenter
horizontalCenter: parent.horizontalCenter
}
font.pixelSize: 20
text: qsTr("Page")
color: couleur_blanc_casse
}
MouseArea {
anchors.fill: parent
onClicked: {
shipheart_clic();
stackView.push("config_page.qml", {"id_page": id_page})
}
}
}
//-----------------------------
// Bouton Enregistrer/créer :
//-----------------------------
Rectangle {
width: parent.width
height: (parent.height/2) - (parent.spacing/2)
color: shipheart_couleur_fond_bouton
radius: shipheart_rayon_bouton
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 {
anchors.fill: parent
onClicked: {
shipheart_clic();
if (flag_nouvelles_valeurs) enregistrer_points();
}
}
}
}
}
//-------------------------------------------------------
// Ecran Droite :
//-------------------------------------------------------
Rectangle {
id: zone_affichage_droite
x: zone_affichage_haut_gauche.width + espace_colonnes_principales
y: shipheart_hauteur_bandeau_haut + ((affichage_hauteur_utile - height) / 2);
width: (shipheart_largeur_page * (1 - pourcentage_largeur_colonne_gauche)) - (espace_colonnes_principales / 2)
height: (affichage_hauteur_utile - espace_lignes_principales) * (zone_affichage_droite.width / shipheart_largeur_page) // Permet d'avoir le même ratio que lors de l'affichage dans 'page_personnalisee.qml'
color: "transparent"
border.color: couleur_gris_moyen
//==========================
// Image de la silhouette :
Image {
id: image_silhouette
anchors.fill: parent
source: (fichier !== "")
? "file://" + shipheart_repertoire_config + shipheart_sous_repertoire_images_silhouette + "/" + fichier
: ""
fillMode: Image.PreserveAspectFit
opacity: (opacite / 100)
visible: (fichier !== "")
}
//==========================
// Points :
Repeater {
model: modele_liste_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 (par exemple, dans le menu de gauche, la remise aux conditions initiales d'un point) et qu'il faut aligner le rectangle/cerlce sur ces nouvelles coordonnées.
radius: diametre_points_deplacables / 2 // Défini dans l'entete
width: diametre_points_deplacables // Rectangle => devient cercle
height: width
color: couleur_bleu_koriolan_tres_transparent
// Principe de coordonnées X/Y d'un point :
// Les valeurs X et Y stockées dans la chaine Points dans la BDD sont des valeurs décimales, calculées
// à partir du CENTRE de l'image.
// La valeur 0/0 représente donc le centre de l'image, 0.5/0.5 représente le coin en bas à droite de l'image,
// le point -0.5/-0.5 le point en haut à gauche et 0.5/0.0, le point sur le bord droit de l'image et au milieu dans le sens vertical.
// Important, les valeurs peuvent être supérieures à 0.5 en valeur absolue car le point peut être hors de l'image. Par exemple,
// dans le cas d'une image large et peu haute, des points peuvent être posés au-dessus ou au-dessous (et réciproquement).
// L'objectif de cette méthode est de pouvoir ensuite afficher la page dans n'importe quelle proportion avec des points
// toujours correctement placés sur ou autour de l'image.
//-----------------------------------------------------
// Croix de positionnement :
// (actives lorsque le point est en cours de déplacement)
Rectangle {
y: diametre_points_deplacables / 2
width: diametre_points_deplacables
height: 1
color: couleur_green_yellow
visible: model.actif
}
Rectangle {
x: diametre_points_deplacables / 2
width: 1
height: diametre_points_deplacables
color: couleur_green_yellow
visible: model.actif
}
//-----------------------------------------------------
// Icone :
// (remplacée par la croix de positionnement en cours de déplacement)
Image {
source: "/images/" + shipheart_page_personnalisee_type_point_icones[model.type_source][model.type_point]
sourceSize.height: model.taille_point
anchors {
horizontalCenter: parent.horizontalCenter
verticalCenter: parent.verticalCenter
}
visible: !model.actif
}
//-----------------------------------------------------
// N° du point afin de faire le lien avec l'écran de gauche :
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_blanc_casse
}
//-----------------------------------------------------
// Prise en compte des déplacements horizontaux :
onXChanged: { // Si le rectangle/cercle a bougé (avec le doigt), on met à jour la valeur de X de 'modèle_liste_points'
if (image_silhouette.visible) modele_liste_points.setProperty(index, "x", ((x + (point_deplacable.width / 2) - (zone_affichage_droite.width / 2)) / image_silhouette.paintedWidth) )
else modele_liste_points.setProperty(index, "x", ((x + (point_deplacable.width / 2) - (zone_affichage_droite.width / 2)) / zone_affichage_droite.width) )
if ((Math.abs(model.x - model.x_initial) > ecart_maxi_detectable)
|| (Math.abs(model.y - model.y_initial) > ecart_maxi_detectable)) { // On est obligé de tester X et Y à chaque fois car on pourrait dévalider un changed si X est revenu à sa position initiale et pas Y
modele_liste_points.setProperty(index, "changed", true);
}
else modele_liste_points.setProperty(index, "changed", false);
}
//-----------------------------------------------------
// Prise en compte des déplacements verticaux :
onYChanged: { // Iem pour l'axe Y
if (image_silhouette.visible) modele_liste_points.setProperty(index, "y", ((y + (point_deplacable.height / 2) - (zone_affichage_droite.height / 2)) / image_silhouette.paintedHeight) )
else modele_liste_points.setProperty(index, "y", ((y + (point_deplacable.height / 2) - (zone_affichage_droite.height / 2)) / zone_affichage_droite.height) )
if ((Math.abs(model.x - model.x_initial) > ecart_maxi_detectable)
|| (Math.abs(model.y - model.y_initial) > ecart_maxi_detectable)) { // On est obligé de tester X et Y à chaque fois car on pourrait dévalider un changed si Y est revenu à sa position initiale et pas X
modele_liste_points.setProperty(index, "changed", true);
}
else modele_liste_points.setProperty(index, "changed", false);
}
//-----------------------------------------------------
MouseArea {
id: mouse_area_point_deplacable // Zone dragable
anchors.fill: parent
drag.target: point_deplacable
drag.axis: Drag.XAndYAxis
drag {
// Note : Le 'point' est le coin haut-gauche du rectangle (retaillé en cercle).
// Il faut donc décaler les références d'une demi-largeur de rectangle pour que
// le centre du cercle arrive aux extrémités attendues...
minimumX: 0 - (point_deplacable.width / 2)
maximumX: zone_affichage_droite.width - (point_deplacable.width / 2)
minimumY: 0 - (point_deplacable.width / 2)
maximumY: zone_affichage_droite.height - (point_deplacable.width / 2)
}
preventStealing: true
onPressed: {
modele_liste_points.setProperty(index, "actif", true) // Permet d'informer le bouton correpondant sur la liste gauche de passer en "actif"
liste_points.currentIndex = index // Permet de ramener le bouton correspondant dans la zone visible du ListView de gauche
}
onReleased: {
modele_liste_points.setProperty(index, "actif", false) // Permet d'informer le bouton correpondant sur la liste gauche de repasser au "repos"
}
}
onFlag_init_point_deplacableChanged: { // Activé en fin d'init : Positionne le rectangle/cercle sur la valeur initiale du modèle.
if (image_silhouette.visible)
{
point_deplacable.x = (model.x * image_silhouette.paintedWidth) + (zone_affichage_droite.width / 2) - (point_deplacable.width / 2)
point_deplacable.y = (model.y * image_silhouette.paintedHeight) + (zone_affichage_droite.height / 2) - (point_deplacable.height / 2)
}
else
{
point_deplacable.x = (model.x * zone_affichage_droite.width) + (zone_affichage_droite.width / 2) - (point_deplacable.width / 2)
point_deplacable.y = (model.y * zone_affichage_droite.height) + (zone_affichage_droite.height / 2) - (point_deplacable.height / 2)
}
}
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é...
if (image_silhouette.visible)
{
point_deplacable.x = (model.x * image_silhouette.paintedWidth) + (zone_affichage_droite.width / 2) - (point_deplacable.width / 2)
point_deplacable.y = (model.y * image_silhouette.paintedHeight) + (zone_affichage_droite.height / 2) - (point_deplacable.height / 2)
}
else
{
point_deplacable.x = (model.x * zone_affichage_droite.width) + (zone_affichage_droite.width / 2) - (point_deplacable.width / 2)
point_deplacable.y = (model.y * zone_affichage_droite.height) + (zone_affichage_droite.height / 2) - (point_deplacable.height / 2)
}
}
}
}
}
}
//-------------------------------------------------------
// 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 * pourcentage_bandeau_bas_largeur_zone_retour) - parent.leftPadding - (parent.spacing / 2)
height: shipheart_hauteur_bandeau_bas - parent.topPadding - parent.bottomPadding;
radius: shipheart_rayon_bouton
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<br />Cliquer sur Annuler pour revenir en arrière et les enregistrer.<br />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:
}
}
}
// 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;
if (id_page === 0) stackView.push("config_page.qml", {"id_page": 0}); // Lors de la création, on va directement à la config de la page
else
{
flag_nouvelles_valeurs = false;
i = 0;
// Récupération des infos correspondant à la page concernée :
req = 'SELECT Nom, ';
// req += ' Type, ';
req += ' Fichier, ';
req += ' Opacite, ';
req += ' Taille, ';
req += ' Offset_x, ';
req += ' Offset_y, ';
req += ' Points ';
req += ' FROM TB_Pages ';
req += ' WHERE Cle_primaire=' + id_page + ' ';
//console.log("req = [ " + req + "; ]");
res = bdd.requete_unique(req);
nom_page = res[i++];
fichier = res[i++];
opacite = Number(res[i++]);
taille = Number(res[i++]);
offset_x = Number(res[i++]);
offset_y = Number(res[i++]);
chaine_points = res[i++];
// Remplissage des courbes et modèles :
if (chaine_points !== "") init_points(chaine_points.split(';'));
else init_points(""); // L'appel avec un array vide (cas du résultat de split sur une chaine vide) donne des résultats pas très clairs...
flag_init = false; // Indique que l'init est terminée
}
}
//=============================================================================================================
// Met à jour 'modele_liste_points', 'points_originaux' et 'points_nouveaux' avec les paramètres contenu dans 'chaines_points'
//=============================================================================================================
function init_points(chaines_points)
{
var req, res;
var nb;
var i;
var chaine_point;
var point;
var validite;
var type_source;
var id_source;
var x, y;
var type_point;
var taille_point;
var icone;
// RAZ des courbes :
nb_points = 0;
modele_liste_points.clear();
flag_aucun_points = true;
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 :
for (chaine_point of chaines_points) // Chaque point est du type : [validite/type_source/id_source/x/y/type_point/taille_point]
{
point = chaine_point.split('/');
i = 0;
validite = Number(point[i++]);
type_source = Number(point[i++]);
id_source = Number(point[i++]);
x = Number(point[i++]);
y = Number(point[i++]);
type_point = Number(point[i++]);
taille_point = Number(point[i++]);
// Recherche des infos de l'équipement :
switch(type_source)
{
case shipheart_type_source_circuits:
req = ' SELECT Nom FROM TB_Circuits WHERE Cle_primaire=' + id_source + ' ';
icone = "icone_consommation_electrique.png";
break;
case shipheart_type_source_reservoirs:
req = ' SELECT Nom FROM TB_Reservoirs WHERE Cle_primaire=' + id_source + ' ';
icone = "reservoir_eau.png";
break;
case shipheart_type_source_batteries:
req = ' SELECT Nom FROM TB_Batteries WHERE Cle_primaire=' + id_source + ' ';
icone = "batterie_moyen.png";
break;
case shipheart_type_source_capteurs:
req = ' SELECT Nom FROM TB_Capteurs WHERE Cle_primaire=' + id_source + ' ';
icone = "icone_auxiliaires.png";
break;
case shipheart_type_source_modules:
req = ' SELECT Type FROM TB_Modules_config WHERE Adresse_k_bus=' + id_source + ' ';
icone = "soh_sombre.png";
break;
}
//console.log("req = [ " + req + "; ]");
res = bdd.requete_unique(req);
modele_liste_points.append({
"validite": validite,
"type_source": type_source,
"id_source": id_source,
"x": x,
"y": y,
"x_initial": x,
"y_initial": y,
"type_point": type_point,
"taille_point": taille_point,
"nom": (type_source !== shipheart_type_source_modules) ? res[0] : Fonctions.nom_type_module(Number(res[0])),
"icone": icone,
"actif": false,
"changed": false }); // Modèle pour la liste de gauche + les rectangle/cercles utilisés pour les bouger
flag_aucun_points = false; // Utilisé pour afficher le message "aucune données...'
if (++nb_points >= shipheart_page_personnalisee_nb_max_points)
{
flag_affichage_ajout_point = false;
break;
}
}
flag_alignement_demande = true // Permet de déclencher un traitement au niveau 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.
}
//=============================================================================================================
// Enregistre dans la BDD les nouvelles valeurs
//=============================================================================================================
function enregistrer_points()
{
var i;
var req;
var chaine = "";
// Génération de la chaine de points [validite/type_source/id_source/x/y/type_point/taille_point] :
for(i = 0; i < modele_liste_points.count; i++) {
if (chaine !== "") chaine += ";";
chaine += (modele_liste_points.get(i).validite) ? "1" : "0";
chaine += "/";
chaine += (modele_liste_points.get(i).type_source);
chaine += "/";
chaine += (modele_liste_points.get(i).id_source);
chaine += "/";
chaine += (modele_liste_points.get(i).x).toFixed(4);
chaine += "/";
chaine += (modele_liste_points.get(i).y).toFixed(4);
chaine += "/";
chaine += (modele_liste_points.get(i).type_point);
chaine += "/";
chaine += (modele_liste_points.get(i).taille_point);
modele_liste_points.setProperty(i, "changed", false) // On en profite pour RAZ tous les flags Changed
}
flag_nouvelles_valeurs = false; // ainsi que le flag de nouvelles valeurs
// Mise à jour de l'entrée dans la table :
req = 'UPDATE TB_Pages SET Points="' + chaine + '" WHERE Cle_primaire=' + id_page + ' '
//console.log("req = [ " + req + "; ]");
bdd.requete_unique(req);
}
}
+741
View File
@@ -0,0 +1,741 @@
//========================================================================================================================================================================
// CONFIG_PAGE_POINT.QML
// Ce fichier traite la configuration d'un point dans une page. La page permet notamment de :
// - Modifier la config du point (ou de le créer la première fois),
// - Supprimer le point
// La page est appelée depuis la page config_page_edition.qml, en cliquant sur le point à modifier (ou sur le '+' en vue d'une création)
//========================================================================================================================================================================
import QtQuick 2.12
import QtQml.Models
import qt.koriolan.shipheart 1.0
import"fonctions.js" as Fonctions
Shipheart_page {
id: page_config_page_point
// Mise en forme - Largeur :
readonly property double pourcentage_largeur_colonne_gauche: 0.75
readonly property double espace_colonnes_principales: 20
readonly property double pourcentage_largeur_labels_formulaire: 0.5
readonly property double pourcentage_largeur_unites_formulaire: 0.1
readonly property int largeur_bouton_suite: 15
readonly property double pourcentage_bandeau_bas_largeur_zone_retour: 1
readonly property int nombre_widgets_heure_par_ligne: 12
// Mise en forme - Hauteur :
readonly property double pourcentage_hauteur_ligne_haut: 1
readonly property int hauteur_ligne_formulaire: 60
readonly property int hauteur_bouton_droit: 80
readonly property int espace_lignes_principales: 20
readonly property double affichage_hauteur_utile: shipheart_hauteur_page - shipheart_hauteur_bandeau_haut - shipheart_hauteur_bandeau_bas
// Timings :
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 bool flag_init: true // Permet de bloquer certains affichage tant que l'init n'est pas terminée
// Données :
property int valeur_timer_appui_long: 0
property bool flag_point_existant: false
property int type_source: 0
property int id_source: -1
property int type_point: 0
property int taille_point: 25
// Modèles :
// Note : les modèles chargés dans 'main.qml' n'ont pas à être rechargés dans les pages suivantes
//-------------------------------------------------
// Signaux :
//-------------------------------------------------
signal enregistrer_config_page_point(bool new_flag_point_existant, int new_type_source, int new_id_source, int type_point, int taille_point) // Valeurs à lire lorsque le "OnEnregistrer' est activé
//===========================================================
// 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 {
}
}
}
//===========================================================
// 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;
if (mousearea_bouton_supprimer.pressed)
{
shipheart_clic();
page_config_page_point.enregistrer_config_page_point(false, type_source, id_source, type_point, taille_point);
stackView.pop(); // Retour à la page d'édition de la page
}
}
}
}
// Titre de la page :
Shipheart_titre_1 {titre: (type_source === -1) ? qsTr("Création d'un point") : qsTr("Configuration du point"); 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);
color: "transparent"
ListView {
id: grille_principale
width: parent.width
height: parent.height
// snapMode: GridView.SnapToRow // L'arret de l'affichage se fera sur une rangée entière.
clip: true // Nécessaire pour que les Delegates s'arretent à l'intérieur de la GridView
model: modele_grille_principale
footer: Rectangle { // Permet de dégager la place suffisante en fin de GridView pour accéder même au dernier élément lorsque que clavier est déplié
height: affichage_hauteur_utile - hauteur_ligne_formulaire // Cette valeur permet de bien remonter les drniers champs (utile dans le cas de Textarea notamment) en laissant toujours une hauteur de lugne de formulaire au moins (évite qu'on "perde" l'affichage qui serait trop décalé vers le haut)
color: "transparent"
}
ObjectModel {
id: modele_grille_principale
//-------------------------------------------------------------------------------
// Ligne de formulaire : Type_source
Rectangle {
id: ligne_type_source
width: (shipheart_largeur_page * pourcentage_largeur_colonne_gauche) - (espace_colonnes_principales / 2)
height: visible ? hauteur_ligne_formulaire : 0
color: "transparent"
Shipheart_formulaire_label {
text: qsTr("Type de la source")
width: parent.width * pourcentage_largeur_labels_formulaire
couleur: (champ_formulaire_Type_source.currentIndex !== 0)
? shipheart_formulaire_couleur_texte_par_defaut
: shipheart_formulaire_couleur_texte_en_erreur
}
Shipheart_formulaire_combobox {
id: champ_formulaire_Type_source
model: ListModel { }
width: parent.width * (1 - pourcentage_largeur_labels_formulaire)
onActivated: {
shipheart_clic();
if (type_source !== champ_formulaire_Type_source.model.get(champ_formulaire_Type_source.currentIndex).valeur)
{
type_source = champ_formulaire_Type_source.model.get(champ_formulaire_Type_source.currentIndex).valeur;
id_source = -1;
type_point = 0;
if (type_source > 0) mise_a_jour_source();
}
}
}
Shipheart_formulaire_ligne_separation {
haut_bas_hautbas: shipheart_formulaire_ligne_separation_basse
}
}
//-------------------------------------------------------------------------------
// Ligne de formulaire : ID_Source
Rectangle {
id: ligne_source
width: (shipheart_largeur_page * pourcentage_largeur_colonne_gauche) - (espace_colonnes_principales / 2)
height: visible ? hauteur_ligne_formulaire : 0
color: "transparent"
visible: (type_source !== 0)
Shipheart_formulaire_label {
text: qsTr("Source")
width: parent.width * pourcentage_largeur_labels_formulaire
couleur: (champ_formulaire_Source.currentIndex !== 0)
? shipheart_formulaire_couleur_texte_par_defaut
: shipheart_formulaire_couleur_texte_en_erreur
}
Shipheart_formulaire_combobox {
id: champ_formulaire_Source
model: ListModel { }
width: parent.width * (1 - pourcentage_largeur_labels_formulaire)
onActivated: {
shipheart_clic();
id_source = champ_formulaire_Source.model.get(champ_formulaire_Source.currentIndex).valeur;
mise_a_jour_type_point();
}
}
Shipheart_formulaire_ligne_separation {
haut_bas_hautbas: shipheart_formulaire_ligne_separation_basse
}
}
//-------------------------------------------------------------------------------
// Ligne de formulaire : Type_point
Rectangle {
id: ligne_type_point
width: (shipheart_largeur_page * pourcentage_largeur_colonne_gauche) - (espace_colonnes_principales / 2)
height: visible ? hauteur_ligne_formulaire : 0
color: "transparent"
visible: (type_source !== 0)
&& (id_source !== -1)
&& (type_source !== shipheart_type_source_modules)
// && (champ_formulaire_Type_point.model.count > 1)
Shipheart_formulaire_label {
text: qsTr("Type de point")
width: parent.width * pourcentage_largeur_labels_formulaire
couleur: shipheart_formulaire_couleur_texte_par_defaut
}
Shipheart_formulaire_combobox_icones {
id: champ_formulaire_Type_point
model: ListModel { }
width: parent.width * (1 - pourcentage_largeur_labels_formulaire)
onActivated: {
shipheart_clic();
type_point = champ_formulaire_Type_point.model.get(champ_formulaire_Type_point.currentIndex).valeur;
}
}
Shipheart_formulaire_ligne_separation {
haut_bas_hautbas: shipheart_formulaire_ligne_separation_basse
}
}
//-------------------------------------------------------------------------------
// Ligne de formulaire : Taille_point
Rectangle {
id: ligne_taille_point
width: (shipheart_largeur_page * pourcentage_largeur_colonne_gauche) - (espace_colonnes_principales / 2)
height: visible ? hauteur_ligne_formulaire : 0
color: "transparent"
visible: (type_source !== 0)
&& (id_source !== -1)
// && (champ_formulaire_Taille_point.model.count > 1)
Shipheart_formulaire_label {
text: qsTr("Taille du point")
width: parent.width * pourcentage_largeur_labels_formulaire
couleur: shipheart_formulaire_couleur_texte_par_defaut
}
Shipheart_formulaire_combobox {
id: champ_formulaire_Taille_point
model: ListModel { }
width: parent.width * (1 - pourcentage_largeur_labels_formulaire)
onActivated: {
shipheart_clic();
taille_point = champ_formulaire_Taille_point.model.get(champ_formulaire_Taille_point.currentIndex).valeur;
}
}
Shipheart_formulaire_ligne_separation {
haut_bas_hautbas: shipheart_formulaire_ligne_separation_basse
}
}
} // Fin de Grille principale
}
}
//-------------------------------------------------------
// 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 * pourcentage_hauteur_ligne_haut) - (espace_lignes_principales / 2);
color: "transparent"
Column {
anchors.fill: parent
spacing: 10
//.......................................................
// Bouton Enregistrer/créer :
//.......................................................
Rectangle {
id: bouton_enregistrer_creer
width: parent.width
height: hauteur_bouton_droit
color: shipheart_couleur_fond_bouton
radius: shipheart_rayon_bouton
visible: (champ_formulaire_Type_source.currentIndex !== 0)
&& (champ_formulaire_Source.currentIndex !== 0)
Text {
anchors {
verticalCenter: parent.verticalCenter
horizontalCenter: parent.horizontalCenter
}
font.pixelSize: 20
text: flag_point_existant ? qsTr("Enregistrer") : qsTr("Créer le point")
color: couleur_blanc_casse
}
MouseArea {
anchors.fill: parent
onClicked: {
shipheart_clic();
page_config_page_point.enregistrer_config_page_point(true, type_source, id_source, type_point, taille_point);
stackView.pop(); // Retour à la page d'édition de la page
}
}
}
//.......................................................
// Bouton de suppression
//.......................................................
Rectangle {
width: parent.width
height: hauteur_bouton_droit
color: shipheart_couleur_fond_bouton
radius: shipheart_rayon_bouton
visible: flag_point_existant
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_supprimer.pressed
}
Text {
anchors {
verticalCenter: parent.verticalCenter
horizontalCenter: parent.horizontalCenter
}
font.pixelSize: 20
text: qsTr("Supprimer")
color: couleur_blanc_casse
}
MouseArea {
id: mousearea_bouton_supprimer
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;
}
}
}
//.......................................................
// Timer de reconfiguration :
//.......................................................
Timer {
id: timer_tempo_reconfiguration
interval: 250 // Cette tempo n'est utilisée que pour le premier cycle
triggeredOnStart: false
running: false
repeat: true
onTriggered: {
if (bdd.check_fin_configuration())
{
timer_tempo_reconfiguration.stop();
page_config_page_point.enregistrer_config_page_point(flag_point_existant, type_source, id_source, type_point, taille_point);
stackView.pop(); // Retour à la page d'édition de la page
}
else timer_tempo_reconfiguration.interval = 100 // Tempo plus courte pour les lectures suivantes
}
}
}
}
//-------------------------------------------------------
// 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 * pourcentage_bandeau_bas_largeur_zone_retour) - (2 * parent.padding)
height: shipheart_hauteur_bandeau_bas - parent.topPadding - parent.bottomPadding;
radius: shipheart_rayon_bouton
color: shipheart_couleur_fond_bouton
Image {
source: "/images/icone_retour.png"
sourceSize.height: 30
anchors {
verticalCenter: parent.verticalCenter
horizontalCenter: parent.horizontalCenter
}
}
MouseArea { // Zone d'interaction. On est obligé de la mettre dans la grille pour que Rectangle et MouseArea soient frères ou parent/enfant ET qu'on n'occupe qu'un espace dans la grille.
id: mouse_area_bandeau_bas_zone_retour
anchors.fill: parent
onClicked: {
shipheart_clic();
stackView.pop()
}
}
}
}
//-------------------------------------------------------
// Boite de validation :
//-------------------------------------------------------
Shipheart_boite_dialogue {
id: boite_information
}
//===========================================================
// Busy Indicator :
//===========================================================
Shipheart_busy_indicator {
id: busy_indicator
running: timer_tempo_reconfiguration.running
}
// 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;
// Remplissage des différents modèles, dans l'ordre des priorités :
mise_a_jour_type_source();
// Mise à jour du modèle de taille sans attendre car elle est indépendante du type de source ou de point :
mise_a_jour_taille_point();
flag_init = false; // Indique que l'init est terminée
}
//=============================================================================================================
// MAJ du type de source
//=============================================================================================================
function mise_a_jour_type_source()
{
var i;
// Effacement du modèle et premier enregistrement avec 'Non défini' :
champ_formulaire_Type_source.model.clear();
champ_formulaire_Type_source.model.append( {
"valeur": -1,
"texte": qsTr("Non défini"),
"actif": true });
champ_formulaire_Type_source.model.append( {
"valeur": shipheart_type_source_capteurs,
"texte": qsTr("Capteur"),
"actif": true });
champ_formulaire_Type_source.model.append( {
"valeur": shipheart_type_source_circuits,
"texte": qsTr("Circuit"),
"actif": true });
champ_formulaire_Type_source.model.append( {
"valeur": shipheart_type_source_reservoirs,
"texte": qsTr("Réservoir"),
"actif": true });
champ_formulaire_Type_source.model.append( {
"valeur": shipheart_type_source_batteries,
"texte": qsTr("Batterie"),
"actif": true });
champ_formulaire_Type_source.model.append( {
"valeur": shipheart_type_source_modules,
"texte": qsTr("Modules"),
"actif": true });
// Mise à jour de l'index courant du modèle champ_formulaire_Type_source :
i = recherche_index_valeur_modele(type_source, champ_formulaire_Type_source.model, "Type_source");
if (i === -1) champ_formulaire_Type_source.currentIndex = recherche_index_valeur_modele(-1, champ_formulaire_Type_source.model, "Type_source");
else champ_formulaire_Type_source.currentIndex = i;
// Appel du remplissage des modèles de niveau inférieur si cela est possible :
if (type_source > 0) mise_a_jour_source();
}
//=============================================================================================================
// MAJ de la source
//=============================================================================================================
function mise_a_jour_source()
{
var req, res;
var nb;
var i;
// Effacement du modèle et premier enregistrement avec 'Non défini' :
champ_formulaire_Source.model.clear();
champ_formulaire_Source.model.append( {
"valeur": -1,
"texte": qsTr("Non défini"),
"actif": true });
switch(type_source)
{
case shipheart_type_source_circuits:
req = 'SELECT Cle_primaire, ';
req += 'Nom, ';
req += 'Nom_famille ';
req += 'FROM TB_Circuits, ';
req += ' (SELECT Cle_primaire AS Cle_famille, Nom AS Nom_famille, Priorite_affichage AS Priorite_famille FROM TB_Familles WHERE Type_source=' + shipheart_type_source_circuits + ') AS TB_Familles ';
req += ' WHERE ID_Famille=Cle_famille ';
req += ' ORDER BY Priorite_famille, ABS(Priorite_affichage_accueil)';
break;
case shipheart_type_source_capteurs:
req = 'SELECT Cle_primaire, ';
req += 'Nom, ';
req += 'Nom_famille ';
req += 'FROM TB_Capteurs, ';
req += ' (SELECT Cle_primaire AS Cle_famille, Nom AS Nom_famille, Priorite_affichage AS Priorite_famille FROM TB_Familles WHERE Type_source=' + shipheart_type_source_capteurs + ') AS TB_Familles ';
req += ' WHERE ID_Famille=Cle_famille ';
req += ' ORDER BY Priorite_famille, ABS(Priorite_affichage_accueil)';
break;
case shipheart_type_source_reservoirs:
req = 'SELECT Cle_primaire, ';
req += 'Nom, ';
req += 'Nom_famille ';
req += 'FROM TB_Reservoirs, ';
req += ' (SELECT Cle_primaire AS Cle_famille, Nom AS Nom_famille, Priorite_affichage AS Priorite_famille FROM TB_Familles WHERE Type_source=' + shipheart_type_source_reservoirs + ') AS TB_Familles ';
req += ' WHERE ID_Famille=Cle_famille ';
req += ' ORDER BY Priorite_famille, ABS(Priorite_affichage_accueil)';
break;
case shipheart_type_source_batteries:
req = 'SELECT Cle_primaire, ';
req += 'Nom, ';
req += 'Nom_famille ';
req += 'FROM TB_Batteries, ';
req += ' (SELECT Cle_primaire AS Cle_famille, Nom AS Nom_famille, Priorite_affichage AS Priorite_famille FROM TB_Familles WHERE Type_source=' + shipheart_type_source_batteries + ') AS TB_Familles ';
req += ' WHERE ID_Famille=Cle_famille ';
req += ' ORDER BY Priorite_famille, ABS(Priorite_affichage_accueil)';
break;
case shipheart_type_source_modules:
req = 'SELECT Adresse_k_bus, ';
req += 'Type, ';
req += '"" AS Nom_famille '; // Permet de passer le traitement des familles sans erreur et ajout de ligne inutile (les batteries n'ont pas de notion de famille)
req += 'FROM TB_Modules_config ';
req += ' ORDER BY Adresse_k_bus';
break;
default:
table = "";
break;
}
if (req !== "")
{
//console.log("Requête = [ " + req + "; ]");
nb = bdd.requete(req);
var prec_famille = "";
for (i = 0; i < nb; i ++)
{
res = bdd.lecture_ligne();
if (!res.isEmpty)
{
// Ajout de le ligne de famille :
if (prec_famille !== res[2])
{
champ_formulaire_Source.model.append( {
"groupe": true,
"valeur": 0,
"texte": res[2],
"actif": false });
prec_famille = res[2];
}
// Ajout de la ligne normale :
var texte;
if (type_source === shipheart_type_source_modules)
{
texte = Fonctions.nom_type_module(Number(res[1]));
if (Number(res[0]) !== 0) texte += " @" + res[0];
}
else texte = res[1];
champ_formulaire_Source.model.append( {
"groupe": false,
"valeur": Number(res[0]),
"texte": texte,
"actif": true });
}
}
}
// Mise à jour de l'index courant du modèle :
i = recherche_index_valeur_modele(id_source, champ_formulaire_Source.model, "id_source");
if (i === -1) champ_formulaire_Source.currentIndex = recherche_index_valeur_modele(0, champ_formulaire_Source.model, "id_source");
else champ_formulaire_Source.currentIndex = i;
// Appel du remplissage des modèles de niveau inférieur si cela est possible :
if (id_source !== -1) mise_a_jour_type_point();
}
//=============================================================================================================
// MAJ du type de point
//=============================================================================================================
function mise_a_jour_type_point()
{
var req, res;
var i;
var libelle;
// Effacement du modèle :
champ_formulaire_Type_point.model.clear();
// Boucle sur toutes les entrées du tableau des types de points correspondant au 'type_source' (cf dans 'main.qml') :
for (i = 0; i < shipheart_page_personnalisee_type_point_icones[type_source].length; i ++)
{
if ((type_source === shipheart_type_source_circuits) && (i === 0)) continue; // Pour les circuits, on n'autorise pas le premier élément qui correspond à l'icone OFF
champ_formulaire_Type_point.model.append( {
"valeur": i,
"texte": "",
"icone": shipheart_page_personnalisee_type_point_icones[type_source][i],
"actif": true });
}
// Mise à jour de l'index courant du modèle :
i = recherche_index_valeur_modele(type_point, champ_formulaire_Type_point.model, "type_point");
if (i === -1) champ_formulaire_Type_point.currentIndex = recherche_index_valeur_modele(1, champ_formulaire_Type_point.model, "type_point");
else champ_formulaire_Type_point.currentIndex = i;
// Cas particulier des modules : on ne laisse pas le choix de l'icone à afficher...
if (type_source === shipheart_type_source_modules)
{
req = 'SELECT Type FROM TB_Modules_config WHERE Adresse_k_bus=' + id_source + ' ';
//console.log("Requête = [ " + req + "; ]");
res = bdd.requete_unique(req);
type_point = Number(res[0]); // Pour les modules, on a autant de fichiers que de type, classés dans l'ordre des Type.
}
}
//=============================================================================================================
// MAJ de la taille de point
//=============================================================================================================
function mise_a_jour_taille_point()
{
var i;
// Effacement du modèle ::
champ_formulaire_Taille_point.model.clear();
for (i = 2; i <= 16; i ++)
{
champ_formulaire_Taille_point.model.append( {
"valeur": (i * 5),
"texte": (i * 5),
"actif": true });
}
// Mise à jour de l'index courant du modèle :
i = recherche_index_valeur_modele(taille_point, champ_formulaire_Taille_point.model, "taille_point");
if (i === -1) champ_formulaire_Taille_point.currentIndex = recherche_index_valeur_modele(25, champ_formulaire_Taille_point.model, "taille_point");
else champ_formulaire_Taille_point.currentIndex = i;
}
//=============================================================================================================
// Recherche de l'index d'une certaine valeur dans un modele
//=============================================================================================================
function recherche_index_valeur_modele(cible, modele, nom_modele)
{
var i;
for (i=0; i<modele.count; i ++)
{
if (modele.get(i).valeur === cible) break; // Cas où la valeur cible a été trouvée parmi les valeurs du modèle
}
if (i === modele.count)
{
console.log("Aucune valeur trouvée pour cible = [" + cible + "] du modèle = " + nom_modele);
i = -1; // Cas où la valeur n'a pas été trouvée parmi les valeurs du modèle
}
//else console.log("Valeur trouvée : [" + i + "] pour cible = [" + cible + "] soit texte = [" + modele.get(i).texte + "]");
return(i);
}
}
+528
View File
@@ -0,0 +1,528 @@
//========================================================================================================================================================================
// CONFIG_PAGES.QML
// Ce fichier traite la configuration des pages. La page permet notamment de :
// - Afficher, pour toutes les pages déjà configurées (et donc d'accéder à la configuration détaillée de l'une d'elle ; ou sa création)
// - valider/dévalider l'affichage d'une page via un bouton en bout de ligne basculable par clic-droit
// - Changer l'ordre d'affichage sur la page d'accueil
// La page est appelée depuis la page PARAMETRES_AVANCES, en cliquant sur "Batteries"
//========================================================================================================================================================================
import QtQuick 2.12
import QtQuick.Controls 2.12
import QtQml.Models 2.1
import qt.koriolan.shipheart 1.0
Shipheart_page {
id: page_config_pages
// Mis en forme :
readonly property double affichage_hauteur_utile: shipheart_hauteur_page - shipheart_hauteur_bandeau_haut - shipheart_hauteur_bandeau_bas
readonly property int hauteur_bouton_droit: 80
readonly property double pourcentage_largeur_colonne_gauche: 0.75
readonly property double pourcentage_bandeau_bas_largeur_zone_retour: 1.0
readonly property int espace_colonnes_principales: 20
readonly property double pourcentage_largeur_bouton_affichage_dans_colonne_gauche: 0.15
readonly property int nombre_pages_affiches_sur_une_colonne: 8
readonly property int periode_init_raffraichissement_parametres: 1 // En ms
readonly property int periode_raffraichissement_parametres: 100 // En ms
// Affichage :
property int niveau_page: 0
// Modèles :
// Note : les modèles chargés dans 'main.qml' n'ont pas à être rechargés dans les pages suivantes
//===========================================================
// Timer periode raffraichissement parametres :
//===========================================================
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;
}
else {
}
}
}
//===========================================================
// Lorsque la page est sur le point d'être affichée dans la pile :
//===========================================================
StackView.onActivating: {
init_page(); // Permet que lors du retour sur la page, l'état des différentes variables soit mise à jour avant l'affichage proprement dit. Y compris lors de l'ouverture initiale...
}
// Titre de la page :
Shipheart_titre_1 {titre:qsTr("Configuration des pages"); hauteur: shipheart_hauteur_bandeau_haut}
//-------------------------------------------------------
// Ecran de gauche (liste des pages) :
//-------------------------------------------------------
Rectangle {
id: zone_affichage_gauche
x: 0
y: shipheart_hauteur_bandeau_haut;
width: (shipheart_largeur_page * pourcentage_largeur_colonne_gauche) - (espace_colonnes_principales / 2)
height: affichage_hauteur_utile
color: "transparent"
//-------------------------------------------------------------------
// Grille pour afficher les pages :
ListView {
id: grille_principale
property bool deplacement_en_cours: false // Indique si un déplacement (au global) est en cours ou pas
property int save_current_index: 0 // Permet de garder le Current_index du début du déplacement et de compenser les changements qui sont fait automatiquement, notamment en début et fin de liste. Le principe est qu'une fois le mouvement déclenché, ON NE CHANGE PAS de CurrentIndex !!
x: 0
y: 0
width: parent.width
height: parent.height
clip: true // Limite l'affichage des délégués dans la zone de ListView (sinon, on affiche au moins un délégué au-dessus et un en dessous...)
highlightRangeMode: ListView.StrictlyEnforceRange // Permet de faire défiler la liste jusqu'à ne garder que le dernier élément affiché
displaced: Transition { // Gère la transition lors d'un déplacement
NumberAnimation{
properties: "x,y" // Ici, on devrait pouvoir limiter à Y
duration: 500 // Vitesse à laquelle se déplacent les boutons lorsqu'on les déplace
}
}
model: modele_grille_principale
delegate: bouton_page
Component {
id: bouton_page
MouseArea {
id: dragArea_groupe // Zone correspondant à un seul bouton (on est dans le Delegate)
width: grille_principale.width
height: grille_principale.height / nombre_pages_affiches_sur_une_colonne
property bool bouton_appuye: false // On ne peut pas utiliser la propriété globale "deplacement_en_cours" car il faut une propriété par délégué
pressAndHoldInterval: 500; // Temporise avant de passer en mode "déplacement"
drag.target: bouton_appuye ? contenu_bouton_page : undefined
drag.axis: Drag.YAxis // On ne se déplace que sur l'axe Y
onClicked: {
shipheart_clic();
if (model.configurable) stackView.push("config_page_edition.qml", {"id_page": model.id})
}
onPressAndHold: {
shipheart_clic();
bouton_appuye = true // Indication (au niveau du bouton seul) du début de l'appui long donc du déplacement
grille_principale.deplacement_en_cours = true // Indication (au niveau de ListView) du début du déplacement
grille_principale.save_current_index = grille_principale.currentIndex; // Sauvegarde du CurrentIndex au début du déplacement
}
onReleased: {
bouton_appuye = false // Indication (au niveau du bouton seul) de la fin de l'appui long donc du déplacement
grille_principale.deplacement_en_cours = false // Indication (au niveau de ListView) de la fin du déplacement
grille_principale.currentIndex = grille_principale.save_current_index; // Restitution de la valeur initiale du CurrentIndex (a priori, pas nécessaire car mis à jour au fur et à mesure des changements dans "onCurrentIndexChanged")
}
Rectangle {
id: contenu_bouton_page
anchors {
horizontalCenter: parent.horizontalCenter
verticalCenter: parent.verticalCenter
}
width: dragArea_groupe.width - 5
height: dragArea_groupe.height - 5
color: "transparent"
opacity: dragArea_groupe.bouton_appuye ? 0.6 : 1.0
//------------------------------------
// Bouton avec le nom de la page :
//------------------------------------
Rectangle {
height: parent.height - 5
width: (model.masquable) ?
((parent.width * (1 - pourcentage_largeur_bouton_affichage_dans_colonne_gauche)) - 10) :
parent.width
color: model.configurable ?
shipheart_couleur_fond_bouton :
"transparent"
border.width: 2
border.color: shipheart_couleur_fond_bouton
radius: shipheart_rayon_bouton
anchors {
left: parent.left
verticalCenter: parent.verticalCenter
}
//--------------------------------------------------------------------
// Icone du bouton :
Rectangle { // Le passage par un rectangle avant l'image permet de centrer l'image sur le rectangle et pas juste caler les images à gauche (elles n'ont pas la même largeur donc elles n'étaient pas centrées les unes par rapport aux autres)
width: 35
height: parent.height
x: 20
color: "transparent"
visible: (model.type < shipheart_type_page_personnalisee_non_masquable)
Image {
source: (model.icone !== "") ? ("/images/" + model.icone) : "" // Sans le test, on génère un warning lorsque 'icone' est vide
sourceSize.height: parent.height * 0.5
sourceSize.width: parent.width
anchors {
verticalCenter: parent.verticalCenter
horizontalCenter: parent.horizontalCenter
}
visible: (model.icone !== "")
}
}
//--------------------------------------------------------------------
// Texte du bouton :
Text {
visible: (model.type >= shipheart_type_page_personnalisee_non_masquable)
anchors.fill: parent
horizontalAlignment: Qt.AlignLeft
verticalAlignment: Qt.AlignVCenter
leftPadding: 20
text: model.nom
color: couleur_blanc_casse
font.pixelSize: 18
wrapMode: Label.Wrap
}
}
//------------------------------------
// Bouton Visible :
//------------------------------------
Rectangle {
x: (parent.width * (1 - pourcentage_largeur_bouton_affichage_dans_colonne_gauche))
height: parent.height - 10
width:(parent.width * pourcentage_largeur_bouton_affichage_dans_colonne_gauche)
anchors {
right: parent.right
verticalCenter: parent.verticalCenter
}
radius: shipheart_rayon_bouton
color: shipheart_couleur_fond_bouton
visible: model.masquable
//----------------------------
// Coche verte ou croix rouge :
Image {
source: (model.priorite_affichage <= 0) ? "/images/faux.png" : "/images/vrai.png"
sourceSize.height: parent.height * 0.5
anchors {
verticalCenter: parent.verticalCenter
horizontalCenter: parent.horizontalCenter
}
}
//----------------------------
MouseArea {
id: mouse_area_cdes_auto
x: 0
y: 0
width: parent.width
height: parent.height
pressAndHoldInterval: shipheart_mouse_press_and_hold_interval_long
onPressAndHold: {
shipheart_clic();
toggle_validite(model.index);
}
}
}
Drag.active: dragArea_groupe.bouton_appuye
Drag.source: dragArea_groupe
Drag.hotSpot.x: width / 2
Drag.hotSpot.y: height / 2
states: State{
when: dragArea_groupe.bouton_appuye
ParentChange {
target: contenu_bouton_page
parent: grille_principale
}
AnchorChanges {
target: contenu_bouton_page
anchors { // Necessaire pour autoriser le mouvement
horizontalCenter: undefined
verticalCenter: undefined
}
}
}
}
DropArea {
enabled: (index !== 0) // On interdit de bouger la première ligne (Ephéméride)
anchors.fill: parent
onEntered: {
if (drag.source.DelegateModel.itemsIndex !== dragArea_groupe.DelegateModel.itemsIndex)
{
//console.log("\tMouvement de Source index = " + drag.source.DelegateModel.itemsIndex + " vers Dest index = " + dragArea.DelegateModel.itemsIndex)
modele_grille_principale.move(drag.source.DelegateModel.itemsIndex, dragArea_groupe.DelegateModel.itemsIndex, 1)
mise_a_jour_ordre_pages();
}
}
}
}
}
ListModel {
id: modele_grille_principale
}
Label {
anchors.fill: parent
horizontalAlignment: Qt.AlignHCenter
verticalAlignment: Qt.AlignVCenter
visible: parent.count === 0
text: qsTr("Aucune page n'est encore configurée...")
color: couleur_rouge_transparent
font.pixelSize: 15
font.bold: true
wrapMode: Label.Wrap
}
}
}
//-------------------------------------------------------
// Ecran Droite :
//-------------------------------------------------------
Rectangle {
id: zone_affichage_droite
x: zone_affichage_gauche.width + espace_colonnes_principales
y: shipheart_hauteur_bandeau_haut + 5; // Le +5 permet d'aligner le haut du premier bouton de droite avec le haut du premier bouton de la liste de gauche (
width: (shipheart_largeur_page * (1 - pourcentage_largeur_colonne_gauche)) - (espace_colonnes_principales / 2)
height: affichage_hauteur_utile
color: "transparent"
Column {
anchors.fill: parent
spacing: 10
//----------------------
// Bouton + :
Rectangle {
width: parent.width
height: hauteur_bouton_droit
color: shipheart_couleur_fond_bouton
radius: shipheart_rayon_bouton
visible: !clavier.active
Text {
anchors {
verticalCenter: parent.verticalCenter
horizontalCenter: parent.horizontalCenter
}
font.pixelSize: 40
text: qsTr("+")
color: couleur_blanc_casse
}
MouseArea {
anchors.fill: parent
onClicked: {
shipheart_clic();
stackView.push("config_page_edition.qml", {"id_page": 0})
}
}
}
}
}
//-------------------------------------------------------
// Bandeau bas :
//-------------------------------------------------------
Row {
x: 0
y: shipheart_hauteur_page - shipheart_hauteur_bandeau_bas;
width: shipheart_largeur_page;
height: shipheart_hauteur_bandeau_bas;
padding: 5
spacing: 5
//.................................................
// Bouton retour
//.................................................
Rectangle {
id: bandeau_bas_zone_retour
width: (parent.width * pourcentage_bandeau_bas_largeur_zone_retour) - parent.leftPadding - (parent.spacing / 2)
height: shipheart_hauteur_bandeau_bas - parent.topPadding - parent.bottomPadding;
radius: shipheart_rayon_bouton
color: shipheart_couleur_fond_bouton
Image {
source: "/images/icone_retour.png"
sourceSize.height: 30
anchors {
verticalCenter: parent.verticalCenter
horizontalCenter: parent.horizontalCenter
}
}
MouseArea { // Zone d'interaction. On est obligé de la mettre dans la grille pour que Rectangle et MouseArea soient frères ou parent/enfant ET qu'on n'occupe qu'un espace dans la grille.
id: mouse_area_bandeau_bas_zone_retour
anchors.fill: parent
onClicked: {
shipheart_clic();
stackView.pop()
}
}
}
}
// 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()
{
niveau_page = stackView.depth - 1; // Permet de stocker le niveau de la page dans la Stackview
mise_a_jour_donnees();
}
//=============================================================================================================
// Mise à jour des données
//=============================================================================================================
function mise_a_jour_donnees()
{
var req, res;
var i;
var nb_pages;
var type;
var configurable;
var masquable;
modele_grille_principale.clear(); // RAZ des valeurs existantes
req = 'SELECT Cle_primaire, ';
req += ' Nom, ';
req += ' Type, ';
req += ' Fichier, ';
req += ' Priorite_affichage ';
req += ' FROM TB_Pages ORDER BY ABS(Priorite_affichage), Cle_primaire';
//console.log("Requête = [ " + req + "; ]");
nb_pages = bdd.requete(req);
for (i = 0; i < nb_pages; i ++)
{
res = bdd.lecture_ligne();
if (!res.isEmpty)
{
// Recherche du caractère configurable et/ou masquable de la page :
type = Number(res[2]);
switch(type)
{
//-------------------------------------------------
case shipheart_type_page_preconfiguree_ephemeride:
configurable = false;
masquable = false;
break;
//-------------------------------------------------
case shipheart_type_page_preconfiguree_chronometre:
case shipheart_type_page_personnalisee_minuteur:
case shipheart_type_page_preconfiguree_reveil:
configurable = false;
masquable = true;
break;
//-------------------------------------------------
case shipheart_type_page_personnalisee_non_masquable:
configurable = true;
masquable = false;
break;
//-------------------------------------------------
case shipheart_type_page_personnalisee_masquable:
configurable = true;
masquable = true;
break;
//-------------------------------------------------
case shipheart_type_page_inconnu:
continue;
}
// Mise à jour du modèle :
modele_grille_principale.append({
"id": Number(res[0]),
"nom": res[1],
"type": type,
"configurable": configurable,
"masquable": masquable,
"icone": (Number(res[2]) >= shipheart_type_page_personnalisee_non_masquable) ? "" : res[3], // On n'utilise les icones que pour les pages préconfigurées (et il faut bien faire la distinctions car les fichiers icones sont stockées dans 'images' alors que les fichiers silhouette sont stockées dans 'docs/images_silhouette')
"priorite_affichage": Number(res[4]) });
}
else break; // Ne devrait pas arriver....
}
}
//=============================================================================================================
// Mise à jour de l'ordre des pages
//=============================================================================================================
function mise_a_jour_ordre_pages()
{
var req;
var i;
var new_priorite_affichage;
for (i = 0; i < modele_grille_principale.count; i++)
{
new_priorite_affichage = i + 1; // Nouvelle valeur arbitraire
if (modele_grille_principale.get(i).priorite_affichage < 0) new_priorite_affichage *= (-1); // Passage en négatif pour les pages déjà dévalidées
// Mise à jour dans le modele :
modele_grille_principale.set(i, { "priorite_affichage": new_priorite_affichage } );
// Mis à jour de la nouvelle valeur dans la BDD :
req = 'UPDATE TB_Pages SET Priorite_affichage=' + new_priorite_affichage + ' WHERE Cle_primaire=' + modele_grille_principale.get(i).id + ' ';
//console.log("Requête = [ " + req + "; ]");
bdd.requete_unique(req);
}
}
//=============================================================================================================
// Bascule de la visibilité d'une page
//=============================================================================================================
function toggle_validite(index)
{
var req;
var priorite_affichage;
// Recherche de la valeur actuelle et calcul de la nouvelle :
priorite_affichage = modele_grille_principale.get(index).priorite_affichage * (-1);
// Mise à jour de la nouvelle valeur dans le modèle :
modele_grille_principale.set(index, {"priorite_affichage": priorite_affichage});
// Mis à jour de la nouvelle valeur dans la BDD :
req = 'UPDATE TB_Pages SET Priorite_affichage=' + priorite_affichage + ' WHERE Cle_primaire=' + modele_grille_principale.get(index).id + ' ';
//console.log("Requête = [ " + req + "; ]");
bdd.requete_unique(req);
}
}
+509
View File
@@ -0,0 +1,509 @@
//========================================================================================================================================================================
// CONFIG_RESEAU_ETHERNET.QML
// Ce fichier traite la configuration du réseau Ethernet
// La page est appelée depuis la page parametres_avances.qml
//========================================================================================================================================================================
import QtQuick 2.12
import QtQuick.Controls 2.12
import QtQml.Models
import qt.koriolan.shipheart 1.0
Shipheart_page {
id: page_config_reseau_ethernet_wifi
// Mis en forme :
readonly property double pourcentage_largeur_colonne_gauche: 0.75
readonly property double espace_colonnes_principales: 20
readonly property double pourcentage_hauteur_ligne_haut: 0
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 hauteur_ligne_formulaire: 60
readonly property double pourcentage_largeur_labels_formulaire: 0.45
readonly property int hauteur_bouton_droit: 80
readonly property double pourcentage_bandeau_bas_largeur_zone_retour: 1
readonly property int periode_init_raffraichissement_parametres: 1 // En ms
readonly property int periode_raffraichissement_parametres: 1000 // En ms
// Affichage :
property bool bouton_bandeau_bas_zone_retour_actif: false
property int protection_mode: 0
property bool flag_init: true // Permet de bloquer certains affichage tant que l'init n'est pas terminée
property int position_wifi_sta: 0
property int position_wifi_ap: 0
// Données :
property string bssid_wifi_sta: ""
// 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 if (!bouton_bandeau_bas_zone_retour_actif)
{
mise_a_jour();
}
}
}
//===========================================================
// Lorsque la page est sur le point d'être affichée dans la pile :
//===========================================================
StackView.onActivating: {
mise_a_jour();
}
// Titre de la page :
Shipheart_titre_1 {titre:qsTr("Réseau Ethernet & Wifi"); 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;
color: "transparent"
ListView {
id: grille_principale
width: parent.width
height: parent.height
snapMode: GridView.SnapToRow // L'arret de l'affichage se fera sur une rangée entière
clip: true // Nécessaire pour que les Delegates s'arretent à l'intérieur de la GridView
model: modele_grille_principale
footer: Rectangle { // Permet de dégager la place suffisante en fin de GridView pour accéder même au dernier élément lorsque que clavier est déplié
height: shipheart_hauteur_clavier
color: "transparent"
}
ObjectModel {
id: modele_grille_principale
// Cf Ship2net.qml pour ajouter ici des champs de saisie...
}
}
}
//-------------------------------------------------------
// Ecran Milieu - Gauche :
//-------------------------------------------------------
Rectangle {
id: zone_affichage_milieu_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;
color: "transparent"
//-------------------------------------------------------------------
// Grille pour afficher les valeurs des paramètres non configurables :
GridView {
id: grille_informations
width: parent.width
height: parent.height
cellWidth: parent.width;
cellHeight: hauteur_ligne_formulaire
snapMode: GridView.SnapToRow // L'arret de l'affichage se fera sur une rangée entière
clip: true // Nécessaire pour que les Delegates s'arretent à l'intérieur de la GridView
model: modele_grille_informations
delegate: Rectangle {
width: zone_affichage_milieu_gauche.width
height: visible ? hauteur_ligne_formulaire : 0
color: "transparent"
visible: !flag_init
// Sous-titre :
Shipheart_titre_2 {
titre: model.titre
hauteur: shipheart_hauteur_bandeau_haut
visible: !flag_init && (model.titre !== "")
}
// Champ d'info :
Shipheart_formulaire_label {
text: model.nom_champ
width: parent.width * pourcentage_largeur_labels_formulaire
visible: (model.titre === "")
}
Shipheart_formulaire_valeur_fixe {
text: model.valeur_champ
width: parent.width * (1 - pourcentage_largeur_labels_formulaire)
x: parent.width * pourcentage_largeur_labels_formulaire
visible: (model.titre === "")
color: (model.etat === "NORMAL") ?
couleur_blanc_casse :
(model.etat === "OK") ? couleur_vert : couleur_rouge
}
Shipheart_formulaire_ligne_separation {
haut_bas_hautbas: shipheart_formulaire_ligne_separation_basse
visible: (model.titre === "")
}
Shipheart_formulaire_ligne_separation {
haut_bas_hautbas: shipheart_formulaire_ligne_separation_haute
epaisseur_ligne: 2
force: shipheart_formulaire_ligne_separation_force_elevee
visible: (model.titre !== "")
}
}
ListModel {
id: modele_grille_informations
}
}
}
//-------------------------------------------------------
// 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 * pourcentage_hauteur_ligne_haut) - (espace_lignes_principales / 2);
color: "transparent"
// ATTENTION : cette barre latérale n'est pas "standard", la 'column' habituelle a été supprimée pour permettre le positionnement des boutons à certaines hauteur...
// Bouton Wifi STA :
Rectangle {
y: position_wifi_sta * hauteur_ligne_formulaire
width: parent.width
height: hauteur_bouton_droit
color: shipheart_couleur_fond_bouton
radius: shipheart_rayon_bouton
Text {
anchors {
verticalCenter: parent.verticalCenter
horizontalCenter: parent.horizontalCenter
}
font.pixelSize: 20
text: qsTr("Modifier")
color: couleur_blanc_casse
}
MouseArea {
anchors.fill: parent
onClicked: {
shipheart_clic();
stackView.push("config_reseau_wifi_sta.qml");
}
}
}
// Bouton Wifi AP :
Rectangle {
y: position_wifi_ap * hauteur_ligne_formulaire
width: parent.width
height: hauteur_bouton_droit
color: shipheart_couleur_fond_bouton
radius: shipheart_rayon_bouton
Text {
anchors {
verticalCenter: parent.verticalCenter
horizontalCenter: parent.horizontalCenter
}
font.pixelSize: 20
text: qsTr("Modifier")
color: couleur_blanc_casse
}
MouseArea {
anchors.fill: parent
onClicked: {
shipheart_clic();
stackView.push("config_reseau_wifi_ap.qml");
}
}
}
}
//-------------------------------------------------------
// 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 * pourcentage_bandeau_bas_largeur_zone_retour) - (2 * parent.padding)
height: shipheart_hauteur_bandeau_bas - parent.topPadding - parent.bottomPadding;
radius: shipheart_rayon_bouton
color: shipheart_couleur_fond_bouton
Image {
source: "/images/icone_retour.png"
sourceSize.height: 30
anchors {
verticalCenter: parent.verticalCenter
horizontalCenter: parent.horizontalCenter
}
}
MouseArea { // Zone d'interaction. On est obligé de la mettre dans la grille pour que Rectangle et MouseArea soient frères ou parent/enfant ET qu'on n'occupe qu'un espace dans la grille.
id: mouse_area_bandeau_bas_zone_retour
anchors.fill: parent
onClicked: {
shipheart_clic();
stackView.pop()
}
}
}
}
// 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 mode_validation_maj;
//---------------------------------------------------------------------------------------
// Mise à jour des informations non configurables :
mise_a_jour();
flag_init = false;
}
//=============================================================================================================
// Mise à jour des valeurs courantes
//=============================================================================================================
function mise_a_jour()
{
var req, res;
var nb;
var i;
var nom_champ, valeur_champ;
var flag_valide;
var wifi_sta_cde;
var wifi_ap_cde;
modele_grille_informations.clear();
//=======================================================================================
// Ethernet:
//=======================================================================================
modele_grille_informations.append({ "titre": qsTr("Ethernet"), "nom_champ": "", "valeur_champ": "" });
//---------------------------------------------------------------------------------------
// Adresse IP Ethernet:
req = 'SELECT Valeur FROM TB_Systeme WHERE Nom="Adresse_ip_ethernet" ';
res = bdd.requete_unique(req);
modele_grille_informations.append({ "titre": "",
"nom_champ":qsTr("Adresse IP"),
"valeur_champ": (res[0] === shipheart_adresse_ip_inconnue) ? qsTr("Non connecté") : res[0],
"etat": (res[0] === shipheart_adresse_ip_inconnue) ? "NOK" : "OK"
});
//=======================================================================================
// Wifi STA :
//=======================================================================================
position_wifi_sta = modele_grille_informations.count;
modele_grille_informations.append({ "titre": "Wifi STA [STAtion - " + qsTr("Connexion à un réseau existant") + "]", "nom_champ": "", "valeur_champ": "" });
//---------------------------------------------------------------------------------------
// Commande_sta:
req = 'SELECT Valeur FROM TB_Configuration WHERE Nom="Wifi_sta_cde" ';
res = bdd.requete_unique(req);
wifi_sta_cde = Number(res[0]);
modele_grille_informations.append({ "titre": "",
"nom_champ":qsTr("Etat"),
"valeur_champ": (wifi_sta_cde !== 0) ? qsTr("On") : qsTr("Off"),
"etat": (wifi_sta_cde === 0) ? "NOK" : "NORMAL"
});
//---------------------------------------------------------------------------------------
// BSSID_STA :
if (wifi_sta_cde !== 0)
{
req = 'SELECT Valeur FROM TB_Configuration WHERE Nom="BSSID_Wifi_sta" ';
res = bdd.requete_unique(req);
bssid_wifi_sta = res[0];
}
//---------------------------------------------------------------------------------------
// SSid_STA & Pwd_STA :
if (wifi_sta_cde !== 0)
{
var nom_reseau_wifi_sta = "--";
var psk_reseau_wifi_sta = "--";
if (bssid_wifi_sta !== "")
{
req = 'SELECT Nom, Psk FROM TB_Wifi_sta WHERE Bssid="' + bssid_wifi_sta + '" ';
res = bdd.requete_unique(req);
nom_reseau_wifi_sta = res[0];
psk_reseau_wifi_sta = res[1];
}
modele_grille_informations.append({ "titre": "",
"nom_champ":qsTr("Nom du réseau [SSID]"),
"valeur_champ": nom_reseau_wifi_sta,
"etat": "NORMAL"
});
/* modele_grille_informations.append({ "titre": "",
"nom_champ":qsTr("Mot de passe d'accès au réseau"),
"valeur_champ": psk_reseau_wifi_sta,
"etat": "NORMAL"
});
*/
}
//---------------------------------------------------------------------------------------
// Ip_STA :
if (wifi_sta_cde !== 0)
{
req = 'SELECT Valeur FROM TB_Systeme WHERE Nom="Adresse_ip_wifi_sta" ';
res = bdd.requete_unique(req);
modele_grille_informations.append({ "titre": "",
"nom_champ":qsTr("Adresse IP"),
"valeur_champ": (res[0] === shipheart_adresse_ip_inconnue) ? qsTr("Non connecté") : res[0],
"etat": (res[0] === shipheart_adresse_ip_inconnue) ? "NOK" : "OK"
});
}
//=======================================================================================
// Wifi AP :
//=======================================================================================
position_wifi_ap = modele_grille_informations.count;
modele_grille_informations.append({ "titre": "Wifi AP [Access Point - " + qsTr("Génération d'un réseau spécifique") + "]", "nom_champ": "", "valeur_champ": "" });
//---------------------------------------------------------------------------------------
// Commande AP :
req = 'SELECT Valeur FROM TB_Configuration WHERE Nom="Wifi_ap_cde" ';
res = bdd.requete_unique(req);
wifi_ap_cde = Number(res[0]);
modele_grille_informations.append({ "titre": "",
"nom_champ":qsTr("Etat"),
"valeur_champ": (wifi_ap_cde !== 0) ? qsTr("On") : qsTr("Off"),
"etat": (wifi_ap_cde === 0) ? "NOK" : "NORMAL"
});
//---------------------------------------------------------------------------------------
// SSid_AP :
if (wifi_ap_cde !== 0)
{
req = 'SELECT Valeur FROM TB_Configuration WHERE Nom="Wifi_ap_ssid" ';
res = bdd.requete_unique(req);
modele_grille_informations.append({ "titre": "",
"nom_champ":qsTr("Nom du réseau [SSID]"),
"valeur_champ": res[0],
"etat": "NORMAL"
});
}
//---------------------------------------------------------------------------------------
// Pwd_AP :
/* if (wifi_ap_cde !== 0)
{
req = 'SELECT Valeur FROM TB_Configuration WHERE Nom="Wifi_ap_pwd" ';
res = bdd.requete_unique(req);
modele_grille_informations.append({ "titre": "",
"nom_champ":qsTr("Mot de passe d'accès au réseau"),
"valeur_champ": res[0],
"etat": "NORMAL"
});
}
*/
//---------------------------------------------------------------------------------------
// Canal :
if (wifi_ap_cde !== 0)
{
req = 'SELECT Valeur FROM TB_Configuration WHERE Nom="Wifi_ap_canal" ';
res = bdd.requete_unique(req);
var canal = Number(res[0]);
var freq;
if (canal < 36) freq = "2.4";
else freq = "5";
modele_grille_informations.append({ "titre": "",
"nom_champ": qsTr("Canal"),
"valeur_champ": canal + " [" + freq + "GHz]",
"etat": "NORMAL"
});
}
//---------------------------------------------------------------------------------------
// Ip_AP :
if (wifi_ap_cde !== 0)
{
req = 'SELECT Valeur FROM TB_Systeme WHERE Nom="Adresse_ip_wifi_ap" ';
res = bdd.requete_unique(req);
modele_grille_informations.append({ "titre": "",
"nom_champ":qsTr("Adresse IP"),
"valeur_champ": (res[0] === shipheart_adresse_ip_inconnue) ? qsTr("Non connecté") : res[0],
"etat": (res[0] === shipheart_adresse_ip_inconnue) ? "NOK" : "OK"
});
}
}
//=============================================================================================================
// Recherche de l'index d'une certaine valeur dans un modele
//=============================================================================================================
function recherche_index_valeur_modele(cible, modele, nom_modele)
{
var i;
for (i=0; i<modele.count; i ++)
{
if (modele.get(i).valeur === cible) break; // Cas où la valeur cible a été trouvée parmi les valeurs du modèle
}
if (i === modele.count)
{
console.log("Aucune valeur trouvée pour cible = [" + cible + "] du modèle = " + nom_modele);
i = -1; // Cas où la valeur n'a pas été trouvée parmi les valeurs du modèle
}
//else console.log("Valeur trouvée : [" + i + "] pour cible = [" + cible + "] soit texte = [" + modele.get(i).texte + "]");
return(i);
}
}
File diff suppressed because it is too large Load Diff
+346
View File
@@ -0,0 +1,346 @@
//========================================================================================================================================================================
// CONFIG_RESEAU_NMEA.QML
// Ce fichier traite la configuration de PGNs NMEA2000. La page permet notamment de :
// - Afficher tous les PGN/Field déjà configurés (présents ou absent mais utilisés sur un capteur)
// - Afficher tous les PGN que ShipHeart peut traiter et qu'il a vu passer sur le réseau "récemment"
// La page est appelée depuis la page PARAMETRES_AVANCES.
//========================================================================================================================================================================
import QtQuick 2.12
import QtQuick.Controls 2.12
import qt.koriolan.shipheart 1.0
import"fonctions_date_heure.js" as Fonctions_date_heure
Shipheart_page {
id: page_config_reseau_nmea
// Mis en forme :
readonly property double affichage_hauteur_utile: shipheart_hauteur_page - shipheart_hauteur_bandeau_haut - shipheart_hauteur_bandeau_bas
readonly property double pourcentage_largeur_labels_formulaire: 0.5
readonly property int nombre_capteurs_affiches_sur_une_colonne: 8
readonly property int nombre_sondes_affichees_sur_une_colonne: 8
readonly property double pourcentage_largeur_colonne_gauche: 0.75
readonly property int espace_colonnes_principales: 20
readonly property double pourcentage_bandeau_bas_largeur_zone_retour: 1.0
readonly property int periode_init_raffraichissement_parametres: 1 // En ms
readonly property int periode_raffraichissement_parametres: 1000 // En ms /!\ Laisser à 1s afin que le compteur secondes fonctionne correctement...
// Affichage :
property int niveau_page: 0
property int defaut_bus_ow: 0
// Modèles :
// Note : les modèles chargés dans 'main.qml' n'ont pas à être rechargés dans les pages suivantes
//===========================================================
// Timer periode raffraichissement parametres :
//===========================================================
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_donnees();
}
}
}
// Titre de la page :
Shipheart_titre_1 {titre:qsTr("Réseau NMEA2000"); hauteur: shipheart_hauteur_bandeau_haut}
//-------------------------------------------------------
// Ecran de gauche (liste des PGNs) :
//-------------------------------------------------------
Rectangle {
id: zone_affichage_gauche
x: 0
y: shipheart_hauteur_bandeau_haut;
width: (shipheart_largeur_page * pourcentage_largeur_colonne_gauche) - (espace_colonnes_principales / 2)
height: affichage_hauteur_utile
color: "transparent"
//-------------------------------------------------------------------
// Grille pour afficher les PGNs :
GridView {
id: grille_principale
width: parent.width
height: parent.height
cellWidth: parent.width;
cellHeight: affichage_hauteur_utile / nombre_sondes_affichees_sur_une_colonne
snapMode: GridView.SnapToRow // L'arret de l'affichage se fera sur une rangée entière
clip: true // Nécessaire pour que les Delegates s'arretent à l'intérieur de la GridView
model: modele_grille_principale
delegate: Rectangle {
width: grille_principale.cellWidth
height: grille_principale.cellHeight
color: "transparent"
Rectangle {
width: parent.width - 10
height: parent.height - 10
anchors.horizontalCenter: parent.horizontalCenter
anchors.verticalCenter: parent.verticalCenter
radius: shipheart_rayon_bouton
color: shipheart_couleur_fond_bouton
border.color: couleur_blanc_casse
border.width: (actif === 0) ? 1 : 0
Text {
anchors.fill: parent
horizontalAlignment: Qt.AlignLeft
verticalAlignment: Qt.AlignTop
topPadding: 10
leftPadding: 20
text: "PGN : " + pgn + " - Field : " + field
color: (actif === 1) ? couleur_gris_moyen : couleur_blanc_casse
font.pixelSize: 14
wrapMode: Label.Wrap
}
Text {
anchors.fill: parent
horizontalAlignment: Qt.AlignLeft
verticalAlignment: Qt.AlignTop
topPadding: 10
leftPadding: parent.width / 4
text: nom
color: (actif === 1) ? couleur_gris_moyen : couleur_blanc_casse
font.pixelSize: 16
font.weight: Font.Bold
wrapMode: Label.Wrap
}
Text {
anchors.fill: parent
horizontalAlignment: Qt.AlignRight
verticalAlignment: Qt.AlignTop
topPadding: 10
rightPadding: 20
text: Fonctions_date_heure.chaine_duree(age *1000) // age + " s"
color: (actif === 1) ? couleur_gris_moyen : couleur_blanc_casse
font.pixelSize: 14
wrapMode: Label.Wrap
}
Text {
anchors.fill: parent
horizontalAlignment: Qt.AlignLeft
verticalAlignment: Qt.AlignBottom
bottomPadding: 10
leftPadding: 20
text: nom_capteur
color: (actif === 1) ? couleur_gris_moyen : couleur_blanc_casse
font.pixelSize: 18
font.weight: Font.Bold
wrapMode: Label.Wrap
}
Text {
anchors.fill: parent
horizontalAlignment: Qt.AlignRight
verticalAlignment: Qt.AlignBottom
bottomPadding: 10
rightPadding: 20
visible: valeur_valide
text: valeur
color: (actif === 1) ? couleur_gris_moyen : couleur_blanc_casse
font.pixelSize: 18
font.bold: true
}
}
MouseArea { // Zone d'interaction. On est obligé de la mettre dans la grille pour que Rectangle et MouseArea soient frères ou parent/enfant ET qu'on n'occupe qu'un espace dans la grille.
anchors.fill: parent
enabled: actif === 0
onClicked: {
shipheart_clic();
stackView.push("config_capteur.qml", {"type_entrees": shipheart_capteur_type_entrees_nmea, "entrees": pgn + ":" + field});
}
}
}
ListModel {
id: modele_grille_principale
}
Label {
anchors.fill: parent
horizontalAlignment: Qt.AlignHCenter
verticalAlignment: Qt.AlignVCenter
visible: parent.count === 0
text: qsTr("Aucun PGN n'a encore été détecté sur le réseau NMEA2000...")
color: couleur_rouge_transparent
font.pixelSize: 15
font.bold: true
wrapMode: Label.Wrap
}
}
}
//-------------------------------------------------------
// Ecran de droite (liste des sondes de température) :
//-------------------------------------------------------
Rectangle {
id: zone_affichage_droite
x: (shipheart_largeur_page * pourcentage_largeur_colonne_gauche) + (espace_colonnes_principales / 2)
y: shipheart_hauteur_bandeau_haut;
width: (shipheart_largeur_page * (1 - pourcentage_largeur_colonne_gauche)) - (espace_colonnes_principales / 2)
height: affichage_hauteur_utile
color: "transparent"
}
//-------------------------------------------------------
// Bandeau bas :
//-------------------------------------------------------
Row {
x: 0
y: shipheart_hauteur_page - shipheart_hauteur_bandeau_bas;
width: shipheart_largeur_page;
height: shipheart_hauteur_bandeau_bas;
padding: 5
spacing: 5
//.................................................
// Bouton retour
//.................................................
Rectangle {
id: bandeau_bas_zone_retour
width: (parent.width * pourcentage_bandeau_bas_largeur_zone_retour) - (2 * parent.padding)
height: shipheart_hauteur_bandeau_bas - parent.topPadding - parent.bottomPadding;
radius: shipheart_rayon_bouton
color: shipheart_couleur_fond_bouton
Image {
source: "/images/icone_retour.png"
sourceSize.height: 30
anchors {
verticalCenter: parent.verticalCenter
horizontalCenter: parent.horizontalCenter
}
}
MouseArea { // Zone d'interaction. On est obligé de la mettre dans la grille pour que Rectangle et MouseArea soient frères ou parent/enfant ET qu'on n'occupe qu'un espace dans la grille.
id: mouse_area_bandeau_bas_zone_retour
anchors.fill: parent
onClicked: {
shipheart_clic();
stackView.pop()
}
}
}
}
// 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()
{
niveau_page = stackView.depth - 1; // Permet de stocker le niveau de la page dans la Stackview
mise_a_jour_donnees();
}
//=============================================================================================================
// Mise à jour des données
//=============================================================================================================
function mise_a_jour_donnees()
{
var req, res;
var i;
var nb;
//=======================================================================
// Grille principale (liste des sondes de température configurées) :
modele_grille_principale.clear(); // RAZ des valeurs existantes
req = 'SELECT ';
req += ' Pgn, ';
req += ' Field, '
req += ' Nom, ';
req += ' Actif, ';
req += ' (SELECT Unite FROM TB_Variables WHERE Cle_primaire=ID_Variable) AS Unite, ';
req += ' Valeur, ';
req += ' Precision_affichage_mesures,'
req += ' (UNIX_TIMESTAMP() - UNIX_TIMESTAMP(Date_maj)) AS Age ';
req += ' FROM TB_Nmea2000 ';
req += ' ORDER BY Pgn, Field';
//console.log("Requête = [ " + req + "; ]");
nb = bdd.requete(req);
for (i = 0; i < nb; i ++)
{
res = bdd.lecture_ligne();
if (!res.isEmpty)
{
modele_grille_principale.append({ "pgn": Number(res[0]),
"field": Number(res[1]),
"nom": res[2],
"actif": Number(res[3]),
"valeur": Number(res[5]).toFixed(Number(res[6])) + ((res[4] !== "") ? (" " + res[4]) : ""),
"valeur_valide": true,
"age": Number(res[7]),
"nom_capteur": qsTr("PGN non affecté") });
}
}
// Mise à jour du nom des capteurs utilisant ces sondes :
for (i = 0; i < modele_grille_principale.count; i ++)
{
if (modele_grille_principale.get(i).actif > 0)
{
req = 'SELECT Nom FROM TB_Capteurs WHERE Adresse_module=0 AND Type_entrees=' + shipheart_capteur_type_entrees_nmea + ' AND Entrees="' + modele_grille_principale.get(i).pgn + ':' + modele_grille_principale.get(i).field + '" ';
//console.log("Requête = [ " + req + "; ]");
var n = bdd.requete(req);
if (n > 0)
{
res = bdd.lecture_ligne();
modele_grille_principale.setProperty(i, "nom_capteur", res[0]);
}
}
}
// Mise à jour du champ 'valeur_valide' : :
for (i = 0; i < modele_grille_principale.count; i ++)
{
if (modele_grille_principale.get(i).actif === 0) // On ne teste que les entrées qui sont Inactive (les Actives ont forcément une valeur valide)
{
// Recherche su au moins un PGN/Field pointe sur le même PGN et est actif (dans ce cas, le Compagnon et l'Appli maintiennent à jour les valeurs de tous les Fields, même ceux qui ne sont pas valides)
req = 'SELECT COUNT(*) FROM TB_Nmea2000 WHERE Pgn=' + modele_grille_principale.get(i).pgn + ' AND Actif=1 ';
//console.log("Requête = [ " + req + "; ]");
res = bdd.requete_unique(req);
if (Number(res[0]) === 0) modele_grille_principale.setProperty(i, "valeur_valide", false); // Si aucun PGN/Field pointant sur le même PGN n'est actif, alors la valeur ne sera jamais mise à jour et il vaut mieux ne pas l'afficher...
}
}
}
}
+490
View File
@@ -0,0 +1,490 @@
//========================================================================================================================================================================
// CONFIG_RESEAU_ONE_WIRE.QML
// Ce fichier traite la configuration des devices One Wire. La page permet notamment de :
// - Afficher tous les capteurs déjà configurés (présents ou absent mais utilisés sur un capteur)
// - Déclencher la recherche de nouveaux capteurs OW
// La page est appelée depuis la page PARAMETRES_AVANCES.
//========================================================================================================================================================================
import QtQuick 2.12
import QtQuick.Controls 2.12
import qt.koriolan.shipheart 1.0
Shipheart_page {
id: page_config_reseau_one_wire
// Mis en forme :
readonly property double affichage_hauteur_utile: shipheart_hauteur_page - shipheart_hauteur_bandeau_haut - shipheart_hauteur_bandeau_bas
readonly property double pourcentage_largeur_labels_formulaire: 0.5
readonly property int nombre_capteurs_affiches_sur_une_colonne: 8
readonly property int nombre_sondes_affichees_sur_une_colonne: 8
readonly property double pourcentage_largeur_colonne_gauche: 0.75
readonly property int espace_colonnes_principales: 20
readonly property double pourcentage_bandeau_bas_largeur_zone_retour: 1.0
readonly property int periode_init_raffraichissement_parametres: 1 // En ms
readonly property int periode_raffraichissement_parametres: 1000 // En ms /!\ Laisser à 1s afin que le compteur secondes fonctionne correctement...
readonly property int periode_decouverte_reseau_ow: 10 // En secondes
// Affichage :
property int niveau_page: 0
property int defaut_bus_ow: 0
property int compteur_secondes: periode_decouverte_reseau_ow // Cette init permet de débuter par une découverte réseau
// 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();
}
if (stackView.depth === (niveau_page + 1)) // Désactive le raffraichissement quand la page n'est pas activée dans la pile
{
compteur_secondes ++;
if (compteur_secondes >= periode_decouverte_reseau_ow)
{
compteur_secondes = 0;
demande_decouverte_ow(); // On fait une découverte OW toutes les 10 secondes tant qu'on est sur cette page (et uniquement quand on est sur cette page !)
}
else
{
if (compteur_secondes >= 2) mise_a_jour_donnees(); // La mise à jour des données se fait toutes les secondes, sauf entre 0 et 2s (pour laisser le temps à la découverte réseau de se terminer et ainsi éviter les affichages tronqués)
}
}
}
}
// Titre de la page :
Shipheart_titre_1 {titre:qsTr("Réseau One Wire"); hauteur: shipheart_hauteur_bandeau_haut}
//-------------------------------------------------------
// Ecran de gauche (liste des capteurs) :
//-------------------------------------------------------
Rectangle {
id: zone_affichage_gauche
x: 0
y: shipheart_hauteur_bandeau_haut;
width: (shipheart_largeur_page * pourcentage_largeur_colonne_gauche) - (espace_colonnes_principales / 2)
height: affichage_hauteur_utile
color: "transparent"
//-------------------------------------------------------------------
// Grille pour afficher les devices :
GridView {
id: grille_principale
width: parent.width
height: affichage_hauteur_utile - (affichage_hauteur_utile / nombre_sondes_affichees_sur_une_colonne)
cellWidth: parent.width;
cellHeight: affichage_hauteur_utile / nombre_sondes_affichees_sur_une_colonne
snapMode: GridView.SnapToRow // L'arret de l'affichage se fera sur une rangée entière
clip: true // Nécessaire pour que les Delegates s'arretent à l'intérieur de la GridView
model: modele_grille_principale
visible: (defaut_bus_ow === 0)
delegate: Rectangle {
width: grille_principale.cellWidth
height: grille_principale.cellHeight
color: "transparent"
Rectangle {
width: parent.width - 10
height: parent.height - 10
anchors.horizontalCenter: parent.horizontalCenter
anchors.verticalCenter: parent.verticalCenter
radius: shipheart_rayon_bouton
color: (erreur === 0) ? shipheart_couleur_fond_bouton : couleur_rouge_transparent
border.color: couleur_blanc_casse
border.width: ((affecte === 0) && (erreur === 0)) ? 1 : 0
Text {
anchors.fill: parent
horizontalAlignment: Qt.AlignLeft
verticalAlignment: Qt.AlignTop
topPadding: 10
leftPadding: 20
text: "ID : " + rom_code
color: ((affecte === 1) && (erreur === 0)) ? couleur_gris_moyen : couleur_blanc_casse
font.pixelSize: 14
wrapMode: Label.Wrap
}
Text {
anchors.fill: parent
horizontalAlignment: Qt.AlignLeft
verticalAlignment: Qt.AlignTop
topPadding: 10
leftPadding: parent.width / 2
text: device_family + " [" + device_family_description + "]"
color: ((affecte === 1) && (erreur === 0)) ? couleur_gris_moyen : couleur_blanc_casse
font.pixelSize: 14
wrapMode: Label.Wrap
}
Text {
anchors.fill: parent
horizontalAlignment: Qt.AlignLeft
verticalAlignment: Qt.AlignBottom
bottomPadding: 10
leftPadding: 20
text: nom
color: ((affecte === 1) && (erreur === 0)) ? couleur_gris_moyen : couleur_blanc_casse
font.pixelSize: 18
font.weight: Font.Bold
wrapMode: Label.Wrap
}
Text {
anchors.fill: parent
horizontalAlignment: Qt.AlignLeft
verticalAlignment: Qt.AlignBottom
bottomPadding: 10
leftPadding: parent.width / 2
text: emplacement
color: ((affecte === 1) && (erreur === 0)) ? couleur_gris_moyen : couleur_blanc_casse
font.pixelSize: 14
wrapMode: Label.Wrap
}
Text {
anchors.fill: parent
horizontalAlignment: Qt.AlignRight
verticalAlignment: Qt.AlignVCenter
rightPadding: 20
text: valeur
color: ((affecte === 1) && (erreur === 0)) ? couleur_gris_moyen : couleur_blanc_casse
font.pixelSize: 18
font.bold: true
// wrapMode: Label.Wrap
}
}
MouseArea { // Zone d'interaction. On est obligé de la mettre dans la grille pour que Rectangle et MouseArea soient frères ou parent/enfant ET qu'on n'occupe qu'un espace dans la grille.
anchors.fill: parent
onPressAndHold: {
shipheart_clic();
stackView.push("config_reseau_one_wire_modification.qml", {"rom_code": rom_code});
}
onClicked: {
shipheart_clic();
if (affecte === 0) stackView.push("config_capteur.qml", {"type_entrees": shipheart_capteur_type_entrees_ow, "entrees": rom_code});
}
}
}
ListModel {
id: modele_grille_principale
}
Label {
anchors.fill: parent
horizontalAlignment: Qt.AlignHCenter
verticalAlignment: Qt.AlignVCenter
visible: parent.count === 0
text: qsTr("Aucune sonde n'est encore installée...")
color: couleur_rouge_transparent
font.pixelSize: 15
font.bold: true
wrapMode: Label.Wrap
}
}
Rectangle {
id: message_defaut_bus_ow
anchors.fill: parent
color: "transparent"
border.width: 3
border.color: couleur_orange
radius: shipheart_rayon_bouton
visible: (defaut_bus_ow !== 0)
Text { // Affiche parfaitement 1, 2 ou 3 lignes de texte. A partir de 4 et 5, ça mort sur le contour du rectangle mais c'est encore lisible.
id: etat_circuit
width: parent.width - anchors.leftMargin;
wrapMode: Text.Wrap
horizontalAlignment: Text.AlignHCenter
text: qsTr("Le bus One Wire est actuellement détecté en court-circuit. <br />Cet état ne présente aucun danger mais l'empêche de fonctionner. <br /><br />Vérifier le câblage correspondant jusqu'à ce que ce message disparaisse.");
anchors {
horizontalCenter: parent.horizontalCenter
verticalCenter: parent.verticalCenter
}
font.pixelSize: 18
color: couleur_blanc_casse
}
}
}
//-------------------------------------------------------
// Ecran de droite (liste des sondes de température) :
//-------------------------------------------------------
Rectangle {
id: zone_affichage_droite
x: (shipheart_largeur_page * pourcentage_largeur_colonne_gauche) + (espace_colonnes_principales / 2)
y: shipheart_hauteur_bandeau_haut;
width: (shipheart_largeur_page * (1 - pourcentage_largeur_colonne_gauche)) - (espace_colonnes_principales / 2)
height: affichage_hauteur_utile
color: "transparent"
}
//-------------------------------------------------------
// Bandeau bas :
//-------------------------------------------------------
Row {
x: 0
y: shipheart_hauteur_page - shipheart_hauteur_bandeau_bas;
width: shipheart_largeur_page;
height: shipheart_hauteur_bandeau_bas;
padding: 5
spacing: 5
//.................................................
// Bouton retour
//.................................................
Rectangle {
id: bandeau_bas_zone_retour
width: (parent.width * pourcentage_bandeau_bas_largeur_zone_retour) - (2 * parent.padding)
height: shipheart_hauteur_bandeau_bas - parent.topPadding - parent.bottomPadding;
radius: shipheart_rayon_bouton
color: shipheart_couleur_fond_bouton
Image {
source: "/images/icone_retour.png"
sourceSize.height: 30
anchors {
verticalCenter: parent.verticalCenter
horizontalCenter: parent.horizontalCenter
}
}
MouseArea { // Zone d'interaction. On est obligé de la mettre dans la grille pour que Rectangle et MouseArea soient frères ou parent/enfant ET qu'on n'occupe qu'un espace dans la grille.
id: mouse_area_bandeau_bas_zone_retour
anchors.fill: parent
onClicked: {
shipheart_clic();
stackView.pop()
}
}
}
}
// 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'.
// Principes :
// L'état de chaque device est caractérisé par 3 indicateurs :
// - Affectation : indique si le device est utilisé par un capteur (ou un équipement en général).
// - si 0 : le device n'est PAS utilisé
// - si 1 : le device est utilisé
// - Présence : indique si le device est physiquement présent (et donc qu'il a communiqué correctement lors de la dernière communication de la part du Compagnon)
// - si 0 : le device est absent
// - si 1 : le device est présent
// - Etat : indique l'état du device lui-même ou, par extension, l'état du réseau.
// - si 0 : le device fonctionne correctement
// - si -1 : le device comporte une erreur interne
// - si -2 : l'ensemble du bus est en erreur (cf alors également 'Defaut_bus_ow' dans TB_Système)
//
// Dans TB_One_wire, il n'y a JAMAIS d'entrée avec Présence=0 et Affectation=0. En effet, dans ce cas, l'entrée est supprimée.
//
// L'indicateur d'erreur est calculé comme la synthèse entre 'Etat<0' OU 'Affecté=1 ET Présence=0'. Si l'une de ces condition est vraie, l'indicateur d'erreur est vrai.
// Au-delà de 'erreur' qui prévaut pour l'affichage, on utilise 'Affectation' pour indiquer si le device est occupé ou pas.
//
// En synthèse, on gère 3 états distincts pour un device :
// 1. Erreur=0 et Affecté=1 : cas normal d'un device utilisé
// 2. Erreur=0 et Affecté=0 : cas normal d'un device non utilisé
// 3. Erreur=1 : cas d'un device en erreur (et a priori, utilisé car sinon, il est peu probabe qu'il soit dans la liste)
// A noter qu'un 4ème état existe mais il est global : si TB_Systeme.defaut_bus_ow=1, une page dédiée est affichée sur tout l'écran pour indiquer un défaut bus global.
//=============================================================================================================
// Init de la page
//=============================================================================================================
function init_page()
{
niveau_page = stackView.depth - 1; // Permet de stocker le niveau de la page dans la Stackview
mise_a_jour_donnees();
}
//=============================================================================================================
// Mise à jour des données
//=============================================================================================================
function mise_a_jour_donnees()
{
var req, res;
var i;
var nb_sondes;
var device_family;
var device_family_description;
var erreur;
//=======================================================================
// Grille principale (liste des sondes de température configurées) :
modele_grille_principale.clear(); // RAZ des valeurs existantes
req = 'SELECT Valeur FROM TB_Systeme WHERE Nom="Defaut_bus_ow" ';
res = bdd.requete_unique(req);
defaut_bus_ow = Number(res[0]);
if (defaut_bus_ow === 0) // Si un défaut de bus OW est en cours, il est inutile d'afficher quoi que ce soit...
{
req = 'SELECT ';
req += ' Rom_code, ';
req += ' CONV(substring_index(Rom_code,":",1), 16, 10) AS Type, ' // Extrait le premier octet du Rom_Code qui contient le Device-Family puis le convertit de Hexa en Décimal
req += ' Affecte, ';
req += ' Presence, ';
req += ' Etat, ';
req += ' Valeur, ';
req += ' Emplacement ';
req += ' FROM TB_One_wire ';
req += ' ORDER BY Rom_code';
nb_sondes = bdd.requete(req);
//console.log("Requête =" + req + " et nb réponses = " + nb_sondes);
for (i = 0; i < nb_sondes; i ++)
{
res = bdd.lecture_ligne();
if (!res.isEmpty)
{
// Recherche du type de device en fonction de ROM_CODE_0 :
switch(Number(res[1]))
{
case 0x01: device_family = "DS1990A, DS1990R, DS2401, DS2411"; device_family_description= "Serial number"; break;
case 0x02: device_family = "DS1991"; device_family_description= "Secure memory"; break;
case 0x04: device_family = "DS2404"; device_family_description= "EconoRAM time chip"; break;
case 0x05: device_family = "DS2405"; device_family_description= "Addressable switch"; break;
case 0x06: device_family = "DS1993"; device_family_description= "4Kb Memory button"; break;
case 0x08: device_family = "DS1992"; device_family_description= "1kb memory button"; break;
case 0x0A: device_family = "DS1995"; device_family_description= "16kb memory button"; break;
case 0x0B: device_family = "DS1985, DS2505"; device_family_description= "16kb add-only memory"; break;
case 0x0C: device_family = "DS1996"; device_family_description= "64kb memory button"; break;
case 0x0F: device_family = "DS1986, DS2506"; device_family_description= "64kb add-only memory"; break;
case 0x10: device_family = "DS18S20"; device_family_description= "Temperature sensor"; break;
case 0x12: device_family = "DS2406, DS2407"; device_family_description= "Dual addressable switch"; break;
case 0x14: device_family = "DS1971, DS2430A"; device_family_description= "256b EEPROM"; break;
case 0x16: device_family = "DS1954, DS1957"; device_family_description= "Coprocessor ibutton"; break;
case 0x18: device_family = "DS1962, DS1963S"; device_family_description= "4kb monetary device with SHA"; break;
case 0x1A: device_family = "DS1963L"; device_family_description= "4kb monetary device"; break;
case 0x1B: device_family = "DS2436"; device_family_description= "Battery ID/monitor"; break;
case 0x1C: device_family = "DS28E04-100"; device_family_description= "4kb EEPROM with PIO"; break;
case 0x1D: device_family = "DS2423"; device_family_description= "4kb 1Wire RAM with counter"; break;
case 0x1E: device_family = "DS2437"; device_family_description= "Smart battery monitor IC"; break;
case 0x1F: device_family = "DS2409"; device_family_description= "Microlan coupler"; break;
case 0x20: device_family = "DS2450"; device_family_description= "Quad ADC"; break;
case 0x21: device_family = "DS1921G, DS1921H, DS1921Z"; device_family_description= "Thermochron loggers"; break;
case 0x22: device_family = "DS1822"; device_family_description= "Econo digital thermometer"; break;
case 0x23: device_family = "DS1973, DS2433"; device_family_description= "4kb EEPROM"; break;
case 0x24: device_family = "DS2415"; device_family_description= "Time chip"; break;
case 0x26: device_family = "DS2438"; device_family_description= "Smart battery monitor"; break;
case 0x27: device_family = "DS2417"; device_family_description= "Time chip"; break;
case 0x28: device_family = "DS18B20"; device_family_description= "Temperature sensor"; break; // Cas des DS18B20
case 0x29: device_family = "DS2408"; device_family_description= "8-channel switch"; break;
case 0x2C: device_family = "DS2890"; device_family_description= "Digital potentiometer"; break;
case 0x2D: device_family = "DS1972, DS2431"; device_family_description= "1024b memory"; break;
case 0x2E: device_family = "DS2770"; device_family_description= "Battery monitor/charge controller"; break;
case 0x30: device_family = "DS2760"; device_family_description= "Precision li+ battery monitor"; break;
case 0x31: device_family = "DS2720"; device_family_description= "Single cell li+ protection IC"; break;
case 0x32: device_family = "DS2780"; device_family_description= "Fuel gauge IC"; break;
case 0x33: device_family = "DS1961S, DS2432"; device_family_description= "1kb memory with SHA"; break;
case 0x34: device_family = "DS2703"; device_family_description= "Sha battery authentication"; break;
case 0x35: device_family = "DS2755"; device_family_description= "Fuel gauge"; break;
case 0x36: device_family = "DS2740"; device_family_description= "Coulomb counter"; break;
case 0x37: device_family = "DS1977"; device_family_description= "32kb memory"; break;
case 0x3D: device_family = "DS2781"; device_family_description= "Fuel gauge IC"; break;
case 0x3A: device_family = "DS2413"; device_family_description= "Two-channel switch"; break;
case 0x3B: device_family = "DS1825, MAX31826, MAX31850"; device_family_description= "Temperature sensor, TC reader"; break;
case 0x41: device_family = "DS1923, DS1922E, DS1922L, DS1922T"; device_family_description= "Hygrochrons"; break;
case 0x42: device_family = "DS28EA00"; device_family_description= "Digital thermometer with sequence detect"; break;
case 0x43: device_family = "DS28EC20"; device_family_description= "20kb memory"; break;
case 0x44: device_family = "DS28E10"; device_family_description= "Sha1 authenticator"; break;
case 0x51: device_family = "DS2751"; device_family_description= "Battery fuel gauge"; break;
case 0x7E: device_family = "EDS00xx"; device_family_description= "EDS sensor adapter"; break;
case 0x81: device_family = "USBID, DS1420"; device_family_description= "ID"; break;
case 0x82: device_family = "DS1425"; device_family_description= "ID and pw protected RAM"; break;
default: device_family = "<i>" + qsTr("Type inconnu") + "</i>"; break;
}
erreur = 0;
if (Number(res[4]) < 0) erreur = 1;
else
{
if ((Number(res[2]) === 1) && (Number(res[3]) === 0)) erreur = 1;
}
//console.log("Sonde OW trouvée : ROM_Code = [" + res[0] + "] donc Device_type = " + device_family_description);
modele_grille_principale.append({
"rom_code": res[0],
"nom": qsTr("Sonde non affectée"),
"device_family": device_family,
"device_family_description": device_family_description,
"affecte": Number(res[2]),
"presence": Number(res[3]),
"erreur": erreur,
"valeur": Number(res[5]).toFixed(2) + " °C",
"emplacement": res[6] });
console.log("ID:" + res[0] + " est " + Number(res[2]));
}
}
// Mise à jour du nom des capteurs utilisant ces sondes :
for (i = 0; i < modele_grille_principale.count; i ++)
{
if (modele_grille_principale.get(i).affecte > 0)
{
req = 'SELECT Nom FROM TB_Capteurs WHERE Adresse_module=0 AND Type_entrees=' + shipheart_capteur_type_entrees_ow + ' AND Entrees="' + modele_grille_principale.get(i).rom_code + '" ';
//console.log("Requête = [ " + req + "; ]");
var nb = bdd.requete(req);
if (nb > 0)
{
res = bdd.lecture_ligne();
modele_grille_principale.setProperty(i, "nom", res[0]);
}
}
}
}
}
//=============================================================================================================
// Demande de découverte OW
//=============================================================================================================
function demande_decouverte_ow()
{
var req, res;
var i;
var nb;
req = 'UPDATE TB_Systeme SET Valeur=1 WHERE Nom="Demande_decouverte_ow" ';
//console.log("req =" + req);
bdd.requete_unique(req);
}
}
+264
View File
@@ -0,0 +1,264 @@
//========================================================================================================================================================================
// CONFIG_RESEAU_ONE_WIRE_MODIFICATION.QML
// Ce fichier traite la configuration d'un device OW
// La page est appelée depuis la page config_reseau_one_wire.qml, en cliquant sur un device
//========================================================================================================================================================================
import QtQuick 2.12
import QtQuick.Controls 2.12
import QtQml.Models
import qt.koriolan.shipheart 1.0
Shipheart_page {
id: page_config_reseau_one_wire_modification
// Mis en forme :
readonly property double pourcentage_largeur_colonne_gauche: 0.75
readonly property double espace_colonnes_principales: 20
readonly property double pourcentage_hauteur_ligne_haut: 1
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 hauteur_ligne_formulaire: 60
readonly property double pourcentage_largeur_labels_formulaire: 0.4
readonly property int hauteur_bouton_droit: 80
readonly property double pourcentage_bandeau_bas_largeur_zone_retour: 1
readonly property int periode_init_raffraichissement_parametres: 1 // En ms
readonly property int periode_raffraichissement_parametres: 1000 // En ms
// Affichage :
// Données :
property string rom_code;
// 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 {
}
}
}
// Titre de la page :
Shipheart_titre_1 {titre:qsTr("Configuration de la sonde [%1]").arg(rom_code); 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);
color: "transparent"
ListView {
id: grille_principale
width: parent.width
height: parent.height
snapMode: GridView.SnapToRow // L'arret de l'affichage se fera sur une rangée entière
clip: true // Nécessaire pour que les Delegates s'arretent à l'intérieur de la GridView
model: modele_grille_principale
footer: Rectangle { // Permet de dégager la place suffisante en fin de GridView pour accéder même au dernier élément lorsque que clavier est déplié
height: shipheart_hauteur_clavier
color: "transparent"
}
ObjectModel {
id: modele_grille_principale
//-------------------------------------------------------------------------------
// Ligne de formulaire : Emplacement
Rectangle {
width: (shipheart_largeur_page * pourcentage_largeur_colonne_gauche) - (espace_colonnes_principales / 2)
implicitHeight: champ_formulaire_Emplacement.implicitHeight
color: "transparent"
Shipheart_formulaire_label {
text: qsTr("Emplacement de la sonde")
width: parent.width * pourcentage_largeur_labels_formulaire
}
Shipheart_formulaire_textarea {
id: champ_formulaire_Emplacement
width: parent.width * (1 - pourcentage_largeur_labels_formulaire)
horizontalAlignment: Text.AlignJustify
wrapMode: Label.Wrap
}
}
//-------------------------------------------------------------------------------
// Ligne d'extension pour laisser la liste remonter au-dessus de l'écran si besoin :
Rectangle {
width: parent.width
height: 100
color: "transparent"
}
}
}
}
//-------------------------------------------------------
// 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 * pourcentage_hauteur_ligne_haut) - (espace_lignes_principales / 2);
color: "transparent"
Column {
anchors.fill: parent
spacing: 10
// Bouton Enregistrer :
Rectangle {
width: parent.width
height: hauteur_bouton_droit
color: shipheart_couleur_fond_bouton
radius: shipheart_rayon_bouton
// visible: !clavier.active // SDIR #255
Text {
anchors {
verticalCenter: parent.verticalCenter
horizontalCenter: parent.horizontalCenter
}
font.pixelSize: 20
text: qsTr("Enregistrer")
color: couleur_blanc_casse
}
MouseArea {
anchors.fill: parent
onClicked: {
shipheart_clic();
mise_a_jour();
stackView.pop();
}
}
}
}
}
//-------------------------------------------------------
// 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 * pourcentage_bandeau_bas_largeur_zone_retour) - (2 * parent.padding)
height: shipheart_hauteur_bandeau_bas - parent.topPadding - parent.bottomPadding;
radius: shipheart_rayon_bouton
color: shipheart_couleur_fond_bouton
Image {
source: "/images/icone_retour.png"
sourceSize.height: 30
anchors {
verticalCenter: parent.verticalCenter
horizontalCenter: parent.horizontalCenter
}
}
MouseArea { // Zone d'interaction. On est obligé de la mettre dans la grille pour que Rectangle et MouseArea soient frères ou parent/enfant ET qu'on n'occupe qu'un espace dans la grille.
id: mouse_area_bandeau_bas_zone_retour
anchors.fill: parent
onClicked: {
shipheart_clic();
stackView.pop()
}
}
}
}
// 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 nb;
var i;
//console.log("Rom_code = " + rom_code)
//---------------------------------------------------------------------------------------
// Emplacement :
req = 'SELECT Emplacement FROM TB_One_wire WHERE Rom_code="' + rom_code + '" ';
//console.log("Requête = [ " + req + "; ]")
res = bdd.requete_unique(req);
champ_formulaire_Emplacement.text = res[0];
}
//=============================================================================================================
// Mise à jour des valeurs saisies
//=============================================================================================================
function mise_a_jour()
{
var req;
var val;
var texte_emplacement;
var texte_commentaires;
texte_emplacement = champ_formulaire_Emplacement.text.replace(/\"/g, "\\\"");
//console.log("Emplacement = [" + champ_formulaire_Emplacement.text + "] et texte modifié = [" + texte_emplacement + "]")
req = 'UPDATE TB_One_wire SET Emplacement="' + texte_emplacement + '" WHERE Rom_code="' + rom_code + '" ';
//console.log("Requête = [ " + req + "; ]")
bdd.requete_unique(req);
}
}
+555
View File
@@ -0,0 +1,555 @@
//========================================================================================================================================================================
// CONFIG_RESEAU_WIFI_AP.QML
// Ce fichier traite la configuration du Wifi en mode AP
// La page est appelée depuis la page parametres_avances.qml
//========================================================================================================================================================================
import QtQuick 2.12
import QtQml.Models
import qt.koriolan.shipheart 1.0
Shipheart_page {
id: page_config_reseau_wifi_ap
// Mis en forme :
readonly property double pourcentage_largeur_colonne_gauche: 0.75
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 hauteur_ligne_formulaire: 60
readonly property double pourcentage_largeur_labels_formulaire: 0.4
readonly property int hauteur_bouton_droit: 80
readonly property double pourcentage_bandeau_bas_largeur_zone_retour: 1
readonly property int periode_init_raffraichissement_parametres: 1 // En ms
readonly property int periode_raffraichissement_parametres: 1000 // En ms
// Affichage :
property int protection_mode: 0
property bool flag_init: true // Permet de bloquer certains affichage tant que l'init n'est pas terminée
// Données :
property string ip_ap: ""
// 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();
}
}
}
// Titre de la page :
Shipheart_titre_1 {titre:qsTr("Configuration Wifi en mode AP"); 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 - espace_lignes_principales;
color: "transparent"
ListView {
id: grille_principale
width: parent.width
height: parent.height
snapMode: GridView.SnapToRow // L'arret de l'affichage se fera sur une rangée entière
clip: true // Nécessaire pour que les Delegates s'arretent à l'intérieur de la GridView
model: modele_grille_principale
footer: Rectangle { // Permet de dégager la place suffisante en fin de GridView pour accéder même au dernier élément lorsque que clavier est déplié
height: shipheart_hauteur_clavier
color: "transparent"
}
ObjectModel {
id: modele_grille_principale
//-------------------------------------------------------------------------------
// Ligne de formulaire : Ap_Commande
Rectangle {
width: parent.width
height: visible ? hauteur_ligne_formulaire : 0
color: "transparent"
//visible: false
Shipheart_formulaire_label {
text: qsTr("Etat")
width: parent.width * pourcentage_largeur_labels_formulaire
}
Shipheart_formulaire_combobox {
id: champ_formulaire_Ap_commande
model: ListModel { }
width: parent.width * (1 - pourcentage_largeur_labels_formulaire)
onActivated: {
shipheart_clic();
}
}
Shipheart_formulaire_ligne_separation {
haut_bas_hautbas: shipheart_formulaire_ligne_separation_basse
}
}
//-------------------------------------------------------------------------------
// Ligne de formulaire : AP_SSID
Rectangle {
width: (shipheart_largeur_page * pourcentage_largeur_colonne_gauche) - (espace_colonnes_principales / 2)
height: visible ? hauteur_ligne_formulaire : 0
color: "transparent"
visible: !flag_init && (champ_formulaire_Ap_commande.model.get(champ_formulaire_Ap_commande.currentIndex).valeur === 1)
Shipheart_formulaire_label {
text: qsTr("Nom du réseau (SSID)")
width: parent.width * pourcentage_largeur_labels_formulaire
}
Shipheart_formulaire_textfield {
id: champ_formulaire_Ap_ssid
width: parent.width * (1 - pourcentage_largeur_labels_formulaire)
x: parent.width * pourcentage_largeur_labels_formulaire
}
Shipheart_formulaire_ligne_separation {
haut_bas_hautbas: shipheart_formulaire_ligne_separation_basse
}
}
//-------------------------------------------------------------------------------
// Ligne de formulaire : AP_PWD
Rectangle {
width: (shipheart_largeur_page * pourcentage_largeur_colonne_gauche) - (espace_colonnes_principales / 2)
height: visible ? hauteur_ligne_formulaire : 0
color: "transparent"
visible: !flag_init && (champ_formulaire_Ap_commande.model.get(champ_formulaire_Ap_commande.currentIndex).valeur === 1)
Shipheart_formulaire_label {
text: qsTr("Mot de passe d'accès au réseau")
width: parent.width * pourcentage_largeur_labels_formulaire
}
Shipheart_formulaire_textfield {
id: champ_formulaire_Ap_pwd
width: parent.width * (1 - pourcentage_largeur_labels_formulaire)
x: parent.width * pourcentage_largeur_labels_formulaire
}
Shipheart_formulaire_ligne_separation {
haut_bas_hautbas: shipheart_formulaire_ligne_separation_basse
}
}
//-------------------------------------------------------------------------------
// Ligne de formulaire : AP_Canal
Rectangle {
width: parent.width
height: visible ? hauteur_ligne_formulaire : 0
color: "transparent"
visible: !flag_init && (champ_formulaire_Ap_commande.model.get(champ_formulaire_Ap_commande.currentIndex).valeur === 1)
//visible: false
Shipheart_formulaire_label {
text: qsTr("Canal")
width: parent.width * pourcentage_largeur_labels_formulaire
}
Shipheart_formulaire_combobox {
id: champ_formulaire_Ap_canal
model: ListModel { }
width: parent.width * (1 - pourcentage_largeur_labels_formulaire)
onActivated: {
shipheart_clic();
}
}
Shipheart_formulaire_ligne_separation {
haut_bas_hautbas: shipheart_formulaire_ligne_separation_basse
}
}
//-------------------------------------------------------------------------------
// Ligne de formulaire : Ip_AP
Rectangle {
width: (shipheart_largeur_page * pourcentage_largeur_colonne_gauche) - (espace_colonnes_principales / 2)
height: visible ? hauteur_ligne_formulaire : 0
color: "transparent"
visible: !flag_init && (champ_formulaire_Ap_commande.model.get(champ_formulaire_Ap_commande.currentIndex).valeur === 1)
//visible: false
Shipheart_formulaire_label {
text: qsTr("Adresse IP")
width: parent.width * pourcentage_largeur_labels_formulaire
}
Shipheart_formulaire_valeur_fixe {
text: (ip_ap === shipheart_adresse_ip_inconnue) ? qsTr("Non connecté") : ip_ap
width: parent.width * (1 - pourcentage_largeur_labels_formulaire)
x: parent.width * pourcentage_largeur_labels_formulaire
padding: 10
}
Shipheart_formulaire_ligne_separation {
haut_bas_hautbas: shipheart_formulaire_ligne_separation_basse
}
}
}
}
}
//-------------------------------------------------------
// 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 - (espace_lignes_principales / 2);
color: "transparent"
Column {
anchors.fill: parent
spacing: 10
// Bouton Enregistrer :
Rectangle {
width: parent.width
height: hauteur_bouton_droit
color: shipheart_couleur_fond_bouton
radius: shipheart_rayon_bouton
// visible: !clavier.active // SDIR #255
Text {
anchors {
verticalCenter: parent.verticalCenter
horizontalCenter: parent.horizontalCenter
}
font.pixelSize: 20
text: qsTr("Enregistrer")
color: couleur_blanc_casse
}
MouseArea {
anchors.fill: parent
onClicked: {
shipheart_clic();
var texte_erreur = check_saisie();
if (texte_erreur === "")
{
enregistrement(); // Mise à jour des données saisies dans la BDD et reconfiguration par l'application pour prise en compte.
stackView.pop(); // Retour aux Paramètres Avancés
}
else
{
boite_validation.niveau = 2
boite_validation.imageTitreSource = "/images/attention.png"
boite_validation.message = texte_erreur;
boite_validation.open();
}
}
}
}
}
}
//-------------------------------------------------------
// 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 * pourcentage_bandeau_bas_largeur_zone_retour) - (2 * parent.padding)
height: shipheart_hauteur_bandeau_bas - parent.topPadding - parent.bottomPadding;
radius: shipheart_rayon_bouton
color: shipheart_couleur_fond_bouton
Image {
source: "/images/icone_retour.png"
sourceSize.height: 30
anchors {
verticalCenter: parent.verticalCenter
horizontalCenter: parent.horizontalCenter
}
}
MouseArea { // Zone d'interaction. On est obligé de la mettre dans la grille pour que Rectangle et MouseArea soient frères ou parent/enfant ET qu'on n'occupe qu'un espace dans la grille.
id: mouse_area_bandeau_bas_zone_retour
anchors.fill: parent
onClicked: {
shipheart_clic();
stackView.pop()
}
}
}
}
//-------------------------------------------------------
// Boite 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
boutonAnnuler: true
onClicked: {
switch(bouton)
{
case shipheart_boite_dialogue_bouton_1:
break;
case shipheart_boite_dialogue_bouton_2:
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 nb;
var i;
var nom_champ, valeur_champ;
var commande_ap;
var mode;
var ap_canal;
//---------------------------------------------------------------------------------------
// Commande AP :
req = 'SELECT Valeur FROM TB_Configuration WHERE Nom="Wifi_ap_cde" ';
res = bdd.requete_unique(req);
commande_ap = Number(res[0]);
champ_formulaire_Ap_commande.model.clear();
champ_formulaire_Ap_commande.model.append({"valeur": 0, "texte": qsTr("Off") });
champ_formulaire_Ap_commande.model.append({"valeur": 1, "texte": qsTr("On") });
// Positionnement de l'index sur la valeur en cours :
i = recherche_index_valeur_modele(commande_ap, champ_formulaire_Ap_commande.model, "commande_ap");
if (i === -1) champ_formulaire_Ap_commande.currentIndex = recherche_index_valeur_modele(1, champ_formulaire_Ap_commande.model, "commande_ap");
else champ_formulaire_Ap_commande.currentIndex = i;
//---------------------------------------------------------------------------------------
// SSid_AP :
req = 'SELECT Valeur FROM TB_Configuration WHERE Nom="Wifi_ap_ssid" ';
res = bdd.requete_unique(req);
champ_formulaire_Ap_ssid.text = res[0];
//---------------------------------------------------------------------------------------
// Pwd_AP :
req = 'SELECT Valeur FROM TB_Configuration WHERE Nom="Wifi_ap_pwd" ';
res = bdd.requete_unique(req);
champ_formulaire_Ap_pwd.text = res[0];
//---------------------------------------------------------------------------------------
// Canal :
req = 'SELECT Valeur FROM TB_Configuration WHERE Nom="Wifi_ap_canal" ';
res = bdd.requete_unique(req);
ap_canal = Number(res[0]);
champ_formulaire_Ap_canal.model.clear();
var canaux = [];
if (shipheart_generation_shipbase === shipheart_generation_shipbase_v1)
{
for (i=1; i <= 11; i ++) canaux.push(i); // Canaux accessibles en 2.4GHz
}
else if (shipheart_generation_shipbase === shipheart_generation_shipbase_v2)
{
for (i=1; i <= 13; i ++) canaux.push(i); // Canaux accessibles en 2.4GHz
canaux.push(36); // Canaux accessibles en 5GHz (sans "logique" permettant d'éviter l'enregistrement manuel...)
canaux.push(40);
canaux.push(44);
canaux.push(48);
// Les canaux > 48 déclenchent une erreur... Ils apparaissent effectivement avec la mention '(no IR, radar detection)' dans le retour de 'iw list'.
/* canaux.push(52);
canaux.push(56);
canaux.push(60);
canaux.push(64);
canaux.push(100);
canaux.push(104);
canaux.push(108);
canaux.push(112);
canaux.push(116);
canaux.push(120);
canaux.push(124);
canaux.push(128);
canaux.push(132);
canaux.push(136);
canaux.push(140);
*/
}
// Remplissage du modèle :
var freq;
for (const canal of canaux)
{
if (canal < 36) freq = "2.4";
else freq = "5";
champ_formulaire_Ap_canal.model.append({"valeur": canal, "texte": qsTr("Canal %1 - [%2GHz]").arg(canal).arg(freq) });
}
// Positionnement de l'index sur la valeur en cours :
i = recherche_index_valeur_modele(ap_canal, champ_formulaire_Ap_canal.model, "Canal");
if (i === -1) champ_formulaire_Ap_canal.currentIndex = recherche_index_valeur_modele(11, champ_formulaire_Ap_canal.model, "Canal");
else champ_formulaire_Ap_canal.currentIndex = i;
//---------------------------------------------------------------------------------------
// Ip_AP :
req = 'SELECT Valeur FROM TB_Systeme WHERE Nom="Adresse_ip_wifi_ap" ';
res = bdd.requete_unique(req);
ip_ap = res[0];
flag_init = false; // Indique que l'init est terminée
}
//=============================================================================================================
// Mise à jour
//=============================================================================================================
function mise_a_jour()
{
var req, res;
//-------------------------------------------------------------------------------
// Mode AP
//-------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------
// Ip_AP :
req = 'SELECT Valeur FROM TB_Systeme WHERE Nom="Adresse_ip_wifi_ap" ';
res = bdd.requete_unique(req);
ip_ap = res[0];
}
//=============================================================================================================
// Check des valeurs saisies
// Retourne une chaine vide si Ok, le message d'erreur si NOK
//=============================================================================================================
function check_saisie()
{
if (champ_formulaire_Ap_commande.model.get(champ_formulaire_Ap_commande.currentIndex).valeur > 0)
{
var liste_caracteres_autorisés_ssid_ap = "-."; // En plus des chiffres et des lettres majuscules et minuscules
var regex_ssid_ap = new RegExp("(^[0-9A-Za-z]+$)");
var liste_caracteres_autorisés_pwd_ap = "-_./="; // En plus des chiffres et des lettres majuscules et minuscules
var regex_pwd_ap = new RegExp("(^[0-9A-Za-z" + liste_caracteres_autorisés_pwd_ap + "]+$)");
if ((champ_formulaire_Ap_ssid.text.length < 1) || (champ_formulaire_Ap_ssid.text.length > 32)) return(qsTr("Le nom du réseau (SSID) doit comprendre entre 1 et 32 caractères. <br />Merci d'entrer un nom valide."));
if ((champ_formulaire_Ap_pwd.text.length < 8) || (champ_formulaire_Ap_pwd.text.length > 63)) return(qsTr("Le mot de passe d'accès au réseau doit comprendre entre 8 et 63 caractères. <br />Merci d'entrer un mot de passe valide."));
if (! regex_ssid_ap.test(champ_formulaire_Ap_ssid.text)) // S'il y a autre chose que le regex
{
return(qsTr("Le nom du réseau (SSID) doit comprendre uniquement :<br />- des lettres majuscules [A - Z],<br />- des lettres minuscules [a - z],<br />- des chiffres [0 - 9]<br />- les caractères spéciaux [%1].<br /><br />Merci d'entrer un nom valide.").arg(liste_caracteres_autorisés_ssid_ap));
}
if (! regex_pwd_ap.test(champ_formulaire_Ap_pwd.text)) // S'il y a autre chose que le regex
{
return(qsTr("Le mot de passe d'accès au réseau doit comprendre uniquement :<br />- des lettres majuscules [A - Z],<br />- des lettres minuscules [a - z],<br />- des chiffres [0 - 9]<br />- les caractères spéciaux [%1].<br /><br />Merci d'entrer un nom valide.").arg(liste_caracteres_autorisés_pwd_ap));
}
}
return("");
}
//=============================================================================================================
// Mise à jour des valeurs saisies
//=============================================================================================================
function enregistrement()
{
var req;
// Mise à jour des nouvelles valeurs dans la BDD :
req = 'UPDATE TB_Configuration SET Valeur="' + champ_formulaire_Ap_commande.model.get(champ_formulaire_Ap_commande.currentIndex).valeur + '" WHERE Nom="Wifi_ap_cde" ';
bdd.requete_unique(req);
req = 'UPDATE TB_Configuration SET Valeur="' + champ_formulaire_Ap_ssid.text + '" WHERE Nom="Wifi_ap_ssid" ';
bdd.requete_unique(req);
req = 'UPDATE TB_Configuration SET Valeur="' + champ_formulaire_Ap_pwd.text + '" WHERE Nom="Wifi_ap_pwd" ';
bdd.requete_unique(req);
req = 'UPDATE TB_Configuration SET Valeur="' + champ_formulaire_Ap_canal.model.get(champ_formulaire_Ap_canal.currentIndex).valeur + '" WHERE Nom="Wifi_ap_canal" ';
bdd.requete_unique(req);
// Demande de rechargement de la config du Wifi :
req = 'UPDATE TB_Configuration SET Valeur=1 WHERE Nom="Wifi_new_config" ';
bdd.requete_unique(req);
}
//=============================================================================================================
// Recherche de l'index d'une certaine valeur dans un modele
//=============================================================================================================
function recherche_index_valeur_modele(cible, modele, nom_modele)
{
var i;
for (i=0; i<modele.count; i ++)
{
if (modele.get(i).valeur === cible) break; // Cas où la valeur cible a été trouvée parmi les valeurs du modèle
}
if (i === modele.count)
{
console.log("Aucune valeur trouvée pour cible = [" + cible + "] du modèle = " + nom_modele);
i = -1; // Cas où la valeur n'a pas été trouvée parmi les valeurs du modèle
}
//else console.log("Valeur trouvée : [" + i + "] pour cible = [" + cible + "] soit texte = [" + modele.get(i).texte + "]");
return(i);
}
}
+591
View File
@@ -0,0 +1,591 @@
//========================================================================================================================================================================
// CONFIG_RESEAU_WIFI_STA.QML
// Ce fichier traite la configuration du Wifi en mode STA
// La page est appelée depuis la page parametres_avances.qml
//========================================================================================================================================================================
import QtQuick 2.12
import QtQuick.Controls 2.12
import QtQml.Models
import qt.koriolan.shipheart 1.0
Shipheart_page {
id: page_config_reseau_wifi_sta
// Mis en forme :
readonly property double pourcentage_largeur_colonne_gauche: 0.75
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 hauteur_ligne_formulaire: 60
readonly property double pourcentage_largeur_labels_formulaire: 0.4
readonly property int hauteur_bouton_droit: 80
readonly property double pourcentage_bandeau_bas_largeur_zone_retour: 1
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
readonly property int periode_scan_wifi_sta: 15 // en s
// Affichage :
property int protection_mode: 0
property bool flag_init: true // Permet de bloquer certains affichage tant que l'init n'est pas terminée
// Données :
property int valeur_timer_appui_long: 0
property int compteur_scan_wifi_sta: 0
property int stackview_niveau_page: 0
property string bssid: ""
property bool flag_pwd_visible: false
property bool flag_wifi_sta_up: false
// 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
{
if (stackView.depth === stackview_niveau_page) // Permet d'éviter les raffraichissements si on est sur la page de liste des réseaux
{
mise_a_jour();
if (-- compteur_scan_wifi_sta <= 0) // Une demande est faite au démarrage de la page
{
if (flag_wifi_sta_up) demande_de_scan(); // Si le réseau est down, ce n'est pas la peine de faire un scan (yne erreur est émise par le shell dans ce cas mais rien de bloquant)
compteur_scan_wifi_sta = periode_scan_wifi_sta ;
}
}
}
}
}
//===========================================================
// Lorsque la page est sur le point d'être affichée dans la pile :
//===========================================================
StackView.onActivating: {
compteur_scan_wifi_sta = 0;
}
// Titre de la page :
Shipheart_titre_1 {titre:qsTr("Configuration Wifi en mode STA"); 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 - espace_lignes_principales;
color: "transparent"
ListView {
id: grille_principale
width: parent.width
height: parent.height
snapMode: GridView.SnapToRow // L'arret de l'affichage se fera sur une rangée entière
clip: true // Nécessaire pour que les Delegates s'arretent à l'intérieur de la GridView
model: modele_grille_principale
footer: Rectangle { // Permet de dégager la place suffisante en fin de GridView pour accéder même au dernier élément lorsque que clavier est déplié
height: shipheart_hauteur_clavier
color: "transparent"
}
ObjectModel {
id: modele_grille_principale
//-------------------------------------------------------------------------------
// Ligne de formulaire : STA_Commande
Rectangle {
width: parent.width
height: visible ? hauteur_ligne_formulaire : 0
color: "transparent"
//visible: false
Shipheart_formulaire_label {
text: qsTr("Etat")
width: parent.width * pourcentage_largeur_labels_formulaire
}
Shipheart_formulaire_combobox {
id: champ_formulaire_Sta_commande
model: ListModel { }
width: parent.width * (1 - pourcentage_largeur_labels_formulaire)
onActivated: {
shipheart_clic();
}
}
Shipheart_formulaire_ligne_separation {
haut_bas_hautbas: shipheart_formulaire_ligne_separation_basse
}
}
//-------------------------------------------------------------------------------
// Ligne de formulaire : STA_Liste_ssid
Rectangle {
width: parent.width
height: visible ? hauteur_ligne_formulaire : 0
color: "transparent"
visible: !flag_init && (champ_formulaire_Sta_commande.model.get(champ_formulaire_Sta_commande.currentIndex).valeur === 1)
Shipheart_formulaire_label {
text: qsTr("Réseau")
width: parent.width * pourcentage_largeur_labels_formulaire
}
Shipheart_formulaire_combobox {
id: champ_formulaire_Sta_liste_ssid
model: ListModel { }
width: parent.width * (1 - pourcentage_largeur_labels_formulaire)
onActivated: {
shipheart_clic();
// Mémorisation du BSSID choisit (permet que les mise à jour de la liste de scan ne remette pas en cause ce choix) :
bssid = champ_formulaire_Sta_liste_ssid.model.get(champ_formulaire_Sta_liste_ssid.currentIndex).bssid;
// Pré-remplissage du Psk du nouveau réseau sélectionné (s'il était déjà connu) :
champ_formulaire_Sta_psk.text = champ_formulaire_Sta_liste_ssid.model.get(champ_formulaire_Sta_liste_ssid.currentIndex).psk;
// Mise à jour du flag de masquage de Pwd :
flag_pwd_visible = champ_formulaire_Sta_liste_ssid.model.get(champ_formulaire_Sta_liste_ssid.currentIndex).connu;
}
}
Shipheart_formulaire_ligne_separation {
haut_bas_hautbas: shipheart_formulaire_ligne_separation_basse
}
}
//-------------------------------------------------------------------------------
// Ligne de formulaire : STA_PSK
Rectangle {
width: (shipheart_largeur_page * pourcentage_largeur_colonne_gauche) - (espace_colonnes_principales / 2)
height: visible ? hauteur_ligne_formulaire : 0
color: "transparent"
visible: !flag_init &&
(champ_formulaire_Sta_commande.model.get(champ_formulaire_Sta_commande.currentIndex).valeur === 1) &&
(champ_formulaire_Sta_liste_ssid.currentIndex !== 0)
Shipheart_formulaire_label {
text: qsTr("Mot de passe d'accès au réseau")
width: parent.width * pourcentage_largeur_labels_formulaire
}
Shipheart_formulaire_textfield {
id: champ_formulaire_Sta_psk
width: parent.width * (1 - pourcentage_largeur_labels_formulaire)
x: parent.width * pourcentage_largeur_labels_formulaire
password_type: flag_pwd_visible
}
Shipheart_formulaire_ligne_separation {
haut_bas_hautbas: shipheart_formulaire_ligne_separation_basse
}
}
}
}
}
//-------------------------------------------------------
// 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 - (espace_lignes_principales / 2);
color: "transparent"
Column {
anchors.fill: parent
spacing: 10
//.......................................................
// Bouton Enregistrer :
//.......................................................
Rectangle {
width: parent.width
height: hauteur_bouton_droit
color: shipheart_couleur_fond_bouton
radius: shipheart_rayon_bouton
visible: !flag_init
// && !clavier.active // SDIR #255
/* &&
(
(champ_formulaire_Sta_commande.model.get(champ_formulaire_Sta_commande.currentIndex).valeur === 0) // Commande OFF
|| // OU
( (champ_formulaire_Sta_liste_ssid.currentIndex !== 0) // Réseau existant choisit
&& // ET
(champ_formulaire_Sta_psk.text.length > 0) ) // Psk non vide (il sera validé au moment de l'enregistrement pour le contenu)
)
*/
Text {
anchors {
verticalCenter: parent.verticalCenter
horizontalCenter: parent.horizontalCenter
}
font.pixelSize: 20
text: qsTr("Enregistrer")
color: couleur_blanc_casse
}
MouseArea {
anchors.fill: parent
onClicked: {
shipheart_clic();
var texte_erreur = check_saisie();
if (texte_erreur === "")
{
enregistrement(); // Mise à jour des données saisies dans la BDD et reconfiguration par l'application pour prise en compte.
stackView.pop(); // Retour aux Paramètres Avancés
}
else
{
boite_validation.niveau = 2
boite_validation.imageTitreSource = "/images/attention.png"
boite_validation.message = texte_erreur;
boite_validation.open();
}
}
}
}
//.......................................................
// Bouton Liste des réseaux :
//.......................................................
Rectangle {
width: parent.width
height: hauteur_bouton_droit
color: shipheart_couleur_fond_bouton
radius: shipheart_rayon_bouton
visible: !flag_init && !clavier.active
Text {
anchors {
verticalCenter: parent.verticalCenter
horizontalCenter: parent.horizontalCenter
}
font.pixelSize: 20
text: qsTr("Réseaux connus")
color: couleur_blanc_casse
}
MouseArea {
anchors.fill: parent
onClicked: {
shipheart_clic();
stackView.push("config_reseau_wifi_sta_liste.qml");
}
}
}
}
}
//-------------------------------------------------------
// 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 * pourcentage_bandeau_bas_largeur_zone_retour) - (2 * parent.padding)
height: shipheart_hauteur_bandeau_bas - parent.topPadding - parent.bottomPadding;
radius: shipheart_rayon_bouton
color: shipheart_couleur_fond_bouton
Image {
source: "/images/icone_retour.png"
sourceSize.height: 30
anchors {
verticalCenter: parent.verticalCenter
horizontalCenter: parent.horizontalCenter
}
}
MouseArea { // Zone d'interaction. On est obligé de la mettre dans la grille pour que Rectangle et MouseArea soient frères ou parent/enfant ET qu'on n'occupe qu'un espace dans la grille.
id: mouse_area_bandeau_bas_zone_retour
anchors.fill: parent
onClicked: {
shipheart_clic();
stackView.pop()
}
}
}
}
//-------------------------------------------------------
// Boite 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
boutonAnnuler: true
onClicked: {
switch(bouton)
{
case shipheart_boite_dialogue_bouton_1:
break;
case shipheart_boite_dialogue_bouton_2:
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 nb;
var i;
var nom_champ, valeur_champ;
var commande_sta;
var commande_ap;
var mode;
var ap_canal;
stackview_niveau_page = stackView.depth;
//-------------------------------------------------------------------------------
// Mode STA
//-------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------
// Bssid_sta:
req = 'SELECT Valeur FROM TB_Configuration WHERE Nom="BSSID_Wifi_sta" ';
res = bdd.requete_unique(req);
bssid = res[0];
//---------------------------------------------------------------------------------------
// Commande_sta:
req = 'SELECT Valeur FROM TB_Configuration WHERE Nom="Wifi_sta_cde" ';
res = bdd.requete_unique(req);
commande_sta = Number(res[0]);
champ_formulaire_Sta_commande.model.clear();
champ_formulaire_Sta_commande.model.append({"valeur": 0, "texte": qsTr("Off") });
champ_formulaire_Sta_commande.model.append({"valeur": 1, "texte": qsTr("On") });
// Positionnement de l'index sur la valeur en cours :
i = recherche_index_valeur_modele(commande_sta, champ_formulaire_Sta_commande.model, "commande_sta");
if (i === -1) champ_formulaire_Sta_commande.currentIndex = recherche_index_valeur_modele(1, champ_formulaire_Sta_commande.model, "commande_sta");
else champ_formulaire_Sta_commande.currentIndex = i;
mise_a_jour();
flag_init = false; // Indique que l'init est terminée
}
//=============================================================================================================
// Mise à jour
//=============================================================================================================
function mise_a_jour()
{
var req, res;
var nb;
var i;
var index_en_cours;
//---------------------------------------------------------------------------------------
// Wifi STA Up :
req = 'SELECT Valeur FROM TB_Configuration WHERE Nom="Wifi_sta_cde" ';
res = bdd.requete_unique(req);
flag_wifi_sta_up = (Number(res[0]) !== 0);
//---------------------------------------------------------------------------------------
// Sta_liste_ssid :
index_en_cours = 0;
champ_formulaire_Sta_liste_ssid.model.clear();
champ_formulaire_Sta_liste_ssid.model.append({
"texte": qsTr("Aucun réseau..."),
"bssid": "",
"nom_ssid": "",
"psk": "",
"canal_ssid": 0,
"qualite_ssid": 0,
"connu": false });
req = 'SELECT Bssid, Presence, Nom, Psk, Canal, Qualite, Connu FROM TB_Wifi_sta ORDER BY Qualite DESC ';
//console.log("req = [ " + req + " ;]");
nb = bdd.requete(req);
for (i = 0; i < nb; i ++)
{
res = bdd.lecture_ligne();
if (!res.isEmpty)
{
if (bssid === res[0])
{
index_en_cours = champ_formulaire_Sta_liste_ssid.model.count;
if (flag_init) champ_formulaire_Sta_psk.text = res[3]; // On précharge le Psk à l'init (ensuite, c'est uniquement si changement de BSSID)
}
else if (Number(res[1]) === 0) continue; // Si le réseau n'est pas présent et n'est pas celui en cours, on ne le présente pas
champ_formulaire_Sta_liste_ssid.model.append({
"texte": "<table width='95%'><tbody><tr><td colspan='3'><b>" + res[2] + "</b></td></tr> <tr style=\"font-size:14px\"><td>" + res[0] + "</td><td align='center'>" + ((Number(res[4]) < 20) ? "2.4GHz" : "5GHz") + " - Channel " + res[4] + "</td><td align='right'>[" + Number(res[5]) + " %]</td></tr> <tbody></table>",
"bssid": res[0],
"nom_ssid": res[2],
"psk": res[3],
"canal_ssid": Number(res[4]),
"qualite_ssid": Number(res[5]),
"connu": (Number(res[6]) === 1)});
}
else break; // Ne devrait pas arriver....
}
//console.log("index en cours = " + index_en_cours);
champ_formulaire_Sta_liste_ssid.currentIndex = index_en_cours;
flag_pwd_visible = champ_formulaire_Sta_liste_ssid.model.get(index_en_cours).connu;
}
//=============================================================================================================
// Check des valeurs saisies
// Retourne une chaine vide si Ok, le message d'erreur si NOK
//=============================================================================================================
function check_saisie()
{
if (champ_formulaire_Sta_commande.model.get(champ_formulaire_Sta_commande.currentIndex).valeur > 0)
{
// NOTE : la mise en commentaires ci-dessosu vient du fait qu'il y a peu d'intérêt à verrouiller le contenu et la taille du SSID comme du PSK.
// L'idée est en effet que l'on s'adapte à des réseaux existants donc, si les valeurs entrées leur conviennent, à nous aussi...
/* var liste_caracteres_autorisés_ssid_sta = "-. "; // En plus des chiffres et des lettres majuscules et minuscules
var regex_ssid_sta = new RegExp("(^[0-9A-Za-z" + liste_caracteres_autorisés_ssid_sta + "]+$)");
var liste_caracteres_autorisés_pwd_sta = "-_./="; // En plus des chiffres et des lettres majuscules et minuscules
var regex_pwd_sta = new RegExp("(^[0-9A-Za-z" + liste_caracteres_autorisés_pwd_sta + "]+$)");
if ((champ_formulaire_Sta_liste_ssid.model.get(champ_formulaire_Sta_liste_ssid.currentIndex).nom_ssid.length < 1) || (champ_formulaire_Sta_liste_ssid.model.get(champ_formulaire_Sta_liste_ssid.currentIndex).nom_ssid.length > 32)) return(qsTr("Le nom du réseau (SSID) doit comprendre entre 1 et 32 caractères. <br />Merci d'entrer un nom valide."));
if ((champ_formulaire_Sta_psk.text.length < 8) || (champ_formulaire_Sta_psk.text.length > 63)) return(qsTr("Le mot de passe d'accès au réseau doit comprendre entre 8 et 63 caractères. <br />Merci d'entrer un mot de passe valide."));
if (! regex_ssid_sta.test(champ_formulaire_Sta_liste_ssid.model.get(champ_formulaire_Sta_liste_ssid.currentIndex).nom_ssid)) // S'il y a autre chose que le regex
{
return(qsTr("Le nom du réseau (SSID) doit comprendre uniquement :<br />- des lettres majuscules [A - Z],<br />- des lettres minuscules [a - z],<br />- des chiffres [0 - 9]<br />- les caractères spéciaux [%1].<br /><br />Merci d'entrer un nom valide.").arg(liste_caracteres_autorisés_ssid_sta));
}
if (! regex_pwd_sta.test(champ_formulaire_Sta_psk.text)) // S'il y a autre chose que le regex
{
return(qsTr("Le mot de passe d'accès au réseau doit comprendre uniquement :<br />- des lettres majuscules [A - Z],<br />- des lettres minuscules [a - z],<br />- des chiffres [0 - 9]<br />- les caractères spéciaux [%1].<br /><br />Merci d'entrer un nom valide.").arg(liste_caracteres_autorisés_pwd_sta));
}
*/
}
return("");
}
//=============================================================================================================
// Mise à jour des valeurs saisies
//=============================================================================================================
function enregistrement()
{
var req;
// Mise à jour dans TB_Configuration :
req = 'UPDATE TB_Configuration SET Valeur="' + champ_formulaire_Sta_commande.model.get(champ_formulaire_Sta_commande.currentIndex).valeur + '" WHERE Nom="Wifi_sta_cde" ';
bdd.requete_unique(req);
req = 'UPDATE TB_Configuration SET Valeur="' + champ_formulaire_Sta_liste_ssid.model.get(champ_formulaire_Sta_liste_ssid.currentIndex).bssid + '" WHERE Nom="BSSID_Wifi_sta" ';
bdd.requete_unique(req);
// Mise à jour des données du réseau choisit dans TB_Wifi_sta (si le BSSID est autre que AUCUN) :
if (champ_formulaire_Sta_liste_ssid.model.get(champ_formulaire_Sta_liste_ssid.currentIndex).bssid !== "")
{
req = 'UPDATE TB_Wifi_sta SET ';
req += ' Psk="' + champ_formulaire_Sta_psk.text + '", ';
req += ' Connu=1 ';
req += ' WHERE Bssid="' + bssid + '" ';
console.log("req = [ " + req + " ;]");
bdd.requete_unique(req);
}
// Demande de rechargement de la config du Wifi :
req = 'UPDATE TB_Configuration SET Valeur=1 WHERE Nom="Wifi_new_config" ';
bdd.requete_unique(req);
}
//=============================================================================================================
// Demande de scan de Wifi STA
//=============================================================================================================
function demande_de_scan()
{
var req;
// Demande de scan :
req = 'UPDATE TB_Configuration SET Valeur=2 WHERE Nom="Wifi_new_config" ';
bdd.requete_unique(req);
}
//=============================================================================================================
// Recherche de l'index d'une certaine valeur dans un modele
//=============================================================================================================
function recherche_index_valeur_modele(cible, modele, nom_modele)
{
var i;
for (i=0; i<modele.count; i ++)
{
if (modele.get(i).valeur === cible) break; // Cas où la valeur cible a été trouvée parmi les valeurs du modèle
}
if (i === modele.count)
{
console.log("Aucune valeur trouvée pour cible = [" + cible + "] du modèle = " + nom_modele);
i = -1; // Cas où la valeur n'a pas été trouvée parmi les valeurs du modèle
}
//else console.log("Valeur trouvée : [" + i + "] pour cible = [" + cible + "] soit texte = [" + modele.get(i).texte + "]");
return(i);
}
}
+376
View File
@@ -0,0 +1,376 @@
//========================================================================================================================================================================
// CONFIG_RESEAU_WIFI_STA_LISTE.QML
// Ce fichier traite la gestion de la liste des SSID du Wifi en mode STA
// La page est appelée depuis la page config_reseau_wifi_sta.qml
//========================================================================================================================================================================
import QtQuick 2.12
import QtQuick.Controls 2.12
import qt.koriolan.shipheart 1.0
Shipheart_page {
id: page_config_reseau_wifi_sta_liste
// Mis en forme :
readonly property double pourcentage_largeur_colonne_gauche: 0.75
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 hauteur_ligne_reseau: 80
readonly property double pourcentage_largeur_labels_formulaire: 0.4
readonly property int hauteur_bouton_droit: 80
readonly property double pourcentage_bandeau_bas_largeur_zone_retour: 1
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 bool flag_init: true // Permet de bloquer certains affichage tant que l'init n'est pas terminée
// Données :
property int valeur_timer_appui_long: 0
property int compteur_scan_wifi_sta: 0
property string bssid_en_cours: ""
// 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
{
}
}
}
//===========================================================
// 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;
if (mousearea_bouton_oublier_reseau.pressed)
{
oublier_reseaux();
}
}
}
}
// Titre de la page :
Shipheart_titre_1 {titre:qsTr("Liste des réseaux Wifi connus"); 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 - espace_lignes_principales;
color: "transparent"
ListView {
id: grille_principale
width: parent.width
height: parent.height
snapMode: GridView.SnapToRow // L'arret de l'affichage se fera sur une rangée entière
clip: true // Nécessaire pour que les Delegates s'arretent à l'intérieur de la GridView
highlightRangeMode: ListView.StrictlyEnforceRange // Permet de faire défiler la liste jusqu'à ne garder que le dernier élément affiché (et comme la liste des modules physiques est obligée d'utiliser ce paramètre, on fait pareil sur cette liste pour que les deux soient synchro)
model: modele_grille_principale
delegate: Rectangle {
width: zone_affichage_haut_gauche.width
height: hauteur_ligne_reseau
color: "transparent"
Rectangle {
width: parent.width - 10
height: parent.height - 10
anchors.horizontalCenter: parent.horizontalCenter
anchors.verticalCenter: parent.verticalCenter
radius: shipheart_rayon_bouton
color: (bssid_en_cours === model.bssid) ? shipheart_couleur_fond_bouton_actif : shipheart_couleur_fond_bouton
Text {
id: reseau_nom
anchors.fill: parent
horizontalAlignment: Qt.AlignLeft
verticalAlignment: Qt.AlignTop
topPadding: 10
leftPadding: 20
text: nom
color: model.presence ? couleur_blanc_casse : couleur_gris_moyen
font.pixelSize: 22
font.weight: Font.Bold
}
Text {
anchors.fill: parent
horizontalAlignment: Qt.AlignLeft
verticalAlignment: Qt.AlignBottom
bottomPadding: 10
leftPadding: 20
text: model.bssid
color: reseau_nom.color
font.pixelSize: 18
}
Text {
anchors.fill: parent
horizontalAlignment: Qt.AlignRight
verticalAlignment: Qt.AlignVCenter
rightPadding: 20
text: ((model.canal < 20) ? "2.4GHz" : "5GHz") + " - Channel " + model.canal
color: reseau_nom.color
font.pixelSize: 18
}
MouseArea {
anchors.fill: parent
onClicked: {
shipheart_clic();
if (bssid_en_cours === model.bssid) bssid_en_cours = "";
else bssid_en_cours = model.bssid
}
}
}
}
ListModel {
id: modele_grille_principale
}
Label {
anchors.fill: parent
horizontalAlignment: Qt.AlignHCenter
verticalAlignment: Qt.AlignVCenter
visible: parent.count === 0
text: qsTr("Aucun réseau n'a été détecté...")
color: couleur_rouge_transparent
font.pixelSize: 15
font.bold: true
wrapMode: Label.Wrap
}
}
}
//-------------------------------------------------------
// 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 - (espace_lignes_principales / 2);
color: "transparent"
Column {
anchors.fill: parent
spacing: 10
//.......................................................
// Bouton de suppression
//.......................................................
Rectangle {
width: parent.width
height: hauteur_bouton_droit
color: shipheart_couleur_fond_bouton
radius: shipheart_rayon_bouton
visible: true
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_oublier_reseau.pressed
}
Text {
anchors {
verticalCenter: parent.verticalCenter
horizontalCenter: parent.horizontalCenter
}
font.pixelSize: 20
text: (bssid_en_cours === "") ? qsTr("Oublier tous les réseaux") : qsTr("Oublier ce réseau")
color: couleur_blanc_casse
}
MouseArea {
id: mousearea_bouton_oublier_reseau
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;
}
}
}
}
}
//-------------------------------------------------------
// 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 * pourcentage_bandeau_bas_largeur_zone_retour) - (2 * parent.padding)
height: shipheart_hauteur_bandeau_bas - parent.topPadding - parent.bottomPadding;
radius: shipheart_rayon_bouton
color: shipheart_couleur_fond_bouton
Image {
source: "/images/icone_retour.png"
sourceSize.height: 30
anchors {
verticalCenter: parent.verticalCenter
horizontalCenter: parent.horizontalCenter
}
}
MouseArea { // Zone d'interaction. On est obligé de la mettre dans la grille pour que Rectangle et MouseArea soient frères ou parent/enfant ET qu'on n'occupe qu'un espace dans la grille.
id: mouse_area_bandeau_bas_zone_retour
anchors.fill: parent
onClicked: {
shipheart_clic();
stackView.pop()
}
}
}
}
// 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 nb;
var i;
modele_grille_principale.clear(); // RAZ des valeurs existantes
req = 'SELECT Bssid, Presence, Connu, Nom, Psk, Canal, Qualite FROM TB_Wifi_sta WHERE Connu=1 ORDER BY Nom ';
//console.log("req = [ " + req + " ;]");
nb = bdd.requete(req);
for (i = 0; i < nb; i ++)
{
res = bdd.lecture_ligne();
if (!res.isEmpty)
{
modele_grille_principale.append({
"bssid": res[0],
"presence": (Number(res[1]) !== 0),
"connu": (Number(res[2]) !== 0),
"nom": res[3],
"psk": res[4],
"canal": Number(res[5]),
"qualite": Number(res[6]) });
}
else break; // Ne devrait pas arriver....
}
flag_init = false; // Indique que l'init est terminée
}
//=============================================================================================================
// Requête de suppression des réseaux à oublier
//=============================================================================================================
function oublier_reseaux()
{
var req;
var i;
if (bssid_en_cours === "") // Oubli de tous les réseaux
{
req = 'DELETE FROM TB_Wifi_sta ';
bdd.requete_unique(req);
stackView.pop(); // Si on les a tous supprimés, autant revenir au menu précédent
}
else // Oubli du seul réseau sélectionné :
{
req = 'DELETE FROM TB_Wifi_sta WHERE Bssid="' + bssid_en_cours + '" ';
bdd.requete_unique(req);
for (i=0; i<modele_grille_principale.count; i++)
{
if (modele_grille_principale.get(i).bssid === bssid_en_cours)
{
modele_grille_principale.remove(i, 1);
break;
}
}
bssid_en_cours = ""; // Déselection du réseau qui vient d'être supprimé
}
}
}
+1595
View File
File diff suppressed because it is too large Load Diff
+311
View File
@@ -0,0 +1,311 @@
//========================================================================================================================================================================
// CONFIG_RESERVOIRS.QML
// Ce fichier traite la configuration des réservoirs. La page permet notamment de :
// - Afficher, pour tous les réservoirs déjà configurés (et donc d'accéder à la configuration détaillée de l'un d'eux ; ou sa création)
// - Accéder au menu de changement de l'ordre d'affichage sur la page d'accueil
// La page est appelée depuis la page PARAMETRES_AVANCES, en cliquant sur "Réservoirs"
//========================================================================================================================================================================
import QtQuick 2.12
import QtQuick.Controls 2.12
import qt.koriolan.shipheart 1.0
Shipheart_page {
id: page_config_reservoirs
// Mis en forme :
readonly property double affichage_hauteur_utile: shipheart_hauteur_page - shipheart_hauteur_bandeau_haut - shipheart_hauteur_bandeau_bas
readonly property int hauteur_bouton_droit: 80
readonly property double pourcentage_largeur_colonne_gauche: 0.75
readonly property int espace_colonnes_principales: 20
readonly property double pourcentage_bandeau_bas_largeur_zone_retour: 1.0
readonly property int nombre_reservoirs_affiches_sur_une_colonne: 8
readonly property int periode_init_raffraichissement_parametres: 1 // En ms
readonly property int periode_raffraichissement_parametres: 100 // En ms
// Affichage :
property int niveau_page: 0
property bool bouton_bandeau_bas_zone_retour_actif: false
// 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();
}
if (stackView.depth === (niveau_page + 1)) // Désactive le raffraichissement quand la page n'est pas activée dans la pile
{
if (!bouton_bandeau_bas_zone_retour_actif)
{
}
}
}
}
//===========================================================
// Lorsque la page est sur le point d'être affichée dans la pile :
//===========================================================
StackView.onActivating: {
mise_a_jour_donnees(); // Permet que lors du retour sur la page, l'état des différentes variables soit mise à jour avant l'affichage proprement dit.
}
// Titre de la page :
Shipheart_titre_1 {titre:qsTr("Configuration des réservoirs"); hauteur: shipheart_hauteur_bandeau_haut}
//-------------------------------------------------------
// Ecran de gauche (liste des réservoirs) :
//-------------------------------------------------------
Rectangle {
id: zone_affichage_gauche
x: 0
y: shipheart_hauteur_bandeau_haut;
width: (shipheart_largeur_page * pourcentage_largeur_colonne_gauche) - (espace_colonnes_principales / 2)
height: affichage_hauteur_utile
color: "transparent"
//-------------------------------------------------------------------
// Grille pour afficher les réservoirs :
GridView {
id: grille_principale
width: parent.width
height: parent.height
cellWidth: parent.width;
cellHeight: height / nombre_reservoirs_affiches_sur_une_colonne
snapMode: GridView.SnapToRow // L'arret de l'affichage se fera sur une rangée entière
clip: true // Nécessaire pour que les Delegates s'arretent à l'intérieur de la GridView
model: modele_grille_principale
delegate: Rectangle {
width: grille_principale.cellWidth
height: grille_principale.cellHeight
color: "transparent"
Rectangle { // c'est le bouton proprement dit...
height: parent.height - 10
width:parent.width - 10
color: shipheart_couleur_fond_bouton
border.color: couleur_rouge
border.width: (model.etat_systeme === 0) ? 0 : 3
radius: shipheart_rayon_bouton
anchors.horizontalCenter: parent.horizontalCenter
anchors.verticalCenter: parent.verticalCenter
Text {
anchors.fill: parent
horizontalAlignment: Qt.AlignLeft
verticalAlignment: Qt.AlignVCenter
leftPadding: 20
text: model.nom
color: couleur_blanc_casse
font.pixelSize: 18
wrapMode: Label.Wrap
}
MouseArea{
anchors.fill : parent
enabled : !grille_principale.moving // Permet de bloquer le Mousearea pendant le déplacement de la grille
onReleased: { // Permet déclencher à la fin d'un clic (court) sans mouvement
shipheart_clic();
stackView.push("config_reservoir.qml", {"id_reservoir": model.id})
}
}
}
}
ListModel {
id: modele_grille_principale
}
Label {
anchors.fill: parent
horizontalAlignment: Qt.AlignHCenter
verticalAlignment: Qt.AlignVCenter
visible: parent.count === 0
text: qsTr("Aucun réservoir n'est encore configuré...")
color: couleur_rouge_transparent
font.pixelSize: 15
font.bold: true
wrapMode: Label.Wrap
}
}
}
//-------------------------------------------------------
// Ecran Droite :
//-------------------------------------------------------
Rectangle {
id: zone_affichage_droite
x: zone_affichage_gauche.width + espace_colonnes_principales
y: shipheart_hauteur_bandeau_haut + 5; // Le +5 permet d'aligner le haut du premier bouton de droite avec le haut du premier bouton de la liste de gauche (
width: (shipheart_largeur_page * (1 - pourcentage_largeur_colonne_gauche)) - (espace_colonnes_principales / 2)
height: affichage_hauteur_utile
color: "transparent"
Column {
anchors.fill: parent
spacing: 10
//----------------------
// Bouton + :
Rectangle {
width: parent.width
height: hauteur_bouton_droit
color: shipheart_couleur_fond_bouton
radius: shipheart_rayon_bouton
visible: !clavier.active
Text {
anchors {
verticalCenter: parent.verticalCenter
horizontalCenter: parent.horizontalCenter
}
font.pixelSize: 40
text: qsTr("+")
color: couleur_blanc_casse
}
MouseArea {
anchors.fill: parent
onClicked: {
shipheart_clic();
stackView.push("config_reservoir.qml");
}
}
}
//----------------------
// Bouton tri :
Rectangle {
width: parent.width
height: hauteur_bouton_droit
color: shipheart_couleur_fond_bouton
radius: shipheart_rayon_bouton
visible: !clavier.active // && (modele_grille_principale.count > 1) // SDIR #258
Image {
id: image_parametres
source: "/images/tri.png"
sourceSize.height: 25
anchors {
verticalCenter: parent.verticalCenter
horizontalCenter: parent.horizontalCenter
}
}
MouseArea {
anchors.fill: parent
onClicked: {
shipheart_clic();
stackView.push("config_ordre_elements.qml", {"table": "TB_Reservoirs"})
}
}
}
}
}
//-------------------------------------------------------
// Bandeau bas :
//-------------------------------------------------------
Row {
x: 0
y: shipheart_hauteur_page - shipheart_hauteur_bandeau_bas;
width: shipheart_largeur_page;
height: shipheart_hauteur_bandeau_bas;
padding: 5
spacing: 5
//.................................................
// Bouton retour
//.................................................
Rectangle {
id: bandeau_bas_zone_retour
width: (parent.width * pourcentage_bandeau_bas_largeur_zone_retour) - (2 * parent.padding)
height: shipheart_hauteur_bandeau_bas - parent.topPadding - parent.bottomPadding;
radius: shipheart_rayon_bouton
color: shipheart_couleur_fond_bouton
Image {
source: "/images/icone_retour.png"
sourceSize.height: 30
anchors {
verticalCenter: parent.verticalCenter
horizontalCenter: parent.horizontalCenter
}
}
MouseArea { // Zone d'interaction. On est obligé de la mettre dans la grille pour que Rectangle et MouseArea soient frères ou parent/enfant ET qu'on n'occupe qu'un espace dans la grille.
id: mouse_area_bandeau_bas_zone_retour
anchors.fill: parent
onClicked: {
shipheart_clic();
stackView.pop()
}
}
}
}
// 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()
{
niveau_page = stackView.depth - 1; // Permet de stocker le niveau de la page dans la Stackview
mise_a_jour_donnees();
}
//=============================================================================================================
// Mise à jour des données
//=============================================================================================================
function mise_a_jour_donnees()
{
var req, res;
var i;
var nb_reservoirs;
modele_grille_principale.clear(); // RAZ des valeurs existantes
req = 'SELECT Cle_primaire, ';
req += ' Etat_systeme, ';
req += ' Nom ';
req += ' FROM TB_Reservoirs ORDER BY Priorite_affichage_accueil';
//console.log("req = [ " + req + '; ]');
nb_reservoirs = bdd.requete(req);
for (i = 0; i < nb_reservoirs; i ++)
{
res = bdd.lecture_ligne();
if (!res.isEmpty)
{
modele_grille_principale.append({"id": Number(res[0]), "etat_systeme": Number(res[1]), "nom": res[2] });
}
else break; // Ne devrait pas arriver....
}
}
}
+746
View File
@@ -0,0 +1,746 @@
//========================================================================================================================================================================
// CONFIG_SHIP2NET.QML
// Ce fichier traite la configuration de Ship2net et de la liaison 4G-LTE en particulier
// La page est appelée depuis la page parametres_avances.qml, en cliquant sur "Ship2net" depuis la colonne "Vos paramètres"
//========================================================================================================================================================================
import QtQuick 2.12
import QtQml.Models
import qt.koriolan.shipheart 1.0
Shipheart_page {
id: page_config_ship2net
// Mis en forme :
readonly property double pourcentage_largeur_colonne_gauche: 0.75
readonly property double espace_colonnes_principales: 20
readonly property double pourcentage_hauteur_ligne_haut: 1
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 hauteur_ligne_formulaire: 60
readonly property double pourcentage_largeur_labels_formulaire: 0.45
readonly property int hauteur_bouton_droit: 80
readonly property double pourcentage_bandeau_bas_largeur_zone_retour: 1
readonly property int periode_init_raffraichissement_parametres: 1 // En ms
readonly property int periode_raffraichissement_parametres: 1000 // En ms
// Affichage :
property bool bouton_bandeau_bas_zone_retour_actif: false
property int protection_mode: 0
property bool flag_init: true // Permet de bloquer certains affichage tant que l'init n'est pas terminée
// Données :
property int acces_webgl_1_validation: 0
property string acces_webgl_1_parametres: ""
property int acces_webgl_2_validation: 0
property string acces_webgl_2_parametres: ""
property int acces_webgl_3_validation: 0
property string acces_webgl_3_parametres: ""
// 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 if (!bouton_bandeau_bas_zone_retour_actif)
{
mise_a_jour();
}
}
}
// Titre de la page :
Shipheart_titre_1 {titre:qsTr("Ship2net"); 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;
color: "transparent"
ListView {
id: grille_principale
width: parent.width
height: parent.height
snapMode: GridView.SnapToRow // L'arret de l'affichage se fera sur une rangée entière
clip: true // Nécessaire pour que les Delegates s'arretent à l'intérieur de la GridView
model: modele_grille_principale
footer: Rectangle { // Permet de dégager la place suffisante en fin de GridView pour accéder même au dernier élément lorsque que clavier est déplié
height: shipheart_hauteur_clavier
color: "transparent"
}
ObjectModel {
id: modele_grille_principale
//-------------------------------------------------------------------------------
// Ligne de formulaire : Periode_sauvegarde_bdd
Rectangle {
width: grille_principale.width
height: visible ? hauteur_ligne_formulaire : 0
color: "transparent"
visible : (shipheart_generation_shipbase === shipheart_generation_shipbase_v2) // Ce champ n'est pas configurable sur V1
Shipheart_formulaire_label {
text: qsTr("Période des sauvegardes")
width: parent.width * pourcentage_largeur_labels_formulaire
}
Shipheart_formulaire_combobox {
id: champ_formulaire_Periode_sauvegarde_bdd
model: ListModel { }
width: parent.width * (1 - pourcentage_largeur_labels_formulaire)
onActivated: {
shipheart_clic();
}
}
Shipheart_formulaire_ligne_separation {
haut_bas_hautbas: shipheart_formulaire_ligne_separation_basse
}
}
//-------------------------------------------------------------------------------
// Ligne de formulaire : Periode_connexion_ship2net
Rectangle {
width: grille_principale.width
height: visible ? hauteur_ligne_formulaire : 0
color: "transparent"
visible : (shipheart_generation_shipbase === shipheart_generation_shipbase_v2) // Ce champ n'est pas configurable sur V1
Shipheart_formulaire_label {
text: qsTr("Période de connexion à Ship2net")
width: parent.width * pourcentage_largeur_labels_formulaire
}
Shipheart_formulaire_combobox {
id: champ_formulaire_Periode_connexion_ship2net
model: ListModel { }
width: parent.width * (1 - pourcentage_largeur_labels_formulaire)
onActivated: {
shipheart_clic();
}
}
Shipheart_formulaire_ligne_separation {
haut_bas_hautbas: shipheart_formulaire_ligne_separation_basse
}
}
//-------------------------------------------------------------------------------
// Ligne de formulaire : Mode_validation_maj
Rectangle {
width: grille_principale.width
height: visible ? hauteur_ligne_formulaire : 0
color: "transparent"
Shipheart_formulaire_label {
text: qsTr("Mode de validation des mises à jour")
width: parent.width * pourcentage_largeur_labels_formulaire
}
Shipheart_formulaire_combobox {
id: champ_formulaire_Mode_validation_maj
model: ListModel { }
width: parent.width * (1 - pourcentage_largeur_labels_formulaire)
onActivated: {
shipheart_clic();
}
}
Shipheart_formulaire_ligne_separation {
haut_bas_hautbas: shipheart_formulaire_ligne_separation_basse
}
}
//-------------------------------------------------------------------------------
// Ligne de formulaire : Acces_webgl_1_validation
Rectangle {
width: grille_principale.width
height: visible ? hauteur_ligne_formulaire : 0
color: "transparent"
Shipheart_formulaire_label {
text: qsTr("Accès distant WebGL #1")
width: parent.width * pourcentage_largeur_labels_formulaire
}
Shipheart_formulaire_combobox {
id: champ_formulaire_Acces_webgl_1_validation
model: ListModel { }
width: parent.width * (1 - pourcentage_largeur_labels_formulaire)
onActivated: {
shipheart_clic();
acces_webgl_1_validation = champ_formulaire_Acces_webgl_1_validation.model.get(champ_formulaire_Acces_webgl_1_validation.currentIndex).valeur;
}
}
Shipheart_formulaire_ligne_separation {
haut_bas_hautbas: shipheart_formulaire_ligne_separation_basse
}
}
//-------------------------------------------------------------------------------
// Ligne de formulaire : Acces_webgl_2_validation
Rectangle {
width: grille_principale.width
height: visible ? hauteur_ligne_formulaire : 0
color: "transparent"
Shipheart_formulaire_label {
text: qsTr("Accès distant WebGL #2")
width: parent.width * pourcentage_largeur_labels_formulaire
}
Shipheart_formulaire_combobox {
id: champ_formulaire_Acces_webgl_2_validation
model: ListModel { }
width: parent.width * (1 - pourcentage_largeur_labels_formulaire)
onActivated: {
shipheart_clic();
acces_webgl_2_validation = champ_formulaire_Acces_webgl_2_validation.model.get(champ_formulaire_Acces_webgl_2_validation.currentIndex).valeur;
}
}
Shipheart_formulaire_ligne_separation {
haut_bas_hautbas: shipheart_formulaire_ligne_separation_basse
}
}
//-------------------------------------------------------------------------------
// Ligne de formulaire : Acces_webgl_3_validation
Rectangle {
width: grille_principale.width
height: visible ? hauteur_ligne_formulaire : 0
color: "transparent"
Shipheart_formulaire_label {
text: qsTr("Accès distant WebGL #3")
width: parent.width * pourcentage_largeur_labels_formulaire
}
Shipheart_formulaire_combobox {
id: champ_formulaire_Acces_webgl_3_validation
model: ListModel { }
width: parent.width * (1 - pourcentage_largeur_labels_formulaire)
onActivated: {
shipheart_clic();
acces_webgl_3_validation = champ_formulaire_Acces_webgl_3_validation.model.get(champ_formulaire_Acces_webgl_3_validation.currentIndex).valeur;
}
}
Shipheart_formulaire_ligne_separation {
haut_bas_hautbas: shipheart_formulaire_ligne_separation_basse
}
}
}
}
}
//-------------------------------------------------------
// Ecran Milieu - Gauche :
//-------------------------------------------------------
Rectangle {
id: zone_affichage_milieu_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;
color: "transparent"
//-------------------------------------------------------------------
// Grille pour afficher les valeurs des paramètres non configurables :
GridView {
id: grille_informations
width: parent.width
height: parent.height
cellWidth: parent.width;
cellHeight: hauteur_ligne_formulaire
snapMode: GridView.SnapToRow // L'arret de l'affichage se fera sur une rangée entière
clip: true // Nécessaire pour que les Delegates s'arretent à l'intérieur de la GridView
model: modele_grille_informations
delegate: Rectangle {
width: zone_affichage_milieu_gauche.width
height: visible ? hauteur_ligne_formulaire : 0
color: "transparent"
visible: !flag_init
Shipheart_formulaire_label {
text: model.nom_champ
width: parent.width * pourcentage_largeur_labels_formulaire
}
Shipheart_formulaire_valeur_fixe {
text: model.valeur_champ
width: parent.width * (1 - pourcentage_largeur_labels_formulaire)
x: parent.width * pourcentage_largeur_labels_formulaire
}
Shipheart_formulaire_ligne_separation {
haut_bas_hautbas: shipheart_formulaire_ligne_separation_basse
}
}
ListModel {
id: modele_grille_informations
}
}
}
//-------------------------------------------------------
// 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 * pourcentage_hauteur_ligne_haut) - (espace_lignes_principales / 2);
color: "transparent"
Column {
anchors.fill: parent
spacing: 10
//.......................................................
// Bouton Enregistrer/créer :
//.......................................................
Rectangle {
width: parent.width
height: hauteur_bouton_droit
color: shipheart_couleur_fond_bouton
radius: shipheart_rayon_bouton
// visible: !clavier.active // SDIR #255
Text {
anchors {
verticalCenter: parent.verticalCenter
horizontalCenter: parent.horizontalCenter
}
font.pixelSize: 20
text: qsTr("Enregistrer")
color: couleur_blanc_casse
}
MouseArea {
anchors.fill: parent
onClicked: {
shipheart_clic();
enregistrement(); // Mise à jour des données saisies dans la BDD et reconfiguration par l'application pour prise en compte.
timer_tempo_reconfiguration.restart();
}
}
}
//.......................................................
// Timer de reconfiguration :
//.......................................................
Timer {
id: timer_tempo_reconfiguration
interval: 250 // Cette tempo n'est utilisée que pour le premier cycle
triggeredOnStart: false
running: false
repeat: true
onTriggered: {
if (bdd.check_fin_configuration())
{
timer_tempo_reconfiguration.stop();
stackView.pop(); // Retour à la page de config des évènements
}
else timer_tempo_reconfiguration.interval = 100 // Tempo plus courte pour les lectures suivantes
}
}
}
}
//-------------------------------------------------------
// 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 * pourcentage_bandeau_bas_largeur_zone_retour) - (2 * parent.padding)
height: shipheart_hauteur_bandeau_bas - parent.topPadding - parent.bottomPadding;
radius: shipheart_rayon_bouton
color: shipheart_couleur_fond_bouton
Image {
source: "/images/icone_retour.png"
sourceSize.height: 30
anchors {
verticalCenter: parent.verticalCenter
horizontalCenter: parent.horizontalCenter
}
}
MouseArea { // Zone d'interaction. On est obligé de la mettre dans la grille pour que Rectangle et MouseArea soient frères ou parent/enfant ET qu'on n'occupe qu'un espace dans la grille.
id: mouse_area_bandeau_bas_zone_retour
anchors.fill: parent
onClicked: {
shipheart_clic();
stackView.pop()
}
}
}
}
//===========================================================
// Busy Indicator :
//===========================================================
Shipheart_busy_indicator {
id: busy_indicator
running: timer_tempo_reconfiguration.running
}
// 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 periode_sauvegarde_bdd;
var periode_connexion_ship2net;
var mode_validation_maj;
var cle;
var deb, fin;
var port;
//---------------------------------------------------------------------------------------
// Mise à jour des informations non configurables :
mise_a_jour();
//---------------------------------------------------------------------------------------
// Periode sauvegarde BDD :
req = 'SELECT Valeur FROM TB_Configuration WHERE Nom="Periode_sauvegarde_bdd" ';
res = bdd.requete_unique(req);
periode_sauvegarde_bdd = Number(res[0]);
champ_formulaire_Periode_sauvegarde_bdd.model.clear();
champ_formulaire_Periode_sauvegarde_bdd.model.append({"valeur": 0, "texte": qsTr("Jamais") });
if (shipheart_niveau_acces_en_cours >= 10)
{
champ_formulaire_Periode_sauvegarde_bdd.model.append({"valeur": (2*60), "texte": qsTr("2 minutes") });
champ_formulaire_Periode_sauvegarde_bdd.model.append({"valeur": (10*60), "texte": qsTr("10 minutes") });
}
champ_formulaire_Periode_sauvegarde_bdd.model.append({"valeur": (30*60), "texte": qsTr("30 minutes") });
champ_formulaire_Periode_sauvegarde_bdd.model.append({"valeur": (1*60*60), "texte": qsTr("1 heure") });
champ_formulaire_Periode_sauvegarde_bdd.model.append({"valeur": (2*60*60), "texte": qsTr("2 heures") });
champ_formulaire_Periode_sauvegarde_bdd.model.append({"valeur": (4*60*60), "texte": qsTr("4 heures") });
champ_formulaire_Periode_sauvegarde_bdd.model.append({"valeur": (12*60*60), "texte": qsTr("12 heures") });
champ_formulaire_Periode_sauvegarde_bdd.model.append({"valeur": (24*60*60), "texte": qsTr("24 heures") });
// Positionnement de l'index sur la valeur en cours :
i = recherche_index_valeur_modele(periode_sauvegarde_bdd, champ_formulaire_Periode_sauvegarde_bdd.model, "periode_sauvegarde_bdd");
if (i === -1) champ_formulaire_Periode_sauvegarde_bdd.currentIndex = recherche_index_valeur_modele((2*60*60), champ_formulaire_Periode_sauvegarde_bdd.model, "periode_sauvegarde_bdd");
else champ_formulaire_Periode_sauvegarde_bdd.currentIndex = i;
//---------------------------------------------------------------------------------------
// Periode connexion Ship2net :
req = 'SELECT Valeur FROM TB_Configuration WHERE Nom="Periode_connexion_ship2net" ';
res = bdd.requete_unique(req);
periode_connexion_ship2net = Number(res[0]);
champ_formulaire_Periode_connexion_ship2net.model.clear();
champ_formulaire_Periode_connexion_ship2net.model.append({"valeur": 0, "texte": qsTr("Jamais") });
champ_formulaire_Periode_connexion_ship2net.model.append({"valeur": (2*60), "texte": qsTr("2 minutes") });
champ_formulaire_Periode_connexion_ship2net.model.append({"valeur": (10*60), "texte": qsTr("10 minutes") });
champ_formulaire_Periode_connexion_ship2net.model.append({"valeur": (30*60), "texte": qsTr("30 minutes") });
champ_formulaire_Periode_connexion_ship2net.model.append({"valeur": (1*60*60), "texte": qsTr("1 heure") });
champ_formulaire_Periode_connexion_ship2net.model.append({"valeur": (4*60*60), "texte": qsTr("4 heures") });
champ_formulaire_Periode_connexion_ship2net.model.append({"valeur": (12*60*60), "texte": qsTr("12 heures") });
champ_formulaire_Periode_connexion_ship2net.model.append({"valeur": (24*60*60), "texte": qsTr("24 heures") });
// Positionnement de l'index sur la valeur en cours :
i = recherche_index_valeur_modele(periode_connexion_ship2net, champ_formulaire_Periode_connexion_ship2net.model, "periode_connexion_ship2net");
if (i === -1) champ_formulaire_Periode_connexion_ship2net.currentIndex = recherche_index_valeur_modele((2*60*60), champ_formulaire_Periode_connexion_ship2net.model, "periode_connexion_ship2net");
else champ_formulaire_Periode_connexion_ship2net.currentIndex = i;
//---------------------------------------------------------------------------------------
// Mode_validation_maj :
req = 'SELECT Valeur FROM TB_Configuration WHERE Nom="Mode_validation_maj" ';
res = bdd.requete_unique(req);
mode_validation_maj = Number(res[0]);
champ_formulaire_Mode_validation_maj.model.clear();
champ_formulaire_Mode_validation_maj.model.append({"valeur": 0, "texte": qsTr("Aucune mise à jour") });
champ_formulaire_Mode_validation_maj.model.append({"valeur": 1, "texte": qsTr("Validation manuelle des mises à jour reçues") });
champ_formulaire_Mode_validation_maj.model.append({"valeur": 2, "texte": qsTr("Validation automatique des mises à jour reçues") });
// Positionnement de l'index sur la valeur en cours :
i = recherche_index_valeur_modele(mode_validation_maj, champ_formulaire_Mode_validation_maj.model, "mode_validation_maj");
if (i === -1) champ_formulaire_Mode_validation_maj.currentIndex = recherche_index_valeur_modele(2, champ_formulaire_Mode_validation_maj.model, "mode_validation_maj");
else champ_formulaire_Mode_validation_maj.currentIndex = i;
//---------------------------------------------------------------------------------------
// Acces_webgl_1_validation :
req = 'SELECT Valeur FROM TB_Configuration WHERE Nom="Acces_webgl_1_validation" ';
res = bdd.requete_unique(req);
acces_webgl_1_validation = Number(res[0]);
req = 'SELECT Valeur FROM TB_Configuration WHERE Nom="Acces_webgl_1_parametres" ';
res = bdd.requete_unique(req);
acces_webgl_1_parametres = res[0]; // Par exemple : '-platform webgl:port=8081:wsserverport=8082'. NB : ne PAS mettre d'espace à la fin car cela peut poser des soucis à l'Appli pour détecter le process en cours d'exécution.
cle = ":port=";
deb = acces_webgl_1_parametres.indexOf(cle) + cle.length;
fin = acces_webgl_1_parametres.indexOf(":", deb);
//console.log("parametres = [" + acces_webgl_1_parametres + "], deb = " + deb + " et fin = " + fin);
port = acces_webgl_1_parametres.slice(deb, fin);
champ_formulaire_Acces_webgl_1_validation.model.clear();
champ_formulaire_Acces_webgl_1_validation.model.append({"valeur": 0, "texte": qsTr("Dévalidé") });
champ_formulaire_Acces_webgl_1_validation.model.append({"valeur": 1, "texte": qsTr("Valide [port %1]").arg(port) });
// Positionnement de l'index sur la valeur en cours :
if (acces_webgl_1_validation === 1) champ_formulaire_Acces_webgl_1_validation.currentIndex = 1;
else champ_formulaire_Acces_webgl_1_validation.currentIndex = 0;
//---------------------------------------------------------------------------------------
// Acces_webgl_2_validation :
req = 'SELECT Valeur FROM TB_Configuration WHERE Nom="Acces_webgl_2_validation" ';
res = bdd.requete_unique(req);
acces_webgl_2_validation = Number(res[0]);
req = 'SELECT Valeur FROM TB_Configuration WHERE Nom="Acces_webgl_2_parametres" ';
res = bdd.requete_unique(req);
acces_webgl_2_parametres = res[0];
cle = ":port=";
deb = acces_webgl_2_parametres.indexOf(cle) + cle.length;
fin = acces_webgl_2_parametres.indexOf(":", deb);
//console.log("parametres = [" + acces_webgl_2_parametres + "], deb = " + deb + " et fin = " + fin);
port = acces_webgl_2_parametres.slice(deb, fin);
champ_formulaire_Acces_webgl_2_validation.model.clear();
champ_formulaire_Acces_webgl_2_validation.model.append({"valeur": 0, "texte": qsTr("Dévalidé") });
champ_formulaire_Acces_webgl_2_validation.model.append({"valeur": 1, "texte": qsTr("Valide [port %1]").arg(port) });
// Positionnement de l'index sur la valeur en cours :
if (acces_webgl_2_validation === 1) champ_formulaire_Acces_webgl_2_validation.currentIndex = 1;
else champ_formulaire_Acces_webgl_2_validation.currentIndex = 0;
//---------------------------------------------------------------------------------------
// Acces_webgl_3_validation :
req = 'SELECT Valeur FROM TB_Configuration WHERE Nom="Acces_webgl_3_validation" ';
res = bdd.requete_unique(req);
acces_webgl_3_validation = Number(res[0]);
req = 'SELECT Valeur FROM TB_Configuration WHERE Nom="Acces_webgl_3_parametres" ';
res = bdd.requete_unique(req);
acces_webgl_3_parametres = res[0];
cle = ":port=";
deb = acces_webgl_3_parametres.indexOf(cle) + cle.length;
fin = acces_webgl_3_parametres.indexOf(":", deb);
//console.log("parametres = [" + acces_webgl_3_parametres + "], deb = " + deb + " et fin = " + fin);
port = acces_webgl_3_parametres.slice(deb, fin);
champ_formulaire_Acces_webgl_3_validation.model.clear();
champ_formulaire_Acces_webgl_3_validation.model.append({"valeur": 0, "texte": qsTr("Dévalidé") });
champ_formulaire_Acces_webgl_3_validation.model.append({"valeur": 1, "texte": qsTr("Valide [port %1]").arg(port) });
// Positionnement de l'index sur la valeur en cours :
if (acces_webgl_3_validation === 1) champ_formulaire_Acces_webgl_3_validation.currentIndex = 1;
else champ_formulaire_Acces_webgl_3_validation.currentIndex = 0;
flag_init = false;
}
//=============================================================================================================
// Mise à jour des valeurs courantes
//=============================================================================================================
function mise_a_jour()
{
var req, res;
var nb;
var i;
var nom_champ, valeur_champ;
var mode_validation_maj;
var flag_valide;
modele_grille_informations.clear();
/* req = 'SELECT Nom, Valeur FROM TB_Systeme ORDER BY Cle_primaire';
//console.log("req =" + req);
nb = bdd.requete(req);
for (i = 0; i < nb; i ++)
{
res = bdd.lecture_ligne();
if (!res.isEmpty)
{
flag_valide = true;
switch(res[0])
{
case "Adresse_ip_ethernet":
nom_champ = qsTr("Adresse IP sur réseau Ethernet");
valeur_champ = (res[1] === shipheart_adresse_ip_inconnue) ? qsTr("Non connecté") : res[1];
if (shipheart_generation_shipbase < shipheart_generation_shipbase_v2) flag_valide = false;
break;
case "Adresse_ip_wifi_sta":
nom_champ = qsTr("Adresse IP sur réseau Wifi (STA)");
valeur_champ = (res[1] === shipheart_adresse_ip_inconnue) ? qsTr("Non connecté") : res[1];
if (shipheart_generation_shipbase < shipheart_generation_shipbase_v2) flag_valide = false;
break;
case "Adresse_ip_wifi_ap":
nom_champ = qsTr("Adresse IP sur réseau Wifi ShipHeart (AP)");
valeur_champ = (res[1] === shipheart_adresse_ip_inconnue) ? qsTr("Non connecté") : res[1];
if (shipheart_generation_shipbase < shipheart_generation_shipbase_v2) flag_valide = false;
break;
default: continue;
}
if (flag_valide) modele_grille_informations.append({"commande": false, "nom_champ": nom_champ, "valeur_champ": valeur_champ});
}
else break; // Ne devrait pas arriver....
}
*/
}
//=============================================================================================================
// Enregistrement des valeurs saisies
//=============================================================================================================
function enregistrement()
{
var req;
// Mise à jour de la nouvelle valeur de 'Periode_sauvegarde_bdd' dans la BDD :
req = 'UPDATE TB_Configuration SET Valeur="' + champ_formulaire_Periode_sauvegarde_bdd.model.get(champ_formulaire_Periode_sauvegarde_bdd.currentIndex).valeur + '" ';
req += ' WHERE Nom="Periode_sauvegarde_bdd" '
//console.log("Requête = [ " + req + " ]")
bdd.requete_unique(req);
// Mise à jour de la nouvelle valeur de 'Periode_connexion_ship2net' dans la BDD :
req = 'UPDATE TB_Configuration SET Valeur="' + champ_formulaire_Periode_connexion_ship2net.model.get(champ_formulaire_Periode_connexion_ship2net.currentIndex).valeur + '" ';
req += ' WHERE Nom="Periode_connexion_ship2net" '
//console.log("Requête = [ " + req + " ]")
bdd.requete_unique(req);
// Mise à jour de la nouvelle valeur de 'Mode_validation_maj' dans la BDD :
req = 'UPDATE TB_Configuration SET Valeur="' + champ_formulaire_Mode_validation_maj.model.get(champ_formulaire_Mode_validation_maj.currentIndex).valeur + '" ';
req += ' WHERE Nom="Mode_validation_maj" '
//console.log("Requête = [ " + req + " ]")
bdd.requete_unique(req);
// Note : la logique aurait voulu que l'on traite ici le lancement et l'arrêt des processus 'WebGL'. Malheureusement, les tentatives de
// lancement via 'systeme.commande_shell()' n'ont pas fonctionné... En conséquence, on ne fait que mettre à jour les champs et l'Appli
// se charge ensuite de lancer/arreter les process correspondants.
// Mise à jour des nouvelles valeurs relatives à l'accès WebGL #1 dans la BDD :
req = 'UPDATE TB_Configuration SET Valeur="' + acces_webgl_1_validation + '" WHERE Nom="Acces_webgl_1_validation" '
//console.log("Requête = [ " + req + " ]")
bdd.requete_unique(req);
req = 'UPDATE TB_Configuration SET Valeur="' + acces_webgl_1_parametres + '" WHERE Nom="Acces_webgl_1_parametres" '
//console.log("Requête = [ " + req + " ]")
bdd.requete_unique(req);
// Mise à jour des nouvelles valeurs relatives à l'accès WebGL #2 dans la BDD :
req = 'UPDATE TB_Configuration SET Valeur="' + acces_webgl_2_validation + '" WHERE Nom="Acces_webgl_2_validation" '
//console.log("Requête = [ " + req + " ]")
bdd.requete_unique(req);
req = 'UPDATE TB_Configuration SET Valeur="' + acces_webgl_2_parametres + '" WHERE Nom="Acces_webgl_2_parametres" '
//console.log("Requête = [ " + req + " ]")
bdd.requete_unique(req);
// Mise à jour des nouvelles valeurs relatives à l'accès WebGL #3 dans la BDD :
req = 'UPDATE TB_Configuration SET Valeur="' + acces_webgl_3_validation + '" WHERE Nom="Acces_webgl_3_validation" '
//console.log("Requête = [ " + req + " ]")
bdd.requete_unique(req);
req = 'UPDATE TB_Configuration SET Valeur="' + acces_webgl_3_parametres + '" WHERE Nom="Acces_webgl_3_parametres" '
//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);
}
//=============================================================================================================
// Recherche de l'index d'une certaine valeur dans un modele
//=============================================================================================================
function recherche_index_valeur_modele(cible, modele, nom_modele)
{
var i;
for (i=0; i<modele.count; i ++)
{
if (modele.get(i).valeur === cible) break; // Cas où la valeur cible a été trouvée parmi les valeurs du modèle
}
if (i === modele.count)
{
console.log("Aucune valeur trouvée pour cible = [" + cible + "] du modèle = " + nom_modele);
i = -1; // Cas où la valeur n'a pas été trouvée parmi les valeurs du modèle
}
//else console.log("Valeur trouvée : [" + i + "] pour cible = [" + cible + "] soit texte = [" + modele.get(i).texte + "]");
return(i);
}
}
+518
View File
@@ -0,0 +1,518 @@
//========================================================================================================================================================================
// CONFIG_SHIPRS.QML
// La page est appelée depuis la page 'parametres_avances'home_attente.qml' lorsque la connexion a la BDD n'a pu être effectuée
//========================================================================================================================================================================
import QtQuick 2.12
import QtQuick.Controls 2.12
import QtQml.Models
import qt.koriolan.shipheart 1.0
import"fonctions.js" as Fonctions
Shipheart_page {
id: page_config_shiprs
// Mis en forme :
readonly property double pourcentage_largeur_colonne_gauche: 0.75
readonly property double espace_colonnes_principales: 20
readonly property double pourcentage_hauteur_message_alerte: 0.45
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 hauteur_ligne_formulaire: 60
readonly property double pourcentage_largeur_labels_formulaire: 0.45
readonly property int hauteur_bouton_droit: 80
readonly property double pourcentage_bandeau_bas_largeur_zone_retour: 1
readonly property int periode_init_raffraichissement_parametres: 1 // En ms
readonly property int periode_raffraichissement_parametres: 1000 // En ms
readonly property int step_timer_sortie: 10 // en ms
// Affichage :
property bool bouton_bandeau_bas_zone_retour_actif: false
property bool flag_init: true // Permet de bloquer certains affichage tant que l'init n'est pas terminée
// Données :
property bool connexion_impossible: false
property string adresse_ip_shipbase: "inconnue<br /><i>unknown</i>"
property int taille_ecran_shiprs: 10
property bool configuration_modifiee: false
property string message_erreur: ""
property string message_info: ""
property int valeur_timer_sortie: 0
// Modèles :
// Note : les modèles chargés dans 'main.qml' n'ont pas à être rechargés dans les pages suivantes
//===========================================================
// Timer periode raffraichissement parametres :
//===========================================================
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 if (!bouton_bandeau_bas_zone_retour_actif)
{
// mise_a_jour();
}
}
}
//===========================================================
// Timer pour gérer l'appui long du reboot :
//===========================================================
Timer {
id: timer_sortie
interval: step_timer_sortie
triggeredOnStart: false
repeat: true
onTriggered: {
if (valeur_timer_sortie < shipheart_mouse_press_and_hold_interval_tres_long) valeur_timer_sortie+= step_timer_sortie;
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_sortie.running = false;
valeur_timer_sortie = 0;
// Demande de Reset (via linux si on est sur un ShipRS !) :
let commande = "sudo sh -c \"sudo reboot now\" ";
//console.log("Commande shell : [" + commande + "]");
systeme.commande_shell(commande, "");
}
}
}
// Titre de la p"/images/ak14.png"age :
Shipheart_titre_1 {titre:"ShipRS"; hauteur: shipheart_hauteur_bandeau_haut}
//-------------------------------------------------------
// Ecran Haut :
//-------------------------------------------------------
Rectangle {
id: zone_affichage_message_alerte
x: 0
y: shipheart_hauteur_bandeau_haut;
width: shipheart_largeur_page;
height: visible ? (shipheart_hauteur_page - shipheart_hauteur_bandeau_haut) * pourcentage_hauteur_message_alerte : 0;
color: "transparent"
border.width: 3
border.color: couleur_rouge
radius: shipheart_rayon_bouton
visible: connexion_impossible
Image {
id: picto_alerte
source: "/images/ak14.png"
sourceSize.height: 100;
anchors {
left: parent.left
leftMargin: 20
verticalCenter: parent.verticalCenter
}
}
Text { // Affiche parfaitement 1, 2 ou 3 lignes de texte. A partir de 4 et 5, ça mort sur le contour du rectangle mais c'est encore lisible.
id: message_alerte
width: parent.width - picto_alerte.width - picto_alerte.anchors.leftMargin - anchors.leftMargin;
wrapMode: Text.Wrap
horizontalAlignment: Text.AlignHCenter
leftPadding: 10 // Dégage le pictogramme sur la gauche
rightPadding: 10 // Evite de se caler sur le bord droit du rectangle
text: "La connexion à ShipBase a échoué.<br />\
Vérifiez avant tout que ShipBase est bien sous tension et qu'il est bien connecté au réseau local.<br />\
Vérifiez ensuite que ShipRS est également connecté au réseau local via son port Ethernet.<br />\
Enfin, assurez-vous que l'adresse IP de ShipBase sur le réseau local est bien celle renseignée ci-dessous.<br />\
Dans le cas contraire, vous pouvez la mettre à jour puis cliquer sur [Enregistrer].<br />\
<b>Attention : La prise en compte de cette nouvelle adresse ne sera prise en compte qu'après un redémarrage de ShipRS.</b><br /><br />
<i>The connection to ShipBase has failed.<br />
First of all, check that ShipBase is switched on and connected to the local network.<br />
Then check that ShipRS is also connected to the local network via its Ethernet port.<br />
Finally, make sure that the IP address of ShipBase on the local network is the one given below.<br />
If not, you can update it and click on [Save].<br />
<b>Please note: The new address will only be taken into account after a restart of ShipRS.</b></i>";
anchors {
left: picto_alerte.right
verticalCenter: parent.verticalCenter
}
font.pixelSize: 18
color: couleur_blanc_casse
}
}
//-------------------------------------------------------
// Ecran Bas - Gauche :
//-------------------------------------------------------
Rectangle {
id: zone_affichage_saisie_parametres
x: 0
y: shipheart_hauteur_bandeau_haut + zone_affichage_message_alerte.height + espace_lignes_principales;
width: (shipheart_largeur_page * pourcentage_largeur_colonne_gauche) - (espace_colonnes_principales / 2);
height: ((shipheart_hauteur_page - shipheart_hauteur_bandeau_haut) * (1 - pourcentage_hauteur_message_alerte)) - espace_lignes_principales;
color: "transparent"
ListView {
id: grille_principale
width: parent.width
height: parent.height
snapMode: GridView.SnapToRow // L'arret de l'affichage se fera sur une rangée entière
clip: true // Nécessaire pour que les Delegates s'arretent à l'intérieur de la GridView
model: modele_grille_principale
footer: Rectangle { // Permet de dégager la place suffisante en fin de GridView pour accéder même au dernier élément lorsque que clavier est déplié
height: shipheart_hauteur_clavier
color: "transparent"
}
ObjectModel {
id: modele_grille_principale
//-------------------------------------------------------------------------------
// Ligne de formulaire : Adresse IP
Rectangle {
width: (shipheart_largeur_page * pourcentage_largeur_colonne_gauche) - (espace_colonnes_principales / 2)
height: visible ? hauteur_ligne_formulaire : 0
color: "transparent"
Shipheart_formulaire_label {
text: "Adresse IP de ShipBase sur le réseau local<br /><i>ShipBase IP address on local network</i>"
width: parent.width * pourcentage_largeur_labels_formulaire
}
Shipheart_formulaire_textfield {
id: champ_formulaire_Adresse_ip
width: parent.width * (1 - pourcentage_largeur_labels_formulaire)
x: parent.width * pourcentage_largeur_labels_formulaire
}
Shipheart_formulaire_ligne_separation {
haut_bas_hautbas: shipheart_formulaire_ligne_separation_basse
}
}
//-------------------------------------------------------------------------------
// Ligne de formulaire : Taille écran
Rectangle {
width: (shipheart_largeur_page * pourcentage_largeur_colonne_gauche) - (espace_colonnes_principales / 2)
height: visible ? hauteur_ligne_formulaire : 0
color: "transparent"
Shipheart_formulaire_label {
text: "Taille de l'écran<br /><i>Screen size</i>"
width: parent.width * pourcentage_largeur_labels_formulaire
}
Shipheart_formulaire_combobox {
id: champ_formulaire_Taille_ecran
model: ListModel { }
width: parent.width * (1 - pourcentage_largeur_labels_formulaire)
onActivated: {
shipheart_clic();
}
}
Shipheart_formulaire_ligne_separation {
haut_bas_hautbas: shipheart_formulaire_ligne_separation_basse
}
}
//-------------------------------------------------------------------------------
// Message d'erreur si adresse IP invalide :
Rectangle {
width: (shipheart_largeur_page * pourcentage_largeur_colonne_gauche) - (espace_colonnes_principales / 2)
height: visible ? hauteur_ligne_formulaire : 0
color: "transparent"
visible: (message_erreur !== "") || (message_info !== "")
Text {
// height: parent.height * proportion_hauteur_texte_droite
width: parent.width;
anchors.horizontalCenter: parent.horizontalCenter
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
topPadding: 20 // Cette valeur permet d'être aligné avec la version courante...
bottomPadding: 50
font.pixelSize: 18
text: (message_erreur !== "") ? message_erreur : message_info
color: (message_erreur !== "") ? couleur_rouge : couleur_blanc_casse
wrapMode: Text.Wrap
}
}
}
}
}
//-------------------------------------------------------
// Ecran Bas - Droite :
//-------------------------------------------------------
Rectangle {
id: zone_affichage_droite
x: zone_affichage_saisie_parametres.width + espace_colonnes_principales
y: shipheart_hauteur_bandeau_haut + zone_affichage_message_alerte.height + espace_lignes_principales;
width: (shipheart_largeur_page * (1 - pourcentage_largeur_colonne_gauche)) - (espace_colonnes_principales / 2);
height: ((shipheart_hauteur_page - shipheart_hauteur_bandeau_haut) * (1 - pourcentage_hauteur_message_alerte)) - espace_lignes_principales;
color: "transparent"
visible: !clavier.active
Column {
anchors.fill: parent
spacing: 10
// Bouton Enregistrer :
Rectangle {
width: parent.width
height: hauteur_bouton_droit
color: shipheart_couleur_fond_bouton
radius: shipheart_rayon_bouton
Text {
anchors {
verticalCenter: parent.verticalCenter
horizontalCenter: parent.horizontalCenter
}
font.pixelSize: 20
text: "Enregistrer<br /><i>Save</i>"
color: couleur_blanc_casse
}
MouseArea {
anchors.fill: parent
onClicked: {
enregistrement();
if ((!connexion_impossible) && (message_erreur === ""))
{
shipheart_clic();
stackView.pop()
}
}
}
}
//--------------------------------------------------------------------
// Bouton Rebooter :
Rectangle {
width: parent.width
height: hauteur_bouton_droit
color: shipheart_couleur_fond_bouton
radius: shipheart_rayon_bouton
visible: connexion_impossible && configuration_modifiee
Label {
anchors.centerIn: parent
text: "Redémarrer ShipRS<br /><i>Restart ShipRS</i>"
color: couleur_blanc_casse
font.pixelSize:20
}
// Barre grandissante pour montrer durée d'appui vs tempo nécessaire :
Rectangle {
width: (valeur_timer_sortie / shipheart_mouse_press_and_hold_interval_tres_long) * parent.width
height: parent.height
color: couleur_rouge_transparent
radius: shipheart_rayon_bouton
}
MouseArea {
anchors.fill: parent
onPressed: { // Déclenché lors de l'appui inital
valeur_timer_sortie = 0;
timer_sortie.running = true;
}
onClicked: { // Déclenché si l'appui n'a pas été suffisement long pour avoir un Press&Hold :
timer_sortie.running = false;
valeur_timer_sortie = 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_sortie.running = false;
valeur_timer_sortie = 0;
}
}
}
}
}
//-------------------------------------------------------
// 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
visible: !connexion_impossible
//.................................................
// Bouton retour
//.................................................
Rectangle {
id: bandeau_bas_zone_retour
width: (parent.width * pourcentage_bandeau_bas_largeur_zone_retour) - (2 * parent.padding)
height: shipheart_hauteur_bandeau_bas - parent.topPadding - parent.bottomPadding;
radius: shipheart_rayon_bouton
color: shipheart_couleur_fond_bouton
Image {
source: "/images/icone_retour.png"
sourceSize.height: 30
anchors {
verticalCenter: parent.verticalCenter
horizontalCenter: parent.horizontalCenter
}
}
MouseArea { // Zone d'interaction. On est obligé de la mettre dans la grille pour que Rectangle et MouseArea soient frères ou parent/enfant ET qu'on n'occupe qu'un espace dans la grille.
id: mouse_area_bandeau_bas_zone_retour
anchors.fill: parent
onClicked: {
shipheart_clic();
stackView.pop()
}
}
}
}
//=============================================================================================================
// Init de la page
//=============================================================================================================
function init_page()
{
var i;
var lignes;
lignes = systeme.lecture_fichier(shipheart_nom_complet_fichier_shiprs_conf);
// ATTENTION : la première ligne renvoyée par la fonction n'est PAS lue dans le fichier mais indique si la lecture s'est déroulée correctement ("Ok") ou pas ("Nok").
console.log("Résultat de la lecture de 'shiprs.conf' = " + lignes[0]);
console.log("Adresse IP de ShipBase dans 'shiprs.conf' = " + lignes[1]);
console.log("Taille écran dans 'shiprs.conf' = " + lignes[2]);
if (lignes[0] === "Ok")
{
adresse_ip_shipbase = lignes[1];
taille_ecran_shiprs = lignes[2];
}
else
{
adresse_ip_shipbase = "???.???.???.???";
taille_ecran_shiprs = 10;
message_erreur = "/!\\ La lecture du fichier 'shiprs.conf' est impossible<br /><i>/!\\ ShipRS config file can't be accessed</i>";
}
//-----------------------------------------------------------------------------------------
champ_formulaire_Adresse_ip.text = adresse_ip_shipbase;
//-----------------------------------------------------------------------------------------
champ_formulaire_Taille_ecran.model.clear();
champ_formulaire_Taille_ecran.model.append({"valeur": 7, "texte": "7\" " });
champ_formulaire_Taille_ecran.model.append({"valeur": 10, "texte": "10\" " });
champ_formulaire_Taille_ecran.model.append({"valeur": 22, "texte": "22\" " });
// Positionnement de l'index sur la valeur en cours :
i = recherche_index_valeur_modele(taille_ecran_shiprs, champ_formulaire_Taille_ecran.model, "taille_ecran_shiprs");
if (i === -1) champ_formulaire_Taille_ecran.currentIndex = recherche_index_valeur_modele(10, champ_formulaire_Taille_ecran.model, "taille_ecran_shiprs");
else champ_formulaire_Taille_ecran.currentIndex = i;
flag_init = false;
}
//=============================================================================================================
// Enregistrement des valeurs saisies
//=============================================================================================================
function enregistrement()
{
var lignes = [];
message_erreur = ""; // Nécessaire si une erreur était présente au préalable.
message_info = ""; // Nécessaire si un message était présent au préalable.
// Vérification de la validité du format de l'adresse IP saisie :
if (Fonctions.test_format_adresse_ip_v4(champ_formulaire_Adresse_ip.text))
//-----------------------------------------------------------
// Cas où le format est valide :
{
if(champ_formulaire_Adresse_ip.text !== "192.168.4.1")
{
adresse_ip_shipbase = champ_formulaire_Adresse_ip.text;
taille_ecran_shiprs = champ_formulaire_Taille_ecran.model.get(champ_formulaire_Taille_ecran.currentIndex).valeur;
lignes.push(adresse_ip_shipbase);
lignes.push(taille_ecran_shiprs);
if (systeme.ecriture_fichier(shipheart_nom_complet_fichier_shiprs_conf, lignes) === -1)
{
message_erreur = "/!\\ L'écriture du fichier 'shiprs.conf' est impossible.<br /><i>/!\\ 'shiprs.conf' file cannot be written to</i>";
}
else
{
configuration_modifiee = true;
message_info = "Les nouveaux paramètres ont été enregistrés.<br />Vous pouvez maintenant maintenir appuyé le bouton [Redémarrer ShipRS] jusqu'au redémarrage pour que ces nouveaux paramètres soient pris en compte.<br /><i>The new settings have been saved.<br />You can now press and hold the [Restart ShipRS] button until the system restarts to take these new settings into account.</i>";
}
//console.log("Format d'adresse IP valide...")
}
else message_erreur = "L'adresse IP [192.168.4.1] n'est pas autorisée. Merci d'entrer une adresse différente...<br /><i>The [192.168.4.1] IP address is not authorized. Please enter a different address...</i>";
}
//-----------------------------------------------------------
// Cas où le format est invalide :
else
{
message_erreur = "L'adresse IP saisie n'est pas valide et ne peut pas être enregistrée. Merci d'entrer une chaine valide...<br /><i>The IP address entered is invalid and cannot be saved. Please enter a valid string...</i>";
// champ_formulaire_Adresse_ip.text = adresse_ip_shipbase; // Retour à la valeur précédente
//console.log("Format d'adresse IP invalide !")
}
}
//=============================================================================================================
// Recherche de l'index d'une certaine valeur dans un modele
//=============================================================================================================
function recherche_index_valeur_modele(cible, modele, nom_modele)
{
var i;
for (i=0; i<modele.count; i ++)
{
if (modele.get(i).valeur === cible) break; // Cas où la valeur cible a été trouvée parmi les valeurs du modèle
}
if (i === modele.count)
{
console.log("Aucune valeur trouvée pour cible = [" + cible + "] du modèle = " + nom_modele);
i = -1; // Cas où la valeur n'a pas été trouvée parmi les valeurs du modèle
}
//else console.log("Valeur trouvée : [" + i + "] pour cible = [" + cible + "] soit texte = [" + modele.get(i).texte + "]");
return(i);
}
}
+476
View File
@@ -0,0 +1,476 @@
//========================================================================================================================================================================
// CONFIG_SPECIFIQUE_CLIENT_SUNCY.QML
// Ce fichier traite la configuration des paramètres spécifiques au client Suncy Concept
// La page est appelée depuis la page parametres_avances.qml, en cliquant sur "Vos paramètres" > "Spécifique xxxx" (ce bouton n'est visible que si l'option Spécifique_client
// contient la valeur adéquate dans TB_COnfiguration)
//========================================================================================================================================================================
import QtQuick 2.12
import QtQml.Models
import qt.koriolan.shipheart 1.0
Shipheart_page {
id: page_config_specifique_client_suncy
// Mis en forme :
readonly property double pourcentage_largeur_colonne_gauche: 0.75
readonly property double espace_colonnes_principales: 20
readonly property double pourcentage_hauteur_ligne_haut: 1
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 hauteur_ligne_formulaire: 60
readonly property double pourcentage_largeur_labels_formulaire: 0.4
readonly property int hauteur_bouton_droit: 80
readonly property double pourcentage_bandeau_bas_largeur_zone_retour: 1
readonly property int periode_init_raffraichissement_parametres: 1 // En ms
readonly property int periode_raffraichissement_parametres: 1000 // En ms
// Affichage :
property bool bouton_bandeau_bas_zone_retour_actif: false
property int protection_mode: 0
// Données :
property int min_taille_code: 6
property int max_taille_code: 8
// 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 if (!bouton_bandeau_bas_zone_retour_actif) {
}
}
}
// Titre de la page :
Shipheart_titre_1 {titre:qsTr("Configuration spécifique à Suncy Concept"); 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);
color: "transparent"
ListView {
id: grille_principale
width: parent.width
height: parent.height
snapMode: GridView.SnapToRow // L'arret de l'affichage se fera sur une rangée entière
clip: true // Nécessaire pour que les Delegates s'arretent à l'intérieur de la GridView
model: modele_grille_principale
footer: Rectangle { // Permet de dégager la place suffisante en fin de GridView pour accéder même au dernier élément lorsque que clavier est déplié
height: shipheart_hauteur_clavier
color: "transparent"
}
ObjectModel {
id: modele_grille_principale
//-------------------------------------------------------------------------------
// Ligne de formulaire : Sortie activation moteur
Rectangle {
width: parent.width
height: visible ? hauteur_ligne_formulaire : 0
color: "transparent"
Shipheart_formulaire_label {
text: qsTr("Circuit d'activation moteur")
width: parent.width * pourcentage_largeur_labels_formulaire
}
Shipheart_formulaire_combobox {
id: champ_formulaire_Circuit_activation_moteur
model: ListModel { }
width: parent.width * (1 - pourcentage_largeur_labels_formulaire)
onActivated: {
shipheart_clic();
}
}
Shipheart_formulaire_ligne_separation {
haut_bas_hautbas: shipheart_formulaire_ligne_separation_basse
}
}
// Espace :
Rectangle {
width: parent.width
height: 50
color: "transparent"
}
// Sous-titre :
Shipheart_titre_2 {titre:qsTr("Nouveau code"); hauteur: shipheart_hauteur_bandeau_haut}
//-------------------------------------------------------------------------------
// Ligne de formulaire : Code
Rectangle {
width: (shipheart_largeur_page * pourcentage_largeur_colonne_gauche) - (espace_colonnes_principales / 2)
height: visible ? hauteur_ligne_formulaire : 0
color: "transparent"
Shipheart_formulaire_label {
text: qsTr("Code d'accès au bateau")
width: parent.width * pourcentage_largeur_labels_formulaire
}
Shipheart_formulaire_textfield {
id: champ_formulaire_Code
width: parent.width * (1 - pourcentage_largeur_labels_formulaire)
x: parent.width * pourcentage_largeur_labels_formulaire
flag_num_only: true
password_type: true
maximumLength: max_taille_code_pattern
}
}
//-------------------------------------------------------------------------------
// Ligne de formulaire : Répéter Code
Rectangle {
width: (shipheart_largeur_page * pourcentage_largeur_colonne_gauche) - (espace_colonnes_principales / 2)
height: visible ? hauteur_ligne_formulaire : 0
color: "transparent"
Shipheart_formulaire_label {
text: qsTr("Répéter le code d'accès")
width: parent.width * pourcentage_largeur_labels_formulaire
}
Shipheart_formulaire_textfield {
id: champ_formulaire_Code_repetition
width: parent.width * (1 - pourcentage_largeur_labels_formulaire)
x: parent.width * pourcentage_largeur_labels_formulaire
flag_num_only: true
password_type: true
maximumLength: max_taille_code_pattern
}
}
}
}
}
//-------------------------------------------------------
// 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 * pourcentage_hauteur_ligne_haut) - (espace_lignes_principales / 2);
color: "transparent"
Column {
anchors.fill: parent
spacing: 10
// Bouton Enregistrer :
Rectangle {
width: parent.width
height: hauteur_bouton_droit
color: shipheart_couleur_fond_bouton
radius: shipheart_rayon_bouton
// visible: !clavier.active // SDIR #255
Text {
anchors {
verticalCenter: parent.verticalCenter
horizontalCenter: parent.horizontalCenter
}
font.pixelSize: 20
text: qsTr("Enregistrer")
color: couleur_blanc_casse
}
MouseArea {
anchors.fill: parent
onClicked: {
shipheart_clic();
if (mise_a_jour() !== -1) stackView.pop(); // On ne revient à la page précédente que si la saisie est valide...
}
}
}
}
}
//-------------------------------------------------------
// 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 * pourcentage_bandeau_bas_largeur_zone_retour) - (2 * parent.padding)
height: shipheart_hauteur_bandeau_bas - parent.topPadding - parent.bottomPadding;
radius: shipheart_rayon_bouton
color: shipheart_couleur_fond_bouton
Image {
source: "/images/icone_retour.png"
sourceSize.height: 30
anchors {
verticalCenter: parent.verticalCenter
horizontalCenter: parent.horizontalCenter
}
}
MouseArea { // Zone d'interaction. On est obligé de la mettre dans la grille pour que Rectangle et MouseArea soient frères ou parent/enfant ET qu'on n'occupe qu'un espace dans la grille.
id: mouse_area_bandeau_bas_zone_retour
anchors.fill: parent
onClicked: {
shipheart_clic();
stackView.pop()
}
}
}
}
//-------------------------------------------------------
// Boite d'information :
//-------------------------------------------------------
Shipheart_boite_dialogue {
id: boite_information
}
// 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 nb_circuits;
var i, j;
//---------------------------------------------------------------------------------------
// Taille du code :
req = 'SELECT Valeur FROM TB_Configuration WHERE Nom="Min_taille_code" ';
res = bdd.requete_unique(req);
min_taille_code = Number(res[0]);
req = 'SELECT Valeur FROM TB_Configuration WHERE Nom="Max_taille_code" ';
res = bdd.requete_unique(req);
max_taille_code = Number(res[0]);
//---------------------------------------------------------------------------------------
// Paramètres spécifiques :
for (i = 1; i < shipheart_specifique_client.length; i ++)
{
var parametre = shipheart_specifique_client[i].split('=');
switch(parametre[0])
{
//--------------------------------------------------------
case 'id_circuit_activation_moteur':
champ_formulaire_Circuit_activation_moteur.model.clear();
champ_formulaire_Circuit_activation_moteur.model.append({"valeur": 0, "texte": qsTr("Aucune") });
req = 'SELECT Cle_primaire, Nom, Adresse_module, No_circuit FROM TB_Circuits ORDER BY Adresse_module, No_circuit';
//console.log("req =" + req);
nb_circuits = bdd.requete(req);
for (j = 0; j < nb_circuits; j ++)
{
res = bdd.lecture_ligne();
if (!res.isEmpty)
{
champ_formulaire_Circuit_activation_moteur.model.append({"valeur": Number(res[0]), "texte": qsTr(" [module %1 - circuit %2] %3").arg(Number(res[2])).arg(Number(res[3]) + 1).arg(res[1])});
}
else break; // Ne devrait pas arriver....
}
// Positionnement de l'index sur la valeur en cours :
var defaut_circuit_activation_moteur = 0
j = recherche_index_valeur_modele(Number(parametre[1]), champ_formulaire_Circuit_activation_moteur.model, "Circuit_activation_moteur");
if (j === -1) champ_formulaire_Circuit_activation_moteur.currentIndex = recherche_index_valeur_modele(defaut_circuit_activation_moteur, champ_formulaire_Circuit_activation_moteur.model, "Circuit_activation_moteur");
else champ_formulaire_Circuit_activation_moteur.currentIndex = j;
break;
//--------------------------------------------------------
default:
console.log("paramètre spécifique client inconnu : [" + parametre[0] + "] = [" + parametre[1] + "]");
break;
}
}
}
//=============================================================================================================
// Mise à jour des valeurs saisies
//=============================================================================================================
function mise_a_jour()
{
var req;
var i;
var chaine_specifique_client;
//=============================================
// Configuration du code d'accès :
var nouveau_code_saisi, nouveau_code_repetition_saisi;
var nouveau_code_saisi_encrypte;
var erreur = false;
nouveau_code_saisi = champ_formulaire_Code.text
nouveau_code_repetition_saisi = champ_formulaire_Code_repetition.text
do { // Fake loop
//.........................................
// Si aucune valeur de code n'est saisie :
if ((nouveau_code_saisi === "") && (nouveau_code_repetition_saisi === "")) break;
//.................................................
// Si la répétition n'est pas égale à l'original :
if (nouveau_code_repetition_saisi !== nouveau_code_saisi)
{
boite_information.niveau = 1
boite_information.imageTitreSource = "/images/attention.png"
boite_information.message = qsTr("Les deux valeurs saisies doivent être identiques !\n\nDe plus, le code doit contenir :\n- %1 chiffres au minimum\n- %2 chiffres au maximum.").arg(min_taille_code).arg(max_taille_code);
boite_information.open();
erreur = true;
break;
}
//.................................................
// Si le format du nouveau code n'est pas correct :
if ((nouveau_code_saisi.length < min_taille_code) || (nouveau_code_saisi.length > max_taille_code))
{
//console.log("Le code saisie a une longueur anormale [" + nouveau_code_saisi.length + "]")
boite_information.niveau = 1
boite_information.imageTitreSource = "/images/attention.png";
boite_information.message = qsTr("Le nouveau code doit contenir :\n- %1 chiffres au minimum\n- %2 chiffres au maximum.\n\nLe code saisi ne répond pas à ces critères et sa mise à jour n'est donc pas effectuée.\n\nMerci de saisir un code au format valide si vous souhaitez le modifier.").arg(min_taille_code).arg(max_taille_code);
boite_information.open();
erreur = true;
break;
}
//.................................................
// Si tout est Ok :
nouveau_code_saisi_encrypte = bdd.sha256(nouveau_code_saisi);
req = 'UPDATE TB_Configuration SET Valeur="' + nouveau_code_saisi_encrypte + '" WHERE Nom="Code_client" ';
//console.log("Requête = " + req)
bdd.requete_unique(req);
shipheart_code_client = nouveau_code_saisi_encrypte;
} while (0);
if (erreur === false)
{
//=============================================
// Mise à jour des paramètres spécifiques :
chaine_specifique_client = shipheart_specifique_client[0]; // Premier champ toujours présent = n° de client spécifique
for (i = 1; i < shipheart_specifique_client.length; i ++) // On commence à 1 car le premier champ est déjà renseigné...
{
var parametre = shipheart_specifique_client[i].split('=');
switch(parametre[0])
{
//--------------------------------------------------------
case 'id_circuit_activation_moteur':
var val = Number(champ_formulaire_Circuit_activation_moteur.model.get(champ_formulaire_Circuit_activation_moteur.currentIndex).valeur);
if (val !== Number(parametre[1]))
{
bdd.requete_unique("UPDATE TB_Circuits SET New_consigne=0 WHERE Cle_primaire='" + Number(parametre[1]) + "'"); // Coupure de l'ancien circuit
if (val > 0) bdd.requete_unique("UPDATE TB_Circuits SET New_consigne=100 WHERE Cle_primaire='" + val + "'"); // Activation du nouveau circuit (sauf si le nouveau circuit est 'Aucun')
}
shipheart_specifique_client[i] = parametre[0]; // Mise à jour de la propriété reconstituer l'ensemble de la chaine dans tous les cas
shipheart_specifique_client[i] += "=" + val;
break;
//--------------------------------------------------------
default :
break;
}
chaine_specifique_client += ";" + shipheart_specifique_client[i]; // Remplissage de la chaine complète pour mise à jour de la BDD
}
// Mise à jour de la BDD :
req = 'UPDATE TB_Configuration SET Valeur="' + chaine_specifique_client + '" WHERE Nom="Specifique_client" ';
//console.log("Requête = " + req)
bdd.requete_unique(req);
}
if (erreur === false) return(0);
else return(-1);
}
//=============================================================================================================
// Recherche de l'index d'une certaine valeur dans un modele
//=============================================================================================================
function recherche_index_valeur_modele(cible, modele, nom_modele)
{
var i;
for (i=0; i<modele.count; i ++)
{
if (modele.get(i).valeur === cible) break; // Cas où la valeur cible a été trouvée parmi les valeurs du modèle
}
if (i === modele.count)
{
console.log("Aucune valeur trouvée pour cible = [" + cible + "] du modèle = " + nom_modele);
i = -1; // Cas où la valeur n'a pas été trouvée parmi les valeurs du modèle
}
//else console.log("Valeur trouvée : [" + i + "] pour cible = [" + cible + "] soit texte = [" + modele.get(i).texte + "]");
return(i);
}
}
File diff suppressed because it is too large Load Diff
+773
View File
@@ -0,0 +1,773 @@
//========================================================================================================================================================================
// CONFIG_VERSION.QML
// Ce fichier traite les informations de versions de ShipHeart
// La page est appelée depuis la page parametres_avances.qml, en cliquant sur "Version"
//========================================================================================================================================================================
import QtQuick 2.12
import QtQuick.Controls 2.12
import qt.koriolan.shipheart 1.0
Shipheart_page {
id: page_config_version
// Mis en forme :
readonly property double pourcentage_largeur_colonne_gauche: 0.50
readonly property double espace_colonnes_principales: 20
readonly property double pourcentage_hauteur_ligne_haut: 0.7
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 hauteur_ligne_formulaire: 60
readonly property double pourcentage_largeur_labels_formulaire: 0.4
readonly property double proportion_hauteur_texte_droite: 0.4
readonly property int hauteur_bouton_droit: 80
readonly property int largeur_bouton_droit: 200
readonly property double pourcentage_bandeau_bas_largeur_zone_retour: 0.86
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 int protection_mode: 0
// Données :
property int valeur_timer_appui_long: 0
property string version_actuelle: ""
property string date_version_actuelle: ""
property string version_nouvelle: ""
property int type_version_nouvelle: 0 // 0: aucune, 1= à valider, 2= à charger
property int demande_maj_soft_en_cours: shipheart_demande_maj_soft_aucune
property bool flag_demande_factory_reset_en_cours: false
property bool flag_demande_factory_reset: false
// 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
{
// Si on est en cours de Factory Reset et qu'un fichier '.enc' est dispo, c'est que ça doit être le fichier
// de Factory Reset généré par sauvegarde.php qui est prêt :
if ((flag_demande_factory_reset) &&
(shipheart_nb_fichiers_a_charger > 0)) // On ne peut pas utiliser (shipheart_fichier_a_charger != "") car on ne prend pas en compte les fichiers de type 'new' (ni 'restore).
{
busy_indicator.running = false;
rebooter();
}
mise_a_jour();
}
}
}
//===========================================================
// 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;
shipheart_clic();
if (mousearea_installer_now.pressed)
{
boite_validation_securisee.niveau = 2;
boite_validation_securisee.boutonOk = false
boite_validation_securisee.boutonAnnuler = true
boite_validation_securisee.boutonSecurise = true
boite_validation_securisee.boutonSecuriseTexte = qsTr("Confirmer");
boite_validation_securisee.imageTitreSource = "/images/attention.png";
boite_validation_securisee.message = qsTr("<b>Mise à jour immédiate</b><br /><br />Si vous confirmez, cette mise à jour peut nécessiter <u>quelques minutes</u> pendant lesquelles <b>votre système ShipHeart ne sera pas accessible</b>.<br /><br />Vous ne devez confirmer l'installation que si vous êtes en situation sûre (au port, au mouillage, ...) et que la perte de contrôle de vos systèmes de bord pendant ce laps de temps est sans aucun danger !<br /><br />Cliquez sur Annuler pour revenir en arrière et effectuer cette installation ultérieurement.")
boite_validation_securisee.alignementMessage = Text.AlignHCenter
boite_validation_securisee.affichageAscenseur = true;
boite_validation_securisee.open();
}
else if (mousearea_installer_next_start.pressed)
{
boite_validation_securisee.niveau = 1;
boite_validation_securisee.boutonOk = false
boite_validation_securisee.boutonAnnuler = true
boite_validation_securisee.boutonSecurise = true
boite_validation_securisee.boutonSecuriseTexte = qsTr("Confirmer");
boite_validation_securisee.imageTitreSource = "/images/attention.png";
boite_validation_securisee.message = qsTr("Si vous confirmez, cette mise à jour sera marquée pour être chargée au prochain démarrage de ShipHeart.<br />Cliquez sur Annuler pour revenir en arrière et la conserver.")
boite_validation_securisee.alignementMessage = Text.AlignHCenter
boite_validation_securisee.affichageAscenseur = true;
boite_validation_securisee.open();
}
else if (mousearea_installer_never.pressed)
{
boite_validation_securisee.niveau = 1;
boite_validation_securisee.boutonOk = false
boite_validation_securisee.boutonAnnuler = true
boite_validation_securisee.boutonSecurise = true
boite_validation_securisee.boutonSecuriseTexte = qsTr("Confirmer");
boite_validation_securisee.imageTitreSource = "/images/attention.png";
boite_validation_securisee.message = qsTr("<b>Suppression de version</b><br /><br />Si vous confirmez, cette mise à jour sera définitivement supprimée.<br />Cliquez sur Annuler pour revenir en arrière et la conserver.")
boite_validation_securisee.alignementMessage = Text.AlignHCenter
boite_validation_securisee.affichageAscenseur = true;
boite_validation_securisee.open();
}
else if (mousearea_factory_reset.pressed)
{
flag_demande_factory_reset_en_cours = true;
boite_validation_securisee.niveau = 2;
boite_validation_securisee.boutonOk = false
boite_validation_securisee.boutonAnnuler = true
boite_validation_securisee.boutonSecurise = true
boite_validation_securisee.boutonSecuriseTexte = qsTr("Réinitialiser");
boite_validation_securisee.imageTitreSource = "/images/attention.png";
boite_validation_securisee.message = qsTr("<b>Réinitialisation Usine</b><br /><br />Si vous confirmez, tous les paramètres de ShipHeart seront complètement et définitivement effacés !<br /><br />Aucune sauvegarde ne sera conservée et tout retour en arrière sera impossible... <br /><br />Cliquez sur Annuler pour revenir en arrière si vous n'êtes pas certain de votre décision.<br /><br />Note : En cas de doute, vous pouvez effectuer une Sauvegarde et <u>la stocker sur une clé externe</u> avant d'effectuer cette réinitialisation usine.")
boite_validation_securisee.alignementMessage = Text.AlignHCenter
boite_validation_securisee.affichageAscenseur = true;
boite_validation_securisee.open();
}
}
}
}
// Titre de la page :
Shipheart_titre_1 {titre:qsTr("Version"); 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);
color: "transparent"
//-------------------------------------------------------------------
// Grille pour afficher les paramètres :
GridView {
id: grille_informations
width: parent.width
height: parent.height
cellWidth: parent.width;
cellHeight: hauteur_ligne_formulaire
snapMode: GridView.SnapToRow // L'arret de l'affichage se fera sur une rangée entière
clip: true // Nécessaire pour que les Delegates s'arretent à l'intérieur de la GridView
model: modele_grille_informations
delegate: Rectangle {
width: grille_informations.cellWidth - 10
height: grille_informations.cellHeight - 5
color: "transparent"
Rectangle { // c'est le bouton proprement dit...
height: parent.height
width:parent.width
color: "transparent"
radius: shipheart_rayon_bouton
anchors.bottom: parent.bottom
Text {
anchors.fill: parent
horizontalAlignment: Qt.AlignLeft
verticalAlignment: Qt.AlignVCenter
leftPadding: 20
text: model.nom_champ + " : " + model.valeur_champ
color: couleur_blanc_casse
font.pixelSize: 18
font.bold: false
wrapMode: Label.Wrap
}
}
}
ListModel {
id: modele_grille_informations
}
}
}
//-------------------------------------------------------
// 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 * pourcentage_hauteur_ligne_haut) - (espace_lignes_principales / 2);
color: "transparent"
border.color: couleur_blanc_casse_transparent
border.width: 2
visible: (type_version_nouvelle > 0)
Column {
anchors.fill: parent
spacing: 20
//=========================================================================
// Texte d'introduction
//=========================================================================
Text {
// height: parent.height * proportion_hauteur_texte_droite
width: parent.width;
anchors.horizontalCenter: parent.horizontalCenter
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
topPadding: 20 // Cette valeur permet d'être aligné avec la version courante...
bottomPadding: 50
font.pixelSize: 18
text: {
let message;
switch (type_version_nouvelle)
{
//-----------------------------
// Fichier dans A_VALIDER :
case shipheart_demande_maj_soft_validation:
message = qsTr("La version <b>%1</b> est disponible.<br /><br />Souhaitez-vous : ").arg(version_nouvelle);
break;
//-----------------------------
// Fichier dans A_CHARGER :
case shipheart_demande_maj_soft_chargement:
message = qsTr("La version <b>%1</b> est disponible.<br /><br />Elle sera automatiquement installée lors du prochain démarrage de ShipHeart.<br />A moins que vous ne choisissiez l'une des options ci-dessous :").arg(version_nouvelle)
break;
//-----------------------------
// Aucun fichier ni dans A_CHARGER, ni dans A_VALIDER :
case shipheart_demande_maj_soft_aucune:
default:
message = "";
}
return message;
}
color: couleur_blanc_casse
wrapMode: Text.Wrap
}
//=========================================================================
// Boutons :
//=========================================================================
Row {
height: hauteur_bouton_droit
anchors.horizontalCenter: parent.horizontalCenter
spacing: 20
//---------------------------------------------------------------
Rectangle {
id: bouton_installer_now
height: hauteur_bouton_droit
width:largeur_bouton_droit
color: shipheart_couleur_fond_bouton
radius: shipheart_rayon_bouton
visible: (type_version_nouvelle > 0)
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_installer_now.pressed
}
Text {
anchors.fill: parent
horizontalAlignment: Qt.AlignHCenter
verticalAlignment: Qt.AlignVCenter
text: qsTr("L'installer\nmaintenant")
color: couleur_blanc_casse
font.pixelSize: 16
wrapMode: Label.Wrap
padding: 10
}
MouseArea {
id: mousearea_installer_now
anchors.fill: parent
onPressed: { // Déclenché lors de l'appui inital
timer_appui_long.running = true;
valeur_timer_appui_long = 0;
demande_maj_soft_en_cours = shipheart_demande_maj_soft_chargement;
}
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;
}
}
}
//---------------------------------------------------------------
Rectangle {
id: bouton_installer_next_start
height: hauteur_bouton_droit
width:largeur_bouton_droit
color: shipheart_couleur_fond_bouton
radius: shipheart_rayon_bouton
visible: (type_version_nouvelle === 1)
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_installer_next_start.pressed
}
Text {
anchors.fill: parent
horizontalAlignment: Qt.AlignHCenter
verticalAlignment: Qt.AlignVCenter
text: qsTr("L'installer au prochain démarrage de ShipHeart")
color: couleur_blanc_casse
font.pixelSize: 16
wrapMode: Label.Wrap
padding: 10
}
MouseArea {
id: mousearea_installer_next_start
anchors.fill: parent
onPressed: { // Déclenché lors de l'appui inital
timer_appui_long.running = true;
valeur_timer_appui_long = 0;
demande_maj_soft_en_cours = shipheart_demande_maj_soft_validation;
}
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;
}
}
}
//---------------------------------------------------------------
Rectangle {
id: bouton_installer_never
height: hauteur_bouton_droit
width:largeur_bouton_droit
color: shipheart_couleur_fond_bouton
radius: shipheart_rayon_bouton
visible: (type_version_nouvelle > 0)
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_installer_never.pressed
}
Text {
anchors.fill: parent
horizontalAlignment: Qt.AlignHCenter
verticalAlignment: Qt.AlignVCenter
text: qsTr("Ne jamais l'installer")
color: couleur_blanc_casse
font.pixelSize: 16
wrapMode: Label.Wrap
padding: 10
}
MouseArea {
id: mousearea_installer_never
anchors.fill: parent
onPressed: { // Déclenché lors de l'appui inital
timer_appui_long.running = true;
valeur_timer_appui_long = 0;
demande_maj_soft_en_cours = shipheart_demande_maj_soft_annulation;
}
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 Bas :
//-------------------------------------------------------
Rectangle {
id: zone_affichage_bas
x: 0
y: shipheart_hauteur_bandeau_haut + zone_affichage_haut_gauche.height + espace_lignes_principales;
width: shipheart_largeur_page
height: (affichage_hauteur_utile * (1 - pourcentage_hauteur_ligne_haut)) - espace_lignes_principales;
color: "transparent"
Text {
width: parent.width;
anchors.horizontalCenter: parent.horizontalCenter
horizontalAlignment: Text.AlignJustify
padding: 20 // Cette valeur permet d'être aligné avec la version courante...
font.pixelSize: 16
text: qsTr("Le présent produit est conçu et fabriqué par la société Koriolan qui dispose de toutes les licences et droits d'exploitation sur les images, \
logiciels et brevets utilisés tant pour sa conception que pour son fonctionnement. \
ShipHeart, Ship2net et Koriolan sont des marques déposées par la société Koriolan. \
Pour toute question relative à ce produit, merci de vous rendre sur le site 'koriolan.tech' ou de nous contacter à l'adresse 'contact@koriolan.tech'.");
color: couleur_blanc_casse
wrapMode: Text.Wrap
}
}
//-------------------------------------------------------
// Bandeau bas :
//-------------------------------------------------------
Row {
x: 0
y: shipheart_hauteur_page - shipheart_hauteur_bandeau_bas;
width: shipheart_largeur_page;
height: shipheart_hauteur_bandeau_bas;
spacing: 5
padding: 5
//.................................................
// Bouton retour
//.................................................
Rectangle {
id: bandeau_bas_zone_retour
width: (bandeau_bas_zone_bouton_factory_reset.visible) ?
(parent.width * pourcentage_bandeau_bas_largeur_zone_retour - (parent.spacing / 2)) :
(parent.width - (2 * parent.padding))
height: shipheart_hauteur_bandeau_bas - parent.topPadding - parent.bottomPadding;
radius: shipheart_rayon_bouton
color: shipheart_couleur_fond_bouton
Image {
source: "/images/icone_retour.png"
sourceSize.height: 30
anchors {
verticalCenter: parent.verticalCenter
horizontalCenter: parent.horizontalCenter
}
}
MouseArea {
id: mouse_area_bandeau_bas_zone_retour
anchors.fill: parent
onClicked: {
shipheart_clic();
shipheart_compteur_time_out_niveau_acces = shipheart_time_out_niveau_acces; // Rechargement au moment de la sortie (SDIR #187)
shipheart_flag_blocage_compteurs_pendant_config = false; // Déblocage du comptage (SDIR #187)
stackView.pop(stackView.get(shipheart_niveau_home)); // Retour direct à la Home Page quand on quitte les Paramètres Avancés...
}
}
}
//.................................................
// Bouton Factory Reset :
//.................................................
Rectangle {
id: bandeau_bas_zone_bouton_factory_reset
width: (parent.width * (1 - pourcentage_bandeau_bas_largeur_zone_retour)) - (parent.spacing / 2)
height: shipheart_hauteur_bandeau_bas - parent.topPadding - parent.bottomPadding;
radius: shipheart_rayon_bouton
color: shipheart_couleur_fond_bouton
visible: (shipheart_type_installation === shipheart_type_installation_shipbase)
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_factory_reset.pressed
}
Image {
source: mousearea_factory_reset.pressed ? "/images/factory_reset_actif.png" : "/images/factory_reset_sombre.png"
sourceSize.height: 40
anchors {
verticalCenter: parent.verticalCenter
horizontalCenter: parent.horizontalCenter
}
}
MouseArea {
id: mousearea_factory_reset
anchors.fill: parent
onPressed: { // Déclenché lors de l'appui inital
valeur_timer_appui_long = 0;
timer_appui_long.running = true;
}
onClicked: { // Déclenché si l'appui n'a pas été suffisement long pour avoir un Press&Hold :
valeur_timer_appui_long = 0;
timer_appui_long.running = false;
}
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 !!)
valeur_timer_appui_long = 0;
timer_appui_long.running = false;
}
}
}
}
//---------------------------
// Busy Indicator :
Shipheart_busy_indicator {
id: busy_indicator
}
//-------------------------------------------------------
// Boite d'information :
//-------------------------------------------------------
Shipheart_boite_dialogue_validation_securisee {
id: boite_validation_securisee
onClicked: {
switch(bouton)
{
case shipheart_boite_dialogue_bouton_4: // Confirmation sécurisée :
if (demande_maj_soft_en_cours !== shipheart_demande_maj_soft_aucune)
{
demande_maj(demande_maj_soft_en_cours);
demande_maj_soft_en_cours = shipheart_demande_maj_soft_aucune;
}
else if (flag_demande_factory_reset_en_cours)
{
demande_factory_reset();
}
break;
case shipheart_boite_dialogue_bouton_2: // Annuler : on revient sur la page pour que l'on puisse enregistrer.
demande_maj_soft_en_cours = shipheart_demande_maj_soft_aucune;
flag_demande_factory_reset_en_cours = false;
break;
default:
}
boite_validation_securisee.close();
}
}
// 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 nb;
var i;
var nom_champ, valeur_champ;
//----------------------------------------------------------------------------
// Mise à jour des informations sur les différentes versions sous-jacentes :
modele_grille_informations.clear();
req = 'SELECT Nom, Valeur FROM TB_Systeme ORDER BY Cle_primaire';
//console.log("req =" + req);
nb = bdd.requete(req);
for (i = 0; i < nb; i ++)
{
res = bdd.lecture_ligne();
if (!res.isEmpty)
{
switch(res[0])
{
// case "Version_soft_package": nom_champ = qsTr("Version du package logiciel"); valeur_champ = res[1]; break;
case "Version_soft_no_package":
version_actuelle = res[1];
nom_champ = qsTr("Version du package logiciel");
valeur_champ = res[1];
break;
case "Version_soft_application_cm3p":
nom_champ = qsTr("Version de l'application");
valeur_champ = res[1];
break;
case "Version_soft_application_qt":
nom_champ = qsTr("Version de l'interface graphique");
valeur_champ = res[1];
break;
case "Version_soft_modem":
if (shipheart_generation_shipbase >= shipheart_generation_shipbase_v2) continue;
nom_champ = qsTr("Version du firmware du modem");
valeur_champ = res[1].replace("Revision:", "");
break;
case "No_imei":
if (shipheart_generation_shipbase >= shipheart_generation_shipbase_v2) continue;
nom_champ = qsTr("Numéro d'IMEI");
valeur_champ = res[1];
break;
case "No_iccid":
if (shipheart_generation_shipbase >= shipheart_generation_shipbase_v2) continue;
nom_champ = qsTr("Numéro d'ICCID");
valeur_champ = res[1].replace("f", "");
break;
case "No_imsi":
if (shipheart_generation_shipbase >= shipheart_generation_shipbase_v2) continue;
nom_champ = qsTr("Numéro d'IMSI");
valeur_champ = res[1];
break;
default: continue;
}
modele_grille_informations.append({"nom_champ": nom_champ, "valeur_champ": valeur_champ});
}
else break; // Ne devrait pas arriver....
}
mise_a_jour();
}
//=============================================================================================================
// Mise à jour de l'état des fichiers disponibles :
//=============================================================================================================
function mise_a_jour()
{
var i;
var fragments_new_version;
version_nouvelle = "";
if (shipheart_fichier_a_charger !== "") // Si un fichier a chargé existe, on ne considère que celui-ci :
{
let c = shipheart_fichier_a_charger.split('.'); // Extraction de la partie avant le premier '.'
fragments_new_version = c[0].split('_'); // Extractions des fragments de version
type_version_nouvelle = shipheart_demande_maj_soft_chargement;
}
else if(shipheart_fichier_a_valider !== "") // ... sinon, on regarde si un fichier à valider existe et on le traite :
{
let c = shipheart_fichier_a_valider.split('.'); // Extraction de la partie avant le premier '.'
fragments_new_version = c[0].split('_'); // Extractions des fragments de version
type_version_nouvelle = shipheart_demande_maj_soft_validation;
}
else type_version_nouvelle = shipheart_demande_maj_soft_aucune;
if (type_version_nouvelle > shipheart_demande_maj_soft_aucune) version_nouvelle = fragments_new_version[2] + "." + fragments_new_version[3] + "." + fragments_new_version[4];
else version_nouvelle = "";
//console.log("Version nouvelle = " + version_nouvelle)
}
//=============================================================================================================
// Mise à jour de la valeur de Demande_maj_soft dans TB_Systeme
//=============================================================================================================
function demande_maj(demande_maj_soft)
{
var commande;
switch (demande_maj_soft)
{
//-----------------------------
// Fichier dans A_VALIDER :
// Note : l'utilisation de 'sudo sh -c "commande" ' permet de lancer un shell en sudo qui, ensuite exécute la commande (en sudo donc)
// Sans passer par cette astuce, l'appel se fait depuis le shell qui a lancé Qt (non-sudo donc) et le fait d'ajouter 'sudo' DANS la commande
// n'est pas prise en compte. La commandes est alors exécutée en non-sudo en, selon, les droits nécessaires, peut échouer.
case shipheart_demande_maj_soft_validation:
commande = 'sudo sh -c "mv ' + shipheart_repertoire_versions_a_valider + '/' + shipheart_fichier_a_valider + ' ' + shipheart_repertoire_versions_a_charger + '/' + shipheart_fichier_a_valider + '" ';
//console.log("Exécution de la commande [ " + commande + " ]");
systeme.commande_shell_non_bloquante(commande);
break;
//-----------------------------
// Fichier dans A_CHARGER :
case shipheart_demande_maj_soft_chargement:
rebooter();
break;
//------------------------------------------------------------
// Demande d'annulation :
case shipheart_demande_maj_soft_annulation:
// Vidage du répertoire A_VALIDER :
commande = 'sudo sh -c "rm -f ' + shipheart_repertoire_versions_a_valider + '/*" ';
//console.log("Exécution de la commande [ " + commande + " ]");
systeme.commande_shell(commande, "");
// Vidage du répertoire A_CHARGER :
commande = 'sudo sh -c "rm -f ' + shipheart_repertoire_versions_a_charger + '/*" ';
//console.log("Exécution de la commande [ " + commande + " ]");
systeme.commande_shell(commande, "");
break;
//-----------------------------
// Aucun fichier ni dans A_CHARGER, ni dans A_VALIDER :
case shipheart_demande_maj_soft_aucune:
default:
break;
}
// Dermande de synchro rapide de l'appli :
synchro_bdd(shipheart_type_synchro_data);
}
//=============================================================================================================
// Traitement de la demande de Factory Reset :
//=============================================================================================================
function demande_factory_reset()
{
var commande;
flag_demande_factory_reset_en_cours = false;
// Vidage du répertoire A_CHARGER :
commande = 'sudo sh -c "rm -f ' + shipheart_repertoire_versions_a_charger + '/*" ';
//console.log("Exécution de la commande [ " + commande + " ]");
systeme.commande_shell(commande, "");
// Vidage du répertoire A_VALIDER :
// Peut être utile pour le fonctionnement après le Factory Reset.
commande = 'sudo sh -c "rm -f ' + shipheart_repertoire_versions_a_valider + '/*" ';
//console.log("Exécution de la commande [ " + commande + " ]");
systeme.commande_shell(commande, "");
// Commande d'exécution du script :
commande = 'sudo sh -c "php ' + shipheart_repertoire_utils + '/' + shipheart_nom_fichier_sauvegarde + ' Factory_reset" ';
//console.log("Exécution de la commande [ " + commande + " ]");
systeme.commande_shell_non_bloquante(commande); // On doit sortir sans attendre la fin de l'exécution pour que le sablier fonctionne.
flag_demande_factory_reset = true; // Permet de valider la recherche d'un fichier 'enc' de fin de sauvegarde
busy_indicator.running = true; // Lance le sablier.
}
}
+311
View File
@@ -0,0 +1,311 @@
import QtQuick 2.12
import QtQuick.Layouts 1.12
import qt.koriolan.shipheart 1.0
import"fonctions.js" as Fonctions
import"fonctions_date_heure.js" as Fonctions_date_heure
import"suncalc.js" as Suncalc
Shipheart_page {
id: page_ephemeride
// Mis en forme :
readonly property double pourcentage_largeur_page_affichage_date_heure: 0.6
readonly property double espace_colonnes_principales: 30
// Affichage :
property bool bouton_bandeau_bas_zone_zone_retour_actif: false
// Données :
property string jour_semaine_courant
property string date_courante
property string heure_courante
property string langue: "FR"
property double test:0
// Modèles :
// Note : les modèles chargés dans 'main.qml' n'ont pas à être rechargés dans les pages suivantes
//===========================================================
// Timer 1 seconde :
//===========================================================
Timer {
id: timer_secondes
interval: 1000
triggeredOnStart: true
running: true
repeat: true
onTriggered: {
// Calcul de la date et de l'heure + mise en forme
var date = Fonctions_date_heure.date_courante(null, shipheart_fuseau_horaire, shipheart_heure_ete);
jour_semaine_courant = Fonctions_date_heure.chaine_jour_semaine(date)
date_courante = Fonctions_date_heure.chaine_date_longue(date, shipheart_langue)
heure_courante = Fonctions_date_heure.chaine_heure(date);
// Position :
mise_a_jour();
}
}
// Titre de la page :
Shipheart_titre_1 {titre:qsTr("Ephéméride"); hauteur: shipheart_hauteur_bandeau_haut}
//-------------------------------------------------------
// Ecran de gauche :
//-------------------------------------------------------
Rectangle {
x: 0
y: shipheart_hauteur_bandeau_haut;
width: (shipheart_largeur_page * pourcentage_largeur_page_affichage_date_heure) - (espace_colonnes_principales / 2);
height: shipheart_hauteur_page - shipheart_hauteur_bandeau_haut - shipheart_hauteur_bandeau_bas;
color: "transparent"
Text {
id: texte_jour_semaine
text: jour_semaine_courant
anchors {
verticalCenter: parent.verticalCenter
verticalCenterOffset: -200
horizontalCenter: parent.horizontalCenter
}
// font.family: "Montserrat"
font.pixelSize: 60
color: couleur_blanc_casse
}
Text {
id: texte_date
text: date_courante
textFormat: Text.RichText // Autorise la prise en compte de <sup> par exemple...
anchors {
verticalCenter: parent.verticalCenter
verticalCenterOffset: -100
horizontalCenter: parent.horizontalCenter
}
font.pixelSize: 60
color: couleur_blanc_casse
}
Text {
id: texte_heure
text: heure_courante
anchors {
verticalCenter: parent.verticalCenter
verticalCenterOffset: 100
horizontalCenter: parent.horizontalCenter
}
font.pixelSize: 100
// font.weight: Font.Bold
color: couleur_blanc_casse
}
Text {
id: texte_fuseau
text: (shipheart_fuseau_horaire > 0) ? "[ UTC +" + shipheart_fuseau_horaire + " ]" : ((shipheart_fuseau_horaire < 0) ? "[ UTC " + shipheart_fuseau_horaire + " ]" : "[ UTC ]" )
// NB : Il n'y a pas de '-' dans le second cas car il est mis automatiquement par la valeur numérique (qui est négative).
anchors {
verticalCenter: parent.verticalCenter
verticalCenterOffset: 250
horizontalCenter: parent.horizontalCenter
}
font.pixelSize: 20
color: couleur_blanc_casse
}
Text {
id: texte_heure_ete
text: (shipheart_heure_ete === 0) ? qsTr("Heure d'hiver") : qsTr("Heure d'été")
anchors {
verticalCenter: parent.verticalCenter
verticalCenterOffset: 250 + texte_fuseau.height + 10
horizontalCenter: parent.horizontalCenter
}
font.pixelSize: 16
// font.italic: true
color: couleur_gris_fonce
}
}
//-------------------------------------------------------
// Ecran de droite :
//-------------------------------------------------------
Rectangle {
x: (shipheart_largeur_page * pourcentage_largeur_page_affichage_date_heure) + espace_colonnes_principales;
y: shipheart_hauteur_bandeau_haut;
width: ((shipheart_largeur_page * (1 - pourcentage_largeur_page_affichage_date_heure)) - (espace_colonnes_principales / 2)) * 0.8;
height: shipheart_hauteur_page - shipheart_hauteur_bandeau_haut - shipheart_hauteur_bandeau_bas;
color: "transparent"
ColumnLayout {
width: parent.width
anchors.verticalCenter: parent.verticalCenter
spacing: 0
ListModel {
id: modele_ephemeride
ListElement { evenement: qsTr("Aube nautique"); heure: "00:00"; ligne_sup: 1; ligne_inf: 1; hauteur:40; image: ""; }
ListElement { evenement: qsTr("Lever du soleil"); heure: "00:00"; ligne_sup: 0; ligne_inf: 1; hauteur:40; image: ""; }
ListElement { evenement: qsTr("Zénith"); heure: "00:00"; ligne_sup: 0; ligne_inf: 1; hauteur:40; image: ""; }
ListElement { evenement: qsTr("Coucher du soleil"); heure: "00:00"; ligne_sup: 0; ligne_inf: 1; hauteur:40; image: ""; }
ListElement { evenement: qsTr("Crépuscule nautique"); heure: "00:00"; ligne_sup: 0; ligne_inf: 1; hauteur:40; image: ""; }
ListElement { evenement: ""; heure: ""; ligne_sup: 0; ligne_inf: 1; hauteur:60; image: ""; }
ListElement { evenement: qsTr("Lune"); heure: ""; ligne_sup: 0; ligne_inf: 1; hauteur:60; image: "/images/lune_1.png"; }
ListElement { evenement: ""; heure: ""; ligne_sup: 0; ligne_inf: 1; hauteur:60; image: ""; }
ListElement { evenement: qsTr("Latitude"); heure: "Inconnue"; ligne_sup: 0; ligne_inf: 1; hauteur:40; image: ""; }
ListElement { evenement: qsTr("Longitude"); heure: "Inconnue"; ligne_sup: 0; ligne_inf: 1; hauteur:40; image: ""; }
}
Repeater {
model: modele_ephemeride
Rectangle {
Layout.alignment: Qt.AlignCenter
Layout.preferredWidth: parent.width
Layout.preferredHeight: model.hauteur
color: "transparent"
Rectangle {
id: ligne_sup
height:1
width:parent.width
color: couleur_gris_tres_fonce_transparent
visible: model.ligne_sup ? 1 : 0;
}
Text {
id: titre_ligne
width: parent.width / 2;
anchors.verticalCenter: parent.verticalCenter
horizontalAlignment: Text.AlignLeft
leftPadding: 10
text: model.evenement
font.pixelSize: 18
color: couleur_blanc_casse
}
Text {
x: titre_ligne.width;
width: parent.width - titre_ligne.width;
anchors.verticalCenter: parent.verticalCenter
horizontalAlignment: Text.AlignRight
rightPadding: 10
text: model.heure
font.pixelSize: 18
color: couleur_blanc_casse
visible: (model.image === "") ? true : false;
}
Image {
visible: (model.image === "") ? false : true;
source: model.image;
anchors.right: parent.right
anchors.rightMargin: 20
anchors.verticalCenter: parent.verticalCenter
sourceSize.height: 40;
}
Rectangle {
id: ligne_inf
anchors.top: parent.bottom;
height:1
width:parent.width
color: couleur_gris_tres_fonce_transparent
visible: model.ligne_inf ? 1 : 0;
}
}
}
}
}
//-------------------------------------------------------
// Bandeau bas :
//-------------------------------------------------------
Row {
x: 0
y: shipheart_hauteur_page - shipheart_hauteur_bandeau_bas;
width: shipheart_largeur_page;
height: shipheart_hauteur_bandeau_bas;
spacing: 5
padding: 5
//.................................................
// Bouton retour
//.................................................
Rectangle {
id: bandeau_bas_zone_retour
width: parent.width - (2 * parent.padding)
height: shipheart_hauteur_bandeau_bas - parent.topPadding - parent.bottomPadding;
radius: 5
color: shipheart_couleur_fond_bouton
Image {
source: "/images/icone_retour.png"
sourceSize.height: 30
anchors {
verticalCenter: parent.verticalCenter
horizontalCenter: parent.horizontalCenter
}
}
MouseArea { // Zone d'interaction. On est obligé de la mettre dans la grille pour que Rectangle et MouseArea soient frères ou parent/enfant ET qu'on n'occupe qu'un espace dans la grille.
id: mouse_area_bandeau_bas_zone_retour
anchors.fill: parent
onClicked: {
shipheart_clic();
stackView.pop()
}
}
}
}
//===========================================================
// Economiseur : Doit être placé en fin de page pour fonctionner
//===========================================================
MouseArea {
anchors.fill: parent
preventStealing: true // Permet que cela fonctionne même si un élément flickable (notamment) soit derrière et "vole" le click
enabled: flag_economiseur_actif
onClicked: {
mise_en_veille(0);
}
}
//=============================================================================================================
// Mise à jour de la position
//=============================================================================================================
function mise_a_jour()
{
var req, res
var lat, lon;
var altitude = 0;
// Latitude :
modele_ephemeride.set(8, {"heure": Fonctions.chaine_latitude(shipheart_latitude) })
// Longitude :
modele_ephemeride.set(9, {"heure": Fonctions.chaine_longitude(shipheart_longitude) })
// Informations sur le soleil :
modele_ephemeride.set(0, {"heure": shipheart_heure_aube_nautique });
modele_ephemeride.set(1, {"heure": shipheart_heure_lever_soleil });
modele_ephemeride.set(2, {"heure": shipheart_heure_zenith });
modele_ephemeride.set(3, {"heure": shipheart_heure_coucher_soleil });
modele_ephemeride.set(4, {"heure": shipheart_heure_crepuscule_nautique });
// Ajout du calcul de la hauteur du soleil au zénith :
// NON TESTE !! A Tester !!
//var sun_positions = Suncalc.SunCalc.getPosition(Fonctions_date_heure.date_courante(null, shipheart_fuseau_horaire, shipheart_heure_ete), lat, lon);
//console.log("Hauteur du soleil au Zénith : " + (sun_positions.altitude * (180/3.141592653)).toFixed(1) + "°"); // La valeur d'altitude est retournée en radians
//console.log("Azimuth du soleil au Zénith : " + (sun_positions.azimuth * (180/3.141592653)).toFixed(1) + "°"); // La valeur d'azimuth est retournée en radians
// Informations sur la lune :
//console.time('calcul_heures_lune')
var moon_status = Suncalc.SunCalc.getMoonIllumination(Fonctions_date_heure.date_courante(null, shipheart_fuseau_horaire, shipheart_heure_ete));
//console.timeEnd('calcul_heures_lune')
var phase = Number(moon_status.phase);
modele_ephemeride.set(6, {"image": "/images/lune_" + Number(Math.trunc(phase * 16) + 1) + ".png" }) // Sélection d'une image entre 'lune_1.png' et 'lune_16.png'
}
}
+378
View File
@@ -0,0 +1,378 @@
//========================================================================================================================================================================
// ETALONNAGE_CAPTEUR.QML
// Ce fichier traite l'étalonnage d'une carte. La page affiche notamment :
// - La valeur brute actuelle
// - la valeur corrigée actuelle (rensignée manuellement)
// - l'offset actuel
// La page est appelée depuis la page config_capteur.qml, en cliquant sur le bouton ETALONNAGE lorsqu'il est disponible.
//========================================================================================================================================================================
import QtQuick 2.12
import QtQml.Models
import qt.koriolan.shipheart 1.0
Shipheart_page {
id: page_etalonnage_capteur
// Mis en forme :
readonly property double pourcentage_largeur_colonne_gauche: 0.75
readonly property double espace_colonnes_principales: 20
readonly property double pourcentage_hauteur_ligne_haut: 1
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 hauteur_ligne_formulaire: 60
readonly property double pourcentage_largeur_labels_formulaire: 0.4
readonly property int hauteur_bouton_droit: 80
readonly property double pourcentage_bandeau_bas_largeur_zone_retour: 1
readonly property int periode_init_raffraichissement_parametres: 1 // En ms
readonly property int periode_raffraichissement_parametres: 1000 // En ms
// Affichage :
property bool bouton_bandeau_bas_zone_retour_actif: false
property int protection_mode: 0
property bool flag_init: true // Permet de bloquer certains affichage tant que l'init n'est pas terminée
// Données :
property int id_capteur: -1
property string nom_capteur: "---"
property double valeur_old: 0
property double valeur_brute: 0
property double valeur_new: 0
property double offset_old: 0
property double offset_new: 0
property string symbole_unite: ""
property int precision_affichage: 1
property int nouvelle_unite: 0
// Modèles :
// Note : les modèles chargés dans 'main.qml' n'ont pas à être rechargés dans les pages suivantes
//===========================================================
// Timer periode raffraichissement parametres capteur :
//===========================================================
Timer {
id: timer_raffraichissement_parametres
interval: periode_init_raffraichissement_parametres
triggeredOnStart: false
running: true
repeat: true
onTriggered: {
if (interval == periode_init_raffraichissement_parametres) // Premier passage, 1ms après le premier chargement
{
interval = periode_raffraichissement_parametres;
init_page();
}
else if (!clavier.active) mise_a_jour(); // Permet la mise à jour en temps réel du moment que le clavier n'est pas actif.
}
}
// Titre de la page :
Shipheart_titre_1 {titre:qsTr("Etalonnage du capteur [%1]").arg(nom_capteur); 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);
color: "transparent"
ListView {
id: grille_principale
width: parent.width
height: parent.height
snapMode: GridView.SnapToRow // L'arret de l'affichage se fera sur une rangée entière
clip: true // Nécessaire pour que les Delegates s'arretent à l'intérieur de la GridView
model: modele_grille_principale
footer: Rectangle { // Permet de dégager la place suffisante en fin de GridView pour accéder même au dernier élément lorsque que clavier est déplié
height: shipheart_hauteur_clavier
color: "transparent"
}
ObjectModel {
id: modele_grille_principale
//-------------------------------------------------------------------------------
// Ligne de formulaire : Valeur brute actuelle
Rectangle {
width: (shipheart_largeur_page * pourcentage_largeur_colonne_gauche) - (espace_colonnes_principales / 2)
height: visible ? hauteur_ligne_formulaire : 0
color: "transparent"
Shipheart_formulaire_label {
text: qsTr("Valeur brute actuelle")
width: parent.width * pourcentage_largeur_labels_formulaire
}
Shipheart_formulaire_valeur_fixe {
text: valeur_brute.toFixed(precision_affichage) + " " + symbole_unite
width: parent.width * (1 - pourcentage_largeur_labels_formulaire)
x: parent.width * pourcentage_largeur_labels_formulaire
}
Shipheart_formulaire_ligne_separation {
haut_bas_hautbas: shipheart_formulaire_ligne_separation_basse
}
}
//-------------------------------------------------------------------------------
// Ligne de formulaire : Valeur_corrigee
Rectangle {
width: (shipheart_largeur_page * pourcentage_largeur_colonne_gauche) - (espace_colonnes_principales / 2)
height: visible ? hauteur_ligne_formulaire : 0
color: "transparent"
Shipheart_formulaire_label {
text: qsTr("Valeur corrigée actuelle (%1)").arg(symbole_unite)
width: parent.width * pourcentage_largeur_labels_formulaire
}
Shipheart_formulaire_textfield {
id: champ_formulaire_Valeur_corrigee
text: valeur_new.toFixed(precision_affichage)
width: parent.width * (1 - pourcentage_largeur_labels_formulaire)
x: parent.width * pourcentage_largeur_labels_formulaire
flag_num_only: true
onAccepted: { // Mise à jour à l'appui sur RETURN mais PAS en repliant le clavier (avec le bouton du clavier et la flèche vers le bas).
mise_a_jour(); // Dans tous les cas, la MAJ sera faite à l'enregistrement.
}
}
Shipheart_formulaire_ligne_separation {
haut_bas_hautbas: shipheart_formulaire_ligne_separation_basse
}
}
//-------------------------------------------------------------------------------
// Ligne de formulaire : Offset
Rectangle {
width: (shipheart_largeur_page * pourcentage_largeur_colonne_gauche) - (espace_colonnes_principales / 2)
height: visible ? hauteur_ligne_formulaire : 0
color: "transparent"
Shipheart_formulaire_label {
text: qsTr("Correction")
width: parent.width * pourcentage_largeur_labels_formulaire
}
Shipheart_formulaire_valeur_fixe {
text: ((offset_new > 0) ? "+" : "") + offset_new.toFixed(precision_affichage) + " " + symbole_unite
width: parent.width * (1 - pourcentage_largeur_labels_formulaire)
x: parent.width * pourcentage_largeur_labels_formulaire
}
Shipheart_formulaire_ligne_separation {
haut_bas_hautbas: shipheart_formulaire_ligne_separation_basse
}
}
}
}
}
//-------------------------------------------------------
// 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 * pourcentage_hauteur_ligne_haut) - (espace_lignes_principales / 2);
color: "transparent"
Column {
anchors.fill: parent
spacing: 10
// Bouton Enregistrer :
Rectangle {
width: parent.width
height: hauteur_bouton_droit
color: shipheart_couleur_fond_bouton
radius: shipheart_rayon_bouton
// visible: !clavier.active // SDIR #255
Text {
anchors {
verticalCenter: parent.verticalCenter
horizontalCenter: parent.horizontalCenter
}
font.pixelSize: 20
text: qsTr("Enregistrer")
color: couleur_blanc_casse
}
MouseArea {
anchors.fill: parent
onClicked: {
shipheart_clic();
enregistrement();
stackView.pop();
}
}
}
}
}
//-------------------------------------------------------
// 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 * pourcentage_bandeau_bas_largeur_zone_retour) - (2 * parent.padding)
height: shipheart_hauteur_bandeau_bas - parent.topPadding - parent.bottomPadding;
radius: shipheart_rayon_bouton
color: shipheart_couleur_fond_bouton
Image {
source: "/images/icone_retour.png"
sourceSize.height: 30
anchors {
verticalCenter: parent.verticalCenter
horizontalCenter: parent.horizontalCenter
}
}
MouseArea { // Zone d'interaction. On est obligé de la mettre dans la grille pour que Rectangle et MouseArea soient frères ou parent/enfant ET qu'on n'occupe qu'un espace dans la grille.
id: mouse_area_bandeau_bas_zone_retour
anchors.fill: parent
onClicked: {
shipheart_clic();
stackView.pop()
}
}
}
}
//===========================================================
// Economiseur : Doit être placé en fin de page pour fonctionner
//===========================================================
MouseArea {
anchors.fill: parent
preventStealing: true // Permet que cela fonctionne même si un élément flickable (notamment) soit derrière et "vole" le click
enabled: flag_economiseur_actif
onClicked: {
mise_en_veille(0);
}
}
//=============================================================================================================
// Initialistion de la page
//=============================================================================================================
function init_page()
{
var i;
var res;
var id_variable;
var tmp;
if (id_capteur > 0) res = bdd.requete_unique('SELECT Nom, Valeur, Offset, Precision_affichage_accueil, ID_Variable FROM TB_Capteurs WHERE Cle_primaire=' + id_capteur + ' ');
else
{
console.log("La page 'etalonnage_capteur.qml' a été appelée sans 'id_capteur' valide. L'affichage de la page est impossible");
stackView.pop();
}
i = 0;
nom_capteur = res[i++];
valeur_old = Number(res[i++]); // La Valeur en BDD est une valeur corrigée avec l'offset BDD
offset_old = Number(res[i++]);
precision_affichage = Number(res[i++]);
id_variable = Number(res[i++]);
valeur_brute = valeur_old - offset_old;
valeur_new = valeur_old;
offset_new = offset_old;
res = bdd.requete_unique('SELECT Unite, Conversion_unite FROM TB_Variables WHERE Cle_primaire=' + id_variable + ' ');
symbole_unite = res[0];
// Non utilisé pour le moment...
/* switch(Number(res[1]))
{
case shipheart_type_unite_volume:
nouvelle_unite = shipheart_unite_volume;
symbole_unite = shipheart_unite_volume_symbole;
break;
case shipheart_type_unite_temperature:
nouvelle_unite = shipheart_unite_temperature;
symbole_unite = shipheart_unite_temperature_symbole;
break;
default:
nouvelle_unite = 0;
symbole_unite = unite;
break;
}
*/
}
//=============================================================================================================
// Mise à jour de la valeur instantannée
//=============================================================================================================
function mise_a_jour()
{
var res
valeur_new = Number(champ_formulaire_Valeur_corrigee.text);
offset_new = valeur_new - valeur_brute;
/* // Mise à jour avec dernière valeur en cours :
res = bdd.requete_unique('SELECT Valeur FROM TB_Capteurs WHERE Cle_primaire="' + id_capteur + '" ');
valeur_brute = Number(res[0]);
valeur_corrigee = valeur_brute + offset;
*/
}
//=============================================================================================================
// Enregistrement de la nouvelle valeur d'offset
//=============================================================================================================
function enregistrement()
{
var req, res
mise_a_jour(); // Pour avoir la dernière valeur d'offset
// Mise à jour de l'offset de correction :
req = 'UPDATE TB_Capteurs SET Offset=' + offset_new + ' WHERE Cle_primaire="' + id_capteur + '" ';
res = 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);
}
}
+904
View File
@@ -0,0 +1,904 @@
//========================================================================================================================================================================
// ETALONNAGE_CARTE.QML
// Ce fichier traite l'étalonnage d'une carte. La page affiche notamment :
// - La valeur brute actuelle
// - la valeur corrigée actuelle (rensignée manuellement)
// - l'offset actuel
// La page est appelée depuis la page information_materiel.qml, en cliquant sur le bouton ETALONNAGE lorsqu'il est disponible.
//========================================================================================================================================================================
import QtQuick 2.12
import QtQuick.Controls 2.12
import qt.koriolan.shipheart 1.0
Shipheart_page {
id: page_etalonnage_carte
// Mis en forme :
readonly property double pourcentage_largeur_colonne_gauche: 0.75
readonly property double espace_colonnes_principales: 20
readonly property double pourcentage_hauteur_ligne_haut: 1
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 hauteur_ligne_formulaire: 60
readonly property double pourcentage_largeur_labels_formulaire: 0.4
readonly property double nombre_colonnes_secondaires: 2 // Cette valeur permet de découper les (1.0 - pourcentage_largeur_labels_formulaire) x pourcentage_largeur_colonne_gauche pour afficher les infos de chaque point d'étalonnage
readonly property double espace_colonnes_secondaires: 20
readonly property int nombre_circuits_affiches_sur_une_colonne: 5
readonly property int hauteur_bouton_droit: 80
readonly property double pourcentage_bandeau_bas_largeur_zone_retour: 1
readonly property int periode_init_raffraichissement_parametres: 1 // En ms
readonly property int periode_raffraichissement_parametres: 1000 // En ms
// Affichage :
property bool bouton_bandeau_bas_zone_retour_actif: false
property int protection_mode: 0
property bool flag_init: true // Permet de bloquer certains affichage tant que l'init n'est pas terminée
// Données :
property int adresse_module: -1
property string nom_carte: "---"
property int id_batterie: -1 // Cle_primaire de la batterie à laquelle le module est affecté (cas d'un ShipBS)
property string eeprom: ""
property double courant_differentiel: 0
property double courant_decharge: 0
property double tension: 0
property double resistance: 0
property bool flag_point_memorise_1: false
property double courant_differentiel_1: 0
property double courant_decharge_1: 0
property double tension_1: 0
property bool flag_point_memorise_2: false
property double courant_differentiel_2: 0
property double courant_decharge_2: 0
property double tension_2: 0
// Modèles :
// Note : les modèles chargés dans 'main.qml' n'ont pas à être rechargés dans les pages suivantes
//===========================================================
// Timer periode raffraichissement parametres capteur :
//===========================================================
Timer {
id: timer_raffraichissement_parametres
interval: periode_init_raffraichissement_parametres
triggeredOnStart: false
running: true
repeat: true
onTriggered: {
if (interval == periode_init_raffraichissement_parametres) // Premier passage, 1ms après le premier chargement
{
interval = periode_raffraichissement_parametres;
init_page();
}
else if (!clavier.active) mise_a_jour(); // Permet la mise à jour en temps réel du moment que le clavier n'est pas actif.
}
}
// Titre de la page :
Shipheart_titre_1 {titre:qsTr("Etalonnage du module [%1 - #%2]").arg(nom_carte).arg(adresse_module); 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);
color: "transparent"
//-------------------------------------------------------------------------------
// Ligne de formulaire : N° des points d'étalonnage
Rectangle {
width: (shipheart_largeur_page * pourcentage_largeur_colonne_gauche) - (espace_colonnes_principales / 2)
height: visible ? hauteur_ligne_formulaire : 0
color: "transparent"
// Label de la ligne :
Shipheart_formulaire_label {
id: label_no_points
text: ""
width: parent.width * pourcentage_largeur_labels_formulaire
}
// Première valeur d'étalonnage :
Rectangle {
id: val_1_no_points
width: (parent.width * (1 - pourcentage_largeur_labels_formulaire) / nombre_colonnes_secondaires) - (espace_colonnes_secondaires / 2)
height: parent.height
anchors {
left: label_no_points.right
leftMargin: 0
}
color: "transparent"
Label {
anchors {
verticalCenter: parent.verticalCenter
horizontalCenter: parent.horizontalCenter
}
font.pixelSize: 18
color: couleur_blanc_casse
font.weight: Font.Bold
text: qsTr("Point 1");
}
}
// Deuxième valeur d'étalonnage :
Rectangle {
id: val_2_no_points
width: (parent.width * (1 - pourcentage_largeur_labels_formulaire) / nombre_colonnes_secondaires) - (espace_colonnes_secondaires / 2)
height: parent.height
anchors {
left: val_1_no_points.right
leftMargin: espace_colonnes_secondaires
}
color: "transparent"
Label {
anchors {
verticalCenter: parent.verticalCenter
horizontalCenter: parent.horizontalCenter
}
font.pixelSize: 18
color: couleur_blanc_casse
font.weight: Font.Bold
text: qsTr("Point 2");
}
}
}
//-------------------------------------------------------------------------------
// Ligne de formulaire : Courant de décharge
Rectangle {
y: hauteur_ligne_formulaire
width: (shipheart_largeur_page * pourcentage_largeur_colonne_gauche) - (espace_colonnes_principales / 2)
height: visible ? hauteur_ligne_formulaire : 0
color: "transparent"
// Label de la ligne :
Shipheart_formulaire_label {
id: label_courant_decharge
text: qsTr("Courant de décharge")
width: parent.width * pourcentage_largeur_labels_formulaire
}
// Première valeur d'étalonnage :
Rectangle {
id: val_1_courant_decharge
width: (parent.width * (1 - pourcentage_largeur_labels_formulaire) / nombre_colonnes_secondaires) - (espace_colonnes_secondaires / 2)
height: parent.height
anchors {
left: label_courant_decharge.right
leftMargin: 0
}
color: "transparent"
Label {
anchors {
verticalCenter: parent.verticalCenter
horizontalCenter: parent.horizontalCenter
}
font.pixelSize: 18
color: couleur_blanc_casse
text: courant_decharge_1.toFixed(3) + " A"
}
}
// Deuxième valeur d'étalonnage :
Rectangle {
id: val_2_courant_decharge
width: (parent.width * (1 - pourcentage_largeur_labels_formulaire) / nombre_colonnes_secondaires) - (espace_colonnes_secondaires / 2)
height: parent.height
anchors {
left: val_1_courant_decharge.right
leftMargin: espace_colonnes_secondaires
}
color: "transparent"
Label {
anchors {
verticalCenter: parent.verticalCenter
horizontalCenter: parent.horizontalCenter
}
font.pixelSize: 18
color: couleur_blanc_casse
text: courant_decharge_2.toFixed(3) + " A"
}
}
// Ligne de séparation :
Shipheart_formulaire_ligne_separation {
haut_bas_hautbas: shipheart_formulaire_ligne_separation_haute
}
}
//-------------------------------------------------------------------------------
// Ligne de formulaire : Courant différentiel
Rectangle {
y: 2 * hauteur_ligne_formulaire
width: (shipheart_largeur_page * pourcentage_largeur_colonne_gauche) - (espace_colonnes_principales / 2)
height: visible ? hauteur_ligne_formulaire : 0
color: "transparent"
// Label de la ligne :
Shipheart_formulaire_label {
id: label_courant_differentiel
text: qsTr("Courant différentiel")
width: parent.width * pourcentage_largeur_labels_formulaire
}
// Première valeur d'étalonnage :
Rectangle {
id: val_1_courant_differentiel
width: (parent.width * (1 - pourcentage_largeur_labels_formulaire) / nombre_colonnes_secondaires) - (espace_colonnes_secondaires / 2)
height: parent.height
anchors {
left: label_courant_differentiel.right
leftMargin: 0
}
color: "transparent"
Label {
anchors {
verticalCenter: parent.verticalCenter
horizontalCenter: parent.horizontalCenter
}
font.pixelSize: 18
color: couleur_blanc_casse
text: courant_differentiel_1.toFixed(3) + " A"
}
}
// Deuxième valeur d'étalonnage :
Rectangle {
id: val_2_courant_differentiel
width: (parent.width * (1 - pourcentage_largeur_labels_formulaire) / nombre_colonnes_secondaires) - (espace_colonnes_secondaires / 2)
height: parent.height
anchors {
left: val_1_courant_differentiel.right
leftMargin: espace_colonnes_secondaires
}
color: "transparent"
Label {
anchors {
verticalCenter: parent.verticalCenter
horizontalCenter: parent.horizontalCenter
}
font.pixelSize: 18
color: couleur_blanc_casse
text: courant_differentiel_2.toFixed(3) + " A"
}
}
// Ligne de séparation :
Shipheart_formulaire_ligne_separation {
haut_bas_hautbas: shipheart_formulaire_ligne_separation_haute
}
}
//-------------------------------------------------------------------------------
// Ligne de formulaire : Boutons de mémorisation
Rectangle {
y: 3 * hauteur_ligne_formulaire
width: (shipheart_largeur_page * pourcentage_largeur_colonne_gauche) - (espace_colonnes_principales / 2)
height: visible ? hauteur_bouton_droit : 0
color: "transparent"
// Label de la ligne :
Shipheart_formulaire_label {
id: label_boutons
text: ""
width: parent.width * pourcentage_largeur_labels_formulaire
}
// Première valeur d'étalonnage :
Rectangle {
id: val_1_boutons
width: (parent.width * (1 - pourcentage_largeur_labels_formulaire) / nombre_colonnes_secondaires) - (espace_colonnes_secondaires / 2)
height: parent.height - 10
radius: shipheart_rayon_bouton
anchors {
left: label_boutons.right
leftMargin: 0
verticalCenter: parent.verticalCenter
}
color: shipheart_couleur_fond_bouton
Label {
anchors {
verticalCenter: parent.verticalCenter
horizontalCenter: parent.horizontalCenter
}
font.pixelSize: 18
color: flag_point_memorise_1 ? couleur_gris_fonce : couleur_blanc_casse
text: flag_point_memorise_1 ? qsTr("Recalculer ce point") : qsTr("Mémoriser ce point")
}
MouseArea {
anchors.fill: parent
onClicked: {
shipheart_clic();
if (flag_point_memorise_1) flag_point_memorise_1 = false;
else
{
if (flag_point_memorise_2)
{
if ((courant_differentiel_1 === courant_differentiel_2) ||
(courant_decharge_1 === courant_decharge_2))
{
boite_alerte.open();
}
else flag_point_memorise_1 = true;
}
else flag_point_memorise_1 = true;
}
}
}
}
// Deuxième valeur d'étalonnage :
Rectangle {
id: val_2_boutons
width: (parent.width * (1 - pourcentage_largeur_labels_formulaire) / nombre_colonnes_secondaires) - (espace_colonnes_secondaires / 2)
height: parent.height - 10
radius: shipheart_rayon_bouton
anchors {
left: val_1_boutons.right
leftMargin: espace_colonnes_secondaires
verticalCenter: parent.verticalCenter
}
color: shipheart_couleur_fond_bouton
Label {
anchors {
verticalCenter: parent.verticalCenter
horizontalCenter: parent.horizontalCenter
}
font.pixelSize: 18
color: flag_point_memorise_2 ? couleur_gris_fonce : couleur_blanc_casse
text: flag_point_memorise_2 ? qsTr("Recalculer ce point") : qsTr("Mémoriser ce point")
}
MouseArea {
anchors.fill: parent
onClicked: {
shipheart_clic();
if (flag_point_memorise_2) flag_point_memorise_2 = false;
else
{
if (flag_point_memorise_1)
{
if ((courant_differentiel_1 === courant_differentiel_2) ||
(courant_decharge_1 === courant_decharge_2))
{
boite_alerte.open();
}
else flag_point_memorise_2 = true;
}
else flag_point_memorise_2 = true;
}
}
}
}
// Ligne de séparation :
Shipheart_formulaire_ligne_separation {
haut_bas_hautbas: shipheart_formulaire_ligne_separation_haute_basse
}
}
//-------------------------------------------------------------------------------
// Boutons de circuits :
ListView {
id: grille_principale
x: parent.width * pourcentage_largeur_labels_formulaire
y: (3 * hauteur_ligne_formulaire) + hauteur_bouton_droit
width: parent.width * (1- pourcentage_largeur_labels_formulaire)
height: parent.height - y
snapMode: GridView.SnapToRow // L'arret de l'affichage se fera sur une rangée entière
clip: true // Nécessaire pour que les Delegates s'arretent à l'intérieur de la GridView
model: modele_grille_principale
delegate: Rectangle {
width: grille_principale.width
height: grille_principale.height / nombre_circuits_affiches_sur_une_colonne
color: "transparent"
Rectangle { // c'est le bouton proprement dit...
width:parent.width
height: parent.height - 10
color: shipheart_couleur_fond_bouton
radius: shipheart_rayon_bouton
anchors.bottom: parent.bottom
Text { // Nom du circuit...
anchors.fill: parent
horizontalAlignment: Qt.AlignLeft
verticalAlignment: Qt.AlignTop
topPadding: 10
leftPadding: 20
text: model.nom
color: commandable ? couleur_blanc_casse : couleur_gris_fonce
font.pixelSize: 18
}
Text { // Intensité du circuit...
anchors.fill: parent
horizontalAlignment: Qt.AlignLeft
verticalAlignment: Qt.AlignBottom
bottomPadding: 10
leftPadding: 20
text: model.intensite.toFixed(1) + " A"
color: commandable ? couleur_blanc_casse : couleur_gris_fonce
font.pixelSize: 18
}
Rectangle { // Zone de consigne du circuit...
anchors {
top: parent.top
bottom: parent.bottom
right: parent.right
}
width: 100
radius: shipheart_rayon_bouton
color: (consigne === 0) ? couleur_rouge_transparent : couleur_vert_transparent
visible: commandable
Text { // Valeur de la consigne...
anchors.fill: parent
horizontalAlignment: Qt.AlignHCenter
verticalAlignment: Qt.AlignVCenter
text: (consigne === 0) ? qsTr("Off") : ((consigne === 100) ? qsTr("On") : qsTr("On (%1 %%)").arg(consigne))
color: (consigne === 0) ? couleur_rouge : couleur_green_yellow
font.pixelSize: 18
}
}
MouseArea{
anchors.fill : parent
onClicked: {
shipheart_clic();
toggle_circuit(index);
}
}
}
}
/* footer: Rectangle { // Permet de dégager la place suffisante en fin de GridView pour accéder même au dernier élément lorsque que clavier est déplié
height: shipheart_hauteur_clavier
color: "transparent"
}
*/
ListModel {
id: modele_grille_principale
}
}
}
//-------------------------------------------------------
// 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 * pourcentage_hauteur_ligne_haut) - (espace_lignes_principales / 2);
color: "transparent"
Column {
anchors.fill: parent
spacing: 10
// Bouton Enregistrer :
Rectangle {
width: parent.width
height: hauteur_bouton_droit
color: shipheart_couleur_fond_bouton
radius: shipheart_rayon_bouton
visible:
// !clavier.active && // SDIR #255
!flag_init &&
flag_point_memorise_1 &&
flag_point_memorise_2
Text {
anchors {
verticalCenter: parent.verticalCenter
horizontalCenter: parent.horizontalCenter
}
font.pixelSize: 20
text: qsTr("Enregistrer")
color: couleur_blanc_casse
}
MouseArea {
anchors.fill: parent
onClicked: {
shipheart_clic();
enregistrement();
sortie();
stackView.pop();
}
}
}
// Bouton Information :
Rectangle {
width: parent.width
height: hauteur_bouton_droit
color: shipheart_couleur_fond_bouton
radius: shipheart_rayon_bouton
visible: (clavier.active || flag_init) ? false : true; // Si le clavier est déplié ou si l'init est eb cours, on ne propose pas le bouton
Text {
anchors {
verticalCenter: parent.verticalCenter
horizontalCenter: parent.horizontalCenter
}
font.pixelSize: 20
text: qsTr("Aide")
color: couleur_blanc_casse
}
MouseArea {
anchors.fill: parent
onClicked: {
shipheart_clic();
boite_information_shipbs.open();
}
}
}
}
}
//-------------------------------------------------------
// 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 * pourcentage_bandeau_bas_largeur_zone_retour) - (2 * parent.padding)
height: shipheart_hauteur_bandeau_bas - parent.topPadding - parent.bottomPadding;
radius: shipheart_rayon_bouton
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();
sortie();
stackView.pop()
}
}
}
}
//===========================================================
// Boite d'alerte :
//===========================================================
Shipheart_boite_dialogue {
id: boite_alerte
niveau: 1
imageTitreSource: "/images/attention.png";
message: qsTr("L'étalonnage ne peut pas se faire avec deux points identiques.<br /><br />
Essayer de modifier au moins l'un des deux points de référence en jouant sur la commande des circuits ci-dessous.<br />")
}
//===========================================================
// Boite d'information :
//===========================================================
Shipheart_boite_dialogue {
id: boite_information_shipbs
niveau: 0
imageTitreSource: "/images/ampoule.png";
alignementMessage: Text.AlignJustify
message: qsTr("Le principe de l'étalonnage est de comparer en <u>2 points différents</u> la valeur de courant différentiel mesurée par le ShipBS d'une part et
le courant de décharge mesuré par le système ShipHeart d'autre part.<br />
Par exemple, en soumettant le ShipBS en cours d'étalonnage à une consommation faible ou nulle (premier point)
puis en mettant en fonctionnement un ou plusieurs appareils pour obtenir une consommation de plusieurs ampères si possible (second point).<br />
<br /><u><b>Très important</b> : pendant toute la phase d'étalonnage aucun courant de <b>charge</b> ne doit être appliqué sur ShipBS !</u>
Il est donc essentiel de déconnecter tous les chargeurs, régulateurs solaires, etc... du système pendant l'étalonnage.
Faute de cela, les valeurs d'étalonnage seront erronées...<br /><br />
Remarques :<br />
- toutes les consommations doivent passer par le système ShipHeart ! Il faut donc s'assurer qu'aucune charge n'est connectée directement
au parc de batteries.<br />
- les valeurs de courant n'ont pas d'importance.<br />
- le courant du point 2 peut être inférieur ou supérieur à celui du point 1 (mais, aussi \"éloignés\" que possible l'un de l'autre).<br />
- il est conseillé de choisir des charges dont la consommation est relativement stable ou, a minima, évolue peu sur une période de quelques secondes.")
}
//===========================================================
// Economiseur : Doit être placé en fin de page pour fonctionner
//===========================================================
MouseArea {
anchors.fill: parent
preventStealing: true // Permet que cela fonctionne même si un élément flickable (notamment) soit derrière et "vole" le click
enabled: flag_economiseur_actif
onClicked: {
mise_en_veille(0);
}
}
//=============================================================================================================
// Initialistion de la page
//=============================================================================================================
function init_page()
{
var i;
var req, res;
var nb;
var id_variable;
var tmp;
if (adresse_module <= 0)
{
console.log("La page 'etalonnage_carte.qml' a été appelée sans 'adresse_module' valide. L'affichage de la page est impossible");
return;
}
// Recherche des informations sur la carte :
req = 'SELECT Eeprom, Type FROM TB_Modules_physiques WHERE Adresse_k_bus=' + adresse_module + ' ';
nb = bdd.requete(req);
if (nb !== 0)
{
res = bdd.lecture_ligne();
i = 0;
eeprom = res[i++]; // Sauvegarde car on va l'écraser sur la carte
switch(Number(res[i++]))
{
//-----------------------------------------
case shipheart_type_module_shipbase:
nom_carte = "ShipBase";
break;
//-----------------------------------------
case shipheart_type_module_shippower812:
nom_carte = "ShipPower812";
break;
//-----------------------------------------
case shipheart_type_module_shipBS:
nom_carte = "ShipBS";
break;
//-----------------------------------------
case shipheart_type_module_shippower4AC:
nom_carte = "ShipPower4AC";
break;
//-----------------------------------------
case shipheart_type_module_shippower8DC:
nom_carte = "ShipPower8DC";
break;
//-----------------------------------------
default:
nom_carte = "Modèle inconnu";
break;
}
}
else
{
console.log("Aucun module n'existe avec l'adresse K-bus indiquée. L'affichage de la page 'etalonnage_carte.qml' est impossible")
return;
}
// Recherche des informations sur la batterie affectée à cette carte :
req = 'SELECT Cle_primaire, Resistance_interne FROM TB_Batteries WHERE Type_entrees=' + shipheart_batteries_type_entrees_ibs + ' AND Adresse_module=' + adresse_module + ' ';
nb = bdd.requete(req);
if (nb !== 0)
{
res = bdd.lecture_ligne();
i = 0;
id_batterie = Number(res[i++]);
resistance = Number(res[i++]);
}
else
{
console.log("Aucune batterie n'est affectée à la carte à étalonner. L'affichage de la page 'etalonnage_carte.qml' est impossible")
return;
}
//-------------------------------------------------------------------
// Remplissage du modèle de circuits :
modele_grille_principale.clear();
// Récupération des informations sur les circuits rattachés à ce ShipBS :
req = 'SELECT Cle_primaire, Nom, Consigne, Intensite, Adresse_module, No_circuit FROM TB_Circuits WHERE Adresse_module IN (SELECT Adresse_k_bus FROM TB_Modules_config WHERE ID_Batterie=' + id_batterie + ') ORDER BY Adresse_module, No_circuit';
nb = bdd.requete(req);
for (i = 0; i < nb; i ++)
{
res = bdd.lecture_ligne();
if (!res.isEmpty)
{
if ((Number(res[4]) !== 0) || (Number(res[5]) !== 2)) // On exclus le port USB arrière car sa consommation est inclue dans ShipBase et sa commande semble peu pertinente...
{
modele_grille_principale.append({ "id": Number(res[0]),
"nom": res[1],
"consigne": Number(res[2]),
"consigne_init": Number(res[2]),
"intensite": Number(res[3]),
"commandable": !((Number(res[4]) === 0) && (Number(res[5]) === 0)) });
}
}
else break; // Ne devrait pas arriver....
}
//--------------------------------------------------------------------
// Demande de mis à 0 du gain et offset de la carte (uniquement les valeurs courantes, pas les valeurs usine) :
req = 'UPDATE TB_Modules_physiques SET Eeprom="00000000", New_eeprom=1 WHERE Adresse_k_bus=' + adresse_module + ' ';
bdd.requete_unique(req);
boite_information_shipbs.open();
flag_init = false;
}
//=============================================================================================================
// Mise à jour de la valeur instantannée
//=============================================================================================================
function mise_a_jour()
{
var req, res;
var nb;
var i;
i = 0;
// Récupération des informations sur les circuits rattachés à ce ShipBS :
for (i=0; i < modele_grille_principale.count; i++) // L'init de 'i' dépend de la présence ou non de ShipBase dans la liste
{
req = 'SELECT Consigne, Intensite FROM TB_Circuits WHERE Cle_primaire=' + modele_grille_principale.get(i).id + ' ';
res = bdd.requete_unique(req);
modele_grille_principale.set(i, {"consigne": Number(res[0]), "intensite": Number(res[1])});
}
// Récupération des courants différentiel et de décharge de la batterie en cours :
req = 'SELECT Courant_decharge, courant_differentiel, Tension FROM TB_Batteries WHERE Cle_primaire=' + id_batterie + ' ';
res = bdd.requete_unique(req);
i = 0;
courant_decharge = Number(res[i++]);
courant_differentiel = Number(res[i++]);
tension = Number(res[i++]);
if (!flag_point_memorise_1)
{
courant_decharge_1 = courant_decharge;
courant_differentiel_1 = courant_differentiel;
tension_1 = tension;
}
if (!flag_point_memorise_2)
{
courant_decharge_2 = courant_decharge;
courant_differentiel_2 = courant_differentiel;
tension_2 = tension;
}
}
//=============================================================================================================
// Commande d'un circuit
//=============================================================================================================
function toggle_circuit(index)
{
var consigne;
if (modele_grille_principale.get(index).consigne === 0) consigne = 100;
else consigne = 0;
modele_grille_principale.set(index, {"consigne": consigne});
// Mise à jour de la nouvelle consigne du circuit:
bdd.requete_unique('UPDATE TB_Circuits SET New_consigne=' + consigne + ' WHERE Cle_primaire="' + modele_grille_principale.get(index).id + '" ');
synchro_bdd(shipheart_type_synchro_data);
}
//=============================================================================================================
// Enregistrement des nouvelles valeur d'étalonnage
//=============================================================================================================
function enregistrement()
{
var req;
var a, b;
var gain, offset;
var chaine_gain, chaine_offset;
a = (courant_decharge_1 - courant_decharge_2) / (courant_differentiel_1 - courant_differentiel_2);
b = courant_decharge_1 - (a * courant_differentiel_1);
gain = Math.round(((a - 1) * 10000));
offset = Math.round((b * 1000));
if ((gain > -9999) && (gain < 9999) && (offset > -32000) && (offset < 32000)) // Si l egain ou l'offset sont hors limite, on n'enregistre pas de valeur d'étalonnage...
{
chaine_gain = bdd.int16_to_chaine_hexa(gain);
chaine_offset = bdd.int16_to_chaine_hexa(offset);
//console.log("Gain = " + gain + " soit, en hexa = [" + chaine_gain + "] et Offset = " + offset + " soit, en hexa = [" + chaine_offset + "]");
var new_eeprom;
new_eeprom = chaine_offset.substring(2,3) + chaine_offset.substring(3,4) + chaine_offset.substring(0,1) + chaine_offset.substring(1,2);
new_eeprom += chaine_gain.substring(2,3) + chaine_gain.substring(3,4) + chaine_gain.substring(0,1) + chaine_gain.substring(1,2);
new_eeprom += eeprom.substring(8);
eeprom = new_eeprom;
}
// Sinon, on conserve la valeur précédente de l'EEPROM qui va être rechargée dans le module.
//console.log("Eeprom = " + eeprom);
}
//=============================================================================================================
// Opération avant sortie
//=============================================================================================================
function sortie()
{
var req;
var i;
var r;
// Estimation de la résistance interne :
if ((tension_1 !== tension_2) && (courant_decharge_1 !== courant_decharge_2))
{
r = ((tension_1 - tension_2) * 1000) / (courant_decharge_1 - courant_decharge_2); // Le 'x 1000' permet d'avoir une valeur en mOhms. Le sens (I1 - I2) est inversé par rapport à la formule car les courants de décharge sont négatifs par convention.
}
//console.log("tension_1 = " + tension_1 + "V, tension_2 = " + tension_2 + "V, courant_decharge_1 = " + courant_decharge_1 + "A, courant_decharge_2 = " + courant_decharge_2 + "A");
if ((r > 0) && (r < 10000)) resistance = r.toFixed(2); // On ne prend pas en compte les valeurs négatives ou > 10 Ohms
else console.log("La valeur de résistance interne calculée est erronée : " + r + " mOhms");
// Mise à jour de la résistance dans TB_Batteries :
req = 'UPDATE TB_Batteries SET Resistance_interne="' + resistance + '" WHERE Cle_primaire=' + id_batterie + ' ';
//console.log("Requête = " + req)
bdd.requete_unique(req);
// Mise à jour de la valeur d'EEPROM :
req = 'UPDATE TB_Modules_physiques SET Eeprom="' + eeprom + '", New_eeprom=1 WHERE Adresse_k_bus=' + adresse_module + ' ';
//console.log("Requête = " + req)
bdd.requete_unique(req);
// Mise à leur valeur initiale des consignes des circuits rattachés à ce ShipBS :
for (i=0; i < modele_grille_principale.count; i++) // L'init de 'i' dépend de la présence ou non de ShipBase dans la liste
{
bdd.requete_unique('UPDATE TB_Circuits SET New_consigne=' + modele_grille_principale.get(i).consigne_init + ' WHERE Cle_primaire="' + modele_grille_principale.get(i).id + '" ');
}
// Demande de reconfiguration système via la BDD car on a modifié des valeurs de configuration :
// synchro_bdd(shipheart_type_synchro_config);
}
}
Executable
+622
View File
@@ -0,0 +1,622 @@
.pragma library
// Types unites :
const UNITE_VOLUME = 1;
const UNITE_TEMPERATURE = 2;
const UNITE_LATITUDE = 3;
const UNITE_LONGITUDE = 4;
// Unités de type Volume :
const UNITE_VOLUME_LITRES = 0;
const UNITE_VOLUME_US_GALLONS = 1;
const UNITE_VOLUME_IMP_GALLONS = 2;
// Unités de type Température :
const UNITE_TEMPERATURE_CELSIUS = 0;
const UNITE_TEMPERATURE_FAHRENHEIT = 1;
// Langues :
const LANGUE_FR = 0;
const LANGUE_EN = 1;
const LANGUE_DE = 2;
const LANGUE_ES = 3;
const LANGUE_PT = 4;
const LANGUE_NL = 5;
const LANGUE_IT = 6;
const LANGUE_LV = 7; // Lettonie
const LANGUE_CA = 8; // Catalan
// Positionnement GNSS :
const POSITION_VALEUR_ERRONNEE = 200000000; // Valeur hors limite de latitude (-90 000 000 à +90 000 000) et longitude (-180 000 000 à +180 000 000)
const SHIPHEART_TYPE_CARTE_INCONNU = -1;
const SHIPHEART_TYPE_CARTE_SHIPBASE = 0;
const SHIPHEART_TYPE_CARTE_SHIPPOWER812 = 1;
const SHIPHEART_TYPE_CARTE_SHIPBS = 2;
const SHIPHEART_TYPE_CARTE_SHIPPOWER4AC = 3;
const SHIPHEART_TYPE_CARTE_SHIPPOWER8DC = 4;
const SHIPHEART_SOURCE_MESURE_CIRCUIT = 1;
const SHIPHEART_SOURCE_MESURE_CAPTEUR = 2;
const SHIPHEART_SOURCE_MESURE_RESERVOIR = 3;
const SHIPHEART_SOURCE_MESURE_BATTERIE = 4;
//=====================================================================================
// Fonctions : Défini les paramètres d'affichage de la courbe en fonction du type de
// période de temps choisie.
// Reçoit : Le type de période
// Retour : Les différents paramètres de la courbe
//=====================================================================================
function get_parametres_courbe(type_periode_graphique)
{
switch(type_periode_graphique)
{
default:
case 1:
return {
duree: (1 * 1 * 60 * 60), // En secondes
regroupement: 1, // En secondes
graduations_x: 7, // Nombre de bandes verticales + 1
format_x: "HH:mm", // exemple : "yyyy-MM-dd HH:mm:ss"
format: 0 // 0=format court ("hh:mm") et 1=format long ("MM/dd hh:mm")
}
case 2:
return {
duree: (1 * 24 * 60 * 60), // En secondes
regroupement: 1, // En secondes
graduations_x: 13, // Nombre de bandes verticales + 1
format_x: "HH:mm", // exemple : "yyyy-MM-dd HH:mm:ss"
format: 0 // 0=format court ("hh:mm") et 1=format long ("MM/dd hh:mm")
}
case 3:
return {
duree: (7 * 24 * 60 * 60), // En secondes
// regroupement: (2 * 60), // En secondes
regroupement: 1, // En secondes
graduations_x: 8, // Nombre de bandes verticales + 1
format_x: "dd/MM HH:mm", // exemple : "yyyy-MM-dd HH:mm:ss"
format: 1 // 0=format court ("hh:mm") et 1=format long ("MM/dd hh:mm")
}
case 4:
return {
duree: (30 * 24 * 60 * 60), // En secondes
// regroupement: (5 * 60), // En secondes
regroupement: 1, // En secondes
graduations_x: 31, // Nombre de bandes verticales + 1
format_x: "dd", // exemple : "yyyy-MM-dd HH:mm:ss"
format: 1 // 0=format court ("hh:mm") et 1=format long ("MM/dd hh:mm")
}
}
}
//=====================================================================================
// Fonctions : Calcule les valeurs mini et maxi optimales en fonction des valeurs
// brutes de mini et de maxi de la série de données.
// Reçoit : La valeur mini et maxi brutes
// Retour : La valeur mini et maxi optimale
//=====================================================================================
function ajustement_mini_maxi(min, max, nb_graduations)
{
var ratio;
if ((max < min) || (nb_graduations <= 0)) return(min); // Si MIN > MAX ou si le nombre de graduations est nul, on ne peut rien faire...
if (min === max)
{
if (min !== 0)
{
min = max * 0.95; // On met le min à -5% en-dessous du maxi
max = max * 1.05; // On met le maxi à 5% au-dessus du mini
}
else return {mini: 0, maxi: 0.5 }; // Si les deux valeurs sont égales à 0, on renvoit arbitrairement 0 et 1 en mini/maxi
}
ratio = Math.log10((max - min) / nb_graduations);
if (ratio < 0) ratio = Math.trunc(ratio) - 1; // Permet que -1.425 devienne -2
else ratio = Math.trunc(ratio) + 1; // Permet que +1.425 devienne +2
return {
mini: Math.floor(min * Math.pow(10, (-ratio)))*Math.pow(10, ratio),
maxi: Math.ceil(max * Math.pow(10, (-ratio)))*Math.pow(10, ratio),
};
}
//=====================================================================================
// Fonctions : Calcule la précision d'affichage optimale en fonction des valeurs brutes
// de mini et de maxi de la série de données.
// Reçoit : La valeur mini et maxi brutes
// Retour : La précision optimale d'affichage
//=====================================================================================
function precision_affichage_mini_maxi(min, max, nb_graduations)
{
var ratio;
if ((max < min) || (nb_graduations <= 0)) return(0); // Dans ce cas, on ne peut rien faire...
if (min === max)
{
if (min !== 0)
{
min = max * 0.95; // On met le min à -5% en-dessous du maxi
max = max * 1.05; // On met le maxi à 5% au-dessus du mini
}
else return 0; // Si les deux valeurs sont égales à 0, on renvoit arbitrairement 0 comme précision
}
ratio = Math.log10((max - min) / (nb_graduations - 1)); // L'objectif est de définir le nombre de décades de l'intervalle entre 2 graduations.
//console.log("Ratio = " + ratio);
if (ratio <= -2) return(3); // Si l'intervalle est <= 0.001
else if (ratio <= -1) return(2); // Si l'intervalle est <= 0.01
else if (ratio <= 0) return(1); // Si l'intervalle est <= 0.1
else return(0); // Si l'intervalle est <= 1
}
//=====================================================================================
// Fonctions : Génère une chaine de deux digits avec un caractère devant si la valeur
// est inférieure à 9
// Reçoit : La valeur et le caractère à ajouter si besoin (en général, '0' ou ' ')
// Retour : La chaine sur 2 digits
//=====================================================================================
function format_deux_digits(val, lead)
{
if (val <= 9) return(lead + val);
else return(val);
}
//=====================================================================================
// Fonctions : Convertit une grandeur reçue dans son unité par défaut dans l'unité
// passée en paramètre. La valeur obtenue est formattée avec la précision
// demandée et le symbole de la nouvelle unité est ajoutée à la fin (si
// elle n'est pas vide).
// Reçoit : La valeur à convertir, l'unité cible, le symbole de cette unité (ou
// une chaine vide si aucune) et la précision (sous forme d'entier >= 0)
// Retour : La valeur dans la nouvelle unité avec l'unité à la fin si elle est demandée
//=====================================================================================
function conversion_temperature(val, unite, symbole, precision)
{
var retour;
var tmp;
switch(unite)
{
//--------------------------------------------------------------------------------
// Températures :
case UNITE_TEMPERATURE_CELSIUS:
retour = val.toFixed(precision); // Unité de base des températures
break;
case UNITE_TEMPERATURE_FAHRENHEIT:
retour = ((val * 1.8)+32).toFixed(precision); // T(°F) = (9/5).T(°C) + 32
break;
//--------------------------------------------------------------------------------
// Autres :
default:
retour = val.toFixed(precision); // Pour les capteurs dont l'unité de température ne serait pas connue
break;
}
if (symbole !== "") retour = retour + " " + symbole;
return(retour);
}
//=====================================================================================
// Fonctions : Convertit une grandeur reçue dans son unité par défaut dans l'unité
// passée en paramètre. La valeur obtenue est formattée avec la précision
// demandée et le symbole de la nouvelle unité est ajoutée à la fin (si
// elle n'est pas vide).
// Reçoit : La valeur à convertir, l'unité cible, le symbole de cette unité (ou
// une chaine vide si aucune) et la précision (sous forme d'entier >= 0)
// Retour : La valeur dans la nouvelle unité avec l'unité à la fin si elle est demandée
//=====================================================================================
function conversion_volume(val, unite, symbole, precision)
{
var retour;
var tmp;
switch(unite)
{
//--------------------------------------------------------------------------------
// Volumes :
case UNITE_VOLUME_LITRES:
retour = val.toFixed(precision); // Unité de base des volumes
break;
case UNITE_VOLUME_US_GALLONS:
retour = (val / 3.785411784).toFixed(precision); // V(US Ga) = V(l) / 3.785411784
break;
case UNITE_VOLUME_IMP_GALLONS:
retour = (val / 4.546090).toFixed(precision); // V(Imp Ga) = V(l) / 4.546090
break;
//--------------------------------------------------------------------------------
// Autres :
default:
retour = val.toFixed(precision); // Pour les capteurs dont l'unité de volume ne serait pas connue
break;
}
if (symbole !== "") retour = retour + " " + symbole;
return(retour);
}
//=====================================================================================
// Fonctions : Transfère vers la fonction correspondant à 'type_unite'
//=====================================================================================
function conversion_unites(val, unite, symbole, precision, type_unite)
{
var retour;
switch(type_unite)
{
case UNITE_VOLUME:
retour = conversion_volume(val, unite, symbole, precision);
break;
case UNITE_TEMPERATURE:
retour = conversion_temperature(val, unite, symbole, precision);
break;
case UNITE_LATITUDE:
retour = chaine_latitude(val);
break;
case UNITE_LONGITUDE:
retour = chaine_longitude(val);
break;
default:
retour = val.toFixed(precision); // Pour les capteurs dont l'unité n'est ni un volume, ni une température
}
return(retour);
}
//=====================================================================================
// Fonction : Génère une chaine de latitude et/ou de longitude sous la forme 'xx.xxx°N'
// ou 'xx.xxx°N - xx.xxx°E'
// Reçoit : La valeur de latitude et/ou de longitude (-1 si pas utile)
// Retour : La chaine générée
//=====================================================================================
function chaine_position(lat, lon)
{
var chaine_lat, chaine_lon;
var retour;
// Calcul de la chaine de Latitude :
if (lat !== -1) chaine_lat = chaine_latitude(lat);
else chaine_lat = ""; // Si la valeur n'est pas demandée, cette partie de chaine sera vide
// Calcul de la chaine de Longitude :
if (lon !== -1) chaine_lon = chaine_longitude(lon);
else chaine_lon = ""; // Si la valeur n'est pas demandée, cette partie de chaine sera vide
if ((chaine_lat !== "") && (chaine_lon !== "")) retour = chaine_lat + " , " + chaine_lon;
else retour = chaine_lat + chaine_lon; // Au moins une de vide, donc soit LAT, soit LON, soit ""
return(retour);
}
//=====================================================================================
// Fonction : Génère une chaine de latitude sous la forme 'xx.xxx°N' ou 'xx.xxx°S'
// Reçoit : La valeur de latitude
// Retour : La chaine générée ou '---' si erreur
//=====================================================================================
function chaine_latitude(lat)
{
var chaine_lat
var retour;
var suffixe;
if (lat !== POSITION_VALEUR_ERRONNEE)
{
if (lat >= 0) suffixe = "N"; // Les valeurs positives sont considérées comme au Nord
else suffixe = "S" // ... et les négatives au Sud
chaine_lat = Math.trunc(lat) + "°" + ((lat - Math.trunc(lat)) * 60).toFixed(3) + "' " + suffixe; // On fixe la précision à 3 digits après le point pour les secondes
}
else chaine_lat = "---°"; // Si la valeur reçue est hors limite, on affiche "---°"
return(chaine_lat);
}
//=====================================================================================
// Fonction : Génère une chaine de longitude sous la forme 'xx.xxx°E' ou 'xx.xxx°W'
// Reçoit : La valeur de longitude
// Retour : La chaine générée ou '---' si erreur
//=====================================================================================
function chaine_longitude(lon)
{
var chaine_lon;
var retour;
var suffixe;
if (lon !== POSITION_VALEUR_ERRONNEE)
{
if (lon >= 0) suffixe = "E"; // Les valeurs positives sont considérées comme à l'Est
else suffixe = "W" // ... et les négatives à l'Ouest
chaine_lon = Math.trunc(lon) + "°" + ((lon - Math.trunc(lon)) * 60).toFixed(3) + "' " + suffixe; // On fixe la précision à 3 digits après le point pour les secondes
}
else chaine_lon = "---°"; // Si la valeur reçue est hors limite, on affiche "---°"
return(chaine_lon);
}
//=====================================================================================
// Fonction : Vérifie si la chaine reçue en paramètre correspond bien à une adresse IP V4
// Reçoit : La chaine à vérifier
// Retour : TRUE si l'adresse est correcte, FALSE sinon
//=====================================================================================
function test_format_adresse_ip_v4(ip) {
var Reg = new RegExp(/^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/);
return Reg.test(ip);
}
//=====================================================================================
// Fonction : Renvoit le nom du type de carte à partir du code de type de carte
// Reçoit : Le code du type
// Retour : La chaine de caractère correspondant au type de carte reçu/
//=====================================================================================
function nom_type_module(code)
{
var nom;
switch(code)
{
//-----------------------------------------
case SHIPHEART_TYPE_CARTE_INCONNU:
nom = qsTr("Type non défini");
break;
//-----------------------------------------
case SHIPHEART_TYPE_CARTE_SHIPBASE:
nom = "ShipBase";
break;
//-----------------------------------------
case SHIPHEART_TYPE_CARTE_SHIPPOWER812:
nom = "ShipPower812";
break;
//-----------------------------------------
case SHIPHEART_TYPE_CARTE_SHIPBS:
nom = "ShipBS";
break;
//-----------------------------------------
case SHIPHEART_TYPE_CARTE_SHIPPOWER4AC:
nom = "ShipPower4AC";
break;
//-----------------------------------------
case SHIPHEART_TYPE_CARTE_SHIPPOWER8DC:
nom = "ShipPower8DC";
break;
//-----------------------------------------
default:
nom = qsTr("Modèle inconnu");
break;
}
return(nom);
}
//=====================================================================================
// Fonction : Renvoit le nom du type d'équipement à partir du code de type
// Reçoit : Le code du type
// Retour : La chaine de caractère correspondant au type d'équipement reçu
//=====================================================================================
function nom_type_equipement(code)
{
var nom;
switch(code)
{
//-----------------------------------------
case SHIPHEART_SOURCE_MESURE_CIRCUIT:
nom = qsTr("Circuit");
break;
//-----------------------------------------
case SHIPHEART_SOURCE_MESURE_CAPTEUR:
nom = qsTr("Capteur");
break;
//-----------------------------------------
case SHIPHEART_SOURCE_MESURE_RESERVOIR:
nom = qsTr("Réservoir");
break;
//-----------------------------------------
case SHIPHEART_SOURCE_MESURE_BATTERIE:
nom = qsTr("Batterie");
break;
//-----------------------------------------
default:
nom = qsTr("Type inconnu");
break;
}
return(nom);
}
//=====================================================================================
// Fonction : Renvoit le nom d'une variabme à partir de son code
// Reçoit : Le code de la variable
// Retour : La chaine de caractère correspondant au code variable reçu
//=====================================================================================
function nom_variable(code)
{
var nom;
switch(code)
{
//---------------------
case "Circ_consigne":
nom = qsTr("Consigne");
break;
//---------------------
case "Circ_tension":
nom = qsTr("Tension");
break;
//---------------------
case "Circ_courant":
nom = qsTr("Courant");
break;
//---------------------
case "Circ_frequence":
nom = qsTr("Fréquence");
break;
//---------------------
case "Circ_puissance":
nom = qsTr("Puissance");
break;
//---------------------
case "Circ_energie":
nom = qsTr("Energie");
break;
//---------------------
case "Circ_tension_secteur":
nom = qsTr("Tension");
break;
//---------------------
case "Batt_temperature":
nom = qsTr("Température");
break;
//---------------------
case "Batt_tension":
nom = qsTr("Tension");
break;
//---------------------
case "Batt_courant_differentiel":
nom = qsTr("Courant différentiel");
break;
//---------------------
case "Batt_courant_charge":
nom = qsTr("Courant de charge");
break;
//---------------------
case "Batt_courant_decharge":
nom = qsTr("Courant de décharge");
break;
//---------------------
case "Batt_soh":
nom = qsTr("SOH (state Of Health)");
break;
//---------------------
case "Batt_soc":
nom = qsTr("SOC (State Of Charge)");
break;
//---------------------
case "Batt_autonomie":
nom = qsTr("Autonomie");
break;
//---------------------
case "Batt_puissance":
nom = qsTr("Puissance");
break;
//---------------------
case "Batt_energie":
nom = qsTr("Energie");
break;
}
return(nom);
}
//=====================================================================================
// Fonctions : Génère une chaine de caractère correspondant à la taille du fichier
// dont la valeur, en octet, est reçue en paramètres
// Reçoit : La taille du fichier, en octets
// Retour : La chaine
//=====================================================================================
function taille_fichier(taille, langue)
{
var unite_octet;
switch(langue)
{
//--------------------------------------------------------
case LANGUE_EN:
case LANGUE_ES:
case LANGUE_PT:
case LANGUE_NL:
case LANGUE_IT:
case LANGUE_LV: // Lettonie
case LANGUE_CA:
default:
unite_octet = "b";
break;
//---------------------------------------------------------
case LANGUE_FR:
unite_octet = "o";
break;
//---------------------------------------------------------
case LANGUE_DE:
unite_octet = "B";
break;
}
// Inférieur à 1ko :
if (taille < 1000) return (taille + unite_octet);
// Entre 1ko et moins de 1Mo :
taille /= 1000;
if (taille < 10) return (taille.toFixed(1) + "k" + unite_octet);
if (taille < 1000) return (taille.toFixed(0) + "k" + unite_octet);
// Entre 1Mo et moins de 1Go :
taille /= 1000;
if (taille < 10) return (taille.toFixed(1) + "M" + unite_octet);
if (taille < 1000) return (taille.toFixed(0) + "M" + unite_octet);
// Entre 1Go et moins de 1To :
taille /= 1000;
if (taille < 10) return (taille.toFixed(1) + "G" + unite_octet);
if (taille < 1000) return (taille.toFixed(0) + "G" + unite_octet);
taille /= 1000;
return (taille.toFixed(1) + "T" + unite_octet);
}
+169
View File
@@ -0,0 +1,169 @@
.pragma library
//=====================================================================================
// Fonctions : Fournit une chaine de caractères constituant une liste de valeurs§niveaux
// de batterie. La liste est composée de couples de nombre séparés par des ';'
// et les deux valeurs au sein d'un couple sont séparés par '/'.
// Par 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"
// La chaine est établie en fonction du paramètre de 'Tension_nominale' recue en paramètre.
// Reçoit : La tension nomniale (a priori, 12, 24 ou 48)
// Retour : La chaine
//=====================================================================================
function chaine_tension_niveau_batterie_usine(tension)
{
var chaine;
switch(tension)
{
default:
case 12:
chaine = "13.800/100.00";
chaine += ";13.200/98.00";
chaine += ";13.000/96.00";
chaine += ";12.800/94.00";
chaine += ";12.600/90.00";
chaine += ";12.480/80.00";
chaine += ";12.100/30.00";
chaine += ";12.020/20.00";
chaine += ";11.800/10.00";
chaine += ";11.500/5.00";
chaine += ";11.000/2.00";
chaine += ";10.500/0.00";
break;
case 24:
chaine = "27.600/100.00";
chaine += ";26.400/98.00";
chaine += ";26.000/96.00";
chaine += ";25.600/94.00";
chaine += ";25.200/90.00";
chaine += ";24.960/80.00";
chaine += ";24.200/30.00";
chaine += ";24.040/20.00";
chaine += ";23.600/10.00";
chaine += ";23.000/5.00";
chaine += ";22.000/2.00";
chaine += ";21.000/0.00";
break;
case 48:
// Version issue des tables Suncy :
chaine = "56.000/100.00";
chaine += ";50.000/84.00";
chaine += ";49.300/79.00";
chaine += ";49.000/74.00";
chaine += ";48.800/69.00";
chaine += ";48.000/30.00";
chaine += ";47.600/20.00";
chaine += ";47.200/15.00";
chaine += ";46.400/10.00";
chaine += ";45.000/5.00";
chaine += ";43.500/2.00";
chaine += ";42.000/0.00";
break;
}
return(chaine);
}
//=====================================================================================
// Fonctions : Fournit une chaine de caractères constituant une liste de temérature/résistance_interne
// de batterie. La liste est composée de couples de nombre séparés par des ';'
// et les deux valeurs au sein d'un couple sont séparés par '/'.
// La chaine est établie en fonction du paramètre de 'Type' (de batterie) recue en paramètre.
// Reçoit : Le type de batterie
// Retour : La chaine
//=====================================================================================
function chaine_temperature_resistance_interne_usine(type)
{
var chaine;
switch(type)
{
default:
case 0: // Non défini
case 1: // Pb-Acide liquide
case 2: // Pb-AGM/VRLA
case 3: // Pb-Gel
case 4: // Pb-Carbone
chaine = "30/0.9";
chaine += ";25/1.0";
chaine += ";20/1.0";
chaine += ";15/1.1";
chaine += ";10/1.2";
chaine += ";5/1.3";
chaine += ";0/1.6";
chaine += ";-5/1.8";
chaine += ";-10/2.3";
chaine += ";-15/2.9";
chaine += ";-20/3.7";
chaine += ";-25/4.7";
chaine += ";-30/6.0"; // 30/0.9;25/1.0;20/1.0;15/1.1;10/1.2;5/1.3;0/1.6;-5/1.8;-10/2.3;-15/2.9;-20/3.7;-25/4.7;-30/6.0
break;
case 10: // Lithium
chaine = "25/0.8";
chaine += ";20/1.0";
chaine += ";15/1.0";
chaine += ";10/1.3";
chaine += ";5/1.8";
chaine += ";0/3.0";
chaine += ";-5/4.5";
chaine += ";-10/6.3";
chaine += ";-15/8.3";
chaine += ";-20/11.0";
chaine += ";-25/16.0";
chaine += ";-30/23.0"; // 25/0.8;20/1.0;15/1.0;10/1.3;5/1.8;0/3.0;-5/4.5;-10/6.3;-15/8.3;-20/11.0;-25/16.0;-30/23.0
break;
}
return(chaine);
}
//=====================================================================================
// Fonctions : Calcule le coefficient de Peukert à partir de 2 couples de Capacité/Durée
// de batterie. Si ces valeurs ne sont pas exploitables, la fonction renvoit la
// valeur par défaut dépendant du type de batterie.
// La formule pour calculer le coefficient est :
// n = log (T0/T1) / [log(C1/C0) - log (T1/T0)]
// Reçoit : Le type de batterie, la capacité 1 et la durée 1, la capacité 2 et la durée 2
// Retour : Le coefficient de Peukert
//=====================================================================================
function coefficient_de_peukert(type, capacite_1, heures_capacite_1, capacite_2, heures_capacite_2)
{
var coeff_peukert = 0;
if ((capacite_1 > 0) && (heures_capacite_1 > 0) && (capacite_2 > 0) && (heures_capacite_2 > 0))
{
var denom = (Math.log(capacite_2/capacite_1) - Math.log(heures_capacite_2/heures_capacite_1));
if (denom !== 0) coeff_peukert = Math.log(heures_capacite_1/heures_capacite_2) / denom;
}
if (coeff_peukert < 1) // On traite le cas où on n'a fait aucun calcul, où 'denom' est nul et si le résultat lui-même n'est pas conforme.
{
switch(type)
{
default:
case 0: // Non défini
coeff_peukert = 1.25
break;
case 1: // Plomb acide liquide
coeff_peukert = 1.30
break;
case 2: // Plomb AGM
coeff_peukert = 1.10
break;
case 3: // Plomb Gel
coeff_peukert = 1.10
break;
case 4: // Plomb Carbone
coeff_peukert = 1.15
break;
case 10: // Lithium
coeff_peukert = 1.05
break;
}
}
return(coeff_peukert);
}
+20
View File
@@ -0,0 +1,20 @@
.pragma library
//=====================================================================================
// Fonctions : Vérifie si le circuit dispose de commandes AUTO
// Reçoit : La liste à traiter
// Retour : 0 si aucune commande auto n'existe, 1 si un ou plusieurs commandes auto existent
//=====================================================================================
function check_commandes_auto(liste)
{
if (liste === "") return (0);
else return (1);
}
+80
View File
@@ -0,0 +1,80 @@
.pragma library
const MAX_COULEURS_TEST_ECRAN = 8;
//=====================================================================================
// Fonctions : Fournit une couleur en fonction d'un numéro reçu. Si le numéro est
// supérieur au maxi, il y a bouclage pour toujours retourner une couleur.
// Reçoit : Le numéro
// Retour : La couleur
//=====================================================================================
function couleur(no)
{
var couleur;
var max_couleurs = 12; // !! Vérifier que ce nombre est cohérent avec le nombre de cases ci-dessous... (ceci dit, il peut être inférieur sans soucis)
no %= max_couleurs;
switch (no) // Cf https://doc.qt.io/qt-5/qml-color.html#svg-color-reference
{
case 0: couleur = "aquamarine"; break;
case 1: couleur = "darkcyan"; break;
case 2: couleur = "coral"; break;
case 3: couleur = "lightskyblue"; break;
case 4: couleur = "cadetblue"; break;
case 5: couleur = "darkturquoise"; break;
case 6: couleur = "mediumpurple"; break;
case 7: couleur = "darkseagreen"; break;
case 8: couleur = "cornflowerblue"; break;
case 9: couleur = "darkcyan"; break;
case 10: couleur = "mediumaquamarine"; break;
case 11: couleur = "mediumturquoise"; break;
default: couleur = "orangered"; // Ne doit jamais arriver
}
return(couleur);
}
//=================================================================================================
// Fonction : Recherche la couleur suivante dans la liste (et reboucle dans le cas de fin de liste)
//=================================================================================================
function next_color_test_ecran(no)
{
if (++ no >= MAX_COULEURS_TEST_ECRAN) no = 0;
return(no);
}
//=====================================================================================
// Fonctions : Fournit une couleur en fonction d'un numéro reçu. Si le numéro est
// supérieur au maxi, il y a bouclage pour toujours retourner une couleur.
// Reçoit : Le numéro
// Retour : La couleur
//=====================================================================================
function couleur_test_ecran(no)
{
var couleur;
no %= MAX_COULEURS_TEST_ECRAN;
switch (no) // Cf https://doc.qt.io/qt-5/qml-color.html#svg-color-reference
{
case 0: couleur = "white"; break;
case 1: couleur = "red"; break;
case 2: couleur = "blue"; break;
case 3: couleur = "green"; break;
case 4: couleur = "black"; break;
case 5: couleur = "aqua"; break;
case 6: couleur = "fuchsia"; break;
case 7: couleur = "yellow"; break;
default: couleur = "orangered"; // Ne doit jamais arriver
}
return(couleur);
}
+541
View File
@@ -0,0 +1,541 @@
.pragma library
.import "fonctions.js" as Fonctions // Permet d'utiliser les fonctions classiques ci-dessous... (cf https://doc.qt.io/qt-5/qtqml-javascript-imports.html )
const LANGUE_FR = 0;
const LANGUE_EN = 1;
const LANGUE_DE = 2;
const LANGUE_ES = 3;
const LANGUE_PT = 4;
const LANGUE_NL = 5;
const LANGUE_IT = 6;
const LANGUE_LV = 7; // Lettonie
const LANGUE_CA = 8; // Catalan
//=====================================================================================
// Fonctions : Renvoit le durée, en secondes, de l'une des périodes d'analyse prédéfinies.
// Reçoit : La période d'analyse (0: 1 heure, 1: 24 heures, 2: 7 jours et 3: 30 jours)
// Retour : La durée, en secondes
//=====================================================================================
function duree_periode_analyse(periode_analyse)
{
var duree;
switch(periode_analyse)
{
default:
case 0 : // 1 heure
duree = (1 * 1 * 60 * 60);
break;
case 1 : // 24 heures
duree = (1 * 24 * 60 * 60);
break;
case 2 : // 7 jours
duree = (7 * 24 * 60 * 60);
break;
case 3 : // 30 jours
duree = (30 * 24 * 60 * 60);
break;
}
return(duree)
}
//=====================================================================================
// Fonctions : Renvoit le nom de l'une des périodes d'analyse prédéfinies.
// Reçoit : La période d'analyse (0: 1 heure, 1: 24 heures, 2: 7 jours et 3: 30 jours)
// Retour : Le texte du nom
//=====================================================================================
function nom_periode_analyse(periode_analyse)
{
var nom;
switch(periode_analyse)
{
default:
case 0 : // 1 heure
nom = qsTr("1 heure");
break;
case 1 : // 24 heures
nom = qsTr("24 heures");
break;
case 2 : // 7 jours
nom = qsTr("7 jours");
break;
case 3 : // 30 jours
nom = qsTr("30 jours");
break;
}
return(nom);
}
//=====================================================================================
// Fonctions : Renvoit da date de début et de fin de l'intervalle en cours d'analyse.
// Reçoit : La période d'analyse (0: 1 heure, 1: 24 heures, 2: 7 jours et 3: 30 jours)
// La date de début et la date de fin en UTC au format JS
// Le fuseau horaire et l'heure d'été en cours
// Retour : La date de début et la date de fin en UTC au format JS mais ajustées à
// la période en cours d'analyse.
//=====================================================================================
function ajustement_periode_analyse(periode_analyse, debut, fin, fuseau, heure_ete)
{
var date_debut, date_fin;
// Passage en heure LOCALE pour effectuer les traitements :
date_debut = date_courante(debut, fuseau, heure_ete); // En LOCALE
date_fin = date_courante(fin, fuseau, heure_ete); // EN LOCALE
// Ajustements à l'intervalle fini d'analyse :
switch(periode_analyse)
{
default:
case 0: // 1 heure
date_debut.setMinutes(Math.trunc(date_debut.getMinutes() / 5) * 5);
date_debut.setSeconds(0);
date_fin.setMinutes(Math.trunc(date_fin.getMinutes() / 5) * 5);
date_fin.setSeconds(0);
break;
case 1: // 24 heures
date_debut.setMinutes(0);
date_debut.setSeconds(0);
date_fin.setMinutes(0);
date_fin.setSeconds(0);
break;
case 2: // 7 jours
date_debut.setHours(Math.trunc(date_debut.getHours() / 6) * 6);
date_debut.setMinutes(0);
date_debut.setSeconds(0);
date_fin.setHours(Math.trunc(date_fin.getHours() / 6) * 6);
date_fin.setMinutes(0);
date_fin.setSeconds(0);
break;
case 3: // 30 jours
date_debut.setHours(0);
date_debut.setMinutes(0);
date_debut.setSeconds(0);
date_fin.setHours(0);
date_fin.setMinutes(0);
date_fin.setSeconds(0);
break;
}
return {
debut: date_utc(date_debut, fuseau, heure_ete), // Retour en UTC
fin: date_utc(date_fin, fuseau, heure_ete) // Retour en UTC
}
}
//=====================================================================================
// Fonctions : Génère une chaine de la forme "hh:mm" en heure locale à partir des
// heures et minutes UTC reçues en paramètre
// Reçoit : Valeur de heures et minutes, fuseau horaire
// Retour : La chaine sur 5 caractères "hh:mm'
//=====================================================================================
function conversion_heure_minutes_locales(hr, mn, fuseau, heure_ete)
{
var retour;
var today = new Date;
var date = new Date(today.getFullYear(), today.getMonth(), today.getDate(), hr, mn, 0);
date = date_add_secondes(date, ((fuseau + heure_ete) * 60 * 60)); // Correction du fuseau horaire pour l'affichage
retour = Fonctions.format_deux_digits(date.getHours(), '0') + ":" + Fonctions.format_deux_digits(date.getMinutes(), '0');
//console.log("Valeur reçue Hr=" + hr + " et min=" + mn + " et chaine de retour =" + retour);
return(retour)
}
//=====================================================================================
// Fonctions : Génère la date du moment ou convertit une date existante en tenant
// compte du fuseau horaire reçu
// Reçoit : La date à convertir au format JS (ou NULL si on veut la date courante), le fuseau, et le décalage heure Eté/Hiver
// Retour : La date dans le fuseau et avec décalage Eté/Hiver, au format JS
//=====================================================================================
function date_courante(dt, fuseau, heure_ete)
{
var date;
if (dt === null)
{
date = new Date;
date = date_add_secondes(date, ((fuseau + heure_ete) * 60 * 60)); // Correction du fuseau horaire pour l'affichage
return(date);
}
else
{
date = date_add_secondes(dt, ((fuseau + heure_ete) * 60 * 60)); // Correction du fuseau horaire pour l'affichage
return(date);
}
}
//=====================================================================================
// Fonctions : Convertit une date existante en UTC
// Reçoit : La date à convertir au format JS, le fuseau, et le décalage heure Eté/Hiver
// Retour : La date UTC, au format JS
//=====================================================================================
function date_utc(dt, fuseau, heure_ete)
{
var date;
if (dt === null) return (null);
date = date_add_secondes(dt, -((fuseau + heure_ete) * 60 * 60)); // Correction du fuseau horaire et de l'heure Ete/Hiver
return(date);
}
//=====================================================================================
// Fonctions : Convertit une valeur d'heure en une chaine dépendant de la langue
// Reçoit : La valeur d'heure, entre 00 et 23, ou 24 (fin de nuit), 25 (aube nautique),
// 26 (aube), 27 (lever du soleil), 28 (zénith), 29 (coucher du soleil),
// 30 (crépuscule), 31 (crépuscule nautique) ou 32 (début de nuit).
// Et la valeur de minutes, entre 00 et 59, ou 60 (aucune minute à retourner)
// Retour : La chaine
//=====================================================================================
function nom_heure(heure, minute)
{
var chaine_h;
var chaine_m;
var texte_heures_speciales = [qsTr("Fin de nuit"), qsTr("Aube nautique"), qsTr("Aube"), qsTr("Lever du soleil"), qsTr("Zénith"), qsTr("Coucher du soleil"), qsTr("Crépuscule"), qsTr("Crépuscule nautique"), qsTr("Début de nuit")];
if ((heure < 0) || (heure > 32)) return("---");
if (heure < 24)
{
chaine_h = qsTr("%1h").arg(((heure<10) ? "0" + heure : "" + heure));
chaine_m = "" + ((minute<10) ? "0" + minute : "" + minute); // On ne renvoit les minutes que si l'on n'est pas sur une heure spéciale.
}
else
{
chaine_h = texte_heures_speciales[heure - 24];
chaine_m = "";
}
return (chaine_h + chaine_m);
}
//=====================================================================================
// Fonctions : Convertit une date au format JS en une chaine au format 'hh:mm:ss'
// Reçoit : La date à convertir
// Retour : La chaine
//=====================================================================================
function chaine_heure(date)
{
var retour;
var heures = date.getHours();
var minutes = date.getMinutes();
var secondes = date.getSeconds();
retour = (heures <= 9) ? "0" + heures : heures;
retour += ":";
retour += (minutes <= 9) ? "0" + minutes : minutes;
retour += ":";
retour += (secondes <= 9) ? "0" + secondes : secondes;
return (retour);
}
//=====================================================================================
// Fonctions : Convertit une date au format JS en une chaine au format 'hh:mm'
// Reçoit : La date à convertir
// Retour : La chaine
//=====================================================================================
function chaine_heure_courte(date)
{
var retour;
var heures = date.getHours();
var minutes = date.getMinutes();
retour = (heures <= 9) ? "0" + heures : heures;
retour += ":";
retour += (minutes <= 9) ? "0" + minutes : minutes;
return (retour);
}
//=====================================================================================
// Fonctions : Convertit une date au format JS en une chaine au format 'dd/MM hh:mm'
// Reçoit : La date à convertir
// Retour : La chaine
//=====================================================================================
function chaine_date_heure_courte(date)
{
var retour;
var mois = date.getMonth() + 1; // Les mois commencent à 0
var jour = date.getDate();
var heures = date.getHours();
var minutes = date.getMinutes();
retour = (jour <= 9) ? "0" + jour : jour;
retour += "/";
retour += (mois <= 9) ? "0" + mois : mois ;
retour += " ";
retour += (heures <= 9) ? "0" + heures : heures;
retour += ":";
retour += (minutes <= 9) ? "0" + minutes : minutes;
return (retour);
}
//=====================================================================================
// Fonctions : Convertit une date au format JS en une chaine au format 'JJ/MM/AAAA'
// Reçoit : La date à convertir
// Retour : La chaine
//=====================================================================================
function chaine_date(date)
{
var retour;
var annee = date.getFullYear();
var mois = date.getMonth() + 1; // Les mois commencent à 0
var jour = date.getDate();
retour = (jour <= 9) ? "0" + jour : jour;
retour += "/";
retour += (mois <= 9) ? "0" + mois : mois ;
retour += "/";
retour += annee;
return (retour);
}
//=====================================================================================
// Fonctions : Convertit un n° de jour (1=lundi, 7=dimanche, 8=tous les jours) en une
// chaine de jour de la semaine dépendant de la langue
// Reçoit : Le n° du jour
// Retour : La chaine
//=====================================================================================
function nom_jour_semaine(no)
{
var textes_jour_semaine = [qsTr("Lundi"), qsTr("Mardi"), qsTr("Mercredi"), qsTr("Jeudi"), qsTr("Vendredi"), qsTr("Samedi"), qsTr("Dimanche"), qsTr("Tous les jours")];
if ((no < 1) || (no > 8)) return("---");
return (textes_jour_semaine[(no - 1)]);
}
//=====================================================================================
// Fonctions : Convertit une date au format JS en une chaine de jour de la seamine dépendant de la langue
// Reçoit : La date
// Retour : La chaine
//=====================================================================================
function chaine_jour_semaine(date)
{
var jour_semaine = date.getDay();
if (jour_semaine === 0) jour_semaine = 7;
return (nom_jour_semaine(jour_semaine));
}
//=====================================================================================
// Fonctions : Convertit une date au format JS en une chaine au format 'JJ/MM/AAAA'
// ou sa version correspondante au pays
// Reçoit : La date à convertir, le code langue (de 1 à N - Cf 'fonctions_localisation.js')
// Retour : La chaine
//=====================================================================================
function chaine_date_longue(date, langue)
{
var retour = "";
var texte_jour = "";
var annee = date.getFullYear();
var mois = date.getMonth() + 1; // Les mois commencent à 0
var jour = date.getDate();
var jour_semaine = date.getDay();
var textes_mois = [qsTr("janvier"), qsTr("février"), qsTr("mars"), qsTr("avril"), qsTr("mai"), qsTr("juin"), qsTr("juillet"), qsTr("août"), qsTr("septembre"), qsTr("octobre"), qsTr("novembre"), qsTr("décembre")];
switch(langue)
{
//--------------------------------------------------------
default:
// Mise en forme n° du jour :
if (jour === 1) texte_jour = "1<sup>er</sup>";
else if ((jour > 1) && (jour <= 9)) texte_jour = "0" + jour;
else texte_jour = jour;
retour += texte_jour;
retour += ' ';
retour += textes_mois[mois - 1];
retour += ' ';
retour += annee;
break;
//---------------------------------------------------------
case LANGUE_EN:
// Mise en forme n° du jour :
if ((jour === 1) || (jour === 21) || (jour === 31)) texte_jour = jour + "<sup>st</sup>";
else if ((jour === 1) || (jour === 22)) texte_jour = jour + "<sup>nd</sup>";
else if ((jour === 3) || (jour === 23)) texte_jour = jour + "<sup>rd</sup>";
else texte_jour = jour + "th";
retour += texte_jour;
retour += ' ';
retour += textes_mois[mois - 1];
retour += ' ';
retour += annee;
break;
//---------------------------------------------------------
case LANGUE_DE:
// Mise en forme n° du jour :
texte_jour = jour + ".";
retour += texte_jour;
retour += ' ';
retour += textes_mois[mois - 1];
retour += ' ';
retour += annee;
break;
}
return (retour);
}
//=====================================================================================
// Fonctions : Convertit une date au format JS en une chaine au format 'YYY-MM-dd hh:mm:ss'
// Reçoit : La date à convertir
// Retour : La chaine
//=====================================================================================
function chaine_date_heure_bdd(date)
{
var retour;
var annee = date.getFullYear();
var mois = date.getMonth() + 1; // Les mois commencent à 0
var jour = date.getDate();
var heures = date.getHours();
var minutes = date.getMinutes();
var secondes = date.getSeconds();
retour = annee;
retour += "-";
retour += (mois <= 9) ? "0" + mois : mois;
retour += "-";
retour += (jour <= 9) ? "0" + jour : jour;
retour += " ";
retour += (heures <= 9) ? "0" + heures : heures;
retour += ":";
retour += (minutes <= 9) ? "0" + minutes : minutes;
retour += ":";
retour += (secondes <= 9) ? "0" + secondes : secondes;
return (retour);
}
//=====================================================================================
// Fonctions : Convertit la date au format JS reçue en une chaine au format 'YYMMdd_hhmmss'
// Reçoit : La date au format JS à convertir
// Retour : La chaine
//=====================================================================================
function chaine_date_heure_fichier(date)
{
var retour;
var annee = date.getYear(); // Sur 2 digits
var mois = date.getMonth() + 1; // Les mois commencent à 0
var jour = date.getDate();
var heures = date.getHours();
var minutes = date.getMinutes();
var secondes = date.getSeconds();
retour = annee;
retour += (mois <= 9) ? "0" + mois : mois;
retour += (jour <= 9) ? "0" + jour : jour;
retour += "_";
retour += (heures <= 9) ? "0" + heures : heures;
retour += (minutes <= 9) ? "0" + minutes : minutes;
retour += (secondes <= 9) ? "0" + secondes : secondes;
return (retour);
}
//=====================================================================================
// Fonctions : Convertit une chaine provenant de la BDD ('YYY-MM-ddThh:mm:ss.000') en
// une date au format JS
// Reçoit : La chaine à convertir
// Retour : La date
//=====================================================================================
function chaine_bdd_date_heure(chaine_date)
{
var t, result = null;
if (typeof chaine_date === 'string')
{
t = chaine_date.split(/[-:.T ]/); // On coupe la chaine initiale en recherchant les '-', ':', '.', ' ' et 'T'
// console.log(t[0] + "/" + t[1] + "/" + t[2] + "//" + t[3] + "/" + t[4] + "/" + t[5]);
result = new Date(t[0], t[1] - 1, t[2], t[3] || 0, t[4] || 0, t[5] || 0); //when t[3], t[4] and t[5] are missing they defaults to zero
}
else console.log("Le type reçu dans la fonction 'chaine_bdd_date_heure' n'est pas correct");
return result;
}
//=====================================================================================
// Fonctions : Ajoute une quantité de secondes à une date. Si le nombre de secondes est
// négatif, il s'agit d'une soustraction...
// Reçoit : La date à convertir et le nombre de secondes à ajouter
// Retour : La nouvelle date
//=====================================================================================
function date_add_secondes(date, secondes)
{
var date_retour = new Date();
date_retour.setTime(date.getTime() + (secondes *1000));
return(date_retour);
}
//=====================================================================================
// Fonctions : Convertir une valeur en ms en une chaine de durée (jour-heures-minutes-secondes)
// Reçoit : Le nombre à convertir
// Retour : La chaine de durée
//=====================================================================================
function chaine_duree(duree)
{
var x;
duree = Math.abs(duree); // Si la valeur est négative, on prend la valeur absolue (évite les erreurs...)
var secondes = Math.trunc(duree / 1000); // Passage de ms en s
var minutes = Math.trunc(secondes/60);
var heures = Math.trunc(minutes/60);
var jours = Math.trunc(heures/24);
secondes = secondes % 60;
minutes = minutes % 60;
heures = heures % 24;
if (jours !== 0) return(qsTr(jours + " jours," + heures + " heures"));
else if (heures !== 0) return(qsTr(heures + " heures, " + minutes + " mins"));
else if (minutes !== 0) return(qsTr(minutes + " mins, " + secondes + " secs"));
else if (secondes !== 0) return(qsTr(secondes + " secondes"));
else return("---")
}
+350
View File
@@ -0,0 +1,350 @@
.pragma library
// Type d'historique :
const HISTORIQUE_TYPE_SYSTEME = 0;
const HISTORIQUE_TYPE_CIRCUITS = 1;
const HISTORIQUE_TYPE_CAPTEURS = 2;
const HISTORIQUE_TYPE_RESERVOIRS = 3;
const HISTORIQUE_TYPE_BATTERIES = 4;
const HISTORIQUE_TYPE_USB = 5;
// Sous-types d'historique :
//-------------------------------------------------------------
// Système :
const HISTORIQUE_SOUS_TYPE_SYSTEME_REINIT = 1;
const HISTORIQUE_SOUS_TYPE_SYSTEME_ERREUR_MYSQL = 2;
const HISTORIQUE_SOUS_TYPE_SYSTEME_CONNEXION_USER = 3;
const HISTORIQUE_SOUS_TYPE_SYSTEME_REBOOT = 4;
const HISTORIQUE_SOUS_TYPE_SYSTEME_ARRET = 5;
const HISTORIQUE_SOUS_TYPE_SYSTEME_TIME_OUT_CONNEXION = 6;
const HISTORIQUE_SOUS_TYPE_SYSTEME_DECONNEXION_USER = 7;
const HISTORIQUE_SOUS_TYPE_SYSTEME_CONFIGURATION_INVALIDE_DEMARRAGE_IMPOSSIBLE = 8;
const HISTORIQUE_SOUS_TYPE_SYSTEME_SIGNAL_RECU = 9; // La valeur du signal reçu est stocké dans Valeur
const HISTORIQUE_SOUS_TYPE_SYSTEME_DEMANDE_REINIT = 10;
const HISTORIQUE_SOUS_TYPE_SYSTEME_CHANGEMENT_VEILLE = 11; // La nouvelle valeur de 'veille' est stockée dans Valeur
const HISTORIQUE_SOUS_TYPE_SYSTEME_ERREUR_SAUVEGARDE_BDD = 12; // Le message d'info sur l'erreur est stocké dans le complément.
const HISTORIQUE_SOUS_TYPE_SYSTEME_MODEM_DEMANDE_ACTIVATION = 20; // La Valeur indique dans quel sens (On ou Off)
const HISTORIQUE_SOUS_TYPE_SYSTEME_MODEM_CHANGEMENT_ETAT = 21; // La Valeur indique dans quel sens (On ou Off)
const HISTORIQUE_SOUS_TYPE_SYSTEME_ERREUR_CHANGEMENT_ETAT = 22; // La Valeur indique dans quel sens (On ou Off) le changement était demandé et a échoué
const HISTORIQUE_SOUS_TYPE_SYSTEME_MODEM_ERREUR_ETABLISSEMENT_LIAISON = 23;
const HISTORIQUE_SOUS_TYPE_SYSTEME_CHANGEMENT_ETAT_NON_SOLLICITE = 24; // La Valeur indique dans quel sens (On ou Off) le modem se trouve alors qu'il ne devrait pas
const HISTORIQUE_SOUS_TYPE_SYSTEME_CHANGEMENT_REVISION_FIRMWARE = 25; // La valeur de révision est stockée dans Complément
const HISTORIQUE_SOUS_TYPE_SYSTEME_CHANGEMENT_IMEI = 26; // Le n° est stocké dans Complément
const HISTORIQUE_SOUS_TYPE_SYSTEME_MODEM_CHANGEMENT_IMSI = 27; // Le n° est stocké dans Complément
const HISTORIQUE_SOUS_TYPE_SYSTEME_MODEM_CHANGEMENT_ICCID = 28; // Le n° est stocké dans Complément
const HISTORIQUE_SOUS_TYPE_SYSTEME_MODEM_CHANGEMENT_NO_TELEPHONE = 29; // Le n° est stocké dans Complément
const HISTORIQUE_SOUS_TYPE_SYSTEME_MODEM_CHANGEMENT_APN = 30; // Le n° est stocké dans Complément
const HISTORIQUE_SOUS_TYPE_SYSTEME_MODEM_ERREUR_FTP = 31; // Le code d'erreur reçu du modem est stocké dans Valeur et la réponse complète du modem dans Complement
const HISTORIQUE_SOUS_TYPE_SYSTEME_MODEM_ERREUR_GNSS = 32; // Le cycle d'acquisition n'a pas pu être réalisé complètement. Le nombre de cycles valides est indiqué dans Valeur.
const HISTORIQUE_SOUS_TYPE_SYSTEME_MODEM_ERREUR_M_BUS_PLEIN = 33; // Le nombre de refus de nouvelle commande dans le buffer de commandes est trop important. Le nb de refus consécutifs est dans Valeur
const HISTORIQUE_SOUS_TYPE_SYSTEME_MODEM_PERTE_LIAISON_4G_LTE = 34;
const HISTORIQUE_SOUS_TYPE_SYSTEME_MODEM_ETABLISSEMENT_LIAISON_4G_LTE = 35;
const HISTORIQUE_SOUS_TYPE_SYSTEME_MODULES_NOUVEAU_MODULE = 40; // L'adresse K-Bus où est apparu le module est dans 'id'. Le Type de module est dans Valeur. L'adresse MAC est dans Complément
const HISTORIQUE_SOUS_TYPE_SYSTEME_MODULES_SUPPRESSION_MODULE = 41; // L'adresse K-Bus où était le module est dans 'id'. Le Type de module est dans Valeur. L'adresse MAC est dans Complément
const HISTORIQUE_SOUS_TYPE_SYSTEME_FTP_UPLOAD_FICHIER_DEBUT_OK = 50; // Le nom du fichier source et du fichier destination sont stockés dans complément. La taille dans valeur
const HISTORIQUE_SOUS_TYPE_SYSTEME_FTP_UPLOAD_FICHIER_DEBUT_NOK = 51; // Le nom du fichier source et du fichier destination sont stockés dans complément. La taille dans valeur
const HISTORIQUE_SOUS_TYPE_SYSTEME_FTP_UPLOAD_FICHIER_FIN_OK = 52; // Le nom du fichier source et du fichier destination sont stockés dans complément. La taille dans valeur
const HISTORIQUE_SOUS_TYPE_SYSTEME_FTP_UPLOAD_FICHIER_FIN_NOK = 53; // Le nom du fichier source et du fichier destination sont stockés dans complément
const HISTORIQUE_SOUS_TYPE_SYSTEME_FTP_DOWNLOAD_FICHIER_DEBUT_OK = 60; // Le nom du fichier source et du fichier destination sont stockés dans complément.
const HISTORIQUE_SOUS_TYPE_SYSTEME_FTP_DOWNLOAD_FICHIER_DEBUT_NOK = 61; // Le nom du fichier source et du fichier destination sont stockés dans complément. La taille dans valeur
const HISTORIQUE_SOUS_TYPE_SYSTEME_FTP_DOWNLOAD_FICHIER_FIN_OK = 62; // Le nom du fichier source et du fichier destination sont stockés dans complément.
const HISTORIQUE_SOUS_TYPE_SYSTEME_FTP_DOWNLOAD_FICHIER_FIN_NOK = 63; // Le nom du fichier source et du fichier destination sont stockés dans complément. La taille dans valeur
const HISTORIQUE_SOUS_TYPE_SYSTEME_FTP_DOWNLOAD_LIST_NOK = 64; // Le nom du fichier de LIST est stocké dans complément.
const HISTORIQUE_SOUS_TYPE_SYSTEME_FTP_DOWNLOAD_NOMBRE_FICHIERS_LOCAUX_TROP_ELEVE = 65; // Le nombre maxi admissible est stocké dans Valeur
const HISTORIQUE_SOUS_TYPE_SYSTEME_FTP_DOWNLOAD_NOMBRE_FICHIERS_SERVEUR_TROP_ELEVE = 66; // Le nombre maxi admissible est stocké dans Valeur
const HISTORIQUE_SOUS_TYPE_SYSTEME_EXECUTION_BASH_DEBUT_OK = 70; // Le nom du fichier utilisé pour l'upload est dans le complément.
const HISTORIQUE_SOUS_TYPE_SYSTEME_EXECUTION_BASH_DEBUT_NOK = 71; // Le nom du fichier utilisé pour l'upload est dans le complément.
const HISTORIQUE_SOUS_TYPE_SYSTEME_EXECUTION_BASH_FIN_OK = 72; // Le nom du fichier utilisé pour l'upload est dans le complément.
const HISTORIQUE_SOUS_TYPE_SYSTEME_EXECUTION_BASH_FIN_NOK = 73; // Le nom du fichier utilisé pour l'upload est dans le complément.
const HISTORIQUE_SOUS_TYPE_SYSTEME_COPIE_ARCHIVE_A_VALIDER_OK = 74; // Le nom du fichier est dans le complément.
const HISTORIQUE_SOUS_TYPE_SYSTEME_COPIE_ARCHIVE_A_VALIDER_NOK = 75; // Le nom du fichier est dans le complément.
const HISTORIQUE_SOUS_TYPE_SYSTEME_COPIE_ARCHIVE_A_CHARGER_OK = 76; // Le nom du fichier est dans le complément.
const HISTORIQUE_SOUS_TYPE_SYSTEME_COPIE_ARCHIVE_A_CHARGER_NOK = 77; // Le nom du fichier est dans le complément.
const HISTORIQUE_SOUS_TYPE_SYSTEME_CHARGEMENT_CODE_DEBUT_OK = 80; // Le nom du fichier utilisé pour l'upload est dans le complément. L'ID de la carte dans ID
const HISTORIQUE_SOUS_TYPE_SYSTEME_CHARGEMENT_CODE_DEBUT_NOK = 81; // Le nom du fichier utilisé pour l'upload est dans le complément. L'ID de la carte dans ID
const HISTORIQUE_SOUS_TYPE_SYSTEME_CHARGEMENT_CODE_ERASE_FLASH_NOK = 82; // Le nom du fichier utilisé pour l'upload est dans le complément. L'ID de la carte dans ID
const HISTORIQUE_SOUS_TYPE_SYSTEME_CHARGEMENT_CODE_ECRITURE_FLASH_NOK = 83; // Le nom du fichier utilisé pour l'upload est dans le complément. L'ID de la carte dans ID
const HISTORIQUE_SOUS_TYPE_SYSTEME_CHARGEMENT_CODE_CRC_FLASH_NOK = 84; // Le nom du fichier utilisé pour l'upload est dans le complément. L'ID de la carte dans ID
const HISTORIQUE_SOUS_TYPE_SYSTEME_CHARGEMENT_CODE_FIN_OK = 85; // Le nom du fichier utilisé pour l'upload est dans le complément. L'ID de la carte dans ID
const HISTORIQUE_SOUS_TYPE_SYSTEME_CHARGEMENT_CODE_FIN_NOK = 86; // Le nom du fichier utilisé pour l'upload est dans le complément. L'ID de la carte dans ID
const HISTORIQUE_SOUS_TYPE_SYSTEME_UART_BUFFER_RX_PLEIN = 100; // L'ID précise quel UART
const HISTORIQUE_SOUS_TYPE_SYSTEME_BUS_BUFFER_COMMANDES_PLEIN = 101; // L'ID précise quel bus ('M' ou 'C')
const HISTORIQUE_SOUS_TYPE_SYSTEME_BACKLIGHT_CHANGEMENT_CONSIGNE = 110; // La valeur indique la nouvelle consigne
const HISTORIQUE_SOUS_TYPE_SYSTEME_VOLUME_CHANGEMENT_CONSIGNE = 111; // La valeur indique la nouvelle consigne
const HISTORIQUE_SOUS_TYPE_SYSTEME_LEDS_CHANGEMENT_CONSIGNE = 112; // La valeur indique la nouvelle consigne
const HISTORIQUE_SOUS_TYPE_SYSTEME_WIFI_MISE_A_JOUR_CONFIG = 130; // Les informations sur la nouvelle config sont stockées dans Complément
//-------------------------------------------------------------
// Circuits :
const HISTORIQUE_SOUS_TYPE_CIRCUITS_CHANGEMENT_CONSIGNE = 1;
const HISTORIQUE_SOUS_TYPE_CIRCUITS_OK = 10; // La valeur de limitation (en mA) est stockée dans Valeur
const HISTORIQUE_SOUS_TYPE_CIRCUITS_LIMITATION = 11; // La valeur de limitation (en mA) est stockée dans Valeur
const HISTORIQUE_SOUS_TYPE_CIRCUITS_PROTECTION = 12; // La valeur de limitation (en mA) est stockée dans Valeur
const HISTORIQUE_SOUS_TYPE_CIRCUITS_DISJONCTE = 13; // La valeur de limitation (en mA) est stockée dans Valeur
const HISTORIQUE_SOUS_TYPE_CIRCUITS_CC_BATTERIE = 14; // La valeur de limitation (en mA) est stockée dans Valeur
const HISTORIQUE_SOUS_TYPE_CIRCUITS_OPEN_LOAD = 15; // La valeur de limitation (en mA) est stockée dans Valeur
const HISTORIQUE_SOUS_TYPE_CIRCUITS_CREATION = 20;
const HISTORIQUE_SOUS_TYPE_CIRCUITS_MODIFICATION = 21;
const HISTORIQUE_SOUS_TYPE_CIRCUITS_SUPPRESSION = 22;
const HISTORIQUE_SOUS_TYPE_CIRCUITS_CONFIGURATION_INVALIDE = 30; // L'ID précise la clé primaire. Valeur contient l'ID_Carte actuellement configuré
const HISTORIQUE_SOUS_TYPE_CIRCUIT_EXPIRATION_TIME_OUT = 40; // La nouvelle valeur de consigne est stockée dans Valeur. De plus, Complément comprend 'origine_consigne/id_origine_consigne'.
//-------------------------------------------------------------
// Capteurs :
const HISTORIQUE_SOUS_TYPE_CAPTEURS_CAPTEUR_PASSAGE_OK = 1; // L'ID précise la clé primaire. VAleur contient la valeur du capteur au moment du changement
const HISTORIQUE_SOUS_TYPE_CAPTEURS_CAPTEUR_PASSAGE_ALERTE = 2; // L'ID précise la clé primaire. VAleur contient la valeur du capteur au moment du changement
const HISTORIQUE_SOUS_TYPE_CAPTEURS_CAPTEUR_PASSAGE_ALARME = 3; // L'ID précise la clé primaire. VAleur contient la valeur du capteur au moment du changement
//-------------------------------------------------------------
// Batteries :
const HISTORIQUE_SOUS_TYPE_BATTERIES_PASSAGE_NIVEAU_NORMAL = 1; // L'ID précise la batterie. La valeur indique l'ancien état
const HISTORIQUE_SOUS_TYPE_BATTERIES_PASSAGE_NIVEAU_ALERTE = 2; // L'ID précise la batterie. La valeur indique l'ancien état
const HISTORIQUE_SOUS_TYPE_BATTERIES_PASSAGE_NIVEAU_ALARME = 3; // L'ID précise la batterie. La valeur indique l'ancien état
//-------------------------------------------------------------
// Réservoirs :
const HISTORIQUE_SOUS_TYPE_RESERVOIRS_PASSAGE_NIVEAU_NORMAL = 1; // L'ID précise le Réservoir. La valeur indique l'ancien état
const HISTORIQUE_SOUS_TYPE_RESERVOIRS_PASSAGE_NIVEAU_ALERTE = 2; // L'ID précise le Réservoir. La valeur indique l'ancien état
const HISTORIQUE_SOUS_TYPE_RESERVOIRS_PASSAGE_NIVEAU_ALARME = 3; // L'ID précise le Réservoir. La valeur indique l'ancien état
//-------------------------------------------------------------
// USB :
const HISTORIQUE_SOUS_TYPE_USB_CHANGEMENT_CONSIGNE = 1; // La valeur indique le nouvel état
const HISTORIQUE_SOUS_TYPE_USB_PASSAGE_OK = 10; // L'ID contient le n° du port (1 à 3)
const HISTORIQUE_SOUS_TYPE_USB_PASSAGE_DEFAUT = 11; // L'ID contient le n° du port (1 à 3)
//=====================================================================================
// Fonctions : Génère une chaine avec le libellé de l'événement.
// Reçoit : Le type, le SOUS_TYPE, le nom de l'ID concerné, la valeur et le
// complément de l'événement.
// NB : le nom de l'ID peut être une chaine vide. dans ce cas, le texte
// généré suppose que l'on connait de façon non ambigüe l'objet concerné
// (c'est utile quand on affiche l'historique d'un circuit particulier par
// exemple).
// Retour : La chaine générée
//=====================================================================================
function historique_libelle(type, event, nom_id, valeur, complement)
{
var libelle;
var texte_nom;
var texte_event_systeme;
var texte_valeur_systeme;
if (nom_id === "") texte_nom = "";
else texte_nom = "'" + nom_id + "' ";
texte_event_systeme = "";
texte_valeur_systeme = "";
switch (type)
{
//----------------------------------------------------------------------------------------------------------------
case HISTORIQUE_TYPE_SYSTEME:
switch (event)
{
case HISTORIQUE_SOUS_TYPE_SYSTEME_REINIT: texte_event_systeme = "HISTORIQUE_SOUS_TYPE_SYSTEME_REINIT"; break;
case HISTORIQUE_SOUS_TYPE_SYSTEME_ERREUR_MYSQL: texte_event_systeme = "HISTORIQUE_SOUS_TYPE_SYSTEME_ERREUR_MYSQL"; break;
case HISTORIQUE_SOUS_TYPE_SYSTEME_CONNEXION_USER: texte_event_systeme = "HISTORIQUE_SOUS_TYPE_SYSTEME_CONNEXION_USER"; break;
case HISTORIQUE_SOUS_TYPE_SYSTEME_REBOOT: texte_event_systeme = "HISTORIQUE_SOUS_TYPE_SYSTEME_REBOOT"; break;
case HISTORIQUE_SOUS_TYPE_SYSTEME_ARRET: texte_event_systeme = "HISTORIQUE_SOUS_TYPE_SYSTEME_ARRET"; break;
case HISTORIQUE_SOUS_TYPE_SYSTEME_TIME_OUT_CONNEXION: texte_event_systeme = "HISTORIQUE_SOUS_TYPE_SYSTEME_TIME_OUT_CONNEXION"; break;
case HISTORIQUE_SOUS_TYPE_SYSTEME_DECONNEXION_USER: texte_event_systeme = "HISTORIQUE_SOUS_TYPE_SYSTEME_DECONNEXION_USER"; break;
case HISTORIQUE_SOUS_TYPE_SYSTEME_CONFIGURATION_INVALIDE_DEMARRAGE_IMPOSSIBLE: texte_event_systeme = "HISTORIQUE_SOUS_TYPE_SYSTEME_CONFIGURATION_INVALIDE_DEMARRAGE_IMPOSSIBLE"; break;
case HISTORIQUE_SOUS_TYPE_SYSTEME_SIGNAL_RECU: texte_event_systeme = "HISTORIQUE_SOUS_TYPE_SYSTEME_SIGNAL_RECU"; texte_valeur_systeme = valeur; break;
case HISTORIQUE_SOUS_TYPE_SYSTEME_DEMANDE_REINIT: texte_event_systeme = "HISTORIQUE_SOUS_TYPE_SYSTEME_DEMANDE_REINIT"; break;
case HISTORIQUE_SOUS_TYPE_SYSTEME_CHANGEMENT_VEILLE: texte_event_systeme = "HISTORIQUE_SOUS_TYPE_SYSTEME_CHANGEMENT_VEILLE"; break;
case HISTORIQUE_SOUS_TYPE_SYSTEME_ERREUR_SAUVEGARDE_BDD : texte_event_systeme = "HISTORIQUE_SOUS_TYPE_SYSTEME_ERREUR_SAUVEGARDE_BDD"; break;
case HISTORIQUE_SOUS_TYPE_SYSTEME_MODEM_DEMANDE_ACTIVATION: texte_event_systeme = "HISTORIQUE_SOUS_TYPE_SYSTEME_MODEM_DEMANDE_ACTIVATION"; break;
case HISTORIQUE_SOUS_TYPE_SYSTEME_MODEM_CHANGEMENT_ETAT: texte_event_systeme = "HISTORIQUE_SOUS_TYPE_SYSTEME_MODEM_CHANGEMENT_ETAT"; texte_valeur_systeme = valeur; break;
case HISTORIQUE_SOUS_TYPE_SYSTEME_ERREUR_CHANGEMENT_ETAT: texte_event_systeme = "HISTORIQUE_SOUS_TYPE_SYSTEME_ERREUR_CHANGEMENT_ETAT"; break;
case HISTORIQUE_SOUS_TYPE_SYSTEME_MODEM_ERREUR_ETABLISSEMENT_LIAISON: texte_event_systeme = "HISTORIQUE_SOUS_TYPE_SYSTEME_MODEM_ERREUR_ETABLISSEMENT_LIAISON"; break;
case HISTORIQUE_SOUS_TYPE_SYSTEME_CHANGEMENT_ETAT_NON_SOLLICITE: texte_event_systeme = "HISTORIQUE_SOUS_TYPE_SYSTEME_CHANGEMENT_ETAT_NON_SOLLICITE"; break;
case HISTORIQUE_SOUS_TYPE_SYSTEME_CHANGEMENT_REVISION_FIRMWARE: texte_event_systeme = "HISTORIQUE_SOUS_TYPE_SYSTEME_CHANGEMENT_REVISION_FIRMWARE"; break;
case HISTORIQUE_SOUS_TYPE_SYSTEME_CHANGEMENT_IMEI: texte_event_systeme = "HISTORIQUE_SOUS_TYPE_SYSTEME_CHANGEMENT_IMEI"; break;
case HISTORIQUE_SOUS_TYPE_SYSTEME_MODEM_CHANGEMENT_IMSI: texte_event_systeme = "HISTORIQUE_SOUS_TYPE_SYSTEME_MODEM_CHANGEMENT_IMSI"; break;
case HISTORIQUE_SOUS_TYPE_SYSTEME_MODEM_CHANGEMENT_ICCID: texte_event_systeme = "HISTORIQUE_SOUS_TYPE_SYSTEME_MODEM_CHANGEMENT_ICCID"; break;
case HISTORIQUE_SOUS_TYPE_SYSTEME_MODEM_CHANGEMENT_NO_TELEPHONE: texte_event_systeme = "HISTORIQUE_SOUS_TYPE_SYSTEME_MODEM_CHANGEMENT_NO_TELEPHONE"; break;
case HISTORIQUE_SOUS_TYPE_SYSTEME_MODEM_CHANGEMENT_APN: texte_event_systeme = "HISTORIQUE_SOUS_TYPE_SYSTEME_MODEM_CHANGEMENT_APN"; break;
case HISTORIQUE_SOUS_TYPE_SYSTEME_MODEM_ERREUR_FTP: texte_event_systeme = "HISTORIQUE_SOUS_TYPE_SYSTEME_MODEM_ERREUR_FTP"; break;
case HISTORIQUE_SOUS_TYPE_SYSTEME_MODEM_ERREUR_GNSS: texte_event_systeme = "HISTORIQUE_SOUS_TYPE_SYSTEME_MODEM_ERREUR_GNSS"; break;
case HISTORIQUE_SOUS_TYPE_SYSTEME_MODEM_ERREUR_M_BUS_PLEIN: texte_event_systeme = "HISTORIQUE_SOUS_TYPE_SYSTEME_MODEM_ERREUR_M_BUS_PLEIN"; break;
case HISTORIQUE_SOUS_TYPE_SYSTEME_MODEM_PERTE_LIAISON_4G_LTE: texte_event_systeme = "SYSTEME_MODEM_PERTE_LIAISON_4G_LTE"; break;
case HISTORIQUE_SOUS_TYPE_SYSTEME_MODEM_ETABLISSEMENT_LIAISON_4G_LTE: texte_event_systeme = "SYSTEME_MODEM_ETABLISSEMENT_LIAISON_4G_LTE"; break;
case SYSTEME_MODULES_NOUVEAU_MODULE: texte_event_systeme = "HISTORIQUE_SOUS_TYPE_SYSTEME_MODULES_NOUVEAU_MODULE"; break;
case SYSTEME_MODULES_SUPPRESSION_MODULE: texte_event_systeme = "HISTORIQUE_SOUS_TYPE_SYSTEME_MODULES_SUPPRESSION_MODULE"; break;
case HISTORIQUE_SOUS_TYPE_SYSTEME_FTP_UPLOAD_FICHIER_DEBUT_OK: texte_event_systeme = "HISTORIQUE_SOUS_TYPE_SYSTEME_FTP_UPLOAD_FICHIER_DEBUT_OK"; break;
case HISTORIQUE_SOUS_TYPE_SYSTEME_FTP_UPLOAD_FICHIER_DEBUT_NOK: texte_event_systeme = "HISTORIQUE_SOUS_TYPE_SYSTEME_FTP_UPLOAD_FICHIER_DEBUT_NOK"; break;
case HISTORIQUE_SOUS_TYPE_SYSTEME_FTP_UPLOAD_FICHIER_FIN_OK: texte_event_systeme = "HISTORIQUE_SOUS_TYPE_SYSTEME_FTP_UPLOAD_FICHIER_FIN_OK"; break;
case HISTORIQUE_SOUS_TYPE_SYSTEME_FTP_UPLOAD_FICHIER_FIN_NOK: texte_event_systeme = "HISTORIQUE_SOUS_TYPE_SYSTEME_FTP_UPLOAD_FICHIER_FIN_NOK"; break;
case HISTORIQUE_SOUS_TYPE_SYSTEME_FTP_DOWNLOAD_FICHIER_DEBUT_OK: texte_event_systeme = "HISTORIQUE_SOUS_TYPE_SYSTEME_FTP_DOWNLOAD_FICHIER_DEBUT_OK"; break;
case HISTORIQUE_SOUS_TYPE_SYSTEME_FTP_DOWNLOAD_FICHIER_DEBUT_NOK: texte_event_systeme = "HISTORIQUE_SOUS_TYPE_SYSTEME_FTP_DOWNLOAD_FICHIER_DEBUT_NOK"; break;
case HISTORIQUE_SOUS_TYPE_SYSTEME_FTP_DOWNLOAD_FICHIER_FIN_OK: texte_event_systeme = "HISTORIQUE_SOUS_TYPE_SYSTEME_FTP_DOWNLOAD_FICHIER_FIN_OK"; break;
case HISTORIQUE_SOUS_TYPE_SYSTEME_FTP_DOWNLOAD_FICHIER_FIN_NOK: texte_event_systeme = "HISTORIQUE_SOUS_TYPE_SYSTEME_FTP_DOWNLOAD_FICHIER_FIN_NOK"; break;
case HISTORIQUE_SOUS_TYPE_SYSTEME_FTP_DOWNLOAD_LIST_NOK: texte_event_systeme = "HISTORIQUE_SOUS_TYPE_SYSTEME_FTP_DOWNLOAD_LIST_NOK"; break;
case HISTORIQUE_SOUS_TYPE_SYSTEME_FTP_DOWNLOAD_NOMBRE_FICHIERS_LOCAUX_TROP_ELEVE: texte_event_systeme = "HISTORIQUE_SOUS_TYPE_SYSTEME_FTP_DOWNLOAD_NOMBRE_FICHIERS_LOCAUX_TROP_ELEVE"; break;
case HISTORIQUE_SOUS_TYPE_SYSTEME_FTP_DOWNLOAD_NOMBRE_FICHIERS_SERVEUR_TROP_ELEVE: texte_event_systeme = "HISTORIQUE_SOUS_TYPE_SYSTEME_FTP_DOWNLOAD_NOMBRE_FICHIERS_SERVEUR_TROP_ELEVE"; break;
case HISTORIQUE_SOUS_TYPE_SYSTEME_EXECUTION_BASH_DEBUT_OK: texte_event_systeme = "HISTORIQUE_SOUS_TYPE_SYSTEME_EXECUTION_BASH_DEBUT_OK"; break;
case HISTORIQUE_SOUS_TYPE_SYSTEME_EXECUTION_BASH_DEBUT_NOK: texte_event_systeme = "HISTORIQUE_SOUS_TYPE_SYSTEME_EXECUTION_BASH_DEBUT_NOK"; break;
case HISTORIQUE_SOUS_TYPE_SYSTEME_EXECUTION_BASH_FIN_OK: texte_event_systeme = "HISTORIQUE_SOUS_TYPE_SYSTEME_EXECUTION_BASH_FIN_OK"; break;
case HISTORIQUE_SOUS_TYPE_SYSTEME_EXECUTION_BASH_FIN_NOK: texte_event_systeme = "HISTORIQUE_SOUS_TYPE_SYSTEME_EXECUTION_BASH_FIN_NOK"; break;
case HISTORIQUE_SOUS_TYPE_SYSTEME_COPIE_ARCHIVE_A_VALIDER_OK: texte_event_systeme = "HISTORIQUE_SOUS_TYPE_SYSTEME_COPIE_ARCHIVE_A_VALIDER_OK"; break;
case HISTORIQUE_SOUS_TYPE_SYSTEME_COPIE_ARCHIVE_A_VALIDER_NOK: texte_event_systeme = "HISTORIQUE_SOUS_TYPE_SYSTEME_COPIE_ARCHIVE_A_VALIDER_NOK"; break;
case HISTORIQUE_SOUS_TYPE_SYSTEME_COPIE_ARCHIVE_A_CHARGER_OK: texte_event_systeme = "HISTORIQUE_SOUS_TYPE_SYSTEME_COPIE_ARCHIVE_A_CHARGER_OK"; break;
case HISTORIQUE_SOUS_TYPE_SYSTEME_COPIE_ARCHIVE_A_CHARGER_NOK: texte_event_systeme = "HISTORIQUE_SOUS_TYPE_SYSTEME_COPIE_ARCHIVE_A_CHARGER_NOK"; break;
case HISTORIQUE_SOUS_TYPE_SYSTEME_CHARGEMENT_CODE_DEBUT_OK: texte_event_systeme = "HISTORIQUE_SOUS_TYPE_SYSTEME_CHARGEMENT_CODE_DEBUT_OK"; break;
case HISTORIQUE_SOUS_TYPE_SYSTEME_CHARGEMENT_CODE_DEBUT_NOK: texte_event_systeme = "HISTORIQUE_SOUS_TYPE_SYSTEME_CHARGEMENT_CODE_DEBUT_NOK"; break;
case HISTORIQUE_SOUS_TYPE_SYSTEME_CHARGEMENT_CODE_ERASE_FLASH_NOK: texte_event_systeme = "HISTORIQUE_SOUS_TYPE_SYSTEME_CHARGEMENT_CODE_ERASE_FLASH_NOK"; break;
case HISTORIQUE_SOUS_TYPE_SYSTEME_CHARGEMENT_CODE_ECRITURE_FLASH_NOK: texte_event_systeme = "HISTORIQUE_SOUS_TYPE_SYSTEME_CHARGEMENT_CODE_ECRITURE_FLASH_NOK"; break;
case HISTORIQUE_SOUS_TYPE_SYSTEME_CHARGEMENT_CODE_CRC_FLASH_NOK: texte_event_systeme = "HISTORIQUE_SOUS_TYPE_SYSTEME_CHARGEMENT_CODE_CRC_FLASH_NOK"; break;
case HISTORIQUE_SOUS_TYPE_SYSTEME_CHARGEMENT_CODE_FIN_OK: texte_event_systeme = "HISTORIQUE_SOUS_TYPE_SYSTEME_CHARGEMENT_CODE_FIN_OK"; break;
case HISTORIQUE_SOUS_TYPE_SYSTEME_CHARGEMENT_CODE_FIN_NOK: texte_event_systeme = "HISTORIQUE_SOUS_TYPE_SYSTEME_CHARGEMENT_CODE_FIN_NOK"; break;
case HISTORIQUE_SOUS_TYPE_SYSTEME_UART_BUFFER_RX_PLEIN: texte_event_systeme = "HISTORIQUE_SOUS_TYPE_SYSTEME_UART_BUFFER_RX_PLEIN"; break;
case HISTORIQUE_SOUS_TYPE_SYSTEME_BUS_BUFFER_COMMANDES_PLEIN: texte_event_systeme = "HISTORIQUE_SOUS_TYPE_SYSTEME_BUS_BUFFER_COMMANDES_PLEIN"; break;
case HISTORIQUE_SOUS_TYPE_SYSTEME_BACKLIGHT_CHANGEMENT_CONSIGNE: texte_event_systeme = "HISTORIQUE_SOUS_TYPE_SYSTEME_BACKLIGHT_CHANGEMENT_CONSIGNE"; break;
case HISTORIQUE_SOUS_TYPE_SYSTEME_VOLUME_CHANGEMENT_CONSIGNE: texte_event_systeme = "HISTORIQUE_SOUS_TYPE_SYSTEME_VOLUME_CHANGEMENT_CONSIGNE"; break;
case HISTORIQUE_SOUS_TYPE_SYSTEME_LEDS_CHANGEMENT_CONSIGNE: texte_event_systeme = "HISTORIQUE_SOUS_TYPE_SYSTEME_LEDS_CHANGEMENT_CONSIGNE"; break;
case HISTORIQUE_SOUS_TYPE_SYSTEME_WIFI_MISE_A_JOUR_CONFIG : texte_event_systeme = "HISTORIQUE_SOUS_TYPE_SYSTEME_WIFI_MISE_A_JOUR_CONFIG"; break;
default : libelle = "Evénement de type SYSTEME avec code de sous-type inconnu [" + event + "]";
}
texte_event_systeme = texte_event_systeme.replace('HISTORIQUE_SOUS_TYPE_SYSTEME_', '');
texte_event_systeme = texte_event_systeme.replace(/_/g, ' ');
if (texte_event_systeme !== "") libelle = "Evénement système de type '" + texte_event_systeme.replace(/_/g, ' ') + "' [" + event + "]"; // le 'g' permet de remplacer toutes les occurences (sinon, seule la première sera remplacée)
if (texte_valeur_systeme !== "") libelle = libelle + " avec valeur = [" + texte_valeur_systeme + "]";
break;
//----------------------------------------------------------------------------------------------------------------
case HISTORIQUE_TYPE_CIRCUITS:
switch (event)
{
case HISTORIQUE_SOUS_TYPE_CIRCUITS_CHANGEMENT_CONSIGNE:
if (valeur === 0) libelle = qsTr("Commande 'OFF' du circuit %1").arg(texte_nom);
else if (valeur === 100) libelle = qsTr("Commande 'ON' du circuit %1").arg(texte_nom);
else libelle = qsTr("Commande à %1 % du circuit %2").arg(valeur).arg(texte_nom);
break;
case HISTORIQUE_SOUS_TYPE_CIRCUITS_OK: libelle = qsTr("Le circuit %1 est revenu à son état de fonctionnement normal").arg(texte_nom); break;
case HISTORIQUE_SOUS_TYPE_CIRCUITS_LIMITATION: libelle = qsTr("L'alimentation du circuit %1 a été coupée car sa limite de courant (%2 A) a été dépassée").arg(texte_nom).arg(valeur.toFixed(2)); break;
case HISTORIQUE_SOUS_TYPE_CIRCUITS_PROTECTION: libelle = qsTr("L'alimentation du circuit %1 a été coupée car les circuits de protection interne ont détecté une anomalie").arg(texte_nom); break;
case HISTORIQUE_SOUS_TYPE_CIRCUITS_DISJONCTE: libelle = qsTr("L'alimentation du circuit %1 a été coupée car le seuil de disjonction a été dépassé").arg(texte_nom); break;
case HISTORIQUE_SOUS_TYPE_CIRCUITS_CC_BATTERIE: libelle = qsTr("Le circuit %1 est en court-circuit avec le + batterie").arg(texte_nom); break;
case HISTORIQUE_SOUS_TYPE_CIRCUITS_OPEN_LOAD: libelle = qsTr("L'alimentation du circuit %1 a été coupée car la consommation totale du module a été dépassé").arg(texte_nom); break;
case HISTORIQUE_SOUS_TYPE_CIRCUITS_CREATION: libelle = qsTr("Le circuit %1 a été créé").arg(texte_nom); break;
case HISTORIQUE_SOUS_TYPE_CIRCUITS_MODIFICATION: libelle = qsTr("La configuration du circuit %1 a été modifiée").arg(texte_nom); break;
case HISTORIQUE_SOUS_TYPE_CIRCUITS_SUPPRESSION: libelle = qsTr("Le circuit %1 a été supprimé").arg(texte_nom); break;
case HISTORIQUE_SOUS_TYPE_CIRCUITS_CONFIGURATION_INVALIDE: libelle = qsTr("La configuration du circuit %1 est invalide car le module sur lequel le circuit est configuré n'est pas présent").arg(texte_nom); break;
case HISTORIQUE_SOUS_TYPE_CIRCUIT_EXPIRATION_TIME_OUT: libelle = qsTr("Le circuit %1 a fonctionné trop longtemps (> %2 secondes). Il a donc été désactivé automatiquement.").arg(texte_nom).arg(valeur); break;
default : libelle = qsTr("Evénement de type CIRCUITS avec code de sous-type inconnu [%1]").arg(event)
}
break;
//----------------------------------------------------------------------------------------------------------------
case HISTORIQUE_TYPE_CAPTEURS:
switch (event)
{
case HISTORIQUE_SOUS_TYPE_CAPTEURS_CAPTEUR_PASSAGE_OK: libelle = qsTr("Le capteur %1 est repassé à l'état normal").arg(texte_nom); break;
case HISTORIQUE_SOUS_TYPE_CAPTEURS_CAPTEUR_PASSAGE_ALERTE: libelle = qsTr("Le capteur %1 est passé à l'état ALERTE").arg(texte_nom); break;
case HISTORIQUE_SOUS_TYPE_CAPTEURS_CAPTEUR_PASSAGE_ALARME: libelle = qsTr("Le capteur %1 est passé à l'état ALARME").arg(texte_nom); break;
default : libelle = qsTr("Evénement de type CAPTEURS avec code de sous-type inconnu [%1]").arg(event)
}
break;
//----------------------------------------------------------------------------------------------------------------
case HISTORIQUE_TYPE_BATTERIES:
switch (event)
{
case HISTORIQUE_SOUS_TYPE_BATTERIES_PASSAGE_NIVEAU_NORMAL: libelle = qsTr("Le niveau de charge de la batterie %1 est repassé au niveau normal").arg(texte_nom); break;
case HISTORIQUE_SOUS_TYPE_BATTERIES_PASSAGE_NIVEAU_ALERTE:
if (valeur === HISTORIQUE_SOUS_TYPE_BATTERIES_PASSAGE_NIVEAU_NORMAL) libelle = qsTr("Le niveau de charge de la batterie %1 est passé en dessous du niveau d'alerte").arg(texte_nom);
else libelle = qsTr("Le niveau de charge de la batterie %1 est passé au-dessus du niveau d'alarme (il est maintenant au niveau d'alerte uniquement)").arg(texte_nom);
break;
case HISTORIQUE_SOUS_TYPE_BATTERIES_PASSAGE_NIVEAU_ALARME: libelle = qsTr("Le niveau de charge de la batterie %1 est passé en-dessous du niveau d'alarme").arg(texte_nom); break;
default : libelle = qsTr("Evénement de type BATTERIES avec code de sous-type inconnu [%1]").arg(event)
}
break;
//----------------------------------------------------------------------------------------------------------------
case HISTORIQUE_TYPE_RESERVOIRS:
switch (event)
{
case HISTORIQUE_SOUS_TYPE_RESERVOIRS_PASSAGE_NIVEAU_NORMAL: libelle = qsTr("Le niveau du réservoir %1 est repassé au niveau normal").arg(texte_nom); break;
case HISTORIQUE_SOUS_TYPE_RESERVOIRS_PASSAGE_NIVEAU_ALERTE:
if (valeur === HISTORIQUE_SOUS_TYPE_RESERVOIRS_PASSAGE_NIVEAU_NORMAL) libelle = qsTr("Le niveau du réservoir %1 est passé en dessous du niveau d'alerte").arg(texte_nom);
else libelle = qsTr("Le niveau du réservoir %1 est passé au-dessus du niveau d'alarme (il est maintenant au niveau d'alerte uniquement)").arg(texte_nom);
break;
case HISTORIQUE_SOUS_TYPE_RESERVOIRS_PASSAGE_NIVEAU_ALARME: libelle = qsTr("Le niveau du réservoir %1 est passé en-dessous du niveau d'alarme").arg(texte_nom); break;
default : libelle = qsTr("Evénement de type RESERVOIRS avec code de sous-type inconnu [%1]").arg(event)
}
break;
//----------------------------------------------------------------------------------------------------------------
case HISTORIQUE_TYPE_USB:
switch (event)
{
case HISTORIQUE_SOUS_TYPE_USB_CHANGEMENT_CONSIGNE: libelle = qsTr("La consigne des ports USB est passée à %1").arg(valeur); break;
case HISTORIQUE_SOUS_TYPE_USB_PASSAGE_OK: libelle = qsTr("L'état des ports USB est redevenu normal"); break;
case HISTORIQUE_SOUS_TYPE_USB_PASSAGE_DEFAUT: libelle = qsTr("Défaut détecté sur les ports USB"); break;
default : libelle = qsTr("Evénement de type USB avec code de sous-type inconnu [%1]").arg(event)
}
break;
//----------------------------------------------------------------------------------------------------------------
default : libelle = qsTr("Evénement de type INCONNU (avec code de sous-type [%1]").arg(event)
}
return(libelle);
}
+125
View File
@@ -0,0 +1,125 @@
.pragma library
// Nb : l'ordre des array ci-dessous défini l'ordre d'affichage (sur la page Paramètres notamment)
let langues = [
['FR', 'fr_FR', 'Français' ], // 0
['EN', 'en_EN', 'English' ], // 1
['IT', 'it_IT', 'Italiano' ], // 2
['ES', 'es_ES', 'Español' ], // 3
['DE', 'de_DE', 'Deutsch' ], // 4
['PT', 'pt_PT', 'Português' ], // 5
['NL', 'nl_NL', 'Nederlands' ], // 6
['LV', 'lv_LV', 'Latviski' ], // 7
['CA', 'ca_ES', 'català' ] // 8
];
let volumes = [
['Litres', 'lit.' ], // 0
['US Gallons', 'US Ga' ], // 1
['Imperial Gallons', 'Imp. Ga' ] // 2
];
let temperatures = [
['Celsius', '°C' ], // 0
['Fahrenheit', '°F' ] // 1
];
//=====================================================================================
// Fonctions : Fournit le nombre de langues disponibles
// Reçoit : Rien
// Retour : Le nb de langues
//=====================================================================================
function nb_langues()
{
return(langues.length);
}
//=====================================================================================
// Fonctions : Fournit le code pays de la langue dont le n° est passé en paramètre
//=====================================================================================
function code_pays(no)
{
if ((no < 0) || (no >= langues.length)) no = 0;
return(langues[no][0]);
}
//=====================================================================================
// Retour : Le code langue (par exemple : "fr_FR" ou "en_EN"
//=====================================================================================
function code_langue(no)
{
if ((no < 0) || (no >= langues.length)) no = 0;
return(langues[no][1]);
}
//=====================================================================================
// Retour : Le nom de la langue (par exemple : "Français" ou "English"
//=====================================================================================
function nom_langue(no)
{
if ((no < 0) || (no >= langues.length)) no = 0;
return(langues[no][2]);
}
//=====================================================================================
// Fonctions : Fournit le nombre d'unités de volume
// Reçoit : Rien
// Retour : Le nb d'unités de volume
//=====================================================================================
function nb_unites_volume()
{
return(volumes.length);
}
//=====================================================================================
// Retour : Le nom (par exemple : "Litres" ou "US Gallons"
//=====================================================================================
function nom_unite_volume(no)
{
if ((no < 0) || (no >= volumes.length)) no = 0;
return(volumes[no][0]);
}
//=====================================================================================
// Retour : Le symbole (par exemple : "lit." ou "US Ga")
//=====================================================================================
function symbole_unite_volume(no)
{
if ((no < 0) || (no >= volumes.length)) no = 0;
return(volumes[no][1]);
}
//=====================================================================================
// Fonctions : Fournit le nombre d'unités de température
// Reçoit : Rien
// Retour : Le nb d'unités de temperature
//=====================================================================================
function nb_unites_temperature()
{
return(temperatures.length);
}
//=====================================================================================
// Retour : Le nom (par exemple : "Celsius" ou "Farenheit"
//=====================================================================================
function nom_unite_temperature(no)
{
if ((no < 0) || (no >= temperatures.length)) no = 0;
return(temperatures[no][0]);
}
//=====================================================================================
// Retour : Le symbole (par exemple : "°C" ou "°F")
//=====================================================================================
function symbole_unite_temperature(no)
{
if ((no < 0) || (no >= temperatures.length)) no = 0;
return(temperatures[no][1]);
}
+86
View File
@@ -0,0 +1,86 @@
.pragma library
//=====================================================================================
// Fonctions : Fournit une chaine de caractères constituant une liste de valeurs§niveaux
// du réservoir. La liste est composée de couples de nombre séparés par des ';'
// et les deux valeurs au sein d'un couple sont séparés par '/'.
// Par exemple : "0/0.00;190/100.00"
// La chaine est établie en fonction du paramètre de 'mode' et de 'sous_mode' du réservoir.
// Reçoit : Le type d'entrée, le mode et sous_mode utilisés pour ce réservoir
// Retour : La chaine
//=====================================================================================
function chaine_valeur_niveau_reservoir_usine(type, mode, sous_mode)
{
var i;
var masque;
var chaine = "";
switch (type)
{
//===============================================
case 1 : // Entrée Auxiliaire :
if ((mode < 1) || (mode > 12)) break; // Cf 'shipheart_reservoir_max_nombre_entrees_aux' dans main.qml
masque = 1;
chaine = "0/0";
for (i=1; i<=mode; i++)
{
chaine += ';' + masque + '/' + ((100*i) / mode).toFixed(2);
masque = (masque << 1) | 1;
}
break;
//===============================================
case 2 : // Entrée Level Sensor :
switch(mode)
{
//------------------------------------------------------------
case 0:
break;
//------------------------------------------------------------
case 1: // 0-10V
chaine = "0.000/0.00";
chaine += ";10.000/100.00";
break;
//------------------------------------------------------------
case 2: // 500R
switch(sous_mode)
{
case 1: // 0-190R
chaine = "2/0.00";
chaine += ";185/100.00";
break;
case 2: // 240-30R
chaine = "235/0.00";
chaine += ";45/100.00";
break;
case 0: // 0-500R
default:
chaine = "0/0.00";
chaine += ";700/100.00";
break;
}
break;
//------------------------------------------------------------
case 3: // 4-20mA
chaine = "4.000/0.00";
chaine += ";20.000/100.00";
break;
//------------------------------------------------------------
default: // Non défini (on part donc sur une entrée en tension de 0 à 10V)
chaine = "0.000/0.00";
chaine += ";10.000/100.00";
break;
}
break;
}
console.log("Chaine de valeurs/niveaux pour le réservoir = [" + chaine + "]")
return(chaine);
}
+20
View File
@@ -0,0 +1,20 @@
# Exécuté à CHAQUE build (pas seulement à chaque reconfiguration CMake) via
# la cible "generate_version" dans CMakeLists.txt, qui n'a aucun suivi de
# fichier et est donc toujours considérée périmée par Ninja/Make.
#
# Reproduit le comportement du .pro d'origine : deux exécutables différents
# (même par une modification infime du code) obtiennent systématiquement un
# horodatage différent, garantissant l'unicité recherchée.
string(TIMESTAMP APP_VERSION "%y%m%d_%H%M%S")
file(WRITE "${OUTPUT_FILE}"
"#ifndef _VERSION_H
#define _VERSION_H
#define VERSION \"${APP_VERSION}\"
#endif // _VERSION_H
")
message(STATUS "Version ShipHeart Qt régénérée : ${APP_VERSION}")
+3880
View File
File diff suppressed because it is too large Load Diff
+222
View File
@@ -0,0 +1,222 @@
//========================================================================================================================================================================
// HOME_ACCUEIL.QML
// Ce fichier est la page d'accueil par défaut. Il propose l'affichage du logo ShipHeart et le nom du bateau.
// La page est appelée depuis la page main.qml lorsque le paramètre 'shipheart_specifique_client[0]' est égal à 0
//========================================================================================================================================================================
import QtQuick 2.12
import QtQuick.Controls 2.12
import qt.koriolan.shipheart 1.0
Shipheart_page {
id: page_home_o_yachts
// Mis en forme :
readonly property double pourcentage_hauteur_ligne_haut: 0.7
readonly property double espace_lignes_principales: 0 // L'espace ne sert à rien et est même contre-productif pour avoir le logo centré entre haut de l'écran et le texte "Bienvenue à bord...."
readonly property double affichage_hauteur_utile: shipheart_hauteur_page - shipheart_hauteur_bandeau_bas
readonly property int periode_raffraichissement_parametres: 1000 // En ms
readonly property int duree_affichage: 15 // En s
// Affichage :
// Données :
property string nom_du_bateau: ""
property int compteur_affichage: 0
// Modèles :
// Note : les modèles chargés dans 'main.qml' n'ont pas à être rechargés dans les pages suivantes
//-------------------------------------------------
// Ouverture :
//-------------------------------------------------
StackView.onActivating: { // Le 'onActivating' permet de ne pas voir la mise à jour puisqu'elle est lancée dès que la page va être chargée
init_page();
timer_raffraichissement_parametres.start();
}
//-------------------------------------------------
// Fermeture :
//-------------------------------------------------
StackView.onDeactivating: {
timer_raffraichissement_parametres.stop();
}
//===========================================================
// Timer periode raffraichissement parametres :
//===========================================================
Timer {
id: timer_raffraichissement_parametres
interval: periode_raffraichissement_parametres
triggeredOnStart: false
running: true
repeat: true
onTriggered: {
if (++ compteur_affichage > duree_affichage) sortie_page();
}
}
//-------------------------------------------------------
// Ecran Haut :
//-------------------------------------------------------
Rectangle {
id: zone_affichage_haut_gauche
x: 0
y: 0
width: shipheart_largeur_page
height: (affichage_hauteur_utile * pourcentage_hauteur_ligne_haut) - (espace_lignes_principales);
color: "transparent"
Image {
source: (shipheart_fichier_image_accueil !== "")
? "file://" + shipheart_repertoire_config + shipheart_sous_repertoire_images_accueil + "/" + shipheart_fichier_image_accueil
: "/images/" + shipheart_nom_fichier_image_accueil_par_defaut
sourceSize.height: parent.height * 0.7
anchors {
verticalCenter: parent.verticalCenter
horizontalCenter: parent.horizontalCenter
}
}
}
//-------------------------------------------------------
// Ecran Bas :
//-------------------------------------------------------
Rectangle {
id: zone_affichage_bas_gauche
x: 0
y: zone_affichage_haut_gauche.height + espace_lignes_principales;
width: shipheart_largeur_page
height: (affichage_hauteur_utile * (1 - pourcentage_hauteur_ligne_haut)) - (espace_lignes_principales);
color: "transparent"
Text {
anchors {
top: parent.top // Permet que ce texte soit juste en-dessous de la zone haute (sur laquelle le logo est centré => donc le logo est centré entre haut de l'écran et ce texte)
horizontalCenter: parent.horizontalCenter
}
font.pixelSize: 24
text: qsTr("Bienvenue à bord de");
color: couleur_blanc_casse
}
Text {
anchors {
verticalCenter: parent.verticalCenter
horizontalCenter: parent.horizontalCenter
verticalCenterOffset: -20 // Remonté légèrement le nom du bateau...
}
font.pixelSize: 50
text: '" ' + nom_du_bateau + ' "';
color: couleur_blanc_casse
font.weight: Font.Black
}
}
//-------------------------------------------------------
// 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
Rectangle {
width: parent.width
height: parent.height
color: shipheart_couleur_fond_bouton //"transparent"
Rectangle { // Barre augmentant selon l'évolution du compteur_affichage
width: (compteur_affichage / duree_affichage) * parent.width
height: parent.height
color: shipheart_couleur_fond_bouton_actif
opacity: 0.5
radius: shipheart_rayon_bouton
Behavior on width { NumberAnimation { duration: periode_raffraichissement_parametres } }
}
Image {
source: "/images/right_sombre.png"
sourceSize.height: parent.height * 0.5
anchors {
verticalCenter: parent.verticalCenter
horizontalCenter: parent.horizontalCenter
horizontalCenterOffset: -10
}
}
Image {
source: "/images/right_sombre.png"
sourceSize.height: parent.height * 0.5
anchors {
verticalCenter: parent.verticalCenter
horizontalCenter: parent.horizontalCenter
horizontalCenterOffset: +10
}
}
MouseArea {
anchors.fill: parent
onClicked: {
shipheart_clic();
sortie_page();
//stackView.push("parametres_avances.qml");
}
}
}
}
//===========================================================
// Economiseur : Doit être placé en fin de page pour fonctionner
//===========================================================
MouseArea {
anchors.fill: parent
preventStealing: true // Permet que cela fonctionne même si un élément flickable (notamment) soit derrière et "vole" le click
enabled: flag_economiseur_actif
onClicked: {
mise_en_veille(0);
}
}
//=============================================================================================================
// Init de la page
//=============================================================================================================
function init_page()
{
var req, res
mise_en_veille(0);
console.log("Init de Home_accueil...");
// Mise à jour du nom du bateau :
req = 'SELECT Valeur FROM TB_Configuration WHERE Nom="Nom_du_bateau" ';
res = bdd.requete_unique(req);
nom_du_bateau = res[0];
compteur_affichage = 0;
}
//=============================================================================================================
// Sortie de la page
//=============================================================================================================
function sortie_page()
{
compteur_affichage = 0; // Pour le cas où on réouvrirait la page (via le bouton paramètres / appui long) ; dans ce cas, la remise à 0 du compteur prend quelques secondes (ce sera donc fait avant si on le remet de suite à 0)
stackView.push("home.qml");
}
}
+282
View File
@@ -0,0 +1,282 @@
//========================================================================================================================================================================
// HOME_ATTENTE.QML
// Ce fichier affiche un fond d'écran standard mais avec rien dedans. Il est utilisé au démarrage, pour attendre que l'ApplicationWindow soit prête
// et que l'accès à la BDD permette de décider quelle page d'accueil lancer.
// Sans cette page, la StackView affiche une page toute blanche pendant 1-2s avant que la première Home page soit chargée...
//========================================================================================================================================================================
import QtQuick 2.12
import QtQuick.Controls 2.12
import qt.koriolan.shipheart 1.0
Shipheart_page {
id: page_home_attente
//==============================================================
// Recherche de la Home Page à utiliser :
//==============================================================
Timer {
id: timer_recherche_home_page
interval: 300
running: true
repeat: true
onTriggered: {
console.log("Adresse de ShipBase = " + AppInfo.adresse_shipbase)
console.log("Etat de la connexion à la BDD = " + AppInfo.etat_connexion_bdd)
console.log("Type d'installation = " + AppInfo.type_installation)
console.log("Taille de l'écran = " + AppInfo.taille_ecran)
//----------------------------------------------------
// Valeurs correspondant au type d'installation :
//----------------------------------------------------
switch (Number(AppInfo.type_installation))
{
default:
case shipheart_type_installation_shipbase:
shipheart_type_installation = shipheart_type_installation_shipbase;
break;
case shipheart_type_installation_ecran_deporte:
switch (Number(AppInfo.taille_ecran))
{
default:
case 7:
shipheart_type_installation = shipheart_type_installation_ecran_deporte_7_pouces;
break;
case 10:
shipheart_type_installation = shipheart_type_installation_ecran_deporte_10_pouces;
break;
case 22:
shipheart_type_installation = shipheart_type_installation_ecran_deporte_22_pouces;
break;
}
break;
}
console.log("Type d'installation = " + shipheart_type_installation)
//shipheart_type_installation = shipheart_type_installation_shipbase
//shipheart_type_installation = shipheart_type_installation_ecran_deporte_22_pouces
// Valeurs par défaut :
shipheart_largeur_page = 1280
shipheart_hauteur_page = 800
shipheart_hauteur_bandeau_haut = 60
shipheart_hauteur_titre_2 = 50
shipheart_hauteur_bandeau_bas = 80
shipheart_hauteur_clavier = 305
//----------------------------------------------------
// Cas où la connexion à la BDD n'a pas été possible :
//----------------------------------------------------
if ((AppInfo.etat_connexion_bdd === 0) && (Number(AppInfo.type_installation) === shipheart_type_installation_ecran_deporte))
{
stackView.push("config_shiprs.qml", {"connexion_impossible": true})
timer_recherche_home_page.stop(); // Arret du timer car il n'est plus utile maintenant
}
else
{
//----------------------------------------------------
// Paramètres spécifiques dans le cas des ShipRS :
// NB il est important de ne PAS faire ce traitement avant l'appel
// à 'connexion_impossible.qml' car, sinon, on peut se trouver dans une
// config de 22" (par exemple) et ne plus avoir accès aux boutons de
// configuration !!
//----------------------------------------------------
switch (shipheart_type_installation)
{
//---------------------------------------------------------------------------
case shipheart_type_installation_ecran_deporte_7_pouces:
shipheart_largeur_page = 1280;
shipheart_hauteur_page = 800;
synchro_repertoires_shiprs();
break;
//---------------------------------------------------------------------------
case shipheart_type_installation_ecran_deporte_10_pouces:
shipheart_largeur_page = 1280; // Spec de l'écran DENSITRON de ShipBase
shipheart_hauteur_page = 800; // Spec de l'écran DENSITRON de ShipBase
synchro_repertoires_shiprs();
break;
//---------------------------------------------------------------------------
case shipheart_type_installation_ecran_deporte_22_pouces:
shipheart_largeur_page = 1920;
shipheart_hauteur_page = 1080;
synchro_repertoires_shiprs();
break;
//---------------------------------------------------------------------------
default: // Dans ce cas, ce sont les valeurs par défaut qui sont conservées.
case shipheart_type_installation_shipbase:
break;
}
//----------------------------------------------------
// Recherche de la version de ShipBase :
//----------------------------------------------------
init_shipheart_version_cm();
//----------------------------------------------------
// Recherche du thème d'affichage :
//----------------------------------------------------
init_shipheart_theme();
//----------------------------------------------------
// Première page à afficher selon le client :
//----------------------------------------------------
var shipheart_nom_initial_page = "";
switch (Number(init_shipheart_specifique_client()))
{
//---------------------------------
default:
case shipheart_specifique_client_tous:
shipheart_nom_initial_page = "qrc:/home_accueil.qml"
break;
//---------------------------------
case shipheart_specifique_client_suncy:
shipheart_nom_initial_page = "qrc:/home_suncy.qml";
break;
}
if ((shipheart_generation_shipbase != shipheart_generation_shipbase_inconnue) && // A priori, ne devrait pas arriver mais si on n'a pas trouvé, on va reommencer la recherche après une période de timer.
(shipheart_nom_initial_page !== ""))
{
timer_recherche_home_page.stop(); // Arret du timer car il n'est plus utile maintenant
stackView.push(shipheart_nom_initial_page)
//console.log("La Home Page sélectionnée est '" + shipheart_nom_initial_page + "'")
}
}
}
}
//==============================================================
// Réactivation de la recherche lors d'un retour sur cette page :
//==============================================================
StackView.onActivated: {
//console.log("!!! Page Home_attente activée")
timer_recherche_home_page.start(); // Relance le timer pour faire une nouvelle recherche
}
//==============================================================
// Zone d'affichage :
//==============================================================
Rectangle {
x: 0
y: 0
width: 5000 // Permet d'être qûr qu'on couvre tout l'écran, quel qu'il soit
height: 5000 // Permet d'être qûr qu'on couvre tout l'écran
color: "transparent" // C'est la ligne importante qui permet de ne pas avoir un "flash' blanc au démarrage
}
//============================================================================
// Fonction : Récpère dans la BDD la valeur de 'Specifique_client' dans
// TB_Configuration. sépare chaque champs (séparés par des ';').
// Met à jour la propriété globale 'shipheart_specifique_client'
// avec toutes les valeurs. Renvoi la première.
// Reçoit : Rien
// Retour : La valeur de 'shipheart_specifique_client[0]'
//============================================================================
function init_shipheart_specifique_client()
{
var req, res
// Récupération du paramètre 'TB_Configuration.Specifique_client'
req = 'SELECT Valeur FROM TB_Configuration WHERE Nom="Specifique_client" ';
res = bdd.requete_unique(req);
shipheart_specifique_client = res[0].split(';') // Mémorisation de l'ensemble du tableau
return(shipheart_specifique_client[0]); // Renvoi de la première valeur
}
//============================================================================
// Fonction : Récupère dans la BDD la valeur de 'Theme' dans
// TB_Configuration et met à jour la propriété globale 'shipheart_theme'.
// Reçoit : Rien
// Retour : Rien
//============================================================================
function init_shipheart_theme()
{
var req, res;
var nb;
// Chargement du thème demandé en config :
chargement_theme();
// Récupération du paramètre 'TB_Configuration.Fichier_image_accueil'
req = 'SELECT Valeur FROM TB_Configuration WHERE Nom="Fichier_image_accueil" ';
//console.log("Requête = [ " + req + "; ]");
nb = bdd.requete(req);
//console.log("NB = "+nb);
if (nb >0)
{
res = bdd.lecture_ligne();
if (!res.isEmpty)
{
shipheart_fichier_image_accueil = res[0];
console.log("Le fichier d'image d'accueil est : [" + shipheart_fichier_image_accueil + "]");
}
else
{
shipheart_fichier_image_accueil = "";
console.log("/!\ Erreur dans le traitementr de la requête de recherche de fichier d'image d'accueil a échouée. Le nom de fichier est mis à sa valeur par défaut...");
}
}
else
{
shipheart_fichier_image_accueil = "";
console.log("/!\ La requête de recherche de fichier d'image d'accueil a échouée. Le nom de fichier est mis à sa valeur par défaut...");
}
return;
}
//============================================================================
// Fonction : Récupère dans la BDD la valeur de 'Version_cm' dans
// TB_Systeme et met à jour la propriété globale 'shipheart_generation_shipbase'.
// Reçoit : Rien
// Retour : Rien
//============================================================================
function init_shipheart_version_cm()
{
var req, res
// Récupération du paramètre 'TB_Systeme.Version_cm'
req = 'SELECT Valeur FROM TB_Systeme WHERE Nom="Version_cm" ';
res = bdd.requete_unique(req);
if (res[0].indexOf("CM3+") !== -1) shipheart_generation_shipbase = shipheart_generation_shipbase_v1;
else if (res[0].indexOf("CM4") !== -1) shipheart_generation_shipbase = shipheart_generation_shipbase_v2;
else if (res[0].indexOf("CM5") !== -1) shipheart_generation_shipbase = shipheart_generation_shipbase_v3;
else shipheart_generation_shipbase = shipheart_generation_shipbase_inconnue;
console.log("Génération de ShipBase = " + shipheart_generation_shipbase);
return;
}
//=============================================================================================================
// Synchroniser répertoire docs ShipRS
//=============================================================================================================
function synchro_repertoires_shiprs()
{
var commande;
commande = "rsync -aPs --delete -e \"ssh -p 2022 -o StrictHostKeyChecking=no \" koriolan@" + AppInfo.adresse_shipbase + ":" + shipheart_repertoire_config + "/ " + shipheart_repertoire_config + "/";
systeme.commande_shell(commande, "");
}
}
+372
View File
@@ -0,0 +1,372 @@
//========================================================================================================================================================================
// HOME_SUNCY.QML
// Ce fichier est la page d'accueil pour le client Suncy. Il propose un clavier et un petit message d'accueil.
// La page est appelée depuis la page main.qml lorsque le paramètre 'shipheart_specifique_client[0]' est égal à 1
//========================================================================================================================================================================
import QtQuick 2.12
import qt.koriolan.shipheart 1.0
Shipheart_page {
id: page_home_suncy
// Mis en forme :
readonly property double pourcentage_largeur_colonne_gauche: 0.5
readonly property double espace_colonnes_principales: 20
readonly property double pourcentage_hauteur_ligne_haut: 0.15
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 double pourcentage_bandeau_bas_largeur_zone_retour: 1
readonly property int periode_init_raffraichissement_parametres: 1 // En ms
readonly property int periode_raffraichissement_parametres: 1000 // En ms
// Affichage :
property bool bouton_bandeau_bas_zone_retour_actif: false
property int protection_mode: 0
// Données :
property string code_saisi: ""
property string code_saisi_pattern: "" // Contient une suite de '*' dont le nombre correspond à la taille du code entré (avec un maxi)
property int max_taille_code_pattern: 10
// 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();
timer_raffraichissement_parametres.stop(); // Arret du timer une fois l'init faite car il n'y a rien à faire.
}
}
}
// Titre de la page :
Shipheart_titre_1 {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);
color: "transparent"
border.width: 1
border.color: couleur_gris_moyen
radius: shipheart_rayon_bouton
Text {
anchors {
verticalCenter: parent.verticalCenter
verticalCenterOffset: 12 // Les '*' sont naturellement décalées vers le haut de la ligne...
horizontalCenter: parent.horizontalCenter
}
font.pixelSize: 80
font.letterSpacing: 10
text: code_saisi_pattern;
color: couleur_blanc_casse
}
}
//-------------------------------------------------------
// 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 / 2);
color: "transparent"
Grid {
id: grille_clavier
width: parent.width * 0.6
height: parent.height * 0.8
columns: 3
rows: 4
spacing: 10
anchors {
verticalCenter: parent.verticalCenter
horizontalCenter: parent.horizontalCenter
}
Repeater {
model: modele_clavier
Rectangle {
width: (grille_clavier.width - ((grille_clavier.columns - 1) * grille_clavier.spacing)) / grille_clavier.columns
height: (grille_clavier.height - ((grille_clavier.rows - 1) * grille_clavier.spacing)) / grille_clavier.rows
color: shipheart_couleur_fond_bouton
radius: shipheart_rayon_bouton
Text {
anchors {
verticalCenter: parent.verticalCenter
horizontalCenter: parent.horizontalCenter
}
font.pixelSize: 50
text: libelle;
color: couleur_blanc_casse
visible: (libelle === "") ? false : true
}
Image {
source: icone
sourceSize.height: 30
anchors {
verticalCenter: parent.verticalCenter
horizontalCenter: parent.horizontalCenter
}
visible: (libelle === "") ? true : false
}
MouseArea {
anchors.fill: parent
onClicked: {
shipheart_clic();
switch(action)
{
case "backspace": code_saisi = code_saisi.slice(0, code_saisi.length - 1); break;
case "clear": code_saisi = ""; break;
default: code_saisi += libelle; break;
}
if (check_code() === 0) deverrouillage();
mise_a_jour_pattern();
}
}
}
}
ListModel {
id: modele_clavier
ListElement { libelle: "7"; action: ""; icone: "" }
ListElement { libelle: "8"; action: ""; icone: "" }
ListElement { libelle: "9"; action: ""; icone: "" }
ListElement { libelle: "4"; action: ""; icone: "" }
ListElement { libelle: "5"; action: ""; icone: "" }
ListElement { libelle: "6"; action: ""; icone: "" }
ListElement { libelle: "1"; action: ""; icone: "" }
ListElement { libelle: "2"; action: ""; icone: "" }
ListElement { libelle: "3"; action: ""; icone: "" }
ListElement { libelle: ""; action: "backspace"; icone: "/images/backspace.png" }
ListElement { libelle: "0"; action: ""; icone: "" }
ListElement { libelle: ""; action: "clear"; icone: "/images/double_fleche_gauche.png" }
}
}
}
//-------------------------------------------------------
// 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"
Image {
source: "/images/logo_suncy_concept.png"
sourceSize.height: parent.height * 0.2;
// sourceSize.width: bandeau_bas_largeur_zone_status_connexions
anchors.top: parent.top
anchors.horizontalCenter: parent.horizontalCenter
}
Text {
id: texte_home_suncy_fr
width: parent.width
anchors {
verticalCenter: parent.verticalCenter
horizontalCenter: parent.horizontalCenter
}
horizontalAlignment: Text.AlignHCenter
wrapMode: Text.Wrap
padding: 20
font.pixelSize: 20
text: qsTr("L'accès au bateau est protégé.\nMerci d'entrer votre code de déverrouillage pour y accéder...")
color: couleur_blanc_casse
visible: true
}
Text {
id: texte_home_suncy_en
width: parent.width
anchors {
verticalCenter: parent.verticalCenter
horizontalCenter: parent.horizontalCenter
verticalCenterOffset: 100
}
horizontalAlignment: Text.AlignHCenter
wrapMode: Text.Wrap
padding: 20
font.pixelSize: 20
font.italic: true
text: "Ship access is restricted.\nPlease enter your unlocking code to proceed...";
color: couleur_blanc_casse
visible: true
}
}
//-------------------------------------------------------
// 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
}
//===========================================================
// Economiseur : Doit être placé en fin de page pour fonctionner
//===========================================================
MouseArea {
anchors.fill: parent
preventStealing: true // Permet que cela fonctionne même si un élément flickable (notamment) soit derrière et "vole" le click
enabled: flag_economiseur_actif
onClicked: {
mise_en_veille(0);
}
}
//=============================================================================================================
// Init de la page
//=============================================================================================================
function init_page()
{
var req, res
mise_en_veille(0);
activation_moteur(0); // Dans le doute, on coupe l'activation du moteur dès le démarrage
//console.log("Mode actuel : " + protection_mode);
// Coupure de tous les circuits par défaut :
req = 'UPDATE TB_Circuits SET New_consigne="0" '
bdd.requete_unique(req);
req = 'SELECT Valeur FROM TB_Configuration WHERE Nom="Code_client" ';
res = bdd.requete_unique(req);
shipheart_code_client = res[0];
req = 'SELECT Valeur FROM TB_Configuration WHERE Nom="Max_taille_code" ';
res = bdd.requete_unique(req);
if (Number(res[0]) >= max_taille_code_pattern) max_taille_code_pattern = Number(res[0]) + 2;
}
//=============================================================================================================
// Déverrouillage
//=============================================================================================================
function deverrouillage()
{
var i;
//console.log("Le code est valide. La page est déverrouillée....")
if ((activation_moteur(100)) === -1) console.log("/!\ Aucune sortie valide d'activation du moteur a été trouvée")
code_saisi = "";
stackView.push("home.qml");
}
//=============================================================================================================
// Vérification du code_saisi vs le code
//=============================================================================================================
function check_code()
{
var code_saisi_encrypte;
code_saisi_encrypte = bdd.sha256(code_saisi);
//console.debug("Le code saisi est [" + code_saisi + "] et le code saisi encrypté est [" + code_saisi_encrypte + "]");
//console.debug("Le hash du code Client est [" + shipheart_code_client + "]");
if (shipheart_code_client.localeCompare(code_saisi_encrypte) === 0) return(0);
else return(-1);
}
//=============================================================================================================
// Mise à jour du pattern
//=============================================================================================================
function mise_a_jour_pattern()
{
var longueur;
var i;
longueur = code_saisi.length;
if (longueur > max_taille_code_pattern) longueur = max_taille_code_pattern;
code_saisi_pattern = "";
for (i=0; i<longueur; i++) code_saisi_pattern += '*';
}
//=====================================================================================
// Fonctions : Recherche dans le tableau 'shipheart_specifique_client' le paramètre contenant
// le n° de circuit à utiliser pour activer le moteur. Si le circuit existe,
// envoi la consigne reçue comme nouvelle consigne du circuit.
// Reçoit : La consigne (entre 0 et 100%)
// Retour : 0 si Ok (un circuit a été trouvé) ou -1 si pas de circuit trouvé.
//=====================================================================================
function activation_moteur(consigne)
{
var i;
var res;
if (consigne < 0) consigne = 0;
if (consigne > 100) consigne = 100;
// Acivation du moteur si un numéro de circuit valide pour le faire existe :
for (i = 1; i < shipheart_specifique_client.length; i ++) // Parcourt de tous les paramètres spécifiques au client (sauf le 1er qui est le n° de client et qui est déjà connu et testé)
{
var parametre = shipheart_specifique_client[i].split('='); // Séparation du nom du champ et de sa valeur
if (parametre[0] === 'id_circuit_activation_moteur') // Si le paramètre en cours est bien celui concernant l'ID du circuit d'activation du moteur
{
bdd.requete_unique("UPDATE TB_Circuits SET New_consigne=" + consigne + " WHERE Cle_primaire='" + Number(parametre[1]) + "'")
}
}
return (-1); // Cas où on n'a trouvé aucune sortie valide.
}
}
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Some files were not shown because too many files have changed in this diff Show More