Registro de componentes en Device Update

En este artículo se muestra un ejemplo de implementación de un enumerador de componentes de Device Update for IoT Hub. Puede hacer referencia a este ejemplo para implementar un enumerador de componentes personalizado para los dispositivos IoT que desee. Un componente es una identidad por debajo del nivel de dispositivo que tiene una relación de composición con el dispositivo host.

En este artículo se muestra un enumerador de componentes que se usa con un dispositivo IoT virtual que se denomina Contoso Virtual Vacuum. Los enumeradores de componentes se usan para implementar la característica de actualización de proxy.

La actualización de proxy permite actualizar varios componentes del mismo dispositivo IoT o varios sensores conectados al dispositivo IoT con una única implementación de forma inalámbrica. La actualización de proxy admite un orden de instalación para la actualización de los componentes. También admite la actualización en varios pasos con funcionalidades de preinstalación, instalación y posinstalación.

Los casos de uso en que se aplican las actualizaciones de proxy incluyen, entre otros, los siguientes:

  • Aplicar archivos de actualización específicos a las particiones del dispositivo.
  • Aplicar archivos de actualización específicos a las aplicaciones o componentes del dispositivo.
  • Aplicar archivos de actualización específicos a los sensores conectados a los dispositivos IoT mediante un protocolo de red (por ejemplo, USB y bus CAN).

Para obtener más información, consulte Actualizaciones de proxy y actualización de varios componentes.

El agente de Device Update se ejecuta en el dispositivo host. Puede enviar cada actualización a un componente específico o a un grupo de componentes de la misma clase de hardware (es decir, que requieren la misma actualización de software o firmware).

¿Qué es un enumerador de componentes?

Un enumerador de componentes es una extensión del agente de Device Update que se usa para proporcionar información sobre todos los componentes que se necesitan para realizar una actualización, de forma inalámbrica, mediante una conexión de Azure IoT Hub de un dispositivo host.

El agente de Device Update es independiente de los dispositivos y los componentes. Por sí mismo, el agente no sabe nada sobre los componentes de un dispositivo host (o que están conectados a él) en el momento de la actualización.

Para habilitar las actualizaciones de proxy, los creadores de dispositivos deberán identificar todos los componentes del dispositivo que puedan actualizarse y asignar un nombre único a cada uno de ellos. Además, se puede asignar un nombre de grupo a los componentes de la misma clase de hardware, de modo que se pueda instalar la misma actualización en todos los componentes del mismo grupo. Después, desde el controlador de contenido de la actualización, se podrá instalar y aplicar la actualización en los componentes correctos.

Un diagrama donde se muestra el flujo de actualización de proxy.

Estas son las responsabilidades de cada parte del flujo de actualización de proxy:

  • Creador de dispositivos

    • Diseñar y crear el dispositivo.

    • Integrar el agente de Device Update y sus dependencias.

    • Implementar una extensión del enumerador de componentes que sea específica para un dispositivo concreto y registrarla en el agente de Device Update.

      El enumerador de componentes usa la información de un inventario de componentes o un archivo de configuración para aumentar los datos de componentes estáticos (se requiere Device Update) con datos dinámicos (por ejemplo, versión de firmware, estado de conexión e identidad de hardware).

    • Crear una actualización de proxy que contenga una o varias actualizaciones secundarias destinadas a uno o varios componentes del dispositivo (o conectados a él).

    • Enviar la actualización al operador de soluciones.

  • Operador de soluciones

    • Importar la actualización y el manifiesto al servicio Device Update.

    • Implementar la actualización en un grupo de dispositivos.

  • Agente de Device Update

    • Obtener la información de la actualización desde IoT Hub, mediante el dispositivo gemelo o el módulo gemelo.

    • Invocar a un controlador de pasos para procesar la actualización de proxy destinada a uno o varios componentes del dispositivo.

      En el ejemplo que se proporciona en este artículo, se incluyen dos actualizaciones: host-fw-1.1 y motors-fw-1.1. Para cada actualización secundaria, el controlador de pasos primario invoca al controlador de pasos secundario para enumerar todos los componentes que coincidan con las propiedades Compatibilities especificadas en el archivo de manifiesto de la actualización secundaria. A continuación, el controlador descarga, instala y aplica la actualización secundaria en todos los componentes de destino.

      Para obtener los componentes coincidentes, la actualización secundaria llama a la API SelectComponents que proporciona el enumerador de componentes. Si no hay componentes que coincidan, se omite la actualización secundaria.

    • Recopilar todos los resultados de las actualizaciones primarias y secundarias y, después, notificarlos a IoT Hub.

  • Controlador de pasos secundario

    • Iterar una lista de instancias de componentes que son compatibles con el contenido de la actualización secundaria. Para más información, consulte Controlador de pasos.

