##// END OF EJS Templates
Merge branch 'feature/DateTimeUTC' into develop
Alexandre Leroux -
r493:a774a1678512 merge
parent child
Show More
@@ -0,0 +1,19
1 #ifndef SCIQLOP_DATEUTILS_H
2 #define SCIQLOP_DATEUTILS_H
3
4 #include "CoreGlobal.h"
5
6 #include <QDateTime>
7
8 /**
9 * Utility class with methods for dates
10 */
11 struct SCIQLOP_CORE_EXPORT DateUtils {
12 /// Converts seconds (since epoch) to datetime. By default, the datetime is in UTC
13 static QDateTime dateTime(double secs, Qt::TimeSpec timeSpec = Qt::UTC) noexcept;
14
15 /// Converts datetime to seconds since epoch
16 static double secondsSinceEpoch(const QDateTime &dateTime) noexcept;
17 };
18
19 #endif // SCIQLOP_DATEUTILS_H
@@ -0,0 +1,13
1 #include "Common/DateUtils.h"
2
3 QDateTime DateUtils::dateTime(double secs, Qt::TimeSpec timeSpec) noexcept
4 {
5 // Uses msecs to be Qt 4 compatible
6 return QDateTime::fromMSecsSinceEpoch(secs * 1000., timeSpec);
7 }
8
9 double DateUtils::secondsSinceEpoch(const QDateTime &dateTime) noexcept
10 {
11 // Uses msecs to be Qt 4 compatible
12 return dateTime.toMSecsSinceEpoch() / 1000.;
13 }
@@ -1,255 +1,254
1 1 /*------------------------------------------------------------------------------
2 2 -- This file is a part of the SciQLop Software
3 3 -- Copyright (C) 2017, Plasma Physics Laboratory - CNRS
4 4 --
5 5 -- This program is free software; you can redistribute it and/or modify
6 6 -- it under the terms of the GNU General Public License as published by
7 7 -- the Free Software Foundation; either version 2 of the License, or
8 8 -- (at your option) any later version.
9 9 --
10 10 -- This program is distributed in the hope that it will be useful,
11 11 -- but WITHOUT ANY WARRANTY; without even the implied warranty of
12 12 -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 13 -- GNU General Public License for more details.
14 14 --
15 15 -- You should have received a copy of the GNU General Public License
16 16 -- along with this program; if not, write to the Free Software
17 17 -- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 18 -------------------------------------------------------------------------------*/
19 19 /*-- Author : Alexis Jeandet
20 20 -- Mail : alexis.jeandet@member.fsf.org
21 21 ----------------------------------------------------------------------------*/
22 22 #include "MainWindow.h"
23 23 #include "ui_MainWindow.h"
24 24
25 25 #include <DataSource/DataSourceController.h>
26 26 #include <DataSource/DataSourceWidget.h>
27 27 #include <Settings/SqpSettingsDialog.h>
28 28 #include <Settings/SqpSettingsGeneralWidget.h>
29 29 #include <SidePane/SqpSidePane.h>
30 30 #include <SqpApplication.h>
31 31 #include <Time/TimeController.h>
32 32 #include <TimeWidget/TimeWidget.h>
33 33 #include <Variable/Variable.h>
34 34 #include <Variable/VariableController.h>
35 35 #include <Visualization/VisualizationController.h>
36 36
37 37 #include <QAction>
38 38 #include <QDate>
39 #include <QDateTime>
40 39 #include <QDir>
41 40 #include <QFileDialog>
42 41 #include <QToolBar>
43 42 #include <QToolButton>
44 43 #include <memory.h>
45 44
46 45 #include "iostream"
47 46
48 47 Q_LOGGING_CATEGORY(LOG_MainWindow, "MainWindow")
49 48
50 49 namespace {
51 50 const auto LEFTMAININSPECTORWIDGETSPLITTERINDEX = 0;
52 51 const auto LEFTINSPECTORSIDEPANESPLITTERINDEX = 1;
53 52 const auto VIEWPLITTERINDEX = 2;
54 53 const auto RIGHTINSPECTORSIDEPANESPLITTERINDEX = 3;
55 54 const auto RIGHTMAININSPECTORWIDGETSPLITTERINDEX = 4;
56 55 }
57 56
58 57 class MainWindow::MainWindowPrivate {
59 58 public:
60 59 explicit MainWindowPrivate(MainWindow *mainWindow)
61 60 : m_LastOpenLeftInspectorSize{},
62 61 m_LastOpenRightInspectorSize{},
63 62 m_GeneralSettingsWidget{new SqpSettingsGeneralWidget{mainWindow}},
64 63 m_SettingsDialog{new SqpSettingsDialog{mainWindow}}
65 64 {
66 65 }
67 66
68 67 QSize m_LastOpenLeftInspectorSize;
69 68 QSize m_LastOpenRightInspectorSize;
70 69 /// General settings widget. MainWindow has the ownership
71 70 SqpSettingsGeneralWidget *m_GeneralSettingsWidget;
72 71 /// Settings dialog. MainWindow has the ownership
73 72 SqpSettingsDialog *m_SettingsDialog;
74 73 };
75 74
76 75 MainWindow::MainWindow(QWidget *parent)
77 76 : QMainWindow{parent},
78 77 m_Ui{new Ui::MainWindow},
79 78 impl{spimpl::make_unique_impl<MainWindowPrivate>(this)}
80 79 {
81 80 m_Ui->setupUi(this);
82 81
83 82 m_Ui->splitter->setCollapsible(LEFTINSPECTORSIDEPANESPLITTERINDEX, false);
84 83 m_Ui->splitter->setCollapsible(RIGHTINSPECTORSIDEPANESPLITTERINDEX, false);
85 84
86 85
87 86 auto leftSidePane = m_Ui->leftInspectorSidePane->sidePane();
88 87 auto openLeftInspectorAction = new QAction{QIcon{
89 88 ":/icones/previous.png",
90 89 },
91 90 tr("Show/hide the left inspector"), this};
92 91
93 92
94 93 auto spacerLeftTop = new QWidget{};
95 94 spacerLeftTop->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
96 95
97 96 auto spacerLeftBottom = new QWidget{};
98 97 spacerLeftBottom->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
99 98
100 99 leftSidePane->addWidget(spacerLeftTop);
101 100 leftSidePane->addAction(openLeftInspectorAction);
102 101 leftSidePane->addWidget(spacerLeftBottom);
103 102
104 103
105 104 auto rightSidePane = m_Ui->rightInspectorSidePane->sidePane();
106 105 auto openRightInspectorAction = new QAction{QIcon{
107 106 ":/icones/next.png",
108 107 },
109 108 tr("Show/hide the right inspector"), this};
110 109
111 110 auto spacerRightTop = new QWidget{};
112 111 spacerRightTop->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
113 112
114 113 auto spacerRightBottom = new QWidget{};
115 114 spacerRightBottom->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
116 115
117 116 rightSidePane->addWidget(spacerRightTop);
118 117 rightSidePane->addAction(openRightInspectorAction);
119 118 rightSidePane->addWidget(spacerRightBottom);
120 119
121 120 openLeftInspectorAction->setCheckable(true);
122 121 openRightInspectorAction->setCheckable(true);
123 122
124 123 auto openInspector = [this](bool checked, bool right, auto action) {
125 124
126 125 action->setIcon(QIcon{(checked xor right) ? ":/icones/next.png" : ":/icones/previous.png"});
127 126
128 127 auto &lastInspectorSize
129 128 = right ? impl->m_LastOpenRightInspectorSize : impl->m_LastOpenLeftInspectorSize;
130 129
131 130 auto nextInspectorSize = right ? m_Ui->rightMainInspectorWidget->size()
132 131 : m_Ui->leftMainInspectorWidget->size();
133 132
134 133 // Update of the last opened geometry
135 134 if (checked) {
136 135 lastInspectorSize = nextInspectorSize;
137 136 }
138 137
139 138 auto startSize = lastInspectorSize;
140 139 auto endSize = startSize;
141 140 endSize.setWidth(0);
142 141
143 142 auto splitterInspectorIndex
144 143 = right ? RIGHTMAININSPECTORWIDGETSPLITTERINDEX : LEFTMAININSPECTORWIDGETSPLITTERINDEX;
145 144
146 145 auto currentSizes = m_Ui->splitter->sizes();
147 146 if (checked) {
148 147 // adjust sizes individually here, e.g.
149 148 currentSizes[splitterInspectorIndex] -= lastInspectorSize.width();
150 149 currentSizes[VIEWPLITTERINDEX] += lastInspectorSize.width();
151 150 m_Ui->splitter->setSizes(currentSizes);
152 151 }
153 152 else {
154 153 // adjust sizes individually here, e.g.
155 154 currentSizes[splitterInspectorIndex] += lastInspectorSize.width();
156 155 currentSizes[VIEWPLITTERINDEX] -= lastInspectorSize.width();
157 156 m_Ui->splitter->setSizes(currentSizes);
158 157 }
159 158
160 159 };
161 160
162 161
163 162 connect(openLeftInspectorAction, &QAction::triggered,
164 163 [openInspector, openLeftInspectorAction](bool checked) {
165 164 openInspector(checked, false, openLeftInspectorAction);
166 165 });
167 166 connect(openRightInspectorAction, &QAction::triggered,
168 167 [openInspector, openRightInspectorAction](bool checked) {
169 168 openInspector(checked, true, openRightInspectorAction);
170 169 });
171 170
172 171 // //// //
173 172 // Menu //
174 173 // //// //
175 174 this->menuBar()->addAction(tr("File"));
176 175 auto toolsMenu = this->menuBar()->addMenu(tr("Tools"));
177 176 toolsMenu->addAction(tr("Settings..."), [this]() {
178 177 // Loads settings
179 178 impl->m_SettingsDialog->loadSettings();
180 179
181 180 // Open settings dialog and save settings if the dialog is accepted
182 181 if (impl->m_SettingsDialog->exec() == QDialog::Accepted) {
183 182 impl->m_SettingsDialog->saveSettings();
184 183 }
185 184
186 185 });
187 186
188 187 auto mainToolBar = this->addToolBar(QStringLiteral("MainToolBar"));
189 188
190 189 auto timeWidget = new TimeWidget{};
191 190 mainToolBar->addWidget(timeWidget);
192 191
193 192 // //////// //
194 193 // Settings //
195 194 // //////// //
196 195
197 196 // Registers "general settings" widget to the settings dialog
198 197 impl->m_SettingsDialog->registerWidget(QStringLiteral("General"),
199 198 impl->m_GeneralSettingsWidget);
200 199
201 200 // /////////// //
202 201 // Connections //
203 202 // /////////// //
204 203
205 204 // Controllers / controllers connections
206 205 connect(&sqpApp->timeController(), SIGNAL(timeUpdated(SqpDateTime)),
207 206 &sqpApp->variableController(), SLOT(onDateTimeOnSelection(SqpDateTime)));
208 207
209 208 // Widgets / controllers connections
210 209
211 210 // DataSource
212 211 connect(&sqpApp->dataSourceController(), SIGNAL(dataSourceItemSet(DataSourceItem *)),
213 212 m_Ui->dataSourceWidget, SLOT(addDataSource(DataSourceItem *)));
214 213
215 214 // Time
216 215 connect(timeWidget, SIGNAL(timeUpdated(SqpDateTime)), &sqpApp->timeController(),
217 216 SLOT(onTimeToUpdate(SqpDateTime)));
218 217
219 218 // Visualization
220 219 connect(&sqpApp->visualizationController(),
221 220 SIGNAL(variableAboutToBeDeleted(std::shared_ptr<Variable>)), m_Ui->view,
222 221 SLOT(onVariableAboutToBeDeleted(std::shared_ptr<Variable>)));
223 222
224 223 connect(&sqpApp->visualizationController(),
225 224 SIGNAL(rangeChanged(std::shared_ptr<Variable>, const SqpDateTime &)), m_Ui->view,
226 225 SLOT(onRangeChanged(std::shared_ptr<Variable>, const SqpDateTime &)));
227 226
228 227 // Widgets / widgets connections
229 228
230 229 // For the following connections, we use DirectConnection to allow each widget that can
231 230 // potentially attach a menu to the variable's menu to do so before this menu is displayed.
232 231 // The order of connections is also important, since it determines the order in which each
233 232 // widget will attach its menu
234 233 connect(
235 234 m_Ui->variableInspectorWidget,
236 235 SIGNAL(tableMenuAboutToBeDisplayed(QMenu *, const QVector<std::shared_ptr<Variable> > &)),
237 236 m_Ui->view, SLOT(attachVariableMenu(QMenu *, const QVector<std::shared_ptr<Variable> > &)),
238 237 Qt::DirectConnection);
239 238 }
240 239
241 240 MainWindow::~MainWindow()
242 241 {
243 242 }
244 243
245 244 void MainWindow::changeEvent(QEvent *e)
246 245 {
247 246 QMainWindow::changeEvent(e);
248 247 switch (e->type()) {
249 248 case QEvent::LanguageChange:
250 249 m_Ui->retranslateUi(this);
251 250 break;
252 251 default:
253 252 break;
254 253 }
255 254 }
@@ -1,69 +1,69
1 1 #ifndef SCIQLOP_IDATASERIES_H
2 2 #define SCIQLOP_IDATASERIES_H
3 3
4 4 #include <Common/MetaTypes.h>
5 5
6 6 #include <memory>
7 7
8 8 #include <QString>
9 9
10 10 template <int Dim>
11 11 class ArrayData;
12 12
13 13 struct Unit {
14 14 explicit Unit(const QString &name = {}, bool timeUnit = false)
15 15 : m_Name{name}, m_TimeUnit{timeUnit}
16 16 {
17 17 }
18 18
19 19 inline bool operator==(const Unit &other) const
20 20 {
21 21 return std::tie(m_Name, m_TimeUnit) == std::tie(other.m_Name, other.m_TimeUnit);
22 22 }
23 23 inline bool operator!=(const Unit &other) const { return !(*this == other); }
24 24
25 25 QString m_Name; ///< Unit name
26 bool m_TimeUnit; ///< The unit is a unit of time
26 bool m_TimeUnit; ///< The unit is a unit of time (UTC)
27 27 };
28 28
29 29 /**
30 30 * @brief The IDataSeries aims to declare a data series.
31 31 *
32 32 * A data series is an entity that contains at least :
33 33 * - one dataset representing the x-axis
34 34 * - one dataset representing the values
35 35 *
36 36 * Each dataset is represented by an ArrayData, and is associated with a unit.
37 37 *
38 38 * An ArrayData can be unidimensional or two-dimensional, depending on the implementation of the
39 39 * IDataSeries. The x-axis dataset is always unidimensional.
40 40 *
41 41 * @sa ArrayData
42 42 */
43 43 class IDataSeries {
44 44 public:
45 45 virtual ~IDataSeries() noexcept = default;
46 46
47 47 /// Returns the x-axis dataset
48 48 virtual std::shared_ptr<ArrayData<1> > xAxisData() = 0;
49 49
50 50 /// Returns the x-axis dataset (as const)
51 51 virtual const std::shared_ptr<ArrayData<1> > xAxisData() const = 0;
52 52
53 53 virtual Unit xAxisUnit() const = 0;
54 54
55 55 virtual Unit valuesUnit() const = 0;
56 56
57 57 virtual void merge(IDataSeries *dataSeries) = 0;
58 58
59 59 virtual std::unique_ptr<IDataSeries> clone() const = 0;
60 60
61 61 virtual void lockRead() = 0;
62 62 virtual void lockWrite() = 0;
63 63 virtual void unlock() = 0;
64 64 };
65 65
66 66 // Required for using shared_ptr in signals/slots
67 67 SCIQLOP_REGISTER_META_TYPE(IDATASERIES_PTR_REGISTRY, std::shared_ptr<IDataSeries>)
68 68
69 69 #endif // SCIQLOP_IDATASERIES_H
@@ -1,44 +1,43
1 1 #ifndef SCIQLOP_SQPDATETIME_H
2 2 #define SCIQLOP_SQPDATETIME_H
3 3
4 4 #include <QObject>
5 5
6 #include <QDateTime>
7 6 #include <QDebug>
8 7
8 #include <Common/DateUtils.h>
9 9 #include <Common/MetaTypes.h>
10 10
11 11 /**
12 12 * @brief The SqpDateTime struct holds the information of time parameters
13 13 */
14 14 struct SqpDateTime {
15 /// Start time
15 /// Start time (UTC)
16 16 double m_TStart;
17 /// End time
17 /// End time (UTC)
18 18 double m_TEnd;
19 19
20 20 bool contains(const SqpDateTime &dateTime) const noexcept
21 21 {
22 22 return (m_TStart <= dateTime.m_TStart && m_TEnd >= dateTime.m_TEnd);
23 23 }
24 24
25 25 bool intersect(const SqpDateTime &dateTime) const noexcept
26 26 {
27 27 return (m_TEnd >= dateTime.m_TStart && m_TStart <= dateTime.m_TEnd);
28 28 }
29 29 };
30 30
31 31 inline QDebug operator<<(QDebug d, SqpDateTime obj)
32 32 {
33 auto tendDateTimeStart = QDateTime::fromMSecsSinceEpoch(obj.m_TStart * 1000);
34 auto tendDateTimeEnd = QDateTime::fromMSecsSinceEpoch(obj.m_TEnd * 1000);
33 auto tendDateTimeStart = DateUtils::dateTime(obj.m_TStart);
34 auto tendDateTimeEnd = DateUtils::dateTime(obj.m_TEnd);
35 35
36 // QDebug << "ts: " << tendDateTimeStart << " te: " << tendDateTimeEnd;
37 36 d << "ts: " << tendDateTimeStart << " te: " << tendDateTimeEnd;
38 37 return d;
39 38 }
40 39
41 40 // Required for using shared_ptr in signals/slots
42 41 SCIQLOP_REGISTER_META_TYPE(SQPDATETIME_REGISTRY, SqpDateTime)
43 42
44 43 #endif // SCIQLOP_SQPDATETIME_H
@@ -1,244 +1,243
1 1 #include <Variable/Variable.h>
2 2 #include <Variable/VariableCacheController.h>
3 3 #include <Variable/VariableController.h>
4 4 #include <Variable/VariableModel.h>
5 5
6 6 #include <Data/DataProviderParameters.h>
7 7 #include <Data/IDataProvider.h>
8 8 #include <Data/IDataSeries.h>
9 9 #include <Time/TimeController.h>
10 10
11 #include <QDateTime>
12 11 #include <QMutex>
13 12 #include <QThread>
14 13 #include <QUuid>
15 14 #include <QtCore/QItemSelectionModel>
16 15
17 16 #include <unordered_map>
18 17
19 18 Q_LOGGING_CATEGORY(LOG_VariableController, "VariableController")
20 19
21 20 struct VariableController::VariableControllerPrivate {
22 21 explicit VariableControllerPrivate(VariableController *parent)
23 22 : m_WorkingMutex{},
24 23 m_VariableModel{new VariableModel{parent}},
25 24 m_VariableSelectionModel{new QItemSelectionModel{m_VariableModel, parent}},
26 25 m_VariableCacheController{std::make_unique<VariableCacheController>()}
27 26 {
28 27 }
29 28
30 29 QMutex m_WorkingMutex;
31 30 /// Variable model. The VariableController has the ownership
32 31 VariableModel *m_VariableModel;
33 32 QItemSelectionModel *m_VariableSelectionModel;
34 33
35 34
36 35 TimeController *m_TimeController{nullptr};
37 36 std::unique_ptr<VariableCacheController> m_VariableCacheController;
38 37
39 38 std::unordered_map<std::shared_ptr<Variable>, std::shared_ptr<IDataProvider> >
40 39 m_VariableToProviderMap;
41 40 std::unordered_map<std::shared_ptr<Variable>, QUuid> m_VariableToIdentifierMap;
42 41 };
43 42
44 43 VariableController::VariableController(QObject *parent)
45 44 : QObject{parent}, impl{spimpl::make_unique_impl<VariableControllerPrivate>(this)}
46 45 {
47 46 qCDebug(LOG_VariableController()) << tr("VariableController construction")
48 47 << QThread::currentThread();
49 48
50 49 connect(impl->m_VariableModel, &VariableModel::abortProgessRequested, this,
51 50 &VariableController::onAbortProgressRequested);
52 51 }
53 52
54 53 VariableController::~VariableController()
55 54 {
56 55 qCDebug(LOG_VariableController()) << tr("VariableController destruction")
57 56 << QThread::currentThread();
58 57 this->waitForFinish();
59 58 }
60 59
61 60 VariableModel *VariableController::variableModel() noexcept
62 61 {
63 62 return impl->m_VariableModel;
64 63 }
65 64
66 65 QItemSelectionModel *VariableController::variableSelectionModel() noexcept
67 66 {
68 67 return impl->m_VariableSelectionModel;
69 68 }
70 69
71 70 void VariableController::setTimeController(TimeController *timeController) noexcept
72 71 {
73 72 impl->m_TimeController = timeController;
74 73 }
75 74
76 75 void VariableController::deleteVariable(std::shared_ptr<Variable> variable) noexcept
77 76 {
78 77 if (!variable) {
79 78 qCCritical(LOG_VariableController()) << "Can't delete variable: variable is null";
80 79 return;
81 80 }
82 81
83 82 // Spreads in SciQlop that the variable will be deleted, so that potential receivers can
84 83 // make some treatments before the deletion
85 84 emit variableAboutToBeDeleted(variable);
86 85
87 86 // Deletes identifier
88 87 impl->m_VariableToIdentifierMap.erase(variable);
89 88
90 89 // Deletes provider
91 90 auto nbProvidersDeleted = impl->m_VariableToProviderMap.erase(variable);
92 91 qCDebug(LOG_VariableController())
93 92 << tr("Number of providers deleted for variable %1: %2")
94 93 .arg(variable->name(), QString::number(nbProvidersDeleted));
95 94
96 95 // Clears cache
97 96 impl->m_VariableCacheController->clear(variable);
98 97
99 98 // Deletes from model
100 99 impl->m_VariableModel->deleteVariable(variable);
101 100 }
102 101
103 102 void VariableController::deleteVariables(
104 103 const QVector<std::shared_ptr<Variable> > &variables) noexcept
105 104 {
106 105 for (auto variable : qAsConst(variables)) {
107 106 deleteVariable(variable);
108 107 }
109 108 }
110 109
111 110 void VariableController::abortProgress(std::shared_ptr<Variable> variable)
112 111 {
113 112 }
114 113
115 114 void VariableController::createVariable(const QString &name, const QVariantHash &metadata,
116 115 std::shared_ptr<IDataProvider> provider) noexcept
117 116 {
118 117
119 118 if (!impl->m_TimeController) {
120 119 qCCritical(LOG_VariableController())
121 120 << tr("Impossible to create variable: The time controller is null");
122 121 return;
123 122 }
124 123
125 124 auto dateTime = impl->m_TimeController->dateTime();
126 125
127 126 if (auto newVariable = impl->m_VariableModel->createVariable(name, dateTime, metadata)) {
128 127 auto identifier = QUuid::createUuid();
129 128
130 129 // store the provider
131 130 impl->m_VariableToProviderMap[newVariable] = provider;
132 131 impl->m_VariableToIdentifierMap[newVariable] = identifier;
133 132
134 133 auto addDateTimeAcquired = [ this, varW = std::weak_ptr<Variable>{newVariable} ](
135 134 QUuid identifier, auto dataSeriesAcquired, auto dateTimeToPutInCache)
136 135 {
137 136 if (auto variable = varW.lock()) {
138 137 auto varIdentifier = impl->m_VariableToIdentifierMap.at(variable);
139 138 if (varIdentifier == identifier) {
140 139 impl->m_VariableCacheController->addDateTime(variable, dateTimeToPutInCache);
141 140 variable->setDataSeries(dataSeriesAcquired);
142 141 emit variable->updated();
143 142 }
144 143 }
145 144 };
146 145
147 146 connect(provider.get(), &IDataProvider::dataProvided, addDateTimeAcquired);
148 147 connect(provider.get(), &IDataProvider::dataProvidedProgress, this,
149 148 &VariableController::onVariableRetrieveDataInProgress);
150 149 this->onRequestDataLoading(newVariable, dateTime);
151 150 }
152 151 }
153 152
154 153 void VariableController::onDateTimeOnSelection(const SqpDateTime &dateTime)
155 154 {
156 155 qCDebug(LOG_VariableController()) << "VariableController::onDateTimeOnSelection"
157 156 << QThread::currentThread()->objectName();
158 157 auto selectedRows = impl->m_VariableSelectionModel->selectedRows();
159 158
160 159 for (const auto &selectedRow : qAsConst(selectedRows)) {
161 160 if (auto selectedVariable = impl->m_VariableModel->variable(selectedRow.row())) {
162 161 selectedVariable->setDateTime(dateTime);
163 162 this->onRequestDataLoading(selectedVariable, dateTime);
164 163
165 164 // notify that rescale operation has to be done
166 165 emit rangeChanged(selectedVariable, dateTime);
167 166 }
168 167 }
169 168 }
170 169
171 170 void VariableController::onVariableRetrieveDataInProgress(QUuid identifier, double progress)
172 171 {
173 172 auto findReply = [identifier](const auto &entry) { return identifier == entry.second; };
174 173
175 174 auto end = impl->m_VariableToIdentifierMap.cend();
176 175 auto it = std::find_if(impl->m_VariableToIdentifierMap.cbegin(), end, findReply);
177 176 if (it != end) {
178 177 impl->m_VariableModel->setDataProgress(it->first, progress);
179 178 }
180 179 }
181 180
182 181 void VariableController::onAbortProgressRequested(std::shared_ptr<Variable> variable)
183 182 {
184 183 qCDebug(LOG_VariableController()) << "TORM: VariableController::onAbortProgressRequested"
185 184 << QThread::currentThread()->objectName();
186 185
187 186 auto it = impl->m_VariableToIdentifierMap.find(variable);
188 187 if (it != impl->m_VariableToIdentifierMap.cend()) {
189 188 impl->m_VariableToProviderMap.at(variable)->requestDataAborting(it->second);
190 189 }
191 190 else {
192 191 qCWarning(LOG_VariableController())
193 192 << tr("Aborting progression of inexistant variable detected !!!")
194 193 << QThread::currentThread()->objectName();
195 194 }
196 195 }
197 196
198 197
199 198 void VariableController::onRequestDataLoading(std::shared_ptr<Variable> variable,
200 199 const SqpDateTime &dateTime)
201 200 {
202 201 qCDebug(LOG_VariableController()) << "VariableController::onRequestDataLoading"
203 202 << QThread::currentThread()->objectName();
204 203 // we want to load data of the variable for the dateTime.
205 204 // First we check if the cache contains some of them.
206 205 // For the other, we ask the provider to give them.
207 206 if (variable) {
208 207
209 208 auto dateTimeListNotInCache
210 209 = impl->m_VariableCacheController->provideNotInCacheDateTimeList(variable, dateTime);
211 210
212 211 if (!dateTimeListNotInCache.empty()) {
213 212 // Ask the provider for each data on the dateTimeListNotInCache
214 213 auto identifier = impl->m_VariableToIdentifierMap.at(variable);
215 214 impl->m_VariableToProviderMap.at(variable)->requestDataLoading(
216 215 identifier,
217 216 DataProviderParameters{std::move(dateTimeListNotInCache), variable->metadata()});
218 217 }
219 218 else {
220 219 emit variable->updated();
221 220 }
222 221 }
223 222 else {
224 223 qCCritical(LOG_VariableController()) << tr("Impossible to load data of a variable null");
225 224 }
226 225 }
227 226
228 227
229 228 void VariableController::initialize()
230 229 {
231 230 qCDebug(LOG_VariableController()) << tr("VariableController init") << QThread::currentThread();
232 231 impl->m_WorkingMutex.lock();
233 232 qCDebug(LOG_VariableController()) << tr("VariableController init END");
234 233 }
235 234
236 235 void VariableController::finalize()
237 236 {
238 237 impl->m_WorkingMutex.unlock();
239 238 }
240 239
241 240 void VariableController::waitForFinish()
242 241 {
243 242 QMutexLocker locker{&impl->m_WorkingMutex};
244 243 }
@@ -1,248 +1,249
1 1 #include <Variable/Variable.h>
2 2 #include <Variable/VariableModel.h>
3 3
4 #include <Common/DateUtils.h>
5
4 6 #include <Data/IDataSeries.h>
5 7
6 #include <QDateTime>
7 8 #include <QSize>
8 9 #include <unordered_map>
9 10
10 11 Q_LOGGING_CATEGORY(LOG_VariableModel, "VariableModel")
11 12
12 13 namespace {
13 14
14 15 // Column indexes
15 16 const auto NAME_COLUMN = 0;
16 17 const auto TSTART_COLUMN = 1;
17 18 const auto TEND_COLUMN = 2;
18 19 const auto NB_COLUMNS = 3;
19 20
20 21 // Column properties
21 22 const auto DEFAULT_HEIGHT = 25;
22 23 const auto DEFAULT_WIDTH = 100;
23 24
24 25 struct ColumnProperties {
25 26 ColumnProperties(const QString &name = {}, int width = DEFAULT_WIDTH,
26 27 int height = DEFAULT_HEIGHT)
27 28 : m_Name{name}, m_Width{width}, m_Height{height}
28 29 {
29 30 }
30 31
31 32 QString m_Name;
32 33 int m_Width;
33 34 int m_Height;
34 35 };
35 36
36 37 const auto COLUMN_PROPERTIES
37 38 = QHash<int, ColumnProperties>{{NAME_COLUMN, {QObject::tr("Name")}},
38 39 {TSTART_COLUMN, {QObject::tr("tStart"), 180}},
39 40 {TEND_COLUMN, {QObject::tr("tEnd"), 180}}};
40 41
41 42 /// Format for datetimes
42 43 const auto DATETIME_FORMAT = QStringLiteral("dd/MM/yyyy \nhh:mm:ss:zzz");
43 44
44 45
45 46 } // namespace
46 47
47 48 struct VariableModel::VariableModelPrivate {
48 49 /// Variables created in SciQlop
49 50 std::vector<std::shared_ptr<Variable> > m_Variables;
50 51 std::unordered_map<std::shared_ptr<Variable>, double> m_VariableToProgress;
51 52
52 53 /// Return the row index of the variable. -1 if it's not found
53 54 int indexOfVariable(Variable *variable) const noexcept;
54 55 };
55 56
56 57 VariableModel::VariableModel(QObject *parent)
57 58 : QAbstractTableModel{parent}, impl{spimpl::make_unique_impl<VariableModelPrivate>()}
58 59 {
59 60 }
60 61
61 62 std::shared_ptr<Variable> VariableModel::createVariable(const QString &name,
62 63 const SqpDateTime &dateTime,
63 64 const QVariantHash &metadata) noexcept
64 65 {
65 66 auto insertIndex = rowCount();
66 67 beginInsertRows({}, insertIndex, insertIndex);
67 68
68 69 auto variable = std::make_shared<Variable>(name, dateTime, metadata);
69 70
70 71 impl->m_Variables.push_back(variable);
71 72 connect(variable.get(), &Variable::updated, this, &VariableModel::onVariableUpdated);
72 73
73 74 endInsertRows();
74 75
75 76 return variable;
76 77 }
77 78
78 79 void VariableModel::deleteVariable(std::shared_ptr<Variable> variable) noexcept
79 80 {
80 81 if (!variable) {
81 82 qCCritical(LOG_Variable()) << "Can't delete a null variable from the model";
82 83 return;
83 84 }
84 85
85 86 // Finds variable in the model
86 87 auto begin = impl->m_Variables.cbegin();
87 88 auto end = impl->m_Variables.cend();
88 89 auto it = std::find(begin, end, variable);
89 90 if (it != end) {
90 91 auto removeIndex = std::distance(begin, it);
91 92
92 93 // Deletes variable
93 94 beginRemoveRows({}, removeIndex, removeIndex);
94 95 impl->m_Variables.erase(it);
95 96 endRemoveRows();
96 97 }
97 98 else {
98 99 qCritical(LOG_VariableModel())
99 100 << tr("Can't delete variable %1 from the model: the variable is not in the model")
100 101 .arg(variable->name());
101 102 }
102 103
103 104 // Removes variable from progress map
104 105 impl->m_VariableToProgress.erase(variable);
105 106 }
106 107
107 108
108 109 std::shared_ptr<Variable> VariableModel::variable(int index) const
109 110 {
110 111 return (index >= 0 && index < impl->m_Variables.size()) ? impl->m_Variables[index] : nullptr;
111 112 }
112 113
113 114 void VariableModel::setDataProgress(std::shared_ptr<Variable> variable, double progress)
114 115 {
115 116 if (progress > 0.0) {
116 117 impl->m_VariableToProgress[variable] = progress;
117 118 }
118 119 else {
119 120 impl->m_VariableToProgress.erase(variable);
120 121 }
121 122 auto modelIndex = createIndex(impl->indexOfVariable(variable.get()), NAME_COLUMN);
122 123
123 124 emit dataChanged(modelIndex, modelIndex);
124 125 }
125 126
126 127 int VariableModel::columnCount(const QModelIndex &parent) const
127 128 {
128 129 Q_UNUSED(parent);
129 130
130 131 return NB_COLUMNS;
131 132 }
132 133
133 134 int VariableModel::rowCount(const QModelIndex &parent) const
134 135 {
135 136 Q_UNUSED(parent);
136 137
137 138 return impl->m_Variables.size();
138 139 }
139 140
140 141 QVariant VariableModel::data(const QModelIndex &index, int role) const
141 142 {
142 143 if (!index.isValid()) {
143 144 return QVariant{};
144 145 }
145 146
146 147 if (index.row() < 0 || index.row() >= rowCount()) {
147 148 return QVariant{};
148 149 }
149 150
150 151 if (role == Qt::DisplayRole) {
151 152 if (auto variable = impl->m_Variables.at(index.row()).get()) {
152 153 /// Lambda function that builds the variant to return for a time value
153 auto dateTimeVariant = [](double time) {
154 auto dateTime = QDateTime::fromMSecsSinceEpoch(time * 1000.);
154 auto dateTimeVariant = [](double secs) {
155 auto dateTime = DateUtils::dateTime(secs);
155 156 return dateTime.toString(DATETIME_FORMAT);
156 157 };
157 158
158 159 switch (index.column()) {
159 160 case NAME_COLUMN:
160 161 return variable->name();
161 162 case TSTART_COLUMN:
162 163 return dateTimeVariant(variable->dateTime().m_TStart);
163 164 case TEND_COLUMN:
164 165 return dateTimeVariant(variable->dateTime().m_TEnd);
165 166 default:
166 167 // No action
167 168 break;
168 169 }
169 170
170 171 qWarning(LOG_VariableModel())
171 172 << tr("Can't get data (unknown column %1)").arg(index.column());
172 173 }
173 174 else {
174 175 qWarning(LOG_VariableModel()) << tr("Can't get data (no variable)");
175 176 }
176 177 }
177 178 else if (role == VariableRoles::ProgressRole) {
178 179 if (auto variable = impl->m_Variables.at(index.row())) {
179 180
180 181 auto it = impl->m_VariableToProgress.find(variable);
181 182 if (it != impl->m_VariableToProgress.cend()) {
182 183 return it->second;
183 184 }
184 185 }
185 186 }
186 187
187 188 return QVariant{};
188 189 }
189 190
190 191 QVariant VariableModel::headerData(int section, Qt::Orientation orientation, int role) const
191 192 {
192 193 if (role != Qt::DisplayRole && role != Qt::SizeHintRole) {
193 194 return QVariant{};
194 195 }
195 196
196 197 if (orientation == Qt::Horizontal) {
197 198 auto propertiesIt = COLUMN_PROPERTIES.find(section);
198 199 if (propertiesIt != COLUMN_PROPERTIES.cend()) {
199 200 // Role is either DisplayRole or SizeHintRole
200 201 return (role == Qt::DisplayRole)
201 202 ? QVariant{propertiesIt->m_Name}
202 203 : QVariant{QSize{propertiesIt->m_Width, propertiesIt->m_Height}};
203 204 }
204 205 else {
205 206 qWarning(LOG_VariableModel())
206 207 << tr("Can't get header data (unknown column %1)").arg(section);
207 208 }
208 209 }
209 210
210 211 return QVariant{};
211 212 }
212 213
213 214 void VariableModel::abortProgress(const QModelIndex &index)
214 215 {
215 216 if (auto variable = impl->m_Variables.at(index.row())) {
216 217 emit abortProgessRequested(variable);
217 218 }
218 219 }
219 220
220 221 void VariableModel::onVariableUpdated() noexcept
221 222 {
222 223 // Finds variable that has been updated in the model
223 224 if (auto updatedVariable = dynamic_cast<Variable *>(sender())) {
224 225 auto updatedVariableIndex = impl->indexOfVariable(updatedVariable);
225 226
226 227 if (updatedVariableIndex > -1) {
227 228 emit dataChanged(createIndex(updatedVariableIndex, 0),
228 229 createIndex(updatedVariableIndex, columnCount() - 1));
229 230 }
230 231 }
231 232 }
232 233
233 234 int VariableModel::VariableModelPrivate::indexOfVariable(Variable *variable) const noexcept
234 235 {
235 236 auto begin = std::cbegin(m_Variables);
236 237 auto end = std::cend(m_Variables);
237 238 auto it
238 239 = std::find_if(begin, end, [variable](const auto &var) { return var.get() == variable; });
239 240
240 241 if (it != end) {
241 242 // Gets the index of the variable in the model: we assume here that views have the same
242 243 // order as the model
243 244 return std::distance(begin, it);
244 245 }
245 246 else {
246 247 return -1;
247 248 }
248 249 }
@@ -1,48 +1,50
1 1 #include "TimeWidget/TimeWidget.h"
2 2 #include "ui_TimeWidget.h"
3 3
4 #include <Common/DateUtils.h>
4 5 #include <SqpApplication.h>
5 6 #include <Time/TimeController.h>
6 7
7 8 TimeWidget::TimeWidget(QWidget *parent) : QWidget{parent}, ui{new Ui::TimeWidget}
8 9 {
9 10 ui->setupUi(this);
10 11
11 12 ui->applyToolButton->setIcon(sqpApp->style()->standardIcon(QStyle::SP_DialogApplyButton));
12 13
13 14 // Connection
14 15 connect(ui->startDateTimeEdit, &QDateTimeEdit::dateTimeChanged, this,
15 16 &TimeWidget::onTimeUpdateRequested);
16 17
17 18 connect(ui->endDateTimeEdit, &QDateTimeEdit::dateTimeChanged, this,
18 19 &TimeWidget::onTimeUpdateRequested);
19 20
20 21
21 22 connect(ui->applyToolButton, &QToolButton::clicked, &sqpApp->timeController(),
22 23 &TimeController::onTimeNotify);
23 24
24 25 // Initialisation
25 ui->startDateTimeEdit->setDateTime(
26 QDateTime::currentDateTime().addSecs(-3600)); // one hour berefore
27 ui->endDateTimeEdit->setDateTime(QDateTime::currentDateTime());
26 auto endDateTime = QDateTime::currentDateTimeUtc();
27 auto startDateTime = endDateTime.addSecs(-3600); // one hour before
28
29 ui->startDateTimeEdit->setDateTime(startDateTime);
30 ui->endDateTimeEdit->setDateTime(endDateTime);
31
32 auto dateTime = SqpDateTime{DateUtils::secondsSinceEpoch(startDateTime),
33 DateUtils::secondsSinceEpoch(endDateTime)};
28 34
29 auto dateTime
30 = SqpDateTime{QDateTime::currentDateTime().addSecs(-3600).toMSecsSinceEpoch() / 1000.0,
31 QDateTime::currentDateTime().toMSecsSinceEpoch() / 1000.0};
32 35 sqpApp->timeController().onTimeToUpdate(dateTime);
33 36 }
34 37
35 38
36 39 TimeWidget::~TimeWidget()
37 40 {
38 41 delete ui;
39 42 }
40 43
41 44 void TimeWidget::onTimeUpdateRequested()
42 45 {
43 auto dateTime = SqpDateTime{
44 static_cast<double>(ui->startDateTimeEdit->dateTime().toMSecsSinceEpoch() / 1000.),
45 static_cast<double>(ui->endDateTimeEdit->dateTime().toMSecsSinceEpoch()) / 1000.};
46 auto dateTime = SqpDateTime{DateUtils::secondsSinceEpoch(ui->startDateTimeEdit->dateTime()),
47 DateUtils::secondsSinceEpoch(ui->endDateTimeEdit->dateTime())};
46 48
47 49 emit timeUpdated(std::move(dateTime));
48 50 }
@@ -1,161 +1,162
1 1 #include "Visualization/VisualizationGraphHelper.h"
2 2 #include "Visualization/qcustomplot.h"
3 3
4 4 #include <Data/ScalarSeries.h>
5 5
6 6 #include <Variable/Variable.h>
7 7
8 8 Q_LOGGING_CATEGORY(LOG_VisualizationGraphHelper, "VisualizationGraphHelper")
9 9
10 10 namespace {
11 11
12 12 class SqpDataContainer : public QCPGraphDataContainer {
13 13 public:
14 14 void appendGraphData(const QCPGraphData &data) { mData.append(data); }
15 15 };
16 16
17 17
18 18 /// Format for datetimes on a axis
19 19 const auto DATETIME_TICKER_FORMAT = QStringLiteral("yyyy/MM/dd \nhh:mm:ss");
20 20
21 21 /// Generates the appropriate ticker for an axis, depending on whether the axis displays time or
22 22 /// non-time data
23 23 QSharedPointer<QCPAxisTicker> axisTicker(bool isTimeAxis)
24 24 {
25 25 if (isTimeAxis) {
26 26 auto dateTicker = QSharedPointer<QCPAxisTickerDateTime>::create();
27 27 dateTicker->setDateTimeFormat(DATETIME_TICKER_FORMAT);
28 dateTicker->setDateTimeSpec(Qt::UTC);
28 29
29 30 return dateTicker;
30 31 }
31 32 else {
32 33 // default ticker
33 34 return QSharedPointer<QCPAxisTicker>::create();
34 35 }
35 36 }
36 37
37 38 void updateScalarData(QCPAbstractPlottable *component, ScalarSeries &scalarSeries,
38 39 const SqpDateTime &dateTime)
39 40 {
40 41 qCDebug(LOG_VisualizationGraphHelper()) << "TORM: updateScalarData"
41 42 << QThread::currentThread()->objectName();
42 43 if (auto qcpGraph = dynamic_cast<QCPGraph *>(component)) {
43 44 scalarSeries.lockRead();
44 45 {
45 46 const auto &xData = scalarSeries.xAxisData()->cdata();
46 47 const auto &valuesData = scalarSeries.valuesData()->cdata();
47 48
48 49 auto xDataBegin = xData.cbegin();
49 50 auto xDataEnd = xData.cend();
50 51
51 52 qCInfo(LOG_VisualizationGraphHelper()) << "TORM: Current points in cache"
52 53 << xData.count();
53 54
54 55 auto sqpDataContainer = QSharedPointer<SqpDataContainer>::create();
55 56 qcpGraph->setData(sqpDataContainer);
56 57
57 58 auto lowerIt = std::lower_bound(xDataBegin, xDataEnd, dateTime.m_TStart);
58 59 auto upperIt = std::upper_bound(xDataBegin, xDataEnd, dateTime.m_TEnd);
59 60 auto distance = std::distance(xDataBegin, lowerIt);
60 61
61 62 auto valuesDataIt = valuesData.cbegin() + distance;
62 63 for (auto xAxisDataIt = lowerIt; xAxisDataIt != upperIt;
63 64 ++xAxisDataIt, ++valuesDataIt) {
64 65 sqpDataContainer->appendGraphData(QCPGraphData(*xAxisDataIt, *valuesDataIt));
65 66 }
66 67
67 68 qCInfo(LOG_VisualizationGraphHelper()) << "TORM: Current points displayed"
68 69 << sqpDataContainer->size();
69 70 }
70 71 scalarSeries.unlock();
71 72
72 73
73 74 // Display all data
74 75 component->parentPlot()->replot();
75 76 }
76 77 else {
77 78 /// @todo DEBUG
78 79 }
79 80 }
80 81
81 82 QCPAbstractPlottable *createScalarSeriesComponent(ScalarSeries &scalarSeries, QCustomPlot &plot,
82 83 const SqpDateTime &dateTime)
83 84 {
84 85 auto component = plot.addGraph();
85 86
86 87 if (component) {
87 88 // // Graph data
88 89 component->setData(scalarSeries.xAxisData()->data(), scalarSeries.valuesData()->data(),
89 90 true);
90 91
91 92 updateScalarData(component, scalarSeries, dateTime);
92 93
93 94 // Axes properties
94 95 /// @todo : for the moment, no control is performed on the axes: the units and the tickers
95 96 /// are fixed for the default x-axis and y-axis of the plot, and according to the new graph
96 97
97 98 auto setAxisProperties = [](auto axis, const auto &unit) {
98 99 // label (unit name)
99 100 axis->setLabel(unit.m_Name);
100 101
101 102 // ticker (depending on the type of unit)
102 103 axis->setTicker(axisTicker(unit.m_TimeUnit));
103 104 };
104 105 setAxisProperties(plot.xAxis, scalarSeries.xAxisUnit());
105 106 setAxisProperties(plot.yAxis, scalarSeries.valuesUnit());
106 107
107 108 // Display all data
108 109 component->rescaleAxes();
109 110 plot.replot();
110 111 }
111 112 else {
112 113 qCDebug(LOG_VisualizationGraphHelper())
113 114 << QObject::tr("Can't create graph for the scalar series");
114 115 }
115 116
116 117 return component;
117 118 }
118 119
119 120 } // namespace
120 121
121 122 QVector<QCPAbstractPlottable *> VisualizationGraphHelper::create(std::shared_ptr<Variable> variable,
122 123 QCustomPlot &plot) noexcept
123 124 {
124 125 auto result = QVector<QCPAbstractPlottable *>{};
125 126
126 127 if (variable) {
127 128 // Gets the data series of the variable to call the creation of the right components
128 129 // according to its type
129 130 if (auto scalarSeries = dynamic_cast<ScalarSeries *>(variable->dataSeries())) {
130 131 result.append(createScalarSeriesComponent(*scalarSeries, plot, variable->dateTime()));
131 132 }
132 133 else {
133 134 qCDebug(LOG_VisualizationGraphHelper())
134 135 << QObject::tr("Can't create graph plottables : unmanaged data series type");
135 136 }
136 137 }
137 138 else {
138 139 qCDebug(LOG_VisualizationGraphHelper())
139 140 << QObject::tr("Can't create graph plottables : the variable is null");
140 141 }
141 142
142 143 return result;
143 144 }
144 145
145 146 void VisualizationGraphHelper::updateData(QVector<QCPAbstractPlottable *> plotableVect,
146 147 IDataSeries *dataSeries, const SqpDateTime &dateTime)
147 148 {
148 149 if (auto scalarSeries = dynamic_cast<ScalarSeries *>(dataSeries)) {
149 150 if (plotableVect.size() == 1) {
150 151 updateScalarData(plotableVect.at(0), *scalarSeries, dateTime);
151 152 }
152 153 else {
153 154 qCCritical(LOG_VisualizationGraphHelper()) << QObject::tr(
154 155 "Can't update Data of a scalarSeries because there is not only one component "
155 156 "associated");
156 157 }
157 158 }
158 159 else {
159 160 /// @todo DEBUG
160 161 }
161 162 }
@@ -1,118 +1,123
1 1 #include "Visualization/VisualizationGraphRenderingDelegate.h"
2 2 #include "Visualization/qcustomplot.h"
3 3
4 #include <Common/DateUtils.h>
5
4 6 namespace {
5 7
6 8 const auto DATETIME_FORMAT = QStringLiteral("yyyy/MM/dd hh:mm:ss:zzz");
7 9
8 10 const auto TEXT_TRACER_FORMAT = QStringLiteral("key: %1\nvalue: %2");
9 11
10 12 /// Timeout after which a tracer is displayed
11 13 const auto TRACER_TIMEOUT = 500;
12 14
13 15 /// Formats a data value according to the axis on which it is present
14 16 QString formatValue(double value, const QCPAxis &axis)
15 17 {
16 18 // If the axis is a time axis, formats the value as a date
17 return qSharedPointerDynamicCast<QCPAxisTickerDateTime>(axis.ticker())
18 ? QCPAxisTickerDateTime::keyToDateTime(value).toString(DATETIME_FORMAT)
19 : QString::number(value);
19 if (auto axisTicker = qSharedPointerDynamicCast<QCPAxisTickerDateTime>(axis.ticker())) {
20 return DateUtils::dateTime(value, axisTicker->dateTimeSpec()).toString(DATETIME_FORMAT);
21 }
22 else {
23 return QString::number(value);
24 }
20 25 }
21 26
22 27 void initPointTracerStyle(QCPItemTracer &tracer) noexcept
23 28 {
24 29 tracer.setInterpolating(false);
25 30 tracer.setStyle(QCPItemTracer::tsPlus);
26 31 tracer.setPen(QPen(Qt::black));
27 32 tracer.setBrush(Qt::black);
28 33 tracer.setSize(10);
29 34 }
30 35
31 36 void initTextTracerStyle(QCPItemText &tracer) noexcept
32 37 {
33 38 tracer.setPen(QPen{Qt::gray});
34 39 tracer.setBrush(Qt::white);
35 40 tracer.setPadding(QMargins{6, 6, 6, 6});
36 41 tracer.setPositionAlignment(Qt::AlignTop | Qt::AlignLeft);
37 42 tracer.setTextAlignment(Qt::AlignLeft);
38 43 }
39 44
40 45 } // namespace
41 46
42 47 struct VisualizationGraphRenderingDelegate::VisualizationGraphRenderingDelegatePrivate {
43 48 explicit VisualizationGraphRenderingDelegatePrivate(QCustomPlot &plot)
44 49 : m_Plot{plot},
45 50 m_PointTracer{new QCPItemTracer{&plot}},
46 51 m_TextTracer{new QCPItemText{&plot}},
47 52 m_TracerTimer{}
48 53 {
49 54 initPointTracerStyle(*m_PointTracer);
50 55 initTextTracerStyle(*m_TextTracer);
51 56
52 57 m_TracerTimer.setInterval(TRACER_TIMEOUT);
53 58 m_TracerTimer.setSingleShot(true);
54 59 }
55 60
56 61 QCustomPlot &m_Plot;
57 62 QCPItemTracer *m_PointTracer;
58 63 QCPItemText *m_TextTracer;
59 64 QTimer m_TracerTimer;
60 65 };
61 66
62 67 VisualizationGraphRenderingDelegate::VisualizationGraphRenderingDelegate(QCustomPlot &plot)
63 68 : impl{spimpl::make_unique_impl<VisualizationGraphRenderingDelegatePrivate>(plot)}
64 69 {
65 70 }
66 71
67 72 void VisualizationGraphRenderingDelegate::onMouseMove(QMouseEvent *event) noexcept
68 73 {
69 74 // Cancels pending refresh
70 75 impl->m_TracerTimer.disconnect();
71 76
72 77 auto showTracers = [ eventPos = event->pos(), this ]()
73 78 {
74 79 // Lambda function to display a tracer
75 80 auto displayTracer = [this](auto &tracer) {
76 81 // Tracer is set on top of the plot's main layer
77 82 tracer.setLayer(impl->m_Plot.layer("main"));
78 83 tracer.setVisible(true);
79 84 impl->m_Plot.replot();
80 85 };
81 86
82 87 // Reinits tracers
83 88 impl->m_PointTracer->setGraph(nullptr);
84 89 impl->m_PointTracer->setVisible(false);
85 90 impl->m_TextTracer->setVisible(false);
86 91 impl->m_Plot.replot();
87 92
88 93 // Gets the graph under the mouse position
89 94 if (auto graph = qobject_cast<QCPGraph *>(impl->m_Plot.plottableAt(eventPos))) {
90 95 auto mouseKey = graph->keyAxis()->pixelToCoord(eventPos.x());
91 96 auto graphData = graph->data();
92 97
93 98 // Gets the closest data point to the mouse
94 99 auto graphDataIt = graphData->findBegin(mouseKey);
95 100 if (graphDataIt != graphData->constEnd()) {
96 101 auto key = formatValue(graphDataIt->key, *graph->keyAxis());
97 102 auto value = formatValue(graphDataIt->value, *graph->valueAxis());
98 103 impl->m_TextTracer->setText(TEXT_TRACER_FORMAT.arg(key, value));
99 104
100 105 // Displays point tracer
101 106 impl->m_PointTracer->setGraph(graph);
102 107 impl->m_PointTracer->setGraphKey(mouseKey);
103 108 displayTracer(*impl->m_PointTracer);
104 109
105 110 // Displays text tracer
106 111 auto tracerPosition = impl->m_TextTracer->position;
107 112 tracerPosition->setAxes(graph->keyAxis(), graph->valueAxis());
108 113 tracerPosition->setCoords(impl->m_PointTracer->position->key(),
109 114 impl->m_PointTracer->position->value());
110 115 displayTracer(*impl->m_TextTracer);
111 116 }
112 117 }
113 118 };
114 119
115 120 // Starts the timer to display tracers at timeout
116 121 QObject::connect(&impl->m_TracerTimer, &QTimer::timeout, showTracers);
117 122 impl->m_TracerTimer.start();
118 123 }
@@ -1,92 +1,98
1 1 <?xml version="1.0" encoding="UTF-8"?>
2 2 <ui version="4.0">
3 3 <class>TimeWidget</class>
4 4 <widget class="QWidget" name="TimeWidget">
5 5 <property name="geometry">
6 6 <rect>
7 7 <x>0</x>
8 8 <y>0</y>
9 9 <width>716</width>
10 10 <height>48</height>
11 11 </rect>
12 12 </property>
13 13 <property name="sizePolicy">
14 14 <sizepolicy hsizetype="Fixed" vsizetype="Preferred">
15 15 <horstretch>0</horstretch>
16 16 <verstretch>0</verstretch>
17 17 </sizepolicy>
18 18 </property>
19 19 <property name="windowTitle">
20 20 <string>Form</string>
21 21 </property>
22 22 <layout class="QHBoxLayout" name="horizontalLayout_2">
23 23 <item>
24 24 <widget class="QLabel" name="label">
25 25 <property name="sizePolicy">
26 26 <sizepolicy hsizetype="Minimum" vsizetype="Preferred">
27 27 <horstretch>0</horstretch>
28 28 <verstretch>0</verstretch>
29 29 </sizepolicy>
30 30 </property>
31 31 <property name="text">
32 32 <string>TStart :</string>
33 33 </property>
34 34 </widget>
35 35 </item>
36 36 <item>
37 37 <widget class="QDateTimeEdit" name="startDateTimeEdit">
38 38 <property name="sizePolicy">
39 39 <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
40 40 <horstretch>0</horstretch>
41 41 <verstretch>0</verstretch>
42 42 </sizepolicy>
43 43 </property>
44 44 <property name="displayFormat">
45 45 <string>dd/MM/yyyy HH:mm:ss:zzz</string>
46 46 </property>
47 47 <property name="calendarPopup">
48 48 <bool>true</bool>
49 49 </property>
50 <property name="timeSpec">
51 <enum>Qt::UTC</enum>
52 </property>
50 53 </widget>
51 54 </item>
52 55 <item>
53 56 <widget class="QLabel" name="label_2">
54 57 <property name="sizePolicy">
55 58 <sizepolicy hsizetype="Minimum" vsizetype="Preferred">
56 59 <horstretch>0</horstretch>
57 60 <verstretch>0</verstretch>
58 61 </sizepolicy>
59 62 </property>
60 63 <property name="text">
61 64 <string>TEnd :</string>
62 65 </property>
63 66 </widget>
64 67 </item>
65 68 <item>
66 69 <widget class="QDateTimeEdit" name="endDateTimeEdit">
67 70 <property name="sizePolicy">
68 71 <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
69 72 <horstretch>0</horstretch>
70 73 <verstretch>0</verstretch>
71 74 </sizepolicy>
72 75 </property>
73 76 <property name="displayFormat">
74 77 <string>dd/MM/yyyy HH:mm:ss:zzz</string>
75 78 </property>
76 79 <property name="calendarPopup">
77 80 <bool>true</bool>
78 81 </property>
82 <property name="timeSpec">
83 <enum>Qt::UTC</enum>
84 </property>
79 85 </widget>
80 86 </item>
81 87 <item>
82 88 <widget class="QToolButton" name="applyToolButton">
83 89 <property name="text">
84 90 <string>...</string>
85 91 </property>
86 92 </widget>
87 93 </item>
88 94 </layout>
89 95 </widget>
90 96 <resources/>
91 97 <connections/>
92 98 </ui>
@@ -1,147 +1,148
1 1 #include "AmdaProvider.h"
2 2 #include "AmdaDefs.h"
3 3 #include "AmdaResultParser.h"
4 4
5 #include <Common/DateUtils.h>
5 6 #include <Data/DataProviderParameters.h>
6 7 #include <Network/NetworkController.h>
7 8 #include <SqpApplication.h>
8 9 #include <Variable/Variable.h>
9 10
10 11 #include <QNetworkAccessManager>
11 12 #include <QNetworkReply>
12 13 #include <QTemporaryFile>
13 14 #include <QThread>
14 15
15 16 Q_LOGGING_CATEGORY(LOG_AmdaProvider, "AmdaProvider")
16 17
17 18 namespace {
18 19
19 20 /// URL format for a request on AMDA server. The parameters are as follows:
20 21 /// - %1: start date
21 22 /// - %2: end date
22 23 /// - %3: parameter id
23 24 const auto AMDA_URL_FORMAT = QStringLiteral(
24 25 "http://amda.irap.omp.eu/php/rest/"
25 26 "getParameter.php?startTime=%1&stopTime=%2&parameterID=%3&outputFormat=ASCII&"
26 27 "timeFormat=ISO8601&gzip=0");
27 28
28 29 /// Dates format passed in the URL (e.g 2013-09-23T09:00)
29 30 const auto AMDA_TIME_FORMAT = QStringLiteral("yyyy-MM-ddThh:mm:ss");
30 31
31 32 /// Formats a time to a date that can be passed in URL
32 33 QString dateFormat(double sqpDateTime) noexcept
33 34 {
34 auto dateTime = QDateTime::fromMSecsSinceEpoch(sqpDateTime * 1000.);
35 auto dateTime = DateUtils::dateTime(sqpDateTime);
35 36 return dateTime.toString(AMDA_TIME_FORMAT);
36 37 }
37 38
38 39 } // namespace
39 40
40 41 AmdaProvider::AmdaProvider()
41 42 {
42 43 qCDebug(LOG_AmdaProvider()) << tr("AmdaProvider::AmdaProvider") << QThread::currentThread();
43 44 if (auto app = sqpApp) {
44 45 auto &networkController = app->networkController();
45 46 connect(this, SIGNAL(requestConstructed(QNetworkRequest, QUuid,
46 47 std::function<void(QNetworkReply *, QUuid)>)),
47 48 &networkController,
48 49 SLOT(onProcessRequested(QNetworkRequest, QUuid,
49 50 std::function<void(QNetworkReply *, QUuid)>)));
50 51
51 52
52 53 connect(&sqpApp->networkController(), SIGNAL(replyDownloadProgress(QUuid, double)), this,
53 54 SIGNAL(dataProvidedProgress(QUuid, double)));
54 55 }
55 56 }
56 57
57 58 void AmdaProvider::requestDataLoading(QUuid token, const DataProviderParameters &parameters)
58 59 {
59 60 // NOTE: Try to use multithread if possible
60 61 const auto times = parameters.m_Times;
61 62 const auto data = parameters.m_Data;
62 63 for (const auto &dateTime : qAsConst(times)) {
63 64 retrieveData(token, dateTime, data);
64 65 }
65 66 }
66 67
67 68 void AmdaProvider::requestDataAborting(QUuid identifier)
68 69 {
69 70 if (auto app = sqpApp) {
70 71 auto &networkController = app->networkController();
71 72 networkController.onReplyCanceled(identifier);
72 73 }
73 74 }
74 75
75 76 void AmdaProvider::retrieveData(QUuid token, const SqpDateTime &dateTime, const QVariantHash &data)
76 77 {
77 78 // Retrieves product ID from data: if the value is invalid, no request is made
78 79 auto productId = data.value(AMDA_XML_ID_KEY).toString();
79 80 if (productId.isNull()) {
80 81 qCCritical(LOG_AmdaProvider()) << tr("Can't retrieve data: unknown product id");
81 82 return;
82 83 }
83 84 qCInfo(LOG_AmdaProvider()) << tr("AmdaProvider::retrieveData") << dateTime;
84 85
85 86 // /////////// //
86 87 // Creates URL //
87 88 // /////////// //
88 89
89 90 auto startDate = dateFormat(dateTime.m_TStart);
90 91 auto endDate = dateFormat(dateTime.m_TEnd);
91 92
92 93 auto url = QUrl{QString{AMDA_URL_FORMAT}.arg(startDate, endDate, productId)};
93 94 qCInfo(LOG_AmdaProvider()) << tr("AmdaProvider::retrieveData url:") << url;
94 95 auto tempFile = std::make_shared<QTemporaryFile>();
95 96
96 97 // LAMBDA
97 98 auto httpDownloadFinished
98 99 = [this, dateTime, tempFile, token](QNetworkReply *reply, QUuid dataId) noexcept {
99 100 Q_UNUSED(dataId);
100 101
101 102 // Don't do anything if the reply was abort
102 103 if (reply->error() != QNetworkReply::OperationCanceledError) {
103 104
104 105 if (tempFile) {
105 106 auto replyReadAll = reply->readAll();
106 107 if (!replyReadAll.isEmpty()) {
107 108 tempFile->write(replyReadAll);
108 109 }
109 110 tempFile->close();
110 111
111 112 // Parse results file
112 113 if (auto dataSeries = AmdaResultParser::readTxt(tempFile->fileName())) {
113 114 emit dataProvided(token, dataSeries, dateTime);
114 115 }
115 116 else {
116 117 /// @todo ALX : debug
117 118 }
118 119 }
119 120 }
120 121
121 122 };
122 123 auto httpFinishedLambda
123 124 = [this, httpDownloadFinished, tempFile](QNetworkReply *reply, QUuid dataId) noexcept {
124 125
125 126 // Don't do anything if the reply was abort
126 127 if (reply->error() != QNetworkReply::OperationCanceledError) {
127 128 auto downloadFileUrl = QUrl{QString{reply->readAll()}};
128 129
129 130
130 131 qCInfo(LOG_AmdaProvider()) << tr("AmdaProvider::retrieveData downloadFileUrl:")
131 132 << downloadFileUrl;
132 133 // Executes request for downloading file //
133 134
134 135 // Creates destination file
135 136 if (tempFile->open()) {
136 137 // Executes request
137 138 emit requestConstructed(QNetworkRequest{downloadFileUrl}, dataId,
138 139 httpDownloadFinished);
139 140 }
140 141 }
141 142 };
142 143
143 144 // //////////////// //
144 145 // Executes request //
145 146 // //////////////// //
146 147 emit requestConstructed(QNetworkRequest{url}, token, httpFinishedLambda);
147 148 }
@@ -1,145 +1,152
1 1 #include "AmdaResultParser.h"
2 2
3 #include <Common/DateUtils.h>
3 4 #include <Data/ScalarSeries.h>
4 5
5 6 #include <QDateTime>
6 7 #include <QFile>
7 8 #include <QRegularExpression>
8 9
9 10 #include <cmath>
10 11
11 12 Q_LOGGING_CATEGORY(LOG_AmdaResultParser, "AmdaResultParser")
12 13
13 14 namespace {
14 15
15 16 /// Message in result file when the file was not found on server
16 17 const auto FILE_NOT_FOUND_MESSAGE = QStringLiteral("Not Found");
17 18
18 19 /// Format for dates in result files
19 20 const auto DATE_FORMAT = QStringLiteral("yyyy-MM-ddThh:mm:ss.zzz");
20 21
21 22 /// Separator between values in a result line
22 23 const auto RESULT_LINE_SEPARATOR = QRegularExpression{QStringLiteral("\\s+")};
23 24
24 25 /// Regex to find unit in a line. Examples of valid lines:
25 26 /// ... - Units : nT - ...
26 27 /// ... -Units:nT- ...
27 28 /// ... -Units: mΒ²- ...
28 29 /// ... - Units : m/s - ...
29 30 const auto UNIT_REGEX = QRegularExpression{QStringLiteral("-\\s*Units\\s*:\\s*(.+?)\\s*-")};
30 31
31 32 /// Converts a string date to a double date
32 33 /// @return a double that represents the date in seconds, NaN if the string date can't be converted
33 34 double doubleDate(const QString &stringDate) noexcept
34 35 {
35 36 auto dateTime = QDateTime::fromString(stringDate, DATE_FORMAT);
36 return dateTime.isValid() ? (dateTime.toMSecsSinceEpoch() / 1000.)
37 dateTime.setTimeSpec(Qt::UTC);
38 return dateTime.isValid() ? DateUtils::secondsSinceEpoch(dateTime)
37 39 : std::numeric_limits<double>::quiet_NaN();
38 40 }
39 41
42 /// Checks if a line is a comment line
43 bool isCommentLine(const QString &line)
44 {
45 return line.startsWith("#");
46 }
47
40 48 /**
41 49 * Reads stream to retrieve x-axis unit
42 50 * @param stream the stream to read
43 51 * @return the unit that has been read in the stream, a default unit (time unit with no label) if an
44 52 * error occured during reading
45 53 */
46 54 Unit readXAxisUnit(QTextStream &stream)
47 55 {
48 56 QString line{};
49 57
50 if (stream.readLineInto(&line)) {
58 // Searches unit in the comment lines
59 while (stream.readLineInto(&line) && isCommentLine(line)) {
51 60 auto match = UNIT_REGEX.match(line);
52 61 if (match.hasMatch()) {
53 62 return Unit{match.captured(1), true};
54 63 }
55 else {
56 qCWarning(LOG_AmdaResultParser())
57 << QObject::tr("Can't read unit: invalid line %1").arg(line);
58 }
59 }
60 else {
61 qCWarning(LOG_AmdaResultParser()) << QObject::tr("Can't read unit: end of file");
62 64 }
63 65
66 qCWarning(LOG_AmdaResultParser()) << QObject::tr("The unit could not be found in the file");
67
64 68 // Error cases
65 69 return Unit{{}, true};
66 70 }
67 71
68 72 /**
69 73 * Reads stream to retrieve results
70 74 * @param stream the stream to read
71 75 * @return the pair of vectors x-axis data/values data that has been read in the stream
72 76 */
73 77 QPair<QVector<double>, QVector<double> > readResults(QTextStream &stream)
74 78 {
75 79 auto xData = QVector<double>{};
76 80 auto valuesData = QVector<double>{};
77 81
78 82 QString line{};
83
79 84 while (stream.readLineInto(&line)) {
80 auto lineData = line.split(RESULT_LINE_SEPARATOR, QString::SkipEmptyParts);
81 if (lineData.size() == 2) {
82 // X : the data is converted from date to double (in secs)
83 auto x = doubleDate(lineData.at(0));
84
85 // Value
86 bool valueOk;
87 auto value = lineData.at(1).toDouble(&valueOk);
88
89 // Adds result only if x and value are valid
90 if (!std::isnan(x) && !std::isnan(value) && valueOk) {
91 xData.push_back(x);
92 valuesData.push_back(value);
85 // Ignore comment lines
86 if (!isCommentLine(line)) {
87 auto lineData = line.split(RESULT_LINE_SEPARATOR, QString::SkipEmptyParts);
88 if (lineData.size() == 2) {
89 // X : the data is converted from date to double (in secs)
90 auto x = doubleDate(lineData.at(0));
91
92 // Value
93 bool valueOk;
94 auto value = lineData.at(1).toDouble(&valueOk);
95
96 // Adds result only if x and value are valid
97 if (!std::isnan(x) && !std::isnan(value) && valueOk) {
98 xData.push_back(x);
99 valuesData.push_back(value);
100 }
101 else {
102 qCWarning(LOG_AmdaResultParser())
103 << QObject::tr(
104 "Can't retrieve results from line %1: x and/or value are invalid")
105 .arg(line);
106 }
93 107 }
94 108 else {
95 109 qCWarning(LOG_AmdaResultParser())
96 << QObject::tr(
97 "Can't retrieve results from line %1: x and/or value are invalid")
98 .arg(line);
110 << QObject::tr("Can't retrieve results from line %1: invalid line").arg(line);
99 111 }
100 112 }
101 else {
102 qCWarning(LOG_AmdaResultParser())
103 << QObject::tr("Can't retrieve results from line %1: invalid line").arg(line);
104 }
105 113 }
106 114
107 115 return qMakePair(std::move(xData), std::move(valuesData));
108 116 }
109 117
110 118 } // namespace
111 119
112 120 std::shared_ptr<IDataSeries> AmdaResultParser::readTxt(const QString &filePath) noexcept
113 121 {
114 122 QFile file{filePath};
115 123
116 124 if (!file.open(QFile::ReadOnly | QIODevice::Text)) {
117 125 qCCritical(LOG_AmdaResultParser())
118 126 << QObject::tr("Can't retrieve AMDA data from file %1: %2")
119 127 .arg(filePath, file.errorString());
120 128 return nullptr;
121 129 }
122 130
123 131 QTextStream stream{&file};
124 132
125 133 // Checks if the file was found on the server
126 134 auto firstLine = stream.readLine();
127 135 if (firstLine.compare(FILE_NOT_FOUND_MESSAGE) == 0) {
128 136 qCCritical(LOG_AmdaResultParser())
129 137 << QObject::tr("Can't retrieve AMDA data from file %1: file was not found on server")
130 138 .arg(filePath);
131 139 return nullptr;
132 140 }
133 141
134 // Ignore comments lines
135 stream.readLine();
136
137 142 // Reads x-axis unit
143 stream.seek(0); // returns to the beginning of the file
138 144 auto xAxisUnit = readXAxisUnit(stream);
139 145
140 146 // Reads results
147 stream.seek(0); // returns to the beginning of the file
141 148 auto results = readResults(stream);
142 149
143 150 return std::make_shared<ScalarSeries>(std::move(results.first), std::move(results.second),
144 151 xAxisUnit, Unit{});
145 152 }
@@ -1,182 +1,182
1 1 #include "AmdaResultParser.h"
2 2
3 3 #include <Data/ScalarSeries.h>
4 4
5 5 #include <QObject>
6 6 #include <QtTest>
7 7
8 8 namespace {
9 9
10 10 /// Path for the tests
11 11 const auto TESTS_RESOURCES_PATH
12 12 = QFileInfo{QString{AMDA_TESTS_RESOURCES_DIR}, "TestAmdaResultParser"}.absoluteFilePath();
13 13
14 14 QString inputFilePath(const QString &inputFileName)
15 15 {
16 16 return QFileInfo{TESTS_RESOURCES_PATH, inputFileName}.absoluteFilePath();
17 17 }
18 18
19 19 struct ExpectedResults {
20 20 explicit ExpectedResults() = default;
21 21
22 22 /// Ctor with QVector<QDateTime> as x-axis data. Datetimes are converted to doubles
23 23 explicit ExpectedResults(Unit xAxisUnit, Unit valuesUnit, const QVector<QDateTime> &xAxisData,
24 24 QVector<double> valuesData)
25 25 : m_ParsingOK{true},
26 26 m_XAxisUnit{xAxisUnit},
27 27 m_ValuesUnit{valuesUnit},
28 28 m_XAxisData{},
29 29 m_ValuesData{std::move(valuesData)}
30 30 {
31 31 // Converts QVector<QDateTime> to QVector<double>
32 32 std::transform(xAxisData.cbegin(), xAxisData.cend(), std::back_inserter(m_XAxisData),
33 33 [](const auto &dateTime) { return dateTime.toMSecsSinceEpoch() / 1000.; });
34 34 }
35 35
36 36 /**
37 37 * Validates a DataSeries compared to the expected results
38 38 * @param results the DataSeries to validate
39 39 */
40 40 void validate(std::shared_ptr<IDataSeries> results)
41 41 {
42 42 if (m_ParsingOK) {
43 43 auto scalarSeries = dynamic_cast<ScalarSeries *>(results.get());
44 44 QVERIFY(scalarSeries != nullptr);
45 45
46 46 // Checks units
47 47 QVERIFY(scalarSeries->xAxisUnit() == m_XAxisUnit);
48 48 QVERIFY(scalarSeries->valuesUnit() == m_ValuesUnit);
49 49
50 50 // Checks values
51 51 QVERIFY(scalarSeries->xAxisData()->data() == m_XAxisData);
52 52 QVERIFY(scalarSeries->valuesData()->data() == m_ValuesData);
53 53 }
54 54 else {
55 55 QVERIFY(results == nullptr);
56 56 }
57 57 }
58 58
59 59 // Parsing was successfully completed
60 60 bool m_ParsingOK{false};
61 61 // Expected x-axis unit
62 62 Unit m_XAxisUnit{};
63 63 // Expected values unit
64 64 Unit m_ValuesUnit{};
65 65 // Expected x-axis data
66 66 QVector<double> m_XAxisData{};
67 67 // Expected values data
68 68 QVector<double> m_ValuesData{};
69 69 };
70 70
71 71 } // namespace
72 72
73 73 Q_DECLARE_METATYPE(ExpectedResults)
74 74
75 75 class TestAmdaResultParser : public QObject {
76 76 Q_OBJECT
77 77 private slots:
78 78 /// Input test data
79 79 /// @sa testTxtJson()
80 80 void testReadTxt_data();
81 81
82 82 /// Tests parsing of a TXT file
83 83 void testReadTxt();
84 84 };
85 85
86 86 void TestAmdaResultParser::testReadTxt_data()
87 87 {
88 88 // ////////////// //
89 89 // Test structure //
90 90 // ////////////// //
91 91
92 92 // Name of TXT file to read
93 93 QTest::addColumn<QString>("inputFileName");
94 94 // Expected results
95 95 QTest::addColumn<ExpectedResults>("expectedResults");
96 96
97 97 // ////////// //
98 98 // Test cases //
99 99 // ////////// //
100 100
101 101 auto dateTime = [](int year, int month, int day, int hours, int minutes, int seconds) {
102 return QDateTime{{year, month, day}, {hours, minutes, seconds}};
102 return QDateTime{{year, month, day}, {hours, minutes, seconds}, Qt::UTC};
103 103 };
104 104
105 105 // Valid file
106 106 QTest::newRow("Valid file")
107 107 << QStringLiteral("ValidScalar1.txt")
108 108 << ExpectedResults{
109 109 Unit{QStringLiteral("nT"), true}, Unit{},
110 110 QVector<QDateTime>{dateTime(2013, 9, 23, 9, 0, 30), dateTime(2013, 9, 23, 9, 1, 30),
111 111 dateTime(2013, 9, 23, 9, 2, 30), dateTime(2013, 9, 23, 9, 3, 30),
112 112 dateTime(2013, 9, 23, 9, 4, 30), dateTime(2013, 9, 23, 9, 5, 30),
113 113 dateTime(2013, 9, 23, 9, 6, 30), dateTime(2013, 9, 23, 9, 7, 30),
114 114 dateTime(2013, 9, 23, 9, 8, 30), dateTime(2013, 9, 23, 9, 9, 30)},
115 115 QVector<double>{-2.83950, -2.71850, -2.52150, -2.57633, -2.58050, -2.48325, -2.63025,
116 116 -2.55800, -2.43250, -2.42200}};
117 117
118 118 // Valid files but with some invalid lines (wrong unit, wrong values, etc.)
119 119 QTest::newRow("No unit file") << QStringLiteral("NoUnit.txt")
120 120 << ExpectedResults{Unit{QStringLiteral(""), true}, Unit{},
121 121 QVector<QDateTime>{}, QVector<double>{}};
122 122 QTest::newRow("Wrong unit file")
123 123 << QStringLiteral("WrongUnit.txt")
124 124 << ExpectedResults{Unit{QStringLiteral(""), true}, Unit{},
125 125 QVector<QDateTime>{dateTime(2013, 9, 23, 9, 0, 30),
126 126 dateTime(2013, 9, 23, 9, 1, 30),
127 127 dateTime(2013, 9, 23, 9, 2, 30)},
128 128 QVector<double>{-2.83950, -2.71850, -2.52150}};
129 129
130 130 QTest::newRow("Wrong results file (date of first line is invalid")
131 131 << QStringLiteral("WrongDate.txt")
132 132 << ExpectedResults{
133 133 Unit{QStringLiteral("nT"), true}, Unit{},
134 134 QVector<QDateTime>{dateTime(2013, 9, 23, 9, 1, 30), dateTime(2013, 9, 23, 9, 2, 30)},
135 135 QVector<double>{-2.71850, -2.52150}};
136 136
137 137 QTest::newRow("Wrong results file (too many values for first line")
138 138 << QStringLiteral("TooManyValues.txt")
139 139 << ExpectedResults{
140 140 Unit{QStringLiteral("nT"), true}, Unit{},
141 141 QVector<QDateTime>{dateTime(2013, 9, 23, 9, 1, 30), dateTime(2013, 9, 23, 9, 2, 30)},
142 142 QVector<double>{-2.71850, -2.52150}};
143 143
144 144 QTest::newRow("Wrong results file (value of first line is invalid")
145 145 << QStringLiteral("WrongValue.txt")
146 146 << ExpectedResults{
147 147 Unit{QStringLiteral("nT"), true}, Unit{},
148 148 QVector<QDateTime>{dateTime(2013, 9, 23, 9, 1, 30), dateTime(2013, 9, 23, 9, 2, 30)},
149 149 QVector<double>{-2.71850, -2.52150}};
150 150
151 151 QTest::newRow("Wrong results file (value of first line is NaN")
152 152 << QStringLiteral("NaNValue.txt")
153 153 << ExpectedResults{
154 154 Unit{QStringLiteral("nT"), true}, Unit{},
155 155 QVector<QDateTime>{dateTime(2013, 9, 23, 9, 1, 30), dateTime(2013, 9, 23, 9, 2, 30)},
156 156 QVector<double>{-2.71850, -2.52150}};
157 157
158 158 // Invalid files
159 159 QTest::newRow("Invalid file (unexisting file)") << QStringLiteral("UnexistingFile.txt")
160 160 << ExpectedResults{};
161 161
162 162 QTest::newRow("Invalid file (file not found on server)") << QStringLiteral("FileNotFound.txt")
163 163 << ExpectedResults{};
164 164 }
165 165
166 166 void TestAmdaResultParser::testReadTxt()
167 167 {
168 168 QFETCH(QString, inputFileName);
169 169 QFETCH(ExpectedResults, expectedResults);
170 170
171 171 // Parses file
172 172 auto filePath = inputFilePath(inputFileName);
173 173 auto results = AmdaResultParser::readTxt(filePath);
174 174
175 175 // ///////////////// //
176 176 // Validates results //
177 177 // ///////////////// //
178 178 expectedResults.validate(results);
179 179 }
180 180
181 181 QTEST_MAIN(TestAmdaResultParser)
182 182 #include "TestAmdaResultParser.moc"
@@ -1,101 +1,100
1 1 #include "CosinusProvider.h"
2 2
3 3 #include <Data/DataProviderParameters.h>
4 4 #include <Data/ScalarSeries.h>
5 5
6 6 #include <cmath>
7 7
8 #include <QDateTime>
9 8 #include <QFuture>
10 9 #include <QThread>
11 10 #include <QtConcurrent/QtConcurrent>
12 11
13 12 Q_LOGGING_CATEGORY(LOG_CosinusProvider, "CosinusProvider")
14 13
15 14 std::shared_ptr<IDataSeries> CosinusProvider::retrieveData(QUuid token, const SqpDateTime &dateTime)
16 15 {
17 16 // TODO: Add Mutex
18 17 auto dataIndex = 0;
19 18
20 19 // Gets the timerange from the parameters
21 20 double freq = 100.0;
22 21 double start = std::ceil(dateTime.m_TStart * freq); // 100 htz
23 22 double end = std::floor(dateTime.m_TEnd * freq); // 100 htz
24 23
25 24 // We assure that timerange is valid
26 25 if (end < start) {
27 26 std::swap(start, end);
28 27 }
29 28
30 29 // Generates scalar series containing cosinus values (one value per second)
31 30 auto dataCount = end - start;
32 31
33 32 auto xAxisData = QVector<double>{};
34 33 xAxisData.resize(dataCount);
35 34
36 35 auto valuesData = QVector<double>{};
37 36 valuesData.resize(dataCount);
38 37
39 38 int progress = 0;
40 39 auto progressEnd = dataCount;
41 40 for (auto time = start; time < end; ++time, ++dataIndex) {
42 41 auto it = m_VariableToEnableProvider.find(token);
43 42 if (it != m_VariableToEnableProvider.end() && it.value()) {
44 43 const auto timeOnFreq = time / freq;
45 44
46 45 xAxisData.replace(dataIndex, timeOnFreq);
47 46 valuesData.replace(dataIndex, std::cos(timeOnFreq));
48 47
49 48 // progression
50 49 int currentProgress = (time - start) * 100.0 / progressEnd;
51 50 if (currentProgress != progress) {
52 51 progress = currentProgress;
53 52
54 53 emit dataProvidedProgress(token, progress);
55 54 }
56 55 }
57 56 else {
58 57 if (!it.value()) {
59 58 qCDebug(LOG_CosinusProvider())
60 59 << "CosinusProvider::retrieveData: ARRET De l'acquisition detectΓ©"
61 60 << end - time;
62 61 }
63 62 }
64 63 }
65 64 emit dataProvidedProgress(token, 0.0);
66 65
67 66 return std::make_shared<ScalarSeries>(std::move(xAxisData), std::move(valuesData),
68 67 Unit{QStringLiteral("t"), true}, Unit{});
69 68 }
70 69
71 70 void CosinusProvider::requestDataLoading(QUuid token, const DataProviderParameters &parameters)
72 71 {
73 72 // TODO: Add Mutex
74 73 m_VariableToEnableProvider[token] = true;
75 74 qCDebug(LOG_CosinusProvider()) << "CosinusProvider::requestDataLoading"
76 75 << QThread::currentThread()->objectName();
77 76 // NOTE: Try to use multithread if possible
78 77 const auto times = parameters.m_Times;
79 78
80 79 for (const auto &dateTime : qAsConst(times)) {
81 80 if (m_VariableToEnableProvider[token]) {
82 81 auto scalarSeries = this->retrieveData(token, dateTime);
83 82 emit dataProvided(token, scalarSeries, dateTime);
84 83 }
85 84 }
86 85 }
87 86
88 87 void CosinusProvider::requestDataAborting(QUuid identifier)
89 88 {
90 89 // TODO: Add Mutex
91 90 qCDebug(LOG_CosinusProvider()) << "CosinusProvider::requestDataAborting" << identifier
92 91 << QThread::currentThread()->objectName();
93 92 auto it = m_VariableToEnableProvider.find(identifier);
94 93 if (it != m_VariableToEnableProvider.end()) {
95 94 it.value() = false;
96 95 }
97 96 else {
98 97 qCWarning(LOG_CosinusProvider())
99 98 << tr("Aborting progression of inexistant identifier detected !!!");
100 99 }
101 100 }
General Comments 0
You need to be logged in to leave comments. Login now