aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSona Kurazyan <sona.kurazyan@qt.io>2019-02-28 15:59:06 +0100
committerSona Kurazyan <sona.kurazyan@qt.io>2019-03-20 08:18:45 +0000
commitdb37d03101ba46f2180e7b27ed4e3a44f23cb0fb (patch)
treee1fd5ac327a7deb39feedb630f51356425345a30
parent7a3552189ca2493bba025012243763a2cf5ebb06 (diff)
Add CoAP client example
Simple Qt Widgets based example demonstrating how to use CoAP API. Covers: - Sending GET/POST/PUT/DELETE requests - Observing resources - Resource discovery Change-Id: I2abb3fd670ff86958e73331413d1f0d0e8ce4a5d Reviewed-by: Karsten Heimrich <karsten.heimrich@qt.io> Reviewed-by: Edward Welbourne <edward.welbourne@qt.io>
-rw-r--r--examples/examples.pro1
-rw-r--r--examples/simplecoapclient/main.cpp62
-rw-r--r--examples/simplecoapclient/mainwindow.cpp248
-rw-r--r--examples/simplecoapclient/mainwindow.h99
-rw-r--r--examples/simplecoapclient/mainwindow.ui360
-rw-r--r--examples/simplecoapclient/optiondialog.cpp121
-rw-r--r--examples/simplecoapclient/optiondialog.h84
-rw-r--r--examples/simplecoapclient/optiondialog.ui154
-rw-r--r--examples/simplecoapclient/simplecoapclient.pro33
9 files changed, 1162 insertions, 0 deletions
diff --git a/examples/examples.pro b/examples/examples.pro
index 0490212..619c9e5 100644
--- a/examples/examples.pro
+++ b/examples/examples.pro
@@ -2,4 +2,5 @@ TEMPLATE = subdirs
CONFIG += no_docs_target
SUBDIRS += \
+ simplecoapclient \
testapp
diff --git a/examples/simplecoapclient/main.cpp b/examples/simplecoapclient/main.cpp
new file mode 100644
index 0000000..2968a11
--- /dev/null
+++ b/examples/simplecoapclient/main.cpp
@@ -0,0 +1,62 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples of the QtCoap module.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "mainwindow.h"
+
+#include <QApplication>
+
+int main(int argc, char *argv[])
+{
+ QApplication a(argc, argv);
+ MainWindow w;
+ w.show();
+
+ return a.exec();
+}
diff --git a/examples/simplecoapclient/mainwindow.cpp b/examples/simplecoapclient/mainwindow.cpp
new file mode 100644
index 0000000..f133650
--- /dev/null
+++ b/examples/simplecoapclient/mainwindow.cpp
@@ -0,0 +1,248 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples of the QtCoap module.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "mainwindow.h"
+#include "optiondialog.h"
+#include "ui_mainwindow.h"
+
+#include <QCoapDiscoveryReply>
+#include <QCoapReply>
+#include <QDateTime>
+#include <QFileDialog>
+#include <QMessageBox>
+#include <QMetaEnum>
+#include <QNetworkInterface>
+
+MainWindow::MainWindow(QWidget *parent) :
+ QMainWindow(parent),
+ ui(new Ui::MainWindow),
+ m_client(new QCoapClient(QtCoap::NoSec, this))
+{
+ ui->setupUi(this);
+
+ ui->methodComboBox->addItem("Get", QtCoap::Method::Get);
+ ui->methodComboBox->addItem("Put", QtCoap::Method::Put);
+ ui->methodComboBox->addItem("Post", QtCoap::Method::Post);
+ ui->methodComboBox->addItem("Delete", QtCoap::Method::Delete);
+
+ fillHostSelector();
+ ui->hostComboBox->setFocus();
+
+ connect(m_client, &QCoapClient::finished, this, &MainWindow::onFinished);
+ connect(m_client, &QCoapClient::error, this, &MainWindow::onError);
+}
+
+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 = "--------------- "
+ + QDateTime::currentDateTime().toString()
+ + " ---------------\n"
+ + message + "\n\n";
+ ui->textEdit->setTextColor(isError ? Qt::red : Qt::black);
+ ui->textEdit->insertPlainText(content);
+ ui->textEdit->ensureCursorVisible();
+}
+
+void MainWindow::onFinished(QCoapReply *reply)
+{
+ if (reply->errorReceived() == QtCoap::NoError)
+ addMessage(reply->message().payload());
+}
+
+static QString errorMessage(QtCoap::Error errorCode)
+{
+ const auto error = QMetaEnum::fromType<QtCoap::Error>().valueToKey(errorCode);
+ return QString("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(QCoapDiscoveryReply *reply, QVector<QCoapResource> resources)
+{
+ if (reply->errorReceived() != QtCoap::NoError)
+ return;
+
+ QString message;
+ for (const auto &resource : resources) {
+ ui->resourceComboBox->addItem(resource.path());
+ message += "Discovered resource: \"" + resource.title() + "\" on path "
+ + resource.path() + "\n";
+ }
+ addMessage(message);
+}
+
+void MainWindow::onNotified(QCoapReply *reply, const QCoapMessage &message)
+{
+ if (reply->errorReceived() == QtCoap::NoError)
+ addMessage("Received observe notification with payload: " + message.payload());
+}
+
+void MainWindow::on_runButton_clicked()
+{
+ const auto msgType = ui->msgTypeCheckBox->isChecked() ? QCoapMessage::Confirmable
+ : QCoapMessage::NonConfirmable;
+ QUrl url;
+ url.setHost(ui->hostComboBox->currentText());
+ url.setPort(ui->portSpinBox->value());
+ url.setPath(ui->resourceComboBox->currentText());
+
+ QCoapRequest request(url, msgType);
+ for (const auto &option : m_options)
+ request.addOption(option);
+ m_options.clear();
+
+ const auto method = ui->methodComboBox->currentData(Qt::UserRole).value<QtCoap::Method>();
+ switch (method) {
+ case QtCoap::Get:
+ m_client->get(request);
+ break;
+ case QtCoap::Put:
+ m_client->put(request, m_currentData);
+ break;
+ case QtCoap::Post:
+ m_client->post(request, m_currentData);
+ break;
+ case QtCoap::Delete:
+ m_client->deleteResource(request);
+ break;
+ default:
+ break;
+ }
+ m_currentData.clear();
+}
+
+void MainWindow::on_discoverButton_clicked()
+{
+ QUrl url;
+ url.setHost(ui->hostComboBox->currentText());
+ url.setPort(ui->portSpinBox->value());
+
+ QCoapDiscoveryReply *discoverReply = m_client->discover(url, ui->discoveryPathEdit->text());
+ if (discoverReply)
+ connect(discoverReply, &QCoapDiscoveryReply::discovered, this, &MainWindow::onDiscovered);
+ else
+ QMessageBox::critical(this, "Error", "Something went wrong, discovery request failed.");
+}
+
+void MainWindow::on_observeButton_clicked()
+{
+ QUrl url;
+ url.setHost(ui->hostComboBox->currentText());
+ url.setPort(ui->portSpinBox->value());
+ url.setPath(ui->resourceComboBox->currentText());
+
+ QCoapReply *observeReply = m_client->observe(url);
+ if (!observeReply) {
+ QMessageBox::critical(this, "Error", "Something went wrong, observe request failed.");
+ return;
+ }
+
+ connect(observeReply, &QCoapReply::notified, this, &MainWindow::onNotified);
+
+ ui->cancelObserveButton->setEnabled(true);
+ connect(ui->cancelObserveButton, &QPushButton::clicked, this, [&]() {
+ m_client->cancelObserve(url);
+ ui->cancelObserveButton->setEnabled(false);
+ });
+}
+
+void MainWindow::on_addOptionsButton_clicked()
+{
+ OptionDialog dialog;
+ 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, "Error", QString("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::Put || method == QtCoap::Post);
+}
diff --git a/examples/simplecoapclient/mainwindow.h b/examples/simplecoapclient/mainwindow.h
new file mode 100644
index 0000000..a561c0e
--- /dev/null
+++ b/examples/simplecoapclient/mainwindow.h
@@ -0,0 +1,99 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples of the QtCoap module.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef MAINWINDOW_H
+#define MAINWINDOW_H
+
+#include <QCoapClient>
+#include <QCoapOption>
+#include <QCoapResource>
+#include <QMainWindow>
+
+QT_BEGIN_NAMESPACE
+namespace Ui {
+class MainWindow;
+}
+class QCoapMessage;
+QT_END_NAMESPACE
+
+class MainWindow : public QMainWindow
+{
+ Q_OBJECT
+
+public:
+ explicit MainWindow(QWidget *parent = nullptr);
+ ~MainWindow();
+
+private:
+ void fillHostSelector();
+ void addMessage(const QString &message, bool isError = false);
+
+private slots:
+ void onFinished(QCoapReply *reply);
+ void onError(QCoapReply *reply, QtCoap::Error error);
+ void onDiscovered(QCoapDiscoveryReply *reply, QVector<QCoapResource> resources);
+ void onNotified(QCoapReply *reply, const QCoapMessage &message);
+
+ void on_runButton_clicked();
+ void on_discoverButton_clicked();
+ void on_observeButton_clicked();
+ void on_addOptionsButton_clicked();
+ void on_contentButton_clicked();
+ void on_resourceComboBox_editTextChanged(const QString &text);
+ void on_methodComboBox_currentIndexChanged(int index);
+
+private:
+ Ui::MainWindow *ui;
+ QCoapClient *m_client;
+ QVector<QCoapOption> m_options;
+ QByteArray m_currentData;
+};
+
+#endif // MAINWINDOW_H
diff --git a/examples/simplecoapclient/mainwindow.ui b/examples/simplecoapclient/mainwindow.ui
new file mode 100644
index 0000000..f6cff95
--- /dev/null
+++ b/examples/simplecoapclient/mainwindow.ui
@@ -0,0 +1,360 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>MainWindow</class>
+ <widget class="QMainWindow" name="MainWindow">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>731</width>
+ <height>530</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>CoAP Client</string>
+ </property>
+ <widget class="QWidget" name="centralWidget">
+ <layout class="QGridLayout" name="gridLayout_3">
+ <item row="0" column="0">
+ <layout class="QGridLayout" name="gridLayout">
+ <item row="0" column="0">
+ <widget class="QLabel" name="label">
+ <property name="text">
+ <string>Host</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="4" column="0">
+ <widget class="QLabel" name="label_4">
+ <property name="text">
+ <string>Request Method</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="1">
+ <layout class="QHBoxLayout" name="horizontalLayout_2">
+ <item>
+ <widget class="QComboBox" name="resourceComboBox">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="editable">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="observeButton">
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>Observe</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer name="horizontalSpacer_3">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </item>
+ <item row="0" column="1">
+ <layout class="QHBoxLayout" name="horizontalLayout_5">
+ <item>
+ <widget class="QComboBox" name="hostComboBox">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>250</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="editable">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer name="horizontalSpacer">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>QSizePolicy::Expanding</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </item>
+ <item row="4" column="1">
+ <layout class="QHBoxLayout" name="horizontalLayout_3">
+ <item>
+ <widget class="QComboBox" name="methodComboBox">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>150</width>
+ <height>0</height>
+ </size>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QCheckBox" name="msgTypeCheckBox">
+ <property name="text">
+ <string>Confirmable</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="addOptionsButton">
+ <property name="text">
+ <string>Add Options</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="contentButton">
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>Add Content</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer name="horizontalSpacer_4">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </item>
+ <item row="2" column="0">
+ <widget class="QLabel" name="label_8">
+ <property name="text">
+ <string>Disscovery Path:</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="6" column="0" colspan="2">
+ <widget class="QGroupBox" name="groupBox">
+ <property name="title">
+ <string>Output</string>
+ </property>
+ <layout class="QGridLayout" name="gridLayout_2">
+ <item row="0" column="0">
+ <widget class="QTextEdit" name="textEdit">
+ <property name="readOnly">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QLabel" name="label_2">
+ <property name="text">
+ <string>Port</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="5" column="1">
+ <layout class="QHBoxLayout" name="horizontalLayout_12">
+ <item>
+ <widget class="QPushButton" name="runButton">
+ <property name="text">
+ <string>Run</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="cancelObserveButton">
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>Cancel Observe</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer name="horizontalSpacer_6">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </item>
+ <item row="3" column="0">
+ <widget class="QLabel" name="label_3">
+ <property name="text">
+ <string>Resource</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="1">
+ <layout class="QHBoxLayout" name="horizontalLayout_13">
+ <item>
+ <widget class="QLineEdit" name="discoveryPathEdit">
+ <property name="text">
+ <string>/.well-known/core</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="discoverButton">
+ <property name="text">
+ <string>Discover</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer name="horizontalSpacer_7">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </item>
+ <item row="1" column="1">
+ <layout class="QHBoxLayout" name="horizontalLayout_6">
+ <item>
+ <widget class="QSpinBox" name="portSpinBox">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>100</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximum">
+ <number>65535</number>
+ </property>
+ <property name="value">
+ <number>5683</number>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer name="horizontalSpacer_2">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </item>
+ <item row="5" column="0">
+ <widget class="QLabel" name="label_7">
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ <widget class="QMenuBar" name="menuBar">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>731</width>
+ <height>22</height>
+ </rect>
+ </property>
+ </widget>
+ <widget class="QToolBar" name="mainToolBar">
+ <attribute name="toolBarArea">
+ <enum>TopToolBarArea</enum>
+ </attribute>
+ <attribute name="toolBarBreak">
+ <bool>false</bool>
+ </attribute>
+ </widget>
+ <widget class="QStatusBar" name="statusBar"/>
+ </widget>
+ <layoutdefault spacing="6" margin="11"/>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/examples/simplecoapclient/optiondialog.cpp b/examples/simplecoapclient/optiondialog.cpp
new file mode 100644
index 0000000..5125541
--- /dev/null
+++ b/examples/simplecoapclient/optiondialog.cpp
@@ -0,0 +1,121 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples of the QtCoap module.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "optiondialog.h"
+#include "ui_optiondialog.h"
+
+OptionDialog::OptionDialog(QWidget *parent) :
+ QDialog(parent),
+ ui(new Ui::OptionDialog)
+{
+ ui->setupUi(this);
+
+ fillOptions();
+
+ auto header = ui->tableWidget->horizontalHeader();
+ header->setSectionResizeMode(QHeaderView::Stretch);
+}
+
+OptionDialog::~OptionDialog()
+{
+ delete ui;
+}
+
+QVector<QCoapOption> OptionDialog::options() const
+{
+ return m_options;
+}
+
+void OptionDialog::on_addButton_clicked()
+{
+ const auto option =
+ ui->optionComboBox->currentData(Qt::UserRole).value<QCoapOption::OptionName>();
+ m_options.push_back(QCoapOption(option, ui->optionValueEdit->text()));
+
+ const auto rowCount = ui->tableWidget->rowCount();
+ ui->tableWidget->insertRow(rowCount);
+
+ QTableWidgetItem *optionItem = new QTableWidgetItem(ui->optionComboBox->currentText());
+ optionItem->setFlags(optionItem->flags() ^ Qt::ItemIsEditable);
+ ui->tableWidget->setItem(rowCount, 0, optionItem);
+
+ QTableWidgetItem *valueItem = new QTableWidgetItem(ui->optionValueEdit->text());
+ valueItem->setFlags(valueItem->flags() ^ Qt::ItemIsEditable);
+ ui->tableWidget->setItem(rowCount, 1, valueItem);
+}
+
+void OptionDialog::on_clearButton_clicked()
+{
+ m_options.clear();
+ ui->tableWidget->setRowCount(0);
+}
+
+void OptionDialog::fillOptions()
+{
+ ui->tableWidget->setHorizontalHeaderLabels({"Name", "Value"});
+ ui->optionComboBox->addItem("None", QCoapOption::Invalid);
+ ui->optionComboBox->addItem("Block1", QCoapOption::Block1);
+ ui->optionComboBox->addItem("Block2", QCoapOption::Block2);
+ ui->optionComboBox->addItem("Content-Format", QCoapOption::ContentFormat);
+ ui->optionComboBox->addItem("If-Match", QCoapOption::IfMatch);
+ ui->optionComboBox->addItem("If-None-Match", QCoapOption::IfNoneMatch);
+ ui->optionComboBox->addItem("Location-Path", QCoapOption::LocationPath);
+ ui->optionComboBox->addItem("Location-Query", QCoapOption::LocationQuery);
+ ui->optionComboBox->addItem("Max-Age", QCoapOption::MaxAge);
+ ui->optionComboBox->addItem("Observe", QCoapOption::Observe);
+ ui->optionComboBox->addItem("Proxy-Scheme", QCoapOption::ProxyScheme);
+ ui->optionComboBox->addItem("Proxy-Uri", QCoapOption::ProxyUri);
+ ui->optionComboBox->addItem("Size1", QCoapOption::Size1);
+ ui->optionComboBox->addItem("Size2", QCoapOption::Size2);
+ ui->optionComboBox->addItem("Uri-Host", QCoapOption::UriHost);
+ ui->optionComboBox->addItem("Uri-Path", QCoapOption::UriPath);
+ ui->optionComboBox->addItem("Uri-Port", QCoapOption::UriPort);
+ ui->optionComboBox->addItem("Uri-Query", QCoapOption::UriQuery);
+}
diff --git a/examples/simplecoapclient/optiondialog.h b/examples/simplecoapclient/optiondialog.h
new file mode 100644
index 0000000..ecd0b34
--- /dev/null
+++ b/examples/simplecoapclient/optiondialog.h
@@ -0,0 +1,84 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples of the QtCoap module.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef OPTIONDIALOG_H
+#define OPTIONDIALOG_H
+
+#include <QCoapOption>
+#include <QDialog>
+
+QT_BEGIN_NAMESPACE
+namespace Ui {
+class OptionDialog;
+}
+QT_END_NAMESPACE
+
+class OptionDialog : public QDialog
+{
+ Q_OBJECT
+
+public:
+ explicit OptionDialog(QWidget *parent = nullptr);
+ ~OptionDialog();
+
+ QVector<QCoapOption> options() const;
+
+private slots:
+ void on_addButton_clicked();
+ void on_clearButton_clicked();
+
+private:
+ void fillOptions();
+
+ Ui::OptionDialog *ui;
+ QVector<QCoapOption> m_options;
+};
+
+#endif // OPTIONDIALOG_H
diff --git a/examples/simplecoapclient/optiondialog.ui b/examples/simplecoapclient/optiondialog.ui
new file mode 100644
index 0000000..7313b32
--- /dev/null
+++ b/examples/simplecoapclient/optiondialog.ui
@@ -0,0 +1,154 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>OptionDialog</class>
+ <widget class="QDialog" name="OptionDialog">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>414</width>
+ <height>313</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Add Options</string>
+ </property>
+ <layout class="QHBoxLayout" name="horizontalLayout_3">
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout_2">
+ <item>
+ <layout class="QVBoxLayout" name="verticalLayout_2">
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout">
+ <item>
+ <widget class="QLabel" name="label">
+ <property name="text">
+ <string>Option</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QComboBox" name="optionComboBox">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>120</width>
+ <height>0</height>
+ </size>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLineEdit" name="optionValueEdit"/>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <widget class="QTableWidget" name="tableWidget">
+ <property name="columnCount">
+ <number>2</number>
+ </property>
+ <column/>
+ <column/>
+ </widget>
+ </item>
+ <item>
+ <widget class="QDialogButtonBox" name="buttonBox">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="standardButtons">
+ <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <item>
+ <widget class="QPushButton" name="addButton">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>100</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="text">
+ <string>Add</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="clearButton">
+ <property name="text">
+ <string>Clear</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer name="verticalSpacer">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections>
+ <connection>
+ <sender>buttonBox</sender>
+ <signal>accepted()</signal>
+ <receiver>OptionDialog</receiver>
+ <slot>accept()</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>152</x>
+ <y>289</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>206</x>
+ <y>156</y>
+ </hint>
+ </hints>
+ </connection>
+ <connection>
+ <sender>buttonBox</sender>
+ <signal>rejected()</signal>
+ <receiver>OptionDialog</receiver>
+ <slot>reject()</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>152</x>
+ <y>289</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>206</x>
+ <y>156</y>
+ </hint>
+ </hints>
+ </connection>
+ </connections>
+</ui>
diff --git a/examples/simplecoapclient/simplecoapclient.pro b/examples/simplecoapclient/simplecoapclient.pro
new file mode 100644
index 0000000..16fa118
--- /dev/null
+++ b/examples/simplecoapclient/simplecoapclient.pro
@@ -0,0 +1,33 @@
+QT += core gui network coap widgets
+
+TARGET = simplecoapclient
+TEMPLATE = app
+
+# The following define makes your compiler emit warnings if you use
+# any feature of Qt which has been marked as deprecated (the exact warnings
+# depend on your compiler). Please consult the documentation of the
+# deprecated API in order to know how to port your code away from it.
+DEFINES += QT_DEPRECATED_WARNINGS
+
+# You can also make your code fail to compile if you use deprecated APIs.
+# In order to do so, uncomment the following line.
+# You can also select to disable deprecated APIs only up to a certain version of Qt.
+#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
+
+CONFIG += c++11
+
+SOURCES += \
+ main.cpp \
+ mainwindow.cpp \
+ optiondialog.cpp
+
+HEADERS += \
+ mainwindow.h \
+ optiondialog.h
+
+FORMS += \
+ mainwindow.ui \
+ optiondialog.ui
+
+target.path = $$[QT_INSTALL_EXAMPLES]/simplecoapclient
+INSTALLS += target