##// END OF EJS Templates
Few methods added to ease integration in existing GUI...
jeandet -
r25:5f4f9560990b
parent child
Show More
@@ -1,65 +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 15 #include <Common/Numeric.h>
16 16 #include <Data/DateTimeRange.h>
17 17
18 enum class TransformationType { ZoomOut, ZoomIn, PanRight, PanLeft, Unknown };
19
18 20 namespace DateTimeRangeHelper {
19 21
20 22
21 23 inline bool isnan(const DateTimeRange& range)
22 24 {
23 25 return std::isnan(range.m_TStart) && std::isnan(range.m_TEnd);
24 26 }
25 27
26 28 inline bool hasnan(const DateTimeRange& range)
27 29 {
28 30 return std::isnan(range.m_TStart) || std::isnan(range.m_TEnd);
29 31 }
30 32
31 33 inline bool isPureShift(const DateTimeRange& range1, const DateTimeRange& range2)
32 34 {
33 35 return SciQLop::numeric::almost_equal<double>(range1.delta(), range2.delta(), 1)
34 36 && !SciQLop::numeric::almost_equal(range1.m_TStart, range2.m_TStart, 1);
35 37 }
36 38
37 39 inline bool isPureZoom(const DateTimeRange& range1, const DateTimeRange& range2)
38 40 {
39 41 return !SciQLop::numeric::almost_equal<double>(range1.delta(),range2.delta(),1)&&
40 42 SciQLop::numeric::almost_equal<double>(range1.center(), range2.center(),1);
41 43 }
42 44
45
43 46 /**
44 47 * @brief computeTransformation such as range2 = zoom*range1 + shift
45 48 * @param range1
46 49 * @param range2
47 50 * @return trnaformation applied to range1 to get range2 or an object of type
48 51 * InvalidDateTimeRangeTransformation if the transformation has NaN or forbiden values
49 52 */
50 53 inline std::optional<DateTimeRangeTransformation>
51 54 computeTransformation(const DateTimeRange& range1, const DateTimeRange& range2)
52 55 {
53 56 std::optional<DateTimeRangeTransformation> transformation;
54 57 double zoom = range2.delta()/range1.delta();
55 58 Seconds<double> shift = range2.center() - (range1*zoom).center();
56 59 bool zoomValid = zoom!=0. && !std::isnan(zoom) && !std::isinf(zoom);
57 60 bool shiftValid = !std::isnan(shift.value) && !std::isinf(shift.value);
58 61 if(zoomValid && shiftValid)
59 62 transformation = DateTimeRangeTransformation{zoom, shift};
60 63 return transformation;
61 64 }
62 65
66 inline TransformationType getTransformationType(const DateTimeRange& range1, const DateTimeRange& range2)
67 {
68 auto transformation = computeTransformation (range1,range2);
69 if(transformation.has_value ())
70 {
71 if(SciQLop::numeric::almost_equal(transformation->zoom,1.))
72 {
73 if(transformation->shift > 0.)
74 return TransformationType::PanRight;
75 return TransformationType::PanLeft;
76 }
77 if(transformation->zoom > 0.)
78 return TransformationType::ZoomOut;
79 return TransformationType::ZoomIn;
80 }
81 return TransformationType::Unknown;
82 }
83
63 84 }
64 85
65 86 #endif // SCIQLOP_DATETIMERANGEHELPER_H
@@ -1,46 +1,52
1 1 #include <memory>
2 2 #include <vector>
3 3 #include <set>
4 4 #include <QHash>
5 5 #include <QObject>
6 6 #include <QMutexLocker>
7 7 #include <QUuid>
8 8 #include <QByteArray>
9 9 #include <QItemSelectionModel>
10 10 #include <Common/spimpl.h>
11 11 #include <Variable/Variable.h>
12 12 //#include <Variable/VariableSynchronizationGroup.h>
13 13 #include <Variable/VariableModel.h>
14 14 #include <Data/IDataProvider.h>
15 15 #include "Data/DateTimeRange.h"
16 16
17 17 class VariableController2: public QObject
18 18 {
19 19 class VariableController2Private;
20 20 Q_OBJECT
21 21
22 22 spimpl::unique_impl_ptr<VariableController2Private> impl;
23 23
24 24 public:
25 25 explicit VariableController2();
26 26 std::shared_ptr<Variable> createVariable(const QString &name, const QVariantHash &metadata,
27 27 const std::shared_ptr<IDataProvider>& provider,
28 28 const DateTimeRange &range);
29 29
30 30 std::shared_ptr<Variable> cloneVariable(const std::shared_ptr<Variable>& variable);
31 31 void deleteVariable(const std::shared_ptr<Variable>& variable);
32 32 void changeRange(const std::shared_ptr<Variable>& variable, const DateTimeRange& r);
33 33 void asyncChangeRange(const std::shared_ptr<Variable>& variable, const DateTimeRange& r);
34 34 const std::vector<std::shared_ptr<Variable>> variables();
35 35
36 36 bool isReady(const std::shared_ptr<Variable>& variable);
37 37
38 38 void synchronize(const std::shared_ptr<Variable>& var, const std::shared_ptr<Variable>& with);
39 39
40 //This should be somewhere else VC has nothing to do with MIMEData
40 41 QByteArray mimeData(const std::vector<std::shared_ptr<Variable>> &variables) const;
42 const std::vector<std::shared_ptr<Variable>> variables(QByteArray mimeData);
43
44 const std::shared_ptr<Variable>& operator[] (int index) const;
45 std::shared_ptr<Variable> operator[] (int index);
46
41 47
42 48 signals:
43 49 void variableAdded(const std::shared_ptr<Variable>&);
44 50 void variableDeleted(const std::shared_ptr<Variable>&);
45 51
46 52 };
@@ -1,358 +1,403
1 1 #include <QQueue>
2 2 #include <QThreadPool>
3 3 #include <QRunnable>
4 4 #include <QObject>
5 5 #include <QDataStream>
6 6
7 7 #include "Variable/VariableController2.h"
8 8 #include "Variable/VariableSynchronizationGroup2.h"
9 9 #include <Common/containers.h>
10 10 #include <Common/debug.h>
11 11 #include <Data/DataProviderParameters.h>
12 12 #include <Data/DateTimeRangeHelper.h>
13 13 #include <Data/DateTimeRange.h>
14 14 #include <Variable/VariableCacheStrategyFactory.h>
15 15 #include <Variable/private/VCTransaction.h>
16 16 #include <QCoreApplication>
17 17
18 18
19 19
20 20 class VariableController2::VariableController2Private
21 21 {
22 22 struct threadSafeVaraiblesMaps
23 23 {
24 24 inline void addVariable(const std::shared_ptr<Variable>& variable, const std::shared_ptr<IDataProvider>& provider, const std::shared_ptr<VariableSynchronizationGroup2>& synchronizationGroup)
25 25 {
26 26 QWriteLocker lock{&_lock};
27 27 _variables[*variable] = variable;
28 28 _providers[*variable] = provider;
29 29 _synchronizationGroups[*variable] = synchronizationGroup;
30 30 }
31 31
32 32 inline void removeVariable(const std::shared_ptr<Variable>& variable)
33 33 {
34 34 QWriteLocker lock{&_lock};
35 35 _variables.remove(*variable);
36 36 _providers.remove(*variable);
37 37 _synchronizationGroups.remove(*variable);
38 38 }
39 39
40 40 inline void synchronize(const std::shared_ptr<Variable>& variable, const std::optional<std::shared_ptr<Variable>>& with)
41 41 {
42 42 QWriteLocker lock{&_lock};
43 43 if(with.has_value())
44 44 {
45 45 auto newGroup = _synchronizationGroups[*with.value()];
46 46 newGroup->addVariable(*variable);
47 47 _synchronizationGroups[*variable] = newGroup;
48 48 }
49 49 else
50 50 {
51 51 _synchronizationGroups[*variable] = std::make_shared<VariableSynchronizationGroup2>(*variable);
52 52 }
53 53 }
54 54
55 55 inline std::shared_ptr<Variable> variable(QUuid variable)
56 56 {
57 57 QReadLocker lock{&_lock};
58 58 [[unlikely]]
59 59 if(!_variables.contains(variable))
60 60 SCIQLOP_ERROR(threadSafeVaraiblesMaps,"Unknown Variable");
61 61 return _variables[variable];
62 62 }
63 63
64 inline std::shared_ptr<Variable> variable(int index)
65 {
66 QReadLocker lock{&_lock};
67 [[unlikely]]
68 if(!_variables.size() > index)
69 SCIQLOP_ERROR(threadSafeVaraiblesMaps,"Index is out of bounds");
70 return _variables.values()[index];
71 }
72
64 73 inline const std::vector<std::shared_ptr<Variable>> variables()
65 74 {
66 75 std::vector<std::shared_ptr<Variable>> vars;
67 76 QReadLocker lock{&_lock};
68 77 for(const auto &var:_variables)
69 78 {
70 79 vars.push_back(var);
71 80 }
72 81 return vars;
73 82 }
74 83
75 84 inline std::shared_ptr<IDataProvider> provider(QUuid variable)
76 85 {
77 86 QReadLocker lock{&_lock};
78 87 [[unlikely]]
79 88 if(!_providers.contains(variable))
80 89 SCIQLOP_ERROR(threadSafeVaraiblesMaps,"Unknown Variable");
81 90 return _providers[variable];
82 91 }
83 92
84 93 inline std::shared_ptr<VariableSynchronizationGroup2> group(QUuid variable)
85 94 {
86 95 QReadLocker lock{&_lock};
87 96 [[unlikely]]
88 97 if(!_synchronizationGroups.contains(variable))
89 98 SCIQLOP_ERROR(threadSafeVaraiblesMaps,"Unknown Variable");
90 99 return _synchronizationGroups[variable];
91 100 }
92 101
93 102 inline bool has(const std::shared_ptr<Variable>& variable)
94 103 {
95 104 QReadLocker lock{&_lock};
96 105 return _variables.contains(*variable);
97 106 }
98 107
99 108 private:
100 109 QMap<QUuid,std::shared_ptr<Variable>> _variables;
101 110 QMap<QUuid,std::shared_ptr<IDataProvider>> _providers;
102 111 QMap<QUuid,std::shared_ptr<VariableSynchronizationGroup2>> _synchronizationGroups;
103 112 QReadWriteLock _lock{QReadWriteLock::Recursive};
104 113 }_maps;
105 114 QThreadPool _ThreadPool;
106 115 VCTransactionsQueues _transactions;
107 116 std::unique_ptr<VariableCacheStrategy> _cacheStrategy;
108 117
109 118 void _transactionComplete(QUuid group, std::shared_ptr<VCTransaction> transaction)
110 119 {
111 120 if(transaction->done())
112 121 {
113 122 _transactions.complete(group);
114 123 }
115 124 this->_processTransactions();
116 125 }
117 126 void _processTransactions()
118 127 {
119 128 auto nextTransactions = _transactions.nextTransactions();
120 129 auto pendingTransactions = _transactions.pendingTransactions();
121 130 for( auto [groupID, newTransaction] : nextTransactions)
122 131 {
123 132 if(newTransaction.has_value() && !pendingTransactions[groupID].has_value())
124 133 {
125 134 _transactions.start(groupID);
126 135 auto refVar = _maps.variable(newTransaction.value()->refVar);
127 136 auto ranges = _computeAllRangesInGroup(refVar,newTransaction.value()->range);
128 137 for( auto const& [ID, range] : ranges)
129 138 {
130 139 auto provider = _maps.provider(ID);
131 140 auto variable = _maps.variable(ID);
132 141 auto [missingRanges, newCacheRange] = _computeMissingRanges(variable,range);
133 142 auto exe = new TransactionExe(variable, provider, missingRanges, range, newCacheRange);
134 143 QObject::connect(exe,
135 144 &TransactionExe::transactionComplete,
136 145 [groupID=groupID,transaction=newTransaction.value(),this]()
137 146 {
138 147 this->_transactionComplete(groupID, transaction);
139 148 }
140 149 );
141 150 _ThreadPool.start(exe);
142 151 }
143 152 }
144 153 }
145 154 }
146 155
147 156 std::map<QUuid,DateTimeRange> _computeAllRangesInGroup(const std::shared_ptr<Variable>& refVar, DateTimeRange r)
148 157 {
149 158 std::map<QUuid,DateTimeRange> ranges;
150 159 if(!DateTimeRangeHelper::hasnan(r))
151 160 {
152 161 auto group = _maps.group(*refVar);
153 162 if(auto transformation = DateTimeRangeHelper::computeTransformation(refVar->range(),r);
154 163 transformation.has_value())
155 164 {
156 165 for(auto varId:group->variables())
157 166 {
158 167 auto var = _maps.variable(varId);
159 168 auto newRange = var->range().transform(transformation.value());
160 169 ranges[varId] = newRange;
161 170 }
162 171 }
163 172 else // force new range to all variables -> may be weird if more than one var in the group
164 173 // @TODO ensure that there is no side effects
165 174 {
166 175 for(auto varId:group->variables())
167 176 {
168 177 auto var = _maps.variable(varId);
169 178 ranges[varId] = r;
170 179 }
171 180 }
172 181 }
173 182 else
174 183 {
175 184 SCIQLOP_ERROR(VariableController2Private, "Invalid range containing NaN");
176 185 }
177 186 return ranges;
178 187 }
179 188
180 189 std::pair<std::vector<DateTimeRange>,DateTimeRange> _computeMissingRanges(const std::shared_ptr<Variable>& var, DateTimeRange r)
181 190 {
182 191 DateTimeRange newCacheRange;
183 192 std::vector<DateTimeRange> missingRanges;
184 193 if(DateTimeRangeHelper::hasnan(var->cacheRange()))
185 194 {
186 195 newCacheRange = _cacheStrategy->computeRange(r,r);
187 196 missingRanges = {newCacheRange};
188 197 }
189 198 else
190 199 {
191 200 newCacheRange = _cacheStrategy->computeRange(var->cacheRange(),r);
192 201 missingRanges = newCacheRange - var->cacheRange();
193 202 }
194 203 return {missingRanges,newCacheRange};
195 204 }
196 205
197 206 void _changeRange(QUuid id, DateTimeRange r)
198 207 {
199 208 _changeRange(_maps.variable(id) ,r);
200 209 }
201 210 void _changeRange(const std::shared_ptr<Variable>& var, DateTimeRange r)
202 211 {
203 212 auto provider = _maps.provider(*var);
204 213 auto [missingRanges, newCacheRange] = _computeMissingRanges(var,r);
205 214 std::vector<IDataSeries*> data;
206 215 for(auto range:missingRanges)
207 216 {
208 217 data.push_back(provider->getData(DataProviderParameters{{range}, var->metadata()}));
209 218 }
210 219 var->updateData(data, r, newCacheRange, true);
211 220 }
212 221 public:
213 222 VariableController2Private(QObject* parent=Q_NULLPTR)
214 223 :_cacheStrategy(VariableCacheStrategyFactory::createCacheStrategy(CacheStrategy::SingleThreshold))
215 224 {
216 225 Q_UNUSED(parent);
217 226 this->_ThreadPool.setMaxThreadCount(32);
218 227 }
219 228
220 229 /*
221 230 * This dtor has to like this even if this is ugly, because default dtor would rely on
222 231 * declaration order to destruct members and that would always lead to regressions when
223 232 * modifying class members
224 233 */
225 234 ~VariableController2Private()
226 235 {
227 236 while (this->_ThreadPool.activeThreadCount())
228 237 {
229 238 this->_ThreadPool.waitForDone(100);
230 239 }
231 240 }
232 241
233 242 std::shared_ptr<Variable> createVariable(const QString &name, const QVariantHash &metadata, std::shared_ptr<IDataProvider> provider)
234 243 {
235 244 auto newVar = std::make_shared<Variable>(name,metadata);
236 245 auto group = std::make_shared<VariableSynchronizationGroup2>(newVar->ID());
237 246 _maps.addVariable(newVar,std::move(provider),group);
238 247 this->_transactions.addEntry(*group);
239 248 return newVar;
240 249 }
241 250
251 std::shared_ptr<Variable> variable(QUuid ID)
252 {
253 return _maps.variable(ID);
254 }
255
256 std::shared_ptr<Variable> variable(int index)
257 {
258 return _maps.variable(index);
259 }
260
242 261 std::shared_ptr<Variable> cloneVariable(const std::shared_ptr<Variable>& variable)
243 262 {
244 263 auto newVar = variable->clone();
245 264 _maps.synchronize(newVar,std::nullopt);
246 265 _maps.addVariable(newVar,_maps.provider(*variable),_maps.group(*newVar));
247 266 this->_transactions.addEntry(*_maps.group(*newVar));
248 267 return newVar;
249 268 }
250 269
251 270 bool hasPendingTransactions(const std::shared_ptr<Variable>& variable)
252 271 {
253 272 return _transactions.active(*_maps.group(*variable));
254 273 }
255 274
256 275 void deleteVariable(const std::shared_ptr<Variable>& variable)
257 276 {
258 277 _maps.removeVariable(variable);
259 278 }
260 279
261 280 void asyncChangeRange(const std::shared_ptr<Variable>& variable, const DateTimeRange& r)
262 281 {
263 282 if(!DateTimeRangeHelper::hasnan(r))
264 283 {
265 284 auto group = _maps.group(*variable);
266 285 // Just overwrite next transaction
267 286 {
268 287 _transactions.enqueue(*group,std::make_shared<VCTransaction>(variable->ID(), r, static_cast<int>(group->variables().size())));
269 288 }
270 289 _processTransactions();
271 290 }
272 291 else
273 292 {
274 293 SCIQLOP_ERROR(VariableController2Private, "Invalid range containing NaN");
275 294 }
276 295 }
277 296
278 297 void changeRange(const std::shared_ptr<Variable>& variable, DateTimeRange r)
279 298 {
280 299 asyncChangeRange(variable,r);
281 300 while (hasPendingTransactions(variable))
282 301 {
283 302 QCoreApplication::processEvents();
284 303 }
285 304 }
286 305
287 306 inline void synchronize(const std::shared_ptr<Variable>& var, const std::shared_ptr<Variable>& with)
288 307 {
289 308 _maps.synchronize(var, with);
290 309 }
291 310
292 311 inline const std::vector<std::shared_ptr<Variable>> variables()
293 312 {
294 313 return _maps.variables();
295 314 }
296 315
297 316 };
298 317
299 318 VariableController2::VariableController2()
300 319 :impl{spimpl::make_unique_impl<VariableController2Private>()}
301 320 {}
302 321
303 322 std::shared_ptr<Variable> VariableController2::createVariable(const QString &name, const QVariantHash &metadata, const std::shared_ptr<IDataProvider>& provider, const DateTimeRange &range)
304 323 {
305 324 auto var = impl->createVariable(name, metadata, provider);
306 325 emit variableAdded(var);
307 326 if(!DateTimeRangeHelper::hasnan(range))
308 327 impl->changeRange(var,range);
309 328 else
310 329 SCIQLOP_ERROR(VariableController2, "Creating a variable with default constructed DateTimeRange is an error");
311 330 return var;
312 331 }
313 332
314 333 std::shared_ptr<Variable> VariableController2::cloneVariable(const std::shared_ptr<Variable> &variable)
315 334 {
316 335 return impl->cloneVariable(variable);
317 336 }
318 337
319 338 void VariableController2::deleteVariable(const std::shared_ptr<Variable>& variable)
320 339 {
321 340 impl->deleteVariable(variable);
322 341 emit variableDeleted(variable);
323 342 }
324 343
325 344 void VariableController2::changeRange(const std::shared_ptr<Variable>& variable, const DateTimeRange& r)
326 345 {
327 346 impl->changeRange(variable, r);
328 347 }
329 348
330 349 void VariableController2::asyncChangeRange(const std::shared_ptr<Variable> &variable, const DateTimeRange &r)
331 350 {
332 351 impl->asyncChangeRange(variable, r);
333 352 }
334 353
335 354 const std::vector<std::shared_ptr<Variable> > VariableController2::variables()
336 355 {
337 356 return impl->variables();
338 357 }
339 358
340 359 bool VariableController2::isReady(const std::shared_ptr<Variable> &variable)
341 360 {
342 361 return impl->hasPendingTransactions(variable);
343 362 }
344 363
345 364 void VariableController2::synchronize(const std::shared_ptr<Variable> &var, const std::shared_ptr<Variable> &with)
346 365 {
347 366 impl->synchronize(var, with);
348 367 }
349 368
350 369 QByteArray VariableController2::mimeData(const std::vector<std::shared_ptr<Variable> > &variables) const
351 370 {
352 371 auto encodedData = QByteArray{};
353 372 QDataStream stream{&encodedData, QIODevice::WriteOnly};
354 373 for (auto &var : variables) {
355 374 stream << var->ID().toByteArray();
356 375 }
357 376 return encodedData;
358 377 }
378
379 const std::vector<std::shared_ptr<Variable>> VariableController2::variables(QByteArray mimeData)
380 {
381 std::vector<std::shared_ptr<Variable>> variables;
382 QDataStream stream{mimeData};
383
384 QVariantList ids;
385 stream >> ids;
386
387 for (const auto& id : ids) {
388 auto uuid = QUuid{id.toByteArray()};
389 variables.push_back (impl->variable(uuid));
390 }
391
392 return variables;
393 }
394
395 const std::shared_ptr<Variable> &VariableController2::operator[](int index) const
396 {
397 return impl->variable (index);
398 }
399
400 std::shared_ptr<Variable> VariableController2::operator[](int index)
401 {
402 return impl->variable (index);
403 }
General Comments 0
You need to be logged in to leave comments. Login now