Compartir por


Asignación de datos mediante flujos de datos

Importante

Versión preliminar de operaciones de Azure IoT: habilitada por Azure Arc está actualmente en versión preliminar. No se debería usar este software en versión preliminar en entornos de producción.

Deberá implementar una nueva instalación de Azure IoT Operations cuando esté disponible una versión general. No podrá actualizar una instalación de versión preliminar.

Consulte Términos de uso complementarios para las versiones preliminares de Microsoft Azure para conocer los términos legales que se aplican a las características de Azure que se encuentran en la versión beta, en versión preliminar o que todavía no se han publicado para que estén disponibles con carácter general.

Use el lenguaje de asignación de flujos de datos para transformar datos en Operaciones de IoT de Azure. La sintaxis es una manera sencilla, pero eficaz, de definir asignaciones que transforman datos de un formato a otro. En este artículo se proporciona información general sobre el lenguaje de asignación de flujos de datos y los conceptos clave.

La asignación le permiten transformar datos de un formato a otro. Tenga en cuenta el registro de entrada siguiente:

{
  "Name": "Grace Owens",
  "Place of birth": "London, TX",
  "Birth Date": "19840202",
  "Start Date": "20180812",
  "Position": "Analyst",
  "Office": "Kent, WA"
}

Compárelo con el registro de salida:

{
  "Employee": {
    "Name": "Grace Owens",
    "Date of Birth": "19840202"
  },
  "Employment": {
    "Start Date": "20180812",
    "Position": "Analyst, Kent, WA",
    "Base Salary": 78000
  }
}

En el registro de salida, se realizan los cambios siguientes en los datos del registro de entrada:

  • Campos a los que se ha cambiado de nombre: el campo Birth Date ahora es Date of Birth.
  • Campos reestructurados: tanto Name como Date of Birth se agrupan en la nueva categoría de Employee.
  • Campo eliminado: el campo Place of birth se quita porque no está presente en la salida.
  • Campo agregado: el campo Base Salary es un campo nuevo en la categoría Employment.
  • Valores de campo modificados o combinados: el campo Position de la salida combina los campos Position y Office de la entrada.

Las transformaciones se logran mediante la asignación, que normalmente implica lo siguiente:

  • Definición de entrada: identificación de los campos de los registros de entrada que se usan.
  • Definición de salida: especificación de dónde y cómo se organizan los campos de entrada en los registros de salida.
  • Conversión (opcional): modificación de los campos de entrada que caben en los campos de salida. expression es necesario cuando se combinan varios campos de entrada en un único campo de salida.

A continuación se muestra una asignación de ejemplo:

- inputs:
  - BirthDate
  output: Employee.DateOfBirth

- inputs:
  - Position # - - - $1
  - Office # - - - - $2
  output: Employment.Position
  expression: $1 + ", " + $2

- inputs:
  - $context(position).BaseSalary
  output: Employment.BaseSalary

En el ejemplo se asigna:

  • Asignación uno a uno: BirthDate se asigna directamente a Employee.DateOfBirth sin conversión.
  • Asignación de varios a uno: se combina Position y Office en un único campo de Employment.Position. La fórmula de conversión ($1 + ", " + $2) combina estos campos en una cadena con formato.
  • Datos contextuales: BaseSalary se agrega desde un conjunto de datos contextual denominado position.

Referencias de campo

Las referencias de campo muestran cómo especificar rutas de acceso en la entrada y salida mediante la notación de puntos, como Employee.DateOfBirth, o bien el acceso a datos desde un conjunto de datos contextual mediante $context(position).

Propiedades de usuario MQTT

Al usar MQTT como origen o destino, puede acceder a las propiedades de usuario de MQTT en el lenguaje de asignación. Las propiedades de usuario se pueden asignar en la entrada o salida.

En el ejemplo siguiente, la propiedad topic de MQTT se asigna al campo origin_topic en la salida.

    inputs:
       - $metadata.topic
    output: origin_topic

