##// END OF EJS Templates
Updates data series creation according to the value type (vector or scalar)
Alexandre Leroux -
r532:522bb785789b
parent child
Show More
@@ -1,194 +1,216
1 #include "AmdaResultParser.h"
1 #include "AmdaResultParser.h"
2
2
3 #include <Common/DateUtils.h>
3 #include <Common/DateUtils.h>
4 #include <Data/ScalarSeries.h>
4 #include <Data/ScalarSeries.h>
5 #include <Data/VectorSeries.h>
5
6
6 #include <QDateTime>
7 #include <QDateTime>
7 #include <QFile>
8 #include <QFile>
8 #include <QRegularExpression>
9 #include <QRegularExpression>
9
10
10 #include <cmath>
11 #include <cmath>
11
12
12 Q_LOGGING_CATEGORY(LOG_AmdaResultParser, "AmdaResultParser")
13 Q_LOGGING_CATEGORY(LOG_AmdaResultParser, "AmdaResultParser")
13
14
14 namespace {
15 namespace {
15
16
16 /// Message in result file when the file was not found on server
17 /// Message in result file when the file was not found on server
17 const auto FILE_NOT_FOUND_MESSAGE = QStringLiteral("Not Found");
18 const auto FILE_NOT_FOUND_MESSAGE = QStringLiteral("Not Found");
18
19
19 /// Format for dates in result files
20 /// Format for dates in result files
20 const auto DATE_FORMAT = QStringLiteral("yyyy-MM-ddThh:mm:ss.zzz");
21 const auto DATE_FORMAT = QStringLiteral("yyyy-MM-ddThh:mm:ss.zzz");
21
22
22 /// Separator between values in a result line
23 /// Separator between values in a result line
23 const auto RESULT_LINE_SEPARATOR = QRegularExpression{QStringLiteral("\\s+")};
24 const auto RESULT_LINE_SEPARATOR = QRegularExpression{QStringLiteral("\\s+")};
24
25
25 /// Regex to find unit in a line. Examples of valid lines:
26 /// Regex to find unit in a line. Examples of valid lines:
26 /// ... - Units : nT - ...
27 /// ... - Units : nT - ...
27 /// ... -Units:nT- ...
28 /// ... -Units:nT- ...
28 /// ... -Units: mΒ²- ...
29 /// ... -Units: mΒ²- ...
29 /// ... - Units : m/s - ...
30 /// ... - Units : m/s - ...
30 const auto UNIT_REGEX = QRegularExpression{QStringLiteral("-\\s*Units\\s*:\\s*(.+?)\\s*-")};
31 const auto UNIT_REGEX = QRegularExpression{QStringLiteral("-\\s*Units\\s*:\\s*(.+?)\\s*-")};
31
32
32 /// Converts a string date to a double date
33 /// Converts a string date to a double date
33 /// @return a double that represents the date in seconds, NaN if the string date can't be converted
34 /// @return a double that represents the date in seconds, NaN if the string date can't be converted
34 double doubleDate(const QString &stringDate) noexcept
35 double doubleDate(const QString &stringDate) noexcept
35 {
36 {
36 auto dateTime = QDateTime::fromString(stringDate, DATE_FORMAT);
37 auto dateTime = QDateTime::fromString(stringDate, DATE_FORMAT);
37 dateTime.setTimeSpec(Qt::UTC);
38 dateTime.setTimeSpec(Qt::UTC);
38 return dateTime.isValid() ? DateUtils::secondsSinceEpoch(dateTime)
39 return dateTime.isValid() ? DateUtils::secondsSinceEpoch(dateTime)
39 : std::numeric_limits<double>::quiet_NaN();
40 : std::numeric_limits<double>::quiet_NaN();
40 }
41 }
41
42
42 /// Checks if a line is a comment line
43 /// Checks if a line is a comment line
43 bool isCommentLine(const QString &line)
44 bool isCommentLine(const QString &line)
44 {
45 {
45 return line.startsWith("#");
46 return line.startsWith("#");
46 }
47 }
47
48
48 /// @return the number of lines to be read depending on the type of value passed in parameter
49 /// @return the number of lines to be read depending on the type of value passed in parameter
49 int nbValues(AmdaResultParser::ValueType valueType) noexcept
50 int nbValues(AmdaResultParser::ValueType valueType) noexcept
50 {
51 {
51 switch (valueType) {
52 switch (valueType) {
52 case AmdaResultParser::ValueType::SCALAR:
53 case AmdaResultParser::ValueType::SCALAR:
53 return 1;
54 return 1;
54 case AmdaResultParser::ValueType::VECTOR:
55 case AmdaResultParser::ValueType::VECTOR:
55 return 3;
56 return 3;
56 case AmdaResultParser::ValueType::UNKNOWN:
57 case AmdaResultParser::ValueType::UNKNOWN:
57 // Invalid case
58 // Invalid case
58 break;
59 break;
59 }
60 }
60
61
61 // Invalid cases
62 // Invalid cases
62 qCCritical(LOG_AmdaResultParser())
63 qCCritical(LOG_AmdaResultParser())
63 << QObject::tr("Can't get the number of values to read: unsupported type");
64 << QObject::tr("Can't get the number of values to read: unsupported type");
64 return 0;
65 return 0;
65 }
66 }
66
67
67 /**
68 /**
68 * Reads stream to retrieve x-axis unit
69 * Reads stream to retrieve x-axis unit
69 * @param stream the stream to read
70 * @param stream the stream to read
70 * @return the unit that has been read in the stream, a default unit (time unit with no label) if an
71 * @return the unit that has been read in the stream, a default unit (time unit with no label) if an
71 * error occured during reading
72 * error occured during reading
72 */
73 */
73 Unit readXAxisUnit(QTextStream &stream)
74 Unit readXAxisUnit(QTextStream &stream)
74 {
75 {
75 QString line{};
76 QString line{};
76
77
77 // Searches unit in the comment lines
78 // Searches unit in the comment lines
78 while (stream.readLineInto(&line) && isCommentLine(line)) {
79 while (stream.readLineInto(&line) && isCommentLine(line)) {
79 auto match = UNIT_REGEX.match(line);
80 auto match = UNIT_REGEX.match(line);
80 if (match.hasMatch()) {
81 if (match.hasMatch()) {
81 return Unit{match.captured(1), true};
82 return Unit{match.captured(1), true};
82 }
83 }
83 }
84 }
84
85
85 qCWarning(LOG_AmdaResultParser()) << QObject::tr("The unit could not be found in the file");
86 qCWarning(LOG_AmdaResultParser()) << QObject::tr("The unit could not be found in the file");
86
87
87 // Error cases
88 // Error cases
88 return Unit{{}, true};
89 return Unit{{}, true};
89 }
90 }
90
91
91 /**
92 /**
92 * Reads stream to retrieve results
93 * Reads stream to retrieve results
93 * @param stream the stream to read
94 * @param stream the stream to read
94 * @return the pair of vectors x-axis data/values data that has been read in the stream
95 * @return the pair of vectors x-axis data/values data that has been read in the stream
95 */
96 */
96 QPair<QVector<double>, QVector<QVector<double> > >
97 QPair<QVector<double>, QVector<QVector<double> > >
97 readResults(QTextStream &stream, AmdaResultParser::ValueType valueType)
98 readResults(QTextStream &stream, AmdaResultParser::ValueType valueType)
98 {
99 {
99 auto expectedNbValues = nbValues(valueType);
100 auto expectedNbValues = nbValues(valueType);
100
101
101 auto xData = QVector<double>{};
102 auto xData = QVector<double>{};
102 auto valuesData = QVector<QVector<double> >(expectedNbValues);
103 auto valuesData = QVector<QVector<double> >(expectedNbValues);
103
104
104 QString line{};
105 QString line{};
105
106
106 while (stream.readLineInto(&line)) {
107 while (stream.readLineInto(&line)) {
107 // Ignore comment lines
108 // Ignore comment lines
108 if (!isCommentLine(line)) {
109 if (!isCommentLine(line)) {
109 auto lineData = line.split(RESULT_LINE_SEPARATOR, QString::SkipEmptyParts);
110 auto lineData = line.split(RESULT_LINE_SEPARATOR, QString::SkipEmptyParts);
110 if (lineData.size() == expectedNbValues + 1) {
111 if (lineData.size() == expectedNbValues + 1) {
111 // X : the data is converted from date to double (in secs)
112 // X : the data is converted from date to double (in secs)
112 auto x = doubleDate(lineData.at(0));
113 auto x = doubleDate(lineData.at(0));
113
114
114 // Adds result only if x is valid. Then, if value is invalid, it is set to NaN
115 // Adds result only if x is valid. Then, if value is invalid, it is set to NaN
115 if (!std::isnan(x)) {
116 if (!std::isnan(x)) {
116 xData.push_back(x);
117 xData.push_back(x);
117
118
118 // Values
119 // Values
119 for (auto valueIndex = 0; valueIndex < expectedNbValues; ++valueIndex) {
120 for (auto valueIndex = 0; valueIndex < expectedNbValues; ++valueIndex) {
120 auto column = valueIndex + 1;
121 auto column = valueIndex + 1;
121
122
122 bool valueOk;
123 bool valueOk;
123 auto value = lineData.at(column).toDouble(&valueOk);
124 auto value = lineData.at(column).toDouble(&valueOk);
124
125
125 if (!valueOk) {
126 if (!valueOk) {
126 qCWarning(LOG_AmdaResultParser())
127 qCWarning(LOG_AmdaResultParser())
127 << QObject::tr(
128 << QObject::tr(
128 "Value from (line %1, column %2) is invalid and will be "
129 "Value from (line %1, column %2) is invalid and will be "
129 "converted to NaN")
130 "converted to NaN")
130 .arg(line, column);
131 .arg(line, column);
131 value = std::numeric_limits<double>::quiet_NaN();
132 value = std::numeric_limits<double>::quiet_NaN();
132 }
133 }
133 valuesData[valueIndex].append(value);
134 valuesData[valueIndex].append(value);
134 }
135 }
135 }
136 }
136 else {
137 else {
137 qCWarning(LOG_AmdaResultParser())
138 qCWarning(LOG_AmdaResultParser())
138 << QObject::tr("Can't retrieve results from line %1: x is invalid")
139 << QObject::tr("Can't retrieve results from line %1: x is invalid")
139 .arg(line);
140 .arg(line);
140 }
141 }
141 }
142 }
142 else {
143 else {
143 qCWarning(LOG_AmdaResultParser())
144 qCWarning(LOG_AmdaResultParser())
144 << QObject::tr("Can't retrieve results from line %1: invalid line").arg(line);
145 << QObject::tr("Can't retrieve results from line %1: invalid line").arg(line);
145 }
146 }
146 }
147 }
147 }
148 }
148
149
149 return qMakePair(std::move(xData), std::move(valuesData));
150 return qMakePair(std::move(xData), std::move(valuesData));
150 }
151 }
151
152
152 } // namespace
153 } // namespace
153
154
154 std::shared_ptr<IDataSeries> AmdaResultParser::readTxt(const QString &filePath,
155 std::shared_ptr<IDataSeries> AmdaResultParser::readTxt(const QString &filePath,
155 ValueType valueType) noexcept
156 ValueType valueType) noexcept
156 {
157 {
157 if (valueType == ValueType::UNKNOWN) {
158 if (valueType == ValueType::UNKNOWN) {
158 qCCritical(LOG_AmdaResultParser())
159 qCCritical(LOG_AmdaResultParser())
159 << QObject::tr("Can't retrieve AMDA data: the type of values to be read is unknown");
160 << QObject::tr("Can't retrieve AMDA data: the type of values to be read is unknown");
160 return nullptr;
161 return nullptr;
161 }
162 }
162
163
163 QFile file{filePath};
164 QFile file{filePath};
164
165
165 if (!file.open(QFile::ReadOnly | QIODevice::Text)) {
166 if (!file.open(QFile::ReadOnly | QIODevice::Text)) {
166 qCCritical(LOG_AmdaResultParser())
167 qCCritical(LOG_AmdaResultParser())
167 << QObject::tr("Can't retrieve AMDA data from file %1: %2")
168 << QObject::tr("Can't retrieve AMDA data from file %1: %2")
168 .arg(filePath, file.errorString());
169 .arg(filePath, file.errorString());
169 return nullptr;
170 return nullptr;
170 }
171 }
171
172
172 QTextStream stream{&file};
173 QTextStream stream{&file};
173
174
174 // Checks if the file was found on the server
175 // Checks if the file was found on the server
175 auto firstLine = stream.readLine();
176 auto firstLine = stream.readLine();
176 if (firstLine.compare(FILE_NOT_FOUND_MESSAGE) == 0) {
177 if (firstLine.compare(FILE_NOT_FOUND_MESSAGE) == 0) {
177 qCCritical(LOG_AmdaResultParser())
178 qCCritical(LOG_AmdaResultParser())
178 << QObject::tr("Can't retrieve AMDA data from file %1: file was not found on server")
179 << QObject::tr("Can't retrieve AMDA data from file %1: file was not found on server")
179 .arg(filePath);
180 .arg(filePath);
180 return nullptr;
181 return nullptr;
181 }
182 }
182
183
183 // Reads x-axis unit
184 // Reads x-axis unit
184 stream.seek(0); // returns to the beginning of the file
185 stream.seek(0); // returns to the beginning of the file
185 auto xAxisUnit = readXAxisUnit(stream);
186 auto xAxisUnit = readXAxisUnit(stream);
186
187
187 // Reads results
188 // Reads results
188 stream.seek(0); // returns to the beginning of the file
189 stream.seek(0); // returns to the beginning of the file
189 auto results = readResults(stream, valueType);
190 auto results = readResults(stream, valueType);
190
191
192 // Creates data series
193 switch (valueType) {
194 case ValueType::SCALAR:
195 Q_ASSERT(results.second.size() == 1);
196 return std::make_shared<ScalarSeries>(
197 std::move(results.first), std::move(results.second.takeFirst()), xAxisUnit, Unit{});
198 case ValueType::VECTOR: {
199 Q_ASSERT(results.second.size() == 3);
200 auto xValues = results.second.takeFirst();
201 auto yValues = results.second.takeFirst();
202 auto zValues = results.second.takeFirst();
203 return std::make_shared<VectorSeries>(std::move(results.first), std::move(xValues),
204 std::move(yValues), std::move(zValues), xAxisUnit,
205 Unit{});
206 }
207 case ValueType::UNKNOWN:
208 // Invalid case
209 break;
210 }
191
211
192 return std::make_shared<ScalarSeries>(std::move(results.first), std::move(results.second),
212 // Invalid cases
193 xAxisUnit, Unit{});
213 qCCritical(LOG_AmdaResultParser())
214 << QObject::tr("Can't create data series: unsupported value type");
215 return nullptr;
194 }
216 }
General Comments 0
You need to be logged in to leave comments. Login now