diff --git a/plugins/amda/include/AmdaResultParserHelper.h b/plugins/amda/include/AmdaResultParserHelper.h index e161fd4..b25d063 100644 --- a/plugins/amda/include/AmdaResultParserHelper.h +++ b/plugins/amda/include/AmdaResultParserHelper.h @@ -56,7 +56,12 @@ public: void readResultLine(const QString &line) override; private: + /// @return the reading order of the "value" columns for a result line of the AMDA file + std::vector valuesIndexes() const; + Properties m_Properties{}; + std::vector m_XAxisData{}; + std::vector m_ValuesData{}; }; /** @@ -70,7 +75,12 @@ public: void readResultLine(const QString &line) override; private: + /// @return the reading order of the "value" columns for a result line of the AMDA file + std::vector valuesIndexes() const; + Properties m_Properties{}; + std::vector m_XAxisData{}; + std::vector m_ValuesData{}; }; #endif // SCIQLOP_AMDARESULTPARSERHELPER_H diff --git a/plugins/amda/src/AmdaResultParserHelper.cpp b/plugins/amda/src/AmdaResultParserHelper.cpp index 8a9d3b2..da7c057 100644 --- a/plugins/amda/src/AmdaResultParserHelper.cpp +++ b/plugins/amda/src/AmdaResultParserHelper.cpp @@ -1,11 +1,28 @@ #include "AmdaResultParserHelper.h" +#include + +#include #include +#include + +#include +#include Q_LOGGING_CATEGORY(LOG_AmdaResultParserHelper, "AmdaResultParserHelper") namespace { +// ///////// // +// Constants // +// ///////// // + +/// Separator between values in a result line +const auto RESULT_LINE_SEPARATOR = QRegularExpression{QStringLiteral("\\s+")}; + +/// Format for dates in result files +const auto DATE_FORMAT = QStringLiteral("yyyy-MM-ddThh:mm:ss.zzz"); + // /////// // // Methods // // /////// // @@ -28,6 +45,87 @@ bool checkUnit(const Properties &properties, const QString &key, const QString & return true; } +QDateTime dateTimeFromString(const QString &stringDate) noexcept +{ +#if QT_VERSION >= QT_VERSION_CHECK(5, 8, 0) + return QDateTime::fromString(stringDate, Qt::ISODateWithMs); +#else + return QDateTime::fromString(stringDate, DATE_FORMAT); +#endif +} + +/// Converts a string date to a double date +/// @return a double that represents the date in seconds, NaN if the string date can't be converted +double doubleDate(const QString &stringDate) noexcept +{ + // Format: yyyy-MM-ddThh:mm:ss.zzz + auto dateTime = dateTimeFromString(stringDate); + dateTime.setTimeSpec(Qt::UTC); + return dateTime.isValid() ? DateUtils::secondsSinceEpoch(dateTime) + : std::numeric_limits::quiet_NaN(); +} + +/** + * Reads a line from the AMDA file and tries to extract a x-axis data and value data from it + * @param xAxisData the vector in which to store the x-axis data extracted + * @param valuesData the vector in which to store the value extracted + * @param line the line to read to extract the property + * @param valuesIndexes indexes of insertion of read values. For example, if the line contains three + * columns of values, and valuesIndexes are {2, 0, 1}, the value of the third column will be read + * and inserted first, then the value of the first column, and finally the value of the second + * column. + * @param fillValue value that tags an invalid data. For example, if fillValue is -1 and a read + * value is -1, then this value is considered as invalid and converted to NaN + */ +void tryReadResult(std::vector &xAxisData, std::vector &valuesData, + const QString &line, const std::vector &valuesIndexes, + double fillValue = std::numeric_limits::quiet_NaN()) +{ + auto lineData = line.split(RESULT_LINE_SEPARATOR, QString::SkipEmptyParts); + + // Checks that the line contains expected number of values + x-axis value + if (lineData.size() == valuesIndexes.size() + 1) { + // X : the data is converted from date to double (in secs) + auto x = doubleDate(lineData.at(0)); + + // Adds result only if x is valid. Then, if value is invalid, it is set to NaN + if (!std::isnan(x)) { + xAxisData.push_back(x); + + // Values + for (auto valueIndex : valuesIndexes) { + bool valueOk; + // we use valueIndex + 1 to skip column 0 (x-axis value) + auto value = lineData.at(valueIndex + 1).toDouble(&valueOk); + + if (!valueOk) { + qCWarning(LOG_AmdaResultParserHelper()) + << QObject::tr( + "Value from (line %1, column %2) is invalid and will be " + "converted to NaN") + .arg(line, valueIndex); + value = std::numeric_limits::quiet_NaN(); + } + + // Handles fill value + if (!std::isnan(fillValue) && !std::isnan(value) && fillValue == value) { + value = std::numeric_limits::quiet_NaN(); + } + + valuesData.push_back(value); + } + } + else { + qCWarning(LOG_AmdaResultParserHelper()) + << QObject::tr("Can't retrieve results from line %1: x is invalid").arg(line); + } + } + else { + qCWarning(LOG_AmdaResultParserHelper()) + << QObject::tr("Can't retrieve results from line %1: invalid line").arg(line); + } +} + /** * Reads a line from the AMDA file and tries to extract a property from it * @param properties the properties map in which to put the property extracted from the line @@ -81,7 +179,9 @@ bool ScalarParserHelper::checkProperties() std::shared_ptr ScalarParserHelper::createSeries() { - /// @todo ALX + return std::make_shared(std::move(m_XAxisData), std::move(m_ValuesData), + m_Properties.value(X_AXIS_UNIT_PROPERTY).value(), + m_Properties.value(VALUES_UNIT_PROPERTY).value()); } void ScalarParserHelper::readPropertyLine(const QString &line) @@ -91,7 +191,14 @@ void ScalarParserHelper::readPropertyLine(const QString &line) void ScalarParserHelper::readResultLine(const QString &line) { - /// @todo ALX + tryReadResult(m_XAxisData, m_ValuesData, line, valuesIndexes()); +} + +std::vector ScalarParserHelper::valuesIndexes() const +{ + // Only one value to read + static auto result = std::vector{0}; + return result; } // ////////////////// // @@ -106,7 +213,9 @@ bool VectorParserHelper::checkProperties() std::shared_ptr VectorParserHelper::createSeries() { - /// @todo ALX + return std::make_shared(std::move(m_XAxisData), std::move(m_ValuesData), + m_Properties.value(X_AXIS_UNIT_PROPERTY).value(), + m_Properties.value(VALUES_UNIT_PROPERTY).value()); } void VectorParserHelper::readPropertyLine(const QString &line) @@ -116,5 +225,12 @@ void VectorParserHelper::readPropertyLine(const QString &line) void VectorParserHelper::readResultLine(const QString &line) { - /// @todo ALX + tryReadResult(m_XAxisData, m_ValuesData, line, valuesIndexes()); +} + +std::vector VectorParserHelper::valuesIndexes() const +{ + // 3 values to read, in order in the file (x, y, z) + static auto result = std::vector{0, 1, 2}; + return result; }