En producción, los creadores de dispositivos pueden usar controladores existentes o implementar un controlador personalizado que invoque cualquier instalador necesario para una actualización de forma inalámbrica. Para más información, vea Implementación de un controlador personalizado de contenido de actualización.

Componentes de Virtual Vacuum

En este artículo, se usa un dispositivo IoT virtual para mostrar los conceptos y las características clave. El dispositivo Contoso Virtual Vacuum consta de los cinco componentes lógicos siguientes:

  • Firmware del host
  • Sistema de archivos de arranque del host
  • Sistema de archivos raíz del host
  • Tres motores (rueda izquierda, rueda derecha y vacío)
  • Dos cámaras (frontal y trasera)

Diagrama donde se muestran los componentes del dispositivo Contoso Virtual Vacuum.

Los componentes del dispositivo se simulan en la siguiente estructura de directorios:

/usr/local/contoso-devices/vacuum-1/hostfw
/usr/local/contoso-devices/vacuum-1/bootfs
/usr/local/contoso-devices/vacuum-1/rootfs
/usr/local/contoso-devices/vacuum-1/motors/0   /* left motor */
/usr/local/contoso-devices/vacuum-1/motors/1   /* right motor */
/usr/local/contoso-devices/vacuum-1/motors/2   /* vacuum motor */
/usr/local/contoso-devices/vacuum-1/cameras/0  /* front camera */
/usr/local/contoso-devices/vacuum-1/cameras/1  /* rear camera */

El directorio de cada componente contiene un archivo JSON que almacena un número de versión de software ficticio de cada componente. Los archivos JSON de ejemplo son firmware.json y diskimage.json.

Para esta demostración, se copiará el archivo firmware.json o diskimage.json (carga de actualización) en el directorio de los componentes de destino a fin de actualizar el firmware de los componentes.

Este es un archivo firmware.json de ejemplo:

{
    "version": "0.5",
    "description": "This component is generated for testing purposes."
}

Nota

Contoso Virtual Vacuum contiene versiones de software o firmware con el fin de demostrar la actualización de proxy. No proporciona ninguna otra funcionalidad.

Implementar un enumerador de componentes (lenguaje C)

Requisitos

Implementar todas las API declaradas en component_enumerator_extension.hpp:

Función Argumentos Valores devueltos
char* GetAllComponents() Ninguna Cadena JSON que contiene una matriz de todos los valores de ComponentInfo. Para obtener más información, consulte Valores devueltos de ejemplo.
char* SelectComponents(char* selector) Cadena JSON que contiene uno o varios pares nombre-valor que se usan para seleccionar los componentes de destino de la actualización. Cadena JSON que contiene una matriz de los valores de ComponentInfo. Para obtener más información, consulte Valores devueltos de ejemplo.
void FreeComponentsDataString(char* string) Puntero al búfer de cadena devuelto previamente por las funciones GetAllComponents o SelectComponents. Ninguna

ComponentInfo

La cadena JSON ComponentInfo también debe incorporar las siguientes propiedades:

Nombre Tipo Descripción
id string Identidad única de un componente (ámbito de dispositivo). Algunos ejemplos son el número de serie del hardware, el id. de la partición de disco y la ruta de acceso de archivo única del componente.
name string Nombre lógico de un componente. Esta propiedad se usa para representar el nombre que un creador de dispositivos asigna a un componente que está disponible en todos los dispositivos de la misma clase device.

