VariableController.cpp
442 lines
| 16.2 KiB
| text/x-c
|
CppLexer
r219 | #include <Variable/Variable.h> | |||
r510 | #include <Variable/VariableAcquisitionWorker.h> | |||
#include <Variable/VariableCacheStrategy.h> | ||||
|
r106 | #include <Variable/VariableController.h> | ||
|
r108 | #include <Variable/VariableModel.h> | ||
|
r683 | #include <Variable/VariableSynchronizer.h> | ||
|
r106 | |||
|
r682 | #include <Data/AcquisitionUtils.h> | ||
|
r154 | #include <Data/DataProviderParameters.h> | ||
#include <Data/IDataProvider.h> | ||||
#include <Data/IDataSeries.h> | ||||
r585 | #include <Data/VariableRequest.h> | |||
r179 | #include <Time/TimeController.h> | |||
|
r154 | |||
|
r106 | #include <QMutex> | ||
#include <QThread> | ||||
|
r347 | #include <QUuid> | ||
r281 | #include <QtCore/QItemSelectionModel> | |||
|
r106 | |||
r209 | #include <unordered_map> | |||
|
r106 | Q_LOGGING_CATEGORY(LOG_VariableController, "VariableController") | ||
r510 | namespace { | |||
r516 | SqpRange computeSynchroRangeRequested(const SqpRange &varRange, const SqpRange &graphRange, | |||
r510 | const SqpRange &oldGraphRange) | |||
{ | ||||
|
r682 | auto zoomType = AcquisitionUtils::getZoomType(graphRange, oldGraphRange); | ||
r510 | ||||
auto varRangeRequested = varRange; | ||||
switch (zoomType) { | ||||
case AcquisitionZoomType::ZoomIn: { | ||||
r516 | auto deltaLeft = graphRange.m_TStart - oldGraphRange.m_TStart; | |||
auto deltaRight = oldGraphRange.m_TEnd - graphRange.m_TEnd; | ||||
r510 | varRangeRequested.m_TStart += deltaLeft; | |||
varRangeRequested.m_TEnd -= deltaRight; | ||||
break; | ||||
} | ||||
case AcquisitionZoomType::ZoomOut: { | ||||
r516 | auto deltaLeft = oldGraphRange.m_TStart - graphRange.m_TStart; | |||
auto deltaRight = graphRange.m_TEnd - oldGraphRange.m_TEnd; | ||||
r510 | varRangeRequested.m_TStart -= deltaLeft; | |||
varRangeRequested.m_TEnd += deltaRight; | ||||
break; | ||||
} | ||||
case AcquisitionZoomType::PanRight: { | ||||
r516 | auto deltaRight = graphRange.m_TEnd - oldGraphRange.m_TEnd; | |||
r510 | varRangeRequested.m_TStart += deltaRight; | |||
varRangeRequested.m_TEnd += deltaRight; | ||||
break; | ||||
} | ||||
case AcquisitionZoomType::PanLeft: { | ||||
r516 | auto deltaLeft = oldGraphRange.m_TStart - graphRange.m_TStart; | |||
r510 | varRangeRequested.m_TStart -= deltaLeft; | |||
varRangeRequested.m_TEnd -= deltaLeft; | ||||
break; | ||||
} | ||||
case AcquisitionZoomType::Unknown: { | ||||
qCCritical(LOG_VariableController()) | ||||
<< VariableController::tr("Impossible to synchronize: zoom type unknown"); | ||||
break; | ||||
} | ||||
default: | ||||
qCCritical(LOG_VariableController()) << VariableController::tr( | ||||
"Impossible to synchronize: zoom type not take into account"); | ||||
// No action | ||||
break; | ||||
} | ||||
return varRangeRequested; | ||||
} | ||||
|
r682 | } // namespace | ||
r510 | ||||
|
r106 | struct VariableController::VariableControllerPrivate { | ||
|
r148 | explicit VariableControllerPrivate(VariableController *parent) | ||
r209 | : m_WorkingMutex{}, | |||
m_VariableModel{new VariableModel{parent}}, | ||||
r281 | m_VariableSelectionModel{new QItemSelectionModel{m_VariableModel, parent}}, | |||
r510 | m_VariableCacheStrategy{std::make_unique<VariableCacheStrategy>()}, | |||
r538 | m_VariableAcquisitionWorker{std::make_unique<VariableAcquisitionWorker>()}, | |||
|
r683 | m_VariableSynchronizer{std::make_unique<VariableSynchronizer>()}, | ||
r538 | q{parent} | |||
r510 | { | |||
m_VariableAcquisitionWorker->moveToThread(&m_VariableAcquisitionWorkerThread); | ||||
m_VariableAcquisitionWorkerThread.setObjectName("VariableAcquisitionWorkerThread"); | ||||
} | ||||
virtual ~VariableControllerPrivate() | ||||
|
r106 | { | ||
r510 | qCDebug(LOG_VariableController()) << tr("VariableControllerPrivate destruction"); | |||
m_VariableAcquisitionWorkerThread.quit(); | ||||
m_VariableAcquisitionWorkerThread.wait(); | ||||
|
r106 | } | ||
|
r682 | void processRequest(std::shared_ptr<Variable> variable, const SqpRange &rangeRequested) | ||
{ | ||||
Q_ASSERT(variable != nullptr); | ||||
r510 | ||||
|
r682 | if (!m_VariableModel->containsVariable(variable)) { | ||
qCCritical(LOG_VariableController()) | ||||
<< QObject::tr("Can't process request for variable %1: variable is not registered") | ||||
.arg(variable->name()); | ||||
return; | ||||
} | ||||
r510 | ||||
|
r683 | variable->setRange(rangeRequested); | ||
|
r682 | // Gets ranges in/out of variable range | ||
auto requestRange | ||||
= m_VariableCacheStrategy->computeStrategyRanges(variable->range(), rangeRequested); | ||||
auto notInCacheRanges = variable->provideNotInCacheRangeList(requestRange.second); | ||||
// Creates request for out-of-cache ranges | ||||
if (!notInCacheRanges.isEmpty()) { | ||||
// Gets provider for request | ||||
if (auto provider = m_Providers.at(variable)) { | ||||
|
r683 | DataProviderParameters providerParameters{std::move(notInCacheRanges), | ||
variable->metadata()}; | ||||
VariableRequest request{requestRange.first, requestRange.second, provider, | ||||
std::move(providerParameters)}; | ||||
m_VariableAcquisitionWorker->pushVariableRequest(variable, std::move(request)); | ||||
|
r682 | } | ||
|
r683 | // Calls UI update for in-cache range | ||
auto inCacheRanges = variable->provideInCacheRangeList(requestRange.second); | ||||
if (!inCacheRanges.isEmpty()) { | ||||
emit q->updateVarDisplaying(variable, inCacheRanges.first()); | ||||
} | ||||
|
r682 | } | ||
else { | ||||
|
r683 | // No request to make: we simply update variable ranges | ||
variable->setCacheRange(requestRange.second); | ||||
emit variable->updated(); | ||||
|
r682 | } | ||
} | ||||
r510 | ||||
|
r682 | void registerProvider(std::shared_ptr<IDataProvider> provider) | ||
{ | ||||
Q_ASSERT(provider != nullptr); | ||||
connect(provider.get(), &IDataProvider::dataProvided, m_VariableAcquisitionWorker.get(), | ||||
|
r683 | &VariableAcquisitionWorker::onDataAcquired); | ||
|
r682 | connect(provider.get(), &IDataProvider::dataProvidedProgress, | ||
m_VariableAcquisitionWorker.get(), | ||||
&VariableAcquisitionWorker::onVariableRetrieveDataInProgress); | ||||
} | ||||
r510 | ||||
|
r106 | QMutex m_WorkingMutex; | ||
|
r148 | /// Variable model. The VariableController has the ownership | ||
VariableModel *m_VariableModel; | ||||
r281 | QItemSelectionModel *m_VariableSelectionModel; | |||
r179 | ||||
r181 | TimeController *m_TimeController{nullptr}; | |||
r510 | std::unique_ptr<VariableCacheStrategy> m_VariableCacheStrategy; | |||
std::unique_ptr<VariableAcquisitionWorker> m_VariableAcquisitionWorker; | ||||
QThread m_VariableAcquisitionWorkerThread; | ||||
r219 | ||||
|
r683 | /// Handler for variables synchronization | ||
std::unique_ptr<VariableSynchronizer> m_VariableSynchronizer; | ||||
|
r682 | std::unordered_map<std::shared_ptr<Variable>, std::shared_ptr<IDataProvider> > m_Providers; | ||
r538 | ||||
VariableController *q; | ||||
|
r106 | }; | ||
VariableController::VariableController(QObject *parent) | ||||
|
r148 | : QObject{parent}, impl{spimpl::make_unique_impl<VariableControllerPrivate>(this)} | ||
|
r106 | { | ||
|
r680 | qCDebug(LOG_VariableController()) | ||
<< tr("VariableController construction") << QThread::currentThread(); | ||||
r388 | ||||
connect(impl->m_VariableModel, &VariableModel::abortProgessRequested, this, | ||||
&VariableController::onAbortProgressRequested); | ||||
r510 | ||||
connect(impl->m_VariableAcquisitionWorker.get(), &VariableAcquisitionWorker::dataProvided, this, | ||||
&VariableController::onDataProvided); | ||||
connect(impl->m_VariableAcquisitionWorker.get(), | ||||
&VariableAcquisitionWorker::variableRequestInProgress, this, | ||||
&VariableController::onVariableRetrieveDataInProgress); | ||||
connect(&impl->m_VariableAcquisitionWorkerThread, &QThread::started, | ||||
impl->m_VariableAcquisitionWorker.get(), &VariableAcquisitionWorker::initialize); | ||||
connect(&impl->m_VariableAcquisitionWorkerThread, &QThread::finished, | ||||
impl->m_VariableAcquisitionWorker.get(), &VariableAcquisitionWorker::finalize); | ||||
impl->m_VariableAcquisitionWorkerThread.start(); | ||||
|
r106 | } | ||
VariableController::~VariableController() | ||||
{ | ||||
|
r680 | qCDebug(LOG_VariableController()) | ||
<< tr("VariableController destruction") << QThread::currentThread(); | ||||
|
r106 | this->waitForFinish(); | ||
} | ||||
|
r154 | VariableModel *VariableController::variableModel() noexcept | ||
|
r108 | { | ||
|
r154 | return impl->m_VariableModel; | ||
|
r108 | } | ||
r281 | QItemSelectionModel *VariableController::variableSelectionModel() noexcept | |||
{ | ||||
return impl->m_VariableSelectionModel; | ||||
} | ||||
r179 | void VariableController::setTimeController(TimeController *timeController) noexcept | |||
{ | ||||
impl->m_TimeController = timeController; | ||||
} | ||||
|
r650 | std::shared_ptr<Variable> | ||
VariableController::cloneVariable(std::shared_ptr<Variable> variable) noexcept | ||||
{ | ||||
|
r654 | if (impl->m_VariableModel->containsVariable(variable)) { | ||
// Clones variable | ||||
auto duplicate = variable->clone(); | ||||
|
r651 | |||
|
r654 | // Adds clone to model | ||
impl->m_VariableModel->addVariable(duplicate); | ||||
|
r652 | |||
|
r656 | // Registers provider | ||
|
r682 | auto variableProvider = impl->m_Providers.at(variable); | ||
|
r656 | auto duplicateProvider = variableProvider != nullptr ? variableProvider->clone() : nullptr; | ||
|
r682 | impl->m_Providers[duplicate] = duplicateProvider; | ||
|
r656 | if (duplicateProvider) { | ||
impl->registerProvider(duplicateProvider); | ||||
} | ||||
|
r654 | return duplicate; | ||
} | ||||
else { | ||||
qCCritical(LOG_VariableController()) | ||||
<< tr("Can't create duplicate of variable %1: variable not registered in the model") | ||||
.arg(variable->name()); | ||||
return nullptr; | ||||
} | ||||
|
r650 | } | ||
|
r303 | void VariableController::deleteVariable(std::shared_ptr<Variable> variable) noexcept | ||
{ | ||||
if (!variable) { | ||||
qCCritical(LOG_VariableController()) << "Can't delete variable: variable is null"; | ||||
return; | ||||
} | ||||
|
r309 | // Spreads in SciQlop that the variable will be deleted, so that potential receivers can | ||
// make some treatments before the deletion | ||||
emit variableAboutToBeDeleted(variable); | ||||
|
r304 | |||
|
r684 | // Deletes from synchronization group | ||
impl->m_VariableSynchronizer->removeVariable(variable); | ||||
// Cancels pending requests | ||||
impl->m_VariableAcquisitionWorker->cancelVariableRequest(variable); | ||||
|
r304 | // Deletes provider | ||
|
r682 | auto nbProvidersDeleted = impl->m_Providers.erase(variable); | ||
|
r304 | qCDebug(LOG_VariableController()) | ||
<< tr("Number of providers deleted for variable %1: %2") | ||||
.arg(variable->name(), QString::number(nbProvidersDeleted)); | ||||
|
r303 | |||
|
r305 | |||
|
r306 | // Deletes from model | ||
impl->m_VariableModel->deleteVariable(variable); | ||||
} | ||||
|
r303 | |||
void VariableController::deleteVariables( | ||||
const QVector<std::shared_ptr<Variable> > &variables) noexcept | ||||
{ | ||||
for (auto variable : qAsConst(variables)) { | ||||
deleteVariable(variable); | ||||
} | ||||
} | ||||
r388 | void VariableController::abortProgress(std::shared_ptr<Variable> variable) | |||
{ | ||||
} | ||||
r553 | std::shared_ptr<Variable> | |||
VariableController::createVariable(const QString &name, const QVariantHash &metadata, | ||||
std::shared_ptr<IDataProvider> provider) noexcept | ||||
|
r143 | { | ||
r179 | if (!impl->m_TimeController) { | |||
qCCritical(LOG_VariableController()) | ||||
<< tr("Impossible to create variable: The time controller is null"); | ||||
r553 | return nullptr; | |||
r179 | } | |||
r510 | auto range = impl->m_TimeController->dateTime(); | |||
|
r377 | |||
r510 | if (auto newVariable = impl->m_VariableModel->createVariable(name, range, metadata)) { | |||
// Associate the provider | ||||
|
r683 | auto newVariableProvider = provider != nullptr ? provider->clone() : nullptr; | ||
impl->m_Providers[newVariable] = newVariableProvider; | ||||
if (newVariableProvider) { | ||||
impl->registerProvider(newVariableProvider); | ||||
} | ||||
r293 | ||||
|
r682 | impl->processRequest(newVariable, range); | ||
r553 | ||||
return newVariable; | ||||
|
r154 | } | ||
|
r143 | } | ||
r471 | void VariableController::onDateTimeOnSelection(const SqpRange &dateTime) | |||
r281 | { | |||
r585 | // TODO check synchronisation and Rescale | |||
|
r680 | qCDebug(LOG_VariableController()) | ||
<< "VariableController::onDateTimeOnSelection" << QThread::currentThread()->objectName(); | ||||
r281 | auto selectedRows = impl->m_VariableSelectionModel->selectedRows(); | |||
for (const auto &selectedRow : qAsConst(selectedRows)) { | ||||
if (auto selectedVariable = impl->m_VariableModel->variable(selectedRow.row())) { | ||||
r510 | selectedVariable->setRange(dateTime); | |||
|
r682 | impl->processRequest(selectedVariable, dateTime); | ||
r408 | ||||
// notify that rescale operation has to be done | ||||
r403 | emit rangeChanged(selectedVariable, dateTime); | |||
r281 | } | |||
} | ||||
} | ||||
|
r683 | void VariableController::onDataProvided(std::shared_ptr<Variable> variable, VariableRequest request) | ||
r369 | { | |||
|
r683 | Q_ASSERT(variable != nullptr); | ||
if (!impl->m_VariableModel->containsVariable(variable)) { | ||||
qCCritical(LOG_VariableController()) | ||||
<< QObject::tr("Can't update date of variable %1: variable is not registered (anymore)") | ||||
.arg(variable->name()); | ||||
return; | ||||
r510 | } | |||
|
r683 | |||
variable->setCacheRange(request.m_CacheRangeRequested); | ||||
variable->mergeDataSeries(request.m_Result); | ||||
emit variable->updated(); | ||||
r510 | } | |||
r369 | ||||
r510 | void VariableController::onVariableRetrieveDataInProgress(QUuid identifier, double progress) | |||
{ | ||||
|
r683 | /// @todo ALX | ||
// if (auto var = impl->findVariable(identifier)) { | ||||
// impl->m_VariableModel->setDataProgress(var, progress); | ||||
// } | ||||
// else { | ||||
// qCCritical(LOG_VariableController()) | ||||
// << tr("Impossible to notify progression of a null variable"); | ||||
// } | ||||
r369 | } | |||
r388 | void VariableController::onAbortProgressRequested(std::shared_ptr<Variable> variable) | |||
{ | ||||
|
r682 | /// @todo ALX | ||
// qCDebug(LOG_VariableController()) << "TORM: VariableController::onAbortProgressRequested" | ||||
// << QThread::currentThread()->objectName(); | ||||
r388 | ||||
|
r682 | // auto it = impl->m_VariableToIdentifierMap.find(variable); | ||
// if (it != impl->m_VariableToIdentifierMap.cend()) { | ||||
// impl->m_VariableToProviderMap.at(variable)->requestDataAborting(it->second); | ||||
// } | ||||
// else { | ||||
// qCWarning(LOG_VariableController()) | ||||
// << tr("Aborting progression of inexistant variable detected !!!") | ||||
// << QThread::currentThread()->objectName(); | ||||
// } | ||||
r388 | } | |||
r510 | void VariableController::onAddSynchronizationGroupId(QUuid synchronizationGroupId) | |||
{ | ||||
|
r683 | impl->m_VariableSynchronizer->addGroup(synchronizationGroupId); | ||
r510 | } | |||
void VariableController::onRemoveSynchronizationGroupId(QUuid synchronizationGroupId) | ||||
{ | ||||
|
r683 | impl->m_VariableSynchronizer->removeGroup(synchronizationGroupId); | ||
r510 | } | |||
r511 | void VariableController::onAddSynchronized(std::shared_ptr<Variable> variable, | |||
QUuid synchronizationGroupId) | ||||
{ | ||||
|
r683 | impl->m_VariableSynchronizer->addVariable(variable, synchronizationGroupId); | ||
r511 | } | |||
|
r676 | void VariableController::desynchronize(std::shared_ptr<Variable> variable, | ||
QUuid synchronizationGroupId) | ||||
{ | ||||
|
r684 | // As a variable can't be into more than one synchronization group,we don't need group id here | ||
Q_UNUSED(synchronizationGroupId); | ||||
impl->m_VariableSynchronizer->removeVariable(variable); | ||||
|
r676 | } | ||
r219 | ||||
|
r683 | void VariableController::onRequestDataLoading(const QVector<std::shared_ptr<Variable> > &variables, | ||
r510 | const SqpRange &range, const SqpRange &oldRange, | |||
bool synchronise) | ||||
r219 | { | |||
|
r683 | // Set of variables that have been processed | ||
std::set<std::shared_ptr<Variable> > processedVariables; | ||||
std::map<std::shared_ptr<Variable>, SqpRange> oldRanges; | ||||
r510 | ||||
|
r683 | // Process requests for all variables | ||
r517 | for (const auto &var : variables) { | |||
|
r682 | impl->processRequest(var, range); | ||
|
r683 | processedVariables.insert(var); | ||
r510 | } | |||
r219 | ||||
|
r683 | // Handles synchronisation | ||
r510 | if (synchronise) { | |||
|
r683 | for (const auto &variable : variables) { | ||
// Finds the variables in the same synchronization group | ||||
auto synchronizedVariables | ||||
= impl->m_VariableSynchronizer->synchronizedVariables(variable); | ||||
for (const auto &synchronizedVariable : synchronizedVariables) { | ||||
// Processes variable (if it hasn't been already processed) | ||||
if (processedVariables.count(synchronizedVariable) == 0) { | ||||
auto rangeRequested = computeSynchroRangeRequested( | ||||
synchronizedVariable->range(), range, oldRange); | ||||
impl->processRequest(synchronizedVariable, rangeRequested); | ||||
processedVariables.insert(synchronizedVariable); | ||||
r510 | } | |||
} | ||||
r276 | } | |||
r219 | } | |||
} | ||||
|
r106 | void VariableController::initialize() | ||
{ | ||||
qCDebug(LOG_VariableController()) << tr("VariableController init") << QThread::currentThread(); | ||||
impl->m_WorkingMutex.lock(); | ||||
qCDebug(LOG_VariableController()) << tr("VariableController init END"); | ||||
} | ||||
void VariableController::finalize() | ||||
{ | ||||
impl->m_WorkingMutex.unlock(); | ||||
} | ||||
void VariableController::waitForFinish() | ||||
{ | ||||
QMutexLocker locker{&impl->m_WorkingMutex}; | ||||
} | ||||