Auf dieser Seite

Schnelle CoAP-Multicast-Erkennung

Verwendung des CoAP-Clients zur Multicast-Ressourcenerkennung mit einer Benutzeroberfläche nach dem „ Qt Quick “-Prinzip.

Multicast-Erkennungsfenster mit den erkannten CoAP-Ressourcen

Das Beispiel „Schnelle CoAP-Multicast-Erkennung“ veranschaulicht, wie man „ QCoapClient “ als QML-Typ registriert und in einer „ Qt Quick “-Anwendung zur CoAP-Multicast-Ressourcenerkennung einsetzt.

Hinweis: „ Qt CoAP “ bietet in der aktuellen Version keine QML-API. Sie können jedoch die C++-Klassen des Moduls für QML verfügbar machen, wie in diesem Beispiel gezeigt.

Ausführen des Beispiels

Sie können das Beispiel wie folgt ausführen:

Einrichten eines CoAP-Servers

Um die Beispielanwendung auszuführen, müssen Sie zunächst mindestens einen CoAP-Server einrichten und starten, der die Multicast-Ressourcenerkennung unterstützt. Ihnen stehen folgende Optionen zur Verfügung:

  • Erstellen und starten Sie CoAP-Server manuell mithilfe von libcoap, Californium oder einer anderen CoAP-Server-Implementierung, die Multicast- und Ressourcenerkennungsfunktionen unterstützt.
  • Verwenden Sie das auf Docker Hub verfügbare fertige Docker-Image, das einen CoAP-Server auf Basis des Multicast-Server-Beispiels von Californium erstellt und startet.
Verwendung des Docker-basierten Test-Servers

Der folgende Befehl ruft den Docker-Container für den CoAP-Server von Docker Hub ab und startet ihn:

docker run --name coap-multicast-server -d --rm --net=host tqtc/coap-multicast-test-server:californium-2.0.0

Hinweis: Sie können mehrere Multicast-CoAP-Server (auf demselben Host oder auf anderen Hosts im Netzwerk) ausführen, indem Sie dem obigen Befehl eine andere ` --name ` übergeben.

Um den Docker-Container nach der Nutzung zu beenden, ermitteln Sie zunächst die ID des Containers, indem Sie den Befehl ` docker ps ` ausführen. Die Ausgabe sieht wie folgt aus:

$ docker ps
CONTAINER ID   IMAGE
8b991fae7789   tqtc/coap-multicast-test-server:californium-2.0.0

Verwenden Sie anschließend diese ID, um den Container zu beenden:

docker stop <container_id>

C++-Klassen für QML verfügbar machen

In diesem Beispiel müssen Sie die Klassen „ QCoapResource “ und „ QCoapClient “ sowie den Namespace „ QtCoap “ für QML verfügbar machen. Erstellen Sie dazu benutzerdefinierte Wrapper-Klassen und verwenden Sie die speziellen Registrierungsmakros.

Erstellen Sie die Klasse „ QmlCoapResource “ als Wrapper für „ QCoapResource “. Verwenden Sie das Makro „ Q_PROPERTY “, um mehrere Eigenschaften für QML zugänglich zu machen. Da die Klasse nicht direkt aus QML instanziierbar sein muss, registrieren Sie sie mit dem Makro „ QML_ANONYMOUS “.

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 &resource)
        : QCoapResource(resource) {}

    QString hostStr() const { return host().toString(); }
};

Erstellen Sie anschließend die Klasse „ QmlCoapMulticastClient “ mit der Klasse „ QCoapClient “ als Basisklasse. Verwenden Sie das Makro „ Q_PROPERTY “, um eine benutzerdefinierte Eigenschaft freizugeben, und erstellen Sie außerdem mehrere Methoden mit dem Makro „ Q_INVOKABLE “. Sowohl auf die Eigenschaft als auch auf die aufrufbaren Methoden kann von QML aus zugegriffen werden. Im Gegensatz zu „ QmlCoapResource “ soll diese Klasse von QML aus instanziierbar sein; verwenden Sie daher das Makro „ QML_NAMED_ELEMENT “, um die Klasse in QML zu registrieren.

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 &host, int port, const QString &discoveryPath);
    Q_INVOKABLE void discover(QtCoap::MulticastGroup group, int port, const QString &discoveryPath);
    Q_INVOKABLE void stopDiscovery();

    bool isDiscovering() const;

Q_SIGNALS:
    void discovered(const QmlCoapResource &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<QCoapResource> &resources);

private:
    QCoapResourceDiscoveryReply *m_reply = nullptr;
};

Registrieren Sie abschließend den Namespace „ QtCoap “, damit Sie die dort bereitgestellten Enums verwenden können:

namespace QCoapForeignNamespace
{
    Q_NAMESPACE
    QML_FOREIGN_NAMESPACE(QtCoap)
    QML_NAMED_ELEMENT(QtCoap)
}

Anpassung der Build-Dateien

Um die benutzerdefinierten Typen in QML verfügbar zu machen, aktualisieren Sie die Dateien des Build-Systems entsprechend.

CMake-Build

Fügen Sie bei einem CMake-basierten Build Folgendes in die Datei „ CMakeLists.txt “ ein:

qt_add_qml_module(quickmulticastclient
    URI CoapClientModule
    VERSION 1.0
    SOURCES
        qmlcoapmulticastclient.cpp qmlcoapmulticastclient.h
    QML_FILES
        Main.qml
)
qmake-Build

Für einen qmake-Build ändern Sie die Datei „ quickmulticastclient.pro “ wie folgt:

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

