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 }