##// END OF EJS Templates
Merge branch 'feature/AcqBasedOnPreviousRequest' into develop
perrinel -
r815:3edf24abad73 merge
parent child
Show More
@@ -1,840 +1,896
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 auto deltaLeft = graphRange.m_TStart - oldGraphRange.m_TStart;
51 52 auto deltaRight = graphRange.m_TEnd - oldGraphRange.m_TEnd;
52 varRangeRequested.m_TStart += deltaRight;
53 varRangeRequested.m_TStart += deltaLeft;
53 54 varRangeRequested.m_TEnd += deltaRight;
54 55 break;
55 56 }
56 57 case AcquisitionZoomType::PanLeft: {
57 58 auto deltaLeft = oldGraphRange.m_TStart - graphRange.m_TStart;
59 auto deltaRight = oldGraphRange.m_TEnd - graphRange.m_TEnd;
58 60 varRangeRequested.m_TStart -= deltaLeft;
59 varRangeRequested.m_TEnd -= deltaLeft;
61 varRangeRequested.m_TEnd -= deltaRight;
60 62 break;
61 63 }
62 64 case AcquisitionZoomType::Unknown: {
63 65 qCCritical(LOG_VariableController())
64 66 << VariableController::tr("Impossible to synchronize: zoom type unknown");
65 67 break;
66 68 }
67 69 default:
68 70 qCCritical(LOG_VariableController()) << VariableController::tr(
69 71 "Impossible to synchronize: zoom type not take into account");
70 72 // No action
71 73 break;
72 74 }
73 75
74 76 return varRangeRequested;
75 77 }
76 78 }
77 79
78 80 struct VariableController::VariableControllerPrivate {
79 81 explicit VariableControllerPrivate(VariableController *parent)
80 82 : m_WorkingMutex{},
81 83 m_VariableModel{new VariableModel{parent}},
82 84 m_VariableSelectionModel{new QItemSelectionModel{m_VariableModel, parent}},
83 85 // m_VariableCacheStrategy{std::make_unique<VariableCacheStrategy>()},
84 86 m_VariableCacheStrategy{VariableCacheStrategyFactory::createCacheStrategy(
85 87 CacheStrategy::SingleThreshold)},
86 88 m_VariableAcquisitionWorker{std::make_unique<VariableAcquisitionWorker>()},
87 89 q{parent}
88 90 {
89 91
90 92 m_VariableAcquisitionWorker->moveToThread(&m_VariableAcquisitionWorkerThread);
91 93 m_VariableAcquisitionWorkerThread.setObjectName("VariableAcquisitionWorkerThread");
92 94 }
93 95
94 96
95 97 virtual ~VariableControllerPrivate()
96 98 {
97 99 qCDebug(LOG_VariableController()) << tr("VariableControllerPrivate destruction");
98 100 m_VariableAcquisitionWorkerThread.quit();
99 101 m_VariableAcquisitionWorkerThread.wait();
100 102 }
101 103
102 104
103 105 void processRequest(std::shared_ptr<Variable> var, const SqpRange &rangeRequested,
104 106 QUuid varRequestId);
105 107
106 108 QVector<SqpRange> provideNotInCacheDateTimeList(std::shared_ptr<Variable> variable,
107 109 const SqpRange &dateTime);
108 110
109 111 std::shared_ptr<Variable> findVariable(QUuid vIdentifier);
110 112 std::shared_ptr<IDataSeries>
111 113 retrieveDataSeries(const QVector<AcquisitionDataPacket> acqDataPacketVector);
112 114
113 115 void registerProvider(std::shared_ptr<IDataProvider> provider);
114 116
115 117 void storeVariableRequest(QUuid varId, QUuid varRequestId, const VariableRequest &varRequest);
116 118 QUuid acceptVariableRequest(QUuid varId, std::shared_ptr<IDataSeries> dataSeries);
117 119 void updateVariableRequest(QUuid varRequestId);
118 120 void cancelVariableRequest(QUuid varRequestId);
119 121
122 SqpRange getLastRequestedRange(QUuid varId);
123
120 124 QMutex m_WorkingMutex;
121 125 /// Variable model. The VariableController has the ownership
122 126 VariableModel *m_VariableModel;
123 127 QItemSelectionModel *m_VariableSelectionModel;
124 128
125 129
126 130 TimeController *m_TimeController{nullptr};
127 131 std::unique_ptr<VariableCacheStrategy> m_VariableCacheStrategy;
128 132 std::unique_ptr<VariableAcquisitionWorker> m_VariableAcquisitionWorker;
129 133 QThread m_VariableAcquisitionWorkerThread;
130 134
131 135 std::unordered_map<std::shared_ptr<Variable>, std::shared_ptr<IDataProvider> >
132 136 m_VariableToProviderMap;
133 137 std::unordered_map<std::shared_ptr<Variable>, QUuid> m_VariableToIdentifierMap;
134 138 std::map<QUuid, std::shared_ptr<VariableSynchronizationGroup> >
135 139 m_GroupIdToVariableSynchronizationGroupMap;
136 140 std::map<QUuid, QUuid> m_VariableIdGroupIdMap;
137 141 std::set<std::shared_ptr<IDataProvider> > m_ProviderSet;
138 142
139 143 std::map<QUuid, std::map<QUuid, VariableRequest> > m_VarRequestIdToVarIdVarRequestMap;
140 144
141 145 std::map<QUuid, std::deque<QUuid> > m_VarIdToVarRequestIdQueueMap;
142 146
143 147
144 148 VariableController *q;
145 149 };
146 150
147 151
148 152 VariableController::VariableController(QObject *parent)
149 153 : QObject{parent}, impl{spimpl::make_unique_impl<VariableControllerPrivate>(this)}
150 154 {
151 155 qCDebug(LOG_VariableController()) << tr("VariableController construction")
152 156 << QThread::currentThread();
153 157
154 158 connect(impl->m_VariableModel, &VariableModel::abortProgessRequested, this,
155 159 &VariableController::onAbortProgressRequested);
156 160
157 161 connect(impl->m_VariableAcquisitionWorker.get(),
158 162 &VariableAcquisitionWorker::variableCanceledRequested, this,
159 163 &VariableController::onAbortAcquisitionRequested);
160 164
161 165 connect(impl->m_VariableAcquisitionWorker.get(), &VariableAcquisitionWorker::dataProvided, this,
162 166 &VariableController::onDataProvided);
163 167 connect(impl->m_VariableAcquisitionWorker.get(),
164 168 &VariableAcquisitionWorker::variableRequestInProgress, this,
165 169 &VariableController::onVariableRetrieveDataInProgress);
166 170
167 171
168 172 connect(&impl->m_VariableAcquisitionWorkerThread, &QThread::started,
169 173 impl->m_VariableAcquisitionWorker.get(), &VariableAcquisitionWorker::initialize);
170 174 connect(&impl->m_VariableAcquisitionWorkerThread, &QThread::finished,
171 175 impl->m_VariableAcquisitionWorker.get(), &VariableAcquisitionWorker::finalize);
172 176
173 177
174 178 impl->m_VariableAcquisitionWorkerThread.start();
175 179 }
176 180
177 181 VariableController::~VariableController()
178 182 {
179 183 qCDebug(LOG_VariableController()) << tr("VariableController destruction")
180 184 << QThread::currentThread();
181 185 this->waitForFinish();
182 186 }
183 187
184 188 VariableModel *VariableController::variableModel() noexcept
185 189 {
186 190 return impl->m_VariableModel;
187 191 }
188 192
189 193 QItemSelectionModel *VariableController::variableSelectionModel() noexcept
190 194 {
191 195 return impl->m_VariableSelectionModel;
192 196 }
193 197
194 198 void VariableController::setTimeController(TimeController *timeController) noexcept
195 199 {
196 200 impl->m_TimeController = timeController;
197 201 }
198 202
199 203 std::shared_ptr<Variable>
200 204 VariableController::cloneVariable(std::shared_ptr<Variable> variable) noexcept
201 205 {
202 206 if (impl->m_VariableModel->containsVariable(variable)) {
203 207 // Clones variable
204 208 auto duplicate = variable->clone();
205 209
206 210 // Adds clone to model
207 211 impl->m_VariableModel->addVariable(duplicate);
208 212
209 213 // Generates clone identifier
210 214 impl->m_VariableToIdentifierMap[duplicate] = QUuid::createUuid();
211 215
212 216 // Registers provider
213 217 auto variableProvider = impl->m_VariableToProviderMap.at(variable);
214 218 auto duplicateProvider = variableProvider != nullptr ? variableProvider->clone() : nullptr;
215 219
216 220 impl->m_VariableToProviderMap[duplicate] = duplicateProvider;
217 221 if (duplicateProvider) {
218 222 impl->registerProvider(duplicateProvider);
219 223 }
220 224
221 225 return duplicate;
222 226 }
223 227 else {
224 228 qCCritical(LOG_VariableController())
225 229 << tr("Can't create duplicate of variable %1: variable not registered in the model")
226 230 .arg(variable->name());
227 231 return nullptr;
228 232 }
229 233 }
230 234
231 235 void VariableController::deleteVariable(std::shared_ptr<Variable> variable) noexcept
232 236 {
233 237 if (!variable) {
234 238 qCCritical(LOG_VariableController()) << "Can't delete variable: variable is null";
235 239 return;
236 240 }
237 241
238 242 // Spreads in SciQlop that the variable will be deleted, so that potential receivers can
239 243 // make some treatments before the deletion
240 244 emit variableAboutToBeDeleted(variable);
241 245
242 246 // Deletes identifier
243 247 impl->m_VariableToIdentifierMap.erase(variable);
244 248
245 249 // Deletes provider
246 250 auto nbProvidersDeleted = impl->m_VariableToProviderMap.erase(variable);
247 251 qCDebug(LOG_VariableController())
248 252 << tr("Number of providers deleted for variable %1: %2")
249 253 .arg(variable->name(), QString::number(nbProvidersDeleted));
250 254
251 255
252 256 // Deletes from model
253 257 impl->m_VariableModel->deleteVariable(variable);
254 258 }
255 259
256 260 void VariableController::deleteVariables(
257 261 const QVector<std::shared_ptr<Variable> > &variables) noexcept
258 262 {
259 263 for (auto variable : qAsConst(variables)) {
260 264 deleteVariable(variable);
261 265 }
262 266 }
263 267
264 268 std::shared_ptr<Variable>
265 269 VariableController::createVariable(const QString &name, const QVariantHash &metadata,
266 270 std::shared_ptr<IDataProvider> provider) noexcept
267 271 {
268 272 if (!impl->m_TimeController) {
269 273 qCCritical(LOG_VariableController())
270 274 << tr("Impossible to create variable: The time controller is null");
271 275 return nullptr;
272 276 }
273 277
274 278 auto range = impl->m_TimeController->dateTime();
275 279
276 280 if (auto newVariable = impl->m_VariableModel->createVariable(name, metadata)) {
277 281 auto identifier = QUuid::createUuid();
278 282
279 283 // store the provider
280 284 impl->registerProvider(provider);
281 285
282 286 // Associate the provider
283 287 impl->m_VariableToProviderMap[newVariable] = provider;
284 288 qCInfo(LOG_VariableController()) << "createVariable: " << identifier;
285 289 impl->m_VariableToIdentifierMap[newVariable] = identifier;
286 290
287 291
288 292 auto varRequestId = QUuid::createUuid();
289 293 impl->processRequest(newVariable, range, varRequestId);
290 294 impl->updateVariableRequest(varRequestId);
291 295
292 296 return newVariable;
293 297 }
294 298 }
295 299
296 300 void VariableController::onDateTimeOnSelection(const SqpRange &dateTime)
297 301 {
298 302 // TODO check synchronisation and Rescale
299 303 qCDebug(LOG_VariableController()) << "VariableController::onDateTimeOnSelection"
300 304 << QThread::currentThread()->objectName();
301 305 auto selectedRows = impl->m_VariableSelectionModel->selectedRows();
302 306 auto varRequestId = QUuid::createUuid();
303 307
304 308 for (const auto &selectedRow : qAsConst(selectedRows)) {
305 309 if (auto selectedVariable = impl->m_VariableModel->variable(selectedRow.row())) {
306 310 selectedVariable->setRange(dateTime);
307 311 impl->processRequest(selectedVariable, dateTime, varRequestId);
308 312
309 313 // notify that rescale operation has to be done
310 314 emit rangeChanged(selectedVariable, dateTime);
311 315 }
312 316 }
313 317 impl->updateVariableRequest(varRequestId);
314 318 }
315 319
316 320 void VariableController::onDataProvided(QUuid vIdentifier, const SqpRange &rangeRequested,
317 321 const SqpRange &cacheRangeRequested,
318 322 QVector<AcquisitionDataPacket> dataAcquired)
319 323 {
320 324 auto retrievedDataSeries = impl->retrieveDataSeries(dataAcquired);
321 325 auto varRequestId = impl->acceptVariableRequest(vIdentifier, retrievedDataSeries);
322 326 if (!varRequestId.isNull()) {
323 327 impl->updateVariableRequest(varRequestId);
324 328 }
325 329 }
326 330
327 331 void VariableController::onVariableRetrieveDataInProgress(QUuid identifier, double progress)
328 332 {
329 333 qCDebug(LOG_VariableController())
330 334 << "TORM: variableController::onVariableRetrieveDataInProgress"
331 335 << QThread::currentThread()->objectName() << progress;
332 336 if (auto var = impl->findVariable(identifier)) {
333 337 impl->m_VariableModel->setDataProgress(var, progress);
334 338 }
335 339 else {
336 340 qCCritical(LOG_VariableController())
337 341 << tr("Impossible to notify progression of a null variable");
338 342 }
339 343 }
340 344
341 345 void VariableController::onAbortProgressRequested(std::shared_ptr<Variable> variable)
342 346 {
343 347 auto it = impl->m_VariableToIdentifierMap.find(variable);
344 348 if (it != impl->m_VariableToIdentifierMap.cend()) {
345 349 impl->m_VariableAcquisitionWorker->abortProgressRequested(it->second);
346 350
347 351 QUuid varRequestId;
348 352 auto varIdToVarRequestIdQueueMapIt = impl->m_VarIdToVarRequestIdQueueMap.find(it->second);
349 353 if (varIdToVarRequestIdQueueMapIt != impl->m_VarIdToVarRequestIdQueueMap.cend()) {
350 354 auto &varRequestIdQueue = varIdToVarRequestIdQueueMapIt->second;
351 355 varRequestId = varRequestIdQueue.front();
352 356 impl->cancelVariableRequest(varRequestId);
353 357
354 358 // Finish the progression for the request
355 359 impl->m_VariableModel->setDataProgress(variable, 0.0);
356 360 }
357 361 else {
358 362 qCWarning(LOG_VariableController())
359 363 << tr("Aborting progression of inexistant variable request detected !!!")
360 364 << QThread::currentThread()->objectName();
361 365 }
362 366 }
363 367 else {
364 368 qCWarning(LOG_VariableController())
365 369 << tr("Aborting progression of inexistant variable detected !!!")
366 370 << QThread::currentThread()->objectName();
367 371 }
368 372 }
369 373
370 374 void VariableController::onAbortAcquisitionRequested(QUuid vIdentifier)
371 375 {
372 376 qCDebug(LOG_VariableController()) << "TORM: variableController::onAbortAcquisitionRequested"
373 377 << QThread::currentThread()->objectName() << vIdentifier;
374 378
375 379 if (auto var = impl->findVariable(vIdentifier)) {
376 380 this->onAbortProgressRequested(var);
377 381 }
378 382 else {
379 383 qCCritical(LOG_VariableController())
380 384 << tr("Impossible to abort Acquisition Requestof a null variable");
381 385 }
382 386 }
383 387
384 388 void VariableController::onAddSynchronizationGroupId(QUuid synchronizationGroupId)
385 389 {
386 390 qCDebug(LOG_VariableController()) << "TORM: VariableController::onAddSynchronizationGroupId"
387 391 << QThread::currentThread()->objectName()
388 392 << synchronizationGroupId;
389 393 auto vSynchroGroup = std::make_shared<VariableSynchronizationGroup>();
390 394 impl->m_GroupIdToVariableSynchronizationGroupMap.insert(
391 395 std::make_pair(synchronizationGroupId, vSynchroGroup));
392 396 }
393 397
394 398 void VariableController::onRemoveSynchronizationGroupId(QUuid synchronizationGroupId)
395 399 {
396 400 impl->m_GroupIdToVariableSynchronizationGroupMap.erase(synchronizationGroupId);
397 401 }
398 402
399 403 void VariableController::onAddSynchronized(std::shared_ptr<Variable> variable,
400 404 QUuid synchronizationGroupId)
401 405
402 406 {
403 407 qCDebug(LOG_VariableController()) << "TORM: VariableController::onAddSynchronized"
404 408 << synchronizationGroupId;
405 409 auto varToVarIdIt = impl->m_VariableToIdentifierMap.find(variable);
406 410 if (varToVarIdIt != impl->m_VariableToIdentifierMap.cend()) {
407 411 auto groupIdToVSGIt
408 412 = impl->m_GroupIdToVariableSynchronizationGroupMap.find(synchronizationGroupId);
409 413 if (groupIdToVSGIt != impl->m_GroupIdToVariableSynchronizationGroupMap.cend()) {
410 414 impl->m_VariableIdGroupIdMap.insert(
411 415 std::make_pair(varToVarIdIt->second, synchronizationGroupId));
412 416 groupIdToVSGIt->second->addVariableId(varToVarIdIt->second);
413 417 }
414 418 else {
415 419 qCCritical(LOG_VariableController())
416 420 << tr("Impossible to synchronize a variable with an unknown sycnhronization group")
417 421 << variable->name();
418 422 }
419 423 }
420 424 else {
421 425 qCCritical(LOG_VariableController())
422 426 << tr("Impossible to synchronize a variable with no identifier") << variable->name();
423 427 }
424 428 }
425 429
426 430 void VariableController::desynchronize(std::shared_ptr<Variable> variable,
427 431 QUuid synchronizationGroupId)
428 432 {
429 433 // Gets variable id
430 434 auto variableIt = impl->m_VariableToIdentifierMap.find(variable);
431 435 if (variableIt == impl->m_VariableToIdentifierMap.cend()) {
432 436 qCCritical(LOG_VariableController())
433 437 << tr("Can't desynchronize variable %1: variable identifier not found")
434 438 .arg(variable->name());
435 439 return;
436 440 }
437 441
438 442 // Gets synchronization group
439 443 auto groupIt = impl->m_GroupIdToVariableSynchronizationGroupMap.find(synchronizationGroupId);
440 444 if (groupIt == impl->m_GroupIdToVariableSynchronizationGroupMap.cend()) {
441 445 qCCritical(LOG_VariableController())
442 446 << tr("Can't desynchronize variable %1: unknown synchronization group")
443 447 .arg(variable->name());
444 448 return;
445 449 }
446 450
447 451 auto variableId = variableIt->second;
448 452
449 453 // Removes variable from synchronization group
450 454 auto synchronizationGroup = groupIt->second;
451 455 synchronizationGroup->removeVariableId(variableId);
452 456
453 457 // Removes link between variable and synchronization group
454 458 impl->m_VariableIdGroupIdMap.erase(variableId);
455 459 }
456 460
457 461 void VariableController::onRequestDataLoading(QVector<std::shared_ptr<Variable> > variables,
458 462 const SqpRange &range, const SqpRange &oldRange,
459 463 bool synchronise)
460 464 {
461 465 // NOTE: oldRange isn't really necessary since oldRange == variable->range().
462 466
463 467 // we want to load data of the variable for the dateTime.
464 468 // First we check if the cache contains some of them.
465 469 // For the other, we ask the provider to give them.
466 470
467 471 auto varRequestId = QUuid::createUuid();
468 472 qCDebug(LOG_VariableController()) << "VariableController::onRequestDataLoading"
469 473 << QThread::currentThread()->objectName() << varRequestId;
470 474
471 475 for (const auto &var : variables) {
472 476 qCDebug(LOG_VariableController()) << "processRequest for" << var->name() << varRequestId;
473 477 impl->processRequest(var, range, varRequestId);
474 478 }
475 479
476 480 if (synchronise) {
477 481 // Get the group ids
478 482 qCDebug(LOG_VariableController())
479 483 << "TORM VariableController::onRequestDataLoading for synchro var ENABLE";
480 484 auto groupIds = std::set<QUuid>{};
481 485 auto groupIdToOldRangeMap = std::map<QUuid, SqpRange>{};
482 486 for (const auto &var : variables) {
483 487 auto varToVarIdIt = impl->m_VariableToIdentifierMap.find(var);
484 488 if (varToVarIdIt != impl->m_VariableToIdentifierMap.cend()) {
485 489 auto vId = varToVarIdIt->second;
486 490 auto varIdToGroupIdIt = impl->m_VariableIdGroupIdMap.find(vId);
487 491 if (varIdToGroupIdIt != impl->m_VariableIdGroupIdMap.cend()) {
488 492 auto gId = varIdToGroupIdIt->second;
489 493 groupIdToOldRangeMap.insert(std::make_pair(gId, var->range()));
490 494 if (groupIds.find(gId) == groupIds.cend()) {
491 495 qCDebug(LOG_VariableController()) << "Synchro detect group " << gId;
492 496 groupIds.insert(gId);
493 497 }
494 498 }
495 499 }
496 500 }
497 501
498 502 // We assume here all group ids exist
499 503 for (const auto &gId : groupIds) {
500 504 auto vSynchronizationGroup = impl->m_GroupIdToVariableSynchronizationGroupMap.at(gId);
501 505 auto vSyncIds = vSynchronizationGroup->getIds();
502 506 qCDebug(LOG_VariableController()) << "Var in synchro group ";
503 507 for (auto vId : vSyncIds) {
504 508 auto var = impl->findVariable(vId);
505 509
506 510 // Don't process already processed var
507 511 if (!variables.contains(var)) {
508 512 if (var != nullptr) {
509 513 qCDebug(LOG_VariableController()) << "processRequest synchro for"
510 514 << var->name();
511 515 auto vSyncRangeRequested = computeSynchroRangeRequested(
512 516 var->range(), range, groupIdToOldRangeMap.at(gId));
513 517 qCDebug(LOG_VariableController()) << "synchro RR" << vSyncRangeRequested;
514 518 impl->processRequest(var, vSyncRangeRequested, varRequestId);
515 519 }
516 520 else {
517 521 qCCritical(LOG_VariableController())
518 522
519 523 << tr("Impossible to synchronize a null variable");
520 524 }
521 525 }
522 526 }
523 527 }
524 528 }
525 529
526 530 impl->updateVariableRequest(varRequestId);
527 531 }
528 532
529 533
530 534 void VariableController::initialize()
531 535 {
532 536 qCDebug(LOG_VariableController()) << tr("VariableController init") << QThread::currentThread();
533 537 impl->m_WorkingMutex.lock();
534 538 qCDebug(LOG_VariableController()) << tr("VariableController init END");
535 539 }
536 540
537 541 void VariableController::finalize()
538 542 {
539 543 impl->m_WorkingMutex.unlock();
540 544 }
541 545
542 546 void VariableController::waitForFinish()
543 547 {
544 548 QMutexLocker locker{&impl->m_WorkingMutex};
545 549 }
546 550
547 551 AcquisitionZoomType VariableController::getZoomType(const SqpRange &range, const SqpRange &oldRange)
548 552 {
549 553 // t1.m_TStart <= t2.m_TStart && t2.m_TEnd <= t1.m_TEnd
550 554 auto zoomType = AcquisitionZoomType::Unknown;
551 555 if (range.m_TStart <= oldRange.m_TStart && oldRange.m_TEnd <= range.m_TEnd) {
556 qCCritical(LOG_VariableController()) << "zoomtype: ZoomOut";
552 557 zoomType = AcquisitionZoomType::ZoomOut;
553 558 }
554 559 else if (range.m_TStart > oldRange.m_TStart && range.m_TEnd > oldRange.m_TEnd) {
560 qCCritical(LOG_VariableController()) << "zoomtype: PanRight";
555 561 zoomType = AcquisitionZoomType::PanRight;
556 562 }
557 563 else if (range.m_TStart < oldRange.m_TStart && range.m_TEnd < oldRange.m_TEnd) {
564 qCCritical(LOG_VariableController()) << "zoomtype: PanLeft";
558 565 zoomType = AcquisitionZoomType::PanLeft;
559 566 }
560 567 else if (range.m_TStart > oldRange.m_TStart && oldRange.m_TEnd > range.m_TEnd) {
568 qCCritical(LOG_VariableController()) << "zoomtype: ZoomIn";
561 569 zoomType = AcquisitionZoomType::ZoomIn;
562 570 }
563 571 else {
564 572 qCCritical(LOG_VariableController()) << "getZoomType: Unknown type detected";
565 573 }
566 574 return zoomType;
567 575 }
568 576
569 577 void VariableController::VariableControllerPrivate::processRequest(std::shared_ptr<Variable> var,
570 578 const SqpRange &rangeRequested,
571 579 QUuid varRequestId)
572 580 {
573
574 // TODO: protect at
575 581 auto varRequest = VariableRequest{};
576 auto varId = m_VariableToIdentifierMap.at(var);
577 582
578 auto varStrategyRangesRequested
579 = m_VariableCacheStrategy->computeRange(var->range(), rangeRequested);
583 auto it = m_VariableToIdentifierMap.find(var);
584 if (it != m_VariableToIdentifierMap.cend()) {
580 585
581 auto notInCacheRangeList = QVector<SqpRange>{varStrategyRangesRequested.second};
582 auto inCacheRangeList = QVector<SqpRange>{};
583 if (m_VarIdToVarRequestIdQueueMap.find(varId) == m_VarIdToVarRequestIdQueueMap.cend()) {
584 notInCacheRangeList = var->provideNotInCacheRangeList(varStrategyRangesRequested.second);
585 inCacheRangeList = var->provideInCacheRangeList(varStrategyRangesRequested.second);
586 }
586 auto varId = it->second;
587
588 auto oldRange = getLastRequestedRange(varId);
589
590 // check for update oldRange to the last request range.
591 if (oldRange == INVALID_RANGE) {
592 oldRange = var->range();
593 }
587 594
588 if (!notInCacheRangeList.empty()) {
589 varRequest.m_RangeRequested = varStrategyRangesRequested.first;
590 varRequest.m_CacheRangeRequested = varStrategyRangesRequested.second;
595 auto varStrategyRangesRequested
596 = m_VariableCacheStrategy->computeRange(oldRange, rangeRequested);
591 597
592 // store VarRequest
593 storeVariableRequest(varId, varRequestId, varRequest);
598 auto notInCacheRangeList = QVector<SqpRange>{varStrategyRangesRequested.second};
599 auto inCacheRangeList = QVector<SqpRange>{};
600 if (m_VarIdToVarRequestIdQueueMap.find(varId) == m_VarIdToVarRequestIdQueueMap.cend()) {
601 notInCacheRangeList
602 = var->provideNotInCacheRangeList(varStrategyRangesRequested.second);
603 inCacheRangeList = var->provideInCacheRangeList(varStrategyRangesRequested.second);
604 }
594 605
595 auto varProvider = m_VariableToProviderMap.at(var);
596 if (varProvider != nullptr) {
597 auto varRequestIdCanceled = m_VariableAcquisitionWorker->pushVariableRequest(
598 varRequestId, varId, varStrategyRangesRequested.first,
599 varStrategyRangesRequested.second,
600 DataProviderParameters{std::move(notInCacheRangeList), var->metadata()},
601 varProvider);
606 if (!notInCacheRangeList.empty()) {
607 varRequest.m_RangeRequested = varStrategyRangesRequested.first;
608 varRequest.m_CacheRangeRequested = varStrategyRangesRequested.second;
609
610 // store VarRequest
611 storeVariableRequest(varId, varRequestId, varRequest);
612
613 auto varProvider = m_VariableToProviderMap.at(var);
614 if (varProvider != nullptr) {
615 auto varRequestIdCanceled = m_VariableAcquisitionWorker->pushVariableRequest(
616 varRequestId, varId, varStrategyRangesRequested.first,
617 varStrategyRangesRequested.second,
618 DataProviderParameters{std::move(notInCacheRangeList), var->metadata()},
619 varProvider);
620
621 if (!varRequestIdCanceled.isNull()) {
622 qCDebug(LOG_VariableAcquisitionWorker()) << tr("vsarRequestIdCanceled: ")
623 << varRequestIdCanceled;
624 cancelVariableRequest(varRequestIdCanceled);
625 }
626 }
627 else {
628 qCCritical(LOG_VariableController())
629 << "Impossible to provide data with a null provider";
630 }
602 631
603 if (!varRequestIdCanceled.isNull()) {
604 qCDebug(LOG_VariableAcquisitionWorker()) << tr("vsarRequestIdCanceled: ")
605 << varRequestIdCanceled;
606 cancelVariableRequest(varRequestIdCanceled);
632 if (!inCacheRangeList.empty()) {
633 emit q->updateVarDisplaying(var, inCacheRangeList.first());
607 634 }
608 635 }
609 636 else {
610 qCCritical(LOG_VariableController())
611 << "Impossible to provide data with a null provider";
612 }
613
614 if (!inCacheRangeList.empty()) {
615 emit q->updateVarDisplaying(var, inCacheRangeList.first());
637 varRequest.m_RangeRequested = varStrategyRangesRequested.first;
638 varRequest.m_CacheRangeRequested = varStrategyRangesRequested.second;
639 // store VarRequest
640 storeVariableRequest(varId, varRequestId, varRequest);
641 acceptVariableRequest(
642 varId, var->dataSeries()->subDataSeries(varStrategyRangesRequested.second));
616 643 }
617 644 }
618 else {
619 varRequest.m_RangeRequested = varStrategyRangesRequested.first;
620 varRequest.m_CacheRangeRequested = varStrategyRangesRequested.second;
621 // store VarRequest
622 storeVariableRequest(varId, varRequestId, varRequest);
623 acceptVariableRequest(varId,
624 var->dataSeries()->subDataSeries(varStrategyRangesRequested.second));
625 }
626 645 }
627 646
628 647 std::shared_ptr<Variable>
629 648 VariableController::VariableControllerPrivate::findVariable(QUuid vIdentifier)
630 649 {
631 650 std::shared_ptr<Variable> var;
632 651 auto findReply = [vIdentifier](const auto &entry) { return vIdentifier == entry.second; };
633 652
634 653 auto end = m_VariableToIdentifierMap.cend();
635 654 auto it = std::find_if(m_VariableToIdentifierMap.cbegin(), end, findReply);
636 655 if (it != end) {
637 656 var = it->first;
638 657 }
639 658 else {
640 659 qCCritical(LOG_VariableController())
641 660 << tr("Impossible to find the variable with the identifier: ") << vIdentifier;
642 661 }
643 662
644 663 return var;
645 664 }
646 665
647 666 std::shared_ptr<IDataSeries> VariableController::VariableControllerPrivate::retrieveDataSeries(
648 667 const QVector<AcquisitionDataPacket> acqDataPacketVector)
649 668 {
650 669 qCDebug(LOG_VariableController()) << tr("TORM: retrieveDataSeries acqDataPacketVector size")
651 670 << acqDataPacketVector.size();
652 671 std::shared_ptr<IDataSeries> dataSeries;
653 672 if (!acqDataPacketVector.isEmpty()) {
654 673 dataSeries = acqDataPacketVector[0].m_DateSeries;
655 674 for (int i = 1; i < acqDataPacketVector.size(); ++i) {
656 675 dataSeries->merge(acqDataPacketVector[i].m_DateSeries.get());
657 676 }
658 677 }
659 678 qCDebug(LOG_VariableController()) << tr("TORM: retrieveDataSeries acqDataPacketVector size END")
660 679 << acqDataPacketVector.size();
661 680 return dataSeries;
662 681 }
663 682
664 683 void VariableController::VariableControllerPrivate::registerProvider(
665 684 std::shared_ptr<IDataProvider> provider)
666 685 {
667 686 if (m_ProviderSet.find(provider) == m_ProviderSet.end()) {
668 687 qCDebug(LOG_VariableController()) << tr("Registering of a new provider")
669 688 << provider->objectName();
670 689 m_ProviderSet.insert(provider);
671 690 connect(provider.get(), &IDataProvider::dataProvided, m_VariableAcquisitionWorker.get(),
672 691 &VariableAcquisitionWorker::onVariableDataAcquired);
673 692 connect(provider.get(), &IDataProvider::dataProvidedProgress,
674 693 m_VariableAcquisitionWorker.get(),
675 694 &VariableAcquisitionWorker::onVariableRetrieveDataInProgress);
676 695 connect(provider.get(), &IDataProvider::dataProvidedFailed,
677 696 m_VariableAcquisitionWorker.get(),
678 697 &VariableAcquisitionWorker::onVariableAcquisitionFailed);
679 698 }
680 699 else {
681 700 qCDebug(LOG_VariableController()) << tr("Cannot register provider, it already exists ");
682 701 }
683 702 }
684 703
685 704 void VariableController::VariableControllerPrivate::storeVariableRequest(
686 705 QUuid varId, QUuid varRequestId, const VariableRequest &varRequest)
687 706 {
688 707 // First request for the variable. we can create an entry for it
689 708 auto varIdToVarRequestIdQueueMapIt = m_VarIdToVarRequestIdQueueMap.find(varId);
690 709 if (varIdToVarRequestIdQueueMapIt == m_VarIdToVarRequestIdQueueMap.cend()) {
691 710 auto varRequestIdQueue = std::deque<QUuid>{};
692 711 qCDebug(LOG_VariableController()) << tr("Store REQUEST in QUEUE");
693 712 varRequestIdQueue.push_back(varRequestId);
694 713 m_VarIdToVarRequestIdQueueMap.insert(std::make_pair(varId, std::move(varRequestIdQueue)));
695 714 }
696 715 else {
697 716 qCDebug(LOG_VariableController()) << tr("Store REQUEST in EXISTING QUEUE");
698 717 auto &varRequestIdQueue = varIdToVarRequestIdQueueMapIt->second;
699 718 varRequestIdQueue.push_back(varRequestId);
700 719 }
701 720
702 721 auto varRequestIdToVarIdVarRequestMapIt = m_VarRequestIdToVarIdVarRequestMap.find(varRequestId);
703 722 if (varRequestIdToVarIdVarRequestMapIt == m_VarRequestIdToVarIdVarRequestMap.cend()) {
704 723 auto varIdToVarRequestMap = std::map<QUuid, VariableRequest>{};
705 724 varIdToVarRequestMap.insert(std::make_pair(varId, varRequest));
706 725 qCDebug(LOG_VariableController()) << tr("Store REQUESTID in MAP");
707 726 m_VarRequestIdToVarIdVarRequestMap.insert(
708 727 std::make_pair(varRequestId, std::move(varIdToVarRequestMap)));
709 728 }
710 729 else {
711 730 auto &varIdToVarRequestMap = varRequestIdToVarIdVarRequestMapIt->second;
712 731 qCDebug(LOG_VariableController()) << tr("Store REQUESTID in EXISTING MAP");
713 732 varIdToVarRequestMap.insert(std::make_pair(varId, varRequest));
714 733 }
715 734 }
716 735
717 736 QUuid VariableController::VariableControllerPrivate::acceptVariableRequest(
718 737 QUuid varId, std::shared_ptr<IDataSeries> dataSeries)
719 738 {
720 739 QUuid varRequestId;
721 740 auto varIdToVarRequestIdQueueMapIt = m_VarIdToVarRequestIdQueueMap.find(varId);
722 741 if (varIdToVarRequestIdQueueMapIt != m_VarIdToVarRequestIdQueueMap.cend()) {
723 742 auto &varRequestIdQueue = varIdToVarRequestIdQueueMapIt->second;
724 743 varRequestId = varRequestIdQueue.front();
725 744 auto varRequestIdToVarIdVarRequestMapIt
726 745 = m_VarRequestIdToVarIdVarRequestMap.find(varRequestId);
727 746 if (varRequestIdToVarIdVarRequestMapIt != m_VarRequestIdToVarIdVarRequestMap.cend()) {
728 747 auto &varIdToVarRequestMap = varRequestIdToVarIdVarRequestMapIt->second;
729 748 auto varIdToVarRequestMapIt = varIdToVarRequestMap.find(varId);
730 749 if (varIdToVarRequestMapIt != varIdToVarRequestMap.cend()) {
731 750 qCDebug(LOG_VariableController()) << tr("acceptVariableRequest");
732 751 auto &varRequest = varIdToVarRequestMapIt->second;
733 752 varRequest.m_DataSeries = dataSeries;
734 753 varRequest.m_CanUpdate = true;
735 754 }
736 755 else {
737 756 qCDebug(LOG_VariableController())
738 757 << tr("Impossible to acceptVariableRequest of a unknown variable id attached "
739 758 "to a variableRequestId")
740 759 << varRequestId << varId;
741 760 }
742 761 }
743 762 else {
744 763 qCCritical(LOG_VariableController())
745 764 << tr("Impossible to acceptVariableRequest of a unknown variableRequestId")
746 765 << varRequestId;
747 766 }
748 767
749 768 varRequestIdQueue.pop_front();
750 769 if (varRequestIdQueue.empty()) {
751 770 qCDebug(LOG_VariableController())
752 771 << tr("TORM Erase REQUEST because it has been accepted") << varId;
753 772 m_VarIdToVarRequestIdQueueMap.erase(varId);
754 773 }
755 774 }
756 775 else {
757 776 qCCritical(LOG_VariableController())
758 777 << tr("Impossible to acceptVariableRequest of a unknown variable id") << varId;
759 778 }
760 779
761 780 return varRequestId;
762 781 }
763 782
764 783 void VariableController::VariableControllerPrivate::updateVariableRequest(QUuid varRequestId)
765 784 {
766 785
767 786 auto varRequestIdToVarIdVarRequestMapIt = m_VarRequestIdToVarIdVarRequestMap.find(varRequestId);
768 787 if (varRequestIdToVarIdVarRequestMapIt != m_VarRequestIdToVarIdVarRequestMap.cend()) {
769 788 bool processVariableUpdate = true;
770 789 auto &varIdToVarRequestMap = varRequestIdToVarIdVarRequestMapIt->second;
771 790 for (auto varIdToVarRequestMapIt = varIdToVarRequestMap.cbegin();
772 791 (varIdToVarRequestMapIt != varIdToVarRequestMap.cend()) && processVariableUpdate;
773 792 ++varIdToVarRequestMapIt) {
774 793 processVariableUpdate &= varIdToVarRequestMapIt->second.m_CanUpdate;
775 794 qCDebug(LOG_VariableController()) << tr("updateVariableRequest")
776 795 << processVariableUpdate;
777 796 }
778 797
779 798 if (processVariableUpdate) {
780 799 for (auto varIdToVarRequestMapIt = varIdToVarRequestMap.cbegin();
781 800 varIdToVarRequestMapIt != varIdToVarRequestMap.cend(); ++varIdToVarRequestMapIt) {
782 801 if (auto var = findVariable(varIdToVarRequestMapIt->first)) {
783 802 auto &varRequest = varIdToVarRequestMapIt->second;
784 803 var->setRange(varRequest.m_RangeRequested);
785 804 var->setCacheRange(varRequest.m_CacheRangeRequested);
786 805 qCDebug(LOG_VariableController()) << tr("1: onDataProvided")
787 806 << varRequest.m_RangeRequested;
788 807 qCDebug(LOG_VariableController()) << tr("2: onDataProvided")
789 808 << varRequest.m_CacheRangeRequested;
790 809 var->mergeDataSeries(varRequest.m_DataSeries);
791 810 qCDebug(LOG_VariableController()) << tr("3: onDataProvided");
792 811
793 812 /// @todo MPL: confirm
794 813 // Variable update is notified only if there is no pending request for it
795 814 // if
796 815 // (m_VarIdToVarRequestIdQueueMap.count(varIdToVarRequestMapIt->first)
797 816 // == 0) {
798 817 emit var->updated();
799 818 // }
800 819 }
801 820 else {
802 821 qCCritical(LOG_VariableController())
803 822 << tr("Impossible to update data to a null variable");
804 823 }
805 824 }
806 825
807 826 // cleaning varRequestId
808 827 qCDebug(LOG_VariableController()) << tr("0: erase REQUEST in MAP ?")
809 828 << m_VarRequestIdToVarIdVarRequestMap.size();
810 829 m_VarRequestIdToVarIdVarRequestMap.erase(varRequestId);
811 830 qCDebug(LOG_VariableController()) << tr("1: erase REQUEST in MAP ?")
812 831 << m_VarRequestIdToVarIdVarRequestMap.size();
813 832 }
814 833 }
815 834 else {
816 835 qCCritical(LOG_VariableController())
817 836 << tr("Cannot updateVariableRequest for a unknow varRequestId") << varRequestId;
818 837 }
819 838 }
820 839
821 840 void VariableController::VariableControllerPrivate::cancelVariableRequest(QUuid varRequestId)
822 841 {
823 842 // cleaning varRequestId
824 843 m_VarRequestIdToVarIdVarRequestMap.erase(varRequestId);
825 844
826 845 for (auto varIdToVarRequestIdQueueMapIt = m_VarIdToVarRequestIdQueueMap.begin();
827 846 varIdToVarRequestIdQueueMapIt != m_VarIdToVarRequestIdQueueMap.end();) {
828 847 auto &varRequestIdQueue = varIdToVarRequestIdQueueMapIt->second;
829 848 varRequestIdQueue.erase(
830 849 std::remove(varRequestIdQueue.begin(), varRequestIdQueue.end(), varRequestId),
831 850 varRequestIdQueue.end());
832 851 if (varRequestIdQueue.empty()) {
833 852 varIdToVarRequestIdQueueMapIt
834 853 = m_VarIdToVarRequestIdQueueMap.erase(varIdToVarRequestIdQueueMapIt);
835 854 }
836 855 else {
837 856 ++varIdToVarRequestIdQueueMapIt;
838 857 }
839 858 }
840 859 }
860
861 SqpRange VariableController::VariableControllerPrivate::getLastRequestedRange(QUuid varId)
862 {
863 auto lastRangeRequested = SqpRange{INVALID_RANGE};
864 auto varIdToVarRequestIdQueueMapIt = m_VarIdToVarRequestIdQueueMap.find(varId);
865 if (varIdToVarRequestIdQueueMapIt != m_VarIdToVarRequestIdQueueMap.cend()) {
866 auto &varRequestIdQueue = varIdToVarRequestIdQueueMapIt->second;
867 auto varRequestId = varRequestIdQueue.back();
868 auto varRequestIdToVarIdVarRequestMapIt
869 = m_VarRequestIdToVarIdVarRequestMap.find(varRequestId);
870 if (varRequestIdToVarIdVarRequestMapIt != m_VarRequestIdToVarIdVarRequestMap.cend()) {
871 auto &varIdToVarRequestMap = varRequestIdToVarIdVarRequestMapIt->second;
872 auto varIdToVarRequestMapIt = varIdToVarRequestMap.find(varId);
873 if (varIdToVarRequestMapIt != varIdToVarRequestMap.cend()) {
874 auto &varRequest = varIdToVarRequestMapIt->second;
875 lastRangeRequested = varRequest.m_RangeRequested;
876 }
877 else {
878 qCDebug(LOG_VariableController())
879 << tr("Impossible to getLastRequestedRange of a unknown variable id attached "
880 "to a variableRequestId")
881 << varRequestId << varId;
882 }
883 }
884 else {
885 qCCritical(LOG_VariableController())
886 << tr("Impossible to getLastRequestedRange of a unknown variableRequestId")
887 << varRequestId;
888 }
889 }
890 else {
891 qDebug(LOG_VariableController())
892 << tr("Impossible to getLastRequestedRange of a unknown variable id") << varId;
893 }
894
895 return lastRangeRequested;
896 }
@@ -1,310 +1,391
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 40 /// Provider used for the tests
41 41 class TestProvider : public IDataProvider {
42 42 std::shared_ptr<IDataProvider> clone() const { return std::make_shared<TestProvider>(); }
43 43
44 44 void requestDataLoading(QUuid acqIdentifier, const DataProviderParameters &parameters) override
45 45 {
46 46 const auto &ranges = parameters.m_Times;
47 47
48 48 for (const auto &range : ranges) {
49 49 // Generates data series
50 50 auto valuesData = values(range);
51 51
52 52 std::vector<double> xAxisData{};
53 53 for (auto i = range.m_TStart; i <= range.m_TEnd; ++i) {
54 54 xAxisData.push_back(i);
55 55 }
56 56
57 57 auto dataSeries = std::make_shared<ScalarSeries>(
58 58 std::move(xAxisData), std::move(valuesData), Unit{"t", true}, Unit{});
59 59
60 60 emit dataProvided(acqIdentifier, dataSeries, range);
61 61 }
62 62 }
63 63
64 64 void requestDataAborting(QUuid acqIdentifier) override
65 65 {
66 66 // Does nothing
67 67 }
68 68 };
69 69
70 70 /**
71 71 * Interface representing an operation performed on a variable controller.
72 72 * This interface is used in tests to apply a set of operations and check the status of the
73 73 * controller after each operation
74 74 */
75 75 struct IOperation {
76 76 virtual ~IOperation() = default;
77 77 /// Executes the operation on the variable controller
78 78 virtual void exec(VariableController &variableController) const = 0;
79 79 };
80 80
81 81 /**
82 82 *Variable creation operation in the controller
83 83 */
84 84 struct Create : public IOperation {
85 85 explicit Create(int index) : m_Index{index} {}
86 86
87 87 void exec(VariableController &variableController) const override
88 88 {
89 89 auto variable = variableController.createVariable(QString::number(m_Index), {},
90 90 std::make_unique<TestProvider>());
91 91 }
92 92
93 93 int m_Index; ///< The index of the variable to create in the controller
94 94 };
95 95
96 96 /**
97 97 * Variable move/shift operation in the controller
98 98 */
99 99 struct Move : public IOperation {
100 100 explicit Move(int index, const SqpRange &newRange, bool shift = false)
101 101 : m_Index{index}, m_NewRange{newRange}, m_Shift{shift}
102 102 {
103 103 }
104 104
105 105 void exec(VariableController &variableController) const override
106 106 {
107 107 if (auto variable = variableController.variableModel()->variable(m_Index)) {
108 108 variableController.onRequestDataLoading({variable}, m_NewRange, variable->range(),
109 109 !m_Shift);
110 110 }
111 111 }
112 112
113 113 int m_Index; ///< The index of the variable to move
114 114 SqpRange m_NewRange; ///< The new range of the variable
115 115 bool m_Shift; ///< Performs a shift (
116 116 };
117 117
118 118 /**
119 119 * Variable synchronization/desynchronization operation in the controller
120 120 */
121 121 struct Synchronize : public IOperation {
122 122 explicit Synchronize(int index, QUuid syncId, bool synchronize = true)
123 123 : m_Index{index}, m_SyncId{syncId}, m_Synchronize{synchronize}
124 124 {
125 125 }
126 126
127 127 void exec(VariableController &variableController) const override
128 128 {
129 129 if (auto variable = variableController.variableModel()->variable(m_Index)) {
130 130 if (m_Synchronize) {
131 131 variableController.onAddSynchronized(variable, m_SyncId);
132 132 }
133 133 else {
134 134 variableController.desynchronize(variable, m_SyncId);
135 135 }
136 136 }
137 137 }
138 138
139 139 int m_Index; ///< The index of the variable to sync/desync
140 140 QUuid m_SyncId; ///< The synchronization group of the variable
141 141 bool m_Synchronize; ///< Performs sync or desync operation
142 142 };
143 143
144 144 /**
145 145 * Test Iteration
146 146 *
147 147 * A test iteration includes an operation to be performed, and a set of expected ranges after each
148 148 * operation. Each range is tested after the operation to ensure that:
149 149 * - the range of the variable is the expected range
150 150 * - the data of the variable are those generated for the expected range
151 151 */
152 152 struct Iteration {
153 153 std::shared_ptr<IOperation> m_Operation; ///< Operation to perform
154 154 std::map<int, SqpRange> m_ExpectedRanges; ///< Expected ranges (by variable index)
155 155 };
156 156
157 157 using Iterations = std::vector<Iteration>;
158 158
159 159 } // namespace
160 160
161 161 Q_DECLARE_METATYPE(Iterations)
162 162
163 163 class TestVariableSync : public QObject {
164 164 Q_OBJECT
165 165
166 166 private slots:
167 167 /// Input data for @sa testSync()
168 168 void testSync_data();
169 169
170 170 /// Tests synchronization between variables through several operations
171 171 void testSync();
172 172 };
173 173
174 void TestVariableSync::testSync_data()
175 {
176 // ////////////// //
177 // Test structure //
178 // ////////////// //
179
180 QTest::addColumn<QUuid>("syncId");
181 QTest::addColumn<SqpRange>("initialRange");
182 QTest::addColumn<Iterations>("iterations");
183
184 // ////////// //
185 // Test cases //
186 // ////////// //
174 namespace {
187 175
176 void testSyncCase1()
177 {
188 178 // Id used to synchronize variables in the controller
189 179 auto syncId = QUuid::createUuid();
190 180
191 181 /// Generates a range according to a start time and a end time (the date is the same)
192 182 auto range = [](const QTime &startTime, const QTime &endTime) {
193 183 return SqpRange{DateUtils::secondsSinceEpoch(QDateTime{{2017, 1, 1}, startTime, Qt::UTC}),
194 184 DateUtils::secondsSinceEpoch(QDateTime{{2017, 1, 1}, endTime, Qt::UTC})};
195 185 };
196 186
197 187 auto initialRange = range({12, 0}, {13, 0});
198 188
199 189 Iterations iterations{};
200 190 // Creates variables var0, var1 and var2
201 191 iterations.push_back({std::make_shared<Create>(0), {{0, initialRange}}});
202 192 iterations.push_back({std::make_shared<Create>(1), {{0, initialRange}, {1, initialRange}}});
203 193 iterations.push_back(
204 194 {std::make_shared<Create>(2), {{0, initialRange}, {1, initialRange}, {2, initialRange}}});
205 195
206 196 // Adds variables into the sync group (ranges don't need to be tested here)
207 197 iterations.push_back({std::make_shared<Synchronize>(0, syncId)});
208 198 iterations.push_back({std::make_shared<Synchronize>(1, syncId)});
209 199 iterations.push_back({std::make_shared<Synchronize>(2, syncId)});
210 200
211 201 // Moves var0: ranges of var0, var1 and var2 change
212 202 auto newRange = range({12, 30}, {13, 30});
213 203 iterations.push_back(
214 204 {std::make_shared<Move>(0, newRange), {{0, newRange}, {1, newRange}, {2, newRange}}});
215 205
216 206 // Moves var1: ranges of var0, var1 and var2 change
217 207 newRange = range({13, 0}, {14, 0});
218 208 iterations.push_back(
219 209 {std::make_shared<Move>(0, newRange), {{0, newRange}, {1, newRange}, {2, newRange}}});
220 210
221 211 // Moves var2: ranges of var0, var1 and var2 change
222 212 newRange = range({13, 30}, {14, 30});
223 213 iterations.push_back(
224 214 {std::make_shared<Move>(0, newRange), {{0, newRange}, {1, newRange}, {2, newRange}}});
225 215
226 216 // Desyncs var2 and moves var0:
227 217 // - ranges of var0 and var1 change
228 218 // - range of var2 doesn't change anymore
229 219 auto var2Range = newRange;
230 220 newRange = range({13, 45}, {14, 45});
231 221 iterations.push_back({std::make_shared<Synchronize>(2, syncId, false)});
232 222 iterations.push_back(
233 223 {std::make_shared<Move>(0, newRange), {{0, newRange}, {1, newRange}, {2, var2Range}}});
234 224
235 225 // Shifts var0: although var1 is synchronized with var0, its range doesn't change
236 226 auto var1Range = newRange;
237 227 newRange = range({14, 45}, {15, 45});
238 228 iterations.push_back({std::make_shared<Move>(0, newRange, true),
239 229 {{0, newRange}, {1, var1Range}, {2, var2Range}}});
240 230
241 231 // Moves var0 through several operations:
242 232 // - range of var0 changes
243 233 // - range or var1 changes according to the previous shift (one hour)
244 234 auto moveVar0 = [&iterations](const auto &var0NewRange, const auto &var1ExpectedRange) {
245 235 iterations.push_back(
246 236 {std::make_shared<Move>(0, var0NewRange), {{0, var0NewRange}, {1, var1ExpectedRange}}});
247 237 };
248 238 // Pan left
249 239 moveVar0(range({14, 30}, {15, 30}), range({13, 30}, {14, 30}));
250 240 // Pan right
251 241 moveVar0(range({16, 0}, {17, 0}), range({15, 0}, {16, 0}));
252 242 // Zoom in
253 243 moveVar0(range({16, 30}, {16, 45}), range({15, 30}, {15, 45}));
254 244 // Zoom out
255 245 moveVar0(range({12, 0}, {18, 0}), range({11, 0}, {17, 0}));
256 246
257 QTest::newRow("sync1") << syncId << initialRange << std::move(iterations);
247 QTest::newRow("sync1") << syncId << initialRange << std::move(iterations) << 200;
248 }
249
250 void testSyncCase2()
251 {
252 // Id used to synchronize variables in the controller
253 auto syncId = QUuid::createUuid();
254
255 /// Generates a range according to a start time and a end time (the date is the same)
256 auto dateTime = [](int year, int month, int day, int hours, int minutes, int seconds) {
257 return DateUtils::secondsSinceEpoch(
258 QDateTime{{year, month, day}, QTime{hours, minutes, seconds}, Qt::UTC});
259 };
260
261 auto initialRange = SqpRange{dateTime(2017, 1, 1, 12, 0, 0), dateTime(2017, 1, 1, 13, 0, 0)};
262
263 Iterations iterations{};
264 // Creates variables var0 and var1
265 iterations.push_back({std::make_shared<Create>(0), {{0, initialRange}}});
266 iterations.push_back({std::make_shared<Create>(1), {{0, initialRange}, {1, initialRange}}});
267
268 // Adds variables into the sync group (ranges don't need to be tested here)
269 iterations.push_back({std::make_shared<Synchronize>(0, syncId)});
270 iterations.push_back({std::make_shared<Synchronize>(1, syncId)});
271
272
273 // Moves var0 through several operations:
274 // - range of var0 changes
275 // - range or var1 changes according to the previous shift (one hour)
276 auto moveVar0 = [&iterations](const auto &var0NewRange) {
277 iterations.push_back(
278 {std::make_shared<Move>(0, var0NewRange), {{0, var0NewRange}, {1, var0NewRange}}});
279 };
280 moveVar0(SqpRange{dateTime(2017, 1, 1, 12, 0, 0), dateTime(2017, 1, 1, 13, 0, 0)});
281 moveVar0(SqpRange{dateTime(2017, 1, 1, 14, 0, 0), dateTime(2017, 1, 1, 15, 0, 0)});
282 moveVar0(SqpRange{dateTime(2017, 1, 1, 8, 0, 0), dateTime(2017, 1, 1, 9, 0, 0)});
283 // moveVar0(SqpRange{dateTime(2017, 1, 1, 7, 30, 0), dateTime(2017, 1, 1, 9, 30, 0)});
284 moveVar0(SqpRange{dateTime(2017, 1, 1, 2, 0, 0), dateTime(2017, 1, 1, 4, 0, 0)});
285 moveVar0(SqpRange{dateTime(2017, 1, 1, 6, 0, 0), dateTime(2017, 1, 1, 8, 0, 0)});
286
287 moveVar0(SqpRange{dateTime(2017, 1, 10, 6, 0, 0), dateTime(2017, 1, 15, 8, 0, 0)});
288 moveVar0(SqpRange{dateTime(2017, 1, 17, 6, 0, 0), dateTime(2017, 1, 25, 8, 0, 0)});
289 moveVar0(SqpRange{dateTime(2017, 1, 2, 6, 0, 0), dateTime(2017, 1, 8, 8, 0, 0)});
290
291 moveVar0(SqpRange{dateTime(2017, 4, 10, 6, 0, 0), dateTime(2017, 6, 15, 8, 0, 0)});
292 moveVar0(SqpRange{dateTime(2017, 1, 17, 6, 0, 0), dateTime(2017, 2, 25, 8, 0, 0)});
293 moveVar0(SqpRange{dateTime(2017, 7, 2, 6, 0, 0), dateTime(2017, 10, 8, 8, 0, 0)});
294 moveVar0(SqpRange{dateTime(2017, 4, 10, 6, 0, 0), dateTime(2017, 6, 15, 8, 0, 0)});
295 moveVar0(SqpRange{dateTime(2017, 1, 17, 6, 0, 0), dateTime(2017, 2, 25, 8, 0, 0)});
296 moveVar0(SqpRange{dateTime(2017, 7, 2, 6, 0, 0), dateTime(2017, 10, 8, 8, 0, 0)});
297 moveVar0(SqpRange{dateTime(2017, 4, 10, 6, 0, 0), dateTime(2017, 6, 15, 8, 0, 0)});
298 moveVar0(SqpRange{dateTime(2017, 1, 17, 6, 0, 0), dateTime(2017, 2, 25, 8, 0, 0)});
299 moveVar0(SqpRange{dateTime(2017, 7, 2, 6, 0, 0), dateTime(2017, 10, 8, 8, 0, 0)});
300 moveVar0(SqpRange{dateTime(2017, 4, 10, 6, 0, 0), dateTime(2017, 6, 15, 8, 0, 0)});
301 moveVar0(SqpRange{dateTime(2017, 1, 17, 6, 0, 0), dateTime(2017, 2, 25, 8, 0, 0)});
302 moveVar0(SqpRange{dateTime(2017, 7, 2, 6, 0, 0), dateTime(2017, 10, 8, 8, 0, 0)});
303
304
305 QTest::newRow("sync2") << syncId << initialRange << iterations << 4000;
306 // QTest::newRow("sync3") << syncId << initialRange << iterations << 5000;
307 }
308 }
309
310 void TestVariableSync::testSync_data()
311 {
312 // ////////////// //
313 // Test structure //
314 // ////////////// //
315
316 QTest::addColumn<QUuid>("syncId");
317 QTest::addColumn<SqpRange>("initialRange");
318 QTest::addColumn<Iterations>("iterations");
319 QTest::addColumn<int>("operationDelay");
320
321 // ////////// //
322 // Test cases //
323 // ////////// //
324
325 testSyncCase1();
326 testSyncCase2();
258 327 }
259 328
260 329 void TestVariableSync::testSync()
261 330 {
262 331 // Inits controllers
263 332 TimeController timeController{};
264 333 VariableController variableController{};
265 334 variableController.setTimeController(&timeController);
266 335
267 336 QFETCH(QUuid, syncId);
268 337 QFETCH(SqpRange, initialRange);
269 338 timeController.onTimeToUpdate(initialRange);
270 339
271 340 // Synchronization group used
272 341 variableController.onAddSynchronizationGroupId(syncId);
273 342
274 // For each iteration:
275 // - execute operation
276 // - compare the variables' state to the expected states
277 QFETCH(Iterations, iterations);
278 for (const auto &iteration : iterations) {
279 iteration.m_Operation->exec(variableController);
280 QTest::qWait(OPERATION_DELAY);
281
282 for (const auto &expectedRangeEntry : iteration.m_ExpectedRanges) {
343 auto validateRanges = [&variableController](const auto &expectedRanges) {
344 for (const auto &expectedRangeEntry : expectedRanges) {
283 345 auto variableIndex = expectedRangeEntry.first;
284 346 auto expectedRange = expectedRangeEntry.second;
285 347
286 348 // Gets the variable in the controller
287 349 auto variable = variableController.variableModel()->variable(variableIndex);
288 350
289 351 // Compares variable's range to the expected range
290 352 QVERIFY(variable != nullptr);
291 353 auto range = variable->range();
292 354 QCOMPARE(range, expectedRange);
293 355
294 356 // Compares variable's data with values expected for its range
295 357 auto dataSeries = variable->dataSeries();
296 358 QVERIFY(dataSeries != nullptr);
297 359
298 360 auto it = dataSeries->xAxisRange(range.m_TStart, range.m_TEnd);
299 361 auto expectedValues = values(range);
362 qInfo() << std::distance(it.first, it.second) << expectedValues.size();
300 363 QVERIFY(std::equal(it.first, it.second, expectedValues.cbegin(), expectedValues.cend(),
301 364 [](const auto &dataSeriesIt, const auto &expectedValue) {
302 365 return dataSeriesIt.value() == expectedValue;
303 366 }));
304 367 }
368 };
369
370 // For each iteration:
371 // - execute operation
372 // - compare the variables' state to the expected states
373 QFETCH(Iterations, iterations);
374 QFETCH(int, operationDelay);
375 for (const auto &iteration : iterations) {
376 iteration.m_Operation->exec(variableController);
377 QTest::qWait(operationDelay);
378
379 validateRanges(iteration.m_ExpectedRanges);
380 }
381
382 for (const auto &iteration : iterations) {
383 iteration.m_Operation->exec(variableController);
305 384 }
385 QTest::qWait(operationDelay);
386 validateRanges(iterations.back().m_ExpectedRanges);
306 387 }
307 388
308 389 QTEST_MAIN(TestVariableSync)
309 390
310 391 #include "TestVariableSync.moc"
@@ -1,302 +1,304
1 1 #include "Visualization/VisualizationZoneWidget.h"
2 2
3 3 #include "Visualization/IVisualizationWidgetVisitor.h"
4 4 #include "Visualization/QCustomPlotSynchronizer.h"
5 5 #include "Visualization/VisualizationGraphWidget.h"
6 6 #include "ui_VisualizationZoneWidget.h"
7 7
8 8 #include <Data/SqpRange.h>
9 9 #include <Variable/Variable.h>
10 10 #include <Variable/VariableController.h>
11 11
12 12 #include <QUuid>
13 13 #include <SqpApplication.h>
14 14 #include <cmath>
15 15
16 16 Q_LOGGING_CATEGORY(LOG_VisualizationZoneWidget, "VisualizationZoneWidget")
17 17
18 18 namespace {
19 19
20 20 /// Minimum height for graph added in zones (in pixels)
21 21 const auto GRAPH_MINIMUM_HEIGHT = 300;
22 22
23 23 /// Generates a default name for a new graph, according to the number of graphs already displayed in
24 24 /// the zone
25 25 QString defaultGraphName(const QLayout &layout)
26 26 {
27 27 auto count = 0;
28 28 for (auto i = 0; i < layout.count(); ++i) {
29 29 if (dynamic_cast<VisualizationGraphWidget *>(layout.itemAt(i)->widget())) {
30 30 count++;
31 31 }
32 32 }
33 33
34 34 return QObject::tr("Graph %1").arg(count + 1);
35 35 }
36 36
37 37 /**
38 38 * Applies a function to all graphs of the zone represented by its layout
39 39 * @param layout the layout that contains graphs
40 40 * @param fun the function to apply to each graph
41 41 */
42 42 template <typename Fun>
43 43 void processGraphs(QLayout &layout, Fun fun)
44 44 {
45 45 for (auto i = 0; i < layout.count(); ++i) {
46 46 if (auto item = layout.itemAt(i)) {
47 47 if (auto visualizationGraphWidget
48 48 = dynamic_cast<VisualizationGraphWidget *>(item->widget())) {
49 49 fun(*visualizationGraphWidget);
50 50 }
51 51 }
52 52 }
53 53 }
54 54
55 55 } // namespace
56 56
57 57 struct VisualizationZoneWidget::VisualizationZoneWidgetPrivate {
58 58
59 59 explicit VisualizationZoneWidgetPrivate()
60 60 : m_SynchronisationGroupId{QUuid::createUuid()},
61 61 m_Synchronizer{std::make_unique<QCustomPlotSynchronizer>()}
62 62 {
63 63 }
64 64 QUuid m_SynchronisationGroupId;
65 65 std::unique_ptr<IGraphSynchronizer> m_Synchronizer;
66 66 };
67 67
68 68 VisualizationZoneWidget::VisualizationZoneWidget(const QString &name, QWidget *parent)
69 69 : QWidget{parent},
70 70 ui{new Ui::VisualizationZoneWidget},
71 71 impl{spimpl::make_unique_impl<VisualizationZoneWidgetPrivate>()}
72 72 {
73 73 ui->setupUi(this);
74 74
75 75 ui->zoneNameLabel->setText(name);
76 76
77 77 // 'Close' options : widget is deleted when closed
78 78 setAttribute(Qt::WA_DeleteOnClose);
79 79 connect(ui->closeButton, &QToolButton::clicked, this, &VisualizationZoneWidget::close);
80 80 ui->closeButton->setIcon(sqpApp->style()->standardIcon(QStyle::SP_TitleBarCloseButton));
81 81
82 82 // Synchronisation id
83 83 QMetaObject::invokeMethod(&sqpApp->variableController(), "onAddSynchronizationGroupId",
84 84 Qt::QueuedConnection, Q_ARG(QUuid, impl->m_SynchronisationGroupId));
85 85 }
86 86
87 87 VisualizationZoneWidget::~VisualizationZoneWidget()
88 88 {
89 89 delete ui;
90 90 }
91 91
92 92 void VisualizationZoneWidget::addGraph(VisualizationGraphWidget *graphWidget)
93 93 {
94 94 // Synchronize new graph with others in the zone
95 95 impl->m_Synchronizer->addGraph(*graphWidget);
96 96
97 97 ui->visualizationZoneFrame->layout()->addWidget(graphWidget);
98 98 }
99 99
100 100 VisualizationGraphWidget *VisualizationZoneWidget::createGraph(std::shared_ptr<Variable> variable)
101 101 {
102 102 auto graphWidget = new VisualizationGraphWidget{
103 103 defaultGraphName(*ui->visualizationZoneFrame->layout()), this};
104 104
105 105
106 106 // Set graph properties
107 107 graphWidget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::MinimumExpanding);
108 108 graphWidget->setMinimumHeight(GRAPH_MINIMUM_HEIGHT);
109 109
110 110
111 111 // Lambda to synchronize zone widget
112 112 auto synchronizeZoneWidget = [this, graphWidget](const SqpRange &graphRange,
113 113 const SqpRange &oldGraphRange) {
114 114
115 115 auto zoomType = VariableController::getZoomType(graphRange, oldGraphRange);
116 116 auto frameLayout = ui->visualizationZoneFrame->layout();
117 117 for (auto i = 0; i < frameLayout->count(); ++i) {
118 118 auto graphChild
119 119 = dynamic_cast<VisualizationGraphWidget *>(frameLayout->itemAt(i)->widget());
120 120 if (graphChild && (graphChild != graphWidget)) {
121 121
122 122 auto graphChildRange = graphChild->graphRange();
123 123 switch (zoomType) {
124 124 case AcquisitionZoomType::ZoomIn: {
125 125 auto deltaLeft = graphRange.m_TStart - oldGraphRange.m_TStart;
126 126 auto deltaRight = oldGraphRange.m_TEnd - graphRange.m_TEnd;
127 127 graphChildRange.m_TStart += deltaLeft;
128 128 graphChildRange.m_TEnd -= deltaRight;
129 129 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: ZoomIn");
130 130 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: deltaLeft")
131 131 << deltaLeft;
132 132 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: deltaRight")
133 133 << deltaRight;
134 134 qCDebug(LOG_VisualizationZoneWidget())
135 135 << tr("TORM: dt") << graphRange.m_TEnd - graphRange.m_TStart;
136 136
137 137 break;
138 138 }
139 139
140 140 case AcquisitionZoomType::ZoomOut: {
141 141 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: ZoomOut");
142 142 auto deltaLeft = oldGraphRange.m_TStart - graphRange.m_TStart;
143 143 auto deltaRight = graphRange.m_TEnd - oldGraphRange.m_TEnd;
144 144 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: deltaLeft")
145 145 << deltaLeft;
146 146 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: deltaRight")
147 147 << deltaRight;
148 148 qCDebug(LOG_VisualizationZoneWidget())
149 149 << tr("TORM: dt") << graphRange.m_TEnd - graphRange.m_TStart;
150 150 graphChildRange.m_TStart -= deltaLeft;
151 151 graphChildRange.m_TEnd += deltaRight;
152 152 break;
153 153 }
154 154 case AcquisitionZoomType::PanRight: {
155 155 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: PanRight");
156 auto deltaLeft = graphRange.m_TStart - oldGraphRange.m_TStart;
156 157 auto deltaRight = graphRange.m_TEnd - oldGraphRange.m_TEnd;
157 graphChildRange.m_TStart += deltaRight;
158 graphChildRange.m_TStart += deltaLeft;
158 159 graphChildRange.m_TEnd += deltaRight;
159 160 qCDebug(LOG_VisualizationZoneWidget())
160 161 << tr("TORM: dt") << graphRange.m_TEnd - graphRange.m_TStart;
161 162 break;
162 163 }
163 164 case AcquisitionZoomType::PanLeft: {
164 165 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: PanLeft");
165 166 auto deltaLeft = oldGraphRange.m_TStart - graphRange.m_TStart;
167 auto deltaRight = oldGraphRange.m_TEnd - graphRange.m_TEnd;
166 168 graphChildRange.m_TStart -= deltaLeft;
167 graphChildRange.m_TEnd -= deltaLeft;
169 graphChildRange.m_TEnd -= deltaRight;
168 170 break;
169 171 }
170 172 case AcquisitionZoomType::Unknown: {
171 173 qCDebug(LOG_VisualizationZoneWidget())
172 174 << tr("Impossible to synchronize: zoom type unknown");
173 175 break;
174 176 }
175 177 default:
176 178 qCCritical(LOG_VisualizationZoneWidget())
177 179 << tr("Impossible to synchronize: zoom type not take into account");
178 180 // No action
179 181 break;
180 182 }
181 183 graphChild->enableAcquisition(false);
182 184 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: Range before: ")
183 185 << graphChild->graphRange();
184 186 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: Range after : ")
185 187 << graphChildRange;
186 188 qCDebug(LOG_VisualizationZoneWidget())
187 189 << tr("TORM: child dt") << graphChildRange.m_TEnd - graphChildRange.m_TStart;
188 190 graphChild->setGraphRange(graphChildRange);
189 191 graphChild->enableAcquisition(true);
190 192 }
191 193 }
192 194 };
193 195
194 196 // connection for synchronization
195 197 connect(graphWidget, &VisualizationGraphWidget::synchronize, synchronizeZoneWidget);
196 198 connect(graphWidget, &VisualizationGraphWidget::variableAdded, this,
197 199 &VisualizationZoneWidget::onVariableAdded);
198 200 connect(graphWidget, &VisualizationGraphWidget::variableAboutToBeRemoved, this,
199 201 &VisualizationZoneWidget::onVariableAboutToBeRemoved);
200 202
201 203 auto range = SqpRange{};
202 204
203 205 // Apply visitor to graph children
204 206 auto layout = ui->visualizationZoneFrame->layout();
205 207 if (layout->count() > 0) {
206 208 // Case of a new graph in a existant zone
207 209 if (auto visualizationGraphWidget
208 210 = dynamic_cast<VisualizationGraphWidget *>(layout->itemAt(0)->widget())) {
209 211 range = visualizationGraphWidget->graphRange();
210 212 }
211 213 }
212 214 else {
213 215 // Case of a new graph as the first of the zone
214 216 range = variable->range();
215 217 }
216 218
217 219 this->addGraph(graphWidget);
218 220
219 221 graphWidget->addVariable(variable, range);
220 222
221 223 // get y using variable range
222 224 if (auto dataSeries = variable->dataSeries()) {
223 225 dataSeries->lockRead();
224 226 auto valuesBounds
225 227 = dataSeries->valuesBounds(variable->range().m_TStart, variable->range().m_TEnd);
226 228 auto end = dataSeries->cend();
227 229 if (valuesBounds.first != end && valuesBounds.second != end) {
228 230 auto rangeValue = [](const auto &value) { return std::isnan(value) ? 0. : value; };
229 231
230 232 auto minValue = rangeValue(valuesBounds.first->minValue());
231 233 auto maxValue = rangeValue(valuesBounds.second->maxValue());
232 234
233 235 graphWidget->setYRange(SqpRange{minValue, maxValue});
234 236 }
235 237 dataSeries->unlock();
236 238 }
237 239
238 240 return graphWidget;
239 241 }
240 242
241 243 void VisualizationZoneWidget::accept(IVisualizationWidgetVisitor *visitor)
242 244 {
243 245 if (visitor) {
244 246 visitor->visitEnter(this);
245 247
246 248 // Apply visitor to graph children: widgets different from graphs are not visited (no
247 249 // action)
248 250 processGraphs(
249 251 *ui->visualizationZoneFrame->layout(),
250 252 [visitor](VisualizationGraphWidget &graphWidget) { graphWidget.accept(visitor); });
251 253
252 254 visitor->visitLeave(this);
253 255 }
254 256 else {
255 257 qCCritical(LOG_VisualizationZoneWidget()) << tr("Can't visit widget : the visitor is null");
256 258 }
257 259 }
258 260
259 261 bool VisualizationZoneWidget::canDrop(const Variable &variable) const
260 262 {
261 263 // A tab can always accomodate a variable
262 264 Q_UNUSED(variable);
263 265 return true;
264 266 }
265 267
266 268 bool VisualizationZoneWidget::contains(const Variable &variable) const
267 269 {
268 270 Q_UNUSED(variable);
269 271 return false;
270 272 }
271 273
272 274 QString VisualizationZoneWidget::name() const
273 275 {
274 276 return ui->zoneNameLabel->text();
275 277 }
276 278
277 279 void VisualizationZoneWidget::closeEvent(QCloseEvent *event)
278 280 {
279 281 // Closes graphs in the zone
280 282 processGraphs(*ui->visualizationZoneFrame->layout(),
281 283 [](VisualizationGraphWidget &graphWidget) { graphWidget.close(); });
282 284
283 285 // Delete synchronization group from variable controller
284 286 QMetaObject::invokeMethod(&sqpApp->variableController(), "onRemoveSynchronizationGroupId",
285 287 Qt::QueuedConnection, Q_ARG(QUuid, impl->m_SynchronisationGroupId));
286 288
287 289 QWidget::closeEvent(event);
288 290 }
289 291
290 292 void VisualizationZoneWidget::onVariableAdded(std::shared_ptr<Variable> variable)
291 293 {
292 294 QMetaObject::invokeMethod(&sqpApp->variableController(), "onAddSynchronized",
293 295 Qt::QueuedConnection, Q_ARG(std::shared_ptr<Variable>, variable),
294 296 Q_ARG(QUuid, impl->m_SynchronisationGroupId));
295 297 }
296 298
297 299 void VisualizationZoneWidget::onVariableAboutToBeRemoved(std::shared_ptr<Variable> variable)
298 300 {
299 301 QMetaObject::invokeMethod(&sqpApp->variableController(), "desynchronize", Qt::QueuedConnection,
300 302 Q_ARG(std::shared_ptr<Variable>, variable),
301 303 Q_ARG(QUuid, impl->m_SynchronisationGroupId));
302 304 }
@@ -1,36 +1,41
1 1 #ifndef SCIQLOP_COSINUSPROVIDER_H
2 2 #define SCIQLOP_COSINUSPROVIDER_H
3 3
4 4 #include "MockPluginGlobal.h"
5 5
6 6 #include <Data/IDataProvider.h>
7 7
8 8 #include <QLoggingCategory>
9 9 #include <QUuid>
10 10
11 11 #include <QHash>
12 12 Q_DECLARE_LOGGING_CATEGORY(LOG_CosinusProvider)
13 13
14 14 /**
15 15 * @brief The CosinusProvider class is an example of how a data provider can generate data
16 16 */
17 17 class SCIQLOP_MOCKPLUGIN_EXPORT CosinusProvider : public IDataProvider {
18 18 public:
19 19 std::shared_ptr<IDataProvider> clone() const override;
20 20
21 21 /// @sa IDataProvider::requestDataLoading(). The current impl isn't thread safe.
22 22 void requestDataLoading(QUuid acqIdentifier, const DataProviderParameters &parameters) override;
23 23
24 24
25 25 /// @sa IDataProvider::requestDataAborting(). The current impl isn't thread safe.
26 26 void requestDataAborting(QUuid acqIdentifier) override;
27 27
28 28
29 /// Provide data
30 std::shared_ptr<IDataSeries> provideDataSeries(const SqpRange &dataRangeRequested,
31 const QVariantHash &data);
32
33
29 34 private:
30 35 std::shared_ptr<IDataSeries>
31 36 retrieveData(QUuid acqIdentifier, const SqpRange &dataRangeRequested, const QVariantHash &data);
32 37
33 38 QHash<QUuid, bool> m_VariableToEnableProvider;
34 39 };
35 40
36 41 #endif // SCIQLOP_COSINUSPROVIDER_H
@@ -1,201 +1,213
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 <Data/VectorSeries.h>
7 7
8 8 #include <cmath>
9 9
10 10 #include <QFuture>
11 11 #include <QThread>
12 12 #include <QtConcurrent/QtConcurrent>
13 13
14 14 Q_LOGGING_CATEGORY(LOG_CosinusProvider, "CosinusProvider")
15 15
16 16 namespace {
17 17
18 18 /// Abstract cosinus type
19 19 struct ICosinusType {
20 20 virtual ~ICosinusType() = default;
21 21 /// @return the number of components generated for the type
22 22 virtual int componentCount() const = 0;
23 23 /// @return the data series created for the type
24 24 virtual std::shared_ptr<IDataSeries> createDataSeries(std::vector<double> xAxisData,
25 25 std::vector<double> valuesData,
26 26 Unit xAxisUnit,
27 27 Unit valuesUnit) const = 0;
28 28 };
29 29
30 30 struct ScalarCosinus : public ICosinusType {
31 31 int componentCount() const override { return 1; }
32 32
33 33 std::shared_ptr<IDataSeries> createDataSeries(std::vector<double> xAxisData,
34 34 std::vector<double> valuesData, Unit xAxisUnit,
35 35 Unit valuesUnit) const override
36 36 {
37 37 return std::make_shared<ScalarSeries>(std::move(xAxisData), std::move(valuesData),
38 38 xAxisUnit, valuesUnit);
39 39 }
40 40 };
41 41 struct VectorCosinus : public ICosinusType {
42 42 int componentCount() const override { return 3; }
43 43
44 44 std::shared_ptr<IDataSeries> createDataSeries(std::vector<double> xAxisData,
45 45 std::vector<double> valuesData, Unit xAxisUnit,
46 46 Unit valuesUnit) const override
47 47 {
48 48 return std::make_shared<VectorSeries>(std::move(xAxisData), std::move(valuesData),
49 49 xAxisUnit, valuesUnit);
50 50 }
51 51 };
52 52
53 53 /// Converts string to cosinus type
54 54 /// @return the cosinus type if the string could be converted, nullptr otherwise
55 55 std::unique_ptr<ICosinusType> cosinusType(const QString &type) noexcept
56 56 {
57 57 if (type.compare(QStringLiteral("scalar"), Qt::CaseInsensitive) == 0) {
58 58 return std::make_unique<ScalarCosinus>();
59 59 }
60 60 else if (type.compare(QStringLiteral("vector"), Qt::CaseInsensitive) == 0) {
61 61 return std::make_unique<VectorCosinus>();
62 62 }
63 63 else {
64 64 return nullptr;
65 65 }
66 66 }
67 67
68 68 } // namespace
69 69
70 70 std::shared_ptr<IDataProvider> CosinusProvider::clone() const
71 71 {
72 72 // No copy is made in clone
73 73 return std::make_shared<CosinusProvider>();
74 74 }
75 75
76 76 std::shared_ptr<IDataSeries> CosinusProvider::retrieveData(QUuid acqIdentifier,
77 77 const SqpRange &dataRangeRequested,
78 78 const QVariantHash &data)
79 79 {
80 80 // TODO: Add Mutex
81 81 auto dataIndex = 0;
82 82
83 83 // Retrieves cosinus type
84 84 auto typeVariant = data.value(COSINUS_TYPE_KEY, COSINUS_TYPE_DEFAULT_VALUE);
85 85 if (!typeVariant.canConvert<QString>()) {
86 86 qCCritical(LOG_CosinusProvider()) << tr("Can't retrieve data: invalid type");
87 87 return nullptr;
88 88 }
89 89
90 90 auto type = cosinusType(typeVariant.toString());
91 91 if (!type) {
92 92 qCCritical(LOG_CosinusProvider()) << tr("Can't retrieve data: unknown type");
93 93 return nullptr;
94 94 }
95 95
96 96 // Retrieves frequency
97 97 auto freqVariant = data.value(COSINUS_FREQUENCY_KEY, COSINUS_FREQUENCY_DEFAULT_VALUE);
98 98 if (!freqVariant.canConvert<double>()) {
99 99 qCCritical(LOG_CosinusProvider()) << tr("Can't retrieve data: invalid frequency");
100 100 return nullptr;
101 101 }
102 102
103 103 // Gets the timerange from the parameters
104 104 double freq = freqVariant.toDouble();
105 105 double start = std::ceil(dataRangeRequested.m_TStart * freq);
106 106 double end = std::floor(dataRangeRequested.m_TEnd * freq);
107 107
108 108 // We assure that timerange is valid
109 109 if (end < start) {
110 110 std::swap(start, end);
111 111 }
112 112
113 113 // Generates scalar series containing cosinus values (one value per second, end value is
114 114 // included)
115 115 auto dataCount = end - start + 1;
116 116
117 117 // Number of components (depending on the cosinus type)
118 118 auto componentCount = type->componentCount();
119 119
120 120 auto xAxisData = std::vector<double>{};
121 121 xAxisData.resize(dataCount);
122 122
123 123 auto valuesData = std::vector<double>{};
124 124 valuesData.resize(dataCount * componentCount);
125 125
126 126 int progress = 0;
127 127 auto progressEnd = dataCount;
128 128 for (auto time = start; time <= end; ++time, ++dataIndex) {
129 129 auto it = m_VariableToEnableProvider.find(acqIdentifier);
130 130 if (it != m_VariableToEnableProvider.end() && it.value()) {
131 131 const auto timeOnFreq = time / freq;
132 132
133 133 xAxisData[dataIndex] = timeOnFreq;
134 134
135 135 // Generates all components' values
136 136 // Example: for a vector, values will be : cos(x), cos(x)/2, cos(x)/3
137 137 auto value = std::cos(timeOnFreq);
138 138 for (auto i = 0; i < componentCount; ++i) {
139 139 valuesData[componentCount * dataIndex + i] = value / (i + 1);
140 140 }
141 141
142 142 // progression
143 143 int currentProgress = (time - start) * 100.0 / progressEnd;
144 144 if (currentProgress != progress) {
145 145 progress = currentProgress;
146 146
147 147 emit dataProvidedProgress(acqIdentifier, progress);
148 qCInfo(LOG_CosinusProvider()) << "TORM: CosinusProvider::retrieveData"
149 << QThread::currentThread()->objectName() << progress;
148 qCDebug(LOG_CosinusProvider()) << "TORM: CosinusProvider::retrieveData"
149 << QThread::currentThread()->objectName()
150 << progress;
150 151 // NOTE: Try to use multithread if possible
151 152 }
152 153 }
153 154 else {
154 155 if (!it.value()) {
155 156 qCDebug(LOG_CosinusProvider())
156 157 << "CosinusProvider::retrieveData: ARRET De l'acquisition detectΓ©"
157 158 << end - time;
158 159 }
159 160 }
160 161 }
161 162 if (progress != 100) {
162 163 // We can close progression beacause all data has been retrieved
163 164 emit dataProvidedProgress(acqIdentifier, 100);
164 165 }
165 166 return type->createDataSeries(std::move(xAxisData), std::move(valuesData),
166 167 Unit{QStringLiteral("t"), true}, Unit{});
167 168 }
168 169
169 170 void CosinusProvider::requestDataLoading(QUuid acqIdentifier,
170 171 const DataProviderParameters &parameters)
171 172 {
172 173 // TODO: Add Mutex
173 174 m_VariableToEnableProvider[acqIdentifier] = true;
174 175 qCDebug(LOG_CosinusProvider()) << "TORM: CosinusProvider::requestDataLoading"
175 176 << QThread::currentThread()->objectName();
176 177 // NOTE: Try to use multithread if possible
177 178 const auto times = parameters.m_Times;
178 179
179 180 for (const auto &dateTime : qAsConst(times)) {
180 181 if (m_VariableToEnableProvider[acqIdentifier]) {
181 182 auto scalarSeries = this->retrieveData(acqIdentifier, dateTime, parameters.m_Data);
182 183 qCDebug(LOG_CosinusProvider()) << "TORM: CosinusProvider::dataProvided";
183 184 emit dataProvided(acqIdentifier, scalarSeries, dateTime);
184 185 }
185 186 }
186 187 }
187 188
188 189 void CosinusProvider::requestDataAborting(QUuid acqIdentifier)
189 190 {
190 191 // TODO: Add Mutex
191 192 qCDebug(LOG_CosinusProvider()) << "CosinusProvider::requestDataAborting" << acqIdentifier
192 193 << QThread::currentThread()->objectName();
193 194 auto it = m_VariableToEnableProvider.find(acqIdentifier);
194 195 if (it != m_VariableToEnableProvider.end()) {
195 196 it.value() = false;
196 197 }
197 198 else {
198 199 qCWarning(LOG_CosinusProvider())
199 200 << tr("Aborting progression of inexistant identifier detected !!!");
200 201 }
201 202 }
203
204 std::shared_ptr<IDataSeries> CosinusProvider::provideDataSeries(const SqpRange &dataRangeRequested,
205 const QVariantHash &data)
206 {
207 auto uid = QUuid::createUuid();
208 m_VariableToEnableProvider[uid] = true;
209 auto dataSeries = this->retrieveData(uid, dataRangeRequested, data);
210
211 m_VariableToEnableProvider.remove(uid);
212 return dataSeries;
213 }
@@ -1,193 +1,194
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 /// Delay after each operation on the variable before validating it (in ms)
28 const auto OPERATION_DELAY = 250;
29
30 27 /**
31 28 * Verifies that the data in the candidate series are identical to the data in the reference series
32 29 * in a specific range
33 30 * @param candidate the candidate data series
34 31 * @param range the range to check
35 32 * @param reference the reference data series
36 33 * @return true if the data of the candidate series and the reference series are identical in the
37 34 * range, false otherwise
38 35 */
39 36 bool checkDataSeries(std::shared_ptr<IDataSeries> candidate, const SqpRange &range,
40 37 std::shared_ptr<IDataSeries> reference)
41 38 {
42 39 if (candidate == nullptr || reference == nullptr) {
43 40 return candidate == reference;
44 41 }
45 42
46 43 auto referenceIt = reference->xAxisRange(range.m_TStart, range.m_TEnd);
47 44
45 qInfo() << "candidateSize" << std::distance(candidate->cbegin(), candidate->cend());
46 qInfo() << "refSize" << std::distance(referenceIt.first, referenceIt.second);
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 /// Generates the data series from the reading of a data stream
58 std::shared_ptr<IDataSeries> readDataStream(QTextStream &stream)
59 {
60 std::vector<double> xAxisData, valuesData;
61
62 QString line{};
63 while (stream.readLineInto(&line)) {
64 // Separates date (x-axis data) to value data
65 auto splitLine = line.split('\t');
66 if (splitLine.size() == 2) {
67 // Converts datetime to double
68 auto dateTime = QDateTime::fromString(splitLine[0], DATETIME_FORMAT);
69 dateTime.setTimeSpec(Qt::UTC);
70 xAxisData.push_back(DateUtils::secondsSinceEpoch(dateTime));
71
72 valuesData.push_back(splitLine[1].toDouble());
73 }
74 }
75
76 return std::make_shared<ScalarSeries>(std::move(xAxisData), std::move(valuesData),
77 Unit{{}, true}, Unit{});
78 }
79
80 57 } // namespace
81 58
82 59 /**
83 60 * @brief The TestCosinusAcquisition class tests acquisition in SciQlop (operations like zooms in,
84 61 * zooms out, pans) of data from CosinusProvider
85 62 * @sa CosinusProvider
86 63 */
87 64 class TestCosinusAcquisition : public QObject {
88 65 Q_OBJECT
89 66
90 67 private slots:
91 68 /// Input data for @sa testAcquisition()
92 69 void testAcquisition_data();
93 70 void testAcquisition();
94 71 };
95 72
96 73 void TestCosinusAcquisition::testAcquisition_data()
97 74 {
98 75 // ////////////// //
99 76 // Test structure //
100 77 // ////////////// //
101 78
102 QTest::addColumn<QString>("dataFilename"); // File containing expected data of acquisitions
103 QTest::addColumn<SqpRange>("initialRange"); // First acquisition
79 QTest::addColumn<SqpRange>("referenceRange"); // Range for generating reference series
80 QTest::addColumn<SqpRange>("initialRange"); // First acquisition
81 QTest::addColumn<int>("operationDelay"); // Acquisitions to make
104 82 QTest::addColumn<std::vector<SqpRange> >("operations"); // Acquisitions to make
105 83
106 84 // ////////// //
107 85 // Test cases //
108 86 // ////////// //
109 87
110 88 auto dateTime = [](int year, int month, int day, int hours, int minutes, int seconds) {
111 89 return DateUtils::secondsSinceEpoch(
112 90 QDateTime{{year, month, day}, {hours, minutes, seconds}, Qt::UTC});
113 91 };
114 92
115 93 QTest::newRow("cosinus")
116 << "Cosinus_100Hz_20170101_1200_20170101_1300.txt"
117 << SqpRange{dateTime(2017, 1, 1, 12, 30, 0), dateTime(2017, 1, 1, 12, 35, 1)}
94 << SqpRange{dateTime(2017, 1, 1, 12, 0, 0), dateTime(2017, 1, 1, 13, 0, 0)}
95 << SqpRange{dateTime(2017, 1, 1, 12, 30, 0), dateTime(2017, 1, 1, 12, 35, 1)} << 250
118 96 << std::vector<SqpRange>{
119 97 // Pan (jump) left
120 98 SqpRange{dateTime(2017, 1, 1, 12, 45, 0), dateTime(2017, 1, 1, 12, 50, 0)},
121 99 // Pan (jump) right
122 100 SqpRange{dateTime(2017, 1, 1, 12, 15, 0), dateTime(2017, 1, 1, 12, 20, 0)},
123 101 // Pan (overlay) right
124 102 SqpRange{dateTime(2017, 1, 1, 12, 14, 0), dateTime(2017, 1, 1, 12, 19, 0)},
125 103 // Pan (overlay) left
126 104 SqpRange{dateTime(2017, 1, 1, 12, 15, 0), dateTime(2017, 1, 1, 12, 20, 0)},
127 105 // Pan (overlay) left
128 106 SqpRange{dateTime(2017, 1, 1, 12, 16, 0), dateTime(2017, 1, 1, 12, 21, 0)},
129 107 // Zoom in
130 108 SqpRange{dateTime(2017, 1, 1, 12, 17, 30), dateTime(2017, 1, 1, 12, 19, 30)},
131 109 // Zoom out
132 110 SqpRange{dateTime(2017, 1, 1, 12, 12, 30), dateTime(2017, 1, 1, 12, 24, 30)}};
111
112 QTest::newRow("cosinus_big")
113 << SqpRange{dateTime(2017, 1, 1, 1, 0, 0), dateTime(2017, 1, 5, 13, 0, 0)}
114 << SqpRange{dateTime(2017, 1, 2, 6, 30, 0), dateTime(2017, 1, 2, 18, 30, 0)} << 5000
115 << std::vector<SqpRange>{
116 // Pan (jump) left
117 SqpRange{dateTime(2017, 1, 1, 13, 30, 0), dateTime(2017, 1, 1, 18, 30, 0)},
118 // Pan (jump) right
119 SqpRange{dateTime(2017, 1, 3, 4, 30, 0), dateTime(2017, 1, 3, 10, 30, 0)},
120 // Pan (overlay) right
121 SqpRange{dateTime(2017, 1, 3, 8, 30, 0), dateTime(2017, 1, 3, 12, 30, 0)},
122 // Pan (overlay) left
123 SqpRange{dateTime(2017, 1, 2, 8, 30, 0), dateTime(2017, 1, 3, 10, 30, 0)},
124 // Pan (overlay) left
125 SqpRange{dateTime(2017, 1, 1, 12, 30, 0), dateTime(2017, 1, 3, 5, 30, 0)},
126 // Zoom in
127 SqpRange{dateTime(2017, 1, 2, 2, 30, 0), dateTime(2017, 1, 2, 8, 30, 0)},
128 // Zoom out
129 SqpRange{dateTime(2017, 1, 1, 14, 30, 0), dateTime(2017, 1, 3, 12, 30, 0)}};
133 130 }
134 131
135 132 void TestCosinusAcquisition::testAcquisition()
136 133 {
137 // Retrieves data file
138 QFETCH(QString, dataFilename);
139
140 auto dataFilePath = QFileInfo{TESTS_RESOURCES_PATH, dataFilename}.absoluteFilePath();
141 QFile dataFile{dataFilePath};
142
143 if (dataFile.open(QFile::ReadOnly)) {
144 // Generates data series to compare with
145 QTextStream dataStream{&dataFile};
146 auto dataSeries = readDataStream(dataStream);
147
148 /// Lambda used to validate a variable at each step
149 auto validateVariable = [dataSeries](std::shared_ptr<Variable> variable,
150 const SqpRange &range) {
151 // Checks that the variable's range has changed
152 QCOMPARE(variable->range(), range);
153
154 // Checks the variable's data series
155 QVERIFY(checkDataSeries(variable->dataSeries(), variable->cacheRange(), dataSeries));
156 };
157
158 // Creates variable
159 QFETCH(SqpRange, initialRange);
160 sqpApp->timeController().onTimeToUpdate(initialRange);
161 auto provider = std::make_shared<CosinusProvider>();
162 auto variable = sqpApp->variableController().createVariable(
163 "MMS", {{COSINUS_TYPE_KEY, "scalar"}, {COSINUS_FREQUENCY_KEY, 100.}}, provider);
164
165 QTest::qWait(OPERATION_DELAY);
166 validateVariable(variable, initialRange);
167
168 // Makes operations on the variable
169 QFETCH(std::vector<SqpRange>, operations);
170 for (const auto &operation : operations) {
171 // Asks request on the variable and waits during its execution
172 sqpApp->variableController().onRequestDataLoading({variable}, operation,
173 variable->range(), true);
174
175 QTest::qWait(OPERATION_DELAY);
176 validateVariable(variable, operation);
177 }
134 // Retrieves reference range
135 QFETCH(SqpRange, referenceRange);
136 CosinusProvider referenceProvider{};
137 auto dataSeries = referenceProvider.provideDataSeries(
138 referenceRange, {{COSINUS_TYPE_KEY, "scalar"}, {COSINUS_FREQUENCY_KEY, 100.}});
139
140 auto end = dataSeries->cend() - 1;
141 qInfo() << dataSeries->nbPoints() << dataSeries->cbegin()->x() << end->x();
142
143 /// Lambda used to validate a variable at each step
144 auto validateVariable
145 = [dataSeries](std::shared_ptr<Variable> variable, const SqpRange &range) {
146 // Checks that the variable's range has changed
147 QCOMPARE(variable->range(), range);
148
149 // Checks the variable's data series
150 QVERIFY(checkDataSeries(variable->dataSeries(), variable->cacheRange(), dataSeries));
151 };
152
153 // Creates variable
154 QFETCH(SqpRange, initialRange);
155 sqpApp->timeController().onTimeToUpdate(initialRange);
156 auto provider = std::make_shared<CosinusProvider>();
157 auto variable = sqpApp->variableController().createVariable(
158 "MMS", {{COSINUS_TYPE_KEY, "scalar"}, {COSINUS_FREQUENCY_KEY, 100.}}, provider);
159
160 QFETCH(int, operationDelay);
161 QTest::qWait(operationDelay);
162 validateVariable(variable, initialRange);
163
164 // Makes operations on the variable
165 QFETCH(std::vector<SqpRange>, operations);
166 for (const auto &operation : operations) {
167 // Asks request on the variable and waits during its execution
168 sqpApp->variableController().onRequestDataLoading({variable}, operation, variable->range(),
169 true);
170
171 QTest::qWait(operationDelay);
172 validateVariable(variable, operation);
178 173 }
179 else {
180 QFAIL("Can't read input data file");
174
175
176 for (const auto &operation : operations) {
177 // Asks request on the variable and waits during its execution
178 sqpApp->variableController().onRequestDataLoading({variable}, operation, variable->range(),
179 true);
181 180 }
181 QTest::qWait(operationDelay);
182 validateVariable(variable, operations.back());
182 183 }
183 184
184 185 int main(int argc, char *argv[])
185 186 {
186 187 SqpApplication app{argc, argv};
187 188 app.setAttribute(Qt::AA_Use96Dpi, true);
188 189 TestCosinusAcquisition testObject{};
189 190 QTEST_SET_MAIN_SOURCE_PATH
190 191 return QTest::qExec(&testObject, argc, argv);
191 192 }
192 193
193 194 #include "TestCosinusAcquisition.moc"
General Comments 3
Under Review
author

Auto status change to "Under Review"

Approved
author

Status change > Approved

You need to be logged in to leave comments. Login now