Simple CoAP Client
// Copyright (C) 2019 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause #include "mainwindow.h" #include "optiondialog.h" #include "ui_mainwindow.h" #include <QCoapClient> #include <QCoapResourceDiscoveryReply> #include <QCoapReply> #include <QDateTime> #include <QFileDialog> #include <QHostInfo> #include <QMessageBox> #include <QMetaEnum> #include <QNetworkInterface> using namespace Qt::StringLiterals; MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow) { m_client = new QCoapClient(QtCoap::SecurityMode::NoSecurity, this); connect(m_client, &QCoapClient::finished, this, &MainWindow::onFinished); connect(m_client, &QCoapClient::error, this, &MainWindow::onError); ui->setupUi(this); ui->methodComboBox->addItem(tr("Get"), QVariant::fromValue(QtCoap::Method::Get)); ui->methodComboBox->addItem(tr("Put"), QVariant::fromValue(QtCoap::Method::Put)); ui->methodComboBox->addItem(tr("Post"), QVariant::fromValue(QtCoap::Method::Post)); ui->methodComboBox->addItem(tr("Delete"), QVariant::fromValue(QtCoap::Method::Delete)); fillHostSelector(); ui->hostComboBox->setFocus(); } MainWindow::~MainWindow() { delete ui; } void MainWindow::fillHostSelector() { const auto networkInterfaces = QNetworkInterface::allInterfaces(); for (const auto &interface : networkInterfaces) for (const auto &address : interface.addressEntries()) ui->hostComboBox->addItem(address.ip().toString()); } void MainWindow::addMessage(const QString &message, bool isError) { const QString content = "--------------- %1 ---------------\n%2\n\n"_L1 .arg(QDateTime::currentDateTime().toString(), message); ui->textEdit->setTextColor(isError ? Qt::red : Qt::black); ui->textEdit->insertPlainText(content); ui->textEdit->ensureCursorVisible(); } void MainWindow::onFinished(QCoapReply *reply) { if (reply->errorReceived() == QtCoap::Error::Ok) addMessage(reply->message().payload()); } static QString errorMessage(QtCoap::Error errorCode) { const auto error = QMetaEnum::fromType<QtCoap::Error>().valueToKey(static_cast<int>(errorCode)); return MainWindow::tr("Request failed with error: %1\n").arg(error); } void MainWindow::onError(QCoapReply *reply, QtCoap::Error error) { const auto errorCode = reply ? reply->errorReceived() : error; addMessage(errorMessage(errorCode), true); } void MainWindow::onDiscovered(QCoapResourceDiscoveryReply *reply, QList<QCoapResource> resources) { if (reply->errorReceived() != QtCoap::Error::Ok) return; QString message; for (const auto &resource : std::as_const(resources)) { ui->resourceComboBox->addItem(resource.path()); message += tr("Discovered resource: \"%1\" on path %2\n") .arg(resource.title(), resource.path()); } addMessage(message); } void MainWindow::onNotified(QCoapReply *reply, const QCoapMessage &message) { if (reply->errorReceived() == QtCoap::Error::Ok) { addMessage(tr("Received observe notification with payload: %1") .arg(QString::fromUtf8(message.payload()))); } } static QString tryToResolveHostName(const QString hostName) { const auto hostInfo = QHostInfo::fromName(hostName); if (!hostInfo.addresses().empty()) return hostInfo.addresses().first().toString(); return hostName; } void MainWindow::on_runButton_clicked() { const auto msgType = ui->msgTypeCheckBox->isChecked() ? QCoapMessage::Type::Confirmable : QCoapMessage::Type::NonConfirmable; QUrl url; url.setHost(tryToResolveHostName(ui->hostComboBox->currentText())); url.setPort(ui->portSpinBox->value()); url.setPath(ui->resourceComboBox->currentText()); QCoapRequest request(url, msgType); for (const auto &option : std::as_const(m_options)) request.addOption(option); m_options.clear(); const auto method = ui->methodComboBox->currentData(Qt::UserRole).value<QtCoap::Method>(); switch (method) { case QtCoap::Method::Get: m_client->get(request); break; case QtCoap::Method::Put: m_client->put(request, m_currentData); break; case QtCoap::Method::Post: m_client->post(request, m_currentData); break; case QtCoap::Method::Delete: m_client->deleteResource(request); break; default: break; } m_currentData.clear(); } void MainWindow::on_discoverButton_clicked() { QUrl url; url.setHost(tryToResolveHostName(ui->hostComboBox->currentText())); url.setPort(ui->portSpinBox->value()); QCoapResourceDiscoveryReply *discoverReply = m_client->discover(url, ui->discoveryPathEdit->text()); if (discoverReply) { connect(discoverReply, &QCoapResourceDiscoveryReply::discovered, this, &MainWindow::onDiscovered); } else { QMessageBox::critical(this, tr("Error"), tr("Something went wrong, discovery request failed.")); } } void MainWindow::on_observeButton_clicked() { QUrl url; url.setHost(tryToResolveHostName(ui->hostComboBox->currentText())); url.setPort(ui->portSpinBox->value()); url.setPath(ui->resourceComboBox->currentText()); QCoapReply *observeReply = m_client->observe(url); if (!observeReply) { QMessageBox::critical(this, tr("Error"), tr("Something went wrong, observe request failed.")); return; } connect(observeReply, &QCoapReply::notified, this, &MainWindow::onNotified); ui->cancelObserveButton->setEnabled(true); connect(ui->cancelObserveButton, &QPushButton::clicked, this, [this, url]() { m_client->cancelObserve(url); ui->cancelObserveButton->setEnabled(false); }); } void MainWindow::on_addOptionsButton_clicked() { OptionDialog dialog(m_options); if (dialog.exec() == QDialog::Accepted) m_options = dialog.options(); } void MainWindow::on_contentButton_clicked() { QFileDialog dialog(this); dialog.setFileMode(QFileDialog::AnyFile); if (!dialog.exec()) return; const auto fileName = dialog.selectedFiles().back(); QFile file(fileName); if (!file.open(QIODevice::ReadOnly)) { QMessageBox::critical(this, tr("Error"), tr("Failed to read from file %1").arg(fileName)); return; } m_currentData = file.readAll(); } void MainWindow::on_resourceComboBox_editTextChanged(const QString &text) { ui->observeButton->setEnabled(!text.isEmpty()); } void MainWindow::on_methodComboBox_currentIndexChanged(int index) { Q_UNUSED(index); const auto method = ui->methodComboBox->currentData(Qt::UserRole).value<QtCoap::Method>(); ui->contentButton->setEnabled(method == QtCoap::Method::Put || method == QtCoap::Method::Post); }