AmdaResultParser.cpp
147 lines
| 4.6 KiB
| text/x-c
|
CppLexer
Alexandre Leroux
|
r380 | #include "AmdaResultParser.h" | ||
Alexandre Leroux
|
r489 | #include <Common/DateUtils.h> | ||
Alexandre Leroux
|
r380 | #include <Data/ScalarSeries.h> | ||
#include <QDateTime> | ||||
#include <QFile> | ||||
Alexandre Leroux
|
r393 | #include <QRegularExpression> | ||
Alexandre Leroux
|
r380 | |||
r400 | #include <cmath> | |||
Alexandre Leroux
|
r380 | Q_LOGGING_CATEGORY(LOG_AmdaResultParser, "AmdaResultParser") | ||
namespace { | ||||
Alexandre Leroux
|
r446 | /// Message in result file when the file was not found on server | ||
const auto FILE_NOT_FOUND_MESSAGE = QStringLiteral("Not Found"); | ||||
Alexandre Leroux
|
r380 | /// Format for dates in result files | ||
const auto DATE_FORMAT = QStringLiteral("yyyy-MM-ddThh:mm:ss.zzz"); | ||||
Alexandre Leroux
|
r394 | /// Separator between values in a result line | ||
const auto RESULT_LINE_SEPARATOR = QRegularExpression{QStringLiteral("\\s+")}; | ||||
Alexandre Leroux
|
r393 | /// Regex to find unit in a line. Examples of valid lines: | ||
/// ... - Units : nT - ... | ||||
/// ... -Units:nT- ... | ||||
/// ... -Units: m²- ... | ||||
/// ... - Units : m/s - ... | ||||
const auto UNIT_REGEX = QRegularExpression{QStringLiteral("-\\s*Units\\s*:\\s*(.+?)\\s*-")}; | ||||
Alexandre Leroux
|
r394 | /// 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 | ||||
Alexandre Leroux
|
r380 | double doubleDate(const QString &stringDate) noexcept | ||
{ | ||||
auto dateTime = QDateTime::fromString(stringDate, DATE_FORMAT); | ||||
Alexandre Leroux
|
r489 | dateTime.setTimeSpec(Qt::UTC); | ||
return dateTime.isValid() ? DateUtils::secondsSinceEpoch(dateTime) | ||||
Alexandre Leroux
|
r394 | : std::numeric_limits<double>::quiet_NaN(); | ||
Alexandre Leroux
|
r380 | } | ||
Alexandre Leroux
|
r393 | /** | ||
* 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{}; | ||||
if (stream.readLineInto(&line)) { | ||||
auto match = UNIT_REGEX.match(line); | ||||
if (match.hasMatch()) { | ||||
return Unit{match.captured(1), true}; | ||||
} | ||||
else { | ||||
qCWarning(LOG_AmdaResultParser()) | ||||
<< QObject::tr("Can't read unit: invalid line %1").arg(line); | ||||
} | ||||
} | ||||
else { | ||||
qCWarning(LOG_AmdaResultParser()) << QObject::tr("Can't read unit: end of file"); | ||||
} | ||||
// Error cases | ||||
return Unit{{}, true}; | ||||
} | ||||
Alexandre Leroux
|
r394 | /** | ||
* 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 | ||||
*/ | ||||
QPair<QVector<double>, QVector<double> > readResults(QTextStream &stream) | ||||
{ | ||||
auto xData = QVector<double>{}; | ||||
auto valuesData = QVector<double>{}; | ||||
QString line{}; | ||||
while (stream.readLineInto(&line)) { | ||||
auto lineData = line.split(RESULT_LINE_SEPARATOR, QString::SkipEmptyParts); | ||||
if (lineData.size() == 2) { | ||||
// X : the data is converted from date to double (in secs) | ||||
auto x = doubleDate(lineData.at(0)); | ||||
// Value | ||||
bool valueOk; | ||||
auto value = lineData.at(1).toDouble(&valueOk); | ||||
// Adds result only if x and value are valid | ||||
if (!std::isnan(x) && !std::isnan(value) && valueOk) { | ||||
xData.push_back(x); | ||||
valuesData.push_back(value); | ||||
} | ||||
else { | ||||
qCWarning(LOG_AmdaResultParser()) | ||||
<< QObject::tr( | ||||
"Can't retrieve results from line %1: x and/or value are invalid") | ||||
.arg(line); | ||||
} | ||||
} | ||||
else { | ||||
qCWarning(LOG_AmdaResultParser()) | ||||
<< QObject::tr("Can't retrieve results from line %1: invalid line").arg(line); | ||||
} | ||||
} | ||||
return qMakePair(std::move(xData), std::move(valuesData)); | ||||
} | ||||
Alexandre Leroux
|
r380 | } // namespace | ||
std::shared_ptr<IDataSeries> AmdaResultParser::readTxt(const QString &filePath) noexcept | ||||
{ | ||||
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}; | ||||
Alexandre Leroux
|
r446 | // 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; | ||||
} | ||||
// Ignore comments lines | ||||
Alexandre Leroux
|
r380 | stream.readLine(); | ||
Alexandre Leroux
|
r393 | // Reads x-axis unit | ||
auto xAxisUnit = readXAxisUnit(stream); | ||||
// Reads results | ||||
Alexandre Leroux
|
r394 | auto results = readResults(stream); | ||
Alexandre Leroux
|
r380 | |||
Alexandre Leroux
|
r394 | return std::make_shared<ScalarSeries>(std::move(results.first), std::move(results.second), | ||
xAxisUnit, Unit{}); | ||||
Alexandre Leroux
|
r380 | } | ||