diff --git a/app/vera-exclusions/exclusions.txt b/app/vera-exclusions/exclusions.txt index 02a0e85..4cfea6f 100644 --- a/app/vera-exclusions/exclusions.txt +++ b/app/vera-exclusions/exclusions.txt @@ -1,3 +1,5 @@ # Ignore false positive relative to App macro \.h:\d+:.IPSIS_S04.*found: Ui +# Ignore false positive relative to macros +Main\.cpp:\d+:.*IPSIS_S04_VARIABLE.*found: (__ppc64__) \ No newline at end of file diff --git a/core/CMakeLists.txt b/core/CMakeLists.txt index 90fcc84..c96774f 100644 --- a/core/CMakeLists.txt +++ b/core/CMakeLists.txt @@ -24,7 +24,7 @@ INCLUDE_DIRECTORIES(${SCIQLOP-PLUGIN_INCLUDE_DIR}) # # Find Qt modules # -SCIQLOP_FIND_QT(Core) +SCIQLOP_FIND_QT(Core Network) # # Compile the library library @@ -39,7 +39,7 @@ ADD_LIBRARY(${SQPCORE_LIBRARY_NAME} ${MODULE_SOURCES}) set_property(TARGET ${SQPCORE_LIBRARY_NAME} PROPERTY CXX_STANDARD 14) set_property(TARGET ${SQPCORE_LIBRARY_NAME} PROPERTY CXX_STANDARD_REQUIRED ON) TARGET_LINK_LIBRARIES(${SQPCORE_LIBRARY_NAME}) -qt5_use_modules(${SQPCORE_LIBRARY_NAME} Core) +qt5_use_modules(${SQPCORE_LIBRARY_NAME} Core Network) INSTALL(TARGETS ${SQPCORE_LIBRARY_NAME} RUNTIME DESTINATION ${INSTALL_BINARY_DIR} diff --git a/core/include/Data/IDataProvider.h b/core/include/Data/IDataProvider.h index 59799d2..2b4cc75 100644 --- a/core/include/Data/IDataProvider.h +++ b/core/include/Data/IDataProvider.h @@ -12,6 +12,8 @@ class DataProviderParameters; class IDataSeries; +class QNetworkReply; +class QNetworkRequest; /** * @brief The IDataProvider interface aims to declare a data provider. @@ -27,14 +29,32 @@ class IDataProvider : public QObject { public: virtual ~IDataProvider() noexcept = default; - virtual void requestDataLoading(QUuid token, const QVector &dateTimeList) = 0; + /** + * @brief requestDataLoading provide datas for the data identified by identifier for all + * SqpDateTime of dateTimeList + */ + virtual void requestDataLoading(QUuid identifier, const QVector &dateTimeList) = 0; signals: - void dataProvided(QUuid token, std::shared_ptr dateSerie, + /** + * @brief dataProvided send dataSeries under dateTime and that corresponds of the data + * identified by identifier + */ + void dataProvided(QUuid identifier, std::shared_ptr dateSerie, const SqpDateTime &dateTime); + + + /** + * @brief requestConstructed send a request for the data identified by identifier + * @callback is the methode call by the reply of the request when it is finished. + */ + void requestConstructed(const QNetworkRequest &request, QUuid identifier, + std::function callback); }; // Required for using shared_ptr in signals/slots SCIQLOP_REGISTER_META_TYPE(IDATAPROVIDER_PTR_REGISTRY, std::shared_ptr) +SCIQLOP_REGISTER_META_TYPE(IDATAPROVIDER_FUNCTION_REGISTRY, + std::function) #endif // SCIQLOP_IDATAPROVIDER_H diff --git a/core/include/Network/NetworkController.h b/core/include/Network/NetworkController.h index 0e36901..dd4fca5 100644 --- a/core/include/Network/NetworkController.h +++ b/core/include/Network/NetworkController.h @@ -3,11 +3,16 @@ #include #include +#include #include +#include Q_DECLARE_LOGGING_CATEGORY(LOG_NetworkController) +class QNetworkReply; +class QNetworkRequest; + /** * @brief The NetworkController class aims to handle all network connection of SciQlop. */ @@ -16,10 +21,18 @@ class NetworkController : public QObject { public: explicit NetworkController(QObject *parent = 0); - void initialize(); void finalize(); +public slots: + void onProcessRequested(const QNetworkRequest &request, QUuid identifier, + std::function callback); + void onReplyCanceled(QUuid identifier); + +signals: + void replyFinished(QNetworkReply *reply, QUuid identifier); + void replyDownloadProgress(QUuid identifier); + private: void waitForFinish(); diff --git a/core/src/Network/NetworkController.cpp b/core/src/Network/NetworkController.cpp index e015af4..eadc56b 100644 --- a/core/src/Network/NetworkController.cpp +++ b/core/src/Network/NetworkController.cpp @@ -1,13 +1,21 @@ #include "Network/NetworkController.h" #include +#include +#include +#include #include +#include + Q_LOGGING_CATEGORY(LOG_NetworkController, "NetworkController") struct NetworkController::NetworkControllerPrivate { explicit NetworkControllerPrivate(NetworkController *parent) : m_WorkingMutex{} {} QMutex m_WorkingMutex; + + std::unordered_map m_NetworkReplyToVariableId; + std::unique_ptr m_AccessManager{nullptr}; }; NetworkController::NetworkController(QObject *parent) @@ -15,10 +23,46 @@ NetworkController::NetworkController(QObject *parent) { } +void NetworkController::onProcessRequested(const QNetworkRequest &request, QUuid identifier, + std::function callback) +{ + qCDebug(LOG_NetworkController()) << tr("NetworkController registered") + << QThread::currentThread(); + auto reply = impl->m_AccessManager->get(request); + + // Store the couple reply id + impl->m_NetworkReplyToVariableId[reply] = identifier; + + auto onReplyFinished = [reply, this, identifier, callback]() { + + qCDebug(LOG_NetworkController()) << tr("NetworkController onReplyFinished") + << QThread::currentThread(); + auto it = impl->m_NetworkReplyToVariableId.find(reply); + if (it != impl->m_NetworkReplyToVariableId.cend()) { + callback(reply, identifier); + } + }; + + auto onReplyDownloadProgress = [reply, this]() { + + qCDebug(LOG_NetworkController()) << tr("NetworkController onReplyDownloadProgress") + << QThread::currentThread(); + auto it = impl->m_NetworkReplyToVariableId.find(reply); + if (it != impl->m_NetworkReplyToVariableId.cend()) { + emit this->replyDownloadProgress(it->second); + } + }; + + + connect(reply, &QNetworkReply::finished, this, onReplyFinished); + connect(reply, &QNetworkReply::downloadProgress, this, onReplyDownloadProgress); +} + void NetworkController::initialize() { qCDebug(LOG_NetworkController()) << tr("NetworkController init") << QThread::currentThread(); impl->m_WorkingMutex.lock(); + impl->m_AccessManager = std::make_unique(); qCDebug(LOG_NetworkController()) << tr("NetworkController init END"); } @@ -27,6 +71,17 @@ void NetworkController::finalize() impl->m_WorkingMutex.unlock(); } +void NetworkController::onReplyCanceled(QUuid identifier) +{ + auto findReply = [identifier](const auto &entry) { return identifier == entry.second; }; + + auto end = impl->m_NetworkReplyToVariableId.cend(); + auto it = std::find_if(impl->m_NetworkReplyToVariableId.cbegin(), end, findReply); + if (it != end) { + it->first->abort(); + } +} + void NetworkController::waitForFinish() { QMutexLocker locker{&impl->m_WorkingMutex}; diff --git a/core/src/Variable/VariableController.cpp b/core/src/Variable/VariableController.cpp index 24bebc2..3039def 100644 --- a/core/src/Variable/VariableController.cpp +++ b/core/src/Variable/VariableController.cpp @@ -38,7 +38,7 @@ struct VariableController::VariableControllerPrivate { std::unordered_map, std::shared_ptr > m_VariableToProviderMap; - std::unordered_map, QUuid> m_VariableToToken; + std::unordered_map, QUuid> m_VariableToIdentifier; }; VariableController::VariableController(QObject *parent) @@ -120,18 +120,18 @@ void VariableController::createVariable(const QString &name, /// in sciqlop auto dateTime = impl->m_TimeController->dateTime(); if (auto newVariable = impl->m_VariableModel->createVariable(name, dateTime)) { - auto token = QUuid::createUuid(); + auto identifier = QUuid::createUuid(); // store the provider impl->m_VariableToProviderMap[newVariable] = provider; - impl->m_VariableToToken[newVariable] = token; + impl->m_VariableToIdentifier[newVariable] = identifier; auto addDateTimeAcquired = [ this, varW = std::weak_ptr{newVariable} ]( - QUuid token, auto dataSeriesAcquired, auto dateTimeToPutInCache) + QUuid identifier, auto dataSeriesAcquired, auto dateTimeToPutInCache) { if (auto variable = varW.lock()) { - auto varToken = impl->m_VariableToToken.at(variable); - if (varToken == token) { + auto varIdentifier = impl->m_VariableToIdentifier.at(variable); + if (varIdentifier == identifier) { impl->m_VariableCacheController->addDateTime(variable, dateTimeToPutInCache); variable->setDataSeries(dataSeriesAcquired); } @@ -173,9 +173,9 @@ void VariableController::onRequestDataLoading(std::shared_ptr variable if (!dateTimeListNotInCache.empty()) { // Ask the provider for each data on the dateTimeListNotInCache - auto token = impl->m_VariableToToken.at(variable); + auto identifier = impl->m_VariableToIdentifier.at(variable); impl->m_VariableToProviderMap.at(variable)->requestDataLoading( - token, std::move(dateTimeListNotInCache)); + identifier, std::move(dateTimeListNotInCache)); } else { emit variable->updated(); diff --git a/core/vera-exclusions/exclusions.txt b/core/vera-exclusions/exclusions.txt index 7946185..8577c89 100644 --- a/core/vera-exclusions/exclusions.txt +++ b/core/vera-exclusions/exclusions.txt @@ -8,6 +8,7 @@ DataSourceItem\.h:\d+:.*IPSIS_S01.* ArrayData\.h:\d+:.*IPSIS_S04_VARIABLE.*found: (D) ArrayData\.h:\d+:.*IPSIS_S06.*found: (D) ArrayData\.h:\d+:.*IPSIS_S06.*found: (Dim) +DataSeries\.h:\d+:.*IPSIS_S04_VARIABLE.* # Ignore false positive relative to an alias DataSourceItemAction\.h:\d+:.*IPSIS_S06.*found: (ExecuteFunction) diff --git a/gui/src/SqpApplication.cpp b/gui/src/SqpApplication.cpp index a50fa86..597a3a3 100644 --- a/gui/src/SqpApplication.cpp +++ b/gui/src/SqpApplication.cpp @@ -78,7 +78,7 @@ public: SqpApplication::SqpApplication(int &argc, char **argv) : QApplication{argc, argv}, impl{spimpl::make_unique_impl()} { - qCInfo(LOG_SqpApplication()) << tr("SqpApplication construction"); + qCDebug(LOG_SqpApplication()) << tr("SqpApplication construction") << QThread::currentThread(); connect(&impl->m_DataSourceControllerThread, &QThread::started, impl->m_DataSourceController.get(), &DataSourceController::initialize); diff --git a/plugins/amda/include/AmdaProvider.h b/plugins/amda/include/AmdaProvider.h index 38c5924..d6c0c59 100644 --- a/plugins/amda/include/AmdaProvider.h +++ b/plugins/amda/include/AmdaProvider.h @@ -12,6 +12,8 @@ Q_DECLARE_LOGGING_CATEGORY(LOG_AmdaProvider) +class QNetworkReply; + /** * @brief The AmdaProvider class is an example of how a data provider can generate data */ @@ -22,15 +24,15 @@ public: void requestDataLoading(QUuid token, const QVector &dateTimeList) override; private: - void retrieveData(QUuid token, const DataProviderParameters ¶meters) const; + void retrieveData(QUuid token, const DataProviderParameters ¶meters); class AmdaProviderPrivate; spimpl::unique_impl_ptr impl; -private slots: - void httpFinished() noexcept; - void httpDownloadFinished() noexcept; - void httpDownloadReadyRead() noexcept; + // private slots: + // void httpFinished(QNetworkReply *reply, QUuid dataId) noexcept; + // void httpDownloadFinished(QNetworkReply *reply, QUuid dataId) noexcept; + // void httpDownloadReadyRead(QNetworkReply *reply, QUuid dataId) noexcept; }; #endif // SCIQLOP_AMDAPROVIDER_H diff --git a/plugins/amda/src/AmdaProvider.cpp b/plugins/amda/src/AmdaProvider.cpp index 88777fc..a7a6911 100644 --- a/plugins/amda/src/AmdaProvider.cpp +++ b/plugins/amda/src/AmdaProvider.cpp @@ -2,10 +2,14 @@ #include "AmdaResultParser.h" #include +#include +#include +#include #include #include #include +#include Q_LOGGING_CATEGORY(LOG_AmdaProvider, "AmdaProvider") @@ -30,18 +34,26 @@ QString dateFormat(double sqpDateTime) noexcept return dateTime.toString(AMDA_TIME_FORMAT); } + } // namespace struct AmdaProvider::AmdaProviderPrivate { DataProviderParameters m_Params{}; std::unique_ptr m_AccessManager{nullptr}; QNetworkReply *m_Reply{nullptr}; - std::unique_ptr m_File{nullptr}; + // std::unique_ptr m_File{nullptr}; QUuid m_Token; }; AmdaProvider::AmdaProvider() : impl{spimpl::make_unique_impl()} { + qCDebug(LOG_NetworkController()) << tr("AmdaProvider::AmdaProvider") + << QThread::currentThread(); + if (auto app = sqpApp) { + auto &networkController = app->networkController(); + connect(this, &AmdaProvider::requestConstructed, &networkController, + &NetworkController::onProcessRequested); + } } void AmdaProvider::requestDataLoading(QUuid token, const QVector &dateTimeList) @@ -52,7 +64,7 @@ void AmdaProvider::requestDataLoading(QUuid token, const QVector &d } } -void AmdaProvider::retrieveData(QUuid token, const DataProviderParameters ¶meters) const +void AmdaProvider::retrieveData(QUuid token, const DataProviderParameters ¶meters) { // /////////// // // Creates URL // @@ -64,71 +76,53 @@ void AmdaProvider::retrieveData(QUuid token, const DataProviderParameters ¶m auto url = QUrl{QString{AMDA_URL_FORMAT}.arg(startDate, endDate, productId)}; - // //////////////// // - // Executes request // - // //////////////// // + auto tempFile = std::make_shared(); - impl->m_Token = token; - impl->m_Params = parameters; - impl->m_AccessManager = std::make_unique(); - impl->m_Reply = impl->m_AccessManager->get(QNetworkRequest{url}); - connect(impl->m_Reply, &QNetworkReply::finished, this, &AmdaProvider::httpFinished); -} -void AmdaProvider::httpFinished() noexcept -{ - // ////////////////////// // - // Gets download file url // - // ////////////////////// // - - auto downloadFileUrl = QUrl{QString{impl->m_Reply->readAll()}}; - - // ///////////////////////////////////// // - // Executes request for downloading file // - // ///////////////////////////////////// // - - // Deletes old reply - impl->m_Reply->deleteLater(); - impl->m_Reply = nullptr; - - // Creates destination file - impl->m_File = std::make_unique(); - if (impl->m_File->open()) { - qCDebug(LOG_AmdaProvider()) << "Temp file: " << impl->m_File->fileName(); - - // Executes request - impl->m_AccessManager = std::make_unique(); - impl->m_Reply = impl->m_AccessManager->get(QNetworkRequest{downloadFileUrl}); - connect(impl->m_Reply, &QNetworkReply::finished, this, - &AmdaProvider::httpDownloadReadyRead); - connect(impl->m_Reply, &QNetworkReply::finished, this, &AmdaProvider::httpDownloadFinished); - } -} + // LAMBDA + auto httpDownloadFinished = [this, tempFile](QNetworkReply *reply, QUuid dataId) noexcept { -void AmdaProvider::httpDownloadFinished() noexcept -{ - if (impl->m_File) { - impl->m_File->close(); + if (tempFile) { + auto replyReadAll = reply->readAll(); + if (!replyReadAll.isEmpty()) { + tempFile->write(replyReadAll); + } + tempFile->close(); - // Parse results file - if (auto dataSeries = AmdaResultParser::readTxt(impl->m_File->fileName())) { - emit dataProvided(impl->m_Token, dataSeries, impl->m_Params.m_Time); - } - else { - /// @todo ALX : debug + // Parse results file + if (auto dataSeries = AmdaResultParser::readTxt(tempFile->fileName())) { + emit dataProvided(impl->m_Token, dataSeries, impl->m_Params.m_Time); + } + else { + /// @todo ALX : debug + } } - impl->m_File = nullptr; - } + // Deletes reply + reply->deleteLater(); + reply = nullptr; + }; + auto httpFinishedLambda = [this, httpDownloadFinished, tempFile](QNetworkReply *reply, + QUuid dataId) noexcept { - // Deletes reply - impl->m_Reply->deleteLater(); - impl->m_Reply = nullptr; -} + auto downloadFileUrl = QUrl{QString{reply->readAll()}}; + // Deletes old reply + reply->deleteLater(); -void AmdaProvider::httpDownloadReadyRead() noexcept -{ - if (impl->m_File) { - impl->m_File->write(impl->m_Reply->readAll()); - } + // Executes request for downloading file // + + // Creates destination file + if (tempFile->open()) { + // Executes request + emit requestConstructed(QNetworkRequest{downloadFileUrl}, dataId, httpDownloadFinished); + } + }; + + // //////////////// // + // Executes request // + // //////////////// // + + impl->m_Token = token; + impl->m_Params = parameters; + emit requestConstructed(QNetworkRequest{url}, token, httpFinishedLambda); }