##// END OF EJS Templates
temp commit
Alexandre Leroux -
r680:00ac2d91df64
parent child
Show More
This diff has been collapsed as it changes many lines, (775 lines changed) Show them Hide them
@@ -0,0 +1,775
1 #include <Variable/Variable.h>
2 #include <Variable/VariableAcquisitionWorker.h>
3 #include <Variable/VariableCacheStrategy.h>
4 #include <Variable/VariableController.h>
5 #include <Variable/VariableModel.h>
6 #include <Variable/VariableSynchronizationGroup.h>
7
8 #include <Data/DataProviderParameters.h>
9 #include <Data/IDataProvider.h>
10 #include <Data/IDataSeries.h>
11 #include <Data/VariableRequest.h>
12 #include <Time/TimeController.h>
13
14 #include <QMutex>
15 #include <QThread>
16 #include <QUuid>
17 #include <QtCore/QItemSelectionModel>
18
19 #include <deque>
20 #include <set>
21 #include <unordered_map>
22
23 Q_LOGGING_CATEGORY(LOG_VariableController, "VariableController")
24
25 namespace {
26
27 SqpRange computeSynchroRangeRequested(const SqpRange &varRange, const SqpRange &graphRange,
28 const SqpRange &oldGraphRange)
29 {
30 auto zoomType = VariableController::getZoomType(graphRange, oldGraphRange);
31
32 auto varRangeRequested = varRange;
33 switch (zoomType) {
34 case AcquisitionZoomType::ZoomIn: {
35 auto deltaLeft = graphRange.m_TStart - oldGraphRange.m_TStart;
36 auto deltaRight = oldGraphRange.m_TEnd - graphRange.m_TEnd;
37 varRangeRequested.m_TStart += deltaLeft;
38 varRangeRequested.m_TEnd -= deltaRight;
39 break;
40 }
41
42 case AcquisitionZoomType::ZoomOut: {
43 auto deltaLeft = oldGraphRange.m_TStart - graphRange.m_TStart;
44 auto deltaRight = graphRange.m_TEnd - oldGraphRange.m_TEnd;
45 varRangeRequested.m_TStart -= deltaLeft;
46 varRangeRequested.m_TEnd += deltaRight;
47 break;
48 }
49 case AcquisitionZoomType::PanRight: {
50 auto deltaRight = graphRange.m_TEnd - oldGraphRange.m_TEnd;
51 varRangeRequested.m_TStart += deltaRight;
52 varRangeRequested.m_TEnd += deltaRight;
53 break;
54 }
55 case AcquisitionZoomType::PanLeft: {
56 auto deltaLeft = oldGraphRange.m_TStart - graphRange.m_TStart;
57 varRangeRequested.m_TStart -= deltaLeft;
58 varRangeRequested.m_TEnd -= deltaLeft;
59 break;
60 }
61 case AcquisitionZoomType::Unknown: {
62 qCCritical(LOG_VariableController())
63 << VariableController::tr("Impossible to synchronize: zoom type unknown");
64 break;
65 }
66 default:
67 qCCritical(LOG_VariableController()) << VariableController::tr(
68 "Impossible to synchronize: zoom type not take into account");
69 // No action
70 break;
71 }
72
73 return varRangeRequested;
74 }
75 }
76
77 struct VariableController::VariableControllerPrivate {
78 explicit VariableControllerPrivate(VariableController *parent)
79 : m_WorkingMutex{},
80 m_VariableModel{new VariableModel{parent}},
81 m_VariableSelectionModel{new QItemSelectionModel{m_VariableModel, parent}},
82 m_VariableCacheStrategy{std::make_unique<VariableCacheStrategy>()},
83 m_VariableAcquisitionWorker{std::make_unique<VariableAcquisitionWorker>()},
84 q{parent}
85 {
86
87 m_VariableAcquisitionWorker->moveToThread(&m_VariableAcquisitionWorkerThread);
88 m_VariableAcquisitionWorkerThread.setObjectName("VariableAcquisitionWorkerThread");
89 }
90
91
92 virtual ~VariableControllerPrivate()
93 {
94 qCDebug(LOG_VariableController()) << tr("VariableControllerPrivate destruction");
95 m_VariableAcquisitionWorkerThread.quit();
96 m_VariableAcquisitionWorkerThread.wait();
97 }
98
99
100 void processRequest(std::shared_ptr<Variable> var, const SqpRange &rangeRequested,
101 QUuid varRequestId);
102
103 QVector<SqpRange> provideNotInCacheDateTimeList(std::shared_ptr<Variable> variable,
104 const SqpRange &dateTime);
105
106 std::shared_ptr<Variable> findVariable(QUuid vIdentifier);
107 std::shared_ptr<IDataSeries>
108 retrieveDataSeries(const QVector<AcquisitionDataPacket> acqDataPacketVector);
109
110 void registerProvider(std::shared_ptr<IDataProvider> provider);
111
112 void storeVariableRequest(QUuid varId, QUuid varRequestId, const VariableRequest &varRequest);
113 QUuid acceptVariableRequest(QUuid varId, std::shared_ptr<IDataSeries> dataSeries);
114 void updateVariableRequest(QUuid varRequestId);
115 void cancelVariableRequest(QUuid varRequestId);
116
117 QMutex m_WorkingMutex;
118 /// Variable model. The VariableController has the ownership
119 VariableModel *m_VariableModel;
120 QItemSelectionModel *m_VariableSelectionModel;
121
122
123 TimeController *m_TimeController{nullptr};
124 std::unique_ptr<VariableCacheStrategy> m_VariableCacheStrategy;
125 std::unique_ptr<VariableAcquisitionWorker> m_VariableAcquisitionWorker;
126 QThread m_VariableAcquisitionWorkerThread;
127
128 std::unordered_map<std::shared_ptr<Variable>, std::shared_ptr<IDataProvider> >
129 m_VariableToProviderMap;
130 std::unordered_map<std::shared_ptr<Variable>, QUuid> m_VariableToIdentifierMap;
131 std::map<QUuid, std::shared_ptr<VariableSynchronizationGroup> >
132 m_GroupIdToVariableSynchronizationGroupMap;
133 std::map<QUuid, QUuid> m_VariableIdGroupIdMap;
134 std::set<std::shared_ptr<IDataProvider> > m_ProviderSet;
135
136 std::map<QUuid, std::map<QUuid, VariableRequest> > m_VarRequestIdToVarIdVarRequestMap;
137
138 std::map<QUuid, std::deque<QUuid> > m_VarIdToVarRequestIdQueueMap;
139
140
141 VariableController *q;
142 };
143
144
145 VariableController::VariableController(QObject *parent)
146 : QObject{parent}, impl{spimpl::make_unique_impl<VariableControllerPrivate>(this)}
147 {
148 qCDebug(LOG_VariableController()) << tr("VariableController construction")
149 << QThread::currentThread();
150
151 connect(impl->m_VariableModel, &VariableModel::abortProgessRequested, this,
152 &VariableController::onAbortProgressRequested);
153
154 connect(impl->m_VariableAcquisitionWorker.get(), &VariableAcquisitionWorker::dataProvided, this,
155 &VariableController::onDataProvided);
156 connect(impl->m_VariableAcquisitionWorker.get(),
157 &VariableAcquisitionWorker::variableRequestInProgress, this,
158 &VariableController::onVariableRetrieveDataInProgress);
159
160 connect(&impl->m_VariableAcquisitionWorkerThread, &QThread::started,
161 impl->m_VariableAcquisitionWorker.get(), &VariableAcquisitionWorker::initialize);
162 connect(&impl->m_VariableAcquisitionWorkerThread, &QThread::finished,
163 impl->m_VariableAcquisitionWorker.get(), &VariableAcquisitionWorker::finalize);
164
165
166 impl->m_VariableAcquisitionWorkerThread.start();
167 }
168
169 VariableController::~VariableController()
170 {
171 qCDebug(LOG_VariableController()) << tr("VariableController destruction")
172 << QThread::currentThread();
173 this->waitForFinish();
174 }
175
176 VariableModel *VariableController::variableModel() noexcept
177 {
178 return impl->m_VariableModel;
179 }
180
181 QItemSelectionModel *VariableController::variableSelectionModel() noexcept
182 {
183 return impl->m_VariableSelectionModel;
184 }
185
186 void VariableController::setTimeController(TimeController *timeController) noexcept
187 {
188 impl->m_TimeController = timeController;
189 }
190
191 std::shared_ptr<Variable>
192 VariableController::cloneVariable(std::shared_ptr<Variable> variable) noexcept
193 {
194 if (impl->m_VariableModel->containsVariable(variable)) {
195 // Clones variable
196 auto duplicate = variable->clone();
197
198 // Adds clone to model
199 impl->m_VariableModel->addVariable(duplicate);
200
201 // Generates clone identifier
202 impl->m_VariableToIdentifierMap[duplicate] = QUuid::createUuid();
203
204 // Registers provider
205 auto variableProvider = impl->m_VariableToProviderMap.at(variable);
206 auto duplicateProvider = variableProvider != nullptr ? variableProvider->clone() : nullptr;
207
208 impl->m_VariableToProviderMap[duplicate] = duplicateProvider;
209 if (duplicateProvider) {
210 impl->registerProvider(duplicateProvider);
211 }
212
213 return duplicate;
214 }
215 else {
216 qCCritical(LOG_VariableController())
217 << tr("Can't create duplicate of variable %1: variable not registered in the model")
218 .arg(variable->name());
219 return nullptr;
220 }
221 }
222
223 void VariableController::deleteVariable(std::shared_ptr<Variable> variable) noexcept
224 {
225 if (!variable) {
226 qCCritical(LOG_VariableController()) << "Can't delete variable: variable is null";
227 return;
228 }
229
230 // Spreads in SciQlop that the variable will be deleted, so that potential receivers can
231 // make some treatments before the deletion
232 emit variableAboutToBeDeleted(variable);
233
234 // Deletes identifier
235 impl->m_VariableToIdentifierMap.erase(variable);
236
237 // Deletes provider
238 auto nbProvidersDeleted = impl->m_VariableToProviderMap.erase(variable);
239 qCDebug(LOG_VariableController())
240 << tr("Number of providers deleted for variable %1: %2")
241 .arg(variable->name(), QString::number(nbProvidersDeleted));
242
243
244 // Deletes from model
245 impl->m_VariableModel->deleteVariable(variable);
246 }
247
248 void VariableController::deleteVariables(
249 const QVector<std::shared_ptr<Variable> > &variables) noexcept
250 {
251 for (auto variable : qAsConst(variables)) {
252 deleteVariable(variable);
253 }
254 }
255
256 void VariableController::abortProgress(std::shared_ptr<Variable> variable)
257 {
258 }
259
260 std::shared_ptr<Variable>
261 VariableController::createVariable(const QString &name, const QVariantHash &metadata,
262 std::shared_ptr<IDataProvider> provider) noexcept
263 {
264 if (!impl->m_TimeController) {
265 qCCritical(LOG_VariableController())
266 << tr("Impossible to create variable: The time controller is null");
267 return nullptr;
268 }
269
270 auto range = impl->m_TimeController->dateTime();
271
272 if (auto newVariable = impl->m_VariableModel->createVariable(name, range, metadata)) {
273 auto identifier = QUuid::createUuid();
274
275 // store the provider
276 impl->registerProvider(provider);
277
278 // Associate the provider
279 impl->m_VariableToProviderMap[newVariable] = provider;
280 impl->m_VariableToIdentifierMap[newVariable] = identifier;
281
282
283 auto varRequestId = QUuid::createUuid();
284 qCInfo(LOG_VariableController()) << "processRequest for" << name << varRequestId;
285 impl->processRequest(newVariable, range, varRequestId);
286 impl->updateVariableRequest(varRequestId);
287
288 return newVariable;
289 }
290 }
291
292 void VariableController::onDateTimeOnSelection(const SqpRange &dateTime)
293 {
294 // TODO check synchronisation and Rescale
295 qCDebug(LOG_VariableController()) << "VariableController::onDateTimeOnSelection"
296 << QThread::currentThread()->objectName();
297 auto selectedRows = impl->m_VariableSelectionModel->selectedRows();
298 auto varRequestId = QUuid::createUuid();
299
300 for (const auto &selectedRow : qAsConst(selectedRows)) {
301 if (auto selectedVariable = impl->m_VariableModel->variable(selectedRow.row())) {
302 selectedVariable->setRange(dateTime);
303 impl->processRequest(selectedVariable, dateTime, varRequestId);
304
305 // notify that rescale operation has to be done
306 emit rangeChanged(selectedVariable, dateTime);
307 }
308 }
309 impl->updateVariableRequest(varRequestId);
310 }
311
312 void VariableController::onDataProvided(QUuid vIdentifier, const SqpRange &rangeRequested,
313 const SqpRange &cacheRangeRequested,
314 QVector<AcquisitionDataPacket> dataAcquired)
315 {
316 auto retrievedDataSeries = impl->retrieveDataSeries(dataAcquired);
317 auto varRequestId = impl->acceptVariableRequest(vIdentifier, retrievedDataSeries);
318 if (!varRequestId.isNull()) {
319 impl->updateVariableRequest(varRequestId);
320 }
321 }
322
323 void VariableController::onVariableRetrieveDataInProgress(QUuid identifier, double progress)
324 {
325 if (auto var = impl->findVariable(identifier)) {
326 impl->m_VariableModel->setDataProgress(var, progress);
327 }
328 else {
329 qCCritical(LOG_VariableController())
330 << tr("Impossible to notify progression of a null variable");
331 }
332 }
333
334 void VariableController::onAbortProgressRequested(std::shared_ptr<Variable> variable)
335 {
336 qCDebug(LOG_VariableController()) << "TORM: VariableController::onAbortProgressRequested"
337 << QThread::currentThread()->objectName();
338
339 auto it = impl->m_VariableToIdentifierMap.find(variable);
340 if (it != impl->m_VariableToIdentifierMap.cend()) {
341 impl->m_VariableToProviderMap.at(variable)->requestDataAborting(it->second);
342 }
343 else {
344 qCWarning(LOG_VariableController())
345 << tr("Aborting progression of inexistant variable detected !!!")
346 << QThread::currentThread()->objectName();
347 }
348 }
349
350 void VariableController::onAddSynchronizationGroupId(QUuid synchronizationGroupId)
351 {
352 qCDebug(LOG_VariableController()) << "TORM: VariableController::onAddSynchronizationGroupId"
353 << QThread::currentThread()->objectName()
354 << synchronizationGroupId;
355 auto vSynchroGroup = std::make_shared<VariableSynchronizationGroup>();
356 impl->m_GroupIdToVariableSynchronizationGroupMap.insert(
357 std::make_pair(synchronizationGroupId, vSynchroGroup));
358 }
359
360 void VariableController::onRemoveSynchronizationGroupId(QUuid synchronizationGroupId)
361 {
362 impl->m_GroupIdToVariableSynchronizationGroupMap.erase(synchronizationGroupId);
363 }
364
365 void VariableController::onAddSynchronized(std::shared_ptr<Variable> variable,
366 QUuid synchronizationGroupId)
367
368 {
369 qCDebug(LOG_VariableController()) << "TORM: VariableController::onAddSynchronized"
370 << synchronizationGroupId;
371 auto varToVarIdIt = impl->m_VariableToIdentifierMap.find(variable);
372 if (varToVarIdIt != impl->m_VariableToIdentifierMap.cend()) {
373 auto groupIdToVSGIt
374 = impl->m_GroupIdToVariableSynchronizationGroupMap.find(synchronizationGroupId);
375 if (groupIdToVSGIt != impl->m_GroupIdToVariableSynchronizationGroupMap.cend()) {
376 impl->m_VariableIdGroupIdMap.insert(
377 std::make_pair(varToVarIdIt->second, synchronizationGroupId));
378 groupIdToVSGIt->second->addVariableId(varToVarIdIt->second);
379 }
380 else {
381 qCCritical(LOG_VariableController())
382 << tr("Impossible to synchronize a variable with an unknown sycnhronization group")
383 << variable->name();
384 }
385 }
386 else {
387 qCCritical(LOG_VariableController())
388 << tr("Impossible to synchronize a variable with no identifier") << variable->name();
389 }
390 }
391
392
393 void VariableController::onRequestDataLoading(QVector<std::shared_ptr<Variable> > variables,
394 const SqpRange &range, const SqpRange &oldRange,
395 bool synchronise)
396 {
397 // NOTE: oldRange isn't really necessary since oldRange == variable->range().
398
399 // we want to load data of the variable for the dateTime.
400 // First we check if the cache contains some of them.
401 // For the other, we ask the provider to give them.
402
403 auto varRequestId = QUuid::createUuid();
404 qCInfo(LOG_VariableController()) << "VariableController::onRequestDataLoading"
405 << QThread::currentThread()->objectName() << varRequestId;
406
407 for (const auto &var : variables) {
408 qCDebug(LOG_VariableController()) << "processRequest for" << var->name() << varRequestId;
409 impl->processRequest(var, range, varRequestId);
410 }
411
412 if (synchronise) {
413 // Get the group ids
414 qCDebug(LOG_VariableController())
415 << "TORM VariableController::onRequestDataLoading for synchro var ENABLE";
416 auto groupIds = std::set<QUuid>{};
417 auto groupIdToOldRangeMap = std::map<QUuid, SqpRange>{};
418 for (const auto &var : variables) {
419 auto varToVarIdIt = impl->m_VariableToIdentifierMap.find(var);
420 if (varToVarIdIt != impl->m_VariableToIdentifierMap.cend()) {
421 auto vId = varToVarIdIt->second;
422 auto varIdToGroupIdIt = impl->m_VariableIdGroupIdMap.find(vId);
423 if (varIdToGroupIdIt != impl->m_VariableIdGroupIdMap.cend()) {
424 auto gId = varIdToGroupIdIt->second;
425 groupIdToOldRangeMap.insert(std::make_pair(gId, var->range()));
426 if (groupIds.find(gId) == groupIds.cend()) {
427 qCDebug(LOG_VariableController()) << "Synchro detect group " << gId;
428 groupIds.insert(gId);
429 }
430 }
431 }
432 }
433
434 // We assume here all group ids exist
435 for (const auto &gId : groupIds) {
436 auto vSynchronizationGroup = impl->m_GroupIdToVariableSynchronizationGroupMap.at(gId);
437 auto vSyncIds = vSynchronizationGroup->getIds();
438 qCDebug(LOG_VariableController()) << "Var in synchro group ";
439 for (auto vId : vSyncIds) {
440 auto var = impl->findVariable(vId);
441
442 // Don't process already processed var
443 if (!variables.contains(var)) {
444 if (var != nullptr) {
445 qCDebug(LOG_VariableController()) << "processRequest synchro for"
446 << var->name();
447 auto vSyncRangeRequested = computeSynchroRangeRequested(
448 var->range(), range, groupIdToOldRangeMap.at(gId));
449 qCDebug(LOG_VariableController()) << "synchro RR" << vSyncRangeRequested;
450 impl->processRequest(var, vSyncRangeRequested, varRequestId);
451 }
452 else {
453 qCCritical(LOG_VariableController())
454
455 << tr("Impossible to synchronize a null variable");
456 }
457 }
458 }
459 }
460 }
461
462 impl->updateVariableRequest(varRequestId);
463 }
464
465
466 void VariableController::initialize()
467 {
468 qCDebug(LOG_VariableController()) << tr("VariableController init") << QThread::currentThread();
469 impl->m_WorkingMutex.lock();
470 qCDebug(LOG_VariableController()) << tr("VariableController init END");
471 }
472
473 void VariableController::finalize()
474 {
475 impl->m_WorkingMutex.unlock();
476 }
477
478 void VariableController::waitForFinish()
479 {
480 QMutexLocker locker{&impl->m_WorkingMutex};
481 }
482
483 AcquisitionZoomType VariableController::getZoomType(const SqpRange &range, const SqpRange &oldRange)
484 {
485 // t1.m_TStart <= t2.m_TStart && t2.m_TEnd <= t1.m_TEnd
486 auto zoomType = AcquisitionZoomType::Unknown;
487 if (range.m_TStart <= oldRange.m_TStart && oldRange.m_TEnd <= range.m_TEnd) {
488 zoomType = AcquisitionZoomType::ZoomOut;
489 }
490 else if (range.m_TStart > oldRange.m_TStart && range.m_TEnd > oldRange.m_TEnd) {
491 zoomType = AcquisitionZoomType::PanRight;
492 }
493 else if (range.m_TStart < oldRange.m_TStart && range.m_TEnd < oldRange.m_TEnd) {
494 zoomType = AcquisitionZoomType::PanLeft;
495 }
496 else if (range.m_TStart > oldRange.m_TStart && oldRange.m_TEnd > range.m_TEnd) {
497 zoomType = AcquisitionZoomType::ZoomIn;
498 }
499 else {
500 qCCritical(LOG_VariableController()) << "getZoomType: Unknown type detected";
501 }
502 return zoomType;
503 }
504
505 void VariableController::VariableControllerPrivate::processRequest(std::shared_ptr<Variable> var,
506 const SqpRange &rangeRequested,
507 QUuid varRequestId)
508 {
509
510 // TODO: protect at
511 auto varRequest = VariableRequest{};
512 auto varId = m_VariableToIdentifierMap.at(var);
513
514 auto varStrategyRangesRequested
515 = m_VariableCacheStrategy->computeStrategyRanges(var->range(), rangeRequested);
516 auto notInCacheRangeList = var->provideNotInCacheRangeList(varStrategyRangesRequested.second);
517 auto inCacheRangeList = var->provideInCacheRangeList(varStrategyRangesRequested.second);
518
519 if (!notInCacheRangeList.empty()) {
520 varRequest.m_RangeRequested = varStrategyRangesRequested.first;
521 varRequest.m_CacheRangeRequested = varStrategyRangesRequested.second;
522 qCDebug(LOG_VariableAcquisitionWorker()) << tr("TORM processRequest RR ") << rangeRequested;
523 qCDebug(LOG_VariableAcquisitionWorker()) << tr("TORM processRequest R ")
524 << varStrategyRangesRequested.first;
525 qCDebug(LOG_VariableAcquisitionWorker()) << tr("TORM processRequest CR ")
526 << varStrategyRangesRequested.second;
527 // store VarRequest
528 storeVariableRequest(varId, varRequestId, varRequest);
529
530 auto varProvider = m_VariableToProviderMap.at(var);
531 if (varProvider != nullptr) {
532 auto varRequestIdCanceled = m_VariableAcquisitionWorker->pushVariableRequest(
533 varRequestId, varId, varStrategyRangesRequested.first,
534 varStrategyRangesRequested.second,
535 DataProviderParameters{std::move(notInCacheRangeList), var->metadata()},
536 varProvider);
537
538 if (!varRequestIdCanceled.isNull()) {
539 qCInfo(LOG_VariableAcquisitionWorker()) << tr("varRequestIdCanceled: ")
540 << varRequestIdCanceled;
541 cancelVariableRequest(varRequestIdCanceled);
542 }
543 }
544 else {
545 qCCritical(LOG_VariableController())
546 << "Impossible to provide data with a null provider";
547 }
548
549 if (!inCacheRangeList.empty()) {
550 emit q->updateVarDisplaying(var, inCacheRangeList.first());
551 }
552 }
553 else {
554
555 varRequest.m_RangeRequested = varStrategyRangesRequested.first;
556 varRequest.m_CacheRangeRequested = varStrategyRangesRequested.second;
557 // store VarRequest
558 storeVariableRequest(varId, varRequestId, varRequest);
559 acceptVariableRequest(varId,
560 var->dataSeries()->subDataSeries(varStrategyRangesRequested.second));
561 }
562 }
563
564 std::shared_ptr<Variable>
565 VariableController::VariableControllerPrivate::findVariable(QUuid vIdentifier)
566 {
567 std::shared_ptr<Variable> var;
568 auto findReply = [vIdentifier](const auto &entry) { return vIdentifier == entry.second; };
569
570 auto end = m_VariableToIdentifierMap.cend();
571 auto it = std::find_if(m_VariableToIdentifierMap.cbegin(), end, findReply);
572 if (it != end) {
573 var = it->first;
574 }
575 else {
576 qCCritical(LOG_VariableController())
577 << tr("Impossible to find the variable with the identifier: ") << vIdentifier;
578 }
579
580 return var;
581 }
582
583 std::shared_ptr<IDataSeries> VariableController::VariableControllerPrivate::retrieveDataSeries(
584 const QVector<AcquisitionDataPacket> acqDataPacketVector)
585 {
586 qCDebug(LOG_VariableController()) << tr("TORM: retrieveDataSeries acqDataPacketVector size")
587 << acqDataPacketVector.size();
588 std::shared_ptr<IDataSeries> dataSeries;
589 if (!acqDataPacketVector.isEmpty()) {
590 dataSeries = acqDataPacketVector[0].m_DateSeries;
591 for (int i = 1; i < acqDataPacketVector.size(); ++i) {
592 dataSeries->merge(acqDataPacketVector[i].m_DateSeries.get());
593 }
594 }
595 qCDebug(LOG_VariableController()) << tr("TORM: retrieveDataSeries acqDataPacketVector size END")
596 << acqDataPacketVector.size();
597 return dataSeries;
598 }
599
600 void VariableController::VariableControllerPrivate::registerProvider(
601 std::shared_ptr<IDataProvider> provider)
602 {
603 if (m_ProviderSet.find(provider) == m_ProviderSet.end()) {
604 qCDebug(LOG_VariableController()) << tr("Registering of a new provider")
605 << provider->objectName();
606 m_ProviderSet.insert(provider);
607 connect(provider.get(), &IDataProvider::dataProvided, m_VariableAcquisitionWorker.get(),
608 &VariableAcquisitionWorker::onVariableDataAcquired);
609 connect(provider.get(), &IDataProvider::dataProvidedProgress,
610 m_VariableAcquisitionWorker.get(),
611 &VariableAcquisitionWorker::onVariableRetrieveDataInProgress);
612 }
613 else {
614 qCDebug(LOG_VariableController()) << tr("Cannot register provider, it already exists ");
615 }
616 }
617
618 void VariableController::VariableControllerPrivate::storeVariableRequest(
619 QUuid varId, QUuid varRequestId, const VariableRequest &varRequest)
620 {
621 // First request for the variable. we can create an entry for it
622 auto varIdToVarRequestIdQueueMapIt = m_VarIdToVarRequestIdQueueMap.find(varId);
623 if (varIdToVarRequestIdQueueMapIt == m_VarIdToVarRequestIdQueueMap.cend()) {
624 auto varRequestIdQueue = std::deque<QUuid>{};
625 qCDebug(LOG_VariableController()) << tr("Store REQUEST in QUEUE");
626 varRequestIdQueue.push_back(varRequestId);
627 m_VarIdToVarRequestIdQueueMap.insert(std::make_pair(varId, std::move(varRequestIdQueue)));
628 }
629 else {
630 qCDebug(LOG_VariableController()) << tr("Store REQUEST in EXISTING QUEUE");
631 auto &varRequestIdQueue = varIdToVarRequestIdQueueMapIt->second;
632 varRequestIdQueue.push_back(varRequestId);
633 }
634
635 auto varRequestIdToVarIdVarRequestMapIt = m_VarRequestIdToVarIdVarRequestMap.find(varRequestId);
636 if (varRequestIdToVarIdVarRequestMapIt == m_VarRequestIdToVarIdVarRequestMap.cend()) {
637 auto varIdToVarRequestMap = std::map<QUuid, VariableRequest>{};
638 varIdToVarRequestMap.insert(std::make_pair(varId, varRequest));
639 qCDebug(LOG_VariableController()) << tr("Store REQUESTID in MAP");
640 m_VarRequestIdToVarIdVarRequestMap.insert(
641 std::make_pair(varRequestId, std::move(varIdToVarRequestMap)));
642 }
643 else {
644 auto &varIdToVarRequestMap = varRequestIdToVarIdVarRequestMapIt->second;
645 qCDebug(LOG_VariableController()) << tr("Store REQUESTID in EXISTING MAP");
646 varIdToVarRequestMap.insert(std::make_pair(varId, varRequest));
647 }
648 }
649
650 QUuid VariableController::VariableControllerPrivate::acceptVariableRequest(
651 QUuid varId, std::shared_ptr<IDataSeries> dataSeries)
652 {
653 QUuid varRequestId;
654 auto varIdToVarRequestIdQueueMapIt = m_VarIdToVarRequestIdQueueMap.find(varId);
655 if (varIdToVarRequestIdQueueMapIt != m_VarIdToVarRequestIdQueueMap.cend()) {
656 auto &varRequestIdQueue = varIdToVarRequestIdQueueMapIt->second;
657 varRequestId = varRequestIdQueue.front();
658 auto varRequestIdToVarIdVarRequestMapIt
659 = m_VarRequestIdToVarIdVarRequestMap.find(varRequestId);
660 if (varRequestIdToVarIdVarRequestMapIt != m_VarRequestIdToVarIdVarRequestMap.cend()) {
661 auto &varIdToVarRequestMap = varRequestIdToVarIdVarRequestMapIt->second;
662 auto varIdToVarRequestMapIt = varIdToVarRequestMap.find(varId);
663 if (varIdToVarRequestMapIt != varIdToVarRequestMap.cend()) {
664 qCDebug(LOG_VariableController()) << tr("acceptVariableRequest");
665 auto &varRequest = varIdToVarRequestMapIt->second;
666 varRequest.m_DataSeries = dataSeries;
667 varRequest.m_CanUpdate = true;
668 }
669 else {
670 qCDebug(LOG_VariableController())
671 << tr("Impossible to acceptVariableRequest of a unknown variable id attached "
672 "to a variableRequestId")
673 << varRequestId << varId;
674 }
675 }
676 else {
677 qCCritical(LOG_VariableController())
678 << tr("Impossible to acceptVariableRequest of a unknown variableRequestId")
679 << varRequestId;
680 }
681
682 qCDebug(LOG_VariableController()) << tr("1: erase REQUEST in QUEUE ?")
683 << varRequestIdQueue.size();
684 varRequestIdQueue.pop_front();
685 qCDebug(LOG_VariableController()) << tr("2: erase REQUEST in QUEUE ?")
686 << varRequestIdQueue.size();
687 if (varRequestIdQueue.empty()) {
688 m_VarIdToVarRequestIdQueueMap.erase(varId);
689 }
690 }
691 else {
692 qCCritical(LOG_VariableController())
693 << tr("Impossible to acceptVariableRequest of a unknown variable id") << varId;
694 }
695
696 return varRequestId;
697 }
698
699 void VariableController::VariableControllerPrivate::updateVariableRequest(QUuid varRequestId)
700 {
701
702 auto varRequestIdToVarIdVarRequestMapIt = m_VarRequestIdToVarIdVarRequestMap.find(varRequestId);
703 if (varRequestIdToVarIdVarRequestMapIt != m_VarRequestIdToVarIdVarRequestMap.cend()) {
704 bool processVariableUpdate = true;
705 auto &varIdToVarRequestMap = varRequestIdToVarIdVarRequestMapIt->second;
706 for (auto varIdToVarRequestMapIt = varIdToVarRequestMap.cbegin();
707 (varIdToVarRequestMapIt != varIdToVarRequestMap.cend()) && processVariableUpdate;
708 ++varIdToVarRequestMapIt) {
709 processVariableUpdate &= varIdToVarRequestMapIt->second.m_CanUpdate;
710 qCDebug(LOG_VariableController()) << tr("updateVariableRequest")
711 << processVariableUpdate;
712 }
713
714 if (processVariableUpdate) {
715 for (auto varIdToVarRequestMapIt = varIdToVarRequestMap.cbegin();
716 varIdToVarRequestMapIt != varIdToVarRequestMap.cend(); ++varIdToVarRequestMapIt) {
717 if (auto var = findVariable(varIdToVarRequestMapIt->first)) {
718 auto &varRequest = varIdToVarRequestMapIt->second;
719 var->setRange(varRequest.m_RangeRequested);
720 var->setCacheRange(varRequest.m_CacheRangeRequested);
721 qCDebug(LOG_VariableController()) << tr("1: onDataProvided")
722 << varRequest.m_RangeRequested;
723 qCDebug(LOG_VariableController()) << tr("2: onDataProvided")
724 << varRequest.m_CacheRangeRequested;
725 var->mergeDataSeries(varRequest.m_DataSeries);
726 qCDebug(LOG_VariableController()) << tr("3: onDataProvided")
727 << varRequest.m_DataSeries->range();
728 qCDebug(LOG_VariableController()) << tr("4: onDataProvided");
729
730 /// @todo MPL: confirm
731 // Variable update is notified only if there is no pending request for it
732 if (m_VarIdToVarRequestIdQueueMap.count(varIdToVarRequestMapIt->first) == 0) {
733 emit var->updated();
734 }
735 }
736 else {
737 qCCritical(LOG_VariableController())
738 << tr("Impossible to update data to a null variable");
739 }
740 }
741
742 // cleaning varRequestId
743 qCDebug(LOG_VariableController()) << tr("0: erase REQUEST in MAP ?")
744 << m_VarRequestIdToVarIdVarRequestMap.size();
745 m_VarRequestIdToVarIdVarRequestMap.erase(varRequestId);
746 qCDebug(LOG_VariableController()) << tr("1: erase REQUEST in MAP ?")
747 << m_VarRequestIdToVarIdVarRequestMap.size();
748 }
749 }
750 else {
751 qCCritical(LOG_VariableController())
752 << tr("Cannot updateVariableRequest for a unknow varRequestId") << varRequestId;
753 }
754 }
755
756 void VariableController::VariableControllerPrivate::cancelVariableRequest(QUuid varRequestId)
757 {
758 // cleaning varRequestId
759 m_VarRequestIdToVarIdVarRequestMap.erase(varRequestId);
760
761 for (auto varIdToVarRequestIdQueueMapIt = m_VarIdToVarRequestIdQueueMap.begin();
762 varIdToVarRequestIdQueueMapIt != m_VarIdToVarRequestIdQueueMap.end();) {
763 auto &varRequestIdQueue = varIdToVarRequestIdQueueMapIt->second;
764 varRequestIdQueue.erase(
765 std::remove(varRequestIdQueue.begin(), varRequestIdQueue.end(), varRequestId),
766 varRequestIdQueue.end());
767 if (varRequestIdQueue.empty()) {
768 varIdToVarRequestIdQueueMapIt
769 = m_VarIdToVarRequestIdQueueMap.erase(varIdToVarRequestIdQueueMapIt);
770 }
771 else {
772 ++varIdToVarRequestIdQueueMapIt;
773 }
774 }
775 }
This diff has been collapsed as it changes many lines, (636 lines changed) Show them Hide them
@@ -0,0 +1,636
1 #include "Data/DataSeries.h"
2 #include "Data/ScalarSeries.h"
3 #include "Data/VectorSeries.h"
4
5 #include <cmath>
6
7 #include <QObject>
8 #include <QtTest>
9
10 Q_DECLARE_METATYPE(std::shared_ptr<ScalarSeries>)
11 Q_DECLARE_METATYPE(std::shared_ptr<VectorSeries>)
12
13 namespace {
14
15 using DataContainer = std::vector<double>;
16
17 void validateRange(DataSeriesIterator first, DataSeriesIterator last, const DataContainer &xData,
18 const DataContainer &valuesData)
19 {
20 QVERIFY(std::equal(first, last, xData.cbegin(), xData.cend(),
21 [](const auto &it, const auto &expectedX) { return it.x() == expectedX; }));
22 QVERIFY(std::equal(
23 first, last, valuesData.cbegin(), valuesData.cend(),
24 [](const auto &it, const auto &expectedVal) { return it.value() == expectedVal; }));
25 }
26
27 void validateRange(DataSeriesIterator first, DataSeriesIterator last, const DataContainer &xData,
28 const std::vector<DataContainer> &valuesData)
29 {
30 QVERIFY(std::equal(first, last, xData.cbegin(), xData.cend(),
31 [](const auto &it, const auto &expectedX) { return it.x() == expectedX; }));
32 for (auto i = 0; i < valuesData.size(); ++i) {
33 auto componentData = valuesData.at(i);
34
35 QVERIFY(std::equal(
36 first, last, componentData.cbegin(), componentData.cend(),
37 [i](const auto &it, const auto &expectedVal) { return it.value(i) == expectedVal; }));
38 }
39 }
40
41 } // namespace
42
43 class TestDataSeries : public QObject {
44 Q_OBJECT
45 private:
46 template <typename T>
47 void testValuesBoundsStructure()
48 {
49 // ////////////// //
50 // Test structure //
51 // ////////////// //
52
53 // Data series to get values bounds
54 QTest::addColumn<std::shared_ptr<T> >("dataSeries");
55
56 // x-axis range
57 QTest::addColumn<double>("minXAxis");
58 QTest::addColumn<double>("maxXAxis");
59
60 // Expected results
61 QTest::addColumn<bool>(
62 "expectedOK"); // Test is expected to be ok (i.e. method doesn't return end iterators)
63 QTest::addColumn<double>("expectedMinValue");
64 QTest::addColumn<double>("expectedMaxValue");
65 }
66
67 template <typename T>
68 void testValuesBounds()
69 {
70 QFETCH(std::shared_ptr<T>, dataSeries);
71 QFETCH(double, minXAxis);
72 QFETCH(double, maxXAxis);
73
74 QFETCH(bool, expectedOK);
75 QFETCH(double, expectedMinValue);
76 QFETCH(double, expectedMaxValue);
77
78 auto minMaxIts = dataSeries->valuesBounds(minXAxis, maxXAxis);
79 auto end = dataSeries->cend();
80
81 // Checks iterators with expected result
82 QCOMPARE(expectedOK, minMaxIts.first != end && minMaxIts.second != end);
83
84 if (expectedOK) {
85 auto compare = [](const auto &v1, const auto &v2) {
86 return (std::isnan(v1) && std::isnan(v2)) || v1 == v2;
87 };
88
89 QVERIFY(compare(expectedMinValue, minMaxIts.first->minValue()));
90 QVERIFY(compare(expectedMaxValue, minMaxIts.second->maxValue()));
91 }
92 }
93
94 template <typename T>
95 void testPurgeStructure()
96 {
97 // ////////////// //
98 // Test structure //
99 // ////////////// //
100
101 // Data series to purge
102 QTest::addColumn<std::shared_ptr<T> >("dataSeries");
103 QTest::addColumn<double>("min");
104 QTest::addColumn<double>("max");
105
106 // Expected values after purge
107 QTest::addColumn<DataContainer>("expectedXAxisData");
108 QTest::addColumn<std::vector<DataContainer> >("expectedValuesData");
109 }
110
111 template <typename T>
112 void testPurge()
113 {
114 QFETCH(std::shared_ptr<T>, dataSeries);
115 QFETCH(double, min);
116 QFETCH(double, max);
117
118 dataSeries->purge(min, max);
119
120 // Validates results
121 QFETCH(DataContainer, expectedXAxisData);
122 QFETCH(std::vector<DataContainer>, expectedValuesData);
123
124 validateRange(dataSeries->cbegin(), dataSeries->cend(), expectedXAxisData,
125 expectedValuesData);
126 }
127
128 private slots:
129
130 /// Input test data
131 /// @sa testCtor()
132 void testCtor_data();
133
134 /// Tests construction of a data series
135 void testCtor();
136
137 /// Input test data
138 /// @sa testMerge()
139 void testMerge_data();
140
141 /// Tests merge of two data series
142 void testMerge();
143
144 /// Input test data
145 /// @sa testPurgeScalar()
146 void testPurgeScalar_data();
147
148 /// Tests purge of a scalar series
149 void testPurgeScalar();
150
151 /// Input test data
152 /// @sa testPurgeVector()
153 void testPurgeVector_data();
154
155 /// Tests purge of a vector series
156 void testPurgeVector();
157
158 /// Input test data
159 /// @sa testMinXAxisData()
160 void testMinXAxisData_data();
161
162 /// Tests get min x-axis data of a data series
163 void testMinXAxisData();
164
165 /// Input test data
166 /// @sa testMaxXAxisData()
167 void testMaxXAxisData_data();
168
169 /// Tests get max x-axis data of a data series
170 void testMaxXAxisData();
171
172 /// Input test data
173 /// @sa testXAxisRange()
174 void testXAxisRange_data();
175
176 /// Tests get x-axis range of a data series
177 void testXAxisRange();
178
179 /// Input test data
180 /// @sa testValuesBoundsScalar()
181 void testValuesBoundsScalar_data();
182
183 /// Tests get values bounds of a scalar series
184 void testValuesBoundsScalar();
185
186 /// Input test data
187 /// @sa testValuesBoundsVector()
188 void testValuesBoundsVector_data();
189
190 /// Tests get values bounds of a vector series
191 void testValuesBoundsVector();
192 };
193
194 void TestDataSeries::testCtor_data()
195 {
196 // ////////////// //
197 // Test structure //
198 // ////////////// //
199
200 // x-axis data
201 QTest::addColumn<DataContainer>("xAxisData");
202 // values data
203 QTest::addColumn<DataContainer>("valuesData");
204
205 // expected x-axis data
206 QTest::addColumn<DataContainer>("expectedXAxisData");
207 // expected values data
208 QTest::addColumn<DataContainer>("expectedValuesData");
209
210 // ////////// //
211 // Test cases //
212 // ////////// //
213
214 QTest::newRow("invalidData (different sizes of vectors)")
215 << DataContainer{1., 2., 3., 4., 5.} << DataContainer{100., 200., 300.} << DataContainer{}
216 << DataContainer{};
217
218 QTest::newRow("sortedData") << DataContainer{1., 2., 3., 4., 5.}
219 << DataContainer{100., 200., 300., 400., 500.}
220 << DataContainer{1., 2., 3., 4., 5.}
221 << DataContainer{100., 200., 300., 400., 500.};
222
223 QTest::newRow("unsortedData") << DataContainer{5., 4., 3., 2., 1.}
224 << DataContainer{100., 200., 300., 400., 500.}
225 << DataContainer{1., 2., 3., 4., 5.}
226 << DataContainer{500., 400., 300., 200., 100.};
227
228 QTest::newRow("unsortedData2")
229 << DataContainer{1., 4., 3., 5., 2.} << DataContainer{100., 200., 300., 400., 500.}
230 << DataContainer{1., 2., 3., 4., 5.} << DataContainer{100., 500., 300., 200., 400.};
231 }
232
233 void TestDataSeries::testCtor()
234 {
235 // Creates series
236 QFETCH(DataContainer, xAxisData);
237 QFETCH(DataContainer, valuesData);
238
239 auto series = std::make_shared<ScalarSeries>(std::move(xAxisData), std::move(valuesData),
240 Unit{}, Unit{});
241
242 // Validates results : we check that the data series is sorted on its x-axis data
243 QFETCH(DataContainer, expectedXAxisData);
244 QFETCH(DataContainer, expectedValuesData);
245
246 validateRange(series->cbegin(), series->cend(), expectedXAxisData, expectedValuesData);
247 }
248
249 namespace {
250
251 std::shared_ptr<ScalarSeries> createScalarSeries(DataContainer xAxisData, DataContainer valuesData)
252 {
253 return std::make_shared<ScalarSeries>(std::move(xAxisData), std::move(valuesData), Unit{},
254 Unit{});
255 }
256
257 std::shared_ptr<VectorSeries> createVectorSeries(DataContainer xAxisData, DataContainer xValuesData,
258 DataContainer yValuesData,
259 DataContainer zValuesData)
260 {
261 return std::make_shared<VectorSeries>(std::move(xAxisData), std::move(xValuesData),
262 std::move(yValuesData), std::move(zValuesData), Unit{},
263 Unit{});
264 }
265
266 } // namespace
267
268 void TestDataSeries::testMerge_data()
269 {
270 // ////////////// //
271 // Test structure //
272 // ////////////// //
273
274 // Data series to merge
275 QTest::addColumn<std::shared_ptr<ScalarSeries> >("dataSeries");
276 QTest::addColumn<std::shared_ptr<ScalarSeries> >("dataSeries2");
277
278 // Expected values in the first data series after merge
279 QTest::addColumn<DataContainer>("expectedXAxisData");
280 QTest::addColumn<DataContainer>("expectedValuesData");
281
282 // ////////// //
283 // Test cases //
284 // ////////// //
285
286 QTest::newRow("sortedMerge")
287 << createScalarSeries({1., 2., 3., 4., 5.}, {100., 200., 300., 400., 500.})
288 << createScalarSeries({6., 7., 8., 9., 10.}, {600., 700., 800., 900., 1000.})
289 << DataContainer{1., 2., 3., 4., 5., 6., 7., 8., 9., 10.}
290 << DataContainer{100., 200., 300., 400., 500., 600., 700., 800., 900., 1000.};
291
292 QTest::newRow("unsortedMerge")
293 << createScalarSeries({6., 7., 8., 9., 10.}, {600., 700., 800., 900., 1000.})
294 << createScalarSeries({1., 2., 3., 4., 5.}, {100., 200., 300., 400., 500.})
295 << DataContainer{1., 2., 3., 4., 5., 6., 7., 8., 9., 10.}
296 << DataContainer{100., 200., 300., 400., 500., 600., 700., 800., 900., 1000.};
297
298 QTest::newRow("unsortedMerge2 (merge not made because source is in the bounds of dest)")
299 << createScalarSeries({1., 2., 8., 9., 10}, {100., 200., 800., 900., 1000.})
300 << createScalarSeries({3., 4., 5., 6., 7.}, {300., 400., 500., 600., 700.})
301 << DataContainer{1., 2., 8., 9., 10.} << DataContainer{100., 200., 800., 900., 1000.};
302
303 QTest::newRow("unsortedMerge3")
304 << createScalarSeries({3., 4., 5., 7., 8}, {300., 400., 500., 700., 800.})
305 << createScalarSeries({1., 2., 3., 7., 10.}, {100., 200., 333., 777., 1000.})
306 << DataContainer{1., 2., 3., 4., 5., 7., 8., 10.}
307 << DataContainer{100., 200., 300., 400., 500., 700., 800., 1000.};
308 }
309
310 void TestDataSeries::testMerge()
311 {
312 // Merges series
313 QFETCH(std::shared_ptr<ScalarSeries>, dataSeries);
314 QFETCH(std::shared_ptr<ScalarSeries>, dataSeries2);
315
316 dataSeries->merge(dataSeries2.get());
317
318 // Validates results : we check that the merge is valid and the data series is sorted on its
319 // x-axis data
320 QFETCH(DataContainer, expectedXAxisData);
321 QFETCH(DataContainer, expectedValuesData);
322
323 validateRange(dataSeries->cbegin(), dataSeries->cend(), expectedXAxisData, expectedValuesData);
324 }
325
326 void TestDataSeries::testPurgeScalar_data()
327 {
328 testPurgeStructure<ScalarSeries>();
329
330 // ////////// //
331 // Test cases //
332 // ////////// //
333
334 QTest::newRow("purgeScalar") << createScalarSeries({1., 2., 3., 4., 5.},
335 {100., 200., 300., 400., 500.})
336 << 2. << 4. << DataContainer{2., 3., 4.}
337 << std::vector<DataContainer>{{200., 300., 400.}};
338 QTest::newRow("purgeScalar2") << createScalarSeries({1., 2., 3., 4., 5.},
339 {100., 200., 300., 400., 500.})
340 << 0. << 2.5 << DataContainer{1., 2.}
341 << std::vector<DataContainer>{{100., 200.}};
342 QTest::newRow("purgeScalar3") << createScalarSeries({1., 2., 3., 4., 5.},
343 {100., 200., 300., 400., 500.})
344 << 3.5 << 7. << DataContainer{4., 5.}
345 << std::vector<DataContainer>{{400., 500.}};
346 QTest::newRow("purgeScalar4") << createScalarSeries({1., 2., 3., 4., 5.},
347 {100., 200., 300., 400., 500.})
348 << 0. << 7. << DataContainer{1., 2., 3., 4., 5.}
349 << std::vector<DataContainer>{{100., 200., 300., 400., 500.}};
350 QTest::newRow("purgeScalar5") << createScalarSeries({1., 2., 3., 4., 5.},
351 {100., 200., 300., 400., 500.})
352 << 5.5 << 7. << DataContainer{} << std::vector<DataContainer>{{}};
353 }
354
355 void TestDataSeries::testPurgeScalar()
356 {
357 testPurge<ScalarSeries>();
358 }
359
360 void TestDataSeries::testPurgeVector_data()
361 {
362 testPurgeStructure<VectorSeries>();
363
364 // ////////// //
365 // Test cases //
366 // ////////// //
367
368 QTest::newRow("purgeVector") << createVectorSeries({1., 2., 3., 4., 5.}, {6., 7., 8., 9., 10.},
369 {11., 12., 13., 14., 15.},
370 {16., 17., 18., 19., 20.})
371 << 2. << 4. << DataContainer{2., 3., 4.}
372 << std::vector<DataContainer>{
373 {7., 8., 9.}, {12., 13., 14.}, {17., 18., 19.}};
374 }
375
376 void TestDataSeries::testPurgeVector()
377 {
378 testPurge<VectorSeries>();
379 }
380
381 void TestDataSeries::testMinXAxisData_data()
382 {
383 // ////////////// //
384 // Test structure //
385 // ////////////// //
386
387 // Data series to get min data
388 QTest::addColumn<std::shared_ptr<ScalarSeries> >("dataSeries");
389
390 // Min data
391 QTest::addColumn<double>("min");
392
393 // Expected results
394 QTest::addColumn<bool>(
395 "expectedOK"); // if true, expects to have a result (i.e. the iterator != end iterator)
396 QTest::addColumn<double>(
397 "expectedMin"); // Expected value when method doesn't return end iterator
398
399 // ////////// //
400 // Test cases //
401 // ////////// //
402
403 QTest::newRow("minData1") << createScalarSeries({1., 2., 3., 4., 5.},
404 {100., 200., 300., 400., 500.})
405 << 0. << true << 1.;
406 QTest::newRow("minData2") << createScalarSeries({1., 2., 3., 4., 5.},
407 {100., 200., 300., 400., 500.})
408 << 1. << true << 1.;
409 QTest::newRow("minData3") << createScalarSeries({1., 2., 3., 4., 5.},
410 {100., 200., 300., 400., 500.})
411 << 1.1 << true << 2.;
412 QTest::newRow("minData4") << createScalarSeries({1., 2., 3., 4., 5.},
413 {100., 200., 300., 400., 500.})
414 << 5. << true << 5.;
415 QTest::newRow("minData5") << createScalarSeries({1., 2., 3., 4., 5.},
416 {100., 200., 300., 400., 500.})
417 << 5.1 << false << std::numeric_limits<double>::quiet_NaN();
418 QTest::newRow("minData6") << createScalarSeries({}, {}) << 1.1 << false
419 << std::numeric_limits<double>::quiet_NaN();
420 }
421
422 void TestDataSeries::testMinXAxisData()
423 {
424 QFETCH(std::shared_ptr<ScalarSeries>, dataSeries);
425 QFETCH(double, min);
426
427 QFETCH(bool, expectedOK);
428 QFETCH(double, expectedMin);
429
430 auto it = dataSeries->minXAxisData(min);
431
432 QCOMPARE(expectedOK, it != dataSeries->cend());
433
434 // If the method doesn't return a end iterator, checks with expected value
435 if (expectedOK) {
436 QCOMPARE(expectedMin, it->x());
437 }
438 }
439
440 void TestDataSeries::testMaxXAxisData_data()
441 {
442 // ////////////// //
443 // Test structure //
444 // ////////////// //
445
446 // Data series to get max data
447 QTest::addColumn<std::shared_ptr<ScalarSeries> >("dataSeries");
448
449 // Max data
450 QTest::addColumn<double>("max");
451
452 // Expected results
453 QTest::addColumn<bool>(
454 "expectedOK"); // if true, expects to have a result (i.e. the iterator != end iterator)
455 QTest::addColumn<double>(
456 "expectedMax"); // Expected value when method doesn't return end iterator
457
458 // ////////// //
459 // Test cases //
460 // ////////// //
461
462 QTest::newRow("maxData1") << createScalarSeries({1., 2., 3., 4., 5.},
463 {100., 200., 300., 400., 500.})
464 << 6. << true << 5.;
465 QTest::newRow("maxData2") << createScalarSeries({1., 2., 3., 4., 5.},
466 {100., 200., 300., 400., 500.})
467 << 5. << true << 5.;
468 QTest::newRow("maxData3") << createScalarSeries({1., 2., 3., 4., 5.},
469 {100., 200., 300., 400., 500.})
470 << 4.9 << true << 4.;
471 QTest::newRow("maxData4") << createScalarSeries({1., 2., 3., 4., 5.},
472 {100., 200., 300., 400., 500.})
473 << 1.1 << true << 1.;
474 QTest::newRow("maxData5") << createScalarSeries({1., 2., 3., 4., 5.},
475 {100., 200., 300., 400., 500.})
476 << 1. << true << 1.;
477 QTest::newRow("maxData6") << createScalarSeries({}, {}) << 1.1 << false
478 << std::numeric_limits<double>::quiet_NaN();
479 }
480
481 void TestDataSeries::testMaxXAxisData()
482 {
483 QFETCH(std::shared_ptr<ScalarSeries>, dataSeries);
484 QFETCH(double, max);
485
486 QFETCH(bool, expectedOK);
487 QFETCH(double, expectedMax);
488
489 auto it = dataSeries->maxXAxisData(max);
490
491 QCOMPARE(expectedOK, it != dataSeries->cend());
492
493 // If the method doesn't return a end iterator, checks with expected value
494 if (expectedOK) {
495 QCOMPARE(expectedMax, it->x());
496 }
497 }
498
499 void TestDataSeries::testXAxisRange_data()
500 {
501 // ////////////// //
502 // Test structure //
503 // ////////////// //
504
505 // Data series to get x-axis range
506 QTest::addColumn<std::shared_ptr<ScalarSeries> >("dataSeries");
507
508 // Min/max values
509 QTest::addColumn<double>("min");
510 QTest::addColumn<double>("max");
511
512 // Expected values
513 QTest::addColumn<DataContainer>("expectedXAxisData");
514 QTest::addColumn<DataContainer>("expectedValuesData");
515
516 // ////////// //
517 // Test cases //
518 // ////////// //
519
520 QTest::newRow("xAxisRange1") << createScalarSeries({1., 2., 3., 4., 5.},
521 {100., 200., 300., 400., 500.})
522 << -1. << 3.2 << DataContainer{1., 2., 3.}
523 << DataContainer{100., 200., 300.};
524 QTest::newRow("xAxisRange2") << createScalarSeries({1., 2., 3., 4., 5.},
525 {100., 200., 300., 400., 500.})
526 << 1. << 4. << DataContainer{1., 2., 3., 4.}
527 << DataContainer{100., 200., 300., 400.};
528 QTest::newRow("xAxisRange3") << createScalarSeries({1., 2., 3., 4., 5.},
529 {100., 200., 300., 400., 500.})
530 << 1. << 3.9 << DataContainer{1., 2., 3.}
531 << DataContainer{100., 200., 300.};
532 QTest::newRow("xAxisRange4") << createScalarSeries({1., 2., 3., 4., 5.},
533 {100., 200., 300., 400., 500.})
534 << 0. << 0.9 << DataContainer{} << DataContainer{};
535 QTest::newRow("xAxisRange5") << createScalarSeries({1., 2., 3., 4., 5.},
536 {100., 200., 300., 400., 500.})
537 << 0. << 1. << DataContainer{1.} << DataContainer{100.};
538 QTest::newRow("xAxisRange6") << createScalarSeries({1., 2., 3., 4., 5.},
539 {100., 200., 300., 400., 500.})
540 << 2.1 << 6. << DataContainer{3., 4., 5.}
541 << DataContainer{300., 400., 500.};
542 QTest::newRow("xAxisRange7") << createScalarSeries({1., 2., 3., 4., 5.},
543 {100., 200., 300., 400., 500.})
544 << 6. << 9. << DataContainer{} << DataContainer{};
545 QTest::newRow("xAxisRange8") << createScalarSeries({1., 2., 3., 4., 5.},
546 {100., 200., 300., 400., 500.})
547 << 5. << 9. << DataContainer{5.} << DataContainer{500.};
548 }
549
550 void TestDataSeries::testXAxisRange()
551 {
552 QFETCH(std::shared_ptr<ScalarSeries>, dataSeries);
553 QFETCH(double, min);
554 QFETCH(double, max);
555
556 QFETCH(DataContainer, expectedXAxisData);
557 QFETCH(DataContainer, expectedValuesData);
558
559 auto bounds = dataSeries->xAxisRange(min, max);
560 validateRange(bounds.first, bounds.second, expectedXAxisData, expectedValuesData);
561 }
562
563 void TestDataSeries::testValuesBoundsScalar_data()
564 {
565 testValuesBoundsStructure<ScalarSeries>();
566
567 // ////////// //
568 // Test cases //
569 // ////////// //
570 auto nan = std::numeric_limits<double>::quiet_NaN();
571
572 QTest::newRow("scalarBounds1")
573 << createScalarSeries({1., 2., 3., 4., 5.}, {100., 200., 300., 400., 500.}) << 0. << 6.
574 << true << 100. << 500.;
575 QTest::newRow("scalarBounds2")
576 << createScalarSeries({1., 2., 3., 4., 5.}, {100., 200., 300., 400., 500.}) << 2. << 4.
577 << true << 200. << 400.;
578 QTest::newRow("scalarBounds3")
579 << createScalarSeries({1., 2., 3., 4., 5.}, {100., 200., 300., 400., 500.}) << 0. << 0.5
580 << false << nan << nan;
581 QTest::newRow("scalarBounds4")
582 << createScalarSeries({1., 2., 3., 4., 5.}, {100., 200., 300., 400., 500.}) << 5.1 << 6.
583 << false << nan << nan;
584 QTest::newRow("scalarBounds5") << createScalarSeries({1.}, {100.}) << 0. << 2. << true << 100.
585 << 100.;
586 QTest::newRow("scalarBounds6") << createScalarSeries({}, {}) << 0. << 2. << false << nan << nan;
587
588 // Tests with NaN values: NaN values are not included in min/max search
589 QTest::newRow("scalarBounds7")
590 << createScalarSeries({1., 2., 3., 4., 5.}, {nan, 200., 300., 400., nan}) << 0. << 6.
591 << true << 200. << 400.;
592 QTest::newRow("scalarBounds8")
593 << createScalarSeries({1., 2., 3., 4., 5.}, {nan, nan, nan, nan, nan}) << 0. << 6. << true
594 << std::numeric_limits<double>::quiet_NaN() << std::numeric_limits<double>::quiet_NaN();
595 }
596
597 void TestDataSeries::testValuesBoundsScalar()
598 {
599 testValuesBounds<ScalarSeries>();
600 }
601
602 void TestDataSeries::testValuesBoundsVector_data()
603 {
604 testValuesBoundsStructure<VectorSeries>();
605
606 // ////////// //
607 // Test cases //
608 // ////////// //
609 auto nan = std::numeric_limits<double>::quiet_NaN();
610
611 QTest::newRow("vectorBounds1")
612 << createVectorSeries({1., 2., 3., 4., 5.}, {10., 15., 20., 13., 12.},
613 {35., 24., 10., 9., 0.3}, {13., 14., 12., 9., 24.})
614 << 0. << 6. << true << 0.3 << 35.; // min/max in same component
615 QTest::newRow("vectorBounds2")
616 << createVectorSeries({1., 2., 3., 4., 5.}, {2.3, 15., 20., 13., 12.},
617 {35., 24., 10., 9., 4.}, {13., 14., 12., 9., 24.})
618 << 0. << 6. << true << 2.3 << 35.; // min/max in same entry
619 QTest::newRow("vectorBounds3")
620 << createVectorSeries({1., 2., 3., 4., 5.}, {2.3, 15., 20., 13., 12.},
621 {35., 24., 10., 9., 4.}, {13., 14., 12., 9., 24.})
622 << 2. << 3. << true << 10. << 24.;
623
624 // Tests with NaN values: NaN values are not included in min/max search
625 QTest::newRow("vectorBounds4")
626 << createVectorSeries({1., 2.}, {nan, nan}, {nan, nan}, {nan, nan}) << 0. << 6. << true
627 << nan << nan;
628 }
629
630 void TestDataSeries::testValuesBoundsVector()
631 {
632 testValuesBounds<VectorSeries>();
633 }
634
635 QTEST_MAIN(TestDataSeries)
636 #include "TestDataSeries.moc"
@@ -1,460 +1,462
1 1 /*
2 2 ====================================================================
3 3 A Smart Pointer to IMPLementation (i.e. Smart PIMPL or just SPIMPL).
4 4 ====================================================================
5 5
6 6 Version: 1.1
7 7
8 8 Latest version:
9 9 https://github.com/oliora/samples/blob/master/spimpl.h
10 10 Rationale and description:
11 11 http://oliora.github.io/2015/12/29/pimpl-and-rule-of-zero.html
12 12
13 13 Copyright (c) 2015 Andrey Upadyshev (oliora@gmail.com)
14 14
15 15 Distributed under the Boost Software License, Version 1.0.
16 16 See http://www.boost.org/LICENSE_1_0.txt
17 17
18 18 Changes history
19 19 ---------------
20 20 v1.1:
21 21 - auto_ptr support is disabled by default for C++17 compatibility
22 22 v1.0:
23 23 - Released
24 24 */
25 25
26 26 #ifndef SPIMPL_H_
27 27 #define SPIMPL_H_
28 28
29 29 #include <cassert>
30 30 #include <memory>
31 31 #include <type_traits>
32 32
33 33 #if defined _MSC_VER && _MSC_VER < 1900 // MS Visual Studio before VS2015
34 34 #define SPIMPL_NO_CPP11_NOEXCEPT
35 35 #define SPIMPL_NO_CPP11_CONSTEXPR
36 36 #define SPIMPL_NO_CPP11_DEFAULT_MOVE_SPEC_FUNC
37 37 #endif
38 38
39 39 #if !defined SPIMPL_NO_CPP11_NOEXCEPT
40 40 #define SPIMPL_NOEXCEPT noexcept
41 41 #else
42 42 #define SPIMPL_NOEXCEPT
43 43 #endif
44 44
45 45 #if !defined SPIMPL_NO_CPP11_CONSTEXPR
46 46 #define SPIMPL_CONSTEXPR constexpr
47 47 #else
48 48 #define SPIMPL_CONSTEXPR
49 49 #endif
50 50
51 51 // define SPIMPL_HAS_AUTO_PTR to enable constructor and assignment operator that accept
52 52 // std::auto_ptr
53 53 // TODO: auto detect std::auto_ptr support
54 54
55 55
56 56 namespace spimpl {
57 57 namespace details {
58 58 template <class T>
59 59 T *default_copy(T *src)
60 60 {
61 61 static_assert(sizeof(T) > 0, "default_copy cannot copy incomplete type");
62 62 static_assert(!std::is_void<T>::value, "default_copy cannot copy incomplete type");
63 63 return new T(*src);
64 64 }
65 65
66 66 template <class T>
67 67 void default_delete(T *p) SPIMPL_NOEXCEPT
68 68 {
69 69 static_assert(sizeof(T) > 0, "default_delete cannot delete incomplete type");
70 70 static_assert(!std::is_void<T>::value, "default_delete cannot delete incomplete type");
71 71 delete p;
72 72 }
73 73
74 74 template <class T>
75 75 struct default_deleter {
76 76 using type = void (*)(T *);
77 77 };
78 78
79 79 template <class T>
80 80 using default_deleter_t = typename default_deleter<T>::type;
81 81
82 82 template <class T>
83 83 struct default_copier {
84 84 using type = T *(*)(T *);
85 85 };
86 86
87 87 template <class T>
88 88 using default_copier_t = typename default_copier<T>::type;
89 89
90 90 template <class T, class D, class C>
91 91 struct is_default_manageable
92 : public std::integral_constant<bool, std::is_same<D, default_deleter_t<T> >::value
93 && std::is_same<C, default_copier_t<T> >::value> {
92 : public std::integral_constant<bool,
93 std::is_same<D, default_deleter_t<T> >::value
94 && std::is_same<C, default_copier_t<T> >::value> {
94 95 };
95 96 }
96 97
97 98
98 99 template <class T, class Deleter = details::default_deleter_t<T>,
99 100 class Copier = details::default_copier_t<T> >
100 101 class impl_ptr {
101 102 private:
102 103 static_assert(!std::is_array<T>::value,
103 104 "impl_ptr specialization for arrays is not implemented");
104 105 struct dummy_t_ {
105 106 int dummy__;
106 107 };
107 108
108 109 public:
109 110 using pointer = T *;
110 111 using element_type = T;
111 112 using copier_type = typename std::decay<Copier>::type;
112 113 using deleter_type = typename std::decay<Deleter>::type;
113 114 using unique_ptr_type = std::unique_ptr<T, deleter_type>;
114 115 using is_default_manageable = details::is_default_manageable<T, deleter_type, copier_type>;
115 116
116 117 SPIMPL_CONSTEXPR impl_ptr() SPIMPL_NOEXCEPT : ptr_(nullptr, deleter_type{}),
117 118 copier_(copier_type{})
118 119 {
119 120 }
120 121
121 122 SPIMPL_CONSTEXPR impl_ptr(std::nullptr_t) SPIMPL_NOEXCEPT : impl_ptr() {}
122 123
123 124 template <class D, class C>
124 125 impl_ptr(pointer p, D &&d, C &&c,
125 126 typename std::enable_if<std::is_convertible<D, deleter_type>::value
126 127 && std::is_convertible<C, copier_type>::value,
127 128 dummy_t_>::type
128 129 = dummy_t_()) SPIMPL_NOEXCEPT : ptr_(std::move(p), std::forward<D>(d)),
129 130 copier_(std::forward<C>(c))
130 131 {
131 132 }
132 133
133 134 template <class U>
134 impl_ptr(U *u, typename std::enable_if<std::is_convertible<U *, pointer>::value
135 && is_default_manageable::value,
136 dummy_t_>::type
137 = dummy_t_()) SPIMPL_NOEXCEPT
135 impl_ptr(U *u,
136 typename std::enable_if<std::is_convertible<U *, pointer>::value
137 && is_default_manageable::value,
138 dummy_t_>::type
139 = dummy_t_()) SPIMPL_NOEXCEPT
138 140 : impl_ptr(u, &details::default_delete<T>, &details::default_copy<T>)
139 141 {
140 142 }
141 143
142 144 impl_ptr(const impl_ptr &r) : impl_ptr(r.clone()) {}
143 145
144 146 #ifndef SPIMPL_NO_CPP11_DEFAULT_MOVE_SPEC_FUNC
145 147 impl_ptr(impl_ptr &&r) SPIMPL_NOEXCEPT = default;
146 148 #else
147 149 impl_ptr(impl_ptr &&r) SPIMPL_NOEXCEPT : ptr_(std::move(r.ptr_)), copier_(std::move(r.copier_))
148 150 {
149 151 }
150 152 #endif
151 153
152 154 #ifdef SPIMPL_HAS_AUTO_PTR
153 155 template <class U>
154 impl_ptr(std::auto_ptr<U> &&u, typename std::enable_if<std::is_convertible<U *, pointer>::value
155 && is_default_manageable::value,
156 dummy_t_>::type
157 = dummy_t_()) SPIMPL_NOEXCEPT
158 : ptr_(u.release(), &details::default_delete<T>),
159 copier_(&details::default_copy<T>)
156 impl_ptr(std::auto_ptr<U> &&u,
157 typename std::enable_if<std::is_convertible<U *, pointer>::value
158 && is_default_manageable::value,
159 dummy_t_>::type
160 = dummy_t_()) SPIMPL_NOEXCEPT : ptr_(u.release(), &details::default_delete<T>),
161 copier_(&details::default_copy<T>)
160 162 {
161 163 }
162 164 #endif
163 165
164 166 template <class U>
165 167 impl_ptr(std::unique_ptr<U> &&u,
166 168 typename std::enable_if<std::is_convertible<U *, pointer>::value
167 169 && is_default_manageable::value,
168 170 dummy_t_>::type
169 171 = dummy_t_()) SPIMPL_NOEXCEPT : ptr_(u.release(), &details::default_delete<T>),
170 172 copier_(&details::default_copy<T>)
171 173 {
172 174 }
173 175
174 176 template <class U, class D, class C>
175 177 impl_ptr(std::unique_ptr<U, D> &&u, C &&c,
176 178 typename std::enable_if<std::is_convertible<U *, pointer>::value
177 179 && std::is_convertible<D, deleter_type>::value
178 180 && std::is_convertible<C, copier_type>::value,
179 181 dummy_t_>::type
180 182 = dummy_t_()) SPIMPL_NOEXCEPT : ptr_(std::move(u)),
181 183 copier_(std::forward<C>(c))
182 184 {
183 185 }
184 186
185 187 template <class U, class D, class C>
186 188 impl_ptr(impl_ptr<U, D, C> &&u,
187 189 typename std::enable_if<std::is_convertible<U *, pointer>::value
188 190 && std::is_convertible<D, deleter_type>::value
189 191 && std::is_convertible<C, copier_type>::value,
190 192 dummy_t_>::type
191 193 = dummy_t_()) SPIMPL_NOEXCEPT : ptr_(std::move(u.ptr_)),
192 194 copier_(std::move(u.copier_))
193 195 {
194 196 }
195 197
196 198 impl_ptr &operator=(const impl_ptr &r)
197 199 {
198 200 if (this == &r)
199 201 return *this;
200 202
201 203 return operator=(r.clone());
202 204 }
203 205
204 206 #ifndef SPIMPL_NO_CPP11_DEFAULT_MOVE_SPEC_FUNC
205 207 impl_ptr &operator=(impl_ptr &&r) SPIMPL_NOEXCEPT = default;
206 208 #else
207 209 impl_ptr &operator=(impl_ptr &&r) SPIMPL_NOEXCEPT
208 210 {
209 211 ptr_ = std::move(r.ptr_);
210 212 copier_ = std::move(r.copier_);
211 213 return *this;
212 214 }
213 215 #endif
214 216
215 217 template <class U, class D, class C>
216 218 typename std::enable_if<std::is_convertible<U *, pointer>::value
217 219 && std::is_convertible<D, deleter_type>::value
218 220 && std::is_convertible<C, copier_type>::value,
219 221 impl_ptr &>::type
220 222 operator=(impl_ptr<U, D, C> &&u) SPIMPL_NOEXCEPT
221 223 {
222 224 ptr_ = std::move(u.ptr_);
223 225 copier_ = std::move(u.copier_);
224 226 return *this;
225 227 }
226 228
227 229 template <class U, class D, class C>
228 230 typename std::enable_if<std::is_convertible<U *, pointer>::value
229 231 && std::is_convertible<D, deleter_type>::value
230 232 && std::is_convertible<C, copier_type>::value,
231 233 impl_ptr &>::type
232 234 operator=(const impl_ptr<U, D, C> &u)
233 235 {
234 236 return operator=(u.clone());
235 237 }
236 238
237 239 //
238 240
239 241 #ifdef SPIMPL_HAS_AUTO_PTR
240 242 template <class U>
241 243 typename std::enable_if<std::is_convertible<U *, pointer>::value
242 244 && is_default_manageable::value,
243 245 impl_ptr &>::type
244 246 operator=(std::auto_ptr<U> &&u) SPIMPL_NOEXCEPT
245 247 {
246 248 return operator=(impl_ptr(std::move(u)));
247 249 }
248 250 #endif
249 251
250 252 template <class U>
251 253 typename std::enable_if<std::is_convertible<U *, pointer>::value
252 254 && is_default_manageable::value,
253 255 impl_ptr &>::type
254 256 operator=(std::unique_ptr<U> &&u) SPIMPL_NOEXCEPT
255 257 {
256 258 return operator=(impl_ptr(std::move(u)));
257 259 }
258 260
259 261 impl_ptr clone() const
260 262 {
261 263 return impl_ptr(ptr_ ? copier_(ptr_.get()) : nullptr, ptr_.get_deleter(), copier_);
262 264 }
263 265
264 266 typename std::remove_reference<T>::type &operator*() const { return *ptr_; }
265 267 pointer operator->() const SPIMPL_NOEXCEPT { return get(); }
266 268 pointer get() const SPIMPL_NOEXCEPT { return ptr_.get(); }
267 269
268 270 void swap(impl_ptr &u) SPIMPL_NOEXCEPT
269 271 {
270 272 using std::swap;
271 273 ptr_.swap(u.ptr_);
272 274 swap(copier_, u.copier_);
273 275 }
274 276
275 277 pointer release() SPIMPL_NOEXCEPT { return ptr_.release(); }
276 278
277 279 unique_ptr_type release_unique() SPIMPL_NOEXCEPT { return std::move(ptr_); }
278 280
279 281 explicit operator bool() const SPIMPL_NOEXCEPT { return static_cast<bool>(ptr_); }
280 282
281 283 typename std::remove_reference<deleter_type>::type &get_deleter() SPIMPL_NOEXCEPT
282 284 {
283 285 return ptr_.get_deleter();
284 286 }
285 287 const typename std::remove_reference<deleter_type>::type &get_deleter() const SPIMPL_NOEXCEPT
286 288 {
287 289 return ptr_.get_deleter();
288 290 }
289 291
290 292 typename std::remove_reference<copier_type>::type &get_copier() SPIMPL_NOEXCEPT
291 293 {
292 294 return copier_;
293 295 }
294 296 const typename std::remove_reference<copier_type>::type &get_copier() const SPIMPL_NOEXCEPT
295 297 {
296 298 return copier_;
297 299 }
298 300
299 301 private:
300 302 unique_ptr_type ptr_;
301 303 copier_type copier_;
302 304 };
303 305
304 306
305 307 template <class T, class D, class C>
306 308 inline void swap(impl_ptr<T, D, C> &l, impl_ptr<T, D, C> &r) SPIMPL_NOEXCEPT
307 309 {
308 310 l.swap(r);
309 311 }
310 312
311 313
312 314 template <class T1, class D1, class C1, class T2, class D2, class C2>
313 315 inline bool operator==(const impl_ptr<T1, D1, C1> &l, const impl_ptr<T2, D2, C2> &r)
314 316 {
315 317 return l.get() == r.get();
316 318 }
317 319
318 320 template <class T1, class D1, class C1, class T2, class D2, class C2>
319 321 inline bool operator!=(const impl_ptr<T1, D1, C1> &l, const impl_ptr<T2, D2, C2> &r)
320 322 {
321 323 return !(l == r);
322 324 }
323 325
324 326 template <class T1, class D1, class C1, class T2, class D2, class C2>
325 327 inline bool operator<(const impl_ptr<T1, D1, C1> &l, const impl_ptr<T2, D2, C2> &r)
326 328 {
327 329 using P1 = typename impl_ptr<T1, D1, C1>::pointer;
328 330 using P2 = typename impl_ptr<T2, D2, C2>::pointer;
329 331 using CT = typename std::common_type<P1, P2>::type;
330 332 return std::less<CT>()(l.get(), r.get());
331 333 }
332 334
333 335 template <class T1, class D1, class C1, class T2, class D2, class C2>
334 336 inline bool operator>(const impl_ptr<T1, D1, C1> &l, const impl_ptr<T2, D2, C2> &r)
335 337 {
336 338 return r < l;
337 339 }
338 340
339 341 template <class T1, class D1, class C1, class T2, class D2, class C2>
340 342 inline bool operator<=(const impl_ptr<T1, D1, C1> &l, const impl_ptr<T2, D2, C2> &r)
341 343 {
342 344 return !(r < l);
343 345 }
344 346
345 347 template <class T1, class D1, class C1, class T2, class D2, class C2>
346 348 inline bool operator>=(const impl_ptr<T1, D1, C1> &l, const impl_ptr<T2, D2, C2> &r)
347 349 {
348 350 return !(l < r);
349 351 }
350 352
351 353 template <class T, class D, class C>
352 354 inline bool operator==(const impl_ptr<T, D, C> &p, std::nullptr_t) SPIMPL_NOEXCEPT
353 355 {
354 356 return !p;
355 357 }
356 358
357 359 template <class T, class D, class C>
358 360 inline bool operator==(std::nullptr_t, const impl_ptr<T, D, C> &p) SPIMPL_NOEXCEPT
359 361 {
360 362 return !p;
361 363 }
362 364
363 365 template <class T, class D, class C>
364 366 inline bool operator!=(const impl_ptr<T, D, C> &p, std::nullptr_t) SPIMPL_NOEXCEPT
365 367 {
366 368 return static_cast<bool>(p);
367 369 }
368 370
369 371 template <class T, class D, class C>
370 372 inline bool operator!=(std::nullptr_t, const impl_ptr<T, D, C> &p) SPIMPL_NOEXCEPT
371 373 {
372 374 return static_cast<bool>(p);
373 375 }
374 376
375 377 template <class T, class D, class C>
376 378 inline bool operator<(const impl_ptr<T, D, C> &l, std::nullptr_t)
377 379 {
378 380 using P = typename impl_ptr<T, D, C>::pointer;
379 381 return std::less<P>()(l.get(), nullptr);
380 382 }
381 383
382 384 template <class T, class D, class C>
383 385 inline bool operator<(std::nullptr_t, const impl_ptr<T, D, C> &p)
384 386 {
385 387 using P = typename impl_ptr<T, D, C>::pointer;
386 388 return std::less<P>()(nullptr, p.get());
387 389 }
388 390
389 391 template <class T, class D, class C>
390 392 inline bool operator>(const impl_ptr<T, D, C> &p, std::nullptr_t)
391 393 {
392 394 return nullptr < p;
393 395 }
394 396
395 397 template <class T, class D, class C>
396 398 inline bool operator>(std::nullptr_t, const impl_ptr<T, D, C> &p)
397 399 {
398 400 return p < nullptr;
399 401 }
400 402
401 403 template <class T, class D, class C>
402 404 inline bool operator<=(const impl_ptr<T, D, C> &p, std::nullptr_t)
403 405 {
404 406 return !(nullptr < p);
405 407 }
406 408
407 409 template <class T, class D, class C>
408 410 inline bool operator<=(std::nullptr_t, const impl_ptr<T, D, C> &p)
409 411 {
410 412 return !(p < nullptr);
411 413 }
412 414
413 415 template <class T, class D, class C>
414 416 inline bool operator>=(const impl_ptr<T, D, C> &p, std::nullptr_t)
415 417 {
416 418 return !(p < nullptr);
417 419 }
418 420
419 421 template <class T, class D, class C>
420 422 inline bool operator>=(std::nullptr_t, const impl_ptr<T, D, C> &p)
421 423 {
422 424 return !(nullptr < p);
423 425 }
424 426
425 427
426 428 template <class T, class... Args>
427 429 inline impl_ptr<T> make_impl(Args &&... args)
428 430 {
429 431 return impl_ptr<T>(new T(std::forward<Args>(args)...), &details::default_delete<T>,
430 432 &details::default_copy<T>);
431 433 }
432 434
433 435
434 436 // Helpers to manage unique impl, stored in std::unique_ptr
435 437
436 438 template <class T, class Deleter = void (*)(T *)>
437 439 using unique_impl_ptr = std::unique_ptr<T, Deleter>;
438 440
439 441 template <class T, class... Args>
440 442 inline unique_impl_ptr<T> make_unique_impl(Args &&... args)
441 443 {
442 444 static_assert(!std::is_array<T>::value, "unique_impl_ptr does not support arrays");
443 445 return unique_impl_ptr<T>(new T(std::forward<Args>(args)...), &details::default_delete<T>);
444 446 }
445 447 }
446 448
447 449 namespace std {
448 450 template <class T, class D, class C>
449 451 struct hash<spimpl::impl_ptr<T, D, C> > {
450 452 using argument_type = spimpl::impl_ptr<T, D, C>;
451 453 using result_type = size_t;
452 454
453 455 result_type operator()(const argument_type &p) const SPIMPL_NOEXCEPT
454 456 {
455 457 return hash<typename argument_type::pointer>()(p.get());
456 458 }
457 459 };
458 460 }
459 461
460 462 #endif // SPIMPL_H_
@@ -1,143 +1,143
1 1 #include "DataSource/DataSourceController.h"
2 2 #include "DataSource/DataSourceItem.h"
3 3
4 4 #include <Data/IDataProvider.h>
5 5
6 6 #include <QMutex>
7 7 #include <QThread>
8 8
9 9 #include <QDir>
10 10 #include <QStandardPaths>
11 11
12 12 Q_LOGGING_CATEGORY(LOG_DataSourceController, "DataSourceController")
13 13
14 14 namespace {
15 15
16 16 /**
17 17 * Builds the metadata of the variable that will be generated from the loading of an item
18 18 * @param dataSourceItem the data source item from which to generate the metadata
19 19 * @return the metadata of the variable
20 20 */
21 21 QVariantHash variableMetadata(const DataSourceItem &dataSourceItem)
22 22 {
23 23 // Variable metadata contains...
24 24
25 25 // ... all metadata of the item
26 26 auto result = dataSourceItem.data();
27 27
28 28 // ... and the name of the plugin, recovered from root item
29 29 result.insert(QStringLiteral("plugin"), dataSourceItem.rootItem().name());
30 30
31 31 return result;
32 32 }
33 33
34 34 } // namespace
35 35
36 36 class DataSourceController::DataSourceControllerPrivate {
37 37 public:
38 38 QMutex m_WorkingMutex;
39 39 /// Data sources registered
40 40 QHash<QUuid, QString> m_DataSources;
41 41 /// Data sources structures
42 42 std::map<QUuid, std::unique_ptr<DataSourceItem> > m_DataSourceItems;
43 43 /// Data providers registered
44 44 /// @remarks Data providers are stored as shared_ptr as they can be sent to a variable and
45 45 /// continue to live without necessarily the data source controller
46 46 std::map<QUuid, std::shared_ptr<IDataProvider> > m_DataProviders;
47 47 };
48 48
49 49 DataSourceController::DataSourceController(QObject *parent)
50 50 : impl{spimpl::make_unique_impl<DataSourceControllerPrivate>()}
51 51 {
52 qCDebug(LOG_DataSourceController()) << tr("DataSourceController construction")
53 << QThread::currentThread();
52 qCDebug(LOG_DataSourceController())
53 << tr("DataSourceController construction") << QThread::currentThread();
54 54 }
55 55
56 56 DataSourceController::~DataSourceController()
57 57 {
58 qCDebug(LOG_DataSourceController()) << tr("DataSourceController destruction")
59 << QThread::currentThread();
58 qCDebug(LOG_DataSourceController())
59 << tr("DataSourceController destruction") << QThread::currentThread();
60 60 this->waitForFinish();
61 61 }
62 62
63 63 QUuid DataSourceController::registerDataSource(const QString &dataSourceName) noexcept
64 64 {
65 65 auto dataSourceUid = QUuid::createUuid();
66 66 impl->m_DataSources.insert(dataSourceUid, dataSourceName);
67 67
68 68 return dataSourceUid;
69 69 }
70 70
71 71 void DataSourceController::setDataSourceItem(
72 72 const QUuid &dataSourceUid, std::unique_ptr<DataSourceItem> dataSourceItem) noexcept
73 73 {
74 74 if (!dataSourceItem) {
75 75 qCWarning(LOG_DataSourceController())
76 76 << tr("Data source item can't be registered (null item)");
77 77 return;
78 78 }
79 79
80 80 if (impl->m_DataSources.contains(dataSourceUid)) {
81 81 // The data provider is implicitly converted to a shared_ptr
82 82 impl->m_DataSourceItems.insert(std::make_pair(dataSourceUid, std::move(dataSourceItem)));
83 83
84 84 // Retrieves the data source item to emit the signal with it
85 85 auto it = impl->m_DataSourceItems.find(dataSourceUid);
86 86 if (it != impl->m_DataSourceItems.end()) {
87 87 emit dataSourceItemSet(it->second.get());
88 88 }
89 89 }
90 90 else {
91 91 qCWarning(LOG_DataSourceController()) << tr("Can't set data source item for uid %1 : no "
92 92 "data source has been registered with the uid")
93 93 .arg(dataSourceUid.toString());
94 94 }
95 95 }
96 96
97 97 void DataSourceController::setDataProvider(const QUuid &dataSourceUid,
98 98 std::unique_ptr<IDataProvider> dataProvider) noexcept
99 99 {
100 100 if (impl->m_DataSources.contains(dataSourceUid)) {
101 101 impl->m_DataProviders.insert(std::make_pair(dataSourceUid, std::move(dataProvider)));
102 102 }
103 103 else {
104 104 qCWarning(LOG_DataSourceController()) << tr("Can't set data provider for uid %1 : no data "
105 105 "source has been registered with the uid")
106 106 .arg(dataSourceUid.toString());
107 107 }
108 108 }
109 109
110 110 void DataSourceController::loadProductItem(const QUuid &dataSourceUid,
111 111 const DataSourceItem &productItem) noexcept
112 112 {
113 113 if (productItem.type() == DataSourceItemType::PRODUCT
114 114 || productItem.type() == DataSourceItemType::COMPONENT) {
115 115 /// Retrieves the data provider of the data source (if any)
116 116 auto it = impl->m_DataProviders.find(dataSourceUid);
117 117 auto dataProvider = (it != impl->m_DataProviders.end()) ? it->second : nullptr;
118 118
119 119 emit variableCreationRequested(productItem.name(), variableMetadata(productItem),
120 120 dataProvider);
121 121 }
122 122 else {
123 123 qCWarning(LOG_DataSourceController()) << tr("Can't load an item that is not a product");
124 124 }
125 125 }
126 126
127 127 void DataSourceController::initialize()
128 128 {
129 qCDebug(LOG_DataSourceController()) << tr("DataSourceController init")
130 << QThread::currentThread();
129 qCDebug(LOG_DataSourceController())
130 << tr("DataSourceController init") << QThread::currentThread();
131 131 impl->m_WorkingMutex.lock();
132 132 qCDebug(LOG_DataSourceController()) << tr("DataSourceController init END");
133 133 }
134 134
135 135 void DataSourceController::finalize()
136 136 {
137 137 impl->m_WorkingMutex.unlock();
138 138 }
139 139
140 140 void DataSourceController::waitForFinish()
141 141 {
142 142 QMutexLocker locker{&impl->m_WorkingMutex};
143 143 }
@@ -1,134 +1,134
1 1 #include "Network/NetworkController.h"
2 2
3 3 #include <QMutex>
4 4 #include <QNetworkAccessManager>
5 5 #include <QNetworkReply>
6 6 #include <QNetworkRequest>
7 7 #include <QReadWriteLock>
8 8 #include <QThread>
9 9
10 10 #include <unordered_map>
11 11
12 12 Q_LOGGING_CATEGORY(LOG_NetworkController, "NetworkController")
13 13
14 14 struct NetworkController::NetworkControllerPrivate {
15 15 explicit NetworkControllerPrivate(NetworkController *parent) : m_WorkingMutex{} {}
16 16
17 17 void lockRead() { m_Lock.lockForRead(); }
18 18 void lockWrite() { m_Lock.lockForWrite(); }
19 19 void unlock() { m_Lock.unlock(); }
20 20
21 21 QMutex m_WorkingMutex;
22 22
23 23 QReadWriteLock m_Lock;
24 24 std::unordered_map<QNetworkReply *, QUuid> m_NetworkReplyToVariableId;
25 25 std::unique_ptr<QNetworkAccessManager> m_AccessManager{nullptr};
26 26 };
27 27
28 28 NetworkController::NetworkController(QObject *parent)
29 29 : QObject(parent), impl{spimpl::make_unique_impl<NetworkControllerPrivate>(this)}
30 30 {
31 31 }
32 32
33 33 void NetworkController::onProcessRequested(const QNetworkRequest &request, QUuid identifier,
34 34 std::function<void(QNetworkReply *, QUuid)> callback)
35 35 {
36 qCDebug(LOG_NetworkController()) << tr("NetworkController registered")
37 << QThread::currentThread()->objectName();
36 qCDebug(LOG_NetworkController())
37 << tr("NetworkController registered") << QThread::currentThread()->objectName();
38 38 auto reply = impl->m_AccessManager->get(request);
39 39
40 40 // Store the couple reply id
41 41 impl->lockWrite();
42 42 impl->m_NetworkReplyToVariableId[reply] = identifier;
43 43 impl->unlock();
44 44
45 45 auto onReplyFinished = [reply, this, identifier, callback]() {
46 46
47 qCDebug(LOG_NetworkController()) << tr("NetworkController onReplyFinished")
48 << QThread::currentThread() << reply;
47 qCDebug(LOG_NetworkController())
48 << tr("NetworkController onReplyFinished") << QThread::currentThread() << reply;
49 49 impl->lockRead();
50 50 auto it = impl->m_NetworkReplyToVariableId.find(reply);
51 51 impl->unlock();
52 52 if (it != impl->m_NetworkReplyToVariableId.cend()) {
53 53 impl->lockWrite();
54 54 impl->m_NetworkReplyToVariableId.erase(reply);
55 55 impl->unlock();
56 56 // Deletes reply
57 57 callback(reply, identifier);
58 58 reply->deleteLater();
59 59
60 60 emit this->replyDownloadProgress(identifier, 0);
61 61 }
62 62
63 qCDebug(LOG_NetworkController()) << tr("NetworkController onReplyFinished END")
64 << QThread::currentThread() << reply;
63 qCDebug(LOG_NetworkController())
64 << tr("NetworkController onReplyFinished END") << QThread::currentThread() << reply;
65 65 };
66 66
67 67 auto onReplyProgress = [reply, this](qint64 bytesRead, qint64 totalBytes) {
68 68
69 69 double progress = (bytesRead * 100.0) / totalBytes;
70 70 qCDebug(LOG_NetworkController()) << tr("NetworkController onReplyProgress") << progress
71 71 << QThread::currentThread() << reply;
72 72 impl->lockRead();
73 73 auto it = impl->m_NetworkReplyToVariableId.find(reply);
74 74 impl->unlock();
75 75 if (it != impl->m_NetworkReplyToVariableId.cend()) {
76 76 emit this->replyDownloadProgress(it->second, progress);
77 77 }
78 qCDebug(LOG_NetworkController()) << tr("NetworkController onReplyProgress END")
79 << QThread::currentThread() << reply;
78 qCDebug(LOG_NetworkController())
79 << tr("NetworkController onReplyProgress END") << QThread::currentThread() << reply;
80 80 };
81 81
82 82
83 83 connect(reply, &QNetworkReply::finished, this, onReplyFinished);
84 84 connect(reply, &QNetworkReply::downloadProgress, this, onReplyProgress);
85 85 qCDebug(LOG_NetworkController()) << tr("NetworkController registered END")
86 86 << QThread::currentThread()->objectName() << reply;
87 87 }
88 88
89 89 void NetworkController::initialize()
90 90 {
91 91 qCDebug(LOG_NetworkController()) << tr("NetworkController init") << QThread::currentThread();
92 92 impl->m_WorkingMutex.lock();
93 93 impl->m_AccessManager = std::make_unique<QNetworkAccessManager>();
94 94
95 95
96 96 auto onReplyErrors = [this](QNetworkReply *reply, const QList<QSslError> &errors) {
97 97
98 98 qCCritical(LOG_NetworkController()) << tr("NetworkAcessManager errors: ") << errors;
99 99
100 100 };
101 101
102 102
103 103 connect(impl->m_AccessManager.get(), &QNetworkAccessManager::sslErrors, this, onReplyErrors);
104 104
105 105 qCDebug(LOG_NetworkController()) << tr("NetworkController init END");
106 106 }
107 107
108 108 void NetworkController::finalize()
109 109 {
110 110 impl->m_WorkingMutex.unlock();
111 111 }
112 112
113 113 void NetworkController::onReplyCanceled(QUuid identifier)
114 114 {
115 115 auto findReply = [identifier](const auto &entry) { return identifier == entry.second; };
116 qCDebug(LOG_NetworkController()) << tr("NetworkController onReplyCanceled")
117 << QThread::currentThread();
116 qCDebug(LOG_NetworkController())
117 << tr("NetworkController onReplyCanceled") << QThread::currentThread();
118 118
119 119
120 120 impl->lockRead();
121 121 auto end = impl->m_NetworkReplyToVariableId.cend();
122 122 auto it = std::find_if(impl->m_NetworkReplyToVariableId.cbegin(), end, findReply);
123 123 impl->unlock();
124 124 if (it != end) {
125 125 it->first->abort();
126 126 }
127 qCDebug(LOG_NetworkController()) << tr("NetworkController onReplyCanceled END")
128 << QThread::currentThread();
127 qCDebug(LOG_NetworkController())
128 << tr("NetworkController onReplyCanceled END") << QThread::currentThread();
129 129 }
130 130
131 131 void NetworkController::waitForFinish()
132 132 {
133 133 QMutexLocker locker{&impl->m_WorkingMutex};
134 134 }
@@ -1,307 +1,307
1 1 #include "Variable/Variable.h"
2 2
3 3 #include <Data/IDataSeries.h>
4 4 #include <Data/SqpRange.h>
5 5
6 6 #include <QMutex>
7 7 #include <QReadWriteLock>
8 8 #include <QThread>
9 9
10 10 Q_LOGGING_CATEGORY(LOG_Variable, "Variable")
11 11
12 12 struct Variable::VariablePrivate {
13 13 explicit VariablePrivate(const QString &name, const SqpRange &dateTime,
14 14 const QVariantHash &metadata)
15 15 : m_Name{name},
16 16 m_Range{dateTime},
17 17 m_Metadata{metadata},
18 18 m_DataSeries{nullptr},
19 19 m_RealRange{INVALID_RANGE},
20 20 m_NbPoints{0}
21 21 {
22 22 }
23 23
24 24 VariablePrivate(const VariablePrivate &other)
25 25 : m_Name{other.m_Name},
26 26 m_Range{other.m_Range},
27 27 m_Metadata{other.m_Metadata},
28 28 m_DataSeries{other.m_DataSeries != nullptr ? other.m_DataSeries->clone() : nullptr},
29 29 m_RealRange{other.m_RealRange},
30 30 m_NbPoints{other.m_NbPoints}
31 31 {
32 32 }
33 33
34 34 void lockRead() { m_Lock.lockForRead(); }
35 35 void lockWrite() { m_Lock.lockForWrite(); }
36 36 void unlock() { m_Lock.unlock(); }
37 37
38 38 void purgeDataSeries()
39 39 {
40 40 if (m_DataSeries) {
41 41 m_DataSeries->purge(m_CacheRange.m_TStart, m_CacheRange.m_TEnd);
42 42 }
43 43 updateRealRange();
44 44 updateNbPoints();
45 45 }
46 46
47 47 void updateNbPoints() { m_NbPoints = m_DataSeries ? m_DataSeries->nbPoints() : 0; }
48 48
49 49 /// Updates real range according to current variable range and data series
50 50 void updateRealRange()
51 51 {
52 52 if (m_DataSeries) {
53 53 m_DataSeries->lockRead();
54 54 auto end = m_DataSeries->cend();
55 55 auto minXAxisIt = m_DataSeries->minXAxisData(m_Range.m_TStart);
56 56 auto maxXAxisIt = m_DataSeries->maxXAxisData(m_Range.m_TEnd);
57 57
58 58 m_RealRange = (minXAxisIt != end && maxXAxisIt != end)
59 59 ? SqpRange{minXAxisIt->x(), maxXAxisIt->x()}
60 60 : INVALID_RANGE;
61 61 m_DataSeries->unlock();
62 62 }
63 63 else {
64 64 m_RealRange = INVALID_RANGE;
65 65 }
66 66 }
67 67
68 68 QString m_Name;
69 69
70 70 SqpRange m_Range;
71 71 SqpRange m_CacheRange;
72 72 QVariantHash m_Metadata;
73 73 std::shared_ptr<IDataSeries> m_DataSeries;
74 74 SqpRange m_RealRange;
75 75 int m_NbPoints;
76 76
77 77 QReadWriteLock m_Lock;
78 78 };
79 79
80 80 Variable::Variable(const QString &name, const SqpRange &dateTime, const QVariantHash &metadata)
81 81 : impl{spimpl::make_unique_impl<VariablePrivate>(name, dateTime, metadata)}
82 82 {
83 83 }
84 84
85 85 Variable::Variable(const Variable &other)
86 86 : impl{spimpl::make_unique_impl<VariablePrivate>(*other.impl)}
87 87 {
88 88 }
89 89
90 90 std::shared_ptr<Variable> Variable::clone() const
91 91 {
92 92 return std::make_shared<Variable>(*this);
93 93 }
94 94
95 95 QString Variable::name() const noexcept
96 96 {
97 97 impl->lockRead();
98 98 auto name = impl->m_Name;
99 99 impl->unlock();
100 100 return name;
101 101 }
102 102
103 103 void Variable::setName(const QString &name) noexcept
104 104 {
105 105 impl->lockWrite();
106 106 impl->m_Name = name;
107 107 impl->unlock();
108 108 }
109 109
110 110 SqpRange Variable::range() const noexcept
111 111 {
112 112 impl->lockRead();
113 113 auto range = impl->m_Range;
114 114 impl->unlock();
115 115 return range;
116 116 }
117 117
118 118 void Variable::setRange(const SqpRange &range) noexcept
119 119 {
120 120 impl->lockWrite();
121 121 impl->m_Range = range;
122 122 impl->updateRealRange();
123 123 impl->unlock();
124 124 }
125 125
126 126 SqpRange Variable::cacheRange() const noexcept
127 127 {
128 128 impl->lockRead();
129 129 auto cacheRange = impl->m_CacheRange;
130 130 impl->unlock();
131 131 return cacheRange;
132 132 }
133 133
134 134 void Variable::setCacheRange(const SqpRange &cacheRange) noexcept
135 135 {
136 136 impl->lockWrite();
137 137 if (cacheRange != impl->m_CacheRange) {
138 138 impl->m_CacheRange = cacheRange;
139 139 impl->purgeDataSeries();
140 140 }
141 141 impl->unlock();
142 142 }
143 143
144 144 int Variable::nbPoints() const noexcept
145 145 {
146 146 return impl->m_NbPoints;
147 147 }
148 148
149 149 SqpRange Variable::realRange() const noexcept
150 150 {
151 151 return impl->m_RealRange;
152 152 }
153 153
154 154 void Variable::mergeDataSeries(std::shared_ptr<IDataSeries> dataSeries) noexcept
155 155 {
156 156 qCDebug(LOG_Variable()) << "TORM Variable::mergeDataSeries"
157 157 << QThread::currentThread()->objectName();
158 158 if (!dataSeries) {
159 159 /// @todo ALX : log
160 160 return;
161 161 }
162 162
163 163 // Add or merge the data
164 164 impl->lockWrite();
165 165 if (!impl->m_DataSeries) {
166 166 impl->m_DataSeries = dataSeries->clone();
167 167 }
168 168 else {
169 169 impl->m_DataSeries->merge(dataSeries.get());
170 170 }
171 171 impl->purgeDataSeries();
172 172 impl->unlock();
173 173 }
174 174
175 175 std::shared_ptr<IDataSeries> Variable::dataSeries() const noexcept
176 176 {
177 177 impl->lockRead();
178 178 auto dataSeries = impl->m_DataSeries;
179 179 impl->unlock();
180 180
181 181 return dataSeries;
182 182 }
183 183
184 184 QVariantHash Variable::metadata() const noexcept
185 185 {
186 186 impl->lockRead();
187 187 auto metadata = impl->m_Metadata;
188 188 impl->unlock();
189 189 return metadata;
190 190 }
191 191
192 192 bool Variable::contains(const SqpRange &range) const noexcept
193 193 {
194 194 impl->lockRead();
195 195 auto res = impl->m_Range.contains(range);
196 196 impl->unlock();
197 197 return res;
198 198 }
199 199
200 200 bool Variable::intersect(const SqpRange &range) const noexcept
201 201 {
202 202
203 203 impl->lockRead();
204 204 auto res = impl->m_Range.intersect(range);
205 205 impl->unlock();
206 206 return res;
207 207 }
208 208
209 209 bool Variable::isInside(const SqpRange &range) const noexcept
210 210 {
211 211 impl->lockRead();
212 212 auto res = range.contains(SqpRange{impl->m_Range.m_TStart, impl->m_Range.m_TEnd});
213 213 impl->unlock();
214 214 return res;
215 215 }
216 216
217 217 bool Variable::cacheContains(const SqpRange &range) const noexcept
218 218 {
219 219 impl->lockRead();
220 220 auto res = impl->m_CacheRange.contains(range);
221 221 impl->unlock();
222 222 return res;
223 223 }
224 224
225 225 bool Variable::cacheIntersect(const SqpRange &range) const noexcept
226 226 {
227 227 impl->lockRead();
228 228 auto res = impl->m_CacheRange.intersect(range);
229 229 impl->unlock();
230 230 return res;
231 231 }
232 232
233 233 bool Variable::cacheIsInside(const SqpRange &range) const noexcept
234 234 {
235 235 impl->lockRead();
236 236 auto res = range.contains(SqpRange{impl->m_CacheRange.m_TStart, impl->m_CacheRange.m_TEnd});
237 237 impl->unlock();
238 238 return res;
239 239 }
240 240
241 241
242 242 QVector<SqpRange> Variable::provideNotInCacheRangeList(const SqpRange &range) const noexcept
243 243 {
244 244 // This code assume that cach in contigue. Can return 0, 1 or 2 SqpRange
245 245
246 246 auto notInCache = QVector<SqpRange>{};
247 247
248 248 if (!this->cacheContains(range)) {
249 249 if (range.m_TEnd <= impl->m_CacheRange.m_TStart
250 250 || range.m_TStart >= impl->m_CacheRange.m_TEnd) {
251 251 notInCache << range;
252 252 }
253 253 else if (range.m_TStart < impl->m_CacheRange.m_TStart
254 254 && range.m_TEnd <= impl->m_CacheRange.m_TEnd) {
255 255 notInCache << SqpRange{range.m_TStart, impl->m_CacheRange.m_TStart};
256 256 }
257 257 else if (range.m_TStart < impl->m_CacheRange.m_TStart
258 258 && range.m_TEnd > impl->m_CacheRange.m_TEnd) {
259 259 notInCache << SqpRange{range.m_TStart, impl->m_CacheRange.m_TStart}
260 260 << SqpRange{impl->m_CacheRange.m_TEnd, range.m_TEnd};
261 261 }
262 262 else if (range.m_TStart < impl->m_CacheRange.m_TEnd) {
263 263 notInCache << SqpRange{impl->m_CacheRange.m_TEnd, range.m_TEnd};
264 264 }
265 265 else {
266 qCCritical(LOG_Variable()) << tr("Detection of unknown case.")
267 << QThread::currentThread();
266 qCCritical(LOG_Variable())
267 << tr("Detection of unknown case.") << QThread::currentThread();
268 268 }
269 269 }
270 270
271 271 return notInCache;
272 272 }
273 273
274 274 QVector<SqpRange> Variable::provideInCacheRangeList(const SqpRange &range) const noexcept
275 275 {
276 276 // This code assume that cach in contigue. Can return 0 or 1 SqpRange
277 277
278 278 auto inCache = QVector<SqpRange>{};
279 279
280 280
281 281 if (this->intersect(range)) {
282 282 if (range.m_TStart <= impl->m_CacheRange.m_TStart
283 283 && range.m_TEnd >= impl->m_CacheRange.m_TStart
284 284 && range.m_TEnd < impl->m_CacheRange.m_TEnd) {
285 285 inCache << SqpRange{impl->m_CacheRange.m_TStart, range.m_TEnd};
286 286 }
287 287
288 288 else if (range.m_TStart >= impl->m_CacheRange.m_TStart
289 289 && range.m_TEnd <= impl->m_CacheRange.m_TEnd) {
290 290 inCache << range;
291 291 }
292 292 else if (range.m_TStart > impl->m_CacheRange.m_TStart
293 293 && range.m_TEnd > impl->m_CacheRange.m_TEnd) {
294 294 inCache << SqpRange{range.m_TStart, impl->m_CacheRange.m_TEnd};
295 295 }
296 296 else if (range.m_TStart <= impl->m_CacheRange.m_TStart
297 297 && range.m_TEnd >= impl->m_CacheRange.m_TEnd) {
298 298 inCache << impl->m_CacheRange;
299 299 }
300 300 else {
301 qCCritical(LOG_Variable()) << tr("Detection of unknown case.")
302 << QThread::currentThread();
301 qCCritical(LOG_Variable())
302 << tr("Detection of unknown case.") << QThread::currentThread();
303 303 }
304 304 }
305 305
306 306 return inCache;
307 307 }
@@ -1,238 +1,238
1 1 #include "Variable/VariableAcquisitionWorker.h"
2 2
3 3 #include "Variable/Variable.h"
4 4
5 5 #include <Data/AcquisitionRequest.h>
6 6 #include <Data/SqpRange.h>
7 7
8 8 #include <unordered_map>
9 9 #include <utility>
10 10
11 11 #include <QMutex>
12 12 #include <QReadWriteLock>
13 13 #include <QThread>
14 14
15 15 Q_LOGGING_CATEGORY(LOG_VariableAcquisitionWorker, "VariableAcquisitionWorker")
16 16
17 17 struct VariableAcquisitionWorker::VariableAcquisitionWorkerPrivate {
18 18
19 19 explicit VariableAcquisitionWorkerPrivate() : m_Lock{QReadWriteLock::Recursive} {}
20 20
21 21 void lockRead() { m_Lock.lockForRead(); }
22 22 void lockWrite() { m_Lock.lockForWrite(); }
23 23 void unlock() { m_Lock.unlock(); }
24 24
25 25 void removeVariableRequest(QUuid vIdentifier);
26 26
27 27 QMutex m_WorkingMutex;
28 28 QReadWriteLock m_Lock;
29 29
30 30 std::map<QUuid, QVector<AcquisitionDataPacket> > m_AcqIdentifierToAcqDataPacketVectorMap;
31 31 std::map<QUuid, AcquisitionRequest> m_AcqIdentifierToAcqRequestMap;
32 32 std::map<QUuid, std::pair<QUuid, QUuid> > m_VIdentifierToCurrrentAcqIdNextIdPairMap;
33 33 };
34 34
35 35
36 36 VariableAcquisitionWorker::VariableAcquisitionWorker(QObject *parent)
37 37 : QObject{parent}, impl{spimpl::make_unique_impl<VariableAcquisitionWorkerPrivate>()}
38 38 {
39 39 }
40 40
41 41 VariableAcquisitionWorker::~VariableAcquisitionWorker()
42 42 {
43 qCInfo(LOG_VariableAcquisitionWorker()) << tr("VariableAcquisitionWorker destruction")
44 << QThread::currentThread();
43 qCInfo(LOG_VariableAcquisitionWorker())
44 << tr("VariableAcquisitionWorker destruction") << QThread::currentThread();
45 45 this->waitForFinish();
46 46 }
47 47
48 48
49 49 QUuid VariableAcquisitionWorker::pushVariableRequest(QUuid varRequestId, QUuid vIdentifier,
50 50 SqpRange rangeRequested,
51 51 SqpRange cacheRangeRequested,
52 52 DataProviderParameters parameters,
53 53 std::shared_ptr<IDataProvider> provider)
54 54 {
55 55 qCDebug(LOG_VariableAcquisitionWorker())
56 56 << tr("TORM VariableAcquisitionWorker::pushVariableRequest ") << cacheRangeRequested;
57 57 auto varRequestIdCanceled = QUuid();
58 58
59 59 // Request creation
60 60 auto acqRequest = AcquisitionRequest{};
61 61 acqRequest.m_VarRequestId = varRequestId;
62 62 acqRequest.m_vIdentifier = vIdentifier;
63 63 acqRequest.m_DataProviderParameters = parameters;
64 64 acqRequest.m_RangeRequested = rangeRequested;
65 65 acqRequest.m_CacheRangeRequested = cacheRangeRequested;
66 66 acqRequest.m_Size = parameters.m_Times.size();
67 67 acqRequest.m_Provider = provider;
68 68
69 69
70 70 // Register request
71 71 impl->lockWrite();
72 72 impl->m_AcqIdentifierToAcqRequestMap.insert(
73 73 std::make_pair(acqRequest.m_AcqIdentifier, acqRequest));
74 74
75 75 auto it = impl->m_VIdentifierToCurrrentAcqIdNextIdPairMap.find(vIdentifier);
76 76 if (it != impl->m_VIdentifierToCurrrentAcqIdNextIdPairMap.cend()) {
77 77 // A current request already exists, we can replace the next one
78 78 auto nextAcqId = it->second.second;
79 79 auto acqIdentifierToAcqRequestMapIt = impl->m_AcqIdentifierToAcqRequestMap.find(nextAcqId);
80 80 if (acqIdentifierToAcqRequestMapIt != impl->m_AcqIdentifierToAcqRequestMap.cend()) {
81 81 auto request = acqIdentifierToAcqRequestMapIt->second;
82 82 varRequestIdCanceled = request.m_VarRequestId;
83 83 }
84 84
85 85 it->second.second = acqRequest.m_AcqIdentifier;
86 86 impl->unlock();
87 87 }
88 88 else {
89 89 // First request for the variable, it must be stored and executed
90 90 impl->m_VIdentifierToCurrrentAcqIdNextIdPairMap.insert(
91 91 std::make_pair(vIdentifier, std::make_pair(acqRequest.m_AcqIdentifier, QUuid())));
92 92 impl->unlock();
93 93
94 94 QMetaObject::invokeMethod(this, "onExecuteRequest", Qt::QueuedConnection,
95 95 Q_ARG(QUuid, acqRequest.m_AcqIdentifier));
96 96 }
97 97
98 98 return varRequestIdCanceled;
99 99 }
100 100
101 101 void VariableAcquisitionWorker::abortProgressRequested(QUuid vIdentifier)
102 102 {
103 103 // TODO
104 104 }
105 105
106 106 void VariableAcquisitionWorker::onVariableRetrieveDataInProgress(QUuid acqIdentifier,
107 107 double progress)
108 108 {
109 109 // TODO
110 110 }
111 111
112 112 void VariableAcquisitionWorker::onVariableDataAcquired(QUuid acqIdentifier,
113 113 std::shared_ptr<IDataSeries> dataSeries,
114 114 SqpRange dataRangeAcquired)
115 115 {
116 qCDebug(LOG_VariableAcquisitionWorker()) << tr("onVariableDataAcquired on range ")
117 << acqIdentifier << dataRangeAcquired;
116 qCDebug(LOG_VariableAcquisitionWorker())
117 << tr("onVariableDataAcquired on range ") << acqIdentifier << dataRangeAcquired;
118 118 impl->lockWrite();
119 119 auto aIdToARit = impl->m_AcqIdentifierToAcqRequestMap.find(acqIdentifier);
120 120 if (aIdToARit != impl->m_AcqIdentifierToAcqRequestMap.cend()) {
121 121 // Store the result
122 122 auto dataPacket = AcquisitionDataPacket{};
123 123 dataPacket.m_Range = dataRangeAcquired;
124 124 dataPacket.m_DateSeries = dataSeries;
125 125
126 126 auto aIdToADPVit = impl->m_AcqIdentifierToAcqDataPacketVectorMap.find(acqIdentifier);
127 127 if (aIdToADPVit != impl->m_AcqIdentifierToAcqDataPacketVectorMap.cend()) {
128 128 // A current request result already exists, we can update it
129 129 aIdToADPVit->second.push_back(dataPacket);
130 130 }
131 131 else {
132 132 // First request result for the variable, it must be stored
133 133 impl->m_AcqIdentifierToAcqDataPacketVectorMap.insert(
134 134 std::make_pair(acqIdentifier, QVector<AcquisitionDataPacket>() << dataPacket));
135 135 }
136 136
137 137
138 138 // Decrement the counter of the request
139 139 auto &acqRequest = aIdToARit->second;
140 140 acqRequest.m_Size = acqRequest.m_Size - 1;
141 141
142 142 // if the counter is 0, we can return data then run the next request if it exists and
143 143 // removed the finished request
144 144 if (acqRequest.m_Size == 0) {
145 145 // Return the data
146 146 aIdToADPVit = impl->m_AcqIdentifierToAcqDataPacketVectorMap.find(acqIdentifier);
147 147 if (aIdToADPVit != impl->m_AcqIdentifierToAcqDataPacketVectorMap.cend()) {
148 148 emit dataProvided(acqRequest.m_vIdentifier, acqRequest.m_RangeRequested,
149 149 acqRequest.m_CacheRangeRequested, aIdToADPVit->second);
150 150 }
151 151
152 152 // Execute the next one
153 153 auto it
154 154 = impl->m_VIdentifierToCurrrentAcqIdNextIdPairMap.find(acqRequest.m_vIdentifier);
155 155
156 156 if (it != impl->m_VIdentifierToCurrrentAcqIdNextIdPairMap.cend()) {
157 157 if (it->second.second.isNull()) {
158 158 // There is no next request, we can remove the variable request
159 159 impl->removeVariableRequest(acqRequest.m_vIdentifier);
160 160 }
161 161 else {
162 162 auto acqIdentifierToRemove = it->second.first;
163 163 // Move the next request to the current request
164 164 it->second.first = it->second.second;
165 165 it->second.second = QUuid();
166 166 // Remove AcquisitionRequest and results;
167 167 impl->m_AcqIdentifierToAcqRequestMap.erase(acqIdentifierToRemove);
168 168 impl->m_AcqIdentifierToAcqDataPacketVectorMap.erase(acqIdentifierToRemove);
169 169 // Execute the current request
170 170 QMetaObject::invokeMethod(this, "onExecuteRequest", Qt::QueuedConnection,
171 171 Q_ARG(QUuid, it->second.first));
172 172 }
173 173 }
174 174 else {
175 175 qCCritical(LOG_VariableAcquisitionWorker())
176 176 << tr("Impossible to execute the acquisition on an unfound variable ");
177 177 }
178 178 }
179 179 }
180 180 else {
181 181 qCCritical(LOG_VariableAcquisitionWorker())
182 182 << tr("Impossible to retrieve AcquisitionRequest for the incoming data");
183 183 }
184 184 impl->unlock();
185 185 }
186 186
187 187 void VariableAcquisitionWorker::initialize()
188 188 {
189 qCDebug(LOG_VariableAcquisitionWorker()) << tr("VariableAcquisitionWorker init")
190 << QThread::currentThread();
189 qCDebug(LOG_VariableAcquisitionWorker())
190 << tr("VariableAcquisitionWorker init") << QThread::currentThread();
191 191 impl->m_WorkingMutex.lock();
192 192 qCDebug(LOG_VariableAcquisitionWorker()) << tr("VariableAcquisitionWorker init END");
193 193 }
194 194
195 195 void VariableAcquisitionWorker::finalize()
196 196 {
197 197 impl->m_WorkingMutex.unlock();
198 198 }
199 199
200 200 void VariableAcquisitionWorker::waitForFinish()
201 201 {
202 202 QMutexLocker locker{&impl->m_WorkingMutex};
203 203 }
204 204
205 205 void VariableAcquisitionWorker::VariableAcquisitionWorkerPrivate::removeVariableRequest(
206 206 QUuid vIdentifier)
207 207 {
208 208 lockWrite();
209 209 auto it = m_VIdentifierToCurrrentAcqIdNextIdPairMap.find(vIdentifier);
210 210
211 211 if (it != m_VIdentifierToCurrrentAcqIdNextIdPairMap.cend()) {
212 212 // A current request already exists, we can replace the next one
213 213
214 214 m_AcqIdentifierToAcqRequestMap.erase(it->second.first);
215 215 m_AcqIdentifierToAcqDataPacketVectorMap.erase(it->second.first);
216 216
217 217 m_AcqIdentifierToAcqRequestMap.erase(it->second.second);
218 218 m_AcqIdentifierToAcqDataPacketVectorMap.erase(it->second.second);
219 219 }
220 220 m_VIdentifierToCurrrentAcqIdNextIdPairMap.erase(vIdentifier);
221 221 unlock();
222 222 }
223 223
224 224 void VariableAcquisitionWorker::onExecuteRequest(QUuid acqIdentifier)
225 225 {
226 226 qCDebug(LOG_VariableAcquisitionWorker()) << tr("onExecuteRequest") << QThread::currentThread();
227 227 impl->lockRead();
228 228 auto it = impl->m_AcqIdentifierToAcqRequestMap.find(acqIdentifier);
229 229 if (it != impl->m_AcqIdentifierToAcqRequestMap.cend()) {
230 230 auto request = it->second;
231 231 impl->unlock();
232 232 request.m_Provider->requestDataLoading(acqIdentifier, request.m_DataProviderParameters);
233 233 }
234 234 else {
235 235 impl->unlock();
236 236 // TODO log no acqIdentifier recognized
237 237 }
238 238 }
@@ -1,225 +1,225
1 1 #include "Variable/VariableCacheController.h"
2 2
3 3 #include "Variable/Variable.h"
4 4 #include <unordered_map>
5 5
6 6 #include <QThread>
7 7 Q_LOGGING_CATEGORY(LOG_VariableCacheController, "VariableCacheController")
8 8
9 9 struct VariableCacheController::VariableCacheControllerPrivate {
10 10
11 11 std::unordered_map<std::shared_ptr<Variable>, QVector<SqpRange> > m_VariableToSqpRangeListMap;
12 12
13 13 void addInCacheDataByEnd(const SqpRange &dateTime, QVector<SqpRange> &dateTimeList,
14 14 QVector<SqpRange> &notInCache, int cacheIndex, double currentTStart);
15 15
16 16 void addInCacheDataByStart(const SqpRange &dateTime, QVector<SqpRange> &dateTimeList,
17 17 QVector<SqpRange> &notInCache, int cacheIndex, double currentTStart);
18 18
19 19
20 20 void addDateTimeRecurse(const SqpRange &dateTime, QVector<SqpRange> &dateTimeList,
21 21 int cacheIndex);
22 22 };
23 23
24 24
25 25 VariableCacheController::VariableCacheController(QObject *parent)
26 26 : QObject{parent}, impl{spimpl::make_unique_impl<VariableCacheControllerPrivate>()}
27 27 {
28 28 }
29 29
30 30 void VariableCacheController::addDateTime(std::shared_ptr<Variable> variable,
31 31 const SqpRange &dateTime)
32 32 {
33 qCDebug(LOG_VariableCacheController()) << "VariableCacheController::addDateTime"
34 << QThread::currentThread()->objectName();
33 qCDebug(LOG_VariableCacheController())
34 << "VariableCacheController::addDateTime" << QThread::currentThread()->objectName();
35 35 if (variable) {
36 36 auto findVariableIte = impl->m_VariableToSqpRangeListMap.find(variable);
37 37 if (findVariableIte == impl->m_VariableToSqpRangeListMap.end()) {
38 38 impl->m_VariableToSqpRangeListMap[variable].push_back(dateTime);
39 39 }
40 40 else {
41 41
42 42 // addDateTime modify the list<SqpRange> of the variable in a way to ensure
43 43 // that the list is ordered : l(0) < l(1). We assume also a < b
44 44 // (with a & b of type SqpRange) means ts(b) > te(a)
45 45
46 46 // The algorithm will try the merge of two interval:
47 47 // - dateTime will be compare with the first interval of the list:
48 48 // A: if it is inferior, it will be inserted and it's finished.
49 49 // B: if it is in intersection, it will be merge then the merged one
50 50 // will be compared to the next interval. The old one is remove from the list
51 51 // C: if it is superior, we do the same with the next interval of the list
52 52
53 53 try {
54 54 impl->addDateTimeRecurse(dateTime, impl->m_VariableToSqpRangeListMap.at(variable),
55 55 0);
56 56 }
57 57 catch (const std::out_of_range &e) {
58 58 qCWarning(LOG_VariableCacheController()) << "addDateTime" << e.what();
59 59 }
60 60 }
61 61 }
62 62 }
63 63
64 64 void VariableCacheController::clear(std::shared_ptr<Variable> variable) noexcept
65 65 {
66 66 if (!variable) {
67 67 qCCritical(LOG_VariableCacheController()) << "Can't clear variable cache: variable is null";
68 68 return;
69 69 }
70 70
71 71 auto nbEntries = impl->m_VariableToSqpRangeListMap.erase(variable);
72 72
73 73 auto clearCacheMessage
74 74 = (nbEntries != 0)
75 75 ? tr("Variable cache cleared for variable %1").arg(variable->name())
76 76 : tr("No deletion of variable cache: no cache was associated with the variable");
77 77 qCDebug(LOG_VariableCacheController()) << clearCacheMessage;
78 78 }
79 79
80 80 QVector<SqpRange>
81 81 VariableCacheController::provideNotInCacheDateTimeList(std::shared_ptr<Variable> variable,
82 82 const SqpRange &dateTime)
83 83 {
84 84 qCDebug(LOG_VariableCacheController())
85 85 << "VariableCacheController::provideNotInCacheDateTimeList"
86 86 << QThread::currentThread()->objectName();
87 87 auto notInCache = QVector<SqpRange>{};
88 88
89 89 // This algorithm is recursif. The idea is to localise the start time then the end time in the
90 90 // list of date time request associated to the variable
91 91 // We assume that the list is ordered in a way that l(0) < l(1). We assume also a < b
92 92 // (with a & b of type SqpRange) means ts(b) > te(a)
93 93 auto it = impl->m_VariableToSqpRangeListMap.find(variable);
94 94 if (it != impl->m_VariableToSqpRangeListMap.end()) {
95 95 impl->addInCacheDataByStart(dateTime, it->second, notInCache, 0, dateTime.m_TStart);
96 96 }
97 97 else {
98 98 notInCache << dateTime;
99 99 }
100 100
101 101 return notInCache;
102 102 }
103 103
104 104 QVector<SqpRange>
105 105 VariableCacheController::dateCacheList(std::shared_ptr<Variable> variable) const noexcept
106 106 {
107 qCDebug(LOG_VariableCacheController()) << "VariableCacheController::dateCacheList"
108 << QThread::currentThread()->objectName();
107 qCDebug(LOG_VariableCacheController())
108 << "VariableCacheController::dateCacheList" << QThread::currentThread()->objectName();
109 109 try {
110 110 return impl->m_VariableToSqpRangeListMap.at(variable);
111 111 }
112 112 catch (const std::out_of_range &e) {
113 113 qCWarning(LOG_VariableCacheController()) << e.what();
114 114 return QVector<SqpRange>{};
115 115 }
116 116 }
117 117
118 118 void VariableCacheController::VariableCacheControllerPrivate::addDateTimeRecurse(
119 119 const SqpRange &dateTime, QVector<SqpRange> &dateTimeList, int cacheIndex)
120 120 {
121 121 const auto dateTimeListSize = dateTimeList.count();
122 122 if (cacheIndex >= dateTimeListSize) {
123 123 dateTimeList.push_back(dateTime);
124 124 // there is no anymore interval to compore, we can just push_back it
125 125 return;
126 126 }
127 127
128 128 auto currentDateTime = dateTimeList[cacheIndex];
129 129
130 130 if (dateTime.m_TEnd < currentDateTime.m_TStart) {
131 131 // The compared one is < to current one compared, we can insert it
132 132 dateTimeList.insert(cacheIndex, dateTime);
133 133 }
134 134 else if (dateTime.m_TStart > currentDateTime.m_TEnd) {
135 135 // The compared one is > to current one compared we can comparet if to the next one
136 136 addDateTimeRecurse(dateTime, dateTimeList, ++cacheIndex);
137 137 }
138 138 else {
139 139 // Merge cases: we need to merge the two interval, remove the old one from the list then
140 140 // rerun the algo from this index with the merged interval
141 141 auto mTStart = std::min(dateTime.m_TStart, currentDateTime.m_TStart);
142 142 auto mTEnd = std::max(dateTime.m_TEnd, currentDateTime.m_TEnd);
143 143 auto mergeDateTime = SqpRange{mTStart, mTEnd};
144 144
145 145 dateTimeList.remove(cacheIndex);
146 146 addDateTimeRecurse(mergeDateTime, dateTimeList, cacheIndex);
147 147 }
148 148 }
149 149
150 150
151 151 void VariableCacheController::VariableCacheControllerPrivate::addInCacheDataByEnd(
152 152 const SqpRange &dateTime, QVector<SqpRange> &dateTimeList, QVector<SqpRange> &notInCache,
153 153 int cacheIndex, double currentTStart)
154 154 {
155 155 const auto dateTimeListSize = dateTimeList.count();
156 156 if (cacheIndex >= dateTimeListSize) {
157 157 if (currentTStart < dateTime.m_TEnd) {
158 158
159 159 // te localised after all other interval: The last interval is [currentTsart, te]
160 160 notInCache.push_back(SqpRange{currentTStart, dateTime.m_TEnd});
161 161 }
162 162 return;
163 163 }
164 164
165 165 auto currentDateTimeJ = dateTimeList[cacheIndex];
166 166 if (dateTime.m_TEnd <= currentDateTimeJ.m_TStart) {
167 167 // te localised between to interval: The last interval is [currentTsart, te]
168 168 notInCache.push_back(SqpRange{currentTStart, dateTime.m_TEnd});
169 169 }
170 170 else {
171 171 notInCache.push_back(SqpRange{currentTStart, currentDateTimeJ.m_TStart});
172 172 if (dateTime.m_TEnd > currentDateTimeJ.m_TEnd) {
173 173 // te not localised before the current interval: we need to look at the next interval
174 174 addInCacheDataByEnd(dateTime, dateTimeList, notInCache, ++cacheIndex,
175 175 currentDateTimeJ.m_TEnd);
176 176 }
177 177 }
178 178 }
179 179
180 180 void VariableCacheController::VariableCacheControllerPrivate::addInCacheDataByStart(
181 181 const SqpRange &dateTime, QVector<SqpRange> &dateTimeList, QVector<SqpRange> &notInCache,
182 182 int cacheIndex, double currentTStart)
183 183 {
184 184 const auto dateTimeListSize = dateTimeList.count();
185 185 if (cacheIndex >= dateTimeListSize) {
186 186 // ts localised after all other interval: The last interval is [ts, te]
187 187 notInCache.push_back(SqpRange{currentTStart, dateTime.m_TEnd});
188 188 return;
189 189 }
190 190
191 191 auto currentDateTimeI = dateTimeList[cacheIndex];
192 192 if (currentTStart < currentDateTimeI.m_TStart) {
193 193
194 194 // ts localised between to interval: let's localized te
195 195 addInCacheDataByEnd(dateTime, dateTimeList, notInCache, cacheIndex, currentTStart);
196 196 }
197 197 else if (currentTStart < currentDateTimeI.m_TEnd) {
198 198 if (dateTime.m_TEnd > currentDateTimeI.m_TEnd) {
199 199 // ts not localised before the current interval: we need to look at the next interval
200 200 // We can assume now current tstart is the last interval tend, because data between them
201 201 // are
202 202 // in the cache
203 203 addInCacheDataByStart(dateTime, dateTimeList, notInCache, ++cacheIndex,
204 204 currentDateTimeI.m_TEnd);
205 205 }
206 206 }
207 207 else {
208 208 // ts not localised before the current interval: we need to look at the next interval
209 209 addInCacheDataByStart(dateTime, dateTimeList, notInCache, ++cacheIndex, currentTStart);
210 210 }
211 211 }
212 212
213 213
214 214 void VariableCacheController::displayCache(std::shared_ptr<Variable> variable) const
215 215 {
216 216 auto variableDateTimeList = impl->m_VariableToSqpRangeListMap.find(variable);
217 217 if (variableDateTimeList != impl->m_VariableToSqpRangeListMap.end()) {
218 qCInfo(LOG_VariableCacheController()) << tr("VariableCacheController::displayCache")
219 << variableDateTimeList->second;
218 qCInfo(LOG_VariableCacheController())
219 << tr("VariableCacheController::displayCache") << variableDateTimeList->second;
220 220 }
221 221 else {
222 222 qCWarning(LOG_VariableCacheController())
223 223 << tr("Cannot display a variable that is not in the cache");
224 224 }
225 225 }
@@ -1,805 +1,805
1 1 #include <Variable/Variable.h>
2 2 #include <Variable/VariableAcquisitionWorker.h>
3 3 #include <Variable/VariableCacheStrategy.h>
4 4 #include <Variable/VariableController.h>
5 5 #include <Variable/VariableModel.h>
6 6 #include <Variable/VariableSynchronizationGroup.h>
7 7
8 8 #include <Data/DataProviderParameters.h>
9 9 #include <Data/IDataProvider.h>
10 10 #include <Data/IDataSeries.h>
11 11 #include <Data/VariableRequest.h>
12 12 #include <Time/TimeController.h>
13 13
14 14 #include <QMutex>
15 15 #include <QThread>
16 16 #include <QUuid>
17 17 #include <QtCore/QItemSelectionModel>
18 18
19 19 #include <deque>
20 20 #include <set>
21 21 #include <unordered_map>
22 22
23 23 Q_LOGGING_CATEGORY(LOG_VariableController, "VariableController")
24 24
25 25 namespace {
26 26
27 27 SqpRange computeSynchroRangeRequested(const SqpRange &varRange, const SqpRange &graphRange,
28 28 const SqpRange &oldGraphRange)
29 29 {
30 30 auto zoomType = VariableController::getZoomType(graphRange, oldGraphRange);
31 31
32 32 auto varRangeRequested = varRange;
33 33 switch (zoomType) {
34 34 case AcquisitionZoomType::ZoomIn: {
35 35 auto deltaLeft = graphRange.m_TStart - oldGraphRange.m_TStart;
36 36 auto deltaRight = oldGraphRange.m_TEnd - graphRange.m_TEnd;
37 37 varRangeRequested.m_TStart += deltaLeft;
38 38 varRangeRequested.m_TEnd -= deltaRight;
39 39 break;
40 40 }
41 41
42 42 case AcquisitionZoomType::ZoomOut: {
43 43 auto deltaLeft = oldGraphRange.m_TStart - graphRange.m_TStart;
44 44 auto deltaRight = graphRange.m_TEnd - oldGraphRange.m_TEnd;
45 45 varRangeRequested.m_TStart -= deltaLeft;
46 46 varRangeRequested.m_TEnd += deltaRight;
47 47 break;
48 48 }
49 49 case AcquisitionZoomType::PanRight: {
50 50 auto deltaRight = graphRange.m_TEnd - oldGraphRange.m_TEnd;
51 51 varRangeRequested.m_TStart += deltaRight;
52 52 varRangeRequested.m_TEnd += deltaRight;
53 53 break;
54 54 }
55 55 case AcquisitionZoomType::PanLeft: {
56 56 auto deltaLeft = oldGraphRange.m_TStart - graphRange.m_TStart;
57 57 varRangeRequested.m_TStart -= deltaLeft;
58 58 varRangeRequested.m_TEnd -= deltaLeft;
59 59 break;
60 60 }
61 61 case AcquisitionZoomType::Unknown: {
62 62 qCCritical(LOG_VariableController())
63 63 << VariableController::tr("Impossible to synchronize: zoom type unknown");
64 64 break;
65 65 }
66 66 default:
67 67 qCCritical(LOG_VariableController()) << VariableController::tr(
68 68 "Impossible to synchronize: zoom type not take into account");
69 69 // No action
70 70 break;
71 71 }
72 72
73 73 return varRangeRequested;
74 74 }
75 75 }
76 76
77 77 struct VariableController::VariableControllerPrivate {
78 78 explicit VariableControllerPrivate(VariableController *parent)
79 79 : m_WorkingMutex{},
80 80 m_VariableModel{new VariableModel{parent}},
81 81 m_VariableSelectionModel{new QItemSelectionModel{m_VariableModel, parent}},
82 82 m_VariableCacheStrategy{std::make_unique<VariableCacheStrategy>()},
83 83 m_VariableAcquisitionWorker{std::make_unique<VariableAcquisitionWorker>()},
84 84 q{parent}
85 85 {
86 86
87 87 m_VariableAcquisitionWorker->moveToThread(&m_VariableAcquisitionWorkerThread);
88 88 m_VariableAcquisitionWorkerThread.setObjectName("VariableAcquisitionWorkerThread");
89 89 }
90 90
91 91
92 92 virtual ~VariableControllerPrivate()
93 93 {
94 94 qCDebug(LOG_VariableController()) << tr("VariableControllerPrivate destruction");
95 95 m_VariableAcquisitionWorkerThread.quit();
96 96 m_VariableAcquisitionWorkerThread.wait();
97 97 }
98 98
99 99
100 100 void processRequest(std::shared_ptr<Variable> var, const SqpRange &rangeRequested,
101 101 QUuid varRequestId);
102 102
103 103 QVector<SqpRange> provideNotInCacheDateTimeList(std::shared_ptr<Variable> variable,
104 104 const SqpRange &dateTime);
105 105
106 106 std::shared_ptr<Variable> findVariable(QUuid vIdentifier);
107 107 std::shared_ptr<IDataSeries>
108 108 retrieveDataSeries(const QVector<AcquisitionDataPacket> acqDataPacketVector);
109 109
110 110 void registerProvider(std::shared_ptr<IDataProvider> provider);
111 111
112 112 void storeVariableRequest(QUuid varId, QUuid varRequestId, const VariableRequest &varRequest);
113 113 QUuid acceptVariableRequest(QUuid varId, std::shared_ptr<IDataSeries> dataSeries);
114 114 void updateVariableRequest(QUuid varRequestId);
115 115 void cancelVariableRequest(QUuid varRequestId);
116 116
117 117 QMutex m_WorkingMutex;
118 118 /// Variable model. The VariableController has the ownership
119 119 VariableModel *m_VariableModel;
120 120 QItemSelectionModel *m_VariableSelectionModel;
121 121
122 122
123 123 TimeController *m_TimeController{nullptr};
124 124 std::unique_ptr<VariableCacheStrategy> m_VariableCacheStrategy;
125 125 std::unique_ptr<VariableAcquisitionWorker> m_VariableAcquisitionWorker;
126 126 QThread m_VariableAcquisitionWorkerThread;
127 127
128 128 std::unordered_map<std::shared_ptr<Variable>, std::shared_ptr<IDataProvider> >
129 129 m_VariableToProviderMap;
130 130 std::unordered_map<std::shared_ptr<Variable>, QUuid> m_VariableToIdentifierMap;
131 131 std::map<QUuid, std::shared_ptr<VariableSynchronizationGroup> >
132 132 m_GroupIdToVariableSynchronizationGroupMap;
133 133 std::map<QUuid, QUuid> m_VariableIdGroupIdMap;
134 134 std::set<std::shared_ptr<IDataProvider> > m_ProviderSet;
135 135
136 136 std::map<QUuid, std::map<QUuid, VariableRequest> > m_VarRequestIdToVarIdVarRequestMap;
137 137
138 138 std::map<QUuid, std::deque<QUuid> > m_VarIdToVarRequestIdQueueMap;
139 139
140 140
141 141 VariableController *q;
142 142 };
143 143
144 144
145 145 VariableController::VariableController(QObject *parent)
146 146 : QObject{parent}, impl{spimpl::make_unique_impl<VariableControllerPrivate>(this)}
147 147 {
148 qCDebug(LOG_VariableController()) << tr("VariableController construction")
149 << QThread::currentThread();
148 qCDebug(LOG_VariableController())
149 << tr("VariableController construction") << QThread::currentThread();
150 150
151 151 connect(impl->m_VariableModel, &VariableModel::abortProgessRequested, this,
152 152 &VariableController::onAbortProgressRequested);
153 153
154 154 connect(impl->m_VariableAcquisitionWorker.get(), &VariableAcquisitionWorker::dataProvided, this,
155 155 &VariableController::onDataProvided);
156 156 connect(impl->m_VariableAcquisitionWorker.get(),
157 157 &VariableAcquisitionWorker::variableRequestInProgress, this,
158 158 &VariableController::onVariableRetrieveDataInProgress);
159 159
160 160 connect(&impl->m_VariableAcquisitionWorkerThread, &QThread::started,
161 161 impl->m_VariableAcquisitionWorker.get(), &VariableAcquisitionWorker::initialize);
162 162 connect(&impl->m_VariableAcquisitionWorkerThread, &QThread::finished,
163 163 impl->m_VariableAcquisitionWorker.get(), &VariableAcquisitionWorker::finalize);
164 164
165 165
166 166 impl->m_VariableAcquisitionWorkerThread.start();
167 167 }
168 168
169 169 VariableController::~VariableController()
170 170 {
171 qCDebug(LOG_VariableController()) << tr("VariableController destruction")
172 << QThread::currentThread();
171 qCDebug(LOG_VariableController())
172 << tr("VariableController destruction") << QThread::currentThread();
173 173 this->waitForFinish();
174 174 }
175 175
176 176 VariableModel *VariableController::variableModel() noexcept
177 177 {
178 178 return impl->m_VariableModel;
179 179 }
180 180
181 181 QItemSelectionModel *VariableController::variableSelectionModel() noexcept
182 182 {
183 183 return impl->m_VariableSelectionModel;
184 184 }
185 185
186 186 void VariableController::setTimeController(TimeController *timeController) noexcept
187 187 {
188 188 impl->m_TimeController = timeController;
189 189 }
190 190
191 191 std::shared_ptr<Variable>
192 192 VariableController::cloneVariable(std::shared_ptr<Variable> variable) noexcept
193 193 {
194 194 if (impl->m_VariableModel->containsVariable(variable)) {
195 195 // Clones variable
196 196 auto duplicate = variable->clone();
197 197
198 198 // Adds clone to model
199 199 impl->m_VariableModel->addVariable(duplicate);
200 200
201 201 // Generates clone identifier
202 202 impl->m_VariableToIdentifierMap[duplicate] = QUuid::createUuid();
203 203
204 204 // Registers provider
205 205 auto variableProvider = impl->m_VariableToProviderMap.at(variable);
206 206 auto duplicateProvider = variableProvider != nullptr ? variableProvider->clone() : nullptr;
207 207
208 208 impl->m_VariableToProviderMap[duplicate] = duplicateProvider;
209 209 if (duplicateProvider) {
210 210 impl->registerProvider(duplicateProvider);
211 211 }
212 212
213 213 return duplicate;
214 214 }
215 215 else {
216 216 qCCritical(LOG_VariableController())
217 217 << tr("Can't create duplicate of variable %1: variable not registered in the model")
218 218 .arg(variable->name());
219 219 return nullptr;
220 220 }
221 221 }
222 222
223 223 void VariableController::deleteVariable(std::shared_ptr<Variable> variable) noexcept
224 224 {
225 225 if (!variable) {
226 226 qCCritical(LOG_VariableController()) << "Can't delete variable: variable is null";
227 227 return;
228 228 }
229 229
230 230 // Spreads in SciQlop that the variable will be deleted, so that potential receivers can
231 231 // make some treatments before the deletion
232 232 emit variableAboutToBeDeleted(variable);
233 233
234 234 // Deletes identifier
235 235 impl->m_VariableToIdentifierMap.erase(variable);
236 236
237 237 // Deletes provider
238 238 auto nbProvidersDeleted = impl->m_VariableToProviderMap.erase(variable);
239 239 qCDebug(LOG_VariableController())
240 240 << tr("Number of providers deleted for variable %1: %2")
241 241 .arg(variable->name(), QString::number(nbProvidersDeleted));
242 242
243 243
244 244 // Deletes from model
245 245 impl->m_VariableModel->deleteVariable(variable);
246 246 }
247 247
248 248 void VariableController::deleteVariables(
249 249 const QVector<std::shared_ptr<Variable> > &variables) noexcept
250 250 {
251 251 for (auto variable : qAsConst(variables)) {
252 252 deleteVariable(variable);
253 253 }
254 254 }
255 255
256 256 void VariableController::abortProgress(std::shared_ptr<Variable> variable)
257 257 {
258 258 }
259 259
260 260 std::shared_ptr<Variable>
261 261 VariableController::createVariable(const QString &name, const QVariantHash &metadata,
262 262 std::shared_ptr<IDataProvider> provider) noexcept
263 263 {
264 264 if (!impl->m_TimeController) {
265 265 qCCritical(LOG_VariableController())
266 266 << tr("Impossible to create variable: The time controller is null");
267 267 return nullptr;
268 268 }
269 269
270 270 auto range = impl->m_TimeController->dateTime();
271 271
272 272 if (auto newVariable = impl->m_VariableModel->createVariable(name, range, metadata)) {
273 273 auto identifier = QUuid::createUuid();
274 274
275 275 // store the provider
276 276 impl->registerProvider(provider);
277 277
278 278 // Associate the provider
279 279 impl->m_VariableToProviderMap[newVariable] = provider;
280 280 impl->m_VariableToIdentifierMap[newVariable] = identifier;
281 281
282 282
283 283 auto varRequestId = QUuid::createUuid();
284 284 qCInfo(LOG_VariableController()) << "processRequest for" << name << varRequestId;
285 285 impl->processRequest(newVariable, range, varRequestId);
286 286 impl->updateVariableRequest(varRequestId);
287 287
288 288 return newVariable;
289 289 }
290 290 }
291 291
292 292 void VariableController::onDateTimeOnSelection(const SqpRange &dateTime)
293 293 {
294 294 // TODO check synchronisation and Rescale
295 qCDebug(LOG_VariableController()) << "VariableController::onDateTimeOnSelection"
296 << QThread::currentThread()->objectName();
295 qCDebug(LOG_VariableController())
296 << "VariableController::onDateTimeOnSelection" << QThread::currentThread()->objectName();
297 297 auto selectedRows = impl->m_VariableSelectionModel->selectedRows();
298 298 auto varRequestId = QUuid::createUuid();
299 299
300 300 for (const auto &selectedRow : qAsConst(selectedRows)) {
301 301 if (auto selectedVariable = impl->m_VariableModel->variable(selectedRow.row())) {
302 302 selectedVariable->setRange(dateTime);
303 303 impl->processRequest(selectedVariable, dateTime, varRequestId);
304 304
305 305 // notify that rescale operation has to be done
306 306 emit rangeChanged(selectedVariable, dateTime);
307 307 }
308 308 }
309 309 impl->updateVariableRequest(varRequestId);
310 310 }
311 311
312 312 void VariableController::onDataProvided(QUuid vIdentifier, const SqpRange &rangeRequested,
313 313 const SqpRange &cacheRangeRequested,
314 314 QVector<AcquisitionDataPacket> dataAcquired)
315 315 {
316 316 auto retrievedDataSeries = impl->retrieveDataSeries(dataAcquired);
317 317 auto varRequestId = impl->acceptVariableRequest(vIdentifier, retrievedDataSeries);
318 318 if (!varRequestId.isNull()) {
319 319 impl->updateVariableRequest(varRequestId);
320 320 }
321 321 }
322 322
323 323 void VariableController::onVariableRetrieveDataInProgress(QUuid identifier, double progress)
324 324 {
325 325 if (auto var = impl->findVariable(identifier)) {
326 326 impl->m_VariableModel->setDataProgress(var, progress);
327 327 }
328 328 else {
329 329 qCCritical(LOG_VariableController())
330 330 << tr("Impossible to notify progression of a null variable");
331 331 }
332 332 }
333 333
334 334 void VariableController::onAbortProgressRequested(std::shared_ptr<Variable> variable)
335 335 {
336 336 qCDebug(LOG_VariableController()) << "TORM: VariableController::onAbortProgressRequested"
337 337 << QThread::currentThread()->objectName();
338 338
339 339 auto it = impl->m_VariableToIdentifierMap.find(variable);
340 340 if (it != impl->m_VariableToIdentifierMap.cend()) {
341 341 impl->m_VariableToProviderMap.at(variable)->requestDataAborting(it->second);
342 342 }
343 343 else {
344 344 qCWarning(LOG_VariableController())
345 345 << tr("Aborting progression of inexistant variable detected !!!")
346 346 << QThread::currentThread()->objectName();
347 347 }
348 348 }
349 349
350 350 void VariableController::onAddSynchronizationGroupId(QUuid synchronizationGroupId)
351 351 {
352 qCDebug(LOG_VariableController()) << "TORM: VariableController::onAddSynchronizationGroupId"
353 << QThread::currentThread()->objectName()
354 << synchronizationGroupId;
352 qCDebug(LOG_VariableController())
353 << "TORM: VariableController::onAddSynchronizationGroupId"
354 << QThread::currentThread()->objectName() << synchronizationGroupId;
355 355 auto vSynchroGroup = std::make_shared<VariableSynchronizationGroup>();
356 356 impl->m_GroupIdToVariableSynchronizationGroupMap.insert(
357 357 std::make_pair(synchronizationGroupId, vSynchroGroup));
358 358 }
359 359
360 360 void VariableController::onRemoveSynchronizationGroupId(QUuid synchronizationGroupId)
361 361 {
362 362 impl->m_GroupIdToVariableSynchronizationGroupMap.erase(synchronizationGroupId);
363 363 }
364 364
365 365 void VariableController::onAddSynchronized(std::shared_ptr<Variable> variable,
366 366 QUuid synchronizationGroupId)
367 367
368 368 {
369 qCDebug(LOG_VariableController()) << "TORM: VariableController::onAddSynchronized"
370 << synchronizationGroupId;
369 qCDebug(LOG_VariableController())
370 << "TORM: VariableController::onAddSynchronized" << synchronizationGroupId;
371 371 auto varToVarIdIt = impl->m_VariableToIdentifierMap.find(variable);
372 372 if (varToVarIdIt != impl->m_VariableToIdentifierMap.cend()) {
373 373 auto groupIdToVSGIt
374 374 = impl->m_GroupIdToVariableSynchronizationGroupMap.find(synchronizationGroupId);
375 375 if (groupIdToVSGIt != impl->m_GroupIdToVariableSynchronizationGroupMap.cend()) {
376 376 impl->m_VariableIdGroupIdMap.insert(
377 377 std::make_pair(varToVarIdIt->second, synchronizationGroupId));
378 378 groupIdToVSGIt->second->addVariableId(varToVarIdIt->second);
379 379 }
380 380 else {
381 381 qCCritical(LOG_VariableController())
382 382 << tr("Impossible to synchronize a variable with an unknown sycnhronization group")
383 383 << variable->name();
384 384 }
385 385 }
386 386 else {
387 387 qCCritical(LOG_VariableController())
388 388 << tr("Impossible to synchronize a variable with no identifier") << variable->name();
389 389 }
390 390 }
391 391
392 392 void VariableController::desynchronize(std::shared_ptr<Variable> variable,
393 393 QUuid synchronizationGroupId)
394 394 {
395 395 // Gets variable id
396 396 auto variableIt = impl->m_VariableToIdentifierMap.find(variable);
397 397 if (variableIt == impl->m_VariableToIdentifierMap.cend()) {
398 398 qCCritical(LOG_VariableController())
399 399 << tr("Can't desynchronize variable %1: variable identifier not found")
400 400 .arg(variable->name());
401 401 return;
402 402 }
403 403
404 404 // Gets synchronization group
405 405 auto groupIt = impl->m_GroupIdToVariableSynchronizationGroupMap.find(synchronizationGroupId);
406 406 if (groupIt == impl->m_GroupIdToVariableSynchronizationGroupMap.cend()) {
407 407 qCCritical(LOG_VariableController())
408 408 << tr("Can't desynchronize variable %1: unknown synchronization group")
409 409 .arg(variable->name());
410 410 return;
411 411 }
412 412
413 413 auto variableId = variableIt->second;
414 414
415 415 // Removes variable from synchronization group
416 416 auto synchronizationGroup = groupIt->second;
417 417 synchronizationGroup->removeVariableId(variableId);
418 418
419 419 // Removes link between variable and synchronization group
420 420 impl->m_VariableIdGroupIdMap.erase(variableId);
421 421 }
422 422
423 423 void VariableController::onRequestDataLoading(QVector<std::shared_ptr<Variable> > variables,
424 424 const SqpRange &range, const SqpRange &oldRange,
425 425 bool synchronise)
426 426 {
427 427 // NOTE: oldRange isn't really necessary since oldRange == variable->range().
428 428
429 429 // we want to load data of the variable for the dateTime.
430 430 // First we check if the cache contains some of them.
431 431 // For the other, we ask the provider to give them.
432 432
433 433 auto varRequestId = QUuid::createUuid();
434 434 qCInfo(LOG_VariableController()) << "VariableController::onRequestDataLoading"
435 435 << QThread::currentThread()->objectName() << varRequestId;
436 436
437 437 for (const auto &var : variables) {
438 438 qCDebug(LOG_VariableController()) << "processRequest for" << var->name() << varRequestId;
439 439 impl->processRequest(var, range, varRequestId);
440 440 }
441 441
442 442 if (synchronise) {
443 443 // Get the group ids
444 444 qCDebug(LOG_VariableController())
445 445 << "TORM VariableController::onRequestDataLoading for synchro var ENABLE";
446 446 auto groupIds = std::set<QUuid>{};
447 447 auto groupIdToOldRangeMap = std::map<QUuid, SqpRange>{};
448 448 for (const auto &var : variables) {
449 449 auto varToVarIdIt = impl->m_VariableToIdentifierMap.find(var);
450 450 if (varToVarIdIt != impl->m_VariableToIdentifierMap.cend()) {
451 451 auto vId = varToVarIdIt->second;
452 452 auto varIdToGroupIdIt = impl->m_VariableIdGroupIdMap.find(vId);
453 453 if (varIdToGroupIdIt != impl->m_VariableIdGroupIdMap.cend()) {
454 454 auto gId = varIdToGroupIdIt->second;
455 455 groupIdToOldRangeMap.insert(std::make_pair(gId, var->range()));
456 456 if (groupIds.find(gId) == groupIds.cend()) {
457 457 qCDebug(LOG_VariableController()) << "Synchro detect group " << gId;
458 458 groupIds.insert(gId);
459 459 }
460 460 }
461 461 }
462 462 }
463 463
464 464 // We assume here all group ids exist
465 465 for (const auto &gId : groupIds) {
466 466 auto vSynchronizationGroup = impl->m_GroupIdToVariableSynchronizationGroupMap.at(gId);
467 467 auto vSyncIds = vSynchronizationGroup->getIds();
468 468 qCDebug(LOG_VariableController()) << "Var in synchro group ";
469 469 for (auto vId : vSyncIds) {
470 470 auto var = impl->findVariable(vId);
471 471
472 472 // Don't process already processed var
473 473 if (!variables.contains(var)) {
474 474 if (var != nullptr) {
475 qCDebug(LOG_VariableController()) << "processRequest synchro for"
476 << var->name();
475 qCDebug(LOG_VariableController())
476 << "processRequest synchro for" << var->name();
477 477 auto vSyncRangeRequested = computeSynchroRangeRequested(
478 478 var->range(), range, groupIdToOldRangeMap.at(gId));
479 479 qCDebug(LOG_VariableController()) << "synchro RR" << vSyncRangeRequested;
480 480 impl->processRequest(var, vSyncRangeRequested, varRequestId);
481 481 }
482 482 else {
483 483 qCCritical(LOG_VariableController())
484 484
485 485 << tr("Impossible to synchronize a null variable");
486 486 }
487 487 }
488 488 }
489 489 }
490 490 }
491 491
492 492 impl->updateVariableRequest(varRequestId);
493 493 }
494 494
495 495
496 496 void VariableController::initialize()
497 497 {
498 498 qCDebug(LOG_VariableController()) << tr("VariableController init") << QThread::currentThread();
499 499 impl->m_WorkingMutex.lock();
500 500 qCDebug(LOG_VariableController()) << tr("VariableController init END");
501 501 }
502 502
503 503 void VariableController::finalize()
504 504 {
505 505 impl->m_WorkingMutex.unlock();
506 506 }
507 507
508 508 void VariableController::waitForFinish()
509 509 {
510 510 QMutexLocker locker{&impl->m_WorkingMutex};
511 511 }
512 512
513 513 AcquisitionZoomType VariableController::getZoomType(const SqpRange &range, const SqpRange &oldRange)
514 514 {
515 515 // t1.m_TStart <= t2.m_TStart && t2.m_TEnd <= t1.m_TEnd
516 516 auto zoomType = AcquisitionZoomType::Unknown;
517 517 if (range.m_TStart <= oldRange.m_TStart && oldRange.m_TEnd <= range.m_TEnd) {
518 518 zoomType = AcquisitionZoomType::ZoomOut;
519 519 }
520 520 else if (range.m_TStart > oldRange.m_TStart && range.m_TEnd > oldRange.m_TEnd) {
521 521 zoomType = AcquisitionZoomType::PanRight;
522 522 }
523 523 else if (range.m_TStart < oldRange.m_TStart && range.m_TEnd < oldRange.m_TEnd) {
524 524 zoomType = AcquisitionZoomType::PanLeft;
525 525 }
526 526 else if (range.m_TStart > oldRange.m_TStart && oldRange.m_TEnd > range.m_TEnd) {
527 527 zoomType = AcquisitionZoomType::ZoomIn;
528 528 }
529 529 else {
530 530 qCCritical(LOG_VariableController()) << "getZoomType: Unknown type detected";
531 531 }
532 532 return zoomType;
533 533 }
534 534
535 535 void VariableController::VariableControllerPrivate::processRequest(std::shared_ptr<Variable> var,
536 536 const SqpRange &rangeRequested,
537 537 QUuid varRequestId)
538 538 {
539 539
540 540 // TODO: protect at
541 541 auto varRequest = VariableRequest{};
542 542 auto varId = m_VariableToIdentifierMap.at(var);
543 543
544 544 auto varStrategyRangesRequested
545 545 = m_VariableCacheStrategy->computeStrategyRanges(var->range(), rangeRequested);
546 546 auto notInCacheRangeList = var->provideNotInCacheRangeList(varStrategyRangesRequested.second);
547 547 auto inCacheRangeList = var->provideInCacheRangeList(varStrategyRangesRequested.second);
548 548
549 549 if (!notInCacheRangeList.empty()) {
550 550 varRequest.m_RangeRequested = varStrategyRangesRequested.first;
551 551 varRequest.m_CacheRangeRequested = varStrategyRangesRequested.second;
552 552 qCDebug(LOG_VariableAcquisitionWorker()) << tr("TORM processRequest RR ") << rangeRequested;
553 qCDebug(LOG_VariableAcquisitionWorker()) << tr("TORM processRequest R ")
554 << varStrategyRangesRequested.first;
555 qCDebug(LOG_VariableAcquisitionWorker()) << tr("TORM processRequest CR ")
556 << varStrategyRangesRequested.second;
553 qCDebug(LOG_VariableAcquisitionWorker())
554 << tr("TORM processRequest R ") << varStrategyRangesRequested.first;
555 qCDebug(LOG_VariableAcquisitionWorker())
556 << tr("TORM processRequest CR ") << varStrategyRangesRequested.second;
557 557 // store VarRequest
558 558 storeVariableRequest(varId, varRequestId, varRequest);
559 559
560 560 auto varProvider = m_VariableToProviderMap.at(var);
561 561 if (varProvider != nullptr) {
562 562 auto varRequestIdCanceled = m_VariableAcquisitionWorker->pushVariableRequest(
563 563 varRequestId, varId, varStrategyRangesRequested.first,
564 564 varStrategyRangesRequested.second,
565 565 DataProviderParameters{std::move(notInCacheRangeList), var->metadata()},
566 566 varProvider);
567 567
568 568 if (!varRequestIdCanceled.isNull()) {
569 qCInfo(LOG_VariableAcquisitionWorker()) << tr("varRequestIdCanceled: ")
570 << varRequestIdCanceled;
569 qCInfo(LOG_VariableAcquisitionWorker())
570 << tr("varRequestIdCanceled: ") << varRequestIdCanceled;
571 571 cancelVariableRequest(varRequestIdCanceled);
572 572 }
573 573 }
574 574 else {
575 575 qCCritical(LOG_VariableController())
576 576 << "Impossible to provide data with a null provider";
577 577 }
578 578
579 579 if (!inCacheRangeList.empty()) {
580 580 emit q->updateVarDisplaying(var, inCacheRangeList.first());
581 581 }
582 582 }
583 583 else {
584 584
585 585 varRequest.m_RangeRequested = varStrategyRangesRequested.first;
586 586 varRequest.m_CacheRangeRequested = varStrategyRangesRequested.second;
587 587 // store VarRequest
588 588 storeVariableRequest(varId, varRequestId, varRequest);
589 589 acceptVariableRequest(varId,
590 590 var->dataSeries()->subDataSeries(varStrategyRangesRequested.second));
591 591 }
592 592 }
593 593
594 594 std::shared_ptr<Variable>
595 595 VariableController::VariableControllerPrivate::findVariable(QUuid vIdentifier)
596 596 {
597 597 std::shared_ptr<Variable> var;
598 598 auto findReply = [vIdentifier](const auto &entry) { return vIdentifier == entry.second; };
599 599
600 600 auto end = m_VariableToIdentifierMap.cend();
601 601 auto it = std::find_if(m_VariableToIdentifierMap.cbegin(), end, findReply);
602 602 if (it != end) {
603 603 var = it->first;
604 604 }
605 605 else {
606 606 qCCritical(LOG_VariableController())
607 607 << tr("Impossible to find the variable with the identifier: ") << vIdentifier;
608 608 }
609 609
610 610 return var;
611 611 }
612 612
613 613 std::shared_ptr<IDataSeries> VariableController::VariableControllerPrivate::retrieveDataSeries(
614 614 const QVector<AcquisitionDataPacket> acqDataPacketVector)
615 615 {
616 qCDebug(LOG_VariableController()) << tr("TORM: retrieveDataSeries acqDataPacketVector size")
617 << acqDataPacketVector.size();
616 qCDebug(LOG_VariableController())
617 << tr("TORM: retrieveDataSeries acqDataPacketVector size") << acqDataPacketVector.size();
618 618 std::shared_ptr<IDataSeries> dataSeries;
619 619 if (!acqDataPacketVector.isEmpty()) {
620 620 dataSeries = acqDataPacketVector[0].m_DateSeries;
621 621 for (int i = 1; i < acqDataPacketVector.size(); ++i) {
622 622 dataSeries->merge(acqDataPacketVector[i].m_DateSeries.get());
623 623 }
624 624 }
625 625 qCDebug(LOG_VariableController()) << tr("TORM: retrieveDataSeries acqDataPacketVector size END")
626 626 << acqDataPacketVector.size();
627 627 return dataSeries;
628 628 }
629 629
630 630 void VariableController::VariableControllerPrivate::registerProvider(
631 631 std::shared_ptr<IDataProvider> provider)
632 632 {
633 633 if (m_ProviderSet.find(provider) == m_ProviderSet.end()) {
634 qCDebug(LOG_VariableController()) << tr("Registering of a new provider")
635 << provider->objectName();
634 qCDebug(LOG_VariableController())
635 << tr("Registering of a new provider") << provider->objectName();
636 636 m_ProviderSet.insert(provider);
637 637 connect(provider.get(), &IDataProvider::dataProvided, m_VariableAcquisitionWorker.get(),
638 638 &VariableAcquisitionWorker::onVariableDataAcquired);
639 639 connect(provider.get(), &IDataProvider::dataProvidedProgress,
640 640 m_VariableAcquisitionWorker.get(),
641 641 &VariableAcquisitionWorker::onVariableRetrieveDataInProgress);
642 642 }
643 643 else {
644 644 qCDebug(LOG_VariableController()) << tr("Cannot register provider, it already exists ");
645 645 }
646 646 }
647 647
648 648 void VariableController::VariableControllerPrivate::storeVariableRequest(
649 649 QUuid varId, QUuid varRequestId, const VariableRequest &varRequest)
650 650 {
651 651 // First request for the variable. we can create an entry for it
652 652 auto varIdToVarRequestIdQueueMapIt = m_VarIdToVarRequestIdQueueMap.find(varId);
653 653 if (varIdToVarRequestIdQueueMapIt == m_VarIdToVarRequestIdQueueMap.cend()) {
654 654 auto varRequestIdQueue = std::deque<QUuid>{};
655 655 qCDebug(LOG_VariableController()) << tr("Store REQUEST in QUEUE");
656 656 varRequestIdQueue.push_back(varRequestId);
657 657 m_VarIdToVarRequestIdQueueMap.insert(std::make_pair(varId, std::move(varRequestIdQueue)));
658 658 }
659 659 else {
660 660 qCDebug(LOG_VariableController()) << tr("Store REQUEST in EXISTING QUEUE");
661 661 auto &varRequestIdQueue = varIdToVarRequestIdQueueMapIt->second;
662 662 varRequestIdQueue.push_back(varRequestId);
663 663 }
664 664
665 665 auto varRequestIdToVarIdVarRequestMapIt = m_VarRequestIdToVarIdVarRequestMap.find(varRequestId);
666 666 if (varRequestIdToVarIdVarRequestMapIt == m_VarRequestIdToVarIdVarRequestMap.cend()) {
667 667 auto varIdToVarRequestMap = std::map<QUuid, VariableRequest>{};
668 668 varIdToVarRequestMap.insert(std::make_pair(varId, varRequest));
669 669 qCDebug(LOG_VariableController()) << tr("Store REQUESTID in MAP");
670 670 m_VarRequestIdToVarIdVarRequestMap.insert(
671 671 std::make_pair(varRequestId, std::move(varIdToVarRequestMap)));
672 672 }
673 673 else {
674 674 auto &varIdToVarRequestMap = varRequestIdToVarIdVarRequestMapIt->second;
675 675 qCDebug(LOG_VariableController()) << tr("Store REQUESTID in EXISTING MAP");
676 676 varIdToVarRequestMap.insert(std::make_pair(varId, varRequest));
677 677 }
678 678 }
679 679
680 680 QUuid VariableController::VariableControllerPrivate::acceptVariableRequest(
681 681 QUuid varId, std::shared_ptr<IDataSeries> dataSeries)
682 682 {
683 683 QUuid varRequestId;
684 684 auto varIdToVarRequestIdQueueMapIt = m_VarIdToVarRequestIdQueueMap.find(varId);
685 685 if (varIdToVarRequestIdQueueMapIt != m_VarIdToVarRequestIdQueueMap.cend()) {
686 686 auto &varRequestIdQueue = varIdToVarRequestIdQueueMapIt->second;
687 687 varRequestId = varRequestIdQueue.front();
688 688 auto varRequestIdToVarIdVarRequestMapIt
689 689 = m_VarRequestIdToVarIdVarRequestMap.find(varRequestId);
690 690 if (varRequestIdToVarIdVarRequestMapIt != m_VarRequestIdToVarIdVarRequestMap.cend()) {
691 691 auto &varIdToVarRequestMap = varRequestIdToVarIdVarRequestMapIt->second;
692 692 auto varIdToVarRequestMapIt = varIdToVarRequestMap.find(varId);
693 693 if (varIdToVarRequestMapIt != varIdToVarRequestMap.cend()) {
694 694 qCDebug(LOG_VariableController()) << tr("acceptVariableRequest");
695 695 auto &varRequest = varIdToVarRequestMapIt->second;
696 696 varRequest.m_DataSeries = dataSeries;
697 697 varRequest.m_CanUpdate = true;
698 698 }
699 699 else {
700 700 qCDebug(LOG_VariableController())
701 701 << tr("Impossible to acceptVariableRequest of a unknown variable id attached "
702 702 "to a variableRequestId")
703 703 << varRequestId << varId;
704 704 }
705 705 }
706 706 else {
707 707 qCCritical(LOG_VariableController())
708 708 << tr("Impossible to acceptVariableRequest of a unknown variableRequestId")
709 709 << varRequestId;
710 710 }
711 711
712 qCDebug(LOG_VariableController()) << tr("1: erase REQUEST in QUEUE ?")
713 << varRequestIdQueue.size();
712 qCDebug(LOG_VariableController())
713 << tr("1: erase REQUEST in QUEUE ?") << varRequestIdQueue.size();
714 714 varRequestIdQueue.pop_front();
715 qCDebug(LOG_VariableController()) << tr("2: erase REQUEST in QUEUE ?")
716 << varRequestIdQueue.size();
715 qCDebug(LOG_VariableController())
716 << tr("2: erase REQUEST in QUEUE ?") << varRequestIdQueue.size();
717 717 if (varRequestIdQueue.empty()) {
718 718 m_VarIdToVarRequestIdQueueMap.erase(varId);
719 719 }
720 720 }
721 721 else {
722 722 qCCritical(LOG_VariableController())
723 723 << tr("Impossible to acceptVariableRequest of a unknown variable id") << varId;
724 724 }
725 725
726 726 return varRequestId;
727 727 }
728 728
729 729 void VariableController::VariableControllerPrivate::updateVariableRequest(QUuid varRequestId)
730 730 {
731 731
732 732 auto varRequestIdToVarIdVarRequestMapIt = m_VarRequestIdToVarIdVarRequestMap.find(varRequestId);
733 733 if (varRequestIdToVarIdVarRequestMapIt != m_VarRequestIdToVarIdVarRequestMap.cend()) {
734 734 bool processVariableUpdate = true;
735 735 auto &varIdToVarRequestMap = varRequestIdToVarIdVarRequestMapIt->second;
736 736 for (auto varIdToVarRequestMapIt = varIdToVarRequestMap.cbegin();
737 737 (varIdToVarRequestMapIt != varIdToVarRequestMap.cend()) && processVariableUpdate;
738 738 ++varIdToVarRequestMapIt) {
739 739 processVariableUpdate &= varIdToVarRequestMapIt->second.m_CanUpdate;
740 qCDebug(LOG_VariableController()) << tr("updateVariableRequest")
741 << processVariableUpdate;
740 qCDebug(LOG_VariableController())
741 << tr("updateVariableRequest") << processVariableUpdate;
742 742 }
743 743
744 744 if (processVariableUpdate) {
745 745 for (auto varIdToVarRequestMapIt = varIdToVarRequestMap.cbegin();
746 746 varIdToVarRequestMapIt != varIdToVarRequestMap.cend(); ++varIdToVarRequestMapIt) {
747 747 if (auto var = findVariable(varIdToVarRequestMapIt->first)) {
748 748 auto &varRequest = varIdToVarRequestMapIt->second;
749 749 var->setRange(varRequest.m_RangeRequested);
750 750 var->setCacheRange(varRequest.m_CacheRangeRequested);
751 qCDebug(LOG_VariableController()) << tr("1: onDataProvided")
752 << varRequest.m_RangeRequested;
753 qCDebug(LOG_VariableController()) << tr("2: onDataProvided")
754 << varRequest.m_CacheRangeRequested;
751 qCDebug(LOG_VariableController())
752 << tr("1: onDataProvided") << varRequest.m_RangeRequested;
753 qCDebug(LOG_VariableController())
754 << tr("2: onDataProvided") << varRequest.m_CacheRangeRequested;
755 755 var->mergeDataSeries(varRequest.m_DataSeries);
756 qCDebug(LOG_VariableController()) << tr("3: onDataProvided")
757 << varRequest.m_DataSeries->range();
756 qCDebug(LOG_VariableController())
757 << tr("3: onDataProvided") << varRequest.m_DataSeries->range();
758 758 qCDebug(LOG_VariableController()) << tr("4: onDataProvided");
759 759
760 760 /// @todo MPL: confirm
761 761 // Variable update is notified only if there is no pending request for it
762 762 if (m_VarIdToVarRequestIdQueueMap.count(varIdToVarRequestMapIt->first) == 0) {
763 763 emit var->updated();
764 764 }
765 765 }
766 766 else {
767 767 qCCritical(LOG_VariableController())
768 768 << tr("Impossible to update data to a null variable");
769 769 }
770 770 }
771 771
772 772 // cleaning varRequestId
773 qCDebug(LOG_VariableController()) << tr("0: erase REQUEST in MAP ?")
774 << m_VarRequestIdToVarIdVarRequestMap.size();
773 qCDebug(LOG_VariableController())
774 << tr("0: erase REQUEST in MAP ?") << m_VarRequestIdToVarIdVarRequestMap.size();
775 775 m_VarRequestIdToVarIdVarRequestMap.erase(varRequestId);
776 qCDebug(LOG_VariableController()) << tr("1: erase REQUEST in MAP ?")
777 << m_VarRequestIdToVarIdVarRequestMap.size();
776 qCDebug(LOG_VariableController())
777 << tr("1: erase REQUEST in MAP ?") << m_VarRequestIdToVarIdVarRequestMap.size();
778 778 }
779 779 }
780 780 else {
781 781 qCCritical(LOG_VariableController())
782 782 << tr("Cannot updateVariableRequest for a unknow varRequestId") << varRequestId;
783 783 }
784 784 }
785 785
786 786 void VariableController::VariableControllerPrivate::cancelVariableRequest(QUuid varRequestId)
787 787 {
788 788 // cleaning varRequestId
789 789 m_VarRequestIdToVarIdVarRequestMap.erase(varRequestId);
790 790
791 791 for (auto varIdToVarRequestIdQueueMapIt = m_VarIdToVarRequestIdQueueMap.begin();
792 792 varIdToVarRequestIdQueueMapIt != m_VarIdToVarRequestIdQueueMap.end();) {
793 793 auto &varRequestIdQueue = varIdToVarRequestIdQueueMapIt->second;
794 794 varRequestIdQueue.erase(
795 795 std::remove(varRequestIdQueue.begin(), varRequestIdQueue.end(), varRequestId),
796 796 varRequestIdQueue.end());
797 797 if (varRequestIdQueue.empty()) {
798 798 varIdToVarRequestIdQueueMapIt
799 799 = m_VarIdToVarRequestIdQueueMap.erase(varIdToVarRequestIdQueueMapIt);
800 800 }
801 801 else {
802 802 ++varIdToVarRequestIdQueueMapIt;
803 803 }
804 804 }
805 805 }
@@ -1,48 +1,48
1 1 #include <Visualization/VisualizationController.h>
2 2
3 3 #include <Variable/Variable.h>
4 4
5 5 #include <QMutex>
6 6 #include <QThread>
7 7
8 8 #include <QDir>
9 9 #include <QStandardPaths>
10 10
11 11 Q_LOGGING_CATEGORY(LOG_VisualizationController, "VisualizationController")
12 12
13 13 class VisualizationController::VisualizationControllerPrivate {
14 14 public:
15 15 QMutex m_WorkingMutex;
16 16 };
17 17
18 18 VisualizationController::VisualizationController(QObject *parent)
19 19 : impl{spimpl::make_unique_impl<VisualizationControllerPrivate>()}
20 20 {
21 qCDebug(LOG_VisualizationController()) << tr("VisualizationController construction")
22 << QThread::currentThread();
21 qCDebug(LOG_VisualizationController())
22 << tr("VisualizationController construction") << QThread::currentThread();
23 23 }
24 24
25 25 VisualizationController::~VisualizationController()
26 26 {
27 qCDebug(LOG_VisualizationController()) << tr("VisualizationController destruction")
28 << QThread::currentThread();
27 qCDebug(LOG_VisualizationController())
28 << tr("VisualizationController destruction") << QThread::currentThread();
29 29 this->waitForFinish();
30 30 }
31 31
32 32 void VisualizationController::initialize()
33 33 {
34 qCDebug(LOG_VisualizationController()) << tr("VisualizationController init")
35 << QThread::currentThread();
34 qCDebug(LOG_VisualizationController())
35 << tr("VisualizationController init") << QThread::currentThread();
36 36 impl->m_WorkingMutex.lock();
37 37 qCDebug(LOG_VisualizationController()) << tr("VisualizationController init END");
38 38 }
39 39
40 40 void VisualizationController::finalize()
41 41 {
42 42 impl->m_WorkingMutex.unlock();
43 43 }
44 44
45 45 void VisualizationController::waitForFinish()
46 46 {
47 47 QMutexLocker locker{&impl->m_WorkingMutex};
48 48 }
@@ -1,636 +1,636
1 1 #include "Data/DataSeries.h"
2 2 #include "Data/ScalarSeries.h"
3 3 #include "Data/VectorSeries.h"
4 4
5 5 #include <cmath>
6 6
7 7 #include <QObject>
8 8 #include <QtTest>
9 9
10 10 Q_DECLARE_METATYPE(std::shared_ptr<ScalarSeries>)
11 11 Q_DECLARE_METATYPE(std::shared_ptr<VectorSeries>)
12 12
13 13 namespace {
14 14
15 15 using DataContainer = std::vector<double>;
16 16
17 17 void validateRange(DataSeriesIterator first, DataSeriesIterator last, const DataContainer &xData,
18 18 const DataContainer &valuesData)
19 19 {
20 20 QVERIFY(std::equal(first, last, xData.cbegin(), xData.cend(),
21 21 [](const auto &it, const auto &expectedX) { return it.x() == expectedX; }));
22 22 QVERIFY(std::equal(
23 23 first, last, valuesData.cbegin(), valuesData.cend(),
24 24 [](const auto &it, const auto &expectedVal) { return it.value() == expectedVal; }));
25 25 }
26 26
27 27 void validateRange(DataSeriesIterator first, DataSeriesIterator last, const DataContainer &xData,
28 28 const std::vector<DataContainer> &valuesData)
29 29 {
30 30 QVERIFY(std::equal(first, last, xData.cbegin(), xData.cend(),
31 31 [](const auto &it, const auto &expectedX) { return it.x() == expectedX; }));
32 32 for (auto i = 0; i < valuesData.size(); ++i) {
33 33 auto componentData = valuesData.at(i);
34 34
35 35 QVERIFY(std::equal(
36 36 first, last, componentData.cbegin(), componentData.cend(),
37 37 [i](const auto &it, const auto &expectedVal) { return it.value(i) == expectedVal; }));
38 38 }
39 39 }
40 40
41 41 } // namespace
42 42
43 43 class TestDataSeries : public QObject {
44 44 Q_OBJECT
45 45 private:
46 46 template <typename T>
47 47 void testValuesBoundsStructure()
48 48 {
49 49 // ////////////// //
50 50 // Test structure //
51 51 // ////////////// //
52 52
53 53 // Data series to get values bounds
54 54 QTest::addColumn<std::shared_ptr<T> >("dataSeries");
55 55
56 56 // x-axis range
57 57 QTest::addColumn<double>("minXAxis");
58 58 QTest::addColumn<double>("maxXAxis");
59 59
60 60 // Expected results
61 61 QTest::addColumn<bool>(
62 62 "expectedOK"); // Test is expected to be ok (i.e. method doesn't return end iterators)
63 63 QTest::addColumn<double>("expectedMinValue");
64 64 QTest::addColumn<double>("expectedMaxValue");
65 65 }
66 66
67 67 template <typename T>
68 68 void testValuesBounds()
69 69 {
70 70 QFETCH(std::shared_ptr<T>, dataSeries);
71 71 QFETCH(double, minXAxis);
72 72 QFETCH(double, maxXAxis);
73 73
74 74 QFETCH(bool, expectedOK);
75 75 QFETCH(double, expectedMinValue);
76 76 QFETCH(double, expectedMaxValue);
77 77
78 78 auto minMaxIts = dataSeries->valuesBounds(minXAxis, maxXAxis);
79 79 auto end = dataSeries->cend();
80 80
81 81 // Checks iterators with expected result
82 82 QCOMPARE(expectedOK, minMaxIts.first != end && minMaxIts.second != end);
83 83
84 84 if (expectedOK) {
85 85 auto compare = [](const auto &v1, const auto &v2) {
86 86 return (std::isnan(v1) && std::isnan(v2)) || v1 == v2;
87 87 };
88 88
89 89 QVERIFY(compare(expectedMinValue, minMaxIts.first->minValue()));
90 90 QVERIFY(compare(expectedMaxValue, minMaxIts.second->maxValue()));
91 91 }
92 92 }
93 93
94 94 template <typename T>
95 95 void testPurgeStructure()
96 96 {
97 97 // ////////////// //
98 98 // Test structure //
99 99 // ////////////// //
100 100
101 101 // Data series to purge
102 102 QTest::addColumn<std::shared_ptr<T> >("dataSeries");
103 103 QTest::addColumn<double>("min");
104 104 QTest::addColumn<double>("max");
105 105
106 106 // Expected values after purge
107 107 QTest::addColumn<DataContainer>("expectedXAxisData");
108 108 QTest::addColumn<std::vector<DataContainer> >("expectedValuesData");
109 109 }
110 110
111 111 template <typename T>
112 112 void testPurge()
113 113 {
114 114 QFETCH(std::shared_ptr<T>, dataSeries);
115 115 QFETCH(double, min);
116 116 QFETCH(double, max);
117 117
118 118 dataSeries->purge(min, max);
119 119
120 120 // Validates results
121 121 QFETCH(DataContainer, expectedXAxisData);
122 122 QFETCH(std::vector<DataContainer>, expectedValuesData);
123 123
124 124 validateRange(dataSeries->cbegin(), dataSeries->cend(), expectedXAxisData,
125 125 expectedValuesData);
126 126 }
127 127
128 128 private slots:
129 129
130 130 /// Input test data
131 131 /// @sa testCtor()
132 132 void testCtor_data();
133 133
134 134 /// Tests construction of a data series
135 135 void testCtor();
136 136
137 137 /// Input test data
138 138 /// @sa testMerge()
139 139 void testMerge_data();
140 140
141 141 /// Tests merge of two data series
142 142 void testMerge();
143 143
144 144 /// Input test data
145 145 /// @sa testPurgeScalar()
146 146 void testPurgeScalar_data();
147 147
148 148 /// Tests purge of a scalar series
149 149 void testPurgeScalar();
150 150
151 151 /// Input test data
152 152 /// @sa testPurgeVector()
153 153 void testPurgeVector_data();
154 154
155 155 /// Tests purge of a vector series
156 156 void testPurgeVector();
157 157
158 158 /// Input test data
159 159 /// @sa testMinXAxisData()
160 160 void testMinXAxisData_data();
161 161
162 162 /// Tests get min x-axis data of a data series
163 163 void testMinXAxisData();
164 164
165 165 /// Input test data
166 166 /// @sa testMaxXAxisData()
167 167 void testMaxXAxisData_data();
168 168
169 169 /// Tests get max x-axis data of a data series
170 170 void testMaxXAxisData();
171 171
172 172 /// Input test data
173 173 /// @sa testXAxisRange()
174 174 void testXAxisRange_data();
175 175
176 176 /// Tests get x-axis range of a data series
177 177 void testXAxisRange();
178 178
179 179 /// Input test data
180 180 /// @sa testValuesBoundsScalar()
181 181 void testValuesBoundsScalar_data();
182 182
183 183 /// Tests get values bounds of a scalar series
184 184 void testValuesBoundsScalar();
185 185
186 186 /// Input test data
187 187 /// @sa testValuesBoundsVector()
188 188 void testValuesBoundsVector_data();
189 189
190 190 /// Tests get values bounds of a vector series
191 191 void testValuesBoundsVector();
192 192 };
193 193
194 194 void TestDataSeries::testCtor_data()
195 195 {
196 196 // ////////////// //
197 197 // Test structure //
198 198 // ////////////// //
199 199
200 200 // x-axis data
201 201 QTest::addColumn<DataContainer>("xAxisData");
202 202 // values data
203 203 QTest::addColumn<DataContainer>("valuesData");
204 204
205 205 // expected x-axis data
206 206 QTest::addColumn<DataContainer>("expectedXAxisData");
207 207 // expected values data
208 208 QTest::addColumn<DataContainer>("expectedValuesData");
209 209
210 210 // ////////// //
211 211 // Test cases //
212 212 // ////////// //
213 213
214 214 QTest::newRow("invalidData (different sizes of vectors)")
215 215 << DataContainer{1., 2., 3., 4., 5.} << DataContainer{100., 200., 300.} << DataContainer{}
216 216 << DataContainer{};
217 217
218 218 QTest::newRow("sortedData") << DataContainer{1., 2., 3., 4., 5.}
219 219 << DataContainer{100., 200., 300., 400., 500.}
220 220 << DataContainer{1., 2., 3., 4., 5.}
221 221 << DataContainer{100., 200., 300., 400., 500.};
222 222
223 223 QTest::newRow("unsortedData") << DataContainer{5., 4., 3., 2., 1.}
224 224 << DataContainer{100., 200., 300., 400., 500.}
225 225 << DataContainer{1., 2., 3., 4., 5.}
226 226 << DataContainer{500., 400., 300., 200., 100.};
227 227
228 228 QTest::newRow("unsortedData2")
229 229 << DataContainer{1., 4., 3., 5., 2.} << DataContainer{100., 200., 300., 400., 500.}
230 230 << DataContainer{1., 2., 3., 4., 5.} << DataContainer{100., 500., 300., 200., 400.};
231 231 }
232 232
233 233 void TestDataSeries::testCtor()
234 234 {
235 235 // Creates series
236 236 QFETCH(DataContainer, xAxisData);
237 237 QFETCH(DataContainer, valuesData);
238 238
239 239 auto series = std::make_shared<ScalarSeries>(std::move(xAxisData), std::move(valuesData),
240 240 Unit{}, Unit{});
241 241
242 242 // Validates results : we check that the data series is sorted on its x-axis data
243 243 QFETCH(DataContainer, expectedXAxisData);
244 244 QFETCH(DataContainer, expectedValuesData);
245 245
246 246 validateRange(series->cbegin(), series->cend(), expectedXAxisData, expectedValuesData);
247 247 }
248 248
249 249 namespace {
250 250
251 251 std::shared_ptr<ScalarSeries> createScalarSeries(DataContainer xAxisData, DataContainer valuesData)
252 252 {
253 253 return std::make_shared<ScalarSeries>(std::move(xAxisData), std::move(valuesData), Unit{},
254 254 Unit{});
255 255 }
256 256
257 257 std::shared_ptr<VectorSeries> createVectorSeries(DataContainer xAxisData, DataContainer xValuesData,
258 258 DataContainer yValuesData,
259 259 DataContainer zValuesData)
260 260 {
261 261 return std::make_shared<VectorSeries>(std::move(xAxisData), std::move(xValuesData),
262 262 std::move(yValuesData), std::move(zValuesData), Unit{},
263 263 Unit{});
264 264 }
265 265
266 266 } // namespace
267 267
268 268 void TestDataSeries::testMerge_data()
269 269 {
270 270 // ////////////// //
271 271 // Test structure //
272 272 // ////////////// //
273 273
274 274 // Data series to merge
275 275 QTest::addColumn<std::shared_ptr<ScalarSeries> >("dataSeries");
276 276 QTest::addColumn<std::shared_ptr<ScalarSeries> >("dataSeries2");
277 277
278 278 // Expected values in the first data series after merge
279 279 QTest::addColumn<DataContainer>("expectedXAxisData");
280 280 QTest::addColumn<DataContainer>("expectedValuesData");
281 281
282 282 // ////////// //
283 283 // Test cases //
284 284 // ////////// //
285 285
286 286 QTest::newRow("sortedMerge")
287 287 << createScalarSeries({1., 2., 3., 4., 5.}, {100., 200., 300., 400., 500.})
288 288 << createScalarSeries({6., 7., 8., 9., 10.}, {600., 700., 800., 900., 1000.})
289 289 << DataContainer{1., 2., 3., 4., 5., 6., 7., 8., 9., 10.}
290 290 << DataContainer{100., 200., 300., 400., 500., 600., 700., 800., 900., 1000.};
291 291
292 292 QTest::newRow("unsortedMerge")
293 293 << createScalarSeries({6., 7., 8., 9., 10.}, {600., 700., 800., 900., 1000.})
294 294 << createScalarSeries({1., 2., 3., 4., 5.}, {100., 200., 300., 400., 500.})
295 295 << DataContainer{1., 2., 3., 4., 5., 6., 7., 8., 9., 10.}
296 296 << DataContainer{100., 200., 300., 400., 500., 600., 700., 800., 900., 1000.};
297 297
298 298 QTest::newRow("unsortedMerge2 (merge not made because source is in the bounds of dest)")
299 299 << createScalarSeries({1., 2., 8., 9., 10}, {100., 200., 800., 900., 1000.})
300 300 << createScalarSeries({3., 4., 5., 6., 7.}, {300., 400., 500., 600., 700.})
301 301 << DataContainer{1., 2., 8., 9., 10.} << DataContainer{100., 200., 800., 900., 1000.};
302 302
303 303 QTest::newRow("unsortedMerge3")
304 304 << createScalarSeries({3., 4., 5., 7., 8}, {300., 400., 500., 700., 800.})
305 305 << createScalarSeries({1., 2., 3., 7., 10.}, {100., 200., 333., 777., 1000.})
306 306 << DataContainer{1., 2., 3., 4., 5., 7., 8., 10.}
307 307 << DataContainer{100., 200., 300., 400., 500., 700., 800., 1000.};
308 308 }
309 309
310 310 void TestDataSeries::testMerge()
311 311 {
312 312 // Merges series
313 313 QFETCH(std::shared_ptr<ScalarSeries>, dataSeries);
314 314 QFETCH(std::shared_ptr<ScalarSeries>, dataSeries2);
315 315
316 316 dataSeries->merge(dataSeries2.get());
317 317
318 318 // Validates results : we check that the merge is valid and the data series is sorted on its
319 319 // x-axis data
320 320 QFETCH(DataContainer, expectedXAxisData);
321 321 QFETCH(DataContainer, expectedValuesData);
322 322
323 323 validateRange(dataSeries->cbegin(), dataSeries->cend(), expectedXAxisData, expectedValuesData);
324 324 }
325 325
326 326 void TestDataSeries::testPurgeScalar_data()
327 327 {
328 328 testPurgeStructure<ScalarSeries>();
329 329
330 330 // ////////// //
331 331 // Test cases //
332 332 // ////////// //
333 333
334 334 QTest::newRow("purgeScalar") << createScalarSeries({1., 2., 3., 4., 5.},
335 335 {100., 200., 300., 400., 500.})
336 336 << 2. << 4. << DataContainer{2., 3., 4.}
337 337 << std::vector<DataContainer>{{200., 300., 400.}};
338 338 QTest::newRow("purgeScalar2") << createScalarSeries({1., 2., 3., 4., 5.},
339 339 {100., 200., 300., 400., 500.})
340 340 << 0. << 2.5 << DataContainer{1., 2.}
341 341 << std::vector<DataContainer>{{100., 200.}};
342 342 QTest::newRow("purgeScalar3") << createScalarSeries({1., 2., 3., 4., 5.},
343 343 {100., 200., 300., 400., 500.})
344 344 << 3.5 << 7. << DataContainer{4., 5.}
345 345 << std::vector<DataContainer>{{400., 500.}};
346 346 QTest::newRow("purgeScalar4") << createScalarSeries({1., 2., 3., 4., 5.},
347 347 {100., 200., 300., 400., 500.})
348 348 << 0. << 7. << DataContainer{1., 2., 3., 4., 5.}
349 349 << std::vector<DataContainer>{{100., 200., 300., 400., 500.}};
350 350 QTest::newRow("purgeScalar5") << createScalarSeries({1., 2., 3., 4., 5.},
351 351 {100., 200., 300., 400., 500.})
352 352 << 5.5 << 7. << DataContainer{} << std::vector<DataContainer>{{}};
353 353 }
354 354
355 355 void TestDataSeries::testPurgeScalar()
356 356 {
357 357 testPurge<ScalarSeries>();
358 358 }
359 359
360 360 void TestDataSeries::testPurgeVector_data()
361 361 {
362 362 testPurgeStructure<VectorSeries>();
363 363
364 364 // ////////// //
365 365 // Test cases //
366 366 // ////////// //
367 367
368 368 QTest::newRow("purgeVector") << createVectorSeries({1., 2., 3., 4., 5.}, {6., 7., 8., 9., 10.},
369 369 {11., 12., 13., 14., 15.},
370 370 {16., 17., 18., 19., 20.})
371 371 << 2. << 4. << DataContainer{2., 3., 4.}
372 372 << std::vector<DataContainer>{
373 373 {7., 8., 9.}, {12., 13., 14.}, {17., 18., 19.}};
374 374 }
375 375
376 376 void TestDataSeries::testPurgeVector()
377 377 {
378 378 testPurge<VectorSeries>();
379 379 }
380 380
381 381 void TestDataSeries::testMinXAxisData_data()
382 382 {
383 383 // ////////////// //
384 384 // Test structure //
385 385 // ////////////// //
386 386
387 387 // Data series to get min data
388 388 QTest::addColumn<std::shared_ptr<ScalarSeries> >("dataSeries");
389 389
390 390 // Min data
391 391 QTest::addColumn<double>("min");
392 392
393 393 // Expected results
394 394 QTest::addColumn<bool>(
395 395 "expectedOK"); // if true, expects to have a result (i.e. the iterator != end iterator)
396 396 QTest::addColumn<double>(
397 397 "expectedMin"); // Expected value when method doesn't return end iterator
398 398
399 399 // ////////// //
400 400 // Test cases //
401 401 // ////////// //
402 402
403 403 QTest::newRow("minData1") << createScalarSeries({1., 2., 3., 4., 5.},
404 404 {100., 200., 300., 400., 500.})
405 405 << 0. << true << 1.;
406 406 QTest::newRow("minData2") << createScalarSeries({1., 2., 3., 4., 5.},
407 407 {100., 200., 300., 400., 500.})
408 408 << 1. << true << 1.;
409 409 QTest::newRow("minData3") << createScalarSeries({1., 2., 3., 4., 5.},
410 410 {100., 200., 300., 400., 500.})
411 411 << 1.1 << true << 2.;
412 412 QTest::newRow("minData4") << createScalarSeries({1., 2., 3., 4., 5.},
413 413 {100., 200., 300., 400., 500.})
414 414 << 5. << true << 5.;
415 415 QTest::newRow("minData5") << createScalarSeries({1., 2., 3., 4., 5.},
416 416 {100., 200., 300., 400., 500.})
417 417 << 5.1 << false << std::numeric_limits<double>::quiet_NaN();
418 418 QTest::newRow("minData6") << createScalarSeries({}, {}) << 1.1 << false
419 419 << std::numeric_limits<double>::quiet_NaN();
420 420 }
421 421
422 422 void TestDataSeries::testMinXAxisData()
423 423 {
424 424 QFETCH(std::shared_ptr<ScalarSeries>, dataSeries);
425 425 QFETCH(double, min);
426 426
427 427 QFETCH(bool, expectedOK);
428 428 QFETCH(double, expectedMin);
429 429
430 430 auto it = dataSeries->minXAxisData(min);
431 431
432 432 QCOMPARE(expectedOK, it != dataSeries->cend());
433 433
434 434 // If the method doesn't return a end iterator, checks with expected value
435 435 if (expectedOK) {
436 436 QCOMPARE(expectedMin, it->x());
437 437 }
438 438 }
439 439
440 440 void TestDataSeries::testMaxXAxisData_data()
441 441 {
442 442 // ////////////// //
443 443 // Test structure //
444 444 // ////////////// //
445 445
446 446 // Data series to get max data
447 447 QTest::addColumn<std::shared_ptr<ScalarSeries> >("dataSeries");
448 448
449 449 // Max data
450 450 QTest::addColumn<double>("max");
451 451
452 452 // Expected results
453 453 QTest::addColumn<bool>(
454 454 "expectedOK"); // if true, expects to have a result (i.e. the iterator != end iterator)
455 455 QTest::addColumn<double>(
456 456 "expectedMax"); // Expected value when method doesn't return end iterator
457 457
458 458 // ////////// //
459 459 // Test cases //
460 460 // ////////// //
461 461
462 462 QTest::newRow("maxData1") << createScalarSeries({1., 2., 3., 4., 5.},
463 463 {100., 200., 300., 400., 500.})
464 464 << 6. << true << 5.;
465 465 QTest::newRow("maxData2") << createScalarSeries({1., 2., 3., 4., 5.},
466 466 {100., 200., 300., 400., 500.})
467 467 << 5. << true << 5.;
468 468 QTest::newRow("maxData3") << createScalarSeries({1., 2., 3., 4., 5.},
469 469 {100., 200., 300., 400., 500.})
470 470 << 4.9 << true << 4.;
471 471 QTest::newRow("maxData4") << createScalarSeries({1., 2., 3., 4., 5.},
472 472 {100., 200., 300., 400., 500.})
473 473 << 1.1 << true << 1.;
474 474 QTest::newRow("maxData5") << createScalarSeries({1., 2., 3., 4., 5.},
475 475 {100., 200., 300., 400., 500.})
476 476 << 1. << true << 1.;
477 477 QTest::newRow("maxData6") << createScalarSeries({}, {}) << 1.1 << false
478 478 << std::numeric_limits<double>::quiet_NaN();
479 479 }
480 480
481 481 void TestDataSeries::testMaxXAxisData()
482 482 {
483 483 QFETCH(std::shared_ptr<ScalarSeries>, dataSeries);
484 484 QFETCH(double, max);
485 485
486 486 QFETCH(bool, expectedOK);
487 487 QFETCH(double, expectedMax);
488 488
489 489 auto it = dataSeries->maxXAxisData(max);
490 490
491 491 QCOMPARE(expectedOK, it != dataSeries->cend());
492 492
493 493 // If the method doesn't return a end iterator, checks with expected value
494 494 if (expectedOK) {
495 495 QCOMPARE(expectedMax, it->x());
496 496 }
497 497 }
498 498
499 499 void TestDataSeries::testXAxisRange_data()
500 500 {
501 501 // ////////////// //
502 502 // Test structure //
503 503 // ////////////// //
504 504
505 505 // Data series to get x-axis range
506 506 QTest::addColumn<std::shared_ptr<ScalarSeries> >("dataSeries");
507 507
508 508 // Min/max values
509 509 QTest::addColumn<double>("min");
510 510 QTest::addColumn<double>("max");
511 511
512 512 // Expected values
513 513 QTest::addColumn<DataContainer>("expectedXAxisData");
514 514 QTest::addColumn<DataContainer>("expectedValuesData");
515 515
516 516 // ////////// //
517 517 // Test cases //
518 518 // ////////// //
519 519
520 520 QTest::newRow("xAxisRange1") << createScalarSeries({1., 2., 3., 4., 5.},
521 521 {100., 200., 300., 400., 500.})
522 522 << -1. << 3.2 << DataContainer{1., 2., 3.}
523 523 << DataContainer{100., 200., 300.};
524 524 QTest::newRow("xAxisRange2") << createScalarSeries({1., 2., 3., 4., 5.},
525 525 {100., 200., 300., 400., 500.})
526 526 << 1. << 4. << DataContainer{1., 2., 3., 4.}
527 527 << DataContainer{100., 200., 300., 400.};
528 528 QTest::newRow("xAxisRange3") << createScalarSeries({1., 2., 3., 4., 5.},
529 529 {100., 200., 300., 400., 500.})
530 530 << 1. << 3.9 << DataContainer{1., 2., 3.}
531 531 << DataContainer{100., 200., 300.};
532 532 QTest::newRow("xAxisRange4") << createScalarSeries({1., 2., 3., 4., 5.},
533 533 {100., 200., 300., 400., 500.})
534 534 << 0. << 0.9 << DataContainer{} << DataContainer{};
535 535 QTest::newRow("xAxisRange5") << createScalarSeries({1., 2., 3., 4., 5.},
536 536 {100., 200., 300., 400., 500.})
537 537 << 0. << 1. << DataContainer{1.} << DataContainer{100.};
538 538 QTest::newRow("xAxisRange6") << createScalarSeries({1., 2., 3., 4., 5.},
539 539 {100., 200., 300., 400., 500.})
540 540 << 2.1 << 6. << DataContainer{3., 4., 5.}
541 541 << DataContainer{300., 400., 500.};
542 542 QTest::newRow("xAxisRange7") << createScalarSeries({1., 2., 3., 4., 5.},
543 543 {100., 200., 300., 400., 500.})
544 544 << 6. << 9. << DataContainer{} << DataContainer{};
545 545 QTest::newRow("xAxisRange8") << createScalarSeries({1., 2., 3., 4., 5.},
546 546 {100., 200., 300., 400., 500.})
547 547 << 5. << 9. << DataContainer{5.} << DataContainer{500.};
548 548 }
549 549
550 550 void TestDataSeries::testXAxisRange()
551 551 {
552 552 QFETCH(std::shared_ptr<ScalarSeries>, dataSeries);
553 553 QFETCH(double, min);
554 554 QFETCH(double, max);
555 555
556 556 QFETCH(DataContainer, expectedXAxisData);
557 557 QFETCH(DataContainer, expectedValuesData);
558 558
559 559 auto bounds = dataSeries->xAxisRange(min, max);
560 560 validateRange(bounds.first, bounds.second, expectedXAxisData, expectedValuesData);
561 561 }
562 562
563 563 void TestDataSeries::testValuesBoundsScalar_data()
564 564 {
565 565 testValuesBoundsStructure<ScalarSeries>();
566 566
567 567 // ////////// //
568 568 // Test cases //
569 569 // ////////// //
570 570 auto nan = std::numeric_limits<double>::quiet_NaN();
571 571
572 572 QTest::newRow("scalarBounds1")
573 573 << createScalarSeries({1., 2., 3., 4., 5.}, {100., 200., 300., 400., 500.}) << 0. << 6.
574 574 << true << 100. << 500.;
575 575 QTest::newRow("scalarBounds2")
576 576 << createScalarSeries({1., 2., 3., 4., 5.}, {100., 200., 300., 400., 500.}) << 2. << 4.
577 577 << true << 200. << 400.;
578 578 QTest::newRow("scalarBounds3")
579 579 << createScalarSeries({1., 2., 3., 4., 5.}, {100., 200., 300., 400., 500.}) << 0. << 0.5
580 580 << false << nan << nan;
581 581 QTest::newRow("scalarBounds4")
582 582 << createScalarSeries({1., 2., 3., 4., 5.}, {100., 200., 300., 400., 500.}) << 5.1 << 6.
583 583 << false << nan << nan;
584 QTest::newRow("scalarBounds5") << createScalarSeries({1.}, {100.}) << 0. << 2. << true << 100.
585 << 100.;
584 QTest::newRow("scalarBounds5")
585 << createScalarSeries({1.}, {100.}) << 0. << 2. << true << 100. << 100.;
586 586 QTest::newRow("scalarBounds6") << createScalarSeries({}, {}) << 0. << 2. << false << nan << nan;
587 587
588 588 // Tests with NaN values: NaN values are not included in min/max search
589 589 QTest::newRow("scalarBounds7")
590 590 << createScalarSeries({1., 2., 3., 4., 5.}, {nan, 200., 300., 400., nan}) << 0. << 6.
591 591 << true << 200. << 400.;
592 592 QTest::newRow("scalarBounds8")
593 593 << createScalarSeries({1., 2., 3., 4., 5.}, {nan, nan, nan, nan, nan}) << 0. << 6. << true
594 594 << std::numeric_limits<double>::quiet_NaN() << std::numeric_limits<double>::quiet_NaN();
595 595 }
596 596
597 597 void TestDataSeries::testValuesBoundsScalar()
598 598 {
599 599 testValuesBounds<ScalarSeries>();
600 600 }
601 601
602 602 void TestDataSeries::testValuesBoundsVector_data()
603 603 {
604 604 testValuesBoundsStructure<VectorSeries>();
605 605
606 606 // ////////// //
607 607 // Test cases //
608 608 // ////////// //
609 609 auto nan = std::numeric_limits<double>::quiet_NaN();
610 610
611 611 QTest::newRow("vectorBounds1")
612 612 << createVectorSeries({1., 2., 3., 4., 5.}, {10., 15., 20., 13., 12.},
613 613 {35., 24., 10., 9., 0.3}, {13., 14., 12., 9., 24.})
614 614 << 0. << 6. << true << 0.3 << 35.; // min/max in same component
615 615 QTest::newRow("vectorBounds2")
616 616 << createVectorSeries({1., 2., 3., 4., 5.}, {2.3, 15., 20., 13., 12.},
617 617 {35., 24., 10., 9., 4.}, {13., 14., 12., 9., 24.})
618 618 << 0. << 6. << true << 2.3 << 35.; // min/max in same entry
619 619 QTest::newRow("vectorBounds3")
620 620 << createVectorSeries({1., 2., 3., 4., 5.}, {2.3, 15., 20., 13., 12.},
621 621 {35., 24., 10., 9., 4.}, {13., 14., 12., 9., 24.})
622 622 << 2. << 3. << true << 10. << 24.;
623 623
624 624 // Tests with NaN values: NaN values are not included in min/max search
625 625 QTest::newRow("vectorBounds4")
626 626 << createVectorSeries({1., 2.}, {nan, nan}, {nan, nan}, {nan, nan}) << 0. << 6. << true
627 627 << nan << nan;
628 628 }
629 629
630 630 void TestDataSeries::testValuesBoundsVector()
631 631 {
632 632 testValuesBounds<VectorSeries>();
633 633 }
634 634
635 635 QTEST_MAIN(TestDataSeries)
636 636 #include "TestDataSeries.moc"
@@ -1,356 +1,356
1 1 #include "Visualization/VisualizationGraphWidget.h"
2 2 #include "Visualization/IVisualizationWidgetVisitor.h"
3 3 #include "Visualization/VisualizationDefs.h"
4 4 #include "Visualization/VisualizationGraphHelper.h"
5 5 #include "Visualization/VisualizationGraphRenderingDelegate.h"
6 6 #include "ui_VisualizationGraphWidget.h"
7 7
8 8 #include <Data/ArrayData.h>
9 9 #include <Data/IDataSeries.h>
10 10 #include <Settings/SqpSettingsDefs.h>
11 11 #include <SqpApplication.h>
12 12 #include <Variable/Variable.h>
13 13 #include <Variable/VariableController.h>
14 14
15 15 #include <unordered_map>
16 16
17 17 Q_LOGGING_CATEGORY(LOG_VisualizationGraphWidget, "VisualizationGraphWidget")
18 18
19 19 namespace {
20 20
21 21 /// Key pressed to enable zoom on horizontal axis
22 22 const auto HORIZONTAL_ZOOM_MODIFIER = Qt::NoModifier;
23 23
24 24 /// Key pressed to enable zoom on vertical axis
25 25 const auto VERTICAL_ZOOM_MODIFIER = Qt::ControlModifier;
26 26
27 27 } // namespace
28 28
29 29 struct VisualizationGraphWidget::VisualizationGraphWidgetPrivate {
30 30
31 31 explicit VisualizationGraphWidgetPrivate(const QString &name)
32 32 : m_Name{name},
33 33 m_DoAcquisition{true},
34 34 m_IsCalibration{false},
35 35 m_RenderingDelegate{nullptr}
36 36 {
37 37 }
38 38
39 39 QString m_Name;
40 40 // 1 variable -> n qcpplot
41 41 std::map<std::shared_ptr<Variable>, PlottablesMap> m_VariableToPlotMultiMap;
42 42 bool m_DoAcquisition;
43 43 bool m_IsCalibration;
44 44 QCPItemTracer *m_TextTracer;
45 45 /// Delegate used to attach rendering features to the plot
46 46 std::unique_ptr<VisualizationGraphRenderingDelegate> m_RenderingDelegate;
47 47 };
48 48
49 49 VisualizationGraphWidget::VisualizationGraphWidget(const QString &name, QWidget *parent)
50 50 : QWidget{parent},
51 51 ui{new Ui::VisualizationGraphWidget},
52 52 impl{spimpl::make_unique_impl<VisualizationGraphWidgetPrivate>(name)}
53 53 {
54 54 ui->setupUi(this);
55 55
56 56 // 'Close' options : widget is deleted when closed
57 57 setAttribute(Qt::WA_DeleteOnClose);
58 58
59 59 // Set qcpplot properties :
60 60 // - Drag (on x-axis) and zoom are enabled
61 61 // - Mouse wheel on qcpplot is intercepted to determine the zoom orientation
62 62 ui->widget->setInteractions(QCP::iRangeDrag | QCP::iRangeZoom | QCP::iSelectItems);
63 63 ui->widget->axisRect()->setRangeDrag(Qt::Horizontal);
64 64
65 65 // The delegate must be initialized after the ui as it uses the plot
66 66 impl->m_RenderingDelegate = std::make_unique<VisualizationGraphRenderingDelegate>(*this);
67 67
68 68 connect(ui->widget, &QCustomPlot::mousePress, this, &VisualizationGraphWidget::onMousePress);
69 69 connect(ui->widget, &QCustomPlot::mouseRelease, this,
70 70 &VisualizationGraphWidget::onMouseRelease);
71 71 connect(ui->widget, &QCustomPlot::mouseMove, this, &VisualizationGraphWidget::onMouseMove);
72 72 connect(ui->widget, &QCustomPlot::mouseWheel, this, &VisualizationGraphWidget::onMouseWheel);
73 73 connect(ui->widget->xAxis, static_cast<void (QCPAxis::*)(const QCPRange &, const QCPRange &)>(
74 74 &QCPAxis::rangeChanged),
75 75 this, &VisualizationGraphWidget::onRangeChanged, Qt::DirectConnection);
76 76
77 77 // Activates menu when right clicking on the graph
78 78 ui->widget->setContextMenuPolicy(Qt::CustomContextMenu);
79 79 connect(ui->widget, &QCustomPlot::customContextMenuRequested, this,
80 80 &VisualizationGraphWidget::onGraphMenuRequested);
81 81
82 82 connect(this, &VisualizationGraphWidget::requestDataLoading, &sqpApp->variableController(),
83 83 &VariableController::onRequestDataLoading);
84 84
85 85 connect(&sqpApp->variableController(), &VariableController::updateVarDisplaying, this,
86 86 &VisualizationGraphWidget::onUpdateVarDisplaying);
87 87 }
88 88
89 89
90 90 VisualizationGraphWidget::~VisualizationGraphWidget()
91 91 {
92 92 delete ui;
93 93 }
94 94
95 95 void VisualizationGraphWidget::enableAcquisition(bool enable)
96 96 {
97 97 impl->m_DoAcquisition = enable;
98 98 }
99 99
100 100 void VisualizationGraphWidget::addVariable(std::shared_ptr<Variable> variable, SqpRange range)
101 101 {
102 102 // Uses delegate to create the qcpplot components according to the variable
103 103 auto createdPlottables = VisualizationGraphHelper::create(variable, *ui->widget);
104 104 impl->m_VariableToPlotMultiMap.insert({variable, std::move(createdPlottables)});
105 105
106 106 // Set axes properties according to the units of the data series
107 107 /// @todo : for the moment, no control is performed on the axes: the units and the tickers
108 108 /// are fixed for the default x-axis and y-axis of the plot, and according to the new graph
109 109 auto xAxisUnit = Unit{};
110 110 auto valuesUnit = Unit{};
111 111
112 112 if (auto dataSeries = variable->dataSeries()) {
113 113 dataSeries->lockRead();
114 114 xAxisUnit = dataSeries->xAxisUnit();
115 115 valuesUnit = dataSeries->valuesUnit();
116 116 dataSeries->unlock();
117 117 }
118 118 impl->m_RenderingDelegate->setAxesProperties(xAxisUnit, valuesUnit);
119 119
120 120 connect(variable.get(), SIGNAL(updated()), this, SLOT(onDataCacheVariableUpdated()));
121 121
122 122 auto varRange = variable->range();
123 123
124 124 this->enableAcquisition(false);
125 125 this->setGraphRange(range);
126 126 this->enableAcquisition(true);
127 127
128 128 emit requestDataLoading(QVector<std::shared_ptr<Variable> >() << variable, range, varRange,
129 129 false);
130 130
131 131 emit variableAdded(variable);
132 132 }
133 133
134 134 void VisualizationGraphWidget::removeVariable(std::shared_ptr<Variable> variable) noexcept
135 135 {
136 136 // Each component associated to the variable :
137 137 // - is removed from qcpplot (which deletes it)
138 138 // - is no longer referenced in the map
139 139 auto variableIt = impl->m_VariableToPlotMultiMap.find(variable);
140 140 if (variableIt != impl->m_VariableToPlotMultiMap.cend()) {
141 141 emit variableAboutToBeRemoved(variable);
142 142
143 143 auto &plottablesMap = variableIt->second;
144 144
145 145 for (auto plottableIt = plottablesMap.cbegin(), plottableEnd = plottablesMap.cend();
146 146 plottableIt != plottableEnd;) {
147 147 ui->widget->removePlottable(plottableIt->second);
148 148 plottableIt = plottablesMap.erase(plottableIt);
149 149 }
150 150
151 151 impl->m_VariableToPlotMultiMap.erase(variableIt);
152 152 }
153 153
154 154 // Updates graph
155 155 ui->widget->replot();
156 156 }
157 157
158 158 void VisualizationGraphWidget::setRange(std::shared_ptr<Variable> variable, const SqpRange &range)
159 159 {
160 160 // Note: in case of different axes that depends on variable, we could start with a code like
161 161 // that:
162 162 // auto componentsIt = impl->m_VariableToPlotMultiMap.equal_range(variable);
163 163 // for (auto it = componentsIt.first; it != componentsIt.second;) {
164 164 // }
165 165 ui->widget->xAxis->setRange(range.m_TStart, range.m_TEnd);
166 166 ui->widget->replot();
167 167 }
168 168
169 169 void VisualizationGraphWidget::setYRange(const SqpRange &range)
170 170 {
171 171 ui->widget->yAxis->setRange(range.m_TStart, range.m_TEnd);
172 172 }
173 173
174 174 SqpRange VisualizationGraphWidget::graphRange() const noexcept
175 175 {
176 176 auto graphRange = ui->widget->xAxis->range();
177 177 return SqpRange{graphRange.lower, graphRange.upper};
178 178 }
179 179
180 180 void VisualizationGraphWidget::setGraphRange(const SqpRange &range)
181 181 {
182 182 qCDebug(LOG_VisualizationGraphWidget()) << tr("VisualizationGraphWidget::setGraphRange START");
183 183 ui->widget->xAxis->setRange(range.m_TStart, range.m_TEnd);
184 184 ui->widget->replot();
185 185 qCDebug(LOG_VisualizationGraphWidget()) << tr("VisualizationGraphWidget::setGraphRange END");
186 186 }
187 187
188 188 void VisualizationGraphWidget::accept(IVisualizationWidgetVisitor *visitor)
189 189 {
190 190 if (visitor) {
191 191 visitor->visit(this);
192 192 }
193 193 else {
194 194 qCCritical(LOG_VisualizationGraphWidget())
195 195 << tr("Can't visit widget : the visitor is null");
196 196 }
197 197 }
198 198
199 199 bool VisualizationGraphWidget::canDrop(const Variable &variable) const
200 200 {
201 201 /// @todo : for the moment, a graph can always accomodate a variable
202 202 Q_UNUSED(variable);
203 203 return true;
204 204 }
205 205
206 206 bool VisualizationGraphWidget::contains(const Variable &variable) const
207 207 {
208 208 // Finds the variable among the keys of the map
209 209 auto variablePtr = &variable;
210 210 auto findVariable
211 211 = [variablePtr](const auto &entry) { return variablePtr == entry.first.get(); };
212 212
213 213 auto end = impl->m_VariableToPlotMultiMap.cend();
214 214 auto it = std::find_if(impl->m_VariableToPlotMultiMap.cbegin(), end, findVariable);
215 215 return it != end;
216 216 }
217 217
218 218 QString VisualizationGraphWidget::name() const
219 219 {
220 220 return impl->m_Name;
221 221 }
222 222
223 223 void VisualizationGraphWidget::closeEvent(QCloseEvent *event)
224 224 {
225 225 Q_UNUSED(event);
226 226
227 227 // Prevents that all variables will be removed from graph when it will be closed
228 228 for (auto &variableEntry : impl->m_VariableToPlotMultiMap) {
229 229 emit variableAboutToBeRemoved(variableEntry.first);
230 230 }
231 231 }
232 232
233 233 void VisualizationGraphWidget::enterEvent(QEvent *event)
234 234 {
235 235 Q_UNUSED(event);
236 236 impl->m_RenderingDelegate->showGraphOverlay(true);
237 237 }
238 238
239 239 void VisualizationGraphWidget::leaveEvent(QEvent *event)
240 240 {
241 241 Q_UNUSED(event);
242 242 impl->m_RenderingDelegate->showGraphOverlay(false);
243 243 }
244 244
245 245 QCustomPlot &VisualizationGraphWidget::plot() noexcept
246 246 {
247 247 return *ui->widget;
248 248 }
249 249
250 250 void VisualizationGraphWidget::onGraphMenuRequested(const QPoint &pos) noexcept
251 251 {
252 252 QMenu graphMenu{};
253 253
254 254 // Iterates on variables (unique keys)
255 255 for (auto it = impl->m_VariableToPlotMultiMap.cbegin(),
256 256 end = impl->m_VariableToPlotMultiMap.cend();
257 257 it != end; it = impl->m_VariableToPlotMultiMap.upper_bound(it->first)) {
258 258 // 'Remove variable' action
259 259 graphMenu.addAction(tr("Remove variable %1").arg(it->first->name()),
260 260 [ this, var = it->first ]() { removeVariable(var); });
261 261 }
262 262
263 263 if (!graphMenu.isEmpty()) {
264 264 graphMenu.exec(QCursor::pos());
265 265 }
266 266 }
267 267
268 268 void VisualizationGraphWidget::onRangeChanged(const QCPRange &t1, const QCPRange &t2)
269 269 {
270 qCDebug(LOG_VisualizationGraphWidget()) << tr("TORM: VisualizationGraphWidget::onRangeChanged")
271 << QThread::currentThread()->objectName() << "DoAcqui"
272 << impl->m_DoAcquisition;
270 qCDebug(LOG_VisualizationGraphWidget())
271 << tr("TORM: VisualizationGraphWidget::onRangeChanged")
272 << QThread::currentThread()->objectName() << "DoAcqui" << impl->m_DoAcquisition;
273 273
274 274 auto graphRange = SqpRange{t1.lower, t1.upper};
275 275 auto oldGraphRange = SqpRange{t2.lower, t2.upper};
276 276
277 277 if (impl->m_DoAcquisition) {
278 278 QVector<std::shared_ptr<Variable> > variableUnderGraphVector;
279 279
280 280 for (auto it = impl->m_VariableToPlotMultiMap.begin(),
281 281 end = impl->m_VariableToPlotMultiMap.end();
282 282 it != end; it = impl->m_VariableToPlotMultiMap.upper_bound(it->first)) {
283 283 variableUnderGraphVector.push_back(it->first);
284 284 }
285 285 emit requestDataLoading(std::move(variableUnderGraphVector), graphRange, oldGraphRange,
286 286 !impl->m_IsCalibration);
287 287
288 288 if (!impl->m_IsCalibration) {
289 289 qCDebug(LOG_VisualizationGraphWidget())
290 290 << tr("TORM: VisualizationGraphWidget::Synchronize notify !!")
291 291 << QThread::currentThread()->objectName() << graphRange << oldGraphRange;
292 292 emit synchronize(graphRange, oldGraphRange);
293 293 }
294 294 }
295 295 }
296 296
297 297 void VisualizationGraphWidget::onMouseMove(QMouseEvent *event) noexcept
298 298 {
299 299 // Handles plot rendering when mouse is moving
300 300 impl->m_RenderingDelegate->onMouseMove(event);
301 301 }
302 302
303 303 void VisualizationGraphWidget::onMouseWheel(QWheelEvent *event) noexcept
304 304 {
305 305 auto zoomOrientations = QFlags<Qt::Orientation>{};
306 306
307 307 // Lambda that enables a zoom orientation if the key modifier related to this orientation
308 308 // has
309 309 // been pressed
310 310 auto enableOrientation
311 311 = [&zoomOrientations, event](const auto &orientation, const auto &modifier) {
312 312 auto orientationEnabled = event->modifiers().testFlag(modifier);
313 313 zoomOrientations.setFlag(orientation, orientationEnabled);
314 314 };
315 315 enableOrientation(Qt::Vertical, VERTICAL_ZOOM_MODIFIER);
316 316 enableOrientation(Qt::Horizontal, HORIZONTAL_ZOOM_MODIFIER);
317 317
318 318 ui->widget->axisRect()->setRangeZoom(zoomOrientations);
319 319 }
320 320
321 321 void VisualizationGraphWidget::onMousePress(QMouseEvent *event) noexcept
322 322 {
323 323 impl->m_IsCalibration = event->modifiers().testFlag(Qt::ControlModifier);
324 324 }
325 325
326 326 void VisualizationGraphWidget::onMouseRelease(QMouseEvent *event) noexcept
327 327 {
328 328 impl->m_IsCalibration = false;
329 329 }
330 330
331 331 void VisualizationGraphWidget::onDataCacheVariableUpdated()
332 332 {
333 333 auto graphRange = ui->widget->xAxis->range();
334 334 auto dateTime = SqpRange{graphRange.lower, graphRange.upper};
335 335
336 336 for (auto &variableEntry : impl->m_VariableToPlotMultiMap) {
337 337 auto variable = variableEntry.first;
338 338 qCDebug(LOG_VisualizationGraphWidget())
339 339 << "TORM: VisualizationGraphWidget::onDataCacheVariableUpdated S" << variable->range();
340 340 qCDebug(LOG_VisualizationGraphWidget())
341 341 << "TORM: VisualizationGraphWidget::onDataCacheVariableUpdated E" << dateTime;
342 342 if (dateTime.contains(variable->range()) || dateTime.intersect(variable->range())) {
343 343 VisualizationGraphHelper::updateData(variableEntry.second, variable->dataSeries(),
344 344 variable->range());
345 345 }
346 346 }
347 347 }
348 348
349 349 void VisualizationGraphWidget::onUpdateVarDisplaying(std::shared_ptr<Variable> variable,
350 350 const SqpRange &range)
351 351 {
352 352 auto it = impl->m_VariableToPlotMultiMap.find(variable);
353 353 if (it != impl->m_VariableToPlotMultiMap.end()) {
354 354 VisualizationGraphHelper::updateData(it->second, variable->dataSeries(), range);
355 355 }
356 356 }
@@ -1,302 +1,302
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 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: deltaLeft")
131 << deltaLeft;
132 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: deltaRight")
133 << deltaRight;
130 qCDebug(LOG_VisualizationZoneWidget())
131 << tr("TORM: deltaLeft") << deltaLeft;
132 qCDebug(LOG_VisualizationZoneWidget())
133 << tr("TORM: deltaRight") << 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 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: deltaLeft")
145 << deltaLeft;
146 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: deltaRight")
147 << deltaRight;
144 qCDebug(LOG_VisualizationZoneWidget())
145 << tr("TORM: deltaLeft") << deltaLeft;
146 qCDebug(LOG_VisualizationZoneWidget())
147 << tr("TORM: deltaRight") << 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 156 auto deltaRight = graphRange.m_TEnd - oldGraphRange.m_TEnd;
157 157 graphChildRange.m_TStart += deltaRight;
158 158 graphChildRange.m_TEnd += deltaRight;
159 159 qCDebug(LOG_VisualizationZoneWidget())
160 160 << tr("TORM: dt") << graphRange.m_TEnd - graphRange.m_TStart;
161 161 break;
162 162 }
163 163 case AcquisitionZoomType::PanLeft: {
164 164 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: PanLeft");
165 165 auto deltaLeft = oldGraphRange.m_TStart - graphRange.m_TStart;
166 166 graphChildRange.m_TStart -= deltaLeft;
167 167 graphChildRange.m_TEnd -= deltaLeft;
168 168 break;
169 169 }
170 170 case AcquisitionZoomType::Unknown: {
171 171 qCDebug(LOG_VisualizationZoneWidget())
172 172 << tr("Impossible to synchronize: zoom type unknown");
173 173 break;
174 174 }
175 175 default:
176 176 qCCritical(LOG_VisualizationZoneWidget())
177 177 << tr("Impossible to synchronize: zoom type not take into account");
178 178 // No action
179 179 break;
180 180 }
181 181 graphChild->enableAcquisition(false);
182 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: Range before: ")
183 << graphChild->graphRange();
184 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: Range after : ")
185 << graphChildRange;
182 qCDebug(LOG_VisualizationZoneWidget())
183 << tr("TORM: Range before: ") << graphChild->graphRange();
184 qCDebug(LOG_VisualizationZoneWidget())
185 << tr("TORM: Range after : ") << graphChildRange;
186 186 qCDebug(LOG_VisualizationZoneWidget())
187 187 << tr("TORM: child dt") << graphChildRange.m_TEnd - graphChildRange.m_TStart;
188 188 graphChild->setGraphRange(graphChildRange);
189 189 graphChild->enableAcquisition(true);
190 190 }
191 191 }
192 192 };
193 193
194 194 // connection for synchronization
195 195 connect(graphWidget, &VisualizationGraphWidget::synchronize, synchronizeZoneWidget);
196 196 connect(graphWidget, &VisualizationGraphWidget::variableAdded, this,
197 197 &VisualizationZoneWidget::onVariableAdded);
198 198 connect(graphWidget, &VisualizationGraphWidget::variableAboutToBeRemoved, this,
199 199 &VisualizationZoneWidget::onVariableAboutToBeRemoved);
200 200
201 201 auto range = SqpRange{};
202 202
203 203 // Apply visitor to graph children
204 204 auto layout = ui->visualizationZoneFrame->layout();
205 205 if (layout->count() > 0) {
206 206 // Case of a new graph in a existant zone
207 207 if (auto visualizationGraphWidget
208 208 = dynamic_cast<VisualizationGraphWidget *>(layout->itemAt(0)->widget())) {
209 209 range = visualizationGraphWidget->graphRange();
210 210 }
211 211 }
212 212 else {
213 213 // Case of a new graph as the first of the zone
214 214 range = variable->range();
215 215 }
216 216
217 217 this->addGraph(graphWidget);
218 218
219 219 graphWidget->addVariable(variable, range);
220 220
221 221 // get y using variable range
222 222 if (auto dataSeries = variable->dataSeries()) {
223 223 dataSeries->lockRead();
224 224 auto valuesBounds
225 225 = dataSeries->valuesBounds(variable->range().m_TStart, variable->range().m_TEnd);
226 226 auto end = dataSeries->cend();
227 227 if (valuesBounds.first != end && valuesBounds.second != end) {
228 228 auto rangeValue = [](const auto &value) { return std::isnan(value) ? 0. : value; };
229 229
230 230 auto minValue = rangeValue(valuesBounds.first->minValue());
231 231 auto maxValue = rangeValue(valuesBounds.second->maxValue());
232 232
233 233 graphWidget->setYRange(SqpRange{minValue, maxValue});
234 234 }
235 235 dataSeries->unlock();
236 236 }
237 237
238 238 return graphWidget;
239 239 }
240 240
241 241 void VisualizationZoneWidget::accept(IVisualizationWidgetVisitor *visitor)
242 242 {
243 243 if (visitor) {
244 244 visitor->visitEnter(this);
245 245
246 246 // Apply visitor to graph children: widgets different from graphs are not visited (no
247 247 // action)
248 248 processGraphs(
249 249 *ui->visualizationZoneFrame->layout(),
250 250 [visitor](VisualizationGraphWidget &graphWidget) { graphWidget.accept(visitor); });
251 251
252 252 visitor->visitLeave(this);
253 253 }
254 254 else {
255 255 qCCritical(LOG_VisualizationZoneWidget()) << tr("Can't visit widget : the visitor is null");
256 256 }
257 257 }
258 258
259 259 bool VisualizationZoneWidget::canDrop(const Variable &variable) const
260 260 {
261 261 // A tab can always accomodate a variable
262 262 Q_UNUSED(variable);
263 263 return true;
264 264 }
265 265
266 266 bool VisualizationZoneWidget::contains(const Variable &variable) const
267 267 {
268 268 Q_UNUSED(variable);
269 269 return false;
270 270 }
271 271
272 272 QString VisualizationZoneWidget::name() const
273 273 {
274 274 return ui->zoneNameLabel->text();
275 275 }
276 276
277 277 void VisualizationZoneWidget::closeEvent(QCloseEvent *event)
278 278 {
279 279 // Closes graphs in the zone
280 280 processGraphs(*ui->visualizationZoneFrame->layout(),
281 281 [](VisualizationGraphWidget &graphWidget) { graphWidget.close(); });
282 282
283 283 // Delete synchronization group from variable controller
284 284 QMetaObject::invokeMethod(&sqpApp->variableController(), "onRemoveSynchronizationGroupId",
285 285 Qt::QueuedConnection, Q_ARG(QUuid, impl->m_SynchronisationGroupId));
286 286
287 287 QWidget::closeEvent(event);
288 288 }
289 289
290 290 void VisualizationZoneWidget::onVariableAdded(std::shared_ptr<Variable> variable)
291 291 {
292 292 QMetaObject::invokeMethod(&sqpApp->variableController(), "onAddSynchronized",
293 293 Qt::QueuedConnection, Q_ARG(std::shared_ptr<Variable>, variable),
294 294 Q_ARG(QUuid, impl->m_SynchronisationGroupId));
295 295 }
296 296
297 297 void VisualizationZoneWidget::onVariableAboutToBeRemoved(std::shared_ptr<Variable> variable)
298 298 {
299 299 QMetaObject::invokeMethod(&sqpApp->variableController(), "desynchronize", Qt::QueuedConnection,
300 300 Q_ARG(std::shared_ptr<Variable>, variable),
301 301 Q_ARG(QUuid, impl->m_SynchronisationGroupId));
302 302 }
@@ -1,176 +1,176
1 1 #include "AmdaParser.h"
2 2
3 3 #include <DataSource/DataSourceItem.h>
4 4
5 5 #include <QObject>
6 6 #include <QtTest>
7 7
8 8 #include <QString>
9 9
10 10 namespace {
11 11
12 12 /// Path for the tests
13 13 const auto TESTS_RESOURCES_PATH
14 14 = QFileInfo{QString{AMDA_TESTS_RESOURCES_DIR}, "TestAmdaParser"}.absoluteFilePath();
15 15
16 16 QString inputFilePath(const QString &inputFileName)
17 17 {
18 18 return QFileInfo{TESTS_RESOURCES_PATH, inputFileName}.absoluteFilePath();
19 19 }
20 20
21 21 struct ExpectedResults {
22 22 explicit ExpectedResults() = default;
23 23 explicit ExpectedResults(std::shared_ptr<DataSourceItem> item)
24 24 : m_ParsingOK{true}, m_Item{std::move(item)}
25 25 {
26 26 }
27 27
28 28 // Parsing was successfully completed
29 29 bool m_ParsingOK{false};
30 30 // Expected item after parsing
31 31 std::shared_ptr<DataSourceItem> m_Item{nullptr};
32 32 };
33 33
34 34 // ///////////////////////////////// //
35 35 // Set of expected results for tests //
36 36 // ///////////////////////////////// //
37 37
38 38 ExpectedResults validResults1()
39 39 {
40 40 auto component1 = std::make_unique<DataSourceItem>(
41 41 DataSourceItemType::COMPONENT,
42 42 QHash<QString, QVariant>{{"name", "Bx"}, {"xml:id", "ice_b_cse(0)"}});
43 43 auto component2 = std::make_unique<DataSourceItem>(
44 44 DataSourceItemType::COMPONENT,
45 45 QHash<QString, QVariant>{{"name", "By"}, {"xml:id", "ice_b_cse(1)"}});
46 46 auto component3 = std::make_unique<DataSourceItem>(
47 47 DataSourceItemType::COMPONENT,
48 48 QHash<QString, QVariant>{{"name", "Bz"}, {"xml:id", "ice_b_cse(2)"}});
49 49 auto parameter1 = std::make_unique<DataSourceItem>(
50 50 DataSourceItemType::PRODUCT,
51 51 QHash<QString, QVariant>{
52 52 {"name", "B_cse"}, {"units", "nT"}, {"display_type", ""}, {"xml:id", "ice_b_cse"}});
53 53 parameter1->appendChild(std::move(component1));
54 54 parameter1->appendChild(std::move(component2));
55 55 parameter1->appendChild(std::move(component3));
56 56
57 57 auto parameter2 = std::make_unique<DataSourceItem>(
58 58 DataSourceItemType::PRODUCT,
59 59 QHash<QString, QVariant>{
60 60 {"name", "|B|"}, {"units", "nT"}, {"display_type", ""}, {"xml:id", "ice_b_tot"}});
61 61
62 62 auto dataset = std::make_unique<DataSourceItem>(
63 63 DataSourceItemType::NODE, QHash<QString, QVariant>{{"att", ""},
64 64 {"restricted", ""},
65 65 {"name", "Magnetic Field"},
66 66 {"xml:id", "ice:mag:p21"},
67 67 {"sampling", "0.3s"},
68 68 {"maxSampling", ""},
69 69 {"dataStart", "1985/09/10"},
70 70 {"dataStop", "1985/09/14"},
71 71 {"dataSource", "PDS"},
72 72 {"target", ""}});
73 73 dataset->appendChild(std::move(parameter1));
74 74 dataset->appendChild(std::move(parameter2));
75 75
76 76 auto instrument = std::make_unique<DataSourceItem>(
77 77 DataSourceItemType::NODE, QHash<QString, QVariant>{{"att", ""},
78 78 {"name", "MAG"},
79 79 {"xml:id", "ICE@Giacobini-Zinner:MAG"},
80 80 {"desc", "Vector Helium Magnetometer"},
81 81 {"restricted", ""}});
82 82 instrument->appendChild(std::move(dataset));
83 83
84 84 auto mission = std::make_unique<DataSourceItem>(
85 85 DataSourceItemType::NODE,
86 86 QHash<QString, QVariant>{{"att", ""},
87 87 {"name", "ICE@Giacobini-Zinner"},
88 88 {"rank", "93"},
89 89 {"xml:id", "ICE@Giacobini-Zinner"},
90 90 {"desc", "International Cometary Explorer"},
91 91 {"target", "Comet"},
92 92 {"available", "1"}});
93 93 mission->appendChild(std::move(instrument));
94 94
95 95 auto item = std::make_shared<DataSourceItem>(DataSourceItemType::NODE,
96 96 QHash<QString, QVariant>{
97 97 {"name", "AMDA"},
98 98 {"desc", "AMDA_Internal_Data_Base"},
99 99 {"xml:id", "myLocalData-treeRootNode"},
100 100 });
101 101 item->appendChild(std::move(mission));
102 102
103 103 return ExpectedResults{item};
104 104 }
105 105
106 106 ExpectedResults invalidResults()
107 107 {
108 108 return ExpectedResults{};
109 109 }
110 110
111 111 } // namespace
112 112
113 113 Q_DECLARE_METATYPE(ExpectedResults)
114 114
115 115 class TestAmdaParser : public QObject {
116 116 Q_OBJECT
117 117 private slots:
118 118 /// Input test data
119 119 /// @sa testReadJson()
120 120 void testReadJson_data();
121 121
122 122 /// Tests parsing of a JSON file
123 123 void testReadJson();
124 124 };
125 125
126 126 void TestAmdaParser::testReadJson_data()
127 127 {
128 128 // ////////////// //
129 129 // Test structure //
130 130 // ////////////// //
131 131
132 132 // Name of JSON file to read
133 133 QTest::addColumn<QString>("inputFileName");
134 134 // Expected results
135 135 QTest::addColumn<ExpectedResults>("expectedResults");
136 136
137 137 // ////////// //
138 138 // Test cases //
139 139 // ////////// //
140 140
141 141 // Valid file
142 142 QTest::newRow("Valid file") << QStringLiteral("ValidFile1.json") << validResults1();
143 143
144 144 // Invalid files
145 QTest::newRow("Invalid file (unexisting file)") << QStringLiteral("UnexistingFile.json")
146 << invalidResults();
147 QTest::newRow("Invalid file (two root objects)") << QStringLiteral("TwoRootsFile.json")
148 << invalidResults();
149 QTest::newRow("Invalid file (wrong root key)") << QStringLiteral("WrongRootKey.json")
150 << invalidResults();
151 QTest::newRow("Invalid file (wrong root type)") << QStringLiteral("WrongRootType.json")
152 << invalidResults();
145 QTest::newRow("Invalid file (unexisting file)")
146 << QStringLiteral("UnexistingFile.json") << invalidResults();
147 QTest::newRow("Invalid file (two root objects)")
148 << QStringLiteral("TwoRootsFile.json") << invalidResults();
149 QTest::newRow("Invalid file (wrong root key)")
150 << QStringLiteral("WrongRootKey.json") << invalidResults();
151 QTest::newRow("Invalid file (wrong root type)")
152 << QStringLiteral("WrongRootType.json") << invalidResults();
153 153 }
154 154
155 155 void TestAmdaParser::testReadJson()
156 156 {
157 157 QFETCH(QString, inputFileName);
158 158 QFETCH(ExpectedResults, expectedResults);
159 159
160 160 // Parses file
161 161 auto filePath = inputFilePath(inputFileName);
162 162 auto item = AmdaParser::readJson(filePath);
163 163
164 164 // Validates results
165 165 if (expectedResults.m_ParsingOK) {
166 166 QVERIFY(item != nullptr);
167 167 QVERIFY(expectedResults.m_Item != nullptr);
168 168 QVERIFY(*item == *expectedResults.m_Item);
169 169 }
170 170 else {
171 171 QVERIFY(item == nullptr);
172 172 }
173 173 }
174 174
175 175 QTEST_MAIN(TestAmdaParser)
176 176 #include "TestAmdaParser.moc"
@@ -1,281 +1,281
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 58 QVERIFY(dataSeries != nullptr);
59 59
60 60 // Checks units
61 61 QVERIFY(dataSeries->xAxisUnit() == m_XAxisUnit);
62 62 QVERIFY(dataSeries->valuesUnit() == m_ValuesUnit);
63 63
64 64 auto verifyRange = [dataSeries](const auto &expectedData, const auto &equalFun) {
65 65 QVERIFY(std::equal(dataSeries->cbegin(), dataSeries->cend(), expectedData.cbegin(),
66 66 expectedData.cend(),
67 67 [&equalFun](const auto &dataSeriesIt, const auto &expectedX) {
68 68 return equalFun(dataSeriesIt, expectedX);
69 69 }));
70 70 };
71 71
72 72 // Checks x-axis data
73 73 verifyRange(m_XAxisData, [](const auto &seriesIt, const auto &value) {
74 74 return seriesIt.x() == value;
75 75 });
76 76
77 77 // Checks values data of each component
78 78 for (auto i = 0; i < m_ValuesData.size(); ++i) {
79 79 verifyRange(m_ValuesData.at(i), [i](const auto &seriesIt, const auto &value) {
80 80 auto itValue = seriesIt.value(i);
81 81 return (std::isnan(itValue) && std::isnan(value)) || seriesIt.value(i) == value;
82 82 });
83 83 }
84 84 }
85 85 else {
86 86 QVERIFY(results == nullptr);
87 87 }
88 88 }
89 89
90 90 // Parsing was successfully completed
91 91 bool m_ParsingOK{false};
92 92 // Expected x-axis unit
93 93 Unit m_XAxisUnit{};
94 94 // Expected values unit
95 95 Unit m_ValuesUnit{};
96 96 // Expected x-axis data
97 97 QVector<double> m_XAxisData{};
98 98 // Expected values data
99 99 QVector<QVector<double> > m_ValuesData{};
100 100 };
101 101
102 102 } // namespace
103 103
104 104 Q_DECLARE_METATYPE(ExpectedResults<ScalarSeries>)
105 105 Q_DECLARE_METATYPE(ExpectedResults<VectorSeries>)
106 106
107 107 class TestAmdaResultParser : public QObject {
108 108 Q_OBJECT
109 109 private:
110 110 template <typename T>
111 111 void testReadDataStructure()
112 112 {
113 113 // ////////////// //
114 114 // Test structure //
115 115 // ////////////// //
116 116
117 117 // Name of TXT file to read
118 118 QTest::addColumn<QString>("inputFileName");
119 119 // Expected results
120 120 QTest::addColumn<ExpectedResults<T> >("expectedResults");
121 121 }
122 122
123 123 template <typename T>
124 124 void testRead(AmdaResultParser::ValueType valueType)
125 125 {
126 126 QFETCH(QString, inputFileName);
127 127 QFETCH(ExpectedResults<T>, expectedResults);
128 128
129 129 // Parses file
130 130 auto filePath = inputFilePath(inputFileName);
131 131 auto results = AmdaResultParser::readTxt(filePath, valueType);
132 132
133 133 // ///////////////// //
134 134 // Validates results //
135 135 // ///////////////// //
136 136 expectedResults.validate(results);
137 137 }
138 138
139 139 private slots:
140 140 /// Input test data
141 141 /// @sa testReadScalarTxt()
142 142 void testReadScalarTxt_data();
143 143
144 144 /// Tests parsing scalar series of a TXT file
145 145 void testReadScalarTxt();
146 146
147 147 /// Input test data
148 148 /// @sa testReadVectorTxt()
149 149 void testReadVectorTxt_data();
150 150
151 151 /// Tests parsing vector series of a TXT file
152 152 void testReadVectorTxt();
153 153 };
154 154
155 155 void TestAmdaResultParser::testReadScalarTxt_data()
156 156 {
157 157 testReadDataStructure<ScalarSeries>();
158 158
159 159 // ////////// //
160 160 // Test cases //
161 161 // ////////// //
162 162
163 163 // Valid files
164 164 QTest::newRow("Valid file")
165 165 << QStringLiteral("ValidScalar1.txt")
166 166 << ExpectedResults<ScalarSeries>{
167 167 Unit{QStringLiteral("nT"), true}, Unit{},
168 168 QVector<QDateTime>{dateTime(2013, 9, 23, 9, 0, 30), dateTime(2013, 9, 23, 9, 1, 30),
169 169 dateTime(2013, 9, 23, 9, 2, 30), dateTime(2013, 9, 23, 9, 3, 30),
170 170 dateTime(2013, 9, 23, 9, 4, 30), dateTime(2013, 9, 23, 9, 5, 30),
171 171 dateTime(2013, 9, 23, 9, 6, 30), dateTime(2013, 9, 23, 9, 7, 30),
172 172 dateTime(2013, 9, 23, 9, 8, 30), dateTime(2013, 9, 23, 9, 9, 30)},
173 173 QVector<double>{-2.83950, -2.71850, -2.52150, -2.57633, -2.58050, -2.48325, -2.63025,
174 174 -2.55800, -2.43250, -2.42200}};
175 175
176 176 QTest::newRow("Valid file (value of first line is invalid but it is converted to NaN")
177 177 << QStringLiteral("WrongValue.txt")
178 178 << ExpectedResults<ScalarSeries>{
179 179 Unit{QStringLiteral("nT"), true}, Unit{},
180 180 QVector<QDateTime>{dateTime(2013, 9, 23, 9, 0, 30), dateTime(2013, 9, 23, 9, 1, 30),
181 181 dateTime(2013, 9, 23, 9, 2, 30)},
182 182 QVector<double>{std::numeric_limits<double>::quiet_NaN(), -2.71850, -2.52150}};
183 183
184 184 QTest::newRow("Valid file that contains NaN values")
185 185 << QStringLiteral("NaNValue.txt")
186 186 << ExpectedResults<ScalarSeries>{
187 187 Unit{QStringLiteral("nT"), true}, Unit{},
188 188 QVector<QDateTime>{dateTime(2013, 9, 23, 9, 0, 30), dateTime(2013, 9, 23, 9, 1, 30),
189 189 dateTime(2013, 9, 23, 9, 2, 30)},
190 190 QVector<double>{std::numeric_limits<double>::quiet_NaN(), -2.71850, -2.52150}};
191 191
192 192 // Valid files but with some invalid lines (wrong unit, wrong values, etc.)
193 193 QTest::newRow("No unit file") << QStringLiteral("NoUnit.txt")
194 194 << ExpectedResults<ScalarSeries>{Unit{QStringLiteral(""), true},
195 195 Unit{}, QVector<QDateTime>{},
196 196 QVector<double>{}};
197 197 QTest::newRow("Wrong unit file")
198 198 << QStringLiteral("WrongUnit.txt")
199 199 << ExpectedResults<ScalarSeries>{Unit{QStringLiteral(""), true}, Unit{},
200 200 QVector<QDateTime>{dateTime(2013, 9, 23, 9, 0, 30),
201 201 dateTime(2013, 9, 23, 9, 1, 30),
202 202 dateTime(2013, 9, 23, 9, 2, 30)},
203 203 QVector<double>{-2.83950, -2.71850, -2.52150}};
204 204
205 205 QTest::newRow("Wrong results file (date of first line is invalid")
206 206 << QStringLiteral("WrongDate.txt")
207 207 << ExpectedResults<ScalarSeries>{
208 208 Unit{QStringLiteral("nT"), true}, Unit{},
209 209 QVector<QDateTime>{dateTime(2013, 9, 23, 9, 1, 30), dateTime(2013, 9, 23, 9, 2, 30)},
210 210 QVector<double>{-2.71850, -2.52150}};
211 211
212 212 QTest::newRow("Wrong results file (too many values for first line")
213 213 << QStringLiteral("TooManyValues.txt")
214 214 << ExpectedResults<ScalarSeries>{
215 215 Unit{QStringLiteral("nT"), true}, Unit{},
216 216 QVector<QDateTime>{dateTime(2013, 9, 23, 9, 1, 30), dateTime(2013, 9, 23, 9, 2, 30)},
217 217 QVector<double>{-2.71850, -2.52150}};
218 218
219 219 QTest::newRow("Wrong results file (x of first line is NaN")
220 220 << QStringLiteral("NaNX.txt")
221 221 << ExpectedResults<ScalarSeries>{
222 222 Unit{QStringLiteral("nT"), true}, Unit{},
223 223 QVector<QDateTime>{dateTime(2013, 9, 23, 9, 1, 30), dateTime(2013, 9, 23, 9, 2, 30)},
224 224 QVector<double>{-2.71850, -2.52150}};
225 225
226 226 QTest::newRow("Invalid file type (vector)")
227 227 << QStringLiteral("ValidVector1.txt")
228 228 << ExpectedResults<ScalarSeries>{Unit{QStringLiteral("nT"), true}, Unit{},
229 229 QVector<QDateTime>{}, QVector<double>{}};
230 230
231 231 // Invalid files
232 QTest::newRow("Invalid file (unexisting file)") << QStringLiteral("UnexistingFile.txt")
233 << ExpectedResults<ScalarSeries>{};
232 QTest::newRow("Invalid file (unexisting file)")
233 << QStringLiteral("UnexistingFile.txt") << ExpectedResults<ScalarSeries>{};
234 234
235 QTest::newRow("Invalid file (file not found on server)") << QStringLiteral("FileNotFound.txt")
236 << ExpectedResults<ScalarSeries>{};
235 QTest::newRow("Invalid file (file not found on server)")
236 << QStringLiteral("FileNotFound.txt") << ExpectedResults<ScalarSeries>{};
237 237 }
238 238
239 239 void TestAmdaResultParser::testReadScalarTxt()
240 240 {
241 241 testRead<ScalarSeries>(AmdaResultParser::ValueType::SCALAR);
242 242 }
243 243
244 244 void TestAmdaResultParser::testReadVectorTxt_data()
245 245 {
246 246 testReadDataStructure<VectorSeries>();
247 247
248 248 // ////////// //
249 249 // Test cases //
250 250 // ////////// //
251 251
252 252 // Valid files
253 253 QTest::newRow("Valid file")
254 254 << QStringLiteral("ValidVector1.txt")
255 255 << ExpectedResults<VectorSeries>{
256 256 Unit{QStringLiteral("nT"), true}, Unit{},
257 257 QVector<QDateTime>{dateTime(2013, 7, 2, 9, 13, 50), dateTime(2013, 7, 2, 9, 14, 6),
258 258 dateTime(2013, 7, 2, 9, 14, 22), dateTime(2013, 7, 2, 9, 14, 38),
259 259 dateTime(2013, 7, 2, 9, 14, 54), dateTime(2013, 7, 2, 9, 15, 10),
260 260 dateTime(2013, 7, 2, 9, 15, 26), dateTime(2013, 7, 2, 9, 15, 42),
261 261 dateTime(2013, 7, 2, 9, 15, 58), dateTime(2013, 7, 2, 9, 16, 14)},
262 262 QVector<QVector<double> >{
263 263 {-0.332, -1.011, -1.457, -1.293, -1.217, -1.443, -1.278, -1.202, -1.22, -1.259},
264 264 {3.206, 2.999, 2.785, 2.736, 2.612, 2.564, 2.892, 2.862, 2.859, 2.764},
265 265 {0.058, 0.496, 1.018, 1.485, 1.662, 1.505, 1.168, 1.244, 1.15, 1.358}}};
266 266
267 267 // Valid files but with some invalid lines (wrong unit, wrong values, etc.)
268 268 QTest::newRow("Invalid file type (scalar)")
269 269 << QStringLiteral("ValidScalar1.txt")
270 270 << ExpectedResults<VectorSeries>{Unit{QStringLiteral("nT"), true}, Unit{},
271 271 QVector<QDateTime>{},
272 272 QVector<QVector<double> >{{}, {}, {}}};
273 273 }
274 274
275 275 void TestAmdaResultParser::testReadVectorTxt()
276 276 {
277 277 testRead<VectorSeries>(AmdaResultParser::ValueType::VECTOR);
278 278 }
279 279
280 280 QTEST_MAIN(TestAmdaResultParser)
281 281 #include "TestAmdaResultParser.moc"
@@ -1,109 +1,109
1 1 #include "CosinusProvider.h"
2 2
3 3 #include <Data/DataProviderParameters.h>
4 4 #include <Data/ScalarSeries.h>
5 5
6 6 #include <cmath>
7 7
8 8 #include <QFuture>
9 9 #include <QThread>
10 10 #include <QtConcurrent/QtConcurrent>
11 11
12 12 Q_LOGGING_CATEGORY(LOG_CosinusProvider, "CosinusProvider")
13 13
14 14 std::shared_ptr<IDataProvider> CosinusProvider::clone() const
15 15 {
16 16 // No copy is made in clone
17 17 return std::make_shared<CosinusProvider>();
18 18 }
19 19
20 20 std::shared_ptr<IDataSeries> CosinusProvider::retrieveData(QUuid acqIdentifier,
21 21 const SqpRange &dataRangeRequested)
22 22 {
23 23 // TODO: Add Mutex
24 24 auto dataIndex = 0;
25 25
26 26 // Gets the timerange from the parameters
27 double freq = 100.0;
27 double freq = 1.0;
28 28 double start = std::ceil(dataRangeRequested.m_TStart * freq); // 100 htz
29 29 double end = std::floor(dataRangeRequested.m_TEnd * freq); // 100 htz
30 30
31 31 // We assure that timerange is valid
32 32 if (end < start) {
33 33 std::swap(start, end);
34 34 }
35 35
36 36 // Generates scalar series containing cosinus values (one value per second)
37 37 auto dataCount = end - start;
38 38
39 39 auto xAxisData = std::vector<double>{};
40 40 xAxisData.resize(dataCount);
41 41
42 42 auto valuesData = std::vector<double>{};
43 43 valuesData.resize(dataCount);
44 44
45 45 int progress = 0;
46 46 auto progressEnd = dataCount;
47 47 for (auto time = start; time < end; ++time, ++dataIndex) {
48 48 auto it = m_VariableToEnableProvider.find(acqIdentifier);
49 49 if (it != m_VariableToEnableProvider.end() && it.value()) {
50 50 const auto timeOnFreq = time / freq;
51 51
52 52 xAxisData[dataIndex] = timeOnFreq;
53 53 valuesData[dataIndex] = std::cos(timeOnFreq);
54 54
55 55 // progression
56 56 int currentProgress = (time - start) * 100.0 / progressEnd;
57 57 if (currentProgress != progress) {
58 58 progress = currentProgress;
59 59
60 60 emit dataProvidedProgress(acqIdentifier, progress);
61 61 }
62 62 }
63 63 else {
64 64 if (!it.value()) {
65 65 qCDebug(LOG_CosinusProvider())
66 66 << "CosinusProvider::retrieveData: ARRET De l'acquisition detectΓ©"
67 67 << end - time;
68 68 }
69 69 }
70 70 }
71 71 emit dataProvidedProgress(acqIdentifier, 0.0);
72 72
73 73 return std::make_shared<ScalarSeries>(std::move(xAxisData), std::move(valuesData),
74 74 Unit{QStringLiteral("t"), true}, Unit{});
75 75 }
76 76
77 77 void CosinusProvider::requestDataLoading(QUuid acqIdentifier,
78 78 const DataProviderParameters &parameters)
79 79 {
80 80 // TODO: Add Mutex
81 81 m_VariableToEnableProvider[acqIdentifier] = true;
82 qCDebug(LOG_CosinusProvider()) << "TORM: CosinusProvider::requestDataLoading"
83 << QThread::currentThread()->objectName();
82 qCDebug(LOG_CosinusProvider())
83 << "TORM: CosinusProvider::requestDataLoading" << QThread::currentThread()->objectName();
84 84 // NOTE: Try to use multithread if possible
85 85 const auto times = parameters.m_Times;
86 86
87 87 for (const auto &dateTime : qAsConst(times)) {
88 88 if (m_VariableToEnableProvider[acqIdentifier]) {
89 89 auto scalarSeries = this->retrieveData(acqIdentifier, dateTime);
90 90 qCDebug(LOG_CosinusProvider()) << "TORM: CosinusProvider::dataProvided";
91 91 emit dataProvided(acqIdentifier, scalarSeries, dateTime);
92 92 }
93 93 }
94 94 }
95 95
96 96 void CosinusProvider::requestDataAborting(QUuid acqIdentifier)
97 97 {
98 98 // TODO: Add Mutex
99 99 qCDebug(LOG_CosinusProvider()) << "CosinusProvider::requestDataAborting" << acqIdentifier
100 100 << QThread::currentThread()->objectName();
101 101 auto it = m_VariableToEnableProvider.find(acqIdentifier);
102 102 if (it != m_VariableToEnableProvider.end()) {
103 103 it.value() = false;
104 104 }
105 105 else {
106 106 qCWarning(LOG_CosinusProvider())
107 107 << tr("Aborting progression of inexistant identifier detected !!!");
108 108 }
109 109 }
General Comments 0
You need to be logged in to leave comments. Login now