También puede asignar propiedades MQTT a un encabezado de salida. En el ejemplo siguiente, topic del MQTT se asigna al campo origin_topic en la propiedad de usuario de la salida:

    inputs:
       - $metadata.topic
    output: $metadata.user_property.origin_topic

Selectores de conjuntos de datos de contextualización

Estos selectores permiten que las asignaciones integren datos adicionales de bases de datos externas, denominadas conjuntos de datos de contextualización.

Filtrado de registros

El filtrado de registros implica establecer condiciones para seleccionar qué registros se deben procesar o quitar.

Notación de puntos

La notación de puntos se usa ampliamente en informática para hacer referencia a campos, incluso de manera recursiva. En programación, los nombres de campo normalmente constan de letras y números. Un ejemplo de notación de puntos estándar podría ser como el de este ejemplo:

- inputs:
  - Person.Address.Street.Number

En un flujo de datos, una ruta de acceso descrita por la notación de puntos podría incluir cadenas y algunos caracteres especiales sin necesidad de escape:

- inputs:
  - Person.Date of Birth

En otros casos, es necesario el escape:

- inputs:
  - nsu=http://opcfoundation.org/UA/Plc/Applications;s=RandomSignedInt32

El ejemplo anterior, entre otros caracteres especiales, contiene puntos dentro del nombre del campo. Si no se escapa, el nombre del campo serviría como separador en la notación de puntos.

Mientras un flujo de datos analiza una ruta de acceso, trata solo dos caracteres como especiales:

  • Los puntos (.) actúan como separadores de campo.
  • Las comillas simples, cuando se colocan al principio o al final de un segmento, inician una sección con escape donde los puntos no se tratan como separadores de campo.

Cualquier otro carácter se trata como parte del nombre del campo. Esta flexibilidad es útil en formatos como JSON, donde los nombres de campo pueden ser cadenas arbitrarias.

La definición de ruta de acceso también debe cumplir las reglas de YAML. Una vez que se incluye un carácter con significado especial en la ruta de acceso, se necesitan las comillas adecuadas en la configuración. Consulte la documentación de YAML para obtener reglas precisas. Estos son algunos ejemplos en los que se muestra la necesidad de un formato cuidadoso:

- inputs:
  - ':Person:.:name:'   # ':' cannot be used as the first character without single quotation marks
  - '100 celsius.hot'   # numbers followed by text would not be interpreted as a string without single quotation marks

Escape

La función principal del escape en una ruta de acceso con notación de puntos es dar cabida al uso de puntos que forman parte de nombres de campo en lugar de separadores:

- inputs:
  - 'Payload."Tag.10".Value'

