##// END OF EJS Templates
Switched to cpp_utils package...
jeandet -
r94:29637e951955
parent child
Show More
@@ -1,225 +1,225
1 1 #ifndef SCIQLOP_DATETIMERANGE_H
2 2 #define SCIQLOP_DATETIMERANGE_H
3 3
4 4 #include <Common/DateUtils.h>
5 5 #include <Common/MetaTypes.h>
6 #include <Common/Numeric.h>
6 #include <Numeric.h>
7 7 #include <QDebug>
8 8 #include <QObject>
9 9 #include <cmath>
10 10 #include <opaque/numeric_typedef.hpp>
11 11
12 12 template<typename T>
13 13 struct Seconds
14 14 : opaque::numeric_typedef<T, Seconds<T>>,
15 15 opaque::binop::multipliable<Seconds<T>, true, Seconds<T>, T, T>,
16 16 opaque::binop::dividable<Seconds<T>, true, Seconds<T>, T, T>,
17 17 opaque::binop::addable<Seconds<T>, true, Seconds<T>, T, T>,
18 18 opaque::binop::subtractable<Seconds<T>, true, Seconds<T>, T, T>
19 19
20 20 {
21 21 using base = opaque::numeric_typedef<T, Seconds<T>>;
22 22 using base::base;
23 23 operator T() const { return this->value; }
24 24 };
25 25
26 26 struct InvalidDateTimeRangeTransformation
27 27 {};
28 28
29 29 struct DateTimeRangeTransformation
30 30 {
31 31 double zoom;
32 32 Seconds<double> shift;
33 33 bool operator==(const DateTimeRangeTransformation& other) const
34 34 {
35 return SciQLop::numeric::almost_equal(zoom, other.zoom, 1) &&
36 SciQLop::numeric::almost_equal<double>(shift, other.shift, 1);
35 return cpp_utils::numeric::almost_equal(zoom, other.zoom, 1) &&
36 cpp_utils::numeric::almost_equal<double>(shift, other.shift, 1);
37 37 }
38 38 DateTimeRangeTransformation
39 39 merge(const DateTimeRangeTransformation& other) const
40 40 {
41 41 return DateTimeRangeTransformation{zoom * other.zoom, shift + other.shift};
42 42 }
43 43 };
44 44
45 45 /**
46 46 * @brief The SqpRange struct holds the information of time parameters
47 47 */
48 48 struct DateTimeRange
49 49 {
50 50 DateTimeRange() : m_TStart(std::nan("")), m_TEnd(std::nan("")) {}
51 51 DateTimeRange(double TStart, double TEnd) : m_TStart(TStart), m_TEnd(TEnd) {}
52 52 DateTimeRange(const std::pair<double, double>& range)
53 53 : m_TStart(range.first), m_TEnd(range.second)
54 54 {}
55 55 /// Creates SqpRange from dates and times
56 56 static DateTimeRange fromDateTime(const QDate& startDate,
57 57 const QTime& startTime,
58 58 const QDate& endDate, const QTime& endTime)
59 59 {
60 60 return {
61 61 DateUtils::secondsSinceEpoch(QDateTime{startDate, startTime, Qt::UTC}),
62 62 DateUtils::secondsSinceEpoch(QDateTime{endDate, endTime, Qt::UTC})};
63 63 }
64 64
65 65 static DateTimeRange fromDateTime(const QDateTime& start,
66 66 const QDateTime& end)
67 67 {
68 68 return {DateUtils::secondsSinceEpoch(start),
69 69 DateUtils::secondsSinceEpoch(end)};
70 70 }
71 71
72 72 /// Start time (UTC)
73 73 double m_TStart;
74 74 /// End time (UTC)
75 75 double m_TEnd;
76 76
77 77 Seconds<double> delta() const noexcept
78 78 {
79 79 return Seconds<double>{this->m_TEnd - this->m_TStart};
80 80 }
81 81
82 82 bool contains(const DateTimeRange& dateTime) const noexcept
83 83 {
84 84 return (m_TStart <= dateTime.m_TStart && m_TEnd >= dateTime.m_TEnd);
85 85 }
86 86
87 87 Seconds<double> center() const noexcept
88 88 {
89 89 return Seconds<double>((m_TStart + m_TEnd) / 2.);
90 90 }
91 91
92 92 bool intersect(const DateTimeRange& dateTime) const noexcept
93 93 {
94 94 return (m_TEnd >= dateTime.m_TStart && m_TStart <= dateTime.m_TEnd);
95 95 }
96 96
97 97 inline DateTimeRange transform(const DateTimeRangeTransformation& tr) const
98 98 noexcept;
99 99
100 100 bool operator==(const DateTimeRange& other) const
101 101 {
102 return SciQLop::numeric::almost_equal(m_TStart, other.m_TStart, 1) &&
103 SciQLop::numeric::almost_equal(m_TEnd, other.m_TEnd, 1);
102 return cpp_utils::numeric::almost_equal(m_TStart, other.m_TStart, 1) &&
103 cpp_utils::numeric::almost_equal(m_TEnd, other.m_TEnd, 1);
104 104 }
105 105
106 106 bool operator!=(const DateTimeRange& other) const
107 107 {
108 108 return !(*this == other);
109 109 }
110 110
111 111 void grow(double factor) noexcept
112 112 {
113 113 double grow_v{delta() * (factor - 1.) / 2.};
114 114 m_TStart -= grow_v;
115 115 m_TEnd += grow_v;
116 116 }
117 117
118 118 void shrink(double factor) noexcept
119 119 {
120 120 double shrink_v{this->delta() * (1. - factor) / 2.};
121 121 m_TStart += shrink_v;
122 122 m_TEnd -= shrink_v;
123 123 }
124 124
125 125 DateTimeRange& operator*=(double k)
126 126 {
127 127 this->grow(k);
128 128 return *this;
129 129 }
130 130
131 131 DateTimeRange& operator/=(double k)
132 132 {
133 133 this->shrink(k);
134 134 return *this;
135 135 }
136 136
137 137 // compute set difference
138 138 std::vector<DateTimeRange> operator-(const DateTimeRange& other) const
139 139 {
140 140 std::vector<DateTimeRange> result;
141 141 if(std::isnan(other.m_TStart) || std::isnan(other.m_TEnd) ||
142 142 !this->intersect(other))
143 143 { result.emplace_back(m_TStart, m_TEnd); }
144 144 else
145 145 {
146 146 if(this->m_TStart < other.m_TStart)
147 147 { result.emplace_back(this->m_TStart, other.m_TStart); }
148 148 if(this->m_TEnd > other.m_TEnd)
149 149 { result.emplace_back(other.m_TEnd, this->m_TEnd); }
150 150 }
151 151 return result;
152 152 }
153 153 };
154 154
155 155 template<class T> DateTimeRange& operator+=(DateTimeRange& r, Seconds<T> offset)
156 156 {
157 157 shift(r, offset);
158 158 return r;
159 159 }
160 160
161 161 template<class T> DateTimeRange& operator-=(DateTimeRange& r, Seconds<T> offset)
162 162 {
163 163 shift(r, -offset);
164 164 return r;
165 165 }
166 166
167 167 template<class T> void shift(DateTimeRange& r, Seconds<T> offset)
168 168 {
169 169 r.m_TEnd += static_cast<double>(offset);
170 170 r.m_TStart += static_cast<double>(offset);
171 171 }
172 172
173 173 inline DateTimeRange operator*(const DateTimeRange& r, double k)
174 174 {
175 175 DateTimeRange result{r};
176 176 result.grow(k);
177 177 return result;
178 178 }
179 179
180 180 inline DateTimeRange operator/(const DateTimeRange& r, double k)
181 181 {
182 182 DateTimeRange result{r};
183 183 result.shrink(k);
184 184 return result;
185 185 }
186 186
187 187 template<class T>
188 188 DateTimeRange operator+(const DateTimeRange& r, Seconds<T> offset)
189 189 {
190 190 DateTimeRange result{r};
191 191 shift(result, offset);
192 192 return result;
193 193 }
194 194
195 195 template<class T>
196 196 DateTimeRange operator-(const DateTimeRange& r, Seconds<T> offset)
197 197 {
198 198 DateTimeRange result{r};
199 199 shift(result, -offset);
200 200 return result;
201 201 }
202 202
203 203 const auto INVALID_RANGE =
204 204 DateTimeRange{std::numeric_limits<double>::quiet_NaN(),
205 205 std::numeric_limits<double>::quiet_NaN()};
206 206
207 207 inline QDebug operator<<(QDebug d, DateTimeRange obj)
208 208 {
209 209 auto tendDateTimeStart = DateUtils::dateTime(obj.m_TStart);
210 210 auto tendDateTimeEnd = DateUtils::dateTime(obj.m_TEnd);
211 211
212 212 d << "ts: " << tendDateTimeStart << " te: " << tendDateTimeEnd;
213 213 return d;
214 214 }
215 215
216 216 DateTimeRange
217 217 DateTimeRange::transform(const DateTimeRangeTransformation& tr) const noexcept
218 218 {
219 219 return DateTimeRange{*this} * tr.zoom + tr.shift;
220 220 }
221 221
222 222 // Required for using shared_ptr in signals/slots
223 223 SCIQLOP_REGISTER_META_TYPE(SQPRANGE_REGISTRY, DateTimeRange)
224 224
225 225 #endif // SCIQLOP_DATETIMERANGE_H
@@ -1,86 +1,86
1 1 #ifndef SCIQLOP_DATETIMERANGEHELPER_H
2 2 #define SCIQLOP_DATETIMERANGEHELPER_H
3 3
4 4 #include <optional>
5 5
6 6 #include <cmath>
7 7 #include <variant>
8 8 #include <QObject>
9 9
10 10 #include <QDebug>
11 11
12 12 #include <opaque/numeric_typedef.hpp>
13 13 #include <Common/DateUtils.h>
14 14 #include <Common/MetaTypes.h>
15 #include <Common/Numeric.h>
15 #include <Numeric.h>
16 16 #include <Data/DateTimeRange.h>
17 17
18 18 enum class TransformationType { ZoomOut, ZoomIn, PanRight, PanLeft, Unknown };
19 19
20 20 namespace DateTimeRangeHelper {
21 21
22 22
23 23 inline bool isnan(const DateTimeRange& range)
24 24 {
25 25 return std::isnan(range.m_TStart) && std::isnan(range.m_TEnd);
26 26 }
27 27
28 28 inline bool hasnan(const DateTimeRange& range)
29 29 {
30 30 return std::isnan(range.m_TStart) || std::isnan(range.m_TEnd);
31 31 }
32 32
33 33 inline bool isPureShift(const DateTimeRange& range1, const DateTimeRange& range2)
34 34 {
35 return SciQLop::numeric::almost_equal<double>(range1.delta(), range2.delta(), 1)
36 && !SciQLop::numeric::almost_equal(range1.m_TStart, range2.m_TStart, 1);
35 return cpp_utils::numeric::almost_equal<double>(range1.delta(), range2.delta(), 1)
36 && !cpp_utils::numeric::almost_equal(range1.m_TStart, range2.m_TStart, 1);
37 37 }
38 38
39 39 inline bool isPureZoom(const DateTimeRange& range1, const DateTimeRange& range2)
40 40 {
41 return !SciQLop::numeric::almost_equal<double>(range1.delta(),range2.delta(),1)&&
42 SciQLop::numeric::almost_equal<double>(range1.center(), range2.center(),1);
41 return !cpp_utils::numeric::almost_equal<double>(range1.delta(),range2.delta(),1)&&
42 cpp_utils::numeric::almost_equal<double>(range1.center(), range2.center(),1);
43 43 }
44 44
45 45
46 46 /**
47 47 * @brief computeTransformation such as range2 = zoom*range1 + shift
48 48 * @param range1
49 49 * @param range2
50 50 * @return trnaformation applied to range1 to get range2 or an object of type
51 51 * InvalidDateTimeRangeTransformation if the transformation has NaN or forbiden values
52 52 */
53 53 inline std::optional<DateTimeRangeTransformation>
54 54 computeTransformation(const DateTimeRange& range1, const DateTimeRange& range2)
55 55 {
56 56 std::optional<DateTimeRangeTransformation> transformation;
57 57 double zoom = range2.delta()/range1.delta();
58 58 Seconds<double> shift = range2.center() - (range1*zoom).center();
59 59 bool zoomValid = zoom!=0. && !std::isnan(zoom) && !std::isinf(zoom);
60 60 bool shiftValid = !std::isnan(shift.value) && !std::isinf(shift.value);
61 61 if(zoomValid && shiftValid)
62 62 transformation = DateTimeRangeTransformation{zoom, shift};
63 63 return transformation;
64 64 }
65 65
66 66 inline TransformationType getTransformationType(const DateTimeRange& range1, const DateTimeRange& range2)
67 67 {
68 68 auto transformation = computeTransformation (range1,range2);
69 69 if(transformation.has_value ())
70 70 {
71 if(SciQLop::numeric::almost_equal(transformation->zoom,1.))
71 if(cpp_utils::numeric::almost_equal(transformation->zoom,1.))
72 72 {
73 73 if(transformation->shift > 0.)
74 74 return TransformationType::PanRight;
75 75 return TransformationType::PanLeft;
76 76 }
77 77 if(transformation->zoom > 0.)
78 78 return TransformationType::ZoomOut;
79 79 return TransformationType::ZoomIn;
80 80 }
81 81 return TransformationType::Unknown;
82 82 }
83 83
84 84 }
85 85
86 86 #endif // SCIQLOP_DATETIMERANGEHELPER_H
@@ -1,60 +1,60
1 1 #ifndef SCIQLOP_IDATAPROVIDER_H
2 2 #define SCIQLOP_IDATAPROVIDER_H
3 3
4 4 #include "CoreGlobal.h"
5 5
6 6 #include <Common/MetaTypes.h>
7 #include <Common/deprecate.h>
7 #include <deprecate.h>
8 8 #include <Data/DateTimeRange.h>
9 9 #include <QObject>
10 10 #include <QUuid>
11 11 #include <TimeSeries.h>
12 12 #include <functional>
13 13 #include <memory>
14 14
15 15 class DataProviderParameters;
16 16 class IDataSeries;
17 17 DEPRECATE(class QNetworkReply; class QNetworkRequest;)
18 18
19 19 /**
20 20 * @brief The IDataProvider interface aims to declare a data provider.
21 21 *
22 22 * A data provider is an entity that generates data and returns it according to
23 23 * various parameters (time interval, product to retrieve the data, etc.) Since
24 24 * its client mihgt use it from different threads it has to be either stateless
25 25 * and/or thread safe
26 26 *
27 27 * @sa IDataSeries
28 28 */
29 29 class SCIQLOP_CORE_EXPORT IDataProvider : public QObject
30 30 {
31 31 Q_OBJECT
32 32
33 33 QUuid _id=QUuid::createUuid();
34 34
35 35 public:
36 36 virtual ~IDataProvider() noexcept = default;
37 37 // virtual std::shared_ptr<IDataProvider> clone() const = 0;
38 38
39 39 // Synchronous call -> asyncGetData may be written for asynchronous get
40 40 virtual TimeSeries::ITimeSerie*
41 41 getData(const DataProviderParameters& parameters) = 0;
42 42
43 43 QUuid id() const { return _id; }
44 44 QString name()
45 45 {
46 46 return QString("%1-%2").arg(this->metaObject()->className()).arg(id().toString());
47 47 }
48 48
49 49 signals:
50 50
51 51 void progress(QUuid requestID, double progress);
52 52 };
53 53
54 54 // Required for using shared_ptr in signals/slots
55 55 SCIQLOP_REGISTER_META_TYPE(IDATAPROVIDER_PTR_REGISTRY,
56 56 std::shared_ptr<IDataProvider>)
57 57 SCIQLOP_REGISTER_META_TYPE(IDATAPROVIDER_FUNCTION_REGISTRY,
58 58 std::function<void(QNetworkReply*, QUuid)>)
59 59
60 60 #endif // SCIQLOP_IDATAPROVIDER_H
@@ -1,189 +1,189
1 1 #ifndef SCIQLOP_DATASOURCEITEM_H
2 2 #define SCIQLOP_DATASOURCEITEM_H
3 3
4 4 #include "CoreGlobal.h"
5 5
6 6 #include <Common/spimpl.h>
7 #include <Common/trees.h>
7 #include <trees/algorithms.hpp>
8 8 #include <QVariant>
9 9 #include <QVector>
10 10 #include <iostream>
11 11 #include <iomanip>
12 12
13 13 class DataSourceItemAction;
14 14
15 15 /**
16 16 * Possible types of an item
17 17 */
18 18 enum class DataSourceItemType
19 19 {
20 20 NODE,
21 21 PRODUCT,
22 22 COMPONENT
23 23 };
24 24
25 25
26 26
27 27 /**
28 28 * @brief The DataSourceItem class aims to represent a structure element of a
29 29 * data source. A data source has a tree structure that is made up of a main
30 30 * DataSourceItem object (root) containing other DataSourceItem objects
31 31 * (children). For each DataSourceItem can be associated a set of data
32 32 * representing it.
33 33 */
34 34 class SCIQLOP_CORE_EXPORT DataSourceItem
35 35 {
36 36
37 37 public:
38 38 using iterator_type = decltype (std::begin(std::declval<std::vector<std::unique_ptr<DataSourceItem>>>()));
39 39 using const_iterator_type = decltype (std::cbegin(std::declval<std::vector<std::unique_ptr<DataSourceItem>>>()));
40 40 /// Key associated with the name of the item
41 41 static const QString NAME_DATA_KEY;
42 42 /// Key associated with the plugin of the item
43 43 static const QString PLUGIN_DATA_KEY;
44 44 /// Key associated with a unique id of the plugin
45 45 static const QString ID_DATA_KEY;
46 46
47 47 explicit DataSourceItem(DataSourceItemType type, const QString& name);
48 48 explicit DataSourceItem(DataSourceItemType type, QVariantHash data = {});
49 49
50 50 std::unique_ptr<DataSourceItem> clone() const;
51 51
52 52 /// @return the actions of the item as a vector
53 53 QVector<DataSourceItemAction*> actions() const noexcept;
54 54
55 55 /**
56 56 * Adds an action to the item. The item takes ownership of the action, and the
57 57 * action is automatically associated to the item
58 58 * @param action the action to add
59 59 */
60 60 void addAction(std::unique_ptr<DataSourceItemAction> action) noexcept;
61 61
62 62 /**
63 63 * Adds a child to the item. The item takes ownership of the child.
64 64 * @param child the child to add
65 65 */
66 66 void appendChild(std::unique_ptr<DataSourceItem> child) noexcept;
67 67
68 68 /**
69 69 * Returns the item's child associated to an index
70 70 * @param childIndex the index to search
71 71 * @return a pointer to the child if index is valid, nullptr otherwise
72 72 */
73 73 DataSourceItem* child(int childIndex) const noexcept;
74 74
75 75 int childCount() const noexcept;
76 76
77 77 /**
78 78 * Get the data associated to a key
79 79 * @param key the key to search
80 80 * @return the data found if key is valid, default QVariant otherwise
81 81 */
82 82 QVariant data(const QString& key) const noexcept;
83 83
84 84 /// Gets all data
85 85 QVariantHash data() const noexcept;
86 86
87 87 /**
88 88 * Merge in the item the source item passed as parameter.
89 89 *
90 90 * The merge is done by adding as child of the item the complete tree
91 91 * represented by the source item. If a part of the tree already exists in the
92 92 * item (based on the name of the nodes), it is merged by completing the
93 93 * existing tree by items "leaves" (products, components or nodes with no
94 94 * child).
95 95 *
96 96 * For example, with item representing the tree:
97 97 * R (root node)
98 98 * - N1 (node)
99 99 * -- N11 (node)
100 100 * --- P1 (product)
101 101 * --- P2 (product)
102 102 * - N2 (node)
103 103 *
104 104 * and the source item representing the tree:
105 105 * N1 (root node)
106 106 * - N11 (node)
107 107 * -- P3 (product)
108 108 * - N12 (node)
109 109 *
110 110 * The leaves of the source item to merge into the item are N1/N11/P3 and
111 111 * N1/N12 => we therefore have the following merge result:
112 112 * R
113 113 * - N1
114 114 * -- N11
115 115 * --- P1
116 116 * --- P2
117 117 * --- P3 (added leaf)
118 118 * -- N12 (added leaf)
119 119 *
120 120 * @param item the source item
121 121 * @remarks No control is performed on products or components that are merged
122 122 * into the same tree part (two products or components may have the same name)
123 123 * @remarks the merge is made by copy (source item is not changed and still
124 124 * exists after the operation)
125 125 */
126 126 void merge(const DataSourceItem& item);
127 127
128 128 bool isRoot() const noexcept;
129 129
130 130 QString name() const noexcept;
131 131
132 132 /**
133 133 * Get the item's parent
134 134 * @return a pointer to the parent if it exists, nullptr if the item is a root
135 135 */
136 136 DataSourceItem* parentItem() const noexcept;
137 137
138 138 /**
139 139 * Gets the item's root
140 140 * @return the top parent, the item itself if it's the root item
141 141 */
142 142 const DataSourceItem& rootItem() const noexcept;
143 143
144 144 /**
145 145 * Sets or appends a value to a key
146 146 * @param key the key
147 147 * @param value the value
148 148 * @param append if true, the value is added to the values already existing
149 149 * for the key, otherwise it replaces the existing values
150 150 */
151 151 void setData(const QString& key, const QVariant& value,
152 152 bool append = false) noexcept;
153 153
154 154 DataSourceItemType type() const noexcept;
155 155
156 156 /**
157 157 * @brief Searches the first child matching the specified data.
158 158 * @param data The data to search.
159 159 * @param recursive So the search recursively.
160 160 * @return the item matching the data or nullptr if it was not found.
161 161 */
162 162 DataSourceItem* findItem(const QVariantHash& data, bool recursive);
163 163
164 164 DataSourceItem* findItem(const QString& name);
165 165
166 166 /**
167 167 * @brief Searches the first child matching the specified \p ID_DATA_KEY in
168 168 * its metadata.
169 169 * @param id The id to search.
170 170 * @param recursive So the search recursively.
171 171 * @return the item matching the data or nullptr if it was not found.
172 172 */
173 173 DataSourceItem* findItem(const QString& datasourceIdKey, bool recursive);
174 174
175 175 bool operator==(const DataSourceItem& other);
176 176 bool operator!=(const DataSourceItem& other);
177 177
178 178 iterator_type begin() noexcept;
179 179 iterator_type end() noexcept;
180 180 const_iterator_type begin()const noexcept;
181 181 const_iterator_type end()const noexcept;
182 182 const_iterator_type cbegin()const noexcept;
183 183 const_iterator_type cend()const noexcept;
184 184 private:
185 185 class DataSourceItemPrivate;
186 186 spimpl::unique_impl_ptr<DataSourceItemPrivate> impl;
187 187 };
188 188
189 189 #endif // SCIQLOP_DATASOURCEITEMMODEL_H
@@ -1,98 +1,98
1 1 #ifndef SCIQLOP_VARIABLE2_H
2 2 #define SCIQLOP_VARIABLE2_H
3 3
4 4 #include "CoreGlobal.h"
5 5
6 6 #include <Common/MetaTypes.h>
7 #include <Common/deprecate.h>
7 #include <deprecate.h>
8 8 #include <Common/spimpl.h>
9 9 #include <Data/DataSeriesType.h>
10 10 #include <Data/DateTimeRange.h>
11 11 #include <Data/MultiComponentTimeSerie.h>
12 12 #include <Data/ScalarTimeSerie.h>
13 13 #include <Data/SpectrogramTimeSerie.h>
14 14 #include <Data/VectorTimeSerie.h>
15 15 #include <QDataStream>
16 16 #include <QObject>
17 17 #include <QReadWriteLock>
18 18 #include <QUuid>
19 19 #include <TimeSeries.h>
20 20 #include <optional>
21 21
22 22 class SCIQLOP_CORE_EXPORT Variable2 : public QObject
23 23 {
24 24 Q_OBJECT
25 25
26 26 public:
27 27 explicit Variable2(const QString& name, const QVariantHash& metadata = {});
28 28
29 29 /// Copy ctor
30 30 explicit Variable2(const Variable2& other);
31 31
32 32 std::shared_ptr<Variable2> clone() const;
33 33
34 34 QString name();
35 35 void setName(const QString& name);
36 36 DateTimeRange range();
37 37 void setRange(const DateTimeRange& range);
38 38 std::optional<DateTimeRange> realRange();
39 39
40 40 std::size_t nbPoints();
41 41
42 42 /// @return the data of the variable, nullptr if there is no data
43 43 std::shared_ptr<TimeSeries::ITimeSerie> data();
44 44
45 45 /// @return the type of data that the variable holds
46 46 DataSeriesType type();
47 47
48 48 QVariantHash metadata() const noexcept;
49 49
50 50 // void setData(const std::vector<AnyTimeSerie*>& dataSeries,
51 51 // const DateTimeRange& range, bool notify = true);
52 52
53 53 void setData(const std::vector<TimeSeries::ITimeSerie*>& dataSeries,
54 54 const DateTimeRange& range, bool notify = true);
55 55
56 56 static QByteArray
57 57 mimeData(const std::vector<std::shared_ptr<Variable2>>& variables)
58 58 {
59 59 auto encodedData = QByteArray{};
60 60 QDataStream stream{&encodedData, QIODevice::WriteOnly};
61 61 for(auto& var : variables)
62 62 {
63 63 stream << var->ID().toByteArray();
64 64 }
65 65 return encodedData;
66 66 }
67 67
68 68 static std::vector<QUuid> IDs(QByteArray mimeData)
69 69 {
70 70 std::vector<QUuid> variables;
71 71 QDataStream stream{mimeData};
72 72
73 73 QVariantList ids;
74 74 stream >> ids;
75 75 std::transform(std::cbegin(ids), std::cend(ids),
76 76 std::back_inserter(variables),
77 77 [](const auto& id) { return id.toByteArray(); });
78 78 return variables;
79 79 }
80 80
81 81 operator QUuid() { return _uuid; }
82 82 QUuid ID() { return _uuid; }
83 83 signals:
84 84 void updated(QUuid ID);
85 85
86 86 private:
87 87 struct VariablePrivate;
88 88 spimpl::unique_impl_ptr<VariablePrivate> impl;
89 89 QUuid _uuid;
90 90 QReadWriteLock m_lock;
91 91 };
92 92
93 93 // Required for using shared_ptr in signals/slots
94 94 SCIQLOP_REGISTER_META_TYPE(VARIABLE_PTR_REGISTRY, std::shared_ptr<Variable2>)
95 95 SCIQLOP_REGISTER_META_TYPE(VARIABLE_PTR_VECTOR_REGISTRY,
96 96 QVector<std::shared_ptr<Variable2>>)
97 97
98 98 #endif // SCIQLOP_VARIABLE2_H
@@ -1,80 +1,80
1 1 #ifndef SCIQLOP_VARIABLESYNCHRONIZATIONGROUP2_H
2 2 #define SCIQLOP_VARIABLESYNCHRONIZATIONGROUP2_H
3 3
4 4 #include <QUuid>
5 5 #include <set>
6 6
7 7 #include "CoreGlobal.h"
8 8 #include <Common/spimpl.h>
9 #include <Common/containers.h>
9 #include <containers/algorithms.hpp>
10 10
11 11 /**
12 12 * @brief The VariableSynchronizationGroup2 class holds a list of Variables uuid which are synchronized
13 13 * @note This class is part of SciQLop internals, as a normal user you shouldn't have to care about it
14 14 */
15 15 class SCIQLOP_CORE_EXPORT VariableSynchronizationGroup2
16 16 {
17 17
18 18 public:
19 19 explicit VariableSynchronizationGroup2()=default;
20 20 /**
21 21 * @brief VariableSynchronizationGroup2 is a convenience ctor to build a group with a default variable
22 22 * @param variable
23 23 */
24 24 explicit VariableSynchronizationGroup2(QUuid variable)
25 25 :_variables{{variable}}
26 26 {}
27 27
28 28 /**
29 29 * @brief addVariable adds the given variable to the group, does nothing if the varaible is alredy in the group
30 30 * @param variable
31 31 * @sa removeVariable
32 32 */
33 33 void addVariable(QUuid variable) noexcept
34 34 {
35 35 this->_variables.insert(variable);
36 36 }
37 37
38 38 /**
39 39 * @brief removeVariable removes the given variable from the group, does nothing if the varaible is not in the group
40 40 * @param variable
41 41 * @sa addVariable
42 42 */
43 43 void removeVariable(QUuid variable) noexcept
44 44 {
45 45 this->_variables.erase(variable);
46 46 }
47 47
48 48 /**
49 49 * @brief contains checks if the given variable is in the group
50 50 * @param variable
51 51 * @return true if the variable is in the group
52 52 */
53 53 bool contains(QUuid variable) const noexcept
54 54 {
55 return SciQLop::containers::contains(this->_variables,variable);
55 return cpp_utils::containers::contains(this->_variables,variable);
56 56 }
57 57
58 58 /**
59 59 * @brief variables
60 60 * @return the list of synchronized variables in this group as a std::set
61 61 */
62 62 const std::set<QUuid> &variables() const noexcept
63 63 {
64 64 return this->_variables;
65 65 }
66 66
67 67 inline bool isEmpty()
68 68 {
69 69 return _variables.size()==0;
70 70 }
71 71
72 72 inline QUuid ID(){return _ID;}
73 73
74 74 operator QUuid() {return _ID;}
75 75 private:
76 76 std::set<QUuid> _variables;
77 77 QUuid _ID = QUuid::createUuid();
78 78 };
79 79
80 80 #endif // SCIQLOP_VARIABLESYNCHRONIZATIONGROUP2_H
@@ -1,149 +1,149
1 1 #pragma once
2 2 #include "Variable/VariableSynchronizationGroup2.h"
3 3
4 #include <Common/containers.h>
4 #include <containers/algorithms.hpp>
5 5 #include <Common/debug.h>
6 6 #include <Data/DataProviderParameters.h>
7 7 #include <Data/DateTimeRange.h>
8 8 #include <Data/DateTimeRangeHelper.h>
9 9 #include <Data/IDataProvider.h>
10 10 #include <QObject>
11 11 #include <QReadWriteLock>
12 12 #include <QRunnable>
13 13 #include <QThreadPool>
14 14 #include <TimeSeries.h>
15 15 #include <Variable/Variable2.h>
16 16
17 17 struct VCTransaction
18 18 {
19 19 VCTransaction(QUuid refVar, DateTimeRange range, int varCount)
20 20 : refVar{refVar}, range{range}, _remainingVars{varCount}
21 21 {}
22 22
23 23 QUuid refVar;
24 24 DateTimeRange range;
25 25 bool ready()
26 26 {
27 27 QReadLocker lock{&_lock};
28 28 return _remainingVars == 0;
29 29 }
30 30
31 31 bool done()
32 32 {
33 33 QWriteLocker lock{&_lock};
34 34 _remainingVars -= 1;
35 35 return _remainingVars == 0;
36 36 }
37 37
38 38 private:
39 39 QReadWriteLock _lock;
40 40 int _remainingVars;
41 41 };
42 42
43 43 class TransactionExe : public QObject, public QRunnable
44 44 {
45 45 Q_OBJECT
46 46 std::shared_ptr<Variable2> _variable;
47 47 std::shared_ptr<IDataProvider> _provider;
48 48 std::vector<DateTimeRange> _ranges;
49 49 DateTimeRange _range;
50 50 bool _overwrite;
51 51
52 52 public:
53 53 TransactionExe(const std::shared_ptr<Variable2>& variable,
54 54 const std::shared_ptr<IDataProvider>& provider,
55 55 const std::vector<DateTimeRange>& ranges, DateTimeRange range,
56 56 bool overwrite = true)
57 57 : _variable{variable}, _provider{provider}, _ranges{ranges},
58 58 _range{range}, _overwrite{overwrite}
59 59 {
60 60 setAutoDelete(true);
61 61 }
62 62 void run() override
63 63 {
64 64 std::vector<TimeSeries::ITimeSerie*> data;
65 65 for(auto range : _ranges)
66 66 {
67 67 auto ds = _provider->getData(
68 68 DataProviderParameters{{range}, _variable->metadata()});
69 69 if(ds and ds->size()) data.push_back(ds); // skip empty dataSeries
70 70 }
71 71 if(_overwrite)
72 72 _variable->setData(data, _range, true);
73 73 else
74 74 {
75 75 data.push_back(_variable->data().get());
76 76 _variable->setData(data, _range, true);
77 77 }
78 78 std::for_each(std::begin(data), std::end(data),
79 79 [](TimeSeries::ITimeSerie* ts) { delete ts; });
80 80 emit transactionComplete();
81 81 }
82 82 signals:
83 83 void transactionComplete();
84 84 };
85 85
86 86 class VCTransactionsQueues
87 87 {
88 88 QReadWriteLock _mutex{QReadWriteLock::Recursive};
89 89 std::map<QUuid, std::optional<std::shared_ptr<VCTransaction>>>
90 90 _nextTransactions;
91 91 std::map<QUuid, std::optional<std::shared_ptr<VCTransaction>>>
92 92 _pendingTransactions;
93 93
94 94 public:
95 95 void addEntry(QUuid id)
96 96 {
97 97 QWriteLocker lock{&_mutex};
98 98 _nextTransactions[id] = std::nullopt;
99 99 _pendingTransactions[id] = std::nullopt;
100 100 }
101 101
102 102 void removeEntry(QUuid id)
103 103 {
104 104 QWriteLocker lock{&_mutex};
105 105 _nextTransactions.erase(id);
106 106 _pendingTransactions.erase(id);
107 107 }
108 108
109 109 std::map<QUuid, std::optional<std::shared_ptr<VCTransaction>>>
110 110 pendingTransactions()
111 111 {
112 112 QReadLocker lock{&_mutex};
113 113 return _pendingTransactions;
114 114 }
115 115
116 116 std::map<QUuid, std::optional<std::shared_ptr<VCTransaction>>>
117 117 nextTransactions()
118 118 {
119 119 QReadLocker lock{&_mutex};
120 120 return _nextTransactions;
121 121 }
122 122
123 123 std::optional<std::shared_ptr<VCTransaction>> start(QUuid id)
124 124 {
125 125 QWriteLocker lock{&_mutex};
126 126 _pendingTransactions[id] = _nextTransactions[id];
127 127 _nextTransactions[id] = std::nullopt;
128 128 return _pendingTransactions[id];
129 129 }
130 130
131 131 void enqueue(QUuid id, std::shared_ptr<VCTransaction> transaction)
132 132 {
133 133 QWriteLocker lock{&_mutex};
134 134 _nextTransactions[id] = transaction;
135 135 }
136 136
137 137 void complete(QUuid id)
138 138 {
139 139 QWriteLocker lock{&_mutex};
140 140 _pendingTransactions[id] = std::nullopt;
141 141 }
142 142
143 143 bool active(QUuid id)
144 144 {
145 145 QReadLocker lock{&_mutex};
146 146 return _nextTransactions[id].has_value() ||
147 147 _pendingTransactions[id].has_value();
148 148 }
149 149 };
@@ -1,111 +1,106
1 1
2 2 catalogicpp_dep = dependency('catalogicpp', required : true, fallback:['catalogicpp','catalogicpp_dep'])
3 3 pybind11_dep = dependency('pybind11', required : true, fallback:['pybind11','pybind11_dep'])
4 4 timeseries_dep = dependency('TimeSeries', required : true, fallback:['TimeSeries','time_series_dep'])
5 cpp_utils_dep = dependency('cpp_utils', fallback:['cpp_utils','cpp_utils_dep'])
5 6
6 7 core_moc_headers = [
7 './include/Common/containers.h',
8 './include/Common/StringUtils.h',
9 './include/Common/Numeric.h',
10 8 './include/Common/spimpl.h',
11 9 './include/Common/DateUtils.h',
12 10 './include/Common/MimeTypesDef.h',
13 11 './include/Common/SignalWaiter.h',
14 './include/Common/deprecate.h',
15 12 './include/Common/debug.h',
16 13 './include/Common/MetaTypes.h',
17 './include/Common/cpp_utils.h',
18 14 './include/Common/SortUtils.h',
19 15 './include/Data/DateTimeRangeHelper.h',
20 16 './include/Data/ScalarTimeSerie.h',
21 17 './include/Data/DateTimeRange.h',
22 18 './include/Data/DataProviderParameters.h',
23 19 './include/Data/TimeSeriesUtils.h',
24 20 './include/Data/VectorTimeSerie.h',
25 21 './include/Data/SqpIterator.h',
26 22 './include/Data/IDataProvider.h',
27 23 './include/Data/SpectrogramTimeSerie.h',
28 24 './include/Data/MultiComponentTimeSerie.h',
29 25 './include/Data/DataSeriesType.h',
30 26 './include/CoreGlobal.h',
31 27 './include/Network/NetworkController.h',
32 28 './include/Network/Response.h',
33 29 './include/Network/Downloader.h',
34 30 './include/Settings/SqpSettingsDefs.h',
35 31 './include/Settings/ISqpSettingsBindable.h',
36 32 './include/DataSource/DataSourceController.h',
37 33 './include/DataSource/DataSourceItem.h',
38 34 './include/DataSource/DataSourceItemAction.h',
39 35 './include/DataSource/DataSourceItemMergeHelper.h',
40 36 './include/Time/TimeController.h',
41 37 './include/PluginManager/PluginManager.h',
42 38 './include/Version.h',
43 39 './include/Catalogue/CatalogueController.h',
44 40 './include/Plugin/IPlugin.h',
45 41 './include/Variable/VariableModel2.h',
46 42 './include/Variable/VariableController2.h',
47 43 './include/Variable/Variable2.h',
48 44 './include/Variable/VariableSynchronizationGroup2.h',
49 45 './include/Variable/private/VCTransaction.h']
50 46
51 47
52 48
53 49 core_moc_sources = ['src/Network/Downloader.cpp']
54 50
55 51
56 52 core_moc_files = qt5.preprocess(moc_headers : core_moc_headers, moc_sources: core_moc_sources)
57 53
58 54 core_sources = ['./src/Common/MimeTypesDef.cpp',
59 55 './src/Common/SignalWaiter.cpp',
60 56 './src/Common/DateUtils.cpp',
61 './src/Common/StringUtils.cpp',
62 57 './src/Network/Downloader.cpp',
63 58 './src/Network/NetworkController.cpp',
64 59 './src/Settings/SqpSettingsDefs.cpp',
65 60 './src/DataSource/DataSourceItemAction.cpp',
66 61 './src/DataSource/DataSourceItemMergeHelper.cpp',
67 62 './src/DataSource/DataSourceItem.cpp',
68 63 './src/DataSource/DataSourceController.cpp',
69 64 './src/Time/TimeController.cpp',
70 65 './src/PluginManager/PluginManager.cpp',
71 66 './src/Version.cpp',
72 67 './src/Catalogue/CatalogueController.cpp',
73 68 './src/Variable/VariableSynchronizationGroup2.cpp',
74 69 './src/Variable/Variable2.cpp',
75 70 './src/Variable/VariableController2.cpp',
76 71 './src/Variable/VariableModel2.cpp']
77 72
78 73
79 74 core_inc = include_directories(['include', 'include/Plugin'])
80 75
81 76 sciqlop_core_lib = library('sciqlopcore',
82 77 core_sources,
83 78 core_moc_files,
84 79 cpp_args : '-DCORE_LIB',
85 80 include_directories : core_inc,
86 dependencies : [qt5core, qt5network, catalogicpp_dep, pybind11_dep, timeseries_dep],
81 dependencies : [qt5core, qt5network, catalogicpp_dep, pybind11_dep, timeseries_dep, cpp_utils_dep],
87 82 install : true
88 83 )
89 84
90 85
91 86 sciqlop_core = declare_dependency(link_with : sciqlop_core_lib,
92 87 include_directories : core_inc,
93 dependencies : [qt5core, qt5network, catalogicpp_dep, pybind11_dep, timeseries_dep])
88 dependencies : [qt5core, qt5network, catalogicpp_dep, pybind11_dep, timeseries_dep, cpp_utils_dep])
94 89
95 90 pymod = import('python')
96 91 python3 = pymod.find_installation('python3')
97 92
98 93 pysciqlopcore_srcs = [
99 94 './src/pybind11_wrappers/CatalogWrappers.cpp',
100 95 './src/pybind11_wrappers/QtWrappers.cpp',
101 96 './src/pybind11_wrappers/CoreWrappers.cpp'
102 97 ]
103 98
104 99 python3.extension_module('pysciqlopcore', './src/pybind11_wrappers/CoreWrappers.cpp',
105 100 dependencies: [sciqlop_core],
106 101 install: true
107 102 )
108 103
109 104
110 105 subdir('tests')
111 106
@@ -1,800 +1,800
1 1 #include <Catalogue/CatalogueController.h>
2 2 #include <CatalogueIO.hpp>
3 #include <Common/containers.h>
3 #include <containers/algorithms.hpp>
4 4 #include <Common/debug.h>
5 5 #include <QDataStream>
6 6 #include <QDir>
7 7 #include <QMutex>
8 8 #include <QStandardPaths>
9 9 #include <QThread>
10 10
11 using namespace SciQLop::containers;
11 using namespace cpp_utils::containers;
12 12
13 13 // class CatalogueController::CatalogueControllerPrivate
14 14 //{
15 15
16 16 // public:
17 17 // explicit CatalogueControllerPrivate(CatalogueController* parent) : m_Q {
18 18 // parent } {}
19 19
20 20 // CatalogueDao m_CatalogueDao;
21 21
22 22 // QStringList m_RepositoryList;
23 23 // CatalogueController* m_Q;
24 24
25 25 // QSet<QString> m_KeysWithChanges;
26 26
27 27 // QString eventUniqueKey(const std::shared_ptr<DBEvent>& event) const;
28 28 // QString catalogueUniqueKey(const std::shared_ptr<DBCatalogue>& catalogue)
29 29 // const;
30 30
31 31 // void copyDBtoDB(const QString& dbFrom, const QString& dbTo);
32 32 // QString toWorkRepository(QString repository);
33 33 // QString toSyncRepository(QString repository);
34 34 // void savAllDB();
35 35
36 36 // void saveEvent(std::shared_ptr<DBEvent> event, bool persist = true);
37 37 // void saveCatalogue(std::shared_ptr<DBCatalogue> catalogue, bool persist =
38 38 // true);
39 39
40 40 // std::shared_ptr<IRequestPredicate> createFinder(
41 41 // const QUuid& uniqId, const QString& repository, DBType type);
42 42 //};
43 43
44 44 CatalogueController::CatalogueController(QObject* parent)
45 45 //: impl { spimpl::make_unique_impl<CatalogueControllerPrivate>(this) }
46 46 {}
47 47
48 48 CatalogueController::~CatalogueController() {}
49 49
50 50 QStringList CatalogueController::repositories() const
51 51 {
52 52 QStringList repos;
53 53 std::transform(std::begin(_currentRepos), std::end(_currentRepos),
54 54 std::back_inserter(repos),
55 55 [](auto& pair) { return pair.first; });
56 56 return repos;
57 57 }
58 58
59 59 void CatalogueController::loadRepository(const QString& path,
60 60 const QString& name)
61 61 {
62 62 if(QFile::exists(path))
63 63 {
64 64 auto repo = CatalogiCpp::load_repository<time_t>(path.toStdString());
65 65 _lastSavedRepos[name] = repo;
66 66 _currentRepos[name] = repo;
67 67 }
68 68 }
69 69
70 70 void CatalogueController::saveRepository(const QString& path,
71 71 const QString& name)
72 72 {
73 73 if(contains(_currentRepos, name))
74 74 {
75 75 CatalogiCpp::save_repository(_currentRepos[name], path.toStdString());
76 76 _lastSavedRepos[name] = _currentRepos[name];
77 77 }
78 78 }
79 79
80 80 std::vector<CatalogueController::Event_ptr> CatalogueController::events()
81 81 {
82 82 std::vector<CatalogueController::Event_ptr> e_list;
83 83 for(auto& [_, repo] : _currentRepos)
84 84 {
85 85 for(auto& [_, event] : repo.events())
86 86 e_list.push_back(event);
87 87 }
88 88 return e_list;
89 89 }
90 90
91 91 std::vector<CatalogueController::Event_ptr>
92 92 CatalogueController::events(const CatalogueController::Catalogue_ptr& catalogue)
93 93 {
94 94 std::vector<CatalogueController::Event_ptr> e_list;
95 95 for(auto& [_, event] : catalogue->events())
96 96 e_list.push_back(event);
97 97 return e_list;
98 98 }
99 99
100 100 std::vector<std::shared_ptr<CatalogueController::Event_t>>
101 101 CatalogueController::events(const QString& repository)
102 102 {
103 103 std::vector<std::shared_ptr<CatalogueController::Event_t>> e_list;
104 104 if(contains(_currentRepos, repository))
105 105 {
106 106 auto repo = _currentRepos[repository];
107 107 for(auto& [_, event] : repo.events())
108 108 e_list.push_back(event);
109 109 }
110 110 return e_list;
111 111 }
112 112
113 113 std::vector<CatalogueController::Catalogue_ptr>
114 114 CatalogueController::catalogues()
115 115 {
116 116 std::vector<CatalogueController::Catalogue_ptr> c_list;
117 117 for(auto& [_, repo] : _currentRepos)
118 118 {
119 119 for(auto& [_, catalogue] : repo.catalogues())
120 120 c_list.push_back(catalogue);
121 121 }
122 122 return c_list;
123 123 }
124 124
125 125 std::vector<CatalogueController::Catalogue_ptr>
126 126 CatalogueController::catalogues(const QString& repository)
127 127 {
128 128 std::vector<CatalogueController::Catalogue_ptr> c_list;
129 129 if(contains(_currentRepos, repository))
130 130 {
131 131 auto repo = _currentRepos[repository];
132 132 for(auto& [_, catalogue] : repo.catalogues())
133 133 c_list.push_back(catalogue);
134 134 }
135 135 return c_list;
136 136 }
137 137
138 138 bool CatalogueController::hasUnsavedChanges(
139 139 CatalogueController::Event_ptr event)
140 140 {
141 141 if(auto repo = repository(event))
142 142 {
143 143 if(contains(_lastSavedRepos, *repo))
144 144 { return *event != *(_lastSavedRepos[*repo].event(event->uuid)); }
145 145 }
146 146 return true;
147 147 }
148 148
149 149 std::optional<QString>
150 150 CatalogueController::repository(CatalogueController::Event_ptr event)
151 151 {
152 152 for(auto& [repoName, repo] : _currentRepos)
153 153 {
154 154 if(repo.event(event->uuid)) return repoName;
155 155 }
156 156 return std::nullopt;
157 157 }
158 158
159 159 std::optional<QString>
160 160 CatalogueController::repository(CatalogueController::Catalogue_ptr catalogue)
161 161 {
162 162 for(auto& [repoName, repo] : _currentRepos)
163 163 {
164 164 if(repo.catalogue(catalogue->uuid)) return repoName;
165 165 }
166 166 return std::nullopt;
167 167 }
168 168
169 169 void CatalogueController::save(CatalogueController::Event_ptr event)
170 170 {
171 171 auto repo_name = repository(event);
172 172 if(repo_name && contains(_lastSavedRepos, *repo_name))
173 173 {
174 174 auto repo = _lastSavedRepos[*repo_name];
175 175 if(contains(repo.events(), event->uuid))
176 176 {
177 177 auto saved_evemt = _lastSavedRepos[*repo_name].event(event->uuid);
178 178 *saved_evemt = *event;
179 179 }
180 180 else
181 181 {
182 182 // TODO/Question should we also take care of which catalogue has it?
183 183 // if an event is created and added to a catalogue, what should we do if
184 184 // the user clicks the event save button?
185 185 // - Only save event value in all events list?
186 186 // - Also save the fact that this event is added to a catalogue and save
187 187 // the catalogue?
188 188 repo.add(event);
189 189 }
190 190 }
191 191 }
192 192
193 193 void CatalogueController::save(CatalogueController::Catalogue_ptr catalogue)
194 194 {
195 195 auto repo_name = repository(catalogue);
196 196 if(repo_name && contains(_lastSavedRepos, *repo_name))
197 197 {
198 198 auto repo = _lastSavedRepos[*repo_name];
199 199 if(contains(repo.catalogues(), catalogue->uuid))
200 200 {
201 201 auto saved_catalogue = repo.catalogue(catalogue->uuid);
202 202 *saved_catalogue = *catalogue;
203 203 }
204 204 else
205 205 {
206 206 repo.add(catalogue);
207 207 }
208 208 }
209 209 }
210 210
211 211 void CatalogueController::save(const QString& repository)
212 212 {
213 213 if(contains(_currentRepos, repository))
214 214 { _lastSavedRepos[repository] = _currentRepos[repository]; }
215 215 else
216 216 {
217 217 SCIQLOP_ERROR(CatalogueController, "Trying to save an unknown repository");
218 218 }
219 219 }
220 220
221 221 void CatalogueController::add(const QString& repository)
222 222 {
223 223 if(!contains(_currentRepos, repository))
224 224 {
225 225 _currentRepos[repository] = Repository_t{};
226 226 emit repositoryAdded(repository);
227 227 }
228 228 }
229 229
230 230 CatalogueController::Catalogue_ptr
231 231 CatalogueController::add(const QString& catalogue, const QString& repository)
232 232 {
233 233 if(!contains(_currentRepos, repository)) { add(repository); }
234 234 auto new_catalogue = make_catalogue_ptr();
235 235 new_catalogue->name = catalogue.toStdString();
236 236 _currentRepos[repository].add(new_catalogue);
237 237 emit catalogueAdded(new_catalogue, repository);
238 238 return new_catalogue;
239 239 }
240 240
241 241 void CatalogueController::add(CatalogueController::Event_ptr event,
242 242 CatalogueController::Catalogue_ptr catalogue)
243 243 {
244 244 catalogue->add(event);
245 245 emit this->catalogueChanged(catalogue);
246 246 }
247 247
248 248 void CatalogueController::add(CatalogueController::Event_ptr event,
249 249 const QString& repository)
250 250 {}
251 251
252 252 // void CatalogueController::saveDB(const QString& destinationPath, const
253 253 // QString& repositoryName)
254 254 //{
255 255 // if (!impl->m_CatalogueDao.saveDB(destinationPath, repositoryName))
256 256 // {
257 257 // qCCritical(LOG_CatalogueController())
258 258 // << tr("Impossible to saveDB %1 from %2 ").arg(repositoryName,
259 259 // destinationPath);
260 260 // }
261 261 //}
262 262
263 263 // std::list<std::shared_ptr<DBEvent>> CatalogueController::retrieveEvents(
264 264 // const QString& repository) const
265 265 //{
266 266 // QString dbDireName = repository.isEmpty() ? REPOSITORY_DEFAULT :
267 267 // repository;
268 268
269 269 // auto eventsShared = std::list<std::shared_ptr<DBEvent>> {};
270 270 // auto events =
271 271 // impl->m_CatalogueDao.getEvents(impl->toWorkRepository(dbDireName)); for
272 272 // (auto event : events)
273 273 // {
274 274 // eventsShared.push_back(std::make_shared<DBEvent>(event));
275 275 // }
276 276 // return eventsShared;
277 277 //}
278 278
279 279 // std::list<std::shared_ptr<DBEvent>> CatalogueController::retrieveAllEvents()
280 280 // const
281 281 //{
282 282 // auto eventsShared = std::list<std::shared_ptr<DBEvent>> {};
283 283 // for (auto repository : impl->m_RepositoryList)
284 284 // {
285 285 // eventsShared.splice(eventsShared.end(), retrieveEvents(repository));
286 286 // }
287 287
288 288 // return eventsShared;
289 289 //}
290 290
291 291 // std::list<std::shared_ptr<DBEvent>>
292 292 // CatalogueController::retrieveEventsFromCatalogue(
293 293 // std::shared_ptr<DBCatalogue> catalogue) const
294 294 //{
295 295 // auto eventsShared = std::list<std::shared_ptr<DBEvent>> {};
296 296 // auto events = impl->m_CatalogueDao.getCatalogueEvents(*catalogue);
297 297 // for (auto event : events)
298 298 // {
299 299 // eventsShared.push_back(std::make_shared<DBEvent>(event));
300 300 // }
301 301 // return eventsShared;
302 302 //}
303 303
304 304 // void CatalogueController::updateEvent(std::shared_ptr<DBEvent> event)
305 305 //{
306 306 // event->setRepository(impl->toWorkRepository(event->getRepository()));
307 307
308 308 // auto uniqueId = impl->eventUniqueKey(event);
309 309 // impl->m_KeysWithChanges.insert(uniqueId);
310 310
311 311 // impl->m_CatalogueDao.updateEvent(*event);
312 312 //}
313 313
314 314 // void CatalogueController::updateEventProduct(std::shared_ptr<DBEventProduct>
315 315 // eventProduct)
316 316 //{
317 317 // impl->m_CatalogueDao.updateEventProduct(*eventProduct);
318 318 //}
319 319
320 320 // void CatalogueController::removeEvent(std::shared_ptr<DBEvent> event)
321 321 //{
322 322 // // Remove it from both repository and repository_work
323 323 // event->setRepository(impl->toWorkRepository(event->getRepository()));
324 324 // impl->m_CatalogueDao.removeEvent(*event);
325 325 // event->setRepository(impl->toSyncRepository(event->getRepository()));
326 326 // impl->m_CatalogueDao.removeEvent(*event);
327 327 // impl->savAllDB();
328 328 //}
329 329
330 330 // void CatalogueController::addEvent(std::shared_ptr<DBEvent> event)
331 331 //{
332 332 // event->setRepository(impl->toWorkRepository(event->getRepository()));
333 333
334 334 // auto eventTemp = *event;
335 335 // impl->m_CatalogueDao.addEvent(eventTemp);
336 336
337 337 // // Call update is necessary at the creation of add Event if it has some
338 338 // tags or some event
339 339 // // products
340 340 // if (!event->getEventProducts().empty() || !event->getTags().empty())
341 341 // {
342 342
343 343 // auto eventProductsTemp = eventTemp.getEventProducts();
344 344 // auto eventProductTempUpdated = std::list<DBEventProduct> {};
345 345 // for (auto eventProductTemp : eventProductsTemp)
346 346 // {
347 347 // eventProductTemp.setEvent(eventTemp);
348 348 // eventProductTempUpdated.push_back(eventProductTemp);
349 349 // }
350 350 // eventTemp.setEventProducts(eventProductTempUpdated);
351 351
352 352 // impl->m_CatalogueDao.updateEvent(eventTemp);
353 353 // }
354 354
355 355 // auto workPred = impl->createFinder(event->getUniqId(),
356 356 // event->getRepository(), DBType::WORK);
357 357
358 358 // auto workEvent = impl->m_CatalogueDao.getEvent(workPred);
359 359 // *event = workEvent;
360 360
361 361 // auto uniqueId = impl->eventUniqueKey(event);
362 362 // impl->m_KeysWithChanges.insert(uniqueId);
363 363 //}
364 364
365 365 // void CatalogueController::saveEvent(std::shared_ptr<DBEvent> event)
366 366 //{
367 367 // impl->saveEvent(event, true);
368 368 // impl->m_KeysWithChanges.remove(impl->eventUniqueKey(event));
369 369 //}
370 370
371 371 // void CatalogueController::discardEvent(std::shared_ptr<DBEvent> event, bool&
372 372 // removed)
373 373 //{
374 374 // auto syncPred = impl->createFinder(event->getUniqId(),
375 375 // event->getRepository(), DBType::SYNC); auto workPred =
376 376 // impl->createFinder(event->getUniqId(), event->getRepository(),
377 377 // DBType::WORK);
378 378
379 379 // auto syncEvent = impl->m_CatalogueDao.getEvent(syncPred);
380 380 // if (!syncEvent.getUniqId().isNull())
381 381 // {
382 382 // removed = false;
383 383 // impl->m_CatalogueDao.copyEvent(
384 384 // syncEvent, impl->toWorkRepository(event->getRepository()), true);
385 385
386 386 // auto workEvent = impl->m_CatalogueDao.getEvent(workPred);
387 387 // *event = workEvent;
388 388 // impl->m_KeysWithChanges.remove(impl->eventUniqueKey(event));
389 389 // }
390 390 // else
391 391 // {
392 392 // removed = true;
393 393 // // Since the element wasn't in sync repository. Discard it means
394 394 // remove it
395 395 // event->setRepository(impl->toWorkRepository(event->getRepository()));
396 396 // impl->m_CatalogueDao.removeEvent(*event);
397 397 // }
398 398 //}
399 399
400 400 // bool CatalogueController::eventHasChanges(std::shared_ptr<DBEvent> event)
401 401 // const
402 402 //{
403 403 // return impl->m_KeysWithChanges.contains(impl->eventUniqueKey(event));
404 404 //}
405 405
406 406 // std::list<std::shared_ptr<DBCatalogue>>
407 407 // CatalogueController::retrieveCatalogues(
408 408 // const QString& repository) const
409 409 //{
410 410 // QString dbDireName = repository.isEmpty() ? REPOSITORY_DEFAULT :
411 411 // repository;
412 412
413 413 // auto cataloguesShared = std::list<std::shared_ptr<DBCatalogue>> {};
414 414 // auto catalogues =
415 415 // impl->m_CatalogueDao.getCatalogues(impl->toWorkRepository(dbDireName));
416 416 // for (auto catalogue : catalogues)
417 417 // {
418 418 // cataloguesShared.push_back(std::make_shared<DBCatalogue>(catalogue));
419 419 // }
420 420 // return cataloguesShared;
421 421 //}
422 422
423 423 // void CatalogueController::addCatalogue(std::shared_ptr<DBCatalogue>
424 424 // catalogue)
425 425 //{
426 426 // catalogue->setRepository(impl->toWorkRepository(catalogue->getRepository()));
427 427
428 428 // auto catalogueTemp = *catalogue;
429 429 // impl->m_CatalogueDao.addCatalogue(catalogueTemp);
430 430
431 431 // auto workPred
432 432 // = impl->createFinder(catalogue->getUniqId(),
433 433 // catalogue->getRepository(), DBType::WORK);
434 434
435 435 // auto workCatalogue = impl->m_CatalogueDao.getCatalogue(workPred);
436 436 // *catalogue = workCatalogue;
437 437
438 438 // auto uniqueId = impl->catalogueUniqueKey(catalogue);
439 439 // impl->m_KeysWithChanges.insert(uniqueId);
440 440 //}
441 441
442 442 // void CatalogueController::updateCatalogue(std::shared_ptr<DBCatalogue>
443 443 // catalogue)
444 444 //{
445 445 // catalogue->setRepository(impl->toWorkRepository(catalogue->getRepository()));
446 446
447 447 // auto uniqueId = impl->catalogueUniqueKey(catalogue);
448 448 // impl->m_KeysWithChanges.insert(uniqueId);
449 449
450 450 // impl->m_CatalogueDao.updateCatalogue(*catalogue);
451 451 //}
452 452
453 453 // void CatalogueController::removeCatalogue(std::shared_ptr<DBCatalogue>
454 454 // catalogue)
455 455 //{
456 456 // // Remove it from both repository and repository_work
457 457 // catalogue->setRepository(impl->toWorkRepository(catalogue->getRepository()));
458 458 // impl->m_CatalogueDao.removeCatalogue(*catalogue);
459 459 // catalogue->setRepository(impl->toSyncRepository(catalogue->getRepository()));
460 460 // impl->m_CatalogueDao.removeCatalogue(*catalogue);
461 461 // impl->savAllDB();
462 462 //}
463 463
464 464 // void CatalogueController::saveCatalogue(std::shared_ptr<DBCatalogue>
465 465 // catalogue)
466 466 //{
467 467 // impl->saveCatalogue(catalogue, true);
468 468 // impl->m_KeysWithChanges.remove(impl->catalogueUniqueKey(catalogue));
469 469
470 470 // // remove key of events of the catalogue
471 471 // if (catalogue->getType() == CatalogueType::STATIC)
472 472 // {
473 473 // auto events = this->retrieveEventsFromCatalogue(catalogue);
474 474 // for (auto event : events)
475 475 // {
476 476 // impl->m_KeysWithChanges.remove(impl->eventUniqueKey(event));
477 477 // }
478 478 // }
479 479 //}
480 480
481 481 // void CatalogueController::discardCatalogue(std::shared_ptr<DBCatalogue>
482 482 // catalogue, bool& removed)
483 483 //{
484 484 // auto syncPred
485 485 // = impl->createFinder(catalogue->getUniqId(),
486 486 // catalogue->getRepository(), DBType::SYNC);
487 487 // auto workPred
488 488 // = impl->createFinder(catalogue->getUniqId(),
489 489 // catalogue->getRepository(), DBType::WORK);
490 490
491 491 // auto syncCatalogue = impl->m_CatalogueDao.getCatalogue(syncPred);
492 492 // if (!syncCatalogue.getUniqId().isNull())
493 493 // {
494 494 // removed = false;
495 495 // impl->m_CatalogueDao.copyCatalogue(
496 496 // syncCatalogue, impl->toWorkRepository(catalogue->getRepository()),
497 497 // true);
498 498
499 499 // auto workCatalogue = impl->m_CatalogueDao.getCatalogue(workPred);
500 500 // *catalogue = workCatalogue;
501 501 // impl->m_KeysWithChanges.remove(impl->catalogueUniqueKey(catalogue));
502 502 // }
503 503 // else
504 504 // {
505 505 // removed = true;
506 506 // // Since the element wasn't in sync repository. Discard it means
507 507 // remove it
508 508 // catalogue->setRepository(impl->toWorkRepository(catalogue->getRepository()));
509 509 // impl->m_CatalogueDao.removeCatalogue(*catalogue);
510 510 // }
511 511 //}
512 512
513 513 // void CatalogueController::saveAll()
514 514 //{
515 515 // for (auto repository : impl->m_RepositoryList)
516 516 // {
517 517 // // Save Event
518 518 // auto events = this->retrieveEvents(repository);
519 519 // for (auto event : events)
520 520 // {
521 521 // impl->saveEvent(event, false);
522 522 // }
523 523
524 524 // // Save Catalogue
525 525 // auto catalogues = this->retrieveCatalogues(repository);
526 526 // for (auto catalogue : catalogues)
527 527 // {
528 528 // impl->saveCatalogue(catalogue, false);
529 529 // }
530 530 // }
531 531
532 532 // impl->savAllDB();
533 533 // impl->m_KeysWithChanges.clear();
534 534 //}
535 535
536 536 // bool CatalogueController::hasChanges() const
537 537 //{
538 538 // return !impl->m_KeysWithChanges.isEmpty();
539 539 //}
540 540
541 541 // QByteArray CatalogueController::mimeDataForEvents(
542 542 // const QVector<std::shared_ptr<DBEvent>>& events) const
543 543 //{
544 544 // auto encodedData = QByteArray {};
545 545
546 546 // QMap<QString, QVariantList> idsPerRepository;
547 547 // for (auto event : events)
548 548 // {
549 549 // idsPerRepository[event->getRepository()] << event->getUniqId();
550 550 // }
551 551
552 552 // QDataStream stream { &encodedData, QIODevice::WriteOnly };
553 553 // stream << idsPerRepository;
554 554
555 555 // return encodedData;
556 556 //}
557 557
558 558 // QVector<std::shared_ptr<DBEvent>> CatalogueController::eventsForMimeData(
559 559 // const QByteArray& mimeData) const
560 560 //{
561 561 // auto events = QVector<std::shared_ptr<DBEvent>> {};
562 562 // QDataStream stream { mimeData };
563 563
564 564 // QMap<QString, QVariantList> idsPerRepository;
565 565 // stream >> idsPerRepository;
566 566
567 567 // for (auto it = idsPerRepository.cbegin(); it != idsPerRepository.cend();
568 568 // ++it)
569 569 // {
570 570 // auto repository = it.key();
571 571 // auto allRepositoryEvent = retrieveEvents(repository);
572 572 // for (auto uuid : it.value())
573 573 // {
574 574 // for (auto repositoryEvent : allRepositoryEvent)
575 575 // {
576 576 // if (uuid.toUuid() == repositoryEvent->getUniqId())
577 577 // {
578 578 // events << repositoryEvent;
579 579 // }
580 580 // }
581 581 // }
582 582 // }
583 583
584 584 // return events;
585 585 //}
586 586
587 587 // QByteArray CatalogueController::mimeDataForCatalogues(
588 588 // const QVector<std::shared_ptr<DBCatalogue>>& catalogues) const
589 589 //{
590 590 // auto encodedData = QByteArray {};
591 591
592 592 // QMap<QString, QVariantList> idsPerRepository;
593 593 // for (auto catalogue : catalogues)
594 594 // {
595 595 // idsPerRepository[catalogue->getRepository()] <<
596 596 // catalogue->getUniqId();
597 597 // }
598 598
599 599 // QDataStream stream { &encodedData, QIODevice::WriteOnly };
600 600 // stream << idsPerRepository;
601 601
602 602 // return encodedData;
603 603 //}
604 604
605 605 // QVector<std::shared_ptr<DBCatalogue>>
606 606 // CatalogueController::cataloguesForMimeData(
607 607 // const QByteArray& mimeData) const
608 608 //{
609 609 // auto catalogues = QVector<std::shared_ptr<DBCatalogue>> {};
610 610 // QDataStream stream { mimeData };
611 611
612 612 // QMap<QString, QVariantList> idsPerRepository;
613 613 // stream >> idsPerRepository;
614 614
615 615 // for (auto it = idsPerRepository.cbegin(); it != idsPerRepository.cend();
616 616 // ++it)
617 617 // {
618 618 // auto repository = it.key();
619 619 // auto allRepositoryCatalogues = retrieveCatalogues(repository);
620 620 // for (auto uuid : it.value())
621 621 // {
622 622 // for (auto repositoryCatalogues : allRepositoryCatalogues)
623 623 // {
624 624 // if (uuid.toUuid() == repositoryCatalogues->getUniqId())
625 625 // {
626 626 // catalogues << repositoryCatalogues;
627 627 // }
628 628 // }
629 629 // }
630 630 // }
631 631
632 632 // return catalogues;
633 633 //}
634 634
635 635 // void CatalogueController::initialize()
636 636 //{
637 637 // qCDebug(LOG_CatalogueController())
638 638 // << tr("CatalogueController init") << QThread::currentThread();
639 639
640 640 // impl->m_CatalogueDao.initialize();
641 641 // auto defaultRepositoryLocation
642 642 // = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);
643 643
644 644 // QDir defaultRepositoryLocationDir;
645 645 // if (defaultRepositoryLocationDir.mkpath(defaultRepositoryLocation))
646 646 // {
647 647 // defaultRepositoryLocationDir.cd(defaultRepositoryLocation);
648 648 // auto defaultRepository =
649 649 // defaultRepositoryLocationDir.absoluteFilePath(REPOSITORY_DEFAULT);
650 650
651 651 // qCInfo(LOG_CatalogueController())
652 652 // << tr("Persistant data loading from: ") << defaultRepository;
653 653
654 654 // QDir dbDir(defaultRepository);
655 655 // impl->m_RepositoryList << REPOSITORY_DEFAULT;
656 656 // if (dbDir.exists())
657 657 // {
658 658 // auto dirName = dbDir.dirName();
659 659
660 660 // if (impl->m_CatalogueDao.addDB(defaultRepository, dirName))
661 661 // {
662 662 // impl->copyDBtoDB(dirName, impl->toWorkRepository(dirName));
663 663 // }
664 664 // }
665 665 // else
666 666 // {
667 667 // qCInfo(LOG_CatalogueController())
668 668 // << tr("Initialisation of Default repository detected") <<
669 669 // defaultRepository;
670 670 // }
671 671 // }
672 672 // else
673 673 // {
674 674 // qCWarning(LOG_CatalogueController())
675 675 // << tr("Cannot load the persistent default repository from ")
676 676 // << defaultRepositoryLocation;
677 677 // }
678 678
679 679 // qCDebug(LOG_CatalogueController()) << tr("CatalogueController init END");
680 680 //}
681 681
682 682 // QString CatalogueController::CatalogueControllerPrivate::eventUniqueKey(
683 683 // const std::shared_ptr<DBEvent>& event) const
684 684 //{
685 685 // return event->getUniqId().toString().append(event->getRepository());
686 686 //}
687 687
688 688 // QString CatalogueController::CatalogueControllerPrivate::catalogueUniqueKey(
689 689 // const std::shared_ptr<DBCatalogue>& catalogue) const
690 690 //{
691 691 // return
692 692 // catalogue->getUniqId().toString().append(catalogue->getRepository());
693 693 //}
694 694
695 695 // void CatalogueController::CatalogueControllerPrivate::copyDBtoDB(
696 696 // const QString& dbFrom, const QString& dbTo)
697 697 //{
698 698 // // auto cataloguesShared = std::list<std::shared_ptr<DBCatalogue> >{};
699 699 // auto catalogues = m_CatalogueDao.getCatalogues(dbFrom);
700 700 // auto events = m_CatalogueDao.getEvents(dbFrom);
701 701 // for (auto catalogue : catalogues)
702 702 // {
703 703 // m_CatalogueDao.copyCatalogue(catalogue, dbTo, true);
704 704 // }
705 705
706 706 // for (auto event : events)
707 707 // {
708 708 // m_CatalogueDao.copyEvent(event, dbTo, true);
709 709 // }
710 710 //}
711 711
712 712 // QString
713 713 // CatalogueController::CatalogueControllerPrivate::toWorkRepository(QString
714 714 // repository)
715 715 //{
716 716 // auto syncRepository = toSyncRepository(repository);
717 717
718 718 // return QString("%1%2").arg(syncRepository, REPOSITORY_WORK_SUFFIX);
719 719 //}
720 720
721 721 // QString
722 722 // CatalogueController::CatalogueControllerPrivate::toSyncRepository(QString
723 723 // repository)
724 724 //{
725 725 // auto syncRepository = repository;
726 726 // if (repository.endsWith(REPOSITORY_WORK_SUFFIX))
727 727 // {
728 728 // syncRepository.remove(REPOSITORY_WORK_SUFFIX);
729 729 // }
730 730 // else if (repository.endsWith(REPOSITORY_TRASH_SUFFIX))
731 731 // {
732 732 // syncRepository.remove(REPOSITORY_TRASH_SUFFIX);
733 733 // }
734 734 // return syncRepository;
735 735 //}
736 736
737 737 // void CatalogueController::CatalogueControllerPrivate::savAllDB()
738 738 //{
739 739 // for (auto repository : m_RepositoryList)
740 740 // {
741 741 // auto defaultRepositoryLocation
742 742 // =
743 743 // QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);
744 744 // m_CatalogueDao.saveDB(defaultRepositoryLocation, repository);
745 745 // }
746 746 //}
747 747
748 748 // void CatalogueController::CatalogueControllerPrivate::saveEvent(
749 749 // std::shared_ptr<DBEvent> event, bool persist)
750 750 //{
751 751 // m_CatalogueDao.copyEvent(*event, toSyncRepository(event->getRepository()),
752 752 // true); if (persist)
753 753 // {
754 754 // savAllDB();
755 755 // }
756 756 //}
757 757
758 758 // void CatalogueController::CatalogueControllerPrivate::saveCatalogue(
759 759 // std::shared_ptr<DBCatalogue> catalogue, bool persist)
760 760 //{
761 761 // m_CatalogueDao.copyCatalogue(*catalogue,
762 762 // toSyncRepository(catalogue->getRepository()), true); if (persist)
763 763 // {
764 764 // savAllDB();
765 765 // }
766 766 //}
767 767
768 768 // std::shared_ptr<IRequestPredicate>
769 769 // CatalogueController::CatalogueControllerPrivate::createFinder(
770 770 // const QUuid& uniqId, const QString& repository, DBType type)
771 771 //{
772 772 // // update catalogue parameter
773 773 // auto uniqIdPredicate = std::make_shared<ComparaisonPredicate>(
774 774 // QString { "uniqId" }, uniqId, ComparaisonOperation::EQUALEQUAL);
775 775
776 776 // auto repositoryType = repository;
777 777 // switch (type)
778 778 // {
779 779 // case DBType::SYNC:
780 780 // repositoryType = toSyncRepository(repositoryType);
781 781 // break;
782 782 // case DBType::WORK:
783 783 // repositoryType = toWorkRepository(repositoryType);
784 784 // break;
785 785 // case DBType::TRASH:
786 786 // default:
787 787 // break;
788 788 // }
789 789
790 790 // auto repositoryPredicate = std::make_shared<ComparaisonPredicate>(
791 791 // QString { "repository" }, repositoryType,
792 792 // ComparaisonOperation::EQUALEQUAL);
793 793
794 794 // auto finderPred =
795 795 // std::make_shared<CompoundPredicate>(CompoundOperation::AND);
796 796 // finderPred->AddRequestPredicate(uniqIdPredicate);
797 797 // finderPred->AddRequestPredicate(repositoryPredicate);
798 798
799 799 // return finderPred;
800 800 //}
@@ -1,298 +1,298
1 1 #include "DataSource/DataSourceController.h"
2 2
3 3 #include "DataSource/DataSourceItem.h"
4 4 #include "DataSource/DataSourceItemAction.h"
5 5
6 #include <Common/containers.h>
6 #include <containers/algorithms.hpp>
7 7 #include <Data/IDataProvider.h>
8 8 #include <QDataStream>
9 9 #include <QDir>
10 10 #include <QMutex>
11 11 #include <QStandardPaths>
12 12 #include <QThread>
13 13
14 14 Q_LOGGING_CATEGORY(LOG_DataSourceController, "DataSourceController")
15 15
16 16 std::unique_ptr<DataSourceItem> make_folder_item(const QString& name)
17 17 {
18 18 return std::make_unique<DataSourceItem>(DataSourceItemType::NODE, name);
19 19 }
20 20
21 21 template<typename T>
22 22 DataSourceItem* make_path_items(const T& path_list_begin,
23 23 const T& path_list_end, DataSourceItem* root)
24 24 {
25 25 std::for_each(path_list_begin, path_list_end,
26 26 [&root](const auto& folder_name) mutable {
27 27 auto folder_ptr = root->findItem(folder_name);
28 28 if(folder_ptr == nullptr)
29 29 {
30 30 auto folder = make_folder_item(folder_name);
31 31 folder_ptr = folder.get();
32 32 root->appendChild(std::move(folder));
33 33 }
34 34 root = folder_ptr;
35 35 });
36 36 return root;
37 37 }
38 38
39 39 std::unique_ptr<DataSourceItem>
40 40 make_product_item(const QVariantHash& metaData, const QUuid& dataSourceUid,
41 41 const QString& DATA_SOURCE_NAME, DataSourceController* dc)
42 42 {
43 43 auto result =
44 44 std::make_unique<DataSourceItem>(DataSourceItemType::PRODUCT, metaData);
45 45
46 46 // Adds plugin name to product metadata
47 47 result->setData(DataSourceItem::PLUGIN_DATA_KEY, DATA_SOURCE_NAME);
48 48 result->setData(DataSourceItem::ID_DATA_KEY,
49 49 metaData.value(DataSourceItem::NAME_DATA_KEY));
50 50
51 51 auto productName = metaData.value(DataSourceItem::NAME_DATA_KEY).toString();
52 52
53 53 // Add action to load product from DataSourceController
54 54 result->addAction(std::make_unique<DataSourceItemAction>(
55 55 QObject::tr("Load %1 product").arg(productName),
56 56 [productName, dataSourceUid, dc](DataSourceItem& item) {
57 57 if(dc) { dc->loadProductItem(dataSourceUid, item); }
58 58 }));
59 59
60 60 return result;
61 61 }
62 62
63 63 class DataSourceController::DataSourceControllerPrivate
64 64 {
65 65 public:
66 66 QMutex m_WorkingMutex;
67 67 /// Data sources registered
68 68 QHash<QUuid, QString> m_DataSources;
69 69 /// Data sources structures
70 70 std::map<QUuid, std::unique_ptr<DataSourceItem>> m_DataSourceItems;
71 71 /// Data providers registered
72 72 /// @remarks Data providers are stored as shared_ptr as they can be sent to a
73 73 /// variable and continue to live without necessarily the data source
74 74 /// controller
75 75 std::map<QUuid, std::shared_ptr<IDataProvider>> m_DataProviders;
76 76
77 77 // Search for the first datasource item matching the specified data
78 78 DataSourceItem* findDataSourceItem(const QVariantHash& data)
79 79 {
80 80 DataSourceItem* sourceItem = nullptr;
81 81 for(const auto& item : m_DataSourceItems)
82 82 {
83 83 sourceItem = item.second->findItem(data, true);
84 84 if(sourceItem) { break; }
85 85 }
86 86
87 87 return sourceItem;
88 88 }
89 89
90 90 // Search for the first datasource item matching the specified ID_DATA_KEY
91 91 DataSourceItem* findDataSourceItem(const QString& datasourceIdKey)
92 92 {
93 93 DataSourceItem* sourceItem = nullptr;
94 94 for(const auto& item : m_DataSourceItems)
95 95 {
96 96 sourceItem = item.second->findItem(datasourceIdKey, true);
97 97 if(sourceItem) { break; }
98 98 }
99 99
100 100 return sourceItem;
101 101 }
102 102 };
103 103
104 104 DataSourceController::DataSourceController(QObject* parent)
105 105 : impl{spimpl::make_unique_impl<DataSourceControllerPrivate>()}
106 106 {
107 107 qCDebug(LOG_DataSourceController())
108 108 << tr("DataSourceController construction") << QThread::currentThread();
109 109 }
110 110
111 111 DataSourceController::~DataSourceController()
112 112 {
113 113 qCDebug(LOG_DataSourceController())
114 114 << tr("DataSourceController destruction") << QThread::currentThread();
115 115 this->waitForFinish();
116 116 }
117 117
118 118 QUuid DataSourceController::registerDataSource(
119 119 const QString& dataSourceName) noexcept
120 120 {
121 121 auto dataSourceUid = QUuid::createUuid();
122 122 impl->m_DataSources.insert(dataSourceUid, dataSourceName);
123 123
124 124 return dataSourceUid;
125 125 }
126 126
127 127 void DataSourceController::registerProvider(IDataProvider *provider) noexcept
128 128 {
129 129 impl->m_DataSources.insert(provider->id(), provider->name());
130 130 impl->m_DataProviders.insert({provider->id(), std::unique_ptr<IDataProvider>{provider}});
131 131 // TODO rethink this, I don't get why we have to do this crap
132 132 // why do we need a root node for each provider
133 133 setDataSourceItem(provider->id(),make_folder_item(provider->name()));
134 134 }
135 135
136 136 void DataSourceController::setDataSourceItem(
137 137 const QUuid& dataSourceUid,
138 138 std::unique_ptr<DataSourceItem> dataSourceItem) noexcept
139 139 {
140 140 if(!dataSourceItem)
141 141 {
142 142 qCWarning(LOG_DataSourceController())
143 143 << tr("Data source item can't be registered (null item)");
144 144 return;
145 145 }
146 146
147 147 if(impl->m_DataSources.contains(dataSourceUid))
148 148 {
149 149 // The data provider is implicitly converted to a shared_ptr
150 150 impl->m_DataSourceItems.insert(
151 151 std::make_pair(dataSourceUid, std::move(dataSourceItem)));
152 152
153 153 // Retrieves the data source item to emit the signal with it
154 154 auto it = impl->m_DataSourceItems.find(dataSourceUid);
155 155 if(it != impl->m_DataSourceItems.end())
156 156 { emit dataSourceItemSet(it->second.get()); }
157 157 }
158 158 else
159 159 {
160 160 qCWarning(LOG_DataSourceController())
161 161 << tr("Can't set data source item for uid %1 : no "
162 162 "data source has been registered with the uid")
163 163 .arg(dataSourceUid.toString());
164 164 }
165 165 }
166 166
167 167
168 168
169 169 void DataSourceController::setDataSourceItem(const QUuid& dataSourceUid, const QString& path,
170 170 const QMap<QString, QString> &metaData) noexcept
171 171 {
172 172 if(auto it = impl->m_DataSourceItems.find(dataSourceUid);
173 173 it != impl->m_DataSourceItems.end())
174 174 {
175 175 auto path_list = path.split('/', QString::SkipEmptyParts);
176 176 auto name = *(std::cend(path_list) - 1);
177 177 auto path_item = make_path_items(
178 178 std::cbegin(path_list), std::cend(path_list) - 1, it->second.get());
179 179 QVariantHash meta_data{{DataSourceItem::NAME_DATA_KEY, name}};
180 180 for(auto& key : metaData.keys())
181 181 {
182 182 meta_data[key] = metaData[key];
183 183 }
184 184 path_item->appendChild(
185 185 make_product_item(meta_data, dataSourceUid, "test", this));
186 186 emit dataSourceItemSet(it->second.get());
187 187 }
188 188 }
189 189
190 190 void DataSourceController::setDataProvider(
191 191 const QUuid& dataSourceUid,
192 192 std::unique_ptr<IDataProvider> dataProvider) noexcept
193 193 {
194 194 if(impl->m_DataSources.contains(dataSourceUid))
195 195 {
196 196 impl->m_DataProviders.insert(
197 197 std::make_pair(dataSourceUid, std::move(dataProvider)));
198 198 }
199 199 else
200 200 {
201 201 qCWarning(LOG_DataSourceController())
202 202 << tr("Can't set data provider for uid %1 : no data "
203 203 "source has been registered with the uid")
204 204 .arg(dataSourceUid.toString());
205 205 }
206 206 }
207 207
208 208 void DataSourceController::loadProductItem(
209 209 const QUuid& dataSourceUid, const DataSourceItem& productItem) noexcept
210 210 {
211 211 if(productItem.type() == DataSourceItemType::PRODUCT ||
212 212 productItem.type() == DataSourceItemType::COMPONENT)
213 213 {
214 214 /// Retrieves the data provider of the data source (if any)
215 215 auto it = impl->m_DataProviders.find(dataSourceUid);
216 216 auto dataProvider =
217 217 (it != impl->m_DataProviders.end()) ? it->second : nullptr;
218 218
219 219 emit createVariable(productItem.name(), productItem.data(), dataProvider);
220 220 }
221 221 else
222 222 {
223 223 qCWarning(LOG_DataSourceController())
224 224 << tr("Can't load an item that is not a product");
225 225 }
226 226 }
227 227
228 228 QByteArray
229 229 DataSourceController::mimeDataForProductsData(const QVariantList& productsData)
230 230 {
231 231 QByteArray encodedData;
232 232 QDataStream stream{&encodedData, QIODevice::WriteOnly};
233 233
234 234 stream << productsData;
235 235
236 236 return encodedData;
237 237 }
238 238
239 239 QVariantList
240 240 DataSourceController::productsDataForMimeData(const QByteArray& mimeData)
241 241 {
242 242 QDataStream stream{mimeData};
243 243
244 244 QVariantList productList;
245 245 stream >> productList;
246 246
247 247 return productList;
248 248 }
249 249
250 250 void DataSourceController::initialize()
251 251 {
252 252 qCDebug(LOG_DataSourceController())
253 253 << tr("DataSourceController init") << QThread::currentThread();
254 254 impl->m_WorkingMutex.lock();
255 255 qCDebug(LOG_DataSourceController()) << tr("DataSourceController init END");
256 256 }
257 257
258 258 void DataSourceController::finalize() { impl->m_WorkingMutex.unlock(); }
259 259
260 260 void DataSourceController::requestVariableFromProductIdKey(
261 261 const QString& datasourceIdKey)
262 262 {
263 263 auto sourceItem = impl->findDataSourceItem(datasourceIdKey);
264 264
265 265 if(sourceItem)
266 266 {
267 267 auto sourceName = sourceItem->rootItem().name();
268 268 auto sourceId = impl->m_DataSources.key(sourceName);
269 269 loadProductItem(sourceId, *sourceItem);
270 270 }
271 271 else
272 272 {
273 273 qCWarning(LOG_DataSourceController())
274 274 << tr("requestVariable, product data not found");
275 275 }
276 276 }
277 277
278 278 void DataSourceController::requestVariable(const QVariantHash& productData)
279 279 {
280 280 auto sourceItem = impl->findDataSourceItem(productData);
281 281
282 282 if(sourceItem)
283 283 {
284 284 auto sourceName = sourceItem->rootItem().name();
285 285 auto sourceId = impl->m_DataSources.key(sourceName);
286 286 loadProductItem(sourceId, *sourceItem);
287 287 }
288 288 else
289 289 {
290 290 qCWarning(LOG_DataSourceController())
291 291 << tr("requestVariable, product data not found");
292 292 }
293 293 }
294 294
295 295 void DataSourceController::waitForFinish()
296 296 {
297 297 QMutexLocker locker{&impl->m_WorkingMutex};
298 298 }
@@ -1,448 +1,448
1 1 #include "Variable/VariableController2.h"
2 2
3 3 #include "Variable/VariableSynchronizationGroup2.h"
4 4
5 #include <Common/containers.h>
5 #include <containers/algorithms.hpp>
6 6 #include <Common/debug.h>
7 7 #include <Data/DataProviderParameters.h>
8 8 #include <Data/DateTimeRange.h>
9 9 #include <Data/DateTimeRangeHelper.h>
10 10 #include <QCoreApplication>
11 11 #include <QDataStream>
12 12 #include <QObject>
13 13 #include <QQueue>
14 14 #include <QRunnable>
15 15 #include <QThreadPool>
16 16 #include <Variable/private/VCTransaction.h>
17 17
18 18 class VariableController2::VariableController2Private
19 19 {
20 20 struct threadSafeVaraiblesMaps
21 21 {
22 22 inline void
23 23 addVariable(const std::shared_ptr<Variable2>& variable,
24 24 const std::shared_ptr<IDataProvider>& provider,
25 25 const std::shared_ptr<VariableSynchronizationGroup2>&
26 26 synchronizationGroup)
27 27 {
28 28 QWriteLocker lock{&_lock};
29 29 _variables[*variable] = variable;
30 30 _providers[*variable] = provider;
31 31 _synchronizationGroups[*variable] = synchronizationGroup;
32 32 }
33 33
34 34 inline void removeVariable(const std::shared_ptr<Variable2>& variable)
35 35 {
36 36 QWriteLocker lock{&_lock};
37 37 _variables.erase(*variable);
38 38 _providers.remove(*variable);
39 39 _synchronizationGroups.remove(*variable);
40 40 }
41 41
42 42 inline void
43 43 synchronize(const std::shared_ptr<Variable2>& variable,
44 44 const std::optional<std::shared_ptr<Variable2>>& with)
45 45 {
46 46 QWriteLocker lock{&_lock};
47 47 if(with.has_value())
48 48 {
49 49 auto newGroup = _synchronizationGroups[*with.value()];
50 50 newGroup->addVariable(*variable);
51 51 _synchronizationGroups[*variable] = newGroup;
52 52 }
53 53 else
54 54 {
55 55 _synchronizationGroups[*variable] =
56 56 std::make_shared<VariableSynchronizationGroup2>(*variable);
57 57 }
58 58 }
59 59
60 60 inline std::shared_ptr<Variable2> variable(QUuid variable)
61 61 {
62 62 QReadLocker lock{&_lock};
63 63 auto it = _variables.find(variable);
64 64 #if __cplusplus > 201703L
65 65 [[unlikely]]
66 66 #endif
67 67 if(it == _variables.end())
68 68 SCIQLOP_ERROR(threadSafeVaraiblesMaps, "Unknown Variable");
69 69 return (*it).second;
70 70 }
71 71
72 72 inline std::shared_ptr<Variable2> variable(int index)
73 73 {
74 74 QReadLocker lock{&_lock};
75 75 #if __cplusplus > 201703L
76 76 [[unlikely]]
77 77 #endif
78 if(!_variables.size() > index)
78 if(!(_variables.size() > static_cast<unsigned int>(index)))
79 79 SCIQLOP_ERROR(threadSafeVaraiblesMaps, "Index is out of bounds");
80 80 auto it = _variables.cbegin();
81 81 while(index != 0)
82 82 {
83 83 index -= 1;
84 84 it++;
85 85 }
86 86 return (*(it)).second;
87 87 }
88 88
89 89 inline const std::vector<std::shared_ptr<Variable2>> variables()
90 90 {
91 91 std::vector<std::shared_ptr<Variable2>> vars;
92 92 QReadLocker lock{&_lock};
93 93 for(const auto& [id, var] : _variables)
94 94 {
95 95 vars.push_back(var);
96 96 }
97 97 return vars;
98 98 }
99 99
100 100 inline std::shared_ptr<IDataProvider> provider(QUuid variable)
101 101 {
102 102 QReadLocker lock{&_lock};
103 103 #if __cplusplus > 201703L
104 104 [[unlikely]]
105 105 #endif
106 106 if(!_providers.contains(variable))
107 107 SCIQLOP_ERROR(threadSafeVaraiblesMaps, "Unknown Variable");
108 108 return _providers[variable];
109 109 }
110 110
111 111 inline std::shared_ptr<VariableSynchronizationGroup2> group(QUuid variable)
112 112 {
113 113 QReadLocker lock{&_lock};
114 114 #if __cplusplus > 201703L
115 115 [[unlikely]]
116 116 #endif
117 117 if(!_synchronizationGroups.contains(variable))
118 118 SCIQLOP_ERROR(threadSafeVaraiblesMaps, "Unknown Variable");
119 119 return _synchronizationGroups[variable];
120 120 }
121 121
122 122 inline bool has(const std::shared_ptr<Variable2>& variable)
123 123 {
124 124 QReadLocker lock{&_lock};
125 125 return _variables.find(*variable) == _variables.end();
126 126 }
127 127
128 128 private:
129 129 std::map<QUuid, std::shared_ptr<Variable2>> _variables;
130 130 QMap<QUuid, std::shared_ptr<IDataProvider>> _providers;
131 131 QMap<QUuid, std::shared_ptr<VariableSynchronizationGroup2>>
132 132 _synchronizationGroups;
133 133 QReadWriteLock _lock{QReadWriteLock::Recursive};
134 134 } _maps;
135 135 std::vector<QUuid> _variablesToRemove;
136 136 QThreadPool* _ThreadPool;
137 137 VCTransactionsQueues _transactions;
138 138
139 139 void _transactionComplete(QUuid group,
140 140 std::shared_ptr<VCTransaction> transaction)
141 141 {
142 142 if(transaction->done()) { _transactions.complete(group); }
143 143 this->_processTransactions();
144 144 }
145 145
146 146 void _cleanupVariables()
147 147 {
148 148 for(auto id : _variablesToRemove)
149 149 {
150 150 auto v = this->variable(id);
151 151 if(!hasPendingTransactions(v))
152 152 {
153 153 _variablesToRemove.erase(std::remove(_variablesToRemove.begin(),
154 154 _variablesToRemove.end(), id),
155 155 _variablesToRemove.end());
156 156 this->deleteVariable(v);
157 157 }
158 158 }
159 159 }
160 160
161 161 void _processTransactions(bool fragmented = false)
162 162 {
163 163 auto nextTransactions = _transactions.nextTransactions();
164 164 auto pendingTransactions = _transactions.pendingTransactions();
165 165 for(auto [groupID, newTransaction] : nextTransactions)
166 166 {
167 167 if(newTransaction.has_value() &&
168 168 !pendingTransactions[groupID].has_value())
169 169 {
170 170 _transactions.start(groupID);
171 171 auto refVar = _maps.variable(newTransaction.value()->refVar);
172 172 auto ranges =
173 173 _computeAllRangesInGroup(refVar, newTransaction.value()->range);
174 174 for(auto const& [ID, range] : ranges)
175 175 {
176 176 auto provider = _maps.provider(ID);
177 177 auto variable = _maps.variable(ID);
178 178 if(fragmented)
179 179 {
180 180 auto missingRanges = _computeMissingRanges(variable, range);
181 181
182 182 auto exe =
183 183 new TransactionExe(variable, provider, missingRanges, range);
184 184 QObject::connect(
185 185 exe, &TransactionExe::transactionComplete,
186 186 [groupID = groupID, transaction = newTransaction.value(),
187 187 this]() { this->_transactionComplete(groupID, transaction); });
188 188 _ThreadPool->start(exe);
189 189 }
190 190 else
191 191 {
192 192 auto exe = new TransactionExe(variable, provider, {range}, range);
193 193 QObject::connect(
194 194 exe, &TransactionExe::transactionComplete,
195 195 [groupID = groupID, transaction = newTransaction.value(),
196 196 this]() { this->_transactionComplete(groupID, transaction); });
197 197 _ThreadPool->start(exe);
198 198 }
199 199 }
200 200 }
201 201 }
202 202 // after each transaction update we get a new distribution of idle and
203 203 // working variables so we can delete variables which are waiting to be
204 204 // deleted if they are now idle
205 205 _cleanupVariables();
206 206 }
207 207
208 208 std::map<QUuid, DateTimeRange>
209 209 _computeAllRangesInGroup(const std::shared_ptr<Variable2>& refVar,
210 210 DateTimeRange r)
211 211 {
212 212 std::map<QUuid, DateTimeRange> ranges;
213 213 if(!DateTimeRangeHelper::hasnan(r))
214 214 {
215 215 auto group = _maps.group(*refVar);
216 216 if(auto transformation =
217 217 DateTimeRangeHelper::computeTransformation(refVar->range(), r);
218 218 transformation.has_value())
219 219 {
220 220 for(auto varId : group->variables())
221 221 {
222 222 auto var = _maps.variable(varId);
223 223 auto newRange = var->range().transform(transformation.value());
224 224 ranges[varId] = newRange;
225 225 }
226 226 }
227 227 else // force new range to all variables -> may be weird if more than one
228 228 // var in the group
229 229 // TODO ensure that there is no side effects
230 230 {
231 231 for(auto varId : group->variables())
232 232 {
233 233 auto var = _maps.variable(varId);
234 234 ranges[varId] = r;
235 235 }
236 236 }
237 237 }
238 238 else
239 239 {
240 240 SCIQLOP_ERROR(VariableController2Private, "Invalid range containing NaN");
241 241 }
242 242 return ranges;
243 243 }
244 244
245 245 std::vector<DateTimeRange>
246 246 _computeMissingRanges(const std::shared_ptr<Variable2>& var, DateTimeRange r)
247 247 {
248 248 return r - var->range();
249 249 }
250 250
251 251 void _changeRange(QUuid id, DateTimeRange r)
252 252 {
253 253 _changeRange(_maps.variable(id), r);
254 254 }
255 255 void _changeRange(const std::shared_ptr<Variable2>& var, DateTimeRange r)
256 256 {
257 257 auto provider = _maps.provider(*var);
258 258 auto missingRanges = _computeMissingRanges(var, r);
259 259 std::vector<TimeSeries::ITimeSerie*> data;
260 260 for(auto range : missingRanges)
261 261 {
262 262 data.push_back(
263 263 provider->getData(DataProviderParameters{{range}, var->metadata()}));
264 264 }
265 265 data.push_back(var->data().get()); // might be smarter
266 266 var->setData(data, r, true);
267 267 std::for_each(std::begin(data), std::end(data), [](auto ts) { delete ts; });
268 268 }
269 269
270 270 public:
271 271 VariableController2Private(QObject* parent = Q_NULLPTR)
272 272 {
273 273 Q_UNUSED(parent);
274 274 this->_ThreadPool = new QThreadPool();
275 275 this->_ThreadPool->setMaxThreadCount(32);
276 276 }
277 277
278 278 /*
279 279 * This dtor has to like this even if this is ugly, because default dtor would
280 280 * rely on declaration order to destruct members and that would always lead to
281 281 * regressions when modifying class members
282 282 */
283 283 ~VariableController2Private() { delete this->_ThreadPool; }
284 284
285 285 std::shared_ptr<Variable2>
286 286 createVariable(const QString& name, const QVariantHash& metadata,
287 287 std::shared_ptr<IDataProvider> provider)
288 288 {
289 289 auto newVar = std::make_shared<Variable2>(name, metadata);
290 290 auto group = std::make_shared<VariableSynchronizationGroup2>(newVar->ID());
291 291 _maps.addVariable(newVar, std::move(provider), group);
292 292 this->_transactions.addEntry(*group);
293 293 return newVar;
294 294 }
295 295
296 296 std::shared_ptr<Variable2> variable(QUuid ID) { return _maps.variable(ID); }
297 297
298 298 std::shared_ptr<Variable2> variable(int index)
299 299 {
300 300 return _maps.variable(index);
301 301 }
302 302
303 303 std::shared_ptr<Variable2>
304 304 cloneVariable(const std::shared_ptr<Variable2>& variable)
305 305 {
306 306 auto newVar = variable->clone();
307 307 _maps.synchronize(newVar, std::nullopt);
308 308 _maps.addVariable(newVar, _maps.provider(*variable), _maps.group(*newVar));
309 309 this->_transactions.addEntry(*_maps.group(*newVar));
310 310 return newVar;
311 311 }
312 312
313 313 bool hasPendingTransactions(const std::shared_ptr<Variable2>& variable)
314 314 {
315 315 return _transactions.active(*_maps.group(*variable));
316 316 }
317 317
318 318 bool hasPendingTransactions()
319 319 {
320 320 bool has = false;
321 321 for(const auto& var : _maps.variables())
322 322 {
323 323 has |= _transactions.active(*_maps.group(*var));
324 324 }
325 325 return has;
326 326 }
327 327
328 328 void deleteVariable(const std::shared_ptr<Variable2>& variable)
329 329 {
330 330 if(!hasPendingTransactions(variable))
331 331 _maps.removeVariable(variable);
332 332 else
333 333 _variablesToRemove.push_back(variable->ID());
334 334 }
335 335
336 336 void asyncChangeRange(const std::shared_ptr<Variable2>& variable,
337 337 const DateTimeRange& r)
338 338 {
339 339 if(!DateTimeRangeHelper::hasnan(r))
340 340 {
341 341 auto group = _maps.group(*variable);
342 342 // Just overwrite next transaction
343 343 {
344 344 _transactions.enqueue(*group,
345 345 std::make_shared<VCTransaction>(
346 346 variable->ID(), r,
347 347 static_cast<int>(group->variables().size())));
348 348 }
349 349 _processTransactions();
350 350 }
351 351 else
352 352 {
353 353 SCIQLOP_ERROR(VariableController2Private, "Invalid range containing NaN");
354 354 }
355 355 }
356 356
357 357 void changeRange(const std::shared_ptr<Variable2>& variable, DateTimeRange r)
358 358 {
359 359 asyncChangeRange(variable, r);
360 360 while(hasPendingTransactions(variable))
361 361 {
362 362 QCoreApplication::processEvents();
363 363 }
364 364 }
365 365
366 366 inline void synchronize(const std::shared_ptr<Variable2>& var,
367 367 const std::shared_ptr<Variable2>& with)
368 368 {
369 369 _maps.synchronize(var, with);
370 370 }
371 371
372 372 inline const std::vector<std::shared_ptr<Variable2>> variables()
373 373 {
374 374 return _maps.variables();
375 375 }
376 376 };
377 377
378 378 VariableController2::VariableController2()
379 379 : impl{spimpl::make_unique_impl<VariableController2Private>()}
380 380 {}
381 381
382 382 std::shared_ptr<Variable2> VariableController2::createVariable(
383 383 const QString& name, const QVariantHash& metadata,
384 384 const std::shared_ptr<IDataProvider>& provider, const DateTimeRange& range)
385 385 {
386 386 auto var = impl->createVariable(name, metadata, provider);
387 387 var->setRange(range); // even with no data this is it's range
388 388 if(!DateTimeRangeHelper::hasnan(range))
389 389 impl->asyncChangeRange(var, range);
390 390 else
391 391 SCIQLOP_ERROR(VariableController2, "Creating a variable with default "
392 392 "constructed DateTimeRange is an error");
393 393 emit variableAdded(var);
394 394 return var;
395 395 }
396 396
397 397 std::shared_ptr<Variable2>
398 398 VariableController2::cloneVariable(const std::shared_ptr<Variable2>& variable)
399 399 {
400 400 return impl->cloneVariable(variable);
401 401 }
402 402
403 403 void VariableController2::deleteVariable(
404 404 const std::shared_ptr<Variable2>& variable)
405 405 {
406 406 impl->deleteVariable(variable);
407 407 emit variableDeleted(variable);
408 408 }
409 409
410 410 void VariableController2::changeRange(
411 411 const std::shared_ptr<Variable2>& variable, const DateTimeRange& r)
412 412 {
413 413 impl->changeRange(variable, r);
414 414 }
415 415
416 416 void VariableController2::asyncChangeRange(
417 417 const std::shared_ptr<Variable2>& variable, const DateTimeRange& r)
418 418 {
419 419 impl->asyncChangeRange(variable, r);
420 420 }
421 421
422 422 const std::vector<std::shared_ptr<Variable2>> VariableController2::variables()
423 423 {
424 424 return impl->variables();
425 425 }
426 426
427 427 bool VariableController2::isReady(const std::shared_ptr<Variable2>& variable)
428 428 {
429 429 return !impl->hasPendingTransactions(variable);
430 430 }
431 431
432 432 bool VariableController2::isReady() { return !impl->hasPendingTransactions(); }
433 433
434 434 void VariableController2::synchronize(const std::shared_ptr<Variable2>& var,
435 435 const std::shared_ptr<Variable2>& with)
436 436 {
437 437 impl->synchronize(var, with);
438 438 }
439 439
440 440 const std::vector<std::shared_ptr<Variable2>>
441 441 VariableController2::variables(const std::vector<QUuid>& ids)
442 442 {
443 443 std::vector<std::shared_ptr<Variable2>> variables;
444 444 std::transform(std::cbegin(ids), std::cend(ids),
445 445 std::back_inserter(variables),
446 446 [this](const auto& id) { return impl->variable(id); });
447 447 return variables;
448 448 }
@@ -1,278 +1,279
1 1 #include <Common/DateUtils.h>
2 2 #include <Common/MimeTypesDef.h>
3 #include <Common/StringUtils.h>
4 #include <Common/containers.h>
3 #include <cpp_utils_qt/cpp_utils_qt.hpp>
4 #include <strings/algorithms.hpp>
5 #include <containers/algorithms.hpp>
5 6 #include <DataSource/DataSourceController.h>
6 7 #include <QMimeData>
7 8 #include <QSize>
8 9 #include <QTimer>
9 10 #include <Time/TimeController.h>
10 11 #include <Variable/Variable2.h>
11 12 #include <Variable/VariableController2.h>
12 13 #include <Variable/VariableModel2.h>
13 14 #include <unordered_map>
14 15
15 16 namespace
16 17 {
17 18 // Column indexes
18 19 const auto NAME_COLUMN = 0;
19 20 const auto TSTART_COLUMN = 1;
20 21 const auto TEND_COLUMN = 2;
21 22 const auto NBPOINTS_COLUMN = 3;
22 23 const auto UNIT_COLUMN = 4;
23 24 const auto MISSION_COLUMN = 5;
24 25 const auto PLUGIN_COLUMN = 6;
25 26 const auto NB_COLUMNS = 7;
26 27
27 28 // Column properties
28 29 const auto DEFAULT_HEIGHT = 25;
29 30 const auto DEFAULT_WIDTH = 100;
30 31
31 32 struct ColumnProperties
32 33 {
33 34 ColumnProperties(const QString& name = {}, int width = DEFAULT_WIDTH,
34 35 int height = DEFAULT_HEIGHT)
35 36 : m_Name{name}, m_Width{width}, m_Height{height}
36 37 {}
37 38
38 39 QString m_Name;
39 40 int m_Width;
40 41 int m_Height;
41 42 };
42 43
43 44 const auto COLUMN_PROPERTIES = QHash<int, ColumnProperties>{
44 45 {NAME_COLUMN, {QObject::tr("Name")}},
45 46 {TSTART_COLUMN, {QObject::tr("tStart"), 180}},
46 47 {TEND_COLUMN, {QObject::tr("tEnd"), 180}},
47 48 {NBPOINTS_COLUMN, {QObject::tr("Nb points")}},
48 49 {UNIT_COLUMN, {QObject::tr("Unit")}},
49 50 {MISSION_COLUMN, {QObject::tr("Mission")}},
50 51 {PLUGIN_COLUMN, {QObject::tr("Plugin")}}};
51 52
52 53 QString uniqueName(const QString& defaultName,
53 54 const std::vector<std::shared_ptr<Variable2>>& variables)
54 55 {
55 56 auto forbiddenNames = std::vector<QString>(variables.size());
56 57 std::transform(variables.cbegin(), variables.cend(), forbiddenNames.begin(),
57 58 [](const auto& variable) { return variable->name(); });
58 auto uniqueName = StringUtils::uniqueName(defaultName, forbiddenNames);
59 auto uniqueName = cpp_utils::strings::make_unique_name(defaultName, forbiddenNames);
59 60 Q_ASSERT(!uniqueName.isEmpty());
60 61
61 62 return uniqueName;
62 63 }
63 64
64 65 } // namespace
65 66
66 67 VariableModel2::VariableModel2(QObject* parent) : QAbstractTableModel{parent} {}
67 68
68 69 int VariableModel2::columnCount(const QModelIndex& parent) const
69 70 {
70 71 Q_UNUSED(parent);
71 72
72 73 return NB_COLUMNS;
73 74 }
74 75
75 76 int VariableModel2::rowCount(const QModelIndex& parent) const
76 77 {
77 78 Q_UNUSED(parent);
78 79 return _variables.size();
79 80 }
80 81
81 82 QVariant VariableModel2::data(const QModelIndex& index, int role) const
82 83 {
83 84 if(!index.isValid()) { return QVariant{}; }
84 85
85 86 if(index.row() < 0 || index.row() >= rowCount()) { return QVariant{}; }
86 87
87 88 if(role == Qt::DisplayRole)
88 89 {
89 90 if(auto variable = _variables[index.row()])
90 91 {
91 92 switch(index.column())
92 93 {
93 94 case NAME_COLUMN: return variable->name();
94 95 case TSTART_COLUMN:
95 96 {
96 97 if(auto range = variable->realRange(); range.has_value())
97 98 return DateUtils::dateTime(range.value().m_TStart)
98 99 .toString(DATETIME_FORMAT);
99 100 return QVariant{};
100 101 }
101 102 case TEND_COLUMN:
102 103 {
103 104 if(auto range = variable->realRange(); range.has_value())
104 105 return DateUtils::dateTime(range.value().m_TEnd)
105 106 .toString(DATETIME_FORMAT);
106 107 return QVariant{};
107 108 }
108 109 case NBPOINTS_COLUMN: return int(variable->nbPoints());
109 110 case UNIT_COLUMN:
110 111 return variable->metadata().value(QStringLiteral("units"));
111 112 case MISSION_COLUMN:
112 113 return variable->metadata().value(QStringLiteral("mission"));
113 114 case PLUGIN_COLUMN:
114 115 return variable->metadata().value(QStringLiteral("plugin"));
115 116 default:
116 117 // No action
117 118 break;
118 119 }
119 120 }
120 121 }
121 122 else if(role == VariableRoles::ProgressRole)
122 123 {
123 124 return QVariant{};
124 125 }
125 126
126 127 return QVariant{};
127 128 }
128 129
129 130 QVariant VariableModel2::headerData(int section, Qt::Orientation orientation,
130 131 int role) const
131 132 {
132 133 if(role != Qt::DisplayRole && role != Qt::SizeHintRole) { return QVariant{}; }
133 134
134 135 if(orientation == Qt::Horizontal)
135 136 {
136 137 auto propertiesIt = COLUMN_PROPERTIES.find(section);
137 138 if(propertiesIt != COLUMN_PROPERTIES.cend())
138 139 {
139 140 // Role is either DisplayRole or SizeHintRole
140 141 return (role == Qt::DisplayRole)
141 142 ? QVariant{propertiesIt->m_Name}
142 143 : QVariant{
143 144 QSize{propertiesIt->m_Width, propertiesIt->m_Height}};
144 145 }
145 146 }
146 147
147 148 return QVariant{};
148 149 }
149 150
150 151 Qt::ItemFlags VariableModel2::flags(const QModelIndex& index) const
151 152 {
152 153 return QAbstractTableModel::flags(index) | Qt::ItemIsDragEnabled |
153 154 Qt::ItemIsDropEnabled;
154 155 }
155 156
156 157 Qt::DropActions VariableModel2::supportedDropActions() const
157 158 {
158 159 return Qt::CopyAction | Qt::MoveAction;
159 160 }
160 161
161 162 Qt::DropActions VariableModel2::supportedDragActions() const
162 163 {
163 164 return Qt::CopyAction | Qt::MoveAction;
164 165 }
165 166
166 167 QStringList VariableModel2::mimeTypes() const
167 168 {
168 169 return {MIME_TYPE_VARIABLE_LIST, MIME_TYPE_TIME_RANGE};
169 170 }
170 171
171 172 QMimeData* VariableModel2::mimeData(const QModelIndexList& indexes) const
172 173 {
173 174 auto mimeData = new QMimeData;
174 175 std::vector<std::shared_ptr<Variable2>> variables;
175 176
176 177 DateTimeRange firstTimeRange;
177 178 for(const auto& index : indexes)
178 179 {
179 180 if(index.column() == 0)
180 181 { // only the first column
181 182 auto variable = _variables[index.row()];
182 183 if(variable.get() && index.isValid())
183 184 {
184 185 if(variables.size() == 0)
185 186 {
186 187 // Gets the range of the first variable
187 188 firstTimeRange = variable->range();
188 189 }
189 190 variables.push_back(variable);
190 191 }
191 192 }
192 193 }
193 194
194 195 auto variablesEncodedData = Variable2::mimeData(variables);
195 196 mimeData->setData(MIME_TYPE_VARIABLE_LIST, variablesEncodedData);
196 197
197 198 if(variables.size() == 1)
198 199 {
199 200 // No time range MIME data if multiple variables are dragged
200 201 auto timeEncodedData = TimeController::mimeDataForTimeRange(firstTimeRange);
201 202 mimeData->setData(MIME_TYPE_TIME_RANGE, timeEncodedData);
202 203 }
203 204
204 205 return mimeData;
205 206 }
206 207
207 208 bool VariableModel2::canDropMimeData(const QMimeData* data,
208 209 Qt::DropAction action, int row, int column,
209 210 const QModelIndex& parent) const
210 211 {
211 212 Q_UNUSED(column);
212 213 // drop of a product
213 214 return data->hasFormat(MIME_TYPE_PRODUCT_LIST) ||
214 215 (data->hasFormat(MIME_TYPE_TIME_RANGE) && parent.isValid() &&
215 216 !data->hasFormat(MIME_TYPE_VARIABLE_LIST));
216 217 }
217 218
218 219 bool VariableModel2::dropMimeData(const QMimeData* data, Qt::DropAction action,
219 220 int row, int column,
220 221 const QModelIndex& parent)
221 222 {
222 223 auto dropDone = false;
223 224
224 225 if(data->hasFormat(MIME_TYPE_PRODUCT_LIST))
225 226 {
226 227 auto productList = DataSourceController::productsDataForMimeData(
227 228 data->data(MIME_TYPE_PRODUCT_LIST));
228 229
229 230 for(auto metaData : productList)
230 231 {
231 232 emit createVariable(metaData.toHash());
232 233 }
233 234
234 235 dropDone = true;
235 236 }
236 237 else if(data->hasFormat(MIME_TYPE_TIME_RANGE) && parent.isValid())
237 238 {
238 239 auto variable = _variables[parent.row()];
239 240 auto range =
240 241 TimeController::timeRangeForMimeData(data->data(MIME_TYPE_TIME_RANGE));
241 242
242 243 emit asyncChangeRange(variable, range);
243 244
244 245 dropDone = true;
245 246 }
246 247
247 248 return dropDone;
248 249 }
249 250
250 251 void VariableModel2::variableUpdated(QUuid id) noexcept
251 252 {
252 253 emit dataChanged(QModelIndex(), QModelIndex());
253 254 }
254 255
255 256 void VariableModel2::variableAdded(const std::shared_ptr<Variable2>& variable)
256 257 {
257 if(!SciQLop::containers::contains(_variables, variable))
258 if(!cpp_utils::containers::contains(_variables, variable))
258 259 {
259 260 beginInsertRows(QModelIndex(), this->_variables.size(),
260 261 this->_variables.size());
261 262 this->_variables.push_back(variable);
262 263 endInsertRows();
263 264 connect(variable.get(), &Variable2::updated, this,
264 265 &VariableModel2::variableUpdated);
265 266 }
266 267 }
267 268
268 269 void VariableModel2::variableDeleted(const std::shared_ptr<Variable2>& variable)
269 270 {
270 271 auto it = std::find(_variables.begin(), _variables.end(), variable);
271 272 if(it != _variables.end())
272 273 {
273 274 auto index = std::distance(_variables.begin(), it);
274 275 beginRemoveRows(QModelIndex(), index, index);
275 276 _variables.erase(it);
276 277 endRemoveRows();
277 278 }
278 279 }
@@ -1,248 +1,248
1 #include <Common/Numeric.h>
1 #include <Numeric.h>
2 2 #include <Data/DateTimeRange.h>
3 3 #include <Data/DateTimeRangeHelper.h>
4 4 #include <QObject>
5 5 #include <QUuid>
6 6 #include <QtTest>
7 7 #include <limits>
8 8
9 9 Q_DECLARE_METATYPE(Seconds<double>);
10 10 Q_DECLARE_METATYPE(DateTimeRangeTransformation);
11 11
12 12 DateTimeRange computeZoom(QDateTime start, QDateTime stop, double zoom)
13 13 {
14 14 double start_epoch = start.toMSecsSinceEpoch();
15 15 double stop_epoch = stop.toMSecsSinceEpoch();
16 16 auto delta = stop_epoch - start_epoch;
17 17 auto dt_ms = (zoom - 1.) * (double(delta)) / 2.;
18 18 return DateTimeRange{(start_epoch - dt_ms) / 1000.,
19 19 (stop_epoch + dt_ms) / 1000.};
20 20 }
21 21
22 22 class TestDateTimeRange : public QObject
23 23 {
24 24 Q_OBJECT
25 25
26 26 private slots:
27 27 void testRangeDelta_data()
28 28 {
29 29 QTest::addColumn<QDateTime>("tstart");
30 30 QTest::addColumn<QDateTime>("tend");
31 31 QTest::addColumn<double>("expected");
32 32 auto now = QDateTime::currentDateTime();
33 33 auto yesterday = QDateTime::currentDateTime().addDays(-1);
34 34 QTest::newRow("No delta") << now << now << 0.;
35 35 QTest::newRow("One day delta") << yesterday << now << 60. * 60. * 24.;
36 36 QTest::newRow("Minus one day delta")
37 37 << now << yesterday << -60. * 60. * 24.;
38 38 }
39 39
40 40 void testRangeDelta()
41 41 {
42 42 QFETCH(QDateTime, tstart);
43 43 QFETCH(QDateTime, tend);
44 44 QFETCH(double, expected);
45 45 auto range = DateTimeRange::fromDateTime(tstart, tend);
46 46 double delta = range.delta();
47 47 // Since it is built from QDateTime don't expect better resolution
48 48 QVERIFY((delta - expected) <= 0.002);
49 49 }
50 50
51 51 void testRangeShift_data()
52 52 {
53 53 QTest::addColumn<DateTimeRange>("initial");
54 54 QTest::addColumn<Seconds<double>>("shift");
55 55 QTest::addColumn<DateTimeRange>("expected");
56 56 auto now = QDateTime::currentDateTime();
57 57 auto yestd = QDateTime::currentDateTime().addDays(-1);
58 58 auto range = DateTimeRange::fromDateTime(yestd, now);
59 59 QTest::newRow("No shift") << range << Seconds<double>{0.} << range;
60 60
61 61 QTest::newRow("One milisecond left")
62 62 << range << Seconds<double>{-.001}
63 63 << DateTimeRange::fromDateTime(yestd.addMSecs(-1.), now.addMSecs(-1.));
64 64 QTest::newRow("One milisecond right")
65 65 << range << Seconds<double>{.001}
66 66 << DateTimeRange::fromDateTime(yestd.addMSecs(1.), now.addMSecs(1.));
67 67 QTest::newRow("One second left")
68 68 << range << Seconds<double>{-1.}
69 69 << DateTimeRange::fromDateTime(yestd.addSecs(-1.), now.addSecs(-1.));
70 70 QTest::newRow("One second right")
71 71 << range << Seconds<double>{1.}
72 72 << DateTimeRange::fromDateTime(yestd.addSecs(1.), now.addSecs(1.));
73 73 QTest::newRow("One year left")
74 74 << range << Seconds<double>{-365. * 24. * 60. * 60.}
75 75 << DateTimeRange::fromDateTime(yestd.addSecs(-365 * 24 * 60 * 60),
76 76 now.addSecs(-365 * 24 * 60 * 60));
77 77 QTest::newRow("One year right")
78 78 << range << Seconds<double>{365. * 24. * 60. * 60.}
79 79 << DateTimeRange::fromDateTime(yestd.addSecs(365 * 24 * 60 * 60),
80 80 now.addSecs(365 * 24 * 60 * 60));
81 81 }
82 82
83 83 void testRangeShift()
84 84 {
85 85 QFETCH(DateTimeRange, initial);
86 86 QFETCH(Seconds<double>, shift);
87 87 QFETCH(DateTimeRange, expected);
88 88 QCOMPARE(initial + shift, expected);
89 89 }
90 90
91 91 void testRangeZoom_data()
92 92 {
93 93 QTest::addColumn<DateTimeRange>("initial");
94 94 QTest::addColumn<double>("zoom");
95 95 QTest::addColumn<DateTimeRange>("expected");
96 96 auto now = QDateTime::currentDateTime();
97 97 auto yestd = QDateTime::currentDateTime().addDays(-1);
98 98 auto range = DateTimeRange::fromDateTime(yestd, now);
99 99 QTest::newRow("No zoom") << range << 1. << range;
100 100
101 101 QTest::newRow("Zoom IN 0.001")
102 102 << range << 1.001 << computeZoom(yestd, now, 1.001);
103 103 QTest::newRow("Zoom OUT 0.001")
104 104 << range << 0.999 << computeZoom(yestd, now, 0.999);
105 105 }
106 106 void testRangeZoom()
107 107 {
108 108 QFETCH(DateTimeRange, initial);
109 109 QFETCH(double, zoom);
110 110 QFETCH(DateTimeRange, expected);
111 111 QCOMPARE(initial * zoom, expected);
112 112 }
113 113
114 114 void testRangeContains_data()
115 115 {
116 116 QTest::addColumn<DateTimeRange>("range");
117 117 QTest::addColumn<DateTimeRange>("range2");
118 118 QTest::addColumn<bool>("contains");
119 119 auto now = QDateTime::currentDateTime();
120 120 auto yestd = QDateTime::currentDateTime().addDays(-1);
121 121 auto range = DateTimeRange::fromDateTime(yestd, now);
122 122 QTest::newRow("Same range") << range << range << true;
123 123 QTest::newRow("Smaller range") << range << range * 0.8 << true;
124 124 QTest::newRow("Bigger range") << range << range * 1.2 << false;
125 125 QTest::newRow("Shifted range with overlap")
126 126 << range << range + Seconds<double>{1000.} << false;
127 127 QTest::newRow("Shifted range without overlap")
128 128 << range << range + Seconds<double>{24. * 60. * 60. * 10} << false;
129 129 }
130 130
131 131 void testRangeContains()
132 132 {
133 133 QFETCH(DateTimeRange, range);
134 134 QFETCH(DateTimeRange, range2);
135 135 QFETCH(bool, contains);
136 136 QCOMPARE(range.contains(range2), contains);
137 137 }
138 138
139 139 void testRangeIntersect_data()
140 140 {
141 141 QTest::addColumn<DateTimeRange>("range");
142 142 QTest::addColumn<DateTimeRange>("range2");
143 143 QTest::addColumn<bool>("contains");
144 144 auto now = QDateTime::currentDateTime();
145 145 auto yestd = QDateTime::currentDateTime().addDays(-1);
146 146 auto tomorrow = QDateTime::currentDateTime().addDays(1);
147 147 auto range = DateTimeRange::fromDateTime(yestd, now);
148 148 auto range2 = DateTimeRange::fromDateTime(now, tomorrow);
149 149 QTest::newRow("Same range") << range << range << true;
150 150 QTest::newRow("Smaller range") << range << range * 0.8 << true;
151 151 QTest::newRow("Bigger range") << range << range * 1.2 << true;
152 152 QTest::newRow("Shifted range with overlap")
153 153 << range << range + Seconds<double>{1000.} << true;
154 154 QTest::newRow("Shifted range with overlaping boundary")
155 155 << range << range2 << true;
156 156 QTest::newRow("Shifted range .1 seonds outside")
157 157 << range << range2 + Seconds<double>{.1} << false;
158 158 QTest::newRow("Shifted range without overlap")
159 159 << range << range + Seconds<double>{24. * 60. * 60. * 10} << false;
160 160 }
161 161
162 162 void testRangeIntersect()
163 163 {
164 164 QFETCH(DateTimeRange, range);
165 165 QFETCH(DateTimeRange, range2);
166 166 QFETCH(bool, contains);
167 167 QCOMPARE(range.intersect(range2), contains);
168 168 }
169 169
170 170 void testRangeTransformations_data()
171 171 {
172 172 QTest::addColumn<DateTimeRange>("range1");
173 173 QTest::addColumn<DateTimeRange>("range2");
174 174 QTest::addColumn<DateTimeRangeTransformation>("transformation");
175 175 auto now = QDateTime::currentDateTime();
176 176 auto yestd = QDateTime::currentDateTime().addDays(-1);
177 177 auto range = DateTimeRange::fromDateTime(yestd, now);
178 178
179 179 QTest::newRow("Same range")
180 180 << range << range
181 181 << DateTimeRangeTransformation{1., Seconds<double>{0.}};
182 182
183 183 QTest::newRow("Transformatio 1.1x + 10s")
184 184 << range << range * 1.1 + Seconds<double>{10.}
185 185 << DateTimeRangeTransformation{1.1, Seconds<double>{10.}};
186 186
187 187 QTest::newRow("Transformatio 1.x + 10s")
188 188 << range << range * 1. + Seconds<double>{10.}
189 189 << DateTimeRangeTransformation{1., Seconds<double>{10.}};
190 190
191 191 QTest::newRow("Transformatio 1.1x + 0s")
192 192 << range << range * 1.1 + Seconds<double>{0.}
193 193 << DateTimeRangeTransformation{1.1, Seconds<double>{0.}};
194 194
195 195 QTest::newRow("Transformatio 0.9x - 10s")
196 196 << range << range * 0.9 + Seconds<double>{-10.}
197 197 << DateTimeRangeTransformation{0.9, Seconds<double>{-10.}};
198 198 }
199 199 void testRangeTransformations()
200 200 {
201 201 QFETCH(DateTimeRange, range1);
202 202 QFETCH(DateTimeRange, range2);
203 203 QFETCH(DateTimeRangeTransformation, transformation);
204 204 auto computed_tr =
205 205 DateTimeRangeHelper::computeTransformation(range1, range2).value();
206 206 QCOMPARE(computed_tr, transformation);
207 207 QCOMPARE(range1.transform(transformation), range2);
208 208 QCOMPARE(range1.transform(computed_tr), range2);
209 209 }
210 210
211 211 void testRangeDiff_data()
212 212 {
213 213 QTest::addColumn<DateTimeRange>("source");
214 214 QTest::addColumn<DateTimeRange>("destination");
215 215 QTest::addColumn<int>("count");
216 216 auto now = QDateTime::currentDateTime();
217 217 auto yestd = QDateTime::currentDateTime().addDays(-1);
218 218 auto range = DateTimeRange::fromDateTime(yestd, now);
219 219 QTest::newRow("Same range") << range << range << 0;
220 220 QTest::newRow("No missing data (zoom in)") << range << range * 0.9 << 0;
221 221 QTest::newRow("Missing data on both sides (zoom out)")
222 222 << range << range * 2. << 2;
223 223 QTest::newRow("Missing data on left side (pan left)")
224 224 << range << range - Seconds<double>{1000.} << 1;
225 225 QTest::newRow("Missing data on right side (pan right)")
226 226 << range << range + Seconds<double>{1000.} << 1;
227 227 QTest::newRow("Missing data on right side no intersect (big pan right)")
228 228 << range << range + Seconds<double>{24. * 60. * 60. * 2} << 1;
229 229 QTest::newRow("Missing data on left side no intersect (big pan left)")
230 230 << range << range - Seconds<double>{24. * 60. * 60. * 2} << 1;
231 231 }
232 232
233 233 void testRangeDiff()
234 234 {
235 235 QFETCH(DateTimeRange, source);
236 236 QFETCH(DateTimeRange, destination);
237 237 QFETCH(int, count);
238 238 auto diff = destination - source;
239 239 QCOMPARE(diff.size(), count);
240 240 for(const auto& range : diff)
241 241 {
242 242 QVERIFY(range.delta().value > 0.);
243 243 }
244 244 }
245 245 };
246 246 QTEST_MAIN(TestDateTimeRange)
247 247
248 248 #include "TestDateTimeRange.moc"
@@ -1,157 +1,157
1 #include <Common/containers.h>
1 #include <containers/algorithms.hpp>
2 2 #include <Data/DataProviderParameters.h>
3 3 #include <Data/DateTimeRange.h>
4 4 #include <Data/IDataProvider.h>
5 5 #include <QObject>
6 6 #include <QtTest>
7 7 #include <TestUtils/TestProviders.h>
8 8 #include <Variable/VariableController2.h>
9 9 #include <algorithm>
10 10 #include <cmath>
11 11 #include <numeric>
12 12
13 13 #define TEST_VC2_FIXTURE(slope) \
14 14 VariableController2 vc; \
15 15 auto provider = std::make_shared<SimpleRange<slope>>();
16 16
17 17 #define TEST_VC2_CREATE_DEFAULT_VAR(name) \
18 18 auto range = DateTimeRange::fromDateTime(QDate(2018, 8, 7), QTime(14, 00), \
19 19 QDate(2018, 8, 7), QTime(16, 00)); \
20 20 auto name = vc.createVariable("name", {}, provider, range); \
21 21 while(!vc.isReady(name)) \
22 22 QCoreApplication::processEvents();
23 23
24 24 Q_DECLARE_METATYPE(DateTimeRangeTransformation);
25 25
26 26 class TestVariableController2 : public QObject
27 27 {
28 28 Q_OBJECT
29 29 public:
30 30 explicit TestVariableController2(QObject* parent = nullptr) : QObject(parent)
31 31 {}
32 32 signals:
33 33
34 34 private slots:
35 35 void initTestCase() {}
36 36 void cleanupTestCase() {}
37 37
38 38 void testCreateVariable()
39 39 {
40 40 TEST_VC2_FIXTURE(2);
41 41 auto range = DateTimeRange::fromDateTime(QDate(2018, 8, 7), QTime(14, 00),
42 42 QDate(2018, 8, 7), QTime(16, 00));
43 43 bool callbackCalled = false;
44 44 connect(&vc, &VariableController2::variableAdded,
45 45 [&callbackCalled](std::shared_ptr<Variable2>) {
46 46 callbackCalled = true;
47 47 });
48 48 QVERIFY(!callbackCalled);
49 49 auto var1 = vc.createVariable("var1", {}, provider, range);
50 QVERIFY(SciQLop::containers::contains(vc.variables(), var1));
50 QVERIFY(cpp_utils::containers::contains(vc.variables(), var1));
51 51 QVERIFY(callbackCalled);
52 52 }
53 53
54 54 void testDeleteVariable()
55 55 {
56 56 TEST_VC2_FIXTURE(1);
57 57 auto range = DateTimeRange::fromDateTime(QDate(2018, 8, 7), QTime(14, 00),
58 58 QDate(2018, 8, 7), QTime(16, 00));
59 59 bool callbackCalled = false;
60 60 connect(&vc, &VariableController2::variableDeleted,
61 61 [&callbackCalled](std::shared_ptr<Variable2>) {
62 62 callbackCalled = true;
63 63 });
64 64 auto var1 = vc.createVariable("var1", {}, provider, range);
65 65 while(!vc.isReady(var1))
66 66 QCoreApplication::processEvents();
67 QVERIFY(SciQLop::containers::contains(vc.variables(), var1));
67 QVERIFY(cpp_utils::containers::contains(vc.variables(), var1));
68 68 QVERIFY(!callbackCalled);
69 69 vc.deleteVariable(var1);
70 QVERIFY(!SciQLop::containers::contains(vc.variables(), var1));
70 QVERIFY(!cpp_utils::containers::contains(vc.variables(), var1));
71 71 QVERIFY(callbackCalled);
72 72 }
73 73
74 74 void testGetData()
75 75 {
76 76 TEST_VC2_FIXTURE(10);
77 77 TEST_VC2_CREATE_DEFAULT_VAR(var1);
78 78 check_variable_state<RangeType<10>>(var1, range);
79 79 }
80 80
81 81 void testZoom_data()
82 82 {
83 83 QTest::addColumn<double>("zoom");
84 84 QTest::newRow("Zoom IN 10x") << .1;
85 85 QTest::newRow("Zoom OUT 10x") << 10.;
86 86 QTest::newRow("Zoom IN 1x") << 1.;
87 87 }
88 88 void testZoom()
89 89 {
90 90 TEST_VC2_FIXTURE(100);
91 91 TEST_VC2_CREATE_DEFAULT_VAR(var1);
92 92 check_variable_state<RangeType<100>>(var1, range);
93 93
94 94 QFETCH(double, zoom);
95 95 range *= zoom;
96 96 vc.changeRange(var1, range);
97 97 check_variable_state<RangeType<100>>(var1, range);
98 98 }
99 99
100 100 void testPan_data()
101 101 {
102 102 QTest::addColumn<double>("pan");
103 103 QTest::newRow("Right 1000 seconds") << 1000.;
104 104 QTest::newRow("Left 1000 seconds") << -1000.;
105 105 QTest::newRow("Right 0.1 seconds") << .1;
106 106 QTest::newRow("Left 0.1 seconds") << -.1;
107 107 }
108 108 void testPan()
109 109 {
110 110 TEST_VC2_FIXTURE(10);
111 111 TEST_VC2_CREATE_DEFAULT_VAR(var1);
112 112 check_variable_state<RangeType<10>>(var1, range);
113 113
114 114 QFETCH(double, pan);
115 115
116 116 range += Seconds<double>{pan};
117 117 vc.changeRange(var1, range);
118 118 check_variable_state<RangeType<10>>(var1, range);
119 119 }
120 120
121 121 void testCache_data()
122 122 {
123 123 QTest::addColumn<DateTimeRangeTransformation>("transformation");
124 124 QTest::addColumn<int>("expectedIncrement");
125 125 QTest::newRow("zoom in")
126 126 << DateTimeRangeTransformation{0.8, Seconds<double>(0.)} << 0;
127 127 QTest::newRow("tiny zoom out")
128 128 << DateTimeRangeTransformation{1.01, Seconds<double>(0.)} << 0;
129 129 QTest::newRow("just under cache zoom out")
130 130 << DateTimeRangeTransformation{2.0 / 1.1, Seconds<double>(0.)} << 0;
131 131 QTest::newRow("just over cache zoom out")
132 132 << DateTimeRangeTransformation{2.001 / 1.1, Seconds<double>(0.)} << 2;
133 133 QTest::newRow("tiny pan left")
134 134 << DateTimeRangeTransformation{1., Seconds<double>(-100.)} << 0;
135 135 QTest::newRow("tiny pan right")
136 136 << DateTimeRangeTransformation{1., Seconds<double>(100.)} << 0;
137 137 }
138 138 void testCache()
139 139 {
140 140 QSKIP("The cache is disabled for now");
141 141 TEST_VC2_FIXTURE(10);
142 142 TEST_VC2_CREATE_DEFAULT_VAR(var1);
143 143 check_variable_state<RangeType<10>>(var1, range);
144 144
145 145 QFETCH(DateTimeRangeTransformation, transformation);
146 146 QFETCH(int, expectedIncrement);
147 147 auto initialCount = provider->callCounter;
148 148 range = range.transform(transformation);
149 149 vc.changeRange(var1, range);
150 150 check_variable_state<RangeType<10>>(var1, range);
151 151 QCOMPARE(provider->callCounter - initialCount, expectedIncrement);
152 152 }
153 153 };
154 154
155 155 QTEST_MAIN(TestVariableController2)
156 156
157 157 #include "TestVariableController2.moc"
@@ -1,64 +1,64
1 #include <Common/containers.h>
1 #include <containers/algorithms.hpp>
2 2 #include <Data/DataProviderParameters.h>
3 3 #include <Data/DateTimeRange.h>
4 4 #include <Data/IDataProvider.h>
5 5 #include <Data/ScalarTimeSerie.h>
6 6 #include <QObject>
7 7 #include <QtTest>
8 8 #include <TestUtils/TestProviders.h>
9 9 #include <Variable/VariableController2.h>
10 10 #include <algorithm>
11 11 #include <cmath>
12 12 #include <numeric>
13 13
14 14 #define TEST_VC2_FIXTURE(slope) \
15 15 VariableController2 vc; \
16 16 auto provider = std::make_shared<SimpleRange<slope>>();
17 17
18 18 #define TEST_VC2_CREATE_DEFAULT_VARS(name1, name2, name3) \
19 19 auto range = DateTimeRange::fromDateTime(QDate(2018, 8, 7), QTime(14, 00), \
20 20 QDate(2018, 8, 7), QTime(16, 00)); \
21 21 auto name1 = vc.createVariable("name1", {}, provider, range); \
22 22 auto name2 = vc.createVariable("name1", {}, provider, range); \
23 23 auto name3 = vc.createVariable("name1", {}, provider, range); \
24 24 vc.synchronize(name1, name2);
25 25
26 26 class TestVariableController2Async : public QObject
27 27 {
28 28 Q_OBJECT
29 29 public:
30 30 explicit TestVariableController2Async(QObject* parent = nullptr)
31 31 : QObject(parent)
32 32 {}
33 33 signals:
34 34
35 35 private slots:
36 36 void initTestCase() {}
37 37 void cleanupTestCase() {}
38 38
39 39 void testSimplePan()
40 40 {
41 41 TEST_VC2_FIXTURE(2);
42 42 auto range = DateTimeRange::fromDateTime(QDate(2018, 8, 7), QTime(14, 00),
43 43 QDate(2018, 8, 7), QTime(16, 00));
44 44 int variableUpdated = 0;
45 45 auto var1 = vc.createVariable("var1", {}, provider, range);
46 46 auto var2 = vc.createVariable("var2", {}, provider, range);
47 47 auto var3 = vc.createVariable("var3", {}, provider, range);
48 48 connect(&(*var2), &Variable2::updated,
49 49 [&variableUpdated]() { variableUpdated += 1; });
50 50 vc.synchronize(var1, var2);
51 51 vc.asyncChangeRange(var1, range + Seconds<double>{10000.});
52 52 vc.asyncChangeRange(var1, range + Seconds<double>{50000.});
53 53 vc.asyncChangeRange(var1, range + Seconds<double>{100000.});
54 54 vc.asyncChangeRange(var1, range + Seconds<double>{150000.});
55 55 while(!vc.isReady(var1) || !vc.isReady(var2))
56 56 {
57 57 QCoreApplication::processEvents();
58 58 }
59 59 }
60 60 };
61 61
62 62 QTEST_MAIN(TestVariableController2Async)
63 63
64 64 #include "TestVariableController2Async.moc"
@@ -1,138 +1,138
1 #include <Common/containers.h>
1 #include <containers/algorithms.hpp>
2 2 #include <Data/DataProviderParameters.h>
3 3 #include <Data/DateTimeRange.h>
4 4 #include <Data/IDataProvider.h>
5 5 #include <Data/ScalarTimeSerie.h>
6 6 #include <QObject>
7 7 #include <QtTest>
8 8 #include <TestUtils/TestProviders.h>
9 9 #include <Variable/VariableController2.h>
10 10 #include <algorithm>
11 11 #include <cmath>
12 12 #include <numeric>
13 13
14 14 #define TEST_VC2_FIXTURE(slope) \
15 15 VariableController2 vc; \
16 16 auto provider = std::make_shared<SimpleRange<slope>>();
17 17
18 18 #define TEST_VC2_CREATE_DEFAULT_VARS(name1, name2, name3) \
19 19 auto range = DateTimeRange::fromDateTime(QDate(2018, 8, 7), QTime(14, 00), \
20 20 QDate(2018, 8, 7), QTime(16, 00)); \
21 21 auto name1 = vc.createVariable("name1", {}, provider, range); \
22 22 while(!vc.isReady(name1)) \
23 23 QCoreApplication::processEvents(); \
24 24 auto name2 = vc.cloneVariable(name1); \
25 25 auto name3 = vc.cloneVariable(name2); \
26 26 vc.synchronize(name1, name2); \
27 27 while(!vc.isReady(name1)) \
28 28 QCoreApplication::processEvents();
29 29
30 30 class TestVariableController2WithSync : public QObject
31 31 {
32 32 Q_OBJECT
33 33 public:
34 34 explicit TestVariableController2WithSync(QObject* parent = nullptr)
35 35 : QObject(parent)
36 36 {}
37 37 signals:
38 38
39 39 private slots:
40 40 void initTestCase() {}
41 41 void cleanupTestCase() {}
42 42
43 43 void testCreateVariable()
44 44 {
45 45 TEST_VC2_FIXTURE(2);
46 46 auto range = DateTimeRange::fromDateTime(QDate(2018, 8, 7), QTime(14, 00),
47 47 QDate(2018, 8, 7), QTime(16, 00));
48 48 bool callbackCalled = false;
49 49 connect(&vc, &VariableController2::variableAdded,
50 50 [&callbackCalled](std::shared_ptr<Variable2>) {
51 51 callbackCalled = true;
52 52 });
53 53 QVERIFY(!callbackCalled);
54 54 auto var1 = vc.createVariable("var1", {}, provider, range);
55 QVERIFY(SciQLop::containers::contains(vc.variables(), var1));
55 QVERIFY(cpp_utils::containers::contains(vc.variables(), var1));
56 56 QVERIFY(callbackCalled);
57 57 }
58 58
59 59 void testDeleteVariable()
60 60 {
61 61 TEST_VC2_FIXTURE(1);
62 62 auto range = DateTimeRange::fromDateTime(QDate(2018, 8, 7), QTime(14, 00),
63 63 QDate(2018, 8, 7), QTime(16, 00));
64 64 bool callbackCalled = false;
65 65 connect(&vc, &VariableController2::variableDeleted,
66 66 [&callbackCalled](std::shared_ptr<Variable2>) {
67 67 callbackCalled = true;
68 68 });
69 69 auto var1 = vc.createVariable("var1", {}, provider, range);
70 QVERIFY(SciQLop::containers::contains(vc.variables(), var1));
70 QVERIFY(cpp_utils::containers::contains(vc.variables(), var1));
71 71 QVERIFY(!callbackCalled);
72 72 while(!vc.isReady(var1))
73 73 {
74 74 qApp->processEvents();
75 75 }
76 76 vc.deleteVariable(var1);
77 QVERIFY(!SciQLop::containers::contains(vc.variables(), var1));
77 QVERIFY(!cpp_utils::containers::contains(vc.variables(), var1));
78 78 QVERIFY(callbackCalled);
79 79 }
80 80
81 81 void testGetData()
82 82 {
83 83 TEST_VC2_FIXTURE(10);
84 84 TEST_VC2_CREATE_DEFAULT_VARS(var1, var2, var3);
85 85 check_variable_state<RangeType<10>>(var1, range);
86 86 auto newRange = var2->range() * 1.5 + Seconds<double>{1000.};
87 87 vc.changeRange(var2, newRange);
88 88 check_variable_state<RangeType<10>>(var1, newRange);
89 89 check_variable_state<RangeType<10>>(var2, newRange);
90 90 check_variable_state<RangeType<10>>(var3, range);
91 91 }
92 92
93 93 void testZoom_data()
94 94 {
95 95 QTest::addColumn<double>("zoom");
96 96 QTest::newRow("Zoom IN 10x") << .1;
97 97 QTest::newRow("Zoom OUT 10x") << 10.;
98 98 QTest::newRow("Zoom IN 1x") << 1.;
99 99 }
100 100 void testZoom()
101 101 {
102 102 TEST_VC2_FIXTURE(100);
103 103 TEST_VC2_CREATE_DEFAULT_VARS(var1, var2, var3);
104 104 check_variable_state<RangeType<100>>(var1, range);
105 105
106 106 QFETCH(double, zoom);
107 107 range *= zoom;
108 108 vc.changeRange(var1, range);
109 109 check_variable_state<RangeType<100>>(var1, range);
110 110 check_variable_state<RangeType<100>>(var2, range);
111 111 }
112 112
113 113 void testPan_data()
114 114 {
115 115 QTest::addColumn<double>("pan");
116 116 QTest::newRow("Right 1000 seconds") << 1000.;
117 117 QTest::newRow("Left 1000 seconds") << -1000.;
118 118 QTest::newRow("Right 0.1 seconds") << .1;
119 119 QTest::newRow("Left 0.1 seconds") << -.1;
120 120 }
121 121 void testPan()
122 122 {
123 123 TEST_VC2_FIXTURE(10);
124 124 TEST_VC2_CREATE_DEFAULT_VARS(var1, var2, var3);
125 125 check_variable_state<RangeType<10>>(var1, range);
126 126
127 127 QFETCH(double, pan);
128 128
129 129 range += Seconds<double>{pan};
130 130 vc.changeRange(var1, range);
131 131 check_variable_state<RangeType<10>>(var1, range);
132 132 check_variable_state<RangeType<10>>(var2, range);
133 133 }
134 134 };
135 135
136 136 QTEST_MAIN(TestVariableController2WithSync)
137 137
138 138 #include "TestVariableController2WithSync.moc"
@@ -1,83 +1,73
1 1 TestUtils = library('TestUtils', 'TestUtils/TestProviders.h', 'TestUtils/TestProviders.cpp',
2 2 dependencies : [sciqlop_core, qt5test]
3 3 )
4 4
5 5
6 6 TestUtils_dep = declare_dependency(link_with : TestUtils,
7 7 dependencies : [sciqlop_core, qt5test])
8 8
9 9
10 10
11 11 tests = [
12 12 {
13 'name':'TestStringUtils',
14 'sources': ['Common/TestStringUtils.cpp'],
15 'deps': [sciqlop_core, qt5test]
16 },
17 {
18 'name':'TestContainers',
19 'sources': ['Common/TestContainers.cpp'],
20 'deps': [sciqlop_core, qt5test]
21 },
22 {
23 13 'name':'TestSyncGroup',
24 14 'sources': ['Variable/TestSyncGroup.cpp'],
25 15 'deps': [sciqlop_core, qt5test]
26 16 },
27 17 {
28 18 'name':'TestDateTimeRange',
29 19 'sources': ['Data/TestDateTimeRange.cpp'],
30 20 'deps': [sciqlop_core, qt5test]
31 21 },
32 22 {
33 23 'name':'TestDataSourceController',
34 24 'sources': [
35 25 'DataSource/TestDataSourceController.cpp',
36 26 'DataSource/DataSourceItemBuilder.cpp'
37 27 ],
38 28 'deps': [sciqlop_core, qt5test]
39 29 },
40 30 {
41 31 'name':'TestDataSourceItem',
42 32 'sources': [
43 33 'DataSource/TestDataSourceItem.cpp',
44 34 'DataSource/DataSourceItemBuilder.cpp'
45 35 ],
46 36 'deps': [sciqlop_core, qt5test]
47 37 },
48 38 {
49 39 'name':'TestVariable',
50 40 'sources': ['Variable/TestVariable.cpp'],
51 41 'deps': [sciqlop_core, qt5test]
52 42 },
53 43 {
54 44 'name':'TestDownloader',
55 45 'sources': ['Network/TestDownloader.cpp'],
56 46 'deps': [sciqlop_core, qt5test, qt5Concurrent]
57 47 },
58 48 {
59 49 'name':'TestVariableController2',
60 50 'sources': ['Variable/TestVariableController2.cpp'],
61 51 'deps': [sciqlop_core, qt5test, qt5Concurrent, TestUtils_dep]
62 52 },
63 53 {
64 54 'name':'TestVariableController2Async',
65 55 'sources': ['Variable/TestVariableController2Async.cpp'],
66 56 'deps': [sciqlop_core, qt5test, qt5Concurrent, TestUtils_dep]
67 57 },
68 58 {
69 59 'name':'TestVariableController2WithSync',
70 60 'sources': ['Variable/TestVariableController2WithSync.cpp'],
71 61 'deps': [sciqlop_core, qt5test, qt5Concurrent, TestUtils_dep]
72 62 }
73 63 ]
74 64
75 65 foreach unit_test : tests
76 66 test_moc_files = qt5.preprocess(moc_sources : unit_test['sources'])
77 67 test_exe = executable(unit_test['name'],unit_test['sources'] , test_moc_files,
78 68 dependencies : unit_test['deps'],
79 69 cpp_args : ['-DCORE_TESTS_RESOURCES_DIR="'+meson.current_source_dir()+'/../tests-resources"']
80 70 )
81 71 test('Test-' + unit_test['name'], test_exe, args: ['-teamcity', '-o', '@0@.teamcity.txt'.format(unit_test['name'])])
82 72 endforeach
83 73
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
General Comments 0
You need to be logged in to leave comments. Login now