Por ejemplo, todos los dispositivos Contoso Virtual Vacuum contienen un motor que dirige una rueda izquierda. Contoso ha asignado motor izquierdo como nombre común (lógico) para este motor, para hacer referencia fácilmente a este componente en lugar del id. de hardware, que puede ser único globalmente.
group string Grupo al que pertenece este componente.

Por ejemplo, todos los motores podrían pertenecer al grupo motors.
manufacturer string En el caso de los componentes de hardware físicos, esta propiedad se usa para indicar el nombre del fabricante o proveedor.

En el caso de un componente lógico, como una partición de disco o un directorio, puede ser cualquier valor definido por el creador del dispositivo.
model string En el caso de los componentes de hardware físicos, esta propiedad se usa para indicar el nombre del modelo.

En el caso de un componente lógico, como una partición de disco o un directorio, esta propiedad puede corresponderse con cualquier valor que defina el creador del dispositivo.
properties object Objeto JSON que contiene cualquier propiedad opcional específica del dispositivo.

A continuación, se muestra un ejemplo de código ComponentInfo que se basa en los componentes del dispositivo Contoso Virtual Vacuum:

{
    "id": "contoso-motor-serial-00000",
    "name": "left-motor",
    "group": "motors",
    "manufacturer": "contoso",
    "model": "virtual-motor",
    "properties": {
        "path": "\/usr\/local\/contoso-devices\/vacuum-1\/motors\/0",
        "firmwareDataFile": "firmware.json",
        "status": "connected",
        "version" : "motor-fw-1.0"
    }
}

Valores devueltos de ejemplo

A continuación, se muestra un documento JSON devuelto desde la función GetAllComponents. Este se basa en el ejemplo de implementación del enumerador de componentes de Contoso Virtual Vacuum.

{
    "components": [
        {
            "id": "hostfw",
            "name": "hostfw",
            "group": "firmware",
            "manufacturer": "contoso",
            "model": "virtual-firmware",
            "properties": {
                "path": "\/usr\/local\/contoso-devices\/vacuum-1\/hostfw",
                "firmwareDataFile": "firmware.json",
                "status": "ok",
                "version" : "host-fw-1.0"
            }
        },
        {
            "id": "bootfs",
            "name": "bootfs",
            "group": "boot-image",
            "manufacturer": "contoso",
            "model": "virtual-disk",
            "properties": {
                "path": "\/usr\/local\/contoso-devices\/vacuum-1\/bootfs",
                "firmwareDataFile": "diskimage.json",
                "status": "ok",
                "version" : "boot-fs-1.0"
            }
        },
        {
            "id": "rootfs",
            "name": "rootfs",
            "group": "os-image",
            "manufacturer": "contoso",
            "model": "virtual-os",
            "properties": {
                "path": "\/usr\/local\/contoso-devices\/vacuum-1\/rootfs",
                "firmwareDataFile": "diskimage.json",
                "status": "ok",
                "version" : "root-fs-1.0"
            }
        },
        {
            "id": "contoso-motor-serial-00000",
            "name": "left-motor",
            "group": "motors",
            "manufacturer": "contoso",
            "model": "virtual-motor",
            "properties": {
                "path": "\/usr\/local\/contoso-devices\/vacuum-1\/motors\/0",
                "firmwareDataFile": "firmware.json",
                "status": "ok",
                "version" : "motor-fw-1.0"
            }
        },
        {
            "id": "contoso-motor-serial-00001",
            "name": "right-motor",
            "group": "motors",
            "manufacturer": "contoso",
            "model": "virtual-motor",
            "properties": {
                "path": "\/usr\/local\/contoso-devices\/vacuum-1\/motors\/1",
                "firmwareDataFile": "firmware.json",
                "status": "ok",
                "version" : "motor-fw-1.0"
            }
        },
        {
            "id": "contoso-motor-serial-00002",
            "name": "vacuum-motor",
            "group": "motors",
            "manufacturer": "contoso",
            "model": "virtual-motor",
            "properties": {
                "path": "\/usr\/local\/contoso-devices\/vacuum-1\/motors\/2",
                "firmwareDataFile": "firmware.json",
                "status": "ok",
                "version" : "motor-fw-1.0"
            }
        },
        {
            "id": "contoso-camera-serial-00000",
            "name": "front-camera",
            "group": "cameras",
            "manufacturer": "contoso",
            "model": "virtual-camera",
            "properties": {
                "path": "\/usr\/local\/contoso-devices\/vacuum-1\/camera\/0",
                "firmwareDataFile": "firmware.json",
                "status": "ok",
                "version" : "camera-fw-1.0"
            }
        },
        {
            "id": "contoso-camera-serial-00001",
            "name": "rear-camera",
            "group": "cameras",
            "manufacturer": "contoso",
            "model": "virtual-camera",
            "properties": {
                "path": "\/usr\/local\/contoso-devices\/vacuum-1\/camera\/1",
                "firmwareDataFile": "firmware.json",
                "status": "ok",
                "version" : "camera-fw-1.0"
            }
        }
    ]
}

