@@ -1,1 +1,1 | |||||
1 | Subproject commit c08d1b8ad2976824def52cf0fca26a72b1069356 |
|
1 | Subproject commit a05b0ab234936e28b6ba79535ccaee297d83a1e8 |
@@ -1,158 +1,157 | |||||
1 | #ifndef SCIQLOP_VISUALIZATIONGRAPHWIDGET_H |
|
1 | #ifndef SCIQLOP_VISUALIZATIONGRAPHWIDGET_H | |
2 | #define SCIQLOP_VISUALIZATIONGRAPHWIDGET_H |
|
2 | #define SCIQLOP_VISUALIZATIONGRAPHWIDGET_H | |
3 |
|
3 | |||
4 | #include "Visualization/IVisualizationWidget.h" |
|
4 | #include "Visualization/IVisualizationWidget.h" | |
5 | #include "Visualization/VisualizationDragWidget.h" |
|
5 | #include "Visualization/VisualizationDragWidget.h" | |
6 |
|
6 | |||
7 | #include <QLoggingCategory> |
|
7 | #include <QLoggingCategory> | |
8 | #include <QWidget> |
|
8 | #include <QWidget> | |
9 |
|
9 | |||
10 | #include <memory> |
|
10 | #include <memory> | |
11 |
|
11 | |||
12 | #include <Common/spimpl.h> |
|
12 | #include <Common/spimpl.h> | |
13 |
|
13 | |||
14 | Q_DECLARE_LOGGING_CATEGORY(LOG_VisualizationGraphWidget) |
|
14 | Q_DECLARE_LOGGING_CATEGORY(LOG_VisualizationGraphWidget) | |
15 |
|
15 | |||
16 | class QCPRange; |
|
16 | class QCPRange; | |
17 | class QCustomPlot; |
|
17 | class QCustomPlot; | |
18 | class DateTimeRange; |
|
18 | class DateTimeRange; | |
19 | class Variable; |
|
19 | class Variable; | |
20 | class VisualizationWidget; |
|
20 | class VisualizationWidget; | |
21 | class VisualizationZoneWidget; |
|
21 | class VisualizationZoneWidget; | |
22 | class VisualizationSelectionZoneItem; |
|
22 | class VisualizationSelectionZoneItem; | |
23 |
|
23 | |||
24 | namespace Ui { |
|
24 | namespace Ui { | |
25 | class VisualizationGraphWidget; |
|
25 | class VisualizationGraphWidget; | |
26 | } // namespace Ui |
|
26 | } // namespace Ui | |
27 |
|
27 | |||
28 | /// Defines options that can be associated with the graph |
|
28 | /// Defines options that can be associated with the graph | |
29 | enum GraphFlag { |
|
29 | enum GraphFlag { | |
30 | DisableAll = 0x0, ///< Disables acquisition and synchronization |
|
30 | DisableAll = 0x0, ///< Disables acquisition and synchronization | |
31 | EnableAcquisition = 0x1, ///< When this flag is set, the change of the graph's range leads to |
|
31 | EnableAcquisition = 0x1, ///< When this flag is set, the change of the graph's range leads to | |
32 | /// the acquisition of data |
|
32 | /// the acquisition of data | |
33 | EnableSynchronization = 0x2, ///< When this flag is set, the change of the graph's range causes |
|
33 | EnableSynchronization = 0x2, ///< When this flag is set, the change of the graph's range causes | |
34 | /// the call to the synchronization of the graphs contained in the |
|
34 | /// the call to the synchronization of the graphs contained in the | |
35 | /// same zone of this graph |
|
35 | /// same zone of this graph | |
36 | EnableAll = ~DisableAll ///< Enables acquisition and synchronization |
|
36 | EnableAll = ~DisableAll ///< Enables acquisition and synchronization | |
37 | }; |
|
37 | }; | |
38 |
|
38 | |||
39 | Q_DECLARE_FLAGS(GraphFlags, GraphFlag) |
|
39 | Q_DECLARE_FLAGS(GraphFlags, GraphFlag) | |
40 | Q_DECLARE_OPERATORS_FOR_FLAGS(GraphFlags) |
|
40 | Q_DECLARE_OPERATORS_FOR_FLAGS(GraphFlags) | |
41 |
|
41 | |||
42 | class VisualizationGraphWidget : public VisualizationDragWidget, public IVisualizationWidget { |
|
42 | class VisualizationGraphWidget : public VisualizationDragWidget, public IVisualizationWidget { | |
43 | Q_OBJECT |
|
43 | Q_OBJECT | |
44 |
|
44 | |||
45 | friend class QCustomPlotSynchronizer; |
|
45 | friend class QCustomPlotSynchronizer; | |
46 | friend class VisualizationGraphRenderingDelegate; |
|
46 | friend class VisualizationGraphRenderingDelegate; | |
47 |
|
47 | |||
48 | public: |
|
48 | public: | |
49 | explicit VisualizationGraphWidget(const QString &name = {}, QWidget *parent = 0); |
|
49 | explicit VisualizationGraphWidget(const QString &name = {}, QWidget *parent = 0); | |
50 | virtual ~VisualizationGraphWidget(); |
|
50 | virtual ~VisualizationGraphWidget(); | |
51 |
|
51 | |||
52 | /// Returns the VisualizationZoneWidget which contains the graph or nullptr |
|
52 | /// Returns the VisualizationZoneWidget which contains the graph or nullptr | |
53 | VisualizationZoneWidget *parentZoneWidget() const noexcept; |
|
53 | VisualizationZoneWidget *parentZoneWidget() const noexcept; | |
54 |
|
54 | |||
55 | /// Returns the main VisualizationWidget which contains the graph or nullptr |
|
55 | /// Returns the main VisualizationWidget which contains the graph or nullptr | |
56 | VisualizationWidget *parentVisualizationWidget() const; |
|
56 | VisualizationWidget *parentVisualizationWidget() const; | |
57 |
|
57 | |||
58 | /// Sets graph options |
|
58 | /// Sets graph options | |
59 | void setFlags(GraphFlags flags); |
|
59 | void setFlags(GraphFlags flags); | |
60 |
|
60 | |||
61 | void addVariable(std::shared_ptr<Variable> variable, DateTimeRange range); |
|
61 | void addVariable(std::shared_ptr<Variable> variable, DateTimeRange range); | |
62 |
|
62 | |||
63 | /// Removes a variable from the graph |
|
63 | /// Removes a variable from the graph | |
64 | void removeVariable(std::shared_ptr<Variable> variable) noexcept; |
|
64 | void removeVariable(std::shared_ptr<Variable> variable) noexcept; | |
65 |
|
65 | |||
66 | /// Returns the list of all variables used in the graph |
|
66 | /// Returns the list of all variables used in the graph | |
67 | std::vector<std::shared_ptr<Variable> > variables() const; |
|
67 | std::vector<std::shared_ptr<Variable> > variables() const; | |
68 |
|
68 | |||
69 | /// Sets the y-axis range based on the data of a variable |
|
69 | /// Sets the y-axis range based on the data of a variable | |
70 | void setYRange(std::shared_ptr<Variable> variable); |
|
70 | void setYRange(std::shared_ptr<Variable> variable); | |
71 | DateTimeRange graphRange() const noexcept; |
|
71 | DateTimeRange graphRange() const noexcept; | |
72 | void setGraphRange(const DateTimeRange &range, bool calibration = false); |
|
72 | void setGraphRange(const DateTimeRange &range, bool calibration = false); | |
73 | void setAutoRangeOnVariableInitialization(bool value); |
|
73 | void setAutoRangeOnVariableInitialization(bool value); | |
74 |
|
74 | |||
75 | // Zones |
|
75 | // Zones | |
76 | /// Returns the ranges of all the selection zones on the graph |
|
76 | /// Returns the ranges of all the selection zones on the graph | |
77 | QVector<DateTimeRange> selectionZoneRanges() const; |
|
77 | QVector<DateTimeRange> selectionZoneRanges() const; | |
78 | /// Adds new selection zones in the graph |
|
78 | /// Adds new selection zones in the graph | |
79 | void addSelectionZones(const QVector<DateTimeRange> &ranges); |
|
79 | void addSelectionZones(const QVector<DateTimeRange> &ranges); | |
80 | /// Adds a new selection zone in the graph |
|
80 | /// Adds a new selection zone in the graph | |
81 | VisualizationSelectionZoneItem *addSelectionZone(const QString &name, const DateTimeRange &range); |
|
81 | VisualizationSelectionZoneItem *addSelectionZone(const QString &name, const DateTimeRange &range); | |
82 | /// Removes the specified selection zone |
|
82 | /// Removes the specified selection zone | |
83 | void removeSelectionZone(VisualizationSelectionZoneItem *selectionZone); |
|
83 | void removeSelectionZone(VisualizationSelectionZoneItem *selectionZone); | |
84 |
|
84 | |||
85 | /// Undo the last zoom done with a zoom box |
|
85 | /// Undo the last zoom done with a zoom box | |
86 | void undoZoom(); |
|
86 | void undoZoom(); | |
87 |
|
87 | |||
88 | // IVisualizationWidget interface |
|
88 | // IVisualizationWidget interface | |
89 | void accept(IVisualizationWidgetVisitor *visitor) override; |
|
89 | void accept(IVisualizationWidgetVisitor *visitor) override; | |
90 | bool canDrop(const Variable &variable) const override; |
|
90 | bool canDrop(const Variable &variable) const override; | |
91 | bool contains(const Variable &variable) const override; |
|
91 | bool contains(const Variable &variable) const override; | |
92 | QString name() const override; |
|
92 | QString name() const override; | |
93 |
|
93 | |||
94 | // VisualisationDragWidget |
|
94 | // VisualisationDragWidget | |
95 | QMimeData *mimeData(const QPoint &position) const override; |
|
95 | QMimeData *mimeData(const QPoint &position) const override; | |
96 | QPixmap customDragPixmap(const QPoint &dragPosition) override; |
|
96 | QPixmap customDragPixmap(const QPoint &dragPosition) override; | |
97 | bool isDragAllowed() const override; |
|
97 | bool isDragAllowed() const override; | |
98 | void highlightForMerge(bool highlighted) override; |
|
98 | void highlightForMerge(bool highlighted) override; | |
99 |
|
99 | |||
100 | // Cursors |
|
100 | // Cursors | |
101 | /// Adds or moves the vertical cursor at the specified value on the x-axis |
|
101 | /// Adds or moves the vertical cursor at the specified value on the x-axis | |
102 | void addVerticalCursor(double time); |
|
102 | void addVerticalCursor(double time); | |
103 | /// Adds or moves the vertical cursor at the specified value on the x-axis |
|
103 | /// Adds or moves the vertical cursor at the specified value on the x-axis | |
104 | void addVerticalCursorAtViewportPosition(double position); |
|
104 | void addVerticalCursorAtViewportPosition(double position); | |
105 | void removeVerticalCursor(); |
|
105 | void removeVerticalCursor(); | |
106 | /// Adds or moves the vertical cursor at the specified value on the y-axis |
|
106 | /// Adds or moves the vertical cursor at the specified value on the y-axis | |
107 | void addHorizontalCursor(double value); |
|
107 | void addHorizontalCursor(double value); | |
108 | /// Adds or moves the vertical cursor at the specified value on the y-axis |
|
108 | /// Adds or moves the vertical cursor at the specified value on the y-axis | |
109 | void addHorizontalCursorAtViewportPosition(double position); |
|
109 | void addHorizontalCursorAtViewportPosition(double position); | |
110 | void removeHorizontalCursor(); |
|
110 | void removeHorizontalCursor(); | |
111 |
|
111 | |||
112 | signals: |
|
112 | signals: | |
113 | void synchronize(const DateTimeRange &range, const DateTimeRange &oldRange); |
|
113 | void synchronize(const DateTimeRange &range, const DateTimeRange &oldRange); | |
114 |
void |
|
114 | void changeRange(const std::shared_ptr<Variable>& variable, const DateTimeRange &range); | |
115 | bool synchronise); |
|
|||
116 |
|
115 | |||
117 | /// Signal emitted when the variable is about to be removed from the graph |
|
116 | /// Signal emitted when the variable is about to be removed from the graph | |
118 | void variableAboutToBeRemoved(std::shared_ptr<Variable> var); |
|
117 | void variableAboutToBeRemoved(std::shared_ptr<Variable> var); | |
119 | /// Signal emitted when the variable has been added to the graph |
|
118 | /// Signal emitted when the variable has been added to the graph | |
120 | void variableAdded(std::shared_ptr<Variable> var); |
|
119 | void variableAdded(std::shared_ptr<Variable> var); | |
121 |
|
120 | |||
122 | protected: |
|
121 | protected: | |
123 | void closeEvent(QCloseEvent *event) override; |
|
122 | void closeEvent(QCloseEvent *event) override; | |
124 | void enterEvent(QEvent *event) override; |
|
123 | void enterEvent(QEvent *event) override; | |
125 | void leaveEvent(QEvent *event) override; |
|
124 | void leaveEvent(QEvent *event) override; | |
126 |
|
125 | |||
127 | QCustomPlot &plot() const noexcept; |
|
126 | QCustomPlot &plot() const noexcept; | |
128 |
|
127 | |||
129 | private: |
|
128 | private: | |
130 | Ui::VisualizationGraphWidget *ui; |
|
129 | Ui::VisualizationGraphWidget *ui; | |
131 |
|
130 | |||
132 | class VisualizationGraphWidgetPrivate; |
|
131 | class VisualizationGraphWidgetPrivate; | |
133 | spimpl::unique_impl_ptr<VisualizationGraphWidgetPrivate> impl; |
|
132 | spimpl::unique_impl_ptr<VisualizationGraphWidgetPrivate> impl; | |
134 |
|
133 | |||
135 | private slots: |
|
134 | private slots: | |
136 | /// Slot called when right clicking on the graph (displays a menu) |
|
135 | /// Slot called when right clicking on the graph (displays a menu) | |
137 | void onGraphMenuRequested(const QPoint &pos) noexcept; |
|
136 | void onGraphMenuRequested(const QPoint &pos) noexcept; | |
138 |
|
137 | |||
139 | /// Rescale the X axe to range parameter |
|
138 | /// Rescale the X axe to range parameter | |
140 | void onRangeChanged(const QCPRange &t1, const QCPRange &t2); |
|
139 | void onRangeChanged(const QCPRange &t1, const QCPRange &t2); | |
141 |
|
140 | |||
142 | /// Slot called when a mouse double click was made |
|
141 | /// Slot called when a mouse double click was made | |
143 | void onMouseDoubleClick(QMouseEvent *event) noexcept; |
|
142 | void onMouseDoubleClick(QMouseEvent *event) noexcept; | |
144 | /// Slot called when a mouse move was made |
|
143 | /// Slot called when a mouse move was made | |
145 | void onMouseMove(QMouseEvent *event) noexcept; |
|
144 | void onMouseMove(QMouseEvent *event) noexcept; | |
146 | /// Slot called when a mouse wheel was made, to perform some processing before the zoom is done |
|
145 | /// Slot called when a mouse wheel was made, to perform some processing before the zoom is done | |
147 | void onMouseWheel(QWheelEvent *event) noexcept; |
|
146 | void onMouseWheel(QWheelEvent *event) noexcept; | |
148 | /// Slot called when a mouse press was made, to activate the calibration of a graph |
|
147 | /// Slot called when a mouse press was made, to activate the calibration of a graph | |
149 | void onMousePress(QMouseEvent *event) noexcept; |
|
148 | void onMousePress(QMouseEvent *event) noexcept; | |
150 | /// Slot called when a mouse release was made, to deactivate the calibration of a graph |
|
149 | /// Slot called when a mouse release was made, to deactivate the calibration of a graph | |
151 | void onMouseRelease(QMouseEvent *event) noexcept; |
|
150 | void onMouseRelease(QMouseEvent *event) noexcept; | |
152 |
|
151 | |||
153 | void onDataCacheVariableUpdated(); |
|
152 | void onDataCacheVariableUpdated(); | |
154 |
|
153 | |||
155 | void onUpdateVarDisplaying(std::shared_ptr<Variable> variable, const DateTimeRange &range); |
|
154 | void onUpdateVarDisplaying(std::shared_ptr<Variable> variable, const DateTimeRange &range); | |
156 | }; |
|
155 | }; | |
157 |
|
156 | |||
158 | #endif // SCIQLOP_VISUALIZATIONGRAPHWIDGET_H |
|
157 | #endif // SCIQLOP_VISUALIZATIONGRAPHWIDGET_H |
@@ -1,199 +1,196 | |||||
1 | #include "SqpApplication.h" |
|
1 | #include "SqpApplication.h" | |
2 |
|
2 | |||
3 | #include <Actions/ActionsGuiController.h> |
|
3 | #include <Actions/ActionsGuiController.h> | |
4 | #include <Catalogue/CatalogueController.h> |
|
4 | #include <Catalogue/CatalogueController.h> | |
5 | #include <Data/IDataProvider.h> |
|
5 | #include <Data/IDataProvider.h> | |
6 | #include <DataSource/DataSourceController.h> |
|
6 | #include <DataSource/DataSourceController.h> | |
7 | #include <DragAndDrop/DragDropGuiController.h> |
|
7 | #include <DragAndDrop/DragDropGuiController.h> | |
8 | #include <Network/NetworkController.h> |
|
8 | #include <Network/NetworkController.h> | |
9 | #include <QThread> |
|
9 | #include <QThread> | |
10 | #include <Time/TimeController.h> |
|
10 | #include <Time/TimeController.h> | |
11 | #include <Variable/Variable.h> |
|
11 | #include <Variable/Variable.h> | |
12 | #include <Variable/VariableController2.h> |
|
12 | #include <Variable/VariableController2.h> | |
13 | #include <Variable/VariableModel2.h> |
|
13 | #include <Variable/VariableModel2.h> | |
14 | #include <Visualization/VisualizationController.h> |
|
14 | #include <Visualization/VisualizationController.h> | |
15 |
|
15 | |||
16 | Q_LOGGING_CATEGORY(LOG_SqpApplication, "SqpApplication") |
|
16 | Q_LOGGING_CATEGORY(LOG_SqpApplication, "SqpApplication") | |
17 |
|
17 | |||
18 | class SqpApplication::SqpApplicationPrivate { |
|
18 | class SqpApplication::SqpApplicationPrivate { | |
19 | public: |
|
19 | public: | |
20 | SqpApplicationPrivate() |
|
20 | SqpApplicationPrivate() | |
21 | : m_VariableController{std::make_shared<VariableController2>()}, |
|
21 | : m_VariableController{std::make_shared<VariableController2>()}, | |
22 | m_PlotInterractionMode(SqpApplication::PlotsInteractionMode::None), |
|
22 | m_PlotInterractionMode(SqpApplication::PlotsInteractionMode::None), | |
23 | m_PlotCursorMode(SqpApplication::PlotsCursorMode::NoCursor) |
|
23 | m_PlotCursorMode(SqpApplication::PlotsCursorMode::NoCursor) | |
24 | { |
|
24 | { | |
25 | // /////////////////////////////// // |
|
25 | // /////////////////////////////// // | |
26 | // Connections between controllers // |
|
26 | // Connections between controllers // | |
27 | // /////////////////////////////// // |
|
27 | // /////////////////////////////// // | |
28 |
|
28 | |||
29 | // VariableController <-> DataSourceController |
|
29 | // VariableController <-> DataSourceController | |
30 | connect(&m_DataSourceController, |
|
30 | connect(&m_DataSourceController, | |
31 | &DataSourceController::createVariable,[](const QString &variableName, |
|
31 | &DataSourceController::createVariable,[](const QString &variableName, | |
32 | const QVariantHash &variableMetadata, |
|
32 | const QVariantHash &variableMetadata, | |
33 | std::shared_ptr<IDataProvider> variableProvider) |
|
33 | std::shared_ptr<IDataProvider> variableProvider) | |
34 | { |
|
34 | { | |
35 | sqpApp->variableController().createVariable(variableName,variableMetadata,variableProvider,sqpApp->timeController().dateTime()); |
|
35 | sqpApp->variableController().createVariable(variableName,variableMetadata,variableProvider,sqpApp->timeController().dateTime()); | |
36 | }); |
|
36 | }); | |
37 |
|
37 | |||
38 | // connect(m_VariableController->variableModel(), &VariableModel::requestVariable, |
|
|||
39 | // m_DataSourceController.get(), &DataSourceController::requestVariable); |
|
|||
40 |
|
||||
41 | // VariableController <-> VisualizationController |
|
38 | // VariableController <-> VisualizationController | |
42 | // connect(m_VariableController.get(), |
|
39 | // connect(m_VariableController.get(), | |
43 | // SIGNAL(variableAboutToBeDeleted(std::shared_ptr<Variable>)), |
|
40 | // SIGNAL(variableAboutToBeDeleted(std::shared_ptr<Variable>)), | |
44 | // m_VisualizationController.get(), |
|
41 | // m_VisualizationController.get(), | |
45 | // SIGNAL(variableAboutToBeDeleted(std::shared_ptr<Variable>)), Qt::DirectConnection); |
|
42 | // SIGNAL(variableAboutToBeDeleted(std::shared_ptr<Variable>)), Qt::DirectConnection); | |
46 |
|
43 | |||
47 | // connect(m_VariableController.get(), |
|
44 | // connect(m_VariableController.get(), | |
48 | // SIGNAL(rangeChanged(std::shared_ptr<Variable>, const DateTimeRange &)), |
|
45 | // SIGNAL(rangeChanged(std::shared_ptr<Variable>, const DateTimeRange &)), | |
49 | // m_VisualizationController.get(), |
|
46 | // m_VisualizationController.get(), | |
50 | // SIGNAL(rangeChanged(std::shared_ptr<Variable>, const DateTimeRange &))); |
|
47 | // SIGNAL(rangeChanged(std::shared_ptr<Variable>, const DateTimeRange &))); | |
51 |
|
48 | |||
52 |
|
49 | |||
53 | m_DataSourceController.moveToThread(&m_DataSourceControllerThread); |
|
50 | m_DataSourceController.moveToThread(&m_DataSourceControllerThread); | |
54 | m_DataSourceControllerThread.setObjectName("DataSourceControllerThread"); |
|
51 | m_DataSourceControllerThread.setObjectName("DataSourceControllerThread"); | |
55 | m_NetworkController.moveToThread(&m_NetworkControllerThread); |
|
52 | m_NetworkController.moveToThread(&m_NetworkControllerThread); | |
56 | m_NetworkControllerThread.setObjectName("NetworkControllerThread"); |
|
53 | m_NetworkControllerThread.setObjectName("NetworkControllerThread"); | |
57 | m_VisualizationController.moveToThread(&m_VisualizationControllerThread); |
|
54 | m_VisualizationController.moveToThread(&m_VisualizationControllerThread); | |
58 | m_VisualizationControllerThread.setObjectName("VsualizationControllerThread"); |
|
55 | m_VisualizationControllerThread.setObjectName("VsualizationControllerThread"); | |
59 |
|
56 | |||
60 | // Additionnal init |
|
57 | // Additionnal init | |
61 | //m_VariableController->setTimeController(m_TimeController.get()); |
|
58 | //m_VariableController->setTimeController(m_TimeController.get()); | |
62 | } |
|
59 | } | |
63 |
|
60 | |||
64 | virtual ~SqpApplicationPrivate() |
|
61 | virtual ~SqpApplicationPrivate() | |
65 | { |
|
62 | { | |
66 | m_DataSourceControllerThread.quit(); |
|
63 | m_DataSourceControllerThread.quit(); | |
67 | m_DataSourceControllerThread.wait(); |
|
64 | m_DataSourceControllerThread.wait(); | |
68 |
|
65 | |||
69 | m_NetworkControllerThread.quit(); |
|
66 | m_NetworkControllerThread.quit(); | |
70 | m_NetworkControllerThread.wait(); |
|
67 | m_NetworkControllerThread.wait(); | |
71 |
|
68 | |||
72 | m_VisualizationControllerThread.quit(); |
|
69 | m_VisualizationControllerThread.quit(); | |
73 | m_VisualizationControllerThread.wait(); |
|
70 | m_VisualizationControllerThread.wait(); | |
74 | } |
|
71 | } | |
75 |
|
72 | |||
76 | DataSourceController m_DataSourceController; |
|
73 | DataSourceController m_DataSourceController; | |
77 | std::shared_ptr<VariableController2> m_VariableController; |
|
74 | std::shared_ptr<VariableController2> m_VariableController; | |
78 | TimeController m_TimeController; |
|
75 | TimeController m_TimeController; | |
79 | NetworkController m_NetworkController; |
|
76 | NetworkController m_NetworkController; | |
80 | VisualizationController m_VisualizationController; |
|
77 | VisualizationController m_VisualizationController; | |
81 | CatalogueController m_CatalogueController; |
|
78 | CatalogueController m_CatalogueController; | |
82 |
|
79 | |||
83 | QThread m_DataSourceControllerThread; |
|
80 | QThread m_DataSourceControllerThread; | |
84 | QThread m_NetworkControllerThread; |
|
81 | QThread m_NetworkControllerThread; | |
85 | QThread m_VisualizationControllerThread; |
|
82 | QThread m_VisualizationControllerThread; | |
86 |
|
83 | |||
87 | DragDropGuiController m_DragDropGuiController; |
|
84 | DragDropGuiController m_DragDropGuiController; | |
88 | ActionsGuiController m_ActionsGuiController; |
|
85 | ActionsGuiController m_ActionsGuiController; | |
89 |
|
86 | |||
90 | SqpApplication::PlotsInteractionMode m_PlotInterractionMode; |
|
87 | SqpApplication::PlotsInteractionMode m_PlotInterractionMode; | |
91 | SqpApplication::PlotsCursorMode m_PlotCursorMode; |
|
88 | SqpApplication::PlotsCursorMode m_PlotCursorMode; | |
92 | }; |
|
89 | }; | |
93 |
|
90 | |||
94 |
|
91 | |||
95 | SqpApplication::SqpApplication(int &argc, char **argv) |
|
92 | SqpApplication::SqpApplication(int &argc, char **argv) | |
96 | : QApplication{argc, argv}, impl{spimpl::make_unique_impl<SqpApplicationPrivate>()} |
|
93 | : QApplication{argc, argv}, impl{spimpl::make_unique_impl<SqpApplicationPrivate>()} | |
97 | { |
|
94 | { | |
98 | qCDebug(LOG_SqpApplication()) << tr("SqpApplication construction") << QThread::currentThread(); |
|
95 | qCDebug(LOG_SqpApplication()) << tr("SqpApplication construction") << QThread::currentThread(); | |
99 |
|
96 | |||
100 | QGuiApplication::setAttribute(Qt::AA_EnableHighDpiScaling); |
|
97 | QGuiApplication::setAttribute(Qt::AA_EnableHighDpiScaling); | |
101 |
|
98 | |||
102 | connect(&impl->m_DataSourceControllerThread, &QThread::started, |
|
99 | connect(&impl->m_DataSourceControllerThread, &QThread::started, | |
103 | &impl->m_DataSourceController, &DataSourceController::initialize); |
|
100 | &impl->m_DataSourceController, &DataSourceController::initialize); | |
104 | connect(&impl->m_DataSourceControllerThread, &QThread::finished, |
|
101 | connect(&impl->m_DataSourceControllerThread, &QThread::finished, | |
105 | &impl->m_DataSourceController, &DataSourceController::finalize); |
|
102 | &impl->m_DataSourceController, &DataSourceController::finalize); | |
106 |
|
103 | |||
107 | connect(&impl->m_NetworkControllerThread, &QThread::started, &impl->m_NetworkController, |
|
104 | connect(&impl->m_NetworkControllerThread, &QThread::started, &impl->m_NetworkController, | |
108 | &NetworkController::initialize); |
|
105 | &NetworkController::initialize); | |
109 | connect(&impl->m_NetworkControllerThread, &QThread::finished, &impl->m_NetworkController, |
|
106 | connect(&impl->m_NetworkControllerThread, &QThread::finished, &impl->m_NetworkController, | |
110 | &NetworkController::finalize); |
|
107 | &NetworkController::finalize); | |
111 |
|
108 | |||
112 | connect(&impl->m_VisualizationControllerThread, &QThread::started, |
|
109 | connect(&impl->m_VisualizationControllerThread, &QThread::started, | |
113 | &impl->m_VisualizationController, &VisualizationController::initialize); |
|
110 | &impl->m_VisualizationController, &VisualizationController::initialize); | |
114 | connect(&impl->m_VisualizationControllerThread, &QThread::finished, |
|
111 | connect(&impl->m_VisualizationControllerThread, &QThread::finished, | |
115 | &impl->m_VisualizationController, &VisualizationController::finalize); |
|
112 | &impl->m_VisualizationController, &VisualizationController::finalize); | |
116 |
|
113 | |||
117 | impl->m_DataSourceControllerThread.start(); |
|
114 | impl->m_DataSourceControllerThread.start(); | |
118 | impl->m_NetworkControllerThread.start(); |
|
115 | impl->m_NetworkControllerThread.start(); | |
119 | impl->m_VisualizationControllerThread.start(); |
|
116 | impl->m_VisualizationControllerThread.start(); | |
120 | impl->m_CatalogueController.initialize(); |
|
117 | impl->m_CatalogueController.initialize(); | |
121 | } |
|
118 | } | |
122 |
|
119 | |||
123 | SqpApplication::~SqpApplication() |
|
120 | SqpApplication::~SqpApplication() | |
124 | { |
|
121 | { | |
125 | } |
|
122 | } | |
126 |
|
123 | |||
127 | void SqpApplication::initialize() |
|
124 | void SqpApplication::initialize() | |
128 | { |
|
125 | { | |
129 | } |
|
126 | } | |
130 |
|
127 | |||
131 | DataSourceController &SqpApplication::dataSourceController() noexcept |
|
128 | DataSourceController &SqpApplication::dataSourceController() noexcept | |
132 | { |
|
129 | { | |
133 | return impl->m_DataSourceController; |
|
130 | return impl->m_DataSourceController; | |
134 | } |
|
131 | } | |
135 |
|
132 | |||
136 | NetworkController &SqpApplication::networkController() noexcept |
|
133 | NetworkController &SqpApplication::networkController() noexcept | |
137 | { |
|
134 | { | |
138 | return impl->m_NetworkController; |
|
135 | return impl->m_NetworkController; | |
139 | } |
|
136 | } | |
140 |
|
137 | |||
141 | TimeController &SqpApplication::timeController() noexcept |
|
138 | TimeController &SqpApplication::timeController() noexcept | |
142 | { |
|
139 | { | |
143 | return impl->m_TimeController; |
|
140 | return impl->m_TimeController; | |
144 | } |
|
141 | } | |
145 |
|
142 | |||
146 | VariableController2 &SqpApplication::variableController() noexcept |
|
143 | VariableController2 &SqpApplication::variableController() noexcept | |
147 | { |
|
144 | { | |
148 | return *impl->m_VariableController; |
|
145 | return *impl->m_VariableController; | |
149 | } |
|
146 | } | |
150 |
|
147 | |||
151 | std::shared_ptr<VariableController2> SqpApplication::variableControllerOwner() noexcept |
|
148 | std::shared_ptr<VariableController2> SqpApplication::variableControllerOwner() noexcept | |
152 | { |
|
149 | { | |
153 | return impl->m_VariableController; |
|
150 | return impl->m_VariableController; | |
154 | } |
|
151 | } | |
155 |
|
152 | |||
156 | //VariableModel2 &SqpApplication::variableModel() noexcept |
|
153 | //VariableModel2 &SqpApplication::variableModel() noexcept | |
157 | //{ |
|
154 | //{ | |
158 | // return impl->m_VariableModel; |
|
155 | // return impl->m_VariableModel; | |
159 | //} |
|
156 | //} | |
160 |
|
157 | |||
161 | VisualizationController &SqpApplication::visualizationController() noexcept |
|
158 | VisualizationController &SqpApplication::visualizationController() noexcept | |
162 | { |
|
159 | { | |
163 | return impl->m_VisualizationController; |
|
160 | return impl->m_VisualizationController; | |
164 | } |
|
161 | } | |
165 |
|
162 | |||
166 | CatalogueController &SqpApplication::catalogueController() noexcept |
|
163 | CatalogueController &SqpApplication::catalogueController() noexcept | |
167 | { |
|
164 | { | |
168 | return impl->m_CatalogueController; |
|
165 | return impl->m_CatalogueController; | |
169 | } |
|
166 | } | |
170 |
|
167 | |||
171 | DragDropGuiController &SqpApplication::dragDropGuiController() noexcept |
|
168 | DragDropGuiController &SqpApplication::dragDropGuiController() noexcept | |
172 | { |
|
169 | { | |
173 | return impl->m_DragDropGuiController; |
|
170 | return impl->m_DragDropGuiController; | |
174 | } |
|
171 | } | |
175 |
|
172 | |||
176 | ActionsGuiController &SqpApplication::actionsGuiController() noexcept |
|
173 | ActionsGuiController &SqpApplication::actionsGuiController() noexcept | |
177 | { |
|
174 | { | |
178 | return impl->m_ActionsGuiController; |
|
175 | return impl->m_ActionsGuiController; | |
179 | } |
|
176 | } | |
180 |
|
177 | |||
181 | SqpApplication::PlotsInteractionMode SqpApplication::plotsInteractionMode() const |
|
178 | SqpApplication::PlotsInteractionMode SqpApplication::plotsInteractionMode() const | |
182 | { |
|
179 | { | |
183 | return impl->m_PlotInterractionMode; |
|
180 | return impl->m_PlotInterractionMode; | |
184 | } |
|
181 | } | |
185 |
|
182 | |||
186 | void SqpApplication::setPlotsInteractionMode(SqpApplication::PlotsInteractionMode mode) |
|
183 | void SqpApplication::setPlotsInteractionMode(SqpApplication::PlotsInteractionMode mode) | |
187 | { |
|
184 | { | |
188 | impl->m_PlotInterractionMode = mode; |
|
185 | impl->m_PlotInterractionMode = mode; | |
189 | } |
|
186 | } | |
190 |
|
187 | |||
191 | SqpApplication::PlotsCursorMode SqpApplication::plotsCursorMode() const |
|
188 | SqpApplication::PlotsCursorMode SqpApplication::plotsCursorMode() const | |
192 | { |
|
189 | { | |
193 | return impl->m_PlotCursorMode; |
|
190 | return impl->m_PlotCursorMode; | |
194 | } |
|
191 | } | |
195 |
|
192 | |||
196 | void SqpApplication::setPlotsCursorMode(SqpApplication::PlotsCursorMode mode) |
|
193 | void SqpApplication::setPlotsCursorMode(SqpApplication::PlotsCursorMode mode) | |
197 | { |
|
194 | { | |
198 | impl->m_PlotCursorMode = mode; |
|
195 | impl->m_PlotCursorMode = mode; | |
199 | } |
|
196 | } |
@@ -1,155 +1,156 | |||||
1 | #include "TimeWidget/TimeWidget.h" |
|
1 | #include "TimeWidget/TimeWidget.h" | |
2 | #include "ui_TimeWidget.h" |
|
2 | #include "ui_TimeWidget.h" | |
3 |
|
3 | |||
4 | #include <Common/DateUtils.h> |
|
4 | #include <Common/DateUtils.h> | |
5 | #include <Common/MimeTypesDef.h> |
|
5 | #include <Common/MimeTypesDef.h> | |
6 |
|
6 | |||
7 | #include <DragAndDrop/DragDropGuiController.h> |
|
7 | #include <DragAndDrop/DragDropGuiController.h> | |
8 | #include <SqpApplication.h> |
|
8 | #include <SqpApplication.h> | |
9 | #include <Time/TimeController.h> |
|
9 | #include <Time/TimeController.h> | |
10 |
|
10 | |||
11 | #include <QDrag> |
|
11 | #include <QDrag> | |
12 | #include <QDragEnterEvent> |
|
12 | #include <QDragEnterEvent> | |
13 | #include <QDropEvent> |
|
13 | #include <QDropEvent> | |
14 | #include <QMimeData> |
|
14 | #include <QMimeData> | |
15 | #include <QStyle> |
|
15 | #include <QStyle> | |
16 |
|
16 | |||
17 |
|
17 | |||
18 | struct TimeWidget::TimeWidgetPrivate { |
|
18 | struct TimeWidget::TimeWidgetPrivate { | |
19 |
|
19 | |||
20 | explicit TimeWidgetPrivate() {} |
|
20 | explicit TimeWidgetPrivate() {} | |
21 |
|
21 | |||
22 | QPoint m_DragStartPosition; |
|
22 | QPoint m_DragStartPosition; | |
23 | }; |
|
23 | }; | |
24 |
|
24 | |||
25 | TimeWidget::TimeWidget(QWidget *parent) |
|
25 | TimeWidget::TimeWidget(QWidget *parent) | |
26 | : QWidget{parent}, |
|
26 | : QWidget{parent}, | |
27 | ui{new Ui::TimeWidget}, |
|
27 | ui{new Ui::TimeWidget}, | |
28 | impl{spimpl::make_unique_impl<TimeWidgetPrivate>()} |
|
28 | impl{spimpl::make_unique_impl<TimeWidgetPrivate>()} | |
29 | { |
|
29 | { | |
30 | ui->setupUi(this); |
|
30 | ui->setupUi(this); | |
31 |
|
31 | |||
32 | ui->applyToolButton->setIcon(sqpApp->style()->standardIcon(QStyle::SP_DialogApplyButton)); |
|
32 | ui->applyToolButton->setIcon(sqpApp->style()->standardIcon(QStyle::SP_DialogApplyButton)); | |
33 |
|
33 | |||
34 | // Connection |
|
34 | // Connection | |
35 | connect(ui->startDateTimeEdit, &QDateTimeEdit::dateTimeChanged, this, |
|
35 | connect(ui->startDateTimeEdit, &QDateTimeEdit::dateTimeChanged, this, | |
36 | &TimeWidget::onTimeUpdateRequested); |
|
36 | &TimeWidget::onTimeUpdateRequested); | |
37 |
|
37 | |||
38 | connect(ui->endDateTimeEdit, &QDateTimeEdit::dateTimeChanged, this, |
|
38 | connect(ui->endDateTimeEdit, &QDateTimeEdit::dateTimeChanged, this, | |
39 | &TimeWidget::onTimeUpdateRequested); |
|
39 | &TimeWidget::onTimeUpdateRequested); | |
40 |
|
40 | |||
41 |
|
41 | |||
42 | connect(ui->applyToolButton, &QToolButton::clicked, &sqpApp->timeController(), |
|
42 | connect(ui->applyToolButton, &QToolButton::clicked, &sqpApp->timeController(), | |
43 | &TimeController::onTimeNotify); |
|
43 | &TimeController::onTimeNotify); | |
44 |
|
44 | |||
45 | // Initialisation |
|
45 | // Initialisation | |
46 | auto endDateTime = QDateTime::currentDateTimeUtc(); |
|
46 | auto endDateTime = QDateTime::currentDateTimeUtc(); | |
47 | auto startDateTime = endDateTime.addSecs(-3600); // one hour before |
|
47 | auto startDateTime = endDateTime.addSecs(-3600); // one hour before | |
48 |
|
48 | |||
49 | ui->startDateTimeEdit->setDateTime(startDateTime); |
|
49 | ui->startDateTimeEdit->setDateTime(startDateTime); | |
50 | ui->endDateTimeEdit->setDateTime(endDateTime); |
|
50 | ui->endDateTimeEdit->setDateTime(endDateTime); | |
51 |
|
51 | |||
52 | auto dateTime = DateTimeRange{DateUtils::secondsSinceEpoch(startDateTime), |
|
52 | auto dateTime = DateTimeRange{DateUtils::secondsSinceEpoch(startDateTime), | |
53 | DateUtils::secondsSinceEpoch(endDateTime)}; |
|
53 | DateUtils::secondsSinceEpoch(endDateTime)}; | |
54 |
|
54 | |||
55 | sqpApp->timeController().setDateTimeRange(dateTime); |
|
55 | sqpApp->timeController().setDateTimeRange(dateTime); | |
56 | } |
|
56 | } | |
57 |
|
57 | |||
58 |
|
58 | |||
59 | TimeWidget::~TimeWidget() |
|
59 | TimeWidget::~TimeWidget() | |
60 | { |
|
60 | { | |
61 | delete ui; |
|
61 | delete ui; | |
62 | } |
|
62 | } | |
63 |
|
63 | |||
64 | void TimeWidget::setTimeRange(DateTimeRange time) |
|
64 | void TimeWidget::setTimeRange(DateTimeRange time) | |
65 | { |
|
65 | { | |
66 | auto startDateTime = DateUtils::dateTime(time.m_TStart); |
|
66 | auto startDateTime = DateUtils::dateTime(time.m_TStart); | |
67 | auto endDateTime = DateUtils::dateTime(time.m_TEnd); |
|
67 | auto endDateTime = DateUtils::dateTime(time.m_TEnd); | |
68 |
|
68 | |||
69 | ui->startDateTimeEdit->setDateTime(startDateTime); |
|
69 | ui->startDateTimeEdit->setDateTime(startDateTime); | |
70 | ui->endDateTimeEdit->setDateTime(endDateTime); |
|
70 | ui->endDateTimeEdit->setDateTime(endDateTime); | |
71 | } |
|
71 | } | |
72 |
|
72 | |||
73 | DateTimeRange TimeWidget::timeRange() const |
|
73 | DateTimeRange TimeWidget::timeRange() const | |
74 | { |
|
74 | { | |
75 | return DateTimeRange{DateUtils::secondsSinceEpoch(ui->startDateTimeEdit->dateTime()), |
|
75 | return DateTimeRange{DateUtils::secondsSinceEpoch(ui->startDateTimeEdit->dateTime()), | |
76 | DateUtils::secondsSinceEpoch(ui->endDateTimeEdit->dateTime())}; |
|
76 | DateUtils::secondsSinceEpoch(ui->endDateTimeEdit->dateTime())}; | |
77 | } |
|
77 | } | |
78 |
|
78 | |||
79 | void TimeWidget::onTimeUpdateRequested() |
|
79 | void TimeWidget::onTimeUpdateRequested() | |
80 | { |
|
80 | { | |
81 | auto dateTime = timeRange(); |
|
81 | auto dateTime = timeRange(); | |
82 | emit timeUpdated(std::move(dateTime)); |
|
82 | emit timeUpdated(std::move(dateTime)); | |
|
83 | sqpApp->timeController().setDateTimeRange(dateTime); | |||
83 | } |
|
84 | } | |
84 |
|
85 | |||
85 | void TimeWidget::dragEnterEvent(QDragEnterEvent *event) |
|
86 | void TimeWidget::dragEnterEvent(QDragEnterEvent *event) | |
86 | { |
|
87 | { | |
87 | if (event->mimeData()->hasFormat(MIME_TYPE_TIME_RANGE)) { |
|
88 | if (event->mimeData()->hasFormat(MIME_TYPE_TIME_RANGE)) { | |
88 | event->acceptProposedAction(); |
|
89 | event->acceptProposedAction(); | |
89 | setStyleSheet("QDateTimeEdit{background-color: #BBD5EE; border:2px solid #2A7FD4}"); |
|
90 | setStyleSheet("QDateTimeEdit{background-color: #BBD5EE; border:2px solid #2A7FD4}"); | |
90 | } |
|
91 | } | |
91 | else { |
|
92 | else { | |
92 | event->ignore(); |
|
93 | event->ignore(); | |
93 | } |
|
94 | } | |
94 | } |
|
95 | } | |
95 |
|
96 | |||
96 | void TimeWidget::dragLeaveEvent(QDragLeaveEvent *event) |
|
97 | void TimeWidget::dragLeaveEvent(QDragLeaveEvent *event) | |
97 | { |
|
98 | { | |
98 | setStyleSheet(QString{}); |
|
99 | setStyleSheet(QString{}); | |
99 | } |
|
100 | } | |
100 |
|
101 | |||
101 | void TimeWidget::dropEvent(QDropEvent *event) |
|
102 | void TimeWidget::dropEvent(QDropEvent *event) | |
102 | { |
|
103 | { | |
103 | if (event->mimeData()->hasFormat(MIME_TYPE_TIME_RANGE)) { |
|
104 | if (event->mimeData()->hasFormat(MIME_TYPE_TIME_RANGE)) { | |
104 | auto mimeData = event->mimeData()->data(MIME_TYPE_TIME_RANGE); |
|
105 | auto mimeData = event->mimeData()->data(MIME_TYPE_TIME_RANGE); | |
105 | auto timeRange = TimeController::timeRangeForMimeData(mimeData); |
|
106 | auto timeRange = TimeController::timeRangeForMimeData(mimeData); | |
106 |
|
107 | |||
107 | setTimeRange(timeRange); |
|
108 | setTimeRange(timeRange); | |
108 | } |
|
109 | } | |
109 | else { |
|
110 | else { | |
110 | event->ignore(); |
|
111 | event->ignore(); | |
111 | } |
|
112 | } | |
112 |
|
113 | |||
113 | setStyleSheet(QString{}); |
|
114 | setStyleSheet(QString{}); | |
114 | } |
|
115 | } | |
115 |
|
116 | |||
116 |
|
117 | |||
117 | void TimeWidget::mousePressEvent(QMouseEvent *event) |
|
118 | void TimeWidget::mousePressEvent(QMouseEvent *event) | |
118 | { |
|
119 | { | |
119 | if (event->button() == Qt::LeftButton) { |
|
120 | if (event->button() == Qt::LeftButton) { | |
120 | impl->m_DragStartPosition = event->pos(); |
|
121 | impl->m_DragStartPosition = event->pos(); | |
121 | } |
|
122 | } | |
122 |
|
123 | |||
123 | QWidget::mousePressEvent(event); |
|
124 | QWidget::mousePressEvent(event); | |
124 | } |
|
125 | } | |
125 |
|
126 | |||
126 | void TimeWidget::mouseMoveEvent(QMouseEvent *event) |
|
127 | void TimeWidget::mouseMoveEvent(QMouseEvent *event) | |
127 | { |
|
128 | { | |
128 | if (!(event->buttons() & Qt::LeftButton)) { |
|
129 | if (!(event->buttons() & Qt::LeftButton)) { | |
129 | return; |
|
130 | return; | |
130 | } |
|
131 | } | |
131 |
|
132 | |||
132 | if ((event->pos() - impl->m_DragStartPosition).manhattanLength() |
|
133 | if ((event->pos() - impl->m_DragStartPosition).manhattanLength() | |
133 | < QApplication::startDragDistance()) { |
|
134 | < QApplication::startDragDistance()) { | |
134 | return; |
|
135 | return; | |
135 | } |
|
136 | } | |
136 |
|
137 | |||
137 | // Note: The management of the drag object is done by Qt |
|
138 | // Note: The management of the drag object is done by Qt | |
138 | auto drag = new QDrag{this}; |
|
139 | auto drag = new QDrag{this}; | |
139 |
|
140 | |||
140 | auto mimeData = new QMimeData; |
|
141 | auto mimeData = new QMimeData; | |
141 | auto timeData = TimeController::mimeDataForTimeRange(timeRange()); |
|
142 | auto timeData = TimeController::mimeDataForTimeRange(timeRange()); | |
142 | mimeData->setData(MIME_TYPE_TIME_RANGE, timeData); |
|
143 | mimeData->setData(MIME_TYPE_TIME_RANGE, timeData); | |
143 |
|
144 | |||
144 | drag->setMimeData(mimeData); |
|
145 | drag->setMimeData(mimeData); | |
145 |
|
146 | |||
146 | auto pixmap = QPixmap{":/icones/time.png"}; |
|
147 | auto pixmap = QPixmap{":/icones/time.png"}; | |
147 | drag->setPixmap(pixmap.scaledToWidth(22)); |
|
148 | drag->setPixmap(pixmap.scaledToWidth(22)); | |
148 |
|
149 | |||
149 | sqpApp->dragDropGuiController().resetDragAndDrop(); |
|
150 | sqpApp->dragDropGuiController().resetDragAndDrop(); | |
150 |
|
151 | |||
151 | // Note: The exec() is blocking on windows but not on linux and macOS |
|
152 | // Note: The exec() is blocking on windows but not on linux and macOS | |
152 | drag->exec(Qt::MoveAction | Qt::CopyAction); |
|
153 | drag->exec(Qt::MoveAction | Qt::CopyAction); | |
153 |
|
154 | |||
154 | QWidget::mouseMoveEvent(event); |
|
155 | QWidget::mouseMoveEvent(event); | |
155 | } |
|
156 | } |
@@ -1,1083 +1,1042 | |||||
1 | #include "Visualization/VisualizationGraphWidget.h" |
|
1 | #include "Visualization/VisualizationGraphWidget.h" | |
2 | #include "Visualization/IVisualizationWidgetVisitor.h" |
|
2 | #include "Visualization/IVisualizationWidgetVisitor.h" | |
3 | #include "Visualization/VisualizationCursorItem.h" |
|
3 | #include "Visualization/VisualizationCursorItem.h" | |
4 | #include "Visualization/VisualizationDefs.h" |
|
4 | #include "Visualization/VisualizationDefs.h" | |
5 | #include "Visualization/VisualizationGraphHelper.h" |
|
5 | #include "Visualization/VisualizationGraphHelper.h" | |
6 | #include "Visualization/VisualizationGraphRenderingDelegate.h" |
|
6 | #include "Visualization/VisualizationGraphRenderingDelegate.h" | |
7 | #include "Visualization/VisualizationMultiZoneSelectionDialog.h" |
|
7 | #include "Visualization/VisualizationMultiZoneSelectionDialog.h" | |
8 | #include "Visualization/VisualizationSelectionZoneItem.h" |
|
8 | #include "Visualization/VisualizationSelectionZoneItem.h" | |
9 | #include "Visualization/VisualizationSelectionZoneManager.h" |
|
9 | #include "Visualization/VisualizationSelectionZoneManager.h" | |
10 | #include "Visualization/VisualizationWidget.h" |
|
10 | #include "Visualization/VisualizationWidget.h" | |
11 | #include "Visualization/VisualizationZoneWidget.h" |
|
11 | #include "Visualization/VisualizationZoneWidget.h" | |
12 | #include "ui_VisualizationGraphWidget.h" |
|
12 | #include "ui_VisualizationGraphWidget.h" | |
13 |
|
13 | |||
14 | #include <Actions/ActionsGuiController.h> |
|
14 | #include <Actions/ActionsGuiController.h> | |
15 | #include <Actions/FilteringAction.h> |
|
15 | #include <Actions/FilteringAction.h> | |
16 | #include <Common/MimeTypesDef.h> |
|
16 | #include <Common/MimeTypesDef.h> | |
17 | #include <Data/ArrayData.h> |
|
17 | #include <Data/ArrayData.h> | |
18 | #include <Data/IDataSeries.h> |
|
18 | #include <Data/IDataSeries.h> | |
19 | #include <Data/SpectrogramSeries.h> |
|
19 | #include <Data/SpectrogramSeries.h> | |
20 | #include <DragAndDrop/DragDropGuiController.h> |
|
20 | #include <DragAndDrop/DragDropGuiController.h> | |
21 | #include <Settings/SqpSettingsDefs.h> |
|
21 | #include <Settings/SqpSettingsDefs.h> | |
22 | #include <SqpApplication.h> |
|
22 | #include <SqpApplication.h> | |
23 | #include <Time/TimeController.h> |
|
23 | #include <Time/TimeController.h> | |
24 | #include <Variable/Variable.h> |
|
24 | #include <Variable/Variable.h> | |
25 | #include <Variable/VariableController2.h> |
|
25 | #include <Variable/VariableController2.h> | |
26 |
|
26 | |||
27 | #include <unordered_map> |
|
27 | #include <unordered_map> | |
28 |
|
28 | |||
29 | Q_LOGGING_CATEGORY(LOG_VisualizationGraphWidget, "VisualizationGraphWidget") |
|
29 | Q_LOGGING_CATEGORY(LOG_VisualizationGraphWidget, "VisualizationGraphWidget") | |
30 |
|
30 | |||
31 | namespace { |
|
31 | namespace { | |
32 |
|
32 | |||
33 | /// Key pressed to enable drag&drop in all modes |
|
33 | /// Key pressed to enable drag&drop in all modes | |
34 | const auto DRAG_DROP_MODIFIER = Qt::AltModifier; |
|
34 | const auto DRAG_DROP_MODIFIER = Qt::AltModifier; | |
35 |
|
35 | |||
36 | /// Key pressed to enable zoom on horizontal axis |
|
36 | /// Key pressed to enable zoom on horizontal axis | |
37 | const auto HORIZONTAL_ZOOM_MODIFIER = Qt::ControlModifier; |
|
37 | const auto HORIZONTAL_ZOOM_MODIFIER = Qt::ControlModifier; | |
38 |
|
38 | |||
39 | /// Key pressed to enable zoom on vertical axis |
|
39 | /// Key pressed to enable zoom on vertical axis | |
40 | const auto VERTICAL_ZOOM_MODIFIER = Qt::ShiftModifier; |
|
40 | const auto VERTICAL_ZOOM_MODIFIER = Qt::ShiftModifier; | |
41 |
|
41 | |||
42 | /// Speed of a step of a wheel event for a pan, in percentage of the axis range |
|
42 | /// Speed of a step of a wheel event for a pan, in percentage of the axis range | |
43 | const auto PAN_SPEED = 5; |
|
43 | const auto PAN_SPEED = 5; | |
44 |
|
44 | |||
45 | /// Key pressed to enable a calibration pan |
|
45 | /// Key pressed to enable a calibration pan | |
46 | const auto VERTICAL_PAN_MODIFIER = Qt::AltModifier; |
|
46 | const auto VERTICAL_PAN_MODIFIER = Qt::AltModifier; | |
47 |
|
47 | |||
48 | /// Key pressed to enable multi selection of selection zones |
|
48 | /// Key pressed to enable multi selection of selection zones | |
49 | const auto MULTI_ZONE_SELECTION_MODIFIER = Qt::ControlModifier; |
|
49 | const auto MULTI_ZONE_SELECTION_MODIFIER = Qt::ControlModifier; | |
50 |
|
50 | |||
51 | /// Minimum size for the zoom box, in percentage of the axis range |
|
51 | /// Minimum size for the zoom box, in percentage of the axis range | |
52 | const auto ZOOM_BOX_MIN_SIZE = 0.8; |
|
52 | const auto ZOOM_BOX_MIN_SIZE = 0.8; | |
53 |
|
53 | |||
54 | /// Format of the dates appearing in the label of a cursor |
|
54 | /// Format of the dates appearing in the label of a cursor | |
55 | const auto CURSOR_LABELS_DATETIME_FORMAT = QStringLiteral("yyyy/MM/dd\nhh:mm:ss:zzz"); |
|
55 | const auto CURSOR_LABELS_DATETIME_FORMAT = QStringLiteral("yyyy/MM/dd\nhh:mm:ss:zzz"); | |
56 |
|
56 | |||
57 | } // namespace |
|
57 | } // namespace | |
58 |
|
58 | |||
59 | struct VisualizationGraphWidget::VisualizationGraphWidgetPrivate { |
|
59 | struct VisualizationGraphWidget::VisualizationGraphWidgetPrivate { | |
60 |
|
60 | |||
61 | explicit VisualizationGraphWidgetPrivate(const QString &name) |
|
61 | explicit VisualizationGraphWidgetPrivate(const QString &name) | |
62 | : m_Name{name}, |
|
62 | : m_Name{name}, | |
63 | m_Flags{GraphFlag::EnableAll}, |
|
63 | m_Flags{GraphFlag::EnableAll}, | |
64 | m_IsCalibration{false}, |
|
64 | m_IsCalibration{false}, | |
65 | m_RenderingDelegate{nullptr} |
|
65 | m_RenderingDelegate{nullptr} | |
66 | { |
|
66 | { | |
67 | } |
|
67 | } | |
68 |
|
68 | |||
69 | void updateData(PlottablesMap &plottables, std::shared_ptr<Variable> variable, |
|
69 | void updateData(PlottablesMap &plottables, std::shared_ptr<Variable> variable, | |
70 | const DateTimeRange &range) |
|
70 | const DateTimeRange &range) | |
71 | { |
|
71 | { | |
72 | VisualizationGraphHelper::updateData(plottables, variable, range); |
|
72 | VisualizationGraphHelper::updateData(plottables, variable, range); | |
73 |
|
73 | |||
74 | // Prevents that data has changed to update rendering |
|
74 | // Prevents that data has changed to update rendering | |
75 | m_RenderingDelegate->onPlotUpdated(); |
|
75 | m_RenderingDelegate->onPlotUpdated(); | |
76 | } |
|
76 | } | |
77 |
|
77 | |||
78 | QString m_Name; |
|
78 | QString m_Name; | |
79 | // 1 variable -> n qcpplot |
|
79 | // 1 variable -> n qcpplot | |
80 | std::map<std::shared_ptr<Variable>, PlottablesMap> m_VariableToPlotMultiMap; |
|
80 | std::map<std::shared_ptr<Variable>, PlottablesMap> m_VariableToPlotMultiMap; | |
81 | GraphFlags m_Flags; |
|
81 | GraphFlags m_Flags; | |
82 | bool m_IsCalibration; |
|
82 | bool m_IsCalibration; | |
83 | /// Delegate used to attach rendering features to the plot |
|
83 | /// Delegate used to attach rendering features to the plot | |
84 | std::unique_ptr<VisualizationGraphRenderingDelegate> m_RenderingDelegate; |
|
84 | std::unique_ptr<VisualizationGraphRenderingDelegate> m_RenderingDelegate; | |
85 |
|
85 | |||
86 | QCPItemRect *m_DrawingZoomRect = nullptr; |
|
86 | QCPItemRect *m_DrawingZoomRect = nullptr; | |
87 | QStack<QPair<QCPRange, QCPRange> > m_ZoomStack; |
|
87 | QStack<QPair<QCPRange, QCPRange> > m_ZoomStack; | |
88 |
|
88 | |||
89 | std::unique_ptr<VisualizationCursorItem> m_HorizontalCursor = nullptr; |
|
89 | std::unique_ptr<VisualizationCursorItem> m_HorizontalCursor = nullptr; | |
90 | std::unique_ptr<VisualizationCursorItem> m_VerticalCursor = nullptr; |
|
90 | std::unique_ptr<VisualizationCursorItem> m_VerticalCursor = nullptr; | |
91 |
|
91 | |||
92 | VisualizationSelectionZoneItem *m_DrawingZone = nullptr; |
|
92 | VisualizationSelectionZoneItem *m_DrawingZone = nullptr; | |
93 | VisualizationSelectionZoneItem *m_HoveredZone = nullptr; |
|
93 | VisualizationSelectionZoneItem *m_HoveredZone = nullptr; | |
94 | QVector<VisualizationSelectionZoneItem *> m_SelectionZones; |
|
94 | QVector<VisualizationSelectionZoneItem *> m_SelectionZones; | |
95 |
|
95 | |||
96 | bool m_HasMovedMouse = false; // Indicates if the mouse moved in a releaseMouse even |
|
96 | bool m_HasMovedMouse = false; // Indicates if the mouse moved in a releaseMouse even | |
97 |
|
97 | |||
98 | bool m_VariableAutoRangeOnInit = true; |
|
98 | bool m_VariableAutoRangeOnInit = true; | |
99 |
|
99 | |||
100 | void startDrawingRect(const QPoint &pos, QCustomPlot &plot) |
|
100 | void startDrawingRect(const QPoint &pos, QCustomPlot &plot) | |
101 | { |
|
101 | { | |
102 | removeDrawingRect(plot); |
|
102 | removeDrawingRect(plot); | |
103 |
|
103 | |||
104 | auto axisPos = posToAxisPos(pos, plot); |
|
104 | auto axisPos = posToAxisPos(pos, plot); | |
105 |
|
105 | |||
106 | m_DrawingZoomRect = new QCPItemRect{&plot}; |
|
106 | m_DrawingZoomRect = new QCPItemRect{&plot}; | |
107 | QPen p; |
|
107 | QPen p; | |
108 | p.setWidth(2); |
|
108 | p.setWidth(2); | |
109 | m_DrawingZoomRect->setPen(p); |
|
109 | m_DrawingZoomRect->setPen(p); | |
110 |
|
110 | |||
111 | m_DrawingZoomRect->topLeft->setCoords(axisPos); |
|
111 | m_DrawingZoomRect->topLeft->setCoords(axisPos); | |
112 | m_DrawingZoomRect->bottomRight->setCoords(axisPos); |
|
112 | m_DrawingZoomRect->bottomRight->setCoords(axisPos); | |
113 | } |
|
113 | } | |
114 |
|
114 | |||
115 | void removeDrawingRect(QCustomPlot &plot) |
|
115 | void removeDrawingRect(QCustomPlot &plot) | |
116 | { |
|
116 | { | |
117 | if (m_DrawingZoomRect) { |
|
117 | if (m_DrawingZoomRect) { | |
118 | plot.removeItem(m_DrawingZoomRect); // the item is deleted by QCustomPlot |
|
118 | plot.removeItem(m_DrawingZoomRect); // the item is deleted by QCustomPlot | |
119 | m_DrawingZoomRect = nullptr; |
|
119 | m_DrawingZoomRect = nullptr; | |
120 | plot.replot(QCustomPlot::rpQueuedReplot); |
|
120 | plot.replot(QCustomPlot::rpQueuedReplot); | |
121 | } |
|
121 | } | |
122 | } |
|
122 | } | |
123 |
|
123 | |||
124 | void startDrawingZone(const QPoint &pos, VisualizationGraphWidget *graph) |
|
124 | void startDrawingZone(const QPoint &pos, VisualizationGraphWidget *graph) | |
125 | { |
|
125 | { | |
126 | endDrawingZone(graph); |
|
126 | endDrawingZone(graph); | |
127 |
|
127 | |||
128 | auto axisPos = posToAxisPos(pos, graph->plot()); |
|
128 | auto axisPos = posToAxisPos(pos, graph->plot()); | |
129 |
|
129 | |||
130 | m_DrawingZone = new VisualizationSelectionZoneItem{&graph->plot()}; |
|
130 | m_DrawingZone = new VisualizationSelectionZoneItem{&graph->plot()}; | |
131 | m_DrawingZone->setRange(axisPos.x(), axisPos.x()); |
|
131 | m_DrawingZone->setRange(axisPos.x(), axisPos.x()); | |
132 | m_DrawingZone->setEditionEnabled(false); |
|
132 | m_DrawingZone->setEditionEnabled(false); | |
133 | } |
|
133 | } | |
134 |
|
134 | |||
135 | void endDrawingZone(VisualizationGraphWidget *graph) |
|
135 | void endDrawingZone(VisualizationGraphWidget *graph) | |
136 | { |
|
136 | { | |
137 | if (m_DrawingZone) { |
|
137 | if (m_DrawingZone) { | |
138 | auto drawingZoneRange = m_DrawingZone->range(); |
|
138 | auto drawingZoneRange = m_DrawingZone->range(); | |
139 | if (qAbs(drawingZoneRange.m_TEnd - drawingZoneRange.m_TStart) > 0) { |
|
139 | if (qAbs(drawingZoneRange.m_TEnd - drawingZoneRange.m_TStart) > 0) { | |
140 | m_DrawingZone->setEditionEnabled(true); |
|
140 | m_DrawingZone->setEditionEnabled(true); | |
141 | addSelectionZone(m_DrawingZone); |
|
141 | addSelectionZone(m_DrawingZone); | |
142 | } |
|
142 | } | |
143 | else { |
|
143 | else { | |
144 | graph->plot().removeItem(m_DrawingZone); // the item is deleted by QCustomPlot |
|
144 | graph->plot().removeItem(m_DrawingZone); // the item is deleted by QCustomPlot | |
145 | } |
|
145 | } | |
146 |
|
146 | |||
147 | graph->plot().replot(QCustomPlot::rpQueuedReplot); |
|
147 | graph->plot().replot(QCustomPlot::rpQueuedReplot); | |
148 | m_DrawingZone = nullptr; |
|
148 | m_DrawingZone = nullptr; | |
149 | } |
|
149 | } | |
150 | } |
|
150 | } | |
151 |
|
151 | |||
152 | void setSelectionZonesEditionEnabled(bool value) |
|
152 | void setSelectionZonesEditionEnabled(bool value) | |
153 | { |
|
153 | { | |
154 | for (auto s : m_SelectionZones) { |
|
154 | for (auto s : m_SelectionZones) { | |
155 | s->setEditionEnabled(value); |
|
155 | s->setEditionEnabled(value); | |
156 | } |
|
156 | } | |
157 | } |
|
157 | } | |
158 |
|
158 | |||
159 | void addSelectionZone(VisualizationSelectionZoneItem *zone) { m_SelectionZones << zone; } |
|
159 | void addSelectionZone(VisualizationSelectionZoneItem *zone) { m_SelectionZones << zone; } | |
160 |
|
160 | |||
161 | VisualizationSelectionZoneItem *selectionZoneAt(const QPoint &pos, |
|
161 | VisualizationSelectionZoneItem *selectionZoneAt(const QPoint &pos, | |
162 | const QCustomPlot &plot) const |
|
162 | const QCustomPlot &plot) const | |
163 | { |
|
163 | { | |
164 | VisualizationSelectionZoneItem *selectionZoneItemUnderCursor = nullptr; |
|
164 | VisualizationSelectionZoneItem *selectionZoneItemUnderCursor = nullptr; | |
165 | auto minDistanceToZone = -1; |
|
165 | auto minDistanceToZone = -1; | |
166 | for (auto zone : m_SelectionZones) { |
|
166 | for (auto zone : m_SelectionZones) { | |
167 | auto distanceToZone = zone->selectTest(pos, false); |
|
167 | auto distanceToZone = zone->selectTest(pos, false); | |
168 | if ((minDistanceToZone < 0 || distanceToZone <= minDistanceToZone) |
|
168 | if ((minDistanceToZone < 0 || distanceToZone <= minDistanceToZone) | |
169 | && distanceToZone >= 0 && distanceToZone < plot.selectionTolerance()) { |
|
169 | && distanceToZone >= 0 && distanceToZone < plot.selectionTolerance()) { | |
170 | selectionZoneItemUnderCursor = zone; |
|
170 | selectionZoneItemUnderCursor = zone; | |
171 | } |
|
171 | } | |
172 | } |
|
172 | } | |
173 |
|
173 | |||
174 | return selectionZoneItemUnderCursor; |
|
174 | return selectionZoneItemUnderCursor; | |
175 | } |
|
175 | } | |
176 |
|
176 | |||
177 | QVector<VisualizationSelectionZoneItem *> selectionZonesAt(const QPoint &pos, |
|
177 | QVector<VisualizationSelectionZoneItem *> selectionZonesAt(const QPoint &pos, | |
178 | const QCustomPlot &plot) const |
|
178 | const QCustomPlot &plot) const | |
179 | { |
|
179 | { | |
180 | QVector<VisualizationSelectionZoneItem *> zones; |
|
180 | QVector<VisualizationSelectionZoneItem *> zones; | |
181 | for (auto zone : m_SelectionZones) { |
|
181 | for (auto zone : m_SelectionZones) { | |
182 | auto distanceToZone = zone->selectTest(pos, false); |
|
182 | auto distanceToZone = zone->selectTest(pos, false); | |
183 | if (distanceToZone >= 0 && distanceToZone < plot.selectionTolerance()) { |
|
183 | if (distanceToZone >= 0 && distanceToZone < plot.selectionTolerance()) { | |
184 | zones << zone; |
|
184 | zones << zone; | |
185 | } |
|
185 | } | |
186 | } |
|
186 | } | |
187 |
|
187 | |||
188 | return zones; |
|
188 | return zones; | |
189 | } |
|
189 | } | |
190 |
|
190 | |||
191 | void moveSelectionZoneOnTop(VisualizationSelectionZoneItem *zone, QCustomPlot &plot) |
|
191 | void moveSelectionZoneOnTop(VisualizationSelectionZoneItem *zone, QCustomPlot &plot) | |
192 | { |
|
192 | { | |
193 | if (!m_SelectionZones.isEmpty() && m_SelectionZones.last() != zone) { |
|
193 | if (!m_SelectionZones.isEmpty() && m_SelectionZones.last() != zone) { | |
194 | zone->moveToTop(); |
|
194 | zone->moveToTop(); | |
195 | m_SelectionZones.removeAll(zone); |
|
195 | m_SelectionZones.removeAll(zone); | |
196 | m_SelectionZones.append(zone); |
|
196 | m_SelectionZones.append(zone); | |
197 | } |
|
197 | } | |
198 | } |
|
198 | } | |
199 |
|
199 | |||
200 | QPointF posToAxisPos(const QPoint &pos, QCustomPlot &plot) const |
|
200 | QPointF posToAxisPos(const QPoint &pos, QCustomPlot &plot) const | |
201 | { |
|
201 | { | |
202 | auto axisX = plot.axisRect()->axis(QCPAxis::atBottom); |
|
202 | auto axisX = plot.axisRect()->axis(QCPAxis::atBottom); | |
203 | auto axisY = plot.axisRect()->axis(QCPAxis::atLeft); |
|
203 | auto axisY = plot.axisRect()->axis(QCPAxis::atLeft); | |
204 | return QPointF{axisX->pixelToCoord(pos.x()), axisY->pixelToCoord(pos.y())}; |
|
204 | return QPointF{axisX->pixelToCoord(pos.x()), axisY->pixelToCoord(pos.y())}; | |
205 | } |
|
205 | } | |
206 |
|
206 | |||
207 | bool pointIsInAxisRect(const QPointF &axisPoint, QCustomPlot &plot) const |
|
207 | bool pointIsInAxisRect(const QPointF &axisPoint, QCustomPlot &plot) const | |
208 | { |
|
208 | { | |
209 | auto axisX = plot.axisRect()->axis(QCPAxis::atBottom); |
|
209 | auto axisX = plot.axisRect()->axis(QCPAxis::atBottom); | |
210 | auto axisY = plot.axisRect()->axis(QCPAxis::atLeft); |
|
210 | auto axisY = plot.axisRect()->axis(QCPAxis::atLeft); | |
211 | return axisX->range().contains(axisPoint.x()) && axisY->range().contains(axisPoint.y()); |
|
211 | return axisX->range().contains(axisPoint.x()) && axisY->range().contains(axisPoint.y()); | |
212 | } |
|
212 | } | |
213 | }; |
|
213 | }; | |
214 |
|
214 | |||
215 | VisualizationGraphWidget::VisualizationGraphWidget(const QString &name, QWidget *parent) |
|
215 | VisualizationGraphWidget::VisualizationGraphWidget(const QString &name, QWidget *parent) | |
216 | : VisualizationDragWidget{parent}, |
|
216 | : VisualizationDragWidget{parent}, | |
217 | ui{new Ui::VisualizationGraphWidget}, |
|
217 | ui{new Ui::VisualizationGraphWidget}, | |
218 | impl{spimpl::make_unique_impl<VisualizationGraphWidgetPrivate>(name)} |
|
218 | impl{spimpl::make_unique_impl<VisualizationGraphWidgetPrivate>(name)} | |
219 | { |
|
219 | { | |
220 | ui->setupUi(this); |
|
220 | ui->setupUi(this); | |
221 |
|
221 | |||
222 | // 'Close' options : widget is deleted when closed |
|
222 | // 'Close' options : widget is deleted when closed | |
223 | setAttribute(Qt::WA_DeleteOnClose); |
|
223 | setAttribute(Qt::WA_DeleteOnClose); | |
224 |
|
224 | |||
225 | // Set qcpplot properties : |
|
225 | // Set qcpplot properties : | |
226 | // - zoom is enabled |
|
226 | // - zoom is enabled | |
227 | // - Mouse wheel on qcpplot is intercepted to determine the zoom orientation |
|
227 | // - Mouse wheel on qcpplot is intercepted to determine the zoom orientation | |
228 | ui->widget->setInteractions(QCP::iRangeZoom); |
|
228 | ui->widget->setInteractions(QCP::iRangeZoom); | |
229 | ui->widget->axisRect()->setRangeDrag(Qt::Horizontal | Qt::Vertical); |
|
229 | ui->widget->axisRect()->setRangeDrag(Qt::Horizontal | Qt::Vertical); | |
230 |
|
230 | |||
231 | // The delegate must be initialized after the ui as it uses the plot |
|
231 | // The delegate must be initialized after the ui as it uses the plot | |
232 | impl->m_RenderingDelegate = std::make_unique<VisualizationGraphRenderingDelegate>(*this); |
|
232 | impl->m_RenderingDelegate = std::make_unique<VisualizationGraphRenderingDelegate>(*this); | |
233 |
|
233 | |||
234 | // Init the cursors |
|
234 | // Init the cursors | |
235 | impl->m_HorizontalCursor = std::make_unique<VisualizationCursorItem>(&plot()); |
|
235 | impl->m_HorizontalCursor = std::make_unique<VisualizationCursorItem>(&plot()); | |
236 | impl->m_HorizontalCursor->setOrientation(Qt::Horizontal); |
|
236 | impl->m_HorizontalCursor->setOrientation(Qt::Horizontal); | |
237 | impl->m_VerticalCursor = std::make_unique<VisualizationCursorItem>(&plot()); |
|
237 | impl->m_VerticalCursor = std::make_unique<VisualizationCursorItem>(&plot()); | |
238 | impl->m_VerticalCursor->setOrientation(Qt::Vertical); |
|
238 | impl->m_VerticalCursor->setOrientation(Qt::Vertical); | |
239 |
|
239 | |||
240 | connect(ui->widget, &QCustomPlot::mousePress, this, &VisualizationGraphWidget::onMousePress); |
|
240 | connect(ui->widget, &QCustomPlot::mousePress, this, &VisualizationGraphWidget::onMousePress); | |
241 | connect(ui->widget, &QCustomPlot::mouseRelease, this, |
|
241 | connect(ui->widget, &QCustomPlot::mouseRelease, this, | |
242 | &VisualizationGraphWidget::onMouseRelease); |
|
242 | &VisualizationGraphWidget::onMouseRelease); | |
243 | connect(ui->widget, &QCustomPlot::mouseMove, this, &VisualizationGraphWidget::onMouseMove); |
|
243 | connect(ui->widget, &QCustomPlot::mouseMove, this, &VisualizationGraphWidget::onMouseMove); | |
244 | connect(ui->widget, &QCustomPlot::mouseWheel, this, &VisualizationGraphWidget::onMouseWheel); |
|
244 | connect(ui->widget, &QCustomPlot::mouseWheel, this, &VisualizationGraphWidget::onMouseWheel); | |
245 | connect(ui->widget, &QCustomPlot::mouseDoubleClick, this, |
|
245 | connect(ui->widget, &QCustomPlot::mouseDoubleClick, this, | |
246 | &VisualizationGraphWidget::onMouseDoubleClick); |
|
246 | &VisualizationGraphWidget::onMouseDoubleClick); | |
247 | connect(ui->widget->xAxis, static_cast<void (QCPAxis::*)(const QCPRange &, const QCPRange &)>( |
|
247 | connect(ui->widget->xAxis, static_cast<void (QCPAxis::*)(const QCPRange &, const QCPRange &)>( | |
248 | &QCPAxis::rangeChanged), |
|
248 | &QCPAxis::rangeChanged), | |
249 | this, &VisualizationGraphWidget::onRangeChanged, Qt::DirectConnection); |
|
249 | this, &VisualizationGraphWidget::onRangeChanged, Qt::DirectConnection); | |
250 |
|
250 | |||
251 | // Activates menu when right clicking on the graph |
|
251 | // Activates menu when right clicking on the graph | |
252 | ui->widget->setContextMenuPolicy(Qt::CustomContextMenu); |
|
252 | ui->widget->setContextMenuPolicy(Qt::CustomContextMenu); | |
253 | connect(ui->widget, &QCustomPlot::customContextMenuRequested, this, |
|
253 | connect(ui->widget, &QCustomPlot::customContextMenuRequested, this, | |
254 | &VisualizationGraphWidget::onGraphMenuRequested); |
|
254 | &VisualizationGraphWidget::onGraphMenuRequested); | |
255 |
|
255 | |||
256 | //@TODO implement this :) |
|
256 | //@TODO implement this :) | |
257 | // connect(this, &VisualizationGraphWidget::requestDataLoading, &sqpApp->variableController(), |
|
257 | // connect(this, &VisualizationGraphWidget::requestDataLoading, &sqpApp->variableController(), | |
258 | // &VariableController::onRequestDataLoading); |
|
258 | // &VariableController::onRequestDataLoading); | |
259 |
|
259 | |||
260 | // connect(&sqpApp->variableController(), &VariableController2::updateVarDisplaying, this, |
|
260 | // connect(&sqpApp->variableController(), &VariableController2::updateVarDisplaying, this, | |
261 | // &VisualizationGraphWidget::onUpdateVarDisplaying); |
|
261 | // &VisualizationGraphWidget::onUpdateVarDisplaying); | |
262 |
|
262 | |||
263 | // Necessary for all platform since Qt::AA_EnableHighDpiScaling is enable. |
|
263 | // Necessary for all platform since Qt::AA_EnableHighDpiScaling is enable. | |
264 | plot().setPlottingHint(QCP::phFastPolylines, true); |
|
264 | plot().setPlottingHint(QCP::phFastPolylines, true); | |
265 | } |
|
265 | } | |
266 |
|
266 | |||
267 |
|
267 | |||
268 | VisualizationGraphWidget::~VisualizationGraphWidget() |
|
268 | VisualizationGraphWidget::~VisualizationGraphWidget() | |
269 | { |
|
269 | { | |
270 | delete ui; |
|
270 | delete ui; | |
271 | } |
|
271 | } | |
272 |
|
272 | |||
273 | VisualizationZoneWidget *VisualizationGraphWidget::parentZoneWidget() const noexcept |
|
273 | VisualizationZoneWidget *VisualizationGraphWidget::parentZoneWidget() const noexcept | |
274 | { |
|
274 | { | |
275 | auto parent = parentWidget(); |
|
275 | auto parent = parentWidget(); | |
276 | while (parent != nullptr && !qobject_cast<VisualizationZoneWidget *>(parent)) { |
|
276 | while (parent != nullptr && !qobject_cast<VisualizationZoneWidget *>(parent)) { | |
277 | parent = parent->parentWidget(); |
|
277 | parent = parent->parentWidget(); | |
278 | } |
|
278 | } | |
279 |
|
279 | |||
280 | return qobject_cast<VisualizationZoneWidget *>(parent); |
|
280 | return qobject_cast<VisualizationZoneWidget *>(parent); | |
281 | } |
|
281 | } | |
282 |
|
282 | |||
283 | VisualizationWidget *VisualizationGraphWidget::parentVisualizationWidget() const |
|
283 | VisualizationWidget *VisualizationGraphWidget::parentVisualizationWidget() const | |
284 | { |
|
284 | { | |
285 | auto parent = parentWidget(); |
|
285 | auto parent = parentWidget(); | |
286 | while (parent != nullptr && !qobject_cast<VisualizationWidget *>(parent)) { |
|
286 | while (parent != nullptr && !qobject_cast<VisualizationWidget *>(parent)) { | |
287 | parent = parent->parentWidget(); |
|
287 | parent = parent->parentWidget(); | |
288 | } |
|
288 | } | |
289 |
|
289 | |||
290 | return qobject_cast<VisualizationWidget *>(parent); |
|
290 | return qobject_cast<VisualizationWidget *>(parent); | |
291 | } |
|
291 | } | |
292 |
|
292 | |||
293 | void VisualizationGraphWidget::setFlags(GraphFlags flags) |
|
293 | void VisualizationGraphWidget::setFlags(GraphFlags flags) | |
294 | { |
|
294 | { | |
295 | impl->m_Flags = std::move(flags); |
|
295 | impl->m_Flags = std::move(flags); | |
296 | } |
|
296 | } | |
297 |
|
297 | |||
298 | void VisualizationGraphWidget::addVariable(std::shared_ptr<Variable> variable, DateTimeRange range) |
|
298 | void VisualizationGraphWidget::addVariable(std::shared_ptr<Variable> variable, DateTimeRange range) | |
299 | { |
|
299 | { | |
300 | /// Lambda used to set graph's units and range according to the variable passed in parameter |
|
|||
301 | auto loadRange = [this](std::shared_ptr<Variable> variable, const DateTimeRange &range) { |
|
|||
302 | impl->m_RenderingDelegate->setAxesUnits(*variable); |
|
|||
303 |
|
||||
304 | this->setFlags(GraphFlag::DisableAll); |
|
|||
305 | setGraphRange(range); |
|
|||
306 | this->setFlags(GraphFlag::EnableAll); |
|
|||
307 | emit requestDataLoading({variable}, range, false); |
|
|||
308 | }; |
|
|||
309 |
|
||||
310 | connect(variable.get(), SIGNAL(updated()), this, SLOT(onDataCacheVariableUpdated())); |
|
|||
311 |
|
||||
312 | // Calls update of graph's range and units when the data of the variable have been initialized. |
|
|||
313 | // Note: we use QueuedConnection here as the update event must be called in the UI thread |
|
|||
314 | connect(variable.get(), &Variable::dataInitialized, this, |
|
|||
315 | [ varW = std::weak_ptr<Variable>{variable}, range, loadRange, this ]() { |
|
|||
316 | if (auto var = varW.lock()) { |
|
|||
317 | // If the variable is the first added in the graph, we load its range |
|
|||
318 | auto firstVariableInGraph = range == INVALID_RANGE; |
|
|||
319 | auto loadedRange = graphRange(); |
|
|||
320 | if (impl->m_VariableAutoRangeOnInit) { |
|
|||
321 | loadedRange = firstVariableInGraph ? var->range() : range; |
|
|||
322 | } |
|
|||
323 | loadRange(var, loadedRange); |
|
|||
324 | setYRange(var); |
|
|||
325 | } |
|
|||
326 | }, |
|
|||
327 | Qt::QueuedConnection); |
|
|||
328 |
|
||||
329 | // Uses delegate to create the qcpplot components according to the variable |
|
300 | // Uses delegate to create the qcpplot components according to the variable | |
330 | auto createdPlottables = VisualizationGraphHelper::create(variable, *ui->widget); |
|
301 | auto createdPlottables = VisualizationGraphHelper::create(variable, *ui->widget); | |
331 |
|
302 | |||
332 | // Sets graph properties |
|
303 | // Sets graph properties | |
333 | impl->m_RenderingDelegate->setGraphProperties(*variable, createdPlottables); |
|
304 | impl->m_RenderingDelegate->setGraphProperties(*variable, createdPlottables); | |
334 |
|
305 | |||
335 | impl->m_VariableToPlotMultiMap.insert({variable, std::move(createdPlottables)}); |
|
306 | impl->m_VariableToPlotMultiMap.insert({variable, std::move(createdPlottables)}); | |
336 |
|
307 | |||
337 | // If the variable already has its data loaded, load its units and its range in the graph |
|
308 | // If the variable already has its data loaded, load its units and its range in the graph | |
338 | if (variable->dataSeries() != nullptr) { |
|
309 | if (variable->dataSeries() != nullptr) { | |
339 | loadRange(variable, range); |
|
310 | impl->m_RenderingDelegate->setAxesUnits(*variable); | |
|
311 | this->setFlags(GraphFlag::DisableAll); | |||
|
312 | setGraphRange(range); | |||
|
313 | this->setFlags(GraphFlag::EnableAll); | |||
340 | } |
|
314 | } | |
341 |
|
315 | connect(variable.get(),&Variable::updated, | ||
|
316 | [variable=variable,this]() | |||
|
317 | { | |||
|
318 | QMetaObject::invokeMethod(this,[variable=variable,this](){this->onUpdateVarDisplaying(variable,this->graphRange());}); | |||
|
319 | }); | |||
|
320 | this->onUpdateVarDisplaying(variable,range);//My bullshit | |||
342 | emit variableAdded(variable); |
|
321 | emit variableAdded(variable); | |
343 | } |
|
322 | } | |
344 |
|
323 | |||
345 | void VisualizationGraphWidget::removeVariable(std::shared_ptr<Variable> variable) noexcept |
|
324 | void VisualizationGraphWidget::removeVariable(std::shared_ptr<Variable> variable) noexcept | |
346 | { |
|
325 | { | |
347 | // Each component associated to the variable : |
|
326 | // Each component associated to the variable : | |
348 | // - is removed from qcpplot (which deletes it) |
|
327 | // - is removed from qcpplot (which deletes it) | |
349 | // - is no longer referenced in the map |
|
328 | // - is no longer referenced in the map | |
350 | auto variableIt = impl->m_VariableToPlotMultiMap.find(variable); |
|
329 | auto variableIt = impl->m_VariableToPlotMultiMap.find(variable); | |
351 | if (variableIt != impl->m_VariableToPlotMultiMap.cend()) { |
|
330 | if (variableIt != impl->m_VariableToPlotMultiMap.cend()) { | |
352 | emit variableAboutToBeRemoved(variable); |
|
331 | emit variableAboutToBeRemoved(variable); | |
353 |
|
332 | |||
354 | auto &plottablesMap = variableIt->second; |
|
333 | auto &plottablesMap = variableIt->second; | |
355 |
|
334 | |||
356 | for (auto plottableIt = plottablesMap.cbegin(), plottableEnd = plottablesMap.cend(); |
|
335 | for (auto plottableIt = plottablesMap.cbegin(), plottableEnd = plottablesMap.cend(); | |
357 | plottableIt != plottableEnd;) { |
|
336 | plottableIt != plottableEnd;) { | |
358 | ui->widget->removePlottable(plottableIt->second); |
|
337 | ui->widget->removePlottable(plottableIt->second); | |
359 | plottableIt = plottablesMap.erase(plottableIt); |
|
338 | plottableIt = plottablesMap.erase(plottableIt); | |
360 | } |
|
339 | } | |
361 |
|
340 | |||
362 | impl->m_VariableToPlotMultiMap.erase(variableIt); |
|
341 | impl->m_VariableToPlotMultiMap.erase(variableIt); | |
363 | } |
|
342 | } | |
364 |
|
343 | |||
365 | // Updates graph |
|
344 | // Updates graph | |
366 | ui->widget->replot(); |
|
345 | ui->widget->replot(); | |
367 | } |
|
346 | } | |
368 |
|
347 | |||
369 | std::vector<std::shared_ptr<Variable> > VisualizationGraphWidget::variables() const |
|
348 | std::vector<std::shared_ptr<Variable> > VisualizationGraphWidget::variables() const | |
370 | { |
|
349 | { | |
371 | auto variables = std::vector<std::shared_ptr<Variable> >{}; |
|
350 | auto variables = std::vector<std::shared_ptr<Variable> >{}; | |
372 | for (auto it = std::cbegin(impl->m_VariableToPlotMultiMap); |
|
351 | for (auto it = std::cbegin(impl->m_VariableToPlotMultiMap); | |
373 | it != std::cend(impl->m_VariableToPlotMultiMap); ++it) { |
|
352 | it != std::cend(impl->m_VariableToPlotMultiMap); ++it) { | |
374 | variables.push_back (it->first); |
|
353 | variables.push_back (it->first); | |
375 | } |
|
354 | } | |
376 |
|
355 | |||
377 | return variables; |
|
356 | return variables; | |
378 | } |
|
357 | } | |
379 |
|
358 | |||
380 | void VisualizationGraphWidget::setYRange(std::shared_ptr<Variable> variable) |
|
359 | void VisualizationGraphWidget::setYRange(std::shared_ptr<Variable> variable) | |
381 | { |
|
360 | { | |
382 | if (!variable) { |
|
361 | if (!variable) { | |
383 | qCCritical(LOG_VisualizationGraphWidget()) << "Can't set y-axis range: variable is null"; |
|
362 | qCCritical(LOG_VisualizationGraphWidget()) << "Can't set y-axis range: variable is null"; | |
384 | return; |
|
363 | return; | |
385 | } |
|
364 | } | |
386 |
|
365 | |||
387 | VisualizationGraphHelper::setYAxisRange(variable, *ui->widget); |
|
366 | VisualizationGraphHelper::setYAxisRange(variable, *ui->widget); | |
388 | } |
|
367 | } | |
389 |
|
368 | |||
390 | DateTimeRange VisualizationGraphWidget::graphRange() const noexcept |
|
369 | DateTimeRange VisualizationGraphWidget::graphRange() const noexcept | |
391 | { |
|
370 | { | |
392 | auto graphRange = ui->widget->xAxis->range(); |
|
371 | auto graphRange = ui->widget->xAxis->range(); | |
393 | return DateTimeRange{graphRange.lower, graphRange.upper}; |
|
372 | return DateTimeRange{graphRange.lower, graphRange.upper}; | |
394 | } |
|
373 | } | |
395 |
|
374 | |||
396 | void VisualizationGraphWidget::setGraphRange(const DateTimeRange &range, bool calibration) |
|
375 | void VisualizationGraphWidget::setGraphRange(const DateTimeRange &range, bool calibration) | |
397 | { |
|
376 | { | |
398 | qCDebug(LOG_VisualizationGraphWidget()) << tr("VisualizationGraphWidget::setGraphRange START"); |
|
|||
399 |
|
||||
400 | if (calibration) { |
|
377 | if (calibration) { | |
401 | impl->m_IsCalibration = true; |
|
378 | impl->m_IsCalibration = true; | |
402 | } |
|
379 | } | |
403 |
|
380 | |||
404 | ui->widget->xAxis->setRange(range.m_TStart, range.m_TEnd); |
|
381 | ui->widget->xAxis->setRange(range.m_TStart, range.m_TEnd); | |
405 | ui->widget->replot(); |
|
382 | ui->widget->replot(); | |
406 |
|
383 | |||
407 | if (calibration) { |
|
384 | if (calibration) { | |
408 | impl->m_IsCalibration = false; |
|
385 | impl->m_IsCalibration = false; | |
409 | } |
|
386 | } | |
410 |
|
||||
411 | qCDebug(LOG_VisualizationGraphWidget()) << tr("VisualizationGraphWidget::setGraphRange END"); |
|
|||
412 | } |
|
387 | } | |
413 |
|
388 | |||
414 | void VisualizationGraphWidget::setAutoRangeOnVariableInitialization(bool value) |
|
389 | void VisualizationGraphWidget::setAutoRangeOnVariableInitialization(bool value) | |
415 | { |
|
390 | { | |
416 | impl->m_VariableAutoRangeOnInit = value; |
|
391 | impl->m_VariableAutoRangeOnInit = value; | |
417 | } |
|
392 | } | |
418 |
|
393 | |||
419 | QVector<DateTimeRange> VisualizationGraphWidget::selectionZoneRanges() const |
|
394 | QVector<DateTimeRange> VisualizationGraphWidget::selectionZoneRanges() const | |
420 | { |
|
395 | { | |
421 | QVector<DateTimeRange> ranges; |
|
396 | QVector<DateTimeRange> ranges; | |
422 | for (auto zone : impl->m_SelectionZones) { |
|
397 | for (auto zone : impl->m_SelectionZones) { | |
423 | ranges << zone->range(); |
|
398 | ranges << zone->range(); | |
424 | } |
|
399 | } | |
425 |
|
400 | |||
426 | return ranges; |
|
401 | return ranges; | |
427 | } |
|
402 | } | |
428 |
|
403 | |||
429 | void VisualizationGraphWidget::addSelectionZones(const QVector<DateTimeRange> &ranges) |
|
404 | void VisualizationGraphWidget::addSelectionZones(const QVector<DateTimeRange> &ranges) | |
430 | { |
|
405 | { | |
431 | for (const auto &range : ranges) { |
|
406 | for (const auto &range : ranges) { | |
432 | // note: ownership is transfered to QCustomPlot |
|
407 | // note: ownership is transfered to QCustomPlot | |
433 | auto zone = new VisualizationSelectionZoneItem(&plot()); |
|
408 | auto zone = new VisualizationSelectionZoneItem(&plot()); | |
434 | zone->setRange(range.m_TStart, range.m_TEnd); |
|
409 | zone->setRange(range.m_TStart, range.m_TEnd); | |
435 | impl->addSelectionZone(zone); |
|
410 | impl->addSelectionZone(zone); | |
436 | } |
|
411 | } | |
437 |
|
412 | |||
438 | plot().replot(QCustomPlot::rpQueuedReplot); |
|
413 | plot().replot(QCustomPlot::rpQueuedReplot); | |
439 | } |
|
414 | } | |
440 |
|
415 | |||
441 | VisualizationSelectionZoneItem *VisualizationGraphWidget::addSelectionZone(const QString &name, |
|
416 | VisualizationSelectionZoneItem *VisualizationGraphWidget::addSelectionZone(const QString &name, | |
442 | const DateTimeRange &range) |
|
417 | const DateTimeRange &range) | |
443 | { |
|
418 | { | |
444 | // note: ownership is transfered to QCustomPlot |
|
419 | // note: ownership is transfered to QCustomPlot | |
445 | auto zone = new VisualizationSelectionZoneItem(&plot()); |
|
420 | auto zone = new VisualizationSelectionZoneItem(&plot()); | |
446 | zone->setName(name); |
|
421 | zone->setName(name); | |
447 | zone->setRange(range.m_TStart, range.m_TEnd); |
|
422 | zone->setRange(range.m_TStart, range.m_TEnd); | |
448 | impl->addSelectionZone(zone); |
|
423 | impl->addSelectionZone(zone); | |
449 |
|
424 | |||
450 | plot().replot(QCustomPlot::rpQueuedReplot); |
|
425 | plot().replot(QCustomPlot::rpQueuedReplot); | |
451 |
|
426 | |||
452 | return zone; |
|
427 | return zone; | |
453 | } |
|
428 | } | |
454 |
|
429 | |||
455 | void VisualizationGraphWidget::removeSelectionZone(VisualizationSelectionZoneItem *selectionZone) |
|
430 | void VisualizationGraphWidget::removeSelectionZone(VisualizationSelectionZoneItem *selectionZone) | |
456 | { |
|
431 | { | |
457 | parentVisualizationWidget()->selectionZoneManager().setSelected(selectionZone, false); |
|
432 | parentVisualizationWidget()->selectionZoneManager().setSelected(selectionZone, false); | |
458 |
|
433 | |||
459 | if (impl->m_HoveredZone == selectionZone) { |
|
434 | if (impl->m_HoveredZone == selectionZone) { | |
460 | impl->m_HoveredZone = nullptr; |
|
435 | impl->m_HoveredZone = nullptr; | |
461 | setCursor(Qt::ArrowCursor); |
|
436 | setCursor(Qt::ArrowCursor); | |
462 | } |
|
437 | } | |
463 |
|
438 | |||
464 | impl->m_SelectionZones.removeAll(selectionZone); |
|
439 | impl->m_SelectionZones.removeAll(selectionZone); | |
465 | plot().removeItem(selectionZone); |
|
440 | plot().removeItem(selectionZone); | |
466 | plot().replot(QCustomPlot::rpQueuedReplot); |
|
441 | plot().replot(QCustomPlot::rpQueuedReplot); | |
467 | } |
|
442 | } | |
468 |
|
443 | |||
469 | void VisualizationGraphWidget::undoZoom() |
|
444 | void VisualizationGraphWidget::undoZoom() | |
470 | { |
|
445 | { | |
471 | auto zoom = impl->m_ZoomStack.pop(); |
|
446 | auto zoom = impl->m_ZoomStack.pop(); | |
472 | auto axisX = plot().axisRect()->axis(QCPAxis::atBottom); |
|
447 | auto axisX = plot().axisRect()->axis(QCPAxis::atBottom); | |
473 | auto axisY = plot().axisRect()->axis(QCPAxis::atLeft); |
|
448 | auto axisY = plot().axisRect()->axis(QCPAxis::atLeft); | |
474 |
|
449 | |||
475 | axisX->setRange(zoom.first); |
|
450 | axisX->setRange(zoom.first); | |
476 | axisY->setRange(zoom.second); |
|
451 | axisY->setRange(zoom.second); | |
477 |
|
452 | |||
478 | plot().replot(QCustomPlot::rpQueuedReplot); |
|
453 | plot().replot(QCustomPlot::rpQueuedReplot); | |
479 | } |
|
454 | } | |
480 |
|
455 | |||
481 | void VisualizationGraphWidget::accept(IVisualizationWidgetVisitor *visitor) |
|
456 | void VisualizationGraphWidget::accept(IVisualizationWidgetVisitor *visitor) | |
482 | { |
|
457 | { | |
483 | if (visitor) { |
|
458 | if (visitor) { | |
484 | visitor->visit(this); |
|
459 | visitor->visit(this); | |
485 | } |
|
460 | } | |
486 | else { |
|
461 | else { | |
487 | qCCritical(LOG_VisualizationGraphWidget()) |
|
462 | qCCritical(LOG_VisualizationGraphWidget()) | |
488 | << tr("Can't visit widget : the visitor is null"); |
|
463 | << tr("Can't visit widget : the visitor is null"); | |
489 | } |
|
464 | } | |
490 | } |
|
465 | } | |
491 |
|
466 | |||
492 | bool VisualizationGraphWidget::canDrop(const Variable &variable) const |
|
467 | bool VisualizationGraphWidget::canDrop(const Variable &variable) const | |
493 | { |
|
468 | { | |
494 | auto isSpectrogram = [](const auto &variable) { |
|
469 | auto isSpectrogram = [](const auto &variable) { | |
495 | return std::dynamic_pointer_cast<SpectrogramSeries>(variable.dataSeries()) != nullptr; |
|
470 | return std::dynamic_pointer_cast<SpectrogramSeries>(variable.dataSeries()) != nullptr; | |
496 | }; |
|
471 | }; | |
497 |
|
472 | |||
498 | // - A spectrogram series can't be dropped on graph with existing plottables |
|
473 | // - A spectrogram series can't be dropped on graph with existing plottables | |
499 | // - No data series can be dropped on graph with existing spectrogram series |
|
474 | // - No data series can be dropped on graph with existing spectrogram series | |
500 | return isSpectrogram(variable) |
|
475 | return isSpectrogram(variable) | |
501 | ? impl->m_VariableToPlotMultiMap.empty() |
|
476 | ? impl->m_VariableToPlotMultiMap.empty() | |
502 | : std::none_of( |
|
477 | : std::none_of( | |
503 | impl->m_VariableToPlotMultiMap.cbegin(), impl->m_VariableToPlotMultiMap.cend(), |
|
478 | impl->m_VariableToPlotMultiMap.cbegin(), impl->m_VariableToPlotMultiMap.cend(), | |
504 | [isSpectrogram](const auto &entry) { return isSpectrogram(*entry.first); }); |
|
479 | [isSpectrogram](const auto &entry) { return isSpectrogram(*entry.first); }); | |
505 | } |
|
480 | } | |
506 |
|
481 | |||
507 | bool VisualizationGraphWidget::contains(const Variable &variable) const |
|
482 | bool VisualizationGraphWidget::contains(const Variable &variable) const | |
508 | { |
|
483 | { | |
509 | // Finds the variable among the keys of the map |
|
484 | // Finds the variable among the keys of the map | |
510 | auto variablePtr = &variable; |
|
485 | auto variablePtr = &variable; | |
511 | auto findVariable |
|
486 | auto findVariable | |
512 | = [variablePtr](const auto &entry) { return variablePtr == entry.first.get(); }; |
|
487 | = [variablePtr](const auto &entry) { return variablePtr == entry.first.get(); }; | |
513 |
|
488 | |||
514 | auto end = impl->m_VariableToPlotMultiMap.cend(); |
|
489 | auto end = impl->m_VariableToPlotMultiMap.cend(); | |
515 | auto it = std::find_if(impl->m_VariableToPlotMultiMap.cbegin(), end, findVariable); |
|
490 | auto it = std::find_if(impl->m_VariableToPlotMultiMap.cbegin(), end, findVariable); | |
516 | return it != end; |
|
491 | return it != end; | |
517 | } |
|
492 | } | |
518 |
|
493 | |||
519 | QString VisualizationGraphWidget::name() const |
|
494 | QString VisualizationGraphWidget::name() const | |
520 | { |
|
495 | { | |
521 | return impl->m_Name; |
|
496 | return impl->m_Name; | |
522 | } |
|
497 | } | |
523 |
|
498 | |||
524 | QMimeData *VisualizationGraphWidget::mimeData(const QPoint &position) const |
|
499 | QMimeData *VisualizationGraphWidget::mimeData(const QPoint &position) const | |
525 | { |
|
500 | { | |
526 | auto mimeData = new QMimeData; |
|
501 | auto mimeData = new QMimeData; | |
527 |
|
502 | |||
528 | auto selectionZoneItemUnderCursor = impl->selectionZoneAt(position, plot()); |
|
503 | auto selectionZoneItemUnderCursor = impl->selectionZoneAt(position, plot()); | |
529 | if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones |
|
504 | if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones | |
530 | && selectionZoneItemUnderCursor) { |
|
505 | && selectionZoneItemUnderCursor) { | |
531 | mimeData->setData(MIME_TYPE_TIME_RANGE, TimeController::mimeDataForTimeRange( |
|
506 | mimeData->setData(MIME_TYPE_TIME_RANGE, TimeController::mimeDataForTimeRange( | |
532 | selectionZoneItemUnderCursor->range())); |
|
507 | selectionZoneItemUnderCursor->range())); | |
533 | mimeData->setData(MIME_TYPE_SELECTION_ZONE, TimeController::mimeDataForTimeRange( |
|
508 | mimeData->setData(MIME_TYPE_SELECTION_ZONE, TimeController::mimeDataForTimeRange( | |
534 | selectionZoneItemUnderCursor->range())); |
|
509 | selectionZoneItemUnderCursor->range())); | |
535 | } |
|
510 | } | |
536 | else { |
|
511 | else { | |
537 | mimeData->setData(MIME_TYPE_GRAPH, QByteArray{}); |
|
512 | mimeData->setData(MIME_TYPE_GRAPH, QByteArray{}); | |
538 |
|
513 | |||
539 | auto timeRangeData = TimeController::mimeDataForTimeRange(graphRange()); |
|
514 | auto timeRangeData = TimeController::mimeDataForTimeRange(graphRange()); | |
540 | mimeData->setData(MIME_TYPE_TIME_RANGE, timeRangeData); |
|
515 | mimeData->setData(MIME_TYPE_TIME_RANGE, timeRangeData); | |
541 | } |
|
516 | } | |
542 |
|
517 | |||
543 | return mimeData; |
|
518 | return mimeData; | |
544 | } |
|
519 | } | |
545 |
|
520 | |||
546 | QPixmap VisualizationGraphWidget::customDragPixmap(const QPoint &dragPosition) |
|
521 | QPixmap VisualizationGraphWidget::customDragPixmap(const QPoint &dragPosition) | |
547 | { |
|
522 | { | |
548 | auto selectionZoneItemUnderCursor = impl->selectionZoneAt(dragPosition, plot()); |
|
523 | auto selectionZoneItemUnderCursor = impl->selectionZoneAt(dragPosition, plot()); | |
549 | if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones |
|
524 | if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones | |
550 | && selectionZoneItemUnderCursor) { |
|
525 | && selectionZoneItemUnderCursor) { | |
551 |
|
526 | |||
552 | auto zoneTopLeft = selectionZoneItemUnderCursor->topLeft->pixelPosition(); |
|
527 | auto zoneTopLeft = selectionZoneItemUnderCursor->topLeft->pixelPosition(); | |
553 | auto zoneBottomRight = selectionZoneItemUnderCursor->bottomRight->pixelPosition(); |
|
528 | auto zoneBottomRight = selectionZoneItemUnderCursor->bottomRight->pixelPosition(); | |
554 |
|
529 | |||
555 | auto zoneSize = QSizeF{qAbs(zoneBottomRight.x() - zoneTopLeft.x()), |
|
530 | auto zoneSize = QSizeF{qAbs(zoneBottomRight.x() - zoneTopLeft.x()), | |
556 | qAbs(zoneBottomRight.y() - zoneTopLeft.y())} |
|
531 | qAbs(zoneBottomRight.y() - zoneTopLeft.y())} | |
557 | .toSize(); |
|
532 | .toSize(); | |
558 |
|
533 | |||
559 | auto pixmap = QPixmap(zoneSize); |
|
534 | auto pixmap = QPixmap(zoneSize); | |
560 | render(&pixmap, QPoint(), QRegion{QRect{zoneTopLeft.toPoint(), zoneSize}}); |
|
535 | render(&pixmap, QPoint(), QRegion{QRect{zoneTopLeft.toPoint(), zoneSize}}); | |
561 |
|
536 | |||
562 | return pixmap; |
|
537 | return pixmap; | |
563 | } |
|
538 | } | |
564 |
|
539 | |||
565 | return QPixmap(); |
|
540 | return QPixmap(); | |
566 | } |
|
541 | } | |
567 |
|
542 | |||
568 | bool VisualizationGraphWidget::isDragAllowed() const |
|
543 | bool VisualizationGraphWidget::isDragAllowed() const | |
569 | { |
|
544 | { | |
570 | return true; |
|
545 | return true; | |
571 | } |
|
546 | } | |
572 |
|
547 | |||
573 | void VisualizationGraphWidget::highlightForMerge(bool highlighted) |
|
548 | void VisualizationGraphWidget::highlightForMerge(bool highlighted) | |
574 | { |
|
549 | { | |
575 | if (highlighted) { |
|
550 | if (highlighted) { | |
576 | plot().setBackground(QBrush(QColor("#BBD5EE"))); |
|
551 | plot().setBackground(QBrush(QColor("#BBD5EE"))); | |
577 | } |
|
552 | } | |
578 | else { |
|
553 | else { | |
579 | plot().setBackground(QBrush(Qt::white)); |
|
554 | plot().setBackground(QBrush(Qt::white)); | |
580 | } |
|
555 | } | |
581 |
|
556 | |||
582 | plot().update(); |
|
557 | plot().update(); | |
583 | } |
|
558 | } | |
584 |
|
559 | |||
585 | void VisualizationGraphWidget::addVerticalCursor(double time) |
|
560 | void VisualizationGraphWidget::addVerticalCursor(double time) | |
586 | { |
|
561 | { | |
587 | impl->m_VerticalCursor->setPosition(time); |
|
562 | impl->m_VerticalCursor->setPosition(time); | |
588 | impl->m_VerticalCursor->setVisible(true); |
|
563 | impl->m_VerticalCursor->setVisible(true); | |
589 |
|
564 | |||
590 | auto text |
|
565 | auto text | |
591 | = DateUtils::dateTime(time).toString(CURSOR_LABELS_DATETIME_FORMAT).replace(' ', '\n'); |
|
566 | = DateUtils::dateTime(time).toString(CURSOR_LABELS_DATETIME_FORMAT).replace(' ', '\n'); | |
592 | impl->m_VerticalCursor->setLabelText(text); |
|
567 | impl->m_VerticalCursor->setLabelText(text); | |
593 | } |
|
568 | } | |
594 |
|
569 | |||
595 | void VisualizationGraphWidget::addVerticalCursorAtViewportPosition(double position) |
|
570 | void VisualizationGraphWidget::addVerticalCursorAtViewportPosition(double position) | |
596 | { |
|
571 | { | |
597 | impl->m_VerticalCursor->setAbsolutePosition(position); |
|
572 | impl->m_VerticalCursor->setAbsolutePosition(position); | |
598 | impl->m_VerticalCursor->setVisible(true); |
|
573 | impl->m_VerticalCursor->setVisible(true); | |
599 |
|
574 | |||
600 | auto axis = plot().axisRect()->axis(QCPAxis::atBottom); |
|
575 | auto axis = plot().axisRect()->axis(QCPAxis::atBottom); | |
601 | auto text |
|
576 | auto text | |
602 | = DateUtils::dateTime(axis->pixelToCoord(position)).toString(CURSOR_LABELS_DATETIME_FORMAT); |
|
577 | = DateUtils::dateTime(axis->pixelToCoord(position)).toString(CURSOR_LABELS_DATETIME_FORMAT); | |
603 | impl->m_VerticalCursor->setLabelText(text); |
|
578 | impl->m_VerticalCursor->setLabelText(text); | |
604 | } |
|
579 | } | |
605 |
|
580 | |||
606 | void VisualizationGraphWidget::removeVerticalCursor() |
|
581 | void VisualizationGraphWidget::removeVerticalCursor() | |
607 | { |
|
582 | { | |
608 | impl->m_VerticalCursor->setVisible(false); |
|
583 | impl->m_VerticalCursor->setVisible(false); | |
609 | plot().replot(QCustomPlot::rpQueuedReplot); |
|
584 | plot().replot(QCustomPlot::rpQueuedReplot); | |
610 | } |
|
585 | } | |
611 |
|
586 | |||
612 | void VisualizationGraphWidget::addHorizontalCursor(double value) |
|
587 | void VisualizationGraphWidget::addHorizontalCursor(double value) | |
613 | { |
|
588 | { | |
614 | impl->m_HorizontalCursor->setPosition(value); |
|
589 | impl->m_HorizontalCursor->setPosition(value); | |
615 | impl->m_HorizontalCursor->setVisible(true); |
|
590 | impl->m_HorizontalCursor->setVisible(true); | |
616 | impl->m_HorizontalCursor->setLabelText(QString::number(value)); |
|
591 | impl->m_HorizontalCursor->setLabelText(QString::number(value)); | |
617 | } |
|
592 | } | |
618 |
|
593 | |||
619 | void VisualizationGraphWidget::addHorizontalCursorAtViewportPosition(double position) |
|
594 | void VisualizationGraphWidget::addHorizontalCursorAtViewportPosition(double position) | |
620 | { |
|
595 | { | |
621 | impl->m_HorizontalCursor->setAbsolutePosition(position); |
|
596 | impl->m_HorizontalCursor->setAbsolutePosition(position); | |
622 | impl->m_HorizontalCursor->setVisible(true); |
|
597 | impl->m_HorizontalCursor->setVisible(true); | |
623 |
|
598 | |||
624 | auto axis = plot().axisRect()->axis(QCPAxis::atLeft); |
|
599 | auto axis = plot().axisRect()->axis(QCPAxis::atLeft); | |
625 | impl->m_HorizontalCursor->setLabelText(QString::number(axis->pixelToCoord(position))); |
|
600 | impl->m_HorizontalCursor->setLabelText(QString::number(axis->pixelToCoord(position))); | |
626 | } |
|
601 | } | |
627 |
|
602 | |||
628 | void VisualizationGraphWidget::removeHorizontalCursor() |
|
603 | void VisualizationGraphWidget::removeHorizontalCursor() | |
629 | { |
|
604 | { | |
630 | impl->m_HorizontalCursor->setVisible(false); |
|
605 | impl->m_HorizontalCursor->setVisible(false); | |
631 | plot().replot(QCustomPlot::rpQueuedReplot); |
|
606 | plot().replot(QCustomPlot::rpQueuedReplot); | |
632 | } |
|
607 | } | |
633 |
|
608 | |||
634 | void VisualizationGraphWidget::closeEvent(QCloseEvent *event) |
|
609 | void VisualizationGraphWidget::closeEvent(QCloseEvent *event) | |
635 | { |
|
610 | { | |
636 | Q_UNUSED(event); |
|
611 | Q_UNUSED(event); | |
637 |
|
612 | |||
638 | for (auto i : impl->m_SelectionZones) { |
|
613 | for (auto i : impl->m_SelectionZones) { | |
639 | parentVisualizationWidget()->selectionZoneManager().setSelected(i, false); |
|
614 | parentVisualizationWidget()->selectionZoneManager().setSelected(i, false); | |
640 | } |
|
615 | } | |
641 |
|
616 | |||
642 | // Prevents that all variables will be removed from graph when it will be closed |
|
617 | // Prevents that all variables will be removed from graph when it will be closed | |
643 | for (auto &variableEntry : impl->m_VariableToPlotMultiMap) { |
|
618 | for (auto &variableEntry : impl->m_VariableToPlotMultiMap) { | |
644 | emit variableAboutToBeRemoved(variableEntry.first); |
|
619 | emit variableAboutToBeRemoved(variableEntry.first); | |
645 | } |
|
620 | } | |
646 | } |
|
621 | } | |
647 |
|
622 | |||
648 | void VisualizationGraphWidget::enterEvent(QEvent *event) |
|
623 | void VisualizationGraphWidget::enterEvent(QEvent *event) | |
649 | { |
|
624 | { | |
650 | Q_UNUSED(event); |
|
625 | Q_UNUSED(event); | |
651 | impl->m_RenderingDelegate->showGraphOverlay(true); |
|
626 | impl->m_RenderingDelegate->showGraphOverlay(true); | |
652 | } |
|
627 | } | |
653 |
|
628 | |||
654 | void VisualizationGraphWidget::leaveEvent(QEvent *event) |
|
629 | void VisualizationGraphWidget::leaveEvent(QEvent *event) | |
655 | { |
|
630 | { | |
656 | Q_UNUSED(event); |
|
631 | Q_UNUSED(event); | |
657 | impl->m_RenderingDelegate->showGraphOverlay(false); |
|
632 | impl->m_RenderingDelegate->showGraphOverlay(false); | |
658 |
|
633 | |||
659 | if (auto parentZone = parentZoneWidget()) { |
|
634 | if (auto parentZone = parentZoneWidget()) { | |
660 | parentZone->notifyMouseLeaveGraph(this); |
|
635 | parentZone->notifyMouseLeaveGraph(this); | |
661 | } |
|
636 | } | |
662 | else { |
|
637 | else { | |
663 | qCWarning(LOG_VisualizationGraphWidget()) << "leaveEvent: No parent zone widget"; |
|
638 | qCWarning(LOG_VisualizationGraphWidget()) << "leaveEvent: No parent zone widget"; | |
664 | } |
|
639 | } | |
665 |
|
640 | |||
666 | if (impl->m_HoveredZone) { |
|
641 | if (impl->m_HoveredZone) { | |
667 | impl->m_HoveredZone->setHovered(false); |
|
642 | impl->m_HoveredZone->setHovered(false); | |
668 | impl->m_HoveredZone = nullptr; |
|
643 | impl->m_HoveredZone = nullptr; | |
669 | } |
|
644 | } | |
670 | } |
|
645 | } | |
671 |
|
646 | |||
672 | QCustomPlot &VisualizationGraphWidget::plot() const noexcept |
|
647 | QCustomPlot &VisualizationGraphWidget::plot() const noexcept | |
673 | { |
|
648 | { | |
674 | return *ui->widget; |
|
649 | return *ui->widget; | |
675 | } |
|
650 | } | |
676 |
|
651 | |||
677 | void VisualizationGraphWidget::onGraphMenuRequested(const QPoint &pos) noexcept |
|
652 | void VisualizationGraphWidget::onGraphMenuRequested(const QPoint &pos) noexcept | |
678 | { |
|
653 | { | |
679 | QMenu graphMenu{}; |
|
654 | QMenu graphMenu{}; | |
680 |
|
655 | |||
681 | // Iterates on variables (unique keys) |
|
656 | // Iterates on variables (unique keys) | |
682 | for (auto it = impl->m_VariableToPlotMultiMap.cbegin(), |
|
657 | for (auto it = impl->m_VariableToPlotMultiMap.cbegin(), | |
683 | end = impl->m_VariableToPlotMultiMap.cend(); |
|
658 | end = impl->m_VariableToPlotMultiMap.cend(); | |
684 | it != end; it = impl->m_VariableToPlotMultiMap.upper_bound(it->first)) { |
|
659 | it != end; it = impl->m_VariableToPlotMultiMap.upper_bound(it->first)) { | |
685 | // 'Remove variable' action |
|
660 | // 'Remove variable' action | |
686 | graphMenu.addAction(tr("Remove variable %1").arg(it->first->name()), |
|
661 | graphMenu.addAction(tr("Remove variable %1").arg(it->first->name()), | |
687 | [ this, var = it->first ]() { removeVariable(var); }); |
|
662 | [ this, var = it->first ]() { removeVariable(var); }); | |
688 | } |
|
663 | } | |
689 |
|
664 | |||
690 | if (!impl->m_ZoomStack.isEmpty()) { |
|
665 | if (!impl->m_ZoomStack.isEmpty()) { | |
691 | if (!graphMenu.isEmpty()) { |
|
666 | if (!graphMenu.isEmpty()) { | |
692 | graphMenu.addSeparator(); |
|
667 | graphMenu.addSeparator(); | |
693 | } |
|
668 | } | |
694 |
|
669 | |||
695 | graphMenu.addAction(tr("Undo Zoom"), [this]() { undoZoom(); }); |
|
670 | graphMenu.addAction(tr("Undo Zoom"), [this]() { undoZoom(); }); | |
696 | } |
|
671 | } | |
697 |
|
672 | |||
698 | // Selection Zone Actions |
|
673 | // Selection Zone Actions | |
699 | auto selectionZoneItem = impl->selectionZoneAt(pos, plot()); |
|
674 | auto selectionZoneItem = impl->selectionZoneAt(pos, plot()); | |
700 | if (selectionZoneItem) { |
|
675 | if (selectionZoneItem) { | |
701 | auto selectedItems = parentVisualizationWidget()->selectionZoneManager().selectedItems(); |
|
676 | auto selectedItems = parentVisualizationWidget()->selectionZoneManager().selectedItems(); | |
702 | selectedItems.removeAll(selectionZoneItem); |
|
677 | selectedItems.removeAll(selectionZoneItem); | |
703 | selectedItems.prepend(selectionZoneItem); // Put the current selection zone first |
|
678 | selectedItems.prepend(selectionZoneItem); // Put the current selection zone first | |
704 |
|
679 | |||
705 | auto zoneActions = sqpApp->actionsGuiController().selectionZoneActions(); |
|
680 | auto zoneActions = sqpApp->actionsGuiController().selectionZoneActions(); | |
706 | if (!zoneActions.isEmpty() && !graphMenu.isEmpty()) { |
|
681 | if (!zoneActions.isEmpty() && !graphMenu.isEmpty()) { | |
707 | graphMenu.addSeparator(); |
|
682 | graphMenu.addSeparator(); | |
708 | } |
|
683 | } | |
709 |
|
684 | |||
710 | QHash<QString, QMenu *> subMenus; |
|
685 | QHash<QString, QMenu *> subMenus; | |
711 | QHash<QString, bool> subMenusEnabled; |
|
686 | QHash<QString, bool> subMenusEnabled; | |
712 | QHash<QString, FilteringAction *> filteredMenu; |
|
687 | QHash<QString, FilteringAction *> filteredMenu; | |
713 |
|
688 | |||
714 | for (auto zoneAction : zoneActions) { |
|
689 | for (auto zoneAction : zoneActions) { | |
715 |
|
690 | |||
716 | auto isEnabled = zoneAction->isEnabled(selectedItems); |
|
691 | auto isEnabled = zoneAction->isEnabled(selectedItems); | |
717 |
|
692 | |||
718 | auto menu = &graphMenu; |
|
693 | auto menu = &graphMenu; | |
719 | QString menuPath; |
|
694 | QString menuPath; | |
720 | for (auto subMenuName : zoneAction->subMenuList()) { |
|
695 | for (auto subMenuName : zoneAction->subMenuList()) { | |
721 | menuPath += '/'; |
|
696 | menuPath += '/'; | |
722 | menuPath += subMenuName; |
|
697 | menuPath += subMenuName; | |
723 |
|
698 | |||
724 | if (!subMenus.contains(menuPath)) { |
|
699 | if (!subMenus.contains(menuPath)) { | |
725 | menu = menu->addMenu(subMenuName); |
|
700 | menu = menu->addMenu(subMenuName); | |
726 | subMenus[menuPath] = menu; |
|
701 | subMenus[menuPath] = menu; | |
727 | subMenusEnabled[menuPath] = isEnabled; |
|
702 | subMenusEnabled[menuPath] = isEnabled; | |
728 | } |
|
703 | } | |
729 | else { |
|
704 | else { | |
730 | menu = subMenus.value(menuPath); |
|
705 | menu = subMenus.value(menuPath); | |
731 | if (isEnabled) { |
|
706 | if (isEnabled) { | |
732 | // The sub menu is enabled if at least one of its actions is enabled |
|
707 | // The sub menu is enabled if at least one of its actions is enabled | |
733 | subMenusEnabled[menuPath] = true; |
|
708 | subMenusEnabled[menuPath] = true; | |
734 | } |
|
709 | } | |
735 | } |
|
710 | } | |
736 | } |
|
711 | } | |
737 |
|
712 | |||
738 | FilteringAction *filterAction = nullptr; |
|
713 | FilteringAction *filterAction = nullptr; | |
739 | if (sqpApp->actionsGuiController().isMenuFiltered(zoneAction->subMenuList())) { |
|
714 | if (sqpApp->actionsGuiController().isMenuFiltered(zoneAction->subMenuList())) { | |
740 | filterAction = filteredMenu.value(menuPath); |
|
715 | filterAction = filteredMenu.value(menuPath); | |
741 | if (!filterAction) { |
|
716 | if (!filterAction) { | |
742 | filterAction = new FilteringAction{this}; |
|
717 | filterAction = new FilteringAction{this}; | |
743 | filteredMenu[menuPath] = filterAction; |
|
718 | filteredMenu[menuPath] = filterAction; | |
744 | menu->addAction(filterAction); |
|
719 | menu->addAction(filterAction); | |
745 | } |
|
720 | } | |
746 | } |
|
721 | } | |
747 |
|
722 | |||
748 | auto action = menu->addAction(zoneAction->name()); |
|
723 | auto action = menu->addAction(zoneAction->name()); | |
749 | action->setEnabled(isEnabled); |
|
724 | action->setEnabled(isEnabled); | |
750 | action->setShortcut(zoneAction->displayedShortcut()); |
|
725 | action->setShortcut(zoneAction->displayedShortcut()); | |
751 | QObject::connect(action, &QAction::triggered, |
|
726 | QObject::connect(action, &QAction::triggered, | |
752 | [zoneAction, selectedItems]() { zoneAction->execute(selectedItems); }); |
|
727 | [zoneAction, selectedItems]() { zoneAction->execute(selectedItems); }); | |
753 |
|
728 | |||
754 | if (filterAction && zoneAction->isFilteringAllowed()) { |
|
729 | if (filterAction && zoneAction->isFilteringAllowed()) { | |
755 | filterAction->addActionToFilter(action); |
|
730 | filterAction->addActionToFilter(action); | |
756 | } |
|
731 | } | |
757 | } |
|
732 | } | |
758 |
|
733 | |||
759 | for (auto it = subMenus.cbegin(); it != subMenus.cend(); ++it) { |
|
734 | for (auto it = subMenus.cbegin(); it != subMenus.cend(); ++it) { | |
760 | it.value()->setEnabled(subMenusEnabled[it.key()]); |
|
735 | it.value()->setEnabled(subMenusEnabled[it.key()]); | |
761 | } |
|
736 | } | |
762 | } |
|
737 | } | |
763 |
|
738 | |||
764 | if (!graphMenu.isEmpty()) { |
|
739 | if (!graphMenu.isEmpty()) { | |
765 | graphMenu.exec(QCursor::pos()); |
|
740 | graphMenu.exec(QCursor::pos()); | |
766 | } |
|
741 | } | |
767 | } |
|
742 | } | |
768 |
|
743 | |||
769 | void VisualizationGraphWidget::onRangeChanged(const QCPRange &t1, const QCPRange &t2) |
|
744 | void VisualizationGraphWidget::onRangeChanged(const QCPRange &t1, const QCPRange &t2) | |
770 | { |
|
745 | { | |
771 | qCDebug(LOG_VisualizationGraphWidget()) << tr("TORM: VisualizationGraphWidget::onRangeChanged") |
|
|||
772 | << QThread::currentThread()->objectName() << "DoAcqui" |
|
|||
773 | << impl->m_Flags.testFlag(GraphFlag::EnableAcquisition); |
|
|||
774 |
|
||||
775 | auto graphRange = DateTimeRange{t1.lower, t1.upper}; |
|
746 | auto graphRange = DateTimeRange{t1.lower, t1.upper}; | |
776 | auto oldGraphRange = DateTimeRange{t2.lower, t2.upper}; |
|
747 | auto oldGraphRange = DateTimeRange{t2.lower, t2.upper}; | |
777 |
|
748 | |||
778 | if (impl->m_Flags.testFlag(GraphFlag::EnableAcquisition)) { |
|
749 | if (impl->m_Flags.testFlag(GraphFlag::EnableAcquisition)) { | |
779 | QVector<std::shared_ptr<Variable> > variableUnderGraphVector; |
|
|||
780 |
|
||||
781 | for (auto it = impl->m_VariableToPlotMultiMap.begin(), |
|
750 | for (auto it = impl->m_VariableToPlotMultiMap.begin(), | |
782 | end = impl->m_VariableToPlotMultiMap.end(); |
|
751 | end = impl->m_VariableToPlotMultiMap.end(); | |
783 | it != end; it = impl->m_VariableToPlotMultiMap.upper_bound(it->first)) { |
|
752 | it != end; it = impl->m_VariableToPlotMultiMap.upper_bound(it->first)) { | |
784 | variableUnderGraphVector.push_back(it->first); |
|
753 | sqpApp->variableController().asyncChangeRange(it->first, graphRange); | |
785 | } |
|
754 | } | |
786 | emit requestDataLoading(std::move(variableUnderGraphVector), graphRange, |
|
|||
787 | !impl->m_IsCalibration); |
|
|||
788 | } |
|
755 | } | |
789 |
|
756 | |||
790 |
if (impl->m_Flags.testFlag(GraphFlag::EnableSynchronization) && !impl->m_IsCalibration) |
|
757 | if (impl->m_Flags.testFlag(GraphFlag::EnableSynchronization) && !impl->m_IsCalibration) | |
791 | qCDebug(LOG_VisualizationGraphWidget()) |
|
758 | { | |
792 | << tr("TORM: VisualizationGraphWidget::Synchronize notify !!") |
|
|||
793 | << QThread::currentThread()->objectName() << graphRange << oldGraphRange; |
|
|||
794 | emit synchronize(graphRange, oldGraphRange); |
|
759 | emit synchronize(graphRange, oldGraphRange); | |
795 | } |
|
760 | } | |
796 |
|
761 | |||
797 | auto pos = mapFromGlobal(QCursor::pos()); |
|
762 | auto pos = mapFromGlobal(QCursor::pos()); | |
798 | auto axisPos = impl->posToAxisPos(pos, plot()); |
|
763 | auto axisPos = impl->posToAxisPos(pos, plot()); | |
799 | if (auto parentZone = parentZoneWidget()) { |
|
764 | if (auto parentZone = parentZoneWidget()) { | |
800 | if (impl->pointIsInAxisRect(axisPos, plot())) { |
|
765 | if (impl->pointIsInAxisRect(axisPos, plot())) { | |
801 | parentZone->notifyMouseMoveInGraph(pos, axisPos, this); |
|
766 | parentZone->notifyMouseMoveInGraph(pos, axisPos, this); | |
802 | } |
|
767 | } | |
803 | else { |
|
768 | else { | |
804 | parentZone->notifyMouseLeaveGraph(this); |
|
769 | parentZone->notifyMouseLeaveGraph(this); | |
805 | } |
|
770 | } | |
806 | } |
|
771 | } | |
807 | else { |
|
|||
808 | qCWarning(LOG_VisualizationGraphWidget()) << "onMouseMove: No parent zone widget"; |
|
|||
809 | } |
|
|||
810 |
|
772 | |||
811 | // Quits calibration |
|
773 | // Quits calibration | |
812 | impl->m_IsCalibration = false; |
|
774 | impl->m_IsCalibration = false; | |
813 | } |
|
775 | } | |
814 |
|
776 | |||
815 | void VisualizationGraphWidget::onMouseDoubleClick(QMouseEvent *event) noexcept |
|
777 | void VisualizationGraphWidget::onMouseDoubleClick(QMouseEvent *event) noexcept | |
816 | { |
|
778 | { | |
817 | impl->m_RenderingDelegate->onMouseDoubleClick(event); |
|
779 | impl->m_RenderingDelegate->onMouseDoubleClick(event); | |
818 | } |
|
780 | } | |
819 |
|
781 | |||
820 | void VisualizationGraphWidget::onMouseMove(QMouseEvent *event) noexcept |
|
782 | void VisualizationGraphWidget::onMouseMove(QMouseEvent *event) noexcept | |
821 | { |
|
783 | { | |
822 | // Handles plot rendering when mouse is moving |
|
784 | // Handles plot rendering when mouse is moving | |
823 | impl->m_RenderingDelegate->onMouseMove(event); |
|
785 | impl->m_RenderingDelegate->onMouseMove(event); | |
824 |
|
786 | |||
825 | auto axisPos = impl->posToAxisPos(event->pos(), plot()); |
|
787 | auto axisPos = impl->posToAxisPos(event->pos(), plot()); | |
826 |
|
788 | |||
827 | // Zoom box and zone drawing |
|
789 | // Zoom box and zone drawing | |
828 | if (impl->m_DrawingZoomRect) { |
|
790 | if (impl->m_DrawingZoomRect) { | |
829 | impl->m_DrawingZoomRect->bottomRight->setCoords(axisPos); |
|
791 | impl->m_DrawingZoomRect->bottomRight->setCoords(axisPos); | |
830 | } |
|
792 | } | |
831 | else if (impl->m_DrawingZone) { |
|
793 | else if (impl->m_DrawingZone) { | |
832 | impl->m_DrawingZone->setEnd(axisPos.x()); |
|
794 | impl->m_DrawingZone->setEnd(axisPos.x()); | |
833 | } |
|
795 | } | |
834 |
|
796 | |||
835 | // Cursor |
|
797 | // Cursor | |
836 | if (auto parentZone = parentZoneWidget()) { |
|
798 | if (auto parentZone = parentZoneWidget()) { | |
837 | if (impl->pointIsInAxisRect(axisPos, plot())) { |
|
799 | if (impl->pointIsInAxisRect(axisPos, plot())) { | |
838 | parentZone->notifyMouseMoveInGraph(event->pos(), axisPos, this); |
|
800 | parentZone->notifyMouseMoveInGraph(event->pos(), axisPos, this); | |
839 | } |
|
801 | } | |
840 | else { |
|
802 | else { | |
841 | parentZone->notifyMouseLeaveGraph(this); |
|
803 | parentZone->notifyMouseLeaveGraph(this); | |
842 | } |
|
804 | } | |
843 | } |
|
805 | } | |
844 | else { |
|
|||
845 | qCWarning(LOG_VisualizationGraphWidget()) << "onMouseMove: No parent zone widget"; |
|
|||
846 | } |
|
|||
847 |
|
806 | |||
848 | // Search for the selection zone under the mouse |
|
807 | // Search for the selection zone under the mouse | |
849 | auto selectionZoneItemUnderCursor = impl->selectionZoneAt(event->pos(), plot()); |
|
808 | auto selectionZoneItemUnderCursor = impl->selectionZoneAt(event->pos(), plot()); | |
850 | if (selectionZoneItemUnderCursor && !impl->m_DrawingZone |
|
809 | if (selectionZoneItemUnderCursor && !impl->m_DrawingZone | |
851 | && sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones) { |
|
810 | && sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones) { | |
852 |
|
811 | |||
853 | // Sets the appropriate cursor shape |
|
812 | // Sets the appropriate cursor shape | |
854 | auto cursorShape = selectionZoneItemUnderCursor->curshorShapeForPosition(event->pos()); |
|
813 | auto cursorShape = selectionZoneItemUnderCursor->curshorShapeForPosition(event->pos()); | |
855 | setCursor(cursorShape); |
|
814 | setCursor(cursorShape); | |
856 |
|
815 | |||
857 | // Manages the hovered zone |
|
816 | // Manages the hovered zone | |
858 | if (selectionZoneItemUnderCursor != impl->m_HoveredZone) { |
|
817 | if (selectionZoneItemUnderCursor != impl->m_HoveredZone) { | |
859 | if (impl->m_HoveredZone) { |
|
818 | if (impl->m_HoveredZone) { | |
860 | impl->m_HoveredZone->setHovered(false); |
|
819 | impl->m_HoveredZone->setHovered(false); | |
861 | } |
|
820 | } | |
862 | selectionZoneItemUnderCursor->setHovered(true); |
|
821 | selectionZoneItemUnderCursor->setHovered(true); | |
863 | impl->m_HoveredZone = selectionZoneItemUnderCursor; |
|
822 | impl->m_HoveredZone = selectionZoneItemUnderCursor; | |
864 | plot().replot(QCustomPlot::rpQueuedReplot); |
|
823 | plot().replot(QCustomPlot::rpQueuedReplot); | |
865 | } |
|
824 | } | |
866 | } |
|
825 | } | |
867 | else { |
|
826 | else { | |
868 | // There is no zone under the mouse or the interaction mode is not "selection zones" |
|
827 | // There is no zone under the mouse or the interaction mode is not "selection zones" | |
869 | if (impl->m_HoveredZone) { |
|
828 | if (impl->m_HoveredZone) { | |
870 | impl->m_HoveredZone->setHovered(false); |
|
829 | impl->m_HoveredZone->setHovered(false); | |
871 | impl->m_HoveredZone = nullptr; |
|
830 | impl->m_HoveredZone = nullptr; | |
872 | } |
|
831 | } | |
873 |
|
832 | |||
874 | setCursor(Qt::ArrowCursor); |
|
833 | setCursor(Qt::ArrowCursor); | |
875 | } |
|
834 | } | |
876 |
|
835 | |||
877 | impl->m_HasMovedMouse = true; |
|
836 | impl->m_HasMovedMouse = true; | |
878 | VisualizationDragWidget::mouseMoveEvent(event); |
|
837 | VisualizationDragWidget::mouseMoveEvent(event); | |
879 | } |
|
838 | } | |
880 |
|
839 | |||
881 | void VisualizationGraphWidget::onMouseWheel(QWheelEvent *event) noexcept |
|
840 | void VisualizationGraphWidget::onMouseWheel(QWheelEvent *event) noexcept | |
882 | { |
|
841 | { | |
883 | // Processes event only if the wheel occurs on axis rect |
|
842 | // Processes event only if the wheel occurs on axis rect | |
884 | if (!dynamic_cast<QCPAxisRect *>(ui->widget->layoutElementAt(event->posF()))) { |
|
843 | if (!dynamic_cast<QCPAxisRect *>(ui->widget->layoutElementAt(event->posF()))) { | |
885 | return; |
|
844 | return; | |
886 | } |
|
845 | } | |
887 |
|
846 | |||
888 | auto value = event->angleDelta().x() + event->angleDelta().y(); |
|
847 | auto value = event->angleDelta().x() + event->angleDelta().y(); | |
889 | if (value != 0) { |
|
848 | if (value != 0) { | |
890 |
|
849 | |||
891 | auto direction = value > 0 ? 1.0 : -1.0; |
|
850 | auto direction = value > 0 ? 1.0 : -1.0; | |
892 | auto isZoomX = event->modifiers().testFlag(HORIZONTAL_ZOOM_MODIFIER); |
|
851 | auto isZoomX = event->modifiers().testFlag(HORIZONTAL_ZOOM_MODIFIER); | |
893 | auto isZoomY = event->modifiers().testFlag(VERTICAL_ZOOM_MODIFIER); |
|
852 | auto isZoomY = event->modifiers().testFlag(VERTICAL_ZOOM_MODIFIER); | |
894 | impl->m_IsCalibration = event->modifiers().testFlag(VERTICAL_PAN_MODIFIER); |
|
853 | impl->m_IsCalibration = event->modifiers().testFlag(VERTICAL_PAN_MODIFIER); | |
895 |
|
854 | |||
896 | auto zoomOrientations = QFlags<Qt::Orientation>{}; |
|
855 | auto zoomOrientations = QFlags<Qt::Orientation>{}; | |
897 | zoomOrientations.setFlag(Qt::Horizontal, isZoomX); |
|
856 | zoomOrientations.setFlag(Qt::Horizontal, isZoomX); | |
898 | zoomOrientations.setFlag(Qt::Vertical, isZoomY); |
|
857 | zoomOrientations.setFlag(Qt::Vertical, isZoomY); | |
899 |
|
858 | |||
900 | ui->widget->axisRect()->setRangeZoom(zoomOrientations); |
|
859 | ui->widget->axisRect()->setRangeZoom(zoomOrientations); | |
901 |
|
860 | |||
902 | if (!isZoomX && !isZoomY) { |
|
861 | if (!isZoomX && !isZoomY) { | |
903 | auto axis = plot().axisRect()->axis(QCPAxis::atBottom); |
|
862 | auto axis = plot().axisRect()->axis(QCPAxis::atBottom); | |
904 | auto diff = direction * (axis->range().size() * (PAN_SPEED / 100.0)); |
|
863 | auto diff = direction * (axis->range().size() * (PAN_SPEED / 100.0)); | |
905 |
|
864 | |||
906 | axis->setRange(axis->range() + diff); |
|
865 | axis->setRange(axis->range() + diff); | |
907 |
|
866 | |||
908 | if (plot().noAntialiasingOnDrag()) { |
|
867 | if (plot().noAntialiasingOnDrag()) { | |
909 | plot().setNotAntialiasedElements(QCP::aeAll); |
|
868 | plot().setNotAntialiasedElements(QCP::aeAll); | |
910 | } |
|
869 | } | |
911 |
|
870 | |||
912 | plot().replot(QCustomPlot::rpQueuedReplot); |
|
871 | //plot().replot(QCustomPlot::rpQueuedReplot); | |
913 | } |
|
872 | } | |
914 | } |
|
873 | } | |
915 | } |
|
874 | } | |
916 |
|
875 | |||
917 | void VisualizationGraphWidget::onMousePress(QMouseEvent *event) noexcept |
|
876 | void VisualizationGraphWidget::onMousePress(QMouseEvent *event) noexcept | |
918 | { |
|
877 | { | |
919 | auto isDragDropClick = event->modifiers().testFlag(DRAG_DROP_MODIFIER); |
|
878 | auto isDragDropClick = event->modifiers().testFlag(DRAG_DROP_MODIFIER); | |
920 | auto isSelectionZoneMode |
|
879 | auto isSelectionZoneMode | |
921 | = sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones; |
|
880 | = sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones; | |
922 | auto isLeftClick = event->buttons().testFlag(Qt::LeftButton); |
|
881 | auto isLeftClick = event->buttons().testFlag(Qt::LeftButton); | |
923 |
|
882 | |||
924 | if (!isDragDropClick && isLeftClick) { |
|
883 | if (!isDragDropClick && isLeftClick) { | |
925 | if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::ZoomBox) { |
|
884 | if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::ZoomBox) { | |
926 | // Starts a zoom box |
|
885 | // Starts a zoom box | |
927 | impl->startDrawingRect(event->pos(), plot()); |
|
886 | impl->startDrawingRect(event->pos(), plot()); | |
928 | } |
|
887 | } | |
929 | else if (isSelectionZoneMode && impl->m_DrawingZone == nullptr) { |
|
888 | else if (isSelectionZoneMode && impl->m_DrawingZone == nullptr) { | |
930 | // Starts a new selection zone |
|
889 | // Starts a new selection zone | |
931 | auto zoneAtPos = impl->selectionZoneAt(event->pos(), plot()); |
|
890 | auto zoneAtPos = impl->selectionZoneAt(event->pos(), plot()); | |
932 | if (!zoneAtPos) { |
|
891 | if (!zoneAtPos) { | |
933 | impl->startDrawingZone(event->pos(), this); |
|
892 | impl->startDrawingZone(event->pos(), this); | |
934 | } |
|
893 | } | |
935 | } |
|
894 | } | |
936 | } |
|
895 | } | |
937 |
|
896 | |||
938 | // Allows mouse panning only in default mode |
|
897 | // Allows mouse panning only in default mode | |
939 | plot().setInteraction(QCP::iRangeDrag, sqpApp->plotsInteractionMode() |
|
898 | plot().setInteraction(QCP::iRangeDrag, sqpApp->plotsInteractionMode() | |
940 | == SqpApplication::PlotsInteractionMode::None |
|
899 | == SqpApplication::PlotsInteractionMode::None | |
941 | && !isDragDropClick); |
|
900 | && !isDragDropClick); | |
942 |
|
901 | |||
943 | // Allows zone edition only in selection zone mode without drag&drop |
|
902 | // Allows zone edition only in selection zone mode without drag&drop | |
944 | impl->setSelectionZonesEditionEnabled(isSelectionZoneMode && !isDragDropClick); |
|
903 | impl->setSelectionZonesEditionEnabled(isSelectionZoneMode && !isDragDropClick); | |
945 |
|
904 | |||
946 | // Selection / Deselection |
|
905 | // Selection / Deselection | |
947 | if (isSelectionZoneMode) { |
|
906 | if (isSelectionZoneMode) { | |
948 | auto isMultiSelectionClick = event->modifiers().testFlag(MULTI_ZONE_SELECTION_MODIFIER); |
|
907 | auto isMultiSelectionClick = event->modifiers().testFlag(MULTI_ZONE_SELECTION_MODIFIER); | |
949 | auto selectionZoneItemUnderCursor = impl->selectionZoneAt(event->pos(), plot()); |
|
908 | auto selectionZoneItemUnderCursor = impl->selectionZoneAt(event->pos(), plot()); | |
950 |
|
909 | |||
951 |
|
910 | |||
952 | if (selectionZoneItemUnderCursor && !selectionZoneItemUnderCursor->selected() |
|
911 | if (selectionZoneItemUnderCursor && !selectionZoneItemUnderCursor->selected() | |
953 | && !isMultiSelectionClick) { |
|
912 | && !isMultiSelectionClick) { | |
954 | parentVisualizationWidget()->selectionZoneManager().select( |
|
913 | parentVisualizationWidget()->selectionZoneManager().select( | |
955 | {selectionZoneItemUnderCursor}); |
|
914 | {selectionZoneItemUnderCursor}); | |
956 | } |
|
915 | } | |
957 | else if (!selectionZoneItemUnderCursor && !isMultiSelectionClick && isLeftClick) { |
|
916 | else if (!selectionZoneItemUnderCursor && !isMultiSelectionClick && isLeftClick) { | |
958 | parentVisualizationWidget()->selectionZoneManager().clearSelection(); |
|
917 | parentVisualizationWidget()->selectionZoneManager().clearSelection(); | |
959 | } |
|
918 | } | |
960 | else { |
|
919 | else { | |
961 | // No selection change |
|
920 | // No selection change | |
962 | } |
|
921 | } | |
963 |
|
922 | |||
964 | if (selectionZoneItemUnderCursor && isLeftClick) { |
|
923 | if (selectionZoneItemUnderCursor && isLeftClick) { | |
965 | selectionZoneItemUnderCursor->setAssociatedEditedZones( |
|
924 | selectionZoneItemUnderCursor->setAssociatedEditedZones( | |
966 | parentVisualizationWidget()->selectionZoneManager().selectedItems()); |
|
925 | parentVisualizationWidget()->selectionZoneManager().selectedItems()); | |
967 | } |
|
926 | } | |
968 | } |
|
927 | } | |
969 |
|
928 | |||
970 |
|
929 | |||
971 | impl->m_HasMovedMouse = false; |
|
930 | impl->m_HasMovedMouse = false; | |
972 | VisualizationDragWidget::mousePressEvent(event); |
|
931 | VisualizationDragWidget::mousePressEvent(event); | |
973 | } |
|
932 | } | |
974 |
|
933 | |||
975 | void VisualizationGraphWidget::onMouseRelease(QMouseEvent *event) noexcept |
|
934 | void VisualizationGraphWidget::onMouseRelease(QMouseEvent *event) noexcept | |
976 | { |
|
935 | { | |
977 | if (impl->m_DrawingZoomRect) { |
|
936 | if (impl->m_DrawingZoomRect) { | |
978 |
|
937 | |||
979 | auto axisX = plot().axisRect()->axis(QCPAxis::atBottom); |
|
938 | auto axisX = plot().axisRect()->axis(QCPAxis::atBottom); | |
980 | auto axisY = plot().axisRect()->axis(QCPAxis::atLeft); |
|
939 | auto axisY = plot().axisRect()->axis(QCPAxis::atLeft); | |
981 |
|
940 | |||
982 | auto newAxisXRange = QCPRange{impl->m_DrawingZoomRect->topLeft->coords().x(), |
|
941 | auto newAxisXRange = QCPRange{impl->m_DrawingZoomRect->topLeft->coords().x(), | |
983 | impl->m_DrawingZoomRect->bottomRight->coords().x()}; |
|
942 | impl->m_DrawingZoomRect->bottomRight->coords().x()}; | |
984 |
|
943 | |||
985 | auto newAxisYRange = QCPRange{impl->m_DrawingZoomRect->topLeft->coords().y(), |
|
944 | auto newAxisYRange = QCPRange{impl->m_DrawingZoomRect->topLeft->coords().y(), | |
986 | impl->m_DrawingZoomRect->bottomRight->coords().y()}; |
|
945 | impl->m_DrawingZoomRect->bottomRight->coords().y()}; | |
987 |
|
946 | |||
988 | impl->removeDrawingRect(plot()); |
|
947 | impl->removeDrawingRect(plot()); | |
989 |
|
948 | |||
990 | if (newAxisXRange.size() > axisX->range().size() * (ZOOM_BOX_MIN_SIZE / 100.0) |
|
949 | if (newAxisXRange.size() > axisX->range().size() * (ZOOM_BOX_MIN_SIZE / 100.0) | |
991 | && newAxisYRange.size() > axisY->range().size() * (ZOOM_BOX_MIN_SIZE / 100.0)) { |
|
950 | && newAxisYRange.size() > axisY->range().size() * (ZOOM_BOX_MIN_SIZE / 100.0)) { | |
992 | impl->m_ZoomStack.push(qMakePair(axisX->range(), axisY->range())); |
|
951 | impl->m_ZoomStack.push(qMakePair(axisX->range(), axisY->range())); | |
993 | axisX->setRange(newAxisXRange); |
|
952 | axisX->setRange(newAxisXRange); | |
994 | axisY->setRange(newAxisYRange); |
|
953 | axisY->setRange(newAxisYRange); | |
995 |
|
954 | |||
996 | plot().replot(QCustomPlot::rpQueuedReplot); |
|
955 | plot().replot(QCustomPlot::rpQueuedReplot); | |
997 | } |
|
956 | } | |
998 | } |
|
957 | } | |
999 |
|
958 | |||
1000 | impl->endDrawingZone(this); |
|
959 | impl->endDrawingZone(this); | |
1001 |
|
960 | |||
1002 | // Selection / Deselection |
|
961 | // Selection / Deselection | |
1003 | auto isSelectionZoneMode |
|
962 | auto isSelectionZoneMode | |
1004 | = sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones; |
|
963 | = sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones; | |
1005 | if (isSelectionZoneMode) { |
|
964 | if (isSelectionZoneMode) { | |
1006 | auto isMultiSelectionClick = event->modifiers().testFlag(MULTI_ZONE_SELECTION_MODIFIER); |
|
965 | auto isMultiSelectionClick = event->modifiers().testFlag(MULTI_ZONE_SELECTION_MODIFIER); | |
1007 | auto selectionZoneItemUnderCursor = impl->selectionZoneAt(event->pos(), plot()); |
|
966 | auto selectionZoneItemUnderCursor = impl->selectionZoneAt(event->pos(), plot()); | |
1008 | if (selectionZoneItemUnderCursor && event->button() == Qt::LeftButton |
|
967 | if (selectionZoneItemUnderCursor && event->button() == Qt::LeftButton | |
1009 | && !impl->m_HasMovedMouse) { |
|
968 | && !impl->m_HasMovedMouse) { | |
1010 |
|
969 | |||
1011 | auto zonesUnderCursor = impl->selectionZonesAt(event->pos(), plot()); |
|
970 | auto zonesUnderCursor = impl->selectionZonesAt(event->pos(), plot()); | |
1012 | if (zonesUnderCursor.count() > 1) { |
|
971 | if (zonesUnderCursor.count() > 1) { | |
1013 | // There are multiple zones under the mouse. |
|
972 | // There are multiple zones under the mouse. | |
1014 | // Performs the selection with a selection dialog. |
|
973 | // Performs the selection with a selection dialog. | |
1015 | VisualizationMultiZoneSelectionDialog dialog{this}; |
|
974 | VisualizationMultiZoneSelectionDialog dialog{this}; | |
1016 | dialog.setZones(zonesUnderCursor); |
|
975 | dialog.setZones(zonesUnderCursor); | |
1017 | dialog.move(mapToGlobal(event->pos() - QPoint(dialog.width() / 2, 20))); |
|
976 | dialog.move(mapToGlobal(event->pos() - QPoint(dialog.width() / 2, 20))); | |
1018 | dialog.activateWindow(); |
|
977 | dialog.activateWindow(); | |
1019 | dialog.raise(); |
|
978 | dialog.raise(); | |
1020 | if (dialog.exec() == QDialog::Accepted) { |
|
979 | if (dialog.exec() == QDialog::Accepted) { | |
1021 | auto selection = dialog.selectedZones(); |
|
980 | auto selection = dialog.selectedZones(); | |
1022 |
|
981 | |||
1023 | if (!isMultiSelectionClick) { |
|
982 | if (!isMultiSelectionClick) { | |
1024 | parentVisualizationWidget()->selectionZoneManager().clearSelection(); |
|
983 | parentVisualizationWidget()->selectionZoneManager().clearSelection(); | |
1025 | } |
|
984 | } | |
1026 |
|
985 | |||
1027 | for (auto it = selection.cbegin(); it != selection.cend(); ++it) { |
|
986 | for (auto it = selection.cbegin(); it != selection.cend(); ++it) { | |
1028 | auto zone = it.key(); |
|
987 | auto zone = it.key(); | |
1029 | auto isSelected = it.value(); |
|
988 | auto isSelected = it.value(); | |
1030 | parentVisualizationWidget()->selectionZoneManager().setSelected(zone, |
|
989 | parentVisualizationWidget()->selectionZoneManager().setSelected(zone, | |
1031 | isSelected); |
|
990 | isSelected); | |
1032 |
|
991 | |||
1033 | if (isSelected) { |
|
992 | if (isSelected) { | |
1034 | // Puts the zone on top of the stack so it can be moved or resized |
|
993 | // Puts the zone on top of the stack so it can be moved or resized | |
1035 | impl->moveSelectionZoneOnTop(zone, plot()); |
|
994 | impl->moveSelectionZoneOnTop(zone, plot()); | |
1036 | } |
|
995 | } | |
1037 | } |
|
996 | } | |
1038 | } |
|
997 | } | |
1039 | } |
|
998 | } | |
1040 | else { |
|
999 | else { | |
1041 | if (!isMultiSelectionClick) { |
|
1000 | if (!isMultiSelectionClick) { | |
1042 | parentVisualizationWidget()->selectionZoneManager().select( |
|
1001 | parentVisualizationWidget()->selectionZoneManager().select( | |
1043 | {selectionZoneItemUnderCursor}); |
|
1002 | {selectionZoneItemUnderCursor}); | |
1044 | impl->moveSelectionZoneOnTop(selectionZoneItemUnderCursor, plot()); |
|
1003 | impl->moveSelectionZoneOnTop(selectionZoneItemUnderCursor, plot()); | |
1045 | } |
|
1004 | } | |
1046 | else { |
|
1005 | else { | |
1047 | parentVisualizationWidget()->selectionZoneManager().setSelected( |
|
1006 | parentVisualizationWidget()->selectionZoneManager().setSelected( | |
1048 | selectionZoneItemUnderCursor, !selectionZoneItemUnderCursor->selected() |
|
1007 | selectionZoneItemUnderCursor, !selectionZoneItemUnderCursor->selected() | |
1049 | || event->button() == Qt::RightButton); |
|
1008 | || event->button() == Qt::RightButton); | |
1050 | } |
|
1009 | } | |
1051 | } |
|
1010 | } | |
1052 | } |
|
1011 | } | |
1053 | else { |
|
1012 | else { | |
1054 | // No selection change |
|
1013 | // No selection change | |
1055 | } |
|
1014 | } | |
1056 | } |
|
1015 | } | |
1057 | } |
|
1016 | } | |
1058 |
|
1017 | |||
1059 | void VisualizationGraphWidget::onDataCacheVariableUpdated() |
|
1018 | void VisualizationGraphWidget::onDataCacheVariableUpdated() | |
1060 | { |
|
1019 | { | |
1061 | auto graphRange = ui->widget->xAxis->range(); |
|
1020 | auto graphRange = ui->widget->xAxis->range(); | |
1062 | auto dateTime = DateTimeRange{graphRange.lower, graphRange.upper}; |
|
1021 | auto dateTime = DateTimeRange{graphRange.lower, graphRange.upper}; | |
1063 |
|
1022 | |||
1064 | for (auto &variableEntry : impl->m_VariableToPlotMultiMap) { |
|
1023 | for (auto &variableEntry : impl->m_VariableToPlotMultiMap) { | |
1065 | auto variable = variableEntry.first; |
|
1024 | auto variable = variableEntry.first; | |
1066 | qCDebug(LOG_VisualizationGraphWidget()) |
|
1025 | qCDebug(LOG_VisualizationGraphWidget()) | |
1067 | << "TORM: VisualizationGraphWidget::onDataCacheVariableUpdated S" << variable->range(); |
|
1026 | << "TORM: VisualizationGraphWidget::onDataCacheVariableUpdated S" << variable->range(); | |
1068 | qCDebug(LOG_VisualizationGraphWidget()) |
|
1027 | qCDebug(LOG_VisualizationGraphWidget()) | |
1069 | << "TORM: VisualizationGraphWidget::onDataCacheVariableUpdated E" << dateTime; |
|
1028 | << "TORM: VisualizationGraphWidget::onDataCacheVariableUpdated E" << dateTime; | |
1070 | if (dateTime.contains(variable->range()) || dateTime.intersect(variable->range())) { |
|
1029 | if (dateTime.contains(variable->range()) || dateTime.intersect(variable->range())) { | |
1071 | impl->updateData(variableEntry.second, variable, variable->range()); |
|
1030 | impl->updateData(variableEntry.second, variable, variable->range()); | |
1072 | } |
|
1031 | } | |
1073 | } |
|
1032 | } | |
1074 | } |
|
1033 | } | |
1075 |
|
1034 | |||
1076 | void VisualizationGraphWidget::onUpdateVarDisplaying(std::shared_ptr<Variable> variable, |
|
1035 | void VisualizationGraphWidget::onUpdateVarDisplaying(std::shared_ptr<Variable> variable, | |
1077 | const DateTimeRange &range) |
|
1036 | const DateTimeRange &range) | |
1078 | { |
|
1037 | { | |
1079 | auto it = impl->m_VariableToPlotMultiMap.find(variable); |
|
1038 | auto it = impl->m_VariableToPlotMultiMap.find(variable); | |
1080 | if (it != impl->m_VariableToPlotMultiMap.end()) { |
|
1039 | if (it != impl->m_VariableToPlotMultiMap.end()) { | |
1081 | impl->updateData(it->second, variable, range); |
|
1040 | impl->updateData(it->second, variable, range); | |
1082 | } |
|
1041 | } | |
1083 | } |
|
1042 | } |
@@ -1,44 +1,30 | |||||
1 | #ifndef SCIQLOP_AMDAPROVIDER_H |
|
1 | #ifndef SCIQLOP_AMDAPROVIDER_H | |
2 | #define SCIQLOP_AMDAPROVIDER_H |
|
2 | #define SCIQLOP_AMDAPROVIDER_H | |
3 |
|
3 | |||
4 | #include "AmdaGlobal.h" |
|
4 | #include "AmdaGlobal.h" | |
5 |
|
5 | |||
6 | #include <Data/IDataProvider.h> |
|
6 | #include <Data/IDataProvider.h> | |
7 |
|
7 | |||
8 | #include <QLoggingCategory> |
|
8 | #include <QLoggingCategory> | |
9 |
|
9 | |||
10 | #include <map> |
|
10 | #include <map> | |
11 |
|
11 | |||
12 | Q_DECLARE_LOGGING_CATEGORY(LOG_AmdaProvider) |
|
12 | Q_DECLARE_LOGGING_CATEGORY(LOG_AmdaProvider) | |
13 |
|
13 | |||
14 | class QNetworkReply; |
|
14 | class QNetworkReply; | |
15 | class QNetworkRequest; |
|
15 | class QNetworkRequest; | |
16 |
|
16 | |||
17 | /** |
|
17 | /** | |
18 | * @brief The AmdaProvider class is an example of how a data provider can generate data |
|
18 | * @brief The AmdaProvider class is an example of how a data provider can generate data | |
19 | */ |
|
19 | */ | |
20 | class SCIQLOP_AMDA_EXPORT AmdaProvider : public IDataProvider { |
|
20 | class SCIQLOP_AMDA_EXPORT AmdaProvider : public IDataProvider { | |
21 | Q_OBJECT |
|
21 | Q_OBJECT | |
22 | public: |
|
22 | public: | |
23 | explicit AmdaProvider(); |
|
23 | explicit AmdaProvider(); | |
24 | std::shared_ptr<IDataProvider> clone() const override; |
|
24 | std::shared_ptr<IDataProvider> clone() const override; | |
25 |
|
25 | |||
26 |
v |
|
26 | virtual IDataSeries *getData(const DataProviderParameters ¶meters)override; | |
27 |
|
27 | |||
28 | void requestDataAborting(QUuid acqIdentifier) override; |
|
|||
29 |
|
||||
30 | private: |
|
|||
31 | void retrieveData(QUuid token, const DateTimeRange &dateTime, const QVariantHash &data); |
|
|||
32 |
|
||||
33 | void updateRequestProgress(QUuid acqIdentifier, std::shared_ptr<QNetworkRequest> request, |
|
|||
34 | double progress); |
|
|||
35 |
|
||||
36 | std::map<QUuid, std::map<std::shared_ptr<QNetworkRequest>, double> > |
|
|||
37 | m_AcqIdToRequestProgressMap; |
|
|||
38 |
|
||||
39 | private slots: |
|
|||
40 | void onReplyDownloadProgress(QUuid acqIdentifier, |
|
|||
41 | std::shared_ptr<QNetworkRequest> networkRequest, double progress); |
|
|||
42 | }; |
|
28 | }; | |
43 |
|
29 | |||
44 | #endif // SCIQLOP_AMDAPROVIDER_H |
|
30 | #endif // SCIQLOP_AMDAPROVIDER_H |
@@ -1,21 +1,23 | |||||
1 | #ifndef SCIQLOP_AMDARESULTPARSER_H |
|
1 | #ifndef SCIQLOP_AMDARESULTPARSER_H | |
2 | #define SCIQLOP_AMDARESULTPARSER_H |
|
2 | #define SCIQLOP_AMDARESULTPARSER_H | |
3 |
|
3 | |||
4 | #include "AmdaGlobal.h" |
|
4 | #include "AmdaGlobal.h" | |
5 |
|
5 | |||
6 | #include <Data/DataSeriesType.h> |
|
6 | #include <Data/DataSeriesType.h> | |
7 |
|
7 | |||
8 | #include <QLoggingCategory> |
|
8 | #include <QLoggingCategory> | |
9 |
|
9 | |||
10 | #include <memory> |
|
10 | #include <memory> | |
11 |
|
11 | |||
12 | class IDataSeries; |
|
12 | class IDataSeries; | |
13 |
|
13 | |||
14 | Q_DECLARE_LOGGING_CATEGORY(LOG_AmdaResultParser) |
|
14 | Q_DECLARE_LOGGING_CATEGORY(LOG_AmdaResultParser) | |
15 |
|
15 | |||
16 | struct SCIQLOP_AMDA_EXPORT AmdaResultParser { |
|
16 | struct SCIQLOP_AMDA_EXPORT AmdaResultParser { | |
17 | static std::shared_ptr<IDataSeries> readTxt(const QString &filePath, |
|
17 | static std::shared_ptr<IDataSeries> readTxt(const QString &filePath, | |
18 | DataSeriesType valueType) noexcept; |
|
18 | DataSeriesType valueType) noexcept; | |
|
19 | static IDataSeries* readTxt(QTextStream stream, | |||
|
20 | DataSeriesType type)noexcept; | |||
19 | }; |
|
21 | }; | |
20 |
|
22 | |||
21 | #endif // SCIQLOP_AMDARESULTPARSER_H |
|
23 | #endif // SCIQLOP_AMDARESULTPARSER_H |
@@ -1,107 +1,107 | |||||
1 | #ifndef SCIQLOP_AMDARESULTPARSERHELPER_H |
|
1 | #ifndef SCIQLOP_AMDARESULTPARSERHELPER_H | |
2 | #define SCIQLOP_AMDARESULTPARSERHELPER_H |
|
2 | #define SCIQLOP_AMDARESULTPARSERHELPER_H | |
3 |
|
3 | |||
4 | #include "AmdaResultParserDefs.h" |
|
4 | #include "AmdaResultParserDefs.h" | |
5 |
|
5 | |||
6 | #include <QtCore/QLoggingCategory> |
|
6 | #include <QtCore/QLoggingCategory> | |
7 | #include <QtCore/QString> |
|
7 | #include <QtCore/QString> | |
8 |
|
8 | |||
9 | #include <memory> |
|
9 | #include <memory> | |
10 |
|
10 | |||
11 | class IDataSeries; |
|
11 | class IDataSeries; | |
12 |
|
12 | |||
13 | Q_DECLARE_LOGGING_CATEGORY(LOG_AmdaResultParserHelper) |
|
13 | Q_DECLARE_LOGGING_CATEGORY(LOG_AmdaResultParserHelper) | |
14 |
|
14 | |||
15 | /** |
|
15 | /** | |
16 | * Helper used to interpret the data of an AMDA result file and generate the corresponding data |
|
16 | * Helper used to interpret the data of an AMDA result file and generate the corresponding data | |
17 | * series. |
|
17 | * series. | |
18 | * |
|
18 | * | |
19 | * It proposes methods allowing to read line by line an AMDA file and to extract the properties |
|
19 | * It proposes methods allowing to read line by line an AMDA file and to extract the properties | |
20 | * (from the header) and the values corresponding to the data series |
|
20 | * (from the header) and the values corresponding to the data series | |
21 | * |
|
21 | * | |
22 | * @sa DataSeries |
|
22 | * @sa DataSeries | |
23 | */ |
|
23 | */ | |
24 | struct IAmdaResultParserHelper { |
|
24 | struct IAmdaResultParserHelper { | |
25 | virtual ~IAmdaResultParserHelper() noexcept = default; |
|
25 | virtual ~IAmdaResultParserHelper() noexcept = default; | |
26 |
|
26 | |||
27 | /// Verifies that the extracted properties are well formed and possibly applies other treatments |
|
27 | /// Verifies that the extracted properties are well formed and possibly applies other treatments | |
28 | /// on them |
|
28 | /// on them | |
29 | /// @return true if the properties are well formed, false otherwise |
|
29 | /// @return true if the properties are well formed, false otherwise | |
30 | virtual bool checkProperties() = 0; |
|
30 | virtual bool checkProperties() = 0; | |
31 |
|
31 | |||
32 | /// Creates the data series from the properties and values extracted from the AMDA file. |
|
32 | /// Creates the data series from the properties and values extracted from the AMDA file. | |
33 | /// @warning as the data are moved in the data series, the helper shouldn't be used after |
|
33 | /// @warning as the data are moved in the data series, the helper shouldn't be used after | |
34 | /// calling this method |
|
34 | /// calling this method | |
35 | /// @return the data series created |
|
35 | /// @return the data series created | |
36 |
virtual |
|
36 | virtual IDataSeries* createSeries() = 0; | |
37 |
|
37 | |||
38 | /// Reads a line from the AMDA file to extract a property that will be used to generate the data |
|
38 | /// Reads a line from the AMDA file to extract a property that will be used to generate the data | |
39 | /// series |
|
39 | /// series | |
40 | /// @param line tahe line to interpret |
|
40 | /// @param line tahe line to interpret | |
41 | virtual void readPropertyLine(const QString &line) = 0; |
|
41 | virtual void readPropertyLine(const QString &line) = 0; | |
42 |
|
42 | |||
43 | /// Reads a line from the AMDA file to extract a value that will be set in the data series |
|
43 | /// Reads a line from the AMDA file to extract a value that will be set in the data series | |
44 | /// @param line the line to interpret |
|
44 | /// @param line the line to interpret | |
45 | virtual void readResultLine(const QString &line) = 0; |
|
45 | virtual void readResultLine(const QString &line) = 0; | |
46 | }; |
|
46 | }; | |
47 |
|
47 | |||
48 | /** |
|
48 | /** | |
49 | * Implementation of @sa IAmdaResultParserHelper for scalars |
|
49 | * Implementation of @sa IAmdaResultParserHelper for scalars | |
50 | */ |
|
50 | */ | |
51 | class ScalarParserHelper : public IAmdaResultParserHelper { |
|
51 | class ScalarParserHelper : public IAmdaResultParserHelper { | |
52 | public: |
|
52 | public: | |
53 | bool checkProperties() override; |
|
53 | bool checkProperties() override; | |
54 |
|
|
54 | IDataSeries* createSeries() override; | |
55 | void readPropertyLine(const QString &line) override; |
|
55 | void readPropertyLine(const QString &line) override; | |
56 | void readResultLine(const QString &line) override; |
|
56 | void readResultLine(const QString &line) override; | |
57 |
|
57 | |||
58 | private: |
|
58 | private: | |
59 | /// @return the reading order of the "value" columns for a result line of the AMDA file |
|
59 | /// @return the reading order of the "value" columns for a result line of the AMDA file | |
60 | std::vector<int> valuesIndexes() const; |
|
60 | std::vector<int> valuesIndexes() const; | |
61 |
|
61 | |||
62 | Properties m_Properties{}; |
|
62 | Properties m_Properties{}; | |
63 | std::vector<double> m_XAxisData{}; |
|
63 | std::vector<double> m_XAxisData{}; | |
64 | std::vector<double> m_ValuesData{}; |
|
64 | std::vector<double> m_ValuesData{}; | |
65 | }; |
|
65 | }; | |
66 |
|
66 | |||
67 | /** |
|
67 | /** | |
68 | * Implementation of @sa IAmdaResultParserHelper for spectrograms |
|
68 | * Implementation of @sa IAmdaResultParserHelper for spectrograms | |
69 | */ |
|
69 | */ | |
70 | class SpectrogramParserHelper : public IAmdaResultParserHelper { |
|
70 | class SpectrogramParserHelper : public IAmdaResultParserHelper { | |
71 | public: |
|
71 | public: | |
72 | bool checkProperties() override; |
|
72 | bool checkProperties() override; | |
73 |
|
|
73 | IDataSeries* createSeries() override; | |
74 | void readPropertyLine(const QString &line) override; |
|
74 | void readPropertyLine(const QString &line) override; | |
75 | void readResultLine(const QString &line) override; |
|
75 | void readResultLine(const QString &line) override; | |
76 |
|
76 | |||
77 | private: |
|
77 | private: | |
78 | void handleDataHoles(); |
|
78 | void handleDataHoles(); | |
79 |
|
79 | |||
80 | Properties m_Properties{}; |
|
80 | Properties m_Properties{}; | |
81 | std::vector<double> m_XAxisData{}; |
|
81 | std::vector<double> m_XAxisData{}; | |
82 | std::vector<double> m_YAxisData{}; |
|
82 | std::vector<double> m_YAxisData{}; | |
83 | std::vector<double> m_ValuesData{}; |
|
83 | std::vector<double> m_ValuesData{}; | |
84 | std::vector<int> m_ValuesIndexes{}; |
|
84 | std::vector<int> m_ValuesIndexes{}; | |
85 | double m_FillValue{std::numeric_limits<double>::quiet_NaN()}; |
|
85 | double m_FillValue{std::numeric_limits<double>::quiet_NaN()}; | |
86 | }; |
|
86 | }; | |
87 |
|
87 | |||
88 | /** |
|
88 | /** | |
89 | * Implementation of @sa IAmdaResultParserHelper for vectors |
|
89 | * Implementation of @sa IAmdaResultParserHelper for vectors | |
90 | */ |
|
90 | */ | |
91 | class VectorParserHelper : public IAmdaResultParserHelper { |
|
91 | class VectorParserHelper : public IAmdaResultParserHelper { | |
92 | public: |
|
92 | public: | |
93 | bool checkProperties() override; |
|
93 | bool checkProperties() override; | |
94 |
|
|
94 | IDataSeries* createSeries() override; | |
95 | void readPropertyLine(const QString &line) override; |
|
95 | void readPropertyLine(const QString &line) override; | |
96 | void readResultLine(const QString &line) override; |
|
96 | void readResultLine(const QString &line) override; | |
97 |
|
97 | |||
98 | private: |
|
98 | private: | |
99 | /// @return the reading order of the "value" columns for a result line of the AMDA file |
|
99 | /// @return the reading order of the "value" columns for a result line of the AMDA file | |
100 | std::vector<int> valuesIndexes() const; |
|
100 | std::vector<int> valuesIndexes() const; | |
101 |
|
101 | |||
102 | Properties m_Properties{}; |
|
102 | Properties m_Properties{}; | |
103 | std::vector<double> m_XAxisData{}; |
|
103 | std::vector<double> m_XAxisData{}; | |
104 | std::vector<double> m_ValuesData{}; |
|
104 | std::vector<double> m_ValuesData{}; | |
105 | }; |
|
105 | }; | |
106 |
|
106 | |||
107 | #endif // SCIQLOP_AMDARESULTPARSERHELPER_H |
|
107 | #endif // SCIQLOP_AMDARESULTPARSERHELPER_H |
@@ -1,274 +1,88 | |||||
1 | #include "AmdaProvider.h" |
|
1 | #include "AmdaProvider.h" | |
2 | #include "AmdaDefs.h" |
|
2 | #include "AmdaDefs.h" | |
3 | #include "AmdaResultParser.h" |
|
3 | #include "AmdaResultParser.h" | |
4 | #include "AmdaServer.h" |
|
4 | #include "AmdaServer.h" | |
5 |
|
5 | |||
6 | #include <Common/DateUtils.h> |
|
6 | #include <Common/DateUtils.h> | |
7 | #include <Data/DataProviderParameters.h> |
|
7 | #include <Data/DataProviderParameters.h> | |
8 | #include <Network/NetworkController.h> |
|
8 | #include <Network/NetworkController.h> | |
9 | #include <SqpApplication.h> |
|
9 | #include <SqpApplication.h> | |
10 | #include <Variable/Variable.h> |
|
10 | #include <Variable/Variable.h> | |
11 |
|
11 | |||
12 | #include <QNetworkAccessManager> |
|
12 | #include <QNetworkAccessManager> | |
13 | #include <QNetworkReply> |
|
13 | #include <QNetworkReply> | |
14 | #include <QTemporaryFile> |
|
14 | #include <QTemporaryFile> | |
15 | #include <QThread> |
|
15 | #include <QThread> | |
|
16 | #include <QJsonDocument> | |||
|
17 | #include <Network/Downloader.h> | |||
16 |
|
18 | |||
17 | Q_LOGGING_CATEGORY(LOG_AmdaProvider, "AmdaProvider") |
|
19 | Q_LOGGING_CATEGORY(LOG_AmdaProvider, "AmdaProvider") | |
18 |
|
20 | |||
19 | namespace { |
|
21 | namespace { | |
20 |
|
22 | |||
21 | /// URL format for a request on AMDA server. The parameters are as follows: |
|
23 | /// URL format for a request on AMDA server. The parameters are as follows: | |
22 | /// - %1: server URL |
|
24 | /// - %1: server URL | |
23 | /// - %2: start date |
|
25 | /// - %2: start date | |
24 | /// - %3: end date |
|
26 | /// - %3: end date | |
25 | /// - %4: parameter id |
|
27 | /// - %4: parameter id | |
26 | /// AMDA V2: http://amdatest.irap.omp.eu/php/rest/ |
|
28 | /// AMDA V2: http://amdatest.irap.omp.eu/php/rest/ | |
27 | const auto AMDA_URL_FORMAT = QStringLiteral( |
|
29 | const auto AMDA_URL_FORMAT = QStringLiteral( | |
28 | "http://%1/php/rest/" |
|
30 | "http://%1/php/rest/" | |
29 | "getParameter.php?startTime=%2&stopTime=%3¶meterID=%4&outputFormat=ASCII&" |
|
31 | "getParameter.php?startTime=%2&stopTime=%3¶meterID=%4&outputFormat=ASCII&" | |
30 | "timeFormat=ISO8601&gzip=0"); |
|
32 | "timeFormat=ISO8601&gzip=0"); | |
31 |
|
33 | |||
|
34 | const auto AMDA_URL_FORMAT_WITH_TOKEN = QStringLiteral( | |||
|
35 | "http://%1/php/rest/" | |||
|
36 | "getParameter.php?startTime=%2&stopTime=%3¶meterID=%4&outputFormat=ASCII&" | |||
|
37 | "timeFormat=ISO8601&gzip=0&" | |||
|
38 | "token=%5"); | |||
|
39 | ||||
|
40 | const auto AMDA_TOKEN_URL_FORMAT = QStringLiteral( | |||
|
41 | "http://%1/php/rest/" | |||
|
42 | "auth.php"); | |||
|
43 | ||||
32 | /// Dates format passed in the URL (e.g 2013-09-23T09:00) |
|
44 | /// Dates format passed in the URL (e.g 2013-09-23T09:00) | |
33 | const auto AMDA_TIME_FORMAT = QStringLiteral("yyyy-MM-ddThh:mm:ss"); |
|
45 | const auto AMDA_TIME_FORMAT = QStringLiteral("yyyy-MM-ddThh:mm:ss"); | |
34 |
|
46 | |||
35 | /// Formats a time to a date that can be passed in URL |
|
47 | /// Formats a time to a date that can be passed in URL | |
36 | QString dateFormat(double sqpRange) noexcept |
|
48 | QString dateFormat(double sqpRange) noexcept | |
37 | { |
|
49 | { | |
38 | auto dateTime = DateUtils::dateTime(sqpRange); |
|
50 | auto dateTime = DateUtils::dateTime(sqpRange); | |
39 | return dateTime.toString(AMDA_TIME_FORMAT); |
|
51 | return dateTime.toString(AMDA_TIME_FORMAT); | |
40 | } |
|
52 | } | |
41 |
|
53 | |||
42 |
|
54 | |||
43 | } // namespace |
|
55 | } // namespace | |
44 |
|
56 | |||
45 | AmdaProvider::AmdaProvider() |
|
57 | AmdaProvider::AmdaProvider() | |
46 | { |
|
58 | { | |
47 | qCDebug(LOG_AmdaProvider()) << tr("AmdaProvider::AmdaProvider") << QThread::currentThread(); |
|
|||
48 | if (auto app = sqpApp) { |
|
|||
49 | auto &networkController = app->networkController(); |
|
|||
50 | connect(this, SIGNAL(requestConstructed(std::shared_ptr<QNetworkRequest>, QUuid, |
|
|||
51 | std::function<void(QNetworkReply *, QUuid)>)), |
|
|||
52 | &networkController, |
|
|||
53 | SLOT(onProcessRequested(std::shared_ptr<QNetworkRequest>, QUuid, |
|
|||
54 | std::function<void(QNetworkReply *, QUuid)>))); |
|
|||
55 |
|
||||
56 |
|
59 | |||
57 | connect(&sqpApp->networkController(), |
|
|||
58 | SIGNAL(replyDownloadProgress(QUuid, std::shared_ptr<QNetworkRequest>, double)), |
|
|||
59 | this, |
|
|||
60 | SLOT(onReplyDownloadProgress(QUuid, std::shared_ptr<QNetworkRequest>, double))); |
|
|||
61 | } |
|
|||
62 | } |
|
60 | } | |
63 |
|
61 | |||
64 | std::shared_ptr<IDataProvider> AmdaProvider::clone() const |
|
62 | std::shared_ptr<IDataProvider> AmdaProvider::clone() const | |
65 | { |
|
63 | { | |
66 | // No copy is made in the clone |
|
64 | // No copy is made in the clone | |
67 | return std::make_shared<AmdaProvider>(); |
|
65 | return std::make_shared<AmdaProvider>(); | |
68 | } |
|
66 | } | |
69 |
|
67 | |||
70 |
|
|
68 | IDataSeries* AmdaProvider::getData(const DataProviderParameters ¶meters) | |
71 | { |
|
|||
72 | // NOTE: Try to use multithread if possible |
|
|||
73 | const auto times = parameters.m_Times; |
|
|||
74 | const auto data = parameters.m_Data; |
|
|||
75 | for (const auto &dateTime : qAsConst(times)) { |
|
|||
76 | qCDebug(LOG_AmdaProvider()) << tr("TORM AmdaProvider::requestDataLoading ") << acqIdentifier |
|
|||
77 | << dateTime; |
|
|||
78 | this->retrieveData(acqIdentifier, dateTime, data); |
|
|||
79 |
|
||||
80 |
|
||||
81 | // TORM when AMDA will support quick asynchrone request |
|
|||
82 | QThread::msleep(1000); |
|
|||
83 | } |
|
|||
84 | } |
|
|||
85 |
|
||||
86 | void AmdaProvider::requestDataAborting(QUuid acqIdentifier) |
|
|||
87 | { |
|
|||
88 | if (auto app = sqpApp) { |
|
|||
89 | auto &networkController = app->networkController(); |
|
|||
90 | networkController.onReplyCanceled(acqIdentifier); |
|
|||
91 | } |
|
|||
92 | } |
|
|||
93 |
|
||||
94 | void AmdaProvider::onReplyDownloadProgress(QUuid acqIdentifier, |
|
|||
95 | std::shared_ptr<QNetworkRequest> networkRequest, |
|
|||
96 | double progress) |
|
|||
97 | { |
|
|||
98 | qCDebug(LOG_AmdaProvider()) << tr("onReplyDownloadProgress") << acqIdentifier |
|
|||
99 | << networkRequest.get() << progress; |
|
|||
100 | auto acqIdToRequestProgressMapIt = m_AcqIdToRequestProgressMap.find(acqIdentifier); |
|
|||
101 | if (acqIdToRequestProgressMapIt != m_AcqIdToRequestProgressMap.end()) { |
|
|||
102 |
|
||||
103 | // Update the progression for the current request |
|
|||
104 | auto requestPtr = networkRequest; |
|
|||
105 | auto findRequest = [requestPtr](const auto &entry) { return requestPtr == entry.first; }; |
|
|||
106 |
|
||||
107 | auto &requestProgressMap = acqIdToRequestProgressMapIt->second; |
|
|||
108 | auto requestProgressMapEnd = requestProgressMap.end(); |
|
|||
109 | auto requestProgressMapIt |
|
|||
110 | = std::find_if(requestProgressMap.begin(), requestProgressMapEnd, findRequest); |
|
|||
111 |
|
||||
112 | if (requestProgressMapIt != requestProgressMapEnd) { |
|
|||
113 | requestProgressMapIt->second = progress; |
|
|||
114 | } |
|
|||
115 | else { |
|
|||
116 | // This case can happened when a progression is send after the request has been |
|
|||
117 | // finished. |
|
|||
118 | // Generaly the case when aborting a request |
|
|||
119 | qCDebug(LOG_AmdaProvider()) << tr("Can't retrieve Request in progress") << acqIdentifier |
|
|||
120 | << networkRequest.get() << progress; |
|
|||
121 | } |
|
|||
122 |
|
||||
123 | // Compute the current final progress and notify it |
|
|||
124 | double finalProgress = 0.0; |
|
|||
125 |
|
||||
126 | auto fraq = requestProgressMap.size(); |
|
|||
127 |
|
||||
128 | for (auto requestProgress : requestProgressMap) { |
|
|||
129 | finalProgress += requestProgress.second; |
|
|||
130 | qCDebug(LOG_AmdaProvider()) << tr("Current final progress without fraq:") |
|
|||
131 | << finalProgress << requestProgress.second; |
|
|||
132 | } |
|
|||
133 |
|
||||
134 | if (fraq > 0) { |
|
|||
135 | finalProgress = finalProgress / fraq; |
|
|||
136 | } |
|
|||
137 |
|
||||
138 | qCDebug(LOG_AmdaProvider()) << tr("Current final progress: ") << fraq << finalProgress; |
|
|||
139 | emit dataProvidedProgress(acqIdentifier, finalProgress); |
|
|||
140 | } |
|
|||
141 | else { |
|
|||
142 | // This case can happened when a progression is send after the request has been finished. |
|
|||
143 | // Generaly the case when aborting a request |
|
|||
144 | emit dataProvidedProgress(acqIdentifier, 100.0); |
|
|||
145 | } |
|
|||
146 | } |
|
|||
147 |
|
||||
148 | void AmdaProvider::retrieveData(QUuid token, const DateTimeRange &dateTime, const QVariantHash &data) |
|
|||
149 | { |
|
69 | { | |
150 | // Retrieves product ID from data: if the value is invalid, no request is made |
|
70 | auto range = parameters.m_Times.front(); | |
151 | auto productId = data.value(AMDA_XML_ID_KEY).toString(); |
|
71 | auto metaData = parameters.m_Data; | |
152 | if (productId.isNull()) { |
|
72 | auto productId = metaData.value(AMDA_XML_ID_KEY).toString(); | |
153 | qCCritical(LOG_AmdaProvider()) << tr("Can't retrieve data: unknown product id"); |
|
|||
154 | return; |
|
|||
155 | } |
|
|||
156 |
|
||||
157 | // Retrieves the data type that determines whether the expected format for the result file is |
|
|||
158 | // scalar, vector... |
|
|||
159 | auto productValueType |
|
73 | auto productValueType | |
160 |
= DataSeriesTypeUtils::fromString( |
|
74 | = DataSeriesTypeUtils::fromString(metaData.value(AMDA_DATA_TYPE_KEY).toString()); | |
161 |
|
75 | auto startDate = dateFormat(range.m_TStart); | ||
162 | // /////////// // |
|
76 | auto endDate = dateFormat(range.m_TEnd); | |
163 | // Creates URL // |
|
77 | QVariantHash urlProperties{{AMDA_SERVER_KEY, metaData.value(AMDA_SERVER_KEY)}}; | |
164 | // /////////// // |
|
78 | auto token_url = QString{AMDA_TOKEN_URL_FORMAT}.arg(AmdaServer::instance().url(urlProperties)); | |
165 |
|
79 | auto response = Downloader::get(token_url); | ||
166 | auto startDate = dateFormat(dateTime.m_TStart); |
|
80 | auto url = QString{AMDA_URL_FORMAT_WITH_TOKEN}.arg(AmdaServer::instance().url(urlProperties), | |
167 | auto endDate = dateFormat(dateTime.m_TEnd); |
|
81 | startDate, endDate, productId, QString(response.data())); | |
168 |
|
82 | response = Downloader::get(url); | ||
169 | QVariantHash urlProperties{{AMDA_SERVER_KEY, data.value(AMDA_SERVER_KEY)}}; |
|
83 | auto test = QJsonDocument::fromJson(response.data()); | |
170 | auto url = QUrl{QString{AMDA_URL_FORMAT}.arg(AmdaServer::instance().url(urlProperties), |
|
84 | url = test["dataFileURLs"].toString(); | |
171 | startDate, endDate, productId)}; |
|
85 | response = Downloader::get(url); | |
172 | qCInfo(LOG_AmdaProvider()) << tr("TORM AmdaProvider::retrieveData url:") << url; |
|
86 | return AmdaResultParser::readTxt(QTextStream{response.data()},productValueType); | |
173 | auto tempFile = std::make_shared<QTemporaryFile>(); |
|
|||
174 |
|
||||
175 | // LAMBDA |
|
|||
176 | auto httpDownloadFinished = [this, dateTime, tempFile, |
|
|||
177 | productValueType](QNetworkReply *reply, QUuid dataId) noexcept { |
|
|||
178 |
|
||||
179 | // Don't do anything if the reply was abort |
|
|||
180 | if (reply->error() == QNetworkReply::NoError) { |
|
|||
181 |
|
||||
182 | if (tempFile) { |
|
|||
183 | auto replyReadAll = reply->readAll(); |
|
|||
184 | if (!replyReadAll.isEmpty()) { |
|
|||
185 | tempFile->write(replyReadAll); |
|
|||
186 | } |
|
|||
187 | tempFile->close(); |
|
|||
188 |
|
||||
189 | // Parse results file |
|
|||
190 | if (auto dataSeries |
|
|||
191 | = AmdaResultParser::readTxt(tempFile->fileName(), productValueType)) { |
|
|||
192 | emit dataProvided(dataId, dataSeries, dateTime); |
|
|||
193 | } |
|
|||
194 | else { |
|
|||
195 | /// @todo ALX : debug |
|
|||
196 | emit dataProvidedFailed(dataId); |
|
|||
197 | } |
|
|||
198 | } |
|
|||
199 | m_AcqIdToRequestProgressMap.erase(dataId); |
|
|||
200 | } |
|
|||
201 | else { |
|
|||
202 | qCCritical(LOG_AmdaProvider()) << tr("httpDownloadFinished ERROR"); |
|
|||
203 | emit dataProvidedFailed(dataId); |
|
|||
204 | } |
|
|||
205 |
|
||||
206 | }; |
|
|||
207 | auto httpFinishedLambda |
|
|||
208 | = [this, httpDownloadFinished, tempFile](QNetworkReply *reply, QUuid dataId) noexcept { |
|
|||
209 |
|
||||
210 | // Don't do anything if the reply was abort |
|
|||
211 | if (reply->error() == QNetworkReply::NoError) { |
|
|||
212 | auto downloadFileUrl = QUrl{QString{reply->readAll()}.trimmed()}; |
|
|||
213 |
|
||||
214 | qCInfo(LOG_AmdaProvider()) |
|
|||
215 | << tr("TORM AmdaProvider::retrieveData downloadFileUrl:") << downloadFileUrl; |
|
|||
216 | // Executes request for downloading file // |
|
|||
217 |
|
||||
218 | // Creates destination file |
|
|||
219 | if (tempFile->open()) { |
|
|||
220 | // Executes request and store the request for progression |
|
|||
221 | auto request = std::make_shared<QNetworkRequest>(downloadFileUrl); |
|
|||
222 | updateRequestProgress(dataId, request, 0.0); |
|
|||
223 | emit requestConstructed(request, dataId, httpDownloadFinished); |
|
|||
224 | } |
|
|||
225 | else { |
|
|||
226 | emit dataProvidedFailed(dataId); |
|
|||
227 | } |
|
|||
228 | } |
|
|||
229 | else { |
|
|||
230 | qCCritical(LOG_AmdaProvider()) << tr("httpFinishedLambda ERROR"); |
|
|||
231 | m_AcqIdToRequestProgressMap.erase(dataId); |
|
|||
232 | emit dataProvidedFailed(dataId); |
|
|||
233 | } |
|
|||
234 | }; |
|
|||
235 |
|
||||
236 | // //////////////// // |
|
|||
237 | // Executes request // |
|
|||
238 | // //////////////// // |
|
|||
239 |
|
||||
240 | auto request = std::make_shared<QNetworkRequest>(url); |
|
|||
241 | qCDebug(LOG_AmdaProvider()) << tr("First Request creation") << request.get(); |
|
|||
242 | updateRequestProgress(token, request, 0.0); |
|
|||
243 |
|
||||
244 | emit requestConstructed(request, token, httpFinishedLambda); |
|
|||
245 | } |
|
87 | } | |
246 |
|
88 | |||
247 | void AmdaProvider::updateRequestProgress(QUuid acqIdentifier, |
|
|||
248 | std::shared_ptr<QNetworkRequest> request, double progress) |
|
|||
249 | { |
|
|||
250 | qCDebug(LOG_AmdaProvider()) << tr("updateRequestProgress request") << request.get(); |
|
|||
251 | auto acqIdToRequestProgressMapIt = m_AcqIdToRequestProgressMap.find(acqIdentifier); |
|
|||
252 | if (acqIdToRequestProgressMapIt != m_AcqIdToRequestProgressMap.end()) { |
|
|||
253 | auto &requestProgressMap = acqIdToRequestProgressMapIt->second; |
|
|||
254 | auto requestProgressMapIt = requestProgressMap.find(request); |
|
|||
255 | if (requestProgressMapIt != requestProgressMap.end()) { |
|
|||
256 | requestProgressMapIt->second = progress; |
|
|||
257 | qCDebug(LOG_AmdaProvider()) << tr("updateRequestProgress new progress for request") |
|
|||
258 | << acqIdentifier << request.get() << progress; |
|
|||
259 | } |
|
|||
260 | else { |
|
|||
261 | qCDebug(LOG_AmdaProvider()) << tr("updateRequestProgress new request") << acqIdentifier |
|
|||
262 | << request.get() << progress; |
|
|||
263 | acqIdToRequestProgressMapIt->second.insert(std::make_pair(request, progress)); |
|
|||
264 | } |
|
|||
265 | } |
|
|||
266 | else { |
|
|||
267 | qCDebug(LOG_AmdaProvider()) << tr("updateRequestProgress new acqIdentifier") |
|
|||
268 | << acqIdentifier << request.get() << progress; |
|
|||
269 | auto requestProgressMap = std::map<std::shared_ptr<QNetworkRequest>, double>{}; |
|
|||
270 | requestProgressMap.insert(std::make_pair(request, progress)); |
|
|||
271 | m_AcqIdToRequestProgressMap.insert( |
|
|||
272 | std::make_pair(acqIdentifier, std::move(requestProgressMap))); |
|
|||
273 | } |
|
|||
274 | } |
|
@@ -1,133 +1,141 | |||||
1 | #include "AmdaResultParser.h" |
|
1 | #include "AmdaResultParser.h" | |
2 |
|
2 | |||
3 | #include "AmdaResultParserHelper.h" |
|
3 | #include "AmdaResultParserHelper.h" | |
4 |
|
4 | |||
5 | #include <QFile> |
|
5 | #include <QFile> | |
6 |
|
6 | |||
7 | #include <cmath> |
|
7 | #include <cmath> | |
|
8 | #include <Data/IDataSeries.h> | |||
8 |
|
9 | |||
9 | Q_LOGGING_CATEGORY(LOG_AmdaResultParser, "AmdaResultParser") |
|
10 | Q_LOGGING_CATEGORY(LOG_AmdaResultParser, "AmdaResultParser") | |
10 |
|
11 | |||
11 | namespace { |
|
12 | namespace { | |
12 |
|
13 | |||
13 | /// Message in result file when the file was not found on server |
|
14 | /// Message in result file when the file was not found on server | |
14 | const auto FILE_NOT_FOUND_MESSAGE = QStringLiteral("Not Found"); |
|
15 | const auto FILE_NOT_FOUND_MESSAGE = QStringLiteral("Not Found"); | |
15 |
|
16 | |||
16 | /// Checks if a line is a comment line |
|
17 | /// Checks if a line is a comment line | |
17 | bool isCommentLine(const QString &line) |
|
18 | bool isCommentLine(const QString &line) | |
18 | { |
|
19 | { | |
19 | return line.startsWith("#"); |
|
20 | return line.startsWith("#"); | |
20 | } |
|
21 | } | |
21 |
|
22 | |||
22 | /** |
|
23 | /** | |
23 | * Creates helper that will be used to read AMDA file, according to the type passed as parameter |
|
24 | * Creates helper that will be used to read AMDA file, according to the type passed as parameter | |
24 | * @param valueType the type of values expected in the AMDA file (scalars, vectors, spectrograms...) |
|
25 | * @param valueType the type of values expected in the AMDA file (scalars, vectors, spectrograms...) | |
25 | * @return the helper created |
|
26 | * @return the helper created | |
26 | */ |
|
27 | */ | |
27 | std::unique_ptr<IAmdaResultParserHelper> createHelper(DataSeriesType valueType) |
|
28 | std::unique_ptr<IAmdaResultParserHelper> createHelper(DataSeriesType valueType) | |
28 | { |
|
29 | { | |
29 | switch (valueType) { |
|
30 | switch (valueType) { | |
30 | case DataSeriesType::SCALAR: |
|
31 | case DataSeriesType::SCALAR: | |
31 | return std::make_unique<ScalarParserHelper>(); |
|
32 | return std::make_unique<ScalarParserHelper>(); | |
32 | case DataSeriesType::SPECTROGRAM: |
|
33 | case DataSeriesType::SPECTROGRAM: | |
33 | return std::make_unique<SpectrogramParserHelper>(); |
|
34 | return std::make_unique<SpectrogramParserHelper>(); | |
34 | case DataSeriesType::VECTOR: |
|
35 | case DataSeriesType::VECTOR: | |
35 | return std::make_unique<VectorParserHelper>(); |
|
36 | return std::make_unique<VectorParserHelper>(); | |
36 | case DataSeriesType::UNKNOWN: |
|
37 | case DataSeriesType::UNKNOWN: | |
37 | // Invalid case |
|
38 | // Invalid case | |
38 | break; |
|
39 | break; | |
39 | } |
|
40 | } | |
40 |
|
41 | |||
41 | // Invalid cases |
|
42 | // Invalid cases | |
42 | qCCritical(LOG_AmdaResultParser()) |
|
43 | qCCritical(LOG_AmdaResultParser()) | |
43 | << QObject::tr("Can't create helper to read result file: unsupported type"); |
|
44 | << QObject::tr("Can't create helper to read result file: unsupported type"); | |
44 | return nullptr; |
|
45 | return nullptr; | |
45 | } |
|
46 | } | |
46 |
|
47 | |||
47 | /** |
|
48 | /** | |
48 | * Reads properties of the stream passed as parameter |
|
49 | * Reads properties of the stream passed as parameter | |
49 | * @param helper the helper used to read properties line by line |
|
50 | * @param helper the helper used to read properties line by line | |
50 | * @param stream the stream to read |
|
51 | * @param stream the stream to read | |
51 | */ |
|
52 | */ | |
52 | void readProperties(IAmdaResultParserHelper &helper, QTextStream &stream) |
|
53 | void readProperties(IAmdaResultParserHelper &helper, QTextStream &stream) | |
53 | { |
|
54 | { | |
54 | // Searches properties in the comment lines (as long as the reading has not reached the data) |
|
55 | // Searches properties in the comment lines (as long as the reading has not reached the data) | |
55 | // AMDA V2: while (stream.readLineInto(&line) && !line.contains(DATA_HEADER_REGEX)) { |
|
56 | // AMDA V2: while (stream.readLineInto(&line) && !line.contains(DATA_HEADER_REGEX)) { | |
56 | QString line{}; |
|
57 | QString line{}; | |
57 | while (stream.readLineInto(&line) && isCommentLine(line)) { |
|
58 | while (stream.readLineInto(&line) && isCommentLine(line)) { | |
58 | helper.readPropertyLine(line); |
|
59 | helper.readPropertyLine(line); | |
59 | } |
|
60 | } | |
60 | } |
|
61 | } | |
61 |
|
62 | |||
62 | /** |
|
63 | /** | |
63 | * Reads results of the stream passed as parameter |
|
64 | * Reads results of the stream passed as parameter | |
64 | * @param helper the helper used to read results line by line |
|
65 | * @param helper the helper used to read results line by line | |
65 | * @param stream the stream to read |
|
66 | * @param stream the stream to read | |
66 | */ |
|
67 | */ | |
67 | void readResults(IAmdaResultParserHelper &helper, QTextStream &stream) |
|
68 | void readResults(IAmdaResultParserHelper &helper, QTextStream &stream) | |
68 | { |
|
69 | { | |
69 | QString line{}; |
|
70 | QString line{}; | |
70 |
|
71 | |||
71 | // Skip comment lines |
|
72 | // Skip comment lines | |
72 | while (stream.readLineInto(&line) && isCommentLine(line)) { |
|
73 | while (stream.readLineInto(&line) && isCommentLine(line)) { | |
73 | } |
|
74 | } | |
74 |
|
75 | |||
75 | if (!stream.atEnd()) { |
|
76 | if (!stream.atEnd()) { | |
76 | do { |
|
77 | do { | |
77 | helper.readResultLine(line); |
|
78 | helper.readResultLine(line); | |
78 | } while (stream.readLineInto(&line)); |
|
79 | } while (stream.readLineInto(&line)); | |
79 | } |
|
80 | } | |
80 | } |
|
81 | } | |
81 |
|
82 | |||
82 | } // namespace |
|
83 | } // namespace | |
83 |
|
84 | |||
84 | std::shared_ptr<IDataSeries> AmdaResultParser::readTxt(const QString &filePath, |
|
85 | std::shared_ptr<IDataSeries> AmdaResultParser::readTxt(const QString &filePath, | |
85 | DataSeriesType type) noexcept |
|
86 | DataSeriesType type) noexcept | |
86 | { |
|
87 | { | |
87 | if (type == DataSeriesType::UNKNOWN) { |
|
88 | if (type == DataSeriesType::UNKNOWN) { | |
88 | qCCritical(LOG_AmdaResultParser()) |
|
89 | qCCritical(LOG_AmdaResultParser()) | |
89 | << QObject::tr("Can't retrieve AMDA data: the type of values to be read is unknown"); |
|
90 | << QObject::tr("Can't retrieve AMDA data: the type of values to be read is unknown"); | |
90 | return nullptr; |
|
91 | return nullptr; | |
91 | } |
|
92 | } | |
92 |
|
93 | |||
93 | QFile file{filePath}; |
|
94 | QFile file{filePath}; | |
94 |
|
95 | |||
95 | if (!file.open(QFile::ReadOnly | QIODevice::Text)) { |
|
96 | if (!file.open(QFile::ReadOnly | QIODevice::Text)) { | |
96 | qCCritical(LOG_AmdaResultParser()) |
|
97 | qCCritical(LOG_AmdaResultParser()) | |
97 | << QObject::tr("Can't retrieve AMDA data from file %1: %2") |
|
98 | << QObject::tr("Can't retrieve AMDA data from file %1: %2") | |
98 | .arg(filePath, file.errorString()); |
|
99 | .arg(filePath, file.errorString()); | |
99 | return nullptr; |
|
100 | return nullptr; | |
100 | } |
|
101 | } | |
101 |
|
102 | |||
102 | QTextStream stream{&file}; |
|
103 | return std::shared_ptr<IDataSeries>{AmdaResultParser::readTxt(QTextStream{&file},type)}; | |
|
104 | } | |||
|
105 | ||||
|
106 | IDataSeries *AmdaResultParser::readTxt(QTextStream stream, | |||
|
107 | DataSeriesType type) noexcept | |||
|
108 | { | |||
|
109 | if (type == DataSeriesType::UNKNOWN) | |||
|
110 | { | |||
|
111 | return nullptr; | |||
|
112 | } | |||
103 |
|
113 | |||
104 | // Checks if the file was found on the server |
|
114 | // Checks if the file was found on the server | |
105 | auto firstLine = stream.readLine(); |
|
115 | auto firstLine = stream.readLine(); | |
106 |
if (firstLine.compare(FILE_NOT_FOUND_MESSAGE) == 0) |
|
116 | if (firstLine.compare(FILE_NOT_FOUND_MESSAGE) == 0) | |
107 | qCCritical(LOG_AmdaResultParser()) |
|
117 | { | |
108 | << QObject::tr("Can't retrieve AMDA data from file %1: file was not found on server") |
|
|||
109 | .arg(filePath); |
|
|||
110 | return nullptr; |
|
118 | return nullptr; | |
111 | } |
|
119 | } | |
112 |
|
120 | |||
113 | auto helper = createHelper(type); |
|
121 | auto helper = createHelper(type); | |
114 | Q_ASSERT(helper != nullptr); |
|
122 | Q_ASSERT(helper != nullptr); | |
115 |
|
123 | |||
116 | // Reads header file to retrieve properties |
|
124 | // Reads header file to retrieve properties | |
117 | stream.seek(0); // returns to the beginning of the file |
|
125 | stream.seek(0); // returns to the beginning of the file | |
118 | readProperties(*helper, stream); |
|
126 | readProperties(*helper, stream); | |
119 |
|
127 | |||
120 | // Checks properties |
|
128 | // Checks properties | |
121 | if (helper->checkProperties()) { |
|
129 | if (helper->checkProperties()) { | |
122 | // Reads results |
|
130 | // Reads results | |
123 | // AMDA V2: remove line |
|
131 | // AMDA V2: remove line | |
124 | stream.seek(0); // returns to the beginning of the file |
|
132 | stream.seek(0); // returns to the beginning of the file | |
125 | readResults(*helper, stream); |
|
133 | readResults(*helper, stream); | |
126 |
|
134 | |||
127 | // Creates data series |
|
135 | // Creates data series | |
128 | return helper->createSeries(); |
|
136 | return helper->createSeries(); | |
129 | } |
|
137 | } | |
130 | else { |
|
138 | else { | |
131 | return nullptr; |
|
139 | return nullptr; | |
132 | } |
|
140 | } | |
133 | } |
|
141 | } |
@@ -1,432 +1,432 | |||||
1 | #include "AmdaResultParserHelper.h" |
|
1 | #include "AmdaResultParserHelper.h" | |
2 |
|
2 | |||
3 | #include <Common/DateUtils.h> |
|
3 | #include <Common/DateUtils.h> | |
4 | #include <Common/SortUtils.h> |
|
4 | #include <Common/SortUtils.h> | |
5 |
|
5 | |||
6 | #include <Data/DataSeriesUtils.h> |
|
6 | #include <Data/DataSeriesUtils.h> | |
7 | #include <Data/ScalarSeries.h> |
|
7 | #include <Data/ScalarSeries.h> | |
8 | #include <Data/SpectrogramSeries.h> |
|
8 | #include <Data/SpectrogramSeries.h> | |
9 | #include <Data/Unit.h> |
|
9 | #include <Data/Unit.h> | |
10 | #include <Data/VectorSeries.h> |
|
10 | #include <Data/VectorSeries.h> | |
11 |
|
11 | |||
12 | #include <QtCore/QDateTime> |
|
12 | #include <QtCore/QDateTime> | |
13 | #include <QtCore/QRegularExpression> |
|
13 | #include <QtCore/QRegularExpression> | |
14 |
|
14 | |||
15 | #include <functional> |
|
15 | #include <functional> | |
16 |
|
16 | |||
17 | Q_LOGGING_CATEGORY(LOG_AmdaResultParserHelper, "AmdaResultParserHelper") |
|
17 | Q_LOGGING_CATEGORY(LOG_AmdaResultParserHelper, "AmdaResultParserHelper") | |
18 |
|
18 | |||
19 | namespace { |
|
19 | namespace { | |
20 |
|
20 | |||
21 | // ///////// // |
|
21 | // ///////// // | |
22 | // Constants // |
|
22 | // Constants // | |
23 | // ///////// // |
|
23 | // ///////// // | |
24 |
|
24 | |||
25 | /// Separator between values in a result line |
|
25 | /// Separator between values in a result line | |
26 | const auto RESULT_LINE_SEPARATOR = QRegularExpression{QStringLiteral("\\s+")}; |
|
26 | const auto RESULT_LINE_SEPARATOR = QRegularExpression{QStringLiteral("\\s+")}; | |
27 |
|
27 | |||
28 | /// Format for dates in result files |
|
28 | /// Format for dates in result files | |
29 | const auto DATE_FORMAT = QStringLiteral("yyyy-MM-ddThh:mm:ss.zzz"); |
|
29 | const auto DATE_FORMAT = QStringLiteral("yyyy-MM-ddThh:mm:ss.zzz"); | |
30 |
|
30 | |||
31 | // /////// // |
|
31 | // /////// // | |
32 | // Methods // |
|
32 | // Methods // | |
33 | // /////// // |
|
33 | // /////// // | |
34 |
|
34 | |||
35 | /** |
|
35 | /** | |
36 | * Checks that the properties contain a specific unit and that this unit is valid |
|
36 | * Checks that the properties contain a specific unit and that this unit is valid | |
37 | * @param properties the properties map in which to search unit |
|
37 | * @param properties the properties map in which to search unit | |
38 | * @param key the key to search for the unit in the properties |
|
38 | * @param key the key to search for the unit in the properties | |
39 | * @param errorMessage the error message to log in case the unit is invalid |
|
39 | * @param errorMessage the error message to log in case the unit is invalid | |
40 | * @return true if the unit is valid, false it it's invalid or was not found in the properties |
|
40 | * @return true if the unit is valid, false it it's invalid or was not found in the properties | |
41 | */ |
|
41 | */ | |
42 | bool checkUnit(const Properties &properties, const QString &key, const QString &errorMessage) |
|
42 | bool checkUnit(const Properties &properties, const QString &key, const QString &errorMessage) | |
43 | { |
|
43 | { | |
44 | auto unit = properties.value(key).value<Unit>(); |
|
44 | auto unit = properties.value(key).value<Unit>(); | |
45 | if (unit.m_Name.isEmpty()) { |
|
45 | if (unit.m_Name.isEmpty()) { | |
46 | qCWarning(LOG_AmdaResultParserHelper()) << errorMessage; |
|
46 | qCWarning(LOG_AmdaResultParserHelper()) << errorMessage; | |
47 | return false; |
|
47 | return false; | |
48 | } |
|
48 | } | |
49 |
|
49 | |||
50 | return true; |
|
50 | return true; | |
51 | } |
|
51 | } | |
52 |
|
52 | |||
53 | QDateTime dateTimeFromString(const QString &stringDate) noexcept |
|
53 | QDateTime dateTimeFromString(const QString &stringDate) noexcept | |
54 | { |
|
54 | { | |
55 | #if QT_VERSION >= QT_VERSION_CHECK(5, 8, 0) |
|
55 | #if QT_VERSION >= QT_VERSION_CHECK(5, 8, 0) | |
56 | return QDateTime::fromString(stringDate, Qt::ISODateWithMs); |
|
56 | return QDateTime::fromString(stringDate, Qt::ISODateWithMs); | |
57 | #else |
|
57 | #else | |
58 | return QDateTime::fromString(stringDate, DATE_FORMAT); |
|
58 | return QDateTime::fromString(stringDate, DATE_FORMAT); | |
59 | #endif |
|
59 | #endif | |
60 | } |
|
60 | } | |
61 |
|
61 | |||
62 | /// Converts a string date to a double date |
|
62 | /// Converts a string date to a double date | |
63 | /// @return a double that represents the date in seconds, NaN if the string date can't be converted |
|
63 | /// @return a double that represents the date in seconds, NaN if the string date can't be converted | |
64 | double doubleDate(const QString &stringDate) noexcept |
|
64 | double doubleDate(const QString &stringDate) noexcept | |
65 | { |
|
65 | { | |
66 | // Format: yyyy-MM-ddThh:mm:ss.zzz |
|
66 | // Format: yyyy-MM-ddThh:mm:ss.zzz | |
67 | auto dateTime = dateTimeFromString(stringDate); |
|
67 | auto dateTime = dateTimeFromString(stringDate); | |
68 | dateTime.setTimeSpec(Qt::UTC); |
|
68 | dateTime.setTimeSpec(Qt::UTC); | |
69 | return dateTime.isValid() ? DateUtils::secondsSinceEpoch(dateTime) |
|
69 | return dateTime.isValid() ? DateUtils::secondsSinceEpoch(dateTime) | |
70 | : std::numeric_limits<double>::quiet_NaN(); |
|
70 | : std::numeric_limits<double>::quiet_NaN(); | |
71 | } |
|
71 | } | |
72 |
|
72 | |||
73 | /** |
|
73 | /** | |
74 | * Reads a line from the AMDA file and tries to extract a x-axis data and value data from it |
|
74 | * Reads a line from the AMDA file and tries to extract a x-axis data and value data from it | |
75 | * @param xAxisData the vector in which to store the x-axis data extracted |
|
75 | * @param xAxisData the vector in which to store the x-axis data extracted | |
76 | * @param valuesData the vector in which to store the value extracted |
|
76 | * @param valuesData the vector in which to store the value extracted | |
77 | * @param line the line to read to extract the property |
|
77 | * @param line the line to read to extract the property | |
78 | * @param valuesIndexes indexes of insertion of read values. For example, if the line contains three |
|
78 | * @param valuesIndexes indexes of insertion of read values. For example, if the line contains three | |
79 | * columns of values, and valuesIndexes are {2, 0, 1}, the value of the third column will be read |
|
79 | * columns of values, and valuesIndexes are {2, 0, 1}, the value of the third column will be read | |
80 | * and inserted first, then the value of the first column, and finally the value of the second |
|
80 | * and inserted first, then the value of the first column, and finally the value of the second | |
81 | * column. |
|
81 | * column. | |
82 | * @param fillValue value that tags an invalid data. For example, if fillValue is -1 and a read |
|
82 | * @param fillValue value that tags an invalid data. For example, if fillValue is -1 and a read | |
83 | * value is -1, then this value is considered as invalid and converted to NaN |
|
83 | * value is -1, then this value is considered as invalid and converted to NaN | |
84 | */ |
|
84 | */ | |
85 | void tryReadResult(std::vector<double> &xAxisData, std::vector<double> &valuesData, |
|
85 | void tryReadResult(std::vector<double> &xAxisData, std::vector<double> &valuesData, | |
86 | const QString &line, const std::vector<int> &valuesIndexes, |
|
86 | const QString &line, const std::vector<int> &valuesIndexes, | |
87 | double fillValue = std::numeric_limits<double>::quiet_NaN()) |
|
87 | double fillValue = std::numeric_limits<double>::quiet_NaN()) | |
88 | { |
|
88 | { | |
89 | auto lineData = line.split(RESULT_LINE_SEPARATOR, QString::SkipEmptyParts); |
|
89 | auto lineData = line.split(RESULT_LINE_SEPARATOR, QString::SkipEmptyParts); | |
90 |
|
90 | |||
91 | // Checks that the line contains expected number of values + x-axis value |
|
91 | // Checks that the line contains expected number of values + x-axis value | |
92 | if (static_cast<size_t>(lineData.size()) == valuesIndexes.size() + 1) { |
|
92 | if (static_cast<size_t>(lineData.size()) == valuesIndexes.size() + 1) { | |
93 | // X : the data is converted from date to double (in secs) |
|
93 | // X : the data is converted from date to double (in secs) | |
94 | auto x = doubleDate(lineData.at(0)); |
|
94 | auto x = doubleDate(lineData.at(0)); | |
95 |
|
95 | |||
96 | // Adds result only if x is valid. Then, if value is invalid, it is set to NaN |
|
96 | // Adds result only if x is valid. Then, if value is invalid, it is set to NaN | |
97 | if (!std::isnan(x)) { |
|
97 | if (!std::isnan(x)) { | |
98 | xAxisData.push_back(x); |
|
98 | xAxisData.push_back(x); | |
99 |
|
99 | |||
100 | // Values |
|
100 | // Values | |
101 | for (auto valueIndex : valuesIndexes) { |
|
101 | for (auto valueIndex : valuesIndexes) { | |
102 | bool valueOk; |
|
102 | bool valueOk; | |
103 | // we use valueIndex + 1 to skip column 0 (x-axis value) |
|
103 | // we use valueIndex + 1 to skip column 0 (x-axis value) | |
104 | auto value = lineData.at(valueIndex + 1).toDouble(&valueOk); |
|
104 | auto value = lineData.at(valueIndex + 1).toDouble(&valueOk); | |
105 |
|
105 | |||
106 | if (!valueOk) { |
|
106 | if (!valueOk) { | |
107 | qCWarning(LOG_AmdaResultParserHelper()) |
|
107 | qCWarning(LOG_AmdaResultParserHelper()) | |
108 | << QObject::tr( |
|
108 | << QObject::tr( | |
109 | "Value from (line %1, column %2) is invalid and will be " |
|
109 | "Value from (line %1, column %2) is invalid and will be " | |
110 | "converted to NaN") |
|
110 | "converted to NaN") | |
111 | .arg(line, valueIndex); |
|
111 | .arg(line, valueIndex); | |
112 | value = std::numeric_limits<double>::quiet_NaN(); |
|
112 | value = std::numeric_limits<double>::quiet_NaN(); | |
113 | } |
|
113 | } | |
114 |
|
114 | |||
115 | // Handles fill value |
|
115 | // Handles fill value | |
116 | if (!std::isnan(fillValue) && !std::isnan(value) && fillValue == value) { |
|
116 | if (!std::isnan(fillValue) && !std::isnan(value) && fillValue == value) { | |
117 | value = std::numeric_limits<double>::quiet_NaN(); |
|
117 | value = std::numeric_limits<double>::quiet_NaN(); | |
118 | } |
|
118 | } | |
119 |
|
119 | |||
120 | valuesData.push_back(value); |
|
120 | valuesData.push_back(value); | |
121 | } |
|
121 | } | |
122 | } |
|
122 | } | |
123 | else { |
|
123 | else { | |
124 | qCWarning(LOG_AmdaResultParserHelper()) |
|
124 | qCWarning(LOG_AmdaResultParserHelper()) | |
125 | << QObject::tr("Can't retrieve results from line %1: x is invalid").arg(line); |
|
125 | << QObject::tr("Can't retrieve results from line %1: x is invalid").arg(line); | |
126 | } |
|
126 | } | |
127 | } |
|
127 | } | |
128 | else { |
|
128 | else { | |
129 | qCWarning(LOG_AmdaResultParserHelper()) |
|
129 | qCWarning(LOG_AmdaResultParserHelper()) | |
130 | << QObject::tr("Can't retrieve results from line %1: invalid line").arg(line); |
|
130 | << QObject::tr("Can't retrieve results from line %1: invalid line").arg(line); | |
131 | } |
|
131 | } | |
132 | } |
|
132 | } | |
133 |
|
133 | |||
134 | /** |
|
134 | /** | |
135 | * Reads a line from the AMDA file and tries to extract a property from it |
|
135 | * Reads a line from the AMDA file and tries to extract a property from it | |
136 | * @param properties the properties map in which to put the property extracted from the line |
|
136 | * @param properties the properties map in which to put the property extracted from the line | |
137 | * @param key the key to which the property is added in the properties map |
|
137 | * @param key the key to which the property is added in the properties map | |
138 | * @param line the line to read to extract the property |
|
138 | * @param line the line to read to extract the property | |
139 | * @param regexes the expected regexes to extract the property. If the line matches one regex, the |
|
139 | * @param regexes the expected regexes to extract the property. If the line matches one regex, the | |
140 | * property is generated |
|
140 | * property is generated | |
141 | * @param fun the function used to generate the property |
|
141 | * @param fun the function used to generate the property | |
142 | * @return true if the property could be generated, false if the line does not match the regex, or |
|
142 | * @return true if the property could be generated, false if the line does not match the regex, or | |
143 | * if a property has already been generated for the key |
|
143 | * if a property has already been generated for the key | |
144 | */ |
|
144 | */ | |
145 | template <typename GeneratePropertyFun> |
|
145 | template <typename GeneratePropertyFun> | |
146 | bool tryReadProperty(Properties &properties, const QString &key, const QString &line, |
|
146 | bool tryReadProperty(Properties &properties, const QString &key, const QString &line, | |
147 | const std::vector<QRegularExpression> ®exes, GeneratePropertyFun fun) |
|
147 | const std::vector<QRegularExpression> ®exes, GeneratePropertyFun fun) | |
148 | { |
|
148 | { | |
149 | if (properties.contains(key)) { |
|
149 | if (properties.contains(key)) { | |
150 | return false; |
|
150 | return false; | |
151 | } |
|
151 | } | |
152 |
|
152 | |||
153 | // Searches for a match among all possible regexes |
|
153 | // Searches for a match among all possible regexes | |
154 | auto hasMatch = false; |
|
154 | auto hasMatch = false; | |
155 | for (auto regexIt = regexes.cbegin(), end = regexes.cend(); regexIt != end && !hasMatch; |
|
155 | for (auto regexIt = regexes.cbegin(), end = regexes.cend(); regexIt != end && !hasMatch; | |
156 | ++regexIt) { |
|
156 | ++regexIt) { | |
157 | auto match = regexIt->match(line); |
|
157 | auto match = regexIt->match(line); | |
158 | auto hasMatch = match.hasMatch(); |
|
158 | auto hasMatch = match.hasMatch(); | |
159 | if (hasMatch) { |
|
159 | if (hasMatch) { | |
160 | properties.insert(key, fun(match)); |
|
160 | properties.insert(key, fun(match)); | |
161 | } |
|
161 | } | |
162 | } |
|
162 | } | |
163 |
|
163 | |||
164 | return hasMatch; |
|
164 | return hasMatch; | |
165 | } |
|
165 | } | |
166 |
|
166 | |||
167 | /** |
|
167 | /** | |
168 | * Reads a line from the AMDA file and tries to extract a data from it. Date is converted to double |
|
168 | * Reads a line from the AMDA file and tries to extract a data from it. Date is converted to double | |
169 | * @sa tryReadProperty() |
|
169 | * @sa tryReadProperty() | |
170 | */ |
|
170 | */ | |
171 | bool tryReadDate(Properties &properties, const QString &key, const QString &line, |
|
171 | bool tryReadDate(Properties &properties, const QString &key, const QString &line, | |
172 | const std::vector<QRegularExpression> ®exes, bool timeUnit = false) |
|
172 | const std::vector<QRegularExpression> ®exes, bool timeUnit = false) | |
173 | { |
|
173 | { | |
174 | return tryReadProperty(properties, key, line, regexes, [timeUnit](const auto &match) { |
|
174 | return tryReadProperty(properties, key, line, regexes, [timeUnit](const auto &match) { | |
175 | return QVariant::fromValue(doubleDate(match.captured(1))); |
|
175 | return QVariant::fromValue(doubleDate(match.captured(1))); | |
176 | }); |
|
176 | }); | |
177 | } |
|
177 | } | |
178 |
|
178 | |||
179 | /** |
|
179 | /** | |
180 | * Reads a line from the AMDA file and tries to extract a double from it |
|
180 | * Reads a line from the AMDA file and tries to extract a double from it | |
181 | * @sa tryReadProperty() |
|
181 | * @sa tryReadProperty() | |
182 | */ |
|
182 | */ | |
183 | bool tryReadDouble(Properties &properties, const QString &key, const QString &line, |
|
183 | bool tryReadDouble(Properties &properties, const QString &key, const QString &line, | |
184 | const std::vector<QRegularExpression> ®exes) |
|
184 | const std::vector<QRegularExpression> ®exes) | |
185 | { |
|
185 | { | |
186 | return tryReadProperty(properties, key, line, regexes, [](const auto &match) { |
|
186 | return tryReadProperty(properties, key, line, regexes, [](const auto &match) { | |
187 | bool ok; |
|
187 | bool ok; | |
188 |
|
188 | |||
189 | // If the value can't be converted to double, it is set to NaN |
|
189 | // If the value can't be converted to double, it is set to NaN | |
190 | auto doubleValue = match.captured(1).toDouble(&ok); |
|
190 | auto doubleValue = match.captured(1).toDouble(&ok); | |
191 | if (!ok) { |
|
191 | if (!ok) { | |
192 | doubleValue = std::numeric_limits<double>::quiet_NaN(); |
|
192 | doubleValue = std::numeric_limits<double>::quiet_NaN(); | |
193 | } |
|
193 | } | |
194 |
|
194 | |||
195 | return QVariant::fromValue(doubleValue); |
|
195 | return QVariant::fromValue(doubleValue); | |
196 | }); |
|
196 | }); | |
197 | } |
|
197 | } | |
198 |
|
198 | |||
199 | /** |
|
199 | /** | |
200 | * Reads a line from the AMDA file and tries to extract a vector of doubles from it |
|
200 | * Reads a line from the AMDA file and tries to extract a vector of doubles from it | |
201 | * @param sep the separator of double values in the line |
|
201 | * @param sep the separator of double values in the line | |
202 | * @sa tryReadProperty() |
|
202 | * @sa tryReadProperty() | |
203 | */ |
|
203 | */ | |
204 | bool tryReadDoubles(Properties &properties, const QString &key, const QString &line, |
|
204 | bool tryReadDoubles(Properties &properties, const QString &key, const QString &line, | |
205 | const std::vector<QRegularExpression> ®exes, |
|
205 | const std::vector<QRegularExpression> ®exes, | |
206 | const QString &sep = QStringLiteral(",")) |
|
206 | const QString &sep = QStringLiteral(",")) | |
207 | { |
|
207 | { | |
208 | return tryReadProperty(properties, key, line, regexes, [sep](const auto &match) { |
|
208 | return tryReadProperty(properties, key, line, regexes, [sep](const auto &match) { | |
209 | std::vector<double> doubleValues{}; |
|
209 | std::vector<double> doubleValues{}; | |
210 |
|
210 | |||
211 | // If the value can't be converted to double, it is set to NaN |
|
211 | // If the value can't be converted to double, it is set to NaN | |
212 | auto values = match.captured(1).split(sep); |
|
212 | auto values = match.captured(1).split(sep); | |
213 | for (auto value : values) { |
|
213 | for (auto value : values) { | |
214 | bool ok; |
|
214 | bool ok; | |
215 |
|
215 | |||
216 | auto doubleValue = value.toDouble(&ok); |
|
216 | auto doubleValue = value.toDouble(&ok); | |
217 | if (!ok) { |
|
217 | if (!ok) { | |
218 | doubleValue = std::numeric_limits<double>::quiet_NaN(); |
|
218 | doubleValue = std::numeric_limits<double>::quiet_NaN(); | |
219 | } |
|
219 | } | |
220 |
|
220 | |||
221 | doubleValues.push_back(doubleValue); |
|
221 | doubleValues.push_back(doubleValue); | |
222 | } |
|
222 | } | |
223 |
|
223 | |||
224 | return QVariant::fromValue(doubleValues); |
|
224 | return QVariant::fromValue(doubleValues); | |
225 | }); |
|
225 | }); | |
226 | } |
|
226 | } | |
227 |
|
227 | |||
228 | /** |
|
228 | /** | |
229 | * Reads a line from the AMDA file and tries to extract a unit from it |
|
229 | * Reads a line from the AMDA file and tries to extract a unit from it | |
230 | * @sa tryReadProperty() |
|
230 | * @sa tryReadProperty() | |
231 | */ |
|
231 | */ | |
232 | bool tryReadUnit(Properties &properties, const QString &key, const QString &line, |
|
232 | bool tryReadUnit(Properties &properties, const QString &key, const QString &line, | |
233 | const std::vector<QRegularExpression> ®exes, bool timeUnit = false) |
|
233 | const std::vector<QRegularExpression> ®exes, bool timeUnit = false) | |
234 | { |
|
234 | { | |
235 | return tryReadProperty(properties, key, line, regexes, [timeUnit](const auto &match) { |
|
235 | return tryReadProperty(properties, key, line, regexes, [timeUnit](const auto &match) { | |
236 | return QVariant::fromValue(Unit{match.captured(1), timeUnit}); |
|
236 | return QVariant::fromValue(Unit{match.captured(1), timeUnit}); | |
237 | }); |
|
237 | }); | |
238 | } |
|
238 | } | |
239 |
|
239 | |||
240 | } // namespace |
|
240 | } // namespace | |
241 |
|
241 | |||
242 | // ////////////////// // |
|
242 | // ////////////////// // | |
243 | // ScalarParserHelper // |
|
243 | // ScalarParserHelper // | |
244 | // ////////////////// // |
|
244 | // ////////////////// // | |
245 |
|
245 | |||
246 | bool ScalarParserHelper::checkProperties() |
|
246 | bool ScalarParserHelper::checkProperties() | |
247 | { |
|
247 | { | |
248 | return checkUnit(m_Properties, X_AXIS_UNIT_PROPERTY, |
|
248 | return checkUnit(m_Properties, X_AXIS_UNIT_PROPERTY, | |
249 | QObject::tr("The x-axis unit could not be found in the file")); |
|
249 | QObject::tr("The x-axis unit could not be found in the file")); | |
250 | } |
|
250 | } | |
251 |
|
251 | |||
252 |
|
|
252 | IDataSeries* ScalarParserHelper::createSeries() | |
253 | { |
|
253 | { | |
254 |
return |
|
254 | return new ScalarSeries(std::move(m_XAxisData), std::move(m_ValuesData), | |
255 | m_Properties.value(X_AXIS_UNIT_PROPERTY).value<Unit>(), |
|
255 | m_Properties.value(X_AXIS_UNIT_PROPERTY).value<Unit>(), | |
256 | m_Properties.value(VALUES_UNIT_PROPERTY).value<Unit>()); |
|
256 | m_Properties.value(VALUES_UNIT_PROPERTY).value<Unit>()); | |
257 | } |
|
257 | } | |
258 |
|
258 | |||
259 | void ScalarParserHelper::readPropertyLine(const QString &line) |
|
259 | void ScalarParserHelper::readPropertyLine(const QString &line) | |
260 | { |
|
260 | { | |
261 | tryReadUnit(m_Properties, X_AXIS_UNIT_PROPERTY, line, |
|
261 | tryReadUnit(m_Properties, X_AXIS_UNIT_PROPERTY, line, | |
262 | {DEFAULT_X_AXIS_UNIT_REGEX, ALTERNATIVE_X_AXIS_UNIT_REGEX}, true); |
|
262 | {DEFAULT_X_AXIS_UNIT_REGEX, ALTERNATIVE_X_AXIS_UNIT_REGEX}, true); | |
263 | } |
|
263 | } | |
264 |
|
264 | |||
265 | void ScalarParserHelper::readResultLine(const QString &line) |
|
265 | void ScalarParserHelper::readResultLine(const QString &line) | |
266 | { |
|
266 | { | |
267 | tryReadResult(m_XAxisData, m_ValuesData, line, valuesIndexes()); |
|
267 | tryReadResult(m_XAxisData, m_ValuesData, line, valuesIndexes()); | |
268 | } |
|
268 | } | |
269 |
|
269 | |||
270 | std::vector<int> ScalarParserHelper::valuesIndexes() const |
|
270 | std::vector<int> ScalarParserHelper::valuesIndexes() const | |
271 | { |
|
271 | { | |
272 | // Only one value to read |
|
272 | // Only one value to read | |
273 | static auto result = std::vector<int>{0}; |
|
273 | static auto result = std::vector<int>{0}; | |
274 | return result; |
|
274 | return result; | |
275 | } |
|
275 | } | |
276 |
|
276 | |||
277 | // /////////////////////// // |
|
277 | // /////////////////////// // | |
278 | // SpectrogramParserHelper // |
|
278 | // SpectrogramParserHelper // | |
279 | // /////////////////////// // |
|
279 | // /////////////////////// // | |
280 |
|
280 | |||
281 | bool SpectrogramParserHelper::checkProperties() |
|
281 | bool SpectrogramParserHelper::checkProperties() | |
282 | { |
|
282 | { | |
283 | // Generates y-axis data from bands extracted (take the middle of the intervals) |
|
283 | // Generates y-axis data from bands extracted (take the middle of the intervals) | |
284 | auto minBands = m_Properties.value(MIN_BANDS_PROPERTY).value<std::vector<double> >(); |
|
284 | auto minBands = m_Properties.value(MIN_BANDS_PROPERTY).value<std::vector<double> >(); | |
285 | auto maxBands = m_Properties.value(MAX_BANDS_PROPERTY).value<std::vector<double> >(); |
|
285 | auto maxBands = m_Properties.value(MAX_BANDS_PROPERTY).value<std::vector<double> >(); | |
286 |
|
286 | |||
287 | if (minBands.size() < 2 || minBands.size() != maxBands.size()) { |
|
287 | if (minBands.size() < 2 || minBands.size() != maxBands.size()) { | |
288 | qCWarning(LOG_AmdaResultParserHelper()) << QObject::tr( |
|
288 | qCWarning(LOG_AmdaResultParserHelper()) << QObject::tr( | |
289 | "Can't generate y-axis data from bands extracted: bands intervals are invalid"); |
|
289 | "Can't generate y-axis data from bands extracted: bands intervals are invalid"); | |
290 | return false; |
|
290 | return false; | |
291 | } |
|
291 | } | |
292 |
|
292 | |||
293 | std::transform( |
|
293 | std::transform( | |
294 | minBands.begin(), minBands.end(), maxBands.begin(), std::back_inserter(m_YAxisData), |
|
294 | minBands.begin(), minBands.end(), maxBands.begin(), std::back_inserter(m_YAxisData), | |
295 | [](const auto &minValue, const auto &maxValue) { return (minValue + maxValue) / 2.; }); |
|
295 | [](const auto &minValue, const auto &maxValue) { return (minValue + maxValue) / 2.; }); | |
296 |
|
296 | |||
297 | // Generates values indexes, i.e. the order in which each value will be retrieved (in ascending |
|
297 | // Generates values indexes, i.e. the order in which each value will be retrieved (in ascending | |
298 | // order of the associated bands) |
|
298 | // order of the associated bands) | |
299 | m_ValuesIndexes = SortUtils::sortPermutation(m_YAxisData, std::less<double>()); |
|
299 | m_ValuesIndexes = SortUtils::sortPermutation(m_YAxisData, std::less<double>()); | |
300 |
|
300 | |||
301 | // Sorts y-axis data accoding to the ascending order |
|
301 | // Sorts y-axis data accoding to the ascending order | |
302 | m_YAxisData = SortUtils::sort(m_YAxisData, 1, m_ValuesIndexes); |
|
302 | m_YAxisData = SortUtils::sort(m_YAxisData, 1, m_ValuesIndexes); | |
303 |
|
303 | |||
304 | // Sets fill value |
|
304 | // Sets fill value | |
305 | m_FillValue = m_Properties.value(FILL_VALUE_PROPERTY).value<double>(); |
|
305 | m_FillValue = m_Properties.value(FILL_VALUE_PROPERTY).value<double>(); | |
306 |
|
306 | |||
307 | return true; |
|
307 | return true; | |
308 | } |
|
308 | } | |
309 |
|
309 | |||
310 |
|
|
310 | IDataSeries* SpectrogramParserHelper::createSeries() | |
311 | { |
|
311 | { | |
312 | // Before creating the series, we handle its data holes |
|
312 | // Before creating the series, we handle its data holes | |
313 | handleDataHoles(); |
|
313 | handleDataHoles(); | |
314 |
|
314 | |||
315 |
return |
|
315 | return new SpectrogramSeries( | |
316 | std::move(m_XAxisData), std::move(m_YAxisData), std::move(m_ValuesData), |
|
316 | std::move(m_XAxisData), std::move(m_YAxisData), std::move(m_ValuesData), | |
317 | Unit{"t", true}, // x-axis unit is always a time unit |
|
317 | Unit{"t", true}, // x-axis unit is always a time unit | |
318 | m_Properties.value(Y_AXIS_UNIT_PROPERTY).value<Unit>(), |
|
318 | m_Properties.value(Y_AXIS_UNIT_PROPERTY).value<Unit>(), | |
319 | m_Properties.value(VALUES_UNIT_PROPERTY).value<Unit>(), |
|
319 | m_Properties.value(VALUES_UNIT_PROPERTY).value<Unit>(), | |
320 | m_Properties.value(MIN_SAMPLING_PROPERTY).value<double>()); |
|
320 | m_Properties.value(MIN_SAMPLING_PROPERTY).value<double>()); | |
321 | } |
|
321 | } | |
322 |
|
322 | |||
323 | void SpectrogramParserHelper::readPropertyLine(const QString &line) |
|
323 | void SpectrogramParserHelper::readPropertyLine(const QString &line) | |
324 | { |
|
324 | { | |
325 | // Set of functions to test on the line to generate a property. If a function is valid (i.e. a |
|
325 | // Set of functions to test on the line to generate a property. If a function is valid (i.e. a | |
326 | // property has been generated for the line), the line is treated as processed and the other |
|
326 | // property has been generated for the line), the line is treated as processed and the other | |
327 | // functions are not called |
|
327 | // functions are not called | |
328 | std::vector<std::function<bool()> > functions{ |
|
328 | std::vector<std::function<bool()> > functions{ | |
329 | // values unit |
|
329 | // values unit | |
330 | [&] { |
|
330 | [&] { | |
331 | return tryReadUnit(m_Properties, VALUES_UNIT_PROPERTY, line, |
|
331 | return tryReadUnit(m_Properties, VALUES_UNIT_PROPERTY, line, | |
332 | {SPECTROGRAM_VALUES_UNIT_REGEX}); |
|
332 | {SPECTROGRAM_VALUES_UNIT_REGEX}); | |
333 | }, |
|
333 | }, | |
334 | // y-axis unit |
|
334 | // y-axis unit | |
335 | [&] { |
|
335 | [&] { | |
336 | return tryReadUnit(m_Properties, Y_AXIS_UNIT_PROPERTY, line, |
|
336 | return tryReadUnit(m_Properties, Y_AXIS_UNIT_PROPERTY, line, | |
337 | {SPECTROGRAM_Y_AXIS_UNIT_REGEX}); |
|
337 | {SPECTROGRAM_Y_AXIS_UNIT_REGEX}); | |
338 | }, |
|
338 | }, | |
339 | // min sampling |
|
339 | // min sampling | |
340 | [&] { |
|
340 | [&] { | |
341 | return tryReadDouble(m_Properties, MIN_SAMPLING_PROPERTY, line, |
|
341 | return tryReadDouble(m_Properties, MIN_SAMPLING_PROPERTY, line, | |
342 | {SPECTROGRAM_MIN_SAMPLING_REGEX}); |
|
342 | {SPECTROGRAM_MIN_SAMPLING_REGEX}); | |
343 | }, |
|
343 | }, | |
344 | // max sampling |
|
344 | // max sampling | |
345 | [&] { |
|
345 | [&] { | |
346 | return tryReadDouble(m_Properties, MAX_SAMPLING_PROPERTY, line, |
|
346 | return tryReadDouble(m_Properties, MAX_SAMPLING_PROPERTY, line, | |
347 | {SPECTROGRAM_MAX_SAMPLING_REGEX}); |
|
347 | {SPECTROGRAM_MAX_SAMPLING_REGEX}); | |
348 | }, |
|
348 | }, | |
349 | // fill value |
|
349 | // fill value | |
350 | [&] { |
|
350 | [&] { | |
351 | return tryReadDouble(m_Properties, FILL_VALUE_PROPERTY, line, |
|
351 | return tryReadDouble(m_Properties, FILL_VALUE_PROPERTY, line, | |
352 | {SPECTROGRAM_FILL_VALUE_REGEX}); |
|
352 | {SPECTROGRAM_FILL_VALUE_REGEX}); | |
353 | }, |
|
353 | }, | |
354 | // min bounds of each band |
|
354 | // min bounds of each band | |
355 | [&] { |
|
355 | [&] { | |
356 | return tryReadDoubles(m_Properties, MIN_BANDS_PROPERTY, line, |
|
356 | return tryReadDoubles(m_Properties, MIN_BANDS_PROPERTY, line, | |
357 | {SPECTROGRAM_MIN_BANDS_REGEX}); |
|
357 | {SPECTROGRAM_MIN_BANDS_REGEX}); | |
358 | }, |
|
358 | }, | |
359 | // max bounds of each band |
|
359 | // max bounds of each band | |
360 | [&] { |
|
360 | [&] { | |
361 | return tryReadDoubles(m_Properties, MAX_BANDS_PROPERTY, line, |
|
361 | return tryReadDoubles(m_Properties, MAX_BANDS_PROPERTY, line, | |
362 | {SPECTROGRAM_MAX_BANDS_REGEX}); |
|
362 | {SPECTROGRAM_MAX_BANDS_REGEX}); | |
363 | }, |
|
363 | }, | |
364 | // start time of data |
|
364 | // start time of data | |
365 | [&] { |
|
365 | [&] { | |
366 | return tryReadDate(m_Properties, START_TIME_PROPERTY, line, |
|
366 | return tryReadDate(m_Properties, START_TIME_PROPERTY, line, | |
367 | {SPECTROGRAM_START_TIME_REGEX}); |
|
367 | {SPECTROGRAM_START_TIME_REGEX}); | |
368 | }, |
|
368 | }, | |
369 | // end time of data |
|
369 | // end time of data | |
370 | [&] { |
|
370 | [&] { | |
371 | return tryReadDate(m_Properties, END_TIME_PROPERTY, line, {SPECTROGRAM_END_TIME_REGEX}); |
|
371 | return tryReadDate(m_Properties, END_TIME_PROPERTY, line, {SPECTROGRAM_END_TIME_REGEX}); | |
372 | }}; |
|
372 | }}; | |
373 |
|
373 | |||
374 | for (auto function : functions) { |
|
374 | for (auto function : functions) { | |
375 | // Stops at the first function that is valid |
|
375 | // Stops at the first function that is valid | |
376 | if (function()) { |
|
376 | if (function()) { | |
377 | return; |
|
377 | return; | |
378 | } |
|
378 | } | |
379 | } |
|
379 | } | |
380 | } |
|
380 | } | |
381 |
|
381 | |||
382 | void SpectrogramParserHelper::readResultLine(const QString &line) |
|
382 | void SpectrogramParserHelper::readResultLine(const QString &line) | |
383 | { |
|
383 | { | |
384 | tryReadResult(m_XAxisData, m_ValuesData, line, m_ValuesIndexes, m_FillValue); |
|
384 | tryReadResult(m_XAxisData, m_ValuesData, line, m_ValuesIndexes, m_FillValue); | |
385 | } |
|
385 | } | |
386 |
|
386 | |||
387 | void SpectrogramParserHelper::handleDataHoles() |
|
387 | void SpectrogramParserHelper::handleDataHoles() | |
388 | { |
|
388 | { | |
389 | // Fills data holes according to the max resolution found in the AMDA file |
|
389 | // Fills data holes according to the max resolution found in the AMDA file | |
390 | auto resolution = m_Properties.value(MAX_SAMPLING_PROPERTY).value<double>(); |
|
390 | auto resolution = m_Properties.value(MAX_SAMPLING_PROPERTY).value<double>(); | |
391 | auto fillValue = m_Properties.value(FILL_VALUE_PROPERTY).value<double>(); |
|
391 | auto fillValue = m_Properties.value(FILL_VALUE_PROPERTY).value<double>(); | |
392 | auto minBound = m_Properties.value(START_TIME_PROPERTY).value<double>(); |
|
392 | auto minBound = m_Properties.value(START_TIME_PROPERTY).value<double>(); | |
393 | auto maxBound = m_Properties.value(END_TIME_PROPERTY).value<double>(); |
|
393 | auto maxBound = m_Properties.value(END_TIME_PROPERTY).value<double>(); | |
394 |
|
394 | |||
395 | DataSeriesUtils::fillDataHoles(m_XAxisData, m_ValuesData, resolution, fillValue, minBound, |
|
395 | DataSeriesUtils::fillDataHoles(m_XAxisData, m_ValuesData, resolution, fillValue, minBound, | |
396 | maxBound); |
|
396 | maxBound); | |
397 | } |
|
397 | } | |
398 |
|
398 | |||
399 | // ////////////////// // |
|
399 | // ////////////////// // | |
400 | // VectorParserHelper // |
|
400 | // VectorParserHelper // | |
401 | // ////////////////// // |
|
401 | // ////////////////// // | |
402 |
|
402 | |||
403 | bool VectorParserHelper::checkProperties() |
|
403 | bool VectorParserHelper::checkProperties() | |
404 | { |
|
404 | { | |
405 | return checkUnit(m_Properties, X_AXIS_UNIT_PROPERTY, |
|
405 | return checkUnit(m_Properties, X_AXIS_UNIT_PROPERTY, | |
406 | QObject::tr("The x-axis unit could not be found in the file")); |
|
406 | QObject::tr("The x-axis unit could not be found in the file")); | |
407 | } |
|
407 | } | |
408 |
|
408 | |||
409 |
|
|
409 | IDataSeries* VectorParserHelper::createSeries() | |
410 | { |
|
410 | { | |
411 |
return |
|
411 | return new VectorSeries(std::move(m_XAxisData), std::move(m_ValuesData), | |
412 | m_Properties.value(X_AXIS_UNIT_PROPERTY).value<Unit>(), |
|
412 | m_Properties.value(X_AXIS_UNIT_PROPERTY).value<Unit>(), | |
413 | m_Properties.value(VALUES_UNIT_PROPERTY).value<Unit>()); |
|
413 | m_Properties.value(VALUES_UNIT_PROPERTY).value<Unit>()); | |
414 | } |
|
414 | } | |
415 |
|
415 | |||
416 | void VectorParserHelper::readPropertyLine(const QString &line) |
|
416 | void VectorParserHelper::readPropertyLine(const QString &line) | |
417 | { |
|
417 | { | |
418 | tryReadUnit(m_Properties, X_AXIS_UNIT_PROPERTY, line, |
|
418 | tryReadUnit(m_Properties, X_AXIS_UNIT_PROPERTY, line, | |
419 | {DEFAULT_X_AXIS_UNIT_REGEX, ALTERNATIVE_X_AXIS_UNIT_REGEX}, true); |
|
419 | {DEFAULT_X_AXIS_UNIT_REGEX, ALTERNATIVE_X_AXIS_UNIT_REGEX}, true); | |
420 | } |
|
420 | } | |
421 |
|
421 | |||
422 | void VectorParserHelper::readResultLine(const QString &line) |
|
422 | void VectorParserHelper::readResultLine(const QString &line) | |
423 | { |
|
423 | { | |
424 | tryReadResult(m_XAxisData, m_ValuesData, line, valuesIndexes()); |
|
424 | tryReadResult(m_XAxisData, m_ValuesData, line, valuesIndexes()); | |
425 | } |
|
425 | } | |
426 |
|
426 | |||
427 | std::vector<int> VectorParserHelper::valuesIndexes() const |
|
427 | std::vector<int> VectorParserHelper::valuesIndexes() const | |
428 | { |
|
428 | { | |
429 | // 3 values to read, in order in the file (x, y, z) |
|
429 | // 3 values to read, in order in the file (x, y, z) | |
430 | static auto result = std::vector<int>{0, 1, 2}; |
|
430 | static auto result = std::vector<int>{0, 1, 2}; | |
431 | return result; |
|
431 | return result; | |
432 | } |
|
432 | } |
@@ -1,126 +1,126 | |||||
1 | /*------------------------------------------------------------------------------ |
|
1 | /*------------------------------------------------------------------------------ | |
2 | -- This file is a part of the SciQLOP Software |
|
2 | -- This file is a part of the SciQLOP Software | |
3 | -- Copyright (C) 2018, Plasma Physics Laboratory - CNRS |
|
3 | -- Copyright (C) 2018, Plasma Physics Laboratory - CNRS | |
4 | -- |
|
4 | -- | |
5 | -- This program is free software; you can redistribute it and/or modify |
|
5 | -- This program is free software; you can redistribute it and/or modify | |
6 | -- it under the terms of the GNU General Public License as published by |
|
6 | -- it under the terms of the GNU General Public License as published by | |
7 | -- the Free Software Foundation; either version 2 of the License, or |
|
7 | -- the Free Software Foundation; either version 2 of the License, or | |
8 | -- (at your option) any later version. |
|
8 | -- (at your option) any later version. | |
9 | -- |
|
9 | -- | |
10 | -- This program is distributed in the hope that it will be useful, |
|
10 | -- This program is distributed in the hope that it will be useful, | |
11 | -- but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
11 | -- but WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 | -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
12 | -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
13 | -- GNU General Public License for more details. |
|
13 | -- GNU General Public License for more details. | |
14 | -- |
|
14 | -- | |
15 | -- You should have received a copy of the GNU General Public License |
|
15 | -- You should have received a copy of the GNU General Public License | |
16 | -- along with this program; if not, write to the Free Software |
|
16 | -- along with this program; if not, write to the Free Software | |
17 | -- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
|
17 | -- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
18 | -------------------------------------------------------------------------------*/ |
|
18 | -------------------------------------------------------------------------------*/ | |
19 | /*-- Author : Alexis Jeandet |
|
19 | /*-- Author : Alexis Jeandet | |
20 | -- Mail : alexis.jeandet@member.fsf.org |
|
20 | -- Mail : alexis.jeandet@member.fsf.org | |
21 | ----------------------------------------------------------------------------*/ |
|
21 | ----------------------------------------------------------------------------*/ | |
22 | #include <string> |
|
22 | #include <string> | |
23 | #include <sstream> |
|
23 | #include <sstream> | |
24 | #include <memory> |
|
24 | #include <memory> | |
25 |
|
25 | |||
26 | #include <pybind11/pybind11.h> |
|
26 | #include <pybind11/pybind11.h> | |
27 | #include <pybind11/operators.h> |
|
27 | #include <pybind11/operators.h> | |
28 | #include <pybind11/embed.h> |
|
28 | #include <pybind11/embed.h> | |
29 | #include <pybind11/numpy.h> |
|
29 | #include <pybind11/numpy.h> | |
30 | #include <pybind11/chrono.h> |
|
30 | #include <pybind11/chrono.h> | |
31 |
|
31 | |||
32 | #include <SqpApplication.h> |
|
32 | #include <SqpApplication.h> | |
33 | #include <Variable/VariableController2.h> |
|
33 | #include <Variable/VariableController2.h> | |
34 | #include <Time/TimeController.h> |
|
34 | #include <Time/TimeController.h> | |
35 | #include <Data/DateTimeRange.h> |
|
35 | #include <Data/DateTimeRange.h> | |
36 | #include <Data/DataSeriesType.h> |
|
36 | #include <Data/DataSeriesType.h> | |
37 | #include <Common/DateUtils.h> |
|
37 | #include <Common/DateUtils.h> | |
38 | #include <Variable/Variable.h> |
|
38 | #include <Variable/Variable.h> | |
39 | #include <Data/ScalarSeries.h> |
|
39 | #include <Data/ScalarSeries.h> | |
40 | #include <Data/VectorSeries.h> |
|
40 | #include <Data/VectorSeries.h> | |
41 |
|
41 | |||
42 | #include <AmdaProvider.h> |
|
42 | #include <AmdaProvider.h> | |
43 | #include <AmdaResultParser.h> |
|
43 | #include <AmdaResultParser.h> | |
44 |
|
44 | |||
45 | //#include <QDate> |
|
45 | //#include <QDate> | |
46 | //#include <QTime> |
|
46 | //#include <QTime> | |
47 | //#include <QUuid> |
|
47 | //#include <QUuid> | |
48 | //#include <QString> |
|
48 | //#include <QString> | |
49 | #include <QFile> |
|
49 | #include <QFile> | |
50 |
|
50 | |||
51 | #include <pywrappers_common.h> |
|
51 | #include <pywrappers_common.h> | |
52 | #include <CoreWrappers.h> |
|
52 | #include <CoreWrappers.h> | |
53 |
|
53 | |||
54 | #include "PyTestAmdaWrapper.h" |
|
54 | #include "PyTestAmdaWrapper.h" | |
55 |
|
55 | |||
56 |
|
56 | |||
57 | using namespace std::chrono; |
|
57 | using namespace std::chrono; | |
58 |
|
58 | |||
59 |
|
59 | |||
60 |
|
60 | |||
61 |
|
61 | |||
62 | PYBIND11_MODULE(pytestamda, m){ |
|
62 | PYBIND11_MODULE(pytestamda, m){ | |
63 |
|
63 | |||
64 | int argc = 0; |
|
64 | int argc = 0; | |
65 | char ** argv=nullptr; |
|
65 | char ** argv=nullptr; | |
66 | SqpApplication::setOrganizationName("LPP"); |
|
66 | SqpApplication::setOrganizationName("LPP"); | |
67 | SqpApplication::setOrganizationDomain("lpp.fr"); |
|
67 | SqpApplication::setOrganizationDomain("lpp.fr"); | |
68 | SqpApplication::setApplicationName("SciQLop"); |
|
68 | SqpApplication::setApplicationName("SciQLop"); | |
69 | static SqpApplication app(argc, argv); |
|
69 | static SqpApplication app(argc, argv); | |
70 |
|
70 | |||
71 | auto qtmod = py::module::import("sciqlopqt"); |
|
71 | auto qtmod = py::module::import("sciqlopqt"); | |
72 | auto sciqlopmod = py::module::import("pysciqlopcore"); |
|
72 | auto sciqlopmod = py::module::import("pysciqlopcore"); | |
73 |
|
73 | |||
74 | m.doc() = "hello"; |
|
74 | m.doc() = "hello"; | |
75 |
|
75 | |||
76 | // py::class_<VariableController>(m, "VariableController") |
|
76 | // py::class_<VariableController>(m, "VariableController") | |
77 | // .def_static("createVariable",[](const QString &name, |
|
77 | // .def_static("createVariable",[](const QString &name, | |
78 | // std::shared_ptr<IDataProvider> provider, const DateTimeRange& range){ |
|
78 | // std::shared_ptr<IDataProvider> provider, const DateTimeRange& range){ | |
79 | // return sqpApp->variableController().createVariable(name, {{"dataType", "vector"}, {"xml:id", "c1_b"}}, provider, range); |
|
79 | // return sqpApp->variableController().createVariable(name, {{"dataType", "vector"}, {"xml:id", "c1_b"}}, provider, range); | |
80 | // }) |
|
80 | // }) | |
81 | // .def_static("hasPendingDownloads", |
|
81 | // .def_static("hasPendingDownloads", | |
82 | // [](){return sqpApp->variableController().hasPendingDownloads();} |
|
82 | // [](){return sqpApp->variableController().hasPendingDownloads();} | |
83 | // ) |
|
83 | // ) | |
84 | // .def_static("addSynchronizationGroup", |
|
84 | // .def_static("addSynchronizationGroup", | |
85 | // [](QUuid uuid){sqpApp->variableController().onAddSynchronizationGroupId(uuid);} |
|
85 | // [](QUuid uuid){sqpApp->variableController().onAddSynchronizationGroupId(uuid);} | |
86 | // ) |
|
86 | // ) | |
87 | // .def_static("removeSynchronizationGroup", |
|
87 | // .def_static("removeSynchronizationGroup", | |
88 | // [](QUuid uuid){sqpApp->variableController().onRemoveSynchronizationGroupId(uuid);} |
|
88 | // [](QUuid uuid){sqpApp->variableController().onRemoveSynchronizationGroupId(uuid);} | |
89 | // ) |
|
89 | // ) | |
90 | // .def_static("synchronizeVar", |
|
90 | // .def_static("synchronizeVar", | |
91 | // [](std::shared_ptr<Variable> variable, QUuid uuid){sqpApp->variableController().onAddSynchronized(variable, uuid);} |
|
91 | // [](std::shared_ptr<Variable> variable, QUuid uuid){sqpApp->variableController().onAddSynchronized(variable, uuid);} | |
92 | // ) |
|
92 | // ) | |
93 | // .def_static("deSynchronizeVar", |
|
93 | // .def_static("deSynchronizeVar", | |
94 | // [](std::shared_ptr<Variable> variable, QUuid uuid){sqpApp->variableController().desynchronize(variable, uuid);} |
|
94 | // [](std::shared_ptr<Variable> variable, QUuid uuid){sqpApp->variableController().desynchronize(variable, uuid);} | |
95 | // ) |
|
95 | // ) | |
96 | // .def_static("deleteVariable", |
|
96 | // .def_static("deleteVariable", | |
97 | // [](std::shared_ptr<Variable> variable){ |
|
97 | // [](std::shared_ptr<Variable> variable){ | |
98 | // sqpApp->variableController().deleteVariable(variable);} |
|
98 | // sqpApp->variableController().deleteVariable(variable);} | |
99 | // ) |
|
99 | // ) | |
100 | // .def_static("update_range",[](std::shared_ptr<Variable> variable, const DateTimeRange &range, bool synchronise){ |
|
100 | // .def_static("update_range",[](std::shared_ptr<Variable> variable, const DateTimeRange &range, bool synchronise){ | |
101 | // sqpApp->variableController().onRequestDataLoading({variable}, range, synchronise); |
|
101 | // sqpApp->variableController().onRequestDataLoading({variable}, range, synchronise); | |
102 | // }) |
|
102 | // }) | |
103 | // .def_static("wait_for_downloads",[](){ |
|
103 | // .def_static("wait_for_downloads",[](){ | |
104 | // while (sqpApp->variableController().hasPendingDownloads()) { |
|
104 | // while (sqpApp->variableController().hasPendingDownloads()) { | |
105 | // usleep(100); |
|
105 | // usleep(100); | |
106 | // } |
|
106 | // } | |
107 | // }); |
|
107 | // }); | |
108 |
|
108 | |||
109 | py::class_<TimeController>(m,"TimeController") |
|
109 | py::class_<TimeController>(m,"TimeController") | |
110 | .def_static("setTime", [](DateTimeRange range){sqpApp->timeController().setDateTimeRange(range);}); |
|
110 | .def_static("setTime", [](DateTimeRange range){sqpApp->timeController().setDateTimeRange(range);}); | |
111 |
|
111 | |||
112 |
|
112 | |||
113 | auto amda_provider = std::make_shared<AmdaProvider>(); |
|
113 | auto amda_provider = std::make_shared<AmdaProvider>(); | |
114 | m.def("amda_provider",[amda_provider](){return amda_provider;}, py::return_value_policy::copy); |
|
114 | m.def("amda_provider",[amda_provider](){return amda_provider;}, py::return_value_policy::copy); | |
115 |
|
115 | |||
116 | py::class_<AmdaProvider, std::shared_ptr<AmdaProvider>, IDataProvider>(m, "AmdaProvider"); |
|
116 | py::class_<AmdaProvider, std::shared_ptr<AmdaProvider>, IDataProvider>(m, "AmdaProvider"); | |
117 |
|
117 | |||
118 | py::class_<AmdaResultParser>(m, "AmdaResultParser") |
|
118 | // py::class_<AmdaResultParser>(m, "AmdaResultParser") | |
119 | .def_static("readTxt", AmdaResultParser::readTxt) |
|
119 | // .def_static("readTxt", AmdaResultParser::readTxt) | |
120 | .def("readScalarTxt", [](const QString& path){ |
|
120 | // .def("readScalarTxt", [](const QString& path){ | |
121 | return std::dynamic_pointer_cast<ScalarSeries>(AmdaResultParser::readTxt(path, DataSeriesType::SCALAR)); |
|
121 | // return std::dynamic_pointer_cast<ScalarSeries>(AmdaResultParser::readTxt(path, DataSeriesType::SCALAR)); | |
122 | }, py::return_value_policy::copy); |
|
122 | // }, py::return_value_policy::copy); | |
123 |
|
123 | |||
124 |
|
124 | |||
125 | } |
|
125 | } | |
126 |
|
126 |
@@ -1,29 +1,29 | |||||
1 | include_directories(include) |
|
1 | include_directories(include) | |
2 | FILE (GLOB_RECURSE mockplugin_SRCS |
|
2 | FILE (GLOB_RECURSE mockplugin_SRCS | |
3 | include/*.h |
|
3 | include/*.h | |
4 | src/*.cpp |
|
4 | src/*.cpp | |
5 | resources/*.qrc |
|
5 | resources/*.qrc | |
6 | ) |
|
6 | ) | |
7 |
|
7 | |||
8 | add_definitions(-DQT_PLUGIN) |
|
8 | add_definitions(-DQT_PLUGIN) | |
9 | add_definitions(-DPLUGIN_JSON_FILE_PATH="${CMAKE_CURRENT_SOURCE_DIR}/resources/mockplugin.json") |
|
9 | add_definitions(-DPLUGIN_JSON_FILE_PATH="${CMAKE_CURRENT_SOURCE_DIR}/resources/mockplugin.json") | |
10 | if(NOT BUILD_SHARED_LIBS) |
|
10 | if(NOT BUILD_SHARED_LIBS) | |
11 | add_definitions(-DQT_STATICPLUGIN) |
|
11 | add_definitions(-DQT_STATICPLUGIN) | |
12 | endif() |
|
12 | endif() | |
13 |
|
13 | |||
14 | add_library(mockplugin ${mockplugin_SRCS}) |
|
14 | add_library(mockplugin ${mockplugin_SRCS}) | |
15 | SET_TARGET_PROPERTIES(mockplugin PROPERTIES WINDOWS_EXPORT_ALL_SYMBOLS TRUE) |
|
15 | SET_TARGET_PROPERTIES(mockplugin PROPERTIES WINDOWS_EXPORT_ALL_SYMBOLS TRUE) | |
16 |
|
16 | |||
17 | target_link_libraries(mockplugin sciqlopgui) |
|
17 | target_link_libraries(mockplugin sciqlopgui) | |
18 |
|
18 | |||
19 | install(TARGETS mockplugin |
|
19 | install(TARGETS mockplugin | |
20 | ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}/SciQlop |
|
20 | ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}/SciQlop | |
21 | LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}/SciQlop |
|
21 | LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}/SciQlop | |
22 | RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) |
|
22 | RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) | |
23 |
|
23 | |||
24 | include(sciqlop_tests) |
|
24 | include(sciqlop_tests) | |
25 |
|
25 | |||
26 | add_definitions(-DMOCKPLUGIN_TESTS_RESOURCES_DIR="${CMAKE_CURRENT_LIST_DIR}/tests-resources") |
|
26 | add_definitions(-DMOCKPLUGIN_TESTS_RESOURCES_DIR="${CMAKE_CURRENT_LIST_DIR}/tests-resources") | |
27 |
|
27 | |||
28 | declare_test(TestCosinusAcquisition TestCosinusAcquisition tests/TestCosinusAcquisition.cpp "mockplugin;Qt5::Test") |
|
28 | #declare_test(TestCosinusAcquisition TestCosinusAcquisition tests/TestCosinusAcquisition.cpp "mockplugin;Qt5::Test") | |
29 |
|
29 |
@@ -1,45 +1,31 | |||||
1 | #ifndef SCIQLOP_COSINUSPROVIDER_H |
|
1 | #ifndef SCIQLOP_COSINUSPROVIDER_H | |
2 | #define SCIQLOP_COSINUSPROVIDER_H |
|
2 | #define SCIQLOP_COSINUSPROVIDER_H | |
3 |
|
3 | |||
4 | #include "MockPluginGlobal.h" |
|
4 | #include "MockPluginGlobal.h" | |
5 |
|
5 | |||
6 | #include <Data/IDataProvider.h> |
|
6 | #include <Data/IDataProvider.h> | |
7 |
|
7 | |||
8 | #include <QLoggingCategory> |
|
8 | #include <QLoggingCategory> | |
9 | #include <QUuid> |
|
9 | #include <QUuid> | |
10 |
|
10 | |||
11 | #include <QHash> |
|
11 | #include <QHash> | |
12 | Q_DECLARE_LOGGING_CATEGORY(LOG_CosinusProvider) |
|
|||
13 |
|
12 | |||
14 | /** |
|
13 | /** | |
15 | * @brief The CosinusProvider class is an example of how a data provider can generate data |
|
14 | * @brief The CosinusProvider class is an example of how a data provider can generate data | |
16 | */ |
|
15 | */ | |
17 | class SCIQLOP_MOCKPLUGIN_EXPORT CosinusProvider : public IDataProvider { |
|
16 | class SCIQLOP_MOCKPLUGIN_EXPORT CosinusProvider : public IDataProvider { | |
18 | public: |
|
17 | public: | |
19 | std::shared_ptr<IDataProvider> clone() const override; |
|
18 | std::shared_ptr<IDataProvider> clone() const override; | |
20 |
|
19 | |||
21 | /// @sa IDataProvider::requestDataLoading(). The current impl isn't thread safe. |
|
|||
22 | void requestDataLoading(QUuid acqIdentifier, const DataProviderParameters ¶meters) override; |
|
|||
23 |
|
||||
24 |
|
||||
25 | virtual IDataSeries* getData(const DataProviderParameters ¶meters) override; |
|
20 | virtual IDataSeries* getData(const DataProviderParameters ¶meters) override; | |
26 |
|
21 | |||
27 | /// @sa IDataProvider::requestDataAborting(). The current impl isn't thread safe. |
|
|||
28 | void requestDataAborting(QUuid acqIdentifier) override; |
|
|||
29 |
|
||||
30 |
|
||||
31 | /// Provide data |
|
|||
32 | std::shared_ptr<IDataSeries> provideDataSeries(const DateTimeRange &dataRangeRequested, |
|
|||
33 | const QVariantHash &data); |
|
|||
34 |
|
||||
35 |
|
||||
36 | private: |
|
22 | private: | |
37 | std::shared_ptr<IDataSeries> |
|
23 | std::shared_ptr<IDataSeries> | |
38 | retrieveData(QUuid acqIdentifier, const DateTimeRange &dataRangeRequested, const QVariantHash &data); |
|
24 | retrieveData(QUuid acqIdentifier, const DateTimeRange &dataRangeRequested, const QVariantHash &data); | |
39 |
|
25 | |||
40 | IDataSeries* _generate(const DateTimeRange &range, const QVariantHash &metaData); |
|
26 | IDataSeries* _generate(const DateTimeRange &range, const QVariantHash &metaData); | |
41 |
|
27 | |||
42 | QHash<QUuid, bool> m_VariableToEnableProvider; |
|
28 | QHash<QUuid, bool> m_VariableToEnableProvider; | |
43 | }; |
|
29 | }; | |
44 |
|
30 | |||
45 | #endif // SCIQLOP_COSINUSPROVIDER_H |
|
31 | #endif // SCIQLOP_COSINUSPROVIDER_H |
@@ -1,332 +1,199 | |||||
1 | #include "CosinusProvider.h" |
|
1 | #include "CosinusProvider.h" | |
2 | #include "MockDefs.h" |
|
2 | #include "MockDefs.h" | |
3 |
|
3 | |||
4 | #include <Data/DataProviderParameters.h> |
|
4 | #include <Data/DataProviderParameters.h> | |
5 | #include <Data/ScalarSeries.h> |
|
5 | #include <Data/ScalarSeries.h> | |
6 | #include <Data/SpectrogramSeries.h> |
|
6 | #include <Data/SpectrogramSeries.h> | |
7 | #include <Data/VectorSeries.h> |
|
7 | #include <Data/VectorSeries.h> | |
8 |
|
8 | |||
9 | #include <cmath> |
|
9 | #include <cmath> | |
10 | #include <set> |
|
10 | #include <set> | |
11 |
|
11 | |||
12 | #include <QFuture> |
|
12 | #include <QFuture> | |
13 | #include <QThread> |
|
13 | #include <QThread> | |
14 | #include <QtConcurrent/QtConcurrent> |
|
14 | #include <QtConcurrent/QtConcurrent> | |
15 |
|
15 | |||
16 | Q_LOGGING_CATEGORY(LOG_CosinusProvider, "CosinusProvider") |
|
|||
17 |
|
16 | |||
18 | namespace { |
|
17 | namespace { | |
19 |
|
18 | |||
20 | /// Number of bands generated for a spectrogram |
|
19 | /// Number of bands generated for a spectrogram | |
21 | const auto SPECTROGRAM_NUMBER_BANDS = 30; |
|
20 | const auto SPECTROGRAM_NUMBER_BANDS = 30; | |
22 |
|
21 | |||
23 | /// Bands for which to generate NaN values for a spectrogram |
|
22 | /// Bands for which to generate NaN values for a spectrogram | |
24 | const auto SPECTROGRAM_NAN_BANDS = std::set<int>{1, 3, 10, 20}; |
|
23 | const auto SPECTROGRAM_NAN_BANDS = std::set<int>{1, 3, 10, 20}; | |
25 |
|
24 | |||
26 | /// Bands for which to generate zeros for a spectrogram |
|
25 | /// Bands for which to generate zeros for a spectrogram | |
27 | const auto SPECTROGRAM_ZERO_BANDS = std::set<int>{2, 15, 19, 29}; |
|
26 | const auto SPECTROGRAM_ZERO_BANDS = std::set<int>{2, 15, 19, 29}; | |
28 |
|
27 | |||
29 | /// Abstract cosinus type |
|
28 | /// Abstract cosinus type | |
30 | struct ICosinusType { |
|
29 | struct ICosinusType { | |
31 | virtual ~ICosinusType() = default; |
|
30 | virtual ~ICosinusType() = default; | |
32 | /// @return the number of components generated for the type |
|
31 | /// @return the number of components generated for the type | |
33 | virtual std::size_t componentCount() const = 0; |
|
32 | virtual std::size_t componentCount() const = 0; | |
34 | /// @return the data series created for the type |
|
33 | /// @return the data series created for the type | |
35 | virtual IDataSeries* createDataSeries(std::vector<double> xAxisData, |
|
34 | virtual IDataSeries* createDataSeries(std::vector<double> xAxisData, | |
36 | std::vector<double> valuesData) const = 0; |
|
35 | std::vector<double> valuesData) const = 0; | |
37 | /// Generates values (one value per component) |
|
36 | /// Generates values (one value per component) | |
38 | /// @param x the x-axis data used to generate values |
|
37 | /// @param x the x-axis data used to generate values | |
39 | /// @param values the vector in which to insert the generated values |
|
38 | /// @param values the vector in which to insert the generated values | |
40 | /// @param dataIndex the index of insertion of the generated values |
|
39 | /// @param dataIndex the index of insertion of the generated values | |
41 | /// |
|
40 | /// | |
42 | virtual void generateValues(double x, std::vector<double> &values, int dataIndex) const = 0; |
|
41 | virtual void generateValues(double x, std::vector<double> &values, int dataIndex) const = 0; | |
43 | }; |
|
42 | }; | |
44 |
|
43 | |||
45 | struct ScalarCosinus : public ICosinusType { |
|
44 | struct ScalarCosinus : public ICosinusType { | |
46 | std::size_t componentCount() const override { return 1; } |
|
45 | std::size_t componentCount() const override { return 1; } | |
47 |
|
46 | |||
48 | IDataSeries* createDataSeries(std::vector<double> xAxisData, |
|
47 | IDataSeries* createDataSeries(std::vector<double> xAxisData, | |
49 | std::vector<double> valuesData) const override |
|
48 | std::vector<double> valuesData) const override | |
50 | { |
|
49 | { | |
51 | return new ScalarSeries(std::move(xAxisData), std::move(valuesData), |
|
50 | return new ScalarSeries(std::move(xAxisData), std::move(valuesData), | |
52 | Unit{QStringLiteral("t"), true}, Unit{}); |
|
51 | Unit{QStringLiteral("t"), true}, Unit{}); | |
53 | } |
|
52 | } | |
54 |
|
53 | |||
55 | void generateValues(double x, std::vector<double> &values, int dataIndex) const override |
|
54 | void generateValues(double x, std::vector<double> &values, int dataIndex) const override | |
56 | { |
|
55 | { | |
57 | values[dataIndex] = std::cos(x); |
|
56 | values[dataIndex] = std::cos(x); | |
58 | } |
|
57 | } | |
59 | }; |
|
58 | }; | |
60 |
|
59 | |||
61 | struct SpectrogramCosinus : public ICosinusType { |
|
60 | struct SpectrogramCosinus : public ICosinusType { | |
62 | /// Ctor with y-axis |
|
61 | /// Ctor with y-axis | |
63 | explicit SpectrogramCosinus(std::vector<double> yAxisData, Unit yAxisUnit, Unit valuesUnit) |
|
62 | explicit SpectrogramCosinus(std::vector<double> yAxisData, Unit yAxisUnit, Unit valuesUnit) | |
64 | : m_YAxisData{std::move(yAxisData)}, |
|
63 | : m_YAxisData{std::move(yAxisData)}, | |
65 | m_YAxisUnit{std::move(yAxisUnit)}, |
|
64 | m_YAxisUnit{std::move(yAxisUnit)}, | |
66 | m_ValuesUnit{std::move(valuesUnit)} |
|
65 | m_ValuesUnit{std::move(valuesUnit)} | |
67 | { |
|
66 | { | |
68 | } |
|
67 | } | |
69 |
|
68 | |||
70 | std::size_t componentCount() const override { return m_YAxisData.size(); } |
|
69 | std::size_t componentCount() const override { return m_YAxisData.size(); } | |
71 |
|
70 | |||
72 | IDataSeries* createDataSeries(std::vector<double> xAxisData, |
|
71 | IDataSeries* createDataSeries(std::vector<double> xAxisData, | |
73 | std::vector<double> valuesData) const override |
|
72 | std::vector<double> valuesData) const override | |
74 | { |
|
73 | { | |
75 | return new SpectrogramSeries( |
|
74 | return new SpectrogramSeries( | |
76 | std::move(xAxisData), m_YAxisData, std::move(valuesData), |
|
75 | std::move(xAxisData), m_YAxisData, std::move(valuesData), | |
77 | Unit{QStringLiteral("t"), true}, m_YAxisUnit, m_ValuesUnit); |
|
76 | Unit{QStringLiteral("t"), true}, m_YAxisUnit, m_ValuesUnit); | |
78 | } |
|
77 | } | |
79 |
|
78 | |||
80 | void generateValues(double x, std::vector<double> &values, int dataIndex) const override |
|
79 | void generateValues(double x, std::vector<double> &values, int dataIndex) const override | |
81 | { |
|
80 | { | |
82 | auto componentCount = this->componentCount(); |
|
81 | auto componentCount = this->componentCount(); | |
83 | for (int i = 0; i < componentCount; ++i) { |
|
82 | for (int i = 0; i < componentCount; ++i) { | |
84 | auto y = m_YAxisData[i]; |
|
83 | auto y = m_YAxisData[i]; | |
85 |
|
84 | |||
86 | double value; |
|
85 | double value; | |
87 |
|
86 | |||
88 | if (SPECTROGRAM_ZERO_BANDS.find(y) != SPECTROGRAM_ZERO_BANDS.end()) { |
|
87 | if (SPECTROGRAM_ZERO_BANDS.find(y) != SPECTROGRAM_ZERO_BANDS.end()) { | |
89 | value = 0.; |
|
88 | value = 0.; | |
90 | } |
|
89 | } | |
91 | else if (SPECTROGRAM_NAN_BANDS.find(y) != SPECTROGRAM_NAN_BANDS.end()) { |
|
90 | else if (SPECTROGRAM_NAN_BANDS.find(y) != SPECTROGRAM_NAN_BANDS.end()) { | |
92 | value = std::numeric_limits<double>::quiet_NaN(); |
|
91 | value = std::numeric_limits<double>::quiet_NaN(); | |
93 | } |
|
92 | } | |
94 | else { |
|
93 | else { | |
95 | // Generates value for non NaN/zero bands |
|
94 | // Generates value for non NaN/zero bands | |
96 | auto r = 3 * std::sqrt(x * x + y * y) + 1e-2; |
|
95 | auto r = 3 * std::sqrt(x * x + y * y) + 1e-2; | |
97 | value = 2 * x * (std::cos(r + 2) / r - std::sin(r + 2) / r); |
|
96 | value = 2 * x * (std::cos(r + 2) / r - std::sin(r + 2) / r); | |
98 | } |
|
97 | } | |
99 |
|
98 | |||
100 | values[componentCount * dataIndex + i] = value; |
|
99 | values[componentCount * dataIndex + i] = value; | |
101 | } |
|
100 | } | |
102 | } |
|
101 | } | |
103 |
|
102 | |||
104 | std::vector<double> m_YAxisData; |
|
103 | std::vector<double> m_YAxisData; | |
105 | Unit m_YAxisUnit; |
|
104 | Unit m_YAxisUnit; | |
106 | Unit m_ValuesUnit; |
|
105 | Unit m_ValuesUnit; | |
107 | }; |
|
106 | }; | |
108 |
|
107 | |||
109 | struct VectorCosinus : public ICosinusType { |
|
108 | struct VectorCosinus : public ICosinusType { | |
110 | std::size_t componentCount() const override { return 3; } |
|
109 | std::size_t componentCount() const override { return 3; } | |
111 |
|
110 | |||
112 | IDataSeries* createDataSeries(std::vector<double> xAxisData, |
|
111 | IDataSeries* createDataSeries(std::vector<double> xAxisData, | |
113 | std::vector<double> valuesData) const override |
|
112 | std::vector<double> valuesData) const override | |
114 | { |
|
113 | { | |
115 | return new VectorSeries(std::move(xAxisData), std::move(valuesData), |
|
114 | return new VectorSeries(std::move(xAxisData), std::move(valuesData), | |
116 | Unit{QStringLiteral("t"), true}, Unit{}); |
|
115 | Unit{QStringLiteral("t"), true}, Unit{}); | |
117 | } |
|
116 | } | |
118 |
|
117 | |||
119 | void generateValues(double x, std::vector<double> &values, int dataIndex) const override |
|
118 | void generateValues(double x, std::vector<double> &values, int dataIndex) const override | |
120 | { |
|
119 | { | |
121 | // Generates value for each component: cos(x), cos(x)/2, cos(x)/3 |
|
120 | // Generates value for each component: cos(x), cos(x)/2, cos(x)/3 | |
122 | auto xValue = std::cos(x); |
|
121 | auto xValue = std::cos(x); | |
123 | auto componentCount = this->componentCount(); |
|
122 | auto componentCount = this->componentCount(); | |
124 | for (auto i = 0; i < componentCount; ++i) { |
|
123 | for (auto i = 0; i < componentCount; ++i) { | |
125 | values[componentCount * dataIndex + i] = xValue / (i + 1); |
|
124 | values[componentCount * dataIndex + i] = xValue / (i + 1); | |
126 | } |
|
125 | } | |
127 | } |
|
126 | } | |
128 | }; |
|
127 | }; | |
129 |
|
128 | |||
130 | /// Converts string to cosinus type |
|
129 | /// Converts string to cosinus type | |
131 | /// @return the cosinus type if the string could be converted, nullptr otherwise |
|
130 | /// @return the cosinus type if the string could be converted, nullptr otherwise | |
132 | std::unique_ptr<ICosinusType> cosinusType(const QString &type) noexcept |
|
131 | std::unique_ptr<ICosinusType> cosinusType(const QString &type) noexcept | |
133 | { |
|
132 | { | |
134 | if (type.compare(QStringLiteral("scalar"), Qt::CaseInsensitive) == 0) { |
|
133 | if (type.compare(QStringLiteral("scalar"), Qt::CaseInsensitive) == 0) { | |
135 | return std::make_unique<ScalarCosinus>(); |
|
134 | return std::make_unique<ScalarCosinus>(); | |
136 | } |
|
135 | } | |
137 | else if (type.compare(QStringLiteral("spectrogram"), Qt::CaseInsensitive) == 0) { |
|
136 | else if (type.compare(QStringLiteral("spectrogram"), Qt::CaseInsensitive) == 0) { | |
138 | // Generates default y-axis data for spectrogram [0., 1., 2., ...] |
|
137 | // Generates default y-axis data for spectrogram [0., 1., 2., ...] | |
139 | std::vector<double> yAxisData(SPECTROGRAM_NUMBER_BANDS); |
|
138 | std::vector<double> yAxisData(SPECTROGRAM_NUMBER_BANDS); | |
140 | std::iota(yAxisData.begin(), yAxisData.end(), 0.); |
|
139 | std::iota(yAxisData.begin(), yAxisData.end(), 0.); | |
141 |
|
140 | |||
142 | return std::make_unique<SpectrogramCosinus>(std::move(yAxisData), Unit{"eV"}, |
|
141 | return std::make_unique<SpectrogramCosinus>(std::move(yAxisData), Unit{"eV"}, | |
143 | Unit{"eV/(cm^2-s-sr-eV)"}); |
|
142 | Unit{"eV/(cm^2-s-sr-eV)"}); | |
144 | } |
|
143 | } | |
145 | else if (type.compare(QStringLiteral("vector"), Qt::CaseInsensitive) == 0) { |
|
144 | else if (type.compare(QStringLiteral("vector"), Qt::CaseInsensitive) == 0) { | |
146 | return std::make_unique<VectorCosinus>(); |
|
145 | return std::make_unique<VectorCosinus>(); | |
147 | } |
|
146 | } | |
148 | else { |
|
147 | else { | |
149 | return nullptr; |
|
148 | return nullptr; | |
150 | } |
|
149 | } | |
151 | } |
|
150 | } | |
152 |
|
151 | |||
153 | } // namespace |
|
152 | } // namespace | |
154 |
|
153 | |||
155 | std::shared_ptr<IDataProvider> CosinusProvider::clone() const |
|
154 | std::shared_ptr<IDataProvider> CosinusProvider::clone() const | |
156 | { |
|
155 | { | |
157 | // No copy is made in clone |
|
156 | // No copy is made in clone | |
158 | return std::make_shared<CosinusProvider>(); |
|
157 | return std::make_shared<CosinusProvider>(); | |
159 | } |
|
158 | } | |
160 |
|
159 | |||
161 | std::shared_ptr<IDataSeries> CosinusProvider::retrieveData(QUuid acqIdentifier, |
|
|||
162 | const DateTimeRange &dataRangeRequested, |
|
|||
163 | const QVariantHash &data) |
|
|||
164 | { |
|
|||
165 | // TODO: Add Mutex |
|
|||
166 | auto dataIndex = 0; |
|
|||
167 |
|
||||
168 | // Retrieves cosinus type |
|
|||
169 | auto typeVariant = data.value(COSINUS_TYPE_KEY, COSINUS_TYPE_DEFAULT_VALUE); |
|
|||
170 | if (!typeVariant.canConvert<QString>()) { |
|
|||
171 | qCCritical(LOG_CosinusProvider()) << tr("Can't retrieve data: invalid type"); |
|
|||
172 | return nullptr; |
|
|||
173 | } |
|
|||
174 |
|
||||
175 | auto type = cosinusType(typeVariant.toString()); |
|
|||
176 | if (!type) { |
|
|||
177 | qCCritical(LOG_CosinusProvider()) << tr("Can't retrieve data: unknown type"); |
|
|||
178 | return nullptr; |
|
|||
179 | } |
|
|||
180 |
|
||||
181 | // Retrieves frequency |
|
|||
182 | auto freqVariant = data.value(COSINUS_FREQUENCY_KEY, COSINUS_FREQUENCY_DEFAULT_VALUE); |
|
|||
183 | if (!freqVariant.canConvert<double>()) { |
|
|||
184 | qCCritical(LOG_CosinusProvider()) << tr("Can't retrieve data: invalid frequency"); |
|
|||
185 | return nullptr; |
|
|||
186 | } |
|
|||
187 |
|
||||
188 | // Gets the timerange from the parameters |
|
|||
189 | double freq = freqVariant.toDouble(); |
|
|||
190 | double start = std::ceil(dataRangeRequested.m_TStart * freq); |
|
|||
191 | double end = std::floor(dataRangeRequested.m_TEnd * freq); |
|
|||
192 |
|
||||
193 | // We assure that timerange is valid |
|
|||
194 | if (end < start) { |
|
|||
195 | std::swap(start, end); |
|
|||
196 | } |
|
|||
197 |
|
||||
198 | // Generates scalar series containing cosinus values (one value per second, end value is |
|
|||
199 | // included) |
|
|||
200 | auto dataCount = end - start + 1; |
|
|||
201 |
|
||||
202 | // Number of components (depending on the cosinus type) |
|
|||
203 | auto componentCount = type->componentCount(); |
|
|||
204 |
|
||||
205 | auto xAxisData = std::vector<double>{}; |
|
|||
206 | xAxisData.resize(dataCount); |
|
|||
207 |
|
||||
208 | auto valuesData = std::vector<double>{}; |
|
|||
209 | valuesData.resize(dataCount * componentCount); |
|
|||
210 |
|
||||
211 | int progress = 0; |
|
|||
212 | auto progressEnd = dataCount; |
|
|||
213 | for (auto time = start; time <= end; ++time, ++dataIndex) { |
|
|||
214 | auto it = m_VariableToEnableProvider.find(acqIdentifier); |
|
|||
215 | if (it != m_VariableToEnableProvider.end() && it.value()) { |
|
|||
216 | const auto x = time / freq; |
|
|||
217 |
|
||||
218 | xAxisData[dataIndex] = x; |
|
|||
219 |
|
||||
220 | // Generates values (depending on the type) |
|
|||
221 | type->generateValues(x, valuesData, dataIndex); |
|
|||
222 |
|
||||
223 | // progression |
|
|||
224 | int currentProgress = (time - start) * 100.0 / progressEnd; |
|
|||
225 | if (currentProgress != progress) { |
|
|||
226 | progress = currentProgress; |
|
|||
227 |
|
||||
228 | emit dataProvidedProgress(acqIdentifier, progress); |
|
|||
229 | qCDebug(LOG_CosinusProvider()) << "TORM: CosinusProvider::retrieveData" |
|
|||
230 | << QThread::currentThread()->objectName() |
|
|||
231 | << progress; |
|
|||
232 | // NOTE: Try to use multithread if possible |
|
|||
233 | } |
|
|||
234 | } |
|
|||
235 | else { |
|
|||
236 | if (!it.value()) { |
|
|||
237 | qCDebug(LOG_CosinusProvider()) |
|
|||
238 | << "CosinusProvider::retrieveData: ARRET De l'acquisition detecté" |
|
|||
239 | << end - time; |
|
|||
240 | } |
|
|||
241 | } |
|
|||
242 | } |
|
|||
243 | if (progress != 100) { |
|
|||
244 | // We can close progression beacause all data has been retrieved |
|
|||
245 | emit dataProvidedProgress(acqIdentifier, 100); |
|
|||
246 | } |
|
|||
247 | return std::shared_ptr<IDataSeries>(type->createDataSeries(std::move(xAxisData), std::move(valuesData))); |
|
|||
248 | } |
|
|||
249 |
|
||||
250 | IDataSeries *CosinusProvider::_generate(const DateTimeRange &range, const QVariantHash &metaData) |
|
160 | IDataSeries *CosinusProvider::_generate(const DateTimeRange &range, const QVariantHash &metaData) | |
251 | { |
|
161 | { | |
252 | auto dataIndex = 0; |
|
162 | auto dataIndex = 0; | |
253 |
|
163 | |||
254 | // Retrieves cosinus type |
|
164 | // Retrieves cosinus type | |
255 | auto typeVariant = metaData.value(COSINUS_TYPE_KEY, COSINUS_TYPE_DEFAULT_VALUE); |
|
165 | auto typeVariant = metaData.value(COSINUS_TYPE_KEY, COSINUS_TYPE_DEFAULT_VALUE); | |
256 | auto type = cosinusType(typeVariant.toString()); |
|
166 | auto type = cosinusType(typeVariant.toString()); | |
257 | auto freqVariant = metaData.value(COSINUS_FREQUENCY_KEY, COSINUS_FREQUENCY_DEFAULT_VALUE); |
|
167 | auto freqVariant = metaData.value(COSINUS_FREQUENCY_KEY, COSINUS_FREQUENCY_DEFAULT_VALUE); | |
258 | double freq = freqVariant.toDouble(); |
|
168 | double freq = freqVariant.toDouble(); | |
259 | double start = std::ceil(range.m_TStart * freq); |
|
169 | double start = std::ceil(range.m_TStart * freq); | |
260 | double end = std::floor(range.m_TEnd * freq); |
|
170 | double end = std::floor(range.m_TEnd * freq); | |
261 | if (end < start) { |
|
171 | if (end < start) { | |
262 | std::swap(start, end); |
|
172 | std::swap(start, end); | |
263 | } |
|
173 | } | |
264 | std::size_t dataCount = static_cast<std::size_t>(end - start + 1); |
|
174 | std::size_t dataCount = static_cast<std::size_t>(end - start + 1); | |
265 | std::size_t componentCount = type->componentCount(); |
|
175 | std::size_t componentCount = type->componentCount(); | |
266 |
|
176 | |||
267 | auto xAxisData = std::vector<double>{}; |
|
177 | auto xAxisData = std::vector<double>{}; | |
268 | xAxisData.resize(dataCount); |
|
178 | xAxisData.resize(dataCount); | |
269 |
|
179 | |||
270 | auto valuesData = std::vector<double>{}; |
|
180 | auto valuesData = std::vector<double>{}; | |
271 | valuesData.resize(dataCount * componentCount); |
|
181 | valuesData.resize(dataCount * componentCount); | |
272 |
|
182 | |||
273 | int progress = 0; |
|
183 | int progress = 0; | |
274 | auto progressEnd = dataCount; |
|
184 | auto progressEnd = dataCount; | |
275 | for (auto time = start; time <= end; ++time, ++dataIndex) |
|
185 | for (auto time = start; time <= end; ++time, ++dataIndex) | |
276 | { |
|
186 | { | |
277 | const auto x = time / freq; |
|
187 | const auto x = time / freq; | |
278 | xAxisData[dataIndex] = x; |
|
188 | xAxisData[dataIndex] = x; | |
279 | // Generates values (depending on the type) |
|
189 | // Generates values (depending on the type) | |
280 | type->generateValues(x, valuesData, dataIndex); |
|
190 | type->generateValues(x, valuesData, dataIndex); | |
281 | } |
|
191 | } | |
282 | return type->createDataSeries(std::move(xAxisData), std::move(valuesData)); |
|
192 | return type->createDataSeries(std::move(xAxisData), std::move(valuesData)); | |
283 | } |
|
193 | } | |
284 |
|
194 | |||
285 | void CosinusProvider::requestDataLoading(QUuid acqIdentifier, |
|
|||
286 | const DataProviderParameters ¶meters) |
|
|||
287 | { |
|
|||
288 | // TODO: Add Mutex |
|
|||
289 | m_VariableToEnableProvider[acqIdentifier] = true; |
|
|||
290 | qCDebug(LOG_CosinusProvider()) << "TORM: CosinusProvider::requestDataLoading" |
|
|||
291 | << QThread::currentThread()->objectName(); |
|
|||
292 | // NOTE: Try to use multithread if possible |
|
|||
293 | const auto times = parameters.m_Times; |
|
|||
294 |
|
||||
295 | for (const auto &dateTime : qAsConst(times)) { |
|
|||
296 | if (m_VariableToEnableProvider[acqIdentifier]) { |
|
|||
297 | auto scalarSeries = this->retrieveData(acqIdentifier, dateTime, parameters.m_Data); |
|
|||
298 | emit dataProvided(acqIdentifier, scalarSeries, dateTime); |
|
|||
299 | } |
|
|||
300 | } |
|
|||
301 | } |
|
|||
302 |
|
||||
303 | IDataSeries* CosinusProvider::getData(const DataProviderParameters ¶meters) |
|
195 | IDataSeries* CosinusProvider::getData(const DataProviderParameters ¶meters) | |
304 | { |
|
196 | { | |
305 | return _generate(parameters.m_Times.front(),parameters.m_Data); |
|
197 | return _generate(parameters.m_Times.front(),parameters.m_Data); | |
306 | } |
|
198 | } | |
307 |
|
199 | |||
308 |
|
||||
309 | void CosinusProvider::requestDataAborting(QUuid acqIdentifier) |
|
|||
310 | { |
|
|||
311 | qCDebug(LOG_CosinusProvider()) << "CosinusProvider::requestDataAborting" << acqIdentifier |
|
|||
312 | << QThread::currentThread()->objectName(); |
|
|||
313 | auto it = m_VariableToEnableProvider.find(acqIdentifier); |
|
|||
314 | if (it != m_VariableToEnableProvider.end()) { |
|
|||
315 | it.value() = false; |
|
|||
316 | } |
|
|||
317 | else { |
|
|||
318 | qCDebug(LOG_CosinusProvider()) |
|
|||
319 | << tr("Aborting progression of inexistant identifier detected !!!"); |
|
|||
320 | } |
|
|||
321 | } |
|
|||
322 |
|
||||
323 | std::shared_ptr<IDataSeries> CosinusProvider::provideDataSeries(const DateTimeRange &dataRangeRequested, |
|
|||
324 | const QVariantHash &data) |
|
|||
325 | { |
|
|||
326 | auto uid = QUuid::createUuid(); |
|
|||
327 | m_VariableToEnableProvider[uid] = true; |
|
|||
328 | auto dataSeries = this->retrieveData(uid, dataRangeRequested, data); |
|
|||
329 |
|
||||
330 | m_VariableToEnableProvider.remove(uid); |
|
|||
331 | return dataSeries; |
|
|||
332 | } |
|
1 | NO CONTENT: file was removed |
|
NO CONTENT: file was removed |
General Comments 0
You need to be logged in to leave comments.
Login now