##// END OF EJS Templates
Uses std::vector instead of QVector in ArrayData (1)...
Alexandre Leroux -
r694:c050c224f3de
parent child
Show More
@@ -1,139 +1,139
1 1 #ifndef SCIQLOP_SORTUTILS_H
2 2 #define SCIQLOP_SORTUTILS_H
3 3
4 4 #include <algorithm>
5 5 #include <cmath>
6 6 #include <numeric>
7 7 #include <vector>
8 8
9 9 /**
10 10 * Utility class with methods for sorting data
11 11 */
12 12 struct SortUtils {
13 13 /**
14 14 * Generates a vector representing the index of insertion of each data of a container if this
15 15 * one had to be sorted according to a comparison function.
16 16 *
17 17 * For example:
18 18 * If the container is a vector {1; 4; 2; 5; 3} and the comparison function is std::less, the
19 19 * result would be : {0; 3; 1; 4; 2}
20 20 *
21 21 * @tparam Container the type of the container.
22 22 * @tparam Compare the type of the comparison function
23 23 * @param container the container from which to generate the result. The container must have a
24 24 * at() method that returns a value associated to an index
25 25 * @param compare the comparison function
26 26 */
27 27 template <typename Container, typename Compare>
28 28 static std::vector<int> sortPermutation(const Container &container, const Compare &compare)
29 29 {
30 30 auto permutation = std::vector<int>{};
31 31 permutation.resize(container.size());
32 32
33 33 std::iota(permutation.begin(), permutation.end(), 0);
34 34 std::sort(permutation.begin(), permutation.end(),
35 35 [&](int i, int j) { return compare(container.at(i), container.at(j)); });
36 36 return permutation;
37 37 }
38 38
39 39 /**
40 40 * Sorts a container according to indices passed in parameter. The number of data in the
41 41 * container must be a multiple of the number of indices used to sort the container.
42 42 *
43 43 * Example 1:
44 44 * container: {1, 2, 3, 4, 5, 6}
45 45 * sortPermutation: {1, 0}
46 46 *
47 47 * Values will be sorted three by three, and the result will be:
48 48 * {4, 5, 6, 1, 2, 3}
49 49 *
50 50 * Example 2:
51 51 * container: {1, 2, 3, 4, 5, 6}
52 52 * sortPermutation: {2, 0, 1}
53 53 *
54 54 * Values will be sorted two by two, and the result will be:
55 55 * {5, 6, 1, 2, 3, 4}
56 56 *
57 57 * @param container the container sorted
58 58 * @param sortPermutation the indices used to sort the container
59 59 * @return the container sorted
60 60 * @warning no verification is made on validity of sortPermutation (i.e. the vector has unique
61 61 * indices and its range is [0 ; vector.size()[ )
62 62 */
63 63 template <typename Container>
64 64 static Container sort(const Container &container, int nbValues,
65 65 const std::vector<int> &sortPermutation)
66 66 {
67 67 auto containerSize = container.size();
68 68 if (containerSize % nbValues != 0
69 69 || ((containerSize / nbValues) != sortPermutation.size())) {
70 70 return Container{};
71 71 }
72 72
73 73 // Inits result
74 74 auto sortedData = Container{};
75 75 sortedData.reserve(containerSize);
76 76
77 77 for (auto i = 0, componentIndex = 0, permutationIndex = 0; i < containerSize;
78 78 ++i, componentIndex = i % nbValues, permutationIndex = i / nbValues) {
79 79 auto insertIndex = sortPermutation.at(permutationIndex) * nbValues + componentIndex;
80 sortedData.append(container.at(insertIndex));
80 sortedData.push_back(container.at(insertIndex));
81 81 }
82 82
83 83 return sortedData;
84 84 }
85 85
86 86 /**
87 87 * Compares two values that can be NaN. This method is intended to be used as a compare function
88 88 * for searching min value by excluding NaN values.
89 89 *
90 90 * Examples of use:
91 91 * - f({1, 3, 2, 4, 5}) will return 1
92 92 * - f({NaN, 3, 2, 4, 5}) will return 2 (NaN is excluded)
93 93 * - f({NaN, NaN, 3, NaN, NaN}) will return 3 (NaN are excluded)
94 94 * - f({NaN, NaN, NaN, NaN, NaN}) will return NaN (no existing value)
95 95 *
96 96 * @param v1 first value
97 97 * @param v2 second value
98 98 * @return true if v1 < v2, false otherwise
99 99 * @sa std::min_element
100 100 */
101 101 template <typename T>
102 102 static bool minCompareWithNaN(const T &v1, const T &v2)
103 103 {
104 104 // Table used with NaN values:
105 105 // NaN < v2 -> false
106 106 // v1 < NaN -> true
107 107 // NaN < NaN -> false
108 108 // v1 < v2 -> v1 < v2
109 109 return std::isnan(v1) ? false : std::isnan(v2) || (v1 < v2);
110 110 }
111 111
112 112 /**
113 113 * Compares two values that can be NaN. This method is intended to be used as a compare function
114 114 * for searching max value by excluding NaN values.
115 115 *
116 116 * Examples of use:
117 117 * - f({1, 3, 2, 4, 5}) will return 5
118 118 * - f({1, 3, 2, 4, NaN}) will return 4 (NaN is excluded)
119 119 * - f({NaN, NaN, 3, NaN, NaN}) will return 3 (NaN are excluded)
120 120 * - f({NaN, NaN, NaN, NaN, NaN}) will return NaN (no existing value)
121 121 *
122 122 * @param v1 first value
123 123 * @param v2 second value
124 124 * @return true if v1 < v2, false otherwise
125 125 * @sa std::max_element
126 126 */
127 127 template <typename T>
128 128 static bool maxCompareWithNaN(const T &v1, const T &v2)
129 129 {
130 130 // Table used with NaN values:
131 131 // NaN < v2 -> true
132 132 // v1 < NaN -> false
133 133 // NaN < NaN -> false
134 134 // v1 < v2 -> v1 < v2
135 135 return std::isnan(v1) ? true : !std::isnan(v2) && (v1 < v2);
136 136 }
137 137 };
138 138
139 139 #endif // SCIQLOP_SORTUTILS_H
@@ -1,379 +1,378
1 1 #ifndef SCIQLOP_ARRAYDATA_H
2 2 #define SCIQLOP_ARRAYDATA_H
3 3
4 4 #include "Data/ArrayDataIterator.h"
5 5 #include <Common/SortUtils.h>
6 6
7 7 #include <QReadLocker>
8 8 #include <QReadWriteLock>
9 9 #include <QVector>
10 10
11 11 #include <memory>
12 12
13 13 template <int Dim>
14 14 class ArrayData;
15 15
16 using DataContainer = QVector<double>;
16 using DataContainer = std::vector<double>;
17 17
18 18 namespace arraydata_detail {
19 19
20 20 /// Struct used to sort ArrayData
21 21 template <int Dim>
22 22 struct Sort {
23 23 static std::shared_ptr<ArrayData<Dim> > sort(const DataContainer &data, int nbComponents,
24 24 const std::vector<int> &sortPermutation)
25 25 {
26 26 return std::make_shared<ArrayData<Dim> >(
27 27 SortUtils::sort(data, nbComponents, sortPermutation), nbComponents);
28 28 }
29 29 };
30 30
31 31 /// Specialization for uni-dimensional ArrayData
32 32 template <>
33 33 struct Sort<1> {
34 34 static std::shared_ptr<ArrayData<1> > sort(const DataContainer &data, int nbComponents,
35 35 const std::vector<int> &sortPermutation)
36 36 {
37 37 Q_UNUSED(nbComponents)
38 38 return std::make_shared<ArrayData<1> >(SortUtils::sort(data, 1, sortPermutation));
39 39 }
40 40 };
41 41
42 42 template <int Dim, bool IsConst>
43 43 class IteratorValue;
44 44
45 45 template <int Dim, bool IsConst>
46 46 struct IteratorValueBuilder {
47 47 };
48 48
49 49 template <int Dim>
50 50 struct IteratorValueBuilder<Dim, true> {
51 51 using DataContainerIterator = DataContainer::const_iterator;
52 52
53 53 static void swap(IteratorValue<Dim, true> &o1, IteratorValue<Dim, true> &o2) {}
54 54 };
55 55
56 56 template <int Dim>
57 57 struct IteratorValueBuilder<Dim, false> {
58 58 using DataContainerIterator = DataContainer::iterator;
59 59
60 60 static void swap(IteratorValue<Dim, false> &o1, IteratorValue<Dim, false> &o2)
61 61 {
62 62 for (auto i = 0; i < o1.m_NbComponents; ++i) {
63 63 std::iter_swap(o1.m_It + i, o2.m_It + i);
64 64 }
65 65 }
66 66 };
67 67
68 68 template <int Dim, bool IsConst>
69 69 class IteratorValue : public ArrayDataIteratorValue::Impl {
70 70 public:
71 71 friend class ArrayData<Dim>;
72 72 friend class IteratorValueBuilder<Dim, IsConst>;
73 73
74 74 using DataContainerIterator =
75 75 typename IteratorValueBuilder<Dim, IsConst>::DataContainerIterator;
76 76
77 77 template <bool IC = IsConst, typename = std::enable_if_t<IC == true> >
78 78 explicit IteratorValue(const DataContainer &container, int nbComponents, bool begin)
79 79 : m_It{begin ? container.cbegin() : container.cend()}, m_NbComponents{nbComponents}
80 80 {
81 81 }
82 82
83 83 template <bool IC = IsConst, typename = std::enable_if_t<IC == false> >
84 84 explicit IteratorValue(DataContainer &container, int nbComponents, bool begin)
85 85 : m_It{begin ? container.begin() : container.end()}, m_NbComponents{nbComponents}
86 86 {
87 87 }
88 88
89 89 IteratorValue(const IteratorValue &other) = default;
90 90
91 91 std::unique_ptr<ArrayDataIteratorValue::Impl> clone() const override
92 92 {
93 93 return std::make_unique<IteratorValue<Dim, IsConst> >(*this);
94 94 }
95 95
96 96 int distance(const ArrayDataIteratorValue::Impl &other) const override try {
97 97 /// @todo ALX : validate
98 98 const auto &otherImpl = dynamic_cast<const IteratorValue &>(other);
99 99 return std::distance(otherImpl.m_It, m_It) / m_NbComponents;
100 100 }
101 101 catch (const std::bad_cast &) {
102 102 return 0;
103 103 }
104 104
105 105 bool equals(const ArrayDataIteratorValue::Impl &other) const override try {
106 106 const auto &otherImpl = dynamic_cast<const IteratorValue &>(other);
107 107 return std::tie(m_It, m_NbComponents) == std::tie(otherImpl.m_It, otherImpl.m_NbComponents);
108 108 }
109 109 catch (const std::bad_cast &) {
110 110 return false;
111 111 }
112 112
113 113 bool lowerThan(const ArrayDataIteratorValue::Impl &other) const override try {
114 114 const auto &otherImpl = dynamic_cast<const IteratorValue &>(other);
115 115 return m_It < otherImpl.m_It;
116 116 }
117 117 catch (const std::bad_cast &) {
118 118 return false;
119 119 }
120 120
121 121 std::unique_ptr<ArrayDataIteratorValue::Impl> advance(int offset) const override
122 122 {
123 123 auto result = clone();
124 124 while (offset--) {
125 125 result->next();
126 126 }
127 127 return result;
128 128 }
129 129
130 130 void next() override { std::advance(m_It, m_NbComponents); }
131 131 void prev() override { std::advance(m_It, -m_NbComponents); }
132 132
133 133 double at(int componentIndex) const override { return *(m_It + componentIndex); }
134 134 double first() const override { return *m_It; }
135 135 double min() const override
136 136 {
137 137 auto values = this->values();
138 138 auto end = values.cend();
139 139 auto it = std::min_element(values.cbegin(), end, [](const auto &v1, const auto &v2) {
140 140 return SortUtils::minCompareWithNaN(v1, v2);
141 141 });
142 142
143 143 return it != end ? *it : std::numeric_limits<double>::quiet_NaN();
144 144 }
145 145 double max() const override
146 146 {
147 147 auto values = this->values();
148 148 auto end = values.cend();
149 149 auto it = std::max_element(values.cbegin(), end, [](const auto &v1, const auto &v2) {
150 150 return SortUtils::maxCompareWithNaN(v1, v2);
151 151 });
152 152 return it != end ? *it : std::numeric_limits<double>::quiet_NaN();
153 153 }
154 154
155 155 QVector<double> values() const override
156 156 {
157 157 auto result = QVector<double>{};
158 158 for (auto i = 0; i < m_NbComponents; ++i) {
159 159 result.push_back(*(m_It + i));
160 160 }
161 161
162 162 return result;
163 163 }
164 164
165 165 void swap(ArrayDataIteratorValue::Impl &other) override
166 166 {
167 167 auto &otherImpl = dynamic_cast<IteratorValue &>(other);
168 168 IteratorValueBuilder<Dim, IsConst>::swap(*this, otherImpl);
169 169 }
170 170
171 171 private:
172 172 DataContainerIterator m_It;
173 173 int m_NbComponents;
174 174 };
175 175
176 176 } // namespace arraydata_detail
177 177
178 178 /**
179 179 * @brief The ArrayData class represents a dataset for a data series.
180 180 *
181 181 * A dataset can be unidimensional or two-dimensional. This property is determined by the Dim
182 182 * template-parameter. In a case of a two-dimensional dataset, each dataset component has the same
183 183 * number of values
184 184 *
185 185 * @tparam Dim the dimension of the ArrayData (one or two)
186 186 * @sa IDataSeries
187 187 */
188 188 template <int Dim>
189 189 class ArrayData {
190 190 public:
191 191 // ///// //
192 192 // Ctors //
193 193 // ///// //
194 194
195 195 /**
196 196 * Ctor for a unidimensional ArrayData
197 197 * @param data the data the ArrayData will hold
198 198 */
199 199 template <int D = Dim, typename = std::enable_if_t<D == 1> >
200 200 explicit ArrayData(DataContainer data) : m_Data{std::move(data)}, m_NbComponents{1}
201 201 {
202 202 }
203 203
204 204 /**
205 205 * Ctor for a two-dimensional ArrayData. The number of components (number of lines) must be
206 206 * greater than 2 and must be a divisor of the total number of data in the vector
207 207 * @param data the data the ArrayData will hold
208 208 * @param nbComponents the number of components
209 209 * @throws std::invalid_argument if the number of components is less than 2 or is not a divisor
210 210 * of the size of the data
211 211 */
212 212 template <int D = Dim, typename = std::enable_if_t<D == 2> >
213 213 explicit ArrayData(DataContainer data, int nbComponents)
214 214 : m_Data{std::move(data)}, m_NbComponents{nbComponents}
215 215 {
216 216 if (nbComponents < 2) {
217 217 throw std::invalid_argument{
218 218 QString{"A multidimensional ArrayData must have at least 2 components (found: %1)"}
219 219 .arg(nbComponents)
220 220 .toStdString()};
221 221 }
222 222
223 223 if (m_Data.size() % m_NbComponents != 0) {
224 224 throw std::invalid_argument{QString{
225 225 "The number of components (%1) is inconsistent with the total number of data (%2)"}
226 226 .arg(m_Data.size(), nbComponents)
227 227 .toStdString()};
228 228 }
229 229 }
230 230
231 231 /// Copy ctor
232 232 explicit ArrayData(const ArrayData &other)
233 233 {
234 234 QReadLocker otherLocker{&other.m_Lock};
235 235 m_Data = other.m_Data;
236 236 m_NbComponents = other.m_NbComponents;
237 237 }
238 238
239 239 // /////////////// //
240 240 // General methods //
241 241 // /////////////// //
242 242
243 243 /**
244 244 * Merges into the array data an other array data. The two array datas must have the same number
245 245 * of components so the merge can be done
246 246 * @param other the array data to merge with
247 247 * @param prepend if true, the other array data is inserted at the beginning, otherwise it is
248 248 * inserted at the end
249 249 */
250 250 void add(const ArrayData<Dim> &other, bool prepend = false)
251 251 {
252 252 QWriteLocker locker{&m_Lock};
253 253 QReadLocker otherLocker{&other.m_Lock};
254 254
255 255 if (m_NbComponents != other.componentCount()) {
256 256 return;
257 257 }
258 258
259 259 if (prepend) {
260 260 auto otherDataSize = other.m_Data.size();
261 261 m_Data.insert(m_Data.begin(), otherDataSize, 0.);
262 262 for (auto i = 0; i < otherDataSize; ++i) {
263 263 m_Data.replace(i, other.m_Data.at(i));
264 264 }
265 265 }
266 266 else {
267 267 m_Data.append(other.m_Data);
268 268 }
269 269 }
270 270
271 271 void clear()
272 272 {
273 273 QWriteLocker locker{&m_Lock};
274 274 m_Data.clear();
275 275 }
276 276
277 277 int componentCount() const noexcept { return m_NbComponents; }
278 278
279 279 /// @return the size (i.e. number of values) of a single component
280 280 /// @remarks in a case of a two-dimensional ArrayData, each component has the same size
281 281 int size() const
282 282 {
283 283 QReadLocker locker{&m_Lock};
284 284 return m_Data.size() / m_NbComponents;
285 285 }
286 286
287 287 std::shared_ptr<ArrayData<Dim> > sort(const std::vector<int> &sortPermutation)
288 288 {
289 289 QReadLocker locker{&m_Lock};
290 290 return arraydata_detail::Sort<Dim>::sort(m_Data, m_NbComponents, sortPermutation);
291 291 }
292 292
293 293 // ///////// //
294 294 // Iterators //
295 295 // ///////// //
296 296
297 297 ArrayDataIterator begin()
298 298 {
299 299 return ArrayDataIterator{
300 300 ArrayDataIteratorValue{std::make_unique<arraydata_detail::IteratorValue<Dim, false> >(
301 301 m_Data, m_NbComponents, true)}};
302 302 }
303 303
304 304 ArrayDataIterator end()
305 305 {
306 306 return ArrayDataIterator{
307 307 ArrayDataIteratorValue{std::make_unique<arraydata_detail::IteratorValue<Dim, false> >(
308 308 m_Data, m_NbComponents, false)}};
309 309 }
310 310
311 311 ArrayDataIterator cbegin() const
312 312 {
313 313 return ArrayDataIterator{
314 314 ArrayDataIteratorValue{std::make_unique<arraydata_detail::IteratorValue<Dim, true> >(
315 315 m_Data, m_NbComponents, true)}};
316 316 }
317 317
318 318 ArrayDataIterator cend() const
319 319 {
320 320 return ArrayDataIterator{
321 321 ArrayDataIteratorValue{std::make_unique<arraydata_detail::IteratorValue<Dim, true> >(
322 322 m_Data, m_NbComponents, false)}};
323 323 }
324 324
325 325 void erase(ArrayDataIterator first, ArrayDataIterator last)
326 326 {
327 327 auto firstImpl = dynamic_cast<arraydata_detail::IteratorValue<Dim, false> *>(first->impl());
328 328 auto lastImpl = dynamic_cast<arraydata_detail::IteratorValue<Dim, false> *>(last->impl());
329 329
330 330 if (firstImpl && lastImpl) {
331 331 m_Data.erase(firstImpl->m_It, lastImpl->m_It);
332 332 }
333 333 }
334 334
335 335 /// Inserts at the end of the array data the values passed as a parameter. This
336 336 /// method is intended to be used in the context of generating a back insert iterator, or only
337 337 /// if it's ensured that the total size of the vector is consistent with the number of
338 338 /// components of the array data
339 339 /// @param values the values to insert
340 340 /// @sa http://en.cppreference.com/w/cpp/iterator/back_inserter
341 341 void push_back(const QVector<double> &values)
342 342 {
343 343 Q_ASSERT(values.size() % m_NbComponents == 0);
344 344 m_Data.append(values);
345 345 }
346 346
347 347 /**
348 348 * @return the data at a specified index
349 349 * @remarks index must be a valid position
350 350 */
351 351 double at(int index) const noexcept
352 352 {
353 353 QReadLocker locker{&m_Lock};
354 354 return m_Data.at(index);
355 355 }
356 356
357 357 // ///////////// //
358 358 // 1-dim methods //
359 359 // ///////////// //
360 360
361 361 /**
362 362 * @return the data as a vector, as a const reference
363 363 * @remarks this method is only available for a unidimensional ArrayData
364 364 */
365 365 template <int D = Dim, typename = std::enable_if_t<D == 1> >
366 const QVector<double> &cdata() const noexcept
366 DataContainer cdata() const noexcept
367 367 {
368 QReadLocker locker{&m_Lock};
369 368 return m_Data;
370 369 }
371 370
372 371 private:
373 372 DataContainer m_Data;
374 373 /// Number of components (lines). Is always 1 in a 1-dim ArrayData
375 374 int m_NbComponents;
376 375 mutable QReadWriteLock m_Lock;
377 376 };
378 377
379 378 #endif // SCIQLOP_ARRAYDATA_H
@@ -1,410 +1,410
1 1 #ifndef SCIQLOP_DATASERIES_H
2 2 #define SCIQLOP_DATASERIES_H
3 3
4 4 #include "CoreGlobal.h"
5 5
6 6 #include <Common/SortUtils.h>
7 7
8 8 #include <Data/ArrayData.h>
9 9 #include <Data/DataSeriesMergeHelper.h>
10 10 #include <Data/IDataSeries.h>
11 11
12 12 #include <QLoggingCategory>
13 13 #include <QReadLocker>
14 14 #include <QReadWriteLock>
15 15 #include <memory>
16 16
17 17 // We don't use the Qt macro since the log is used in the header file, which causes multiple log
18 18 // definitions with inheritance. Inline method is used instead
19 19 inline const QLoggingCategory &LOG_DataSeries()
20 20 {
21 21 static const QLoggingCategory category{"DataSeries"};
22 22 return category;
23 23 }
24 24
25 25 template <int Dim>
26 26 class DataSeries;
27 27
28 28 namespace dataseries_detail {
29 29
30 30 template <int Dim, bool IsConst>
31 31 class IteratorValue : public DataSeriesIteratorValue::Impl {
32 32 public:
33 33 friend class DataSeries<Dim>;
34 34
35 35 template <bool IC = IsConst, typename = std::enable_if_t<IC == false> >
36 36 explicit IteratorValue(DataSeries<Dim> &dataSeries, bool begin)
37 37 : m_XIt(begin ? dataSeries.xAxisData()->begin() : dataSeries.xAxisData()->end()),
38 38 m_ValuesIt(begin ? dataSeries.valuesData()->begin() : dataSeries.valuesData()->end())
39 39 {
40 40 }
41 41
42 42 template <bool IC = IsConst, typename = std::enable_if_t<IC == true> >
43 43 explicit IteratorValue(const DataSeries<Dim> &dataSeries, bool begin)
44 44 : m_XIt(begin ? dataSeries.xAxisData()->cbegin() : dataSeries.xAxisData()->cend()),
45 45 m_ValuesIt(begin ? dataSeries.valuesData()->cbegin()
46 46 : dataSeries.valuesData()->cend())
47 47 {
48 48 }
49 49
50 50 IteratorValue(const IteratorValue &other) = default;
51 51
52 52 std::unique_ptr<DataSeriesIteratorValue::Impl> clone() const override
53 53 {
54 54 return std::make_unique<IteratorValue<Dim, IsConst> >(*this);
55 55 }
56 56
57 57 int distance(const DataSeriesIteratorValue::Impl &other) const override try {
58 58 const auto &otherImpl = dynamic_cast<const IteratorValue &>(other);
59 59 return m_XIt->distance(*otherImpl.m_XIt);
60 60 }
61 61 catch (const std::bad_cast &) {
62 62 return 0;
63 63 }
64 64
65 65 bool equals(const DataSeriesIteratorValue::Impl &other) const override try {
66 66 const auto &otherImpl = dynamic_cast<const IteratorValue &>(other);
67 67 return std::tie(m_XIt, m_ValuesIt) == std::tie(otherImpl.m_XIt, otherImpl.m_ValuesIt);
68 68 }
69 69 catch (const std::bad_cast &) {
70 70 return false;
71 71 }
72 72
73 73 bool lowerThan(const DataSeriesIteratorValue::Impl &other) const override try {
74 74 const auto &otherImpl = dynamic_cast<const IteratorValue &>(other);
75 75 return m_XIt->lowerThan(*otherImpl.m_XIt);
76 76 }
77 77 catch (const std::bad_cast &) {
78 78 return false;
79 79 }
80 80
81 81 std::unique_ptr<DataSeriesIteratorValue::Impl> advance(int offset) const override
82 82 {
83 83 auto result = clone();
84 84 while (offset--) {
85 85 result->next();
86 86 }
87 87 return result;
88 88 }
89 89
90 90 void next() override
91 91 {
92 92 ++m_XIt;
93 93 ++m_ValuesIt;
94 94 }
95 95
96 96 void prev() override
97 97 {
98 98 --m_XIt;
99 99 --m_ValuesIt;
100 100 }
101 101
102 102 double x() const override { return m_XIt->at(0); }
103 103 double value() const override { return m_ValuesIt->at(0); }
104 104 double value(int componentIndex) const override { return m_ValuesIt->at(componentIndex); }
105 105 double minValue() const override { return m_ValuesIt->min(); }
106 106 double maxValue() const override { return m_ValuesIt->max(); }
107 107 QVector<double> values() const override { return m_ValuesIt->values(); }
108 108
109 109 void swap(DataSeriesIteratorValue::Impl &other) override
110 110 {
111 111 auto &otherImpl = dynamic_cast<IteratorValue &>(other);
112 112 m_XIt->impl()->swap(*otherImpl.m_XIt->impl());
113 113 m_ValuesIt->impl()->swap(*otherImpl.m_ValuesIt->impl());
114 114 }
115 115
116 116 private:
117 117 ArrayDataIterator m_XIt;
118 118 ArrayDataIterator m_ValuesIt;
119 119 };
120 120 } // namespace dataseries_detail
121 121
122 122 /**
123 123 * @brief The DataSeries class is the base (abstract) implementation of IDataSeries.
124 124 *
125 125 * It proposes to set a dimension for the values ​​data.
126 126 *
127 127 * A DataSeries is always sorted on its x-axis data.
128 128 *
129 129 * @tparam Dim The dimension of the values data
130 130 *
131 131 */
132 132 template <int Dim>
133 133 class SCIQLOP_CORE_EXPORT DataSeries : public IDataSeries {
134 134 friend class DataSeriesMergeHelper;
135 135
136 136 public:
137 137 /// Tag needed to define the push_back() method
138 138 /// @sa push_back()
139 139 using value_type = DataSeriesIteratorValue;
140 140
141 141 /// @sa IDataSeries::xAxisData()
142 142 std::shared_ptr<ArrayData<1> > xAxisData() override { return m_XAxisData; }
143 143 const std::shared_ptr<ArrayData<1> > xAxisData() const { return m_XAxisData; }
144 144
145 145 /// @sa IDataSeries::xAxisUnit()
146 146 Unit xAxisUnit() const override { return m_XAxisUnit; }
147 147
148 148 /// @return the values dataset
149 149 std::shared_ptr<ArrayData<Dim> > valuesData() { return m_ValuesData; }
150 150 const std::shared_ptr<ArrayData<Dim> > valuesData() const { return m_ValuesData; }
151 151
152 152 /// @sa IDataSeries::valuesUnit()
153 153 Unit valuesUnit() const override { return m_ValuesUnit; }
154 154
155 155
156 156 SqpRange range() const override
157 157 {
158 if (!m_XAxisData->cdata().isEmpty()) {
159 return SqpRange{m_XAxisData->cdata().first(), m_XAxisData->cdata().last()};
158 if (!m_XAxisData->cdata().empty()) {
159 return SqpRange{m_XAxisData->cdata().front(), m_XAxisData->cdata().back()};
160 160 }
161 161
162 162 return SqpRange{};
163 163 }
164 164
165 165 void clear()
166 166 {
167 167 m_XAxisData->clear();
168 168 m_ValuesData->clear();
169 169 }
170 170
171 171 bool isEmpty() const noexcept { return m_XAxisData->size() == 0; }
172 172
173 173 /// Merges into the data series an other data series
174 174 /// @remarks the data series to merge with is cleared after the operation
175 175 void merge(IDataSeries *dataSeries) override
176 176 {
177 177 dataSeries->lockWrite();
178 178 lockWrite();
179 179
180 180 if (auto other = dynamic_cast<DataSeries<Dim> *>(dataSeries)) {
181 181 DataSeriesMergeHelper::merge(*other, *this);
182 182 }
183 183 else {
184 184 qCWarning(LOG_DataSeries())
185 185 << QObject::tr("Detection of a type of IDataSeries we cannot merge with !");
186 186 }
187 187 unlock();
188 188 dataSeries->unlock();
189 189 }
190 190
191 191 void purge(double min, double max) override
192 192 {
193 193 // Nothing to purge if series is empty
194 194 if (isEmpty()) {
195 195 return;
196 196 }
197 197
198 198 if (min > max) {
199 199 std::swap(min, max);
200 200 }
201 201
202 202 // Nothing to purge if series min/max are inside purge range
203 203 auto xMin = cbegin()->x();
204 204 auto xMax = (--cend())->x();
205 205 if (xMin >= min && xMax <= max) {
206 206 return;
207 207 }
208 208
209 209 auto lowerIt = std::lower_bound(
210 210 begin(), end(), min, [](const auto &it, const auto &val) { return it.x() < val; });
211 211 erase(begin(), lowerIt);
212 212 auto upperIt = std::upper_bound(
213 213 begin(), end(), max, [](const auto &val, const auto &it) { return val < it.x(); });
214 214 erase(upperIt, end());
215 215 }
216 216
217 217 // ///////// //
218 218 // Iterators //
219 219 // ///////// //
220 220
221 221 DataSeriesIterator begin() override
222 222 {
223 223 return DataSeriesIterator{DataSeriesIteratorValue{
224 224 std::make_unique<dataseries_detail::IteratorValue<Dim, false> >(*this, true)}};
225 225 }
226 226
227 227 DataSeriesIterator end() override
228 228 {
229 229 return DataSeriesIterator{DataSeriesIteratorValue{
230 230 std::make_unique<dataseries_detail::IteratorValue<Dim, false> >(*this, false)}};
231 231 }
232 232
233 233 DataSeriesIterator cbegin() const override
234 234 {
235 235 return DataSeriesIterator{DataSeriesIteratorValue{
236 236 std::make_unique<dataseries_detail::IteratorValue<Dim, true> >(*this, true)}};
237 237 }
238 238
239 239 DataSeriesIterator cend() const override
240 240 {
241 241 return DataSeriesIterator{DataSeriesIteratorValue{
242 242 std::make_unique<dataseries_detail::IteratorValue<Dim, true> >(*this, false)}};
243 243 }
244 244
245 245 void erase(DataSeriesIterator first, DataSeriesIterator last)
246 246 {
247 247 auto firstImpl
248 248 = dynamic_cast<dataseries_detail::IteratorValue<Dim, false> *>(first->impl());
249 249 auto lastImpl = dynamic_cast<dataseries_detail::IteratorValue<Dim, false> *>(last->impl());
250 250
251 251 if (firstImpl && lastImpl) {
252 252 m_XAxisData->erase(firstImpl->m_XIt, lastImpl->m_XIt);
253 253 m_ValuesData->erase(firstImpl->m_ValuesIt, lastImpl->m_ValuesIt);
254 254 }
255 255 }
256 256
257 257 /// @sa IDataSeries::minXAxisData()
258 258 DataSeriesIterator minXAxisData(double minXAxisData) const override
259 259 {
260 260 return std::lower_bound(
261 261 cbegin(), cend(), minXAxisData,
262 262 [](const auto &itValue, const auto &value) { return itValue.x() < value; });
263 263 }
264 264
265 265 /// @sa IDataSeries::maxXAxisData()
266 266 DataSeriesIterator maxXAxisData(double maxXAxisData) const override
267 267 {
268 268 // Gets the first element that greater than max value
269 269 auto it = std::upper_bound(
270 270 cbegin(), cend(), maxXAxisData,
271 271 [](const auto &value, const auto &itValue) { return value < itValue.x(); });
272 272
273 273 return it == cbegin() ? cend() : --it;
274 274 }
275 275
276 276 std::pair<DataSeriesIterator, DataSeriesIterator> xAxisRange(double minXAxisData,
277 277 double maxXAxisData) const override
278 278 {
279 279 if (minXAxisData > maxXAxisData) {
280 280 std::swap(minXAxisData, maxXAxisData);
281 281 }
282 282
283 283 auto begin = cbegin();
284 284 auto end = cend();
285 285
286 286 auto lowerIt = std::lower_bound(
287 287 begin, end, minXAxisData,
288 288 [](const auto &itValue, const auto &value) { return itValue.x() < value; });
289 289 auto upperIt = std::upper_bound(
290 290 begin, end, maxXAxisData,
291 291 [](const auto &value, const auto &itValue) { return value < itValue.x(); });
292 292
293 293 return std::make_pair(lowerIt, upperIt);
294 294 }
295 295
296 296 std::pair<DataSeriesIterator, DataSeriesIterator>
297 297 valuesBounds(double minXAxisData, double maxXAxisData) const override
298 298 {
299 299 // Places iterators to the correct x-axis range
300 300 auto xAxisRangeIts = xAxisRange(minXAxisData, maxXAxisData);
301 301
302 302 // Returns end iterators if the range is empty
303 303 if (xAxisRangeIts.first == xAxisRangeIts.second) {
304 304 return std::make_pair(cend(), cend());
305 305 }
306 306
307 307 // Gets the iterator on the min of all values data
308 308 auto minIt = std::min_element(
309 309 xAxisRangeIts.first, xAxisRangeIts.second, [](const auto &it1, const auto &it2) {
310 310 return SortUtils::minCompareWithNaN(it1.minValue(), it2.minValue());
311 311 });
312 312
313 313 // Gets the iterator on the max of all values data
314 314 auto maxIt = std::max_element(
315 315 xAxisRangeIts.first, xAxisRangeIts.second, [](const auto &it1, const auto &it2) {
316 316 return SortUtils::maxCompareWithNaN(it1.maxValue(), it2.maxValue());
317 317 });
318 318
319 319 return std::make_pair(minIt, maxIt);
320 320 }
321 321
322 322 // /////// //
323 323 // Mutexes //
324 324 // /////// //
325 325
326 326 virtual void lockRead() { m_Lock.lockForRead(); }
327 327 virtual void lockWrite() { m_Lock.lockForWrite(); }
328 328 virtual void unlock() { m_Lock.unlock(); }
329 329
330 330 // ///// //
331 331 // Other //
332 332 // ///// //
333 333
334 334 /// Inserts at the end of the data series the value of the iterator passed as a parameter. This
335 335 /// method is intended to be used in the context of generating a back insert iterator
336 336 /// @param iteratorValue the iterator value containing the values to insert
337 337 /// @sa http://en.cppreference.com/w/cpp/iterator/back_inserter
338 338 /// @sa merge()
339 339 /// @sa value_type
340 340 void push_back(const value_type &iteratorValue)
341 341 {
342 342 m_XAxisData->push_back(QVector<double>{iteratorValue.x()});
343 343 m_ValuesData->push_back(iteratorValue.values());
344 344 }
345 345
346 346 protected:
347 347 /// Protected ctor (DataSeries is abstract). The vectors must have the same size, otherwise a
348 348 /// DataSeries with no values will be created.
349 349 /// @remarks data series is automatically sorted on its x-axis data
350 350 explicit DataSeries(std::shared_ptr<ArrayData<1> > xAxisData, const Unit &xAxisUnit,
351 351 std::shared_ptr<ArrayData<Dim> > valuesData, const Unit &valuesUnit)
352 352 : m_XAxisData{xAxisData},
353 353 m_XAxisUnit{xAxisUnit},
354 354 m_ValuesData{valuesData},
355 355 m_ValuesUnit{valuesUnit}
356 356 {
357 357 if (m_XAxisData->size() != m_ValuesData->size()) {
358 358 clear();
359 359 }
360 360
361 361 // Sorts data if it's not the case
362 362 const auto &xAxisCData = m_XAxisData->cdata();
363 363 if (!std::is_sorted(xAxisCData.cbegin(), xAxisCData.cend())) {
364 364 sort();
365 365 }
366 366 }
367 367
368 368 /// Copy ctor
369 369 explicit DataSeries(const DataSeries<Dim> &other)
370 370 : m_XAxisData{std::make_shared<ArrayData<1> >(*other.m_XAxisData)},
371 371 m_XAxisUnit{other.m_XAxisUnit},
372 372 m_ValuesData{std::make_shared<ArrayData<Dim> >(*other.m_ValuesData)},
373 373 m_ValuesUnit{other.m_ValuesUnit}
374 374 {
375 375 // Since a series is ordered from its construction and is always ordered, it is not
376 376 // necessary to call the sort method here ('other' is sorted)
377 377 }
378 378
379 379 /// Assignment operator
380 380 template <int D>
381 381 DataSeries &operator=(DataSeries<D> other)
382 382 {
383 383 std::swap(m_XAxisData, other.m_XAxisData);
384 384 std::swap(m_XAxisUnit, other.m_XAxisUnit);
385 385 std::swap(m_ValuesData, other.m_ValuesData);
386 386 std::swap(m_ValuesUnit, other.m_ValuesUnit);
387 387
388 388 return *this;
389 389 }
390 390
391 391 private:
392 392 /**
393 393 * Sorts data series on its x-axis data
394 394 */
395 395 void sort() noexcept
396 396 {
397 397 auto permutation = SortUtils::sortPermutation(*m_XAxisData, std::less<double>());
398 398 m_XAxisData = m_XAxisData->sort(permutation);
399 399 m_ValuesData = m_ValuesData->sort(permutation);
400 400 }
401 401
402 402 std::shared_ptr<ArrayData<1> > m_XAxisData;
403 403 Unit m_XAxisUnit;
404 404 std::shared_ptr<ArrayData<Dim> > m_ValuesData;
405 405 Unit m_ValuesUnit;
406 406
407 407 QReadWriteLock m_Lock;
408 408 };
409 409
410 410 #endif // SCIQLOP_DATASERIES_H
@@ -1,27 +1,27
1 1 #ifndef SCIQLOP_SCALARSERIES_H
2 2 #define SCIQLOP_SCALARSERIES_H
3 3
4 4 #include "CoreGlobal.h"
5 5
6 6 #include <Data/DataSeries.h>
7 7
8 8 /**
9 9 * @brief The ScalarSeries class is the implementation for a data series representing a scalar.
10 10 */
11 11 class SCIQLOP_CORE_EXPORT ScalarSeries : public DataSeries<1> {
12 12 public:
13 13 /**
14 14 * Ctor with two vectors. The vectors must have the same size, otherwise a ScalarSeries with no
15 15 * values will be created.
16 16 * @param xAxisData x-axis data
17 17 * @param valuesData values data
18 18 */
19 explicit ScalarSeries(QVector<double> xAxisData, QVector<double> valuesData,
19 explicit ScalarSeries(std::vector<double> xAxisData, std::vector<double> valuesData,
20 20 const Unit &xAxisUnit, const Unit &valuesUnit);
21 21
22 22 std::unique_ptr<IDataSeries> clone() const override;
23 23
24 24 std::shared_ptr<IDataSeries> subDataSeries(const SqpRange &range) override;
25 25 };
26 26
27 27 #endif // SCIQLOP_SCALARSERIES_H
@@ -1,34 +1,34
1 1 #ifndef SCIQLOP_VECTORSERIES_H
2 2 #define SCIQLOP_VECTORSERIES_H
3 3
4 4 #include "CoreGlobal.h"
5 5
6 6 #include <Data/DataSeries.h>
7 7
8 8 /**
9 9 * @brief The VectorSeries class is the implementation for a data series representing a vector.
10 10 */
11 11 class SCIQLOP_CORE_EXPORT VectorSeries : public DataSeries<2> {
12 12 public:
13 13 /**
14 14 * Ctor with three vectors (one per component). The vectors must have the same size, otherwise a
15 15 * ScalarSeries with no values will be created.
16 16 * @param xAxisData x-axis data
17 17 * @param xvaluesData x-values data
18 18 * @param yvaluesData y-values data
19 19 * @param zvaluesData z-values data
20 20 */
21 explicit VectorSeries(QVector<double> xAxisData, QVector<double> xValuesData,
22 QVector<double> yValuesData, QVector<double> zValuesData,
21 explicit VectorSeries(std::vector<double> xAxisData, std::vector<double> xValuesData,
22 std::vector<double> yValuesData, std::vector<double> zValuesData,
23 23 const Unit &xAxisUnit, const Unit &valuesUnit);
24 24
25 25 /// Default Ctor
26 explicit VectorSeries(QVector<double> xAxisData, QVector<double> valuesData,
26 explicit VectorSeries(std::vector<double> xAxisData, std::vector<double> valuesData,
27 27 const Unit &xAxisUnit, const Unit &valuesUnit);
28 28
29 29 std::unique_ptr<IDataSeries> clone() const;
30 30
31 31 std::shared_ptr<IDataSeries> subDataSeries(const SqpRange &range) override;
32 32 };
33 33
34 34 #endif // SCIQLOP_VECTORSERIES_H
@@ -1,31 +1,31
1 1 #include <Data/ScalarSeries.h>
2 2
3 ScalarSeries::ScalarSeries(QVector<double> xAxisData, QVector<double> valuesData,
3 ScalarSeries::ScalarSeries(std::vector<double> xAxisData, std::vector<double> valuesData,
4 4 const Unit &xAxisUnit, const Unit &valuesUnit)
5 5 : DataSeries{std::make_shared<ArrayData<1> >(std::move(xAxisData)), xAxisUnit,
6 6 std::make_shared<ArrayData<1> >(std::move(valuesData)), valuesUnit}
7 7 {
8 8 }
9 9
10 10 std::unique_ptr<IDataSeries> ScalarSeries::clone() const
11 11 {
12 12 return std::make_unique<ScalarSeries>(*this);
13 13 }
14 14
15 15 std::shared_ptr<IDataSeries> ScalarSeries::subDataSeries(const SqpRange &range)
16 16 {
17 auto subXAxisData = QVector<double>();
18 auto subValuesData = QVector<double>();
17 auto subXAxisData = std::vector<double>();
18 auto subValuesData = std::vector<double>();
19 19 this->lockRead();
20 20 {
21 21 auto bounds = xAxisRange(range.m_TStart, range.m_TEnd);
22 22 for (auto it = bounds.first; it != bounds.second; ++it) {
23 subXAxisData.append(it->x());
24 subValuesData.append(it->value());
23 subXAxisData.push_back(it->x());
24 subValuesData.push_back(it->value());
25 25 }
26 26 }
27 27 this->unlock();
28 28
29 return std::make_shared<ScalarSeries>(subXAxisData, subValuesData, this->xAxisUnit(),
30 this->valuesUnit());
29 return std::make_shared<ScalarSeries>(std::move(subXAxisData), std::move(subValuesData),
30 this->xAxisUnit(), this->valuesUnit());
31 31 }
@@ -1,83 +1,88
1 1 #include "Data/VectorSeries.h"
2 2
3 3 namespace {
4 4
5 5 /**
6 6 * Flatten the three components of a vector to a single QVector that can be passed to an ArrayData
7 7 *
8 8 * Example:
9 9 * xValues = {1, 2, 3}
10 10 * yValues = {4, 5, 6}
11 11 * zValues = {7, 8, 9}
12 12 *
13 13 * result = {1, 4, 7, 2, 5, 8, 3, 6, 9}
14 14 *
15 15 * @param xValues the x-component values of the vector
16 16 * @param yValues the y-component values of the vector
17 17 * @param zValues the z-component values of the vector
18 18 * @return the single QVector
19 19 * @remarks the three components are consumed
20 20 * @sa ArrayData
21 21 */
22 QVector<double> flatten(QVector<double> xValues, QVector<double> yValues, QVector<double> zValues)
22 std::vector<double> flatten(std::vector<double> xValues, std::vector<double> yValues,
23 std::vector<double> zValues)
23 24 {
24 25 if (xValues.size() != yValues.size() || xValues.size() != zValues.size()) {
25 26 /// @todo ALX : log
26 27 return {};
27 28 }
28 29
29 auto result = QVector<double>{};
30 auto result = std::vector<double>();
30 31 result.reserve(xValues.size() * 3);
31 32
32 while (!xValues.isEmpty()) {
33 result.append({xValues.takeFirst(), yValues.takeFirst(), zValues.takeFirst()});
33 while (!xValues.empty()) {
34 result.insert(result.cend(), {xValues.front(), yValues.front(), zValues.front()});
35 xValues.erase(xValues.begin());
36 yValues.erase(yValues.begin());
37 zValues.erase(zValues.begin());
34 38 }
35 39
36 40 return result;
37 41 }
38 42
39 43 } // namespace
40 44
41 VectorSeries::VectorSeries(QVector<double> xAxisData, QVector<double> xValuesData,
42 QVector<double> yValuesData, QVector<double> zValuesData,
45 VectorSeries::VectorSeries(std::vector<double> xAxisData, std::vector<double> xValuesData,
46 std::vector<double> yValuesData, std::vector<double> zValuesData,
43 47 const Unit &xAxisUnit, const Unit &valuesUnit)
44 48 : VectorSeries{std::move(xAxisData), flatten(std::move(xValuesData), std::move(yValuesData),
45 49 std::move(zValuesData)),
46 50 xAxisUnit, valuesUnit}
47 51 {
48 52 }
49 53
50 VectorSeries::VectorSeries(QVector<double> xAxisData, QVector<double> valuesData,
54 VectorSeries::VectorSeries(std::vector<double> xAxisData, std::vector<double> valuesData,
51 55 const Unit &xAxisUnit, const Unit &valuesUnit)
52 56 : DataSeries{std::make_shared<ArrayData<1> >(std::move(xAxisData)), xAxisUnit,
53 57 std::make_shared<ArrayData<2> >(std::move(valuesData), 3), valuesUnit}
54 58 {
55 59 }
56 60
57 61 std::unique_ptr<IDataSeries> VectorSeries::clone() const
58 62 {
59 63 return std::make_unique<VectorSeries>(*this);
60 64 }
61 65
62 66 std::shared_ptr<IDataSeries> VectorSeries::subDataSeries(const SqpRange &range)
63 67 {
64 auto subXAxisData = QVector<double>();
65 auto subXValuesData = QVector<double>();
66 auto subYValuesData = QVector<double>();
67 auto subZValuesData = QVector<double>();
68 auto subXAxisData = std::vector<double>();
69 auto subXValuesData = std::vector<double>();
70 auto subYValuesData = std::vector<double>();
71 auto subZValuesData = std::vector<double>();
68 72
69 73 this->lockRead();
70 74 {
71 75 auto bounds = xAxisRange(range.m_TStart, range.m_TEnd);
72 76 for (auto it = bounds.first; it != bounds.second; ++it) {
73 subXAxisData.append(it->x());
74 subXValuesData.append(it->value(0));
75 subYValuesData.append(it->value(1));
76 subZValuesData.append(it->value(2));
77 subXAxisData.push_back(it->x());
78 subXValuesData.push_back(it->value(0));
79 subYValuesData.push_back(it->value(1));
80 subZValuesData.push_back(it->value(2));
77 81 }
78 82 }
79 83 this->unlock();
80 84
81 return std::make_shared<VectorSeries>(subXAxisData, subXValuesData, subYValuesData,
82 subZValuesData, this->xAxisUnit(), this->valuesUnit());
85 return std::make_shared<VectorSeries>(std::move(subXAxisData), std::move(subXValuesData),
86 std::move(subYValuesData), std::move(subZValuesData),
87 this->xAxisUnit(), this->valuesUnit());
83 88 }
@@ -1,216 +1,217
1 1 #include "AmdaResultParser.h"
2 2
3 3 #include <Common/DateUtils.h>
4 4 #include <Data/ScalarSeries.h>
5 5 #include <Data/VectorSeries.h>
6 6
7 7 #include <QDateTime>
8 8 #include <QFile>
9 9 #include <QRegularExpression>
10 10
11 11 #include <cmath>
12 12
13 13 Q_LOGGING_CATEGORY(LOG_AmdaResultParser, "AmdaResultParser")
14 14
15 15 namespace {
16 16
17 17 /// Message in result file when the file was not found on server
18 18 const auto FILE_NOT_FOUND_MESSAGE = QStringLiteral("Not Found");
19 19
20 20 /// Format for dates in result files
21 21 const auto DATE_FORMAT = QStringLiteral("yyyy-MM-ddThh:mm:ss.zzz");
22 22
23 23 /// Separator between values in a result line
24 24 const auto RESULT_LINE_SEPARATOR = QRegularExpression{QStringLiteral("\\s+")};
25 25
26 26 /// Regex to find unit in a line. Examples of valid lines:
27 27 /// ... - Units : nT - ...
28 28 /// ... -Units:nT- ...
29 29 /// ... -Units: mΒ²- ...
30 30 /// ... - Units : m/s - ...
31 31 const auto UNIT_REGEX = QRegularExpression{QStringLiteral("-\\s*Units\\s*:\\s*(.+?)\\s*-")};
32 32
33 33 /// Converts a string date to a double date
34 34 /// @return a double that represents the date in seconds, NaN if the string date can't be converted
35 35 double doubleDate(const QString &stringDate) noexcept
36 36 {
37 37 auto dateTime = QDateTime::fromString(stringDate, DATE_FORMAT);
38 38 dateTime.setTimeSpec(Qt::UTC);
39 39 return dateTime.isValid() ? DateUtils::secondsSinceEpoch(dateTime)
40 40 : std::numeric_limits<double>::quiet_NaN();
41 41 }
42 42
43 43 /// Checks if a line is a comment line
44 44 bool isCommentLine(const QString &line)
45 45 {
46 46 return line.startsWith("#");
47 47 }
48 48
49 49 /// @return the number of lines to be read depending on the type of value passed in parameter
50 50 int nbValues(AmdaResultParser::ValueType valueType) noexcept
51 51 {
52 52 switch (valueType) {
53 53 case AmdaResultParser::ValueType::SCALAR:
54 54 return 1;
55 55 case AmdaResultParser::ValueType::VECTOR:
56 56 return 3;
57 57 case AmdaResultParser::ValueType::UNKNOWN:
58 58 // Invalid case
59 59 break;
60 60 }
61 61
62 62 // Invalid cases
63 63 qCCritical(LOG_AmdaResultParser())
64 64 << QObject::tr("Can't get the number of values to read: unsupported type");
65 65 return 0;
66 66 }
67 67
68 68 /**
69 69 * Reads stream to retrieve x-axis unit
70 70 * @param stream the stream to read
71 71 * @return the unit that has been read in the stream, a default unit (time unit with no label) if an
72 72 * error occured during reading
73 73 */
74 74 Unit readXAxisUnit(QTextStream &stream)
75 75 {
76 76 QString line{};
77 77
78 78 // Searches unit in the comment lines
79 79 while (stream.readLineInto(&line) && isCommentLine(line)) {
80 80 auto match = UNIT_REGEX.match(line);
81 81 if (match.hasMatch()) {
82 82 return Unit{match.captured(1), true};
83 83 }
84 84 }
85 85
86 86 qCWarning(LOG_AmdaResultParser()) << QObject::tr("The unit could not be found in the file");
87 87
88 88 // Error cases
89 89 return Unit{{}, true};
90 90 }
91 91
92 92 /**
93 93 * Reads stream to retrieve results
94 94 * @param stream the stream to read
95 95 * @return the pair of vectors x-axis data/values data that has been read in the stream
96 96 */
97 97 QPair<QVector<double>, QVector<QVector<double> > >
98 98 readResults(QTextStream &stream, AmdaResultParser::ValueType valueType)
99 99 {
100 100 auto expectedNbValues = nbValues(valueType);
101 101
102 102 auto xData = QVector<double>{};
103 103 auto valuesData = QVector<QVector<double> >(expectedNbValues);
104 104
105 105 QString line{};
106 106
107 107 while (stream.readLineInto(&line)) {
108 108 // Ignore comment lines
109 109 if (!isCommentLine(line)) {
110 110 auto lineData = line.split(RESULT_LINE_SEPARATOR, QString::SkipEmptyParts);
111 111 if (lineData.size() == expectedNbValues + 1) {
112 112 // X : the data is converted from date to double (in secs)
113 113 auto x = doubleDate(lineData.at(0));
114 114
115 115 // Adds result only if x is valid. Then, if value is invalid, it is set to NaN
116 116 if (!std::isnan(x)) {
117 117 xData.push_back(x);
118 118
119 119 // Values
120 120 for (auto valueIndex = 0; valueIndex < expectedNbValues; ++valueIndex) {
121 121 auto column = valueIndex + 1;
122 122
123 123 bool valueOk;
124 124 auto value = lineData.at(column).toDouble(&valueOk);
125 125
126 126 if (!valueOk) {
127 127 qCWarning(LOG_AmdaResultParser())
128 128 << QObject::tr(
129 129 "Value from (line %1, column %2) is invalid and will be "
130 130 "converted to NaN")
131 131 .arg(line, column);
132 132 value = std::numeric_limits<double>::quiet_NaN();
133 133 }
134 134 valuesData[valueIndex].append(value);
135 135 }
136 136 }
137 137 else {
138 138 qCWarning(LOG_AmdaResultParser())
139 139 << QObject::tr("Can't retrieve results from line %1: x is invalid")
140 140 .arg(line);
141 141 }
142 142 }
143 143 else {
144 144 qCWarning(LOG_AmdaResultParser())
145 145 << QObject::tr("Can't retrieve results from line %1: invalid line").arg(line);
146 146 }
147 147 }
148 148 }
149 149
150 150 return qMakePair(std::move(xData), std::move(valuesData));
151 151 }
152 152
153 153 } // namespace
154 154
155 155 std::shared_ptr<IDataSeries> AmdaResultParser::readTxt(const QString &filePath,
156 156 ValueType valueType) noexcept
157 157 {
158 158 if (valueType == ValueType::UNKNOWN) {
159 159 qCCritical(LOG_AmdaResultParser())
160 160 << QObject::tr("Can't retrieve AMDA data: the type of values to be read is unknown");
161 161 return nullptr;
162 162 }
163 163
164 164 QFile file{filePath};
165 165
166 166 if (!file.open(QFile::ReadOnly | QIODevice::Text)) {
167 167 qCCritical(LOG_AmdaResultParser())
168 168 << QObject::tr("Can't retrieve AMDA data from file %1: %2")
169 169 .arg(filePath, file.errorString());
170 170 return nullptr;
171 171 }
172 172
173 173 QTextStream stream{&file};
174 174
175 175 // Checks if the file was found on the server
176 176 auto firstLine = stream.readLine();
177 177 if (firstLine.compare(FILE_NOT_FOUND_MESSAGE) == 0) {
178 178 qCCritical(LOG_AmdaResultParser())
179 179 << QObject::tr("Can't retrieve AMDA data from file %1: file was not found on server")
180 180 .arg(filePath);
181 181 return nullptr;
182 182 }
183 183
184 184 // Reads x-axis unit
185 185 stream.seek(0); // returns to the beginning of the file
186 186 auto xAxisUnit = readXAxisUnit(stream);
187 187
188 188 // Reads results
189 189 stream.seek(0); // returns to the beginning of the file
190 190 auto results = readResults(stream, valueType);
191 191
192 192 // Creates data series
193 193 switch (valueType) {
194 194 case ValueType::SCALAR:
195 195 Q_ASSERT(results.second.size() == 1);
196 196 return std::make_shared<ScalarSeries>(
197 std::move(results.first), std::move(results.second.takeFirst()), xAxisUnit, Unit{});
197 std::move(results.first.toStdVector()),
198 std::move(results.second.takeFirst().toStdVector()), xAxisUnit, Unit{});
198 199 case ValueType::VECTOR: {
199 200 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{});
201 auto xValues = results.second.takeFirst().toStdVector();
202 auto yValues = results.second.takeFirst().toStdVector();
203 auto zValues = results.second.takeFirst().toStdVector();
204 return std::make_shared<VectorSeries>(std::move(results.first.toStdVector()),
205 std::move(xValues), std::move(yValues),
206 std::move(zValues), xAxisUnit, Unit{});
206 207 }
207 208 case ValueType::UNKNOWN:
208 209 // Invalid case
209 210 break;
210 211 }
211 212
212 213 // Invalid cases
213 214 qCCritical(LOG_AmdaResultParser())
214 215 << QObject::tr("Can't create data series: unsupported value type");
215 216 return nullptr;
216 217 }
@@ -1,103 +1,103
1 1 #include "CosinusProvider.h"
2 2
3 3 #include <Data/DataProviderParameters.h>
4 4 #include <Data/ScalarSeries.h>
5 5
6 6 #include <cmath>
7 7
8 8 #include <QFuture>
9 9 #include <QThread>
10 10 #include <QtConcurrent/QtConcurrent>
11 11
12 12 Q_LOGGING_CATEGORY(LOG_CosinusProvider, "CosinusProvider")
13 13
14 14 std::shared_ptr<IDataSeries> CosinusProvider::retrieveData(QUuid acqIdentifier,
15 15 const SqpRange &dataRangeRequested)
16 16 {
17 17 // TODO: Add Mutex
18 18 auto dataIndex = 0;
19 19
20 20 // Gets the timerange from the parameters
21 double freq = 1.0;
21 double freq = 100.0;
22 22 double start = std::ceil(dataRangeRequested.m_TStart * freq); // 100 htz
23 23 double end = std::floor(dataRangeRequested.m_TEnd * freq); // 100 htz
24 24
25 25 // We assure that timerange is valid
26 26 if (end < start) {
27 27 std::swap(start, end);
28 28 }
29 29
30 30 // Generates scalar series containing cosinus values (one value per second)
31 31 auto dataCount = end - start;
32 32
33 auto xAxisData = QVector<double>{};
33 auto xAxisData = std::vector<double>{};
34 34 xAxisData.resize(dataCount);
35 35
36 auto valuesData = QVector<double>{};
36 auto valuesData = std::vector<double>{};
37 37 valuesData.resize(dataCount);
38 38
39 39 int progress = 0;
40 40 auto progressEnd = dataCount;
41 41 for (auto time = start; time < end; ++time, ++dataIndex) {
42 42 auto it = m_VariableToEnableProvider.find(acqIdentifier);
43 43 if (it != m_VariableToEnableProvider.end() && it.value()) {
44 44 const auto timeOnFreq = time / freq;
45 45
46 xAxisData.replace(dataIndex, timeOnFreq);
47 valuesData.replace(dataIndex, std::cos(timeOnFreq));
46 xAxisData[dataIndex] = timeOnFreq;
47 valuesData[dataIndex] = std::cos(timeOnFreq);
48 48
49 49 // progression
50 50 int currentProgress = (time - start) * 100.0 / progressEnd;
51 51 if (currentProgress != progress) {
52 52 progress = currentProgress;
53 53
54 54 emit dataProvidedProgress(acqIdentifier, progress);
55 55 }
56 56 }
57 57 else {
58 58 if (!it.value()) {
59 59 qCDebug(LOG_CosinusProvider())
60 60 << "CosinusProvider::retrieveData: ARRET De l'acquisition detectΓ©"
61 61 << end - time;
62 62 }
63 63 }
64 64 }
65 65 emit dataProvidedProgress(acqIdentifier, 0.0);
66 66
67 67 return std::make_shared<ScalarSeries>(std::move(xAxisData), std::move(valuesData),
68 68 Unit{QStringLiteral("t"), true}, Unit{});
69 69 }
70 70
71 71 void CosinusProvider::requestDataLoading(QUuid acqIdentifier,
72 72 const DataProviderParameters &parameters)
73 73 {
74 74 // TODO: Add Mutex
75 75 m_VariableToEnableProvider[acqIdentifier] = true;
76 76 qCDebug(LOG_CosinusProvider()) << "TORM: CosinusProvider::requestDataLoading"
77 77 << QThread::currentThread()->objectName();
78 78 // NOTE: Try to use multithread if possible
79 79 const auto times = parameters.m_Times;
80 80
81 81 for (const auto &dateTime : qAsConst(times)) {
82 82 if (m_VariableToEnableProvider[acqIdentifier]) {
83 83 auto scalarSeries = this->retrieveData(acqIdentifier, dateTime);
84 84 qCDebug(LOG_CosinusProvider()) << "TORM: CosinusProvider::dataProvided";
85 85 emit dataProvided(acqIdentifier, scalarSeries, dateTime);
86 86 }
87 87 }
88 88 }
89 89
90 90 void CosinusProvider::requestDataAborting(QUuid acqIdentifier)
91 91 {
92 92 // TODO: Add Mutex
93 93 qCDebug(LOG_CosinusProvider()) << "CosinusProvider::requestDataAborting" << acqIdentifier
94 94 << QThread::currentThread()->objectName();
95 95 auto it = m_VariableToEnableProvider.find(acqIdentifier);
96 96 if (it != m_VariableToEnableProvider.end()) {
97 97 it.value() = false;
98 98 }
99 99 else {
100 100 qCWarning(LOG_CosinusProvider())
101 101 << tr("Aborting progression of inexistant identifier detected !!!");
102 102 }
103 103 }
General Comments 0
You need to be logged in to leave comments. Login now