AWS CLI で AWS IoT Sitewise の階層化されたアセットモデルを作成してみた

AWS IoT SiteWise のアセットモデルの計算式は CLI で設定すると楽です。
2022.04.13

前回の記事で、AWS IoT SiteWise にてアセットモデルを階層化してセンサーデータを可視化しました。

その際、アセットモデルの作成は全てマネジメントコンソールで行いましたが、「定義の変換」「メトリクス」 のプロパティで利用する計算式の入力方法が特殊だったかと思います。
そこで今回は、この計算式の設定を AWS CLI で行ってみます。また、階層をまたいだ計算式の設定方法も紹介したいと思います。

はじめに

本記事は下記のドキュメントを参考にしながら、実際の手順や気になった点を確認してみたものになります。
実際に試してみて初めて判明した内容も含んでいますので、ドキュメントと合わせてご覧いただけると幸いです。

課題

先程も書きましたが、アセットモデルを作成するとき一部のアセットプロパティ(メトリクスやデータ変換など)では計算式を設定することがあります。(例:華氏を摂氏に変換)

しかし、マネジメントコンソールで計算式を設定するときは、テキストのコピペで入力することができず、下記のような操作が必要になります。(以下は前回の記事からの引用です)

01-set-math

数式が単純だったり数が少なければコンソールからの入力でも問題ありませんが、モデルの階層をまたいでプロパティを参照するような複雑な計算式になるとうまく入力ができないことがあり、ちゃぶ台をひっくり返したくなります。

そこで、次の章より簡単なサンプルを交えつつ、AWS CLI による設定方法を見ていきたいと思います。

AWS CLI で単純なアセットモデルを作成する

