Azure IoT Hubを久しぶりに触ってみたので個人的な備忘録 4（c2dメッセージとメッセージエンリッチメント）

# はじめに
これは、最近AzureのIoT Hubを触ってなかったので、触ってみる個人的な備忘録です。
何回かに分けてツラツラ触ってみたいと思います。
忙しくなったら途中で終わるかもです。

前回まではデバイス側の基本的な実装をおこなったので、今回はサービス側(クラウド側)をやります。
[初回はこちら](https://qiita.com/mstakaha1113/items/ff5e86ce969caceccb8d)：メッセージングとデバイスツイン
[２回はこちら](https://qiita.com/mstakaha1113/items/02f5ffb49367c3c4bcdd)：メッセージルーティング
[３回はこちら](https://qiita.com/mstakaha1113/items/bf312e34f0ee53fde239)：ファイルアップロード

# サービス側で実施できる主なこと
IoT Hubのサービス側では、主に以下ができます
- メッセージのやりとり
- プロパティのやりとり(デバイスツイン)
- ダイレクトメソッド
- デバイスの管理

他にも、フェールオーバーやモニタリングなどもありますが、言い出すとネットワークとかセキュリティとかキリがないですが、AzureのPaaSでできそうなことはフンワリできます。
Device Updateなるものも出てきていますので、機会があればやってみようと思います。

今回はメッセージのやりとりを対象とします。

# メッセージのやりとりでサービス側からできること
メッセージのやりとりでIoT Hubが主導的に実施するのは主に以下の２つです
- デバイスに対してメッセージを送信する(c2dメッセージ)
- デバイスからのメッセージに情報を追加して後続の処理に引き渡す(メッセージエンリッチメント)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/439826/c092cf10-ee48-a941-2180-5ede448cfdd9.png)

# c2dメッセージをゼロベースから作成
c2dメッセージをゼロベースから作成します。
今回もC#、.NETは6.0.202、VSCodeで作成します。

とりあえずこちらに沿ってハローワールドだけ作成。
[チュートリアル: Visual Studio Code を使用して .NET コンソール アプリケーションを作成する](https://docs.microsoft.com/ja-jp/dotnet/core/tutorials/with-visual-studio-code?pivots=dotnet-6-0)
フォルダ名は "service1" としました。

サービスクライアントを利用するため、Program.csの1行目に以下を追加します。
> using Microsoft.Azure.Devices;

また、ターミナル(Ctrl+@)よりパッケージを追加します。
> dotnet add package Microsoft.Azure.Devices.

あとはこちらを参考にサンプルを作成します。
[デバイスに IoT Hub でクラウドからメッセージを送信する (.NET)](https://docs.microsoft.com/ja-jp/azure/iot-hub/iot-hub-csharp-csharp-c2d)
参考ドキュメントには配信後の動作状況の確認なども含まれていますが、全部省略しています。
起動したら固定文字列を送ってプログラムは終了します。
[こちら](https://qiita.com/mstakaha1113/items/ff5e86ce969caceccb8d)で以前作成したdevice1を動かすと、メッセージを受信できます。

完成したコードはこちらを参照ください。
このままでは使い物にはなりませんが、理解するに必要最小限にはなっているかと思います。
https://github.com/matakaha/iotsample2022/blob/main/service1/Program.cs

余談ですが、ここで利用する接続文字列は、今までデバイスで利用していたものとは別ですのでご注意くださいませ。
IoT Hub→共有アクセスポリシー→service→接続文字列をご利用ください。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/439826/8cb68758-1ab6-3564-0e5f-3819e5b39cb0.png)

もう１つ余談ですが、今回は配信後の動作状況を省略しました。
ここも実施する場合は、参考ドキュメント通りにサービス側を作成します。
そのうえで、デバイス側を改修してみてください。
改修ポイントは２点
- 通信プロトコルをMQTTからAMQPに変更する（TransportType.MqttをTransportType.Amqp）
- メッセージ受信完了通知で受信拒否する（CompleteAsyncをRejectAsyncやAbandonAsync）

これで拒否したステータスがサービス側で取得できます。また、ここらへんがMQTTとAMQPの動作上の差異となってきます。

# メッセージエンリッチメント
メッセージエンリッチメントは設定するだけで動作します。
IoT Hubで受信したメッセージに情報を追加することができます。
作業そのものはチュートリアルとおり実施しました。
[チュートリアル: Azure IoT Hub のメッセージ エンリッチメントを使用する](https://docs.microsoft.com/ja-jp/azure/iot-hub/tutorial-message-enrichments)
## チュートリアルの概要
チュートリアルは、以下の図のようなものを作成しています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/439826/1d277021-784b-696f-db45-7ede2b803c7a.png)
### 事前準備
- IoT Hubの作成(ContosoTestHubMsgEn~~)
- Blob Storageの作成(contosostorage~~)
- Blobのコンテナを２つ作成(originalコンテナとenrichedコンテナ)
- デバイスIDの作成(Contoso-Test-Device)
- メッセージルーティングで利用するルーティングエンドポイントを２つ作成(ContosoStorageEndpointOriginalとContosoStorageEndpointEnriched)
- ルート(ルーティングの条件はlevel="storage")を２つ作成(ContosoStorageRouteOriginalとContosoStorageRouteEnriched)
- ContosoStorageEndpointEnrichedルートにはエンリッチメントを３つ設定（myIotHub:\\$iothubname, DeviceLocation:$twin.tags.location, customerID:6ce345b8-1e4a-411e-9398-d34587459a3a）

### 動作
1. デバイスからd2cメッセージが送信される。メッセージにはプロパティ"level"が付与されており、ランダムに"storage"という値が入る(ルーティング条件：level="storage"に合致する)
1. メッセージルーティングでContosoStorageRouteOriginalとContosoStorageRouteEnrichedで条件を確認、ルーティング条件：level="storage"と合致するメッセージであった場合は、各々のエンドポイントへメッセージをルーティングする
1. ContosoStorageRouteEnrichedにルーティングされたメッセージはメッセージエンリッチメントが適用される
1. ContosoStorageEndpointOriginalへ向かったメッセージはBlobのoriginalコンテナのjsonに書き込まれる
1. ContosoStorageEndpointEnrichedへ向かったエンリッチメントが適用されたメッセージはBlobのenrichedコンテナのjsonに書き込まれる


## そもそも何ができるのか
チュートリアルを確認するとわかりますが、メッセージなどに情報を追加して後続の処理に投げ込むことができます。
例えば、デバイスツインのReported propertiesでバッテリー残量を投げているデバイスがあるとします。メッセージとともに、このバッテリー残量もチェックしたい場合、通常であればメッセージ処理時にデバイスIDを取得し、デバイスIDを使ってIoT HubにあるReported propertiesのバッテリー残量を問い合わせるAPIを１回投げなければなりません。もしくはデバイスがメッセージにもバッテリー残量を付与して送信しなければなりません。
メッセージエンリッチメントを使ってメッセージにバッテリー残量を付与すれば、デバイスの負荷を増やすことなく、またAPI１回投げ込む手間をかけることなく、バッテリー残量を取得することができます。

# さいごに
今回はサービス側のメッセージあれこれを実施してみました。
個人的な学びとしては、メッセージルーティングで同じ条件で２つのルートを作成すると両方にメッセージがコピーされるんだということを学びました。
言われてみればそうなるとわかるけど、そんなこと考えてもみませんでしたので、学びになりました。
これを使うべきか、コンシューマーグループを使うのか、また似て非なる機能があるのだと発見です。

久しぶりにIoT Hubしてみました。
ひととおりやってみて、以前よりドキュメントが充実してきたなぁという印象です。
次回はx.509のデバイス認証か、IoT Edgeでもやろうかと思います。