From db2b1901cd02e7c63968dea3014d2b0485041625 2017-06-26 07:33:57 From: Alexandre Leroux Date: 2017-06-26 07:33:57 Subject: [PATCH] Merge branch 'feature/VariableCache' into develop --- diff --git a/core/include/Data/ArrayData.h b/core/include/Data/ArrayData.h index b08f0c2..5be205f 100644 --- a/core/include/Data/ArrayData.h +++ b/core/include/Data/ArrayData.h @@ -49,6 +49,32 @@ public: return m_Data.at(0); } + /** + * @return the data as a vector + * @remarks this method is only available for a unidimensional ArrayData + */ + template > + const QVector &data(double tStart, double tEnd) const noexcept + { + return m_Data.at(tStart); + } + + // TODO Comment + template > + void merge(ArrayData<1> *arrayData) + { + if (!m_Data.empty()) { + m_Data[0] += arrayData->data(); + } + } + + template > + int size() + { + return m_Data[0].size(); + } + + private: QVector > m_Data; }; diff --git a/core/include/Data/DataSeries.h b/core/include/Data/DataSeries.h index f9de7dc..6232a63 100644 --- a/core/include/Data/DataSeries.h +++ b/core/include/Data/DataSeries.h @@ -29,6 +29,15 @@ public: /// @sa IDataSeries::valuesUnit() Unit valuesUnit() const override { return m_ValuesUnit; } + /// @sa IDataSeries::merge() + void merge(IDataSeries *dataSeries) override + { + if (auto dimDataSeries = dynamic_cast *>(dataSeries)) { + m_XAxisData->merge(dimDataSeries->xAxisData().get()); + m_ValuesData->merge(dimDataSeries->valuesData().get()); + } + } + protected: /// Protected ctor (DataSeries is abstract) explicit DataSeries(std::shared_ptr > xAxisData, const Unit &xAxisUnit, diff --git a/core/include/Data/IDataProvider.h b/core/include/Data/IDataProvider.h index 628d123..5d025cf 100644 --- a/core/include/Data/IDataProvider.h +++ b/core/include/Data/IDataProvider.h @@ -5,6 +5,8 @@ #include +#include + class DataProviderParameters; class IDataSeries; @@ -16,14 +18,21 @@ class IDataSeries; * * @sa IDataSeries */ -class IDataProvider { +class IDataProvider : public QObject { + + Q_OBJECT public: virtual ~IDataProvider() noexcept = default; virtual std::unique_ptr retrieveData(const DataProviderParameters ¶meters) const = 0; -}; + + virtual void requestDataLoading(const QVector &dateTimeList) = 0; + +signals: + void dataProvided(std::shared_ptr dateSerie, SqpDateTime dateTime); +}; // Required for using shared_ptr in signals/slots Q_DECLARE_METATYPE(std::shared_ptr) diff --git a/core/include/Data/IDataSeries.h b/core/include/Data/IDataSeries.h index 26a80a1..bbea31f 100644 --- a/core/include/Data/IDataSeries.h +++ b/core/include/Data/IDataSeries.h @@ -1,10 +1,12 @@ #ifndef SCIQLOP_IDATASERIES_H #define SCIQLOP_IDATASERIES_H -#include #include +#include +#include + template class ArrayData; @@ -42,6 +44,11 @@ public: virtual Unit xAxisUnit() const = 0; virtual Unit valuesUnit() const = 0; + + virtual void merge(IDataSeries *dataSeries) = 0; }; +// Required for using shared_ptr in signals/slots +Q_DECLARE_METATYPE(std::shared_ptr) + #endif // SCIQLOP_IDATASERIES_H diff --git a/core/include/Data/SqpDateTime.h b/core/include/Data/SqpDateTime.h index a6d1aa4..7ecec9f 100644 --- a/core/include/Data/SqpDateTime.h +++ b/core/include/Data/SqpDateTime.h @@ -1,6 +1,7 @@ #ifndef SCIQLOP_SQPDATETIME_H #define SCIQLOP_SQPDATETIME_H +#include /** * @brief The SqpDateTime struct holds the information of time parameters */ @@ -9,6 +10,14 @@ struct SqpDateTime { double m_TStart; /// End time double m_TEnd; + + bool contains(const SqpDateTime &dateTime) + { + return (m_TStart <= dateTime.m_TStart && m_TEnd >= dateTime.m_TEnd); + } }; +// Required for using shared_ptr in signals/slots +Q_DECLARE_METATYPE(SqpDateTime) + #endif // SCIQLOP_SQPDATETIME_H diff --git a/core/include/Variable/Variable.h b/core/include/Variable/Variable.h index 228202c..c233bde 100644 --- a/core/include/Variable/Variable.h +++ b/core/include/Variable/Variable.h @@ -1,29 +1,48 @@ #ifndef SCIQLOP_VARIABLE_H #define SCIQLOP_VARIABLE_H -#include +#include + +#include #include +#include + +Q_DECLARE_LOGGING_CATEGORY(LOG_Variable) + class IDataSeries; class QString; /** * @brief The Variable class represents a variable in SciQlop. */ -class Variable { +class Variable : public QObject { + + Q_OBJECT + public: - explicit Variable(const QString &name, const QString &unit, const QString &mission); + explicit Variable(const QString &name, const QString &unit, const QString &mission, + const SqpDateTime &dateTime); QString name() const noexcept; QString mission() const noexcept; QString unit() const noexcept; - - void addDataSeries(std::unique_ptr dataSeries) noexcept; + SqpDateTime dateTime() const noexcept; /// @return the data of the variable, nullptr if there is no data IDataSeries *dataSeries() const noexcept; + bool contains(SqpDateTime dateTime); + void setDataSeries(std::unique_ptr dataSeries) noexcept; + +public slots: + void onAddDataSeries(std::shared_ptr dataSeries) noexcept; + +signals: + void dataCacheUpdated(); + + private: class VariablePrivate; spimpl::unique_impl_ptr impl; diff --git a/core/include/Variable/VariableCacheController.h b/core/include/Variable/VariableCacheController.h new file mode 100644 index 0000000..edcc65d --- /dev/null +++ b/core/include/Variable/VariableCacheController.h @@ -0,0 +1,33 @@ +#ifndef SCIQLOP_VARIABLECACHECONTROLLER_H +#define SCIQLOP_VARIABLECACHECONTROLLER_H + +#include + +#include + +#include + +class Variable; + +/// This class aims to store in the cash all of the dateTime already requested to the variable. +class VariableCacheController : public QObject { + Q_OBJECT +public: + explicit VariableCacheController(QObject *parent = 0); + + + void addDateTime(std::shared_ptr variable, const SqpDateTime &dateTime); + + /// Return all of the SqpDataTime part of the dateTime whose are not in the cache + QVector provideNotInCacheDateTimeList(std::shared_ptr variable, + const SqpDateTime &dateTime); + + + QVector dateCacheList(std::shared_ptr variable) const noexcept; + +private: + class VariableCacheControllerPrivate; + spimpl::unique_impl_ptr impl; +}; + +#endif // SCIQLOP_VARIABLECACHECONTROLLER_H diff --git a/core/include/Variable/VariableController.h b/core/include/Variable/VariableController.h index 0945b53..603c1e9 100644 --- a/core/include/Variable/VariableController.h +++ b/core/include/Variable/VariableController.h @@ -1,11 +1,14 @@ #ifndef SCIQLOP_VARIABLECONTROLLER_H #define SCIQLOP_VARIABLECONTROLLER_H +#include + #include #include #include + class IDataProvider; class TimeController; class Variable; @@ -26,6 +29,10 @@ public: void setTimeController(TimeController *timeController) noexcept; + + /// Request the data loading of the variable whithin dateTime + void requestDataLoading(std::shared_ptr variable, const SqpDateTime &dateTime); + signals: /// Signal emitted when a variable has been created void variableCreated(std::shared_ptr variable); diff --git a/core/include/Variable/VariableModel.h b/core/include/Variable/VariableModel.h index 140a02a..490f43b 100644 --- a/core/include/Variable/VariableModel.h +++ b/core/include/Variable/VariableModel.h @@ -1,11 +1,14 @@ #ifndef SCIQLOP_VARIABLEMODEL_H #define SCIQLOP_VARIABLEMODEL_H -#include + +#include #include #include +#include + Q_DECLARE_LOGGING_CATEGORY(LOG_VariableModel) class IDataSeries; @@ -21,11 +24,13 @@ public: /** * Creates a new variable in the model * @param name the name of the new variable + * @param dateTime the dateTime of the new variable * @param defaultDataSeries the default data of the new variable * @return the pointer to the new variable */ std::shared_ptr - createVariable(const QString &name, std::unique_ptr defaultDataSeries) noexcept; + createVariable(const QString &name, const SqpDateTime &dateTime, + std::unique_ptr defaultDataSeries) noexcept; // /////////////////////////// // // QAbstractTableModel methods // diff --git a/core/src/Variable/Variable.cpp b/core/src/Variable/Variable.cpp index 544d73d..43864d5 100644 --- a/core/src/Variable/Variable.cpp +++ b/core/src/Variable/Variable.cpp @@ -1,21 +1,32 @@ #include "Variable/Variable.h" #include +#include + +Q_LOGGING_CATEGORY(LOG_Variable, "Variable") struct Variable::VariablePrivate { - explicit VariablePrivate(const QString &name, const QString &unit, const QString &mission) - : m_Name{name}, m_Unit{unit}, m_Mission{mission}, m_DataSeries{nullptr} + explicit VariablePrivate(const QString &name, const QString &unit, const QString &mission, + const SqpDateTime &dateTime) + : m_Name{name}, + m_Unit{unit}, + m_Mission{mission}, + m_DateTime{dateTime}, + m_DataSeries{nullptr} { } QString m_Name; QString m_Unit; QString m_Mission; + + SqpDateTime m_DateTime; // The dateTime available in the view and loaded. not the cache. std::unique_ptr m_DataSeries; }; -Variable::Variable(const QString &name, const QString &unit, const QString &mission) - : impl{spimpl::make_unique_impl(name, unit, mission)} +Variable::Variable(const QString &name, const QString &unit, const QString &mission, + const SqpDateTime &dateTime) + : impl{spimpl::make_unique_impl(name, unit, mission, dateTime)} { } @@ -34,15 +45,45 @@ QString Variable::unit() const noexcept return impl->m_Unit; } -void Variable::addDataSeries(std::unique_ptr dataSeries) noexcept +SqpDateTime Variable::dateTime() const noexcept +{ + return impl->m_DateTime; +} + +void Variable::setDataSeries(std::unique_ptr dataSeries) noexcept { if (!impl->m_DataSeries) { impl->m_DataSeries = std::move(dataSeries); } - /// @todo : else, merge the two data series (if possible) +} + +void Variable::onAddDataSeries(std::shared_ptr dataSeries) noexcept +{ + if (impl->m_DataSeries) { + impl->m_DataSeries->merge(dataSeries.get()); + + emit dataCacheUpdated(); + } } IDataSeries *Variable::dataSeries() const noexcept { return impl->m_DataSeries.get(); } + +bool Variable::contains(SqpDateTime dateTime) +{ + if (!impl->m_DateTime.contains(dateTime)) { + // The current variable dateTime isn't enough to display the dateTime requested. + // We have to update it to the new dateTime requested. + // the correspondant new data to display will be given by the cache if possible and the + // provider if necessary. + qCInfo(LOG_Variable()) << "NEW DATE NEEDED"; + + impl->m_DateTime = dateTime; + + return false; + } + + return true; +} diff --git a/core/src/Variable/VariableCacheController.cpp b/core/src/Variable/VariableCacheController.cpp new file mode 100644 index 0000000..63209ce --- /dev/null +++ b/core/src/Variable/VariableCacheController.cpp @@ -0,0 +1,173 @@ +#include "Variable/VariableCacheController.h" + +#include "Variable/Variable.h" +#include + +struct VariableCacheController::VariableCacheControllerPrivate { + + std::unordered_map, QVector > + m_VariableToSqpDateTimeListMap; + + void addInCacheDataByEnd(const SqpDateTime &dateTime, QVector &dateTimeList, + QVector ¬InCache, int cacheIndex, + double currentTStart); + + void addInCacheDataByStart(const SqpDateTime &dateTime, QVector &dateTimeList, + QVector ¬InCache, int cacheIndex, + double currentTStart); + + + void addDateTimeRecurse(const SqpDateTime &dateTime, QVector &dateTimeList, + int cacheIndex); +}; + + +VariableCacheController::VariableCacheController(QObject *parent) + : QObject(parent), impl{spimpl::make_unique_impl()} +{ +} + +void VariableCacheController::addDateTime(std::shared_ptr variable, + const SqpDateTime &dateTime) +{ + if (variable) { + auto findVariableIte = impl->m_VariableToSqpDateTimeListMap.find(variable); + if (findVariableIte == impl->m_VariableToSqpDateTimeListMap.end()) { + impl->m_VariableToSqpDateTimeListMap[variable].push_back(dateTime); + } + else { + + // addDateTime modify the list of the variable in a way to ensure + // that the list is ordered : l(0) < l(1). We assume also a < b + // (with a & b of type SqpDateTime) means ts(b) > te(a) + + // The algorithm will try the merge of two interval: + // - dateTime will be compare with the first interval of the list: + // A: if it is inferior, it will be inserted and it's finished. + // B: if it is in intersection, it will be merge then the merged one + // will be compared to the next interval. The old one is remove from the list + // C: if it is superior, we do the same with the next interval of the list + + int cacheIndex = 0; + impl->addDateTimeRecurse(dateTime, impl->m_VariableToSqpDateTimeListMap.at(variable), + cacheIndex); + } + } +} + +QVector +VariableCacheController::provideNotInCacheDateTimeList(std::shared_ptr variable, + const SqpDateTime &dateTime) +{ + auto notInCache = QVector{}; + + // This algorithm is recursif. The idea is to localise the start time then the end time in the + // list of date time request associated to the variable + // We assume that the list is ordered in a way that l(0) < l(1). We assume also a < b + // (with a & b of type SqpDateTime) means ts(b) > te(a) + + impl->addInCacheDataByStart(dateTime, impl->m_VariableToSqpDateTimeListMap.at(variable), + notInCache, 0, dateTime.m_TStart); + + return notInCache; +} + +QVector +VariableCacheController::dateCacheList(std::shared_ptr variable) const noexcept +{ + return impl->m_VariableToSqpDateTimeListMap.at(variable); +} + +void VariableCacheController::VariableCacheControllerPrivate::addDateTimeRecurse( + const SqpDateTime &dateTime, QVector &dateTimeList, int cacheIndex) +{ + const auto dateTimeListSize = dateTimeList.count(); + if (cacheIndex >= dateTimeListSize) { + dateTimeList.push_back(dateTime); + // there is no anymore interval to compore, we can just push_back it + return; + } + + auto currentDateTime = dateTimeList[cacheIndex]; + + if (dateTime.m_TEnd < currentDateTime.m_TStart) { + // The compared one is < to current one compared, we can insert it + dateTimeList.insert(cacheIndex, dateTime); + } + + else if (dateTime.m_TStart > currentDateTime.m_TEnd) { + // The compared one is > to current one compared we can comparet if to the next one + addDateTimeRecurse(dateTime, dateTimeList, ++cacheIndex); + } + else { + // Merge cases: we need to merge the two interval, remove the old one from the list then + // rerun the algo from this index with the merged interval + auto mTStart = std::min(dateTime.m_TStart, currentDateTime.m_TStart); + auto mTEnd = std::max(dateTime.m_TEnd, currentDateTime.m_TEnd); + auto mergeDateTime = SqpDateTime{mTStart, mTEnd}; + + dateTimeList.remove(cacheIndex); + addDateTimeRecurse(mergeDateTime, dateTimeList, cacheIndex); + } +} + + +void VariableCacheController::VariableCacheControllerPrivate::addInCacheDataByEnd( + const SqpDateTime &dateTime, QVector &dateTimeList, + QVector ¬InCache, int cacheIndex, double currentTStart) +{ + const auto dateTimeListSize = dateTimeList.count(); + if (cacheIndex >= dateTimeListSize) { + if (currentTStart < dateTime.m_TEnd) { + + // te localised after all other interval: The last interval is [currentTsart, te] + notInCache.push_back(SqpDateTime{currentTStart, dateTime.m_TEnd}); + } + return; + } + + auto currentDateTimeJ = dateTimeList[cacheIndex]; + if (dateTime.m_TEnd <= currentDateTimeJ.m_TStart) { + // te localised between to interval: The last interval is [currentTsart, te] + notInCache.push_back(SqpDateTime{currentTStart, dateTime.m_TEnd}); + } + else { + notInCache.push_back(SqpDateTime{currentTStart, currentDateTimeJ.m_TStart}); + if (dateTime.m_TEnd > currentDateTimeJ.m_TEnd) { + // te not localised before the current interval: we need to look at the next interval + addInCacheDataByEnd(dateTime, dateTimeList, notInCache, ++cacheIndex, + currentDateTimeJ.m_TEnd); + } + } +} + +void VariableCacheController::VariableCacheControllerPrivate::addInCacheDataByStart( + const SqpDateTime &dateTime, QVector &dateTimeList, + QVector ¬InCache, int cacheIndex, double currentTStart) +{ + const auto dateTimeListSize = dateTimeList.count(); + if (cacheIndex >= dateTimeListSize) { + // ts localised after all other interval: The last interval is [ts, te] + notInCache.push_back(SqpDateTime{currentTStart, dateTime.m_TEnd}); + return; + } + + auto currentDateTimeI = dateTimeList[cacheIndex]; + auto cacheIndexJ = cacheIndex; + if (currentTStart < currentDateTimeI.m_TStart) { + + // ts localised between to interval: let's localized te + addInCacheDataByEnd(dateTime, dateTimeList, notInCache, cacheIndexJ, currentTStart); + } + else if (dateTime.m_TStart < currentDateTimeI.m_TEnd) { + // ts not localised before the current interval: we need to look at the next interval + // We can assume now current tstart is the last interval tend, because data between them are + // in the cache + addInCacheDataByStart(dateTime, dateTimeList, notInCache, ++cacheIndex, + currentDateTimeI.m_TEnd); + } + else { + // ts not localised before the current interval: we need to look at the next interval + addInCacheDataByStart(dateTime, dateTimeList, notInCache, ++cacheIndex, currentTStart); + } +} diff --git a/core/src/Variable/VariableController.cpp b/core/src/Variable/VariableController.cpp index ccd7a5c..54124b6 100644 --- a/core/src/Variable/VariableController.cpp +++ b/core/src/Variable/VariableController.cpp @@ -1,3 +1,5 @@ +#include +#include #include #include @@ -7,9 +9,12 @@ #include