Sur cette page

Découverte rapide de la multidiffusion CoAP

Utilisation du client CoAP pour la découverte de ressources de multidiffusion avec une interface utilisateur Qt Quick.

Fenêtre de découverte du multicast montrant les ressources CoAP découvertes

L'exemple de découverte rapide de multidiffusion CoAP montre comment enregistrer QCoapClient en tant que type QML et l'utiliser dans une application Qt Quick pour la découverte de ressources de multidiffusion CoAP.

Remarque : Qt CoAP ne fournit pas d'API QML dans sa version actuelle. Cependant, vous pouvez mettre les classes C++ du module à la disposition de QML, comme le montre cet exemple.

Exécution de l'exemple

Pour exécuter l'exemple à partir de Qt Creatorouvrez le mode Welcome et sélectionnez l'exemple à partir de Examples. Pour plus d'informations, voir Qt Creator: Tutoriel : Construire et exécuter.

Configuration d'un serveur CoAP

Pour exécuter l'application d'exemple, vous devez d'abord configurer et démarrer au moins un serveur CoAP prenant en charge la découverte de ressources multicast. Vous avez les options suivantes :

  • Construire et exécuter manuellement des serveurs CoAP en utilisant libcoap, Californium, ou toute autre implémentation de serveur CoAP, qui prend en charge les fonctionnalités de multidiffusion et de découverte de ressources.
  • Utiliser l'image Docker prête à l'emploi disponible sur Docker Hub, qui construit et démarre le serveur CoAP basé sur l'exemple de serveur multicast de Californium.
Utilisation du serveur de test basé sur Docker

La commande suivante extrait le conteneur Docker pour le serveur CoAP du Docker Hub et le démarre :

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

Note : Vous pouvez exécuter plus d'un serveur CoAP multicast (sur le même hôte ou sur d'autres hôtes du réseau) en passant un --name différent à la commande ci-dessus.

Pour mettre fin au conteneur Docker après utilisation, il faut d'abord obtenir l'ID du conteneur en exécutant la commande docker ps. La sortie ressemblera à ceci :

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

Ensuite, utilisez cet identifiant pour arrêter le conteneur :

docker stop <container_id>

Exposer des classes C++ à QML

Dans cet exemple, vous devez exposer les classes QCoapResource et QCoapClient, ainsi que l'espace de noms QtCoap, à QML. Pour ce faire, créez des classes enveloppantes personnalisées et utilisez les macros d'enregistrement spéciales.

Créez la classe QmlCoapResource en tant que classe enveloppante autour de QCoapResource. Utilisez la macro Q_PROPERTY pour rendre plusieurs propriétés accessibles à partir de QML. La classe n'a pas besoin d'être directement instanciable à partir de QML, utilisez donc la macro QML_ANONYMOUS pour l'enregistrer.

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(); }
};

Ensuite, créez la classe QmlCoapMulticastClient avec la classe QCoapClient comme classe de base. Utilisez la macro Q_PROPERTY pour exposer une propriété personnalisée et créez également plusieurs méthodes Q_INVOKABLE. La propriété et les méthodes invocables sont accessibles à partir de QML. Contrairement à QmlCoapResource, vous voulez pouvoir créer cette classe à partir de QML, utilisez donc la macro QML_NAMED_ELEMENT pour enregistrer la classe dans 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 &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;
};

Enfin, enregistrez l'espace de noms QtCoap, afin de pouvoir utiliser les enums qui y sont fournis :

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

Ajustement des fichiers de construction

Pour rendre les types personnalisés disponibles à partir de QML, mettez à jour les fichiers du système de construction en conséquence.

Construction CMake

Pour une compilation basée sur CMake, ajoutez ce qui suit au fichier CMakeLists.txt:

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

Pour une compilation qmake, modifiez le fichier quickmulticastclient.pro de la manière suivante :

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

Utilisation des nouveaux types QML

Maintenant que les classes C++ sont correctement exposées à QML, vous pouvez utiliser les nouveaux types :

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)
    }
}

Le signal QmlCoapMulticastClient::finished() déclenche le gestionnaire de signal onFinished, pour afficher l'état de la demande dans l'interface utilisateur. Notez que l'exemple n'utilise pas directement les signaux de QCoapClient, car les signaux error() et finished() prennent un QCoapReply comme paramètre (qui n'est pas exposé à QML), et l'exemple n'a besoin que du code d'erreur.

Le constructeur de QmlCoapMulticastClient transmet les signaux de QCoapClient au signal QmlCoapMulticastClient::finished():

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));
            });
}

Lorsque l'on appuie sur le bouton Discover, l'une des méthodes surchargées discover() est invoquée, en fonction du groupe de multidiffusion sélectionné :

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);
        }
    }
}

Cette surcharge est appelée lorsqu'un groupe de multidiffusion personnalisé ou une adresse d'hôte est sélectionné :

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.");
    }
}

Et cette surcharge est appelée lorsque l'un des groupes de multidiffusion suggérés est sélectionné dans l'interface utilisateur :

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.");
    }
}

Le signal QCoapResourceDiscoveryReply::discovered() fournit une liste de QCoapResources, qui n'est pas un type QML. Pour rendre les ressources disponibles en QML, transmettez chaque ressource de la liste au signal QmlCoapMulticastClient::discovered(), qui prend un QmlCoapResource à la place :

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

Les ressources découvertes sont ajoutées à l'adresse resourceModel de la liste dans l'interface utilisateur :

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

Lorsque la découverte est en cours, appuyez sur le bouton Stop Discovery pour arrêter la découverte. En interne, cela se fait en abandonnant la requête en cours :

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

Files :

© 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.