El siguiente documento JSON se devuelve desde la función SelectComponents. Se basa en la implementación de ejemplo del enumerador de componentes de Contoso.

Este es el parámetro de entrada para seleccionar el grupo de componentes motors:

{
    "group" : "motors"
}

Esta es la salida del parámetro. Todos los componentes pertenecen al grupo motors.

{
    "components": [
        {
            "id": "contoso-motor-serial-00000",
            "name": "left-motor",
            "group": "motors",
            "manufacturer": "contoso",
            "model": "virtual-motor",
            "properties": {
                "path": "\/usr\/local\/contoso-devices\/vacuum-1\/motors\/0",
                "firmwareDataFile": "firmware.json",
                "status": "ok",
                "version" : "motor-fw-1.0"
            }
        },
        {
            "id": "contoso-motor-serial-00001",
            "name": "right-motor",
            "group": "motors",
            "manufacturer": "contoso",
            "model": "virtual-motor",
            "properties": {
                "path": "\/usr\/local\/contoso-devices\/vacuum-1\/motors\/1",
                "firmwareDataFile": "firmware.json",
                "status": "ok",
                "version" : "motor-fw-1.0"
            }
        },
        {
            "id": "contoso-motor-serial-00002",
            "name": "vacuum-motor",
            "group": "motors",
            "manufacturer": "contoso",
            "model": "virtual-motor",
            "properties": {
                "path": "\/usr\/local\/contoso-devices\/vacuum-1\/motors\/2",
                "firmwareDataFile": "firmware.json",
                "status": "ok",
                "version" : "motor-fw-1.0"
            }
        }
    ]
}

Este es el parámetro de entrada para seleccionar un componente individual llamado hostfw:

{
    "name" : "hostfw"
}

Esta es la salida del parámetro para el componente hostfw:

{
    "components": [
        {
            "id": "hostfw",
            "name": "hostfw",
            "group": "firmware",
            "manufacturer": "contoso",
            "model": "virtual-firmware",
            "properties": {
                "path": "\/usr\/local\/contoso-devices\/vacuum-1\/hostfw",
                "firmwareDataFile": "firmware.json",
                "status": "ok",
                "version" : "host-fw-1.0"
            }
        }
    ]
}

Nota

En el ejemplo anterior, se ha demostrado que, si es necesario, es posible enviar una actualización más reciente a cualquier instancia de un componente seleccionado mediante la propiedad name. Por ejemplo, implemente la actualización motor-fw-2.0 en vacuum-motor mientras sigue utilizando motor-fw-1.0 en left-motor y right-motor.

Archivo de inventario

En el caso del ejemplo de implementación del enumerador de componentes de Contoso Virtual Vacuum, que se mostró anteriormente, la información de los componentes específicos de ese dispositivo se leerá desde el archivo component-inventory.json. Este ejemplo de implementación solo se muestra a modo de demostración.

