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