##// END OF EJS Templates
Parser refactoring (5)...
Alexandre Leroux -
r948:64a6137ab3a2
parent child
Show More
@@ -1,35 +1,40
1 1 #ifndef SCIQLOP_AMDARESULTPARSERDEFS_H
2 2 #define SCIQLOP_AMDARESULTPARSERDEFS_H
3 3
4 4 #include <QtCore/QRegularExpression>
5 5 #include <QtCore/QString>
6 6 #include <QtCore/QVariantHash>
7 7
8 8 // ////////// //
9 9 // Properties //
10 10 // ////////// //
11 11
12 12 /// Alias to represent properties read in the header of AMDA file
13 13 using Properties = QVariantHash;
14 14
15 15 extern const QString X_AXIS_UNIT_PROPERTY;
16 16
17 17 // /////////////////// //
18 18 // Regular expressions //
19 19 // /////////////////// //
20 20
21 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 27 // /// ... PARAMETER_UNITS : nT ...
23 28 // /// ... PARAMETER_UNITS:nT ...
24 29 // /// ... PARAMETER_UNITS: mΒ² ...
25 30 // /// ... PARAMETER_UNITS : m/s ...
26 31 // const auto UNIT_REGEX = QRegularExpression{QStringLiteral("\\s*PARAMETER_UNITS\\s*:\\s*(.+)")};
27 32
28 33 /// Regex to find x-axis unit in a line. Examples of valid lines:
29 34 /// ... - Units : nT - ...
30 35 /// ... -Units:nT- ...
31 36 /// ... -Units: mΒ²- ...
32 37 /// ... - Units : m/s - ...
33 38 extern const QRegularExpression DEFAULT_X_AXIS_UNIT_REGEX;
34 39
35 40 #endif // SCIQLOP_AMDARESULTPARSERDEFS_H
@@ -1,240 +1,131
1 1 #include "AmdaResultParser.h"
2 2
3 #include <Common/DateUtils.h>
4 #include <Data/ScalarSeries.h>
5 #include <Data/VectorSeries.h>
3 #include "AmdaResultParserHelper.h"
6 4
7 #include <QDateTime>
8 5 #include <QFile>
9 #include <QRegularExpression>
10 6
11 7 #include <cmath>
12 8
13 9 Q_LOGGING_CATEGORY(LOG_AmdaResultParser, "AmdaResultParser")
14 10
15 11 namespace {
16 12
17 13 /// Message in result file when the file was not found on server
18 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 16 /// Checks if a line is a comment line
67 17 bool isCommentLine(const QString &line)
68 18 {
69 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
73 int nbValues(AmdaResultParser::ValueType valueType) noexcept
22 /**
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 29 switch (valueType) {
76 30 case AmdaResultParser::ValueType::SCALAR:
77 return 1;
31 return std::make_unique<ScalarParserHelper>();
78 32 case AmdaResultParser::ValueType::VECTOR:
79 return 3;
33 return std::make_unique<VectorParserHelper>();
80 34 case AmdaResultParser::ValueType::UNKNOWN:
81 35 // Invalid case
82 36 break;
83 37 }
84 38
85 39 // Invalid cases
86 40 qCCritical(LOG_AmdaResultParser())
87 << QObject::tr("Can't get the number of values to read: unsupported type");
88 return 0;
41 << QObject::tr("Can't create helper to read result file: unsupported type");
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 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{};
100
101 // Searches unit in the comment lines (as long as the reading has not reached the data header)
52 // Searches properties in the comment lines (as long as the reading has not reached the data)
102 53 // AMDA V2: while (stream.readLineInto(&line) && !line.contains(DATA_HEADER_REGEX)) {
54 QString line{};
103 55 while (stream.readLineInto(&line) && isCommentLine(line)) {
104 auto match = UNIT_REGEX.match(line);
105 if (match.hasMatch()) {
106 return Unit{match.captured(1), true};
107 }
56 helper.readPropertyLine(line);
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 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> >
122 readResults(QTextStream &stream, AmdaResultParser::ValueType valueType)
65 void readResults(IAmdaResultParserHelper &helper, QTextStream &stream)
123 66 {
124 auto expectedNbValues = nbValues(valueType) + 1;
125
126 auto xData = std::vector<double>{};
127 auto valuesData = std::vector<double>{};
128
129 67 QString line{};
130 68
131 69 // Skip comment lines
132 70 while (stream.readLineInto(&line) && isCommentLine(line)) {
133 71 }
134 72
135 73 if (!stream.atEnd()) {
136 74 do {
137 auto lineData = line.split(RESULT_LINE_SEPARATOR, QString::SkipEmptyParts);
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 }
75 helper.readResultLine(line);
174 76 } while (stream.readLineInto(&line));
175 77 }
176
177 return std::make_pair(std::move(xData), std::move(valuesData));
178 78 }
179 79
180 80 } // namespace
181 81
182 82 std::shared_ptr<IDataSeries> AmdaResultParser::readTxt(const QString &filePath,
183 83 ValueType valueType) noexcept
184 84 {
185 85 if (valueType == ValueType::UNKNOWN) {
186 86 qCCritical(LOG_AmdaResultParser())
187 87 << QObject::tr("Can't retrieve AMDA data: the type of values to be read is unknown");
188 88 return nullptr;
189 89 }
190 90
191 91 QFile file{filePath};
192 92
193 93 if (!file.open(QFile::ReadOnly | QIODevice::Text)) {
194 94 qCCritical(LOG_AmdaResultParser())
195 95 << QObject::tr("Can't retrieve AMDA data from file %1: %2")
196 96 .arg(filePath, file.errorString());
197 97 return nullptr;
198 98 }
199 99
200 100 QTextStream stream{&file};
201 101
202 102 // Checks if the file was found on the server
203 103 auto firstLine = stream.readLine();
204 104 if (firstLine.compare(FILE_NOT_FOUND_MESSAGE) == 0) {
205 105 qCCritical(LOG_AmdaResultParser())
206 106 << QObject::tr("Can't retrieve AMDA data from file %1: file was not found on server")
207 107 .arg(filePath);
208 108 return nullptr;
209 109 }
210 110
211 // Reads x-axis unit
212 stream.seek(0); // returns to the beginning of the file
213 auto xAxisUnit = readXAxisUnit(stream);
214 if (xAxisUnit.m_Name.isEmpty()) {
215 return nullptr;
216 }
111 auto helper = createHelper(valueType);
112 Q_ASSERT(helper != nullptr);
217 113
218 // Reads results
219 // AMDA V2: remove line
114 // Reads header file to retrieve properties
220 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
224 switch (valueType) {
225 case ValueType::SCALAR:
226 return std::make_shared<ScalarSeries>(std::move(results.first),
227 std::move(results.second), xAxisUnit, Unit{});
228 case ValueType::VECTOR:
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 }
118 // Checks properties
119 if (helper->checkProperties()) {
120 // Reads results
121 // AMDA V2: remove line
122 stream.seek(0); // returns to the beginning of the file
123 readResults(*helper, stream);
235 124
236 // Invalid cases
237 qCCritical(LOG_AmdaResultParser())
238 << QObject::tr("Can't create data series: unsupported value type");
239 return nullptr;
125 // Creates data series
126 return helper->createSeries();
127 }
128 else {
129 return nullptr;
130 }
240 131 }
General Comments 0
You need to be logged in to leave comments. Login now