AWS IoT Coreのtopicデザインホワイトペーパを読んでみた Part.2

# はじめに

こちらはpart2です。この説明の前は[part.1](https://qiita.com/TakashiKOYANAGAWA/items/2a9e8c17cb2febce3296)を参照ください。

## Applications on AWS:AWS上のアプリケーション
このセクション以降はAWS IoTを使ったMQTTトピックの開発のベストプラクティスです。

### AWS IoT Shadow Example: AWS IoTシャドウの例
この例では、管理者が風力タービンにコマンドを送信して、風に対応するようにタービンブレードの現在の角度を変更します。 これを行うために、管理者はまず、デバイスにコマンドを発行するための主なメカニズムとして、またデバイスに関連する重要なデバイスの読み込みのための格納場所としてAWS IoT Shadowを活用します。
このシナリオでは、次の詳細が想定されています。

- タービンの名称として、タービン8000
- 管理者は、タービンA8000にスコープされた一時的なIAM許可を使用することが許可されている内部アプリケーションを利用しています
- 管理者のセッション識別子はsession-admin100

#### 風力タービンデバイスシャドーへのコマンド要求

  管理者は、MQTT Shadowトピックのturbine8000を使用して、IoTシャドウにメッセージをパブリッシュします。 コマンドペイロードは、shadowのdesired stateを設定することを含む。トラッキングのために、管理者はIoT ShadowにclientTokenとしてセッション識別子を埋め込みます。

![スクリーンショット 2018-11-04 19.38.12.png](https://qiita-image-store.s3.amazonaws.com/0/100893/1a116931-7d91-f34e-712a-11d625e24da6.png)

#### タービンのコマンド処理

シャドウを使用する場合、デバイスは、AWS IoT shadowから適切な通知を受け取るために、いくつかの異なるMQTTトピックにサブスクライブする責任があります。 最低でも、デバイスがupdate/delta、update/rejected、get/accepted、およびget/rejectedの予約済みシャドウトピックをサブスクライブして、シャドウリクエストの成功と失敗を受信することを確認してください。

![スクリーンショット 2018-11-04 19.41.23.png](https://qiita-image-store.s3.amazonaws.com/0/100893/1914bc90-a9fe-ffe2-264d-1ed948f7e05c.png)

この例では、風力タービンは、reported stateとdesired stateが異なるとupdate/deltaに関するメッセージを受信する。 コマンドを処理する前に、風力タービンはclientTokenフィールドをチェックします。 次に、メタデータ内のタイムスタンプフィールドを検査します。
（いくつかのユースケースでは、コマンドは特定の期間だけ有効であるとみなすべきです
風力タービンが確認されると、ブレードの角度が調整され、更新トピックを使用して報告された新しい状態で応答が発行されます。

#### コマンドレスポンスを管理者へ送信

管理者は、報告された状態の変更に関する更新を受信するために、更新/受け入れられたトピックと更新/拒否されたトピックを購読します。 タービンが更新された状態をIoTシャドウにパブリッシュすると、管理者はupdate/acceptedトピックに関する更新されたメッセージの成功応答を受信する。

![スクリーンショット 2018-11-04 19.46.51.png](https://qiita-image-store.s3.amazonaws.com/0/100893/97d9b640-83c2-5448-9472-2078a435cf3e.png)

### MQTTコマンドのトピックの例
スマート・ドア・ロック・アプリケーションの場合、ユーザーは、承認された訪問者に対して発行される一時キーをトリガーするコマンドをロックに送信できなければなりません。 一時キーは、TTL、コード、および許可されたユーザーに関する情報で構成されます。 一時的なキーを作成する機能により、別の個人が指定した期間ロックを開くことができます。 このユースケースは、プライマリオーナーが働いている間に家に到着した家族を訪問するなどのシナリオに適用されます。

このシナリオでは、次の詳細が想定されています。

- 住宅所有者のモバイルデバイスIDがmobile-1です
- 承認された訪問者には、mobile-2のモバイルデバイスIDがあります
- スマートロックは、家の他のロックのグループの一部としてインストールされます。
- ロックセットにgroupIdのコンテキストがあります。ここで、groupIdはグループ3と同じです
-フロントドアのスマートロックのロックIDはlock-1です
- スマートロックハードウェアにはseries100のシリーズ番号があります。 このシリーズは、この製品バージョンの一意の識別子です。

### スマートロックコードを生成するためのコマンド要求
プライマリオーナーは、承認された訪問者に対して作成された一時的なアクセスコードを要求するために、まずロックにコマンドを発行します。 コマンドペイロードは、住宅所有者のモバイルデバイスの識別情報、現在の要求を追跡するためのランダムに生成されたセッション識別子、コマンドのタイプを含むアクションフィールド、およびトピックフィールドを含む。 スマートロックはトピックフィールドのトピックを使用して、その応答を住宅所有者にパブリッシュします。

![スクリーンショット 2018-11-04 19.46.51.png](https://qiita-image-store.s3.amazonaws.com/0/100893/94a184dc-4e3c-b2ed-87bc-7e5733f9b159.png)

要求、応答、およびテレメトリーの内部標準を使用してMQTTメッセージを拡張します。 たとえば、ペイロードにコマンドのリクエスターを追加することで、アプリケーションはユースケースに基づいて異なる応答トピックを指定できます。 住宅所有者が一時的なロックを要求したが、家のすべてのドアロックに到達するための応答が必要な場合は、トピックフィールドを変更して、デバイスのグループに送信することができます。

#### スマートロックのコマンド処理

スマート・ロックは、MQTTトピックからコマンド・メッセージを受け取ります。 このアプリケーションのコマンドに一貫した命名スキーマを利用することで、スマートロックは特定のコマンドトピックでコマンドを受け取ることができます。 このトピック設計は、ロックがそのIDの後に単一レベルのMQTTワイルドカードを使用してサブスクライブすることも可能にします。 単一レベルワイルドカードコマンドは、IoTアプリケーションが新しいコマンドタイプを追加するのと下位互換性があります。

![スクリーンショット 2018-11-04 19.52.01.png](https://qiita-image-store.s3.amazonaws.com/0/100893/504673b1-4e04-0002-704c-28b7ca1b9c93.png)

スマート・ロックは、MQTTペイロードを受け取った後、コマンド要求を解析して、実行するアクションのタイプを判別します。 この場合、コマンドは資格情報に関連しています。 また、デバイスは、コマンドペイロードからクライアントID、セッションID、および応答トピックを抽出します。 ホームには複数の権限のある住宅所有者が存在することができるため、この変更を要求した住宅所有者はクライアントIDによって決まります。 この例では、アクションフィールドには、資格情報要求のタイプ、生成パスワード、および一時キーの関連ユーザが含まれています。 最後に、デバイスはMQTTメッセージの応答トピック・フィールドを取得し、この情報を使用して応答をパブリッシュします。

![スクリーンショット 2018-11-04 19.54.22.png](https://qiita-image-store.s3.amazonaws.com/0/100893/8ddae2c9-36a2-649b-ce37-d7a2fca32e14.png)

#### モバイルクライアントに送信されたコマンド応答

住宅所有者のモバイルクライアントは、グループ内のスマートロックのコマンド応答に加入します。 モバイルクライアントがコマンドレスポンスを正常に受信すると、一時コードと追加の権限がデバイスで処理され、同時にAWSクラウドに保存されます。 後で、許可された訪問者は、臨時コードをスマートロックに適用するために、OAuthクレデンシャルの交換、ドアのローカルプレゼンスの証明など、追加のセキュリティ資格情報とともに使用することができます。
このワークフローでは、アプリケーションは1つのデバイスに対してコマンドトピックを使用しました。
しかし、同様のワークフローを使用して、家庭内のすべてのドアをロックするなど、グループ内の複数のデバイスのコマンドを要求することができます。

![スクリーンショット 2018-11-04 19.57.48.png](https://qiita-image-store.s3.amazonaws.com/0/100893/4a1ff03a-e55a-815a-bb89-4b36af8e921b.png)

### MQTTテレメトリのトピックの例

このセクションでは、部屋の使用状況を監視するために建物全体に配置された占有センサーのセットからテレメトリを集約する例を示します。 占有センサーは、AWS Greengrassを実行しているローカルゲートウェイと通信します。 [AWS Greengrass](https://docs.aws.amazon.com/ja_jp/greengrass/latest/developerguide/what-is-gg.html)は、MQTTトピックのすべてのセンサー・メトリックをAWS IoTコアに配信します。 このユースケースはテレメトリーに焦点を当てているため、GreengrassとAWS IoT Coreの間でローカルまたは上流のどちらにも対応するトピックは必要ありません。
このシナリオでは、次の詳細が想定されています。

- 占有センサは、occupancy-1およびoccupancy-2のIDを有する
- 建物はbuilding-frescoと呼ばれています
- 各センサーは、buildingfrescoの特定のフロアとルーム名に配置されます。
- Greengrassローカルゲートウェイには、ゲートウェイ1の一意の識別子があります
- 現在のビルオートメーションシステムは、acmeと呼ばれる内部プロジェクト

#### 占有センサからグリーングラスへのローカルテレメトリ

各占有センサは、1分間に1回、および人が部屋に入るか出るたびに、占有率確認し通知する。 占有センサは装置の状態とは正確に相関せず、その代わりに部屋の状態を参照するので、センサは遠隔測定の話題に部屋の状態をパブリッシュする。 ペイロードには、タイムスタンプ、占有数、部屋が空いているときにライトを消すための効率カウントダウンが含まれています。 占有センサーは、建物内のセンサーの位置に関するコンテキスト情報とそれに関連するプロジェクトを含むMQTTトピックを使用します。 Greengrassコアはすべての占有センサーデータをローカルに受信します。

![スクリーンショット 2018-11-04 20.04.15.png](https://qiita-image-store.s3.amazonaws.com/0/100893/834eed82-2815-c76d-99f5-cc142b996d56.png)

#### エッジからクラウドへのグリーングラステレメトリ

この例では、Greengrassの主な役割は、複数の占有センサーからのデータを集計し、そのデータをAWS IoTコアに送信することです。 Greengrassはクラウドのローカルブリッジなので、Greengrassは各センサーの読みにメタデータを追加します。 Greengrassは各メッセージに建物情報を追加し、建物の全体的な使用状況を5分単位で表示します。 Greengrassはまた、適切なアプリケーションIDであるacmeを組み込むことによってMQTTトピックを補強します。

![スクリーンショット 2018-11-04 20.05.40.png](https://qiita-image-store.s3.amazonaws.com/0/100893/5b3dc366-4eb8-9631-e247-6c59f645b2b0.png)

## AWS IoT Rules EngineでのMQTTトピックの使用に関するベストプラクティス

AWS IoT Rules Engineを使用すると、AWS IoT Coreに送信されるメッセージをAWSサービスとやりとりする方法を定義できます。 AWS IoTルールは、SQL SELECT文、トピックフィルタ、およびルールアクションで構成されます。 SQL SELECT文は、受信MQTTメッセージからデータを抽出できます。 AWT IoTルールのトピックフィルタは、どのMQTTトピックがIoTルールアクションをトリガするかを指定します。

ルールエンジンは、メッセージを他のAWSサービスに誘導したり、デバイスに再パブリッシュしたりするための中心的な役割を果たします。 IoTルールは、運用メトリックの収集、データのエンリッチメント化、分析目的でのデバイス遠隔測定のデータ集約、およびエラーのトラブルシューティングなどのユースケースをサポートします。

#### ルールエンジンとテレメトリのトピックス
次のような遠隔測定のトピック構造を使用することをお勧めします。

```
dt/<application-prefix>/<context>/<thing-name>/<dt-type>
```

applicationprefixとして定義されたMQTTトピック遠隔測定パターンの第2フィールドは、fleet内のデバイス間の不変の自然な分岐を表します。 アプリケーションの一般的な属性は、デバイスハードウェアのバージョンまたはIoTアプリケーションの名前です。 遠隔測定MQTT構造を使用すると、特定のアプリケーション・バージョンに関連付けられたすべてのテレメトリーを取得するIoTルールを作成できます。

```

 "sql":"SELECT *, topic(2) as applicationVersion, topic(3) as
contextIdentifier FROM 'dt/#'",
 "awsIoTSqlVersion":"2016-03-23",
 "ruleDisabled":false,
 "actions":[{
 ...
 }]
}
```

MQTTトピック構造は階層をミラーリングするため、このルールはMQTTトピック階層のさまざまな部分を選択し、新しいペイロードに挿入することができます。
これらの属性は、メッセージが処理され、他のAWSサービスに格納される際のさらなるコンテキストを提供します。

#### ルールエンジンとコマンドトピックの統合
ルールエンジンは、コマンドがAWS IoTデバイスシャドウ、IoTジョブ、またはMQTTコマンドトピックを使用して送信されたかどうかにかかわらず、コマンドの成功または失敗をキャプチャする理想的なサービスです。

#### コマンドの成功のトラッキング
AWS IoT Rules Engineは、個々のコマンドの成功率を追跡するために使用できます。 IoTルールは、セッション識別子などのペイロード情報を外挿する。 ルール選択ステートメントに追加のメタデータを生成します。たとえば、有効期間を作成します。 新しいメッセージペイロードをAmazon DynamoDBなどのデータストアに一時的に格納します。 次のルールは、顧客のこのユースケースの共通の実装を反映しています。 IoTルールは各セッションを個々のDynamoDBレコードとして保存し、WHERE句でこれを受信コマンドと認識するため、このルールではstatusという名前のリテラル値が追加されます。

```

 "sql":"SELECT session ID AS token,timestamp()/1000 as ttl,
topic AS responseTopic, client ID AS requestorId, action.type AS
commandType, 'In Progress' AS status FROM 'cmd/series100/# WHERE
topic(5) == 'credentials' ",
 "awsIoTSqlVersion":"2016-03-23",
 "ruleDisabled":false,
 "actions":[{
   "dynamoDBv2": {
     "roleArn":"arn:aws:sns:us-east-1:12345678:sns_role",
     "putItem":{
     "tableName": "command_sessions_table"
    }
   }
 }]
}
```
IoTエコシステムがコマンド要求を受信すると、前述のルールはすべてのトランスミットコマンドの記録を保持します。

MQTTトピックのベスト・プラクティスに従うことで、応答トピックにはコマンド自体として重複する情報（例えば、元のセッションIDとスマート・ロックで使用される応答トピック）が含まれます。 追加された機能として、クラウドアプリケーションは、セッションIDを使用して応答メタデータからの情報を使用して特定のコマンドのステータスを更新する第2のIoTルールを有することができます。

以下に例を上げます

```

 "sql":"SELECT sessionId AS token, timestamp()/1000 as ttl, topic() AS responseTopic, client ID AS requestorId, res.code AS responseCode, 'Complete' AS status 
FROM 'cmd/series100/# WHERE topic(5) == 'res' ",
 "awsIoTSqlVersion":"2016-03-23",
 "ruleDisabled":false,
   "actions":[{
     "dynamoDBv2": {
     "roleArn":"arn:aws:sns:us-east-1:12345678:sns_role",
     "putItem":{
     "tableName": "command_sessions_table"
   }
  }
 }]
}
```

#### MQTTに関するルール・エンジン機能の調整
IoTルールの使用方法を定義するときは、MQTTのトピックとAWS IoTルール・エンジンに関する次の推奨事項を確認してください。

- トピック（10進）ルール関数を使用して、MQTTトピックに含まれるコンテキスト情報を使用してMQTTメッセージを拡張します。
- timestamp（）ルール関数を使用して、メッセージがAWS IoT Coreに到達した時間を相関させるタイムスタンプを含める。
- コマンドがJSON内にある場合は、ルールエンジンのSELECT句とWHERE句で、セッションIDなどのコンテキストペイロードメタデータを参照します。追加のペイロード情報を使用して、ルールがトリガーされるかどうかを判断することができます。
- AWS IoTアクションの置換テンプレートを使用して、トリガされるAWS IoTルールアクションの一部として変数を表現する。置換表現を使用すると、下流のIoT Ruleアクションのスケーリングと動的ルーティングが容易になります。
- コマンドまたは要求の完了を追跡するには、AWS IoT Rules Engineを使用して、データとステータスをDynamoDBなどのサービスに格納します。メッセージが処理されると、TTLフィールドを使用してDynamoDBからデータを自動的にアーカイブできます。コマンドが高いスループットで送信される場合、Amazon KinesisのIoT Rules Engineを活用して、DynamoDBストレージの前にデータをバッファリングすることができます。
- AWS IoT Rules WHERE句を使用して、AWS IoT Actionに適用されないメッセージをフィルタリングします。 WHERE句は、get_thing_shadow（thingName、roleARN）やaws_lambda（functionArn、inputJson）などのJSONペイロードまたはルールエンジン関数で使用できます。
- AWS IoT Coreがメッセージを受信した後、Amazon KinesisやAmazon SQSなどのAWSサービスを使用して、メッセージがパブリッシュされたMQTTトピックと共にメッセージペイロードをバッファリングします。 メッセージがバッファリングされると、AWS LambdaまたはAmazon EC2で独自のロジックを実行して、ペイロードまたはトピックからフィールド、ペイロードに個々のデバイス、デバイスのタイプ、 またはデバイスグループなどをマップすることができます。

## 結論
MQTTは、シンプルで安全で柔軟性があり、堅牢なIoTプロトコルです。ますます多くの顧客使用事例に合わせて調整できるデバイスとクラウド間の通信ネットワークを定義することができます。このホワイト・ペーパーでは、AWT IoTでMQTTを使用する際の最初のステップをサポートするために、IoTデバイス通信の拡張方法を検討する際に使用できるベスト・プラクティス、ガイドライン、および考慮事項について説明しました。 AWS IoTでは、複数のMQTT通信パターン（ポイント・ツー・ポイント、ブロードキャスト、ファンイン）をさまざまなユースケースに関連して定義できます。さらに、AWS IoTサービスは、AWS IoTジョブ、AWS IoTシャドウ、AWS IoTルールエンジンなど、追加のマネージドサービスを提供します。

IoTソリューションをスケーリングする場合は、テレメトリとコマンドを定義するためのベスト・プラクティスを使用し、MQTT全体の設計がこのホワイト・ペーパーで概説されているMQTTのベスト・プラクティス全体と矛盾しないようにしてください。最後に、IoTのトピック構造がクラウドの運用上の見通しやビジネスの可視性にどのように影響するかを検討してください。

強力なビジネス価値を実現するには、AWT IoTルールを計画、開発、設計、使用して、IoTアプリケーションのMQTTトピック・スキーマを十分に活用する必要があります。

# 免責
本投稿は、個人の意見/取り組みで、所属する企業や団体は関係ありません。
また意訳等などもあるために、参考程度にしてください。