##// END OF EJS Templates
Merge branch 'feature/AmdaProviderUpdate' into develop
Alexandre Leroux -
r398:6ff08786d807 merge
parent child
Show More
@@ -0,0 +1,6
1 #Sampling Time : 60
2 #Time Format : YYYY-MM-DDThh:mm:ss.mls
3 #imf(0) - Type : Local Parameter @ CDPP/AMDA - Name : bx_gse - Units : nT - Size : 1 - Frame : GSE - Mission : ACE - Instrument : MFI - Dataset : mfi_final-prelim
4 2013-09-23T09:00:30.000 NaN
5 2013-09-23T09:01:30.000 -2.71850
6 2013-09-23T09:02:30.000 -2.52150 No newline at end of file
@@ -0,0 +1,2
1 #Sampling Time : 60
2 #Time Format : YYYY-MM-DDThh:mm:ss.mls No newline at end of file
@@ -0,0 +1,6
1 #Sampling Time : 60
2 #Time Format : YYYY-MM-DDThh:mm:ss.mls
3 #imf(0) - Type : Local Parameter @ CDPP/AMDA - Name : bx_gse - Units : nT - Size : 1 - Frame : GSE - Mission : ACE - Instrument : MFI - Dataset : mfi_final-prelim
4 2013-09-23T09:00:30.000 -2.83950 1.05141 3.01547
5 2013-09-23T09:01:30.000 -2.71850
6 2013-09-23T09:02:30.000 -2.52150 No newline at end of file
@@ -0,0 +1,13
1 #Sampling Time : 60
2 #Time Format : YYYY-MM-DDThh:mm:ss.mls
3 #imf(0) - Type : Local Parameter @ CDPP/AMDA - Name : bx_gse - Units : nT - Size : 1 - Frame : GSE - Mission : ACE - Instrument : MFI - Dataset : mfi_final-prelim
4 2013-09-23T09:00:30.000 -2.83950
5 2013-09-23T09:01:30.000 -2.71850
6 2013-09-23T09:02:30.000 -2.52150
7 2013-09-23T09:03:30.000 -2.57633
8 2013-09-23T09:04:30.000 -2.58050
9 2013-09-23T09:05:30.000 -2.48325
10 2013-09-23T09:06:30.000 -2.63025
11 2013-09-23T09:07:30.000 -2.55800
12 2013-09-23T09:08:30.000 -2.43250
13 2013-09-23T09:09:30.000 -2.42200 No newline at end of file
@@ -0,0 +1,6
1 #Sampling Time : 60
2 #Time Format : YYYY-MM-DDThh:mm:ss.mls
3 #imf(0) - Type : Local Parameter @ CDPP/AMDA - Name : bx_gse - Units : nT - Size : 1 - Frame : GSE - Mission : ACE - Instrument : MFI - Dataset : mfi_final-prelim
4 23/09/2013 07:50:30 -2.83950
5 2013-09-23T09:01:30.000 -2.71850
6 2013-09-23T09:02:30.000 -2.52150 No newline at end of file
@@ -0,0 +1,6
1 #Sampling Time : 60
2 #Time Format : YYYY-MM-DDThh:mm:ss.mls
3 #Wrong unit comment
4 2013-09-23T09:00:30.000 -2.83950
5 2013-09-23T09:01:30.000 -2.71850
6 2013-09-23T09:02:30.000 -2.52150 No newline at end of file
@@ -0,0 +1,6
1 #Sampling Time : 60
2 #Time Format : YYYY-MM-DDThh:mm:ss.mls
3 #imf(0) - Type : Local Parameter @ CDPP/AMDA - Name : bx_gse - Units : nT - Size : 1 - Frame : GSE - Mission : ACE - Instrument : MFI - Dataset : mfi_final-prelim
4 2013-09-23T09:00:30.000 abc
5 2013-09-23T09:01:30.000 -2.71850
6 2013-09-23T09:02:30.000 -2.52150 No newline at end of file
@@ -0,0 +1,179
1 #include "AmdaResultParser.h"
2
3 #include <Data/ScalarSeries.h>
4
5 #include <QObject>
6 #include <QtTest>
7
8 namespace {
9
10 /// Path for the tests
11 const auto TESTS_RESOURCES_PATH
12 = QFileInfo{QString{AMDA_TESTS_RESOURCES_DIR}, "TestAmdaResultParser"}.absoluteFilePath();
13
14 QString inputFilePath(const QString &inputFileName)
15 {
16 return QFileInfo{TESTS_RESOURCES_PATH, inputFileName}.absoluteFilePath();
17 }
18
19 struct ExpectedResults {
20 explicit ExpectedResults() = default;
21
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,
24 QVector<double> valuesData)
25 : m_ParsingOK{true},
26 m_XAxisUnit{xAxisUnit},
27 m_ValuesUnit{valuesUnit},
28 m_XAxisData{},
29 m_ValuesData{std::move(valuesData)}
30 {
31 // Converts QVector<QDateTime> to QVector<double>
32 std::transform(xAxisData.cbegin(), xAxisData.cend(), std::back_inserter(m_XAxisData),
33 [](const auto &dateTime) { return dateTime.toMSecsSinceEpoch() / 1000.; });
34 }
35
36 /**
37 * Validates a DataSeries compared to the expected results
38 * @param results the DataSeries to validate
39 */
40 void validate(std::shared_ptr<IDataSeries> results)
41 {
42 if (m_ParsingOK) {
43 auto scalarSeries = dynamic_cast<ScalarSeries *>(results.get());
44 QVERIFY(scalarSeries != nullptr);
45
46 // Checks units
47 QVERIFY(scalarSeries->xAxisUnit() == m_XAxisUnit);
48 QVERIFY(scalarSeries->valuesUnit() == m_ValuesUnit);
49
50 // Checks values
51 QVERIFY(scalarSeries->xAxisData()->data() == m_XAxisData);
52 QVERIFY(scalarSeries->valuesData()->data() == m_ValuesData);
53 }
54 else {
55 QVERIFY(results == nullptr);
56 }
57 }
58
59 // Parsing was successfully completed
60 bool m_ParsingOK{false};
61 // Expected x-axis unit
62 Unit m_XAxisUnit{};
63 // Expected values unit
64 Unit m_ValuesUnit{};
65 // Expected x-axis data
66 QVector<double> m_XAxisData{};
67 // Expected values data
68 QVector<double> m_ValuesData{};
69 };
70
71 } // namespace
72
73 Q_DECLARE_METATYPE(ExpectedResults)
74
75 class TestAmdaResultParser : public QObject {
76 Q_OBJECT
77 private slots:
78 /// Input test data
79 /// @sa testTxtJson()
80 void testReadTxt_data();
81
82 /// Tests parsing of a TXT file
83 void testReadTxt();
84 };
85
86 void TestAmdaResultParser::testReadTxt_data()
87 {
88 // ////////////// //
89 // Test structure //
90 // ////////////// //
91
92 // Name of TXT file to read
93 QTest::addColumn<QString>("inputFileName");
94 // Expected results
95 QTest::addColumn<ExpectedResults>("expectedResults");
96
97 // ////////// //
98 // Test cases //
99 // ////////// //
100
101 auto dateTime = [](int year, int month, int day, int hours, int minutes, int seconds) {
102 return QDateTime{{year, month, day}, {hours, minutes, seconds}};
103 };
104
105 // Valid file
106 QTest::newRow("Valid file")
107 << QStringLiteral("ValidScalar1.txt")
108 << ExpectedResults{
109 Unit{QStringLiteral("nT"), true}, Unit{},
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),
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),
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,
116 -2.55800, -2.43250, -2.42200}};
117
118 // Valid files but with some invalid lines (wrong unit, wrong values, etc.)
119 QTest::newRow("No unit file") << QStringLiteral("NoUnit.txt")
120 << ExpectedResults{Unit{QStringLiteral(""), true}, Unit{},
121 QVector<QDateTime>{}, QVector<double>{}};
122 QTest::newRow("Wrong unit file")
123 << QStringLiteral("WrongUnit.txt")
124 << ExpectedResults{Unit{QStringLiteral(""), true}, Unit{},
125 QVector<QDateTime>{dateTime(2013, 9, 23, 9, 0, 30),
126 dateTime(2013, 9, 23, 9, 1, 30),
127 dateTime(2013, 9, 23, 9, 2, 30)},
128 QVector<double>{-2.83950, -2.71850, -2.52150}};
129
130 QTest::newRow("Wrong results file (date of first line is invalid")
131 << QStringLiteral("WrongDate.txt")
132 << ExpectedResults{
133 Unit{QStringLiteral("nT"), true}, Unit{},
134 QVector<QDateTime>{dateTime(2013, 9, 23, 9, 1, 30), dateTime(2013, 9, 23, 9, 2, 30)},
135 QVector<double>{-2.71850, -2.52150}};
136
137 QTest::newRow("Wrong results file (too many values for first line")
138 << QStringLiteral("TooManyValues.txt")
139 << ExpectedResults{
140 Unit{QStringLiteral("nT"), true}, Unit{},
141 QVector<QDateTime>{dateTime(2013, 9, 23, 9, 1, 30), dateTime(2013, 9, 23, 9, 2, 30)},
142 QVector<double>{-2.71850, -2.52150}};
143
144 QTest::newRow("Wrong results file (value of first line is invalid")
145 << QStringLiteral("WrongValue.txt")
146 << ExpectedResults{
147 Unit{QStringLiteral("nT"), true}, Unit{},
148 QVector<QDateTime>{dateTime(2013, 9, 23, 9, 1, 30), dateTime(2013, 9, 23, 9, 2, 30)},
149 QVector<double>{-2.71850, -2.52150}};
150
151 QTest::newRow("Wrong results file (value of first line is NaN")
152 << QStringLiteral("NaNValue.txt")
153 << ExpectedResults{
154 Unit{QStringLiteral("nT"), true}, Unit{},
155 QVector<QDateTime>{dateTime(2013, 9, 23, 9, 1, 30), dateTime(2013, 9, 23, 9, 2, 30)},
156 QVector<double>{-2.71850, -2.52150}};
157
158 // Invalid file
159 QTest::newRow("Invalid file (unexisting file)")
160 << QStringLiteral("UnexistingFile.txt") << ExpectedResults{};
161 }
162
163 void TestAmdaResultParser::testReadTxt()
164 {
165 QFETCH(QString, inputFileName);
166 QFETCH(ExpectedResults, expectedResults);
167
168 // Parses file
169 auto filePath = inputFilePath(inputFileName);
170 auto results = AmdaResultParser::readTxt(filePath);
171
172 // ///////////////// //
173 // Validates results //
174 // ///////////////// //
175 expectedResults.validate(results);
176 }
177
178 QTEST_MAIN(TestAmdaResultParser)
179 #include "TestAmdaResultParser.moc"
@@ -1,105 +1,116
1 #ifndef SCIQLOP_ARRAYDATA_H
1 #ifndef SCIQLOP_ARRAYDATA_H
2 #define SCIQLOP_ARRAYDATA_H
2 #define SCIQLOP_ARRAYDATA_H
3
3
4 #include <QReadLocker>
4 #include <QReadLocker>
5 #include <QReadWriteLock>
5 #include <QReadWriteLock>
6 #include <QVector>
6 #include <QVector>
7 /**
7 /**
8 * @brief The ArrayData class represents a dataset for a data series.
8 * @brief The ArrayData class represents a dataset for a data series.
9 *
9 *
10 * A dataset can be unidimensional or two-dimensional. This property is determined by the Dim
10 * A dataset can be unidimensional or two-dimensional. This property is determined by the Dim
11 * template-parameter.
11 * template-parameter.
12 *
12 *
13 * @tparam Dim the dimension of the ArrayData (one or two)
13 * @tparam Dim the dimension of the ArrayData (one or two)
14 * @sa IDataSeries
14 * @sa IDataSeries
15 */
15 */
16 template <int Dim>
16 template <int Dim>
17 class ArrayData {
17 class ArrayData {
18 public:
18 public:
19 /**
19 /**
20 * Ctor for a unidimensional ArrayData
20 * Ctor for a unidimensional ArrayData
21 * @param nbColumns the number of values the ArrayData will hold
21 * @param nbColumns the number of values the ArrayData will hold
22 */
22 */
23 template <int D = Dim, typename = std::enable_if_t<D == 1> >
23 template <int D = Dim, typename = std::enable_if_t<D == 1> >
24 explicit ArrayData(int nbColumns) : m_Data{1, QVector<double>{}}
24 explicit ArrayData(int nbColumns) : m_Data{1, QVector<double>{}}
25 {
25 {
26 QWriteLocker locker{&m_Lock};
26 QWriteLocker locker{&m_Lock};
27 m_Data[0].resize(nbColumns);
27 m_Data[0].resize(nbColumns);
28 }
28 }
29
29
30 /**
31 * Ctor for a unidimensional ArrayData
32 * @param data the data the ArrayData will hold
33 */
34 template <int D = Dim, typename = std::enable_if_t<D == 1> >
35 explicit ArrayData(QVector<double> data) : m_Data{1, QVector<double>{}}
36 {
37 QWriteLocker locker{&m_Lock};
38 m_Data[0] = std::move(data);
39 }
40
30 /// Copy ctor
41 /// Copy ctor
31 explicit ArrayData(const ArrayData &other)
42 explicit ArrayData(const ArrayData &other)
32 {
43 {
33 QReadLocker otherLocker{&other.m_Lock};
44 QReadLocker otherLocker{&other.m_Lock};
34 QWriteLocker locker{&m_Lock};
45 QWriteLocker locker{&m_Lock};
35 m_Data = other.m_Data;
46 m_Data = other.m_Data;
36 }
47 }
37
48
38 /**
49 /**
39 * Sets a data at a specified index. The index has to be valid to be effective
50 * Sets a data at a specified index. The index has to be valid to be effective
40 * @param index the index to which the data will be set
51 * @param index the index to which the data will be set
41 * @param data the data to set
52 * @param data the data to set
42 * @remarks this method is only available for a unidimensional ArrayData
53 * @remarks this method is only available for a unidimensional ArrayData
43 */
54 */
44 template <int D = Dim, typename = std::enable_if_t<D == 1> >
55 template <int D = Dim, typename = std::enable_if_t<D == 1> >
45 void setData(int index, double data) noexcept
56 void setData(int index, double data) noexcept
46 {
57 {
47 QWriteLocker locker{&m_Lock};
58 QWriteLocker locker{&m_Lock};
48 if (index >= 0 && index < m_Data.at(0).size()) {
59 if (index >= 0 && index < m_Data.at(0).size()) {
49 m_Data[0].replace(index, data);
60 m_Data[0].replace(index, data);
50 }
61 }
51 }
62 }
52
63
53 /**
64 /**
54 * @return the data as a vector
65 * @return the data as a vector
55 * @remarks this method is only available for a unidimensional ArrayData
66 * @remarks this method is only available for a unidimensional ArrayData
56 */
67 */
57 template <int D = Dim, typename = std::enable_if_t<D == 1> >
68 template <int D = Dim, typename = std::enable_if_t<D == 1> >
58 QVector<double> data() const noexcept
69 QVector<double> data() const noexcept
59 {
70 {
60 QReadLocker locker{&m_Lock};
71 QReadLocker locker{&m_Lock};
61 return m_Data[0];
72 return m_Data[0];
62 }
73 }
63
74
64 /**
75 /**
65 * @return the data as a vector
76 * @return the data as a vector
66 * @remarks this method is only available for a unidimensional ArrayData
77 * @remarks this method is only available for a unidimensional ArrayData
67 */
78 */
68 template <int D = Dim, typename = std::enable_if_t<D == 1> >
79 template <int D = Dim, typename = std::enable_if_t<D == 1> >
69 QVector<double> data(double tStart, double tEnd) const noexcept
80 QVector<double> data(double tStart, double tEnd) const noexcept
70 {
81 {
71 QReadLocker locker{&m_Lock};
82 QReadLocker locker{&m_Lock};
72 return m_Data.at(tStart);
83 return m_Data.at(tStart);
73 }
84 }
74
85
75 // TODO Comment
86 // TODO Comment
76 template <int D = Dim, typename = std::enable_if_t<D == 1> >
87 template <int D = Dim, typename = std::enable_if_t<D == 1> >
77 void merge(const ArrayData<1> &arrayData)
88 void merge(const ArrayData<1> &arrayData)
78 {
89 {
79 QWriteLocker locker{&m_Lock};
90 QWriteLocker locker{&m_Lock};
80 if (!m_Data.empty()) {
91 if (!m_Data.empty()) {
81 QReadLocker otherLocker{&arrayData.m_Lock};
92 QReadLocker otherLocker{&arrayData.m_Lock};
82 m_Data[0] += arrayData.data();
93 m_Data[0] += arrayData.data();
83 }
94 }
84 }
95 }
85
96
86 template <int D = Dim, typename = std::enable_if_t<D == 1> >
97 template <int D = Dim, typename = std::enable_if_t<D == 1> >
87 int size() const
98 int size() const
88 {
99 {
89 QReadLocker locker{&m_Lock};
100 QReadLocker locker{&m_Lock};
90 return m_Data[0].size();
101 return m_Data[0].size();
91 }
102 }
92
103
93 void clear()
104 void clear()
94 {
105 {
95 QWriteLocker locker{&m_Lock};
106 QWriteLocker locker{&m_Lock};
96 m_Data.clear();
107 m_Data.clear();
97 }
108 }
98
109
99
110
100 private:
111 private:
101 QVector<QVector<double> > m_Data;
112 QVector<QVector<double> > m_Data;
102 mutable QReadWriteLock m_Lock;
113 mutable QReadWriteLock m_Lock;
103 };
114 };
104
115
105 #endif // SCIQLOP_ARRAYDATA_H
116 #endif // SCIQLOP_ARRAYDATA_H
@@ -1,63 +1,69
1 #ifndef SCIQLOP_IDATASERIES_H
1 #ifndef SCIQLOP_IDATASERIES_H
2 #define SCIQLOP_IDATASERIES_H
2 #define SCIQLOP_IDATASERIES_H
3
3
4 #include <Common/MetaTypes.h>
4 #include <Common/MetaTypes.h>
5
5
6 #include <memory>
6 #include <memory>
7
7
8 #include <QString>
8 #include <QString>
9
9
10 template <int Dim>
10 template <int Dim>
11 class ArrayData;
11 class ArrayData;
12
12
13 struct Unit {
13 struct Unit {
14 explicit Unit(const QString &name = {}, bool timeUnit = false)
14 explicit Unit(const QString &name = {}, bool timeUnit = false)
15 : m_Name{name}, m_TimeUnit{timeUnit}
15 : m_Name{name}, m_TimeUnit{timeUnit}
16 {
16 {
17 }
17 }
18
18
19 inline bool operator==(const Unit &other) const
20 {
21 return std::tie(m_Name, m_TimeUnit) == std::tie(other.m_Name, other.m_TimeUnit);
22 }
23 inline bool operator!=(const Unit &other) const { return !(*this == other); }
24
19 QString m_Name; ///< Unit name
25 QString m_Name; ///< Unit name
20 bool m_TimeUnit; ///< The unit is a unit of time
26 bool m_TimeUnit; ///< The unit is a unit of time
21 };
27 };
22
28
23 /**
29 /**
24 * @brief The IDataSeries aims to declare a data series.
30 * @brief The IDataSeries aims to declare a data series.
25 *
31 *
26 * A data series is an entity that contains at least :
32 * A data series is an entity that contains at least :
27 * - one dataset representing the x-axis
33 * - one dataset representing the x-axis
28 * - one dataset representing the values
34 * - one dataset representing the values
29 *
35 *
30 * Each dataset is represented by an ArrayData, and is associated with a unit.
36 * Each dataset is represented by an ArrayData, and is associated with a unit.
31 *
37 *
32 * An ArrayData can be unidimensional or two-dimensional, depending on the implementation of the
38 * An ArrayData can be unidimensional or two-dimensional, depending on the implementation of the
33 * IDataSeries. The x-axis dataset is always unidimensional.
39 * IDataSeries. The x-axis dataset is always unidimensional.
34 *
40 *
35 * @sa ArrayData
41 * @sa ArrayData
36 */
42 */
37 class IDataSeries {
43 class IDataSeries {
38 public:
44 public:
39 virtual ~IDataSeries() noexcept = default;
45 virtual ~IDataSeries() noexcept = default;
40
46
41 /// Returns the x-axis dataset
47 /// Returns the x-axis dataset
42 virtual std::shared_ptr<ArrayData<1> > xAxisData() = 0;
48 virtual std::shared_ptr<ArrayData<1> > xAxisData() = 0;
43
49
44 /// Returns the x-axis dataset (as const)
50 /// Returns the x-axis dataset (as const)
45 virtual const std::shared_ptr<ArrayData<1> > xAxisData() const = 0;
51 virtual const std::shared_ptr<ArrayData<1> > xAxisData() const = 0;
46
52
47 virtual Unit xAxisUnit() const = 0;
53 virtual Unit xAxisUnit() const = 0;
48
54
49 virtual Unit valuesUnit() const = 0;
55 virtual Unit valuesUnit() const = 0;
50
56
51 virtual void merge(IDataSeries *dataSeries) = 0;
57 virtual void merge(IDataSeries *dataSeries) = 0;
52
58
53 virtual std::unique_ptr<IDataSeries> clone() const = 0;
59 virtual std::unique_ptr<IDataSeries> clone() const = 0;
54
60
55 virtual void lockRead() = 0;
61 virtual void lockRead() = 0;
56 virtual void lockWrite() = 0;
62 virtual void lockWrite() = 0;
57 virtual void unlock() = 0;
63 virtual void unlock() = 0;
58 };
64 };
59
65
60 // Required for using shared_ptr in signals/slots
66 // Required for using shared_ptr in signals/slots
61 SCIQLOP_REGISTER_META_TYPE(IDATASERIES_PTR_REGISTRY, std::shared_ptr<IDataSeries>)
67 SCIQLOP_REGISTER_META_TYPE(IDATASERIES_PTR_REGISTRY, std::shared_ptr<IDataSeries>)
62
68
63 #endif // SCIQLOP_IDATASERIES_H
69 #endif // SCIQLOP_IDATASERIES_H
@@ -1,30 +1,39
1 #ifndef SCIQLOP_SCALARSERIES_H
1 #ifndef SCIQLOP_SCALARSERIES_H
2 #define SCIQLOP_SCALARSERIES_H
2 #define SCIQLOP_SCALARSERIES_H
3
3
4 #include <Data/DataSeries.h>
4 #include <Data/DataSeries.h>
5
5
6 /**
6 /**
7 * @brief The ScalarSeries class is the implementation for a data series representing a scalar.
7 * @brief The ScalarSeries class is the implementation for a data series representing a scalar.
8 */
8 */
9 class ScalarSeries : public DataSeries<1> {
9 class ScalarSeries : public DataSeries<1> {
10 public:
10 public:
11 /**
11 /**
12 * Ctor
12 * Ctor
13 * @param size the number of data the series will hold
13 * @param size the number of data the series will hold
14 * @param xAxisUnit x-axis unit
14 * @param xAxisUnit x-axis unit
15 * @param valuesUnit values unit
15 * @param valuesUnit values unit
16 */
16 */
17 explicit ScalarSeries(int size, const Unit &xAxisUnit, const Unit &valuesUnit);
17 explicit ScalarSeries(int size, const Unit &xAxisUnit, const Unit &valuesUnit);
18
18
19 /**
19 /**
20 * Ctor with two vectors. The vectors must have the same size, otherwise a ScalarSeries with no
21 * values will be created.
22 * @param xAxisData x-axis data
23 * @param valuesData values data
24 */
25 explicit ScalarSeries(QVector<double> xAxisData, QVector<double> valuesData,
26 const Unit &xAxisUnit, const Unit &valuesUnit);
27
28 /**
20 * Sets data for a specific index. The index has to be valid to be effective
29 * Sets data for a specific index. The index has to be valid to be effective
21 * @param index the index to which the data will be set
30 * @param index the index to which the data will be set
22 * @param x the x-axis data
31 * @param x the x-axis data
23 * @param value the value data
32 * @param value the value data
24 */
33 */
25 void setData(int index, double x, double value) noexcept;
34 void setData(int index, double x, double value) noexcept;
26
35
27 std::unique_ptr<IDataSeries> clone() const;
36 std::unique_ptr<IDataSeries> clone() const;
28 };
37 };
29
38
30 #endif // SCIQLOP_SCALARSERIES_H
39 #endif // SCIQLOP_SCALARSERIES_H
@@ -1,18 +1,25
1 #include <Data/ScalarSeries.h>
1 #include <Data/ScalarSeries.h>
2
2
3 ScalarSeries::ScalarSeries(int size, const Unit &xAxisUnit, const Unit &valuesUnit)
3 ScalarSeries::ScalarSeries(int size, const Unit &xAxisUnit, const Unit &valuesUnit)
4 : DataSeries{std::make_shared<ArrayData<1> >(size), xAxisUnit,
4 : DataSeries{std::make_shared<ArrayData<1> >(size), xAxisUnit,
5 std::make_shared<ArrayData<1> >(size), valuesUnit}
5 std::make_shared<ArrayData<1> >(size), valuesUnit}
6 {
6 {
7 }
7 }
8
8
9 ScalarSeries::ScalarSeries(QVector<double> xAxisData, QVector<double> valuesData,
10 const Unit &xAxisUnit, const Unit &valuesUnit)
11 : DataSeries{std::make_shared<ArrayData<1> >(std::move(xAxisData)), xAxisUnit,
12 std::make_shared<ArrayData<1> >(std::move(valuesData)), valuesUnit}
13 {
14 }
15
9 void ScalarSeries::setData(int index, double x, double value) noexcept
16 void ScalarSeries::setData(int index, double x, double value) noexcept
10 {
17 {
11 xAxisData()->setData(index, x);
18 xAxisData()->setData(index, x);
12 valuesData()->setData(index, value);
19 valuesData()->setData(index, value);
13 }
20 }
14
21
15 std::unique_ptr<IDataSeries> ScalarSeries::clone() const
22 std::unique_ptr<IDataSeries> ScalarSeries::clone() const
16 {
23 {
17 return std::make_unique<ScalarSeries>(*this);
24 return std::make_unique<ScalarSeries>(*this);
18 }
25 }
@@ -1,70 +1,132
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
8
8 Q_LOGGING_CATEGORY(LOG_AmdaResultParser, "AmdaResultParser")
9 Q_LOGGING_CATEGORY(LOG_AmdaResultParser, "AmdaResultParser")
9
10
10 namespace {
11 namespace {
11
12
12 /// Format for dates in result files
13 /// Format for dates in result files
13 const auto DATE_FORMAT = QStringLiteral("yyyy-MM-ddThh:mm:ss.zzz");
14 const auto DATE_FORMAT = QStringLiteral("yyyy-MM-ddThh:mm:ss.zzz");
14
15
15 /// @todo ALX
16 /// Separator between values in a result line
17 const auto RESULT_LINE_SEPARATOR = QRegularExpression{QStringLiteral("\\s+")};
18
19 /// Regex to find unit in a line. Examples of valid lines:
20 /// ... - Units : nT - ...
21 /// ... -Units:nT- ...
22 /// ... -Units: mΒ²- ...
23 /// ... - Units : m/s - ...
24 const auto UNIT_REGEX = QRegularExpression{QStringLiteral("-\\s*Units\\s*:\\s*(.+?)\\s*-")};
25
26 /// 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
16 double doubleDate(const QString &stringDate) noexcept
28 double doubleDate(const QString &stringDate) noexcept
17 {
29 {
18 auto dateTime = QDateTime::fromString(stringDate, DATE_FORMAT);
30 auto dateTime = QDateTime::fromString(stringDate, DATE_FORMAT);
19 return dateTime.toMSecsSinceEpoch() / 1000.;
31 return dateTime.isValid() ? (dateTime.toMSecsSinceEpoch() / 1000.)
32 : std::numeric_limits<double>::quiet_NaN();
20 }
33 }
21
34
22 } // namespace
35 /**
23
36 * Reads stream to retrieve x-axis unit
24 std::shared_ptr<IDataSeries> AmdaResultParser::readTxt(const QString &filePath) noexcept
37 * @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
39 * error occured during reading
40 */
41 Unit readXAxisUnit(QTextStream &stream)
25 {
42 {
26 QFile file{filePath};
43 QString line{};
27
44
28 if (!file.open(QFile::ReadOnly | QIODevice::Text)) {
45 if (stream.readLineInto(&line)) {
29 qCCritical(LOG_AmdaResultParser())
46 auto match = UNIT_REGEX.match(line);
30 << QObject::tr("Can't retrieve AMDA data from file %1: %2")
47 if (match.hasMatch()) {
31 .arg(filePath, file.errorString());
48 return Unit{match.captured(1), true};
32 return nullptr;
49 }
50 else {
51 qCWarning(LOG_AmdaResultParser())
52 << QObject::tr("Can't read unit: invalid line %1").arg(line);
53 }
54 }
55 else {
56 qCWarning(LOG_AmdaResultParser()) << QObject::tr("Can't read unit: end of file");
57 }
58
59 // Error cases
60 return Unit{{}, true};
33 }
61 }
34
62
63 /**
64 * Reads stream to retrieve results
65 * @param stream the stream to read
66 * @return the pair of vectors x-axis data/values data that has been read in the stream
67 */
68 QPair<QVector<double>, QVector<double> > readResults(QTextStream &stream)
69 {
35 auto xData = QVector<double>{};
70 auto xData = QVector<double>{};
36 auto valuesData = QVector<double>{};
71 auto valuesData = QVector<double>{};
37
72
38 QTextStream stream{&file};
39
40 // Ignore comment lines (3 lines)
41 stream.readLine();
42 stream.readLine();
43 stream.readLine();
44
45 QString line{};
73 QString line{};
46 auto lineRegex = QRegExp{QStringLiteral("\\s+")};
47 while (stream.readLineInto(&line)) {
74 while (stream.readLineInto(&line)) {
48 auto lineData = line.split(lineRegex, QString::SkipEmptyParts);
75 auto lineData = line.split(RESULT_LINE_SEPARATOR, QString::SkipEmptyParts);
49 if (lineData.size() == 2) {
76 if (lineData.size() == 2) {
50 // X : the data is converted from date to double (in secs)
77 // X : the data is converted from date to double (in secs)
51 xData.push_back(doubleDate(lineData.at(0)));
78 auto x = doubleDate(lineData.at(0));
52
79
53 // Value
80 // Value
54 valuesData.push_back(lineData.at(1).toDouble());
81 bool valueOk;
82 auto value = lineData.at(1).toDouble(&valueOk);
83
84 // Adds result only if x and value are valid
85 if (!std::isnan(x) && !std::isnan(value) && valueOk) {
86 xData.push_back(x);
87 valuesData.push_back(value);
88 }
89 else {
90 qCWarning(LOG_AmdaResultParser())
91 << QObject::tr(
92 "Can't retrieve results from line %1: x and/or value are invalid")
93 .arg(line);
94 }
55 }
95 }
56 else {
96 else {
57 /// @todo ALX : log
97 qCWarning(LOG_AmdaResultParser())
98 << QObject::tr("Can't retrieve results from line %1: invalid line").arg(line);
99 }
58 }
100 }
101
102 return qMakePair(std::move(xData), std::move(valuesData));
59 }
103 }
60
104
61 /// @todo ALX : handle units
105 } // namespace
62 auto scalarSeries = std::make_shared<ScalarSeries>(xData.size(), Unit{"nT", true}, Unit{});
106
107 std::shared_ptr<IDataSeries> AmdaResultParser::readTxt(const QString &filePath) noexcept
108 {
109 QFile file{filePath};
63
110
64 const auto count = xData.size();
111 if (!file.open(QFile::ReadOnly | QIODevice::Text)) {
65 for (auto i = 0; i < count; ++i) {
112 qCCritical(LOG_AmdaResultParser())
66 scalarSeries->setData(i, xData.at(i), valuesData.at(i));
113 << QObject::tr("Can't retrieve AMDA data from file %1: %2")
114 .arg(filePath, file.errorString());
115 return nullptr;
67 }
116 }
68
117
69 return scalarSeries;
118 QTextStream stream{&file};
119
120 // Ignore first two lines (comments lines)
121 stream.readLine();
122 stream.readLine();
123
124 // Reads x-axis unit
125 auto xAxisUnit = readXAxisUnit(stream);
126
127 // Reads results
128 auto results = readResults(stream);
129
130 return std::make_shared<ScalarSeries>(std::move(results.first), std::move(results.second),
131 xAxisUnit, Unit{});
70 }
132 }
General Comments 0
You need to be logged in to leave comments. Login now