IoT
awsIoT

デバイスシャドウ/デバイスツインを活用した実装のベストプラクティス (2018年末)

デバイスシャドウ、デバイスツインは、デバイスの状態に関する情報 (メタデータ、構成、状態など) を格納する ことを目的としています。

詳しくは AWS IoT Core の Device Shadow や Microsoft Azure の Device Twin を読んでください。

(以下、AWS IoT Core を対象にし、名称はシャドウで統一します)

ここではシャドウの実装に関するベストプラクティスを紹介します。

2019年1月2日追記: Wio LTE を対象とした実装を公開しました


実装すべき内容3点

実装すべきは以下3点です。便宜上 reporter updator restore とします。1



  • Reporter


    • デバイス自身の状態を取得し、取得できた内容を /update へ送信する




  • updator



    • /update/delta から得られた内容を基にデバイス自身の状態を更新する




  • restore



    • /get に問い合わせをして、返ってきた内容を基にデバイス自身の状態を更新する




Reporter

デバイス自身の状態を取得し、取得できた内容を /update へ送信します。

Reporter の実装は必須です。

image.png

送信時の JSON ですが、例えば Digital I/O の D38 番の状態を 0 もしくは 1 で表現するなら、以下の通りです。(d38とか、値は自由に設定できます)

{

"state": {
"reported": {
"d38": 1
}
}
}

詳しくは AWS IoT Core であれば シャドウの更新 を見てください。

Reporter の実行は任意ですが、 デバイスへの更新が発生した時には必ず実行してください

そうしないと、デバイスとシャドウが同期しなくなり、シャドウの中身が役に立たなくなります。

例えば後述する Updator や Restore を実行した結果、デバイスの状態が変化したら無論 Reporter を実行する必要がありますが、実世界上からの干渉によってデバイスの状態が変化したときも Reporter を実行してください。(例えば、実世界でボタンが押された、等)

image.png


Updator

/update/delta から得られた内容を基にデバイス自身の状態を更新します。

Updator の実装は任意です。AWS IoT Core からの状態更新要求を受け取らないのであれば実装しなくても問題ありません。

image.png

Reporter のところでも解説しましたが、 Device に反映したら Reporter を起動するようにします。


Restore

/get に問い合わせをして、返ってきた内容を基にデバイス自身の状態を更新します。

Restore の実装は任意です。ここで解説する挙動が不要であれば実装しなくても問題ありません。

image.png

AWS IoT Core の場合 /get に空文字 (例えば {}) を Publish すると /get/accpeted に以下のような JSON が返ります。


/get/accepted

{

"state": {
"desired":{"d38":1},
"reported":{"d38":0},
"delta":{"d38":1}
},
"metadata":{...}
}

この中で重要なのは reporteddelta です。

state.reported には後述する reporter が送信した "状態" が入っています。この内容を参照しながらデバイスを構成することで「復元」ができます。

state.delta には、オフライン中に発生した要求で、かつデバイスが適用すべき要求が入っています。この内容を参照しながらデバイスを構成することで「オフラインへの対応」ができます。

それぞれ実行するか否かは任意です。

たとえば状態復元しないのであれば reported は無視すれば良いわけです。

また「定期実行」と書いてありますが、定期的に sync することもお勧めです。


Updator との実装共有について

「デバイスの状態を更新する」という関数/メソッドを作る際、/get/accpeted/update/delta では JSON の階層が違うため、ちょっと工夫が必要です。


/get/accpeted

{"state":{"delta":{"d38":1}...}



/update/delta

{"state":{"d38":1}}


以上のようにトピックに応じて state の下の階層を掘り出してから使ってください。


全部を実装した場合のシーケンス

image.png


その他に実装すべきこと


接続を維持する方法

シャドウはステートフル・プロトコル、すなわちセッションを維持する必要があるため、セッション切断時における再接続も実装が必要です。

再接続の必要性判定&処理は Publish 直前に入れるのが良いでしょう。疑似コードで解説します。

def publish(topic, payload) {

if (!MQTT.is_connected) {
MQTT.connect()
}
MQTT.publish(topic, payload)
}

ここで解説した Restore/get へのpublishが含まれているため、Restore を定期的に実行することで自動的に再接続判定&処理がなされるという事になります。


Device ID

デバイスの ID をデバイスに埋め込むと、生産や交換が大変です。これを

(記載中: TODO)


Appendix

シーケンス図は PlantUML で記述しました。(レンダリングには PlantUML previewer を使いました)


Reporter

participant Device

participant "AWS IoT Core"

Device -> Device: Device の状態を取得
Device -> "AWS IoT Core": `{state: {reported: ...}} ` を `/update` に Publish



Updator

participant Device

participant "AWS IoT Core"

group 起動時
Device --> "AWS IoT Core": Subscribe to `/update/delta`
end

group `/update/delta` 着信時のコールバック
Device <- "AWS IoT Core": `{state: {...}}` が返ってくる
Device -> Device: `state` の内容を Device に反映
Device -> "AWS IoT Core": **Reporter**
end



Restore

participant Device

participant "AWS IoT Core"

group 起動時
Device --> "AWS IoT Core": Subscribe to `/get/accepted`
end

group 起動時 もしくは 定期実行
Device -> "AWS IoT Core": `/get` に Publush
Device <- "AWS IoT Core": `{state: {...}}` が返ってくる
Device -> Device: `state.reported` の内容を Device に反映
note right: 最後にデバイス自身が報告した状態を復元する
Device -> Device: `state.delta` の内容を Device に反映
note right: オフライン中に発生した要求を反映するため
Device -> "AWS IoT Core": **Reporter**
end



あとがき

新年からこんなことやってます。





  1. センスがないのは許してください