Measurement Transformation Expression Language

インデックス

概要

IoTAgent ライブラリは、サウス・バウンド APIからの情報を Context Broker に報告する情報に適合させるために使用できる測定変換用の式言語を提供します。この言語の式は、メインの README.md の Device Provisioning API のセクションで説明されているように、プロビジョニングされた属性用に設定できます。

測定値の変換

式の定義

式は、デバイス・プロビジョニング または、構成プロビジョニングのいずれかで Active 属性に対して定義できます。次の例は、定義された式を持つデバイス・プロビジョニングのペイロードを示しています :

{
   "devices":[
      {
         "device_id":"45",
         "protocol":"GENERIC_PROTO",
         "entity_name":"WasteContainer:WC45",
         "entity_type":"WasteContainer",
         "attributes":[
            {
               "name":"location",
               "type":"geo:point",
               "expression": "${@latitude}, ${@longitude}"
            },
            {
               "name":"fillingLevel",
               "type":"Number",
               "expression": "${@level / 100}"
            },
            {
               "object_id":"tt",
               "name":"temperature",
               "type":"Number"
            }
         ]
      }
   ]
}

expression 属性の値は、任意の数の式パターンを含むことができる文字列です。各式パターンは、以下の配列でマークされます : ${<expression>} では、<expression> が式言語の有効な構成です。下記の定義を参照してください。完全な式を評価するには、すべての式パターンを評価可能にする必要があります。すべての式パターンのすべての変数に対して測定値が必要です。

設定とデバイスのプロビジョニングでは、まったく同じ構文が機能します。

変数値

属性式には、他の属性の値から取得した値を含めることができます。これらの値には、デフォルトで String 型があります。変数が関係する場合、ほとんどの算術演算 (*, /, など...) では、元の型にかかわらず、その値が Number にキャストされます。たとえば、変数 @humidity が値 '50' (文字列値)を持つ場合、次の式は次のようになります :

${@humidity * 10}

結果として、500が与えられます。つまり、値 '50' は数値にキャストされ、50 を取得するには 10 が掛けられます。このキャストが失敗した場合 (変数の値が数値でないため。たとえば 'Fifty')、全体的な結果は、NaN になります。

式の実行

式が宣言されたデバイスの新しい測定値が IoTAgent に到着するたびに、デバイスのすべての式の実行がチェックされます。式を含む定義済みのすべてのアクティブ属性について、IoTAgent は、受信した測定値に変数が含まれている式が含まれているものを確認します。変数がカバーされているすべてについて、それらの式は受信した値で実行され、その値は Context Broker で更新されます。

例 : 次のプロビジョニング情報を持つデバイスが IoTAgent にプロビジョニングされている場合 :

{
   "name":"location",
   "type":"geo:point",
   "expression": "${latitude}, ${longitude}"
},
{
   "name":"fillingLevel",
   "type":"Number",
   "expression": "${@fillingLevel / 100}",
   "cast": "Number"
},

次の値を持つ測定値が IoTAgent に到着します :

latitude: 1.9
level: 85.3

実行される唯一の式ルールは 'fillingLevel' 属性のルールになります。Context Broker に送信される値 '0.853' が生成されます。

式が適用されるのは、サウス・バウンド・インタフェースの IoT Agent が受け取った属性名が式変数と一致する場合のみです。それ以外の場合は、サウス・バウンドの値が直接使用されます。次の例を使って説明しましょう :

  "consumption": {
   "type": "String",
   "value": "${trim(@spaces)}"
 }
  • ケース1 : サウス・バウンド・インターフェイスで次の測定値が受信されます :
consumption: "0.44"

spaces 属性が含まれていないため、式は適用されず、consumption 測定の値がそのまま使用されるため、CBに次のように送信されます :

"consumption": {
 "type": "String",
 "value": "0.44"
}
  • ケース2 : サウス・バウンド・インターフェイスで次の測定値が受信されます :
consumption: "0.44"
spaces: "  foobar  "

spaces 属性が含まれているので、次に、次に、式が評価され、0.44 の値をオーバーライドし、CBに次のものを送信します :

"consumption": {
 "type": "String",
 "value": "foobar"
}

言語の説明

parse() 関数が expressionParser.js で、動作する方法は次のとおりです :

  • 式には、文字列または数値の2つの戻り値の型があります。この戻り値の型は、変換される各属性に対して構成する必要があります。デフォルト値の型は String です。
  • 式をエラーなく実行すると、その結果は設定された型にキャストされます。変換が失敗した場合 (たとえば、式が null または String で、Number にキャストされている場合)、測定の更新は失敗し、エラーがデバイスに報告されます。

しかし、Expression Translation プラグインがその機能のために使用する使用法は、常に String 型を使用しています。つまり、最終的に式の結果は常に String にキャストされます。しかし、NGSIv2 では、String 型の結果を正しい型 (すなわち、プロビジョニング操作の属性に対して定義された型) に再キャストできます。これに関する詳細については、NGSIv2のサポートをご覧ください。

変数

IoT Agent によって受信された測定で報告されたすべての情報は、使用する式に使用できます。サウス・バウンドから来るすべての属性に対して、構文 @<object_id> を持つ変数は、式言語での使用のために作成されます。

定数

式言語では、次の2種類の定数を使用できます :

  • 数値 (整数または実数)
  • 文字列 (二重引用符で表示)

