AmdaProvider.cpp
282 lines
| 11.0 KiB
| text/x-c
|
CppLexer
Alexandre Leroux
|
r377 | #include "AmdaProvider.h" | ||
Alexandre Leroux
|
r413 | #include "AmdaDefs.h" | ||
Alexandre Leroux
|
r380 | #include "AmdaResultParser.h" | ||
Alexandre Leroux
|
r488 | #include <Common/DateUtils.h> | ||
Alexandre Leroux
|
r378 | #include <Data/DataProviderParameters.h> | ||
r388 | #include <Network/NetworkController.h> | |||
#include <SqpApplication.h> | ||||
#include <Variable/Variable.h> | ||||
Alexandre Leroux
|
r378 | |||
#include <QNetworkAccessManager> | ||||
#include <QNetworkReply> | ||||
Alexandre Leroux
|
r379 | #include <QTemporaryFile> | ||
r388 | #include <QThread> | |||
Alexandre Leroux
|
r377 | |||
Q_LOGGING_CATEGORY(LOG_AmdaProvider, "AmdaProvider") | ||||
Alexandre Leroux
|
r378 | namespace { | ||
/// URL format for a request on AMDA server. The parameters are as follows: | ||||
/// - %1: start date | ||||
/// - %2: end date | ||||
/// - %3: parameter id | ||||
Alexandre Leroux
|
r790 | /// AMDA V2: http://amdatest.irap.omp.eu/php/rest/ | ||
Alexandre Leroux
|
r378 | const auto AMDA_URL_FORMAT = QStringLiteral( | ||
Alexandre Leroux
|
r790 | "http://amda.irap.omp.eu/php/rest/" | ||
Alexandre Leroux
|
r473 | "getParameter.php?startTime=%1&stopTime=%2¶meterID=%3&outputFormat=ASCII&" | ||
Alexandre Leroux
|
r378 | "timeFormat=ISO8601&gzip=0"); | ||
/// Dates format passed in the URL (e.g 2013-09-23T09:00) | ||||
r440 | const auto AMDA_TIME_FORMAT = QStringLiteral("yyyy-MM-ddThh:mm:ss"); | |||
Alexandre Leroux
|
r378 | |||
/// Formats a time to a date that can be passed in URL | ||||
r512 | QString dateFormat(double sqpRange) noexcept | |||
Alexandre Leroux
|
r378 | { | ||
r512 | auto dateTime = DateUtils::dateTime(sqpRange); | |||
Alexandre Leroux
|
r378 | return dateTime.toString(AMDA_TIME_FORMAT); | ||
} | ||||
Alexandre Leroux
|
r566 | AmdaResultParser::ValueType valueType(const QString &valueType) | ||
{ | ||||
if (valueType == QStringLiteral("scalar")) { | ||||
return AmdaResultParser::ValueType::SCALAR; | ||||
} | ||||
else if (valueType == QStringLiteral("vector")) { | ||||
return AmdaResultParser::ValueType::VECTOR; | ||||
} | ||||
else { | ||||
return AmdaResultParser::ValueType::UNKNOWN; | ||||
} | ||||
} | ||||
Alexandre Leroux
|
r378 | } // namespace | ||
Alexandre Leroux
|
r409 | AmdaProvider::AmdaProvider() | ||
Alexandre Leroux
|
r377 | { | ||
Alexandre Leroux
|
r460 | qCDebug(LOG_AmdaProvider()) << tr("AmdaProvider::AmdaProvider") << QThread::currentThread(); | ||
r388 | if (auto app = sqpApp) { | |||
auto &networkController = app->networkController(); | ||||
r752 | connect(this, SIGNAL(requestConstructed(std::shared_ptr<QNetworkRequest>, QUuid, | |||
Alexandre Leroux
|
r416 | std::function<void(QNetworkReply *, QUuid)>)), | ||
&networkController, | ||||
r752 | SLOT(onProcessRequested(std::shared_ptr<QNetworkRequest>, QUuid, | |||
Alexandre Leroux
|
r416 | std::function<void(QNetworkReply *, QUuid)>))); | ||
r425 | ||||
r750 | connect(&sqpApp->networkController(), | |||
r752 | SIGNAL(replyDownloadProgress(QUuid, std::shared_ptr<QNetworkRequest>, double)), | |||
this, | ||||
SLOT(onReplyDownloadProgress(QUuid, std::shared_ptr<QNetworkRequest>, double))); | ||||
r388 | } | |||
Alexandre Leroux
|
r377 | } | ||
Alexandre Leroux
|
r712 | std::shared_ptr<IDataProvider> AmdaProvider::clone() const | ||
{ | ||||
// No copy is made in the clone | ||||
return std::make_shared<AmdaProvider>(); | ||||
} | ||||
r539 | void AmdaProvider::requestDataLoading(QUuid acqIdentifier, const DataProviderParameters ¶meters) | |||
Alexandre Leroux
|
r377 | { | ||
// NOTE: Try to use multithread if possible | ||||
Alexandre Leroux
|
r408 | const auto times = parameters.m_Times; | ||
Alexandre Leroux
|
r413 | const auto data = parameters.m_Data; | ||
Alexandre Leroux
|
r408 | for (const auto &dateTime : qAsConst(times)) { | ||
r761 | qCDebug(LOG_AmdaProvider()) << tr("TORM AmdaProvider::requestDataLoading ") << acqIdentifier | |||
<< dateTime; | ||||
r540 | this->retrieveData(acqIdentifier, dateTime, data); | |||
r546 | ||||
r750 | ||||
r628 | // TORM when AMDA will support quick asynchrone request | |||
QThread::msleep(1000); | ||||
Alexandre Leroux
|
r377 | } | ||
} | ||||
r539 | void AmdaProvider::requestDataAborting(QUuid acqIdentifier) | |||
r422 | { | |||
if (auto app = sqpApp) { | ||||
auto &networkController = app->networkController(); | ||||
r539 | networkController.onReplyCanceled(acqIdentifier); | |||
r422 | } | |||
} | ||||
r750 | void AmdaProvider::onReplyDownloadProgress(QUuid acqIdentifier, | |||
r752 | std::shared_ptr<QNetworkRequest> networkRequest, | |||
double progress) | ||||
r750 | { | |||
r760 | qCDebug(LOG_AmdaProvider()) << tr("onReplyDownloadProgress") << acqIdentifier | |||
<< networkRequest.get() << progress; | ||||
r750 | auto acqIdToRequestProgressMapIt = m_AcqIdToRequestProgressMap.find(acqIdentifier); | |||
if (acqIdToRequestProgressMapIt != m_AcqIdToRequestProgressMap.end()) { | ||||
r760 | // Update the progression for the current request | |||
r752 | auto requestPtr = networkRequest; | |||
auto findRequest = [requestPtr](const auto &entry) { return requestPtr == entry.first; }; | ||||
r750 | ||||
auto &requestProgressMap = acqIdToRequestProgressMapIt->second; | ||||
auto requestProgressMapEnd = requestProgressMap.end(); | ||||
auto requestProgressMapIt | ||||
= std::find_if(requestProgressMap.begin(), requestProgressMapEnd, findRequest); | ||||
if (requestProgressMapIt != requestProgressMapEnd) { | ||||
requestProgressMapIt->second = progress; | ||||
} | ||||
else { | ||||
r758 | // This case can happened when a progression is send after the request has been | |||
// finished. | ||||
// Generaly the case when aborting a request | ||||
r761 | qCDebug(LOG_AmdaProvider()) << tr("Can't retrieve Request in progress") << acqIdentifier | |||
<< networkRequest.get() << progress; | ||||
r750 | } | |||
r760 | // Compute the current final progress and notify it | |||
r750 | double finalProgress = 0.0; | |||
auto fraq = requestProgressMap.size(); | ||||
for (auto requestProgress : requestProgressMap) { | ||||
finalProgress += requestProgress.second; | ||||
r760 | qCDebug(LOG_AmdaProvider()) << tr("Current final progress without fraq:") | |||
r752 | << finalProgress << requestProgress.second; | |||
r750 | } | |||
if (fraq > 0) { | ||||
finalProgress = finalProgress / fraq; | ||||
} | ||||
r760 | qCDebug(LOG_AmdaProvider()) << tr("Current final progress: ") << fraq << finalProgress; | |||
r750 | emit dataProvidedProgress(acqIdentifier, finalProgress); | |||
} | ||||
else { | ||||
r758 | // This case can happened when a progression is send after the request has been finished. | |||
// Generaly the case when aborting a request | ||||
emit dataProvidedProgress(acqIdentifier, 100.0); | ||||
r750 | } | |||
} | ||||
r512 | void AmdaProvider::retrieveData(QUuid token, const SqpRange &dateTime, const QVariantHash &data) | |||
Alexandre Leroux
|
r378 | { | ||
Alexandre Leroux
|
r413 | // Retrieves product ID from data: if the value is invalid, no request is made | ||
auto productId = data.value(AMDA_XML_ID_KEY).toString(); | ||||
if (productId.isNull()) { | ||||
qCCritical(LOG_AmdaProvider()) << tr("Can't retrieve data: unknown product id"); | ||||
return; | ||||
} | ||||
Alexandre Leroux
|
r566 | // Retrieves the data type that determines whether the expected format for the result file is | ||
// scalar, vector... | ||||
auto productValueType = valueType(data.value(AMDA_DATA_TYPE_KEY).toString()); | ||||
Alexandre Leroux
|
r378 | // /////////// // | ||
// Creates URL // | ||||
// /////////// // | ||||
Alexandre Leroux
|
r408 | auto startDate = dateFormat(dateTime.m_TStart); | ||
auto endDate = dateFormat(dateTime.m_TEnd); | ||||
Alexandre Leroux
|
r378 | |||
auto url = QUrl{QString{AMDA_URL_FORMAT}.arg(startDate, endDate, productId)}; | ||||
r760 | qCInfo(LOG_AmdaProvider()) << tr("TORM AmdaProvider::retrieveData url:") << url; | |||
r388 | auto tempFile = std::make_shared<QTemporaryFile>(); | |||
Alexandre Leroux
|
r378 | |||
r388 | // LAMBDA | |||
Alexandre Leroux
|
r566 | auto httpDownloadFinished = [this, dateTime, tempFile, | ||
productValueType](QNetworkReply *reply, QUuid dataId) noexcept { | ||||
// Don't do anything if the reply was abort | ||||
r760 | if (reply->error() == QNetworkReply::NoError) { | |||
Alexandre Leroux
|
r566 | |||
if (tempFile) { | ||||
auto replyReadAll = reply->readAll(); | ||||
if (!replyReadAll.isEmpty()) { | ||||
tempFile->write(replyReadAll); | ||||
} | ||||
tempFile->close(); | ||||
// Parse results file | ||||
if (auto dataSeries | ||||
= AmdaResultParser::readTxt(tempFile->fileName(), productValueType)) { | ||||
emit dataProvided(dataId, dataSeries, dateTime); | ||||
} | ||||
else { | ||||
/// @todo ALX : debug | ||||
r761 | emit dataProvidedFailed(dataId); | |||
Alexandre Leroux
|
r566 | } | ||
} | ||||
r750 | m_AcqIdToRequestProgressMap.erase(dataId); | |||
Alexandre Leroux
|
r566 | } | ||
r760 | else { | |||
qCCritical(LOG_AmdaProvider()) << tr("httpDownloadFinished ERROR"); | ||||
r761 | emit dataProvidedFailed(dataId); | |||
r760 | } | |||
Alexandre Leroux
|
r566 | |||
}; | ||||
r431 | auto httpFinishedLambda | |||
= [this, httpDownloadFinished, tempFile](QNetworkReply *reply, QUuid dataId) noexcept { | ||||
Alexandre Leroux
|
r379 | |||
r431 | // Don't do anything if the reply was abort | |||
r760 | if (reply->error() == QNetworkReply::NoError) { | |||
Alexandre Leroux
|
r790 | // AMDA v2: auto downloadFileUrl = QUrl{QString{reply->readAll()}.trimmed()}; | ||
auto downloadFileUrl = QUrl{QString{reply->readAll()}}; | ||||
r422 | ||||
r760 | qCInfo(LOG_AmdaProvider()) | |||
r546 | << tr("TORM AmdaProvider::retrieveData downloadFileUrl:") << downloadFileUrl; | |||
r431 | // Executes request for downloading file // | |||
r388 | ||||
r431 | // Creates destination file | |||
if (tempFile->open()) { | ||||
r752 | // Executes request and store the request for progression | |||
r750 | auto request = std::make_shared<QNetworkRequest>(downloadFileUrl); | |||
updateRequestProgress(dataId, request, 0.0); | ||||
r752 | emit requestConstructed(request, dataId, httpDownloadFinished); | |||
r431 | } | |||
r761 | else { | |||
emit dataProvidedFailed(dataId); | ||||
} | ||||
r431 | } | |||
r750 | else { | |||
r760 | qCCritical(LOG_AmdaProvider()) << tr("httpFinishedLambda ERROR"); | |||
r750 | m_AcqIdToRequestProgressMap.erase(dataId); | |||
r761 | emit dataProvidedFailed(dataId); | |||
r750 | } | |||
r431 | }; | |||
r388 | ||||
// //////////////// // | ||||
// Executes request // | ||||
// //////////////// // | ||||
r750 | ||||
auto request = std::make_shared<QNetworkRequest>(url); | ||||
r752 | qCDebug(LOG_AmdaProvider()) << tr("First Request creation") << request.get(); | |||
r750 | updateRequestProgress(token, request, 0.0); | |||
r752 | emit requestConstructed(request, token, httpFinishedLambda); | |||
r750 | } | |||
void AmdaProvider::updateRequestProgress(QUuid acqIdentifier, | ||||
std::shared_ptr<QNetworkRequest> request, double progress) | ||||
{ | ||||
r828 | qCDebug(LOG_AmdaProvider()) << tr("updateRequestProgress request") << request.get(); | |||
r750 | auto acqIdToRequestProgressMapIt = m_AcqIdToRequestProgressMap.find(acqIdentifier); | |||
if (acqIdToRequestProgressMapIt != m_AcqIdToRequestProgressMap.end()) { | ||||
auto &requestProgressMap = acqIdToRequestProgressMapIt->second; | ||||
auto requestProgressMapIt = requestProgressMap.find(request); | ||||
if (requestProgressMapIt != requestProgressMap.end()) { | ||||
requestProgressMapIt->second = progress; | ||||
r752 | qCDebug(LOG_AmdaProvider()) << tr("updateRequestProgress new progress for request") | |||
<< acqIdentifier << request.get() << progress; | ||||
r750 | } | |||
else { | ||||
r752 | qCDebug(LOG_AmdaProvider()) << tr("updateRequestProgress new request") << acqIdentifier | |||
<< request.get() << progress; | ||||
r750 | acqIdToRequestProgressMapIt->second.insert(std::make_pair(request, progress)); | |||
} | ||||
} | ||||
else { | ||||
r752 | qCDebug(LOG_AmdaProvider()) << tr("updateRequestProgress new acqIdentifier") | |||
<< acqIdentifier << request.get() << progress; | ||||
r750 | auto requestProgressMap = std::map<std::shared_ptr<QNetworkRequest>, double>{}; | |||
requestProgressMap.insert(std::make_pair(request, progress)); | ||||
m_AcqIdToRequestProgressMap.insert( | ||||
std::make_pair(acqIdentifier, std::move(requestProgressMap))); | ||||
} | ||||
Alexandre Leroux
|
r377 | } | ||