Referencia: JQ Expressions for Digital Twin Adapters

Prácticos patrones de expresión JQ para entradas de dispositivos, sobres de adaptadores, condiciones de ruta y asignaciones de carga útil para normalizar la telemetría en lo que se define en los modelos gemelos digitales.

Conceptos clave

Puede escribir expresiones JQ dentro de un adaptador de gemelos digitales como marcadores de posición ${ ... } para calcular los valores de destino en payload-mapping y para evaluar las condiciones de ruta.

Formas comunes de utilizar expresiones JQ:

  • Entrada de dispositivo: cargas útiles raw publicadas por los dispositivos.
  • Sobre: declara puntos finales de referencia y unidades de carga útil de ejemplo, por ejemplo, puede definir la asignación timeObserved.
  • Rutas: evalúe las condiciones, incluidos los puntos finales, las cabeceras, el cuerpo y seleccione una asignación de carga útil.
  • Asignaciones de carga útil: transforma, convierte unidades, cambia el nombre de las claves y se normaliza en el esquema del modelo DTDL.
  • Salida: salida JSON normalizada que debe cumplir la validación de modelos gemelos digitales, incluidos los tipos, rangos y unidades.
  • Compatibilidad con funciones de asignación: se aceptan las funciones aritméticas y floor. Funciones como toInteger y number no están soportadas.
  • Confrontación de punto final: utiliza condiciones basadas en segmentos con endpoint(n), por ejemplo: ${endpoint(1) == 'home' and endpoint(2) == 'sonnen' and endpoint(3) == 'status'} en lugar de patrones comodín no soportados.
  • Esquema entero frente a doble:
    • Para schema: "integer", asegúrese de que la asignación emite valores numéricos integrales (por ejemplo, "${(.velocity_kph / 1.609) | floor}").
    • Para schema: "double", se aceptan salidas fraccionadas; utilice floor solo si desea almacenamiento de números enteros como doble, por ejemplo, 68.0.
  • Tipo de conversión flexible: se aceptan cadenas numéricas y dobles de números enteros cuando coinciden con el tipo de modelo (por ejemplo, entero). Los ayudantes de fundición como number() y toInteger siguen sin estar soportados en las expresiones de ruta; confíen en la aritmética y floor, o adopten schema: "double" para conservar fracciones.
    • Transferencia (mph, esquema "integer"): {"speed": "60"} y {"speed": 60.0} se almacenan como 60. {"speed": "60.2"} se rechaza a menos que la asignación se asocie a un entero, por ejemplo, con floor.
    • Ruta métrica (kph → mph): {"velocity_kph": 110}68; {"velocity_kph": "110"}68 porque la asignación floor emite un número entero. Mantenga las entradas aritméticas numéricas para evitar errores de expresión; prefiera 110 en lugar de "110" cuando sea posible.
    • La redondeo sigue siendo explícita: la conversión flexible no redondea automáticamente 68.35 a 68. Utilice floor para el esquema entero o cambie el modelo a schema: "double" para conservar las fracciones.
  • Soporte y gestión de tiempo: si no se proporciona timeObserved, la plataforma puede utilizar receivedTime. Utilice fromdateformat, todateformat y funciones relacionadas para las conversiones de tiempo en asignaciones de sobre o carga útil.
Nota

La validación de datos se realiza en el modelo de DTDL. Si una propiedad se declara como integer, la salida normalizada debe ser un valor integer, no string ni float. Consulte el patrón de corrección de enteros a continuación.

Para obtener más información, consulte DTMI Validation Extension Reference.

Ejemplos de entrada de dispositivo

Cargas útiles entrantes típicas con esquemas específicos de la unidad:

Unidades estándar de EE.UU. millas por hora:

{
  "speed": 60
}

Unidades métricas europeas kilómetros por hora:

{
  "velocity_kph": 110
}

Carga útil anidada de un sensor:

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

Matriz de muestras:

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

Estos ejemplos muestran un modelo DTDL donde la temperatura es un entero dentro del rango de 0 a 100 y la humedad es opcional.

Cuando ambos valores están presentes y la temperatura está dentro del rango permitido y del tipo correcto, es válido y se ingiere el valor de entrada completo:
{ "temperature": 60, "humidity": 45 }
Cuando un valor está ausente, solo se ingieren los datos válidos. En este ejemplo falta la temperatura para que se ingiera la humedad y no se actualice el valor de temperatura.
{
"humidity": 45
}
Cuando un valor es null, se ignora y solo se ingiere el valor válido:
{
		“temperature”: null
 		“humidity”: 45
	}

Asignación de sobres

envelope.json declara referenceEndpoint y referencePayload. Opcionalmente, una asignación de sobre puede definir time-observed:

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

La plataforma puede proporcionar el identificador especial receivedTime cuando el dispositivo omite time. Si se especifica la asignación de sobre y contiene un valor timeObserved, receivedTime se utiliza como valor timeObserved.

Patrones de condición de ruta