手始めに簡単なアセットモデルを作成してみましょう。作成するモデルは次のようなアセットプロパティを持つものとします。

  • Measurements(センサーデータ)
    • 温度(プロパティ名:temperature
      • データ型:DOUBLE
      • 単位:
    • 湿度(プロパティ名:humidity
      • データ型:DOUBLE
      • 単位:%
  • Metrics (温度の 5 分間の平均)
    • プロパティ名:avg_temperature

上記の内容に沿った設定内容を Yaml もしくは JSON ファイルとして作成します。(今回は Yaml で作成)

sample-asset-model.yaml

---
assetModelName: Sample Asset Model
assetModelProperties:
# 温度
- name: temperature
  dataType: DOUBLE
  unit: '℃'
  type:
    measurement: {}
# 湿度
- name: humidity
  dataType: DOUBLE
  unit: '%'
  type:
    measurement: {}
# アセットプロパティのタイプ:Metrics(温度 の5分間の平均)
- name: avg_temperature
  dataType: DOUBLE
  type:
    metric:
      expression: avg(my_var_temperature)
      variables:
      - name: my_var_temperature
        value:
          propertyId: temperature
      window:
        tumbling:
          interval: 5m

Yaml ファイルが作成できたら下記のコマンドでアセットモデルを作成します。

$ aws iotsitewise create-asset-model \
  --cli-input-yaml file://sample-asset-model.yaml

コマンドと Yaml ファイルの詳細については下記を参照して下さい。

コマンドが実行できたら、コンソールで内容を確認してみましょう。
温度と湿度のプロパティが作成されていますね。

03-sample-asset-measure

メトリクスも「温度の5分間の平均」として作成できていました。

04-sample-asset-metrics

設定内容のポイント

ここでポイントになるのは、温度の平均を定義しているプロパティの「20行目以降の箇所」です。(下記は該当箇所より抜粋)
ここでは数式を avg(my_var_temperature) と定義しています。

metric:
  expression: avg(my_var_temperature)
  variables:
  - name: my_var_temperature
    value:
      propertyId: temperature

しかし、直感的に捉えると平均値の数式は avg(temperature) となってほしいですし、実際にコンソールで確認すると下記のように表示されます。

02-def-metrics

ここで先程の Yaml ファイルを改めて確認すると「計算式で参照するアセットプロパティ(temperature)を my_var_temperature という変数に渡して計算式にセット」していることが分かります。
(下記は該当箇所を抜粋したもの)

metric:
  expression: avg(my_var_temperature)
  variables:
  - name: my_var_temperature
    value:
      propertyId: temperature

このように内部で変数化して計算式を組み立てているので、コンソール上でもテキストでコピペができないようになっているものと考えられます。

なお、この変数名はなんでもいいですが正規表現 ^[a-z][a-z0-9_]*$ にマッチしたものである必要があります。

コンソールで設定したアセットモデルの詳細

ここまでの内容で、アセットプロパティで計算式をセットする時に対象のプロパティを内部で変数化することが分かりました。では逆に、コンソールで設定した数式はどうなっているか見てみましょう。

先程 CLI で作成したモデル「Sample Asset Model」は削除して、改めて同じ内容のモデルをコンソール上で作成しておきます。

コンソール上でモデルが作成できたら、モデルの詳細を確認してみましょう。
モデルの詳細を参照するのは describe-asset-model コマンドを使います。

$ aws iotsitewise describe-asset-model \
  --asset-model-id [コンソールで作成したアセットモデルの ID ]

すると、下記のようなレスポンスを受け取ることができます。

{
    "assetModelId": "91902cd8-bbca-4c22-a112-54d262e06ae3",
    "assetModelArn": "arn:aws:iotsitewise:ap-northeast-1:[AWS_ACCOUNT_ID]:asset-model/91902cd8-bbca-4c22-a112-54d262e06ae3",
    "assetModelName": "Sample Asset Model",
    "assetModelProperties": [
        {
            "id": "9044fc52-6ff5-432d-be49-9e3b191ef1e0",
            "name": "temperature",
            "dataType": "DOUBLE",
            "unit": "℃",
            "type": {
                "measurement": {}
            }
        },
        {
            "id": "61b7c464-b83e-4c16-b755-c43ad51f6443",
            "name": "humidity",
            "dataType": "DOUBLE",
            "unit": "%",
            "type": {
                "measurement": {}
            }
        },
        {
            "id": "79f9393f-0396-4a99-9592-52618a693c7c",
            "name": "avg_temperature",
            "dataType": "DOUBLE",
            "type": {
                "metric": {
                    "expression": "avg(var_temperature)",
                    "variables": [
                        {
                            "name": "var_temperature",
                            "value": {
                                "propertyId": "9044fc52-6ff5-432d-be49-9e3b191ef1e0"
                            }
                        }
                    ],
                    "window": {
                        "tumbling": {
                            "interval": "5m"
                        }
                    }
                }
            }
        }
    ],
    "assetModelHierarchies": [],
    "assetModelCompositeModels": [],
    "assetModelCreationDate": "2022-04-12T17:51:21+09:00",
    "assetModelLastUpdateDate": "2022-04-12T17:51:25+09:00",
    "assetModelStatus": {
        "state": "ACTIVE"
    }
}

上記の通り、メトリクスの数式には var_temperature という変数で温度のプロパティ ID (9044fc52-6ff5-432d-be49-9e3b191ef1e0) がセットされていることが分かりました。

どうやら同じ階層内でプロパティを参照する場合は、var_[対象のプロパティ名] という形で変数が作成されるようです。

なお、この propertyId を設定する時は「プロパティ ID」もしくは「プロパティ名」が指定可能です。

AWS CLI で階層化したアセットモデルを作成する

以上で AWS CLI による基本的なモデルの作成方法が分かりました。
次は階層化したアセットモデルを作成して、さらに下位モデルのプロパティから統計値を計算するメトリクスを作成してみたいと思います。

具体的には前回の階層化されたアセットモデルを AWS CLI で作ってみます。
作成するモデルはそれぞれ次のようなアセットプロパティを持つものとします。

  • 下位アセットモデル(モデル名:Lower Test Model
    • Measurements(センサーデータ)
      • クロック数(プロパティ名:clock
        • データ型:DOUBLE
        • 単位:Hz
      • 温度(プロパティ名:temperature
        • データ型:DOUBLE
        • 単位:
    • Transforms(クロック数 clock の単位を MHz に変換)
      • プロパティ名:clock_MHz
    • Metrics(クロック数 clock_MHz の平均)
      • プロパティ名:avg_clock
      • 平均値を取る間隔:1分間
  • 上位アセットモデル(モデル名:Upper Test Model
    • Metrics(下位モデルのアセット全体のクロック数の平均)
      • プロパティ名:total_avg_clock
      • 平均値を取る間隔:1分間

下位アセットモデルの作成

最初に下位モデルを作成します。(上位モデルから下位モデルを参照するので下位モデルから作成します)
先程と同じようにメトリクスの数式には my_var_clock_mhz という変数に、同じモデル内の clock_MHz プロパティを入れて計算式にセットしています。

create-asset-model-lower.yaml

---
assetModelName: Lower Test Model
assetModelProperties:
- name: clock
  dataType: DOUBLE
  unit: Hz
  type:
    measurement: {}
- name: temperature
  dataType: DOUBLE
  unit: "℃"
  type:
    measurement: {}
- name: clock_MHz
  dataType: DOUBLE
  unit: MHz
  type:
    transform:
      expression: var_clock/1000000
      variables:
      - name: var_clock
        value:
          propertyId: clock
- name: avg_clock
  dataType: DOUBLE
  type:
    metric:
      expression: avg(my_var_clock_mhz)
      variables:
      - name: my_var_clock_mhz
        value:
          propertyId: clock_MHz
      window:
        tumbling:
          interval: 1m

先程と同様に create-asset-model コマンドでアセットモデルを作成します。

$ aws iotsitewise create-asset-model \
  --cli-input-yaml file://create-asset-model-lower.yaml

上位アセットモデルの作成

次に下記の Yaml ファイルを作成して上位モデルを作成します。

create-asset-model-upper.yaml

---
assetModelName: Upper Test Model
assetModelProperties:
- name: total_avg_clock
  dataType: DOUBLE
  type:
    metric:
      expression: avg(my_lower_model_metrics)
      variables:
      - name: my_lower_model_metrics
        value:
          propertyId: [参照先の下位モデルのメトリクス(avg_clock)のプロパティ ID ]
          hierarchyId: device_hierarchy
      window:
        tumbling:
          interval: 1m
assetModelHierarchies: 
- name: device_hierarchy
  childAssetModelId: [下位モデルのモデル ID]

ファイルができたら同じようにcreate-asset-model コマンドでアセットモデルを作成します。

$ aws iotsitewise create-asset-model \
  --cli-input-yaml file://create-asset-model-upper.yaml

コンソールで確認すると「コンソール上で設定した場合」と同じようにメトリクスが定義されていました。

05-upper-model-metrics

メトリクスで参照されている階層(device_hierarchy)も定義できていることが分かります。

07-define-hierarchy

設定内容のポイント

ポイントは 11〜 13 行目の内容です。 (下記は該当箇所の抜粋)

metric:
  expression: avg(my_lower_model_metrics)
  variables:
  - name: my_lower_model_metrics
    value:
      propertyId: [参照先の下位モデルのメトリクス(avg_clock)のプロパティ ID ]
      hierarchyId: device_hierarchy

先程の「下位モデル」や冒頭の「単純なアセットモデル」の Yaml ファイルでは、計算式に設定するプロパティを propertyId の指定だけで行っていました。

今回は階層をまたいだ下位モデルのプロパティを参照するため、対象の「階層ID(hierarchyId)」と「その階層にある対象のプロパティ(propertyId)」の両方を指定しています。これにより参照すべきプロパティを特定することができるようになります。

階層をまたがない場合なら同じ階層のプロパティを参照することになるので、propertyId には「プロパティ名」を指定できますが、階層をまたぐ場合は ID そのものを指定する必要があります。
一方で hierarchyId は「階層ID」もしくは「階層名」をセットすることができます。

なお、propertyIdchildAssetModelId の値については、事前に作成した下位モデルの内容から取得してセットする必要があります。(下記は該当箇所より抜粋)

    metric:
      expression: avg(my_lower_model_metrics)
      variables:
      - name: my_lower_model_metrics
        value:
          propertyId: [参照先の下位モデルのメトリクス(avg_clock)のプロパティ ID ]
          hierarchyId: device_hierarchy
      window:
        tumbling:
          interval: 1m
assetModelHierarchies: 
- name: device_hierarchy
  childAssetModelId: [下位モデルのモデル ID]

ぞれぞれの意味は下記のとおりです。

  • propertyId : 参照している下位モデルのメトリクス(avg_clock)のプロパティID
  • childAssetModelId : 参照している下位モデル(Lower Test Model)のモデルID

各 ID は次のコマンドで取得可能です。(マネジメントコンソール上からも確認できます)

  • 下位モデルのモデル ID
$ aws iotsitewise list-asset-models \
  --query "assetModelSummaries[?name=='Lower Test Model'].id"
  • 参照先の下位モデルのメトリクス(avg_clock)のプロパティID
$ aws iotsitewise describe-asset-model \
  --asset-model-id [上記コマンドで取得したモデル ID] \
  --query "assetModelProperties[?name=='avg_clock'].id"

コンソールで設定したアセットモデルの詳細

先程と同じように、CLI ではなくコンソール上で設定した場合の設定内容も見てみましょう。今回は「階層をまたいで設定した上位モデルのメトリクスの計算式」を見てみます。

先程 CLI で作成した上位モデル (Upper Test Model)を削除して、コンソール上で同じ設定内容で作成し直します。
以下のように計算式の見た目は変わっていません。(作り直したのでプロパティ ID は変わっています)

06-make-upper-model-on-console

次に describe-asset-model コマンドで作成したモデルの詳細を見てみましょう。

aws iotsitewise describe-asset-model \
  --asset-model-id [コンソールで作成したアセットモデルの ID ]

次のようなレスポンスが得られました。

{
    "assetModelId": "3e527ae5-3820-4019-a18d-6f011baf6ef8",
    "assetModelArn": "arn:aws:iotsitewise:ap-northeast-1:[AWS_ACCOUNT_ID]:asset-model/3e527ae5-3820-4019-a18d-6f011baf6ef8",
    "assetModelName": "Upper Test Model",
    "assetModelProperties": [
        {
            "id": "d5f26461-701e-41e6-aca9-bbf15742eb3c",
            "name": "total_avg_clock",
            "dataType": "DOUBLE",
            "type": {
                "metric": {
                    "expression": "avg(v_lowertestmode_device_hierarchy_avg_clock)",
                    "variables": [
                        {
                            "name": "v_lowertestmode_device_hierarchy_avg_clock",
                            "value": {
                                "propertyId": "cbf2bbe3-34ee-4e56-bf55-8956a67ca696",
                                "hierarchyId": "a3179b56-17df-460b-a84f-2fa27215c987"
                            }
                        }
                    ],
                    "window": {
                        "tumbling": {
                            "interval": "1m"
                        }
                    }
                }
            }
        }
    ],
    "assetModelHierarchies": [
        {
            "id": "a3179b56-17df-460b-a84f-2fa27215c987",
            "name": "device_hierarchy",
            "childAssetModelId": "823c183c-ea4d-492a-8878-8fb32190f1d3"
        }
    ],
    "assetModelCompositeModels": [],
    "assetModelCreationDate": "2022-04-12T19:49:53+09:00",
    "assetModelLastUpdateDate": "2022-04-12T19:49:59+09:00",
    "assetModelStatus": {
        "state": "ACTIVE"
    }
}

注目ポイントは 12 行目(と 15 行目)です。
これまでに何度も説明していますが、アセットプロパティで計算式をセットする際は対象のプロパティを変数化して計算式に渡します。
今回は v_lowertestmode_device_hierarchy_avg_clock という変数になっていることが上記の 12 行目から分かります。そして、この変数名の構成を少し分解してみると次のような3つのブロックから成る書式である事が分かります。

v_[ブロックA]_[ブロックB]_[ブロックC]
  • ブロックA
    • 階層定義で参照している下位のモデル名(Lower Test Model
      • lowertestmodeの部分
  • ブロックB
    • 階層名
      • device_hierarchyの部分
  • ブロックC
    • 参照したい下位アセットのメトリクス名
      • avg_clockの部分

網羅的に確認したわけではありませんが、モデル名や階層名を色々変えて検証してみた所、各ブロックで下記のような変換が行われた変数がセットされているようでした。

  • 英大文字の削除 or 小文字に変換
  • スペースの削除
  • ハイフン(-)の削除

これは、内部で変数として利用できる文字として、正規表現 ^[a-z][a-z0-9_]*$ にマッチしたものに限る制約に則った変換が実行されていると考えられます。

ちなみに CLI で作成した場合は、設定ファイル(JSON, YAML)で指定した変数がそのままセットされます。

「内部で使われている変数」を把握しておくといいケース

あまりケースとしては多くないかもしれませんが、初めにコンソールで設定したアセットモデルを後から AWS CLI などで更新する際は「内部で使われている変数」を事前に確認しておく必要があります。

アセットモデルのコンソール画面では「内部で使われている変数」は確認できないので describe-asset-model コマンド等を使って事前に確認しておきましょう。

なお、後でアセットモデルを更新する場合は、update-asset-model コマンドを使いますが、設定ファイルの記述方法や注意すべきポイントは create-asset-model コマンドと同様です。

最後に

AWS CLI でアセットモデルを作成してみたことで、マネジメントコンソールだとメトリクスなどの計算式がコピペで設定できない理由の一端を垣間見ることができました。
非常に細かい点を交えた内容になりましたが、どなたかのお役に立てれば幸いです。

以上です。