CosinusProvider.cpp
272 lines
| 10.0 KiB
| text/x-c
|
CppLexer
Alexandre Leroux
|
r128 | #include "CosinusProvider.h" | ||
Alexandre Leroux
|
r780 | #include "MockDefs.h" | ||
Alexandre Leroux
|
r128 | |||
#include <Data/DataProviderParameters.h> | ||||
#include <Data/ScalarSeries.h> | ||||
Alexandre Leroux
|
r899 | #include <Data/SpectrogramSeries.h> | ||
Alexandre Leroux
|
r783 | #include <Data/VectorSeries.h> | ||
Alexandre Leroux
|
r128 | |||
r135 | #include <cmath> | |||
r428 | #include <QFuture> | |||
r364 | #include <QThread> | |||
r428 | #include <QtConcurrent/QtConcurrent> | |||
r298 | ||||
r231 | Q_LOGGING_CATEGORY(LOG_CosinusProvider, "CosinusProvider") | |||
Alexandre Leroux
|
r782 | namespace { | ||
Alexandre Leroux
|
r899 | /// Number of bands generated for a spectrogram | ||
const auto SPECTROGRAM_NUMBER_BANDS = 30; | ||||
Alexandre Leroux
|
r782 | /// Abstract cosinus type | ||
struct ICosinusType { | ||||
virtual ~ICosinusType() = default; | ||||
/// @return the number of components generated for the type | ||||
virtual int componentCount() const = 0; | ||||
/// @return the data series created for the type | ||||
virtual std::shared_ptr<IDataSeries> createDataSeries(std::vector<double> xAxisData, | ||||
Alexandre Leroux
|
r915 | std::vector<double> valuesData) const = 0; | ||
Alexandre Leroux
|
r898 | /// Generates values (one value per component) | ||
/// @param x the x-axis data used to generate values | ||||
/// @param values the vector in which to insert the generated values | ||||
/// @param dataIndex the index of insertion of the generated values | ||||
/// | ||||
virtual void generateValues(double x, std::vector<double> &values, int dataIndex) const = 0; | ||||
Alexandre Leroux
|
r782 | }; | ||
Alexandre Leroux
|
r783 | struct ScalarCosinus : public ICosinusType { | ||
int componentCount() const override { return 1; } | ||||
std::shared_ptr<IDataSeries> createDataSeries(std::vector<double> xAxisData, | ||||
Alexandre Leroux
|
r915 | std::vector<double> valuesData) const override | ||
Alexandre Leroux
|
r783 | { | ||
return std::make_shared<ScalarSeries>(std::move(xAxisData), std::move(valuesData), | ||||
Alexandre Leroux
|
r915 | Unit{QStringLiteral("t"), true}, Unit{}); | ||
Alexandre Leroux
|
r783 | } | ||
Alexandre Leroux
|
r898 | |||
void generateValues(double x, std::vector<double> &values, int dataIndex) const override | ||||
{ | ||||
values[dataIndex] = std::cos(x); | ||||
} | ||||
Alexandre Leroux
|
r783 | }; | ||
Alexandre Leroux
|
r899 | |||
struct SpectrogramCosinus : public ICosinusType { | ||||
/// Ctor with y-axis | ||||
Alexandre Leroux
|
r915 | explicit SpectrogramCosinus(std::vector<double> yAxisData, Unit yAxisUnit, Unit valuesUnit) | ||
: m_YAxisData{std::move(yAxisData)}, | ||||
m_YAxisUnit{std::move(yAxisUnit)}, | ||||
m_ValuesUnit{std::move(valuesUnit)} | ||||
Alexandre Leroux
|
r899 | { | ||
} | ||||
int componentCount() const override { return m_YAxisData.size(); } | ||||
std::shared_ptr<IDataSeries> createDataSeries(std::vector<double> xAxisData, | ||||
Alexandre Leroux
|
r915 | std::vector<double> valuesData) const override | ||
Alexandre Leroux
|
r899 | { | ||
Alexandre Leroux
|
r915 | return std::make_shared<SpectrogramSeries>( | ||
std::move(xAxisData), m_YAxisData, std::move(valuesData), | ||||
Unit{QStringLiteral("t"), true}, m_YAxisUnit, m_ValuesUnit); | ||||
Alexandre Leroux
|
r899 | } | ||
void generateValues(double x, std::vector<double> &values, int dataIndex) const override | ||||
{ | ||||
Alexandre Leroux
|
r900 | auto componentCount = this->componentCount(); | ||
for (int i = 0; i < componentCount; ++i) { | ||||
auto y = m_YAxisData[i]; | ||||
auto r = 3 * std::sqrt(x * x + y * y) + 1e-2; | ||||
auto value = 2 * x * (std::cos(r + 2) / r - std::sin(r + 2) / r); | ||||
values[componentCount * dataIndex + i] = value; | ||||
} | ||||
Alexandre Leroux
|
r899 | } | ||
std::vector<double> m_YAxisData; | ||||
Unit m_YAxisUnit; | ||||
Alexandre Leroux
|
r915 | Unit m_ValuesUnit; | ||
Alexandre Leroux
|
r899 | }; | ||
Alexandre Leroux
|
r783 | struct VectorCosinus : public ICosinusType { | ||
int componentCount() const override { return 3; } | ||||
std::shared_ptr<IDataSeries> createDataSeries(std::vector<double> xAxisData, | ||||
Alexandre Leroux
|
r915 | std::vector<double> valuesData) const override | ||
Alexandre Leroux
|
r783 | { | ||
return std::make_shared<VectorSeries>(std::move(xAxisData), std::move(valuesData), | ||||
Alexandre Leroux
|
r915 | Unit{QStringLiteral("t"), true}, Unit{}); | ||
Alexandre Leroux
|
r783 | } | ||
Alexandre Leroux
|
r898 | |||
void generateValues(double x, std::vector<double> &values, int dataIndex) const override | ||||
{ | ||||
// Generates value for each component: cos(x), cos(x)/2, cos(x)/3 | ||||
auto xValue = std::cos(x); | ||||
auto componentCount = this->componentCount(); | ||||
for (auto i = 0; i < componentCount; ++i) { | ||||
values[componentCount * dataIndex + i] = xValue / (i + 1); | ||||
} | ||||
} | ||||
Alexandre Leroux
|
r783 | }; | ||
Alexandre Leroux
|
r784 | /// Converts string to cosinus type | ||
/// @return the cosinus type if the string could be converted, nullptr otherwise | ||||
std::unique_ptr<ICosinusType> cosinusType(const QString &type) noexcept | ||||
{ | ||||
if (type.compare(QStringLiteral("scalar"), Qt::CaseInsensitive) == 0) { | ||||
return std::make_unique<ScalarCosinus>(); | ||||
} | ||||
Alexandre Leroux
|
r899 | else if (type.compare(QStringLiteral("spectrogram"), Qt::CaseInsensitive) == 0) { | ||
// Generates default y-axis data for spectrogram [0., 1., 2., ...] | ||||
std::vector<double> yAxisData(SPECTROGRAM_NUMBER_BANDS); | ||||
std::iota(yAxisData.begin(), yAxisData.end(), 0.); | ||||
Alexandre Leroux
|
r915 | return std::make_unique<SpectrogramCosinus>(std::move(yAxisData), Unit{"eV"}, | ||
Unit{"eV/(cm^2-s-sr-eV)"}); | ||||
Alexandre Leroux
|
r899 | } | ||
Alexandre Leroux
|
r784 | else if (type.compare(QStringLiteral("vector"), Qt::CaseInsensitive) == 0) { | ||
return std::make_unique<VectorCosinus>(); | ||||
} | ||||
else { | ||||
return nullptr; | ||||
} | ||||
} | ||||
Alexandre Leroux
|
r782 | } // namespace | ||
Alexandre Leroux
|
r712 | std::shared_ptr<IDataProvider> CosinusProvider::clone() const | ||
{ | ||||
// No copy is made in clone | ||||
return std::make_shared<CosinusProvider>(); | ||||
} | ||||
r539 | std::shared_ptr<IDataSeries> CosinusProvider::retrieveData(QUuid acqIdentifier, | |||
Alexandre Leroux
|
r779 | const SqpRange &dataRangeRequested, | ||
const QVariantHash &data) | ||||
Alexandre Leroux
|
r128 | { | ||
r428 | // TODO: Add Mutex | |||
r298 | auto dataIndex = 0; | |||
r231 | ||||
Alexandre Leroux
|
r784 | // Retrieves cosinus type | ||
auto typeVariant = data.value(COSINUS_TYPE_KEY, COSINUS_TYPE_DEFAULT_VALUE); | ||||
if (!typeVariant.canConvert<QString>()) { | ||||
qCCritical(LOG_CosinusProvider()) << tr("Can't retrieve data: invalid type"); | ||||
return nullptr; | ||||
} | ||||
auto type = cosinusType(typeVariant.toString()); | ||||
if (!type) { | ||||
qCCritical(LOG_CosinusProvider()) << tr("Can't retrieve data: unknown type"); | ||||
return nullptr; | ||||
} | ||||
Alexandre Leroux
|
r780 | // Retrieves frequency | ||
auto freqVariant = data.value(COSINUS_FREQUENCY_KEY, COSINUS_FREQUENCY_DEFAULT_VALUE); | ||||
if (!freqVariant.canConvert<double>()) { | ||||
qCCritical(LOG_CosinusProvider()) << tr("Can't retrieve data: invalid frequency"); | ||||
return nullptr; | ||||
} | ||||
r231 | // Gets the timerange from the parameters | |||
Alexandre Leroux
|
r780 | double freq = freqVariant.toDouble(); | ||
double start = std::ceil(dataRangeRequested.m_TStart * freq); | ||||
double end = std::floor(dataRangeRequested.m_TEnd * freq); | ||||
r231 | ||||
// We assure that timerange is valid | ||||
if (end < start) { | ||||
std::swap(start, end); | ||||
} | ||||
Alexandre Leroux
|
r745 | // Generates scalar series containing cosinus values (one value per second, end value is | ||
// included) | ||||
auto dataCount = end - start + 1; | ||||
r231 | ||||
Alexandre Leroux
|
r785 | // Number of components (depending on the cosinus type) | ||
auto componentCount = type->componentCount(); | ||||
Alexandre Leroux
|
r694 | auto xAxisData = std::vector<double>{}; | ||
Alexandre Leroux
|
r452 | xAxisData.resize(dataCount); | ||
Alexandre Leroux
|
r694 | auto valuesData = std::vector<double>{}; | ||
Alexandre Leroux
|
r785 | valuesData.resize(dataCount * componentCount); | ||
r428 | ||||
int progress = 0; | ||||
Alexandre Leroux
|
r452 | auto progressEnd = dataCount; | ||
Alexandre Leroux
|
r745 | for (auto time = start; time <= end; ++time, ++dataIndex) { | ||
r539 | auto it = m_VariableToEnableProvider.find(acqIdentifier); | |||
r428 | if (it != m_VariableToEnableProvider.end() && it.value()) { | |||
Alexandre Leroux
|
r898 | const auto x = time / freq; | ||
Alexandre Leroux
|
r452 | |||
Alexandre Leroux
|
r898 | xAxisData[dataIndex] = x; | ||
Alexandre Leroux
|
r785 | |||
Alexandre Leroux
|
r898 | // Generates values (depending on the type) | ||
type->generateValues(x, valuesData, dataIndex); | ||||
r428 | ||||
// progression | ||||
int currentProgress = (time - start) * 100.0 / progressEnd; | ||||
if (currentProgress != progress) { | ||||
progress = currentProgress; | ||||
r539 | emit dataProvidedProgress(acqIdentifier, progress); | |||
r811 | qCDebug(LOG_CosinusProvider()) << "TORM: CosinusProvider::retrieveData" | |||
<< QThread::currentThread()->objectName() | ||||
<< progress; | ||||
r750 | // NOTE: Try to use multithread if possible | |||
r428 | } | |||
} | ||||
else { | ||||
if (!it.value()) { | ||||
qCDebug(LOG_CosinusProvider()) | ||||
<< "CosinusProvider::retrieveData: ARRET De l'acquisition detecté" | ||||
<< end - time; | ||||
} | ||||
} | ||||
r231 | } | |||
r750 | if (progress != 100) { | |||
// We can close progression beacause all data has been retrieved | ||||
emit dataProvidedProgress(acqIdentifier, 100); | ||||
} | ||||
Alexandre Leroux
|
r915 | return type->createDataSeries(std::move(xAxisData), std::move(valuesData)); | ||
Alexandre Leroux
|
r128 | } | ||
Alexandre Leroux
|
r310 | |||
r539 | void CosinusProvider::requestDataLoading(QUuid acqIdentifier, | |||
const DataProviderParameters ¶meters) | ||||
Alexandre Leroux
|
r310 | { | ||
r428 | // TODO: Add Mutex | |||
r539 | m_VariableToEnableProvider[acqIdentifier] = true; | |||
r548 | qCDebug(LOG_CosinusProvider()) << "TORM: CosinusProvider::requestDataLoading" | |||
r367 | << QThread::currentThread()->objectName(); | |||
Alexandre Leroux
|
r310 | // NOTE: Try to use multithread if possible | ||
Alexandre Leroux
|
r408 | const auto times = parameters.m_Times; | ||
r428 | ||||
Alexandre Leroux
|
r408 | for (const auto &dateTime : qAsConst(times)) { | ||
r539 | if (m_VariableToEnableProvider[acqIdentifier]) { | |||
Alexandre Leroux
|
r779 | auto scalarSeries = this->retrieveData(acqIdentifier, dateTime, parameters.m_Data); | ||
r539 | emit dataProvided(acqIdentifier, scalarSeries, dateTime); | |||
r428 | } | |||
Alexandre Leroux
|
r310 | } | ||
} | ||||
r422 | ||||
r539 | void CosinusProvider::requestDataAborting(QUuid acqIdentifier) | |||
r422 | { | |||
r539 | qCDebug(LOG_CosinusProvider()) << "CosinusProvider::requestDataAborting" << acqIdentifier | |||
r432 | << QThread::currentThread()->objectName(); | |||
r539 | auto it = m_VariableToEnableProvider.find(acqIdentifier); | |||
r428 | if (it != m_VariableToEnableProvider.end()) { | |||
it.value() = false; | ||||
} | ||||
else { | ||||
r823 | qCDebug(LOG_CosinusProvider()) | |||
r428 | << tr("Aborting progression of inexistant identifier detected !!!"); | |||
} | ||||
r422 | } | |||
r811 | ||||
std::shared_ptr<IDataSeries> 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; | ||||
} | ||||