##// END OF EJS Templates
Made Variable data update atomic ease thread safety and avoid mixing...
jeandet -
r16:5da3a19e8770
parent child
Show More
@@ -1,507 +1,510
1 #ifndef SCIQLOP_DATASERIES_H
1 #ifndef SCIQLOP_DATASERIES_H
2 #define SCIQLOP_DATASERIES_H
2 #define SCIQLOP_DATASERIES_H
3
3
4 #include "CoreGlobal.h"
4 #include "CoreGlobal.h"
5
5
6 #include <Common/SortUtils.h>
6 #include <Common/SortUtils.h>
7
7
8 #include <Data/ArrayData.h>
8 #include <Data/ArrayData.h>
9 #include <Data/DataSeriesMergeHelper.h>
9 #include <Data/DataSeriesMergeHelper.h>
10 #include <Data/IDataSeries.h>
10 #include <Data/IDataSeries.h>
11 #include <Data/OptionalAxis.h>
11 #include <Data/OptionalAxis.h>
12
12
13 #include <QLoggingCategory>
13 #include <QLoggingCategory>
14 #include <QReadLocker>
14 #include <QReadLocker>
15 #include <QReadWriteLock>
15 #include <QReadWriteLock>
16 #include <memory>
16 #include <memory>
17
17
18 // We don't use the Qt macro since the log is used in the header file, which causes multiple log
18 // We don't use the Qt macro since the log is used in the header file, which causes multiple log
19 // definitions with inheritance. Inline method is used instead
19 // definitions with inheritance. Inline method is used instead
20 inline const QLoggingCategory &LOG_DataSeries()
20 inline const QLoggingCategory &LOG_DataSeries()
21 {
21 {
22 static const QLoggingCategory category{"DataSeries"};
22 static const QLoggingCategory category{"DataSeries"};
23 return category;
23 return category;
24 }
24 }
25
25
26 template <int Dim>
26 template <int Dim>
27 class DataSeries;
27 class DataSeries;
28
28
29 namespace dataseries_detail {
29 namespace dataseries_detail {
30
30
31 template <int Dim, bool IsConst>
31 template <int Dim, bool IsConst>
32 class IteratorValue : public DataSeriesIteratorValue::Impl {
32 class IteratorValue : public DataSeriesIteratorValue::Impl {
33 public:
33 public:
34 friend class DataSeries<Dim>;
34 friend class DataSeries<Dim>;
35
35
36 template <bool IC = IsConst, typename = std::enable_if_t<IC == false> >
36 template <bool IC = IsConst, typename = std::enable_if_t<IC == false> >
37 explicit IteratorValue(DataSeries<Dim> &dataSeries, bool begin)
37 explicit IteratorValue(DataSeries<Dim> &dataSeries, bool begin)
38 : m_XIt(begin ? dataSeries.xAxisData()->begin() : dataSeries.xAxisData()->end()),
38 : m_XIt(begin ? dataSeries.xAxisData()->begin() : dataSeries.xAxisData()->end()),
39 m_ValuesIt(begin ? dataSeries.valuesData()->begin() : dataSeries.valuesData()->end()),
39 m_ValuesIt(begin ? dataSeries.valuesData()->begin() : dataSeries.valuesData()->end()),
40 m_YItBegin{dataSeries.yAxis().begin()},
40 m_YItBegin{dataSeries.yAxis().begin()},
41 m_YItEnd{dataSeries.yAxis().end()}
41 m_YItEnd{dataSeries.yAxis().end()}
42 {
42 {
43 }
43 }
44
44
45 template <bool IC = IsConst, typename = std::enable_if_t<IC == true> >
45 template <bool IC = IsConst, typename = std::enable_if_t<IC == true> >
46 explicit IteratorValue(const DataSeries<Dim> &dataSeries, bool begin)
46 explicit IteratorValue(const DataSeries<Dim> &dataSeries, bool begin)
47 : m_XIt(begin ? dataSeries.xAxisData()->cbegin() : dataSeries.xAxisData()->cend()),
47 : m_XIt(begin ? dataSeries.xAxisData()->cbegin() : dataSeries.xAxisData()->cend()),
48 m_ValuesIt(begin ? dataSeries.valuesData()->cbegin()
48 m_ValuesIt(begin ? dataSeries.valuesData()->cbegin()
49 : dataSeries.valuesData()->cend()),
49 : dataSeries.valuesData()->cend()),
50 m_YItBegin{dataSeries.yAxis().cbegin()},
50 m_YItBegin{dataSeries.yAxis().cbegin()},
51 m_YItEnd{dataSeries.yAxis().cend()}
51 m_YItEnd{dataSeries.yAxis().cend()}
52 {
52 {
53 }
53 }
54
54
55 IteratorValue(const IteratorValue &other) = default;
55 IteratorValue(const IteratorValue &other) = default;
56
56
57 std::unique_ptr<DataSeriesIteratorValue::Impl> clone() const override
57 std::unique_ptr<DataSeriesIteratorValue::Impl> clone() const override
58 {
58 {
59 return std::make_unique<IteratorValue<Dim, IsConst> >(*this);
59 return std::make_unique<IteratorValue<Dim, IsConst> >(*this);
60 }
60 }
61
61
62 int distance(const DataSeriesIteratorValue::Impl &other) const override try {
62 int distance(const DataSeriesIteratorValue::Impl &other) const override try {
63 const auto &otherImpl = dynamic_cast<const IteratorValue &>(other);
63 const auto &otherImpl = dynamic_cast<const IteratorValue &>(other);
64 return m_XIt->distance(*otherImpl.m_XIt);
64 return m_XIt->distance(*otherImpl.m_XIt);
65 }
65 }
66 catch (const std::bad_cast &) {
66 catch (const std::bad_cast &) {
67 return 0;
67 return 0;
68 }
68 }
69
69
70 bool equals(const DataSeriesIteratorValue::Impl &other) const override try {
70 bool equals(const DataSeriesIteratorValue::Impl &other) const override try {
71 const auto &otherImpl = dynamic_cast<const IteratorValue &>(other);
71 const auto &otherImpl = dynamic_cast<const IteratorValue &>(other);
72 return std::tie(m_XIt, m_ValuesIt, m_YItBegin, m_YItEnd)
72 return std::tie(m_XIt, m_ValuesIt, m_YItBegin, m_YItEnd)
73 == std::tie(otherImpl.m_XIt, otherImpl.m_ValuesIt, otherImpl.m_YItBegin,
73 == std::tie(otherImpl.m_XIt, otherImpl.m_ValuesIt, otherImpl.m_YItBegin,
74 otherImpl.m_YItEnd);
74 otherImpl.m_YItEnd);
75 }
75 }
76 catch (const std::bad_cast &) {
76 catch (const std::bad_cast &) {
77 return false;
77 return false;
78 }
78 }
79
79
80 bool lowerThan(const DataSeriesIteratorValue::Impl &other) const override try {
80 bool lowerThan(const DataSeriesIteratorValue::Impl &other) const override try {
81 const auto &otherImpl = dynamic_cast<const IteratorValue &>(other);
81 const auto &otherImpl = dynamic_cast<const IteratorValue &>(other);
82 return m_XIt->lowerThan(*otherImpl.m_XIt);
82 return m_XIt->lowerThan(*otherImpl.m_XIt);
83 }
83 }
84 catch (const std::bad_cast &) {
84 catch (const std::bad_cast &) {
85 return false;
85 return false;
86 }
86 }
87
87
88 std::unique_ptr<DataSeriesIteratorValue::Impl> advance(int offset) const override
88 std::unique_ptr<DataSeriesIteratorValue::Impl> advance(int offset) const override
89 {
89 {
90 auto result = clone();
90 auto result = clone();
91 result->next(offset);
91 result->next(offset);
92 return result;
92 return result;
93 }
93 }
94
94
95 void next(int offset) override
95 void next(int offset) override
96 {
96 {
97 m_XIt->next(offset);
97 m_XIt->next(offset);
98 m_ValuesIt->next(offset);
98 m_ValuesIt->next(offset);
99 }
99 }
100
100
101 void prev() override
101 void prev() override
102 {
102 {
103 --m_XIt;
103 --m_XIt;
104 --m_ValuesIt;
104 --m_ValuesIt;
105 }
105 }
106
106
107 double x() const override { return m_XIt->at(0); }
107 double x() const override { return m_XIt->at(0); }
108 std::vector<double> y() const override
108 std::vector<double> y() const override
109 {
109 {
110 std::vector<double> result{};
110 std::vector<double> result{};
111 std::transform(m_YItBegin, m_YItEnd, std::back_inserter(result),
111 std::transform(m_YItBegin, m_YItEnd, std::back_inserter(result),
112 [](const auto &it) { return it.first(); });
112 [](const auto &it) { return it.first(); });
113
113
114 return result;
114 return result;
115 }
115 }
116
116
117 double value() const override { return m_ValuesIt->at(0); }
117 double value() const override { return m_ValuesIt->at(0); }
118 double value(int componentIndex) const override { return m_ValuesIt->at(componentIndex); }
118 double value(int componentIndex) const override { return m_ValuesIt->at(componentIndex); }
119 double minValue() const override { return m_ValuesIt->min(); }
119 double minValue() const override { return m_ValuesIt->min(); }
120 double maxValue() const override { return m_ValuesIt->max(); }
120 double maxValue() const override { return m_ValuesIt->max(); }
121 QVector<double> values() const override { return m_ValuesIt->values(); }
121 QVector<double> values() const override { return m_ValuesIt->values(); }
122
122
123 void swap(DataSeriesIteratorValue::Impl &other) override
123 void swap(DataSeriesIteratorValue::Impl &other) override
124 {
124 {
125 auto &otherImpl = dynamic_cast<IteratorValue &>(other);
125 auto &otherImpl = dynamic_cast<IteratorValue &>(other);
126 m_XIt->impl()->swap(*otherImpl.m_XIt->impl());
126 m_XIt->impl()->swap(*otherImpl.m_XIt->impl());
127 m_ValuesIt->impl()->swap(*otherImpl.m_ValuesIt->impl());
127 m_ValuesIt->impl()->swap(*otherImpl.m_ValuesIt->impl());
128 m_YItBegin->impl()->swap(*otherImpl.m_YItBegin->impl());
128 m_YItBegin->impl()->swap(*otherImpl.m_YItBegin->impl());
129 m_YItEnd->impl()->swap(*otherImpl.m_YItEnd->impl());
129 m_YItEnd->impl()->swap(*otherImpl.m_YItEnd->impl());
130 }
130 }
131
131
132 private:
132 private:
133 ArrayDataIterator m_XIt;
133 ArrayDataIterator m_XIt;
134 ArrayDataIterator m_ValuesIt;
134 ArrayDataIterator m_ValuesIt;
135 ArrayDataIterator m_YItBegin;
135 ArrayDataIterator m_YItBegin;
136 ArrayDataIterator m_YItEnd;
136 ArrayDataIterator m_YItEnd;
137 };
137 };
138 } // namespace dataseries_detail
138 } // namespace dataseries_detail
139
139
140 /**
140 /**
141 * @brief The DataSeries class is the base (abstract) implementation of IDataSeries.
141 * @brief The DataSeries class is the base (abstract) implementation of IDataSeries.
142 *
142 *
143 * The DataSeries represents values on one or two axes, according to these rules:
143 * The DataSeries represents values on one or two axes, according to these rules:
144 * - the x-axis is always defined
144 * - the x-axis is always defined
145 * - an y-axis can be defined or not. If set, additional consistency checks apply to the values (see
145 * - an y-axis can be defined or not. If set, additional consistency checks apply to the values (see
146 * below)
146 * below)
147 * - the values are defined on one or two dimensions. In the case of 2-dim values, the data is
147 * - the values are defined on one or two dimensions. In the case of 2-dim values, the data is
148 * distributed into components (for example, a vector defines three components)
148 * distributed into components (for example, a vector defines three components)
149 * - New values can be added to the series, on the x-axis.
149 * - New values can be added to the series, on the x-axis.
150 * - Once initialized to the series creation, the y-axis (if defined) is no longer modifiable
150 * - Once initialized to the series creation, the y-axis (if defined) is no longer modifiable
151 * - Data representing values and axes are associated with a unit
151 * - Data representing values and axes are associated with a unit
152 * - The data series is always sorted in ascending order on the x-axis.
152 * - The data series is always sorted in ascending order on the x-axis.
153 *
153 *
154 * Consistency checks are carried out between the axes and the values. These controls are provided
154 * Consistency checks are carried out between the axes and the values. These controls are provided
155 * throughout the DataSeries lifecycle:
155 * throughout the DataSeries lifecycle:
156 * - the number of data on the x-axis must be equal to the number of values (in the case of
156 * - the number of data on the x-axis must be equal to the number of values (in the case of
157 * 2-dim ArrayData for values, the test is performed on the number of values per component)
157 * 2-dim ArrayData for values, the test is performed on the number of values per component)
158 * - if the y-axis is defined, the number of components of the ArrayData for values must equal the
158 * - if the y-axis is defined, the number of components of the ArrayData for values must equal the
159 * number of data on the y-axis.
159 * number of data on the y-axis.
160 *
160 *
161 * Examples:
161 * Examples:
162 * 1)
162 * 1)
163 * - x-axis: [1 ; 2 ; 3]
163 * - x-axis: [1 ; 2 ; 3]
164 * - y-axis: not defined
164 * - y-axis: not defined
165 * - values: [10 ; 20 ; 30] (1-dim ArrayData)
165 * - values: [10 ; 20 ; 30] (1-dim ArrayData)
166 * => the DataSeries is valid, as x-axis and values have the same number of data
166 * => the DataSeries is valid, as x-axis and values have the same number of data
167 *
167 *
168 * 2)
168 * 2)
169 * - x-axis: [1 ; 2 ; 3]
169 * - x-axis: [1 ; 2 ; 3]
170 * - y-axis: not defined
170 * - y-axis: not defined
171 * - values: [10 ; 20 ; 30 ; 40] (1-dim ArrayData)
171 * - values: [10 ; 20 ; 30 ; 40] (1-dim ArrayData)
172 * => the DataSeries is invalid, as x-axis and values haven't the same number of data
172 * => the DataSeries is invalid, as x-axis and values haven't the same number of data
173 *
173 *
174 * 3)
174 * 3)
175 * - x-axis: [1 ; 2 ; 3]
175 * - x-axis: [1 ; 2 ; 3]
176 * - y-axis: not defined
176 * - y-axis: not defined
177 * - values: [10 ; 20 ; 30
177 * - values: [10 ; 20 ; 30
178 * 40 ; 50 ; 60] (2-dim ArrayData)
178 * 40 ; 50 ; 60] (2-dim ArrayData)
179 * => the DataSeries is valid, as x-axis has 3 data and values contains 2 components with 3
179 * => the DataSeries is valid, as x-axis has 3 data and values contains 2 components with 3
180 * data each
180 * data each
181 *
181 *
182 * 4)
182 * 4)
183 * - x-axis: [1 ; 2 ; 3]
183 * - x-axis: [1 ; 2 ; 3]
184 * - y-axis: [1 ; 2]
184 * - y-axis: [1 ; 2]
185 * - values: [10 ; 20 ; 30
185 * - values: [10 ; 20 ; 30
186 * 40 ; 50 ; 60] (2-dim ArrayData)
186 * 40 ; 50 ; 60] (2-dim ArrayData)
187 * => the DataSeries is valid, as:
187 * => the DataSeries is valid, as:
188 * - x-axis has 3 data and values contains 2 components with 3 data each AND
188 * - x-axis has 3 data and values contains 2 components with 3 data each AND
189 * - y-axis has 2 data and values contains 2 components
189 * - y-axis has 2 data and values contains 2 components
190 *
190 *
191 * 5)
191 * 5)
192 * - x-axis: [1 ; 2 ; 3]
192 * - x-axis: [1 ; 2 ; 3]
193 * - y-axis: [1 ; 2 ; 3]
193 * - y-axis: [1 ; 2 ; 3]
194 * - values: [10 ; 20 ; 30
194 * - values: [10 ; 20 ; 30
195 * 40 ; 50 ; 60] (2-dim ArrayData)
195 * 40 ; 50 ; 60] (2-dim ArrayData)
196 * => the DataSeries is invalid, as:
196 * => the DataSeries is invalid, as:
197 * - x-axis has 3 data and values contains 2 components with 3 data each BUT
197 * - x-axis has 3 data and values contains 2 components with 3 data each BUT
198 * - y-axis has 3 data and values contains only 2 components
198 * - y-axis has 3 data and values contains only 2 components
199 *
199 *
200 * @tparam Dim The dimension of the values data
200 * @tparam Dim The dimension of the values data
201 *
201 *
202 */
202 */
203 template <int Dim>
203 template <int Dim>
204 class SCIQLOP_CORE_EXPORT DataSeries : public IDataSeries {
204 class SCIQLOP_CORE_EXPORT DataSeries : public IDataSeries {
205 friend class DataSeriesMergeHelper;
205 friend class DataSeriesMergeHelper;
206
206
207 public:
207 public:
208 /// @sa IDataSeries::xAxisData()
208 /// @sa IDataSeries::xAxisData()
209 std::shared_ptr<ArrayData<1> > xAxisData() override { return m_XAxisData; }
209 std::shared_ptr<ArrayData<1> > xAxisData() override { return m_XAxisData; }
210 const std::shared_ptr<ArrayData<1> > xAxisData() const { return m_XAxisData; }
210 const std::shared_ptr<ArrayData<1> > xAxisData() const override { return m_XAxisData; }
211
211
212 /// @sa IDataSeries::xAxisUnit()
212 /// @sa IDataSeries::xAxisUnit()
213 Unit xAxisUnit() const override { return m_XAxisUnit; }
213 Unit xAxisUnit() const override { return m_XAxisUnit; }
214
214
215 /// @sa IDataSeries::yAxisUnit()
215 /// @sa IDataSeries::yAxisUnit()
216 Unit yAxisUnit() const override { return m_YAxis.unit(); }
216 Unit yAxisUnit() const override { return m_YAxis.unit(); }
217
217
218 /// @return the values dataset
218 /// @return the values dataset
219 std::shared_ptr<ArrayData<Dim> > valuesData() { return m_ValuesData; }
219 std::shared_ptr<ArrayData<Dim> > valuesData() { return m_ValuesData; }
220 const std::shared_ptr<ArrayData<Dim> > valuesData() const { return m_ValuesData; }
220 const std::shared_ptr<ArrayData<Dim> > valuesData() const { return m_ValuesData; }
221
221
222 /// @sa IDataSeries::valuesUnit()
222 /// @sa IDataSeries::valuesUnit()
223 Unit valuesUnit() const override { return m_ValuesUnit; }
223 Unit valuesUnit() const override { return m_ValuesUnit; }
224
224
225 int nbPoints() const override { return m_ValuesData->totalSize(); }
225 int nbPoints() const override { return m_ValuesData->totalSize(); }
226
226
227 std::pair<double, double> yBounds() const override { return m_YAxis.bounds(); }
227 std::pair<double, double> yBounds() const override { return m_YAxis.bounds(); }
228
228
229 void clear()
229 void clear()
230 {
230 {
231 m_XAxisData->clear();
231 m_XAxisData->clear();
232 m_ValuesData->clear();
232 m_ValuesData->clear();
233 }
233 }
234
234
235 bool isEmpty() const noexcept { return m_XAxisData->size() == 0; }
235 bool isEmpty() const noexcept { return m_XAxisData->size() == 0; }
236
236
237 /// Merges into the data series an other data series.
237 /// Merges into the data series an other data series.
238 ///
238 ///
239 /// The two dataseries:
239 /// The two dataseries:
240 /// - must be of the same dimension
240 /// - must be of the same dimension
241 /// - must have the same y-axis (if defined)
241 /// - must have the same y-axis (if defined)
242 ///
242 ///
243 /// If the prerequisites are not valid, the method does nothing
243 /// If the prerequisites are not valid, the method does nothing
244 ///
244 ///
245 /// @remarks the data series to merge with is cleared after the operation
245 /// @remarks the data series to merge with is cleared after the operation
246 void merge(IDataSeries *dataSeries) override
246 void merge(IDataSeries *dataSeries) override
247 {
247 {
248 dataSeries->lockWrite();
248 dataSeries->lockWrite();
249 lockWrite();
249 lockWrite();
250
250
251 if (auto other = dynamic_cast<DataSeries<Dim> *>(dataSeries)) {
251 if (auto other = dynamic_cast<DataSeries<Dim> *>(dataSeries)) {
252 if (m_YAxis == other->m_YAxis) {
252 if (m_YAxis == other->m_YAxis) {
253 DataSeriesMergeHelper::merge(*other, *this);
253 DataSeriesMergeHelper::merge(*other, *this);
254 }
254 }
255 else {
255 else {
256 qCWarning(LOG_DataSeries())
256 qCWarning(LOG_DataSeries())
257 << QObject::tr("Can't merge data series that have not the same y-axis");
257 << QObject::tr("Can't merge data series that have not the same y-axis");
258 }
258 }
259 }
259 }
260 else {
260 else {
261 qCWarning(LOG_DataSeries())
261 qCWarning(LOG_DataSeries())
262 << QObject::tr("Detection of a type of IDataSeries we cannot merge with !");
262 << QObject::tr("Detection of a type of IDataSeries we cannot merge with !");
263 }
263 }
264 unlock();
264 unlock();
265 dataSeries->unlock();
265 dataSeries->unlock();
266 }
266 }
267
267
268 void purge(double min, double max) override
268 void purge(double min, double max) override
269 {
269 {
270 // Nothing to purge if series is empty
270 // Nothing to purge if series is empty
271 if (isEmpty()) {
271 if (isEmpty()) {
272 return;
272 return;
273 }
273 }
274
274
275 if (min > max) {
275 if (min > max) {
276 std::swap(min, max);
276 std::swap(min, max);
277 }
277 }
278
278
279 // Nothing to purge if series min/max are inside purge range
279 // Nothing to purge if series min/max are inside purge range
280 auto xMin = cbegin()->x();
280 auto xMin = cbegin()->x();
281 auto xMax = (--cend())->x();
281 auto xMax = (--cend())->x();
282 if (xMin >= min && xMax <= max) {
282 if (xMin >= min && xMax <= max) {
283 return;
283 return;
284 }
284 }
285
285
286 auto lowerIt = std::lower_bound(
286 auto lowerIt = std::lower_bound(
287 begin(), end(), min, [](const auto &it, const auto &val) { return it.x() < val; });
287 begin(), end(), min, [](const auto &it, const auto &val) { return it.x() < val; });
288 erase(begin(), lowerIt);
288 erase(begin(), lowerIt);
289 auto upperIt = std::upper_bound(
289 auto upperIt = std::upper_bound(
290 begin(), end(), max, [](const auto &val, const auto &it) { return val < it.x(); });
290 begin(), end(), max, [](const auto &val, const auto &it) { return val < it.x(); });
291 erase(upperIt, end());
291 erase(upperIt, end());
292 }
292 }
293
293
294 // ///////// //
294 // ///////// //
295 // Iterators //
295 // Iterators //
296 // ///////// //
296 // ///////// //
297
297
298 DataSeriesIterator begin() override
298 DataSeriesIterator begin() override
299 {
299 {
300 return DataSeriesIterator{DataSeriesIteratorValue{
300 return DataSeriesIterator{DataSeriesIteratorValue{
301 std::make_unique<dataseries_detail::IteratorValue<Dim, false> >(*this, true)}};
301 std::make_unique<dataseries_detail::IteratorValue<Dim, false> >(*this, true)}};
302 }
302 }
303
303
304 DataSeriesIterator end() override
304 DataSeriesIterator end() override
305 {
305 {
306 return DataSeriesIterator{DataSeriesIteratorValue{
306 return DataSeriesIterator{DataSeriesIteratorValue{
307 std::make_unique<dataseries_detail::IteratorValue<Dim, false> >(*this, false)}};
307 std::make_unique<dataseries_detail::IteratorValue<Dim, false> >(*this, false)}};
308 }
308 }
309
309
310 DataSeriesIterator cbegin() const override
310 DataSeriesIterator cbegin() const override
311 {
311 {
312 return DataSeriesIterator{DataSeriesIteratorValue{
312 return DataSeriesIterator{DataSeriesIteratorValue{
313 std::make_unique<dataseries_detail::IteratorValue<Dim, true> >(*this, true)}};
313 std::make_unique<dataseries_detail::IteratorValue<Dim, true> >(*this, true)}};
314 }
314 }
315
315
316 DataSeriesIterator cend() const override
316 DataSeriesIterator cend() const override
317 {
317 {
318 return DataSeriesIterator{DataSeriesIteratorValue{
318 return DataSeriesIterator{DataSeriesIteratorValue{
319 std::make_unique<dataseries_detail::IteratorValue<Dim, true> >(*this, false)}};
319 std::make_unique<dataseries_detail::IteratorValue<Dim, true> >(*this, false)}};
320 }
320 }
321
321
322 void erase(DataSeriesIterator first, DataSeriesIterator last)
322 void erase(DataSeriesIterator first, DataSeriesIterator last)
323 {
323 {
324 auto firstImpl
324 auto firstImpl
325 = dynamic_cast<dataseries_detail::IteratorValue<Dim, false> *>(first->impl());
325 = dynamic_cast<dataseries_detail::IteratorValue<Dim, false> *>(first->impl());
326 auto lastImpl = dynamic_cast<dataseries_detail::IteratorValue<Dim, false> *>(last->impl());
326 auto lastImpl = dynamic_cast<dataseries_detail::IteratorValue<Dim, false> *>(last->impl());
327
327
328 if (firstImpl && lastImpl) {
328 if (firstImpl && lastImpl) {
329 m_XAxisData->erase(firstImpl->m_XIt, lastImpl->m_XIt);
329 m_XAxisData->erase(firstImpl->m_XIt, lastImpl->m_XIt);
330 m_ValuesData->erase(firstImpl->m_ValuesIt, lastImpl->m_ValuesIt);
330 m_ValuesData->erase(firstImpl->m_ValuesIt, lastImpl->m_ValuesIt);
331 }
331 }
332 }
332 }
333
333
334 void insert(DataSeriesIterator first, DataSeriesIterator last, bool prepend = false)
334 void insert(DataSeriesIterator first, DataSeriesIterator last, bool prepend = false)
335 {
335 {
336 auto firstImpl = dynamic_cast<dataseries_detail::IteratorValue<Dim, true> *>(first->impl());
336 auto firstImpl = dynamic_cast<dataseries_detail::IteratorValue<Dim, true> *>(first->impl());
337 auto lastImpl = dynamic_cast<dataseries_detail::IteratorValue<Dim, true> *>(last->impl());
337 auto lastImpl = dynamic_cast<dataseries_detail::IteratorValue<Dim, true> *>(last->impl());
338
338
339 if (firstImpl && lastImpl) {
339 if (firstImpl && lastImpl) {
340 m_XAxisData->insert(firstImpl->m_XIt, lastImpl->m_XIt, prepend);
340 m_XAxisData->insert(firstImpl->m_XIt, lastImpl->m_XIt, prepend);
341 m_ValuesData->insert(firstImpl->m_ValuesIt, lastImpl->m_ValuesIt, prepend);
341 m_ValuesData->insert(firstImpl->m_ValuesIt, lastImpl->m_ValuesIt, prepend);
342 }
342 }
343 }
343 }
344
344
345 /// @sa IDataSeries::minXAxisData()
345 /// @sa IDataSeries::minXAxisData()
346 DataSeriesIterator minXAxisData(double minXAxisData) const override
346 DataSeriesIterator minXAxisData(double minXAxisData) const override
347 {
347 {
348 return std::lower_bound(
348 return std::lower_bound(
349 cbegin(), cend(), minXAxisData,
349 cbegin(), cend(), minXAxisData,
350 [](const auto &itValue, const auto &value) { return itValue.x() < value; });
350 [](const auto &itValue, const auto &value) { return itValue.x() < value; });
351 }
351 }
352
352
353 /// @sa IDataSeries::maxXAxisData()
353 /// @sa IDataSeries::maxXAxisData()
354 DataSeriesIterator maxXAxisData(double maxXAxisData) const override
354 DataSeriesIterator maxXAxisData(double maxXAxisData) const override
355 {
355 {
356 // Gets the first element that greater than max value
356 // Gets the first element that greater than max value
357 auto it = std::upper_bound(
357 auto it = std::upper_bound(
358 cbegin(), cend(), maxXAxisData,
358 cbegin(), cend(), maxXAxisData,
359 [](const auto &value, const auto &itValue) { return value < itValue.x(); });
359 [](const auto &value, const auto &itValue) { return value < itValue.x(); });
360
360
361 return it == cbegin() ? cend() : --it;
361 return it == cbegin() ? cend() : --it;
362 }
362 }
363
363
364 std::pair<DataSeriesIterator, DataSeriesIterator> xAxisRange(double minXAxisData,
364 std::pair<DataSeriesIterator, DataSeriesIterator> xAxisRange(double minXAxisData,
365 double maxXAxisData) const override
365 double maxXAxisData) const override
366 {
366 {
367 if (minXAxisData > maxXAxisData) {
367 if (minXAxisData > maxXAxisData) {
368 std::swap(minXAxisData, maxXAxisData);
368 std::swap(minXAxisData, maxXAxisData);
369 }
369 }
370
370
371 auto begin = cbegin();
371 auto begin = cbegin();
372 auto end = cend();
372 auto end = cend();
373
373
374 auto lowerIt = std::lower_bound(
374 auto lowerIt = std::lower_bound(
375 begin, end, minXAxisData,
375 begin, end, minXAxisData,
376 [](const auto &itValue, const auto &value) { return itValue.x() < value; });
376 [](const auto &itValue, const auto &value) { return itValue.x() < value; });
377 auto upperIt = std::upper_bound(
377 auto upperIt = std::upper_bound(
378 lowerIt, end, maxXAxisData,
378 lowerIt, end, maxXAxisData,
379 [](const auto &value, const auto &itValue) { return value < itValue.x(); });
379 [](const auto &value, const auto &itValue) { return value < itValue.x(); });
380
380
381 return std::make_pair(lowerIt, upperIt);
381 return std::make_pair(lowerIt, upperIt);
382 }
382 }
383
383
384 std::pair<DataSeriesIterator, DataSeriesIterator>
384 std::pair<DataSeriesIterator, DataSeriesIterator>
385 valuesBounds(double minXAxisData, double maxXAxisData) const override
385 valuesBounds(double minXAxisData, double maxXAxisData) const override
386 {
386 {
387 // Places iterators to the correct x-axis range
387 // Places iterators to the correct x-axis range
388 auto xAxisRangeIts = xAxisRange(minXAxisData, maxXAxisData);
388 auto xAxisRangeIts = xAxisRange(minXAxisData, maxXAxisData);
389
389
390 // Returns end iterators if the range is empty
390 // Returns end iterators if the range is empty
391 if (xAxisRangeIts.first == xAxisRangeIts.second) {
391 if (xAxisRangeIts.first == xAxisRangeIts.second) {
392 return std::make_pair(cend(), cend());
392 return std::make_pair(cend(), cend());
393 }
393 }
394
394
395 // Gets the iterator on the min of all values data
395 // Gets the iterator on the min of all values data
396 auto minIt = std::min_element(
396 auto minIt = std::min_element(
397 xAxisRangeIts.first, xAxisRangeIts.second, [](const auto &it1, const auto &it2) {
397 xAxisRangeIts.first, xAxisRangeIts.second, [](const auto &it1, const auto &it2) {
398 return SortUtils::minCompareWithNaN(it1.minValue(), it2.minValue());
398 return SortUtils::minCompareWithNaN(it1.minValue(), it2.minValue());
399 });
399 });
400
400
401 // Gets the iterator on the max of all values data
401 // Gets the iterator on the max of all values data
402 auto maxIt = std::max_element(
402 auto maxIt = std::max_element(
403 xAxisRangeIts.first, xAxisRangeIts.second, [](const auto &it1, const auto &it2) {
403 xAxisRangeIts.first, xAxisRangeIts.second, [](const auto &it1, const auto &it2) {
404 return SortUtils::maxCompareWithNaN(it1.maxValue(), it2.maxValue());
404 return SortUtils::maxCompareWithNaN(it1.maxValue(), it2.maxValue());
405 });
405 });
406
406
407 return std::make_pair(minIt, maxIt);
407 return std::make_pair(minIt, maxIt);
408 }
408 }
409
409
410 /// @return the y-axis associated to the data series
410 /// @return the y-axis associated to the data series
411 const OptionalAxis &yAxis() const { return m_YAxis; }
411 const OptionalAxis &yAxis() const { return m_YAxis; }
412 OptionalAxis &yAxis() { return m_YAxis; }
412 OptionalAxis &yAxis() { return m_YAxis; }
413
413
414 // /////// //
414 // /////// //
415 // Mutexes //
415 // Mutexes //
416 // /////// //
416 // /////// //
417
417
418 virtual void lockRead() { m_Lock.lockForRead(); }
418 virtual QReadLocker getReadLock() override { return QReadLocker{&m_Lock}; }
419 virtual void lockWrite() { m_Lock.lockForWrite(); }
419 virtual QWriteLocker getWriteLock() override { return QWriteLocker{&m_Lock}; }
420 virtual void unlock() { m_Lock.unlock(); }
420
421 virtual void lockRead() override { m_Lock.lockForRead(); }
422 virtual void lockWrite() override { m_Lock.lockForWrite(); }
423 virtual void unlock() override { m_Lock.unlock(); }
421
424
422 protected:
425 protected:
423 /// Protected ctor (DataSeries is abstract).
426 /// Protected ctor (DataSeries is abstract).
424 ///
427 ///
425 /// Data vectors must be consistent with each other, otherwise an exception will be thrown (@sa
428 /// Data vectors must be consistent with each other, otherwise an exception will be thrown (@sa
426 /// class description for consistent rules)
429 /// class description for consistent rules)
427 /// @remarks data series is automatically sorted on its x-axis data
430 /// @remarks data series is automatically sorted on its x-axis data
428 /// @throws std::invalid_argument if the data are inconsistent with each other
431 /// @throws std::invalid_argument if the data are inconsistent with each other
429 explicit DataSeries(std::shared_ptr<ArrayData<1> > xAxisData, const Unit &xAxisUnit,
432 explicit DataSeries(std::shared_ptr<ArrayData<1> > xAxisData, const Unit &xAxisUnit,
430 std::shared_ptr<ArrayData<Dim> > valuesData, const Unit &valuesUnit,
433 std::shared_ptr<ArrayData<Dim> > valuesData, const Unit &valuesUnit,
431 OptionalAxis yAxis = OptionalAxis{})
434 OptionalAxis yAxis = OptionalAxis{})
432 : m_XAxisData{xAxisData},
435 : m_XAxisData{xAxisData},
433 m_XAxisUnit{xAxisUnit},
436 m_XAxisUnit{xAxisUnit},
434 m_ValuesData{valuesData},
437 m_ValuesData{valuesData},
435 m_ValuesUnit{valuesUnit},
438 m_ValuesUnit{valuesUnit},
436 m_YAxis{std::move(yAxis)}
439 m_YAxis{std::move(yAxis)}
437 {
440 {
438 if (m_XAxisData->size() != m_ValuesData->size()) {
441 if (m_XAxisData->size() != m_ValuesData->size()) {
439 throw std::invalid_argument{
442 throw std::invalid_argument{
440 "The number of values by component must be equal to the number of x-axis data"};
443 "The number of values by component must be equal to the number of x-axis data"};
441 }
444 }
442
445
443 // Validates y-axis (if defined)
446 // Validates y-axis (if defined)
444 if (yAxis.isDefined() && (yAxis.size() != m_ValuesData->componentCount())) {
447 if (yAxis.isDefined() && (yAxis.size() != m_ValuesData->componentCount())) {
445 throw std::invalid_argument{
448 throw std::invalid_argument{
446 "As the y-axis is defined, the number of value components must be equal to the "
449 "As the y-axis is defined, the number of value components must be equal to the "
447 "number of y-axis data"};
450 "number of y-axis data"};
448 }
451 }
449
452
450 // Sorts data if it's not the case
453 // Sorts data if it's not the case
451 const auto &xAxisCData = m_XAxisData->cdata();
454 const auto &xAxisCData = m_XAxisData->cdata();
452 if (!std::is_sorted(xAxisCData.cbegin(), xAxisCData.cend())) {
455 if (!std::is_sorted(xAxisCData.cbegin(), xAxisCData.cend())) {
453 sort();
456 sort();
454 }
457 }
455 }
458 }
456
459
457 /// Copy ctor
460 /// Copy ctor
458 explicit DataSeries(const DataSeries<Dim> &other)
461 explicit DataSeries(const DataSeries<Dim> &other)
459 : m_XAxisData{std::make_shared<ArrayData<1> >(*other.m_XAxisData)},
462 : m_XAxisData{std::make_shared<ArrayData<1> >(*other.m_XAxisData)},
460 m_XAxisUnit{other.m_XAxisUnit},
463 m_XAxisUnit{other.m_XAxisUnit},
461 m_ValuesData{std::make_shared<ArrayData<Dim> >(*other.m_ValuesData)},
464 m_ValuesData{std::make_shared<ArrayData<Dim> >(*other.m_ValuesData)},
462 m_ValuesUnit{other.m_ValuesUnit},
465 m_ValuesUnit{other.m_ValuesUnit},
463 m_YAxis{other.m_YAxis}
466 m_YAxis{other.m_YAxis}
464 {
467 {
465 // Since a series is ordered from its construction and is always ordered, it is not
468 // Since a series is ordered from its construction and is always ordered, it is not
466 // necessary to call the sort method here ('other' is sorted)
469 // necessary to call the sort method here ('other' is sorted)
467 }
470 }
468
471
469 /// Assignment operator
472 /// Assignment operator
470 template <int D>
473 template <int D>
471 DataSeries &operator=(DataSeries<D> other)
474 DataSeries &operator=(DataSeries<D> other)
472 {
475 {
473 std::swap(m_XAxisData, other.m_XAxisData);
476 std::swap(m_XAxisData, other.m_XAxisData);
474 std::swap(m_XAxisUnit, other.m_XAxisUnit);
477 std::swap(m_XAxisUnit, other.m_XAxisUnit);
475 std::swap(m_ValuesData, other.m_ValuesData);
478 std::swap(m_ValuesData, other.m_ValuesData);
476 std::swap(m_ValuesUnit, other.m_ValuesUnit);
479 std::swap(m_ValuesUnit, other.m_ValuesUnit);
477 std::swap(m_YAxis, other.m_YAxis);
480 std::swap(m_YAxis, other.m_YAxis);
478
481
479 return *this;
482 return *this;
480 }
483 }
481
484
482 private:
485 private:
483 /**
486 /**
484 * Sorts data series on its x-axis data
487 * Sorts data series on its x-axis data
485 */
488 */
486 void sort() noexcept
489 void sort() noexcept
487 {
490 {
488 auto permutation = SortUtils::sortPermutation(*m_XAxisData, std::less<double>());
491 auto permutation = SortUtils::sortPermutation(*m_XAxisData, std::less<double>());
489 m_XAxisData = m_XAxisData->sort(permutation);
492 m_XAxisData = m_XAxisData->sort(permutation);
490 m_ValuesData = m_ValuesData->sort(permutation);
493 m_ValuesData = m_ValuesData->sort(permutation);
491 }
494 }
492
495
493 // x-axis
496 // x-axis
494 std::shared_ptr<ArrayData<1> > m_XAxisData;
497 std::shared_ptr<ArrayData<1> > m_XAxisData;
495 Unit m_XAxisUnit;
498 Unit m_XAxisUnit;
496
499
497 // values
500 // values
498 std::shared_ptr<ArrayData<Dim> > m_ValuesData;
501 std::shared_ptr<ArrayData<Dim> > m_ValuesData;
499 Unit m_ValuesUnit;
502 Unit m_ValuesUnit;
500
503
501 // y-axis (optional)
504 // y-axis (optional)
502 OptionalAxis m_YAxis;
505 OptionalAxis m_YAxis;
503
506
504 QReadWriteLock m_Lock;
507 QReadWriteLock m_Lock;
505 };
508 };
506
509
507 #endif // SCIQLOP_DATASERIES_H
510 #endif // SCIQLOP_DATASERIES_H
@@ -1,102 +1,108
1 #ifndef SCIQLOP_IDATASERIES_H
1 #ifndef SCIQLOP_IDATASERIES_H
2 #define SCIQLOP_IDATASERIES_H
2 #define SCIQLOP_IDATASERIES_H
3
3
4 #include <memory>
5
6 #include <QReadWriteLock>
7 #include <QString>
8
4 #include <Common/MetaTypes.h>
9 #include <Common/MetaTypes.h>
5 #include <Data/DataSeriesIterator.h>
10 #include <Data/DataSeriesIterator.h>
6 #include <Data/DateTimeRange.h>
11 #include <Data/DateTimeRange.h>
7 #include <Data/Unit.h>
12 #include <Data/Unit.h>
13 #include <Common/deprecate.h>
8
14
9 #include <memory>
10
11 #include <QString>
12
15
13 template <int Dim>
16 template <int Dim>
14 class ArrayData;
17 class ArrayData;
15
18
16 /**
19 /**
17 * @brief The IDataSeries aims to declare a data series.
20 * @brief The IDataSeries aims to declare a data series.
18 *
21 *
19 * A data series is an entity that contains at least :
22 * A data series is an entity that contains at least :
20 * - one dataset representing the x-axis
23 * - one dataset representing the x-axis
21 * - one dataset representing the values
24 * - one dataset representing the values
22 *
25 *
23 * Each dataset is represented by an ArrayData, and is associated with a unit.
26 * Each dataset is represented by an ArrayData, and is associated with a unit.
24 *
27 *
25 * An ArrayData can be unidimensional or two-dimensional, depending on the implementation of the
28 * An ArrayData can be unidimensional or two-dimensional, depending on the implementation of the
26 * IDataSeries. The x-axis dataset is always unidimensional.
29 * IDataSeries. The x-axis dataset is always unidimensional.
27 *
30 *
28 * @sa ArrayData
31 * @sa ArrayData
29 */
32 */
30 class IDataSeries {
33 class IDataSeries {
31 public:
34 public:
32 virtual ~IDataSeries() noexcept = default;
35 virtual ~IDataSeries() noexcept = default;
33
36
34 /// Returns the x-axis dataset
37 /// Returns the x-axis dataset
35 virtual std::shared_ptr<ArrayData<1> > xAxisData() = 0;
38 virtual std::shared_ptr<ArrayData<1> > xAxisData() = 0;
36
39
37 /// Returns the x-axis dataset (as const)
40 /// Returns the x-axis dataset (as const)
38 virtual const std::shared_ptr<ArrayData<1> > xAxisData() const = 0;
41 virtual const std::shared_ptr<ArrayData<1> > xAxisData() const = 0;
39
42
40 virtual Unit xAxisUnit() const = 0;
43 virtual Unit xAxisUnit() const = 0;
41
44
42 /// @return the y-axis unit, if axis is defined, default unit otherwise
45 /// @return the y-axis unit, if axis is defined, default unit otherwise
43 virtual Unit yAxisUnit() const = 0;
46 virtual Unit yAxisUnit() const = 0;
44
47
45 virtual Unit valuesUnit() const = 0;
48 virtual Unit valuesUnit() const = 0;
46
49
47 virtual void merge(IDataSeries *dataSeries) = 0;
50 virtual void merge(IDataSeries *dataSeries) = 0;
48 /// Removes from data series all entries whose value on the x-axis is not between min and max
51 /// Removes from data series all entries whose value on the x-axis is not between min and max
49 virtual void purge(double min, double max) = 0;
52 virtual void purge(double min, double max) = 0;
50
53
51 /// @todo Review the name and signature of this method
54 /// @todo Review the name and signature of this method
52 virtual std::shared_ptr<IDataSeries> subDataSeries(const DateTimeRange &range) = 0;
55 virtual std::shared_ptr<IDataSeries> subDataSeries(const DateTimeRange &range) = 0;
53
56
54 virtual std::unique_ptr<IDataSeries> clone() const = 0;
57 virtual std::unique_ptr<IDataSeries> clone() const = 0;
55
58
56 /// @return the total number of points contained in the data series
59 /// @return the total number of points contained in the data series
57 virtual int nbPoints() const = 0;
60 virtual int nbPoints() const = 0;
58
61
59 /// @return the bounds of the y-axis axis (if defined)
62 /// @return the bounds of the y-axis axis (if defined)
60 virtual std::pair<double, double> yBounds() const = 0;
63 virtual std::pair<double, double> yBounds() const = 0;
61
64
62 // ///////// //
65 // ///////// //
63 // Iterators //
66 // Iterators //
64 // ///////// //
67 // ///////// //
65
68
66 virtual DataSeriesIterator cbegin() const = 0;
69 virtual DataSeriesIterator cbegin() const = 0;
67 virtual DataSeriesIterator cend() const = 0;
70 virtual DataSeriesIterator cend() const = 0;
68 virtual DataSeriesIterator begin() = 0;
71 virtual DataSeriesIterator begin() = 0;
69 virtual DataSeriesIterator end() = 0;
72 virtual DataSeriesIterator end() = 0;
70
73
71 /// @return the iterator to the first entry of the data series whose x-axis data is greater than
74 /// @return the iterator to the first entry of the data series whose x-axis data is greater than
72 /// or equal to the value passed in parameter, or the end iterator if there is no matching value
75 /// or equal to the value passed in parameter, or the end iterator if there is no matching value
73 virtual DataSeriesIterator minXAxisData(double minXAxisData) const = 0;
76 virtual DataSeriesIterator minXAxisData(double minXAxisData) const = 0;
74
77
75 /// @return the iterator to the last entry of the data series whose x-axis data is less than or
78 /// @return the iterator to the last entry of the data series whose x-axis data is less than or
76 /// equal to the value passed in parameter, or the end iterator if there is no matching value
79 /// equal to the value passed in parameter, or the end iterator if there is no matching value
77 virtual DataSeriesIterator maxXAxisData(double maxXAxisData) const = 0;
80 virtual DataSeriesIterator maxXAxisData(double maxXAxisData) const = 0;
78
81
79 /// @return the iterators pointing to the range of data whose x-axis values are between min and
82 /// @return the iterators pointing to the range of data whose x-axis values are between min and
80 /// max passed in parameters
83 /// max passed in parameters
81 virtual std::pair<DataSeriesIterator, DataSeriesIterator>
84 virtual std::pair<DataSeriesIterator, DataSeriesIterator>
82 xAxisRange(double minXAxisData, double maxXAxisData) const = 0;
85 xAxisRange(double minXAxisData, double maxXAxisData) const = 0;
83
86
84 /// @return two iterators pointing to the data that have respectively the min and the max value
87 /// @return two iterators pointing to the data that have respectively the min and the max value
85 /// data of a data series' range. The search is performed for a given x-axis range.
88 /// data of a data series' range. The search is performed for a given x-axis range.
86 /// @sa xAxisRange()
89 /// @sa xAxisRange()
87 virtual std::pair<DataSeriesIterator, DataSeriesIterator>
90 virtual std::pair<DataSeriesIterator, DataSeriesIterator>
88 valuesBounds(double minXAxisData, double maxXAxisData) const = 0;
91 valuesBounds(double minXAxisData, double maxXAxisData) const = 0;
89
92
90 // /////// //
93 // /////// //
91 // Mutexes //
94 // Mutexes //
92 // /////// //
95 // /////// //
93
96 virtual QReadLocker getReadLock() = 0;
97 virtual QWriteLocker getWriteLock() = 0;
98 DEPRECATE(
94 virtual void lockRead() = 0;
99 virtual void lockRead() = 0;
95 virtual void lockWrite() = 0;
100 virtual void lockWrite() = 0;
96 virtual void unlock() = 0;
101 virtual void unlock() = 0;
102 )
97 };
103 };
98
104
99 // Required for using shared_ptr in signals/slots
105 // Required for using shared_ptr in signals/slots
100 SCIQLOP_REGISTER_META_TYPE(IDATASERIES_PTR_REGISTRY, std::shared_ptr<IDataSeries>)
106 SCIQLOP_REGISTER_META_TYPE(IDATASERIES_PTR_REGISTRY, std::shared_ptr<IDataSeries>)
101
107
102 #endif // SCIQLOP_IDATASERIES_H
108 #endif // SCIQLOP_IDATASERIES_H
@@ -1,34 +1,34
1 #ifndef SCIQLOP_VECTORSERIES_H
1 #ifndef SCIQLOP_VECTORSERIES_H
2 #define SCIQLOP_VECTORSERIES_H
2 #define SCIQLOP_VECTORSERIES_H
3
3
4 #include "CoreGlobal.h"
4 #include "CoreGlobal.h"
5
5
6 #include <Data/DataSeries.h>
6 #include <Data/DataSeries.h>
7
7
8 /**
8 /**
9 * @brief The VectorSeries class is the implementation for a data series representing a vector.
9 * @brief The VectorSeries class is the implementation for a data series representing a vector.
10 */
10 */
11 class SCIQLOP_CORE_EXPORT VectorSeries : public DataSeries<2> {
11 class SCIQLOP_CORE_EXPORT VectorSeries : public DataSeries<2> {
12 public:
12 public:
13 /**
13 /**
14 * Ctor with three vectors (one per component). The vectors must have the same size, otherwise a
14 * Ctor with three vectors (one per component). The vectors must have the same size, otherwise a
15 * ScalarSeries with no values will be created.
15 * ScalarSeries with no values will be created.
16 * @param xAxisData x-axis data
16 * @param xAxisData x-axis data
17 * @param xvaluesData x-values data
17 * @param xvaluesData x-values data
18 * @param yvaluesData y-values data
18 * @param yvaluesData y-values data
19 * @param zvaluesData z-values data
19 * @param zvaluesData z-values data
20 */
20 */
21 explicit VectorSeries(std::vector<double> xAxisData, std::vector<double> xValuesData,
21 explicit VectorSeries(std::vector<double> xAxisData, std::vector<double> xValuesData,
22 std::vector<double> yValuesData, std::vector<double> zValuesData,
22 std::vector<double> yValuesData, std::vector<double> zValuesData,
23 const Unit &xAxisUnit, const Unit &valuesUnit);
23 const Unit &xAxisUnit, const Unit &valuesUnit);
24
24
25 /// Default Ctor
25 /// Default Ctor
26 explicit VectorSeries(std::vector<double> xAxisData, std::vector<double> valuesData,
26 explicit VectorSeries(std::vector<double> xAxisData, std::vector<double> valuesData,
27 const Unit &xAxisUnit, const Unit &valuesUnit);
27 const Unit &xAxisUnit, const Unit &valuesUnit);
28
28
29 std::unique_ptr<IDataSeries> clone() const;
29 std::unique_ptr<IDataSeries> clone() const override;
30
30
31 std::shared_ptr<IDataSeries> subDataSeries(const DateTimeRange &range) override;
31 std::shared_ptr<IDataSeries> subDataSeries(const DateTimeRange &range) override;
32 };
32 };
33
33
34 #endif // SCIQLOP_VECTORSERIES_H
34 #endif // SCIQLOP_VECTORSERIES_H
@@ -1,107 +1,112
1 #ifndef SCIQLOP_VARIABLE_H
1 #ifndef SCIQLOP_VARIABLE_H
2 #define SCIQLOP_VARIABLE_H
2 #define SCIQLOP_VARIABLE_H
3
3
4 #include "CoreGlobal.h"
4 #include <QLoggingCategory>
5 #include <QObject>
6 #include <QUuid>
7 #include <QReadWriteLock>
5
8
9 #include "CoreGlobal.h"
6 #include <Data/DataSeriesIterator.h>
10 #include <Data/DataSeriesIterator.h>
7 #include <Data/DataSeriesType.h>
11 #include <Data/DataSeriesType.h>
8 #include <Data/DateTimeRange.h>
12 #include <Data/DateTimeRange.h>
9
13
10 #include <QLoggingCategory>
11 #include <QObject>
12 #include <QUuid>
13
14
14 #include <Common/deprecate.h>
15 #include <Common/deprecate.h>
15 #include <Common/MetaTypes.h>
16 #include <Common/MetaTypes.h>
16 #include <Common/spimpl.h>
17 #include <Common/spimpl.h>
17
18
18 Q_DECLARE_LOGGING_CATEGORY(LOG_Variable)
19 Q_DECLARE_LOGGING_CATEGORY(LOG_Variable)
19
20
20 class IDataSeries;
21 class IDataSeries;
21 class QString;
22 class QString;
22
23
23 /**
24 /**
24 * @brief The Variable class represents a variable in SciQlop.
25 * @brief The Variable class represents a variable in SciQlop.
25 */
26 */
26 class SCIQLOP_CORE_EXPORT Variable : public QObject {
27 class SCIQLOP_CORE_EXPORT Variable : public QObject {
27
28
28 Q_OBJECT
29 Q_OBJECT
29
30
30 public:
31 public:
31 explicit Variable(const QString &name, const QVariantHash &metadata = {});
32 explicit Variable(const QString &name, const QVariantHash &metadata = {});
32
33
33 /// Copy ctor
34 /// Copy ctor
34 explicit Variable(const Variable &other);
35 explicit Variable(const Variable &other);
35
36
36 std::shared_ptr<Variable> clone() const;
37 std::shared_ptr<Variable> clone() const;
37
38
38 QString name() const noexcept;
39 QString name() const noexcept;
39 void setName(const QString &name) noexcept;
40 void setName(const QString &name) noexcept;
40 DateTimeRange range() const noexcept;
41 DateTimeRange range() const noexcept;
41 void setRange(const DateTimeRange &range, bool notify=false) noexcept;
42 void setRange(const DateTimeRange &range, bool notify=false) noexcept;
42 DateTimeRange cacheRange() const noexcept;
43 DateTimeRange cacheRange() const noexcept;
43 void setCacheRange(const DateTimeRange &cacheRange) noexcept;
44 void setCacheRange(const DateTimeRange &cacheRange) noexcept;
44
45
45 /// @return the number of points hold by the variable. The number of points is updated each time
46 /// @return the number of points hold by the variable. The number of points is updated each time
46 /// the data series changes
47 /// the data series changes
47 unsigned int nbPoints() const noexcept;
48 unsigned int nbPoints() const noexcept;
48
49
49 /// Returns the real range of the variable, i.e. the min and max x-axis values of the data
50 /// Returns the real range of the variable, i.e. the min and max x-axis values of the data
50 /// series between the range of the variable. The real range is updated each time the variable
51 /// series between the range of the variable. The real range is updated each time the variable
51 /// range or the data series changed
52 /// range or the data series changed
52 /// @return the real range, invalid range if the data series is null or empty
53 /// @return the real range, invalid range if the data series is null or empty
53 /// @sa setDataSeries()
54 /// @sa setDataSeries()
54 /// @sa setRange()
55 /// @sa setRange()
55 std::optional<DateTimeRange> realRange() const noexcept;
56 std::optional<DateTimeRange> realRange() const noexcept;
56
57
57 /// @return the data of the variable, nullptr if there is no data
58 /// @return the data of the variable, nullptr if there is no data
58 std::shared_ptr<IDataSeries> dataSeries() const noexcept;
59 std::shared_ptr<IDataSeries> dataSeries() const noexcept;
59
60
60 /// @return the type of data that the variable holds
61 /// @return the type of data that the variable holds
61 DataSeriesType type() const noexcept;
62 DataSeriesType type() const noexcept;
62
63
63 QVariantHash metadata() const noexcept;
64 QVariantHash metadata() const noexcept;
64 DEPRECATE(
65 DEPRECATE(
65 bool contains(const DateTimeRange &range) const noexcept;
66 bool contains(const DateTimeRange &range) const noexcept;
66 bool intersect(const DateTimeRange &range) const noexcept;
67 bool intersect(const DateTimeRange &range) const noexcept;
67 bool isInside(const DateTimeRange &range) const noexcept;
68 bool isInside(const DateTimeRange &range) const noexcept;
68
69
69 bool cacheContains(const DateTimeRange &range) const noexcept;
70 bool cacheContains(const DateTimeRange &range) const noexcept;
70 bool cacheIntersect(const DateTimeRange &range) const noexcept;
71 bool cacheIntersect(const DateTimeRange &range) const noexcept;
71 bool cacheIsInside(const DateTimeRange &range) const noexcept;
72 bool cacheIsInside(const DateTimeRange &range) const noexcept;
72 )
73 )
73 DEPRECATE(
74 DEPRECATE(
74 QVector<DateTimeRange> provideNotInCacheRangeList(const DateTimeRange &range) const noexcept;
75 QVector<DateTimeRange> provideNotInCacheRangeList(const DateTimeRange &range) const noexcept;
75 QVector<DateTimeRange> provideInCacheRangeList(const DateTimeRange &range) const noexcept;
76 QVector<DateTimeRange> provideInCacheRangeList(const DateTimeRange &range) const noexcept;
76 )
77 )
77 DEPRECATE(
78 DEPRECATE(
78 void mergeDataSeries(std::shared_ptr<IDataSeries> dataSeries) noexcept;
79 void mergeDataSeries(std::shared_ptr<IDataSeries> dataSeries) noexcept;
79 )
80 )
80 void mergeDataSeries(IDataSeries* dataSeries, bool notify=false) noexcept;
81
82 void updateData(const std::vector<IDataSeries*>& dataSeries,
83 const DateTimeRange& newRange, const DateTimeRange& newCacheRange,
84 bool notify=true);
81
85
82 DEPRECATE(
86 DEPRECATE(
83 static QVector<DateTimeRange> provideNotInCacheRangeList(const DateTimeRange &oldRange,
87 static QVector<DateTimeRange> provideNotInCacheRangeList(const DateTimeRange &oldRange,
84 const DateTimeRange &nextRange);
88 const DateTimeRange &nextRange);
85
89
86 static QVector<DateTimeRange> provideInCacheRangeList(const DateTimeRange &oldRange,
90 static QVector<DateTimeRange> provideInCacheRangeList(const DateTimeRange &oldRange,
87 const DateTimeRange &nextRange);
91 const DateTimeRange &nextRange);
88 )
92 )
89
93
90 QUuid ID(){return _uuid;}
94 QUuid ID(){return _uuid;}
91 signals:
95 signals:
92 void updated();
96 void updated();
93 DEPRECATE(
97 DEPRECATE(
94 /// Signal emitted when when the data series of the variable is loaded for the first time
98 /// Signal emitted when when the data series of the variable is loaded for the first time
95 void dataInitialized();
99 void dataInitialized();
96 )
100 )
97 private:
101 private:
98 class VariablePrivate;
102 class VariablePrivate;
99 spimpl::unique_impl_ptr<VariablePrivate> impl;
103 spimpl::unique_impl_ptr<VariablePrivate> impl;
100 QUuid _uuid;
104 QUuid _uuid;
105 QReadWriteLock m_lock;
101 };
106 };
102
107
103 // Required for using shared_ptr in signals/slots
108 // Required for using shared_ptr in signals/slots
104 SCIQLOP_REGISTER_META_TYPE(VARIABLE_PTR_REGISTRY, std::shared_ptr<Variable>)
109 SCIQLOP_REGISTER_META_TYPE(VARIABLE_PTR_REGISTRY, std::shared_ptr<Variable>)
105 SCIQLOP_REGISTER_META_TYPE(VARIABLE_PTR_VECTOR_REGISTRY, QVector<std::shared_ptr<Variable> >)
110 SCIQLOP_REGISTER_META_TYPE(VARIABLE_PTR_VECTOR_REGISTRY, QVector<std::shared_ptr<Variable> >)
106
111
107 #endif // SCIQLOP_VARIABLE_H
112 #endif // SCIQLOP_VARIABLE_H
@@ -1,447 +1,436
1 #include <optional>
1 #include <optional>
2 #include <QMutex>
2 #include <QMutex>
3 #include <QReadWriteLock>
3 #include <QReadWriteLock>
4 #include <QThread>
4 #include <QThread>
5
5
6 #include "Variable/Variable.h"
6 #include "Variable/Variable.h"
7
7
8 #include <Data/IDataSeries.h>
8 #include <Data/IDataSeries.h>
9 #include <Data/DateTimeRange.h>
9 #include <Data/DateTimeRange.h>
10
10
11 #include <Common/debug.h>
11 #include <Common/debug.h>
12
12
13 Q_LOGGING_CATEGORY(LOG_Variable, "Variable")
13 Q_LOGGING_CATEGORY(LOG_Variable, "Variable")
14
14
15 namespace {
16
15
17 /**
16 /**
18 * Searches in metadata for a value that can be converted to DataSeriesType
17 * Searches in metadata for a value that can be converted to DataSeriesType
19 * @param metadata the metadata where to search
18 * @param metadata the metadata where to search
20 * @return the value converted to a DataSeriesType if it was found, UNKNOWN type otherwise
19 * @return the value converted to a DataSeriesType if it was found, UNKNOWN type otherwise
21 * @sa DataSeriesType
20 * @sa DataSeriesType
22 */
21 */
23 DataSeriesType findDataSeriesType(const QVariantHash &metadata)
22 static DataSeriesType findDataSeriesType(const QVariantHash &metadata)
24 {
23 {
25 auto dataSeriesType = DataSeriesType::UNKNOWN;
24 auto dataSeriesType = DataSeriesType::UNKNOWN;
26
25
27 // Go through the metadata and stop at the first value that could be converted to DataSeriesType
26 // Go through the metadata and stop at the first value that could be converted to DataSeriesType
28 for (auto it = metadata.cbegin(), end = metadata.cend();
27 for (auto it = metadata.cbegin(), end = metadata.cend();
29 it != end && dataSeriesType == DataSeriesType::UNKNOWN; ++it) {
28 it != end && dataSeriesType == DataSeriesType::UNKNOWN; ++it) {
30 dataSeriesType = DataSeriesTypeUtils::fromString(it.value().toString());
29 dataSeriesType = DataSeriesTypeUtils::fromString(it.value().toString());
31 }
30 }
32
31
33 return dataSeriesType;
32 return dataSeriesType;
34 }
33 }
35
34
36 } // namespace
37
35
38 #define VP_PROPERTY(property,getter,setter,type) \
36 #define VP_PROPERTY(property,getter,setter,type) \
39 type getter() noexcept\
37 type getter() noexcept\
40 {\
38 {\
41 QReadLocker lock{&m_Lock};\
39 QReadLocker lock{&m_Lock};\
42 return property;\
40 return property;\
43 }\
41 }\
44 void setter(const type& getter) noexcept\
42 void setter(const type& getter) noexcept\
45 {\
43 {\
46 QWriteLocker lock{&m_Lock};\
44 QWriteLocker lock{&m_Lock};\
47 property = getter;\
45 property = getter;\
48 }\
46 }\
49 type property;\
47 type property;\
50
48
51 #define V_FW_GETTER_SETTER(getter,setter, type)\
49 #define V_FW_GETTER_SETTER(getter,setter, type)\
52 type Variable::getter() const noexcept \
50 type Variable::getter() const noexcept \
53 {\
51 {\
54 return impl->getter();\
52 return impl->getter();\
55 }\
53 }\
56 void Variable::setter(const type& getter) noexcept \
54 void Variable::setter(const type& getter) noexcept \
57 {\
55 {\
58 impl->setter(getter);\
56 impl->setter(getter);\
59 }\
57 }\
60
58
61 struct Variable::VariablePrivate {
59 struct Variable::VariablePrivate {
62 explicit VariablePrivate(const QString &name, const QVariantHash &metadata)
60 explicit VariablePrivate(const QString &name, const QVariantHash &metadata)
63 : m_Name{name},
61 : m_Name{name},
64 m_Range{INVALID_RANGE},
62 m_Range{INVALID_RANGE},
65 m_CacheRange{INVALID_RANGE},
63 m_CacheRange{INVALID_RANGE},
66 m_Metadata{metadata},
64 m_Metadata{metadata},
67 m_DataSeries{nullptr},
65 m_DataSeries{nullptr},
68 m_RealRange{INVALID_RANGE},
66 m_RealRange{INVALID_RANGE},
69 m_NbPoints{0},
67 m_NbPoints{0},
70 m_Type{findDataSeriesType(m_Metadata)}
68 m_Type{findDataSeriesType(m_Metadata)}
71 {
69 {
72 }
70 }
73
71
74 VariablePrivate(const VariablePrivate &other)
72 VariablePrivate(const VariablePrivate &other)
75 : m_Name{other.m_Name},
73 : m_Name{other.m_Name},
76 m_Range{other.m_Range},
74 m_Range{other.m_Range},
77 m_CacheRange{other.m_CacheRange},
75 m_CacheRange{other.m_CacheRange},
78 m_Metadata{other.m_Metadata},
76 m_Metadata{other.m_Metadata},
79 m_DataSeries{other.m_DataSeries != nullptr ? other.m_DataSeries->clone() : nullptr},
77 m_DataSeries{other.m_DataSeries != nullptr ? other.m_DataSeries->clone() : nullptr},
80 m_RealRange{other.m_RealRange},
78 m_RealRange{other.m_RealRange},
81 m_NbPoints{other.m_NbPoints},
79 m_NbPoints{other.m_NbPoints},
82 m_Type{findDataSeriesType(m_Metadata)}
80 m_Type{findDataSeriesType(m_Metadata)}
83 {
81 {
84 }
82 }
85
83
86 void lockRead() { m_Lock.lockForRead(); }
84 void lockRead() { m_Lock.lockForRead(); }
87 void lockWrite() { m_Lock.lockForWrite(); }
85 void lockWrite() { m_Lock.lockForWrite(); }
88 void unlock() { m_Lock.unlock(); }
86 void unlock() { m_Lock.unlock(); }
89
87
90 void purgeDataSeries()
88 void purgeDataSeries()
91 {
89 {
92 if (m_DataSeries) {
90 if (m_DataSeries) {
93 m_DataSeries->purge(m_CacheRange.m_TStart, m_CacheRange.m_TEnd);
91 m_DataSeries->purge(m_CacheRange.m_TStart, m_CacheRange.m_TEnd);
94 }
92 }
95 updateRealRange();
93 updateRealRange();
96 updateNbPoints();
94 updateNbPoints();
97 }
95 }
98
96 void mergeDataSeries(const std::vector<IDataSeries*>& dataseries)
97 {
98 QWriteLocker lock{&m_Lock};
99 for(auto ds:dataseries)
100 {
101 if(m_DataSeries)
102 m_DataSeries->merge(ds);
103 else
104 m_DataSeries = ds->clone();
105 }
106 }
99 void updateNbPoints() { m_NbPoints = m_DataSeries ? m_DataSeries->nbPoints() : 0; }
107 void updateNbPoints() { m_NbPoints = m_DataSeries ? m_DataSeries->nbPoints() : 0; }
100
108
101 /// Updates real range according to current variable range and data series
109 /// Updates real range according to current variable range and data series
102 void updateRealRange()
110 void updateRealRange()
103 {
111 {
104 if (m_DataSeries) {
112 if (m_DataSeries) {
105 m_DataSeries->lockRead();
113 auto lock = m_DataSeries->getReadLock();
106 auto end = m_DataSeries->cend();
114 auto end = m_DataSeries->cend();
107 auto minXAxisIt = m_DataSeries->minXAxisData(m_Range.m_TStart);
115 auto minXAxisIt = m_DataSeries->minXAxisData(m_Range.m_TStart);
108 auto maxXAxisIt = m_DataSeries->maxXAxisData(m_Range.m_TEnd);
116 auto maxXAxisIt = m_DataSeries->maxXAxisData(m_Range.m_TEnd);
109
117 if(minXAxisIt != end && maxXAxisIt != end && minXAxisIt->x() <= maxXAxisIt->x())
110 m_RealRange
118 m_RealRange = DateTimeRange{minXAxisIt->x(), maxXAxisIt->x()};
111 = (minXAxisIt != end && maxXAxisIt != end && minXAxisIt->x() <= maxXAxisIt->x())
119 else
112 ? DateTimeRange{minXAxisIt->x(), maxXAxisIt->x()}
120 m_RealRange = std::nullopt;
113 : INVALID_RANGE;
114 m_DataSeries->unlock();
115 }
121 }
116 else {
122 else {
117 m_RealRange = std::nullopt;
123 m_RealRange = std::nullopt;
118 }
124 }
119 }
125 }
120
126
121 VP_PROPERTY(m_Name, name, setName, QString)
127 VP_PROPERTY(m_Name, name, setName, QString)
122 VP_PROPERTY(m_Range, range, setRange, DateTimeRange)
128 VP_PROPERTY(m_Range, range, setRange, DateTimeRange)
123 VP_PROPERTY(m_CacheRange, cacheRange, setCacheRange, DateTimeRange)
129 VP_PROPERTY(m_CacheRange, cacheRange, setCacheRange, DateTimeRange)
124 VP_PROPERTY(m_Metadata, metadata, setMetadata, QVariantHash)
130 VP_PROPERTY(m_Metadata, metadata, setMetadata, QVariantHash)
125 VP_PROPERTY(m_DataSeries, dataSeries, setDataSeries, std::shared_ptr<IDataSeries>)
131 VP_PROPERTY(m_DataSeries, dataSeries, setDataSeries, std::shared_ptr<IDataSeries>)
126 VP_PROPERTY(m_RealRange, realRange, setRealRange, std::optional<DateTimeRange>)
132 VP_PROPERTY(m_RealRange, realRange, setRealRange, std::optional<DateTimeRange>)
127 unsigned int m_NbPoints;
133 unsigned int m_NbPoints;
128 VP_PROPERTY(m_Type, type, setType, DataSeriesType)
134 VP_PROPERTY(m_Type, type, setType, DataSeriesType)
129 QReadWriteLock m_Lock;
135 QReadWriteLock m_Lock;
130 };
136 };
131
137
132 Variable::Variable(const QString &name, const QVariantHash &metadata)
138 Variable::Variable(const QString &name, const QVariantHash &metadata)
133 : impl{spimpl::make_unique_impl<VariablePrivate>(name, metadata)},
139 : impl{spimpl::make_unique_impl<VariablePrivate>(name, metadata)},
134 _uuid{QUuid::createUuid()}
140 _uuid{QUuid::createUuid()}
135 {
141 {
136 }
142 }
137
143
138 Variable::Variable(const Variable &other)
144 Variable::Variable(const Variable &other)
139 : impl{spimpl::make_unique_impl<VariablePrivate>(*other.impl)},
145 : impl{spimpl::make_unique_impl<VariablePrivate>(*other.impl)},
140 _uuid{QUuid::createUuid()} //is a clone but must have a != uuid
146 _uuid{QUuid::createUuid()} //is a clone but must have a != uuid
141 {
147 {
142 }
148 }
143
149
144 std::shared_ptr<Variable> Variable::clone() const
150 std::shared_ptr<Variable> Variable::clone() const
145 {
151 {
146 return std::make_shared<Variable>(*this);
152 return std::make_shared<Variable>(*this);
147 }
153 }
148
154
149 V_FW_GETTER_SETTER(name,setName,QString)
155 V_FW_GETTER_SETTER(name,setName,QString)
150
156
151 DateTimeRange Variable::range() const noexcept
157 DateTimeRange Variable::range() const noexcept
152 {
158 {
153 return impl->range();
159 return impl->range();
154 }
160 }
155
161
156 void Variable::setRange(const DateTimeRange &range, bool notify) noexcept
162 void Variable::setRange(const DateTimeRange &range, bool notify) noexcept
157 {
163 {
158 impl->setRange(range);
164 impl->setRange(range);
159 impl->updateRealRange();
165 impl->updateRealRange();
160 if(notify)
166 if(notify)
161 emit this->updated();
167 emit this->updated();
162 }
168 }
163
169
164 V_FW_GETTER_SETTER(cacheRange, setCacheRange, DateTimeRange)
170 V_FW_GETTER_SETTER(cacheRange, setCacheRange, DateTimeRange)
165
171
166 unsigned int Variable::nbPoints() const noexcept
172 unsigned int Variable::nbPoints() const noexcept
167 {
173 {
168 return impl->m_NbPoints;
174 return impl->m_NbPoints;
169 }
175 }
170
176
171 std::optional<DateTimeRange> Variable::realRange() const noexcept
177 std::optional<DateTimeRange> Variable::realRange() const noexcept
172 {
178 {
173 return impl->realRange();
179 return impl->realRange();
174 }
180 }
175
181
176 void Variable::mergeDataSeries(std::shared_ptr<IDataSeries> dataSeries) noexcept
182 void Variable::mergeDataSeries(std::shared_ptr<IDataSeries> dataSeries) noexcept
177 {
183 {
178 qCDebug(LOG_Variable()) << "TORM Variable::mergeDataSeries"
184 qCDebug(LOG_Variable()) << "TORM Variable::mergeDataSeries"
179 << QThread::currentThread()->objectName();
185 << QThread::currentThread()->objectName();
180 if (!dataSeries) {
186 if (!dataSeries) {
181 /// @todo ALX : log
187 /// @todo ALX : log
182 return;
188 return;
183 }
189 }
184
190
185 auto dataInit = false;
191 auto dataInit = false;
186
192
187 // Add or merge the data
193 // Add or merge the data
188 impl->lockWrite();
194 impl->lockWrite();
189 if (!impl->m_DataSeries) {
195 if (!impl->m_DataSeries) {
190 impl->m_DataSeries = dataSeries->clone();
196 impl->m_DataSeries = dataSeries->clone();
191 dataInit = true;
197 dataInit = true;
192 }
198 }
193 else {
199 else {
194 impl->m_DataSeries->merge(dataSeries.get());
200 impl->m_DataSeries->merge(dataSeries.get());
195 }
201 }
196 impl->purgeDataSeries();
202 impl->purgeDataSeries();
197 impl->unlock();
203 impl->unlock();
198
204
199 if (dataInit) {
205 if (dataInit) {
200 emit dataInitialized();
206 emit dataInitialized();
201 }
207 }
202 }
208 }
203
209
204 void Variable::mergeDataSeries(IDataSeries *dataSeries, bool notify) noexcept
210 void Variable::updateData(const std::vector<IDataSeries *> &dataSeries, const DateTimeRange &newRange, const DateTimeRange &newCacheRange, bool notify)
205 {
211 {
206 if (dataSeries==nullptr) {
212 {
207 SCIQLOP_ERROR(Variable,"Given IDataSeries is nullptr");
213 QWriteLocker lock{&m_lock};
208 return;
214 impl->mergeDataSeries(dataSeries);
209 }
215 impl->setRange(newRange);
210
216 impl->setCacheRange(newCacheRange);
211 auto dataInit = false;
212 // @TODO move impl to Pimpl this is what it stands for...
213 // Add or merge the data
214 impl->lockWrite();
215 if (!impl->m_DataSeries) {
216 //@TODO find a better way
217 impl->m_DataSeries = dataSeries->clone();
218 dataInit = true;
219 delete dataSeries;
220 }
221 else {
222 impl->m_DataSeries->merge(dataSeries);
223 }
224 impl->purgeDataSeries();
217 impl->purgeDataSeries();
225 impl->unlock();
226
227 if (dataInit) {
228 emit dataInitialized();
229 }
218 }
230 if(notify)
219 if(notify)
231 emit this->updated();
220 emit updated();
232 }
221 }
233
222
234
223
235 std::shared_ptr<IDataSeries> Variable::dataSeries() const noexcept
224 std::shared_ptr<IDataSeries> Variable::dataSeries() const noexcept
236 {
225 {
237 return impl->dataSeries();
226 return impl->dataSeries();
238 }
227 }
239
228
240 DataSeriesType Variable::type() const noexcept
229 DataSeriesType Variable::type() const noexcept
241 {
230 {
242 return impl->type();
231 return impl->type();
243 }
232 }
244
233
245 QVariantHash Variable::metadata() const noexcept
234 QVariantHash Variable::metadata() const noexcept
246 {
235 {
247 impl->lockRead();
236 impl->lockRead();
248 auto metadata = impl->m_Metadata;
237 auto metadata = impl->m_Metadata;
249 impl->unlock();
238 impl->unlock();
250 return metadata;
239 return metadata;
251 }
240 }
252
241
253 bool Variable::contains(const DateTimeRange &range) const noexcept
242 bool Variable::contains(const DateTimeRange &range) const noexcept
254 {
243 {
255 impl->lockRead();
244 impl->lockRead();
256 auto res = impl->m_Range.contains(range);
245 auto res = impl->m_Range.contains(range);
257 impl->unlock();
246 impl->unlock();
258 return res;
247 return res;
259 }
248 }
260
249
261 bool Variable::intersect(const DateTimeRange &range) const noexcept
250 bool Variable::intersect(const DateTimeRange &range) const noexcept
262 {
251 {
263
252
264 impl->lockRead();
253 impl->lockRead();
265 auto res = impl->m_Range.intersect(range);
254 auto res = impl->m_Range.intersect(range);
266 impl->unlock();
255 impl->unlock();
267 return res;
256 return res;
268 }
257 }
269
258
270 bool Variable::isInside(const DateTimeRange &range) const noexcept
259 bool Variable::isInside(const DateTimeRange &range) const noexcept
271 {
260 {
272 impl->lockRead();
261 impl->lockRead();
273 auto res = range.contains(DateTimeRange{impl->m_Range.m_TStart, impl->m_Range.m_TEnd});
262 auto res = range.contains(DateTimeRange{impl->m_Range.m_TStart, impl->m_Range.m_TEnd});
274 impl->unlock();
263 impl->unlock();
275 return res;
264 return res;
276 }
265 }
277
266
278 bool Variable::cacheContains(const DateTimeRange &range) const noexcept
267 bool Variable::cacheContains(const DateTimeRange &range) const noexcept
279 {
268 {
280 impl->lockRead();
269 impl->lockRead();
281 auto res = impl->m_CacheRange.contains(range);
270 auto res = impl->m_CacheRange.contains(range);
282 impl->unlock();
271 impl->unlock();
283 return res;
272 return res;
284 }
273 }
285
274
286 bool Variable::cacheIntersect(const DateTimeRange &range) const noexcept
275 bool Variable::cacheIntersect(const DateTimeRange &range) const noexcept
287 {
276 {
288 impl->lockRead();
277 impl->lockRead();
289 auto res = impl->m_CacheRange.intersect(range);
278 auto res = impl->m_CacheRange.intersect(range);
290 impl->unlock();
279 impl->unlock();
291 return res;
280 return res;
292 }
281 }
293
282
294 bool Variable::cacheIsInside(const DateTimeRange &range) const noexcept
283 bool Variable::cacheIsInside(const DateTimeRange &range) const noexcept
295 {
284 {
296 impl->lockRead();
285 impl->lockRead();
297 auto res = range.contains(DateTimeRange{impl->m_CacheRange.m_TStart, impl->m_CacheRange.m_TEnd});
286 auto res = range.contains(DateTimeRange{impl->m_CacheRange.m_TStart, impl->m_CacheRange.m_TEnd});
298 impl->unlock();
287 impl->unlock();
299 return res;
288 return res;
300 }
289 }
301
290
302
291
303 QVector<DateTimeRange> Variable::provideNotInCacheRangeList(const DateTimeRange &range) const noexcept
292 QVector<DateTimeRange> Variable::provideNotInCacheRangeList(const DateTimeRange &range) const noexcept
304 {
293 {
305 // This code assume that cach in contigue. Can return 0, 1 or 2 SqpRange
294 // This code assume that cach in contigue. Can return 0, 1 or 2 SqpRange
306 auto notInCache = QVector<DateTimeRange>{};
295 auto notInCache = QVector<DateTimeRange>{};
307 if (impl->m_CacheRange != INVALID_RANGE) {
296 if (impl->m_CacheRange != INVALID_RANGE) {
308
297
309 if (!this->cacheContains(range)) {
298 if (!this->cacheContains(range)) {
310 if (range.m_TEnd <= impl->m_CacheRange.m_TStart
299 if (range.m_TEnd <= impl->m_CacheRange.m_TStart
311 || range.m_TStart >= impl->m_CacheRange.m_TEnd) {
300 || range.m_TStart >= impl->m_CacheRange.m_TEnd) {
312 notInCache << range;
301 notInCache << range;
313 }
302 }
314 else if (range.m_TStart < impl->m_CacheRange.m_TStart
303 else if (range.m_TStart < impl->m_CacheRange.m_TStart
315 && range.m_TEnd <= impl->m_CacheRange.m_TEnd) {
304 && range.m_TEnd <= impl->m_CacheRange.m_TEnd) {
316 notInCache << DateTimeRange{range.m_TStart, impl->m_CacheRange.m_TStart};
305 notInCache << DateTimeRange{range.m_TStart, impl->m_CacheRange.m_TStart};
317 }
306 }
318 else if (range.m_TStart < impl->m_CacheRange.m_TStart
307 else if (range.m_TStart < impl->m_CacheRange.m_TStart
319 && range.m_TEnd > impl->m_CacheRange.m_TEnd) {
308 && range.m_TEnd > impl->m_CacheRange.m_TEnd) {
320 notInCache << DateTimeRange{range.m_TStart, impl->m_CacheRange.m_TStart}
309 notInCache << DateTimeRange{range.m_TStart, impl->m_CacheRange.m_TStart}
321 << DateTimeRange{impl->m_CacheRange.m_TEnd, range.m_TEnd};
310 << DateTimeRange{impl->m_CacheRange.m_TEnd, range.m_TEnd};
322 }
311 }
323 else if (range.m_TStart < impl->m_CacheRange.m_TEnd) {
312 else if (range.m_TStart < impl->m_CacheRange.m_TEnd) {
324 notInCache << DateTimeRange{impl->m_CacheRange.m_TEnd, range.m_TEnd};
313 notInCache << DateTimeRange{impl->m_CacheRange.m_TEnd, range.m_TEnd};
325 }
314 }
326 else {
315 else {
327 qCCritical(LOG_Variable()) << tr("Detection of unknown case.")
316 qCCritical(LOG_Variable()) << tr("Detection of unknown case.")
328 << QThread::currentThread();
317 << QThread::currentThread();
329 }
318 }
330 }
319 }
331 }
320 }
332 else {
321 else {
333 notInCache << range;
322 notInCache << range;
334 }
323 }
335
324
336 return notInCache;
325 return notInCache;
337 }
326 }
338
327
339 QVector<DateTimeRange> Variable::provideInCacheRangeList(const DateTimeRange &range) const noexcept
328 QVector<DateTimeRange> Variable::provideInCacheRangeList(const DateTimeRange &range) const noexcept
340 {
329 {
341 // This code assume that cach in contigue. Can return 0 or 1 SqpRange
330 // This code assume that cach in contigue. Can return 0 or 1 SqpRange
342
331
343 auto inCache = QVector<DateTimeRange>{};
332 auto inCache = QVector<DateTimeRange>{};
344
333
345 if (impl->m_CacheRange != INVALID_RANGE) {
334 if (impl->m_CacheRange != INVALID_RANGE) {
346
335
347 if (this->cacheIntersect(range)) {
336 if (this->cacheIntersect(range)) {
348 if (range.m_TStart <= impl->m_CacheRange.m_TStart
337 if (range.m_TStart <= impl->m_CacheRange.m_TStart
349 && range.m_TEnd >= impl->m_CacheRange.m_TStart
338 && range.m_TEnd >= impl->m_CacheRange.m_TStart
350 && range.m_TEnd < impl->m_CacheRange.m_TEnd) {
339 && range.m_TEnd < impl->m_CacheRange.m_TEnd) {
351 inCache << DateTimeRange{impl->m_CacheRange.m_TStart, range.m_TEnd};
340 inCache << DateTimeRange{impl->m_CacheRange.m_TStart, range.m_TEnd};
352 }
341 }
353
342
354 else if (range.m_TStart >= impl->m_CacheRange.m_TStart
343 else if (range.m_TStart >= impl->m_CacheRange.m_TStart
355 && range.m_TEnd <= impl->m_CacheRange.m_TEnd) {
344 && range.m_TEnd <= impl->m_CacheRange.m_TEnd) {
356 inCache << range;
345 inCache << range;
357 }
346 }
358 else if (range.m_TStart > impl->m_CacheRange.m_TStart
347 else if (range.m_TStart > impl->m_CacheRange.m_TStart
359 && range.m_TEnd > impl->m_CacheRange.m_TEnd) {
348 && range.m_TEnd > impl->m_CacheRange.m_TEnd) {
360 inCache << DateTimeRange{range.m_TStart, impl->m_CacheRange.m_TEnd};
349 inCache << DateTimeRange{range.m_TStart, impl->m_CacheRange.m_TEnd};
361 }
350 }
362 else if (range.m_TStart <= impl->m_CacheRange.m_TStart
351 else if (range.m_TStart <= impl->m_CacheRange.m_TStart
363 && range.m_TEnd >= impl->m_CacheRange.m_TEnd) {
352 && range.m_TEnd >= impl->m_CacheRange.m_TEnd) {
364 inCache << impl->m_CacheRange;
353 inCache << impl->m_CacheRange;
365 }
354 }
366 else {
355 else {
367 qCCritical(LOG_Variable()) << tr("Detection of unknown case.")
356 qCCritical(LOG_Variable()) << tr("Detection of unknown case.")
368 << QThread::currentThread();
357 << QThread::currentThread();
369 }
358 }
370 }
359 }
371 }
360 }
372
361
373 return inCache;
362 return inCache;
374 }
363 }
375
364
376
365
377 QVector<DateTimeRange> Variable::provideNotInCacheRangeList(const DateTimeRange &oldRange,
366 QVector<DateTimeRange> Variable::provideNotInCacheRangeList(const DateTimeRange &oldRange,
378 const DateTimeRange &nextRange)
367 const DateTimeRange &nextRange)
379 {
368 {
380
369
381 // This code assume that cach in contigue. Can return 0, 1 or 2 SqpRange
370 // This code assume that cach in contigue. Can return 0, 1 or 2 SqpRange
382 auto notInCache = QVector<DateTimeRange>{};
371 auto notInCache = QVector<DateTimeRange>{};
383 if (oldRange != INVALID_RANGE) {
372 if (oldRange != INVALID_RANGE) {
384
373
385 if (!oldRange.contains(nextRange)) {
374 if (!oldRange.contains(nextRange)) {
386 if (nextRange.m_TEnd <= oldRange.m_TStart || nextRange.m_TStart >= oldRange.m_TEnd) {
375 if (nextRange.m_TEnd <= oldRange.m_TStart || nextRange.m_TStart >= oldRange.m_TEnd) {
387 notInCache << nextRange;
376 notInCache << nextRange;
388 }
377 }
389 else if (nextRange.m_TStart < oldRange.m_TStart
378 else if (nextRange.m_TStart < oldRange.m_TStart
390 && nextRange.m_TEnd <= oldRange.m_TEnd) {
379 && nextRange.m_TEnd <= oldRange.m_TEnd) {
391 notInCache << DateTimeRange{nextRange.m_TStart, oldRange.m_TStart};
380 notInCache << DateTimeRange{nextRange.m_TStart, oldRange.m_TStart};
392 }
381 }
393 else if (nextRange.m_TStart < oldRange.m_TStart && nextRange.m_TEnd > oldRange.m_TEnd) {
382 else if (nextRange.m_TStart < oldRange.m_TStart && nextRange.m_TEnd > oldRange.m_TEnd) {
394 notInCache << DateTimeRange{nextRange.m_TStart, oldRange.m_TStart}
383 notInCache << DateTimeRange{nextRange.m_TStart, oldRange.m_TStart}
395 << DateTimeRange{oldRange.m_TEnd, nextRange.m_TEnd};
384 << DateTimeRange{oldRange.m_TEnd, nextRange.m_TEnd};
396 }
385 }
397 else if (nextRange.m_TStart < oldRange.m_TEnd) {
386 else if (nextRange.m_TStart < oldRange.m_TEnd) {
398 notInCache << DateTimeRange{oldRange.m_TEnd, nextRange.m_TEnd};
387 notInCache << DateTimeRange{oldRange.m_TEnd, nextRange.m_TEnd};
399 }
388 }
400 else {
389 else {
401 qCCritical(LOG_Variable()) << tr("Detection of unknown case.")
390 qCCritical(LOG_Variable()) << tr("Detection of unknown case.")
402 << QThread::currentThread();
391 << QThread::currentThread();
403 }
392 }
404 }
393 }
405 }
394 }
406 else {
395 else {
407 notInCache << nextRange;
396 notInCache << nextRange;
408 }
397 }
409
398
410 return notInCache;
399 return notInCache;
411 }
400 }
412
401
413 QVector<DateTimeRange> Variable::provideInCacheRangeList(const DateTimeRange &oldRange,
402 QVector<DateTimeRange> Variable::provideInCacheRangeList(const DateTimeRange &oldRange,
414 const DateTimeRange &nextRange)
403 const DateTimeRange &nextRange)
415 {
404 {
416 // This code assume that cach is contigue. Can return 0 or 1 SqpRange
405 // This code assume that cach is contigue. Can return 0 or 1 SqpRange
417
406
418 auto inCache = QVector<DateTimeRange>{};
407 auto inCache = QVector<DateTimeRange>{};
419
408
420 if (oldRange != INVALID_RANGE) {
409 if (oldRange != INVALID_RANGE) {
421
410
422 if (oldRange.intersect(nextRange)) {
411 if (oldRange.intersect(nextRange)) {
423 if (nextRange.m_TStart <= oldRange.m_TStart && nextRange.m_TEnd >= oldRange.m_TStart
412 if (nextRange.m_TStart <= oldRange.m_TStart && nextRange.m_TEnd >= oldRange.m_TStart
424 && nextRange.m_TEnd < oldRange.m_TEnd) {
413 && nextRange.m_TEnd < oldRange.m_TEnd) {
425 inCache << DateTimeRange{oldRange.m_TStart, nextRange.m_TEnd};
414 inCache << DateTimeRange{oldRange.m_TStart, nextRange.m_TEnd};
426 }
415 }
427
416
428 else if (nextRange.m_TStart >= oldRange.m_TStart
417 else if (nextRange.m_TStart >= oldRange.m_TStart
429 && nextRange.m_TEnd <= oldRange.m_TEnd) {
418 && nextRange.m_TEnd <= oldRange.m_TEnd) {
430 inCache << nextRange;
419 inCache << nextRange;
431 }
420 }
432 else if (nextRange.m_TStart > oldRange.m_TStart && nextRange.m_TEnd > oldRange.m_TEnd) {
421 else if (nextRange.m_TStart > oldRange.m_TStart && nextRange.m_TEnd > oldRange.m_TEnd) {
433 inCache << DateTimeRange{nextRange.m_TStart, oldRange.m_TEnd};
422 inCache << DateTimeRange{nextRange.m_TStart, oldRange.m_TEnd};
434 }
423 }
435 else if (nextRange.m_TStart <= oldRange.m_TStart
424 else if (nextRange.m_TStart <= oldRange.m_TStart
436 && nextRange.m_TEnd >= oldRange.m_TEnd) {
425 && nextRange.m_TEnd >= oldRange.m_TEnd) {
437 inCache << oldRange;
426 inCache << oldRange;
438 }
427 }
439 else {
428 else {
440 qCCritical(LOG_Variable()) << tr("Detection of unknown case.")
429 qCCritical(LOG_Variable()) << tr("Detection of unknown case.")
441 << QThread::currentThread();
430 << QThread::currentThread();
442 }
431 }
443 }
432 }
444 }
433 }
445
434
446 return inCache;
435 return inCache;
447 }
436 }
@@ -1,200 +1,199
1 #include "Variable/VariableController2.h"
1 #include "Variable/VariableController2.h"
2 #include "Variable/VariableSynchronizationGroup2.h"
2 #include "Variable/VariableSynchronizationGroup2.h"
3 #include <Common/containers.h>
3 #include <Common/containers.h>
4 #include <Common/debug.h>
4 #include <Common/debug.h>
5 #include <Data/DataProviderParameters.h>
5 #include <Data/DataProviderParameters.h>
6 #include <Data/DateTimeRangeHelper.h>
6 #include <Data/DateTimeRangeHelper.h>
7 #include <Variable/VariableCacheStrategyFactory.h>
7 #include <Variable/VariableCacheStrategyFactory.h>
8
8
9 class VariableController2::VariableController2Private
9 class VariableController2::VariableController2Private
10 {
10 {
11 QMap<QUuid,std::shared_ptr<Variable>> _variables;
11 QMap<QUuid,std::shared_ptr<Variable>> _variables;
12 QMap<QUuid,std::shared_ptr<IDataProvider>> _providers;
12 QMap<QUuid,std::shared_ptr<IDataProvider>> _providers;
13 QMap<QUuid,std::shared_ptr<VariableSynchronizationGroup2>> _synchronizationGroups;
13 QMap<QUuid,std::shared_ptr<VariableSynchronizationGroup2>> _synchronizationGroups;
14 std::unique_ptr<VariableCacheStrategy> _cacheStrategy;
14 std::unique_ptr<VariableCacheStrategy> _cacheStrategy;
15 bool p_contains(const std::shared_ptr<Variable>& variable)
15 bool p_contains(const std::shared_ptr<Variable>& variable)
16 {
16 {
17 return _providers.contains(variable->ID());
17 return _providers.contains(variable->ID());
18 }
18 }
19 bool v_contains(const std::shared_ptr<Variable>& variable)
19 bool v_contains(const std::shared_ptr<Variable>& variable)
20 {
20 {
21 return SciQLop::containers::contains(this->_variables, variable);
21 return SciQLop::containers::contains(this->_variables, variable);
22 }
22 }
23 bool sg_contains(const std::shared_ptr<Variable>& variable)
23 bool sg_contains(const std::shared_ptr<Variable>& variable)
24 {
24 {
25 return _synchronizationGroups.contains(variable->ID());
25 return _synchronizationGroups.contains(variable->ID());
26 }
26 }
27
27
28 void _changeRange(const std::shared_ptr<Variable>& var, DateTimeRange r)
28 void _changeRange(const std::shared_ptr<Variable>& var, DateTimeRange r)
29 {
29 {
30 auto provider = _providers[var->ID()];
30 auto provider = _providers[var->ID()];
31 DateTimeRange newCacheRange;
31 DateTimeRange newCacheRange;
32 std::vector<DateTimeRange> missingRanges;
32 std::vector<DateTimeRange> missingRanges;
33 if(DateTimeRangeHelper::hasnan(var->cacheRange()))
33 if(DateTimeRangeHelper::hasnan(var->cacheRange()))
34 {
34 {
35 newCacheRange = _cacheStrategy->computeRange(r,r);
35 newCacheRange = _cacheStrategy->computeRange(r,r);
36 missingRanges = {newCacheRange};
36 missingRanges = {newCacheRange};
37 }
37 }
38 else
38 else
39 {
39 {
40 newCacheRange = _cacheStrategy->computeRange(var->cacheRange(),r);
40 newCacheRange = _cacheStrategy->computeRange(var->cacheRange(),r);
41 missingRanges = newCacheRange - var->cacheRange();
41 missingRanges = newCacheRange - var->cacheRange();
42 }
42 }
43 std::vector<IDataSeries*> data;
43 for(auto range:missingRanges)
44 for(auto range:missingRanges)
44 {
45 {
45 auto data = provider->getData(DataProviderParameters{{range},var->metadata()});
46 data.push_back(provider->getData(DataProviderParameters{{range},var->metadata()}));
46 var->mergeDataSeries(data);
47 }
47 }
48 var->setCacheRange(newCacheRange);
48 var->updateData(data,r,newCacheRange,true);
49 var->setRange(r);
50 }
49 }
51 public:
50 public:
52 VariableController2Private(QObject* parent=Q_NULLPTR)
51 VariableController2Private(QObject* parent=Q_NULLPTR)
53 :_cacheStrategy(VariableCacheStrategyFactory::createCacheStrategy(CacheStrategy::SingleThreshold))
52 :_cacheStrategy(VariableCacheStrategyFactory::createCacheStrategy(CacheStrategy::SingleThreshold))
54 {
53 {
55 Q_UNUSED(parent);
54 Q_UNUSED(parent);
56 }
55 }
57
56
58 ~VariableController2Private() = default;
57 ~VariableController2Private() = default;
59
58
60 std::shared_ptr<Variable> createVariable(const QString &name, const QVariantHash &metadata, std::shared_ptr<IDataProvider> provider)
59 std::shared_ptr<Variable> createVariable(const QString &name, const QVariantHash &metadata, std::shared_ptr<IDataProvider> provider)
61 {
60 {
62 auto newVar = std::make_shared<Variable>(name,metadata);
61 auto newVar = std::make_shared<Variable>(name,metadata);
63 this->_variables[newVar->ID()] = newVar;
62 this->_variables[newVar->ID()] = newVar;
64 this->_providers[newVar->ID()] = std::move(provider);
63 this->_providers[newVar->ID()] = std::move(provider);
65 this->_synchronizationGroups[newVar->ID()] = std::make_shared<VariableSynchronizationGroup2>(newVar->ID());
64 this->_synchronizationGroups[newVar->ID()] = std::make_shared<VariableSynchronizationGroup2>(newVar->ID());
66 return newVar;
65 return newVar;
67 }
66 }
68
67
69 void deleteVariable(const std::shared_ptr<Variable>& variable)
68 void deleteVariable(const std::shared_ptr<Variable>& variable)
70 {
69 {
71 /*
70 /*
72 * Removing twice a var is ok but a var without provider has to be a hard error
71 * Removing twice a var is ok but a var without provider has to be a hard error
73 * this means we got the var controller in an inconsistent state
72 * this means we got the var controller in an inconsistent state
74 */
73 */
75 if(v_contains(variable))
74 if(v_contains(variable))
76 this->_variables.remove(variable->ID());
75 this->_variables.remove(variable->ID());
77 if(p_contains(variable))
76 if(p_contains(variable))
78 this->_providers.remove(variable->ID());
77 this->_providers.remove(variable->ID());
79 else
78 else
80 SCIQLOP_ERROR(VariableController2Private, "No provider found for given variable");
79 SCIQLOP_ERROR(VariableController2Private, "No provider found for given variable");
81 }
80 }
82
81
83 void asyncChangeRange(const std::shared_ptr<Variable>& variable, const DateTimeRange& r)
82 void asyncChangeRange(const std::shared_ptr<Variable>& variable, const DateTimeRange& r)
84 {
83 {
85
84
86 }
85 }
87
86
88 void changeRange(const std::shared_ptr<Variable>& variable, DateTimeRange r)
87 void changeRange(const std::shared_ptr<Variable>& variable, DateTimeRange r)
89 {
88 {
90 if(p_contains(variable))
89 if(p_contains(variable))
91 {
90 {
92 if(!DateTimeRangeHelper::hasnan(r))
91 if(!DateTimeRangeHelper::hasnan(r))
93 {
92 {
94 auto group = _synchronizationGroups[variable->ID()];
93 auto group = _synchronizationGroups[variable->ID()];
95 if(auto transformation = DateTimeRangeHelper::computeTransformation(variable->range(),r);
94 if(auto transformation = DateTimeRangeHelper::computeTransformation(variable->range(),r);
96 transformation.has_value())
95 transformation.has_value())
97 {
96 {
98 for(auto varId:group->variables())
97 for(auto varId:group->variables())
99 {
98 {
100 auto var = _variables[varId];
99 auto var = _variables[varId];
101 auto newRange = var->range().transform(transformation.value());
100 auto newRange = var->range().transform(transformation.value());
102 _changeRange(var,newRange);
101 _changeRange(var,newRange);
103 }
102 }
104 }
103 }
105 else // force new range to all variables -> may be weird if more than one var in the group
104 else // force new range to all variables -> may be weird if more than one var in the group
106 // @TODO ensure that there is no side effects
105 // @TODO ensure that there is no side effects
107 {
106 {
108 for(auto varId:group->variables())
107 for(auto varId:group->variables())
109 {
108 {
110 auto var = _variables[varId];
109 auto var = _variables[varId];
111 _changeRange(var,r);
110 _changeRange(var,r);
112 }
111 }
113 }
112 }
114 }
113 }
115 else
114 else
116 {
115 {
117 SCIQLOP_ERROR(VariableController2Private, "Invalid range containing NaN");
116 SCIQLOP_ERROR(VariableController2Private, "Invalid range containing NaN");
118 }
117 }
119 }
118 }
120 else
119 else
121 {
120 {
122 SCIQLOP_ERROR(VariableController2Private, "No provider found for given variable");
121 SCIQLOP_ERROR(VariableController2Private, "No provider found for given variable");
123 }
122 }
124 }
123 }
125
124
126 void synchronize(const std::shared_ptr<Variable>& var, const std::shared_ptr<Variable>& with)
125 void synchronize(const std::shared_ptr<Variable>& var, const std::shared_ptr<Variable>& with)
127 {
126 {
128 if(v_contains(var) && v_contains(with))
127 if(v_contains(var) && v_contains(with))
129 {
128 {
130 if(sg_contains(var) && sg_contains(with))
129 if(sg_contains(var) && sg_contains(with))
131 {
130 {
132
131
133 auto dest_group = this->_synchronizationGroups[with->ID()];
132 auto dest_group = this->_synchronizationGroups[with->ID()];
134 this->_synchronizationGroups[var->ID()] = dest_group;
133 this->_synchronizationGroups[var->ID()] = dest_group;
135 dest_group->addVariable(var->ID());
134 dest_group->addVariable(var->ID());
136 }
135 }
137 else
136 else
138 {
137 {
139 SCIQLOP_ERROR(VariableController2Private, "At least one of the given variables isn't in a sync group");
138 SCIQLOP_ERROR(VariableController2Private, "At least one of the given variables isn't in a sync group");
140 }
139 }
141 }
140 }
142 else
141 else
143 {
142 {
144 SCIQLOP_ERROR(VariableController2Private, "At least one of the given variables is not found");
143 SCIQLOP_ERROR(VariableController2Private, "At least one of the given variables is not found");
145 }
144 }
146 }
145 }
147
146
148 const std::set<std::shared_ptr<Variable>> variables()
147 const std::set<std::shared_ptr<Variable>> variables()
149 {
148 {
150 std::set<std::shared_ptr<Variable>> vars;
149 std::set<std::shared_ptr<Variable>> vars;
151 for(const auto &var:_variables)
150 for(const auto &var:_variables)
152 {
151 {
153 vars.insert(var);
152 vars.insert(var);
154 }
153 }
155 return vars;
154 return vars;
156 }
155 }
157
156
158 };
157 };
159
158
160 VariableController2::VariableController2()
159 VariableController2::VariableController2()
161 :impl{spimpl::make_unique_impl<VariableController2Private>()}
160 :impl{spimpl::make_unique_impl<VariableController2Private>()}
162 {}
161 {}
163
162
164 std::shared_ptr<Variable> VariableController2::createVariable(const QString &name, const QVariantHash &metadata, const std::shared_ptr<IDataProvider>& provider, const DateTimeRange &range)
163 std::shared_ptr<Variable> VariableController2::createVariable(const QString &name, const QVariantHash &metadata, const std::shared_ptr<IDataProvider>& provider, const DateTimeRange &range)
165 {
164 {
166 auto var = impl->createVariable(name, metadata, provider);
165 auto var = impl->createVariable(name, metadata, provider);
167 emit variableAdded(var);
166 emit variableAdded(var);
168 if(!DateTimeRangeHelper::hasnan(range))
167 if(!DateTimeRangeHelper::hasnan(range))
169 impl->changeRange(var,range);
168 impl->changeRange(var,range);
170 else
169 else
171 SCIQLOP_ERROR(VariableController2, "Creating a variable with default constructed DateTimeRange is an error");
170 SCIQLOP_ERROR(VariableController2, "Creating a variable with default constructed DateTimeRange is an error");
172 return var;
171 return var;
173 }
172 }
174
173
175 void VariableController2::deleteVariable(const std::shared_ptr<Variable>& variable)
174 void VariableController2::deleteVariable(const std::shared_ptr<Variable>& variable)
176 {
175 {
177 impl->deleteVariable(variable);
176 impl->deleteVariable(variable);
178 emit variableDeleted(variable);
177 emit variableDeleted(variable);
179 }
178 }
180
179
181 void VariableController2::changeRange(const std::shared_ptr<Variable>& variable, const DateTimeRange& r)
180 void VariableController2::changeRange(const std::shared_ptr<Variable>& variable, const DateTimeRange& r)
182 {
181 {
183 impl->changeRange(variable, r);
182 impl->changeRange(variable, r);
184 }
183 }
185
184
186 void VariableController2::asyncChangeRange(const std::shared_ptr<Variable> &variable, const DateTimeRange &r)
185 void VariableController2::asyncChangeRange(const std::shared_ptr<Variable> &variable, const DateTimeRange &r)
187 {
186 {
188 impl->asyncChangeRange(variable, r);
187 impl->asyncChangeRange(variable, r);
189 }
188 }
190
189
191 const std::set<std::shared_ptr<Variable> > VariableController2::variables()
190 const std::set<std::shared_ptr<Variable> > VariableController2::variables()
192 {
191 {
193 return impl->variables();
192 return impl->variables();
194 }
193 }
195
194
196 void VariableController2::synchronize(const std::shared_ptr<Variable> &var, const std::shared_ptr<Variable> &with)
195 void VariableController2::synchronize(const std::shared_ptr<Variable> &var, const std::shared_ptr<Variable> &with)
197 {
196 {
198 impl->synchronize(var, with);
197 impl->synchronize(var, with);
199 }
198 }
200
199
@@ -1,76 +1,76
1 #include <QObject>
1 #include <QObject>
2 #include <QtTest>
2 #include <QtTest>
3
3
4 #include <Data/IDataProvider.h>
4 #include <Data/IDataProvider.h>
5 #include <Time/TimeController.h>
5 #include <Time/TimeController.h>
6 #include <Variable/Variable.h>
6 #include <Variable/Variable.h>
7 #include <Variable/VariableController.h>
7 #include <Variable/VariableController.h>
8
8
9 #include <memory>
9 #include <memory>
10
10
11 namespace {
11 namespace {
12
12
13 /// Provider used for the tests
13 /// Provider used for the tests
14 class TestProvider : public IDataProvider {
14 class TestProvider : public IDataProvider {
15 std::shared_ptr<IDataProvider> clone() const { return std::make_shared<TestProvider>(); }
15 std::shared_ptr<IDataProvider> clone() const override { return std::make_shared<TestProvider>(); }
16
16
17 void requestDataLoading(QUuid acqIdentifier, const DataProviderParameters &parameters) override
17 void requestDataLoading(QUuid acqIdentifier, const DataProviderParameters &parameters) override
18 {
18 {
19 // Does nothing
19 // Does nothing
20 }
20 }
21
21
22 void requestDataAborting(QUuid acqIdentifier) override
22 void requestDataAborting(QUuid acqIdentifier) override
23 {
23 {
24 // Does nothing
24 // Does nothing
25 }
25 }
26 };
26 };
27
27
28 /// Generates a time controller for the tests
28 /// Generates a time controller for the tests
29 std::unique_ptr<TimeController> defaultTimeController()
29 std::unique_ptr<TimeController> defaultTimeController()
30 {
30 {
31 auto timeController = std::make_unique<TimeController>();
31 auto timeController = std::make_unique<TimeController>();
32
32
33 QDateTime start{QDate{2017, 01, 01}, QTime{0, 0, 0, 0}};
33 QDateTime start{QDate{2017, 01, 01}, QTime{0, 0, 0, 0}};
34 QDateTime end{QDate{2017, 01, 02}, QTime{0, 0, 0, 0}};
34 QDateTime end{QDate{2017, 01, 02}, QTime{0, 0, 0, 0}};
35 timeController->setDateTimeRange(
35 timeController->setDateTimeRange(
36 DateTimeRange{DateUtils::secondsSinceEpoch(start), DateUtils::secondsSinceEpoch(end)});
36 DateTimeRange{DateUtils::secondsSinceEpoch(start), DateUtils::secondsSinceEpoch(end)});
37
37
38 return timeController;
38 return timeController;
39 }
39 }
40
40
41 } // namespace
41 } // namespace
42
42
43 class TestVariableController : public QObject {
43 class TestVariableController : public QObject {
44 Q_OBJECT
44 Q_OBJECT
45
45
46 private slots:
46 private slots:
47 void initTestCase() { QSKIP("Skip it"); }
47 void initTestCase() { QSKIP("Skip it"); }
48
48
49 /// Test removes variable from controller
49 /// Test removes variable from controller
50 void testDeleteVariable();
50 void testDeleteVariable();
51 };
51 };
52
52
53 void TestVariableController::testDeleteVariable()
53 void TestVariableController::testDeleteVariable()
54 {
54 {
55 // Creates variable controller
55 // Creates variable controller
56 auto timeController = defaultTimeController();
56 auto timeController = defaultTimeController();
57 VariableController variableController{};
57 VariableController variableController{};
58 //variableController.setTimeController(timeController.get());
58 //variableController.setTimeController(timeController.get());
59
59
60 // Creates a variable from the controller
60 // Creates a variable from the controller
61 auto variable
61 auto variable
62 = variableController.createVariable("variable", {}, std::make_shared<TestProvider>(), timeController->dateTime());
62 = variableController.createVariable("variable", {}, std::make_shared<TestProvider>(), timeController->dateTime());
63
63
64 qDebug() << QString::number(variable.use_count());
64 qDebug() << QString::number(variable.use_count());
65
65
66 // Removes the variable from the controller
66 // Removes the variable from the controller
67 variableController.deleteVariable(variable);
67 variableController.deleteVariable(variable);
68
68
69 // Verifies that the variable has been deleted: this implies that the number of shared_ptr
69 // Verifies that the variable has been deleted: this implies that the number of shared_ptr
70 // objects referring to the variable is 1 (the reference of this scope). Otherwise, the deletion
70 // objects referring to the variable is 1 (the reference of this scope). Otherwise, the deletion
71 // is considered invalid since the variable is still referenced in the controller
71 // is considered invalid since the variable is still referenced in the controller
72 QVERIFY(variable.use_count() == 1);
72 QVERIFY(variable.use_count() == 1);
73 }
73 }
74
74
75 QTEST_MAIN(TestVariableController)
75 QTEST_MAIN(TestVariableController)
76 #include "TestVariableController.moc"
76 #include "TestVariableController.moc"
@@ -1,508 +1,508
1 #include <QObject>
1 #include <QObject>
2 #include <QtTest>
2 #include <QtTest>
3
3
4 #include <memory>
4 #include <memory>
5
5
6 #include <Data/DataProviderParameters.h>
6 #include <Data/DataProviderParameters.h>
7 #include <Data/IDataProvider.h>
7 #include <Data/IDataProvider.h>
8 #include <Data/ScalarSeries.h>
8 #include <Data/ScalarSeries.h>
9 #include <Time/TimeController.h>
9 #include <Time/TimeController.h>
10 #include <Variable/Variable.h>
10 #include <Variable/Variable.h>
11 #include <Variable/VariableController.h>
11 #include <Variable/VariableController.h>
12 #include <Variable/VariableModel.h>
12 #include <Variable/VariableModel.h>
13
13
14 namespace {
14 namespace {
15
15
16 /// Delay after each operation on the variable before validating it (in ms)
16 /// Delay after each operation on the variable before validating it (in ms)
17 const auto OPERATION_DELAY = 100;
17 const auto OPERATION_DELAY = 100;
18
18
19 /**
19 /**
20 * Generates values according to a range. The value generated for a time t is the number of seconds
20 * Generates values according to a range. The value generated for a time t is the number of seconds
21 * of difference between t and a reference value (which is midnight -> 00:00:00)
21 * of difference between t and a reference value (which is midnight -> 00:00:00)
22 *
22 *
23 * Example: For a range between 00:00:10 and 00:00:20, the generated values are
23 * Example: For a range between 00:00:10 and 00:00:20, the generated values are
24 * {10,11,12,13,14,15,16,17,18,19,20}
24 * {10,11,12,13,14,15,16,17,18,19,20}
25 */
25 */
26 std::vector<double> values(const DateTimeRange &range)
26 std::vector<double> values(const DateTimeRange &range)
27 {
27 {
28 QTime referenceTime{0, 0};
28 QTime referenceTime{0, 0};
29
29
30 std::vector<double> result{};
30 std::vector<double> result{};
31
31
32 for (auto i = range.m_TStart; i <= range.m_TEnd; ++i) {
32 for (auto i = range.m_TStart; i <= range.m_TEnd; ++i) {
33 auto time = DateUtils::dateTime(i).time();
33 auto time = DateUtils::dateTime(i).time();
34 result.push_back(referenceTime.secsTo(time));
34 result.push_back(referenceTime.secsTo(time));
35 }
35 }
36
36
37 return result;
37 return result;
38 }
38 }
39
39
40 void validateRanges(VariableController &variableController,
40 void validateRanges(VariableController &variableController,
41 const std::map<int, DateTimeRange> &expectedRanges)
41 const std::map<int, DateTimeRange> &expectedRanges)
42 {
42 {
43 for (const auto &expectedRangeEntry : expectedRanges) {
43 for (const auto &expectedRangeEntry : expectedRanges) {
44 auto variableIndex = expectedRangeEntry.first;
44 auto variableIndex = expectedRangeEntry.first;
45 auto expectedRange = expectedRangeEntry.second;
45 auto expectedRange = expectedRangeEntry.second;
46
46
47 // Gets the variable in the controller
47 // Gets the variable in the controller
48 auto variable = variableController.variableModel()->variable(variableIndex);
48 auto variable = variableController.variableModel()->variable(variableIndex);
49
49
50 // Compares variable's range to the expected range
50 // Compares variable's range to the expected range
51 QVERIFY(variable != nullptr);
51 QVERIFY(variable != nullptr);
52 auto range = variable->range();
52 auto range = variable->range();
53 qInfo() << "range vs expected range" << range << expectedRange;
53 qInfo() << "range vs expected range" << range << expectedRange;
54 QCOMPARE(range, expectedRange);
54 QCOMPARE(range, expectedRange);
55
55
56 // Compares variable's data with values expected for its range
56 // Compares variable's data with values expected for its range
57 auto dataSeries = variable->dataSeries();
57 auto dataSeries = variable->dataSeries();
58 QVERIFY(dataSeries != nullptr);
58 QVERIFY(dataSeries != nullptr);
59
59
60 auto it = dataSeries->xAxisRange(range.m_TStart, range.m_TEnd);
60 auto it = dataSeries->xAxisRange(range.m_TStart, range.m_TEnd);
61 auto expectedValues = values(range);
61 auto expectedValues = values(range);
62 qInfo() << std::distance(it.first, it.second) << expectedValues.size();
62 qInfo() << std::distance(it.first, it.second) << expectedValues.size();
63 QVERIFY(std::equal(it.first, it.second, expectedValues.cbegin(), expectedValues.cend(),
63 QVERIFY(std::equal(it.first, it.second, expectedValues.cbegin(), expectedValues.cend(),
64 [](const auto &dataSeriesIt, const auto &expectedValue) {
64 [](const auto &dataSeriesIt, const auto &expectedValue) {
65 return dataSeriesIt.value() == expectedValue;
65 return dataSeriesIt.value() == expectedValue;
66 }));
66 }));
67 }
67 }
68 }
68 }
69
69
70 /// Provider used for the tests
70 /// Provider used for the tests
71 class TestProvider : public IDataProvider {
71 class TestProvider : public IDataProvider {
72 std::shared_ptr<IDataProvider> clone() const { return std::make_shared<TestProvider>(); }
72 std::shared_ptr<IDataProvider> clone() const override { return std::make_shared<TestProvider>(); }
73
73
74 void requestDataLoading(QUuid acqIdentifier, const DataProviderParameters &parameters) override
74 void requestDataLoading(QUuid acqIdentifier, const DataProviderParameters &parameters) override
75 {
75 {
76 const auto &ranges = parameters.m_Times;
76 const auto &ranges = parameters.m_Times;
77
77
78 for (const auto &range : ranges) {
78 for (const auto &range : ranges) {
79 // Generates data series
79 // Generates data series
80 auto valuesData = values(range);
80 auto valuesData = values(range);
81
81
82 std::vector<double> xAxisData{};
82 std::vector<double> xAxisData{};
83 for (auto i = range.m_TStart; i <= range.m_TEnd; ++i) {
83 for (auto i = range.m_TStart; i <= range.m_TEnd; ++i) {
84 xAxisData.push_back(i);
84 xAxisData.push_back(i);
85 }
85 }
86
86
87 auto dataSeries = std::make_shared<ScalarSeries>(
87 auto dataSeries = std::make_shared<ScalarSeries>(
88 std::move(xAxisData), std::move(valuesData), Unit{"t", true}, Unit{});
88 std::move(xAxisData), std::move(valuesData), Unit{"t", true}, Unit{});
89
89
90 emit dataProvided(acqIdentifier, dataSeries, range);
90 emit dataProvided(acqIdentifier, dataSeries, range);
91 }
91 }
92 }
92 }
93
93
94 void requestDataAborting(QUuid acqIdentifier) override
94 void requestDataAborting(QUuid acqIdentifier) override
95 {
95 {
96 // Does nothing
96 // Does nothing
97 }
97 }
98 };
98 };
99
99
100 /**
100 /**
101 * Interface representing an operation performed on a variable controller.
101 * Interface representing an operation performed on a variable controller.
102 * This interface is used in tests to apply a set of operations and check the status of the
102 * This interface is used in tests to apply a set of operations and check the status of the
103 * controller after each operation
103 * controller after each operation
104 */
104 */
105 struct IOperation {
105 struct IOperation {
106 virtual ~IOperation() = default;
106 virtual ~IOperation() = default;
107 /// Executes the operation on the variable controller
107 /// Executes the operation on the variable controller
108 virtual void exec(VariableController &variableController) const = 0;
108 virtual void exec(VariableController &variableController) const = 0;
109 };
109 };
110
110
111 /**
111 /**
112 *Variable creation operation in the controller
112 *Variable creation operation in the controller
113 */
113 */
114 struct Create : public IOperation {
114 struct Create : public IOperation {
115 explicit Create(int index, const DateTimeRange &range) : m_Index{index},m_range(range) {}
115 explicit Create(int index, const DateTimeRange &range) : m_Index{index},m_range(range) {}
116
116
117 void exec(VariableController &variableController) const override
117 void exec(VariableController &variableController) const override
118 {
118 {
119 auto variable = variableController.createVariable(QString::number(m_Index), {},
119 auto variable = variableController.createVariable(QString::number(m_Index), {},
120 std::make_unique<TestProvider>(), m_range);
120 std::make_unique<TestProvider>(), m_range);
121 }
121 }
122 int m_Index; ///< The index of the variable to create in the controller
122 int m_Index; ///< The index of the variable to create in the controller
123 DateTimeRange m_range;
123 DateTimeRange m_range;
124 };
124 };
125
125
126 /**
126 /**
127 * Variable move/shift operation in the controller
127 * Variable move/shift operation in the controller
128 */
128 */
129 struct Move : public IOperation {
129 struct Move : public IOperation {
130 explicit Move(int index, const DateTimeRange &newRange, bool shift = false, int delayMS = 10)
130 explicit Move(int index, const DateTimeRange &newRange, bool shift = false, int delayMS = 10)
131 : m_Index{index}, m_NewRange{newRange}, m_Shift{shift}, m_DelayMs{delayMS}
131 : m_Index{index}, m_NewRange{newRange}, m_Shift{shift}, m_DelayMs{delayMS}
132 {
132 {
133 }
133 }
134
134
135 void exec(VariableController &variableController) const override
135 void exec(VariableController &variableController) const override
136 {
136 {
137 if (auto variable = variableController.variableModel()->variable(m_Index)) {
137 if (auto variable = variableController.variableModel()->variable(m_Index)) {
138 variableController.onRequestDataLoading({variable}, m_NewRange, !m_Shift);
138 variableController.onRequestDataLoading({variable}, m_NewRange, !m_Shift);
139 QTest::qWait(m_DelayMs);
139 QTest::qWait(m_DelayMs);
140 }
140 }
141 }
141 }
142
142
143 int m_Index; ///< The index of the variable to move
143 int m_Index; ///< The index of the variable to move
144 DateTimeRange m_NewRange; ///< The new range of the variable
144 DateTimeRange m_NewRange; ///< The new range of the variable
145 bool m_Shift; ///< Performs a shift (
145 bool m_Shift; ///< Performs a shift (
146 int m_DelayMs; ///< wait the delay after running the request (
146 int m_DelayMs; ///< wait the delay after running the request (
147 };
147 };
148
148
149 /**
149 /**
150 * Variable synchronization/desynchronization operation in the controller
150 * Variable synchronization/desynchronization operation in the controller
151 */
151 */
152 struct Synchronize : public IOperation {
152 struct Synchronize : public IOperation {
153 explicit Synchronize(int index, QUuid syncId, bool synchronize = true)
153 explicit Synchronize(int index, QUuid syncId, bool synchronize = true)
154 : m_Index{index}, m_SyncId{syncId}, m_Synchronize{synchronize}
154 : m_Index{index}, m_SyncId{syncId}, m_Synchronize{synchronize}
155 {
155 {
156 }
156 }
157
157
158 void exec(VariableController &variableController) const override
158 void exec(VariableController &variableController) const override
159 {
159 {
160 if (auto variable = variableController.variableModel()->variable(m_Index)) {
160 if (auto variable = variableController.variableModel()->variable(m_Index)) {
161 if (m_Synchronize) {
161 if (m_Synchronize) {
162 variableController.onAddSynchronized(variable, m_SyncId);
162 variableController.onAddSynchronized(variable, m_SyncId);
163 }
163 }
164 else {
164 else {
165 variableController.desynchronize(variable, m_SyncId);
165 variableController.desynchronize(variable, m_SyncId);
166 }
166 }
167 }
167 }
168 }
168 }
169
169
170 int m_Index; ///< The index of the variable to sync/desync
170 int m_Index; ///< The index of the variable to sync/desync
171 QUuid m_SyncId; ///< The synchronization group of the variable
171 QUuid m_SyncId; ///< The synchronization group of the variable
172 bool m_Synchronize; ///< Performs sync or desync operation
172 bool m_Synchronize; ///< Performs sync or desync operation
173 };
173 };
174
174
175 /**
175 /**
176 * Test Iteration
176 * Test Iteration
177 *
177 *
178 * A test iteration includes an operation to be performed, and a set of expected ranges after each
178 * A test iteration includes an operation to be performed, and a set of expected ranges after each
179 * operation. Each range is tested after the operation to ensure that:
179 * operation. Each range is tested after the operation to ensure that:
180 * - the range of the variable is the expected range
180 * - the range of the variable is the expected range
181 * - the data of the variable are those generated for the expected range
181 * - the data of the variable are those generated for the expected range
182 */
182 */
183 struct Iteration {
183 struct Iteration {
184 std::shared_ptr<IOperation> m_Operation; ///< Operation to perform
184 std::shared_ptr<IOperation> m_Operation; ///< Operation to perform
185 std::map<int, DateTimeRange> m_ExpectedRanges; ///< Expected ranges (by variable index)
185 std::map<int, DateTimeRange> m_ExpectedRanges; ///< Expected ranges (by variable index)
186 };
186 };
187
187
188 using Iterations = std::vector<Iteration>;
188 using Iterations = std::vector<Iteration>;
189
189
190 } // namespace
190 } // namespace
191
191
192 Q_DECLARE_METATYPE(Iterations)
192 Q_DECLARE_METATYPE(Iterations)
193
193
194 class TestVariableSync : public QObject {
194 class TestVariableSync : public QObject {
195 Q_OBJECT
195 Q_OBJECT
196
196
197 private slots:
197 private slots:
198 void initTestCase() { QSKIP("Temporarily disables TestVariableSync"); }
198 void initTestCase() { QSKIP("Temporarily disables TestVariableSync"); }
199
199
200 /// Input data for @sa testSync()
200 /// Input data for @sa testSync()
201 void testSync_data();
201 void testSync_data();
202
202
203 /// Input data for @sa testSyncOneVar()
203 /// Input data for @sa testSyncOneVar()
204 void testSyncOneVar_data();
204 void testSyncOneVar_data();
205
205
206 /// Tests synchronization between variables through several operations
206 /// Tests synchronization between variables through several operations
207 void testSync();
207 void testSync();
208
208
209 /// Tests synchronization between variables through several operations
209 /// Tests synchronization between variables through several operations
210 void testSyncOneVar();
210 void testSyncOneVar();
211 };
211 };
212
212
213 namespace {
213 namespace {
214
214
215 void testSyncCase1()
215 void testSyncCase1()
216 {
216 {
217 // Id used to synchronize variables in the controller
217 // Id used to synchronize variables in the controller
218 auto syncId = QUuid::createUuid();
218 auto syncId = QUuid::createUuid();
219
219
220 /// Generates a range according to a start time and a end time (the date is the same)
220 /// Generates a range according to a start time and a end time (the date is the same)
221 auto range = [](const QTime &startTime, const QTime &endTime) {
221 auto range = [](const QTime &startTime, const QTime &endTime) {
222 return DateTimeRange{DateUtils::secondsSinceEpoch(QDateTime{{2017, 1, 1}, startTime, Qt::UTC}),
222 return DateTimeRange{DateUtils::secondsSinceEpoch(QDateTime{{2017, 1, 1}, startTime, Qt::UTC}),
223 DateUtils::secondsSinceEpoch(QDateTime{{2017, 1, 1}, endTime, Qt::UTC})};
223 DateUtils::secondsSinceEpoch(QDateTime{{2017, 1, 1}, endTime, Qt::UTC})};
224 };
224 };
225
225
226 auto initialRange = range({12, 0}, {13, 0});
226 auto initialRange = range({12, 0}, {13, 0});
227
227
228 Iterations iterations{};
228 Iterations iterations{};
229 // Creates variables var0, var1 and var2
229 // Creates variables var0, var1 and var2
230 iterations.push_back({std::make_shared<Create>(0, initialRange), {{0, initialRange}}});
230 iterations.push_back({std::make_shared<Create>(0, initialRange), {{0, initialRange}}});
231 iterations.push_back({std::make_shared<Create>(1, initialRange), {{0, initialRange}, {1, initialRange}}});
231 iterations.push_back({std::make_shared<Create>(1, initialRange), {{0, initialRange}, {1, initialRange}}});
232 iterations.push_back(
232 iterations.push_back(
233 {std::make_shared<Create>(2, initialRange), {{0, initialRange}, {1, initialRange}, {2, initialRange}}});
233 {std::make_shared<Create>(2, initialRange), {{0, initialRange}, {1, initialRange}, {2, initialRange}}});
234
234
235 // Adds variables into the sync group (ranges don't need to be tested here)
235 // Adds variables into the sync group (ranges don't need to be tested here)
236 iterations.push_back({std::make_shared<Synchronize>(0, syncId)});
236 iterations.push_back({std::make_shared<Synchronize>(0, syncId)});
237 iterations.push_back({std::make_shared<Synchronize>(1, syncId)});
237 iterations.push_back({std::make_shared<Synchronize>(1, syncId)});
238 iterations.push_back({std::make_shared<Synchronize>(2, syncId)});
238 iterations.push_back({std::make_shared<Synchronize>(2, syncId)});
239
239
240 // Moves var0: ranges of var0, var1 and var2 change
240 // Moves var0: ranges of var0, var1 and var2 change
241 auto newRange = range({12, 30}, {13, 30});
241 auto newRange = range({12, 30}, {13, 30});
242 iterations.push_back(
242 iterations.push_back(
243 {std::make_shared<Move>(0, newRange), {{0, newRange}, {1, newRange}, {2, newRange}}});
243 {std::make_shared<Move>(0, newRange), {{0, newRange}, {1, newRange}, {2, newRange}}});
244
244
245 // Moves var1: ranges of var0, var1 and var2 change
245 // Moves var1: ranges of var0, var1 and var2 change
246 newRange = range({13, 0}, {14, 0});
246 newRange = range({13, 0}, {14, 0});
247 iterations.push_back(
247 iterations.push_back(
248 {std::make_shared<Move>(0, newRange), {{0, newRange}, {1, newRange}, {2, newRange}}});
248 {std::make_shared<Move>(0, newRange), {{0, newRange}, {1, newRange}, {2, newRange}}});
249
249
250 // Moves var2: ranges of var0, var1 and var2 change
250 // Moves var2: ranges of var0, var1 and var2 change
251 newRange = range({13, 30}, {14, 30});
251 newRange = range({13, 30}, {14, 30});
252 iterations.push_back(
252 iterations.push_back(
253 {std::make_shared<Move>(0, newRange), {{0, newRange}, {1, newRange}, {2, newRange}}});
253 {std::make_shared<Move>(0, newRange), {{0, newRange}, {1, newRange}, {2, newRange}}});
254
254
255 // Desyncs var2 and moves var0:
255 // Desyncs var2 and moves var0:
256 // - ranges of var0 and var1 change
256 // - ranges of var0 and var1 change
257 // - range of var2 doesn't change anymore
257 // - range of var2 doesn't change anymore
258 auto var2Range = newRange;
258 auto var2Range = newRange;
259 newRange = range({13, 45}, {14, 45});
259 newRange = range({13, 45}, {14, 45});
260 iterations.push_back({std::make_shared<Synchronize>(2, syncId, false)});
260 iterations.push_back({std::make_shared<Synchronize>(2, syncId, false)});
261 iterations.push_back(
261 iterations.push_back(
262 {std::make_shared<Move>(0, newRange), {{0, newRange}, {1, newRange}, {2, var2Range}}});
262 {std::make_shared<Move>(0, newRange), {{0, newRange}, {1, newRange}, {2, var2Range}}});
263
263
264 // Shifts var0: although var1 is synchronized with var0, its range doesn't change
264 // Shifts var0: although var1 is synchronized with var0, its range doesn't change
265 auto var1Range = newRange;
265 auto var1Range = newRange;
266 newRange = range({14, 45}, {15, 45});
266 newRange = range({14, 45}, {15, 45});
267 iterations.push_back({std::make_shared<Move>(0, newRange, true),
267 iterations.push_back({std::make_shared<Move>(0, newRange, true),
268 {{0, newRange}, {1, var1Range}, {2, var2Range}}});
268 {{0, newRange}, {1, var1Range}, {2, var2Range}}});
269
269
270 // Moves var0 through several operations:
270 // Moves var0 through several operations:
271 // - range of var0 changes
271 // - range of var0 changes
272 // - range or var1 changes according to the previous shift (one hour)
272 // - range or var1 changes according to the previous shift (one hour)
273 auto moveVar0 = [&iterations](const auto &var0NewRange, const auto &var1ExpectedRange) {
273 auto moveVar0 = [&iterations](const auto &var0NewRange, const auto &var1ExpectedRange) {
274 iterations.push_back(
274 iterations.push_back(
275 {std::make_shared<Move>(0, var0NewRange), {{0, var0NewRange}, {1, var1ExpectedRange}}});
275 {std::make_shared<Move>(0, var0NewRange), {{0, var0NewRange}, {1, var1ExpectedRange}}});
276 };
276 };
277
277
278 // Pan left
278 // Pan left
279 moveVar0(range({14, 30}, {15, 30}), range({13, 30}, {14, 30}));
279 moveVar0(range({14, 30}, {15, 30}), range({13, 30}, {14, 30}));
280 // Pan right
280 // Pan right
281 moveVar0(range({16, 0}, {17, 0}), range({15, 0}, {16, 0}));
281 moveVar0(range({16, 0}, {17, 0}), range({15, 0}, {16, 0}));
282 // Zoom in
282 // Zoom in
283 moveVar0(range({16, 30}, {16, 45}), range({15, 30}, {15, 45}));
283 moveVar0(range({16, 30}, {16, 45}), range({15, 30}, {15, 45}));
284 // Zoom out
284 // Zoom out
285 moveVar0(range({16, 15}, {17, 0}), range({15, 15}, {16, 0}));
285 moveVar0(range({16, 15}, {17, 0}), range({15, 15}, {16, 0}));
286
286
287 QTest::newRow("sync1") << syncId << initialRange << std::move(iterations) << 200;
287 QTest::newRow("sync1") << syncId << initialRange << std::move(iterations) << 200;
288 }
288 }
289
289
290 void testSyncCase2()
290 void testSyncCase2()
291 {
291 {
292 // Id used to synchronize variables in the controller
292 // Id used to synchronize variables in the controller
293 auto syncId = QUuid::createUuid();
293 auto syncId = QUuid::createUuid();
294
294
295 /// Generates a range according to a start time and a end time (the date is the same)
295 /// Generates a range according to a start time and a end time (the date is the same)
296 auto dateTime = [](int year, int month, int day, int hours, int minutes, int seconds) {
296 auto dateTime = [](int year, int month, int day, int hours, int minutes, int seconds) {
297 return DateUtils::secondsSinceEpoch(
297 return DateUtils::secondsSinceEpoch(
298 QDateTime{{year, month, day}, QTime{hours, minutes, seconds}, Qt::UTC});
298 QDateTime{{year, month, day}, QTime{hours, minutes, seconds}, Qt::UTC});
299 };
299 };
300
300
301 auto initialRange = DateTimeRange{dateTime(2017, 1, 1, 12, 0, 0), dateTime(2017, 1, 1, 13, 0, 0)};
301 auto initialRange = DateTimeRange{dateTime(2017, 1, 1, 12, 0, 0), dateTime(2017, 1, 1, 13, 0, 0)};
302
302
303 Iterations iterations{};
303 Iterations iterations{};
304 // Creates variables var0 and var1
304 // Creates variables var0 and var1
305 iterations.push_back({std::make_shared<Create>(0, initialRange), {{0, initialRange}}});
305 iterations.push_back({std::make_shared<Create>(0, initialRange), {{0, initialRange}}});
306 iterations.push_back({std::make_shared<Create>(1, initialRange), {{0, initialRange}, {1, initialRange}}});
306 iterations.push_back({std::make_shared<Create>(1, initialRange), {{0, initialRange}, {1, initialRange}}});
307
307
308 // Adds variables into the sync group (ranges don't need to be tested here)
308 // Adds variables into the sync group (ranges don't need to be tested here)
309 iterations.push_back({std::make_shared<Synchronize>(0, syncId)});
309 iterations.push_back({std::make_shared<Synchronize>(0, syncId)});
310 iterations.push_back({std::make_shared<Synchronize>(1, syncId)});
310 iterations.push_back({std::make_shared<Synchronize>(1, syncId)});
311
311
312
312
313 // Moves var0 through several operations:
313 // Moves var0 through several operations:
314 // - range of var0 changes
314 // - range of var0 changes
315 // - range or var1 changes according to the previous shift (one hour)
315 // - range or var1 changes according to the previous shift (one hour)
316 auto moveVar0 = [&iterations](const auto &var0NewRange) {
316 auto moveVar0 = [&iterations](const auto &var0NewRange) {
317 iterations.push_back(
317 iterations.push_back(
318 {std::make_shared<Move>(0, var0NewRange), {{0, var0NewRange}, {1, var0NewRange}}});
318 {std::make_shared<Move>(0, var0NewRange), {{0, var0NewRange}, {1, var0NewRange}}});
319 };
319 };
320 moveVar0(DateTimeRange{dateTime(2017, 1, 1, 12, 0, 0), dateTime(2017, 1, 1, 13, 0, 0)});
320 moveVar0(DateTimeRange{dateTime(2017, 1, 1, 12, 0, 0), dateTime(2017, 1, 1, 13, 0, 0)});
321 moveVar0(DateTimeRange{dateTime(2017, 1, 1, 14, 0, 0), dateTime(2017, 1, 1, 15, 0, 0)});
321 moveVar0(DateTimeRange{dateTime(2017, 1, 1, 14, 0, 0), dateTime(2017, 1, 1, 15, 0, 0)});
322 moveVar0(DateTimeRange{dateTime(2017, 1, 1, 8, 0, 0), dateTime(2017, 1, 1, 9, 0, 0)});
322 moveVar0(DateTimeRange{dateTime(2017, 1, 1, 8, 0, 0), dateTime(2017, 1, 1, 9, 0, 0)});
323 // moveVar0(SqpRange{dateTime(2017, 1, 1, 7, 30, 0), dateTime(2017, 1, 1, 9, 30, 0)});
323 // moveVar0(SqpRange{dateTime(2017, 1, 1, 7, 30, 0), dateTime(2017, 1, 1, 9, 30, 0)});
324 moveVar0(DateTimeRange{dateTime(2017, 1, 1, 2, 0, 0), dateTime(2017, 1, 1, 4, 0, 0)});
324 moveVar0(DateTimeRange{dateTime(2017, 1, 1, 2, 0, 0), dateTime(2017, 1, 1, 4, 0, 0)});
325 moveVar0(DateTimeRange{dateTime(2017, 1, 1, 6, 0, 0), dateTime(2017, 1, 1, 8, 0, 0)});
325 moveVar0(DateTimeRange{dateTime(2017, 1, 1, 6, 0, 0), dateTime(2017, 1, 1, 8, 0, 0)});
326
326
327 moveVar0(DateTimeRange{dateTime(2017, 1, 10, 6, 0, 0), dateTime(2017, 1, 15, 8, 0, 0)});
327 moveVar0(DateTimeRange{dateTime(2017, 1, 10, 6, 0, 0), dateTime(2017, 1, 15, 8, 0, 0)});
328 moveVar0(DateTimeRange{dateTime(2017, 1, 17, 6, 0, 0), dateTime(2017, 1, 25, 8, 0, 0)});
328 moveVar0(DateTimeRange{dateTime(2017, 1, 17, 6, 0, 0), dateTime(2017, 1, 25, 8, 0, 0)});
329 moveVar0(DateTimeRange{dateTime(2017, 1, 2, 6, 0, 0), dateTime(2017, 1, 8, 8, 0, 0)});
329 moveVar0(DateTimeRange{dateTime(2017, 1, 2, 6, 0, 0), dateTime(2017, 1, 8, 8, 0, 0)});
330
330
331 moveVar0(DateTimeRange{dateTime(2017, 4, 10, 6, 0, 0), dateTime(2017, 6, 15, 8, 0, 0)});
331 moveVar0(DateTimeRange{dateTime(2017, 4, 10, 6, 0, 0), dateTime(2017, 6, 15, 8, 0, 0)});
332 moveVar0(DateTimeRange{dateTime(2017, 1, 17, 6, 0, 0), dateTime(2017, 2, 25, 8, 0, 0)});
332 moveVar0(DateTimeRange{dateTime(2017, 1, 17, 6, 0, 0), dateTime(2017, 2, 25, 8, 0, 0)});
333 moveVar0(DateTimeRange{dateTime(2017, 7, 2, 6, 0, 0), dateTime(2017, 10, 8, 8, 0, 0)});
333 moveVar0(DateTimeRange{dateTime(2017, 7, 2, 6, 0, 0), dateTime(2017, 10, 8, 8, 0, 0)});
334 moveVar0(DateTimeRange{dateTime(2017, 4, 10, 6, 0, 0), dateTime(2017, 6, 15, 8, 0, 0)});
334 moveVar0(DateTimeRange{dateTime(2017, 4, 10, 6, 0, 0), dateTime(2017, 6, 15, 8, 0, 0)});
335 moveVar0(DateTimeRange{dateTime(2017, 1, 17, 6, 0, 0), dateTime(2017, 2, 25, 8, 0, 0)});
335 moveVar0(DateTimeRange{dateTime(2017, 1, 17, 6, 0, 0), dateTime(2017, 2, 25, 8, 0, 0)});
336 moveVar0(DateTimeRange{dateTime(2017, 7, 2, 6, 0, 0), dateTime(2017, 10, 8, 8, 0, 0)});
336 moveVar0(DateTimeRange{dateTime(2017, 7, 2, 6, 0, 0), dateTime(2017, 10, 8, 8, 0, 0)});
337 moveVar0(DateTimeRange{dateTime(2017, 4, 10, 6, 0, 0), dateTime(2017, 6, 15, 8, 0, 0)});
337 moveVar0(DateTimeRange{dateTime(2017, 4, 10, 6, 0, 0), dateTime(2017, 6, 15, 8, 0, 0)});
338 moveVar0(DateTimeRange{dateTime(2017, 1, 17, 6, 0, 0), dateTime(2017, 2, 25, 8, 0, 0)});
338 moveVar0(DateTimeRange{dateTime(2017, 1, 17, 6, 0, 0), dateTime(2017, 2, 25, 8, 0, 0)});
339 moveVar0(DateTimeRange{dateTime(2017, 7, 2, 6, 0, 0), dateTime(2017, 10, 8, 8, 0, 0)});
339 moveVar0(DateTimeRange{dateTime(2017, 7, 2, 6, 0, 0), dateTime(2017, 10, 8, 8, 0, 0)});
340 moveVar0(DateTimeRange{dateTime(2017, 4, 10, 6, 0, 0), dateTime(2017, 6, 15, 8, 0, 0)});
340 moveVar0(DateTimeRange{dateTime(2017, 4, 10, 6, 0, 0), dateTime(2017, 6, 15, 8, 0, 0)});
341 moveVar0(DateTimeRange{dateTime(2017, 1, 17, 6, 0, 0), dateTime(2017, 2, 25, 8, 0, 0)});
341 moveVar0(DateTimeRange{dateTime(2017, 1, 17, 6, 0, 0), dateTime(2017, 2, 25, 8, 0, 0)});
342 moveVar0(DateTimeRange{dateTime(2017, 7, 2, 6, 0, 0), dateTime(2017, 10, 8, 8, 0, 0)});
342 moveVar0(DateTimeRange{dateTime(2017, 7, 2, 6, 0, 0), dateTime(2017, 10, 8, 8, 0, 0)});
343
343
344
344
345 QTest::newRow("sync2") << syncId << initialRange << iterations << 4000;
345 QTest::newRow("sync2") << syncId << initialRange << iterations << 4000;
346 // QTest::newRow("sync3") << syncId << initialRange << iterations << 5000;
346 // QTest::newRow("sync3") << syncId << initialRange << iterations << 5000;
347 }
347 }
348
348
349 void testSyncOnVarCase1()
349 void testSyncOnVarCase1()
350 {
350 {
351 // Id used to synchronize variables in the controller
351 // Id used to synchronize variables in the controller
352 auto syncId = QUuid::createUuid();
352 auto syncId = QUuid::createUuid();
353
353
354 /// Generates a range according to a start time and a end time (the date is the same)
354 /// Generates a range according to a start time and a end time (the date is the same)
355 auto range = [](const QTime &startTime, const QTime &endTime) {
355 auto range = [](const QTime &startTime, const QTime &endTime) {
356 return DateTimeRange{DateUtils::secondsSinceEpoch(QDateTime{{2017, 1, 1}, startTime, Qt::UTC}),
356 return DateTimeRange{DateUtils::secondsSinceEpoch(QDateTime{{2017, 1, 1}, startTime, Qt::UTC}),
357 DateUtils::secondsSinceEpoch(QDateTime{{2017, 1, 1}, endTime, Qt::UTC})};
357 DateUtils::secondsSinceEpoch(QDateTime{{2017, 1, 1}, endTime, Qt::UTC})};
358 };
358 };
359
359
360 auto initialRange = range({12, 0}, {13, 0});
360 auto initialRange = range({12, 0}, {13, 0});
361
361
362 Iterations creations{};
362 Iterations creations{};
363 // Creates variables var0, var1 and var2
363 // Creates variables var0, var1 and var2
364 creations.push_back({std::make_shared<Create>(0, initialRange), {{0, initialRange}}});
364 creations.push_back({std::make_shared<Create>(0, initialRange), {{0, initialRange}}});
365
365
366 Iterations synchronization{};
366 Iterations synchronization{};
367 // Adds variables into the sync group (ranges don't need to be tested here)
367 // Adds variables into the sync group (ranges don't need to be tested here)
368 synchronization.push_back({std::make_shared<Synchronize>(0, syncId)});
368 synchronization.push_back({std::make_shared<Synchronize>(0, syncId)});
369
369
370 Iterations iterations{};
370 Iterations iterations{};
371
371
372 // Moves var0 through several operations
372 // Moves var0 through several operations
373 auto moveOp = [&iterations](const auto &requestedRange, const auto &expectedRange, auto delay) {
373 auto moveOp = [&iterations](const auto &requestedRange, const auto &expectedRange, auto delay) {
374 iterations.push_back(
374 iterations.push_back(
375 {std::make_shared<Move>(0, requestedRange, true, delay), {{0, expectedRange}}});
375 {std::make_shared<Move>(0, requestedRange, true, delay), {{0, expectedRange}}});
376 };
376 };
377
377
378 // we assume here 300 ms is enough to finsh a operation
378 // we assume here 300 ms is enough to finsh a operation
379 int delayToFinish = 300;
379 int delayToFinish = 300;
380 // jump to right, let's the operation time to finish
380 // jump to right, let's the operation time to finish
381 moveOp(range({14, 30}, {15, 30}), range({14, 30}, {15, 30}), delayToFinish);
381 moveOp(range({14, 30}, {15, 30}), range({14, 30}, {15, 30}), delayToFinish);
382 // pan to right, let's the operation time to finish
382 // pan to right, let's the operation time to finish
383 moveOp(range({14, 45}, {15, 45}), range({14, 45}, {15, 45}), delayToFinish);
383 moveOp(range({14, 45}, {15, 45}), range({14, 45}, {15, 45}), delayToFinish);
384 // jump to left, let's the operation time to finish
384 // jump to left, let's the operation time to finish
385 moveOp(range({03, 30}, {04, 30}), range({03, 30}, {04, 30}), delayToFinish);
385 moveOp(range({03, 30}, {04, 30}), range({03, 30}, {04, 30}), delayToFinish);
386 // Pan to left, let's the operation time to finish
386 // Pan to left, let's the operation time to finish
387 moveOp(range({03, 10}, {04, 10}), range({03, 10}, {04, 10}), delayToFinish);
387 moveOp(range({03, 10}, {04, 10}), range({03, 10}, {04, 10}), delayToFinish);
388 // Zoom in, let's the operation time to finish
388 // Zoom in, let's the operation time to finish
389 moveOp(range({03, 30}, {04, 00}), range({03, 30}, {04, 00}), delayToFinish);
389 moveOp(range({03, 30}, {04, 00}), range({03, 30}, {04, 00}), delayToFinish);
390 // Zoom out left, let's the operation time to finish
390 // Zoom out left, let's the operation time to finish
391 moveOp(range({01, 10}, {18, 10}), range({01, 10}, {18, 10}), delayToFinish);
391 moveOp(range({01, 10}, {18, 10}), range({01, 10}, {18, 10}), delayToFinish);
392 // Go back to initial range
392 // Go back to initial range
393 moveOp(initialRange, initialRange, delayToFinish);
393 moveOp(initialRange, initialRange, delayToFinish);
394
394
395
395
396 // jump to right, let's the operation time to finish
396 // jump to right, let's the operation time to finish
397 // moveOp(range({14, 30}, {15, 30}), initialRange, delayToFinish);
397 // moveOp(range({14, 30}, {15, 30}), initialRange, delayToFinish);
398 // Zoom out left, let's the operation time to finish
398 // Zoom out left, let's the operation time to finish
399 moveOp(range({01, 10}, {18, 10}), initialRange, delayToFinish);
399 moveOp(range({01, 10}, {18, 10}), initialRange, delayToFinish);
400 // Go back to initial range
400 // Go back to initial range
401 moveOp(initialRange, initialRange, 300);
401 moveOp(initialRange, initialRange, 300);
402
402
403 QTest::newRow("syncOnVarCase1") << syncId << initialRange << std::move(creations)
403 QTest::newRow("syncOnVarCase1") << syncId << initialRange << std::move(creations)
404 << std::move(iterations);
404 << std::move(iterations);
405 }
405 }
406 }
406 }
407
407
408 void TestVariableSync::testSync_data()
408 void TestVariableSync::testSync_data()
409 {
409 {
410 // ////////////// //
410 // ////////////// //
411 // Test structure //
411 // Test structure //
412 // ////////////// //
412 // ////////////// //
413
413
414 QTest::addColumn<QUuid>("syncId");
414 QTest::addColumn<QUuid>("syncId");
415 QTest::addColumn<DateTimeRange>("initialRange");
415 QTest::addColumn<DateTimeRange>("initialRange");
416 QTest::addColumn<Iterations>("iterations");
416 QTest::addColumn<Iterations>("iterations");
417 QTest::addColumn<int>("operationDelay");
417 QTest::addColumn<int>("operationDelay");
418
418
419 // ////////// //
419 // ////////// //
420 // Test cases //
420 // Test cases //
421 // ////////// //
421 // ////////// //
422
422
423 testSyncCase1();
423 testSyncCase1();
424 testSyncCase2();
424 testSyncCase2();
425 }
425 }
426
426
427 void TestVariableSync::testSyncOneVar_data()
427 void TestVariableSync::testSyncOneVar_data()
428 {
428 {
429 // ////////////// //
429 // ////////////// //
430 // Test structure //
430 // Test structure //
431 // ////////////// //
431 // ////////////// //
432
432
433 QTest::addColumn<QUuid>("syncId");
433 QTest::addColumn<QUuid>("syncId");
434 QTest::addColumn<DateTimeRange>("initialRange");
434 QTest::addColumn<DateTimeRange>("initialRange");
435 QTest::addColumn<Iterations>("creations");
435 QTest::addColumn<Iterations>("creations");
436 QTest::addColumn<Iterations>("iterations");
436 QTest::addColumn<Iterations>("iterations");
437
437
438 // ////////// //
438 // ////////// //
439 // Test cases //
439 // Test cases //
440 // ////////// //
440 // ////////// //
441
441
442 testSyncOnVarCase1();
442 testSyncOnVarCase1();
443 }
443 }
444
444
445 void TestVariableSync::testSync()
445 void TestVariableSync::testSync()
446 {
446 {
447 // Inits controllers
447 // Inits controllers
448 TimeController timeController{};
448 TimeController timeController{};
449 VariableController variableController{};
449 VariableController variableController{};
450 //variableController.setTimeController(&timeController);
450 //variableController.setTimeController(&timeController);
451
451
452 QFETCH(QUuid, syncId);
452 QFETCH(QUuid, syncId);
453 QFETCH(DateTimeRange, initialRange);
453 QFETCH(DateTimeRange, initialRange);
454 timeController.setDateTimeRange(initialRange);
454 timeController.setDateTimeRange(initialRange);
455
455
456 // Synchronization group used
456 // Synchronization group used
457 variableController.onAddSynchronizationGroupId(syncId);
457 variableController.onAddSynchronizationGroupId(syncId);
458
458
459 // For each iteration:
459 // For each iteration:
460 // - execute operation
460 // - execute operation
461 // - compare the variables' state to the expected states
461 // - compare the variables' state to the expected states
462 QFETCH(Iterations, iterations);
462 QFETCH(Iterations, iterations);
463 QFETCH(int, operationDelay);
463 QFETCH(int, operationDelay);
464 for (const auto &iteration : iterations) {
464 for (const auto &iteration : iterations) {
465 iteration.m_Operation->exec(variableController);
465 iteration.m_Operation->exec(variableController);
466 QTest::qWait(operationDelay);
466 QTest::qWait(operationDelay);
467
467
468 validateRanges(variableController, iteration.m_ExpectedRanges);
468 validateRanges(variableController, iteration.m_ExpectedRanges);
469 }
469 }
470 }
470 }
471
471
472 void TestVariableSync::testSyncOneVar()
472 void TestVariableSync::testSyncOneVar()
473 {
473 {
474 // Inits controllers
474 // Inits controllers
475 TimeController timeController{};
475 TimeController timeController{};
476 VariableController variableController{};
476 VariableController variableController{};
477 //variableController.setTimeController(&timeController);
477 //variableController.setTimeController(&timeController);
478
478
479 QFETCH(QUuid, syncId);
479 QFETCH(QUuid, syncId);
480 QFETCH(DateTimeRange, initialRange);
480 QFETCH(DateTimeRange, initialRange);
481 timeController.setDateTimeRange(initialRange);
481 timeController.setDateTimeRange(initialRange);
482
482
483 // Synchronization group used
483 // Synchronization group used
484 variableController.onAddSynchronizationGroupId(syncId);
484 variableController.onAddSynchronizationGroupId(syncId);
485
485
486 // For each iteration:
486 // For each iteration:
487 // - execute operation
487 // - execute operation
488 // - compare the variables' state to the expected states
488 // - compare the variables' state to the expected states
489 QFETCH(Iterations, iterations);
489 QFETCH(Iterations, iterations);
490 QFETCH(Iterations, creations);
490 QFETCH(Iterations, creations);
491
491
492 for (const auto &creation : creations) {
492 for (const auto &creation : creations) {
493 creation.m_Operation->exec(variableController);
493 creation.m_Operation->exec(variableController);
494 QTest::qWait(300);
494 QTest::qWait(300);
495 }
495 }
496
496
497 for (const auto &iteration : iterations) {
497 for (const auto &iteration : iterations) {
498 iteration.m_Operation->exec(variableController);
498 iteration.m_Operation->exec(variableController);
499 }
499 }
500
500
501 if (!iterations.empty()) {
501 if (!iterations.empty()) {
502 validateRanges(variableController, iterations.back().m_ExpectedRanges);
502 validateRanges(variableController, iterations.back().m_ExpectedRanges);
503 }
503 }
504 }
504 }
505
505
506 QTEST_MAIN(TestVariableSync)
506 QTEST_MAIN(TestVariableSync)
507
507
508 #include "TestVariableSync.moc"
508 #include "TestVariableSync.moc"
General Comments 0
You need to be logged in to leave comments. Login now