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