IoTCoreのRuleActionでAWS IoT SiteWiseのデータを更新する

2022.04.07

概要

MQTTメッセージを介してAWS IoT SiteWiseのデータを更新する方法について確認してみました。

目次

  • 1.はじめに
    • 1-1.本エントリーを読むために必要なSiteWiseに関する知識
  • 2.やってみる
    • 2-1.構成
    • 2-2.SiteWise
    • 2-3.IoT Core(Rule Action)
    • 2-4.SiteWiseの測定値を更新できることの確認
  • 3.まとめ

1.はじめに

本エントリーではSiteWiseへの理解を深めることを目的として「できるだけ最低限の解説&作業」に着目します。

1-1.本エントリーを読むために必要なSiteWiseに関する知識

SiteWiseでは、まず「Asset Model」、「Asset Model Properties」を定義します。その後に具体的に特定のデバイス(Asset)を定義します。他にも理解すべき事項はありますが、まずはこの点を理解しておく必要があります。

概念 役割、内容
Asset Model 機器、もしくはプロセスの仮想表現 風力タービン
Asset Model Properties 「Asset Model」を構成する属性値 プロペラ回転速度、電力出力
Asset 特定の機器、プロセス XX県に設置された風力タービン、YY県に設置された風力タービン
もう少し詳しく知りたい方はこちらもCheck
以下を知っておくとBetter
- 「Asset」は必ず「Asset Model」から作成される必要があります。
- 既存の「Asset Model」を後から更新することも可能です。その場合はその「Asset Model」に属する全ての「Asset」に変更が反映されます
- 参考: https://docs.aws.amazon.com/ja_jp/iot-sitewise/latest/userguide/industrial-asset-models.html

2.やってみる

2-1.構成

今回の構成は以下の通りです。

SiteWise⇨IoT Coreの順にリソースを作成してみます。
(この順番はどちらが先でも構いません)

2-2.SiteWise

「Asset Model」、「Asset Model Properties」の作成

create-asset-modelを参考にしました。

aws iotsitewise create-asset-model \
--cli-input-json file://create-wind-turbine-model.json
create-wind-turbine-model.jsonの内容
{
    "assetModelName": "Wind Turbine Model",
    "assetModelDescription": "Represents a wind turbine",
    "assetModelProperties": [
        {
            "name": "Serial Number",
            "dataType": "STRING",
            "type": {
                "attribute": {}
            }
        },
        {
            "name": "Generated Power",
            "dataType": "DOUBLE",
            "unit": "kW",
            "type": {
                "measurement": {}
            }
        },
        {
            "name": "Temperature C",
            "dataType": "DOUBLE",
            "unit": "Celsius",
            "type": {
                "measurement": {}
            }
        },
        {
            "name": "Temperature F",
            "dataType": "DOUBLE",
            "unit": "Fahrenheit",
            "type": {
                "transform": {
                    "expression": "temp_c * 9 / 5 + 32",
                    "variables": [
                        {
                            "name": "temp_c",
                            "value": {
                                "propertyId": "Temperature C"
                            }
                        }
                    ]
                }
            }
        },
        {
            "name": "Total Generated Power",
            "dataType": "DOUBLE",
            "unit": "kW",
            "type": {
                "metric": {
                    "expression": "sum(power)",
                    "variables": [
                        {
                            "name": "power",
                            "value": {
                                "propertyId": "Generated Power"
                            }
                        }
                    ],
                    "window": {
                        "tumbling": {
                            "interval": "1h"
                        }
                    }
                }
            }
        }
    ]
}

「Asset Model」、「Asset Model Properties」についてもう少し詳しく知りたい方は以下をご確認ください。

もう少し詳しく知りたい方はこちらもCheck

今回作成したリソースをAWSのマネコンから確認し、理解を深めていきます。 以下の今回作成したリソースを見てください。

真ん中に色々なタブや「アセット」という項目があることがわかります。 まだ「Asset Model」を作成したばかりなので「Asset」がないことがわかります。

また、「Asset Model Properties」にはいくつか種類があり、それぞれの種類ごとのタブが画面中央に表示されています。 (attribute(属性), measurement(測定), transform(変換), metric(メトリクス)の4種類) 本エントリーでは深入りしませんが、各属性値の特性にあった項目を採用する必要があります

以上からわかるかと思いますが、「Asset Model」とは1つ以上の「Asset Model Properties」からなります。

