Changeset View
Changeset View
Standalone View
Standalone View
sensors/SensorDataModel.cpp
- This file was added.
1 | /* | ||||
---|---|---|---|---|---|
2 | Copyright (c) 2019 Eike Hein <hein@kde.org> | ||||
3 | Copyright (C) 2020 Arjen Hiemstra <ahiemstra@heimr.nl> | ||||
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 <QMetaEnum> | ||||
22 | | ||||
23 | #include "SensorDataModel.h" | ||||
24 | #include "Formatter.h" | ||||
25 | #include "SensorDaemonInterface_p.h" | ||||
26 | #include "SensorInfo_p.h" | ||||
27 | #include "sensors_logging.h" | ||||
28 | | ||||
29 | using namespace KSysGuard; | ||||
30 | | ||||
31 | class Q_DECL_HIDDEN SensorDataModel::Private | ||||
32 | { | ||||
33 | public: | ||||
34 | Private(SensorDataModel *qq) : q(qq) { } | ||||
35 | | ||||
36 | void sensorsChanged(); | ||||
37 | void addSensor(const QString &id); | ||||
38 | void removeSensor(const QString &id); | ||||
39 | | ||||
40 | QStringList requestedSensors; | ||||
41 | | ||||
42 | QStringList sensors; | ||||
43 | QStringList objects; | ||||
44 | | ||||
45 | QHash<QString, SensorInfo> sensorInfos; | ||||
46 | QHash<QString, QVariant> sensorData; | ||||
47 | | ||||
48 | bool usedByQml = false; | ||||
49 | bool componentComplete = false; | ||||
50 | bool loaded = false; | ||||
51 | | ||||
52 | private: | ||||
53 | SensorDataModel *q; | ||||
54 | }; | ||||
55 | | ||||
56 | SensorDataModel::SensorDataModel(const QStringList &sensorIds, QObject *parent) | ||||
57 | : QAbstractTableModel(parent) | ||||
58 | , d(new Private(this)) | ||||
59 | { | ||||
60 | connect(SensorDaemonInterface::instance(), &SensorDaemonInterface::sensorAdded, this, &SensorDataModel::onSensorAdded); | ||||
61 | connect(SensorDaemonInterface::instance(), &SensorDaemonInterface::sensorRemoved, this, &SensorDataModel::onSensorRemoved); | ||||
62 | connect(SensorDaemonInterface::instance(), &SensorDaemonInterface::metaDataChanged, this, &SensorDataModel::onMetaDataChanged); | ||||
63 | connect(SensorDaemonInterface::instance(), &SensorDaemonInterface::valueChanged, this, &SensorDataModel::onValueChanged); | ||||
64 | | ||||
65 | // Empty string is used for entries that do not specify a wildcard object | ||||
66 | d->objects << QStringLiteral(""); | ||||
67 | | ||||
68 | setSensors(sensorIds); | ||||
69 | } | ||||
70 | | ||||
71 | SensorDataModel::~SensorDataModel() | ||||
72 | { | ||||
73 | } | ||||
74 | | ||||
75 | QHash<int, QByteArray> SensorDataModel::roleNames() const | ||||
76 | { | ||||
77 | QHash<int, QByteArray> roles = QAbstractItemModel::roleNames(); | ||||
78 | | ||||
79 | QMetaEnum e = metaObject()->enumerator(metaObject()->indexOfEnumerator("AdditionalRoles")); | ||||
80 | | ||||
81 | for (int i = 0; i < e.keyCount(); ++i) { | ||||
82 | roles.insert(e.value(i), e.key(i)); | ||||
83 | } | ||||
84 | | ||||
85 | return roles; | ||||
86 | } | ||||
87 | | ||||
88 | QVariant SensorDataModel::data(const QModelIndex &index, int role) const | ||||
89 | { | ||||
90 | const bool check = checkIndex(index, CheckIndexOption::IndexIsValid | CheckIndexOption::DoNotUseParent); | ||||
91 | if (!check) { | ||||
92 | return QVariant(); | ||||
93 | } | ||||
94 | | ||||
95 | auto sensor = d->sensors.at(index.column()); | ||||
96 | auto info = d->sensorInfos.value(sensor); | ||||
97 | auto data = d->sensorData.value(sensor); | ||||
98 | | ||||
99 | switch (role) { | ||||
100 | case Qt::DisplayRole: | ||||
101 | case FormattedValue: | ||||
102 | return Formatter::formatValue(data, info.unit); | ||||
103 | case Value: | ||||
104 | return data; | ||||
105 | case Unit: | ||||
106 | return info.unit; | ||||
107 | case Name: | ||||
108 | return info.name; | ||||
109 | case ShortName: | ||||
110 | if (info.shortName.isEmpty()) { | ||||
111 | return info.name; | ||||
112 | } | ||||
113 | return info.shortName; | ||||
114 | case Description: | ||||
115 | return info.description; | ||||
116 | case Minimum: | ||||
117 | return info.min; | ||||
118 | case Maximum: | ||||
119 | return info.max; | ||||
120 | case Type: | ||||
121 | return info.variantType; | ||||
122 | case SensorId: | ||||
123 | return sensor; | ||||
124 | default: | ||||
125 | break; | ||||
126 | } | ||||
127 | | ||||
128 | return QVariant(); | ||||
129 | } | ||||
130 | | ||||
131 | QVariant SensorDataModel::headerData(int section, Qt::Orientation orientation, int role) const | ||||
132 | { | ||||
133 | if (orientation == Qt::Vertical) { | ||||
134 | return QVariant(); | ||||
135 | } | ||||
136 | | ||||
137 | if (section < 0 || section >= d->sensors.size()) { | ||||
138 | return QVariant(); | ||||
139 | } | ||||
140 | | ||||
141 | auto sensor = d->sensors.at(section); | ||||
142 | auto info = d->sensorInfos.value(sensor); | ||||
143 | | ||||
144 | switch (role) { | ||||
145 | case Qt::DisplayRole: | ||||
146 | case ShortName: | ||||
147 | if (info.shortName.isEmpty()) { | ||||
148 | return info.name; | ||||
149 | } | ||||
150 | return info.shortName; | ||||
151 | case Name: | ||||
152 | return info.name; | ||||
153 | case SensorId: | ||||
154 | return sensor; | ||||
155 | case Unit: | ||||
156 | return info.unit; | ||||
157 | case Description: | ||||
158 | return info.description; | ||||
159 | case Minimum: | ||||
160 | return info.min; | ||||
161 | case Maximum: | ||||
162 | return info.max; | ||||
163 | case Type: | ||||
164 | return info.variantType; | ||||
165 | default: | ||||
166 | break; | ||||
167 | } | ||||
168 | | ||||
169 | return QVariant(); | ||||
170 | } | ||||
171 | | ||||
172 | int SensorDataModel::rowCount(const QModelIndex &parent) const | ||||
173 | { | ||||
174 | if (parent.isValid()) { | ||||
175 | return 0; | ||||
176 | } | ||||
177 | | ||||
178 | return d->objects.count(); | ||||
179 | } | ||||
180 | | ||||
181 | int SensorDataModel::columnCount(const QModelIndex &parent) const | ||||
182 | { | ||||
183 | if (parent.isValid()) { | ||||
184 | return 0; | ||||
185 | } | ||||
186 | | ||||
187 | return d->sensors.count(); | ||||
188 | } | ||||
189 | | ||||
190 | qreal SensorDataModel::minimum() const | ||||
191 | { | ||||
192 | if (d->sensors.isEmpty()) { | ||||
193 | return 0; | ||||
194 | } | ||||
195 | | ||||
196 | auto result = std::min_element(d->sensorInfos.cbegin(), d->sensorInfos.cend(), [](const SensorInfo &first, const SensorInfo &second) { | ||||
197 | return first.min < second.min; | ||||
198 | }); | ||||
199 | return (*result).min; | ||||
200 | } | ||||
201 | | ||||
202 | qreal SensorDataModel::maximum() const | ||||
203 | { | ||||
204 | if (d->sensors.isEmpty()) { | ||||
205 | return 0; | ||||
206 | } | ||||
207 | | ||||
208 | auto result = std::max_element(d->sensorInfos.cbegin(), d->sensorInfos.cend(), [](const SensorInfo &first, const SensorInfo &second) { | ||||
209 | return first.max < second.max; | ||||
210 | }); | ||||
211 | return (*result).max; | ||||
212 | } | ||||
213 | | ||||
214 | QStringList SensorDataModel::sensors() const | ||||
215 | { | ||||
216 | return d->requestedSensors; | ||||
217 | } | ||||
218 | | ||||
219 | void SensorDataModel::setSensors(const QStringList &sensorIds) | ||||
220 | { | ||||
221 | if (d->requestedSensors == sensorIds) { | ||||
222 | return; | ||||
223 | } | ||||
224 | | ||||
225 | d->requestedSensors = sensorIds; | ||||
226 | | ||||
227 | if (!d->usedByQml || d->componentComplete) { | ||||
228 | d->sensorsChanged(); | ||||
229 | } | ||||
230 | Q_EMIT sensorsChanged(); | ||||
231 | } | ||||
232 | | ||||
233 | void SensorDataModel::addSensor(const QString &sensorId) | ||||
234 | { | ||||
235 | d->addSensor(sensorId); | ||||
236 | } | ||||
237 | | ||||
238 | void SensorDataModel::removeSensor(const QString &sensorId) | ||||
239 | { | ||||
240 | d->removeSensor(sensorId); | ||||
241 | } | ||||
242 | | ||||
243 | int KSysGuard::SensorDataModel::column(const QString& sensorId) const | ||||
244 | { | ||||
245 | return d->sensors.indexOf(sensorId); | ||||
246 | } | ||||
247 | | ||||
248 | void KSysGuard::SensorDataModel::classBegin() | ||||
249 | { | ||||
250 | d->usedByQml = true; | ||||
251 | } | ||||
252 | | ||||
253 | void KSysGuard::SensorDataModel::componentComplete() | ||||
254 | { | ||||
255 | d->componentComplete = true; | ||||
256 | | ||||
257 | d->sensorsChanged(); | ||||
258 | | ||||
259 | emit sensorsChanged(); | ||||
260 | } | ||||
261 | | ||||
262 | void SensorDataModel::Private::addSensor(const QString &id) | ||||
263 | { | ||||
264 | if (requestedSensors.indexOf(id) != -1) { | ||||
265 | return; | ||||
266 | } | ||||
267 | | ||||
268 | qCDebug(LIBKSYSGUARD_SENSORS) << "Add Sensor" << id; | ||||
269 | | ||||
270 | sensors.append(id); | ||||
271 | SensorDaemonInterface::instance()->subscribe(id); | ||||
272 | SensorDaemonInterface::instance()->requestMetaData(id); | ||||
273 | } | ||||
274 | | ||||
275 | void SensorDataModel::Private::removeSensor(const QString &id) | ||||
276 | { | ||||
277 | const int col = sensors.indexOf(id); | ||||
278 | if (col == -1) { | ||||
279 | return; | ||||
280 | } | ||||
281 | | ||||
282 | q->beginRemoveColumns(QModelIndex(), col, col); | ||||
283 | | ||||
284 | sensors.removeAt(col); | ||||
285 | sensorInfos.remove(id); | ||||
286 | sensorData.remove(id); | ||||
287 | | ||||
288 | q->endRemoveColumns(); | ||||
289 | } | ||||
290 | | ||||
291 | void SensorDataModel::onSensorAdded(const QString &sensorId) | ||||
292 | { | ||||
293 | d->addSensor(sensorId); | ||||
294 | } | ||||
295 | | ||||
296 | void SensorDataModel::onSensorRemoved(const QString &sensorId) | ||||
297 | { | ||||
298 | d->removeSensor(sensorId); | ||||
299 | } | ||||
300 | | ||||
301 | void SensorDataModel::onMetaDataChanged(const QString &sensorId, const SensorInfo &info) | ||||
302 | { | ||||
303 | auto column = d->sensors.indexOf(sensorId); | ||||
304 | if (column == -1) { | ||||
305 | return; | ||||
306 | } | ||||
307 | | ||||
308 | qCDebug(LIBKSYSGUARD_SENSORS) << "Received metadata change for" << sensorId; | ||||
309 | | ||||
310 | // Simple case: Just an update for a sensor's metadata | ||||
311 | if (d->sensorInfos.contains(sensorId)) { | ||||
312 | d->sensorInfos[sensorId] = info; | ||||
313 | Q_EMIT dataChanged(index(0, column), index(0, column), {Qt::DisplayRole, Name, ShortName, Description, Unit, Minimum, Maximum, Type, FormattedValue}); | ||||
314 | return; | ||||
315 | } | ||||
316 | | ||||
317 | // Otherwise, it's a new sensor that was added | ||||
318 | beginInsertColumns(QModelIndex{}, column, column); | ||||
319 | d->sensorInfos[sensorId] = info; | ||||
320 | d->sensorData[sensorId] = QVariant{}; | ||||
321 | endInsertColumns(); | ||||
322 | | ||||
323 | SensorDaemonInterface::instance()->requestValue(sensorId); | ||||
324 | emit sensorMetaDataChanged(); | ||||
325 | } | ||||
326 | | ||||
327 | void SensorDataModel::onValueChanged(const QString &sensorId, const QVariant &value) | ||||
328 | { | ||||
329 | if (!d->sensorData.contains(sensorId)) { | ||||
330 | return; | ||||
331 | } | ||||
332 | | ||||
333 | auto column = d->sensors.indexOf(sensorId); | ||||
334 | d->sensorData[sensorId] = value; | ||||
335 | Q_EMIT dataChanged(index(0, column), index(0, column), {Qt::DisplayRole, Value, FormattedValue}); | ||||
336 | } | ||||
337 | | ||||
338 | void SensorDataModel::Private::sensorsChanged() | ||||
339 | { | ||||
340 | #if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) | ||||
341 | auto newSet = QSet<QString>{requestedSensors.begin(), requestedSensors.end()}; | ||||
342 | auto currentSet = QSet<QString>{sensors.begin(), sensors.end()}; | ||||
343 | #else | ||||
344 | auto newSet = requestedSensors.toSet(); | ||||
345 | auto currentSet = sensors.toSet(); | ||||
346 | #endif | ||||
347 | | ||||
348 | const auto addedSensors = newSet - currentSet; | ||||
349 | const auto removedSensors = currentSet - newSet; | ||||
350 | | ||||
351 | sensors.append(addedSensors.values()); | ||||
352 | | ||||
353 | SensorDaemonInterface::instance()->subscribe(addedSensors.values()); | ||||
354 | SensorDaemonInterface::instance()->requestMetaData(addedSensors.values()); | ||||
355 | | ||||
356 | bool itemsRemoved = false; | ||||
357 | for (auto sensor : removedSensors) { | ||||
358 | removeSensor(sensor); | ||||
359 | itemsRemoved = true; | ||||
360 | } | ||||
361 | if (itemsRemoved) { | ||||
362 | emit q->sensorMetaDataChanged(); | ||||
363 | } | ||||
364 | } |