##// END OF EJS Templates
Add satic method to compute vector of in or not in ranges between...
Add satic method to compute vector of in or not in ranges between two ranges

File last commit:

r792:c2af6c15676c
r821:97f935302b90
Show More
AmdaResultParser.cpp
237 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);
// 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;
}