Assetの作成

aws iotsitewise create-asset \
--asset-model-id ${先ほど作成したModelのModelID} \
--asset-name "Wind Turbine 1"
作成したAssetをマネコンから見るとどんな感じなの...?

こんな感じです。

「Model」のマネコン画像と「SerialNumber」属性のIDが一致していることがわかりますね。 このように、同一モデルからいくつAssetを作っても属性や測定値等のIDは同一です。 特定のAssetのPropertyを指定する際は「Asset」と「Property」の両方のIDを指定する必要があることがわかります。 (これらの値を使う代わりに後述する「エイリアス」という仕組みを使うことも可能です。こちらの方が便利だと思います)

エイリアスの作成

先ほど作成したAssetの「GeneratedPower」プロパティを更新する際のエイリアスを作成しておきます。

aws iotsitewise update-asset-property \
--asset-id ${先ほど作成したassetのassetId} \
--property-id ${「Wind Turbine Model」モデル作成時に発行された「Generated Power」measurementのID} \
--property-alias /windTurbine1/generatedPower

上記の「property-alias」で指定した「windTurbine1」には、イメージとしては対象デバイスのthingNameを入れるケースが多いのではないかと思います。
(今回は「SiteWiseにデータを溜め込むために必要な最低限の作業」ということでThingの作成は省略していますが、実際に「SiteWise」を利用する際はIoTCoreでThingを作成し、Thing単位でデータを管理したいはずなので)

豆知識

「属性」、「測定値」、「変換」、「メトリクス」が更新された際に、MQTTメッセージを送信するように設定することも可能です。 その場合は上記コマンドに「property-notification-state」オプションを追加してください。

https://awscli.amazonaws.com/v2/documentation/api/latest/reference/iotsitewise/update-asset-property.html

「Temperature C」プロパティを更新するためのエイリアスも同様の手順で作成しておきます。

aws iotsitewise update-asset-property \
--asset-id ${assetId} \
--property-id ${「Wind Turbine Model」モデル作成時に発行された「Temperature C」measurementのID} \
--property-alias /windTurbine1/temperatureC

以下の通り、対象のアセットにエイリアスを作成することができました。

2-3.IoT Core(Rule Action)

デバイスからのMQTTプロトコルのメッセージをトリガーに、指定した「Asset」の「Asset Model Properties」を更新するRuleActionを作成します。IAM RoleはRuleActionが実行できる程度の権限を設定してください。

aws iot create-topic-rule \
--rule-name "WindTurbineUpdateGeneratedPowerPropRule" \
--topic-rule-payload file://WindTurbineUpdateGeneratedPowerPropRule.json
ここで作成したRuleActionってどんなことをしているの...?
メッセージの内容を「SiteWise」に送信し、特定の「Asset」の「Asset Model property」を更新します。 以下にアクションの内容を示したAWSのマネコンのスクショを貼っておきます。

「プロパティエイリアス」にて更新対象の「Asset」&「Asset Model property」を指定しています。 「timeInSeconds」はここで初登場ですね。「timeInSeconds」で指定した値が「Asset Model property」の更新日時として採用される(AWSに送信された日時ではない)点に注意してください。

このruleActionを作成する際に考えたこと

エイリアスやMQTTトピックパスにthingNameを使うのがいいと思いました。というのも、以下の通りMQTTクライアントIDにはthingNameを利用することが推奨されており、エイリアスにもthingNameを利用することでデバイスやAWSで管理すべき情報を必要最低限のもののみに減らせる、と考えたためです。

>一般的なデバイスのユースケースでは、デフォルトの MQTT クライアント ID としてモノの名前が使用されます。MQTT クライアント ID、証明書、またはシャドウ状態をモノのレジストリ名として使用するというマッピングは強制されませんが、レジストリと Device Shadow サービスの両方で、モノの名前を MQTT クライアント ID として使用することをお勧めします。こうすることで、デバイスの証明書モデルや Shadows の柔軟性を失うことなく、IoT 群の秩序や利便性を維持することができます。

参照: https://docs.aws.amazon.com/ja_jp/iot/latest/developerguide/iot-thing-management.html

thingNameを採用することで、以下のようなメリットもあります。
参照: https://dev.classmethod.jp/articles/aws-iot-core-policy/

