OCI Internet of Thingsプラットフォームでのデジタル・ツイン・アダプタの操作 (2026/08/13)

OCI Internet of Thingsプラットフォームでのデジタル・ツイン・アダプタの操作 (2026/08/13)

https://blogs.oracle.com/cloud-infrastructure/working-with-iot-digital-twin-adapters

投稿者:Pete St. Pierre | Director, Product Management

デジタルツインアプリケーションでは、各アセットの一貫したビューが必要ですが、デバイスのテレメトリデータはさまざまな形式で届くことがよくあります。あるデバイスはmotorTemperatureという名前の属性を送信し、別のデバイスはmtrTempを送信するかもしれません。また、別のデバイスはpsi単位で圧力値を報告する一方で、モデルは圧力をbar単位で保存しているかもしれません。OCI IoT Platform (OCI IoT Platform) のデジタルツインアダプタは、これらのデバイス固有のペイロードを、アプリケーションが使用する標準的なモデル構造に変換します。

WaterPumpモデルを使用して、アダプタがタイムスタンプ、ネストされたコンポーネント、属性名、単位変換、エンドポイントベースのルーティング、および選択されたJQ式をどのように処理して、異種テレメトリストリームを標準モデルにマッピングするかを示します。

はじめに

デジタルツインアダプタは、直接接続されたデバイスと間接接続されたデバイスの両方に適用されます。どちらの場合も、アダプタは受信したペイロードをデジタルツインモデルにマッピングし、アプリケーションが一貫性のある資産表現を読み取れるようにします。

これらの例は、デバイステレメトリにおけるアダプタの動作に焦点を当てています。ゲートウェイ構成、ゲートウェイルーティング、およびゲートウェイテレメトリ設定は、これらの例の範囲外です。

例:ウォーターポンプモデル

この例では、ポンプレベルのテレメトリと再利用可能なElectricMotorコンポーネントを備えたWaterPumpモデルを使用します。WaterPumpモデルは、関連記事「OCI IoTプラットフォームにおけるデジタルツインモデルの理解」で説明されている形式に従います。ターゲットモデルの形状は次のようになります。


クリップボードにコピーされました
エラー: コピーできませんでした
クリップボードにコピーされました
エラー: コピーできませんでした
{
  "motor": {
    "motorTemperature": 68.4,
    "vibrationLevel": 1.7,
    "powerConsumption": 12.6
  },
  "flowRate": 247.5,
  "dischargePressure": 4.3
}

このモデルでは、モーターのテレメトリをポンプの一部として扱います。アプリケーションは、 1つの標準構造体から、 motor.motorTemperaturemotor.vibrationLevelmotor.powerConsumptionflowRate、およびdischargePressureを読み取ることができます。

アダプターは何をするのか

アダプタは、受信したデバイスペイロードをデジタルツインモデルにマッピングします。モデルはターゲット構造を定義し、アダプタはソースペイロードの値がどのようにしてその構造に移動するかを記述します。

デジタルツインのワークフローは、3つの関連オブジェクトを使用します。モデルは資産を記述し、アダプタはペイロード値をモデルにマッピングします。デジタルツインインスタンスは、モデルをアダプタに関連付けます。この分離により、アプリケーションが安定したモデルを読み取る一方で、デバイスのペイロードは変化させることができます。

アダプタ定義とデジタルツインモデルを組み合わせてテレメトリを正規化する例

図1:デジタルツインインスタンスは、モデルとアダプタを関連付けることで、アプリケーションが正規化されたツイン状態を読み取れるようにします。

アダプタの説明の構造

アダプタ記述では、エンベロープ記述と受信ルート記述という、関連する2つのJSON要素を使用します。エンベロープは受信メッセージの形状を記述し、受信ルートは一致するメッセージがデジタルツインモデルにどのようにマッピングされるかを定義します。

エンベロープの説明:


クリップボードにコピーされました
エラー: コピーできませんでした
クリップボードにコピーされました
エラー: コピーできませんでした
{
    "referenceEndpoint": "/waterpump",
    "referencePayload": {
      "dataFormat": "JSON",
      "data": {
        "motor": {
          "motorTemperature": 68.4,
          "vibrationLevel": 1.7,
          "powerConsumption": 12.6
        },
        "flowRate": 247.5,
        "dischargePressure": 4.3
    }
  }
}

エンベロープの説明は、モデルを識別し、代表的な受信メッセージを提供します。displayName フィールドdescriptionフィールドにより、開発者がファイルを読みやすくすることができます。digitalTwinModelSpecUri は、説明WaterPumpモデルに接続します。inboundEnvelopeセクションには、referenceEndpointreferencePayload含まれます。referencePayload は、期待されるテレメトリをテストおよび文書化するために使用されるデータ形式とサンプルペイロード構造を定義します。

