Qt Interface Frameworkについて最後に記事を書いたのはかなり前のことなので、Qt 6.11のニュースに入る前に、その機能と注目すべき理由を簡単に振り返っておきましょう。
Qt Interface Framework は ミドルウェア API を構築するための完全なツールチェーンを提供します 。API を .qface IDL ファイルに記述すると Qt Interface Framework Generator (ifcodegen)は、フロントエンドのC++クラスやQMLクラス、バックエンドインターフェース、シミュレーションバックエンド、さらにはIPCトランスポートレイヤーなど、必要なすべてのグルーコードを生成します。
APIが公開する もの (機能)と、 それがどのように 実装されるか(バックエンド)をきれいに分離します。 つまり、開発の初期段階ではシミュレーション用のバックエンド、プロセス間通信用の Qt Remote Objects バックエンド、実際のハードウェアに接続するプロダクション用のバックエンドを、アプリケーションコードを一行も変更することなく切り替えることができます。
要するに、一度定義すれば、あとはジェネレータがすべて処理します。APIを変更して再ビルドすれば、すべてが同期された状態を維持します。
このパターンは、UIとサービス層が並行して進化する場合に有用です。実際、これはほとんどの複雑なプロジェクトに当てはまります。車載インフォテインメントシステム、スマートホームコントローラ、産業用HMI、あるいはIoTダッシュボードを構築する場合でも、Qt Interface Frameworkは適しています。
シミュレーションバックエンド (backend_simulator)は、QMLスクリプトを使用したモックデータに対してUIを開発しテストすることができます。また、 QtRemoteObjectsバックエンド (backend_qtro)は、アプリケーションと別のサーバープロセス間のプロセス間通信を提供します。
しかし、この2つに限定されるわけではありません。ジェネレータ・テンプレート・システムは完全に拡張可能です。 Jinjaベースのコード生成テンプレート を作成し、必要なIPCメカニズムやプロトコルのバックエンドプラグインを作成することができます。D-Bus、gRPC、独自のプロトコル、または他の何かであっても、一度テンプレートを定義すれば、ジェネレータはIDLファイル内のすべてのインターフェースのボイラープレートを生成します。
Qt 6.11では、この機能を両方向に拡張しています。シミュレーションバックエンドには、実行時の検査を行うための新しいコントロールパネルが追加され、3つ目の組み込みオプションとして、まったく新しいMQTTバックエンドテンプレートがラインナップに加わりました。今後のリリースでは、さらに多くのバックエンドタイプを標準でサポートしていく予定です。
本ブログ記事では、新しいMQTTバックエンドテンプレートに焦点を当てます。シミュレーションコントロールパネルの詳細については、続編の記事で取り上げます。
新しいbackend_mqttコードテンプレートは、MQTTを介して通信を行う、完全に機能するバックエンドプラグインを生成します。MQTTは、IoT、産業オートメーション、テレメトリのシナリオで広く使用されている軽量なパブリッシュ/サブスクライブプロトコルです。既存のbackend_qtroテンプレートがQtRemoteObjectsベースのバックエンドを生成するのと同様に、backend_mqttテンプレートは、内部でQtMqttモジュールを使用し、設定可能なMQTTトピックを介してプロパティ値の同期、操作の呼び出し、シグナルの受信を行うバックエンドを生成します。
MQTTは、IoT、産業オートメーション、テレメトリのシナリオで広く使用されている軽量なパブリッシュ/サブスクライブプロトコルです。Qtアプリケーションが、センサー、アクチュエータ、エッジゲートウェイ、あるいはすでにMQTTに対応しているシステムと通信する必要がある場合、このテンプレートを使用すれば、手動でのコーディングをほとんど行わずにそのギャップを埋めることができます。
具体的な例を見てみましょう。MQTTとThreadの両方に対応する必要があるスマートホームパネルを構築しているものの、両プロトコルのロジックを書き直すのは避けたいと仮定します。この例ではMQTTの部分を扱いますが、Threadの統合は独自のカスタム生成テンプレートを作成することで実現可能です。@config_mqttアノテーションを.qfaceファイルに直接記述することで、IDLはAPIとそのMQTTマッピングを1か所で記述します。
モジュール SmartHome.Sensors 1.0@config_mqtt: {default_server:"mqtt://192.168.1.100:1883",topic_prefix:「home/livingroom/"、retain: true, qos:1}interface 温度センサー現在の温度実湿度@config_mqtt: {mandatory: false}.ブール sensorActive@config_mqtt: {topic:"commands/calibrate",result_topic:"commands/calibrate/result", qos:2}void calibrate()@config_mqtt: {retain: false}.シグナル alert(文字列メッセージ)}
ここで何が起きているのか、簡単に見ていきましょう。
インターフェースレベルのトピックプレフィックス 「home/livingroom/」 が、各シンボルのトピックの先頭に自動的に付加されます。デフォルトでは、このトピックはその名前そのものなので、currentTemperature プロパティは home/livingroom/currentTemperature に、humidity は home/livingroom/humidity にマッピングされます。プロパティ名とは異なる名前を付けたい場合のみ、明示的にトピックを設定する必要があります。これは、home/livingroom/commands/calibrate にパブリッシュする calibrate 操作の場合と同様です。
retain フラグは、MQTT ブローカーがトピックに最後にパブリッシュされたメッセージを保存するかどうかを制御します。新しいサブスクライバーが接続すると、次の更新を待つことなく、すぐに保持された値を受け取ります。この例では、インターフェースレベルでこの設定が true に設定されています。これは、温度や湿度のようなプロパティには理にかなっています。新しく接続したクライアントは、直ちに現在の測定値を知るべきだからです。アラート信号については、アラートは一時的なイベントであり、遅れて接続したサブスクライバーに再送信されるべきではないため、これを false に上書きします。
qos(Quality of Service)設定は、配信の保証レベルを定義します。QoS 0は「fire and forget(送信して忘れる)」を意味し、QoS 1は「少なくとも1回」の配信を保証し、QoS 2は「正確に1回」の配信を保証します。インターフェースのデフォルトはQoS 1に設定されており、これはセンサーデータにとって適切なバランスです。calibrate操作ではQoS 2を使用します。これは、キャリブレーションコマンドが正確に1回だけ配信されることを確実にするためです。2回トリガーされると問題が生じる可能性があるためです。
MQTTはパブリッシュ/サブスクライブプロトコルであり、リクエスト/レスポンスパターンをネイティブにはサポートしていません。backend_mqttテンプレートは、リクエストを送信するための操作トピックと、レスポンスを受信するための別のresult_topicという2つのトピックを使用することで、この問題を解決します。この例では、フロントエンドからcalibrate()を呼び出すと、home/livingroom/commands/calibrateにメッセージがパブリッシュされます。その後、バックエンドはhome/livingroom/commands/calibrate/resultをサブスクライブして戻り値を受信し、通常のQIfPendingReplyメカニズムを介して呼び出し元に返されます。result_topicが指定されていない場合、テンプレートは操作のトピックに_resultを付加することで自動的に生成します。
このファイルからMQTTバックエンドを生成するには、CMakeLists.txtに次の1行を追加します。
qt_ifcodegen_add_plugin(sensors_backend_mqttIDL_FILES sensors.qfaceテンプレート backend_mqtt)
reset_on_error や clear_on_change などのオプションを含む、利用可能な @config_mqtt アノテーションの完全なリストについては、backend_mqtt テンプレートリファレンスを参照してください。
大規模なプロジェクトでは、config_mqtt アノテーションを .qface ファイルに埋め込む代わりに、別の YAML ファイルに記述することもできます。これにより、IDL をクリーンに保ち、インターフェース定義を変更することなく、異なる MQTT 設定(例:開発用と本番用のブローカー)を維持できます。詳細については、アノテーションのドキュメントを参照してください。
backend_mqtt テンプレートとシミュレーションコントロールパネルは、Qt 6.11 においていずれも技術プレビュー (Technical Preview) です。つまり、現時点では API の安定性を保証するものではありません。ぜひお試しいただき、フィードバックをお寄せください。
これら 2 つの主要機能に加え、Qt 6.11 では使い勝手を向上させるいくつかの改善も施されています。
QFaceおよびJinjaに対するIDEサポートの強化
QFace IDLおよびJinjaテンプレート言語は、Qt Creatorにおいて構文ハイライト、コード折りたたみ、および選択範囲のサポートが追加されました。Jinjaのハイライト機能は、二次ファイル拡張子に基づいて埋め込み言語を認識するため、myclass.cpp.jinja のようなファイルは、Jinjaディレクティブが重ねられたC++としてハイライトされます。これにより、カスタムコード生成テンプレートの作成やメンテナンスが格段に快適になります。
Qt Interface Framework を初めてご利用になる場合は、以下のリソースから始めるのが最適です。新機能については、シミュレーションコントロールパネルのドキュメントおよびbackend_mqtt テンプレートリファレンスをご確認ください。
自動車、IoT、産業オートメーション、あるいはそれとは全く異なる分野など、皆様が Qt Interface Framework をどのように活用されているか、ぜひお聞かせください。bugreports.qt.io でバグレポートを投稿するか、Qt Forum までお気軽にお問い合わせください。