File indexing completed on 2024-05-12 15:28:00
0001 /*************************************************************************** 0002 File : LiveDataDock.cpp 0003 Project : LabPlot 0004 Description : Dock widget for live data properties 0005 -------------------------------------------------------------------- 0006 Copyright : (C) 2017 by Fabian Kristof (fkristofszabolcs@gmail.com) 0007 Copyright : (C) 2018-2019 Kovacs Ferencz (kferike98@gmail.com) 0008 Copyright : (C) 2018 by Stefan Gerlach (stefan.gerlach@uni.kn) 0009 Copyright : (C) 2017-2020 Alexander Semke (alexander.semke@web.de) 0010 ***************************************************************************/ 0011 0012 /*************************************************************************** 0013 * * 0014 * This program is free software; you can redistribute it and/or modify * 0015 * it under the terms of the GNU General Public License as published by * 0016 * the Free Software Foundation; either version 2 of the License, or * 0017 * (at your option) any later version. * 0018 * * 0019 * This program is distributed in the hope that it will be useful, * 0020 * but WITHOUT ANY WARRANTY; without even the implied warranty of * 0021 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 0022 * GNU General Public License for more details. * 0023 * * 0024 * You should have received a copy of the GNU General Public License * 0025 * along with this program; if not, write to the Free Software * 0026 * Foundation, Inc., 51 Franklin Street, Fifth Floor, * 0027 * Boston, MA 02110-1301 USA * 0028 * * 0029 ***************************************************************************/ 0030 #include "LiveDataDock.h" 0031 #include "kdefrontend/GuiTools.h" 0032 0033 #include <QCompleter> 0034 #include <QFile> 0035 #include <QStandardItemModel> 0036 #include <QTimer> 0037 #include <QTreeWidgetItem> 0038 0039 #include <KLocalizedString> 0040 0041 #ifdef HAVE_MQTT 0042 #include "kdefrontend/widgets/MQTTWillSettingsWidget.h" 0043 #include "kdefrontend/datasources/MQTTSubscriptionWidget.h" 0044 #include <QMessageBox> 0045 #include <QWidgetAction> 0046 #include <QMenu> 0047 #endif 0048 0049 LiveDataDock::LiveDataDock(QWidget* parent) : BaseDock(parent) 0050 #ifdef HAVE_MQTT 0051 , 0052 m_subscriptionWidget(new MQTTSubscriptionWidget(this)) 0053 #endif 0054 { 0055 ui.setupUi(this); 0056 m_leName = ui.leName; 0057 //leComment = // not available 0058 0059 ui.bUpdateNow->setIcon(QIcon::fromTheme(QLatin1String("view-refresh"))); 0060 0061 connect(ui.leName, &QLineEdit::textChanged, this, &LiveDataDock::nameChanged); 0062 connect(ui.bPausePlayReading, &QPushButton::clicked, this, &LiveDataDock::pauseContinueReading); 0063 connect(ui.bUpdateNow, &QPushButton::clicked, this, &LiveDataDock::updateNow); 0064 connect(ui.sbUpdateInterval, static_cast<void (QSpinBox::*) (int)>(&QSpinBox::valueChanged), this, &LiveDataDock::updateIntervalChanged); 0065 0066 connect(ui.sbKeepNValues, static_cast<void (QSpinBox::*) (int)>(&QSpinBox::valueChanged), this, &LiveDataDock::keepNValuesChanged); 0067 connect(ui.sbSampleSize, static_cast<void (QSpinBox::*) (int)>(&QSpinBox::valueChanged), this, &LiveDataDock::sampleSizeChanged); 0068 connect(ui.cbUpdateType, static_cast<void (QComboBox::*) (int)>(&QComboBox::currentIndexChanged), this, &LiveDataDock::updateTypeChanged); 0069 connect(ui.cbReadingType, static_cast<void (QComboBox::*) (int)>(&QComboBox::currentIndexChanged), this, &LiveDataDock::readingTypeChanged); 0070 0071 #ifdef HAVE_MQTT 0072 connect(ui.bWillUpdateNow, &QPushButton::clicked, this, &LiveDataDock::willUpdateNow); 0073 connect(ui.bLWT, &QPushButton::clicked, this, &LiveDataDock::showWillSettings); 0074 connect(m_subscriptionWidget, &MQTTSubscriptionWidget::enableWill, this, &LiveDataDock::enableWill); 0075 0076 ui.swSubscriptions->addWidget(m_subscriptionWidget); 0077 ui.swSubscriptions->setCurrentWidget(m_subscriptionWidget); 0078 0079 ui.bLWT->setToolTip(i18n("Manage MQTT connection's will settings")); 0080 ui.bLWT->setIcon(ui.bLWT->style()->standardIcon(QStyle::SP_FileDialogDetailedView)); 0081 0082 QString info = i18n("Specify the 'Last Will and Testament' message (LWT). At least one topic has to be subscribed."); 0083 ui.lLWT->setToolTip(info); 0084 ui.bLWT->setToolTip(info); 0085 ui.bLWT->setEnabled(false); 0086 ui.bLWT->setIcon(ui.bLWT->style()->standardIcon(QStyle::SP_FileDialogDetailedView)); 0087 #endif 0088 } 0089 0090 #ifdef HAVE_MQTT 0091 LiveDataDock::~LiveDataDock() { 0092 for (auto & host : m_hosts) 0093 delete host.client; 0094 0095 delete m_subscriptionWidget; 0096 } 0097 #else 0098 LiveDataDock::~LiveDataDock() = default; 0099 #endif 0100 0101 #ifdef HAVE_MQTT 0102 /*! 0103 * \brief Sets the MQTTClient of this dock widget 0104 * \param clients 0105 */ 0106 void LiveDataDock::setMQTTClient(MQTTClient* const client) { 0107 m_liveDataSource = nullptr; // prevent updates due to changes to input widgets 0108 auto oldclient = m_mqttClient; 0109 m_mqttClient = nullptr; // prevent updates due to changes to input widgets 0110 const QPair<QString, quint16> id(client->clientHostName(), client->clientPort()); 0111 0112 ui.leName->setText(client->name()); 0113 ui.leSourceInfo->setText(QStringLiteral("%1:%2").arg(id.first).arg(id.second)); 0114 ui.sbUpdateInterval->setValue(client->updateInterval()); 0115 ui.cbUpdateType->setCurrentIndex(static_cast<int>(client->updateType())); 0116 ui.cbReadingType->setCurrentIndex(static_cast<int>(client->readingType())); 0117 0118 if (client->updateType() == MQTTClient::UpdateType::NewData) { 0119 ui.lUpdateInterval->hide(); 0120 ui.sbUpdateInterval->hide(); 0121 } 0122 0123 m_paused = client->isPaused(); 0124 if (m_paused) { 0125 ui.bPausePlayReading->setText(i18n("Continue reading")); 0126 ui.bPausePlayReading->setIcon(QIcon::fromTheme(QLatin1String("media-record"))); 0127 } else { 0128 ui.bPausePlayReading->setText(i18n("Pause reading")); 0129 ui.bPausePlayReading->setIcon(QIcon::fromTheme(QLatin1String("media-playback-pause"))); 0130 } 0131 0132 ui.sbKeepNValues->setValue(client->keepNValues()); 0133 ui.sbKeepNValues->setEnabled(true); 0134 0135 if (client->readingType() == MQTTClient::ReadingType::TillEnd) { 0136 ui.lSampleSize->hide(); 0137 ui.sbSampleSize->hide(); 0138 } else 0139 ui.sbSampleSize->setValue(client->sampleSize()); 0140 0141 // disable "whole file" option 0142 const auto* model = qobject_cast<const QStandardItemModel*>(ui.cbReadingType->model()); 0143 QStandardItem* item = model->item(static_cast<int>(LiveDataSource::ReadingType::WholeFile)); 0144 item->setFlags(item->flags() & ~(Qt::ItemIsSelectable | Qt::ItemIsEnabled)); 0145 if (ui.cbReadingType->currentIndex() == static_cast<int>(LiveDataSource::ReadingType::WholeFile)) 0146 ui.cbReadingType->setCurrentIndex(static_cast<int>(LiveDataSource::ReadingType::TillEnd)); 0147 0148 m_mqttClient = client; // updates may be applied from now on 0149 0150 //show MQTT connected options 0151 ui.lTopics->show(); 0152 ui.swSubscriptions->setVisible(true); 0153 m_subscriptionWidget->setVisible(true); 0154 m_subscriptionWidget->makeVisible(true); 0155 ui.lLWT->show(); 0156 ui.bLWT->show(); 0157 0158 m_previousHost = m_currentHost; 0159 0160 //if there isn't a client with this hostname we instantiate a new one 0161 auto it = m_hosts.find(id); 0162 if (it == m_hosts.end()) { 0163 m_currentHost = &m_hosts[id]; 0164 m_currentHost->count = 1; 0165 m_currentHost->client = new QMqttClient; 0166 0167 connect(client, &MQTTClient::clientAboutToBeDeleted, this, &LiveDataDock::removeClient); 0168 0169 connect(m_currentHost->client, &QMqttClient::connected, this, &LiveDataDock::onMQTTConnect); 0170 connect(m_currentHost->client, &QMqttClient::messageReceived, this, &LiveDataDock::mqttMessageReceived); 0171 0172 connect(m_subscriptionWidget, &MQTTSubscriptionWidget::reparentTopic, client, &MQTTClient::reparentTopic); 0173 connect(m_subscriptionWidget, &MQTTSubscriptionWidget::addBeforeRemoveSubscription, client, &MQTTClient::addBeforeRemoveSubscription); 0174 connect(m_subscriptionWidget, &MQTTSubscriptionWidget::removeMQTTSubscription, client, &MQTTClient::removeMQTTSubscription); 0175 connect(m_subscriptionWidget, &MQTTSubscriptionWidget::makeSubscription, client, &MQTTClient::addMQTTSubscription); 0176 0177 m_currentHost->client->setHostname(id.first); 0178 m_currentHost->client->setPort(id.second); 0179 0180 if (client->MQTTUseAuthentication()) { 0181 m_currentHost->client->setUsername(client->clientUserName()); 0182 m_currentHost->client->setPassword(client->clientPassword()); 0183 } 0184 0185 if (client->MQTTUseID()) 0186 m_currentHost->client->setClientId(client->clientID()); 0187 0188 m_currentHost->client->connectToHost(); 0189 } else { 0190 m_currentHost = &it.value(); 0191 ++m_currentHost->count; 0192 } 0193 0194 if (m_previousMQTTClient == nullptr) { 0195 m_updateSubscriptionConn = connect(client, &MQTTClient::MQTTSubscribed, [this]() { 0196 emit updateSubscriptionTree(m_mqttClient->MQTTSubscriptions()); 0197 }); 0198 0199 //Fill the subscription tree(useful if the MQTTClient was loaded) 0200 QVector<QString> topics = client->topicNames(); 0201 for (const auto& topic : topics) 0202 addTopicToTree(topic); 0203 emit updateSubscriptionTree(m_mqttClient->MQTTSubscriptions()); 0204 } 0205 0206 //if the previous MQTTClient's host name was different from the current one we have to disconnect some slots 0207 //and clear the tree widgets 0208 else if (m_previousMQTTClient->clientHostName() != client->clientHostName()) { 0209 disconnect(m_updateSubscriptionConn); 0210 disconnect(m_previousHost->client, &QMqttClient::messageReceived, this, &LiveDataDock::mqttMessageReceived); 0211 connect(m_previousHost->client, &QMqttClient::messageReceived, this, &LiveDataDock::mqttMessageReceivedInBackground); 0212 0213 disconnect(m_currentHost->client, &QMqttClient::messageReceived, this, &LiveDataDock::mqttMessageReceivedInBackground); 0214 0215 disconnect(m_subscriptionWidget, &MQTTSubscriptionWidget::reparentTopic, m_previousMQTTClient, &MQTTClient::reparentTopic); 0216 disconnect(m_subscriptionWidget, &MQTTSubscriptionWidget::addBeforeRemoveSubscription, m_previousMQTTClient, &MQTTClient::addBeforeRemoveSubscription); 0217 disconnect(m_subscriptionWidget, &MQTTSubscriptionWidget::removeMQTTSubscription, m_previousMQTTClient, &MQTTClient::removeMQTTSubscription); 0218 disconnect(m_subscriptionWidget, &MQTTSubscriptionWidget::makeSubscription, m_previousMQTTClient, &MQTTClient::addMQTTSubscription); 0219 0220 m_previousHost->topicList = m_subscriptionWidget->getTopicList(); 0221 m_subscriptionWidget->setTopicList(m_currentHost->topicList); 0222 0223 emit MQTTClearTopics(); 0224 //repopulating the tree widget with the already known topics of the client 0225 for (const auto& topic : m_currentHost->addedTopics) 0226 addTopicToTree(topic); 0227 0228 //fill subscriptions tree widget 0229 emit updateSubscriptionTree(m_mqttClient->MQTTSubscriptions()); 0230 0231 m_updateSubscriptionConn = connect(client, &MQTTClient::MQTTSubscribed, [this]() { 0232 emit updateSubscriptionTree(m_mqttClient->MQTTSubscriptions()); 0233 }); 0234 connect(m_currentHost->client, &QMqttClient::messageReceived, this, &LiveDataDock::mqttMessageReceived); 0235 0236 connect(m_subscriptionWidget, &MQTTSubscriptionWidget::reparentTopic, client, &MQTTClient::reparentTopic); 0237 connect(m_subscriptionWidget, &MQTTSubscriptionWidget::addBeforeRemoveSubscription, client, &MQTTClient::addBeforeRemoveSubscription); 0238 connect(m_subscriptionWidget, &MQTTSubscriptionWidget::removeMQTTSubscription, client, &MQTTClient::removeMQTTSubscription); 0239 connect(m_subscriptionWidget, &MQTTSubscriptionWidget::makeSubscription, client, &MQTTClient::addMQTTSubscription); 0240 } 0241 0242 if (client->willUpdateType() == MQTTClient::WillUpdateType::OnClick && client->MQTTWillUse()) 0243 ui.bWillUpdateNow->show(); 0244 0245 m_previousMQTTClient = oldclient; 0246 } 0247 #endif 0248 0249 /*! 0250 * \brief Sets the live data source of this dock widget 0251 * \param source 0252 */ 0253 void LiveDataDock::setLiveDataSource(LiveDataSource* const source) { 0254 #ifdef HAVE_MQTT 0255 m_mqttClient = nullptr; 0256 #endif 0257 m_liveDataSource = nullptr; // prevent updates due to changes to input widgets 0258 0259 ui.leName->setText(source->name()); 0260 ui.leName->setStyleSheet(""); 0261 ui.leName->setToolTip(""); 0262 const LiveDataSource::SourceType sourceType = source->sourceType(); 0263 const LiveDataSource::ReadingType readingType = source->readingType(); 0264 const LiveDataSource::UpdateType updateType = source->updateType(); 0265 const AbstractFileFilter::FileType fileType = source->fileType(); 0266 ui.sbUpdateInterval->setValue(source->updateInterval()); 0267 ui.cbUpdateType->setCurrentIndex(static_cast<int>(updateType)); 0268 ui.cbReadingType->setCurrentIndex(static_cast<int>(readingType)); 0269 0270 switch (sourceType) { 0271 case LiveDataSource::SourceType::FileOrPipe: { 0272 ui.leSourceInfo->setText(source->fileName()); 0273 bool invalid = !QFile::exists(source->fileName()); 0274 GuiTools::highlight(ui.leSourceInfo, invalid); 0275 break; 0276 } 0277 case LiveDataSource::SourceType::NetworkTcpSocket: 0278 case LiveDataSource::SourceType::NetworkUdpSocket: 0279 ui.leSourceInfo->setText(QStringLiteral("%1:%2").arg(source->host()).arg(source->port())); 0280 break; 0281 case LiveDataSource::SourceType::LocalSocket: 0282 ui.leSourceInfo->setText(source->localSocketName()); 0283 break; 0284 case LiveDataSource::SourceType::SerialPort: 0285 ui.leSourceInfo->setText(source->serialPortName()); 0286 break; 0287 case LiveDataSource::SourceType::MQTT: 0288 break; 0289 } 0290 0291 if (updateType == LiveDataSource::UpdateType::NewData) { 0292 ui.lUpdateInterval->hide(); 0293 ui.sbUpdateInterval->hide(); 0294 } 0295 0296 m_paused = source->isPaused(); 0297 if (m_paused) { 0298 ui.bPausePlayReading->setText(i18n("Continue Reading")); 0299 ui.bPausePlayReading->setIcon(QIcon::fromTheme(QLatin1String("media-record"))); 0300 } else { 0301 ui.bPausePlayReading->setText(i18n("Pause Reading")); 0302 ui.bPausePlayReading->setIcon(QIcon::fromTheme(QLatin1String("media-playback-pause"))); 0303 } 0304 0305 ui.sbKeepNValues->setValue(source->keepNValues()); 0306 0307 // disable "whole file" when having no file (i.e. socket or port) 0308 auto* model = qobject_cast<const QStandardItemModel*>(ui.cbReadingType->model()); 0309 QStandardItem* item = model->item(static_cast<int>(LiveDataSource::ReadingType::WholeFile)); 0310 if (sourceType == LiveDataSource::SourceType::FileOrPipe) { 0311 item->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled); 0312 //for file types other than ASCII and binary we support re-reading the whole file only 0313 //select "read whole file" and deactivate the combobox 0314 if (fileType != AbstractFileFilter::FileType::Ascii && fileType != AbstractFileFilter::FileType::Binary) { 0315 ui.cbReadingType->setCurrentIndex(static_cast<int>(LiveDataSource::ReadingType::WholeFile)); 0316 ui.cbReadingType->setEnabled(false); 0317 } else 0318 ui.cbReadingType->setEnabled(true); 0319 } else { 0320 if (ui.cbReadingType->currentIndex() == static_cast<int>(LiveDataSource::ReadingType::WholeFile)) 0321 ui.cbReadingType->setCurrentIndex(static_cast<int>(LiveDataSource::ReadingType::TillEnd)); 0322 item->setFlags(item->flags() & ~(Qt::ItemIsSelectable | Qt::ItemIsEnabled)); 0323 } 0324 0325 if (((sourceType == LiveDataSource::SourceType::FileOrPipe || sourceType == LiveDataSource::SourceType::NetworkUdpSocket) && 0326 (readingType == LiveDataSource::ReadingType::ContinuousFixed || readingType == LiveDataSource::ReadingType::FromEnd))) 0327 ui.sbSampleSize->setValue(source->sampleSize()); 0328 else { 0329 ui.lSampleSize->hide(); 0330 ui.sbSampleSize->hide(); 0331 } 0332 0333 // disable "on new data"-option if not available 0334 model = qobject_cast<const QStandardItemModel*>(ui.cbUpdateType->model()); 0335 item = model->item(static_cast<int>(LiveDataSource::UpdateType::NewData)); 0336 if (sourceType == LiveDataSource::SourceType::NetworkTcpSocket || sourceType == LiveDataSource::SourceType::NetworkUdpSocket || 0337 sourceType == LiveDataSource::SourceType::SerialPort) 0338 item->setFlags(item->flags() & ~(Qt::ItemIsSelectable | Qt::ItemIsEnabled)); 0339 else 0340 item->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled); 0341 0342 ui.lTopics->hide(); 0343 ui.bLWT->hide(); 0344 ui.lLWT->hide(); 0345 ui.bWillUpdateNow->hide(); 0346 ui.swSubscriptions->hide(); 0347 #ifdef HAVE_MQTT 0348 m_subscriptionWidget->hide(); 0349 m_subscriptionWidget->hide(); 0350 #endif 0351 m_liveDataSource = source; // updates may be applied from now on 0352 } 0353 0354 /*! 0355 * \brief Modifies the sample size of the live data source or MQTTClient object 0356 * \param sampleSize 0357 */ 0358 void LiveDataDock::sampleSizeChanged(int sampleSize) { 0359 if (m_liveDataSource) 0360 m_liveDataSource->setSampleSize(sampleSize); 0361 #ifdef HAVE_MQTT 0362 else if (m_mqttClient) 0363 m_mqttClient->setSampleSize(sampleSize); 0364 #endif 0365 } 0366 0367 /*! 0368 * \brief Updates the live data source now 0369 */ 0370 void LiveDataDock::updateNow() { 0371 if (m_liveDataSource) 0372 m_liveDataSource->updateNow(); 0373 #ifdef HAVE_MQTT 0374 else if (m_mqttClient) 0375 m_mqttClient->updateNow(); 0376 #endif 0377 } 0378 0379 void LiveDataDock::nameChanged(const QString& name) { 0380 if (m_liveDataSource) { 0381 if (!m_liveDataSource->setName(name, false)) { 0382 ui.leName->setStyleSheet("background:red;"); 0383 ui.leName->setToolTip(i18n("Please choose another name, because this is already in use.")); 0384 return; 0385 } 0386 } 0387 #ifdef HAVE_MQTT 0388 else if (m_mqttClient) { 0389 if (!m_mqttClient->setName(name, false)) { 0390 ui.leName->setStyleSheet("background:red;"); 0391 ui.leName->setToolTip(i18n("Please choose another name, because this is already in use.")); 0392 return; 0393 } 0394 } 0395 #endif 0396 ui.leName->setStyleSheet(""); 0397 ui.leName->setToolTip(""); 0398 } 0399 0400 /*! 0401 * \brief LiveDataDock::updateTypeChanged 0402 * \param idx 0403 */ 0404 void LiveDataDock::updateTypeChanged(int idx) { 0405 if (m_liveDataSource) { 0406 DEBUG("LiveDataDock::updateTypeChanged()"); 0407 const auto updateType = static_cast<LiveDataSource::UpdateType>(idx); 0408 0409 switch (updateType) { 0410 case LiveDataSource::UpdateType::TimeInterval: { 0411 ui.lUpdateInterval->show(); 0412 ui.sbUpdateInterval->show(); 0413 const auto s = m_liveDataSource->sourceType(); 0414 const auto r = m_liveDataSource->readingType(); 0415 const bool showSampleSize = ((s == LiveDataSource::SourceType::FileOrPipe || s == LiveDataSource::SourceType::NetworkUdpSocket) && 0416 (r == LiveDataSource::ReadingType::ContinuousFixed || r == LiveDataSource::ReadingType::FromEnd)); 0417 ui.lSampleSize->setVisible(showSampleSize); 0418 ui.sbSampleSize->setVisible(showSampleSize); 0419 0420 m_liveDataSource->setUpdateType(updateType); 0421 m_liveDataSource->setUpdateInterval(ui.sbUpdateInterval->value()); 0422 break; 0423 } 0424 case LiveDataSource::UpdateType::NewData: 0425 ui.lUpdateInterval->hide(); 0426 ui.sbUpdateInterval->hide(); 0427 ui.lSampleSize->hide(); 0428 ui.sbSampleSize->hide(); 0429 0430 m_liveDataSource->setUpdateType(updateType); 0431 } 0432 } 0433 #ifdef HAVE_MQTT 0434 else if (m_mqttClient) { 0435 DEBUG("LiveDataDock::updateTypeChanged()"); 0436 const auto type = static_cast<MQTTClient::UpdateType>(idx); 0437 0438 if (type == MQTTClient::UpdateType::TimeInterval) { 0439 ui.lUpdateInterval->show(); 0440 ui.sbUpdateInterval->show(); 0441 0442 m_mqttClient->setUpdateType(type); 0443 m_mqttClient->setUpdateInterval(ui.sbUpdateInterval->value()); 0444 } else if (type == MQTTClient::UpdateType::NewData) { 0445 ui.lUpdateInterval->hide(); 0446 ui.sbUpdateInterval->hide(); 0447 0448 m_mqttClient->setUpdateType(type); 0449 } 0450 } 0451 #endif 0452 } 0453 0454 /*! 0455 * \brief Handles the change of the reading type in the dock widget 0456 * \param idx 0457 */ 0458 void LiveDataDock::readingTypeChanged(int idx) { 0459 if (m_liveDataSource) { 0460 const auto type = static_cast<LiveDataSource::ReadingType>(idx); 0461 const auto sourceType = m_liveDataSource->sourceType(); 0462 const auto updateType = m_liveDataSource->updateType(); 0463 0464 if (sourceType == LiveDataSource::SourceType::NetworkTcpSocket || sourceType == LiveDataSource::SourceType::LocalSocket 0465 || sourceType == LiveDataSource::SourceType::SerialPort 0466 || type == LiveDataSource::ReadingType::TillEnd || type == LiveDataSource::ReadingType::WholeFile 0467 || updateType == LiveDataSource::UpdateType::NewData) { 0468 ui.lSampleSize->hide(); 0469 ui.sbSampleSize->hide(); 0470 } else { 0471 ui.lSampleSize->show(); 0472 ui.sbSampleSize->show(); 0473 } 0474 0475 m_liveDataSource->setReadingType(type); 0476 } 0477 #ifdef HAVE_MQTT 0478 else if (m_mqttClient) { 0479 auto type = static_cast<MQTTClient::ReadingType>(idx); 0480 0481 if (type == MQTTClient::ReadingType::TillEnd) { 0482 ui.lSampleSize->hide(); 0483 ui.sbSampleSize->hide(); 0484 } else { 0485 ui.lSampleSize->show(); 0486 ui.sbSampleSize->show(); 0487 } 0488 0489 m_mqttClient->setReadingType(type); 0490 } 0491 #endif 0492 } 0493 0494 /*! 0495 * \brief Modifies the update interval of the live data source 0496 * \param updateInterval 0497 */ 0498 void LiveDataDock::updateIntervalChanged(int updateInterval) { 0499 if (m_liveDataSource) 0500 m_liveDataSource->setUpdateInterval(updateInterval); 0501 #ifdef HAVE_MQTT 0502 else if (m_mqttClient) 0503 m_mqttClient->setUpdateInterval(updateInterval); 0504 #endif 0505 } 0506 0507 /*! 0508 * \brief Modifies the number of samples to keep in each of the live data source 0509 * \param keepNValues 0510 */ 0511 void LiveDataDock::keepNValuesChanged(const int keepNValues) { 0512 if (m_liveDataSource) 0513 m_liveDataSource->setKeepNValues(keepNValues); 0514 #ifdef HAVE_MQTT 0515 else if (m_mqttClient) 0516 m_mqttClient->setKeepNValues(keepNValues); 0517 #endif 0518 } 0519 0520 /*! 0521 * \brief Pauses the reading of the live data source 0522 */ 0523 void LiveDataDock::pauseReading() { 0524 if (m_liveDataSource) 0525 m_liveDataSource->pauseReading(); 0526 #ifdef HAVE_MQTT 0527 else if (m_mqttClient) 0528 m_mqttClient->pauseReading(); 0529 #endif 0530 } 0531 0532 /*! 0533 * \brief Continues the reading of the live data source 0534 */ 0535 void LiveDataDock::continueReading() { 0536 if (m_liveDataSource) 0537 m_liveDataSource->continueReading(); 0538 #ifdef HAVE_MQTT 0539 else if (m_mqttClient) 0540 m_mqttClient->continueReading(); 0541 #endif 0542 } 0543 0544 /*! 0545 * \brief Handles the pausing/continuing of reading of the live data source 0546 */ 0547 void LiveDataDock::pauseContinueReading() { 0548 m_paused = !m_paused; 0549 0550 if (m_paused) { 0551 pauseReading(); 0552 ui.bPausePlayReading->setText(i18n("Continue Reading")); 0553 ui.bPausePlayReading->setIcon(QIcon::fromTheme(QLatin1String("media-record"))); 0554 } else { 0555 continueReading(); 0556 ui.bPausePlayReading->setText(i18n("Pause Reading")); 0557 ui.bPausePlayReading->setIcon(QIcon::fromTheme(QLatin1String("media-playback-pause"))); 0558 } 0559 } 0560 0561 #ifdef HAVE_MQTT 0562 0563 /*! 0564 *\brief called when use will message checkbox's state is changed in the will settings widget, 0565 * Sets the mqttUseWill according to state for the m_mqttClient 0566 * 0567 * \param state the state of the checbox 0568 */ 0569 void LiveDataDock::useWillMessage(bool use) { 0570 if (use) { 0571 m_mqttClient->setMQTTWillUse(true); 0572 if (m_mqttClient->willUpdateType() == MQTTClient::WillUpdateType::OnClick) 0573 ui.bWillUpdateNow->show(); 0574 } else { 0575 m_mqttClient->setMQTTWillUse(false); 0576 ui.bWillUpdateNow->hide(); 0577 } 0578 } 0579 0580 /*! 0581 *\brief called when will message's QoS is changed in the will settings widget 0582 * sets the will QoS level for the m_mqttClient 0583 * 0584 * \param QoS the QoS level of the will message 0585 */ 0586 void LiveDataDock::willQoSChanged(int QoS) { 0587 m_mqttClient->setWillQoS(QoS); 0588 } 0589 0590 /*! 0591 *\brief called when will message's retain flag is changed in the will settings widget 0592 * sets the retain flag for the will message in in m_mqttClient 0593 * 0594 * \param state the state of the will retain chechbox 0595 */ 0596 void LiveDataDock::willRetainChanged(bool useWillRetainMessages) { 0597 if (useWillRetainMessages) 0598 m_mqttClient->setWillRetain(true); 0599 else 0600 m_mqttClient->setWillRetain(false); 0601 } 0602 0603 /*! 0604 *\brief called when will topic combobox's current item is changed in the will settings widget 0605 * sets the will topic for the m_mqttClient 0606 * 0607 * \param topic the current text of cbWillTopic 0608 */ 0609 void LiveDataDock::willTopicChanged(const QString& topic) { 0610 if (m_mqttClient->willTopic() != topic) 0611 m_mqttClient->clearLastMessage(); 0612 0613 m_mqttClient->setWillTopic(topic); 0614 } 0615 0616 /*! 0617 *\brief called when the selected will message type is changed in the will settings widget 0618 * sets the will message type for the m_mqttClient 0619 * 0620 * \param type the selected will message type 0621 */ 0622 void LiveDataDock::willMessageTypeChanged(MQTTClient::WillMessageType willMessageType) { 0623 m_mqttClient->setWillMessageType(willMessageType); 0624 } 0625 0626 /*! 0627 *\brief called when the will own message is changed in the will settings widget 0628 * sets the will own message for the m_mqttClient 0629 * 0630 * \param message the will message given by the user 0631 */ 0632 void LiveDataDock::willOwnMessageChanged(const QString& message) { 0633 m_mqttClient->setWillOwnMessage(message); 0634 } 0635 0636 /*! 0637 *\brief called when the selected update type for the will message is changed in the will settings widget 0638 * sets the will update type for the m_mqttClient 0639 * 0640 * \param type the selected will update type 0641 */ 0642 void LiveDataDock::willUpdateTypeChanged(int updateType) { 0643 m_mqttClient->setWillUpdateType(static_cast<MQTTClient::WillUpdateType>(updateType)); 0644 0645 if (updateType == static_cast<int>(MQTTClient::WillUpdateType::TimePeriod)) { 0646 ui.bWillUpdateNow->hide(); 0647 m_mqttClient->startWillTimer(); 0648 } else if (updateType == static_cast<int>(MQTTClient::WillUpdateType::OnClick)) { 0649 ui.bWillUpdateNow->show(); 0650 0651 //if update type is on click we stop the will timer 0652 m_mqttClient->stopWillTimer(); 0653 } 0654 } 0655 0656 /*! 0657 *\brief called when the will update now button is pressed 0658 * updates the will message of m_mqttClient 0659 */ 0660 void LiveDataDock::willUpdateNow() { 0661 m_mqttClient->updateWillMessage(); 0662 } 0663 0664 /*! 0665 *\brief called when the update interval for will message is changed in the will settings widget 0666 * sets the will update interval for the m_mqttClient, then starts the will timer for each one 0667 * 0668 * \param interval the new will update interval 0669 */ 0670 void LiveDataDock::willUpdateIntervalChanged(int interval) { 0671 m_mqttClient->setWillTimeInterval(interval); 0672 m_mqttClient->startWillTimer(); 0673 } 0674 0675 /*! 0676 *\brief called when the will statistics are changed in the will settings widget 0677 * adds or removes the statistic represented by the index from m_mqttClient 0678 */ 0679 void LiveDataDock::statisticsChanged(MQTTClient::WillStatisticsType willStatisticsType) { 0680 if (willStatisticsType != MQTTClient::WillStatisticsType::NoStatistics) { 0681 //if it's not already added and it's checked we add it 0682 if (!m_mqttClient->willStatistics().at(static_cast<int>(willStatisticsType))) 0683 m_mqttClient->addWillStatistics(willStatisticsType); 0684 else //otherwise remove it 0685 m_mqttClient->removeWillStatistics(willStatisticsType); 0686 } 0687 } 0688 0689 /*! 0690 *\brief called when the client connects to the broker successfully, it subscribes to every topic (# wildcard) 0691 * in order to later list every available topic 0692 */ 0693 void LiveDataDock::onMQTTConnect() { 0694 if (!m_currentHost || !m_currentHost->client || !m_currentHost->client->subscribe(QMqttTopicFilter(QLatin1String("#")), 1)) 0695 QMessageBox::critical(this, i18n("Couldn't subscribe"), i18n("Couldn't subscribe to all available topics. Something went wrong")); 0696 } 0697 0698 /*! 0699 *\brief called when the client receives a message 0700 * if the message arrived from a new topic, the topic is put in twTopics 0701 */ 0702 void LiveDataDock::mqttMessageReceived(const QByteArray& message, const QMqttTopicName& topic) { 0703 Q_UNUSED(message) 0704 if (!m_currentHost->addedTopics.contains(topic.name())) { 0705 m_currentHost->addedTopics.push_back(topic.name()); 0706 addTopicToTree(topic.name()); 0707 } 0708 } 0709 0710 /*! 0711 *\brief Adds topicName to twTopics 0712 * 0713 * \param topicName the name of the topic, which will be added to the tree widget 0714 */ 0715 void LiveDataDock::addTopicToTree(const QString &topicName) { 0716 QStringList name; 0717 QChar sep = '/'; 0718 QString rootName; 0719 if (topicName.contains(sep)) { 0720 QStringList list = topicName.split(sep, QString::SkipEmptyParts); 0721 0722 if (!list.isEmpty()) { 0723 rootName = list.at(0); 0724 name.append(list.at(0)); 0725 QTreeWidgetItem* currentItem; 0726 //check whether the first level of the topic can be found in twTopics 0727 int topItemIdx = -1; 0728 for (int i = 0; i < m_subscriptionWidget->topicCount(); ++i) { 0729 if (m_subscriptionWidget->topLevelTopic(i)->text(0) == list.at(0)) { 0730 topItemIdx = i; 0731 break; 0732 } 0733 } 0734 //if not we simply add every level of the topic to the tree 0735 if ( topItemIdx < 0) { 0736 currentItem = new QTreeWidgetItem(name); 0737 m_subscriptionWidget->addTopic(currentItem); 0738 for (int i = 1; i < list.size(); ++i) { 0739 name.clear(); 0740 name.append(list.at(i)); 0741 currentItem->addChild(new QTreeWidgetItem(name)); 0742 currentItem = currentItem->child(0); 0743 } 0744 } 0745 //otherwise we search for the first level that isn't part of the tree, 0746 //then add every level of the topic to the tree from that certain level 0747 else { 0748 currentItem = m_subscriptionWidget->topLevelTopic(topItemIdx); 0749 int listIdx = 1; 0750 for (; listIdx < list.size(); ++listIdx) { 0751 QTreeWidgetItem* childItem = nullptr; 0752 bool found = false; 0753 for (int j = 0; j < currentItem->childCount(); ++j) { 0754 childItem = currentItem->child(j); 0755 if (childItem->text(0) == list.at(listIdx)) { 0756 found = true; 0757 currentItem = childItem; 0758 break; 0759 } 0760 } 0761 if (!found) { 0762 //this is the level that isn't present in the tree 0763 break; 0764 } 0765 } 0766 0767 //add every level to the tree starting with the first level that isn't part of the tree 0768 for (; listIdx < list.size(); ++listIdx) { 0769 name.clear(); 0770 name.append(list.at(listIdx)); 0771 currentItem->addChild(new QTreeWidgetItem(name)); 0772 currentItem = currentItem->child(currentItem->childCount() - 1); 0773 } 0774 } 0775 } 0776 } else { 0777 rootName = topicName; 0778 name.append(topicName); 0779 m_subscriptionWidget->addTopic(new QTreeWidgetItem(name)); 0780 } 0781 0782 //if a subscribed topic contains the new topic, we have to update twSubscriptions 0783 for (int i = 0; i < m_subscriptionWidget->subscriptionCount(); ++i) { 0784 QStringList subscriptionName = m_subscriptionWidget->topLevelSubscription(i)->text(0).split('/', QString::SkipEmptyParts); 0785 if (rootName == subscriptionName[0]) { 0786 emit updateSubscriptionTree(m_mqttClient->MQTTSubscriptions()); 0787 break; 0788 } 0789 } 0790 0791 //signals that a newTopic was added, in order to fill the completer of leTopics 0792 //we have to pass the whole topic name, not just the root name, for testing purposes 0793 emit newTopic(topicName); 0794 } 0795 0796 /*! 0797 *\brief called when a client receives a message, if the clients hostname isn't identic with the host name of MQTTClient 0798 * if the message arrived from a new topic, the topic is added to the host data 0799 */ 0800 void LiveDataDock::mqttMessageReceivedInBackground(const QByteArray& message, const QMqttTopicName& topic) { 0801 Q_UNUSED(message) 0802 if (!m_currentHost->addedTopics.contains(topic.name())) 0803 m_currentHost->addedTopics.push_back(topic.name()); 0804 } 0805 0806 /*! 0807 *\brief called when an MQTTClient is about to be deleted 0808 * removes every data connected to the MQTTClient, and disconnects the corresponding client from the host 0809 * 0810 * \param hostname the host name of the MQTTClient that will be deleted 0811 * \param name the host name of the MQTTClient that will be deleted 0812 */ 0813 void LiveDataDock::removeClient(const QString& hostname, quint16 port) { 0814 auto it = m_hosts.find(qMakePair(hostname, port)); 0815 if (it == m_hosts.end()) 0816 return; 0817 0818 MQTTHost & host = it.value(); 0819 0820 if (host.count > 1) { 0821 --host.count; 0822 return; 0823 } 0824 0825 host.client->disconnectFromHost(); 0826 0827 if (m_previousMQTTClient && m_previousMQTTClient->clientHostName() == hostname) { 0828 disconnect(m_previousHost->client, &QMqttClient::messageReceived, this, &LiveDataDock::mqttMessageReceivedInBackground); 0829 m_previousMQTTClient = nullptr; 0830 } 0831 0832 if (m_mqttClient && m_mqttClient->clientHostName() == hostname) { 0833 emit MQTTClearTopics(); 0834 m_mqttClient = nullptr; 0835 } 0836 0837 delete host.client; 0838 m_hosts.erase(it); 0839 } 0840 0841 /*! 0842 * \brief Used for testing the MQTT related features 0843 * \param topic 0844 */ 0845 bool LiveDataDock::testSubscribe(const QString& topic) { 0846 QStringList topicList = topic.split('/', QString::SkipEmptyParts); 0847 QTreeWidgetItem* currentItem = nullptr; 0848 for (int i = 0; i < m_subscriptionWidget->topicCount(); ++i) { 0849 if (m_subscriptionWidget->topLevelTopic(i)->text(0) == topicList[0]) { 0850 currentItem = m_subscriptionWidget->topLevelTopic(i); 0851 break; 0852 } 0853 } 0854 0855 if (currentItem) { 0856 for (int i = 1 ; i < topicList.size(); ++i) { 0857 if (topicList[i] == '#') 0858 break; 0859 0860 for (int j = 0; j < currentItem->childCount(); ++j) { 0861 if (currentItem->child(j)->text(0) == topicList[i]) { 0862 currentItem = currentItem->child(j); 0863 break; 0864 } else if (j == currentItem->childCount() - 1) 0865 return false; 0866 0867 } 0868 } 0869 } else 0870 return false; 0871 0872 m_subscriptionWidget->testSubscribe(currentItem); 0873 return true; 0874 } 0875 0876 /*! 0877 * \brief Used for testing the MQTT related features 0878 * \param topic 0879 */ 0880 bool LiveDataDock::testUnsubscribe(const QString& topic) { 0881 QTreeWidgetItem* currentItem = nullptr; 0882 for (int i = 0; i < m_subscriptionWidget->subscriptionCount(); ++i) { 0883 if (MQTTSubscriptionWidget::checkTopicContains(m_subscriptionWidget->topLevelSubscription(i)->text(0), topic)) { 0884 currentItem = m_subscriptionWidget->topLevelSubscription(i); 0885 break; 0886 } 0887 } 0888 0889 if (currentItem) { 0890 do { 0891 if (topic == currentItem->text(0)) { 0892 m_subscriptionWidget->testUnsubscribe(currentItem); 0893 return true; 0894 } else { 0895 for (int i = 0; i < currentItem->childCount(); ++i) { 0896 qDebug()<<currentItem->child(i)->text(0)<<" "<<topic; 0897 if (MQTTSubscriptionWidget::checkTopicContains(currentItem->child(i)->text(0), topic)) { 0898 currentItem = currentItem->child(i); 0899 break; 0900 } else if (i == currentItem->childCount() - 1) 0901 return false; 0902 } 0903 } 0904 } while (currentItem); 0905 } else 0906 return false; 0907 0908 return false; 0909 } 0910 0911 void LiveDataDock::showWillSettings() { 0912 QMenu menu; 0913 const QVector<QString>& topics = m_mqttClient->topicNames(); 0914 MQTTWillSettingsWidget willSettingsWidget(&menu, m_mqttClient->willSettings(), topics); 0915 0916 connect(&willSettingsWidget, &MQTTWillSettingsWidget::applyClicked, [this, &menu, &willSettingsWidget]() { 0917 this->useWillMessage(willSettingsWidget.will().enabled); 0918 this->willMessageTypeChanged(willSettingsWidget.will().willMessageType); 0919 this->updateTypeChanged(static_cast<int>(willSettingsWidget.will().willUpdateType)); 0920 this->willRetainChanged(willSettingsWidget.will().willRetain); 0921 this->willUpdateIntervalChanged(willSettingsWidget.will().willTimeInterval); 0922 this->willOwnMessageChanged(willSettingsWidget.will().willOwnMessage); 0923 this->willTopicChanged(willSettingsWidget.will().willTopic); 0924 this->statisticsChanged(willSettingsWidget.statisticsType()); 0925 0926 menu.close(); 0927 }); 0928 0929 auto* widgetAction = new QWidgetAction(this); 0930 widgetAction->setDefaultWidget(&willSettingsWidget); 0931 menu.addAction(widgetAction); 0932 0933 QPoint pos(ui.bLWT->sizeHint().width(), ui.bLWT->sizeHint().height()); 0934 menu.exec(ui.bLWT->mapToGlobal(pos)); 0935 } 0936 0937 void LiveDataDock::enableWill(bool enable) { 0938 if(enable) { 0939 if(!ui.bLWT->isEnabled()) 0940 ui.bLWT->setEnabled(enable); 0941 } else 0942 ui.bLWT->setEnabled(enable); 0943 } 0944 #endif