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