#include "tb_mesures.h" #include "shipheart.h" //#include #include #include #include #include #include //============================================================================ // 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 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::max(); m_xMax.ry() = std::numeric_limits::min(); m_yMin.ry() = std::numeric_limits::max(); m_yMax.ry() = std::numeric_limits::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(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 tempo; qint64 i; qint64 nb; qint64 taille_destination; //QElapsedTimer myTimer; //myTimer.start(); if ((serie_source) && (serie_destination)) { QXYSeries *xySerie_source = static_cast(serie_source); QXYSeries *xySerie_destination = static_cast(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"; }