##// END OF EJS Templates
Merge pull request 301 from SciQLop-fork develop...
perrinel -
r773:9549ea5ab7e8 merge
parent child
Show More
@@ -0,0 +1,44
1 #ifndef SCIQLOP_VARIABLECACHESTRATEGYFACTORY_H
2 #define SCIQLOP_VARIABLECACHESTRATEGYFACTORY_H
3
4
5 #include <memory>
6 #include <stdexcept>
7
8 #include "VariableCacheStrategy.h"
9 #include "VariableSingleThresholdCacheStrategy.h"
10
11 #include <QLoggingCategory>
12 #include <QString>
13
14 Q_LOGGING_CATEGORY(LOG_VariableCacheStrategyFactory, "VariableCacheStrategyFactory")
15
16 enum class CacheStrategy { SingleThreshold, TwoThreashold };
17
18 class VariableCacheStrategyFactory {
19
20 using cacheStratPtr = std::unique_ptr<VariableCacheStrategy>;
21
22 public:
23 static cacheStratPtr createCacheStrategy(CacheStrategy specificStrategy)
24 {
25 switch (specificStrategy) {
26 case CacheStrategy::SingleThreshold: {
27 return std::unique_ptr<VariableCacheStrategy>{
28 new VariableSingleThresholdCacheStrategy{}};
29 break;
30 }
31 case CacheStrategy::TwoThreashold: {
32 qCCritical(LOG_VariableCacheStrategyFactory())
33 << QObject::tr("cache strategy not implemented yet");
34 break;
35 }
36 default:
37 qCCritical(LOG_VariableCacheStrategyFactory())
38 << QObject::tr("Unknown cache strategy");
39 }
40 }
41 };
42
43
44 #endif // VARIABLECACHESTRATEGYFACTORY_H
@@ -0,0 +1,32
1 #ifndef SCIQLOP_VARIABLESINGLETHRESHOLDCACHESTRATEGY_H
2 #define SCIQLOP_VARIABLESINGLETHRESHOLDCACHESTRATEGY_H
3
4 #include "Settings/SqpSettingsDefs.h"
5 #include "VariableCacheStrategy.h"
6
7
8 /// This class aims to hande the cache strategy.
9 class SCIQLOP_CORE_EXPORT VariableSingleThresholdCacheStrategy : public VariableCacheStrategy {
10 public:
11 VariableSingleThresholdCacheStrategy() = default;
12
13 std::pair<SqpRange, SqpRange> computeRange(const SqpRange &vRange,
14 const SqpRange &rangeRequested) override
15 {
16
17 auto varRanges = std::pair<SqpRange, SqpRange>{};
18
19 auto toleranceFactor = SqpSettings::toleranceValue(
20 GENERAL_TOLERANCE_AT_UPDATE_KEY, GENERAL_TOLERANCE_AT_UPDATE_DEFAULT_VALUE);
21 auto tolerance = toleranceFactor * (rangeRequested.m_TEnd - rangeRequested.m_TStart);
22
23 varRanges.first = rangeRequested;
24 varRanges.second
25 = SqpRange{rangeRequested.m_TStart - tolerance, rangeRequested.m_TEnd + tolerance};
26
27 return varRanges;
28 }
29 };
30
31
32 #endif // SCIQLOP_VARIABLESINGLETHRESHOLDCACHESTRATEGY_H
@@ -0,0 +1,310
1 #include <QObject>
2 #include <QtTest>
3
4 #include <memory>
5
6 #include <Data/DataProviderParameters.h>
7 #include <Data/IDataProvider.h>
8 #include <Data/ScalarSeries.h>
9 #include <Time/TimeController.h>
10 #include <Variable/Variable.h>
11 #include <Variable/VariableController.h>
12 #include <Variable/VariableModel.h>
13
14 namespace {
15
16 /// Delay after each operation on the variable before validating it (in ms)
17 const auto OPERATION_DELAY = 100;
18
19 /**
20 * Generates values according to a range. The value generated for a time t is the number of seconds
21 * of difference between t and a reference value (which is midnight -> 00:00:00)
22 *
23 * Example: For a range between 00:00:10 and 00:00:20, the generated values are
24 * {10,11,12,13,14,15,16,17,18,19,20}
25 */
26 std::vector<double> values(const SqpRange &range)
27 {
28 QTime referenceTime{0, 0};
29
30 std::vector<double> result{};
31
32 for (auto i = range.m_TStart; i <= range.m_TEnd; ++i) {
33 auto time = DateUtils::dateTime(i).time();
34 result.push_back(referenceTime.secsTo(time));
35 }
36
37 return result;
38 }
39
40 /// Provider used for the tests
41 class TestProvider : public IDataProvider {
42 std::shared_ptr<IDataProvider> clone() const { return std::make_shared<TestProvider>(); }
43
44 void requestDataLoading(QUuid acqIdentifier, const DataProviderParameters &parameters) override
45 {
46 const auto &ranges = parameters.m_Times;
47
48 for (const auto &range : ranges) {
49 // Generates data series
50 auto valuesData = values(range);
51
52 std::vector<double> xAxisData{};
53 for (auto i = range.m_TStart; i <= range.m_TEnd; ++i) {
54 xAxisData.push_back(i);
55 }
56
57 auto dataSeries = std::make_shared<ScalarSeries>(
58 std::move(xAxisData), std::move(valuesData), Unit{"t", true}, Unit{});
59
60 emit dataProvided(acqIdentifier, dataSeries, range);
61 }
62 }
63
64 void requestDataAborting(QUuid acqIdentifier) override
65 {
66 // Does nothing
67 }
68 };
69
70 /**
71 * Interface representing an operation performed on a variable controller.
72 * This interface is used in tests to apply a set of operations and check the status of the
73 * controller after each operation
74 */
75 struct IOperation {
76 virtual ~IOperation() = default;
77 /// Executes the operation on the variable controller
78 virtual void exec(VariableController &variableController) const = 0;
79 };
80
81 /**
82 *Variable creation operation in the controller
83 */
84 struct Create : public IOperation {
85 explicit Create(int index) : m_Index{index} {}
86
87 void exec(VariableController &variableController) const override
88 {
89 auto variable = variableController.createVariable(QString::number(m_Index), {},
90 std::make_unique<TestProvider>());
91 }
92
93 int m_Index; ///< The index of the variable to create in the controller
94 };
95
96 /**
97 * Variable move/shift operation in the controller
98 */
99 struct Move : public IOperation {
100 explicit Move(int index, const SqpRange &newRange, bool shift = false)
101 : m_Index{index}, m_NewRange{newRange}, m_Shift{shift}
102 {
103 }
104
105 void exec(VariableController &variableController) const override
106 {
107 if (auto variable = variableController.variableModel()->variable(m_Index)) {
108 variableController.onRequestDataLoading({variable}, m_NewRange, variable->range(),
109 !m_Shift);
110 }
111 }
112
113 int m_Index; ///< The index of the variable to move
114 SqpRange m_NewRange; ///< The new range of the variable
115 bool m_Shift; ///< Performs a shift (
116 };
117
118 /**
119 * Variable synchronization/desynchronization operation in the controller
120 */
121 struct Synchronize : public IOperation {
122 explicit Synchronize(int index, QUuid syncId, bool synchronize = true)
123 : m_Index{index}, m_SyncId{syncId}, m_Synchronize{synchronize}
124 {
125 }
126
127 void exec(VariableController &variableController) const override
128 {
129 if (auto variable = variableController.variableModel()->variable(m_Index)) {
130 if (m_Synchronize) {
131 variableController.onAddSynchronized(variable, m_SyncId);
132 }
133 else {
134 variableController.desynchronize(variable, m_SyncId);
135 }
136 }
137 }
138
139 int m_Index; ///< The index of the variable to sync/desync
140 QUuid m_SyncId; ///< The synchronization group of the variable
141 bool m_Synchronize; ///< Performs sync or desync operation
142 };
143
144 /**
145 * Test Iteration
146 *
147 * A test iteration includes an operation to be performed, and a set of expected ranges after each
148 * operation. Each range is tested after the operation to ensure that:
149 * - the range of the variable is the expected range
150 * - the data of the variable are those generated for the expected range
151 */
152 struct Iteration {
153 std::shared_ptr<IOperation> m_Operation; ///< Operation to perform
154 std::map<int, SqpRange> m_ExpectedRanges; ///< Expected ranges (by variable index)
155 };
156
157 using Iterations = std::vector<Iteration>;
158
159 } // namespace
160
161 Q_DECLARE_METATYPE(Iterations)
162
163 class TestVariableSync : public QObject {
164 Q_OBJECT
165
166 private slots:
167 /// Input data for @sa testSync()
168 void testSync_data();
169
170 /// Tests synchronization between variables through several operations
171 void testSync();
172 };
173
174 void TestVariableSync::testSync_data()
175 {
176 // ////////////// //
177 // Test structure //
178 // ////////////// //
179
180 QTest::addColumn<QUuid>("syncId");
181 QTest::addColumn<SqpRange>("initialRange");
182 QTest::addColumn<Iterations>("iterations");
183
184 // ////////// //
185 // Test cases //
186 // ////////// //
187
188 // Id used to synchronize variables in the controller
189 auto syncId = QUuid::createUuid();
190
191 /// Generates a range according to a start time and a end time (the date is the same)
192 auto range = [](const QTime &startTime, const QTime &endTime) {
193 return SqpRange{DateUtils::secondsSinceEpoch(QDateTime{{2017, 1, 1}, startTime, Qt::UTC}),
194 DateUtils::secondsSinceEpoch(QDateTime{{2017, 1, 1}, endTime, Qt::UTC})};
195 };
196
197 auto initialRange = range({12, 0}, {13, 0});
198
199 Iterations iterations{};
200 // Creates variables var0, var1 and var2
201 iterations.push_back({std::make_shared<Create>(0), {{0, initialRange}}});
202 iterations.push_back({std::make_shared<Create>(1), {{0, initialRange}, {1, initialRange}}});
203 iterations.push_back(
204 {std::make_shared<Create>(2), {{0, initialRange}, {1, initialRange}, {2, initialRange}}});
205
206 // Adds variables into the sync group (ranges don't need to be tested here)
207 iterations.push_back({std::make_shared<Synchronize>(0, syncId)});
208 iterations.push_back({std::make_shared<Synchronize>(1, syncId)});
209 iterations.push_back({std::make_shared<Synchronize>(2, syncId)});
210
211 // Moves var0: ranges of var0, var1 and var2 change
212 auto newRange = range({12, 30}, {13, 30});
213 iterations.push_back(
214 {std::make_shared<Move>(0, newRange), {{0, newRange}, {1, newRange}, {2, newRange}}});
215
216 // Moves var1: ranges of var0, var1 and var2 change
217 newRange = range({13, 0}, {14, 0});
218 iterations.push_back(
219 {std::make_shared<Move>(0, newRange), {{0, newRange}, {1, newRange}, {2, newRange}}});
220
221 // Moves var2: ranges of var0, var1 and var2 change
222 newRange = range({13, 30}, {14, 30});
223 iterations.push_back(
224 {std::make_shared<Move>(0, newRange), {{0, newRange}, {1, newRange}, {2, newRange}}});
225
226 // Desyncs var2 and moves var0:
227 // - ranges of var0 and var1 change
228 // - range of var2 doesn't change anymore
229 auto var2Range = newRange;
230 newRange = range({13, 45}, {14, 45});
231 iterations.push_back({std::make_shared<Synchronize>(2, syncId, false)});
232 iterations.push_back(
233 {std::make_shared<Move>(0, newRange), {{0, newRange}, {1, newRange}, {2, var2Range}}});
234
235 // Shifts var0: although var1 is synchronized with var0, its range doesn't change
236 auto var1Range = newRange;
237 newRange = range({14, 45}, {15, 45});
238 iterations.push_back({std::make_shared<Move>(0, newRange, true),
239 {{0, newRange}, {1, var1Range}, {2, var2Range}}});
240
241 // Moves var0 through several operations:
242 // - range of var0 changes
243 // - range or var1 changes according to the previous shift (one hour)
244 auto moveVar0 = [&iterations](const auto &var0NewRange, const auto &var1ExpectedRange) {
245 iterations.push_back(
246 {std::make_shared<Move>(0, var0NewRange), {{0, var0NewRange}, {1, var1ExpectedRange}}});
247 };
248 // Pan left
249 moveVar0(range({14, 30}, {15, 30}), range({13, 30}, {14, 30}));
250 // Pan right
251 moveVar0(range({16, 0}, {17, 0}), range({15, 0}, {16, 0}));
252 // Zoom in
253 moveVar0(range({16, 30}, {16, 45}), range({15, 30}, {15, 45}));
254 // Zoom out
255 moveVar0(range({12, 0}, {18, 0}), range({11, 0}, {17, 0}));
256
257 QTest::newRow("sync1") << syncId << initialRange << std::move(iterations);
258 }
259
260 void TestVariableSync::testSync()
261 {
262 // Inits controllers
263 TimeController timeController{};
264 VariableController variableController{};
265 variableController.setTimeController(&timeController);
266
267 QFETCH(QUuid, syncId);
268 QFETCH(SqpRange, initialRange);
269 timeController.onTimeToUpdate(initialRange);
270
271 // Synchronization group used
272 variableController.onAddSynchronizationGroupId(syncId);
273
274 // For each iteration:
275 // - execute operation
276 // - compare the variables' state to the expected states
277 QFETCH(Iterations, iterations);
278 for (const auto &iteration : iterations) {
279 iteration.m_Operation->exec(variableController);
280 QTest::qWait(OPERATION_DELAY);
281
282 for (const auto &expectedRangeEntry : iteration.m_ExpectedRanges) {
283 auto variableIndex = expectedRangeEntry.first;
284 auto expectedRange = expectedRangeEntry.second;
285
286 // Gets the variable in the controller
287 auto variable = variableController.variableModel()->variable(variableIndex);
288
289 // Compares variable's range to the expected range
290 QVERIFY(variable != nullptr);
291 auto range = variable->range();
292 QCOMPARE(range, expectedRange);
293
294 // Compares variable's data with values expected for its range
295 auto dataSeries = variable->dataSeries();
296 QVERIFY(dataSeries != nullptr);
297
298 auto it = dataSeries->xAxisRange(range.m_TStart, range.m_TEnd);
299 auto expectedValues = values(range);
300 QVERIFY(std::equal(it.first, it.second, expectedValues.cbegin(), expectedValues.cend(),
301 [](const auto &dataSeriesIt, const auto &expectedValue) {
302 return dataSeriesIt.value() == expectedValue;
303 }));
304 }
305 }
306 }
307
308 QTEST_MAIN(TestVariableSync)
309
310 #include "TestVariableSync.moc"
@@ -18,23 +18,14 Q_DECLARE_LOGGING_CATEGORY(LOG_VariableCacheStrategy)
18 18
19 19 class Variable;
20 20
21 /**
22 * Possible types of zoom operation
23 */
24 enum class CacheStrategy { FixedTolerance, TwoThreashold };
25
26 21 /// This class aims to hande the cache strategy.
27 class SCIQLOP_CORE_EXPORT VariableCacheStrategy : public QObject {
28 Q_OBJECT
29 public:
30 explicit VariableCacheStrategy(QObject *parent = 0);
31
32 std::pair<SqpRange, SqpRange> computeStrategyRanges(const SqpRange &vRange,
33 const SqpRange &rangeRequested);
22 class SCIQLOP_CORE_EXPORT VariableCacheStrategy {
34 23
35 private:
36 class VariableCacheStrategyPrivate;
37 spimpl::unique_impl_ptr<VariableCacheStrategyPrivate> impl;
24 public:
25 virtual std::pair<SqpRange, SqpRange> computeRange(const SqpRange &vRange,
26 const SqpRange &rangeRequested)
27 = 0;
38 28 };
39 29
30
40 31 #endif // SCIQLOP_VARIABLECACHESTRATEGY_H
@@ -33,7 +33,7 std::vector<double> flatten(std::vector<double> xValues, std::vector<double> yVa
33 33 result.push_back(xValues[i]);
34 34 result.push_back(yValues[i]);
35 35 result.push_back(zValues[i]);
36 }
36 }
37 37
38 38 return result;
39 39 }
@@ -1,6 +1,7
1 1 #include <Variable/Variable.h>
2 2 #include <Variable/VariableAcquisitionWorker.h>
3 3 #include <Variable/VariableCacheStrategy.h>
4 #include <Variable/VariableCacheStrategyFactory.h>
4 5 #include <Variable/VariableController.h>
5 6 #include <Variable/VariableModel.h>
6 7 #include <Variable/VariableSynchronizationGroup.h>
@@ -79,7 +80,9 struct VariableController::VariableControllerPrivate {
79 80 : m_WorkingMutex{},
80 81 m_VariableModel{new VariableModel{parent}},
81 82 m_VariableSelectionModel{new QItemSelectionModel{m_VariableModel, parent}},
82 m_VariableCacheStrategy{std::make_unique<VariableCacheStrategy>()},
83 // m_VariableCacheStrategy{std::make_unique<VariableCacheStrategy>()},
84 m_VariableCacheStrategy{VariableCacheStrategyFactory::createCacheStrategy(
85 CacheStrategy::SingleThreshold)},
83 86 m_VariableAcquisitionWorker{std::make_unique<VariableAcquisitionWorker>()},
84 87 q{parent}
85 88 {
@@ -573,7 +576,7 void VariableController::VariableControllerPrivate::processRequest(std::shared_p
573 576 auto varId = m_VariableToIdentifierMap.at(var);
574 577
575 578 auto varStrategyRangesRequested
576 = m_VariableCacheStrategy->computeStrategyRanges(var->range(), rangeRequested);
579 = m_VariableCacheStrategy->computeRange(var->range(), rangeRequested);
577 580
578 581 auto notInCacheRangeList = QVector<SqpRange>{varStrategyRangesRequested.second};
579 582 auto inCacheRangeList = QVector<SqpRange>{};
@@ -7,8 +7,8 tests = [
7 7 [['Data/TestTwoDimArrayData.cpp'],'test_2d','Two Dim Array test'],
8 8 [['DataSource/TestDataSourceController.cpp'],'test_data_source','DataSourceController test'],
9 9 [['Variable/TestVariableCacheController.cpp'],'test_variable_cache','VariableCacheController test'],
10 [['Variable/TestVariableController.cpp'],'test_variable_controler','VariableController test'],
11 [['Variable/TestVariable.cpp'],'test_variable','Variable test']
10 [['Variable/TestVariable.cpp'],'test_variable','Variable test'],
11 [['Variable/TestVariableSync.cpp'],'test_variable_sync','Variable synchronization test']
12 12 ]
13 13
14 14 foreach unit_test : tests
@@ -22,8 +22,8 gui_ui_files = [
22 22 'ui/SidePane/SqpSidePane.ui',
23 23 'ui/TimeWidget/TimeWidget.ui',
24 24 'ui/Variable/VariableInspectorWidget.ui',
25 'ui/Variable/VariableMenuHeaderWidget.ui',
26 25 'ui/Variable/RenameVariableDialog.ui',
26 'ui/Variable/VariableMenuHeaderWidget.ui',
27 27 'ui/Visualization/VisualizationGraphWidget.ui',
28 28 'ui/Visualization/VisualizationTabWidget.ui',
29 29 'ui/Visualization/VisualizationWidget.ui',
@@ -184,22 +184,29 void VariableInspectorWidget::onTableMenuRequested(const QPoint &pos) noexcept
184 184 if (selectedVariables.size() == 1) {
185 185 auto selectedVariable = selectedVariables.front();
186 186
187 auto duplicateFun = [&selectedVariable]() {
188 sqpApp->variableController().cloneVariable(selectedVariable);
187 auto duplicateFun = [varW = std::weak_ptr<Variable>(selectedVariable)]()
188 {
189 if (auto var = varW.lock()) {
190 sqpApp->variableController().cloneVariable(var);
191 }
189 192 };
190 193
191 194 tableMenu.addAction(tr("Duplicate"), duplicateFun);
192 195
193 auto renameFun = [&selectedVariable, &model, this]() {
194 // Generates forbidden names (names associated to existing variables)
195 auto allVariables = model->variables();
196 auto forbiddenNames = QVector<QString>(allVariables.size());
197 std::transform(allVariables.cbegin(), allVariables.cend(), forbiddenNames.begin(),
198 [](const auto &variable) { return variable->name(); });
199
200 RenameVariableDialog dialog{selectedVariable->name(), forbiddenNames, this};
201 if (dialog.exec() == QDialog::Accepted) {
202 selectedVariable->setName(dialog.name());
196 auto renameFun = [ varW = std::weak_ptr<Variable>(selectedVariable), &model, this ]()
197 {
198 if (auto var = varW.lock()) {
199 // Generates forbidden names (names associated to existing variables)
200 auto allVariables = model->variables();
201 auto forbiddenNames = QVector<QString>(allVariables.size());
202 std::transform(allVariables.cbegin(), allVariables.cend(),
203 forbiddenNames.begin(),
204 [](const auto &variable) { return variable->name(); });
205
206 RenameVariableDialog dialog{var->name(), forbiddenNames, this};
207 if (dialog.exec() == QDialog::Accepted) {
208 var->setName(dialog.name());
209 }
203 210 }
204 211 };
205 212
1 NO CONTENT: file was removed
General Comments 0
You need to be logged in to leave comments. Login now