313 lines
16 KiB
C++
313 lines
16 KiB
C++
#include "tb_mesures.h"
|
|
#include "shipheart.h"
|
|
|
|
//#include <QDateTime>
|
|
#include <QElapsedTimer>
|
|
#include <QSqlError>
|
|
#include <QSqlRecord>
|
|
#include <QSqlQuery>
|
|
#include <QXYSeries>
|
|
|
|
|
|
//============================================================================
|
|
// Prototypes :
|
|
//============================================================================
|
|
qreal conversion_unite(qreal val, quint8 type_unite, quint32 unite);
|
|
|
|
|
|
//============================================================================
|
|
// Création du modèle
|
|
//============================================================================
|
|
TbMesuresModel::TbMesuresModel() :
|
|
QObject() {
|
|
|
|
}
|
|
|
|
|
|
|
|
//================================================================================================================================
|
|
// Fonction : Fonction principale de mise à jour des data à partir des données de la BDD. Les données proprement dites sont
|
|
// stockées dans le vecteur 'points'. Et les valeurs de min et max en x et y sont également calculées.
|
|
// Reçoit : Le pointeur sur la série (donc la courbe),
|
|
// le début de la requête à exécuter, en UTC (jusqu'aux filtre sur les Dates),
|
|
// la fin de la requete à exécuter, en UTC (après le filtre sur les Dates). En général, comprend les GROUP BY et les SORT,
|
|
// la date de début de la courbe,
|
|
// la date de fin de la courbe,
|
|
// le coefficient valeur/unité du capteur en cours (pour pouvoir calculer les valeurs réelles de Y auxquelles on ne touchera ensuite plus jusqu'à l'affichage de la courbe),
|
|
// le fuseau horaire afin de retourner les points avec des DateTime dans le bon fuseau (NB : l'heure d'été peut être ajoutée dedans lors de l'appel).
|
|
// Retour : Une liste de chaine contenant le nombre de lignes retournées, les points m_xMin(x, y), m_xMax(x, y), m_yMin(x, y),
|
|
// m_yMax(x, y) et la valeur m_yAvg.
|
|
//================================================================================================================================
|
|
QStringList TbMesuresModel::maj_data(QAbstractSeries *serie, QString table, quint32 id, quint32 id_variable, QDateTime date_debut, QDateTime date_fin, quint32 regroupement_donnees, qreal coeff_valeur_unite, qreal fuseau, quint8 type_unite, quint32 unite)
|
|
{
|
|
QStringList retour;
|
|
QVector<QPointF> temp_points; // Vecteur temporaire
|
|
QPointF m_xMin;
|
|
QPointF m_xMax;
|
|
QPointF m_yMin;
|
|
QPointF m_yMax;
|
|
qreal xVal, yVal;
|
|
qreal xVal_prec, yVal_prec;
|
|
qreal xCumul, yCumul;
|
|
qreal moyenne;
|
|
quint32 diviseur;
|
|
QString requete;
|
|
QSqlQuery query;
|
|
QSqlRecord resultat;
|
|
qint32 nb_lignes;
|
|
quint32 i;
|
|
bool flag_premier_point_ok;
|
|
|
|
QElapsedTimer myTimer;
|
|
myTimer.start();
|
|
|
|
|
|
//-----------------------------------------------
|
|
// Init des valeurs par défaut :
|
|
nb_lignes = 0;
|
|
temp_points.reserve(1500); // A priori, on réserve la mémoire pour 1500 lignes (soit un peu plus que 24h x 60 min.). Mais la taille sera agrandie en dynamique si besoin...
|
|
m_xMin.ry() = std::numeric_limits<qreal>::max();
|
|
m_xMax.ry() = std::numeric_limits<qreal>::min();
|
|
m_yMin.ry() = std::numeric_limits<qreal>::max();
|
|
m_yMax.ry() = std::numeric_limits<qreal>::min();
|
|
if (coeff_valeur_unite == 0) diviseur = 1;
|
|
else diviseur = coeff_valeur_unite;
|
|
xCumul = 0;
|
|
yCumul = 0;
|
|
|
|
query.setForwardOnly(true);
|
|
|
|
//-----------------------------------------------
|
|
// Recherche du point de départ :
|
|
requete = " SELECT Date, Valeur FROM " + table + " WHERE ID='" + QString::number(id) + "' AND ID_Variable='" + QString::number(id_variable) + "' ";
|
|
requete += " AND Date<'" + date_debut.toString(FORMAT_DATE_HEURE_BDD) + "' ";
|
|
// requete += " AND Valeur IS NOT NULL ";
|
|
requete += " ORDER BY Date DESC LIMIT 1 ";
|
|
//qDebug() << "Requête de recherche du premier point : " << requete;
|
|
|
|
yVal = 0;
|
|
flag_premier_point_ok = false;
|
|
if (query.exec(requete) == true) // Exécution sans erreur de la requête :
|
|
{
|
|
if (query.next()) // Cas normal où on a trouvé un point précédent l'intervalle :
|
|
{
|
|
yVal = conversion_unite(query.record().value(1).toReal() / diviseur, type_unite, unite);
|
|
flag_premier_point_ok = true;
|
|
|
|
}
|
|
else qDebug() << "La requête de recherche du premier point n'a trouvé aucune valeur précédent le début de la courbe. La valeur Y initiale est fixée à " + QString::number(yVal);
|
|
}
|
|
else qDebug() << "/!\\ Erreur d'exécution de la requête de recherche du premier point. La valeur Y initiale est fixée à " + QString::number(yVal);
|
|
|
|
xVal = date_debut.toMSecsSinceEpoch() + (fuseau * 60 * 60 * 1000); // On ajuste immédiatement les valeurs de date au fuseau en cours
|
|
m_xMin = QPointF(xVal, yVal); // Mémorisation du point de début de l'axe X
|
|
|
|
if (flag_premier_point_ok)
|
|
{
|
|
temp_points << QPointF(xVal, yVal); // Ajout à la courbe, comme premier point...
|
|
|
|
if (yVal < m_yMin.ry()) m_yMin = QPointF(xVal, yVal); // Mise à jour du mini
|
|
if (yVal > m_yMax.ry()) m_yMax = QPointF(xVal, yVal); // Mise à jour du maxi
|
|
|
|
nb_lignes ++;
|
|
}
|
|
|
|
//-------------------------------------------------------------------
|
|
// Génération de la requête principale de récupération des data de la courbe :
|
|
if (regroupement_donnees <= 1)
|
|
{
|
|
requete = " SELECT Date, Valeur FROM " + table + " WHERE ID='" + QString::number(id) + "' AND ID_Variable='" + QString::number(id_variable) + "' ";
|
|
requete += " AND Date>'" + date_debut.toString(FORMAT_DATE_HEURE_BDD) + "' AND Date<='" + date_fin.toString(FORMAT_DATE_HEURE_BDD) + "' ";
|
|
requete += " AND Valeur IS NOT NULL ";
|
|
requete += " ORDER BY Date ";
|
|
}
|
|
else
|
|
{
|
|
requete = " SELECT Date, AVG(Valeur) FROM " + table + " WHERE ID='" + QString::number(id) + "' AND ID_Variable='" + QString::number(id_variable) + "' ";
|
|
requete += " AND Date>'" + date_debut.toString(FORMAT_DATE_HEURE_BDD) + "' AND Date<='" + date_fin.toString(FORMAT_DATE_HEURE_BDD) + "' ";
|
|
requete += " AND Valeur IS NOT NULL ";
|
|
requete += " GROUP BY (UNIX_TIMESTAMP(Date) DIV " + QString::number(regroupement_donnees) + ") ORDER BY Date ";
|
|
}
|
|
//qDebug() << "Requête générée : " << requete;
|
|
|
|
|
|
//-----------------------------------------------
|
|
// Récupération des données dans la BDD :
|
|
if (query.exec(requete) == true) // TRUE = pas d'erreur
|
|
{
|
|
nb_lignes += query.size();
|
|
|
|
//qDebug() << "Nombre d'enregistrements récupérés par la requête : " << nb_lignes << " et temps écoulé : " << myTimer.elapsed() << "ms";
|
|
|
|
//-----------------------------------------------
|
|
// Traitement des tous les enregistrements récupérés :
|
|
for (i = 0; i < (quint32)nb_lignes; i ++)
|
|
{
|
|
if (!query.next()) break; // Ce break ne devrait pas arriver puisqu'on a compté la taille du query avant de lancer la boucle...
|
|
|
|
resultat = query.record();
|
|
|
|
// Mise à jour des valeurs précédentes avant de calculer les nouvelles :
|
|
xVal_prec = xVal;
|
|
yVal_prec = yVal;
|
|
|
|
// Mise à jour des nouvelles valeurs :
|
|
xVal = resultat.value(0).toDateTime().toMSecsSinceEpoch() + (fuseau * 60 * 60 * 1000); // On ajuste immédiatement les valeurs de date au fuseau en cours
|
|
yVal = resultat.value(1).toReal() / diviseur;
|
|
|
|
// Ajustement des valeurs en fonction de la conversion d'unité concernée :
|
|
yVal = conversion_unite(yVal, type_unite, unite);
|
|
|
|
//qDebug() << "Nouveau point " << i << " : Date = " << resultat.value(0).toDateTime() << " / " << xVal << " et y = " << yVal;
|
|
|
|
// Calcul des cumuls :
|
|
if ((flag_premier_point_ok) || (i > 0)) // Si le premier point n'a pas été trouvé, on ne prends pas en compte l'intervalle entre le départ et le premier point dans la fenêtre
|
|
{
|
|
yCumul += (xVal - xVal_prec) * (yVal_prec);
|
|
xCumul += (xVal - xVal_prec);
|
|
}
|
|
|
|
// Calcul des points yMin et yMax :
|
|
if (yVal < m_yMin.ry()) m_yMin = QPointF(xVal, yVal);
|
|
if (yVal > m_yMax.ry()) m_yMax = QPointF(xVal, yVal);
|
|
|
|
// Ajout des points dans la série temporaire :
|
|
temp_points << QPointF(xVal, yVal_prec); // Ajout d'un point avec la valeur courante de X mais avec le Y du point précédent (permet de "maintenir" la valeur précédente jusqu'au point en cours).
|
|
temp_points << QPointF(xVal, yVal); // Point en cours
|
|
}
|
|
|
|
//qDebug() << "Fin de boucle et temps écoulé : " << myTimer.elapsed() << "ms";
|
|
|
|
//-----------------------------------------------
|
|
// Calcul du point de fin :
|
|
if (nb_lignes > 0) // Si aucun point n'a été trouvé, il ne faut pas ajouter le dernier point (qui n'aura aucune signification)
|
|
{
|
|
xVal_prec = xVal;
|
|
yVal_prec = yVal;
|
|
xVal = date_fin.toMSecsSinceEpoch() + (fuseau * 60 * 60 * 1000); // On ajuste immédiatement les valeurs de date au fuseau en cours
|
|
yCumul += (xVal - xVal_prec) * (yVal_prec); // Dernier calcul de cumul
|
|
xCumul += (xVal - xVal_prec); // Dernier calcul de cumul
|
|
m_xMax = QPointF(xVal, yVal); // Mémorisation du point de fin de l'axe X. NB : la valeur de yVal est volontairement laissée inchangée (valeur Y du dernier point). Au passage, on évite les ajustements d'unités...
|
|
temp_points << QPointF(xVal, yVal); // Ajout à la courbe, comme dernier point...
|
|
nb_lignes ++;
|
|
}
|
|
|
|
//-----------------------------------------------
|
|
// Mise à jour des valeurs de la courbe :
|
|
if (serie)
|
|
{
|
|
QXYSeries *xySeries = static_cast<QXYSeries *>(serie);
|
|
xySeries->replace(temp_points); // Mise à jour des valeurs de la courbe avec le vecteur temporaire (Use replace instead of clear + append, it's optimized for performance)
|
|
}
|
|
else qDebug() << "Valeur de 'series' non valide !";
|
|
|
|
//qDebug() << "yMin : " << m_yMin.ry() << " et yMax : " << m_yMax.ry();
|
|
|
|
//-----------------------------------------------
|
|
// Préparation des valeurs de retour :
|
|
// NB : le 'f' est nécessaire pour les dates ('-----rx()') au moins ; sinon la précision n'est pas suffisante !!
|
|
if (xCumul != 0) moyenne = yCumul / xCumul;
|
|
else moyenne = 0;
|
|
|
|
retour << QString::number(nb_lignes);
|
|
retour = retour << QString::number(m_xMin.rx(), 'f') << QString::number(m_xMin.ry(), 'f') << QString::number(m_xMax.rx(), 'f') << QString::number(m_xMax.ry(), 'f');
|
|
retour = retour << QString::number(m_yMin.rx(), 'f') << QString::number(m_yMin.ry(), 'f') << QString::number(m_yMax.rx(), 'f') << QString::number(m_yMax.ry(), 'f');
|
|
retour = retour << QString::number(moyenne, 'f');
|
|
|
|
//qDebug() << "FIN de maj_data : " << nb_lignes << " enregistrements en " << myTimer.elapsed() << "ms\n";
|
|
|
|
}
|
|
else // Si une erreur lors de la requête a été détectée :
|
|
{
|
|
qDebug() << "/!\\ Erreur à l'exécution de la requête principale de 'maj_data()' ";
|
|
retour << QString::number(0);
|
|
}
|
|
|
|
//qDebug() << "FIN de maj_data";
|
|
return(retour);
|
|
}
|
|
|
|
|
|
//================================================================================================================================
|
|
// Fonction : Calcule une valeur dans l'unité demandée.
|
|
// Reçoit : La valeur à convertir (dans l'une des unités de base : °C ou litres. Les autres sont laissées inchangées
|
|
// Retour : la valeur converte
|
|
//================================================================================================================================
|
|
qreal conversion_unite(qreal val, quint8 type_unite, quint32 unite)
|
|
{
|
|
qreal retour = val;
|
|
|
|
switch(type_unite)
|
|
{
|
|
//-----------------------
|
|
case UNITE_VOLUME :
|
|
{
|
|
switch(unite)
|
|
{
|
|
case UNITE_VOLUME_LITRES: { break; } // Aucune conversion, on reste en litres
|
|
case UNITE_VOLUME_US_GALLONS: { retour = val / 3.785411784; break; } // Conversion litres => US Ga
|
|
case UNITE_VOLUME_IMP_GALLONS: { retour = val / 4.54609; break; } // Conversion litres => Imp. Ga
|
|
default : { break; } // Aucune conversion, on reste en ... ce qu'on est.
|
|
}
|
|
break;
|
|
}
|
|
|
|
//-----------------------
|
|
case UNITE_TEMPERATURE :
|
|
{
|
|
switch(unite)
|
|
{
|
|
case UNITE_TEMPERATURE_CELSIUS: { break; } // Aucune conversion, on reste en °C
|
|
case UNITE_TEMPERATURE_FAHRENHEIT: { retour = (1.8 * val) + 32; break; } // Conversion °C => °F
|
|
default : { break; } // Aucune conversion, on reste en ... ce qu'on est.
|
|
}
|
|
break;
|
|
}
|
|
|
|
//-----------------------
|
|
default: break;
|
|
}
|
|
|
|
return (retour);
|
|
}
|
|
|
|
|
|
//================================================================================================================================
|
|
// Fonction : Fonction de copie des points d'une série (par exemple, une LineSerie d'un graphique) dans ceux d'une autre série.
|
|
// Reçoit : Le pointeur sur la série source et un sur la série destinataire.
|
|
// Retour : Rien
|
|
//================================================================================================================================
|
|
void TbMesuresModel::copie_series(QAbstractSeries *serie_source, QAbstractSeries *serie_destination)
|
|
{
|
|
QVector<QPointF> tempo;
|
|
qint64 i;
|
|
qint64 nb;
|
|
qint64 taille_destination;
|
|
|
|
//QElapsedTimer myTimer;
|
|
//myTimer.start();
|
|
|
|
if ((serie_source) && (serie_destination))
|
|
{
|
|
QXYSeries *xySerie_source = static_cast<QXYSeries *>(serie_source);
|
|
QXYSeries *xySerie_destination = static_cast<QXYSeries *>(serie_destination);
|
|
|
|
// Copie des points de la source vers destination :
|
|
nb = xySerie_source->count(); // Nombre de points à transférer
|
|
for (i = 0; i < nb; i ++) // Boucle sur tous les points de la source
|
|
{
|
|
tempo << xySerie_source->at(i); // Copie de chaque point dans une structure temporaire
|
|
}
|
|
|
|
xySerie_destination->replace(tempo); // Copie de la structure temporaire dans la série destination
|
|
|
|
// Suppression des éventuels points de la destination si la source est plus petite :
|
|
taille_destination = xySerie_destination->count();
|
|
if (taille_destination > nb) xySerie_destination->removePoints(nb, (taille_destination - nb));
|
|
|
|
//qDebug() << "Copie Série faite. Nb d'enregistrements copiés = " << nb << "Temps d'exécution = " << myTimer.elapsed() << "ms\n";
|
|
}
|
|
else qDebug() << "Copie série impossible car au moins un pointeur (source et/ou destination) est NULL\n";
|
|
}
|
|
|