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