エンベロープの説明は、アダプタマッピングが適用される前にデバイスまたはインテグレーションが送信する内容を示します。このデフォルトスタイルの例では、ペイロードはすでにWaterPumpモデルの形状に従っています。モーターテレメトリはmotorの下にネストされflowRatedischargePressure は最上位レベルに表示されます。受信ルートの説明:


クリップボードにコピーされました
エラー: コピーできませんでした
クリップボードにコピーされました
エラー: コピーできませんでした
[
    {
      "condition": "*",
      "description": "Map the component-aware water pump payload directly to the WaterPump model.",
      "payloadMapping": {
      "$.motor.motorTemperature": "$.motor.motorTemperature",
      "$.motor.vibrationLevel": "$.motor.vibrationLevel",
      "$.motor.powerConsumption": "$.motor.powerConsumption",
      "$.flowRate": "$.flowRate",
      "$.dischargePressure": "$.dischargePressure"
    },
      "referencePayload": {
        "dataFormat": "JSON",
        "data": {
          "motor": {
            "motorTemperature": 68.4,
            "vibrationLevel": 1.7,
            "powerConsumption": 12.6
          },
          "flowRate": 247.5,
          "dischargePressure": 4.3
        }
      }
    }
]

受信ルートは、プラットフォームがアダプタルートに一致するメッセージをどのように処理するかを定義します。inboundRoutes配列には、1 つ以上のルート定義を含めることができます。各ルートには、条件説明payloadMapping、およびルートレベルの referencePayload が含まれます。条件は、ルートが適用されるタイミングを決定します。ワイルドカード値 * は、一致するすべての受信メッセージにルートを適用します。

payloadMappingセクションでは、ソース値デジタルツインモデルの属性にマッピングします。このデフォルトスタイルの例では、入力属性名がモデルと一致するため、flowRatedischargePressureは直接マッピングされます。motorマッピングでは、ネストされたコンポーネントオブジェクトを作成し、motorTemperaturevibrationLevelpowerConsumption をWaterPumpモデルで使用されるElectricMotorコンポーネントにマッピングします

JQが当てはまる場所

JQはJSON用のクエリおよび変換言語です。アダプタでは、$.flowRateのような単純なパスによって、受信ペイロードから値がコピーされます。${(.dischPressPsi * 0.0689475729)}のようなJQ式は、プラットフォームがデジタルツインに書き込む前に値を変換します。

  • envelopeMappingを使用して、timeObservedなどのメタデータを抽出します。
  • payloadMappingを使用して、テレメトリ値をマッピングまたは変換します。
  • JQ式を使用して、オブジェクトの構築、フィールド名の変更、単位の変換、タイムスタンプ値の正規化を行います。

JQ式は、経路評価およびペイロードマッピング中にターゲット値を計算します。式は、経路条件およびマッピングにおいて、${ … }などのプレースホルダー構文を使用します。エンドポイントセグメント、ヘッダー、またはペイロード値に基づいて経路を選択したり、受信テレメトリを変換したり、単位を変換したり、フィールド名を変更したり、タイムスタンプを正規化したり、デジタルツインモデルのスキーマに一致するJSONを生成したりできます。正規化された出力は、属性タイプ、範囲、単位など、モデル検証を満たす必要があります。

アダプタのマッピングでは、モデルスキーマも考慮する必要があります。算術演算とfloor関数はサポートされていますが、number()toInteger()などの型変換ヘルパーはルート式ではサポートされていません。整数型のモデル属性の場合、マッピングは${(.velocity_kph / 1.609) | floor }のような整数の数値を出力する必要があります。double型の属性の場合、小数値も受け入れられます。floor関数は、整数を格納する場合にのみ使用してください。

エンドポイントのマッチングでは、ワイルドカードパターンではなく、${ endpoint (1) == 'home' and endpoint (2) == 'data' and endpoint (3) == 'status'} のようなセグメントベースの条件を使用する必要があります。

時刻処理に関しては、デバイスが観測タイムスタンプを提供する場合はtimeObservedをマッピングします。そうでない場合は、プラットフォームは受信した時刻を使用します。fromdateformatやtodateformatなどの関数を使用するとエンベロープまたはペイロードのマッピングでタイムスタンプを正規化できます。

カスタムアダプターの作成

