##// END OF EJS Templates
Fix test to make them sucess
perrinel -
r839:105359887123
parent child
Show More
@@ -1,1012 +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 80 enum class VariableRequestHandlerState { OFF, RUNNING, PENDING };
81 81
82 82 struct VariableRequestHandler {
83 83
84 84 VariableRequestHandler()
85 85 {
86 86 m_CanUpdate = false;
87 87 m_State = VariableRequestHandlerState::OFF;
88 88 }
89 89
90 90 QUuid m_VarId;
91 91 VariableRequest m_RunningVarRequest;
92 92 VariableRequest m_PendingVarRequest;
93 93 VariableRequestHandlerState m_State;
94 94 bool m_CanUpdate;
95 95 };
96 96
97 97 struct VariableController::VariableControllerPrivate {
98 98 explicit VariableControllerPrivate(VariableController *parent)
99 99 : m_WorkingMutex{},
100 100 m_VariableModel{new VariableModel{parent}},
101 101 m_VariableSelectionModel{new QItemSelectionModel{m_VariableModel, parent}},
102 102 // m_VariableCacheStrategy{std::make_unique<VariableCacheStrategy>()},
103 103 m_VariableCacheStrategy{VariableCacheStrategyFactory::createCacheStrategy(
104 104 CacheStrategy::SingleThreshold)},
105 105 m_VariableAcquisitionWorker{std::make_unique<VariableAcquisitionWorker>()},
106 106 q{parent}
107 107 {
108 108
109 109 m_VariableAcquisitionWorker->moveToThread(&m_VariableAcquisitionWorkerThread);
110 110 m_VariableAcquisitionWorkerThread.setObjectName("VariableAcquisitionWorkerThread");
111 111 }
112 112
113 113
114 114 virtual ~VariableControllerPrivate()
115 115 {
116 116 qCDebug(LOG_VariableController()) << tr("VariableControllerPrivate destruction");
117 117 m_VariableAcquisitionWorkerThread.quit();
118 118 m_VariableAcquisitionWorkerThread.wait();
119 119 }
120 120
121 121
122 122 void processRequest(std::shared_ptr<Variable> var, const SqpRange &rangeRequested,
123 123 QUuid varRequestId);
124 124
125 125 std::shared_ptr<Variable> findVariable(QUuid vIdentifier);
126 126 std::shared_ptr<IDataSeries>
127 127 retrieveDataSeries(const QVector<AcquisitionDataPacket> acqDataPacketVector);
128 128
129 129 void registerProvider(std::shared_ptr<IDataProvider> provider);
130 130
131 131 void storeVariableRequest(QUuid varId, QUuid varRequestId, const VariableRequest &varRequest);
132 132 QUuid acceptVariableRequest(QUuid varId, std::shared_ptr<IDataSeries> dataSeries);
133 133 void updateVariables(QUuid varRequestId);
134 134 void updateVariableRequest(QUuid varRequestId);
135 135 void cancelVariableRequest(QUuid varRequestId);
136 136 void executeVarRequest(std::shared_ptr<Variable> var, VariableRequest &varRequest);
137 137
138 138 QMutex m_WorkingMutex;
139 139 /// Variable model. The VariableController has the ownership
140 140 VariableModel *m_VariableModel;
141 141 QItemSelectionModel *m_VariableSelectionModel;
142 142
143 143
144 144 TimeController *m_TimeController{nullptr};
145 145 std::unique_ptr<VariableCacheStrategy> m_VariableCacheStrategy;
146 146 std::unique_ptr<VariableAcquisitionWorker> m_VariableAcquisitionWorker;
147 147 QThread m_VariableAcquisitionWorkerThread;
148 148
149 149 std::unordered_map<std::shared_ptr<Variable>, std::shared_ptr<IDataProvider> >
150 150 m_VariableToProviderMap;
151 151 std::unordered_map<std::shared_ptr<Variable>, QUuid> m_VariableToIdentifierMap;
152 152 std::map<QUuid, std::shared_ptr<VariableSynchronizationGroup> >
153 153 m_GroupIdToVariableSynchronizationGroupMap;
154 154 std::map<QUuid, QUuid> m_VariableIdGroupIdMap;
155 155 std::set<std::shared_ptr<IDataProvider> > m_ProviderSet;
156 156
157 157 std::map<QUuid, std::list<QUuid> > m_VarGroupIdToVarIds;
158 158 std::map<QUuid, std::unique_ptr<VariableRequestHandler> > m_VarIdToVarRequestHandler;
159 159
160 160 VariableController *q;
161 161 };
162 162
163 163
164 164 VariableController::VariableController(QObject *parent)
165 165 : QObject{parent}, impl{spimpl::make_unique_impl<VariableControllerPrivate>(this)}
166 166 {
167 167 qCDebug(LOG_VariableController()) << tr("VariableController construction")
168 168 << QThread::currentThread();
169 169
170 170 connect(impl->m_VariableModel, &VariableModel::abortProgessRequested, this,
171 171 &VariableController::onAbortProgressRequested);
172 172
173 173 connect(impl->m_VariableAcquisitionWorker.get(),
174 174 &VariableAcquisitionWorker::variableCanceledRequested, this,
175 175 &VariableController::onAbortAcquisitionRequested);
176 176
177 177 connect(impl->m_VariableAcquisitionWorker.get(), &VariableAcquisitionWorker::dataProvided, this,
178 178 &VariableController::onDataProvided);
179 179 connect(impl->m_VariableAcquisitionWorker.get(),
180 180 &VariableAcquisitionWorker::variableRequestInProgress, this,
181 181 &VariableController::onVariableRetrieveDataInProgress);
182 182
183 183
184 184 connect(&impl->m_VariableAcquisitionWorkerThread, &QThread::started,
185 185 impl->m_VariableAcquisitionWorker.get(), &VariableAcquisitionWorker::initialize);
186 186 connect(&impl->m_VariableAcquisitionWorkerThread, &QThread::finished,
187 187 impl->m_VariableAcquisitionWorker.get(), &VariableAcquisitionWorker::finalize);
188 188
189 189
190 190 impl->m_VariableAcquisitionWorkerThread.start();
191 191 }
192 192
193 193 VariableController::~VariableController()
194 194 {
195 195 qCDebug(LOG_VariableController()) << tr("VariableController destruction")
196 196 << QThread::currentThread();
197 197 this->waitForFinish();
198 198 }
199 199
200 200 VariableModel *VariableController::variableModel() noexcept
201 201 {
202 202 return impl->m_VariableModel;
203 203 }
204 204
205 205 QItemSelectionModel *VariableController::variableSelectionModel() noexcept
206 206 {
207 207 return impl->m_VariableSelectionModel;
208 208 }
209 209
210 210 void VariableController::setTimeController(TimeController *timeController) noexcept
211 211 {
212 212 impl->m_TimeController = timeController;
213 213 }
214 214
215 215 std::shared_ptr<Variable>
216 216 VariableController::cloneVariable(std::shared_ptr<Variable> variable) noexcept
217 217 {
218 218 if (impl->m_VariableModel->containsVariable(variable)) {
219 219 // Clones variable
220 220 auto duplicate = variable->clone();
221 221
222 222 // Adds clone to model
223 223 impl->m_VariableModel->addVariable(duplicate);
224 224
225 225 // Generates clone identifier
226 226 impl->m_VariableToIdentifierMap[duplicate] = QUuid::createUuid();
227 227
228 228 // Registers provider
229 229 auto variableProvider = impl->m_VariableToProviderMap.at(variable);
230 230 auto duplicateProvider = variableProvider != nullptr ? variableProvider->clone() : nullptr;
231 231
232 232 impl->m_VariableToProviderMap[duplicate] = duplicateProvider;
233 233 if (duplicateProvider) {
234 234 impl->registerProvider(duplicateProvider);
235 235 }
236 236
237 237 return duplicate;
238 238 }
239 239 else {
240 240 qCCritical(LOG_VariableController())
241 241 << tr("Can't create duplicate of variable %1: variable not registered in the model")
242 242 .arg(variable->name());
243 243 return nullptr;
244 244 }
245 245 }
246 246
247 247 void VariableController::deleteVariable(std::shared_ptr<Variable> variable) noexcept
248 248 {
249 249 if (!variable) {
250 250 qCCritical(LOG_VariableController()) << "Can't delete variable: variable is null";
251 251 return;
252 252 }
253 253
254 254 // Spreads in SciQlop that the variable will be deleted, so that potential receivers can
255 255 // make some treatments before the deletion
256 256 emit variableAboutToBeDeleted(variable);
257 257
258 258 // Deletes identifier
259 259 impl->m_VariableToIdentifierMap.erase(variable);
260 260
261 261 // Deletes provider
262 262 auto nbProvidersDeleted = impl->m_VariableToProviderMap.erase(variable);
263 263 qCDebug(LOG_VariableController())
264 264 << tr("Number of providers deleted for variable %1: %2")
265 265 .arg(variable->name(), QString::number(nbProvidersDeleted));
266 266
267 267
268 268 // Deletes from model
269 269 impl->m_VariableModel->deleteVariable(variable);
270 270 }
271 271
272 272 void VariableController::deleteVariables(
273 273 const QVector<std::shared_ptr<Variable> > &variables) noexcept
274 274 {
275 275 for (auto variable : qAsConst(variables)) {
276 276 deleteVariable(variable);
277 277 }
278 278 }
279 279
280 280 std::shared_ptr<Variable>
281 281 VariableController::createVariable(const QString &name, const QVariantHash &metadata,
282 282 std::shared_ptr<IDataProvider> provider) noexcept
283 283 {
284 284 if (!impl->m_TimeController) {
285 285 qCCritical(LOG_VariableController())
286 286 << tr("Impossible to create variable: The time controller is null");
287 287 return nullptr;
288 288 }
289 289
290 290 auto range = impl->m_TimeController->dateTime();
291 291
292 292 if (auto newVariable = impl->m_VariableModel->createVariable(name, metadata)) {
293 293 auto varId = QUuid::createUuid();
294 294
295 295 // Create the handler
296 296 auto varRequestHandler = std::make_unique<VariableRequestHandler>();
297 297 varRequestHandler->m_VarId = varId;
298 298
299 299 impl->m_VarIdToVarRequestHandler.insert(
300 300 std::make_pair(varId, std::move(varRequestHandler)));
301 301
302 302 // store the provider
303 303 impl->registerProvider(provider);
304 304
305 305 // Associate the provider
306 306 impl->m_VariableToProviderMap[newVariable] = provider;
307 307 impl->m_VariableToIdentifierMap[newVariable] = varId;
308 308
309 309 this->onRequestDataLoading(QVector<std::shared_ptr<Variable> >{newVariable}, range, false);
310 310
311 311 // auto varRequestId = QUuid::createUuid();
312 312 // qCInfo(LOG_VariableController()) << "createVariable: " << varId << varRequestId;
313 313 // impl->processRequest(newVariable, range, varRequestId);
314 314 // impl->updateVariableRequest(varRequestId);
315 315
316 316 return newVariable;
317 317 }
318 318 }
319 319
320 320 void VariableController::onDateTimeOnSelection(const SqpRange &dateTime)
321 321 {
322 322 // NOTE: Even if acquisition request is aborting, the graphe range will be changed
323 323 qCDebug(LOG_VariableController()) << "VariableController::onDateTimeOnSelection"
324 324 << QThread::currentThread()->objectName();
325 325 auto selectedRows = impl->m_VariableSelectionModel->selectedRows();
326 326
327 327 // NOTE we only permit the time modification for one variable
328 328 // DEPRECATED
329 329 // auto variables = QVector<std::shared_ptr<Variable> >{};
330 330 // for (const auto &selectedRow : qAsConst(selectedRows)) {
331 331 // if (auto selectedVariable =
332 332 // impl->m_VariableModel->variable(selectedRow.row())) {
333 333 // variables << selectedVariable;
334 334
335 335 // // notify that rescale operation has to be done
336 336 // emit rangeChanged(selectedVariable, dateTime);
337 337 // }
338 338 // }
339 339 // if (!variables.isEmpty()) {
340 340 // this->onRequestDataLoading(variables, dateTime, synchro);
341 341 // }
342 342 if (selectedRows.size() == 1) {
343 343
344 344 if (auto selectedVariable
345 345 = impl->m_VariableModel->variable(qAsConst(selectedRows).first().row())) {
346 346
347 347 auto itVar = impl->m_VariableToIdentifierMap.find(selectedVariable);
348 348 if (itVar == impl->m_VariableToIdentifierMap.cend()) {
349 349 qCCritical(LOG_VariableController())
350 350 << tr("Impossible to onDateTimeOnSelection request for unknown variable");
351 351 return;
352 352 }
353 353
354 354 // notify that rescale operation has to be done
355 355 emit rangeChanged(selectedVariable, dateTime);
356 356
357 357 auto synchro = impl->m_VariableIdGroupIdMap.find(itVar->second)
358 358 != impl->m_VariableIdGroupIdMap.cend();
359 359
360 360 this->onRequestDataLoading(QVector<std::shared_ptr<Variable> >{selectedVariable},
361 361 dateTime, synchro);
362 362 }
363 363 }
364 364 else if (selectedRows.size() > 1) {
365 365 qCCritical(LOG_VariableController())
366 366 << tr("Impossible to set time for more than 1 variable in the same time");
367 367 }
368 368 else {
369 369 qCWarning(LOG_VariableController())
370 370 << tr("There is no variable selected to set the time one");
371 371 }
372 372 }
373 373
374 374 void VariableController::onDataProvided(QUuid vIdentifier, const SqpRange &rangeRequested,
375 375 const SqpRange &cacheRangeRequested,
376 376 QVector<AcquisitionDataPacket> dataAcquired)
377 377 {
378 378 qCDebug(LOG_VariableController()) << tr("onDataProvided") << QThread::currentThread();
379 379 auto retrievedDataSeries = impl->retrieveDataSeries(dataAcquired);
380 380 auto varRequestId = impl->acceptVariableRequest(vIdentifier, retrievedDataSeries);
381 381 if (!varRequestId.isNull()) {
382 382 impl->updateVariables(varRequestId);
383 383 }
384 384 }
385 385
386 386 void VariableController::onVariableRetrieveDataInProgress(QUuid identifier, double progress)
387 387 {
388 388 qCDebug(LOG_VariableController())
389 389 << "TORM: variableController::onVariableRetrieveDataInProgress"
390 390 << QThread::currentThread()->objectName() << progress;
391 391 if (auto var = impl->findVariable(identifier)) {
392 392 impl->m_VariableModel->setDataProgress(var, progress);
393 393 }
394 394 else {
395 395 qCCritical(LOG_VariableController())
396 396 << tr("Impossible to notify progression of a null variable");
397 397 }
398 398 }
399 399
400 400 void VariableController::onAbortProgressRequested(std::shared_ptr<Variable> variable)
401 401 {
402 402 qCDebug(LOG_VariableController()) << "TORM: variableController::onAbortProgressRequested"
403 403 << QThread::currentThread()->objectName() << variable->name();
404 404
405 405 auto itVar = impl->m_VariableToIdentifierMap.find(variable);
406 406 if (itVar == impl->m_VariableToIdentifierMap.cend()) {
407 407 qCCritical(LOG_VariableController())
408 408 << tr("Impossible to onAbortProgressRequested request for unknown variable");
409 409 return;
410 410 }
411 411
412 412 auto varId = itVar->second;
413 413
414 414 auto itVarHandler = impl->m_VarIdToVarRequestHandler.find(varId);
415 415 if (itVarHandler == impl->m_VarIdToVarRequestHandler.cend()) {
416 416 qCCritical(LOG_VariableController())
417 417 << tr("Impossible to onAbortProgressRequested for variable with unknown handler");
418 418 return;
419 419 }
420 420
421 421 auto varHandler = itVarHandler->second.get();
422 422
423 423 // case where a variable has a running request
424 424 if (varHandler->m_State != VariableRequestHandlerState::OFF) {
425 425 impl->cancelVariableRequest(varHandler->m_RunningVarRequest.m_VariableGroupId);
426 426 }
427 427 }
428 428
429 429 void VariableController::onAbortAcquisitionRequested(QUuid vIdentifier)
430 430 {
431 431 qCDebug(LOG_VariableController()) << "TORM: variableController::onAbortAcquisitionRequested"
432 432 << QThread::currentThread()->objectName() << vIdentifier;
433 433
434 434 if (auto var = impl->findVariable(vIdentifier)) {
435 435 this->onAbortProgressRequested(var);
436 436 }
437 437 else {
438 438 qCCritical(LOG_VariableController())
439 439 << tr("Impossible to abort Acquisition Requestof a null variable");
440 440 }
441 441 }
442 442
443 443 void VariableController::onAddSynchronizationGroupId(QUuid synchronizationGroupId)
444 444 {
445 445 qCDebug(LOG_VariableController()) << "TORM: VariableController::onAddSynchronizationGroupId"
446 446 << QThread::currentThread()->objectName()
447 447 << synchronizationGroupId;
448 448 auto vSynchroGroup = std::make_shared<VariableSynchronizationGroup>();
449 449 impl->m_GroupIdToVariableSynchronizationGroupMap.insert(
450 450 std::make_pair(synchronizationGroupId, vSynchroGroup));
451 451 }
452 452
453 453 void VariableController::onRemoveSynchronizationGroupId(QUuid synchronizationGroupId)
454 454 {
455 455 impl->m_GroupIdToVariableSynchronizationGroupMap.erase(synchronizationGroupId);
456 456 }
457 457
458 458 void VariableController::onAddSynchronized(std::shared_ptr<Variable> variable,
459 459 QUuid synchronizationGroupId)
460 460
461 461 {
462 462 qCDebug(LOG_VariableController()) << "TORM: VariableController::onAddSynchronized"
463 463 << synchronizationGroupId;
464 464 auto varToVarIdIt = impl->m_VariableToIdentifierMap.find(variable);
465 465 if (varToVarIdIt != impl->m_VariableToIdentifierMap.cend()) {
466 466 auto groupIdToVSGIt
467 467 = impl->m_GroupIdToVariableSynchronizationGroupMap.find(synchronizationGroupId);
468 468 if (groupIdToVSGIt != impl->m_GroupIdToVariableSynchronizationGroupMap.cend()) {
469 469 impl->m_VariableIdGroupIdMap.insert(
470 470 std::make_pair(varToVarIdIt->second, synchronizationGroupId));
471 471 groupIdToVSGIt->second->addVariableId(varToVarIdIt->second);
472 472 }
473 473 else {
474 474 qCCritical(LOG_VariableController())
475 475 << tr("Impossible to synchronize a variable with an unknown sycnhronization group")
476 476 << variable->name();
477 477 }
478 478 }
479 479 else {
480 480 qCCritical(LOG_VariableController())
481 481 << tr("Impossible to synchronize a variable with no identifier") << variable->name();
482 482 }
483 483 }
484 484
485 485 void VariableController::desynchronize(std::shared_ptr<Variable> variable,
486 486 QUuid synchronizationGroupId)
487 487 {
488 488 // Gets variable id
489 489 auto variableIt = impl->m_VariableToIdentifierMap.find(variable);
490 490 if (variableIt == impl->m_VariableToIdentifierMap.cend()) {
491 491 qCCritical(LOG_VariableController())
492 492 << tr("Can't desynchronize variable %1: variable identifier not found")
493 493 .arg(variable->name());
494 494 return;
495 495 }
496 496
497 497 // Gets synchronization group
498 498 auto groupIt = impl->m_GroupIdToVariableSynchronizationGroupMap.find(synchronizationGroupId);
499 499 if (groupIt == impl->m_GroupIdToVariableSynchronizationGroupMap.cend()) {
500 500 qCCritical(LOG_VariableController())
501 501 << tr("Can't desynchronize variable %1: unknown synchronization group")
502 502 .arg(variable->name());
503 503 return;
504 504 }
505 505
506 506 auto variableId = variableIt->second;
507 507
508 508 // Removes variable from synchronization group
509 509 auto synchronizationGroup = groupIt->second;
510 510 synchronizationGroup->removeVariableId(variableId);
511 511
512 512 // Removes link between variable and synchronization group
513 513 impl->m_VariableIdGroupIdMap.erase(variableId);
514 514 }
515 515
516 516 void VariableController::onRequestDataLoading(QVector<std::shared_ptr<Variable> > variables,
517 517 const SqpRange &range, bool synchronise)
518 518 {
519 519 // variables is assumed synchronized
520 520 // TODO: Asser variables synchronization
521 521 // we want to load data of the variable for the dateTime.
522 522 if (variables.isEmpty()) {
523 523 return;
524 524 }
525 525
526 526 auto varRequestId = QUuid::createUuid();
527 527 qCDebug(LOG_VariableController()) << "VariableController::onRequestDataLoading"
528 528 << QThread::currentThread()->objectName() << varRequestId
529 529 << range << synchronise;
530 530
531 531 if (!synchronise) {
532 532 auto varIds = std::list<QUuid>{};
533 533 for (const auto &var : variables) {
534 534 auto vId = impl->m_VariableToIdentifierMap.at(var);
535 535 varIds.push_back(vId);
536 536 }
537 537 impl->m_VarGroupIdToVarIds.insert(std::make_pair(varRequestId, varIds));
538 538 for (const auto &var : variables) {
539 qCInfo(LOG_VariableController()) << "processRequest for" << var->name() << varRequestId
540 << varIds.size();
539 qCDebug(LOG_VariableController()) << "processRequest for" << var->name() << varRequestId
540 << varIds.size();
541 541 impl->processRequest(var, range, varRequestId);
542 542 }
543 543 }
544 544 else {
545 545 auto vId = impl->m_VariableToIdentifierMap.at(variables.first());
546 546 auto varIdToGroupIdIt = impl->m_VariableIdGroupIdMap.find(vId);
547 547 if (varIdToGroupIdIt != impl->m_VariableIdGroupIdMap.cend()) {
548 548 auto groupId = varIdToGroupIdIt->second;
549 549
550 550 auto vSynchronizationGroup
551 551 = impl->m_GroupIdToVariableSynchronizationGroupMap.at(groupId);
552 552 auto vSyncIds = vSynchronizationGroup->getIds();
553 553
554 554 auto varIds = std::list<QUuid>{};
555 555 for (auto vId : vSyncIds) {
556 556 varIds.push_back(vId);
557 557 }
558 558 impl->m_VarGroupIdToVarIds.insert(std::make_pair(varRequestId, varIds));
559 559
560 560 for (auto vId : vSyncIds) {
561 561 auto var = impl->findVariable(vId);
562 562
563 563 // Don't process already processed var
564 564 if (var != nullptr) {
565 565 qCDebug(LOG_VariableController()) << "processRequest synchro for" << var->name()
566 566 << varRequestId;
567 567 auto vSyncRangeRequested
568 568 = variables.contains(var)
569 569 ? range
570 570 : computeSynchroRangeRequested(var->range(), range,
571 571 variables.first()->range());
572 572 qCDebug(LOG_VariableController()) << "synchro RR" << vSyncRangeRequested;
573 573 impl->processRequest(var, vSyncRangeRequested, varRequestId);
574 574 }
575 575 else {
576 576 qCCritical(LOG_VariableController())
577 577
578 578 << tr("Impossible to synchronize a null variable");
579 579 }
580 580 }
581 581 }
582 582 }
583 583
584 584 impl->updateVariables(varRequestId);
585 585 }
586 586
587 587
588 588 void VariableController::initialize()
589 589 {
590 590 qCDebug(LOG_VariableController()) << tr("VariableController init") << QThread::currentThread();
591 591 impl->m_WorkingMutex.lock();
592 592 qCDebug(LOG_VariableController()) << tr("VariableController init END");
593 593 }
594 594
595 595 void VariableController::finalize()
596 596 {
597 597 impl->m_WorkingMutex.unlock();
598 598 }
599 599
600 600 void VariableController::waitForFinish()
601 601 {
602 602 QMutexLocker locker{&impl->m_WorkingMutex};
603 603 }
604 604
605 605 AcquisitionZoomType VariableController::getZoomType(const SqpRange &range, const SqpRange &oldRange)
606 606 {
607 607 // t1.m_TStart <= t2.m_TStart && t2.m_TEnd <= t1.m_TEnd
608 608 auto zoomType = AcquisitionZoomType::Unknown;
609 609 if (range.m_TStart <= oldRange.m_TStart && oldRange.m_TEnd <= range.m_TEnd) {
610 610 qCDebug(LOG_VariableController()) << "zoomtype: ZoomOut";
611 611 zoomType = AcquisitionZoomType::ZoomOut;
612 612 }
613 613 else if (range.m_TStart > oldRange.m_TStart && range.m_TEnd > oldRange.m_TEnd) {
614 614 qCDebug(LOG_VariableController()) << "zoomtype: PanRight";
615 615 zoomType = AcquisitionZoomType::PanRight;
616 616 }
617 617 else if (range.m_TStart < oldRange.m_TStart && range.m_TEnd < oldRange.m_TEnd) {
618 618 qCDebug(LOG_VariableController()) << "zoomtype: PanLeft";
619 619 zoomType = AcquisitionZoomType::PanLeft;
620 620 }
621 621 else if (range.m_TStart > oldRange.m_TStart && oldRange.m_TEnd > range.m_TEnd) {
622 622 qCDebug(LOG_VariableController()) << "zoomtype: ZoomIn";
623 623 zoomType = AcquisitionZoomType::ZoomIn;
624 624 }
625 625 else {
626 626 qCDebug(LOG_VariableController()) << "getZoomType: Unknown type detected";
627 627 }
628 628 return zoomType;
629 629 }
630 630
631 631 void VariableController::VariableControllerPrivate::processRequest(std::shared_ptr<Variable> var,
632 632 const SqpRange &rangeRequested,
633 633 QUuid varRequestId)
634 634 {
635 635 auto itVar = m_VariableToIdentifierMap.find(var);
636 636 if (itVar == m_VariableToIdentifierMap.cend()) {
637 637 qCCritical(LOG_VariableController())
638 638 << tr("Impossible to process request for unknown variable");
639 639 return;
640 640 }
641 641
642 642 auto varId = itVar->second;
643 643
644 644 auto itVarHandler = m_VarIdToVarRequestHandler.find(varId);
645 645 if (itVarHandler == m_VarIdToVarRequestHandler.cend()) {
646 646 qCCritical(LOG_VariableController())
647 647 << tr("Impossible to process request for variable with unknown handler");
648 648 return;
649 649 }
650 650
651 651 auto oldRange = var->range();
652 652
653 653 auto varHandler = itVarHandler->second.get();
654 654
655 655 if (varHandler->m_State != VariableRequestHandlerState::OFF) {
656 656 oldRange = varHandler->m_RunningVarRequest.m_RangeRequested;
657 657 }
658 658
659 659 auto varRequest = VariableRequest{};
660 660 varRequest.m_VariableGroupId = varRequestId;
661 661 auto varStrategyRangesRequested
662 662 = m_VariableCacheStrategy->computeRange(oldRange, rangeRequested);
663 663 varRequest.m_RangeRequested = varStrategyRangesRequested.first;
664 664 varRequest.m_CacheRangeRequested = varStrategyRangesRequested.second;
665 665
666 666 switch (varHandler->m_State) {
667 667 case VariableRequestHandlerState::OFF: {
668 668 qCDebug(LOG_VariableController()) << tr("Process Request OFF")
669 669 << varRequest.m_RangeRequested
670 670 << varRequest.m_CacheRangeRequested;
671 671 varHandler->m_RunningVarRequest = varRequest;
672 672 varHandler->m_State = VariableRequestHandlerState::RUNNING;
673 673 executeVarRequest(var, varRequest);
674 674 break;
675 675 }
676 676 case VariableRequestHandlerState::RUNNING: {
677 677 qCDebug(LOG_VariableController()) << tr("Process Request RUNNING")
678 678 << varRequest.m_RangeRequested
679 679 << varRequest.m_CacheRangeRequested;
680 680 varHandler->m_State = VariableRequestHandlerState::PENDING;
681 681 varHandler->m_PendingVarRequest = varRequest;
682 682 break;
683 683 }
684 684 case VariableRequestHandlerState::PENDING: {
685 685 qCDebug(LOG_VariableController()) << tr("Process Request PENDING")
686 686 << varRequest.m_RangeRequested
687 687 << varRequest.m_CacheRangeRequested;
688 688 auto variableGroupIdToCancel = varHandler->m_PendingVarRequest.m_VariableGroupId;
689 689 cancelVariableRequest(variableGroupIdToCancel);
690 690 // Cancel variable can make state downgrade
691 691 varHandler->m_State = VariableRequestHandlerState::PENDING;
692 692 varHandler->m_PendingVarRequest = varRequest;
693 693
694 694 break;
695 695 }
696 696 default:
697 697 qCCritical(LOG_VariableController())
698 698 << QObject::tr("Unknown VariableRequestHandlerState");
699 699 }
700 700 }
701 701
702 702 std::shared_ptr<Variable>
703 703 VariableController::VariableControllerPrivate::findVariable(QUuid vIdentifier)
704 704 {
705 705 std::shared_ptr<Variable> var;
706 706 auto findReply = [vIdentifier](const auto &entry) { return vIdentifier == entry.second; };
707 707
708 708 auto end = m_VariableToIdentifierMap.cend();
709 709 auto it = std::find_if(m_VariableToIdentifierMap.cbegin(), end, findReply);
710 710 if (it != end) {
711 711 var = it->first;
712 712 }
713 713 else {
714 714 qCCritical(LOG_VariableController())
715 715 << tr("Impossible to find the variable with the identifier: ") << vIdentifier;
716 716 }
717 717
718 718 return var;
719 719 }
720 720
721 721 std::shared_ptr<IDataSeries> VariableController::VariableControllerPrivate::retrieveDataSeries(
722 722 const QVector<AcquisitionDataPacket> acqDataPacketVector)
723 723 {
724 724 qCDebug(LOG_VariableController()) << tr("TORM: retrieveDataSeries acqDataPacketVector size")
725 725 << acqDataPacketVector.size();
726 726 std::shared_ptr<IDataSeries> dataSeries;
727 727 if (!acqDataPacketVector.isEmpty()) {
728 728 dataSeries = acqDataPacketVector[0].m_DateSeries;
729 729 for (int i = 1; i < acqDataPacketVector.size(); ++i) {
730 730 dataSeries->merge(acqDataPacketVector[i].m_DateSeries.get());
731 731 }
732 732 }
733 733 qCDebug(LOG_VariableController()) << tr("TORM: retrieveDataSeries acqDataPacketVector size END")
734 734 << acqDataPacketVector.size();
735 735 return dataSeries;
736 736 }
737 737
738 738 void VariableController::VariableControllerPrivate::registerProvider(
739 739 std::shared_ptr<IDataProvider> provider)
740 740 {
741 741 if (m_ProviderSet.find(provider) == m_ProviderSet.end()) {
742 742 qCDebug(LOG_VariableController()) << tr("Registering of a new provider")
743 743 << provider->objectName();
744 744 m_ProviderSet.insert(provider);
745 745 connect(provider.get(), &IDataProvider::dataProvided, m_VariableAcquisitionWorker.get(),
746 746 &VariableAcquisitionWorker::onVariableDataAcquired);
747 747 connect(provider.get(), &IDataProvider::dataProvidedProgress,
748 748 m_VariableAcquisitionWorker.get(),
749 749 &VariableAcquisitionWorker::onVariableRetrieveDataInProgress);
750 750 connect(provider.get(), &IDataProvider::dataProvidedFailed,
751 751 m_VariableAcquisitionWorker.get(),
752 752 &VariableAcquisitionWorker::onVariableAcquisitionFailed);
753 753 }
754 754 else {
755 755 qCDebug(LOG_VariableController()) << tr("Cannot register provider, it already exists ");
756 756 }
757 757 }
758 758
759 759 QUuid VariableController::VariableControllerPrivate::acceptVariableRequest(
760 760 QUuid varId, std::shared_ptr<IDataSeries> dataSeries)
761 761 {
762 762 auto itVarHandler = m_VarIdToVarRequestHandler.find(varId);
763 763 if (itVarHandler == m_VarIdToVarRequestHandler.cend()) {
764 764 return QUuid();
765 765 }
766 766
767 767 auto varHandler = itVarHandler->second.get();
768 768 if (varHandler->m_State == VariableRequestHandlerState::OFF) {
769 769 qCCritical(LOG_VariableController())
770 770 << tr("acceptVariableRequest impossible on a variable with OFF state");
771 771 }
772 772
773 773 varHandler->m_RunningVarRequest.m_DataSeries = dataSeries;
774 774 varHandler->m_CanUpdate = true;
775 775
776 776 // Element traitΓ©, on a dΓ©jΓ  toutes les donnΓ©es necessaires
777 777 auto varGroupId = varHandler->m_RunningVarRequest.m_VariableGroupId;
778 778 qCDebug(LOG_VariableController()) << "Variable::acceptVariableRequest" << varGroupId
779 779 << m_VarGroupIdToVarIds.size();
780 780
781 781 return varHandler->m_RunningVarRequest.m_VariableGroupId;
782 782 }
783 783
784 784 void VariableController::VariableControllerPrivate::updateVariables(QUuid varRequestId)
785 785 {
786 786 qCDebug(LOG_VariableController()) << "VariableControllerPrivate::updateVariables"
787 787 << QThread::currentThread()->objectName() << varRequestId;
788 788
789 789 auto varGroupIdToVarIdsIt = m_VarGroupIdToVarIds.find(varRequestId);
790 790 if (varGroupIdToVarIdsIt == m_VarGroupIdToVarIds.end()) {
791 791 qCWarning(LOG_VariableController())
792 792 << tr("Impossible to updateVariables of unknown variables") << varRequestId;
793 793 return;
794 794 }
795 795
796 796 auto &varIds = varGroupIdToVarIdsIt->second;
797 797 auto varIdsEnd = varIds.end();
798 798 bool processVariableUpdate = true;
799 799 qCDebug(LOG_VariableController()) << "VariableControllerPrivate::updateVariables"
800 800 << varRequestId << varIds.size();
801 801 for (auto varIdsIt = varIds.begin(); (varIdsIt != varIdsEnd) && processVariableUpdate;
802 802 ++varIdsIt) {
803 803 auto itVarHandler = m_VarIdToVarRequestHandler.find(*varIdsIt);
804 804 if (itVarHandler != m_VarIdToVarRequestHandler.cend()) {
805 805 processVariableUpdate &= itVarHandler->second->m_CanUpdate;
806 806 }
807 807 }
808 808
809 809 if (processVariableUpdate) {
810 810 qCDebug(LOG_VariableController()) << "Final update OK for the var request" << varIds.size();
811 811 for (auto varIdsIt = varIds.begin(); varIdsIt != varIdsEnd; ++varIdsIt) {
812 812 auto itVarHandler = m_VarIdToVarRequestHandler.find(*varIdsIt);
813 813 if (itVarHandler != m_VarIdToVarRequestHandler.cend()) {
814 814 if (auto var = findVariable(*varIdsIt)) {
815 815 auto &varRequest = itVarHandler->second->m_RunningVarRequest;
816 816 var->setRange(varRequest.m_RangeRequested);
817 817 var->setCacheRange(varRequest.m_CacheRangeRequested);
818 818 qCDebug(LOG_VariableController()) << tr("1: onDataProvided")
819 819 << varRequest.m_RangeRequested
820 820 << varRequest.m_CacheRangeRequested;
821 821 qCDebug(LOG_VariableController()) << tr("2: onDataProvided var points before")
822 822 << var->nbPoints()
823 823 << varRequest.m_DataSeries->nbPoints();
824 824 var->mergeDataSeries(varRequest.m_DataSeries);
825 825 qCDebug(LOG_VariableController()) << tr("3: onDataProvided var points after")
826 826 << var->nbPoints();
827 827
828 828 emit var->updated();
829 829 qCDebug(LOG_VariableController()) << tr("Update OK");
830 830 }
831 831 else {
832 832 qCCritical(LOG_VariableController())
833 833 << tr("Impossible to update data to a null variable");
834 834 }
835 835 }
836 836 }
837 837 updateVariableRequest(varRequestId);
838 838
839 839 // cleaning varRequestId
840 840 qCDebug(LOG_VariableController()) << tr("m_VarGroupIdToVarIds erase") << varRequestId;
841 841 m_VarGroupIdToVarIds.erase(varRequestId);
842 842 }
843 843 }
844 844
845 845
846 846 void VariableController::VariableControllerPrivate::updateVariableRequest(QUuid varRequestId)
847 847 {
848 848 auto varGroupIdToVarIdsIt = m_VarGroupIdToVarIds.find(varRequestId);
849 849 if (varGroupIdToVarIdsIt == m_VarGroupIdToVarIds.end()) {
850 850 qCCritical(LOG_VariableController()) << QObject::tr(
851 851 "Impossible to updateVariableRequest since varGroupdId isn't here anymore");
852 852
853 853 return;
854 854 }
855 855
856 856 auto &varIds = varGroupIdToVarIdsIt->second;
857 857 auto varIdsEnd = varIds.end();
858 858 for (auto varIdsIt = varIds.begin(); (varIdsIt != varIdsEnd); ++varIdsIt) {
859 859 auto itVarHandler = m_VarIdToVarRequestHandler.find(*varIdsIt);
860 860 if (itVarHandler != m_VarIdToVarRequestHandler.cend()) {
861 861
862 862 auto varHandler = itVarHandler->second.get();
863 863 varHandler->m_CanUpdate = false;
864 864
865 865
866 866 switch (varHandler->m_State) {
867 867 case VariableRequestHandlerState::OFF: {
868 868 qCCritical(LOG_VariableController())
869 869 << QObject::tr("Impossible to update a variable with handler in OFF state");
870 870 } break;
871 871 case VariableRequestHandlerState::RUNNING: {
872 872 varHandler->m_State = VariableRequestHandlerState::OFF;
873 873 varHandler->m_RunningVarRequest = VariableRequest{};
874 874 break;
875 875 }
876 876 case VariableRequestHandlerState::PENDING: {
877 877 varHandler->m_State = VariableRequestHandlerState::RUNNING;
878 878 varHandler->m_RunningVarRequest = varHandler->m_PendingVarRequest;
879 879 varHandler->m_PendingVarRequest = VariableRequest{};
880 880 auto var = findVariable(itVarHandler->first);
881 881 executeVarRequest(var, varHandler->m_RunningVarRequest);
882 882 break;
883 883 }
884 884 default:
885 885 qCCritical(LOG_VariableController())
886 886 << QObject::tr("Unknown VariableRequestHandlerState");
887 887 }
888 888 }
889 889 }
890 890 }
891 891
892 892
893 893 void VariableController::VariableControllerPrivate::cancelVariableRequest(QUuid varRequestId)
894 894 {
895 895 qCDebug(LOG_VariableController()) << tr("cancelVariableRequest") << varRequestId;
896 896
897 897 auto varGroupIdToVarIdsIt = m_VarGroupIdToVarIds.find(varRequestId);
898 898 if (varGroupIdToVarIdsIt == m_VarGroupIdToVarIds.end()) {
899 899 qCCritical(LOG_VariableController())
900 900 << tr("Impossible to cancelVariableRequest for unknown varGroupdId") << varRequestId;
901 901 return;
902 902 }
903 903
904 904 auto &varIds = varGroupIdToVarIdsIt->second;
905 905 auto varIdsEnd = varIds.end();
906 906 for (auto varIdsIt = varIds.begin(); (varIdsIt != varIdsEnd); ++varIdsIt) {
907 907 auto itVarHandler = m_VarIdToVarRequestHandler.find(*varIdsIt);
908 908 if (itVarHandler != m_VarIdToVarRequestHandler.cend()) {
909 909
910 910 auto varHandler = itVarHandler->second.get();
911 911 varHandler->m_VarId = QUuid{};
912 912 switch (varHandler->m_State) {
913 913 case VariableRequestHandlerState::OFF: {
914 914 qCWarning(LOG_VariableController())
915 915 << QObject::tr("Impossible to cancel a variable with no running request");
916 916 break;
917 917 }
918 918 case VariableRequestHandlerState::RUNNING: {
919 919
920 920 if (varHandler->m_RunningVarRequest.m_VariableGroupId == varRequestId) {
921 921 auto var = findVariable(itVarHandler->first);
922 922 auto varProvider = m_VariableToProviderMap.at(var);
923 923 if (varProvider != nullptr) {
924 924 m_VariableAcquisitionWorker->abortProgressRequested(
925 925 itVarHandler->first);
926 926 }
927 927 m_VariableModel->setDataProgress(var, 0.0);
928 928 varHandler->m_CanUpdate = false;
929 929 varHandler->m_State = VariableRequestHandlerState::OFF;
930 930 varHandler->m_RunningVarRequest = VariableRequest{};
931 931 }
932 932 else {
933 933 // TODO: log Impossible to cancel the running variable request beacause its
934 934 // varRequestId isn't not the canceled one
935 935 }
936 936 break;
937 937 }
938 938 case VariableRequestHandlerState::PENDING: {
939 939 if (varHandler->m_RunningVarRequest.m_VariableGroupId == varRequestId) {
940 940 auto var = findVariable(itVarHandler->first);
941 941 auto varProvider = m_VariableToProviderMap.at(var);
942 942 if (varProvider != nullptr) {
943 943 m_VariableAcquisitionWorker->abortProgressRequested(
944 944 itVarHandler->first);
945 945 }
946 946 m_VariableModel->setDataProgress(var, 0.0);
947 947 varHandler->m_CanUpdate = false;
948 948 varHandler->m_State = VariableRequestHandlerState::RUNNING;
949 949 varHandler->m_RunningVarRequest = varHandler->m_PendingVarRequest;
950 950 varHandler->m_PendingVarRequest = VariableRequest{};
951 951 executeVarRequest(var, varHandler->m_RunningVarRequest);
952 952 }
953 953 else if (varHandler->m_PendingVarRequest.m_VariableGroupId == varRequestId) {
954 954 varHandler->m_State = VariableRequestHandlerState::RUNNING;
955 955 varHandler->m_PendingVarRequest = VariableRequest{};
956 956 }
957 957 else {
958 958 // TODO: log Impossible to cancel the variable request beacause its
959 959 // varRequestId isn't not the canceled one
960 960 }
961 961 break;
962 962 }
963 963 default:
964 964 qCCritical(LOG_VariableController())
965 965 << QObject::tr("Unknown VariableRequestHandlerState");
966 966 }
967 967 }
968 968 }
969 969 qCDebug(LOG_VariableController()) << tr("cancelVariableRequest: erase") << varRequestId;
970 970 m_VarGroupIdToVarIds.erase(varRequestId);
971 971 }
972 972
973 973 void VariableController::VariableControllerPrivate::executeVarRequest(std::shared_ptr<Variable> var,
974 974 VariableRequest &varRequest)
975 975 {
976 976 qCDebug(LOG_VariableController()) << tr("TORM: executeVarRequest");
977 977
978 978 auto varId = m_VariableToIdentifierMap.at(var);
979 979
980 980 auto varCacheRange = var->cacheRange();
981 981 auto varCacheRangeRequested = varRequest.m_CacheRangeRequested;
982 982 auto notInCacheRangeList
983 983 = Variable::provideNotInCacheRangeList(varCacheRange, varCacheRangeRequested);
984 984 auto inCacheRangeList
985 985 = Variable::provideInCacheRangeList(varCacheRange, varCacheRangeRequested);
986 986
987 987 if (!notInCacheRangeList.empty()) {
988 988
989 989 auto varProvider = m_VariableToProviderMap.at(var);
990 990 if (varProvider != nullptr) {
991 991 qCDebug(LOG_VariableController()) << "executeVarRequest " << varRequest.m_RangeRequested
992 992 << varRequest.m_CacheRangeRequested;
993 993 m_VariableAcquisitionWorker->pushVariableRequest(
994 994 varRequest.m_VariableGroupId, varId, varRequest.m_RangeRequested,
995 995 varRequest.m_CacheRangeRequested,
996 996 DataProviderParameters{std::move(notInCacheRangeList), var->metadata()},
997 997 varProvider);
998 998 }
999 999 else {
1000 1000 qCCritical(LOG_VariableController())
1001 1001 << "Impossible to provide data with a null provider";
1002 1002 }
1003 1003
1004 1004 if (!inCacheRangeList.empty()) {
1005 1005 emit q->updateVarDisplaying(var, inCacheRangeList.first());
1006 1006 }
1007 1007 }
1008 1008 else {
1009 1009 acceptVariableRequest(varId,
1010 1010 var->dataSeries()->subDataSeries(varRequest.m_CacheRangeRequested));
1011 1011 }
1012 1012 }
@@ -1,637 +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 40 void validateRanges(VariableController &variableController,
41 41 const std::map<int, SqpRange> &expectedRanges)
42 42 {
43 43 for (const auto &expectedRangeEntry : expectedRanges) {
44 44 auto variableIndex = expectedRangeEntry.first;
45 45 auto expectedRange = expectedRangeEntry.second;
46 46
47 47 // Gets the variable in the controller
48 48 auto variable = variableController.variableModel()->variable(variableIndex);
49 49
50 50 // Compares variable's range to the expected range
51 51 QVERIFY(variable != nullptr);
52 52 auto range = variable->range();
53 53 qInfo() << "range vs expected range" << range << expectedRange;
54 54 QCOMPARE(range, expectedRange);
55 55
56 56 // Compares variable's data with values expected for its range
57 57 auto dataSeries = variable->dataSeries();
58 58 QVERIFY(dataSeries != nullptr);
59 59
60 60 auto it = dataSeries->xAxisRange(range.m_TStart, range.m_TEnd);
61 61 auto expectedValues = values(range);
62 62 qInfo() << std::distance(it.first, it.second) << expectedValues.size();
63 63 QVERIFY(std::equal(it.first, it.second, expectedValues.cbegin(), expectedValues.cend(),
64 64 [](const auto &dataSeriesIt, const auto &expectedValue) {
65 65 return dataSeriesIt.value() == expectedValue;
66 66 }));
67 67 }
68 68 }
69 69
70 70 /// Provider used for the tests
71 71 class TestProvider : public IDataProvider {
72 72 std::shared_ptr<IDataProvider> clone() const { return std::make_shared<TestProvider>(); }
73 73
74 74 void requestDataLoading(QUuid acqIdentifier, const DataProviderParameters &parameters) override
75 75 {
76 76 const auto &ranges = parameters.m_Times;
77 77
78 78 for (const auto &range : ranges) {
79 79 // Generates data series
80 80 auto valuesData = values(range);
81 81
82 82 std::vector<double> xAxisData{};
83 83 for (auto i = range.m_TStart; i <= range.m_TEnd; ++i) {
84 84 xAxisData.push_back(i);
85 85 }
86 86
87 87 auto dataSeries = std::make_shared<ScalarSeries>(
88 88 std::move(xAxisData), std::move(valuesData), Unit{"t", true}, Unit{});
89 89
90 90 emit dataProvided(acqIdentifier, dataSeries, range);
91 91 }
92 92 }
93 93
94 94 void requestDataAborting(QUuid acqIdentifier) override
95 95 {
96 96 // Does nothing
97 97 }
98 98 };
99 99
100 100 /**
101 101 * Interface representing an operation performed on a variable controller.
102 102 * This interface is used in tests to apply a set of operations and check the status of the
103 103 * controller after each operation
104 104 */
105 105 struct IOperation {
106 106 virtual ~IOperation() = default;
107 107 /// Executes the operation on the variable controller
108 108 virtual void exec(VariableController &variableController) const = 0;
109 109 };
110 110
111 111 /**
112 112 *Variable creation operation in the controller
113 113 */
114 114 struct Create : public IOperation {
115 115 explicit Create(int index) : m_Index{index} {}
116 116
117 117 void exec(VariableController &variableController) const override
118 118 {
119 119 auto variable = variableController.createVariable(QString::number(m_Index), {},
120 120 std::make_unique<TestProvider>());
121 121 }
122 122
123 123 int m_Index; ///< The index of the variable to create in the controller
124 124 };
125 125
126 126 /**
127 127 * Variable move/shift operation in the controller
128 128 */
129 129 struct Move : public IOperation {
130 130 explicit Move(int index, const SqpRange &newRange, bool shift = false, int delayMS = 10)
131 131 : m_Index{index}, m_NewRange{newRange}, m_Shift{shift}, m_DelayMs{delayMS}
132 132 {
133 133 }
134 134
135 135 void exec(VariableController &variableController) const override
136 136 {
137 137 if (auto variable = variableController.variableModel()->variable(m_Index)) {
138 138 variableController.onRequestDataLoading({variable}, m_NewRange, !m_Shift);
139 139 QTest::qWait(m_DelayMs);
140 140 }
141 141 }
142 142
143 143 int m_Index; ///< The index of the variable to move
144 144 SqpRange m_NewRange; ///< The new range of the variable
145 145 bool m_Shift; ///< Performs a shift (
146 146 int m_DelayMs; ///< wait the delay after running the request (
147 147 };
148 148
149 149 /**
150 150 * Variable synchronization/desynchronization operation in the controller
151 151 */
152 152 struct Synchronize : public IOperation {
153 153 explicit Synchronize(int index, QUuid syncId, bool synchronize = true)
154 154 : m_Index{index}, m_SyncId{syncId}, m_Synchronize{synchronize}
155 155 {
156 156 }
157 157
158 158 void exec(VariableController &variableController) const override
159 159 {
160 160 if (auto variable = variableController.variableModel()->variable(m_Index)) {
161 161 if (m_Synchronize) {
162 162 variableController.onAddSynchronized(variable, m_SyncId);
163 163 }
164 164 else {
165 165 variableController.desynchronize(variable, m_SyncId);
166 166 }
167 167 }
168 168 }
169 169
170 170 int m_Index; ///< The index of the variable to sync/desync
171 171 QUuid m_SyncId; ///< The synchronization group of the variable
172 172 bool m_Synchronize; ///< Performs sync or desync operation
173 173 };
174 174
175 175 /**
176 176 * Test Iteration
177 177 *
178 178 * A test iteration includes an operation to be performed, and a set of expected ranges after each
179 179 * operation. Each range is tested after the operation to ensure that:
180 180 * - the range of the variable is the expected range
181 181 * - the data of the variable are those generated for the expected range
182 182 */
183 183 struct Iteration {
184 184 std::shared_ptr<IOperation> m_Operation; ///< Operation to perform
185 185 std::map<int, SqpRange> m_ExpectedRanges; ///< Expected ranges (by variable index)
186 186 };
187 187
188 188 using Iterations = std::vector<Iteration>;
189 189
190 190 } // namespace
191 191
192 192 Q_DECLARE_METATYPE(Iterations)
193 193
194 194 class TestVariableSync : public QObject {
195 195 Q_OBJECT
196 196
197 197 private slots:
198 198 /// Input data for @sa testSync()
199 199 void testSync_data();
200 200
201 /// Input data for @sa testSyncWithAborting()
202 void testSyncWithAborting_data();
203
204 201 /// Input data for @sa testSyncOneVar()
205 202 void testSyncOneVar_data();
206 203
207 /// Tests synchronization between variables through several operations with automatic aborting
208 void testSyncWithAborting();
209
210 204 /// Tests synchronization between variables through several operations
211 205 void testSync();
212 206
213 207 /// Tests synchronization between variables through several operations
214 208 void testSyncOneVar();
215 209 };
216 210
217 211 namespace {
218 212
219 213 void testSyncCase1()
220 214 {
221 215 // Id used to synchronize variables in the controller
222 216 auto syncId = QUuid::createUuid();
223 217
224 218 /// Generates a range according to a start time and a end time (the date is the same)
225 219 auto range = [](const QTime &startTime, const QTime &endTime) {
226 220 return SqpRange{DateUtils::secondsSinceEpoch(QDateTime{{2017, 1, 1}, startTime, Qt::UTC}),
227 221 DateUtils::secondsSinceEpoch(QDateTime{{2017, 1, 1}, endTime, Qt::UTC})};
228 222 };
229 223
230 224 auto initialRange = range({12, 0}, {13, 0});
231 225
232 226 Iterations iterations{};
233 227 // Creates variables var0, var1 and var2
234 228 iterations.push_back({std::make_shared<Create>(0), {{0, initialRange}}});
235 229 iterations.push_back({std::make_shared<Create>(1), {{0, initialRange}, {1, initialRange}}});
236 230 iterations.push_back(
237 231 {std::make_shared<Create>(2), {{0, initialRange}, {1, initialRange}, {2, initialRange}}});
238 232
239 233 // Adds variables into the sync group (ranges don't need to be tested here)
240 234 iterations.push_back({std::make_shared<Synchronize>(0, syncId)});
241 235 iterations.push_back({std::make_shared<Synchronize>(1, syncId)});
242 236 iterations.push_back({std::make_shared<Synchronize>(2, syncId)});
243 237
244 238 // Moves var0: ranges of var0, var1 and var2 change
245 239 auto newRange = range({12, 30}, {13, 30});
246 240 iterations.push_back(
247 241 {std::make_shared<Move>(0, newRange), {{0, newRange}, {1, newRange}, {2, newRange}}});
248 242
249 243 // Moves var1: ranges of var0, var1 and var2 change
250 244 newRange = range({13, 0}, {14, 0});
251 245 iterations.push_back(
252 246 {std::make_shared<Move>(0, newRange), {{0, newRange}, {1, newRange}, {2, newRange}}});
253 247
254 248 // Moves var2: ranges of var0, var1 and var2 change
255 249 newRange = range({13, 30}, {14, 30});
256 250 iterations.push_back(
257 251 {std::make_shared<Move>(0, newRange), {{0, newRange}, {1, newRange}, {2, newRange}}});
258 252
259 253 // Desyncs var2 and moves var0:
260 254 // - ranges of var0 and var1 change
261 255 // - range of var2 doesn't change anymore
262 256 auto var2Range = newRange;
263 257 newRange = range({13, 45}, {14, 45});
264 258 iterations.push_back({std::make_shared<Synchronize>(2, syncId, false)});
265 259 iterations.push_back(
266 260 {std::make_shared<Move>(0, newRange), {{0, newRange}, {1, newRange}, {2, var2Range}}});
267 261
268 262 // Shifts var0: although var1 is synchronized with var0, its range doesn't change
269 263 auto var1Range = newRange;
270 264 newRange = range({14, 45}, {15, 45});
271 265 iterations.push_back({std::make_shared<Move>(0, newRange, true),
272 266 {{0, newRange}, {1, var1Range}, {2, var2Range}}});
273 267
274 268 // Moves var0 through several operations:
275 269 // - range of var0 changes
276 270 // - range or var1 changes according to the previous shift (one hour)
277 271 auto moveVar0 = [&iterations](const auto &var0NewRange, const auto &var1ExpectedRange) {
278 272 iterations.push_back(
279 273 {std::make_shared<Move>(0, var0NewRange), {{0, var0NewRange}, {1, var1ExpectedRange}}});
280 274 };
281 275
282 276 // Pan left
283 277 moveVar0(range({14, 30}, {15, 30}), range({13, 30}, {14, 30}));
284 278 // Pan right
285 279 moveVar0(range({16, 0}, {17, 0}), range({15, 0}, {16, 0}));
286 280 // Zoom in
287 281 moveVar0(range({16, 30}, {16, 45}), range({15, 30}, {15, 45}));
288 282 // Zoom out
289 283 moveVar0(range({16, 15}, {17, 0}), range({15, 15}, {16, 0}));
290 284
291 285 QTest::newRow("sync1") << syncId << initialRange << std::move(iterations) << 200;
292 286 }
293 287
294 void testSyncCase1WithAborting()
295 {
296 // Id used to synchronize variables in the controller
297 auto syncId = QUuid::createUuid();
298
299 /// Generates a range according to a start time and a end time (the date is the same)
300 auto range = [](const QTime &startTime, const QTime &endTime) {
301 return SqpRange{DateUtils::secondsSinceEpoch(QDateTime{{2017, 1, 1}, startTime, Qt::UTC}),
302 DateUtils::secondsSinceEpoch(QDateTime{{2017, 1, 1}, endTime, Qt::UTC})};
303 };
304
305 auto initialRange = range({12, 0}, {13, 0});
306
307 Iterations creations{};
308 // Creates variables var0, var1 and var2
309 creations.push_back({std::make_shared<Create>(0), {{0, initialRange}}});
310 creations.push_back({std::make_shared<Create>(1), {{0, initialRange}, {1, initialRange}}});
311
312 // Adds variables into the sync group (ranges don't need to be tested here)
313 Iterations iterations{};
314 iterations.push_back({std::make_shared<Synchronize>(0, syncId)});
315 iterations.push_back({std::make_shared<Synchronize>(1, syncId)});
316
317 // Moves var0: ranges of var0, var1
318 auto currentRange = range({12, 30}, {13, 30});
319 iterations.push_back(
320 {std::make_shared<Move>(0, currentRange), {{0, currentRange}, {1, currentRange}}});
321
322 // Moves var0: ranges of var0, var1
323 auto pendingRange = range({13, 0}, {14, 0});
324 iterations.push_back(
325 {std::make_shared<Move>(0, pendingRange), {{0, pendingRange}, {1, pendingRange}}});
326
327 // Moves var0: ranges of var0, var1
328 pendingRange = range({13, 30}, {14, 30});
329 iterations.push_back(
330 {std::make_shared<Move>(0, pendingRange), {{0, pendingRange}, {1, pendingRange}}});
331
332 // moves var0:
333 // - ranges of var0 and var1 change
334 auto var2Range = pendingRange;
335 pendingRange = range({13, 45}, {14, 45});
336 iterations.push_back(
337 {std::make_shared<Move>(0, pendingRange), {{0, pendingRange}, {1, pendingRange}}});
338
339 // Shifts var0: although var1 is synchronized with var0, its range doesn't change
340 auto var1Range = pendingRange;
341 pendingRange = range({14, 45}, {15, 45});
342 iterations.push_back(
343 {std::make_shared<Move>(0, pendingRange, false), {{0, pendingRange}, {1, pendingRange}}});
344
345 // Moves var0 through several operations:
346 // - range of var0 changes
347 // - range or var1 changes according to the previous shift (one hour)
348 auto moveVar0 = [&iterations](const auto &var0NewRange, const auto &var1ExpectedRange) {
349 iterations.push_back(
350 {std::make_shared<Move>(0, var0NewRange), {{0, var0NewRange}, {1, var1ExpectedRange}}});
351 };
352
353 // Pan left
354 moveVar0(range({14, 30}, {15, 30}), range({14, 30}, {15, 30}));
355 // Pan right
356 moveVar0(range({16, 0}, {17, 0}), range({16, 0}, {17, 0}));
357 // Zoom in
358 moveVar0(range({16, 30}, {16, 45}), range({16, 30}, {16, 45}));
359 // Zoom out
360 moveVar0(range({16, 15}, {17, 0}), range({16, 15}, {17, 0}));
361
362 QTest::newRow("syncWithAborting1") << syncId << currentRange << std::move(creations)
363 << std::move(iterations) << 200;
364 }
365
366 288 void testSyncCase2()
367 289 {
368 290 // Id used to synchronize variables in the controller
369 291 auto syncId = QUuid::createUuid();
370 292
371 293 /// Generates a range according to a start time and a end time (the date is the same)
372 294 auto dateTime = [](int year, int month, int day, int hours, int minutes, int seconds) {
373 295 return DateUtils::secondsSinceEpoch(
374 296 QDateTime{{year, month, day}, QTime{hours, minutes, seconds}, Qt::UTC});
375 297 };
376 298
377 299 auto initialRange = SqpRange{dateTime(2017, 1, 1, 12, 0, 0), dateTime(2017, 1, 1, 13, 0, 0)};
378 300
379 301 Iterations iterations{};
380 302 // Creates variables var0 and var1
381 303 iterations.push_back({std::make_shared<Create>(0), {{0, initialRange}}});
382 304 iterations.push_back({std::make_shared<Create>(1), {{0, initialRange}, {1, initialRange}}});
383 305
384 306 // Adds variables into the sync group (ranges don't need to be tested here)
385 307 iterations.push_back({std::make_shared<Synchronize>(0, syncId)});
386 308 iterations.push_back({std::make_shared<Synchronize>(1, syncId)});
387 309
388 310
389 311 // Moves var0 through several operations:
390 312 // - range of var0 changes
391 313 // - range or var1 changes according to the previous shift (one hour)
392 314 auto moveVar0 = [&iterations](const auto &var0NewRange) {
393 315 iterations.push_back(
394 316 {std::make_shared<Move>(0, var0NewRange), {{0, var0NewRange}, {1, var0NewRange}}});
395 317 };
396 318 moveVar0(SqpRange{dateTime(2017, 1, 1, 12, 0, 0), dateTime(2017, 1, 1, 13, 0, 0)});
397 319 moveVar0(SqpRange{dateTime(2017, 1, 1, 14, 0, 0), dateTime(2017, 1, 1, 15, 0, 0)});
398 320 moveVar0(SqpRange{dateTime(2017, 1, 1, 8, 0, 0), dateTime(2017, 1, 1, 9, 0, 0)});
399 321 // moveVar0(SqpRange{dateTime(2017, 1, 1, 7, 30, 0), dateTime(2017, 1, 1, 9, 30, 0)});
400 322 moveVar0(SqpRange{dateTime(2017, 1, 1, 2, 0, 0), dateTime(2017, 1, 1, 4, 0, 0)});
401 323 moveVar0(SqpRange{dateTime(2017, 1, 1, 6, 0, 0), dateTime(2017, 1, 1, 8, 0, 0)});
402 324
403 325 moveVar0(SqpRange{dateTime(2017, 1, 10, 6, 0, 0), dateTime(2017, 1, 15, 8, 0, 0)});
404 326 moveVar0(SqpRange{dateTime(2017, 1, 17, 6, 0, 0), dateTime(2017, 1, 25, 8, 0, 0)});
405 327 moveVar0(SqpRange{dateTime(2017, 1, 2, 6, 0, 0), dateTime(2017, 1, 8, 8, 0, 0)});
406 328
407 329 moveVar0(SqpRange{dateTime(2017, 4, 10, 6, 0, 0), dateTime(2017, 6, 15, 8, 0, 0)});
408 330 moveVar0(SqpRange{dateTime(2017, 1, 17, 6, 0, 0), dateTime(2017, 2, 25, 8, 0, 0)});
409 331 moveVar0(SqpRange{dateTime(2017, 7, 2, 6, 0, 0), dateTime(2017, 10, 8, 8, 0, 0)});
410 332 moveVar0(SqpRange{dateTime(2017, 4, 10, 6, 0, 0), dateTime(2017, 6, 15, 8, 0, 0)});
411 333 moveVar0(SqpRange{dateTime(2017, 1, 17, 6, 0, 0), dateTime(2017, 2, 25, 8, 0, 0)});
412 334 moveVar0(SqpRange{dateTime(2017, 7, 2, 6, 0, 0), dateTime(2017, 10, 8, 8, 0, 0)});
413 335 moveVar0(SqpRange{dateTime(2017, 4, 10, 6, 0, 0), dateTime(2017, 6, 15, 8, 0, 0)});
414 336 moveVar0(SqpRange{dateTime(2017, 1, 17, 6, 0, 0), dateTime(2017, 2, 25, 8, 0, 0)});
415 337 moveVar0(SqpRange{dateTime(2017, 7, 2, 6, 0, 0), dateTime(2017, 10, 8, 8, 0, 0)});
416 338 moveVar0(SqpRange{dateTime(2017, 4, 10, 6, 0, 0), dateTime(2017, 6, 15, 8, 0, 0)});
417 339 moveVar0(SqpRange{dateTime(2017, 1, 17, 6, 0, 0), dateTime(2017, 2, 25, 8, 0, 0)});
418 340 moveVar0(SqpRange{dateTime(2017, 7, 2, 6, 0, 0), dateTime(2017, 10, 8, 8, 0, 0)});
419 341
420 342
421 343 QTest::newRow("sync2") << syncId << initialRange << iterations << 4000;
422 344 // QTest::newRow("sync3") << syncId << initialRange << iterations << 5000;
423 345 }
424 346
425 347 void testSyncOnVarCase1()
426 348 {
427 349 // Id used to synchronize variables in the controller
428 350 auto syncId = QUuid::createUuid();
429 351
430 352 /// Generates a range according to a start time and a end time (the date is the same)
431 353 auto range = [](const QTime &startTime, const QTime &endTime) {
432 354 return SqpRange{DateUtils::secondsSinceEpoch(QDateTime{{2017, 1, 1}, startTime, Qt::UTC}),
433 355 DateUtils::secondsSinceEpoch(QDateTime{{2017, 1, 1}, endTime, Qt::UTC})};
434 356 };
435 357
436 358 auto initialRange = range({12, 0}, {13, 0});
437 359
438 360 Iterations creations{};
439 361 // Creates variables var0, var1 and var2
440 362 creations.push_back({std::make_shared<Create>(0), {{0, initialRange}}});
441 363
442 364 Iterations synchronization{};
443 365 // Adds variables into the sync group (ranges don't need to be tested here)
444 366 synchronization.push_back({std::make_shared<Synchronize>(0, syncId)});
445 367
446 368 Iterations iterations{};
447 369
448 370 // Moves var0 through several operations
449 371 auto moveOp = [&iterations](const auto &requestedRange, const auto &expectedRange, auto delay) {
450 372 iterations.push_back(
451 373 {std::make_shared<Move>(0, requestedRange, true, delay), {{0, expectedRange}}});
452 374 };
453 375
454 376 // we assume here 300 ms is enough to finsh a operation
455 377 int delayToFinish = 300;
456 378 // jump to right, let's the operation time to finish
457 379 moveOp(range({14, 30}, {15, 30}), range({14, 30}, {15, 30}), delayToFinish);
458 380 // pan to right, let's the operation time to finish
459 381 moveOp(range({14, 45}, {15, 45}), range({14, 45}, {15, 45}), delayToFinish);
460 382 // jump to left, let's the operation time to finish
461 383 moveOp(range({03, 30}, {04, 30}), range({03, 30}, {04, 30}), delayToFinish);
462 384 // Pan to left, let's the operation time to finish
463 385 moveOp(range({03, 10}, {04, 10}), range({03, 10}, {04, 10}), delayToFinish);
464 386 // Zoom in, let's the operation time to finish
465 387 moveOp(range({03, 30}, {04, 00}), range({03, 30}, {04, 00}), delayToFinish);
466 388 // Zoom out left, let's the operation time to finish
467 389 moveOp(range({01, 10}, {18, 10}), range({01, 10}, {18, 10}), delayToFinish);
468 390 // Go back to initial range
469 391 moveOp(initialRange, initialRange, delayToFinish);
470 392
471 393
472 394 // jump to right, let's the operation time to finish
473 395 // moveOp(range({14, 30}, {15, 30}), initialRange, delayToFinish);
474 396 // Zoom out left, let's the operation time to finish
475 397 moveOp(range({01, 10}, {18, 10}), initialRange, delayToFinish);
476 398 // Go back to initial range
477 399 moveOp(initialRange, initialRange, 300);
478 400
479 401 QTest::newRow("syncOnVarCase1") << syncId << initialRange << std::move(creations)
480 402 << std::move(iterations);
481 403 }
482 404 }
483 405
484 406 void TestVariableSync::testSync_data()
485 407 {
486 408 // ////////////// //
487 409 // Test structure //
488 410 // ////////////// //
489 411
490 412 QTest::addColumn<QUuid>("syncId");
491 413 QTest::addColumn<SqpRange>("initialRange");
492 414 QTest::addColumn<Iterations>("iterations");
493 415 QTest::addColumn<int>("operationDelay");
494 416
495 417 // ////////// //
496 418 // Test cases //
497 419 // ////////// //
498 420
499 421 testSyncCase1();
500 422 testSyncCase2();
501 423 }
502 424
503 void TestVariableSync::testSyncWithAborting_data()
504 {
505 // ////////////// //
506 // Test structure //
507 // ////////////// //
508
509 QTest::addColumn<QUuid>("syncId");
510 QTest::addColumn<SqpRange>("initialRange");
511 QTest::addColumn<Iterations>("creations");
512 QTest::addColumn<Iterations>("iterations");
513 QTest::addColumn<int>("operationDelay");
514
515 // ////////// //
516 // Test cases //
517 // ////////// //
518
519 testSyncCase1WithAborting();
520 }
521
522 425 void TestVariableSync::testSyncOneVar_data()
523 426 {
524 427 // ////////////// //
525 428 // Test structure //
526 429 // ////////////// //
527 430
528 431 QTest::addColumn<QUuid>("syncId");
529 432 QTest::addColumn<SqpRange>("initialRange");
530 433 QTest::addColumn<Iterations>("creations");
531 434 QTest::addColumn<Iterations>("iterations");
532 435
533 436 // ////////// //
534 437 // Test cases //
535 438 // ////////// //
536 439
537 440 testSyncOnVarCase1();
538 441 }
539 442
540 443 void TestVariableSync::testSync()
541 444 {
542 445 // Inits controllers
543 446 TimeController timeController{};
544 447 VariableController variableController{};
545 448 variableController.setTimeController(&timeController);
546 449
547 450 QFETCH(QUuid, syncId);
548 451 QFETCH(SqpRange, initialRange);
549 452 timeController.onTimeToUpdate(initialRange);
550 453
551 454 // Synchronization group used
552 455 variableController.onAddSynchronizationGroupId(syncId);
553 456
554 457 // For each iteration:
555 458 // - execute operation
556 459 // - compare the variables' state to the expected states
557 460 QFETCH(Iterations, iterations);
558 461 QFETCH(int, operationDelay);
559 462 for (const auto &iteration : iterations) {
560 463 iteration.m_Operation->exec(variableController);
561 464 QTest::qWait(operationDelay);
562 465
563 466 validateRanges(variableController, iteration.m_ExpectedRanges);
564 467 }
565 468 }
566 469
567 void TestVariableSync::testSyncWithAborting()
568 {
569 // Inits controllers
570 TimeController timeController{};
571 VariableController variableController{};
572 variableController.setTimeController(&timeController);
573
574 QFETCH(QUuid, syncId);
575 QFETCH(SqpRange, initialRange);
576 timeController.onTimeToUpdate(initialRange);
577
578 // Synchronization group used
579 variableController.onAddSynchronizationGroupId(syncId);
580
581 // For each iteration:
582 // - execute operation
583 // - compare the variables' state to the expected states
584 QFETCH(Iterations, iterations);
585 QFETCH(Iterations, creations);
586 QFETCH(int, operationDelay);
587
588 for (const auto &creation : creations) {
589 creation.m_Operation->exec(variableController);
590 QTest::qWait(operationDelay);
591 }
592
593 for (const auto &iteration : iterations) {
594 iteration.m_Operation->exec(variableController);
595 }
596
597 QTest::qWait(operationDelay);
598 validateRanges(variableController, iterations.back().m_ExpectedRanges);
599 }
600
601 470 void TestVariableSync::testSyncOneVar()
602 471 {
603 472 // Inits controllers
604 473 TimeController timeController{};
605 474 VariableController variableController{};
606 475 variableController.setTimeController(&timeController);
607 476
608 477 QFETCH(QUuid, syncId);
609 478 QFETCH(SqpRange, initialRange);
610 479 timeController.onTimeToUpdate(initialRange);
611 480
612 481 // Synchronization group used
613 482 variableController.onAddSynchronizationGroupId(syncId);
614 483
615 484 // For each iteration:
616 485 // - execute operation
617 486 // - compare the variables' state to the expected states
618 487 QFETCH(Iterations, iterations);
619 488 QFETCH(Iterations, creations);
620 489
621 490 for (const auto &creation : creations) {
622 491 creation.m_Operation->exec(variableController);
623 492 QTest::qWait(300);
624 493 }
625 494
626 495 for (const auto &iteration : iterations) {
627 496 iteration.m_Operation->exec(variableController);
628 497 }
629 498
630 499 if (!iterations.empty()) {
631 500 validateRanges(variableController, iterations.back().m_ExpectedRanges);
632 501 }
633 502 }
634 503
635 504 QTEST_MAIN(TestVariableSync)
636 505
637 506 #include "TestVariableSync.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"
General Comments 3
Under Review
author

Auto status change to "Under Review"

Approved
author

Merge lasted acquisition developpement on main Sciqlop branch

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