##// END OF EJS Templates
Removed old IDataProvider IF, some small steps toward new VC integration...
jeandet -
r1351:d755f1f0a484
parent child
Show More
@@ -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 requestDataLoading(QVector<std::shared_ptr<Variable> > variable, const DateTimeRange &range,
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 void requestDataLoading(QUuid acqIdentifier, const DataProviderParameters &parameters) override;
26 virtual IDataSeries *getData(const DataProviderParameters &parameters)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 std::shared_ptr<IDataSeries> createSeries() = 0;
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 std::shared_ptr<IDataSeries> createSeries() override;
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 std::shared_ptr<IDataSeries> createSeries() override;
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 std::shared_ptr<IDataSeries> createSeries() override;
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&parameterID=%4&outputFormat=ASCII&"
31 "getParameter.php?startTime=%2&stopTime=%3&parameterID=%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&parameterID=%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 void AmdaProvider::requestDataLoading(QUuid acqIdentifier, const DataProviderParameters &parameters)
68 IDataSeries* AmdaProvider::getData(const DataProviderParameters &parameters)
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(data.value(AMDA_DATA_TYPE_KEY).toString());
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> &regexes, GeneratePropertyFun fun)
147 const std::vector<QRegularExpression> &regexes, 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> &regexes, bool timeUnit = false)
172 const std::vector<QRegularExpression> &regexes, 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> &regexes)
184 const std::vector<QRegularExpression> &regexes)
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> &regexes,
205 const std::vector<QRegularExpression> &regexes,
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> &regexes, bool timeUnit = false)
233 const std::vector<QRegularExpression> &regexes, 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 std::shared_ptr<IDataSeries> ScalarParserHelper::createSeries()
252 IDataSeries* ScalarParserHelper::createSeries()
253 {
253 {
254 return std::make_shared<ScalarSeries>(std::move(m_XAxisData), std::move(m_ValuesData),
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 std::shared_ptr<IDataSeries> SpectrogramParserHelper::createSeries()
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 std::make_shared<SpectrogramSeries>(
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 std::shared_ptr<IDataSeries> VectorParserHelper::createSeries()
409 IDataSeries* VectorParserHelper::createSeries()
410 {
410 {
411 return std::make_shared<VectorSeries>(std::move(m_XAxisData), std::move(m_ValuesData),
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 &parameters) override;
23
24
25 virtual IDataSeries* getData(const DataProviderParameters &parameters) override;
20 virtual IDataSeries* getData(const DataProviderParameters &parameters) 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 &parameters)
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 &parameters)
195 IDataSeries* CosinusProvider::getData(const DataProviderParameters &parameters)
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