現在許可されている文字は次のとおりです :

  • すべての英数字
  • 空白

許可された操作

現在、以下の操作が属性型別に分かれています。

数値演算

  • 乗算 ('*')
  • 区分 ('/')
  • 加算 ('+')
  • 代入 ('-' バイナリ)
  • 否定 ('-' 単項)
  • べき乗 ('^')

文字列操作

  • concatenation ('#') : # で区切られた2つの値の連結を返します。
  • substring location (indexOf(<variable>, <substring>)) : <variable> の文字列値に最初に現れた部分文字列 <substring> を見つけることができるインデックスを返します。
  • substring (substr(<variable>, <start> <end>)) : <start> 位置から開始し、<end> 位置で終了する、パラメータとして渡された文字列変数の部分文字列を返します。
  • whitespace removal (trim(<string>)) : パラメータとして渡された文字列を囲むすべてのスペースを削除します。

他の使用可能な演算子

括弧を使用すると、操作の優先順位を定義できます。トークン間の空白は通常無視されます。

式の例

次の表は、2つの属性を持つ測定の式と期待される結果を示しています : 値が6の "@value" と 値が「DevId629」の "@name"

予想される結果
'5 * @value' 30
'(6 + @value) * 3' 36
'@value / 12 + 1' 1.5
'(5 + 2) * (@value + 7)' 91
'@value * 5.2' 31.2
'"Pruebas " + "De Strings"' 'Pruebas De Strings'
'@name value is @value' 'DevId629 value is 6'

NGSIv2 のサポート

前のセクションで説明したように、式には String 型または Number 型の2つの戻り型があります。前者はデフォルトです。式をエラーなく実行すると、その結果は設定された型にキャストされます。

一方、NGSIv1では、すべての属性の値が String 型であるため、式パーサーでは式の型は常に String に設定され、サウス・バウンドからの情報の変換は replace 命令を使用して行われます。したがって、CB に送信される値は常に Strings です。これは前の例で見ることができます。

一方、NGSIv2 は、JSON 仕様(文字列、数値、ブール値、オブジェクト、配列、および null)に記述されているすべての型を完全にサポートしています。したがって、属性の型フィールドと送信される値の型の間の矛盾を避けるために、式の結果を適切な型 (属性の定義に使用する型) にキャストする必要があります。

現在、式パーサーは JSON 配列と JSON ドキュメントをサポートしていません。この問題に対処するための新しい問題が https://github.com/telefonicaid/iotagent-node-lib/issues/568 で作成されました。残りの型のワークフローは次のようになります。

  1. 変数は、式の型に関係なく String にキャストされます。これについては上記のコメントを参照してください
  2. 式が適用されます
  3. 出力の型は元の属性型に再び変換されます
  4. 属性型が "Integer" の場合、値は整数 (JSON number)にキャストされます
  5. 属性型が "Float" の場合、値は浮動小数点数 (JSON number) にキャストされます
  6. 属性型が "Boolean" の場合、値は ブール値 (JSON boolean)にキャストされます。この変換を行うためにのみ、true または 1 は、true にキャストされます
  7. 属性型が "None" の場合、値は null にキャストされます (JSON null)

例 : 次のプロビジョニング情報を持つデバイスが IoTAgent にプロビジョニングされている場合 :

{
   "name":"status",
   "type":"Boolean",
   "expression": '${@status *  20}'
}

次の値を持つ測定値が IoTAgent に到着します :

status: true
  1. * は乗算であるため、式の型は status を Number にキャストします。true の Number へのキャストは 1 です
  2. 式が評価され、結果は 20 になります
  3. Expression Plugin は常に表現型として String を使用するので、20は 20 にキャストされます。
  4. 属性型は Boolean であるため、結果はブール値にキャストされてから CB に送られます。'20' のブール値へのキャストは、false です。'true' または '1' のみが true にキャストされます。

このワークフローの他の例を、NGSIv2 でサポートされているさまざまなタイプの属性と、2つの可能なタイプの式である、Integer (算術演算) と Strings について以下に示します。

  • pressure (of type "Integer"): 52 -> ${@pressure * 20} -> ${ 52 * 20 } -> $ { 1040 } -> $ { "1040"} -> 1040
  • pressure (of type "Integer"): 52 -> ${trim(@pressure)} -> ${trim("52")} -> $ { "52" } -> $ { "52"} -> 52
  • consumption (of type "Float"): 0.44 -> ${@consumption * 20} -> ${ 0.44 * 20 } -> $ { 8.8 } -> $ { "8.8"} -> 8.8
  • consumption (of type "Float"): 0.44 -> ${trim(@consumption)} -> ${trim("0.44")} -> $ { "0.44" } -> $ { "0.44"} -> 0.44
  • active (of type "None"): null -> ${@active * 20} -> ${ 0 * 20 } -> $ { 0 } -> $ { "0"} -> null
  • active (of type "None"): null -> ${trim(@active)} -> ${trim("null")} -> $ { "null" } -> $ { "null"} -> null
  • update (of type "Boolean"): true -> ${@update * 20} -> ${ 1 * 20 } -> $ { 20 } -> $ { "20"} -> False
  • update (of type "Boolean"): false -> ${@update * 20} -> ${ 0 * 20 } -> $ { 0 } -> $ { "0"} -> False
  • update (of type "Boolean"): true -> ${trim(@updated)} -> ${trim("true")} -> $ { "true" } -> $ { "true"} -> True