また、MQTTトピックにthingNameを入れておくと、各デバイス証明書にアタッチするIoTPolicyに付与するMQTTトピックパスに対する権限を、そのthingNameに関するもののみに絞り込むことが可能となり、よりセキュリティ的に恩恵を受けることができます。
(各thingは自分のthingNameに該当するトピックにしかメッセージをPublishできない、等のIoTPolicyを設定する)

それと、更新したいプロパティごとに「異なるトピックを採用」&「Ruleを作成」しています。

私がハマった箇所

より上位の階層として「デバイスを管理する会社名とか入れられると嬉しいかも」と思いpropertyAliasをSQL内で計算してAS句を使用しようとしたのですが、以下の通りそれはNGのようです。「propertyAliasはデバイスが知っている情報だけ(thingName,更新対象の測定値名等)で構成する」というのも重要な設計ポイントかもしれません。

>置換テンプレート内の式は "SELECT..." ステートメントとは独立して評価されるため、AS 句を使用して作成されたエイリアスを参照することはできません。元のペイロード、関数、および演算子に存在する情報のみを参照できます。

https://docs.aws.amazon.com/ja_jp/iot/latest/developerguide/iot-substitution-templates.html

WindTurbineUpdateGeneratedPowerPropRule.jsonの中身
{
  "sql": "SELECT generatedPower, timeInSeconds, propertyAlias FROM 'things/+/props/generatedPower/update'",
  "description": "Update props.",
  "ruleDisabled": false,
  "awsIotSqlVersion": "2016-03-23",
  "actions": [
    {
      "iotSiteWise": {
        "putAssetPropertyValueEntries": [
          {
            "propertyAlias": "${propertyAlias}",
            "propertyValues": [
              {
                "value": {
                  "doubleValue": "${generatedPower}"
                },
                "timestamp": {
                  "timeInSeconds": "${timeInSeconds}"
                }
              }
            ]
          }
        ],
        "roleArn": ${RuleActionを実行するために用意したIAM RoleのARN}
      }
    }
  ]
}

ほぼほぼ同じような内容で以下のRuleも作成しました。 (関係箇所をgeneratedPowerをTemperatureCに置き換えただけ)

aws iot create-topic-rule \
--rule-name "WindTurbinUpdateTemperatureCRule" \
--topic-rule-payload file://WindTurbinUpdateTemperatureCRule.json

2-4.SiteWiseの測定値を更新できることの確認

さて、ここまでで対象のAsset ModelのPropertyを更新できるようになりましたので、実際に更新処理を試してみます。
まずは、変更前を確認しておきます。

変更前

以下の通り「測定値」には値がまだ入っていません。

では、AWSのマネコンから以下の内容でMQTTプロトコルでメッセージを送信することで、値を更新してみます。 (トピックは「things/windTurbine1/props/generatedPower/update」)

{
  "propertyAlias": "/windTurbine1/generatedPower",
  "generatedPower": 111.111,
  "timeInSeconds": 1649307600
}

変更後

「Generated Power」測定値を更新できました。「Generated Power」測定値を参照しているメトリクスも更新できています。

今度は「Temperature C」測定値を更新してみます。

(トピックは「things/windTurbine1/props/temperatureC/update」)

{
  "propertyAlias": "/windTurbine1/temperatureC",
  "temperatureC": 999.999,
  "timeInSeconds": 1649308000
}

更新できました。

「TemperatureF」への変換もできてます。
(適当な値入れたので、とんでもない温度になってるけど...)

3.まとめ

今回のようにMQTTメッセージでmeasurementを更新する場合は以下を気にすると良さそうです。

  • propertyAliasはデバイスが知っている情報だけ(ex.thingName、更新対象の属性名)で構成する
    • IoTCoreのSQL内でAS句を使用したエイリアスは利用不可能なため
  • IoTCoreRuleを作成するトピックパスにはデバイスを特定する情報(ex.thingName)を入れ、デバイス証明書にアタッチするIoTPolicyにてそのデバイスに関するトピックにしかメッセージを送信できないように権限を絞り込む
    • まあ、これはSiteWiseに限らずIoTCoreでMQTT使う場合の基本的な考え方とも思いますが
  • 更新対象のmeasurementごとに別々のIoTCoreRuleを作成する

SiteWiseのmeasurementを更新する方法は他にも色々ありますので、そちらも検討して実運用に最適な方法を検討したいですね。

参照