##// END OF EJS Templates
Merge branch 'feature/CloneVariable' into develop
Alexandre Leroux -
r714:ceac74ef218a merge
parent child
Show More
@@ -0,0 +1,35
1 #ifndef SCIQLOP_STRINGUTILS_H
2 #define SCIQLOP_STRINGUTILS_H
3
4 #include "CoreGlobal.h"
5
6 #include <vector>
7
8 class QString;
9
10 /**
11 * Utility class with methods for strings
12 */
13 struct SCIQLOP_CORE_EXPORT StringUtils {
14 /**
15 * Generates a unique name from a default name and a set of forbidden names.
16 *
17 * Generating the unique name is done by adding an index to the default name and stopping at the
18 * first index for which the generated name is not in the forbidden names.
19 *
20 * Examples (defaultName, forbiddenNames -> result):
21 * - "FGM", {"FGM"} -> "FGM1"
22 * - "FGM", {"ABC"} -> "FGM"
23 * - "FGM", {"FGM", "FGM1"} -> "FGM2"
24 * - "FGM", {"FGM", "FGM2"} -> "FGM1"
25 * - "", {"ABC"} -> "1"
26 *
27 * @param defaultName the default name
28 * @param forbiddenNames the set of forbidden names
29 * @return the unique name generated
30 */
31 static QString uniqueName(const QString &defaultName,
32 const std::vector<QString> &forbiddenNames) noexcept;
33 };
34
35 #endif // SCIQLOP_STRINGUTILS_H
@@ -0,0 +1,30
1 #include "Common/StringUtils.h"
2
3 #include <QRegExp>
4 #include <QString>
5
6 #include <set>
7
8 QString StringUtils::uniqueName(const QString &defaultName,
9 const std::vector<QString> &forbiddenNames) noexcept
10 {
11 // Gets the base of the unique name to generate, by removing trailing number (for example, base
12 // name of "FGM12" is "FGM")
13 auto baseName = defaultName;
14 baseName.remove(QRegExp{QStringLiteral("\\d*$")});
15
16 // Finds the unique name by adding an index to the base name and stops when the generated name
17 // isn't forbidden
18 QString newName{};
19 auto forbidden = true;
20 for (auto i = 0; forbidden; ++i) {
21 newName = (i == 0) ? baseName : baseName + QString::number(i);
22 forbidden = newName.isEmpty()
23 || std::any_of(forbiddenNames.cbegin(), forbiddenNames.cend(),
24 [&newName](const auto &name) {
25 return name.compare(newName, Qt::CaseInsensitive) == 0;
26 });
27 }
28
29 return newName;
30 }
@@ -0,0 +1,50
1 #include <Common/StringUtils.h>
2
3 #include <QObject>
4 #include <QtTest>
5
6 class TestStringUtils : public QObject {
7 Q_OBJECT
8
9 private slots:
10 void testUniqueName_data();
11 void testUniqueName();
12 };
13
14 void TestStringUtils::testUniqueName_data()
15 {
16 // ////////////// //
17 // Test structure //
18 // ////////////// //
19
20 QTest::addColumn<QString>("defaultName");
21 QTest::addColumn<std::vector<QString> >("forbiddenNames");
22 QTest::addColumn<QString>("expectedName");
23
24 // ////////// //
25 // Test cases //
26 // ////////// //
27
28 QTest::newRow("uniqueName") << "FGM" << std::vector<QString>{"FGM2"} << "FGM";
29 QTest::newRow("uniqueName2") << "FGM2" << std::vector<QString>{"FGM", "FGM1", "FGM2"} << "FGM3";
30 QTest::newRow("uniqueName3") << "FGM1" << std::vector<QString>{"FGM1"} << "FGM";
31 QTest::newRow("uniqueName4") << "FGM" << std::vector<QString>{"FGM"} << "FGM1";
32 QTest::newRow("uniqueName5") << "FGM" << std::vector<QString>{"FGM", "FGM1", "FGM3"} << "FGM2";
33 QTest::newRow("uniqueName6") << "FGM" << std::vector<QString>{"A", "B", "C"} << "FGM";
34 QTest::newRow("uniqueName7") << "FGM" << std::vector<QString>{"fGm", "FGm1", "Fgm2"} << "FGM3";
35 QTest::newRow("uniqueName8") << "" << std::vector<QString>{"A", "B", "C"} << "1";
36 QTest::newRow("uniqueName9") << "24" << std::vector<QString>{"A", "B", "C"} << "1";
37 }
38
39 void TestStringUtils::testUniqueName()
40 {
41 QFETCH(QString, defaultName);
42 QFETCH(std::vector<QString>, forbiddenNames);
43 QFETCH(QString, expectedName);
44
45 auto result = StringUtils::uniqueName(defaultName, forbiddenNames);
46 QCOMPARE(result, expectedName);
47 }
48
49 QTEST_MAIN(TestStringUtils)
50 #include "TestStringUtils.moc"
@@ -1,76 +1,77
1 1 #ifndef SCIQLOP_IDATAPROVIDER_H
2 2 #define SCIQLOP_IDATAPROVIDER_H
3 3
4 4 #include "CoreGlobal.h"
5 5
6 6 #include <memory>
7 7
8 8 #include <QObject>
9 9 #include <QUuid>
10 10
11 11 #include <Common/MetaTypes.h>
12 12
13 13 #include <Data/SqpRange.h>
14 14
15 15 #include <functional>
16 16
17 17 class DataProviderParameters;
18 18 class IDataSeries;
19 19 class QNetworkReply;
20 20 class QNetworkRequest;
21 21
22 22 /**
23 23 * @brief The IDataProvider interface aims to declare a data provider.
24 24 *
25 25 * A data provider is an entity that generates data and returns it according to various parameters
26 26 * (time interval, product to retrieve the data, etc.)
27 27 *
28 28 * @sa IDataSeries
29 29 */
30 30 class SCIQLOP_CORE_EXPORT IDataProvider : public QObject {
31 31
32 32 Q_OBJECT
33 33 public:
34 34 virtual ~IDataProvider() noexcept = default;
35 virtual std::shared_ptr<IDataProvider> clone() const = 0;
35 36
36 37 /**
37 38 * @brief requestDataLoading provide datas for the data identified by acqIdentifier and
38 39 * parameters
39 40 */
40 41 virtual void requestDataLoading(QUuid acqIdentifier, const DataProviderParameters &parameters)
41 42 = 0;
42 43
43 44 /**
44 45 * @brief requestDataAborting stop data loading of the data identified by acqIdentifier
45 46 */
46 47 virtual void requestDataAborting(QUuid acqIdentifier) = 0;
47 48
48 49 signals:
49 50 /**
50 51 * @brief dataProvided send dataSeries under dateTime and that corresponds of the data
51 52 * identified by acqIdentifier
52 53 */
53 54 void dataProvided(QUuid acqIdentifier, std::shared_ptr<IDataSeries> dateSeriesAcquired,
54 55 const SqpRange &dataRangeAcquired);
55 56
56 57 /**
57 58 * @brief dataProvided send dataSeries under dateTime and that corresponds of the data
58 59 * identified by identifier
59 60 */
60 61 void dataProvidedProgress(QUuid acqIdentifier, double progress);
61 62
62 63
63 64 /**
64 65 * @brief requestConstructed send a request for the data identified by acqIdentifier
65 66 * @callback is the methode call by the reply of the request when it is finished.
66 67 */
67 68 void requestConstructed(const QNetworkRequest &request, QUuid acqIdentifier,
68 69 std::function<void(QNetworkReply *, QUuid)> callback);
69 70 };
70 71
71 72 // Required for using shared_ptr in signals/slots
72 73 SCIQLOP_REGISTER_META_TYPE(IDATAPROVIDER_PTR_REGISTRY, std::shared_ptr<IDataProvider>)
73 74 SCIQLOP_REGISTER_META_TYPE(IDATAPROVIDER_FUNCTION_REGISTRY,
74 75 std::function<void(QNetworkReply *, QUuid)>)
75 76
76 77 #endif // SCIQLOP_IDATAPROVIDER_H
@@ -1,75 +1,80
1 1 #ifndef SCIQLOP_VARIABLE_H
2 2 #define SCIQLOP_VARIABLE_H
3 3
4 4 #include "CoreGlobal.h"
5 5
6 6 #include <Data/DataSeriesIterator.h>
7 7 #include <Data/SqpRange.h>
8 8
9 9 #include <QLoggingCategory>
10 10 #include <QObject>
11 11
12 12 #include <Common/MetaTypes.h>
13 13 #include <Common/spimpl.h>
14 14
15 15 Q_DECLARE_LOGGING_CATEGORY(LOG_Variable)
16 16
17 17 class IDataSeries;
18 18 class QString;
19 19
20 20 /**
21 21 * @brief The Variable class represents a variable in SciQlop.
22 22 */
23 23 class SCIQLOP_CORE_EXPORT Variable : public QObject {
24 24
25 25 Q_OBJECT
26 26
27 27 public:
28 28 explicit Variable(const QString &name, const SqpRange &dateTime,
29 29 const QVariantHash &metadata = {});
30 30
31 /// Copy ctor
32 explicit Variable(const Variable &other);
33
34 std::shared_ptr<Variable> clone() const;
35
31 36 QString name() const noexcept;
32 37 void setName(const QString &name) noexcept;
33 38 SqpRange range() const noexcept;
34 39 void setRange(const SqpRange &range) noexcept;
35 40 SqpRange cacheRange() const noexcept;
36 41 void setCacheRange(const SqpRange &cacheRange) noexcept;
37 42
38 43 /// Returns the real range of the variable, i.e. the min and max x-axis values of the data
39 44 /// series between the range of the variable. The real range is updated each time the variable
40 45 /// range or the data series changed
41 46 /// @return the real range, invalid range if the data series is null or empty
42 47 /// @sa setDataSeries()
43 48 /// @sa setRange()
44 49 SqpRange realRange() const noexcept;
45 50
46 51 /// @return the data of the variable, nullptr if there is no data
47 52 std::shared_ptr<IDataSeries> dataSeries() const noexcept;
48 53
49 54 QVariantHash metadata() const noexcept;
50 55
51 56 bool contains(const SqpRange &range) const noexcept;
52 57 bool intersect(const SqpRange &range) const noexcept;
53 58 bool isInside(const SqpRange &range) const noexcept;
54 59
55 60 bool cacheContains(const SqpRange &range) const noexcept;
56 61 bool cacheIntersect(const SqpRange &range) const noexcept;
57 62 bool cacheIsInside(const SqpRange &range) const noexcept;
58 63
59 64 QVector<SqpRange> provideNotInCacheRangeList(const SqpRange &range) const noexcept;
60 65 QVector<SqpRange> provideInCacheRangeList(const SqpRange &range) const noexcept;
61 66 void mergeDataSeries(std::shared_ptr<IDataSeries> dataSeries) noexcept;
62 67
63 68 signals:
64 69 void updated();
65 70
66 71 private:
67 72 class VariablePrivate;
68 73 spimpl::unique_impl_ptr<VariablePrivate> impl;
69 74 };
70 75
71 76 // Required for using shared_ptr in signals/slots
72 77 SCIQLOP_REGISTER_META_TYPE(VARIABLE_PTR_REGISTRY, std::shared_ptr<Variable>)
73 78 SCIQLOP_REGISTER_META_TYPE(VARIABLE_PTR_VECTOR_REGISTRY, QVector<std::shared_ptr<Variable> >)
74 79
75 80 #endif // SCIQLOP_VARIABLE_H
@@ -1,122 +1,129
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 * Clones the variable passed in parameter and adds the duplicate to the controller
46 * @param variable the variable to duplicate
47 * @return the duplicate created, nullptr if the variable couldn't be created
48 */
49 std::shared_ptr<Variable> cloneVariable(std::shared_ptr<Variable> variable) noexcept;
50
51 /**
45 52 * Deletes from the controller the variable passed in parameter.
46 53 *
47 54 * Delete a variable includes:
48 55 * - the deletion of the various references to the variable in SciQlop
49 56 * - the deletion of the model variable
50 57 * - the deletion of the provider associated with the variable
51 58 * - removing the cache associated with the variable
52 59 *
53 60 * @param variable the variable to delete from the controller.
54 61 */
55 62 void deleteVariable(std::shared_ptr<Variable> variable) noexcept;
56 63
57 64 /**
58 65 * Deletes from the controller the variables passed in parameter.
59 66 * @param variables the variables to delete from the controller.
60 67 * @sa deleteVariable()
61 68 */
62 69 void deleteVariables(const QVector<std::shared_ptr<Variable> > &variables) noexcept;
63 70
64 71 /**
65 72 * @brief abort the variable retrieve data progression
66 73 */
67 74 void abortProgress(std::shared_ptr<Variable> variable);
68 75
69 76 static AcquisitionZoomType getZoomType(const SqpRange &range, const SqpRange &oldRange);
70 77 signals:
71 78 /// Signal emitted when a variable is about to be deleted from the controller
72 79 void variableAboutToBeDeleted(std::shared_ptr<Variable> variable);
73 80
74 81 /// Signal emitted when a data acquisition is requested on a range for a variable
75 82 void rangeChanged(std::shared_ptr<Variable> variable, const SqpRange &range);
76 83
77 84 /// Signal emitted when a sub range of the cacheRange of the variable can be displayed
78 85 void updateVarDisplaying(std::shared_ptr<Variable> variable, const SqpRange &range);
79 86
80 87 public slots:
81 88 /// Request the data loading of the variable whithin range
82 89 void onRequestDataLoading(QVector<std::shared_ptr<Variable> > variables, const SqpRange &range,
83 90 const SqpRange &oldRange, bool synchronise);
84 91 /**
85 92 * Creates a new variable and adds it to the model
86 93 * @param name the name of the new variable
87 94 * @param metadata the metadata of the new variable
88 95 * @param provider the data provider for the new variable
89 96 * @return the pointer to the new variable or nullptr if the creation failed
90 97 */
91 98 std::shared_ptr<Variable> createVariable(const QString &name, const QVariantHash &metadata,
92 99 std::shared_ptr<IDataProvider> provider) noexcept;
93 100
94 101 /// Update the temporal parameters of every selected variable to dateTime
95 102 void onDateTimeOnSelection(const SqpRange &dateTime);
96 103
97 104
98 105 void onDataProvided(QUuid vIdentifier, const SqpRange &rangeRequested,
99 106 const SqpRange &cacheRangeRequested,
100 107 QVector<AcquisitionDataPacket> dataAcquired);
101 108
102 109 void onVariableRetrieveDataInProgress(QUuid identifier, double progress);
103 110
104 111 /// Cancel the current request for the variable
105 112 void onAbortProgressRequested(std::shared_ptr<Variable> variable);
106 113
107 114 /// synchronization group methods
108 115 void onAddSynchronizationGroupId(QUuid synchronizationGroupId);
109 116 void onRemoveSynchronizationGroupId(QUuid synchronizationGroupId);
110 117 void onAddSynchronized(std::shared_ptr<Variable> variable, QUuid synchronizationGroupId);
111 118
112 119 void initialize();
113 120 void finalize();
114 121
115 122 private:
116 123 void waitForFinish();
117 124
118 125 class VariableControllerPrivate;
119 126 spimpl::unique_impl_ptr<VariableControllerPrivate> impl;
120 127 };
121 128
122 129 #endif // SCIQLOP_VARIABLECONTROLLER_H
@@ -1,81 +1,96
1 1 #ifndef SCIQLOP_VARIABLEMODEL_H
2 2 #define SCIQLOP_VARIABLEMODEL_H
3 3
4 4 #include "CoreGlobal.h"
5 5
6 6 #include <Data/SqpRange.h>
7 7
8 8 #include <QAbstractTableModel>
9 9 #include <QLoggingCategory>
10 10
11 11 #include <Common/MetaTypes.h>
12 12 #include <Common/spimpl.h>
13 13
14 14 Q_DECLARE_LOGGING_CATEGORY(LOG_VariableModel)
15 15
16 16 enum VariableRoles { ProgressRole = Qt::UserRole };
17 17
18 18
19 19 class IDataSeries;
20 20 class Variable;
21 21
22 22 /**
23 23 * @brief The VariableModel class aims to hold the variables that have been created in SciQlop
24 24 */
25 25 class SCIQLOP_CORE_EXPORT VariableModel : public QAbstractTableModel {
26 26 Q_OBJECT
27 27 public:
28 28 explicit VariableModel(QObject *parent = nullptr);
29 29
30 30 /**
31 * Adds an existing variable in the model.
32 * @param variable the variable to add.
33 * @remarks the variable's name is modified to avoid name duplicates
34 * @remarks this method does nothing if the variable already exists in the model
35 */
36 void addVariable(std::shared_ptr<Variable> variable) noexcept;
37
38 /**
39 * Checks that a variable is contained in the model
40 * @param variable the variable to check
41 * @return true if the variable is in the model, false otherwise
42 */
43 bool containsVariable(std::shared_ptr<Variable> variable) const noexcept;
44
45 /**
31 46 * Creates a new variable in the model
32 47 * @param name the name of the new variable
33 48 * @param dateTime the dateTime of the new variable
34 49 * @param metadata the metadata associated to the new variable
35 50 * @return the pointer to the new variable
36 51 */
37 52 std::shared_ptr<Variable> createVariable(const QString &name, const SqpRange &dateTime,
38 53 const QVariantHash &metadata) noexcept;
39 54
40 55 /**
41 56 * Deletes a variable from the model, if it exists
42 57 * @param variable the variable to delete
43 58 */
44 59 void deleteVariable(std::shared_ptr<Variable> variable) noexcept;
45 60
46 61
47 62 std::shared_ptr<Variable> variable(int index) const;
48 63 std::vector<std::shared_ptr<Variable> > variables() const;
49 64
50 65 void setDataProgress(std::shared_ptr<Variable> variable, double progress);
51 66
52 67
53 68 // /////////////////////////// //
54 69 // QAbstractTableModel methods //
55 70 // /////////////////////////// //
56 71
57 72 virtual int columnCount(const QModelIndex &parent = QModelIndex{}) const override;
58 73 virtual int rowCount(const QModelIndex &parent = QModelIndex{}) const override;
59 74 virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
60 75 virtual QVariant headerData(int section, Qt::Orientation orientation,
61 76 int role = Qt::DisplayRole) const override;
62 77
63 78
64 79 void abortProgress(const QModelIndex &index);
65 80
66 81 signals:
67 82 void abortProgessRequested(std::shared_ptr<Variable> variable);
68 83
69 84 private:
70 85 class VariableModelPrivate;
71 86 spimpl::unique_impl_ptr<VariableModelPrivate> impl;
72 87
73 88 private slots:
74 89 /// Slot called when data of a variable has been updated
75 90 void onVariableUpdated() noexcept;
76 91 };
77 92
78 93 // Registers QVector<int> metatype so it can be used in VariableModel::dataChanged() signal
79 94 SCIQLOP_REGISTER_META_TYPE(QVECTOR_INT_REGISTRY, QVector<int>)
80 95
81 96 #endif // SCIQLOP_VARIABLEMODEL_H
@@ -1,62 +1,63
1 1
2 2 core_moc_headers = [
3 3 'include/Data/IDataProvider.h',
4 4 'include/DataSource/DataSourceController.h',
5 5 'include/DataSource/DataSourceItemAction.h',
6 6 'include/Network/NetworkController.h',
7 7 'include/Time/TimeController.h',
8 8 'include/Variable/Variable.h',
9 9 'include/Variable/VariableCacheController.h',
10 10 'include/Variable/VariableController.h',
11 11 'include/Variable/VariableAcquisitionWorker.h',
12 12 'include/Variable/VariableCacheStrategy.h',
13 13 'include/Variable/VariableSynchronizationGroup.h',
14 14 'include/Variable/VariableModel.h',
15 15 'include/Visualization/VisualizationController.h'
16 16 ]
17 17
18 18
19 19 core_moc_files = qt5.preprocess(moc_headers : core_moc_headers)
20 20
21 21 core_sources = [
22 22 'src/Common/DateUtils.cpp',
23 'src/Common/StringUtils.cpp',
23 24 'src/Data/ScalarSeries.cpp',
24 25 'src/Data/DataSeriesIterator.cpp',
25 26 'src/Data/ArrayDataIterator.cpp',
26 27 'src/Data/VectorSeries.cpp',
27 28 'src/DataSource/DataSourceController.cpp',
28 29 'src/DataSource/DataSourceItem.cpp',
29 30 'src/DataSource/DataSourceItemAction.cpp',
30 31 'src/Network/NetworkController.cpp',
31 32 'src/Plugin/PluginManager.cpp',
32 33 'src/Settings/SqpSettingsDefs.cpp',
33 34 'src/Time/TimeController.cpp',
34 35 'src/Variable/Variable.cpp',
35 36 'src/Variable/VariableCacheController.cpp',
36 37 'src/Variable/VariableController.cpp',
37 38 'src/Variable/VariableAcquisitionWorker.cpp',
38 39 'src/Variable/VariableCacheStrategy.cpp',
39 40 'src/Variable/VariableSynchronizationGroup.cpp',
40 41 'src/Variable/VariableModel.cpp',
41 42 'src/Visualization/VisualizationController.cpp'
42 43 ]
43 44
44 45 core_inc = include_directories(['include', '../plugin/include'])
45 46
46 47 sciqlop_core_lib = library('sciqlopcore',
47 48 core_sources,
48 49 core_moc_files,
49 50 cpp_args : '-DCORE_LIB',
50 51 include_directories : core_inc,
51 52 dependencies : [qt5core, qt5network],
52 53 install : true
53 54 )
54 55
55 56
56 57 sciqlop_core = declare_dependency(link_with : sciqlop_core_lib,
57 58 include_directories : core_inc,
58 59 dependencies : [qt5core, qt5network])
59 60
60 61
61 62 subdir('tests')
62 63
@@ -1,277 +1,296
1 1 #include "Variable/Variable.h"
2 2
3 3 #include <Data/IDataSeries.h>
4 4 #include <Data/SqpRange.h>
5 5
6 6 #include <QMutex>
7 7 #include <QReadWriteLock>
8 8 #include <QThread>
9 9
10 10 Q_LOGGING_CATEGORY(LOG_Variable, "Variable")
11 11
12 12 struct Variable::VariablePrivate {
13 13 explicit VariablePrivate(const QString &name, const SqpRange &dateTime,
14 14 const QVariantHash &metadata)
15 15 : m_Name{name},
16 16 m_Range{dateTime},
17 17 m_Metadata{metadata},
18 18 m_DataSeries{nullptr},
19 19 m_RealRange{INVALID_RANGE}
20 20 {
21 21 }
22 22
23 VariablePrivate(const VariablePrivate &other)
24 : m_Name{other.m_Name},
25 m_Range{other.m_Range},
26 m_Metadata{other.m_Metadata},
27 m_DataSeries{other.m_DataSeries != nullptr ? other.m_DataSeries->clone() : nullptr},
28 m_RealRange{other.m_RealRange}
29 {
30 }
31
23 32 void lockRead() { m_Lock.lockForRead(); }
24 33 void lockWrite() { m_Lock.lockForWrite(); }
25 34 void unlock() { m_Lock.unlock(); }
26 35
27 36 void purgeDataSeries()
28 37 {
29 38 if (m_DataSeries) {
30 39 m_DataSeries->purge(m_CacheRange.m_TStart, m_CacheRange.m_TEnd);
31 40 }
32 41 updateRealRange();
33 42 }
34 43
35 44 /// Updates real range according to current variable range and data series
36 45 void updateRealRange()
37 46 {
38 47 if (m_DataSeries) {
39 48 m_DataSeries->lockRead();
40 49 auto end = m_DataSeries->cend();
41 50 auto minXAxisIt = m_DataSeries->minXAxisData(m_Range.m_TStart);
42 51 auto maxXAxisIt = m_DataSeries->maxXAxisData(m_Range.m_TEnd);
43 52
44 53 m_RealRange = (minXAxisIt != end && maxXAxisIt != end)
45 54 ? SqpRange{minXAxisIt->x(), maxXAxisIt->x()}
46 55 : INVALID_RANGE;
47 56 m_DataSeries->unlock();
48 57 }
49 58 else {
50 59 m_RealRange = INVALID_RANGE;
51 60 }
52 61 }
53 62
54 63 QString m_Name;
55 64
56 65 SqpRange m_Range;
57 66 SqpRange m_CacheRange;
58 67 QVariantHash m_Metadata;
59 68 std::shared_ptr<IDataSeries> m_DataSeries;
60 69 SqpRange m_RealRange;
61 70
62 71 QReadWriteLock m_Lock;
63 72 };
64 73
65 74 Variable::Variable(const QString &name, const SqpRange &dateTime, const QVariantHash &metadata)
66 75 : impl{spimpl::make_unique_impl<VariablePrivate>(name, dateTime, metadata)}
67 76 {
68 77 }
69 78
79 Variable::Variable(const Variable &other)
80 : impl{spimpl::make_unique_impl<VariablePrivate>(*other.impl)}
81 {
82 }
83
84 std::shared_ptr<Variable> Variable::clone() const
85 {
86 return std::make_shared<Variable>(*this);
87 }
88
70 89 QString Variable::name() const noexcept
71 90 {
72 91 impl->lockRead();
73 92 auto name = impl->m_Name;
74 93 impl->unlock();
75 94 return name;
76 95 }
77 96
78 97 void Variable::setName(const QString &name) noexcept
79 98 {
80 99 impl->lockWrite();
81 100 impl->m_Name = name;
82 101 impl->unlock();
83 102 }
84 103
85 104 SqpRange Variable::range() const noexcept
86 105 {
87 106 impl->lockRead();
88 107 auto range = impl->m_Range;
89 108 impl->unlock();
90 109 return range;
91 110 }
92 111
93 112 void Variable::setRange(const SqpRange &range) noexcept
94 113 {
95 114 impl->lockWrite();
96 115 impl->m_Range = range;
97 116 impl->updateRealRange();
98 117 impl->unlock();
99 118 }
100 119
101 120 SqpRange Variable::cacheRange() const noexcept
102 121 {
103 122 impl->lockRead();
104 123 auto cacheRange = impl->m_CacheRange;
105 124 impl->unlock();
106 125 return cacheRange;
107 126 }
108 127
109 128 void Variable::setCacheRange(const SqpRange &cacheRange) noexcept
110 129 {
111 130 impl->lockWrite();
112 131 if (cacheRange != impl->m_CacheRange) {
113 132 impl->m_CacheRange = cacheRange;
114 133 impl->purgeDataSeries();
115 134 }
116 135 impl->unlock();
117 136 }
118 137
119 138 SqpRange Variable::realRange() const noexcept
120 139 {
121 140 return impl->m_RealRange;
122 141 }
123 142
124 143 void Variable::mergeDataSeries(std::shared_ptr<IDataSeries> dataSeries) noexcept
125 144 {
126 145 qCDebug(LOG_Variable()) << "TORM Variable::mergeDataSeries"
127 146 << QThread::currentThread()->objectName();
128 147 if (!dataSeries) {
129 148 /// @todo ALX : log
130 149 return;
131 150 }
132 151
133 152 // Add or merge the data
134 153 impl->lockWrite();
135 154 if (!impl->m_DataSeries) {
136 155 impl->m_DataSeries = dataSeries->clone();
137 156 }
138 157 else {
139 158 impl->m_DataSeries->merge(dataSeries.get());
140 159 }
141 160 impl->purgeDataSeries();
142 161 impl->unlock();
143 162 }
144 163
145 164 std::shared_ptr<IDataSeries> Variable::dataSeries() const noexcept
146 165 {
147 166 impl->lockRead();
148 167 auto dataSeries = impl->m_DataSeries;
149 168 impl->unlock();
150 169
151 170 return dataSeries;
152 171 }
153 172
154 173 QVariantHash Variable::metadata() const noexcept
155 174 {
156 175 impl->lockRead();
157 176 auto metadata = impl->m_Metadata;
158 177 impl->unlock();
159 178 return metadata;
160 179 }
161 180
162 181 bool Variable::contains(const SqpRange &range) const noexcept
163 182 {
164 183 impl->lockRead();
165 184 auto res = impl->m_Range.contains(range);
166 185 impl->unlock();
167 186 return res;
168 187 }
169 188
170 189 bool Variable::intersect(const SqpRange &range) const noexcept
171 190 {
172 191
173 192 impl->lockRead();
174 193 auto res = impl->m_Range.intersect(range);
175 194 impl->unlock();
176 195 return res;
177 196 }
178 197
179 198 bool Variable::isInside(const SqpRange &range) const noexcept
180 199 {
181 200 impl->lockRead();
182 201 auto res = range.contains(SqpRange{impl->m_Range.m_TStart, impl->m_Range.m_TEnd});
183 202 impl->unlock();
184 203 return res;
185 204 }
186 205
187 206 bool Variable::cacheContains(const SqpRange &range) const noexcept
188 207 {
189 208 impl->lockRead();
190 209 auto res = impl->m_CacheRange.contains(range);
191 210 impl->unlock();
192 211 return res;
193 212 }
194 213
195 214 bool Variable::cacheIntersect(const SqpRange &range) const noexcept
196 215 {
197 216 impl->lockRead();
198 217 auto res = impl->m_CacheRange.intersect(range);
199 218 impl->unlock();
200 219 return res;
201 220 }
202 221
203 222 bool Variable::cacheIsInside(const SqpRange &range) const noexcept
204 223 {
205 224 impl->lockRead();
206 225 auto res = range.contains(SqpRange{impl->m_CacheRange.m_TStart, impl->m_CacheRange.m_TEnd});
207 226 impl->unlock();
208 227 return res;
209 228 }
210 229
211 230
212 231 QVector<SqpRange> Variable::provideNotInCacheRangeList(const SqpRange &range) const noexcept
213 232 {
214 233 // This code assume that cach in contigue. Can return 0, 1 or 2 SqpRange
215 234
216 235 auto notInCache = QVector<SqpRange>{};
217 236
218 237 if (!this->cacheContains(range)) {
219 238 if (range.m_TEnd <= impl->m_CacheRange.m_TStart
220 239 || range.m_TStart >= impl->m_CacheRange.m_TEnd) {
221 240 notInCache << range;
222 241 }
223 242 else if (range.m_TStart < impl->m_CacheRange.m_TStart
224 243 && range.m_TEnd <= impl->m_CacheRange.m_TEnd) {
225 244 notInCache << SqpRange{range.m_TStart, impl->m_CacheRange.m_TStart};
226 245 }
227 246 else if (range.m_TStart < impl->m_CacheRange.m_TStart
228 247 && range.m_TEnd > impl->m_CacheRange.m_TEnd) {
229 248 notInCache << SqpRange{range.m_TStart, impl->m_CacheRange.m_TStart}
230 249 << SqpRange{impl->m_CacheRange.m_TEnd, range.m_TEnd};
231 250 }
232 251 else if (range.m_TStart < impl->m_CacheRange.m_TEnd) {
233 252 notInCache << SqpRange{impl->m_CacheRange.m_TEnd, range.m_TEnd};
234 253 }
235 254 else {
236 255 qCCritical(LOG_Variable()) << tr("Detection of unknown case.")
237 256 << QThread::currentThread();
238 257 }
239 258 }
240 259
241 260 return notInCache;
242 261 }
243 262
244 263 QVector<SqpRange> Variable::provideInCacheRangeList(const SqpRange &range) const noexcept
245 264 {
246 265 // This code assume that cach in contigue. Can return 0 or 1 SqpRange
247 266
248 267 auto inCache = QVector<SqpRange>{};
249 268
250 269
251 270 if (this->intersect(range)) {
252 271 if (range.m_TStart <= impl->m_CacheRange.m_TStart
253 272 && range.m_TEnd >= impl->m_CacheRange.m_TStart
254 273 && range.m_TEnd < impl->m_CacheRange.m_TEnd) {
255 274 inCache << SqpRange{impl->m_CacheRange.m_TStart, range.m_TEnd};
256 275 }
257 276
258 277 else if (range.m_TStart >= impl->m_CacheRange.m_TStart
259 278 && range.m_TEnd <= impl->m_CacheRange.m_TEnd) {
260 279 inCache << range;
261 280 }
262 281 else if (range.m_TStart > impl->m_CacheRange.m_TStart
263 282 && range.m_TEnd > impl->m_CacheRange.m_TEnd) {
264 283 inCache << SqpRange{range.m_TStart, impl->m_CacheRange.m_TEnd};
265 284 }
266 285 else if (range.m_TStart <= impl->m_CacheRange.m_TStart
267 286 && range.m_TEnd >= impl->m_CacheRange.m_TEnd) {
268 287 inCache << impl->m_CacheRange;
269 288 }
270 289 else {
271 290 qCCritical(LOG_Variable()) << tr("Detection of unknown case.")
272 291 << QThread::currentThread();
273 292 }
274 293 }
275 294
276 295 return inCache;
277 296 }
@@ -1,743 +1,775
1 1 #include <Variable/Variable.h>
2 2 #include <Variable/VariableAcquisitionWorker.h>
3 3 #include <Variable/VariableCacheStrategy.h>
4 4 #include <Variable/VariableController.h>
5 5 #include <Variable/VariableModel.h>
6 6 #include <Variable/VariableSynchronizationGroup.h>
7 7
8 8 #include <Data/DataProviderParameters.h>
9 9 #include <Data/IDataProvider.h>
10 10 #include <Data/IDataSeries.h>
11 11 #include <Data/VariableRequest.h>
12 12 #include <Time/TimeController.h>
13 13
14 14 #include <QMutex>
15 15 #include <QThread>
16 16 #include <QUuid>
17 17 #include <QtCore/QItemSelectionModel>
18 18
19 19 #include <deque>
20 20 #include <set>
21 21 #include <unordered_map>
22 22
23 23 Q_LOGGING_CATEGORY(LOG_VariableController, "VariableController")
24 24
25 25 namespace {
26 26
27 27 SqpRange computeSynchroRangeRequested(const SqpRange &varRange, const SqpRange &graphRange,
28 28 const SqpRange &oldGraphRange)
29 29 {
30 30 auto zoomType = VariableController::getZoomType(graphRange, oldGraphRange);
31 31
32 32 auto varRangeRequested = varRange;
33 33 switch (zoomType) {
34 34 case AcquisitionZoomType::ZoomIn: {
35 35 auto deltaLeft = graphRange.m_TStart - oldGraphRange.m_TStart;
36 36 auto deltaRight = oldGraphRange.m_TEnd - graphRange.m_TEnd;
37 37 varRangeRequested.m_TStart += deltaLeft;
38 38 varRangeRequested.m_TEnd -= deltaRight;
39 39 break;
40 40 }
41 41
42 42 case AcquisitionZoomType::ZoomOut: {
43 43 auto deltaLeft = oldGraphRange.m_TStart - graphRange.m_TStart;
44 44 auto deltaRight = graphRange.m_TEnd - oldGraphRange.m_TEnd;
45 45 varRangeRequested.m_TStart -= deltaLeft;
46 46 varRangeRequested.m_TEnd += deltaRight;
47 47 break;
48 48 }
49 49 case AcquisitionZoomType::PanRight: {
50 50 auto deltaRight = graphRange.m_TEnd - oldGraphRange.m_TEnd;
51 51 varRangeRequested.m_TStart += deltaRight;
52 52 varRangeRequested.m_TEnd += deltaRight;
53 53 break;
54 54 }
55 55 case AcquisitionZoomType::PanLeft: {
56 56 auto deltaLeft = oldGraphRange.m_TStart - graphRange.m_TStart;
57 57 varRangeRequested.m_TStart -= deltaLeft;
58 58 varRangeRequested.m_TEnd -= deltaLeft;
59 59 break;
60 60 }
61 61 case AcquisitionZoomType::Unknown: {
62 62 qCCritical(LOG_VariableController())
63 63 << VariableController::tr("Impossible to synchronize: zoom type unknown");
64 64 break;
65 65 }
66 66 default:
67 67 qCCritical(LOG_VariableController()) << VariableController::tr(
68 68 "Impossible to synchronize: zoom type not take into account");
69 69 // No action
70 70 break;
71 71 }
72 72
73 73 return varRangeRequested;
74 74 }
75 75 }
76 76
77 77 struct VariableController::VariableControllerPrivate {
78 78 explicit VariableControllerPrivate(VariableController *parent)
79 79 : m_WorkingMutex{},
80 80 m_VariableModel{new VariableModel{parent}},
81 81 m_VariableSelectionModel{new QItemSelectionModel{m_VariableModel, parent}},
82 82 m_VariableCacheStrategy{std::make_unique<VariableCacheStrategy>()},
83 83 m_VariableAcquisitionWorker{std::make_unique<VariableAcquisitionWorker>()},
84 84 q{parent}
85 85 {
86 86
87 87 m_VariableAcquisitionWorker->moveToThread(&m_VariableAcquisitionWorkerThread);
88 88 m_VariableAcquisitionWorkerThread.setObjectName("VariableAcquisitionWorkerThread");
89 89 }
90 90
91 91
92 92 virtual ~VariableControllerPrivate()
93 93 {
94 94 qCDebug(LOG_VariableController()) << tr("VariableControllerPrivate destruction");
95 95 m_VariableAcquisitionWorkerThread.quit();
96 96 m_VariableAcquisitionWorkerThread.wait();
97 97 }
98 98
99 99
100 100 void processRequest(std::shared_ptr<Variable> var, const SqpRange &rangeRequested,
101 101 QUuid varRequestId);
102 102
103 103 QVector<SqpRange> provideNotInCacheDateTimeList(std::shared_ptr<Variable> variable,
104 104 const SqpRange &dateTime);
105 105
106 106 std::shared_ptr<Variable> findVariable(QUuid vIdentifier);
107 107 std::shared_ptr<IDataSeries>
108 108 retrieveDataSeries(const QVector<AcquisitionDataPacket> acqDataPacketVector);
109 109
110 110 void registerProvider(std::shared_ptr<IDataProvider> provider);
111 111
112 112 void storeVariableRequest(QUuid varId, QUuid varRequestId, const VariableRequest &varRequest);
113 113 QUuid acceptVariableRequest(QUuid varId, std::shared_ptr<IDataSeries> dataSeries);
114 114 void updateVariableRequest(QUuid varRequestId);
115 115 void cancelVariableRequest(QUuid varRequestId);
116 116
117 117 QMutex m_WorkingMutex;
118 118 /// Variable model. The VariableController has the ownership
119 119 VariableModel *m_VariableModel;
120 120 QItemSelectionModel *m_VariableSelectionModel;
121 121
122 122
123 123 TimeController *m_TimeController{nullptr};
124 124 std::unique_ptr<VariableCacheStrategy> m_VariableCacheStrategy;
125 125 std::unique_ptr<VariableAcquisitionWorker> m_VariableAcquisitionWorker;
126 126 QThread m_VariableAcquisitionWorkerThread;
127 127
128 128 std::unordered_map<std::shared_ptr<Variable>, std::shared_ptr<IDataProvider> >
129 129 m_VariableToProviderMap;
130 130 std::unordered_map<std::shared_ptr<Variable>, QUuid> m_VariableToIdentifierMap;
131 131 std::map<QUuid, std::shared_ptr<VariableSynchronizationGroup> >
132 132 m_GroupIdToVariableSynchronizationGroupMap;
133 133 std::map<QUuid, QUuid> m_VariableIdGroupIdMap;
134 134 std::set<std::shared_ptr<IDataProvider> > m_ProviderSet;
135 135
136 136 std::map<QUuid, std::map<QUuid, VariableRequest> > m_VarRequestIdToVarIdVarRequestMap;
137 137
138 138 std::map<QUuid, std::deque<QUuid> > m_VarIdToVarRequestIdQueueMap;
139 139
140 140
141 141 VariableController *q;
142 142 };
143 143
144 144
145 145 VariableController::VariableController(QObject *parent)
146 146 : QObject{parent}, impl{spimpl::make_unique_impl<VariableControllerPrivate>(this)}
147 147 {
148 148 qCDebug(LOG_VariableController()) << tr("VariableController construction")
149 149 << QThread::currentThread();
150 150
151 151 connect(impl->m_VariableModel, &VariableModel::abortProgessRequested, this,
152 152 &VariableController::onAbortProgressRequested);
153 153
154 154 connect(impl->m_VariableAcquisitionWorker.get(), &VariableAcquisitionWorker::dataProvided, this,
155 155 &VariableController::onDataProvided);
156 156 connect(impl->m_VariableAcquisitionWorker.get(),
157 157 &VariableAcquisitionWorker::variableRequestInProgress, this,
158 158 &VariableController::onVariableRetrieveDataInProgress);
159 159
160 160 connect(&impl->m_VariableAcquisitionWorkerThread, &QThread::started,
161 161 impl->m_VariableAcquisitionWorker.get(), &VariableAcquisitionWorker::initialize);
162 162 connect(&impl->m_VariableAcquisitionWorkerThread, &QThread::finished,
163 163 impl->m_VariableAcquisitionWorker.get(), &VariableAcquisitionWorker::finalize);
164 164
165 165
166 166 impl->m_VariableAcquisitionWorkerThread.start();
167 167 }
168 168
169 169 VariableController::~VariableController()
170 170 {
171 171 qCDebug(LOG_VariableController()) << tr("VariableController destruction")
172 172 << QThread::currentThread();
173 173 this->waitForFinish();
174 174 }
175 175
176 176 VariableModel *VariableController::variableModel() noexcept
177 177 {
178 178 return impl->m_VariableModel;
179 179 }
180 180
181 181 QItemSelectionModel *VariableController::variableSelectionModel() noexcept
182 182 {
183 183 return impl->m_VariableSelectionModel;
184 184 }
185 185
186 186 void VariableController::setTimeController(TimeController *timeController) noexcept
187 187 {
188 188 impl->m_TimeController = timeController;
189 189 }
190 190
191 std::shared_ptr<Variable>
192 VariableController::cloneVariable(std::shared_ptr<Variable> variable) noexcept
193 {
194 if (impl->m_VariableModel->containsVariable(variable)) {
195 // Clones variable
196 auto duplicate = variable->clone();
197
198 // Adds clone to model
199 impl->m_VariableModel->addVariable(duplicate);
200
201 // Generates clone identifier
202 impl->m_VariableToIdentifierMap[duplicate] = QUuid::createUuid();
203
204 // Registers provider
205 auto variableProvider = impl->m_VariableToProviderMap.at(variable);
206 auto duplicateProvider = variableProvider != nullptr ? variableProvider->clone() : nullptr;
207
208 impl->m_VariableToProviderMap[duplicate] = duplicateProvider;
209 if (duplicateProvider) {
210 impl->registerProvider(duplicateProvider);
211 }
212
213 return duplicate;
214 }
215 else {
216 qCCritical(LOG_VariableController())
217 << tr("Can't create duplicate of variable %1: variable not registered in the model")
218 .arg(variable->name());
219 return nullptr;
220 }
221 }
222
191 223 void VariableController::deleteVariable(std::shared_ptr<Variable> variable) noexcept
192 224 {
193 225 if (!variable) {
194 226 qCCritical(LOG_VariableController()) << "Can't delete variable: variable is null";
195 227 return;
196 228 }
197 229
198 230 // Spreads in SciQlop that the variable will be deleted, so that potential receivers can
199 231 // make some treatments before the deletion
200 232 emit variableAboutToBeDeleted(variable);
201 233
202 234 // Deletes identifier
203 235 impl->m_VariableToIdentifierMap.erase(variable);
204 236
205 237 // Deletes provider
206 238 auto nbProvidersDeleted = impl->m_VariableToProviderMap.erase(variable);
207 239 qCDebug(LOG_VariableController())
208 240 << tr("Number of providers deleted for variable %1: %2")
209 241 .arg(variable->name(), QString::number(nbProvidersDeleted));
210 242
211 243
212 244 // Deletes from model
213 245 impl->m_VariableModel->deleteVariable(variable);
214 246 }
215 247
216 248 void VariableController::deleteVariables(
217 249 const QVector<std::shared_ptr<Variable> > &variables) noexcept
218 250 {
219 251 for (auto variable : qAsConst(variables)) {
220 252 deleteVariable(variable);
221 253 }
222 254 }
223 255
224 256 void VariableController::abortProgress(std::shared_ptr<Variable> variable)
225 257 {
226 258 }
227 259
228 260 std::shared_ptr<Variable>
229 261 VariableController::createVariable(const QString &name, const QVariantHash &metadata,
230 262 std::shared_ptr<IDataProvider> provider) noexcept
231 263 {
232 264 if (!impl->m_TimeController) {
233 265 qCCritical(LOG_VariableController())
234 266 << tr("Impossible to create variable: The time controller is null");
235 267 return nullptr;
236 268 }
237 269
238 270 auto range = impl->m_TimeController->dateTime();
239 271
240 272 if (auto newVariable = impl->m_VariableModel->createVariable(name, range, metadata)) {
241 273 auto identifier = QUuid::createUuid();
242 274
243 275 // store the provider
244 276 impl->registerProvider(provider);
245 277
246 278 // Associate the provider
247 279 impl->m_VariableToProviderMap[newVariable] = provider;
248 280 impl->m_VariableToIdentifierMap[newVariable] = identifier;
249 281
250 282
251 283 auto varRequestId = QUuid::createUuid();
252 284 qCInfo(LOG_VariableController()) << "processRequest for" << name << varRequestId;
253 285 impl->processRequest(newVariable, range, varRequestId);
254 286 impl->updateVariableRequest(varRequestId);
255 287
256 288 return newVariable;
257 289 }
258 290 }
259 291
260 292 void VariableController::onDateTimeOnSelection(const SqpRange &dateTime)
261 293 {
262 294 // TODO check synchronisation and Rescale
263 295 qCDebug(LOG_VariableController()) << "VariableController::onDateTimeOnSelection"
264 296 << QThread::currentThread()->objectName();
265 297 auto selectedRows = impl->m_VariableSelectionModel->selectedRows();
266 298 auto varRequestId = QUuid::createUuid();
267 299
268 300 for (const auto &selectedRow : qAsConst(selectedRows)) {
269 301 if (auto selectedVariable = impl->m_VariableModel->variable(selectedRow.row())) {
270 302 selectedVariable->setRange(dateTime);
271 303 impl->processRequest(selectedVariable, dateTime, varRequestId);
272 304
273 305 // notify that rescale operation has to be done
274 306 emit rangeChanged(selectedVariable, dateTime);
275 307 }
276 308 }
277 309 impl->updateVariableRequest(varRequestId);
278 310 }
279 311
280 312 void VariableController::onDataProvided(QUuid vIdentifier, const SqpRange &rangeRequested,
281 313 const SqpRange &cacheRangeRequested,
282 314 QVector<AcquisitionDataPacket> dataAcquired)
283 315 {
284 316 auto retrievedDataSeries = impl->retrieveDataSeries(dataAcquired);
285 317 auto varRequestId = impl->acceptVariableRequest(vIdentifier, retrievedDataSeries);
286 318 if (!varRequestId.isNull()) {
287 319 impl->updateVariableRequest(varRequestId);
288 320 }
289 321 }
290 322
291 323 void VariableController::onVariableRetrieveDataInProgress(QUuid identifier, double progress)
292 324 {
293 325 if (auto var = impl->findVariable(identifier)) {
294 326 impl->m_VariableModel->setDataProgress(var, progress);
295 327 }
296 328 else {
297 329 qCCritical(LOG_VariableController())
298 330 << tr("Impossible to notify progression of a null variable");
299 331 }
300 332 }
301 333
302 334 void VariableController::onAbortProgressRequested(std::shared_ptr<Variable> variable)
303 335 {
304 336 qCDebug(LOG_VariableController()) << "TORM: VariableController::onAbortProgressRequested"
305 337 << QThread::currentThread()->objectName();
306 338
307 339 auto it = impl->m_VariableToIdentifierMap.find(variable);
308 340 if (it != impl->m_VariableToIdentifierMap.cend()) {
309 341 impl->m_VariableToProviderMap.at(variable)->requestDataAborting(it->second);
310 342 }
311 343 else {
312 344 qCWarning(LOG_VariableController())
313 345 << tr("Aborting progression of inexistant variable detected !!!")
314 346 << QThread::currentThread()->objectName();
315 347 }
316 348 }
317 349
318 350 void VariableController::onAddSynchronizationGroupId(QUuid synchronizationGroupId)
319 351 {
320 352 qCDebug(LOG_VariableController()) << "TORM: VariableController::onAddSynchronizationGroupId"
321 353 << QThread::currentThread()->objectName()
322 354 << synchronizationGroupId;
323 355 auto vSynchroGroup = std::make_shared<VariableSynchronizationGroup>();
324 356 impl->m_GroupIdToVariableSynchronizationGroupMap.insert(
325 357 std::make_pair(synchronizationGroupId, vSynchroGroup));
326 358 }
327 359
328 360 void VariableController::onRemoveSynchronizationGroupId(QUuid synchronizationGroupId)
329 361 {
330 362 impl->m_GroupIdToVariableSynchronizationGroupMap.erase(synchronizationGroupId);
331 363 }
332 364
333 365 void VariableController::onAddSynchronized(std::shared_ptr<Variable> variable,
334 366 QUuid synchronizationGroupId)
335 367
336 368 {
337 369 qCDebug(LOG_VariableController()) << "TORM: VariableController::onAddSynchronized"
338 370 << synchronizationGroupId;
339 371 auto varToVarIdIt = impl->m_VariableToIdentifierMap.find(variable);
340 372 if (varToVarIdIt != impl->m_VariableToIdentifierMap.cend()) {
341 373 auto groupIdToVSGIt
342 374 = impl->m_GroupIdToVariableSynchronizationGroupMap.find(synchronizationGroupId);
343 375 if (groupIdToVSGIt != impl->m_GroupIdToVariableSynchronizationGroupMap.cend()) {
344 376 impl->m_VariableIdGroupIdMap.insert(
345 377 std::make_pair(varToVarIdIt->second, synchronizationGroupId));
346 378 groupIdToVSGIt->second->addVariableId(varToVarIdIt->second);
347 379 }
348 380 else {
349 381 qCCritical(LOG_VariableController())
350 382 << tr("Impossible to synchronize a variable with an unknown sycnhronization group")
351 383 << variable->name();
352 384 }
353 385 }
354 386 else {
355 387 qCCritical(LOG_VariableController())
356 388 << tr("Impossible to synchronize a variable with no identifier") << variable->name();
357 389 }
358 390 }
359 391
360 392
361 393 void VariableController::onRequestDataLoading(QVector<std::shared_ptr<Variable> > variables,
362 394 const SqpRange &range, const SqpRange &oldRange,
363 395 bool synchronise)
364 396 {
365 397 // NOTE: oldRange isn't really necessary since oldRange == variable->range().
366 398
367 399 // we want to load data of the variable for the dateTime.
368 400 // First we check if the cache contains some of them.
369 401 // For the other, we ask the provider to give them.
370 402
371 403 auto varRequestId = QUuid::createUuid();
372 404 qCInfo(LOG_VariableController()) << "VariableController::onRequestDataLoading"
373 405 << QThread::currentThread()->objectName() << varRequestId;
374 406
375 407 for (const auto &var : variables) {
376 408 qCDebug(LOG_VariableController()) << "processRequest for" << var->name() << varRequestId;
377 409 impl->processRequest(var, range, varRequestId);
378 410 }
379 411
380 412 if (synchronise) {
381 413 // Get the group ids
382 414 qCDebug(LOG_VariableController())
383 415 << "TORM VariableController::onRequestDataLoading for synchro var ENABLE";
384 416 auto groupIds = std::set<QUuid>{};
385 417 auto groupIdToOldRangeMap = std::map<QUuid, SqpRange>{};
386 418 for (const auto &var : variables) {
387 419 auto varToVarIdIt = impl->m_VariableToIdentifierMap.find(var);
388 420 if (varToVarIdIt != impl->m_VariableToIdentifierMap.cend()) {
389 421 auto vId = varToVarIdIt->second;
390 422 auto varIdToGroupIdIt = impl->m_VariableIdGroupIdMap.find(vId);
391 423 if (varIdToGroupIdIt != impl->m_VariableIdGroupIdMap.cend()) {
392 424 auto gId = varIdToGroupIdIt->second;
393 425 groupIdToOldRangeMap.insert(std::make_pair(gId, var->range()));
394 426 if (groupIds.find(gId) == groupIds.cend()) {
395 427 qCDebug(LOG_VariableController()) << "Synchro detect group " << gId;
396 428 groupIds.insert(gId);
397 429 }
398 430 }
399 431 }
400 432 }
401 433
402 434 // We assume here all group ids exist
403 435 for (const auto &gId : groupIds) {
404 436 auto vSynchronizationGroup = impl->m_GroupIdToVariableSynchronizationGroupMap.at(gId);
405 437 auto vSyncIds = vSynchronizationGroup->getIds();
406 438 qCDebug(LOG_VariableController()) << "Var in synchro group ";
407 439 for (auto vId : vSyncIds) {
408 440 auto var = impl->findVariable(vId);
409 441
410 442 // Don't process already processed var
411 443 if (!variables.contains(var)) {
412 444 if (var != nullptr) {
413 445 qCDebug(LOG_VariableController()) << "processRequest synchro for"
414 446 << var->name();
415 447 auto vSyncRangeRequested = computeSynchroRangeRequested(
416 448 var->range(), range, groupIdToOldRangeMap.at(gId));
417 449 qCDebug(LOG_VariableController()) << "synchro RR" << vSyncRangeRequested;
418 450 impl->processRequest(var, vSyncRangeRequested, varRequestId);
419 451 }
420 452 else {
421 453 qCCritical(LOG_VariableController())
422 454
423 455 << tr("Impossible to synchronize a null variable");
424 456 }
425 457 }
426 458 }
427 459 }
428 460 }
429 461
430 462 impl->updateVariableRequest(varRequestId);
431 463 }
432 464
433 465
434 466 void VariableController::initialize()
435 467 {
436 468 qCDebug(LOG_VariableController()) << tr("VariableController init") << QThread::currentThread();
437 469 impl->m_WorkingMutex.lock();
438 470 qCDebug(LOG_VariableController()) << tr("VariableController init END");
439 471 }
440 472
441 473 void VariableController::finalize()
442 474 {
443 475 impl->m_WorkingMutex.unlock();
444 476 }
445 477
446 478 void VariableController::waitForFinish()
447 479 {
448 480 QMutexLocker locker{&impl->m_WorkingMutex};
449 481 }
450 482
451 483 AcquisitionZoomType VariableController::getZoomType(const SqpRange &range, const SqpRange &oldRange)
452 484 {
453 485 // t1.m_TStart <= t2.m_TStart && t2.m_TEnd <= t1.m_TEnd
454 486 auto zoomType = AcquisitionZoomType::Unknown;
455 487 if (range.m_TStart <= oldRange.m_TStart && oldRange.m_TEnd <= range.m_TEnd) {
456 488 zoomType = AcquisitionZoomType::ZoomOut;
457 489 }
458 490 else if (range.m_TStart > oldRange.m_TStart && range.m_TEnd > oldRange.m_TEnd) {
459 491 zoomType = AcquisitionZoomType::PanRight;
460 492 }
461 493 else if (range.m_TStart < oldRange.m_TStart && range.m_TEnd < oldRange.m_TEnd) {
462 494 zoomType = AcquisitionZoomType::PanLeft;
463 495 }
464 496 else if (range.m_TStart > oldRange.m_TStart && oldRange.m_TEnd > range.m_TEnd) {
465 497 zoomType = AcquisitionZoomType::ZoomIn;
466 498 }
467 499 else {
468 500 qCCritical(LOG_VariableController()) << "getZoomType: Unknown type detected";
469 501 }
470 502 return zoomType;
471 503 }
472 504
473 505 void VariableController::VariableControllerPrivate::processRequest(std::shared_ptr<Variable> var,
474 506 const SqpRange &rangeRequested,
475 507 QUuid varRequestId)
476 508 {
477 509
478 510 // TODO: protect at
479 511 auto varRequest = VariableRequest{};
480 512 auto varId = m_VariableToIdentifierMap.at(var);
481 513
482 514 auto varStrategyRangesRequested
483 515 = m_VariableCacheStrategy->computeStrategyRanges(var->range(), rangeRequested);
484 516 auto notInCacheRangeList = var->provideNotInCacheRangeList(varStrategyRangesRequested.second);
485 517 auto inCacheRangeList = var->provideInCacheRangeList(varStrategyRangesRequested.second);
486 518
487 519 if (!notInCacheRangeList.empty()) {
488 520 varRequest.m_RangeRequested = varStrategyRangesRequested.first;
489 521 varRequest.m_CacheRangeRequested = varStrategyRangesRequested.second;
490 522 qCDebug(LOG_VariableAcquisitionWorker()) << tr("TORM processRequest RR ") << rangeRequested;
491 523 qCDebug(LOG_VariableAcquisitionWorker()) << tr("TORM processRequest R ")
492 524 << varStrategyRangesRequested.first;
493 525 qCDebug(LOG_VariableAcquisitionWorker()) << tr("TORM processRequest CR ")
494 526 << varStrategyRangesRequested.second;
495 527 // store VarRequest
496 528 storeVariableRequest(varId, varRequestId, varRequest);
497 529
498 530 auto varProvider = m_VariableToProviderMap.at(var);
499 531 if (varProvider != nullptr) {
500 532 auto varRequestIdCanceled = m_VariableAcquisitionWorker->pushVariableRequest(
501 533 varRequestId, varId, varStrategyRangesRequested.first,
502 534 varStrategyRangesRequested.second,
503 535 DataProviderParameters{std::move(notInCacheRangeList), var->metadata()},
504 536 varProvider);
505 537
506 538 if (!varRequestIdCanceled.isNull()) {
507 539 qCInfo(LOG_VariableAcquisitionWorker()) << tr("varRequestIdCanceled: ")
508 540 << varRequestIdCanceled;
509 541 cancelVariableRequest(varRequestIdCanceled);
510 542 }
511 543 }
512 544 else {
513 545 qCCritical(LOG_VariableController())
514 546 << "Impossible to provide data with a null provider";
515 547 }
516 548
517 549 if (!inCacheRangeList.empty()) {
518 550 emit q->updateVarDisplaying(var, inCacheRangeList.first());
519 551 }
520 552 }
521 553 else {
522 554
523 555 varRequest.m_RangeRequested = varStrategyRangesRequested.first;
524 556 varRequest.m_CacheRangeRequested = varStrategyRangesRequested.second;
525 557 // store VarRequest
526 558 storeVariableRequest(varId, varRequestId, varRequest);
527 559 acceptVariableRequest(varId,
528 560 var->dataSeries()->subDataSeries(varStrategyRangesRequested.second));
529 561 }
530 562 }
531 563
532 564 std::shared_ptr<Variable>
533 565 VariableController::VariableControllerPrivate::findVariable(QUuid vIdentifier)
534 566 {
535 567 std::shared_ptr<Variable> var;
536 568 auto findReply = [vIdentifier](const auto &entry) { return vIdentifier == entry.second; };
537 569
538 570 auto end = m_VariableToIdentifierMap.cend();
539 571 auto it = std::find_if(m_VariableToIdentifierMap.cbegin(), end, findReply);
540 572 if (it != end) {
541 573 var = it->first;
542 574 }
543 575 else {
544 576 qCCritical(LOG_VariableController())
545 577 << tr("Impossible to find the variable with the identifier: ") << vIdentifier;
546 578 }
547 579
548 580 return var;
549 581 }
550 582
551 583 std::shared_ptr<IDataSeries> VariableController::VariableControllerPrivate::retrieveDataSeries(
552 584 const QVector<AcquisitionDataPacket> acqDataPacketVector)
553 585 {
554 586 qCDebug(LOG_VariableController()) << tr("TORM: retrieveDataSeries acqDataPacketVector size")
555 587 << acqDataPacketVector.size();
556 588 std::shared_ptr<IDataSeries> dataSeries;
557 589 if (!acqDataPacketVector.isEmpty()) {
558 590 dataSeries = acqDataPacketVector[0].m_DateSeries;
559 591 for (int i = 1; i < acqDataPacketVector.size(); ++i) {
560 592 dataSeries->merge(acqDataPacketVector[i].m_DateSeries.get());
561 593 }
562 594 }
563 595 qCDebug(LOG_VariableController()) << tr("TORM: retrieveDataSeries acqDataPacketVector size END")
564 596 << acqDataPacketVector.size();
565 597 return dataSeries;
566 598 }
567 599
568 600 void VariableController::VariableControllerPrivate::registerProvider(
569 601 std::shared_ptr<IDataProvider> provider)
570 602 {
571 603 if (m_ProviderSet.find(provider) == m_ProviderSet.end()) {
572 604 qCDebug(LOG_VariableController()) << tr("Registering of a new provider")
573 605 << provider->objectName();
574 606 m_ProviderSet.insert(provider);
575 607 connect(provider.get(), &IDataProvider::dataProvided, m_VariableAcquisitionWorker.get(),
576 608 &VariableAcquisitionWorker::onVariableDataAcquired);
577 609 connect(provider.get(), &IDataProvider::dataProvidedProgress,
578 610 m_VariableAcquisitionWorker.get(),
579 611 &VariableAcquisitionWorker::onVariableRetrieveDataInProgress);
580 612 }
581 613 else {
582 614 qCDebug(LOG_VariableController()) << tr("Cannot register provider, it already exists ");
583 615 }
584 616 }
585 617
586 618 void VariableController::VariableControllerPrivate::storeVariableRequest(
587 619 QUuid varId, QUuid varRequestId, const VariableRequest &varRequest)
588 620 {
589 621 // First request for the variable. we can create an entry for it
590 622 auto varIdToVarRequestIdQueueMapIt = m_VarIdToVarRequestIdQueueMap.find(varId);
591 623 if (varIdToVarRequestIdQueueMapIt == m_VarIdToVarRequestIdQueueMap.cend()) {
592 624 auto varRequestIdQueue = std::deque<QUuid>{};
593 625 qCDebug(LOG_VariableController()) << tr("Store REQUEST in QUEUE");
594 626 varRequestIdQueue.push_back(varRequestId);
595 627 m_VarIdToVarRequestIdQueueMap.insert(std::make_pair(varId, std::move(varRequestIdQueue)));
596 628 }
597 629 else {
598 630 qCDebug(LOG_VariableController()) << tr("Store REQUEST in EXISTING QUEUE");
599 631 auto &varRequestIdQueue = varIdToVarRequestIdQueueMapIt->second;
600 632 varRequestIdQueue.push_back(varRequestId);
601 633 }
602 634
603 635 auto varRequestIdToVarIdVarRequestMapIt = m_VarRequestIdToVarIdVarRequestMap.find(varRequestId);
604 636 if (varRequestIdToVarIdVarRequestMapIt == m_VarRequestIdToVarIdVarRequestMap.cend()) {
605 637 auto varIdToVarRequestMap = std::map<QUuid, VariableRequest>{};
606 638 varIdToVarRequestMap.insert(std::make_pair(varId, varRequest));
607 639 qCDebug(LOG_VariableController()) << tr("Store REQUESTID in MAP");
608 640 m_VarRequestIdToVarIdVarRequestMap.insert(
609 641 std::make_pair(varRequestId, std::move(varIdToVarRequestMap)));
610 642 }
611 643 else {
612 644 auto &varIdToVarRequestMap = varRequestIdToVarIdVarRequestMapIt->second;
613 645 qCDebug(LOG_VariableController()) << tr("Store REQUESTID in EXISTING MAP");
614 646 varIdToVarRequestMap.insert(std::make_pair(varId, varRequest));
615 647 }
616 648 }
617 649
618 650 QUuid VariableController::VariableControllerPrivate::acceptVariableRequest(
619 651 QUuid varId, std::shared_ptr<IDataSeries> dataSeries)
620 652 {
621 653 QUuid varRequestId;
622 654 auto varIdToVarRequestIdQueueMapIt = m_VarIdToVarRequestIdQueueMap.find(varId);
623 655 if (varIdToVarRequestIdQueueMapIt != m_VarIdToVarRequestIdQueueMap.cend()) {
624 656 auto &varRequestIdQueue = varIdToVarRequestIdQueueMapIt->second;
625 657 varRequestId = varRequestIdQueue.front();
626 658 auto varRequestIdToVarIdVarRequestMapIt
627 659 = m_VarRequestIdToVarIdVarRequestMap.find(varRequestId);
628 660 if (varRequestIdToVarIdVarRequestMapIt != m_VarRequestIdToVarIdVarRequestMap.cend()) {
629 661 auto &varIdToVarRequestMap = varRequestIdToVarIdVarRequestMapIt->second;
630 662 auto varIdToVarRequestMapIt = varIdToVarRequestMap.find(varId);
631 663 if (varIdToVarRequestMapIt != varIdToVarRequestMap.cend()) {
632 664 qCDebug(LOG_VariableController()) << tr("acceptVariableRequest");
633 665 auto &varRequest = varIdToVarRequestMapIt->second;
634 666 varRequest.m_DataSeries = dataSeries;
635 667 varRequest.m_CanUpdate = true;
636 668 }
637 669 else {
638 670 qCDebug(LOG_VariableController())
639 671 << tr("Impossible to acceptVariableRequest of a unknown variable id attached "
640 672 "to a variableRequestId")
641 673 << varRequestId << varId;
642 674 }
643 675 }
644 676 else {
645 677 qCCritical(LOG_VariableController())
646 678 << tr("Impossible to acceptVariableRequest of a unknown variableRequestId")
647 679 << varRequestId;
648 680 }
649 681
650 682 qCDebug(LOG_VariableController()) << tr("1: erase REQUEST in QUEUE ?")
651 683 << varRequestIdQueue.size();
652 684 varRequestIdQueue.pop_front();
653 685 qCDebug(LOG_VariableController()) << tr("2: erase REQUEST in QUEUE ?")
654 686 << varRequestIdQueue.size();
655 687 if (varRequestIdQueue.empty()) {
656 688 m_VarIdToVarRequestIdQueueMap.erase(varId);
657 689 }
658 690 }
659 691 else {
660 692 qCCritical(LOG_VariableController())
661 693 << tr("Impossible to acceptVariableRequest of a unknown variable id") << varId;
662 694 }
663 695
664 696 return varRequestId;
665 697 }
666 698
667 699 void VariableController::VariableControllerPrivate::updateVariableRequest(QUuid varRequestId)
668 700 {
669 701
670 702 auto varRequestIdToVarIdVarRequestMapIt = m_VarRequestIdToVarIdVarRequestMap.find(varRequestId);
671 703 if (varRequestIdToVarIdVarRequestMapIt != m_VarRequestIdToVarIdVarRequestMap.cend()) {
672 704 bool processVariableUpdate = true;
673 705 auto &varIdToVarRequestMap = varRequestIdToVarIdVarRequestMapIt->second;
674 706 for (auto varIdToVarRequestMapIt = varIdToVarRequestMap.cbegin();
675 707 (varIdToVarRequestMapIt != varIdToVarRequestMap.cend()) && processVariableUpdate;
676 708 ++varIdToVarRequestMapIt) {
677 709 processVariableUpdate &= varIdToVarRequestMapIt->second.m_CanUpdate;
678 710 qCDebug(LOG_VariableController()) << tr("updateVariableRequest")
679 711 << processVariableUpdate;
680 712 }
681 713
682 714 if (processVariableUpdate) {
683 715 for (auto varIdToVarRequestMapIt = varIdToVarRequestMap.cbegin();
684 716 varIdToVarRequestMapIt != varIdToVarRequestMap.cend(); ++varIdToVarRequestMapIt) {
685 717 if (auto var = findVariable(varIdToVarRequestMapIt->first)) {
686 718 auto &varRequest = varIdToVarRequestMapIt->second;
687 719 var->setRange(varRequest.m_RangeRequested);
688 720 var->setCacheRange(varRequest.m_CacheRangeRequested);
689 721 qCDebug(LOG_VariableController()) << tr("1: onDataProvided")
690 722 << varRequest.m_RangeRequested;
691 723 qCDebug(LOG_VariableController()) << tr("2: onDataProvided")
692 724 << varRequest.m_CacheRangeRequested;
693 725 var->mergeDataSeries(varRequest.m_DataSeries);
694 726 qCDebug(LOG_VariableController()) << tr("3: onDataProvided")
695 727 << varRequest.m_DataSeries->range();
696 728 qCDebug(LOG_VariableController()) << tr("4: onDataProvided");
697 729
698 730 /// @todo MPL: confirm
699 731 // Variable update is notified only if there is no pending request for it
700 732 if (m_VarIdToVarRequestIdQueueMap.count(varIdToVarRequestMapIt->first) == 0) {
701 733 emit var->updated();
702 734 }
703 735 }
704 736 else {
705 737 qCCritical(LOG_VariableController())
706 738 << tr("Impossible to update data to a null variable");
707 739 }
708 740 }
709 741
710 742 // cleaning varRequestId
711 743 qCDebug(LOG_VariableController()) << tr("0: erase REQUEST in MAP ?")
712 744 << m_VarRequestIdToVarIdVarRequestMap.size();
713 745 m_VarRequestIdToVarIdVarRequestMap.erase(varRequestId);
714 746 qCDebug(LOG_VariableController()) << tr("1: erase REQUEST in MAP ?")
715 747 << m_VarRequestIdToVarIdVarRequestMap.size();
716 748 }
717 749 }
718 750 else {
719 751 qCCritical(LOG_VariableController())
720 752 << tr("Cannot updateVariableRequest for a unknow varRequestId") << varRequestId;
721 753 }
722 754 }
723 755
724 756 void VariableController::VariableControllerPrivate::cancelVariableRequest(QUuid varRequestId)
725 757 {
726 758 // cleaning varRequestId
727 759 m_VarRequestIdToVarIdVarRequestMap.erase(varRequestId);
728 760
729 761 for (auto varIdToVarRequestIdQueueMapIt = m_VarIdToVarRequestIdQueueMap.begin();
730 762 varIdToVarRequestIdQueueMapIt != m_VarIdToVarRequestIdQueueMap.end();) {
731 763 auto &varRequestIdQueue = varIdToVarRequestIdQueueMapIt->second;
732 764 varRequestIdQueue.erase(
733 765 std::remove(varRequestIdQueue.begin(), varRequestIdQueue.end(), varRequestId),
734 766 varRequestIdQueue.end());
735 767 if (varRequestIdQueue.empty()) {
736 768 varIdToVarRequestIdQueueMapIt
737 769 = m_VarIdToVarRequestIdQueueMap.erase(varIdToVarRequestIdQueueMapIt);
738 770 }
739 771 else {
740 772 ++varIdToVarRequestIdQueueMapIt;
741 773 }
742 774 }
743 775 }
@@ -1,265 +1,290
1 1 #include <Variable/Variable.h>
2 2 #include <Variable/VariableModel.h>
3 3
4 4 #include <Common/DateUtils.h>
5 #include <Common/StringUtils.h>
5 6
6 7 #include <Data/IDataSeries.h>
7 8
8 9 #include <QSize>
9 10 #include <unordered_map>
10 11
11 12 Q_LOGGING_CATEGORY(LOG_VariableModel, "VariableModel")
12 13
13 14 namespace {
14 15
15 16 // Column indexes
16 17 const auto NAME_COLUMN = 0;
17 18 const auto TSTART_COLUMN = 1;
18 19 const auto TEND_COLUMN = 2;
19 20 const auto UNIT_COLUMN = 3;
20 21 const auto MISSION_COLUMN = 4;
21 22 const auto PLUGIN_COLUMN = 5;
22 23 const auto NB_COLUMNS = 6;
23 24
24 25 // Column properties
25 26 const auto DEFAULT_HEIGHT = 25;
26 27 const auto DEFAULT_WIDTH = 100;
27 28
28 29 struct ColumnProperties {
29 30 ColumnProperties(const QString &name = {}, int width = DEFAULT_WIDTH,
30 31 int height = DEFAULT_HEIGHT)
31 32 : m_Name{name}, m_Width{width}, m_Height{height}
32 33 {
33 34 }
34 35
35 36 QString m_Name;
36 37 int m_Width;
37 38 int m_Height;
38 39 };
39 40
40 41 const auto COLUMN_PROPERTIES = QHash<int, ColumnProperties>{
41 42 {NAME_COLUMN, {QObject::tr("Name")}}, {TSTART_COLUMN, {QObject::tr("tStart"), 180}},
42 43 {TEND_COLUMN, {QObject::tr("tEnd"), 180}}, {UNIT_COLUMN, {QObject::tr("Unit")}},
43 44 {MISSION_COLUMN, {QObject::tr("Mission")}}, {PLUGIN_COLUMN, {QObject::tr("Plugin")}}};
44 45
45 46 /// Format for datetimes
46 47 const auto DATETIME_FORMAT = QStringLiteral("dd/MM/yyyy \nhh:mm:ss:zzz");
47 48
49 QString uniqueName(const QString &defaultName,
50 const std::vector<std::shared_ptr<Variable> > &variables)
51 {
52 auto forbiddenNames = std::vector<QString>(variables.size());
53 std::transform(variables.cbegin(), variables.cend(), forbiddenNames.begin(),
54 [](const auto &variable) { return variable->name(); });
55 auto uniqueName = StringUtils::uniqueName(defaultName, forbiddenNames);
56 Q_ASSERT(!uniqueName.isEmpty());
57
58 return uniqueName;
59 }
48 60
49 61 } // namespace
50 62
51 63 struct VariableModel::VariableModelPrivate {
52 64 /// Variables created in SciQlop
53 65 std::vector<std::shared_ptr<Variable> > m_Variables;
54 66 std::unordered_map<std::shared_ptr<Variable>, double> m_VariableToProgress;
55 67
56 68 /// Return the row index of the variable. -1 if it's not found
57 69 int indexOfVariable(Variable *variable) const noexcept;
58 70 };
59 71
60 72 VariableModel::VariableModel(QObject *parent)
61 73 : QAbstractTableModel{parent}, impl{spimpl::make_unique_impl<VariableModelPrivate>()}
62 74 {
63 75 }
64 76
65 std::shared_ptr<Variable> VariableModel::createVariable(const QString &name,
66 const SqpRange &dateTime,
67 const QVariantHash &metadata) noexcept
77 void VariableModel::addVariable(std::shared_ptr<Variable> variable) noexcept
68 78 {
69 79 auto insertIndex = rowCount();
70 80 beginInsertRows({}, insertIndex, insertIndex);
71 81
72 auto variable = std::make_shared<Variable>(name, dateTime, metadata);
82 // Generates unique name for the variable
83 variable->setName(uniqueName(variable->name(), impl->m_Variables));
73 84
74 85 impl->m_Variables.push_back(variable);
75 86 connect(variable.get(), &Variable::updated, this, &VariableModel::onVariableUpdated);
76 87
77 88 endInsertRows();
89 }
90
91 bool VariableModel::containsVariable(std::shared_ptr<Variable> variable) const noexcept
92 {
93 auto end = impl->m_Variables.cend();
94 return std::find(impl->m_Variables.cbegin(), end, variable) != end;
95 }
96
97 std::shared_ptr<Variable> VariableModel::createVariable(const QString &name,
98 const SqpRange &dateTime,
99 const QVariantHash &metadata) noexcept
100 {
101 auto variable = std::make_shared<Variable>(name, dateTime, metadata);
102 addVariable(variable);
78 103
79 104 return variable;
80 105 }
81 106
82 107 void VariableModel::deleteVariable(std::shared_ptr<Variable> variable) noexcept
83 108 {
84 109 if (!variable) {
85 110 qCCritical(LOG_Variable()) << "Can't delete a null variable from the model";
86 111 return;
87 112 }
88 113
89 114 // Finds variable in the model
90 115 auto begin = impl->m_Variables.cbegin();
91 116 auto end = impl->m_Variables.cend();
92 117 auto it = std::find(begin, end, variable);
93 118 if (it != end) {
94 119 auto removeIndex = std::distance(begin, it);
95 120
96 121 // Deletes variable
97 122 beginRemoveRows({}, removeIndex, removeIndex);
98 123 impl->m_Variables.erase(it);
99 124 endRemoveRows();
100 125 }
101 126 else {
102 127 qCritical(LOG_VariableModel())
103 128 << tr("Can't delete variable %1 from the model: the variable is not in the model")
104 129 .arg(variable->name());
105 130 }
106 131
107 132 // Removes variable from progress map
108 133 impl->m_VariableToProgress.erase(variable);
109 134 }
110 135
111 136
112 137 std::shared_ptr<Variable> VariableModel::variable(int index) const
113 138 {
114 139 return (index >= 0 && index < impl->m_Variables.size()) ? impl->m_Variables[index] : nullptr;
115 140 }
116 141
117 142 std::vector<std::shared_ptr<Variable> > VariableModel::variables() const
118 143 {
119 144 return impl->m_Variables;
120 145 }
121 146
122 147 void VariableModel::setDataProgress(std::shared_ptr<Variable> variable, double progress)
123 148 {
124 149 if (progress > 0.0) {
125 150 impl->m_VariableToProgress[variable] = progress;
126 151 }
127 152 else {
128 153 impl->m_VariableToProgress.erase(variable);
129 154 }
130 155 auto modelIndex = createIndex(impl->indexOfVariable(variable.get()), NAME_COLUMN);
131 156
132 157 emit dataChanged(modelIndex, modelIndex);
133 158 }
134 159
135 160 int VariableModel::columnCount(const QModelIndex &parent) const
136 161 {
137 162 Q_UNUSED(parent);
138 163
139 164 return NB_COLUMNS;
140 165 }
141 166
142 167 int VariableModel::rowCount(const QModelIndex &parent) const
143 168 {
144 169 Q_UNUSED(parent);
145 170
146 171 return impl->m_Variables.size();
147 172 }
148 173
149 174 QVariant VariableModel::data(const QModelIndex &index, int role) const
150 175 {
151 176 if (!index.isValid()) {
152 177 return QVariant{};
153 178 }
154 179
155 180 if (index.row() < 0 || index.row() >= rowCount()) {
156 181 return QVariant{};
157 182 }
158 183
159 184 if (role == Qt::DisplayRole) {
160 185 if (auto variable = impl->m_Variables.at(index.row()).get()) {
161 186 switch (index.column()) {
162 187 case NAME_COLUMN:
163 188 return variable->name();
164 189 case TSTART_COLUMN: {
165 190 auto range = variable->realRange();
166 191 return range != INVALID_RANGE
167 192 ? DateUtils::dateTime(range.m_TStart).toString(DATETIME_FORMAT)
168 193 : QVariant{};
169 194 }
170 195 case TEND_COLUMN: {
171 196 auto range = variable->realRange();
172 197 return range != INVALID_RANGE
173 198 ? DateUtils::dateTime(range.m_TEnd).toString(DATETIME_FORMAT)
174 199 : QVariant{};
175 200 }
176 201 case UNIT_COLUMN:
177 202 return variable->metadata().value(QStringLiteral("units"));
178 203 case MISSION_COLUMN:
179 204 return variable->metadata().value(QStringLiteral("mission"));
180 205 case PLUGIN_COLUMN:
181 206 return variable->metadata().value(QStringLiteral("plugin"));
182 207 default:
183 208 // No action
184 209 break;
185 210 }
186 211
187 212 qWarning(LOG_VariableModel())
188 213 << tr("Can't get data (unknown column %1)").arg(index.column());
189 214 }
190 215 else {
191 216 qWarning(LOG_VariableModel()) << tr("Can't get data (no variable)");
192 217 }
193 218 }
194 219 else if (role == VariableRoles::ProgressRole) {
195 220 if (auto variable = impl->m_Variables.at(index.row())) {
196 221
197 222 auto it = impl->m_VariableToProgress.find(variable);
198 223 if (it != impl->m_VariableToProgress.cend()) {
199 224 return it->second;
200 225 }
201 226 }
202 227 }
203 228
204 229 return QVariant{};
205 230 }
206 231
207 232 QVariant VariableModel::headerData(int section, Qt::Orientation orientation, int role) const
208 233 {
209 234 if (role != Qt::DisplayRole && role != Qt::SizeHintRole) {
210 235 return QVariant{};
211 236 }
212 237
213 238 if (orientation == Qt::Horizontal) {
214 239 auto propertiesIt = COLUMN_PROPERTIES.find(section);
215 240 if (propertiesIt != COLUMN_PROPERTIES.cend()) {
216 241 // Role is either DisplayRole or SizeHintRole
217 242 return (role == Qt::DisplayRole)
218 243 ? QVariant{propertiesIt->m_Name}
219 244 : QVariant{QSize{propertiesIt->m_Width, propertiesIt->m_Height}};
220 245 }
221 246 else {
222 247 qWarning(LOG_VariableModel())
223 248 << tr("Can't get header data (unknown column %1)").arg(section);
224 249 }
225 250 }
226 251
227 252 return QVariant{};
228 253 }
229 254
230 255 void VariableModel::abortProgress(const QModelIndex &index)
231 256 {
232 257 if (auto variable = impl->m_Variables.at(index.row())) {
233 258 emit abortProgessRequested(variable);
234 259 }
235 260 }
236 261
237 262 void VariableModel::onVariableUpdated() noexcept
238 263 {
239 264 // Finds variable that has been updated in the model
240 265 if (auto updatedVariable = dynamic_cast<Variable *>(sender())) {
241 266 auto updatedVariableIndex = impl->indexOfVariable(updatedVariable);
242 267
243 268 if (updatedVariableIndex > -1) {
244 269 emit dataChanged(createIndex(updatedVariableIndex, 0),
245 270 createIndex(updatedVariableIndex, columnCount() - 1));
246 271 }
247 272 }
248 273 }
249 274
250 275 int VariableModel::VariableModelPrivate::indexOfVariable(Variable *variable) const noexcept
251 276 {
252 277 auto begin = std::cbegin(m_Variables);
253 278 auto end = std::cend(m_Variables);
254 279 auto it
255 280 = std::find_if(begin, end, [variable](const auto &var) { return var.get() == variable; });
256 281
257 282 if (it != end) {
258 283 // Gets the index of the variable in the model: we assume here that views have the same
259 284 // order as the model
260 285 return std::distance(begin, it);
261 286 }
262 287 else {
263 288 return -1;
264 289 }
265 290 }
@@ -1,72 +1,74
1 1 #include <QObject>
2 2 #include <QtTest>
3 3
4 4 #include <Data/IDataProvider.h>
5 5 #include <Time/TimeController.h>
6 6 #include <Variable/Variable.h>
7 7 #include <Variable/VariableController.h>
8 8
9 9 #include <memory>
10 10
11 11 namespace {
12 12
13 13 /// Provider used for the tests
14 14 class TestProvider : public IDataProvider {
15 std::shared_ptr<IDataProvider> clone() const { return std::make_shared<TestProvider>(); }
16
15 17 void requestDataLoading(QUuid acqIdentifier, const DataProviderParameters &parameters) override
16 18 {
17 19 // Does nothing
18 20 }
19 21
20 22 void requestDataAborting(QUuid acqIdentifier) override
21 23 {
22 24 // Does nothing
23 25 }
24 26 };
25 27
26 28 /// Generates a time controller for the tests
27 29 std::unique_ptr<TimeController> defaultTimeController()
28 30 {
29 31 auto timeController = std::make_unique<TimeController>();
30 32
31 33 QDateTime start{QDate{2017, 01, 01}, QTime{0, 0, 0, 0}};
32 34 QDateTime end{QDate{2017, 01, 02}, QTime{0, 0, 0, 0}};
33 35 timeController->onTimeToUpdate(
34 36 SqpRange{DateUtils::secondsSinceEpoch(start), DateUtils::secondsSinceEpoch(end)});
35 37
36 38 return timeController;
37 39 }
38 40
39 41 } // namespace
40 42
41 43 class TestVariableController : public QObject {
42 44 Q_OBJECT
43 45
44 46 private slots:
45 47 /// Test removes variable from controller
46 48 void testDeleteVariable();
47 49 };
48 50
49 51 void TestVariableController::testDeleteVariable()
50 52 {
51 53 // Creates variable controller
52 54 auto timeController = defaultTimeController();
53 55 VariableController variableController{};
54 56 variableController.setTimeController(timeController.get());
55 57
56 58 // Creates a variable from the controller
57 59 auto variable
58 60 = variableController.createVariable("variable", {}, std::make_shared<TestProvider>());
59 61
60 62 qDebug() << QString::number(variable.use_count());
61 63
62 64 // Removes the variable from the controller
63 65 variableController.deleteVariable(variable);
64 66
65 67 // Verifies that the variable has been deleted: this implies that the number of shared_ptr
66 68 // objects referring to the variable is 1 (the reference of this scope). Otherwise, the deletion
67 69 // is considered invalid since the variable is still referenced in the controller
68 70 QVERIFY(variable.use_count() == 1);
69 71 }
70 72
71 73 QTEST_MAIN(TestVariableController)
72 74 #include "TestVariableController.moc"
@@ -1,18 +1,19
1 1
2 2
3 3 tests = [
4 [['Common/TestStringUtils.cpp'],'test_string_utils','StringUtils test'],
4 5 [['Data/TestDataSeries.cpp'],'test_data','DataSeries test'],
5 6 [['Data/TestOneDimArrayData.cpp'],'test_1d','One Dim Array test'],
6 7 [['Data/TestTwoDimArrayData.cpp'],'test_2d','Two Dim Array test'],
7 8 [['DataSource/TestDataSourceController.cpp'],'test_data_source','DataSourceController test'],
8 9 [['Variable/TestVariableCacheController.cpp'],'test_variable_cache','VariableCacheController test'],
9 10 [['Variable/TestVariable.cpp'],'test_variable','Variable test']
10 11 ]
11 12
12 13 foreach unit_test : tests
13 14 test_moc_files = qt5.preprocess(moc_sources : unit_test[0])
14 15 test_exe = executable(unit_test[1],unit_test[0] , test_moc_files,
15 16 dependencies : [sciqlop_core, qt5test])
16 17 test(unit_test[2], test_exe, args: ['-teamcity', '-o', '@0@.teamcity.txt'.format(unit_test[1])])
17 18 endforeach
18 19
@@ -1,63 +1,63
1 1 #include "Variable/RenameVariableDialog.h"
2 2
3 3 #include <ui_RenameVariableDialog.h>
4 4
5 5 RenameVariableDialog::RenameVariableDialog(const QString &defaultName,
6 6 const QVector<QString> &forbiddenNames, QWidget *parent)
7 7 : QDialog{parent},
8 8 ui{new Ui::RenameVariableDialog},
9 9 m_DefaultName{defaultName},
10 10 m_ForbiddenNames{forbiddenNames}
11 11 {
12 12 ui->setupUi(this);
13 13
14 14 connect(ui->nameLineEdit, &QLineEdit::textChanged, [this]() { ui->errorLabel->hide(); });
15 15
16 16 ui->nameLineEdit->setText(defaultName);
17 17 ui->nameLineEdit->selectAll();
18 18 ui->nameLineEdit->setFocus();
19 19 }
20 20
21 21 RenameVariableDialog::~RenameVariableDialog() noexcept
22 22 {
23 23 delete ui;
24 24 }
25 25
26 26 QString RenameVariableDialog::name() const noexcept
27 27 {
28 return ui->nameLineEdit->text();
28 return ui->nameLineEdit->text().trimmed();
29 29 }
30 30
31 31 void RenameVariableDialog::accept()
32 32 {
33 33 auto invalidateInput = [this](const auto &error) {
34 34 ui->nameLineEdit->selectAll();
35 35 ui->nameLineEdit->setFocus();
36 36 ui->errorLabel->setText(error);
37 37 ui->errorLabel->show();
38 38 };
39 39
40 40 // Empty name
41 auto name = ui->nameLineEdit->text();
41 auto name = this->name();
42 42 if (name.isEmpty()) {
43 43 invalidateInput(tr("A variable name must be specified"));
44 44 return;
45 45 }
46 46
47 47 // Same name when opening dialog
48 48 if (name.compare(m_DefaultName, Qt::CaseInsensitive) == 0) {
49 49 reject();
50 50 return;
51 51 }
52 52
53 53 // Forbidden name
54 54 auto isForbidden
55 55 = [&name](const auto &it) { return name.compare(it, Qt::CaseInsensitive) == 0; };
56 56 if (std::any_of(m_ForbiddenNames.cbegin(), m_ForbiddenNames.cend(), isForbidden)) {
57 57 invalidateInput(tr("'%1' is already used").arg(name));
58 58 return;
59 59 }
60 60
61 61 // Valid name
62 62 QDialog::accept();
63 63 }
@@ -1,222 +1,228
1 1 #include <Variable/RenameVariableDialog.h>
2 2 #include <Variable/Variable.h>
3 3 #include <Variable/VariableController.h>
4 4 #include <Variable/VariableInspectorWidget.h>
5 5 #include <Variable/VariableMenuHeaderWidget.h>
6 6 #include <Variable/VariableModel.h>
7 7
8 8 #include <ui_VariableInspectorWidget.h>
9 9
10 10 #include <QMouseEvent>
11 11 #include <QSortFilterProxyModel>
12 12 #include <QStyledItemDelegate>
13 13 #include <QWidgetAction>
14 14
15 15 #include <SqpApplication.h>
16 16
17 17 Q_LOGGING_CATEGORY(LOG_VariableInspectorWidget, "VariableInspectorWidget")
18 18
19 19
20 20 class QProgressBarItemDelegate : public QStyledItemDelegate {
21 21
22 22 public:
23 23 QProgressBarItemDelegate(QObject *parent) : QStyledItemDelegate{parent} {}
24 24
25 25 void paint(QPainter *painter, const QStyleOptionViewItem &option,
26 26 const QModelIndex &index) const
27 27 {
28 28 auto data = index.data(Qt::DisplayRole);
29 29 auto progressData = index.data(VariableRoles::ProgressRole);
30 30 if (data.isValid() && progressData.isValid()) {
31 31 auto name = data.value<QString>();
32 32 auto progress = progressData.value<double>();
33 33 if (progress > 0) {
34 34 auto cancelButtonWidth = 20;
35 35 auto progressBarOption = QStyleOptionProgressBar{};
36 36 auto progressRect = option.rect;
37 37 progressRect.setWidth(progressRect.width() - cancelButtonWidth);
38 38 progressBarOption.rect = progressRect;
39 39 progressBarOption.minimum = 0;
40 40 progressBarOption.maximum = 100;
41 41 progressBarOption.progress = progress;
42 42 progressBarOption.text
43 43 = QString("%1 %2").arg(name).arg(QString::number(progress, 'f', 2) + "%");
44 44 progressBarOption.textVisible = true;
45 45 progressBarOption.textAlignment = Qt::AlignCenter;
46 46
47 47
48 48 QApplication::style()->drawControl(QStyle::CE_ProgressBar, &progressBarOption,
49 49 painter);
50 50
51 51 // Cancel button
52 52 auto buttonRect = QRect(progressRect.right(), option.rect.top(), cancelButtonWidth,
53 53 option.rect.height());
54 54 auto buttonOption = QStyleOptionButton{};
55 55 buttonOption.rect = buttonRect;
56 56 buttonOption.text = "X";
57 57
58 58 QApplication::style()->drawControl(QStyle::CE_PushButton, &buttonOption, painter);
59 59 }
60 60 else {
61 61 QStyledItemDelegate::paint(painter, option, index);
62 62 }
63 63 }
64 64 else {
65 65 QStyledItemDelegate::paint(painter, option, index);
66 66 }
67 67 }
68 68
69 69 bool editorEvent(QEvent *event, QAbstractItemModel *model, const QStyleOptionViewItem &option,
70 70 const QModelIndex &index)
71 71 {
72 72 if (event->type() == QEvent::MouseButtonRelease) {
73 73 auto data = index.data(Qt::DisplayRole);
74 74 auto progressData = index.data(VariableRoles::ProgressRole);
75 75 if (data.isValid() && progressData.isValid()) {
76 76 auto cancelButtonWidth = 20;
77 77 auto progressRect = option.rect;
78 78 progressRect.setWidth(progressRect.width() - cancelButtonWidth);
79 79 // Cancel button
80 80 auto buttonRect = QRect(progressRect.right(), option.rect.top(), cancelButtonWidth,
81 81 option.rect.height());
82 82
83 83 auto e = (QMouseEvent *)event;
84 84 auto clickX = e->x();
85 85 auto clickY = e->y();
86 86
87 87 auto x = buttonRect.left(); // the X coordinate
88 88 auto y = buttonRect.top(); // the Y coordinate
89 89 auto w = buttonRect.width(); // button width
90 90 auto h = buttonRect.height(); // button height
91 91
92 92 if (clickX > x && clickX < x + w) {
93 93 if (clickY > y && clickY < y + h) {
94 94 auto variableModel = sqpApp->variableController().variableModel();
95 95 variableModel->abortProgress(index);
96 96 }
97 97 }
98 98 else {
99 99 QStyledItemDelegate::editorEvent(event, model, option, index);
100 100 }
101 101 }
102 102 else {
103 103 QStyledItemDelegate::editorEvent(event, model, option, index);
104 104 }
105 105 }
106 106 else {
107 107 QStyledItemDelegate::editorEvent(event, model, option, index);
108 108 }
109 109 }
110 110 };
111 111
112 112 VariableInspectorWidget::VariableInspectorWidget(QWidget *parent)
113 113 : QWidget{parent},
114 114 ui{new Ui::VariableInspectorWidget},
115 115 m_ProgressBarItemDelegate{new QProgressBarItemDelegate{this}}
116 116 {
117 117 ui->setupUi(this);
118 118
119 119 // Sets model for table
120 120 // auto sortFilterModel = new QSortFilterProxyModel{this};
121 121 // sortFilterModel->setSourceModel(sqpApp->variableController().variableModel());
122 122
123 123 auto variableModel = sqpApp->variableController().variableModel();
124 124 ui->tableView->setModel(variableModel);
125 125
126 126 // Adds extra signal/slot between view and model, so the view can be updated instantly when
127 127 // there is a change of data in the model
128 128 connect(variableModel, SIGNAL(dataChanged(const QModelIndex &, const QModelIndex &)), this,
129 129 SLOT(refresh()));
130 130
131 131 ui->tableView->setSelectionModel(sqpApp->variableController().variableSelectionModel());
132 132 ui->tableView->setItemDelegateForColumn(0, m_ProgressBarItemDelegate);
133 133
134 134 // Fixes column sizes
135 135 auto model = ui->tableView->model();
136 136 const auto count = model->columnCount();
137 137 for (auto i = 0; i < count; ++i) {
138 138 ui->tableView->setColumnWidth(
139 139 i, model->headerData(i, Qt::Horizontal, Qt::SizeHintRole).toSize().width());
140 140 }
141 141
142 142 // Sets selection options
143 143 ui->tableView->setSelectionBehavior(QTableView::SelectRows);
144 144 ui->tableView->setSelectionMode(QTableView::ExtendedSelection);
145 145
146 146 // Connection to show a menu when right clicking on the tree
147 147 ui->tableView->setContextMenuPolicy(Qt::CustomContextMenu);
148 148 connect(ui->tableView, &QTableView::customContextMenuRequested, this,
149 149 &VariableInspectorWidget::onTableMenuRequested);
150 150 }
151 151
152 152 VariableInspectorWidget::~VariableInspectorWidget()
153 153 {
154 154 delete ui;
155 155 }
156 156
157 157 void VariableInspectorWidget::onTableMenuRequested(const QPoint &pos) noexcept
158 158 {
159 159 auto selectedRows = ui->tableView->selectionModel()->selectedRows();
160 160
161 161 // Gets the model to retrieve the underlying selected variables
162 162 auto model = sqpApp->variableController().variableModel();
163 163 auto selectedVariables = QVector<std::shared_ptr<Variable> >{};
164 164 for (const auto &selectedRow : qAsConst(selectedRows)) {
165 165 if (auto selectedVariable = model->variable(selectedRow.row())) {
166 166 selectedVariables.push_back(selectedVariable);
167 167 }
168 168 }
169 169
170 170 QMenu tableMenu{};
171 171
172 172 // Emits a signal so that potential receivers can populate the menu before displaying it
173 173 emit tableMenuAboutToBeDisplayed(&tableMenu, selectedVariables);
174 174
175 175 // Adds menu-specific actions
176 176 if (!selectedVariables.isEmpty()) {
177 177 tableMenu.addSeparator();
178 178
179 // 'Rename' action (only if one variable selected)
179 // 'Rename' and 'Duplicate' actions (only if one variable selected)
180 180 if (selectedVariables.size() == 1) {
181 181 auto selectedVariable = selectedVariables.front();
182 182
183 auto duplicateFun = [&selectedVariable]() {
184 sqpApp->variableController().cloneVariable(selectedVariable);
185 };
186
187 tableMenu.addAction(tr("Duplicate"), duplicateFun);
188
183 189 auto renameFun = [&selectedVariable, &model, this]() {
184 190 // Generates forbidden names (names associated to existing variables)
185 191 auto allVariables = model->variables();
186 192 auto forbiddenNames = QVector<QString>(allVariables.size());
187 193 std::transform(allVariables.cbegin(), allVariables.cend(), forbiddenNames.begin(),
188 194 [](const auto &variable) { return variable->name(); });
189 195
190 196 RenameVariableDialog dialog{selectedVariable->name(), forbiddenNames, this};
191 197 if (dialog.exec() == QDialog::Accepted) {
192 198 selectedVariable->setName(dialog.name());
193 199 }
194 200 };
195 201
196 202 tableMenu.addAction(tr("Rename..."), renameFun);
197 203 }
198 204
199 205 // 'Delete' action
200 206 auto deleteFun = [&selectedVariables]() {
201 207 sqpApp->variableController().deleteVariables(selectedVariables);
202 208 };
203 209
204 210 tableMenu.addAction(QIcon{":/icones/delete.png"}, tr("Delete"), deleteFun);
205 211 }
206 212
207 213 if (!tableMenu.isEmpty()) {
208 214 // Generates menu header (inserted before first action)
209 215 auto firstAction = tableMenu.actions().first();
210 216 auto headerAction = new QWidgetAction{&tableMenu};
211 217 headerAction->setDefaultWidget(new VariableMenuHeaderWidget{selectedVariables, &tableMenu});
212 218 tableMenu.insertAction(firstAction, headerAction);
213 219
214 220 // Displays menu
215 221 tableMenu.exec(QCursor::pos());
216 222 }
217 223 }
218 224
219 225 void VariableInspectorWidget::refresh() noexcept
220 226 {
221 227 ui->tableView->viewport()->update();
222 228 }
@@ -1,30 +1,31
1 1 #ifndef SCIQLOP_AMDAPROVIDER_H
2 2 #define SCIQLOP_AMDAPROVIDER_H
3 3
4 4 #include "AmdaGlobal.h"
5 5
6 6 #include <Data/IDataProvider.h>
7 7
8 8 #include <QLoggingCategory>
9 9
10 10
11 11 Q_DECLARE_LOGGING_CATEGORY(LOG_AmdaProvider)
12 12
13 13 class QNetworkReply;
14 14
15 15 /**
16 16 * @brief The AmdaProvider class is an example of how a data provider can generate data
17 17 */
18 18 class SCIQLOP_AMDA_EXPORT AmdaProvider : public IDataProvider {
19 19 public:
20 20 explicit AmdaProvider();
21 std::shared_ptr<IDataProvider> clone() const override;
21 22
22 23 void requestDataLoading(QUuid acqIdentifier, const DataProviderParameters &parameters) override;
23 24
24 25 void requestDataAborting(QUuid acqIdentifier) override;
25 26
26 27 private:
27 28 void retrieveData(QUuid token, const SqpRange &dateTime, const QVariantHash &data);
28 29 };
29 30
30 31 #endif // SCIQLOP_AMDAPROVIDER_H
@@ -1,168 +1,174
1 1 #include "AmdaProvider.h"
2 2 #include "AmdaDefs.h"
3 3 #include "AmdaResultParser.h"
4 4
5 5 #include <Common/DateUtils.h>
6 6 #include <Data/DataProviderParameters.h>
7 7 #include <Network/NetworkController.h>
8 8 #include <SqpApplication.h>
9 9 #include <Variable/Variable.h>
10 10
11 11 #include <QNetworkAccessManager>
12 12 #include <QNetworkReply>
13 13 #include <QTemporaryFile>
14 14 #include <QThread>
15 15
16 16 Q_LOGGING_CATEGORY(LOG_AmdaProvider, "AmdaProvider")
17 17
18 18 namespace {
19 19
20 20 /// URL format for a request on AMDA server. The parameters are as follows:
21 21 /// - %1: start date
22 22 /// - %2: end date
23 23 /// - %3: parameter id
24 24 const auto AMDA_URL_FORMAT = QStringLiteral(
25 25 "http://amda.irap.omp.eu/php/rest/"
26 26 "getParameter.php?startTime=%1&stopTime=%2&parameterID=%3&outputFormat=ASCII&"
27 27 "timeFormat=ISO8601&gzip=0");
28 28
29 29 /// Dates format passed in the URL (e.g 2013-09-23T09:00)
30 30 const auto AMDA_TIME_FORMAT = QStringLiteral("yyyy-MM-ddThh:mm:ss");
31 31
32 32 /// Formats a time to a date that can be passed in URL
33 33 QString dateFormat(double sqpRange) noexcept
34 34 {
35 35 auto dateTime = DateUtils::dateTime(sqpRange);
36 36 return dateTime.toString(AMDA_TIME_FORMAT);
37 37 }
38 38
39 39 AmdaResultParser::ValueType valueType(const QString &valueType)
40 40 {
41 41 if (valueType == QStringLiteral("scalar")) {
42 42 return AmdaResultParser::ValueType::SCALAR;
43 43 }
44 44 else if (valueType == QStringLiteral("vector")) {
45 45 return AmdaResultParser::ValueType::VECTOR;
46 46 }
47 47 else {
48 48 return AmdaResultParser::ValueType::UNKNOWN;
49 49 }
50 50 }
51 51
52 52 } // namespace
53 53
54 54 AmdaProvider::AmdaProvider()
55 55 {
56 56 qCDebug(LOG_AmdaProvider()) << tr("AmdaProvider::AmdaProvider") << QThread::currentThread();
57 57 if (auto app = sqpApp) {
58 58 auto &networkController = app->networkController();
59 59 connect(this, SIGNAL(requestConstructed(QNetworkRequest, QUuid,
60 60 std::function<void(QNetworkReply *, QUuid)>)),
61 61 &networkController,
62 62 SLOT(onProcessRequested(QNetworkRequest, QUuid,
63 63 std::function<void(QNetworkReply *, QUuid)>)));
64 64
65 65
66 66 connect(&sqpApp->networkController(), SIGNAL(replyDownloadProgress(QUuid, double)), this,
67 67 SIGNAL(dataProvidedProgress(QUuid, double)));
68 68 }
69 69 }
70 70
71 std::shared_ptr<IDataProvider> AmdaProvider::clone() const
72 {
73 // No copy is made in the clone
74 return std::make_shared<AmdaProvider>();
75 }
76
71 77 void AmdaProvider::requestDataLoading(QUuid acqIdentifier, const DataProviderParameters &parameters)
72 78 {
73 79 // NOTE: Try to use multithread if possible
74 80 const auto times = parameters.m_Times;
75 81 const auto data = parameters.m_Data;
76 82 for (const auto &dateTime : qAsConst(times)) {
77 83 this->retrieveData(acqIdentifier, dateTime, data);
78 84
79 85 // TORM when AMDA will support quick asynchrone request
80 86 QThread::msleep(1000);
81 87 }
82 88 }
83 89
84 90 void AmdaProvider::requestDataAborting(QUuid acqIdentifier)
85 91 {
86 92 if (auto app = sqpApp) {
87 93 auto &networkController = app->networkController();
88 94 networkController.onReplyCanceled(acqIdentifier);
89 95 }
90 96 }
91 97
92 98 void AmdaProvider::retrieveData(QUuid token, const SqpRange &dateTime, const QVariantHash &data)
93 99 {
94 100 // Retrieves product ID from data: if the value is invalid, no request is made
95 101 auto productId = data.value(AMDA_XML_ID_KEY).toString();
96 102 if (productId.isNull()) {
97 103 qCCritical(LOG_AmdaProvider()) << tr("Can't retrieve data: unknown product id");
98 104 return;
99 105 }
100 106 qCDebug(LOG_AmdaProvider()) << tr("AmdaProvider::retrieveData") << dateTime;
101 107
102 108 // Retrieves the data type that determines whether the expected format for the result file is
103 109 // scalar, vector...
104 110 auto productValueType = valueType(data.value(AMDA_DATA_TYPE_KEY).toString());
105 111
106 112 // /////////// //
107 113 // Creates URL //
108 114 // /////////// //
109 115
110 116 auto startDate = dateFormat(dateTime.m_TStart);
111 117 auto endDate = dateFormat(dateTime.m_TEnd);
112 118
113 119 auto url = QUrl{QString{AMDA_URL_FORMAT}.arg(startDate, endDate, productId)};
114 120 qCInfo(LOG_AmdaProvider()) << tr("TORM AmdaProvider::retrieveData url:") << url;
115 121 auto tempFile = std::make_shared<QTemporaryFile>();
116 122
117 123 // LAMBDA
118 124 auto httpDownloadFinished = [this, dateTime, tempFile,
119 125 productValueType](QNetworkReply *reply, QUuid dataId) noexcept {
120 126
121 127 // Don't do anything if the reply was abort
122 128 if (reply->error() != QNetworkReply::OperationCanceledError) {
123 129
124 130 if (tempFile) {
125 131 auto replyReadAll = reply->readAll();
126 132 if (!replyReadAll.isEmpty()) {
127 133 tempFile->write(replyReadAll);
128 134 }
129 135 tempFile->close();
130 136
131 137 // Parse results file
132 138 if (auto dataSeries
133 139 = AmdaResultParser::readTxt(tempFile->fileName(), productValueType)) {
134 140 emit dataProvided(dataId, dataSeries, dateTime);
135 141 }
136 142 else {
137 143 /// @todo ALX : debug
138 144 }
139 145 }
140 146 }
141 147
142 148 };
143 149 auto httpFinishedLambda
144 150 = [this, httpDownloadFinished, tempFile](QNetworkReply *reply, QUuid dataId) noexcept {
145 151
146 152 // Don't do anything if the reply was abort
147 153 if (reply->error() != QNetworkReply::OperationCanceledError) {
148 154 auto downloadFileUrl = QUrl{QString{reply->readAll()}};
149 155
150 156
151 157 qCInfo(LOG_AmdaProvider())
152 158 << tr("TORM AmdaProvider::retrieveData downloadFileUrl:") << downloadFileUrl;
153 159 // Executes request for downloading file //
154 160
155 161 // Creates destination file
156 162 if (tempFile->open()) {
157 163 // Executes request
158 164 emit requestConstructed(QNetworkRequest{downloadFileUrl}, dataId,
159 165 httpDownloadFinished);
160 166 }
161 167 }
162 168 };
163 169
164 170 // //////////////// //
165 171 // Executes request //
166 172 // //////////////// //
167 173 emit requestConstructed(QNetworkRequest{url}, token, httpFinishedLambda);
168 174 }
@@ -1,34 +1,36
1 1 #ifndef SCIQLOP_COSINUSPROVIDER_H
2 2 #define SCIQLOP_COSINUSPROVIDER_H
3 3
4 4 #include "MockPluginGlobal.h"
5 5
6 6 #include <Data/IDataProvider.h>
7 7
8 8 #include <QLoggingCategory>
9 9 #include <QUuid>
10 10
11 11 #include <QHash>
12 12 Q_DECLARE_LOGGING_CATEGORY(LOG_CosinusProvider)
13 13
14 14 /**
15 15 * @brief The CosinusProvider class is an example of how a data provider can generate data
16 16 */
17 17 class SCIQLOP_MOCKPLUGIN_EXPORT CosinusProvider : public IDataProvider {
18 18 public:
19 std::shared_ptr<IDataProvider> clone() const override;
20
19 21 /// @sa IDataProvider::requestDataLoading(). The current impl isn't thread safe.
20 22 void requestDataLoading(QUuid acqIdentifier, const DataProviderParameters &parameters) override;
21 23
22 24
23 25 /// @sa IDataProvider::requestDataAborting(). The current impl isn't thread safe.
24 26 void requestDataAborting(QUuid acqIdentifier) override;
25 27
26 28
27 29 private:
28 30 std::shared_ptr<IDataSeries> retrieveData(QUuid acqIdentifier,
29 31 const SqpRange &dataRangeRequested);
30 32
31 33 QHash<QUuid, bool> m_VariableToEnableProvider;
32 34 };
33 35
34 36 #endif // SCIQLOP_COSINUSPROVIDER_H
@@ -1,103 +1,109
1 1 #include "CosinusProvider.h"
2 2
3 3 #include <Data/DataProviderParameters.h>
4 4 #include <Data/ScalarSeries.h>
5 5
6 6 #include <cmath>
7 7
8 8 #include <QFuture>
9 9 #include <QThread>
10 10 #include <QtConcurrent/QtConcurrent>
11 11
12 12 Q_LOGGING_CATEGORY(LOG_CosinusProvider, "CosinusProvider")
13 13
14 std::shared_ptr<IDataProvider> CosinusProvider::clone() const
15 {
16 // No copy is made in clone
17 return std::make_shared<CosinusProvider>();
18 }
19
14 20 std::shared_ptr<IDataSeries> CosinusProvider::retrieveData(QUuid acqIdentifier,
15 21 const SqpRange &dataRangeRequested)
16 22 {
17 23 // TODO: Add Mutex
18 24 auto dataIndex = 0;
19 25
20 26 // Gets the timerange from the parameters
21 27 double freq = 100.0;
22 28 double start = std::ceil(dataRangeRequested.m_TStart * freq); // 100 htz
23 29 double end = std::floor(dataRangeRequested.m_TEnd * freq); // 100 htz
24 30
25 31 // We assure that timerange is valid
26 32 if (end < start) {
27 33 std::swap(start, end);
28 34 }
29 35
30 36 // Generates scalar series containing cosinus values (one value per second)
31 37 auto dataCount = end - start;
32 38
33 39 auto xAxisData = std::vector<double>{};
34 40 xAxisData.resize(dataCount);
35 41
36 42 auto valuesData = std::vector<double>{};
37 43 valuesData.resize(dataCount);
38 44
39 45 int progress = 0;
40 46 auto progressEnd = dataCount;
41 47 for (auto time = start; time < end; ++time, ++dataIndex) {
42 48 auto it = m_VariableToEnableProvider.find(acqIdentifier);
43 49 if (it != m_VariableToEnableProvider.end() && it.value()) {
44 50 const auto timeOnFreq = time / freq;
45 51
46 52 xAxisData[dataIndex] = timeOnFreq;
47 53 valuesData[dataIndex] = std::cos(timeOnFreq);
48 54
49 55 // progression
50 56 int currentProgress = (time - start) * 100.0 / progressEnd;
51 57 if (currentProgress != progress) {
52 58 progress = currentProgress;
53 59
54 60 emit dataProvidedProgress(acqIdentifier, progress);
55 61 }
56 62 }
57 63 else {
58 64 if (!it.value()) {
59 65 qCDebug(LOG_CosinusProvider())
60 66 << "CosinusProvider::retrieveData: ARRET De l'acquisition detectΓ©"
61 67 << end - time;
62 68 }
63 69 }
64 70 }
65 71 emit dataProvidedProgress(acqIdentifier, 0.0);
66 72
67 73 return std::make_shared<ScalarSeries>(std::move(xAxisData), std::move(valuesData),
68 74 Unit{QStringLiteral("t"), true}, Unit{});
69 75 }
70 76
71 77 void CosinusProvider::requestDataLoading(QUuid acqIdentifier,
72 78 const DataProviderParameters &parameters)
73 79 {
74 80 // TODO: Add Mutex
75 81 m_VariableToEnableProvider[acqIdentifier] = true;
76 82 qCDebug(LOG_CosinusProvider()) << "TORM: CosinusProvider::requestDataLoading"
77 83 << QThread::currentThread()->objectName();
78 84 // NOTE: Try to use multithread if possible
79 85 const auto times = parameters.m_Times;
80 86
81 87 for (const auto &dateTime : qAsConst(times)) {
82 88 if (m_VariableToEnableProvider[acqIdentifier]) {
83 89 auto scalarSeries = this->retrieveData(acqIdentifier, dateTime);
84 90 qCDebug(LOG_CosinusProvider()) << "TORM: CosinusProvider::dataProvided";
85 91 emit dataProvided(acqIdentifier, scalarSeries, dateTime);
86 92 }
87 93 }
88 94 }
89 95
90 96 void CosinusProvider::requestDataAborting(QUuid acqIdentifier)
91 97 {
92 98 // TODO: Add Mutex
93 99 qCDebug(LOG_CosinusProvider()) << "CosinusProvider::requestDataAborting" << acqIdentifier
94 100 << QThread::currentThread()->objectName();
95 101 auto it = m_VariableToEnableProvider.find(acqIdentifier);
96 102 if (it != m_VariableToEnableProvider.end()) {
97 103 it.value() = false;
98 104 }
99 105 else {
100 106 qCWarning(LOG_CosinusProvider())
101 107 << tr("Aborting progression of inexistant identifier detected !!!");
102 108 }
103 109 }
General Comments 0
You need to be logged in to leave comments. Login now