##// END OF EJS Templates
Add the cmath include missing
perrinel -
r400:202e9db74e6e
parent child
Show More
@@ -1,132 +1,134
1 #include "AmdaResultParser.h"
1 #include "AmdaResultParser.h"
2
2
3 #include <Data/ScalarSeries.h>
3 #include <Data/ScalarSeries.h>
4
4
5 #include <QDateTime>
5 #include <QDateTime>
6 #include <QFile>
6 #include <QFile>
7 #include <QRegularExpression>
7 #include <QRegularExpression>
8
8
9 #include <cmath>
10
9 Q_LOGGING_CATEGORY(LOG_AmdaResultParser, "AmdaResultParser")
11 Q_LOGGING_CATEGORY(LOG_AmdaResultParser, "AmdaResultParser")
10
12
11 namespace {
13 namespace {
12
14
13 /// Format for dates in result files
15 /// Format for dates in result files
14 const auto DATE_FORMAT = QStringLiteral("yyyy-MM-ddThh:mm:ss.zzz");
16 const auto DATE_FORMAT = QStringLiteral("yyyy-MM-ddThh:mm:ss.zzz");
15
17
16 /// Separator between values in a result line
18 /// Separator between values in a result line
17 const auto RESULT_LINE_SEPARATOR = QRegularExpression{QStringLiteral("\\s+")};
19 const auto RESULT_LINE_SEPARATOR = QRegularExpression{QStringLiteral("\\s+")};
18
20
19 /// Regex to find unit in a line. Examples of valid lines:
21 /// Regex to find unit in a line. Examples of valid lines:
20 /// ... - Units : nT - ...
22 /// ... - Units : nT - ...
21 /// ... -Units:nT- ...
23 /// ... -Units:nT- ...
22 /// ... -Units: mΒ²- ...
24 /// ... -Units: mΒ²- ...
23 /// ... - Units : m/s - ...
25 /// ... - Units : m/s - ...
24 const auto UNIT_REGEX = QRegularExpression{QStringLiteral("-\\s*Units\\s*:\\s*(.+?)\\s*-")};
26 const auto UNIT_REGEX = QRegularExpression{QStringLiteral("-\\s*Units\\s*:\\s*(.+?)\\s*-")};
25
27
26 /// Converts a string date to a double date
28 /// Converts a string date to a double date
27 /// @return a double that represents the date in seconds, NaN if the string date can't be converted
29 /// @return a double that represents the date in seconds, NaN if the string date can't be converted
28 double doubleDate(const QString &stringDate) noexcept
30 double doubleDate(const QString &stringDate) noexcept
29 {
31 {
30 auto dateTime = QDateTime::fromString(stringDate, DATE_FORMAT);
32 auto dateTime = QDateTime::fromString(stringDate, DATE_FORMAT);
31 return dateTime.isValid() ? (dateTime.toMSecsSinceEpoch() / 1000.)
33 return dateTime.isValid() ? (dateTime.toMSecsSinceEpoch() / 1000.)
32 : std::numeric_limits<double>::quiet_NaN();
34 : std::numeric_limits<double>::quiet_NaN();
33 }
35 }
34
36
35 /**
37 /**
36 * Reads stream to retrieve x-axis unit
38 * Reads stream to retrieve x-axis unit
37 * @param stream the stream to read
39 * @param stream the stream to read
38 * @return the unit that has been read in the stream, a default unit (time unit with no label) if an
40 * @return the unit that has been read in the stream, a default unit (time unit with no label) if an
39 * error occured during reading
41 * error occured during reading
40 */
42 */
41 Unit readXAxisUnit(QTextStream &stream)
43 Unit readXAxisUnit(QTextStream &stream)
42 {
44 {
43 QString line{};
45 QString line{};
44
46
45 if (stream.readLineInto(&line)) {
47 if (stream.readLineInto(&line)) {
46 auto match = UNIT_REGEX.match(line);
48 auto match = UNIT_REGEX.match(line);
47 if (match.hasMatch()) {
49 if (match.hasMatch()) {
48 return Unit{match.captured(1), true};
50 return Unit{match.captured(1), true};
49 }
51 }
50 else {
52 else {
51 qCWarning(LOG_AmdaResultParser())
53 qCWarning(LOG_AmdaResultParser())
52 << QObject::tr("Can't read unit: invalid line %1").arg(line);
54 << QObject::tr("Can't read unit: invalid line %1").arg(line);
53 }
55 }
54 }
56 }
55 else {
57 else {
56 qCWarning(LOG_AmdaResultParser()) << QObject::tr("Can't read unit: end of file");
58 qCWarning(LOG_AmdaResultParser()) << QObject::tr("Can't read unit: end of file");
57 }
59 }
58
60
59 // Error cases
61 // Error cases
60 return Unit{{}, true};
62 return Unit{{}, true};
61 }
63 }
62
64
63 /**
65 /**
64 * Reads stream to retrieve results
66 * Reads stream to retrieve results
65 * @param stream the stream to read
67 * @param stream the stream to read
66 * @return the pair of vectors x-axis data/values data that has been read in the stream
68 * @return the pair of vectors x-axis data/values data that has been read in the stream
67 */
69 */
68 QPair<QVector<double>, QVector<double> > readResults(QTextStream &stream)
70 QPair<QVector<double>, QVector<double> > readResults(QTextStream &stream)
69 {
71 {
70 auto xData = QVector<double>{};
72 auto xData = QVector<double>{};
71 auto valuesData = QVector<double>{};
73 auto valuesData = QVector<double>{};
72
74
73 QString line{};
75 QString line{};
74 while (stream.readLineInto(&line)) {
76 while (stream.readLineInto(&line)) {
75 auto lineData = line.split(RESULT_LINE_SEPARATOR, QString::SkipEmptyParts);
77 auto lineData = line.split(RESULT_LINE_SEPARATOR, QString::SkipEmptyParts);
76 if (lineData.size() == 2) {
78 if (lineData.size() == 2) {
77 // X : the data is converted from date to double (in secs)
79 // X : the data is converted from date to double (in secs)
78 auto x = doubleDate(lineData.at(0));
80 auto x = doubleDate(lineData.at(0));
79
81
80 // Value
82 // Value
81 bool valueOk;
83 bool valueOk;
82 auto value = lineData.at(1).toDouble(&valueOk);
84 auto value = lineData.at(1).toDouble(&valueOk);
83
85
84 // Adds result only if x and value are valid
86 // Adds result only if x and value are valid
85 if (!std::isnan(x) && !std::isnan(value) && valueOk) {
87 if (!std::isnan(x) && !std::isnan(value) && valueOk) {
86 xData.push_back(x);
88 xData.push_back(x);
87 valuesData.push_back(value);
89 valuesData.push_back(value);
88 }
90 }
89 else {
91 else {
90 qCWarning(LOG_AmdaResultParser())
92 qCWarning(LOG_AmdaResultParser())
91 << QObject::tr(
93 << QObject::tr(
92 "Can't retrieve results from line %1: x and/or value are invalid")
94 "Can't retrieve results from line %1: x and/or value are invalid")
93 .arg(line);
95 .arg(line);
94 }
96 }
95 }
97 }
96 else {
98 else {
97 qCWarning(LOG_AmdaResultParser())
99 qCWarning(LOG_AmdaResultParser())
98 << QObject::tr("Can't retrieve results from line %1: invalid line").arg(line);
100 << QObject::tr("Can't retrieve results from line %1: invalid line").arg(line);
99 }
101 }
100 }
102 }
101
103
102 return qMakePair(std::move(xData), std::move(valuesData));
104 return qMakePair(std::move(xData), std::move(valuesData));
103 }
105 }
104
106
105 } // namespace
107 } // namespace
106
108
107 std::shared_ptr<IDataSeries> AmdaResultParser::readTxt(const QString &filePath) noexcept
109 std::shared_ptr<IDataSeries> AmdaResultParser::readTxt(const QString &filePath) noexcept
108 {
110 {
109 QFile file{filePath};
111 QFile file{filePath};
110
112
111 if (!file.open(QFile::ReadOnly | QIODevice::Text)) {
113 if (!file.open(QFile::ReadOnly | QIODevice::Text)) {
112 qCCritical(LOG_AmdaResultParser())
114 qCCritical(LOG_AmdaResultParser())
113 << QObject::tr("Can't retrieve AMDA data from file %1: %2")
115 << QObject::tr("Can't retrieve AMDA data from file %1: %2")
114 .arg(filePath, file.errorString());
116 .arg(filePath, file.errorString());
115 return nullptr;
117 return nullptr;
116 }
118 }
117
119
118 QTextStream stream{&file};
120 QTextStream stream{&file};
119
121
120 // Ignore first two lines (comments lines)
122 // Ignore first two lines (comments lines)
121 stream.readLine();
123 stream.readLine();
122 stream.readLine();
124 stream.readLine();
123
125
124 // Reads x-axis unit
126 // Reads x-axis unit
125 auto xAxisUnit = readXAxisUnit(stream);
127 auto xAxisUnit = readXAxisUnit(stream);
126
128
127 // Reads results
129 // Reads results
128 auto results = readResults(stream);
130 auto results = readResults(stream);
129
131
130 return std::make_shared<ScalarSeries>(std::move(results.first), std::move(results.second),
132 return std::make_shared<ScalarSeries>(std::move(results.first), std::move(results.second),
131 xAxisUnit, Unit{});
133 xAxisUnit, Unit{});
132 }
134 }
@@ -1,179 +1,179
1 #include "AmdaResultParser.h"
1 #include "AmdaResultParser.h"
2
2
3 #include <Data/ScalarSeries.h>
3 #include <Data/ScalarSeries.h>
4
4
5 #include <QObject>
5 #include <QObject>
6 #include <QtTest>
6 #include <QtTest>
7
7
8 namespace {
8 namespace {
9
9
10 /// Path for the tests
10 /// Path for the tests
11 const auto TESTS_RESOURCES_PATH
11 const auto TESTS_RESOURCES_PATH
12 = QFileInfo{QString{AMDA_TESTS_RESOURCES_DIR}, "TestAmdaResultParser"}.absoluteFilePath();
12 = QFileInfo{QString{AMDA_TESTS_RESOURCES_DIR}, "TestAmdaResultParser"}.absoluteFilePath();
13
13
14 QString inputFilePath(const QString &inputFileName)
14 QString inputFilePath(const QString &inputFileName)
15 {
15 {
16 return QFileInfo{TESTS_RESOURCES_PATH, inputFileName}.absoluteFilePath();
16 return QFileInfo{TESTS_RESOURCES_PATH, inputFileName}.absoluteFilePath();
17 }
17 }
18
18
19 struct ExpectedResults {
19 struct ExpectedResults {
20 explicit ExpectedResults() = default;
20 explicit ExpectedResults() = default;
21
21
22 /// Ctor with QVector<QDateTime> as x-axis data. Datetimes are converted to doubles
22 /// Ctor with QVector<QDateTime> as x-axis data. Datetimes are converted to doubles
23 explicit ExpectedResults(Unit xAxisUnit, Unit valuesUnit, const QVector<QDateTime> &xAxisData,
23 explicit ExpectedResults(Unit xAxisUnit, Unit valuesUnit, const QVector<QDateTime> &xAxisData,
24 QVector<double> valuesData)
24 QVector<double> valuesData)
25 : m_ParsingOK{true},
25 : m_ParsingOK{true},
26 m_XAxisUnit{xAxisUnit},
26 m_XAxisUnit{xAxisUnit},
27 m_ValuesUnit{valuesUnit},
27 m_ValuesUnit{valuesUnit},
28 m_XAxisData{},
28 m_XAxisData{},
29 m_ValuesData{std::move(valuesData)}
29 m_ValuesData{std::move(valuesData)}
30 {
30 {
31 // Converts QVector<QDateTime> to QVector<double>
31 // Converts QVector<QDateTime> to QVector<double>
32 std::transform(xAxisData.cbegin(), xAxisData.cend(), std::back_inserter(m_XAxisData),
32 std::transform(xAxisData.cbegin(), xAxisData.cend(), std::back_inserter(m_XAxisData),
33 [](const auto &dateTime) { return dateTime.toMSecsSinceEpoch() / 1000.; });
33 [](const auto &dateTime) { return dateTime.toMSecsSinceEpoch() / 1000.; });
34 }
34 }
35
35
36 /**
36 /**
37 * Validates a DataSeries compared to the expected results
37 * Validates a DataSeries compared to the expected results
38 * @param results the DataSeries to validate
38 * @param results the DataSeries to validate
39 */
39 */
40 void validate(std::shared_ptr<IDataSeries> results)
40 void validate(std::shared_ptr<IDataSeries> results)
41 {
41 {
42 if (m_ParsingOK) {
42 if (m_ParsingOK) {
43 auto scalarSeries = dynamic_cast<ScalarSeries *>(results.get());
43 auto scalarSeries = dynamic_cast<ScalarSeries *>(results.get());
44 QVERIFY(scalarSeries != nullptr);
44 QVERIFY(scalarSeries != nullptr);
45
45
46 // Checks units
46 // Checks units
47 QVERIFY(scalarSeries->xAxisUnit() == m_XAxisUnit);
47 QVERIFY(scalarSeries->xAxisUnit() == m_XAxisUnit);
48 QVERIFY(scalarSeries->valuesUnit() == m_ValuesUnit);
48 QVERIFY(scalarSeries->valuesUnit() == m_ValuesUnit);
49
49
50 // Checks values
50 // Checks values
51 QVERIFY(scalarSeries->xAxisData()->data() == m_XAxisData);
51 QVERIFY(scalarSeries->xAxisData()->data() == m_XAxisData);
52 QVERIFY(scalarSeries->valuesData()->data() == m_ValuesData);
52 QVERIFY(scalarSeries->valuesData()->data() == m_ValuesData);
53 }
53 }
54 else {
54 else {
55 QVERIFY(results == nullptr);
55 QVERIFY(results == nullptr);
56 }
56 }
57 }
57 }
58
58
59 // Parsing was successfully completed
59 // Parsing was successfully completed
60 bool m_ParsingOK{false};
60 bool m_ParsingOK{false};
61 // Expected x-axis unit
61 // Expected x-axis unit
62 Unit m_XAxisUnit{};
62 Unit m_XAxisUnit{};
63 // Expected values unit
63 // Expected values unit
64 Unit m_ValuesUnit{};
64 Unit m_ValuesUnit{};
65 // Expected x-axis data
65 // Expected x-axis data
66 QVector<double> m_XAxisData{};
66 QVector<double> m_XAxisData{};
67 // Expected values data
67 // Expected values data
68 QVector<double> m_ValuesData{};
68 QVector<double> m_ValuesData{};
69 };
69 };
70
70
71 } // namespace
71 } // namespace
72
72
73 Q_DECLARE_METATYPE(ExpectedResults)
73 Q_DECLARE_METATYPE(ExpectedResults)
74
74
75 class TestAmdaResultParser : public QObject {
75 class TestAmdaResultParser : public QObject {
76 Q_OBJECT
76 Q_OBJECT
77 private slots:
77 private slots:
78 /// Input test data
78 /// Input test data
79 /// @sa testTxtJson()
79 /// @sa testTxtJson()
80 void testReadTxt_data();
80 void testReadTxt_data();
81
81
82 /// Tests parsing of a TXT file
82 /// Tests parsing of a TXT file
83 void testReadTxt();
83 void testReadTxt();
84 };
84 };
85
85
86 void TestAmdaResultParser::testReadTxt_data()
86 void TestAmdaResultParser::testReadTxt_data()
87 {
87 {
88 // ////////////// //
88 // ////////////// //
89 // Test structure //
89 // Test structure //
90 // ////////////// //
90 // ////////////// //
91
91
92 // Name of TXT file to read
92 // Name of TXT file to read
93 QTest::addColumn<QString>("inputFileName");
93 QTest::addColumn<QString>("inputFileName");
94 // Expected results
94 // Expected results
95 QTest::addColumn<ExpectedResults>("expectedResults");
95 QTest::addColumn<ExpectedResults>("expectedResults");
96
96
97 // ////////// //
97 // ////////// //
98 // Test cases //
98 // Test cases //
99 // ////////// //
99 // ////////// //
100
100
101 auto dateTime = [](int year, int month, int day, int hours, int minutes, int seconds) {
101 auto dateTime = [](int year, int month, int day, int hours, int minutes, int seconds) {
102 return QDateTime{{year, month, day}, {hours, minutes, seconds}};
102 return QDateTime{{year, month, day}, {hours, minutes, seconds}};
103 };
103 };
104
104
105 // Valid file
105 // Valid file
106 QTest::newRow("Valid file")
106 QTest::newRow("Valid file")
107 << QStringLiteral("ValidScalar1.txt")
107 << QStringLiteral("ValidScalar1.txt")
108 << ExpectedResults{
108 << ExpectedResults{
109 Unit{QStringLiteral("nT"), true}, Unit{},
109 Unit{QStringLiteral("nT"), true}, Unit{},
110 QVector<QDateTime>{dateTime(2013, 9, 23, 9, 0, 30), dateTime(2013, 9, 23, 9, 1, 30),
110 QVector<QDateTime>{dateTime(2013, 9, 23, 9, 0, 30), dateTime(2013, 9, 23, 9, 1, 30),
111 dateTime(2013, 9, 23, 9, 2, 30), dateTime(2013, 9, 23, 9, 3, 30),
111 dateTime(2013, 9, 23, 9, 2, 30), dateTime(2013, 9, 23, 9, 3, 30),
112 dateTime(2013, 9, 23, 9, 4, 30), dateTime(2013, 9, 23, 9, 5, 30),
112 dateTime(2013, 9, 23, 9, 4, 30), dateTime(2013, 9, 23, 9, 5, 30),
113 dateTime(2013, 9, 23, 9, 6, 30), dateTime(2013, 9, 23, 9, 7, 30),
113 dateTime(2013, 9, 23, 9, 6, 30), dateTime(2013, 9, 23, 9, 7, 30),
114 dateTime(2013, 9, 23, 9, 8, 30), dateTime(2013, 9, 23, 9, 9, 30)},
114 dateTime(2013, 9, 23, 9, 8, 30), dateTime(2013, 9, 23, 9, 9, 30)},
115 QVector<double>{-2.83950, -2.71850, -2.52150, -2.57633, -2.58050, -2.48325, -2.63025,
115 QVector<double>{-2.83950, -2.71850, -2.52150, -2.57633, -2.58050, -2.48325, -2.63025,
116 -2.55800, -2.43250, -2.42200}};
116 -2.55800, -2.43250, -2.42200}};
117
117
118 // Valid files but with some invalid lines (wrong unit, wrong values, etc.)
118 // Valid files but with some invalid lines (wrong unit, wrong values, etc.)
119 QTest::newRow("No unit file") << QStringLiteral("NoUnit.txt")
119 QTest::newRow("No unit file") << QStringLiteral("NoUnit.txt")
120 << ExpectedResults{Unit{QStringLiteral(""), true}, Unit{},
120 << ExpectedResults{Unit{QStringLiteral(""), true}, Unit{},
121 QVector<QDateTime>{}, QVector<double>{}};
121 QVector<QDateTime>{}, QVector<double>{}};
122 QTest::newRow("Wrong unit file")
122 QTest::newRow("Wrong unit file")
123 << QStringLiteral("WrongUnit.txt")
123 << QStringLiteral("WrongUnit.txt")
124 << ExpectedResults{Unit{QStringLiteral(""), true}, Unit{},
124 << ExpectedResults{Unit{QStringLiteral(""), true}, Unit{},
125 QVector<QDateTime>{dateTime(2013, 9, 23, 9, 0, 30),
125 QVector<QDateTime>{dateTime(2013, 9, 23, 9, 0, 30),
126 dateTime(2013, 9, 23, 9, 1, 30),
126 dateTime(2013, 9, 23, 9, 1, 30),
127 dateTime(2013, 9, 23, 9, 2, 30)},
127 dateTime(2013, 9, 23, 9, 2, 30)},
128 QVector<double>{-2.83950, -2.71850, -2.52150}};
128 QVector<double>{-2.83950, -2.71850, -2.52150}};
129
129
130 QTest::newRow("Wrong results file (date of first line is invalid")
130 QTest::newRow("Wrong results file (date of first line is invalid")
131 << QStringLiteral("WrongDate.txt")
131 << QStringLiteral("WrongDate.txt")
132 << ExpectedResults{
132 << ExpectedResults{
133 Unit{QStringLiteral("nT"), true}, Unit{},
133 Unit{QStringLiteral("nT"), true}, Unit{},
134 QVector<QDateTime>{dateTime(2013, 9, 23, 9, 1, 30), dateTime(2013, 9, 23, 9, 2, 30)},
134 QVector<QDateTime>{dateTime(2013, 9, 23, 9, 1, 30), dateTime(2013, 9, 23, 9, 2, 30)},
135 QVector<double>{-2.71850, -2.52150}};
135 QVector<double>{-2.71850, -2.52150}};
136
136
137 QTest::newRow("Wrong results file (too many values for first line")
137 QTest::newRow("Wrong results file (too many values for first line")
138 << QStringLiteral("TooManyValues.txt")
138 << QStringLiteral("TooManyValues.txt")
139 << ExpectedResults{
139 << ExpectedResults{
140 Unit{QStringLiteral("nT"), true}, Unit{},
140 Unit{QStringLiteral("nT"), true}, Unit{},
141 QVector<QDateTime>{dateTime(2013, 9, 23, 9, 1, 30), dateTime(2013, 9, 23, 9, 2, 30)},
141 QVector<QDateTime>{dateTime(2013, 9, 23, 9, 1, 30), dateTime(2013, 9, 23, 9, 2, 30)},
142 QVector<double>{-2.71850, -2.52150}};
142 QVector<double>{-2.71850, -2.52150}};
143
143
144 QTest::newRow("Wrong results file (value of first line is invalid")
144 QTest::newRow("Wrong results file (value of first line is invalid")
145 << QStringLiteral("WrongValue.txt")
145 << QStringLiteral("WrongValue.txt")
146 << ExpectedResults{
146 << ExpectedResults{
147 Unit{QStringLiteral("nT"), true}, Unit{},
147 Unit{QStringLiteral("nT"), true}, Unit{},
148 QVector<QDateTime>{dateTime(2013, 9, 23, 9, 1, 30), dateTime(2013, 9, 23, 9, 2, 30)},
148 QVector<QDateTime>{dateTime(2013, 9, 23, 9, 1, 30), dateTime(2013, 9, 23, 9, 2, 30)},
149 QVector<double>{-2.71850, -2.52150}};
149 QVector<double>{-2.71850, -2.52150}};
150
150
151 QTest::newRow("Wrong results file (value of first line is NaN")
151 QTest::newRow("Wrong results file (value of first line is NaN")
152 << QStringLiteral("NaNValue.txt")
152 << QStringLiteral("NaNValue.txt")
153 << ExpectedResults{
153 << ExpectedResults{
154 Unit{QStringLiteral("nT"), true}, Unit{},
154 Unit{QStringLiteral("nT"), true}, Unit{},
155 QVector<QDateTime>{dateTime(2013, 9, 23, 9, 1, 30), dateTime(2013, 9, 23, 9, 2, 30)},
155 QVector<QDateTime>{dateTime(2013, 9, 23, 9, 1, 30), dateTime(2013, 9, 23, 9, 2, 30)},
156 QVector<double>{-2.71850, -2.52150}};
156 QVector<double>{-2.71850, -2.52150}};
157
157
158 // Invalid file
158 // Invalid file
159 QTest::newRow("Invalid file (unexisting file)")
159 QTest::newRow("Invalid file (unexisting file)") << QStringLiteral("UnexistingFile.txt")
160 << QStringLiteral("UnexistingFile.txt") << ExpectedResults{};
160 << ExpectedResults{};
161 }
161 }
162
162
163 void TestAmdaResultParser::testReadTxt()
163 void TestAmdaResultParser::testReadTxt()
164 {
164 {
165 QFETCH(QString, inputFileName);
165 QFETCH(QString, inputFileName);
166 QFETCH(ExpectedResults, expectedResults);
166 QFETCH(ExpectedResults, expectedResults);
167
167
168 // Parses file
168 // Parses file
169 auto filePath = inputFilePath(inputFileName);
169 auto filePath = inputFilePath(inputFileName);
170 auto results = AmdaResultParser::readTxt(filePath);
170 auto results = AmdaResultParser::readTxt(filePath);
171
171
172 // ///////////////// //
172 // ///////////////// //
173 // Validates results //
173 // Validates results //
174 // ///////////////// //
174 // ///////////////// //
175 expectedResults.validate(results);
175 expectedResults.validate(results);
176 }
176 }
177
177
178 QTEST_MAIN(TestAmdaResultParser)
178 QTEST_MAIN(TestAmdaResultParser)
179 #include "TestAmdaResultParser.moc"
179 #include "TestAmdaResultParser.moc"
General Comments 0
You need to be logged in to leave comments. Login now