##// END OF EJS Templates
Merge pull request 305 from SciQLop-fork develop...
perrinel -
r806:a502cf5c12a9 merge
parent child
Show More
@@ -1,400 +1,391
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 result->next(offset);
85 85 return result;
86 86 }
87 87
88 88 void next(int offset) override
89 89 {
90 90 m_XIt->next(offset);
91 91 m_ValuesIt->next(offset);
92 92 }
93 93
94 94 void prev() override
95 95 {
96 96 --m_XIt;
97 97 --m_ValuesIt;
98 98 }
99 99
100 100 double x() const override { return m_XIt->at(0); }
101 101 double value() const override { return m_ValuesIt->at(0); }
102 102 double value(int componentIndex) const override { return m_ValuesIt->at(componentIndex); }
103 103 double minValue() const override { return m_ValuesIt->min(); }
104 104 double maxValue() const override { return m_ValuesIt->max(); }
105 105 QVector<double> values() const override { return m_ValuesIt->values(); }
106 106
107 107 void swap(DataSeriesIteratorValue::Impl &other) override
108 108 {
109 109 auto &otherImpl = dynamic_cast<IteratorValue &>(other);
110 110 m_XIt->impl()->swap(*otherImpl.m_XIt->impl());
111 111 m_ValuesIt->impl()->swap(*otherImpl.m_ValuesIt->impl());
112 112 }
113 113
114 114 private:
115 115 ArrayDataIterator m_XIt;
116 116 ArrayDataIterator m_ValuesIt;
117 117 };
118 118 } // namespace dataseries_detail
119 119
120 120 /**
121 121 * @brief The DataSeries class is the base (abstract) implementation of IDataSeries.
122 122 *
123 123 * It proposes to set a dimension for the values ​​data.
124 124 *
125 125 * A DataSeries is always sorted on its x-axis data.
126 126 *
127 127 * @tparam Dim The dimension of the values data
128 128 *
129 129 */
130 130 template <int Dim>
131 131 class SCIQLOP_CORE_EXPORT DataSeries : public IDataSeries {
132 132 friend class DataSeriesMergeHelper;
133 133
134 134 public:
135 135 /// @sa IDataSeries::xAxisData()
136 136 std::shared_ptr<ArrayData<1> > xAxisData() override { return m_XAxisData; }
137 137 const std::shared_ptr<ArrayData<1> > xAxisData() const { return m_XAxisData; }
138 138
139 139 /// @sa IDataSeries::xAxisUnit()
140 140 Unit xAxisUnit() const override { return m_XAxisUnit; }
141 141
142 142 /// @return the values dataset
143 143 std::shared_ptr<ArrayData<Dim> > valuesData() { return m_ValuesData; }
144 144 const std::shared_ptr<ArrayData<Dim> > valuesData() const { return m_ValuesData; }
145 145
146 146 /// @sa IDataSeries::valuesUnit()
147 147 Unit valuesUnit() const override { return m_ValuesUnit; }
148 148
149 149 int nbPoints() const override { return m_XAxisData->totalSize() + m_ValuesData->totalSize(); }
150 150
151 SqpRange range() const override
152 {
153 if (!m_XAxisData->cdata().empty()) {
154 return SqpRange{m_XAxisData->cdata().front(), m_XAxisData->cdata().back()};
155 }
156
157 return SqpRange{};
158 }
159
160 151 void clear()
161 152 {
162 153 m_XAxisData->clear();
163 154 m_ValuesData->clear();
164 155 }
165 156
166 157 bool isEmpty() const noexcept { return m_XAxisData->size() == 0; }
167 158
168 159 /// Merges into the data series an other data series
169 160 /// @remarks the data series to merge with is cleared after the operation
170 161 void merge(IDataSeries *dataSeries) override
171 162 {
172 163 dataSeries->lockWrite();
173 164 lockWrite();
174 165
175 166 if (auto other = dynamic_cast<DataSeries<Dim> *>(dataSeries)) {
176 167 DataSeriesMergeHelper::merge(*other, *this);
177 168 }
178 169 else {
179 170 qCWarning(LOG_DataSeries())
180 171 << QObject::tr("Detection of a type of IDataSeries we cannot merge with !");
181 172 }
182 173 unlock();
183 174 dataSeries->unlock();
184 175 }
185 176
186 177 void purge(double min, double max) override
187 178 {
188 179 // Nothing to purge if series is empty
189 180 if (isEmpty()) {
190 181 return;
191 182 }
192 183
193 184 if (min > max) {
194 185 std::swap(min, max);
195 186 }
196 187
197 188 // Nothing to purge if series min/max are inside purge range
198 189 auto xMin = cbegin()->x();
199 190 auto xMax = (--cend())->x();
200 191 if (xMin >= min && xMax <= max) {
201 192 return;
202 193 }
203 194
204 195 auto lowerIt = std::lower_bound(
205 196 begin(), end(), min, [](const auto &it, const auto &val) { return it.x() < val; });
206 197 erase(begin(), lowerIt);
207 198 auto upperIt = std::upper_bound(
208 199 begin(), end(), max, [](const auto &val, const auto &it) { return val < it.x(); });
209 200 erase(upperIt, end());
210 201 }
211 202
212 203 // ///////// //
213 204 // Iterators //
214 205 // ///////// //
215 206
216 207 DataSeriesIterator begin() override
217 208 {
218 209 return DataSeriesIterator{DataSeriesIteratorValue{
219 210 std::make_unique<dataseries_detail::IteratorValue<Dim, false> >(*this, true)}};
220 211 }
221 212
222 213 DataSeriesIterator end() override
223 214 {
224 215 return DataSeriesIterator{DataSeriesIteratorValue{
225 216 std::make_unique<dataseries_detail::IteratorValue<Dim, false> >(*this, false)}};
226 217 }
227 218
228 219 DataSeriesIterator cbegin() const override
229 220 {
230 221 return DataSeriesIterator{DataSeriesIteratorValue{
231 222 std::make_unique<dataseries_detail::IteratorValue<Dim, true> >(*this, true)}};
232 223 }
233 224
234 225 DataSeriesIterator cend() const override
235 226 {
236 227 return DataSeriesIterator{DataSeriesIteratorValue{
237 228 std::make_unique<dataseries_detail::IteratorValue<Dim, true> >(*this, false)}};
238 229 }
239 230
240 231 void erase(DataSeriesIterator first, DataSeriesIterator last)
241 232 {
242 233 auto firstImpl
243 234 = dynamic_cast<dataseries_detail::IteratorValue<Dim, false> *>(first->impl());
244 235 auto lastImpl = dynamic_cast<dataseries_detail::IteratorValue<Dim, false> *>(last->impl());
245 236
246 237 if (firstImpl && lastImpl) {
247 238 m_XAxisData->erase(firstImpl->m_XIt, lastImpl->m_XIt);
248 239 m_ValuesData->erase(firstImpl->m_ValuesIt, lastImpl->m_ValuesIt);
249 240 }
250 241 }
251 242
252 243 void insert(DataSeriesIterator first, DataSeriesIterator last, bool prepend = false)
253 244 {
254 245 auto firstImpl = dynamic_cast<dataseries_detail::IteratorValue<Dim, true> *>(first->impl());
255 246 auto lastImpl = dynamic_cast<dataseries_detail::IteratorValue<Dim, true> *>(last->impl());
256 247
257 248 if (firstImpl && lastImpl) {
258 249 m_XAxisData->insert(firstImpl->m_XIt, lastImpl->m_XIt, prepend);
259 250 m_ValuesData->insert(firstImpl->m_ValuesIt, lastImpl->m_ValuesIt, prepend);
260 251 }
261 252 }
262 253
263 254 /// @sa IDataSeries::minXAxisData()
264 255 DataSeriesIterator minXAxisData(double minXAxisData) const override
265 256 {
266 257 return std::lower_bound(
267 258 cbegin(), cend(), minXAxisData,
268 259 [](const auto &itValue, const auto &value) { return itValue.x() < value; });
269 260 }
270 261
271 262 /// @sa IDataSeries::maxXAxisData()
272 263 DataSeriesIterator maxXAxisData(double maxXAxisData) const override
273 264 {
274 265 // Gets the first element that greater than max value
275 266 auto it = std::upper_bound(
276 267 cbegin(), cend(), maxXAxisData,
277 268 [](const auto &value, const auto &itValue) { return value < itValue.x(); });
278 269
279 270 return it == cbegin() ? cend() : --it;
280 271 }
281 272
282 273 std::pair<DataSeriesIterator, DataSeriesIterator> xAxisRange(double minXAxisData,
283 274 double maxXAxisData) const override
284 275 {
285 276 if (minXAxisData > maxXAxisData) {
286 277 std::swap(minXAxisData, maxXAxisData);
287 278 }
288 279
289 280 auto begin = cbegin();
290 281 auto end = cend();
291 282
292 283 auto lowerIt = std::lower_bound(
293 284 begin, end, minXAxisData,
294 285 [](const auto &itValue, const auto &value) { return itValue.x() < value; });
295 286 auto upperIt = std::upper_bound(
296 287 lowerIt, end, maxXAxisData,
297 288 [](const auto &value, const auto &itValue) { return value < itValue.x(); });
298 289
299 290 return std::make_pair(lowerIt, upperIt);
300 291 }
301 292
302 293 std::pair<DataSeriesIterator, DataSeriesIterator>
303 294 valuesBounds(double minXAxisData, double maxXAxisData) const override
304 295 {
305 296 // Places iterators to the correct x-axis range
306 297 auto xAxisRangeIts = xAxisRange(minXAxisData, maxXAxisData);
307 298
308 299 // Returns end iterators if the range is empty
309 300 if (xAxisRangeIts.first == xAxisRangeIts.second) {
310 301 return std::make_pair(cend(), cend());
311 302 }
312 303
313 304 // Gets the iterator on the min of all values data
314 305 auto minIt = std::min_element(
315 306 xAxisRangeIts.first, xAxisRangeIts.second, [](const auto &it1, const auto &it2) {
316 307 return SortUtils::minCompareWithNaN(it1.minValue(), it2.minValue());
317 308 });
318 309
319 310 // Gets the iterator on the max of all values data
320 311 auto maxIt = std::max_element(
321 312 xAxisRangeIts.first, xAxisRangeIts.second, [](const auto &it1, const auto &it2) {
322 313 return SortUtils::maxCompareWithNaN(it1.maxValue(), it2.maxValue());
323 314 });
324 315
325 316 return std::make_pair(minIt, maxIt);
326 317 }
327 318
328 319 // /////// //
329 320 // Mutexes //
330 321 // /////// //
331 322
332 323 virtual void lockRead() { m_Lock.lockForRead(); }
333 324 virtual void lockWrite() { m_Lock.lockForWrite(); }
334 325 virtual void unlock() { m_Lock.unlock(); }
335 326
336 327 protected:
337 328 /// Protected ctor (DataSeries is abstract). The vectors must have the same size, otherwise a
338 329 /// DataSeries with no values will be created.
339 330 /// @remarks data series is automatically sorted on its x-axis data
340 331 explicit DataSeries(std::shared_ptr<ArrayData<1> > xAxisData, const Unit &xAxisUnit,
341 332 std::shared_ptr<ArrayData<Dim> > valuesData, const Unit &valuesUnit)
342 333 : m_XAxisData{xAxisData},
343 334 m_XAxisUnit{xAxisUnit},
344 335 m_ValuesData{valuesData},
345 336 m_ValuesUnit{valuesUnit}
346 337 {
347 338 if (m_XAxisData->size() != m_ValuesData->size()) {
348 339 clear();
349 340 }
350 341
351 342 // Sorts data if it's not the case
352 343 const auto &xAxisCData = m_XAxisData->cdata();
353 344 if (!std::is_sorted(xAxisCData.cbegin(), xAxisCData.cend())) {
354 345 sort();
355 346 }
356 347 }
357 348
358 349 /// Copy ctor
359 350 explicit DataSeries(const DataSeries<Dim> &other)
360 351 : m_XAxisData{std::make_shared<ArrayData<1> >(*other.m_XAxisData)},
361 352 m_XAxisUnit{other.m_XAxisUnit},
362 353 m_ValuesData{std::make_shared<ArrayData<Dim> >(*other.m_ValuesData)},
363 354 m_ValuesUnit{other.m_ValuesUnit}
364 355 {
365 356 // Since a series is ordered from its construction and is always ordered, it is not
366 357 // necessary to call the sort method here ('other' is sorted)
367 358 }
368 359
369 360 /// Assignment operator
370 361 template <int D>
371 362 DataSeries &operator=(DataSeries<D> other)
372 363 {
373 364 std::swap(m_XAxisData, other.m_XAxisData);
374 365 std::swap(m_XAxisUnit, other.m_XAxisUnit);
375 366 std::swap(m_ValuesData, other.m_ValuesData);
376 367 std::swap(m_ValuesUnit, other.m_ValuesUnit);
377 368
378 369 return *this;
379 370 }
380 371
381 372 private:
382 373 /**
383 374 * Sorts data series on its x-axis data
384 375 */
385 376 void sort() noexcept
386 377 {
387 378 auto permutation = SortUtils::sortPermutation(*m_XAxisData, std::less<double>());
388 379 m_XAxisData = m_XAxisData->sort(permutation);
389 380 m_ValuesData = m_ValuesData->sort(permutation);
390 381 }
391 382
392 383 std::shared_ptr<ArrayData<1> > m_XAxisData;
393 384 Unit m_XAxisUnit;
394 385 std::shared_ptr<ArrayData<Dim> > m_ValuesData;
395 386 Unit m_ValuesUnit;
396 387
397 388 QReadWriteLock m_Lock;
398 389 };
399 390
400 391 #endif // SCIQLOP_DATASERIES_H
@@ -1,113 +1,111
1 1 #ifndef SCIQLOP_IDATASERIES_H
2 2 #define SCIQLOP_IDATASERIES_H
3 3
4 4 #include <Common/MetaTypes.h>
5 5 #include <Data/DataSeriesIterator.h>
6 6 #include <Data/SqpRange.h>
7 7
8 8 #include <memory>
9 9
10 10 #include <QString>
11 11
12 12 template <int Dim>
13 13 class ArrayData;
14 14
15 15 struct Unit {
16 16 explicit Unit(const QString &name = {}, bool timeUnit = false)
17 17 : m_Name{name}, m_TimeUnit{timeUnit}
18 18 {
19 19 }
20 20
21 21 inline bool operator==(const Unit &other) const
22 22 {
23 23 return std::tie(m_Name, m_TimeUnit) == std::tie(other.m_Name, other.m_TimeUnit);
24 24 }
25 25 inline bool operator!=(const Unit &other) const { return !(*this == other); }
26 26
27 27 QString m_Name; ///< Unit name
28 28 bool m_TimeUnit; ///< The unit is a unit of time (UTC)
29 29 };
30 30
31 31 /**
32 32 * @brief The IDataSeries aims to declare a data series.
33 33 *
34 34 * A data series is an entity that contains at least :
35 35 * - one dataset representing the x-axis
36 36 * - one dataset representing the values
37 37 *
38 38 * Each dataset is represented by an ArrayData, and is associated with a unit.
39 39 *
40 40 * An ArrayData can be unidimensional or two-dimensional, depending on the implementation of the
41 41 * IDataSeries. The x-axis dataset is always unidimensional.
42 42 *
43 43 * @sa ArrayData
44 44 */
45 45 class IDataSeries {
46 46 public:
47 47 virtual ~IDataSeries() noexcept = default;
48 48
49 49 /// Returns the x-axis dataset
50 50 virtual std::shared_ptr<ArrayData<1> > xAxisData() = 0;
51 51
52 52 /// Returns the x-axis dataset (as const)
53 53 virtual const std::shared_ptr<ArrayData<1> > xAxisData() const = 0;
54 54
55 55 virtual Unit xAxisUnit() const = 0;
56 56
57 57 virtual Unit valuesUnit() const = 0;
58 58
59 59 virtual void merge(IDataSeries *dataSeries) = 0;
60 60 /// Removes from data series all entries whose value on the x-axis is not between min and max
61 61 virtual void purge(double min, double max) = 0;
62 62
63 63 /// @todo Review the name and signature of this method
64 64 virtual std::shared_ptr<IDataSeries> subDataSeries(const SqpRange &range) = 0;
65 65
66 66 virtual std::unique_ptr<IDataSeries> clone() const = 0;
67 67
68 68 /// @return the total number of points contained in the data series
69 69 virtual int nbPoints() const = 0;
70 70
71 virtual SqpRange range() const = 0;
72
73 71 // ///////// //
74 72 // Iterators //
75 73 // ///////// //
76 74
77 75 virtual DataSeriesIterator cbegin() const = 0;
78 76 virtual DataSeriesIterator cend() const = 0;
79 77 virtual DataSeriesIterator begin() = 0;
80 78 virtual DataSeriesIterator end() = 0;
81 79
82 80 /// @return the iterator to the first entry of the data series whose x-axis data is greater than
83 81 /// or equal to the value passed in parameter, or the end iterator if there is no matching value
84 82 virtual DataSeriesIterator minXAxisData(double minXAxisData) const = 0;
85 83
86 84 /// @return the iterator to the last entry of the data series whose x-axis data is less than or
87 85 /// equal to the value passed in parameter, or the end iterator if there is no matching value
88 86 virtual DataSeriesIterator maxXAxisData(double maxXAxisData) const = 0;
89 87
90 88 /// @return the iterators pointing to the range of data whose x-axis values are between min and
91 89 /// max passed in parameters
92 90 virtual std::pair<DataSeriesIterator, DataSeriesIterator>
93 91 xAxisRange(double minXAxisData, double maxXAxisData) const = 0;
94 92
95 93 /// @return two iterators pointing to the data that have respectively the min and the max value
96 94 /// data of a data series' range. The search is performed for a given x-axis range.
97 95 /// @sa xAxisRange()
98 96 virtual std::pair<DataSeriesIterator, DataSeriesIterator>
99 97 valuesBounds(double minXAxisData, double maxXAxisData) const = 0;
100 98
101 99 // /////// //
102 100 // Mutexes //
103 101 // /////// //
104 102
105 103 virtual void lockRead() = 0;
106 104 virtual void lockWrite() = 0;
107 105 virtual void unlock() = 0;
108 106 };
109 107
110 108 // Required for using shared_ptr in signals/slots
111 109 SCIQLOP_REGISTER_META_TYPE(IDATASERIES_PTR_REGISTRY, std::shared_ptr<IDataSeries>)
112 110
113 111 #endif // SCIQLOP_IDATASERIES_H
@@ -1,842 +1,840
1 1 #include <Variable/Variable.h>
2 2 #include <Variable/VariableAcquisitionWorker.h>
3 3 #include <Variable/VariableCacheStrategy.h>
4 4 #include <Variable/VariableCacheStrategyFactory.h>
5 5 #include <Variable/VariableController.h>
6 6 #include <Variable/VariableModel.h>
7 7 #include <Variable/VariableSynchronizationGroup.h>
8 8
9 9 #include <Data/DataProviderParameters.h>
10 10 #include <Data/IDataProvider.h>
11 11 #include <Data/IDataSeries.h>
12 12 #include <Data/VariableRequest.h>
13 13 #include <Time/TimeController.h>
14 14
15 15 #include <QMutex>
16 16 #include <QThread>
17 17 #include <QUuid>
18 18 #include <QtCore/QItemSelectionModel>
19 19
20 20 #include <deque>
21 21 #include <set>
22 22 #include <unordered_map>
23 23
24 24 Q_LOGGING_CATEGORY(LOG_VariableController, "VariableController")
25 25
26 26 namespace {
27 27
28 28 SqpRange computeSynchroRangeRequested(const SqpRange &varRange, const SqpRange &graphRange,
29 29 const SqpRange &oldGraphRange)
30 30 {
31 31 auto zoomType = VariableController::getZoomType(graphRange, oldGraphRange);
32 32
33 33 auto varRangeRequested = varRange;
34 34 switch (zoomType) {
35 35 case AcquisitionZoomType::ZoomIn: {
36 36 auto deltaLeft = graphRange.m_TStart - oldGraphRange.m_TStart;
37 37 auto deltaRight = oldGraphRange.m_TEnd - graphRange.m_TEnd;
38 38 varRangeRequested.m_TStart += deltaLeft;
39 39 varRangeRequested.m_TEnd -= deltaRight;
40 40 break;
41 41 }
42 42
43 43 case AcquisitionZoomType::ZoomOut: {
44 44 auto deltaLeft = oldGraphRange.m_TStart - graphRange.m_TStart;
45 45 auto deltaRight = graphRange.m_TEnd - oldGraphRange.m_TEnd;
46 46 varRangeRequested.m_TStart -= deltaLeft;
47 47 varRangeRequested.m_TEnd += deltaRight;
48 48 break;
49 49 }
50 50 case AcquisitionZoomType::PanRight: {
51 51 auto deltaRight = graphRange.m_TEnd - oldGraphRange.m_TEnd;
52 52 varRangeRequested.m_TStart += deltaRight;
53 53 varRangeRequested.m_TEnd += deltaRight;
54 54 break;
55 55 }
56 56 case AcquisitionZoomType::PanLeft: {
57 57 auto deltaLeft = oldGraphRange.m_TStart - graphRange.m_TStart;
58 58 varRangeRequested.m_TStart -= deltaLeft;
59 59 varRangeRequested.m_TEnd -= deltaLeft;
60 60 break;
61 61 }
62 62 case AcquisitionZoomType::Unknown: {
63 63 qCCritical(LOG_VariableController())
64 64 << VariableController::tr("Impossible to synchronize: zoom type unknown");
65 65 break;
66 66 }
67 67 default:
68 68 qCCritical(LOG_VariableController()) << VariableController::tr(
69 69 "Impossible to synchronize: zoom type not take into account");
70 70 // No action
71 71 break;
72 72 }
73 73
74 74 return varRangeRequested;
75 75 }
76 76 }
77 77
78 78 struct VariableController::VariableControllerPrivate {
79 79 explicit VariableControllerPrivate(VariableController *parent)
80 80 : m_WorkingMutex{},
81 81 m_VariableModel{new VariableModel{parent}},
82 82 m_VariableSelectionModel{new QItemSelectionModel{m_VariableModel, parent}},
83 83 // m_VariableCacheStrategy{std::make_unique<VariableCacheStrategy>()},
84 84 m_VariableCacheStrategy{VariableCacheStrategyFactory::createCacheStrategy(
85 85 CacheStrategy::SingleThreshold)},
86 86 m_VariableAcquisitionWorker{std::make_unique<VariableAcquisitionWorker>()},
87 87 q{parent}
88 88 {
89 89
90 90 m_VariableAcquisitionWorker->moveToThread(&m_VariableAcquisitionWorkerThread);
91 91 m_VariableAcquisitionWorkerThread.setObjectName("VariableAcquisitionWorkerThread");
92 92 }
93 93
94 94
95 95 virtual ~VariableControllerPrivate()
96 96 {
97 97 qCDebug(LOG_VariableController()) << tr("VariableControllerPrivate destruction");
98 98 m_VariableAcquisitionWorkerThread.quit();
99 99 m_VariableAcquisitionWorkerThread.wait();
100 100 }
101 101
102 102
103 103 void processRequest(std::shared_ptr<Variable> var, const SqpRange &rangeRequested,
104 104 QUuid varRequestId);
105 105
106 106 QVector<SqpRange> provideNotInCacheDateTimeList(std::shared_ptr<Variable> variable,
107 107 const SqpRange &dateTime);
108 108
109 109 std::shared_ptr<Variable> findVariable(QUuid vIdentifier);
110 110 std::shared_ptr<IDataSeries>
111 111 retrieveDataSeries(const QVector<AcquisitionDataPacket> acqDataPacketVector);
112 112
113 113 void registerProvider(std::shared_ptr<IDataProvider> provider);
114 114
115 115 void storeVariableRequest(QUuid varId, QUuid varRequestId, const VariableRequest &varRequest);
116 116 QUuid acceptVariableRequest(QUuid varId, std::shared_ptr<IDataSeries> dataSeries);
117 117 void updateVariableRequest(QUuid varRequestId);
118 118 void cancelVariableRequest(QUuid varRequestId);
119 119
120 120 QMutex m_WorkingMutex;
121 121 /// Variable model. The VariableController has the ownership
122 122 VariableModel *m_VariableModel;
123 123 QItemSelectionModel *m_VariableSelectionModel;
124 124
125 125
126 126 TimeController *m_TimeController{nullptr};
127 127 std::unique_ptr<VariableCacheStrategy> m_VariableCacheStrategy;
128 128 std::unique_ptr<VariableAcquisitionWorker> m_VariableAcquisitionWorker;
129 129 QThread m_VariableAcquisitionWorkerThread;
130 130
131 131 std::unordered_map<std::shared_ptr<Variable>, std::shared_ptr<IDataProvider> >
132 132 m_VariableToProviderMap;
133 133 std::unordered_map<std::shared_ptr<Variable>, QUuid> m_VariableToIdentifierMap;
134 134 std::map<QUuid, std::shared_ptr<VariableSynchronizationGroup> >
135 135 m_GroupIdToVariableSynchronizationGroupMap;
136 136 std::map<QUuid, QUuid> m_VariableIdGroupIdMap;
137 137 std::set<std::shared_ptr<IDataProvider> > m_ProviderSet;
138 138
139 139 std::map<QUuid, std::map<QUuid, VariableRequest> > m_VarRequestIdToVarIdVarRequestMap;
140 140
141 141 std::map<QUuid, std::deque<QUuid> > m_VarIdToVarRequestIdQueueMap;
142 142
143 143
144 144 VariableController *q;
145 145 };
146 146
147 147
148 148 VariableController::VariableController(QObject *parent)
149 149 : QObject{parent}, impl{spimpl::make_unique_impl<VariableControllerPrivate>(this)}
150 150 {
151 151 qCDebug(LOG_VariableController()) << tr("VariableController construction")
152 152 << QThread::currentThread();
153 153
154 154 connect(impl->m_VariableModel, &VariableModel::abortProgessRequested, this,
155 155 &VariableController::onAbortProgressRequested);
156 156
157 157 connect(impl->m_VariableAcquisitionWorker.get(),
158 158 &VariableAcquisitionWorker::variableCanceledRequested, this,
159 159 &VariableController::onAbortAcquisitionRequested);
160 160
161 161 connect(impl->m_VariableAcquisitionWorker.get(), &VariableAcquisitionWorker::dataProvided, this,
162 162 &VariableController::onDataProvided);
163 163 connect(impl->m_VariableAcquisitionWorker.get(),
164 164 &VariableAcquisitionWorker::variableRequestInProgress, this,
165 165 &VariableController::onVariableRetrieveDataInProgress);
166 166
167 167
168 168 connect(&impl->m_VariableAcquisitionWorkerThread, &QThread::started,
169 169 impl->m_VariableAcquisitionWorker.get(), &VariableAcquisitionWorker::initialize);
170 170 connect(&impl->m_VariableAcquisitionWorkerThread, &QThread::finished,
171 171 impl->m_VariableAcquisitionWorker.get(), &VariableAcquisitionWorker::finalize);
172 172
173 173
174 174 impl->m_VariableAcquisitionWorkerThread.start();
175 175 }
176 176
177 177 VariableController::~VariableController()
178 178 {
179 179 qCDebug(LOG_VariableController()) << tr("VariableController destruction")
180 180 << QThread::currentThread();
181 181 this->waitForFinish();
182 182 }
183 183
184 184 VariableModel *VariableController::variableModel() noexcept
185 185 {
186 186 return impl->m_VariableModel;
187 187 }
188 188
189 189 QItemSelectionModel *VariableController::variableSelectionModel() noexcept
190 190 {
191 191 return impl->m_VariableSelectionModel;
192 192 }
193 193
194 194 void VariableController::setTimeController(TimeController *timeController) noexcept
195 195 {
196 196 impl->m_TimeController = timeController;
197 197 }
198 198
199 199 std::shared_ptr<Variable>
200 200 VariableController::cloneVariable(std::shared_ptr<Variable> variable) noexcept
201 201 {
202 202 if (impl->m_VariableModel->containsVariable(variable)) {
203 203 // Clones variable
204 204 auto duplicate = variable->clone();
205 205
206 206 // Adds clone to model
207 207 impl->m_VariableModel->addVariable(duplicate);
208 208
209 209 // Generates clone identifier
210 210 impl->m_VariableToIdentifierMap[duplicate] = QUuid::createUuid();
211 211
212 212 // Registers provider
213 213 auto variableProvider = impl->m_VariableToProviderMap.at(variable);
214 214 auto duplicateProvider = variableProvider != nullptr ? variableProvider->clone() : nullptr;
215 215
216 216 impl->m_VariableToProviderMap[duplicate] = duplicateProvider;
217 217 if (duplicateProvider) {
218 218 impl->registerProvider(duplicateProvider);
219 219 }
220 220
221 221 return duplicate;
222 222 }
223 223 else {
224 224 qCCritical(LOG_VariableController())
225 225 << tr("Can't create duplicate of variable %1: variable not registered in the model")
226 226 .arg(variable->name());
227 227 return nullptr;
228 228 }
229 229 }
230 230
231 231 void VariableController::deleteVariable(std::shared_ptr<Variable> variable) noexcept
232 232 {
233 233 if (!variable) {
234 234 qCCritical(LOG_VariableController()) << "Can't delete variable: variable is null";
235 235 return;
236 236 }
237 237
238 238 // Spreads in SciQlop that the variable will be deleted, so that potential receivers can
239 239 // make some treatments before the deletion
240 240 emit variableAboutToBeDeleted(variable);
241 241
242 242 // Deletes identifier
243 243 impl->m_VariableToIdentifierMap.erase(variable);
244 244
245 245 // Deletes provider
246 246 auto nbProvidersDeleted = impl->m_VariableToProviderMap.erase(variable);
247 247 qCDebug(LOG_VariableController())
248 248 << tr("Number of providers deleted for variable %1: %2")
249 249 .arg(variable->name(), QString::number(nbProvidersDeleted));
250 250
251 251
252 252 // Deletes from model
253 253 impl->m_VariableModel->deleteVariable(variable);
254 254 }
255 255
256 256 void VariableController::deleteVariables(
257 257 const QVector<std::shared_ptr<Variable> > &variables) noexcept
258 258 {
259 259 for (auto variable : qAsConst(variables)) {
260 260 deleteVariable(variable);
261 261 }
262 262 }
263 263
264 264 std::shared_ptr<Variable>
265 265 VariableController::createVariable(const QString &name, const QVariantHash &metadata,
266 266 std::shared_ptr<IDataProvider> provider) noexcept
267 267 {
268 268 if (!impl->m_TimeController) {
269 269 qCCritical(LOG_VariableController())
270 270 << tr("Impossible to create variable: The time controller is null");
271 271 return nullptr;
272 272 }
273 273
274 274 auto range = impl->m_TimeController->dateTime();
275 275
276 276 if (auto newVariable = impl->m_VariableModel->createVariable(name, metadata)) {
277 277 auto identifier = QUuid::createUuid();
278 278
279 279 // store the provider
280 280 impl->registerProvider(provider);
281 281
282 282 // Associate the provider
283 283 impl->m_VariableToProviderMap[newVariable] = provider;
284 284 qCInfo(LOG_VariableController()) << "createVariable: " << identifier;
285 285 impl->m_VariableToIdentifierMap[newVariable] = identifier;
286 286
287 287
288 288 auto varRequestId = QUuid::createUuid();
289 289 impl->processRequest(newVariable, range, varRequestId);
290 290 impl->updateVariableRequest(varRequestId);
291 291
292 292 return newVariable;
293 293 }
294 294 }
295 295
296 296 void VariableController::onDateTimeOnSelection(const SqpRange &dateTime)
297 297 {
298 298 // TODO check synchronisation and Rescale
299 299 qCDebug(LOG_VariableController()) << "VariableController::onDateTimeOnSelection"
300 300 << QThread::currentThread()->objectName();
301 301 auto selectedRows = impl->m_VariableSelectionModel->selectedRows();
302 302 auto varRequestId = QUuid::createUuid();
303 303
304 304 for (const auto &selectedRow : qAsConst(selectedRows)) {
305 305 if (auto selectedVariable = impl->m_VariableModel->variable(selectedRow.row())) {
306 306 selectedVariable->setRange(dateTime);
307 307 impl->processRequest(selectedVariable, dateTime, varRequestId);
308 308
309 309 // notify that rescale operation has to be done
310 310 emit rangeChanged(selectedVariable, dateTime);
311 311 }
312 312 }
313 313 impl->updateVariableRequest(varRequestId);
314 314 }
315 315
316 316 void VariableController::onDataProvided(QUuid vIdentifier, const SqpRange &rangeRequested,
317 317 const SqpRange &cacheRangeRequested,
318 318 QVector<AcquisitionDataPacket> dataAcquired)
319 319 {
320 320 auto retrievedDataSeries = impl->retrieveDataSeries(dataAcquired);
321 321 auto varRequestId = impl->acceptVariableRequest(vIdentifier, retrievedDataSeries);
322 322 if (!varRequestId.isNull()) {
323 323 impl->updateVariableRequest(varRequestId);
324 324 }
325 325 }
326 326
327 327 void VariableController::onVariableRetrieveDataInProgress(QUuid identifier, double progress)
328 328 {
329 329 qCDebug(LOG_VariableController())
330 330 << "TORM: variableController::onVariableRetrieveDataInProgress"
331 331 << QThread::currentThread()->objectName() << progress;
332 332 if (auto var = impl->findVariable(identifier)) {
333 333 impl->m_VariableModel->setDataProgress(var, progress);
334 334 }
335 335 else {
336 336 qCCritical(LOG_VariableController())
337 337 << tr("Impossible to notify progression of a null variable");
338 338 }
339 339 }
340 340
341 341 void VariableController::onAbortProgressRequested(std::shared_ptr<Variable> variable)
342 342 {
343 343 auto it = impl->m_VariableToIdentifierMap.find(variable);
344 344 if (it != impl->m_VariableToIdentifierMap.cend()) {
345 345 impl->m_VariableAcquisitionWorker->abortProgressRequested(it->second);
346 346
347 347 QUuid varRequestId;
348 348 auto varIdToVarRequestIdQueueMapIt = impl->m_VarIdToVarRequestIdQueueMap.find(it->second);
349 349 if (varIdToVarRequestIdQueueMapIt != impl->m_VarIdToVarRequestIdQueueMap.cend()) {
350 350 auto &varRequestIdQueue = varIdToVarRequestIdQueueMapIt->second;
351 351 varRequestId = varRequestIdQueue.front();
352 352 impl->cancelVariableRequest(varRequestId);
353 353
354 354 // Finish the progression for the request
355 355 impl->m_VariableModel->setDataProgress(variable, 0.0);
356 356 }
357 357 else {
358 358 qCWarning(LOG_VariableController())
359 359 << tr("Aborting progression of inexistant variable request detected !!!")
360 360 << QThread::currentThread()->objectName();
361 361 }
362 362 }
363 363 else {
364 364 qCWarning(LOG_VariableController())
365 365 << tr("Aborting progression of inexistant variable detected !!!")
366 366 << QThread::currentThread()->objectName();
367 367 }
368 368 }
369 369
370 370 void VariableController::onAbortAcquisitionRequested(QUuid vIdentifier)
371 371 {
372 372 qCDebug(LOG_VariableController()) << "TORM: variableController::onAbortAcquisitionRequested"
373 373 << QThread::currentThread()->objectName() << vIdentifier;
374 374
375 375 if (auto var = impl->findVariable(vIdentifier)) {
376 376 this->onAbortProgressRequested(var);
377 377 }
378 378 else {
379 379 qCCritical(LOG_VariableController())
380 380 << tr("Impossible to abort Acquisition Requestof a null variable");
381 381 }
382 382 }
383 383
384 384 void VariableController::onAddSynchronizationGroupId(QUuid synchronizationGroupId)
385 385 {
386 386 qCDebug(LOG_VariableController()) << "TORM: VariableController::onAddSynchronizationGroupId"
387 387 << QThread::currentThread()->objectName()
388 388 << synchronizationGroupId;
389 389 auto vSynchroGroup = std::make_shared<VariableSynchronizationGroup>();
390 390 impl->m_GroupIdToVariableSynchronizationGroupMap.insert(
391 391 std::make_pair(synchronizationGroupId, vSynchroGroup));
392 392 }
393 393
394 394 void VariableController::onRemoveSynchronizationGroupId(QUuid synchronizationGroupId)
395 395 {
396 396 impl->m_GroupIdToVariableSynchronizationGroupMap.erase(synchronizationGroupId);
397 397 }
398 398
399 399 void VariableController::onAddSynchronized(std::shared_ptr<Variable> variable,
400 400 QUuid synchronizationGroupId)
401 401
402 402 {
403 403 qCDebug(LOG_VariableController()) << "TORM: VariableController::onAddSynchronized"
404 404 << synchronizationGroupId;
405 405 auto varToVarIdIt = impl->m_VariableToIdentifierMap.find(variable);
406 406 if (varToVarIdIt != impl->m_VariableToIdentifierMap.cend()) {
407 407 auto groupIdToVSGIt
408 408 = impl->m_GroupIdToVariableSynchronizationGroupMap.find(synchronizationGroupId);
409 409 if (groupIdToVSGIt != impl->m_GroupIdToVariableSynchronizationGroupMap.cend()) {
410 410 impl->m_VariableIdGroupIdMap.insert(
411 411 std::make_pair(varToVarIdIt->second, synchronizationGroupId));
412 412 groupIdToVSGIt->second->addVariableId(varToVarIdIt->second);
413 413 }
414 414 else {
415 415 qCCritical(LOG_VariableController())
416 416 << tr("Impossible to synchronize a variable with an unknown sycnhronization group")
417 417 << variable->name();
418 418 }
419 419 }
420 420 else {
421 421 qCCritical(LOG_VariableController())
422 422 << tr("Impossible to synchronize a variable with no identifier") << variable->name();
423 423 }
424 424 }
425 425
426 426 void VariableController::desynchronize(std::shared_ptr<Variable> variable,
427 427 QUuid synchronizationGroupId)
428 428 {
429 429 // Gets variable id
430 430 auto variableIt = impl->m_VariableToIdentifierMap.find(variable);
431 431 if (variableIt == impl->m_VariableToIdentifierMap.cend()) {
432 432 qCCritical(LOG_VariableController())
433 433 << tr("Can't desynchronize variable %1: variable identifier not found")
434 434 .arg(variable->name());
435 435 return;
436 436 }
437 437
438 438 // Gets synchronization group
439 439 auto groupIt = impl->m_GroupIdToVariableSynchronizationGroupMap.find(synchronizationGroupId);
440 440 if (groupIt == impl->m_GroupIdToVariableSynchronizationGroupMap.cend()) {
441 441 qCCritical(LOG_VariableController())
442 442 << tr("Can't desynchronize variable %1: unknown synchronization group")
443 443 .arg(variable->name());
444 444 return;
445 445 }
446 446
447 447 auto variableId = variableIt->second;
448 448
449 449 // Removes variable from synchronization group
450 450 auto synchronizationGroup = groupIt->second;
451 451 synchronizationGroup->removeVariableId(variableId);
452 452
453 453 // Removes link between variable and synchronization group
454 454 impl->m_VariableIdGroupIdMap.erase(variableId);
455 455 }
456 456
457 457 void VariableController::onRequestDataLoading(QVector<std::shared_ptr<Variable> > variables,
458 458 const SqpRange &range, const SqpRange &oldRange,
459 459 bool synchronise)
460 460 {
461 461 // NOTE: oldRange isn't really necessary since oldRange == variable->range().
462 462
463 463 // we want to load data of the variable for the dateTime.
464 464 // First we check if the cache contains some of them.
465 465 // For the other, we ask the provider to give them.
466 466
467 467 auto varRequestId = QUuid::createUuid();
468 468 qCDebug(LOG_VariableController()) << "VariableController::onRequestDataLoading"
469 469 << QThread::currentThread()->objectName() << varRequestId;
470 470
471 471 for (const auto &var : variables) {
472 472 qCDebug(LOG_VariableController()) << "processRequest for" << var->name() << varRequestId;
473 473 impl->processRequest(var, range, varRequestId);
474 474 }
475 475
476 476 if (synchronise) {
477 477 // Get the group ids
478 478 qCDebug(LOG_VariableController())
479 479 << "TORM VariableController::onRequestDataLoading for synchro var ENABLE";
480 480 auto groupIds = std::set<QUuid>{};
481 481 auto groupIdToOldRangeMap = std::map<QUuid, SqpRange>{};
482 482 for (const auto &var : variables) {
483 483 auto varToVarIdIt = impl->m_VariableToIdentifierMap.find(var);
484 484 if (varToVarIdIt != impl->m_VariableToIdentifierMap.cend()) {
485 485 auto vId = varToVarIdIt->second;
486 486 auto varIdToGroupIdIt = impl->m_VariableIdGroupIdMap.find(vId);
487 487 if (varIdToGroupIdIt != impl->m_VariableIdGroupIdMap.cend()) {
488 488 auto gId = varIdToGroupIdIt->second;
489 489 groupIdToOldRangeMap.insert(std::make_pair(gId, var->range()));
490 490 if (groupIds.find(gId) == groupIds.cend()) {
491 491 qCDebug(LOG_VariableController()) << "Synchro detect group " << gId;
492 492 groupIds.insert(gId);
493 493 }
494 494 }
495 495 }
496 496 }
497 497
498 498 // We assume here all group ids exist
499 499 for (const auto &gId : groupIds) {
500 500 auto vSynchronizationGroup = impl->m_GroupIdToVariableSynchronizationGroupMap.at(gId);
501 501 auto vSyncIds = vSynchronizationGroup->getIds();
502 502 qCDebug(LOG_VariableController()) << "Var in synchro group ";
503 503 for (auto vId : vSyncIds) {
504 504 auto var = impl->findVariable(vId);
505 505
506 506 // Don't process already processed var
507 507 if (!variables.contains(var)) {
508 508 if (var != nullptr) {
509 509 qCDebug(LOG_VariableController()) << "processRequest synchro for"
510 510 << var->name();
511 511 auto vSyncRangeRequested = computeSynchroRangeRequested(
512 512 var->range(), range, groupIdToOldRangeMap.at(gId));
513 513 qCDebug(LOG_VariableController()) << "synchro RR" << vSyncRangeRequested;
514 514 impl->processRequest(var, vSyncRangeRequested, varRequestId);
515 515 }
516 516 else {
517 517 qCCritical(LOG_VariableController())
518 518
519 519 << tr("Impossible to synchronize a null variable");
520 520 }
521 521 }
522 522 }
523 523 }
524 524 }
525 525
526 526 impl->updateVariableRequest(varRequestId);
527 527 }
528 528
529 529
530 530 void VariableController::initialize()
531 531 {
532 532 qCDebug(LOG_VariableController()) << tr("VariableController init") << QThread::currentThread();
533 533 impl->m_WorkingMutex.lock();
534 534 qCDebug(LOG_VariableController()) << tr("VariableController init END");
535 535 }
536 536
537 537 void VariableController::finalize()
538 538 {
539 539 impl->m_WorkingMutex.unlock();
540 540 }
541 541
542 542 void VariableController::waitForFinish()
543 543 {
544 544 QMutexLocker locker{&impl->m_WorkingMutex};
545 545 }
546 546
547 547 AcquisitionZoomType VariableController::getZoomType(const SqpRange &range, const SqpRange &oldRange)
548 548 {
549 549 // t1.m_TStart <= t2.m_TStart && t2.m_TEnd <= t1.m_TEnd
550 550 auto zoomType = AcquisitionZoomType::Unknown;
551 551 if (range.m_TStart <= oldRange.m_TStart && oldRange.m_TEnd <= range.m_TEnd) {
552 552 zoomType = AcquisitionZoomType::ZoomOut;
553 553 }
554 554 else if (range.m_TStart > oldRange.m_TStart && range.m_TEnd > oldRange.m_TEnd) {
555 555 zoomType = AcquisitionZoomType::PanRight;
556 556 }
557 557 else if (range.m_TStart < oldRange.m_TStart && range.m_TEnd < oldRange.m_TEnd) {
558 558 zoomType = AcquisitionZoomType::PanLeft;
559 559 }
560 560 else if (range.m_TStart > oldRange.m_TStart && oldRange.m_TEnd > range.m_TEnd) {
561 561 zoomType = AcquisitionZoomType::ZoomIn;
562 562 }
563 563 else {
564 564 qCCritical(LOG_VariableController()) << "getZoomType: Unknown type detected";
565 565 }
566 566 return zoomType;
567 567 }
568 568
569 569 void VariableController::VariableControllerPrivate::processRequest(std::shared_ptr<Variable> var,
570 570 const SqpRange &rangeRequested,
571 571 QUuid varRequestId)
572 572 {
573 573
574 574 // TODO: protect at
575 575 auto varRequest = VariableRequest{};
576 576 auto varId = m_VariableToIdentifierMap.at(var);
577 577
578 578 auto varStrategyRangesRequested
579 579 = m_VariableCacheStrategy->computeRange(var->range(), rangeRequested);
580 580
581 581 auto notInCacheRangeList = QVector<SqpRange>{varStrategyRangesRequested.second};
582 582 auto inCacheRangeList = QVector<SqpRange>{};
583 583 if (m_VarIdToVarRequestIdQueueMap.find(varId) == m_VarIdToVarRequestIdQueueMap.cend()) {
584 584 notInCacheRangeList = var->provideNotInCacheRangeList(varStrategyRangesRequested.second);
585 585 inCacheRangeList = var->provideInCacheRangeList(varStrategyRangesRequested.second);
586 586 }
587 587
588 588 if (!notInCacheRangeList.empty()) {
589 589 varRequest.m_RangeRequested = varStrategyRangesRequested.first;
590 590 varRequest.m_CacheRangeRequested = varStrategyRangesRequested.second;
591 591
592 592 // store VarRequest
593 593 storeVariableRequest(varId, varRequestId, varRequest);
594 594
595 595 auto varProvider = m_VariableToProviderMap.at(var);
596 596 if (varProvider != nullptr) {
597 597 auto varRequestIdCanceled = m_VariableAcquisitionWorker->pushVariableRequest(
598 598 varRequestId, varId, varStrategyRangesRequested.first,
599 599 varStrategyRangesRequested.second,
600 600 DataProviderParameters{std::move(notInCacheRangeList), var->metadata()},
601 601 varProvider);
602 602
603 603 if (!varRequestIdCanceled.isNull()) {
604 604 qCDebug(LOG_VariableAcquisitionWorker()) << tr("vsarRequestIdCanceled: ")
605 605 << varRequestIdCanceled;
606 606 cancelVariableRequest(varRequestIdCanceled);
607 607 }
608 608 }
609 609 else {
610 610 qCCritical(LOG_VariableController())
611 611 << "Impossible to provide data with a null provider";
612 612 }
613 613
614 614 if (!inCacheRangeList.empty()) {
615 615 emit q->updateVarDisplaying(var, inCacheRangeList.first());
616 616 }
617 617 }
618 618 else {
619 619 varRequest.m_RangeRequested = varStrategyRangesRequested.first;
620 620 varRequest.m_CacheRangeRequested = varStrategyRangesRequested.second;
621 621 // store VarRequest
622 622 storeVariableRequest(varId, varRequestId, varRequest);
623 623 acceptVariableRequest(varId,
624 624 var->dataSeries()->subDataSeries(varStrategyRangesRequested.second));
625 625 }
626 626 }
627 627
628 628 std::shared_ptr<Variable>
629 629 VariableController::VariableControllerPrivate::findVariable(QUuid vIdentifier)
630 630 {
631 631 std::shared_ptr<Variable> var;
632 632 auto findReply = [vIdentifier](const auto &entry) { return vIdentifier == entry.second; };
633 633
634 634 auto end = m_VariableToIdentifierMap.cend();
635 635 auto it = std::find_if(m_VariableToIdentifierMap.cbegin(), end, findReply);
636 636 if (it != end) {
637 637 var = it->first;
638 638 }
639 639 else {
640 640 qCCritical(LOG_VariableController())
641 641 << tr("Impossible to find the variable with the identifier: ") << vIdentifier;
642 642 }
643 643
644 644 return var;
645 645 }
646 646
647 647 std::shared_ptr<IDataSeries> VariableController::VariableControllerPrivate::retrieveDataSeries(
648 648 const QVector<AcquisitionDataPacket> acqDataPacketVector)
649 649 {
650 650 qCDebug(LOG_VariableController()) << tr("TORM: retrieveDataSeries acqDataPacketVector size")
651 651 << acqDataPacketVector.size();
652 652 std::shared_ptr<IDataSeries> dataSeries;
653 653 if (!acqDataPacketVector.isEmpty()) {
654 654 dataSeries = acqDataPacketVector[0].m_DateSeries;
655 655 for (int i = 1; i < acqDataPacketVector.size(); ++i) {
656 656 dataSeries->merge(acqDataPacketVector[i].m_DateSeries.get());
657 657 }
658 658 }
659 659 qCDebug(LOG_VariableController()) << tr("TORM: retrieveDataSeries acqDataPacketVector size END")
660 660 << acqDataPacketVector.size();
661 661 return dataSeries;
662 662 }
663 663
664 664 void VariableController::VariableControllerPrivate::registerProvider(
665 665 std::shared_ptr<IDataProvider> provider)
666 666 {
667 667 if (m_ProviderSet.find(provider) == m_ProviderSet.end()) {
668 668 qCDebug(LOG_VariableController()) << tr("Registering of a new provider")
669 669 << provider->objectName();
670 670 m_ProviderSet.insert(provider);
671 671 connect(provider.get(), &IDataProvider::dataProvided, m_VariableAcquisitionWorker.get(),
672 672 &VariableAcquisitionWorker::onVariableDataAcquired);
673 673 connect(provider.get(), &IDataProvider::dataProvidedProgress,
674 674 m_VariableAcquisitionWorker.get(),
675 675 &VariableAcquisitionWorker::onVariableRetrieveDataInProgress);
676 676 connect(provider.get(), &IDataProvider::dataProvidedFailed,
677 677 m_VariableAcquisitionWorker.get(),
678 678 &VariableAcquisitionWorker::onVariableAcquisitionFailed);
679 679 }
680 680 else {
681 681 qCDebug(LOG_VariableController()) << tr("Cannot register provider, it already exists ");
682 682 }
683 683 }
684 684
685 685 void VariableController::VariableControllerPrivate::storeVariableRequest(
686 686 QUuid varId, QUuid varRequestId, const VariableRequest &varRequest)
687 687 {
688 688 // First request for the variable. we can create an entry for it
689 689 auto varIdToVarRequestIdQueueMapIt = m_VarIdToVarRequestIdQueueMap.find(varId);
690 690 if (varIdToVarRequestIdQueueMapIt == m_VarIdToVarRequestIdQueueMap.cend()) {
691 691 auto varRequestIdQueue = std::deque<QUuid>{};
692 692 qCDebug(LOG_VariableController()) << tr("Store REQUEST in QUEUE");
693 693 varRequestIdQueue.push_back(varRequestId);
694 694 m_VarIdToVarRequestIdQueueMap.insert(std::make_pair(varId, std::move(varRequestIdQueue)));
695 695 }
696 696 else {
697 697 qCDebug(LOG_VariableController()) << tr("Store REQUEST in EXISTING QUEUE");
698 698 auto &varRequestIdQueue = varIdToVarRequestIdQueueMapIt->second;
699 699 varRequestIdQueue.push_back(varRequestId);
700 700 }
701 701
702 702 auto varRequestIdToVarIdVarRequestMapIt = m_VarRequestIdToVarIdVarRequestMap.find(varRequestId);
703 703 if (varRequestIdToVarIdVarRequestMapIt == m_VarRequestIdToVarIdVarRequestMap.cend()) {
704 704 auto varIdToVarRequestMap = std::map<QUuid, VariableRequest>{};
705 705 varIdToVarRequestMap.insert(std::make_pair(varId, varRequest));
706 706 qCDebug(LOG_VariableController()) << tr("Store REQUESTID in MAP");
707 707 m_VarRequestIdToVarIdVarRequestMap.insert(
708 708 std::make_pair(varRequestId, std::move(varIdToVarRequestMap)));
709 709 }
710 710 else {
711 711 auto &varIdToVarRequestMap = varRequestIdToVarIdVarRequestMapIt->second;
712 712 qCDebug(LOG_VariableController()) << tr("Store REQUESTID in EXISTING MAP");
713 713 varIdToVarRequestMap.insert(std::make_pair(varId, varRequest));
714 714 }
715 715 }
716 716
717 717 QUuid VariableController::VariableControllerPrivate::acceptVariableRequest(
718 718 QUuid varId, std::shared_ptr<IDataSeries> dataSeries)
719 719 {
720 720 QUuid varRequestId;
721 721 auto varIdToVarRequestIdQueueMapIt = m_VarIdToVarRequestIdQueueMap.find(varId);
722 722 if (varIdToVarRequestIdQueueMapIt != m_VarIdToVarRequestIdQueueMap.cend()) {
723 723 auto &varRequestIdQueue = varIdToVarRequestIdQueueMapIt->second;
724 724 varRequestId = varRequestIdQueue.front();
725 725 auto varRequestIdToVarIdVarRequestMapIt
726 726 = m_VarRequestIdToVarIdVarRequestMap.find(varRequestId);
727 727 if (varRequestIdToVarIdVarRequestMapIt != m_VarRequestIdToVarIdVarRequestMap.cend()) {
728 728 auto &varIdToVarRequestMap = varRequestIdToVarIdVarRequestMapIt->second;
729 729 auto varIdToVarRequestMapIt = varIdToVarRequestMap.find(varId);
730 730 if (varIdToVarRequestMapIt != varIdToVarRequestMap.cend()) {
731 731 qCDebug(LOG_VariableController()) << tr("acceptVariableRequest");
732 732 auto &varRequest = varIdToVarRequestMapIt->second;
733 733 varRequest.m_DataSeries = dataSeries;
734 734 varRequest.m_CanUpdate = true;
735 735 }
736 736 else {
737 737 qCDebug(LOG_VariableController())
738 738 << tr("Impossible to acceptVariableRequest of a unknown variable id attached "
739 739 "to a variableRequestId")
740 740 << varRequestId << varId;
741 741 }
742 742 }
743 743 else {
744 744 qCCritical(LOG_VariableController())
745 745 << tr("Impossible to acceptVariableRequest of a unknown variableRequestId")
746 746 << varRequestId;
747 747 }
748 748
749 749 varRequestIdQueue.pop_front();
750 750 if (varRequestIdQueue.empty()) {
751 751 qCDebug(LOG_VariableController())
752 752 << tr("TORM Erase REQUEST because it has been accepted") << varId;
753 753 m_VarIdToVarRequestIdQueueMap.erase(varId);
754 754 }
755 755 }
756 756 else {
757 757 qCCritical(LOG_VariableController())
758 758 << tr("Impossible to acceptVariableRequest of a unknown variable id") << varId;
759 759 }
760 760
761 761 return varRequestId;
762 762 }
763 763
764 764 void VariableController::VariableControllerPrivate::updateVariableRequest(QUuid varRequestId)
765 765 {
766 766
767 767 auto varRequestIdToVarIdVarRequestMapIt = m_VarRequestIdToVarIdVarRequestMap.find(varRequestId);
768 768 if (varRequestIdToVarIdVarRequestMapIt != m_VarRequestIdToVarIdVarRequestMap.cend()) {
769 769 bool processVariableUpdate = true;
770 770 auto &varIdToVarRequestMap = varRequestIdToVarIdVarRequestMapIt->second;
771 771 for (auto varIdToVarRequestMapIt = varIdToVarRequestMap.cbegin();
772 772 (varIdToVarRequestMapIt != varIdToVarRequestMap.cend()) && processVariableUpdate;
773 773 ++varIdToVarRequestMapIt) {
774 774 processVariableUpdate &= varIdToVarRequestMapIt->second.m_CanUpdate;
775 775 qCDebug(LOG_VariableController()) << tr("updateVariableRequest")
776 776 << processVariableUpdate;
777 777 }
778 778
779 779 if (processVariableUpdate) {
780 780 for (auto varIdToVarRequestMapIt = varIdToVarRequestMap.cbegin();
781 781 varIdToVarRequestMapIt != varIdToVarRequestMap.cend(); ++varIdToVarRequestMapIt) {
782 782 if (auto var = findVariable(varIdToVarRequestMapIt->first)) {
783 783 auto &varRequest = varIdToVarRequestMapIt->second;
784 784 var->setRange(varRequest.m_RangeRequested);
785 785 var->setCacheRange(varRequest.m_CacheRangeRequested);
786 786 qCDebug(LOG_VariableController()) << tr("1: onDataProvided")
787 787 << varRequest.m_RangeRequested;
788 788 qCDebug(LOG_VariableController()) << tr("2: onDataProvided")
789 789 << varRequest.m_CacheRangeRequested;
790 790 var->mergeDataSeries(varRequest.m_DataSeries);
791 qCDebug(LOG_VariableController()) << tr("3: onDataProvided")
792 << varRequest.m_DataSeries->range();
793 qCDebug(LOG_VariableController()) << tr("4: onDataProvided");
791 qCDebug(LOG_VariableController()) << tr("3: onDataProvided");
794 792
795 793 /// @todo MPL: confirm
796 794 // Variable update is notified only if there is no pending request for it
797 795 // if
798 796 // (m_VarIdToVarRequestIdQueueMap.count(varIdToVarRequestMapIt->first)
799 797 // == 0) {
800 798 emit var->updated();
801 799 // }
802 800 }
803 801 else {
804 802 qCCritical(LOG_VariableController())
805 803 << tr("Impossible to update data to a null variable");
806 804 }
807 805 }
808 806
809 807 // cleaning varRequestId
810 808 qCDebug(LOG_VariableController()) << tr("0: erase REQUEST in MAP ?")
811 809 << m_VarRequestIdToVarIdVarRequestMap.size();
812 810 m_VarRequestIdToVarIdVarRequestMap.erase(varRequestId);
813 811 qCDebug(LOG_VariableController()) << tr("1: erase REQUEST in MAP ?")
814 812 << m_VarRequestIdToVarIdVarRequestMap.size();
815 813 }
816 814 }
817 815 else {
818 816 qCCritical(LOG_VariableController())
819 817 << tr("Cannot updateVariableRequest for a unknow varRequestId") << varRequestId;
820 818 }
821 819 }
822 820
823 821 void VariableController::VariableControllerPrivate::cancelVariableRequest(QUuid varRequestId)
824 822 {
825 823 // cleaning varRequestId
826 824 m_VarRequestIdToVarIdVarRequestMap.erase(varRequestId);
827 825
828 826 for (auto varIdToVarRequestIdQueueMapIt = m_VarIdToVarRequestIdQueueMap.begin();
829 827 varIdToVarRequestIdQueueMapIt != m_VarIdToVarRequestIdQueueMap.end();) {
830 828 auto &varRequestIdQueue = varIdToVarRequestIdQueueMapIt->second;
831 829 varRequestIdQueue.erase(
832 830 std::remove(varRequestIdQueue.begin(), varRequestIdQueue.end(), varRequestId),
833 831 varRequestIdQueue.end());
834 832 if (varRequestIdQueue.empty()) {
835 833 varIdToVarRequestIdQueueMapIt
836 834 = m_VarIdToVarRequestIdQueueMap.erase(varIdToVarRequestIdQueueMapIt);
837 835 }
838 836 else {
839 837 ++varIdToVarRequestIdQueueMapIt;
840 838 }
841 839 }
842 840 }
@@ -1,636 +1,707
1 1 #include "Data/DataSeries.h"
2 2 #include "Data/ScalarSeries.h"
3 3 #include "Data/VectorSeries.h"
4 4
5 5 #include <cmath>
6 6
7 7 #include <QObject>
8 8 #include <QtTest>
9 9
10 10 Q_DECLARE_METATYPE(std::shared_ptr<ScalarSeries>)
11 11 Q_DECLARE_METATYPE(std::shared_ptr<VectorSeries>)
12 12
13 13 namespace {
14 14
15 15 using DataContainer = std::vector<double>;
16 16
17 17 void validateRange(DataSeriesIterator first, DataSeriesIterator last, const DataContainer &xData,
18 18 const DataContainer &valuesData)
19 19 {
20 20 QVERIFY(std::equal(first, last, xData.cbegin(), xData.cend(),
21 21 [](const auto &it, const auto &expectedX) { return it.x() == expectedX; }));
22 22 QVERIFY(std::equal(
23 23 first, last, valuesData.cbegin(), valuesData.cend(),
24 24 [](const auto &it, const auto &expectedVal) { return it.value() == expectedVal; }));
25 25 }
26 26
27 27 void validateRange(DataSeriesIterator first, DataSeriesIterator last, const DataContainer &xData,
28 28 const std::vector<DataContainer> &valuesData)
29 29 {
30 30 QVERIFY(std::equal(first, last, xData.cbegin(), xData.cend(),
31 31 [](const auto &it, const auto &expectedX) { return it.x() == expectedX; }));
32 32 for (auto i = 0; i < valuesData.size(); ++i) {
33 33 auto componentData = valuesData.at(i);
34 34
35 35 QVERIFY(std::equal(
36 36 first, last, componentData.cbegin(), componentData.cend(),
37 37 [i](const auto &it, const auto &expectedVal) { return it.value(i) == expectedVal; }));
38 38 }
39 39 }
40 40
41 41 } // namespace
42 42
43 43 class TestDataSeries : public QObject {
44 44 Q_OBJECT
45 45 private:
46 46 template <typename T>
47 47 void testValuesBoundsStructure()
48 48 {
49 49 // ////////////// //
50 50 // Test structure //
51 51 // ////////////// //
52 52
53 53 // Data series to get values bounds
54 54 QTest::addColumn<std::shared_ptr<T> >("dataSeries");
55 55
56 56 // x-axis range
57 57 QTest::addColumn<double>("minXAxis");
58 58 QTest::addColumn<double>("maxXAxis");
59 59
60 60 // Expected results
61 61 QTest::addColumn<bool>(
62 62 "expectedOK"); // Test is expected to be ok (i.e. method doesn't return end iterators)
63 63 QTest::addColumn<double>("expectedMinValue");
64 64 QTest::addColumn<double>("expectedMaxValue");
65 65 }
66 66
67 67 template <typename T>
68 68 void testValuesBounds()
69 69 {
70 70 QFETCH(std::shared_ptr<T>, dataSeries);
71 71 QFETCH(double, minXAxis);
72 72 QFETCH(double, maxXAxis);
73 73
74 74 QFETCH(bool, expectedOK);
75 75 QFETCH(double, expectedMinValue);
76 76 QFETCH(double, expectedMaxValue);
77 77
78 78 auto minMaxIts = dataSeries->valuesBounds(minXAxis, maxXAxis);
79 79 auto end = dataSeries->cend();
80 80
81 81 // Checks iterators with expected result
82 82 QCOMPARE(expectedOK, minMaxIts.first != end && minMaxIts.second != end);
83 83
84 84 if (expectedOK) {
85 85 auto compare = [](const auto &v1, const auto &v2) {
86 86 return (std::isnan(v1) && std::isnan(v2)) || v1 == v2;
87 87 };
88 88
89 89 QVERIFY(compare(expectedMinValue, minMaxIts.first->minValue()));
90 90 QVERIFY(compare(expectedMaxValue, minMaxIts.second->maxValue()));
91 91 }
92 92 }
93 93
94 94 template <typename T>
95 95 void testPurgeStructure()
96 96 {
97 97 // ////////////// //
98 98 // Test structure //
99 99 // ////////////// //
100 100
101 101 // Data series to purge
102 102 QTest::addColumn<std::shared_ptr<T> >("dataSeries");
103 103 QTest::addColumn<double>("min");
104 104 QTest::addColumn<double>("max");
105 105
106 106 // Expected values after purge
107 107 QTest::addColumn<DataContainer>("expectedXAxisData");
108 108 QTest::addColumn<std::vector<DataContainer> >("expectedValuesData");
109 109 }
110 110
111 111 template <typename T>
112 112 void testPurge()
113 113 {
114 114 QFETCH(std::shared_ptr<T>, dataSeries);
115 115 QFETCH(double, min);
116 116 QFETCH(double, max);
117 117
118 118 dataSeries->purge(min, max);
119 119
120 120 // Validates results
121 121 QFETCH(DataContainer, expectedXAxisData);
122 122 QFETCH(std::vector<DataContainer>, expectedValuesData);
123 123
124 124 validateRange(dataSeries->cbegin(), dataSeries->cend(), expectedXAxisData,
125 125 expectedValuesData);
126 126 }
127 127
128 template <typename SourceType, typename DestType>
129 void testMergeDifferentTypesStructure()
130 {
131 // ////////////// //
132 // Test structure //
133 // ////////////// //
134
135 // Data series to merge
136 QTest::addColumn<std::shared_ptr<DestType> >("dest");
137 QTest::addColumn<std::shared_ptr<SourceType> >("source");
138
139 // Expected values in the dest data series after merge
140 QTest::addColumn<DataContainer>("expectedXAxisData");
141 QTest::addColumn<DataContainer>("expectedValuesData");
142 }
143
144 template <typename SourceType, typename DestType>
145 void testMergeDifferentTypes()
146 {
147 // Merges series
148 QFETCH(std::shared_ptr<SourceType>, source);
149 QFETCH(std::shared_ptr<DestType>, dest);
150
151 dest->merge(source.get());
152
153 // Validates results : we check that the merge is valid and the data series is sorted on its
154 // x-axis data
155 QFETCH(DataContainer, expectedXAxisData);
156 QFETCH(DataContainer, expectedValuesData);
157
158 validateRange(dest->cbegin(), dest->cend(), expectedXAxisData, expectedValuesData);
159 }
160
128 161 private slots:
129 162
130 163 /// Input test data
131 164 /// @sa testCtor()
132 165 void testCtor_data();
133 166
134 167 /// Tests construction of a data series
135 168 void testCtor();
136 169
137 170 /// Input test data
138 171 /// @sa testMerge()
139 172 void testMerge_data();
140 173
141 174 /// Tests merge of two data series
142 175 void testMerge();
143 176
144 177 /// Input test data
178 /// @sa testMergeVectorInScalar()
179 void testMergeVectorInScalar_data();
180
181 /// Tests merge of vector series in scalar series
182 void testMergeVectorInScalar();
183
184 /// Input test data
145 185 /// @sa testPurgeScalar()
146 186 void testPurgeScalar_data();
147 187
148 188 /// Tests purge of a scalar series
149 189 void testPurgeScalar();
150 190
151 191 /// Input test data
152 192 /// @sa testPurgeVector()
153 193 void testPurgeVector_data();
154 194
155 195 /// Tests purge of a vector series
156 196 void testPurgeVector();
157 197
158 198 /// Input test data
159 199 /// @sa testMinXAxisData()
160 200 void testMinXAxisData_data();
161 201
162 202 /// Tests get min x-axis data of a data series
163 203 void testMinXAxisData();
164 204
165 205 /// Input test data
166 206 /// @sa testMaxXAxisData()
167 207 void testMaxXAxisData_data();
168 208
169 209 /// Tests get max x-axis data of a data series
170 210 void testMaxXAxisData();
171 211
172 212 /// Input test data
173 213 /// @sa testXAxisRange()
174 214 void testXAxisRange_data();
175 215
176 216 /// Tests get x-axis range of a data series
177 217 void testXAxisRange();
178 218
179 219 /// Input test data
180 220 /// @sa testValuesBoundsScalar()
181 221 void testValuesBoundsScalar_data();
182 222
183 223 /// Tests get values bounds of a scalar series
184 224 void testValuesBoundsScalar();
185 225
186 226 /// Input test data
187 227 /// @sa testValuesBoundsVector()
188 228 void testValuesBoundsVector_data();
189 229
190 230 /// Tests get values bounds of a vector series
191 231 void testValuesBoundsVector();
192 232 };
193 233
194 234 void TestDataSeries::testCtor_data()
195 235 {
196 236 // ////////////// //
197 237 // Test structure //
198 238 // ////////////// //
199 239
200 240 // x-axis data
201 241 QTest::addColumn<DataContainer>("xAxisData");
202 242 // values data
203 243 QTest::addColumn<DataContainer>("valuesData");
204 244
205 245 // expected x-axis data
206 246 QTest::addColumn<DataContainer>("expectedXAxisData");
207 247 // expected values data
208 248 QTest::addColumn<DataContainer>("expectedValuesData");
209 249
210 250 // ////////// //
211 251 // Test cases //
212 252 // ////////// //
213 253
214 254 QTest::newRow("invalidData (different sizes of vectors)")
215 255 << DataContainer{1., 2., 3., 4., 5.} << DataContainer{100., 200., 300.} << DataContainer{}
216 256 << DataContainer{};
217 257
218 258 QTest::newRow("sortedData") << DataContainer{1., 2., 3., 4., 5.}
219 259 << DataContainer{100., 200., 300., 400., 500.}
220 260 << DataContainer{1., 2., 3., 4., 5.}
221 261 << DataContainer{100., 200., 300., 400., 500.};
222 262
223 263 QTest::newRow("unsortedData") << DataContainer{5., 4., 3., 2., 1.}
224 264 << DataContainer{100., 200., 300., 400., 500.}
225 265 << DataContainer{1., 2., 3., 4., 5.}
226 266 << DataContainer{500., 400., 300., 200., 100.};
227 267
228 268 QTest::newRow("unsortedData2")
229 269 << DataContainer{1., 4., 3., 5., 2.} << DataContainer{100., 200., 300., 400., 500.}
230 270 << DataContainer{1., 2., 3., 4., 5.} << DataContainer{100., 500., 300., 200., 400.};
231 271 }
232 272
233 273 void TestDataSeries::testCtor()
234 274 {
235 275 // Creates series
236 276 QFETCH(DataContainer, xAxisData);
237 277 QFETCH(DataContainer, valuesData);
238 278
239 279 auto series = std::make_shared<ScalarSeries>(std::move(xAxisData), std::move(valuesData),
240 280 Unit{}, Unit{});
241 281
242 282 // Validates results : we check that the data series is sorted on its x-axis data
243 283 QFETCH(DataContainer, expectedXAxisData);
244 284 QFETCH(DataContainer, expectedValuesData);
245 285
246 286 validateRange(series->cbegin(), series->cend(), expectedXAxisData, expectedValuesData);
247 287 }
248 288
249 289 namespace {
250 290
251 291 std::shared_ptr<ScalarSeries> createScalarSeries(DataContainer xAxisData, DataContainer valuesData)
252 292 {
253 293 return std::make_shared<ScalarSeries>(std::move(xAxisData), std::move(valuesData), Unit{},
254 294 Unit{});
255 295 }
256 296
257 297 std::shared_ptr<VectorSeries> createVectorSeries(DataContainer xAxisData, DataContainer xValuesData,
258 298 DataContainer yValuesData,
259 299 DataContainer zValuesData)
260 300 {
261 301 return std::make_shared<VectorSeries>(std::move(xAxisData), std::move(xValuesData),
262 302 std::move(yValuesData), std::move(zValuesData), Unit{},
263 303 Unit{});
264 304 }
265 305
266 306 } // namespace
267 307
268 308 void TestDataSeries::testMerge_data()
269 309 {
270 310 // ////////////// //
271 311 // Test structure //
272 312 // ////////////// //
273 313
274 314 // Data series to merge
275 315 QTest::addColumn<std::shared_ptr<ScalarSeries> >("dataSeries");
276 316 QTest::addColumn<std::shared_ptr<ScalarSeries> >("dataSeries2");
277 317
278 318 // Expected values in the first data series after merge
279 319 QTest::addColumn<DataContainer>("expectedXAxisData");
280 320 QTest::addColumn<DataContainer>("expectedValuesData");
281 321
282 322 // ////////// //
283 323 // Test cases //
284 324 // ////////// //
285 325
286 326 QTest::newRow("sortedMerge")
287 327 << createScalarSeries({1., 2., 3., 4., 5.}, {100., 200., 300., 400., 500.})
288 328 << createScalarSeries({6., 7., 8., 9., 10.}, {600., 700., 800., 900., 1000.})
289 329 << DataContainer{1., 2., 3., 4., 5., 6., 7., 8., 9., 10.}
290 330 << DataContainer{100., 200., 300., 400., 500., 600., 700., 800., 900., 1000.};
291 331
292 332 QTest::newRow("unsortedMerge")
293 333 << createScalarSeries({6., 7., 8., 9., 10.}, {600., 700., 800., 900., 1000.})
294 334 << createScalarSeries({1., 2., 3., 4., 5.}, {100., 200., 300., 400., 500.})
295 335 << DataContainer{1., 2., 3., 4., 5., 6., 7., 8., 9., 10.}
296 336 << DataContainer{100., 200., 300., 400., 500., 600., 700., 800., 900., 1000.};
297 337
298 338 QTest::newRow("unsortedMerge2 (merge not made because source is in the bounds of dest)")
299 339 << createScalarSeries({1., 2., 8., 9., 10}, {100., 200., 800., 900., 1000.})
300 340 << createScalarSeries({3., 4., 5., 6., 7.}, {300., 400., 500., 600., 700.})
301 341 << DataContainer{1., 2., 8., 9., 10.} << DataContainer{100., 200., 800., 900., 1000.};
302 342
303 343 QTest::newRow("unsortedMerge3")
304 344 << createScalarSeries({3., 4., 5., 7., 8}, {300., 400., 500., 700., 800.})
305 345 << createScalarSeries({1., 2., 3., 7., 10.}, {100., 200., 333., 777., 1000.})
306 346 << DataContainer{1., 2., 3., 4., 5., 7., 8., 10.}
307 347 << DataContainer{100., 200., 300., 400., 500., 700., 800., 1000.};
348
349 QTest::newRow("emptySource") << createScalarSeries({3., 4., 5., 7., 8},
350 {300., 400., 500., 700., 800.})
351 << createScalarSeries({}, {}) << DataContainer{3., 4., 5., 7., 8.}
352 << DataContainer{300., 400., 500., 700., 800.};
308 353 }
309 354
310 355 void TestDataSeries::testMerge()
311 356 {
312 357 // Merges series
313 358 QFETCH(std::shared_ptr<ScalarSeries>, dataSeries);
314 359 QFETCH(std::shared_ptr<ScalarSeries>, dataSeries2);
315 360
316 361 dataSeries->merge(dataSeries2.get());
317 362
318 363 // Validates results : we check that the merge is valid and the data series is sorted on its
319 364 // x-axis data
320 365 QFETCH(DataContainer, expectedXAxisData);
321 366 QFETCH(DataContainer, expectedValuesData);
322 367
323 368 validateRange(dataSeries->cbegin(), dataSeries->cend(), expectedXAxisData, expectedValuesData);
324 369 }
325 370
371 void TestDataSeries::testMergeVectorInScalar_data()
372 {
373 testMergeDifferentTypesStructure<VectorSeries, ScalarSeries>();
374
375 // ////////// //
376 // Test cases //
377 // ////////// //
378
379 QTest::newRow("purgeVectorInScalar")
380 << createScalarSeries({1., 2., 3., 4., 5.}, {100., 200., 300., 400., 500.})
381 << createVectorSeries({6., 7., 8., 9., 10.}, {600., 700., 800., 900., 1000.},
382 {610., 710., 810., 910., 1010.}, {620., 720., 820., 920., 1020.})
383 << DataContainer{1., 2., 3., 4., 5.} << DataContainer{100., 200., 300., 400., 500.};
384 }
385
386 void TestDataSeries::testMergeVectorInScalar()
387 {
388 testMergeDifferentTypes<VectorSeries, ScalarSeries>();
389 }
390
326 391 void TestDataSeries::testPurgeScalar_data()
327 392 {
328 393 testPurgeStructure<ScalarSeries>();
329 394
330 395 // ////////// //
331 396 // Test cases //
332 397 // ////////// //
333 398
334 399 QTest::newRow("purgeScalar") << createScalarSeries({1., 2., 3., 4., 5.},
335 400 {100., 200., 300., 400., 500.})
336 401 << 2. << 4. << DataContainer{2., 3., 4.}
337 402 << std::vector<DataContainer>{{200., 300., 400.}};
403 QTest::newRow("purgeScalar1 (min/max swap)")
404 << createScalarSeries({1., 2., 3., 4., 5.}, {100., 200., 300., 400., 500.}) << 4. << 2.
405 << DataContainer{2., 3., 4.} << std::vector<DataContainer>{{200., 300., 400.}};
338 406 QTest::newRow("purgeScalar2") << createScalarSeries({1., 2., 3., 4., 5.},
339 407 {100., 200., 300., 400., 500.})
340 408 << 0. << 2.5 << DataContainer{1., 2.}
341 409 << std::vector<DataContainer>{{100., 200.}};
342 410 QTest::newRow("purgeScalar3") << createScalarSeries({1., 2., 3., 4., 5.},
343 411 {100., 200., 300., 400., 500.})
344 412 << 3.5 << 7. << DataContainer{4., 5.}
345 413 << std::vector<DataContainer>{{400., 500.}};
346 414 QTest::newRow("purgeScalar4") << createScalarSeries({1., 2., 3., 4., 5.},
347 415 {100., 200., 300., 400., 500.})
348 416 << 0. << 7. << DataContainer{1., 2., 3., 4., 5.}
349 417 << std::vector<DataContainer>{{100., 200., 300., 400., 500.}};
350 418 QTest::newRow("purgeScalar5") << createScalarSeries({1., 2., 3., 4., 5.},
351 419 {100., 200., 300., 400., 500.})
352 420 << 5.5 << 7. << DataContainer{} << std::vector<DataContainer>{{}};
353 421 }
354 422
355 423 void TestDataSeries::testPurgeScalar()
356 424 {
357 425 testPurge<ScalarSeries>();
358 426 }
359 427
360 428 void TestDataSeries::testPurgeVector_data()
361 429 {
362 430 testPurgeStructure<VectorSeries>();
363 431
364 432 // ////////// //
365 433 // Test cases //
366 434 // ////////// //
367 435
368 436 QTest::newRow("purgeVector") << createVectorSeries({1., 2., 3., 4., 5.}, {6., 7., 8., 9., 10.},
369 437 {11., 12., 13., 14., 15.},
370 438 {16., 17., 18., 19., 20.})
371 439 << 2. << 4. << DataContainer{2., 3., 4.}
372 440 << std::vector<DataContainer>{
373 441 {7., 8., 9.}, {12., 13., 14.}, {17., 18., 19.}};
374 442 }
375 443
376 444 void TestDataSeries::testPurgeVector()
377 445 {
378 446 testPurge<VectorSeries>();
379 447 }
380 448
381 449 void TestDataSeries::testMinXAxisData_data()
382 450 {
383 451 // ////////////// //
384 452 // Test structure //
385 453 // ////////////// //
386 454
387 455 // Data series to get min data
388 456 QTest::addColumn<std::shared_ptr<ScalarSeries> >("dataSeries");
389 457
390 458 // Min data
391 459 QTest::addColumn<double>("min");
392 460
393 461 // Expected results
394 462 QTest::addColumn<bool>(
395 463 "expectedOK"); // if true, expects to have a result (i.e. the iterator != end iterator)
396 464 QTest::addColumn<double>(
397 465 "expectedMin"); // Expected value when method doesn't return end iterator
398 466
399 467 // ////////// //
400 468 // Test cases //
401 469 // ////////// //
402 470
403 471 QTest::newRow("minData1") << createScalarSeries({1., 2., 3., 4., 5.},
404 472 {100., 200., 300., 400., 500.})
405 473 << 0. << true << 1.;
406 474 QTest::newRow("minData2") << createScalarSeries({1., 2., 3., 4., 5.},
407 475 {100., 200., 300., 400., 500.})
408 476 << 1. << true << 1.;
409 477 QTest::newRow("minData3") << createScalarSeries({1., 2., 3., 4., 5.},
410 478 {100., 200., 300., 400., 500.})
411 479 << 1.1 << true << 2.;
412 480 QTest::newRow("minData4") << createScalarSeries({1., 2., 3., 4., 5.},
413 481 {100., 200., 300., 400., 500.})
414 482 << 5. << true << 5.;
415 483 QTest::newRow("minData5") << createScalarSeries({1., 2., 3., 4., 5.},
416 484 {100., 200., 300., 400., 500.})
417 485 << 5.1 << false << std::numeric_limits<double>::quiet_NaN();
418 486 QTest::newRow("minData6") << createScalarSeries({}, {}) << 1.1 << false
419 487 << std::numeric_limits<double>::quiet_NaN();
420 488 }
421 489
422 490 void TestDataSeries::testMinXAxisData()
423 491 {
424 492 QFETCH(std::shared_ptr<ScalarSeries>, dataSeries);
425 493 QFETCH(double, min);
426 494
427 495 QFETCH(bool, expectedOK);
428 496 QFETCH(double, expectedMin);
429 497
430 498 auto it = dataSeries->minXAxisData(min);
431 499
432 500 QCOMPARE(expectedOK, it != dataSeries->cend());
433 501
434 502 // If the method doesn't return a end iterator, checks with expected value
435 503 if (expectedOK) {
436 504 QCOMPARE(expectedMin, it->x());
437 505 }
438 506 }
439 507
440 508 void TestDataSeries::testMaxXAxisData_data()
441 509 {
442 510 // ////////////// //
443 511 // Test structure //
444 512 // ////////////// //
445 513
446 514 // Data series to get max data
447 515 QTest::addColumn<std::shared_ptr<ScalarSeries> >("dataSeries");
448 516
449 517 // Max data
450 518 QTest::addColumn<double>("max");
451 519
452 520 // Expected results
453 521 QTest::addColumn<bool>(
454 522 "expectedOK"); // if true, expects to have a result (i.e. the iterator != end iterator)
455 523 QTest::addColumn<double>(
456 524 "expectedMax"); // Expected value when method doesn't return end iterator
457 525
458 526 // ////////// //
459 527 // Test cases //
460 528 // ////////// //
461 529
462 530 QTest::newRow("maxData1") << createScalarSeries({1., 2., 3., 4., 5.},
463 531 {100., 200., 300., 400., 500.})
464 532 << 6. << true << 5.;
465 533 QTest::newRow("maxData2") << createScalarSeries({1., 2., 3., 4., 5.},
466 534 {100., 200., 300., 400., 500.})
467 535 << 5. << true << 5.;
468 536 QTest::newRow("maxData3") << createScalarSeries({1., 2., 3., 4., 5.},
469 537 {100., 200., 300., 400., 500.})
470 538 << 4.9 << true << 4.;
471 539 QTest::newRow("maxData4") << createScalarSeries({1., 2., 3., 4., 5.},
472 540 {100., 200., 300., 400., 500.})
473 541 << 1.1 << true << 1.;
474 542 QTest::newRow("maxData5") << createScalarSeries({1., 2., 3., 4., 5.},
475 543 {100., 200., 300., 400., 500.})
476 544 << 1. << true << 1.;
477 545 QTest::newRow("maxData6") << createScalarSeries({}, {}) << 1.1 << false
478 546 << std::numeric_limits<double>::quiet_NaN();
479 547 }
480 548
481 549 void TestDataSeries::testMaxXAxisData()
482 550 {
483 551 QFETCH(std::shared_ptr<ScalarSeries>, dataSeries);
484 552 QFETCH(double, max);
485 553
486 554 QFETCH(bool, expectedOK);
487 555 QFETCH(double, expectedMax);
488 556
489 557 auto it = dataSeries->maxXAxisData(max);
490 558
491 559 QCOMPARE(expectedOK, it != dataSeries->cend());
492 560
493 561 // If the method doesn't return a end iterator, checks with expected value
494 562 if (expectedOK) {
495 563 QCOMPARE(expectedMax, it->x());
496 564 }
497 565 }
498 566
499 567 void TestDataSeries::testXAxisRange_data()
500 568 {
501 569 // ////////////// //
502 570 // Test structure //
503 571 // ////////////// //
504 572
505 573 // Data series to get x-axis range
506 574 QTest::addColumn<std::shared_ptr<ScalarSeries> >("dataSeries");
507 575
508 576 // Min/max values
509 577 QTest::addColumn<double>("min");
510 578 QTest::addColumn<double>("max");
511 579
512 580 // Expected values
513 581 QTest::addColumn<DataContainer>("expectedXAxisData");
514 582 QTest::addColumn<DataContainer>("expectedValuesData");
515 583
516 584 // ////////// //
517 585 // Test cases //
518 586 // ////////// //
519 587
520 QTest::newRow("xAxisRange1") << createScalarSeries({1., 2., 3., 4., 5.},
521 {100., 200., 300., 400., 500.})
522 << -1. << 3.2 << DataContainer{1., 2., 3.}
523 << DataContainer{100., 200., 300.};
588 QTest::newRow("xAxisRange") << createScalarSeries({1., 2., 3., 4., 5.},
589 {100., 200., 300., 400., 500.})
590 << -1. << 3.2 << DataContainer{1., 2., 3.}
591 << DataContainer{100., 200., 300.};
592 QTest::newRow("xAxisRange1 (min/max swap)")
593 << createScalarSeries({1., 2., 3., 4., 5.}, {100., 200., 300., 400., 500.}) << 3.2 << -1.
594 << DataContainer{1., 2., 3.} << DataContainer{100., 200., 300.};
524 595 QTest::newRow("xAxisRange2") << createScalarSeries({1., 2., 3., 4., 5.},
525 596 {100., 200., 300., 400., 500.})
526 597 << 1. << 4. << DataContainer{1., 2., 3., 4.}
527 598 << DataContainer{100., 200., 300., 400.};
528 599 QTest::newRow("xAxisRange3") << createScalarSeries({1., 2., 3., 4., 5.},
529 600 {100., 200., 300., 400., 500.})
530 601 << 1. << 3.9 << DataContainer{1., 2., 3.}
531 602 << DataContainer{100., 200., 300.};
532 603 QTest::newRow("xAxisRange4") << createScalarSeries({1., 2., 3., 4., 5.},
533 604 {100., 200., 300., 400., 500.})
534 605 << 0. << 0.9 << DataContainer{} << DataContainer{};
535 606 QTest::newRow("xAxisRange5") << createScalarSeries({1., 2., 3., 4., 5.},
536 607 {100., 200., 300., 400., 500.})
537 608 << 0. << 1. << DataContainer{1.} << DataContainer{100.};
538 609 QTest::newRow("xAxisRange6") << createScalarSeries({1., 2., 3., 4., 5.},
539 610 {100., 200., 300., 400., 500.})
540 611 << 2.1 << 6. << DataContainer{3., 4., 5.}
541 612 << DataContainer{300., 400., 500.};
542 613 QTest::newRow("xAxisRange7") << createScalarSeries({1., 2., 3., 4., 5.},
543 614 {100., 200., 300., 400., 500.})
544 615 << 6. << 9. << DataContainer{} << DataContainer{};
545 616 QTest::newRow("xAxisRange8") << createScalarSeries({1., 2., 3., 4., 5.},
546 617 {100., 200., 300., 400., 500.})
547 618 << 5. << 9. << DataContainer{5.} << DataContainer{500.};
548 619 }
549 620
550 621 void TestDataSeries::testXAxisRange()
551 622 {
552 623 QFETCH(std::shared_ptr<ScalarSeries>, dataSeries);
553 624 QFETCH(double, min);
554 625 QFETCH(double, max);
555 626
556 627 QFETCH(DataContainer, expectedXAxisData);
557 628 QFETCH(DataContainer, expectedValuesData);
558 629
559 630 auto bounds = dataSeries->xAxisRange(min, max);
560 631 validateRange(bounds.first, bounds.second, expectedXAxisData, expectedValuesData);
561 632 }
562 633
563 634 void TestDataSeries::testValuesBoundsScalar_data()
564 635 {
565 636 testValuesBoundsStructure<ScalarSeries>();
566 637
567 638 // ////////// //
568 639 // Test cases //
569 640 // ////////// //
570 641 auto nan = std::numeric_limits<double>::quiet_NaN();
571 642
572 643 QTest::newRow("scalarBounds1")
573 644 << createScalarSeries({1., 2., 3., 4., 5.}, {100., 200., 300., 400., 500.}) << 0. << 6.
574 645 << true << 100. << 500.;
575 646 QTest::newRow("scalarBounds2")
576 647 << createScalarSeries({1., 2., 3., 4., 5.}, {100., 200., 300., 400., 500.}) << 2. << 4.
577 648 << true << 200. << 400.;
578 649 QTest::newRow("scalarBounds3")
579 650 << createScalarSeries({1., 2., 3., 4., 5.}, {100., 200., 300., 400., 500.}) << 0. << 0.5
580 651 << false << nan << nan;
581 652 QTest::newRow("scalarBounds4")
582 653 << createScalarSeries({1., 2., 3., 4., 5.}, {100., 200., 300., 400., 500.}) << 5.1 << 6.
583 654 << false << nan << nan;
584 655 QTest::newRow("scalarBounds5") << createScalarSeries({1.}, {100.}) << 0. << 2. << true << 100.
585 656 << 100.;
586 657 QTest::newRow("scalarBounds6") << createScalarSeries({}, {}) << 0. << 2. << false << nan << nan;
587 658
588 659 // Tests with NaN values: NaN values are not included in min/max search
589 660 QTest::newRow("scalarBounds7")
590 661 << createScalarSeries({1., 2., 3., 4., 5.}, {nan, 200., 300., 400., nan}) << 0. << 6.
591 662 << true << 200. << 400.;
592 663 QTest::newRow("scalarBounds8")
593 664 << createScalarSeries({1., 2., 3., 4., 5.}, {nan, nan, nan, nan, nan}) << 0. << 6. << true
594 665 << std::numeric_limits<double>::quiet_NaN() << std::numeric_limits<double>::quiet_NaN();
595 666 }
596 667
597 668 void TestDataSeries::testValuesBoundsScalar()
598 669 {
599 670 testValuesBounds<ScalarSeries>();
600 671 }
601 672
602 673 void TestDataSeries::testValuesBoundsVector_data()
603 674 {
604 675 testValuesBoundsStructure<VectorSeries>();
605 676
606 677 // ////////// //
607 678 // Test cases //
608 679 // ////////// //
609 680 auto nan = std::numeric_limits<double>::quiet_NaN();
610 681
611 682 QTest::newRow("vectorBounds1")
612 683 << createVectorSeries({1., 2., 3., 4., 5.}, {10., 15., 20., 13., 12.},
613 684 {35., 24., 10., 9., 0.3}, {13., 14., 12., 9., 24.})
614 685 << 0. << 6. << true << 0.3 << 35.; // min/max in same component
615 686 QTest::newRow("vectorBounds2")
616 687 << createVectorSeries({1., 2., 3., 4., 5.}, {2.3, 15., 20., 13., 12.},
617 688 {35., 24., 10., 9., 4.}, {13., 14., 12., 9., 24.})
618 689 << 0. << 6. << true << 2.3 << 35.; // min/max in same entry
619 690 QTest::newRow("vectorBounds3")
620 691 << createVectorSeries({1., 2., 3., 4., 5.}, {2.3, 15., 20., 13., 12.},
621 692 {35., 24., 10., 9., 4.}, {13., 14., 12., 9., 24.})
622 693 << 2. << 3. << true << 10. << 24.;
623 694
624 695 // Tests with NaN values: NaN values are not included in min/max search
625 696 QTest::newRow("vectorBounds4")
626 697 << createVectorSeries({1., 2.}, {nan, nan}, {nan, nan}, {nan, nan}) << 0. << 6. << true
627 698 << nan << nan;
628 699 }
629 700
630 701 void TestDataSeries::testValuesBoundsVector()
631 702 {
632 703 testValuesBounds<VectorSeries>();
633 704 }
634 705
635 706 QTEST_MAIN(TestDataSeries)
636 707 #include "TestDataSeries.moc"
@@ -1,177 +1,409
1 1 #include <Variable/Variable.h>
2 2
3 #include <Data/ScalarSeries.h>
4
3 5 #include <QObject>
4 6 #include <QtTest>
5 7
6 8 #include <memory>
7 9
10 namespace {
11
12 /// Generates a date in double
13 auto date = [](int year, int month, int day, int hours, int minutes, int seconds) {
14 return DateUtils::secondsSinceEpoch(
15 QDateTime{{year, month, day}, {hours, minutes, seconds}, Qt::UTC});
16 };
17
18 /// Generates a series of test data for a range
19 std::shared_ptr<ScalarSeries> dataSeries(const SqpRange &range)
20 {
21 auto xAxisData = std::vector<double>{};
22 auto valuesData = std::vector<double>{};
23
24 auto value = 0;
25 for (auto x = range.m_TStart; x <= range.m_TEnd; ++x, ++value) {
26 xAxisData.push_back(x);
27 valuesData.push_back(value);
28 }
29
30 return std::make_shared<ScalarSeries>(std::move(xAxisData), std::move(valuesData), Unit{},
31 Unit{});
32 }
33
34 } // namespace
35
36 Q_DECLARE_METATYPE(std::shared_ptr<ScalarSeries>)
37
8 38 class TestVariable : public QObject {
9 39 Q_OBJECT
10 40
11 41 private slots:
12 void testNotInCacheRangeList();
42 void testClone_data();
43 void testClone();
13 44
45 void testNotInCacheRangeList();
14 46 void testInCacheRangeList();
47
48 void testNbPoints_data();
49 void testNbPoints();
50
51 void testRealRange_data();
52 void testRealRange();
15 53 };
16 54
55 void TestVariable::testClone_data()
56 {
57 // ////////////// //
58 // Test structure //
59 // ////////////// //
60
61 QTest::addColumn<QString>("name");
62 QTest::addColumn<QVariantHash>("metadata");
63 QTest::addColumn<SqpRange>("range");
64 QTest::addColumn<SqpRange>("cacheRange");
65 QTest::addColumn<std::shared_ptr<ScalarSeries> >("dataSeries");
66
67 // ////////// //
68 // Test cases //
69 // ////////// //
70
71 auto cacheRange = SqpRange{date(2017, 1, 1, 12, 0, 0), date(2017, 1, 1, 13, 0, 0)};
72 QTest::newRow("clone1") << QStringLiteral("var1")
73 << QVariantHash{{"data1", 1}, {"data2", "abc"}}
74 << SqpRange{date(2017, 1, 1, 12, 30, 0), (date(2017, 1, 1, 12, 45, 0))}
75 << cacheRange << dataSeries(cacheRange);
76 }
77
78 void TestVariable::testClone()
79 {
80 // Creates variable
81 QFETCH(QString, name);
82 QFETCH(QVariantHash, metadata);
83 QFETCH(SqpRange, range);
84 QFETCH(SqpRange, cacheRange);
85 QFETCH(std::shared_ptr<ScalarSeries>, dataSeries);
86
87 Variable variable{name, metadata};
88 variable.setRange(range);
89 variable.setCacheRange(cacheRange);
90 variable.mergeDataSeries(dataSeries);
91
92 // Clones variable
93 auto clone = variable.clone();
94
95 // Checks cloned variable's state
96 QCOMPARE(clone->name(), name);
97 QCOMPARE(clone->metadata(), metadata);
98 QCOMPARE(clone->range(), range);
99 QCOMPARE(clone->cacheRange(), cacheRange);
100
101 // Compares data series
102 if (dataSeries != nullptr) {
103 QVERIFY(clone->dataSeries() != nullptr);
104 QVERIFY(std::equal(dataSeries->cbegin(), dataSeries->cend(), clone->dataSeries()->cbegin(),
105 clone->dataSeries()->cend(), [](const auto &it1, const auto &it2) {
106 return it1.x() == it2.x() && it1.value() == it2.value();
107 }));
108 }
109 else {
110 QVERIFY(clone->dataSeries() == nullptr);
111 }
112 }
17 113
18 114 void TestVariable::testNotInCacheRangeList()
19 115 {
20 116 auto varRS = QDateTime{QDate{2017, 01, 01}, QTime{2, 3, 20, 0}};
21 117 auto varRE = QDateTime{QDate{2017, 01, 01}, QTime{2, 3, 40, 0}};
22 118
23 119 auto sqpR = SqpRange{DateUtils::secondsSinceEpoch(varRS), DateUtils::secondsSinceEpoch(varRE)};
24 120
25 121 auto varCRS = QDateTime{QDate{2017, 01, 01}, QTime{2, 3, 0, 0}};
26 122 auto varCRE = QDateTime{QDate{2017, 01, 01}, QTime{2, 4, 0, 0}};
27 123
28 124 auto sqpCR
29 125 = SqpRange{DateUtils::secondsSinceEpoch(varCRS), DateUtils::secondsSinceEpoch(varCRE)};
30 126
31 127 Variable var{"Var test"};
32 128 var.setRange(sqpR);
33 129 var.setCacheRange(sqpCR);
34 130
35 131 // 1: [ts,te] < varTS
36 132 auto ts = QDateTime{QDate{2017, 01, 01}, QTime{2, 0, 0, 0}};
37 133 auto te = QDateTime{QDate{2017, 01, 01}, QTime{2, 1, 0, 0}};
38 134 auto sqp = SqpRange{DateUtils::secondsSinceEpoch(ts), DateUtils::secondsSinceEpoch(te)};
39 135
40 136 auto notInCach = var.provideNotInCacheRangeList(sqp);
41 137
42 138 QCOMPARE(notInCach.size(), 1);
43 139
44 140 auto notInCachRange = notInCach.first();
45 141
46 142 QCOMPARE(notInCachRange.m_TStart, DateUtils::secondsSinceEpoch(ts));
47 143 QCOMPARE(notInCachRange.m_TEnd, DateUtils::secondsSinceEpoch(te));
48 144
49 145 // 2: ts < varTS < te < varTE
50 146 ts = QDateTime{QDate{2017, 01, 01}, QTime{2, 0, 0, 0}};
51 147 te = QDateTime{QDate{2017, 01, 01}, QTime{2, 3, 30, 0}};
52 148 sqp = SqpRange{DateUtils::secondsSinceEpoch(ts), DateUtils::secondsSinceEpoch(te)};
53 149 notInCach = var.provideNotInCacheRangeList(sqp);
54 150 QCOMPARE(notInCach.size(), 1);
55 151 notInCachRange = notInCach.first();
56 152 QCOMPARE(notInCachRange.m_TStart, DateUtils::secondsSinceEpoch(ts));
57 153 QCOMPARE(notInCachRange.m_TEnd, DateUtils::secondsSinceEpoch(varCRS));
58 154
59 155 // 3: varTS < ts < te < varTE
60 156 ts = QDateTime{QDate{2017, 01, 01}, QTime{2, 3, 20, 0}};
61 157 te = QDateTime{QDate{2017, 01, 01}, QTime{2, 3, 30, 0}};
62 158 sqp = SqpRange{DateUtils::secondsSinceEpoch(ts), DateUtils::secondsSinceEpoch(te)};
63 159 notInCach = var.provideNotInCacheRangeList(sqp);
64 160 QCOMPARE(notInCach.size(), 0);
65 161
66 162
67 163 // 4: varTS < ts < varTE < te
68 164 ts = QDateTime{QDate{2017, 01, 01}, QTime{2, 3, 20, 0}};
69 165 te = QDateTime{QDate{2017, 01, 01}, QTime{2, 5, 0, 0}};
70 166 sqp = SqpRange{DateUtils::secondsSinceEpoch(ts), DateUtils::secondsSinceEpoch(te)};
71 167 notInCach = var.provideNotInCacheRangeList(sqp);
72 168 QCOMPARE(notInCach.size(), 1);
73 169 notInCachRange = notInCach.first();
74 170 QCOMPARE(notInCachRange.m_TStart, DateUtils::secondsSinceEpoch(varCRE));
75 171 QCOMPARE(notInCachRange.m_TEnd, DateUtils::secondsSinceEpoch(te));
76 172
77 173 // 5: varTS < varTE < ts < te
78 174 ts = QDateTime{QDate{2017, 01, 01}, QTime{2, 4, 20, 0}};
79 175 te = QDateTime{QDate{2017, 01, 01}, QTime{2, 5, 0, 0}};
80 176 sqp = SqpRange{DateUtils::secondsSinceEpoch(ts), DateUtils::secondsSinceEpoch(te)};
81 177 notInCach = var.provideNotInCacheRangeList(sqp);
82 178 QCOMPARE(notInCach.size(), 1);
83 179 notInCachRange = notInCach.first();
84 180 QCOMPARE(notInCachRange.m_TStart, DateUtils::secondsSinceEpoch(ts));
85 181 QCOMPARE(notInCachRange.m_TEnd, DateUtils::secondsSinceEpoch(te));
86 182
87 183 // 6: ts <varTS < varTE < te
88 184 ts = QDateTime{QDate{2017, 01, 01}, QTime{2, 1, 0, 0}};
89 185 te = QDateTime{QDate{2017, 01, 01}, QTime{2, 5, 0, 0}};
90 186 sqp = SqpRange{DateUtils::secondsSinceEpoch(ts), DateUtils::secondsSinceEpoch(te)};
91 187 notInCach = var.provideNotInCacheRangeList(sqp);
92 188 QCOMPARE(notInCach.size(), 2);
93 189 notInCachRange = notInCach.first();
94 190 QCOMPARE(notInCachRange.m_TStart, DateUtils::secondsSinceEpoch(ts));
95 191 QCOMPARE(notInCachRange.m_TEnd, DateUtils::secondsSinceEpoch(varCRS));
96 192 notInCachRange = notInCach[1];
97 193 QCOMPARE(notInCachRange.m_TStart, DateUtils::secondsSinceEpoch(varCRE));
98 194 QCOMPARE(notInCachRange.m_TEnd, DateUtils::secondsSinceEpoch(te));
99 195 }
100 196
101 197
102 198 void TestVariable::testInCacheRangeList()
103 199 {
104 200 auto varRS = QDateTime{QDate{2017, 01, 01}, QTime{2, 3, 20, 0}};
105 201 auto varRE = QDateTime{QDate{2017, 01, 01}, QTime{2, 3, 40, 0}};
106 202
107 203 auto sqpR = SqpRange{DateUtils::secondsSinceEpoch(varRS), DateUtils::secondsSinceEpoch(varRE)};
108 204
109 205 auto varCRS = QDateTime{QDate{2017, 01, 01}, QTime{2, 3, 0, 0}};
110 206 auto varCRE = QDateTime{QDate{2017, 01, 01}, QTime{2, 4, 0, 0}};
111 207 auto sqpCR
112 208 = SqpRange{DateUtils::secondsSinceEpoch(varCRS), DateUtils::secondsSinceEpoch(varCRE)};
113 209
114 210 Variable var{"Var test"};
115 211 var.setRange(sqpR);
116 212 var.setCacheRange(sqpCR);
117 213
118 214 // 1: [ts,te] < varTS
119 215 auto ts = QDateTime{QDate{2017, 01, 01}, QTime{2, 0, 0, 0}};
120 216 auto te = QDateTime{QDate{2017, 01, 01}, QTime{2, 1, 0, 0}};
121 217 auto sqp = SqpRange{DateUtils::secondsSinceEpoch(ts), DateUtils::secondsSinceEpoch(te)};
122 218
123 219 auto notInCach = var.provideInCacheRangeList(sqp);
124 220
125 221 QCOMPARE(notInCach.size(), 0);
126 222
127 223 // 2: ts < varTS < te < varTE
128 224 ts = QDateTime{QDate{2017, 01, 01}, QTime{2, 0, 0, 0}};
129 225 te = QDateTime{QDate{2017, 01, 01}, QTime{2, 3, 30, 0}};
130 226 sqp = SqpRange{DateUtils::secondsSinceEpoch(ts), DateUtils::secondsSinceEpoch(te)};
131 227 notInCach = var.provideInCacheRangeList(sqp);
132 228 QCOMPARE(notInCach.size(), 1);
133 229 auto notInCachRange = notInCach.first();
134 230 QCOMPARE(notInCachRange.m_TStart, DateUtils::secondsSinceEpoch(varCRS));
135 231 QCOMPARE(notInCachRange.m_TEnd, DateUtils::secondsSinceEpoch(te));
136 232
137 233 // 3: varTS < ts < te < varTE
138 234 ts = QDateTime{QDate{2017, 01, 01}, QTime{2, 3, 20, 0}};
139 235 te = QDateTime{QDate{2017, 01, 01}, QTime{2, 3, 30, 0}};
140 236 sqp = SqpRange{DateUtils::secondsSinceEpoch(ts), DateUtils::secondsSinceEpoch(te)};
141 237 notInCach = var.provideInCacheRangeList(sqp);
142 238 QCOMPARE(notInCach.size(), 1);
143 239 notInCachRange = notInCach.first();
144 240 QCOMPARE(notInCachRange.m_TStart, DateUtils::secondsSinceEpoch(ts));
145 241 QCOMPARE(notInCachRange.m_TEnd, DateUtils::secondsSinceEpoch(te));
146 242
147 243 // 4: varTS < ts < varTE < te
148 244 ts = QDateTime{QDate{2017, 01, 01}, QTime{2, 3, 20, 0}};
149 245 te = QDateTime{QDate{2017, 01, 01}, QTime{2, 5, 0, 0}};
150 246 sqp = SqpRange{DateUtils::secondsSinceEpoch(ts), DateUtils::secondsSinceEpoch(te)};
151 247 notInCach = var.provideInCacheRangeList(sqp);
152 248 QCOMPARE(notInCach.size(), 1);
153 249 notInCachRange = notInCach.first();
154 250 QCOMPARE(notInCachRange.m_TStart, DateUtils::secondsSinceEpoch(ts));
155 251 QCOMPARE(notInCachRange.m_TEnd, DateUtils::secondsSinceEpoch(varCRE));
156 252
157 253 // 5: varTS < varTE < ts < te
158 254 ts = QDateTime{QDate{2017, 01, 01}, QTime{2, 4, 20, 0}};
159 255 te = QDateTime{QDate{2017, 01, 01}, QTime{2, 5, 0, 0}};
160 256 sqp = SqpRange{DateUtils::secondsSinceEpoch(ts), DateUtils::secondsSinceEpoch(te)};
161 257 notInCach = var.provideInCacheRangeList(sqp);
162 258 QCOMPARE(notInCach.size(), 0);
163 259
164 260 // 6: ts <varTS < varTE < te
165 261 ts = QDateTime{QDate{2017, 01, 01}, QTime{2, 1, 0, 0}};
166 262 te = QDateTime{QDate{2017, 01, 01}, QTime{2, 5, 0, 0}};
167 263 sqp = SqpRange{DateUtils::secondsSinceEpoch(ts), DateUtils::secondsSinceEpoch(te)};
168 264 notInCach = var.provideInCacheRangeList(sqp);
169 265 QCOMPARE(notInCach.size(), 1);
170 266 notInCachRange = notInCach.first();
171 267 QCOMPARE(notInCachRange.m_TStart, DateUtils::secondsSinceEpoch(varCRS));
172 268 QCOMPARE(notInCachRange.m_TEnd, DateUtils::secondsSinceEpoch(varCRE));
173 269 }
174 270
271 namespace {
272
273 /// Struct used to represent an operation for @sa TestVariable::testNbPoints()
274 struct NbPointsOperation {
275 SqpRange m_CacheRange; /// Range to set for the variable
276 std::shared_ptr<ScalarSeries> m_DataSeries; /// Series to merge in the variable
277 int m_ExpectedNbPoints; /// Number of points in the variable expected after operation
278 };
279
280 using NbPointsOperations = std::vector<NbPointsOperation>;
281
282 } // namespace
283
284 Q_DECLARE_METATYPE(NbPointsOperations)
285
286 void TestVariable::testNbPoints_data()
287 {
288 // ////////////// //
289 // Test structure //
290 // ////////////// //
291
292 QTest::addColumn<NbPointsOperations>("operations");
293
294 // ////////// //
295 // Test cases //
296 // ////////// //
297 NbPointsOperations operations{};
298
299 // Sets cache range (expected nb points = series xAxis data + series values data)
300 auto cacheRange = SqpRange{date(2017, 1, 1, 12, 0, 0), date(2017, 1, 1, 12, 0, 9)};
301 operations.push_back({cacheRange, dataSeries(cacheRange), 20});
302
303 // Doubles cache but don't add data series (expected nb points don't change)
304 cacheRange = SqpRange{date(2017, 1, 1, 12, 0, 0), date(2017, 1, 1, 12, 0, 19)};
305 operations.push_back({cacheRange, nullptr, 20});
306
307 // Doubles cache and data series (expected nb points change)
308 cacheRange = SqpRange{date(2017, 1, 1, 12, 0, 0), date(2017, 1, 1, 12, 0, 19)};
309 operations.push_back({cacheRange, dataSeries(cacheRange), 40});
310
311 // Decreases cache (expected nb points decreases as the series is purged)
312 cacheRange = SqpRange{date(2017, 1, 1, 12, 0, 5), date(2017, 1, 1, 12, 0, 9)};
313 operations.push_back({cacheRange, nullptr, 10});
314
315 QTest::newRow("nbPoints1") << operations;
316 }
317
318 void TestVariable::testNbPoints()
319 {
320 // Creates variable
321 Variable variable{"var"};
322 QCOMPARE(variable.nbPoints(), 0);
323
324 QFETCH(NbPointsOperations, operations);
325 for (const auto &operation : operations) {
326 // Sets cache range and merge data series
327 variable.setCacheRange(operation.m_CacheRange);
328 if (operation.m_DataSeries != nullptr) {
329 variable.mergeDataSeries(operation.m_DataSeries);
330 }
331
332 // Checks nb points
333 QCOMPARE(variable.nbPoints(), operation.m_ExpectedNbPoints);
334 }
335 }
336
337 namespace {
338
339 /// Struct used to represent a range operation on a variable
340 /// @sa TestVariable::testRealRange()
341 struct RangeOperation {
342 SqpRange m_CacheRange; /// Range to set for the variable
343 std::shared_ptr<ScalarSeries> m_DataSeries; /// Series to merge in the variable
344 SqpRange m_ExpectedRealRange; /// Real Range expected after operation on the variable
345 };
346
347 using RangeOperations = std::vector<RangeOperation>;
348
349 } // namespace
350
351 Q_DECLARE_METATYPE(RangeOperations)
352
353 void TestVariable::testRealRange_data()
354 {
355 // ////////////// //
356 // Test structure //
357 // ////////////// //
358
359 QTest::addColumn<RangeOperations>("operations");
360
361 // ////////// //
362 // Test cases //
363 // ////////// //
364 RangeOperations operations{};
365
366 // Inits cache range and data series (expected real range = cache range)
367 auto cacheRange = SqpRange{date(2017, 1, 1, 12, 0, 0), date(2017, 1, 1, 13, 0, 0)};
368 operations.push_back({cacheRange, dataSeries(cacheRange), cacheRange});
369
370 // Changes cache range and updates data series (expected real range = cache range)
371 cacheRange = SqpRange{date(2017, 1, 1, 14, 0, 0), date(2017, 1, 1, 15, 0, 0)};
372 operations.push_back({cacheRange, dataSeries(cacheRange), cacheRange});
373
374 // Changes cache range and update data series but with a lower range (expected real range =
375 // data series range)
376 cacheRange = SqpRange{date(2017, 1, 1, 12, 0, 0), date(2017, 1, 1, 16, 0, 0)};
377 auto dataSeriesRange = SqpRange{date(2017, 1, 1, 14, 0, 0), date(2017, 1, 1, 15, 0, 0)};
378 operations.push_back({cacheRange, dataSeries(dataSeriesRange), dataSeriesRange});
379
380 // Changes cache range but DON'T update data series (expected real range = cache range
381 // before operation)
382 cacheRange = SqpRange{date(2017, 1, 1, 10, 0, 0), date(2017, 1, 1, 17, 0, 0)};
383 operations.push_back({cacheRange, nullptr, dataSeriesRange});
384
385 QTest::newRow("realRange1") << operations;
386 }
387
388 void TestVariable::testRealRange()
389 {
390 // Creates variable (real range is invalid)
391 Variable variable{"var"};
392 QCOMPARE(variable.realRange(), INVALID_RANGE);
393
394 QFETCH(RangeOperations, operations);
395 for (const auto &operation : operations) {
396 // Sets cache range and merge data series
397 variable.setCacheRange(operation.m_CacheRange);
398 if (operation.m_DataSeries != nullptr) {
399 variable.mergeDataSeries(operation.m_DataSeries);
400 }
401
402 // Checks real range
403 QCOMPARE(variable.realRange(), operation.m_ExpectedRealRange);
404 }
405 }
406
175 407
176 408 QTEST_MAIN(TestVariable)
177 409 #include "TestVariable.moc"
@@ -1,45 +1,46
1 1
2 2 mockplugin_moc_headers = [
3 3 'include/MockPlugin.h'
4 4 ]
5 5
6 6 mockplugin_sources = [
7 'src/MockDefs.cpp',
7 8 'src/CosinusProvider.cpp',
8 9 'src/MockPlugin.cpp'
9 10 ]
10 11
11 12 mockplugin_inc = include_directories(['include', '../../plugin/include'])
12 13
13 14
14 15 gen = generator(moc,
15 16 output : 'moc_@BASENAME@.cpp',
16 17 arguments : ['@INPUT@',
17 18 '-DPLUGIN_JSON_FILE_PATH="'+meson.source_root()+'/plugins/mockplugin/resources/mockplugin.json"',
18 19 '-I', meson.current_source_dir()+'/include',
19 20 '-I', meson.current_source_dir()+'/../../plugin/include',
20 21 '-o', '@OUTPUT@'])
21 22
22 23 mockplugin_moc_files = gen.process(mockplugin_moc_headers)
23 24
24 25 sciqlop_mockplugin = library('mockplugin',
25 26 mockplugin_sources,
26 27 mockplugin_moc_files,
27 28 cpp_args : '-DMOCKPLUGIN_LIB',
28 29 include_directories : [mockplugin_inc],
29 30 dependencies : [sciqlop_core, sciqlop_gui],
30 31 install : true
31 32 )
32 33
33 34 tests = [
34 35 [['tests/TestCosinusAcquisition.cpp'],'test_cosinus_acquisition','Cosinus Acquisition test']
35 36 ]
36 37
37 38 foreach unit_test : tests
38 39 test_moc_files = qt5.preprocess(moc_sources : unit_test[0])
39 40 test_exe = executable(unit_test[1],unit_test[0] , test_moc_files,
40 41 link_with : [sciqlop_mockplugin],
41 42 include_directories : [mockplugin_inc],
42 43 cpp_args : ['-DMOCKPLUGIN_TESTS_RESOURCES_DIR="'+meson.current_source_dir()+'/tests-resources"'],
43 44 dependencies : [sciqlop_core, sciqlop_gui, qt5test])
44 45 test(unit_test[2], test_exe, args: ['-teamcity', '-o', '@0@.teamcity.txt'.format(unit_test[1])], timeout: 3 * 60)
45 46 endforeach No newline at end of file
General Comments 0
You need to be logged in to leave comments. Login now