diff --git a/core/include/Variable/Variable.h b/core/include/Variable/Variable.h index bf30f18..88c5682 100644 --- a/core/include/Variable/Variable.h +++ b/core/include/Variable/Variable.h @@ -68,6 +68,12 @@ public: QVector provideInCacheRangeList(const SqpRange &range) const noexcept; void mergeDataSeries(std::shared_ptr dataSeries) noexcept; + static QVector provideNotInCacheRangeList(const SqpRange &oldRange, + const SqpRange &nextRange); + + static QVector provideInCacheRangeList(const SqpRange &oldRange, + const SqpRange &nextRange); + signals: void updated(); diff --git a/core/include/Variable/VariableController.h b/core/include/Variable/VariableController.h index b016617..3e18ad1 100644 --- a/core/include/Variable/VariableController.h +++ b/core/include/Variable/VariableController.h @@ -83,7 +83,7 @@ signals: public slots: /// Request the data loading of the variable whithin range void onRequestDataLoading(QVector > variables, const SqpRange &range, - const SqpRange &oldRange, bool synchronise); + bool synchronise); /** * Creates a new variable and adds it to the model * @param name the name of the new variable diff --git a/core/src/Variable/Variable.cpp b/core/src/Variable/Variable.cpp index 988f06a..8d68fa0 100644 --- a/core/src/Variable/Variable.cpp +++ b/core/src/Variable/Variable.cpp @@ -138,7 +138,6 @@ void Variable::setCacheRange(const SqpRange &cacheRange) noexcept impl->lockWrite(); if (cacheRange != impl->m_CacheRange) { impl->m_CacheRange = cacheRange; - impl->purgeDataSeries(); } impl->unlock(); } @@ -174,6 +173,7 @@ void Variable::mergeDataSeries(std::shared_ptr dataSeries) noexcept impl->unlock(); } + std::shared_ptr Variable::dataSeries() const noexcept { impl->lockRead(); @@ -285,7 +285,7 @@ QVector Variable::provideInCacheRangeList(const SqpRange &range) const if (impl->m_CacheRange != INVALID_RANGE) { - if (this->intersect(range)) { + if (this->cacheIntersect(range)) { if (range.m_TStart <= impl->m_CacheRange.m_TStart && range.m_TEnd >= impl->m_CacheRange.m_TStart && range.m_TEnd < impl->m_CacheRange.m_TEnd) { @@ -313,3 +313,76 @@ QVector Variable::provideInCacheRangeList(const SqpRange &range) const return inCache; } + + +QVector Variable::provideNotInCacheRangeList(const SqpRange &oldRange, + const SqpRange &nextRange) +{ + + // This code assume that cach in contigue. Can return 0, 1 or 2 SqpRange + auto notInCache = QVector{}; + if (oldRange != INVALID_RANGE) { + + if (!oldRange.contains(nextRange)) { + if (nextRange.m_TEnd <= oldRange.m_TStart || nextRange.m_TStart >= oldRange.m_TEnd) { + notInCache << nextRange; + } + else if (nextRange.m_TStart < oldRange.m_TStart + && nextRange.m_TEnd <= oldRange.m_TEnd) { + notInCache << SqpRange{nextRange.m_TStart, oldRange.m_TStart}; + } + else if (nextRange.m_TStart < oldRange.m_TStart && nextRange.m_TEnd > oldRange.m_TEnd) { + notInCache << SqpRange{nextRange.m_TStart, oldRange.m_TStart} + << SqpRange{oldRange.m_TEnd, nextRange.m_TEnd}; + } + else if (nextRange.m_TStart < oldRange.m_TEnd) { + notInCache << SqpRange{oldRange.m_TEnd, nextRange.m_TEnd}; + } + else { + qCCritical(LOG_Variable()) << tr("Detection of unknown case.") + << QThread::currentThread(); + } + } + } + else { + notInCache << nextRange; + } + + return notInCache; +} + +QVector Variable::provideInCacheRangeList(const SqpRange &oldRange, + const SqpRange &nextRange) +{ + // This code assume that cach is contigue. Can return 0 or 1 SqpRange + + auto inCache = QVector{}; + + if (oldRange != INVALID_RANGE) { + + if (oldRange.intersect(nextRange)) { + if (nextRange.m_TStart <= oldRange.m_TStart && nextRange.m_TEnd >= oldRange.m_TStart + && nextRange.m_TEnd < oldRange.m_TEnd) { + inCache << SqpRange{oldRange.m_TStart, nextRange.m_TEnd}; + } + + else if (nextRange.m_TStart >= oldRange.m_TStart + && nextRange.m_TEnd <= oldRange.m_TEnd) { + inCache << nextRange; + } + else if (nextRange.m_TStart > oldRange.m_TStart && nextRange.m_TEnd > oldRange.m_TEnd) { + inCache << SqpRange{nextRange.m_TStart, oldRange.m_TEnd}; + } + else if (nextRange.m_TStart <= oldRange.m_TStart + && nextRange.m_TEnd >= oldRange.m_TEnd) { + inCache << oldRange; + } + else { + qCCritical(LOG_Variable()) << tr("Detection of unknown case.") + << QThread::currentThread(); + } + } + } + + return inCache; +} diff --git a/core/src/Variable/VariableAcquisitionWorker.cpp b/core/src/Variable/VariableAcquisitionWorker.cpp index 5eac38c..8487848 100644 --- a/core/src/Variable/VariableAcquisitionWorker.cpp +++ b/core/src/Variable/VariableAcquisitionWorker.cpp @@ -32,6 +32,10 @@ struct VariableAcquisitionWorker::VariableAcquisitionWorkerPrivate { /// Remove the current request and execute the next one if exist void updateToNextRequest(QUuid vIdentifier); + /// Remove and/or abort all AcqRequest in link with varRequestId + void cancelVarRequest(QUuid varRequestId); + void removeAcqRequest(QUuid acqRequestId); + QMutex m_WorkingMutex; QReadWriteLock m_Lock; @@ -67,7 +71,8 @@ QUuid VariableAcquisitionWorker::pushVariableRequest(QUuid varRequestId, QUuid v // Request creation auto acqRequest = AcquisitionRequest{}; - qCInfo(LOG_VariableAcquisitionWorker()) << tr("TpushVariableRequest ") << vIdentifier; + qCDebug(LOG_VariableAcquisitionWorker()) << tr("PushVariableRequest ") << vIdentifier + << varRequestId; acqRequest.m_VarRequestId = varRequestId; acqRequest.m_vIdentifier = vIdentifier; acqRequest.m_DataProviderParameters = parameters; @@ -85,15 +90,19 @@ QUuid VariableAcquisitionWorker::pushVariableRequest(QUuid varRequestId, QUuid v auto it = impl->m_VIdentifierToCurrrentAcqIdNextIdPairMap.find(vIdentifier); if (it != impl->m_VIdentifierToCurrrentAcqIdNextIdPairMap.cend()) { // A current request already exists, we can replace the next one - auto nextAcqId = it->second.second; - auto acqIdentifierToAcqRequestMapIt = impl->m_AcqIdentifierToAcqRequestMap.find(nextAcqId); + auto oldAcqId = it->second.second; + auto acqIdentifierToAcqRequestMapIt = impl->m_AcqIdentifierToAcqRequestMap.find(oldAcqId); if (acqIdentifierToAcqRequestMapIt != impl->m_AcqIdentifierToAcqRequestMap.cend()) { - auto request = acqIdentifierToAcqRequestMapIt->second; - varRequestIdCanceled = request.m_VarRequestId; + auto oldAcqRequest = acqIdentifierToAcqRequestMapIt->second; + varRequestIdCanceled = oldAcqRequest.m_VarRequestId; } it->second.second = acqRequest.m_AcqIdentifier; impl->unlock(); + + // remove old acqIdentifier from the worker + impl->cancelVarRequest(varRequestIdCanceled); + // impl->m_AcqIdentifierToAcqRequestMap.erase(oldAcqId); } else { // First request for the variable, it must be stored and executed @@ -122,10 +131,7 @@ void VariableAcquisitionWorker::abortProgressRequested(QUuid vIdentifier) impl->unlock(); // Remove the current request from the worker - - impl->lockWrite(); impl->updateToNextRequest(vIdentifier); - impl->unlock(); // notify the request aborting to the provider request.m_Provider->requestDataAborting(currentAcqId); @@ -221,22 +227,28 @@ void VariableAcquisitionWorker::onVariableDataAcquired(QUuid acqIdentifier, // if the counter is 0, we can return data then run the next request if it exists and // removed the finished request if (acqRequest.m_Size == acqRequest.m_Progression) { + auto varId = acqRequest.m_vIdentifier; + auto rangeRequested = acqRequest.m_RangeRequested; + auto cacheRangeRequested = acqRequest.m_CacheRangeRequested; // Return the data aIdToADPVit = impl->m_AcqIdentifierToAcqDataPacketVectorMap.find(acqIdentifier); if (aIdToADPVit != impl->m_AcqIdentifierToAcqDataPacketVectorMap.cend()) { - emit dataProvided(acqRequest.m_vIdentifier, acqRequest.m_RangeRequested, - acqRequest.m_CacheRangeRequested, aIdToADPVit->second); + emit dataProvided(varId, rangeRequested, cacheRangeRequested, aIdToADPVit->second); } + impl->unlock(); // Update to the next request impl->updateToNextRequest(acqRequest.m_vIdentifier); } + else { + impl->unlock(); + } } else { + impl->unlock(); qCWarning(LOG_VariableAcquisitionWorker()) << tr("Impossible to retrieve AcquisitionRequest for the incoming data."); } - impl->unlock(); } void VariableAcquisitionWorker::onExecuteRequest(QUuid acqIdentifier) @@ -296,27 +308,109 @@ void VariableAcquisitionWorker::VariableAcquisitionWorkerPrivate::removeVariable void VariableAcquisitionWorker::VariableAcquisitionWorkerPrivate::updateToNextRequest( QUuid vIdentifier) { + lockRead(); auto it = m_VIdentifierToCurrrentAcqIdNextIdPairMap.find(vIdentifier); if (it != m_VIdentifierToCurrrentAcqIdNextIdPairMap.cend()) { if (it->second.second.isNull()) { + unlock(); // There is no next request, we can remove the variable request removeVariableRequest(vIdentifier); } else { auto acqIdentifierToRemove = it->second.first; // Move the next request to the current request - it->second.first = it->second.second; + auto nextRequestId = it->second.second; + it->second.first = nextRequestId; it->second.second = QUuid(); + unlock(); // Remove AcquisitionRequest and results; + lockWrite(); m_AcqIdentifierToAcqRequestMap.erase(acqIdentifierToRemove); m_AcqIdentifierToAcqDataPacketVectorMap.erase(acqIdentifierToRemove); + unlock(); // Execute the current request QMetaObject::invokeMethod(q, "onExecuteRequest", Qt::QueuedConnection, - Q_ARG(QUuid, it->second.first)); + Q_ARG(QUuid, nextRequestId)); } } else { + unlock(); qCCritical(LOG_VariableAcquisitionWorker()) << tr("Impossible to execute the acquisition on an unfound variable "); } } + +void VariableAcquisitionWorker::VariableAcquisitionWorkerPrivate::cancelVarRequest( + QUuid varRequestId) +{ + qCDebug(LOG_VariableAcquisitionWorker()) + << "VariableAcquisitionWorkerPrivate::cancelVarRequest 0"; + lockRead(); + // get all AcqIdentifier in link with varRequestId + QVector acqIdsToRm; + auto cend = m_AcqIdentifierToAcqRequestMap.cend(); + for (auto it = m_AcqIdentifierToAcqRequestMap.cbegin(); it != cend; ++it) { + if (it->second.m_VarRequestId == varRequestId) { + acqIdsToRm << it->first; + } + } + unlock(); + // run aborting or removing of acqIdsToRm + + for (auto acqId : acqIdsToRm) { + removeAcqRequest(acqId); + } + qCDebug(LOG_VariableAcquisitionWorker()) + << "VariableAcquisitionWorkerPrivate::cancelVarRequest end"; +} + +void VariableAcquisitionWorker::VariableAcquisitionWorkerPrivate::removeAcqRequest( + QUuid acqRequestId) +{ + qCDebug(LOG_VariableAcquisitionWorker()) + << "VariableAcquisitionWorkerPrivate::removeAcqRequest"; + QUuid vIdentifier; + std::shared_ptr provider; + lockRead(); + auto acqIt = m_AcqIdentifierToAcqRequestMap.find(acqRequestId); + if (acqIt != m_AcqIdentifierToAcqRequestMap.cend()) { + vIdentifier = acqIt->second.m_vIdentifier; + provider = acqIt->second.m_Provider; + + auto it = m_VIdentifierToCurrrentAcqIdNextIdPairMap.find(vIdentifier); + if (it != m_VIdentifierToCurrrentAcqIdNextIdPairMap.cend()) { + if (it->second.first == acqRequestId) { + // acqRequest is currently running -> let's aborting it + unlock(); + + // Remove the current request from the worker + updateToNextRequest(vIdentifier); + + // notify the request aborting to the provider + provider->requestDataAborting(acqRequestId); + } + else if (it->second.second == acqRequestId) { + it->second.second = QUuid(); + unlock(); + } + else { + unlock(); + } + } + else { + unlock(); + } + } + else { + unlock(); + } + + lockWrite(); + + m_AcqIdentifierToAcqDataPacketVectorMap.erase(acqRequestId); + m_AcqIdentifierToAcqRequestMap.erase(acqRequestId); + + unlock(); + qCDebug(LOG_VariableAcquisitionWorker()) + << "VariableAcquisitionWorkerPrivate::removeAcqRequest END"; +} diff --git a/core/src/Variable/VariableController.cpp b/core/src/Variable/VariableController.cpp index 7b231da..6ef2097 100644 --- a/core/src/Variable/VariableController.cpp +++ b/core/src/Variable/VariableController.cpp @@ -48,15 +48,17 @@ SqpRange computeSynchroRangeRequested(const SqpRange &varRange, const SqpRange & break; } case AcquisitionZoomType::PanRight: { + auto deltaLeft = graphRange.m_TStart - oldGraphRange.m_TStart; auto deltaRight = graphRange.m_TEnd - oldGraphRange.m_TEnd; - varRangeRequested.m_TStart += deltaRight; + varRangeRequested.m_TStart += deltaLeft; varRangeRequested.m_TEnd += deltaRight; break; } case AcquisitionZoomType::PanLeft: { auto deltaLeft = oldGraphRange.m_TStart - graphRange.m_TStart; + auto deltaRight = oldGraphRange.m_TEnd - graphRange.m_TEnd; varRangeRequested.m_TStart -= deltaLeft; - varRangeRequested.m_TEnd -= deltaLeft; + varRangeRequested.m_TEnd -= deltaRight; break; } case AcquisitionZoomType::Unknown: { @@ -103,9 +105,6 @@ struct VariableController::VariableControllerPrivate { void processRequest(std::shared_ptr var, const SqpRange &rangeRequested, QUuid varRequestId); - QVector provideNotInCacheDateTimeList(std::shared_ptr variable, - const SqpRange &dateTime); - std::shared_ptr findVariable(QUuid vIdentifier); std::shared_ptr retrieveDataSeries(const QVector acqDataPacketVector); @@ -117,6 +116,8 @@ struct VariableController::VariableControllerPrivate { void updateVariableRequest(QUuid varRequestId); void cancelVariableRequest(QUuid varRequestId); + SqpRange getLastRequestedRange(QUuid varId); + QMutex m_WorkingMutex; /// Variable model. The VariableController has the ownership VariableModel *m_VariableModel; @@ -295,22 +296,24 @@ VariableController::createVariable(const QString &name, const QVariantHash &meta void VariableController::onDateTimeOnSelection(const SqpRange &dateTime) { - // TODO check synchronisation and Rescale + // NOTE: Even if acquisition request is aborting, the graphe range will be changed qCDebug(LOG_VariableController()) << "VariableController::onDateTimeOnSelection" << QThread::currentThread()->objectName(); auto selectedRows = impl->m_VariableSelectionModel->selectedRows(); - auto varRequestId = QUuid::createUuid(); + auto variables = QVector >{}; for (const auto &selectedRow : qAsConst(selectedRows)) { if (auto selectedVariable = impl->m_VariableModel->variable(selectedRow.row())) { - selectedVariable->setRange(dateTime); - impl->processRequest(selectedVariable, dateTime, varRequestId); + variables << selectedVariable; // notify that rescale operation has to be done emit rangeChanged(selectedVariable, dateTime); } } - impl->updateVariableRequest(varRequestId); + + if (!variables.isEmpty()) { + this->onRequestDataLoading(variables, dateTime, true); + } } void VariableController::onDataProvided(QUuid vIdentifier, const SqpRange &rangeRequested, @@ -455,8 +458,7 @@ void VariableController::desynchronize(std::shared_ptr variable, } void VariableController::onRequestDataLoading(QVector > variables, - const SqpRange &range, const SqpRange &oldRange, - bool synchronise) + const SqpRange &range, bool synchronise) { // NOTE: oldRange isn't really necessary since oldRange == variable->range(). @@ -549,19 +551,23 @@ AcquisitionZoomType VariableController::getZoomType(const SqpRange &range, const // t1.m_TStart <= t2.m_TStart && t2.m_TEnd <= t1.m_TEnd auto zoomType = AcquisitionZoomType::Unknown; if (range.m_TStart <= oldRange.m_TStart && oldRange.m_TEnd <= range.m_TEnd) { + qCDebug(LOG_VariableController()) << "zoomtype: ZoomOut"; zoomType = AcquisitionZoomType::ZoomOut; } else if (range.m_TStart > oldRange.m_TStart && range.m_TEnd > oldRange.m_TEnd) { + qCDebug(LOG_VariableController()) << "zoomtype: PanRight"; zoomType = AcquisitionZoomType::PanRight; } else if (range.m_TStart < oldRange.m_TStart && range.m_TEnd < oldRange.m_TEnd) { + qCDebug(LOG_VariableController()) << "zoomtype: PanLeft"; zoomType = AcquisitionZoomType::PanLeft; } else if (range.m_TStart > oldRange.m_TStart && oldRange.m_TEnd > range.m_TEnd) { + qCDebug(LOG_VariableController()) << "zoomtype: ZoomIn"; zoomType = AcquisitionZoomType::ZoomIn; } else { - qCCritical(LOG_VariableController()) << "getZoomType: Unknown type detected"; + qCDebug(LOG_VariableController()) << "getZoomType: Unknown type detected"; } return zoomType; } @@ -570,59 +576,67 @@ void VariableController::VariableControllerPrivate::processRequest(std::shared_p const SqpRange &rangeRequested, QUuid varRequestId) { - - // TODO: protect at auto varRequest = VariableRequest{}; - auto varId = m_VariableToIdentifierMap.at(var); - auto varStrategyRangesRequested - = m_VariableCacheStrategy->computeRange(var->range(), rangeRequested); + auto it = m_VariableToIdentifierMap.find(var); + if (it != m_VariableToIdentifierMap.cend()) { - auto notInCacheRangeList = QVector{varStrategyRangesRequested.second}; - auto inCacheRangeList = QVector{}; - if (m_VarIdToVarRequestIdQueueMap.find(varId) == m_VarIdToVarRequestIdQueueMap.cend()) { - notInCacheRangeList = var->provideNotInCacheRangeList(varStrategyRangesRequested.second); - inCacheRangeList = var->provideInCacheRangeList(varStrategyRangesRequested.second); - } + auto varId = it->second; - if (!notInCacheRangeList.empty()) { - varRequest.m_RangeRequested = varStrategyRangesRequested.first; - varRequest.m_CacheRangeRequested = varStrategyRangesRequested.second; + auto oldRange = getLastRequestedRange(varId); - // store VarRequest - storeVariableRequest(varId, varRequestId, varRequest); + // check for update oldRange to the last request range. + if (oldRange == INVALID_RANGE) { + oldRange = var->range(); + } - auto varProvider = m_VariableToProviderMap.at(var); - if (varProvider != nullptr) { - auto varRequestIdCanceled = m_VariableAcquisitionWorker->pushVariableRequest( - varRequestId, varId, varStrategyRangesRequested.first, - varStrategyRangesRequested.second, - DataProviderParameters{std::move(notInCacheRangeList), var->metadata()}, - varProvider); + auto varStrategyRangesRequested + = m_VariableCacheStrategy->computeRange(oldRange, rangeRequested); + + auto notInCacheRangeList + = Variable::provideNotInCacheRangeList(oldRange, varStrategyRangesRequested.second); + auto inCacheRangeList + = Variable::provideInCacheRangeList(oldRange, varStrategyRangesRequested.second); + + if (!notInCacheRangeList.empty()) { + varRequest.m_RangeRequested = varStrategyRangesRequested.first; + varRequest.m_CacheRangeRequested = varStrategyRangesRequested.second; + + // store VarRequest + storeVariableRequest(varId, varRequestId, varRequest); + + auto varProvider = m_VariableToProviderMap.at(var); + if (varProvider != nullptr) { + auto varRequestIdCanceled = m_VariableAcquisitionWorker->pushVariableRequest( + varRequestId, varId, varStrategyRangesRequested.first, + varStrategyRangesRequested.second, + DataProviderParameters{std::move(notInCacheRangeList), var->metadata()}, + varProvider); + + if (!varRequestIdCanceled.isNull()) { + qCInfo(LOG_VariableAcquisitionWorker()) << tr("varRequestIdCanceled: ") + << varRequestIdCanceled; + cancelVariableRequest(varRequestIdCanceled); + } + } + else { + qCCritical(LOG_VariableController()) + << "Impossible to provide data with a null provider"; + } - if (!varRequestIdCanceled.isNull()) { - qCDebug(LOG_VariableAcquisitionWorker()) << tr("vsarRequestIdCanceled: ") - << varRequestIdCanceled; - cancelVariableRequest(varRequestIdCanceled); + if (!inCacheRangeList.empty()) { + emit q->updateVarDisplaying(var, inCacheRangeList.first()); } } else { - qCCritical(LOG_VariableController()) - << "Impossible to provide data with a null provider"; - } - - if (!inCacheRangeList.empty()) { - emit q->updateVarDisplaying(var, inCacheRangeList.first()); + varRequest.m_RangeRequested = varStrategyRangesRequested.first; + varRequest.m_CacheRangeRequested = varStrategyRangesRequested.second; + // store VarRequest + storeVariableRequest(varId, varRequestId, varRequest); + acceptVariableRequest( + varId, var->dataSeries()->subDataSeries(varStrategyRangesRequested.second)); } } - else { - varRequest.m_RangeRequested = varStrategyRangesRequested.first; - varRequest.m_CacheRangeRequested = varStrategyRangesRequested.second; - // store VarRequest - storeVariableRequest(varId, varRequestId, varRequest); - acceptVariableRequest(varId, - var->dataSeries()->subDataSeries(varStrategyRangesRequested.second)); - } } std::shared_ptr @@ -784,26 +798,22 @@ void VariableController::VariableControllerPrivate::updateVariableRequest(QUuid var->setRange(varRequest.m_RangeRequested); var->setCacheRange(varRequest.m_CacheRangeRequested); qCDebug(LOG_VariableController()) << tr("1: onDataProvided") - << varRequest.m_RangeRequested; - qCDebug(LOG_VariableController()) << tr("2: onDataProvided") + << varRequest.m_RangeRequested << varRequest.m_CacheRangeRequested; + qCDebug(LOG_VariableController()) << tr("2: onDataProvided var points before") + << var->nbPoints() + << varRequest.m_DataSeries->nbPoints(); var->mergeDataSeries(varRequest.m_DataSeries); - qCDebug(LOG_VariableController()) << tr("3: onDataProvided"); + qCDebug(LOG_VariableController()) << tr("3: onDataProvided var points after") + << var->nbPoints(); - /// @todo MPL: confirm - // Variable update is notified only if there is no pending request for it - // if - // (m_VarIdToVarRequestIdQueueMap.count(varIdToVarRequestMapIt->first) - // == 0) { emit var->updated(); - // } } else { qCCritical(LOG_VariableController()) << tr("Impossible to update data to a null variable"); } } - // cleaning varRequestId qCDebug(LOG_VariableController()) << tr("0: erase REQUEST in MAP ?") << m_VarRequestIdToVarIdVarRequestMap.size(); @@ -832,9 +842,48 @@ void VariableController::VariableControllerPrivate::cancelVariableRequest(QUuid if (varRequestIdQueue.empty()) { varIdToVarRequestIdQueueMapIt = m_VarIdToVarRequestIdQueueMap.erase(varIdToVarRequestIdQueueMapIt); + + // Recompute if there is any next request based on the removed request. } else { ++varIdToVarRequestIdQueueMapIt; } } } + +SqpRange VariableController::VariableControllerPrivate::getLastRequestedRange(QUuid varId) +{ + auto lastRangeRequested = SqpRange{INVALID_RANGE}; + auto varIdToVarRequestIdQueueMapIt = m_VarIdToVarRequestIdQueueMap.find(varId); + if (varIdToVarRequestIdQueueMapIt != m_VarIdToVarRequestIdQueueMap.cend()) { + auto &varRequestIdQueue = varIdToVarRequestIdQueueMapIt->second; + auto varRequestId = varRequestIdQueue.back(); + auto varRequestIdToVarIdVarRequestMapIt + = m_VarRequestIdToVarIdVarRequestMap.find(varRequestId); + if (varRequestIdToVarIdVarRequestMapIt != m_VarRequestIdToVarIdVarRequestMap.cend()) { + auto &varIdToVarRequestMap = varRequestIdToVarIdVarRequestMapIt->second; + auto varIdToVarRequestMapIt = varIdToVarRequestMap.find(varId); + if (varIdToVarRequestMapIt != varIdToVarRequestMap.cend()) { + auto &varRequest = varIdToVarRequestMapIt->second; + lastRangeRequested = varRequest.m_RangeRequested; + } + else { + qCDebug(LOG_VariableController()) + << tr("Impossible to getLastRequestedRange of a unknown variable id attached " + "to a variableRequestId") + << varRequestId << varId; + } + } + else { + qCCritical(LOG_VariableController()) + << tr("Impossible to getLastRequestedRange of a unknown variableRequestId") + << varRequestId; + } + } + else { + qDebug(LOG_VariableController()) + << tr("Impossible to getLastRequestedRange of a unknown variable id") << varId; + } + + return lastRangeRequested; +} diff --git a/core/tests/Variable/TestVariableSync.cpp b/core/tests/Variable/TestVariableSync.cpp index 61daa00..204826e 100644 --- a/core/tests/Variable/TestVariableSync.cpp +++ b/core/tests/Variable/TestVariableSync.cpp @@ -105,8 +105,7 @@ struct Move : public IOperation { void exec(VariableController &variableController) const override { if (auto variable = variableController.variableModel()->variable(m_Index)) { - variableController.onRequestDataLoading({variable}, m_NewRange, variable->range(), - !m_Shift); + variableController.onRequestDataLoading({variable}, m_NewRange, !m_Shift); } } @@ -171,20 +170,10 @@ private slots: void testSync(); }; -void TestVariableSync::testSync_data() -{ - // ////////////// // - // Test structure // - // ////////////// // - - QTest::addColumn("syncId"); - QTest::addColumn("initialRange"); - QTest::addColumn("iterations"); - - // ////////// // - // Test cases // - // ////////// // +namespace { +void testSyncCase1() +{ // Id used to synchronize variables in the controller auto syncId = QUuid::createUuid(); @@ -254,7 +243,86 @@ void TestVariableSync::testSync_data() // Zoom out moveVar0(range({12, 0}, {18, 0}), range({11, 0}, {17, 0})); - QTest::newRow("sync1") << syncId << initialRange << std::move(iterations); + QTest::newRow("sync1") << syncId << initialRange << std::move(iterations) << 200; +} + +void testSyncCase2() +{ + // Id used to synchronize variables in the controller + auto syncId = QUuid::createUuid(); + + /// Generates a range according to a start time and a end time (the date is the same) + auto dateTime = [](int year, int month, int day, int hours, int minutes, int seconds) { + return DateUtils::secondsSinceEpoch( + QDateTime{{year, month, day}, QTime{hours, minutes, seconds}, Qt::UTC}); + }; + + auto initialRange = SqpRange{dateTime(2017, 1, 1, 12, 0, 0), dateTime(2017, 1, 1, 13, 0, 0)}; + + Iterations iterations{}; + // Creates variables var0 and var1 + iterations.push_back({std::make_shared(0), {{0, initialRange}}}); + iterations.push_back({std::make_shared(1), {{0, initialRange}, {1, initialRange}}}); + + // Adds variables into the sync group (ranges don't need to be tested here) + iterations.push_back({std::make_shared(0, syncId)}); + iterations.push_back({std::make_shared(1, syncId)}); + + + // Moves var0 through several operations: + // - range of var0 changes + // - range or var1 changes according to the previous shift (one hour) + auto moveVar0 = [&iterations](const auto &var0NewRange) { + iterations.push_back( + {std::make_shared(0, var0NewRange), {{0, var0NewRange}, {1, var0NewRange}}}); + }; + moveVar0(SqpRange{dateTime(2017, 1, 1, 12, 0, 0), dateTime(2017, 1, 1, 13, 0, 0)}); + moveVar0(SqpRange{dateTime(2017, 1, 1, 14, 0, 0), dateTime(2017, 1, 1, 15, 0, 0)}); + moveVar0(SqpRange{dateTime(2017, 1, 1, 8, 0, 0), dateTime(2017, 1, 1, 9, 0, 0)}); + // moveVar0(SqpRange{dateTime(2017, 1, 1, 7, 30, 0), dateTime(2017, 1, 1, 9, 30, 0)}); + moveVar0(SqpRange{dateTime(2017, 1, 1, 2, 0, 0), dateTime(2017, 1, 1, 4, 0, 0)}); + moveVar0(SqpRange{dateTime(2017, 1, 1, 6, 0, 0), dateTime(2017, 1, 1, 8, 0, 0)}); + + moveVar0(SqpRange{dateTime(2017, 1, 10, 6, 0, 0), dateTime(2017, 1, 15, 8, 0, 0)}); + moveVar0(SqpRange{dateTime(2017, 1, 17, 6, 0, 0), dateTime(2017, 1, 25, 8, 0, 0)}); + moveVar0(SqpRange{dateTime(2017, 1, 2, 6, 0, 0), dateTime(2017, 1, 8, 8, 0, 0)}); + + moveVar0(SqpRange{dateTime(2017, 4, 10, 6, 0, 0), dateTime(2017, 6, 15, 8, 0, 0)}); + moveVar0(SqpRange{dateTime(2017, 1, 17, 6, 0, 0), dateTime(2017, 2, 25, 8, 0, 0)}); + moveVar0(SqpRange{dateTime(2017, 7, 2, 6, 0, 0), dateTime(2017, 10, 8, 8, 0, 0)}); + moveVar0(SqpRange{dateTime(2017, 4, 10, 6, 0, 0), dateTime(2017, 6, 15, 8, 0, 0)}); + moveVar0(SqpRange{dateTime(2017, 1, 17, 6, 0, 0), dateTime(2017, 2, 25, 8, 0, 0)}); + moveVar0(SqpRange{dateTime(2017, 7, 2, 6, 0, 0), dateTime(2017, 10, 8, 8, 0, 0)}); + moveVar0(SqpRange{dateTime(2017, 4, 10, 6, 0, 0), dateTime(2017, 6, 15, 8, 0, 0)}); + moveVar0(SqpRange{dateTime(2017, 1, 17, 6, 0, 0), dateTime(2017, 2, 25, 8, 0, 0)}); + moveVar0(SqpRange{dateTime(2017, 7, 2, 6, 0, 0), dateTime(2017, 10, 8, 8, 0, 0)}); + moveVar0(SqpRange{dateTime(2017, 4, 10, 6, 0, 0), dateTime(2017, 6, 15, 8, 0, 0)}); + moveVar0(SqpRange{dateTime(2017, 1, 17, 6, 0, 0), dateTime(2017, 2, 25, 8, 0, 0)}); + moveVar0(SqpRange{dateTime(2017, 7, 2, 6, 0, 0), dateTime(2017, 10, 8, 8, 0, 0)}); + + + QTest::newRow("sync2") << syncId << initialRange << iterations << 4000; + // QTest::newRow("sync3") << syncId << initialRange << iterations << 5000; +} +} + +void TestVariableSync::testSync_data() +{ + // ////////////// // + // Test structure // + // ////////////// // + + QTest::addColumn("syncId"); + QTest::addColumn("initialRange"); + QTest::addColumn("iterations"); + QTest::addColumn("operationDelay"); + + // ////////// // + // Test cases // + // ////////// // + + testSyncCase1(); + testSyncCase2(); } void TestVariableSync::testSync() @@ -271,15 +339,8 @@ void TestVariableSync::testSync() // Synchronization group used variableController.onAddSynchronizationGroupId(syncId); - // For each iteration: - // - execute operation - // - compare the variables' state to the expected states - QFETCH(Iterations, iterations); - for (const auto &iteration : iterations) { - iteration.m_Operation->exec(variableController); - QTest::qWait(OPERATION_DELAY); - - for (const auto &expectedRangeEntry : iteration.m_ExpectedRanges) { + auto validateRanges = [&variableController](const auto &expectedRanges) { + for (const auto &expectedRangeEntry : expectedRanges) { auto variableIndex = expectedRangeEntry.first; auto expectedRange = expectedRangeEntry.second; @@ -297,12 +358,31 @@ void TestVariableSync::testSync() auto it = dataSeries->xAxisRange(range.m_TStart, range.m_TEnd); auto expectedValues = values(range); + qInfo() << std::distance(it.first, it.second) << expectedValues.size(); QVERIFY(std::equal(it.first, it.second, expectedValues.cbegin(), expectedValues.cend(), [](const auto &dataSeriesIt, const auto &expectedValue) { return dataSeriesIt.value() == expectedValue; })); } + }; + + // For each iteration: + // - execute operation + // - compare the variables' state to the expected states + QFETCH(Iterations, iterations); + QFETCH(int, operationDelay); + for (const auto &iteration : iterations) { + iteration.m_Operation->exec(variableController); + QTest::qWait(operationDelay); + + validateRanges(iteration.m_ExpectedRanges); + } + + for (const auto &iteration : iterations) { + iteration.m_Operation->exec(variableController); } + QTest::qWait(operationDelay); + validateRanges(iterations.back().m_ExpectedRanges); } QTEST_MAIN(TestVariableSync) diff --git a/gui/include/Visualization/VisualizationGraphWidget.h b/gui/include/Visualization/VisualizationGraphWidget.h index 5b7b250..98a1c1d 100644 --- a/gui/include/Visualization/VisualizationGraphWidget.h +++ b/gui/include/Visualization/VisualizationGraphWidget.h @@ -39,7 +39,6 @@ public: /// Removes a variable from the graph void removeVariable(std::shared_ptr variable) noexcept; - void setRange(std::shared_ptr variable, const SqpRange &range); void setYRange(const SqpRange &range); SqpRange graphRange() const noexcept; void setGraphRange(const SqpRange &range); @@ -54,7 +53,7 @@ public: signals: void synchronize(const SqpRange &range, const SqpRange &oldRange); void requestDataLoading(QVector > variable, const SqpRange &range, - const SqpRange &oldRange, bool synchronise); + bool synchronise); /// Signal emitted when the variable is about to be removed from the graph void variableAboutToBeRemoved(std::shared_ptr var); diff --git a/gui/src/Visualization/VisualizationGraphWidget.cpp b/gui/src/Visualization/VisualizationGraphWidget.cpp index fc99f91..50d0562 100644 --- a/gui/src/Visualization/VisualizationGraphWidget.cpp +++ b/gui/src/Visualization/VisualizationGraphWidget.cpp @@ -119,14 +119,11 @@ void VisualizationGraphWidget::addVariable(std::shared_ptr variable, S connect(variable.get(), SIGNAL(updated()), this, SLOT(onDataCacheVariableUpdated())); - auto varRange = variable->range(); - this->enableAcquisition(false); this->setGraphRange(range); this->enableAcquisition(true); - emit requestDataLoading(QVector >() << variable, range, varRange, - false); + emit requestDataLoading(QVector >() << variable, range, false); emit variableAdded(variable); } @@ -155,17 +152,6 @@ void VisualizationGraphWidget::removeVariable(std::shared_ptr variable ui->widget->replot(); } -void VisualizationGraphWidget::setRange(std::shared_ptr variable, const SqpRange &range) -{ - // Note: in case of different axes that depends on variable, we could start with a code like - // that: - // auto componentsIt = impl->m_VariableToPlotMultiMap.equal_range(variable); - // for (auto it = componentsIt.first; it != componentsIt.second;) { - // } - ui->widget->xAxis->setRange(range.m_TStart, range.m_TEnd); - ui->widget->replot(); -} - void VisualizationGraphWidget::setYRange(const SqpRange &range) { ui->widget->yAxis->setRange(range.m_TStart, range.m_TEnd); @@ -282,7 +268,7 @@ void VisualizationGraphWidget::onRangeChanged(const QCPRange &t1, const QCPRange it != end; it = impl->m_VariableToPlotMultiMap.upper_bound(it->first)) { variableUnderGraphVector.push_back(it->first); } - emit requestDataLoading(std::move(variableUnderGraphVector), graphRange, oldGraphRange, + emit requestDataLoading(std::move(variableUnderGraphVector), graphRange, !impl->m_IsCalibration); if (!impl->m_IsCalibration) { diff --git a/gui/src/Visualization/VisualizationZoneWidget.cpp b/gui/src/Visualization/VisualizationZoneWidget.cpp index aacba29..7d1998a 100644 --- a/gui/src/Visualization/VisualizationZoneWidget.cpp +++ b/gui/src/Visualization/VisualizationZoneWidget.cpp @@ -153,8 +153,9 @@ VisualizationGraphWidget *VisualizationZoneWidget::createGraph(std::shared_ptrm_Variable && graphWidget->contains(*impl->m_Variable)) { - graphWidget->setRange(impl->m_Variable, impl->m_Range); + graphWidget->enableAcquisition(false); + graphWidget->setGraphRange(impl->m_Range); + graphWidget->enableAcquisition(true); } } else { diff --git a/plugins/amda/tests/TestAmdaAcquisition.cpp b/plugins/amda/tests/TestAmdaAcquisition.cpp index 5f0bc89..596f8b5 100644 --- a/plugins/amda/tests/TestAmdaAcquisition.cpp +++ b/plugins/amda/tests/TestAmdaAcquisition.cpp @@ -123,8 +123,7 @@ void TestAmdaAcquisition::testAcquisition() auto nextSqpR = SqpRange{DateUtils::secondsSinceEpoch(tStart), DateUtils::secondsSinceEpoch(tEnd)}; - vc.onRequestDataLoading(QVector >{} << var, nextSqpR, - var->range(), true); + vc.onRequestDataLoading(QVector >{} << var, nextSqpR, true); QEventLoop loop; QTimer::singleShot(timeToWaitMs, &loop, &QEventLoop::quit); diff --git a/plugins/mockplugin/include/CosinusProvider.h b/plugins/mockplugin/include/CosinusProvider.h index 2260032..584d9b0 100644 --- a/plugins/mockplugin/include/CosinusProvider.h +++ b/plugins/mockplugin/include/CosinusProvider.h @@ -26,6 +26,11 @@ public: void requestDataAborting(QUuid acqIdentifier) override; + /// Provide data + std::shared_ptr provideDataSeries(const SqpRange &dataRangeRequested, + const QVariantHash &data); + + private: std::shared_ptr retrieveData(QUuid acqIdentifier, const SqpRange &dataRangeRequested, const QVariantHash &data); diff --git a/plugins/mockplugin/src/CosinusProvider.cpp b/plugins/mockplugin/src/CosinusProvider.cpp index ed23a60..827f7f7 100644 --- a/plugins/mockplugin/src/CosinusProvider.cpp +++ b/plugins/mockplugin/src/CosinusProvider.cpp @@ -145,8 +145,9 @@ std::shared_ptr CosinusProvider::retrieveData(QUuid acqIdentifier, progress = currentProgress; emit dataProvidedProgress(acqIdentifier, progress); - qCInfo(LOG_CosinusProvider()) << "TORM: CosinusProvider::retrieveData" - << QThread::currentThread()->objectName() << progress; + qCDebug(LOG_CosinusProvider()) << "TORM: CosinusProvider::retrieveData" + << QThread::currentThread()->objectName() + << progress; // NOTE: Try to use multithread if possible } } @@ -179,7 +180,6 @@ void CosinusProvider::requestDataLoading(QUuid acqIdentifier, for (const auto &dateTime : qAsConst(times)) { if (m_VariableToEnableProvider[acqIdentifier]) { auto scalarSeries = this->retrieveData(acqIdentifier, dateTime, parameters.m_Data); - qCDebug(LOG_CosinusProvider()) << "TORM: CosinusProvider::dataProvided"; emit dataProvided(acqIdentifier, scalarSeries, dateTime); } } @@ -187,7 +187,6 @@ void CosinusProvider::requestDataLoading(QUuid acqIdentifier, void CosinusProvider::requestDataAborting(QUuid acqIdentifier) { - // TODO: Add Mutex qCDebug(LOG_CosinusProvider()) << "CosinusProvider::requestDataAborting" << acqIdentifier << QThread::currentThread()->objectName(); auto it = m_VariableToEnableProvider.find(acqIdentifier); @@ -195,7 +194,18 @@ void CosinusProvider::requestDataAborting(QUuid acqIdentifier) it.value() = false; } else { - qCWarning(LOG_CosinusProvider()) + qCDebug(LOG_CosinusProvider()) << tr("Aborting progression of inexistant identifier detected !!!"); } } + +std::shared_ptr CosinusProvider::provideDataSeries(const SqpRange &dataRangeRequested, + const QVariantHash &data) +{ + auto uid = QUuid::createUuid(); + m_VariableToEnableProvider[uid] = true; + auto dataSeries = this->retrieveData(uid, dataRangeRequested, data); + + m_VariableToEnableProvider.remove(uid); + return dataSeries; +} diff --git a/plugins/mockplugin/tests/TestCosinusAcquisition.cpp b/plugins/mockplugin/tests/TestCosinusAcquisition.cpp index 472a671..8d81fb3 100644 --- a/plugins/mockplugin/tests/TestCosinusAcquisition.cpp +++ b/plugins/mockplugin/tests/TestCosinusAcquisition.cpp @@ -24,9 +24,6 @@ const auto TESTS_RESOURCES_PATH = QFileInfo{ /// Format of dates in data files const auto DATETIME_FORMAT = QStringLiteral("yyyy/MM/dd hh:mm:ss:zzz"); -/// Delay after each operation on the variable before validating it (in ms) -const auto OPERATION_DELAY = 250; - /** * Verifies that the data in the candidate series are identical to the data in the reference series * in a specific range @@ -45,6 +42,9 @@ bool checkDataSeries(std::shared_ptr candidate, const SqpRange &ran auto referenceIt = reference->xAxisRange(range.m_TStart, range.m_TEnd); + qInfo() << "candidateSize" << std::distance(candidate->cbegin(), candidate->cend()); + qInfo() << "refSize" << std::distance(referenceIt.first, referenceIt.second); + return std::equal(candidate->cbegin(), candidate->cend(), referenceIt.first, referenceIt.second, [](const auto &it1, const auto &it2) { // - milliseconds precision for time @@ -54,29 +54,6 @@ bool checkDataSeries(std::shared_ptr candidate, const SqpRange &ran }); } -/// Generates the data series from the reading of a data stream -std::shared_ptr readDataStream(QTextStream &stream) -{ - std::vector xAxisData, valuesData; - - QString line{}; - while (stream.readLineInto(&line)) { - // Separates date (x-axis data) to value data - auto splitLine = line.split('\t'); - if (splitLine.size() == 2) { - // Converts datetime to double - auto dateTime = QDateTime::fromString(splitLine[0], DATETIME_FORMAT); - dateTime.setTimeSpec(Qt::UTC); - xAxisData.push_back(DateUtils::secondsSinceEpoch(dateTime)); - - valuesData.push_back(splitLine[1].toDouble()); - } - } - - return std::make_shared(std::move(xAxisData), std::move(valuesData), - Unit{{}, true}, Unit{}); -} - } // namespace /** @@ -99,8 +76,9 @@ void TestCosinusAcquisition::testAcquisition_data() // Test structure // // ////////////// // - QTest::addColumn("dataFilename"); // File containing expected data of acquisitions - QTest::addColumn("initialRange"); // First acquisition + QTest::addColumn("referenceRange"); // Range for generating reference series + QTest::addColumn("initialRange"); // First acquisition + QTest::addColumn("operationDelay"); // Acquisitions to make QTest::addColumn >("operations"); // Acquisitions to make // ////////// // @@ -113,8 +91,8 @@ void TestCosinusAcquisition::testAcquisition_data() }; QTest::newRow("cosinus") - << "Cosinus_100Hz_20170101_1200_20170101_1300.txt" - << SqpRange{dateTime(2017, 1, 1, 12, 30, 0), dateTime(2017, 1, 1, 12, 35, 1)} + << SqpRange{dateTime(2017, 1, 1, 12, 0, 0), dateTime(2017, 1, 1, 13, 0, 0)} + << SqpRange{dateTime(2017, 1, 1, 12, 30, 0), dateTime(2017, 1, 1, 12, 35, 1)} << 250 << std::vector{ // Pan (jump) left SqpRange{dateTime(2017, 1, 1, 12, 45, 0), dateTime(2017, 1, 1, 12, 50, 0)}, @@ -130,55 +108,76 @@ void TestCosinusAcquisition::testAcquisition_data() SqpRange{dateTime(2017, 1, 1, 12, 17, 30), dateTime(2017, 1, 1, 12, 19, 30)}, // Zoom out SqpRange{dateTime(2017, 1, 1, 12, 12, 30), dateTime(2017, 1, 1, 12, 24, 30)}}; + + QTest::newRow("cosinus_big") + << SqpRange{dateTime(2017, 1, 1, 1, 0, 0), dateTime(2017, 1, 5, 13, 0, 0)} + << SqpRange{dateTime(2017, 1, 2, 6, 30, 0), dateTime(2017, 1, 2, 18, 30, 0)} << 5000 + << std::vector{ + // Pan (jump) left + SqpRange{dateTime(2017, 1, 1, 13, 30, 0), dateTime(2017, 1, 1, 18, 30, 0)}, + // Pan (jump) right + SqpRange{dateTime(2017, 1, 3, 4, 30, 0), dateTime(2017, 1, 3, 10, 30, 0)}, + // Pan (overlay) right + SqpRange{dateTime(2017, 1, 3, 8, 30, 0), dateTime(2017, 1, 3, 12, 30, 0)}, + // Pan (overlay) left + SqpRange{dateTime(2017, 1, 2, 8, 30, 0), dateTime(2017, 1, 3, 10, 30, 0)}, + // Pan (overlay) left + SqpRange{dateTime(2017, 1, 1, 12, 30, 0), dateTime(2017, 1, 3, 5, 30, 0)}, + // Zoom in + SqpRange{dateTime(2017, 1, 2, 2, 30, 0), dateTime(2017, 1, 2, 8, 30, 0)}, + // Zoom out + SqpRange{dateTime(2017, 1, 1, 14, 30, 0), dateTime(2017, 1, 3, 12, 30, 0)}}; } void TestCosinusAcquisition::testAcquisition() { - // Retrieves data file - QFETCH(QString, dataFilename); - - auto dataFilePath = QFileInfo{TESTS_RESOURCES_PATH, dataFilename}.absoluteFilePath(); - QFile dataFile{dataFilePath}; - - if (dataFile.open(QFile::ReadOnly)) { - // Generates data series to compare with - QTextStream dataStream{&dataFile}; - auto dataSeries = readDataStream(dataStream); - - /// Lambda used to validate a variable at each step - auto validateVariable = [dataSeries](std::shared_ptr variable, - const SqpRange &range) { - // Checks that the variable's range has changed - QCOMPARE(variable->range(), range); - - // Checks the variable's data series - QVERIFY(checkDataSeries(variable->dataSeries(), variable->cacheRange(), dataSeries)); - }; - - // Creates variable - QFETCH(SqpRange, initialRange); - sqpApp->timeController().onTimeToUpdate(initialRange); - auto provider = std::make_shared(); - auto variable = sqpApp->variableController().createVariable( - "MMS", {{COSINUS_TYPE_KEY, "scalar"}, {COSINUS_FREQUENCY_KEY, 100.}}, provider); - - QTest::qWait(OPERATION_DELAY); - validateVariable(variable, initialRange); - - // Makes operations on the variable - QFETCH(std::vector, operations); - for (const auto &operation : operations) { - // Asks request on the variable and waits during its execution - sqpApp->variableController().onRequestDataLoading({variable}, operation, - variable->range(), true); - - QTest::qWait(OPERATION_DELAY); - validateVariable(variable, operation); - } + // Retrieves reference range + QFETCH(SqpRange, referenceRange); + CosinusProvider referenceProvider{}; + auto dataSeries = referenceProvider.provideDataSeries( + referenceRange, {{COSINUS_TYPE_KEY, "scalar"}, {COSINUS_FREQUENCY_KEY, 100.}}); + + auto end = dataSeries->cend() - 1; + qInfo() << dataSeries->nbPoints() << dataSeries->cbegin()->x() << end->x(); + + /// Lambda used to validate a variable at each step + auto validateVariable + = [dataSeries](std::shared_ptr variable, const SqpRange &range) { + // Checks that the variable's range has changed + QCOMPARE(variable->range(), range); + + // Checks the variable's data series + QVERIFY(checkDataSeries(variable->dataSeries(), variable->cacheRange(), dataSeries)); + }; + + // Creates variable + QFETCH(SqpRange, initialRange); + sqpApp->timeController().onTimeToUpdate(initialRange); + auto provider = std::make_shared(); + auto variable = sqpApp->variableController().createVariable( + "MMS", {{COSINUS_TYPE_KEY, "scalar"}, {COSINUS_FREQUENCY_KEY, 100.}}, provider); + + QFETCH(int, operationDelay); + QTest::qWait(operationDelay); + validateVariable(variable, initialRange); + + // Makes operations on the variable + QFETCH(std::vector, operations); + for (const auto &operation : operations) { + // Asks request on the variable and waits during its execution + sqpApp->variableController().onRequestDataLoading({variable}, operation, true); + + QTest::qWait(operationDelay); + validateVariable(variable, operation); } - else { - QFAIL("Can't read input data file"); + + + for (const auto &operation : operations) { + // Asks request on the variable and waits during its execution + sqpApp->variableController().onRequestDataLoading({variable}, operation, true); } + QTest::qWait(operationDelay); + validateVariable(variable, operations.back()); } int main(int argc, char *argv[])