En el ejemplo anterior, la ruta de acceso consta de tres segmentos: Payload, Tag.10 y Value. Las comillas simples externas (') son necesarias debido a las reglas de sintaxis de YAML, que permiten la inclusión de comillas dobles dentro de la cadena.

Reglas de escape en la notación de puntos

  • Aplicar escape a cada segmento por separado: si varios segmentos contienen puntos, esos segmentos se deben incluir entre comillas dobles. Otros segmentos también se pueden incluir entre comillas, pero no afecta a la interpretación de la ruta de acceso:

    - inputs:
      - 'Payload."Tag.10".Measurements."Vibration.$12".Value'
    
  • Uso adecuado de comillas dobles: las comillas dobles deben abrir y cerrar un segmento con escape. Las comillas del centro del segmento se consideran parte del nombre del campo:

    - inputs:
      - 'Payload.He said: "Hello", and waved'
    

    En este ejemplo se definen dos campos en dataDestination: Payload y He said: "Hello", and waved. Cuando aparece un punto en estas circunstancias, sigue funcionando como separador:

    - inputs:
      - 'Payload.He said: "No. It is done"'
    

    En este caso, la ruta de acceso se divide en los segmentos Payload, He said: "No y It is done" (empezando por un espacio).

Algoritmo de segmentación

  • Si el primer carácter de un segmento es una comilla, el analizador busca la siguiente comilla. La cadena incluida entre estas comillas se considera un único segmento.
  • Si el segmento no comienza con una comilla, el analizador identifica los segmentos buscando el siguiente punto o el final de la ruta de acceso.

Wildcard (Carácter comodín)

En muchos escenarios, el registro de salida se parece mucho al registro de entrada, y solo se necesitan pequeñas modificaciones. Cuando se trabaja con registros que contienen numerosos campos, la especificación manual de asignaciones para cada campo puede resultar tediosa. Los caracteres comodín simplifican este proceso al permitir asignaciones generalizadas que se pueden aplicar automáticamente a varios campos.

Imagine un escenario básico para comprender el uso de los asteriscos en las asignaciones:

- inputs:
  - '*'
  output: '*'

Aquí se muestra cómo funciona el asterisco (*) en este contexto:

  • Coincidencia de patrones: el asterisco puede coincidir con uno o varios segmentos de una ruta de acceso. Actúa como marcador de posición para los segmentos de la ruta de acceso.
  • Coincidencia de campos: durante el proceso de asignación, el algoritmo evalúa cada campo del registro de entrada con el patrón especificado en inputs. El asterisco del ejemplo anterior coincide con todas las rutas posibles, lo que ajusta de forma eficaz todos los campos individuales de la entrada.
  • Segmento capturado: la parte de la ruta de acceso con la que coincide el asterisco se conoce como captured segment.
  • Asignación de salida: en la configuración de salida, captured segment se coloca donde aparece el asterisco. Esto significa que la estructura de la entrada se conserva en la salida, y captured segment rellena el marcador de posición proporcionado por el asterisco.

Esta configuración muestra la forma más genérica de asignación, donde cada campo de la entrada se asigna directamente a un campo correspondiente en la salida sin modificaciones.

Otro ejemplo muestra cómo se pueden usar caracteres comodín para buscar coincidencias con subsecciones y moverlas de manera conjunta. En este ejemplo se aplanan eficazmente las estructuras anidadas dentro de un objeto JSON.

JSON original:

{
  "ColorProperties": {
    "Hue": "blue",
    "Saturation": "90%",
    "Brightness": "50%",
    "Opacity": "0.8"
  },
  "TextureProperties": {
    "type": "fabric",
    "SurfaceFeel": "soft",
    "SurfaceAppearance": "matte",
    "Pattern": "knitted"
  }
}

Configuración de asignación que usa caracteres comodín:

- inputs:
  - 'ColorProperties.*'
  output: '*'

- inputs:
  - 'TextureProperties.*'
  output: '*'

JSON resultante:

{
  "Hue": "blue",
  "Saturation": "90%",
  "Brightness": "50%",
  "Opacity": "0.8",
  "type": "fabric",
  "SurfaceFeel": "soft",
  "SurfaceAppearance": "matte",
  "Pattern": "knitted"
}

Colocación de los caracteres comodín

Al colocar un carácter comodín, debe seguir estas reglas:

  • Un solo asterisco por dataDestination: solo se permite un asterisco (*) dentro de una ruta de acceso.
  • Coincidencia de segmentos completa: el asterisco debe coincidir siempre con un segmento completo de la ruta de acceso. No se puede usar para que coincida solo con una parte de un segmento, como path1.partial*.path3.
  • Posicionamiento: el asterisco se puede colocar en varias partes de dataDestination:
    • Al inicio: *.path2.path3: aquí, el asterisco coincide con cualquier segmento que lleva a path2.path3.
    • En el centro: path1.*.path3: en esta configuración, el asterisco coincide con cualquier segmento entre path1 y path3.
    • Al final: path1.path2.*: el asterisco al final coincide con cualquier segmento que aparezca después de path1.path2.
  • La ruta de acceso que contiene el asterisco debe incluirse entre comillas simples (').

Caracteres comodín de entrada múltiple

JSON original:

{
  "Saturation": {
    "Max": 0.42,
    "Min": 0.67,
  },
  "Brightness": {
    "Max": 0.78,
    "Min": 0.93,
  },
  "Opacity": {
    "Max": 0.88,
    "Min": 0.91,
  }
}

Configuración de asignación que usa caracteres comodín:

- inputs:
  - '*.Max'   # - $1
  - '*.Min'   # - $2
  output: 'ColorProperties.*'
  expression: ($1 + $2) / 2

JSON resultante:

{
  "ColorProperties" : {
    "Saturation": 0.54,
    "Brightness": 0.85,
    "Opacity": 0.89 
  }    
}

Si hay caracteres comodín de entrada múltiple, el asterisco (*) debe representar de forma coherente el mismo elemento Captured Segment en cada entrada. Por ejemplo, cuando * captura Saturation en el patrón *.Max, el algoritmo de asignación espera que el elemento Saturation.Min correspondiente coincida con el patrón *.Min. En este caso, * se sustituye por el elemento Captured Segment de la primera entrada, lo que guía la coincidencia para las entradas posteriores.

Considere este ejemplo detallado:

JSON original:

{
  "Saturation": {
    "Max": 0.42,
    "Min": 0.67,
    "Mid": {
      "Avg": 0.51,
      "Mean": 0.56
    }
  },
  "Brightness": {
    "Max": 0.78,
    "Min": 0.93,
    "Mid": {
      "Avg": 0.81,
      "Mean": 0.82
    }
  },
  "Opacity": {
    "Max": 0.88,
    "Min": 0.91,
    "Mid": {
      "Avg": 0.89,
      "Mean": 0.89
    }
  }
}

Configuración inicial de asignación que usa caracteres comodín:

- inputs:
  - '*.Max'    # - $1
  - '*.Min'    # - $2
  - '*.Avg'    # - $3
  - '*.Mean'   # - $4
  output: 'ColorProperties.*'
  expression: ($1, $2, $3, $4)

Esta asignación inicial intenta crear una matriz (por ejemplo, para Opacity: [0.88, 0.91, 0.89, 0.89]). Se produce un error en esta configuración porque:

  • El primer elemento *.Max de entrada captura un segmento como Saturation.
  • La asignación espera que las entradas posteriores estén presentes en el mismo nivel:
    • Saturation.Max
    • Saturation.Min
    • Saturation.Avg
    • Saturation.Mean

Como Avg y Mean se anidan dentro de Mid, el asterisco de la asignación inicial no captura correctamente estas rutas de acceso.

Configuración de asignación corregida:

- inputs:
  - '*.Max'        # - $1
  - '*.Min'        # - $2
  - '*.Mid.Avg'    # - $3
  - '*.Mid.Mean'   # - $4
  output: 'ColorProperties.*'
  expression: ($1, $2, $3, $4)

Esta asignación revisada captura con precisión los campos necesarios. Especifica correctamente las rutas de acceso para incluir el objeto Mid anidado, lo que garantiza que los asteriscos funcionen eficazmente en distintos niveles de la estructura JSON.

Comparación de la segunda regla con la especialización

Al usar el ejemplo anterior de caracteres comodín de entrada múltiple, tenga en cuenta las siguientes asignaciones que generan dos valores derivados para cada propiedad:

- inputs:
  - '*.Max'   # - $1
  - '*.Min'   # - $2
  output: 'ColorProperties.*.Avg'
  expression: ($1 + $2) / 2

- inputs:
  - '*.Max'   # - $1
  - '*.Min'   # - $2
  output: 'ColorProperties.*.Diff'
  expression: abs($1 - $2)

Esta asignación está pensada para crear dos cálculos independientes (Avg y Diff) para cada propiedad en ColorProperties. En este ejemplo se muestra el resultado:

{
  "ColorProperties": {
    "Saturation": {
      "Avg": 0.54,
      "Diff": 0.25
    },
    "Brightness": {
      "Avg": 0.85,
      "Diff": 0.15
    },
    "Opacity": {
      "Avg": 0.89,
      "Diff": 0.03
    }
  }
}

Aquí, la definición de la segunda asignación de las mismas entradas actúa como una segunda regla para la asignación.

Ahora, considere un escenario en el que un campo específico necesita un cálculo diferente:

- inputs:
  - '*.Max'   # - $1
  - '*.Min'   # - $2
  output: 'ColorProperties.*'
  expression: ($1 + $2) / 2

- inputs:
  - Opacity.Max   # - $1
  - Opacity.Min   # - $2
  output: ColorProperties.OpacityAdjusted
  expression: ($1 + $2 + 1.32) / 2  

En este caso, el campo Opacity tiene un cálculo único. Hay dos opciones para controlar este escenario superpuesto:

  • Incluya ambas asignaciones para Opacity. Como los campos de salida son diferentes en este ejemplo, no se invalidan entre ellos.
  • Use la regla más específica para Opacity y quite la más genérica.

Considere un caso especial para los mismos campos a fin de decidir la acción correcta:

- inputs:
  - '*.Max'   # - $1
  - '*.Min'   # - $2
  output: 'ColorProperties.*'
  expression: ($1 + $2) / 2

- inputs:
  - Opacity.Max
  - Opacity.Min
  output:   

Un campo output vacío en la segunda definición implica no escribir los campos en el registro de salida (lo que quita Opacity). Esta configuración tiene más de Specialization que de Second Rule.

Resolución de asignaciones superpuestas por flujos de datos:

  • La evaluación avanza desde la regla superior en la definición de asignación.
  • Si una nueva asignación se resuelve en los mismos campos que una regla anterior, se aplican las siguientes condiciones:
    • Se calcula un elemento Rank para cada entrada resuelta en función del número de segmentos que captura el carácter comodín. Por ejemplo, si Captured Segments son Properties.Opacity, Rank es 2. Si solo es Opacity, Rank es 1. Una asignación sin caracteres comodín tiene un valor Rank de 0.
    • Si Rank en la última regla es igual o superior a la regla anterior, un flujo de datos lo trata como una instancia de Second Rule.
    • De lo contrario, el flujo de datos trata la configuración como Specialization.

Por ejemplo, la asignación que dirige Opacity.Max y Opacity.Min a una salida vacía tiene un elemento Rank de cero. Como la segunda regla tiene un elemento Rank inferior a la anterior, se considera una especialización e invalida la regla anterior, que calcularía un valor para Opacity.

Caracteres comodín en conjuntos de datos de contextualización

Ahora, veamos cómo se pueden usar los conjuntos de datos de contextualización con caracteres comodín a través de un ejemplo. Considere un conjunto de datos denominado position que contiene el registro siguiente:

{
  "Position": "Analyst",
  "BaseSalary": 70000,
  "WorkingHours": "Regular"
}

En un ejemplo anterior, se ha usado un campo específico de este conjunto de datos:

- inputs:
  - $context(position).BaseSalary
  output: Employment.BaseSalary

Esta asignación copia BaseSalary del conjunto de datos de contexto directamente en la sección Employment del registro de salida. Si quiere automatizar el proceso e incluir todos los campos del conjunto de datos position en la sección Employment, puede usar caracteres comodín:

- inputs:
  - '$context(position).*'
  output: 'Employment.*'

Esta configuración permite una asignación dinámica en la que cada campo del conjunto de datos position se copia en la sección Employment del registro de salida:

{
    "Employment": {      
      "Position": "Analyst",
      "BaseSalary": 70000,
      "WorkingHours": "Regular"
    }
}

Último valor conocido

Puede realizar un seguimiento del último valor conocido de una propiedad. Sufijo el campo de entrada con ? $last para capturar el último valor conocido del campo. Cuando falta un valor en una carga de entrada posterior, el último valor conocido se asigna a la carga de salida.

Por ejemplo, considere la siguiente asignación:

- inputs:
  - Temperature ? $last
  output: Thermostat.Temperature

En este ejemplo, se realiza el seguimiento del último valor conocido de Temperature. Si una carga de entrada posterior no contiene un valor Temperature, el último valor conocido se usa en la salida.