##// END OF EJS Templates
Updates AMDA provider to handle different server URLs...
Updates AMDA provider to handle different server URLs Retrieves product's attribute (if it exists) that indicates the server to use to get its data, and generates correct URL according to it

File last commit:

r832:4ab17865bd43
r984:a2f640a8dec0
Show More
AmdaResultParser.cpp
240 lines | 8.0 KiB | text/x-c | CppLexer
/ plugins / amda / src / AmdaResultParser.cpp
#include "AmdaResultParser.h"
#include <Common/DateUtils.h>
#include <Data/ScalarSeries.h>
#include <Data/VectorSeries.h>
#include <QDateTime>
#include <QFile>
#include <QRegularExpression>
#include <cmath>
Q_LOGGING_CATEGORY(LOG_AmdaResultParser, "AmdaResultParser")
namespace {
/// Message in result file when the file was not found on server
const auto FILE_NOT_FOUND_MESSAGE = QStringLiteral("Not Found");
/// Separator between values in a result line
const auto RESULT_LINE_SEPARATOR = QRegularExpression{QStringLiteral("\\s+")};
// AMDA V2
// /// Regex to find the header of the data in the file. This header indicates the end of comments
// in
// /// the file
// const auto DATA_HEADER_REGEX = QRegularExpression{QStringLiteral("#\\s*DATA\\s*:")};
/// Format for dates in result files
const auto DATE_FORMAT = QStringLiteral("yyyy-MM-ddThh:mm:ss.zzz");
// AMDA V2
// /// Regex to find unit in a line. Examples of valid lines:
// /// ... PARAMETER_UNITS : nT ...
// /// ... PARAMETER_UNITS:nT ...
// /// ... PARAMETER_UNITS: m² ...
// /// ... PARAMETER_UNITS : m/s ...
// const auto UNIT_REGEX = QRegularExpression{QStringLiteral("\\s*PARAMETER_UNITS\\s*:\\s*(.+)")};
/// ... - Units : nT - ...
/// ... -Units:nT- ...
/// ... -Units: m²- ...
/// ... - Units : m/s - ...
const auto UNIT_REGEX = QRegularExpression{QStringLiteral("-\\s*Units\\s*:\\s*(.+?)\\s*-")};
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<double>::quiet_NaN();
}
/// Checks if a line is a comment line
bool isCommentLine(const QString &line)
{
return line.startsWith("#");
}
/// @return the number of lines to be read depending on the type of value passed in parameter
int nbValues(AmdaResultParser::ValueType valueType) noexcept
{
switch (valueType) {
case AmdaResultParser::ValueType::SCALAR:
return 1;
case AmdaResultParser::ValueType::VECTOR:
return 3;
case AmdaResultParser::ValueType::UNKNOWN:
// Invalid case
break;
}
// Invalid cases
qCCritical(LOG_AmdaResultParser())
<< QObject::tr("Can't get the number of values to read: unsupported type");
return 0;
}
/**
* Reads stream to retrieve x-axis unit
* @param stream the stream to read
* @return the unit that has been read in the stream, a default unit (time unit with no label) if an
* error occured during reading
*/
Unit readXAxisUnit(QTextStream &stream)
{
QString line{};
// Searches unit in the comment lines (as long as the reading has not reached the data header)
// AMDA V2: while (stream.readLineInto(&line) && !line.contains(DATA_HEADER_REGEX)) {
while (stream.readLineInto(&line) && isCommentLine(line)) {
auto match = UNIT_REGEX.match(line);
if (match.hasMatch()) {
return Unit{match.captured(1), true};
}
}
qCWarning(LOG_AmdaResultParser()) << QObject::tr("The unit could not be found in the file");
// Error cases
return Unit{{}, true};
}
/**
* Reads stream to retrieve results
* @param stream the stream to read
* @return the pair of vectors x-axis data/values data that has been read in the stream
*/
std::pair<std::vector<double>, std::vector<double> >
readResults(QTextStream &stream, AmdaResultParser::ValueType valueType)
{
auto expectedNbValues = nbValues(valueType) + 1;
auto xData = std::vector<double>{};
auto valuesData = std::vector<double>{};
QString line{};
// Skip comment lines
while (stream.readLineInto(&line) && isCommentLine(line)) {
}
if (!stream.atEnd()) {
do {
auto lineData = line.split(RESULT_LINE_SEPARATOR, QString::SkipEmptyParts);
if (lineData.size() == expectedNbValues) {
// 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)) {
xData.push_back(x);
// Values
for (auto valueIndex = 1; valueIndex < expectedNbValues; ++valueIndex) {
auto column = valueIndex;
bool valueOk;
auto value = lineData.at(column).toDouble(&valueOk);
if (!valueOk) {
qCWarning(LOG_AmdaResultParser())
<< QObject::tr(
"Value from (line %1, column %2) is invalid and will be "
"converted to NaN")
.arg(line, column);
value = std::numeric_limits<double>::quiet_NaN();
}
valuesData.push_back(value);
}
}
else {
qCWarning(LOG_AmdaResultParser())
<< QObject::tr("Can't retrieve results from line %1: x is invalid")
.arg(line);
}
}
else {
qCWarning(LOG_AmdaResultParser())
<< QObject::tr("Can't retrieve results from line %1: invalid line").arg(line);
}
} while (stream.readLineInto(&line));
}
return std::make_pair(std::move(xData), std::move(valuesData));
}
} // namespace
std::shared_ptr<IDataSeries> AmdaResultParser::readTxt(const QString &filePath,
ValueType valueType) noexcept
{
if (valueType == ValueType::UNKNOWN) {
qCCritical(LOG_AmdaResultParser())
<< QObject::tr("Can't retrieve AMDA data: the type of values to be read is unknown");
return nullptr;
}
QFile file{filePath};
if (!file.open(QFile::ReadOnly | QIODevice::Text)) {
qCCritical(LOG_AmdaResultParser())
<< QObject::tr("Can't retrieve AMDA data from file %1: %2")
.arg(filePath, file.errorString());
return nullptr;
}
QTextStream stream{&file};
// Checks if the file was found on the server
auto firstLine = stream.readLine();
if (firstLine.compare(FILE_NOT_FOUND_MESSAGE) == 0) {
qCCritical(LOG_AmdaResultParser())
<< QObject::tr("Can't retrieve AMDA data from file %1: file was not found on server")
.arg(filePath);
return nullptr;
}
// Reads x-axis unit
stream.seek(0); // returns to the beginning of the file
auto xAxisUnit = readXAxisUnit(stream);
if (xAxisUnit.m_Name.isEmpty()) {
return nullptr;
}
// Reads results
// AMDA V2: remove line
stream.seek(0); // returns to the beginning of the file
auto results = readResults(stream, valueType);
// Creates data series
switch (valueType) {
case ValueType::SCALAR:
return std::make_shared<ScalarSeries>(std::move(results.first),
std::move(results.second), xAxisUnit, Unit{});
case ValueType::VECTOR:
return std::make_shared<VectorSeries>(std::move(results.first),
std::move(results.second), xAxisUnit, Unit{});
case ValueType::UNKNOWN:
// Invalid case
break;
}
// Invalid cases
qCCritical(LOG_AmdaResultParser())
<< QObject::tr("Can't create data series: unsupported value type");
return nullptr;
}