デバイスがモデルと同じ形式でテレメトリを送信すると、サービスは受信メッセージを処理するためのデフォルトアダプタを自動的に作成します。デフォルトアダプタを使用する場合、開発者による操作は不要です。テレメトリがモデルと一致しない場合、またはプラットフォームがサンプルを保存する前にメタデータを調整する必要がある場合は、カスタムアダプタを使用してください。

一般的な調整項目には、日付と時刻の書式設定、ペイロードの形状、属性名、測定単位などがあります。以下の例では、同じWaterPumpモデルを使用しながら、受信テレメトリを変更することで、アダプタがそれぞれのケースをどのように処理するかを示しています。

例1:タイムスタンプの調整

一般的なアダプタタスクは、JSONペイロード内の値に基づいてテレメトリサンプルのタイムスタンプを調整することです。この例では、テレメトリ値は既にWaterPumpモデルと一致しています。アダプタはペイロードからデバイスの観測時間を抽出し、それをtimeObservedにマッピングします。受信テレメトリ:


クリップボードにコピーされました
エラー: コピーできませんでした
クリップボードにコピーされました
エラー: コピーできませんでした
{
  "timestamp": "2026-07-08T12:00:00.000000Z",
  "motor": {
    "motorTemperature": 68.4,
    "vibrationLevel": 1.7,
    "powerConsumption": 12.6
  },
  "flowRate": 247.5,
  "dischargePressure": 4.3
}

エンベロープマッピング:


クリップボードにコピーされました
エラー: コピーできませんでした
クリップボードにコピーされました
エラー: コピーできませんでした
{
  "referenceEndpoint": "/waterpump",
  "envelopeMapping": {
    "timeObserved": "$.timestamp"
  }
}

テレメトリマッピング:


クリップボードにコピーされました
エラー: コピーできませんでした
クリップボードにコピーされました
エラー: コピーできませんでした
{
  "$.motor": "${ {motorTemperature: .motor.motorTemperature, vibrationLevel: .motor.vibrationLevel, powerConsumption: .motor.powerConsumption} }",
  "$.flowRate": "$.flowRate",
  "$.dischargePressure": "$.dischargePressure"
}

タイムスタンプは、デバイスが測定値を観測した日時を示します。これをtimeObservedにマッピングすることで、観測時間と受信時間を分離できます。

タイムスタンプは、アダプター対応のこのフォーマットを使用します。


クリップボードにコピーされました
エラー: コピーできませんでした
クリップボードにコピーされました
エラー: コピーできませんでした
2026-07-08T12:00:00.000000Z

一般的なタイムスタンプ形式には、小数秒を含むISO 8601 UTC、小数秒を含まないISO 8601 UTC、タイムゾーンオフセットを含むISO 8601、エポック秒、エポックミリ秒などがあります。ソースタイムスタンプは、アダプタプロセスが想定する形式に正規化する必要があります。

JQは、正規化のための日付と時刻の機能を提供します。

  • strptime(format) は、フォーマットパターンを使用してタイムスタンプ文字列を解析します。
  • strftime(format)は、解析されたタイムスタンプをフォーマットします。
  • mktimeは、解析された時間配列をエポック秒に変換します。
  • gmtimeはエポック秒をUTC時刻配列に変換します。
  • localtimeはエポック秒をローカルタイム配列に変換します。
  • fromdateiso8601 は、ISO 8601 タイムスタンプをエポック秒に解析します。
  • todateiso8601は、エポック秒を ISO 8601 タイムスタンプとしてフォーマットします。

例2:フラットテレメトリ

次の例では、ペイロードは最上位レベルのすべての値を報告しますが、モデルはモーターコンポーネント内部のモーターテレメトリを想定しています


クリップボードにコピーされました
エラー: コピーできませんでした
クリップボードにコピーされました
エラー: コピーできませんでした
{
  "motorTemperature": 68.4,
  "vibrationLevel": 1.7,
  "powerConsumption": 12.6,
  "flowRate": 247.5,
  "dischargePressure": 4.3
}

アダプターマッピング:


クリップボードにコピーされました
エラー: コピーできませんでした
クリップボードにコピーされました
エラー: コピーできませんでした
{
  "$.motor": "${ {motorTemperature: .motorTemperature, vibrationLevel: .vibrationLevel, powerConsumption: .powerConsumption} }",
  "$.flowRate": "$.flowRate",
  "$.dischargePressure": "$.dischargePressure"
}

アダプタは、テレメトリメッセージ内のフラットフィールドからネストされたモータオブジェクトを構築します。flowRateとdischargePressureは既にモデルと一致しているため、これらのフィールドは直接マッピングされます。

例3:属性名の変更

