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