File indexing completed on 2024-05-19 15:01:37

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