|
@@
-1,398
+1,401
|
|
1
|
#include "AmdaResultParserHelper.h"
|
|
1
|
#include "AmdaResultParserHelper.h"
|
|
2
|
|
|
2
|
|
|
3
|
#include <Common/DateUtils.h>
|
|
3
|
#include <Common/DateUtils.h>
|
|
4
|
#include <Common/SortUtils.h>
|
|
4
|
#include <Common/SortUtils.h>
|
|
5
|
|
|
5
|
|
|
6
|
#include <Data/DataSeriesUtils.h>
|
|
6
|
#include <Data/DataSeriesUtils.h>
|
|
7
|
#include <Data/ScalarSeries.h>
|
|
7
|
#include <Data/ScalarSeries.h>
|
|
8
|
#include <Data/SpectrogramSeries.h>
|
|
8
|
#include <Data/SpectrogramSeries.h>
|
|
9
|
#include <Data/Unit.h>
|
|
9
|
#include <Data/Unit.h>
|
|
10
|
#include <Data/VectorSeries.h>
|
|
10
|
#include <Data/VectorSeries.h>
|
|
11
|
|
|
11
|
|
|
12
|
#include <QtCore/QDateTime>
|
|
12
|
#include <QtCore/QDateTime>
|
|
13
|
#include <QtCore/QRegularExpression>
|
|
13
|
#include <QtCore/QRegularExpression>
|
|
14
|
|
|
14
|
|
|
15
|
#include <functional>
|
|
15
|
#include <functional>
|
|
16
|
|
|
16
|
|
|
17
|
Q_LOGGING_CATEGORY(LOG_AmdaResultParserHelper, "AmdaResultParserHelper")
|
|
17
|
Q_LOGGING_CATEGORY(LOG_AmdaResultParserHelper, "AmdaResultParserHelper")
|
|
18
|
|
|
18
|
|
|
19
|
namespace {
|
|
19
|
namespace {
|
|
20
|
|
|
20
|
|
|
21
|
// ///////// //
|
|
21
|
// ///////// //
|
|
22
|
// Constants //
|
|
22
|
// Constants //
|
|
23
|
// ///////// //
|
|
23
|
// ///////// //
|
|
24
|
|
|
24
|
|
|
25
|
/// Separator between values in a result line
|
|
25
|
/// Separator between values in a result line
|
|
26
|
const auto RESULT_LINE_SEPARATOR = QRegularExpression{QStringLiteral("\\s+")};
|
|
26
|
const auto RESULT_LINE_SEPARATOR = QRegularExpression{QStringLiteral("\\s+")};
|
|
27
|
|
|
27
|
|
|
28
|
/// Format for dates in result files
|
|
28
|
/// Format for dates in result files
|
|
29
|
const auto DATE_FORMAT = QStringLiteral("yyyy-MM-ddThh:mm:ss.zzz");
|
|
29
|
const auto DATE_FORMAT = QStringLiteral("yyyy-MM-ddThh:mm:ss.zzz");
|
|
30
|
|
|
30
|
|
|
31
|
// /////// //
|
|
31
|
// /////// //
|
|
32
|
// Methods //
|
|
32
|
// Methods //
|
|
33
|
// /////// //
|
|
33
|
// /////// //
|
|
34
|
|
|
34
|
|
|
35
|
/**
|
|
35
|
/**
|
|
36
|
* Checks that the properties contain a specific unit and that this unit is valid
|
|
36
|
* Checks that the properties contain a specific unit and that this unit is valid
|
|
37
|
* @param properties the properties map in which to search unit
|
|
37
|
* @param properties the properties map in which to search unit
|
|
38
|
* @param key the key to search for the unit in the properties
|
|
38
|
* @param key the key to search for the unit in the properties
|
|
39
|
* @param errorMessage the error message to log in case the unit is invalid
|
|
39
|
* @param errorMessage the error message to log in case the unit is invalid
|
|
40
|
* @return true if the unit is valid, false it it's invalid or was not found in the properties
|
|
40
|
* @return true if the unit is valid, false it it's invalid or was not found in the properties
|
|
41
|
*/
|
|
41
|
*/
|
|
42
|
bool checkUnit(const Properties &properties, const QString &key, const QString &errorMessage)
|
|
42
|
bool checkUnit(const Properties &properties, const QString &key, const QString &errorMessage)
|
|
43
|
{
|
|
43
|
{
|
|
44
|
auto unit = properties.value(key).value<Unit>();
|
|
44
|
auto unit = properties.value(key).value<Unit>();
|
|
45
|
if (unit.m_Name.isEmpty()) {
|
|
45
|
if (unit.m_Name.isEmpty()) {
|
|
46
|
qCWarning(LOG_AmdaResultParserHelper()) << errorMessage;
|
|
46
|
qCWarning(LOG_AmdaResultParserHelper()) << errorMessage;
|
|
47
|
return false;
|
|
47
|
return false;
|
|
48
|
}
|
|
48
|
}
|
|
49
|
|
|
49
|
|
|
50
|
return true;
|
|
50
|
return true;
|
|
51
|
}
|
|
51
|
}
|
|
52
|
|
|
52
|
|
|
53
|
QDateTime dateTimeFromString(const QString &stringDate) noexcept
|
|
53
|
QDateTime dateTimeFromString(const QString &stringDate) noexcept
|
|
54
|
{
|
|
54
|
{
|
|
55
|
#if QT_VERSION >= QT_VERSION_CHECK(5, 8, 0)
|
|
55
|
#if QT_VERSION >= QT_VERSION_CHECK(5, 8, 0)
|
|
56
|
return QDateTime::fromString(stringDate, Qt::ISODateWithMs);
|
|
56
|
return QDateTime::fromString(stringDate, Qt::ISODateWithMs);
|
|
57
|
#else
|
|
57
|
#else
|
|
58
|
return QDateTime::fromString(stringDate, DATE_FORMAT);
|
|
58
|
return QDateTime::fromString(stringDate, DATE_FORMAT);
|
|
59
|
#endif
|
|
59
|
#endif
|
|
60
|
}
|
|
60
|
}
|
|
61
|
|
|
61
|
|
|
62
|
/// Converts a string date to a double date
|
|
62
|
/// Converts a string date to a double date
|
|
63
|
/// @return a double that represents the date in seconds, NaN if the string date can't be converted
|
|
63
|
/// @return a double that represents the date in seconds, NaN if the string date can't be converted
|
|
64
|
double doubleDate(const QString &stringDate) noexcept
|
|
64
|
double doubleDate(const QString &stringDate) noexcept
|
|
65
|
{
|
|
65
|
{
|
|
66
|
// Format: yyyy-MM-ddThh:mm:ss.zzz
|
|
66
|
// Format: yyyy-MM-ddThh:mm:ss.zzz
|
|
67
|
auto dateTime = dateTimeFromString(stringDate);
|
|
67
|
auto dateTime = dateTimeFromString(stringDate);
|
|
68
|
dateTime.setTimeSpec(Qt::UTC);
|
|
68
|
dateTime.setTimeSpec(Qt::UTC);
|
|
69
|
return dateTime.isValid() ? DateUtils::secondsSinceEpoch(dateTime)
|
|
69
|
return dateTime.isValid() ? DateUtils::secondsSinceEpoch(dateTime)
|
|
70
|
: std::numeric_limits<double>::quiet_NaN();
|
|
70
|
: std::numeric_limits<double>::quiet_NaN();
|
|
71
|
}
|
|
71
|
}
|
|
72
|
|
|
72
|
|
|
73
|
/**
|
|
73
|
/**
|
|
74
|
* Reads a line from the AMDA file and tries to extract a x-axis data and value data from it
|
|
74
|
* Reads a line from the AMDA file and tries to extract a x-axis data and value data from it
|
|
75
|
* @param xAxisData the vector in which to store the x-axis data extracted
|
|
75
|
* @param xAxisData the vector in which to store the x-axis data extracted
|
|
76
|
* @param valuesData the vector in which to store the value extracted
|
|
76
|
* @param valuesData the vector in which to store the value extracted
|
|
77
|
* @param line the line to read to extract the property
|
|
77
|
* @param line the line to read to extract the property
|
|
78
|
* @param valuesIndexes indexes of insertion of read values. For example, if the line contains three
|
|
78
|
* @param valuesIndexes indexes of insertion of read values. For example, if the line contains three
|
|
79
|
* columns of values, and valuesIndexes are {2, 0, 1}, the value of the third column will be read
|
|
79
|
* columns of values, and valuesIndexes are {2, 0, 1}, the value of the third column will be read
|
|
80
|
* and inserted first, then the value of the first column, and finally the value of the second
|
|
80
|
* and inserted first, then the value of the first column, and finally the value of the second
|
|
81
|
* column.
|
|
81
|
* column.
|
|
82
|
* @param fillValue value that tags an invalid data. For example, if fillValue is -1 and a read
|
|
82
|
* @param fillValue value that tags an invalid data. For example, if fillValue is -1 and a read
|
|
83
|
* value is -1, then this value is considered as invalid and converted to NaN
|
|
83
|
* value is -1, then this value is considered as invalid and converted to NaN
|
|
84
|
*/
|
|
84
|
*/
|
|
85
|
void tryReadResult(std::vector<double> &xAxisData, std::vector<double> &valuesData,
|
|
85
|
void tryReadResult(std::vector<double> &xAxisData, std::vector<double> &valuesData,
|
|
86
|
const QString &line, const std::vector<int> &valuesIndexes,
|
|
86
|
const QString &line, const std::vector<int> &valuesIndexes,
|
|
87
|
double fillValue = std::numeric_limits<double>::quiet_NaN())
|
|
87
|
double fillValue = std::numeric_limits<double>::quiet_NaN())
|
|
88
|
{
|
|
88
|
{
|
|
89
|
auto lineData = line.split(RESULT_LINE_SEPARATOR, QString::SkipEmptyParts);
|
|
89
|
auto lineData = line.split(RESULT_LINE_SEPARATOR, QString::SkipEmptyParts);
|
|
90
|
|
|
90
|
|
|
91
|
// Checks that the line contains expected number of values + x-axis value
|
|
91
|
// Checks that the line contains expected number of values + x-axis value
|
|
92
|
if (lineData.size() == valuesIndexes.size() + 1) {
|
|
92
|
if (lineData.size() == valuesIndexes.size() + 1) {
|
|
93
|
// X : the data is converted from date to double (in secs)
|
|
93
|
// X : the data is converted from date to double (in secs)
|
|
94
|
auto x = doubleDate(lineData.at(0));
|
|
94
|
auto x = doubleDate(lineData.at(0));
|
|
95
|
|
|
95
|
|
|
96
|
// Adds result only if x is valid. Then, if value is invalid, it is set to NaN
|
|
96
|
// Adds result only if x is valid. Then, if value is invalid, it is set to NaN
|
|
97
|
if (!std::isnan(x)) {
|
|
97
|
if (!std::isnan(x)) {
|
|
98
|
xAxisData.push_back(x);
|
|
98
|
xAxisData.push_back(x);
|
|
99
|
|
|
99
|
|
|
100
|
// Values
|
|
100
|
// Values
|
|
101
|
for (auto valueIndex : valuesIndexes) {
|
|
101
|
for (auto valueIndex : valuesIndexes) {
|
|
102
|
bool valueOk;
|
|
102
|
bool valueOk;
|
|
103
|
// we use valueIndex + 1 to skip column 0 (x-axis value)
|
|
103
|
// we use valueIndex + 1 to skip column 0 (x-axis value)
|
|
104
|
auto value = lineData.at(valueIndex + 1).toDouble(&valueOk);
|
|
104
|
auto value = lineData.at(valueIndex + 1).toDouble(&valueOk);
|
|
105
|
|
|
105
|
|
|
106
|
if (!valueOk) {
|
|
106
|
if (!valueOk) {
|
|
107
|
qCWarning(LOG_AmdaResultParserHelper())
|
|
107
|
qCWarning(LOG_AmdaResultParserHelper())
|
|
108
|
<< QObject::tr(
|
|
108
|
<< QObject::tr(
|
|
109
|
"Value from (line %1, column %2) is invalid and will be "
|
|
109
|
"Value from (line %1, column %2) is invalid and will be "
|
|
110
|
"converted to NaN")
|
|
110
|
"converted to NaN")
|
|
111
|
.arg(line, valueIndex);
|
|
111
|
.arg(line, valueIndex);
|
|
112
|
value = std::numeric_limits<double>::quiet_NaN();
|
|
112
|
value = std::numeric_limits<double>::quiet_NaN();
|
|
113
|
}
|
|
113
|
}
|
|
114
|
|
|
114
|
|
|
115
|
// Handles fill value
|
|
115
|
// Handles fill value
|
|
116
|
if (!std::isnan(fillValue) && !std::isnan(value) && fillValue == value) {
|
|
116
|
if (!std::isnan(fillValue) && !std::isnan(value) && fillValue == value) {
|
|
117
|
value = std::numeric_limits<double>::quiet_NaN();
|
|
117
|
value = std::numeric_limits<double>::quiet_NaN();
|
|
118
|
}
|
|
118
|
}
|
|
119
|
|
|
119
|
|
|
120
|
valuesData.push_back(value);
|
|
120
|
valuesData.push_back(value);
|
|
121
|
}
|
|
121
|
}
|
|
122
|
}
|
|
122
|
}
|
|
123
|
else {
|
|
123
|
else {
|
|
124
|
qCWarning(LOG_AmdaResultParserHelper())
|
|
124
|
qCWarning(LOG_AmdaResultParserHelper())
|
|
125
|
<< QObject::tr("Can't retrieve results from line %1: x is invalid").arg(line);
|
|
125
|
<< QObject::tr("Can't retrieve results from line %1: x is invalid").arg(line);
|
|
126
|
}
|
|
126
|
}
|
|
127
|
}
|
|
127
|
}
|
|
128
|
else {
|
|
128
|
else {
|
|
129
|
qCWarning(LOG_AmdaResultParserHelper())
|
|
129
|
qCWarning(LOG_AmdaResultParserHelper())
|
|
130
|
<< QObject::tr("Can't retrieve results from line %1: invalid line").arg(line);
|
|
130
|
<< QObject::tr("Can't retrieve results from line %1: invalid line").arg(line);
|
|
131
|
}
|
|
131
|
}
|
|
132
|
}
|
|
132
|
}
|
|
133
|
|
|
133
|
|
|
134
|
/**
|
|
134
|
/**
|
|
135
|
* Reads a line from the AMDA file and tries to extract a property from it
|
|
135
|
* Reads a line from the AMDA file and tries to extract a property from it
|
|
136
|
* @param properties the properties map in which to put the property extracted from the line
|
|
136
|
* @param properties the properties map in which to put the property extracted from the line
|
|
137
|
* @param key the key to which the property is added in the properties map
|
|
137
|
* @param key the key to which the property is added in the properties map
|
|
138
|
* @param line the line to read to extract the property
|
|
138
|
* @param line the line to read to extract the property
|
|
139
|
* @param regex the expected regex to extract the property. If the line matches this regex, the
|
|
139
|
* @param regex the expected regex to extract the property. If the line matches this regex, the
|
|
140
|
* property is generated
|
|
140
|
* property is generated
|
|
141
|
* @param fun the function used to generate the property
|
|
141
|
* @param fun the function used to generate the property
|
|
142
|
* @return true if the property could be generated, false if the line does not match the regex, or
|
|
142
|
* @return true if the property could be generated, false if the line does not match the regex, or
|
|
143
|
* if a property has already been generated for the key
|
|
143
|
* if a property has already been generated for the key
|
|
144
|
*/
|
|
144
|
*/
|
|
145
|
template <typename GeneratePropertyFun>
|
|
145
|
template <typename GeneratePropertyFun>
|
|
146
|
bool tryReadProperty(Properties &properties, const QString &key, const QString &line,
|
|
146
|
bool tryReadProperty(Properties &properties, const QString &key, const QString &line,
|
|
147
|
const QRegularExpression ®ex, GeneratePropertyFun fun)
|
|
147
|
const QRegularExpression ®ex, GeneratePropertyFun fun)
|
|
148
|
{
|
|
148
|
{
|
|
149
|
if (properties.contains(key)) {
|
|
149
|
if (properties.contains(key)) {
|
|
150
|
return false;
|
|
150
|
return false;
|
|
151
|
}
|
|
151
|
}
|
|
152
|
|
|
152
|
|
|
153
|
auto match = regex.match(line);
|
|
153
|
auto match = regex.match(line);
|
|
154
|
if (match.hasMatch()) {
|
|
154
|
if (match.hasMatch()) {
|
|
155
|
properties.insert(key, fun(match));
|
|
155
|
properties.insert(key, fun(match));
|
|
156
|
}
|
|
156
|
}
|
|
157
|
|
|
157
|
|
|
158
|
return match.hasMatch();
|
|
158
|
return match.hasMatch();
|
|
159
|
}
|
|
159
|
}
|
|
160
|
|
|
160
|
|
|
161
|
/**
|
|
161
|
/**
|
|
162
|
* Reads a line from the AMDA file and tries to extract a double from it
|
|
162
|
* Reads a line from the AMDA file and tries to extract a double from it
|
|
163
|
* @sa tryReadProperty()
|
|
163
|
* @sa tryReadProperty()
|
|
164
|
*/
|
|
164
|
*/
|
|
165
|
bool tryReadDouble(Properties &properties, const QString &key, const QString &line,
|
|
165
|
bool tryReadDouble(Properties &properties, const QString &key, const QString &line,
|
|
166
|
const QRegularExpression ®ex)
|
|
166
|
const QRegularExpression ®ex)
|
|
167
|
{
|
|
167
|
{
|
|
168
|
return tryReadProperty(properties, key, line, regex, [](const auto &match) {
|
|
168
|
return tryReadProperty(properties, key, line, regex, [](const auto &match) {
|
|
169
|
bool ok;
|
|
169
|
bool ok;
|
|
170
|
|
|
170
|
|
|
171
|
// If the value can't be converted to double, it is set to NaN
|
|
171
|
// If the value can't be converted to double, it is set to NaN
|
|
172
|
auto doubleValue = match.captured(1).toDouble(&ok);
|
|
172
|
auto doubleValue = match.captured(1).toDouble(&ok);
|
|
173
|
if (!ok) {
|
|
173
|
if (!ok) {
|
|
174
|
doubleValue = std::numeric_limits<double>::quiet_NaN();
|
|
174
|
doubleValue = std::numeric_limits<double>::quiet_NaN();
|
|
175
|
}
|
|
175
|
}
|
|
176
|
|
|
176
|
|
|
177
|
return QVariant::fromValue(doubleValue);
|
|
177
|
return QVariant::fromValue(doubleValue);
|
|
178
|
});
|
|
178
|
});
|
|
179
|
}
|
|
179
|
}
|
|
180
|
|
|
180
|
|
|
181
|
/**
|
|
181
|
/**
|
|
182
|
* Reads a line from the AMDA file and tries to extract a vector of doubles from it
|
|
182
|
* Reads a line from the AMDA file and tries to extract a vector of doubles from it
|
|
183
|
* @param sep the separator of double values in the line
|
|
183
|
* @param sep the separator of double values in the line
|
|
184
|
* @sa tryReadProperty()
|
|
184
|
* @sa tryReadProperty()
|
|
185
|
*/
|
|
185
|
*/
|
|
186
|
bool tryReadDoubles(Properties &properties, const QString &key, const QString &line,
|
|
186
|
bool tryReadDoubles(Properties &properties, const QString &key, const QString &line,
|
|
187
|
const QRegularExpression ®ex, const QString &sep = QStringLiteral(","))
|
|
187
|
const QRegularExpression ®ex, const QString &sep = QStringLiteral(","))
|
|
188
|
{
|
|
188
|
{
|
|
189
|
return tryReadProperty(properties, key, line, regex, [sep](const auto &match) {
|
|
189
|
return tryReadProperty(properties, key, line, regex, [sep](const auto &match) {
|
|
190
|
std::vector<double> doubleValues{};
|
|
190
|
std::vector<double> doubleValues{};
|
|
191
|
|
|
191
|
|
|
192
|
// If the value can't be converted to double, it is set to NaN
|
|
192
|
// If the value can't be converted to double, it is set to NaN
|
|
193
|
auto values = match.captured(1).split(sep);
|
|
193
|
auto values = match.captured(1).split(sep);
|
|
194
|
for (auto value : values) {
|
|
194
|
for (auto value : values) {
|
|
195
|
bool ok;
|
|
195
|
bool ok;
|
|
196
|
|
|
196
|
|
|
197
|
auto doubleValue = value.toDouble(&ok);
|
|
197
|
auto doubleValue = value.toDouble(&ok);
|
|
198
|
if (!ok) {
|
|
198
|
if (!ok) {
|
|
199
|
doubleValue = std::numeric_limits<double>::quiet_NaN();
|
|
199
|
doubleValue = std::numeric_limits<double>::quiet_NaN();
|
|
200
|
}
|
|
200
|
}
|
|
201
|
|
|
201
|
|
|
202
|
doubleValues.push_back(doubleValue);
|
|
202
|
doubleValues.push_back(doubleValue);
|
|
203
|
}
|
|
203
|
}
|
|
204
|
|
|
204
|
|
|
205
|
return QVariant::fromValue(doubleValues);
|
|
205
|
return QVariant::fromValue(doubleValues);
|
|
206
|
});
|
|
206
|
});
|
|
207
|
}
|
|
207
|
}
|
|
208
|
|
|
208
|
|
|
209
|
/**
|
|
209
|
/**
|
|
210
|
* Reads a line from the AMDA file and tries to extract a unit from it
|
|
210
|
* Reads a line from the AMDA file and tries to extract a unit from it
|
|
211
|
* @sa tryReadProperty()
|
|
211
|
* @sa tryReadProperty()
|
|
212
|
*/
|
|
212
|
*/
|
|
213
|
bool tryReadUnit(Properties &properties, const QString &key, const QString &line,
|
|
213
|
bool tryReadUnit(Properties &properties, const QString &key, const QString &line,
|
|
214
|
const QRegularExpression ®ex, bool timeUnit = false)
|
|
214
|
const QRegularExpression ®ex, bool timeUnit = false)
|
|
215
|
{
|
|
215
|
{
|
|
216
|
return tryReadProperty(properties, key, line, regex, [timeUnit](const auto &match) {
|
|
216
|
return tryReadProperty(properties, key, line, regex, [timeUnit](const auto &match) {
|
|
217
|
return QVariant::fromValue(Unit{match.captured(1), timeUnit});
|
|
217
|
return QVariant::fromValue(Unit{match.captured(1), timeUnit});
|
|
218
|
});
|
|
218
|
});
|
|
219
|
}
|
|
219
|
}
|
|
220
|
|
|
220
|
|
|
221
|
} // namespace
|
|
221
|
} // namespace
|
|
222
|
|
|
222
|
|
|
223
|
// ////////////////// //
|
|
223
|
// ////////////////// //
|
|
224
|
// ScalarParserHelper //
|
|
224
|
// ScalarParserHelper //
|
|
225
|
// ////////////////// //
|
|
225
|
// ////////////////// //
|
|
226
|
|
|
226
|
|
|
227
|
bool ScalarParserHelper::checkProperties()
|
|
227
|
bool ScalarParserHelper::checkProperties()
|
|
228
|
{
|
|
228
|
{
|
|
229
|
return checkUnit(m_Properties, X_AXIS_UNIT_PROPERTY,
|
|
229
|
return checkUnit(m_Properties, X_AXIS_UNIT_PROPERTY,
|
|
230
|
QObject::tr("The x-axis unit could not be found in the file"));
|
|
230
|
QObject::tr("The x-axis unit could not be found in the file"));
|
|
231
|
}
|
|
231
|
}
|
|
232
|
|
|
232
|
|
|
233
|
std::shared_ptr<IDataSeries> ScalarParserHelper::createSeries()
|
|
233
|
std::shared_ptr<IDataSeries> ScalarParserHelper::createSeries()
|
|
234
|
{
|
|
234
|
{
|
|
235
|
return std::make_shared<ScalarSeries>(std::move(m_XAxisData), std::move(m_ValuesData),
|
|
235
|
return std::make_shared<ScalarSeries>(std::move(m_XAxisData), std::move(m_ValuesData),
|
|
236
|
m_Properties.value(X_AXIS_UNIT_PROPERTY).value<Unit>(),
|
|
236
|
m_Properties.value(X_AXIS_UNIT_PROPERTY).value<Unit>(),
|
|
237
|
m_Properties.value(VALUES_UNIT_PROPERTY).value<Unit>());
|
|
237
|
m_Properties.value(VALUES_UNIT_PROPERTY).value<Unit>());
|
|
238
|
}
|
|
238
|
}
|
|
239
|
|
|
239
|
|
|
240
|
void ScalarParserHelper::readPropertyLine(const QString &line)
|
|
240
|
void ScalarParserHelper::readPropertyLine(const QString &line)
|
|
241
|
{
|
|
241
|
{
|
|
242
|
tryReadUnit(m_Properties, X_AXIS_UNIT_PROPERTY, line, DEFAULT_X_AXIS_UNIT_REGEX, true);
|
|
242
|
tryReadUnit(m_Properties, X_AXIS_UNIT_PROPERTY, line, DEFAULT_X_AXIS_UNIT_REGEX, true);
|
|
243
|
}
|
|
243
|
}
|
|
244
|
|
|
244
|
|
|
245
|
void ScalarParserHelper::readResultLine(const QString &line)
|
|
245
|
void ScalarParserHelper::readResultLine(const QString &line)
|
|
246
|
{
|
|
246
|
{
|
|
247
|
tryReadResult(m_XAxisData, m_ValuesData, line, valuesIndexes());
|
|
247
|
tryReadResult(m_XAxisData, m_ValuesData, line, valuesIndexes());
|
|
248
|
}
|
|
248
|
}
|
|
249
|
|
|
249
|
|
|
250
|
std::vector<int> ScalarParserHelper::valuesIndexes() const
|
|
250
|
std::vector<int> ScalarParserHelper::valuesIndexes() const
|
|
251
|
{
|
|
251
|
{
|
|
252
|
// Only one value to read
|
|
252
|
// Only one value to read
|
|
253
|
static auto result = std::vector<int>{0};
|
|
253
|
static auto result = std::vector<int>{0};
|
|
254
|
return result;
|
|
254
|
return result;
|
|
255
|
}
|
|
255
|
}
|
|
256
|
|
|
256
|
|
|
257
|
// /////////////////////// //
|
|
257
|
// /////////////////////// //
|
|
258
|
// SpectrogramParserHelper //
|
|
258
|
// SpectrogramParserHelper //
|
|
259
|
// /////////////////////// //
|
|
259
|
// /////////////////////// //
|
|
260
|
|
|
260
|
|
|
261
|
bool SpectrogramParserHelper::checkProperties()
|
|
261
|
bool SpectrogramParserHelper::checkProperties()
|
|
262
|
{
|
|
262
|
{
|
|
263
|
// Generates y-axis data from bands extracted (take the middle of the intervals)
|
|
263
|
// Generates y-axis data from bands extracted (take the middle of the intervals)
|
|
264
|
auto minBands = m_Properties.value(MIN_BANDS_PROPERTY).value<std::vector<double> >();
|
|
264
|
auto minBands = m_Properties.value(MIN_BANDS_PROPERTY).value<std::vector<double> >();
|
|
265
|
auto maxBands = m_Properties.value(MAX_BANDS_PROPERTY).value<std::vector<double> >();
|
|
265
|
auto maxBands = m_Properties.value(MAX_BANDS_PROPERTY).value<std::vector<double> >();
|
|
266
|
|
|
266
|
|
|
267
|
if (minBands.size() != maxBands.size()) {
|
|
267
|
if (minBands.size() != maxBands.size()) {
|
|
268
|
qCWarning(LOG_AmdaResultParserHelper()) << QObject::tr(
|
|
268
|
qCWarning(LOG_AmdaResultParserHelper()) << QObject::tr(
|
|
269
|
"Can't generate y-axis data from bands extracted: bands intervals are invalid");
|
|
269
|
"Can't generate y-axis data from bands extracted: bands intervals are invalid");
|
|
270
|
return false;
|
|
270
|
return false;
|
|
271
|
}
|
|
271
|
}
|
|
272
|
|
|
272
|
|
|
273
|
std::transform(
|
|
273
|
std::transform(
|
|
274
|
minBands.begin(), minBands.end(), maxBands.begin(), std::back_inserter(m_YAxisData),
|
|
274
|
minBands.begin(), minBands.end(), maxBands.begin(), std::back_inserter(m_YAxisData),
|
|
275
|
[](const auto &minValue, const auto &maxValue) { return (minValue + maxValue) / 2.; });
|
|
275
|
[](const auto &minValue, const auto &maxValue) { return (minValue + maxValue) / 2.; });
|
|
276
|
|
|
276
|
|
|
277
|
// Generates values indexes, i.e. the order in which each value will be retrieved (in ascending
|
|
277
|
// Generates values indexes, i.e. the order in which each value will be retrieved (in ascending
|
|
278
|
// order of the associated bands)
|
|
278
|
// order of the associated bands)
|
|
279
|
m_ValuesIndexes = SortUtils::sortPermutation(m_YAxisData, std::less<double>());
|
|
279
|
m_ValuesIndexes = SortUtils::sortPermutation(m_YAxisData, std::less<double>());
|
|
280
|
|
|
280
|
|
|
281
|
// Sorts y-axis data accoding to the ascending order
|
|
281
|
// Sorts y-axis data accoding to the ascending order
|
|
282
|
m_YAxisData = SortUtils::sort(m_YAxisData, 1, m_ValuesIndexes);
|
|
282
|
m_YAxisData = SortUtils::sort(m_YAxisData, 1, m_ValuesIndexes);
|
|
283
|
|
|
283
|
|
|
284
|
// Sets fill value
|
|
284
|
// Sets fill value
|
|
285
|
m_FillValue = m_Properties.value(FILL_VALUE_PROPERTY).value<double>();
|
|
285
|
m_FillValue = m_Properties.value(FILL_VALUE_PROPERTY).value<double>();
|
|
286
|
|
|
286
|
|
|
287
|
return true;
|
|
287
|
return true;
|
|
288
|
}
|
|
288
|
}
|
|
289
|
|
|
289
|
|
|
290
|
std::shared_ptr<IDataSeries> SpectrogramParserHelper::createSeries()
|
|
290
|
std::shared_ptr<IDataSeries> SpectrogramParserHelper::createSeries()
|
|
291
|
{
|
|
291
|
{
|
|
292
|
// Before creating the series, we handle its data holes
|
|
292
|
// Before creating the series, we handle its data holes
|
|
293
|
handleDataHoles();
|
|
293
|
handleDataHoles();
|
|
294
|
|
|
294
|
|
|
295
|
return std::make_shared<SpectrogramSeries>(
|
|
295
|
return std::make_shared<SpectrogramSeries>(
|
|
296
|
std::move(m_XAxisData), std::move(m_YAxisData), std::move(m_ValuesData),
|
|
296
|
std::move(m_XAxisData), std::move(m_YAxisData), std::move(m_ValuesData),
|
|
297
|
Unit{"t", true}, // x-axis unit is always a time unit
|
|
297
|
Unit{"t", true}, // x-axis unit is always a time unit
|
|
298
|
m_Properties.value(Y_AXIS_UNIT_PROPERTY).value<Unit>(),
|
|
298
|
m_Properties.value(Y_AXIS_UNIT_PROPERTY).value<Unit>(),
|
|
299
|
m_Properties.value(VALUES_UNIT_PROPERTY).value<Unit>());
|
|
299
|
m_Properties.value(VALUES_UNIT_PROPERTY).value<Unit>());
|
|
300
|
}
|
|
300
|
}
|
|
301
|
|
|
301
|
|
|
302
|
void SpectrogramParserHelper::readPropertyLine(const QString &line)
|
|
302
|
void SpectrogramParserHelper::readPropertyLine(const QString &line)
|
|
303
|
{
|
|
303
|
{
|
|
304
|
// Set of functions to test on the line to generate a property. If a function is valid (i.e. a
|
|
304
|
// Set of functions to test on the line to generate a property. If a function is valid (i.e. a
|
|
305
|
// property has been generated for the line), the line is treated as processed and the other
|
|
305
|
// property has been generated for the line), the line is treated as processed and the other
|
|
306
|
// functions are not called
|
|
306
|
// functions are not called
|
|
307
|
std::vector<std::function<bool()> > functions{
|
|
307
|
std::vector<std::function<bool()> > functions{
|
|
308
|
// values unit
|
|
308
|
// values unit
|
|
309
|
[&] {
|
|
309
|
[&] {
|
|
310
|
return tryReadUnit(m_Properties, VALUES_UNIT_PROPERTY, line,
|
|
310
|
return tryReadUnit(m_Properties, VALUES_UNIT_PROPERTY, line,
|
|
311
|
SPECTROGRAM_VALUES_UNIT_REGEX);
|
|
311
|
SPECTROGRAM_VALUES_UNIT_REGEX);
|
|
312
|
},
|
|
312
|
},
|
|
313
|
// y-axis unit
|
|
313
|
// y-axis unit
|
|
314
|
[&] {
|
|
314
|
[&] {
|
|
315
|
return tryReadUnit(m_Properties, Y_AXIS_UNIT_PROPERTY, line,
|
|
315
|
return tryReadUnit(m_Properties, Y_AXIS_UNIT_PROPERTY, line,
|
|
316
|
SPECTROGRAM_Y_AXIS_UNIT_REGEX);
|
|
316
|
SPECTROGRAM_Y_AXIS_UNIT_REGEX);
|
|
317
|
},
|
|
317
|
},
|
|
318
|
// min sampling
|
|
318
|
// min sampling
|
|
319
|
[&] {
|
|
319
|
[&] {
|
|
320
|
return tryReadDouble(m_Properties, MIN_SAMPLING_PROPERTY, line,
|
|
320
|
return tryReadDouble(m_Properties, MIN_SAMPLING_PROPERTY, line,
|
|
321
|
SPECTROGRAM_MIN_SAMPLING_REGEX);
|
|
321
|
SPECTROGRAM_MIN_SAMPLING_REGEX);
|
|
322
|
},
|
|
322
|
},
|
|
323
|
// max sampling
|
|
323
|
// max sampling
|
|
324
|
[&] {
|
|
324
|
[&] {
|
|
325
|
return tryReadDouble(m_Properties, MAX_SAMPLING_PROPERTY, line,
|
|
325
|
return tryReadDouble(m_Properties, MAX_SAMPLING_PROPERTY, line,
|
|
326
|
SPECTROGRAM_MAX_SAMPLING_REGEX);
|
|
326
|
SPECTROGRAM_MAX_SAMPLING_REGEX);
|
|
327
|
},
|
|
327
|
},
|
|
328
|
// fill value
|
|
328
|
// fill value
|
|
329
|
[&] {
|
|
329
|
[&] {
|
|
330
|
return tryReadDouble(m_Properties, FILL_VALUE_PROPERTY, line,
|
|
330
|
return tryReadDouble(m_Properties, FILL_VALUE_PROPERTY, line,
|
|
331
|
SPECTROGRAM_FILL_VALUE_REGEX);
|
|
331
|
SPECTROGRAM_FILL_VALUE_REGEX);
|
|
332
|
},
|
|
332
|
},
|
|
333
|
// min bounds of each band
|
|
333
|
// min bounds of each band
|
|
334
|
[&] {
|
|
334
|
[&] {
|
|
335
|
return tryReadDoubles(m_Properties, MIN_BANDS_PROPERTY, line,
|
|
335
|
return tryReadDoubles(m_Properties, MIN_BANDS_PROPERTY, line,
|
|
336
|
SPECTROGRAM_MIN_BANDS_REGEX);
|
|
336
|
SPECTROGRAM_MIN_BANDS_REGEX);
|
|
337
|
},
|
|
337
|
},
|
|
338
|
// max bounds of each band
|
|
338
|
// max bounds of each band
|
|
339
|
[&] {
|
|
339
|
[&] {
|
|
340
|
return tryReadDoubles(m_Properties, MAX_BANDS_PROPERTY, line,
|
|
340
|
return tryReadDoubles(m_Properties, MAX_BANDS_PROPERTY, line,
|
|
341
|
SPECTROGRAM_MAX_BANDS_REGEX);
|
|
341
|
SPECTROGRAM_MAX_BANDS_REGEX);
|
|
342
|
}};
|
|
342
|
}};
|
|
343
|
|
|
343
|
|
|
344
|
for (auto function : functions) {
|
|
344
|
for (auto function : functions) {
|
|
345
|
// Stops at the first function that is valid
|
|
345
|
// Stops at the first function that is valid
|
|
346
|
if (function()) {
|
|
346
|
if (function()) {
|
|
347
|
return;
|
|
347
|
return;
|
|
348
|
}
|
|
348
|
}
|
|
349
|
}
|
|
349
|
}
|
|
350
|
}
|
|
350
|
}
|
|
351
|
|
|
351
|
|
|
352
|
void SpectrogramParserHelper::readResultLine(const QString &line)
|
|
352
|
void SpectrogramParserHelper::readResultLine(const QString &line)
|
|
353
|
{
|
|
353
|
{
|
|
354
|
tryReadResult(m_XAxisData, m_ValuesData, line, m_ValuesIndexes, m_FillValue);
|
|
354
|
tryReadResult(m_XAxisData, m_ValuesData, line, m_ValuesIndexes, m_FillValue);
|
|
355
|
}
|
|
355
|
}
|
|
356
|
|
|
356
|
|
|
357
|
void SpectrogramParserHelper::handleDataHoles()
|
|
357
|
void SpectrogramParserHelper::handleDataHoles()
|
|
358
|
{
|
|
358
|
{
|
|
359
|
// Fills data holes according to the max resolution found in the AMDA file
|
|
359
|
// Fills data holes according to the max resolution found in the AMDA file
|
|
360
|
auto resolution = m_Properties.value(MAX_SAMPLING_PROPERTY).value<double>();
|
|
360
|
auto resolution = m_Properties.value(MAX_SAMPLING_PROPERTY).value<double>();
|
|
361
|
auto fillValue = m_Properties.value(FILL_VALUE_PROPERTY).value<double>();
|
|
361
|
auto fillValue = m_Properties.value(FILL_VALUE_PROPERTY).value<double>();
|
|
|
|
|
362
|
auto minBound = m_Properties.value(START_TIME_PROPERTY).value<double>();
|
|
|
|
|
363
|
auto maxBound = m_Properties.value(END_TIME_PROPERTY).value<double>();
|
|
362
|
|
|
364
|
|
|
363
|
DataSeriesUtils::fillDataHoles(m_XAxisData, m_ValuesData, resolution, fillValue);
|
|
365
|
DataSeriesUtils::fillDataHoles(m_XAxisData, m_ValuesData, resolution, fillValue, minBound,
|
|
|
|
|
366
|
maxBound);
|
|
364
|
}
|
|
367
|
}
|
|
365
|
|
|
368
|
|
|
366
|
// ////////////////// //
|
|
369
|
// ////////////////// //
|
|
367
|
// VectorParserHelper //
|
|
370
|
// VectorParserHelper //
|
|
368
|
// ////////////////// //
|
|
371
|
// ////////////////// //
|
|
369
|
|
|
372
|
|
|
370
|
bool VectorParserHelper::checkProperties()
|
|
373
|
bool VectorParserHelper::checkProperties()
|
|
371
|
{
|
|
374
|
{
|
|
372
|
return checkUnit(m_Properties, X_AXIS_UNIT_PROPERTY,
|
|
375
|
return checkUnit(m_Properties, X_AXIS_UNIT_PROPERTY,
|
|
373
|
QObject::tr("The x-axis unit could not be found in the file"));
|
|
376
|
QObject::tr("The x-axis unit could not be found in the file"));
|
|
374
|
}
|
|
377
|
}
|
|
375
|
|
|
378
|
|
|
376
|
std::shared_ptr<IDataSeries> VectorParserHelper::createSeries()
|
|
379
|
std::shared_ptr<IDataSeries> VectorParserHelper::createSeries()
|
|
377
|
{
|
|
380
|
{
|
|
378
|
return std::make_shared<VectorSeries>(std::move(m_XAxisData), std::move(m_ValuesData),
|
|
381
|
return std::make_shared<VectorSeries>(std::move(m_XAxisData), std::move(m_ValuesData),
|
|
379
|
m_Properties.value(X_AXIS_UNIT_PROPERTY).value<Unit>(),
|
|
382
|
m_Properties.value(X_AXIS_UNIT_PROPERTY).value<Unit>(),
|
|
380
|
m_Properties.value(VALUES_UNIT_PROPERTY).value<Unit>());
|
|
383
|
m_Properties.value(VALUES_UNIT_PROPERTY).value<Unit>());
|
|
381
|
}
|
|
384
|
}
|
|
382
|
|
|
385
|
|
|
383
|
void VectorParserHelper::readPropertyLine(const QString &line)
|
|
386
|
void VectorParserHelper::readPropertyLine(const QString &line)
|
|
384
|
{
|
|
387
|
{
|
|
385
|
tryReadUnit(m_Properties, X_AXIS_UNIT_PROPERTY, line, DEFAULT_X_AXIS_UNIT_REGEX, true);
|
|
388
|
tryReadUnit(m_Properties, X_AXIS_UNIT_PROPERTY, line, DEFAULT_X_AXIS_UNIT_REGEX, true);
|
|
386
|
}
|
|
389
|
}
|
|
387
|
|
|
390
|
|
|
388
|
void VectorParserHelper::readResultLine(const QString &line)
|
|
391
|
void VectorParserHelper::readResultLine(const QString &line)
|
|
389
|
{
|
|
392
|
{
|
|
390
|
tryReadResult(m_XAxisData, m_ValuesData, line, valuesIndexes());
|
|
393
|
tryReadResult(m_XAxisData, m_ValuesData, line, valuesIndexes());
|
|
391
|
}
|
|
394
|
}
|
|
392
|
|
|
395
|
|
|
393
|
std::vector<int> VectorParserHelper::valuesIndexes() const
|
|
396
|
std::vector<int> VectorParserHelper::valuesIndexes() const
|
|
394
|
{
|
|
397
|
{
|
|
395
|
// 3 values to read, in order in the file (x, y, z)
|
|
398
|
// 3 values to read, in order in the file (x, y, z)
|
|
396
|
static auto result = std::vector<int>{0, 1, 2};
|
|
399
|
static auto result = std::vector<int>{0, 1, 2};
|
|
397
|
return result;
|
|
400
|
return result;
|
|
398
|
}
|
|
401
|
}
|