一部のデバイスでは、デバイス付近では意味のある簡潔な名前が使用されていますが、アプリケーション開発者にとってはあまり役に立ちません。この例では、mtrmtrTempvibLvlpwrUseflowRt、およびdischPressは、正規のモデル名にマッピングされます。


クリップボードにコピーされました
エラー: コピーできませんでした
クリップボードにコピーされました
エラー: コピーできませんでした
{
  "mtr": {
    "mtrTemp": 68.4,
    "vibLvl": 1.7,
    "pwrUse": 12.6
  },
  "flowRt": 247.5,
  "dischPress": 4.3
}

アダプターマッピング:


クリップボードにコピーされました
エラー: コピーできませんでした
クリップボードにコピーされました
エラー: コピーできませんでした
{
  "$.motor": "${ {motorTemperature: .mtr.mtrTemp, vibrationLevel: .mtr.vibLvl, powerConsumption: .mtr.pwrUse} }",
  "$.flowRate": "$.flowRt",
  "$.dischargePressure": "$.dischPress"
}

アダプタは、受信したテレメトリ値を保持しつつ、モデルで定義された記述的なモデルフィールドをアプリケーションに提供します。

例4:psiからbarへの単位変換

WaterPumpモデルでは、排出圧力をbar単位で定義していますサンプルデバイスは、ソース値をポンド/平方インチ単位で送信し、そのフィールドにdischPressPsiという名前を付けます。


クリップボードにコピーされました
エラー: コピーできませんでした
クリップボードにコピーされました
エラー: コピーできませんでした
{
  "motor": {
    "motorTemperature": 68.4,
    "vibrationLevel": 1.7,
    "powerConsumption": 12.6
  },
  "flowRate": 247.5,
  "dischPressPsi": 62.37
}

アダプターマッピング:


クリップボードにコピーされました
エラー: コピーできませんでした
クリップボードにコピーされました
エラー: コピーできませんでした
{
  "$.motor": "${ {motorTemperature: .motor.motorTemperature, vibrationLevel: .motor.vibrationLevel, powerConsumption: .motor.powerConsumption} }",
  "$.flowRate": "$.flowRate",
  "$.dischargePressure": "${(.dischPressPsi * 0.0689475729)}"
}

排出圧力の式は、入力されたpsi値に0.0689475729を乗じます。サンプル値では、62.37 psiは約4.30 barに相当します。デバイスが別の単位を公開している場合でも、アプリケーションは1つのモデル単位を読み取ることができます。

例5:エンドポイントによる条件付きルーティング

単位変換は、ペイロードがアダプタに到達する方法によっても左右されます。ルーティング条件は、一方のエンドポイントがデフォルトのテレメトリパスとは異なる単位系で値を送信する場合に対応します。

この例では、english-unitsという名前のエンドポイントセグメントに送信されるテレメトリデータは、モーターの温度を華氏で報告します。モデルは摂氏を想定しているため、このルートでは温度を摂氏に変換します。デフォルトのルートでは、温度は変更されません。

受信ルートファイル:


クリップボードにコピーされました
エラー: コピーできませんでした
クリップボードにコピーされました
エラー: コピーできませんでした
[
    {
      "condition": "${endpoint(2) == \"english-units\"}",
      "description": "Convert motor temperature from Fahrenheit to Celsius for English-unit telemetry.",
      "payloadMapping": {
        "$.motor": "${ {motorTemperature: ((.motor.motorTemperature - 32) * 5 / 9), vibrationLevel: .motor.vibrationLevel, powerConsumption: .motor.powerConsumption} }",
        "$.flowRate": "$.flowRate",
        "$.dischargePressure": "$.dischargePressure"
      },
      "referencePayload": {
        "dataFormat": "JSON",
        "data": {
          "motor": {
            "motorTemperature": 68.4,
            "vibrationLevel": 1.7,
            "powerConsumption": 12.6
          },
          "flowRate": 247.5,
          "dischargePressure": 4.3
        }
      }
    },
    {
      "condition": "*",
      "description": "Map water pump telemetry without changing motor temperature.",
      "payloadMapping": {
        "$.motor": "${ {motorTemperature: .motor.motorTemperature, vibrationLevel: .motor.vibrationLevel, powerConsumption: .motor.powerConsumption} }",
        "$.flowRate": "$.flowRate",
        "$.dischargePressure": "$.dischargePressure"
      },
      "referencePayload": {
        "dataFormat": "JSON",
        "data": {
          "motor": {
            "motorTemperature": 20.2,
            "vibrationLevel": 1.7,
            "powerConsumption": 12.6
          },
          "flowRate": 247.5,
          "dischargePressure": 4.3
        }
      }
    }
]

