Référence : JQ Expressions for Digital Twin Adapters

Modèles d'expression JQ pratiques pour les entrées d'appareil, les enveloppes d'adaptateur, les conditions de routage et les mappages de charge utile afin de normaliser la télémétrie dans ce qui est défini dans les modèles de jumeaux numériques.

Concepts-clés

Vous pouvez écrire des expressions JQ dans un adaptateur jumeau numérique en tant qu'espaces réservés ${ ... } pour calculer les valeurs cible dans payload-mapping et évaluer les conditions de routage.

Méthodes courantes d'utilisation des expressions JQ :

  • Entrée de périphérique : charges utiles brutes publiées par les périphériques.
  • Enveloppe : déclare des adresses de référence et des exemples de formes de charge utile, par exemple, vous pouvez définir un mapping timeObserved.
  • Routages : évaluez les conditions, notamment les adresses, les en-têtes et le corps, et sélectionnez un mapping de charge utile.
  • Mappages de charge utile : transformez, convertissez des unités, renommez des clés et normalisez-les dans le schéma de modèle DTDL.
  • Sortie : sortie JSON normalisée qui doit satisfaire la validation du modèle jumeau numérique, y compris les types, les plages et les unités.
  • Prise en charge des fonctions de mise en correspondance : les fonctions arithmétiques et floor sont acceptées. Les fonctions telles que toInteger et number ne sont pas prises en charge.
  • Correspondance d'adresse : utilise des conditions basées sur les segments avec endpoint(n), par exemple : ${endpoint(1) == 'home' and endpoint(2) == 'sonnen' and endpoint(3) == 'status'} au lieu de modèles génériques non pris en charge.
  • Schéma entier ou double :
    • Pour schema: "integer", assurez-vous que le mapping émet des chiffres entiers (par exemple, "${(.velocity_kph / 1.609) | floor}").
    • Pour schema: "double", les sorties fractionnées sont acceptées. Utilisez floor uniquement si vous voulez que le stockage en nombre entier soit double, par exemple 68.0.
  • Type de conversion standard : les chaînes de type numérique et les doubles de nombres entiers sont acceptées lorsqu'elles correspondent au type de modèle (par exemple, entier). Les aides de conversion de type telles que number() et toInteger ne sont pas prises en charge dans les expressions de routage. Utilisez l'arithmétique et floor ou adoptez schema: "double" pour préserver les fractions.
    • Pass-through (mph, schéma "integer") : {"speed": "60"} et {"speed": 60.0} sont stockés en tant que 60. {"speed": "60.2"} est rejeté sauf si la mise en correspondance est contrainte à un entier, par exemple, avec floor.
    • Route de mesure (kph → mph) : {"velocity_kph": 110}68 ; {"velocity_kph": "110"}68 car le mapping floor émet un entier. Conservez des entrées arithmétiques numériques pour éviter les erreurs d'expression. Dans la mesure du possible, préférez 110 à "110".
    • L'arrondi reste explicite : la conversion temporaire n'arrondi pas automatiquement 68.35 à 68. Utilisez floor pour le schéma d'entiers ou basculez le modèle sur schema: "double" pour conserver les fractions.
  • Gestion de l'enveloppe et du temps : si timeObserved n'est pas fourni, la plate-forme peut utiliser receivedTime. Utilisez fromdateformat, todateformat et les fonctions associées pour les conversions de temps dans les mappings d'enveloppe ou de charge utile.
Remarque

La validation des données se produit par rapport à votre modèle DTDL. Si une propriété est déclarée comme integer, la sortie normalisée doit être une valeur integer, et non une valeur string ou float. Voir le modèle de correction d'entier ci-dessous.

Pour plus d'informations, voir Référence d'extension de validation DTMI

Exemples d'entrée de périphérique

Charge utile entrante standard avec des schémas spécifiques à une unité :

Unités américaines standard miles par heure :

{
  "speed": 60
}

Unités métriques européennes kilomètres par heure :

{
  "velocity_kph": 110
}

Charge utile imbriquée d'un capteur :

{
  "telemetry": { "temp_c": 22.4, "humidity": 48 }
}

Tableau d'échantillons :

{
  "samples": [ { "kph": 30 }, { "kph": 50 }, { "kph": 0 } ]
}

Ces exemples montrent un modèle DTDL où la température est un nombre entier compris entre 0 et 100 et où l'humidité est facultative.

