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