クイックCoAPマルチキャストディスカバリー
CoAP クライアントを Qt Quick ユーザーインターフェースでマルチキャストリソースディスカバリーに使用する。
Quick CoAP Multicast Discovery の例では、QCoapClient を QML タイプとして登録し、Qt Quick アプリケーションで CoAP マルチキャストリソースディスカバリーに使用する方法を示しています。
注意: Qt CoAP は現在のバージョンでは QML API を提供していません。しかし、この例のように、モジュールの C++ クラスを QML で利用できるようにすることは可能です。
例の実行
Qt Creator からサンプルを実行するには、Welcome モードを開き、Examples からサンプルを選択します。詳しくは、Building and Running an Example を参照してください。
CoAP サーバーのセットアップ
サンプル・アプリケーションを実行するには、まずマルチキャスト・リソース・ディスカバリーをサポートする CoAP サーバーを少なくとも 1 つセットアップして起動する必要があります。以下のオプションがあります:
- libcoap、Californium、またはマルチキャストとリソース検出機能をサポートするその他のCoAPサーバー実装を使用して、CoAPサーバーを手動で構築して実行する。
- Californiumのマルチキャストサーバーの例に基づいてCoAPサーバーを構築して起動する、Docker Hubで入手可能な準備のできたDockerイメージを使用する。
Dockerベースのテストサーバーの使用
以下のコマンドは、Docker HubからCoAPサーバー用のDockerコンテナを取り出し、それを起動します:
docker run --name coap-multicast-server -d --rm --net=host tqtc/coap-multicast-test-server:californium-2.0.0
注: 上記のコマンドに異なる--name
を渡すことで、複数のマルチキャスト CoAP サーバーを(同じホストやネットワーク内の他のホストで)実行することができます。
使用後に docker コンテナを終了するには、まずdocker ps
コマンドを実行してコンテナの ID を取得する。出力はこのようになる:
$ docker ps CONTAINER ID IMAGE 8b991fae7789 tqtc/coap-multicast-test-server:californium-2.0.0
その後、この ID を使用してコンテナを停止する:
docker stop <container_id>
C++ クラスを QML に公開する
この例では、QCoapResource とQCoapClient のクラスと、QtCoap の名前空間を QML に公開する必要があります。そのためには、カスタムラッパークラスを作成し、特別な登録マクロを使用します。
QCoapResource のラッパーとしてQmlCoapResource
クラスを作成します。Q_PROPERTY マクロを使い、QML からいくつかのプロパティにアクセスできるようにします。このクラスはQMLから直接インスタンス化する必要はありませんので、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(); } };
その後、QCoapClient クラスを基底クラスとして、QmlCoapMulticastClient
クラスを作成します。Q_PROPERTY マクロを使ってカスタムプロパティを公開し、Q_INVOKABLE メソッドをいくつか作成します。プロパティも呼び出し可能なメソッドも QML からアクセスできます。QmlCoapResource
とは異なり、このクラスは QML から作成できるようにしたいので、QML_NAMED_ELEMENT マクロを使用して 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; };
最後に、QtCoap 名前空間を登録し、そこで提供される列挙型を使用できるようにします:
namespace QCoapForeignNamespace { Q_NAMESPACE QML_FOREIGN_NAMESPACE(QtCoap) QML_NAMED_ELEMENT(QtCoap) }
ビルドファイルの調整
QMLからカスタム型を利用できるようにするために、ビルドシステムファイルを適宜更新してください。
CMakeビルド
CMakeベースのビルドでは、CMakeLists.txt
に以下を追加してください:
qt_add_qml_module(quickmulticastclient URI CoapClientModule VERSION 1.0 SOURCES qmlcoapmulticastclient.cpp qmlcoapmulticastclient.h QML_FILES Main.qml )
qmake ビルド
qmakeビルドの場合は、quickmulticastclient.pro
ファイルを以下のように変更してください:
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
新しいQML型の使用
これで、C++クラスがQMLに正しく公開され、新しい型が使えるようになりました:
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) } }
QmlCoapMulticastClient::finished()
シグナルはonFinished
シグナルハンドラをトリガーし、リクエストのステータスを UI に表示します。QCoapClient error() とfinished() シグナルはどちらもQCoapReply をパラメータとして受け取り(これは QML に公開されていません)、この例ではエラーコードだけが必要だからです。
QmlCoapMulticastClient
のコンストラクタはQCoapClient のシグナルを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)); }); }
Discover ボタンが押されると、選択されたマルチキャストグループに基づいて、オーバーロードされたdiscover()
メソッドの1つが呼び出されます:
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); } } }
このオーバーロードは、カスタムマルチキャストグループまたはホストアドレスが選択されたときに呼び出される:
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."); } }
このオーバーロードは、カスタムマルチキャストグループまたはホストアドレスが選択されたときに呼び出されます。また、このオーバーロードは、提案されたマルチキャストグループの1つがUIで選択されたときに呼び出されます:
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."); } }
QCoapResourceDiscoveryReply::discovered()シグナルはQCoapResourcesのリストを配信します。これはQMLの型ではありません。QMLでリソースを利用できるようにするには、リストの各リソースをQmlCoapMulticastClient::discovered()
シグナルに転送します。 シグナルはQmlCoapResource
を代わりに受け取ります:
void QmlCoapMulticastClient::onDiscovered(QCoapResourceDiscoveryReply *reply, const QList<QCoapResource> &resources) { Q_UNUSED(reply) for (const auto &resource : resources) emit discovered(resource); }
発見されたリソースはUIのリストビューのresourceModel
:
function addResource(resource) { resourceModel.insert(0, {"host" : resource.host, "path" : resource.path, "title" : resource.title}) }
ディスカバリーの進行中に、Stop Discovery ボタンを押すと、ディスカバリーを停止することができます。内部的には、現在のリクエストを中止することで行われます:
void QmlCoapMulticastClient::stopDiscovery() { if (m_reply) m_reply->abortRequest(); }
ファイル
ここに含まれるドキュメントの著作権はそれぞれの所有者に帰属します。 本書で提供されるドキュメントは、Free Software Foundation が発行したGNU Free Documentation License version 1.3に基づいてライセンスされています。 Qtおよびそれぞれのロゴは、フィンランドおよびその他の国におけるThe Qt Company Ltd.の 商標です。その他すべての商標は、それぞれの所有者に帰属します。