Libksysguard

SensorDataModel.cpp
1 /*
2  Copyright (c) 2019 Eike Hein <[email protected]>
3  Copyright (C) 2020 Arjen Hiemstra <[email protected]>
4 
5  This library is free software; you can redistribute it and/or
6  modify it under the terms of the GNU Library General Public
7  License as published by the Free Software Foundation; either
8  version 2 of the License, or (at your option) any later version.
9 
10  This library is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13  Library General Public License for more details.
14 
15  You should have received a copy of the GNU Library General Public License
16  along with this library; see the file COPYING.LIB. If not, write to
17  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18  Boston, MA 02110-1301, USA.
19 */
20 
21 #include "SensorDataModel.h"
22 
23 #include <optional>
24 
25 #include <QMetaEnum>
26 
27 #include "Formatter.h"
28 #include "SensorDaemonInterface_p.h"
29 #include "SensorInfo_p.h"
30 #include "sensors_logging.h"
31 
32 using namespace KSysGuard;
33 
34 class Q_DECL_HIDDEN SensorDataModel::Private
35 {
36 public:
37  Private(SensorDataModel *qq)
38  : q(qq)
39  {
40  }
41 
42  void sensorsChanged();
43  void addSensor(const QString &id);
44  void removeSensor(const QString &id);
45 
46  QStringList requestedSensors;
47 
48  QStringList sensors;
49  QStringList objects;
50 
51  QHash<QString, SensorInfo> sensorInfos;
52  QHash<QString, QVariant> sensorData;
53  QVariantMap sensorColors;
54 
55  bool usedByQml = false;
56  bool componentComplete = false;
57  bool loaded = false;
58  bool enabled = true;
59 
60  std::optional<qreal> minimum;
61  std::optional<qreal> maximum;
62 
63 private:
64  SensorDataModel *q;
65 };
66 
67 SensorDataModel::SensorDataModel(const QStringList &sensorIds, QObject *parent)
68  : QAbstractTableModel(parent)
69  , d(new Private(this))
70 {
71  connect(SensorDaemonInterface::instance(), &SensorDaemonInterface::sensorAdded, this, &SensorDataModel::onSensorAdded);
72  connect(SensorDaemonInterface::instance(), &SensorDaemonInterface::sensorRemoved, this, &SensorDataModel::onSensorRemoved);
73  connect(SensorDaemonInterface::instance(), &SensorDaemonInterface::metaDataChanged, this, &SensorDataModel::onMetaDataChanged);
74  connect(SensorDaemonInterface::instance(), &SensorDaemonInterface::valueChanged, this, &SensorDataModel::onValueChanged);
75 
76  // Empty string is used for entries that do not specify a wildcard object
77  d->objects << QStringLiteral("");
78 
79  setSensors(sensorIds);
80 }
81 
82 SensorDataModel::~SensorDataModel()
83 {
84 }
85 
86 QHash<int, QByteArray> SensorDataModel::roleNames() const
87 {
89 
90  QMetaEnum e = metaObject()->enumerator(metaObject()->indexOfEnumerator("AdditionalRoles"));
91 
92  for (int i = 0; i < e.keyCount(); ++i) {
93  roles.insert(e.value(i), e.key(i));
94  }
95 
96  return roles;
97 }
98 
99 QVariant SensorDataModel::data(const QModelIndex &index, int role) const
100 {
101  const bool check = checkIndex(index, CheckIndexOption::IndexIsValid | CheckIndexOption::DoNotUseParent);
102  if (!check) {
103  return QVariant();
104  }
105 
106  auto sensor = d->sensors.at(index.column());
107  auto info = d->sensorInfos.value(sensor);
108  auto data = d->sensorData.value(sensor);
109 
110  switch (role) {
111  case Qt::DisplayRole:
112  case FormattedValue:
113  return Formatter::formatValue(data, info.unit);
114  case Value:
115  return data;
116  case Unit:
117  return info.unit;
118  case Name:
119  return info.name;
120  case ShortName:
121  if (info.shortName.isEmpty()) {
122  return info.name;
123  }
124  return info.shortName;
125  case Description:
126  return info.description;
127  case Minimum:
128  return info.min;
129  case Maximum:
130  return info.max;
131  case Type:
132  return info.variantType;
133  case SensorId:
134  return sensor;
135  case Color:
136  if (!d->sensorColors.empty()) {
137  return d->sensorColors.value(sensor);
138  }
139  case UpdateInterval:
140  //TODO: Make this dynamic once the backend supports it.
141  return BackendUpdateInterval;
142  default:
143  break;
144  }
145 
146  return QVariant();
147 }
148 
149 QVariant SensorDataModel::headerData(int section, Qt::Orientation orientation, int role) const
150 {
151  if (orientation == Qt::Vertical) {
152  return QVariant();
153  }
154 
155  if (section < 0 || section >= d->sensors.size()) {
156  return QVariant();
157  }
158 
159  auto sensor = d->sensors.at(section);
160  auto info = d->sensorInfos.value(sensor);
161 
162  switch (role) {
163  case Qt::DisplayRole:
164  case ShortName:
165  if (info.shortName.isEmpty()) {
166  return info.name;
167  }
168  return info.shortName;
169  case Name:
170  return info.name;
171  case SensorId:
172  return sensor;
173  case Unit:
174  return info.unit;
175  case Description:
176  return info.description;
177  case Minimum:
178  return info.min;
179  case Maximum:
180  return info.max;
181  case Type:
182  return info.variantType;
183  case UpdateInterval:
184  //TODO: Make this dynamic once the backend supports it.
185  return BackendUpdateInterval;
186  default:
187  break;
188  }
189 
190  return QVariant();
191 }
192 
193 int SensorDataModel::rowCount(const QModelIndex &parent) const
194 {
195  if (parent.isValid()) {
196  return 0;
197  }
198 
199  return d->objects.count();
200 }
201 
202 int SensorDataModel::columnCount(const QModelIndex &parent) const
203 {
204  if (parent.isValid()) {
205  return 0;
206  }
207 
208  return d->sensors.count();
209 }
210 
211 qreal SensorDataModel::minimum() const
212 {
213  if (d->sensors.isEmpty()) {
214  return 0;
215  }
216 
217  if (d->minimum.has_value()) {
218  return d->minimum.value();
219  }
220 
221  auto result = std::min_element(d->sensorInfos.cbegin(), d->sensorInfos.cend(), [](const SensorInfo &first, const SensorInfo &second) { return first.min < second.min; });
222  d->minimum = (*result).min;
223  return d->minimum.value();
224 }
225 
226 qreal SensorDataModel::maximum() const
227 {
228  if (d->sensors.isEmpty()) {
229  return 0;
230  }
231 
232  if (d->maximum.has_value()) {
233  return d->maximum.value();
234  }
235 
236  auto result = std::max_element(d->sensorInfos.cbegin(), d->sensorInfos.cend(), [](const SensorInfo &first, const SensorInfo &second) { return first.max < second.max; });
237  d->maximum = (*result).max;
238  return d->maximum.value();
239 }
240 
241 QStringList SensorDataModel::sensors() const
242 {
243  return d->requestedSensors;
244 }
245 
246 void SensorDataModel::setSensors(const QStringList &sensorIds)
247 {
248  if (d->requestedSensors == sensorIds) {
249  return;
250  }
251 
252  d->requestedSensors = sensorIds;
253 
254  if (!d->usedByQml || d->componentComplete) {
255  d->sensorsChanged();
256  }
257  Q_EMIT readyChanged();
258  Q_EMIT sensorsChanged();
259 }
260 
261 bool SensorDataModel::enabled() const
262 {
263  return d->enabled;
264 }
265 
266 void SensorDataModel::setEnabled(bool newEnabled)
267 {
268  if (newEnabled == d->enabled) {
269  return;
270  }
271 
272  d->enabled = newEnabled;
273  if (d->enabled) {
274  SensorDaemonInterface::instance()->subscribe(d->sensorInfos.keys());
275  SensorDaemonInterface::instance()->requestMetaData(d->sensorInfos.keys());
276  } else {
277  SensorDaemonInterface::instance()->unsubscribe(d->sensorInfos.keys());
278  }
279 
280  Q_EMIT enabledChanged();
281 }
282 
283 QVariantMap SensorDataModel::sensorColors() const
284 {
285  return d->sensorColors;
286 }
287 
288 void SensorDataModel::setSensorColors(const QVariantMap &sensorColors)
289 {
290  if (sensorColors == d->sensorColors) {
291  return;
292  }
293  d->sensorColors = sensorColors;
294  Q_EMIT sensorColorsChanged();
295 }
296 
297 bool KSysGuard::SensorDataModel::isReady() const
298 {
299  return d->sensors.size() == d->requestedSensors.size();
300 }
301 
302 void SensorDataModel::addSensor(const QString &sensorId)
303 {
304  d->addSensor(sensorId);
305 }
306 
307 void SensorDataModel::removeSensor(const QString &sensorId)
308 {
309  d->removeSensor(sensorId);
310 }
311 
312 int KSysGuard::SensorDataModel::column(const QString &sensorId) const
313 {
314  return d->sensors.indexOf(sensorId);
315 }
316 
317 void KSysGuard::SensorDataModel::classBegin()
318 {
319  d->usedByQml = true;
320 }
321 
322 void KSysGuard::SensorDataModel::componentComplete()
323 {
324  d->componentComplete = true;
325 
326  d->sensorsChanged();
327 
328  emit sensorsChanged();
329 }
330 
331 void SensorDataModel::Private::addSensor(const QString &id)
332 {
333  if (!requestedSensors.contains(id)) {
334  return;
335  }
336 
337  qCDebug(LIBKSYSGUARD_SENSORS) << "Add Sensor" << id;
338 
339  sensors.append(id);
340  SensorDaemonInterface::instance()->subscribe(id);
341  SensorDaemonInterface::instance()->requestMetaData(id);
342 }
343 
344 void SensorDataModel::Private::removeSensor(const QString &id)
345 {
346  const int col = sensors.indexOf(id);
347  if (col == -1) {
348  return;
349  }
350 
351  q->beginRemoveColumns(QModelIndex(), col, col);
352 
353  sensors.removeAt(col);
354  sensorInfos.remove(id);
355  sensorData.remove(id);
356 
357  q->endRemoveColumns();
358 }
359 
360 void SensorDataModel::onSensorAdded(const QString &sensorId)
361 {
362  if (!d->enabled) {
363  return;
364  }
365 
366  d->addSensor(sensorId);
367 }
368 
369 void SensorDataModel::onSensorRemoved(const QString &sensorId)
370 {
371  if (!d->enabled) {
372  return;
373  }
374 
375  d->removeSensor(sensorId);
376 }
377 
378 void SensorDataModel::onMetaDataChanged(const QString &sensorId, const SensorInfo &info)
379 {
380  if (!d->enabled) {
381  return;
382  }
383 
384  auto column = d->sensors.indexOf(sensorId);
385  if (column == -1) {
386  return;
387  }
388 
389  qCDebug(LIBKSYSGUARD_SENSORS) << "Received metadata change for" << sensorId;
390 
391  d->minimum.reset();
392  d->maximum.reset();
393 
394  // Simple case: Just an update for a sensor's metadata
395  if (d->sensorInfos.contains(sensorId)) {
396  d->sensorInfos[sensorId] = info;
397  Q_EMIT dataChanged(index(0, column), index(0, column), {Qt::DisplayRole, Name, ShortName, Description, Unit, Minimum, Maximum, Type, FormattedValue});
398  Q_EMIT sensorMetaDataChanged();
399  return;
400  }
401 
402  // Otherwise, it's a new sensor that was added
403  beginInsertColumns(QModelIndex{}, column, column);
404  d->sensorInfos[sensorId] = info;
405  d->sensorData[sensorId] = QVariant{};
406  endInsertColumns();
407 
408  SensorDaemonInterface::instance()->requestValue(sensorId);
409  emit sensorMetaDataChanged();
410  Q_EMIT readyChanged();
411 }
412 
413 void SensorDataModel::onValueChanged(const QString &sensorId, const QVariant &value)
414 {
415  const auto column = d->sensors.indexOf(sensorId);
416  if (column == -1 || !d->enabled) {
417  return;
418  }
419 
420  d->sensorData[sensorId] = value;
421  Q_EMIT dataChanged(index(0, column), index(0, column), {Qt::DisplayRole, Value, FormattedValue});
422 }
423 
424 void SensorDataModel::Private::sensorsChanged()
425 {
426  q->beginResetModel();
427 
428  SensorDaemonInterface::instance()->unsubscribe(sensors);
429 
430  sensors.clear();
431  sensorData.clear();
432  sensorInfos.clear();
433 
434  sensors = requestedSensors;
435 
436  for (const auto& sensor : sensors) {
437  sensorInfos[sensor] = KSysGuard::SensorInfo();
438  }
439 
440  SensorDaemonInterface::instance()->subscribe(requestedSensors);
441  SensorDaemonInterface::instance()->requestMetaData(requestedSensors);
442 
443  q->endResetModel();
444 }
int indexOf(QChar ch, int from, Qt::CaseSensitivity cs) const const
QString & append(QChar ch)
QHash::iterator insert(const Key &key, const T &value)
int keyCount() const const
virtual QHash< int, QByteArray > roleNames() const const
virtual const QMetaObject * metaObject() const const
static QString formatValue(const QVariant &value, Unit unit, MetricPrefix targetPrefix=MetricPrefixAutoAdjust, FormatOptions options=FormatOptionNone)
Converts value to the appropriate displayable string.
Definition: Formatter.cpp:281
Description
A model representing a table of sensors.
bool isValid() const const
DisplayRole
int value(int index) const const
QMetaEnum enumerator(int index) const const
int column() const const
Orientation
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
const char * key(int index) const const
Q_EMITQ_EMIT
This file is part of the KDE documentation.
Documentation copyright © 1996-2021 The KDE developers.
Generated on Tue Mar 2 2021 02:46:14 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.