##// END OF EJS Templates
Merge pull request 196 from SCIQLOP-Initialisation develop...
perrinel -
r495:f81e8612a847 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 }
@@ -0,0 +1,20
1 #ifndef SCIQLOP_VISUALIZATIONGRAPHRENDERINGDELEGATE_H
2 #define SCIQLOP_VISUALIZATIONGRAPHRENDERINGDELEGATE_H
3
4 #include <Common/spimpl.h>
5
6 class QCustomPlot;
7 class QMouseEvent;
8
9 class VisualizationGraphRenderingDelegate {
10 public:
11 explicit VisualizationGraphRenderingDelegate(QCustomPlot &plot);
12
13 void onMouseMove(QMouseEvent *event) noexcept;
14
15 private:
16 class VisualizationGraphRenderingDelegatePrivate;
17 spimpl::unique_impl_ptr<VisualizationGraphRenderingDelegatePrivate> impl;
18 };
19
20 #endif // SCIQLOP_VISUALIZATIONGRAPHRENDERINGDELEGATE_H
@@ -0,0 +1,123
1 #include "Visualization/VisualizationGraphRenderingDelegate.h"
2 #include "Visualization/qcustomplot.h"
3
4 #include <Common/DateUtils.h>
5
6 namespace {
7
8 const auto DATETIME_FORMAT = QStringLiteral("yyyy/MM/dd hh:mm:ss:zzz");
9
10 const auto TEXT_TRACER_FORMAT = QStringLiteral("key: %1\nvalue: %2");
11
12 /// Timeout after which a tracer is displayed
13 const auto TRACER_TIMEOUT = 500;
14
15 /// Formats a data value according to the axis on which it is present
16 QString formatValue(double value, const QCPAxis &axis)
17 {
18 // If the axis is a time axis, formats the value as a date
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 }
25 }
26
27 void initPointTracerStyle(QCPItemTracer &tracer) noexcept
28 {
29 tracer.setInterpolating(false);
30 tracer.setStyle(QCPItemTracer::tsPlus);
31 tracer.setPen(QPen(Qt::black));
32 tracer.setBrush(Qt::black);
33 tracer.setSize(10);
34 }
35
36 void initTextTracerStyle(QCPItemText &tracer) noexcept
37 {
38 tracer.setPen(QPen{Qt::gray});
39 tracer.setBrush(Qt::white);
40 tracer.setPadding(QMargins{6, 6, 6, 6});
41 tracer.setPositionAlignment(Qt::AlignTop | Qt::AlignLeft);
42 tracer.setTextAlignment(Qt::AlignLeft);
43 }
44
45 } // namespace
46
47 struct VisualizationGraphRenderingDelegate::VisualizationGraphRenderingDelegatePrivate {
48 explicit VisualizationGraphRenderingDelegatePrivate(QCustomPlot &plot)
49 : m_Plot{plot},
50 m_PointTracer{new QCPItemTracer{&plot}},
51 m_TextTracer{new QCPItemText{&plot}},
52 m_TracerTimer{}
53 {
54 initPointTracerStyle(*m_PointTracer);
55 initTextTracerStyle(*m_TextTracer);
56
57 m_TracerTimer.setInterval(TRACER_TIMEOUT);
58 m_TracerTimer.setSingleShot(true);
59 }
60
61 QCustomPlot &m_Plot;
62 QCPItemTracer *m_PointTracer;
63 QCPItemText *m_TextTracer;
64 QTimer m_TracerTimer;
65 };
66
67 VisualizationGraphRenderingDelegate::VisualizationGraphRenderingDelegate(QCustomPlot &plot)
68 : impl{spimpl::make_unique_impl<VisualizationGraphRenderingDelegatePrivate>(plot)}
69 {
70 }
71
72 void VisualizationGraphRenderingDelegate::onMouseMove(QMouseEvent *event) noexcept
73 {
74 // Cancels pending refresh
75 impl->m_TracerTimer.disconnect();
76
77 auto showTracers = [ eventPos = event->pos(), this ]()
78 {
79 // Lambda function to display a tracer
80 auto displayTracer = [this](auto &tracer) {
81 // Tracer is set on top of the plot's main layer
82 tracer.setLayer(impl->m_Plot.layer("main"));
83 tracer.setVisible(true);
84 impl->m_Plot.replot();
85 };
86
87 // Reinits tracers
88 impl->m_PointTracer->setGraph(nullptr);
89 impl->m_PointTracer->setVisible(false);
90 impl->m_TextTracer->setVisible(false);
91 impl->m_Plot.replot();
92
93 // Gets the graph under the mouse position
94 if (auto graph = qobject_cast<QCPGraph *>(impl->m_Plot.plottableAt(eventPos))) {
95 auto mouseKey = graph->keyAxis()->pixelToCoord(eventPos.x());
96 auto graphData = graph->data();
97
98 // Gets the closest data point to the mouse
99 auto graphDataIt = graphData->findBegin(mouseKey);
100 if (graphDataIt != graphData->constEnd()) {
101 auto key = formatValue(graphDataIt->key, *graph->keyAxis());
102 auto value = formatValue(graphDataIt->value, *graph->valueAxis());
103 impl->m_TextTracer->setText(TEXT_TRACER_FORMAT.arg(key, value));
104
105 // Displays point tracer
106 impl->m_PointTracer->setGraph(graph);
107 impl->m_PointTracer->setGraphKey(graphDataIt->key);
108 displayTracer(*impl->m_PointTracer);
109
110 // Displays text tracer
111 auto tracerPosition = impl->m_TextTracer->position;
112 tracerPosition->setAxes(graph->keyAxis(), graph->valueAxis());
113 tracerPosition->setCoords(impl->m_PointTracer->position->key(),
114 impl->m_PointTracer->position->value());
115 displayTracer(*impl->m_TextTracer);
116 }
117 }
118 };
119
120 // Starts the timer to display tracers at timeout
121 QObject::connect(&impl->m_TracerTimer, &QTimer::timeout, showTracers);
122 impl->m_TracerTimer.start();
123 }
@@ -1,255 +1,254
1 /*------------------------------------------------------------------------------
1 /*------------------------------------------------------------------------------
2 -- This file is a part of the SciQLop Software
2 -- This file is a part of the SciQLop Software
3 -- Copyright (C) 2017, Plasma Physics Laboratory - CNRS
3 -- Copyright (C) 2017, Plasma Physics Laboratory - CNRS
4 --
4 --
5 -- This program is free software; you can redistribute it and/or modify
5 -- This program is free software; you can redistribute it and/or modify
6 -- it under the terms of the GNU General Public License as published by
6 -- it under the terms of the GNU General Public License as published by
7 -- the Free Software Foundation; either version 2 of the License, or
7 -- the Free Software Foundation; either version 2 of the License, or
8 -- (at your option) any later version.
8 -- (at your option) any later version.
9 --
9 --
10 -- This program is distributed in the hope that it will be useful,
10 -- This program is distributed in the hope that it will be useful,
11 -- but WITHOUT ANY WARRANTY; without even the implied warranty of
11 -- but WITHOUT ANY WARRANTY; without even the implied warranty of
12 -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 -- GNU General Public License for more details.
13 -- GNU General Public License for more details.
14 --
14 --
15 -- You should have received a copy of the GNU General Public License
15 -- You should have received a copy of the GNU General Public License
16 -- along with this program; if not, write to the Free Software
16 -- along with this program; if not, write to the Free Software
17 -- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 -- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 -------------------------------------------------------------------------------*/
18 -------------------------------------------------------------------------------*/
19 /*-- Author : Alexis Jeandet
19 /*-- Author : Alexis Jeandet
20 -- Mail : alexis.jeandet@member.fsf.org
20 -- Mail : alexis.jeandet@member.fsf.org
21 ----------------------------------------------------------------------------*/
21 ----------------------------------------------------------------------------*/
22 #include "MainWindow.h"
22 #include "MainWindow.h"
23 #include "ui_MainWindow.h"
23 #include "ui_MainWindow.h"
24
24
25 #include <DataSource/DataSourceController.h>
25 #include <DataSource/DataSourceController.h>
26 #include <DataSource/DataSourceWidget.h>
26 #include <DataSource/DataSourceWidget.h>
27 #include <Settings/SqpSettingsDialog.h>
27 #include <Settings/SqpSettingsDialog.h>
28 #include <Settings/SqpSettingsGeneralWidget.h>
28 #include <Settings/SqpSettingsGeneralWidget.h>
29 #include <SidePane/SqpSidePane.h>
29 #include <SidePane/SqpSidePane.h>
30 #include <SqpApplication.h>
30 #include <SqpApplication.h>
31 #include <Time/TimeController.h>
31 #include <Time/TimeController.h>
32 #include <TimeWidget/TimeWidget.h>
32 #include <TimeWidget/TimeWidget.h>
33 #include <Variable/Variable.h>
33 #include <Variable/Variable.h>
34 #include <Variable/VariableController.h>
34 #include <Variable/VariableController.h>
35 #include <Visualization/VisualizationController.h>
35 #include <Visualization/VisualizationController.h>
36
36
37 #include <QAction>
37 #include <QAction>
38 #include <QDate>
38 #include <QDate>
39 #include <QDateTime>
40 #include <QDir>
39 #include <QDir>
41 #include <QFileDialog>
40 #include <QFileDialog>
42 #include <QToolBar>
41 #include <QToolBar>
43 #include <QToolButton>
42 #include <QToolButton>
44 #include <memory.h>
43 #include <memory.h>
45
44
46 #include "iostream"
45 #include "iostream"
47
46
48 Q_LOGGING_CATEGORY(LOG_MainWindow, "MainWindow")
47 Q_LOGGING_CATEGORY(LOG_MainWindow, "MainWindow")
49
48
50 namespace {
49 namespace {
51 const auto LEFTMAININSPECTORWIDGETSPLITTERINDEX = 0;
50 const auto LEFTMAININSPECTORWIDGETSPLITTERINDEX = 0;
52 const auto LEFTINSPECTORSIDEPANESPLITTERINDEX = 1;
51 const auto LEFTINSPECTORSIDEPANESPLITTERINDEX = 1;
53 const auto VIEWPLITTERINDEX = 2;
52 const auto VIEWPLITTERINDEX = 2;
54 const auto RIGHTINSPECTORSIDEPANESPLITTERINDEX = 3;
53 const auto RIGHTINSPECTORSIDEPANESPLITTERINDEX = 3;
55 const auto RIGHTMAININSPECTORWIDGETSPLITTERINDEX = 4;
54 const auto RIGHTMAININSPECTORWIDGETSPLITTERINDEX = 4;
56 }
55 }
57
56
58 class MainWindow::MainWindowPrivate {
57 class MainWindow::MainWindowPrivate {
59 public:
58 public:
60 explicit MainWindowPrivate(MainWindow *mainWindow)
59 explicit MainWindowPrivate(MainWindow *mainWindow)
61 : m_LastOpenLeftInspectorSize{},
60 : m_LastOpenLeftInspectorSize{},
62 m_LastOpenRightInspectorSize{},
61 m_LastOpenRightInspectorSize{},
63 m_GeneralSettingsWidget{new SqpSettingsGeneralWidget{mainWindow}},
62 m_GeneralSettingsWidget{new SqpSettingsGeneralWidget{mainWindow}},
64 m_SettingsDialog{new SqpSettingsDialog{mainWindow}}
63 m_SettingsDialog{new SqpSettingsDialog{mainWindow}}
65 {
64 {
66 }
65 }
67
66
68 QSize m_LastOpenLeftInspectorSize;
67 QSize m_LastOpenLeftInspectorSize;
69 QSize m_LastOpenRightInspectorSize;
68 QSize m_LastOpenRightInspectorSize;
70 /// General settings widget. MainWindow has the ownership
69 /// General settings widget. MainWindow has the ownership
71 SqpSettingsGeneralWidget *m_GeneralSettingsWidget;
70 SqpSettingsGeneralWidget *m_GeneralSettingsWidget;
72 /// Settings dialog. MainWindow has the ownership
71 /// Settings dialog. MainWindow has the ownership
73 SqpSettingsDialog *m_SettingsDialog;
72 SqpSettingsDialog *m_SettingsDialog;
74 };
73 };
75
74
76 MainWindow::MainWindow(QWidget *parent)
75 MainWindow::MainWindow(QWidget *parent)
77 : QMainWindow{parent},
76 : QMainWindow{parent},
78 m_Ui{new Ui::MainWindow},
77 m_Ui{new Ui::MainWindow},
79 impl{spimpl::make_unique_impl<MainWindowPrivate>(this)}
78 impl{spimpl::make_unique_impl<MainWindowPrivate>(this)}
80 {
79 {
81 m_Ui->setupUi(this);
80 m_Ui->setupUi(this);
82
81
83 m_Ui->splitter->setCollapsible(LEFTINSPECTORSIDEPANESPLITTERINDEX, false);
82 m_Ui->splitter->setCollapsible(LEFTINSPECTORSIDEPANESPLITTERINDEX, false);
84 m_Ui->splitter->setCollapsible(RIGHTINSPECTORSIDEPANESPLITTERINDEX, false);
83 m_Ui->splitter->setCollapsible(RIGHTINSPECTORSIDEPANESPLITTERINDEX, false);
85
84
86
85
87 auto leftSidePane = m_Ui->leftInspectorSidePane->sidePane();
86 auto leftSidePane = m_Ui->leftInspectorSidePane->sidePane();
88 auto openLeftInspectorAction = new QAction{QIcon{
87 auto openLeftInspectorAction = new QAction{QIcon{
89 ":/icones/previous.png",
88 ":/icones/previous.png",
90 },
89 },
91 tr("Show/hide the left inspector"), this};
90 tr("Show/hide the left inspector"), this};
92
91
93
92
94 auto spacerLeftTop = new QWidget{};
93 auto spacerLeftTop = new QWidget{};
95 spacerLeftTop->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
94 spacerLeftTop->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
96
95
97 auto spacerLeftBottom = new QWidget{};
96 auto spacerLeftBottom = new QWidget{};
98 spacerLeftBottom->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
97 spacerLeftBottom->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
99
98
100 leftSidePane->addWidget(spacerLeftTop);
99 leftSidePane->addWidget(spacerLeftTop);
101 leftSidePane->addAction(openLeftInspectorAction);
100 leftSidePane->addAction(openLeftInspectorAction);
102 leftSidePane->addWidget(spacerLeftBottom);
101 leftSidePane->addWidget(spacerLeftBottom);
103
102
104
103
105 auto rightSidePane = m_Ui->rightInspectorSidePane->sidePane();
104 auto rightSidePane = m_Ui->rightInspectorSidePane->sidePane();
106 auto openRightInspectorAction = new QAction{QIcon{
105 auto openRightInspectorAction = new QAction{QIcon{
107 ":/icones/next.png",
106 ":/icones/next.png",
108 },
107 },
109 tr("Show/hide the right inspector"), this};
108 tr("Show/hide the right inspector"), this};
110
109
111 auto spacerRightTop = new QWidget{};
110 auto spacerRightTop = new QWidget{};
112 spacerRightTop->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
111 spacerRightTop->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
113
112
114 auto spacerRightBottom = new QWidget{};
113 auto spacerRightBottom = new QWidget{};
115 spacerRightBottom->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
114 spacerRightBottom->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
116
115
117 rightSidePane->addWidget(spacerRightTop);
116 rightSidePane->addWidget(spacerRightTop);
118 rightSidePane->addAction(openRightInspectorAction);
117 rightSidePane->addAction(openRightInspectorAction);
119 rightSidePane->addWidget(spacerRightBottom);
118 rightSidePane->addWidget(spacerRightBottom);
120
119
121 openLeftInspectorAction->setCheckable(true);
120 openLeftInspectorAction->setCheckable(true);
122 openRightInspectorAction->setCheckable(true);
121 openRightInspectorAction->setCheckable(true);
123
122
124 auto openInspector = [this](bool checked, bool right, auto action) {
123 auto openInspector = [this](bool checked, bool right, auto action) {
125
124
126 action->setIcon(QIcon{(checked xor right) ? ":/icones/next.png" : ":/icones/previous.png"});
125 action->setIcon(QIcon{(checked xor right) ? ":/icones/next.png" : ":/icones/previous.png"});
127
126
128 auto &lastInspectorSize
127 auto &lastInspectorSize
129 = right ? impl->m_LastOpenRightInspectorSize : impl->m_LastOpenLeftInspectorSize;
128 = right ? impl->m_LastOpenRightInspectorSize : impl->m_LastOpenLeftInspectorSize;
130
129
131 auto nextInspectorSize = right ? m_Ui->rightMainInspectorWidget->size()
130 auto nextInspectorSize = right ? m_Ui->rightMainInspectorWidget->size()
132 : m_Ui->leftMainInspectorWidget->size();
131 : m_Ui->leftMainInspectorWidget->size();
133
132
134 // Update of the last opened geometry
133 // Update of the last opened geometry
135 if (checked) {
134 if (checked) {
136 lastInspectorSize = nextInspectorSize;
135 lastInspectorSize = nextInspectorSize;
137 }
136 }
138
137
139 auto startSize = lastInspectorSize;
138 auto startSize = lastInspectorSize;
140 auto endSize = startSize;
139 auto endSize = startSize;
141 endSize.setWidth(0);
140 endSize.setWidth(0);
142
141
143 auto splitterInspectorIndex
142 auto splitterInspectorIndex
144 = right ? RIGHTMAININSPECTORWIDGETSPLITTERINDEX : LEFTMAININSPECTORWIDGETSPLITTERINDEX;
143 = right ? RIGHTMAININSPECTORWIDGETSPLITTERINDEX : LEFTMAININSPECTORWIDGETSPLITTERINDEX;
145
144
146 auto currentSizes = m_Ui->splitter->sizes();
145 auto currentSizes = m_Ui->splitter->sizes();
147 if (checked) {
146 if (checked) {
148 // adjust sizes individually here, e.g.
147 // adjust sizes individually here, e.g.
149 currentSizes[splitterInspectorIndex] -= lastInspectorSize.width();
148 currentSizes[splitterInspectorIndex] -= lastInspectorSize.width();
150 currentSizes[VIEWPLITTERINDEX] += lastInspectorSize.width();
149 currentSizes[VIEWPLITTERINDEX] += lastInspectorSize.width();
151 m_Ui->splitter->setSizes(currentSizes);
150 m_Ui->splitter->setSizes(currentSizes);
152 }
151 }
153 else {
152 else {
154 // adjust sizes individually here, e.g.
153 // adjust sizes individually here, e.g.
155 currentSizes[splitterInspectorIndex] += lastInspectorSize.width();
154 currentSizes[splitterInspectorIndex] += lastInspectorSize.width();
156 currentSizes[VIEWPLITTERINDEX] -= lastInspectorSize.width();
155 currentSizes[VIEWPLITTERINDEX] -= lastInspectorSize.width();
157 m_Ui->splitter->setSizes(currentSizes);
156 m_Ui->splitter->setSizes(currentSizes);
158 }
157 }
159
158
160 };
159 };
161
160
162
161
163 connect(openLeftInspectorAction, &QAction::triggered,
162 connect(openLeftInspectorAction, &QAction::triggered,
164 [openInspector, openLeftInspectorAction](bool checked) {
163 [openInspector, openLeftInspectorAction](bool checked) {
165 openInspector(checked, false, openLeftInspectorAction);
164 openInspector(checked, false, openLeftInspectorAction);
166 });
165 });
167 connect(openRightInspectorAction, &QAction::triggered,
166 connect(openRightInspectorAction, &QAction::triggered,
168 [openInspector, openRightInspectorAction](bool checked) {
167 [openInspector, openRightInspectorAction](bool checked) {
169 openInspector(checked, true, openRightInspectorAction);
168 openInspector(checked, true, openRightInspectorAction);
170 });
169 });
171
170
172 // //// //
171 // //// //
173 // Menu //
172 // Menu //
174 // //// //
173 // //// //
175 this->menuBar()->addAction(tr("File"));
174 this->menuBar()->addAction(tr("File"));
176 auto toolsMenu = this->menuBar()->addMenu(tr("Tools"));
175 auto toolsMenu = this->menuBar()->addMenu(tr("Tools"));
177 toolsMenu->addAction(tr("Settings..."), [this]() {
176 toolsMenu->addAction(tr("Settings..."), [this]() {
178 // Loads settings
177 // Loads settings
179 impl->m_SettingsDialog->loadSettings();
178 impl->m_SettingsDialog->loadSettings();
180
179
181 // Open settings dialog and save settings if the dialog is accepted
180 // Open settings dialog and save settings if the dialog is accepted
182 if (impl->m_SettingsDialog->exec() == QDialog::Accepted) {
181 if (impl->m_SettingsDialog->exec() == QDialog::Accepted) {
183 impl->m_SettingsDialog->saveSettings();
182 impl->m_SettingsDialog->saveSettings();
184 }
183 }
185
184
186 });
185 });
187
186
188 auto mainToolBar = this->addToolBar(QStringLiteral("MainToolBar"));
187 auto mainToolBar = this->addToolBar(QStringLiteral("MainToolBar"));
189
188
190 auto timeWidget = new TimeWidget{};
189 auto timeWidget = new TimeWidget{};
191 mainToolBar->addWidget(timeWidget);
190 mainToolBar->addWidget(timeWidget);
192
191
193 // //////// //
192 // //////// //
194 // Settings //
193 // Settings //
195 // //////// //
194 // //////// //
196
195
197 // Registers "general settings" widget to the settings dialog
196 // Registers "general settings" widget to the settings dialog
198 impl->m_SettingsDialog->registerWidget(QStringLiteral("General"),
197 impl->m_SettingsDialog->registerWidget(QStringLiteral("General"),
199 impl->m_GeneralSettingsWidget);
198 impl->m_GeneralSettingsWidget);
200
199
201 // /////////// //
200 // /////////// //
202 // Connections //
201 // Connections //
203 // /////////// //
202 // /////////// //
204
203
205 // Controllers / controllers connections
204 // Controllers / controllers connections
206 connect(&sqpApp->timeController(), SIGNAL(timeUpdated(SqpDateTime)),
205 connect(&sqpApp->timeController(), SIGNAL(timeUpdated(SqpDateTime)),
207 &sqpApp->variableController(), SLOT(onDateTimeOnSelection(SqpDateTime)));
206 &sqpApp->variableController(), SLOT(onDateTimeOnSelection(SqpDateTime)));
208
207
209 // Widgets / controllers connections
208 // Widgets / controllers connections
210
209
211 // DataSource
210 // DataSource
212 connect(&sqpApp->dataSourceController(), SIGNAL(dataSourceItemSet(DataSourceItem *)),
211 connect(&sqpApp->dataSourceController(), SIGNAL(dataSourceItemSet(DataSourceItem *)),
213 m_Ui->dataSourceWidget, SLOT(addDataSource(DataSourceItem *)));
212 m_Ui->dataSourceWidget, SLOT(addDataSource(DataSourceItem *)));
214
213
215 // Time
214 // Time
216 connect(timeWidget, SIGNAL(timeUpdated(SqpDateTime)), &sqpApp->timeController(),
215 connect(timeWidget, SIGNAL(timeUpdated(SqpDateTime)), &sqpApp->timeController(),
217 SLOT(onTimeToUpdate(SqpDateTime)));
216 SLOT(onTimeToUpdate(SqpDateTime)));
218
217
219 // Visualization
218 // Visualization
220 connect(&sqpApp->visualizationController(),
219 connect(&sqpApp->visualizationController(),
221 SIGNAL(variableAboutToBeDeleted(std::shared_ptr<Variable>)), m_Ui->view,
220 SIGNAL(variableAboutToBeDeleted(std::shared_ptr<Variable>)), m_Ui->view,
222 SLOT(onVariableAboutToBeDeleted(std::shared_ptr<Variable>)));
221 SLOT(onVariableAboutToBeDeleted(std::shared_ptr<Variable>)));
223
222
224 connect(&sqpApp->visualizationController(),
223 connect(&sqpApp->visualizationController(),
225 SIGNAL(rangeChanged(std::shared_ptr<Variable>, const SqpDateTime &)), m_Ui->view,
224 SIGNAL(rangeChanged(std::shared_ptr<Variable>, const SqpDateTime &)), m_Ui->view,
226 SLOT(onRangeChanged(std::shared_ptr<Variable>, const SqpDateTime &)));
225 SLOT(onRangeChanged(std::shared_ptr<Variable>, const SqpDateTime &)));
227
226
228 // Widgets / widgets connections
227 // Widgets / widgets connections
229
228
230 // For the following connections, we use DirectConnection to allow each widget that can
229 // For the following connections, we use DirectConnection to allow each widget that can
231 // potentially attach a menu to the variable's menu to do so before this menu is displayed.
230 // potentially attach a menu to the variable's menu to do so before this menu is displayed.
232 // The order of connections is also important, since it determines the order in which each
231 // The order of connections is also important, since it determines the order in which each
233 // widget will attach its menu
232 // widget will attach its menu
234 connect(
233 connect(
235 m_Ui->variableInspectorWidget,
234 m_Ui->variableInspectorWidget,
236 SIGNAL(tableMenuAboutToBeDisplayed(QMenu *, const QVector<std::shared_ptr<Variable> > &)),
235 SIGNAL(tableMenuAboutToBeDisplayed(QMenu *, const QVector<std::shared_ptr<Variable> > &)),
237 m_Ui->view, SLOT(attachVariableMenu(QMenu *, const QVector<std::shared_ptr<Variable> > &)),
236 m_Ui->view, SLOT(attachVariableMenu(QMenu *, const QVector<std::shared_ptr<Variable> > &)),
238 Qt::DirectConnection);
237 Qt::DirectConnection);
239 }
238 }
240
239
241 MainWindow::~MainWindow()
240 MainWindow::~MainWindow()
242 {
241 {
243 }
242 }
244
243
245 void MainWindow::changeEvent(QEvent *e)
244 void MainWindow::changeEvent(QEvent *e)
246 {
245 {
247 QMainWindow::changeEvent(e);
246 QMainWindow::changeEvent(e);
248 switch (e->type()) {
247 switch (e->type()) {
249 case QEvent::LanguageChange:
248 case QEvent::LanguageChange:
250 m_Ui->retranslateUi(this);
249 m_Ui->retranslateUi(this);
251 break;
250 break;
252 default:
251 default:
253 break;
252 break;
254 }
253 }
255 }
254 }
@@ -1,69 +1,69
1 #ifndef SCIQLOP_IDATASERIES_H
1 #ifndef SCIQLOP_IDATASERIES_H
2 #define SCIQLOP_IDATASERIES_H
2 #define SCIQLOP_IDATASERIES_H
3
3
4 #include <Common/MetaTypes.h>
4 #include <Common/MetaTypes.h>
5
5
6 #include <memory>
6 #include <memory>
7
7
8 #include <QString>
8 #include <QString>
9
9
10 template <int Dim>
10 template <int Dim>
11 class ArrayData;
11 class ArrayData;
12
12
13 struct Unit {
13 struct Unit {
14 explicit Unit(const QString &name = {}, bool timeUnit = false)
14 explicit Unit(const QString &name = {}, bool timeUnit = false)
15 : m_Name{name}, m_TimeUnit{timeUnit}
15 : m_Name{name}, m_TimeUnit{timeUnit}
16 {
16 {
17 }
17 }
18
18
19 inline bool operator==(const Unit &other) const
19 inline bool operator==(const Unit &other) const
20 {
20 {
21 return std::tie(m_Name, m_TimeUnit) == std::tie(other.m_Name, other.m_TimeUnit);
21 return std::tie(m_Name, m_TimeUnit) == std::tie(other.m_Name, other.m_TimeUnit);
22 }
22 }
23 inline bool operator!=(const Unit &other) const { return !(*this == other); }
23 inline bool operator!=(const Unit &other) const { return !(*this == other); }
24
24
25 QString m_Name; ///< Unit name
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 * @brief The IDataSeries aims to declare a data series.
30 * @brief The IDataSeries aims to declare a data series.
31 *
31 *
32 * A data series is an entity that contains at least :
32 * A data series is an entity that contains at least :
33 * - one dataset representing the x-axis
33 * - one dataset representing the x-axis
34 * - one dataset representing the values
34 * - one dataset representing the values
35 *
35 *
36 * Each dataset is represented by an ArrayData, and is associated with a unit.
36 * Each dataset is represented by an ArrayData, and is associated with a unit.
37 *
37 *
38 * An ArrayData can be unidimensional or two-dimensional, depending on the implementation of the
38 * An ArrayData can be unidimensional or two-dimensional, depending on the implementation of the
39 * IDataSeries. The x-axis dataset is always unidimensional.
39 * IDataSeries. The x-axis dataset is always unidimensional.
40 *
40 *
41 * @sa ArrayData
41 * @sa ArrayData
42 */
42 */
43 class IDataSeries {
43 class IDataSeries {
44 public:
44 public:
45 virtual ~IDataSeries() noexcept = default;
45 virtual ~IDataSeries() noexcept = default;
46
46
47 /// Returns the x-axis dataset
47 /// Returns the x-axis dataset
48 virtual std::shared_ptr<ArrayData<1> > xAxisData() = 0;
48 virtual std::shared_ptr<ArrayData<1> > xAxisData() = 0;
49
49
50 /// Returns the x-axis dataset (as const)
50 /// Returns the x-axis dataset (as const)
51 virtual const std::shared_ptr<ArrayData<1> > xAxisData() const = 0;
51 virtual const std::shared_ptr<ArrayData<1> > xAxisData() const = 0;
52
52
53 virtual Unit xAxisUnit() const = 0;
53 virtual Unit xAxisUnit() const = 0;
54
54
55 virtual Unit valuesUnit() const = 0;
55 virtual Unit valuesUnit() const = 0;
56
56
57 virtual void merge(IDataSeries *dataSeries) = 0;
57 virtual void merge(IDataSeries *dataSeries) = 0;
58
58
59 virtual std::unique_ptr<IDataSeries> clone() const = 0;
59 virtual std::unique_ptr<IDataSeries> clone() const = 0;
60
60
61 virtual void lockRead() = 0;
61 virtual void lockRead() = 0;
62 virtual void lockWrite() = 0;
62 virtual void lockWrite() = 0;
63 virtual void unlock() = 0;
63 virtual void unlock() = 0;
64 };
64 };
65
65
66 // Required for using shared_ptr in signals/slots
66 // Required for using shared_ptr in signals/slots
67 SCIQLOP_REGISTER_META_TYPE(IDATASERIES_PTR_REGISTRY, std::shared_ptr<IDataSeries>)
67 SCIQLOP_REGISTER_META_TYPE(IDATASERIES_PTR_REGISTRY, std::shared_ptr<IDataSeries>)
68
68
69 #endif // SCIQLOP_IDATASERIES_H
69 #endif // SCIQLOP_IDATASERIES_H
@@ -1,44 +1,43
1 #ifndef SCIQLOP_SQPDATETIME_H
1 #ifndef SCIQLOP_SQPDATETIME_H
2 #define SCIQLOP_SQPDATETIME_H
2 #define SCIQLOP_SQPDATETIME_H
3
3
4 #include <QObject>
4 #include <QObject>
5
5
6 #include <QDateTime>
7 #include <QDebug>
6 #include <QDebug>
8
7
8 #include <Common/DateUtils.h>
9 #include <Common/MetaTypes.h>
9 #include <Common/MetaTypes.h>
10
10
11 /**
11 /**
12 * @brief The SqpDateTime struct holds the information of time parameters
12 * @brief The SqpDateTime struct holds the information of time parameters
13 */
13 */
14 struct SqpDateTime {
14 struct SqpDateTime {
15 /// Start time
15 /// Start time (UTC)
16 double m_TStart;
16 double m_TStart;
17 /// End time
17 /// End time (UTC)
18 double m_TEnd;
18 double m_TEnd;
19
19
20 bool contains(const SqpDateTime &dateTime) const noexcept
20 bool contains(const SqpDateTime &dateTime) const noexcept
21 {
21 {
22 return (m_TStart <= dateTime.m_TStart && m_TEnd >= dateTime.m_TEnd);
22 return (m_TStart <= dateTime.m_TStart && m_TEnd >= dateTime.m_TEnd);
23 }
23 }
24
24
25 bool intersect(const SqpDateTime &dateTime) const noexcept
25 bool intersect(const SqpDateTime &dateTime) const noexcept
26 {
26 {
27 return (m_TEnd >= dateTime.m_TStart && m_TStart <= dateTime.m_TEnd);
27 return (m_TEnd >= dateTime.m_TStart && m_TStart <= dateTime.m_TEnd);
28 }
28 }
29 };
29 };
30
30
31 inline QDebug operator<<(QDebug d, SqpDateTime obj)
31 inline QDebug operator<<(QDebug d, SqpDateTime obj)
32 {
32 {
33 auto tendDateTimeStart = QDateTime::fromMSecsSinceEpoch(obj.m_TStart * 1000);
33 auto tendDateTimeStart = DateUtils::dateTime(obj.m_TStart);
34 auto tendDateTimeEnd = QDateTime::fromMSecsSinceEpoch(obj.m_TEnd * 1000);
34 auto tendDateTimeEnd = DateUtils::dateTime(obj.m_TEnd);
35
35
36 // QDebug << "ts: " << tendDateTimeStart << " te: " << tendDateTimeEnd;
37 d << "ts: " << tendDateTimeStart << " te: " << tendDateTimeEnd;
36 d << "ts: " << tendDateTimeStart << " te: " << tendDateTimeEnd;
38 return d;
37 return d;
39 }
38 }
40
39
41 // Required for using shared_ptr in signals/slots
40 // Required for using shared_ptr in signals/slots
42 SCIQLOP_REGISTER_META_TYPE(SQPDATETIME_REGISTRY, SqpDateTime)
41 SCIQLOP_REGISTER_META_TYPE(SQPDATETIME_REGISTRY, SqpDateTime)
43
42
44 #endif // SCIQLOP_SQPDATETIME_H
43 #endif // SCIQLOP_SQPDATETIME_H
@@ -1,244 +1,243
1 #include <Variable/Variable.h>
1 #include <Variable/Variable.h>
2 #include <Variable/VariableCacheController.h>
2 #include <Variable/VariableCacheController.h>
3 #include <Variable/VariableController.h>
3 #include <Variable/VariableController.h>
4 #include <Variable/VariableModel.h>
4 #include <Variable/VariableModel.h>
5
5
6 #include <Data/DataProviderParameters.h>
6 #include <Data/DataProviderParameters.h>
7 #include <Data/IDataProvider.h>
7 #include <Data/IDataProvider.h>
8 #include <Data/IDataSeries.h>
8 #include <Data/IDataSeries.h>
9 #include <Time/TimeController.h>
9 #include <Time/TimeController.h>
10
10
11 #include <QDateTime>
12 #include <QMutex>
11 #include <QMutex>
13 #include <QThread>
12 #include <QThread>
14 #include <QUuid>
13 #include <QUuid>
15 #include <QtCore/QItemSelectionModel>
14 #include <QtCore/QItemSelectionModel>
16
15
17 #include <unordered_map>
16 #include <unordered_map>
18
17
19 Q_LOGGING_CATEGORY(LOG_VariableController, "VariableController")
18 Q_LOGGING_CATEGORY(LOG_VariableController, "VariableController")
20
19
21 struct VariableController::VariableControllerPrivate {
20 struct VariableController::VariableControllerPrivate {
22 explicit VariableControllerPrivate(VariableController *parent)
21 explicit VariableControllerPrivate(VariableController *parent)
23 : m_WorkingMutex{},
22 : m_WorkingMutex{},
24 m_VariableModel{new VariableModel{parent}},
23 m_VariableModel{new VariableModel{parent}},
25 m_VariableSelectionModel{new QItemSelectionModel{m_VariableModel, parent}},
24 m_VariableSelectionModel{new QItemSelectionModel{m_VariableModel, parent}},
26 m_VariableCacheController{std::make_unique<VariableCacheController>()}
25 m_VariableCacheController{std::make_unique<VariableCacheController>()}
27 {
26 {
28 }
27 }
29
28
30 QMutex m_WorkingMutex;
29 QMutex m_WorkingMutex;
31 /// Variable model. The VariableController has the ownership
30 /// Variable model. The VariableController has the ownership
32 VariableModel *m_VariableModel;
31 VariableModel *m_VariableModel;
33 QItemSelectionModel *m_VariableSelectionModel;
32 QItemSelectionModel *m_VariableSelectionModel;
34
33
35
34
36 TimeController *m_TimeController{nullptr};
35 TimeController *m_TimeController{nullptr};
37 std::unique_ptr<VariableCacheController> m_VariableCacheController;
36 std::unique_ptr<VariableCacheController> m_VariableCacheController;
38
37
39 std::unordered_map<std::shared_ptr<Variable>, std::shared_ptr<IDataProvider> >
38 std::unordered_map<std::shared_ptr<Variable>, std::shared_ptr<IDataProvider> >
40 m_VariableToProviderMap;
39 m_VariableToProviderMap;
41 std::unordered_map<std::shared_ptr<Variable>, QUuid> m_VariableToIdentifierMap;
40 std::unordered_map<std::shared_ptr<Variable>, QUuid> m_VariableToIdentifierMap;
42 };
41 };
43
42
44 VariableController::VariableController(QObject *parent)
43 VariableController::VariableController(QObject *parent)
45 : QObject{parent}, impl{spimpl::make_unique_impl<VariableControllerPrivate>(this)}
44 : QObject{parent}, impl{spimpl::make_unique_impl<VariableControllerPrivate>(this)}
46 {
45 {
47 qCDebug(LOG_VariableController()) << tr("VariableController construction")
46 qCDebug(LOG_VariableController()) << tr("VariableController construction")
48 << QThread::currentThread();
47 << QThread::currentThread();
49
48
50 connect(impl->m_VariableModel, &VariableModel::abortProgessRequested, this,
49 connect(impl->m_VariableModel, &VariableModel::abortProgessRequested, this,
51 &VariableController::onAbortProgressRequested);
50 &VariableController::onAbortProgressRequested);
52 }
51 }
53
52
54 VariableController::~VariableController()
53 VariableController::~VariableController()
55 {
54 {
56 qCDebug(LOG_VariableController()) << tr("VariableController destruction")
55 qCDebug(LOG_VariableController()) << tr("VariableController destruction")
57 << QThread::currentThread();
56 << QThread::currentThread();
58 this->waitForFinish();
57 this->waitForFinish();
59 }
58 }
60
59
61 VariableModel *VariableController::variableModel() noexcept
60 VariableModel *VariableController::variableModel() noexcept
62 {
61 {
63 return impl->m_VariableModel;
62 return impl->m_VariableModel;
64 }
63 }
65
64
66 QItemSelectionModel *VariableController::variableSelectionModel() noexcept
65 QItemSelectionModel *VariableController::variableSelectionModel() noexcept
67 {
66 {
68 return impl->m_VariableSelectionModel;
67 return impl->m_VariableSelectionModel;
69 }
68 }
70
69
71 void VariableController::setTimeController(TimeController *timeController) noexcept
70 void VariableController::setTimeController(TimeController *timeController) noexcept
72 {
71 {
73 impl->m_TimeController = timeController;
72 impl->m_TimeController = timeController;
74 }
73 }
75
74
76 void VariableController::deleteVariable(std::shared_ptr<Variable> variable) noexcept
75 void VariableController::deleteVariable(std::shared_ptr<Variable> variable) noexcept
77 {
76 {
78 if (!variable) {
77 if (!variable) {
79 qCCritical(LOG_VariableController()) << "Can't delete variable: variable is null";
78 qCCritical(LOG_VariableController()) << "Can't delete variable: variable is null";
80 return;
79 return;
81 }
80 }
82
81
83 // Spreads in SciQlop that the variable will be deleted, so that potential receivers can
82 // Spreads in SciQlop that the variable will be deleted, so that potential receivers can
84 // make some treatments before the deletion
83 // make some treatments before the deletion
85 emit variableAboutToBeDeleted(variable);
84 emit variableAboutToBeDeleted(variable);
86
85
87 // Deletes identifier
86 // Deletes identifier
88 impl->m_VariableToIdentifierMap.erase(variable);
87 impl->m_VariableToIdentifierMap.erase(variable);
89
88
90 // Deletes provider
89 // Deletes provider
91 auto nbProvidersDeleted = impl->m_VariableToProviderMap.erase(variable);
90 auto nbProvidersDeleted = impl->m_VariableToProviderMap.erase(variable);
92 qCDebug(LOG_VariableController())
91 qCDebug(LOG_VariableController())
93 << tr("Number of providers deleted for variable %1: %2")
92 << tr("Number of providers deleted for variable %1: %2")
94 .arg(variable->name(), QString::number(nbProvidersDeleted));
93 .arg(variable->name(), QString::number(nbProvidersDeleted));
95
94
96 // Clears cache
95 // Clears cache
97 impl->m_VariableCacheController->clear(variable);
96 impl->m_VariableCacheController->clear(variable);
98
97
99 // Deletes from model
98 // Deletes from model
100 impl->m_VariableModel->deleteVariable(variable);
99 impl->m_VariableModel->deleteVariable(variable);
101 }
100 }
102
101
103 void VariableController::deleteVariables(
102 void VariableController::deleteVariables(
104 const QVector<std::shared_ptr<Variable> > &variables) noexcept
103 const QVector<std::shared_ptr<Variable> > &variables) noexcept
105 {
104 {
106 for (auto variable : qAsConst(variables)) {
105 for (auto variable : qAsConst(variables)) {
107 deleteVariable(variable);
106 deleteVariable(variable);
108 }
107 }
109 }
108 }
110
109
111 void VariableController::abortProgress(std::shared_ptr<Variable> variable)
110 void VariableController::abortProgress(std::shared_ptr<Variable> variable)
112 {
111 {
113 }
112 }
114
113
115 void VariableController::createVariable(const QString &name, const QVariantHash &metadata,
114 void VariableController::createVariable(const QString &name, const QVariantHash &metadata,
116 std::shared_ptr<IDataProvider> provider) noexcept
115 std::shared_ptr<IDataProvider> provider) noexcept
117 {
116 {
118
117
119 if (!impl->m_TimeController) {
118 if (!impl->m_TimeController) {
120 qCCritical(LOG_VariableController())
119 qCCritical(LOG_VariableController())
121 << tr("Impossible to create variable: The time controller is null");
120 << tr("Impossible to create variable: The time controller is null");
122 return;
121 return;
123 }
122 }
124
123
125 auto dateTime = impl->m_TimeController->dateTime();
124 auto dateTime = impl->m_TimeController->dateTime();
126
125
127 if (auto newVariable = impl->m_VariableModel->createVariable(name, dateTime, metadata)) {
126 if (auto newVariable = impl->m_VariableModel->createVariable(name, dateTime, metadata)) {
128 auto identifier = QUuid::createUuid();
127 auto identifier = QUuid::createUuid();
129
128
130 // store the provider
129 // store the provider
131 impl->m_VariableToProviderMap[newVariable] = provider;
130 impl->m_VariableToProviderMap[newVariable] = provider;
132 impl->m_VariableToIdentifierMap[newVariable] = identifier;
131 impl->m_VariableToIdentifierMap[newVariable] = identifier;
133
132
134 auto addDateTimeAcquired = [ this, varW = std::weak_ptr<Variable>{newVariable} ](
133 auto addDateTimeAcquired = [ this, varW = std::weak_ptr<Variable>{newVariable} ](
135 QUuid identifier, auto dataSeriesAcquired, auto dateTimeToPutInCache)
134 QUuid identifier, auto dataSeriesAcquired, auto dateTimeToPutInCache)
136 {
135 {
137 if (auto variable = varW.lock()) {
136 if (auto variable = varW.lock()) {
138 auto varIdentifier = impl->m_VariableToIdentifierMap.at(variable);
137 auto varIdentifier = impl->m_VariableToIdentifierMap.at(variable);
139 if (varIdentifier == identifier) {
138 if (varIdentifier == identifier) {
140 impl->m_VariableCacheController->addDateTime(variable, dateTimeToPutInCache);
139 impl->m_VariableCacheController->addDateTime(variable, dateTimeToPutInCache);
141 variable->setDataSeries(dataSeriesAcquired);
140 variable->setDataSeries(dataSeriesAcquired);
142 emit variable->updated();
141 emit variable->updated();
143 }
142 }
144 }
143 }
145 };
144 };
146
145
147 connect(provider.get(), &IDataProvider::dataProvided, addDateTimeAcquired);
146 connect(provider.get(), &IDataProvider::dataProvided, addDateTimeAcquired);
148 connect(provider.get(), &IDataProvider::dataProvidedProgress, this,
147 connect(provider.get(), &IDataProvider::dataProvidedProgress, this,
149 &VariableController::onVariableRetrieveDataInProgress);
148 &VariableController::onVariableRetrieveDataInProgress);
150 this->onRequestDataLoading(newVariable, dateTime);
149 this->onRequestDataLoading(newVariable, dateTime);
151 }
150 }
152 }
151 }
153
152
154 void VariableController::onDateTimeOnSelection(const SqpDateTime &dateTime)
153 void VariableController::onDateTimeOnSelection(const SqpDateTime &dateTime)
155 {
154 {
156 qCDebug(LOG_VariableController()) << "VariableController::onDateTimeOnSelection"
155 qCDebug(LOG_VariableController()) << "VariableController::onDateTimeOnSelection"
157 << QThread::currentThread()->objectName();
156 << QThread::currentThread()->objectName();
158 auto selectedRows = impl->m_VariableSelectionModel->selectedRows();
157 auto selectedRows = impl->m_VariableSelectionModel->selectedRows();
159
158
160 for (const auto &selectedRow : qAsConst(selectedRows)) {
159 for (const auto &selectedRow : qAsConst(selectedRows)) {
161 if (auto selectedVariable = impl->m_VariableModel->variable(selectedRow.row())) {
160 if (auto selectedVariable = impl->m_VariableModel->variable(selectedRow.row())) {
162 selectedVariable->setDateTime(dateTime);
161 selectedVariable->setDateTime(dateTime);
163 this->onRequestDataLoading(selectedVariable, dateTime);
162 this->onRequestDataLoading(selectedVariable, dateTime);
164
163
165 // notify that rescale operation has to be done
164 // notify that rescale operation has to be done
166 emit rangeChanged(selectedVariable, dateTime);
165 emit rangeChanged(selectedVariable, dateTime);
167 }
166 }
168 }
167 }
169 }
168 }
170
169
171 void VariableController::onVariableRetrieveDataInProgress(QUuid identifier, double progress)
170 void VariableController::onVariableRetrieveDataInProgress(QUuid identifier, double progress)
172 {
171 {
173 auto findReply = [identifier](const auto &entry) { return identifier == entry.second; };
172 auto findReply = [identifier](const auto &entry) { return identifier == entry.second; };
174
173
175 auto end = impl->m_VariableToIdentifierMap.cend();
174 auto end = impl->m_VariableToIdentifierMap.cend();
176 auto it = std::find_if(impl->m_VariableToIdentifierMap.cbegin(), end, findReply);
175 auto it = std::find_if(impl->m_VariableToIdentifierMap.cbegin(), end, findReply);
177 if (it != end) {
176 if (it != end) {
178 impl->m_VariableModel->setDataProgress(it->first, progress);
177 impl->m_VariableModel->setDataProgress(it->first, progress);
179 }
178 }
180 }
179 }
181
180
182 void VariableController::onAbortProgressRequested(std::shared_ptr<Variable> variable)
181 void VariableController::onAbortProgressRequested(std::shared_ptr<Variable> variable)
183 {
182 {
184 qCDebug(LOG_VariableController()) << "TORM: VariableController::onAbortProgressRequested"
183 qCDebug(LOG_VariableController()) << "TORM: VariableController::onAbortProgressRequested"
185 << QThread::currentThread()->objectName();
184 << QThread::currentThread()->objectName();
186
185
187 auto it = impl->m_VariableToIdentifierMap.find(variable);
186 auto it = impl->m_VariableToIdentifierMap.find(variable);
188 if (it != impl->m_VariableToIdentifierMap.cend()) {
187 if (it != impl->m_VariableToIdentifierMap.cend()) {
189 impl->m_VariableToProviderMap.at(variable)->requestDataAborting(it->second);
188 impl->m_VariableToProviderMap.at(variable)->requestDataAborting(it->second);
190 }
189 }
191 else {
190 else {
192 qCWarning(LOG_VariableController())
191 qCWarning(LOG_VariableController())
193 << tr("Aborting progression of inexistant variable detected !!!")
192 << tr("Aborting progression of inexistant variable detected !!!")
194 << QThread::currentThread()->objectName();
193 << QThread::currentThread()->objectName();
195 }
194 }
196 }
195 }
197
196
198
197
199 void VariableController::onRequestDataLoading(std::shared_ptr<Variable> variable,
198 void VariableController::onRequestDataLoading(std::shared_ptr<Variable> variable,
200 const SqpDateTime &dateTime)
199 const SqpDateTime &dateTime)
201 {
200 {
202 qCDebug(LOG_VariableController()) << "VariableController::onRequestDataLoading"
201 qCDebug(LOG_VariableController()) << "VariableController::onRequestDataLoading"
203 << QThread::currentThread()->objectName();
202 << QThread::currentThread()->objectName();
204 // we want to load data of the variable for the dateTime.
203 // we want to load data of the variable for the dateTime.
205 // First we check if the cache contains some of them.
204 // First we check if the cache contains some of them.
206 // For the other, we ask the provider to give them.
205 // For the other, we ask the provider to give them.
207 if (variable) {
206 if (variable) {
208
207
209 auto dateTimeListNotInCache
208 auto dateTimeListNotInCache
210 = impl->m_VariableCacheController->provideNotInCacheDateTimeList(variable, dateTime);
209 = impl->m_VariableCacheController->provideNotInCacheDateTimeList(variable, dateTime);
211
210
212 if (!dateTimeListNotInCache.empty()) {
211 if (!dateTimeListNotInCache.empty()) {
213 // Ask the provider for each data on the dateTimeListNotInCache
212 // Ask the provider for each data on the dateTimeListNotInCache
214 auto identifier = impl->m_VariableToIdentifierMap.at(variable);
213 auto identifier = impl->m_VariableToIdentifierMap.at(variable);
215 impl->m_VariableToProviderMap.at(variable)->requestDataLoading(
214 impl->m_VariableToProviderMap.at(variable)->requestDataLoading(
216 identifier,
215 identifier,
217 DataProviderParameters{std::move(dateTimeListNotInCache), variable->metadata()});
216 DataProviderParameters{std::move(dateTimeListNotInCache), variable->metadata()});
218 }
217 }
219 else {
218 else {
220 emit variable->updated();
219 emit variable->updated();
221 }
220 }
222 }
221 }
223 else {
222 else {
224 qCCritical(LOG_VariableController()) << tr("Impossible to load data of a variable null");
223 qCCritical(LOG_VariableController()) << tr("Impossible to load data of a variable null");
225 }
224 }
226 }
225 }
227
226
228
227
229 void VariableController::initialize()
228 void VariableController::initialize()
230 {
229 {
231 qCDebug(LOG_VariableController()) << tr("VariableController init") << QThread::currentThread();
230 qCDebug(LOG_VariableController()) << tr("VariableController init") << QThread::currentThread();
232 impl->m_WorkingMutex.lock();
231 impl->m_WorkingMutex.lock();
233 qCDebug(LOG_VariableController()) << tr("VariableController init END");
232 qCDebug(LOG_VariableController()) << tr("VariableController init END");
234 }
233 }
235
234
236 void VariableController::finalize()
235 void VariableController::finalize()
237 {
236 {
238 impl->m_WorkingMutex.unlock();
237 impl->m_WorkingMutex.unlock();
239 }
238 }
240
239
241 void VariableController::waitForFinish()
240 void VariableController::waitForFinish()
242 {
241 {
243 QMutexLocker locker{&impl->m_WorkingMutex};
242 QMutexLocker locker{&impl->m_WorkingMutex};
244 }
243 }
@@ -1,248 +1,249
1 #include <Variable/Variable.h>
1 #include <Variable/Variable.h>
2 #include <Variable/VariableModel.h>
2 #include <Variable/VariableModel.h>
3
3
4 #include <Common/DateUtils.h>
5
4 #include <Data/IDataSeries.h>
6 #include <Data/IDataSeries.h>
5
7
6 #include <QDateTime>
7 #include <QSize>
8 #include <QSize>
8 #include <unordered_map>
9 #include <unordered_map>
9
10
10 Q_LOGGING_CATEGORY(LOG_VariableModel, "VariableModel")
11 Q_LOGGING_CATEGORY(LOG_VariableModel, "VariableModel")
11
12
12 namespace {
13 namespace {
13
14
14 // Column indexes
15 // Column indexes
15 const auto NAME_COLUMN = 0;
16 const auto NAME_COLUMN = 0;
16 const auto TSTART_COLUMN = 1;
17 const auto TSTART_COLUMN = 1;
17 const auto TEND_COLUMN = 2;
18 const auto TEND_COLUMN = 2;
18 const auto NB_COLUMNS = 3;
19 const auto NB_COLUMNS = 3;
19
20
20 // Column properties
21 // Column properties
21 const auto DEFAULT_HEIGHT = 25;
22 const auto DEFAULT_HEIGHT = 25;
22 const auto DEFAULT_WIDTH = 100;
23 const auto DEFAULT_WIDTH = 100;
23
24
24 struct ColumnProperties {
25 struct ColumnProperties {
25 ColumnProperties(const QString &name = {}, int width = DEFAULT_WIDTH,
26 ColumnProperties(const QString &name = {}, int width = DEFAULT_WIDTH,
26 int height = DEFAULT_HEIGHT)
27 int height = DEFAULT_HEIGHT)
27 : m_Name{name}, m_Width{width}, m_Height{height}
28 : m_Name{name}, m_Width{width}, m_Height{height}
28 {
29 {
29 }
30 }
30
31
31 QString m_Name;
32 QString m_Name;
32 int m_Width;
33 int m_Width;
33 int m_Height;
34 int m_Height;
34 };
35 };
35
36
36 const auto COLUMN_PROPERTIES
37 const auto COLUMN_PROPERTIES
37 = QHash<int, ColumnProperties>{{NAME_COLUMN, {QObject::tr("Name")}},
38 = QHash<int, ColumnProperties>{{NAME_COLUMN, {QObject::tr("Name")}},
38 {TSTART_COLUMN, {QObject::tr("tStart"), 180}},
39 {TSTART_COLUMN, {QObject::tr("tStart"), 180}},
39 {TEND_COLUMN, {QObject::tr("tEnd"), 180}}};
40 {TEND_COLUMN, {QObject::tr("tEnd"), 180}}};
40
41
41 /// Format for datetimes
42 /// Format for datetimes
42 const auto DATETIME_FORMAT = QStringLiteral("dd/MM/yyyy \nhh:mm:ss:zzz");
43 const auto DATETIME_FORMAT = QStringLiteral("dd/MM/yyyy \nhh:mm:ss:zzz");
43
44
44
45
45 } // namespace
46 } // namespace
46
47
47 struct VariableModel::VariableModelPrivate {
48 struct VariableModel::VariableModelPrivate {
48 /// Variables created in SciQlop
49 /// Variables created in SciQlop
49 std::vector<std::shared_ptr<Variable> > m_Variables;
50 std::vector<std::shared_ptr<Variable> > m_Variables;
50 std::unordered_map<std::shared_ptr<Variable>, double> m_VariableToProgress;
51 std::unordered_map<std::shared_ptr<Variable>, double> m_VariableToProgress;
51
52
52 /// Return the row index of the variable. -1 if it's not found
53 /// Return the row index of the variable. -1 if it's not found
53 int indexOfVariable(Variable *variable) const noexcept;
54 int indexOfVariable(Variable *variable) const noexcept;
54 };
55 };
55
56
56 VariableModel::VariableModel(QObject *parent)
57 VariableModel::VariableModel(QObject *parent)
57 : QAbstractTableModel{parent}, impl{spimpl::make_unique_impl<VariableModelPrivate>()}
58 : QAbstractTableModel{parent}, impl{spimpl::make_unique_impl<VariableModelPrivate>()}
58 {
59 {
59 }
60 }
60
61
61 std::shared_ptr<Variable> VariableModel::createVariable(const QString &name,
62 std::shared_ptr<Variable> VariableModel::createVariable(const QString &name,
62 const SqpDateTime &dateTime,
63 const SqpDateTime &dateTime,
63 const QVariantHash &metadata) noexcept
64 const QVariantHash &metadata) noexcept
64 {
65 {
65 auto insertIndex = rowCount();
66 auto insertIndex = rowCount();
66 beginInsertRows({}, insertIndex, insertIndex);
67 beginInsertRows({}, insertIndex, insertIndex);
67
68
68 auto variable = std::make_shared<Variable>(name, dateTime, metadata);
69 auto variable = std::make_shared<Variable>(name, dateTime, metadata);
69
70
70 impl->m_Variables.push_back(variable);
71 impl->m_Variables.push_back(variable);
71 connect(variable.get(), &Variable::updated, this, &VariableModel::onVariableUpdated);
72 connect(variable.get(), &Variable::updated, this, &VariableModel::onVariableUpdated);
72
73
73 endInsertRows();
74 endInsertRows();
74
75
75 return variable;
76 return variable;
76 }
77 }
77
78
78 void VariableModel::deleteVariable(std::shared_ptr<Variable> variable) noexcept
79 void VariableModel::deleteVariable(std::shared_ptr<Variable> variable) noexcept
79 {
80 {
80 if (!variable) {
81 if (!variable) {
81 qCCritical(LOG_Variable()) << "Can't delete a null variable from the model";
82 qCCritical(LOG_Variable()) << "Can't delete a null variable from the model";
82 return;
83 return;
83 }
84 }
84
85
85 // Finds variable in the model
86 // Finds variable in the model
86 auto begin = impl->m_Variables.cbegin();
87 auto begin = impl->m_Variables.cbegin();
87 auto end = impl->m_Variables.cend();
88 auto end = impl->m_Variables.cend();
88 auto it = std::find(begin, end, variable);
89 auto it = std::find(begin, end, variable);
89 if (it != end) {
90 if (it != end) {
90 auto removeIndex = std::distance(begin, it);
91 auto removeIndex = std::distance(begin, it);
91
92
92 // Deletes variable
93 // Deletes variable
93 beginRemoveRows({}, removeIndex, removeIndex);
94 beginRemoveRows({}, removeIndex, removeIndex);
94 impl->m_Variables.erase(it);
95 impl->m_Variables.erase(it);
95 endRemoveRows();
96 endRemoveRows();
96 }
97 }
97 else {
98 else {
98 qCritical(LOG_VariableModel())
99 qCritical(LOG_VariableModel())
99 << tr("Can't delete variable %1 from the model: the variable is not in the model")
100 << tr("Can't delete variable %1 from the model: the variable is not in the model")
100 .arg(variable->name());
101 .arg(variable->name());
101 }
102 }
102
103
103 // Removes variable from progress map
104 // Removes variable from progress map
104 impl->m_VariableToProgress.erase(variable);
105 impl->m_VariableToProgress.erase(variable);
105 }
106 }
106
107
107
108
108 std::shared_ptr<Variable> VariableModel::variable(int index) const
109 std::shared_ptr<Variable> VariableModel::variable(int index) const
109 {
110 {
110 return (index >= 0 && index < impl->m_Variables.size()) ? impl->m_Variables[index] : nullptr;
111 return (index >= 0 && index < impl->m_Variables.size()) ? impl->m_Variables[index] : nullptr;
111 }
112 }
112
113
113 void VariableModel::setDataProgress(std::shared_ptr<Variable> variable, double progress)
114 void VariableModel::setDataProgress(std::shared_ptr<Variable> variable, double progress)
114 {
115 {
115 if (progress > 0.0) {
116 if (progress > 0.0) {
116 impl->m_VariableToProgress[variable] = progress;
117 impl->m_VariableToProgress[variable] = progress;
117 }
118 }
118 else {
119 else {
119 impl->m_VariableToProgress.erase(variable);
120 impl->m_VariableToProgress.erase(variable);
120 }
121 }
121 auto modelIndex = createIndex(impl->indexOfVariable(variable.get()), NAME_COLUMN);
122 auto modelIndex = createIndex(impl->indexOfVariable(variable.get()), NAME_COLUMN);
122
123
123 emit dataChanged(modelIndex, modelIndex);
124 emit dataChanged(modelIndex, modelIndex);
124 }
125 }
125
126
126 int VariableModel::columnCount(const QModelIndex &parent) const
127 int VariableModel::columnCount(const QModelIndex &parent) const
127 {
128 {
128 Q_UNUSED(parent);
129 Q_UNUSED(parent);
129
130
130 return NB_COLUMNS;
131 return NB_COLUMNS;
131 }
132 }
132
133
133 int VariableModel::rowCount(const QModelIndex &parent) const
134 int VariableModel::rowCount(const QModelIndex &parent) const
134 {
135 {
135 Q_UNUSED(parent);
136 Q_UNUSED(parent);
136
137
137 return impl->m_Variables.size();
138 return impl->m_Variables.size();
138 }
139 }
139
140
140 QVariant VariableModel::data(const QModelIndex &index, int role) const
141 QVariant VariableModel::data(const QModelIndex &index, int role) const
141 {
142 {
142 if (!index.isValid()) {
143 if (!index.isValid()) {
143 return QVariant{};
144 return QVariant{};
144 }
145 }
145
146
146 if (index.row() < 0 || index.row() >= rowCount()) {
147 if (index.row() < 0 || index.row() >= rowCount()) {
147 return QVariant{};
148 return QVariant{};
148 }
149 }
149
150
150 if (role == Qt::DisplayRole) {
151 if (role == Qt::DisplayRole) {
151 if (auto variable = impl->m_Variables.at(index.row()).get()) {
152 if (auto variable = impl->m_Variables.at(index.row()).get()) {
152 /// Lambda function that builds the variant to return for a time value
153 /// Lambda function that builds the variant to return for a time value
153 auto dateTimeVariant = [](double time) {
154 auto dateTimeVariant = [](double secs) {
154 auto dateTime = QDateTime::fromMSecsSinceEpoch(time * 1000.);
155 auto dateTime = DateUtils::dateTime(secs);
155 return dateTime.toString(DATETIME_FORMAT);
156 return dateTime.toString(DATETIME_FORMAT);
156 };
157 };
157
158
158 switch (index.column()) {
159 switch (index.column()) {
159 case NAME_COLUMN:
160 case NAME_COLUMN:
160 return variable->name();
161 return variable->name();
161 case TSTART_COLUMN:
162 case TSTART_COLUMN:
162 return dateTimeVariant(variable->dateTime().m_TStart);
163 return dateTimeVariant(variable->dateTime().m_TStart);
163 case TEND_COLUMN:
164 case TEND_COLUMN:
164 return dateTimeVariant(variable->dateTime().m_TEnd);
165 return dateTimeVariant(variable->dateTime().m_TEnd);
165 default:
166 default:
166 // No action
167 // No action
167 break;
168 break;
168 }
169 }
169
170
170 qWarning(LOG_VariableModel())
171 qWarning(LOG_VariableModel())
171 << tr("Can't get data (unknown column %1)").arg(index.column());
172 << tr("Can't get data (unknown column %1)").arg(index.column());
172 }
173 }
173 else {
174 else {
174 qWarning(LOG_VariableModel()) << tr("Can't get data (no variable)");
175 qWarning(LOG_VariableModel()) << tr("Can't get data (no variable)");
175 }
176 }
176 }
177 }
177 else if (role == VariableRoles::ProgressRole) {
178 else if (role == VariableRoles::ProgressRole) {
178 if (auto variable = impl->m_Variables.at(index.row())) {
179 if (auto variable = impl->m_Variables.at(index.row())) {
179
180
180 auto it = impl->m_VariableToProgress.find(variable);
181 auto it = impl->m_VariableToProgress.find(variable);
181 if (it != impl->m_VariableToProgress.cend()) {
182 if (it != impl->m_VariableToProgress.cend()) {
182 return it->second;
183 return it->second;
183 }
184 }
184 }
185 }
185 }
186 }
186
187
187 return QVariant{};
188 return QVariant{};
188 }
189 }
189
190
190 QVariant VariableModel::headerData(int section, Qt::Orientation orientation, int role) const
191 QVariant VariableModel::headerData(int section, Qt::Orientation orientation, int role) const
191 {
192 {
192 if (role != Qt::DisplayRole && role != Qt::SizeHintRole) {
193 if (role != Qt::DisplayRole && role != Qt::SizeHintRole) {
193 return QVariant{};
194 return QVariant{};
194 }
195 }
195
196
196 if (orientation == Qt::Horizontal) {
197 if (orientation == Qt::Horizontal) {
197 auto propertiesIt = COLUMN_PROPERTIES.find(section);
198 auto propertiesIt = COLUMN_PROPERTIES.find(section);
198 if (propertiesIt != COLUMN_PROPERTIES.cend()) {
199 if (propertiesIt != COLUMN_PROPERTIES.cend()) {
199 // Role is either DisplayRole or SizeHintRole
200 // Role is either DisplayRole or SizeHintRole
200 return (role == Qt::DisplayRole)
201 return (role == Qt::DisplayRole)
201 ? QVariant{propertiesIt->m_Name}
202 ? QVariant{propertiesIt->m_Name}
202 : QVariant{QSize{propertiesIt->m_Width, propertiesIt->m_Height}};
203 : QVariant{QSize{propertiesIt->m_Width, propertiesIt->m_Height}};
203 }
204 }
204 else {
205 else {
205 qWarning(LOG_VariableModel())
206 qWarning(LOG_VariableModel())
206 << tr("Can't get header data (unknown column %1)").arg(section);
207 << tr("Can't get header data (unknown column %1)").arg(section);
207 }
208 }
208 }
209 }
209
210
210 return QVariant{};
211 return QVariant{};
211 }
212 }
212
213
213 void VariableModel::abortProgress(const QModelIndex &index)
214 void VariableModel::abortProgress(const QModelIndex &index)
214 {
215 {
215 if (auto variable = impl->m_Variables.at(index.row())) {
216 if (auto variable = impl->m_Variables.at(index.row())) {
216 emit abortProgessRequested(variable);
217 emit abortProgessRequested(variable);
217 }
218 }
218 }
219 }
219
220
220 void VariableModel::onVariableUpdated() noexcept
221 void VariableModel::onVariableUpdated() noexcept
221 {
222 {
222 // Finds variable that has been updated in the model
223 // Finds variable that has been updated in the model
223 if (auto updatedVariable = dynamic_cast<Variable *>(sender())) {
224 if (auto updatedVariable = dynamic_cast<Variable *>(sender())) {
224 auto updatedVariableIndex = impl->indexOfVariable(updatedVariable);
225 auto updatedVariableIndex = impl->indexOfVariable(updatedVariable);
225
226
226 if (updatedVariableIndex > -1) {
227 if (updatedVariableIndex > -1) {
227 emit dataChanged(createIndex(updatedVariableIndex, 0),
228 emit dataChanged(createIndex(updatedVariableIndex, 0),
228 createIndex(updatedVariableIndex, columnCount() - 1));
229 createIndex(updatedVariableIndex, columnCount() - 1));
229 }
230 }
230 }
231 }
231 }
232 }
232
233
233 int VariableModel::VariableModelPrivate::indexOfVariable(Variable *variable) const noexcept
234 int VariableModel::VariableModelPrivate::indexOfVariable(Variable *variable) const noexcept
234 {
235 {
235 auto begin = std::cbegin(m_Variables);
236 auto begin = std::cbegin(m_Variables);
236 auto end = std::cend(m_Variables);
237 auto end = std::cend(m_Variables);
237 auto it
238 auto it
238 = std::find_if(begin, end, [variable](const auto &var) { return var.get() == variable; });
239 = std::find_if(begin, end, [variable](const auto &var) { return var.get() == variable; });
239
240
240 if (it != end) {
241 if (it != end) {
241 // Gets the index of the variable in the model: we assume here that views have the same
242 // Gets the index of the variable in the model: we assume here that views have the same
242 // order as the model
243 // order as the model
243 return std::distance(begin, it);
244 return std::distance(begin, it);
244 }
245 }
245 else {
246 else {
246 return -1;
247 return -1;
247 }
248 }
248 }
249 }
@@ -1,82 +1,84
1 #ifndef SCIQLOP_VISUALIZATIONGRAPHWIDGET_H
1 #ifndef SCIQLOP_VISUALIZATIONGRAPHWIDGET_H
2 #define SCIQLOP_VISUALIZATIONGRAPHWIDGET_H
2 #define SCIQLOP_VISUALIZATIONGRAPHWIDGET_H
3
3
4 #include "Visualization/IVisualizationWidget.h"
4 #include "Visualization/IVisualizationWidget.h"
5
5
6 #include <QLoggingCategory>
6 #include <QLoggingCategory>
7 #include <QWidget>
7 #include <QWidget>
8
8
9 #include <memory>
9 #include <memory>
10
10
11 #include <Common/spimpl.h>
11 #include <Common/spimpl.h>
12
12
13 Q_DECLARE_LOGGING_CATEGORY(LOG_VisualizationGraphWidget)
13 Q_DECLARE_LOGGING_CATEGORY(LOG_VisualizationGraphWidget)
14
14
15 class QCPRange;
15 class QCPRange;
16 class SqpDateTime;
16 class SqpDateTime;
17 class Variable;
17 class Variable;
18
18
19 /**
19 /**
20 * Possible types of zoom operation
20 * Possible types of zoom operation
21 */
21 */
22 enum class VisualizationGraphWidgetZoomType { ZoomOut, ZoomIn, PanRight, PanLeft, Unknown };
22 enum class VisualizationGraphWidgetZoomType { ZoomOut, ZoomIn, PanRight, PanLeft, Unknown };
23
23
24 namespace Ui {
24 namespace Ui {
25 class VisualizationGraphWidget;
25 class VisualizationGraphWidget;
26 } // namespace Ui
26 } // namespace Ui
27
27
28 class VisualizationGraphWidget : public QWidget, public IVisualizationWidget {
28 class VisualizationGraphWidget : public QWidget, public IVisualizationWidget {
29 Q_OBJECT
29 Q_OBJECT
30
30
31 public:
31 public:
32 explicit VisualizationGraphWidget(const QString &name = {}, QWidget *parent = 0);
32 explicit VisualizationGraphWidget(const QString &name = {}, QWidget *parent = 0);
33 virtual ~VisualizationGraphWidget();
33 virtual ~VisualizationGraphWidget();
34
34
35 void enableSynchronize(bool enable);
35 void enableSynchronize(bool enable);
36
36
37 void addVariable(std::shared_ptr<Variable> variable);
37 void addVariable(std::shared_ptr<Variable> variable);
38 void addVariableUsingGraph(std::shared_ptr<Variable> variable);
38 void addVariableUsingGraph(std::shared_ptr<Variable> variable);
39 /// Removes a variable from the graph
39 /// Removes a variable from the graph
40 void removeVariable(std::shared_ptr<Variable> variable) noexcept;
40 void removeVariable(std::shared_ptr<Variable> variable) noexcept;
41
41
42 void setRange(std::shared_ptr<Variable> variable, const SqpDateTime &range);
42 void setRange(std::shared_ptr<Variable> variable, const SqpDateTime &range);
43 SqpDateTime graphRange() const noexcept;
43 SqpDateTime graphRange() const noexcept;
44 void setGraphRange(const SqpDateTime &range);
44 void setGraphRange(const SqpDateTime &range);
45
45
46 // IVisualizationWidget interface
46 // IVisualizationWidget interface
47 void accept(IVisualizationWidgetVisitor *visitor) override;
47 void accept(IVisualizationWidgetVisitor *visitor) override;
48 bool canDrop(const Variable &variable) const override;
48 bool canDrop(const Variable &variable) const override;
49 bool contains(const Variable &variable) const override;
49 bool contains(const Variable &variable) const override;
50 QString name() const override;
50 QString name() const override;
51
51
52
52
53 signals:
53 signals:
54 void requestDataLoading(std::shared_ptr<Variable> variable, const SqpDateTime &dateTime);
54 void requestDataLoading(std::shared_ptr<Variable> variable, const SqpDateTime &dateTime);
55 void synchronize(const SqpDateTime &dateTime, const SqpDateTime &oldDateTime,
55 void synchronize(const SqpDateTime &dateTime, const SqpDateTime &oldDateTime,
56 VisualizationGraphWidgetZoomType zoomType);
56 VisualizationGraphWidgetZoomType zoomType);
57
57
58
58
59 private:
59 private:
60 Ui::VisualizationGraphWidget *ui;
60 Ui::VisualizationGraphWidget *ui;
61
61
62 class VisualizationGraphWidgetPrivate;
62 class VisualizationGraphWidgetPrivate;
63 spimpl::unique_impl_ptr<VisualizationGraphWidgetPrivate> impl;
63 spimpl::unique_impl_ptr<VisualizationGraphWidgetPrivate> impl;
64
64
65 private slots:
65 private slots:
66 /// Slot called when right clicking on the graph (displays a menu)
66 /// Slot called when right clicking on the graph (displays a menu)
67 void onGraphMenuRequested(const QPoint &pos) noexcept;
67 void onGraphMenuRequested(const QPoint &pos) noexcept;
68
68
69 /// Rescale the X axe to range parameter
69 /// Rescale the X axe to range parameter
70 void onRangeChanged(const QCPRange &t1, const QCPRange &t2);
70 void onRangeChanged(const QCPRange &t1, const QCPRange &t2);
71
71
72 /// Slot called when a mouse move was made
73 void onMouseMove(QMouseEvent *event) noexcept;
72 /// Slot called when a mouse wheel was made, to perform some processing before the zoom is done
74 /// Slot called when a mouse wheel was made, to perform some processing before the zoom is done
73 void onMouseWheel(QWheelEvent *event) noexcept;
75 void onMouseWheel(QWheelEvent *event) noexcept;
74 /// Slot called when a mouse press was made, to activate the calibration of a graph
76 /// Slot called when a mouse press was made, to activate the calibration of a graph
75 void onMousePress(QMouseEvent *event) noexcept;
77 void onMousePress(QMouseEvent *event) noexcept;
76 /// Slot called when a mouse release was made, to deactivate the calibration of a graph
78 /// Slot called when a mouse release was made, to deactivate the calibration of a graph
77 void onMouseRelease(QMouseEvent *event) noexcept;
79 void onMouseRelease(QMouseEvent *event) noexcept;
78
80
79 void onDataCacheVariableUpdated();
81 void onDataCacheVariableUpdated();
80 };
82 };
81
83
82 #endif // SCIQLOP_VISUALIZATIONGRAPHWIDGET_H
84 #endif // SCIQLOP_VISUALIZATIONGRAPHWIDGET_H
@@ -1,48 +1,50
1 #include "TimeWidget/TimeWidget.h"
1 #include "TimeWidget/TimeWidget.h"
2 #include "ui_TimeWidget.h"
2 #include "ui_TimeWidget.h"
3
3
4 #include <Common/DateUtils.h>
4 #include <SqpApplication.h>
5 #include <SqpApplication.h>
5 #include <Time/TimeController.h>
6 #include <Time/TimeController.h>
6
7
7 TimeWidget::TimeWidget(QWidget *parent) : QWidget{parent}, ui{new Ui::TimeWidget}
8 TimeWidget::TimeWidget(QWidget *parent) : QWidget{parent}, ui{new Ui::TimeWidget}
8 {
9 {
9 ui->setupUi(this);
10 ui->setupUi(this);
10
11
11 ui->applyToolButton->setIcon(sqpApp->style()->standardIcon(QStyle::SP_DialogApplyButton));
12 ui->applyToolButton->setIcon(sqpApp->style()->standardIcon(QStyle::SP_DialogApplyButton));
12
13
13 // Connection
14 // Connection
14 connect(ui->startDateTimeEdit, &QDateTimeEdit::dateTimeChanged, this,
15 connect(ui->startDateTimeEdit, &QDateTimeEdit::dateTimeChanged, this,
15 &TimeWidget::onTimeUpdateRequested);
16 &TimeWidget::onTimeUpdateRequested);
16
17
17 connect(ui->endDateTimeEdit, &QDateTimeEdit::dateTimeChanged, this,
18 connect(ui->endDateTimeEdit, &QDateTimeEdit::dateTimeChanged, this,
18 &TimeWidget::onTimeUpdateRequested);
19 &TimeWidget::onTimeUpdateRequested);
19
20
20
21
21 connect(ui->applyToolButton, &QToolButton::clicked, &sqpApp->timeController(),
22 connect(ui->applyToolButton, &QToolButton::clicked, &sqpApp->timeController(),
22 &TimeController::onTimeNotify);
23 &TimeController::onTimeNotify);
23
24
24 // Initialisation
25 // Initialisation
25 ui->startDateTimeEdit->setDateTime(
26 auto endDateTime = QDateTime::currentDateTimeUtc();
26 QDateTime::currentDateTime().addSecs(-3600)); // one hour berefore
27 auto startDateTime = endDateTime.addSecs(-3600); // one hour before
27 ui->endDateTimeEdit->setDateTime(QDateTime::currentDateTime());
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 sqpApp->timeController().onTimeToUpdate(dateTime);
35 sqpApp->timeController().onTimeToUpdate(dateTime);
33 }
36 }
34
37
35
38
36 TimeWidget::~TimeWidget()
39 TimeWidget::~TimeWidget()
37 {
40 {
38 delete ui;
41 delete ui;
39 }
42 }
40
43
41 void TimeWidget::onTimeUpdateRequested()
44 void TimeWidget::onTimeUpdateRequested()
42 {
45 {
43 auto dateTime = SqpDateTime{
46 auto dateTime = SqpDateTime{DateUtils::secondsSinceEpoch(ui->startDateTimeEdit->dateTime()),
44 static_cast<double>(ui->startDateTimeEdit->dateTime().toMSecsSinceEpoch() / 1000.),
47 DateUtils::secondsSinceEpoch(ui->endDateTimeEdit->dateTime())};
45 static_cast<double>(ui->endDateTimeEdit->dateTime().toMSecsSinceEpoch()) / 1000.};
46
48
47 emit timeUpdated(std::move(dateTime));
49 emit timeUpdated(std::move(dateTime));
48 }
50 }
@@ -1,161 +1,162
1 #include "Visualization/VisualizationGraphHelper.h"
1 #include "Visualization/VisualizationGraphHelper.h"
2 #include "Visualization/qcustomplot.h"
2 #include "Visualization/qcustomplot.h"
3
3
4 #include <Data/ScalarSeries.h>
4 #include <Data/ScalarSeries.h>
5
5
6 #include <Variable/Variable.h>
6 #include <Variable/Variable.h>
7
7
8 Q_LOGGING_CATEGORY(LOG_VisualizationGraphHelper, "VisualizationGraphHelper")
8 Q_LOGGING_CATEGORY(LOG_VisualizationGraphHelper, "VisualizationGraphHelper")
9
9
10 namespace {
10 namespace {
11
11
12 class SqpDataContainer : public QCPGraphDataContainer {
12 class SqpDataContainer : public QCPGraphDataContainer {
13 public:
13 public:
14 void appendGraphData(const QCPGraphData &data) { mData.append(data); }
14 void appendGraphData(const QCPGraphData &data) { mData.append(data); }
15 };
15 };
16
16
17
17
18 /// Format for datetimes on a axis
18 /// Format for datetimes on a axis
19 const auto DATETIME_TICKER_FORMAT = QStringLiteral("yyyy/MM/dd \nhh:mm:ss");
19 const auto DATETIME_TICKER_FORMAT = QStringLiteral("yyyy/MM/dd \nhh:mm:ss");
20
20
21 /// Generates the appropriate ticker for an axis, depending on whether the axis displays time or
21 /// Generates the appropriate ticker for an axis, depending on whether the axis displays time or
22 /// non-time data
22 /// non-time data
23 QSharedPointer<QCPAxisTicker> axisTicker(bool isTimeAxis)
23 QSharedPointer<QCPAxisTicker> axisTicker(bool isTimeAxis)
24 {
24 {
25 if (isTimeAxis) {
25 if (isTimeAxis) {
26 auto dateTicker = QSharedPointer<QCPAxisTickerDateTime>::create();
26 auto dateTicker = QSharedPointer<QCPAxisTickerDateTime>::create();
27 dateTicker->setDateTimeFormat(DATETIME_TICKER_FORMAT);
27 dateTicker->setDateTimeFormat(DATETIME_TICKER_FORMAT);
28 dateTicker->setDateTimeSpec(Qt::UTC);
28
29
29 return dateTicker;
30 return dateTicker;
30 }
31 }
31 else {
32 else {
32 // default ticker
33 // default ticker
33 return QSharedPointer<QCPAxisTicker>::create();
34 return QSharedPointer<QCPAxisTicker>::create();
34 }
35 }
35 }
36 }
36
37
37 void updateScalarData(QCPAbstractPlottable *component, ScalarSeries &scalarSeries,
38 void updateScalarData(QCPAbstractPlottable *component, ScalarSeries &scalarSeries,
38 const SqpDateTime &dateTime)
39 const SqpDateTime &dateTime)
39 {
40 {
40 qCDebug(LOG_VisualizationGraphHelper()) << "TORM: updateScalarData"
41 qCDebug(LOG_VisualizationGraphHelper()) << "TORM: updateScalarData"
41 << QThread::currentThread()->objectName();
42 << QThread::currentThread()->objectName();
42 if (auto qcpGraph = dynamic_cast<QCPGraph *>(component)) {
43 if (auto qcpGraph = dynamic_cast<QCPGraph *>(component)) {
43 scalarSeries.lockRead();
44 scalarSeries.lockRead();
44 {
45 {
45 const auto &xData = scalarSeries.xAxisData()->cdata();
46 const auto &xData = scalarSeries.xAxisData()->cdata();
46 const auto &valuesData = scalarSeries.valuesData()->cdata();
47 const auto &valuesData = scalarSeries.valuesData()->cdata();
47
48
48 auto xDataBegin = xData.cbegin();
49 auto xDataBegin = xData.cbegin();
49 auto xDataEnd = xData.cend();
50 auto xDataEnd = xData.cend();
50
51
51 qCInfo(LOG_VisualizationGraphHelper()) << "TORM: Current points in cache"
52 qCInfo(LOG_VisualizationGraphHelper()) << "TORM: Current points in cache"
52 << xData.count();
53 << xData.count();
53
54
54 auto sqpDataContainer = QSharedPointer<SqpDataContainer>::create();
55 auto sqpDataContainer = QSharedPointer<SqpDataContainer>::create();
55 qcpGraph->setData(sqpDataContainer);
56 qcpGraph->setData(sqpDataContainer);
56
57
57 auto lowerIt = std::lower_bound(xDataBegin, xDataEnd, dateTime.m_TStart);
58 auto lowerIt = std::lower_bound(xDataBegin, xDataEnd, dateTime.m_TStart);
58 auto upperIt = std::upper_bound(xDataBegin, xDataEnd, dateTime.m_TEnd);
59 auto upperIt = std::upper_bound(xDataBegin, xDataEnd, dateTime.m_TEnd);
59 auto distance = std::distance(xDataBegin, lowerIt);
60 auto distance = std::distance(xDataBegin, lowerIt);
60
61
61 auto valuesDataIt = valuesData.cbegin() + distance;
62 auto valuesDataIt = valuesData.cbegin() + distance;
62 for (auto xAxisDataIt = lowerIt; xAxisDataIt != upperIt;
63 for (auto xAxisDataIt = lowerIt; xAxisDataIt != upperIt;
63 ++xAxisDataIt, ++valuesDataIt) {
64 ++xAxisDataIt, ++valuesDataIt) {
64 sqpDataContainer->appendGraphData(QCPGraphData(*xAxisDataIt, *valuesDataIt));
65 sqpDataContainer->appendGraphData(QCPGraphData(*xAxisDataIt, *valuesDataIt));
65 }
66 }
66
67
67 qCInfo(LOG_VisualizationGraphHelper()) << "TORM: Current points displayed"
68 qCInfo(LOG_VisualizationGraphHelper()) << "TORM: Current points displayed"
68 << sqpDataContainer->size();
69 << sqpDataContainer->size();
69 }
70 }
70 scalarSeries.unlock();
71 scalarSeries.unlock();
71
72
72
73
73 // Display all data
74 // Display all data
74 component->parentPlot()->replot();
75 component->parentPlot()->replot();
75 }
76 }
76 else {
77 else {
77 /// @todo DEBUG
78 /// @todo DEBUG
78 }
79 }
79 }
80 }
80
81
81 QCPAbstractPlottable *createScalarSeriesComponent(ScalarSeries &scalarSeries, QCustomPlot &plot,
82 QCPAbstractPlottable *createScalarSeriesComponent(ScalarSeries &scalarSeries, QCustomPlot &plot,
82 const SqpDateTime &dateTime)
83 const SqpDateTime &dateTime)
83 {
84 {
84 auto component = plot.addGraph();
85 auto component = plot.addGraph();
85
86
86 if (component) {
87 if (component) {
87 // // Graph data
88 // // Graph data
88 component->setData(scalarSeries.xAxisData()->data(), scalarSeries.valuesData()->data(),
89 component->setData(scalarSeries.xAxisData()->data(), scalarSeries.valuesData()->data(),
89 true);
90 true);
90
91
91 updateScalarData(component, scalarSeries, dateTime);
92 updateScalarData(component, scalarSeries, dateTime);
92
93
93 // Axes properties
94 // Axes properties
94 /// @todo : for the moment, no control is performed on the axes: the units and the tickers
95 /// @todo : for the moment, no control is performed on the axes: the units and the tickers
95 /// are fixed for the default x-axis and y-axis of the plot, and according to the new graph
96 /// are fixed for the default x-axis and y-axis of the plot, and according to the new graph
96
97
97 auto setAxisProperties = [](auto axis, const auto &unit) {
98 auto setAxisProperties = [](auto axis, const auto &unit) {
98 // label (unit name)
99 // label (unit name)
99 axis->setLabel(unit.m_Name);
100 axis->setLabel(unit.m_Name);
100
101
101 // ticker (depending on the type of unit)
102 // ticker (depending on the type of unit)
102 axis->setTicker(axisTicker(unit.m_TimeUnit));
103 axis->setTicker(axisTicker(unit.m_TimeUnit));
103 };
104 };
104 setAxisProperties(plot.xAxis, scalarSeries.xAxisUnit());
105 setAxisProperties(plot.xAxis, scalarSeries.xAxisUnit());
105 setAxisProperties(plot.yAxis, scalarSeries.valuesUnit());
106 setAxisProperties(plot.yAxis, scalarSeries.valuesUnit());
106
107
107 // Display all data
108 // Display all data
108 component->rescaleAxes();
109 component->rescaleAxes();
109 plot.replot();
110 plot.replot();
110 }
111 }
111 else {
112 else {
112 qCDebug(LOG_VisualizationGraphHelper())
113 qCDebug(LOG_VisualizationGraphHelper())
113 << QObject::tr("Can't create graph for the scalar series");
114 << QObject::tr("Can't create graph for the scalar series");
114 }
115 }
115
116
116 return component;
117 return component;
117 }
118 }
118
119
119 } // namespace
120 } // namespace
120
121
121 QVector<QCPAbstractPlottable *> VisualizationGraphHelper::create(std::shared_ptr<Variable> variable,
122 QVector<QCPAbstractPlottable *> VisualizationGraphHelper::create(std::shared_ptr<Variable> variable,
122 QCustomPlot &plot) noexcept
123 QCustomPlot &plot) noexcept
123 {
124 {
124 auto result = QVector<QCPAbstractPlottable *>{};
125 auto result = QVector<QCPAbstractPlottable *>{};
125
126
126 if (variable) {
127 if (variable) {
127 // Gets the data series of the variable to call the creation of the right components
128 // Gets the data series of the variable to call the creation of the right components
128 // according to its type
129 // according to its type
129 if (auto scalarSeries = dynamic_cast<ScalarSeries *>(variable->dataSeries())) {
130 if (auto scalarSeries = dynamic_cast<ScalarSeries *>(variable->dataSeries())) {
130 result.append(createScalarSeriesComponent(*scalarSeries, plot, variable->dateTime()));
131 result.append(createScalarSeriesComponent(*scalarSeries, plot, variable->dateTime()));
131 }
132 }
132 else {
133 else {
133 qCDebug(LOG_VisualizationGraphHelper())
134 qCDebug(LOG_VisualizationGraphHelper())
134 << QObject::tr("Can't create graph plottables : unmanaged data series type");
135 << QObject::tr("Can't create graph plottables : unmanaged data series type");
135 }
136 }
136 }
137 }
137 else {
138 else {
138 qCDebug(LOG_VisualizationGraphHelper())
139 qCDebug(LOG_VisualizationGraphHelper())
139 << QObject::tr("Can't create graph plottables : the variable is null");
140 << QObject::tr("Can't create graph plottables : the variable is null");
140 }
141 }
141
142
142 return result;
143 return result;
143 }
144 }
144
145
145 void VisualizationGraphHelper::updateData(QVector<QCPAbstractPlottable *> plotableVect,
146 void VisualizationGraphHelper::updateData(QVector<QCPAbstractPlottable *> plotableVect,
146 IDataSeries *dataSeries, const SqpDateTime &dateTime)
147 IDataSeries *dataSeries, const SqpDateTime &dateTime)
147 {
148 {
148 if (auto scalarSeries = dynamic_cast<ScalarSeries *>(dataSeries)) {
149 if (auto scalarSeries = dynamic_cast<ScalarSeries *>(dataSeries)) {
149 if (plotableVect.size() == 1) {
150 if (plotableVect.size() == 1) {
150 updateScalarData(plotableVect.at(0), *scalarSeries, dateTime);
151 updateScalarData(plotableVect.at(0), *scalarSeries, dateTime);
151 }
152 }
152 else {
153 else {
153 qCCritical(LOG_VisualizationGraphHelper()) << QObject::tr(
154 qCCritical(LOG_VisualizationGraphHelper()) << QObject::tr(
154 "Can't update Data of a scalarSeries because there is not only one component "
155 "Can't update Data of a scalarSeries because there is not only one component "
155 "associated");
156 "associated");
156 }
157 }
157 }
158 }
158 else {
159 else {
159 /// @todo DEBUG
160 /// @todo DEBUG
160 }
161 }
161 }
162 }
@@ -1,412 +1,428
1 #include "Visualization/VisualizationGraphWidget.h"
1 #include "Visualization/VisualizationGraphWidget.h"
2 #include "Visualization/IVisualizationWidgetVisitor.h"
2 #include "Visualization/IVisualizationWidgetVisitor.h"
3 #include "Visualization/VisualizationGraphHelper.h"
3 #include "Visualization/VisualizationGraphHelper.h"
4 #include "Visualization/VisualizationGraphRenderingDelegate.h"
4 #include "ui_VisualizationGraphWidget.h"
5 #include "ui_VisualizationGraphWidget.h"
5
6
6 #include <Data/ArrayData.h>
7 #include <Data/ArrayData.h>
7 #include <Data/IDataSeries.h>
8 #include <Data/IDataSeries.h>
8 #include <Settings/SqpSettingsDefs.h>
9 #include <Settings/SqpSettingsDefs.h>
9 #include <SqpApplication.h>
10 #include <SqpApplication.h>
10 #include <Variable/Variable.h>
11 #include <Variable/Variable.h>
11 #include <Variable/VariableController.h>
12 #include <Variable/VariableController.h>
12
13
13 #include <unordered_map>
14 #include <unordered_map>
14
15
15 Q_LOGGING_CATEGORY(LOG_VisualizationGraphWidget, "VisualizationGraphWidget")
16 Q_LOGGING_CATEGORY(LOG_VisualizationGraphWidget, "VisualizationGraphWidget")
16
17
17 namespace {
18 namespace {
18
19
19 /// Key pressed to enable zoom on horizontal axis
20 /// Key pressed to enable zoom on horizontal axis
20 const auto HORIZONTAL_ZOOM_MODIFIER = Qt::NoModifier;
21 const auto HORIZONTAL_ZOOM_MODIFIER = Qt::NoModifier;
21
22
22 /// Key pressed to enable zoom on vertical axis
23 /// Key pressed to enable zoom on vertical axis
23 const auto VERTICAL_ZOOM_MODIFIER = Qt::ControlModifier;
24 const auto VERTICAL_ZOOM_MODIFIER = Qt::ControlModifier;
24
25
25 /// Gets a tolerance value from application settings. If the setting can't be found, the default
26 /// Gets a tolerance value from application settings. If the setting can't be found, the default
26 /// value passed in parameter is returned
27 /// value passed in parameter is returned
27 double toleranceValue(const QString &key, double defaultValue) noexcept
28 double toleranceValue(const QString &key, double defaultValue) noexcept
28 {
29 {
29 return QSettings{}.value(key, defaultValue).toDouble();
30 return QSettings{}.value(key, defaultValue).toDouble();
30 }
31 }
31
32
32 } // namespace
33 } // namespace
33
34
34 struct VisualizationGraphWidget::VisualizationGraphWidgetPrivate {
35 struct VisualizationGraphWidget::VisualizationGraphWidgetPrivate {
35
36
36 explicit VisualizationGraphWidgetPrivate() : m_DoSynchronize{true}, m_IsCalibration{false} {}
37 explicit VisualizationGraphWidgetPrivate()
37
38 : m_DoSynchronize{true}, m_IsCalibration{false}, m_RenderingDelegate{nullptr}
39 {
40 }
38
41
39 // Return the operation when range changed
42 // Return the operation when range changed
40 VisualizationGraphWidgetZoomType getZoomType(const QCPRange &t1, const QCPRange &t2);
43 VisualizationGraphWidgetZoomType getZoomType(const QCPRange &t1, const QCPRange &t2);
41
44
42 // 1 variable -> n qcpplot
45 // 1 variable -> n qcpplot
43 std::multimap<std::shared_ptr<Variable>, QCPAbstractPlottable *> m_VariableToPlotMultiMap;
46 std::multimap<std::shared_ptr<Variable>, QCPAbstractPlottable *> m_VariableToPlotMultiMap;
44
45 bool m_DoSynchronize;
47 bool m_DoSynchronize;
46 bool m_IsCalibration;
48 bool m_IsCalibration;
49 QCPItemTracer *m_TextTracer;
50 /// Delegate used to attach rendering features to the plot
51 std::unique_ptr<VisualizationGraphRenderingDelegate> m_RenderingDelegate;
47 };
52 };
48
53
49 VisualizationGraphWidget::VisualizationGraphWidget(const QString &name, QWidget *parent)
54 VisualizationGraphWidget::VisualizationGraphWidget(const QString &name, QWidget *parent)
50 : QWidget{parent},
55 : QWidget{parent},
51 ui{new Ui::VisualizationGraphWidget},
56 ui{new Ui::VisualizationGraphWidget},
52 impl{spimpl::make_unique_impl<VisualizationGraphWidgetPrivate>()}
57 impl{spimpl::make_unique_impl<VisualizationGraphWidgetPrivate>()}
53 {
58 {
54 ui->setupUi(this);
59 ui->setupUi(this);
55
60
61 // The delegate must be initialized after the ui as it uses the plot
62 impl->m_RenderingDelegate = std::make_unique<VisualizationGraphRenderingDelegate>(*ui->widget);
63
56 ui->graphNameLabel->setText(name);
64 ui->graphNameLabel->setText(name);
57
65
58 // 'Close' options : widget is deleted when closed
66 // 'Close' options : widget is deleted when closed
59 setAttribute(Qt::WA_DeleteOnClose);
67 setAttribute(Qt::WA_DeleteOnClose);
60 connect(ui->closeButton, &QToolButton::clicked, this, &VisualizationGraphWidget::close);
68 connect(ui->closeButton, &QToolButton::clicked, this, &VisualizationGraphWidget::close);
61 ui->closeButton->setIcon(sqpApp->style()->standardIcon(QStyle::SP_TitleBarCloseButton));
69 ui->closeButton->setIcon(sqpApp->style()->standardIcon(QStyle::SP_TitleBarCloseButton));
62
70
63 // Set qcpplot properties :
71 // Set qcpplot properties :
64 // - Drag (on x-axis) and zoom are enabled
72 // - Drag (on x-axis) and zoom are enabled
65 // - Mouse wheel on qcpplot is intercepted to determine the zoom orientation
73 // - Mouse wheel on qcpplot is intercepted to determine the zoom orientation
66 ui->widget->setInteractions(QCP::iRangeDrag | QCP::iRangeZoom);
74 ui->widget->setInteractions(QCP::iRangeDrag | QCP::iRangeZoom);
67 ui->widget->axisRect()->setRangeDrag(Qt::Horizontal);
75 ui->widget->axisRect()->setRangeDrag(Qt::Horizontal);
76
68 connect(ui->widget, &QCustomPlot::mousePress, this, &VisualizationGraphWidget::onMousePress);
77 connect(ui->widget, &QCustomPlot::mousePress, this, &VisualizationGraphWidget::onMousePress);
69 connect(ui->widget, &QCustomPlot::mouseRelease, this,
78 connect(ui->widget, &QCustomPlot::mouseRelease, this,
70 &VisualizationGraphWidget::onMouseRelease);
79 &VisualizationGraphWidget::onMouseRelease);
80 connect(ui->widget, &QCustomPlot::mouseMove, this, &VisualizationGraphWidget::onMouseMove);
71 connect(ui->widget, &QCustomPlot::mouseWheel, this, &VisualizationGraphWidget::onMouseWheel);
81 connect(ui->widget, &QCustomPlot::mouseWheel, this, &VisualizationGraphWidget::onMouseWheel);
72 connect(ui->widget->xAxis, static_cast<void (QCPAxis::*)(const QCPRange &, const QCPRange &)>(
82 connect(ui->widget->xAxis, static_cast<void (QCPAxis::*)(const QCPRange &, const QCPRange &)>(
73 &QCPAxis::rangeChanged),
83 &QCPAxis::rangeChanged),
74 this, &VisualizationGraphWidget::onRangeChanged, Qt::DirectConnection);
84 this, &VisualizationGraphWidget::onRangeChanged, Qt::DirectConnection);
75
85
76 // Activates menu when right clicking on the graph
86 // Activates menu when right clicking on the graph
77 ui->widget->setContextMenuPolicy(Qt::CustomContextMenu);
87 ui->widget->setContextMenuPolicy(Qt::CustomContextMenu);
78 connect(ui->widget, &QCustomPlot::customContextMenuRequested, this,
88 connect(ui->widget, &QCustomPlot::customContextMenuRequested, this,
79 &VisualizationGraphWidget::onGraphMenuRequested);
89 &VisualizationGraphWidget::onGraphMenuRequested);
80
90
81 connect(this, &VisualizationGraphWidget::requestDataLoading, &sqpApp->variableController(),
91 connect(this, &VisualizationGraphWidget::requestDataLoading, &sqpApp->variableController(),
82 &VariableController::onRequestDataLoading);
92 &VariableController::onRequestDataLoading);
83 }
93 }
84
94
85
95
86 VisualizationGraphWidget::~VisualizationGraphWidget()
96 VisualizationGraphWidget::~VisualizationGraphWidget()
87 {
97 {
88 delete ui;
98 delete ui;
89 }
99 }
90
100
91 void VisualizationGraphWidget::enableSynchronize(bool enable)
101 void VisualizationGraphWidget::enableSynchronize(bool enable)
92 {
102 {
93 impl->m_DoSynchronize = enable;
103 impl->m_DoSynchronize = enable;
94 }
104 }
95
105
96 void VisualizationGraphWidget::addVariable(std::shared_ptr<Variable> variable)
106 void VisualizationGraphWidget::addVariable(std::shared_ptr<Variable> variable)
97 {
107 {
98 // Uses delegate to create the qcpplot components according to the variable
108 // Uses delegate to create the qcpplot components according to the variable
99 auto createdPlottables = VisualizationGraphHelper::create(variable, *ui->widget);
109 auto createdPlottables = VisualizationGraphHelper::create(variable, *ui->widget);
100
110
101 for (auto createdPlottable : qAsConst(createdPlottables)) {
111 for (auto createdPlottable : qAsConst(createdPlottables)) {
102 impl->m_VariableToPlotMultiMap.insert({variable, createdPlottable});
112 impl->m_VariableToPlotMultiMap.insert({variable, createdPlottable});
103 }
113 }
104
114
105 connect(variable.get(), SIGNAL(updated()), this, SLOT(onDataCacheVariableUpdated()));
115 connect(variable.get(), SIGNAL(updated()), this, SLOT(onDataCacheVariableUpdated()));
106 }
116 }
107 void VisualizationGraphWidget::addVariableUsingGraph(std::shared_ptr<Variable> variable)
117 void VisualizationGraphWidget::addVariableUsingGraph(std::shared_ptr<Variable> variable)
108 {
118 {
109
119
110 // when adding a variable, we need to set its time range to the current graph range
120 // when adding a variable, we need to set its time range to the current graph range
111 auto grapheRange = ui->widget->xAxis->range();
121 auto grapheRange = ui->widget->xAxis->range();
112 auto dateTime = SqpDateTime{grapheRange.lower, grapheRange.upper};
122 auto dateTime = SqpDateTime{grapheRange.lower, grapheRange.upper};
113 variable->setDateTime(dateTime);
123 variable->setDateTime(dateTime);
114
124
115 auto variableDateTimeWithTolerance = dateTime;
125 auto variableDateTimeWithTolerance = dateTime;
116
126
117 // add tolerance for each side
127 // add tolerance for each side
118 auto toleranceFactor
128 auto toleranceFactor
119 = toleranceValue(GENERAL_TOLERANCE_AT_INIT_KEY, GENERAL_TOLERANCE_AT_INIT_DEFAULT_VALUE);
129 = toleranceValue(GENERAL_TOLERANCE_AT_INIT_KEY, GENERAL_TOLERANCE_AT_INIT_DEFAULT_VALUE);
120 auto tolerance = toleranceFactor * (dateTime.m_TEnd - dateTime.m_TStart);
130 auto tolerance = toleranceFactor * (dateTime.m_TEnd - dateTime.m_TStart);
121 variableDateTimeWithTolerance.m_TStart -= tolerance;
131 variableDateTimeWithTolerance.m_TStart -= tolerance;
122 variableDateTimeWithTolerance.m_TEnd += tolerance;
132 variableDateTimeWithTolerance.m_TEnd += tolerance;
123
133
124 // Uses delegate to create the qcpplot components according to the variable
134 // Uses delegate to create the qcpplot components according to the variable
125 auto createdPlottables = VisualizationGraphHelper::create(variable, *ui->widget);
135 auto createdPlottables = VisualizationGraphHelper::create(variable, *ui->widget);
126
136
127 for (auto createdPlottable : qAsConst(createdPlottables)) {
137 for (auto createdPlottable : qAsConst(createdPlottables)) {
128 impl->m_VariableToPlotMultiMap.insert({variable, createdPlottable});
138 impl->m_VariableToPlotMultiMap.insert({variable, createdPlottable});
129 }
139 }
130
140
131 connect(variable.get(), SIGNAL(updated()), this, SLOT(onDataCacheVariableUpdated()));
141 connect(variable.get(), SIGNAL(updated()), this, SLOT(onDataCacheVariableUpdated()));
132
142
133 // CHangement detected, we need to ask controller to request data loading
143 // CHangement detected, we need to ask controller to request data loading
134 emit requestDataLoading(variable, variableDateTimeWithTolerance);
144 emit requestDataLoading(variable, variableDateTimeWithTolerance);
135 }
145 }
136
146
137 void VisualizationGraphWidget::removeVariable(std::shared_ptr<Variable> variable) noexcept
147 void VisualizationGraphWidget::removeVariable(std::shared_ptr<Variable> variable) noexcept
138 {
148 {
139 // Each component associated to the variable :
149 // Each component associated to the variable :
140 // - is removed from qcpplot (which deletes it)
150 // - is removed from qcpplot (which deletes it)
141 // - is no longer referenced in the map
151 // - is no longer referenced in the map
142 auto componentsIt = impl->m_VariableToPlotMultiMap.equal_range(variable);
152 auto componentsIt = impl->m_VariableToPlotMultiMap.equal_range(variable);
143 for (auto it = componentsIt.first; it != componentsIt.second;) {
153 for (auto it = componentsIt.first; it != componentsIt.second;) {
144 ui->widget->removePlottable(it->second);
154 ui->widget->removePlottable(it->second);
145 it = impl->m_VariableToPlotMultiMap.erase(it);
155 it = impl->m_VariableToPlotMultiMap.erase(it);
146 }
156 }
147
157
148 // Updates graph
158 // Updates graph
149 ui->widget->replot();
159 ui->widget->replot();
150 }
160 }
151
161
152 void VisualizationGraphWidget::setRange(std::shared_ptr<Variable> variable,
162 void VisualizationGraphWidget::setRange(std::shared_ptr<Variable> variable,
153 const SqpDateTime &range)
163 const SqpDateTime &range)
154 {
164 {
155 // Note: in case of different axes that depends on variable, we could start with a code like
165 // Note: in case of different axes that depends on variable, we could start with a code like
156 // that:
166 // that:
157 // auto componentsIt = impl->m_VariableToPlotMultiMap.equal_range(variable);
167 // auto componentsIt = impl->m_VariableToPlotMultiMap.equal_range(variable);
158 // for (auto it = componentsIt.first; it != componentsIt.second;) {
168 // for (auto it = componentsIt.first; it != componentsIt.second;) {
159 // }
169 // }
160 ui->widget->xAxis->setRange(range.m_TStart, range.m_TEnd);
170 ui->widget->xAxis->setRange(range.m_TStart, range.m_TEnd);
161 ui->widget->replot();
171 ui->widget->replot();
162 }
172 }
163
173
164 SqpDateTime VisualizationGraphWidget::graphRange() const noexcept
174 SqpDateTime VisualizationGraphWidget::graphRange() const noexcept
165 {
175 {
166 auto grapheRange = ui->widget->xAxis->range();
176 auto grapheRange = ui->widget->xAxis->range();
167 return SqpDateTime{grapheRange.lower, grapheRange.upper};
177 return SqpDateTime{grapheRange.lower, grapheRange.upper};
168 }
178 }
169
179
170 void VisualizationGraphWidget::setGraphRange(const SqpDateTime &range)
180 void VisualizationGraphWidget::setGraphRange(const SqpDateTime &range)
171 {
181 {
172 qCDebug(LOG_VisualizationGraphWidget()) << tr("VisualizationGraphWidget::setGraphRange START");
182 qCDebug(LOG_VisualizationGraphWidget()) << tr("VisualizationGraphWidget::setGraphRange START");
173 ui->widget->xAxis->setRange(range.m_TStart, range.m_TEnd);
183 ui->widget->xAxis->setRange(range.m_TStart, range.m_TEnd);
174 ui->widget->replot();
184 ui->widget->replot();
175 qCDebug(LOG_VisualizationGraphWidget()) << tr("VisualizationGraphWidget::setGraphRange END");
185 qCDebug(LOG_VisualizationGraphWidget()) << tr("VisualizationGraphWidget::setGraphRange END");
176 }
186 }
177
187
178 void VisualizationGraphWidget::accept(IVisualizationWidgetVisitor *visitor)
188 void VisualizationGraphWidget::accept(IVisualizationWidgetVisitor *visitor)
179 {
189 {
180 if (visitor) {
190 if (visitor) {
181 visitor->visit(this);
191 visitor->visit(this);
182 }
192 }
183 else {
193 else {
184 qCCritical(LOG_VisualizationGraphWidget())
194 qCCritical(LOG_VisualizationGraphWidget())
185 << tr("Can't visit widget : the visitor is null");
195 << tr("Can't visit widget : the visitor is null");
186 }
196 }
187 }
197 }
188
198
189 bool VisualizationGraphWidget::canDrop(const Variable &variable) const
199 bool VisualizationGraphWidget::canDrop(const Variable &variable) const
190 {
200 {
191 /// @todo : for the moment, a graph can always accomodate a variable
201 /// @todo : for the moment, a graph can always accomodate a variable
192 Q_UNUSED(variable);
202 Q_UNUSED(variable);
193 return true;
203 return true;
194 }
204 }
195
205
196 bool VisualizationGraphWidget::contains(const Variable &variable) const
206 bool VisualizationGraphWidget::contains(const Variable &variable) const
197 {
207 {
198 // Finds the variable among the keys of the map
208 // Finds the variable among the keys of the map
199 auto variablePtr = &variable;
209 auto variablePtr = &variable;
200 auto findVariable
210 auto findVariable
201 = [variablePtr](const auto &entry) { return variablePtr == entry.first.get(); };
211 = [variablePtr](const auto &entry) { return variablePtr == entry.first.get(); };
202
212
203 auto end = impl->m_VariableToPlotMultiMap.cend();
213 auto end = impl->m_VariableToPlotMultiMap.cend();
204 auto it = std::find_if(impl->m_VariableToPlotMultiMap.cbegin(), end, findVariable);
214 auto it = std::find_if(impl->m_VariableToPlotMultiMap.cbegin(), end, findVariable);
205 return it != end;
215 return it != end;
206 }
216 }
207
217
208 QString VisualizationGraphWidget::name() const
218 QString VisualizationGraphWidget::name() const
209 {
219 {
210 return ui->graphNameLabel->text();
220 return ui->graphNameLabel->text();
211 }
221 }
212
222
213 void VisualizationGraphWidget::onGraphMenuRequested(const QPoint &pos) noexcept
223 void VisualizationGraphWidget::onGraphMenuRequested(const QPoint &pos) noexcept
214 {
224 {
215 QMenu graphMenu{};
225 QMenu graphMenu{};
216
226
217 // Iterates on variables (unique keys)
227 // Iterates on variables (unique keys)
218 for (auto it = impl->m_VariableToPlotMultiMap.cbegin(),
228 for (auto it = impl->m_VariableToPlotMultiMap.cbegin(),
219 end = impl->m_VariableToPlotMultiMap.cend();
229 end = impl->m_VariableToPlotMultiMap.cend();
220 it != end; it = impl->m_VariableToPlotMultiMap.upper_bound(it->first)) {
230 it != end; it = impl->m_VariableToPlotMultiMap.upper_bound(it->first)) {
221 // 'Remove variable' action
231 // 'Remove variable' action
222 graphMenu.addAction(tr("Remove variable %1").arg(it->first->name()),
232 graphMenu.addAction(tr("Remove variable %1").arg(it->first->name()),
223 [ this, var = it->first ]() { removeVariable(var); });
233 [ this, var = it->first ]() { removeVariable(var); });
224 }
234 }
225
235
226 if (!graphMenu.isEmpty()) {
236 if (!graphMenu.isEmpty()) {
227 graphMenu.exec(mapToGlobal(pos));
237 graphMenu.exec(mapToGlobal(pos));
228 }
238 }
229 }
239 }
230
240
231 void VisualizationGraphWidget::onRangeChanged(const QCPRange &t1, const QCPRange &t2)
241 void VisualizationGraphWidget::onRangeChanged(const QCPRange &t1, const QCPRange &t2)
232 {
242 {
233 qCInfo(LOG_VisualizationGraphWidget()) << tr("VisualizationGraphWidget::onRangeChanged")
243 qCInfo(LOG_VisualizationGraphWidget()) << tr("VisualizationGraphWidget::onRangeChanged")
234 << QThread::currentThread()->objectName();
244 << QThread::currentThread()->objectName();
235
245
236 auto dateTimeRange = SqpDateTime{t1.lower, t1.upper};
246 auto dateTimeRange = SqpDateTime{t1.lower, t1.upper};
237
247
238 auto zoomType = impl->getZoomType(t1, t2);
248 auto zoomType = impl->getZoomType(t1, t2);
239 for (auto it = impl->m_VariableToPlotMultiMap.cbegin();
249 for (auto it = impl->m_VariableToPlotMultiMap.cbegin();
240 it != impl->m_VariableToPlotMultiMap.cend(); ++it) {
250 it != impl->m_VariableToPlotMultiMap.cend(); ++it) {
241
251
242 auto variable = it->first;
252 auto variable = it->first;
243 auto currentDateTime = dateTimeRange;
253 auto currentDateTime = dateTimeRange;
244
254
245 auto toleranceFactor = toleranceValue(GENERAL_TOLERANCE_AT_UPDATE_KEY,
255 auto toleranceFactor = toleranceValue(GENERAL_TOLERANCE_AT_UPDATE_KEY,
246 GENERAL_TOLERANCE_AT_UPDATE_DEFAULT_VALUE);
256 GENERAL_TOLERANCE_AT_UPDATE_DEFAULT_VALUE);
247 auto tolerance = toleranceFactor * (currentDateTime.m_TEnd - currentDateTime.m_TStart);
257 auto tolerance = toleranceFactor * (currentDateTime.m_TEnd - currentDateTime.m_TStart);
248 auto variableDateTimeWithTolerance = currentDateTime;
258 auto variableDateTimeWithTolerance = currentDateTime;
249 variableDateTimeWithTolerance.m_TStart -= tolerance;
259 variableDateTimeWithTolerance.m_TStart -= tolerance;
250 variableDateTimeWithTolerance.m_TEnd += tolerance;
260 variableDateTimeWithTolerance.m_TEnd += tolerance;
251
261
252 qCDebug(LOG_VisualizationGraphWidget()) << "r" << currentDateTime;
262 qCDebug(LOG_VisualizationGraphWidget()) << "r" << currentDateTime;
253 qCDebug(LOG_VisualizationGraphWidget()) << "t" << variableDateTimeWithTolerance;
263 qCDebug(LOG_VisualizationGraphWidget()) << "t" << variableDateTimeWithTolerance;
254 qCDebug(LOG_VisualizationGraphWidget()) << "v" << variable->dateTime();
264 qCDebug(LOG_VisualizationGraphWidget()) << "v" << variable->dateTime();
255 // If new range with tol is upper than variable datetime parameters. we need to request new
265 // If new range with tol is upper than variable datetime parameters. we need to request new
256 // data
266 // data
257 if (!variable->contains(variableDateTimeWithTolerance)) {
267 if (!variable->contains(variableDateTimeWithTolerance)) {
258
268
259 auto variableDateTimeWithTolerance = currentDateTime;
269 auto variableDateTimeWithTolerance = currentDateTime;
260 if (!variable->isInside(currentDateTime)) {
270 if (!variable->isInside(currentDateTime)) {
261 auto variableDateTime = variable->dateTime();
271 auto variableDateTime = variable->dateTime();
262 if (variable->contains(variableDateTimeWithTolerance)) {
272 if (variable->contains(variableDateTimeWithTolerance)) {
263 qCDebug(LOG_VisualizationGraphWidget())
273 qCDebug(LOG_VisualizationGraphWidget())
264 << tr("TORM: Detection zoom in that need request:");
274 << tr("TORM: Detection zoom in that need request:");
265 // add tolerance for each side
275 // add tolerance for each side
266 tolerance
276 tolerance
267 = toleranceFactor * (currentDateTime.m_TEnd - currentDateTime.m_TStart);
277 = toleranceFactor * (currentDateTime.m_TEnd - currentDateTime.m_TStart);
268 variableDateTimeWithTolerance.m_TStart -= tolerance;
278 variableDateTimeWithTolerance.m_TStart -= tolerance;
269 variableDateTimeWithTolerance.m_TEnd += tolerance;
279 variableDateTimeWithTolerance.m_TEnd += tolerance;
270 }
280 }
271 else if (variableDateTime.m_TStart < currentDateTime.m_TStart) {
281 else if (variableDateTime.m_TStart < currentDateTime.m_TStart) {
272 qCInfo(LOG_VisualizationGraphWidget()) << tr("TORM: Detection pan to right:");
282 qCInfo(LOG_VisualizationGraphWidget()) << tr("TORM: Detection pan to right:");
273
283
274 auto diffEndToKeepDelta = currentDateTime.m_TEnd - variableDateTime.m_TEnd;
284 auto diffEndToKeepDelta = currentDateTime.m_TEnd - variableDateTime.m_TEnd;
275 currentDateTime.m_TStart = variableDateTime.m_TStart + diffEndToKeepDelta;
285 currentDateTime.m_TStart = variableDateTime.m_TStart + diffEndToKeepDelta;
276 // Tolerance have to be added to the right
286 // Tolerance have to be added to the right
277 // add tolerance for right (end) side
287 // add tolerance for right (end) side
278 tolerance
288 tolerance
279 = toleranceFactor * (currentDateTime.m_TEnd - currentDateTime.m_TStart);
289 = toleranceFactor * (currentDateTime.m_TEnd - currentDateTime.m_TStart);
280 variableDateTimeWithTolerance.m_TEnd += tolerance;
290 variableDateTimeWithTolerance.m_TEnd += tolerance;
281 }
291 }
282 else if (variableDateTime.m_TEnd > currentDateTime.m_TEnd) {
292 else if (variableDateTime.m_TEnd > currentDateTime.m_TEnd) {
283 qCDebug(LOG_VisualizationGraphWidget()) << tr("TORM: Detection pan to left: ");
293 qCDebug(LOG_VisualizationGraphWidget()) << tr("TORM: Detection pan to left: ");
284 auto diffStartToKeepDelta
294 auto diffStartToKeepDelta
285 = variableDateTime.m_TStart - currentDateTime.m_TStart;
295 = variableDateTime.m_TStart - currentDateTime.m_TStart;
286 currentDateTime.m_TEnd = variableDateTime.m_TEnd - diffStartToKeepDelta;
296 currentDateTime.m_TEnd = variableDateTime.m_TEnd - diffStartToKeepDelta;
287 // Tolerance have to be added to the left
297 // Tolerance have to be added to the left
288 // add tolerance for left (start) side
298 // add tolerance for left (start) side
289 tolerance
299 tolerance
290 = toleranceFactor * (currentDateTime.m_TEnd - currentDateTime.m_TStart);
300 = toleranceFactor * (currentDateTime.m_TEnd - currentDateTime.m_TStart);
291 variableDateTimeWithTolerance.m_TStart -= tolerance;
301 variableDateTimeWithTolerance.m_TStart -= tolerance;
292 }
302 }
293 else {
303 else {
294 qCCritical(LOG_VisualizationGraphWidget())
304 qCCritical(LOG_VisualizationGraphWidget())
295 << tr("Detection anormal zoom detection: ");
305 << tr("Detection anormal zoom detection: ");
296 }
306 }
297 }
307 }
298 else {
308 else {
299 qCDebug(LOG_VisualizationGraphWidget()) << tr("TORM: Detection zoom out: ");
309 qCDebug(LOG_VisualizationGraphWidget()) << tr("TORM: Detection zoom out: ");
300 // add tolerance for each side
310 // add tolerance for each side
301 tolerance = toleranceFactor * (currentDateTime.m_TEnd - currentDateTime.m_TStart);
311 tolerance = toleranceFactor * (currentDateTime.m_TEnd - currentDateTime.m_TStart);
302 variableDateTimeWithTolerance.m_TStart -= tolerance;
312 variableDateTimeWithTolerance.m_TStart -= tolerance;
303 variableDateTimeWithTolerance.m_TEnd += tolerance;
313 variableDateTimeWithTolerance.m_TEnd += tolerance;
304 zoomType = VisualizationGraphWidgetZoomType::ZoomOut;
314 zoomType = VisualizationGraphWidgetZoomType::ZoomOut;
305 }
315 }
306 if (!variable->contains(dateTimeRange)) {
316 if (!variable->contains(dateTimeRange)) {
307 qCDebug(LOG_VisualizationGraphWidget())
317 qCDebug(LOG_VisualizationGraphWidget())
308 << "TORM: Modif on variable datetime detected" << currentDateTime;
318 << "TORM: Modif on variable datetime detected" << currentDateTime;
309 variable->setDateTime(currentDateTime);
319 variable->setDateTime(currentDateTime);
310 }
320 }
311
321
312 qCDebug(LOG_VisualizationGraphWidget()) << tr("TORM: Request data detection: ");
322 qCDebug(LOG_VisualizationGraphWidget()) << tr("TORM: Request data detection: ");
313 // CHangement detected, we need to ask controller to request data loading
323 // CHangement detected, we need to ask controller to request data loading
314 emit requestDataLoading(variable, variableDateTimeWithTolerance);
324 emit requestDataLoading(variable, variableDateTimeWithTolerance);
315 }
325 }
316 else {
326 else {
317 qCInfo(LOG_VisualizationGraphWidget())
327 qCInfo(LOG_VisualizationGraphWidget())
318 << tr("TORM: Detection zoom in that doesn't need request: ");
328 << tr("TORM: Detection zoom in that doesn't need request: ");
319 zoomType = VisualizationGraphWidgetZoomType::ZoomIn;
329 zoomType = VisualizationGraphWidgetZoomType::ZoomIn;
320 }
330 }
321 }
331 }
322
332
323 if (impl->m_DoSynchronize && !impl->m_IsCalibration) {
333 if (impl->m_DoSynchronize && !impl->m_IsCalibration) {
324 auto oldDateTime = SqpDateTime{t2.lower, t2.upper};
334 auto oldDateTime = SqpDateTime{t2.lower, t2.upper};
325 qCDebug(LOG_VisualizationGraphWidget())
335 qCDebug(LOG_VisualizationGraphWidget())
326 << tr("TORM: VisualizationGraphWidget::Synchronize notify !!")
336 << tr("TORM: VisualizationGraphWidget::Synchronize notify !!")
327 << QThread::currentThread()->objectName();
337 << QThread::currentThread()->objectName();
328 emit synchronize(dateTimeRange, oldDateTime, zoomType);
338 emit synchronize(dateTimeRange, oldDateTime, zoomType);
329 }
339 }
330 }
340 }
331
341
342 void VisualizationGraphWidget::onMouseMove(QMouseEvent *event) noexcept
343 {
344 // Handles plot rendering when mouse is moving
345 impl->m_RenderingDelegate->onMouseMove(event);
346 }
347
332 void VisualizationGraphWidget::onMouseWheel(QWheelEvent *event) noexcept
348 void VisualizationGraphWidget::onMouseWheel(QWheelEvent *event) noexcept
333 {
349 {
334 auto zoomOrientations = QFlags<Qt::Orientation>{};
350 auto zoomOrientations = QFlags<Qt::Orientation>{};
335
351
336 // Lambda that enables a zoom orientation if the key modifier related to this orientation
352 // Lambda that enables a zoom orientation if the key modifier related to this orientation
337 // has
353 // has
338 // been pressed
354 // been pressed
339 auto enableOrientation
355 auto enableOrientation
340 = [&zoomOrientations, event](const auto &orientation, const auto &modifier) {
356 = [&zoomOrientations, event](const auto &orientation, const auto &modifier) {
341 auto orientationEnabled = event->modifiers().testFlag(modifier);
357 auto orientationEnabled = event->modifiers().testFlag(modifier);
342 zoomOrientations.setFlag(orientation, orientationEnabled);
358 zoomOrientations.setFlag(orientation, orientationEnabled);
343 };
359 };
344 enableOrientation(Qt::Vertical, VERTICAL_ZOOM_MODIFIER);
360 enableOrientation(Qt::Vertical, VERTICAL_ZOOM_MODIFIER);
345 enableOrientation(Qt::Horizontal, HORIZONTAL_ZOOM_MODIFIER);
361 enableOrientation(Qt::Horizontal, HORIZONTAL_ZOOM_MODIFIER);
346
362
347 ui->widget->axisRect()->setRangeZoom(zoomOrientations);
363 ui->widget->axisRect()->setRangeZoom(zoomOrientations);
348 }
364 }
349
365
350 void VisualizationGraphWidget::onMousePress(QMouseEvent *event) noexcept
366 void VisualizationGraphWidget::onMousePress(QMouseEvent *event) noexcept
351 {
367 {
352 impl->m_IsCalibration = event->modifiers().testFlag(Qt::ControlModifier);
368 impl->m_IsCalibration = event->modifiers().testFlag(Qt::ControlModifier);
353 }
369 }
354
370
355 void VisualizationGraphWidget::onMouseRelease(QMouseEvent *event) noexcept
371 void VisualizationGraphWidget::onMouseRelease(QMouseEvent *event) noexcept
356 {
372 {
357 impl->m_IsCalibration = false;
373 impl->m_IsCalibration = false;
358 }
374 }
359
375
360 void VisualizationGraphWidget::onDataCacheVariableUpdated()
376 void VisualizationGraphWidget::onDataCacheVariableUpdated()
361 {
377 {
362 // NOTE:
378 // NOTE:
363 // We don't want to call the method for each component of a variable unitarily, but for
379 // We don't want to call the method for each component of a variable unitarily, but for
364 // all
380 // all
365 // its components at once (eg its three components in the case of a vector).
381 // its components at once (eg its three components in the case of a vector).
366
382
367 // The unordered_multimap does not do this easily, so the question is whether to:
383 // The unordered_multimap does not do this easily, so the question is whether to:
368 // - use an ordered_multimap and the algos of std to group the values by key
384 // - use an ordered_multimap and the algos of std to group the values by key
369 // - use a map (unique keys) and store as values directly the list of components
385 // - use a map (unique keys) and store as values directly the list of components
370
386
371 auto grapheRange = ui->widget->xAxis->range();
387 auto grapheRange = ui->widget->xAxis->range();
372 auto dateTime = SqpDateTime{grapheRange.lower, grapheRange.upper};
388 auto dateTime = SqpDateTime{grapheRange.lower, grapheRange.upper};
373
389
374 for (auto it = impl->m_VariableToPlotMultiMap.cbegin();
390 for (auto it = impl->m_VariableToPlotMultiMap.cbegin();
375 it != impl->m_VariableToPlotMultiMap.cend(); ++it) {
391 it != impl->m_VariableToPlotMultiMap.cend(); ++it) {
376 auto variable = it->first;
392 auto variable = it->first;
377 qCDebug(LOG_VisualizationGraphWidget())
393 qCDebug(LOG_VisualizationGraphWidget())
378 << "TORM: VisualizationGraphWidget::onDataCacheVariableUpdated S"
394 << "TORM: VisualizationGraphWidget::onDataCacheVariableUpdated S"
379 << variable->dateTime();
395 << variable->dateTime();
380 qCDebug(LOG_VisualizationGraphWidget())
396 qCDebug(LOG_VisualizationGraphWidget())
381 << "TORM: VisualizationGraphWidget::onDataCacheVariableUpdated E" << dateTime;
397 << "TORM: VisualizationGraphWidget::onDataCacheVariableUpdated E" << dateTime;
382 if (dateTime.contains(variable->dateTime()) || dateTime.intersect(variable->dateTime())) {
398 if (dateTime.contains(variable->dateTime()) || dateTime.intersect(variable->dateTime())) {
383
399
384 VisualizationGraphHelper::updateData(QVector<QCPAbstractPlottable *>{} << it->second,
400 VisualizationGraphHelper::updateData(QVector<QCPAbstractPlottable *>{} << it->second,
385 variable->dataSeries(), variable->dateTime());
401 variable->dataSeries(), variable->dateTime());
386 }
402 }
387 }
403 }
388 }
404 }
389
405
390 VisualizationGraphWidgetZoomType
406 VisualizationGraphWidgetZoomType
391 VisualizationGraphWidget::VisualizationGraphWidgetPrivate::getZoomType(const QCPRange &t1,
407 VisualizationGraphWidget::VisualizationGraphWidgetPrivate::getZoomType(const QCPRange &t1,
392 const QCPRange &t2)
408 const QCPRange &t2)
393 {
409 {
394 // t1.lower <= t2.lower && t2.upper <= t1.upper
410 // t1.lower <= t2.lower && t2.upper <= t1.upper
395 auto zoomType = VisualizationGraphWidgetZoomType::Unknown;
411 auto zoomType = VisualizationGraphWidgetZoomType::Unknown;
396 if (t1.lower <= t2.lower && t2.upper <= t1.upper) {
412 if (t1.lower <= t2.lower && t2.upper <= t1.upper) {
397 zoomType = VisualizationGraphWidgetZoomType::ZoomOut;
413 zoomType = VisualizationGraphWidgetZoomType::ZoomOut;
398 }
414 }
399 else if (t1.lower > t2.lower && t1.upper > t2.upper) {
415 else if (t1.lower > t2.lower && t1.upper > t2.upper) {
400 zoomType = VisualizationGraphWidgetZoomType::PanRight;
416 zoomType = VisualizationGraphWidgetZoomType::PanRight;
401 }
417 }
402 else if (t1.lower < t2.lower && t1.upper < t2.upper) {
418 else if (t1.lower < t2.lower && t1.upper < t2.upper) {
403 zoomType = VisualizationGraphWidgetZoomType::PanLeft;
419 zoomType = VisualizationGraphWidgetZoomType::PanLeft;
404 }
420 }
405 else if (t1.lower > t2.lower && t2.upper > t1.upper) {
421 else if (t1.lower > t2.lower && t2.upper > t1.upper) {
406 zoomType = VisualizationGraphWidgetZoomType::ZoomIn;
422 zoomType = VisualizationGraphWidgetZoomType::ZoomIn;
407 }
423 }
408 else {
424 else {
409 qCCritical(LOG_VisualizationGraphWidget()) << "getZoomType: Unknown type detected";
425 qCCritical(LOG_VisualizationGraphWidget()) << "getZoomType: Unknown type detected";
410 }
426 }
411 return zoomType;
427 return zoomType;
412 }
428 }
@@ -1,92 +1,98
1 <?xml version="1.0" encoding="UTF-8"?>
1 <?xml version="1.0" encoding="UTF-8"?>
2 <ui version="4.0">
2 <ui version="4.0">
3 <class>TimeWidget</class>
3 <class>TimeWidget</class>
4 <widget class="QWidget" name="TimeWidget">
4 <widget class="QWidget" name="TimeWidget">
5 <property name="geometry">
5 <property name="geometry">
6 <rect>
6 <rect>
7 <x>0</x>
7 <x>0</x>
8 <y>0</y>
8 <y>0</y>
9 <width>716</width>
9 <width>716</width>
10 <height>48</height>
10 <height>48</height>
11 </rect>
11 </rect>
12 </property>
12 </property>
13 <property name="sizePolicy">
13 <property name="sizePolicy">
14 <sizepolicy hsizetype="Fixed" vsizetype="Preferred">
14 <sizepolicy hsizetype="Fixed" vsizetype="Preferred">
15 <horstretch>0</horstretch>
15 <horstretch>0</horstretch>
16 <verstretch>0</verstretch>
16 <verstretch>0</verstretch>
17 </sizepolicy>
17 </sizepolicy>
18 </property>
18 </property>
19 <property name="windowTitle">
19 <property name="windowTitle">
20 <string>Form</string>
20 <string>Form</string>
21 </property>
21 </property>
22 <layout class="QHBoxLayout" name="horizontalLayout_2">
22 <layout class="QHBoxLayout" name="horizontalLayout_2">
23 <item>
23 <item>
24 <widget class="QLabel" name="label">
24 <widget class="QLabel" name="label">
25 <property name="sizePolicy">
25 <property name="sizePolicy">
26 <sizepolicy hsizetype="Minimum" vsizetype="Preferred">
26 <sizepolicy hsizetype="Minimum" vsizetype="Preferred">
27 <horstretch>0</horstretch>
27 <horstretch>0</horstretch>
28 <verstretch>0</verstretch>
28 <verstretch>0</verstretch>
29 </sizepolicy>
29 </sizepolicy>
30 </property>
30 </property>
31 <property name="text">
31 <property name="text">
32 <string>TStart :</string>
32 <string>TStart :</string>
33 </property>
33 </property>
34 </widget>
34 </widget>
35 </item>
35 </item>
36 <item>
36 <item>
37 <widget class="QDateTimeEdit" name="startDateTimeEdit">
37 <widget class="QDateTimeEdit" name="startDateTimeEdit">
38 <property name="sizePolicy">
38 <property name="sizePolicy">
39 <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
39 <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
40 <horstretch>0</horstretch>
40 <horstretch>0</horstretch>
41 <verstretch>0</verstretch>
41 <verstretch>0</verstretch>
42 </sizepolicy>
42 </sizepolicy>
43 </property>
43 </property>
44 <property name="displayFormat">
44 <property name="displayFormat">
45 <string>dd/MM/yyyy HH:mm:ss:zzz</string>
45 <string>dd/MM/yyyy HH:mm:ss:zzz</string>
46 </property>
46 </property>
47 <property name="calendarPopup">
47 <property name="calendarPopup">
48 <bool>true</bool>
48 <bool>true</bool>
49 </property>
49 </property>
50 <property name="timeSpec">
51 <enum>Qt::UTC</enum>
52 </property>
50 </widget>
53 </widget>
51 </item>
54 </item>
52 <item>
55 <item>
53 <widget class="QLabel" name="label_2">
56 <widget class="QLabel" name="label_2">
54 <property name="sizePolicy">
57 <property name="sizePolicy">
55 <sizepolicy hsizetype="Minimum" vsizetype="Preferred">
58 <sizepolicy hsizetype="Minimum" vsizetype="Preferred">
56 <horstretch>0</horstretch>
59 <horstretch>0</horstretch>
57 <verstretch>0</verstretch>
60 <verstretch>0</verstretch>
58 </sizepolicy>
61 </sizepolicy>
59 </property>
62 </property>
60 <property name="text">
63 <property name="text">
61 <string>TEnd :</string>
64 <string>TEnd :</string>
62 </property>
65 </property>
63 </widget>
66 </widget>
64 </item>
67 </item>
65 <item>
68 <item>
66 <widget class="QDateTimeEdit" name="endDateTimeEdit">
69 <widget class="QDateTimeEdit" name="endDateTimeEdit">
67 <property name="sizePolicy">
70 <property name="sizePolicy">
68 <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
71 <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
69 <horstretch>0</horstretch>
72 <horstretch>0</horstretch>
70 <verstretch>0</verstretch>
73 <verstretch>0</verstretch>
71 </sizepolicy>
74 </sizepolicy>
72 </property>
75 </property>
73 <property name="displayFormat">
76 <property name="displayFormat">
74 <string>dd/MM/yyyy HH:mm:ss:zzz</string>
77 <string>dd/MM/yyyy HH:mm:ss:zzz</string>
75 </property>
78 </property>
76 <property name="calendarPopup">
79 <property name="calendarPopup">
77 <bool>true</bool>
80 <bool>true</bool>
78 </property>
81 </property>
82 <property name="timeSpec">
83 <enum>Qt::UTC</enum>
84 </property>
79 </widget>
85 </widget>
80 </item>
86 </item>
81 <item>
87 <item>
82 <widget class="QToolButton" name="applyToolButton">
88 <widget class="QToolButton" name="applyToolButton">
83 <property name="text">
89 <property name="text">
84 <string>...</string>
90 <string>...</string>
85 </property>
91 </property>
86 </widget>
92 </widget>
87 </item>
93 </item>
88 </layout>
94 </layout>
89 </widget>
95 </widget>
90 <resources/>
96 <resources/>
91 <connections/>
97 <connections/>
92 </ui>
98 </ui>
@@ -1,147 +1,148
1 #include "AmdaProvider.h"
1 #include "AmdaProvider.h"
2 #include "AmdaDefs.h"
2 #include "AmdaDefs.h"
3 #include "AmdaResultParser.h"
3 #include "AmdaResultParser.h"
4
4
5 #include <Common/DateUtils.h>
5 #include <Data/DataProviderParameters.h>
6 #include <Data/DataProviderParameters.h>
6 #include <Network/NetworkController.h>
7 #include <Network/NetworkController.h>
7 #include <SqpApplication.h>
8 #include <SqpApplication.h>
8 #include <Variable/Variable.h>
9 #include <Variable/Variable.h>
9
10
10 #include <QNetworkAccessManager>
11 #include <QNetworkAccessManager>
11 #include <QNetworkReply>
12 #include <QNetworkReply>
12 #include <QTemporaryFile>
13 #include <QTemporaryFile>
13 #include <QThread>
14 #include <QThread>
14
15
15 Q_LOGGING_CATEGORY(LOG_AmdaProvider, "AmdaProvider")
16 Q_LOGGING_CATEGORY(LOG_AmdaProvider, "AmdaProvider")
16
17
17 namespace {
18 namespace {
18
19
19 /// URL format for a request on AMDA server. The parameters are as follows:
20 /// URL format for a request on AMDA server. The parameters are as follows:
20 /// - %1: start date
21 /// - %1: start date
21 /// - %2: end date
22 /// - %2: end date
22 /// - %3: parameter id
23 /// - %3: parameter id
23 const auto AMDA_URL_FORMAT = QStringLiteral(
24 const auto AMDA_URL_FORMAT = QStringLiteral(
24 "http://amda.irap.omp.eu/php/rest/"
25 "http://amda.irap.omp.eu/php/rest/"
25 "getParameter.php?startTime=%1&stopTime=%2&parameterID=%3&outputFormat=ASCII&"
26 "getParameter.php?startTime=%1&stopTime=%2&parameterID=%3&outputFormat=ASCII&"
26 "timeFormat=ISO8601&gzip=0");
27 "timeFormat=ISO8601&gzip=0");
27
28
28 /// Dates format passed in the URL (e.g 2013-09-23T09:00)
29 /// Dates format passed in the URL (e.g 2013-09-23T09:00)
29 const auto AMDA_TIME_FORMAT = QStringLiteral("yyyy-MM-ddThh:mm:ss");
30 const auto AMDA_TIME_FORMAT = QStringLiteral("yyyy-MM-ddThh:mm:ss");
30
31
31 /// Formats a time to a date that can be passed in URL
32 /// Formats a time to a date that can be passed in URL
32 QString dateFormat(double sqpDateTime) noexcept
33 QString dateFormat(double sqpDateTime) noexcept
33 {
34 {
34 auto dateTime = QDateTime::fromMSecsSinceEpoch(sqpDateTime * 1000.);
35 auto dateTime = DateUtils::dateTime(sqpDateTime);
35 return dateTime.toString(AMDA_TIME_FORMAT);
36 return dateTime.toString(AMDA_TIME_FORMAT);
36 }
37 }
37
38
38 } // namespace
39 } // namespace
39
40
40 AmdaProvider::AmdaProvider()
41 AmdaProvider::AmdaProvider()
41 {
42 {
42 qCDebug(LOG_AmdaProvider()) << tr("AmdaProvider::AmdaProvider") << QThread::currentThread();
43 qCDebug(LOG_AmdaProvider()) << tr("AmdaProvider::AmdaProvider") << QThread::currentThread();
43 if (auto app = sqpApp) {
44 if (auto app = sqpApp) {
44 auto &networkController = app->networkController();
45 auto &networkController = app->networkController();
45 connect(this, SIGNAL(requestConstructed(QNetworkRequest, QUuid,
46 connect(this, SIGNAL(requestConstructed(QNetworkRequest, QUuid,
46 std::function<void(QNetworkReply *, QUuid)>)),
47 std::function<void(QNetworkReply *, QUuid)>)),
47 &networkController,
48 &networkController,
48 SLOT(onProcessRequested(QNetworkRequest, QUuid,
49 SLOT(onProcessRequested(QNetworkRequest, QUuid,
49 std::function<void(QNetworkReply *, QUuid)>)));
50 std::function<void(QNetworkReply *, QUuid)>)));
50
51
51
52
52 connect(&sqpApp->networkController(), SIGNAL(replyDownloadProgress(QUuid, double)), this,
53 connect(&sqpApp->networkController(), SIGNAL(replyDownloadProgress(QUuid, double)), this,
53 SIGNAL(dataProvidedProgress(QUuid, double)));
54 SIGNAL(dataProvidedProgress(QUuid, double)));
54 }
55 }
55 }
56 }
56
57
57 void AmdaProvider::requestDataLoading(QUuid token, const DataProviderParameters &parameters)
58 void AmdaProvider::requestDataLoading(QUuid token, const DataProviderParameters &parameters)
58 {
59 {
59 // NOTE: Try to use multithread if possible
60 // NOTE: Try to use multithread if possible
60 const auto times = parameters.m_Times;
61 const auto times = parameters.m_Times;
61 const auto data = parameters.m_Data;
62 const auto data = parameters.m_Data;
62 for (const auto &dateTime : qAsConst(times)) {
63 for (const auto &dateTime : qAsConst(times)) {
63 retrieveData(token, dateTime, data);
64 retrieveData(token, dateTime, data);
64 }
65 }
65 }
66 }
66
67
67 void AmdaProvider::requestDataAborting(QUuid identifier)
68 void AmdaProvider::requestDataAborting(QUuid identifier)
68 {
69 {
69 if (auto app = sqpApp) {
70 if (auto app = sqpApp) {
70 auto &networkController = app->networkController();
71 auto &networkController = app->networkController();
71 networkController.onReplyCanceled(identifier);
72 networkController.onReplyCanceled(identifier);
72 }
73 }
73 }
74 }
74
75
75 void AmdaProvider::retrieveData(QUuid token, const SqpDateTime &dateTime, const QVariantHash &data)
76 void AmdaProvider::retrieveData(QUuid token, const SqpDateTime &dateTime, const QVariantHash &data)
76 {
77 {
77 // Retrieves product ID from data: if the value is invalid, no request is made
78 // Retrieves product ID from data: if the value is invalid, no request is made
78 auto productId = data.value(AMDA_XML_ID_KEY).toString();
79 auto productId = data.value(AMDA_XML_ID_KEY).toString();
79 if (productId.isNull()) {
80 if (productId.isNull()) {
80 qCCritical(LOG_AmdaProvider()) << tr("Can't retrieve data: unknown product id");
81 qCCritical(LOG_AmdaProvider()) << tr("Can't retrieve data: unknown product id");
81 return;
82 return;
82 }
83 }
83 qCInfo(LOG_AmdaProvider()) << tr("AmdaProvider::retrieveData") << dateTime;
84 qCInfo(LOG_AmdaProvider()) << tr("AmdaProvider::retrieveData") << dateTime;
84
85
85 // /////////// //
86 // /////////// //
86 // Creates URL //
87 // Creates URL //
87 // /////////// //
88 // /////////// //
88
89
89 auto startDate = dateFormat(dateTime.m_TStart);
90 auto startDate = dateFormat(dateTime.m_TStart);
90 auto endDate = dateFormat(dateTime.m_TEnd);
91 auto endDate = dateFormat(dateTime.m_TEnd);
91
92
92 auto url = QUrl{QString{AMDA_URL_FORMAT}.arg(startDate, endDate, productId)};
93 auto url = QUrl{QString{AMDA_URL_FORMAT}.arg(startDate, endDate, productId)};
93 qCInfo(LOG_AmdaProvider()) << tr("AmdaProvider::retrieveData url:") << url;
94 qCInfo(LOG_AmdaProvider()) << tr("AmdaProvider::retrieveData url:") << url;
94 auto tempFile = std::make_shared<QTemporaryFile>();
95 auto tempFile = std::make_shared<QTemporaryFile>();
95
96
96 // LAMBDA
97 // LAMBDA
97 auto httpDownloadFinished
98 auto httpDownloadFinished
98 = [this, dateTime, tempFile, token](QNetworkReply *reply, QUuid dataId) noexcept {
99 = [this, dateTime, tempFile, token](QNetworkReply *reply, QUuid dataId) noexcept {
99 Q_UNUSED(dataId);
100 Q_UNUSED(dataId);
100
101
101 // Don't do anything if the reply was abort
102 // Don't do anything if the reply was abort
102 if (reply->error() != QNetworkReply::OperationCanceledError) {
103 if (reply->error() != QNetworkReply::OperationCanceledError) {
103
104
104 if (tempFile) {
105 if (tempFile) {
105 auto replyReadAll = reply->readAll();
106 auto replyReadAll = reply->readAll();
106 if (!replyReadAll.isEmpty()) {
107 if (!replyReadAll.isEmpty()) {
107 tempFile->write(replyReadAll);
108 tempFile->write(replyReadAll);
108 }
109 }
109 tempFile->close();
110 tempFile->close();
110
111
111 // Parse results file
112 // Parse results file
112 if (auto dataSeries = AmdaResultParser::readTxt(tempFile->fileName())) {
113 if (auto dataSeries = AmdaResultParser::readTxt(tempFile->fileName())) {
113 emit dataProvided(token, dataSeries, dateTime);
114 emit dataProvided(token, dataSeries, dateTime);
114 }
115 }
115 else {
116 else {
116 /// @todo ALX : debug
117 /// @todo ALX : debug
117 }
118 }
118 }
119 }
119 }
120 }
120
121
121 };
122 };
122 auto httpFinishedLambda
123 auto httpFinishedLambda
123 = [this, httpDownloadFinished, tempFile](QNetworkReply *reply, QUuid dataId) noexcept {
124 = [this, httpDownloadFinished, tempFile](QNetworkReply *reply, QUuid dataId) noexcept {
124
125
125 // Don't do anything if the reply was abort
126 // Don't do anything if the reply was abort
126 if (reply->error() != QNetworkReply::OperationCanceledError) {
127 if (reply->error() != QNetworkReply::OperationCanceledError) {
127 auto downloadFileUrl = QUrl{QString{reply->readAll()}};
128 auto downloadFileUrl = QUrl{QString{reply->readAll()}};
128
129
129
130
130 qCInfo(LOG_AmdaProvider()) << tr("AmdaProvider::retrieveData downloadFileUrl:")
131 qCInfo(LOG_AmdaProvider()) << tr("AmdaProvider::retrieveData downloadFileUrl:")
131 << downloadFileUrl;
132 << downloadFileUrl;
132 // Executes request for downloading file //
133 // Executes request for downloading file //
133
134
134 // Creates destination file
135 // Creates destination file
135 if (tempFile->open()) {
136 if (tempFile->open()) {
136 // Executes request
137 // Executes request
137 emit requestConstructed(QNetworkRequest{downloadFileUrl}, dataId,
138 emit requestConstructed(QNetworkRequest{downloadFileUrl}, dataId,
138 httpDownloadFinished);
139 httpDownloadFinished);
139 }
140 }
140 }
141 }
141 };
142 };
142
143
143 // //////////////// //
144 // //////////////// //
144 // Executes request //
145 // Executes request //
145 // //////////////// //
146 // //////////////// //
146 emit requestConstructed(QNetworkRequest{url}, token, httpFinishedLambda);
147 emit requestConstructed(QNetworkRequest{url}, token, httpFinishedLambda);
147 }
148 }
@@ -1,145 +1,152
1 #include "AmdaResultParser.h"
1 #include "AmdaResultParser.h"
2
2
3 #include <Common/DateUtils.h>
3 #include <Data/ScalarSeries.h>
4 #include <Data/ScalarSeries.h>
4
5
5 #include <QDateTime>
6 #include <QDateTime>
6 #include <QFile>
7 #include <QFile>
7 #include <QRegularExpression>
8 #include <QRegularExpression>
8
9
9 #include <cmath>
10 #include <cmath>
10
11
11 Q_LOGGING_CATEGORY(LOG_AmdaResultParser, "AmdaResultParser")
12 Q_LOGGING_CATEGORY(LOG_AmdaResultParser, "AmdaResultParser")
12
13
13 namespace {
14 namespace {
14
15
15 /// Message in result file when the file was not found on server
16 /// Message in result file when the file was not found on server
16 const auto FILE_NOT_FOUND_MESSAGE = QStringLiteral("Not Found");
17 const auto FILE_NOT_FOUND_MESSAGE = QStringLiteral("Not Found");
17
18
18 /// Format for dates in result files
19 /// Format for dates in result files
19 const auto DATE_FORMAT = QStringLiteral("yyyy-MM-ddThh:mm:ss.zzz");
20 const auto DATE_FORMAT = QStringLiteral("yyyy-MM-ddThh:mm:ss.zzz");
20
21
21 /// Separator between values in a result line
22 /// Separator between values in a result line
22 const auto RESULT_LINE_SEPARATOR = QRegularExpression{QStringLiteral("\\s+")};
23 const auto RESULT_LINE_SEPARATOR = QRegularExpression{QStringLiteral("\\s+")};
23
24
24 /// Regex to find unit in a line. Examples of valid lines:
25 /// Regex to find unit in a line. Examples of valid lines:
25 /// ... - Units : nT - ...
26 /// ... - Units : nT - ...
26 /// ... -Units:nT- ...
27 /// ... -Units:nT- ...
27 /// ... -Units: mΒ²- ...
28 /// ... -Units: mΒ²- ...
28 /// ... - Units : m/s - ...
29 /// ... - Units : m/s - ...
29 const auto UNIT_REGEX = QRegularExpression{QStringLiteral("-\\s*Units\\s*:\\s*(.+?)\\s*-")};
30 const auto UNIT_REGEX = QRegularExpression{QStringLiteral("-\\s*Units\\s*:\\s*(.+?)\\s*-")};
30
31
31 /// Converts a string date to a double date
32 /// Converts a string date to a double date
32 /// @return a double that represents the date in seconds, NaN if the string date can't be converted
33 /// @return a double that represents the date in seconds, NaN if the string date can't be converted
33 double doubleDate(const QString &stringDate) noexcept
34 double doubleDate(const QString &stringDate) noexcept
34 {
35 {
35 auto dateTime = QDateTime::fromString(stringDate, DATE_FORMAT);
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 : std::numeric_limits<double>::quiet_NaN();
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 * Reads stream to retrieve x-axis unit
49 * Reads stream to retrieve x-axis unit
42 * @param stream the stream to read
50 * @param stream the stream to read
43 * @return the unit that has been read in the stream, a default unit (time unit with no label) if an
51 * @return the unit that has been read in the stream, a default unit (time unit with no label) if an
44 * error occured during reading
52 * error occured during reading
45 */
53 */
46 Unit readXAxisUnit(QTextStream &stream)
54 Unit readXAxisUnit(QTextStream &stream)
47 {
55 {
48 QString line{};
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 auto match = UNIT_REGEX.match(line);
60 auto match = UNIT_REGEX.match(line);
52 if (match.hasMatch()) {
61 if (match.hasMatch()) {
53 return Unit{match.captured(1), true};
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 // Error cases
68 // Error cases
65 return Unit{{}, true};
69 return Unit{{}, true};
66 }
70 }
67
71
68 /**
72 /**
69 * Reads stream to retrieve results
73 * Reads stream to retrieve results
70 * @param stream the stream to read
74 * @param stream the stream to read
71 * @return the pair of vectors x-axis data/values data that has been read in the stream
75 * @return the pair of vectors x-axis data/values data that has been read in the stream
72 */
76 */
73 QPair<QVector<double>, QVector<double> > readResults(QTextStream &stream)
77 QPair<QVector<double>, QVector<double> > readResults(QTextStream &stream)
74 {
78 {
75 auto xData = QVector<double>{};
79 auto xData = QVector<double>{};
76 auto valuesData = QVector<double>{};
80 auto valuesData = QVector<double>{};
77
81
78 QString line{};
82 QString line{};
83
79 while (stream.readLineInto(&line)) {
84 while (stream.readLineInto(&line)) {
80 auto lineData = line.split(RESULT_LINE_SEPARATOR, QString::SkipEmptyParts);
85 // Ignore comment lines
81 if (lineData.size() == 2) {
86 if (!isCommentLine(line)) {
82 // X : the data is converted from date to double (in secs)
87 auto lineData = line.split(RESULT_LINE_SEPARATOR, QString::SkipEmptyParts);
83 auto x = doubleDate(lineData.at(0));
88 if (lineData.size() == 2) {
84
89 // X : the data is converted from date to double (in secs)
85 // Value
90 auto x = doubleDate(lineData.at(0));
86 bool valueOk;
91
87 auto value = lineData.at(1).toDouble(&valueOk);
92 // Value
88
93 bool valueOk;
89 // Adds result only if x and value are valid
94 auto value = lineData.at(1).toDouble(&valueOk);
90 if (!std::isnan(x) && !std::isnan(value) && valueOk) {
95
91 xData.push_back(x);
96 // Adds result only if x and value are valid
92 valuesData.push_back(value);
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 else {
108 else {
95 qCWarning(LOG_AmdaResultParser())
109 qCWarning(LOG_AmdaResultParser())
96 << QObject::tr(
110 << QObject::tr("Can't retrieve results from line %1: invalid line").arg(line);
97 "Can't retrieve results from line %1: x and/or value are invalid")
98 .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 return qMakePair(std::move(xData), std::move(valuesData));
115 return qMakePair(std::move(xData), std::move(valuesData));
108 }
116 }
109
117
110 } // namespace
118 } // namespace
111
119
112 std::shared_ptr<IDataSeries> AmdaResultParser::readTxt(const QString &filePath) noexcept
120 std::shared_ptr<IDataSeries> AmdaResultParser::readTxt(const QString &filePath) noexcept
113 {
121 {
114 QFile file{filePath};
122 QFile file{filePath};
115
123
116 if (!file.open(QFile::ReadOnly | QIODevice::Text)) {
124 if (!file.open(QFile::ReadOnly | QIODevice::Text)) {
117 qCCritical(LOG_AmdaResultParser())
125 qCCritical(LOG_AmdaResultParser())
118 << QObject::tr("Can't retrieve AMDA data from file %1: %2")
126 << QObject::tr("Can't retrieve AMDA data from file %1: %2")
119 .arg(filePath, file.errorString());
127 .arg(filePath, file.errorString());
120 return nullptr;
128 return nullptr;
121 }
129 }
122
130
123 QTextStream stream{&file};
131 QTextStream stream{&file};
124
132
125 // Checks if the file was found on the server
133 // Checks if the file was found on the server
126 auto firstLine = stream.readLine();
134 auto firstLine = stream.readLine();
127 if (firstLine.compare(FILE_NOT_FOUND_MESSAGE) == 0) {
135 if (firstLine.compare(FILE_NOT_FOUND_MESSAGE) == 0) {
128 qCCritical(LOG_AmdaResultParser())
136 qCCritical(LOG_AmdaResultParser())
129 << QObject::tr("Can't retrieve AMDA data from file %1: file was not found on server")
137 << QObject::tr("Can't retrieve AMDA data from file %1: file was not found on server")
130 .arg(filePath);
138 .arg(filePath);
131 return nullptr;
139 return nullptr;
132 }
140 }
133
141
134 // Ignore comments lines
135 stream.readLine();
136
137 // Reads x-axis unit
142 // Reads x-axis unit
143 stream.seek(0); // returns to the beginning of the file
138 auto xAxisUnit = readXAxisUnit(stream);
144 auto xAxisUnit = readXAxisUnit(stream);
139
145
140 // Reads results
146 // Reads results
147 stream.seek(0); // returns to the beginning of the file
141 auto results = readResults(stream);
148 auto results = readResults(stream);
142
149
143 return std::make_shared<ScalarSeries>(std::move(results.first), std::move(results.second),
150 return std::make_shared<ScalarSeries>(std::move(results.first), std::move(results.second),
144 xAxisUnit, Unit{});
151 xAxisUnit, Unit{});
145 }
152 }
@@ -1,182 +1,182
1 #include "AmdaResultParser.h"
1 #include "AmdaResultParser.h"
2
2
3 #include <Data/ScalarSeries.h>
3 #include <Data/ScalarSeries.h>
4
4
5 #include <QObject>
5 #include <QObject>
6 #include <QtTest>
6 #include <QtTest>
7
7
8 namespace {
8 namespace {
9
9
10 /// Path for the tests
10 /// Path for the tests
11 const auto TESTS_RESOURCES_PATH
11 const auto TESTS_RESOURCES_PATH
12 = QFileInfo{QString{AMDA_TESTS_RESOURCES_DIR}, "TestAmdaResultParser"}.absoluteFilePath();
12 = QFileInfo{QString{AMDA_TESTS_RESOURCES_DIR}, "TestAmdaResultParser"}.absoluteFilePath();
13
13
14 QString inputFilePath(const QString &inputFileName)
14 QString inputFilePath(const QString &inputFileName)
15 {
15 {
16 return QFileInfo{TESTS_RESOURCES_PATH, inputFileName}.absoluteFilePath();
16 return QFileInfo{TESTS_RESOURCES_PATH, inputFileName}.absoluteFilePath();
17 }
17 }
18
18
19 struct ExpectedResults {
19 struct ExpectedResults {
20 explicit ExpectedResults() = default;
20 explicit ExpectedResults() = default;
21
21
22 /// Ctor with QVector<QDateTime> as x-axis data. Datetimes are converted to doubles
22 /// Ctor with QVector<QDateTime> as x-axis data. Datetimes are converted to doubles
23 explicit ExpectedResults(Unit xAxisUnit, Unit valuesUnit, const QVector<QDateTime> &xAxisData,
23 explicit ExpectedResults(Unit xAxisUnit, Unit valuesUnit, const QVector<QDateTime> &xAxisData,
24 QVector<double> valuesData)
24 QVector<double> valuesData)
25 : m_ParsingOK{true},
25 : m_ParsingOK{true},
26 m_XAxisUnit{xAxisUnit},
26 m_XAxisUnit{xAxisUnit},
27 m_ValuesUnit{valuesUnit},
27 m_ValuesUnit{valuesUnit},
28 m_XAxisData{},
28 m_XAxisData{},
29 m_ValuesData{std::move(valuesData)}
29 m_ValuesData{std::move(valuesData)}
30 {
30 {
31 // Converts QVector<QDateTime> to QVector<double>
31 // Converts QVector<QDateTime> to QVector<double>
32 std::transform(xAxisData.cbegin(), xAxisData.cend(), std::back_inserter(m_XAxisData),
32 std::transform(xAxisData.cbegin(), xAxisData.cend(), std::back_inserter(m_XAxisData),
33 [](const auto &dateTime) { return dateTime.toMSecsSinceEpoch() / 1000.; });
33 [](const auto &dateTime) { return dateTime.toMSecsSinceEpoch() / 1000.; });
34 }
34 }
35
35
36 /**
36 /**
37 * Validates a DataSeries compared to the expected results
37 * Validates a DataSeries compared to the expected results
38 * @param results the DataSeries to validate
38 * @param results the DataSeries to validate
39 */
39 */
40 void validate(std::shared_ptr<IDataSeries> results)
40 void validate(std::shared_ptr<IDataSeries> results)
41 {
41 {
42 if (m_ParsingOK) {
42 if (m_ParsingOK) {
43 auto scalarSeries = dynamic_cast<ScalarSeries *>(results.get());
43 auto scalarSeries = dynamic_cast<ScalarSeries *>(results.get());
44 QVERIFY(scalarSeries != nullptr);
44 QVERIFY(scalarSeries != nullptr);
45
45
46 // Checks units
46 // Checks units
47 QVERIFY(scalarSeries->xAxisUnit() == m_XAxisUnit);
47 QVERIFY(scalarSeries->xAxisUnit() == m_XAxisUnit);
48 QVERIFY(scalarSeries->valuesUnit() == m_ValuesUnit);
48 QVERIFY(scalarSeries->valuesUnit() == m_ValuesUnit);
49
49
50 // Checks values
50 // Checks values
51 QVERIFY(scalarSeries->xAxisData()->data() == m_XAxisData);
51 QVERIFY(scalarSeries->xAxisData()->data() == m_XAxisData);
52 QVERIFY(scalarSeries->valuesData()->data() == m_ValuesData);
52 QVERIFY(scalarSeries->valuesData()->data() == m_ValuesData);
53 }
53 }
54 else {
54 else {
55 QVERIFY(results == nullptr);
55 QVERIFY(results == nullptr);
56 }
56 }
57 }
57 }
58
58
59 // Parsing was successfully completed
59 // Parsing was successfully completed
60 bool m_ParsingOK{false};
60 bool m_ParsingOK{false};
61 // Expected x-axis unit
61 // Expected x-axis unit
62 Unit m_XAxisUnit{};
62 Unit m_XAxisUnit{};
63 // Expected values unit
63 // Expected values unit
64 Unit m_ValuesUnit{};
64 Unit m_ValuesUnit{};
65 // Expected x-axis data
65 // Expected x-axis data
66 QVector<double> m_XAxisData{};
66 QVector<double> m_XAxisData{};
67 // Expected values data
67 // Expected values data
68 QVector<double> m_ValuesData{};
68 QVector<double> m_ValuesData{};
69 };
69 };
70
70
71 } // namespace
71 } // namespace
72
72
73 Q_DECLARE_METATYPE(ExpectedResults)
73 Q_DECLARE_METATYPE(ExpectedResults)
74
74
75 class TestAmdaResultParser : public QObject {
75 class TestAmdaResultParser : public QObject {
76 Q_OBJECT
76 Q_OBJECT
77 private slots:
77 private slots:
78 /// Input test data
78 /// Input test data
79 /// @sa testTxtJson()
79 /// @sa testTxtJson()
80 void testReadTxt_data();
80 void testReadTxt_data();
81
81
82 /// Tests parsing of a TXT file
82 /// Tests parsing of a TXT file
83 void testReadTxt();
83 void testReadTxt();
84 };
84 };
85
85
86 void TestAmdaResultParser::testReadTxt_data()
86 void TestAmdaResultParser::testReadTxt_data()
87 {
87 {
88 // ////////////// //
88 // ////////////// //
89 // Test structure //
89 // Test structure //
90 // ////////////// //
90 // ////////////// //
91
91
92 // Name of TXT file to read
92 // Name of TXT file to read
93 QTest::addColumn<QString>("inputFileName");
93 QTest::addColumn<QString>("inputFileName");
94 // Expected results
94 // Expected results
95 QTest::addColumn<ExpectedResults>("expectedResults");
95 QTest::addColumn<ExpectedResults>("expectedResults");
96
96
97 // ////////// //
97 // ////////// //
98 // Test cases //
98 // Test cases //
99 // ////////// //
99 // ////////// //
100
100
101 auto dateTime = [](int year, int month, int day, int hours, int minutes, int seconds) {
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 // Valid file
105 // Valid file
106 QTest::newRow("Valid file")
106 QTest::newRow("Valid file")
107 << QStringLiteral("ValidScalar1.txt")
107 << QStringLiteral("ValidScalar1.txt")
108 << ExpectedResults{
108 << ExpectedResults{
109 Unit{QStringLiteral("nT"), true}, Unit{},
109 Unit{QStringLiteral("nT"), true}, Unit{},
110 QVector<QDateTime>{dateTime(2013, 9, 23, 9, 0, 30), dateTime(2013, 9, 23, 9, 1, 30),
110 QVector<QDateTime>{dateTime(2013, 9, 23, 9, 0, 30), dateTime(2013, 9, 23, 9, 1, 30),
111 dateTime(2013, 9, 23, 9, 2, 30), dateTime(2013, 9, 23, 9, 3, 30),
111 dateTime(2013, 9, 23, 9, 2, 30), dateTime(2013, 9, 23, 9, 3, 30),
112 dateTime(2013, 9, 23, 9, 4, 30), dateTime(2013, 9, 23, 9, 5, 30),
112 dateTime(2013, 9, 23, 9, 4, 30), dateTime(2013, 9, 23, 9, 5, 30),
113 dateTime(2013, 9, 23, 9, 6, 30), dateTime(2013, 9, 23, 9, 7, 30),
113 dateTime(2013, 9, 23, 9, 6, 30), dateTime(2013, 9, 23, 9, 7, 30),
114 dateTime(2013, 9, 23, 9, 8, 30), dateTime(2013, 9, 23, 9, 9, 30)},
114 dateTime(2013, 9, 23, 9, 8, 30), dateTime(2013, 9, 23, 9, 9, 30)},
115 QVector<double>{-2.83950, -2.71850, -2.52150, -2.57633, -2.58050, -2.48325, -2.63025,
115 QVector<double>{-2.83950, -2.71850, -2.52150, -2.57633, -2.58050, -2.48325, -2.63025,
116 -2.55800, -2.43250, -2.42200}};
116 -2.55800, -2.43250, -2.42200}};
117
117
118 // Valid files but with some invalid lines (wrong unit, wrong values, etc.)
118 // Valid files but with some invalid lines (wrong unit, wrong values, etc.)
119 QTest::newRow("No unit file") << QStringLiteral("NoUnit.txt")
119 QTest::newRow("No unit file") << QStringLiteral("NoUnit.txt")
120 << ExpectedResults{Unit{QStringLiteral(""), true}, Unit{},
120 << ExpectedResults{Unit{QStringLiteral(""), true}, Unit{},
121 QVector<QDateTime>{}, QVector<double>{}};
121 QVector<QDateTime>{}, QVector<double>{}};
122 QTest::newRow("Wrong unit file")
122 QTest::newRow("Wrong unit file")
123 << QStringLiteral("WrongUnit.txt")
123 << QStringLiteral("WrongUnit.txt")
124 << ExpectedResults{Unit{QStringLiteral(""), true}, Unit{},
124 << ExpectedResults{Unit{QStringLiteral(""), true}, Unit{},
125 QVector<QDateTime>{dateTime(2013, 9, 23, 9, 0, 30),
125 QVector<QDateTime>{dateTime(2013, 9, 23, 9, 0, 30),
126 dateTime(2013, 9, 23, 9, 1, 30),
126 dateTime(2013, 9, 23, 9, 1, 30),
127 dateTime(2013, 9, 23, 9, 2, 30)},
127 dateTime(2013, 9, 23, 9, 2, 30)},
128 QVector<double>{-2.83950, -2.71850, -2.52150}};
128 QVector<double>{-2.83950, -2.71850, -2.52150}};
129
129
130 QTest::newRow("Wrong results file (date of first line is invalid")
130 QTest::newRow("Wrong results file (date of first line is invalid")
131 << QStringLiteral("WrongDate.txt")
131 << QStringLiteral("WrongDate.txt")
132 << ExpectedResults{
132 << ExpectedResults{
133 Unit{QStringLiteral("nT"), true}, Unit{},
133 Unit{QStringLiteral("nT"), true}, Unit{},
134 QVector<QDateTime>{dateTime(2013, 9, 23, 9, 1, 30), dateTime(2013, 9, 23, 9, 2, 30)},
134 QVector<QDateTime>{dateTime(2013, 9, 23, 9, 1, 30), dateTime(2013, 9, 23, 9, 2, 30)},
135 QVector<double>{-2.71850, -2.52150}};
135 QVector<double>{-2.71850, -2.52150}};
136
136
137 QTest::newRow("Wrong results file (too many values for first line")
137 QTest::newRow("Wrong results file (too many values for first line")
138 << QStringLiteral("TooManyValues.txt")
138 << QStringLiteral("TooManyValues.txt")
139 << ExpectedResults{
139 << ExpectedResults{
140 Unit{QStringLiteral("nT"), true}, Unit{},
140 Unit{QStringLiteral("nT"), true}, Unit{},
141 QVector<QDateTime>{dateTime(2013, 9, 23, 9, 1, 30), dateTime(2013, 9, 23, 9, 2, 30)},
141 QVector<QDateTime>{dateTime(2013, 9, 23, 9, 1, 30), dateTime(2013, 9, 23, 9, 2, 30)},
142 QVector<double>{-2.71850, -2.52150}};
142 QVector<double>{-2.71850, -2.52150}};
143
143
144 QTest::newRow("Wrong results file (value of first line is invalid")
144 QTest::newRow("Wrong results file (value of first line is invalid")
145 << QStringLiteral("WrongValue.txt")
145 << QStringLiteral("WrongValue.txt")
146 << ExpectedResults{
146 << ExpectedResults{
147 Unit{QStringLiteral("nT"), true}, Unit{},
147 Unit{QStringLiteral("nT"), true}, Unit{},
148 QVector<QDateTime>{dateTime(2013, 9, 23, 9, 1, 30), dateTime(2013, 9, 23, 9, 2, 30)},
148 QVector<QDateTime>{dateTime(2013, 9, 23, 9, 1, 30), dateTime(2013, 9, 23, 9, 2, 30)},
149 QVector<double>{-2.71850, -2.52150}};
149 QVector<double>{-2.71850, -2.52150}};
150
150
151 QTest::newRow("Wrong results file (value of first line is NaN")
151 QTest::newRow("Wrong results file (value of first line is NaN")
152 << QStringLiteral("NaNValue.txt")
152 << QStringLiteral("NaNValue.txt")
153 << ExpectedResults{
153 << ExpectedResults{
154 Unit{QStringLiteral("nT"), true}, Unit{},
154 Unit{QStringLiteral("nT"), true}, Unit{},
155 QVector<QDateTime>{dateTime(2013, 9, 23, 9, 1, 30), dateTime(2013, 9, 23, 9, 2, 30)},
155 QVector<QDateTime>{dateTime(2013, 9, 23, 9, 1, 30), dateTime(2013, 9, 23, 9, 2, 30)},
156 QVector<double>{-2.71850, -2.52150}};
156 QVector<double>{-2.71850, -2.52150}};
157
157
158 // Invalid files
158 // Invalid files
159 QTest::newRow("Invalid file (unexisting file)") << QStringLiteral("UnexistingFile.txt")
159 QTest::newRow("Invalid file (unexisting file)") << QStringLiteral("UnexistingFile.txt")
160 << ExpectedResults{};
160 << ExpectedResults{};
161
161
162 QTest::newRow("Invalid file (file not found on server)") << QStringLiteral("FileNotFound.txt")
162 QTest::newRow("Invalid file (file not found on server)") << QStringLiteral("FileNotFound.txt")
163 << ExpectedResults{};
163 << ExpectedResults{};
164 }
164 }
165
165
166 void TestAmdaResultParser::testReadTxt()
166 void TestAmdaResultParser::testReadTxt()
167 {
167 {
168 QFETCH(QString, inputFileName);
168 QFETCH(QString, inputFileName);
169 QFETCH(ExpectedResults, expectedResults);
169 QFETCH(ExpectedResults, expectedResults);
170
170
171 // Parses file
171 // Parses file
172 auto filePath = inputFilePath(inputFileName);
172 auto filePath = inputFilePath(inputFileName);
173 auto results = AmdaResultParser::readTxt(filePath);
173 auto results = AmdaResultParser::readTxt(filePath);
174
174
175 // ///////////////// //
175 // ///////////////// //
176 // Validates results //
176 // Validates results //
177 // ///////////////// //
177 // ///////////////// //
178 expectedResults.validate(results);
178 expectedResults.validate(results);
179 }
179 }
180
180
181 QTEST_MAIN(TestAmdaResultParser)
181 QTEST_MAIN(TestAmdaResultParser)
182 #include "TestAmdaResultParser.moc"
182 #include "TestAmdaResultParser.moc"
@@ -1,101 +1,100
1 #include "CosinusProvider.h"
1 #include "CosinusProvider.h"
2
2
3 #include <Data/DataProviderParameters.h>
3 #include <Data/DataProviderParameters.h>
4 #include <Data/ScalarSeries.h>
4 #include <Data/ScalarSeries.h>
5
5
6 #include <cmath>
6 #include <cmath>
7
7
8 #include <QDateTime>
9 #include <QFuture>
8 #include <QFuture>
10 #include <QThread>
9 #include <QThread>
11 #include <QtConcurrent/QtConcurrent>
10 #include <QtConcurrent/QtConcurrent>
12
11
13 Q_LOGGING_CATEGORY(LOG_CosinusProvider, "CosinusProvider")
12 Q_LOGGING_CATEGORY(LOG_CosinusProvider, "CosinusProvider")
14
13
15 std::shared_ptr<IDataSeries> CosinusProvider::retrieveData(QUuid token, const SqpDateTime &dateTime)
14 std::shared_ptr<IDataSeries> CosinusProvider::retrieveData(QUuid token, const SqpDateTime &dateTime)
16 {
15 {
17 // TODO: Add Mutex
16 // TODO: Add Mutex
18 auto dataIndex = 0;
17 auto dataIndex = 0;
19
18
20 // Gets the timerange from the parameters
19 // Gets the timerange from the parameters
21 double freq = 100.0;
20 double freq = 100.0;
22 double start = std::ceil(dateTime.m_TStart * freq); // 100 htz
21 double start = std::ceil(dateTime.m_TStart * freq); // 100 htz
23 double end = std::floor(dateTime.m_TEnd * freq); // 100 htz
22 double end = std::floor(dateTime.m_TEnd * freq); // 100 htz
24
23
25 // We assure that timerange is valid
24 // We assure that timerange is valid
26 if (end < start) {
25 if (end < start) {
27 std::swap(start, end);
26 std::swap(start, end);
28 }
27 }
29
28
30 // Generates scalar series containing cosinus values (one value per second)
29 // Generates scalar series containing cosinus values (one value per second)
31 auto dataCount = end - start;
30 auto dataCount = end - start;
32
31
33 auto xAxisData = QVector<double>{};
32 auto xAxisData = QVector<double>{};
34 xAxisData.resize(dataCount);
33 xAxisData.resize(dataCount);
35
34
36 auto valuesData = QVector<double>{};
35 auto valuesData = QVector<double>{};
37 valuesData.resize(dataCount);
36 valuesData.resize(dataCount);
38
37
39 int progress = 0;
38 int progress = 0;
40 auto progressEnd = dataCount;
39 auto progressEnd = dataCount;
41 for (auto time = start; time < end; ++time, ++dataIndex) {
40 for (auto time = start; time < end; ++time, ++dataIndex) {
42 auto it = m_VariableToEnableProvider.find(token);
41 auto it = m_VariableToEnableProvider.find(token);
43 if (it != m_VariableToEnableProvider.end() && it.value()) {
42 if (it != m_VariableToEnableProvider.end() && it.value()) {
44 const auto timeOnFreq = time / freq;
43 const auto timeOnFreq = time / freq;
45
44
46 xAxisData.replace(dataIndex, timeOnFreq);
45 xAxisData.replace(dataIndex, timeOnFreq);
47 valuesData.replace(dataIndex, std::cos(timeOnFreq));
46 valuesData.replace(dataIndex, std::cos(timeOnFreq));
48
47
49 // progression
48 // progression
50 int currentProgress = (time - start) * 100.0 / progressEnd;
49 int currentProgress = (time - start) * 100.0 / progressEnd;
51 if (currentProgress != progress) {
50 if (currentProgress != progress) {
52 progress = currentProgress;
51 progress = currentProgress;
53
52
54 emit dataProvidedProgress(token, progress);
53 emit dataProvidedProgress(token, progress);
55 }
54 }
56 }
55 }
57 else {
56 else {
58 if (!it.value()) {
57 if (!it.value()) {
59 qCDebug(LOG_CosinusProvider())
58 qCDebug(LOG_CosinusProvider())
60 << "CosinusProvider::retrieveData: ARRET De l'acquisition detectΓ©"
59 << "CosinusProvider::retrieveData: ARRET De l'acquisition detectΓ©"
61 << end - time;
60 << end - time;
62 }
61 }
63 }
62 }
64 }
63 }
65 emit dataProvidedProgress(token, 0.0);
64 emit dataProvidedProgress(token, 0.0);
66
65
67 return std::make_shared<ScalarSeries>(std::move(xAxisData), std::move(valuesData),
66 return std::make_shared<ScalarSeries>(std::move(xAxisData), std::move(valuesData),
68 Unit{QStringLiteral("t"), true}, Unit{});
67 Unit{QStringLiteral("t"), true}, Unit{});
69 }
68 }
70
69
71 void CosinusProvider::requestDataLoading(QUuid token, const DataProviderParameters &parameters)
70 void CosinusProvider::requestDataLoading(QUuid token, const DataProviderParameters &parameters)
72 {
71 {
73 // TODO: Add Mutex
72 // TODO: Add Mutex
74 m_VariableToEnableProvider[token] = true;
73 m_VariableToEnableProvider[token] = true;
75 qCDebug(LOG_CosinusProvider()) << "CosinusProvider::requestDataLoading"
74 qCDebug(LOG_CosinusProvider()) << "CosinusProvider::requestDataLoading"
76 << QThread::currentThread()->objectName();
75 << QThread::currentThread()->objectName();
77 // NOTE: Try to use multithread if possible
76 // NOTE: Try to use multithread if possible
78 const auto times = parameters.m_Times;
77 const auto times = parameters.m_Times;
79
78
80 for (const auto &dateTime : qAsConst(times)) {
79 for (const auto &dateTime : qAsConst(times)) {
81 if (m_VariableToEnableProvider[token]) {
80 if (m_VariableToEnableProvider[token]) {
82 auto scalarSeries = this->retrieveData(token, dateTime);
81 auto scalarSeries = this->retrieveData(token, dateTime);
83 emit dataProvided(token, scalarSeries, dateTime);
82 emit dataProvided(token, scalarSeries, dateTime);
84 }
83 }
85 }
84 }
86 }
85 }
87
86
88 void CosinusProvider::requestDataAborting(QUuid identifier)
87 void CosinusProvider::requestDataAborting(QUuid identifier)
89 {
88 {
90 // TODO: Add Mutex
89 // TODO: Add Mutex
91 qCDebug(LOG_CosinusProvider()) << "CosinusProvider::requestDataAborting" << identifier
90 qCDebug(LOG_CosinusProvider()) << "CosinusProvider::requestDataAborting" << identifier
92 << QThread::currentThread()->objectName();
91 << QThread::currentThread()->objectName();
93 auto it = m_VariableToEnableProvider.find(identifier);
92 auto it = m_VariableToEnableProvider.find(identifier);
94 if (it != m_VariableToEnableProvider.end()) {
93 if (it != m_VariableToEnableProvider.end()) {
95 it.value() = false;
94 it.value() = false;
96 }
95 }
97 else {
96 else {
98 qCWarning(LOG_CosinusProvider())
97 qCWarning(LOG_CosinusProvider())
99 << tr("Aborting progression of inexistant identifier detected !!!");
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