Programación y difusión de trabajos (Java)
Use Azure IoT Hub para programar y realizar el seguimiento de los trabajos que actualizan millones de dispositivos. Use los trabajos para:
- Actualizar las propiedades deseadas
- Actualizar etiquetas
- Invocar métodos directos
Un trabajo encapsula una de estas acciones y realiza el seguimiento de la ejecución en un conjunto de dispositivos. Una consulta de dispositivo gemelo define el conjunto de dispositivos en que se ejecuta el trabajo. Por ejemplo, una aplicación de back-end puede utilizar un trabajo para invocar un método directo en 10 000 dispositivos que reinicie los dispositivos. Especifique el conjunto de dispositivos con una consulta de dispositivo gemelo y programe el trabajo para que se ejecute en otro momento. Este trabajo realiza el seguimiento del progreso mientras los dispositivos reciben y ejecutan el método directo de reinicio.
Para más información sobre estas funcionalidades, vea:
Dispositivo gemelo y propiedades: Introducción a los dispositivos gemelos y Tutorial: Uso de las propiedades deseadas para configurar dispositivos
Métodos directos: Guía para desarrolladores de IoT Hub: métodos directos
Nota
Las características descritas en este artículo solo están disponibles en el nivel estándar de IoT Hub. Para obtener más información sobre los niveles Básico y Estándar o Gratis de IoT Hub, consulte Elección del nivel adecuado de IoT Hub para la solución.
En este artículo se muestra cómo crear dos aplicaciones de Java:
Una aplicación de dispositivo, simulated-device, que implementa un método directo llamado lockDoor, que se puede llamar mediante la aplicación de back-end.
Una aplicación de back-end, schedule-jobs, que crea dos trabajos. Un trabajo llama al método directo lockDoor y otro trabajo envía actualizaciones de propiedades deseadas a varios dispositivos.
Nota
Consulte los SDK de Azure IoT para obtener más información sobre las herramientas de SDK disponibles para compilar aplicaciones de dispositivo y back-end.
Requisitos previos
Una instancia de IoT Hub en la suscripción de Azure. Si aún no tiene un centro, puede seguir los pasos descritos en Creación de un centro de IoT.
Un dispositivo registrado en su centro de IoT. Si no tiene un dispositivo en el centro de IoT, siga los pasos descritos en Registrar un dispositivo.
Java SE Development Kit 8. Asegúrese de seleccionar Java 8 en Long-term support (Soporte técnico a largo plazo) para obtener descargas de JDK 8.
Asegúrese de que está abierto el puerto 8883 del firewall. En el ejemplo de dispositivo de este artículo se usa el protocolo MQTT, que se comunica mediante el puerto 8883. Este puerto puede estar bloqueado en algunos entornos de red corporativos y educativos. Para más información y para saber cómo solucionar este problema, consulte el artículo sobre la conexión a IoT Hub (MQTT).
Nota
Por simplificar, este artículo no implementa ninguna directiva de reintentos. En el código de producción, deberá implementar directivas de reintentos (por ejemplo, retroceso exponencial), tal y como se sugiere en el artículo Control de errores transitorios.
Obtener la cadena de conexión de IoT Hub
En este artículo se creará un servicio de back-end que programa un trabajo para que invoque un método directo en un dispositivo, programa un trabajo para actualizar el dispositivo gemelo y supervisa el progreso de cada trabajo. Para realizar estas operaciones, el servicio necesita los permisos de lectura del registro y escritura en el registro. De manera predeterminada, todas las instancias del centro de IoT se crean con una directiva de acceso compartido denominada registryReadWrite que concede estos permisos.
Para obtener la cadena de conexión de IoT Hub para la directiva registryReadWrite, siga estos pasos:
En Azure Portal, seleccione Grupos de recursos. Seleccione el grupo de recursos donde se encuentra el centro y, a continuación, seleccione el centro en la lista de recursos.
En el panel de la izquierda del centro, seleccione Directivas de acceso compartido.
En la lista de directivas, seleccione la directiva registryReadWrite.
Copie la Cadena de conexión principal y guarde el valor.
Para obtener más información sobre las directivas de acceso compartido y los permisos de IoT Hub, consulte Permisos y control del acceso.
Importante
En este artículo se incluyen los pasos para conectarse a un servicio mediante una firma de acceso compartido. Este método de autenticación es cómodo para las pruebas y la evaluación, pero la autenticación en un servicio con el Microsoft Entra ID o las identidades administradas es un enfoque más seguro. Para obtener más información, consulte Procedimientos recomendados de seguridad> Seguridad en la nube.
Creación de la aplicación de servicio
En esta sección, creará una aplicación de consola de Java que usa trabajos para:
Llamar al método directo lockDoor en varios dispositivos.
Usar las propiedades deseadas en varios dispositivos.
Para crear la aplicación:
En el equipo de desarrollo, cree una carpeta vacía denominada iot-java-schedule-jobs.
En la carpeta iot-java-schedule-jobs, cree un proyecto de Maven denominado schedule-jobs mediante el siguiente comando en el símbolo del sistema. Observe que este es un comando único y largo:
mvn archetype:generate -DgroupId=com.mycompany.app -DartifactId=schedule-jobs -DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=false
En el símbolo del sistema, vaya a la carpeta schedule-jobs.
Con un editor de texto, abra el archivo pom.xml en la carpeta schedule-jobs y agregue la dependencia siguiente al nodo dependencies. Esta dependencia permite usar el paquete iot-service-client en la aplicación para comunicarse con la instancia de IoT Hub:
<dependency> <groupId>com.microsoft.azure.sdk.iot</groupId> <artifactId>iot-service-client</artifactId> <version>1.17.1</version> <type>jar</type> </dependency>
Nota
Puede comprobar la versión más reciente de iot-service-client mediante la búsqueda de Maven.
Agregue el nodo build después del nodo dependencies. Esta configuración indica a Maven que use Java 1.8 para compilar la aplicación:
<build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.3</version> <configuration> <source>1.8</source> <target>1.8</target> </configuration> </plugin> </plugins> </build>
Guarde y cierre el archivo pom.xml.
Con un editor de texto, abra el archivo schedule-jobs\src\main\java\com\mycompany\app\App.java.
Agregue las siguientes instrucciones import al archivo:
import com.microsoft.azure.sdk.iot.service.devicetwin.DeviceTwinDevice; import com.microsoft.azure.sdk.iot.service.devicetwin.Pair; import com.microsoft.azure.sdk.iot.service.devicetwin.Query; import com.microsoft.azure.sdk.iot.service.devicetwin.SqlQuery; import com.microsoft.azure.sdk.iot.service.jobs.JobClient; import com.microsoft.azure.sdk.iot.service.jobs.JobResult; import com.microsoft.azure.sdk.iot.service.jobs.JobStatus; import java.util.Date; import java.time.Instant; import java.util.HashSet; import java.util.Set; import java.util.UUID;
Agregue las siguientes variables de nivel de clase a la clase App . Reemplace
{youriothubconnectionstring}
por la cadena de conexión de IoT Hub que copió anteriormente en Obtención de la cadena de conexión de IoT Hub:public static final String iotHubConnectionString = "{youriothubconnectionstring}"; public static final String deviceId = "myDeviceId"; // How long the job is permitted to run without // completing its work on the set of devices private static final long maxExecutionTimeInSeconds = 30;
Agregue el método siguiente a la clase App para programar un trabajo de actualización de las propiedades Building y Floor deseadas en el dispositivo gemelo:
private static JobResult scheduleJobSetDesiredProperties(JobClient jobClient, String jobId) { DeviceTwinDevice twin = new DeviceTwinDevice(deviceId); Set<Pair> desiredProperties = new HashSet<Pair>(); desiredProperties.add(new Pair("Building", 43)); desiredProperties.add(new Pair("Floor", 3)); twin.setDesiredProperties(desiredProperties); // Optimistic concurrency control twin.setETag("*"); // Schedule the update twin job to run now // against a single device System.out.println("Schedule job " + jobId + " for device " + deviceId); try { JobResult jobResult = jobClient.scheduleUpdateTwin(jobId, "deviceId='" + deviceId + "'", twin, new Date(), maxExecutionTimeInSeconds); return jobResult; } catch (Exception e) { System.out.println("Exception scheduling desired properties job: " + jobId); System.out.println(e.getMessage()); return null; } }
Para programar un trabajo de llamada al método lockDoor, agregue el método siguiente a la clase App:
private static JobResult scheduleJobCallDirectMethod(JobClient jobClient, String jobId) { // Schedule a job now to call the lockDoor direct method // against a single device. Response and connection // timeouts are set to 5 seconds. System.out.println("Schedule job " + jobId + " for device " + deviceId); try { JobResult jobResult = jobClient.scheduleDeviceMethod(jobId, "deviceId='" + deviceId + "'", "lockDoor", 5L, 5L, null, new Date(), maxExecutionTimeInSeconds); return jobResult; } catch (Exception e) { System.out.println("Exception scheduling direct method job: " + jobId); System.out.println(e.getMessage()); return null; } };
Para supervisar un trabajo, agregue el siguiente método a la clase App:
private static void monitorJob(JobClient jobClient, String jobId) { try { JobResult jobResult = jobClient.getJob(jobId); if(jobResult == null) { System.out.println("No JobResult for: " + jobId); return; } // Check the job result until it's completed while(jobResult.getJobStatus() != JobStatus.completed) { Thread.sleep(100); jobResult = jobClient.getJob(jobId); System.out.println("Status " + jobResult.getJobStatus() + " for job " + jobId); } System.out.println("Final status " + jobResult.getJobStatus() + " for job " + jobId); } catch (Exception e) { System.out.println("Exception monitoring job: " + jobId); System.out.println(e.getMessage()); return; } }
Para consultar los detalles de los trabajos ejecutados, agregue el método siguiente:
private static void queryDeviceJobs(JobClient jobClient, String start) throws Exception { System.out.println("\nQuery device jobs since " + start); // Create a jobs query using the time the jobs started Query deviceJobQuery = jobClient .queryDeviceJob(SqlQuery.createSqlQuery("*", SqlQuery.FromType.JOBS, "devices.jobs.startTimeUtc > '" + start + "'", null).getQuery()); // Iterate over the list of jobs and print the details while (jobClient.hasNextJob(deviceJobQuery)) { System.out.println(jobClient.getNextJob(deviceJobQuery)); } }
Actualice la firma del método main para incluir la cláusula
throws
siguiente:public static void main( String[] args ) throws Exception
Para ejecutar y supervisar dos trabajos de forma secuencial, reemplace el código del método main con lo siguiente:
// Record the start time String start = Instant.now().toString(); // Create JobClient JobClient jobClient = JobClient.createFromConnectionString(iotHubConnectionString); System.out.println("JobClient created with success"); // Schedule twin job desired properties // Maximum concurrent jobs is 1 for Free and S1 tiers String desiredPropertiesJobId = "DPCMD" + UUID.randomUUID(); scheduleJobSetDesiredProperties(jobClient, desiredPropertiesJobId); monitorJob(jobClient, desiredPropertiesJobId); // Schedule twin job direct method String directMethodJobId = "DMCMD" + UUID.randomUUID(); scheduleJobCallDirectMethod(jobClient, directMethodJobId); monitorJob(jobClient, directMethodJobId); // Run a query to show the job detail queryDeviceJobs(jobClient, start); System.out.println("Shutting down schedule-jobs app");
Guarde y cierre el archivo schedule-jobs\src\main\java\com\mycompany\app\App.java.
Compile la aplicación schedule-jobs y corrija los errores. En el símbolo del sistema, vaya a la carpeta schedule-jobs y ejecute el comando siguiente:
mvn clean package -DskipTests
Creación de una aplicación de dispositivo
En esta sección, creará una aplicación de consola de Java que controla las propiedades deseadas enviadas desde IoT Hub e implementa la llamada al método directo.
Importante
En este artículo se incluyen los pasos para conectar un dispositivo mediante una firma de acceso compartido, también denominada autenticación de clave simétrica. Este método de autenticación es cómodo para probar y evaluar, pero autenticar un dispositivo mediante certificados X.509 es un enfoque más seguro. Para obtener más información, consulte Procedimientos recomendados de > Seguridad de la conexión.
En la carpeta iot-java-schedule-jobs, cree un proyecto de Maven denominado simulated-device mediante el comando siguiente en el símbolo del sistema. Observe que este es un comando único y largo:
mvn archetype:generate -DgroupId=com.mycompany.app -DartifactId=simulated-device -DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=false
En el símbolo del sistema, vaya a la nueva carpeta simulated-device.
Con un editor de texto, abra el archivo pom.xml de la carpeta simulated-device y agregue las siguientes dependencias al nodo dependencies. Esta dependencia permite usar el paquete iot-device-client en la aplicación para comunicarse con la instancia de IoT Hub:
<dependency> <groupId>com.microsoft.azure.sdk.iot</groupId> <artifactId>iot-device-client</artifactId> <version>1.17.5</version> </dependency>
Nota
Puede comprobar la versión más reciente de iot-device-client mediante la búsqueda de Maven.
Agregue la siguiente dependencia al nodo dependencies. Esta dependencia configura una instrucción NOP para la fachada de registro de Apache SLF4J, que usa el SDK de cliente de dispositivo para implementar el registro. Esta configuración es opcional, pero si la omite, es posible que vea una advertencia en la consola al ejecutar la aplicación. Para obtener más información sobre el registro en SDK de cliente de dispositivo, consulte Registro en el archivo Léame Samples for the Azure IoT device SDK for Java (Ejemplos para el SDK de dispositivo IoT de Azure para Java).
<dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-nop</artifactId> <version>1.7.28</version> </dependency>
Agregue el nodo build después del nodo dependencies. Esta configuración indica a Maven que use Java 1.8 para compilar la aplicación:
<build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.3</version> <configuration> <source>1.8</source> <target>1.8</target> </configuration> </plugin> </plugins> </build>
Guarde y cierre el archivo pom.xml.
Con un editor de texto, abra el archivo simulated-device\src\main\java\com\mycompany\app\App.java.
Agregue las siguientes instrucciones import al archivo:
import com.microsoft.azure.sdk.iot.device.*; import com.microsoft.azure.sdk.iot.device.DeviceTwin.*; import java.io.IOException; import java.net.URISyntaxException; import java.util.Scanner;
Agregue las siguientes variables de nivel de clase a la clase App . Reemplace
{yourdeviceconnectionstring}
por la cadena de conexión de dispositivo que vio cuando registró un dispositivo en el IoT Hub:private static String connString = "{yourdeviceconnectionstring}"; private static IotHubClientProtocol protocol = IotHubClientProtocol.MQTT; private static final int METHOD_SUCCESS = 200; private static final int METHOD_NOT_DEFINED = 404;
Esta aplicación de ejemplo usa la variable protocol al crear una instancia de un objeto DeviceClient.
Para imprimir las notificaciones de dispositivos gemelos en la consola, agregue la siguiente clase anidada a la clase App:
// Handler for device twin operation notifications from IoT Hub protected static class DeviceTwinStatusCallBack implements IotHubEventCallback { public void execute(IotHubStatusCode status, Object context) { System.out.println("IoT Hub responded to device twin operation with status " + status.name()); } }
Para imprimir las notificaciones de métodos directos en la consola, agregue la siguiente clase anidada a la clase App:
// Handler for direct method notifications from IoT Hub protected static class DirectMethodStatusCallback implements IotHubEventCallback { public void execute(IotHubStatusCode status, Object context) { System.out.println("IoT Hub responded to direct method operation with status " + status.name()); } }
Para controlar las llamadas a métodos directos desde IoT Hub, agregue la siguiente clase anidada a la clase App:
// Handler for direct method calls from IoT Hub protected static class DirectMethodCallback implements DeviceMethodCallback { @Override public DeviceMethodData call(String methodName, Object methodData, Object context) { DeviceMethodData deviceMethodData; switch (methodName) { case "lockDoor": { System.out.println("Executing direct method: " + methodName); deviceMethodData = new DeviceMethodData(METHOD_SUCCESS, "Executed direct method " + methodName); break; } default: { deviceMethodData = new DeviceMethodData(METHOD_NOT_DEFINED, "Not defined direct method " + methodName); } } // Notify IoT Hub of result return deviceMethodData; } }
Actualice la firma del método main para incluir la cláusula
throws
siguiente:public static void main( String[] args ) throws IOException, URISyntaxException
Reemplace el código del método main por el código siguiente:
- Cree un cliente de dispositivo para comunicarse con IoT Hub.
- Cree un objeto Device para almacenar las propiedades del dispositivo gemelo.
// Create a device client DeviceClient client = new DeviceClient(connString, protocol); // An object to manage device twin desired and reported properties Device dataCollector = new Device() { @Override public void PropertyCall(String propertyKey, Object propertyValue, Object context) { System.out.println("Received desired property change: " + propertyKey + " " + propertyValue); } };
Para iniciar los servicios de cliente de dispositivo, agregue el código siguiente al método main:
try { // Open the DeviceClient // Start the device twin services // Subscribe to direct method calls client.open(); client.startDeviceTwin(new DeviceTwinStatusCallBack(), null, dataCollector, null); client.subscribeToDeviceMethod(new DirectMethodCallback(), null, new DirectMethodStatusCallback(), null); } catch (Exception e) { System.out.println("Exception, shutting down \n" + " Cause: " + e.getCause() + " \n" + e.getMessage()); dataCollector.clean(); client.closeNow(); System.out.println("Shutting down..."); }
Para esperar a que el usuario presione la tecla ENTRAR antes de apagarlo, agregue el código siguiente al final del método main:
// Close the app System.out.println("Press any key to exit..."); Scanner scanner = new Scanner(System.in); scanner.nextLine(); dataCollector.clean(); client.closeNow(); scanner.close();
Guarde y cierre el archivo simulated-device\src\main\java\com\mycompany\app\App.java.
Compile la aplicación simulated-device y corrija los errores. En el símbolo del sistema, vaya a la carpeta simulated-device y ejecute el comando siguiente:
mvn clean package -DskipTests
Ejecución de las aplicaciones
Ya está preparado para ejecutar las aplicaciones de consola.
En un símbolo del sistema en la carpeta simulated-device, ejecute el siguiente comando para iniciar la escucha de cambios en las propiedades deseadas y de llamadas a métodos directos de la aplicación de dispositivo:
mvn exec:java -Dexec.mainClass="com.mycompany.app.App"
En un símbolo del sistema en la carpeta
schedule-jobs
, ejecute el siguiente comando para ejecutar la aplicación de servicio schedule-jobs a fin de ejecutar dos trabajos. El primero establece los valores de la propiedad deseada, mientras que el segundo llama al método directo:mvn exec:java -Dexec.mainClass="com.mycompany.app.App"
La aplicación de dispositivo controla el cambio de la propiedad deseada y la llamada al método directo:
Pasos siguientes
En este artículo, ha programado trabajos para ejecutar un método directo y actualizar las propiedades del dispositivo gemelo.
Para continuar explorando el IoT Hub y los patrones de administración de dispositivos, actualice una imagen en el tutorial de Actualización de Dispositivos para Azure IoT Hub mediante la Imagen de Referencia de Raspberry Pi 3 B+.