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 }