Quick CoAP Multicast Discovery▲
The Quick CoAP Multicast Discovery example demonstrates how to register QCoapClient as a QML type and use it in a Qt Quick application for CoAP multicast resource discovery.
Qt CoAP does not provide a QML API in its current version. However, you can make the C++ classes of the module available to QML as shown in this example.
Running the Example▲
To run the example from Qt Creator, open the Welcome mode and select the example from Examples. For more information, visit Building and Running an Example.
Setting Up a CoAP Server▲
To run the example application, you first need to set up and start at least one CoAP server supporting multicast resource discovery. You have the following options:
-
Manually build and run CoAP servers using libcoap, Californium, or any other CoAP server implementation, which supports multicast and resource discovery features.
-
Use the ready Docker image available at Docker Hub, which builds and starts CoAP server based on Californium's multicast server example.
Using the Docker-based Test Server▲
The following command pulls the docker container for the CoAP server from the Docker Hub and starts it:
docker run --
name coap-
multicast-
server -
d --
rm --
net=
host tqtc/
coap-
multicast-
test-
server:californium-
2.0.0
You can run more than one multicast CoAP servers (on the same host or other hosts in the network) by passing a different --name to the command above.
To terminate the docker container after usage, first obtain the container's ID by executing the docker ps command. The output will look like this:
$ docker ps
CONTAINER ID IMAGE
8
b991fae7789 tqtc/
coap-
multicast-
test-
server:californium-
2.0.0
After that, use this ID to stop the container:
docker stop &
lt;container_id&
gt;
Exposign C++ Classes to QML▲
In this example, you need to expose the QCoapResource and QCoapClient classes, as well as the QtCoap namespace, to QML. To achieve this, create custom wrapper classes and use the special registration macros.
Create the QmlCoapResource class as a wrapper around QCoapResource. Use the Q_PROPERTY macro to make several properties accessible from QML. The class does not need to be directly instantiable from QML, so use the QML_ANONYMOUS macro to register it.
class
QmlCoapResource : public
QCoapResource
{
Q_GADGET
Q_PROPERTY(QString title READ title)
Q_PROPERTY(QString host READ hostStr)
Q_PROPERTY(QString path READ path)
QML_ANONYMOUS
public
:
QmlCoapResource() : QCoapResource() {}
QmlCoapResource(const
QCoapResource &
amp;resource)
:
QCoapResource(resource) {}
QString hostStr() const
{
return
host().toString(); }
}
;
After that, create the QmlCoapMulticastClient class with the QCoapClient class as a base class. Use the Q_PROPERTY macro to expose a custom property, and also create several Q_INVOKABLE methods. Both the property and the invokable methods can be accessed from QML. Unlike QmlCoapResource, you want to be able to create this class from QML, so use the QML_NAMED_ELEMENT macro to register the class in QML.
class
QmlCoapMulticastClient : public
QCoapClient
{
Q_OBJECT
Q_PROPERTY(bool
isDiscovering READ isDiscovering NOTIFY isDiscoveringChanged)
QML_NAMED_ELEMENT(CoapMulticastClient)
public
:
QmlCoapMulticastClient(QObject *
parent =
nullptr
);
Q_INVOKABLE void
discover(const
QString &
amp;host, int
port, const
QString &
amp;discoveryPath);
Q_INVOKABLE void
discover(QtCoap::
MulticastGroup group, int
port, const
QString &
amp;discoveryPath);
Q_INVOKABLE void
stopDiscovery();
bool
isDiscovering() const
;
Q_SIGNALS
:
void
discovered(const
QmlCoapResource &
amp;resource);
void
finished(int
error);
// The bool parameter is not provided, because the signal is only used by
// the QML property system, and it does not use the passed value anyway.
void
isDiscoveringChanged();
public
slots:
void
onDiscovered(QCoapResourceDiscoveryReply *
reply, const
QList&
lt;QCoapResource&
gt; &
amp;resources);
private
:
QCoapResourceDiscoveryReply *
m_reply =
nullptr
;
}
;
Finally, register the QtCoap namespace, so that you can use the enums provided there:
namespace
QCoapForeignNamespace
{
Q_NAMESPACE
QML_FOREIGN_NAMESPACE(QtCoap)
QML_NAMED_ELEMENT(QtCoap)
}
Adjusting Build Files▲
To make the custom types available from QML, update the build system files accordingly.
CMake Build▲
For a CMake-based build, add the following to the CMakeLists.txt:
qt_add_qml_module(quickmulticastclient
URI CoapClientModule
VERSION 1.0
SOURCES
qmlcoapmulticastclient.cpp qmlcoapmulticastclient.h
QML_FILES
Main.qml
)
qmake Build▲
For a qmake build, modify the quickmulticastclient.pro file in the following way:
CONFIG +=
qmltypes
QML_IMPORT_NAME =
CoapClientModule
QML_IMPORT_MAJOR_VERSION =
1
...
qml_resources.files =
\
qmldir \
Main.qml
qml_resources.prefix =
/
qt/
qml/
CoapClientModule
RESOURCES +=
qml_resources
Using New QML Types▲
Now, when the C++ classes are properly exposed to QML, you can use the new types:
CoapMulticastClient {
id
:
client
onDiscovered
:
(resource) =&
gt; {
root.addResource(resource) }
onFinished
:
(error) =&
gt; {
statusLabel.text =
(error ===
QtCoap.Error.Ok)
? qsTr("Finished resource discovery."
)
:
qsTr("Resource discovery failed with error code: %1"
).arg(error)
}