File indexing completed on 2024-05-12 03:47:43
0001 /* 0002 File : MQTTClient.cpp 0003 Project : LabPlot 0004 Description : Represents a MQTT Client 0005 -------------------------------------------------------------------- 0006 SPDX-FileCopyrightText: 2018 Kovacs Ferencz <kferike98@gmail.com> 0007 0008 SPDX-License-Identifier: GPL-2.0-or-later 0009 */ 0010 #include "backend/datasources/MQTTClient.h" 0011 #include "backend/datasources/MQTTSubscription.h" 0012 #include "backend/datasources/MQTTTopic.h" 0013 #include "backend/datasources/filters/AsciiFilter.h" 0014 #include "backend/lib/XmlStreamReader.h" 0015 #include "kdefrontend/datasources/MQTTErrorWidget.h" 0016 0017 #include <QTimer> 0018 0019 /*! 0020 \class MQTTClient 0021 \brief The MQTT Client connects to the broker set in ImportFileWidget. 0022 It manages the MQTTSubscriptions, and the MQTTTopics. 0023 0024 \ingroup datasources 0025 */ 0026 MQTTClient::MQTTClient(const QString& name) 0027 : Folder(name, AspectType::MQTTClient) 0028 , m_updateTimer(new QTimer(this)) 0029 , m_client(new QMqttClient(this)) 0030 , m_willTimer(new QTimer(this)) { 0031 // stop reading from the source before removing the child from the project 0032 connect(this, &AbstractAspect::childAspectAboutToBeRemoved, this, &MQTTClient::pauseReading); 0033 0034 connect(m_updateTimer, &QTimer::timeout, this, &MQTTClient::read); 0035 connect(m_client, &QMqttClient::connected, this, &MQTTClient::onMQTTConnect); 0036 connect(m_willTimer, &QTimer::timeout, this, &MQTTClient::updateWillMessage); 0037 connect(m_client, &QMqttClient::errorChanged, this, &MQTTClient::MQTTErrorChanged); 0038 } 0039 0040 MQTTClient::~MQTTClient() { 0041 Q_EMIT clientAboutToBeDeleted(m_client->hostname(), m_client->port()); 0042 // stop reading before deleting the objects 0043 pauseReading(); 0044 0045 delete m_filter; 0046 delete m_updateTimer; 0047 delete m_willTimer; 0048 m_client->disconnectFromHost(); 0049 delete m_client; 0050 } 0051 0052 /*! 0053 * depending on the update type, periodically or on data changes, starts the timer. 0054 */ 0055 void MQTTClient::ready() { 0056 if (m_updateType == UpdateType::TimeInterval) 0057 m_updateTimer->start(m_updateInterval); 0058 } 0059 0060 /*! 0061 * \brief Updates the MQTTTopics of the client 0062 */ 0063 void MQTTClient::updateNow() { 0064 m_updateTimer->stop(); 0065 read(); 0066 if ((m_updateType == UpdateType::TimeInterval) && !m_paused) 0067 m_updateTimer->start(m_updateInterval); 0068 } 0069 0070 /*! 0071 * \brief Continue reading from messages after it was paused. 0072 */ 0073 void MQTTClient::continueReading() { 0074 m_paused = false; 0075 if (m_updateType == UpdateType::TimeInterval) 0076 m_updateTimer->start(m_updateInterval); 0077 } 0078 0079 /*! 0080 * \brief Pause the reading from messages. 0081 */ 0082 void MQTTClient::pauseReading() { 0083 m_paused = true; 0084 if (m_updateType == UpdateType::TimeInterval) 0085 m_updateTimer->stop(); 0086 } 0087 0088 /*! 0089 * \brief Sets the filter of the MQTTClient. 0090 * The ownership of the filter is passed to MQTTClient. 0091 * 0092 * \param f a pointer to the new filter 0093 */ 0094 void MQTTClient::setFilter(AsciiFilter* f) { 0095 delete m_filter; 0096 m_filter = f; 0097 } 0098 0099 /*! 0100 * \brief Returns the filter of the MQTTClient. 0101 */ 0102 AsciiFilter* MQTTClient::filter() const { 0103 return m_filter; 0104 } 0105 0106 /*! 0107 * \brief Sets the MQTTclient's update interval to \c interval 0108 * \param interval 0109 */ 0110 void MQTTClient::setUpdateInterval(int interval) { 0111 m_updateInterval = interval; 0112 if (!m_paused) 0113 m_updateTimer->start(m_updateInterval); 0114 } 0115 0116 /*! 0117 * \brief Returns the MQTTClient's update interval to \c interval 0118 * \param interval 0119 */ 0120 int MQTTClient::updateInterval() const { 0121 return m_updateInterval; 0122 } 0123 0124 /*! 0125 * \brief Sets how many values we should store 0126 * \param keepNValues 0127 */ 0128 void MQTTClient::setKeepNValues(int keepNValues) { 0129 m_keepNValues = keepNValues; 0130 } 0131 0132 /*! 0133 * \brief Returns how many values we should store 0134 */ 0135 int MQTTClient::keepNValues() const { 0136 return m_keepNValues; 0137 } 0138 0139 /*! 0140 * \brief Provides information about whether the reading is paused or not 0141 * 0142 * \return true if the reading is paused 0143 * \return false otherwise 0144 */ 0145 bool MQTTClient::isPaused() const { 0146 return m_paused; 0147 } 0148 0149 /*! 0150 * \brief Sets the size rate to sampleSize 0151 * \param sampleSize 0152 */ 0153 void MQTTClient::setSampleSize(int sampleSize) { 0154 m_sampleSize = sampleSize; 0155 } 0156 0157 /*! 0158 * \brief Returns the size rate 0159 */ 0160 int MQTTClient::sampleSize() const { 0161 return m_sampleSize; 0162 } 0163 0164 /*! 0165 * \brief Sets the MQTTClient's reading type to readingType 0166 * \param readingType 0167 */ 0168 void MQTTClient::setReadingType(ReadingType readingType) { 0169 m_readingType = readingType; 0170 } 0171 0172 /*! 0173 * \brief Returns the MQTTClient's reading type 0174 */ 0175 MQTTClient::ReadingType MQTTClient::readingType() const { 0176 return m_readingType; 0177 } 0178 0179 /*! 0180 * \brief Sets the MQTTClient's update type to updatetype and handles this change 0181 * \param updatetype 0182 */ 0183 void MQTTClient::setUpdateType(UpdateType updateType) { 0184 if (updateType == UpdateType::NewData) 0185 m_updateTimer->stop(); 0186 0187 m_updateType = updateType; 0188 } 0189 0190 /*! 0191 * \brief Returns the MQTTClient's update type 0192 */ 0193 MQTTClient::UpdateType MQTTClient::updateType() const { 0194 return m_updateType; 0195 } 0196 0197 /*! 0198 * \brief Returns the MQTTClient's icon 0199 */ 0200 QIcon MQTTClient::icon() const { 0201 return QIcon::fromTheme(QStringLiteral("network-server-database")); 0202 } 0203 0204 /*! 0205 * \brief Sets the host and port for the client. 0206 * 0207 * \param host the hostname of the broker we want to connect to 0208 * \param port the port used by the broker 0209 */ 0210 void MQTTClient::setMQTTClientHostPort(const QString& host, quint16 port) { 0211 m_client->setHostname(host); 0212 m_client->setPort(port); 0213 } 0214 0215 /*! 0216 * \brief Returns hostname of the broker the client is connected to. 0217 */ 0218 QString MQTTClient::clientHostName() const { 0219 return m_client->hostname(); 0220 } 0221 0222 /*! 0223 * \brief Returns the port used by the broker. 0224 */ 0225 quint16 MQTTClient::clientPort() const { 0226 return m_client->port(); 0227 } 0228 0229 /*! 0230 * \brief Sets the flag on the given value. 0231 * If set true it means that the broker requires authentication, otherwise it doesn't. 0232 * 0233 * \param use 0234 */ 0235 void MQTTClient::setMQTTUseAuthentication(bool use) { 0236 m_MQTTUseAuthentication = use; 0237 } 0238 0239 /*! 0240 * \brief Returns whether the broker requires authentication or not. 0241 */ 0242 bool MQTTClient::MQTTUseAuthentication() const { 0243 return m_MQTTUseAuthentication; 0244 } 0245 0246 /*! 0247 * \brief Sets the username and password for the client. 0248 * 0249 * \param username the username used for authentication 0250 * \param password the password used for authentication 0251 */ 0252 void MQTTClient::setMQTTClientAuthentication(const QString& username, const QString& password) { 0253 m_client->setUsername(username); 0254 m_client->setPassword(password); 0255 } 0256 0257 /*! 0258 * \brief Returns the username used for authentication. 0259 */ 0260 QString MQTTClient::clientUserName() const { 0261 return m_client->username(); 0262 } 0263 0264 /*! 0265 * \brief Returns the password used for authentication. 0266 */ 0267 QString MQTTClient::clientPassword() const { 0268 return m_client->password(); 0269 } 0270 0271 /*! 0272 * \brief Sets the flag on the given value. 0273 * If set true it means that user wants to set the client ID, otherwise it's not the case. 0274 * 0275 * \param use 0276 */ 0277 void MQTTClient::setMQTTUseID(bool use) { 0278 m_MQTTUseID = use; 0279 } 0280 0281 /*! 0282 * \brief Returns whether the user wants to set the client ID or not. 0283 */ 0284 bool MQTTClient::MQTTUseID() const { 0285 return m_MQTTUseID; 0286 } 0287 0288 /*! 0289 * \brief Sets the ID of the client 0290 * 0291 * \param id 0292 */ 0293 void MQTTClient::setMQTTClientId(const QString& clientId) { 0294 m_client->setClientId(clientId); 0295 } 0296 0297 /*! 0298 * \brief Returns the ID of the client 0299 */ 0300 QString MQTTClient::clientID() const { 0301 return m_client->clientId(); 0302 } 0303 0304 /*! 0305 * \brief Sets the flag on the given value. 0306 * If retain is true we interpret retain messages, otherwise we do not 0307 * 0308 * \param retain 0309 */ 0310 void MQTTClient::setMQTTRetain(bool retain) { 0311 m_MQTTRetain = retain; 0312 } 0313 0314 /*! 0315 * \brief Returns the flag, which set to true means that interpret retain messages, otherwise we do not 0316 */ 0317 bool MQTTClient::MQTTRetain() const { 0318 return m_MQTTRetain; 0319 } 0320 0321 /*! 0322 * \brief Returns the name of every MQTTTopics which already received a message, and is child of the MQTTClient 0323 */ 0324 QVector<QString> MQTTClient::topicNames() const { 0325 return m_topicNames; 0326 } 0327 0328 /*! 0329 * \brief Adds the initial subscriptions that were set in ImportFileWidget 0330 * 0331 * \param filter the name of the subscribed topic 0332 * \param qos the qos level of the subscription 0333 */ 0334 void MQTTClient::addInitialMQTTSubscriptions(const QMqttTopicFilter& filter, quint8 qos) { 0335 m_subscribedTopicNameQoS[filter] = qos; 0336 } 0337 0338 /*! 0339 * \brief Returns the name of every MQTTSubscription of the MQTTClient 0340 */ 0341 QVector<QString> MQTTClient::MQTTSubscriptions() const { 0342 return m_subscriptions; 0343 } 0344 0345 /*! 0346 * \brief Adds a new MQTTSubscription to the MQTTClient 0347 * 0348 * \param topic, the name of the topic 0349 * \param QoS 0350 */ 0351 void MQTTClient::addMQTTSubscription(const QString& topicName, quint8 QoS) { 0352 // Check whether the subscription already exists, if it doesn't, we can add it 0353 if (!m_subscriptions.contains(topicName)) { 0354 const QMqttTopicFilter filter{topicName}; 0355 QMqttSubscription* temp = m_client->subscribe(filter, QoS); 0356 0357 if (temp) { 0358 // qDebug()<<"Subscribe to: "<< temp->topic() << " " << temp->qos(); 0359 m_subscriptions.push_back(temp->topic().filter()); 0360 m_subscribedTopicNameQoS[temp->topic().filter()] = temp->qos(); 0361 0362 auto* newSubscription = new MQTTSubscription(temp->topic().filter()); 0363 newSubscription->setMQTTClient(this); 0364 0365 addChildFast(newSubscription); 0366 m_MQTTSubscriptions.push_back(newSubscription); 0367 0368 // Search for inferior subscriptions, that the new subscription contains 0369 bool found = false; 0370 QVector<MQTTSubscription*> inferiorSubscriptions; 0371 for (auto* subscription : m_MQTTSubscriptions) { 0372 if (checkTopicContains(topicName, subscription->subscriptionName()) && topicName != subscription->subscriptionName()) { 0373 found = true; 0374 inferiorSubscriptions.push_back(subscription); 0375 } 0376 } 0377 0378 // If there are some inferior subscriptions, we have to deal with them 0379 if (found) { 0380 for (auto* inferiorSubscription : inferiorSubscriptions) { 0381 // qDebug()<<"Reparent topics of inferior subscription: "<< inferiorSubscription->subscriptionName(); 0382 0383 // We have to reparent every topic of the inferior subscription, so no data is lost 0384 QVector<MQTTTopic*> topics = inferiorSubscription->topics(); 0385 for (auto* topic : topics) { 0386 topic->reparent(newSubscription); 0387 } 0388 0389 // Then remove the subscription and every connected information 0390 QMqttTopicFilter unsubscribeFilter{inferiorSubscription->subscriptionName()}; 0391 m_client->unsubscribe(unsubscribeFilter); 0392 0393 for (int j = 0; j < m_MQTTSubscriptions.size(); ++j) { 0394 if (m_MQTTSubscriptions[j]->subscriptionName() == inferiorSubscription->subscriptionName()) { 0395 m_MQTTSubscriptions.remove(j); 0396 } 0397 } 0398 m_subscriptions.removeAll(inferiorSubscription->subscriptionName()); 0399 m_subscribedTopicNameQoS.remove(inferiorSubscription->subscriptionName()); 0400 0401 removeChild(inferiorSubscription); 0402 } 0403 } 0404 0405 connect(temp, &QMqttSubscription::messageReceived, this, &MQTTClient::MQTTSubscriptionMessageReceived); 0406 0407 Q_EMIT MQTTTopicsChanged(); 0408 } 0409 } 0410 } 0411 0412 /*! 0413 * \brief Removes a MQTTSubscription from the MQTTClient 0414 * 0415 * \param name, the name of the subscription to remove 0416 */ 0417 void MQTTClient::removeMQTTSubscription(const QString& subscriptionName) { 0418 // We can only remove the subscription if it exists 0419 if (m_subscriptions.contains(subscriptionName)) { 0420 // unsubscribe from the topic 0421 const QMqttTopicFilter filter{subscriptionName}; 0422 m_client->unsubscribe(filter); 0423 // qDebug()<<"Unsubscribe from: " << subscriptionName; 0424 0425 // Remove every connected information 0426 m_subscriptions.removeAll(subscriptionName); 0427 0428 for (int i = 0; i < m_MQTTSubscriptions.size(); ++i) { 0429 if (m_MQTTSubscriptions[i]->subscriptionName() == subscriptionName) { 0430 MQTTSubscription* removeSubscription = m_MQTTSubscriptions[i]; 0431 m_MQTTSubscriptions.remove(i); 0432 // Remove every topic of the subscription as well 0433 QVector<MQTTTopic*> topics = removeSubscription->topics(); 0434 for (const auto& topic : topics) 0435 m_topicNames.removeAll(topic->topicName()); 0436 0437 // Remove the MQTTSubscription 0438 removeChild(removeSubscription); 0439 break; 0440 } 0441 } 0442 0443 QMapIterator<QMqttTopicFilter, quint8> j(m_subscribedTopicNameQoS); 0444 while (j.hasNext()) { 0445 j.next(); 0446 if (j.key().filter() == subscriptionName) { 0447 m_subscribedTopicNameQoS.remove(j.key()); 0448 break; 0449 } 0450 } 0451 Q_EMIT MQTTTopicsChanged(); 0452 } 0453 } 0454 0455 /*! 0456 * \brief Adds a MQTTSubscription to the MQTTClient 0457 *Used when the user unsubscribes from a topic of a MQTTSubscription 0458 * 0459 * \param topic, the name of the topic 0460 * \param QoS 0461 */ 0462 void MQTTClient::addBeforeRemoveSubscription(const QString& topicName, quint8 QoS) { 0463 // We can't add the subscription if it already exists 0464 if (!m_subscriptions.contains(topicName)) { 0465 // Subscribe to the topic 0466 QMqttTopicFilter filter{topicName}; 0467 QMqttSubscription* temp = m_client->subscribe(filter, QoS); 0468 if (temp) { 0469 // Add the MQTTSubscription and other connected data 0470 // qDebug()<<"Add subscription before remove: " << temp->topic() << " " << temp->qos(); 0471 m_subscriptions.push_back(temp->topic().filter()); 0472 m_subscribedTopicNameQoS[temp->topic().filter()] = temp->qos(); 0473 0474 auto* newSubscription = new MQTTSubscription(temp->topic().filter()); 0475 newSubscription->setMQTTClient(this); 0476 0477 addChildFast(newSubscription); 0478 m_MQTTSubscriptions.push_back(newSubscription); 0479 0480 // Search for the subscription the topic belonged to 0481 bool found = false; 0482 MQTTSubscription* superiorSubscription = nullptr; 0483 for (auto* subscription : m_MQTTSubscriptions) { 0484 if (checkTopicContains(subscription->subscriptionName(), topicName) && topicName != subscription->subscriptionName()) { 0485 found = true; 0486 superiorSubscription = subscription; 0487 break; 0488 } 0489 } 0490 0491 if (found) { 0492 // Search for topics belonging to the superior(old) subscription 0493 // which are also contained by the new subscription 0494 QVector<MQTTTopic*> topics = superiorSubscription->topics(); 0495 // qDebug()<< topics.size(); 0496 0497 QVector<MQTTTopic*> inferiorTopics; 0498 for (auto* topic : topics) { 0499 if (checkTopicContains(topicName, topic->topicName())) { 0500 inferiorTopics.push_back(topic); 0501 } 0502 } 0503 0504 // Reparent these topics, in order to avoid data loss 0505 for (auto* inferiorTopic : inferiorTopics) { 0506 inferiorTopic->reparent(newSubscription); 0507 } 0508 } 0509 connect(temp, &QMqttSubscription::messageReceived, this, &MQTTClient::MQTTSubscriptionMessageReceived); 0510 } 0511 } 0512 } 0513 0514 /*! 0515 * \brief Reparents the given MQTTTopic to the given MQTTSubscription 0516 * 0517 * \param topic, the name of the MQTTTopic 0518 * \param parent, the name of the MQTTSubscription 0519 */ 0520 void MQTTClient::reparentTopic(const QString& topicName, const QString& parentTopicName) { 0521 // We can only reparent if the parent contained the topic 0522 if (m_subscriptions.contains(parentTopicName) && m_topicNames.contains(topicName)) { 0523 // qDebug() << "Reparent " << topicName << " to " << parentTopicName; 0524 // search for the parent MQTTSubscription 0525 bool found = false; 0526 MQTTSubscription* superiorSubscription = nullptr; 0527 for (auto* subscription : m_MQTTSubscriptions) { 0528 if (subscription->subscriptionName() == parentTopicName) { 0529 found = true; 0530 superiorSubscription = subscription; 0531 break; 0532 } 0533 } 0534 0535 if (found) { 0536 // get every topic of the MQTTClient 0537 QVector<MQTTTopic*> topics = children<MQTTTopic>(AbstractAspect::ChildIndexFlag::Recursive); 0538 // Search for the given topic among the MQTTTopics 0539 for (auto* topic : topics) { 0540 if (topicName == topic->topicName()) { 0541 // if found, it is reparented to the parent MQTTSubscription 0542 topic->reparent(superiorSubscription); 0543 break; 0544 } 0545 } 0546 } 0547 } 0548 } 0549 0550 /*! 0551 *\brief Checks if a topic contains another one 0552 * 0553 * \param superior the name of a topic 0554 * \param inferior the name of a topic 0555 * \return true if superior is equal to or contains(if superior contains wildcards) inferior, 0556 * false otherwise 0557 */ 0558 bool MQTTClient::checkTopicContains(const QString& superior, const QString& inferior) { 0559 if (superior == inferior) 0560 return true; 0561 else { 0562 if (superior.contains(QLatin1String("/"))) { 0563 #if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) 0564 QStringList superiorList = superior.split(QLatin1Char('/'), Qt::SkipEmptyParts); 0565 QStringList inferiorList = inferior.split(QLatin1Char('/'), Qt::SkipEmptyParts); 0566 #else 0567 QStringList superiorList = superior.split(QLatin1Char('/'), QString::SkipEmptyParts); 0568 QStringList inferiorList = inferior.split(QLatin1Char('/'), QString::SkipEmptyParts); 0569 #endif 0570 0571 // a longer topic can't contain a shorter one 0572 if (superiorList.size() > inferiorList.size()) 0573 return false; 0574 0575 bool ok = true; 0576 for (int i = 0; i < superiorList.size(); ++i) { 0577 if (superiorList.at(i) != inferiorList.at(i)) { 0578 if ((superiorList.at(i) != QLatin1Char('+')) && !(superiorList.at(i) == QLatin1Char('#') && i == superiorList.size() - 1)) { 0579 // if the two topics differ, and the superior's current level isn't + or #(which can be only in the last position) 0580 // then superior can't contain inferior 0581 ok = false; 0582 break; 0583 } else if (i == superiorList.size() - 1 && (superiorList.at(i) == QLatin1Char('+') && inferiorList.at(i) == QLatin1Char('#'))) { 0584 // if the two topics differ at the last level 0585 // and the superior's current level is + while the inferior's is #(which can be only in the last position) 0586 // then superior can't contain inferior 0587 ok = false; 0588 break; 0589 } 0590 } 0591 } 0592 return ok; 0593 } 0594 return false; 0595 } 0596 } 0597 0598 /*! 0599 *\brief Returns the '+' wildcard containing topic name, which includes the given topic names 0600 * 0601 * \param first the name of a topic 0602 * \param second the name of a topic 0603 * \return The name of the common topic, if it exists, otherwise an empty string 0604 */ 0605 QString MQTTClient::checkCommonLevel(const QString& first, const QString& second) { 0606 #if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) 0607 QStringList firstList = first.split(QLatin1Char('/'), Qt::SkipEmptyParts); 0608 QStringList secondtList = second.split(QLatin1Char('/'), Qt::SkipEmptyParts); 0609 #else 0610 QStringList firstList = first.split(QLatin1Char('/'), QString::SkipEmptyParts); 0611 QStringList secondtList = second.split(QLatin1Char('/'), QString::SkipEmptyParts); 0612 #endif 0613 QString commonTopic; 0614 0615 if (!firstList.isEmpty()) { 0616 // the two topics have to be the same size and can't be identic 0617 if ((firstList.size() == secondtList.size()) && (first != second)) { 0618 // the index where they differ 0619 int differIndex = -1; 0620 for (int i = 0; i < firstList.size(); ++i) { 0621 if (firstList.at(i) != secondtList.at(i)) { 0622 differIndex = i; 0623 break; 0624 } 0625 } 0626 0627 // they can differ at only one level and that can't be the first 0628 bool differ = false; 0629 if (differIndex > 0) { 0630 for (int j = differIndex + 1; j < firstList.size(); ++j) { 0631 if (firstList.at(j) != secondtList.at(j)) { 0632 differ = true; 0633 break; 0634 } 0635 } 0636 } else 0637 differ = true; 0638 0639 if (!differ) { 0640 for (int i = 0; i < firstList.size(); ++i) { 0641 if (i != differIndex) { 0642 commonTopic.append(firstList.at(i)); 0643 } else { 0644 // we put '+' wildcard at the level where they differ 0645 commonTopic.append(QStringLiteral("+")); 0646 } 0647 0648 if (i != firstList.size() - 1) 0649 commonTopic.append(QStringLiteral("/")); 0650 } 0651 } 0652 } 0653 } 0654 // qDebug() << first << " " << second << " common topic: "<<commonTopic; 0655 return commonTopic; 0656 } 0657 0658 void MQTTClient::setWillSettings(const MQTTWill& settings) { 0659 m_MQTTWill = settings; 0660 } 0661 0662 MQTTClient::MQTTWill MQTTClient::willSettings() const { 0663 return m_MQTTWill; 0664 } 0665 0666 /*! 0667 * \brief Sets whether the user wants to use will message or not 0668 * 0669 * \param use 0670 */ 0671 void MQTTClient::setMQTTWillUse(bool use) { 0672 m_MQTTWill.enabled = use; 0673 if (use == false) 0674 m_willTimer->stop(); 0675 } 0676 0677 /*! 0678 * \brief Returns whether the user wants to use will message or not 0679 */ 0680 bool MQTTClient::MQTTWillUse() const { 0681 return m_MQTTWill.enabled; 0682 } 0683 0684 /*! 0685 * \brief Sets the will topic of the client 0686 * 0687 * \param topic 0688 */ 0689 void MQTTClient::setWillTopic(const QString& topic) { 0690 // qDebug() << "Set will topic:" << topic; 0691 m_MQTTWill.willTopic = topic; 0692 } 0693 0694 /*! 0695 * \brief Returns the will topic of the client 0696 */ 0697 QString MQTTClient::willTopic() const { 0698 return m_MQTTWill.willTopic; 0699 } 0700 0701 /*! 0702 * \brief Sets the retain flag of the client's will message 0703 * 0704 * \param retain 0705 */ 0706 void MQTTClient::setWillRetain(bool retain) { 0707 m_MQTTWill.willRetain = retain; 0708 } 0709 0710 /*! 0711 * \brief Returns the retain flag of the client's will message 0712 */ 0713 bool MQTTClient::willRetain() const { 0714 return m_MQTTWill.willRetain; 0715 } 0716 0717 /*! 0718 * \brief Sets the QoS level of the client's will message 0719 * 0720 * \param QoS 0721 */ 0722 void MQTTClient::setWillQoS(quint8 QoS) { 0723 m_MQTTWill.willQoS = QoS; 0724 } 0725 0726 /*! 0727 * \brief Returns the QoS level of the client's will message 0728 */ 0729 quint8 MQTTClient::willQoS() const { 0730 return m_MQTTWill.willQoS; 0731 } 0732 0733 /*! 0734 * \brief Sets the will message type of the client 0735 * 0736 * \param messageType 0737 */ 0738 void MQTTClient::setWillMessageType(WillMessageType messageType) { 0739 m_MQTTWill.willMessageType = messageType; 0740 } 0741 0742 /*! 0743 * \brief Returns the will message type of the client 0744 */ 0745 MQTTClient::WillMessageType MQTTClient::willMessageType() const { 0746 return m_MQTTWill.willMessageType; 0747 } 0748 0749 /*! 0750 * \brief Sets the own will message of the user 0751 * 0752 * \param ownMessage 0753 */ 0754 void MQTTClient::setWillOwnMessage(const QString& ownMessage) { 0755 m_MQTTWill.willOwnMessage = ownMessage; 0756 } 0757 0758 /*! 0759 * \brief Returns the own will message of the user 0760 */ 0761 QString MQTTClient::willOwnMessage() const { 0762 return m_MQTTWill.willOwnMessage; 0763 } 0764 0765 /*! 0766 * \brief Updates the will message of the client 0767 */ 0768 void MQTTClient::updateWillMessage() { 0769 QVector<const MQTTTopic*> topics = children<const MQTTTopic>(AbstractAspect::ChildIndexFlag::Recursive); 0770 const MQTTTopic* willTopic = nullptr; 0771 0772 // Search for the will topic 0773 for (const auto* topic : topics) { 0774 if (topic->topicName() == m_MQTTWill.willTopic) { 0775 willTopic = topic; 0776 break; 0777 } 0778 } 0779 0780 // if the will topic is found we can update the will message 0781 if (willTopic != nullptr) { 0782 // To update the will message we have to disconnect first, then after setting everything connect again 0783 if (m_MQTTWill.enabled && (m_client->state() == QMqttClient::ClientState::Connected)) { 0784 // Disconnect only once (disconnecting may take a while) 0785 if (!m_disconnectForWill) { 0786 // qDebug() << "Disconnecting from host in order to update will message"; 0787 m_client->disconnectFromHost(); 0788 m_disconnectForWill = true; 0789 } 0790 // Try to update again 0791 updateWillMessage(); 0792 } 0793 // If client is disconnected we can update the settings 0794 else if (m_MQTTWill.enabled && (m_client->state() == QMqttClient::ClientState::Disconnected) && m_disconnectForWill) { 0795 m_client->setWillQoS(m_MQTTWill.willQoS); 0796 m_client->setWillRetain(m_MQTTWill.willRetain); 0797 m_client->setWillTopic(m_MQTTWill.willTopic); 0798 0799 // Set the will message according to m_willMessageType 0800 switch (m_MQTTWill.willMessageType) { 0801 case WillMessageType::OwnMessage: 0802 m_client->setWillMessage(m_MQTTWill.willOwnMessage.toUtf8()); 0803 // qDebug()<<"Will own message" << m_MQTTWill.willOwnMessage; 0804 break; 0805 case WillMessageType::Statistics: { 0806 // Statistics is only possible if the data stored in the MQTTTopic is of type integer or numeric 0807 // check the column mode of the last column in the topic for this. 0808 // TODO: check this logic again - why last column only? 0809 const auto* col = willTopic->child<Column>(willTopic->childCount<Column>() - 1); 0810 auto mode = col->columnMode(); 0811 if (mode == AbstractColumn::ColumnMode::Integer || mode == AbstractColumn::ColumnMode::Double) 0812 m_client->setWillMessage(this->statistics(willTopic).toUtf8()); 0813 else 0814 m_client->setWillMessage(QByteArray()); // empty message 0815 // qDebug() << "Will statistics message: "<< QString(m_client->willMessage()); 0816 break; 0817 } 0818 case WillMessageType::LastMessage: 0819 m_client->setWillMessage(m_MQTTWill.willLastMessage.toUtf8()); 0820 // qDebug()<<"Will last message:\n" << m_MQTTWill.willLastMessage; 0821 break; 0822 default: 0823 break; 0824 } 0825 m_disconnectForWill = false; 0826 // Reconnect with the updated message 0827 m_client->connectToHost(); 0828 // qDebug()<< "Reconnect to host after updating will message"; 0829 } 0830 } 0831 } 0832 0833 /*! 0834 * \brief Returns the statistical data that is needed by the topic for its MQTTClient's will message 0835 * \param topic 0836 */ 0837 QString MQTTClient::statistics(const MQTTTopic* topic) const { 0838 const auto* col = topic->child<Column>(topic->childCount<Column>() - 1); 0839 QString statistics; 0840 0841 QVector<bool> willStatistics = topic->mqttClient()->willStatistics(); 0842 // Add every statistical data to the string, the flag of which is set true 0843 for (int i = 0; i <= willStatistics.size(); i++) { 0844 if (willStatistics[i]) { 0845 switch (static_cast<MQTTClient::WillStatisticsType>(i)) { 0846 case MQTTClient::WillStatisticsType::ArithmeticMean: 0847 statistics += QStringLiteral("Arithmetic mean: ") + QString::number(col->statistics().arithmeticMean) + QStringLiteral("\n"); 0848 break; 0849 case MQTTClient::WillStatisticsType::ContraharmonicMean: 0850 statistics += QStringLiteral("Contraharmonic mean: ") + QString::number(col->statistics().contraharmonicMean) + QStringLiteral("\n"); 0851 break; 0852 case MQTTClient::WillStatisticsType::Entropy: 0853 statistics += QStringLiteral("Entropy: ") + QString::number(col->statistics().entropy) + QStringLiteral("\n"); 0854 break; 0855 case MQTTClient::WillStatisticsType::GeometricMean: 0856 statistics += QStringLiteral("Geometric mean: ") + QString::number(col->statistics().geometricMean) + QStringLiteral("\n"); 0857 break; 0858 case MQTTClient::WillStatisticsType::HarmonicMean: 0859 statistics += QStringLiteral("Harmonic mean: ") + QString::number(col->statistics().harmonicMean) + QStringLiteral("\n"); 0860 break; 0861 case MQTTClient::WillStatisticsType::Kurtosis: 0862 statistics += QStringLiteral("Kurtosis: ") + QString::number(col->statistics().kurtosis) + QStringLiteral("\n"); 0863 break; 0864 case MQTTClient::WillStatisticsType::Maximum: 0865 statistics += QStringLiteral("Maximum: ") + QString::number(col->statistics().maximum) + QStringLiteral("\n"); 0866 break; 0867 case MQTTClient::WillStatisticsType::MeanDeviation: 0868 statistics += QStringLiteral("Mean deviation: ") + QString::number(col->statistics().meanDeviation) + QStringLiteral("\n"); 0869 break; 0870 case MQTTClient::WillStatisticsType::MeanDeviationAroundMedian: 0871 statistics += 0872 QStringLiteral("Mean deviation around median: ") + QString::number(col->statistics().meanDeviationAroundMedian) + QStringLiteral("\n"); 0873 break; 0874 case MQTTClient::WillStatisticsType::Median: 0875 statistics += QStringLiteral("Median: ") + QString::number(col->statistics().median) + QStringLiteral("\n"); 0876 break; 0877 case MQTTClient::WillStatisticsType::MedianDeviation: 0878 statistics += QStringLiteral("Median deviation: ") + QString::number(col->statistics().medianDeviation) + QStringLiteral("\n"); 0879 break; 0880 case MQTTClient::WillStatisticsType::Minimum: 0881 statistics += QStringLiteral("Minimum: ") + QString::number(col->statistics().minimum) + QStringLiteral("\n"); 0882 break; 0883 case MQTTClient::WillStatisticsType::Skewness: 0884 statistics += QStringLiteral("Skewness: ") + QString::number(col->statistics().skewness) + QStringLiteral("\n"); 0885 break; 0886 case MQTTClient::WillStatisticsType::StandardDeviation: 0887 statistics += QStringLiteral("Standard deviation: ") + QString::number(col->statistics().standardDeviation) + QStringLiteral("\n"); 0888 break; 0889 case MQTTClient::WillStatisticsType::Variance: 0890 statistics += QStringLiteral("Variance: ") + QString::number(col->statistics().variance) + QStringLiteral("\n"); 0891 break; 0892 case MQTTClient::WillStatisticsType::NoStatistics: 0893 default: 0894 break; 0895 } 0896 } 0897 } 0898 return statistics; 0899 } 0900 0901 /*! 0902 * \brief Returns the MQTTClient's will update type 0903 */ 0904 MQTTClient::WillUpdateType MQTTClient::willUpdateType() const { 0905 return m_MQTTWill.willUpdateType; 0906 } 0907 0908 /*! 0909 * \brief Sets the MQTTClient's will update type 0910 * 0911 * \param willUpdateType 0912 */ 0913 void MQTTClient::setWillUpdateType(WillUpdateType willUpdateType) { 0914 m_MQTTWill.willUpdateType = willUpdateType; 0915 } 0916 0917 /*! 0918 * \brief Returns the time interval of updating the MQTTClient's will message 0919 */ 0920 int MQTTClient::willTimeInterval() const { 0921 return m_MQTTWill.willTimeInterval; 0922 } 0923 0924 /*! 0925 * \brief Sets the time interval of updating the MQTTClient's will message, if update type is TimePeriod 0926 * 0927 * \param interval 0928 */ 0929 void MQTTClient::setWillTimeInterval(int interval) { 0930 m_MQTTWill.willTimeInterval = interval; 0931 } 0932 0933 /*! 0934 * \brief Clear the lastly received message by the will topic 0935 * Called when the will topic is changed 0936 */ 0937 void MQTTClient::clearLastMessage() { 0938 m_MQTTWill.willLastMessage.clear(); 0939 } 0940 0941 /*! 0942 * \brief Sets true the corresponding flag of the statistic type, 0943 * what means that the given statistic type will be added to the will message 0944 * 0945 * \param statistics 0946 */ 0947 void MQTTClient::addWillStatistics(WillStatisticsType statistic) { 0948 m_MQTTWill.willStatistics[static_cast<int>(statistic)] = true; 0949 } 0950 0951 /*! 0952 * \brief Sets false the corresponding flag of the statistic type, 0953 * what means that the given statistic will no longer be added to the will message 0954 * 0955 * \param statistics 0956 */ 0957 void MQTTClient::removeWillStatistics(WillStatisticsType statistic) { 0958 m_MQTTWill.willStatistics[static_cast<int>(statistic)] = false; 0959 } 0960 0961 /*! 0962 * \brief Returns a bool vector, meaning which statistic types are included in the will message 0963 * If the corresponding value is true, the statistic type is included, otherwise it isn't 0964 */ 0965 QVector<bool> MQTTClient::willStatistics() const { 0966 return m_MQTTWill.willStatistics; 0967 } 0968 0969 /*! 0970 * \brief Starts the will timer, which will update the will message 0971 */ 0972 void MQTTClient::startWillTimer() const { 0973 if (m_MQTTWill.willUpdateType == WillUpdateType::TimePeriod) 0974 m_willTimer->start(m_MQTTWill.willTimeInterval); 0975 } 0976 0977 /*! 0978 * \brief Stops the will timer 0979 */ 0980 void MQTTClient::stopWillTimer() const { 0981 m_willTimer->stop(); 0982 } 0983 0984 // ############################################################################## 0985 // ################################# SLOTS #################################### 0986 // ############################################################################## 0987 0988 /*! 0989 *\brief called periodically when update type is TimeInterval 0990 */ 0991 void MQTTClient::read() { 0992 if (!m_filter) 0993 return; 0994 0995 if (!m_prepared) { 0996 // qDebug()<<"Connect"; 0997 // connect to the broker 0998 m_client->connectToHost(); 0999 m_prepared = true; 1000 } 1001 1002 if ((m_client->state() == QMqttClient::ClientState::Connected) && m_MQTTFirstConnectEstablished) { 1003 // qDebug()<<"Read"; 1004 // Signal for every MQTTTopic that they can read 1005 Q_EMIT readFromTopics(); 1006 } 1007 } 1008 1009 /*! 1010 *\brief called when the client successfully connected to the broker 1011 */ 1012 void MQTTClient::onMQTTConnect() { 1013 if (m_client->error() == QMqttClient::NoError) { 1014 // if this is the first connection (after setting the options in ImportFileWidget or loading saved project) 1015 if (!m_MQTTFirstConnectEstablished) { 1016 // qDebug()<<"connection made in MQTTClient"; 1017 1018 // Subscribe to initial or loaded topics 1019 QMapIterator<QMqttTopicFilter, quint8> i(m_subscribedTopicNameQoS); 1020 while (i.hasNext()) { 1021 i.next(); 1022 // qDebug()<<i.key(); 1023 QMqttSubscription* temp = m_client->subscribe(i.key(), i.value()); 1024 if (temp) { 1025 // If we didn't load the MQTTClient from xml we have to add the MQTTSubscriptions 1026 if (!m_loaded) { 1027 m_subscriptions.push_back(temp->topic().filter()); 1028 1029 auto* newSubscription = new MQTTSubscription(temp->topic().filter()); 1030 newSubscription->setMQTTClient(this); 1031 1032 addChildFast(newSubscription); 1033 1034 m_MQTTSubscriptions.push_back(newSubscription); 1035 } 1036 1037 connect(temp, &QMqttSubscription::messageReceived, this, &MQTTClient::MQTTSubscriptionMessageReceived); 1038 } 1039 } 1040 m_MQTTFirstConnectEstablished = true; 1041 // Signal that the initial subscriptions were made 1042 Q_EMIT MQTTSubscribed(); 1043 } 1044 // if there was already a connection made(happens after updating will message) 1045 else { 1046 // qDebug() << "Start resubscribing after will message update"; 1047 // Only the client has to make the subscriptions again, every other connected data is still available 1048 QMapIterator<QMqttTopicFilter, quint8> i(m_subscribedTopicNameQoS); 1049 while (i.hasNext()) { 1050 i.next(); 1051 QMqttSubscription* temp = m_client->subscribe(i.key(), i.value()); 1052 if (temp) { 1053 // qDebug()<<temp->topic()<<" "<<temp->qos(); 1054 connect(temp, &QMqttSubscription::messageReceived, this, &MQTTClient::MQTTSubscriptionMessageReceived); 1055 } 1056 // else 1057 // qDebug()<<"Couldn't subscribe after will update"; 1058 } 1059 } 1060 } 1061 } 1062 1063 /*! 1064 *\brief called when a message is received by a topic belonging to one of subscriptions of the client. 1065 * It passes the message to the appropriate MQTTSubscription which will pass it to the appropriate MQTTTopic 1066 */ 1067 void MQTTClient::MQTTSubscriptionMessageReceived(const QMqttMessage& msg) { 1068 // Decide to interpret retain message or not 1069 if (!msg.retain() || m_MQTTRetain) { 1070 // If this is the first message from the topic, save its name 1071 if (!m_topicNames.contains(msg.topic().name())) 1072 m_topicNames.push_back(msg.topic().name()); 1073 1074 // Pass the message and the topic name to the MQTTSubscription which contains the topic 1075 for (auto* subscription : m_MQTTSubscriptions) { 1076 if (checkTopicContains(subscription->subscriptionName(), msg.topic().name())) { 1077 subscription->messageArrived(QLatin1String(msg.payload()), msg.topic().name()); 1078 break; 1079 } 1080 } 1081 1082 // if the message was received by the will topic, update the last message received by it 1083 if (msg.topic().name() == m_MQTTWill.willTopic) { 1084 m_MQTTWill.willLastMessage = QLatin1String(msg.payload()); 1085 1086 Q_EMIT MQTTTopicsChanged(); 1087 } 1088 } 1089 } 1090 1091 /*! 1092 *\brief Handles some of the possible errors of the client, using MQTTErrorWidget 1093 */ 1094 void MQTTClient::MQTTErrorChanged(QMqttClient::ClientError clientError) { 1095 if (clientError != QMqttClient::ClientError::NoError) { 1096 auto* errorWidget = new MQTTErrorWidget(clientError, this); 1097 errorWidget->show(); 1098 } 1099 } 1100 1101 /*! 1102 *\brief Called when a subscription is loaded. 1103 * Checks whether every saved subscription was loaded or not. 1104 * If everything is loaded, it makes the connection and starts the reading 1105 * 1106 * \param name, the name of the subscription 1107 */ 1108 void MQTTClient::subscriptionLoaded(const QString& name) { 1109 if (!name.isEmpty()) { 1110 // qDebug() << "Finished loading: " << name; 1111 // Save information about the subscription 1112 m_subscriptionsLoaded++; 1113 m_subscriptions.push_back(name); 1114 QMqttTopicFilter filter{name}; 1115 m_subscribedTopicNameQoS[filter] = 0; 1116 1117 // Save the topics belonging to the subscription 1118 for (const auto* subscription : m_MQTTSubscriptions) { 1119 if (subscription->subscriptionName() == name) { 1120 const auto& topics = subscription->topics(); 1121 for (auto* topic : topics) { 1122 m_topicNames.push_back(topic->topicName()); 1123 } 1124 break; 1125 } 1126 } 1127 1128 // Check whether every subscription was loaded or not 1129 if (m_subscriptionsLoaded == m_subscriptionCountToLoad) { 1130 // if everything was loaded we can start reading 1131 m_loaded = true; 1132 read(); 1133 } 1134 } 1135 } 1136 1137 // ############################################################################## 1138 // ################## Serialization/Deserialization ########################### 1139 // ############################################################################## 1140 /*! 1141 Saves as XML. 1142 */ 1143 void MQTTClient::save(QXmlStreamWriter* writer) const { 1144 writer->writeStartElement(QStringLiteral("MQTTClient")); 1145 writeBasicAttributes(writer); 1146 writeCommentElement(writer); 1147 1148 // general 1149 writer->writeStartElement(QStringLiteral("general")); 1150 writer->writeAttribute(QStringLiteral("subscriptionCount"), QString::number(m_MQTTSubscriptions.size())); 1151 writer->writeAttribute(QStringLiteral("updateType"), QString::number(static_cast<int>(m_updateType))); 1152 writer->writeAttribute(QStringLiteral("readingType"), QString::number(static_cast<int>(m_readingType))); 1153 writer->writeAttribute(QStringLiteral("keepValues"), QString::number(m_keepNValues)); 1154 1155 if (m_updateType == UpdateType::TimeInterval) 1156 writer->writeAttribute(QStringLiteral("updateInterval"), QString::number(m_updateInterval)); 1157 1158 if (m_readingType != ReadingType::TillEnd) 1159 writer->writeAttribute(QStringLiteral("sampleSize"), QString::number(m_sampleSize)); 1160 1161 writer->writeAttribute(QStringLiteral("host"), m_client->hostname()); 1162 writer->writeAttribute(QStringLiteral("port"), QString::number(m_client->port())); 1163 writer->writeAttribute(QStringLiteral("username"), m_client->username()); 1164 writer->writeAttribute(QStringLiteral("password"), m_client->password()); 1165 writer->writeAttribute(QStringLiteral("clientId"), m_client->clientId()); 1166 writer->writeAttribute(QStringLiteral("useRetain"), QString::number(m_MQTTRetain)); 1167 writer->writeAttribute(QStringLiteral("useWill"), QString::number(m_MQTTWill.enabled)); 1168 writer->writeAttribute(QStringLiteral("willTopic"), m_MQTTWill.willTopic); 1169 writer->writeAttribute(QStringLiteral("willOwnMessage"), m_MQTTWill.willOwnMessage); 1170 writer->writeAttribute(QStringLiteral("willQoS"), QString::number(m_MQTTWill.willQoS)); 1171 writer->writeAttribute(QStringLiteral("willRetain"), QString::number(m_MQTTWill.willRetain)); 1172 writer->writeAttribute(QStringLiteral("willMessageType"), QString::number(static_cast<int>(m_MQTTWill.willMessageType))); 1173 writer->writeAttribute(QStringLiteral("willUpdateType"), QString::number(static_cast<int>(m_MQTTWill.willUpdateType))); 1174 writer->writeAttribute(QStringLiteral("willTimeInterval"), QString::number(m_MQTTWill.willTimeInterval)); 1175 1176 for (int i = 0; i < m_MQTTWill.willStatistics.count(); ++i) 1177 writer->writeAttribute(QStringLiteral("willStatistics") + QString::number(i), QString::number(m_MQTTWill.willStatistics[i])); 1178 writer->writeAttribute(QStringLiteral("useID"), QString::number(m_MQTTUseID)); 1179 writer->writeAttribute(QStringLiteral("useAuthentication"), QString::number(m_MQTTUseAuthentication)); 1180 1181 writer->writeEndElement(); 1182 1183 // filter 1184 m_filter->save(writer); 1185 1186 // MQTTSubscription 1187 for (auto* sub : children<MQTTSubscription>(AbstractAspect::ChildIndexFlag::IncludeHidden)) 1188 sub->save(writer); 1189 1190 writer->writeEndElement(); // "MQTTClient" 1191 } 1192 1193 /*! 1194 Loads from XML. 1195 */ 1196 bool MQTTClient::load(XmlStreamReader* reader, bool preview) { 1197 if (!readBasicAttributes(reader)) 1198 return false; 1199 1200 QString attributeWarning = i18n("Attribute '%1' missing or empty, default value is used"); 1201 QXmlStreamAttributes attribs; 1202 QString str; 1203 1204 while (!reader->atEnd()) { 1205 reader->readNext(); 1206 if (reader->isEndElement() && reader->name() == QLatin1String("MQTTClient")) 1207 break; 1208 1209 if (!reader->isStartElement()) 1210 continue; 1211 1212 if (reader->name() == QLatin1String("comment")) { 1213 if (!readCommentElement(reader)) 1214 return false; 1215 } else if (reader->name() == QLatin1String("general")) { 1216 attribs = reader->attributes(); 1217 1218 str = attribs.value(QStringLiteral("subscriptionCount")).toString(); 1219 if (str.isEmpty()) 1220 reader->raiseWarning(attributeWarning.arg(QStringLiteral("'subscriptionCount'"))); 1221 else 1222 m_subscriptionCountToLoad = str.toInt(); 1223 1224 str = attribs.value(QStringLiteral("keepValues")).toString(); 1225 if (str.isEmpty()) 1226 reader->raiseWarning(attributeWarning.arg(QStringLiteral("'keepValues'"))); 1227 else 1228 m_keepNValues = str.toInt(); 1229 1230 str = attribs.value(QStringLiteral("updateType")).toString(); 1231 if (str.isEmpty()) 1232 reader->raiseWarning(attributeWarning.arg(QStringLiteral("'updateType'"))); 1233 else 1234 m_updateType = static_cast<UpdateType>(str.toInt()); 1235 1236 str = attribs.value(QStringLiteral("readingType")).toString(); 1237 if (str.isEmpty()) 1238 reader->raiseWarning(attributeWarning.arg(QStringLiteral("'readingType'"))); 1239 else 1240 m_readingType = static_cast<ReadingType>(str.toInt()); 1241 1242 if (m_updateType == UpdateType::TimeInterval) { 1243 str = attribs.value(QStringLiteral("updateInterval")).toString(); 1244 if (str.isEmpty()) 1245 reader->raiseWarning(attributeWarning.arg(QStringLiteral("'updateInterval'"))); 1246 else 1247 m_updateInterval = str.toInt(); 1248 } 1249 1250 if (m_readingType != ReadingType::TillEnd) { 1251 str = attribs.value(QStringLiteral("sampleSize")).toString(); 1252 if (str.isEmpty()) 1253 reader->raiseWarning(attributeWarning.arg(QStringLiteral("'sampleSize'"))); 1254 else 1255 m_sampleSize = str.toInt(); 1256 } 1257 1258 str = attribs.value(QStringLiteral("host")).toString(); 1259 if (str.isEmpty()) 1260 reader->raiseWarning(attributeWarning.arg(QStringLiteral("'host'"))); 1261 else 1262 m_client->setHostname(str); 1263 1264 str = attribs.value(QStringLiteral("port")).toString(); 1265 if (str.isEmpty()) 1266 reader->raiseWarning(attributeWarning.arg(QStringLiteral("'port'"))); 1267 else 1268 m_client->setPort(str.toUInt()); 1269 1270 str = attribs.value(QStringLiteral("useAuthentication")).toString(); 1271 if (str.isEmpty()) 1272 reader->raiseWarning(attributeWarning.arg(QStringLiteral("'useAuthentication'"))); 1273 else 1274 m_MQTTUseAuthentication = str.toInt(); 1275 1276 if (m_MQTTUseAuthentication) { 1277 str = attribs.value(QStringLiteral("username")).toString(); 1278 if (!str.isEmpty()) 1279 m_client->setUsername(str); 1280 1281 str = attribs.value(QStringLiteral("password")).toString(); 1282 if (!str.isEmpty()) 1283 m_client->setPassword(str); 1284 } 1285 1286 str = attribs.value(QStringLiteral("useID")).toString(); 1287 if (str.isEmpty()) 1288 reader->raiseWarning(attributeWarning.arg(QStringLiteral("'useID'"))); 1289 else 1290 m_MQTTUseID = str.toInt(); 1291 1292 if (m_MQTTUseID) { 1293 str = attribs.value(QStringLiteral("clientId")).toString(); 1294 if (!str.isEmpty()) 1295 m_client->setClientId(str); 1296 } 1297 1298 str = attribs.value(QStringLiteral("useRetain")).toString(); 1299 if (str.isEmpty()) 1300 reader->raiseWarning(attributeWarning.arg(QStringLiteral("'useRetain'"))); 1301 else 1302 m_MQTTRetain = str.toInt(); 1303 1304 str = attribs.value(QStringLiteral("useWill")).toString(); 1305 if (str.isEmpty()) 1306 reader->raiseWarning(attributeWarning.arg(QStringLiteral("'useWill'"))); 1307 else 1308 m_MQTTWill.enabled = str.toInt(); 1309 1310 if (m_MQTTWill.enabled) { 1311 str = attribs.value(QStringLiteral("willTopic")).toString(); 1312 if (str.isEmpty()) 1313 reader->raiseWarning(attributeWarning.arg(QStringLiteral("'willTopic'"))); 1314 else 1315 m_MQTTWill.willTopic = str; 1316 1317 str = attribs.value(QStringLiteral("willOwnMessage")).toString(); 1318 if (str.isEmpty()) 1319 reader->raiseWarning(attributeWarning.arg(QStringLiteral("'willOwnMessage'"))); 1320 else 1321 m_MQTTWill.willOwnMessage = str; 1322 1323 str = attribs.value(QStringLiteral("willQoS")).toString(); 1324 if (str.isEmpty()) 1325 reader->raiseWarning(attributeWarning.arg(QStringLiteral("'willQoS'"))); 1326 else 1327 m_MQTTWill.willQoS = str.toUInt(); 1328 1329 str = attribs.value(QStringLiteral("willRetain")).toString(); 1330 if (str.isEmpty()) 1331 reader->raiseWarning(attributeWarning.arg(QStringLiteral("'willRetain'"))); 1332 else 1333 m_MQTTWill.willRetain = str.toInt(); 1334 1335 str = attribs.value(QStringLiteral("willMessageType")).toString(); 1336 if (str.isEmpty()) 1337 reader->raiseWarning(attributeWarning.arg(QStringLiteral("'willMessageType'"))); 1338 else 1339 m_MQTTWill.willMessageType = static_cast<MQTTClient::WillMessageType>(str.toInt()); 1340 1341 str = attribs.value(QStringLiteral("willUpdateType")).toString(); 1342 if (str.isEmpty()) 1343 reader->raiseWarning(attributeWarning.arg(QStringLiteral("'willUpdateType'"))); 1344 else 1345 m_MQTTWill.willUpdateType = static_cast<MQTTClient::WillUpdateType>(str.toInt()); 1346 1347 str = attribs.value(QStringLiteral("willTimeInterval")).toString(); 1348 if (str.isEmpty()) 1349 reader->raiseWarning(attributeWarning.arg(QStringLiteral("'willTimeInterval'"))); 1350 else 1351 m_MQTTWill.willTimeInterval = str.toInt(); 1352 1353 for (int i = 0; i < m_MQTTWill.willStatistics.count(); ++i) { 1354 str = attribs.value(QStringLiteral("willStatistics") + QString::number(i)).toString(); 1355 if (str.isEmpty()) 1356 reader->raiseWarning(attributeWarning.arg(QStringLiteral("'willTimeInterval'"))); 1357 else 1358 m_MQTTWill.willStatistics[i] = str.toInt(); 1359 } 1360 } 1361 } else if (reader->name() == QLatin1String("asciiFilter")) { 1362 setFilter(new AsciiFilter); 1363 if (!m_filter->load(reader)) 1364 return false; 1365 } else if (reader->name() == QLatin1String("MQTTSubscription")) { 1366 auto* subscription = new MQTTSubscription(QString()); 1367 subscription->setMQTTClient(this); 1368 m_MQTTSubscriptions.push_back(subscription); 1369 connect(subscription, &MQTTSubscription::loaded, this, &MQTTClient::subscriptionLoaded); 1370 if (!subscription->load(reader, preview)) { 1371 delete subscription; 1372 return false; 1373 } 1374 addChildFast(subscription); 1375 } else { // unknown element 1376 reader->raiseWarning(i18n("unknown element '%1'", reader->name().toString())); 1377 if (!reader->skipToEndElement()) 1378 return false; 1379 } 1380 } 1381 1382 return !reader->hasError(); 1383 }