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