##// END OF EJS Templates
Parser refactoring (4)...
Alexandre Leroux -
r936:c24241b06d43
parent child
Show More
@@ -1,76 +1,86
1 #ifndef SCIQLOP_AMDARESULTPARSERHELPER_H
1 #ifndef SCIQLOP_AMDARESULTPARSERHELPER_H
2 #define SCIQLOP_AMDARESULTPARSERHELPER_H
2 #define SCIQLOP_AMDARESULTPARSERHELPER_H
3
3
4 #include "AmdaResultParserDefs.h"
4 #include "AmdaResultParserDefs.h"
5
5
6 #include <QtCore/QLoggingCategory>
6 #include <QtCore/QLoggingCategory>
7 #include <QtCore/QString>
7 #include <QtCore/QString>
8
8
9 #include <memory>
9 #include <memory>
10
10
11 class IDataSeries;
11 class IDataSeries;
12
12
13 Q_DECLARE_LOGGING_CATEGORY(LOG_AmdaResultParserHelper)
13 Q_DECLARE_LOGGING_CATEGORY(LOG_AmdaResultParserHelper)
14
14
15 /**
15 /**
16 * Helper used to interpret the data of an AMDA result file and generate the corresponding data
16 * Helper used to interpret the data of an AMDA result file and generate the corresponding data
17 * series.
17 * series.
18 *
18 *
19 * It proposes methods allowing to read line by line an AMDA file and to extract the properties
19 * It proposes methods allowing to read line by line an AMDA file and to extract the properties
20 * (from the header) and the values corresponding to the data series
20 * (from the header) and the values corresponding to the data series
21 *
21 *
22 * @sa DataSeries
22 * @sa DataSeries
23 */
23 */
24 struct IAmdaResultParserHelper {
24 struct IAmdaResultParserHelper {
25 virtual ~IAmdaResultParserHelper() noexcept = default;
25 virtual ~IAmdaResultParserHelper() noexcept = default;
26
26
27 /// Verifies that the extracted properties are well formed and possibly applies other treatments
27 /// Verifies that the extracted properties are well formed and possibly applies other treatments
28 /// on them
28 /// on them
29 /// @return true if the properties are well formed, false otherwise
29 /// @return true if the properties are well formed, false otherwise
30 virtual bool checkProperties() = 0;
30 virtual bool checkProperties() = 0;
31
31
32 /// Creates the data series from the properties and values extracted from the AMDA file.
32 /// Creates the data series from the properties and values extracted from the AMDA file.
33 /// @warning as the data are moved in the data series, the helper shouldn't be used after
33 /// @warning as the data are moved in the data series, the helper shouldn't be used after
34 /// calling this method
34 /// calling this method
35 /// @return the data series created
35 /// @return the data series created
36 virtual std::shared_ptr<IDataSeries> createSeries() = 0;
36 virtual std::shared_ptr<IDataSeries> createSeries() = 0;
37
37
38 /// Reads a line from the AMDA file to extract a property that will be used to generate the data
38 /// Reads a line from the AMDA file to extract a property that will be used to generate the data
39 /// series
39 /// series
40 /// @param line tahe line to interpret
40 /// @param line tahe line to interpret
41 virtual void readPropertyLine(const QString &line) = 0;
41 virtual void readPropertyLine(const QString &line) = 0;
42
42
43 /// Reads a line from the AMDA file to extract a value that will be set in the data series
43 /// Reads a line from the AMDA file to extract a value that will be set in the data series
44 /// @param line the line to interpret
44 /// @param line the line to interpret
45 virtual void readResultLine(const QString &line) = 0;
45 virtual void readResultLine(const QString &line) = 0;
46 };
46 };
47
47
48 /**
48 /**
49 * Implementation of @sa IAmdaResultParserHelper for scalars
49 * Implementation of @sa IAmdaResultParserHelper for scalars
50 */
50 */
51 class ScalarParserHelper : public IAmdaResultParserHelper {
51 class ScalarParserHelper : public IAmdaResultParserHelper {
52 public:
52 public:
53 bool checkProperties() override;
53 bool checkProperties() override;
54 std::shared_ptr<IDataSeries> createSeries() override;
54 std::shared_ptr<IDataSeries> createSeries() override;
55 void readPropertyLine(const QString &line) override;
55 void readPropertyLine(const QString &line) override;
56 void readResultLine(const QString &line) override;
56 void readResultLine(const QString &line) override;
57
57
58 private:
58 private:
59 /// @return the reading order of the "value" columns for a result line of the AMDA file
60 std::vector<int> valuesIndexes() const;
61
59 Properties m_Properties{};
62 Properties m_Properties{};
63 std::vector<double> m_XAxisData{};
64 std::vector<double> m_ValuesData{};
60 };
65 };
61
66
62 /**
67 /**
63 * Implementation of @sa IAmdaResultParserHelper for vectors
68 * Implementation of @sa IAmdaResultParserHelper for vectors
64 */
69 */
65 class VectorParserHelper : public IAmdaResultParserHelper {
70 class VectorParserHelper : public IAmdaResultParserHelper {
66 public:
71 public:
67 bool checkProperties() override;
72 bool checkProperties() override;
68 std::shared_ptr<IDataSeries> createSeries() override;
73 std::shared_ptr<IDataSeries> createSeries() override;
69 void readPropertyLine(const QString &line) override;
74 void readPropertyLine(const QString &line) override;
70 void readResultLine(const QString &line) override;
75 void readResultLine(const QString &line) override;
71
76
72 private:
77 private:
78 /// @return the reading order of the "value" columns for a result line of the AMDA file
79 std::vector<int> valuesIndexes() const;
80
73 Properties m_Properties{};
81 Properties m_Properties{};
82 std::vector<double> m_XAxisData{};
83 std::vector<double> m_ValuesData{};
74 };
84 };
75
85
76 #endif // SCIQLOP_AMDARESULTPARSERHELPER_H
86 #endif // SCIQLOP_AMDARESULTPARSERHELPER_H
@@ -1,120 +1,236
1 #include "AmdaResultParserHelper.h"
1 #include "AmdaResultParserHelper.h"
2
2
3 #include <Common/DateUtils.h>
4
5 #include <Data/ScalarSeries.h>
3 #include <Data/Unit.h>
6 #include <Data/Unit.h>
7 #include <Data/VectorSeries.h>
8
9 #include <QtCore/QDateTime>
10 #include <QtCore/QRegularExpression>
4
11
5 Q_LOGGING_CATEGORY(LOG_AmdaResultParserHelper, "AmdaResultParserHelper")
12 Q_LOGGING_CATEGORY(LOG_AmdaResultParserHelper, "AmdaResultParserHelper")
6
13
7 namespace {
14 namespace {
8
15
16 // ///////// //
17 // Constants //
18 // ///////// //
19
20 /// Separator between values in a result line
21 const auto RESULT_LINE_SEPARATOR = QRegularExpression{QStringLiteral("\\s+")};
22
23 /// Format for dates in result files
24 const auto DATE_FORMAT = QStringLiteral("yyyy-MM-ddThh:mm:ss.zzz");
25
9 // /////// //
26 // /////// //
10 // Methods //
27 // Methods //
11 // /////// //
28 // /////// //
12
29
13 /**
30 /**
14 * Checks that the properties contain a specific unit and that this unit is valid
31 * Checks that the properties contain a specific unit and that this unit is valid
15 * @param properties the properties map in which to search unit
32 * @param properties the properties map in which to search unit
16 * @param key the key to search for the unit in the properties
33 * @param key the key to search for the unit in the properties
17 * @param errorMessage the error message to log in case the unit is invalid
34 * @param errorMessage the error message to log in case the unit is invalid
18 * @return true if the unit is valid, false it it's invalid or was not found in the properties
35 * @return true if the unit is valid, false it it's invalid or was not found in the properties
19 */
36 */
20 bool checkUnit(const Properties &properties, const QString &key, const QString &errorMessage)
37 bool checkUnit(const Properties &properties, const QString &key, const QString &errorMessage)
21 {
38 {
22 auto unit = properties.value(key).value<Unit>();
39 auto unit = properties.value(key).value<Unit>();
23 if (unit.m_Name.isEmpty()) {
40 if (unit.m_Name.isEmpty()) {
24 qCWarning(LOG_AmdaResultParserHelper()) << errorMessage;
41 qCWarning(LOG_AmdaResultParserHelper()) << errorMessage;
25 return false;
42 return false;
26 }
43 }
27
44
28 return true;
45 return true;
29 }
46 }
30
47
48 QDateTime dateTimeFromString(const QString &stringDate) noexcept
49 {
50 #if QT_VERSION >= QT_VERSION_CHECK(5, 8, 0)
51 return QDateTime::fromString(stringDate, Qt::ISODateWithMs);
52 #else
53 return QDateTime::fromString(stringDate, DATE_FORMAT);
54 #endif
55 }
56
57 /// Converts a string date to a double date
58 /// @return a double that represents the date in seconds, NaN if the string date can't be converted
59 double doubleDate(const QString &stringDate) noexcept
60 {
61 // Format: yyyy-MM-ddThh:mm:ss.zzz
62 auto dateTime = dateTimeFromString(stringDate);
63 dateTime.setTimeSpec(Qt::UTC);
64 return dateTime.isValid() ? DateUtils::secondsSinceEpoch(dateTime)
65 : std::numeric_limits<double>::quiet_NaN();
66 }
67
68 /**
69 * Reads a line from the AMDA file and tries to extract a x-axis data and value data from it
70 * @param xAxisData the vector in which to store the x-axis data extracted
71 * @param valuesData the vector in which to store the value extracted
72 * @param line the line to read to extract the property
73 * @param valuesIndexes indexes of insertion of read values. For example, if the line contains three
74 * columns of values, and valuesIndexes are {2, 0, 1}, the value of the third column will be read
75 * and inserted first, then the value of the first column, and finally the value of the second
76 * column.
77 * @param fillValue value that tags an invalid data. For example, if fillValue is -1 and a read
78 * value is -1, then this value is considered as invalid and converted to NaN
79 */
80 void tryReadResult(std::vector<double> &xAxisData, std::vector<double> &valuesData,
81 const QString &line, const std::vector<int> &valuesIndexes,
82 double fillValue = std::numeric_limits<double>::quiet_NaN())
83 {
84 auto lineData = line.split(RESULT_LINE_SEPARATOR, QString::SkipEmptyParts);
85
86 // Checks that the line contains expected number of values + x-axis value
87 if (lineData.size() == valuesIndexes.size() + 1) {
88 // X : the data is converted from date to double (in secs)
89 auto x = doubleDate(lineData.at(0));
90
91 // Adds result only if x is valid. Then, if value is invalid, it is set to NaN
92 if (!std::isnan(x)) {
93 xAxisData.push_back(x);
94
95 // Values
96 for (auto valueIndex : valuesIndexes) {
97 bool valueOk;
98 // we use valueIndex + 1 to skip column 0 (x-axis value)
99 auto value = lineData.at(valueIndex + 1).toDouble(&valueOk);
100
101 if (!valueOk) {
102 qCWarning(LOG_AmdaResultParserHelper())
103 << QObject::tr(
104 "Value from (line %1, column %2) is invalid and will be "
105 "converted to NaN")
106 .arg(line, valueIndex);
107 value = std::numeric_limits<double>::quiet_NaN();
108 }
109
110 // Handles fill value
111 if (!std::isnan(fillValue) && !std::isnan(value) && fillValue == value) {
112 value = std::numeric_limits<double>::quiet_NaN();
113 }
114
115 valuesData.push_back(value);
116 }
117 }
118 else {
119 qCWarning(LOG_AmdaResultParserHelper())
120 << QObject::tr("Can't retrieve results from line %1: x is invalid").arg(line);
121 }
122 }
123 else {
124 qCWarning(LOG_AmdaResultParserHelper())
125 << QObject::tr("Can't retrieve results from line %1: invalid line").arg(line);
126 }
127 }
128
31 /**
129 /**
32 * Reads a line from the AMDA file and tries to extract a property from it
130 * Reads a line from the AMDA file and tries to extract a property from it
33 * @param properties the properties map in which to put the property extracted from the line
131 * @param properties the properties map in which to put the property extracted from the line
34 * @param key the key to which the property is added in the properties map
132 * @param key the key to which the property is added in the properties map
35 * @param line the line to read to extract the property
133 * @param line the line to read to extract the property
36 * @param regex the expected regex to extract the property. If the line matches this regex, the
134 * @param regex the expected regex to extract the property. If the line matches this regex, the
37 * property is generated
135 * property is generated
38 * @param fun the function used to generate the property
136 * @param fun the function used to generate the property
39 * @return true if the property could be generated, false if the line does not match the regex, or
137 * @return true if the property could be generated, false if the line does not match the regex, or
40 * if a property has already been generated for the key
138 * if a property has already been generated for the key
41 */
139 */
42 template <typename GeneratePropertyFun>
140 template <typename GeneratePropertyFun>
43 bool tryReadProperty(Properties &properties, const QString &key, const QString &line,
141 bool tryReadProperty(Properties &properties, const QString &key, const QString &line,
44 const QRegularExpression &regex, GeneratePropertyFun fun)
142 const QRegularExpression &regex, GeneratePropertyFun fun)
45 {
143 {
46 if (properties.contains(key)) {
144 if (properties.contains(key)) {
47 return false;
145 return false;
48 }
146 }
49
147
50 auto match = regex.match(line);
148 auto match = regex.match(line);
51 if (match.hasMatch()) {
149 if (match.hasMatch()) {
52 properties.insert(key, fun(match));
150 properties.insert(key, fun(match));
53 }
151 }
54
152
55 return match.hasMatch();
153 return match.hasMatch();
56 }
154 }
57
155
58 /**
156 /**
59 * Reads a line from the AMDA file and tries to extract a unit from it
157 * Reads a line from the AMDA file and tries to extract a unit from it
60 * @sa tryReadProperty()
158 * @sa tryReadProperty()
61 */
159 */
62 bool tryReadUnit(Properties &properties, const QString &key, const QString &line,
160 bool tryReadUnit(Properties &properties, const QString &key, const QString &line,
63 const QRegularExpression &regex, bool timeUnit = false)
161 const QRegularExpression &regex, bool timeUnit = false)
64 {
162 {
65 return tryReadProperty(properties, key, line, regex, [timeUnit](const auto &match) {
163 return tryReadProperty(properties, key, line, regex, [timeUnit](const auto &match) {
66 return QVariant::fromValue(Unit{match.captured(1), timeUnit});
164 return QVariant::fromValue(Unit{match.captured(1), timeUnit});
67 });
165 });
68 }
166 }
69
167
70 } // namespace
168 } // namespace
71
169
72 // ////////////////// //
170 // ////////////////// //
73 // ScalarParserHelper //
171 // ScalarParserHelper //
74 // ////////////////// //
172 // ////////////////// //
75
173
76 bool ScalarParserHelper::checkProperties()
174 bool ScalarParserHelper::checkProperties()
77 {
175 {
78 return checkUnit(m_Properties, X_AXIS_UNIT_PROPERTY,
176 return checkUnit(m_Properties, X_AXIS_UNIT_PROPERTY,
79 QObject::tr("The x-axis unit could not be found in the file"));
177 QObject::tr("The x-axis unit could not be found in the file"));
80 }
178 }
81
179
82 std::shared_ptr<IDataSeries> ScalarParserHelper::createSeries()
180 std::shared_ptr<IDataSeries> ScalarParserHelper::createSeries()
83 {
181 {
84 /// @todo ALX
182 return std::make_shared<ScalarSeries>(std::move(m_XAxisData), std::move(m_ValuesData),
183 m_Properties.value(X_AXIS_UNIT_PROPERTY).value<Unit>(),
184 m_Properties.value(VALUES_UNIT_PROPERTY).value<Unit>());
85 }
185 }
86
186
87 void ScalarParserHelper::readPropertyLine(const QString &line)
187 void ScalarParserHelper::readPropertyLine(const QString &line)
88 {
188 {
89 tryReadUnit(m_Properties, X_AXIS_UNIT_PROPERTY, line, DEFAULT_X_AXIS_UNIT_REGEX, true);
189 tryReadUnit(m_Properties, X_AXIS_UNIT_PROPERTY, line, DEFAULT_X_AXIS_UNIT_REGEX, true);
90 }
190 }
91
191
92 void ScalarParserHelper::readResultLine(const QString &line)
192 void ScalarParserHelper::readResultLine(const QString &line)
93 {
193 {
94 /// @todo ALX
194 tryReadResult(m_XAxisData, m_ValuesData, line, valuesIndexes());
195 }
196
197 std::vector<int> ScalarParserHelper::valuesIndexes() const
198 {
199 // Only one value to read
200 static auto result = std::vector<int>{0};
201 return result;
95 }
202 }
96
203
97 // ////////////////// //
204 // ////////////////// //
98 // VectorParserHelper //
205 // VectorParserHelper //
99 // ////////////////// //
206 // ////////////////// //
100
207
101 bool VectorParserHelper::checkProperties()
208 bool VectorParserHelper::checkProperties()
102 {
209 {
103 return checkUnit(m_Properties, X_AXIS_UNIT_PROPERTY,
210 return checkUnit(m_Properties, X_AXIS_UNIT_PROPERTY,
104 QObject::tr("The x-axis unit could not be found in the file"));
211 QObject::tr("The x-axis unit could not be found in the file"));
105 }
212 }
106
213
107 std::shared_ptr<IDataSeries> VectorParserHelper::createSeries()
214 std::shared_ptr<IDataSeries> VectorParserHelper::createSeries()
108 {
215 {
109 /// @todo ALX
216 return std::make_shared<VectorSeries>(std::move(m_XAxisData), std::move(m_ValuesData),
217 m_Properties.value(X_AXIS_UNIT_PROPERTY).value<Unit>(),
218 m_Properties.value(VALUES_UNIT_PROPERTY).value<Unit>());
110 }
219 }
111
220
112 void VectorParserHelper::readPropertyLine(const QString &line)
221 void VectorParserHelper::readPropertyLine(const QString &line)
113 {
222 {
114 tryReadUnit(m_Properties, X_AXIS_UNIT_PROPERTY, line, DEFAULT_X_AXIS_UNIT_REGEX, true);
223 tryReadUnit(m_Properties, X_AXIS_UNIT_PROPERTY, line, DEFAULT_X_AXIS_UNIT_REGEX, true);
115 }
224 }
116
225
117 void VectorParserHelper::readResultLine(const QString &line)
226 void VectorParserHelper::readResultLine(const QString &line)
118 {
227 {
119 /// @todo ALX
228 tryReadResult(m_XAxisData, m_ValuesData, line, valuesIndexes());
229 }
230
231 std::vector<int> VectorParserHelper::valuesIndexes() const
232 {
233 // 3 values to read, in order in the file (x, y, z)
234 static auto result = std::vector<int>{0, 1, 2};
235 return result;
120 }
236 }
General Comments 0
You need to be logged in to leave comments. Login now