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