Lorsque les deux valeurs sont présentes et que la température est comprise dans la plage autorisée et dans le type correct, elle est valide et la valeur d'entrée complète est ingérée :
{ "temperature": 60, "humidity": 45 }
Lorsqu'une valeur est absente, seules les données valides sont incluses. Dans cet exemple, la température est manquante, de sorte que l'humidité est ingérée et que la valeur de la température n'est pas mise à jour.
{
"humidity": 45
}
Lorsqu'une valeur est null, elle est ignorée et seule la valeur valide est ingérée :
{
		“temperature”: null
 		“humidity”: 45
	}

Mappage d'enveloppe

La valeur envelope.json déclare une valeur referenceEndpoint et une valeur referencePayload. Un mapping d'enveloppe peut éventuellement définir time-observed :

{
  "referenceEndpoint": "telemetry/automotive/standard",
  "referencePayload": {
    "dataFormat": "JSON",
    "data": { "speed": 65 }
  }
}
Remarque

L'identificateur spécial receivedTime peut être fourni par la plate-forme lorsque le périphérique omet time. Si le mapping d'enveloppe est spécifié et contient une valeur timeObserved, receivedTime est utilisé comme valeur timeObserved.

Modèles de condition de routage

Les conditions de routage sont des règles ou des expressions utilisées pour déterminer quelle règle de mapping ou de traitement doit être appliquée à un message ou une demande entrant. Si une condition de routage renvoie la valeur True, la règle de mapping ou de transformation associée est déclenchée et appliquée.

Dans cet exemple, la condition de routage digital twin adapter définit deux routes pour le traitement des messages de périphérique entrants, en fonction du format des données, par exemple si des unités de mesure sont reçues sur le 3e segment du chemin d'extrémité, /vehicle/speed/metric-units/, puis

