##// END OF EJS Templates
Merge branch 'feature/AcquisitionTest' into develop
perrinel -
r1400:a4f96b874829 merge
parent child
Show More
@@ -1,40 +1,38
1 1 #ifndef SCIQLOP_ACQUISITIONREQUEST_H
2 2 #define SCIQLOP_ACQUISITIONREQUEST_H
3 3
4 4 #include <QObject>
5 5
6 6 #include <QUuid>
7 7
8 8 #include <Common/DateUtils.h>
9 9 #include <Common/MetaTypes.h>
10 10 #include <Data/DataProviderParameters.h>
11 11 #include <Data/IDataProvider.h>
12 12 #include <Data/SqpRange.h>
13 13
14 14 #include <memory>
15 15
16 16 /**
17 17 * @brief The AcquisitionRequest struct holds the information of an variable request
18 18 */
19 19 struct AcquisitionRequest {
20 20 AcquisitionRequest()
21 21 {
22 22 m_AcqIdentifier = QUuid::createUuid();
23 23 m_Size = 0;
24 24 m_Progression = 0;
25 25 }
26 26
27 27 QUuid m_VarRequestId;
28 28 QUuid m_AcqIdentifier;
29 29 QUuid m_vIdentifier;
30 30 DataProviderParameters m_DataProviderParameters;
31 SqpRange m_RangeRequested;
32 SqpRange m_CacheRangeRequested;
33 31 int m_Size;
34 32 int m_Progression;
35 33 std::shared_ptr<IDataProvider> m_Provider;
36 34 };
37 35
38 36 SCIQLOP_REGISTER_META_TYPE(ACQUISITIONREQUEST_REGISTRY, AcquisitionRequest)
39 37
40 38 #endif // SCIQLOP_ACQUISITIONREQUEST_H
@@ -1,66 +1,64
1 1 #ifndef SCIQLOP_VARIABLEACQUISITIONWORKER_H
2 2 #define SCIQLOP_VARIABLEACQUISITIONWORKER_H
3 3
4 4 #include "CoreGlobal.h"
5 5
6 6 #include <Data/DataProviderParameters.h>
7 7 #include <QLoggingCategory>
8 8 #include <QObject>
9 9 #include <QUuid>
10 10
11 11 #include <Data/AcquisitionDataPacket.h>
12 12 #include <Data/IDataSeries.h>
13 13 #include <Data/SqpRange.h>
14 14
15 15 #include <QLoggingCategory>
16 16
17 17 #include <Common/spimpl.h>
18 18
19 19 Q_DECLARE_LOGGING_CATEGORY(LOG_VariableAcquisitionWorker)
20 20
21 21 class Variable;
22 22 class IDataProvider;
23 23
24 24 /// This class aims to handle all acquisition request
25 25 class SCIQLOP_CORE_EXPORT VariableAcquisitionWorker : public QObject {
26 26 Q_OBJECT
27 27 public:
28 28 explicit VariableAcquisitionWorker(QObject *parent = 0);
29 29 virtual ~VariableAcquisitionWorker();
30 30
31 QUuid pushVariableRequest(QUuid varRequestId, QUuid vIdentifier, SqpRange rangeRequested,
32 SqpRange cacheRangeRequested, DataProviderParameters parameters,
31 QUuid pushVariableRequest(QUuid varRequestId, QUuid vIdentifier,
32 DataProviderParameters parameters,
33 33 std::shared_ptr<IDataProvider> provider);
34 34
35 35 void abortProgressRequested(QUuid vIdentifier);
36 36
37 37 void initialize();
38 38 void finalize();
39 39 signals:
40 void dataProvided(QUuid vIdentifier, const SqpRange &rangeRequested,
41 const SqpRange &cacheRangeRequested,
42 QVector<AcquisitionDataPacket> dataAcquired);
40 void dataProvided(QUuid vIdentifier, QVector<AcquisitionDataPacket> dataAcquired);
43 41
44 42 void variableRequestInProgress(QUuid vIdentifier, double progress);
45 43
46 44
47 45 void variableCanceledRequested(QUuid vIdentifier);
48 46
49 47
50 48 public slots:
51 49 void onVariableDataAcquired(QUuid acqIdentifier, std::shared_ptr<IDataSeries> dataSeries,
52 50 SqpRange dataRangeAcquired);
53 51 void onVariableRetrieveDataInProgress(QUuid acqIdentifier, double progress);
54 52 void onVariableAcquisitionFailed(QUuid acqIdentifier);
55 53
56 54 private:
57 55 void waitForFinish();
58 56
59 57 class VariableAcquisitionWorkerPrivate;
60 58 spimpl::unique_impl_ptr<VariableAcquisitionWorkerPrivate> impl;
61 59
62 60 private slots:
63 61 void onExecuteRequest(QUuid acqIdentifier);
64 62 };
65 63
66 64 #endif // SCIQLOP_VARIABLEACQUISITIONWORKER_H
@@ -1,144 +1,142
1 1 #ifndef SCIQLOP_VARIABLECONTROLLER_H
2 2 #define SCIQLOP_VARIABLECONTROLLER_H
3 3
4 4 #include "CoreGlobal.h"
5 5
6 6 #include <Data/AcquisitionDataPacket.h>
7 7 #include <Data/SqpRange.h>
8 8
9 9 #include <QLoggingCategory>
10 10 #include <QObject>
11 11 #include <QUuid>
12 12
13 13 #include <Common/spimpl.h>
14 14
15 15 class IDataProvider;
16 16 class QItemSelectionModel;
17 17 class TimeController;
18 18 class Variable;
19 19 class VariableModel;
20 20
21 21 Q_DECLARE_LOGGING_CATEGORY(LOG_VariableController)
22 22
23 23
24 24 /**
25 25 * Possible types of zoom operation
26 26 */
27 27 enum class AcquisitionZoomType { ZoomOut, ZoomIn, PanRight, PanLeft, Unknown };
28 28
29 29
30 30 /**
31 31 * @brief The VariableController class aims to handle the variables in SciQlop.
32 32 */
33 33 class SCIQLOP_CORE_EXPORT VariableController : public QObject {
34 34 Q_OBJECT
35 35 public:
36 36 explicit VariableController(QObject *parent = 0);
37 37 virtual ~VariableController();
38 38
39 39 VariableModel *variableModel() noexcept;
40 40 QItemSelectionModel *variableSelectionModel() noexcept;
41 41
42 42 void setTimeController(TimeController *timeController) noexcept;
43 43
44 44 /**
45 45 * Clones the variable passed in parameter and adds the duplicate to the controller
46 46 * @param variable the variable to duplicate
47 47 * @return the duplicate created, nullptr if the variable couldn't be created
48 48 */
49 49 std::shared_ptr<Variable> cloneVariable(std::shared_ptr<Variable> variable) noexcept;
50 50
51 51 /// Returns the MIME data associated to a list of variables
52 52 QByteArray mimeDataForVariables(const QList<std::shared_ptr<Variable> > &variables) const;
53 53
54 54 /// Returns the list of variables contained in a MIME data
55 55 QList<std::shared_ptr<Variable> > variablesForMimeData(const QByteArray &mimeData) const;
56 56
57 57 static AcquisitionZoomType getZoomType(const SqpRange &range, const SqpRange &oldRange);
58 58 signals:
59 59 /// Signal emitted when a variable is about to be deleted from the controller
60 60 void variableAboutToBeDeleted(std::shared_ptr<Variable> variable);
61 61
62 62 /// Signal emitted when a data acquisition is requested on a range for a variable
63 63 void rangeChanged(std::shared_ptr<Variable> variable, const SqpRange &range);
64 64
65 65 /// Signal emitted when a sub range of the cacheRange of the variable can be displayed
66 66 void updateVarDisplaying(std::shared_ptr<Variable> variable, const SqpRange &range);
67 67
68 68 /// Signal emitted when all acquisitions related to the variables have been completed (whether
69 69 /// validated, canceled, or failed)
70 70 void acquisitionFinished();
71 71
72 72 void variableAdded(const std::shared_ptr<Variable> &variable);
73 73
74 74 public slots:
75 75 /**
76 76 * Deletes from the controller the variable passed in parameter.
77 77 *
78 78 * Delete a variable includes:
79 79 * - the deletion of the various references to the variable in SciQlop
80 80 * - the deletion of the model variable
81 81 * - the deletion of the provider associated with the variable
82 82 * - removing the cache associated with the variable
83 83 *
84 84 * @param variable the variable to delete from the controller.
85 85 */
86 86 void deleteVariable(std::shared_ptr<Variable> variable) noexcept;
87 87
88 88 /**
89 89 * Deletes from the controller the variables passed in parameter.
90 90 * @param variables the variables to delete from the controller.
91 91 * @sa deleteVariable()
92 92 */
93 93 void deleteVariables(const QVector<std::shared_ptr<Variable> > &variables) noexcept;
94 94
95 95 /// Request the data loading of the variable whithin range
96 96 void onRequestDataLoading(QVector<std::shared_ptr<Variable> > variables, const SqpRange &range,
97 97 bool synchronise);
98 98 /**
99 99 * Creates a new variable and adds it to the model
100 100 * @param name the name of the new variable
101 101 * @param metadata the metadata of the new variable
102 102 * @param provider the data provider for the new variable
103 103 * @return the pointer to the new variable or nullptr if the creation failed
104 104 */
105 105 std::shared_ptr<Variable> createVariable(const QString &name, const QVariantHash &metadata,
106 106 std::shared_ptr<IDataProvider> provider) noexcept;
107 107
108 108 /// Update the temporal parameters of every selected variable to dateTime
109 109 void onDateTimeOnSelection(const SqpRange &dateTime);
110 110
111 111 /// Update the temporal parameters of the specified variable
112 112 void onUpdateDateTime(std::shared_ptr<Variable> variable, const SqpRange &dateTime);
113 113
114 114
115 void onDataProvided(QUuid vIdentifier, const SqpRange &rangeRequested,
116 const SqpRange &cacheRangeRequested,
117 QVector<AcquisitionDataPacket> dataAcquired);
115 void onDataProvided(QUuid vIdentifier, QVector<AcquisitionDataPacket> dataAcquired);
118 116
119 117 void onVariableRetrieveDataInProgress(QUuid identifier, double progress);
120 118
121 119 /// Cancel the current request for the variable
122 120 void onAbortProgressRequested(std::shared_ptr<Variable> variable);
123 121 void onAbortAcquisitionRequested(QUuid vIdentifier);
124 122
125 123 // synchronization group methods
126 124 void onAddSynchronizationGroupId(QUuid synchronizationGroupId);
127 125 void onRemoveSynchronizationGroupId(QUuid synchronizationGroupId);
128 126 void onAddSynchronized(std::shared_ptr<Variable> variable, QUuid synchronizationGroupId);
129 127
130 128 /// Desynchronizes the variable of the group whose identifier is passed in parameter
131 129 /// @remarks the method does nothing if the variable is not part of the group
132 130 void desynchronize(std::shared_ptr<Variable> variable, QUuid synchronizationGroupId);
133 131
134 132 void initialize();
135 133 void finalize();
136 134
137 135 private:
138 136 void waitForFinish();
139 137
140 138 class VariableControllerPrivate;
141 139 spimpl::unique_impl_ptr<VariableControllerPrivate> impl;
142 140 };
143 141
144 142 #endif // SCIQLOP_VARIABLECONTROLLER_H
@@ -1,416 +1,369
1 1 #include "Variable/VariableAcquisitionWorker.h"
2 2
3 3 #include "Variable/Variable.h"
4 4
5 5 #include <Data/AcquisitionRequest.h>
6 6 #include <Data/SqpRange.h>
7 7
8 8 #include <unordered_map>
9 9 #include <utility>
10 10
11 11 #include <QMutex>
12 12 #include <QReadWriteLock>
13 13 #include <QThread>
14 14
15 15 #include <cmath>
16 16
17 17 Q_LOGGING_CATEGORY(LOG_VariableAcquisitionWorker, "VariableAcquisitionWorker")
18 18
19 19 struct VariableAcquisitionWorker::VariableAcquisitionWorkerPrivate {
20 20
21 21 explicit VariableAcquisitionWorkerPrivate(VariableAcquisitionWorker *parent)
22 22 : m_Lock{QReadWriteLock::Recursive}, q{parent}
23 23 {
24 24 }
25 25
26 26 void lockRead() { m_Lock.lockForRead(); }
27 27 void lockWrite() { m_Lock.lockForWrite(); }
28 28 void unlock() { m_Lock.unlock(); }
29 29
30 30 void removeVariableRequest(QUuid vIdentifier);
31 31
32 /// Remove the current request and execute the next one if exist
33 void updateToNextRequest(QUuid vIdentifier);
34
35 32 /// Remove and/or abort all AcqRequest in link with varRequestId
36 33 void cancelVarRequest(QUuid varRequestId);
37 34 void removeAcqRequest(QUuid acqRequestId);
38 35
39 36 QMutex m_WorkingMutex;
40 37 QReadWriteLock m_Lock;
41 38
42 39 std::map<QUuid, QVector<AcquisitionDataPacket> > m_AcqIdentifierToAcqDataPacketVectorMap;
43 40 std::map<QUuid, AcquisitionRequest> m_AcqIdentifierToAcqRequestMap;
44 std::map<QUuid, std::pair<QUuid, QUuid> > m_VIdentifierToCurrrentAcqIdNextIdPairMap;
41 std::map<QUuid, QUuid> m_VIdentifierToCurrrentAcqIdMap;
45 42 VariableAcquisitionWorker *q;
46 43 };
47 44
48 45
49 46 VariableAcquisitionWorker::VariableAcquisitionWorker(QObject *parent)
50 47 : QObject{parent}, impl{spimpl::make_unique_impl<VariableAcquisitionWorkerPrivate>(this)}
51 48 {
52 49 }
53 50
54 51 VariableAcquisitionWorker::~VariableAcquisitionWorker()
55 52 {
56 53 qCInfo(LOG_VariableAcquisitionWorker()) << tr("VariableAcquisitionWorker destruction")
57 54 << QThread::currentThread();
58 55 this->waitForFinish();
59 56 }
60 57
61 58
62 59 QUuid VariableAcquisitionWorker::pushVariableRequest(QUuid varRequestId, QUuid vIdentifier,
63 SqpRange rangeRequested,
64 SqpRange cacheRangeRequested,
65 60 DataProviderParameters parameters,
66 61 std::shared_ptr<IDataProvider> provider)
67 62 {
68 63 qCDebug(LOG_VariableAcquisitionWorker())
69 << tr("TORM VariableAcquisitionWorker::pushVariableRequest ") << cacheRangeRequested;
64 << tr("TORM VariableAcquisitionWorker::pushVariableRequest varRequestId: ") << varRequestId
65 << "vId: " << vIdentifier;
70 66 auto varRequestIdCanceled = QUuid();
71 67
72 68 // Request creation
73 69 auto acqRequest = AcquisitionRequest{};
74 qCDebug(LOG_VariableAcquisitionWorker()) << tr("PushVariableRequest ") << vIdentifier
75 << varRequestId;
76 70 acqRequest.m_VarRequestId = varRequestId;
77 71 acqRequest.m_vIdentifier = vIdentifier;
78 72 acqRequest.m_DataProviderParameters = parameters;
79 acqRequest.m_RangeRequested = rangeRequested;
80 acqRequest.m_CacheRangeRequested = cacheRangeRequested;
81 73 acqRequest.m_Size = parameters.m_Times.size();
82 74 acqRequest.m_Provider = provider;
75 qCInfo(LOG_VariableAcquisitionWorker()) << tr("Add acqRequest ") << acqRequest.m_AcqIdentifier
76 << acqRequest.m_Size;
83 77
84 78
85 79 // Register request
86 80 impl->lockWrite();
87 81 impl->m_AcqIdentifierToAcqRequestMap.insert(
88 82 std::make_pair(acqRequest.m_AcqIdentifier, acqRequest));
89 83
90 auto it = impl->m_VIdentifierToCurrrentAcqIdNextIdPairMap.find(vIdentifier);
91 if (it != impl->m_VIdentifierToCurrrentAcqIdNextIdPairMap.cend()) {
92 // A current request already exists, we can replace the next one
93 auto oldAcqId = it->second.second;
84 auto it = impl->m_VIdentifierToCurrrentAcqIdMap.find(vIdentifier);
85 if (it != impl->m_VIdentifierToCurrrentAcqIdMap.cend()) {
86 // A current request already exists, we can cancel it
87 // remove old acqIdentifier from the worker
88 auto oldAcqId = it->second;
94 89 auto acqIdentifierToAcqRequestMapIt = impl->m_AcqIdentifierToAcqRequestMap.find(oldAcqId);
95 90 if (acqIdentifierToAcqRequestMapIt != impl->m_AcqIdentifierToAcqRequestMap.cend()) {
96 91 auto oldAcqRequest = acqIdentifierToAcqRequestMapIt->second;
97 92 varRequestIdCanceled = oldAcqRequest.m_VarRequestId;
98 93 }
99
100 it->second.second = acqRequest.m_AcqIdentifier;
101 94 impl->unlock();
102
103 // remove old acqIdentifier from the worker
104 95 impl->cancelVarRequest(varRequestIdCanceled);
105 // impl->m_AcqIdentifierToAcqRequestMap.erase(oldAcqId);
106 96 }
107 97 else {
108 // First request for the variable, it must be stored and executed
109 impl->m_VIdentifierToCurrrentAcqIdNextIdPairMap.insert(
110 std::make_pair(vIdentifier, std::make_pair(acqRequest.m_AcqIdentifier, QUuid())));
111 98 impl->unlock();
112
113 QMetaObject::invokeMethod(this, "onExecuteRequest", Qt::QueuedConnection,
114 Q_ARG(QUuid, acqRequest.m_AcqIdentifier));
115 99 }
116 100
101 // Request for the variable, it must be stored and executed
102 impl->lockWrite();
103 impl->m_VIdentifierToCurrrentAcqIdMap.insert(
104 std::make_pair(vIdentifier, acqRequest.m_AcqIdentifier));
105 impl->unlock();
106
107 QMetaObject::invokeMethod(this, "onExecuteRequest", Qt::QueuedConnection,
108 Q_ARG(QUuid, acqRequest.m_AcqIdentifier));
109
117 110 return varRequestIdCanceled;
118 111 }
119 112
120 113 void VariableAcquisitionWorker::abortProgressRequested(QUuid vIdentifier)
121 114 {
122 115 impl->lockRead();
123 116
124 auto it = impl->m_VIdentifierToCurrrentAcqIdNextIdPairMap.find(vIdentifier);
125 if (it != impl->m_VIdentifierToCurrrentAcqIdNextIdPairMap.cend()) {
126 auto currentAcqId = it->second.first;
117 auto it = impl->m_VIdentifierToCurrrentAcqIdMap.find(vIdentifier);
118 if (it != impl->m_VIdentifierToCurrrentAcqIdMap.cend()) {
119 auto currentAcqId = it->second;
127 120
128 121 auto it = impl->m_AcqIdentifierToAcqRequestMap.find(currentAcqId);
129 122 if (it != impl->m_AcqIdentifierToAcqRequestMap.cend()) {
130 123 auto request = it->second;
131 124 impl->unlock();
132 125
133 // Remove the current request from the worker
134 impl->updateToNextRequest(vIdentifier);
135
136 126 // notify the request aborting to the provider
137 127 request.m_Provider->requestDataAborting(currentAcqId);
138 128 }
139 129 else {
140 130 impl->unlock();
141 131 qCWarning(LOG_VariableAcquisitionWorker())
142 132 << tr("Impossible to abort an unknown acquisition request") << currentAcqId;
143 133 }
144 134 }
145 135 else {
146 136 impl->unlock();
147 137 }
148 138 }
149 139
150 140 void VariableAcquisitionWorker::onVariableRetrieveDataInProgress(QUuid acqIdentifier,
151 141 double progress)
152 142 {
153 143 qCDebug(LOG_VariableAcquisitionWorker()) << tr("TORM: onVariableRetrieveDataInProgress ")
144 << QThread::currentThread()->objectName()
154 145 << acqIdentifier << progress;
155 146 impl->lockRead();
156 147 auto aIdToARit = impl->m_AcqIdentifierToAcqRequestMap.find(acqIdentifier);
157 148 if (aIdToARit != impl->m_AcqIdentifierToAcqRequestMap.cend()) {
158 auto currentPartSize = (aIdToARit->second.m_Size != 0) ? 100 / aIdToARit->second.m_Size : 0;
149 auto progressPartSize
150 = (aIdToARit->second.m_Size != 0) ? 100 / aIdToARit->second.m_Size : 0;
159 151
160 152 auto currentPartProgress
161 = std::isnan(progress) ? 0.0 : (progress * currentPartSize) / 100.0;
162 auto currentAlreadyProgress = aIdToARit->second.m_Progression * currentPartSize;
153 = std::isnan(progress) ? 0.0 : (progress * progressPartSize) / 100.0;
154
155 // We can only give an approximation of the currentProgression since its upgrade is async.
156 auto currentProgression = aIdToARit->second.m_Progression;
157 if (currentProgression == aIdToARit->second.m_Size) {
158 currentProgression = aIdToARit->second.m_Size - 1;
159 }
160
161 auto currentAlreadyProgress = progressPartSize * currentProgression;
162
163 163
164 164 auto finalProgression = currentAlreadyProgress + currentPartProgress;
165 165 emit variableRequestInProgress(aIdToARit->second.m_vIdentifier, finalProgression);
166 qCDebug(LOG_VariableAcquisitionWorker())
167 << tr("TORM: onVariableRetrieveDataInProgress ")
168 << QThread::currentThread()->objectName() << aIdToARit->second.m_vIdentifier
169 << currentPartSize << currentAlreadyProgress << currentPartProgress << finalProgression;
166
170 167 if (finalProgression == 100.0) {
171 168 emit variableRequestInProgress(aIdToARit->second.m_vIdentifier, 0.0);
172 169 }
173 170 }
174 171 impl->unlock();
175 172 }
176 173
177 174 void VariableAcquisitionWorker::onVariableAcquisitionFailed(QUuid acqIdentifier)
178 175 {
179 176 qCDebug(LOG_VariableAcquisitionWorker()) << tr("onVariableAcquisitionFailed")
180 177 << QThread::currentThread();
181 178 impl->lockRead();
182 179 auto it = impl->m_AcqIdentifierToAcqRequestMap.find(acqIdentifier);
183 180 if (it != impl->m_AcqIdentifierToAcqRequestMap.cend()) {
184 181 auto request = it->second;
185 182 impl->unlock();
186 qCDebug(LOG_VariableAcquisitionWorker()) << tr("onVariableAcquisitionFailed")
187 << acqIdentifier << request.m_vIdentifier
188 << QThread::currentThread();
189 183 emit variableCanceledRequested(request.m_vIdentifier);
190 184 }
191 185 else {
192 186 impl->unlock();
193 // TODO log no acqIdentifier recognized
194 187 }
195 188 }
196 189
197 190 void VariableAcquisitionWorker::onVariableDataAcquired(QUuid acqIdentifier,
198 191 std::shared_ptr<IDataSeries> dataSeries,
199 192 SqpRange dataRangeAcquired)
200 193 {
201 194 qCDebug(LOG_VariableAcquisitionWorker()) << tr("TORM: onVariableDataAcquired on range ")
202 195 << acqIdentifier << dataRangeAcquired;
203 196 impl->lockWrite();
204 197 auto aIdToARit = impl->m_AcqIdentifierToAcqRequestMap.find(acqIdentifier);
205 198 if (aIdToARit != impl->m_AcqIdentifierToAcqRequestMap.cend()) {
206 199 // Store the result
207 200 auto dataPacket = AcquisitionDataPacket{};
208 201 dataPacket.m_Range = dataRangeAcquired;
209 202 dataPacket.m_DateSeries = dataSeries;
210 203
211 204 auto aIdToADPVit = impl->m_AcqIdentifierToAcqDataPacketVectorMap.find(acqIdentifier);
212 205 if (aIdToADPVit != impl->m_AcqIdentifierToAcqDataPacketVectorMap.cend()) {
213 206 // A current request result already exists, we can update it
214 207 aIdToADPVit->second.push_back(dataPacket);
215 208 }
216 209 else {
217 210 // First request result for the variable, it must be stored
218 211 impl->m_AcqIdentifierToAcqDataPacketVectorMap.insert(
219 212 std::make_pair(acqIdentifier, QVector<AcquisitionDataPacket>() << dataPacket));
220 213 }
221 214
222 215
223 216 // Decrement the counter of the request
224 217 auto &acqRequest = aIdToARit->second;
225 218 acqRequest.m_Progression = acqRequest.m_Progression + 1;
226 219
227 220 // if the counter is 0, we can return data then run the next request if it exists and
228 221 // removed the finished request
229 222 if (acqRequest.m_Size == acqRequest.m_Progression) {
230 223 auto varId = acqRequest.m_vIdentifier;
231 auto rangeRequested = acqRequest.m_RangeRequested;
232 auto cacheRangeRequested = acqRequest.m_CacheRangeRequested;
233 224 // Return the data
234 225 aIdToADPVit = impl->m_AcqIdentifierToAcqDataPacketVectorMap.find(acqIdentifier);
235 226 if (aIdToADPVit != impl->m_AcqIdentifierToAcqDataPacketVectorMap.cend()) {
236 emit dataProvided(varId, rangeRequested, cacheRangeRequested, aIdToADPVit->second);
227 emit dataProvided(varId, aIdToADPVit->second);
237 228 }
238 229 impl->unlock();
239
240 // Update to the next request
241 impl->updateToNextRequest(acqRequest.m_vIdentifier);
242 230 }
243 231 else {
244 232 impl->unlock();
245 233 }
246 234 }
247 235 else {
248 236 impl->unlock();
249 237 qCWarning(LOG_VariableAcquisitionWorker())
250 238 << tr("Impossible to retrieve AcquisitionRequest for the incoming data.");
251 239 }
252 240 }
253 241
254 242 void VariableAcquisitionWorker::onExecuteRequest(QUuid acqIdentifier)
255 243 {
256 244 qCDebug(LOG_VariableAcquisitionWorker()) << tr("onExecuteRequest") << QThread::currentThread();
257 245 impl->lockRead();
258 246 auto it = impl->m_AcqIdentifierToAcqRequestMap.find(acqIdentifier);
259 247 if (it != impl->m_AcqIdentifierToAcqRequestMap.cend()) {
260 248 auto request = it->second;
261 249 impl->unlock();
262 250 emit variableRequestInProgress(request.m_vIdentifier, 0.1);
251 qCDebug(LOG_VariableAcquisitionWorker()) << tr("Start request 10%") << acqIdentifier
252 << QThread::currentThread();
263 253 request.m_Provider->requestDataLoading(acqIdentifier, request.m_DataProviderParameters);
264 254 }
265 255 else {
266 256 impl->unlock();
267 257 // TODO log no acqIdentifier recognized
268 258 }
269 259 }
270 260
271 261 void VariableAcquisitionWorker::initialize()
272 262 {
273 263 qCDebug(LOG_VariableAcquisitionWorker()) << tr("VariableAcquisitionWorker init")
274 264 << QThread::currentThread();
275 265 impl->m_WorkingMutex.lock();
276 266 qCDebug(LOG_VariableAcquisitionWorker()) << tr("VariableAcquisitionWorker init END");
277 267 }
278 268
279 269 void VariableAcquisitionWorker::finalize()
280 270 {
281 271 impl->m_WorkingMutex.unlock();
282 272 }
283 273
284 274 void VariableAcquisitionWorker::waitForFinish()
285 275 {
286 276 QMutexLocker locker{&impl->m_WorkingMutex};
287 277 }
288 278
289 279 void VariableAcquisitionWorker::VariableAcquisitionWorkerPrivate::removeVariableRequest(
290 280 QUuid vIdentifier)
291 281 {
292 282 lockWrite();
293 auto it = m_VIdentifierToCurrrentAcqIdNextIdPairMap.find(vIdentifier);
283 auto it = m_VIdentifierToCurrrentAcqIdMap.find(vIdentifier);
294 284
295 if (it != m_VIdentifierToCurrrentAcqIdNextIdPairMap.cend()) {
285 if (it != m_VIdentifierToCurrrentAcqIdMap.cend()) {
296 286 // A current request already exists, we can replace the next one
297 287
298 m_AcqIdentifierToAcqRequestMap.erase(it->second.first);
299 m_AcqIdentifierToAcqDataPacketVectorMap.erase(it->second.first);
300
301 m_AcqIdentifierToAcqRequestMap.erase(it->second.second);
302 m_AcqIdentifierToAcqDataPacketVectorMap.erase(it->second.second);
288 qCDebug(LOG_VariableAcquisitionWorker())
289 << "VariableAcquisitionWorkerPrivate::removeVariableRequest "
290 << QThread::currentThread()->objectName() << it->second;
291 m_AcqIdentifierToAcqRequestMap.erase(it->second);
292 m_AcqIdentifierToAcqDataPacketVectorMap.erase(it->second);
303 293 }
304 m_VIdentifierToCurrrentAcqIdNextIdPairMap.erase(vIdentifier);
305 unlock();
306 }
307 294
308 void VariableAcquisitionWorker::VariableAcquisitionWorkerPrivate::updateToNextRequest(
309 QUuid vIdentifier)
310 {
311 lockRead();
312 auto it = m_VIdentifierToCurrrentAcqIdNextIdPairMap.find(vIdentifier);
313 if (it != m_VIdentifierToCurrrentAcqIdNextIdPairMap.cend()) {
314 if (it->second.second.isNull()) {
315 unlock();
316 // There is no next request, we can remove the variable request
317 removeVariableRequest(vIdentifier);
318 }
319 else {
320 auto acqIdentifierToRemove = it->second.first;
321 // Move the next request to the current request
322 auto nextRequestId = it->second.second;
323 it->second.first = nextRequestId;
324 it->second.second = QUuid();
325 unlock();
326 // Remove AcquisitionRequest and results;
327 lockWrite();
328 m_AcqIdentifierToAcqRequestMap.erase(acqIdentifierToRemove);
329 m_AcqIdentifierToAcqDataPacketVectorMap.erase(acqIdentifierToRemove);
330 unlock();
331 // Execute the current request
332 QMetaObject::invokeMethod(q, "onExecuteRequest", Qt::QueuedConnection,
333 Q_ARG(QUuid, nextRequestId));
334 }
335 }
336 else {
337 unlock();
338 qCCritical(LOG_VariableAcquisitionWorker())
339 << tr("Impossible to execute the acquisition on an unfound variable ");
340 }
295 // stop any progression
296 emit q->variableRequestInProgress(vIdentifier, 0.0);
297
298 m_VIdentifierToCurrrentAcqIdMap.erase(vIdentifier);
299 unlock();
341 300 }
342 301
343 302 void VariableAcquisitionWorker::VariableAcquisitionWorkerPrivate::cancelVarRequest(
344 303 QUuid varRequestId)
345 304 {
346 305 qCDebug(LOG_VariableAcquisitionWorker())
347 << "VariableAcquisitionWorkerPrivate::cancelVarRequest 0";
306 << "VariableAcquisitionWorkerPrivate::cancelVarRequest start";
348 307 lockRead();
349 308 // get all AcqIdentifier in link with varRequestId
350 309 QVector<QUuid> acqIdsToRm;
351 310 auto cend = m_AcqIdentifierToAcqRequestMap.cend();
352 311 for (auto it = m_AcqIdentifierToAcqRequestMap.cbegin(); it != cend; ++it) {
353 312 if (it->second.m_VarRequestId == varRequestId) {
354 313 acqIdsToRm << it->first;
355 314 }
356 315 }
357 316 unlock();
358 317 // run aborting or removing of acqIdsToRm
359 318
360 319 for (auto acqId : acqIdsToRm) {
361 320 removeAcqRequest(acqId);
362 321 }
363 322 qCDebug(LOG_VariableAcquisitionWorker())
364 323 << "VariableAcquisitionWorkerPrivate::cancelVarRequest end";
365 324 }
366 325
367 326 void VariableAcquisitionWorker::VariableAcquisitionWorkerPrivate::removeAcqRequest(
368 327 QUuid acqRequestId)
369 328 {
370 329 qCDebug(LOG_VariableAcquisitionWorker())
371 330 << "VariableAcquisitionWorkerPrivate::removeAcqRequest";
372 331 QUuid vIdentifier;
373 332 std::shared_ptr<IDataProvider> provider;
374 333 lockRead();
375 334 auto acqIt = m_AcqIdentifierToAcqRequestMap.find(acqRequestId);
376 335 if (acqIt != m_AcqIdentifierToAcqRequestMap.cend()) {
377 336 vIdentifier = acqIt->second.m_vIdentifier;
378 337 provider = acqIt->second.m_Provider;
379 338
380 auto it = m_VIdentifierToCurrrentAcqIdNextIdPairMap.find(vIdentifier);
381 if (it != m_VIdentifierToCurrrentAcqIdNextIdPairMap.cend()) {
382 if (it->second.first == acqRequestId) {
339 auto it = m_VIdentifierToCurrrentAcqIdMap.find(vIdentifier);
340 if (it != m_VIdentifierToCurrrentAcqIdMap.cend()) {
341 if (it->second == acqRequestId) {
383 342 // acqRequest is currently running -> let's aborting it
384 343 unlock();
385 344
386 // Remove the current request from the worker
387 updateToNextRequest(vIdentifier);
388
389 345 // notify the request aborting to the provider
390 346 provider->requestDataAborting(acqRequestId);
391 347 }
392 else if (it->second.second == acqRequestId) {
393 it->second.second = QUuid();
394 unlock();
395 }
396 348 else {
397 349 unlock();
398 350 }
399 351 }
400 352 else {
401 353 unlock();
402 354 }
403 355 }
404 356 else {
405 357 unlock();
406 358 }
407 359
408 360 lockWrite();
409 361
410 362 m_AcqIdentifierToAcqDataPacketVectorMap.erase(acqRequestId);
411 363 m_AcqIdentifierToAcqRequestMap.erase(acqRequestId);
364 m_VIdentifierToCurrrentAcqIdMap.erase(vIdentifier);
412 365
413 366 unlock();
414 367 qCDebug(LOG_VariableAcquisitionWorker())
415 368 << "VariableAcquisitionWorkerPrivate::removeAcqRequest END";
416 369 }
@@ -1,1104 +1,1101
1 1 #include <Variable/Variable.h>
2 2 #include <Variable/VariableAcquisitionWorker.h>
3 3 #include <Variable/VariableCacheStrategy.h>
4 4 #include <Variable/VariableCacheStrategyFactory.h>
5 5 #include <Variable/VariableController.h>
6 6 #include <Variable/VariableModel.h>
7 7 #include <Variable/VariableSynchronizationGroup.h>
8 8
9 9 #include <Data/DataProviderParameters.h>
10 10 #include <Data/IDataProvider.h>
11 11 #include <Data/IDataSeries.h>
12 12 #include <Data/VariableRequest.h>
13 13 #include <Time/TimeController.h>
14 14
15 15 #include <QDataStream>
16 16 #include <QMutex>
17 17 #include <QThread>
18 18 #include <QUuid>
19 19 #include <QtCore/QItemSelectionModel>
20 20
21 21 #include <deque>
22 22 #include <set>
23 23 #include <unordered_map>
24 24
25 25 Q_LOGGING_CATEGORY(LOG_VariableController, "VariableController")
26 26
27 27 namespace {
28 28
29 29 SqpRange computeSynchroRangeRequested(const SqpRange &varRange, const SqpRange &graphRange,
30 30 const SqpRange &oldGraphRange)
31 31 {
32 32 auto zoomType = VariableController::getZoomType(graphRange, oldGraphRange);
33 33
34 34 auto varRangeRequested = varRange;
35 35 switch (zoomType) {
36 36 case AcquisitionZoomType::ZoomIn: {
37 37 auto deltaLeft = graphRange.m_TStart - oldGraphRange.m_TStart;
38 38 auto deltaRight = oldGraphRange.m_TEnd - graphRange.m_TEnd;
39 39 varRangeRequested.m_TStart += deltaLeft;
40 40 varRangeRequested.m_TEnd -= deltaRight;
41 41 break;
42 42 }
43 43
44 44 case AcquisitionZoomType::ZoomOut: {
45 45 auto deltaLeft = oldGraphRange.m_TStart - graphRange.m_TStart;
46 46 auto deltaRight = graphRange.m_TEnd - oldGraphRange.m_TEnd;
47 47 varRangeRequested.m_TStart -= deltaLeft;
48 48 varRangeRequested.m_TEnd += deltaRight;
49 49 break;
50 50 }
51 51 case AcquisitionZoomType::PanRight: {
52 52 auto deltaLeft = graphRange.m_TStart - oldGraphRange.m_TStart;
53 53 auto deltaRight = graphRange.m_TEnd - oldGraphRange.m_TEnd;
54 54 varRangeRequested.m_TStart += deltaLeft;
55 55 varRangeRequested.m_TEnd += deltaRight;
56 56 break;
57 57 }
58 58 case AcquisitionZoomType::PanLeft: {
59 59 auto deltaLeft = oldGraphRange.m_TStart - graphRange.m_TStart;
60 60 auto deltaRight = oldGraphRange.m_TEnd - graphRange.m_TEnd;
61 61 varRangeRequested.m_TStart -= deltaLeft;
62 62 varRangeRequested.m_TEnd -= deltaRight;
63 63 break;
64 64 }
65 65 case AcquisitionZoomType::Unknown: {
66 66 qCCritical(LOG_VariableController())
67 67 << VariableController::tr("Impossible to synchronize: zoom type unknown");
68 68 break;
69 69 }
70 70 default:
71 71 qCCritical(LOG_VariableController()) << VariableController::tr(
72 72 "Impossible to synchronize: zoom type not take into account");
73 73 // No action
74 74 break;
75 75 }
76 76
77 77 return varRangeRequested;
78 78 }
79 79 }
80 80
81 81 enum class VariableRequestHandlerState { OFF, RUNNING, PENDING };
82 82
83 83 struct VariableRequestHandler {
84 84
85 85 VariableRequestHandler()
86 86 {
87 87 m_CanUpdate = false;
88 88 m_State = VariableRequestHandlerState::OFF;
89 89 }
90 90
91 91 QUuid m_VarId;
92 92 VariableRequest m_RunningVarRequest;
93 93 VariableRequest m_PendingVarRequest;
94 94 VariableRequestHandlerState m_State;
95 95 bool m_CanUpdate;
96 96 };
97 97
98 98 struct VariableController::VariableControllerPrivate {
99 99 explicit VariableControllerPrivate(VariableController *parent)
100 100 : m_WorkingMutex{},
101 101 m_VariableModel{new VariableModel{parent}},
102 102 m_VariableSelectionModel{new QItemSelectionModel{m_VariableModel, parent}},
103 103 // m_VariableCacheStrategy{std::make_unique<VariableCacheStrategy>()},
104 104 m_VariableCacheStrategy{VariableCacheStrategyFactory::createCacheStrategy(
105 105 CacheStrategy::SingleThreshold)},
106 106 m_VariableAcquisitionWorker{std::make_unique<VariableAcquisitionWorker>()},
107 107 q{parent}
108 108 {
109 109
110 110 m_VariableAcquisitionWorker->moveToThread(&m_VariableAcquisitionWorkerThread);
111 111 m_VariableAcquisitionWorkerThread.setObjectName("VariableAcquisitionWorkerThread");
112 112 }
113 113
114 114
115 115 virtual ~VariableControllerPrivate()
116 116 {
117 117 qCDebug(LOG_VariableController()) << tr("VariableControllerPrivate destruction");
118 118 m_VariableAcquisitionWorkerThread.quit();
119 119 m_VariableAcquisitionWorkerThread.wait();
120 120 }
121 121
122 122
123 123 void processRequest(std::shared_ptr<Variable> var, const SqpRange &rangeRequested,
124 124 QUuid varRequestId);
125 125
126 126 std::shared_ptr<Variable> findVariable(QUuid vIdentifier);
127 127 std::shared_ptr<IDataSeries>
128 128 retrieveDataSeries(const QVector<AcquisitionDataPacket> acqDataPacketVector);
129 129
130 130 void registerProvider(std::shared_ptr<IDataProvider> provider);
131 131
132 132 void storeVariableRequest(QUuid varId, QUuid varRequestId, const VariableRequest &varRequest);
133 133 QUuid acceptVariableRequest(QUuid varId, std::shared_ptr<IDataSeries> dataSeries);
134 134 void updateVariables(QUuid varRequestId);
135 135 void updateVariableRequest(QUuid varRequestId);
136 136 void cancelVariableRequest(QUuid varRequestId);
137 137 void executeVarRequest(std::shared_ptr<Variable> var, VariableRequest &varRequest);
138 138
139 139 template <typename VariableIterator>
140 140 void desynchronize(VariableIterator variableIt, const QUuid &syncGroupId);
141 141
142 142 QMutex m_WorkingMutex;
143 143 /// Variable model. The VariableController has the ownership
144 144 VariableModel *m_VariableModel;
145 145 QItemSelectionModel *m_VariableSelectionModel;
146 146
147 147
148 148 TimeController *m_TimeController{nullptr};
149 149 std::unique_ptr<VariableCacheStrategy> m_VariableCacheStrategy;
150 150 std::unique_ptr<VariableAcquisitionWorker> m_VariableAcquisitionWorker;
151 151 QThread m_VariableAcquisitionWorkerThread;
152 152
153 153 std::unordered_map<std::shared_ptr<Variable>, std::shared_ptr<IDataProvider> >
154 154 m_VariableToProviderMap;
155 155 std::unordered_map<std::shared_ptr<Variable>, QUuid> m_VariableToIdentifierMap;
156 156 std::map<QUuid, std::shared_ptr<VariableSynchronizationGroup> >
157 157 m_GroupIdToVariableSynchronizationGroupMap;
158 158 std::map<QUuid, QUuid> m_VariableIdGroupIdMap;
159 159 std::set<std::shared_ptr<IDataProvider> > m_ProviderSet;
160 160
161 161 std::map<QUuid, std::list<QUuid> > m_VarGroupIdToVarIds;
162 162 std::map<QUuid, std::unique_ptr<VariableRequestHandler> > m_VarIdToVarRequestHandler;
163 163
164 164 VariableController *q;
165 165 };
166 166
167 167
168 168 VariableController::VariableController(QObject *parent)
169 169 : QObject{parent}, impl{spimpl::make_unique_impl<VariableControllerPrivate>(this)}
170 170 {
171 171 qCDebug(LOG_VariableController()) << tr("VariableController construction")
172 172 << QThread::currentThread();
173 173
174 174 connect(impl->m_VariableModel, &VariableModel::abortProgessRequested, this,
175 175 &VariableController::onAbortProgressRequested);
176 176
177 177 connect(impl->m_VariableAcquisitionWorker.get(),
178 178 &VariableAcquisitionWorker::variableCanceledRequested, this,
179 179 &VariableController::onAbortAcquisitionRequested);
180 180
181 181 connect(impl->m_VariableAcquisitionWorker.get(), &VariableAcquisitionWorker::dataProvided, this,
182 182 &VariableController::onDataProvided);
183 183 connect(impl->m_VariableAcquisitionWorker.get(),
184 184 &VariableAcquisitionWorker::variableRequestInProgress, this,
185 185 &VariableController::onVariableRetrieveDataInProgress);
186 186
187 187
188 188 connect(&impl->m_VariableAcquisitionWorkerThread, &QThread::started,
189 189 impl->m_VariableAcquisitionWorker.get(), &VariableAcquisitionWorker::initialize);
190 190 connect(&impl->m_VariableAcquisitionWorkerThread, &QThread::finished,
191 191 impl->m_VariableAcquisitionWorker.get(), &VariableAcquisitionWorker::finalize);
192 192
193 193 connect(impl->m_VariableModel, &VariableModel::requestVariableRangeUpdate, this,
194 194 &VariableController::onUpdateDateTime);
195 195
196 196 impl->m_VariableAcquisitionWorkerThread.start();
197 197 }
198 198
199 199 VariableController::~VariableController()
200 200 {
201 201 qCDebug(LOG_VariableController()) << tr("VariableController destruction")
202 202 << QThread::currentThread();
203 203 this->waitForFinish();
204 204 }
205 205
206 206 VariableModel *VariableController::variableModel() noexcept
207 207 {
208 208 return impl->m_VariableModel;
209 209 }
210 210
211 211 QItemSelectionModel *VariableController::variableSelectionModel() noexcept
212 212 {
213 213 return impl->m_VariableSelectionModel;
214 214 }
215 215
216 216 void VariableController::setTimeController(TimeController *timeController) noexcept
217 217 {
218 218 impl->m_TimeController = timeController;
219 219 }
220 220
221 221 std::shared_ptr<Variable>
222 222 VariableController::cloneVariable(std::shared_ptr<Variable> variable) noexcept
223 223 {
224 224 if (impl->m_VariableModel->containsVariable(variable)) {
225 225 // Clones variable
226 226 auto duplicate = variable->clone();
227 227
228 228 // Adds clone to model
229 229 impl->m_VariableModel->addVariable(duplicate);
230 230
231 231 // Generates clone identifier
232 232 impl->m_VariableToIdentifierMap[duplicate] = QUuid::createUuid();
233 233
234 234 // Registers provider
235 235 auto variableProvider = impl->m_VariableToProviderMap.at(variable);
236 236 auto duplicateProvider = variableProvider != nullptr ? variableProvider->clone() : nullptr;
237 237
238 238 impl->m_VariableToProviderMap[duplicate] = duplicateProvider;
239 239 if (duplicateProvider) {
240 240 impl->registerProvider(duplicateProvider);
241 241 }
242 242
243 243 return duplicate;
244 244 }
245 245 else {
246 246 qCCritical(LOG_VariableController())
247 247 << tr("Can't create duplicate of variable %1: variable not registered in the model")
248 248 .arg(variable->name());
249 249 return nullptr;
250 250 }
251 251 }
252 252
253 253 void VariableController::deleteVariable(std::shared_ptr<Variable> variable) noexcept
254 254 {
255 255 if (!variable) {
256 256 qCCritical(LOG_VariableController()) << "Can't delete variable: variable is null";
257 257 return;
258 258 }
259 259
260 260 // Spreads in SciQlop that the variable will be deleted, so that potential receivers can
261 261 // make some treatments before the deletion
262 262 emit variableAboutToBeDeleted(variable);
263 263
264 264 auto variableIt = impl->m_VariableToIdentifierMap.find(variable);
265 265 Q_ASSERT(variableIt != impl->m_VariableToIdentifierMap.cend());
266 266
267 267 auto variableId = variableIt->second;
268 268
269 269 // Removes variable's handler
270 270 impl->m_VarIdToVarRequestHandler.erase(variableId);
271 271
272 272 // Desynchronizes variable (if the variable is in a sync group)
273 273 auto syncGroupIt = impl->m_VariableIdGroupIdMap.find(variableId);
274 274 if (syncGroupIt != impl->m_VariableIdGroupIdMap.cend()) {
275 275 impl->desynchronize(variableIt, syncGroupIt->second);
276 276 }
277 277
278 278 // Deletes identifier
279 279 impl->m_VariableToIdentifierMap.erase(variableIt);
280 280
281 281 // Deletes provider
282 282 auto nbProvidersDeleted = impl->m_VariableToProviderMap.erase(variable);
283 283 qCDebug(LOG_VariableController())
284 284 << tr("Number of providers deleted for variable %1: %2")
285 285 .arg(variable->name(), QString::number(nbProvidersDeleted));
286 286
287 287
288 288 // Deletes from model
289 289 impl->m_VariableModel->deleteVariable(variable);
290 290 }
291 291
292 292 void VariableController::deleteVariables(
293 293 const QVector<std::shared_ptr<Variable> > &variables) noexcept
294 294 {
295 295 for (auto variable : qAsConst(variables)) {
296 296 deleteVariable(variable);
297 297 }
298 298 }
299 299
300 300 QByteArray
301 301 VariableController::mimeDataForVariables(const QList<std::shared_ptr<Variable> > &variables) const
302 302 {
303 303 auto encodedData = QByteArray{};
304 304
305 305 QVariantList ids;
306 306 for (auto &var : variables) {
307 307 auto itVar = impl->m_VariableToIdentifierMap.find(var);
308 308 if (itVar == impl->m_VariableToIdentifierMap.cend()) {
309 309 qCCritical(LOG_VariableController())
310 310 << tr("Impossible to find the data for an unknown variable.");
311 311 }
312 312
313 313 ids << itVar->second.toByteArray();
314 314 }
315 315
316 316 QDataStream stream{&encodedData, QIODevice::WriteOnly};
317 317 stream << ids;
318 318
319 319 return encodedData;
320 320 }
321 321
322 322 QList<std::shared_ptr<Variable> >
323 323 VariableController::variablesForMimeData(const QByteArray &mimeData) const
324 324 {
325 325 auto variables = QList<std::shared_ptr<Variable> >{};
326 326 QDataStream stream{mimeData};
327 327
328 328 QVariantList ids;
329 329 stream >> ids;
330 330
331 331 for (auto id : ids) {
332 332 auto uuid = QUuid{id.toByteArray()};
333 333 auto var = impl->findVariable(uuid);
334 334 variables << var;
335 335 }
336 336
337 337 return variables;
338 338 }
339 339
340 340 std::shared_ptr<Variable>
341 341 VariableController::createVariable(const QString &name, const QVariantHash &metadata,
342 342 std::shared_ptr<IDataProvider> provider) noexcept
343 343 {
344 344 if (!impl->m_TimeController) {
345 345 qCCritical(LOG_VariableController())
346 346 << tr("Impossible to create variable: The time controller is null");
347 347 return nullptr;
348 348 }
349 349
350 350 auto range = impl->m_TimeController->dateTime();
351 351
352 352 if (auto newVariable = impl->m_VariableModel->createVariable(name, metadata)) {
353 353 auto varId = QUuid::createUuid();
354 354
355 355 // Create the handler
356 356 auto varRequestHandler = std::make_unique<VariableRequestHandler>();
357 357 varRequestHandler->m_VarId = varId;
358 358
359 359 impl->m_VarIdToVarRequestHandler.insert(
360 360 std::make_pair(varId, std::move(varRequestHandler)));
361 361
362 362 // store the provider
363 363 impl->registerProvider(provider);
364 364
365 365 // Associate the provider
366 366 impl->m_VariableToProviderMap[newVariable] = provider;
367 367 impl->m_VariableToIdentifierMap[newVariable] = varId;
368 368
369 369 this->onRequestDataLoading(QVector<std::shared_ptr<Variable> >{newVariable}, range, false);
370 370
371 // auto varRequestId = QUuid::createUuid();
372 // qCInfo(LOG_VariableController()) << "createVariable: " << varId << varRequestId;
373 // impl->processRequest(newVariable, range, varRequestId);
374 // impl->updateVariableRequest(varRequestId);
375
376 371 emit variableAdded(newVariable);
377 372
378 373 return newVariable;
379 374 }
380 375
381 376 qCCritical(LOG_VariableController()) << tr("Impossible to create variable");
382 377 return nullptr;
383 378 }
384 379
385 380 void VariableController::onDateTimeOnSelection(const SqpRange &dateTime)
386 381 {
387 382 // NOTE: Even if acquisition request is aborting, the graphe range will be changed
388 383 qCDebug(LOG_VariableController()) << "VariableController::onDateTimeOnSelection"
389 384 << QThread::currentThread()->objectName();
390 385 auto selectedRows = impl->m_VariableSelectionModel->selectedRows();
391 386
392 387 // NOTE we only permit the time modification for one variable
393 // DEPRECATED
394 // auto variables = QVector<std::shared_ptr<Variable> >{};
395 // for (const auto &selectedRow : qAsConst(selectedRows)) {
396 // if (auto selectedVariable =
397 // impl->m_VariableModel->variable(selectedRow.row())) {
398 // variables << selectedVariable;
399
400 // // notify that rescale operation has to be done
401 // emit rangeChanged(selectedVariable, dateTime);
402 // }
403 // }
404 // if (!variables.isEmpty()) {
405 // this->onRequestDataLoading(variables, dateTime, synchro);
406 // }
407 388 if (selectedRows.size() == 1) {
408 389
409 390 if (auto selectedVariable
410 391 = impl->m_VariableModel->variable(qAsConst(selectedRows).first().row())) {
411 392
412 393 onUpdateDateTime(selectedVariable, dateTime);
413 394 }
414 395 }
415 396 else if (selectedRows.size() > 1) {
416 397 qCCritical(LOG_VariableController())
417 398 << tr("Impossible to set time for more than 1 variable in the same time");
418 399 }
419 400 else {
420 401 qCWarning(LOG_VariableController())
421 402 << tr("There is no variable selected to set the time one");
422 403 }
423 404 }
424 405
425 406 void VariableController::onUpdateDateTime(std::shared_ptr<Variable> variable,
426 407 const SqpRange &dateTime)
427 408 {
428 409 auto itVar = impl->m_VariableToIdentifierMap.find(variable);
429 410 if (itVar == impl->m_VariableToIdentifierMap.cend()) {
430 411 qCCritical(LOG_VariableController())
431 412 << tr("Impossible to onDateTimeOnSelection request for unknown variable");
432 413 return;
433 414 }
434 415
435 416 // notify that rescale operation has to be done
436 417 emit rangeChanged(variable, dateTime);
437 418
438 419 auto synchro
439 420 = impl->m_VariableIdGroupIdMap.find(itVar->second) != impl->m_VariableIdGroupIdMap.cend();
440 421
441 422 this->onRequestDataLoading(QVector<std::shared_ptr<Variable> >{variable}, dateTime, synchro);
442 423 }
443 424
444 void VariableController::onDataProvided(QUuid vIdentifier, const SqpRange &rangeRequested,
445 const SqpRange &cacheRangeRequested,
425 void VariableController::onDataProvided(QUuid vIdentifier,
446 426 QVector<AcquisitionDataPacket> dataAcquired)
447 427 {
448 428 qCDebug(LOG_VariableController()) << tr("onDataProvided") << QThread::currentThread();
449 429 auto retrievedDataSeries = impl->retrieveDataSeries(dataAcquired);
450 430 auto varRequestId = impl->acceptVariableRequest(vIdentifier, retrievedDataSeries);
451 431 if (!varRequestId.isNull()) {
452 432 impl->updateVariables(varRequestId);
453 433 }
454 434 }
455 435
456 436 void VariableController::onVariableRetrieveDataInProgress(QUuid identifier, double progress)
457 437 {
458 438 qCDebug(LOG_VariableController())
459 439 << "TORM: variableController::onVariableRetrieveDataInProgress"
460 440 << QThread::currentThread()->objectName() << progress;
461 441 if (auto var = impl->findVariable(identifier)) {
442 qCDebug(LOG_VariableController())
443 << "TORM: variableController::onVariableRetrieveDataInProgress FOUND";
462 444 impl->m_VariableModel->setDataProgress(var, progress);
463 445 }
464 446 else {
465 447 qCCritical(LOG_VariableController())
466 448 << tr("Impossible to notify progression of a null variable");
467 449 }
468 450 }
469 451
470 452 void VariableController::onAbortProgressRequested(std::shared_ptr<Variable> variable)
471 453 {
472 454 qCDebug(LOG_VariableController()) << "TORM: variableController::onAbortProgressRequested"
473 455 << QThread::currentThread()->objectName() << variable->name();
474 456
475 457 auto itVar = impl->m_VariableToIdentifierMap.find(variable);
476 458 if (itVar == impl->m_VariableToIdentifierMap.cend()) {
477 459 qCCritical(LOG_VariableController())
478 460 << tr("Impossible to onAbortProgressRequested request for unknown variable");
479 461 return;
480 462 }
481 463
482 464 auto varId = itVar->second;
483 465
484 466 auto itVarHandler = impl->m_VarIdToVarRequestHandler.find(varId);
485 467 if (itVarHandler == impl->m_VarIdToVarRequestHandler.cend()) {
486 468 qCCritical(LOG_VariableController())
487 469 << tr("Impossible to onAbortProgressRequested for variable with unknown handler");
488 470 return;
489 471 }
490 472
491 473 auto varHandler = itVarHandler->second.get();
492 474
493 475 // case where a variable has a running request
494 476 if (varHandler->m_State != VariableRequestHandlerState::OFF) {
495 477 impl->cancelVariableRequest(varHandler->m_RunningVarRequest.m_VariableGroupId);
496 478 }
497 479 }
498 480
499 481 void VariableController::onAbortAcquisitionRequested(QUuid vIdentifier)
500 482 {
501 483 qCDebug(LOG_VariableController()) << "TORM: variableController::onAbortAcquisitionRequested"
502 484 << QThread::currentThread()->objectName() << vIdentifier;
503 485
504 486 if (auto var = impl->findVariable(vIdentifier)) {
505 487 this->onAbortProgressRequested(var);
506 488 }
507 489 else {
508 490 qCCritical(LOG_VariableController())
509 491 << tr("Impossible to abort Acquisition Requestof a null variable");
510 492 }
511 493 }
512 494
513 495 void VariableController::onAddSynchronizationGroupId(QUuid synchronizationGroupId)
514 496 {
515 497 qCDebug(LOG_VariableController()) << "TORM: VariableController::onAddSynchronizationGroupId"
516 498 << QThread::currentThread()->objectName()
517 499 << synchronizationGroupId;
518 500 auto vSynchroGroup = std::make_shared<VariableSynchronizationGroup>();
519 501 impl->m_GroupIdToVariableSynchronizationGroupMap.insert(
520 502 std::make_pair(synchronizationGroupId, vSynchroGroup));
521 503 }
522 504
523 505 void VariableController::onRemoveSynchronizationGroupId(QUuid synchronizationGroupId)
524 506 {
525 507 impl->m_GroupIdToVariableSynchronizationGroupMap.erase(synchronizationGroupId);
526 508 }
527 509
528 510 void VariableController::onAddSynchronized(std::shared_ptr<Variable> variable,
529 511 QUuid synchronizationGroupId)
530 512
531 513 {
532 514 qCDebug(LOG_VariableController()) << "TORM: VariableController::onAddSynchronized"
533 515 << synchronizationGroupId;
534 516 auto varToVarIdIt = impl->m_VariableToIdentifierMap.find(variable);
535 517 if (varToVarIdIt != impl->m_VariableToIdentifierMap.cend()) {
536 518 auto groupIdToVSGIt
537 519 = impl->m_GroupIdToVariableSynchronizationGroupMap.find(synchronizationGroupId);
538 520 if (groupIdToVSGIt != impl->m_GroupIdToVariableSynchronizationGroupMap.cend()) {
539 521 impl->m_VariableIdGroupIdMap.insert(
540 522 std::make_pair(varToVarIdIt->second, synchronizationGroupId));
541 523 groupIdToVSGIt->second->addVariableId(varToVarIdIt->second);
542 524 }
543 525 else {
544 526 qCCritical(LOG_VariableController())
545 527 << tr("Impossible to synchronize a variable with an unknown sycnhronization group")
546 528 << variable->name();
547 529 }
548 530 }
549 531 else {
550 532 qCCritical(LOG_VariableController())
551 533 << tr("Impossible to synchronize a variable with no identifier") << variable->name();
552 534 }
553 535 }
554 536
555 537 void VariableController::desynchronize(std::shared_ptr<Variable> variable,
556 538 QUuid synchronizationGroupId)
557 539 {
558 540 // Gets variable id
559 541 auto variableIt = impl->m_VariableToIdentifierMap.find(variable);
560 542 if (variableIt == impl->m_VariableToIdentifierMap.cend()) {
561 543 qCCritical(LOG_VariableController())
562 544 << tr("Can't desynchronize variable %1: variable identifier not found")
563 545 .arg(variable->name());
564 546 return;
565 547 }
566 548
567 549 impl->desynchronize(variableIt, synchronizationGroupId);
568 550 }
569 551
570 552 void VariableController::onRequestDataLoading(QVector<std::shared_ptr<Variable> > variables,
571 553 const SqpRange &range, bool synchronise)
572 554 {
573 555 // variables is assumed synchronized
574 556 // TODO: Asser variables synchronization
575 557 // we want to load data of the variable for the dateTime.
576 558 if (variables.isEmpty()) {
577 559 return;
578 560 }
579 561
580 562 auto varRequestId = QUuid::createUuid();
581 563 qCDebug(LOG_VariableController()) << "VariableController::onRequestDataLoading"
582 564 << QThread::currentThread()->objectName() << varRequestId
583 565 << range << synchronise;
584 566
585 567 if (!synchronise) {
586 568 auto varIds = std::list<QUuid>{};
587 569 for (const auto &var : variables) {
588 570 auto vId = impl->m_VariableToIdentifierMap.at(var);
589 571 varIds.push_back(vId);
590 572 }
591 573 impl->m_VarGroupIdToVarIds.insert(std::make_pair(varRequestId, varIds));
592 574 for (const auto &var : variables) {
593 qCDebug(LOG_VariableController()) << "processRequest for" << var->name() << varRequestId
575 qCDebug(LOG_VariableController()) << "onRequestDataLoading: for" << varRequestId
594 576 << varIds.size();
595 577 impl->processRequest(var, range, varRequestId);
596 578 }
597 579 }
598 580 else {
599 581 auto vId = impl->m_VariableToIdentifierMap.at(variables.first());
600 582 auto varIdToGroupIdIt = impl->m_VariableIdGroupIdMap.find(vId);
601 583 if (varIdToGroupIdIt != impl->m_VariableIdGroupIdMap.cend()) {
602 584 auto groupId = varIdToGroupIdIt->second;
603 585
604 586 auto vSynchronizationGroup
605 587 = impl->m_GroupIdToVariableSynchronizationGroupMap.at(groupId);
606 588 auto vSyncIds = vSynchronizationGroup->getIds();
607 589
608 590 auto varIds = std::list<QUuid>{};
609 591 for (auto vId : vSyncIds) {
610 592 varIds.push_back(vId);
611 593 }
594 qCDebug(LOG_VariableController()) << "onRequestDataLoading sync: for" << varRequestId
595 << varIds.size();
612 596 impl->m_VarGroupIdToVarIds.insert(std::make_pair(varRequestId, varIds));
613 597
614 598 for (auto vId : vSyncIds) {
615 599 auto var = impl->findVariable(vId);
616 600
617 601 // Don't process already processed var
618 602 if (var != nullptr) {
619 603 qCDebug(LOG_VariableController()) << "processRequest synchro for" << var->name()
620 604 << varRequestId;
621 605 auto vSyncRangeRequested
622 606 = variables.contains(var)
623 607 ? range
624 608 : computeSynchroRangeRequested(var->range(), range,
625 609 variables.first()->range());
626 610 qCDebug(LOG_VariableController()) << "synchro RR" << vSyncRangeRequested;
627 611 impl->processRequest(var, vSyncRangeRequested, varRequestId);
628 612 }
629 613 else {
630 614 qCCritical(LOG_VariableController())
631 615
632 616 << tr("Impossible to synchronize a null variable");
633 617 }
634 618 }
635 619 }
636 620 }
637 621
638 622 impl->updateVariables(varRequestId);
639 623 }
640 624
641 625
642 626 void VariableController::initialize()
643 627 {
644 628 qCDebug(LOG_VariableController()) << tr("VariableController init") << QThread::currentThread();
645 629 impl->m_WorkingMutex.lock();
646 630 qCDebug(LOG_VariableController()) << tr("VariableController init END");
647 631 }
648 632
649 633 void VariableController::finalize()
650 634 {
651 635 impl->m_WorkingMutex.unlock();
652 636 }
653 637
654 638 void VariableController::waitForFinish()
655 639 {
656 640 QMutexLocker locker{&impl->m_WorkingMutex};
657 641 }
658 642
659 643 AcquisitionZoomType VariableController::getZoomType(const SqpRange &range, const SqpRange &oldRange)
660 644 {
661 645 // t1.m_TStart <= t2.m_TStart && t2.m_TEnd <= t1.m_TEnd
662 646 auto zoomType = AcquisitionZoomType::Unknown;
663 647 if (range.m_TStart <= oldRange.m_TStart && oldRange.m_TEnd <= range.m_TEnd) {
664 648 qCDebug(LOG_VariableController()) << "zoomtype: ZoomOut";
665 649 zoomType = AcquisitionZoomType::ZoomOut;
666 650 }
667 651 else if (range.m_TStart > oldRange.m_TStart && range.m_TEnd > oldRange.m_TEnd) {
668 652 qCDebug(LOG_VariableController()) << "zoomtype: PanRight";
669 653 zoomType = AcquisitionZoomType::PanRight;
670 654 }
671 655 else if (range.m_TStart < oldRange.m_TStart && range.m_TEnd < oldRange.m_TEnd) {
672 656 qCDebug(LOG_VariableController()) << "zoomtype: PanLeft";
673 657 zoomType = AcquisitionZoomType::PanLeft;
674 658 }
675 659 else if (range.m_TStart >= oldRange.m_TStart && oldRange.m_TEnd >= range.m_TEnd) {
676 660 qCDebug(LOG_VariableController()) << "zoomtype: ZoomIn";
677 661 zoomType = AcquisitionZoomType::ZoomIn;
678 662 }
679 663 else {
680 664 qCDebug(LOG_VariableController()) << "getZoomType: Unknown type detected";
681 665 }
682 666 return zoomType;
683 667 }
684 668
685 669 void VariableController::VariableControllerPrivate::processRequest(std::shared_ptr<Variable> var,
686 670 const SqpRange &rangeRequested,
687 671 QUuid varRequestId)
688 672 {
689 673 auto itVar = m_VariableToIdentifierMap.find(var);
690 674 if (itVar == m_VariableToIdentifierMap.cend()) {
691 675 qCCritical(LOG_VariableController())
692 676 << tr("Impossible to process request for unknown variable");
693 677 return;
694 678 }
695 679
696 680 auto varId = itVar->second;
697 681
698 682 auto itVarHandler = m_VarIdToVarRequestHandler.find(varId);
699 683 if (itVarHandler == m_VarIdToVarRequestHandler.cend()) {
700 684 qCCritical(LOG_VariableController())
701 685 << tr("Impossible to process request for variable with unknown handler");
702 686 return;
703 687 }
704 688
705 689 auto oldRange = var->range();
706 690
707 691 auto varHandler = itVarHandler->second.get();
708 692
709 693 if (varHandler->m_State != VariableRequestHandlerState::OFF) {
710 694 oldRange = varHandler->m_RunningVarRequest.m_RangeRequested;
711 695 }
712 696
713 697 auto varRequest = VariableRequest{};
714 698 varRequest.m_VariableGroupId = varRequestId;
715 699 auto varStrategyRangesRequested
716 700 = m_VariableCacheStrategy->computeRange(oldRange, rangeRequested);
717 701 varRequest.m_RangeRequested = varStrategyRangesRequested.first;
718 702 varRequest.m_CacheRangeRequested = varStrategyRangesRequested.second;
719 703
720 704 switch (varHandler->m_State) {
721 705 case VariableRequestHandlerState::OFF: {
722 706 qCDebug(LOG_VariableController()) << tr("Process Request OFF")
723 707 << varRequest.m_RangeRequested
724 708 << varRequest.m_CacheRangeRequested;
725 709 varHandler->m_RunningVarRequest = varRequest;
726 710 varHandler->m_State = VariableRequestHandlerState::RUNNING;
727 711 executeVarRequest(var, varRequest);
728 712 break;
729 713 }
730 714 case VariableRequestHandlerState::RUNNING: {
731 715 qCDebug(LOG_VariableController()) << tr("Process Request RUNNING")
732 716 << varRequest.m_RangeRequested
733 717 << varRequest.m_CacheRangeRequested;
734 718 varHandler->m_State = VariableRequestHandlerState::PENDING;
735 719 varHandler->m_PendingVarRequest = varRequest;
736 720 break;
737 721 }
738 722 case VariableRequestHandlerState::PENDING: {
739 723 qCDebug(LOG_VariableController()) << tr("Process Request PENDING")
740 724 << varRequest.m_RangeRequested
741 725 << varRequest.m_CacheRangeRequested;
742 726 auto variableGroupIdToCancel = varHandler->m_PendingVarRequest.m_VariableGroupId;
743 727 cancelVariableRequest(variableGroupIdToCancel);
744 728 // Cancel variable can make state downgrade
745 729 varHandler->m_State = VariableRequestHandlerState::PENDING;
746 730 varHandler->m_PendingVarRequest = varRequest;
747 731
748 732 break;
749 733 }
750 734 default:
751 735 qCCritical(LOG_VariableController())
752 736 << QObject::tr("Unknown VariableRequestHandlerState");
753 737 }
754 738 }
755 739
756 740 std::shared_ptr<Variable>
757 741 VariableController::VariableControllerPrivate::findVariable(QUuid vIdentifier)
758 742 {
759 743 std::shared_ptr<Variable> var;
760 744 auto findReply = [vIdentifier](const auto &entry) { return vIdentifier == entry.second; };
761 745
762 746 auto end = m_VariableToIdentifierMap.cend();
763 747 auto it = std::find_if(m_VariableToIdentifierMap.cbegin(), end, findReply);
764 748 if (it != end) {
765 749 var = it->first;
766 750 }
767 751 else {
768 752 qCCritical(LOG_VariableController())
769 753 << tr("Impossible to find the variable with the identifier: ") << vIdentifier;
770 754 }
771 755
772 756 return var;
773 757 }
774 758
775 759 std::shared_ptr<IDataSeries> VariableController::VariableControllerPrivate::retrieveDataSeries(
776 760 const QVector<AcquisitionDataPacket> acqDataPacketVector)
777 761 {
778 762 qCDebug(LOG_VariableController()) << tr("TORM: retrieveDataSeries acqDataPacketVector size")
779 763 << acqDataPacketVector.size();
780 764 std::shared_ptr<IDataSeries> dataSeries;
781 765 if (!acqDataPacketVector.isEmpty()) {
782 766 dataSeries = acqDataPacketVector[0].m_DateSeries;
783 767 for (int i = 1; i < acqDataPacketVector.size(); ++i) {
784 768 dataSeries->merge(acqDataPacketVector[i].m_DateSeries.get());
785 769 }
786 770 }
787 771 qCDebug(LOG_VariableController()) << tr("TORM: retrieveDataSeries acqDataPacketVector size END")
788 772 << acqDataPacketVector.size();
789 773 return dataSeries;
790 774 }
791 775
792 776 void VariableController::VariableControllerPrivate::registerProvider(
793 777 std::shared_ptr<IDataProvider> provider)
794 778 {
795 779 if (m_ProviderSet.find(provider) == m_ProviderSet.end()) {
796 780 qCDebug(LOG_VariableController()) << tr("Registering of a new provider")
797 781 << provider->objectName();
798 782 m_ProviderSet.insert(provider);
799 783 connect(provider.get(), &IDataProvider::dataProvided, m_VariableAcquisitionWorker.get(),
800 784 &VariableAcquisitionWorker::onVariableDataAcquired);
801 785 connect(provider.get(), &IDataProvider::dataProvidedProgress,
802 786 m_VariableAcquisitionWorker.get(),
803 787 &VariableAcquisitionWorker::onVariableRetrieveDataInProgress);
804 788 connect(provider.get(), &IDataProvider::dataProvidedFailed,
805 789 m_VariableAcquisitionWorker.get(),
806 790 &VariableAcquisitionWorker::onVariableAcquisitionFailed);
807 791 }
808 792 else {
809 793 qCDebug(LOG_VariableController()) << tr("Cannot register provider, it already exists ");
810 794 }
811 795 }
812 796
813 797 QUuid VariableController::VariableControllerPrivate::acceptVariableRequest(
814 798 QUuid varId, std::shared_ptr<IDataSeries> dataSeries)
815 799 {
816 800 auto itVarHandler = m_VarIdToVarRequestHandler.find(varId);
817 801 if (itVarHandler == m_VarIdToVarRequestHandler.cend()) {
818 802 return QUuid();
819 803 }
820 804
821 805 auto varHandler = itVarHandler->second.get();
822 806 if (varHandler->m_State == VariableRequestHandlerState::OFF) {
823 807 qCCritical(LOG_VariableController())
824 808 << tr("acceptVariableRequest impossible on a variable with OFF state");
825 809 }
826 810
827 811 varHandler->m_RunningVarRequest.m_DataSeries = dataSeries;
828 812 varHandler->m_CanUpdate = true;
829 813
830 814 // Element traitΓ©, on a dΓ©jΓ  toutes les donnΓ©es necessaires
831 815 auto varGroupId = varHandler->m_RunningVarRequest.m_VariableGroupId;
832 816 qCDebug(LOG_VariableController()) << "Variable::acceptVariableRequest" << varGroupId
833 817 << m_VarGroupIdToVarIds.size();
834 818
835 819 return varHandler->m_RunningVarRequest.m_VariableGroupId;
836 820 }
837 821
838 822 void VariableController::VariableControllerPrivate::updateVariables(QUuid varRequestId)
839 823 {
840 824 qCDebug(LOG_VariableController()) << "VariableControllerPrivate::updateVariables"
841 825 << QThread::currentThread()->objectName() << varRequestId;
842 826
843 827 auto varGroupIdToVarIdsIt = m_VarGroupIdToVarIds.find(varRequestId);
844 828 if (varGroupIdToVarIdsIt == m_VarGroupIdToVarIds.end()) {
845 829 qCWarning(LOG_VariableController())
846 830 << tr("Impossible to updateVariables of unknown variables") << varRequestId;
847 831 return;
848 832 }
849 833
850 834 auto &varIds = varGroupIdToVarIdsIt->second;
851 835 auto varIdsEnd = varIds.end();
852 836 bool processVariableUpdate = true;
853 qCDebug(LOG_VariableController()) << "VariableControllerPrivate::updateVariables"
854 << varRequestId << varIds.size();
855 837 for (auto varIdsIt = varIds.begin(); (varIdsIt != varIdsEnd) && processVariableUpdate;
856 838 ++varIdsIt) {
857 839 auto itVarHandler = m_VarIdToVarRequestHandler.find(*varIdsIt);
858 840 if (itVarHandler != m_VarIdToVarRequestHandler.cend()) {
859 841 processVariableUpdate &= itVarHandler->second->m_CanUpdate;
860 842 }
861 843 }
862 844
863 845 if (processVariableUpdate) {
864 qCDebug(LOG_VariableController()) << "Final update OK for the var request" << varIds.size();
865 846 for (auto varIdsIt = varIds.begin(); varIdsIt != varIdsEnd; ++varIdsIt) {
866 847 auto itVarHandler = m_VarIdToVarRequestHandler.find(*varIdsIt);
867 848 if (itVarHandler != m_VarIdToVarRequestHandler.cend()) {
868 849 if (auto var = findVariable(*varIdsIt)) {
869 850 auto &varRequest = itVarHandler->second->m_RunningVarRequest;
870 851 var->setRange(varRequest.m_RangeRequested);
871 852 var->setCacheRange(varRequest.m_CacheRangeRequested);
872 853 qCDebug(LOG_VariableController()) << tr("1: onDataProvided")
873 854 << varRequest.m_RangeRequested
874 855 << varRequest.m_CacheRangeRequested;
875 856 qCDebug(LOG_VariableController()) << tr("2: onDataProvided var points before")
876 857 << var->nbPoints()
877 858 << varRequest.m_DataSeries->nbPoints();
878 859 var->mergeDataSeries(varRequest.m_DataSeries);
879 860 qCDebug(LOG_VariableController()) << tr("3: onDataProvided var points after")
880 861 << var->nbPoints();
881 862
882 863 emit var->updated();
883 qCDebug(LOG_VariableController()) << tr("Update OK");
884 864 }
885 865 else {
886 866 qCCritical(LOG_VariableController())
887 867 << tr("Impossible to update data to a null variable");
888 868 }
889 869 }
890 870 }
891 871 updateVariableRequest(varRequestId);
892 872
893 873 // cleaning varRequestId
894 874 qCDebug(LOG_VariableController()) << tr("m_VarGroupIdToVarIds erase") << varRequestId;
895 875 m_VarGroupIdToVarIds.erase(varRequestId);
896 876 if (m_VarGroupIdToVarIds.empty()) {
897 877 emit q->acquisitionFinished();
898 878 }
899 879 }
900 880 }
901 881
902 882
903 883 void VariableController::VariableControllerPrivate::updateVariableRequest(QUuid varRequestId)
904 884 {
905 885 auto varGroupIdToVarIdsIt = m_VarGroupIdToVarIds.find(varRequestId);
906 886 if (varGroupIdToVarIdsIt == m_VarGroupIdToVarIds.end()) {
907 887 qCCritical(LOG_VariableController()) << QObject::tr(
908 888 "Impossible to updateVariableRequest since varGroupdId isn't here anymore");
909 889
910 890 return;
911 891 }
912 892
913 893 auto &varIds = varGroupIdToVarIdsIt->second;
914 894 auto varIdsEnd = varIds.end();
895
896 // First pass all canUpdate of handler to false
915 897 for (auto varIdsIt = varIds.begin(); (varIdsIt != varIdsEnd); ++varIdsIt) {
916 898 auto itVarHandler = m_VarIdToVarRequestHandler.find(*varIdsIt);
917 899 if (itVarHandler != m_VarIdToVarRequestHandler.cend()) {
918 900
919 901 auto varHandler = itVarHandler->second.get();
920 902 varHandler->m_CanUpdate = false;
903 }
904 }
905 // Second update requests of handler
906 for (auto varIdsIt = varIds.begin(); (varIdsIt != varIdsEnd); ++varIdsIt) {
907 auto itVarHandler = m_VarIdToVarRequestHandler.find(*varIdsIt);
908 if (itVarHandler != m_VarIdToVarRequestHandler.cend()) {
921 909
910 auto varHandler = itVarHandler->second.get();
922 911
923 912 switch (varHandler->m_State) {
924 913 case VariableRequestHandlerState::OFF: {
925 914 qCCritical(LOG_VariableController())
926 915 << QObject::tr("Impossible to update a variable with handler in OFF state");
927 916 } break;
928 917 case VariableRequestHandlerState::RUNNING: {
929 918 varHandler->m_State = VariableRequestHandlerState::OFF;
930 919 varHandler->m_RunningVarRequest = VariableRequest{};
931 920 break;
932 921 }
933 922 case VariableRequestHandlerState::PENDING: {
934 923 varHandler->m_State = VariableRequestHandlerState::RUNNING;
935 924 varHandler->m_RunningVarRequest = varHandler->m_PendingVarRequest;
936 925 varHandler->m_PendingVarRequest = VariableRequest{};
937 926 auto var = findVariable(itVarHandler->first);
938 927 executeVarRequest(var, varHandler->m_RunningVarRequest);
939 928 updateVariables(varHandler->m_RunningVarRequest.m_VariableGroupId);
940 929 break;
941 930 }
942 931 default:
943 932 qCCritical(LOG_VariableController())
944 933 << QObject::tr("Unknown VariableRequestHandlerState");
945 934 }
946 935 }
947 936 }
948 937 }
949 938
950 939
951 940 void VariableController::VariableControllerPrivate::cancelVariableRequest(QUuid varRequestId)
952 941 {
953 942 qCDebug(LOG_VariableController()) << tr("cancelVariableRequest") << varRequestId;
954 943
955 944 auto varGroupIdToVarIdsIt = m_VarGroupIdToVarIds.find(varRequestId);
956 945 if (varGroupIdToVarIdsIt == m_VarGroupIdToVarIds.end()) {
957 946 qCCritical(LOG_VariableController())
958 947 << tr("Impossible to cancelVariableRequest for unknown varGroupdId") << varRequestId;
959 948 return;
960 949 }
961 950
962 951 auto &varIds = varGroupIdToVarIdsIt->second;
963 952 auto varIdsEnd = varIds.end();
953
954 // First pass all canUpdate of handler to false
955 for (auto varIdsIt = varIds.begin(); (varIdsIt != varIdsEnd); ++varIdsIt) {
956 auto itVarHandler = m_VarIdToVarRequestHandler.find(*varIdsIt);
957 if (itVarHandler != m_VarIdToVarRequestHandler.cend()) {
958
959 auto varHandler = itVarHandler->second.get();
960 varHandler->m_CanUpdate = false;
961 }
962 }
963
964 964 for (auto varIdsIt = varIds.begin(); (varIdsIt != varIdsEnd); ++varIdsIt) {
965 965 auto itVarHandler = m_VarIdToVarRequestHandler.find(*varIdsIt);
966 966 if (itVarHandler != m_VarIdToVarRequestHandler.cend()) {
967 967
968 968 auto varHandler = itVarHandler->second.get();
969 969 varHandler->m_VarId = QUuid{};
970 970 switch (varHandler->m_State) {
971 971 case VariableRequestHandlerState::OFF: {
972 972 qCWarning(LOG_VariableController())
973 973 << QObject::tr("Impossible to cancel a variable with no running request");
974 974 break;
975 975 }
976 976 case VariableRequestHandlerState::RUNNING: {
977 977
978 978 if (varHandler->m_RunningVarRequest.m_VariableGroupId == varRequestId) {
979 979 auto var = findVariable(itVarHandler->first);
980 980 auto varProvider = m_VariableToProviderMap.at(var);
981 981 if (varProvider != nullptr) {
982 982 m_VariableAcquisitionWorker->abortProgressRequested(
983 983 itVarHandler->first);
984 984 }
985 985 m_VariableModel->setDataProgress(var, 0.0);
986 varHandler->m_CanUpdate = false;
987 986 varHandler->m_State = VariableRequestHandlerState::OFF;
988 987 varHandler->m_RunningVarRequest = VariableRequest{};
989 988 }
990 989 else {
991 990 // TODO: log Impossible to cancel the running variable request beacause its
992 991 // varRequestId isn't not the canceled one
993 992 }
994 993 break;
995 994 }
996 995 case VariableRequestHandlerState::PENDING: {
997 996 if (varHandler->m_RunningVarRequest.m_VariableGroupId == varRequestId) {
998 997 auto var = findVariable(itVarHandler->first);
999 998 auto varProvider = m_VariableToProviderMap.at(var);
1000 999 if (varProvider != nullptr) {
1001 1000 m_VariableAcquisitionWorker->abortProgressRequested(
1002 1001 itVarHandler->first);
1003 1002 }
1004 1003 m_VariableModel->setDataProgress(var, 0.0);
1005 varHandler->m_CanUpdate = false;
1006 1004 varHandler->m_State = VariableRequestHandlerState::RUNNING;
1007 1005 varHandler->m_RunningVarRequest = varHandler->m_PendingVarRequest;
1008 1006 varHandler->m_PendingVarRequest = VariableRequest{};
1009 1007 executeVarRequest(var, varHandler->m_RunningVarRequest);
1010 1008 }
1011 1009 else if (varHandler->m_PendingVarRequest.m_VariableGroupId == varRequestId) {
1012 1010 varHandler->m_State = VariableRequestHandlerState::RUNNING;
1013 1011 varHandler->m_PendingVarRequest = VariableRequest{};
1014 1012 }
1015 1013 else {
1016 1014 // TODO: log Impossible to cancel the variable request beacause its
1017 1015 // varRequestId isn't not the canceled one
1018 1016 }
1019 1017 break;
1020 1018 }
1021 1019 default:
1022 1020 qCCritical(LOG_VariableController())
1023 1021 << QObject::tr("Unknown VariableRequestHandlerState");
1024 1022 }
1025 1023 }
1026 1024 }
1027 1025 qCDebug(LOG_VariableController()) << tr("cancelVariableRequest: erase") << varRequestId;
1028 1026 m_VarGroupIdToVarIds.erase(varRequestId);
1029 1027 if (m_VarGroupIdToVarIds.empty()) {
1030 1028 emit q->acquisitionFinished();
1031 1029 }
1032 1030 }
1033 1031
1034 1032 void VariableController::VariableControllerPrivate::executeVarRequest(std::shared_ptr<Variable> var,
1035 1033 VariableRequest &varRequest)
1036 1034 {
1037 qCDebug(LOG_VariableController()) << tr("TORM: executeVarRequest");
1038
1039 1035 auto varIdIt = m_VariableToIdentifierMap.find(var);
1040 1036 if (varIdIt == m_VariableToIdentifierMap.cend()) {
1041 1037 qCWarning(LOG_VariableController()) << tr(
1042 1038 "Can't execute request of a variable that is not registered (may has been deleted)");
1043 1039 return;
1044 1040 }
1045 1041
1046 1042 auto varId = varIdIt->second;
1047 1043
1048 1044 auto varCacheRange = var->cacheRange();
1049 1045 auto varCacheRangeRequested = varRequest.m_CacheRangeRequested;
1050 1046 auto notInCacheRangeList
1051 1047 = Variable::provideNotInCacheRangeList(varCacheRange, varCacheRangeRequested);
1052 1048 auto inCacheRangeList
1053 1049 = Variable::provideInCacheRangeList(varCacheRange, varCacheRangeRequested);
1054 1050
1055 1051 if (!notInCacheRangeList.empty()) {
1056 1052
1057 1053 auto varProvider = m_VariableToProviderMap.at(var);
1058 1054 if (varProvider != nullptr) {
1059 1055 qCDebug(LOG_VariableController()) << "executeVarRequest " << varRequest.m_RangeRequested
1060 1056 << varRequest.m_CacheRangeRequested;
1061 1057 m_VariableAcquisitionWorker->pushVariableRequest(
1062 varRequest.m_VariableGroupId, varId, varRequest.m_RangeRequested,
1063 varRequest.m_CacheRangeRequested,
1058 varRequest.m_VariableGroupId, varId,
1064 1059 DataProviderParameters{std::move(notInCacheRangeList), var->metadata()},
1065 1060 varProvider);
1066 1061 }
1067 1062 else {
1068 1063 qCCritical(LOG_VariableController())
1069 1064 << "Impossible to provide data with a null provider";
1070 1065 }
1071 1066
1072 1067 if (!inCacheRangeList.empty()) {
1073 1068 emit q->updateVarDisplaying(var, inCacheRangeList.first());
1074 1069 }
1075 1070 }
1076 1071 else {
1072 qCDebug(LOG_VariableController()) << "All already in the cache "
1073 << varRequest.m_RangeRequested;
1077 1074 acceptVariableRequest(varId,
1078 1075 var->dataSeries()->subDataSeries(varRequest.m_CacheRangeRequested));
1079 1076 }
1080 1077 }
1081 1078
1082 1079 template <typename VariableIterator>
1083 1080 void VariableController::VariableControllerPrivate::desynchronize(VariableIterator variableIt,
1084 1081 const QUuid &syncGroupId)
1085 1082 {
1086 1083 const auto &variable = variableIt->first;
1087 1084 const auto &variableId = variableIt->second;
1088 1085
1089 1086 // Gets synchronization group
1090 1087 auto groupIt = m_GroupIdToVariableSynchronizationGroupMap.find(syncGroupId);
1091 1088 if (groupIt == m_GroupIdToVariableSynchronizationGroupMap.cend()) {
1092 1089 qCCritical(LOG_VariableController())
1093 1090 << tr("Can't desynchronize variable %1: unknown synchronization group")
1094 1091 .arg(variable->name());
1095 1092 return;
1096 1093 }
1097 1094
1098 1095 // Removes variable from synchronization group
1099 1096 auto synchronizationGroup = groupIt->second;
1100 1097 synchronizationGroup->removeVariableId(variableId);
1101 1098
1102 1099 // Removes link between variable and synchronization group
1103 1100 m_VariableIdGroupIdMap.erase(variableId);
1104 1101 }
@@ -1,1082 +1,1082
1 1 #include "Visualization/VisualizationGraphWidget.h"
2 2 #include "Visualization/IVisualizationWidgetVisitor.h"
3 3 #include "Visualization/VisualizationCursorItem.h"
4 4 #include "Visualization/VisualizationDefs.h"
5 5 #include "Visualization/VisualizationGraphHelper.h"
6 6 #include "Visualization/VisualizationGraphRenderingDelegate.h"
7 7 #include "Visualization/VisualizationMultiZoneSelectionDialog.h"
8 8 #include "Visualization/VisualizationSelectionZoneItem.h"
9 9 #include "Visualization/VisualizationSelectionZoneManager.h"
10 10 #include "Visualization/VisualizationWidget.h"
11 11 #include "Visualization/VisualizationZoneWidget.h"
12 12 #include "ui_VisualizationGraphWidget.h"
13 13
14 14 #include <Actions/ActionsGuiController.h>
15 15 #include <Actions/FilteringAction.h>
16 16 #include <Common/MimeTypesDef.h>
17 17 #include <Data/ArrayData.h>
18 18 #include <Data/IDataSeries.h>
19 19 #include <Data/SpectrogramSeries.h>
20 20 #include <DragAndDrop/DragDropGuiController.h>
21 21 #include <Settings/SqpSettingsDefs.h>
22 22 #include <SqpApplication.h>
23 23 #include <Time/TimeController.h>
24 24 #include <Variable/Variable.h>
25 25 #include <Variable/VariableController.h>
26 26
27 27 #include <unordered_map>
28 28
29 29 Q_LOGGING_CATEGORY(LOG_VisualizationGraphWidget, "VisualizationGraphWidget")
30 30
31 31 namespace {
32 32
33 33 /// Key pressed to enable drag&drop in all modes
34 34 const auto DRAG_DROP_MODIFIER = Qt::AltModifier;
35 35
36 36 /// Key pressed to enable zoom on horizontal axis
37 37 const auto HORIZONTAL_ZOOM_MODIFIER = Qt::ControlModifier;
38 38
39 39 /// Key pressed to enable zoom on vertical axis
40 40 const auto VERTICAL_ZOOM_MODIFIER = Qt::ShiftModifier;
41 41
42 42 /// Speed of a step of a wheel event for a pan, in percentage of the axis range
43 43 const auto PAN_SPEED = 5;
44 44
45 45 /// Key pressed to enable a calibration pan
46 46 const auto VERTICAL_PAN_MODIFIER = Qt::AltModifier;
47 47
48 48 /// Key pressed to enable multi selection of selection zones
49 49 const auto MULTI_ZONE_SELECTION_MODIFIER = Qt::ControlModifier;
50 50
51 51 /// Minimum size for the zoom box, in percentage of the axis range
52 52 const auto ZOOM_BOX_MIN_SIZE = 0.8;
53 53
54 54 /// Format of the dates appearing in the label of a cursor
55 55 const auto CURSOR_LABELS_DATETIME_FORMAT = QStringLiteral("yyyy/MM/dd\nhh:mm:ss:zzz");
56 56
57 57 } // namespace
58 58
59 59 struct VisualizationGraphWidget::VisualizationGraphWidgetPrivate {
60 60
61 61 explicit VisualizationGraphWidgetPrivate(const QString &name)
62 62 : m_Name{name},
63 63 m_Flags{GraphFlag::EnableAll},
64 64 m_IsCalibration{false},
65 65 m_RenderingDelegate{nullptr}
66 66 {
67 67 }
68 68
69 69 void updateData(PlottablesMap &plottables, std::shared_ptr<Variable> variable,
70 70 const SqpRange &range)
71 71 {
72 72 VisualizationGraphHelper::updateData(plottables, variable, range);
73 73
74 74 // Prevents that data has changed to update rendering
75 75 m_RenderingDelegate->onPlotUpdated();
76 76 }
77 77
78 78 QString m_Name;
79 79 // 1 variable -> n qcpplot
80 80 std::map<std::shared_ptr<Variable>, PlottablesMap> m_VariableToPlotMultiMap;
81 81 GraphFlags m_Flags;
82 82 bool m_IsCalibration;
83 83 /// Delegate used to attach rendering features to the plot
84 84 std::unique_ptr<VisualizationGraphRenderingDelegate> m_RenderingDelegate;
85 85
86 86 QCPItemRect *m_DrawingZoomRect = nullptr;
87 87 QStack<QPair<QCPRange, QCPRange> > m_ZoomStack;
88 88
89 89 std::unique_ptr<VisualizationCursorItem> m_HorizontalCursor = nullptr;
90 90 std::unique_ptr<VisualizationCursorItem> m_VerticalCursor = nullptr;
91 91
92 92 VisualizationSelectionZoneItem *m_DrawingZone = nullptr;
93 93 VisualizationSelectionZoneItem *m_HoveredZone = nullptr;
94 94 QVector<VisualizationSelectionZoneItem *> m_SelectionZones;
95 95
96 96 bool m_HasMovedMouse = false; // Indicates if the mouse moved in a releaseMouse even
97 97
98 98 bool m_VariableAutoRangeOnInit = true;
99 99
100 100 void startDrawingRect(const QPoint &pos, QCustomPlot &plot)
101 101 {
102 102 removeDrawingRect(plot);
103 103
104 104 auto axisPos = posToAxisPos(pos, plot);
105 105
106 106 m_DrawingZoomRect = new QCPItemRect{&plot};
107 107 QPen p;
108 108 p.setWidth(2);
109 109 m_DrawingZoomRect->setPen(p);
110 110
111 111 m_DrawingZoomRect->topLeft->setCoords(axisPos);
112 112 m_DrawingZoomRect->bottomRight->setCoords(axisPos);
113 113 }
114 114
115 115 void removeDrawingRect(QCustomPlot &plot)
116 116 {
117 117 if (m_DrawingZoomRect) {
118 118 plot.removeItem(m_DrawingZoomRect); // the item is deleted by QCustomPlot
119 119 m_DrawingZoomRect = nullptr;
120 120 plot.replot(QCustomPlot::rpQueuedReplot);
121 121 }
122 122 }
123 123
124 124 void startDrawingZone(const QPoint &pos, VisualizationGraphWidget *graph)
125 125 {
126 126 endDrawingZone(graph);
127 127
128 128 auto axisPos = posToAxisPos(pos, graph->plot());
129 129
130 130 m_DrawingZone = new VisualizationSelectionZoneItem{&graph->plot()};
131 131 m_DrawingZone->setRange(axisPos.x(), axisPos.x());
132 132 m_DrawingZone->setEditionEnabled(false);
133 133 }
134 134
135 135 void endDrawingZone(VisualizationGraphWidget *graph)
136 136 {
137 137 if (m_DrawingZone) {
138 138 auto drawingZoneRange = m_DrawingZone->range();
139 139 if (qAbs(drawingZoneRange.m_TEnd - drawingZoneRange.m_TStart) > 0) {
140 140 m_DrawingZone->setEditionEnabled(true);
141 141 addSelectionZone(m_DrawingZone);
142 142 }
143 143 else {
144 144 graph->plot().removeItem(m_DrawingZone); // the item is deleted by QCustomPlot
145 145 }
146 146
147 147 graph->plot().replot(QCustomPlot::rpQueuedReplot);
148 148 m_DrawingZone = nullptr;
149 149 }
150 150 }
151 151
152 152 void setSelectionZonesEditionEnabled(bool value)
153 153 {
154 154 for (auto s : m_SelectionZones) {
155 155 s->setEditionEnabled(value);
156 156 }
157 157 }
158 158
159 159 void addSelectionZone(VisualizationSelectionZoneItem *zone) { m_SelectionZones << zone; }
160 160
161 161 VisualizationSelectionZoneItem *selectionZoneAt(const QPoint &pos,
162 162 const QCustomPlot &plot) const
163 163 {
164 164 VisualizationSelectionZoneItem *selectionZoneItemUnderCursor = nullptr;
165 165 auto minDistanceToZone = -1;
166 166 for (auto zone : m_SelectionZones) {
167 167 auto distanceToZone = zone->selectTest(pos, false);
168 168 if ((minDistanceToZone < 0 || distanceToZone <= minDistanceToZone)
169 169 && distanceToZone >= 0 && distanceToZone < plot.selectionTolerance()) {
170 170 selectionZoneItemUnderCursor = zone;
171 171 }
172 172 }
173 173
174 174 return selectionZoneItemUnderCursor;
175 175 }
176 176
177 177 QVector<VisualizationSelectionZoneItem *> selectionZonesAt(const QPoint &pos,
178 178 const QCustomPlot &plot) const
179 179 {
180 180 QVector<VisualizationSelectionZoneItem *> zones;
181 181 for (auto zone : m_SelectionZones) {
182 182 auto distanceToZone = zone->selectTest(pos, false);
183 183 if (distanceToZone >= 0 && distanceToZone < plot.selectionTolerance()) {
184 184 zones << zone;
185 185 }
186 186 }
187 187
188 188 return zones;
189 189 }
190 190
191 191 void moveSelectionZoneOnTop(VisualizationSelectionZoneItem *zone, QCustomPlot &plot)
192 192 {
193 193 if (!m_SelectionZones.isEmpty() && m_SelectionZones.last() != zone) {
194 194 zone->moveToTop();
195 195 m_SelectionZones.removeAll(zone);
196 196 m_SelectionZones.append(zone);
197 197 }
198 198 }
199 199
200 200 QPointF posToAxisPos(const QPoint &pos, QCustomPlot &plot) const
201 201 {
202 202 auto axisX = plot.axisRect()->axis(QCPAxis::atBottom);
203 203 auto axisY = plot.axisRect()->axis(QCPAxis::atLeft);
204 204 return QPointF{axisX->pixelToCoord(pos.x()), axisY->pixelToCoord(pos.y())};
205 205 }
206 206
207 207 bool pointIsInAxisRect(const QPointF &axisPoint, QCustomPlot &plot) const
208 208 {
209 209 auto axisX = plot.axisRect()->axis(QCPAxis::atBottom);
210 210 auto axisY = plot.axisRect()->axis(QCPAxis::atLeft);
211 211 return axisX->range().contains(axisPoint.x()) && axisY->range().contains(axisPoint.y());
212 212 }
213 213 };
214 214
215 215 VisualizationGraphWidget::VisualizationGraphWidget(const QString &name, QWidget *parent)
216 216 : VisualizationDragWidget{parent},
217 217 ui{new Ui::VisualizationGraphWidget},
218 218 impl{spimpl::make_unique_impl<VisualizationGraphWidgetPrivate>(name)}
219 219 {
220 220 ui->setupUi(this);
221 221
222 222 // 'Close' options : widget is deleted when closed
223 223 setAttribute(Qt::WA_DeleteOnClose);
224 224
225 225 // Set qcpplot properties :
226 226 // - zoom is enabled
227 227 // - Mouse wheel on qcpplot is intercepted to determine the zoom orientation
228 228 ui->widget->setInteractions(QCP::iRangeZoom);
229 229 ui->widget->axisRect()->setRangeDrag(Qt::Horizontal | Qt::Vertical);
230 230
231 231 // The delegate must be initialized after the ui as it uses the plot
232 232 impl->m_RenderingDelegate = std::make_unique<VisualizationGraphRenderingDelegate>(*this);
233 233
234 234 // Init the cursors
235 235 impl->m_HorizontalCursor = std::make_unique<VisualizationCursorItem>(&plot());
236 236 impl->m_HorizontalCursor->setOrientation(Qt::Horizontal);
237 237 impl->m_VerticalCursor = std::make_unique<VisualizationCursorItem>(&plot());
238 238 impl->m_VerticalCursor->setOrientation(Qt::Vertical);
239 239
240 240 connect(ui->widget, &QCustomPlot::mousePress, this, &VisualizationGraphWidget::onMousePress);
241 241 connect(ui->widget, &QCustomPlot::mouseRelease, this,
242 242 &VisualizationGraphWidget::onMouseRelease);
243 243 connect(ui->widget, &QCustomPlot::mouseMove, this, &VisualizationGraphWidget::onMouseMove);
244 244 connect(ui->widget, &QCustomPlot::mouseWheel, this, &VisualizationGraphWidget::onMouseWheel);
245 245 connect(ui->widget, &QCustomPlot::mouseDoubleClick, this,
246 246 &VisualizationGraphWidget::onMouseDoubleClick);
247 247 connect(ui->widget->xAxis, static_cast<void (QCPAxis::*)(const QCPRange &, const QCPRange &)>(
248 248 &QCPAxis::rangeChanged),
249 this, &VisualizationGraphWidget::onRangeChanged, Qt::DirectConnection);
249 this, &VisualizationGraphWidget::onRangeChanged, Qt::DirectConnection);
250 250
251 251 // Activates menu when right clicking on the graph
252 252 ui->widget->setContextMenuPolicy(Qt::CustomContextMenu);
253 253 connect(ui->widget, &QCustomPlot::customContextMenuRequested, this,
254 254 &VisualizationGraphWidget::onGraphMenuRequested);
255 255
256 256 connect(this, &VisualizationGraphWidget::requestDataLoading, &sqpApp->variableController(),
257 257 &VariableController::onRequestDataLoading);
258 258
259 259 connect(&sqpApp->variableController(), &VariableController::updateVarDisplaying, this,
260 260 &VisualizationGraphWidget::onUpdateVarDisplaying);
261 261
262 262 // Necessary for all platform since Qt::AA_EnableHighDpiScaling is enable.
263 263 plot().setPlottingHint(QCP::phFastPolylines, true);
264 264 }
265 265
266 266
267 267 VisualizationGraphWidget::~VisualizationGraphWidget()
268 268 {
269 269 delete ui;
270 270 }
271 271
272 272 VisualizationZoneWidget *VisualizationGraphWidget::parentZoneWidget() const noexcept
273 273 {
274 274 auto parent = parentWidget();
275 275 while (parent != nullptr && !qobject_cast<VisualizationZoneWidget *>(parent)) {
276 276 parent = parent->parentWidget();
277 277 }
278 278
279 279 return qobject_cast<VisualizationZoneWidget *>(parent);
280 280 }
281 281
282 282 VisualizationWidget *VisualizationGraphWidget::parentVisualizationWidget() const
283 283 {
284 284 auto parent = parentWidget();
285 285 while (parent != nullptr && !qobject_cast<VisualizationWidget *>(parent)) {
286 286 parent = parent->parentWidget();
287 287 }
288 288
289 289 return qobject_cast<VisualizationWidget *>(parent);
290 290 }
291 291
292 292 void VisualizationGraphWidget::setFlags(GraphFlags flags)
293 293 {
294 294 impl->m_Flags = std::move(flags);
295 295 }
296 296
297 297 void VisualizationGraphWidget::addVariable(std::shared_ptr<Variable> variable, SqpRange range)
298 298 {
299 299 /// Lambda used to set graph's units and range according to the variable passed in parameter
300 300 auto loadRange = [this](std::shared_ptr<Variable> variable, const SqpRange &range) {
301 301 impl->m_RenderingDelegate->setAxesUnits(*variable);
302 302
303 303 this->setFlags(GraphFlag::DisableAll);
304 304 setGraphRange(range);
305 305 this->setFlags(GraphFlag::EnableAll);
306 306 emit requestDataLoading({variable}, range, false);
307 307 };
308 308
309 309 connect(variable.get(), SIGNAL(updated()), this, SLOT(onDataCacheVariableUpdated()));
310 310
311 311 // Calls update of graph's range and units when the data of the variable have been initialized.
312 312 // Note: we use QueuedConnection here as the update event must be called in the UI thread
313 313 connect(variable.get(), &Variable::dataInitialized, this,
314 314 [ varW = std::weak_ptr<Variable>{variable}, range, loadRange, this ]() {
315 315 if (auto var = varW.lock()) {
316 316 // If the variable is the first added in the graph, we load its range
317 317 auto firstVariableInGraph = range == INVALID_RANGE;
318 318 auto loadedRange = graphRange();
319 319 if (impl->m_VariableAutoRangeOnInit) {
320 320 loadedRange = firstVariableInGraph ? var->range() : range;
321 321 }
322 322 loadRange(var, loadedRange);
323 323 setYRange(var);
324 324 }
325 325 },
326 326 Qt::QueuedConnection);
327 327
328 328 // Uses delegate to create the qcpplot components according to the variable
329 329 auto createdPlottables = VisualizationGraphHelper::create(variable, *ui->widget);
330 330
331 331 // Sets graph properties
332 332 impl->m_RenderingDelegate->setGraphProperties(*variable, createdPlottables);
333 333
334 334 impl->m_VariableToPlotMultiMap.insert({variable, std::move(createdPlottables)});
335 335
336 336 // If the variable already has its data loaded, load its units and its range in the graph
337 337 if (variable->dataSeries() != nullptr) {
338 338 loadRange(variable, range);
339 339 }
340 340
341 341 emit variableAdded(variable);
342 342 }
343 343
344 344 void VisualizationGraphWidget::removeVariable(std::shared_ptr<Variable> variable) noexcept
345 345 {
346 346 // Each component associated to the variable :
347 347 // - is removed from qcpplot (which deletes it)
348 348 // - is no longer referenced in the map
349 349 auto variableIt = impl->m_VariableToPlotMultiMap.find(variable);
350 350 if (variableIt != impl->m_VariableToPlotMultiMap.cend()) {
351 351 emit variableAboutToBeRemoved(variable);
352 352
353 353 auto &plottablesMap = variableIt->second;
354 354
355 355 for (auto plottableIt = plottablesMap.cbegin(), plottableEnd = plottablesMap.cend();
356 356 plottableIt != plottableEnd;) {
357 357 ui->widget->removePlottable(plottableIt->second);
358 358 plottableIt = plottablesMap.erase(plottableIt);
359 359 }
360 360
361 361 impl->m_VariableToPlotMultiMap.erase(variableIt);
362 362 }
363 363
364 364 // Updates graph
365 365 ui->widget->replot();
366 366 }
367 367
368 368 QList<std::shared_ptr<Variable> > VisualizationGraphWidget::variables() const
369 369 {
370 370 auto variables = QList<std::shared_ptr<Variable> >{};
371 371 for (auto it = std::cbegin(impl->m_VariableToPlotMultiMap);
372 372 it != std::cend(impl->m_VariableToPlotMultiMap); ++it) {
373 373 variables << it->first;
374 374 }
375 375
376 376 return variables;
377 377 }
378 378
379 379 void VisualizationGraphWidget::setYRange(std::shared_ptr<Variable> variable)
380 380 {
381 381 if (!variable) {
382 382 qCCritical(LOG_VisualizationGraphWidget()) << "Can't set y-axis range: variable is null";
383 383 return;
384 384 }
385 385
386 386 VisualizationGraphHelper::setYAxisRange(variable, *ui->widget);
387 387 }
388 388
389 389 SqpRange VisualizationGraphWidget::graphRange() const noexcept
390 390 {
391 391 auto graphRange = ui->widget->xAxis->range();
392 392 return SqpRange{graphRange.lower, graphRange.upper};
393 393 }
394 394
395 395 void VisualizationGraphWidget::setGraphRange(const SqpRange &range, bool calibration)
396 396 {
397 397 qCDebug(LOG_VisualizationGraphWidget()) << tr("VisualizationGraphWidget::setGraphRange START");
398 398
399 399 if (calibration) {
400 400 impl->m_IsCalibration = true;
401 401 }
402 402
403 403 ui->widget->xAxis->setRange(range.m_TStart, range.m_TEnd);
404 404 ui->widget->replot();
405 405
406 406 if (calibration) {
407 407 impl->m_IsCalibration = false;
408 408 }
409 409
410 410 qCDebug(LOG_VisualizationGraphWidget()) << tr("VisualizationGraphWidget::setGraphRange END");
411 411 }
412 412
413 413 void VisualizationGraphWidget::setAutoRangeOnVariableInitialization(bool value)
414 414 {
415 415 impl->m_VariableAutoRangeOnInit = value;
416 416 }
417 417
418 418 QVector<SqpRange> VisualizationGraphWidget::selectionZoneRanges() const
419 419 {
420 420 QVector<SqpRange> ranges;
421 421 for (auto zone : impl->m_SelectionZones) {
422 422 ranges << zone->range();
423 423 }
424 424
425 425 return ranges;
426 426 }
427 427
428 428 void VisualizationGraphWidget::addSelectionZones(const QVector<SqpRange> &ranges)
429 429 {
430 430 for (const auto &range : ranges) {
431 431 // note: ownership is transfered to QCustomPlot
432 432 auto zone = new VisualizationSelectionZoneItem(&plot());
433 433 zone->setRange(range.m_TStart, range.m_TEnd);
434 434 impl->addSelectionZone(zone);
435 435 }
436 436
437 437 plot().replot(QCustomPlot::rpQueuedReplot);
438 438 }
439 439
440 440 VisualizationSelectionZoneItem *VisualizationGraphWidget::addSelectionZone(const QString &name,
441 441 const SqpRange &range)
442 442 {
443 443 // note: ownership is transfered to QCustomPlot
444 444 auto zone = new VisualizationSelectionZoneItem(&plot());
445 445 zone->setName(name);
446 446 zone->setRange(range.m_TStart, range.m_TEnd);
447 447 impl->addSelectionZone(zone);
448 448
449 449 plot().replot(QCustomPlot::rpQueuedReplot);
450 450
451 451 return zone;
452 452 }
453 453
454 454 void VisualizationGraphWidget::removeSelectionZone(VisualizationSelectionZoneItem *selectionZone)
455 455 {
456 456 parentVisualizationWidget()->selectionZoneManager().setSelected(selectionZone, false);
457 457
458 458 if (impl->m_HoveredZone == selectionZone) {
459 459 impl->m_HoveredZone = nullptr;
460 460 setCursor(Qt::ArrowCursor);
461 461 }
462 462
463 463 impl->m_SelectionZones.removeAll(selectionZone);
464 464 plot().removeItem(selectionZone);
465 465 plot().replot(QCustomPlot::rpQueuedReplot);
466 466 }
467 467
468 468 void VisualizationGraphWidget::undoZoom()
469 469 {
470 470 auto zoom = impl->m_ZoomStack.pop();
471 471 auto axisX = plot().axisRect()->axis(QCPAxis::atBottom);
472 472 auto axisY = plot().axisRect()->axis(QCPAxis::atLeft);
473 473
474 474 axisX->setRange(zoom.first);
475 475 axisY->setRange(zoom.second);
476 476
477 477 plot().replot(QCustomPlot::rpQueuedReplot);
478 478 }
479 479
480 480 void VisualizationGraphWidget::accept(IVisualizationWidgetVisitor *visitor)
481 481 {
482 482 if (visitor) {
483 483 visitor->visit(this);
484 484 }
485 485 else {
486 486 qCCritical(LOG_VisualizationGraphWidget())
487 487 << tr("Can't visit widget : the visitor is null");
488 488 }
489 489 }
490 490
491 491 bool VisualizationGraphWidget::canDrop(const Variable &variable) const
492 492 {
493 493 auto isSpectrogram = [](const auto &variable) {
494 494 return std::dynamic_pointer_cast<SpectrogramSeries>(variable.dataSeries()) != nullptr;
495 495 };
496 496
497 497 // - A spectrogram series can't be dropped on graph with existing plottables
498 498 // - No data series can be dropped on graph with existing spectrogram series
499 499 return isSpectrogram(variable)
500 500 ? impl->m_VariableToPlotMultiMap.empty()
501 501 : std::none_of(
502 502 impl->m_VariableToPlotMultiMap.cbegin(), impl->m_VariableToPlotMultiMap.cend(),
503 503 [isSpectrogram](const auto &entry) { return isSpectrogram(*entry.first); });
504 504 }
505 505
506 506 bool VisualizationGraphWidget::contains(const Variable &variable) const
507 507 {
508 508 // Finds the variable among the keys of the map
509 509 auto variablePtr = &variable;
510 510 auto findVariable
511 511 = [variablePtr](const auto &entry) { return variablePtr == entry.first.get(); };
512 512
513 513 auto end = impl->m_VariableToPlotMultiMap.cend();
514 514 auto it = std::find_if(impl->m_VariableToPlotMultiMap.cbegin(), end, findVariable);
515 515 return it != end;
516 516 }
517 517
518 518 QString VisualizationGraphWidget::name() const
519 519 {
520 520 return impl->m_Name;
521 521 }
522 522
523 523 QMimeData *VisualizationGraphWidget::mimeData(const QPoint &position) const
524 524 {
525 525 auto mimeData = new QMimeData;
526 526
527 527 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(position, plot());
528 528 if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones
529 529 && selectionZoneItemUnderCursor) {
530 530 mimeData->setData(MIME_TYPE_TIME_RANGE, TimeController::mimeDataForTimeRange(
531 531 selectionZoneItemUnderCursor->range()));
532 532 mimeData->setData(MIME_TYPE_SELECTION_ZONE, TimeController::mimeDataForTimeRange(
533 533 selectionZoneItemUnderCursor->range()));
534 534 }
535 535 else {
536 536 mimeData->setData(MIME_TYPE_GRAPH, QByteArray{});
537 537
538 538 auto timeRangeData = TimeController::mimeDataForTimeRange(graphRange());
539 539 mimeData->setData(MIME_TYPE_TIME_RANGE, timeRangeData);
540 540 }
541 541
542 542 return mimeData;
543 543 }
544 544
545 545 QPixmap VisualizationGraphWidget::customDragPixmap(const QPoint &dragPosition)
546 546 {
547 547 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(dragPosition, plot());
548 548 if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones
549 549 && selectionZoneItemUnderCursor) {
550 550
551 551 auto zoneTopLeft = selectionZoneItemUnderCursor->topLeft->pixelPosition();
552 552 auto zoneBottomRight = selectionZoneItemUnderCursor->bottomRight->pixelPosition();
553 553
554 554 auto zoneSize = QSizeF{qAbs(zoneBottomRight.x() - zoneTopLeft.x()),
555 555 qAbs(zoneBottomRight.y() - zoneTopLeft.y())}
556 556 .toSize();
557 557
558 558 auto pixmap = QPixmap(zoneSize);
559 559 render(&pixmap, QPoint(), QRegion{QRect{zoneTopLeft.toPoint(), zoneSize}});
560 560
561 561 return pixmap;
562 562 }
563 563
564 564 return QPixmap();
565 565 }
566 566
567 567 bool VisualizationGraphWidget::isDragAllowed() const
568 568 {
569 569 return true;
570 570 }
571 571
572 572 void VisualizationGraphWidget::highlightForMerge(bool highlighted)
573 573 {
574 574 if (highlighted) {
575 575 plot().setBackground(QBrush(QColor("#BBD5EE")));
576 576 }
577 577 else {
578 578 plot().setBackground(QBrush(Qt::white));
579 579 }
580 580
581 581 plot().update();
582 582 }
583 583
584 584 void VisualizationGraphWidget::addVerticalCursor(double time)
585 585 {
586 586 impl->m_VerticalCursor->setPosition(time);
587 587 impl->m_VerticalCursor->setVisible(true);
588 588
589 589 auto text
590 590 = DateUtils::dateTime(time).toString(CURSOR_LABELS_DATETIME_FORMAT).replace(' ', '\n');
591 591 impl->m_VerticalCursor->setLabelText(text);
592 592 }
593 593
594 594 void VisualizationGraphWidget::addVerticalCursorAtViewportPosition(double position)
595 595 {
596 596 impl->m_VerticalCursor->setAbsolutePosition(position);
597 597 impl->m_VerticalCursor->setVisible(true);
598 598
599 599 auto axis = plot().axisRect()->axis(QCPAxis::atBottom);
600 600 auto text
601 601 = DateUtils::dateTime(axis->pixelToCoord(position)).toString(CURSOR_LABELS_DATETIME_FORMAT);
602 602 impl->m_VerticalCursor->setLabelText(text);
603 603 }
604 604
605 605 void VisualizationGraphWidget::removeVerticalCursor()
606 606 {
607 607 impl->m_VerticalCursor->setVisible(false);
608 608 plot().replot(QCustomPlot::rpQueuedReplot);
609 609 }
610 610
611 611 void VisualizationGraphWidget::addHorizontalCursor(double value)
612 612 {
613 613 impl->m_HorizontalCursor->setPosition(value);
614 614 impl->m_HorizontalCursor->setVisible(true);
615 615 impl->m_HorizontalCursor->setLabelText(QString::number(value));
616 616 }
617 617
618 618 void VisualizationGraphWidget::addHorizontalCursorAtViewportPosition(double position)
619 619 {
620 620 impl->m_HorizontalCursor->setAbsolutePosition(position);
621 621 impl->m_HorizontalCursor->setVisible(true);
622 622
623 623 auto axis = plot().axisRect()->axis(QCPAxis::atLeft);
624 624 impl->m_HorizontalCursor->setLabelText(QString::number(axis->pixelToCoord(position)));
625 625 }
626 626
627 627 void VisualizationGraphWidget::removeHorizontalCursor()
628 628 {
629 629 impl->m_HorizontalCursor->setVisible(false);
630 630 plot().replot(QCustomPlot::rpQueuedReplot);
631 631 }
632 632
633 633 void VisualizationGraphWidget::closeEvent(QCloseEvent *event)
634 634 {
635 635 Q_UNUSED(event);
636 636
637 637 for (auto i : impl->m_SelectionZones) {
638 638 parentVisualizationWidget()->selectionZoneManager().setSelected(i, false);
639 639 }
640 640
641 641 // Prevents that all variables will be removed from graph when it will be closed
642 642 for (auto &variableEntry : impl->m_VariableToPlotMultiMap) {
643 643 emit variableAboutToBeRemoved(variableEntry.first);
644 644 }
645 645 }
646 646
647 647 void VisualizationGraphWidget::enterEvent(QEvent *event)
648 648 {
649 649 Q_UNUSED(event);
650 650 impl->m_RenderingDelegate->showGraphOverlay(true);
651 651 }
652 652
653 653 void VisualizationGraphWidget::leaveEvent(QEvent *event)
654 654 {
655 655 Q_UNUSED(event);
656 656 impl->m_RenderingDelegate->showGraphOverlay(false);
657 657
658 658 if (auto parentZone = parentZoneWidget()) {
659 659 parentZone->notifyMouseLeaveGraph(this);
660 660 }
661 661 else {
662 662 qCWarning(LOG_VisualizationGraphWidget()) << "leaveEvent: No parent zone widget";
663 663 }
664 664
665 665 if (impl->m_HoveredZone) {
666 666 impl->m_HoveredZone->setHovered(false);
667 667 impl->m_HoveredZone = nullptr;
668 668 }
669 669 }
670 670
671 671 QCustomPlot &VisualizationGraphWidget::plot() const noexcept
672 672 {
673 673 return *ui->widget;
674 674 }
675 675
676 676 void VisualizationGraphWidget::onGraphMenuRequested(const QPoint &pos) noexcept
677 677 {
678 678 QMenu graphMenu{};
679 679
680 680 // Iterates on variables (unique keys)
681 681 for (auto it = impl->m_VariableToPlotMultiMap.cbegin(),
682 682 end = impl->m_VariableToPlotMultiMap.cend();
683 683 it != end; it = impl->m_VariableToPlotMultiMap.upper_bound(it->first)) {
684 684 // 'Remove variable' action
685 685 graphMenu.addAction(tr("Remove variable %1").arg(it->first->name()),
686 686 [ this, var = it->first ]() { removeVariable(var); });
687 687 }
688 688
689 689 if (!impl->m_ZoomStack.isEmpty()) {
690 690 if (!graphMenu.isEmpty()) {
691 691 graphMenu.addSeparator();
692 692 }
693 693
694 694 graphMenu.addAction(tr("Undo Zoom"), [this]() { undoZoom(); });
695 695 }
696 696
697 697 // Selection Zone Actions
698 698 auto selectionZoneItem = impl->selectionZoneAt(pos, plot());
699 699 if (selectionZoneItem) {
700 700 auto selectedItems = parentVisualizationWidget()->selectionZoneManager().selectedItems();
701 701 selectedItems.removeAll(selectionZoneItem);
702 702 selectedItems.prepend(selectionZoneItem); // Put the current selection zone first
703 703
704 704 auto zoneActions = sqpApp->actionsGuiController().selectionZoneActions();
705 705 if (!zoneActions.isEmpty() && !graphMenu.isEmpty()) {
706 706 graphMenu.addSeparator();
707 707 }
708 708
709 709 QHash<QString, QMenu *> subMenus;
710 710 QHash<QString, bool> subMenusEnabled;
711 711 QHash<QString, FilteringAction *> filteredMenu;
712 712
713 713 for (auto zoneAction : zoneActions) {
714 714
715 715 auto isEnabled = zoneAction->isEnabled(selectedItems);
716 716
717 717 auto menu = &graphMenu;
718 718 QString menuPath;
719 719 for (auto subMenuName : zoneAction->subMenuList()) {
720 720 menuPath += '/';
721 721 menuPath += subMenuName;
722 722
723 723 if (!subMenus.contains(menuPath)) {
724 724 menu = menu->addMenu(subMenuName);
725 725 subMenus[menuPath] = menu;
726 726 subMenusEnabled[menuPath] = isEnabled;
727 727 }
728 728 else {
729 729 menu = subMenus.value(menuPath);
730 730 if (isEnabled) {
731 731 // The sub menu is enabled if at least one of its actions is enabled
732 732 subMenusEnabled[menuPath] = true;
733 733 }
734 734 }
735 735 }
736 736
737 737 FilteringAction *filterAction = nullptr;
738 738 if (sqpApp->actionsGuiController().isMenuFiltered(zoneAction->subMenuList())) {
739 739 filterAction = filteredMenu.value(menuPath);
740 740 if (!filterAction) {
741 741 filterAction = new FilteringAction{this};
742 742 filteredMenu[menuPath] = filterAction;
743 743 menu->addAction(filterAction);
744 744 }
745 745 }
746 746
747 747 auto action = menu->addAction(zoneAction->name());
748 748 action->setEnabled(isEnabled);
749 749 action->setShortcut(zoneAction->displayedShortcut());
750 750 QObject::connect(action, &QAction::triggered,
751 751 [zoneAction, selectedItems]() { zoneAction->execute(selectedItems); });
752 752
753 753 if (filterAction && zoneAction->isFilteringAllowed()) {
754 754 filterAction->addActionToFilter(action);
755 755 }
756 756 }
757 757
758 758 for (auto it = subMenus.cbegin(); it != subMenus.cend(); ++it) {
759 759 it.value()->setEnabled(subMenusEnabled[it.key()]);
760 760 }
761 761 }
762 762
763 763 if (!graphMenu.isEmpty()) {
764 764 graphMenu.exec(QCursor::pos());
765 765 }
766 766 }
767 767
768 768 void VisualizationGraphWidget::onRangeChanged(const QCPRange &t1, const QCPRange &t2)
769 769 {
770 770 qCDebug(LOG_VisualizationGraphWidget()) << tr("TORM: VisualizationGraphWidget::onRangeChanged")
771 771 << QThread::currentThread()->objectName() << "DoAcqui"
772 772 << impl->m_Flags.testFlag(GraphFlag::EnableAcquisition);
773 773
774 774 auto graphRange = SqpRange{t1.lower, t1.upper};
775 775 auto oldGraphRange = SqpRange{t2.lower, t2.upper};
776 776
777 777 if (impl->m_Flags.testFlag(GraphFlag::EnableAcquisition)) {
778 778 QVector<std::shared_ptr<Variable> > variableUnderGraphVector;
779 779
780 780 for (auto it = impl->m_VariableToPlotMultiMap.begin(),
781 781 end = impl->m_VariableToPlotMultiMap.end();
782 782 it != end; it = impl->m_VariableToPlotMultiMap.upper_bound(it->first)) {
783 783 variableUnderGraphVector.push_back(it->first);
784 784 }
785 785 emit requestDataLoading(std::move(variableUnderGraphVector), graphRange,
786 786 !impl->m_IsCalibration);
787 787 }
788 788
789 789 if (impl->m_Flags.testFlag(GraphFlag::EnableSynchronization) && !impl->m_IsCalibration) {
790 790 qCDebug(LOG_VisualizationGraphWidget())
791 791 << tr("TORM: VisualizationGraphWidget::Synchronize notify !!")
792 792 << QThread::currentThread()->objectName() << graphRange << oldGraphRange;
793 793 emit synchronize(graphRange, oldGraphRange);
794 794 }
795 795
796 796 auto pos = mapFromGlobal(QCursor::pos());
797 797 auto axisPos = impl->posToAxisPos(pos, plot());
798 798 if (auto parentZone = parentZoneWidget()) {
799 799 if (impl->pointIsInAxisRect(axisPos, plot())) {
800 800 parentZone->notifyMouseMoveInGraph(pos, axisPos, this);
801 801 }
802 802 else {
803 803 parentZone->notifyMouseLeaveGraph(this);
804 804 }
805 805 }
806 806 else {
807 807 qCWarning(LOG_VisualizationGraphWidget()) << "onMouseMove: No parent zone widget";
808 808 }
809 809
810 810 // Quits calibration
811 811 impl->m_IsCalibration = false;
812 812 }
813 813
814 814 void VisualizationGraphWidget::onMouseDoubleClick(QMouseEvent *event) noexcept
815 815 {
816 816 impl->m_RenderingDelegate->onMouseDoubleClick(event);
817 817 }
818 818
819 819 void VisualizationGraphWidget::onMouseMove(QMouseEvent *event) noexcept
820 820 {
821 821 // Handles plot rendering when mouse is moving
822 822 impl->m_RenderingDelegate->onMouseMove(event);
823 823
824 824 auto axisPos = impl->posToAxisPos(event->pos(), plot());
825 825
826 826 // Zoom box and zone drawing
827 827 if (impl->m_DrawingZoomRect) {
828 828 impl->m_DrawingZoomRect->bottomRight->setCoords(axisPos);
829 829 }
830 830 else if (impl->m_DrawingZone) {
831 831 impl->m_DrawingZone->setEnd(axisPos.x());
832 832 }
833 833
834 834 // Cursor
835 835 if (auto parentZone = parentZoneWidget()) {
836 836 if (impl->pointIsInAxisRect(axisPos, plot())) {
837 837 parentZone->notifyMouseMoveInGraph(event->pos(), axisPos, this);
838 838 }
839 839 else {
840 840 parentZone->notifyMouseLeaveGraph(this);
841 841 }
842 842 }
843 843 else {
844 844 qCWarning(LOG_VisualizationGraphWidget()) << "onMouseMove: No parent zone widget";
845 845 }
846 846
847 847 // Search for the selection zone under the mouse
848 848 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(event->pos(), plot());
849 849 if (selectionZoneItemUnderCursor && !impl->m_DrawingZone
850 850 && sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones) {
851 851
852 852 // Sets the appropriate cursor shape
853 853 auto cursorShape = selectionZoneItemUnderCursor->curshorShapeForPosition(event->pos());
854 854 setCursor(cursorShape);
855 855
856 856 // Manages the hovered zone
857 857 if (selectionZoneItemUnderCursor != impl->m_HoveredZone) {
858 858 if (impl->m_HoveredZone) {
859 859 impl->m_HoveredZone->setHovered(false);
860 860 }
861 861 selectionZoneItemUnderCursor->setHovered(true);
862 862 impl->m_HoveredZone = selectionZoneItemUnderCursor;
863 863 plot().replot(QCustomPlot::rpQueuedReplot);
864 864 }
865 865 }
866 866 else {
867 867 // There is no zone under the mouse or the interaction mode is not "selection zones"
868 868 if (impl->m_HoveredZone) {
869 869 impl->m_HoveredZone->setHovered(false);
870 870 impl->m_HoveredZone = nullptr;
871 871 }
872 872
873 873 setCursor(Qt::ArrowCursor);
874 874 }
875 875
876 876 impl->m_HasMovedMouse = true;
877 877 VisualizationDragWidget::mouseMoveEvent(event);
878 878 }
879 879
880 880 void VisualizationGraphWidget::onMouseWheel(QWheelEvent *event) noexcept
881 881 {
882 882 // Processes event only if the wheel occurs on axis rect
883 883 if (!dynamic_cast<QCPAxisRect *>(ui->widget->layoutElementAt(event->posF()))) {
884 884 return;
885 885 }
886 886
887 887 auto value = event->angleDelta().x() + event->angleDelta().y();
888 888 if (value != 0) {
889 889
890 890 auto direction = value > 0 ? 1.0 : -1.0;
891 891 auto isZoomX = event->modifiers().testFlag(HORIZONTAL_ZOOM_MODIFIER);
892 892 auto isZoomY = event->modifiers().testFlag(VERTICAL_ZOOM_MODIFIER);
893 893 impl->m_IsCalibration = event->modifiers().testFlag(VERTICAL_PAN_MODIFIER);
894 894
895 895 auto zoomOrientations = QFlags<Qt::Orientation>{};
896 896 zoomOrientations.setFlag(Qt::Horizontal, isZoomX);
897 897 zoomOrientations.setFlag(Qt::Vertical, isZoomY);
898 898
899 899 ui->widget->axisRect()->setRangeZoom(zoomOrientations);
900 900
901 901 if (!isZoomX && !isZoomY) {
902 902 auto axis = plot().axisRect()->axis(QCPAxis::atBottom);
903 903 auto diff = direction * (axis->range().size() * (PAN_SPEED / 100.0));
904 904
905 905 axis->setRange(axis->range() + diff);
906 906
907 907 if (plot().noAntialiasingOnDrag()) {
908 908 plot().setNotAntialiasedElements(QCP::aeAll);
909 909 }
910 910
911 911 plot().replot(QCustomPlot::rpQueuedReplot);
912 912 }
913 913 }
914 914 }
915 915
916 916 void VisualizationGraphWidget::onMousePress(QMouseEvent *event) noexcept
917 917 {
918 918 auto isDragDropClick = event->modifiers().testFlag(DRAG_DROP_MODIFIER);
919 919 auto isSelectionZoneMode
920 920 = sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones;
921 921 auto isLeftClick = event->buttons().testFlag(Qt::LeftButton);
922 922
923 923 if (!isDragDropClick && isLeftClick) {
924 924 if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::ZoomBox) {
925 925 // Starts a zoom box
926 926 impl->startDrawingRect(event->pos(), plot());
927 927 }
928 928 else if (isSelectionZoneMode && impl->m_DrawingZone == nullptr) {
929 929 // Starts a new selection zone
930 930 auto zoneAtPos = impl->selectionZoneAt(event->pos(), plot());
931 931 if (!zoneAtPos) {
932 932 impl->startDrawingZone(event->pos(), this);
933 933 }
934 934 }
935 935 }
936 936
937 937 // Allows mouse panning only in default mode
938 938 plot().setInteraction(QCP::iRangeDrag, sqpApp->plotsInteractionMode()
939 939 == SqpApplication::PlotsInteractionMode::None
940 940 && !isDragDropClick);
941 941
942 942 // Allows zone edition only in selection zone mode without drag&drop
943 943 impl->setSelectionZonesEditionEnabled(isSelectionZoneMode && !isDragDropClick);
944 944
945 945 // Selection / Deselection
946 946 if (isSelectionZoneMode) {
947 947 auto isMultiSelectionClick = event->modifiers().testFlag(MULTI_ZONE_SELECTION_MODIFIER);
948 948 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(event->pos(), plot());
949 949
950 950
951 951 if (selectionZoneItemUnderCursor && !selectionZoneItemUnderCursor->selected()
952 952 && !isMultiSelectionClick) {
953 953 parentVisualizationWidget()->selectionZoneManager().select(
954 954 {selectionZoneItemUnderCursor});
955 955 }
956 956 else if (!selectionZoneItemUnderCursor && !isMultiSelectionClick && isLeftClick) {
957 957 parentVisualizationWidget()->selectionZoneManager().clearSelection();
958 958 }
959 959 else {
960 960 // No selection change
961 961 }
962 962
963 963 if (selectionZoneItemUnderCursor && isLeftClick) {
964 964 selectionZoneItemUnderCursor->setAssociatedEditedZones(
965 965 parentVisualizationWidget()->selectionZoneManager().selectedItems());
966 966 }
967 967 }
968 968
969 969
970 970 impl->m_HasMovedMouse = false;
971 971 VisualizationDragWidget::mousePressEvent(event);
972 972 }
973 973
974 974 void VisualizationGraphWidget::onMouseRelease(QMouseEvent *event) noexcept
975 975 {
976 976 if (impl->m_DrawingZoomRect) {
977 977
978 978 auto axisX = plot().axisRect()->axis(QCPAxis::atBottom);
979 979 auto axisY = plot().axisRect()->axis(QCPAxis::atLeft);
980 980
981 981 auto newAxisXRange = QCPRange{impl->m_DrawingZoomRect->topLeft->coords().x(),
982 982 impl->m_DrawingZoomRect->bottomRight->coords().x()};
983 983
984 984 auto newAxisYRange = QCPRange{impl->m_DrawingZoomRect->topLeft->coords().y(),
985 985 impl->m_DrawingZoomRect->bottomRight->coords().y()};
986 986
987 987 impl->removeDrawingRect(plot());
988 988
989 989 if (newAxisXRange.size() > axisX->range().size() * (ZOOM_BOX_MIN_SIZE / 100.0)
990 990 && newAxisYRange.size() > axisY->range().size() * (ZOOM_BOX_MIN_SIZE / 100.0)) {
991 991 impl->m_ZoomStack.push(qMakePair(axisX->range(), axisY->range()));
992 992 axisX->setRange(newAxisXRange);
993 993 axisY->setRange(newAxisYRange);
994 994
995 995 plot().replot(QCustomPlot::rpQueuedReplot);
996 996 }
997 997 }
998 998
999 999 impl->endDrawingZone(this);
1000 1000
1001 1001 // Selection / Deselection
1002 1002 auto isSelectionZoneMode
1003 1003 = sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones;
1004 1004 if (isSelectionZoneMode) {
1005 1005 auto isMultiSelectionClick = event->modifiers().testFlag(MULTI_ZONE_SELECTION_MODIFIER);
1006 1006 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(event->pos(), plot());
1007 1007 if (selectionZoneItemUnderCursor && event->button() == Qt::LeftButton
1008 1008 && !impl->m_HasMovedMouse) {
1009 1009
1010 1010 auto zonesUnderCursor = impl->selectionZonesAt(event->pos(), plot());
1011 1011 if (zonesUnderCursor.count() > 1) {
1012 1012 // There are multiple zones under the mouse.
1013 1013 // Performs the selection with a selection dialog.
1014 1014 VisualizationMultiZoneSelectionDialog dialog{this};
1015 1015 dialog.setZones(zonesUnderCursor);
1016 1016 dialog.move(mapToGlobal(event->pos() - QPoint(dialog.width() / 2, 20)));
1017 1017 dialog.activateWindow();
1018 1018 dialog.raise();
1019 1019 if (dialog.exec() == QDialog::Accepted) {
1020 1020 auto selection = dialog.selectedZones();
1021 1021
1022 1022 if (!isMultiSelectionClick) {
1023 1023 parentVisualizationWidget()->selectionZoneManager().clearSelection();
1024 1024 }
1025 1025
1026 1026 for (auto it = selection.cbegin(); it != selection.cend(); ++it) {
1027 1027 auto zone = it.key();
1028 1028 auto isSelected = it.value();
1029 1029 parentVisualizationWidget()->selectionZoneManager().setSelected(zone,
1030 1030 isSelected);
1031 1031
1032 1032 if (isSelected) {
1033 1033 // Puts the zone on top of the stack so it can be moved or resized
1034 1034 impl->moveSelectionZoneOnTop(zone, plot());
1035 1035 }
1036 1036 }
1037 1037 }
1038 1038 }
1039 1039 else {
1040 1040 if (!isMultiSelectionClick) {
1041 1041 parentVisualizationWidget()->selectionZoneManager().select(
1042 1042 {selectionZoneItemUnderCursor});
1043 1043 impl->moveSelectionZoneOnTop(selectionZoneItemUnderCursor, plot());
1044 1044 }
1045 1045 else {
1046 1046 parentVisualizationWidget()->selectionZoneManager().setSelected(
1047 1047 selectionZoneItemUnderCursor, !selectionZoneItemUnderCursor->selected()
1048 1048 || event->button() == Qt::RightButton);
1049 1049 }
1050 1050 }
1051 1051 }
1052 1052 else {
1053 1053 // No selection change
1054 1054 }
1055 1055 }
1056 1056 }
1057 1057
1058 1058 void VisualizationGraphWidget::onDataCacheVariableUpdated()
1059 1059 {
1060 1060 auto graphRange = ui->widget->xAxis->range();
1061 1061 auto dateTime = SqpRange{graphRange.lower, graphRange.upper};
1062 1062
1063 1063 for (auto &variableEntry : impl->m_VariableToPlotMultiMap) {
1064 1064 auto variable = variableEntry.first;
1065 1065 qCDebug(LOG_VisualizationGraphWidget())
1066 1066 << "TORM: VisualizationGraphWidget::onDataCacheVariableUpdated S" << variable->range();
1067 1067 qCDebug(LOG_VisualizationGraphWidget())
1068 1068 << "TORM: VisualizationGraphWidget::onDataCacheVariableUpdated E" << dateTime;
1069 1069 if (dateTime.contains(variable->range()) || dateTime.intersect(variable->range())) {
1070 1070 impl->updateData(variableEntry.second, variable, variable->range());
1071 1071 }
1072 1072 }
1073 1073 }
1074 1074
1075 1075 void VisualizationGraphWidget::onUpdateVarDisplaying(std::shared_ptr<Variable> variable,
1076 1076 const SqpRange &range)
1077 1077 {
1078 1078 auto it = impl->m_VariableToPlotMultiMap.find(variable);
1079 1079 if (it != impl->m_VariableToPlotMultiMap.end()) {
1080 1080 impl->updateData(it->second, variable, range);
1081 1081 }
1082 1082 }
@@ -1,274 +1,275
1 1 #include "AmdaProvider.h"
2 2 #include "AmdaDefs.h"
3 3 #include "AmdaResultParser.h"
4 4 #include "AmdaServer.h"
5 5
6 6 #include <Common/DateUtils.h>
7 7 #include <Data/DataProviderParameters.h>
8 8 #include <Network/NetworkController.h>
9 9 #include <SqpApplication.h>
10 10 #include <Variable/Variable.h>
11 11
12 12 #include <QNetworkAccessManager>
13 13 #include <QNetworkReply>
14 14 #include <QTemporaryFile>
15 15 #include <QThread>
16 16
17 17 Q_LOGGING_CATEGORY(LOG_AmdaProvider, "AmdaProvider")
18 18
19 19 namespace {
20 20
21 21 /// URL format for a request on AMDA server. The parameters are as follows:
22 22 /// - %1: server URL
23 23 /// - %2: start date
24 24 /// - %3: end date
25 25 /// - %4: parameter id
26 26 /// AMDA V2: http://amdatest.irap.omp.eu/php/rest/
27 27 const auto AMDA_URL_FORMAT = QStringLiteral(
28 28 "http://%1/php/rest/"
29 29 "getParameter.php?startTime=%2&stopTime=%3&parameterID=%4&outputFormat=ASCII&"
30 30 "timeFormat=ISO8601&gzip=0");
31 31
32 32 /// Dates format passed in the URL (e.g 2013-09-23T09:00)
33 33 const auto AMDA_TIME_FORMAT = QStringLiteral("yyyy-MM-ddThh:mm:ss");
34 34
35 35 /// Formats a time to a date that can be passed in URL
36 36 QString dateFormat(double sqpRange) noexcept
37 37 {
38 38 auto dateTime = DateUtils::dateTime(sqpRange);
39 39 return dateTime.toString(AMDA_TIME_FORMAT);
40 40 }
41 41
42 42
43 43 } // namespace
44 44
45 45 AmdaProvider::AmdaProvider()
46 46 {
47 47 qCDebug(LOG_AmdaProvider()) << tr("AmdaProvider::AmdaProvider") << QThread::currentThread();
48 48 if (auto app = sqpApp) {
49 49 auto &networkController = app->networkController();
50 50 connect(this, SIGNAL(requestConstructed(std::shared_ptr<QNetworkRequest>, QUuid,
51 51 std::function<void(QNetworkReply *, QUuid)>)),
52 52 &networkController,
53 53 SLOT(onProcessRequested(std::shared_ptr<QNetworkRequest>, QUuid,
54 54 std::function<void(QNetworkReply *, QUuid)>)));
55 55
56 56
57 57 connect(&sqpApp->networkController(),
58 58 SIGNAL(replyDownloadProgress(QUuid, std::shared_ptr<QNetworkRequest>, double)),
59 59 this,
60 60 SLOT(onReplyDownloadProgress(QUuid, std::shared_ptr<QNetworkRequest>, double)));
61 61 }
62 62 }
63 63
64 64 std::shared_ptr<IDataProvider> AmdaProvider::clone() const
65 65 {
66 66 // No copy is made in the clone
67 67 return std::make_shared<AmdaProvider>();
68 68 }
69 69
70 70 void AmdaProvider::requestDataLoading(QUuid acqIdentifier, const DataProviderParameters &parameters)
71 71 {
72 72 // NOTE: Try to use multithread if possible
73 73 const auto times = parameters.m_Times;
74 74 const auto data = parameters.m_Data;
75 75 for (const auto &dateTime : qAsConst(times)) {
76 76 qCDebug(LOG_AmdaProvider()) << tr("TORM AmdaProvider::requestDataLoading ") << acqIdentifier
77 77 << dateTime;
78 78 this->retrieveData(acqIdentifier, dateTime, data);
79 79
80 80
81 81 // TORM when AMDA will support quick asynchrone request
82 82 QThread::msleep(1000);
83 83 }
84 84 }
85 85
86 86 void AmdaProvider::requestDataAborting(QUuid acqIdentifier)
87 87 {
88 88 if (auto app = sqpApp) {
89 89 auto &networkController = app->networkController();
90 90 networkController.onReplyCanceled(acqIdentifier);
91 91 }
92 92 }
93 93
94 94 void AmdaProvider::onReplyDownloadProgress(QUuid acqIdentifier,
95 95 std::shared_ptr<QNetworkRequest> networkRequest,
96 96 double progress)
97 97 {
98 98 qCDebug(LOG_AmdaProvider()) << tr("onReplyDownloadProgress") << acqIdentifier
99 99 << networkRequest.get() << progress;
100 100 auto acqIdToRequestProgressMapIt = m_AcqIdToRequestProgressMap.find(acqIdentifier);
101 101 if (acqIdToRequestProgressMapIt != m_AcqIdToRequestProgressMap.end()) {
102 102
103 103 // Update the progression for the current request
104 104 auto requestPtr = networkRequest;
105 105 auto findRequest = [requestPtr](const auto &entry) { return requestPtr == entry.first; };
106 106
107 107 auto &requestProgressMap = acqIdToRequestProgressMapIt->second;
108 108 auto requestProgressMapEnd = requestProgressMap.end();
109 109 auto requestProgressMapIt
110 110 = std::find_if(requestProgressMap.begin(), requestProgressMapEnd, findRequest);
111 111
112 112 if (requestProgressMapIt != requestProgressMapEnd) {
113 113 requestProgressMapIt->second = progress;
114 114 }
115 115 else {
116 116 // This case can happened when a progression is send after the request has been
117 117 // finished.
118 118 // Generaly the case when aborting a request
119 119 qCDebug(LOG_AmdaProvider()) << tr("Can't retrieve Request in progress") << acqIdentifier
120 120 << networkRequest.get() << progress;
121 121 }
122 122
123 123 // Compute the current final progress and notify it
124 124 double finalProgress = 0.0;
125 125
126 126 auto fraq = requestProgressMap.size();
127 127
128 128 for (auto requestProgress : requestProgressMap) {
129 129 finalProgress += requestProgress.second;
130 130 qCDebug(LOG_AmdaProvider()) << tr("Current final progress without fraq:")
131 131 << finalProgress << requestProgress.second;
132 132 }
133 133
134 134 if (fraq > 0) {
135 135 finalProgress = finalProgress / fraq;
136 136 }
137 137
138 qCDebug(LOG_AmdaProvider()) << tr("Current final progress: ") << fraq << finalProgress;
138 qCDebug(LOG_AmdaProvider()) << tr("final progress: ")
139 << QThread::currentThread()->objectName() << acqIdentifier
140 << fraq << finalProgress;
139 141 emit dataProvidedProgress(acqIdentifier, finalProgress);
140 142 }
141 143 else {
142 144 // This case can happened when a progression is send after the request has been finished.
143 145 // Generaly the case when aborting a request
146 qCDebug(LOG_AmdaProvider()) << tr("Acquisition request not found: final progress: 100 : ")
147 << QThread::currentThread()->objectName() << acqIdentifier;
144 148 emit dataProvidedProgress(acqIdentifier, 100.0);
145 149 }
146 150 }
147 151
148 152 void AmdaProvider::retrieveData(QUuid token, const SqpRange &dateTime, const QVariantHash &data)
149 153 {
150 154 // Retrieves product ID from data: if the value is invalid, no request is made
151 155 auto productId = data.value(AMDA_XML_ID_KEY).toString();
152 156 if (productId.isNull()) {
153 157 qCCritical(LOG_AmdaProvider()) << tr("Can't retrieve data: unknown product id");
154 158 return;
155 159 }
156 160
157 161 // Retrieves the data type that determines whether the expected format for the result file is
158 162 // scalar, vector...
159 163 auto productValueType
160 164 = DataSeriesTypeUtils::fromString(data.value(AMDA_DATA_TYPE_KEY).toString());
161 165
162 166 // /////////// //
163 167 // Creates URL //
164 168 // /////////// //
165 169
166 170 auto startDate = dateFormat(dateTime.m_TStart);
167 171 auto endDate = dateFormat(dateTime.m_TEnd);
168 172
169 173 QVariantHash urlProperties{{AMDA_SERVER_KEY, data.value(AMDA_SERVER_KEY)}};
170 174 auto url = QUrl{QString{AMDA_URL_FORMAT}.arg(AmdaServer::instance().url(urlProperties),
171 175 startDate, endDate, productId)};
172 qCInfo(LOG_AmdaProvider()) << tr("TORM AmdaProvider::retrieveData url:") << url;
173 176 auto tempFile = std::make_shared<QTemporaryFile>();
174 177
175 178 // LAMBDA
176 179 auto httpDownloadFinished = [this, dateTime, tempFile,
177 180 productValueType](QNetworkReply *reply, QUuid dataId) noexcept {
178 181
179 182 // Don't do anything if the reply was abort
180 183 if (reply->error() == QNetworkReply::NoError) {
181 184
182 185 if (tempFile) {
183 186 auto replyReadAll = reply->readAll();
184 187 if (!replyReadAll.isEmpty()) {
185 188 tempFile->write(replyReadAll);
186 189 }
187 190 tempFile->close();
188 191
189 192 // Parse results file
190 193 if (auto dataSeries
191 194 = AmdaResultParser::readTxt(tempFile->fileName(), productValueType)) {
192 195 emit dataProvided(dataId, dataSeries, dateTime);
193 196 }
194 197 else {
195 198 /// @todo ALX : debug
196 199 emit dataProvidedFailed(dataId);
197 200 }
198 201 }
199 202 m_AcqIdToRequestProgressMap.erase(dataId);
200 203 }
201 204 else {
202 205 qCCritical(LOG_AmdaProvider()) << tr("httpDownloadFinished ERROR");
203 206 emit dataProvidedFailed(dataId);
204 207 }
205 208
206 209 };
207 210 auto httpFinishedLambda
208 211 = [this, httpDownloadFinished, tempFile](QNetworkReply *reply, QUuid dataId) noexcept {
209 212
210 213 // Don't do anything if the reply was abort
211 214 if (reply->error() == QNetworkReply::NoError) {
212 215 auto downloadFileUrl = QUrl{QString{reply->readAll()}.trimmed()};
213 216
214 qCInfo(LOG_AmdaProvider())
215 << tr("TORM AmdaProvider::retrieveData downloadFileUrl:") << downloadFileUrl;
217 qCDebug(LOG_AmdaProvider()) << tr("AmdaProvider::retrieveData downloadFileUrl:")
218 << downloadFileUrl;
216 219 // Executes request for downloading file //
217 220
218 221 // Creates destination file
219 222 if (tempFile->open()) {
220 223 // Executes request and store the request for progression
221 224 auto request = std::make_shared<QNetworkRequest>(downloadFileUrl);
222 225 updateRequestProgress(dataId, request, 0.0);
223 226 emit requestConstructed(request, dataId, httpDownloadFinished);
224 227 }
225 228 else {
226 229 emit dataProvidedFailed(dataId);
227 230 }
228 231 }
229 232 else {
230 233 qCCritical(LOG_AmdaProvider()) << tr("httpFinishedLambda ERROR");
231 234 m_AcqIdToRequestProgressMap.erase(dataId);
232 235 emit dataProvidedFailed(dataId);
233 236 }
234 237 };
235 238
236 239 // //////////////// //
237 240 // Executes request //
238 241 // //////////////// //
239 242
240 243 auto request = std::make_shared<QNetworkRequest>(url);
241 qCDebug(LOG_AmdaProvider()) << tr("First Request creation") << request.get();
242 244 updateRequestProgress(token, request, 0.0);
243 245
244 246 emit requestConstructed(request, token, httpFinishedLambda);
245 247 }
246 248
247 249 void AmdaProvider::updateRequestProgress(QUuid acqIdentifier,
248 250 std::shared_ptr<QNetworkRequest> request, double progress)
249 251 {
250 qCDebug(LOG_AmdaProvider()) << tr("updateRequestProgress request") << request.get();
251 252 auto acqIdToRequestProgressMapIt = m_AcqIdToRequestProgressMap.find(acqIdentifier);
252 253 if (acqIdToRequestProgressMapIt != m_AcqIdToRequestProgressMap.end()) {
253 254 auto &requestProgressMap = acqIdToRequestProgressMapIt->second;
254 255 auto requestProgressMapIt = requestProgressMap.find(request);
255 256 if (requestProgressMapIt != requestProgressMap.end()) {
256 257 requestProgressMapIt->second = progress;
257 258 qCDebug(LOG_AmdaProvider()) << tr("updateRequestProgress new progress for request")
258 259 << acqIdentifier << request.get() << progress;
259 260 }
260 261 else {
261 262 qCDebug(LOG_AmdaProvider()) << tr("updateRequestProgress new request") << acqIdentifier
262 263 << request.get() << progress;
263 264 acqIdToRequestProgressMapIt->second.insert(std::make_pair(request, progress));
264 265 }
265 266 }
266 267 else {
267 268 qCDebug(LOG_AmdaProvider()) << tr("updateRequestProgress new acqIdentifier")
268 269 << acqIdentifier << request.get() << progress;
269 270 auto requestProgressMap = std::map<std::shared_ptr<QNetworkRequest>, double>{};
270 271 requestProgressMap.insert(std::make_pair(request, progress));
271 272 m_AcqIdToRequestProgressMap.insert(
272 273 std::make_pair(acqIdentifier, std::move(requestProgressMap)));
273 274 }
274 275 }
@@ -1,118 +1,119
1 1 #include "FuzzingDefs.h"
2 2
3 3 const QString ACQUISITION_TIMEOUT_PROPERTY = QStringLiteral("acquisitionTimeout");
4 4 const QString NB_MAX_OPERATIONS_PROPERTY = QStringLiteral("component");
5 5 const QString NB_MAX_SYNC_GROUPS_PROPERTY = QStringLiteral("nbSyncGroups");
6 6 const QString NB_MAX_VARIABLES_PROPERTY = QStringLiteral("nbMaxVariables");
7 7 const QString AVAILABLE_OPERATIONS_PROPERTY = QStringLiteral("availableOperations");
8 8 const QString CACHE_TOLERANCE_PROPERTY = QStringLiteral("cacheTolerance");
9 9 const QString INITIAL_RANGE_PROPERTY = QStringLiteral("initialRange");
10 10 const QString MAX_RANGE_PROPERTY = QStringLiteral("maxRange");
11 11 const QString METADATA_POOL_PROPERTY = QStringLiteral("metadataPool");
12 12 const QString PROVIDER_PROPERTY = QStringLiteral("provider");
13 13 const QString OPERATION_DELAY_BOUNDS_PROPERTY = QStringLiteral("operationDelays");
14 14 const QString VALIDATORS_PROPERTY = QStringLiteral("validators");
15 15 const QString VALIDATION_FREQUENCY_BOUNDS_PROPERTY = QStringLiteral("validationFrequencyBounds");
16 const QString CUSTOM_OPERATIONS_PROPERTY = QStringLiteral("customOperations");
16 17
17 18 // //////////// //
18 19 // FuzzingState //
19 20 // //////////// //
20 21
21 22 const SyncGroup &FuzzingState::syncGroup(SyncGroupId id) const
22 23 {
23 24 return m_SyncGroupsPool.at(id);
24 25 }
25 26
26 27 SyncGroup &FuzzingState::syncGroup(SyncGroupId id)
27 28 {
28 29 return m_SyncGroupsPool.at(id);
29 30 }
30 31
31 32 const VariableState &FuzzingState::variableState(VariableId id) const
32 33 {
33 34 return m_VariablesPool.at(id);
34 35 }
35 36
36 37 VariableState &FuzzingState::variableState(VariableId id)
37 38 {
38 39 return m_VariablesPool.at(id);
39 40 }
40 41
41 42 SyncGroupId FuzzingState::syncGroupId(VariableId variableId) const
42 43 {
43 44 auto end = m_SyncGroupsPool.cend();
44 45 auto it
45 46 = std::find_if(m_SyncGroupsPool.cbegin(), end, [&variableId](const auto &syncGroupEntry) {
46 47 const auto &syncGroup = syncGroupEntry.second;
47 48 return syncGroup.m_Variables.find(variableId) != syncGroup.m_Variables.end();
48 49 });
49 50
50 51 return it != end ? it->first : SyncGroupId{};
51 52 }
52 53
53 54 std::vector<SyncGroupId> FuzzingState::syncGroupsIds() const
54 55 {
55 56 std::vector<SyncGroupId> result{};
56 57
57 58 for (const auto &entry : m_SyncGroupsPool) {
58 59 result.push_back(entry.first);
59 60 }
60 61
61 62 return result;
62 63 }
63 64
64 65 void FuzzingState::synchronizeVariable(VariableId variableId, SyncGroupId syncGroupId)
65 66 {
66 67 if (syncGroupId.isNull()) {
67 68 return;
68 69 }
69 70
70 71 // Registers variable into sync group
71 72 auto &syncGroup = m_SyncGroupsPool.at(syncGroupId);
72 73 auto &variableState = m_VariablesPool.at(variableId);
73 74 syncGroup.m_Variables.insert(variableId);
74 75 if (syncGroup.m_Variables.size() == 1) {
75 76 // If it's the first variable, sets the variable range as the sync group range
76 77 syncGroup.m_Range = variableState.m_Range;
77 78 }
78 79 else {
79 80 // If a variable is added to an existing group, sets its range to the group's range
80 81 variableState.m_Range = syncGroup.m_Range;
81 82 }
82 83 }
83 84
84 85 void FuzzingState::desynchronizeVariable(VariableId variableId, SyncGroupId syncGroupId)
85 86 {
86 87 if (syncGroupId.isNull()) {
87 88 return;
88 89 }
89 90
90 91 // Unregisters variable from sync group: if there is no more variable in the group, resets the
91 92 // range
92 93 auto &syncGroup = m_SyncGroupsPool.at(syncGroupId);
93 94 syncGroup.m_Variables.erase(variableId);
94 95 if (syncGroup.m_Variables.empty()) {
95 96 syncGroup.m_Range = INVALID_RANGE;
96 97 }
97 98 }
98 99
99 100 void FuzzingState::updateRanges(VariableId variableId, const SqpRange &newRange)
100 101 {
101 102 auto syncGroupId = this->syncGroupId(variableId);
102 103
103 104 // Retrieves the variables to update:
104 105 // - if the variable is synchronized to others, updates the range of the group and of all
105 106 // synchronized variables
106 107 // - otherwise, updates only the variable
107 108 if (syncGroupId.isNull()) {
108 109 m_VariablesPool.at(variableId).m_Range = newRange;
109 110 }
110 111 else {
111 112 auto &syncGroup = m_SyncGroupsPool.at(syncGroupId);
112 113 syncGroup.m_Range = newRange;
113 114
114 115 for (const auto &variableId : syncGroup.m_Variables) {
115 116 m_VariablesPool.at(variableId).m_Range = newRange;
116 117 }
117 118 }
118 119 }
@@ -1,128 +1,131
1 1 #ifndef SCIQLOP_FUZZINGDEFS_H
2 2 #define SCIQLOP_FUZZINGDEFS_H
3 3
4 4 #include <Data/SqpRange.h>
5 5
6 6 #include <QString>
7 7 #include <QUuid>
8 8 #include <QVariantHash>
9 9
10 10 #include <memory>
11 11 #include <set>
12 12
13 13 // /////// //
14 14 // Aliases //
15 15 // /////// //
16 16
17 17 using MetadataPool = std::vector<QVariantHash>;
18 18 Q_DECLARE_METATYPE(MetadataPool)
19 19
20 20 using Properties = QVariantHash;
21 21
22 22 // ///////// //
23 23 // Constants //
24 24 // ///////// //
25 25
26 26 /// Timeout set for data acquisition for an operation (in ms)
27 27 extern const QString ACQUISITION_TIMEOUT_PROPERTY;
28 28
29 29 /// Max number of operations to generate
30 30 extern const QString NB_MAX_OPERATIONS_PROPERTY;
31 31
32 32 /// Max number of sync groups to create through operations
33 33 extern const QString NB_MAX_SYNC_GROUPS_PROPERTY;
34 34
35 35 /// Max number of variables to manipulate through operations
36 36 extern const QString NB_MAX_VARIABLES_PROPERTY;
37 37
38 38 /// Set of operations available for the test
39 39 extern const QString AVAILABLE_OPERATIONS_PROPERTY;
40 40
41 41 /// Tolerance used for variable's cache (in ratio)
42 42 extern const QString CACHE_TOLERANCE_PROPERTY;
43 43
44 44 /// Range with which the timecontroller is initialized
45 45 extern const QString INITIAL_RANGE_PROPERTY;
46 46
47 47 /// Max range that an operation can reach
48 48 extern const QString MAX_RANGE_PROPERTY;
49 49
50 50 /// Set of metadata that can be associated to a variable
51 51 extern const QString METADATA_POOL_PROPERTY;
52 52
53 53 /// Provider used to retrieve data
54 54 extern const QString PROVIDER_PROPERTY;
55 55
56 56 /// Min/max times left for an operation to execute
57 57 extern const QString OPERATION_DELAY_BOUNDS_PROPERTY;
58 58
59 59 /// Validators used to validate an operation
60 60 extern const QString VALIDATORS_PROPERTY;
61 61
62 62 /// Min/max number of operations to execute before calling validation of the current test's state
63 63 extern const QString VALIDATION_FREQUENCY_BOUNDS_PROPERTY;
64 64
65 /// Custom operations to execute instead of random operations
66 extern const QString CUSTOM_OPERATIONS_PROPERTY;
67
65 68 // /////// //
66 69 // Structs //
67 70 // /////// //
68 71
69 72 class Variable;
70 73 struct VariableState {
71 74 std::shared_ptr<Variable> m_Variable{nullptr};
72 75 SqpRange m_Range{INVALID_RANGE};
73 76 };
74 77
75 78 using VariableId = int;
76 79 using VariablesPool = std::map<VariableId, VariableState>;
77 80
78 81 /**
79 82 * Defines a synchronization group for a fuzzing state. A group reports the variables synchronized
80 83 * with each other, and the current range of the group (i.e. range of the last synchronized variable
81 84 * that has been moved)
82 85 */
83 86 struct SyncGroup {
84 87 std::set<VariableId> m_Variables{};
85 88 SqpRange m_Range{INVALID_RANGE};
86 89 };
87 90
88 91 using SyncGroupId = QUuid;
89 92 using SyncGroupsPool = std::map<SyncGroupId, SyncGroup>;
90 93
91 94 /**
92 95 * Defines a current state during a fuzzing state. It contains all the variables manipulated during
93 96 * the test, as well as the synchronization status of these variables.
94 97 */
95 98 struct FuzzingState {
96 99 const SyncGroup &syncGroup(SyncGroupId id) const;
97 100 SyncGroup &syncGroup(SyncGroupId id);
98 101
99 102 const VariableState &variableState(VariableId id) const;
100 103 VariableState &variableState(VariableId id);
101 104
102 105 /// @return the identifier of the synchronization group in which the variable passed in
103 106 /// parameter is located. If the variable is not in any group, returns an invalid identifier
104 107 SyncGroupId syncGroupId(VariableId variableId) const;
105 108
106 109 /// @return the set of synchronization group identifiers
107 110 std::vector<SyncGroupId> syncGroupsIds() const;
108 111
109 112 /// Updates fuzzing state according to a variable synchronization
110 113 /// @param variableId the variable that is synchronized
111 114 /// @param syncGroupId the synchronization group
112 115 void synchronizeVariable(VariableId variableId, SyncGroupId syncGroupId);
113 116
114 117 /// Updates fuzzing state according to a variable desynchronization
115 118 /// @param variableId the variable that is desynchronized
116 119 /// @param syncGroupId the synchronization group from which to remove the variable
117 120 void desynchronizeVariable(VariableId variableId, SyncGroupId syncGroupId);
118 121
119 122 /// Updates the range of a variable and all variables to which it is synchronized
120 123 /// @param the variable for which to affect the range
121 124 /// @param the range to affect
122 125 void updateRanges(VariableId variableId, const SqpRange &newRange);
123 126
124 127 VariablesPool m_VariablesPool;
125 128 SyncGroupsPool m_SyncGroupsPool;
126 129 };
127 130
128 131 #endif // SCIQLOP_FUZZINGDEFS_H
@@ -1,268 +1,268
1 1 #include "FuzzingOperations.h"
2 2 #include "FuzzingUtils.h"
3 3
4 4 #include <Data/IDataProvider.h>
5 5
6 6 #include <Variable/Variable.h>
7 7 #include <Variable/VariableController.h>
8 8
9 9 #include <QUuid>
10 10
11 11 #include <functional>
12 12
13 13 Q_LOGGING_CATEGORY(LOG_FuzzingOperations, "FuzzingOperations")
14 14
15 15 namespace {
16 16
17 17 struct CreateOperation : public IFuzzingOperation {
18 18 bool canExecute(VariableId variableId, const FuzzingState &fuzzingState) const override
19 19 {
20 20 // A variable can be created only if it doesn't exist yet
21 21 return fuzzingState.variableState(variableId).m_Variable == nullptr;
22 22 }
23 23
24 24 void execute(VariableId variableId, FuzzingState &fuzzingState,
25 25 VariableController &variableController,
26 26 const Properties &properties) const override
27 27 {
28 28 // Retrieves metadata pool from properties, and choose one of the metadata entries to
29 29 // associate it with the variable
30 30 auto metaDataPool = properties.value(METADATA_POOL_PROPERTY).value<MetadataPool>();
31 31 auto variableMetadata = RandomGenerator::instance().randomChoice(metaDataPool);
32 32
33 33 // Retrieves provider
34 34 auto variableProvider
35 35 = properties.value(PROVIDER_PROPERTY).value<std::shared_ptr<IDataProvider> >();
36 36
37 37 auto variableName = QString{"Var_%1"}.arg(QUuid::createUuid().toString());
38 38 qCInfo(LOG_FuzzingOperations()).noquote() << "Creating variable" << variableName
39 39 << "(metadata:" << variableMetadata << ")...";
40 40
41 41 auto newVariable
42 42 = variableController.createVariable(variableName, variableMetadata, variableProvider);
43 43
44 44 // Updates variable's state
45 45 auto &variableState = fuzzingState.variableState(variableId);
46 46 variableState.m_Range = properties.value(INITIAL_RANGE_PROPERTY).value<SqpRange>();
47 47 std::swap(variableState.m_Variable, newVariable);
48 48 }
49 49 };
50 50
51 51 struct DeleteOperation : public IFuzzingOperation {
52 52 bool canExecute(VariableId variableId, const FuzzingState &fuzzingState) const override
53 53 {
54 54 // A variable can be delete only if it exists
55 55 return fuzzingState.variableState(variableId).m_Variable != nullptr;
56 56 }
57 57
58 58 void execute(VariableId variableId, FuzzingState &fuzzingState,
59 59 VariableController &variableController, const Properties &) const override
60 60 {
61 61 auto &variableState = fuzzingState.variableState(variableId);
62 62
63 63 qCInfo(LOG_FuzzingOperations()).noquote() << "Deleting variable"
64 64 << variableState.m_Variable->name() << "...";
65 65 variableController.deleteVariable(variableState.m_Variable);
66 66
67 67 // Updates variable's state
68 68 variableState.m_Range = INVALID_RANGE;
69 69 variableState.m_Variable = nullptr;
70 70
71 71 // Desynchronizes the variable if it was in a sync group
72 72 auto syncGroupId = fuzzingState.syncGroupId(variableId);
73 73 fuzzingState.desynchronizeVariable(variableId, syncGroupId);
74 74 }
75 75 };
76 76
77 77 /**
78 78 * Defines a move operation through a range.
79 79 *
80 80 * A move operation is determined by three functions:
81 81 * - Two 'move' functions, used to indicate in which direction the beginning and the end of a range
82 82 * are going during the operation. These functions will be:
83 83 * -- {<- / <-} for pan left
84 84 * -- {-> / ->} for pan right
85 85 * -- {-> / <-} for zoom in
86 86 * -- {<- / ->} for zoom out
87 87 * - One 'max move' functions, used to compute the max delta at which the operation can move a
88 88 * range, according to a max range. For exemple, for a range of {1, 5} and a max range of {0, 10},
89 89 * max deltas will be:
90 90 * -- {0, 4} for pan left
91 91 * -- {6, 10} for pan right
92 92 * -- {3, 3} for zoom in
93 93 * -- {0, 6} for zoom out (same spacing left and right)
94 94 */
95 95 struct MoveOperation : public IFuzzingOperation {
96 96 using MoveFunction = std::function<double(double currentValue, double maxValue)>;
97 97 using MaxMoveFunction = std::function<double(const SqpRange &range, const SqpRange &maxRange)>;
98 98
99 99 explicit MoveOperation(MoveFunction rangeStartMoveFun, MoveFunction rangeEndMoveFun,
100 100 MaxMoveFunction maxMoveFun,
101 101 const QString &label = QStringLiteral("Move operation"))
102 102 : m_RangeStartMoveFun{std::move(rangeStartMoveFun)},
103 103 m_RangeEndMoveFun{std::move(rangeEndMoveFun)},
104 104 m_MaxMoveFun{std::move(maxMoveFun)},
105 105 m_Label{label}
106 106 {
107 107 }
108 108
109 109 bool canExecute(VariableId variableId, const FuzzingState &fuzzingState) const override
110 110 {
111 111 return fuzzingState.variableState(variableId).m_Variable != nullptr;
112 112 }
113 113
114 114 void execute(VariableId variableId, FuzzingState &fuzzingState,
115 115 VariableController &variableController,
116 116 const Properties &properties) const override
117 117 {
118 118 auto &variableState = fuzzingState.variableState(variableId);
119 119 auto variable = variableState.m_Variable;
120 120
121 121 // Gets the max range defined
122 122 auto maxRange = properties.value(MAX_RANGE_PROPERTY, QVariant::fromValue(INVALID_RANGE))
123 123 .value<SqpRange>();
124 124 auto variableRange = variableState.m_Range;
125 125
126 126 if (maxRange == INVALID_RANGE || variableRange.m_TStart < maxRange.m_TStart
127 127 || variableRange.m_TEnd > maxRange.m_TEnd) {
128 128 qCWarning(LOG_FuzzingOperations()) << "Can't execute operation: invalid max range";
129 129 return;
130 130 }
131 131
132 132 // Computes the max delta at which the variable can move, up to the limits of the max range
133 133 auto deltaMax = m_MaxMoveFun(variableRange, maxRange);
134 134
135 135 // Generates random delta that will be used to move variable
136 136 auto delta = RandomGenerator::instance().generateDouble(0, deltaMax);
137 137
138 138 // Moves variable to its new range
139 139 auto isSynchronized = !fuzzingState.syncGroupId(variableId).isNull();
140 140 auto newVariableRange = SqpRange{m_RangeStartMoveFun(variableRange.m_TStart, delta),
141 141 m_RangeEndMoveFun(variableRange.m_TEnd, delta)};
142 142 qCInfo(LOG_FuzzingOperations()).noquote() << "Performing" << m_Label << "on"
143 143 << variable->name() << "(from" << variableRange
144 144 << "to" << newVariableRange << ")...";
145 145 variableController.onRequestDataLoading({variable}, newVariableRange, isSynchronized);
146 146
147 147 // Updates state
148 148 fuzzingState.updateRanges(variableId, newVariableRange);
149 149 }
150 150
151 151 MoveFunction m_RangeStartMoveFun;
152 152 MoveFunction m_RangeEndMoveFun;
153 153 MaxMoveFunction m_MaxMoveFun;
154 154 QString m_Label;
155 155 };
156 156
157 157 struct SynchronizeOperation : public IFuzzingOperation {
158 158 bool canExecute(VariableId variableId, const FuzzingState &fuzzingState) const override
159 159 {
160 160 auto variable = fuzzingState.variableState(variableId).m_Variable;
161 161 return variable != nullptr && !fuzzingState.m_SyncGroupsPool.empty()
162 162 && fuzzingState.syncGroupId(variableId).isNull();
163 163 }
164 164
165 165 void execute(VariableId variableId, FuzzingState &fuzzingState,
166 166 VariableController &variableController, const Properties &) const override
167 167 {
168 168 auto &variableState = fuzzingState.variableState(variableId);
169 169
170 170 // Chooses a random synchronization group and adds the variable into sync group
171 171 auto syncGroupId = RandomGenerator::instance().randomChoice(fuzzingState.syncGroupsIds());
172 172 qCInfo(LOG_FuzzingOperations()).noquote() << "Adding" << variableState.m_Variable->name()
173 173 << "into synchronization group" << syncGroupId
174 174 << "...";
175 175 variableController.onAddSynchronized(variableState.m_Variable, syncGroupId);
176 176
177 177 // Updates state
178 178 fuzzingState.synchronizeVariable(variableId, syncGroupId);
179 179
180 180 variableController.onRequestDataLoading({variableState.m_Variable}, variableState.m_Range,
181 false);
181 true);
182 182 }
183 183 };
184 184
185 185 struct DesynchronizeOperation : public IFuzzingOperation {
186 186 bool canExecute(VariableId variableId, const FuzzingState &fuzzingState) const override
187 187 {
188 188 auto variable = fuzzingState.variableState(variableId).m_Variable;
189 189 return variable != nullptr && !fuzzingState.syncGroupId(variableId).isNull();
190 190 }
191 191
192 192 void execute(VariableId variableId, FuzzingState &fuzzingState,
193 193 VariableController &variableController, const Properties &) const override
194 194 {
195 195 auto &variableState = fuzzingState.variableState(variableId);
196 196
197 197 // Gets the sync group of the variable
198 198 auto syncGroupId = fuzzingState.syncGroupId(variableId);
199 199
200 200 qCInfo(LOG_FuzzingOperations()).noquote() << "Removing" << variableState.m_Variable->name()
201 201 << "from synchronization group" << syncGroupId
202 202 << "...";
203 203 variableController.desynchronize(variableState.m_Variable, syncGroupId);
204 204
205 205 // Updates state
206 206 fuzzingState.desynchronizeVariable(variableId, syncGroupId);
207 207 }
208 208 };
209 209
210 210 struct UnknownOperation : public IFuzzingOperation {
211 211 bool canExecute(VariableId, const FuzzingState &) const override { return false; }
212 212
213 213 void execute(VariableId, FuzzingState &, VariableController &,
214 214 const Properties &) const override
215 215 {
216 216 }
217 217 };
218 218
219 219 } // namespace
220 220
221 221 std::unique_ptr<IFuzzingOperation> FuzzingOperationFactory::create(FuzzingOperationType type)
222 222 {
223 223 switch (type) {
224 224 case FuzzingOperationType::CREATE:
225 225 return std::make_unique<CreateOperation>();
226 226 case FuzzingOperationType::DELETE:
227 227 return std::make_unique<DeleteOperation>();
228 228 case FuzzingOperationType::PAN_LEFT:
229 229 return std::make_unique<MoveOperation>(
230 230 std::minus<double>(), std::minus<double>(),
231 231 [](const SqpRange &range, const SqpRange &maxRange) {
232 232 return range.m_TStart - maxRange.m_TStart;
233 233 },
234 234 QStringLiteral("Pan left operation"));
235 235 case FuzzingOperationType::PAN_RIGHT:
236 236 return std::make_unique<MoveOperation>(
237 237 std::plus<double>(), std::plus<double>(),
238 238 [](const SqpRange &range, const SqpRange &maxRange) {
239 239 return maxRange.m_TEnd - range.m_TEnd;
240 240 },
241 241 QStringLiteral("Pan right operation"));
242 242 case FuzzingOperationType::ZOOM_IN:
243 243 return std::make_unique<MoveOperation>(
244 244 std::plus<double>(), std::minus<double>(),
245 245 [](const SqpRange &range, const SqpRange &maxRange) {
246 246 Q_UNUSED(maxRange)
247 247 return range.m_TEnd - (range.m_TStart + range.m_TEnd) / 2.;
248 248 },
249 249 QStringLiteral("Zoom in operation"));
250 250 case FuzzingOperationType::ZOOM_OUT:
251 251 return std::make_unique<MoveOperation>(
252 252 std::minus<double>(), std::plus<double>(),
253 253 [](const SqpRange &range, const SqpRange &maxRange) {
254 254 return std::min(range.m_TStart - maxRange.m_TStart,
255 255 maxRange.m_TEnd - range.m_TEnd);
256 256 },
257 257 QStringLiteral("Zoom out operation"));
258 258 case FuzzingOperationType::SYNCHRONIZE:
259 259 return std::make_unique<SynchronizeOperation>();
260 260 case FuzzingOperationType::DESYNCHRONIZE:
261 261 return std::make_unique<DesynchronizeOperation>();
262 262 default:
263 263 // Default case returns unknown operation
264 264 break;
265 265 }
266 266
267 267 return std::make_unique<UnknownOperation>();
268 268 }
@@ -1,395 +1,560
1 1 #include "FuzzingDefs.h"
2 2 #include "FuzzingOperations.h"
3 3 #include "FuzzingUtils.h"
4 4 #include "FuzzingValidators.h"
5 5
6 6 #include "AmdaProvider.h"
7 7
8 8 #include <Common/SignalWaiter.h>
9 9 #include <Network/NetworkController.h>
10 10 #include <Settings/SqpSettingsDefs.h>
11 11 #include <SqpApplication.h>
12 12 #include <Time/TimeController.h>
13 13 #include <Variable/Variable.h>
14 14 #include <Variable/VariableController.h>
15 15
16 16 #include <QLoggingCategory>
17 17 #include <QObject>
18 18 #include <QtTest>
19 19
20 20 #include <memory>
21 21
22 22 Q_LOGGING_CATEGORY(LOG_TestAmdaFuzzing, "TestAmdaFuzzing")
23 23
24 24 /**
25 25 * Macro used to generate a getter for a property in @sa FuzzingTest. The macro generates a static
26 26 * attribute that is initialized by searching in properties the property and use a default value if
27 27 * it's not present. Macro arguments are:
28 28 * - GETTER_NAME : name of the getter
29 29 * - PROPERTY_NAME: used to generate constants for property's name ({PROPERTY_NAME}_PROPERTY) and
30 30 * default value ({PROPERTY_NAME}_DEFAULT_VALUE)
31 31 * - TYPE : return type of the getter
32 32 */
33 33 // clang-format off
34 34 #define DECLARE_PROPERTY_GETTER(GETTER_NAME, PROPERTY_NAME, TYPE) \
35 35 TYPE GETTER_NAME() const \
36 36 { \
37 37 static auto result = m_Properties.value(PROPERTY_NAME##_PROPERTY, PROPERTY_NAME##_DEFAULT_VALUE).value<TYPE>(); \
38 38 return result; \
39 39 } \
40 40 // clang-format on
41 41
42 42 namespace {
43 43
44 44 // /////// //
45 45 // Aliases //
46 46 // /////// //
47 47
48 48 using IntPair = std::pair<int, int>;
49 49 using Weight = double;
50 50 using Weights = std::vector<Weight>;
51 51
52 52 struct OperationProperty {
53 53 Weight m_Weight{1.};
54 54 bool m_WaitAcquisition{false};
55 55 };
56 56
57 57 using VariableOperation = std::pair<VariableId, std::shared_ptr<IFuzzingOperation> >;
58 58 using VariablesOperations = std::vector<VariableOperation>;
59 59
60 struct CustomVariableOperation {
61 VariableOperation m_VariableOperation{};
62 bool m_WaitAcquisition{false};
63 };
64 using CustomVariablesOperations = std::vector<CustomVariableOperation>;
65
60 66 using OperationsTypes = std::map<FuzzingOperationType, OperationProperty>;
61 67 using OperationsPool = std::map<std::shared_ptr<IFuzzingOperation>, OperationProperty>;
62 68
63 69 using Validators = std::vector<std::shared_ptr<IFuzzingValidator> >;
64 70
65 71 // ///////// //
66 72 // Constants //
67 73 // ///////// //
68 74
69 75 // Defaults values used when the associated properties have not been set for the test
70 const auto ACQUISITION_TIMEOUT_DEFAULT_VALUE = 30000;
71 const auto NB_MAX_OPERATIONS_DEFAULT_VALUE = 100;
72 const auto NB_MAX_SYNC_GROUPS_DEFAULT_VALUE = 1;
73 const auto NB_MAX_VARIABLES_DEFAULT_VALUE = 1;
76 const auto ACQUISITION_TIMEOUT_DEFAULT_VALUE = 100000;
77 const auto NB_MAX_OPERATIONS_DEFAULT_VALUE = 160;
78 const auto NB_MAX_SYNC_GROUPS_DEFAULT_VALUE = 8;
79 const auto NB_MAX_VARIABLES_DEFAULT_VALUE = 30;
74 80 const auto AVAILABLE_OPERATIONS_DEFAULT_VALUE = QVariant::fromValue(
75 OperationsTypes{{FuzzingOperationType::CREATE, {1., true}},
76 {FuzzingOperationType::DELETE, {0.1}}, // Delete operation is less frequent
81 OperationsTypes{{FuzzingOperationType::CREATE, {40000., true}},
82 {FuzzingOperationType::DELETE, {0.0}}, // Delete operation is less frequent
77 83 {FuzzingOperationType::PAN_LEFT, {1.}},
78 84 {FuzzingOperationType::PAN_RIGHT, {1.}},
79 85 {FuzzingOperationType::ZOOM_IN, {1.}},
80 86 {FuzzingOperationType::ZOOM_OUT, {1.}},
81 {FuzzingOperationType::SYNCHRONIZE, {0.8}},
82 {FuzzingOperationType::DESYNCHRONIZE, {0.4}}});
87 {FuzzingOperationType::SYNCHRONIZE, {500.0}},
88 {FuzzingOperationType::DESYNCHRONIZE, {0.0}}});
83 89 const auto CACHE_TOLERANCE_DEFAULT_VALUE = 0.2;
84 90
85 91 /// Min/max delays between each operation (in ms)
86 const auto OPERATION_DELAY_BOUNDS_DEFAULT_VALUE = QVariant::fromValue(std::make_pair(100, 3000));
92 const auto OPERATION_DELAY_BOUNDS_DEFAULT_VALUE = QVariant::fromValue(std::make_pair(20, 3000));
87 93
88 94 /// Validators for the tests (executed in the order in which they're defined)
89 95 const auto VALIDATORS_DEFAULT_VALUE = QVariant::fromValue(
90 96 ValidatorsTypes{{FuzzingValidatorType::RANGE, FuzzingValidatorType::DATA}});
91 97
92 98 /// Min/max number of operations to execute before calling validation
93 const auto VALIDATION_FREQUENCY_BOUNDS_DEFAULT_VALUE = QVariant::fromValue(std::make_pair(1, 10));
99 const auto VALIDATION_FREQUENCY_BOUNDS_DEFAULT_VALUE = QVariant::fromValue(std::make_pair(2, 10));
100
101
102 // /////// //////
103 // CUSTOM CASE //
104 // /////// //////
105
106 auto op = [](auto type){
107 return FuzzingOperationFactory::create(type);
108 };
109
110
111 const QVariant CUSTOM_CASE_ONE =QVariant::fromValue(CustomVariablesOperations{{{0, op(FuzzingOperationType::CREATE)}, true},
112 {{0, op(FuzzingOperationType::SYNCHRONIZE)}},
113 {{1, op(FuzzingOperationType::CREATE)}, true},
114 {{1, op(FuzzingOperationType::SYNCHRONIZE)}},
115 {{0, op(FuzzingOperationType::PAN_LEFT)}},
116 {{1, op(FuzzingOperationType::PAN_RIGHT)}},
117 {{0, op(FuzzingOperationType::PAN_LEFT)}},
118 {{1, op(FuzzingOperationType::PAN_RIGHT)}},
119 {{0, op(FuzzingOperationType::PAN_LEFT)}},
120 {{1, op(FuzzingOperationType::PAN_RIGHT)}},
121 {{0, op(FuzzingOperationType::PAN_LEFT)}},
122 {{1, op(FuzzingOperationType::PAN_RIGHT)}},
123 {{0, op(FuzzingOperationType::PAN_LEFT)}},
124 {{1, op(FuzzingOperationType::PAN_RIGHT)}},
125 {{0, op(FuzzingOperationType::PAN_RIGHT)}},
126 {{1, op(FuzzingOperationType::PAN_LEFT)}},
127 {{0, op(FuzzingOperationType::PAN_RIGHT)}},
128 {{1, op(FuzzingOperationType::PAN_RIGHT)}},
129 {{0, op(FuzzingOperationType::PAN_LEFT)}},
130 {{1, op(FuzzingOperationType::PAN_RIGHT)}},
131 {{0, op(FuzzingOperationType::PAN_LEFT)}},
132 {{1, op(FuzzingOperationType::PAN_RIGHT)}},
133 {{0, op(FuzzingOperationType::PAN_LEFT)}},
134 {{1, op(FuzzingOperationType::PAN_RIGHT)}},
135 {{0, op(FuzzingOperationType::PAN_LEFT)}},
136 {{1, op(FuzzingOperationType::PAN_RIGHT)}},
137 {{0, op(FuzzingOperationType::PAN_LEFT)}},
138 {{1, op(FuzzingOperationType::PAN_LEFT)}},
139 {{0, op(FuzzingOperationType::PAN_LEFT)}},
140 {{1, op(FuzzingOperationType::PAN_LEFT)}},
141 {{0, op(FuzzingOperationType::PAN_RIGHT)}},
142 {{1, op(FuzzingOperationType::PAN_LEFT)}},
143 {{0, op(FuzzingOperationType::PAN_RIGHT)}},
144 {{1, op(FuzzingOperationType::PAN_RIGHT)}},
145 {{0, op(FuzzingOperationType::PAN_LEFT)}},
146 {{1, op(FuzzingOperationType::PAN_RIGHT)}},
147 {{0, op(FuzzingOperationType::PAN_LEFT)}},
148 {{1, op(FuzzingOperationType::PAN_RIGHT)}},
149 {{0, op(FuzzingOperationType::PAN_LEFT)}},
150 {{1, op(FuzzingOperationType::PAN_RIGHT)}},
151 {{0, op(FuzzingOperationType::PAN_LEFT)}},
152 {{1, op(FuzzingOperationType::PAN_RIGHT)}},
153 {{0, op(FuzzingOperationType::PAN_LEFT)}},
154 {{1, op(FuzzingOperationType::PAN_RIGHT)}},
155 {{0, op(FuzzingOperationType::PAN_LEFT)}},
156 {{1, op(FuzzingOperationType::PAN_RIGHT)}},
157 {{0, op(FuzzingOperationType::PAN_RIGHT)}},
158 {{1, op(FuzzingOperationType::PAN_LEFT)}},
159 {{0, op(FuzzingOperationType::PAN_RIGHT)}},
160 {{1, op(FuzzingOperationType::PAN_RIGHT)}},
161 {{0, op(FuzzingOperationType::PAN_LEFT)}},
162 {{1, op(FuzzingOperationType::PAN_RIGHT)}},
163 {{0, op(FuzzingOperationType::PAN_LEFT)}},
164 {{1, op(FuzzingOperationType::PAN_RIGHT)}},
165 {{0, op(FuzzingOperationType::PAN_RIGHT)}},
166 {{1, op(FuzzingOperationType::PAN_RIGHT)}},
167 {{0, op(FuzzingOperationType::PAN_LEFT)}},
168 {{1, op(FuzzingOperationType::PAN_RIGHT)}},
169 {{0, op(FuzzingOperationType::PAN_LEFT)}},
170 {{1, op(FuzzingOperationType::PAN_RIGHT)}},
171 {{0, op(FuzzingOperationType::PAN_LEFT)}},
172 {{1, op(FuzzingOperationType::PAN_RIGHT)}},
173 {{0, op(FuzzingOperationType::PAN_RIGHT)}},
174 {{1, op(FuzzingOperationType::PAN_LEFT)}},
175 {{0, op(FuzzingOperationType::PAN_RIGHT)}},
176 {{1, op(FuzzingOperationType::PAN_RIGHT)}},
177 {{0, op(FuzzingOperationType::PAN_LEFT)}},
178 {{1, op(FuzzingOperationType::PAN_RIGHT)}},
179 {{0, op(FuzzingOperationType::PAN_LEFT)}},
180 {{1, op(FuzzingOperationType::PAN_RIGHT)}},
181 {{0, op(FuzzingOperationType::PAN_LEFT)}},
182 {{1, op(FuzzingOperationType::PAN_RIGHT)}},
183 {{0, op(FuzzingOperationType::PAN_LEFT)}},
184 {{1, op(FuzzingOperationType::PAN_RIGHT)}},
185 {{0, op(FuzzingOperationType::PAN_LEFT)}},
186 {{1, op(FuzzingOperationType::PAN_RIGHT)}},
187 {{0, op(FuzzingOperationType::PAN_LEFT)}},
188 {{1, op(FuzzingOperationType::PAN_RIGHT)}},
189 {{0, op(FuzzingOperationType::PAN_RIGHT)}},
190 {{1, op(FuzzingOperationType::PAN_LEFT)}},
191 {{0, op(FuzzingOperationType::PAN_RIGHT)}},
192 {{1, op(FuzzingOperationType::PAN_RIGHT)}},
193 {{0, op(FuzzingOperationType::PAN_RIGHT)}},
194 {{1, op(FuzzingOperationType::PAN_RIGHT)}},
195 {{0, op(FuzzingOperationType::PAN_LEFT)}},
196 {{1, op(FuzzingOperationType::PAN_RIGHT)}},
197 {{0, op(FuzzingOperationType::PAN_LEFT)}},
198 {{1, op(FuzzingOperationType::PAN_RIGHT)}},
199 {{0, op(FuzzingOperationType::PAN_LEFT)}},
200 {{1, op(FuzzingOperationType::PAN_RIGHT)}},
201 {{0, op(FuzzingOperationType::PAN_LEFT)}},
202 {{1, op(FuzzingOperationType::PAN_RIGHT)}},
203 {{0, op(FuzzingOperationType::PAN_LEFT)}},
204 {{1, op(FuzzingOperationType::PAN_RIGHT)}},
205 {{0, op(FuzzingOperationType::PAN_RIGHT)}},
206 {{1, op(FuzzingOperationType::PAN_LEFT)}},
207 {{0, op(FuzzingOperationType::PAN_RIGHT)}},
208 {{1, op(FuzzingOperationType::PAN_RIGHT)}},
209 {{0, op(FuzzingOperationType::PAN_LEFT)}},
210 {{1, op(FuzzingOperationType::PAN_RIGHT)}},
211 });
212
213 const QVariant CUSTOM_CASE_TWO =QVariant::fromValue(CustomVariablesOperations{{{0, op(FuzzingOperationType::CREATE)}, true},
214 {{0, op(FuzzingOperationType::SYNCHRONIZE)}},
215 {{1, op(FuzzingOperationType::CREATE)}, true},
216 {{1, op(FuzzingOperationType::SYNCHRONIZE)}},
217 {{1, op(FuzzingOperationType::ZOOM_OUT)}},
218 {{1, op(FuzzingOperationType::ZOOM_IN)}},
219 });
94 220
95 221 // /////// //
96 222 // Methods //
97 223 // /////// //
98 224
99 225 /// Goes through the variables pool and operations pool to determine the set of {variable/operation}
100 226 /// pairs that are valid (i.e. operation that can be executed on variable)
101 227 std::pair<VariablesOperations, Weights> availableOperations(const FuzzingState &fuzzingState,
102 228 const OperationsPool &operationsPool)
103 229 {
104 230 VariablesOperations result{};
105 231 Weights weights{};
106 232
107 233 for (const auto &variablesPoolEntry : fuzzingState.m_VariablesPool) {
108 234 auto variableId = variablesPoolEntry.first;
109 235
110 236 for (const auto &operationsPoolEntry : operationsPool) {
111 237 auto operation = operationsPoolEntry.first;
112 238 auto operationProperty = operationsPoolEntry.second;
113 239
114 240 // A pair is valid if the current operation can be executed on the current variable
115 241 if (operation->canExecute(variableId, fuzzingState)) {
116 242 result.push_back({variableId, operation});
117 243 weights.push_back(operationProperty.m_Weight);
118 244 }
119 245 }
120 246 }
121 247
122 248 return {result, weights};
123 249 }
124 250
125 251 OperationsPool createOperationsPool(const OperationsTypes &types)
126 252 {
127 253 OperationsPool result{};
128 254
129 255 std::transform(
130 256 types.cbegin(), types.cend(), std::inserter(result, result.end()), [](const auto &type) {
131 257 return std::make_pair(FuzzingOperationFactory::create(type.first), type.second);
132 258 });
133 259
134 260 return result;
135 261 }
136 262
137 263 Validators createValidators(const ValidatorsTypes &types)
138 264 {
139 265 Validators result{};
140 266
141 267 std::transform(types.cbegin(), types.cend(), std::inserter(result, result.end()),
142 268 [](const auto &type) { return FuzzingValidatorFactory::create(type); });
143 269
144 270 return result;
145 271 }
146 272
147 273 /**
148 274 * Validates all the variables' states passed in parameter, according to a set of validators
149 275 * @param variablesPool the variables' states
150 276 * @param validators the validators used for validation
151 277 */
152 278 void validate(const VariablesPool &variablesPool, const Validators &validators)
153 279 {
154 280 for (const auto &variablesPoolEntry : variablesPool) {
155 281 auto variableId = variablesPoolEntry.first;
156 282 const auto &variableState = variablesPoolEntry.second;
157 283
158 284 auto variableMessage = variableState.m_Variable ? variableState.m_Variable->name()
159 285 : QStringLiteral("null variable");
160 286 qCInfo(LOG_TestAmdaFuzzing()).noquote() << "Validating state of variable at index"
161 287 << variableId << "(" << variableMessage << ")...";
162 288
163 289 for (const auto &validator : validators) {
164 290 validator->validate(VariableState{variableState});
165 291 }
166 292
167 293 qCInfo(LOG_TestAmdaFuzzing()).noquote() << "Validation completed.";
168 294 }
169 295 }
170 296
171 297 /**
172 298 * Class to run random tests
173 299 */
174 300 class FuzzingTest {
175 301 public:
176 302 explicit FuzzingTest(VariableController &variableController, Properties properties)
177 303 : m_VariableController{variableController},
178 304 m_Properties{std::move(properties)},
179 305 m_FuzzingState{}
180 306 {
181 307 // Inits variables pool: at init, all variables are null
182 308 for (auto variableId = 0; variableId < nbMaxVariables(); ++variableId) {
183 309 m_FuzzingState.m_VariablesPool[variableId] = VariableState{};
184 310 }
185 311
186 312 // Inits sync groups and registers them into the variable controller
187 313 for (auto i = 0; i < nbMaxSyncGroups(); ++i) {
188 314 auto syncGroupId = SyncGroupId::createUuid();
189 315 variableController.onAddSynchronizationGroupId(syncGroupId);
190 316 m_FuzzingState.m_SyncGroupsPool[syncGroupId] = SyncGroup{};
191 317 }
192 318 }
193 319
194 320 void execute()
195 321 {
196 qCInfo(LOG_TestAmdaFuzzing()).noquote() << "Running" << nbMaxOperations() << "operations on"
197 << nbMaxVariables() << "variable(s)...";
198
199 322
200 323 // Inits the count of the number of operations before the next validation
201 324 int nextValidationCounter = 0;
202 325 auto updateValidationCounter = [this, &nextValidationCounter]() {
203 326 nextValidationCounter = RandomGenerator::instance().generateInt(
204 327 validationFrequencies().first, validationFrequencies().second);
205 328 qCInfo(LOG_TestAmdaFuzzing()).noquote()
206 329 << "Next validation in " << nextValidationCounter << "operation(s)...";
207 330 };
208 331 updateValidationCounter();
209 332
333 // Get custom operations
334 auto customOperations = m_Properties.value(CUSTOM_OPERATIONS_PROPERTY).value<CustomVariablesOperations>();
335 auto isCustomTest = !customOperations.empty();
336
337 auto nbOperations = isCustomTest ? customOperations.size() : nbMaxOperations();
338
339 qCInfo(LOG_TestAmdaFuzzing()).noquote() << "Running" << nbOperations << "operations on"
340 << nbMaxVariables() << "variable(s)...";
341
210 342 auto canExecute = true;
211 for (auto i = 0; i < nbMaxOperations() && canExecute; ++i) {
212 // Retrieves all operations that can be executed in the current context
213 VariablesOperations variableOperations{};
214 Weights weights{};
215 std::tie(variableOperations, weights)
216 = availableOperations(m_FuzzingState, operationsPool());
217
218 canExecute = !variableOperations.empty();
219 if (canExecute) {
220 --nextValidationCounter;
343 for (auto i = 0; i < nbOperations && canExecute; ++i) {
344 VariableOperation variableOperation{};
345 auto forceWait = false;
346
347 if(isCustomTest){
348 auto customOperation = customOperations.front();
349 variableOperation = customOperation.m_VariableOperation;
350 customOperations.erase(customOperations.begin());
351
352 canExecute = variableOperation.second->canExecute(variableOperation.first, m_FuzzingState);
353 forceWait = customOperation.m_WaitAcquisition;
354 } else {
355 // Retrieves all operations that can be executed in the current context
356 VariablesOperations variableOperations{};
357 Weights weights{};
358 std::tie(variableOperations, weights)
359 = availableOperations(m_FuzzingState, operationsPool());
221 360
222 361 // Of the operations available, chooses a random operation and executes it
223 auto variableOperation
362 variableOperation
224 363 = RandomGenerator::instance().randomChoice(variableOperations, weights);
364 canExecute = !variableOperations.empty();
365 forceWait = operationsPool().at(variableOperation.second).m_WaitAcquisition;
366 }
367
368 if (canExecute) {
369 --nextValidationCounter;
225 370
226 371 auto variableId = variableOperation.first;
227 372 auto fuzzingOperation = variableOperation.second;
228 373
229 374 auto waitAcquisition = nextValidationCounter == 0
230 || operationsPool().at(fuzzingOperation).m_WaitAcquisition;
375 || forceWait;
231 376
377 qCInfo(LOG_TestAmdaFuzzing()).noquote() << "Operation :" << i +1 << " on"
378 << nbOperations;
232 379 fuzzingOperation->execute(variableId, m_FuzzingState, m_VariableController,
233 380 m_Properties);
234 381
235 382 if (waitAcquisition) {
236 383 qCDebug(LOG_TestAmdaFuzzing()) << "Waiting for acquisition to finish...";
237 384 SignalWaiter{m_VariableController, SIGNAL(acquisitionFinished())}.wait(
238 385 acquisitionTimeout());
239 386
240 387 // Validates variables
241 388 if (nextValidationCounter == 0) {
242 389 validate(m_FuzzingState.m_VariablesPool, validators());
243 390 updateValidationCounter();
244 391 }
245 392 }
246 393 else {
247 394 // Delays the next operation with a randomly generated time
248 395 auto delay = RandomGenerator::instance().generateInt(operationDelays().first,
249 396 operationDelays().second);
250 397 qCDebug(LOG_TestAmdaFuzzing())
251 398 << "Waiting " << delay << "ms before the next operation...";
252 399 QTest::qWait(delay);
253 400 }
254 401 }
255 402 else {
256 403 qCInfo(LOG_TestAmdaFuzzing()).noquote()
257 404 << "No more operations are available, the execution of the test will stop...";
258 405 }
259 406 }
260 407
261 408 qCInfo(LOG_TestAmdaFuzzing()).noquote() << "Execution of the test completed.";
262 409 }
263 410
264 411 private:
265 412 OperationsPool operationsPool() const
266 413 {
267 414 static auto result = createOperationsPool(
268 415 m_Properties.value(AVAILABLE_OPERATIONS_PROPERTY, AVAILABLE_OPERATIONS_DEFAULT_VALUE)
269 416 .value<OperationsTypes>());
270 417 return result;
271 418 }
272 419
273 420 Validators validators() const
274 421 {
275 422 static auto result
276 423 = createValidators(m_Properties.value(VALIDATORS_PROPERTY, VALIDATORS_DEFAULT_VALUE)
277 424 .value<ValidatorsTypes>());
278 425 return result;
279 426 }
280 427
281 428 DECLARE_PROPERTY_GETTER(nbMaxOperations, NB_MAX_OPERATIONS, int)
282 429 DECLARE_PROPERTY_GETTER(nbMaxSyncGroups, NB_MAX_SYNC_GROUPS, int)
283 430 DECLARE_PROPERTY_GETTER(nbMaxVariables, NB_MAX_VARIABLES, int)
284 431 DECLARE_PROPERTY_GETTER(operationDelays, OPERATION_DELAY_BOUNDS, IntPair)
285 432 DECLARE_PROPERTY_GETTER(validationFrequencies, VALIDATION_FREQUENCY_BOUNDS, IntPair)
286 433 DECLARE_PROPERTY_GETTER(acquisitionTimeout, ACQUISITION_TIMEOUT, int)
287 434
288 435 VariableController &m_VariableController;
289 436 Properties m_Properties;
290 437 FuzzingState m_FuzzingState;
291 438 };
292 439
293 440 } // namespace
294 441
295 442 Q_DECLARE_METATYPE(OperationsTypes)
443 Q_DECLARE_METATYPE(CustomVariablesOperations)
296 444
297 445 class TestAmdaFuzzing : public QObject {
298 446 Q_OBJECT
299 447
300 448 private slots:
301 449 /// Input data for @sa testFuzzing()
302 450 void testFuzzing_data();
303 451 void testFuzzing();
304 452 };
305 453
306 454 void TestAmdaFuzzing::testFuzzing_data()
307 455 {
308 456 // Note: Comment this line to run fuzzing tests
309 QSKIP("Fuzzing tests are disabled by default");
457 // QSKIP("Fuzzing tests are disabled by default");
310 458
311 459 // ////////////// //
312 460 // Test structure //
313 461 // ////////////// //
314 462
315 463 QTest::addColumn<Properties>("properties"); // Properties for random test
316 464
317 465 // ////////// //
318 466 // Test cases //
319 467 // ////////// //
320 468
321 auto maxRange = SqpRange::fromDateTime({2017, 1, 1}, {0, 0}, {2017, 1, 5}, {0, 0});
322 MetadataPool metadataPool{{{"dataType", "vector"}, {"xml:id", "imf"}}};
469 auto maxRange = SqpRange::fromDateTime({2017, 1, 5}, {8, 0}, {2017, 1, 5}, {16, 0});
470 MetadataPool metadataPool{{{"dataType", "vector"}, {"xml:id", "c1_b"}}};
323 471
324 472 // Note: we don't use auto here as we want to pass std::shared_ptr<IDataProvider> as is in the
325 473 // QVariant
326 474 std::shared_ptr<IDataProvider> provider = std::make_shared<AmdaProvider>();
327 475
476
477 // Case Custom one : Only lot of pan on two variables synchronized
478 // QTest::newRow("fuzzingTestPan") << Properties{
479 // {MAX_RANGE_PROPERTY, QVariant::fromValue(maxRange)},
480 // {METADATA_POOL_PROPERTY, QVariant::fromValue(metadataPool)},
481 // {PROVIDER_PROPERTY, QVariant::fromValue(provider)},
482 // {CUSTOM_OPERATIONS_PROPERTY, CUSTOM_CASE_ONE}};
483
484 // QTest::newRow("fuzzingTestZoom") << Properties{
485 // {MAX_RANGE_PROPERTY, QVariant::fromValue(maxRange)},
486 // {METADATA_POOL_PROPERTY, QVariant::fromValue(metadataPool)},
487 // {PROVIDER_PROPERTY, QVariant::fromValue(provider)},
488 // {CUSTOM_OPERATIONS_PROPERTY, CUSTOM_CASE_TWO}};
489
490
491 //// Fuzzing
328 492 QTest::newRow("fuzzingTest") << Properties{
329 {MAX_RANGE_PROPERTY, QVariant::fromValue(maxRange)},
330 {METADATA_POOL_PROPERTY, QVariant::fromValue(metadataPool)},
331 {PROVIDER_PROPERTY, QVariant::fromValue(provider)}};
493 {MAX_RANGE_PROPERTY, QVariant::fromValue(maxRange)},
494 {METADATA_POOL_PROPERTY, QVariant::fromValue(metadataPool)},
495 {PROVIDER_PROPERTY, QVariant::fromValue(provider)}};
496
332 497 }
333 498
334 499 void TestAmdaFuzzing::testFuzzing()
335 500 {
336 501 QFETCH(Properties, properties);
337 502
338 503 // Sets cache property
339 504 QSettings settings{};
340 505 auto cacheTolerance = properties.value(CACHE_TOLERANCE_PROPERTY, CACHE_TOLERANCE_DEFAULT_VALUE);
341 506 settings.setValue(GENERAL_TOLERANCE_AT_INIT_KEY, cacheTolerance);
342 507 settings.setValue(GENERAL_TOLERANCE_AT_UPDATE_KEY, cacheTolerance);
343 508
344 509 auto &variableController = sqpApp->variableController();
345 510 auto &timeController = sqpApp->timeController();
346 511
347 512 // Generates random initial range (bounded to max range)
348 513 auto maxRange = properties.value(MAX_RANGE_PROPERTY, QVariant::fromValue(INVALID_RANGE))
349 514 .value<SqpRange>();
350 515
351 516 QVERIFY(maxRange != INVALID_RANGE);
352 517
353 518 auto initialRangeStart
354 519 = RandomGenerator::instance().generateDouble(maxRange.m_TStart, maxRange.m_TEnd);
355 520 auto initialRangeEnd
356 521 = RandomGenerator::instance().generateDouble(maxRange.m_TStart, maxRange.m_TEnd);
357 522 if (initialRangeStart > initialRangeEnd) {
358 523 std::swap(initialRangeStart, initialRangeEnd);
359 524 }
360 525
361 526 // Sets initial range on time controller
362 527 SqpRange initialRange{initialRangeStart, initialRangeEnd};
363 528 qCInfo(LOG_TestAmdaFuzzing()).noquote() << "Setting initial range to" << initialRange << "...";
364 529 timeController.onTimeToUpdate(initialRange);
365 530 properties.insert(INITIAL_RANGE_PROPERTY, QVariant::fromValue(initialRange));
366 531
367 532 FuzzingTest test{variableController, properties};
368 533 test.execute();
369 534 }
370 535
371 536 int main(int argc, char *argv[])
372 537 {
373 538 // Increases the test function timeout (which is 5 minutes by default) to 12 hours
374 539 // https://stackoverflow.com/questions/42655932/setting-timeout-to-qt-test
375 540 qputenv("QTEST_FUNCTION_TIMEOUT", QByteArray::number(12*60*60*1000));
376 541
377 542 QLoggingCategory::setFilterRules(
378 543 "*.warning=false\n"
379 544 "*.info=false\n"
380 545 "*.debug=false\n"
381 546 "FuzzingOperations.info=true\n"
382 547 "FuzzingValidators.info=true\n"
383 548 "TestAmdaFuzzing.info=true\n");
384 549
385 550 SqpApplication app{argc, argv};
386 551 SqpApplication::setOrganizationName("LPP");
387 552 SqpApplication::setOrganizationDomain("lpp.fr");
388 553 SqpApplication::setApplicationName("SciQLop-TestFuzzing");
389 554 app.setAttribute(Qt::AA_Use96Dpi, true);
390 555 TestAmdaFuzzing testObject{};
391 556 QTEST_SET_MAIN_SOURCE_PATH
392 557 return QTest::qExec(&testObject, argc, argv);
393 558 }
394 559
395 560 #include "TestAmdaFuzzing.moc"
General Comments 0
You need to be logged in to leave comments. Login now