En un escenario de producción, algunas propiedades se deben recuperar directamente de los componentes reales. Estas propiedades incluyen id, manufacturer y model.

El creador del dispositivo define las propiedades name y group. Estos valores nunca deben cambiar una vez definidos. La propiedad name debe ser única dentro del dispositivo.

Archivo component-inventory.json de ejemplo

Nota

El contenido de este archivo tiene casi el mismo aspecto que el valor devuelto desde la función GetAllComponents. Sin embargo, el elemento ComponentInfo de este archivo no contiene las propiedades version y status. El enumerador de componentes rellenará estas propiedades en tiempo de ejecución.

Por ejemplo, para hostfw, el valor de la propiedad properties.version se rellenará con el valor del elemento firmwareDataFile especificado (ficticio) (/usr/local/contoso-devices/vacuum-1/hostfw/firmware.json).

{
    "components": [
        {
            "id": "hostfw",
            "name": "hostfw",
            "group": "firmware",
            "manufacturer": "contoso",
            "model": "virtual-firmware",
            "properties": {
                "path": "\/usr\/local\/contoso-devices\/vacuum-1\/hostfw",
                "firmwareDataFile": "firmware.json",
            }
        },
        {
            "id": "bootfs",
            "name": "bootfs",
            "group": "boot-image",
            "manufacturer": "contoso",
            "model": "virtual-disk",
            "properties": {
                "path": "\/usr\/local\/contoso-devices\/vacuum-1\/bootfs",
                "firmwareDataFile": "diskimage.json",
            }
        },
        {
            "id": "rootfs",
            "name": "rootfs",
            "group": "os-image",
            "manufacturer": "contoso",
            "model": "virtual-os",
            "properties": {
                "path": "\/usr\/local\/contoso-devices\/vacuum-1\/rootfs",
                "firmwareDataFile": "diskimage.json",
            }
        },
        {
            "id": "contoso-motor-serial-00000",
            "name": "left-motor",
            "group": "motors",
            "manufacturer": "contoso",
            "model": "virtual-motor",
            "properties": {
                "path": "\/usr\/local\/contoso-devices\/vacuum-1\/motors\/0",
                "firmwareDataFile": "firmware.json",
            }
        },
        {
            "id": "contoso-motor-serial-00001",
            "name": "right-motor",
            "group": "motors",
            "manufacturer": "contoso",
            "model": "virtual-motor",
            "properties": {
                "path": "\/usr\/local\/contoso-devices\/vacuum-1\/motors\/1",
                "firmwareDataFile": "firmware.json",
            }
        },
        {
            "id": "contoso-motor-serial-00002",
            "name": "vacuum-motor",
            "group": "motors",
            "manufacturer": "contoso",
            "model": "virtual-motor",
            "properties": {
                "path": "\/usr\/local\/contoso-devices\/vacuum-1\/motors\/2",
                "firmwareDataFile": "firmware.json",
            }
        },
        {
            "id": "contoso-camera-serial-00000",
            "name": "front-camera",
            "group": "cameras",
            "manufacturer": "contoso",
            "model": "virtual-camera",
            "properties": {
                "path": "\/usr\/local\/contoso-devices\/vacuum-1\/camera\/0",
                "firmwareDataFile": "firmware.json",
            }
        },
        {
            "id": "contoso-camera-serial-00001",
            "name": "rear-camera",
            "group": "cameras",
            "manufacturer": "contoso",
            "model": "virtual-camera",
            "properties": {
                "path": "\/usr\/local\/contoso-devices\/vacuum-1\/camera\/1",
                "firmwareDataFile": "firmware.json",
            }
        }
    ]
}

Pasos siguientes

En el ejemplo de este artículo se usó el lenguaje C. Para explorar código fuente de ejemplo en C++, consulte los siguientes recursos:

Para ver varias actualizaciones de ejemplo de componentes conectados al dispositivo Contoso Virtual Vacuum, vea Demostración de actualización de proxy.