diff --git a/CMakeLists.txt b/CMakeLists.txt index cb0ec43..a913623 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -81,9 +81,11 @@ FILE (GLOB_RECURSE core_SRCS ./include/Plugin/IPlugin.h ./include/Data/ArrayDataIterator.h ./include/Data/VectorSeries.h + ./include/Data/VectorTimeSerie.h ./include/Data/DateTimeRange.h ./include/Data/DateTimeRangeHelper.h ./include/Data/ScalarSeries.h + ./include/Data/ScalarTimeSerie.h ./include/Data/DataSeriesMergeHelper.h ./include/Data/DataSeries.h ./include/Data/DataSeriesType.h @@ -125,12 +127,14 @@ FILE (GLOB_RECURSE core_SRCS ./src/Common/StringUtils.cpp ./src/Common/SignalWaiter.cpp ./src/Data/ScalarSeries.cpp + ./src/Data/ScalarTimeSerie.cpp ./src/Data/DataSeriesIterator.cpp ./src/Data/OptionalAxis.cpp ./src/Data/ArrayDataIterator.cpp ./src/Data/SpectrogramSeries.cpp ./src/Data/DataSeriesUtils.cpp ./src/Data/VectorSeries.cpp + ./src/Data/VectorTimeSerie.cpp ./src/Network/NetworkController.cpp ./src/Network/Downloader.cpp ./src/Visualization/VisualizationController.cpp @@ -172,6 +176,7 @@ target_include_directories(sciqlopcore PUBLIC target_link_libraries(sciqlopcore PUBLIC Qt5::Core Qt5::Network + TimeSeries ) if(Catalog) diff --git a/include/Data/DataSeriesType.h b/include/Data/DataSeriesType.h index d019c83..ba31397 100644 --- a/include/Data/DataSeriesType.h +++ b/include/Data/DataSeriesType.h @@ -3,24 +3,33 @@ #include -enum class DataSeriesType { SCALAR, SPECTROGRAM, VECTOR, UNKNOWN }; +enum class DataSeriesType +{ + SCALAR, + SPECTROGRAM, + VECTOR, + UNKNOWN +}; -struct DataSeriesTypeUtils { - static DataSeriesType fromString(const QString &type) +struct DataSeriesTypeUtils +{ + static DataSeriesType fromString(const QString& type) + { + if(type.toLower() == QStringLiteral("scalar")) + { return DataSeriesType::SCALAR; } + else if(type.toLower() == QStringLiteral("spectrogram")) + { + return DataSeriesType::SPECTROGRAM; + } + else if(type.toLower() == QStringLiteral("vector")) + { + return DataSeriesType::VECTOR; + } + else { - if (type == QStringLiteral("scalar")) { - return DataSeriesType::SCALAR; - } - else if (type == QStringLiteral("spectrogram")) { - return DataSeriesType::SPECTROGRAM; - } - else if (type == QStringLiteral("vector")) { - return DataSeriesType::VECTOR; - } - else { - return DataSeriesType::UNKNOWN; - } + return DataSeriesType::UNKNOWN; } + } }; #endif // SCIQLOP_DATASERIESTYPE_H diff --git a/include/Data/IDataProvider.h b/include/Data/IDataProvider.h index 5a1aae3..396273a 100644 --- a/include/Data/IDataProvider.h +++ b/include/Data/IDataProvider.h @@ -2,54 +2,48 @@ #define SCIQLOP_IDATAPROVIDER_H #include "CoreGlobal.h" -#include - -#include - -#include -#include #include - +#include #include - +#include +#include #include +#include class DataProviderParameters; class IDataSeries; -DEPRECATE( - class QNetworkReply; - class QNetworkRequest; -) +DEPRECATE(class QNetworkReply; class QNetworkRequest;) /** * @brief The IDataProvider interface aims to declare a data provider. * - * A data provider is an entity that generates data and returns it according to various parameters - * (time interval, product to retrieve the data, etc.) - * Since its client mihgt use it from different threads it has to be either stateless and/or thread safe + * A data provider is an entity that generates data and returns it according to + * various parameters (time interval, product to retrieve the data, etc.) Since + * its client mihgt use it from different threads it has to be either stateless + * and/or thread safe * * @sa IDataSeries */ -class SCIQLOP_CORE_EXPORT IDataProvider : public QObject { - - Q_OBJECT +class SCIQLOP_CORE_EXPORT IDataProvider : public QObject +{ + Q_OBJECT public: - virtual ~IDataProvider() noexcept = default; - virtual std::shared_ptr clone() const = 0; + virtual ~IDataProvider() noexcept = default; + virtual std::shared_ptr clone() const = 0; - // Synchronous call -> asyncGetData may be written for asynchronous get - virtual IDataSeries* getData(const DataProviderParameters ¶meters) = 0; + // Synchronous call -> asyncGetData may be written for asynchronous get + virtual IDataSeries* getData(const DataProviderParameters& parameters) = 0; signals: - void progress(QUuid requestID, double progress); - + void progress(QUuid requestID, double progress); }; // Required for using shared_ptr in signals/slots -SCIQLOP_REGISTER_META_TYPE(IDATAPROVIDER_PTR_REGISTRY, std::shared_ptr) +SCIQLOP_REGISTER_META_TYPE(IDATAPROVIDER_PTR_REGISTRY, + std::shared_ptr) SCIQLOP_REGISTER_META_TYPE(IDATAPROVIDER_FUNCTION_REGISTRY, - std::function) + std::function) #endif // SCIQLOP_IDATAPROVIDER_H diff --git a/include/Data/ScalarTimeSerie.h b/include/Data/ScalarTimeSerie.h new file mode 100644 index 0000000..bc1549d --- /dev/null +++ b/include/Data/ScalarTimeSerie.h @@ -0,0 +1,17 @@ +#ifndef SCIQLOP_SCALARTIMESERIE_H +#define SCIQLOP_SCALARTIMESERIE_H + +#include "CoreGlobal.h" + +#include + +class SCIQLOP_CORE_EXPORT ScalarTimeSerie + : public TimeSeries::TimeSerie +{ +public: + ScalarTimeSerie() {} + ~ScalarTimeSerie() = default; + using TimeSerie::TimeSerie; +}; + +#endif // SCIQLOP_SCALARSERIES_H diff --git a/include/Data/VectorTimeSerie.h b/include/Data/VectorTimeSerie.h new file mode 100644 index 0000000..13cb128 --- /dev/null +++ b/include/Data/VectorTimeSerie.h @@ -0,0 +1,22 @@ +#ifndef SCIQLOP_VECTORTIMESERIE_H +#define SCIQLOP_VECTORTIMESERIE_H + +#include "CoreGlobal.h" + +#include + +struct Vector +{ + double x, y, z; +}; + +class SCIQLOP_CORE_EXPORT VectorTimeSerie + : public TimeSeries::TimeSerie +{ +public: + VectorTimeSerie() {} + ~VectorTimeSerie() = default; + using TimeSerie::TimeSerie; +}; + +#endif // SCIQLOP_VECTORTIMESERIE_H diff --git a/include/Variable/VariableCacheStrategy.h b/include/Variable/VariableCacheStrategy.h index f44e872..7c6d675 100644 --- a/include/Variable/VariableCacheStrategy.h +++ b/include/Variable/VariableCacheStrategy.h @@ -3,30 +3,15 @@ #include "CoreGlobal.h" -#include -#include - #include -#include - -#include -#include - - -Q_DECLARE_LOGGING_CATEGORY(LOG_VariableCacheStrategy) - -class Variable; - /// This class aims to hande the cache strategy. -class SCIQLOP_CORE_EXPORT VariableCacheStrategy { - +class SCIQLOP_CORE_EXPORT VariableCacheStrategy +{ public: - virtual ~VariableCacheStrategy() noexcept = default; - virtual DateTimeRange computeRange(const DateTimeRange ¤tCacheRange, - const DateTimeRange &rangeRequested) - = 0; + virtual ~VariableCacheStrategy() noexcept = default; + virtual DateTimeRange computeRange(const DateTimeRange& currentCacheRange, + const DateTimeRange& rangeRequested) = 0; }; - #endif // SCIQLOP_VARIABLECACHESTRATEGY_H diff --git a/src/Data/ScalarTimeSerie.cpp b/src/Data/ScalarTimeSerie.cpp new file mode 100644 index 0000000..b58999b --- /dev/null +++ b/src/Data/ScalarTimeSerie.cpp @@ -0,0 +1 @@ +#include diff --git a/src/Data/VectorTimeSerie.cpp b/src/Data/VectorTimeSerie.cpp new file mode 100644 index 0000000..30260ee --- /dev/null +++ b/src/Data/VectorTimeSerie.cpp @@ -0,0 +1 @@ +#include diff --git a/src/Variable/VariableController2.cpp b/src/Variable/VariableController2.cpp index 19d918c..92a8ab9 100644 --- a/src/Variable/VariableController2.cpp +++ b/src/Variable/VariableController2.cpp @@ -1,432 +1,452 @@ -#include -#include -#include -#include -#include - #include "Variable/VariableController2.h" + #include "Variable/VariableSynchronizationGroup2.h" + #include #include #include -#include #include +#include +#include +#include +#include +#include +#include +#include #include #include -#include - class VariableController2::VariableController2Private { - struct threadSafeVaraiblesMaps + struct threadSafeVaraiblesMaps + { + inline void + addVariable(const std::shared_ptr& variable, + const std::shared_ptr& provider, + const std::shared_ptr& + synchronizationGroup) { - inline void addVariable(const std::shared_ptr& variable, const std::shared_ptr& provider, const std::shared_ptr& synchronizationGroup) - { - QWriteLocker lock{&_lock}; - _variables[*variable] = variable; - _providers[*variable] = provider; - _synchronizationGroups[*variable] = synchronizationGroup; - } - - inline void removeVariable(const std::shared_ptr& variable) - { - QWriteLocker lock{&_lock}; - _variables.erase(*variable); - _providers.remove(*variable); - _synchronizationGroups.remove(*variable); - } - - inline void synchronize(const std::shared_ptr& variable, const std::optional>& with) - { - QWriteLocker lock{&_lock}; - if(with.has_value()) - { - auto newGroup = _synchronizationGroups[*with.value()]; - newGroup->addVariable(*variable); - _synchronizationGroups[*variable] = newGroup; - } - else - { - _synchronizationGroups[*variable] = std::make_shared(*variable); - } - } - - inline std::shared_ptr variable(QUuid variable) - { - QReadLocker lock{&_lock}; - auto it = _variables.find(variable); - [[unlikely]] - if(it==_variables.end()) - SCIQLOP_ERROR(threadSafeVaraiblesMaps,"Unknown Variable"); - return (*it).second; - } - - inline std::shared_ptr variable(int index) - { - QReadLocker lock{&_lock}; - [[unlikely]] - if(!_variables.size() > index) - SCIQLOP_ERROR(threadSafeVaraiblesMaps,"Index is out of bounds"); - auto it = _variables.cbegin(); - while (index!=0) { - index-=1; - it++; - } - return (*it).second; - } - - inline const std::vector> variables() - { - std::vector> vars; - QReadLocker lock{&_lock}; - for(const auto&[id, var]:_variables) - { - vars.push_back(var); - } - return vars; - } - - inline std::shared_ptr provider(QUuid variable) - { - QReadLocker lock{&_lock}; - [[unlikely]] - if(!_providers.contains(variable)) - SCIQLOP_ERROR(threadSafeVaraiblesMaps,"Unknown Variable"); - return _providers[variable]; - } - - inline std::shared_ptr group(QUuid variable) - { - QReadLocker lock{&_lock}; - [[unlikely]] - if(!_synchronizationGroups.contains(variable)) - SCIQLOP_ERROR(threadSafeVaraiblesMaps,"Unknown Variable"); - return _synchronizationGroups[variable]; - } - - inline bool has(const std::shared_ptr& variable) - { - QReadLocker lock{&_lock}; - return _variables.find(*variable)==_variables.end(); - } - - private: - std::map> _variables; - QMap> _providers; - QMap> _synchronizationGroups; - QReadWriteLock _lock{QReadWriteLock::Recursive}; - }_maps; - std::vector _variablesToRemove; - QThreadPool* _ThreadPool; - VCTransactionsQueues _transactions; - std::unique_ptr _cacheStrategy; - - void _transactionComplete(QUuid group, std::shared_ptr transaction) - { - if(transaction->done()) - { - _transactions.complete(group); - } - this->_processTransactions(); + QWriteLocker lock{&_lock}; + _variables[*variable] = variable; + _providers[*variable] = provider; + _synchronizationGroups[*variable] = synchronizationGroup; } - void _cleanupVariables() + inline void removeVariable(const std::shared_ptr& variable) { - for(auto id:_variablesToRemove) - { - auto v = this->variable(id); - if(!hasPendingTransactions(v)) - { - _variablesToRemove.erase(std::remove(_variablesToRemove.begin(), _variablesToRemove.end(), id), _variablesToRemove.end()); - this->deleteVariable(v); - } - } + QWriteLocker lock{&_lock}; + _variables.erase(*variable); + _providers.remove(*variable); + _synchronizationGroups.remove(*variable); } - void _processTransactions(bool fragmented=false) + inline void + synchronize(const std::shared_ptr& variable, + const std::optional>& with) { - auto nextTransactions = _transactions.nextTransactions(); - auto pendingTransactions = _transactions.pendingTransactions(); - for( auto [groupID, newTransaction] : nextTransactions) - { - if(newTransaction.has_value() && !pendingTransactions[groupID].has_value()) - { - _transactions.start(groupID); - auto refVar = _maps.variable(newTransaction.value()->refVar); - auto ranges = _computeAllRangesInGroup(refVar,newTransaction.value()->range); - for( auto const& [ID, range] : ranges) - { - auto provider = _maps.provider(ID); - auto variable = _maps.variable(ID); - if(fragmented) - { - auto [missingRanges, newCacheRange] = _computeMissingRanges(variable,range); - - auto exe = new TransactionExe(variable, provider, missingRanges, range, newCacheRange); - QObject::connect(exe, - &TransactionExe::transactionComplete, - [groupID=groupID,transaction=newTransaction.value(),this]() - { - this->_transactionComplete(groupID, transaction); - } - ); - _ThreadPool->start(exe); - } - else - { - auto exe = new TransactionExe(variable, provider, {range}, range, range); - QObject::connect(exe, - &TransactionExe::transactionComplete, - [groupID=groupID,transaction=newTransaction.value(),this]() - { - this->_transactionComplete(groupID, transaction); - } - ); - _ThreadPool->start(exe); - } - } - } - } - //after each transaction update we get a new distribution of idle and working variables - //so we can delete variables which are waiting to be deleted if they are now idle - _cleanupVariables(); + QWriteLocker lock{&_lock}; + if(with.has_value()) + { + auto newGroup = _synchronizationGroups[*with.value()]; + newGroup->addVariable(*variable); + _synchronizationGroups[*variable] = newGroup; + } + else + { + _synchronizationGroups[*variable] = + std::make_shared(*variable); + } } - std::map _computeAllRangesInGroup(const std::shared_ptr& refVar, DateTimeRange r) + inline std::shared_ptr variable(QUuid variable) { - std::map ranges; - if(!DateTimeRangeHelper::hasnan(r)) - { - auto group = _maps.group(*refVar); - if(auto transformation = DateTimeRangeHelper::computeTransformation(refVar->range(),r); - transformation.has_value()) - { - for(auto varId:group->variables()) - { - auto var = _maps.variable(varId); - auto newRange = var->range().transform(transformation.value()); - ranges[varId] = newRange; - } - } - else // force new range to all variables -> may be weird if more than one var in the group - // @TODO ensure that there is no side effects - { - for(auto varId:group->variables()) - { - auto var = _maps.variable(varId); - ranges[varId] = r; - } - } - } - else - { - SCIQLOP_ERROR(VariableController2Private, "Invalid range containing NaN"); - } - return ranges; + QReadLocker lock{&_lock}; + auto it = _variables.find(variable); + [[unlikely]] if(it == _variables.end()) + SCIQLOP_ERROR(threadSafeVaraiblesMaps, "Unknown Variable"); + return (*it).second; } - std::pair,DateTimeRange> _computeMissingRanges(const std::shared_ptr& var, DateTimeRange r) + inline std::shared_ptr variable(int index) { - DateTimeRange newCacheRange; - std::vector missingRanges; - if(DateTimeRangeHelper::hasnan(var->cacheRange())) - { - newCacheRange = _cacheStrategy->computeRange(r,r); - missingRanges = {newCacheRange}; - } - else - { - newCacheRange = _cacheStrategy->computeRange(var->cacheRange(),r); - missingRanges = newCacheRange - var->cacheRange(); - } - return {missingRanges,newCacheRange}; + QReadLocker lock{&_lock}; + [[unlikely]] if(!_variables.size() > index) + SCIQLOP_ERROR(threadSafeVaraiblesMaps, "Index is out of bounds"); + auto it = _variables.cbegin(); + while(index != 0) + { + index -= 1; + it++; + } + return (*(it)).second; } - void _changeRange(QUuid id, DateTimeRange r) + inline const std::vector> variables() { - _changeRange(_maps.variable(id) ,r); + std::vector> vars; + QReadLocker lock{&_lock}; + for(const auto& [id, var] : _variables) + { + vars.push_back(var); + } + return vars; } - void _changeRange(const std::shared_ptr& var, DateTimeRange r) + + inline std::shared_ptr provider(QUuid variable) { - auto provider = _maps.provider(*var); - auto [missingRanges, newCacheRange] = _computeMissingRanges(var,r); - std::vector data; - for(auto range:missingRanges) - { - data.push_back(provider->getData(DataProviderParameters{{range}, var->metadata()})); - } - var->updateData(data, r, newCacheRange, true); + QReadLocker lock{&_lock}; + [[unlikely]] if(!_providers.contains(variable)) + SCIQLOP_ERROR(threadSafeVaraiblesMaps, "Unknown Variable"); + return _providers[variable]; } -public: - VariableController2Private(QObject* parent=Q_NULLPTR) - :_cacheStrategy(VariableCacheStrategyFactory::createCacheStrategy(CacheStrategy::SingleThreshold)) + + inline std::shared_ptr group(QUuid variable) { - Q_UNUSED(parent); - this->_ThreadPool = new QThreadPool(); - this->_ThreadPool->setMaxThreadCount(32); + QReadLocker lock{&_lock}; + [[unlikely]] if(!_synchronizationGroups.contains(variable)) + SCIQLOP_ERROR(threadSafeVaraiblesMaps, "Unknown Variable"); + return _synchronizationGroups[variable]; } - /* - * This dtor has to like this even if this is ugly, because default dtor would rely on - * declaration order to destruct members and that would always lead to regressions when - * modifying class members - */ - ~VariableController2Private() + inline bool has(const std::shared_ptr& variable) { - delete this->_ThreadPool; + QReadLocker lock{&_lock}; + return _variables.find(*variable) == _variables.end(); } - std::shared_ptr createVariable(const QString &name, const QVariantHash &metadata, std::shared_ptr provider) + private: + std::map> _variables; + QMap> _providers; + QMap> + _synchronizationGroups; + QReadWriteLock _lock{QReadWriteLock::Recursive}; + } _maps; + std::vector _variablesToRemove; + QThreadPool* _ThreadPool; + VCTransactionsQueues _transactions; + std::unique_ptr _cacheStrategy; + + void _transactionComplete(QUuid group, + std::shared_ptr transaction) + { + if(transaction->done()) { _transactions.complete(group); } + this->_processTransactions(); + } + + void _cleanupVariables() + { + for(auto id : _variablesToRemove) { - auto newVar = std::make_shared(name,metadata); - auto group = std::make_shared(newVar->ID()); - _maps.addVariable(newVar,std::move(provider),group); - this->_transactions.addEntry(*group); - return newVar; + auto v = this->variable(id); + if(!hasPendingTransactions(v)) + { + _variablesToRemove.erase(std::remove(_variablesToRemove.begin(), + _variablesToRemove.end(), id), + _variablesToRemove.end()); + this->deleteVariable(v); + } } + } - std::shared_ptr variable(QUuid ID) + void _processTransactions(bool fragmented = false) + { + auto nextTransactions = _transactions.nextTransactions(); + auto pendingTransactions = _transactions.pendingTransactions(); + for(auto [groupID, newTransaction] : nextTransactions) { - return _maps.variable(ID); + if(newTransaction.has_value() && + !pendingTransactions[groupID].has_value()) + { + _transactions.start(groupID); + auto refVar = _maps.variable(newTransaction.value()->refVar); + auto ranges = + _computeAllRangesInGroup(refVar, newTransaction.value()->range); + for(auto const& [ID, range] : ranges) + { + auto provider = _maps.provider(ID); + auto variable = _maps.variable(ID); + if(fragmented) + { + auto [missingRanges, newCacheRange] = + _computeMissingRanges(variable, range); + + auto exe = new TransactionExe(variable, provider, missingRanges, + range, newCacheRange); + QObject::connect( + exe, &TransactionExe::transactionComplete, + [groupID = groupID, transaction = newTransaction.value(), + this]() { this->_transactionComplete(groupID, transaction); }); + _ThreadPool->start(exe); + } + else + { + auto exe = + new TransactionExe(variable, provider, {range}, range, range); + QObject::connect( + exe, &TransactionExe::transactionComplete, + [groupID = groupID, transaction = newTransaction.value(), + this]() { this->_transactionComplete(groupID, transaction); }); + _ThreadPool->start(exe); + } + } + } } - - std::shared_ptr variable(int index) + // after each transaction update we get a new distribution of idle and + // working variables so we can delete variables which are waiting to be + // deleted if they are now idle + _cleanupVariables(); + } + + std::map + _computeAllRangesInGroup(const std::shared_ptr& refVar, + DateTimeRange r) + { + std::map ranges; + if(!DateTimeRangeHelper::hasnan(r)) { - return _maps.variable(index); + auto group = _maps.group(*refVar); + if(auto transformation = + DateTimeRangeHelper::computeTransformation(refVar->range(), r); + transformation.has_value()) + { + for(auto varId : group->variables()) + { + auto var = _maps.variable(varId); + auto newRange = var->range().transform(transformation.value()); + ranges[varId] = newRange; + } + } + else // force new range to all variables -> may be weird if more than one + // var in the group + // @TODO ensure that there is no side effects + { + for(auto varId : group->variables()) + { + auto var = _maps.variable(varId); + ranges[varId] = r; + } + } } - - std::shared_ptr cloneVariable(const std::shared_ptr& variable) + else { - auto newVar = variable->clone(); - _maps.synchronize(newVar,std::nullopt); - _maps.addVariable(newVar,_maps.provider(*variable),_maps.group(*newVar)); - this->_transactions.addEntry(*_maps.group(*newVar)); - return newVar; + SCIQLOP_ERROR(VariableController2Private, "Invalid range containing NaN"); } - - bool hasPendingTransactions(const std::shared_ptr& variable) + return ranges; + } + + std::pair, DateTimeRange> + _computeMissingRanges(const std::shared_ptr& var, DateTimeRange r) + { + DateTimeRange newCacheRange; + std::vector missingRanges; + if(DateTimeRangeHelper::hasnan(var->cacheRange())) { - return _transactions.active(*_maps.group(*variable)); + newCacheRange = _cacheStrategy->computeRange(r, r); + missingRanges = {newCacheRange}; } - - bool hasPendingTransactions() + else { - bool has = false; - for(const auto& var:_maps.variables()) - { - has |= _transactions.active(*_maps.group(*var)); - } - return has; + newCacheRange = _cacheStrategy->computeRange(var->cacheRange(), r); + missingRanges = newCacheRange - var->cacheRange(); } - - void deleteVariable(const std::shared_ptr& variable) + return {missingRanges, newCacheRange}; + } + + void _changeRange(QUuid id, DateTimeRange r) + { + _changeRange(_maps.variable(id), r); + } + void _changeRange(const std::shared_ptr& var, DateTimeRange r) + { + auto provider = _maps.provider(*var); + auto [missingRanges, newCacheRange] = _computeMissingRanges(var, r); + std::vector data; + for(auto range : missingRanges) { - if(!hasPendingTransactions(variable)) - _maps.removeVariable(variable); - else - _variablesToRemove.push_back(variable->ID()); + data.push_back( + provider->getData(DataProviderParameters{{range}, var->metadata()})); } + var->updateData(data, r, newCacheRange, true); + } - void asyncChangeRange(const std::shared_ptr& variable, const DateTimeRange& r) +public: + VariableController2Private(QObject* parent = Q_NULLPTR) + : _cacheStrategy(VariableCacheStrategyFactory::createCacheStrategy( + CacheStrategy::SingleThreshold)) + { + Q_UNUSED(parent); + this->_ThreadPool = new QThreadPool(); + this->_ThreadPool->setMaxThreadCount(32); + } + + /* + * This dtor has to like this even if this is ugly, because default dtor would + * rely on declaration order to destruct members and that would always lead to + * regressions when modifying class members + */ + ~VariableController2Private() { delete this->_ThreadPool; } + + std::shared_ptr + createVariable(const QString& name, const QVariantHash& metadata, + std::shared_ptr provider) + { + auto newVar = std::make_shared(name, metadata); + auto group = std::make_shared(newVar->ID()); + _maps.addVariable(newVar, std::move(provider), group); + this->_transactions.addEntry(*group); + return newVar; + } + + std::shared_ptr variable(QUuid ID) { return _maps.variable(ID); } + + std::shared_ptr variable(int index) + { + return _maps.variable(index); + } + + std::shared_ptr + cloneVariable(const std::shared_ptr& variable) + { + auto newVar = variable->clone(); + _maps.synchronize(newVar, std::nullopt); + _maps.addVariable(newVar, _maps.provider(*variable), _maps.group(*newVar)); + this->_transactions.addEntry(*_maps.group(*newVar)); + return newVar; + } + + bool hasPendingTransactions(const std::shared_ptr& variable) + { + return _transactions.active(*_maps.group(*variable)); + } + + bool hasPendingTransactions() + { + bool has = false; + for(const auto& var : _maps.variables()) { - if(!DateTimeRangeHelper::hasnan(r)) - { - auto group = _maps.group(*variable); - // Just overwrite next transaction - { - _transactions.enqueue(*group,std::make_shared(variable->ID(), r, static_cast(group->variables().size()))); - } - _processTransactions(); - } - else - { - SCIQLOP_ERROR(VariableController2Private, "Invalid range containing NaN"); - } + has |= _transactions.active(*_maps.group(*var)); } + return has; + } - void changeRange(const std::shared_ptr& variable, DateTimeRange r) + void deleteVariable(const std::shared_ptr& variable) + { + if(!hasPendingTransactions(variable)) + _maps.removeVariable(variable); + else + _variablesToRemove.push_back(variable->ID()); + } + + void asyncChangeRange(const std::shared_ptr& variable, + const DateTimeRange& r) + { + if(!DateTimeRangeHelper::hasnan(r)) { - asyncChangeRange(variable,r); - while (hasPendingTransactions(variable)) - { - QCoreApplication::processEvents(); - } + auto group = _maps.group(*variable); + // Just overwrite next transaction + { + _transactions.enqueue(*group, + std::make_shared( + variable->ID(), r, + static_cast(group->variables().size()))); + } + _processTransactions(); } - - inline void synchronize(const std::shared_ptr& var, const std::shared_ptr& with) + else { - _maps.synchronize(var, with); + SCIQLOP_ERROR(VariableController2Private, "Invalid range containing NaN"); } + } - inline const std::vector> variables() + void changeRange(const std::shared_ptr& variable, DateTimeRange r) + { + asyncChangeRange(variable, r); + while(hasPendingTransactions(variable)) { - return _maps.variables(); + QCoreApplication::processEvents(); } - + } + + inline void synchronize(const std::shared_ptr& var, + const std::shared_ptr& with) + { + _maps.synchronize(var, with); + } + + inline const std::vector> variables() + { + return _maps.variables(); + } }; VariableController2::VariableController2() - :impl{spimpl::make_unique_impl()} + : impl{spimpl::make_unique_impl()} {} -std::shared_ptr VariableController2::createVariable(const QString &name, const QVariantHash &metadata, const std::shared_ptr& provider, const DateTimeRange &range) +std::shared_ptr VariableController2::createVariable( + const QString& name, const QVariantHash& metadata, + const std::shared_ptr& provider, const DateTimeRange& range) { - auto var = impl->createVariable(name, metadata, provider); - var->setRange(range); // even with no data this is it's range - if(!DateTimeRangeHelper::hasnan(range)) - impl->asyncChangeRange(var,range); - else - SCIQLOP_ERROR(VariableController2, "Creating a variable with default constructed DateTimeRange is an error"); - emit variableAdded(var); - return var; + auto var = impl->createVariable(name, metadata, provider); + var->setRange(range); // even with no data this is it's range + if(!DateTimeRangeHelper::hasnan(range)) + impl->asyncChangeRange(var, range); + else + SCIQLOP_ERROR(VariableController2, "Creating a variable with default " + "constructed DateTimeRange is an error"); + emit variableAdded(var); + return var; } -std::shared_ptr VariableController2::cloneVariable(const std::shared_ptr &variable) +std::shared_ptr +VariableController2::cloneVariable(const std::shared_ptr& variable) { - return impl->cloneVariable(variable); + return impl->cloneVariable(variable); } -void VariableController2::deleteVariable(const std::shared_ptr& variable) +void VariableController2::deleteVariable( + const std::shared_ptr& variable) { - impl->deleteVariable(variable); - emit variableDeleted(variable); + impl->deleteVariable(variable); + emit variableDeleted(variable); } -void VariableController2::changeRange(const std::shared_ptr& variable, const DateTimeRange& r) +void VariableController2::changeRange(const std::shared_ptr& variable, + const DateTimeRange& r) { - impl->changeRange(variable, r); + impl->changeRange(variable, r); } -void VariableController2::asyncChangeRange(const std::shared_ptr &variable, const DateTimeRange &r) +void VariableController2::asyncChangeRange( + const std::shared_ptr& variable, const DateTimeRange& r) { - impl->asyncChangeRange(variable, r); + impl->asyncChangeRange(variable, r); } -const std::vector > VariableController2::variables() +const std::vector> VariableController2::variables() { - return impl->variables(); + return impl->variables(); } -bool VariableController2::isReady(const std::shared_ptr &variable) +bool VariableController2::isReady(const std::shared_ptr& variable) { - return !impl->hasPendingTransactions(variable); + return !impl->hasPendingTransactions(variable); } -bool VariableController2::isReady() -{ - return !impl->hasPendingTransactions(); -} +bool VariableController2::isReady() { return !impl->hasPendingTransactions(); } -void VariableController2::synchronize(const std::shared_ptr &var, const std::shared_ptr &with) +void VariableController2::synchronize(const std::shared_ptr& var, + const std::shared_ptr& with) { - impl->synchronize(var, with); + impl->synchronize(var, with); } -const std::vector> VariableController2::variables(const std::vector &ids) +const std::vector> +VariableController2::variables(const std::vector& ids) { - std::vector> variables; - for (const auto& id : ids) { - variables.push_back(impl->variable(id)); - } - return variables; + std::vector> variables; + std::transform(std::cbegin(ids), std::cend(ids), + std::back_inserter(variables), + [this](const auto& id) { return impl->variable(id); }); + return variables; } diff --git a/src/pybind11_wrappers/CoreWrappers.cpp b/src/pybind11_wrappers/CoreWrappers.cpp index 3b3c2dd..6064161 100644 --- a/src/pybind11_wrappers/CoreWrappers.cpp +++ b/src/pybind11_wrappers/CoreWrappers.cpp @@ -1,157 +1,177 @@ -#include -#include -#include -#include -#include -#include -#include - - -#include -#include +#include "CoreWrappers.h" #include "pywrappers_common.h" -#include "CoreWrappers.h" #include +#include +#include #include -#include #include #include -#include -#include - -#include - -#include