diff --git a/app/ui/MainWindow.ui b/app/ui/MainWindow.ui
index 30bc87a..44aecb5 100644
--- a/app/ui/MainWindow.ui
+++ b/app/ui/MainWindow.ui
@@ -11,7 +11,7 @@
- QLop
+ SciQlop v0.0.1
true
@@ -126,7 +126,7 @@
0
0
800
- 26
+ 28
diff --git a/core/include/Data/IDataProvider.h b/core/include/Data/IDataProvider.h
index c627123..59799d2 100644
--- a/core/include/Data/IDataProvider.h
+++ b/core/include/Data/IDataProvider.h
@@ -4,6 +4,7 @@
#include
#include
+#include
#include
@@ -26,10 +27,11 @@ class IDataProvider : public QObject {
public:
virtual ~IDataProvider() noexcept = default;
- virtual void requestDataLoading(const QVector &dateTimeList) = 0;
+ virtual void requestDataLoading(QUuid token, const QVector &dateTimeList) = 0;
signals:
- void dataProvided(std::shared_ptr dateSerie, const SqpDateTime &dateTime);
+ void dataProvided(QUuid token, std::shared_ptr dateSerie,
+ const SqpDateTime &dateTime);
};
// Required for using shared_ptr in signals/slots
diff --git a/core/src/Variable/VariableController.cpp b/core/src/Variable/VariableController.cpp
index 350c766..24bebc2 100644
--- a/core/src/Variable/VariableController.cpp
+++ b/core/src/Variable/VariableController.cpp
@@ -11,6 +11,7 @@
#include
#include
#include
+#include
#include
#include
@@ -37,6 +38,7 @@ struct VariableController::VariableControllerPrivate {
std::unordered_map, std::shared_ptr >
m_VariableToProviderMap;
+ std::unordered_map, QUuid> m_VariableToToken;
};
VariableController::VariableController(QObject *parent)
@@ -118,16 +120,21 @@ 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();
// store the provider
impl->m_VariableToProviderMap[newVariable] = provider;
+ impl->m_VariableToToken[newVariable] = token;
auto addDateTimeAcquired = [ this, varW = std::weak_ptr{newVariable} ](
- auto dataSeriesAcquired, auto dateTimeToPutInCache)
+ QUuid token, auto dataSeriesAcquired, auto dateTimeToPutInCache)
{
if (auto variable = varW.lock()) {
- impl->m_VariableCacheController->addDateTime(variable, dateTimeToPutInCache);
- variable->setDataSeries(dataSeriesAcquired);
+ auto varToken = impl->m_VariableToToken.at(variable);
+ if (varToken == token) {
+ impl->m_VariableCacheController->addDateTime(variable, dateTimeToPutInCache);
+ variable->setDataSeries(dataSeriesAcquired);
+ }
}
};
@@ -166,8 +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);
impl->m_VariableToProviderMap.at(variable)->requestDataLoading(
- std::move(dateTimeListNotInCache));
+ token, std::move(dateTimeListNotInCache));
}
else {
emit variable->updated();
diff --git a/plugins/amda/CMakeLists.txt b/plugins/amda/CMakeLists.txt
index bfa7e21..1b66326 100644
--- a/plugins/amda/CMakeLists.txt
+++ b/plugins/amda/CMakeLists.txt
@@ -13,7 +13,7 @@ INCLUDE_DIRECTORIES(${RESOURCES_DIR})
#
# Find Qt modules
#
-SCIQLOP_FIND_QT(Core Widgets)
+SCIQLOP_FIND_QT(Core Widgets Network)
#
# Find dependent libraries
@@ -68,7 +68,7 @@ INSTALL(TARGETS ${SQPAMDA_LIBRARY_NAME}
TARGET_LINK_LIBRARIES(${SQPAMDA_LIBRARY_NAME} ${LIBRARIES})
-qt5_use_modules(${SQPAMDA_LIBRARY_NAME} Core Widgets)
+qt5_use_modules(${SQPAMDA_LIBRARY_NAME} Core Widgets Network)
add_dependencies(${SQPAMDA_LIBRARY_NAME} ${SQPPLUGIN_LIBRARY_NAME} ${SQPGUI_LIBRARY_NAME} ${SQPCORE_LIBRARY_NAME})
diff --git a/plugins/amda/include/AmdaProvider.h b/plugins/amda/include/AmdaProvider.h
new file mode 100644
index 0000000..38c5924
--- /dev/null
+++ b/plugins/amda/include/AmdaProvider.h
@@ -0,0 +1,36 @@
+#ifndef SCIQLOP_AMDAPROVIDER_H
+#define SCIQLOP_AMDAPROVIDER_H
+
+#include "AmdaGlobal.h"
+
+#include
+
+#include
+
+#include
+
+
+Q_DECLARE_LOGGING_CATEGORY(LOG_AmdaProvider)
+
+/**
+ * @brief The AmdaProvider class is an example of how a data provider can generate data
+ */
+class SCIQLOP_AMDA_EXPORT AmdaProvider : public IDataProvider {
+public:
+ explicit AmdaProvider();
+
+ void requestDataLoading(QUuid token, const QVector &dateTimeList) override;
+
+private:
+ void retrieveData(QUuid token, const DataProviderParameters ¶meters) const;
+
+ class AmdaProviderPrivate;
+ spimpl::unique_impl_ptr impl;
+
+private slots:
+ void httpFinished() noexcept;
+ void httpDownloadFinished() noexcept;
+ void httpDownloadReadyRead() noexcept;
+};
+
+#endif // SCIQLOP_AMDAPROVIDER_H
diff --git a/plugins/amda/include/AmdaResultParser.h b/plugins/amda/include/AmdaResultParser.h
new file mode 100644
index 0000000..8c4bc61
--- /dev/null
+++ b/plugins/amda/include/AmdaResultParser.h
@@ -0,0 +1,19 @@
+#ifndef SCIQLOP_AMDARESULTPARSER_H
+#define SCIQLOP_AMDARESULTPARSER_H
+
+#include "AmdaGlobal.h"
+
+#include
+
+#include
+
+class IDataSeries;
+
+Q_DECLARE_LOGGING_CATEGORY(LOG_AmdaResultParser)
+
+struct SCIQLOP_AMDA_EXPORT AmdaResultParser {
+
+ static std::shared_ptr readTxt(const QString &filePath) noexcept;
+};
+
+#endif // SCIQLOP_AMDARESULTPARSER_H
diff --git a/plugins/amda/src/AmdaPlugin.cpp b/plugins/amda/src/AmdaPlugin.cpp
index 555755d..af18f76 100644
--- a/plugins/amda/src/AmdaPlugin.cpp
+++ b/plugins/amda/src/AmdaPlugin.cpp
@@ -1,8 +1,10 @@
#include "AmdaPlugin.h"
#include "AmdaParser.h"
+#include "AmdaProvider.h"
#include
#include
+#include
#include
@@ -16,6 +18,28 @@ const auto DATA_SOURCE_NAME = QStringLiteral("AMDA");
/// Path of the file used to generate the data source item for AMDA
const auto JSON_FILE_PATH = QStringLiteral(":/samples/AmdaSample.json");
+void associateActions(DataSourceItem &item, const QUuid &dataSourceUid)
+{
+ if (item.type() == DataSourceItemType::PRODUCT) {
+ auto itemName = item.name();
+
+ item.addAction(std::make_unique(
+ QObject::tr("Load %1 product").arg(itemName),
+ [itemName, dataSourceUid](DataSourceItem &item) {
+ if (auto app = sqpApp) {
+ app->dataSourceController().loadProductItem(dataSourceUid, item);
+ }
+ }));
+ }
+
+ auto count = item.childCount();
+ for (auto i = 0; i < count; ++i) {
+ if (auto child = item.child(i)) {
+ associateActions(*child, dataSourceUid);
+ }
+ }
+}
+
} // namespace
void AmdaPlugin::initialize()
@@ -27,11 +51,16 @@ void AmdaPlugin::initialize()
// Sets data source tree
if (auto dataSourceItem = AmdaParser::readJson(JSON_FILE_PATH)) {
+ associateActions(*dataSourceItem, dataSourceUid);
+
dataSourceController.setDataSourceItem(dataSourceUid, std::move(dataSourceItem));
}
else {
qCCritical(LOG_AmdaPlugin()) << tr("No data source item could be generated for AMDA");
}
+
+ // Sets data provider
+ dataSourceController.setDataProvider(dataSourceUid, std::make_unique());
}
else {
qCWarning(LOG_AmdaPlugin()) << tr("Can't access to SciQlop application");
diff --git a/plugins/amda/src/AmdaProvider.cpp b/plugins/amda/src/AmdaProvider.cpp
new file mode 100644
index 0000000..88777fc
--- /dev/null
+++ b/plugins/amda/src/AmdaProvider.cpp
@@ -0,0 +1,134 @@
+#include "AmdaProvider.h"
+#include "AmdaResultParser.h"
+
+#include
+
+#include
+#include
+#include
+
+Q_LOGGING_CATEGORY(LOG_AmdaProvider, "AmdaProvider")
+
+namespace {
+
+/// URL format for a request on AMDA server. The parameters are as follows:
+/// - %1: start date
+/// - %2: end date
+/// - %3: parameter id
+const auto AMDA_URL_FORMAT = QStringLiteral(
+ "http://amda.irap.omp.eu/php/rest/"
+ "getParameter.php?startTime=%1&stopTime=%2¶meterID=%3&sampling=60&outputFormat=ASCII&"
+ "timeFormat=ISO8601&gzip=0");
+
+/// Dates format passed in the URL (e.g 2013-09-23T09:00)
+const auto AMDA_TIME_FORMAT = QStringLiteral("yyyy-MM-ddThh:ss");
+
+/// Formats a time to a date that can be passed in URL
+QString dateFormat(double sqpDateTime) noexcept
+{
+ auto dateTime = QDateTime::fromMSecsSinceEpoch(sqpDateTime * 1000.);
+ 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};
+ QUuid m_Token;
+};
+
+AmdaProvider::AmdaProvider() : impl{spimpl::make_unique_impl()}
+{
+}
+
+void AmdaProvider::requestDataLoading(QUuid token, const QVector &dateTimeList)
+{
+ // NOTE: Try to use multithread if possible
+ for (const auto &dateTime : dateTimeList) {
+ retrieveData(token, DataProviderParameters{dateTime});
+ }
+}
+
+void AmdaProvider::retrieveData(QUuid token, const DataProviderParameters ¶meters) const
+{
+ // /////////// //
+ // Creates URL //
+ // /////////// //
+
+ auto startDate = dateFormat(parameters.m_Time.m_TStart);
+ auto endDate = dateFormat(parameters.m_Time.m_TEnd);
+ auto productId = QStringLiteral("imf(0)");
+
+ auto url = QUrl{QString{AMDA_URL_FORMAT}.arg(startDate, endDate, productId)};
+
+ // //////////////// //
+ // Executes request //
+ // //////////////// //
+
+ 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);
+ }
+}
+
+void AmdaProvider::httpDownloadFinished() noexcept
+{
+ if (impl->m_File) {
+ impl->m_File->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
+ }
+
+ impl->m_File = nullptr;
+ }
+
+ // Deletes reply
+ impl->m_Reply->deleteLater();
+ impl->m_Reply = nullptr;
+}
+
+void AmdaProvider::httpDownloadReadyRead() noexcept
+{
+ if (impl->m_File) {
+ impl->m_File->write(impl->m_Reply->readAll());
+ }
+}
diff --git a/plugins/amda/src/AmdaResultParser.cpp b/plugins/amda/src/AmdaResultParser.cpp
new file mode 100644
index 0000000..b929c7b
--- /dev/null
+++ b/plugins/amda/src/AmdaResultParser.cpp
@@ -0,0 +1,70 @@
+#include "AmdaResultParser.h"
+
+#include
+
+#include
+#include
+
+Q_LOGGING_CATEGORY(LOG_AmdaResultParser, "AmdaResultParser")
+
+namespace {
+
+/// Format for dates in result files
+const auto DATE_FORMAT = QStringLiteral("yyyy-MM-ddThh:mm:ss.zzz");
+
+/// @todo ALX
+double doubleDate(const QString &stringDate) noexcept
+{
+ auto dateTime = QDateTime::fromString(stringDate, DATE_FORMAT);
+ return dateTime.toMSecsSinceEpoch() / 1000.;
+}
+
+} // namespace
+
+std::shared_ptr AmdaResultParser::readTxt(const QString &filePath) noexcept
+{
+ QFile file{filePath};
+
+ if (!file.open(QFile::ReadOnly | QIODevice::Text)) {
+ qCCritical(LOG_AmdaResultParser())
+ << QObject::tr("Can't retrieve AMDA data from file %1: %2")
+ .arg(filePath, file.errorString());
+ return nullptr;
+ }
+
+ auto xData = QVector{};
+ auto valuesData = QVector{};
+
+ QTextStream stream{&file};
+
+ // Ignore comment lines (3 lines)
+ stream.readLine();
+ stream.readLine();
+ stream.readLine();
+
+ QString line{};
+ auto lineRegex = QRegExp{QStringLiteral("\\s+")};
+ while (stream.readLineInto(&line)) {
+ auto lineData = line.split(lineRegex, QString::SkipEmptyParts);
+ if (lineData.size() == 2) {
+ // X : the data is converted from date to double (in secs)
+ xData.push_back(doubleDate(lineData.at(0)));
+
+ // Value
+ valuesData.push_back(lineData.at(1).toDouble());
+ }
+ else {
+ /// @todo ALX : log
+ }
+ }
+
+ /// @todo ALX : handle units
+ auto scalarSeries = std::make_shared(xData.size(), Unit{"nT", true}, Unit{});
+
+ const auto count = xData.size();
+ for (auto i = 0; i < count; ++i) {
+ scalarSeries->setData(i, xData.at(i), valuesData.at(i));
+ }
+
+ return scalarSeries;
+}
diff --git a/plugins/mockplugin/include/CosinusProvider.h b/plugins/mockplugin/include/CosinusProvider.h
index 63c1fd2..41e7db4 100644
--- a/plugins/mockplugin/include/CosinusProvider.h
+++ b/plugins/mockplugin/include/CosinusProvider.h
@@ -14,7 +14,7 @@ Q_DECLARE_LOGGING_CATEGORY(LOG_CosinusProvider)
*/
class SCIQLOP_MOCKPLUGIN_EXPORT CosinusProvider : public IDataProvider {
public:
- void requestDataLoading(const QVector &dateTimeList) override;
+ void requestDataLoading(QUuid token, const QVector &dateTimeList) override;
private:
diff --git a/plugins/mockplugin/src/CosinusProvider.cpp b/plugins/mockplugin/src/CosinusProvider.cpp
index 8bb61dc..f3fab7d 100644
--- a/plugins/mockplugin/src/CosinusProvider.cpp
+++ b/plugins/mockplugin/src/CosinusProvider.cpp
@@ -38,13 +38,13 @@ CosinusProvider::retrieveData(const DataProviderParameters ¶meters) const
return scalarSeries;
}
-void CosinusProvider::requestDataLoading(const QVector &dateTimeList)
+void CosinusProvider::requestDataLoading(QUuid token, const QVector &dateTimeList)
{
qCDebug(LOG_CosinusProvider()) << "CosinusProvider::requestDataLoading"
<< QThread::currentThread()->objectName();
// NOTE: Try to use multithread if possible
for (const auto &dateTime : dateTimeList) {
auto scalarSeries = this->retrieveData(DataProviderParameters{dateTime});
- emit dataProvided(scalarSeries, dateTime);
+ emit dataProvided(token, scalarSeries, dateTime);
}
}