|
|
#include <Variable/Variable.h>
|
|
|
#include <Variable/VariableAcquisitionWorker.h>
|
|
|
#include <Variable/VariableCacheStrategy.h>
|
|
|
#include <Variable/VariableController.h>
|
|
|
#include <Variable/VariableModel.h>
|
|
|
#include <Variable/VariableSynchronizer.h>
|
|
|
|
|
|
#include <Data/AcquisitionUtils.h>
|
|
|
#include <Data/DataProviderParameters.h>
|
|
|
#include <Data/IDataProvider.h>
|
|
|
#include <Data/IDataSeries.h>
|
|
|
#include <Data/VariableRequest.h>
|
|
|
#include <Time/TimeController.h>
|
|
|
|
|
|
#include <QMutex>
|
|
|
#include <QThread>
|
|
|
#include <QUuid>
|
|
|
#include <QtCore/QItemSelectionModel>
|
|
|
|
|
|
#include <unordered_map>
|
|
|
|
|
|
Q_LOGGING_CATEGORY(LOG_VariableController, "VariableController")
|
|
|
|
|
|
namespace {
|
|
|
|
|
|
SqpRange computeSynchroRangeRequested(const SqpRange &varRange, const SqpRange &graphRange,
|
|
|
const SqpRange &oldGraphRange)
|
|
|
{
|
|
|
auto zoomType = AcquisitionUtils::getZoomType(graphRange, oldGraphRange);
|
|
|
|
|
|
auto varRangeRequested = varRange;
|
|
|
switch (zoomType) {
|
|
|
case AcquisitionZoomType::ZoomIn: {
|
|
|
auto deltaLeft = graphRange.m_TStart - oldGraphRange.m_TStart;
|
|
|
auto deltaRight = oldGraphRange.m_TEnd - graphRange.m_TEnd;
|
|
|
varRangeRequested.m_TStart += deltaLeft;
|
|
|
varRangeRequested.m_TEnd -= deltaRight;
|
|
|
break;
|
|
|
}
|
|
|
|
|
|
case AcquisitionZoomType::ZoomOut: {
|
|
|
auto deltaLeft = oldGraphRange.m_TStart - graphRange.m_TStart;
|
|
|
auto deltaRight = graphRange.m_TEnd - oldGraphRange.m_TEnd;
|
|
|
varRangeRequested.m_TStart -= deltaLeft;
|
|
|
varRangeRequested.m_TEnd += deltaRight;
|
|
|
break;
|
|
|
}
|
|
|
case AcquisitionZoomType::PanRight: {
|
|
|
auto deltaRight = graphRange.m_TEnd - oldGraphRange.m_TEnd;
|
|
|
varRangeRequested.m_TStart += deltaRight;
|
|
|
varRangeRequested.m_TEnd += deltaRight;
|
|
|
break;
|
|
|
}
|
|
|
case AcquisitionZoomType::PanLeft: {
|
|
|
auto deltaLeft = oldGraphRange.m_TStart - graphRange.m_TStart;
|
|
|
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;
|
|
|
}
|
|
|
} // namespace
|
|
|
|
|
|
struct VariableController::VariableControllerPrivate {
|
|
|
explicit VariableControllerPrivate(VariableController *parent)
|
|
|
: m_WorkingMutex{},
|
|
|
m_VariableModel{new VariableModel{parent}},
|
|
|
m_VariableSelectionModel{new QItemSelectionModel{m_VariableModel, parent}},
|
|
|
m_VariableCacheStrategy{std::make_unique<VariableCacheStrategy>()},
|
|
|
m_VariableAcquisitionWorker{std::make_unique<VariableAcquisitionWorker>()},
|
|
|
m_VariableSynchronizer{std::make_unique<VariableSynchronizer>()},
|
|
|
q{parent}
|
|
|
{
|
|
|
m_VariableAcquisitionWorker->moveToThread(&m_VariableAcquisitionWorkerThread);
|
|
|
m_VariableAcquisitionWorkerThread.setObjectName("VariableAcquisitionWorkerThread");
|
|
|
}
|
|
|
|
|
|
virtual ~VariableControllerPrivate()
|
|
|
{
|
|
|
qCDebug(LOG_VariableController()) << tr("VariableControllerPrivate destruction");
|
|
|
m_VariableAcquisitionWorkerThread.quit();
|
|
|
m_VariableAcquisitionWorkerThread.wait();
|
|
|
}
|
|
|
|
|
|
void processRequest(std::shared_ptr<Variable> variable, const SqpRange &rangeRequested)
|
|
|
{
|
|
|
Q_ASSERT(variable != nullptr);
|
|
|
|
|
|
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;
|
|
|
}
|
|
|
|
|
|
variable->setRange(rangeRequested);
|
|
|
|
|
|
// 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)) {
|
|
|
DataProviderParameters providerParameters{std::move(notInCacheRanges),
|
|
|
variable->metadata()};
|
|
|
VariableRequest request{requestRange.first, requestRange.second, provider,
|
|
|
std::move(providerParameters)};
|
|
|
m_VariableAcquisitionWorker->pushVariableRequest(variable, std::move(request));
|
|
|
}
|
|
|
|
|
|
// Calls UI update for in-cache range
|
|
|
auto inCacheRanges = variable->provideInCacheRangeList(requestRange.second);
|
|
|
if (!inCacheRanges.isEmpty()) {
|
|
|
emit q->updateVarDisplaying(variable, inCacheRanges.first());
|
|
|
}
|
|
|
}
|
|
|
else {
|
|
|
// No request to make: we simply update variable ranges
|
|
|
variable->setCacheRange(requestRange.second);
|
|
|
emit variable->updated();
|
|
|
}
|
|
|
}
|
|
|
|
|
|
void registerProvider(std::shared_ptr<IDataProvider> provider)
|
|
|
{
|
|
|
Q_ASSERT(provider != nullptr);
|
|
|
connect(provider.get(), &IDataProvider::dataProvided, m_VariableAcquisitionWorker.get(),
|
|
|
&VariableAcquisitionWorker::onDataAcquired);
|
|
|
connect(provider.get(), &IDataProvider::dataProvidedProgress,
|
|
|
m_VariableAcquisitionWorker.get(),
|
|
|
&VariableAcquisitionWorker::onVariableRetrieveDataInProgress);
|
|
|
}
|
|
|
|
|
|
QMutex m_WorkingMutex;
|
|
|
/// Variable model. The VariableController has the ownership
|
|
|
VariableModel *m_VariableModel;
|
|
|
QItemSelectionModel *m_VariableSelectionModel;
|
|
|
|
|
|
TimeController *m_TimeController{nullptr};
|
|
|
std::unique_ptr<VariableCacheStrategy> m_VariableCacheStrategy;
|
|
|
std::unique_ptr<VariableAcquisitionWorker> m_VariableAcquisitionWorker;
|
|
|
QThread m_VariableAcquisitionWorkerThread;
|
|
|
|
|
|
/// Handler for variables synchronization
|
|
|
std::unique_ptr<VariableSynchronizer> m_VariableSynchronizer;
|
|
|
|
|
|
std::unordered_map<std::shared_ptr<Variable>, std::shared_ptr<IDataProvider> > m_Providers;
|
|
|
|
|
|
VariableController *q;
|
|
|
};
|
|
|
|
|
|
VariableController::VariableController(QObject *parent)
|
|
|
: QObject{parent}, impl{spimpl::make_unique_impl<VariableControllerPrivate>(this)}
|
|
|
{
|
|
|
qCDebug(LOG_VariableController())
|
|
|
<< tr("VariableController construction") << QThread::currentThread();
|
|
|
|
|
|
connect(impl->m_VariableModel, &VariableModel::abortProgessRequested, this,
|
|
|
&VariableController::onAbortProgressRequested);
|
|
|
|
|
|
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();
|
|
|
}
|
|
|
|
|
|
VariableController::~VariableController()
|
|
|
{
|
|
|
qCDebug(LOG_VariableController())
|
|
|
<< tr("VariableController destruction") << QThread::currentThread();
|
|
|
this->waitForFinish();
|
|
|
}
|
|
|
|
|
|
VariableModel *VariableController::variableModel() noexcept
|
|
|
{
|
|
|
return impl->m_VariableModel;
|
|
|
}
|
|
|
|
|
|
QItemSelectionModel *VariableController::variableSelectionModel() noexcept
|
|
|
{
|
|
|
return impl->m_VariableSelectionModel;
|
|
|
}
|
|
|
|
|
|
void VariableController::setTimeController(TimeController *timeController) noexcept
|
|
|
{
|
|
|
impl->m_TimeController = timeController;
|
|
|
}
|
|
|
|
|
|
std::shared_ptr<Variable>
|
|
|
VariableController::cloneVariable(std::shared_ptr<Variable> variable) noexcept
|
|
|
{
|
|
|
if (impl->m_VariableModel->containsVariable(variable)) {
|
|
|
// Clones variable
|
|
|
auto duplicate = variable->clone();
|
|
|
|
|
|
// Adds clone to model
|
|
|
impl->m_VariableModel->addVariable(duplicate);
|
|
|
|
|
|
// Registers provider
|
|
|
auto variableProvider = impl->m_Providers.at(variable);
|
|
|
auto duplicateProvider = variableProvider != nullptr ? variableProvider->clone() : nullptr;
|
|
|
|
|
|
impl->m_Providers[duplicate] = duplicateProvider;
|
|
|
if (duplicateProvider) {
|
|
|
impl->registerProvider(duplicateProvider);
|
|
|
}
|
|
|
|
|
|
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;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
void VariableController::deleteVariable(std::shared_ptr<Variable> variable) noexcept
|
|
|
{
|
|
|
if (!variable) {
|
|
|
qCCritical(LOG_VariableController()) << "Can't delete variable: variable is null";
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
// Spreads in SciQlop that the variable will be deleted, so that potential receivers can
|
|
|
// make some treatments before the deletion
|
|
|
emit variableAboutToBeDeleted(variable);
|
|
|
|
|
|
// Deletes provider
|
|
|
auto nbProvidersDeleted = impl->m_Providers.erase(variable);
|
|
|
qCDebug(LOG_VariableController())
|
|
|
<< tr("Number of providers deleted for variable %1: %2")
|
|
|
.arg(variable->name(), QString::number(nbProvidersDeleted));
|
|
|
|
|
|
|
|
|
// Deletes from model
|
|
|
impl->m_VariableModel->deleteVariable(variable);
|
|
|
}
|
|
|
|
|
|
void VariableController::deleteVariables(
|
|
|
const QVector<std::shared_ptr<Variable> > &variables) noexcept
|
|
|
{
|
|
|
for (auto variable : qAsConst(variables)) {
|
|
|
deleteVariable(variable);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
void VariableController::abortProgress(std::shared_ptr<Variable> variable)
|
|
|
{
|
|
|
}
|
|
|
|
|
|
std::shared_ptr<Variable>
|
|
|
VariableController::createVariable(const QString &name, const QVariantHash &metadata,
|
|
|
std::shared_ptr<IDataProvider> provider) noexcept
|
|
|
{
|
|
|
if (!impl->m_TimeController) {
|
|
|
qCCritical(LOG_VariableController())
|
|
|
<< tr("Impossible to create variable: The time controller is null");
|
|
|
return nullptr;
|
|
|
}
|
|
|
|
|
|
auto range = impl->m_TimeController->dateTime();
|
|
|
|
|
|
if (auto newVariable = impl->m_VariableModel->createVariable(name, range, metadata)) {
|
|
|
// Associate the provider
|
|
|
auto newVariableProvider = provider != nullptr ? provider->clone() : nullptr;
|
|
|
impl->m_Providers[newVariable] = newVariableProvider;
|
|
|
if (newVariableProvider) {
|
|
|
impl->registerProvider(newVariableProvider);
|
|
|
}
|
|
|
|
|
|
impl->processRequest(newVariable, range);
|
|
|
|
|
|
return newVariable;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
void VariableController::onDateTimeOnSelection(const SqpRange &dateTime)
|
|
|
{
|
|
|
// TODO check synchronisation and Rescale
|
|
|
qCDebug(LOG_VariableController())
|
|
|
<< "VariableController::onDateTimeOnSelection" << QThread::currentThread()->objectName();
|
|
|
auto selectedRows = impl->m_VariableSelectionModel->selectedRows();
|
|
|
for (const auto &selectedRow : qAsConst(selectedRows)) {
|
|
|
if (auto selectedVariable = impl->m_VariableModel->variable(selectedRow.row())) {
|
|
|
selectedVariable->setRange(dateTime);
|
|
|
impl->processRequest(selectedVariable, dateTime);
|
|
|
|
|
|
// notify that rescale operation has to be done
|
|
|
emit rangeChanged(selectedVariable, dateTime);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
void VariableController::onDataProvided(std::shared_ptr<Variable> variable, VariableRequest request)
|
|
|
{
|
|
|
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;
|
|
|
}
|
|
|
|
|
|
variable->setCacheRange(request.m_CacheRangeRequested);
|
|
|
variable->mergeDataSeries(request.m_Result);
|
|
|
emit variable->updated();
|
|
|
}
|
|
|
|
|
|
void VariableController::onVariableRetrieveDataInProgress(QUuid identifier, double progress)
|
|
|
{
|
|
|
/// @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");
|
|
|
// }
|
|
|
}
|
|
|
|
|
|
void VariableController::onAbortProgressRequested(std::shared_ptr<Variable> variable)
|
|
|
{
|
|
|
/// @todo ALX
|
|
|
// qCDebug(LOG_VariableController()) << "TORM: VariableController::onAbortProgressRequested"
|
|
|
// << QThread::currentThread()->objectName();
|
|
|
|
|
|
// 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();
|
|
|
// }
|
|
|
}
|
|
|
|
|
|
void VariableController::onAddSynchronizationGroupId(QUuid synchronizationGroupId)
|
|
|
{
|
|
|
impl->m_VariableSynchronizer->addGroup(synchronizationGroupId);
|
|
|
}
|
|
|
|
|
|
void VariableController::onRemoveSynchronizationGroupId(QUuid synchronizationGroupId)
|
|
|
{
|
|
|
impl->m_VariableSynchronizer->removeGroup(synchronizationGroupId);
|
|
|
}
|
|
|
|
|
|
void VariableController::onAddSynchronized(std::shared_ptr<Variable> variable,
|
|
|
QUuid synchronizationGroupId)
|
|
|
{
|
|
|
impl->m_VariableSynchronizer->addVariable(variable, synchronizationGroupId);
|
|
|
}
|
|
|
|
|
|
void VariableController::desynchronize(std::shared_ptr<Variable> variable,
|
|
|
QUuid synchronizationGroupId)
|
|
|
{
|
|
|
impl->m_VariableSynchronizer->removeVariable(variable, synchronizationGroupId);
|
|
|
}
|
|
|
|
|
|
void VariableController::onRequestDataLoading(const QVector<std::shared_ptr<Variable> > &variables,
|
|
|
const SqpRange &range, const SqpRange &oldRange,
|
|
|
bool synchronise)
|
|
|
{
|
|
|
// Set of variables that have been processed
|
|
|
std::set<std::shared_ptr<Variable> > processedVariables;
|
|
|
std::map<std::shared_ptr<Variable>, SqpRange> oldRanges;
|
|
|
|
|
|
// Process requests for all variables
|
|
|
for (const auto &var : variables) {
|
|
|
impl->processRequest(var, range);
|
|
|
processedVariables.insert(var);
|
|
|
}
|
|
|
|
|
|
// Handles synchronisation
|
|
|
if (synchronise) {
|
|
|
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);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
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};
|
|
|
}
|
|
|
|