Première tranche verticale : domaine circuit via DataProvider/API

This commit is contained in:
2026-08-14 07:29:00 +02:00
commit 4d29347aab
15 changed files with 2286 additions and 0 deletions
+22
View File
@@ -0,0 +1,22 @@
# 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
*~
+44
View File
@@ -0,0 +1,44 @@
cmake_minimum_required(VERSION 3.16)
project(shipheart_wasm LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
find_package(Qt6 REQUIRED COMPONENTS
Quick
Qml
QuickControls2
Network
)
qt_add_executable(shipheart_wasm
main.cpp
dataprovider.cpp
dataprovider.h
mock/mock_bdd.cpp
mock/mock_bdd.h
mock/mock_systeme.h
mock/mock_tbmesures.h
qml.qrc
)
target_link_libraries(shipheart_wasm PRIVATE
Qt6::Quick
Qt6::Qml
Qt6::QuickControls2
Qt6::Network
)
# Safari iOS a un plafond de mémoire par onglet bien plus bas que les valeurs
# par défaut de Qt pour wasm (cf §19.4 de la synthèse).
set_target_properties(shipheart_wasm PROPERTIES
QT_WASM_INITIAL_MEMORY 134217728 # 128 Mo
QT_WASM_MAXIMUM_MEMORY 536870912 # 512 Mo
)
install(TARGETS shipheart_wasm
DESTINATION ${CMAKE_INSTALL_PREFIX}
)
+60
View File
@@ -0,0 +1,60 @@
#include "dataprovider.h"
#include <QNetworkRequest>
#include <QNetworkReply>
#include <QJsonDocument>
#include <QJsonObject>
#include <QUrl>
#include <QUrlQuery>
DataProvider::DataProvider(QObject *parent) : QObject(parent)
{
}
void DataProvider::get_familles(const QString &type)
{
QUrl url(m_baseUrl + "/elements/familles");
QUrlQuery query;
query.addQueryItem("type", type);
url.setQuery(query);
auto reply = m_manager.get(QNetworkRequest(url));
connect(reply, &QNetworkReply::finished, this, [this, reply]() {
reply->deleteLater();
if (reply->error() != QNetworkReply::NoError) {
emit erreur(reply->errorString());
return;
}
const auto doc = QJsonDocument::fromJson(reply->readAll());
const auto obj = doc.object();
if (obj["status"].toString() != "ok") {
emit erreur(obj["message"].toString());
return;
}
emit famillesRecues(obj["familles"].toArray());
});
}
void DataProvider::get_elements(const QString &type, int famille)
{
QUrl url(m_baseUrl + "/elements");
QUrlQuery query;
query.addQueryItem("type", type);
query.addQueryItem("famille", QString::number(famille));
url.setQuery(query);
auto reply = m_manager.get(QNetworkRequest(url));
connect(reply, &QNetworkReply::finished, this, [this, reply]() {
reply->deleteLater();
if (reply->error() != QNetworkReply::NoError) {
emit erreur(reply->errorString());
return;
}
const auto doc = QJsonDocument::fromJson(reply->readAll());
const auto obj = doc.object();
if (obj["status"].toString() != "ok") {
emit erreur(obj["message"].toString());
return;
}
emit elementsRecus(obj["elements"].toArray());
});
}
+38
View File
@@ -0,0 +1,38 @@
#pragma once
#include <QObject>
#include <QNetworkAccessManager>
#include <QJsonArray>
// ============================================================================
// DataProvider - couche d'accès aux données du client WASM (cas 2/3).
// ----------------------------------------------------------------------------
// Remplace, pour les domaines déjà portés, les appels bdd.* directs du cas 1
// (qui reste inchangé, cf décision §21.1 de la synthèse). Toute la logique
// HTTP/JSON est encapsulée ici - le QML ne voit que des signaux avec des
// tableaux déjà prêts à l'emploi.
//
// Portée actuelle : uniquement le domaine "circuit" (première tranche
// verticale). À généraliser aux 3 autres domaines une fois ce pattern validé.
// ============================================================================
class DataProvider : public QObject
{
Q_OBJECT
public:
explicit DataProvider(QObject *parent = nullptr);
// TODO : rendre l'adresse de base configurable (IP locale du CM5 en cas 2,
// domaine VPS en cas 3 - cf QR code d'accès, §"Architecture cible"/Cas 2-3
// de la synthèse) - en dur sur l'IP locale pour cette première tranche.
Q_INVOKABLE void get_familles(const QString &type);
Q_INVOKABLE void get_elements(const QString &type, int famille);
signals:
void famillesRecues(const QJsonArray &familles);
void elementsRecus(const QJsonArray &elements);
void erreur(const QString &message);
private:
QNetworkAccessManager m_manager;
QString m_baseUrl = QStringLiteral("http://192.168.1.220:8080");
};
+61
View File
@@ -0,0 +1,61 @@
#include <QGuiApplication>
#include <QCursor>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include <QtQml>
#include "mock/mock_bdd.h"
#include "mock/mock_systeme.h"
#include "mock/mock_tbmesures.h"
#include "dataprovider.h"
// ============================================================================
// main.cpp - shipheart_wasm
// ----------------------------------------------------------------------------
// Dérivé du main.cpp du POC (§19). Ajout : enregistrement de DataProvider,
// qui vient s'ajouter aux mocks existants (toujours nécessaires pour
// Shipheart_page.qml/main.qml, qui appellent encore bdd.*/systeme.* pour les
// fonctions non liées aux circuits - synchro, veille, etc., cf §19.4).
// ============================================================================
static QJSValue appVersionSingletonProvider(QQmlEngine *engine, QJSEngine *scriptEngine)
{
Q_UNUSED(engine)
QJSValue appInfo = scriptEngine->newObject();
appInfo.setProperty("adresse_shipbase", QString());
appInfo.setProperty("etat_connexion_bdd", 1);
appInfo.setProperty("version", QStringLiteral("3.0.0-wasm"));
appInfo.setProperty("type_installation", QStringLiteral("0"));
appInfo.setProperty("taille_ecran", QString());
return appInfo;
}
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
app.setOverrideCursor(Qt::BlankCursor);
QGuiApplication::setApplicationName("ShipHeart_Wasm");
QGuiApplication::setApplicationVersion("3.0.0-wasm");
qmlRegisterType<Bdd>("qt.koriolan.shipheart", 1, 0, "Bdd");
qmlRegisterType<TbMesuresModel>("qt.koriolan.shipheart", 1, 0, "TbMesuresModel");
qmlRegisterType<Systeme>("qt.koriolan.shipheart", 1, 0, "Systeme");
qmlRegisterType<DataProvider>("qt.koriolan.shipheart", 1, 0, "DataProvider");
qmlRegisterSingletonType("qt.koriolan.shipheart", 1, 0, "AppInfo", appVersionSingletonProvider);
auto engine = new QQmlApplicationEngine(&app);
engine->setObjectName("currentEngine");
const QUrl url(QStringLiteral("qrc:/main.qml"));
QObject::connect(engine, &QQmlApplicationEngine::objectCreated,
&app, [url](QObject *obj, const QUrl &objUrl)
{
if (!obj && url == objUrl) QCoreApplication::exit(-1);
}, Qt::QueuedConnection);
engine->load(url);
return app.exec();
}
+46
View File
@@ -0,0 +1,46 @@
#include "mock_bdd.h"
// ============================================================================
// Valeurs fixes du POC - à modifier ici si besoin d'un autre scénario de test.
// ============================================================================
QVariant Bdd::requete_unique(const QString &req)
{
// --- Lectures (SELECT) utilisées par le chemin main.qml -> home_attente.qml -> home_accueil.qml ---
if (req.contains("Nom=\"Specifique_client\""))
return QVariantList{ "0" }; // 0 = shipheart_specifique_client_tous -> mène à home_accueil.qml
if (req.contains("Nom=\"Theme\""))
return QVariantList{ "0" }; // Thème 0 = "Original"
if (req.contains("Nom=\"Version_cm\""))
return QVariantList{ "CM5" }; // Doit contenir "CM5" -> shipheart_generation_shipbase_v3
if (req.contains("Nom=\"Nom_du_bateau\""))
return QVariantList{ "Bateau de test (POC WASM)" };
if (req.contains("Validation_economiseur"))
return QVariantList{ "0" }; // Économiseur désactivé par défaut pour ce POC
// --- Écritures (UPDATE) : Veille, Synchro, New_bip, Theme (cas dépassement d'index) ---
// Valeur de retour jamais utilisée par l'appelant dans ces cas -> valeur neutre.
return QVariantList{ QString() };
}
int Bdd::requete(const QString &req)
{
m_derniere_requete = req;
if (req.contains("Nom=\"Fichier_image_accueil\""))
return 1; // 1 ligne de résultat "trouvée"
return 0;
}
QVariant Bdd::lecture_ligne()
{
if (m_derniere_requete.contains("Nom=\"Fichier_image_accueil\""))
return QVariantList{ "" }; // "" -> l'image par défaut est utilisée (cf home_accueil.qml)
return QVariantList{};
}
+33
View File
@@ -0,0 +1,33 @@
#pragma once
#include <QObject>
#include <QString>
#include <QVariant>
#include <QVariantList>
// ============================================================================
// Bdd (MOCK) - POC WebAssembly
// ----------------------------------------------------------------------------
// Bouchon de la classe Bdd réelle (bdd.h/bdd.cpp non disponibles à ce stade).
// Ne se connecte à aucune base de données : reconnaît le texte de chaque
// requête reçue et renvoie une valeur fixe cohérente avec le comportement
// attendu par main.qml / Shipheart_page.qml / home_attente.qml / home_accueil.qml.
//
// Signatures alignées sur l'usage observé dans ces 4 fichiers QML :
// - requete_unique(req) -> une seule valeur de résultat (SELECT ou UPDATE)
// - requete(req)/lecture_ligne() -> requête à plusieurs lignes potentielles
// (ici, une seule ligne utilisée : "Fichier_image_accueil")
// ============================================================================
class Bdd : public QObject
{
Q_OBJECT
public:
explicit Bdd(QObject *parent = nullptr) : QObject(parent) {}
Q_INVOKABLE QVariant requete_unique(const QString &req);
Q_INVOKABLE int requete(const QString &req);
Q_INVOKABLE QVariant lecture_ligne();
private:
QString m_derniere_requete;
};
+26
View File
@@ -0,0 +1,26 @@
#pragma once
#include <QObject>
#include <QString>
// ============================================================================
// Systeme (MOCK) - POC WebAssembly
// ----------------------------------------------------------------------------
// Bouchon de la classe Systeme réelle (systeme.h/systeme.cpp non disponibles
// à ce stade). commande_shell() n'est appelée, dans les fichiers du POC, que
// pour les installations "écran déporté" (synchro_repertoires_shiprs() dans
// home_attente.qml) - hors scope du chemin ShipBase standard testé ici.
// Bouchon neutre par sécurité, au cas où il serait tout de même invoqué.
// ============================================================================
class Systeme : public QObject
{
Q_OBJECT
public:
explicit Systeme(QObject *parent = nullptr) : QObject(parent) {}
Q_INVOKABLE QString commande_shell(const QString &commande, const QString &fichier_sortie)
{
Q_UNUSED(commande)
Q_UNUSED(fichier_sortie)
return QString();
}
};
+17
View File
@@ -0,0 +1,17 @@
#pragma once
#include <QObject>
// ============================================================================
// TbMesuresModel (MOCK) - POC WebAssembly
// ----------------------------------------------------------------------------
// Bouchon minimal : dans le périmètre de ce POC (main.qml jusqu'à
// home_accueil.qml), ce modèle n'est instancié qu'au niveau racine
// (property TbMesuresModel tbmesuresModel : TbMesuresModel{}) et jamais
// utilisé au-delà - un QObject vide suffit à ce stade.
// ============================================================================
class TbMesuresModel : public QObject
{
Q_OBJECT
public:
explicit TbMesuresModel(QObject *parent = nullptr) : QObject(parent) {}
};
+9
View File
@@ -0,0 +1,9 @@
<RCC>
<qresource prefix="/">
<file alias="main.qml">qml/main.qml</file>
<file alias="Shipheart_page.qml">qml/Shipheart_page.qml</file>
<file alias="home_attente.qml">qml/home_attente.qml</file>
<file alias="home_accueil.qml">qml/home_accueil.qml</file>
<file alias="home.qml">qml/home.qml</file>
</qresource>
</RCC>
+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 + "']");
}
}
+183
View File
@@ -0,0 +1,183 @@
import QtQuick 2.12
import QtQuick.Controls 2.12
import QtQuick.Layouts 1.12
import qt.koriolan.shipheart 1.0
// ============================================================================
// home.qml (EMBRYON - POC WASM production, domaine circuit uniquement)
// ----------------------------------------------------------------------------
// Version très réduite du vrai home.qml (~3880 lignes, tous domaines) :
// couvre uniquement l'affichage des circuits, via l'API réelle (DataProvider)
// au lieu de bdd.*. Objectif : valider la chaîne complète (HTTP réel, JSON,
// affichage QML) sur un seul domaine avant de généraliser.
//
// Simplifications volontaires par rapport au vrai home.qml :
// - Lecture seule (pas de changement de consigne au clic - l'endpoint
// d'écriture n'est pas encore construit côté API, cf synthèse §20.7)
// - Un seul domaine affiché (pas de batteries/réservoirs/capteurs/pages
// personnalisées/messages/réveils/minuteur)
// - Délégué d'affichage simplifié (pas Shipheart_bouton_circuit, qui
// embarque des actions d'écriture non encore branchées)
// - mise_a_jour_onglets_circuits() n'existe plus : l'API renvoie déjà
// l'état agrégé par famille dans /elements/familles (cf §20.7/21.5)
// ============================================================================
Shipheart_page {
id: page_accueil
readonly property int deport_haut_masquage_onglets: 20
readonly property int rayon_onglets: 20
property int nombre_colonnes_affichage_circuits: 2
property int nombre_circuits_affiches_sur_une_colonne: 4
property int id_groupe_circuits: 0
ListModel { id: modele_onglets_circuits }
ListModel { id: modele_circuits }
property DataProvider dataProvider: DataProvider {}
Connections {
target: dataProvider
function onFamillesRecues(familles) {
modele_onglets_circuits.clear();
for (var i = 0; i < familles.length; i++) {
var f = familles[i];
modele_onglets_circuits.append({
"id": f.id,
"libelle": f.nom,
"etat": f.etat,
"actif": (f.id === id_groupe_circuits)
});
// Sélectionne la première famille par défaut si aucune n'est choisie :
if (id_groupe_circuits === 0 && i === 0) {
id_groupe_circuits = f.id;
dataProvider.get_elements("circuit", id_groupe_circuits);
}
}
}
function onElementsRecus(elements) {
modele_circuits.clear();
for (var j = 0; j < elements.length; j++) {
modele_circuits.append(elements[j]);
}
}
function onErreur(message) {
console.log("DataProvider erreur : " + message);
}
}
Component.onCompleted: {
dataProvider.get_familles("circuit");
}
// Rafraîchissement périodique (remplace mise_a_jour_circuits(), appelée chaque seconde côté natif) :
Timer {
interval: 1000
running: true
repeat: true
onTriggered: {
if (id_groupe_circuits > 0) dataProvider.get_elements("circuit", id_groupe_circuits);
}
}
ColumnLayout {
anchors.fill: parent
spacing: 10
//-----------------------------------------------------------
// Onglets (familles) :
Rectangle {
id: onglets_circuits
Layout.fillWidth: true
height: shipheart_hauteur_bandeau_haut
color: "transparent"
visible: modele_onglets_circuits.count > 1
GridView {
id: grille_onglets_circuits
anchors.fill: parent
cellWidth: width / Math.max(1, Math.min(modele_onglets_circuits.count, 5))
cellHeight: shipheart_hauteur_bandeau_haut
model: modele_onglets_circuits
clip: true
delegate: Rectangle {
width: grille_onglets_circuits.cellWidth - 2
height: grille_onglets_circuits.cellHeight
radius: rayon_onglets
color: model.actif ? shipheart_couleur_fond_bouton_actif : shipheart_couleur_fond_bouton
Text {
text: model.libelle
anchors.centerIn: parent
color: couleur_blanc_casse
font.pixelSize: 14
font.bold: model.actif
}
MouseArea {
anchors.fill: parent
onClicked: {
id_groupe_circuits = model.id;
for (var k = 0; k < modele_onglets_circuits.count; k++) {
modele_onglets_circuits.set(k, {"actif": (modele_onglets_circuits.get(k).id === model.id)});
}
dataProvider.get_elements("circuit", id_groupe_circuits);
}
}
}
}
}
//-----------------------------------------------------------
// Circuits de la famille sélectionnée :
GridView {
id: circuits_grid
Layout.fillWidth: true
Layout.fillHeight: true
cellWidth: width / nombre_colonnes_affichage_circuits
cellHeight: height / nombre_circuits_affiches_sur_une_colonne
model: modele_circuits
clip: true
delegate: Rectangle {
width: circuits_grid.cellWidth - 4
height: circuits_grid.cellHeight - 4
radius: 10
color: shipheart_couleur_fond_bouton
border.color: (model.etat === 2) ? "red" : (model.etat === 1) ? "orange" : "transparent"
border.width: 2
Column {
anchors.centerIn: parent
spacing: 4
Text {
text: model.nom
color: couleur_blanc_casse
font.pixelSize: 14
font.bold: true
anchors.horizontalCenter: parent.horizontalCenter
}
Text {
text: model.tension.toFixed(1) + " V " + model.intensite.toFixed(2) + " A"
color: couleur_blanc_casse
font.pixelSize: 12
anchors.horizontalCenter: parent.horizontalCenter
}
}
}
Label {
anchors.centerIn: parent
visible: circuits_grid.count === 0
text: qsTr("Aucun circuit dans ce groupe.")
color: couleur_rouge_transparent
}
}
}
}
+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");
}
}
+285
View File
@@ -0,0 +1,285 @@
//========================================================================================================================================================================
// 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_largeur_page = Qt.application.screens[0].width
shipheart_hauteur_page = Qt.application.screens[0].height
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, "");
}
}
+1069
View File
File diff suppressed because it is too large Load Diff