[
  {
    "description": "European metric to mph; convert then floor (no explicit cast).",
    "condition": "${endpoint(3) == \"metric-units\"}",
    "payloadMapping": {
      "$.speed": "${(.velocity_kph / 1.609) | floor}"
    },
    "referencePayload": {
      "dataFormat": "JSON",
      "data": { "velocity_kph": 104 }
    }
  },
  {
    "description": "USA standard units passthrough.",
    "condition": "*",
    "payloadMapping": { "$.speed": "$.speed" }
  }
]
  • endpoint(n) sélectionne le segment de chemin n-th (basé sur 0 ou 1 en fonction de l'adaptateur). Dans le scénario de normalisation des unités de mesure, endpoint(3) est utilisé de sorte que /vehicle/speed/metric-units/ corresponde au troisième segment metric-units.
  • Placez des conditions plus spécifiques avant le processus catch-all "*".
  • payloadMapping:
    • Prend la vitesse du champ velocity_kph en kilomètres par heure à partir de la charge utile et la convertit en miles par heure : .velocity_kph / 1.609
    • Applique floor pour arrondir à l'entier inférieur le plus proche ; floor n'est pas converti. Le résultat peut donc être une virgule flottante.
    • Le résultat est affecté au champ de sortie speed.
  • referencePayload:
    • Présente l'entrée attendue pour ce routage : {"velocity_kph": 104}
  • Adresse d'entrée : /vehicle/speed/metric-units/device123
  • Charge utile d'entrée : { "velocity_kph": 104 } Mapping de sortie : { "speed": 64 } (since 104 / 1.609 = 64.64, floor is 64)

Exemples de mappage de charge utile

Expressions JQ courantes pour les mappings de charge utile :

  • Transmission : "$.speed": "$.speed"
  • Renommer la clé : "$.speed": "${.velocity_kph}"
  • Conversion d'unité : "$.mph": "${.kph / 1.609}"
  • Plancher/plafond/rond : "${.x | floor}", "${.x | ceil}", "${.x | round}"
  • Coalesce/default (dépendant de la version) : "${ if .value? then .value else 0 end }"
  • Conversion de type:
    • Pour numéroter : "${.value | tonumber}" (prend en charge tonumber dans la plupart des builds)
    • Chaîne de destination : "${.value | tostring}"
    Remarque

    Dans un adaptateur jumeau numérique IoT, les fonctions de conversion de type toInteger ou number ne sont pas acceptées dans inbound-routes. Vous pouvez utiliser l'arithmétique avec floor défini pour un schéma d'entier ou utiliser schema: "double" et les formats d'arrondi pour l'inclusion de données en aval. inbound-routes doit être un format JSON valide ; les expressions appartiennent à des chaînes entre guillemets "${ ... }".
  • Extraction imbriquée : "${.telemetry.temp_c}"
  • Carte de tableau : "${[ .samples[] | .kph / 1.609 | floor ]}"
  • Conditionnel : "${ if .kph > 0 then .kph / 1.609 else 0 end }"
Remarque

Selon l'intégration de l'adaptateur, les expressions entre guillemets "${...}" peuvent être sérialisées en tant que chaînes. Lorsque le moteur prend en charge les expressions sans guillemets, par exemple ${...} en tant que valeur brute, il est recommandé que le formulaire émette un nombre JSON plutôt qu'une chaîne.

Nuances : types entiers, chaînes et nombres calculés

Lorsqu'une propriété DTDL est schema: "integer", la sortie normalisée doit être de type entier. Deux modes de défaillance courants lors du calcul des valeurs :

  1. Stringification : une expression encapsulée entre guillemets peut produire "68" (chaîne), échec de la validation d'entier.
  2. Nombre flottant : l'arithmétique produit 68.0 ; certains validateurs le considèrent comme non entier, même s'il est mathématiquement entier.

Corriger les modèles :

  • Utiliser le plancher uniquement pour le schéma d'entiers : "${(.velocity_kph / 1.609) | floor}" pour produire une valeur numérique intégrale qui satisfait à la saisie d'entiers.
  • Autre : basculez la propriété de modèle sur schema: "double" pour préserver la précision des fractions, ou appliquez floor dans le mapping tout en stockant un nombre entier sous forme de double. Arrondir/format dans APEX/SQL si nécessaire.

Exemples de mapping d'adaptateur de jumeau numérique

Cet exemple normalise les kilomètres par heure (KPH) en miles par heure (MPH) à l'aide de floor (pas de conversion).

{
  "description": "European auto uses metric units; convert to mph and floor to whole number.",
  "condition": "${endpoint(3) == \"metric-units\"}",
  "payloadMapping": {
    "$.speed": "${(.velocity_kph / 1.609) | floor}"
  },
  "referencePayload": {
    "dataFormat": "JSON",
    "data": { "velocity_kph": 104 }
  }
}

Exemple : Passthrough catch-all par défaut

{
  "description": "English units passthrough.",
  "condition": "*",
  "payloadMapping": {
    "$.speed": "$.speed"
  }
}

Exemple : télémétrie imbriquée et fusion par défaut

{
  "description": "Extract nested temp; default to 0 when missing.",
  "condition": "${endpoint(2) == \"env\"}",
  "payload-mapping": {
    "$.room_temp_c": "${ if .telemetry.temp_c? then .telemetry.temp_c else 0 end }"
  }
}

Exemple : normalisation de tableau

{
  "description": "Normalize kph samples to mph (whole-number mph via floor).",
  "condition": "${.samples?}",
  "payload-mapping": {
    "$.speeds": "${ [ .samples[] | .kph / 1.609 | floor ] }"
  }
}

Attentes de sortie par rapport à la validation du modèle

Les sorties doivent satisfaire les schémas et les contraintes du modèle jumeau numérique. Pour le modèle d'unité automobile dans model.json, reportez-vous à Scénario : normalisation des unités de mesure à l'aide d'un adaptateur jumeau numérique :

  • name: speed
  • schema: integer
  • unit: milePerHour
  • minimum: 0, maximum: 100

La valeur speed normalisée doit être un entier compris dans 0,100. La validation d'une chaîne calculée "68" ou d'un nombre flottant 68.0 échouera.

Limites et conseils

  • La disponibilité des filtres varie : la plupart des filtres jq principaux (floor, ceil, round, tonumber, tostring, map, select, add)
  • Emission de type : les expressions imbriquées dans des chaînes entre guillemets peuvent être sérialisées en tant que chaînes. Préférez les expressions brutes sans guillemets si elles sont prises en charge pour émettre des types numériques.
  • Gestion nulle : les opérations effectuées sur null peuvent produire null. Utilisez if .x? then ... else ... end pour les valeurs défensives par défaut.
  • Précision : l'arithmétique à virgule flottante peut introduire des artefacts d'arrondi ; appliquez round et floor selon vos besoins avant la conversion.
  • Entier converti : dans les tests observés, toInteger et number n'ont pas été acceptés dans inbound-routes au moment de la création de l'adaptateur. Préférez arithmétique + floor pour le schéma d'entiers ou utilisez schema: "double" et arrondissez le format en aval.

Application de la correction d'entier au scénario automobile

Le scénario crée un modèle avec un entier speed et achemine les charges utiles d'unité de mesure vers le même modèle de jumeau numérique. Sans conversion explicite, la valeur calculée peut être rejetée par la validation en raison d'une dérive de type (chaîne ou nombre de type float).

Correctif utilisé dans le scénario :

"$.speed": "${(.velocity_kph / 1.609) | floor}"

Pourquoi est-ce nécessaire ?

  • floor garantit que la valeur est entièrement numérotée, en respectant la saisie d'entiers.
  • Alternative : utilisez schema: "double" pour préserver la précision fractionnée et arrondir et formater en aval si nécessaire.
Remarque

Lors de l'utilisation d'un schéma d'entiers, préférez l'arithmétique plus floor. Pour le schéma double, vous pouvez omettre floor pour conserver la MPH fractionnaire ou l'inclure pour stocker un nombre entier MPH en tant que double.

Extraits de référence rapide

Exemples pris en charge :

  • Condition : correspondance d'adresse : "condition": "${endpoint(3) == \"metric-units\"}"
  • Mise en correspondance : transmission : "$.speed": "$.speed"
  • Mappage : kph → mph (schéma entier) : "$.speed": "${(.kph / 1.609) | floor}"
  • Mise en correspondance : imbriquée : "$.room_temp_c": "${.telemetry.temp_c}"
  • Mappage : valeur par défaut : "$.value": "${ if .value? then .value else 0 end }"
  • Transformation de tableau : "$.list": "${ [ .arr[] | .x | tonumber ] }"

Comportement d'entier ou de modèle double

Cette section récapitule l'impact du type de schéma de modèle sur la mise en correspondance et la validation des adaptateurs.

  • schéma : "entier"
    • Type : doit être un nombre entier JSON entier. Les chaînes telles que 68 ou les résultats de type float 68.0 peuvent échouer à la validation des entiers.
    • Mise en correspondance : l'arithmétique est autorisée ; floor est pris en charge. Les fonctions telles que toInteger et number ne sont pas prises en charge.
    • Modèle : utilisez "${(.velocity_kph / 1.609) | floor}" pour forcer la MPH intégrale. La validation a réussi et la télémétrie a été acceptée HTTP 202.
    • Plage : les valeurs minimum et maximum sont appliquées, par exemple, 0–100
  • schema: "double"
    • Type : tout entier ou fractionnaire de nombre JSON est accepté s'il est compris dans la plage ; aucun arrondi automatique n'est effectué par la plate-forme.
    • Correspondance : vous pouvez conserver une précision fractionnée, par exemple "${.velocity_kph / 1.609}", ou appliquer également floor pour produire un nombre entier de MPH stocké en double, par exemple : 68.0
    • Plage : si défini, minimum et maximum appliqué, par exemple : 0–100

Résultats de télémétrie observés

Les combinaisons suivantes ont été validées de bout en bout (HTTP/1.1 202 Accepted) :

  • Modèle d'entier + mappage du plancher : "${(.velocity_kph / 1.609) | floor}" → accepté
  • Modèle double + mappage brut : "${.velocity_kph / 1.609}" → mph fractionnaire accepté
  • Mappage du double modèle + du plancher : "${(.velocity_kph / 1.609) | floor}" → nombre entier mph accepté et stocké en double

Exemple de boucle (espaces réservés)

Modèle double, valeur brute correspondant à la valeur mph fractionnaire attendue :

curl -i -X POST \
  -u "european-auto-raw:secret-or-certificate-ocid" \
  -H "Content-Type: application/json" \
  "https://device-host/telemetry/automotive/metric-units" \
  -d '{ "velocity_kph": 110 }'

Double modèle, plancher un nombre entier MPH, qui est stocké comme double :

curl -i -X POST \
  -u "european-auto-dfloor:secret-or-certificate-ocid" \
  -H "Content-Type: application/json" \
  "https://device-host/telemetry/automotive/metric-units" \
  -d '{ "velocity_kph": 110 }'

Réponse HTTP attendue pour chaque cas :

HTTP/1.1 202 Accepted
content-type: text/plain

Accepted

Considérations en aval dans APEX ou SQL

  • Avec double, la vitesse fractionnaire est préservée. Utilisez SQL FLOOR/ROUND pour l'affichage des nombres entiers :
    SELECT FLOOR(speed) AS speed_mph FROM ...
  • Avec integer, assurez-vous que la mise en correspondance génère des valeurs entières (par exemple, via floor) pour satisfaire la validation.

Nom utilisateur et devis d'autorisation

Dans OCI IoT, l'utilisation du nom utilisateur d'authentification de base est égale à l'instance external-key. Si l'instance est créée avec des guillemets intégrés dans la clé externe, ces guillemets font partie du nom utilisateur requis et doivent être envoyés littéralement. Cela provoque souvent des problèmes de citation de shell.

Meilleure pratique : créez des instances de jumeau numérique avec des clés externes qui n'incluent pas de guillemets, par exemple, american-auto. Lorsque vous devez vous authentifier avec des noms utilisateur entre guillemets, créez un en-tête Authorization: Basic ... plutôt que d'utiliser -u pour éviter les erreurs de citation.