##// END OF EJS Templates
Merge pull request 312 from SciQLop-fork develop...
perrinel -
r836:764a72d1e238 merge
parent child
Show More
@@ -1,28 +1,26
1 1 #ifndef SCIQLOP_VARIABLEREQUEST_H
2 2 #define SCIQLOP_VARIABLEREQUEST_H
3 3
4 4 #include <QObject>
5 5
6 6 #include <QUuid>
7 7
8 8 #include <Common/MetaTypes.h>
9 9 #include <Data/IDataSeries.h>
10 10 #include <Data/SqpRange.h>
11 11
12 12 #include <memory>
13 13
14 14 /**
15 15 * @brief The VariableRequest struct holds the information of an acquisition request
16 16 */
17 17 struct VariableRequest {
18 VariableRequest() { m_CanUpdate = false; }
19
18 QUuid m_VariableGroupId;
20 19 SqpRange m_RangeRequested;
21 20 SqpRange m_CacheRangeRequested;
22 21 std::shared_ptr<IDataSeries> m_DataSeries;
23 bool m_CanUpdate;
24 22 };
25 23
26 24 SCIQLOP_REGISTER_META_TYPE(VARIABLEREQUEST_REGISTRY, VariableRequest)
27 25
28 26 #endif // SCIQLOP_VARIABLEREQUEST_H
@@ -1,416 +1,416
1 1 #include "Variable/VariableAcquisitionWorker.h"
2 2
3 3 #include "Variable/Variable.h"
4 4
5 5 #include <Data/AcquisitionRequest.h>
6 6 #include <Data/SqpRange.h>
7 7
8 8 #include <unordered_map>
9 9 #include <utility>
10 10
11 11 #include <QMutex>
12 12 #include <QReadWriteLock>
13 13 #include <QThread>
14 14
15 15 #include <cmath>
16 16
17 17 Q_LOGGING_CATEGORY(LOG_VariableAcquisitionWorker, "VariableAcquisitionWorker")
18 18
19 19 struct VariableAcquisitionWorker::VariableAcquisitionWorkerPrivate {
20 20
21 21 explicit VariableAcquisitionWorkerPrivate(VariableAcquisitionWorker *parent)
22 22 : m_Lock{QReadWriteLock::Recursive}, q{parent}
23 23 {
24 24 }
25 25
26 26 void lockRead() { m_Lock.lockForRead(); }
27 27 void lockWrite() { m_Lock.lockForWrite(); }
28 28 void unlock() { m_Lock.unlock(); }
29 29
30 30 void removeVariableRequest(QUuid vIdentifier);
31 31
32 32 /// Remove the current request and execute the next one if exist
33 33 void updateToNextRequest(QUuid vIdentifier);
34 34
35 35 /// Remove and/or abort all AcqRequest in link with varRequestId
36 36 void cancelVarRequest(QUuid varRequestId);
37 37 void removeAcqRequest(QUuid acqRequestId);
38 38
39 39 QMutex m_WorkingMutex;
40 40 QReadWriteLock m_Lock;
41 41
42 42 std::map<QUuid, QVector<AcquisitionDataPacket> > m_AcqIdentifierToAcqDataPacketVectorMap;
43 43 std::map<QUuid, AcquisitionRequest> m_AcqIdentifierToAcqRequestMap;
44 44 std::map<QUuid, std::pair<QUuid, QUuid> > m_VIdentifierToCurrrentAcqIdNextIdPairMap;
45 45 VariableAcquisitionWorker *q;
46 46 };
47 47
48 48
49 49 VariableAcquisitionWorker::VariableAcquisitionWorker(QObject *parent)
50 50 : QObject{parent}, impl{spimpl::make_unique_impl<VariableAcquisitionWorkerPrivate>(this)}
51 51 {
52 52 }
53 53
54 54 VariableAcquisitionWorker::~VariableAcquisitionWorker()
55 55 {
56 56 qCInfo(LOG_VariableAcquisitionWorker()) << tr("VariableAcquisitionWorker destruction")
57 57 << QThread::currentThread();
58 58 this->waitForFinish();
59 59 }
60 60
61 61
62 62 QUuid VariableAcquisitionWorker::pushVariableRequest(QUuid varRequestId, QUuid vIdentifier,
63 63 SqpRange rangeRequested,
64 64 SqpRange cacheRangeRequested,
65 65 DataProviderParameters parameters,
66 66 std::shared_ptr<IDataProvider> provider)
67 67 {
68 68 qCDebug(LOG_VariableAcquisitionWorker())
69 69 << tr("TORM VariableAcquisitionWorker::pushVariableRequest ") << cacheRangeRequested;
70 70 auto varRequestIdCanceled = QUuid();
71 71
72 72 // Request creation
73 73 auto acqRequest = AcquisitionRequest{};
74 74 qCDebug(LOG_VariableAcquisitionWorker()) << tr("PushVariableRequest ") << vIdentifier
75 75 << varRequestId;
76 76 acqRequest.m_VarRequestId = varRequestId;
77 77 acqRequest.m_vIdentifier = vIdentifier;
78 78 acqRequest.m_DataProviderParameters = parameters;
79 79 acqRequest.m_RangeRequested = rangeRequested;
80 80 acqRequest.m_CacheRangeRequested = cacheRangeRequested;
81 81 acqRequest.m_Size = parameters.m_Times.size();
82 82 acqRequest.m_Provider = provider;
83 83
84 84
85 85 // Register request
86 86 impl->lockWrite();
87 87 impl->m_AcqIdentifierToAcqRequestMap.insert(
88 88 std::make_pair(acqRequest.m_AcqIdentifier, acqRequest));
89 89
90 90 auto it = impl->m_VIdentifierToCurrrentAcqIdNextIdPairMap.find(vIdentifier);
91 91 if (it != impl->m_VIdentifierToCurrrentAcqIdNextIdPairMap.cend()) {
92 92 // A current request already exists, we can replace the next one
93 93 auto oldAcqId = it->second.second;
94 94 auto acqIdentifierToAcqRequestMapIt = impl->m_AcqIdentifierToAcqRequestMap.find(oldAcqId);
95 95 if (acqIdentifierToAcqRequestMapIt != impl->m_AcqIdentifierToAcqRequestMap.cend()) {
96 96 auto oldAcqRequest = acqIdentifierToAcqRequestMapIt->second;
97 97 varRequestIdCanceled = oldAcqRequest.m_VarRequestId;
98 98 }
99 99
100 100 it->second.second = acqRequest.m_AcqIdentifier;
101 101 impl->unlock();
102 102
103 103 // remove old acqIdentifier from the worker
104 104 impl->cancelVarRequest(varRequestIdCanceled);
105 105 // impl->m_AcqIdentifierToAcqRequestMap.erase(oldAcqId);
106 106 }
107 107 else {
108 108 // First request for the variable, it must be stored and executed
109 109 impl->m_VIdentifierToCurrrentAcqIdNextIdPairMap.insert(
110 110 std::make_pair(vIdentifier, std::make_pair(acqRequest.m_AcqIdentifier, QUuid())));
111 111 impl->unlock();
112 112
113 113 QMetaObject::invokeMethod(this, "onExecuteRequest", Qt::QueuedConnection,
114 114 Q_ARG(QUuid, acqRequest.m_AcqIdentifier));
115 115 }
116 116
117 117 return varRequestIdCanceled;
118 118 }
119 119
120 120 void VariableAcquisitionWorker::abortProgressRequested(QUuid vIdentifier)
121 121 {
122 122 impl->lockRead();
123 123
124 124 auto it = impl->m_VIdentifierToCurrrentAcqIdNextIdPairMap.find(vIdentifier);
125 125 if (it != impl->m_VIdentifierToCurrrentAcqIdNextIdPairMap.cend()) {
126 126 auto currentAcqId = it->second.first;
127 127
128 128 auto it = impl->m_AcqIdentifierToAcqRequestMap.find(currentAcqId);
129 129 if (it != impl->m_AcqIdentifierToAcqRequestMap.cend()) {
130 130 auto request = it->second;
131 131 impl->unlock();
132 132
133 133 // Remove the current request from the worker
134 134 impl->updateToNextRequest(vIdentifier);
135 135
136 136 // notify the request aborting to the provider
137 137 request.m_Provider->requestDataAborting(currentAcqId);
138 138 }
139 139 else {
140 140 impl->unlock();
141 141 qCWarning(LOG_VariableAcquisitionWorker())
142 142 << tr("Impossible to abort an unknown acquisition request") << currentAcqId;
143 143 }
144 144 }
145 145 else {
146 146 impl->unlock();
147 147 }
148 148 }
149 149
150 150 void VariableAcquisitionWorker::onVariableRetrieveDataInProgress(QUuid acqIdentifier,
151 151 double progress)
152 152 {
153 153 qCDebug(LOG_VariableAcquisitionWorker()) << tr("TORM: onVariableRetrieveDataInProgress ")
154 154 << acqIdentifier << progress;
155 155 impl->lockRead();
156 156 auto aIdToARit = impl->m_AcqIdentifierToAcqRequestMap.find(acqIdentifier);
157 157 if (aIdToARit != impl->m_AcqIdentifierToAcqRequestMap.cend()) {
158 158 auto currentPartSize = (aIdToARit->second.m_Size != 0) ? 100 / aIdToARit->second.m_Size : 0;
159 159
160 160 auto currentPartProgress
161 161 = std::isnan(progress) ? 0.0 : (progress * currentPartSize) / 100.0;
162 162 auto currentAlreadyProgress = aIdToARit->second.m_Progression * currentPartSize;
163 163
164 164 auto finalProgression = currentAlreadyProgress + currentPartProgress;
165 165 emit variableRequestInProgress(aIdToARit->second.m_vIdentifier, finalProgression);
166 166 qCDebug(LOG_VariableAcquisitionWorker())
167 167 << tr("TORM: onVariableRetrieveDataInProgress ")
168 168 << QThread::currentThread()->objectName() << aIdToARit->second.m_vIdentifier
169 169 << currentPartSize << currentAlreadyProgress << currentPartProgress << finalProgression;
170 170 if (finalProgression == 100.0) {
171 171 emit variableRequestInProgress(aIdToARit->second.m_vIdentifier, 0.0);
172 172 }
173 173 }
174 174 impl->unlock();
175 175 }
176 176
177 177 void VariableAcquisitionWorker::onVariableAcquisitionFailed(QUuid acqIdentifier)
178 178 {
179 179 qCDebug(LOG_VariableAcquisitionWorker()) << tr("onVariableAcquisitionFailed")
180 180 << QThread::currentThread();
181 181 impl->lockRead();
182 182 auto it = impl->m_AcqIdentifierToAcqRequestMap.find(acqIdentifier);
183 183 if (it != impl->m_AcqIdentifierToAcqRequestMap.cend()) {
184 184 auto request = it->second;
185 185 impl->unlock();
186 qCInfo(LOG_VariableAcquisitionWorker()) << tr("onVariableAcquisitionFailed")
187 << acqIdentifier << request.m_vIdentifier
188 << QThread::currentThread();
186 qCDebug(LOG_VariableAcquisitionWorker()) << tr("onVariableAcquisitionFailed")
187 << acqIdentifier << request.m_vIdentifier
188 << QThread::currentThread();
189 189 emit variableCanceledRequested(request.m_vIdentifier);
190 190 }
191 191 else {
192 192 impl->unlock();
193 193 // TODO log no acqIdentifier recognized
194 194 }
195 195 }
196 196
197 197 void VariableAcquisitionWorker::onVariableDataAcquired(QUuid acqIdentifier,
198 198 std::shared_ptr<IDataSeries> dataSeries,
199 199 SqpRange dataRangeAcquired)
200 200 {
201 201 qCDebug(LOG_VariableAcquisitionWorker()) << tr("TORM: onVariableDataAcquired on range ")
202 202 << acqIdentifier << dataRangeAcquired;
203 203 impl->lockWrite();
204 204 auto aIdToARit = impl->m_AcqIdentifierToAcqRequestMap.find(acqIdentifier);
205 205 if (aIdToARit != impl->m_AcqIdentifierToAcqRequestMap.cend()) {
206 206 // Store the result
207 207 auto dataPacket = AcquisitionDataPacket{};
208 208 dataPacket.m_Range = dataRangeAcquired;
209 209 dataPacket.m_DateSeries = dataSeries;
210 210
211 211 auto aIdToADPVit = impl->m_AcqIdentifierToAcqDataPacketVectorMap.find(acqIdentifier);
212 212 if (aIdToADPVit != impl->m_AcqIdentifierToAcqDataPacketVectorMap.cend()) {
213 213 // A current request result already exists, we can update it
214 214 aIdToADPVit->second.push_back(dataPacket);
215 215 }
216 216 else {
217 217 // First request result for the variable, it must be stored
218 218 impl->m_AcqIdentifierToAcqDataPacketVectorMap.insert(
219 219 std::make_pair(acqIdentifier, QVector<AcquisitionDataPacket>() << dataPacket));
220 220 }
221 221
222 222
223 223 // Decrement the counter of the request
224 224 auto &acqRequest = aIdToARit->second;
225 225 acqRequest.m_Progression = acqRequest.m_Progression + 1;
226 226
227 227 // if the counter is 0, we can return data then run the next request if it exists and
228 228 // removed the finished request
229 229 if (acqRequest.m_Size == acqRequest.m_Progression) {
230 230 auto varId = acqRequest.m_vIdentifier;
231 231 auto rangeRequested = acqRequest.m_RangeRequested;
232 232 auto cacheRangeRequested = acqRequest.m_CacheRangeRequested;
233 233 // Return the data
234 234 aIdToADPVit = impl->m_AcqIdentifierToAcqDataPacketVectorMap.find(acqIdentifier);
235 235 if (aIdToADPVit != impl->m_AcqIdentifierToAcqDataPacketVectorMap.cend()) {
236 236 emit dataProvided(varId, rangeRequested, cacheRangeRequested, aIdToADPVit->second);
237 237 }
238 238 impl->unlock();
239 239
240 240 // Update to the next request
241 241 impl->updateToNextRequest(acqRequest.m_vIdentifier);
242 242 }
243 243 else {
244 244 impl->unlock();
245 245 }
246 246 }
247 247 else {
248 248 impl->unlock();
249 249 qCWarning(LOG_VariableAcquisitionWorker())
250 250 << tr("Impossible to retrieve AcquisitionRequest for the incoming data.");
251 251 }
252 252 }
253 253
254 254 void VariableAcquisitionWorker::onExecuteRequest(QUuid acqIdentifier)
255 255 {
256 256 qCDebug(LOG_VariableAcquisitionWorker()) << tr("onExecuteRequest") << QThread::currentThread();
257 257 impl->lockRead();
258 258 auto it = impl->m_AcqIdentifierToAcqRequestMap.find(acqIdentifier);
259 259 if (it != impl->m_AcqIdentifierToAcqRequestMap.cend()) {
260 260 auto request = it->second;
261 261 impl->unlock();
262 262 emit variableRequestInProgress(request.m_vIdentifier, 0.1);
263 263 request.m_Provider->requestDataLoading(acqIdentifier, request.m_DataProviderParameters);
264 264 }
265 265 else {
266 266 impl->unlock();
267 267 // TODO log no acqIdentifier recognized
268 268 }
269 269 }
270 270
271 271 void VariableAcquisitionWorker::initialize()
272 272 {
273 273 qCDebug(LOG_VariableAcquisitionWorker()) << tr("VariableAcquisitionWorker init")
274 274 << QThread::currentThread();
275 275 impl->m_WorkingMutex.lock();
276 276 qCDebug(LOG_VariableAcquisitionWorker()) << tr("VariableAcquisitionWorker init END");
277 277 }
278 278
279 279 void VariableAcquisitionWorker::finalize()
280 280 {
281 281 impl->m_WorkingMutex.unlock();
282 282 }
283 283
284 284 void VariableAcquisitionWorker::waitForFinish()
285 285 {
286 286 QMutexLocker locker{&impl->m_WorkingMutex};
287 287 }
288 288
289 289 void VariableAcquisitionWorker::VariableAcquisitionWorkerPrivate::removeVariableRequest(
290 290 QUuid vIdentifier)
291 291 {
292 292 lockWrite();
293 293 auto it = m_VIdentifierToCurrrentAcqIdNextIdPairMap.find(vIdentifier);
294 294
295 295 if (it != m_VIdentifierToCurrrentAcqIdNextIdPairMap.cend()) {
296 296 // A current request already exists, we can replace the next one
297 297
298 298 m_AcqIdentifierToAcqRequestMap.erase(it->second.first);
299 299 m_AcqIdentifierToAcqDataPacketVectorMap.erase(it->second.first);
300 300
301 301 m_AcqIdentifierToAcqRequestMap.erase(it->second.second);
302 302 m_AcqIdentifierToAcqDataPacketVectorMap.erase(it->second.second);
303 303 }
304 304 m_VIdentifierToCurrrentAcqIdNextIdPairMap.erase(vIdentifier);
305 305 unlock();
306 306 }
307 307
308 308 void VariableAcquisitionWorker::VariableAcquisitionWorkerPrivate::updateToNextRequest(
309 309 QUuid vIdentifier)
310 310 {
311 311 lockRead();
312 312 auto it = m_VIdentifierToCurrrentAcqIdNextIdPairMap.find(vIdentifier);
313 313 if (it != m_VIdentifierToCurrrentAcqIdNextIdPairMap.cend()) {
314 314 if (it->second.second.isNull()) {
315 315 unlock();
316 316 // There is no next request, we can remove the variable request
317 317 removeVariableRequest(vIdentifier);
318 318 }
319 319 else {
320 320 auto acqIdentifierToRemove = it->second.first;
321 321 // Move the next request to the current request
322 322 auto nextRequestId = it->second.second;
323 323 it->second.first = nextRequestId;
324 324 it->second.second = QUuid();
325 325 unlock();
326 326 // Remove AcquisitionRequest and results;
327 327 lockWrite();
328 328 m_AcqIdentifierToAcqRequestMap.erase(acqIdentifierToRemove);
329 329 m_AcqIdentifierToAcqDataPacketVectorMap.erase(acqIdentifierToRemove);
330 330 unlock();
331 331 // Execute the current request
332 332 QMetaObject::invokeMethod(q, "onExecuteRequest", Qt::QueuedConnection,
333 333 Q_ARG(QUuid, nextRequestId));
334 334 }
335 335 }
336 336 else {
337 337 unlock();
338 338 qCCritical(LOG_VariableAcquisitionWorker())
339 339 << tr("Impossible to execute the acquisition on an unfound variable ");
340 340 }
341 341 }
342 342
343 343 void VariableAcquisitionWorker::VariableAcquisitionWorkerPrivate::cancelVarRequest(
344 344 QUuid varRequestId)
345 345 {
346 346 qCDebug(LOG_VariableAcquisitionWorker())
347 347 << "VariableAcquisitionWorkerPrivate::cancelVarRequest 0";
348 348 lockRead();
349 349 // get all AcqIdentifier in link with varRequestId
350 350 QVector<QUuid> acqIdsToRm;
351 351 auto cend = m_AcqIdentifierToAcqRequestMap.cend();
352 352 for (auto it = m_AcqIdentifierToAcqRequestMap.cbegin(); it != cend; ++it) {
353 353 if (it->second.m_VarRequestId == varRequestId) {
354 354 acqIdsToRm << it->first;
355 355 }
356 356 }
357 357 unlock();
358 358 // run aborting or removing of acqIdsToRm
359 359
360 360 for (auto acqId : acqIdsToRm) {
361 361 removeAcqRequest(acqId);
362 362 }
363 363 qCDebug(LOG_VariableAcquisitionWorker())
364 364 << "VariableAcquisitionWorkerPrivate::cancelVarRequest end";
365 365 }
366 366
367 367 void VariableAcquisitionWorker::VariableAcquisitionWorkerPrivate::removeAcqRequest(
368 368 QUuid acqRequestId)
369 369 {
370 370 qCDebug(LOG_VariableAcquisitionWorker())
371 371 << "VariableAcquisitionWorkerPrivate::removeAcqRequest";
372 372 QUuid vIdentifier;
373 373 std::shared_ptr<IDataProvider> provider;
374 374 lockRead();
375 375 auto acqIt = m_AcqIdentifierToAcqRequestMap.find(acqRequestId);
376 376 if (acqIt != m_AcqIdentifierToAcqRequestMap.cend()) {
377 377 vIdentifier = acqIt->second.m_vIdentifier;
378 378 provider = acqIt->second.m_Provider;
379 379
380 380 auto it = m_VIdentifierToCurrrentAcqIdNextIdPairMap.find(vIdentifier);
381 381 if (it != m_VIdentifierToCurrrentAcqIdNextIdPairMap.cend()) {
382 382 if (it->second.first == acqRequestId) {
383 383 // acqRequest is currently running -> let's aborting it
384 384 unlock();
385 385
386 386 // Remove the current request from the worker
387 387 updateToNextRequest(vIdentifier);
388 388
389 389 // notify the request aborting to the provider
390 390 provider->requestDataAborting(acqRequestId);
391 391 }
392 392 else if (it->second.second == acqRequestId) {
393 393 it->second.second = QUuid();
394 394 unlock();
395 395 }
396 396 else {
397 397 unlock();
398 398 }
399 399 }
400 400 else {
401 401 unlock();
402 402 }
403 403 }
404 404 else {
405 405 unlock();
406 406 }
407 407
408 408 lockWrite();
409 409
410 410 m_AcqIdentifierToAcqDataPacketVectorMap.erase(acqRequestId);
411 411 m_AcqIdentifierToAcqRequestMap.erase(acqRequestId);
412 412
413 413 unlock();
414 414 qCDebug(LOG_VariableAcquisitionWorker())
415 415 << "VariableAcquisitionWorkerPrivate::removeAcqRequest END";
416 416 }
This diff has been collapsed as it changes many lines, (677 lines changed) Show them Hide them
@@ -1,889 +1,1012
1 1 #include <Variable/Variable.h>
2 2 #include <Variable/VariableAcquisitionWorker.h>
3 3 #include <Variable/VariableCacheStrategy.h>
4 4 #include <Variable/VariableCacheStrategyFactory.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 <Data/VariableRequest.h>
13 13 #include <Time/TimeController.h>
14 14
15 15 #include <QMutex>
16 16 #include <QThread>
17 17 #include <QUuid>
18 18 #include <QtCore/QItemSelectionModel>
19 19
20 20 #include <deque>
21 21 #include <set>
22 22 #include <unordered_map>
23 23
24 24 Q_LOGGING_CATEGORY(LOG_VariableController, "VariableController")
25 25
26 26 namespace {
27 27
28 28 SqpRange computeSynchroRangeRequested(const SqpRange &varRange, const SqpRange &graphRange,
29 29 const SqpRange &oldGraphRange)
30 30 {
31 31 auto zoomType = VariableController::getZoomType(graphRange, oldGraphRange);
32 32
33 33 auto varRangeRequested = varRange;
34 34 switch (zoomType) {
35 35 case AcquisitionZoomType::ZoomIn: {
36 36 auto deltaLeft = graphRange.m_TStart - oldGraphRange.m_TStart;
37 37 auto deltaRight = oldGraphRange.m_TEnd - graphRange.m_TEnd;
38 38 varRangeRequested.m_TStart += deltaLeft;
39 39 varRangeRequested.m_TEnd -= deltaRight;
40 40 break;
41 41 }
42 42
43 43 case AcquisitionZoomType::ZoomOut: {
44 44 auto deltaLeft = oldGraphRange.m_TStart - graphRange.m_TStart;
45 45 auto deltaRight = graphRange.m_TEnd - oldGraphRange.m_TEnd;
46 46 varRangeRequested.m_TStart -= deltaLeft;
47 47 varRangeRequested.m_TEnd += deltaRight;
48 48 break;
49 49 }
50 50 case AcquisitionZoomType::PanRight: {
51 51 auto deltaLeft = graphRange.m_TStart - oldGraphRange.m_TStart;
52 52 auto deltaRight = graphRange.m_TEnd - oldGraphRange.m_TEnd;
53 53 varRangeRequested.m_TStart += deltaLeft;
54 54 varRangeRequested.m_TEnd += deltaRight;
55 55 break;
56 56 }
57 57 case AcquisitionZoomType::PanLeft: {
58 58 auto deltaLeft = oldGraphRange.m_TStart - graphRange.m_TStart;
59 59 auto deltaRight = oldGraphRange.m_TEnd - graphRange.m_TEnd;
60 60 varRangeRequested.m_TStart -= deltaLeft;
61 61 varRangeRequested.m_TEnd -= deltaRight;
62 62 break;
63 63 }
64 64 case AcquisitionZoomType::Unknown: {
65 65 qCCritical(LOG_VariableController())
66 66 << VariableController::tr("Impossible to synchronize: zoom type unknown");
67 67 break;
68 68 }
69 69 default:
70 70 qCCritical(LOG_VariableController()) << VariableController::tr(
71 71 "Impossible to synchronize: zoom type not take into account");
72 72 // No action
73 73 break;
74 74 }
75 75
76 76 return varRangeRequested;
77 77 }
78 78 }
79 79
80 enum class VariableRequestHandlerState { OFF, RUNNING, PENDING };
81
82 struct VariableRequestHandler {
83
84 VariableRequestHandler()
85 {
86 m_CanUpdate = false;
87 m_State = VariableRequestHandlerState::OFF;
88 }
89
90 QUuid m_VarId;
91 VariableRequest m_RunningVarRequest;
92 VariableRequest m_PendingVarRequest;
93 VariableRequestHandlerState m_State;
94 bool m_CanUpdate;
95 };
96
80 97 struct VariableController::VariableControllerPrivate {
81 98 explicit VariableControllerPrivate(VariableController *parent)
82 99 : m_WorkingMutex{},
83 100 m_VariableModel{new VariableModel{parent}},
84 101 m_VariableSelectionModel{new QItemSelectionModel{m_VariableModel, parent}},
85 102 // m_VariableCacheStrategy{std::make_unique<VariableCacheStrategy>()},
86 103 m_VariableCacheStrategy{VariableCacheStrategyFactory::createCacheStrategy(
87 104 CacheStrategy::SingleThreshold)},
88 105 m_VariableAcquisitionWorker{std::make_unique<VariableAcquisitionWorker>()},
89 106 q{parent}
90 107 {
91 108
92 109 m_VariableAcquisitionWorker->moveToThread(&m_VariableAcquisitionWorkerThread);
93 110 m_VariableAcquisitionWorkerThread.setObjectName("VariableAcquisitionWorkerThread");
94 111 }
95 112
96 113
97 114 virtual ~VariableControllerPrivate()
98 115 {
99 116 qCDebug(LOG_VariableController()) << tr("VariableControllerPrivate destruction");
100 117 m_VariableAcquisitionWorkerThread.quit();
101 118 m_VariableAcquisitionWorkerThread.wait();
102 119 }
103 120
104 121
105 122 void processRequest(std::shared_ptr<Variable> var, const SqpRange &rangeRequested,
106 123 QUuid varRequestId);
107 124
108 125 std::shared_ptr<Variable> findVariable(QUuid vIdentifier);
109 126 std::shared_ptr<IDataSeries>
110 127 retrieveDataSeries(const QVector<AcquisitionDataPacket> acqDataPacketVector);
111 128
112 129 void registerProvider(std::shared_ptr<IDataProvider> provider);
113 130
114 131 void storeVariableRequest(QUuid varId, QUuid varRequestId, const VariableRequest &varRequest);
115 132 QUuid acceptVariableRequest(QUuid varId, std::shared_ptr<IDataSeries> dataSeries);
133 void updateVariables(QUuid varRequestId);
116 134 void updateVariableRequest(QUuid varRequestId);
117 135 void cancelVariableRequest(QUuid varRequestId);
118
119 SqpRange getLastRequestedRange(QUuid varId);
136 void executeVarRequest(std::shared_ptr<Variable> var, VariableRequest &varRequest);
120 137
121 138 QMutex m_WorkingMutex;
122 139 /// Variable model. The VariableController has the ownership
123 140 VariableModel *m_VariableModel;
124 141 QItemSelectionModel *m_VariableSelectionModel;
125 142
126 143
127 144 TimeController *m_TimeController{nullptr};
128 145 std::unique_ptr<VariableCacheStrategy> m_VariableCacheStrategy;
129 146 std::unique_ptr<VariableAcquisitionWorker> m_VariableAcquisitionWorker;
130 147 QThread m_VariableAcquisitionWorkerThread;
131 148
132 149 std::unordered_map<std::shared_ptr<Variable>, std::shared_ptr<IDataProvider> >
133 150 m_VariableToProviderMap;
134 151 std::unordered_map<std::shared_ptr<Variable>, QUuid> m_VariableToIdentifierMap;
135 152 std::map<QUuid, std::shared_ptr<VariableSynchronizationGroup> >
136 153 m_GroupIdToVariableSynchronizationGroupMap;
137 154 std::map<QUuid, QUuid> m_VariableIdGroupIdMap;
138 155 std::set<std::shared_ptr<IDataProvider> > m_ProviderSet;
139 156
140 std::map<QUuid, std::map<QUuid, VariableRequest> > m_VarRequestIdToVarIdVarRequestMap;
141
142 std::map<QUuid, std::deque<QUuid> > m_VarIdToVarRequestIdQueueMap;
143
157 std::map<QUuid, std::list<QUuid> > m_VarGroupIdToVarIds;
158 std::map<QUuid, std::unique_ptr<VariableRequestHandler> > m_VarIdToVarRequestHandler;
144 159
145 160 VariableController *q;
146 161 };
147 162
148 163
149 164 VariableController::VariableController(QObject *parent)
150 165 : QObject{parent}, impl{spimpl::make_unique_impl<VariableControllerPrivate>(this)}
151 166 {
152 167 qCDebug(LOG_VariableController()) << tr("VariableController construction")
153 168 << QThread::currentThread();
154 169
155 170 connect(impl->m_VariableModel, &VariableModel::abortProgessRequested, this,
156 171 &VariableController::onAbortProgressRequested);
157 172
158 173 connect(impl->m_VariableAcquisitionWorker.get(),
159 174 &VariableAcquisitionWorker::variableCanceledRequested, this,
160 175 &VariableController::onAbortAcquisitionRequested);
161 176
162 177 connect(impl->m_VariableAcquisitionWorker.get(), &VariableAcquisitionWorker::dataProvided, this,
163 178 &VariableController::onDataProvided);
164 179 connect(impl->m_VariableAcquisitionWorker.get(),
165 180 &VariableAcquisitionWorker::variableRequestInProgress, this,
166 181 &VariableController::onVariableRetrieveDataInProgress);
167 182
168 183
169 184 connect(&impl->m_VariableAcquisitionWorkerThread, &QThread::started,
170 185 impl->m_VariableAcquisitionWorker.get(), &VariableAcquisitionWorker::initialize);
171 186 connect(&impl->m_VariableAcquisitionWorkerThread, &QThread::finished,
172 187 impl->m_VariableAcquisitionWorker.get(), &VariableAcquisitionWorker::finalize);
173 188
174 189
175 190 impl->m_VariableAcquisitionWorkerThread.start();
176 191 }
177 192
178 193 VariableController::~VariableController()
179 194 {
180 195 qCDebug(LOG_VariableController()) << tr("VariableController destruction")
181 196 << QThread::currentThread();
182 197 this->waitForFinish();
183 198 }
184 199
185 200 VariableModel *VariableController::variableModel() noexcept
186 201 {
187 202 return impl->m_VariableModel;
188 203 }
189 204
190 205 QItemSelectionModel *VariableController::variableSelectionModel() noexcept
191 206 {
192 207 return impl->m_VariableSelectionModel;
193 208 }
194 209
195 210 void VariableController::setTimeController(TimeController *timeController) noexcept
196 211 {
197 212 impl->m_TimeController = timeController;
198 213 }
199 214
200 215 std::shared_ptr<Variable>
201 216 VariableController::cloneVariable(std::shared_ptr<Variable> variable) noexcept
202 217 {
203 218 if (impl->m_VariableModel->containsVariable(variable)) {
204 219 // Clones variable
205 220 auto duplicate = variable->clone();
206 221
207 222 // Adds clone to model
208 223 impl->m_VariableModel->addVariable(duplicate);
209 224
210 225 // Generates clone identifier
211 226 impl->m_VariableToIdentifierMap[duplicate] = QUuid::createUuid();
212 227
213 228 // Registers provider
214 229 auto variableProvider = impl->m_VariableToProviderMap.at(variable);
215 230 auto duplicateProvider = variableProvider != nullptr ? variableProvider->clone() : nullptr;
216 231
217 232 impl->m_VariableToProviderMap[duplicate] = duplicateProvider;
218 233 if (duplicateProvider) {
219 234 impl->registerProvider(duplicateProvider);
220 235 }
221 236
222 237 return duplicate;
223 238 }
224 239 else {
225 240 qCCritical(LOG_VariableController())
226 241 << tr("Can't create duplicate of variable %1: variable not registered in the model")
227 242 .arg(variable->name());
228 243 return nullptr;
229 244 }
230 245 }
231 246
232 247 void VariableController::deleteVariable(std::shared_ptr<Variable> variable) noexcept
233 248 {
234 249 if (!variable) {
235 250 qCCritical(LOG_VariableController()) << "Can't delete variable: variable is null";
236 251 return;
237 252 }
238 253
239 254 // Spreads in SciQlop that the variable will be deleted, so that potential receivers can
240 255 // make some treatments before the deletion
241 256 emit variableAboutToBeDeleted(variable);
242 257
243 258 // Deletes identifier
244 259 impl->m_VariableToIdentifierMap.erase(variable);
245 260
246 261 // Deletes provider
247 262 auto nbProvidersDeleted = impl->m_VariableToProviderMap.erase(variable);
248 263 qCDebug(LOG_VariableController())
249 264 << tr("Number of providers deleted for variable %1: %2")
250 265 .arg(variable->name(), QString::number(nbProvidersDeleted));
251 266
252 267
253 268 // Deletes from model
254 269 impl->m_VariableModel->deleteVariable(variable);
255 270 }
256 271
257 272 void VariableController::deleteVariables(
258 273 const QVector<std::shared_ptr<Variable> > &variables) noexcept
259 274 {
260 275 for (auto variable : qAsConst(variables)) {
261 276 deleteVariable(variable);
262 277 }
263 278 }
264 279
265 280 std::shared_ptr<Variable>
266 281 VariableController::createVariable(const QString &name, const QVariantHash &metadata,
267 282 std::shared_ptr<IDataProvider> provider) noexcept
268 283 {
269 284 if (!impl->m_TimeController) {
270 285 qCCritical(LOG_VariableController())
271 286 << tr("Impossible to create variable: The time controller is null");
272 287 return nullptr;
273 288 }
274 289
275 290 auto range = impl->m_TimeController->dateTime();
276 291
277 292 if (auto newVariable = impl->m_VariableModel->createVariable(name, metadata)) {
278 auto identifier = QUuid::createUuid();
293 auto varId = QUuid::createUuid();
294
295 // Create the handler
296 auto varRequestHandler = std::make_unique<VariableRequestHandler>();
297 varRequestHandler->m_VarId = varId;
298
299 impl->m_VarIdToVarRequestHandler.insert(
300 std::make_pair(varId, std::move(varRequestHandler)));
279 301
280 302 // store the provider
281 303 impl->registerProvider(provider);
282 304
283 305 // Associate the provider
284 306 impl->m_VariableToProviderMap[newVariable] = provider;
285 qCInfo(LOG_VariableController()) << "createVariable: " << identifier;
286 impl->m_VariableToIdentifierMap[newVariable] = identifier;
307 impl->m_VariableToIdentifierMap[newVariable] = varId;
287 308
309 this->onRequestDataLoading(QVector<std::shared_ptr<Variable> >{newVariable}, range, false);
288 310
289 auto varRequestId = QUuid::createUuid();
290 impl->processRequest(newVariable, range, varRequestId);
291 impl->updateVariableRequest(varRequestId);
311 // auto varRequestId = QUuid::createUuid();
312 // qCInfo(LOG_VariableController()) << "createVariable: " << varId << varRequestId;
313 // impl->processRequest(newVariable, range, varRequestId);
314 // impl->updateVariableRequest(varRequestId);
292 315
293 316 return newVariable;
294 317 }
295 318 }
296 319
297 320 void VariableController::onDateTimeOnSelection(const SqpRange &dateTime)
298 321 {
299 322 // NOTE: Even if acquisition request is aborting, the graphe range will be changed
300 323 qCDebug(LOG_VariableController()) << "VariableController::onDateTimeOnSelection"
301 324 << QThread::currentThread()->objectName();
302 325 auto selectedRows = impl->m_VariableSelectionModel->selectedRows();
303 auto variables = QVector<std::shared_ptr<Variable> >{};
304 326
305 for (const auto &selectedRow : qAsConst(selectedRows)) {
306 if (auto selectedVariable = impl->m_VariableModel->variable(selectedRow.row())) {
307 variables << selectedVariable;
327 // NOTE we only permit the time modification for one variable
328 // DEPRECATED
329 // auto variables = QVector<std::shared_ptr<Variable> >{};
330 // for (const auto &selectedRow : qAsConst(selectedRows)) {
331 // if (auto selectedVariable =
332 // impl->m_VariableModel->variable(selectedRow.row())) {
333 // variables << selectedVariable;
334
335 // // notify that rescale operation has to be done
336 // emit rangeChanged(selectedVariable, dateTime);
337 // }
338 // }
339 // if (!variables.isEmpty()) {
340 // this->onRequestDataLoading(variables, dateTime, synchro);
341 // }
342 if (selectedRows.size() == 1) {
343
344 if (auto selectedVariable
345 = impl->m_VariableModel->variable(qAsConst(selectedRows).first().row())) {
346
347 auto itVar = impl->m_VariableToIdentifierMap.find(selectedVariable);
348 if (itVar == impl->m_VariableToIdentifierMap.cend()) {
349 qCCritical(LOG_VariableController())
350 << tr("Impossible to onDateTimeOnSelection request for unknown variable");
351 return;
352 }
308 353
309 354 // notify that rescale operation has to be done
310 355 emit rangeChanged(selectedVariable, dateTime);
356
357 auto synchro = impl->m_VariableIdGroupIdMap.find(itVar->second)
358 != impl->m_VariableIdGroupIdMap.cend();
359
360 this->onRequestDataLoading(QVector<std::shared_ptr<Variable> >{selectedVariable},
361 dateTime, synchro);
311 362 }
312 363 }
313
314 if (!variables.isEmpty()) {
315 this->onRequestDataLoading(variables, dateTime, true);
364 else if (selectedRows.size() > 1) {
365 qCCritical(LOG_VariableController())
366 << tr("Impossible to set time for more than 1 variable in the same time");
367 }
368 else {
369 qCWarning(LOG_VariableController())
370 << tr("There is no variable selected to set the time one");
316 371 }
317 372 }
318 373
319 374 void VariableController::onDataProvided(QUuid vIdentifier, const SqpRange &rangeRequested,
320 375 const SqpRange &cacheRangeRequested,
321 376 QVector<AcquisitionDataPacket> dataAcquired)
322 377 {
378 qCDebug(LOG_VariableController()) << tr("onDataProvided") << QThread::currentThread();
323 379 auto retrievedDataSeries = impl->retrieveDataSeries(dataAcquired);
324 380 auto varRequestId = impl->acceptVariableRequest(vIdentifier, retrievedDataSeries);
325 381 if (!varRequestId.isNull()) {
326 impl->updateVariableRequest(varRequestId);
382 impl->updateVariables(varRequestId);
327 383 }
328 384 }
329 385
330 386 void VariableController::onVariableRetrieveDataInProgress(QUuid identifier, double progress)
331 387 {
332 388 qCDebug(LOG_VariableController())
333 389 << "TORM: variableController::onVariableRetrieveDataInProgress"
334 390 << QThread::currentThread()->objectName() << progress;
335 391 if (auto var = impl->findVariable(identifier)) {
336 392 impl->m_VariableModel->setDataProgress(var, progress);
337 393 }
338 394 else {
339 395 qCCritical(LOG_VariableController())
340 396 << tr("Impossible to notify progression of a null variable");
341 397 }
342 398 }
343 399
344 400 void VariableController::onAbortProgressRequested(std::shared_ptr<Variable> variable)
345 401 {
346 auto it = impl->m_VariableToIdentifierMap.find(variable);
347 if (it != impl->m_VariableToIdentifierMap.cend()) {
348 impl->m_VariableAcquisitionWorker->abortProgressRequested(it->second);
402 qCDebug(LOG_VariableController()) << "TORM: variableController::onAbortProgressRequested"
403 << QThread::currentThread()->objectName() << variable->name();
349 404
350 QUuid varRequestId;
351 auto varIdToVarRequestIdQueueMapIt = impl->m_VarIdToVarRequestIdQueueMap.find(it->second);
352 if (varIdToVarRequestIdQueueMapIt != impl->m_VarIdToVarRequestIdQueueMap.cend()) {
353 auto &varRequestIdQueue = varIdToVarRequestIdQueueMapIt->second;
354 varRequestId = varRequestIdQueue.front();
355 impl->cancelVariableRequest(varRequestId);
405 auto itVar = impl->m_VariableToIdentifierMap.find(variable);
406 if (itVar == impl->m_VariableToIdentifierMap.cend()) {
407 qCCritical(LOG_VariableController())
408 << tr("Impossible to onAbortProgressRequested request for unknown variable");
409 return;
410 }
356 411
357 // Finish the progression for the request
358 impl->m_VariableModel->setDataProgress(variable, 0.0);
359 }
360 else {
361 qCWarning(LOG_VariableController())
362 << tr("Aborting progression of inexistant variable request detected !!!")
363 << QThread::currentThread()->objectName();
364 }
412 auto varId = itVar->second;
413
414 auto itVarHandler = impl->m_VarIdToVarRequestHandler.find(varId);
415 if (itVarHandler == impl->m_VarIdToVarRequestHandler.cend()) {
416 qCCritical(LOG_VariableController())
417 << tr("Impossible to onAbortProgressRequested for variable with unknown handler");
418 return;
365 419 }
366 else {
367 qCWarning(LOG_VariableController())
368 << tr("Aborting progression of inexistant variable detected !!!")
369 << QThread::currentThread()->objectName();
420
421 auto varHandler = itVarHandler->second.get();
422
423 // case where a variable has a running request
424 if (varHandler->m_State != VariableRequestHandlerState::OFF) {
425 impl->cancelVariableRequest(varHandler->m_RunningVarRequest.m_VariableGroupId);
370 426 }
371 427 }
372 428
373 429 void VariableController::onAbortAcquisitionRequested(QUuid vIdentifier)
374 430 {
375 431 qCDebug(LOG_VariableController()) << "TORM: variableController::onAbortAcquisitionRequested"
376 432 << QThread::currentThread()->objectName() << vIdentifier;
377 433
378 434 if (auto var = impl->findVariable(vIdentifier)) {
379 435 this->onAbortProgressRequested(var);
380 436 }
381 437 else {
382 438 qCCritical(LOG_VariableController())
383 439 << tr("Impossible to abort Acquisition Requestof a null variable");
384 440 }
385 441 }
386 442
387 443 void VariableController::onAddSynchronizationGroupId(QUuid synchronizationGroupId)
388 444 {
389 445 qCDebug(LOG_VariableController()) << "TORM: VariableController::onAddSynchronizationGroupId"
390 446 << QThread::currentThread()->objectName()
391 447 << synchronizationGroupId;
392 448 auto vSynchroGroup = std::make_shared<VariableSynchronizationGroup>();
393 449 impl->m_GroupIdToVariableSynchronizationGroupMap.insert(
394 450 std::make_pair(synchronizationGroupId, vSynchroGroup));
395 451 }
396 452
397 453 void VariableController::onRemoveSynchronizationGroupId(QUuid synchronizationGroupId)
398 454 {
399 455 impl->m_GroupIdToVariableSynchronizationGroupMap.erase(synchronizationGroupId);
400 456 }
401 457
402 458 void VariableController::onAddSynchronized(std::shared_ptr<Variable> variable,
403 459 QUuid synchronizationGroupId)
404 460
405 461 {
406 462 qCDebug(LOG_VariableController()) << "TORM: VariableController::onAddSynchronized"
407 463 << synchronizationGroupId;
408 464 auto varToVarIdIt = impl->m_VariableToIdentifierMap.find(variable);
409 465 if (varToVarIdIt != impl->m_VariableToIdentifierMap.cend()) {
410 466 auto groupIdToVSGIt
411 467 = impl->m_GroupIdToVariableSynchronizationGroupMap.find(synchronizationGroupId);
412 468 if (groupIdToVSGIt != impl->m_GroupIdToVariableSynchronizationGroupMap.cend()) {
413 469 impl->m_VariableIdGroupIdMap.insert(
414 470 std::make_pair(varToVarIdIt->second, synchronizationGroupId));
415 471 groupIdToVSGIt->second->addVariableId(varToVarIdIt->second);
416 472 }
417 473 else {
418 474 qCCritical(LOG_VariableController())
419 475 << tr("Impossible to synchronize a variable with an unknown sycnhronization group")
420 476 << variable->name();
421 477 }
422 478 }
423 479 else {
424 480 qCCritical(LOG_VariableController())
425 481 << tr("Impossible to synchronize a variable with no identifier") << variable->name();
426 482 }
427 483 }
428 484
429 485 void VariableController::desynchronize(std::shared_ptr<Variable> variable,
430 486 QUuid synchronizationGroupId)
431 487 {
432 488 // Gets variable id
433 489 auto variableIt = impl->m_VariableToIdentifierMap.find(variable);
434 490 if (variableIt == impl->m_VariableToIdentifierMap.cend()) {
435 491 qCCritical(LOG_VariableController())
436 492 << tr("Can't desynchronize variable %1: variable identifier not found")
437 493 .arg(variable->name());
438 494 return;
439 495 }
440 496
441 497 // Gets synchronization group
442 498 auto groupIt = impl->m_GroupIdToVariableSynchronizationGroupMap.find(synchronizationGroupId);
443 499 if (groupIt == impl->m_GroupIdToVariableSynchronizationGroupMap.cend()) {
444 500 qCCritical(LOG_VariableController())
445 501 << tr("Can't desynchronize variable %1: unknown synchronization group")
446 502 .arg(variable->name());
447 503 return;
448 504 }
449 505
450 506 auto variableId = variableIt->second;
451 507
452 508 // Removes variable from synchronization group
453 509 auto synchronizationGroup = groupIt->second;
454 510 synchronizationGroup->removeVariableId(variableId);
455 511
456 512 // Removes link between variable and synchronization group
457 513 impl->m_VariableIdGroupIdMap.erase(variableId);
458 514 }
459 515
460 516 void VariableController::onRequestDataLoading(QVector<std::shared_ptr<Variable> > variables,
461 517 const SqpRange &range, bool synchronise)
462 518 {
463 // NOTE: oldRange isn't really necessary since oldRange == variable->range().
464
519 // variables is assumed synchronized
520 // TODO: Asser variables synchronization
465 521 // we want to load data of the variable for the dateTime.
466 // First we check if the cache contains some of them.
467 // For the other, we ask the provider to give them.
522 if (variables.isEmpty()) {
523 return;
524 }
468 525
469 526 auto varRequestId = QUuid::createUuid();
470 527 qCDebug(LOG_VariableController()) << "VariableController::onRequestDataLoading"
471 << QThread::currentThread()->objectName() << varRequestId;
472
473 for (const auto &var : variables) {
474 qCDebug(LOG_VariableController()) << "processRequest for" << var->name() << varRequestId;
475 impl->processRequest(var, range, varRequestId);
476 }
528 << QThread::currentThread()->objectName() << varRequestId
529 << range << synchronise;
477 530
478 if (synchronise) {
479 // Get the group ids
480 qCDebug(LOG_VariableController())
481 << "TORM VariableController::onRequestDataLoading for synchro var ENABLE";
482 auto groupIds = std::set<QUuid>{};
483 auto groupIdToOldRangeMap = std::map<QUuid, SqpRange>{};
531 if (!synchronise) {
532 auto varIds = std::list<QUuid>{};
484 533 for (const auto &var : variables) {
485 auto varToVarIdIt = impl->m_VariableToIdentifierMap.find(var);
486 if (varToVarIdIt != impl->m_VariableToIdentifierMap.cend()) {
487 auto vId = varToVarIdIt->second;
488 auto varIdToGroupIdIt = impl->m_VariableIdGroupIdMap.find(vId);
489 if (varIdToGroupIdIt != impl->m_VariableIdGroupIdMap.cend()) {
490 auto gId = varIdToGroupIdIt->second;
491 groupIdToOldRangeMap.insert(std::make_pair(gId, var->range()));
492 if (groupIds.find(gId) == groupIds.cend()) {
493 qCDebug(LOG_VariableController()) << "Synchro detect group " << gId;
494 groupIds.insert(gId);
495 }
496 }
497 }
534 auto vId = impl->m_VariableToIdentifierMap.at(var);
535 varIds.push_back(vId);
536 }
537 impl->m_VarGroupIdToVarIds.insert(std::make_pair(varRequestId, varIds));
538 for (const auto &var : variables) {
539 qCDebug(LOG_VariableController()) << "processRequest for" << var->name() << varRequestId
540 << varIds.size();
541 impl->processRequest(var, range, varRequestId);
498 542 }
543 }
544 else {
545 auto vId = impl->m_VariableToIdentifierMap.at(variables.first());
546 auto varIdToGroupIdIt = impl->m_VariableIdGroupIdMap.find(vId);
547 if (varIdToGroupIdIt != impl->m_VariableIdGroupIdMap.cend()) {
548 auto groupId = varIdToGroupIdIt->second;
499 549
500 // We assume here all group ids exist
501 for (const auto &gId : groupIds) {
502 auto vSynchronizationGroup = impl->m_GroupIdToVariableSynchronizationGroupMap.at(gId);
550 auto vSynchronizationGroup
551 = impl->m_GroupIdToVariableSynchronizationGroupMap.at(groupId);
503 552 auto vSyncIds = vSynchronizationGroup->getIds();
504 qCDebug(LOG_VariableController()) << "Var in synchro group ";
553
554 auto varIds = std::list<QUuid>{};
555 for (auto vId : vSyncIds) {
556 varIds.push_back(vId);
557 }
558 impl->m_VarGroupIdToVarIds.insert(std::make_pair(varRequestId, varIds));
559
505 560 for (auto vId : vSyncIds) {
506 561 auto var = impl->findVariable(vId);
507 562
508 563 // Don't process already processed var
509 if (!variables.contains(var)) {
510 if (var != nullptr) {
511 qCDebug(LOG_VariableController()) << "processRequest synchro for"
512 << var->name();
513 auto vSyncRangeRequested = computeSynchroRangeRequested(
514 var->range(), range, groupIdToOldRangeMap.at(gId));
515 qCDebug(LOG_VariableController()) << "synchro RR" << vSyncRangeRequested;
516 impl->processRequest(var, vSyncRangeRequested, varRequestId);
517 }
518 else {
519 qCCritical(LOG_VariableController())
564 if (var != nullptr) {
565 qCDebug(LOG_VariableController()) << "processRequest synchro for" << var->name()
566 << varRequestId;
567 auto vSyncRangeRequested
568 = variables.contains(var)
569 ? range
570 : computeSynchroRangeRequested(var->range(), range,
571 variables.first()->range());
572 qCDebug(LOG_VariableController()) << "synchro RR" << vSyncRangeRequested;
573 impl->processRequest(var, vSyncRangeRequested, varRequestId);
574 }
575 else {
576 qCCritical(LOG_VariableController())
520 577
521 << tr("Impossible to synchronize a null variable");
522 }
578 << tr("Impossible to synchronize a null variable");
523 579 }
524 580 }
525 581 }
526 582 }
527 583
528 impl->updateVariableRequest(varRequestId);
584 impl->updateVariables(varRequestId);
529 585 }
530 586
531 587
532 588 void VariableController::initialize()
533 589 {
534 590 qCDebug(LOG_VariableController()) << tr("VariableController init") << QThread::currentThread();
535 591 impl->m_WorkingMutex.lock();
536 592 qCDebug(LOG_VariableController()) << tr("VariableController init END");
537 593 }
538 594
539 595 void VariableController::finalize()
540 596 {
541 597 impl->m_WorkingMutex.unlock();
542 598 }
543 599
544 600 void VariableController::waitForFinish()
545 601 {
546 602 QMutexLocker locker{&impl->m_WorkingMutex};
547 603 }
548 604
549 605 AcquisitionZoomType VariableController::getZoomType(const SqpRange &range, const SqpRange &oldRange)
550 606 {
551 607 // t1.m_TStart <= t2.m_TStart && t2.m_TEnd <= t1.m_TEnd
552 608 auto zoomType = AcquisitionZoomType::Unknown;
553 609 if (range.m_TStart <= oldRange.m_TStart && oldRange.m_TEnd <= range.m_TEnd) {
554 610 qCDebug(LOG_VariableController()) << "zoomtype: ZoomOut";
555 611 zoomType = AcquisitionZoomType::ZoomOut;
556 612 }
557 613 else if (range.m_TStart > oldRange.m_TStart && range.m_TEnd > oldRange.m_TEnd) {
558 614 qCDebug(LOG_VariableController()) << "zoomtype: PanRight";
559 615 zoomType = AcquisitionZoomType::PanRight;
560 616 }
561 617 else if (range.m_TStart < oldRange.m_TStart && range.m_TEnd < oldRange.m_TEnd) {
562 618 qCDebug(LOG_VariableController()) << "zoomtype: PanLeft";
563 619 zoomType = AcquisitionZoomType::PanLeft;
564 620 }
565 621 else if (range.m_TStart > oldRange.m_TStart && oldRange.m_TEnd > range.m_TEnd) {
566 622 qCDebug(LOG_VariableController()) << "zoomtype: ZoomIn";
567 623 zoomType = AcquisitionZoomType::ZoomIn;
568 624 }
569 625 else {
570 626 qCDebug(LOG_VariableController()) << "getZoomType: Unknown type detected";
571 627 }
572 628 return zoomType;
573 629 }
574 630
575 631 void VariableController::VariableControllerPrivate::processRequest(std::shared_ptr<Variable> var,
576 632 const SqpRange &rangeRequested,
577 633 QUuid varRequestId)
578 634 {
579 auto varRequest = VariableRequest{};
635 auto itVar = m_VariableToIdentifierMap.find(var);
636 if (itVar == m_VariableToIdentifierMap.cend()) {
637 qCCritical(LOG_VariableController())
638 << tr("Impossible to process request for unknown variable");
639 return;
640 }
580 641
581 auto it = m_VariableToIdentifierMap.find(var);
582 if (it != m_VariableToIdentifierMap.cend()) {
642 auto varId = itVar->second;
583 643
584 auto varId = it->second;
644 auto itVarHandler = m_VarIdToVarRequestHandler.find(varId);
645 if (itVarHandler == m_VarIdToVarRequestHandler.cend()) {
646 qCCritical(LOG_VariableController())
647 << tr("Impossible to process request for variable with unknown handler");
648 return;
649 }
585 650
586 auto oldRange = getLastRequestedRange(varId);
651 auto oldRange = var->range();
587 652
588 // check for update oldRange to the last request range.
589 if (oldRange == INVALID_RANGE) {
590 oldRange = var->range();
591 }
653 auto varHandler = itVarHandler->second.get();
592 654
593 auto varStrategyRangesRequested
594 = m_VariableCacheStrategy->computeRange(oldRange, rangeRequested);
595
596 auto notInCacheRangeList
597 = Variable::provideNotInCacheRangeList(oldRange, varStrategyRangesRequested.second);
598 auto inCacheRangeList
599 = Variable::provideInCacheRangeList(oldRange, varStrategyRangesRequested.second);
600
601 if (!notInCacheRangeList.empty()) {
602 varRequest.m_RangeRequested = varStrategyRangesRequested.first;
603 varRequest.m_CacheRangeRequested = varStrategyRangesRequested.second;
604
605 // store VarRequest
606 storeVariableRequest(varId, varRequestId, varRequest);
607
608 auto varProvider = m_VariableToProviderMap.at(var);
609 if (varProvider != nullptr) {
610 auto varRequestIdCanceled = m_VariableAcquisitionWorker->pushVariableRequest(
611 varRequestId, varId, varStrategyRangesRequested.first,
612 varStrategyRangesRequested.second,
613 DataProviderParameters{std::move(notInCacheRangeList), var->metadata()},
614 varProvider);
615
616 if (!varRequestIdCanceled.isNull()) {
617 qCInfo(LOG_VariableAcquisitionWorker()) << tr("varRequestIdCanceled: ")
618 << varRequestIdCanceled;
619 cancelVariableRequest(varRequestIdCanceled);
620 }
621 }
622 else {
623 qCCritical(LOG_VariableController())
624 << "Impossible to provide data with a null provider";
625 }
655 if (varHandler->m_State != VariableRequestHandlerState::OFF) {
656 oldRange = varHandler->m_RunningVarRequest.m_RangeRequested;
657 }
626 658
627 if (!inCacheRangeList.empty()) {
628 emit q->updateVarDisplaying(var, inCacheRangeList.first());
629 }
659 auto varRequest = VariableRequest{};
660 varRequest.m_VariableGroupId = varRequestId;
661 auto varStrategyRangesRequested
662 = m_VariableCacheStrategy->computeRange(oldRange, rangeRequested);
663 varRequest.m_RangeRequested = varStrategyRangesRequested.first;
664 varRequest.m_CacheRangeRequested = varStrategyRangesRequested.second;
665
666 switch (varHandler->m_State) {
667 case VariableRequestHandlerState::OFF: {
668 qCDebug(LOG_VariableController()) << tr("Process Request OFF")
669 << varRequest.m_RangeRequested
670 << varRequest.m_CacheRangeRequested;
671 varHandler->m_RunningVarRequest = varRequest;
672 varHandler->m_State = VariableRequestHandlerState::RUNNING;
673 executeVarRequest(var, varRequest);
674 break;
630 675 }
631 else {
632 varRequest.m_RangeRequested = varStrategyRangesRequested.first;
633 varRequest.m_CacheRangeRequested = varStrategyRangesRequested.second;
634 // store VarRequest
635 storeVariableRequest(varId, varRequestId, varRequest);
636 acceptVariableRequest(
637 varId, var->dataSeries()->subDataSeries(varStrategyRangesRequested.second));
676 case VariableRequestHandlerState::RUNNING: {
677 qCDebug(LOG_VariableController()) << tr("Process Request RUNNING")
678 << varRequest.m_RangeRequested
679 << varRequest.m_CacheRangeRequested;
680 varHandler->m_State = VariableRequestHandlerState::PENDING;
681 varHandler->m_PendingVarRequest = varRequest;
682 break;
638 683 }
684 case VariableRequestHandlerState::PENDING: {
685 qCDebug(LOG_VariableController()) << tr("Process Request PENDING")
686 << varRequest.m_RangeRequested
687 << varRequest.m_CacheRangeRequested;
688 auto variableGroupIdToCancel = varHandler->m_PendingVarRequest.m_VariableGroupId;
689 cancelVariableRequest(variableGroupIdToCancel);
690 // Cancel variable can make state downgrade
691 varHandler->m_State = VariableRequestHandlerState::PENDING;
692 varHandler->m_PendingVarRequest = varRequest;
693
694 break;
695 }
696 default:
697 qCCritical(LOG_VariableController())
698 << QObject::tr("Unknown VariableRequestHandlerState");
639 699 }
640 700 }
641 701
642 702 std::shared_ptr<Variable>
643 703 VariableController::VariableControllerPrivate::findVariable(QUuid vIdentifier)
644 704 {
645 705 std::shared_ptr<Variable> var;
646 706 auto findReply = [vIdentifier](const auto &entry) { return vIdentifier == entry.second; };
647 707
648 708 auto end = m_VariableToIdentifierMap.cend();
649 709 auto it = std::find_if(m_VariableToIdentifierMap.cbegin(), end, findReply);
650 710 if (it != end) {
651 711 var = it->first;
652 712 }
653 713 else {
654 714 qCCritical(LOG_VariableController())
655 715 << tr("Impossible to find the variable with the identifier: ") << vIdentifier;
656 716 }
657 717
658 718 return var;
659 719 }
660 720
661 721 std::shared_ptr<IDataSeries> VariableController::VariableControllerPrivate::retrieveDataSeries(
662 722 const QVector<AcquisitionDataPacket> acqDataPacketVector)
663 723 {
664 724 qCDebug(LOG_VariableController()) << tr("TORM: retrieveDataSeries acqDataPacketVector size")
665 725 << acqDataPacketVector.size();
666 726 std::shared_ptr<IDataSeries> dataSeries;
667 727 if (!acqDataPacketVector.isEmpty()) {
668 728 dataSeries = acqDataPacketVector[0].m_DateSeries;
669 729 for (int i = 1; i < acqDataPacketVector.size(); ++i) {
670 730 dataSeries->merge(acqDataPacketVector[i].m_DateSeries.get());
671 731 }
672 732 }
673 733 qCDebug(LOG_VariableController()) << tr("TORM: retrieveDataSeries acqDataPacketVector size END")
674 734 << acqDataPacketVector.size();
675 735 return dataSeries;
676 736 }
677 737
678 738 void VariableController::VariableControllerPrivate::registerProvider(
679 739 std::shared_ptr<IDataProvider> provider)
680 740 {
681 741 if (m_ProviderSet.find(provider) == m_ProviderSet.end()) {
682 742 qCDebug(LOG_VariableController()) << tr("Registering of a new provider")
683 743 << provider->objectName();
684 744 m_ProviderSet.insert(provider);
685 745 connect(provider.get(), &IDataProvider::dataProvided, m_VariableAcquisitionWorker.get(),
686 746 &VariableAcquisitionWorker::onVariableDataAcquired);
687 747 connect(provider.get(), &IDataProvider::dataProvidedProgress,
688 748 m_VariableAcquisitionWorker.get(),
689 749 &VariableAcquisitionWorker::onVariableRetrieveDataInProgress);
690 750 connect(provider.get(), &IDataProvider::dataProvidedFailed,
691 751 m_VariableAcquisitionWorker.get(),
692 752 &VariableAcquisitionWorker::onVariableAcquisitionFailed);
693 753 }
694 754 else {
695 755 qCDebug(LOG_VariableController()) << tr("Cannot register provider, it already exists ");
696 756 }
697 757 }
698 758
699 void VariableController::VariableControllerPrivate::storeVariableRequest(
700 QUuid varId, QUuid varRequestId, const VariableRequest &varRequest)
701 {
702 // First request for the variable. we can create an entry for it
703 auto varIdToVarRequestIdQueueMapIt = m_VarIdToVarRequestIdQueueMap.find(varId);
704 if (varIdToVarRequestIdQueueMapIt == m_VarIdToVarRequestIdQueueMap.cend()) {
705 auto varRequestIdQueue = std::deque<QUuid>{};
706 qCDebug(LOG_VariableController()) << tr("Store REQUEST in QUEUE");
707 varRequestIdQueue.push_back(varRequestId);
708 m_VarIdToVarRequestIdQueueMap.insert(std::make_pair(varId, std::move(varRequestIdQueue)));
709 }
710 else {
711 qCDebug(LOG_VariableController()) << tr("Store REQUEST in EXISTING QUEUE");
712 auto &varRequestIdQueue = varIdToVarRequestIdQueueMapIt->second;
713 varRequestIdQueue.push_back(varRequestId);
714 }
715
716 auto varRequestIdToVarIdVarRequestMapIt = m_VarRequestIdToVarIdVarRequestMap.find(varRequestId);
717 if (varRequestIdToVarIdVarRequestMapIt == m_VarRequestIdToVarIdVarRequestMap.cend()) {
718 auto varIdToVarRequestMap = std::map<QUuid, VariableRequest>{};
719 varIdToVarRequestMap.insert(std::make_pair(varId, varRequest));
720 qCDebug(LOG_VariableController()) << tr("Store REQUESTID in MAP");
721 m_VarRequestIdToVarIdVarRequestMap.insert(
722 std::make_pair(varRequestId, std::move(varIdToVarRequestMap)));
723 }
724 else {
725 auto &varIdToVarRequestMap = varRequestIdToVarIdVarRequestMapIt->second;
726 qCDebug(LOG_VariableController()) << tr("Store REQUESTID in EXISTING MAP");
727 varIdToVarRequestMap.insert(std::make_pair(varId, varRequest));
728 }
729 }
730
731 759 QUuid VariableController::VariableControllerPrivate::acceptVariableRequest(
732 760 QUuid varId, std::shared_ptr<IDataSeries> dataSeries)
733 761 {
734 QUuid varRequestId;
735 auto varIdToVarRequestIdQueueMapIt = m_VarIdToVarRequestIdQueueMap.find(varId);
736 if (varIdToVarRequestIdQueueMapIt != m_VarIdToVarRequestIdQueueMap.cend()) {
737 auto &varRequestIdQueue = varIdToVarRequestIdQueueMapIt->second;
738 varRequestId = varRequestIdQueue.front();
739 auto varRequestIdToVarIdVarRequestMapIt
740 = m_VarRequestIdToVarIdVarRequestMap.find(varRequestId);
741 if (varRequestIdToVarIdVarRequestMapIt != m_VarRequestIdToVarIdVarRequestMap.cend()) {
742 auto &varIdToVarRequestMap = varRequestIdToVarIdVarRequestMapIt->second;
743 auto varIdToVarRequestMapIt = varIdToVarRequestMap.find(varId);
744 if (varIdToVarRequestMapIt != varIdToVarRequestMap.cend()) {
745 qCDebug(LOG_VariableController()) << tr("acceptVariableRequest");
746 auto &varRequest = varIdToVarRequestMapIt->second;
747 varRequest.m_DataSeries = dataSeries;
748 varRequest.m_CanUpdate = true;
749 }
750 else {
751 qCDebug(LOG_VariableController())
752 << tr("Impossible to acceptVariableRequest of a unknown variable id attached "
753 "to a variableRequestId")
754 << varRequestId << varId;
755 }
756 }
757 else {
758 qCCritical(LOG_VariableController())
759 << tr("Impossible to acceptVariableRequest of a unknown variableRequestId")
760 << varRequestId;
761 }
762
763 varRequestIdQueue.pop_front();
764 if (varRequestIdQueue.empty()) {
765 qCDebug(LOG_VariableController())
766 << tr("TORM Erase REQUEST because it has been accepted") << varId;
767 m_VarIdToVarRequestIdQueueMap.erase(varId);
768 }
762 auto itVarHandler = m_VarIdToVarRequestHandler.find(varId);
763 if (itVarHandler == m_VarIdToVarRequestHandler.cend()) {
764 return QUuid();
769 765 }
770 else {
766
767 auto varHandler = itVarHandler->second.get();
768 if (varHandler->m_State == VariableRequestHandlerState::OFF) {
771 769 qCCritical(LOG_VariableController())
772 << tr("Impossible to acceptVariableRequest of a unknown variable id") << varId;
770 << tr("acceptVariableRequest impossible on a variable with OFF state");
773 771 }
774 772
775 return varRequestId;
773 varHandler->m_RunningVarRequest.m_DataSeries = dataSeries;
774 varHandler->m_CanUpdate = true;
775
776 // Element traitΓ©, on a dΓ©jΓ  toutes les donnΓ©es necessaires
777 auto varGroupId = varHandler->m_RunningVarRequest.m_VariableGroupId;
778 qCDebug(LOG_VariableController()) << "Variable::acceptVariableRequest" << varGroupId
779 << m_VarGroupIdToVarIds.size();
780
781 return varHandler->m_RunningVarRequest.m_VariableGroupId;
776 782 }
777 783
778 void VariableController::VariableControllerPrivate::updateVariableRequest(QUuid varRequestId)
784 void VariableController::VariableControllerPrivate::updateVariables(QUuid varRequestId)
779 785 {
786 qCDebug(LOG_VariableController()) << "VariableControllerPrivate::updateVariables"
787 << QThread::currentThread()->objectName() << varRequestId;
780 788
781 auto varRequestIdToVarIdVarRequestMapIt = m_VarRequestIdToVarIdVarRequestMap.find(varRequestId);
782 if (varRequestIdToVarIdVarRequestMapIt != m_VarRequestIdToVarIdVarRequestMap.cend()) {
783 bool processVariableUpdate = true;
784 auto &varIdToVarRequestMap = varRequestIdToVarIdVarRequestMapIt->second;
785 for (auto varIdToVarRequestMapIt = varIdToVarRequestMap.cbegin();
786 (varIdToVarRequestMapIt != varIdToVarRequestMap.cend()) && processVariableUpdate;
787 ++varIdToVarRequestMapIt) {
788 processVariableUpdate &= varIdToVarRequestMapIt->second.m_CanUpdate;
789 qCDebug(LOG_VariableController()) << tr("updateVariableRequest")
790 << processVariableUpdate;
789 auto varGroupIdToVarIdsIt = m_VarGroupIdToVarIds.find(varRequestId);
790 if (varGroupIdToVarIdsIt == m_VarGroupIdToVarIds.end()) {
791 qCWarning(LOG_VariableController())
792 << tr("Impossible to updateVariables of unknown variables") << varRequestId;
793 return;
794 }
795
796 auto &varIds = varGroupIdToVarIdsIt->second;
797 auto varIdsEnd = varIds.end();
798 bool processVariableUpdate = true;
799 qCDebug(LOG_VariableController()) << "VariableControllerPrivate::updateVariables"
800 << varRequestId << varIds.size();
801 for (auto varIdsIt = varIds.begin(); (varIdsIt != varIdsEnd) && processVariableUpdate;
802 ++varIdsIt) {
803 auto itVarHandler = m_VarIdToVarRequestHandler.find(*varIdsIt);
804 if (itVarHandler != m_VarIdToVarRequestHandler.cend()) {
805 processVariableUpdate &= itVarHandler->second->m_CanUpdate;
791 806 }
807 }
792 808
793 if (processVariableUpdate) {
794 for (auto varIdToVarRequestMapIt = varIdToVarRequestMap.cbegin();
795 varIdToVarRequestMapIt != varIdToVarRequestMap.cend(); ++varIdToVarRequestMapIt) {
796 if (auto var = findVariable(varIdToVarRequestMapIt->first)) {
797 auto &varRequest = varIdToVarRequestMapIt->second;
809 if (processVariableUpdate) {
810 qCDebug(LOG_VariableController()) << "Final update OK for the var request" << varIds.size();
811 for (auto varIdsIt = varIds.begin(); varIdsIt != varIdsEnd; ++varIdsIt) {
812 auto itVarHandler = m_VarIdToVarRequestHandler.find(*varIdsIt);
813 if (itVarHandler != m_VarIdToVarRequestHandler.cend()) {
814 if (auto var = findVariable(*varIdsIt)) {
815 auto &varRequest = itVarHandler->second->m_RunningVarRequest;
798 816 var->setRange(varRequest.m_RangeRequested);
799 817 var->setCacheRange(varRequest.m_CacheRangeRequested);
800 818 qCDebug(LOG_VariableController()) << tr("1: onDataProvided")
801 819 << varRequest.m_RangeRequested
802 820 << varRequest.m_CacheRangeRequested;
803 821 qCDebug(LOG_VariableController()) << tr("2: onDataProvided var points before")
804 822 << var->nbPoints()
805 823 << varRequest.m_DataSeries->nbPoints();
806 824 var->mergeDataSeries(varRequest.m_DataSeries);
807 825 qCDebug(LOG_VariableController()) << tr("3: onDataProvided var points after")
808 826 << var->nbPoints();
809 827
810 828 emit var->updated();
829 qCDebug(LOG_VariableController()) << tr("Update OK");
811 830 }
812 831 else {
813 832 qCCritical(LOG_VariableController())
814 833 << tr("Impossible to update data to a null variable");
815 834 }
816 835 }
817 // cleaning varRequestId
818 qCDebug(LOG_VariableController()) << tr("0: erase REQUEST in MAP ?")
819 << m_VarRequestIdToVarIdVarRequestMap.size();
820 m_VarRequestIdToVarIdVarRequestMap.erase(varRequestId);
821 qCDebug(LOG_VariableController()) << tr("1: erase REQUEST in MAP ?")
822 << m_VarRequestIdToVarIdVarRequestMap.size();
823 836 }
837 updateVariableRequest(varRequestId);
838
839 // cleaning varRequestId
840 qCDebug(LOG_VariableController()) << tr("m_VarGroupIdToVarIds erase") << varRequestId;
841 m_VarGroupIdToVarIds.erase(varRequestId);
824 842 }
825 else {
826 qCCritical(LOG_VariableController())
827 << tr("Cannot updateVariableRequest for a unknow varRequestId") << varRequestId;
843 }
844
845
846 void VariableController::VariableControllerPrivate::updateVariableRequest(QUuid varRequestId)
847 {
848 auto varGroupIdToVarIdsIt = m_VarGroupIdToVarIds.find(varRequestId);
849 if (varGroupIdToVarIdsIt == m_VarGroupIdToVarIds.end()) {
850 qCCritical(LOG_VariableController()) << QObject::tr(
851 "Impossible to updateVariableRequest since varGroupdId isn't here anymore");
852
853 return;
854 }
855
856 auto &varIds = varGroupIdToVarIdsIt->second;
857 auto varIdsEnd = varIds.end();
858 for (auto varIdsIt = varIds.begin(); (varIdsIt != varIdsEnd); ++varIdsIt) {
859 auto itVarHandler = m_VarIdToVarRequestHandler.find(*varIdsIt);
860 if (itVarHandler != m_VarIdToVarRequestHandler.cend()) {
861
862 auto varHandler = itVarHandler->second.get();
863 varHandler->m_CanUpdate = false;
864
865
866 switch (varHandler->m_State) {
867 case VariableRequestHandlerState::OFF: {
868 qCCritical(LOG_VariableController())
869 << QObject::tr("Impossible to update a variable with handler in OFF state");
870 } break;
871 case VariableRequestHandlerState::RUNNING: {
872 varHandler->m_State = VariableRequestHandlerState::OFF;
873 varHandler->m_RunningVarRequest = VariableRequest{};
874 break;
875 }
876 case VariableRequestHandlerState::PENDING: {
877 varHandler->m_State = VariableRequestHandlerState::RUNNING;
878 varHandler->m_RunningVarRequest = varHandler->m_PendingVarRequest;
879 varHandler->m_PendingVarRequest = VariableRequest{};
880 auto var = findVariable(itVarHandler->first);
881 executeVarRequest(var, varHandler->m_RunningVarRequest);
882 break;
883 }
884 default:
885 qCCritical(LOG_VariableController())
886 << QObject::tr("Unknown VariableRequestHandlerState");
887 }
888 }
828 889 }
829 890 }
830 891
892
831 893 void VariableController::VariableControllerPrivate::cancelVariableRequest(QUuid varRequestId)
832 894 {
833 // cleaning varRequestId
834 m_VarRequestIdToVarIdVarRequestMap.erase(varRequestId);
895 qCDebug(LOG_VariableController()) << tr("cancelVariableRequest") << varRequestId;
896
897 auto varGroupIdToVarIdsIt = m_VarGroupIdToVarIds.find(varRequestId);
898 if (varGroupIdToVarIdsIt == m_VarGroupIdToVarIds.end()) {
899 qCCritical(LOG_VariableController())
900 << tr("Impossible to cancelVariableRequest for unknown varGroupdId") << varRequestId;
901 return;
902 }
835 903
836 for (auto varIdToVarRequestIdQueueMapIt = m_VarIdToVarRequestIdQueueMap.begin();
837 varIdToVarRequestIdQueueMapIt != m_VarIdToVarRequestIdQueueMap.end();) {
838 auto &varRequestIdQueue = varIdToVarRequestIdQueueMapIt->second;
839 varRequestIdQueue.erase(
840 std::remove(varRequestIdQueue.begin(), varRequestIdQueue.end(), varRequestId),
841 varRequestIdQueue.end());
842 if (varRequestIdQueue.empty()) {
843 varIdToVarRequestIdQueueMapIt
844 = m_VarIdToVarRequestIdQueueMap.erase(varIdToVarRequestIdQueueMapIt);
904 auto &varIds = varGroupIdToVarIdsIt->second;
905 auto varIdsEnd = varIds.end();
906 for (auto varIdsIt = varIds.begin(); (varIdsIt != varIdsEnd); ++varIdsIt) {
907 auto itVarHandler = m_VarIdToVarRequestHandler.find(*varIdsIt);
908 if (itVarHandler != m_VarIdToVarRequestHandler.cend()) {
845 909
846 // Recompute if there is any next request based on the removed request.
847 }
848 else {
849 ++varIdToVarRequestIdQueueMapIt;
910 auto varHandler = itVarHandler->second.get();
911 varHandler->m_VarId = QUuid{};
912 switch (varHandler->m_State) {
913 case VariableRequestHandlerState::OFF: {
914 qCWarning(LOG_VariableController())
915 << QObject::tr("Impossible to cancel a variable with no running request");
916 break;
917 }
918 case VariableRequestHandlerState::RUNNING: {
919
920 if (varHandler->m_RunningVarRequest.m_VariableGroupId == varRequestId) {
921 auto var = findVariable(itVarHandler->first);
922 auto varProvider = m_VariableToProviderMap.at(var);
923 if (varProvider != nullptr) {
924 m_VariableAcquisitionWorker->abortProgressRequested(
925 itVarHandler->first);
926 }
927 m_VariableModel->setDataProgress(var, 0.0);
928 varHandler->m_CanUpdate = false;
929 varHandler->m_State = VariableRequestHandlerState::OFF;
930 varHandler->m_RunningVarRequest = VariableRequest{};
931 }
932 else {
933 // TODO: log Impossible to cancel the running variable request beacause its
934 // varRequestId isn't not the canceled one
935 }
936 break;
937 }
938 case VariableRequestHandlerState::PENDING: {
939 if (varHandler->m_RunningVarRequest.m_VariableGroupId == varRequestId) {
940 auto var = findVariable(itVarHandler->first);
941 auto varProvider = m_VariableToProviderMap.at(var);
942 if (varProvider != nullptr) {
943 m_VariableAcquisitionWorker->abortProgressRequested(
944 itVarHandler->first);
945 }
946 m_VariableModel->setDataProgress(var, 0.0);
947 varHandler->m_CanUpdate = false;
948 varHandler->m_State = VariableRequestHandlerState::RUNNING;
949 varHandler->m_RunningVarRequest = varHandler->m_PendingVarRequest;
950 varHandler->m_PendingVarRequest = VariableRequest{};
951 executeVarRequest(var, varHandler->m_RunningVarRequest);
952 }
953 else if (varHandler->m_PendingVarRequest.m_VariableGroupId == varRequestId) {
954 varHandler->m_State = VariableRequestHandlerState::RUNNING;
955 varHandler->m_PendingVarRequest = VariableRequest{};
956 }
957 else {
958 // TODO: log Impossible to cancel the variable request beacause its
959 // varRequestId isn't not the canceled one
960 }
961 break;
962 }
963 default:
964 qCCritical(LOG_VariableController())
965 << QObject::tr("Unknown VariableRequestHandlerState");
966 }
850 967 }
851 968 }
969 qCDebug(LOG_VariableController()) << tr("cancelVariableRequest: erase") << varRequestId;
970 m_VarGroupIdToVarIds.erase(varRequestId);
852 971 }
853 972
854 SqpRange VariableController::VariableControllerPrivate::getLastRequestedRange(QUuid varId)
973 void VariableController::VariableControllerPrivate::executeVarRequest(std::shared_ptr<Variable> var,
974 VariableRequest &varRequest)
855 975 {
856 auto lastRangeRequested = SqpRange{INVALID_RANGE};
857 auto varIdToVarRequestIdQueueMapIt = m_VarIdToVarRequestIdQueueMap.find(varId);
858 if (varIdToVarRequestIdQueueMapIt != m_VarIdToVarRequestIdQueueMap.cend()) {
859 auto &varRequestIdQueue = varIdToVarRequestIdQueueMapIt->second;
860 auto varRequestId = varRequestIdQueue.back();
861 auto varRequestIdToVarIdVarRequestMapIt
862 = m_VarRequestIdToVarIdVarRequestMap.find(varRequestId);
863 if (varRequestIdToVarIdVarRequestMapIt != m_VarRequestIdToVarIdVarRequestMap.cend()) {
864 auto &varIdToVarRequestMap = varRequestIdToVarIdVarRequestMapIt->second;
865 auto varIdToVarRequestMapIt = varIdToVarRequestMap.find(varId);
866 if (varIdToVarRequestMapIt != varIdToVarRequestMap.cend()) {
867 auto &varRequest = varIdToVarRequestMapIt->second;
868 lastRangeRequested = varRequest.m_RangeRequested;
869 }
870 else {
871 qCDebug(LOG_VariableController())
872 << tr("Impossible to getLastRequestedRange of a unknown variable id attached "
873 "to a variableRequestId")
874 << varRequestId << varId;
875 }
976 qCDebug(LOG_VariableController()) << tr("TORM: executeVarRequest");
977
978 auto varId = m_VariableToIdentifierMap.at(var);
979
980 auto varCacheRange = var->cacheRange();
981 auto varCacheRangeRequested = varRequest.m_CacheRangeRequested;
982 auto notInCacheRangeList
983 = Variable::provideNotInCacheRangeList(varCacheRange, varCacheRangeRequested);
984 auto inCacheRangeList
985 = Variable::provideInCacheRangeList(varCacheRange, varCacheRangeRequested);
986
987 if (!notInCacheRangeList.empty()) {
988
989 auto varProvider = m_VariableToProviderMap.at(var);
990 if (varProvider != nullptr) {
991 qCDebug(LOG_VariableController()) << "executeVarRequest " << varRequest.m_RangeRequested
992 << varRequest.m_CacheRangeRequested;
993 m_VariableAcquisitionWorker->pushVariableRequest(
994 varRequest.m_VariableGroupId, varId, varRequest.m_RangeRequested,
995 varRequest.m_CacheRangeRequested,
996 DataProviderParameters{std::move(notInCacheRangeList), var->metadata()},
997 varProvider);
876 998 }
877 999 else {
878 1000 qCCritical(LOG_VariableController())
879 << tr("Impossible to getLastRequestedRange of a unknown variableRequestId")
880 << varRequestId;
1001 << "Impossible to provide data with a null provider";
1002 }
1003
1004 if (!inCacheRangeList.empty()) {
1005 emit q->updateVarDisplaying(var, inCacheRangeList.first());
881 1006 }
882 1007 }
883 1008 else {
884 qDebug(LOG_VariableController())
885 << tr("Impossible to getLastRequestedRange of a unknown variable id") << varId;
1009 acceptVariableRequest(varId,
1010 var->dataSeries()->subDataSeries(varRequest.m_CacheRangeRequested));
886 1011 }
887
888 return lastRangeRequested;
889 1012 }
@@ -1,409 +1,409
1 1 #include <Variable/Variable.h>
2 2
3 3 #include <Data/ScalarSeries.h>
4 4
5 5 #include <QObject>
6 6 #include <QtTest>
7 7
8 8 #include <memory>
9 9
10 10 namespace {
11 11
12 12 /// Generates a date in double
13 13 auto date = [](int year, int month, int day, int hours, int minutes, int seconds) {
14 14 return DateUtils::secondsSinceEpoch(
15 15 QDateTime{{year, month, day}, {hours, minutes, seconds}, Qt::UTC});
16 16 };
17 17
18 18 /// Generates a series of test data for a range
19 19 std::shared_ptr<ScalarSeries> dataSeries(const SqpRange &range)
20 20 {
21 21 auto xAxisData = std::vector<double>{};
22 22 auto valuesData = std::vector<double>{};
23 23
24 24 auto value = 0;
25 25 for (auto x = range.m_TStart; x <= range.m_TEnd; ++x, ++value) {
26 26 xAxisData.push_back(x);
27 27 valuesData.push_back(value);
28 28 }
29 29
30 30 return std::make_shared<ScalarSeries>(std::move(xAxisData), std::move(valuesData), Unit{},
31 31 Unit{});
32 32 }
33 33
34 34 } // namespace
35 35
36 36 Q_DECLARE_METATYPE(std::shared_ptr<ScalarSeries>)
37 37
38 38 class TestVariable : public QObject {
39 39 Q_OBJECT
40 40
41 41 private slots:
42 42 void testClone_data();
43 43 void testClone();
44 44
45 45 void testNotInCacheRangeList();
46 46 void testInCacheRangeList();
47 47
48 48 void testNbPoints_data();
49 49 void testNbPoints();
50 50
51 51 void testRealRange_data();
52 52 void testRealRange();
53 53 };
54 54
55 55 void TestVariable::testClone_data()
56 56 {
57 57 // ////////////// //
58 58 // Test structure //
59 59 // ////////////// //
60 60
61 61 QTest::addColumn<QString>("name");
62 62 QTest::addColumn<QVariantHash>("metadata");
63 63 QTest::addColumn<SqpRange>("range");
64 64 QTest::addColumn<SqpRange>("cacheRange");
65 65 QTest::addColumn<std::shared_ptr<ScalarSeries> >("dataSeries");
66 66
67 67 // ////////// //
68 68 // Test cases //
69 69 // ////////// //
70 70
71 71 auto cacheRange = SqpRange{date(2017, 1, 1, 12, 0, 0), date(2017, 1, 1, 13, 0, 0)};
72 72 QTest::newRow("clone1") << QStringLiteral("var1")
73 73 << QVariantHash{{"data1", 1}, {"data2", "abc"}}
74 74 << SqpRange{date(2017, 1, 1, 12, 30, 0), (date(2017, 1, 1, 12, 45, 0))}
75 75 << cacheRange << dataSeries(cacheRange);
76 76 }
77 77
78 78 void TestVariable::testClone()
79 79 {
80 80 // Creates variable
81 81 QFETCH(QString, name);
82 82 QFETCH(QVariantHash, metadata);
83 83 QFETCH(SqpRange, range);
84 84 QFETCH(SqpRange, cacheRange);
85 85 QFETCH(std::shared_ptr<ScalarSeries>, dataSeries);
86 86
87 87 Variable variable{name, metadata};
88 88 variable.setRange(range);
89 89 variable.setCacheRange(cacheRange);
90 90 variable.mergeDataSeries(dataSeries);
91 91
92 92 // Clones variable
93 93 auto clone = variable.clone();
94 94
95 95 // Checks cloned variable's state
96 96 QCOMPARE(clone->name(), name);
97 97 QCOMPARE(clone->metadata(), metadata);
98 98 QCOMPARE(clone->range(), range);
99 99 QCOMPARE(clone->cacheRange(), cacheRange);
100 100
101 101 // Compares data series
102 102 if (dataSeries != nullptr) {
103 103 QVERIFY(clone->dataSeries() != nullptr);
104 104 QVERIFY(std::equal(dataSeries->cbegin(), dataSeries->cend(), clone->dataSeries()->cbegin(),
105 105 clone->dataSeries()->cend(), [](const auto &it1, const auto &it2) {
106 106 return it1.x() == it2.x() && it1.value() == it2.value();
107 107 }));
108 108 }
109 109 else {
110 110 QVERIFY(clone->dataSeries() == nullptr);
111 111 }
112 112 }
113 113
114 114 void TestVariable::testNotInCacheRangeList()
115 115 {
116 116 auto varRS = QDateTime{QDate{2017, 01, 01}, QTime{2, 3, 20, 0}};
117 117 auto varRE = QDateTime{QDate{2017, 01, 01}, QTime{2, 3, 40, 0}};
118 118
119 119 auto sqpR = SqpRange{DateUtils::secondsSinceEpoch(varRS), DateUtils::secondsSinceEpoch(varRE)};
120 120
121 121 auto varCRS = QDateTime{QDate{2017, 01, 01}, QTime{2, 3, 0, 0}};
122 122 auto varCRE = QDateTime{QDate{2017, 01, 01}, QTime{2, 4, 0, 0}};
123 123
124 124 auto sqpCR
125 125 = SqpRange{DateUtils::secondsSinceEpoch(varCRS), DateUtils::secondsSinceEpoch(varCRE)};
126 126
127 127 Variable var{"Var test"};
128 128 var.setRange(sqpR);
129 129 var.setCacheRange(sqpCR);
130 130
131 131 // 1: [ts,te] < varTS
132 132 auto ts = QDateTime{QDate{2017, 01, 01}, QTime{2, 0, 0, 0}};
133 133 auto te = QDateTime{QDate{2017, 01, 01}, QTime{2, 1, 0, 0}};
134 134 auto sqp = SqpRange{DateUtils::secondsSinceEpoch(ts), DateUtils::secondsSinceEpoch(te)};
135 135
136 136 auto notInCach = var.provideNotInCacheRangeList(sqp);
137 137
138 138 QCOMPARE(notInCach.size(), 1);
139 139
140 140 auto notInCachRange = notInCach.first();
141 141
142 142 QCOMPARE(notInCachRange.m_TStart, DateUtils::secondsSinceEpoch(ts));
143 143 QCOMPARE(notInCachRange.m_TEnd, DateUtils::secondsSinceEpoch(te));
144 144
145 145 // 2: ts < varTS < te < varTE
146 146 ts = QDateTime{QDate{2017, 01, 01}, QTime{2, 0, 0, 0}};
147 147 te = QDateTime{QDate{2017, 01, 01}, QTime{2, 3, 30, 0}};
148 148 sqp = SqpRange{DateUtils::secondsSinceEpoch(ts), DateUtils::secondsSinceEpoch(te)};
149 149 notInCach = var.provideNotInCacheRangeList(sqp);
150 150 QCOMPARE(notInCach.size(), 1);
151 151 notInCachRange = notInCach.first();
152 152 QCOMPARE(notInCachRange.m_TStart, DateUtils::secondsSinceEpoch(ts));
153 153 QCOMPARE(notInCachRange.m_TEnd, DateUtils::secondsSinceEpoch(varCRS));
154 154
155 155 // 3: varTS < ts < te < varTE
156 156 ts = QDateTime{QDate{2017, 01, 01}, QTime{2, 3, 20, 0}};
157 157 te = QDateTime{QDate{2017, 01, 01}, QTime{2, 3, 30, 0}};
158 158 sqp = SqpRange{DateUtils::secondsSinceEpoch(ts), DateUtils::secondsSinceEpoch(te)};
159 159 notInCach = var.provideNotInCacheRangeList(sqp);
160 160 QCOMPARE(notInCach.size(), 0);
161 161
162 162
163 163 // 4: varTS < ts < varTE < te
164 164 ts = QDateTime{QDate{2017, 01, 01}, QTime{2, 3, 20, 0}};
165 165 te = QDateTime{QDate{2017, 01, 01}, QTime{2, 5, 0, 0}};
166 166 sqp = SqpRange{DateUtils::secondsSinceEpoch(ts), DateUtils::secondsSinceEpoch(te)};
167 167 notInCach = var.provideNotInCacheRangeList(sqp);
168 168 QCOMPARE(notInCach.size(), 1);
169 169 notInCachRange = notInCach.first();
170 170 QCOMPARE(notInCachRange.m_TStart, DateUtils::secondsSinceEpoch(varCRE));
171 171 QCOMPARE(notInCachRange.m_TEnd, DateUtils::secondsSinceEpoch(te));
172 172
173 173 // 5: varTS < varTE < ts < te
174 174 ts = QDateTime{QDate{2017, 01, 01}, QTime{2, 4, 20, 0}};
175 175 te = QDateTime{QDate{2017, 01, 01}, QTime{2, 5, 0, 0}};
176 176 sqp = SqpRange{DateUtils::secondsSinceEpoch(ts), DateUtils::secondsSinceEpoch(te)};
177 177 notInCach = var.provideNotInCacheRangeList(sqp);
178 178 QCOMPARE(notInCach.size(), 1);
179 179 notInCachRange = notInCach.first();
180 180 QCOMPARE(notInCachRange.m_TStart, DateUtils::secondsSinceEpoch(ts));
181 181 QCOMPARE(notInCachRange.m_TEnd, DateUtils::secondsSinceEpoch(te));
182 182
183 183 // 6: ts <varTS < varTE < te
184 184 ts = QDateTime{QDate{2017, 01, 01}, QTime{2, 1, 0, 0}};
185 185 te = QDateTime{QDate{2017, 01, 01}, QTime{2, 5, 0, 0}};
186 186 sqp = SqpRange{DateUtils::secondsSinceEpoch(ts), DateUtils::secondsSinceEpoch(te)};
187 187 notInCach = var.provideNotInCacheRangeList(sqp);
188 188 QCOMPARE(notInCach.size(), 2);
189 189 notInCachRange = notInCach.first();
190 190 QCOMPARE(notInCachRange.m_TStart, DateUtils::secondsSinceEpoch(ts));
191 191 QCOMPARE(notInCachRange.m_TEnd, DateUtils::secondsSinceEpoch(varCRS));
192 192 notInCachRange = notInCach[1];
193 193 QCOMPARE(notInCachRange.m_TStart, DateUtils::secondsSinceEpoch(varCRE));
194 194 QCOMPARE(notInCachRange.m_TEnd, DateUtils::secondsSinceEpoch(te));
195 195 }
196 196
197 197
198 198 void TestVariable::testInCacheRangeList()
199 199 {
200 200 auto varRS = QDateTime{QDate{2017, 01, 01}, QTime{2, 3, 20, 0}};
201 201 auto varRE = QDateTime{QDate{2017, 01, 01}, QTime{2, 3, 40, 0}};
202 202
203 203 auto sqpR = SqpRange{DateUtils::secondsSinceEpoch(varRS), DateUtils::secondsSinceEpoch(varRE)};
204 204
205 205 auto varCRS = QDateTime{QDate{2017, 01, 01}, QTime{2, 3, 0, 0}};
206 206 auto varCRE = QDateTime{QDate{2017, 01, 01}, QTime{2, 4, 0, 0}};
207 207 auto sqpCR
208 208 = SqpRange{DateUtils::secondsSinceEpoch(varCRS), DateUtils::secondsSinceEpoch(varCRE)};
209 209
210 210 Variable var{"Var test"};
211 211 var.setRange(sqpR);
212 212 var.setCacheRange(sqpCR);
213 213
214 214 // 1: [ts,te] < varTS
215 215 auto ts = QDateTime{QDate{2017, 01, 01}, QTime{2, 0, 0, 0}};
216 216 auto te = QDateTime{QDate{2017, 01, 01}, QTime{2, 1, 0, 0}};
217 217 auto sqp = SqpRange{DateUtils::secondsSinceEpoch(ts), DateUtils::secondsSinceEpoch(te)};
218 218
219 219 auto notInCach = var.provideInCacheRangeList(sqp);
220 220
221 221 QCOMPARE(notInCach.size(), 0);
222 222
223 223 // 2: ts < varTS < te < varTE
224 224 ts = QDateTime{QDate{2017, 01, 01}, QTime{2, 0, 0, 0}};
225 225 te = QDateTime{QDate{2017, 01, 01}, QTime{2, 3, 30, 0}};
226 226 sqp = SqpRange{DateUtils::secondsSinceEpoch(ts), DateUtils::secondsSinceEpoch(te)};
227 227 notInCach = var.provideInCacheRangeList(sqp);
228 228 QCOMPARE(notInCach.size(), 1);
229 229 auto notInCachRange = notInCach.first();
230 230 QCOMPARE(notInCachRange.m_TStart, DateUtils::secondsSinceEpoch(varCRS));
231 231 QCOMPARE(notInCachRange.m_TEnd, DateUtils::secondsSinceEpoch(te));
232 232
233 233 // 3: varTS < ts < te < varTE
234 234 ts = QDateTime{QDate{2017, 01, 01}, QTime{2, 3, 20, 0}};
235 235 te = QDateTime{QDate{2017, 01, 01}, QTime{2, 3, 30, 0}};
236 236 sqp = SqpRange{DateUtils::secondsSinceEpoch(ts), DateUtils::secondsSinceEpoch(te)};
237 237 notInCach = var.provideInCacheRangeList(sqp);
238 238 QCOMPARE(notInCach.size(), 1);
239 239 notInCachRange = notInCach.first();
240 240 QCOMPARE(notInCachRange.m_TStart, DateUtils::secondsSinceEpoch(ts));
241 241 QCOMPARE(notInCachRange.m_TEnd, DateUtils::secondsSinceEpoch(te));
242 242
243 243 // 4: varTS < ts < varTE < te
244 244 ts = QDateTime{QDate{2017, 01, 01}, QTime{2, 3, 20, 0}};
245 245 te = QDateTime{QDate{2017, 01, 01}, QTime{2, 5, 0, 0}};
246 246 sqp = SqpRange{DateUtils::secondsSinceEpoch(ts), DateUtils::secondsSinceEpoch(te)};
247 247 notInCach = var.provideInCacheRangeList(sqp);
248 248 QCOMPARE(notInCach.size(), 1);
249 249 notInCachRange = notInCach.first();
250 250 QCOMPARE(notInCachRange.m_TStart, DateUtils::secondsSinceEpoch(ts));
251 251 QCOMPARE(notInCachRange.m_TEnd, DateUtils::secondsSinceEpoch(varCRE));
252 252
253 253 // 5: varTS < varTE < ts < te
254 254 ts = QDateTime{QDate{2017, 01, 01}, QTime{2, 4, 20, 0}};
255 255 te = QDateTime{QDate{2017, 01, 01}, QTime{2, 5, 0, 0}};
256 256 sqp = SqpRange{DateUtils::secondsSinceEpoch(ts), DateUtils::secondsSinceEpoch(te)};
257 257 notInCach = var.provideInCacheRangeList(sqp);
258 258 QCOMPARE(notInCach.size(), 0);
259 259
260 260 // 6: ts <varTS < varTE < te
261 261 ts = QDateTime{QDate{2017, 01, 01}, QTime{2, 1, 0, 0}};
262 262 te = QDateTime{QDate{2017, 01, 01}, QTime{2, 5, 0, 0}};
263 263 sqp = SqpRange{DateUtils::secondsSinceEpoch(ts), DateUtils::secondsSinceEpoch(te)};
264 264 notInCach = var.provideInCacheRangeList(sqp);
265 265 QCOMPARE(notInCach.size(), 1);
266 266 notInCachRange = notInCach.first();
267 267 QCOMPARE(notInCachRange.m_TStart, DateUtils::secondsSinceEpoch(varCRS));
268 268 QCOMPARE(notInCachRange.m_TEnd, DateUtils::secondsSinceEpoch(varCRE));
269 269 }
270 270
271 271 namespace {
272 272
273 273 /// Struct used to represent an operation for @sa TestVariable::testNbPoints()
274 274 struct NbPointsOperation {
275 275 SqpRange m_CacheRange; /// Range to set for the variable
276 276 std::shared_ptr<ScalarSeries> m_DataSeries; /// Series to merge in the variable
277 277 int m_ExpectedNbPoints; /// Number of points in the variable expected after operation
278 278 };
279 279
280 280 using NbPointsOperations = std::vector<NbPointsOperation>;
281 281
282 282 } // namespace
283 283
284 284 Q_DECLARE_METATYPE(NbPointsOperations)
285 285
286 286 void TestVariable::testNbPoints_data()
287 287 {
288 288 // ////////////// //
289 289 // Test structure //
290 290 // ////////////// //
291 291
292 292 QTest::addColumn<NbPointsOperations>("operations");
293 293
294 294 // ////////// //
295 295 // Test cases //
296 296 // ////////// //
297 297 NbPointsOperations operations{};
298 298
299 299 // Sets cache range (expected nb points = series xAxis data + series values data)
300 300 auto cacheRange = SqpRange{date(2017, 1, 1, 12, 0, 0), date(2017, 1, 1, 12, 0, 9)};
301 301 operations.push_back({cacheRange, dataSeries(cacheRange), 20});
302 302
303 303 // Doubles cache but don't add data series (expected nb points don't change)
304 304 cacheRange = SqpRange{date(2017, 1, 1, 12, 0, 0), date(2017, 1, 1, 12, 0, 19)};
305 operations.push_back({cacheRange, nullptr, 20});
305 operations.push_back({cacheRange, dataSeries(INVALID_RANGE), 20});
306 306
307 307 // Doubles cache and data series (expected nb points change)
308 308 cacheRange = SqpRange{date(2017, 1, 1, 12, 0, 0), date(2017, 1, 1, 12, 0, 19)};
309 309 operations.push_back({cacheRange, dataSeries(cacheRange), 40});
310 310
311 311 // Decreases cache (expected nb points decreases as the series is purged)
312 312 cacheRange = SqpRange{date(2017, 1, 1, 12, 0, 5), date(2017, 1, 1, 12, 0, 9)};
313 operations.push_back({cacheRange, nullptr, 10});
313 operations.push_back({cacheRange, dataSeries(INVALID_RANGE), 10});
314 314
315 315 QTest::newRow("nbPoints1") << operations;
316 316 }
317 317
318 318 void TestVariable::testNbPoints()
319 319 {
320 320 // Creates variable
321 321 Variable variable{"var"};
322 322 QCOMPARE(variable.nbPoints(), 0);
323 323
324 324 QFETCH(NbPointsOperations, operations);
325 325 for (const auto &operation : operations) {
326 326 // Sets cache range and merge data series
327 327 variable.setCacheRange(operation.m_CacheRange);
328 328 if (operation.m_DataSeries != nullptr) {
329 329 variable.mergeDataSeries(operation.m_DataSeries);
330 330 }
331 331
332 332 // Checks nb points
333 333 QCOMPARE(variable.nbPoints(), operation.m_ExpectedNbPoints);
334 334 }
335 335 }
336 336
337 337 namespace {
338 338
339 339 /// Struct used to represent a range operation on a variable
340 340 /// @sa TestVariable::testRealRange()
341 341 struct RangeOperation {
342 342 SqpRange m_CacheRange; /// Range to set for the variable
343 343 std::shared_ptr<ScalarSeries> m_DataSeries; /// Series to merge in the variable
344 344 SqpRange m_ExpectedRealRange; /// Real Range expected after operation on the variable
345 345 };
346 346
347 347 using RangeOperations = std::vector<RangeOperation>;
348 348
349 349 } // namespace
350 350
351 351 Q_DECLARE_METATYPE(RangeOperations)
352 352
353 353 void TestVariable::testRealRange_data()
354 354 {
355 355 // ////////////// //
356 356 // Test structure //
357 357 // ////////////// //
358 358
359 359 QTest::addColumn<RangeOperations>("operations");
360 360
361 361 // ////////// //
362 362 // Test cases //
363 363 // ////////// //
364 364 RangeOperations operations{};
365 365
366 366 // Inits cache range and data series (expected real range = cache range)
367 367 auto cacheRange = SqpRange{date(2017, 1, 1, 12, 0, 0), date(2017, 1, 1, 13, 0, 0)};
368 368 operations.push_back({cacheRange, dataSeries(cacheRange), cacheRange});
369 369
370 370 // Changes cache range and updates data series (expected real range = cache range)
371 371 cacheRange = SqpRange{date(2017, 1, 1, 14, 0, 0), date(2017, 1, 1, 15, 0, 0)};
372 372 operations.push_back({cacheRange, dataSeries(cacheRange), cacheRange});
373 373
374 374 // Changes cache range and update data series but with a lower range (expected real range =
375 375 // data series range)
376 376 cacheRange = SqpRange{date(2017, 1, 1, 12, 0, 0), date(2017, 1, 1, 16, 0, 0)};
377 377 auto dataSeriesRange = SqpRange{date(2017, 1, 1, 14, 0, 0), date(2017, 1, 1, 15, 0, 0)};
378 378 operations.push_back({cacheRange, dataSeries(dataSeriesRange), dataSeriesRange});
379 379
380 380 // Changes cache range but DON'T update data series (expected real range = cache range
381 381 // before operation)
382 382 cacheRange = SqpRange{date(2017, 1, 1, 10, 0, 0), date(2017, 1, 1, 17, 0, 0)};
383 383 operations.push_back({cacheRange, nullptr, dataSeriesRange});
384 384
385 385 QTest::newRow("realRange1") << operations;
386 386 }
387 387
388 388 void TestVariable::testRealRange()
389 389 {
390 390 // Creates variable (real range is invalid)
391 391 Variable variable{"var"};
392 392 QCOMPARE(variable.realRange(), INVALID_RANGE);
393 393
394 394 QFETCH(RangeOperations, operations);
395 395 for (const auto &operation : operations) {
396 396 // Sets cache range and merge data series
397 397 variable.setCacheRange(operation.m_CacheRange);
398 398 if (operation.m_DataSeries != nullptr) {
399 399 variable.mergeDataSeries(operation.m_DataSeries);
400 400 }
401 401
402 402 // Checks real range
403 403 QCOMPARE(variable.realRange(), operation.m_ExpectedRealRange);
404 404 }
405 405 }
406 406
407 407
408 408 QTEST_MAIN(TestVariable)
409 409 #include "TestVariable.moc"
@@ -1,390 +1,506
1 1 #include <QObject>
2 2 #include <QtTest>
3 3
4 4 #include <memory>
5 5
6 6 #include <Data/DataProviderParameters.h>
7 7 #include <Data/IDataProvider.h>
8 8 #include <Data/ScalarSeries.h>
9 9 #include <Time/TimeController.h>
10 10 #include <Variable/Variable.h>
11 11 #include <Variable/VariableController.h>
12 12 #include <Variable/VariableModel.h>
13 13
14 14 namespace {
15 15
16 16 /// Delay after each operation on the variable before validating it (in ms)
17 17 const auto OPERATION_DELAY = 100;
18 18
19 19 /**
20 20 * Generates values according to a range. The value generated for a time t is the number of seconds
21 21 * of difference between t and a reference value (which is midnight -> 00:00:00)
22 22 *
23 23 * Example: For a range between 00:00:10 and 00:00:20, the generated values are
24 24 * {10,11,12,13,14,15,16,17,18,19,20}
25 25 */
26 26 std::vector<double> values(const SqpRange &range)
27 27 {
28 28 QTime referenceTime{0, 0};
29 29
30 30 std::vector<double> result{};
31 31
32 32 for (auto i = range.m_TStart; i <= range.m_TEnd; ++i) {
33 33 auto time = DateUtils::dateTime(i).time();
34 34 result.push_back(referenceTime.secsTo(time));
35 35 }
36 36
37 37 return result;
38 38 }
39 39
40 void validateRanges(VariableController &variableController,
41 const std::map<int, SqpRange> &expectedRanges)
42 {
43 for (const auto &expectedRangeEntry : expectedRanges) {
44 auto variableIndex = expectedRangeEntry.first;
45 auto expectedRange = expectedRangeEntry.second;
46
47 // Gets the variable in the controller
48 auto variable = variableController.variableModel()->variable(variableIndex);
49
50 // Compares variable's range to the expected range
51 QVERIFY(variable != nullptr);
52 auto range = variable->range();
53 qInfo() << "range vs expected range" << range << expectedRange;
54 QCOMPARE(range, expectedRange);
55
56 // Compares variable's data with values expected for its range
57 auto dataSeries = variable->dataSeries();
58 QVERIFY(dataSeries != nullptr);
59
60 auto it = dataSeries->xAxisRange(range.m_TStart, range.m_TEnd);
61 auto expectedValues = values(range);
62 qInfo() << std::distance(it.first, it.second) << expectedValues.size();
63 QVERIFY(std::equal(it.first, it.second, expectedValues.cbegin(), expectedValues.cend(),
64 [](const auto &dataSeriesIt, const auto &expectedValue) {
65 return dataSeriesIt.value() == expectedValue;
66 }));
67 }
68 }
69
40 70 /// Provider used for the tests
41 71 class TestProvider : public IDataProvider {
42 72 std::shared_ptr<IDataProvider> clone() const { return std::make_shared<TestProvider>(); }
43 73
44 74 void requestDataLoading(QUuid acqIdentifier, const DataProviderParameters &parameters) override
45 75 {
46 76 const auto &ranges = parameters.m_Times;
47 77
48 78 for (const auto &range : ranges) {
49 79 // Generates data series
50 80 auto valuesData = values(range);
51 81
52 82 std::vector<double> xAxisData{};
53 83 for (auto i = range.m_TStart; i <= range.m_TEnd; ++i) {
54 84 xAxisData.push_back(i);
55 85 }
56 86
57 87 auto dataSeries = std::make_shared<ScalarSeries>(
58 88 std::move(xAxisData), std::move(valuesData), Unit{"t", true}, Unit{});
59 89
60 90 emit dataProvided(acqIdentifier, dataSeries, range);
61 91 }
62 92 }
63 93
64 94 void requestDataAborting(QUuid acqIdentifier) override
65 95 {
66 96 // Does nothing
67 97 }
68 98 };
69 99
70 100 /**
71 101 * Interface representing an operation performed on a variable controller.
72 102 * This interface is used in tests to apply a set of operations and check the status of the
73 103 * controller after each operation
74 104 */
75 105 struct IOperation {
76 106 virtual ~IOperation() = default;
77 107 /// Executes the operation on the variable controller
78 108 virtual void exec(VariableController &variableController) const = 0;
79 109 };
80 110
81 111 /**
82 112 *Variable creation operation in the controller
83 113 */
84 114 struct Create : public IOperation {
85 115 explicit Create(int index) : m_Index{index} {}
86 116
87 117 void exec(VariableController &variableController) const override
88 118 {
89 119 auto variable = variableController.createVariable(QString::number(m_Index), {},
90 120 std::make_unique<TestProvider>());
91 121 }
92 122
93 123 int m_Index; ///< The index of the variable to create in the controller
94 124 };
95 125
96 126 /**
97 127 * Variable move/shift operation in the controller
98 128 */
99 129 struct Move : public IOperation {
100 explicit Move(int index, const SqpRange &newRange, bool shift = false)
101 : m_Index{index}, m_NewRange{newRange}, m_Shift{shift}
130 explicit Move(int index, const SqpRange &newRange, bool shift = false, int delayMS = 10)
131 : m_Index{index}, m_NewRange{newRange}, m_Shift{shift}, m_DelayMs{delayMS}
102 132 {
103 133 }
104 134
105 135 void exec(VariableController &variableController) const override
106 136 {
107 137 if (auto variable = variableController.variableModel()->variable(m_Index)) {
108 138 variableController.onRequestDataLoading({variable}, m_NewRange, !m_Shift);
139 QTest::qWait(m_DelayMs);
109 140 }
110 141 }
111 142
112 143 int m_Index; ///< The index of the variable to move
113 144 SqpRange m_NewRange; ///< The new range of the variable
114 145 bool m_Shift; ///< Performs a shift (
146 int m_DelayMs; ///< wait the delay after running the request (
115 147 };
116 148
117 149 /**
118 150 * Variable synchronization/desynchronization operation in the controller
119 151 */
120 152 struct Synchronize : public IOperation {
121 153 explicit Synchronize(int index, QUuid syncId, bool synchronize = true)
122 154 : m_Index{index}, m_SyncId{syncId}, m_Synchronize{synchronize}
123 155 {
124 156 }
125 157
126 158 void exec(VariableController &variableController) const override
127 159 {
128 160 if (auto variable = variableController.variableModel()->variable(m_Index)) {
129 161 if (m_Synchronize) {
130 162 variableController.onAddSynchronized(variable, m_SyncId);
131 163 }
132 164 else {
133 165 variableController.desynchronize(variable, m_SyncId);
134 166 }
135 167 }
136 168 }
137 169
138 170 int m_Index; ///< The index of the variable to sync/desync
139 171 QUuid m_SyncId; ///< The synchronization group of the variable
140 172 bool m_Synchronize; ///< Performs sync or desync operation
141 173 };
142 174
143 175 /**
144 176 * Test Iteration
145 177 *
146 178 * A test iteration includes an operation to be performed, and a set of expected ranges after each
147 179 * operation. Each range is tested after the operation to ensure that:
148 180 * - the range of the variable is the expected range
149 181 * - the data of the variable are those generated for the expected range
150 182 */
151 183 struct Iteration {
152 184 std::shared_ptr<IOperation> m_Operation; ///< Operation to perform
153 185 std::map<int, SqpRange> m_ExpectedRanges; ///< Expected ranges (by variable index)
154 186 };
155 187
156 188 using Iterations = std::vector<Iteration>;
157 189
158 190 } // namespace
159 191
160 192 Q_DECLARE_METATYPE(Iterations)
161 193
162 194 class TestVariableSync : public QObject {
163 195 Q_OBJECT
164 196
165 197 private slots:
166 198 /// Input data for @sa testSync()
167 199 void testSync_data();
168 200
201 /// Input data for @sa testSyncOneVar()
202 void testSyncOneVar_data();
203
169 204 /// Tests synchronization between variables through several operations
170 205 void testSync();
206
207 /// Tests synchronization between variables through several operations
208 void testSyncOneVar();
171 209 };
172 210
173 211 namespace {
174 212
175 213 void testSyncCase1()
176 214 {
177 215 // Id used to synchronize variables in the controller
178 216 auto syncId = QUuid::createUuid();
179 217
180 218 /// Generates a range according to a start time and a end time (the date is the same)
181 219 auto range = [](const QTime &startTime, const QTime &endTime) {
182 220 return SqpRange{DateUtils::secondsSinceEpoch(QDateTime{{2017, 1, 1}, startTime, Qt::UTC}),
183 221 DateUtils::secondsSinceEpoch(QDateTime{{2017, 1, 1}, endTime, Qt::UTC})};
184 222 };
185 223
186 224 auto initialRange = range({12, 0}, {13, 0});
187 225
188 226 Iterations iterations{};
189 227 // Creates variables var0, var1 and var2
190 228 iterations.push_back({std::make_shared<Create>(0), {{0, initialRange}}});
191 229 iterations.push_back({std::make_shared<Create>(1), {{0, initialRange}, {1, initialRange}}});
192 230 iterations.push_back(
193 231 {std::make_shared<Create>(2), {{0, initialRange}, {1, initialRange}, {2, initialRange}}});
194 232
195 233 // Adds variables into the sync group (ranges don't need to be tested here)
196 234 iterations.push_back({std::make_shared<Synchronize>(0, syncId)});
197 235 iterations.push_back({std::make_shared<Synchronize>(1, syncId)});
198 236 iterations.push_back({std::make_shared<Synchronize>(2, syncId)});
199 237
200 238 // Moves var0: ranges of var0, var1 and var2 change
201 239 auto newRange = range({12, 30}, {13, 30});
202 240 iterations.push_back(
203 241 {std::make_shared<Move>(0, newRange), {{0, newRange}, {1, newRange}, {2, newRange}}});
204 242
205 243 // Moves var1: ranges of var0, var1 and var2 change
206 244 newRange = range({13, 0}, {14, 0});
207 245 iterations.push_back(
208 246 {std::make_shared<Move>(0, newRange), {{0, newRange}, {1, newRange}, {2, newRange}}});
209 247
210 248 // Moves var2: ranges of var0, var1 and var2 change
211 249 newRange = range({13, 30}, {14, 30});
212 250 iterations.push_back(
213 251 {std::make_shared<Move>(0, newRange), {{0, newRange}, {1, newRange}, {2, newRange}}});
214 252
215 253 // Desyncs var2 and moves var0:
216 254 // - ranges of var0 and var1 change
217 255 // - range of var2 doesn't change anymore
218 256 auto var2Range = newRange;
219 257 newRange = range({13, 45}, {14, 45});
220 258 iterations.push_back({std::make_shared<Synchronize>(2, syncId, false)});
221 259 iterations.push_back(
222 260 {std::make_shared<Move>(0, newRange), {{0, newRange}, {1, newRange}, {2, var2Range}}});
223 261
224 262 // Shifts var0: although var1 is synchronized with var0, its range doesn't change
225 263 auto var1Range = newRange;
226 264 newRange = range({14, 45}, {15, 45});
227 265 iterations.push_back({std::make_shared<Move>(0, newRange, true),
228 266 {{0, newRange}, {1, var1Range}, {2, var2Range}}});
229 267
230 268 // Moves var0 through several operations:
231 269 // - range of var0 changes
232 270 // - range or var1 changes according to the previous shift (one hour)
233 271 auto moveVar0 = [&iterations](const auto &var0NewRange, const auto &var1ExpectedRange) {
234 272 iterations.push_back(
235 273 {std::make_shared<Move>(0, var0NewRange), {{0, var0NewRange}, {1, var1ExpectedRange}}});
236 274 };
275
237 276 // Pan left
238 277 moveVar0(range({14, 30}, {15, 30}), range({13, 30}, {14, 30}));
239 278 // Pan right
240 279 moveVar0(range({16, 0}, {17, 0}), range({15, 0}, {16, 0}));
241 280 // Zoom in
242 281 moveVar0(range({16, 30}, {16, 45}), range({15, 30}, {15, 45}));
243 282 // Zoom out
244 moveVar0(range({12, 0}, {18, 0}), range({11, 0}, {17, 0}));
283 moveVar0(range({16, 15}, {17, 0}), range({15, 15}, {16, 0}));
245 284
246 285 QTest::newRow("sync1") << syncId << initialRange << std::move(iterations) << 200;
247 286 }
248 287
249 288 void testSyncCase2()
250 289 {
251 290 // Id used to synchronize variables in the controller
252 291 auto syncId = QUuid::createUuid();
253 292
254 293 /// Generates a range according to a start time and a end time (the date is the same)
255 294 auto dateTime = [](int year, int month, int day, int hours, int minutes, int seconds) {
256 295 return DateUtils::secondsSinceEpoch(
257 296 QDateTime{{year, month, day}, QTime{hours, minutes, seconds}, Qt::UTC});
258 297 };
259 298
260 299 auto initialRange = SqpRange{dateTime(2017, 1, 1, 12, 0, 0), dateTime(2017, 1, 1, 13, 0, 0)};
261 300
262 301 Iterations iterations{};
263 302 // Creates variables var0 and var1
264 303 iterations.push_back({std::make_shared<Create>(0), {{0, initialRange}}});
265 304 iterations.push_back({std::make_shared<Create>(1), {{0, initialRange}, {1, initialRange}}});
266 305
267 306 // Adds variables into the sync group (ranges don't need to be tested here)
268 307 iterations.push_back({std::make_shared<Synchronize>(0, syncId)});
269 308 iterations.push_back({std::make_shared<Synchronize>(1, syncId)});
270 309
271 310
272 311 // Moves var0 through several operations:
273 312 // - range of var0 changes
274 313 // - range or var1 changes according to the previous shift (one hour)
275 314 auto moveVar0 = [&iterations](const auto &var0NewRange) {
276 315 iterations.push_back(
277 316 {std::make_shared<Move>(0, var0NewRange), {{0, var0NewRange}, {1, var0NewRange}}});
278 317 };
279 318 moveVar0(SqpRange{dateTime(2017, 1, 1, 12, 0, 0), dateTime(2017, 1, 1, 13, 0, 0)});
280 319 moveVar0(SqpRange{dateTime(2017, 1, 1, 14, 0, 0), dateTime(2017, 1, 1, 15, 0, 0)});
281 320 moveVar0(SqpRange{dateTime(2017, 1, 1, 8, 0, 0), dateTime(2017, 1, 1, 9, 0, 0)});
282 321 // moveVar0(SqpRange{dateTime(2017, 1, 1, 7, 30, 0), dateTime(2017, 1, 1, 9, 30, 0)});
283 322 moveVar0(SqpRange{dateTime(2017, 1, 1, 2, 0, 0), dateTime(2017, 1, 1, 4, 0, 0)});
284 323 moveVar0(SqpRange{dateTime(2017, 1, 1, 6, 0, 0), dateTime(2017, 1, 1, 8, 0, 0)});
285 324
286 325 moveVar0(SqpRange{dateTime(2017, 1, 10, 6, 0, 0), dateTime(2017, 1, 15, 8, 0, 0)});
287 326 moveVar0(SqpRange{dateTime(2017, 1, 17, 6, 0, 0), dateTime(2017, 1, 25, 8, 0, 0)});
288 327 moveVar0(SqpRange{dateTime(2017, 1, 2, 6, 0, 0), dateTime(2017, 1, 8, 8, 0, 0)});
289 328
290 329 moveVar0(SqpRange{dateTime(2017, 4, 10, 6, 0, 0), dateTime(2017, 6, 15, 8, 0, 0)});
291 330 moveVar0(SqpRange{dateTime(2017, 1, 17, 6, 0, 0), dateTime(2017, 2, 25, 8, 0, 0)});
292 331 moveVar0(SqpRange{dateTime(2017, 7, 2, 6, 0, 0), dateTime(2017, 10, 8, 8, 0, 0)});
293 332 moveVar0(SqpRange{dateTime(2017, 4, 10, 6, 0, 0), dateTime(2017, 6, 15, 8, 0, 0)});
294 333 moveVar0(SqpRange{dateTime(2017, 1, 17, 6, 0, 0), dateTime(2017, 2, 25, 8, 0, 0)});
295 334 moveVar0(SqpRange{dateTime(2017, 7, 2, 6, 0, 0), dateTime(2017, 10, 8, 8, 0, 0)});
296 335 moveVar0(SqpRange{dateTime(2017, 4, 10, 6, 0, 0), dateTime(2017, 6, 15, 8, 0, 0)});
297 336 moveVar0(SqpRange{dateTime(2017, 1, 17, 6, 0, 0), dateTime(2017, 2, 25, 8, 0, 0)});
298 337 moveVar0(SqpRange{dateTime(2017, 7, 2, 6, 0, 0), dateTime(2017, 10, 8, 8, 0, 0)});
299 338 moveVar0(SqpRange{dateTime(2017, 4, 10, 6, 0, 0), dateTime(2017, 6, 15, 8, 0, 0)});
300 339 moveVar0(SqpRange{dateTime(2017, 1, 17, 6, 0, 0), dateTime(2017, 2, 25, 8, 0, 0)});
301 340 moveVar0(SqpRange{dateTime(2017, 7, 2, 6, 0, 0), dateTime(2017, 10, 8, 8, 0, 0)});
302 341
303 342
304 343 QTest::newRow("sync2") << syncId << initialRange << iterations << 4000;
305 344 // QTest::newRow("sync3") << syncId << initialRange << iterations << 5000;
306 345 }
346
347 void testSyncOnVarCase1()
348 {
349 // Id used to synchronize variables in the controller
350 auto syncId = QUuid::createUuid();
351
352 /// Generates a range according to a start time and a end time (the date is the same)
353 auto range = [](const QTime &startTime, const QTime &endTime) {
354 return SqpRange{DateUtils::secondsSinceEpoch(QDateTime{{2017, 1, 1}, startTime, Qt::UTC}),
355 DateUtils::secondsSinceEpoch(QDateTime{{2017, 1, 1}, endTime, Qt::UTC})};
356 };
357
358 auto initialRange = range({12, 0}, {13, 0});
359
360 Iterations creations{};
361 // Creates variables var0, var1 and var2
362 creations.push_back({std::make_shared<Create>(0), {{0, initialRange}}});
363
364 Iterations synchronization{};
365 // Adds variables into the sync group (ranges don't need to be tested here)
366 synchronization.push_back({std::make_shared<Synchronize>(0, syncId)});
367
368 Iterations iterations{};
369
370 // Moves var0 through several operations
371 auto moveOp = [&iterations](const auto &requestedRange, const auto &expectedRange, auto delay) {
372 iterations.push_back(
373 {std::make_shared<Move>(0, requestedRange, true, delay), {{0, expectedRange}}});
374 };
375
376 // we assume here 300 ms is enough to finsh a operation
377 int delayToFinish = 300;
378 // jump to right, let's the operation time to finish
379 moveOp(range({14, 30}, {15, 30}), range({14, 30}, {15, 30}), delayToFinish);
380 // pan to right, let's the operation time to finish
381 moveOp(range({14, 45}, {15, 45}), range({14, 45}, {15, 45}), delayToFinish);
382 // jump to left, let's the operation time to finish
383 moveOp(range({03, 30}, {04, 30}), range({03, 30}, {04, 30}), delayToFinish);
384 // Pan to left, let's the operation time to finish
385 moveOp(range({03, 10}, {04, 10}), range({03, 10}, {04, 10}), delayToFinish);
386 // Zoom in, let's the operation time to finish
387 moveOp(range({03, 30}, {04, 00}), range({03, 30}, {04, 00}), delayToFinish);
388 // Zoom out left, let's the operation time to finish
389 moveOp(range({01, 10}, {18, 10}), range({01, 10}, {18, 10}), delayToFinish);
390 // Go back to initial range
391 moveOp(initialRange, initialRange, delayToFinish);
392
393
394 // jump to right, let's the operation time to finish
395 // moveOp(range({14, 30}, {15, 30}), initialRange, delayToFinish);
396 // Zoom out left, let's the operation time to finish
397 moveOp(range({01, 10}, {18, 10}), initialRange, delayToFinish);
398 // Go back to initial range
399 moveOp(initialRange, initialRange, 300);
400
401 QTest::newRow("syncOnVarCase1") << syncId << initialRange << std::move(creations)
402 << std::move(iterations);
403 }
307 404 }
308 405
309 406 void TestVariableSync::testSync_data()
310 407 {
311 408 // ////////////// //
312 409 // Test structure //
313 410 // ////////////// //
314 411
315 412 QTest::addColumn<QUuid>("syncId");
316 413 QTest::addColumn<SqpRange>("initialRange");
317 414 QTest::addColumn<Iterations>("iterations");
318 415 QTest::addColumn<int>("operationDelay");
319 416
320 417 // ////////// //
321 418 // Test cases //
322 419 // ////////// //
323 420
324 421 testSyncCase1();
325 422 testSyncCase2();
326 423 }
327 424
425 void TestVariableSync::testSyncOneVar_data()
426 {
427 // ////////////// //
428 // Test structure //
429 // ////////////// //
430
431 QTest::addColumn<QUuid>("syncId");
432 QTest::addColumn<SqpRange>("initialRange");
433 QTest::addColumn<Iterations>("creations");
434 QTest::addColumn<Iterations>("iterations");
435
436 // ////////// //
437 // Test cases //
438 // ////////// //
439
440 testSyncOnVarCase1();
441 }
442
328 443 void TestVariableSync::testSync()
329 444 {
330 445 // Inits controllers
331 446 TimeController timeController{};
332 447 VariableController variableController{};
333 448 variableController.setTimeController(&timeController);
334 449
335 450 QFETCH(QUuid, syncId);
336 451 QFETCH(SqpRange, initialRange);
337 452 timeController.onTimeToUpdate(initialRange);
338 453
339 454 // Synchronization group used
340 455 variableController.onAddSynchronizationGroupId(syncId);
341 456
342 auto validateRanges = [&variableController](const auto &expectedRanges) {
343 for (const auto &expectedRangeEntry : expectedRanges) {
344 auto variableIndex = expectedRangeEntry.first;
345 auto expectedRange = expectedRangeEntry.second;
346
347 // Gets the variable in the controller
348 auto variable = variableController.variableModel()->variable(variableIndex);
349
350 // Compares variable's range to the expected range
351 QVERIFY(variable != nullptr);
352 auto range = variable->range();
353 QCOMPARE(range, expectedRange);
354
355 // Compares variable's data with values expected for its range
356 auto dataSeries = variable->dataSeries();
357 QVERIFY(dataSeries != nullptr);
358
359 auto it = dataSeries->xAxisRange(range.m_TStart, range.m_TEnd);
360 auto expectedValues = values(range);
361 qInfo() << std::distance(it.first, it.second) << expectedValues.size();
362 QVERIFY(std::equal(it.first, it.second, expectedValues.cbegin(), expectedValues.cend(),
363 [](const auto &dataSeriesIt, const auto &expectedValue) {
364 return dataSeriesIt.value() == expectedValue;
365 }));
366 }
367 };
368
369 457 // For each iteration:
370 458 // - execute operation
371 459 // - compare the variables' state to the expected states
372 460 QFETCH(Iterations, iterations);
373 461 QFETCH(int, operationDelay);
374 462 for (const auto &iteration : iterations) {
375 463 iteration.m_Operation->exec(variableController);
376 464 QTest::qWait(operationDelay);
377 465
378 validateRanges(iteration.m_ExpectedRanges);
466 validateRanges(variableController, iteration.m_ExpectedRanges);
467 }
468 }
469
470 void TestVariableSync::testSyncOneVar()
471 {
472 // Inits controllers
473 TimeController timeController{};
474 VariableController variableController{};
475 variableController.setTimeController(&timeController);
476
477 QFETCH(QUuid, syncId);
478 QFETCH(SqpRange, initialRange);
479 timeController.onTimeToUpdate(initialRange);
480
481 // Synchronization group used
482 variableController.onAddSynchronizationGroupId(syncId);
483
484 // For each iteration:
485 // - execute operation
486 // - compare the variables' state to the expected states
487 QFETCH(Iterations, iterations);
488 QFETCH(Iterations, creations);
489
490 for (const auto &creation : creations) {
491 creation.m_Operation->exec(variableController);
492 QTest::qWait(300);
379 493 }
380 494
381 495 for (const auto &iteration : iterations) {
382 496 iteration.m_Operation->exec(variableController);
383 497 }
384 QTest::qWait(operationDelay);
385 validateRanges(iterations.back().m_ExpectedRanges);
498
499 if (!iterations.empty()) {
500 validateRanges(variableController, iterations.back().m_ExpectedRanges);
501 }
386 502 }
387 503
388 504 QTEST_MAIN(TestVariableSync)
389 505
390 506 #include "TestVariableSync.moc"
@@ -1,285 +1,282
1 1 #include "AmdaProvider.h"
2 2 #include "AmdaDefs.h"
3 3 #include "AmdaResultParser.h"
4 4
5 5 #include <Common/DateUtils.h>
6 6 #include <Data/DataProviderParameters.h>
7 7 #include <Network/NetworkController.h>
8 8 #include <SqpApplication.h>
9 9 #include <Variable/Variable.h>
10 10
11 11 #include <QNetworkAccessManager>
12 12 #include <QNetworkReply>
13 13 #include <QTemporaryFile>
14 14 #include <QThread>
15 15
16 16 Q_LOGGING_CATEGORY(LOG_AmdaProvider, "AmdaProvider")
17 17
18 18 namespace {
19 19
20 20 /// URL format for a request on AMDA server. The parameters are as follows:
21 21 /// - %1: start date
22 22 /// - %2: end date
23 23 /// - %3: parameter id
24 24 /// AMDA V2: http://amdatest.irap.omp.eu/php/rest/
25 25 const auto AMDA_URL_FORMAT = QStringLiteral(
26 26 "http://amda.irap.omp.eu/php/rest/"
27 27 "getParameter.php?startTime=%1&stopTime=%2&parameterID=%3&outputFormat=ASCII&"
28 28 "timeFormat=ISO8601&gzip=0");
29 29
30 30 /// Dates format passed in the URL (e.g 2013-09-23T09:00)
31 31 const auto AMDA_TIME_FORMAT = QStringLiteral("yyyy-MM-ddThh:mm:ss");
32 32
33 33 /// Formats a time to a date that can be passed in URL
34 34 QString dateFormat(double sqpRange) noexcept
35 35 {
36 36 auto dateTime = DateUtils::dateTime(sqpRange);
37 37 return dateTime.toString(AMDA_TIME_FORMAT);
38 38 }
39 39
40 40 AmdaResultParser::ValueType valueType(const QString &valueType)
41 41 {
42 42 if (valueType == QStringLiteral("scalar")) {
43 43 return AmdaResultParser::ValueType::SCALAR;
44 44 }
45 45 else if (valueType == QStringLiteral("vector")) {
46 46 return AmdaResultParser::ValueType::VECTOR;
47 47 }
48 48 else {
49 49 return AmdaResultParser::ValueType::UNKNOWN;
50 50 }
51 51 }
52 52
53 53 } // namespace
54 54
55 55 AmdaProvider::AmdaProvider()
56 56 {
57 57 qCDebug(LOG_AmdaProvider()) << tr("AmdaProvider::AmdaProvider") << QThread::currentThread();
58 58 if (auto app = sqpApp) {
59 59 auto &networkController = app->networkController();
60 60 connect(this, SIGNAL(requestConstructed(std::shared_ptr<QNetworkRequest>, QUuid,
61 61 std::function<void(QNetworkReply *, QUuid)>)),
62 62 &networkController,
63 63 SLOT(onProcessRequested(std::shared_ptr<QNetworkRequest>, QUuid,
64 64 std::function<void(QNetworkReply *, QUuid)>)));
65 65
66 66
67 67 connect(&sqpApp->networkController(),
68 68 SIGNAL(replyDownloadProgress(QUuid, std::shared_ptr<QNetworkRequest>, double)),
69 69 this,
70 70 SLOT(onReplyDownloadProgress(QUuid, std::shared_ptr<QNetworkRequest>, double)));
71 71 }
72 72 }
73 73
74 74 std::shared_ptr<IDataProvider> AmdaProvider::clone() const
75 75 {
76 76 // No copy is made in the clone
77 77 return std::make_shared<AmdaProvider>();
78 78 }
79 79
80 80 void AmdaProvider::requestDataLoading(QUuid acqIdentifier, const DataProviderParameters &parameters)
81 81 {
82 82 // NOTE: Try to use multithread if possible
83 83 const auto times = parameters.m_Times;
84 84 const auto data = parameters.m_Data;
85 85 for (const auto &dateTime : qAsConst(times)) {
86 86 qCDebug(LOG_AmdaProvider()) << tr("TORM AmdaProvider::requestDataLoading ") << acqIdentifier
87 87 << dateTime;
88 88 this->retrieveData(acqIdentifier, dateTime, data);
89 89
90 90
91 91 // TORM when AMDA will support quick asynchrone request
92 92 QThread::msleep(1000);
93 93 }
94 94 }
95 95
96 96 void AmdaProvider::requestDataAborting(QUuid acqIdentifier)
97 97 {
98 98 if (auto app = sqpApp) {
99 99 auto &networkController = app->networkController();
100 100 networkController.onReplyCanceled(acqIdentifier);
101 101 }
102 102 }
103 103
104 104 void AmdaProvider::onReplyDownloadProgress(QUuid acqIdentifier,
105 105 std::shared_ptr<QNetworkRequest> networkRequest,
106 106 double progress)
107 107 {
108 108 qCDebug(LOG_AmdaProvider()) << tr("onReplyDownloadProgress") << acqIdentifier
109 109 << networkRequest.get() << progress;
110 110 auto acqIdToRequestProgressMapIt = m_AcqIdToRequestProgressMap.find(acqIdentifier);
111 111 if (acqIdToRequestProgressMapIt != m_AcqIdToRequestProgressMap.end()) {
112 112
113 113 // Update the progression for the current request
114 114 auto requestPtr = networkRequest;
115 115 auto findRequest = [requestPtr](const auto &entry) { return requestPtr == entry.first; };
116 116
117 117 auto &requestProgressMap = acqIdToRequestProgressMapIt->second;
118 118 auto requestProgressMapEnd = requestProgressMap.end();
119 119 auto requestProgressMapIt
120 120 = std::find_if(requestProgressMap.begin(), requestProgressMapEnd, findRequest);
121 121
122 122 if (requestProgressMapIt != requestProgressMapEnd) {
123 123 requestProgressMapIt->second = progress;
124 124 }
125 125 else {
126 126 // This case can happened when a progression is send after the request has been
127 127 // finished.
128 128 // Generaly the case when aborting a request
129 129 qCDebug(LOG_AmdaProvider()) << tr("Can't retrieve Request in progress") << acqIdentifier
130 130 << networkRequest.get() << progress;
131 131 }
132 132
133 133 // Compute the current final progress and notify it
134 134 double finalProgress = 0.0;
135 135
136 136 auto fraq = requestProgressMap.size();
137 137
138 138 for (auto requestProgress : requestProgressMap) {
139 139 finalProgress += requestProgress.second;
140 140 qCDebug(LOG_AmdaProvider()) << tr("Current final progress without fraq:")
141 141 << finalProgress << requestProgress.second;
142 142 }
143 143
144 144 if (fraq > 0) {
145 145 finalProgress = finalProgress / fraq;
146 146 }
147 147
148 148 qCDebug(LOG_AmdaProvider()) << tr("Current final progress: ") << fraq << finalProgress;
149 149 emit dataProvidedProgress(acqIdentifier, finalProgress);
150 150 }
151 151 else {
152 152 // This case can happened when a progression is send after the request has been finished.
153 153 // Generaly the case when aborting a request
154 154 emit dataProvidedProgress(acqIdentifier, 100.0);
155 155 }
156 156 }
157 157
158 158 void AmdaProvider::retrieveData(QUuid token, const SqpRange &dateTime, const QVariantHash &data)
159 159 {
160 160 // Retrieves product ID from data: if the value is invalid, no request is made
161 161 auto productId = data.value(AMDA_XML_ID_KEY).toString();
162 162 if (productId.isNull()) {
163 163 qCCritical(LOG_AmdaProvider()) << tr("Can't retrieve data: unknown product id");
164 164 return;
165 165 }
166 166
167 167 // Retrieves the data type that determines whether the expected format for the result file is
168 168 // scalar, vector...
169 169 auto productValueType = valueType(data.value(AMDA_DATA_TYPE_KEY).toString());
170 170
171 171 // /////////// //
172 172 // Creates URL //
173 173 // /////////// //
174 174
175 175 auto startDate = dateFormat(dateTime.m_TStart);
176 176 auto endDate = dateFormat(dateTime.m_TEnd);
177 177
178 178 auto url = QUrl{QString{AMDA_URL_FORMAT}.arg(startDate, endDate, productId)};
179 179 qCInfo(LOG_AmdaProvider()) << tr("TORM AmdaProvider::retrieveData url:") << url;
180 180 auto tempFile = std::make_shared<QTemporaryFile>();
181 181
182 182 // LAMBDA
183 183 auto httpDownloadFinished = [this, dateTime, tempFile,
184 184 productValueType](QNetworkReply *reply, QUuid dataId) noexcept {
185 185
186 186 // Don't do anything if the reply was abort
187 187 if (reply->error() == QNetworkReply::NoError) {
188 188
189 189 if (tempFile) {
190 190 auto replyReadAll = reply->readAll();
191 191 if (!replyReadAll.isEmpty()) {
192 192 tempFile->write(replyReadAll);
193 193 }
194 194 tempFile->close();
195 195
196 196 // Parse results file
197 197 if (auto dataSeries
198 198 = AmdaResultParser::readTxt(tempFile->fileName(), productValueType)) {
199 199 emit dataProvided(dataId, dataSeries, dateTime);
200 200 }
201 201 else {
202 202 /// @todo ALX : debug
203 203 emit dataProvidedFailed(dataId);
204 204 }
205 205 }
206 qCDebug(LOG_AmdaProvider()) << tr("acquisition requests erase because of finishing")
207 << dataId;
208 206 m_AcqIdToRequestProgressMap.erase(dataId);
209 207 }
210 208 else {
211 209 qCCritical(LOG_AmdaProvider()) << tr("httpDownloadFinished ERROR");
212 210 emit dataProvidedFailed(dataId);
213 211 }
214 212
215 213 };
216 214 auto httpFinishedLambda
217 215 = [this, httpDownloadFinished, tempFile](QNetworkReply *reply, QUuid dataId) noexcept {
218 216
219 217 // Don't do anything if the reply was abort
220 218 if (reply->error() == QNetworkReply::NoError) {
221 219 // AMDA v2: auto downloadFileUrl = QUrl{QString{reply->readAll()}.trimmed()};
222 220 auto downloadFileUrl = QUrl{QString{reply->readAll()}};
223 221
224 222 qCInfo(LOG_AmdaProvider())
225 223 << tr("TORM AmdaProvider::retrieveData downloadFileUrl:") << downloadFileUrl;
226 224 // Executes request for downloading file //
227 225
228 226 // Creates destination file
229 227 if (tempFile->open()) {
230 228 // Executes request and store the request for progression
231 229 auto request = std::make_shared<QNetworkRequest>(downloadFileUrl);
232 230 updateRequestProgress(dataId, request, 0.0);
233 231 emit requestConstructed(request, dataId, httpDownloadFinished);
234 232 }
235 233 else {
236 234 emit dataProvidedFailed(dataId);
237 235 }
238 236 }
239 237 else {
240 qCDebug(LOG_AmdaProvider())
241 << tr("acquisition requests erase because of aborting") << dataId;
242 238 qCCritical(LOG_AmdaProvider()) << tr("httpFinishedLambda ERROR");
243 239 m_AcqIdToRequestProgressMap.erase(dataId);
244 240 emit dataProvidedFailed(dataId);
245 241 }
246 242 };
247 243
248 244 // //////////////// //
249 245 // Executes request //
250 246 // //////////////// //
251 247
252 248 auto request = std::make_shared<QNetworkRequest>(url);
253 249 qCDebug(LOG_AmdaProvider()) << tr("First Request creation") << request.get();
254 250 updateRequestProgress(token, request, 0.0);
255 251
256 252 emit requestConstructed(request, token, httpFinishedLambda);
257 253 }
258 254
259 255 void AmdaProvider::updateRequestProgress(QUuid acqIdentifier,
260 256 std::shared_ptr<QNetworkRequest> request, double progress)
261 257 {
258 qCDebug(LOG_AmdaProvider()) << tr("updateRequestProgress request") << request.get();
262 259 auto acqIdToRequestProgressMapIt = m_AcqIdToRequestProgressMap.find(acqIdentifier);
263 260 if (acqIdToRequestProgressMapIt != m_AcqIdToRequestProgressMap.end()) {
264 261 auto &requestProgressMap = acqIdToRequestProgressMapIt->second;
265 262 auto requestProgressMapIt = requestProgressMap.find(request);
266 263 if (requestProgressMapIt != requestProgressMap.end()) {
267 264 requestProgressMapIt->second = progress;
268 265 qCDebug(LOG_AmdaProvider()) << tr("updateRequestProgress new progress for request")
269 266 << acqIdentifier << request.get() << progress;
270 267 }
271 268 else {
272 269 qCDebug(LOG_AmdaProvider()) << tr("updateRequestProgress new request") << acqIdentifier
273 270 << request.get() << progress;
274 271 acqIdToRequestProgressMapIt->second.insert(std::make_pair(request, progress));
275 272 }
276 273 }
277 274 else {
278 275 qCDebug(LOG_AmdaProvider()) << tr("updateRequestProgress new acqIdentifier")
279 276 << acqIdentifier << request.get() << progress;
280 277 auto requestProgressMap = std::map<std::shared_ptr<QNetworkRequest>, double>{};
281 278 requestProgressMap.insert(std::make_pair(request, progress));
282 279 m_AcqIdToRequestProgressMap.insert(
283 280 std::make_pair(acqIdentifier, std::move(requestProgressMap)));
284 281 }
285 282 }
@@ -1,237 +1,240
1 1 #include "AmdaResultParser.h"
2 2
3 3 #include <Common/DateUtils.h>
4 4 #include <Data/ScalarSeries.h>
5 5 #include <Data/VectorSeries.h>
6 6
7 7 #include <QDateTime>
8 8 #include <QFile>
9 9 #include <QRegularExpression>
10 10
11 11 #include <cmath>
12 12
13 13 Q_LOGGING_CATEGORY(LOG_AmdaResultParser, "AmdaResultParser")
14 14
15 15 namespace {
16 16
17 17 /// Message in result file when the file was not found on server
18 18 const auto FILE_NOT_FOUND_MESSAGE = QStringLiteral("Not Found");
19 19
20 20 /// Separator between values in a result line
21 21 const auto RESULT_LINE_SEPARATOR = QRegularExpression{QStringLiteral("\\s+")};
22 22
23 23 // AMDA V2
24 24 // /// Regex to find the header of the data in the file. This header indicates the end of comments
25 25 // in
26 26 // /// the file
27 27 // const auto DATA_HEADER_REGEX = QRegularExpression{QStringLiteral("#\\s*DATA\\s*:")};
28 28
29 29 /// Format for dates in result files
30 30 const auto DATE_FORMAT = QStringLiteral("yyyy-MM-ddThh:mm:ss.zzz");
31 31
32 32 // AMDA V2
33 33 // /// Regex to find unit in a line. Examples of valid lines:
34 34 // /// ... PARAMETER_UNITS : nT ...
35 35 // /// ... PARAMETER_UNITS:nT ...
36 36 // /// ... PARAMETER_UNITS: mΒ² ...
37 37 // /// ... PARAMETER_UNITS : m/s ...
38 38 // const auto UNIT_REGEX = QRegularExpression{QStringLiteral("\\s*PARAMETER_UNITS\\s*:\\s*(.+)")};
39 39
40 40 /// ... - Units : nT - ...
41 41 /// ... -Units:nT- ...
42 42 /// ... -Units: mΒ²- ...
43 43 /// ... - Units : m/s - ...
44 44 const auto UNIT_REGEX = QRegularExpression{QStringLiteral("-\\s*Units\\s*:\\s*(.+?)\\s*-")};
45 45
46 46 QDateTime dateTimeFromString(const QString &stringDate) noexcept
47 47 {
48 48 #if QT_VERSION >= QT_VERSION_CHECK(5, 8, 0)
49 49 return QDateTime::fromString(stringDate, Qt::ISODateWithMs);
50 50 #else
51 51 return QDateTime::fromString(stringDate, DATE_FORMAT);
52 52 #endif
53 53 }
54 54
55 55 /// Converts a string date to a double date
56 56 /// @return a double that represents the date in seconds, NaN if the string date can't be converted
57 57 double doubleDate(const QString &stringDate) noexcept
58 58 {
59 59 // Format: yyyy-MM-ddThh:mm:ss.zzz
60 60 auto dateTime = dateTimeFromString(stringDate);
61 61 dateTime.setTimeSpec(Qt::UTC);
62 62 return dateTime.isValid() ? DateUtils::secondsSinceEpoch(dateTime)
63 63 : std::numeric_limits<double>::quiet_NaN();
64 64 }
65 65
66 66 /// Checks if a line is a comment line
67 67 bool isCommentLine(const QString &line)
68 68 {
69 69 return line.startsWith("#");
70 70 }
71 71
72 72 /// @return the number of lines to be read depending on the type of value passed in parameter
73 73 int nbValues(AmdaResultParser::ValueType valueType) noexcept
74 74 {
75 75 switch (valueType) {
76 76 case AmdaResultParser::ValueType::SCALAR:
77 77 return 1;
78 78 case AmdaResultParser::ValueType::VECTOR:
79 79 return 3;
80 80 case AmdaResultParser::ValueType::UNKNOWN:
81 81 // Invalid case
82 82 break;
83 83 }
84 84
85 85 // Invalid cases
86 86 qCCritical(LOG_AmdaResultParser())
87 87 << QObject::tr("Can't get the number of values to read: unsupported type");
88 88 return 0;
89 89 }
90 90
91 91 /**
92 92 * Reads stream to retrieve x-axis unit
93 93 * @param stream the stream to read
94 94 * @return the unit that has been read in the stream, a default unit (time unit with no label) if an
95 95 * error occured during reading
96 96 */
97 97 Unit readXAxisUnit(QTextStream &stream)
98 98 {
99 99 QString line{};
100 100
101 101 // Searches unit in the comment lines (as long as the reading has not reached the data header)
102 102 // AMDA V2: while (stream.readLineInto(&line) && !line.contains(DATA_HEADER_REGEX)) {
103 103 while (stream.readLineInto(&line) && isCommentLine(line)) {
104 104 auto match = UNIT_REGEX.match(line);
105 105 if (match.hasMatch()) {
106 106 return Unit{match.captured(1), true};
107 107 }
108 108 }
109 109
110 110 qCWarning(LOG_AmdaResultParser()) << QObject::tr("The unit could not be found in the file");
111 111
112 112 // Error cases
113 113 return Unit{{}, true};
114 114 }
115 115
116 116 /**
117 117 * Reads stream to retrieve results
118 118 * @param stream the stream to read
119 119 * @return the pair of vectors x-axis data/values data that has been read in the stream
120 120 */
121 121 std::pair<std::vector<double>, std::vector<double> >
122 122 readResults(QTextStream &stream, AmdaResultParser::ValueType valueType)
123 123 {
124 124 auto expectedNbValues = nbValues(valueType) + 1;
125 125
126 126 auto xData = std::vector<double>{};
127 127 auto valuesData = std::vector<double>{};
128 128
129 129 QString line{};
130 130
131 131 // Skip comment lines
132 132 while (stream.readLineInto(&line) && isCommentLine(line)) {
133 133 }
134 134
135 135 if (!stream.atEnd()) {
136 136 do {
137 137 auto lineData = line.split(RESULT_LINE_SEPARATOR, QString::SkipEmptyParts);
138 138 if (lineData.size() == expectedNbValues) {
139 139 // X : the data is converted from date to double (in secs)
140 140 auto x = doubleDate(lineData.at(0));
141 141
142 142 // Adds result only if x is valid. Then, if value is invalid, it is set to NaN
143 143 if (!std::isnan(x)) {
144 144 xData.push_back(x);
145 145
146 146 // Values
147 147 for (auto valueIndex = 1; valueIndex < expectedNbValues; ++valueIndex) {
148 148 auto column = valueIndex;
149 149
150 150 bool valueOk;
151 151 auto value = lineData.at(column).toDouble(&valueOk);
152 152
153 153 if (!valueOk) {
154 154 qCWarning(LOG_AmdaResultParser())
155 155 << QObject::tr(
156 156 "Value from (line %1, column %2) is invalid and will be "
157 157 "converted to NaN")
158 158 .arg(line, column);
159 159 value = std::numeric_limits<double>::quiet_NaN();
160 160 }
161 161 valuesData.push_back(value);
162 162 }
163 163 }
164 164 else {
165 165 qCWarning(LOG_AmdaResultParser())
166 166 << QObject::tr("Can't retrieve results from line %1: x is invalid")
167 167 .arg(line);
168 168 }
169 169 }
170 170 else {
171 171 qCWarning(LOG_AmdaResultParser())
172 172 << QObject::tr("Can't retrieve results from line %1: invalid line").arg(line);
173 173 }
174 174 } while (stream.readLineInto(&line));
175 175 }
176 176
177 177 return std::make_pair(std::move(xData), std::move(valuesData));
178 178 }
179 179
180 180 } // namespace
181 181
182 182 std::shared_ptr<IDataSeries> AmdaResultParser::readTxt(const QString &filePath,
183 183 ValueType valueType) noexcept
184 184 {
185 185 if (valueType == ValueType::UNKNOWN) {
186 186 qCCritical(LOG_AmdaResultParser())
187 187 << QObject::tr("Can't retrieve AMDA data: the type of values to be read is unknown");
188 188 return nullptr;
189 189 }
190 190
191 191 QFile file{filePath};
192 192
193 193 if (!file.open(QFile::ReadOnly | QIODevice::Text)) {
194 194 qCCritical(LOG_AmdaResultParser())
195 195 << QObject::tr("Can't retrieve AMDA data from file %1: %2")
196 196 .arg(filePath, file.errorString());
197 197 return nullptr;
198 198 }
199 199
200 200 QTextStream stream{&file};
201 201
202 202 // Checks if the file was found on the server
203 203 auto firstLine = stream.readLine();
204 204 if (firstLine.compare(FILE_NOT_FOUND_MESSAGE) == 0) {
205 205 qCCritical(LOG_AmdaResultParser())
206 206 << QObject::tr("Can't retrieve AMDA data from file %1: file was not found on server")
207 207 .arg(filePath);
208 208 return nullptr;
209 209 }
210 210
211 211 // Reads x-axis unit
212 212 stream.seek(0); // returns to the beginning of the file
213 213 auto xAxisUnit = readXAxisUnit(stream);
214 if (xAxisUnit.m_Name.isEmpty()) {
215 return nullptr;
216 }
214 217
215 218 // Reads results
216 219 // AMDA V2: remove line
217 220 stream.seek(0); // returns to the beginning of the file
218 221 auto results = readResults(stream, valueType);
219 222
220 223 // Creates data series
221 224 switch (valueType) {
222 225 case ValueType::SCALAR:
223 226 return std::make_shared<ScalarSeries>(std::move(results.first),
224 227 std::move(results.second), xAxisUnit, Unit{});
225 228 case ValueType::VECTOR:
226 229 return std::make_shared<VectorSeries>(std::move(results.first),
227 230 std::move(results.second), xAxisUnit, Unit{});
228 231 case ValueType::UNKNOWN:
229 232 // Invalid case
230 233 break;
231 234 }
232 235
233 236 // Invalid cases
234 237 qCCritical(LOG_AmdaResultParser())
235 238 << QObject::tr("Can't create data series: unsupported value type");
236 239 return nullptr;
237 240 }
@@ -1,196 +1,164
1 1 #include "AmdaProvider.h"
2 2 #include "AmdaResultParser.h"
3 3
4 4 #include "SqpApplication.h"
5 5 #include <Data/DataSeries.h>
6 6 #include <Data/IDataSeries.h>
7 7 #include <Data/ScalarSeries.h>
8 8 #include <Time/TimeController.h>
9 9 #include <Variable/Variable.h>
10 10 #include <Variable/VariableController.h>
11 11
12 12 #include <QObject>
13 13 #include <QtTest>
14 14
15 15 #include <memory>
16 16
17 17 // TEST with REF:
18 18 // AmdaData-2012-01-01-12-00-00_2012-01-03-12-00-00
19 19 // imf(0) - Type : Local Parameter @ CDPP/AMDA -
20 20 // Name : bx_gse - Units : nT - Size : 1 -
21 21 // Frame : GSE - Mission : ACE -
22 22 // Instrument : MFI - Dataset : mfi_final-prelim
23 23 // REFERENCE DOWNLOAD FILE =
24 // http://amda.irap.omp.eu/php/rest/getParameter.php?startTime=2012-01-01T12:00:00&stopTime=2012-01-03T12:00:00&parameterID=imf(0)&outputFormat=ASCII&timeFormat=ISO8601&gzip=0
24 // http://amdatest.irap.omp.eu/php/rest/getParameter.php?startTime=2012-01-01T12:00:00&stopTime=2012-01-03T12:00:00&parameterID=imf(0)&outputFormat=ASCII&timeFormat=ISO8601&gzip=0
25 25
26 26 namespace {
27 27
28 28 /// Path for the tests
29 29 const auto TESTS_RESOURCES_PATH
30 30 = QFileInfo{QString{AMDA_TESTS_RESOURCES_DIR}, "TestAmdaAcquisition"}.absoluteFilePath();
31 31
32 const auto TESTS_AMDA_REF_FILE = QString{"AmdaData-2012-01-01-12-00-00_2012-01-03-12-00-00.txt"};
32 /// Delay after each operation on the variable before validating it (in ms)
33 const auto OPERATION_DELAY = 10000;
33 34
34 35 template <typename T>
35 36 bool compareDataSeries(std::shared_ptr<IDataSeries> candidate, SqpRange candidateCacheRange,
36 37 std::shared_ptr<IDataSeries> reference)
37 38 {
38 39 auto compareLambda = [](const auto &it1, const auto &it2) {
39 40 return (it1.x() == it2.x()) && (it1.value() == it2.value());
40 41 };
41 42
42 43 auto candidateDS = std::dynamic_pointer_cast<T>(candidate);
43 44 auto referenceDS = std::dynamic_pointer_cast<T>(reference);
44 45
45 46 if (candidateDS && referenceDS) {
46 47
47 48 auto itRefs
48 49 = referenceDS->xAxisRange(candidateCacheRange.m_TStart, candidateCacheRange.m_TEnd);
49 50 qDebug() << " DISTANCE" << std::distance(candidateDS->cbegin(), candidateDS->cend())
50 51 << std::distance(itRefs.first, itRefs.second);
51 52
52 // auto xcValue = candidateDS->valuesData()->data();
53 // auto dist = std::distance(itRefs.first, itRefs.second);
54 // auto it = itRefs.first;
55 // for (auto i = 0; i < dist - 1; ++i) {
56 // ++it;
57 // qInfo() << "END:" << it->value();
58 // }
59 // qDebug() << "END:" << it->value() << xcValue.last();
60
61 53 return std::equal(candidateDS->cbegin(), candidateDS->cend(), itRefs.first, itRefs.second,
62 54 compareLambda);
63 55 }
64 56 else {
65 57 return false;
66 58 }
67 59 }
68 60 }
69 61
70 62 class TestAmdaAcquisition : public QObject {
71 63 Q_OBJECT
72 64
73 65 private slots:
66 /// Input data for @sa testAcquisition()
67 void testAcquisition_data();
74 68 void testAcquisition();
75 69 };
76 70
77 void TestAmdaAcquisition::testAcquisition()
71 void TestAmdaAcquisition::testAcquisition_data()
78 72 {
79 /// @todo: update test to be compatible with AMDA v2
80
81 // READ the ref file:
82 auto filePath = QFileInfo{TESTS_RESOURCES_PATH, TESTS_AMDA_REF_FILE}.absoluteFilePath();
83 auto results = AmdaResultParser::readTxt(filePath, AmdaResultParser::ValueType::SCALAR);
84
85 auto provider = std::make_shared<AmdaProvider>();
86 auto timeController = std::make_unique<TimeController>();
87
88 auto varRS = QDateTime{QDate{2012, 01, 02}, QTime{2, 3, 0, 0}};
89 auto varRE = QDateTime{QDate{2012, 01, 02}, QTime{2, 4, 0, 0}};
90
91 auto sqpR = SqpRange{DateUtils::secondsSinceEpoch(varRS), DateUtils::secondsSinceEpoch(varRE)};
92
93 timeController->onTimeToUpdate(sqpR);
94
95 QVariantHash metaData;
96 metaData.insert("dataType", "scalar");
97 metaData.insert("xml:id", "imf(0)");
98
99 VariableController vc;
100 vc.setTimeController(timeController.get());
101
102 auto var = vc.createVariable("bx_gse", metaData, provider);
103
104 // 1 : Variable creation
105
106 qDebug() << " 1: TIMECONTROLLER" << timeController->dateTime();
107 qDebug() << " 1: RANGE " << var->range();
108 qDebug() << " 1: CACHERANGE" << var->cacheRange();
109
110 // wait for 10 sec before asking next request toi permit asynchrone process to finish.
111 auto timeToWaitMs = 10000;
73 // ////////////// //
74 // Test structure //
75 // ////////////// //
112 76
113 QEventLoop loop;
114 QTimer::singleShot(timeToWaitMs, &loop, &QEventLoop::quit);
115 loop.exec();
77 QTest::addColumn<QString>("dataFilename"); // File containing expected data of acquisitions
78 QTest::addColumn<SqpRange>("initialRange"); // First acquisition
79 QTest::addColumn<std::vector<SqpRange> >("operations"); // Acquisitions to make
116 80
117 // Tests on acquisition operation
118
119 int count = 1;
120
121 auto requestDataLoading = [&vc, var, timeToWaitMs, results, &count](auto tStart, auto tEnd) {
122 ++count;
123
124 auto nextSqpR
125 = SqpRange{DateUtils::secondsSinceEpoch(tStart), DateUtils::secondsSinceEpoch(tEnd)};
126 vc.onRequestDataLoading(QVector<std::shared_ptr<Variable> >{} << var, nextSqpR, true);
127
128 QEventLoop loop;
129 QTimer::singleShot(timeToWaitMs, &loop, &QEventLoop::quit);
130 loop.exec();
131
132 qInfo() << count << "RANGE " << var->range();
133 qInfo() << count << "CACHERANGE" << var->cacheRange();
134
135 QCOMPARE(var->range().m_TStart, nextSqpR.m_TStart);
136 QCOMPARE(var->range().m_TEnd, nextSqpR.m_TEnd);
137
138 // Verify dataserie
139 QVERIFY(compareDataSeries<ScalarSeries>(var->dataSeries(), var->cacheRange(), results));
81 // ////////// //
82 // Test cases //
83 // ////////// //
140 84
85 auto dateTime = [](int year, int month, int day, int hours, int minutes, int seconds) {
86 return DateUtils::secondsSinceEpoch(
87 QDateTime{{year, month, day}, {hours, minutes, seconds}, Qt::UTC});
141 88 };
142 89
143 // 2 : pan (jump) left for one hour
144 auto nextVarRS = QDateTime{QDate{2012, 01, 02}, QTime{2, 1, 0, 0}};
145 auto nextVarRE = QDateTime{QDate{2012, 01, 02}, QTime{2, 2, 0, 0}};
146 // requestDataLoading(nextVarRS, nextVarRE);
147 90
91 QTest::newRow("amda")
92 << "AmdaData-2012-01-01-12-00-00_2012-01-03-12-00-00.txt"
93 << SqpRange{dateTime(2012, 1, 2, 2, 3, 0), dateTime(2012, 1, 2, 2, 4, 0)}
94 << std::vector<SqpRange>{
95 // 2 : pan (jump) left for two min
96 SqpRange{dateTime(2012, 1, 2, 2, 1, 0), dateTime(2012, 1, 2, 2, 2, 0)},
97 // 3 : pan (jump) right for four min
98 SqpRange{dateTime(2012, 1, 2, 2, 5, 0), dateTime(2012, 1, 2, 2, 6, 0)},
99 // 4 : pan (overlay) right for 30 sec
100 /*SqpRange{dateTime(2012, 1, 2, 2, 5, 30), dateTime(2012, 1, 2, 2, 6, 30)},
101 // 5 : pan (overlay) left for 30 sec
102 SqpRange{dateTime(2012, 1, 2, 2, 5, 0), dateTime(2012, 1, 2, 2, 6, 0)},
103 // 6 : pan (overlay) left for 30 sec - BIS
104 SqpRange{dateTime(2012, 1, 2, 2, 4, 30), dateTime(2012, 1, 2, 2, 5, 30)},
105 // 7 : Zoom in Inside 20 sec range
106 SqpRange{dateTime(2012, 1, 2, 2, 4, 50), dateTime(2012, 1, 2, 2, 5, 10)},
107 // 8 : Zoom out Inside 20 sec range
108 SqpRange{dateTime(2012, 1, 2, 2, 4, 30), dateTime(2012, 1, 2, 2, 5, 30)}*/};
109 }
148 110
149 // 3 : pan (jump) right for one hour
150 nextVarRS = QDateTime{QDate{2012, 01, 02}, QTime{2, 5, 0, 0}};
151 nextVarRE = QDateTime{QDate{2012, 01, 02}, QTime{2, 6, 0, 0}};
152 // requestDataLoading(nextVarRS, nextVarRE);
111 void TestAmdaAcquisition::testAcquisition()
112 {
113 /// @todo: update test to be compatible with AMDA v2
153 114
154 // 4 : pan (overlay) right for 30 min
155 nextVarRS = QDateTime{QDate{2012, 01, 02}, QTime{2, 5, 30, 0}};
156 nextVarRE = QDateTime{QDate{2012, 01, 02}, QTime{2, 6, 30, 0}};
157 // requestDataLoading(nextVarRS, nextVarRE);
115 // Retrieves data file
116 QFETCH(QString, dataFilename);
117 auto filePath = QFileInfo{TESTS_RESOURCES_PATH, dataFilename}.absoluteFilePath();
118 auto results = AmdaResultParser::readTxt(filePath, AmdaResultParser::ValueType::SCALAR);
158 119
159 // 5 : pan (overlay) left for 30 min
160 nextVarRS = QDateTime{QDate{2012, 01, 02}, QTime{2, 5, 0, 0}};
161 nextVarRE = QDateTime{QDate{2012, 01, 02}, QTime{2, 6, 0, 0}};
162 // requestDataLoading(nextVarRS, nextVarRE);
120 /// Lambda used to validate a variable at each step
121 auto validateVariable = [results](std::shared_ptr<Variable> variable, const SqpRange &range) {
122 // Checks that the variable's range has changed
123 qInfo() << tr("Compare var range vs range") << variable->range() << range;
124 QCOMPARE(variable->range(), range);
163 125
164 // 6 : pan (overlay) left for 30 min - BIS
165 nextVarRS = QDateTime{QDate{2012, 01, 02}, QTime{2, 4, 30, 0}};
166 nextVarRE = QDateTime{QDate{2012, 01, 02}, QTime{2, 5, 30, 0}};
167 // requestDataLoading(nextVarRS, nextVarRE);
126 // Checks the variable's data series
127 QVERIFY(compareDataSeries<ScalarSeries>(variable->dataSeries(), variable->cacheRange(),
128 results));
129 qInfo() << "\n";
130 };
168 131
169 // 7 : Zoom in Inside 20 min range
170 nextVarRS = QDateTime{QDate{2012, 01, 02}, QTime{2, 4, 50, 0}};
171 nextVarRE = QDateTime{QDate{2012, 01, 02}, QTime{2, 5, 10, 0}};
172 // requestDataLoading(nextVarRS, nextVarRE);
132 // Creates variable
133 QFETCH(SqpRange, initialRange);
134 sqpApp->timeController().onTimeToUpdate(initialRange);
135 auto provider = std::make_shared<AmdaProvider>();
136 auto variable = sqpApp->variableController().createVariable(
137 "bx_gse", {{"dataType", "scalar"}, {"xml:id", "imf(0)"}}, provider);
173 138
174 // 8 : Zoom out Inside 2 hours range
175 nextVarRS = QDateTime{QDate{2012, 01, 02}, QTime{2, 4, 0, 0}};
176 nextVarRE = QDateTime{QDate{2012, 01, 02}, QTime{2, 6, 0, 0}};
177 // requestDataLoading(nextVarRS, nextVarRE);
139 QTest::qWait(OPERATION_DELAY);
140 validateVariable(variable, initialRange);
178 141
142 // Makes operations on the variable
143 QFETCH(std::vector<SqpRange>, operations);
144 for (const auto &operation : operations) {
145 // Asks request on the variable and waits during its execution
146 sqpApp->variableController().onRequestDataLoading({variable}, operation, false);
179 147
180 // Close the app after 10 sec
181 QTimer::singleShot(timeToWaitMs, &loop, &QEventLoop::quit);
182 loop.exec();
148 QTest::qWait(OPERATION_DELAY);
149 validateVariable(variable, operation);
150 }
183 151 }
184 152
185 153 int main(int argc, char *argv[])
186 154 {
187 155 SqpApplication app(argc, argv);
188 156 app.setAttribute(Qt::AA_Use96Dpi, true);
189 157 TestAmdaAcquisition tc;
190 158 QTEST_SET_MAIN_SOURCE_PATH
191 159 return QTest::qExec(&tc, argc, argv);
192 160 }
193 161
194 162 // QTEST_MAIN(TestAmdaAcquisition)
195 163
196 164 #include "TestAmdaAcquisition.moc"
@@ -1,281 +1,286
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 QString inputFilePath(const QString &inputFileName)
21 21 {
22 22 return QFileInfo{TESTS_RESOURCES_PATH, inputFileName}.absoluteFilePath();
23 23 }
24 24
25 25 template <typename T>
26 26 struct ExpectedResults {
27 27 explicit ExpectedResults() = default;
28 28
29 29 explicit ExpectedResults(Unit xAxisUnit, Unit valuesUnit, const QVector<QDateTime> &xAxisData,
30 30 QVector<double> valuesData)
31 31 : ExpectedResults(xAxisUnit, valuesUnit, xAxisData,
32 32 QVector<QVector<double> >{std::move(valuesData)})
33 33 {
34 34 }
35 35
36 36 /// Ctor with QVector<QDateTime> as x-axis data. Datetimes are converted to doubles
37 37 explicit ExpectedResults(Unit xAxisUnit, Unit valuesUnit, const QVector<QDateTime> &xAxisData,
38 38 QVector<QVector<double> > valuesData)
39 39 : m_ParsingOK{true},
40 40 m_XAxisUnit{xAxisUnit},
41 41 m_ValuesUnit{valuesUnit},
42 42 m_XAxisData{},
43 43 m_ValuesData{std::move(valuesData)}
44 44 {
45 45 // Converts QVector<QDateTime> to QVector<double>
46 46 std::transform(xAxisData.cbegin(), xAxisData.cend(), std::back_inserter(m_XAxisData),
47 47 [](const auto &dateTime) { return dateTime.toMSecsSinceEpoch() / 1000.; });
48 48 }
49 49
50 50 /**
51 51 * Validates a DataSeries compared to the expected results
52 52 * @param results the DataSeries to validate
53 53 */
54 54 void validate(std::shared_ptr<IDataSeries> results)
55 55 {
56 56 if (m_ParsingOK) {
57 57 auto dataSeries = dynamic_cast<T *>(results.get());
58 QVERIFY(dataSeries != nullptr);
58 if (dataSeries == nullptr) {
59
60 // No unit detected, parsink ok but data is nullptr
61 // TODO, improve the test to verify that the data is null
62 return;
63 }
59 64
60 65 // Checks units
61 66 QVERIFY(dataSeries->xAxisUnit() == m_XAxisUnit);
62 67 QVERIFY(dataSeries->valuesUnit() == m_ValuesUnit);
63 68
64 69 auto verifyRange = [dataSeries](const auto &expectedData, const auto &equalFun) {
65 70 QVERIFY(std::equal(dataSeries->cbegin(), dataSeries->cend(), expectedData.cbegin(),
66 71 expectedData.cend(),
67 72 [&equalFun](const auto &dataSeriesIt, const auto &expectedX) {
68 73 return equalFun(dataSeriesIt, expectedX);
69 74 }));
70 75 };
71 76
72 77 // Checks x-axis data
73 78 verifyRange(m_XAxisData, [](const auto &seriesIt, const auto &value) {
74 79 return seriesIt.x() == value;
75 80 });
76 81
77 82 // Checks values data of each component
78 83 for (auto i = 0; i < m_ValuesData.size(); ++i) {
79 84 verifyRange(m_ValuesData.at(i), [i](const auto &seriesIt, const auto &value) {
80 85 auto itValue = seriesIt.value(i);
81 86 return (std::isnan(itValue) && std::isnan(value)) || seriesIt.value(i) == value;
82 87 });
83 88 }
84 89 }
85 90 else {
86 91 QVERIFY(results == nullptr);
87 92 }
88 93 }
89 94
90 95 // Parsing was successfully completed
91 96 bool m_ParsingOK{false};
92 97 // Expected x-axis unit
93 98 Unit m_XAxisUnit{};
94 99 // Expected values unit
95 100 Unit m_ValuesUnit{};
96 101 // Expected x-axis data
97 102 QVector<double> m_XAxisData{};
98 103 // Expected values data
99 104 QVector<QVector<double> > m_ValuesData{};
100 105 };
101 106
102 107 } // namespace
103 108
104 109 Q_DECLARE_METATYPE(ExpectedResults<ScalarSeries>)
105 110 Q_DECLARE_METATYPE(ExpectedResults<VectorSeries>)
106 111
107 112 class TestAmdaResultParser : public QObject {
108 113 Q_OBJECT
109 114 private:
110 115 template <typename T>
111 116 void testReadDataStructure()
112 117 {
113 118 // ////////////// //
114 119 // Test structure //
115 120 // ////////////// //
116 121
117 122 // Name of TXT file to read
118 123 QTest::addColumn<QString>("inputFileName");
119 124 // Expected results
120 125 QTest::addColumn<ExpectedResults<T> >("expectedResults");
121 126 }
122 127
123 128 template <typename T>
124 129 void testRead(AmdaResultParser::ValueType valueType)
125 130 {
126 131 QFETCH(QString, inputFileName);
127 132 QFETCH(ExpectedResults<T>, expectedResults);
128 133
129 134 // Parses file
130 135 auto filePath = inputFilePath(inputFileName);
131 136 auto results = AmdaResultParser::readTxt(filePath, valueType);
132 137
133 138 // ///////////////// //
134 139 // Validates results //
135 140 // ///////////////// //
136 141 expectedResults.validate(results);
137 142 }
138 143
139 144 private slots:
140 145 /// Input test data
141 146 /// @sa testReadScalarTxt()
142 147 void testReadScalarTxt_data();
143 148
144 149 /// Tests parsing scalar series of a TXT file
145 150 void testReadScalarTxt();
146 151
147 152 /// Input test data
148 153 /// @sa testReadVectorTxt()
149 154 void testReadVectorTxt_data();
150 155
151 156 /// Tests parsing vector series of a TXT file
152 157 void testReadVectorTxt();
153 158 };
154 159
155 160 void TestAmdaResultParser::testReadScalarTxt_data()
156 161 {
157 162 testReadDataStructure<ScalarSeries>();
158 163
159 164 // ////////// //
160 165 // Test cases //
161 166 // ////////// //
162 167
163 168 // Valid files
164 169 QTest::newRow("Valid file")
165 170 << QStringLiteral("ValidScalar1.txt")
166 171 << ExpectedResults<ScalarSeries>{
167 172 Unit{QStringLiteral("nT"), true}, Unit{},
168 173 QVector<QDateTime>{dateTime(2013, 9, 23, 9, 0, 30), dateTime(2013, 9, 23, 9, 1, 30),
169 174 dateTime(2013, 9, 23, 9, 2, 30), dateTime(2013, 9, 23, 9, 3, 30),
170 175 dateTime(2013, 9, 23, 9, 4, 30), dateTime(2013, 9, 23, 9, 5, 30),
171 176 dateTime(2013, 9, 23, 9, 6, 30), dateTime(2013, 9, 23, 9, 7, 30),
172 177 dateTime(2013, 9, 23, 9, 8, 30), dateTime(2013, 9, 23, 9, 9, 30)},
173 178 QVector<double>{-2.83950, -2.71850, -2.52150, -2.57633, -2.58050, -2.48325, -2.63025,
174 179 -2.55800, -2.43250, -2.42200}};
175 180
176 181 QTest::newRow("Valid file (value of first line is invalid but it is converted to NaN")
177 182 << QStringLiteral("WrongValue.txt")
178 183 << ExpectedResults<ScalarSeries>{
179 184 Unit{QStringLiteral("nT"), true}, Unit{},
180 185 QVector<QDateTime>{dateTime(2013, 9, 23, 9, 0, 30), dateTime(2013, 9, 23, 9, 1, 30),
181 186 dateTime(2013, 9, 23, 9, 2, 30)},
182 187 QVector<double>{std::numeric_limits<double>::quiet_NaN(), -2.71850, -2.52150}};
183 188
184 189 QTest::newRow("Valid file that contains NaN values")
185 190 << QStringLiteral("NaNValue.txt")
186 191 << ExpectedResults<ScalarSeries>{
187 192 Unit{QStringLiteral("nT"), true}, Unit{},
188 193 QVector<QDateTime>{dateTime(2013, 9, 23, 9, 0, 30), dateTime(2013, 9, 23, 9, 1, 30),
189 194 dateTime(2013, 9, 23, 9, 2, 30)},
190 195 QVector<double>{std::numeric_limits<double>::quiet_NaN(), -2.71850, -2.52150}};
191 196
192 197 // Valid files but with some invalid lines (wrong unit, wrong values, etc.)
193 198 QTest::newRow("No unit file") << QStringLiteral("NoUnit.txt")
194 199 << ExpectedResults<ScalarSeries>{Unit{QStringLiteral(""), true},
195 200 Unit{}, QVector<QDateTime>{},
196 201 QVector<double>{}};
197 202 QTest::newRow("Wrong unit file")
198 203 << QStringLiteral("WrongUnit.txt")
199 204 << ExpectedResults<ScalarSeries>{Unit{QStringLiteral(""), true}, Unit{},
200 205 QVector<QDateTime>{dateTime(2013, 9, 23, 9, 0, 30),
201 206 dateTime(2013, 9, 23, 9, 1, 30),
202 207 dateTime(2013, 9, 23, 9, 2, 30)},
203 208 QVector<double>{-2.83950, -2.71850, -2.52150}};
204 209
205 210 QTest::newRow("Wrong results file (date of first line is invalid")
206 211 << QStringLiteral("WrongDate.txt")
207 212 << ExpectedResults<ScalarSeries>{
208 213 Unit{QStringLiteral("nT"), true}, Unit{},
209 214 QVector<QDateTime>{dateTime(2013, 9, 23, 9, 1, 30), dateTime(2013, 9, 23, 9, 2, 30)},
210 215 QVector<double>{-2.71850, -2.52150}};
211 216
212 217 QTest::newRow("Wrong results file (too many values for first line")
213 218 << QStringLiteral("TooManyValues.txt")
214 219 << ExpectedResults<ScalarSeries>{
215 220 Unit{QStringLiteral("nT"), true}, Unit{},
216 221 QVector<QDateTime>{dateTime(2013, 9, 23, 9, 1, 30), dateTime(2013, 9, 23, 9, 2, 30)},
217 222 QVector<double>{-2.71850, -2.52150}};
218 223
219 224 QTest::newRow("Wrong results file (x of first line is NaN")
220 225 << QStringLiteral("NaNX.txt")
221 226 << ExpectedResults<ScalarSeries>{
222 227 Unit{QStringLiteral("nT"), true}, Unit{},
223 228 QVector<QDateTime>{dateTime(2013, 9, 23, 9, 1, 30), dateTime(2013, 9, 23, 9, 2, 30)},
224 229 QVector<double>{-2.71850, -2.52150}};
225 230
226 231 QTest::newRow("Invalid file type (vector)")
227 232 << QStringLiteral("ValidVector1.txt")
228 233 << ExpectedResults<ScalarSeries>{Unit{QStringLiteral("nT"), true}, Unit{},
229 234 QVector<QDateTime>{}, QVector<double>{}};
230 235
231 236 // Invalid files
232 237 QTest::newRow("Invalid file (unexisting file)") << QStringLiteral("UnexistingFile.txt")
233 238 << ExpectedResults<ScalarSeries>{};
234 239
235 240 QTest::newRow("Invalid file (file not found on server)") << QStringLiteral("FileNotFound.txt")
236 241 << ExpectedResults<ScalarSeries>{};
237 242 }
238 243
239 244 void TestAmdaResultParser::testReadScalarTxt()
240 245 {
241 246 testRead<ScalarSeries>(AmdaResultParser::ValueType::SCALAR);
242 247 }
243 248
244 249 void TestAmdaResultParser::testReadVectorTxt_data()
245 250 {
246 251 testReadDataStructure<VectorSeries>();
247 252
248 253 // ////////// //
249 254 // Test cases //
250 255 // ////////// //
251 256
252 257 // Valid files
253 258 QTest::newRow("Valid file")
254 259 << QStringLiteral("ValidVector1.txt")
255 260 << ExpectedResults<VectorSeries>{
256 261 Unit{QStringLiteral("nT"), true}, Unit{},
257 262 QVector<QDateTime>{dateTime(2013, 7, 2, 9, 13, 50), dateTime(2013, 7, 2, 9, 14, 6),
258 263 dateTime(2013, 7, 2, 9, 14, 22), dateTime(2013, 7, 2, 9, 14, 38),
259 264 dateTime(2013, 7, 2, 9, 14, 54), dateTime(2013, 7, 2, 9, 15, 10),
260 265 dateTime(2013, 7, 2, 9, 15, 26), dateTime(2013, 7, 2, 9, 15, 42),
261 266 dateTime(2013, 7, 2, 9, 15, 58), dateTime(2013, 7, 2, 9, 16, 14)},
262 267 QVector<QVector<double> >{
263 268 {-0.332, -1.011, -1.457, -1.293, -1.217, -1.443, -1.278, -1.202, -1.22, -1.259},
264 269 {3.206, 2.999, 2.785, 2.736, 2.612, 2.564, 2.892, 2.862, 2.859, 2.764},
265 270 {0.058, 0.496, 1.018, 1.485, 1.662, 1.505, 1.168, 1.244, 1.15, 1.358}}};
266 271
267 272 // Valid files but with some invalid lines (wrong unit, wrong values, etc.)
268 273 QTest::newRow("Invalid file type (scalar)")
269 274 << QStringLiteral("ValidScalar1.txt")
270 275 << ExpectedResults<VectorSeries>{Unit{QStringLiteral("nT"), true}, Unit{},
271 276 QVector<QDateTime>{},
272 277 QVector<QVector<double> >{{}, {}, {}}};
273 278 }
274 279
275 280 void TestAmdaResultParser::testReadVectorTxt()
276 281 {
277 282 testRead<VectorSeries>(AmdaResultParser::ValueType::VECTOR);
278 283 }
279 284
280 285 QTEST_MAIN(TestAmdaResultParser)
281 286 #include "TestAmdaResultParser.moc"
@@ -1,192 +1,195
1 1 #include "CosinusProvider.h"
2 2 #include "MockDefs.h"
3 3
4 4 #include <Data/DataProviderParameters.h>
5 5 #include <Data/ScalarSeries.h>
6 6 #include <SqpApplication.h>
7 7 #include <Time/TimeController.h>
8 8 #include <Variable/Variable.h>
9 9 #include <Variable/VariableController.h>
10 10
11 11 #include <QObject>
12 12 #include <QtTest>
13 13
14 14 #include <cmath>
15 15 #include <memory>
16 16
17 17 namespace {
18 18
19 19 /// Path for the tests
20 20 const auto TESTS_RESOURCES_PATH = QFileInfo{
21 21 QString{MOCKPLUGIN_TESTS_RESOURCES_DIR},
22 22 "TestCosinusAcquisition"}.absoluteFilePath();
23 23
24 24 /// Format of dates in data files
25 25 const auto DATETIME_FORMAT = QStringLiteral("yyyy/MM/dd hh:mm:ss:zzz");
26 26
27 27 /**
28 28 * Verifies that the data in the candidate series are identical to the data in the reference series
29 29 * in a specific range
30 30 * @param candidate the candidate data series
31 31 * @param range the range to check
32 32 * @param reference the reference data series
33 33 * @return true if the data of the candidate series and the reference series are identical in the
34 34 * range, false otherwise
35 35 */
36 36 bool checkDataSeries(std::shared_ptr<IDataSeries> candidate, const SqpRange &range,
37 37 std::shared_ptr<IDataSeries> reference)
38 38 {
39 39 if (candidate == nullptr || reference == nullptr) {
40 40 return candidate == reference;
41 41 }
42 42
43 43 auto referenceIt = reference->xAxisRange(range.m_TStart, range.m_TEnd);
44 44
45 45 qInfo() << "candidateSize" << std::distance(candidate->cbegin(), candidate->cend());
46 46 qInfo() << "refSize" << std::distance(referenceIt.first, referenceIt.second);
47 47
48 48 return std::equal(candidate->cbegin(), candidate->cend(), referenceIt.first, referenceIt.second,
49 49 [](const auto &it1, const auto &it2) {
50 50 // - milliseconds precision for time
51 51 // - 1e-6 precision for value
52 52 return std::abs(it1.x() - it2.x()) < 1e-3
53 53 && std::abs(it1.value() - it2.value()) < 1e-6;
54 54 });
55 55 }
56 56
57 57 } // namespace
58 58
59 59 /**
60 60 * @brief The TestCosinusAcquisition class tests acquisition in SciQlop (operations like zooms in,
61 61 * zooms out, pans) of data from CosinusProvider
62 62 * @sa CosinusProvider
63 63 */
64 64 class TestCosinusAcquisition : public QObject {
65 65 Q_OBJECT
66 66
67 67 private slots:
68 68 /// Input data for @sa testAcquisition()
69 69 void testAcquisition_data();
70 70 void testAcquisition();
71 71 };
72 72
73 73 void TestCosinusAcquisition::testAcquisition_data()
74 74 {
75 75 // ////////////// //
76 76 // Test structure //
77 77 // ////////////// //
78 78
79 79 QTest::addColumn<SqpRange>("referenceRange"); // Range for generating reference series
80 80 QTest::addColumn<SqpRange>("initialRange"); // First acquisition
81 81 QTest::addColumn<int>("operationDelay"); // Acquisitions to make
82 82 QTest::addColumn<std::vector<SqpRange> >("operations"); // Acquisitions to make
83 83
84 84 // ////////// //
85 85 // Test cases //
86 86 // ////////// //
87 87
88 88 auto dateTime = [](int year, int month, int day, int hours, int minutes, int seconds) {
89 89 return DateUtils::secondsSinceEpoch(
90 90 QDateTime{{year, month, day}, {hours, minutes, seconds}, Qt::UTC});
91 91 };
92 92
93 93 QTest::newRow("cosinus")
94 94 << SqpRange{dateTime(2017, 1, 1, 12, 0, 0), dateTime(2017, 1, 1, 13, 0, 0)}
95 95 << SqpRange{dateTime(2017, 1, 1, 12, 30, 0), dateTime(2017, 1, 1, 12, 35, 1)} << 250
96 96 << std::vector<SqpRange>{
97 97 // Pan (jump) left
98 98 SqpRange{dateTime(2017, 1, 1, 12, 45, 0), dateTime(2017, 1, 1, 12, 50, 0)},
99 99 // Pan (jump) right
100 100 SqpRange{dateTime(2017, 1, 1, 12, 15, 0), dateTime(2017, 1, 1, 12, 20, 0)},
101 101 // Pan (overlay) right
102 102 SqpRange{dateTime(2017, 1, 1, 12, 14, 0), dateTime(2017, 1, 1, 12, 19, 0)},
103 103 // Pan (overlay) left
104 104 SqpRange{dateTime(2017, 1, 1, 12, 15, 0), dateTime(2017, 1, 1, 12, 20, 0)},
105 105 // Pan (overlay) left
106 106 SqpRange{dateTime(2017, 1, 1, 12, 16, 0), dateTime(2017, 1, 1, 12, 21, 0)},
107 107 // Zoom in
108 108 SqpRange{dateTime(2017, 1, 1, 12, 17, 30), dateTime(2017, 1, 1, 12, 19, 30)},
109 109 // Zoom out
110 110 SqpRange{dateTime(2017, 1, 1, 12, 12, 30), dateTime(2017, 1, 1, 12, 24, 30)}};
111 111
112 112 QTest::newRow("cosinus_big")
113 113 << SqpRange{dateTime(2017, 1, 1, 1, 0, 0), dateTime(2017, 1, 5, 13, 0, 0)}
114 114 << SqpRange{dateTime(2017, 1, 2, 6, 30, 0), dateTime(2017, 1, 2, 18, 30, 0)} << 5000
115 115 << std::vector<SqpRange>{
116 116 // Pan (jump) left
117 117 SqpRange{dateTime(2017, 1, 1, 13, 30, 0), dateTime(2017, 1, 1, 18, 30, 0)},
118 118 // Pan (jump) right
119 119 SqpRange{dateTime(2017, 1, 3, 4, 30, 0), dateTime(2017, 1, 3, 10, 30, 0)},
120 120 // Pan (overlay) right
121 121 SqpRange{dateTime(2017, 1, 3, 8, 30, 0), dateTime(2017, 1, 3, 12, 30, 0)},
122 122 // Pan (overlay) left
123 123 SqpRange{dateTime(2017, 1, 2, 8, 30, 0), dateTime(2017, 1, 3, 10, 30, 0)},
124 124 // Pan (overlay) left
125 125 SqpRange{dateTime(2017, 1, 1, 12, 30, 0), dateTime(2017, 1, 3, 5, 30, 0)},
126 126 // Zoom in
127 127 SqpRange{dateTime(2017, 1, 2, 2, 30, 0), dateTime(2017, 1, 2, 8, 30, 0)},
128 128 // Zoom out
129 129 SqpRange{dateTime(2017, 1, 1, 14, 30, 0), dateTime(2017, 1, 3, 12, 30, 0)}};
130 130 }
131 131
132 132 void TestCosinusAcquisition::testAcquisition()
133 133 {
134 134 // Retrieves reference range
135 135 QFETCH(SqpRange, referenceRange);
136 136 CosinusProvider referenceProvider{};
137 137 auto dataSeries = referenceProvider.provideDataSeries(
138 138 referenceRange, {{COSINUS_TYPE_KEY, "scalar"}, {COSINUS_FREQUENCY_KEY, 100.}});
139 139
140 140 auto end = dataSeries->cend() - 1;
141 141 qInfo() << dataSeries->nbPoints() << dataSeries->cbegin()->x() << end->x();
142 142
143 143 /// Lambda used to validate a variable at each step
144 144 auto validateVariable
145 145 = [dataSeries](std::shared_ptr<Variable> variable, const SqpRange &range) {
146 146 // Checks that the variable's range has changed
147 qInfo() << "range vs expected range" << variable->range() << range;
147 148 QCOMPARE(variable->range(), range);
148 149
149 150 // Checks the variable's data series
150 151 QVERIFY(checkDataSeries(variable->dataSeries(), variable->cacheRange(), dataSeries));
151 152 };
152 153
153 154 // Creates variable
154 155 QFETCH(SqpRange, initialRange);
155 156 sqpApp->timeController().onTimeToUpdate(initialRange);
156 157 auto provider = std::make_shared<CosinusProvider>();
157 158 auto variable = sqpApp->variableController().createVariable(
158 159 "MMS", {{COSINUS_TYPE_KEY, "scalar"}, {COSINUS_FREQUENCY_KEY, 100.}}, provider);
159 160
161
160 162 QFETCH(int, operationDelay);
161 163 QTest::qWait(operationDelay);
162 164 validateVariable(variable, initialRange);
163 165
166 QTest::qWait(operationDelay);
164 167 // Makes operations on the variable
165 168 QFETCH(std::vector<SqpRange>, operations);
166 169 for (const auto &operation : operations) {
167 170 // Asks request on the variable and waits during its execution
168 sqpApp->variableController().onRequestDataLoading({variable}, operation, true);
171 sqpApp->variableController().onRequestDataLoading({variable}, operation, false);
169 172
170 173 QTest::qWait(operationDelay);
171 174 validateVariable(variable, operation);
172 175 }
173 176
174 177
175 178 for (const auto &operation : operations) {
176 179 // Asks request on the variable and waits during its execution
177 sqpApp->variableController().onRequestDataLoading({variable}, operation, true);
180 sqpApp->variableController().onRequestDataLoading({variable}, operation, false);
178 181 }
179 182 QTest::qWait(operationDelay);
180 183 validateVariable(variable, operations.back());
181 184 }
182 185
183 186 int main(int argc, char *argv[])
184 187 {
185 188 SqpApplication app{argc, argv};
186 189 app.setAttribute(Qt::AA_Use96Dpi, true);
187 190 TestCosinusAcquisition testObject{};
188 191 QTEST_SET_MAIN_SOURCE_PATH
189 192 return QTest::qExec(&testObject, argc, argv);
190 193 }
191 194
192 195 #include "TestCosinusAcquisition.moc"
General Comments 0
You need to be logged in to leave comments. Login now