Endpoint /mesures généralisé aux 4 domaines (capteurs, réservoirs, circuits, batteries), valeur actuelle réelle + points de bord synthétiques
This commit is contained in:
@@ -9,6 +9,8 @@
|
||||
#include <QJsonArray>
|
||||
#include <QJsonDocument>
|
||||
#include <QUrlQuery>
|
||||
#include <QDateTime>
|
||||
#include <QMap>
|
||||
#include <QDebug>
|
||||
|
||||
// ============================================================================
|
||||
@@ -170,10 +172,10 @@ int main(int argc, char *argv[])
|
||||
const QString type = query.queryItemValue("type");
|
||||
|
||||
int typeSource;
|
||||
if (type == "circuit") typeSource = TYPE_SOURCE_CIRCUIT;
|
||||
else if (type == "batterie") typeSource = TYPE_SOURCE_BATTERIE;
|
||||
else if (type == "capteur") typeSource = TYPE_SOURCE_CAPTEUR;
|
||||
else if (type == "reservoir") typeSource = TYPE_SOURCE_RESERVOIR;
|
||||
if (type == "circuits") typeSource = TYPE_SOURCE_CIRCUIT;
|
||||
else if (type == "batteries") typeSource = TYPE_SOURCE_BATTERIE;
|
||||
else if (type == "capteurs") typeSource = TYPE_SOURCE_CAPTEUR;
|
||||
else if (type == "reservoirs") typeSource = TYPE_SOURCE_RESERVOIR;
|
||||
else {
|
||||
QJsonObject err;
|
||||
err["status"] = "erreur";
|
||||
@@ -189,7 +191,7 @@ int main(int argc, char *argv[])
|
||||
const QString nom = q1.value(1).toString();
|
||||
int etat = 0;
|
||||
|
||||
if (type == "circuit") {
|
||||
if (type == "circuits") {
|
||||
// Alarmes (Events) liées à cette famille :
|
||||
QSqlQuery q2;
|
||||
q2.prepare("SELECT COUNT(*) FROM TB_Events WHERE Type_source=:type AND Validite=1 AND Alarme_source>0 "
|
||||
@@ -221,7 +223,7 @@ int main(int argc, char *argv[])
|
||||
etat = 1;
|
||||
}
|
||||
}
|
||||
} else if (type == "batterie") {
|
||||
} else if (type == "batteries") {
|
||||
// Règle plus simple que les circuits (cf §20.4/§21.8 synthèse) :
|
||||
// rouge si Etat_systeme en défaut sur une batterie de la famille,
|
||||
// sinon orange si au moins une alarme (Events), sinon vert.
|
||||
@@ -243,7 +245,7 @@ int main(int argc, char *argv[])
|
||||
while (q3.next()) {
|
||||
if (q3.value(0).toInt() != 0) etat = 2;
|
||||
}
|
||||
} else if (type == "capteur") {
|
||||
} else if (type == "capteurs") {
|
||||
// Période d'obsolescence NMEA (TB_Configuration) :
|
||||
int periodeObsolescenceNmea = 0;
|
||||
{
|
||||
@@ -278,7 +280,7 @@ int main(int argc, char *argv[])
|
||||
if (age > periodeObsolescenceNmea && etat < 1) etat = 1;
|
||||
if ((etatCapteur > 0 || etatSysteme != 0) && etat < 2) etat = 2;
|
||||
}
|
||||
} else if (type == "reservoir") {
|
||||
} else if (type == "reservoirs") {
|
||||
// Alarmes (Events) liées à cette famille :
|
||||
QSqlQuery q2;
|
||||
q2.prepare("SELECT COUNT(*) FROM TB_Events WHERE Type_source=:type AND Validite=1 AND Alarme_source>0 "
|
||||
@@ -326,7 +328,7 @@ int main(int argc, char *argv[])
|
||||
const QString type = query.queryItemValue("type");
|
||||
const QString familleStr = query.queryItemValue("famille");
|
||||
|
||||
if (type != "circuit" && type != "batterie" && type != "capteur" && type != "reservoir") {
|
||||
if (type != "circuits" && type != "batteries" && type != "capteurs" && type != "reservoirs") {
|
||||
QJsonObject err;
|
||||
err["status"] = "erreur";
|
||||
err["message"] = "type non supporté (circuit, batterie, capteur, reservoir)";
|
||||
@@ -344,7 +346,7 @@ int main(int argc, char *argv[])
|
||||
|
||||
QJsonArray elements;
|
||||
|
||||
if (type == "circuit") {
|
||||
if (type == "circuits") {
|
||||
QSqlQuery q;
|
||||
q.prepare(
|
||||
"SELECT Cle_primaire, Etat_systeme, Nom, Etat_commande_carte, Mode_commande, Etat, Consigne, Old_consigne, "
|
||||
@@ -383,7 +385,7 @@ int main(int argc, char *argv[])
|
||||
obj["etat_events"] = q.value(16).toInt();
|
||||
elements.append(obj);
|
||||
}
|
||||
} else if (type == "batterie") {
|
||||
} else if (type == "batteries") {
|
||||
// Rappel : le champ "etat" retourné ici est le NOMBRE D'ALARMES,
|
||||
// pas un code d'état comme pour les circuits - fidèle au modèle
|
||||
// natif existant (modele_batteries), pas une incohérence introduite ici.
|
||||
@@ -411,7 +413,7 @@ int main(int argc, char *argv[])
|
||||
obj["etat"] = q.value(7).toInt();
|
||||
elements.append(obj);
|
||||
}
|
||||
} else if (type == "capteur") {
|
||||
} else if (type == "capteurs") {
|
||||
// Préférences d'unité du bateau (TB_Configuration) :
|
||||
int uniteVolume = 0, uniteTemperature = 0, periodeObsolescenceNmea = 0;
|
||||
{
|
||||
@@ -524,7 +526,7 @@ int main(int argc, char *argv[])
|
||||
obj["obsolete"] = (typeEntrees == TYPE_ENTREES_NMEA && age > periodeObsolescenceNmea);
|
||||
elements.append(obj);
|
||||
}
|
||||
} else if (type == "reservoir") {
|
||||
} else if (type == "reservoirs") {
|
||||
QSqlQuery q;
|
||||
q.prepare(
|
||||
"SELECT Cle_primaire, Nom, Etat_systeme, Niveau, Capacite, Logique, "
|
||||
@@ -644,6 +646,358 @@ int main(int argc, char *argv[])
|
||||
return resp;
|
||||
});
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// GET /mesures?type=<capteurs|reservoirs>&id=<id>&periode=<0-3>
|
||||
// Historique pour le graphique de la page de détail (0=1h, 1=24h,
|
||||
// 2=7j, 3=30j) - regroupement TOUJOURS calculé côté serveur (jamais de
|
||||
// donnée brute non agrégée transmise, cf §20.6/§21.6 synthèse).
|
||||
// Capteurs : id_variable résolu depuis la config de l'élément lui-même.
|
||||
// Réservoirs : id_variable résolu par CODE FIXE ("Res_niveau" dans
|
||||
// TB_Variables) - le niveau est un concept universel, pas une config
|
||||
// propre à chaque réservoir comme pour un capteur.
|
||||
// Circuits/batteries (sélection de variable côté client) à venir.
|
||||
// ------------------------------------------------------------------
|
||||
server.route("/mesures", [](const QHttpServerRequest &request) {
|
||||
QUrlQuery query(request.query());
|
||||
const QString type = query.queryItemValue("type");
|
||||
const QString idStr = query.queryItemValue("id");
|
||||
const QString periodeStr = query.queryItemValue("periode");
|
||||
|
||||
if (type != "capteurs" && type != "reservoirs" && type != "circuits" && type != "batteries") {
|
||||
QJsonObject err;
|
||||
err["status"] = "erreur";
|
||||
err["message"] = "type non supporté pour l'instant (capteurs, reservoirs, circuits, batteries)";
|
||||
return withCors(QHttpServerResponse(QJsonDocument(err).toJson(), QHttpServerResponder::StatusCode::NotImplemented));
|
||||
}
|
||||
|
||||
bool okId = false, okPeriode = false;
|
||||
const int id = idStr.toInt(&okId);
|
||||
const int periode = periodeStr.toInt(&okPeriode);
|
||||
if (!okId || !okPeriode || periode < 0 || periode > 3) {
|
||||
QJsonObject err;
|
||||
err["status"] = "erreur";
|
||||
err["message"] = "paramètres 'id'/'periode' manquants ou invalides (periode: 0=1h, 1=24h, 2=7j, 3=30j)";
|
||||
return withCors(QHttpServerResponse(QJsonDocument(err).toJson(), QHttpServerResponder::StatusCode::BadRequest));
|
||||
}
|
||||
|
||||
// Durée de chaque période, en secondes :
|
||||
static const qint64 DUREES_PERIODE[4] = { 3600, 86400, 604800, 2592000 }; // 1h, 24h, 7j, 30j
|
||||
const qint64 dureeSecondes = DUREES_PERIODE[periode];
|
||||
|
||||
const QDateTime dateFin = QDateTime::currentDateTime();
|
||||
const QDateTime dateDebut = dateFin.addSecs(-dureeSecondes);
|
||||
|
||||
// Regroupement calculé pour viser un nombre de points raisonnable à
|
||||
// l'affichage, quelle que soit la période demandée - valeur à ajuster
|
||||
// visuellement une fois le graphique réellement affiché (pas un
|
||||
// chiffre figé arbitrairement bas, cf discussion §20.6).
|
||||
static const int POINTS_CIBLE = 200;
|
||||
const int regroupement = qMax(1, (int)(dureeSecondes / POINTS_CIBLE));
|
||||
|
||||
// Résolution de l'id_variable, de la précision d'affichage, et du nom
|
||||
// de table de mesures - selon le domaine :
|
||||
int idVariable = 0;
|
||||
int precision = 1;
|
||||
QString nomTable;
|
||||
QString variableCircuit; // Utilisée aussi plus loin (valeur actuelle) si type == "circuits"
|
||||
|
||||
if (type == "capteurs") {
|
||||
nomTable = "TB_Mesures_capteurs";
|
||||
QSqlQuery qc;
|
||||
qc.prepare("SELECT ID_Variable, Precision_affichage_mesures FROM TB_Capteurs WHERE Cle_primaire=:id");
|
||||
qc.bindValue(":id", id);
|
||||
qc.exec();
|
||||
if (!qc.next()) {
|
||||
QJsonObject err;
|
||||
err["status"] = "erreur";
|
||||
err["message"] = "capteur introuvable";
|
||||
return withCors(QHttpServerResponse(QJsonDocument(err).toJson(), QHttpServerResponder::StatusCode::NotFound));
|
||||
}
|
||||
idVariable = qc.value(0).toInt();
|
||||
precision = qc.value(1).toInt();
|
||||
} else if (type == "reservoirs") {
|
||||
nomTable = "TB_Mesures_reservoirs";
|
||||
|
||||
QSqlQuery qExiste;
|
||||
qExiste.prepare("SELECT Cle_primaire FROM TB_Reservoirs WHERE Cle_primaire=:id");
|
||||
qExiste.bindValue(":id", id);
|
||||
qExiste.exec();
|
||||
if (!qExiste.next()) {
|
||||
QJsonObject err;
|
||||
err["status"] = "erreur";
|
||||
err["message"] = "réservoir introuvable";
|
||||
return withCors(QHttpServerResponse(QJsonDocument(err).toJson(), QHttpServerResponder::StatusCode::NotFound));
|
||||
}
|
||||
|
||||
QSqlQuery qv;
|
||||
qv.prepare("SELECT Cle_primaire FROM TB_Variables WHERE Code='Res_niveau'");
|
||||
qv.exec();
|
||||
if (!qv.next()) {
|
||||
QJsonObject err;
|
||||
err["status"] = "erreur";
|
||||
err["message"] = "configuration serveur incomplète (variable 'Res_niveau' introuvable)";
|
||||
return withCors(QHttpServerResponse(QJsonDocument(err).toJson(), QHttpServerResponder::StatusCode::InternalServerError));
|
||||
}
|
||||
idVariable = qv.value(0).toInt();
|
||||
// Pas de colonne de précision dédiée pour les réservoirs (à la
|
||||
// différence des capteurs) - un pourcentage à 1 décimale est une
|
||||
// valeur par défaut raisonnable.
|
||||
precision = 1;
|
||||
} else if (type == "circuits") {
|
||||
nomTable = "TB_Mesures_circuits";
|
||||
|
||||
// Circuits : le client choisit QUELLE variable tracer (contrairement
|
||||
// à réservoirs/capteurs, à valeur fixe) :
|
||||
variableCircuit = query.queryItemValue("variable");
|
||||
if (variableCircuit != "consigne" && variableCircuit != "tension"
|
||||
&& variableCircuit != "intensite" && variableCircuit != "frequence") {
|
||||
QJsonObject err;
|
||||
err["status"] = "erreur";
|
||||
err["message"] = "paramètre 'variable' manquant ou invalide (consigne, tension, intensite, frequence)";
|
||||
return withCors(QHttpServerResponse(QJsonDocument(err).toJson(), QHttpServerResponder::StatusCode::BadRequest));
|
||||
}
|
||||
|
||||
// Résolution du type de module du circuit - nécessaire car SP4AC
|
||||
// utilise "tension_secteur" au lieu de "tension", et la précision
|
||||
// d'affichage diffère selon le type de carte (cf circuit.qml natif) :
|
||||
QSqlQuery qMod;
|
||||
qMod.prepare(
|
||||
"SELECT (SELECT Type FROM TB_Modules_config WHERE Adresse_k_bus=TB_Circuits.Adresse_module) "
|
||||
"FROM TB_Circuits WHERE Cle_primaire=:id"
|
||||
);
|
||||
qMod.bindValue(":id", id);
|
||||
qMod.exec();
|
||||
if (!qMod.next()) {
|
||||
QJsonObject err;
|
||||
err["status"] = "erreur";
|
||||
err["message"] = "circuit introuvable";
|
||||
return withCors(QHttpServerResponse(QJsonDocument(err).toJson(), QHttpServerResponder::StatusCode::NotFound));
|
||||
}
|
||||
const int typeModule = qMod.value(0).toInt();
|
||||
static const int TYPE_MODULE_SP4AC = 3;
|
||||
const bool estSP4AC = (typeModule == TYPE_MODULE_SP4AC);
|
||||
|
||||
QString codeVariable;
|
||||
if (variableCircuit == "consigne") { codeVariable = "Circ_consigne"; precision = 0; }
|
||||
else if (variableCircuit == "intensite") { codeVariable = "Circ_courant"; precision = estSP4AC ? 1 : 2; }
|
||||
else if (variableCircuit == "frequence") { codeVariable = "Circ_frequence"; precision = 1; }
|
||||
else { codeVariable = estSP4AC ? "Circ_tension_secteur" : "Circ_tension"; precision = estSP4AC ? 0 : 2; }
|
||||
|
||||
QSqlQuery qv2;
|
||||
qv2.prepare("SELECT Cle_primaire FROM TB_Variables WHERE Code=:code");
|
||||
qv2.bindValue(":code", codeVariable);
|
||||
qv2.exec();
|
||||
if (!qv2.next()) {
|
||||
QJsonObject err;
|
||||
err["status"] = "erreur";
|
||||
err["message"] = "configuration serveur incomplète (variable '" + codeVariable + "' introuvable)";
|
||||
return withCors(QHttpServerResponse(QJsonDocument(err).toJson(), QHttpServerResponder::StatusCode::InternalServerError));
|
||||
}
|
||||
idVariable = qv2.value(0).toInt();
|
||||
} else { // batteries
|
||||
nomTable = "TB_Mesures_batteries";
|
||||
|
||||
// Batteries : le client choisit QUELLE variable tracer, comme pour
|
||||
// les circuits. Valeur par défaut "soc" si non précisée (le vrai
|
||||
// défaut natif est le comparatif d'énergie, hors périmètre ici).
|
||||
variableCircuit = query.queryItemValue("variable");
|
||||
if (variableCircuit.isEmpty()) variableCircuit = "soc";
|
||||
|
||||
static const QMap<QString, QString> CODES_BATTERIE = {
|
||||
{"soc", "Batt_soc"}, {"tension", "Batt_tension"}, {"temperature", "Batt_temperature"},
|
||||
{"courant_differentiel", "Batt_courant_differentiel"}, {"courant_charge", "Batt_courant_charge"},
|
||||
{"courant_decharge", "Batt_courant_decharge"}, {"soh", "Batt_soh"}, {"autonomie", "Batt_autonomie"}
|
||||
};
|
||||
static const QMap<QString, int> PRECISIONS_BATTERIE = {
|
||||
{"soc", 1}, {"tension", 2}, {"temperature", 1}, {"courant_differentiel", 2},
|
||||
{"courant_charge", 2}, {"courant_decharge", 2}, {"soh", 1}, {"autonomie", 1}
|
||||
};
|
||||
|
||||
if (!CODES_BATTERIE.contains(variableCircuit)) {
|
||||
QJsonObject err;
|
||||
err["status"] = "erreur";
|
||||
err["message"] = "paramètre 'variable' invalide (soc, tension, temperature, courant_differentiel, courant_charge, courant_decharge, soh, autonomie)";
|
||||
return withCors(QHttpServerResponse(QJsonDocument(err).toJson(), QHttpServerResponder::StatusCode::BadRequest));
|
||||
}
|
||||
precision = PRECISIONS_BATTERIE.value(variableCircuit);
|
||||
|
||||
QSqlQuery qv3;
|
||||
qv3.prepare("SELECT Cle_primaire FROM TB_Variables WHERE Code=:code");
|
||||
qv3.bindValue(":code", CODES_BATTERIE.value(variableCircuit));
|
||||
qv3.exec();
|
||||
if (!qv3.next()) {
|
||||
QJsonObject err;
|
||||
err["status"] = "erreur";
|
||||
err["message"] = "configuration serveur incomplète (variable '" + CODES_BATTERIE.value(variableCircuit) + "' introuvable)";
|
||||
return withCors(QHttpServerResponse(QJsonDocument(err).toJson(), QHttpServerResponder::StatusCode::InternalServerError));
|
||||
}
|
||||
idVariable = qv3.value(0).toInt();
|
||||
}
|
||||
|
||||
QString uniteBrute;
|
||||
int typeUnite = 0;
|
||||
{
|
||||
QSqlQuery qv;
|
||||
qv.prepare("SELECT Unite, Conversion_unite FROM TB_Variables WHERE Cle_primaire=:id");
|
||||
qv.bindValue(":id", idVariable);
|
||||
qv.exec();
|
||||
if (qv.next()) {
|
||||
uniteBrute = qv.value(0).toString();
|
||||
typeUnite = qv.value(1).toInt();
|
||||
}
|
||||
}
|
||||
|
||||
int uniteVolume = 0, uniteTemperature = 0;
|
||||
{
|
||||
QSqlQuery q1("SELECT Valeur FROM TB_Configuration WHERE Nom='Unite_volume'");
|
||||
if (q1.next()) uniteVolume = q1.value(0).toInt();
|
||||
}
|
||||
{
|
||||
QSqlQuery q2("SELECT Valeur FROM TB_Configuration WHERE Nom='Unite_temperature'");
|
||||
if (q2.next()) uniteTemperature = q2.value(0).toInt();
|
||||
}
|
||||
|
||||
QString symboleUnite = uniteBrute;
|
||||
if (typeUnite == TYPE_UNITE_VOLUME) symboleUnite = symbole_unite_volume(uniteVolume);
|
||||
else if (typeUnite == TYPE_UNITE_TEMPERATURE) symboleUnite = symbole_unite_temperature(uniteTemperature);
|
||||
|
||||
// Fonction de conversion, réutilisée à plusieurs endroits ci-dessous :
|
||||
auto convertir = [&](double brut) {
|
||||
if (typeUnite == TYPE_UNITE_VOLUME) return conversion_volume(brut, uniteVolume);
|
||||
if (typeUnite == TYPE_UNITE_TEMPERATURE) return conversion_temperature(brut, uniteTemperature);
|
||||
return brut;
|
||||
};
|
||||
|
||||
// Valeur actuelle RÉELLE (TB_Capteurs.Valeur ou TB_Reservoirs.Niveau,
|
||||
// toujours à jour) - PAS extrapolée depuis le dernier point de
|
||||
// l'historique, qui peut être absent (aucune mesure récente) ou vieux
|
||||
// de plusieurs minutes (valeur moyennée par bucket) :
|
||||
QSqlQuery qActuel;
|
||||
if (type == "capteurs") {
|
||||
qActuel.prepare("SELECT Valeur FROM TB_Capteurs WHERE Cle_primaire=:id");
|
||||
} else if (type == "reservoirs") {
|
||||
qActuel.prepare("SELECT Niveau FROM TB_Reservoirs WHERE Cle_primaire=:id");
|
||||
} else if (type == "circuits") {
|
||||
QString colonne;
|
||||
if (variableCircuit == "consigne") colonne = "Consigne";
|
||||
else if (variableCircuit == "intensite") colonne = "Intensite";
|
||||
else if (variableCircuit == "frequence") colonne = "Frequence";
|
||||
else colonne = "Tension";
|
||||
qActuel.prepare(QString("SELECT %1 FROM TB_Circuits WHERE Cle_primaire=:id").arg(colonne));
|
||||
} else { // batteries
|
||||
static const QMap<QString, QString> COLONNES_BATTERIE = {
|
||||
{"soc", "Soc"}, {"tension", "Tension"}, {"temperature", "Temperature"},
|
||||
{"courant_differentiel", "Courant_differentiel"}, {"courant_charge", "Courant_charge"},
|
||||
{"courant_decharge", "Courant_decharge"}, {"soh", "Soh"}, {"autonomie", "Autonomie"}
|
||||
};
|
||||
qActuel.prepare(QString("SELECT %1 FROM TB_Batteries WHERE Cle_primaire=:id").arg(COLONNES_BATTERIE.value(variableCircuit)));
|
||||
}
|
||||
qActuel.bindValue(":id", id);
|
||||
qActuel.exec();
|
||||
double valeurActuelle = 0;
|
||||
if (qActuel.next()) valeurActuelle = convertir(qActuel.value(0).toDouble());
|
||||
|
||||
// Point de départ synthétique : dernière valeur connue AVANT le début
|
||||
// de la période, reportée exactement à la date de début - assure que
|
||||
// la courbe reflète l'état réel dès le premier instant demandé, même
|
||||
// si aucune mesure n'a été enregistrée DURANT la période elle-même
|
||||
// (élément en mode "enregistrement sur changement" - une valeur
|
||||
// stable ne crée aucune nouvelle ligne dans la table de mesures).
|
||||
// Logique identique à celle déjà présente dans tb_mesures.cpp natif.
|
||||
QJsonArray points;
|
||||
double minVal = 0, maxVal = 0, sommeVal = 0;
|
||||
qint64 minT = 0, maxT = 0;
|
||||
int nb = 0;
|
||||
double derniereValeurConnue = 0;
|
||||
bool auMoinsUnePoint = false;
|
||||
|
||||
{
|
||||
QSqlQuery qAvant;
|
||||
qAvant.prepare(
|
||||
QString("SELECT Valeur FROM %1 WHERE ID=:id AND ID_Variable=:idVariable "
|
||||
"AND Date<=:debut AND Valeur IS NOT NULL ORDER BY Date DESC LIMIT 1").arg(nomTable)
|
||||
);
|
||||
qAvant.bindValue(":id", id);
|
||||
qAvant.bindValue(":idVariable", idVariable);
|
||||
qAvant.bindValue(":debut", dateDebut.toString("yyyy-MM-dd HH:mm:ss"));
|
||||
qAvant.exec();
|
||||
if (qAvant.next()) {
|
||||
const double v = convertir(qAvant.value(0).toDouble());
|
||||
const qint64 t = dateDebut.toMSecsSinceEpoch();
|
||||
QJsonObject pt; pt["t"] = t; pt["v"] = v;
|
||||
points.append(pt);
|
||||
minVal = maxVal = v;
|
||||
minT = maxT = t;
|
||||
sommeVal = v;
|
||||
nb = 1;
|
||||
derniereValeurConnue = v;
|
||||
auMoinsUnePoint = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Récupération des points RÉELS de la période, regroupés côté SQL
|
||||
// (moyenne par bucket) - même principe que la branche "barres
|
||||
// d'énergie" déjà validée pour les circuits (§20.6), généralisé ici
|
||||
// à toute courbe de mesure :
|
||||
QSqlQuery qm;
|
||||
qm.prepare(
|
||||
QString("SELECT Date, AVG(Valeur) FROM %1 "
|
||||
"WHERE ID=:id AND ID_Variable=:idVariable AND Date>:debut AND Date<=:fin AND Valeur IS NOT NULL "
|
||||
"GROUP BY (UNIX_TIMESTAMP(Date) DIV :regroupement) ORDER BY Date").arg(nomTable)
|
||||
);
|
||||
qm.bindValue(":id", id);
|
||||
qm.bindValue(":idVariable", idVariable);
|
||||
qm.bindValue(":debut", dateDebut.toString("yyyy-MM-dd HH:mm:ss"));
|
||||
qm.bindValue(":fin", dateFin.toString("yyyy-MM-dd HH:mm:ss"));
|
||||
qm.bindValue(":regroupement", regroupement);
|
||||
qm.exec();
|
||||
|
||||
while (qm.next()) {
|
||||
const QDateTime dt = qm.value(0).toDateTime();
|
||||
const double valeurConvertie = convertir(qm.value(1).toDouble());
|
||||
const qint64 t = dt.toMSecsSinceEpoch();
|
||||
|
||||
QJsonObject pt;
|
||||
pt["t"] = t;
|
||||
pt["v"] = valeurConvertie;
|
||||
points.append(pt);
|
||||
|
||||
if (nb == 0 || valeurConvertie < minVal) { minVal = valeurConvertie; minT = t; }
|
||||
if (nb == 0 || valeurConvertie > maxVal) { maxVal = valeurConvertie; maxT = t; }
|
||||
sommeVal += valeurConvertie;
|
||||
nb++;
|
||||
derniereValeurConnue = valeurConvertie;
|
||||
auMoinsUnePoint = true;
|
||||
}
|
||||
|
||||
// Point de fin synthétique : reporte la dernière valeur connue
|
||||
// jusqu'à "maintenant", pour que la courbe occupe toute la période -
|
||||
// purement visuel, ne modifie pas les statistiques min/max/moyenne
|
||||
// (ce n'est pas une nouvelle mesure, juste la continuité visuelle de
|
||||
// la précédente) :
|
||||
if (auMoinsUnePoint) {
|
||||
QJsonObject ptFin;
|
||||
ptFin["t"] = dateFin.toMSecsSinceEpoch();
|
||||
ptFin["v"] = derniereValeurConnue;
|
||||
points.append(ptFin);
|
||||
}
|
||||
|
||||
QJsonObject reponse;
|
||||
reponse["status"] = "ok";
|
||||
reponse["points"] = points;
|
||||
reponse["unite"] = symboleUnite;
|
||||
reponse["precision"] = precision;
|
||||
reponse["valeur_actuelle"] = valeurActuelle;
|
||||
if (nb > 0) {
|
||||
QJsonObject minObj; minObj["v"] = minVal; minObj["t"] = minT;
|
||||
QJsonObject maxObj; maxObj["v"] = maxVal; maxObj["t"] = maxT;
|
||||
reponse["min"] = minObj;
|
||||
reponse["max"] = maxObj;
|
||||
reponse["moyenne"] = sommeVal / nb;
|
||||
}
|
||||
return withCors(QHttpServerResponse(QJsonDocument(reponse).toJson()));
|
||||
});
|
||||
|
||||
auto tcpServer = new QTcpServer();
|
||||
if (!tcpServer->listen(QHostAddress::Any, 8080) || !server.bind(tcpServer)) {
|
||||
qCritical() << "Impossible de démarrer le serveur HTTP sur le port 8080.";
|
||||
|
||||
Reference in New Issue
Block a user