Verwendung neuer QML-Typen

Sobald die C++-Klassen ordnungsgemäß für QML verfügbar sind, können Sie die neuen Typen verwenden:

CoapMulticastClient {
    id: client
    onDiscovered: (resource) => { root.addResource(resource) }

    onFinished: (error) => {
        statusLabel.text = (error === QtCoap.Error.Ok)
                ? qsTr("Finished resource discovery.")
                : qsTr("Resource discovery failed with error code: %1").arg(error)
    }
}

Das Signal „ QmlCoapMulticastClient::finished() “ löst den Signal-Handler „ onFinished “ aus, um den Status der Anfrage in der Benutzeroberfläche anzuzeigen. Beachten Sie, dass das Beispiel die Signale von „ QCoapClient “ nicht direkt verwendet, da sowohl das Signal „ error()“ als auch das Signal „ finished()“ ein „ QCoapReply “ als Parameter benötigen (das nicht für QML verfügbar ist) und das Beispiel lediglich den Fehlercode benötigt.

Der Konstruktor von ` QmlCoapMulticastClient` leitet die Signale von ` QCoapClient` an das Signal ` QmlCoapMulticastClient::finished() ` weiter:

QmlCoapMulticastClient::QmlCoapMulticastClient(QObject *parent)
    : QCoapClient(QtCoap::SecurityMode::NoSecurity, parent)
{
    connect(this, &QCoapClient::finished, this,
            [this](QCoapReply *reply) {
                if (reply) {
                    emit finished(static_cast<int>(reply->errorReceived()));
                    reply->deleteLater();
                    if (m_reply == reply) {
                        m_reply = nullptr;
                        emit isDiscoveringChanged();
                    }
                } else {
                    qCWarning(lcCoapClient, "Something went wrong, received a null reply");
                }
            });

    connect(this, &QCoapClient::error, this,
            [this](QCoapReply *, QtCoap::Error err) {
                emit finished(static_cast<int>(err));
            });
}

Wenn die Schaltfläche „ Discover “ gedrückt wird, wird je nach ausgewählter Multicast-Gruppe eine der überladenen Methoden von ` discover() ` aufgerufen:

Button {
    id: discoverButton
    text: client.isDiscovering ? qsTr("Stop Discovery") : qsTr("Discover")
    Layout.preferredWidth: 100

    onClicked: {
        if (client.isDiscovering) {
            client.stopDiscovery()
        } else {
            var currentGroup = groupComboBox.model.get(groupComboBox.currentIndex).value;

            var path = "";
            if (currentGroup !== - 1) {
                client.discover(currentGroup, parseInt(portField.text),
                                discoveryPathField.text);
                path = groupComboBox.currentText;
            } else {
                client.discover(customGroupField.text, parseInt(portField.text),
                                discoveryPathField.text);
                path = customGroupField.text + discoveryPathField.text;
            }
            statusLabel.text = qsTr("Discovering resources at %1...").arg(path);
        }
    }
}

Diese Überladung wird aufgerufen, wenn eine benutzerdefinierte Multicast-Gruppe oder eine Host-Adresse ausgewählt wird:

void QmlCoapMulticastClient::discover(const QString &host, int port, const QString &discoveryPath)
{
    QUrl url;
    url.setHost(host);
    url.setPort(port);

    m_reply = QCoapClient::discover(url, discoveryPath);
    if (m_reply) {
        connect(m_reply, &QCoapResourceDiscoveryReply::discovered,
                this, &QmlCoapMulticastClient::onDiscovered);
        emit isDiscoveringChanged();
    } else {
        qCWarning(lcCoapClient, "Discovery request failed.");
    }
}

Und diese Überladung wird aufgerufen, wenn eine der vorgeschlagenen Multicast-Gruppen in der Benutzeroberfläche ausgewählt wird:

void QmlCoapMulticastClient::discover(QtCoap::MulticastGroup group, int port,
                                      const QString &discoveryPath)
{
    m_reply = QCoapClient::discover(group, port, discoveryPath);
    if (m_reply) {
        connect(m_reply, &QCoapResourceDiscoveryReply::discovered,
                this, &QmlCoapMulticastClient::onDiscovered);
        emit isDiscoveringChanged();
    } else {
        qCWarning(lcCoapClient, "Discovery request failed.");
    }
}

Das Signal „ QCoapResourceDiscoveryReply::discovered()“ liefert eine Liste von „ QCoapResources“, die kein QML-Typ ist. Um die Ressourcen in QML verfügbar zu machen, leiten Sie jede Ressource in der Liste an das Signal „ QmlCoapMulticastClient::discovered() “ weiter, das stattdessen einen „ QmlCoapResource “ erwartet:

void QmlCoapMulticastClient::onDiscovered(QCoapResourceDiscoveryReply *reply,
                                          const QList<QCoapResource> &resources)
{
    Q_UNUSED(reply)
    for (const auto &resource : resources)
        emit discovered(resource);
}

Die erkannten Ressourcen werden dem „ resourceModel “ der Listenansicht in der Benutzeroberfläche hinzugefügt:

function addResource(resource) {
    resourceModel.insert(0, {"host" : resource.host,
                             "path" : resource.path,
                             "title" : resource.title})
}

Drücken Sie während der Erkennung auf die Schaltfläche „ Stop Discovery “, um die Erkennung zu beenden. Intern erfolgt dies durch Abbruch der aktuellen Anfrage:

void QmlCoapMulticastClient::stopDiscovery()
{
    if (m_reply)
        m_reply->abortRequest();
}

Dateien:

© 2026 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.