##// END OF EJS Templates
Merge branch 'feature/VectorSeries' into develop
Alexandre Leroux -
r569:385fbf866df8 merge
parent child
Show More
@@ -0,0 +1,30
1 #ifndef SCIQLOP_VECTORSERIES_H
2 #define SCIQLOP_VECTORSERIES_H
3
4 #include "CoreGlobal.h"
5
6 #include <Data/DataSeries.h>
7
8 /**
9 * @brief The VectorSeries class is the implementation for a data series representing a vector.
10 */
11 class SCIQLOP_CORE_EXPORT VectorSeries : public DataSeries<2> {
12 public:
13 /**
14 * Ctor. The vectors must have the same size, otherwise a ScalarSeries with no values will be
15 * created.
16 * @param xAxisData x-axis data
17 * @param xvaluesData x-values data
18 * @param yvaluesData y-values data
19 * @param zvaluesData z-values data
20 */
21 explicit VectorSeries(QVector<double> xAxisData, QVector<double> xValuesData,
22 QVector<double> yValuesData, QVector<double> zValuesData,
23 const Unit &xAxisUnit, const Unit &valuesUnit);
24
25 std::unique_ptr<IDataSeries> clone() const;
26
27 std::shared_ptr<IDataSeries> subDataSeries(const SqpRange &range) override;
28 };
29
30 #endif // SCIQLOP_VECTORSERIES_H
@@ -0,0 +1,39
1 #include "Data/VectorSeries.h"
2
3 VectorSeries::VectorSeries(QVector<double> xAxisData, QVector<double> xValuesData,
4 QVector<double> yValuesData, QVector<double> zValuesData,
5 const Unit &xAxisUnit, const Unit &valuesUnit)
6 : DataSeries{std::make_shared<ArrayData<1> >(std::move(xAxisData)), xAxisUnit,
7 std::make_shared<ArrayData<2> >(QVector<QVector<double> >{
8 std::move(xValuesData), std::move(yValuesData), std::move(zValuesData)}),
9 valuesUnit}
10 {
11 }
12
13 std::unique_ptr<IDataSeries> VectorSeries::clone() const
14 {
15 return std::make_unique<VectorSeries>(*this);
16 }
17
18 std::shared_ptr<IDataSeries> VectorSeries::subDataSeries(const SqpRange &range)
19 {
20 auto subXAxisData = QVector<double>();
21 auto subXValuesData = QVector<double>();
22 auto subYValuesData = QVector<double>();
23 auto subZValuesData = QVector<double>();
24
25 this->lockRead();
26 {
27 auto bounds = subData(range.m_TStart, range.m_TEnd);
28 for (auto it = bounds.first; it != bounds.second; ++it) {
29 subXAxisData.append(it->x());
30 subXValuesData.append(it->value(0));
31 subYValuesData.append(it->value(1));
32 subZValuesData.append(it->value(2));
33 }
34 }
35 this->unlock();
36
37 return std::make_shared<VectorSeries>(subXAxisData, subXValuesData, subYValuesData,
38 subZValuesData, this->xAxisUnit(), this->valuesUnit());
39 }
@@ -0,0 +1,12
1 #Time Format : YYYY-MM-DDThh:mm:ss.mls
2 #imf - Type : Local Parameter @ CDPP/AMDA - Name : imf_gse - Units : nT - Size : 3 - Frame : GSE - Mission : ACE - Instrument : MFI - Dataset : mfi_final-prelim
3 2013-07-02T09:13:50.000 -0.332000 3.20600 0.0580000
4 2013-07-02T09:14:06.000 -1.01100 2.99900 0.496000
5 2013-07-02T09:14:22.000 -1.45700 2.78500 1.01800
6 2013-07-02T09:14:38.000 -1.29300 2.73600 1.48500
7 2013-07-02T09:14:54.000 -1.21700 2.61200 1.66200
8 2013-07-02T09:15:10.000 -1.44300 2.56400 1.50500
9 2013-07-02T09:15:26.000 -1.27800 2.89200 1.16800
10 2013-07-02T09:15:42.000 -1.20200 2.86200 1.24400
11 2013-07-02T09:15:58.000 -1.22000 2.85900 1.15000
12 2013-07-02T09:16:14.000 -1.25900 2.76400 1.35800 No newline at end of file
@@ -1,298 +1,313
1 #ifndef SCIQLOP_ARRAYDATA_H
1 #ifndef SCIQLOP_ARRAYDATA_H
2 #define SCIQLOP_ARRAYDATA_H
2 #define SCIQLOP_ARRAYDATA_H
3
3
4 #include <Common/SortUtils.h>
4 #include <Common/SortUtils.h>
5
5
6 #include <QReadLocker>
6 #include <QReadLocker>
7 #include <QReadWriteLock>
7 #include <QReadWriteLock>
8 #include <QVector>
8 #include <QVector>
9
9
10 #include <memory>
10 #include <memory>
11
11
12 template <int Dim>
12 template <int Dim>
13 class ArrayData;
13 class ArrayData;
14
14
15 using DataContainer = QVector<QVector<double> >;
15 using DataContainer = QVector<QVector<double> >;
16
16
17 namespace arraydata_detail {
17 namespace arraydata_detail {
18
18
19 /// Struct used to sort ArrayData
19 /// Struct used to sort ArrayData
20 template <int Dim>
20 template <int Dim>
21 struct Sort {
21 struct Sort {
22 static std::shared_ptr<ArrayData<Dim> > sort(const DataContainer &data,
22 static std::shared_ptr<ArrayData<Dim> > sort(const DataContainer &data,
23 const std::vector<int> &sortPermutation)
23 const std::vector<int> &sortPermutation)
24 {
24 {
25 auto nbComponents = data.size();
25 auto nbComponents = data.size();
26 auto sortedData = DataContainer(nbComponents);
26 auto sortedData = DataContainer(nbComponents);
27
27
28 for (auto i = 0; i < nbComponents; ++i) {
28 for (auto i = 0; i < nbComponents; ++i) {
29 sortedData[i] = SortUtils::sort(data.at(i), sortPermutation);
29 sortedData[i] = SortUtils::sort(data.at(i), sortPermutation);
30 }
30 }
31
31
32 return std::make_shared<ArrayData<Dim> >(std::move(sortedData));
32 return std::make_shared<ArrayData<Dim> >(std::move(sortedData));
33 }
33 }
34 };
34 };
35
35
36 /// Specialization for uni-dimensional ArrayData
36 /// Specialization for uni-dimensional ArrayData
37 template <>
37 template <>
38 struct Sort<1> {
38 struct Sort<1> {
39 static std::shared_ptr<ArrayData<1> > sort(const DataContainer &data,
39 static std::shared_ptr<ArrayData<1> > sort(const DataContainer &data,
40 const std::vector<int> &sortPermutation)
40 const std::vector<int> &sortPermutation)
41 {
41 {
42 return std::make_shared<ArrayData<1> >(SortUtils::sort(data.at(0), sortPermutation));
42 return std::make_shared<ArrayData<1> >(SortUtils::sort(data.at(0), sortPermutation));
43 }
43 }
44 };
44 };
45
45
46 } // namespace arraydata_detail
46 } // namespace arraydata_detail
47
47
48 /**
48 /**
49 * @brief The ArrayData class represents a dataset for a data series.
49 * @brief The ArrayData class represents a dataset for a data series.
50 *
50 *
51 * A dataset can be unidimensional or two-dimensional. This property is determined by the Dim
51 * A dataset can be unidimensional or two-dimensional. This property is determined by the Dim
52 * template-parameter. In a case of a two-dimensional dataset, each dataset component has the same
52 * template-parameter. In a case of a two-dimensional dataset, each dataset component has the same
53 * number of values
53 * number of values
54 *
54 *
55 * @tparam Dim the dimension of the ArrayData (one or two)
55 * @tparam Dim the dimension of the ArrayData (one or two)
56 * @sa IDataSeries
56 * @sa IDataSeries
57 */
57 */
58 template <int Dim>
58 template <int Dim>
59 class ArrayData {
59 class ArrayData {
60 public:
60 public:
61 class IteratorValue {
61 class IteratorValue {
62 public:
62 public:
63 explicit IteratorValue(const DataContainer &container, bool begin) : m_Its{}
63 explicit IteratorValue(const DataContainer &container, bool begin) : m_Its{}
64 {
64 {
65 for (auto i = 0; i < container.size(); ++i) {
65 for (auto i = 0; i < container.size(); ++i) {
66 m_Its.push_back(begin ? container.at(i).cbegin() : container.at(i).cend());
66 m_Its.push_back(begin ? container.at(i).cbegin() : container.at(i).cend());
67 }
67 }
68 }
68 }
69
69
70 double at(int index) const { return *m_Its.at(index); }
70 double at(int index) const { return *m_Its.at(index); }
71 double first() const { return *m_Its.front(); }
71 double first() const { return *m_Its.front(); }
72
72
73 void next()
73 void next()
74 {
74 {
75 for (auto &it : m_Its) {
75 for (auto &it : m_Its) {
76 ++it;
76 ++it;
77 }
77 }
78 }
78 }
79
79
80 bool operator==(const IteratorValue &other) const { return m_Its == other.m_Its; }
80 bool operator==(const IteratorValue &other) const { return m_Its == other.m_Its; }
81
81
82 private:
82 private:
83 std::vector<DataContainer::value_type::const_iterator> m_Its;
83 std::vector<DataContainer::value_type::const_iterator> m_Its;
84 };
84 };
85
85
86 class Iterator {
86 class Iterator {
87 public:
87 public:
88 using iterator_category = std::forward_iterator_tag;
88 using iterator_category = std::forward_iterator_tag;
89 using value_type = const IteratorValue;
89 using value_type = const IteratorValue;
90 using difference_type = std::ptrdiff_t;
90 using difference_type = std::ptrdiff_t;
91 using pointer = value_type *;
91 using pointer = value_type *;
92 using reference = value_type &;
92 using reference = value_type &;
93
93
94 Iterator(const DataContainer &container, bool begin) : m_CurrentValue{container, begin} {}
94 Iterator(const DataContainer &container, bool begin) : m_CurrentValue{container, begin} {}
95
95
96 virtual ~Iterator() noexcept = default;
96 virtual ~Iterator() noexcept = default;
97 Iterator(const Iterator &) = default;
97 Iterator(const Iterator &) = default;
98 Iterator(Iterator &&) = default;
98 Iterator(Iterator &&) = default;
99 Iterator &operator=(const Iterator &) = default;
99 Iterator &operator=(const Iterator &) = default;
100 Iterator &operator=(Iterator &&) = default;
100 Iterator &operator=(Iterator &&) = default;
101
101
102 Iterator &operator++()
102 Iterator &operator++()
103 {
103 {
104 m_CurrentValue.next();
104 m_CurrentValue.next();
105 return *this;
105 return *this;
106 }
106 }
107
107
108 pointer operator->() const { return &m_CurrentValue; }
108 pointer operator->() const { return &m_CurrentValue; }
109 reference operator*() const { return m_CurrentValue; }
109 reference operator*() const { return m_CurrentValue; }
110
110
111 bool operator==(const Iterator &other) const
111 bool operator==(const Iterator &other) const
112 {
112 {
113 return m_CurrentValue == other.m_CurrentValue;
113 return m_CurrentValue == other.m_CurrentValue;
114 }
114 }
115
115
116 bool operator!=(const Iterator &other) const { return !(*this == other); }
116 bool operator!=(const Iterator &other) const { return !(*this == other); }
117
117
118 private:
118 private:
119 IteratorValue m_CurrentValue;
119 IteratorValue m_CurrentValue;
120 };
120 };
121
121
122 // ///// //
122 // ///// //
123 // Ctors //
123 // Ctors //
124 // ///// //
124 // ///// //
125
125
126 /**
126 /**
127 * Ctor for a unidimensional ArrayData
127 * Ctor for a unidimensional ArrayData
128 * @param data the data the ArrayData will hold
128 * @param data the data the ArrayData will hold
129 */
129 */
130 template <int D = Dim, typename = std::enable_if_t<D == 1> >
130 template <int D = Dim, typename = std::enable_if_t<D == 1> >
131 explicit ArrayData(QVector<double> data) : m_Data{1, QVector<double>{}}
131 explicit ArrayData(QVector<double> data) : m_Data{1, QVector<double>{}}
132 {
132 {
133 m_Data[0] = std::move(data);
133 m_Data[0] = std::move(data);
134 }
134 }
135
135
136 /**
136 /**
137 * Ctor for a two-dimensional ArrayData. The number of components (number of vectors) must be
137 * Ctor for a two-dimensional ArrayData. The number of components (number of vectors) must be
138 * greater than 2 and each component must have the same number of values
138 * greater than 2 and each component must have the same number of values
139 * @param data the data the ArrayData will hold
139 * @param data the data the ArrayData will hold
140 * @throws std::invalid_argument if the number of components is less than 2
140 * @throws std::invalid_argument if the number of components is less than 2
141 * @remarks if the number of values is not the same for each component, no value is set
141 * @remarks if the number of values is not the same for each component, no value is set
142 */
142 */
143 template <int D = Dim, typename = std::enable_if_t<D == 2> >
143 template <int D = Dim, typename = std::enable_if_t<D == 2> >
144 explicit ArrayData(DataContainer data)
144 explicit ArrayData(DataContainer data)
145 {
145 {
146 auto nbComponents = data.size();
146 auto nbComponents = data.size();
147 if (nbComponents < 2) {
147 if (nbComponents < 2) {
148 throw std::invalid_argument{
148 throw std::invalid_argument{
149 QString{"A multidimensional ArrayData must have at least 2 components (found: %1"}
149 QString{"A multidimensional ArrayData must have at least 2 components (found: %1"}
150 .arg(data.size())
150 .arg(data.size())
151 .toStdString()};
151 .toStdString()};
152 }
152 }
153
153
154 auto nbValues = data.front().size();
154 auto nbValues = data.front().size();
155 if (std::all_of(data.cbegin(), data.cend(), [nbValues](const auto &component) {
155 if (std::all_of(data.cbegin(), data.cend(), [nbValues](const auto &component) {
156 return component.size() == nbValues;
156 return component.size() == nbValues;
157 })) {
157 })) {
158 m_Data = std::move(data);
158 m_Data = std::move(data);
159 }
159 }
160 else {
160 else {
161 m_Data = DataContainer{nbComponents, QVector<double>{}};
161 m_Data = DataContainer{nbComponents, QVector<double>{}};
162 }
162 }
163 }
163 }
164
164
165 /// Copy ctor
165 /// Copy ctor
166 explicit ArrayData(const ArrayData &other)
166 explicit ArrayData(const ArrayData &other)
167 {
167 {
168 QReadLocker otherLocker{&other.m_Lock};
168 QReadLocker otherLocker{&other.m_Lock};
169 m_Data = other.m_Data;
169 m_Data = other.m_Data;
170 }
170 }
171
171
172 // /////////////// //
172 // /////////////// //
173 // General methods //
173 // General methods //
174 // /////////////// //
174 // /////////////// //
175
175
176 /**
176 /**
177 * Merges into the array data an other array data. The two array datas must have the same number
177 * Merges into the array data an other array data. The two array datas must have the same number
178 * of components so the merge can be done
178 * of components so the merge can be done
179 * @param other the array data to merge with
179 * @param other the array data to merge with
180 * @param prepend if true, the other array data is inserted at the beginning, otherwise it is
180 * @param prepend if true, the other array data is inserted at the beginning, otherwise it is
181 * inserted at the end
181 * inserted at the end
182 */
182 */
183 void add(const ArrayData<Dim> &other, bool prepend = false)
183 void add(const ArrayData<Dim> &other, bool prepend = false)
184 {
184 {
185 QWriteLocker locker{&m_Lock};
185 QWriteLocker locker{&m_Lock};
186 QReadLocker otherLocker{&other.m_Lock};
186 QReadLocker otherLocker{&other.m_Lock};
187
187
188 auto nbComponents = m_Data.size();
188 auto nbComponents = m_Data.size();
189 if (nbComponents != other.m_Data.size()) {
189 if (nbComponents != other.m_Data.size()) {
190 return;
190 return;
191 }
191 }
192
192
193 for (auto componentIndex = 0; componentIndex < nbComponents; ++componentIndex) {
193 for (auto componentIndex = 0; componentIndex < nbComponents; ++componentIndex) {
194 if (prepend) {
194 if (prepend) {
195 const auto &otherData = other.data(componentIndex);
195 const auto &otherData = other.data(componentIndex);
196 const auto otherDataSize = otherData.size();
196 const auto otherDataSize = otherData.size();
197
197
198 auto &data = m_Data[componentIndex];
198 auto &data = m_Data[componentIndex];
199 data.insert(data.begin(), otherDataSize, 0.);
199 data.insert(data.begin(), otherDataSize, 0.);
200
200
201 for (auto i = 0; i < otherDataSize; ++i) {
201 for (auto i = 0; i < otherDataSize; ++i) {
202 data.replace(i, otherData.at(i));
202 data.replace(i, otherData.at(i));
203 }
203 }
204 }
204 }
205 else {
205 else {
206 m_Data[componentIndex] += other.data(componentIndex);
206 m_Data[componentIndex] += other.data(componentIndex);
207 }
207 }
208 }
208 }
209 }
209 }
210
210
211 void clear()
211 void clear()
212 {
212 {
213 QWriteLocker locker{&m_Lock};
213 QWriteLocker locker{&m_Lock};
214
214
215 auto nbComponents = m_Data.size();
215 auto nbComponents = m_Data.size();
216 for (auto i = 0; i < nbComponents; ++i) {
216 for (auto i = 0; i < nbComponents; ++i) {
217 m_Data[i].clear();
217 m_Data[i].clear();
218 }
218 }
219 }
219 }
220
220
221 /**
221 /**
222 * @return the data of a component
222 * @return the data of a component
223 * @param componentIndex the index of the component to retrieve the data
223 * @param componentIndex the index of the component to retrieve the data
224 * @return the component's data, empty vector if the index is invalid
224 * @return the component's data, empty vector if the index is invalid
225 */
225 */
226 QVector<double> data(int componentIndex) const noexcept
226 QVector<double> data(int componentIndex) const noexcept
227 {
227 {
228 QReadLocker locker{&m_Lock};
228 QReadLocker locker{&m_Lock};
229
229
230 return (componentIndex >= 0 && componentIndex < m_Data.size()) ? m_Data.at(componentIndex)
230 return (componentIndex >= 0 && componentIndex < m_Data.size()) ? m_Data.at(componentIndex)
231 : QVector<double>{};
231 : QVector<double>{};
232 }
232 }
233
233
234 /// @return the size (i.e. number of values) of a single component
234 /// @return the size (i.e. number of values) of a single component
235 /// @remarks in a case of a two-dimensional ArrayData, each component has the same size
235 /// @remarks in a case of a two-dimensional ArrayData, each component has the same size
236 int size() const
236 int size() const
237 {
237 {
238 QReadLocker locker{&m_Lock};
238 QReadLocker locker{&m_Lock};
239 return m_Data[0].size();
239 return m_Data[0].size();
240 }
240 }
241
241
242 std::shared_ptr<ArrayData<Dim> > sort(const std::vector<int> &sortPermutation)
242 std::shared_ptr<ArrayData<Dim> > sort(const std::vector<int> &sortPermutation)
243 {
243 {
244 QReadLocker locker{&m_Lock};
244 QReadLocker locker{&m_Lock};
245 return arraydata_detail::Sort<Dim>::sort(m_Data, sortPermutation);
245 return arraydata_detail::Sort<Dim>::sort(m_Data, sortPermutation);
246 }
246 }
247
247
248 // ///////// //
248 // ///////// //
249 // Iterators //
249 // Iterators //
250 // ///////// //
250 // ///////// //
251
251
252 Iterator cbegin() const { return Iterator{m_Data, true}; }
252 Iterator cbegin() const { return Iterator{m_Data, true}; }
253 Iterator cend() const { return Iterator{m_Data, false}; }
253 Iterator cend() const { return Iterator{m_Data, false}; }
254
254
255 // ///////////// //
255 // ///////////// //
256 // 1-dim methods //
256 // 1-dim methods //
257 // ///////////// //
257 // ///////////// //
258
258
259 /**
259 /**
260 * @return the data at a specified index
260 * @return the data at a specified index
261 * @remarks index must be a valid position
261 * @remarks index must be a valid position
262 * @remarks this method is only available for a unidimensional ArrayData
262 * @remarks this method is only available for a unidimensional ArrayData
263 */
263 */
264 template <int D = Dim, typename = std::enable_if_t<D == 1> >
264 template <int D = Dim, typename = std::enable_if_t<D == 1> >
265 double at(int index) const noexcept
265 double at(int index) const noexcept
266 {
266 {
267 QReadLocker locker{&m_Lock};
267 QReadLocker locker{&m_Lock};
268 return m_Data[0].at(index);
268 return m_Data[0].at(index);
269 }
269 }
270
270
271 /**
271 /**
272 * @return the data as a vector, as a const reference
272 * @return the data as a vector, as a const reference
273 * @remarks this method is only available for a unidimensional ArrayData
273 * @remarks this method is only available for a unidimensional ArrayData
274 */
274 */
275 template <int D = Dim, typename = std::enable_if_t<D == 1> >
275 template <int D = Dim, typename = std::enable_if_t<D == 1> >
276 const QVector<double> &cdata() const noexcept
276 const QVector<double> &cdata() const noexcept
277 {
277 {
278 QReadLocker locker{&m_Lock};
278 QReadLocker locker{&m_Lock};
279 return m_Data.at(0);
279 return m_Data.at(0);
280 }
280 }
281
281
282 /**
282 /**
283 * @return the data as a vector
283 * @return the data as a vector
284 * @remarks this method is only available for a unidimensional ArrayData
284 * @remarks this method is only available for a unidimensional ArrayData
285 */
285 */
286 template <int D = Dim, typename = std::enable_if_t<D == 1> >
286 template <int D = Dim, typename = std::enable_if_t<D == 1> >
287 QVector<double> data() const noexcept
287 QVector<double> data() const noexcept
288 {
288 {
289 QReadLocker locker{&m_Lock};
289 QReadLocker locker{&m_Lock};
290 return m_Data[0];
290 return m_Data[0];
291 }
291 }
292
292
293 // ///////////// //
294 // 2-dim methods //
295 // ///////////// //
296
297 /**
298 * @return the data
299 * @remarks this method is only available for a two-dimensional ArrayData
300 */
301 template <int D = Dim, typename = std::enable_if_t<D == 2> >
302 DataContainer data() const noexcept
303 {
304 QReadLocker locker{&m_Lock};
305 return m_Data;
306 }
307
293 private:
308 private:
294 DataContainer m_Data;
309 DataContainer m_Data;
295 mutable QReadWriteLock m_Lock;
310 mutable QReadWriteLock m_Lock;
296 };
311 };
297
312
298 #endif // SCIQLOP_ARRAYDATA_H
313 #endif // SCIQLOP_ARRAYDATA_H
@@ -1,288 +1,293
1 #ifndef SCIQLOP_DATASERIES_H
1 #ifndef SCIQLOP_DATASERIES_H
2 #define SCIQLOP_DATASERIES_H
2 #define SCIQLOP_DATASERIES_H
3
3
4 #include "CoreGlobal.h"
5
4 #include <Common/SortUtils.h>
6 #include <Common/SortUtils.h>
5
7
6 #include <Data/ArrayData.h>
8 #include <Data/ArrayData.h>
7 #include <Data/IDataSeries.h>
9 #include <Data/IDataSeries.h>
8
10
9 #include <QLoggingCategory>
11 #include <QLoggingCategory>
10
11 #include <QReadLocker>
12 #include <QReadLocker>
12 #include <QReadWriteLock>
13 #include <QReadWriteLock>
13 #include <memory>
14 #include <memory>
14
15
15 Q_DECLARE_LOGGING_CATEGORY(LOG_DataSeries)
16 // We don't use the Qt macro since the log is used in the header file, which causes multiple log
16 Q_LOGGING_CATEGORY(LOG_DataSeries, "DataSeries")
17 // definitions with inheritance. Inline method is used instead
17
18 inline const QLoggingCategory &LOG_DataSeries()
19 {
20 static const QLoggingCategory category{"DataSeries"};
21 return category;
22 }
18
23
19 /**
24 /**
20 * @brief The DataSeries class is the base (abstract) implementation of IDataSeries.
25 * @brief The DataSeries class is the base (abstract) implementation of IDataSeries.
21 *
26 *
22 * It proposes to set a dimension for the values ​​data.
27 * It proposes to set a dimension for the values ​​data.
23 *
28 *
24 * A DataSeries is always sorted on its x-axis data.
29 * A DataSeries is always sorted on its x-axis data.
25 *
30 *
26 * @tparam Dim The dimension of the values data
31 * @tparam Dim The dimension of the values data
27 *
32 *
28 */
33 */
29 template <int Dim>
34 template <int Dim>
30 class DataSeries : public IDataSeries {
35 class SCIQLOP_CORE_EXPORT DataSeries : public IDataSeries {
31 public:
36 public:
32 class IteratorValue {
37 class IteratorValue {
33 public:
38 public:
34 explicit IteratorValue(const DataSeries &dataSeries, bool begin)
39 explicit IteratorValue(const DataSeries &dataSeries, bool begin)
35 : m_XIt(begin ? dataSeries.xAxisData()->cbegin() : dataSeries.xAxisData()->cend()),
40 : m_XIt(begin ? dataSeries.xAxisData()->cbegin() : dataSeries.xAxisData()->cend()),
36 m_ValuesIt(begin ? dataSeries.valuesData()->cbegin()
41 m_ValuesIt(begin ? dataSeries.valuesData()->cbegin()
37 : dataSeries.valuesData()->cend())
42 : dataSeries.valuesData()->cend())
38 {
43 {
39 }
44 }
40
45
41 double x() const { return m_XIt->at(0); }
46 double x() const { return m_XIt->at(0); }
42 double value() const { return m_ValuesIt->at(0); }
47 double value() const { return m_ValuesIt->at(0); }
43 double value(int componentIndex) const { return m_ValuesIt->at(componentIndex); }
48 double value(int componentIndex) const { return m_ValuesIt->at(componentIndex); }
44
49
45 void next()
50 void next()
46 {
51 {
47 ++m_XIt;
52 ++m_XIt;
48 ++m_ValuesIt;
53 ++m_ValuesIt;
49 }
54 }
50
55
51 bool operator==(const IteratorValue &other) const
56 bool operator==(const IteratorValue &other) const
52 {
57 {
53 return std::tie(m_XIt, m_ValuesIt) == std::tie(other.m_XIt, other.m_ValuesIt);
58 return std::tie(m_XIt, m_ValuesIt) == std::tie(other.m_XIt, other.m_ValuesIt);
54 }
59 }
55
60
56 private:
61 private:
57 ArrayData<1>::Iterator m_XIt;
62 ArrayData<1>::Iterator m_XIt;
58 typename ArrayData<Dim>::Iterator m_ValuesIt;
63 typename ArrayData<Dim>::Iterator m_ValuesIt;
59 };
64 };
60
65
61 class Iterator {
66 class Iterator {
62 public:
67 public:
63 using iterator_category = std::forward_iterator_tag;
68 using iterator_category = std::forward_iterator_tag;
64 using value_type = const IteratorValue;
69 using value_type = const IteratorValue;
65 using difference_type = std::ptrdiff_t;
70 using difference_type = std::ptrdiff_t;
66 using pointer = value_type *;
71 using pointer = value_type *;
67 using reference = value_type &;
72 using reference = value_type &;
68
73
69 Iterator(const DataSeries &dataSeries, bool begin) : m_CurrentValue{dataSeries, begin} {}
74 Iterator(const DataSeries &dataSeries, bool begin) : m_CurrentValue{dataSeries, begin} {}
70 virtual ~Iterator() noexcept = default;
75 virtual ~Iterator() noexcept = default;
71 Iterator(const Iterator &) = default;
76 Iterator(const Iterator &) = default;
72 Iterator(Iterator &&) = default;
77 Iterator(Iterator &&) = default;
73 Iterator &operator=(const Iterator &) = default;
78 Iterator &operator=(const Iterator &) = default;
74 Iterator &operator=(Iterator &&) = default;
79 Iterator &operator=(Iterator &&) = default;
75
80
76 Iterator &operator++()
81 Iterator &operator++()
77 {
82 {
78 m_CurrentValue.next();
83 m_CurrentValue.next();
79 return *this;
84 return *this;
80 }
85 }
81
86
82 pointer operator->() const { return &m_CurrentValue; }
87 pointer operator->() const { return &m_CurrentValue; }
83
88
84 reference operator*() const { return m_CurrentValue; }
89 reference operator*() const { return m_CurrentValue; }
85
90
86 bool operator==(const Iterator &other) const
91 bool operator==(const Iterator &other) const
87 {
92 {
88 return m_CurrentValue == other.m_CurrentValue;
93 return m_CurrentValue == other.m_CurrentValue;
89 }
94 }
90
95
91 bool operator!=(const Iterator &other) const { return !(*this == other); }
96 bool operator!=(const Iterator &other) const { return !(*this == other); }
92
97
93 private:
98 private:
94 IteratorValue m_CurrentValue;
99 IteratorValue m_CurrentValue;
95 };
100 };
96
101
97 /// @sa IDataSeries::xAxisData()
102 /// @sa IDataSeries::xAxisData()
98 std::shared_ptr<ArrayData<1> > xAxisData() override { return m_XAxisData; }
103 std::shared_ptr<ArrayData<1> > xAxisData() override { return m_XAxisData; }
99 const std::shared_ptr<ArrayData<1> > xAxisData() const { return m_XAxisData; }
104 const std::shared_ptr<ArrayData<1> > xAxisData() const { return m_XAxisData; }
100
105
101 /// @sa IDataSeries::xAxisUnit()
106 /// @sa IDataSeries::xAxisUnit()
102 Unit xAxisUnit() const override { return m_XAxisUnit; }
107 Unit xAxisUnit() const override { return m_XAxisUnit; }
103
108
104 /// @return the values dataset
109 /// @return the values dataset
105 std::shared_ptr<ArrayData<Dim> > valuesData() { return m_ValuesData; }
110 std::shared_ptr<ArrayData<Dim> > valuesData() { return m_ValuesData; }
106 const std::shared_ptr<ArrayData<Dim> > valuesData() const { return m_ValuesData; }
111 const std::shared_ptr<ArrayData<Dim> > valuesData() const { return m_ValuesData; }
107
112
108 /// @sa IDataSeries::valuesUnit()
113 /// @sa IDataSeries::valuesUnit()
109 Unit valuesUnit() const override { return m_ValuesUnit; }
114 Unit valuesUnit() const override { return m_ValuesUnit; }
110
115
111
116
112 SqpRange range() const override
117 SqpRange range() const override
113 {
118 {
114 if (!m_XAxisData->cdata().isEmpty()) {
119 if (!m_XAxisData->cdata().isEmpty()) {
115 return SqpRange{m_XAxisData->cdata().first(), m_XAxisData->cdata().last()};
120 return SqpRange{m_XAxisData->cdata().first(), m_XAxisData->cdata().last()};
116 }
121 }
117
122
118 return SqpRange{};
123 return SqpRange{};
119 }
124 }
120
125
121 void clear()
126 void clear()
122 {
127 {
123 m_XAxisData->clear();
128 m_XAxisData->clear();
124 m_ValuesData->clear();
129 m_ValuesData->clear();
125 }
130 }
126
131
127 /// Merges into the data series an other data series
132 /// Merges into the data series an other data series
128 /// @remarks the data series to merge with is cleared after the operation
133 /// @remarks the data series to merge with is cleared after the operation
129 void merge(IDataSeries *dataSeries) override
134 void merge(IDataSeries *dataSeries) override
130 {
135 {
131 dataSeries->lockWrite();
136 dataSeries->lockWrite();
132 lockWrite();
137 lockWrite();
133
138
134 if (auto other = dynamic_cast<DataSeries<Dim> *>(dataSeries)) {
139 if (auto other = dynamic_cast<DataSeries<Dim> *>(dataSeries)) {
135 const auto &otherXAxisData = other->xAxisData()->cdata();
140 const auto &otherXAxisData = other->xAxisData()->cdata();
136 const auto &xAxisData = m_XAxisData->cdata();
141 const auto &xAxisData = m_XAxisData->cdata();
137
142
138 // As data series are sorted, we can improve performances of merge, by call the sort
143 // As data series are sorted, we can improve performances of merge, by call the sort
139 // method only if the two data series overlap.
144 // method only if the two data series overlap.
140 if (!otherXAxisData.empty()) {
145 if (!otherXAxisData.empty()) {
141 auto firstValue = otherXAxisData.front();
146 auto firstValue = otherXAxisData.front();
142 auto lastValue = otherXAxisData.back();
147 auto lastValue = otherXAxisData.back();
143
148
144 auto xAxisDataBegin = xAxisData.cbegin();
149 auto xAxisDataBegin = xAxisData.cbegin();
145 auto xAxisDataEnd = xAxisData.cend();
150 auto xAxisDataEnd = xAxisData.cend();
146
151
147 bool prepend;
152 bool prepend;
148 bool sortNeeded;
153 bool sortNeeded;
149
154
150 if (std::lower_bound(xAxisDataBegin, xAxisDataEnd, firstValue) == xAxisDataEnd) {
155 if (std::lower_bound(xAxisDataBegin, xAxisDataEnd, firstValue) == xAxisDataEnd) {
151 // Other data series if after data series
156 // Other data series if after data series
152 prepend = false;
157 prepend = false;
153 sortNeeded = false;
158 sortNeeded = false;
154 }
159 }
155 else if (std::upper_bound(xAxisDataBegin, xAxisDataEnd, lastValue)
160 else if (std::upper_bound(xAxisDataBegin, xAxisDataEnd, lastValue)
156 == xAxisDataBegin) {
161 == xAxisDataBegin) {
157 // Other data series if before data series
162 // Other data series if before data series
158 prepend = true;
163 prepend = true;
159 sortNeeded = false;
164 sortNeeded = false;
160 }
165 }
161 else {
166 else {
162 // The two data series overlap
167 // The two data series overlap
163 prepend = false;
168 prepend = false;
164 sortNeeded = true;
169 sortNeeded = true;
165 }
170 }
166
171
167 // Makes the merge
172 // Makes the merge
168 m_XAxisData->add(*other->xAxisData(), prepend);
173 m_XAxisData->add(*other->xAxisData(), prepend);
169 m_ValuesData->add(*other->valuesData(), prepend);
174 m_ValuesData->add(*other->valuesData(), prepend);
170
175
171 if (sortNeeded) {
176 if (sortNeeded) {
172 sort();
177 sort();
173 }
178 }
174 }
179 }
175
180
176 // Clears the other data series
181 // Clears the other data series
177 other->clear();
182 other->clear();
178 }
183 }
179 else {
184 else {
180 qCWarning(LOG_DataSeries())
185 qCWarning(LOG_DataSeries())
181 << QObject::tr("Detection of a type of IDataSeries we cannot merge with !");
186 << QObject::tr("Detection of a type of IDataSeries we cannot merge with !");
182 }
187 }
183 unlock();
188 unlock();
184 dataSeries->unlock();
189 dataSeries->unlock();
185 }
190 }
186
191
187 // ///////// //
192 // ///////// //
188 // Iterators //
193 // Iterators //
189 // ///////// //
194 // ///////// //
190
195
191 Iterator cbegin() const { return Iterator{*this, true}; }
196 Iterator cbegin() const { return Iterator{*this, true}; }
192
197
193 Iterator cend() const { return Iterator{*this, false}; }
198 Iterator cend() const { return Iterator{*this, false}; }
194
199
195 std::pair<Iterator, Iterator> subData(double min, double max) const
200 std::pair<Iterator, Iterator> subData(double min, double max) const
196 {
201 {
197 if (min > max) {
202 if (min > max) {
198 std::swap(min, max);
203 std::swap(min, max);
199 }
204 }
200
205
201 auto begin = cbegin();
206 auto begin = cbegin();
202 auto end = cend();
207 auto end = cend();
203
208
204 auto lowerIt
209 auto lowerIt
205 = std::lower_bound(begin, end, min, [](const auto &itValue, const auto &value) {
210 = std::lower_bound(begin, end, min, [](const auto &itValue, const auto &value) {
206 return itValue.x() == value;
211 return itValue.x() == value;
207 });
212 });
208 auto upperIt
213 auto upperIt
209 = std::upper_bound(begin, end, max, [](const auto &value, const auto &itValue) {
214 = std::upper_bound(begin, end, max, [](const auto &value, const auto &itValue) {
210 return itValue.x() == value;
215 return itValue.x() == value;
211 });
216 });
212
217
213 return std::make_pair(lowerIt, upperIt);
218 return std::make_pair(lowerIt, upperIt);
214 }
219 }
215
220
216 // /////// //
221 // /////// //
217 // Mutexes //
222 // Mutexes //
218 // /////// //
223 // /////// //
219
224
220 virtual void lockRead() { m_Lock.lockForRead(); }
225 virtual void lockRead() { m_Lock.lockForRead(); }
221 virtual void lockWrite() { m_Lock.lockForWrite(); }
226 virtual void lockWrite() { m_Lock.lockForWrite(); }
222 virtual void unlock() { m_Lock.unlock(); }
227 virtual void unlock() { m_Lock.unlock(); }
223
228
224 protected:
229 protected:
225 /// Protected ctor (DataSeries is abstract). The vectors must have the same size, otherwise a
230 /// Protected ctor (DataSeries is abstract). The vectors must have the same size, otherwise a
226 /// DataSeries with no values will be created.
231 /// DataSeries with no values will be created.
227 /// @remarks data series is automatically sorted on its x-axis data
232 /// @remarks data series is automatically sorted on its x-axis data
228 explicit DataSeries(std::shared_ptr<ArrayData<1> > xAxisData, const Unit &xAxisUnit,
233 explicit DataSeries(std::shared_ptr<ArrayData<1> > xAxisData, const Unit &xAxisUnit,
229 std::shared_ptr<ArrayData<Dim> > valuesData, const Unit &valuesUnit)
234 std::shared_ptr<ArrayData<Dim> > valuesData, const Unit &valuesUnit)
230 : m_XAxisData{xAxisData},
235 : m_XAxisData{xAxisData},
231 m_XAxisUnit{xAxisUnit},
236 m_XAxisUnit{xAxisUnit},
232 m_ValuesData{valuesData},
237 m_ValuesData{valuesData},
233 m_ValuesUnit{valuesUnit}
238 m_ValuesUnit{valuesUnit}
234 {
239 {
235 if (m_XAxisData->size() != m_ValuesData->size()) {
240 if (m_XAxisData->size() != m_ValuesData->size()) {
236 clear();
241 clear();
237 }
242 }
238
243
239 // Sorts data if it's not the case
244 // Sorts data if it's not the case
240 const auto &xAxisCData = m_XAxisData->cdata();
245 const auto &xAxisCData = m_XAxisData->cdata();
241 if (!std::is_sorted(xAxisCData.cbegin(), xAxisCData.cend())) {
246 if (!std::is_sorted(xAxisCData.cbegin(), xAxisCData.cend())) {
242 sort();
247 sort();
243 }
248 }
244 }
249 }
245
250
246 /// Copy ctor
251 /// Copy ctor
247 explicit DataSeries(const DataSeries<Dim> &other)
252 explicit DataSeries(const DataSeries<Dim> &other)
248 : m_XAxisData{std::make_shared<ArrayData<1> >(*other.m_XAxisData)},
253 : m_XAxisData{std::make_shared<ArrayData<1> >(*other.m_XAxisData)},
249 m_XAxisUnit{other.m_XAxisUnit},
254 m_XAxisUnit{other.m_XAxisUnit},
250 m_ValuesData{std::make_shared<ArrayData<Dim> >(*other.m_ValuesData)},
255 m_ValuesData{std::make_shared<ArrayData<Dim> >(*other.m_ValuesData)},
251 m_ValuesUnit{other.m_ValuesUnit}
256 m_ValuesUnit{other.m_ValuesUnit}
252 {
257 {
253 // Since a series is ordered from its construction and is always ordered, it is not
258 // Since a series is ordered from its construction and is always ordered, it is not
254 // necessary to call the sort method here ('other' is sorted)
259 // necessary to call the sort method here ('other' is sorted)
255 }
260 }
256
261
257 /// Assignment operator
262 /// Assignment operator
258 template <int D>
263 template <int D>
259 DataSeries &operator=(DataSeries<D> other)
264 DataSeries &operator=(DataSeries<D> other)
260 {
265 {
261 std::swap(m_XAxisData, other.m_XAxisData);
266 std::swap(m_XAxisData, other.m_XAxisData);
262 std::swap(m_XAxisUnit, other.m_XAxisUnit);
267 std::swap(m_XAxisUnit, other.m_XAxisUnit);
263 std::swap(m_ValuesData, other.m_ValuesData);
268 std::swap(m_ValuesData, other.m_ValuesData);
264 std::swap(m_ValuesUnit, other.m_ValuesUnit);
269 std::swap(m_ValuesUnit, other.m_ValuesUnit);
265
270
266 return *this;
271 return *this;
267 }
272 }
268
273
269 private:
274 private:
270 /**
275 /**
271 * Sorts data series on its x-axis data
276 * Sorts data series on its x-axis data
272 */
277 */
273 void sort() noexcept
278 void sort() noexcept
274 {
279 {
275 auto permutation = SortUtils::sortPermutation(*m_XAxisData, std::less<double>());
280 auto permutation = SortUtils::sortPermutation(*m_XAxisData, std::less<double>());
276 m_XAxisData = m_XAxisData->sort(permutation);
281 m_XAxisData = m_XAxisData->sort(permutation);
277 m_ValuesData = m_ValuesData->sort(permutation);
282 m_ValuesData = m_ValuesData->sort(permutation);
278 }
283 }
279
284
280 std::shared_ptr<ArrayData<1> > m_XAxisData;
285 std::shared_ptr<ArrayData<1> > m_XAxisData;
281 Unit m_XAxisUnit;
286 Unit m_XAxisUnit;
282 std::shared_ptr<ArrayData<Dim> > m_ValuesData;
287 std::shared_ptr<ArrayData<Dim> > m_ValuesData;
283 Unit m_ValuesUnit;
288 Unit m_ValuesUnit;
284
289
285 QReadWriteLock m_Lock;
290 QReadWriteLock m_Lock;
286 };
291 };
287
292
288 #endif // SCIQLOP_DATASERIES_H
293 #endif // SCIQLOP_DATASERIES_H
@@ -1,41 +1,31
1 #include <Data/ScalarSeries.h>
1 #include <Data/ScalarSeries.h>
2
2
3 ScalarSeries::ScalarSeries(QVector<double> xAxisData, QVector<double> valuesData,
3 ScalarSeries::ScalarSeries(QVector<double> xAxisData, QVector<double> valuesData,
4 const Unit &xAxisUnit, const Unit &valuesUnit)
4 const Unit &xAxisUnit, const Unit &valuesUnit)
5 : DataSeries{std::make_shared<ArrayData<1> >(std::move(xAxisData)), xAxisUnit,
5 : DataSeries{std::make_shared<ArrayData<1> >(std::move(xAxisData)), xAxisUnit,
6 std::make_shared<ArrayData<1> >(std::move(valuesData)), valuesUnit}
6 std::make_shared<ArrayData<1> >(std::move(valuesData)), valuesUnit}
7 {
7 {
8 }
8 }
9
9
10 std::unique_ptr<IDataSeries> ScalarSeries::clone() const
10 std::unique_ptr<IDataSeries> ScalarSeries::clone() const
11 {
11 {
12 return std::make_unique<ScalarSeries>(*this);
12 return std::make_unique<ScalarSeries>(*this);
13 }
13 }
14
14
15 std::shared_ptr<IDataSeries> ScalarSeries::subDataSeries(const SqpRange &range)
15 std::shared_ptr<IDataSeries> ScalarSeries::subDataSeries(const SqpRange &range)
16 {
16 {
17 auto subXAxisData = QVector<double>();
17 auto subXAxisData = QVector<double>();
18 auto subValuesData = QVector<double>();
18 auto subValuesData = QVector<double>();
19 this->lockRead();
19 this->lockRead();
20 {
20 {
21 const auto &currentXData = this->xAxisData()->cdata();
21 auto bounds = subData(range.m_TStart, range.m_TEnd);
22 const auto &currentValuesData = this->valuesData()->cdata();
22 for (auto it = bounds.first; it != bounds.second; ++it) {
23
23 subXAxisData.append(it->x());
24 auto xDataBegin = currentXData.cbegin();
24 subValuesData.append(it->value());
25 auto xDataEnd = currentXData.cend();
26
27 auto lowerIt = std::lower_bound(xDataBegin, xDataEnd, range.m_TStart);
28 auto upperIt = std::upper_bound(xDataBegin, xDataEnd, range.m_TEnd);
29 auto distance = std::distance(xDataBegin, lowerIt);
30
31 auto valuesDataIt = currentValuesData.cbegin() + distance;
32 for (auto xAxisDataIt = lowerIt; xAxisDataIt != upperIt; ++xAxisDataIt, ++valuesDataIt) {
33 subXAxisData.append(*xAxisDataIt);
34 subValuesData.append(*valuesDataIt);
35 }
25 }
36 }
26 }
37 this->unlock();
27 this->unlock();
38
28
39 return std::make_shared<ScalarSeries>(subXAxisData, subValuesData, this->xAxisUnit(),
29 return std::make_shared<ScalarSeries>(subXAxisData, subValuesData, this->xAxisUnit(),
40 this->valuesUnit());
30 this->valuesUnit());
41 }
31 }
@@ -1,44 +1,45
1 # On ignore toutes les règles vera++ pour le fichier spimpl
1 # On ignore toutes les règles vera++ pour le fichier spimpl
2 Common/spimpl\.h:\d+:.*
2 Common/spimpl\.h:\d+:.*
3
3
4 # Ignore false positive relative to two class definitions in a same file
4 # Ignore false positive relative to two class definitions in a same file
5 DataSourceItem\.h:\d+:.*IPSIS_S01.*
5 DataSourceItem\.h:\d+:.*IPSIS_S01.*
6
6
7 # Ignore false positive relative to a template class
7 # Ignore false positive relative to a template class
8 ArrayData\.h:\d+:.*IPSIS_S04_VARIABLE.*found: (D)
8 ArrayData\.h:\d+:.*IPSIS_S04_VARIABLE.*found: (D)
9 ArrayData\.h:\d+:.*IPSIS_S04_NAMESPACE.*found: (arraydata_detail)
9 ArrayData\.h:\d+:.*IPSIS_S04_NAMESPACE.*found: (arraydata_detail)
10 ArrayData\.h:\d+:.*IPSIS_S06.*found: (D)
10 ArrayData\.h:\d+:.*IPSIS_S06.*found: (D)
11 ArrayData\.h:\d+:.*IPSIS_S06.*found: (Dim)
11 ArrayData\.h:\d+:.*IPSIS_S06.*found: (Dim)
12 DataSeries\.h:\d+:.*IPSIS_S04_METHOD.*found: LOG_DataSeries
12 DataSeries\.h:\d+:.*IPSIS_S04_VARIABLE.*
13 DataSeries\.h:\d+:.*IPSIS_S04_VARIABLE.*
13
14
14 # Ignore false positive relative to iterators
15 # Ignore false positive relative to iterators
15 ArrayData\.h:\d+:.*IPSIS_S04_VARIABLE.*found: (forward_iterator_tag)
16 ArrayData\.h:\d+:.*IPSIS_S04_VARIABLE.*found: (forward_iterator_tag)
16 ArrayData\.h:\d+:.*IPSIS_S04_VARIABLE.*found: (IteratorValue)
17 ArrayData\.h:\d+:.*IPSIS_S04_VARIABLE.*found: (IteratorValue)
17 ArrayData\.h:\d+:.*IPSIS_S04_VARIABLE.*found: (ptrdiff_t)
18 ArrayData\.h:\d+:.*IPSIS_S04_VARIABLE.*found: (ptrdiff_t)
18 ArrayData\.h:\d+:.*IPSIS_S04_VARIABLE.*found: (value_type)
19 ArrayData\.h:\d+:.*IPSIS_S04_VARIABLE.*found: (value_type)
19 ArrayData\.h:\d+:.*IPSIS_S05.*
20 ArrayData\.h:\d+:.*IPSIS_S05.*
20 ArrayData\.h:\d+:.*IPSIS_S06.*found: (iterator_category)
21 ArrayData\.h:\d+:.*IPSIS_S06.*found: (iterator_category)
21 ArrayData\.h:\d+:.*IPSIS_S06.*found: (forward_iterator_tag)
22 ArrayData\.h:\d+:.*IPSIS_S06.*found: (forward_iterator_tag)
22 ArrayData\.h:\d+:.*IPSIS_S06.*found: (value_type)
23 ArrayData\.h:\d+:.*IPSIS_S06.*found: (value_type)
23 ArrayData\.h:\d+:.*IPSIS_S06.*found: (IteratorValue)
24 ArrayData\.h:\d+:.*IPSIS_S06.*found: (IteratorValue)
24 ArrayData\.h:\d+:.*IPSIS_S06.*found: (difference_type)
25 ArrayData\.h:\d+:.*IPSIS_S06.*found: (difference_type)
25 ArrayData\.h:\d+:.*IPSIS_S06.*found: (ptrdiff_t)
26 ArrayData\.h:\d+:.*IPSIS_S06.*found: (ptrdiff_t)
26 ArrayData\.h:\d+:.*IPSIS_S06.*found: (pointer)
27 ArrayData\.h:\d+:.*IPSIS_S06.*found: (pointer)
27 ArrayData\.h:\d+:.*IPSIS_S06.*found: (reference)
28 ArrayData\.h:\d+:.*IPSIS_S06.*found: (reference)
28 ArrayData\.h:\d+:.*IPSIS_S06.*found: (value_type)
29 ArrayData\.h:\d+:.*IPSIS_S06.*found: (value_type)
29 DataSeries\.h:\d+:.*IPSIS_S05.*
30 DataSeries\.h:\d+:.*IPSIS_S05.*
30 DataSeries\.h:\d+:.*IPSIS_S06.*found: (iterator_category)
31 DataSeries\.h:\d+:.*IPSIS_S06.*found: (iterator_category)
31 DataSeries\.h:\d+:.*IPSIS_S06.*found: (forward_iterator_tag)
32 DataSeries\.h:\d+:.*IPSIS_S06.*found: (forward_iterator_tag)
32 DataSeries\.h:\d+:.*IPSIS_S06.*found: (value_type)
33 DataSeries\.h:\d+:.*IPSIS_S06.*found: (value_type)
33 DataSeries\.h:\d+:.*IPSIS_S06.*found: (IteratorValue)
34 DataSeries\.h:\d+:.*IPSIS_S06.*found: (IteratorValue)
34 DataSeries\.h:\d+:.*IPSIS_S06.*found: (difference_type)
35 DataSeries\.h:\d+:.*IPSIS_S06.*found: (difference_type)
35 DataSeries\.h:\d+:.*IPSIS_S06.*found: (ptrdiff_t)
36 DataSeries\.h:\d+:.*IPSIS_S06.*found: (ptrdiff_t)
36 DataSeries\.h:\d+:.*IPSIS_S06.*found: (pointer)
37 DataSeries\.h:\d+:.*IPSIS_S06.*found: (pointer)
37 DataSeries\.h:\d+:.*IPSIS_S06.*found: (reference)
38 DataSeries\.h:\d+:.*IPSIS_S06.*found: (reference)
38 DataSeries\.h:\d+:.*IPSIS_S06.*found: (value_type)
39 DataSeries\.h:\d+:.*IPSIS_S06.*found: (value_type)
39
40
40 # Ignore false positive relative to an alias
41 # Ignore false positive relative to an alias
41 DataSourceItemAction\.h:\d+:.*IPSIS_S06.*found: (ExecuteFunction)
42 DataSourceItemAction\.h:\d+:.*IPSIS_S06.*found: (ExecuteFunction)
42
43
43 # Ignore false positive relative to unnamed namespace
44 # Ignore false positive relative to unnamed namespace
44 VariableController\.cpp:\d+:.*IPSIS_F13.*
45 VariableController\.cpp:\d+:.*IPSIS_F13.*
@@ -1,16 +1,17
1 #ifndef SCIQLOP_AMDADEFS_H
1 #ifndef SCIQLOP_AMDADEFS_H
2 #define SCIQLOP_AMDADEFS_H
2 #define SCIQLOP_AMDADEFS_H
3
3
4 #include <QString>
4 #include <QString>
5
5
6 // ////////////// //
6 // ////////////// //
7 // AMDA constants //
7 // AMDA constants //
8 // ////////////// //
8 // ////////////// //
9
9
10 // Relevant keys in JSON file
10 // Relevant keys in JSON file
11 extern const QString AMDA_COMPONENT_KEY;
11 extern const QString AMDA_COMPONENT_KEY;
12 extern const QString AMDA_DATA_TYPE_KEY;
12 extern const QString AMDA_PRODUCT_KEY;
13 extern const QString AMDA_PRODUCT_KEY;
13 extern const QString AMDA_ROOT_KEY;
14 extern const QString AMDA_ROOT_KEY;
14 extern const QString AMDA_XML_ID_KEY;
15 extern const QString AMDA_XML_ID_KEY;
15
16
16 #endif // SCIQLOP_AMDADEFS_H
17 #endif // SCIQLOP_AMDADEFS_H
@@ -1,19 +1,21
1 #ifndef SCIQLOP_AMDARESULTPARSER_H
1 #ifndef SCIQLOP_AMDARESULTPARSER_H
2 #define SCIQLOP_AMDARESULTPARSER_H
2 #define SCIQLOP_AMDARESULTPARSER_H
3
3
4 #include "AmdaGlobal.h"
4 #include "AmdaGlobal.h"
5
5
6 #include <QLoggingCategory>
6 #include <QLoggingCategory>
7
7
8 #include <memory>
8 #include <memory>
9
9
10 class IDataSeries;
10 class IDataSeries;
11
11
12 Q_DECLARE_LOGGING_CATEGORY(LOG_AmdaResultParser)
12 Q_DECLARE_LOGGING_CATEGORY(LOG_AmdaResultParser)
13
13
14 struct SCIQLOP_AMDA_EXPORT AmdaResultParser {
14 struct SCIQLOP_AMDA_EXPORT AmdaResultParser {
15 enum class ValueType { SCALAR, VECTOR, UNKNOWN };
15
16
16 static std::shared_ptr<IDataSeries> readTxt(const QString &filePath) noexcept;
17 static std::shared_ptr<IDataSeries> readTxt(const QString &filePath,
18 ValueType valueType) noexcept;
17 };
19 };
18
20
19 #endif // SCIQLOP_AMDARESULTPARSER_H
21 #endif // SCIQLOP_AMDARESULTPARSER_H
@@ -1,6 +1,7
1 #include "AmdaDefs.h"
1 #include "AmdaDefs.h"
2
2
3 const QString AMDA_COMPONENT_KEY = QStringLiteral("component");
3 const QString AMDA_COMPONENT_KEY = QStringLiteral("component");
4 const QString AMDA_DATA_TYPE_KEY = QStringLiteral("dataType");
4 const QString AMDA_PRODUCT_KEY = QStringLiteral("parameter");
5 const QString AMDA_PRODUCT_KEY = QStringLiteral("parameter");
5 const QString AMDA_ROOT_KEY = QStringLiteral("dataCenter");
6 const QString AMDA_ROOT_KEY = QStringLiteral("dataCenter");
6 const QString AMDA_XML_ID_KEY = QStringLiteral("xml:id");
7 const QString AMDA_XML_ID_KEY = QStringLiteral("xml:id");
@@ -1,83 +1,74
1 #include "AmdaPlugin.h"
1 #include "AmdaPlugin.h"
2 #include "AmdaDefs.h"
2 #include "AmdaDefs.h"
3 #include "AmdaParser.h"
3 #include "AmdaParser.h"
4 #include "AmdaProvider.h"
4 #include "AmdaProvider.h"
5
5
6 #include <DataSource/DataSourceController.h>
6 #include <DataSource/DataSourceController.h>
7 #include <DataSource/DataSourceItem.h>
7 #include <DataSource/DataSourceItem.h>
8 #include <DataSource/DataSourceItemAction.h>
8 #include <DataSource/DataSourceItemAction.h>
9
9
10 #include <SqpApplication.h>
10 #include <SqpApplication.h>
11
11
12 Q_LOGGING_CATEGORY(LOG_AmdaPlugin, "AmdaPlugin")
12 Q_LOGGING_CATEGORY(LOG_AmdaPlugin, "AmdaPlugin")
13
13
14 namespace {
14 namespace {
15
15
16 /// Name of the data source
16 /// Name of the data source
17 const auto DATA_SOURCE_NAME = QStringLiteral("AMDA");
17 const auto DATA_SOURCE_NAME = QStringLiteral("AMDA");
18
18
19 /// Path of the file used to generate the data source item for AMDA
19 /// Path of the file used to generate the data source item for AMDA
20 const auto JSON_FILE_PATH = QStringLiteral(":/samples/AmdaSampleV2.json");
20 const auto JSON_FILE_PATH = QStringLiteral(":/samples/AmdaSampleV2.json");
21
21
22 void associateActions(DataSourceItem &item, const QUuid &dataSourceUid)
22 void associateActions(DataSourceItem &item, const QUuid &dataSourceUid)
23 {
23 {
24 auto addLoadAction = [&item, dataSourceUid](const QString &label) {
24 auto addLoadAction = [&item, dataSourceUid](const QString &label) {
25 item.addAction(
25 item.addAction(
26 std::make_unique<DataSourceItemAction>(label, [dataSourceUid](DataSourceItem &item) {
26 std::make_unique<DataSourceItemAction>(label, [dataSourceUid](DataSourceItem &item) {
27 if (auto app = sqpApp) {
27 if (auto app = sqpApp) {
28 app->dataSourceController().loadProductItem(dataSourceUid, item);
28 app->dataSourceController().loadProductItem(dataSourceUid, item);
29 }
29 }
30 }));
30 }));
31 };
31 };
32
32
33 const auto itemType = item.type();
33 const auto itemType = item.type();
34 if (itemType == DataSourceItemType::PRODUCT) {
34 if (itemType == DataSourceItemType::PRODUCT) {
35 /// @todo : As for the moment we do not manage the loading of vectors, in the case of a
36 /// parameter, we update the identifier of download of the data:
37 /// - if the parameter has no component, the identifier remains the same
38 /// - if the parameter has at least one component, the identifier is that of the first
39 /// component (for example, "imf" becomes "imf (0)")
40 if (item.childCount() != 0) {
41 item.setData(AMDA_XML_ID_KEY, item.child(0)->data(AMDA_XML_ID_KEY));
42 }
43
44 addLoadAction(QObject::tr("Load %1 product").arg(item.name()));
35 addLoadAction(QObject::tr("Load %1 product").arg(item.name()));
45 }
36 }
46 else if (itemType == DataSourceItemType::COMPONENT) {
37 else if (itemType == DataSourceItemType::COMPONENT) {
47 addLoadAction(QObject::tr("Load %1 component").arg(item.name()));
38 addLoadAction(QObject::tr("Load %1 component").arg(item.name()));
48 }
39 }
49
40
50 auto count = item.childCount();
41 auto count = item.childCount();
51 for (auto i = 0; i < count; ++i) {
42 for (auto i = 0; i < count; ++i) {
52 if (auto child = item.child(i)) {
43 if (auto child = item.child(i)) {
53 associateActions(*child, dataSourceUid);
44 associateActions(*child, dataSourceUid);
54 }
45 }
55 }
46 }
56 }
47 }
57
48
58 } // namespace
49 } // namespace
59
50
60 void AmdaPlugin::initialize()
51 void AmdaPlugin::initialize()
61 {
52 {
62 if (auto app = sqpApp) {
53 if (auto app = sqpApp) {
63 // Registers to the data source controller
54 // Registers to the data source controller
64 auto &dataSourceController = app->dataSourceController();
55 auto &dataSourceController = app->dataSourceController();
65 auto dataSourceUid = dataSourceController.registerDataSource(DATA_SOURCE_NAME);
56 auto dataSourceUid = dataSourceController.registerDataSource(DATA_SOURCE_NAME);
66
57
67 // Sets data source tree
58 // Sets data source tree
68 if (auto dataSourceItem = AmdaParser::readJson(JSON_FILE_PATH)) {
59 if (auto dataSourceItem = AmdaParser::readJson(JSON_FILE_PATH)) {
69 associateActions(*dataSourceItem, dataSourceUid);
60 associateActions(*dataSourceItem, dataSourceUid);
70
61
71 dataSourceController.setDataSourceItem(dataSourceUid, std::move(dataSourceItem));
62 dataSourceController.setDataSourceItem(dataSourceUid, std::move(dataSourceItem));
72 }
63 }
73 else {
64 else {
74 qCCritical(LOG_AmdaPlugin()) << tr("No data source item could be generated for AMDA");
65 qCCritical(LOG_AmdaPlugin()) << tr("No data source item could be generated for AMDA");
75 }
66 }
76
67
77 // Sets data provider
68 // Sets data provider
78 dataSourceController.setDataProvider(dataSourceUid, std::make_unique<AmdaProvider>());
69 dataSourceController.setDataProvider(dataSourceUid, std::make_unique<AmdaProvider>());
79 }
70 }
80 else {
71 else {
81 qCWarning(LOG_AmdaPlugin()) << tr("Can't access to SciQlop application");
72 qCWarning(LOG_AmdaPlugin()) << tr("Can't access to SciQlop application");
82 }
73 }
83 }
74 }
@@ -1,150 +1,168
1 #include "AmdaProvider.h"
1 #include "AmdaProvider.h"
2 #include "AmdaDefs.h"
2 #include "AmdaDefs.h"
3 #include "AmdaResultParser.h"
3 #include "AmdaResultParser.h"
4
4
5 #include <Common/DateUtils.h>
5 #include <Common/DateUtils.h>
6 #include <Data/DataProviderParameters.h>
6 #include <Data/DataProviderParameters.h>
7 #include <Network/NetworkController.h>
7 #include <Network/NetworkController.h>
8 #include <SqpApplication.h>
8 #include <SqpApplication.h>
9 #include <Variable/Variable.h>
9 #include <Variable/Variable.h>
10
10
11 #include <QNetworkAccessManager>
11 #include <QNetworkAccessManager>
12 #include <QNetworkReply>
12 #include <QNetworkReply>
13 #include <QTemporaryFile>
13 #include <QTemporaryFile>
14 #include <QThread>
14 #include <QThread>
15
15
16 Q_LOGGING_CATEGORY(LOG_AmdaProvider, "AmdaProvider")
16 Q_LOGGING_CATEGORY(LOG_AmdaProvider, "AmdaProvider")
17
17
18 namespace {
18 namespace {
19
19
20 /// URL format for a request on AMDA server. The parameters are as follows:
20 /// URL format for a request on AMDA server. The parameters are as follows:
21 /// - %1: start date
21 /// - %1: start date
22 /// - %2: end date
22 /// - %2: end date
23 /// - %3: parameter id
23 /// - %3: parameter id
24 const auto AMDA_URL_FORMAT = QStringLiteral(
24 const auto AMDA_URL_FORMAT = QStringLiteral(
25 "http://amda.irap.omp.eu/php/rest/"
25 "http://amda.irap.omp.eu/php/rest/"
26 "getParameter.php?startTime=%1&stopTime=%2&parameterID=%3&outputFormat=ASCII&"
26 "getParameter.php?startTime=%1&stopTime=%2&parameterID=%3&outputFormat=ASCII&"
27 "timeFormat=ISO8601&gzip=0");
27 "timeFormat=ISO8601&gzip=0");
28
28
29 /// Dates format passed in the URL (e.g 2013-09-23T09:00)
29 /// Dates format passed in the URL (e.g 2013-09-23T09:00)
30 const auto AMDA_TIME_FORMAT = QStringLiteral("yyyy-MM-ddThh:mm:ss");
30 const auto AMDA_TIME_FORMAT = QStringLiteral("yyyy-MM-ddThh:mm:ss");
31
31
32 /// Formats a time to a date that can be passed in URL
32 /// Formats a time to a date that can be passed in URL
33 QString dateFormat(double sqpRange) noexcept
33 QString dateFormat(double sqpRange) noexcept
34 {
34 {
35 auto dateTime = DateUtils::dateTime(sqpRange);
35 auto dateTime = DateUtils::dateTime(sqpRange);
36 return dateTime.toString(AMDA_TIME_FORMAT);
36 return dateTime.toString(AMDA_TIME_FORMAT);
37 }
37 }
38
38
39 AmdaResultParser::ValueType valueType(const QString &valueType)
40 {
41 if (valueType == QStringLiteral("scalar")) {
42 return AmdaResultParser::ValueType::SCALAR;
43 }
44 else if (valueType == QStringLiteral("vector")) {
45 return AmdaResultParser::ValueType::VECTOR;
46 }
47 else {
48 return AmdaResultParser::ValueType::UNKNOWN;
49 }
50 }
51
39 } // namespace
52 } // namespace
40
53
41 AmdaProvider::AmdaProvider()
54 AmdaProvider::AmdaProvider()
42 {
55 {
43 qCDebug(LOG_AmdaProvider()) << tr("AmdaProvider::AmdaProvider") << QThread::currentThread();
56 qCDebug(LOG_AmdaProvider()) << tr("AmdaProvider::AmdaProvider") << QThread::currentThread();
44 if (auto app = sqpApp) {
57 if (auto app = sqpApp) {
45 auto &networkController = app->networkController();
58 auto &networkController = app->networkController();
46 connect(this, SIGNAL(requestConstructed(QNetworkRequest, QUuid,
59 connect(this, SIGNAL(requestConstructed(QNetworkRequest, QUuid,
47 std::function<void(QNetworkReply *, QUuid)>)),
60 std::function<void(QNetworkReply *, QUuid)>)),
48 &networkController,
61 &networkController,
49 SLOT(onProcessRequested(QNetworkRequest, QUuid,
62 SLOT(onProcessRequested(QNetworkRequest, QUuid,
50 std::function<void(QNetworkReply *, QUuid)>)));
63 std::function<void(QNetworkReply *, QUuid)>)));
51
64
52
65
53 connect(&sqpApp->networkController(), SIGNAL(replyDownloadProgress(QUuid, double)), this,
66 connect(&sqpApp->networkController(), SIGNAL(replyDownloadProgress(QUuid, double)), this,
54 SIGNAL(dataProvidedProgress(QUuid, double)));
67 SIGNAL(dataProvidedProgress(QUuid, double)));
55 }
68 }
56 }
69 }
57
70
58 void AmdaProvider::requestDataLoading(QUuid acqIdentifier, const DataProviderParameters &parameters)
71 void AmdaProvider::requestDataLoading(QUuid acqIdentifier, const DataProviderParameters &parameters)
59 {
72 {
60 // NOTE: Try to use multithread if possible
73 // NOTE: Try to use multithread if possible
61 const auto times = parameters.m_Times;
74 const auto times = parameters.m_Times;
62 const auto data = parameters.m_Data;
75 const auto data = parameters.m_Data;
63 for (const auto &dateTime : qAsConst(times)) {
76 for (const auto &dateTime : qAsConst(times)) {
64 this->retrieveData(acqIdentifier, dateTime, data);
77 this->retrieveData(acqIdentifier, dateTime, data);
65
78
66 // TORM
79 // TORM
67 // QThread::msleep(200);
80 // QThread::msleep(200);
68 }
81 }
69 }
82 }
70
83
71 void AmdaProvider::requestDataAborting(QUuid acqIdentifier)
84 void AmdaProvider::requestDataAborting(QUuid acqIdentifier)
72 {
85 {
73 if (auto app = sqpApp) {
86 if (auto app = sqpApp) {
74 auto &networkController = app->networkController();
87 auto &networkController = app->networkController();
75 networkController.onReplyCanceled(acqIdentifier);
88 networkController.onReplyCanceled(acqIdentifier);
76 }
89 }
77 }
90 }
78
91
79 void AmdaProvider::retrieveData(QUuid token, const SqpRange &dateTime, const QVariantHash &data)
92 void AmdaProvider::retrieveData(QUuid token, const SqpRange &dateTime, const QVariantHash &data)
80 {
93 {
81 // Retrieves product ID from data: if the value is invalid, no request is made
94 // Retrieves product ID from data: if the value is invalid, no request is made
82 auto productId = data.value(AMDA_XML_ID_KEY).toString();
95 auto productId = data.value(AMDA_XML_ID_KEY).toString();
83 if (productId.isNull()) {
96 if (productId.isNull()) {
84 qCCritical(LOG_AmdaProvider()) << tr("Can't retrieve data: unknown product id");
97 qCCritical(LOG_AmdaProvider()) << tr("Can't retrieve data: unknown product id");
85 return;
98 return;
86 }
99 }
87 qCDebug(LOG_AmdaProvider()) << tr("AmdaProvider::retrieveData") << dateTime;
100 qCDebug(LOG_AmdaProvider()) << tr("AmdaProvider::retrieveData") << dateTime;
88
101
102 // Retrieves the data type that determines whether the expected format for the result file is
103 // scalar, vector...
104 auto productValueType = valueType(data.value(AMDA_DATA_TYPE_KEY).toString());
105
89 // /////////// //
106 // /////////// //
90 // Creates URL //
107 // Creates URL //
91 // /////////// //
108 // /////////// //
92
109
93 auto startDate = dateFormat(dateTime.m_TStart);
110 auto startDate = dateFormat(dateTime.m_TStart);
94 auto endDate = dateFormat(dateTime.m_TEnd);
111 auto endDate = dateFormat(dateTime.m_TEnd);
95
112
96 auto url = QUrl{QString{AMDA_URL_FORMAT}.arg(startDate, endDate, productId)};
113 auto url = QUrl{QString{AMDA_URL_FORMAT}.arg(startDate, endDate, productId)};
97 qCInfo(LOG_AmdaProvider()) << tr("TORM AmdaProvider::retrieveData url:") << url;
114 qCInfo(LOG_AmdaProvider()) << tr("TORM AmdaProvider::retrieveData url:") << url;
98 auto tempFile = std::make_shared<QTemporaryFile>();
115 auto tempFile = std::make_shared<QTemporaryFile>();
99
116
100 // LAMBDA
117 // LAMBDA
101 auto httpDownloadFinished
118 auto httpDownloadFinished = [this, dateTime, tempFile,
102 = [this, dateTime, tempFile](QNetworkReply *reply, QUuid dataId) noexcept {
119 productValueType](QNetworkReply *reply, QUuid dataId) noexcept {
103
120
104 // Don't do anything if the reply was abort
121 // Don't do anything if the reply was abort
105 if (reply->error() != QNetworkReply::OperationCanceledError) {
122 if (reply->error() != QNetworkReply::OperationCanceledError) {
106
123
107 if (tempFile) {
124 if (tempFile) {
108 auto replyReadAll = reply->readAll();
125 auto replyReadAll = reply->readAll();
109 if (!replyReadAll.isEmpty()) {
126 if (!replyReadAll.isEmpty()) {
110 tempFile->write(replyReadAll);
127 tempFile->write(replyReadAll);
111 }
128 }
112 tempFile->close();
129 tempFile->close();
113
130
114 // Parse results file
131 // Parse results file
115 if (auto dataSeries = AmdaResultParser::readTxt(tempFile->fileName())) {
132 if (auto dataSeries
116 emit dataProvided(dataId, dataSeries, dateTime);
133 = AmdaResultParser::readTxt(tempFile->fileName(), productValueType)) {
117 }
134 emit dataProvided(dataId, dataSeries, dateTime);
118 else {
135 }
119 /// @todo ALX : debug
136 else {
120 }
137 /// @todo ALX : debug
121 }
138 }
122 }
139 }
123
140 }
124 };
141
142 };
125 auto httpFinishedLambda
143 auto httpFinishedLambda
126 = [this, httpDownloadFinished, tempFile](QNetworkReply *reply, QUuid dataId) noexcept {
144 = [this, httpDownloadFinished, tempFile](QNetworkReply *reply, QUuid dataId) noexcept {
127
145
128 // Don't do anything if the reply was abort
146 // Don't do anything if the reply was abort
129 if (reply->error() != QNetworkReply::OperationCanceledError) {
147 if (reply->error() != QNetworkReply::OperationCanceledError) {
130 auto downloadFileUrl = QUrl{QString{reply->readAll()}};
148 auto downloadFileUrl = QUrl{QString{reply->readAll()}};
131
149
132
150
133 qCInfo(LOG_AmdaProvider())
151 qCInfo(LOG_AmdaProvider())
134 << tr("TORM AmdaProvider::retrieveData downloadFileUrl:") << downloadFileUrl;
152 << tr("TORM AmdaProvider::retrieveData downloadFileUrl:") << downloadFileUrl;
135 // Executes request for downloading file //
153 // Executes request for downloading file //
136
154
137 // Creates destination file
155 // Creates destination file
138 if (tempFile->open()) {
156 if (tempFile->open()) {
139 // Executes request
157 // Executes request
140 emit requestConstructed(QNetworkRequest{downloadFileUrl}, dataId,
158 emit requestConstructed(QNetworkRequest{downloadFileUrl}, dataId,
141 httpDownloadFinished);
159 httpDownloadFinished);
142 }
160 }
143 }
161 }
144 };
162 };
145
163
146 // //////////////// //
164 // //////////////// //
147 // Executes request //
165 // Executes request //
148 // //////////////// //
166 // //////////////// //
149 emit requestConstructed(QNetworkRequest{url}, token, httpFinishedLambda);
167 emit requestConstructed(QNetworkRequest{url}, token, httpFinishedLambda);
150 }
168 }
@@ -1,160 +1,216
1 #include "AmdaResultParser.h"
1 #include "AmdaResultParser.h"
2
2
3 #include <Common/DateUtils.h>
3 #include <Common/DateUtils.h>
4 #include <Data/ScalarSeries.h>
4 #include <Data/ScalarSeries.h>
5 #include <Data/VectorSeries.h>
5
6
6 #include <QDateTime>
7 #include <QDateTime>
7 #include <QFile>
8 #include <QFile>
8 #include <QRegularExpression>
9 #include <QRegularExpression>
9
10
10 #include <cmath>
11 #include <cmath>
11
12
12 Q_LOGGING_CATEGORY(LOG_AmdaResultParser, "AmdaResultParser")
13 Q_LOGGING_CATEGORY(LOG_AmdaResultParser, "AmdaResultParser")
13
14
14 namespace {
15 namespace {
15
16
16 /// Message in result file when the file was not found on server
17 /// Message in result file when the file was not found on server
17 const auto FILE_NOT_FOUND_MESSAGE = QStringLiteral("Not Found");
18 const auto FILE_NOT_FOUND_MESSAGE = QStringLiteral("Not Found");
18
19
19 /// Format for dates in result files
20 /// Format for dates in result files
20 const auto DATE_FORMAT = QStringLiteral("yyyy-MM-ddThh:mm:ss.zzz");
21 const auto DATE_FORMAT = QStringLiteral("yyyy-MM-ddThh:mm:ss.zzz");
21
22
22 /// Separator between values in a result line
23 /// Separator between values in a result line
23 const auto RESULT_LINE_SEPARATOR = QRegularExpression{QStringLiteral("\\s+")};
24 const auto RESULT_LINE_SEPARATOR = QRegularExpression{QStringLiteral("\\s+")};
24
25
25 /// Regex to find unit in a line. Examples of valid lines:
26 /// Regex to find unit in a line. Examples of valid lines:
26 /// ... - Units : nT - ...
27 /// ... - Units : nT - ...
27 /// ... -Units:nT- ...
28 /// ... -Units:nT- ...
28 /// ... -Units: mΒ²- ...
29 /// ... -Units: mΒ²- ...
29 /// ... - Units : m/s - ...
30 /// ... - Units : m/s - ...
30 const auto UNIT_REGEX = QRegularExpression{QStringLiteral("-\\s*Units\\s*:\\s*(.+?)\\s*-")};
31 const auto UNIT_REGEX = QRegularExpression{QStringLiteral("-\\s*Units\\s*:\\s*(.+?)\\s*-")};
31
32
32 /// Converts a string date to a double date
33 /// Converts a string date to a double date
33 /// @return a double that represents the date in seconds, NaN if the string date can't be converted
34 /// @return a double that represents the date in seconds, NaN if the string date can't be converted
34 double doubleDate(const QString &stringDate) noexcept
35 double doubleDate(const QString &stringDate) noexcept
35 {
36 {
36 auto dateTime = QDateTime::fromString(stringDate, DATE_FORMAT);
37 auto dateTime = QDateTime::fromString(stringDate, DATE_FORMAT);
37 dateTime.setTimeSpec(Qt::UTC);
38 dateTime.setTimeSpec(Qt::UTC);
38 return dateTime.isValid() ? DateUtils::secondsSinceEpoch(dateTime)
39 return dateTime.isValid() ? DateUtils::secondsSinceEpoch(dateTime)
39 : std::numeric_limits<double>::quiet_NaN();
40 : std::numeric_limits<double>::quiet_NaN();
40 }
41 }
41
42
42 /// Checks if a line is a comment line
43 /// Checks if a line is a comment line
43 bool isCommentLine(const QString &line)
44 bool isCommentLine(const QString &line)
44 {
45 {
45 return line.startsWith("#");
46 return line.startsWith("#");
46 }
47 }
47
48
49 /// @return the number of lines to be read depending on the type of value passed in parameter
50 int nbValues(AmdaResultParser::ValueType valueType) noexcept
51 {
52 switch (valueType) {
53 case AmdaResultParser::ValueType::SCALAR:
54 return 1;
55 case AmdaResultParser::ValueType::VECTOR:
56 return 3;
57 case AmdaResultParser::ValueType::UNKNOWN:
58 // Invalid case
59 break;
60 }
61
62 // Invalid cases
63 qCCritical(LOG_AmdaResultParser())
64 << QObject::tr("Can't get the number of values to read: unsupported type");
65 return 0;
66 }
67
48 /**
68 /**
49 * Reads stream to retrieve x-axis unit
69 * Reads stream to retrieve x-axis unit
50 * @param stream the stream to read
70 * @param stream the stream to read
51 * @return the unit that has been read in the stream, a default unit (time unit with no label) if an
71 * @return the unit that has been read in the stream, a default unit (time unit with no label) if an
52 * error occured during reading
72 * error occured during reading
53 */
73 */
54 Unit readXAxisUnit(QTextStream &stream)
74 Unit readXAxisUnit(QTextStream &stream)
55 {
75 {
56 QString line{};
76 QString line{};
57
77
58 // Searches unit in the comment lines
78 // Searches unit in the comment lines
59 while (stream.readLineInto(&line) && isCommentLine(line)) {
79 while (stream.readLineInto(&line) && isCommentLine(line)) {
60 auto match = UNIT_REGEX.match(line);
80 auto match = UNIT_REGEX.match(line);
61 if (match.hasMatch()) {
81 if (match.hasMatch()) {
62 return Unit{match.captured(1), true};
82 return Unit{match.captured(1), true};
63 }
83 }
64 }
84 }
65
85
66 qCWarning(LOG_AmdaResultParser()) << QObject::tr("The unit could not be found in the file");
86 qCWarning(LOG_AmdaResultParser()) << QObject::tr("The unit could not be found in the file");
67
87
68 // Error cases
88 // Error cases
69 return Unit{{}, true};
89 return Unit{{}, true};
70 }
90 }
71
91
72 /**
92 /**
73 * Reads stream to retrieve results
93 * Reads stream to retrieve results
74 * @param stream the stream to read
94 * @param stream the stream to read
75 * @return the pair of vectors x-axis data/values data that has been read in the stream
95 * @return the pair of vectors x-axis data/values data that has been read in the stream
76 */
96 */
77 QPair<QVector<double>, QVector<double> > readResults(QTextStream &stream)
97 QPair<QVector<double>, QVector<QVector<double> > >
98 readResults(QTextStream &stream, AmdaResultParser::ValueType valueType)
78 {
99 {
100 auto expectedNbValues = nbValues(valueType);
101
79 auto xData = QVector<double>{};
102 auto xData = QVector<double>{};
80 auto valuesData = QVector<double>{};
103 auto valuesData = QVector<QVector<double> >(expectedNbValues);
81
104
82 QString line{};
105 QString line{};
83
106
84 while (stream.readLineInto(&line)) {
107 while (stream.readLineInto(&line)) {
85 // Ignore comment lines
108 // Ignore comment lines
86 if (!isCommentLine(line)) {
109 if (!isCommentLine(line)) {
87 auto lineData = line.split(RESULT_LINE_SEPARATOR, QString::SkipEmptyParts);
110 auto lineData = line.split(RESULT_LINE_SEPARATOR, QString::SkipEmptyParts);
88 if (lineData.size() == 2) {
111 if (lineData.size() == expectedNbValues + 1) {
89 // X : the data is converted from date to double (in secs)
112 // X : the data is converted from date to double (in secs)
90 auto x = doubleDate(lineData.at(0));
113 auto x = doubleDate(lineData.at(0));
91
114
92 // Value
93 bool valueOk;
94 auto value = lineData.at(1).toDouble(&valueOk);
95
96 // Adds result only if x is valid. Then, if value is invalid, it is set to NaN
115 // Adds result only if x is valid. Then, if value is invalid, it is set to NaN
97 if (!std::isnan(x)) {
116 if (!std::isnan(x)) {
98 xData.push_back(x);
117 xData.push_back(x);
99
118
100 if (!valueOk) {
119 // Values
101 qCWarning(LOG_AmdaResultParser())
120 for (auto valueIndex = 0; valueIndex < expectedNbValues; ++valueIndex) {
102 << QObject::tr(
121 auto column = valueIndex + 1;
103 "Value from line %1 is invalid and will be converted to NaN")
122
104 .arg(line);
123 bool valueOk;
105 value = std::numeric_limits<double>::quiet_NaN();
124 auto value = lineData.at(column).toDouble(&valueOk);
125
126 if (!valueOk) {
127 qCWarning(LOG_AmdaResultParser())
128 << QObject::tr(
129 "Value from (line %1, column %2) is invalid and will be "
130 "converted to NaN")
131 .arg(line, column);
132 value = std::numeric_limits<double>::quiet_NaN();
133 }
134 valuesData[valueIndex].append(value);
106 }
135 }
107
108 valuesData.push_back(value);
109 }
136 }
110 else {
137 else {
111 qCWarning(LOG_AmdaResultParser())
138 qCWarning(LOG_AmdaResultParser())
112 << QObject::tr("Can't retrieve results from line %1: x is invalid")
139 << QObject::tr("Can't retrieve results from line %1: x is invalid")
113 .arg(line);
140 .arg(line);
114 }
141 }
115 }
142 }
116 else {
143 else {
117 qCWarning(LOG_AmdaResultParser())
144 qCWarning(LOG_AmdaResultParser())
118 << QObject::tr("Can't retrieve results from line %1: invalid line").arg(line);
145 << QObject::tr("Can't retrieve results from line %1: invalid line").arg(line);
119 }
146 }
120 }
147 }
121 }
148 }
122
149
123 return qMakePair(std::move(xData), std::move(valuesData));
150 return qMakePair(std::move(xData), std::move(valuesData));
124 }
151 }
125
152
126 } // namespace
153 } // namespace
127
154
128 std::shared_ptr<IDataSeries> AmdaResultParser::readTxt(const QString &filePath) noexcept
155 std::shared_ptr<IDataSeries> AmdaResultParser::readTxt(const QString &filePath,
156 ValueType valueType) noexcept
129 {
157 {
158 if (valueType == ValueType::UNKNOWN) {
159 qCCritical(LOG_AmdaResultParser())
160 << QObject::tr("Can't retrieve AMDA data: the type of values to be read is unknown");
161 return nullptr;
162 }
163
130 QFile file{filePath};
164 QFile file{filePath};
131
165
132 if (!file.open(QFile::ReadOnly | QIODevice::Text)) {
166 if (!file.open(QFile::ReadOnly | QIODevice::Text)) {
133 qCCritical(LOG_AmdaResultParser())
167 qCCritical(LOG_AmdaResultParser())
134 << QObject::tr("Can't retrieve AMDA data from file %1: %2")
168 << QObject::tr("Can't retrieve AMDA data from file %1: %2")
135 .arg(filePath, file.errorString());
169 .arg(filePath, file.errorString());
136 return nullptr;
170 return nullptr;
137 }
171 }
138
172
139 QTextStream stream{&file};
173 QTextStream stream{&file};
140
174
141 // Checks if the file was found on the server
175 // Checks if the file was found on the server
142 auto firstLine = stream.readLine();
176 auto firstLine = stream.readLine();
143 if (firstLine.compare(FILE_NOT_FOUND_MESSAGE) == 0) {
177 if (firstLine.compare(FILE_NOT_FOUND_MESSAGE) == 0) {
144 qCCritical(LOG_AmdaResultParser())
178 qCCritical(LOG_AmdaResultParser())
145 << QObject::tr("Can't retrieve AMDA data from file %1: file was not found on server")
179 << QObject::tr("Can't retrieve AMDA data from file %1: file was not found on server")
146 .arg(filePath);
180 .arg(filePath);
147 return nullptr;
181 return nullptr;
148 }
182 }
149
183
150 // Reads x-axis unit
184 // Reads x-axis unit
151 stream.seek(0); // returns to the beginning of the file
185 stream.seek(0); // returns to the beginning of the file
152 auto xAxisUnit = readXAxisUnit(stream);
186 auto xAxisUnit = readXAxisUnit(stream);
153
187
154 // Reads results
188 // Reads results
155 stream.seek(0); // returns to the beginning of the file
189 stream.seek(0); // returns to the beginning of the file
156 auto results = readResults(stream);
190 auto results = readResults(stream, valueType);
191
192 // Creates data series
193 switch (valueType) {
194 case ValueType::SCALAR:
195 Q_ASSERT(results.second.size() == 1);
196 return std::make_shared<ScalarSeries>(
197 std::move(results.first), std::move(results.second.takeFirst()), xAxisUnit, Unit{});
198 case ValueType::VECTOR: {
199 Q_ASSERT(results.second.size() == 3);
200 auto xValues = results.second.takeFirst();
201 auto yValues = results.second.takeFirst();
202 auto zValues = results.second.takeFirst();
203 return std::make_shared<VectorSeries>(std::move(results.first), std::move(xValues),
204 std::move(yValues), std::move(zValues), xAxisUnit,
205 Unit{});
206 }
207 case ValueType::UNKNOWN:
208 // Invalid case
209 break;
210 }
157
211
158 return std::make_shared<ScalarSeries>(std::move(results.first), std::move(results.second),
212 // Invalid cases
159 xAxisUnit, Unit{});
213 qCCritical(LOG_AmdaResultParser())
214 << QObject::tr("Can't create data series: unsupported value type");
215 return nullptr;
160 }
216 }
@@ -1,212 +1,310
1 #include "AmdaResultParser.h"
1 #include "AmdaResultParser.h"
2
2
3 #include <Data/ScalarSeries.h>
3 #include <Data/ScalarSeries.h>
4 #include <Data/VectorSeries.h>
4
5
5 #include <QObject>
6 #include <QObject>
6 #include <QtTest>
7 #include <QtTest>
7
8
8 namespace {
9 namespace {
9
10
10 /// Path for the tests
11 /// Path for the tests
11 const auto TESTS_RESOURCES_PATH
12 const auto TESTS_RESOURCES_PATH
12 = QFileInfo{QString{AMDA_TESTS_RESOURCES_DIR}, "TestAmdaResultParser"}.absoluteFilePath();
13 = QFileInfo{QString{AMDA_TESTS_RESOURCES_DIR}, "TestAmdaResultParser"}.absoluteFilePath();
13
14
15 QDateTime dateTime(int year, int month, int day, int hours, int minutes, int seconds)
16 {
17 return QDateTime{{year, month, day}, {hours, minutes, seconds}, Qt::UTC};
18 }
19
14 /// Compares two vectors that can potentially contain NaN values
20 /// Compares two vectors that can potentially contain NaN values
15 bool compareVectors(const QVector<double> &v1, const QVector<double> &v2)
21 bool compareVectors(const QVector<double> &v1, const QVector<double> &v2)
16 {
22 {
17 if (v1.size() != v2.size()) {
23 if (v1.size() != v2.size()) {
18 return false;
24 return false;
19 }
25 }
20
26
21 auto result = true;
27 auto result = true;
22 auto v2It = v2.cbegin();
28 auto v2It = v2.cbegin();
23 for (auto v1It = v1.cbegin(), v1End = v1.cend(); v1It != v1End && result; ++v1It, ++v2It) {
29 for (auto v1It = v1.cbegin(), v1End = v1.cend(); v1It != v1End && result; ++v1It, ++v2It) {
24 auto v1Value = *v1It;
30 auto v1Value = *v1It;
25 auto v2Value = *v2It;
31 auto v2Value = *v2It;
26
32
27 // If v1 is NaN, v2 has to be NaN too
33 // If v1 is NaN, v2 has to be NaN too
28 result = std::isnan(v1Value) ? std::isnan(v2Value) : (v1Value == v2Value);
34 result = std::isnan(v1Value) ? std::isnan(v2Value) : (v1Value == v2Value);
29 }
35 }
30
36
31 return result;
37 return result;
32 }
38 }
33
39
40 bool compareVectors(const QVector<QVector<double> > &v1, const QVector<QVector<double> > &v2)
41 {
42 if (v1.size() != v2.size()) {
43 return false;
44 }
45
46 auto result = true;
47 for (auto i = 0; i < v1.size() && result; ++i) {
48 result &= compareVectors(v1.at(i), v2.at(i));
49 }
50
51 return result;
52 }
53
54 QVector<QVector<double> > valuesData(const ArrayData<1> &arrayData)
55 {
56 return QVector<QVector<double> >{arrayData.data()};
57 }
58
59 QVector<QVector<double> > valuesData(const ArrayData<2> &arrayData)
60 {
61 return arrayData.data();
62 }
63
64
34 QString inputFilePath(const QString &inputFileName)
65 QString inputFilePath(const QString &inputFileName)
35 {
66 {
36 return QFileInfo{TESTS_RESOURCES_PATH, inputFileName}.absoluteFilePath();
67 return QFileInfo{TESTS_RESOURCES_PATH, inputFileName}.absoluteFilePath();
37 }
68 }
38
69
70 template <typename T>
39 struct ExpectedResults {
71 struct ExpectedResults {
40 explicit ExpectedResults() = default;
72 explicit ExpectedResults() = default;
41
73
42 /// Ctor with QVector<QDateTime> as x-axis data. Datetimes are converted to doubles
43 explicit ExpectedResults(Unit xAxisUnit, Unit valuesUnit, const QVector<QDateTime> &xAxisData,
74 explicit ExpectedResults(Unit xAxisUnit, Unit valuesUnit, const QVector<QDateTime> &xAxisData,
44 QVector<double> valuesData)
75 QVector<double> valuesData)
76 : ExpectedResults(xAxisUnit, valuesUnit, xAxisData,
77 QVector<QVector<double> >{std::move(valuesData)})
78 {
79 }
80
81 /// Ctor with QVector<QDateTime> as x-axis data. Datetimes are converted to doubles
82 explicit ExpectedResults(Unit xAxisUnit, Unit valuesUnit, const QVector<QDateTime> &xAxisData,
83 QVector<QVector<double> > valuesData)
45 : m_ParsingOK{true},
84 : m_ParsingOK{true},
46 m_XAxisUnit{xAxisUnit},
85 m_XAxisUnit{xAxisUnit},
47 m_ValuesUnit{valuesUnit},
86 m_ValuesUnit{valuesUnit},
48 m_XAxisData{},
87 m_XAxisData{},
49 m_ValuesData{std::move(valuesData)}
88 m_ValuesData{std::move(valuesData)}
50 {
89 {
51 // Converts QVector<QDateTime> to QVector<double>
90 // Converts QVector<QDateTime> to QVector<double>
52 std::transform(xAxisData.cbegin(), xAxisData.cend(), std::back_inserter(m_XAxisData),
91 std::transform(xAxisData.cbegin(), xAxisData.cend(), std::back_inserter(m_XAxisData),
53 [](const auto &dateTime) { return dateTime.toMSecsSinceEpoch() / 1000.; });
92 [](const auto &dateTime) { return dateTime.toMSecsSinceEpoch() / 1000.; });
54 }
93 }
55
94
56 /**
95 /**
57 * Validates a DataSeries compared to the expected results
96 * Validates a DataSeries compared to the expected results
58 * @param results the DataSeries to validate
97 * @param results the DataSeries to validate
59 */
98 */
60 void validate(std::shared_ptr<IDataSeries> results)
99 void validate(std::shared_ptr<IDataSeries> results)
61 {
100 {
62 if (m_ParsingOK) {
101 if (m_ParsingOK) {
63 auto scalarSeries = dynamic_cast<ScalarSeries *>(results.get());
102 auto dataSeries = dynamic_cast<T *>(results.get());
64 QVERIFY(scalarSeries != nullptr);
103 QVERIFY(dataSeries != nullptr);
65
104
66 // Checks units
105 // Checks units
67 QVERIFY(scalarSeries->xAxisUnit() == m_XAxisUnit);
106 QVERIFY(dataSeries->xAxisUnit() == m_XAxisUnit);
68 QVERIFY(scalarSeries->valuesUnit() == m_ValuesUnit);
107 QVERIFY(dataSeries->valuesUnit() == m_ValuesUnit);
69
108
70 // Checks values : as the vectors can potentially contain NaN values, we must use a
109 // Checks values : as the vectors can potentially contain NaN values, we must use a
71 // custom vector comparison method
110 // custom vector comparison method
72 QVERIFY(compareVectors(scalarSeries->xAxisData()->data(), m_XAxisData));
111 QVERIFY(compareVectors(dataSeries->xAxisData()->data(), m_XAxisData));
73 QVERIFY(compareVectors(scalarSeries->valuesData()->data(), m_ValuesData));
112 QVERIFY(compareVectors(valuesData(*dataSeries->valuesData()), m_ValuesData));
74 }
113 }
75 else {
114 else {
76 QVERIFY(results == nullptr);
115 QVERIFY(results == nullptr);
77 }
116 }
78 }
117 }
79
118
80 // Parsing was successfully completed
119 // Parsing was successfully completed
81 bool m_ParsingOK{false};
120 bool m_ParsingOK{false};
82 // Expected x-axis unit
121 // Expected x-axis unit
83 Unit m_XAxisUnit{};
122 Unit m_XAxisUnit{};
84 // Expected values unit
123 // Expected values unit
85 Unit m_ValuesUnit{};
124 Unit m_ValuesUnit{};
86 // Expected x-axis data
125 // Expected x-axis data
87 QVector<double> m_XAxisData{};
126 QVector<double> m_XAxisData{};
88 // Expected values data
127 // Expected values data
89 QVector<double> m_ValuesData{};
128 QVector<QVector<double> > m_ValuesData{};
90 };
129 };
91
130
92 } // namespace
131 } // namespace
93
132
94 Q_DECLARE_METATYPE(ExpectedResults)
133 Q_DECLARE_METATYPE(ExpectedResults<ScalarSeries>)
134 Q_DECLARE_METATYPE(ExpectedResults<VectorSeries>)
95
135
96 class TestAmdaResultParser : public QObject {
136 class TestAmdaResultParser : public QObject {
97 Q_OBJECT
137 Q_OBJECT
138 private:
139 template <typename T>
140 void testReadDataStructure()
141 {
142 // ////////////// //
143 // Test structure //
144 // ////////////// //
145
146 // Name of TXT file to read
147 QTest::addColumn<QString>("inputFileName");
148 // Expected results
149 QTest::addColumn<ExpectedResults<T> >("expectedResults");
150 }
151
152 template <typename T>
153 void testRead(AmdaResultParser::ValueType valueType)
154 {
155 QFETCH(QString, inputFileName);
156 QFETCH(ExpectedResults<T>, expectedResults);
157
158 // Parses file
159 auto filePath = inputFilePath(inputFileName);
160 auto results = AmdaResultParser::readTxt(filePath, valueType);
161
162 // ///////////////// //
163 // Validates results //
164 // ///////////////// //
165 expectedResults.validate(results);
166 }
167
98 private slots:
168 private slots:
99 /// Input test data
169 /// Input test data
100 /// @sa testTxtJson()
170 /// @sa testReadScalarTxt()
101 void testReadTxt_data();
171 void testReadScalarTxt_data();
102
172
103 /// Tests parsing of a TXT file
173 /// Tests parsing scalar series of a TXT file
104 void testReadTxt();
174 void testReadScalarTxt();
175
176 /// Input test data
177 /// @sa testReadVectorTxt()
178 void testReadVectorTxt_data();
179
180 /// Tests parsing vector series of a TXT file
181 void testReadVectorTxt();
105 };
182 };
106
183
107 void TestAmdaResultParser::testReadTxt_data()
184 void TestAmdaResultParser::testReadScalarTxt_data()
108 {
185 {
109 // ////////////// //
186 testReadDataStructure<ScalarSeries>();
110 // Test structure //
111 // ////////////// //
112
113 // Name of TXT file to read
114 QTest::addColumn<QString>("inputFileName");
115 // Expected results
116 QTest::addColumn<ExpectedResults>("expectedResults");
117
187
118 // ////////// //
188 // ////////// //
119 // Test cases //
189 // Test cases //
120 // ////////// //
190 // ////////// //
121
191
122 auto dateTime = [](int year, int month, int day, int hours, int minutes, int seconds) {
123 return QDateTime{{year, month, day}, {hours, minutes, seconds}, Qt::UTC};
124 };
125
126 // Valid files
192 // Valid files
127 QTest::newRow("Valid file")
193 QTest::newRow("Valid file")
128 << QStringLiteral("ValidScalar1.txt")
194 << QStringLiteral("ValidScalar1.txt")
129 << ExpectedResults{
195 << ExpectedResults<ScalarSeries>{
130 Unit{QStringLiteral("nT"), true}, Unit{},
196 Unit{QStringLiteral("nT"), true}, Unit{},
131 QVector<QDateTime>{dateTime(2013, 9, 23, 9, 0, 30), dateTime(2013, 9, 23, 9, 1, 30),
197 QVector<QDateTime>{dateTime(2013, 9, 23, 9, 0, 30), dateTime(2013, 9, 23, 9, 1, 30),
132 dateTime(2013, 9, 23, 9, 2, 30), dateTime(2013, 9, 23, 9, 3, 30),
198 dateTime(2013, 9, 23, 9, 2, 30), dateTime(2013, 9, 23, 9, 3, 30),
133 dateTime(2013, 9, 23, 9, 4, 30), dateTime(2013, 9, 23, 9, 5, 30),
199 dateTime(2013, 9, 23, 9, 4, 30), dateTime(2013, 9, 23, 9, 5, 30),
134 dateTime(2013, 9, 23, 9, 6, 30), dateTime(2013, 9, 23, 9, 7, 30),
200 dateTime(2013, 9, 23, 9, 6, 30), dateTime(2013, 9, 23, 9, 7, 30),
135 dateTime(2013, 9, 23, 9, 8, 30), dateTime(2013, 9, 23, 9, 9, 30)},
201 dateTime(2013, 9, 23, 9, 8, 30), dateTime(2013, 9, 23, 9, 9, 30)},
136 QVector<double>{-2.83950, -2.71850, -2.52150, -2.57633, -2.58050, -2.48325, -2.63025,
202 QVector<double>{-2.83950, -2.71850, -2.52150, -2.57633, -2.58050, -2.48325, -2.63025,
137 -2.55800, -2.43250, -2.42200}};
203 -2.55800, -2.43250, -2.42200}};
138
204
139 QTest::newRow("Valid file (value of first line is invalid but it is converted to NaN")
205 QTest::newRow("Valid file (value of first line is invalid but it is converted to NaN")
140 << QStringLiteral("WrongValue.txt")
206 << QStringLiteral("WrongValue.txt")
141 << ExpectedResults{
207 << ExpectedResults<ScalarSeries>{
142 Unit{QStringLiteral("nT"), true}, Unit{},
208 Unit{QStringLiteral("nT"), true}, Unit{},
143 QVector<QDateTime>{dateTime(2013, 9, 23, 9, 0, 30), dateTime(2013, 9, 23, 9, 1, 30),
209 QVector<QDateTime>{dateTime(2013, 9, 23, 9, 0, 30), dateTime(2013, 9, 23, 9, 1, 30),
144 dateTime(2013, 9, 23, 9, 2, 30)},
210 dateTime(2013, 9, 23, 9, 2, 30)},
145 QVector<double>{std::numeric_limits<double>::quiet_NaN(), -2.71850, -2.52150}};
211 QVector<double>{std::numeric_limits<double>::quiet_NaN(), -2.71850, -2.52150}};
146
212
147 QTest::newRow("Valid file that contains NaN values")
213 QTest::newRow("Valid file that contains NaN values")
148 << QStringLiteral("NaNValue.txt")
214 << QStringLiteral("NaNValue.txt")
149 << ExpectedResults{
215 << ExpectedResults<ScalarSeries>{
150 Unit{QStringLiteral("nT"), true}, Unit{},
216 Unit{QStringLiteral("nT"), true}, Unit{},
151 QVector<QDateTime>{dateTime(2013, 9, 23, 9, 0, 30), dateTime(2013, 9, 23, 9, 1, 30),
217 QVector<QDateTime>{dateTime(2013, 9, 23, 9, 0, 30), dateTime(2013, 9, 23, 9, 1, 30),
152 dateTime(2013, 9, 23, 9, 2, 30)},
218 dateTime(2013, 9, 23, 9, 2, 30)},
153 QVector<double>{std::numeric_limits<double>::quiet_NaN(), -2.71850, -2.52150}};
219 QVector<double>{std::numeric_limits<double>::quiet_NaN(), -2.71850, -2.52150}};
154
220
155 // Valid files but with some invalid lines (wrong unit, wrong values, etc.)
221 // Valid files but with some invalid lines (wrong unit, wrong values, etc.)
156 QTest::newRow("No unit file") << QStringLiteral("NoUnit.txt")
222 QTest::newRow("No unit file") << QStringLiteral("NoUnit.txt")
157 << ExpectedResults{Unit{QStringLiteral(""), true}, Unit{},
223 << ExpectedResults<ScalarSeries>{Unit{QStringLiteral(""), true},
158 QVector<QDateTime>{}, QVector<double>{}};
224 Unit{}, QVector<QDateTime>{},
225 QVector<double>{}};
159 QTest::newRow("Wrong unit file")
226 QTest::newRow("Wrong unit file")
160 << QStringLiteral("WrongUnit.txt")
227 << QStringLiteral("WrongUnit.txt")
161 << ExpectedResults{Unit{QStringLiteral(""), true}, Unit{},
228 << ExpectedResults<ScalarSeries>{Unit{QStringLiteral(""), true}, Unit{},
162 QVector<QDateTime>{dateTime(2013, 9, 23, 9, 0, 30),
229 QVector<QDateTime>{dateTime(2013, 9, 23, 9, 0, 30),
163 dateTime(2013, 9, 23, 9, 1, 30),
230 dateTime(2013, 9, 23, 9, 1, 30),
164 dateTime(2013, 9, 23, 9, 2, 30)},
231 dateTime(2013, 9, 23, 9, 2, 30)},
165 QVector<double>{-2.83950, -2.71850, -2.52150}};
232 QVector<double>{-2.83950, -2.71850, -2.52150}};
166
233
167 QTest::newRow("Wrong results file (date of first line is invalid")
234 QTest::newRow("Wrong results file (date of first line is invalid")
168 << QStringLiteral("WrongDate.txt")
235 << QStringLiteral("WrongDate.txt")
169 << ExpectedResults{
236 << ExpectedResults<ScalarSeries>{
170 Unit{QStringLiteral("nT"), true}, Unit{},
237 Unit{QStringLiteral("nT"), true}, Unit{},
171 QVector<QDateTime>{dateTime(2013, 9, 23, 9, 1, 30), dateTime(2013, 9, 23, 9, 2, 30)},
238 QVector<QDateTime>{dateTime(2013, 9, 23, 9, 1, 30), dateTime(2013, 9, 23, 9, 2, 30)},
172 QVector<double>{-2.71850, -2.52150}};
239 QVector<double>{-2.71850, -2.52150}};
173
240
174 QTest::newRow("Wrong results file (too many values for first line")
241 QTest::newRow("Wrong results file (too many values for first line")
175 << QStringLiteral("TooManyValues.txt")
242 << QStringLiteral("TooManyValues.txt")
176 << ExpectedResults{
243 << ExpectedResults<ScalarSeries>{
177 Unit{QStringLiteral("nT"), true}, Unit{},
244 Unit{QStringLiteral("nT"), true}, Unit{},
178 QVector<QDateTime>{dateTime(2013, 9, 23, 9, 1, 30), dateTime(2013, 9, 23, 9, 2, 30)},
245 QVector<QDateTime>{dateTime(2013, 9, 23, 9, 1, 30), dateTime(2013, 9, 23, 9, 2, 30)},
179 QVector<double>{-2.71850, -2.52150}};
246 QVector<double>{-2.71850, -2.52150}};
180
247
181 QTest::newRow("Wrong results file (x of first line is NaN")
248 QTest::newRow("Wrong results file (x of first line is NaN")
182 << QStringLiteral("NaNX.txt")
249 << QStringLiteral("NaNX.txt")
183 << ExpectedResults{
250 << ExpectedResults<ScalarSeries>{
184 Unit{QStringLiteral("nT"), true}, Unit{},
251 Unit{QStringLiteral("nT"), true}, Unit{},
185 QVector<QDateTime>{dateTime(2013, 9, 23, 9, 1, 30), dateTime(2013, 9, 23, 9, 2, 30)},
252 QVector<QDateTime>{dateTime(2013, 9, 23, 9, 1, 30), dateTime(2013, 9, 23, 9, 2, 30)},
186 QVector<double>{-2.71850, -2.52150}};
253 QVector<double>{-2.71850, -2.52150}};
187
254
255 QTest::newRow("Invalid file type (vector)")
256 << QStringLiteral("ValidVector1.txt")
257 << ExpectedResults<ScalarSeries>{Unit{QStringLiteral("nT"), true}, Unit{},
258 QVector<QDateTime>{}, QVector<double>{}};
259
188 // Invalid files
260 // Invalid files
189 QTest::newRow("Invalid file (unexisting file)") << QStringLiteral("UnexistingFile.txt")
261 QTest::newRow("Invalid file (unexisting file)")
190 << ExpectedResults{};
262 << QStringLiteral("UnexistingFile.txt") << ExpectedResults<ScalarSeries>{};
263
264 QTest::newRow("Invalid file (file not found on server)")
265 << QStringLiteral("FileNotFound.txt") << ExpectedResults<ScalarSeries>{};
266 }
191
267
192 QTest::newRow("Invalid file (file not found on server)") << QStringLiteral("FileNotFound.txt")
268 void TestAmdaResultParser::testReadScalarTxt()
193 << ExpectedResults{};
269 {
270 testRead<ScalarSeries>(AmdaResultParser::ValueType::SCALAR);
194 }
271 }
195
272
196 void TestAmdaResultParser::testReadTxt()
273 void TestAmdaResultParser::testReadVectorTxt_data()
197 {
274 {
198 QFETCH(QString, inputFileName);
275 testReadDataStructure<VectorSeries>();
199 QFETCH(ExpectedResults, expectedResults);
276
277 // ////////// //
278 // Test cases //
279 // ////////// //
280
281 // Valid files
282 QTest::newRow("Valid file")
283 << QStringLiteral("ValidVector1.txt")
284 << ExpectedResults<VectorSeries>{
285 Unit{QStringLiteral("nT"), true}, Unit{},
286 QVector<QDateTime>{dateTime(2013, 7, 2, 9, 13, 50), dateTime(2013, 7, 2, 9, 14, 6),
287 dateTime(2013, 7, 2, 9, 14, 22), dateTime(2013, 7, 2, 9, 14, 38),
288 dateTime(2013, 7, 2, 9, 14, 54), dateTime(2013, 7, 2, 9, 15, 10),
289 dateTime(2013, 7, 2, 9, 15, 26), dateTime(2013, 7, 2, 9, 15, 42),
290 dateTime(2013, 7, 2, 9, 15, 58), dateTime(2013, 7, 2, 9, 16, 14)},
291 QVector<QVector<double> >{
292 {-0.332, -1.011, -1.457, -1.293, -1.217, -1.443, -1.278, -1.202, -1.22, -1.259},
293 {3.206, 2.999, 2.785, 2.736, 2.612, 2.564, 2.892, 2.862, 2.859, 2.764},
294 {0.058, 0.496, 1.018, 1.485, 1.662, 1.505, 1.168, 1.244, 1.15, 1.358}}};
200
295
201 // Parses file
296 // Valid files but with some invalid lines (wrong unit, wrong values, etc.)
202 auto filePath = inputFilePath(inputFileName);
297 QTest::newRow("Invalid file type (scalar)")
203 auto results = AmdaResultParser::readTxt(filePath);
298 << QStringLiteral("ValidScalar1.txt")
299 << ExpectedResults<VectorSeries>{Unit{QStringLiteral("nT"), true}, Unit{},
300 QVector<QDateTime>{},
301 QVector<QVector<double> >{{}, {}, {}}};
302 }
204
303
205 // ///////////////// //
304 void TestAmdaResultParser::testReadVectorTxt()
206 // Validates results //
305 {
207 // ///////////////// //
306 testRead<VectorSeries>(AmdaResultParser::ValueType::VECTOR);
208 expectedResults.validate(results);
209 }
307 }
210
308
211 QTEST_MAIN(TestAmdaResultParser)
309 QTEST_MAIN(TestAmdaResultParser)
212 #include "TestAmdaResultParser.moc"
310 #include "TestAmdaResultParser.moc"
General Comments 0
You need to be logged in to leave comments. Login now