Las condiciones de ruta son reglas o expresiones que se utilizan para determinar qué regla de asignación o procesamiento se debe aplicar a un mensaje o solicitud entrante. Si una condición de ruta se evalúa como verdadera, se dispara y aplica la regla de transformación o asignación asociada.

En este ejemplo, la condición de ruta del adaptador gemelo digital define dos rutas para procesar mensajes de dispositivos entrantes, según el formato de los datos, por ejemplo, si se reciben unidades de métrica en el 3er segmento de la ruta de punto final, /vehicle/speed/metric-units/

[
  {
    "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) selecciona el segmento de ruta de acceso n-th (basado en 0 o 1 según el adaptador). En el escenario de normalización de unidades de medida, se utiliza endpoint(3) para que /vehicle/speed/metric-units/ coincida con el tercer segmento metric-units.
  • Coloque condiciones más específicas antes de la captura de todo "*".
  • payloadMapping:
    • Toma el campo velocity_kph velocidad en kilómetros por hora de la carga útil, y lo convierte a millas por hora: .velocity_kph / 1.609
    • Aplica floor para redondear a la baja al entero más cercano que floor no se emite, por lo que el resultado puede ser un flotante.
    • El resultado se asigna al campo de salida speed.
  • referencePayload:
    • Demuestra la entrada esperada para esta ruta: {"velocity_kph": 104}
  • Punto final de entrada: /vehicle/speed/metric-units/device123
  • Carga útil de entrada: { "velocity_kph": 104 } Asignación de salida: { "speed": 64 } (since 104 / 1.609 = 64.64, floor is 64)

Ejemplos de asignación de carga útil

Expresiones JQ comunes para asignaciones de carga útil:

  • Transferencia: "$.speed": "$.speed"
  • Clave de nombre: "$.speed": "${.velocity_kph}"
  • Unit Conversion: "$.mph": "${.kph / 1.609}"
  • Piso / techo / ronda: "${.x | floor}", "${.x | ceil}", "${.x | round}"
  • Coalesce/default (dependiente de la versión): "${ if .value? then .value else 0 end }"
  • Conversión del tipo:
    • Para numerar: "${.value | tonumber}" (soporta tonumber en la mayoría de las compilaciones)
    • Cadena de destino: "${.value | tostring}"
    Nota

    En un adaptador gemelo digital IoT, las funciones de conversión como toInteger o number no se aceptan en inbound-routes. Puede utilizar la aritmética con floor definido para un esquema de entero o utilizar schema: "double" y formatos de redondeo para la ingestión de datos descendentes. inbound-routes debe ser un JSON válido; las expresiones pertenecen a cadenas entre comillas "${ ... }".
  • Extracción anidada: "${.telemetry.temp_c}"
  • Array map: "${[ .samples[] | .kph / 1.609 | floor ]}"
  • Conditional: "${ if .kph > 0 then .kph / 1.609 else 0 end }"
Nota

Según la integración del adaptador, las expresiones entre comillas "${...}" se pueden serializar como cadenas. Cuando el motor soporta expresiones sin comillas, por ejemplo, ${...} como valor raw, se recomienda que el formulario emita un número JSON en lugar de una cadena.

Nuances: tipos de enteros, cadenas y números calculados

Cuando una propiedad DTDL es schema: "integer", la salida normalizada debe ser un tipo de entero. Dos modos de fallo comunes al calcular valores:

  1. Stringification: una expresión encapsulada entre comillas puede generar "68" (cadena), fallando la validación de enteros.
  2. Números flotantes: la aritmética produce 68.0; algunos validadores tratan esto como no entero, incluso si es matemáticamente integral.

Corregir patrones:

  • Utilice sólo piso para el esquema de enteros: "${(.velocity_kph / 1.609) | floor}" para producir un número integral que satisfaga la escritura de enteros.
  • Alternativo: cambie la propiedad del modelo a schema: "double" para conservar la precisión fraccional o aplique floor en la asignación mientras almacena un número entero como doble. Redondeo/formato en APEX/SQL según sea necesario.

Ejemplos de asignación de adaptadores gemelos digitales

En este ejemplo, se normalizan los kilómetros por hora (KPH) a millas por hora (MPH) mediante floor (sin conversión).

{
  "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 }
  }
}

Ejemplo: transferencia de tipo capturar todo predeterminada

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

Ejemplo: telemetría anidada y fusión por defecto

{
  "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 }"
  }
}

Ejemplo: normalización de matrices

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

Expectativas de salida frente a validación de modelo

Las salidas deben cumplir los esquemas y las restricciones del modelo gemelo digital. Para el modelo de unidad automotriz en model.json, consulte Escenario: Normalización de unidades de medida mediante un adaptador gemelo digital:

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

speed normalizado debe ser un entero dentro de 0,100. Una cadena calculada "68" o un número flotante 68.0 fallarán en la validación.

