##// END OF EJS Templates
Merge branch 'feature/DisplayDataBeforeAcquisition' into develop
perrinel -
r574:23422057e407 merge
parent child
Show More
@@ -1,65 +1,66
1 1 #ifndef SCIQLOP_VARIABLE_H
2 2 #define SCIQLOP_VARIABLE_H
3 3
4 4 #include "CoreGlobal.h"
5 5
6 6 #include <Data/SqpRange.h>
7 7
8 8 #include <QLoggingCategory>
9 9 #include <QObject>
10 10
11 11 #include <Common/MetaTypes.h>
12 12 #include <Common/spimpl.h>
13 13
14 14 Q_DECLARE_LOGGING_CATEGORY(LOG_Variable)
15 15
16 16 class IDataSeries;
17 17 class QString;
18 18
19 19 /**
20 20 * @brief The Variable class represents a variable in SciQlop.
21 21 */
22 22 class SCIQLOP_CORE_EXPORT Variable : public QObject {
23 23
24 24 Q_OBJECT
25 25
26 26 public:
27 27 explicit Variable(const QString &name, const SqpRange &dateTime,
28 28 const QVariantHash &metadata = {});
29 29
30 30 QString name() const noexcept;
31 31 SqpRange range() const noexcept;
32 32 void setRange(const SqpRange &range) noexcept;
33 33 SqpRange cacheRange() const noexcept;
34 34 void setCacheRange(const SqpRange &cacheRange) noexcept;
35 35
36 36 /// @return the data of the variable, nullptr if there is no data
37 37 std::shared_ptr<IDataSeries> dataSeries() const noexcept;
38 38
39 39 QVariantHash metadata() const noexcept;
40 40
41 41 bool contains(const SqpRange &range) const noexcept;
42 42 bool intersect(const SqpRange &range) const noexcept;
43 43 bool isInside(const SqpRange &range) const noexcept;
44 44
45 45 bool cacheContains(const SqpRange &range) const noexcept;
46 46 bool cacheIntersect(const SqpRange &range) const noexcept;
47 47 bool cacheIsInside(const SqpRange &range) const noexcept;
48 48
49 49 QVector<SqpRange> provideNotInCacheRangeList(const SqpRange &range) const noexcept;
50 QVector<SqpRange> provideInCacheRangeList(const SqpRange &range) const noexcept;
50 51 void setDataSeries(std::shared_ptr<IDataSeries> dataSeries) noexcept;
51 52 void mergeDataSeries(std::shared_ptr<IDataSeries> dataSeries) noexcept;
52 53
53 54 signals:
54 55 void updated();
55 56
56 57 private:
57 58 class VariablePrivate;
58 59 spimpl::unique_impl_ptr<VariablePrivate> impl;
59 60 };
60 61
61 62 // Required for using shared_ptr in signals/slots
62 63 SCIQLOP_REGISTER_META_TYPE(VARIABLE_PTR_REGISTRY, std::shared_ptr<Variable>)
63 64 SCIQLOP_REGISTER_META_TYPE(VARIABLE_PTR_VECTOR_REGISTRY, QVector<std::shared_ptr<Variable> >)
64 65
65 66 #endif // SCIQLOP_VARIABLE_H
@@ -1,117 +1,120
1 1 #ifndef SCIQLOP_VARIABLECONTROLLER_H
2 2 #define SCIQLOP_VARIABLECONTROLLER_H
3 3
4 4 #include "CoreGlobal.h"
5 5
6 6 #include <Data/AcquisitionDataPacket.h>
7 7 #include <Data/SqpRange.h>
8 8
9 9 #include <QLoggingCategory>
10 10 #include <QObject>
11 11
12 12 #include <Common/spimpl.h>
13 13
14 14 class IDataProvider;
15 15 class QItemSelectionModel;
16 16 class TimeController;
17 17 class Variable;
18 18 class VariableModel;
19 19
20 20 Q_DECLARE_LOGGING_CATEGORY(LOG_VariableController)
21 21
22 22
23 23 /**
24 24 * Possible types of zoom operation
25 25 */
26 26 enum class AcquisitionZoomType { ZoomOut, ZoomIn, PanRight, PanLeft, Unknown };
27 27
28 28
29 29 /**
30 30 * @brief The VariableController class aims to handle the variables in SciQlop.
31 31 */
32 32 class SCIQLOP_CORE_EXPORT VariableController : public QObject {
33 33 Q_OBJECT
34 34 public:
35 35 explicit VariableController(QObject *parent = 0);
36 36 virtual ~VariableController();
37 37
38 38 VariableModel *variableModel() noexcept;
39 39 QItemSelectionModel *variableSelectionModel() noexcept;
40 40
41 41 void setTimeController(TimeController *timeController) noexcept;
42 42
43 43 /**
44 44 * Deletes from the controller the variable passed in parameter.
45 45 *
46 46 * Delete a variable includes:
47 47 * - the deletion of the various references to the variable in SciQlop
48 48 * - the deletion of the model variable
49 49 * - the deletion of the provider associated with the variable
50 50 * - removing the cache associated with the variable
51 51 *
52 52 * @param variable the variable to delete from the controller.
53 53 */
54 54 void deleteVariable(std::shared_ptr<Variable> variable) noexcept;
55 55
56 56 /**
57 57 * Deletes from the controller the variables passed in parameter.
58 58 * @param variables the variables to delete from the controller.
59 59 * @sa deleteVariable()
60 60 */
61 61 void deleteVariables(const QVector<std::shared_ptr<Variable> > &variables) noexcept;
62 62
63 63 /**
64 64 * @brief abort the variable retrieve data progression
65 65 */
66 66 void abortProgress(std::shared_ptr<Variable> variable);
67 67
68 68 static AcquisitionZoomType getZoomType(const SqpRange &range, const SqpRange &oldRange);
69 69 signals:
70 70 /// Signal emitted when a variable is about to be deleted from the controller
71 71 void variableAboutToBeDeleted(std::shared_ptr<Variable> variable);
72 72
73 73 /// Signal emitted when a data acquisition is requested on a range for a variable
74 74 void rangeChanged(std::shared_ptr<Variable> variable, const SqpRange &range);
75 75
76 /// Signal emitted when a sub range of the cacheRange of the variable can be displayed
77 void updateVarDisplaying(std::shared_ptr<Variable> variable, const SqpRange &range);
78
76 79 public slots:
77 80 /// Request the data loading of the variable whithin range
78 81 void onRequestDataLoading(QVector<std::shared_ptr<Variable> > variables, const SqpRange &range,
79 82 const SqpRange &oldRange, bool synchronise);
80 83 /**
81 84 * Creates a new variable and adds it to the model
82 85 * @param name the name of the new variable
83 86 * @param metadata the metadata of the new variable
84 87 * @param provider the data provider for the new variable
85 88 */
86 89 void createVariable(const QString &name, const QVariantHash &metadata,
87 90 std::shared_ptr<IDataProvider> provider) noexcept;
88 91
89 92 /// Update the temporal parameters of every selected variable to dateTime
90 93 void onDateTimeOnSelection(const SqpRange &dateTime);
91 94
92 95
93 96 void onDataProvided(QUuid vIdentifier, const SqpRange &rangeRequested,
94 97 const SqpRange &cacheRangeRequested,
95 98 QVector<AcquisitionDataPacket> dataAcquired);
96 99
97 100 void onVariableRetrieveDataInProgress(QUuid identifier, double progress);
98 101
99 102 /// Cancel the current request for the variable
100 103 void onAbortProgressRequested(std::shared_ptr<Variable> variable);
101 104
102 105 /// synchronization group methods
103 106 void onAddSynchronizationGroupId(QUuid synchronizationGroupId);
104 107 void onRemoveSynchronizationGroupId(QUuid synchronizationGroupId);
105 108 void onAddSynchronized(std::shared_ptr<Variable> variable, QUuid synchronizationGroupId);
106 109
107 110 void initialize();
108 111 void finalize();
109 112
110 113 private:
111 114 void waitForFinish();
112 115
113 116 class VariableControllerPrivate;
114 117 spimpl::unique_impl_ptr<VariableControllerPrivate> impl;
115 118 };
116 119
117 120 #endif // SCIQLOP_VARIABLECONTROLLER_H
@@ -1,211 +1,243
1 1 #include "Variable/Variable.h"
2 2
3 3 #include <Data/IDataSeries.h>
4 4 #include <Data/SqpRange.h>
5 5
6 6 #include <QMutex>
7 7 #include <QReadWriteLock>
8 8 #include <QThread>
9 9
10 10 Q_LOGGING_CATEGORY(LOG_Variable, "Variable")
11 11
12 12 struct Variable::VariablePrivate {
13 13 explicit VariablePrivate(const QString &name, const SqpRange &dateTime,
14 14 const QVariantHash &metadata)
15 15 : m_Name{name}, m_Range{dateTime}, m_Metadata{metadata}, m_DataSeries{nullptr}
16 16 {
17 17 }
18 18
19 19 void lockRead() { m_Lock.lockForRead(); }
20 20 void lockWrite() { m_Lock.lockForWrite(); }
21 21 void unlock() { m_Lock.unlock(); }
22 22
23 23 QString m_Name;
24 24
25 25 SqpRange m_Range;
26 26 SqpRange m_CacheRange;
27 27 QVariantHash m_Metadata;
28 28 std::shared_ptr<IDataSeries> m_DataSeries;
29 29
30 30 QReadWriteLock m_Lock;
31 31 };
32 32
33 33 Variable::Variable(const QString &name, const SqpRange &dateTime, const QVariantHash &metadata)
34 34 : impl{spimpl::make_unique_impl<VariablePrivate>(name, dateTime, metadata)}
35 35 {
36 36 }
37 37
38 38 QString Variable::name() const noexcept
39 39 {
40 40 impl->lockRead();
41 41 auto name = impl->m_Name;
42 42 impl->unlock();
43 43 return name;
44 44 }
45 45
46 46 SqpRange Variable::range() const noexcept
47 47 {
48 48 impl->lockRead();
49 49 auto range = impl->m_Range;
50 50 impl->unlock();
51 51 return range;
52 52 }
53 53
54 54 void Variable::setRange(const SqpRange &range) noexcept
55 55 {
56 56 impl->lockWrite();
57 57 impl->m_Range = range;
58 58 impl->unlock();
59 59 }
60 60
61 61 SqpRange Variable::cacheRange() const noexcept
62 62 {
63 63 impl->lockRead();
64 64 auto cacheRange = impl->m_CacheRange;
65 65 impl->unlock();
66 66 return cacheRange;
67 67 }
68 68
69 69 void Variable::setCacheRange(const SqpRange &cacheRange) noexcept
70 70 {
71 71 impl->lockWrite();
72 72 impl->m_CacheRange = cacheRange;
73 73 impl->unlock();
74 74 }
75 75
76 76 void Variable::setDataSeries(std::shared_ptr<IDataSeries> dataSeries) noexcept
77 77 {
78 78 qCDebug(LOG_Variable()) << "TORM Variable::setDataSeries"
79 79 << QThread::currentThread()->objectName();
80 80 if (!dataSeries) {
81 81 /// @todo ALX : log
82 82 return;
83 83 }
84 84 impl->lockWrite();
85 85 impl->m_DataSeries = dataSeries->clone();
86 86 impl->unlock();
87 87 }
88 88
89 89 void Variable::mergeDataSeries(std::shared_ptr<IDataSeries> dataSeries) noexcept
90 90 {
91 91 qCDebug(LOG_Variable()) << "TORM Variable::mergeDataSeries"
92 92 << QThread::currentThread()->objectName();
93 93 if (!dataSeries) {
94 94 /// @todo ALX : log
95 95 return;
96 96 }
97 97
98 98 // Add or merge the data
99 99 // Inits the data series of the variable
100 100 impl->lockWrite();
101 101 if (!impl->m_DataSeries) {
102 102 impl->m_DataSeries = dataSeries->clone();
103 103 }
104 104 else {
105 105 impl->m_DataSeries->merge(dataSeries.get());
106 106 }
107 107 impl->unlock();
108 108
109 109 // sub the data
110 110 auto subData = this->dataSeries()->subDataSeries(this->cacheRange());
111 111 qCDebug(LOG_Variable()) << "TORM: Variable::mergeDataSeries sub" << subData->range();
112 112 this->setDataSeries(subData);
113 113 qCDebug(LOG_Variable()) << "TORM: Variable::mergeDataSeries set" << this->dataSeries()->range();
114 114 }
115 115
116 116 std::shared_ptr<IDataSeries> Variable::dataSeries() const noexcept
117 117 {
118 118 impl->lockRead();
119 119 auto dataSeries = impl->m_DataSeries;
120 120 impl->unlock();
121 121
122 122 return dataSeries;
123 123 }
124 124
125 125 QVariantHash Variable::metadata() const noexcept
126 126 {
127 127 impl->lockRead();
128 128 auto metadata = impl->m_Metadata;
129 129 impl->unlock();
130 130 return metadata;
131 131 }
132 132
133 133 bool Variable::contains(const SqpRange &range) const noexcept
134 134 {
135 135 impl->lockRead();
136 136 auto res = impl->m_Range.contains(range);
137 137 impl->unlock();
138 138 return res;
139 139 }
140 140
141 141 bool Variable::intersect(const SqpRange &range) const noexcept
142 142 {
143 143
144 144 impl->lockRead();
145 145 auto res = impl->m_Range.intersect(range);
146 146 impl->unlock();
147 147 return res;
148 148 }
149 149
150 150 bool Variable::isInside(const SqpRange &range) const noexcept
151 151 {
152 152 impl->lockRead();
153 153 auto res = range.contains(SqpRange{impl->m_Range.m_TStart, impl->m_Range.m_TEnd});
154 154 impl->unlock();
155 155 return res;
156 156 }
157 157
158 158 bool Variable::cacheContains(const SqpRange &range) const noexcept
159 159 {
160 160 impl->lockRead();
161 161 auto res = impl->m_CacheRange.contains(range);
162 162 impl->unlock();
163 163 return res;
164 164 }
165 165
166 166 bool Variable::cacheIntersect(const SqpRange &range) const noexcept
167 167 {
168 168 impl->lockRead();
169 169 auto res = impl->m_CacheRange.intersect(range);
170 170 impl->unlock();
171 171 return res;
172 172 }
173 173
174 174 bool Variable::cacheIsInside(const SqpRange &range) const noexcept
175 175 {
176 176 impl->lockRead();
177 177 auto res = range.contains(SqpRange{impl->m_CacheRange.m_TStart, impl->m_CacheRange.m_TEnd});
178 178 impl->unlock();
179 179 return res;
180 180 }
181 181
182 182
183 183 QVector<SqpRange> Variable::provideNotInCacheRangeList(const SqpRange &range) const noexcept
184 184 {
185 // This code assume that cach in contigue. Can return 0, 1 or 2 SqpRange
186
185 187 auto notInCache = QVector<SqpRange>{};
186 188
187 189 if (!this->cacheContains(range)) {
188 190 if (range.m_TEnd <= impl->m_CacheRange.m_TStart
189 191 || range.m_TStart >= impl->m_CacheRange.m_TEnd) {
190 192 notInCache << range;
191 193 }
192 194 else if (range.m_TStart < impl->m_CacheRange.m_TStart
193 195 && range.m_TEnd <= impl->m_CacheRange.m_TEnd) {
194 196 notInCache << SqpRange{range.m_TStart, impl->m_CacheRange.m_TStart};
195 197 }
196 198 else if (range.m_TStart < impl->m_CacheRange.m_TStart
197 199 && range.m_TEnd > impl->m_CacheRange.m_TEnd) {
198 200 notInCache << SqpRange{range.m_TStart, impl->m_CacheRange.m_TStart}
199 201 << SqpRange{impl->m_CacheRange.m_TEnd, range.m_TEnd};
200 202 }
201 203 else if (range.m_TStart < impl->m_CacheRange.m_TEnd) {
202 204 notInCache << SqpRange{impl->m_CacheRange.m_TEnd, range.m_TEnd};
203 205 }
204 206 else {
205 207 qCCritical(LOG_Variable()) << tr("Detection of unknown case.")
206 208 << QThread::currentThread();
207 209 }
208 210 }
209 211
210 212 return notInCache;
211 213 }
214
215 QVector<SqpRange> Variable::provideInCacheRangeList(const SqpRange &range) const noexcept
216 {
217 // This code assume that cach in contigue. Can return 0 or 1 SqpRange
218
219 auto inCache = QVector<SqpRange>{};
220
221
222 if (this->cacheContains(range)) {
223 if (range.m_TStart <= impl->m_CacheRange.m_TEnd
224 && range.m_TEnd >= impl->m_CacheRange.m_TEnd) {
225 inCache << SqpRange{range.m_TStart, impl->m_CacheRange.m_TEnd};
226 }
227
228 else if (range.m_TStart >= impl->m_CacheRange.m_TStart
229 && range.m_TEnd < impl->m_CacheRange.m_TEnd) {
230 inCache << range;
231 }
232 else if (range.m_TStart < impl->m_CacheRange.m_TStart
233 && range.m_TEnd >= impl->m_CacheRange.m_TStart) {
234 inCache << SqpRange{impl->m_CacheRange.m_TStart, range.m_TEnd};
235 }
236 else {
237 qCCritical(LOG_Variable()) << tr("Detection of unknown case.")
238 << QThread::currentThread();
239 }
240 }
241
242 return inCache;
243 }
@@ -1,541 +1,550
1 1 #include <Variable/Variable.h>
2 2 #include <Variable/VariableAcquisitionWorker.h>
3 3 #include <Variable/VariableCacheController.h>
4 4 #include <Variable/VariableCacheStrategy.h>
5 5 #include <Variable/VariableController.h>
6 6 #include <Variable/VariableModel.h>
7 7 #include <Variable/VariableSynchronizationGroup.h>
8 8
9 9 #include <Data/DataProviderParameters.h>
10 10 #include <Data/IDataProvider.h>
11 11 #include <Data/IDataSeries.h>
12 12 #include <Time/TimeController.h>
13 13
14 14 #include <QMutex>
15 15 #include <QThread>
16 16 #include <QUuid>
17 17 #include <QtCore/QItemSelectionModel>
18 18
19 19 #include <set>
20 20 #include <unordered_map>
21 21
22 22 Q_LOGGING_CATEGORY(LOG_VariableController, "VariableController")
23 23
24 24 namespace {
25 25
26 26 SqpRange computeSynchroRangeRequested(const SqpRange &varRange, const SqpRange &graphRange,
27 27 const SqpRange &oldGraphRange)
28 28 {
29 29 auto zoomType = VariableController::getZoomType(graphRange, oldGraphRange);
30 30
31 31 auto varRangeRequested = varRange;
32 32 switch (zoomType) {
33 33 case AcquisitionZoomType::ZoomIn: {
34 34 auto deltaLeft = graphRange.m_TStart - oldGraphRange.m_TStart;
35 35 auto deltaRight = oldGraphRange.m_TEnd - graphRange.m_TEnd;
36 36 varRangeRequested.m_TStart += deltaLeft;
37 37 varRangeRequested.m_TEnd -= deltaRight;
38 38 break;
39 39 }
40 40
41 41 case AcquisitionZoomType::ZoomOut: {
42 42 auto deltaLeft = oldGraphRange.m_TStart - graphRange.m_TStart;
43 43 auto deltaRight = graphRange.m_TEnd - oldGraphRange.m_TEnd;
44 44 varRangeRequested.m_TStart -= deltaLeft;
45 45 varRangeRequested.m_TEnd += deltaRight;
46 46 break;
47 47 }
48 48 case AcquisitionZoomType::PanRight: {
49 49 auto deltaRight = graphRange.m_TEnd - oldGraphRange.m_TEnd;
50 50 varRangeRequested.m_TStart += deltaRight;
51 51 varRangeRequested.m_TEnd += deltaRight;
52 52 break;
53 53 }
54 54 case AcquisitionZoomType::PanLeft: {
55 55 auto deltaLeft = oldGraphRange.m_TStart - graphRange.m_TStart;
56 56 varRangeRequested.m_TStart -= deltaLeft;
57 57 varRangeRequested.m_TEnd -= deltaLeft;
58 58 break;
59 59 }
60 60 case AcquisitionZoomType::Unknown: {
61 61 qCCritical(LOG_VariableController())
62 62 << VariableController::tr("Impossible to synchronize: zoom type unknown");
63 63 break;
64 64 }
65 65 default:
66 66 qCCritical(LOG_VariableController()) << VariableController::tr(
67 67 "Impossible to synchronize: zoom type not take into account");
68 68 // No action
69 69 break;
70 70 }
71 71
72 72 return varRangeRequested;
73 73 }
74 74 }
75 75
76 76 struct VariableController::VariableControllerPrivate {
77 77 explicit VariableControllerPrivate(VariableController *parent)
78 78 : m_WorkingMutex{},
79 79 m_VariableModel{new VariableModel{parent}},
80 80 m_VariableSelectionModel{new QItemSelectionModel{m_VariableModel, parent}},
81 81 m_VariableCacheController{std::make_unique<VariableCacheController>()},
82 82 m_VariableCacheStrategy{std::make_unique<VariableCacheStrategy>()},
83 m_VariableAcquisitionWorker{std::make_unique<VariableAcquisitionWorker>()}
83 m_VariableAcquisitionWorker{std::make_unique<VariableAcquisitionWorker>()},
84 q{parent}
84 85 {
85 86
86 87 m_VariableAcquisitionWorker->moveToThread(&m_VariableAcquisitionWorkerThread);
87 88 m_VariableAcquisitionWorkerThread.setObjectName("VariableAcquisitionWorkerThread");
88 89 }
89 90
90 91
91 92 virtual ~VariableControllerPrivate()
92 93 {
93 94 qCDebug(LOG_VariableController()) << tr("VariableControllerPrivate destruction");
94 95 m_VariableAcquisitionWorkerThread.quit();
95 96 m_VariableAcquisitionWorkerThread.wait();
96 97 }
97 98
98 99
99 100 void processRequest(std::shared_ptr<Variable> var, const SqpRange &rangeRequested);
100 101
101 102 QVector<SqpRange> provideNotInCacheDateTimeList(std::shared_ptr<Variable> variable,
102 103 const SqpRange &dateTime);
103 104
104 105 std::shared_ptr<Variable> findVariable(QUuid vIdentifier);
105 106 std::shared_ptr<IDataSeries>
106 107 retrieveDataSeries(const QVector<AcquisitionDataPacket> acqDataPacketVector);
107 108
108 109 void registerProvider(std::shared_ptr<IDataProvider> provider);
109 110
110 111 QMutex m_WorkingMutex;
111 112 /// Variable model. The VariableController has the ownership
112 113 VariableModel *m_VariableModel;
113 114 QItemSelectionModel *m_VariableSelectionModel;
114 115
115 116
116 117 TimeController *m_TimeController{nullptr};
117 118 std::unique_ptr<VariableCacheController> m_VariableCacheController;
118 119 std::unique_ptr<VariableCacheStrategy> m_VariableCacheStrategy;
119 120 std::unique_ptr<VariableAcquisitionWorker> m_VariableAcquisitionWorker;
120 121 QThread m_VariableAcquisitionWorkerThread;
121 122
122 123 std::unordered_map<std::shared_ptr<Variable>, std::shared_ptr<IDataProvider> >
123 124 m_VariableToProviderMap;
124 125 std::unordered_map<std::shared_ptr<Variable>, QUuid> m_VariableToIdentifierMap;
125 126 std::map<QUuid, std::shared_ptr<VariableSynchronizationGroup> >
126 127 m_GroupIdToVariableSynchronizationGroupMap;
127 128 std::map<QUuid, QUuid> m_VariableIdGroupIdMap;
128 129 std::set<std::shared_ptr<IDataProvider> > m_ProviderSet;
130
131
132 VariableController *q;
129 133 };
130 134
131 135
132 136 VariableController::VariableController(QObject *parent)
133 137 : QObject{parent}, impl{spimpl::make_unique_impl<VariableControllerPrivate>(this)}
134 138 {
135 139 qCDebug(LOG_VariableController()) << tr("VariableController construction")
136 140 << QThread::currentThread();
137 141
138 142 connect(impl->m_VariableModel, &VariableModel::abortProgessRequested, this,
139 143 &VariableController::onAbortProgressRequested);
140 144
141 145 connect(impl->m_VariableAcquisitionWorker.get(), &VariableAcquisitionWorker::dataProvided, this,
142 146 &VariableController::onDataProvided);
143 147 connect(impl->m_VariableAcquisitionWorker.get(),
144 148 &VariableAcquisitionWorker::variableRequestInProgress, this,
145 149 &VariableController::onVariableRetrieveDataInProgress);
146 150
147 151 connect(&impl->m_VariableAcquisitionWorkerThread, &QThread::started,
148 152 impl->m_VariableAcquisitionWorker.get(), &VariableAcquisitionWorker::initialize);
149 153 connect(&impl->m_VariableAcquisitionWorkerThread, &QThread::finished,
150 154 impl->m_VariableAcquisitionWorker.get(), &VariableAcquisitionWorker::finalize);
151 155
152 156
153 157 impl->m_VariableAcquisitionWorkerThread.start();
154 158 }
155 159
156 160 VariableController::~VariableController()
157 161 {
158 162 qCDebug(LOG_VariableController()) << tr("VariableController destruction")
159 163 << QThread::currentThread();
160 164 this->waitForFinish();
161 165 }
162 166
163 167 VariableModel *VariableController::variableModel() noexcept
164 168 {
165 169 return impl->m_VariableModel;
166 170 }
167 171
168 172 QItemSelectionModel *VariableController::variableSelectionModel() noexcept
169 173 {
170 174 return impl->m_VariableSelectionModel;
171 175 }
172 176
173 177 void VariableController::setTimeController(TimeController *timeController) noexcept
174 178 {
175 179 impl->m_TimeController = timeController;
176 180 }
177 181
178 182 void VariableController::deleteVariable(std::shared_ptr<Variable> variable) noexcept
179 183 {
180 184 if (!variable) {
181 185 qCCritical(LOG_VariableController()) << "Can't delete variable: variable is null";
182 186 return;
183 187 }
184 188
185 189 // Spreads in SciQlop that the variable will be deleted, so that potential receivers can
186 190 // make some treatments before the deletion
187 191 emit variableAboutToBeDeleted(variable);
188 192
189 193 // Deletes identifier
190 194 impl->m_VariableToIdentifierMap.erase(variable);
191 195
192 196 // Deletes provider
193 197 auto nbProvidersDeleted = impl->m_VariableToProviderMap.erase(variable);
194 198 qCDebug(LOG_VariableController())
195 199 << tr("Number of providers deleted for variable %1: %2")
196 200 .arg(variable->name(), QString::number(nbProvidersDeleted));
197 201
198 202 // Clears cache
199 203 impl->m_VariableCacheController->clear(variable);
200 204
201 205 // Deletes from model
202 206 impl->m_VariableModel->deleteVariable(variable);
203 207 }
204 208
205 209 void VariableController::deleteVariables(
206 210 const QVector<std::shared_ptr<Variable> > &variables) noexcept
207 211 {
208 212 for (auto variable : qAsConst(variables)) {
209 213 deleteVariable(variable);
210 214 }
211 215 }
212 216
213 217 void VariableController::abortProgress(std::shared_ptr<Variable> variable)
214 218 {
215 219 }
216 220
217 221 void VariableController::createVariable(const QString &name, const QVariantHash &metadata,
218 222 std::shared_ptr<IDataProvider> provider) noexcept
219 223 {
220 224
221 225 if (!impl->m_TimeController) {
222 226 qCCritical(LOG_VariableController())
223 227 << tr("Impossible to create variable: The time controller is null");
224 228 return;
225 229 }
226 230
227 231 auto range = impl->m_TimeController->dateTime();
228 232
229 233 if (auto newVariable = impl->m_VariableModel->createVariable(name, range, metadata)) {
230 234 auto identifier = QUuid::createUuid();
231 235
232 236 // store the provider
233 237 impl->registerProvider(provider);
234 238
235 239 // Associate the provider
236 240 impl->m_VariableToProviderMap[newVariable] = provider;
237 241 impl->m_VariableToIdentifierMap[newVariable] = identifier;
238 242
239 243
240 244 impl->processRequest(newVariable, range);
241 245 }
242 246 }
243 247
244 248 void VariableController::onDateTimeOnSelection(const SqpRange &dateTime)
245 249 {
246 250 // TODO check synchronisation
247 251 qCDebug(LOG_VariableController()) << "VariableController::onDateTimeOnSelection"
248 252 << QThread::currentThread()->objectName();
249 253 auto selectedRows = impl->m_VariableSelectionModel->selectedRows();
250 254
251 255 for (const auto &selectedRow : qAsConst(selectedRows)) {
252 256 if (auto selectedVariable = impl->m_VariableModel->variable(selectedRow.row())) {
253 257 selectedVariable->setRange(dateTime);
254 258 impl->processRequest(selectedVariable, dateTime);
255 259
256 260 // notify that rescale operation has to be done
257 261 emit rangeChanged(selectedVariable, dateTime);
258 262 }
259 263 }
260 264 }
261 265
262 266 void VariableController::onDataProvided(QUuid vIdentifier, const SqpRange &rangeRequested,
263 267 const SqpRange &cacheRangeRequested,
264 268 QVector<AcquisitionDataPacket> dataAcquired)
265 269 {
266 270 if (auto var = impl->findVariable(vIdentifier)) {
267 271 var->setRange(rangeRequested);
268 272 var->setCacheRange(cacheRangeRequested);
269 273 qCDebug(LOG_VariableController()) << tr("1: onDataProvided") << rangeRequested;
270 274 qCDebug(LOG_VariableController()) << tr("2: onDataProvided") << cacheRangeRequested;
271 275
272 276 auto retrievedDataSeries = impl->retrieveDataSeries(dataAcquired);
273 277 qCDebug(LOG_VariableController()) << tr("3: onDataProvided")
274 278 << retrievedDataSeries->range();
275 279 var->mergeDataSeries(retrievedDataSeries);
276 280 qCDebug(LOG_VariableController()) << tr("4: onDataProvided");
277 281 emit var->updated();
278 282 }
279 283 else {
280 284 qCCritical(LOG_VariableController()) << tr("Impossible to provide data to a null variable");
281 285 }
282 286 }
283 287
284 288 void VariableController::onVariableRetrieveDataInProgress(QUuid identifier, double progress)
285 289 {
286 290 if (auto var = impl->findVariable(identifier)) {
287 291 impl->m_VariableModel->setDataProgress(var, progress);
288 292 }
289 293 else {
290 294 qCCritical(LOG_VariableController())
291 295 << tr("Impossible to notify progression of a null variable");
292 296 }
293 297 }
294 298
295 299 void VariableController::onAbortProgressRequested(std::shared_ptr<Variable> variable)
296 300 {
297 301 qCDebug(LOG_VariableController()) << "TORM: VariableController::onAbortProgressRequested"
298 302 << QThread::currentThread()->objectName();
299 303
300 304 auto it = impl->m_VariableToIdentifierMap.find(variable);
301 305 if (it != impl->m_VariableToIdentifierMap.cend()) {
302 306 impl->m_VariableToProviderMap.at(variable)->requestDataAborting(it->second);
303 307 }
304 308 else {
305 309 qCWarning(LOG_VariableController())
306 310 << tr("Aborting progression of inexistant variable detected !!!")
307 311 << QThread::currentThread()->objectName();
308 312 }
309 313 }
310 314
311 315 void VariableController::onAddSynchronizationGroupId(QUuid synchronizationGroupId)
312 316 {
313 317 qCDebug(LOG_VariableController()) << "TORM: VariableController::onAddSynchronizationGroupId"
314 318 << QThread::currentThread()->objectName()
315 319 << synchronizationGroupId;
316 320 auto vSynchroGroup = std::make_shared<VariableSynchronizationGroup>();
317 321 impl->m_GroupIdToVariableSynchronizationGroupMap.insert(
318 322 std::make_pair(synchronizationGroupId, vSynchroGroup));
319 323 }
320 324
321 325 void VariableController::onRemoveSynchronizationGroupId(QUuid synchronizationGroupId)
322 326 {
323 327 impl->m_GroupIdToVariableSynchronizationGroupMap.erase(synchronizationGroupId);
324 328 }
325 329
326 330 void VariableController::onAddSynchronized(std::shared_ptr<Variable> variable,
327 331 QUuid synchronizationGroupId)
328 332
329 333 {
330 334 qCDebug(LOG_VariableController()) << "TORM: VariableController::onAddSynchronized"
331 335 << synchronizationGroupId;
332 336 auto varToVarIdIt = impl->m_VariableToIdentifierMap.find(variable);
333 337 if (varToVarIdIt != impl->m_VariableToIdentifierMap.cend()) {
334 338 auto groupIdToVSGIt
335 339 = impl->m_GroupIdToVariableSynchronizationGroupMap.find(synchronizationGroupId);
336 340 if (groupIdToVSGIt != impl->m_GroupIdToVariableSynchronizationGroupMap.cend()) {
337 341 impl->m_VariableIdGroupIdMap.insert(
338 342 std::make_pair(varToVarIdIt->second, synchronizationGroupId));
339 343 groupIdToVSGIt->second->addVariableId(varToVarIdIt->second);
340 344 }
341 345 else {
342 346 qCCritical(LOG_VariableController())
343 347 << tr("Impossible to synchronize a variable with an unknown sycnhronization group")
344 348 << variable->name();
345 349 }
346 350 }
347 351 else {
348 352 qCCritical(LOG_VariableController())
349 353 << tr("Impossible to synchronize a variable with no identifier") << variable->name();
350 354 }
351 355 }
352 356
353 357
354 358 void VariableController::onRequestDataLoading(QVector<std::shared_ptr<Variable> > variables,
355 359 const SqpRange &range, const SqpRange &oldRange,
356 360 bool synchronise)
357 361 {
358 362 // NOTE: oldRange isn't really necessary since oldRange == variable->range().
359 363
360 364 qCDebug(LOG_VariableController()) << "VariableController::onRequestDataLoading"
361 365 << QThread::currentThread()->objectName();
362 366 // we want to load data of the variable for the dateTime.
363 367 // First we check if the cache contains some of them.
364 368 // For the other, we ask the provider to give them.
365 369
366 370 for (const auto &var : variables) {
367 371 qCDebug(LOG_VariableController()) << "processRequest for" << var->name();
368 372 impl->processRequest(var, range);
369 373 }
370 374
371 375 if (synchronise) {
372 376 // Get the group ids
373 377 qCDebug(LOG_VariableController())
374 378 << "TORM VariableController::onRequestDataLoading for synchro var ENABLE";
375 379 auto groupIds = std::set<QUuid>();
376 380 for (const auto &var : variables) {
377 381 auto varToVarIdIt = impl->m_VariableToIdentifierMap.find(var);
378 382 if (varToVarIdIt != impl->m_VariableToIdentifierMap.cend()) {
379 383 auto vId = varToVarIdIt->second;
380 384 auto varIdToGroupIdIt = impl->m_VariableIdGroupIdMap.find(vId);
381 385 if (varIdToGroupIdIt != impl->m_VariableIdGroupIdMap.cend()) {
382 386 auto gId = varIdToGroupIdIt->second;
383 387 if (groupIds.find(gId) == groupIds.cend()) {
384 388 qCDebug(LOG_VariableController()) << "Synchro detect group " << gId;
385 389 groupIds.insert(gId);
386 390 }
387 391 }
388 392 }
389 393 }
390 394
391 395 // We assume here all group ids exist
392 396 for (const auto &gId : groupIds) {
393 397 auto vSynchronizationGroup = impl->m_GroupIdToVariableSynchronizationGroupMap.at(gId);
394 398 auto vSyncIds = vSynchronizationGroup->getIds();
395 399 qCDebug(LOG_VariableController()) << "Var in synchro group ";
396 400 for (auto vId : vSyncIds) {
397 401 auto var = impl->findVariable(vId);
398 402
399 403 // Don't process already processed var
400 404 if (!variables.contains(var)) {
401 405 if (var != nullptr) {
402 406 qCDebug(LOG_VariableController()) << "processRequest synchro for"
403 407 << var->name();
404 408 auto vSyncRangeRequested
405 409 = computeSynchroRangeRequested(var->range(), range, oldRange);
406 410 impl->processRequest(var, vSyncRangeRequested);
407 411 }
408 412 else {
409 413 qCCritical(LOG_VariableController())
410 414
411 415 << tr("Impossible to synchronize a null variable");
412 416 }
413 417 }
414 418 }
415 419 }
416 420 }
417 421 }
418 422
419 423
420 424 void VariableController::initialize()
421 425 {
422 426 qCDebug(LOG_VariableController()) << tr("VariableController init") << QThread::currentThread();
423 427 impl->m_WorkingMutex.lock();
424 428 qCDebug(LOG_VariableController()) << tr("VariableController init END");
425 429 }
426 430
427 431 void VariableController::finalize()
428 432 {
429 433 impl->m_WorkingMutex.unlock();
430 434 }
431 435
432 436 void VariableController::waitForFinish()
433 437 {
434 438 QMutexLocker locker{&impl->m_WorkingMutex};
435 439 }
436 440
437 441 AcquisitionZoomType VariableController::getZoomType(const SqpRange &range, const SqpRange &oldRange)
438 442 {
439 443 // t1.m_TStart <= t2.m_TStart && t2.m_TEnd <= t1.m_TEnd
440 444 auto zoomType = AcquisitionZoomType::Unknown;
441 445 if (range.m_TStart <= oldRange.m_TStart && oldRange.m_TEnd <= range.m_TEnd) {
442 446 zoomType = AcquisitionZoomType::ZoomOut;
443 447 }
444 448 else if (range.m_TStart > oldRange.m_TStart && range.m_TEnd > oldRange.m_TEnd) {
445 449 zoomType = AcquisitionZoomType::PanRight;
446 450 }
447 451 else if (range.m_TStart < oldRange.m_TStart && range.m_TEnd < oldRange.m_TEnd) {
448 452 zoomType = AcquisitionZoomType::PanLeft;
449 453 }
450 454 else if (range.m_TStart > oldRange.m_TStart && oldRange.m_TEnd > range.m_TEnd) {
451 455 zoomType = AcquisitionZoomType::ZoomIn;
452 456 }
453 457 else {
454 458 qCCritical(LOG_VariableController()) << "getZoomType: Unknown type detected";
455 459 }
456 460 return zoomType;
457 461 }
458 462
459 463 void VariableController::VariableControllerPrivate::processRequest(std::shared_ptr<Variable> var,
460 464 const SqpRange &rangeRequested)
461 465 {
462 466
463 467 auto varRangesRequested
464 468 = m_VariableCacheStrategy->computeCacheRange(var->range(), rangeRequested);
465 469 auto notInCacheRangeList = var->provideNotInCacheRangeList(varRangesRequested.second);
470 auto inCacheRangeList = var->provideInCacheRangeList(varRangesRequested.second);
466 471
467 472 if (!notInCacheRangeList.empty()) {
468 473 auto identifier = m_VariableToIdentifierMap.at(var);
469 474 auto varProvider = m_VariableToProviderMap.at(var);
470 475 if (varProvider != nullptr) {
471 476 m_VariableAcquisitionWorker->pushVariableRequest(
472 477 identifier, varRangesRequested.first, varRangesRequested.second,
473 478 DataProviderParameters{std::move(notInCacheRangeList), var->metadata()},
474 479 varProvider);
475 480 }
476 481 else {
477 482 qCCritical(LOG_VariableController())
478 483 << "Impossible to provide data with a null provider";
479 484 }
485
486 if (!inCacheRangeList.empty()) {
487 emit q->updateVarDisplaying(var, inCacheRangeList.first());
488 }
480 489 }
481 490 else {
482 491 var->setRange(rangeRequested);
483 492 var->setCacheRange(varRangesRequested.second);
484 493 var->setDataSeries(var->dataSeries()->subDataSeries(varRangesRequested.second));
485 494 emit var->updated();
486 495 }
487 496 }
488 497
489 498 std::shared_ptr<Variable>
490 499 VariableController::VariableControllerPrivate::findVariable(QUuid vIdentifier)
491 500 {
492 501 std::shared_ptr<Variable> var;
493 502 auto findReply = [vIdentifier](const auto &entry) { return vIdentifier == entry.second; };
494 503
495 504 auto end = m_VariableToIdentifierMap.cend();
496 505 auto it = std::find_if(m_VariableToIdentifierMap.cbegin(), end, findReply);
497 506 if (it != end) {
498 507 var = it->first;
499 508 }
500 509 else {
501 510 qCCritical(LOG_VariableController())
502 511 << tr("Impossible to find the variable with the identifier: ") << vIdentifier;
503 512 }
504 513
505 514 return var;
506 515 }
507 516
508 517 std::shared_ptr<IDataSeries> VariableController::VariableControllerPrivate::retrieveDataSeries(
509 518 const QVector<AcquisitionDataPacket> acqDataPacketVector)
510 519 {
511 520 qCDebug(LOG_VariableController()) << tr("TORM: retrieveDataSeries acqDataPacketVector size")
512 521 << acqDataPacketVector.size();
513 522 std::shared_ptr<IDataSeries> dataSeries;
514 523 if (!acqDataPacketVector.isEmpty()) {
515 524 dataSeries = acqDataPacketVector[0].m_DateSeries;
516 525 for (int i = 1; i < acqDataPacketVector.size(); ++i) {
517 526 dataSeries->merge(acqDataPacketVector[i].m_DateSeries.get());
518 527 }
519 528 }
520 529 qCDebug(LOG_VariableController()) << tr("TORM: retrieveDataSeries acqDataPacketVector size END")
521 530 << acqDataPacketVector.size();
522 531 return dataSeries;
523 532 }
524 533
525 534 void VariableController::VariableControllerPrivate::registerProvider(
526 535 std::shared_ptr<IDataProvider> provider)
527 536 {
528 537 if (m_ProviderSet.find(provider) == m_ProviderSet.end()) {
529 538 qCDebug(LOG_VariableController()) << tr("Registering of a new provider")
530 539 << provider->objectName();
531 540 m_ProviderSet.insert(provider);
532 541 connect(provider.get(), &IDataProvider::dataProvided, m_VariableAcquisitionWorker.get(),
533 542 &VariableAcquisitionWorker::onVariableDataAcquired);
534 543 connect(provider.get(), &IDataProvider::dataProvidedProgress,
535 544 m_VariableAcquisitionWorker.get(),
536 545 &VariableAcquisitionWorker::onVariableRetrieveDataInProgress);
537 546 }
538 547 else {
539 548 qCDebug(LOG_VariableController()) << tr("Cannot register provider, it already exists ");
540 549 }
541 550 }
@@ -1,84 +1,86
1 1 #ifndef SCIQLOP_VISUALIZATIONGRAPHWIDGET_H
2 2 #define SCIQLOP_VISUALIZATIONGRAPHWIDGET_H
3 3
4 4 #include "Visualization/IVisualizationWidget.h"
5 5
6 6 #include <QLoggingCategory>
7 7 #include <QWidget>
8 8
9 9 #include <memory>
10 10
11 11 #include <Common/spimpl.h>
12 12
13 13 Q_DECLARE_LOGGING_CATEGORY(LOG_VisualizationGraphWidget)
14 14
15 15 class QCPRange;
16 16 class SqpRange;
17 17 class Variable;
18 18
19 19 namespace Ui {
20 20 class VisualizationGraphWidget;
21 21 } // namespace Ui
22 22
23 23 class VisualizationGraphWidget : public QWidget, public IVisualizationWidget {
24 24 Q_OBJECT
25 25
26 26 public:
27 27 explicit VisualizationGraphWidget(const QString &name = {}, QWidget *parent = 0);
28 28 virtual ~VisualizationGraphWidget();
29 29
30 30 /// If acquisition isn't enable, requestDataLoading signal cannot be emit
31 31 void enableAcquisition(bool enable);
32 32
33 33 void addVariable(std::shared_ptr<Variable> variable, SqpRange range);
34 34
35 35 /// Removes a variable from the graph
36 36 void removeVariable(std::shared_ptr<Variable> variable) noexcept;
37 37
38 38 void setRange(std::shared_ptr<Variable> variable, const SqpRange &range);
39 39 void setYRange(const SqpRange &range);
40 40 SqpRange graphRange() const noexcept;
41 41 void setGraphRange(const SqpRange &range);
42 42
43 43 // IVisualizationWidget interface
44 44 void accept(IVisualizationWidgetVisitor *visitor) override;
45 45 bool canDrop(const Variable &variable) const override;
46 46 bool contains(const Variable &variable) const override;
47 47 QString name() const override;
48 48
49 49
50 50 signals:
51 51 void synchronize(const SqpRange &range, const SqpRange &oldRange);
52 52 void requestDataLoading(QVector<std::shared_ptr<Variable> > variable, const SqpRange &range,
53 53 const SqpRange &oldRange, bool synchronise);
54 54
55 55
56 56 void variableAdded(std::shared_ptr<Variable> var);
57 57
58 58
59 59 private:
60 60 Ui::VisualizationGraphWidget *ui;
61 61
62 62 class VisualizationGraphWidgetPrivate;
63 63 spimpl::unique_impl_ptr<VisualizationGraphWidgetPrivate> impl;
64 64
65 65 private slots:
66 66 /// Slot called when right clicking on the graph (displays a menu)
67 67 void onGraphMenuRequested(const QPoint &pos) noexcept;
68 68
69 69 /// Rescale the X axe to range parameter
70 70 void onRangeChanged(const QCPRange &t1, const QCPRange &t2);
71 71
72 72 /// Slot called when a mouse move was made
73 73 void onMouseMove(QMouseEvent *event) noexcept;
74 74 /// Slot called when a mouse wheel was made, to perform some processing before the zoom is done
75 75 void onMouseWheel(QWheelEvent *event) noexcept;
76 76 /// Slot called when a mouse press was made, to activate the calibration of a graph
77 77 void onMousePress(QMouseEvent *event) noexcept;
78 78 /// Slot called when a mouse release was made, to deactivate the calibration of a graph
79 79 void onMouseRelease(QMouseEvent *event) noexcept;
80 80
81 81 void onDataCacheVariableUpdated();
82
83 void onUpdateVarDisplaying(std::shared_ptr<Variable> variable, const SqpRange &range);
82 84 };
83 85
84 86 #endif // SCIQLOP_VISUALIZATIONGRAPHWIDGET_H
@@ -1,210 +1,195
1 1 #include "Visualization/VisualizationGraphHelper.h"
2 2 #include "Visualization/qcustomplot.h"
3 3
4 4 #include <Data/ScalarSeries.h>
5 5
6 6 #include <Variable/Variable.h>
7 7
8 8 Q_LOGGING_CATEGORY(LOG_VisualizationGraphHelper, "VisualizationGraphHelper")
9 9
10 10 namespace {
11 11
12 12 class SqpDataContainer : public QCPGraphDataContainer {
13 13 public:
14 14 void appendGraphData(const QCPGraphData &data) { mData.append(data); }
15 15 };
16 16
17 17
18 18 /// Format for datetimes on a axis
19 19 const auto DATETIME_TICKER_FORMAT = QStringLiteral("yyyy/MM/dd \nhh:mm:ss");
20 20
21 21 /// Generates the appropriate ticker for an axis, depending on whether the axis displays time or
22 22 /// non-time data
23 23 QSharedPointer<QCPAxisTicker> axisTicker(bool isTimeAxis)
24 24 {
25 25 if (isTimeAxis) {
26 26 auto dateTicker = QSharedPointer<QCPAxisTickerDateTime>::create();
27 27 dateTicker->setDateTimeFormat(DATETIME_TICKER_FORMAT);
28 28 dateTicker->setDateTimeSpec(Qt::UTC);
29 29
30 30 return dateTicker;
31 31 }
32 32 else {
33 33 // default ticker
34 34 return QSharedPointer<QCPAxisTicker>::create();
35 35 }
36 36 }
37 37
38 38 void updateScalarData(QCPAbstractPlottable *component, std::shared_ptr<ScalarSeries> scalarSeries,
39 const SqpRange &dateTime)
39 const SqpRange &range)
40 40 {
41 41 qCDebug(LOG_VisualizationGraphHelper()) << "TORM: updateScalarData"
42 42 << QThread::currentThread()->objectName();
43 43 if (auto qcpGraph = dynamic_cast<QCPGraph *>(component)) {
44 44 scalarSeries->lockRead();
45 45 {
46 const auto &xData = scalarSeries->xAxisData()->cdata();
47 const auto &valuesData = scalarSeries->valuesData()->cdata();
48
49 auto xDataBegin = xData.cbegin();
50 auto xDataEnd = xData.cend();
51
52 qCInfo(LOG_VisualizationGraphHelper()) << "TODEBUG: Current points in cache"
53 << xData.count();
54
55 46 auto sqpDataContainer = QSharedPointer<SqpDataContainer>::create();
56 47 qcpGraph->setData(sqpDataContainer);
57
58 auto lowerIt = std::lower_bound(xDataBegin, xDataEnd, dateTime.m_TStart);
59 auto upperIt = std::upper_bound(xDataBegin, xDataEnd, dateTime.m_TEnd);
60 auto distance = std::distance(xDataBegin, lowerIt);
61
62 auto valuesDataIt = valuesData.cbegin() + distance;
63 for (auto xAxisDataIt = lowerIt; xAxisDataIt != upperIt;
64 ++xAxisDataIt, ++valuesDataIt) {
65 sqpDataContainer->appendGraphData(QCPGraphData(*xAxisDataIt, *valuesDataIt));
48 auto bounds = scalarSeries->subData(range.m_TStart, range.m_TEnd);
49 for (auto it = bounds.first; it != bounds.second; ++it) {
50 sqpDataContainer->appendGraphData(QCPGraphData(it->x(), it->value()));
66 51 }
67 52
68 53 qCInfo(LOG_VisualizationGraphHelper()) << "TODEBUG: Current points displayed"
69 54 << sqpDataContainer->size();
70 55 }
71 56 scalarSeries->unlock();
72 57
73 58
74 59 // Display all data
75 60 component->parentPlot()->replot();
76 61 }
77 62 else {
78 63 /// @todo DEBUG
79 64 }
80 65 }
81 66
82 67 QCPAbstractPlottable *createScalarSeriesComponentV2(std::shared_ptr<ScalarSeries> scalarSeries,
83 68 QCustomPlot &plot)
84 69 {
85 70 auto component = plot.addGraph();
86 71
87 72 if (component) {
88 73 // Axes properties
89 74 /// @todo : for the moment, no control is performed on the axes: the units and the tickers
90 75 /// are fixed for the default x-axis and y-axis of the plot, and according to the new graph
91 76
92 77 auto setAxisProperties = [](auto axis, const auto &unit) {
93 78 // label (unit name)
94 79 axis->setLabel(unit.m_Name);
95 80
96 81 // ticker (depending on the type of unit)
97 82 axis->setTicker(axisTicker(unit.m_TimeUnit));
98 83 };
99 84 setAxisProperties(plot.xAxis, scalarSeries->xAxisUnit());
100 85 setAxisProperties(plot.yAxis, scalarSeries->valuesUnit());
101 86 }
102 87 return component;
103 88 }
104 89
105 90 QCPAbstractPlottable *createScalarSeriesComponent(std::shared_ptr<ScalarSeries> scalarSeries,
106 91 QCustomPlot &plot, const SqpRange &dateTime)
107 92 {
108 93 auto component = plot.addGraph();
109 94
110 95 if (component) {
111 96 // // Graph data
112 97 component->setData(scalarSeries->xAxisData()->data(), scalarSeries->valuesData()->data(),
113 98 true);
114 99
115 100 updateScalarData(component, scalarSeries, dateTime);
116 101
117 102 // Axes properties
118 103 /// @todo : for the moment, no control is performed on the axes: the units and the tickers
119 104 /// are fixed for the default x-axis and y-axis of the plot, and according to the new graph
120 105
121 106 auto setAxisProperties = [](auto axis, const auto &unit) {
122 107 // label (unit name)
123 108 axis->setLabel(unit.m_Name);
124 109
125 110 // ticker (depending on the type of unit)
126 111 axis->setTicker(axisTicker(unit.m_TimeUnit));
127 112 };
128 113 setAxisProperties(plot.xAxis, scalarSeries->xAxisUnit());
129 114 setAxisProperties(plot.yAxis, scalarSeries->valuesUnit());
130 115
131 116 // Display all data
132 117 component->rescaleAxes();
133 118 plot.replot();
134 119 }
135 120 else {
136 121 qCDebug(LOG_VisualizationGraphHelper())
137 122 << QObject::tr("Can't create graph for the scalar series");
138 123 }
139 124
140 125 return component;
141 126 }
142 127
143 128 } // namespace
144 129
145 130 QVector<QCPAbstractPlottable *>
146 131 VisualizationGraphHelper::createV2(std::shared_ptr<Variable> variable, QCustomPlot &plot) noexcept
147 132 {
148 133 auto result = QVector<QCPAbstractPlottable *>{};
149 134
150 135 if (variable) {
151 136 // Gets the data series of the variable to call the creation of the right components
152 137 // according to its type
153 138 if (auto scalarSeries = std::dynamic_pointer_cast<ScalarSeries>(variable->dataSeries())) {
154 139 result.append(createScalarSeriesComponentV2(scalarSeries, plot));
155 140 }
156 141 else {
157 142 qCDebug(LOG_VisualizationGraphHelper())
158 143 << QObject::tr("Can't create graph plottables : unmanaged data series type");
159 144 }
160 145 }
161 146 else {
162 147 qCDebug(LOG_VisualizationGraphHelper())
163 148 << QObject::tr("Can't create graph plottables : the variable is null");
164 149 }
165 150
166 151 return result;
167 152 }
168 153
169 154 QVector<QCPAbstractPlottable *> VisualizationGraphHelper::create(std::shared_ptr<Variable> variable,
170 155 QCustomPlot &plot) noexcept
171 156 {
172 157 auto result = QVector<QCPAbstractPlottable *>{};
173 158
174 159 if (variable) {
175 160 // Gets the data series of the variable to call the creation of the right components
176 161 // according to its type
177 162 if (auto scalarSeries = std::dynamic_pointer_cast<ScalarSeries>(variable->dataSeries())) {
178 163 result.append(createScalarSeriesComponent(scalarSeries, plot, variable->range()));
179 164 }
180 165 else {
181 166 qCDebug(LOG_VisualizationGraphHelper())
182 167 << QObject::tr("Can't create graph plottables : unmanaged data series type");
183 168 }
184 169 }
185 170 else {
186 171 qCDebug(LOG_VisualizationGraphHelper())
187 172 << QObject::tr("Can't create graph plottables : the variable is null");
188 173 }
189 174
190 175 return result;
191 176 }
192 177
193 178 void VisualizationGraphHelper::updateData(QVector<QCPAbstractPlottable *> plotableVect,
194 179 std::shared_ptr<IDataSeries> dataSeries,
195 180 const SqpRange &dateTime)
196 181 {
197 182 if (auto scalarSeries = std::dynamic_pointer_cast<ScalarSeries>(dataSeries)) {
198 183 if (plotableVect.size() == 1) {
199 184 updateScalarData(plotableVect.at(0), scalarSeries, dateTime);
200 185 }
201 186 else {
202 187 qCCritical(LOG_VisualizationGraphHelper()) << QObject::tr(
203 188 "Can't update Data of a scalarSeries because there is not only one component "
204 189 "associated");
205 190 }
206 191 }
207 192 else {
208 193 /// @todo DEBUG
209 194 }
210 195 }
@@ -1,307 +1,320
1 1 #include "Visualization/VisualizationGraphWidget.h"
2 2 #include "Visualization/IVisualizationWidgetVisitor.h"
3 3 #include "Visualization/VisualizationGraphHelper.h"
4 4 #include "Visualization/VisualizationGraphRenderingDelegate.h"
5 5 #include "ui_VisualizationGraphWidget.h"
6 6
7 7 #include <Data/ArrayData.h>
8 8 #include <Data/IDataSeries.h>
9 9 #include <Settings/SqpSettingsDefs.h>
10 10 #include <SqpApplication.h>
11 11 #include <Variable/Variable.h>
12 12 #include <Variable/VariableController.h>
13 13
14 14 #include <unordered_map>
15 15
16 16 Q_LOGGING_CATEGORY(LOG_VisualizationGraphWidget, "VisualizationGraphWidget")
17 17
18 18 namespace {
19 19
20 20 /// Key pressed to enable zoom on horizontal axis
21 21 const auto HORIZONTAL_ZOOM_MODIFIER = Qt::NoModifier;
22 22
23 23 /// Key pressed to enable zoom on vertical axis
24 24 const auto VERTICAL_ZOOM_MODIFIER = Qt::ControlModifier;
25 25
26 26 } // namespace
27 27
28 28 struct VisualizationGraphWidget::VisualizationGraphWidgetPrivate {
29 29
30 30 explicit VisualizationGraphWidgetPrivate()
31 31 : m_DoAcquisition{true}, m_IsCalibration{false}, m_RenderingDelegate{nullptr}
32 32 {
33 33 }
34 34
35 35 // 1 variable -> n qcpplot
36 36 std::multimap<std::shared_ptr<Variable>, QCPAbstractPlottable *> m_VariableToPlotMultiMap;
37 37 bool m_DoAcquisition;
38 38 bool m_IsCalibration;
39 39 QCPItemTracer *m_TextTracer;
40 40 /// Delegate used to attach rendering features to the plot
41 41 std::unique_ptr<VisualizationGraphRenderingDelegate> m_RenderingDelegate;
42 42 };
43 43
44 44 VisualizationGraphWidget::VisualizationGraphWidget(const QString &name, QWidget *parent)
45 45 : QWidget{parent},
46 46 ui{new Ui::VisualizationGraphWidget},
47 47 impl{spimpl::make_unique_impl<VisualizationGraphWidgetPrivate>()}
48 48 {
49 49 ui->setupUi(this);
50 50
51 51 // The delegate must be initialized after the ui as it uses the plot
52 52 impl->m_RenderingDelegate = std::make_unique<VisualizationGraphRenderingDelegate>(*ui->widget);
53 53
54 54 ui->graphNameLabel->setText(name);
55 55
56 56 // 'Close' options : widget is deleted when closed
57 57 setAttribute(Qt::WA_DeleteOnClose);
58 58 connect(ui->closeButton, &QToolButton::clicked, this, &VisualizationGraphWidget::close);
59 59 ui->closeButton->setIcon(sqpApp->style()->standardIcon(QStyle::SP_TitleBarCloseButton));
60 60
61 61 // Set qcpplot properties :
62 62 // - Drag (on x-axis) and zoom are enabled
63 63 // - Mouse wheel on qcpplot is intercepted to determine the zoom orientation
64 64 ui->widget->setInteractions(QCP::iRangeDrag | QCP::iRangeZoom);
65 65 ui->widget->axisRect()->setRangeDrag(Qt::Horizontal);
66 66
67 67 connect(ui->widget, &QCustomPlot::mousePress, this, &VisualizationGraphWidget::onMousePress);
68 68 connect(ui->widget, &QCustomPlot::mouseRelease, this,
69 69 &VisualizationGraphWidget::onMouseRelease);
70 70 connect(ui->widget, &QCustomPlot::mouseMove, this, &VisualizationGraphWidget::onMouseMove);
71 71 connect(ui->widget, &QCustomPlot::mouseWheel, this, &VisualizationGraphWidget::onMouseWheel);
72 72 connect(ui->widget->xAxis, static_cast<void (QCPAxis::*)(const QCPRange &, const QCPRange &)>(
73 73 &QCPAxis::rangeChanged),
74 74 this, &VisualizationGraphWidget::onRangeChanged, Qt::DirectConnection);
75 75
76 76 // Activates menu when right clicking on the graph
77 77 ui->widget->setContextMenuPolicy(Qt::CustomContextMenu);
78 78 connect(ui->widget, &QCustomPlot::customContextMenuRequested, this,
79 79 &VisualizationGraphWidget::onGraphMenuRequested);
80 80
81 81 connect(this, &VisualizationGraphWidget::requestDataLoading, &sqpApp->variableController(),
82 82 &VariableController::onRequestDataLoading);
83
84 connect(&sqpApp->variableController(), &VariableController::updateVarDisplaying, this,
85 &VisualizationGraphWidget::onUpdateVarDisplaying);
83 86 }
84 87
85 88
86 89 VisualizationGraphWidget::~VisualizationGraphWidget()
87 90 {
88 91 delete ui;
89 92 }
90 93
91 94 void VisualizationGraphWidget::enableAcquisition(bool enable)
92 95 {
93 96 impl->m_DoAcquisition = enable;
94 97 }
95 98
96 99 void VisualizationGraphWidget::addVariable(std::shared_ptr<Variable> variable, SqpRange range)
97 100 {
98 101 // Uses delegate to create the qcpplot components according to the variable
99 102 auto createdPlottables = VisualizationGraphHelper::createV2(variable, *ui->widget);
100 103
101 104 for (auto createdPlottable : qAsConst(createdPlottables)) {
102 105 impl->m_VariableToPlotMultiMap.insert({variable, createdPlottable});
103 106 }
104 107
105 108 connect(variable.get(), SIGNAL(updated()), this, SLOT(onDataCacheVariableUpdated()));
106 109
107 110 auto varRange = variable->range();
108 111
109 112 this->enableAcquisition(false);
110 113 this->setGraphRange(range);
111 114 this->enableAcquisition(true);
112 115
113 116 emit requestDataLoading(QVector<std::shared_ptr<Variable> >() << variable, range, varRange,
114 117 false);
115 118
116 119 emit variableAdded(variable);
117 120 }
118 121
119 122 void VisualizationGraphWidget::removeVariable(std::shared_ptr<Variable> variable) noexcept
120 123 {
121 124 // Each component associated to the variable :
122 125 // - is removed from qcpplot (which deletes it)
123 126 // - is no longer referenced in the map
124 127 auto componentsIt = impl->m_VariableToPlotMultiMap.equal_range(variable);
125 128 for (auto it = componentsIt.first; it != componentsIt.second;) {
126 129 ui->widget->removePlottable(it->second);
127 130 it = impl->m_VariableToPlotMultiMap.erase(it);
128 131 }
129 132
130 133 // Updates graph
131 134 ui->widget->replot();
132 135 }
133 136
134 137 void VisualizationGraphWidget::setRange(std::shared_ptr<Variable> variable, const SqpRange &range)
135 138 {
136 139 // Note: in case of different axes that depends on variable, we could start with a code like
137 140 // that:
138 141 // auto componentsIt = impl->m_VariableToPlotMultiMap.equal_range(variable);
139 142 // for (auto it = componentsIt.first; it != componentsIt.second;) {
140 143 // }
141 144 ui->widget->xAxis->setRange(range.m_TStart, range.m_TEnd);
142 145 ui->widget->replot();
143 146 }
144 147
145 148 void VisualizationGraphWidget::setYRange(const SqpRange &range)
146 149 {
147 150 ui->widget->yAxis->setRange(range.m_TStart, range.m_TEnd);
148 151 }
149 152
150 153 SqpRange VisualizationGraphWidget::graphRange() const noexcept
151 154 {
152 155 auto graphRange = ui->widget->xAxis->range();
153 156 return SqpRange{graphRange.lower, graphRange.upper};
154 157 }
155 158
156 159 void VisualizationGraphWidget::setGraphRange(const SqpRange &range)
157 160 {
158 161 qCDebug(LOG_VisualizationGraphWidget()) << tr("VisualizationGraphWidget::setGraphRange START");
159 162 ui->widget->xAxis->setRange(range.m_TStart, range.m_TEnd);
160 163 ui->widget->replot();
161 164 qCDebug(LOG_VisualizationGraphWidget()) << tr("VisualizationGraphWidget::setGraphRange END");
162 165 }
163 166
164 167 void VisualizationGraphWidget::accept(IVisualizationWidgetVisitor *visitor)
165 168 {
166 169 if (visitor) {
167 170 visitor->visit(this);
168 171 }
169 172 else {
170 173 qCCritical(LOG_VisualizationGraphWidget())
171 174 << tr("Can't visit widget : the visitor is null");
172 175 }
173 176 }
174 177
175 178 bool VisualizationGraphWidget::canDrop(const Variable &variable) const
176 179 {
177 180 /// @todo : for the moment, a graph can always accomodate a variable
178 181 Q_UNUSED(variable);
179 182 return true;
180 183 }
181 184
182 185 bool VisualizationGraphWidget::contains(const Variable &variable) const
183 186 {
184 187 // Finds the variable among the keys of the map
185 188 auto variablePtr = &variable;
186 189 auto findVariable
187 190 = [variablePtr](const auto &entry) { return variablePtr == entry.first.get(); };
188 191
189 192 auto end = impl->m_VariableToPlotMultiMap.cend();
190 193 auto it = std::find_if(impl->m_VariableToPlotMultiMap.cbegin(), end, findVariable);
191 194 return it != end;
192 195 }
193 196
194 197 QString VisualizationGraphWidget::name() const
195 198 {
196 199 return ui->graphNameLabel->text();
197 200 }
198 201
199 202 void VisualizationGraphWidget::onGraphMenuRequested(const QPoint &pos) noexcept
200 203 {
201 204 QMenu graphMenu{};
202 205
203 206 // Iterates on variables (unique keys)
204 207 for (auto it = impl->m_VariableToPlotMultiMap.cbegin(),
205 208 end = impl->m_VariableToPlotMultiMap.cend();
206 209 it != end; it = impl->m_VariableToPlotMultiMap.upper_bound(it->first)) {
207 210 // 'Remove variable' action
208 211 graphMenu.addAction(tr("Remove variable %1").arg(it->first->name()),
209 212 [ this, var = it->first ]() { removeVariable(var); });
210 213 }
211 214
212 215 if (!graphMenu.isEmpty()) {
213 216 graphMenu.exec(mapToGlobal(pos));
214 217 }
215 218 }
216 219
217 220 void VisualizationGraphWidget::onRangeChanged(const QCPRange &t1, const QCPRange &t2)
218 221 {
219 222 qCDebug(LOG_VisualizationGraphWidget()) << tr("TORM: VisualizationGraphWidget::onRangeChanged")
220 223 << QThread::currentThread()->objectName() << "DoAcqui"
221 224 << impl->m_DoAcquisition;
222 225
223 226 auto graphRange = SqpRange{t1.lower, t1.upper};
224 227 auto oldGraphRange = SqpRange{t2.lower, t2.upper};
225 228
226 229 if (impl->m_DoAcquisition) {
227 230 QVector<std::shared_ptr<Variable> > variableUnderGraphVector;
228 231
229 232 for (auto it = impl->m_VariableToPlotMultiMap.begin(),
230 233 end = impl->m_VariableToPlotMultiMap.end();
231 234 it != end; it = impl->m_VariableToPlotMultiMap.upper_bound(it->first)) {
232 235 variableUnderGraphVector.push_back(it->first);
233 236 }
234 237 emit requestDataLoading(std::move(variableUnderGraphVector), graphRange, oldGraphRange,
235 238 !impl->m_IsCalibration);
236 239
237 240 if (!impl->m_IsCalibration) {
238 241 qCDebug(LOG_VisualizationGraphWidget())
239 242 << tr("TORM: VisualizationGraphWidget::Synchronize notify !!")
240 243 << QThread::currentThread()->objectName() << graphRange << oldGraphRange;
241 244 emit synchronize(graphRange, oldGraphRange);
242 245 }
243 246 }
244 247 }
245 248
246 249 void VisualizationGraphWidget::onMouseMove(QMouseEvent *event) noexcept
247 250 {
248 251 // Handles plot rendering when mouse is moving
249 252 impl->m_RenderingDelegate->onMouseMove(event);
250 253 }
251 254
252 255 void VisualizationGraphWidget::onMouseWheel(QWheelEvent *event) noexcept
253 256 {
254 257 auto zoomOrientations = QFlags<Qt::Orientation>{};
255 258
256 259 // Lambda that enables a zoom orientation if the key modifier related to this orientation
257 260 // has
258 261 // been pressed
259 262 auto enableOrientation
260 263 = [&zoomOrientations, event](const auto &orientation, const auto &modifier) {
261 264 auto orientationEnabled = event->modifiers().testFlag(modifier);
262 265 zoomOrientations.setFlag(orientation, orientationEnabled);
263 266 };
264 267 enableOrientation(Qt::Vertical, VERTICAL_ZOOM_MODIFIER);
265 268 enableOrientation(Qt::Horizontal, HORIZONTAL_ZOOM_MODIFIER);
266 269
267 270 ui->widget->axisRect()->setRangeZoom(zoomOrientations);
268 271 }
269 272
270 273 void VisualizationGraphWidget::onMousePress(QMouseEvent *event) noexcept
271 274 {
272 275 impl->m_IsCalibration = event->modifiers().testFlag(Qt::ControlModifier);
273 276 }
274 277
275 278 void VisualizationGraphWidget::onMouseRelease(QMouseEvent *event) noexcept
276 279 {
277 280 impl->m_IsCalibration = false;
278 281 }
279 282
280 283 void VisualizationGraphWidget::onDataCacheVariableUpdated()
281 284 {
282 285 // NOTE:
283 286 // We don't want to call the method for each component of a variable unitarily, but for
284 287 // all
285 288 // its components at once (eg its three components in the case of a vector).
286 289
287 290 // The unordered_multimap does not do this easily, so the question is whether to:
288 291 // - use an ordered_multimap and the algos of std to group the values by key
289 292 // - use a map (unique keys) and store as values directly the list of components
290 293
291 294 auto graphRange = ui->widget->xAxis->range();
292 295 auto dateTime = SqpRange{graphRange.lower, graphRange.upper};
293 296
294 297 for (auto it = impl->m_VariableToPlotMultiMap.cbegin();
295 298 it != impl->m_VariableToPlotMultiMap.cend(); ++it) {
296 299 auto variable = it->first;
297 300 qCDebug(LOG_VisualizationGraphWidget())
298 301 << "TORM: VisualizationGraphWidget::onDataCacheVariableUpdated S" << variable->range();
299 302 qCDebug(LOG_VisualizationGraphWidget())
300 303 << "TORM: VisualizationGraphWidget::onDataCacheVariableUpdated E" << dateTime;
301 304 if (dateTime.contains(variable->range()) || dateTime.intersect(variable->range())) {
302 305
303 306 VisualizationGraphHelper::updateData(QVector<QCPAbstractPlottable *>{} << it->second,
304 307 variable->dataSeries(), variable->range());
305 308 }
306 309 }
307 310 }
311
312 void VisualizationGraphWidget::onUpdateVarDisplaying(std::shared_ptr<Variable> variable,
313 const SqpRange &range)
314 {
315 auto componentsIt = impl->m_VariableToPlotMultiMap.equal_range(variable);
316 for (auto it = componentsIt.first; it != componentsIt.second;) {
317 VisualizationGraphHelper::updateData(QVector<QCPAbstractPlottable *>{} << it->second,
318 variable->dataSeries(), range);
319 }
320 }
@@ -1,310 +1,310
1 1 #include "AmdaResultParser.h"
2 2
3 3 #include <Data/ScalarSeries.h>
4 4 #include <Data/VectorSeries.h>
5 5
6 6 #include <QObject>
7 7 #include <QtTest>
8 8
9 9 namespace {
10 10
11 11 /// Path for the tests
12 12 const auto TESTS_RESOURCES_PATH
13 13 = QFileInfo{QString{AMDA_TESTS_RESOURCES_DIR}, "TestAmdaResultParser"}.absoluteFilePath();
14 14
15 15 QDateTime dateTime(int year, int month, int day, int hours, int minutes, int seconds)
16 16 {
17 17 return QDateTime{{year, month, day}, {hours, minutes, seconds}, Qt::UTC};
18 18 }
19 19
20 20 /// Compares two vectors that can potentially contain NaN values
21 21 bool compareVectors(const QVector<double> &v1, const QVector<double> &v2)
22 22 {
23 23 if (v1.size() != v2.size()) {
24 24 return false;
25 25 }
26 26
27 27 auto result = true;
28 28 auto v2It = v2.cbegin();
29 29 for (auto v1It = v1.cbegin(), v1End = v1.cend(); v1It != v1End && result; ++v1It, ++v2It) {
30 30 auto v1Value = *v1It;
31 31 auto v2Value = *v2It;
32 32
33 33 // If v1 is NaN, v2 has to be NaN too
34 34 result = std::isnan(v1Value) ? std::isnan(v2Value) : (v1Value == v2Value);
35 35 }
36 36
37 37 return result;
38 38 }
39 39
40 40 bool compareVectors(const QVector<QVector<double> > &v1, const QVector<QVector<double> > &v2)
41 41 {
42 42 if (v1.size() != v2.size()) {
43 43 return false;
44 44 }
45 45
46 46 auto result = true;
47 47 for (auto i = 0; i < v1.size() && result; ++i) {
48 48 result &= compareVectors(v1.at(i), v2.at(i));
49 49 }
50 50
51 51 return result;
52 52 }
53 53
54 54 QVector<QVector<double> > valuesData(const ArrayData<1> &arrayData)
55 55 {
56 56 return QVector<QVector<double> >{arrayData.data()};
57 57 }
58 58
59 59 QVector<QVector<double> > valuesData(const ArrayData<2> &arrayData)
60 60 {
61 61 return arrayData.data();
62 62 }
63 63
64 64
65 65 QString inputFilePath(const QString &inputFileName)
66 66 {
67 67 return QFileInfo{TESTS_RESOURCES_PATH, inputFileName}.absoluteFilePath();
68 68 }
69 69
70 70 template <typename T>
71 71 struct ExpectedResults {
72 72 explicit ExpectedResults() = default;
73 73
74 74 explicit ExpectedResults(Unit xAxisUnit, Unit valuesUnit, const QVector<QDateTime> &xAxisData,
75 75 QVector<double> valuesData)
76 76 : ExpectedResults(xAxisUnit, valuesUnit, xAxisData,
77 77 QVector<QVector<double> >{std::move(valuesData)})
78 78 {
79 79 }
80 80
81 81 /// Ctor with QVector<QDateTime> as x-axis data. Datetimes are converted to doubles
82 82 explicit ExpectedResults(Unit xAxisUnit, Unit valuesUnit, const QVector<QDateTime> &xAxisData,
83 83 QVector<QVector<double> > valuesData)
84 84 : m_ParsingOK{true},
85 85 m_XAxisUnit{xAxisUnit},
86 86 m_ValuesUnit{valuesUnit},
87 87 m_XAxisData{},
88 88 m_ValuesData{std::move(valuesData)}
89 89 {
90 90 // Converts QVector<QDateTime> to QVector<double>
91 91 std::transform(xAxisData.cbegin(), xAxisData.cend(), std::back_inserter(m_XAxisData),
92 92 [](const auto &dateTime) { return dateTime.toMSecsSinceEpoch() / 1000.; });
93 93 }
94 94
95 95 /**
96 96 * Validates a DataSeries compared to the expected results
97 97 * @param results the DataSeries to validate
98 98 */
99 99 void validate(std::shared_ptr<IDataSeries> results)
100 100 {
101 101 if (m_ParsingOK) {
102 102 auto dataSeries = dynamic_cast<T *>(results.get());
103 103 QVERIFY(dataSeries != nullptr);
104 104
105 105 // Checks units
106 106 QVERIFY(dataSeries->xAxisUnit() == m_XAxisUnit);
107 107 QVERIFY(dataSeries->valuesUnit() == m_ValuesUnit);
108 108
109 109 // Checks values : as the vectors can potentially contain NaN values, we must use a
110 110 // custom vector comparison method
111 111 QVERIFY(compareVectors(dataSeries->xAxisData()->data(), m_XAxisData));
112 112 QVERIFY(compareVectors(valuesData(*dataSeries->valuesData()), m_ValuesData));
113 113 }
114 114 else {
115 115 QVERIFY(results == nullptr);
116 116 }
117 117 }
118 118
119 119 // Parsing was successfully completed
120 120 bool m_ParsingOK{false};
121 121 // Expected x-axis unit
122 122 Unit m_XAxisUnit{};
123 123 // Expected values unit
124 124 Unit m_ValuesUnit{};
125 125 // Expected x-axis data
126 126 QVector<double> m_XAxisData{};
127 127 // Expected values data
128 128 QVector<QVector<double> > m_ValuesData{};
129 129 };
130 130
131 131 } // namespace
132 132
133 133 Q_DECLARE_METATYPE(ExpectedResults<ScalarSeries>)
134 134 Q_DECLARE_METATYPE(ExpectedResults<VectorSeries>)
135 135
136 136 class TestAmdaResultParser : public QObject {
137 137 Q_OBJECT
138 138 private:
139 139 template <typename T>
140 140 void testReadDataStructure()
141 141 {
142 142 // ////////////// //
143 143 // Test structure //
144 144 // ////////////// //
145 145
146 146 // Name of TXT file to read
147 147 QTest::addColumn<QString>("inputFileName");
148 148 // Expected results
149 149 QTest::addColumn<ExpectedResults<T> >("expectedResults");
150 150 }
151 151
152 152 template <typename T>
153 153 void testRead(AmdaResultParser::ValueType valueType)
154 154 {
155 155 QFETCH(QString, inputFileName);
156 156 QFETCH(ExpectedResults<T>, expectedResults);
157 157
158 158 // Parses file
159 159 auto filePath = inputFilePath(inputFileName);
160 160 auto results = AmdaResultParser::readTxt(filePath, valueType);
161 161
162 162 // ///////////////// //
163 163 // Validates results //
164 164 // ///////////////// //
165 165 expectedResults.validate(results);
166 166 }
167 167
168 168 private slots:
169 169 /// Input test data
170 170 /// @sa testReadScalarTxt()
171 171 void testReadScalarTxt_data();
172 172
173 173 /// Tests parsing scalar series of a TXT file
174 174 void testReadScalarTxt();
175 175
176 176 /// Input test data
177 177 /// @sa testReadVectorTxt()
178 178 void testReadVectorTxt_data();
179 179
180 180 /// Tests parsing vector series of a TXT file
181 181 void testReadVectorTxt();
182 182 };
183 183
184 184 void TestAmdaResultParser::testReadScalarTxt_data()
185 185 {
186 186 testReadDataStructure<ScalarSeries>();
187 187
188 188 // ////////// //
189 189 // Test cases //
190 190 // ////////// //
191 191
192 192 // Valid files
193 193 QTest::newRow("Valid file")
194 194 << QStringLiteral("ValidScalar1.txt")
195 195 << ExpectedResults<ScalarSeries>{
196 196 Unit{QStringLiteral("nT"), true}, Unit{},
197 197 QVector<QDateTime>{dateTime(2013, 9, 23, 9, 0, 30), dateTime(2013, 9, 23, 9, 1, 30),
198 198 dateTime(2013, 9, 23, 9, 2, 30), dateTime(2013, 9, 23, 9, 3, 30),
199 199 dateTime(2013, 9, 23, 9, 4, 30), dateTime(2013, 9, 23, 9, 5, 30),
200 200 dateTime(2013, 9, 23, 9, 6, 30), dateTime(2013, 9, 23, 9, 7, 30),
201 201 dateTime(2013, 9, 23, 9, 8, 30), dateTime(2013, 9, 23, 9, 9, 30)},
202 202 QVector<double>{-2.83950, -2.71850, -2.52150, -2.57633, -2.58050, -2.48325, -2.63025,
203 203 -2.55800, -2.43250, -2.42200}};
204 204
205 205 QTest::newRow("Valid file (value of first line is invalid but it is converted to NaN")
206 206 << QStringLiteral("WrongValue.txt")
207 207 << ExpectedResults<ScalarSeries>{
208 208 Unit{QStringLiteral("nT"), true}, Unit{},
209 209 QVector<QDateTime>{dateTime(2013, 9, 23, 9, 0, 30), dateTime(2013, 9, 23, 9, 1, 30),
210 210 dateTime(2013, 9, 23, 9, 2, 30)},
211 211 QVector<double>{std::numeric_limits<double>::quiet_NaN(), -2.71850, -2.52150}};
212 212
213 213 QTest::newRow("Valid file that contains NaN values")
214 214 << QStringLiteral("NaNValue.txt")
215 215 << ExpectedResults<ScalarSeries>{
216 216 Unit{QStringLiteral("nT"), true}, Unit{},
217 217 QVector<QDateTime>{dateTime(2013, 9, 23, 9, 0, 30), dateTime(2013, 9, 23, 9, 1, 30),
218 218 dateTime(2013, 9, 23, 9, 2, 30)},
219 219 QVector<double>{std::numeric_limits<double>::quiet_NaN(), -2.71850, -2.52150}};
220 220
221 221 // Valid files but with some invalid lines (wrong unit, wrong values, etc.)
222 222 QTest::newRow("No unit file") << QStringLiteral("NoUnit.txt")
223 223 << ExpectedResults<ScalarSeries>{Unit{QStringLiteral(""), true},
224 224 Unit{}, QVector<QDateTime>{},
225 225 QVector<double>{}};
226 226 QTest::newRow("Wrong unit file")
227 227 << QStringLiteral("WrongUnit.txt")
228 228 << ExpectedResults<ScalarSeries>{Unit{QStringLiteral(""), true}, Unit{},
229 229 QVector<QDateTime>{dateTime(2013, 9, 23, 9, 0, 30),
230 230 dateTime(2013, 9, 23, 9, 1, 30),
231 231 dateTime(2013, 9, 23, 9, 2, 30)},
232 232 QVector<double>{-2.83950, -2.71850, -2.52150}};
233 233
234 234 QTest::newRow("Wrong results file (date of first line is invalid")
235 235 << QStringLiteral("WrongDate.txt")
236 236 << ExpectedResults<ScalarSeries>{
237 237 Unit{QStringLiteral("nT"), true}, Unit{},
238 238 QVector<QDateTime>{dateTime(2013, 9, 23, 9, 1, 30), dateTime(2013, 9, 23, 9, 2, 30)},
239 239 QVector<double>{-2.71850, -2.52150}};
240 240
241 241 QTest::newRow("Wrong results file (too many values for first line")
242 242 << QStringLiteral("TooManyValues.txt")
243 243 << ExpectedResults<ScalarSeries>{
244 244 Unit{QStringLiteral("nT"), true}, Unit{},
245 245 QVector<QDateTime>{dateTime(2013, 9, 23, 9, 1, 30), dateTime(2013, 9, 23, 9, 2, 30)},
246 246 QVector<double>{-2.71850, -2.52150}};
247 247
248 248 QTest::newRow("Wrong results file (x of first line is NaN")
249 249 << QStringLiteral("NaNX.txt")
250 250 << ExpectedResults<ScalarSeries>{
251 251 Unit{QStringLiteral("nT"), true}, Unit{},
252 252 QVector<QDateTime>{dateTime(2013, 9, 23, 9, 1, 30), dateTime(2013, 9, 23, 9, 2, 30)},
253 253 QVector<double>{-2.71850, -2.52150}};
254 254
255 255 QTest::newRow("Invalid file type (vector)")
256 256 << QStringLiteral("ValidVector1.txt")
257 257 << ExpectedResults<ScalarSeries>{Unit{QStringLiteral("nT"), true}, Unit{},
258 258 QVector<QDateTime>{}, QVector<double>{}};
259 259
260 260 // Invalid files
261 QTest::newRow("Invalid file (unexisting file)")
262 << QStringLiteral("UnexistingFile.txt") << ExpectedResults<ScalarSeries>{};
261 QTest::newRow("Invalid file (unexisting file)") << QStringLiteral("UnexistingFile.txt")
262 << ExpectedResults<ScalarSeries>{};
263 263
264 QTest::newRow("Invalid file (file not found on server)")
265 << QStringLiteral("FileNotFound.txt") << ExpectedResults<ScalarSeries>{};
264 QTest::newRow("Invalid file (file not found on server)") << QStringLiteral("FileNotFound.txt")
265 << ExpectedResults<ScalarSeries>{};
266 266 }
267 267
268 268 void TestAmdaResultParser::testReadScalarTxt()
269 269 {
270 270 testRead<ScalarSeries>(AmdaResultParser::ValueType::SCALAR);
271 271 }
272 272
273 273 void TestAmdaResultParser::testReadVectorTxt_data()
274 274 {
275 275 testReadDataStructure<VectorSeries>();
276 276
277 277 // ////////// //
278 278 // Test cases //
279 279 // ////////// //
280 280
281 281 // Valid files
282 282 QTest::newRow("Valid file")
283 283 << QStringLiteral("ValidVector1.txt")
284 284 << ExpectedResults<VectorSeries>{
285 285 Unit{QStringLiteral("nT"), true}, Unit{},
286 286 QVector<QDateTime>{dateTime(2013, 7, 2, 9, 13, 50), dateTime(2013, 7, 2, 9, 14, 6),
287 287 dateTime(2013, 7, 2, 9, 14, 22), dateTime(2013, 7, 2, 9, 14, 38),
288 288 dateTime(2013, 7, 2, 9, 14, 54), dateTime(2013, 7, 2, 9, 15, 10),
289 289 dateTime(2013, 7, 2, 9, 15, 26), dateTime(2013, 7, 2, 9, 15, 42),
290 290 dateTime(2013, 7, 2, 9, 15, 58), dateTime(2013, 7, 2, 9, 16, 14)},
291 291 QVector<QVector<double> >{
292 292 {-0.332, -1.011, -1.457, -1.293, -1.217, -1.443, -1.278, -1.202, -1.22, -1.259},
293 293 {3.206, 2.999, 2.785, 2.736, 2.612, 2.564, 2.892, 2.862, 2.859, 2.764},
294 294 {0.058, 0.496, 1.018, 1.485, 1.662, 1.505, 1.168, 1.244, 1.15, 1.358}}};
295 295
296 296 // Valid files but with some invalid lines (wrong unit, wrong values, etc.)
297 297 QTest::newRow("Invalid file type (scalar)")
298 298 << QStringLiteral("ValidScalar1.txt")
299 299 << ExpectedResults<VectorSeries>{Unit{QStringLiteral("nT"), true}, Unit{},
300 300 QVector<QDateTime>{},
301 301 QVector<QVector<double> >{{}, {}, {}}};
302 302 }
303 303
304 304 void TestAmdaResultParser::testReadVectorTxt()
305 305 {
306 306 testRead<VectorSeries>(AmdaResultParser::ValueType::VECTOR);
307 307 }
308 308
309 309 QTEST_MAIN(TestAmdaResultParser)
310 310 #include "TestAmdaResultParser.moc"
General Comments 0
You need to be logged in to leave comments. Login now