##// END OF EJS Templates
Merge branch 'feature/AmdaProvider' into develop
Alexandre Leroux -
r383:0dae2f94cb54 merge
parent child
Show More
@@ -0,0 +1,36
1 #ifndef SCIQLOP_AMDAPROVIDER_H
2 #define SCIQLOP_AMDAPROVIDER_H
3
4 #include "AmdaGlobal.h"
5
6 #include <Common/spimpl.h>
7
8 #include <Data/IDataProvider.h>
9
10 #include <QLoggingCategory>
11
12
13 Q_DECLARE_LOGGING_CATEGORY(LOG_AmdaProvider)
14
15 /**
16 * @brief The AmdaProvider class is an example of how a data provider can generate data
17 */
18 class SCIQLOP_AMDA_EXPORT AmdaProvider : public IDataProvider {
19 public:
20 explicit AmdaProvider();
21
22 void requestDataLoading(QUuid token, const QVector<SqpDateTime> &dateTimeList) override;
23
24 private:
25 void retrieveData(QUuid token, const DataProviderParameters &parameters) const;
26
27 class AmdaProviderPrivate;
28 spimpl::unique_impl_ptr<AmdaProviderPrivate> impl;
29
30 private slots:
31 void httpFinished() noexcept;
32 void httpDownloadFinished() noexcept;
33 void httpDownloadReadyRead() noexcept;
34 };
35
36 #endif // SCIQLOP_AMDAPROVIDER_H
@@ -0,0 +1,19
1 #ifndef SCIQLOP_AMDARESULTPARSER_H
2 #define SCIQLOP_AMDARESULTPARSER_H
3
4 #include "AmdaGlobal.h"
5
6 #include <QLoggingCategory>
7
8 #include <memory>
9
10 class IDataSeries;
11
12 Q_DECLARE_LOGGING_CATEGORY(LOG_AmdaResultParser)
13
14 struct SCIQLOP_AMDA_EXPORT AmdaResultParser {
15
16 static std::shared_ptr<IDataSeries> readTxt(const QString &filePath) noexcept;
17 };
18
19 #endif // SCIQLOP_AMDARESULTPARSER_H
@@ -0,0 +1,134
1 #include "AmdaProvider.h"
2 #include "AmdaResultParser.h"
3
4 #include <Data/DataProviderParameters.h>
5
6 #include <QNetworkAccessManager>
7 #include <QNetworkReply>
8 #include <QTemporaryFile>
9
10 Q_LOGGING_CATEGORY(LOG_AmdaProvider, "AmdaProvider")
11
12 namespace {
13
14 /// URL format for a request on AMDA server. The parameters are as follows:
15 /// - %1: start date
16 /// - %2: end date
17 /// - %3: parameter id
18 const auto AMDA_URL_FORMAT = QStringLiteral(
19 "http://amda.irap.omp.eu/php/rest/"
20 "getParameter.php?startTime=%1&stopTime=%2&parameterID=%3&sampling=60&outputFormat=ASCII&"
21 "timeFormat=ISO8601&gzip=0");
22
23 /// Dates format passed in the URL (e.g 2013-09-23T09:00)
24 const auto AMDA_TIME_FORMAT = QStringLiteral("yyyy-MM-ddThh:ss");
25
26 /// Formats a time to a date that can be passed in URL
27 QString dateFormat(double sqpDateTime) noexcept
28 {
29 auto dateTime = QDateTime::fromMSecsSinceEpoch(sqpDateTime * 1000.);
30 return dateTime.toString(AMDA_TIME_FORMAT);
31 }
32
33 } // namespace
34
35 struct AmdaProvider::AmdaProviderPrivate {
36 DataProviderParameters m_Params{};
37 std::unique_ptr<QNetworkAccessManager> m_AccessManager{nullptr};
38 QNetworkReply *m_Reply{nullptr};
39 std::unique_ptr<QTemporaryFile> m_File{nullptr};
40 QUuid m_Token;
41 };
42
43 AmdaProvider::AmdaProvider() : impl{spimpl::make_unique_impl<AmdaProviderPrivate>()}
44 {
45 }
46
47 void AmdaProvider::requestDataLoading(QUuid token, const QVector<SqpDateTime> &dateTimeList)
48 {
49 // NOTE: Try to use multithread if possible
50 for (const auto &dateTime : dateTimeList) {
51 retrieveData(token, DataProviderParameters{dateTime});
52 }
53 }
54
55 void AmdaProvider::retrieveData(QUuid token, const DataProviderParameters &parameters) const
56 {
57 // /////////// //
58 // Creates URL //
59 // /////////// //
60
61 auto startDate = dateFormat(parameters.m_Time.m_TStart);
62 auto endDate = dateFormat(parameters.m_Time.m_TEnd);
63 auto productId = QStringLiteral("imf(0)");
64
65 auto url = QUrl{QString{AMDA_URL_FORMAT}.arg(startDate, endDate, productId)};
66
67 // //////////////// //
68 // Executes request //
69 // //////////////// //
70
71 impl->m_Token = token;
72 impl->m_Params = parameters;
73 impl->m_AccessManager = std::make_unique<QNetworkAccessManager>();
74 impl->m_Reply = impl->m_AccessManager->get(QNetworkRequest{url});
75 connect(impl->m_Reply, &QNetworkReply::finished, this, &AmdaProvider::httpFinished);
76 }
77
78 void AmdaProvider::httpFinished() noexcept
79 {
80 // ////////////////////// //
81 // Gets download file url //
82 // ////////////////////// //
83
84 auto downloadFileUrl = QUrl{QString{impl->m_Reply->readAll()}};
85
86 // ///////////////////////////////////// //
87 // Executes request for downloading file //
88 // ///////////////////////////////////// //
89
90 // Deletes old reply
91 impl->m_Reply->deleteLater();
92 impl->m_Reply = nullptr;
93
94 // Creates destination file
95 impl->m_File = std::make_unique<QTemporaryFile>();
96 if (impl->m_File->open()) {
97 qCDebug(LOG_AmdaProvider()) << "Temp file: " << impl->m_File->fileName();
98
99 // Executes request
100 impl->m_AccessManager = std::make_unique<QNetworkAccessManager>();
101 impl->m_Reply = impl->m_AccessManager->get(QNetworkRequest{downloadFileUrl});
102 connect(impl->m_Reply, &QNetworkReply::finished, this,
103 &AmdaProvider::httpDownloadReadyRead);
104 connect(impl->m_Reply, &QNetworkReply::finished, this, &AmdaProvider::httpDownloadFinished);
105 }
106 }
107
108 void AmdaProvider::httpDownloadFinished() noexcept
109 {
110 if (impl->m_File) {
111 impl->m_File->close();
112
113 // Parse results file
114 if (auto dataSeries = AmdaResultParser::readTxt(impl->m_File->fileName())) {
115 emit dataProvided(impl->m_Token, dataSeries, impl->m_Params.m_Time);
116 }
117 else {
118 /// @todo ALX : debug
119 }
120
121 impl->m_File = nullptr;
122 }
123
124 // Deletes reply
125 impl->m_Reply->deleteLater();
126 impl->m_Reply = nullptr;
127 }
128
129 void AmdaProvider::httpDownloadReadyRead() noexcept
130 {
131 if (impl->m_File) {
132 impl->m_File->write(impl->m_Reply->readAll());
133 }
134 }
@@ -0,0 +1,70
1 #include "AmdaResultParser.h"
2
3 #include <Data/ScalarSeries.h>
4
5 #include <QDateTime>
6 #include <QFile>
7
8 Q_LOGGING_CATEGORY(LOG_AmdaResultParser, "AmdaResultParser")
9
10 namespace {
11
12 /// Format for dates in result files
13 const auto DATE_FORMAT = QStringLiteral("yyyy-MM-ddThh:mm:ss.zzz");
14
15 /// @todo ALX
16 double doubleDate(const QString &stringDate) noexcept
17 {
18 auto dateTime = QDateTime::fromString(stringDate, DATE_FORMAT);
19 return dateTime.toMSecsSinceEpoch() / 1000.;
20 }
21
22 } // namespace
23
24 std::shared_ptr<IDataSeries> AmdaResultParser::readTxt(const QString &filePath) noexcept
25 {
26 QFile file{filePath};
27
28 if (!file.open(QFile::ReadOnly | QIODevice::Text)) {
29 qCCritical(LOG_AmdaResultParser())
30 << QObject::tr("Can't retrieve AMDA data from file %1: %2")
31 .arg(filePath, file.errorString());
32 return nullptr;
33 }
34
35 auto xData = QVector<double>{};
36 auto valuesData = QVector<double>{};
37
38 QTextStream stream{&file};
39
40 // Ignore comment lines (3 lines)
41 stream.readLine();
42 stream.readLine();
43 stream.readLine();
44
45 QString line{};
46 auto lineRegex = QRegExp{QStringLiteral("\\s+")};
47 while (stream.readLineInto(&line)) {
48 auto lineData = line.split(lineRegex, QString::SkipEmptyParts);
49 if (lineData.size() == 2) {
50 // X : the data is converted from date to double (in secs)
51 xData.push_back(doubleDate(lineData.at(0)));
52
53 // Value
54 valuesData.push_back(lineData.at(1).toDouble());
55 }
56 else {
57 /// @todo ALX : log
58 }
59 }
60
61 /// @todo ALX : handle units
62 auto scalarSeries = std::make_shared<ScalarSeries>(xData.size(), Unit{"nT", true}, Unit{});
63
64 const auto count = xData.size();
65 for (auto i = 0; i < count; ++i) {
66 scalarSeries->setData(i, xData.at(i), valuesData.at(i));
67 }
68
69 return scalarSeries;
70 }
@@ -11,7 +11,7
11 11 </rect>
12 12 </property>
13 13 <property name="windowTitle">
14 <string>QLop</string>
14 <string>SciQlop v0.0.1</string>
15 15 </property>
16 16 <property name="dockNestingEnabled">
17 17 <bool>true</bool>
@@ -126,7 +126,7
126 126 <x>0</x>
127 127 <y>0</y>
128 128 <width>800</width>
129 <height>26</height>
129 <height>28</height>
130 130 </rect>
131 131 </property>
132 132 </widget>
@@ -4,6 +4,7
4 4 #include <memory>
5 5
6 6 #include <QObject>
7 #include <QUuid>
7 8
8 9 #include <Common/MetaTypes.h>
9 10
@@ -26,10 +27,11 class IDataProvider : public QObject {
26 27 public:
27 28 virtual ~IDataProvider() noexcept = default;
28 29
29 virtual void requestDataLoading(const QVector<SqpDateTime> &dateTimeList) = 0;
30 virtual void requestDataLoading(QUuid token, const QVector<SqpDateTime> &dateTimeList) = 0;
30 31
31 32 signals:
32 void dataProvided(std::shared_ptr<IDataSeries> dateSerie, const SqpDateTime &dateTime);
33 void dataProvided(QUuid token, std::shared_ptr<IDataSeries> dateSerie,
34 const SqpDateTime &dateTime);
33 35 };
34 36
35 37 // Required for using shared_ptr in signals/slots
@@ -11,6 +11,7
11 11 #include <QDateTime>
12 12 #include <QMutex>
13 13 #include <QThread>
14 #include <QUuid>
14 15 #include <QtCore/QItemSelectionModel>
15 16
16 17 #include <unordered_map>
@@ -37,6 +38,7 struct VariableController::VariableControllerPrivate {
37 38
38 39 std::unordered_map<std::shared_ptr<Variable>, std::shared_ptr<IDataProvider> >
39 40 m_VariableToProviderMap;
41 std::unordered_map<std::shared_ptr<Variable>, QUuid> m_VariableToToken;
40 42 };
41 43
42 44 VariableController::VariableController(QObject *parent)
@@ -118,16 +120,21 void VariableController::createVariable(const QString &name,
118 120 /// in sciqlop
119 121 auto dateTime = impl->m_TimeController->dateTime();
120 122 if (auto newVariable = impl->m_VariableModel->createVariable(name, dateTime)) {
123 auto token = QUuid::createUuid();
121 124
122 125 // store the provider
123 126 impl->m_VariableToProviderMap[newVariable] = provider;
127 impl->m_VariableToToken[newVariable] = token;
124 128
125 129 auto addDateTimeAcquired = [ this, varW = std::weak_ptr<Variable>{newVariable} ](
126 auto dataSeriesAcquired, auto dateTimeToPutInCache)
130 QUuid token, auto dataSeriesAcquired, auto dateTimeToPutInCache)
127 131 {
128 132 if (auto variable = varW.lock()) {
129 impl->m_VariableCacheController->addDateTime(variable, dateTimeToPutInCache);
130 variable->setDataSeries(dataSeriesAcquired);
133 auto varToken = impl->m_VariableToToken.at(variable);
134 if (varToken == token) {
135 impl->m_VariableCacheController->addDateTime(variable, dateTimeToPutInCache);
136 variable->setDataSeries(dataSeriesAcquired);
137 }
131 138 }
132 139 };
133 140
@@ -166,8 +173,9 void VariableController::onRequestDataLoading(std::shared_ptr<Variable> variable
166 173
167 174 if (!dateTimeListNotInCache.empty()) {
168 175 // Ask the provider for each data on the dateTimeListNotInCache
176 auto token = impl->m_VariableToToken.at(variable);
169 177 impl->m_VariableToProviderMap.at(variable)->requestDataLoading(
170 std::move(dateTimeListNotInCache));
178 token, std::move(dateTimeListNotInCache));
171 179 }
172 180 else {
173 181 emit variable->updated();
@@ -13,7 +13,7 INCLUDE_DIRECTORIES(${RESOURCES_DIR})
13 13 #
14 14 # Find Qt modules
15 15 #
16 SCIQLOP_FIND_QT(Core Widgets)
16 SCIQLOP_FIND_QT(Core Widgets Network)
17 17
18 18 #
19 19 # Find dependent libraries
@@ -68,7 +68,7 INSTALL(TARGETS ${SQPAMDA_LIBRARY_NAME}
68 68
69 69
70 70 TARGET_LINK_LIBRARIES(${SQPAMDA_LIBRARY_NAME} ${LIBRARIES})
71 qt5_use_modules(${SQPAMDA_LIBRARY_NAME} Core Widgets)
71 qt5_use_modules(${SQPAMDA_LIBRARY_NAME} Core Widgets Network)
72 72
73 73 add_dependencies(${SQPAMDA_LIBRARY_NAME} ${SQPPLUGIN_LIBRARY_NAME} ${SQPGUI_LIBRARY_NAME} ${SQPCORE_LIBRARY_NAME})
74 74
@@ -1,8 +1,10
1 1 #include "AmdaPlugin.h"
2 2 #include "AmdaParser.h"
3 #include "AmdaProvider.h"
3 4
4 5 #include <DataSource/DataSourceController.h>
5 6 #include <DataSource/DataSourceItem.h>
7 #include <DataSource/DataSourceItemAction.h>
6 8
7 9 #include <SqpApplication.h>
8 10
@@ -16,6 +18,28 const auto DATA_SOURCE_NAME = QStringLiteral("AMDA");
16 18 /// Path of the file used to generate the data source item for AMDA
17 19 const auto JSON_FILE_PATH = QStringLiteral(":/samples/AmdaSample.json");
18 20
21 void associateActions(DataSourceItem &item, const QUuid &dataSourceUid)
22 {
23 if (item.type() == DataSourceItemType::PRODUCT) {
24 auto itemName = item.name();
25
26 item.addAction(std::make_unique<DataSourceItemAction>(
27 QObject::tr("Load %1 product").arg(itemName),
28 [itemName, dataSourceUid](DataSourceItem &item) {
29 if (auto app = sqpApp) {
30 app->dataSourceController().loadProductItem(dataSourceUid, item);
31 }
32 }));
33 }
34
35 auto count = item.childCount();
36 for (auto i = 0; i < count; ++i) {
37 if (auto child = item.child(i)) {
38 associateActions(*child, dataSourceUid);
39 }
40 }
41 }
42
19 43 } // namespace
20 44
21 45 void AmdaPlugin::initialize()
@@ -27,11 +51,16 void AmdaPlugin::initialize()
27 51
28 52 // Sets data source tree
29 53 if (auto dataSourceItem = AmdaParser::readJson(JSON_FILE_PATH)) {
54 associateActions(*dataSourceItem, dataSourceUid);
55
30 56 dataSourceController.setDataSourceItem(dataSourceUid, std::move(dataSourceItem));
31 57 }
32 58 else {
33 59 qCCritical(LOG_AmdaPlugin()) << tr("No data source item could be generated for AMDA");
34 60 }
61
62 // Sets data provider
63 dataSourceController.setDataProvider(dataSourceUid, std::make_unique<AmdaProvider>());
35 64 }
36 65 else {
37 66 qCWarning(LOG_AmdaPlugin()) << tr("Can't access to SciQlop application");
@@ -14,7 +14,7 Q_DECLARE_LOGGING_CATEGORY(LOG_CosinusProvider)
14 14 */
15 15 class SCIQLOP_MOCKPLUGIN_EXPORT CosinusProvider : public IDataProvider {
16 16 public:
17 void requestDataLoading(const QVector<SqpDateTime> &dateTimeList) override;
17 void requestDataLoading(QUuid token, const QVector<SqpDateTime> &dateTimeList) override;
18 18
19 19
20 20 private:
@@ -38,13 +38,13 CosinusProvider::retrieveData(const DataProviderParameters &parameters) const
38 38 return scalarSeries;
39 39 }
40 40
41 void CosinusProvider::requestDataLoading(const QVector<SqpDateTime> &dateTimeList)
41 void CosinusProvider::requestDataLoading(QUuid token, const QVector<SqpDateTime> &dateTimeList)
42 42 {
43 43 qCDebug(LOG_CosinusProvider()) << "CosinusProvider::requestDataLoading"
44 44 << QThread::currentThread()->objectName();
45 45 // NOTE: Try to use multithread if possible
46 46 for (const auto &dateTime : dateTimeList) {
47 47 auto scalarSeries = this->retrieveData(DataProviderParameters{dateTime});
48 emit dataProvided(scalarSeries, dateTime);
48 emit dataProvided(token, scalarSeries, dateTime);
49 49 }
50 50 }
General Comments 0
You need to be logged in to leave comments. Login now