Limitaciones y consejos

  • La disponibilidad de los filtros varía: la mayoría de los filtros jq del núcleo (floor, ceil, round, tonumber, tostring, map, select, add)
  • Escribir emisión: las expresiones embebidas en cadenas entre comillas se pueden serializar como cadenas. Preferir expresiones sin comillas raw si están soportadas para emitir tipos numéricos.
  • Gestión nula: las operaciones en null pueden producir null. Utilice if .x? then ... else ... end para valores por defecto defensivos.
  • Precisión: la aritmética de punto flotante puede introducir artefactos de redondeo; aplique round y floor según sea necesario antes de la conversión.
  • Integer cast: en las pruebas observadas, toInteger y number no se aceptaron en inbound-routes en el momento de la creación del adaptador. Prefiera la aritmética + floor para el esquema de enteros o utilice schema: "double" y redondee el formato hacia abajo.

Aplicación de la corrección de enteros al escenario de automoción

El escenario crea un modelo con un entero speed y enruta las cargas útiles de la unidad de métrica al mismo modelo de gemelo digital. Sin una conversión explícita, el valor calculado se puede rechazar mediante la validación debido a un cambio de tipo (cadena o número similar a flotante).

Corrección utilizada en el escenario:

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

Por qué es necesario:

  • floor garantiza que el valor esté numerado en su totalidad y que sea satisfactorio escribir enteros.
  • Alternativa: utilice schema: "double" para preservar la precisión fraccional y redondear y formatear hacia abajo si es necesario.
Nota

Al utilizar un esquema entero, prefiera la aritmética más floor. Para el esquema doble, puede omitir floor para mantener MPH fraccional o incluirlo para almacenar un número entero de MPH como doble.

Fragmentos de referencia rápida

Ejemplos soportados:

  • Condición: coincidencia de punto final: "condition": "${endpoint(3) == \"metric-units\"}"
  • Asignación: transferencia: "$.speed": "$.speed"
  • Asignación: kph → mph (esquema entero): "$.speed": "${(.kph / 1.609) | floor}"
  • Asignación: anidada: "$.room_temp_c": "${.telemetry.temp_c}"
  • Mapping: default (Asignación): "$.value": "${ if .value? then .value else 0 end }"
  • Array transform: "$.list": "${ [ .arr[] | .x | tonumber ] }"

Comportamiento de modelo entero frente a doble

En esta sección se resume cómo afecta el tipo de esquema de modelo a la asignación y validación del adaptador.

  • esquema: "integer"
    • Tipo: debe ser un número JSON entero que sea integral. Las cadenas como 68 o los resultados similares a flotación 68.0 pueden fallar en la validación de enteros.
    • Asignación: se permite la aritmética; se admite floor. Funciones como toInteger y number no están soportadas.
    • Patrón: utilice "${(.velocity_kph / 1.609) | floor}" para forzar a la MPH integral. Esto pasó la validación y la telemetría se aceptó HTTP 202.
    • Rango: se aplican los valores mínimo y máximo, por ejemplo, 0–100
  • schema: "double"
    • Tipo: se acepta cualquier integral o fraccional de número JSON si se encuentra dentro del rango; la plataforma no realiza un redondeo automático.
    • Asignación: puede conservar la precisión fraccional, por ejemplo, "${.velocity_kph / 1.609}", o también aplicar floor para producir un número entero de MPH almacenado como doble, por ejemplo: 68.0
    • Rango: si se define, mínimo y máximo aplicados, por ejemplo: 0–100

Resultados de telemetría observados

Se validaron las siguientes combinaciones de extremo a extremo (HTTP/1.1 202 Accepted):

  • Asignación de modelo entero + piso: "${(.velocity_kph / 1.609) | floor}" → aceptado
  • Modelo doble + asignación raw: "${.velocity_kph / 1.609}" → mph fraccional aceptado
  • Asignación de modelo doble + piso: "${(.velocity_kph / 1.609) | floor}" → mph de número entero aceptado y almacenado como doble

Curl de ejemplo (marcadores de posición)

Modelo doble, valor bruto que es mph fraccional como se esperaba:

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 }'

Modelo doble, piso de un número entero MPH, que se almacena como doble:

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 }'

Respuesta HTTP esperada para cada caso:

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

Accepted

Aspectos descendentes de APEX o SQL

  • Con doble, se conserva la velocidad fraccionada. Utilice SQL FLOOR/ROUND para mostrar números enteros:
    SELECT FLOOR(speed) AS speed_mph FROM ...
  • Con integer, asegúrese de asignar valores integrales de salida (por ejemplo, a través de floor) para satisfacer la validación.

Nombre de usuario y ofertas económicas de autorización

En OCI IoT, el uso del nombre de usuario de autenticación básica es igual a la instancia external-key. Si la instancia se crea con comillas embebidas en la clave externa, esas comillas pasan a formar parte del nombre de usuario necesario y se deben enviar literalmente. Esto a menudo causa problemas de comillas de shell.

Mejores prácticas: cree instancias de gemelos digitales con claves externas que no incluyan comillas, por ejemplo, american-auto. Cuando deba autenticarse con nombres de usuario entre comillas, cree una cabecera Authorization: Basic ... en lugar de utilizar -u para evitar errores de comillas.