##// END OF EJS Templates
Add thread protection for DataSeries...
perrinel -
r594:ad1efe843595
parent child
Show More
@@ -1,271 +1,274
1 #include <Variable/Variable.h>
1 #include <Variable/Variable.h>
2 #include <Variable/VariableModel.h>
2 #include <Variable/VariableModel.h>
3
3
4 #include <Common/DateUtils.h>
4 #include <Common/DateUtils.h>
5
5
6 #include <Data/IDataSeries.h>
6 #include <Data/IDataSeries.h>
7
7
8 #include <QSize>
8 #include <QSize>
9 #include <unordered_map>
9 #include <unordered_map>
10
10
11 Q_LOGGING_CATEGORY(LOG_VariableModel, "VariableModel")
11 Q_LOGGING_CATEGORY(LOG_VariableModel, "VariableModel")
12
12
13 namespace {
13 namespace {
14
14
15 // Column indexes
15 // Column indexes
16 const auto NAME_COLUMN = 0;
16 const auto NAME_COLUMN = 0;
17 const auto TSTART_COLUMN = 1;
17 const auto TSTART_COLUMN = 1;
18 const auto TEND_COLUMN = 2;
18 const auto TEND_COLUMN = 2;
19 const auto UNIT_COLUMN = 3;
19 const auto UNIT_COLUMN = 3;
20 const auto MISSION_COLUMN = 4;
20 const auto MISSION_COLUMN = 4;
21 const auto PLUGIN_COLUMN = 5;
21 const auto PLUGIN_COLUMN = 5;
22 const auto NB_COLUMNS = 6;
22 const auto NB_COLUMNS = 6;
23
23
24 // Column properties
24 // Column properties
25 const auto DEFAULT_HEIGHT = 25;
25 const auto DEFAULT_HEIGHT = 25;
26 const auto DEFAULT_WIDTH = 100;
26 const auto DEFAULT_WIDTH = 100;
27
27
28 struct ColumnProperties {
28 struct ColumnProperties {
29 ColumnProperties(const QString &name = {}, int width = DEFAULT_WIDTH,
29 ColumnProperties(const QString &name = {}, int width = DEFAULT_WIDTH,
30 int height = DEFAULT_HEIGHT)
30 int height = DEFAULT_HEIGHT)
31 : m_Name{name}, m_Width{width}, m_Height{height}
31 : m_Name{name}, m_Width{width}, m_Height{height}
32 {
32 {
33 }
33 }
34
34
35 QString m_Name;
35 QString m_Name;
36 int m_Width;
36 int m_Width;
37 int m_Height;
37 int m_Height;
38 };
38 };
39
39
40 const auto COLUMN_PROPERTIES = QHash<int, ColumnProperties>{
40 const auto COLUMN_PROPERTIES = QHash<int, ColumnProperties>{
41 {NAME_COLUMN, {QObject::tr("Name")}}, {TSTART_COLUMN, {QObject::tr("tStart"), 180}},
41 {NAME_COLUMN, {QObject::tr("Name")}}, {TSTART_COLUMN, {QObject::tr("tStart"), 180}},
42 {TEND_COLUMN, {QObject::tr("tEnd"), 180}}, {UNIT_COLUMN, {QObject::tr("Unit")}},
42 {TEND_COLUMN, {QObject::tr("tEnd"), 180}}, {UNIT_COLUMN, {QObject::tr("Unit")}},
43 {MISSION_COLUMN, {QObject::tr("Mission")}}, {PLUGIN_COLUMN, {QObject::tr("Plugin")}}};
43 {MISSION_COLUMN, {QObject::tr("Mission")}}, {PLUGIN_COLUMN, {QObject::tr("Plugin")}}};
44
44
45 /// Format for datetimes
45 /// Format for datetimes
46 const auto DATETIME_FORMAT = QStringLiteral("dd/MM/yyyy \nhh:mm:ss:zzz");
46 const auto DATETIME_FORMAT = QStringLiteral("dd/MM/yyyy \nhh:mm:ss:zzz");
47
47
48
48
49 } // namespace
49 } // namespace
50
50
51 struct VariableModel::VariableModelPrivate {
51 struct VariableModel::VariableModelPrivate {
52 /// Variables created in SciQlop
52 /// Variables created in SciQlop
53 std::vector<std::shared_ptr<Variable> > m_Variables;
53 std::vector<std::shared_ptr<Variable> > m_Variables;
54 std::unordered_map<std::shared_ptr<Variable>, double> m_VariableToProgress;
54 std::unordered_map<std::shared_ptr<Variable>, double> m_VariableToProgress;
55
55
56 /// Return the row index of the variable. -1 if it's not found
56 /// Return the row index of the variable. -1 if it's not found
57 int indexOfVariable(Variable *variable) const noexcept;
57 int indexOfVariable(Variable *variable) const noexcept;
58 };
58 };
59
59
60 VariableModel::VariableModel(QObject *parent)
60 VariableModel::VariableModel(QObject *parent)
61 : QAbstractTableModel{parent}, impl{spimpl::make_unique_impl<VariableModelPrivate>()}
61 : QAbstractTableModel{parent}, impl{spimpl::make_unique_impl<VariableModelPrivate>()}
62 {
62 {
63 }
63 }
64
64
65 std::shared_ptr<Variable> VariableModel::createVariable(const QString &name,
65 std::shared_ptr<Variable> VariableModel::createVariable(const QString &name,
66 const SqpRange &dateTime,
66 const SqpRange &dateTime,
67 const QVariantHash &metadata) noexcept
67 const QVariantHash &metadata) noexcept
68 {
68 {
69 auto insertIndex = rowCount();
69 auto insertIndex = rowCount();
70 beginInsertRows({}, insertIndex, insertIndex);
70 beginInsertRows({}, insertIndex, insertIndex);
71
71
72 auto variable = std::make_shared<Variable>(name, dateTime, metadata);
72 auto variable = std::make_shared<Variable>(name, dateTime, metadata);
73
73
74 impl->m_Variables.push_back(variable);
74 impl->m_Variables.push_back(variable);
75 connect(variable.get(), &Variable::updated, this, &VariableModel::onVariableUpdated);
75 connect(variable.get(), &Variable::updated, this, &VariableModel::onVariableUpdated);
76
76
77 endInsertRows();
77 endInsertRows();
78
78
79 return variable;
79 return variable;
80 }
80 }
81
81
82 void VariableModel::deleteVariable(std::shared_ptr<Variable> variable) noexcept
82 void VariableModel::deleteVariable(std::shared_ptr<Variable> variable) noexcept
83 {
83 {
84 if (!variable) {
84 if (!variable) {
85 qCCritical(LOG_Variable()) << "Can't delete a null variable from the model";
85 qCCritical(LOG_Variable()) << "Can't delete a null variable from the model";
86 return;
86 return;
87 }
87 }
88
88
89 // Finds variable in the model
89 // Finds variable in the model
90 auto begin = impl->m_Variables.cbegin();
90 auto begin = impl->m_Variables.cbegin();
91 auto end = impl->m_Variables.cend();
91 auto end = impl->m_Variables.cend();
92 auto it = std::find(begin, end, variable);
92 auto it = std::find(begin, end, variable);
93 if (it != end) {
93 if (it != end) {
94 auto removeIndex = std::distance(begin, it);
94 auto removeIndex = std::distance(begin, it);
95
95
96 // Deletes variable
96 // Deletes variable
97 beginRemoveRows({}, removeIndex, removeIndex);
97 beginRemoveRows({}, removeIndex, removeIndex);
98 impl->m_Variables.erase(it);
98 impl->m_Variables.erase(it);
99 endRemoveRows();
99 endRemoveRows();
100 }
100 }
101 else {
101 else {
102 qCritical(LOG_VariableModel())
102 qCritical(LOG_VariableModel())
103 << tr("Can't delete variable %1 from the model: the variable is not in the model")
103 << tr("Can't delete variable %1 from the model: the variable is not in the model")
104 .arg(variable->name());
104 .arg(variable->name());
105 }
105 }
106
106
107 // Removes variable from progress map
107 // Removes variable from progress map
108 impl->m_VariableToProgress.erase(variable);
108 impl->m_VariableToProgress.erase(variable);
109 }
109 }
110
110
111
111
112 std::shared_ptr<Variable> VariableModel::variable(int index) const
112 std::shared_ptr<Variable> VariableModel::variable(int index) const
113 {
113 {
114 return (index >= 0 && index < impl->m_Variables.size()) ? impl->m_Variables[index] : nullptr;
114 return (index >= 0 && index < impl->m_Variables.size()) ? impl->m_Variables[index] : nullptr;
115 }
115 }
116
116
117 void VariableModel::setDataProgress(std::shared_ptr<Variable> variable, double progress)
117 void VariableModel::setDataProgress(std::shared_ptr<Variable> variable, double progress)
118 {
118 {
119 if (progress > 0.0) {
119 if (progress > 0.0) {
120 impl->m_VariableToProgress[variable] = progress;
120 impl->m_VariableToProgress[variable] = progress;
121 }
121 }
122 else {
122 else {
123 impl->m_VariableToProgress.erase(variable);
123 impl->m_VariableToProgress.erase(variable);
124 }
124 }
125 auto modelIndex = createIndex(impl->indexOfVariable(variable.get()), NAME_COLUMN);
125 auto modelIndex = createIndex(impl->indexOfVariable(variable.get()), NAME_COLUMN);
126
126
127 emit dataChanged(modelIndex, modelIndex);
127 emit dataChanged(modelIndex, modelIndex);
128 }
128 }
129
129
130 int VariableModel::columnCount(const QModelIndex &parent) const
130 int VariableModel::columnCount(const QModelIndex &parent) const
131 {
131 {
132 Q_UNUSED(parent);
132 Q_UNUSED(parent);
133
133
134 return NB_COLUMNS;
134 return NB_COLUMNS;
135 }
135 }
136
136
137 int VariableModel::rowCount(const QModelIndex &parent) const
137 int VariableModel::rowCount(const QModelIndex &parent) const
138 {
138 {
139 Q_UNUSED(parent);
139 Q_UNUSED(parent);
140
140
141 return impl->m_Variables.size();
141 return impl->m_Variables.size();
142 }
142 }
143
143
144 QVariant VariableModel::data(const QModelIndex &index, int role) const
144 QVariant VariableModel::data(const QModelIndex &index, int role) const
145 {
145 {
146 if (!index.isValid()) {
146 if (!index.isValid()) {
147 return QVariant{};
147 return QVariant{};
148 }
148 }
149
149
150 if (index.row() < 0 || index.row() >= rowCount()) {
150 if (index.row() < 0 || index.row() >= rowCount()) {
151 return QVariant{};
151 return QVariant{};
152 }
152 }
153
153
154 if (role == Qt::DisplayRole) {
154 if (role == Qt::DisplayRole) {
155 if (auto variable = impl->m_Variables.at(index.row()).get()) {
155 if (auto variable = impl->m_Variables.at(index.row()).get()) {
156 /// Lambda function that builds the variant to return for a time value
156 /// Lambda function that builds the variant to return for a time value
157 /// @param getValueFun function used to get for a data series the iterator on the entry
157 /// @param getValueFun function used to get for a data series the iterator on the entry
158 /// that contains the time value to display
158 /// that contains the time value to display
159 auto dateTimeVariant = [variable](const auto &getValueFun) {
159 auto dateTimeVariant = [variable](const auto &getValueFun) {
160 if (auto dataSeries = variable->dataSeries()) {
160 if (auto dataSeries = variable->dataSeries()) {
161 dataSeries->lockRead();
161 auto it = getValueFun(*dataSeries);
162 auto it = getValueFun(*dataSeries);
162 return (it != dataSeries->cend())
163 auto resVariant = (it != dataSeries->cend())
163 ? DateUtils::dateTime(it->x()).toString(DATETIME_FORMAT)
164 ? DateUtils::dateTime(it->x()).toString(DATETIME_FORMAT)
164 : QVariant{};
165 : QVariant{};
166 dataSeries->unlock();
167 return resVariant;
165 }
168 }
166 else {
169 else {
167 return QVariant{};
170 return QVariant{};
168 }
171 }
169 };
172 };
170
173
171 switch (index.column()) {
174 switch (index.column()) {
172 case NAME_COLUMN:
175 case NAME_COLUMN:
173 return variable->name();
176 return variable->name();
174 case TSTART_COLUMN:
177 case TSTART_COLUMN:
175 // Shows the min value of the data series above the range tstart
178 // Shows the min value of the data series above the range tstart
176 return dateTimeVariant([min = variable->range().m_TStart](
179 return dateTimeVariant([min = variable->range().m_TStart](
177 const auto &dataSeries) { return dataSeries.minXAxisData(min); });
180 const auto &dataSeries) { return dataSeries.minXAxisData(min); });
178 case TEND_COLUMN:
181 case TEND_COLUMN:
179 // Shows the max value of the data series under the range tend
182 // Shows the max value of the data series under the range tend
180 return dateTimeVariant([max = variable->range().m_TEnd](
183 return dateTimeVariant([max = variable->range().m_TEnd](
181 const auto &dataSeries) { return dataSeries.maxXAxisData(max); });
184 const auto &dataSeries) { return dataSeries.maxXAxisData(max); });
182 case UNIT_COLUMN:
185 case UNIT_COLUMN:
183 return variable->metadata().value(QStringLiteral("units"));
186 return variable->metadata().value(QStringLiteral("units"));
184 case MISSION_COLUMN:
187 case MISSION_COLUMN:
185 return variable->metadata().value(QStringLiteral("mission"));
188 return variable->metadata().value(QStringLiteral("mission"));
186 case PLUGIN_COLUMN:
189 case PLUGIN_COLUMN:
187 return variable->metadata().value(QStringLiteral("plugin"));
190 return variable->metadata().value(QStringLiteral("plugin"));
188 default:
191 default:
189 // No action
192 // No action
190 break;
193 break;
191 }
194 }
192
195
193 qWarning(LOG_VariableModel())
196 qWarning(LOG_VariableModel())
194 << tr("Can't get data (unknown column %1)").arg(index.column());
197 << tr("Can't get data (unknown column %1)").arg(index.column());
195 }
198 }
196 else {
199 else {
197 qWarning(LOG_VariableModel()) << tr("Can't get data (no variable)");
200 qWarning(LOG_VariableModel()) << tr("Can't get data (no variable)");
198 }
201 }
199 }
202 }
200 else if (role == VariableRoles::ProgressRole) {
203 else if (role == VariableRoles::ProgressRole) {
201 if (auto variable = impl->m_Variables.at(index.row())) {
204 if (auto variable = impl->m_Variables.at(index.row())) {
202
205
203 auto it = impl->m_VariableToProgress.find(variable);
206 auto it = impl->m_VariableToProgress.find(variable);
204 if (it != impl->m_VariableToProgress.cend()) {
207 if (it != impl->m_VariableToProgress.cend()) {
205 return it->second;
208 return it->second;
206 }
209 }
207 }
210 }
208 }
211 }
209
212
210 return QVariant{};
213 return QVariant{};
211 }
214 }
212
215
213 QVariant VariableModel::headerData(int section, Qt::Orientation orientation, int role) const
216 QVariant VariableModel::headerData(int section, Qt::Orientation orientation, int role) const
214 {
217 {
215 if (role != Qt::DisplayRole && role != Qt::SizeHintRole) {
218 if (role != Qt::DisplayRole && role != Qt::SizeHintRole) {
216 return QVariant{};
219 return QVariant{};
217 }
220 }
218
221
219 if (orientation == Qt::Horizontal) {
222 if (orientation == Qt::Horizontal) {
220 auto propertiesIt = COLUMN_PROPERTIES.find(section);
223 auto propertiesIt = COLUMN_PROPERTIES.find(section);
221 if (propertiesIt != COLUMN_PROPERTIES.cend()) {
224 if (propertiesIt != COLUMN_PROPERTIES.cend()) {
222 // Role is either DisplayRole or SizeHintRole
225 // Role is either DisplayRole or SizeHintRole
223 return (role == Qt::DisplayRole)
226 return (role == Qt::DisplayRole)
224 ? QVariant{propertiesIt->m_Name}
227 ? QVariant{propertiesIt->m_Name}
225 : QVariant{QSize{propertiesIt->m_Width, propertiesIt->m_Height}};
228 : QVariant{QSize{propertiesIt->m_Width, propertiesIt->m_Height}};
226 }
229 }
227 else {
230 else {
228 qWarning(LOG_VariableModel())
231 qWarning(LOG_VariableModel())
229 << tr("Can't get header data (unknown column %1)").arg(section);
232 << tr("Can't get header data (unknown column %1)").arg(section);
230 }
233 }
231 }
234 }
232
235
233 return QVariant{};
236 return QVariant{};
234 }
237 }
235
238
236 void VariableModel::abortProgress(const QModelIndex &index)
239 void VariableModel::abortProgress(const QModelIndex &index)
237 {
240 {
238 if (auto variable = impl->m_Variables.at(index.row())) {
241 if (auto variable = impl->m_Variables.at(index.row())) {
239 emit abortProgessRequested(variable);
242 emit abortProgessRequested(variable);
240 }
243 }
241 }
244 }
242
245
243 void VariableModel::onVariableUpdated() noexcept
246 void VariableModel::onVariableUpdated() noexcept
244 {
247 {
245 // Finds variable that has been updated in the model
248 // Finds variable that has been updated in the model
246 if (auto updatedVariable = dynamic_cast<Variable *>(sender())) {
249 if (auto updatedVariable = dynamic_cast<Variable *>(sender())) {
247 auto updatedVariableIndex = impl->indexOfVariable(updatedVariable);
250 auto updatedVariableIndex = impl->indexOfVariable(updatedVariable);
248
251
249 if (updatedVariableIndex > -1) {
252 if (updatedVariableIndex > -1) {
250 emit dataChanged(createIndex(updatedVariableIndex, 0),
253 emit dataChanged(createIndex(updatedVariableIndex, 0),
251 createIndex(updatedVariableIndex, columnCount() - 1));
254 createIndex(updatedVariableIndex, columnCount() - 1));
252 }
255 }
253 }
256 }
254 }
257 }
255
258
256 int VariableModel::VariableModelPrivate::indexOfVariable(Variable *variable) const noexcept
259 int VariableModel::VariableModelPrivate::indexOfVariable(Variable *variable) const noexcept
257 {
260 {
258 auto begin = std::cbegin(m_Variables);
261 auto begin = std::cbegin(m_Variables);
259 auto end = std::cend(m_Variables);
262 auto end = std::cend(m_Variables);
260 auto it
263 auto it
261 = std::find_if(begin, end, [variable](const auto &var) { return var.get() == variable; });
264 = std::find_if(begin, end, [variable](const auto &var) { return var.get() == variable; });
262
265
263 if (it != end) {
266 if (it != end) {
264 // Gets the index of the variable in the model: we assume here that views have the same
267 // Gets the index of the variable in the model: we assume here that views have the same
265 // order as the model
268 // order as the model
266 return std::distance(begin, it);
269 return std::distance(begin, it);
267 }
270 }
268 else {
271 else {
269 return -1;
272 return -1;
270 }
273 }
271 }
274 }
@@ -1,243 +1,247
1 #include "Visualization/VisualizationGraphHelper.h"
1 #include "Visualization/VisualizationGraphHelper.h"
2 #include "Visualization/qcustomplot.h"
2 #include "Visualization/qcustomplot.h"
3
3
4 #include <Common/ColorUtils.h>
4 #include <Common/ColorUtils.h>
5
5
6 #include <Data/ScalarSeries.h>
6 #include <Data/ScalarSeries.h>
7 #include <Data/VectorSeries.h>
7 #include <Data/VectorSeries.h>
8
8
9 #include <Variable/Variable.h>
9 #include <Variable/Variable.h>
10
10
11 Q_LOGGING_CATEGORY(LOG_VisualizationGraphHelper, "VisualizationGraphHelper")
11 Q_LOGGING_CATEGORY(LOG_VisualizationGraphHelper, "VisualizationGraphHelper")
12
12
13 namespace {
13 namespace {
14
14
15 class SqpDataContainer : public QCPGraphDataContainer {
15 class SqpDataContainer : public QCPGraphDataContainer {
16 public:
16 public:
17 void appendGraphData(const QCPGraphData &data) { mData.append(data); }
17 void appendGraphData(const QCPGraphData &data) { mData.append(data); }
18 };
18 };
19
19
20
20
21 /// Format for datetimes on a axis
21 /// Format for datetimes on a axis
22 const auto DATETIME_TICKER_FORMAT = QStringLiteral("yyyy/MM/dd \nhh:mm:ss");
22 const auto DATETIME_TICKER_FORMAT = QStringLiteral("yyyy/MM/dd \nhh:mm:ss");
23
23
24 /// Generates the appropriate ticker for an axis, depending on whether the axis displays time or
24 /// Generates the appropriate ticker for an axis, depending on whether the axis displays time or
25 /// non-time data
25 /// non-time data
26 QSharedPointer<QCPAxisTicker> axisTicker(bool isTimeAxis)
26 QSharedPointer<QCPAxisTicker> axisTicker(bool isTimeAxis)
27 {
27 {
28 if (isTimeAxis) {
28 if (isTimeAxis) {
29 auto dateTicker = QSharedPointer<QCPAxisTickerDateTime>::create();
29 auto dateTicker = QSharedPointer<QCPAxisTickerDateTime>::create();
30 dateTicker->setDateTimeFormat(DATETIME_TICKER_FORMAT);
30 dateTicker->setDateTimeFormat(DATETIME_TICKER_FORMAT);
31 dateTicker->setDateTimeSpec(Qt::UTC);
31 dateTicker->setDateTimeSpec(Qt::UTC);
32
32
33 return dateTicker;
33 return dateTicker;
34 }
34 }
35 else {
35 else {
36 // default ticker
36 // default ticker
37 return QSharedPointer<QCPAxisTicker>::create();
37 return QSharedPointer<QCPAxisTicker>::create();
38 }
38 }
39 }
39 }
40
40
41 /// Sets axes properties according to the properties of a data series
41 /// Sets axes properties according to the properties of a data series. Not thread safe
42 template <int Dim>
42 template <int Dim>
43 void setAxesProperties(const DataSeries<Dim> &dataSeries, QCustomPlot &plot) noexcept
43 void setAxesProperties(const DataSeries<Dim> &dataSeries, QCustomPlot &plot) noexcept
44 {
44 {
45 /// @todo : for the moment, no control is performed on the axes: the units and the tickers
45 /// @todo : for the moment, no control is performed on the axes: the units and the tickers
46 /// are fixed for the default x-axis and y-axis of the plot, and according to the new graph
46 /// are fixed for the default x-axis and y-axis of the plot, and according to the new graph
47 auto setAxisProperties = [](auto axis, const auto &unit) {
47 auto setAxisProperties = [](auto axis, const auto &unit) {
48 // label (unit name)
48 // label (unit name)
49 axis->setLabel(unit.m_Name);
49 axis->setLabel(unit.m_Name);
50
50
51 // ticker (depending on the type of unit)
51 // ticker (depending on the type of unit)
52 axis->setTicker(axisTicker(unit.m_TimeUnit));
52 axis->setTicker(axisTicker(unit.m_TimeUnit));
53 };
53 };
54 setAxisProperties(plot.xAxis, dataSeries.xAxisUnit());
54 setAxisProperties(plot.xAxis, dataSeries.xAxisUnit());
55 setAxisProperties(plot.yAxis, dataSeries.valuesUnit());
55 setAxisProperties(plot.yAxis, dataSeries.valuesUnit());
56 }
56 }
57
57
58 /**
58 /**
59 * Struct used to create plottables, depending on the type of the data series from which to create
59 * Struct used to create plottables, depending on the type of the data series from which to create
60 * them
60 * them
61 * @tparam T the data series' type
61 * @tparam T the data series' type
62 * @remarks Default implementation can't create plottables
62 * @remarks Default implementation can't create plottables
63 */
63 */
64 template <typename T, typename Enabled = void>
64 template <typename T, typename Enabled = void>
65 struct PlottablesCreator {
65 struct PlottablesCreator {
66 static PlottablesMap createPlottables(T &, QCustomPlot &)
66 static PlottablesMap createPlottables(T &, QCustomPlot &)
67 {
67 {
68 qCCritical(LOG_DataSeries())
68 qCCritical(LOG_DataSeries())
69 << QObject::tr("Can't create plottables: unmanaged data series type");
69 << QObject::tr("Can't create plottables: unmanaged data series type");
70 return {};
70 return {};
71 }
71 }
72 };
72 };
73
73
74 /**
74 /**
75 * Specialization of PlottablesCreator for scalars and vectors
75 * Specialization of PlottablesCreator for scalars and vectors
76 * @sa ScalarSeries
76 * @sa ScalarSeries
77 * @sa VectorSeries
77 * @sa VectorSeries
78 */
78 */
79 template <typename T>
79 template <typename T>
80 struct PlottablesCreator<T,
80 struct PlottablesCreator<T,
81 typename std::enable_if_t<std::is_base_of<ScalarSeries, T>::value
81 typename std::enable_if_t<std::is_base_of<ScalarSeries, T>::value
82 or std::is_base_of<VectorSeries, T>::value> > {
82 or std::is_base_of<VectorSeries, T>::value> > {
83 static PlottablesMap createPlottables(T &dataSeries, QCustomPlot &plot)
83 static PlottablesMap createPlottables(T &dataSeries, QCustomPlot &plot)
84 {
84 {
85 PlottablesMap result{};
85 PlottablesMap result{};
86
86
87 // Gets the number of components of the data series
87 // Gets the number of components of the data series
88 dataSeries.lockRead();
88 auto componentCount = dataSeries.valuesData()->componentCount();
89 auto componentCount = dataSeries.valuesData()->componentCount();
90 dataSeries.unlock();
89
91
90 auto colors = ColorUtils::colors(Qt::blue, Qt::red, componentCount);
92 auto colors = ColorUtils::colors(Qt::blue, Qt::red, componentCount);
91
93
92 // For each component of the data series, creates a QCPGraph to add to the plot
94 // For each component of the data series, creates a QCPGraph to add to the plot
93 for (auto i = 0; i < componentCount; ++i) {
95 for (auto i = 0; i < componentCount; ++i) {
94 auto graph = plot.addGraph();
96 auto graph = plot.addGraph();
95 graph->setPen(QPen{colors.at(i)});
97 graph->setPen(QPen{colors.at(i)});
96
98
97 result.insert({i, graph});
99 result.insert({i, graph});
98 }
100 }
99
101
100 // Axes properties
102 // Axes properties
103 dataSeries.lockRead();
101 setAxesProperties(dataSeries, plot);
104 setAxesProperties(dataSeries, plot);
105 dataSeries.unlock();
102
106
103 plot.replot();
107 plot.replot();
104
108
105 return result;
109 return result;
106 }
110 }
107 };
111 };
108
112
109 /**
113 /**
110 * Struct used to update plottables, depending on the type of the data series from which to update
114 * Struct used to update plottables, depending on the type of the data series from which to update
111 * them
115 * them
112 * @tparam T the data series' type
116 * @tparam T the data series' type
113 * @remarks Default implementation can't update plottables
117 * @remarks Default implementation can't update plottables
114 */
118 */
115 template <typename T, typename Enabled = void>
119 template <typename T, typename Enabled = void>
116 struct PlottablesUpdater {
120 struct PlottablesUpdater {
117 static void updatePlottables(T &, PlottablesMap &, const SqpRange &, bool)
121 static void updatePlottables(T &, PlottablesMap &, const SqpRange &, bool)
118 {
122 {
119 qCCritical(LOG_DataSeries())
123 qCCritical(LOG_DataSeries())
120 << QObject::tr("Can't update plottables: unmanaged data series type");
124 << QObject::tr("Can't update plottables: unmanaged data series type");
121 }
125 }
122 };
126 };
123
127
124 /**
128 /**
125 * Specialization of PlottablesUpdater for scalars and vectors
129 * Specialization of PlottablesUpdater for scalars and vectors
126 * @sa ScalarSeries
130 * @sa ScalarSeries
127 * @sa VectorSeries
131 * @sa VectorSeries
128 */
132 */
129 template <typename T>
133 template <typename T>
130 struct PlottablesUpdater<T,
134 struct PlottablesUpdater<T,
131 typename std::enable_if_t<std::is_base_of<ScalarSeries, T>::value
135 typename std::enable_if_t<std::is_base_of<ScalarSeries, T>::value
132 or std::is_base_of<VectorSeries, T>::value> > {
136 or std::is_base_of<VectorSeries, T>::value> > {
133 static void updatePlottables(T &dataSeries, PlottablesMap &plottables, const SqpRange &range,
137 static void updatePlottables(T &dataSeries, PlottablesMap &plottables, const SqpRange &range,
134 bool rescaleAxes)
138 bool rescaleAxes)
135 {
139 {
136 dataSeries.lockRead();
137
140
138 // For each plottable to update, resets its data
141 // For each plottable to update, resets its data
139 std::map<int, QSharedPointer<SqpDataContainer> > dataContainers{};
142 std::map<int, QSharedPointer<SqpDataContainer> > dataContainers{};
140 for (const auto &plottable : plottables) {
143 for (const auto &plottable : plottables) {
141 if (auto graph = dynamic_cast<QCPGraph *>(plottable.second)) {
144 if (auto graph = dynamic_cast<QCPGraph *>(plottable.second)) {
142 auto dataContainer = QSharedPointer<SqpDataContainer>::create();
145 auto dataContainer = QSharedPointer<SqpDataContainer>::create();
143 graph->setData(dataContainer);
146 graph->setData(dataContainer);
144
147
145 dataContainers.insert({plottable.first, dataContainer});
148 dataContainers.insert({plottable.first, dataContainer});
146 }
149 }
147 }
150 }
151 dataSeries.lockRead();
148
152
149 // - Gets the data of the series included in the current range
153 // - Gets the data of the series included in the current range
150 // - Updates each plottable by adding, for each data item, a point that takes x-axis data
154 // - Updates each plottable by adding, for each data item, a point that takes x-axis data
151 // and value data. The correct value is retrieved according to the index of the component
155 // and value data. The correct value is retrieved according to the index of the component
152 auto subDataIts = dataSeries.xAxisRange(range.m_TStart, range.m_TEnd);
156 auto subDataIts = dataSeries.xAxisRange(range.m_TStart, range.m_TEnd);
153 for (auto it = subDataIts.first; it != subDataIts.second; ++it) {
157 for (auto it = subDataIts.first; it != subDataIts.second; ++it) {
154 for (const auto &dataContainer : dataContainers) {
158 for (const auto &dataContainer : dataContainers) {
155 auto componentIndex = dataContainer.first;
159 auto componentIndex = dataContainer.first;
156 dataContainer.second->appendGraphData(
160 dataContainer.second->appendGraphData(
157 QCPGraphData(it->x(), it->value(componentIndex)));
161 QCPGraphData(it->x(), it->value(componentIndex)));
158 }
162 }
159 }
163 }
160
164
161 dataSeries.unlock();
165 dataSeries.unlock();
162
166
163 if (!plottables.empty()) {
167 if (!plottables.empty()) {
164 auto plot = plottables.begin()->second->parentPlot();
168 auto plot = plottables.begin()->second->parentPlot();
165
169
166 if (rescaleAxes) {
170 if (rescaleAxes) {
167 plot->rescaleAxes();
171 plot->rescaleAxes();
168 }
172 }
169
173
170 plot->replot();
174 plot->replot();
171 }
175 }
172 }
176 }
173 };
177 };
174
178
175 /**
179 /**
176 * Helper used to create/update plottables
180 * Helper used to create/update plottables
177 */
181 */
178 struct IPlottablesHelper {
182 struct IPlottablesHelper {
179 virtual ~IPlottablesHelper() noexcept = default;
183 virtual ~IPlottablesHelper() noexcept = default;
180 virtual PlottablesMap create(QCustomPlot &plot) const = 0;
184 virtual PlottablesMap create(QCustomPlot &plot) const = 0;
181 virtual void update(PlottablesMap &plottables, const SqpRange &range,
185 virtual void update(PlottablesMap &plottables, const SqpRange &range,
182 bool rescaleAxes = false) const = 0;
186 bool rescaleAxes = false) const = 0;
183 };
187 };
184
188
185 /**
189 /**
186 * Default implementation of IPlottablesHelper, which takes data series to create/update plottables
190 * Default implementation of IPlottablesHelper, which takes data series to create/update plottables
187 * @tparam T the data series' type
191 * @tparam T the data series' type
188 */
192 */
189 template <typename T>
193 template <typename T>
190 struct PlottablesHelper : public IPlottablesHelper {
194 struct PlottablesHelper : public IPlottablesHelper {
191 explicit PlottablesHelper(T &dataSeries) : m_DataSeries{dataSeries} {}
195 explicit PlottablesHelper(T &dataSeries) : m_DataSeries{dataSeries} {}
192
196
193 PlottablesMap create(QCustomPlot &plot) const override
197 PlottablesMap create(QCustomPlot &plot) const override
194 {
198 {
195 return PlottablesCreator<T>::createPlottables(m_DataSeries, plot);
199 return PlottablesCreator<T>::createPlottables(m_DataSeries, plot);
196 }
200 }
197
201
198 void update(PlottablesMap &plottables, const SqpRange &range, bool rescaleAxes) const override
202 void update(PlottablesMap &plottables, const SqpRange &range, bool rescaleAxes) const override
199 {
203 {
200 PlottablesUpdater<T>::updatePlottables(m_DataSeries, plottables, range, rescaleAxes);
204 PlottablesUpdater<T>::updatePlottables(m_DataSeries, plottables, range, rescaleAxes);
201 }
205 }
202
206
203 T &m_DataSeries;
207 T &m_DataSeries;
204 };
208 };
205
209
206 /// Creates IPlottablesHelper according to a data series
210 /// Creates IPlottablesHelper according to a data series
207 std::unique_ptr<IPlottablesHelper> createHelper(std::shared_ptr<IDataSeries> dataSeries) noexcept
211 std::unique_ptr<IPlottablesHelper> createHelper(std::shared_ptr<IDataSeries> dataSeries) noexcept
208 {
212 {
209 if (auto scalarSeries = std::dynamic_pointer_cast<ScalarSeries>(dataSeries)) {
213 if (auto scalarSeries = std::dynamic_pointer_cast<ScalarSeries>(dataSeries)) {
210 return std::make_unique<PlottablesHelper<ScalarSeries> >(*scalarSeries);
214 return std::make_unique<PlottablesHelper<ScalarSeries> >(*scalarSeries);
211 }
215 }
212 else if (auto vectorSeries = std::dynamic_pointer_cast<VectorSeries>(dataSeries)) {
216 else if (auto vectorSeries = std::dynamic_pointer_cast<VectorSeries>(dataSeries)) {
213 return std::make_unique<PlottablesHelper<VectorSeries> >(*vectorSeries);
217 return std::make_unique<PlottablesHelper<VectorSeries> >(*vectorSeries);
214 }
218 }
215 else {
219 else {
216 return std::make_unique<PlottablesHelper<IDataSeries> >(*dataSeries);
220 return std::make_unique<PlottablesHelper<IDataSeries> >(*dataSeries);
217 }
221 }
218 }
222 }
219
223
220 } // namespace
224 } // namespace
221
225
222 PlottablesMap VisualizationGraphHelper::create(std::shared_ptr<Variable> variable,
226 PlottablesMap VisualizationGraphHelper::create(std::shared_ptr<Variable> variable,
223 QCustomPlot &plot) noexcept
227 QCustomPlot &plot) noexcept
224 {
228 {
225 if (variable) {
229 if (variable) {
226 auto helper = createHelper(variable->dataSeries());
230 auto helper = createHelper(variable->dataSeries());
227 auto plottables = helper->create(plot);
231 auto plottables = helper->create(plot);
228 return plottables;
232 return plottables;
229 }
233 }
230 else {
234 else {
231 qCDebug(LOG_VisualizationGraphHelper())
235 qCDebug(LOG_VisualizationGraphHelper())
232 << QObject::tr("Can't create graph plottables : the variable is null");
236 << QObject::tr("Can't create graph plottables : the variable is null");
233 return PlottablesMap{};
237 return PlottablesMap{};
234 }
238 }
235 }
239 }
236
240
237 void VisualizationGraphHelper::updateData(PlottablesMap &plottables,
241 void VisualizationGraphHelper::updateData(PlottablesMap &plottables,
238 std::shared_ptr<IDataSeries> dataSeries,
242 std::shared_ptr<IDataSeries> dataSeries,
239 const SqpRange &dateTime)
243 const SqpRange &dateTime)
240 {
244 {
241 auto helper = createHelper(dataSeries);
245 auto helper = createHelper(dataSeries);
242 helper->update(plottables, dateTime);
246 helper->update(plottables, dateTime);
243 }
247 }
@@ -1,257 +1,259
1 #include "Visualization/VisualizationZoneWidget.h"
1 #include "Visualization/VisualizationZoneWidget.h"
2
2
3
3
4 #include "Visualization/IVisualizationWidgetVisitor.h"
4 #include "Visualization/IVisualizationWidgetVisitor.h"
5 #include "Visualization/VisualizationGraphWidget.h"
5 #include "Visualization/VisualizationGraphWidget.h"
6 #include "ui_VisualizationZoneWidget.h"
6 #include "ui_VisualizationZoneWidget.h"
7
7
8 #include <Data/SqpRange.h>
8 #include <Data/SqpRange.h>
9 #include <Variable/Variable.h>
9 #include <Variable/Variable.h>
10 #include <Variable/VariableController.h>
10 #include <Variable/VariableController.h>
11
11
12 #include <QUuid>
12 #include <QUuid>
13 #include <SqpApplication.h>
13 #include <SqpApplication.h>
14 #include <cmath>
14 #include <cmath>
15
15
16 Q_LOGGING_CATEGORY(LOG_VisualizationZoneWidget, "VisualizationZoneWidget")
16 Q_LOGGING_CATEGORY(LOG_VisualizationZoneWidget, "VisualizationZoneWidget")
17
17
18 namespace {
18 namespace {
19
19
20 /// Minimum height for graph added in zones (in pixels)
20 /// Minimum height for graph added in zones (in pixels)
21 const auto GRAPH_MINIMUM_HEIGHT = 300;
21 const auto GRAPH_MINIMUM_HEIGHT = 300;
22
22
23 /// Generates a default name for a new graph, according to the number of graphs already displayed in
23 /// Generates a default name for a new graph, according to the number of graphs already displayed in
24 /// the zone
24 /// the zone
25 QString defaultGraphName(const QLayout &layout)
25 QString defaultGraphName(const QLayout &layout)
26 {
26 {
27 auto count = 0;
27 auto count = 0;
28 for (auto i = 0; i < layout.count(); ++i) {
28 for (auto i = 0; i < layout.count(); ++i) {
29 if (dynamic_cast<VisualizationGraphWidget *>(layout.itemAt(i)->widget())) {
29 if (dynamic_cast<VisualizationGraphWidget *>(layout.itemAt(i)->widget())) {
30 count++;
30 count++;
31 }
31 }
32 }
32 }
33
33
34 return QObject::tr("Graph %1").arg(count + 1);
34 return QObject::tr("Graph %1").arg(count + 1);
35 }
35 }
36
36
37 } // namespace
37 } // namespace
38
38
39 struct VisualizationZoneWidget::VisualizationZoneWidgetPrivate {
39 struct VisualizationZoneWidget::VisualizationZoneWidgetPrivate {
40
40
41 explicit VisualizationZoneWidgetPrivate() : m_SynchronisationGroupId{QUuid::createUuid()} {}
41 explicit VisualizationZoneWidgetPrivate() : m_SynchronisationGroupId{QUuid::createUuid()} {}
42 QUuid m_SynchronisationGroupId;
42 QUuid m_SynchronisationGroupId;
43 };
43 };
44
44
45 VisualizationZoneWidget::VisualizationZoneWidget(const QString &name, QWidget *parent)
45 VisualizationZoneWidget::VisualizationZoneWidget(const QString &name, QWidget *parent)
46 : QWidget{parent},
46 : QWidget{parent},
47 ui{new Ui::VisualizationZoneWidget},
47 ui{new Ui::VisualizationZoneWidget},
48 impl{spimpl::make_unique_impl<VisualizationZoneWidgetPrivate>()}
48 impl{spimpl::make_unique_impl<VisualizationZoneWidgetPrivate>()}
49 {
49 {
50 ui->setupUi(this);
50 ui->setupUi(this);
51
51
52 ui->zoneNameLabel->setText(name);
52 ui->zoneNameLabel->setText(name);
53
53
54 // 'Close' options : widget is deleted when closed
54 // 'Close' options : widget is deleted when closed
55 setAttribute(Qt::WA_DeleteOnClose);
55 setAttribute(Qt::WA_DeleteOnClose);
56 connect(ui->closeButton, &QToolButton::clicked, this, &VisualizationZoneWidget::close);
56 connect(ui->closeButton, &QToolButton::clicked, this, &VisualizationZoneWidget::close);
57 ui->closeButton->setIcon(sqpApp->style()->standardIcon(QStyle::SP_TitleBarCloseButton));
57 ui->closeButton->setIcon(sqpApp->style()->standardIcon(QStyle::SP_TitleBarCloseButton));
58
58
59 // Synchronisation id
59 // Synchronisation id
60 QMetaObject::invokeMethod(&sqpApp->variableController(), "onAddSynchronizationGroupId",
60 QMetaObject::invokeMethod(&sqpApp->variableController(), "onAddSynchronizationGroupId",
61 Qt::QueuedConnection, Q_ARG(QUuid, impl->m_SynchronisationGroupId));
61 Qt::QueuedConnection, Q_ARG(QUuid, impl->m_SynchronisationGroupId));
62 }
62 }
63
63
64 VisualizationZoneWidget::~VisualizationZoneWidget()
64 VisualizationZoneWidget::~VisualizationZoneWidget()
65 {
65 {
66 delete ui;
66 delete ui;
67 }
67 }
68
68
69 void VisualizationZoneWidget::addGraph(VisualizationGraphWidget *graphWidget)
69 void VisualizationZoneWidget::addGraph(VisualizationGraphWidget *graphWidget)
70 {
70 {
71 ui->visualizationZoneFrame->layout()->addWidget(graphWidget);
71 ui->visualizationZoneFrame->layout()->addWidget(graphWidget);
72 }
72 }
73
73
74 VisualizationGraphWidget *VisualizationZoneWidget::createGraph(std::shared_ptr<Variable> variable)
74 VisualizationGraphWidget *VisualizationZoneWidget::createGraph(std::shared_ptr<Variable> variable)
75 {
75 {
76 auto graphWidget = new VisualizationGraphWidget{
76 auto graphWidget = new VisualizationGraphWidget{
77 defaultGraphName(*ui->visualizationZoneFrame->layout()), this};
77 defaultGraphName(*ui->visualizationZoneFrame->layout()), this};
78
78
79
79
80 // Set graph properties
80 // Set graph properties
81 graphWidget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::MinimumExpanding);
81 graphWidget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::MinimumExpanding);
82 graphWidget->setMinimumHeight(GRAPH_MINIMUM_HEIGHT);
82 graphWidget->setMinimumHeight(GRAPH_MINIMUM_HEIGHT);
83
83
84
84
85 // Lambda to synchronize zone widget
85 // Lambda to synchronize zone widget
86 auto synchronizeZoneWidget = [this, graphWidget](const SqpRange &graphRange,
86 auto synchronizeZoneWidget = [this, graphWidget](const SqpRange &graphRange,
87 const SqpRange &oldGraphRange) {
87 const SqpRange &oldGraphRange) {
88
88
89 auto zoomType = VariableController::getZoomType(graphRange, oldGraphRange);
89 auto zoomType = VariableController::getZoomType(graphRange, oldGraphRange);
90 auto frameLayout = ui->visualizationZoneFrame->layout();
90 auto frameLayout = ui->visualizationZoneFrame->layout();
91 for (auto i = 0; i < frameLayout->count(); ++i) {
91 for (auto i = 0; i < frameLayout->count(); ++i) {
92 auto graphChild
92 auto graphChild
93 = dynamic_cast<VisualizationGraphWidget *>(frameLayout->itemAt(i)->widget());
93 = dynamic_cast<VisualizationGraphWidget *>(frameLayout->itemAt(i)->widget());
94 if (graphChild && (graphChild != graphWidget)) {
94 if (graphChild && (graphChild != graphWidget)) {
95
95
96 auto graphChildRange = graphChild->graphRange();
96 auto graphChildRange = graphChild->graphRange();
97 switch (zoomType) {
97 switch (zoomType) {
98 case AcquisitionZoomType::ZoomIn: {
98 case AcquisitionZoomType::ZoomIn: {
99 auto deltaLeft = graphRange.m_TStart - oldGraphRange.m_TStart;
99 auto deltaLeft = graphRange.m_TStart - oldGraphRange.m_TStart;
100 auto deltaRight = oldGraphRange.m_TEnd - graphRange.m_TEnd;
100 auto deltaRight = oldGraphRange.m_TEnd - graphRange.m_TEnd;
101 graphChildRange.m_TStart += deltaLeft;
101 graphChildRange.m_TStart += deltaLeft;
102 graphChildRange.m_TEnd -= deltaRight;
102 graphChildRange.m_TEnd -= deltaRight;
103 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: ZoomIn");
103 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: ZoomIn");
104 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: deltaLeft")
104 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: deltaLeft")
105 << deltaLeft;
105 << deltaLeft;
106 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: deltaRight")
106 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: deltaRight")
107 << deltaRight;
107 << deltaRight;
108 qCDebug(LOG_VisualizationZoneWidget())
108 qCDebug(LOG_VisualizationZoneWidget())
109 << tr("TORM: dt") << graphRange.m_TEnd - graphRange.m_TStart;
109 << tr("TORM: dt") << graphRange.m_TEnd - graphRange.m_TStart;
110
110
111 break;
111 break;
112 }
112 }
113
113
114 case AcquisitionZoomType::ZoomOut: {
114 case AcquisitionZoomType::ZoomOut: {
115 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: ZoomOut");
115 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: ZoomOut");
116 auto deltaLeft = oldGraphRange.m_TStart - graphRange.m_TStart;
116 auto deltaLeft = oldGraphRange.m_TStart - graphRange.m_TStart;
117 auto deltaRight = graphRange.m_TEnd - oldGraphRange.m_TEnd;
117 auto deltaRight = graphRange.m_TEnd - oldGraphRange.m_TEnd;
118 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: deltaLeft")
118 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: deltaLeft")
119 << deltaLeft;
119 << deltaLeft;
120 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: deltaRight")
120 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: deltaRight")
121 << deltaRight;
121 << deltaRight;
122 qCDebug(LOG_VisualizationZoneWidget())
122 qCDebug(LOG_VisualizationZoneWidget())
123 << tr("TORM: dt") << graphRange.m_TEnd - graphRange.m_TStart;
123 << tr("TORM: dt") << graphRange.m_TEnd - graphRange.m_TStart;
124 graphChildRange.m_TStart -= deltaLeft;
124 graphChildRange.m_TStart -= deltaLeft;
125 graphChildRange.m_TEnd += deltaRight;
125 graphChildRange.m_TEnd += deltaRight;
126 break;
126 break;
127 }
127 }
128 case AcquisitionZoomType::PanRight: {
128 case AcquisitionZoomType::PanRight: {
129 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: PanRight");
129 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: PanRight");
130 auto deltaRight = graphRange.m_TEnd - oldGraphRange.m_TEnd;
130 auto deltaRight = graphRange.m_TEnd - oldGraphRange.m_TEnd;
131 graphChildRange.m_TStart += deltaRight;
131 graphChildRange.m_TStart += deltaRight;
132 graphChildRange.m_TEnd += deltaRight;
132 graphChildRange.m_TEnd += deltaRight;
133 qCDebug(LOG_VisualizationZoneWidget())
133 qCDebug(LOG_VisualizationZoneWidget())
134 << tr("TORM: dt") << graphRange.m_TEnd - graphRange.m_TStart;
134 << tr("TORM: dt") << graphRange.m_TEnd - graphRange.m_TStart;
135 break;
135 break;
136 }
136 }
137 case AcquisitionZoomType::PanLeft: {
137 case AcquisitionZoomType::PanLeft: {
138 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: PanLeft");
138 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: PanLeft");
139 auto deltaLeft = oldGraphRange.m_TStart - graphRange.m_TStart;
139 auto deltaLeft = oldGraphRange.m_TStart - graphRange.m_TStart;
140 graphChildRange.m_TStart -= deltaLeft;
140 graphChildRange.m_TStart -= deltaLeft;
141 graphChildRange.m_TEnd -= deltaLeft;
141 graphChildRange.m_TEnd -= deltaLeft;
142 break;
142 break;
143 }
143 }
144 case AcquisitionZoomType::Unknown: {
144 case AcquisitionZoomType::Unknown: {
145 qCDebug(LOG_VisualizationZoneWidget())
145 qCDebug(LOG_VisualizationZoneWidget())
146 << tr("Impossible to synchronize: zoom type unknown");
146 << tr("Impossible to synchronize: zoom type unknown");
147 break;
147 break;
148 }
148 }
149 default:
149 default:
150 qCCritical(LOG_VisualizationZoneWidget())
150 qCCritical(LOG_VisualizationZoneWidget())
151 << tr("Impossible to synchronize: zoom type not take into account");
151 << tr("Impossible to synchronize: zoom type not take into account");
152 // No action
152 // No action
153 break;
153 break;
154 }
154 }
155 graphChild->enableAcquisition(false);
155 graphChild->enableAcquisition(false);
156 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: Range before: ")
156 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: Range before: ")
157 << graphChild->graphRange();
157 << graphChild->graphRange();
158 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: Range after : ")
158 qCDebug(LOG_VisualizationZoneWidget()) << tr("TORM: Range after : ")
159 << graphChildRange;
159 << graphChildRange;
160 qCDebug(LOG_VisualizationZoneWidget())
160 qCDebug(LOG_VisualizationZoneWidget())
161 << tr("TORM: child dt") << graphChildRange.m_TEnd - graphChildRange.m_TStart;
161 << tr("TORM: child dt") << graphChildRange.m_TEnd - graphChildRange.m_TStart;
162 graphChild->setGraphRange(graphChildRange);
162 graphChild->setGraphRange(graphChildRange);
163 graphChild->enableAcquisition(true);
163 graphChild->enableAcquisition(true);
164 }
164 }
165 }
165 }
166 };
166 };
167
167
168 // connection for synchronization
168 // connection for synchronization
169 connect(graphWidget, &VisualizationGraphWidget::synchronize, synchronizeZoneWidget);
169 connect(graphWidget, &VisualizationGraphWidget::synchronize, synchronizeZoneWidget);
170 connect(graphWidget, &VisualizationGraphWidget::variableAdded, this,
170 connect(graphWidget, &VisualizationGraphWidget::variableAdded, this,
171 &VisualizationZoneWidget::onVariableAdded);
171 &VisualizationZoneWidget::onVariableAdded);
172
172
173 auto range = SqpRange{};
173 auto range = SqpRange{};
174
174
175 // Apply visitor to graph children
175 // Apply visitor to graph children
176 auto layout = ui->visualizationZoneFrame->layout();
176 auto layout = ui->visualizationZoneFrame->layout();
177 if (layout->count() > 0) {
177 if (layout->count() > 0) {
178 // Case of a new graph in a existant zone
178 // Case of a new graph in a existant zone
179 if (auto visualizationGraphWidget
179 if (auto visualizationGraphWidget
180 = dynamic_cast<VisualizationGraphWidget *>(layout->itemAt(0)->widget())) {
180 = dynamic_cast<VisualizationGraphWidget *>(layout->itemAt(0)->widget())) {
181 range = visualizationGraphWidget->graphRange();
181 range = visualizationGraphWidget->graphRange();
182 }
182 }
183 }
183 }
184 else {
184 else {
185 // Case of a new graph as the first of the zone
185 // Case of a new graph as the first of the zone
186 range = variable->range();
186 range = variable->range();
187 }
187 }
188
188
189 this->addGraph(graphWidget);
189 this->addGraph(graphWidget);
190
190
191 graphWidget->addVariable(variable, range);
191 graphWidget->addVariable(variable, range);
192
192
193 // get y using variable range
193 // get y using variable range
194 if (auto dataSeries = variable->dataSeries()) {
194 if (auto dataSeries = variable->dataSeries()) {
195 auto valuesBounds = dataSeries->valuesBounds(range.m_TStart, range.m_TEnd);
195 dataSeries->lockRead();
196 auto valuesBounds = dataSeries->valuesBounds(variable->range().m_TStart, variable->range().m_TEnd);
196 auto end = dataSeries->cend();
197 auto end = dataSeries->cend();
197 if (valuesBounds.first != end && valuesBounds.second != end) {
198 if (valuesBounds.first != end && valuesBounds.second != end) {
198 auto rangeValue = [](const auto &value) { return std::isnan(value) ? 0. : value; };
199 auto rangeValue = [](const auto &value) { return std::isnan(value) ? 0. : value; };
199
200
200 auto minValue = rangeValue(valuesBounds.first->minValue());
201 auto minValue = rangeValue(valuesBounds.first->minValue());
201 auto maxValue = rangeValue(valuesBounds.second->maxValue());
202 auto maxValue = rangeValue(valuesBounds.second->maxValue());
202
203
203 graphWidget->setYRange(SqpRange{minValue, maxValue});
204 graphWidget->setYRange(SqpRange{minValue, maxValue});
204 }
205 }
206 dataSeries->unlock();
205 }
207 }
206
208
207 return graphWidget;
209 return graphWidget;
208 }
210 }
209
211
210 void VisualizationZoneWidget::accept(IVisualizationWidgetVisitor *visitor)
212 void VisualizationZoneWidget::accept(IVisualizationWidgetVisitor *visitor)
211 {
213 {
212 if (visitor) {
214 if (visitor) {
213 visitor->visitEnter(this);
215 visitor->visitEnter(this);
214
216
215 // Apply visitor to graph children
217 // Apply visitor to graph children
216 auto layout = ui->visualizationZoneFrame->layout();
218 auto layout = ui->visualizationZoneFrame->layout();
217 for (auto i = 0; i < layout->count(); ++i) {
219 for (auto i = 0; i < layout->count(); ++i) {
218 if (auto item = layout->itemAt(i)) {
220 if (auto item = layout->itemAt(i)) {
219 // Widgets different from graphs are not visited (no action)
221 // Widgets different from graphs are not visited (no action)
220 if (auto visualizationGraphWidget
222 if (auto visualizationGraphWidget
221 = dynamic_cast<VisualizationGraphWidget *>(item->widget())) {
223 = dynamic_cast<VisualizationGraphWidget *>(item->widget())) {
222 visualizationGraphWidget->accept(visitor);
224 visualizationGraphWidget->accept(visitor);
223 }
225 }
224 }
226 }
225 }
227 }
226
228
227 visitor->visitLeave(this);
229 visitor->visitLeave(this);
228 }
230 }
229 else {
231 else {
230 qCCritical(LOG_VisualizationZoneWidget()) << tr("Can't visit widget : the visitor is null");
232 qCCritical(LOG_VisualizationZoneWidget()) << tr("Can't visit widget : the visitor is null");
231 }
233 }
232 }
234 }
233
235
234 bool VisualizationZoneWidget::canDrop(const Variable &variable) const
236 bool VisualizationZoneWidget::canDrop(const Variable &variable) const
235 {
237 {
236 // A tab can always accomodate a variable
238 // A tab can always accomodate a variable
237 Q_UNUSED(variable);
239 Q_UNUSED(variable);
238 return true;
240 return true;
239 }
241 }
240
242
241 bool VisualizationZoneWidget::contains(const Variable &variable) const
243 bool VisualizationZoneWidget::contains(const Variable &variable) const
242 {
244 {
243 Q_UNUSED(variable);
245 Q_UNUSED(variable);
244 return false;
246 return false;
245 }
247 }
246
248
247 QString VisualizationZoneWidget::name() const
249 QString VisualizationZoneWidget::name() const
248 {
250 {
249 return ui->zoneNameLabel->text();
251 return ui->zoneNameLabel->text();
250 }
252 }
251
253
252 void VisualizationZoneWidget::onVariableAdded(std::shared_ptr<Variable> variable)
254 void VisualizationZoneWidget::onVariableAdded(std::shared_ptr<Variable> variable)
253 {
255 {
254 QMetaObject::invokeMethod(&sqpApp->variableController(), "onAddSynchronized",
256 QMetaObject::invokeMethod(&sqpApp->variableController(), "onAddSynchronized",
255 Qt::QueuedConnection, Q_ARG(std::shared_ptr<Variable>, variable),
257 Qt::QueuedConnection, Q_ARG(std::shared_ptr<Variable>, variable),
256 Q_ARG(QUuid, impl->m_SynchronisationGroupId));
258 Q_ARG(QUuid, impl->m_SynchronisationGroupId));
257 }
259 }
@@ -1,103 +1,103
1 #include "CosinusProvider.h"
1 #include "CosinusProvider.h"
2
2
3 #include <Data/DataProviderParameters.h>
3 #include <Data/DataProviderParameters.h>
4 #include <Data/ScalarSeries.h>
4 #include <Data/ScalarSeries.h>
5
5
6 #include <cmath>
6 #include <cmath>
7
7
8 #include <QFuture>
8 #include <QFuture>
9 #include <QThread>
9 #include <QThread>
10 #include <QtConcurrent/QtConcurrent>
10 #include <QtConcurrent/QtConcurrent>
11
11
12 Q_LOGGING_CATEGORY(LOG_CosinusProvider, "CosinusProvider")
12 Q_LOGGING_CATEGORY(LOG_CosinusProvider, "CosinusProvider")
13
13
14 std::shared_ptr<IDataSeries> CosinusProvider::retrieveData(QUuid acqIdentifier,
14 std::shared_ptr<IDataSeries> CosinusProvider::retrieveData(QUuid acqIdentifier,
15 const SqpRange &dataRangeRequested)
15 const SqpRange &dataRangeRequested)
16 {
16 {
17 // TODO: Add Mutex
17 // TODO: Add Mutex
18 auto dataIndex = 0;
18 auto dataIndex = 0;
19
19
20 // Gets the timerange from the parameters
20 // Gets the timerange from the parameters
21 double freq = 100.0;
21 double freq = 1.0;
22 double start = std::ceil(dataRangeRequested.m_TStart * freq); // 100 htz
22 double start = std::ceil(dataRangeRequested.m_TStart * freq); // 100 htz
23 double end = std::floor(dataRangeRequested.m_TEnd * freq); // 100 htz
23 double end = std::floor(dataRangeRequested.m_TEnd * freq); // 100 htz
24
24
25 // We assure that timerange is valid
25 // We assure that timerange is valid
26 if (end < start) {
26 if (end < start) {
27 std::swap(start, end);
27 std::swap(start, end);
28 }
28 }
29
29
30 // Generates scalar series containing cosinus values (one value per second)
30 // Generates scalar series containing cosinus values (one value per second)
31 auto dataCount = end - start;
31 auto dataCount = end - start;
32
32
33 auto xAxisData = QVector<double>{};
33 auto xAxisData = QVector<double>{};
34 xAxisData.resize(dataCount);
34 xAxisData.resize(dataCount);
35
35
36 auto valuesData = QVector<double>{};
36 auto valuesData = QVector<double>{};
37 valuesData.resize(dataCount);
37 valuesData.resize(dataCount);
38
38
39 int progress = 0;
39 int progress = 0;
40 auto progressEnd = dataCount;
40 auto progressEnd = dataCount;
41 for (auto time = start; time < end; ++time, ++dataIndex) {
41 for (auto time = start; time < end; ++time, ++dataIndex) {
42 auto it = m_VariableToEnableProvider.find(acqIdentifier);
42 auto it = m_VariableToEnableProvider.find(acqIdentifier);
43 if (it != m_VariableToEnableProvider.end() && it.value()) {
43 if (it != m_VariableToEnableProvider.end() && it.value()) {
44 const auto timeOnFreq = time / freq;
44 const auto timeOnFreq = time / freq;
45
45
46 xAxisData.replace(dataIndex, timeOnFreq);
46 xAxisData.replace(dataIndex, timeOnFreq);
47 valuesData.replace(dataIndex, std::cos(timeOnFreq));
47 valuesData.replace(dataIndex, std::cos(timeOnFreq));
48
48
49 // progression
49 // progression
50 int currentProgress = (time - start) * 100.0 / progressEnd;
50 int currentProgress = (time - start) * 100.0 / progressEnd;
51 if (currentProgress != progress) {
51 if (currentProgress != progress) {
52 progress = currentProgress;
52 progress = currentProgress;
53
53
54 emit dataProvidedProgress(acqIdentifier, progress);
54 emit dataProvidedProgress(acqIdentifier, progress);
55 }
55 }
56 }
56 }
57 else {
57 else {
58 if (!it.value()) {
58 if (!it.value()) {
59 qCDebug(LOG_CosinusProvider())
59 qCDebug(LOG_CosinusProvider())
60 << "CosinusProvider::retrieveData: ARRET De l'acquisition detectΓ©"
60 << "CosinusProvider::retrieveData: ARRET De l'acquisition detectΓ©"
61 << end - time;
61 << end - time;
62 }
62 }
63 }
63 }
64 }
64 }
65 emit dataProvidedProgress(acqIdentifier, 0.0);
65 emit dataProvidedProgress(acqIdentifier, 0.0);
66
66
67 return std::make_shared<ScalarSeries>(std::move(xAxisData), std::move(valuesData),
67 return std::make_shared<ScalarSeries>(std::move(xAxisData), std::move(valuesData),
68 Unit{QStringLiteral("t"), true}, Unit{});
68 Unit{QStringLiteral("t"), true}, Unit{});
69 }
69 }
70
70
71 void CosinusProvider::requestDataLoading(QUuid acqIdentifier,
71 void CosinusProvider::requestDataLoading(QUuid acqIdentifier,
72 const DataProviderParameters &parameters)
72 const DataProviderParameters &parameters)
73 {
73 {
74 // TODO: Add Mutex
74 // TODO: Add Mutex
75 m_VariableToEnableProvider[acqIdentifier] = true;
75 m_VariableToEnableProvider[acqIdentifier] = true;
76 qCDebug(LOG_CosinusProvider()) << "TORM: CosinusProvider::requestDataLoading"
76 qCDebug(LOG_CosinusProvider()) << "TORM: CosinusProvider::requestDataLoading"
77 << QThread::currentThread()->objectName();
77 << QThread::currentThread()->objectName();
78 // NOTE: Try to use multithread if possible
78 // NOTE: Try to use multithread if possible
79 const auto times = parameters.m_Times;
79 const auto times = parameters.m_Times;
80
80
81 for (const auto &dateTime : qAsConst(times)) {
81 for (const auto &dateTime : qAsConst(times)) {
82 if (m_VariableToEnableProvider[acqIdentifier]) {
82 if (m_VariableToEnableProvider[acqIdentifier]) {
83 auto scalarSeries = this->retrieveData(acqIdentifier, dateTime);
83 auto scalarSeries = this->retrieveData(acqIdentifier, dateTime);
84 qCDebug(LOG_CosinusProvider()) << "TORM: CosinusProvider::dataProvided";
84 qCDebug(LOG_CosinusProvider()) << "TORM: CosinusProvider::dataProvided";
85 emit dataProvided(acqIdentifier, scalarSeries, dateTime);
85 emit dataProvided(acqIdentifier, scalarSeries, dateTime);
86 }
86 }
87 }
87 }
88 }
88 }
89
89
90 void CosinusProvider::requestDataAborting(QUuid acqIdentifier)
90 void CosinusProvider::requestDataAborting(QUuid acqIdentifier)
91 {
91 {
92 // TODO: Add Mutex
92 // TODO: Add Mutex
93 qCDebug(LOG_CosinusProvider()) << "CosinusProvider::requestDataAborting" << acqIdentifier
93 qCDebug(LOG_CosinusProvider()) << "CosinusProvider::requestDataAborting" << acqIdentifier
94 << QThread::currentThread()->objectName();
94 << QThread::currentThread()->objectName();
95 auto it = m_VariableToEnableProvider.find(acqIdentifier);
95 auto it = m_VariableToEnableProvider.find(acqIdentifier);
96 if (it != m_VariableToEnableProvider.end()) {
96 if (it != m_VariableToEnableProvider.end()) {
97 it.value() = false;
97 it.value() = false;
98 }
98 }
99 else {
99 else {
100 qCWarning(LOG_CosinusProvider())
100 qCWarning(LOG_CosinusProvider())
101 << tr("Aborting progression of inexistant identifier detected !!!");
101 << tr("Aborting progression of inexistant identifier detected !!!");
102 }
102 }
103 }
103 }
General Comments 3
Under Review
author

Auto status change to "Under Review"

Approved
author

Status change > Approved

You need to be logged in to leave comments. Login now