最初のルートは、条件${ endpoint (2) == “ english-units “} を使用して、 english-unitsセグメント経由で到着するテレメトリを識別します。このルートは、デフォルト ルートと同じペイロード構造をマッピングしますが、motor.motorTemperatureを (.motor.motorTemperature – 32) * 5 / 9 を使用して華氏から摂氏に変換します。

2番目のルートはワイルドカード条件「*」を使用します。これは、 english-units条件に一致しないメッセージのデフォルトルートとして機能します。このルートでは、motorTemperatureは受信ペイロードから変換なしでモデルに直接マッピングされます。より具体的なルートがワイルドカードルートの前に配置されるため、デフォルトルートが一致する前に変換が適用されます。

例6:JQの追加機能

前述の例では、直接マッピング、単位変換、条件付きルーティングにJQを使用しました。JQは条件ロジック、デフォルト値、オブジェクト構築、値変換もサポートしており、アダプタがデバイスのファームウェアを変更することなくペイロードの変動に対応できるようになっています。

JQ のその他の機能については、 JQ 式の使用に関する OCI Internet of Things Platform のドキュメントに記載されています

以下の例はWaterPumpモデルの一部ではありませんが、モデルがフォールバック値や意味マッピングを必要とする場合に適用される、追加のJQパターンを示しています。

欠損値のデフォルト値:


クリップボードにコピーされました
エラー: コピーできませんでした
クリップボードにコピーされました
エラー: コピーできませんでした
{
  "$.pressure": "${ if has(\"pressure\") then .pressure else 0 end }"
}

このマッピングでは、入力圧力値が存在するかどうかを確認します。デバイスが圧力値を送信する場合、アダプタはその値をモデルにマッピングします。デバイスが値を送信しない場合、この例のアダプタは0を書き込みます。このパターンは、モデルが値を必要とするものの、一部のデバイスがそのフィールドを省略する場合に役立ちます。このパターンは、デフォルト値が適切な場合にのみ使用してください。

意味値のマッピング:


クリップボードにコピーされました
エラー: コピーできませんでした
クリップボードにコピーされました
エラー: コピーできませんでした
{
  "$.operatingState": "${ if .state == 1 then \"running\" elif .state == 0 then \"stopped\" else \"unknown\" end }"
}

このマッピングは、数値デバイス値をアプリケーションが読み取りやすい文字列に変換します。デバイスは停止時に0、実行時に1を送信します。アダプタはモデルに「停止中」または「実行中」を書き込みます。else ブランチは、デバイスが想定範囲外の値を送信した場合に「不明」を書き込みます。

まとめ

デジタルツインアダプタは、デバイスのテレメトリデータを、デジタルツインモデルで定義された安定した形式に変換します。直接マッピングは、ペイロードが既にモデルと一致している場合に有効です。カスタムアダプタは、明示的なタイムスタンプ、フラットなペイロード、省略されたフィールド名、ネストされたコンポーネント、単位変換、エンドポイント固有のルーティングなどの違いを処理します。

手順は一貫しています。まずモデルを作成し、受信したペイロードを検査し、参照ペイロードを追加し、ソースフィールドをモデルパスにマッピングし、値に構造化や変換が必要な場合はJQを使用します。

OCI Internet of Things を構成することで、異種デバイスからのテレメトリを正規化されたビューとして取得できます。まず、シンプルなデジタルツインモデルと、実際のデバイスからの代表的なテレメトリペイロードを 1 つ用意します。次に、そのペイロードをモデルにマッピングするアダプタを作成し、そのモデルをアダプタに関連付けるデジタルツインインスタンスを作成します。デジタルツインインスタンスの構成が完了したら、テストテレメトリを送信し、保存されたツイン値を検査して、タイムスタンプ、単位、属性名、ネストされたコンポーネントが期待どおりに解決されることを確認します。

最初のパスが正常に動作したら、ペイロードのバリエーションをさらに追加します。カスタムアダプタを使用して、フラットテレメトリ、フィールド名の省略、単位変換、ルート固有の動作、およびアプリケーションが異なるデバイス間で一貫したデジタルツイン構造を読み取れるようにするその他の変換を行います。

リソース

コメント

このブログの人気の投稿

Oracle Database 19cサポート・タイムラインの重要な更新 (2024/11/20)

ミリ秒の問題: BCCグループとOCIが市場データ・パフォーマンスを再定義する方法(AWSに対するベンチマークを使用) (2025/11/13)

OCI Object Storageアクセス制御ポリシーの構成 (2024/04/25)