Capítulo 4. Emulación de datos

Este trabajo se ha traducido utilizando IA. Agradecemos tus opiniones y comentarios: translation-feedback@oreilly.com

Veo, oigo y huelo.

¿Debo transmitir una advertencia?

Falsa alarma. Ignórala.

Procesar los datos de los sensores de los simuladores y generar salidas de registro como parte de un evento de actuación está muy bien, pero ¿no sería aún mejor utilizar controles deslizantes para ajustar los valores que lee tu tarea de sensores? Aún mejor sería la posibilidad de mostrar mensajes en una pantalla LED real (o emulada) cuando se activa un evento de actuación, ¿no?

Este capítulo explora cómo instalar, configurar y utilizar un emulador que puede proporcionar una pantalla LED virtual, además de lecturas de temperatura, presión, humedad y otras.

Lo que aprenderás en este capítulo

Gran parte de este capítulo está dedicado a instalar y configurar el emulador Sense-Emu, que es un dispositivo virtual Sense HAT1 virtual que puedes ejecutar en plataformas Windows, Mac o Linux (aunque a mí me ha resultado más fácil ejecutarlo en Linux).

Nota

El Sense HAT es una placa que puede conectarse al GPIO de 40 patillas de una Raspberry Pi y proporciona diversas capacidades de detección, como humedad, presión y temperatura. La matriz LED muestra texto o gráficos en varios colores en una cuadrícula de 8x8. También contiene una unidad de medición inercial (IMU) y un magnetómetro integrados para medir las lecturas de la brújula, la orientación en los ejes x/y/z y la aceleración. El emulador Sense-Emu proporciona una instancia virtual del Sense HAT, que será el centro de atención de este capítulo.

Configurar un dispositivo IoT puede plantearte retos interesantes, al igual que instalar y configurar un emulador. Me centraré en el emulador en este capítulo y asumiré que lo utilizarás para futuros ejercicios, pero puedes optar por utilizar hardware real, como una Raspberry Pi, para ejecutar tu CDA (y GDA).2 Si decides seguir adelante con esta estrategia de implementación, la configuración de tu dispositivo para que pueda funcionar como pasarela y como dispositivo restringido es un poco complicada y, en la mayoría de los casos, será específica de tu dispositivo.

Nota

Si decides configurar Jenkins 2 para ayudarte con CI/CD en tu sistema local, consulta el libro de Brent Laster Jenkins 2 Up & Running (O'Reilly). Si quieres ejecutarlo directamente en una Raspberry Pi con un sistema operativo basado en Linux (como Raspbian), presta atención a la instalación y configuración para entornos basados en Linux. Aunque queda fuera del alcance de este libro, quizá quieras considerar este paso de configuración adicional, ya que puede ayudarte a automatizar tu entorno de desarrollo.

Como recordatorio amistoso, este capítulo se centra exclusivamente en el CDA, por lo que el código está escrito en Python. Al igual que en los capítulos anteriores, asegúrate de seguir los pasos indicados en PIOT-CDA-04-000 y consulta una nueva rama para este capítulo.

Emulación de sensores y actuadores

Los sensores leen datos de un sistema y abarcan una amplia gama de capacidades. Para la siguiente aplicación, construirás emuladores que simulen la funcionalidad de los sensores y te permitan activar uno o varios eventos de actuación, en caso de que los sensores simulados generen datos que requieran que tu dispositivo actúe.

En última instancia, la determinación de esta acción estará en manos de la App Dispositivo de Pasarela y, quizás más adelante, del componente de análisis de tu infraestructura de servicios en la nube. Por ahora, construirás una sencilla función de prueba dentro de tu App Dispositivo Restringido que busque el cruce de un umbral y lo utilice para enviar un evento de actuación al actuador simulado. Una vez que esto funcione correctamente, verás cómo la infraestructura que has desarrollado será una parte importante del siguiente conjunto de ejercicios.

Instalar y configurar un emulador

La modularidad del diseño del CDA te permitirá añadir nuevas funciones al emulador sin hacer demasiados cambios: básicamente, añadirás funciones a las clases SensorAdapterManager y ActuatorAdapterManager para pasar de la simulación a la emulación y luego añadirás nuevas tareas de detección y actuación al emulador.

Las tareas del emulador para la detección se derivarán de BaseSensorSimTask, mientras que las relacionadas con la actuación se derivarán de BaseActuatorSimTask. Dentro de las clases gestoras, el cambio entre simulación y emulación se gestionará dentro del constructor de cada clase.

Antes de profundizar en los ejercicios y el código, vamos a explorar algunas de las dependencias para que la funcionalidad del emulador funcione correctamente.

El emulador Sense-Emu Sense HAT

Construir emuladores puede ser complicado, y somos afortunados de tener acceso a varias herramientas diferentes que permiten probar y emular diversos componentes de hardware útiles en el procesamiento de niveles de perímetro IoT. Una de esas herramientas mencionadas anteriormente es el emulador Sense-Emu Sense HAT, que incluye una interfaz gráfica de usuario (GUI) con palancas que te permiten emular las lecturas de los sensores de una tarjeta Sense HAT.3

Nota

La Sense HAT es una placa que puede añadirse a una Raspberry Pi utilizando el cabezal de entrada/salida de propósito general (GPIO) de 40 patillas. Aunque un análisis detallado de esta capacidad queda fuera del alcance de este libro, una rápida búsqueda en Internet te proporcionará una lista de numerosos recursos con descripciones detalladas de la placa y sus sensores, proyectos de ejemplo y código de muestra que pueden resultarte útiles si piensas incorporar hardware real a tu diseño.

La Figura 4-1 es una captura de pantalla del emulador Sense-Emu ejecutándose en mi sistema local.

Sense-Emu Sense HAT emulator screenshot
Figura 4-1. Captura de pantalla del emulador Sense-Emu Sense HAT

Como puedes ver, hay varias formas de generar datos dentro de la GUI. Antes de tomar esta captura de pantalla, ajusté la temperatura a 28,8 °C, la presión a 984,5 mbar y la humedad a 40,2%.

Una vez que el emulador esté en funcionamiento, necesitarás una forma de interactuar con él utilizando tu CDA. La biblioteca pisense de Python permite la integración con el emulador Sense-Emu y con la placa Sense HAT real. Puedes cambiar entre ellos estableciendo una bandera dentro de la inicialización de la clase Sense HAT en la biblioteca pisense.

Antes de adentrarnos en el código, revisa PIOT-CFG-04-001, que explica la instalación y configuración del emulador Sense-Emu. No dedicaré mucho tiempo a hablar de ello aquí, pero es probable que haya otras dependencias para que el emulador funcione en tu sistema. He ejecutado con éxito el emulador Sense-Emu en Windows utilizando WSL (con un servidor X11 independiente) y en macOS.

Por último, el archivo de configuración PiotConfig.props tiene una propiedad en la sección ConstrainedDevice llamada enableEmulator. Una vez que tu emulador esté instalado y correctamente configurado, y después de que hayas completado los ejercicios de esta sección, cambia el valor de la propiedad a "True" y deberías poder probar tu código con el emulador.

Empecemos a codificar.

Ejercicios de programación

Observarás un patrón común en las relaciones de los componentes para la funcionalidad del emulador: es el mismo que tu diseño e implementación del Capítulo 3 en lo que respecta a la funcionalidad del simulador. Esto proporciona una gran flexibilidad a tu implementación, ya que podrás añadir nuevas capacidades a tu CDA que superen los requisitos especificados en estos ejercicios.

Integrar la Emulación de Sensores y Actuadores en el Diseño de tu Aplicación

La Figura 4-2 ofrece una vista sencilla del diseño del CDA una vez incorporadas estas nuevas funciones.

Constrained Device Application—integrated sensing and actuation app design
Figura 4-2. Diseño de aplicación de detección y actuación integrada en la aplicación de dispositivo restringido

Observa que el diseño se parece mucho al de la Figura 3-3, donde integraste la funcionalidad de simulación en tu CDA. Su modularidad permite añadir tareas de emulación con pequeñas modificaciones en la lógica del gestor.

Ahora echemos un vistazo al diseño detallado representado como UML. La Figura 4-3 muestra una forma de representar los componentes clave de este ejercicio, omitiendo algunos detalles del ejercicio anterior para mayor claridad.

Al igual que el diseño de alto nivel de la Figura 4-2, este diseño detallado es similar al UML representado en la Figura 3-4.

Constrained Device Application—integrated sensing and actuation app UML
Figura 4-3. Dispositivo restringido Aplicación de detección y actuación integrada UML

Veamos en los requisitos y la aplicación del Módulo de Laboratorio 04.

Emulación de sensores

Recuerda las dos funciones clave que realiza BaseSensorSimTask: crea una instancia SensorData , que utiliza para almacenar los últimos datos de simulación del sensor, y proporciona una interfaz pública para generar una nueva instancia y acceder a sus datos.

En PIOT-CDA-04-001, crearás tres nuevas tareas de emulador de sensor, cada una de las cuales derivará de BaseSensorSimTask, igual que hicieron las tareas de simulador de sensor en el Capítulo 3. La diferencia estará en el constructor y en el método generateTelemetry(). Estas tareas de sensor residen en el paquete ./programmingtheiot/cda/emulated. He aquí un resumen de las acciones implicadas:

  • Crea (o edita) HumiditySensorEmulatorTask, PressureSensorEmulatorTask, y TemperatureSensorEmulatorTask.

  • Inicializa la instancia de la clase SenseHAT utilizando la biblioteca pisense.

  • Actualiza generateTelemetry() para recuperar datos del emulador Sense HAT.

Vamos a desgranar estas acciones paso a paso. Me centraré en TemperatureSensorEmulatorTask, ya que las demás serán muy parecidas.

Al igual que con los demás módulos de Python que hayas editado o creado, tendrás que crear la clase y añadir las declaraciones import pertinentes. Si utilizas la base de código python-components, esto ya está hecho por ti y tiene un aspecto similar al siguiente:

from programmingtheiot.data.SensorData import SensorData

import programmingtheiot.common.ConfigConst as ConfigConst

from programmingtheiot.common.ConfigUtil import ConfigUtil
from programmingtheiot.cda.sim.BaseSensorSimTask import BaseSensorSimTask
from programmingtheiot.cda.sim.SensorDataGenerator import SensorDataGenerator

La declaración de clase de TemperatureSensorEmulatorTask tendrá este aspecto:

class TemperatureSensorEmulatorTask(BaseSensorSimTask):

Cuando crees el constructor, observa su similitud con las tareas del simulador que ya has escrito. Las principales diferencias son la lógica de construcción y la lógica de instancia de la clase SenseHAT de la biblioteca pisense:

def __init__(self):
  super(TemperatureSensorEmulatorTask, self).__init__( \
    name = ConfigConst.TEMP_SENSOR_NAME, \
    typeID = ConfigConst.TEMP_SENSOR_TYPE)
  
  self.sh = SenseHAT(emulate = True)
Nota

Si decides utilizar una combinación de placa HAT Sense real y ordenador monoplaca Raspberry Pi, puedes añadir otro parámetro a tu PiotConfig.props dentro de la sección ConstrainedDevice indicando que el hardware está (o no está) presente. Si se va a utilizar hardware real en tu CDA, la clase SenseHAT debería inicializarse con emulate = False en su lugar. Si quieres cambiar dinámicamente entre datos simulados, datos emulados y datos reales (utilizando hardware), considera la posibilidad de utilizar un valor entero para reflejar este comportamiento de inicialización en tu archivo de configuración.

Una vez hecho esto, pasemos a la función generateTelemetry(). Aquí tienes un ejemplo que puedes utilizar (de nuevo, para la clase TemperatureSensorEmulatorTask ):

def generateTelemetry(self) -> SensorData:
  sensorData = \
    SensorData(name = self.getName(), typeID = self.getTypeID())
  
  sensorVal = self.sh.environ.temperature
  
  sensorData.setValue(sensorVal)
  self.latestSensorData = sensorData
          
  return sensorData

En realidad es un poco más sencillo sin los datos simulados (o aleatorios), ¿no?

Ahora puedes pasar a las otras dos tareas: HumiditySensorEmulatorTask y PressureSensorEmulatorTask. Sólo asegúrate de utilizar la llamada correcta a la función SenseHAT para recuperar el valor apropiado para cada una.

Es hora de los emuladores de accionamiento.

Actuadores emuladores

¿Recuerdas la clase base BaseActuatorSimTask del capítulo 3? Al igual que BaseSensorSimTask, realiza dos funciones principales: en este caso, abstrae las funciones "activar" y "desactivar" de un actuador, utilizando el método público updateActuator(ActuatorData). Esto será muy útil para las tareas del actuador emulado, así que las crearemos derivando de BaseActuatorSimTask.

Ahora viene la parte complicada. ¿Qué tipos de eventos de actuación quieres desencadenar? Recuerda nuestro planteamiento del problema del Capítulo 1:

Quiero entender el ambiente de mi casa, cómo cambia con el tiempo, y hacer ajustes para mejorar el confort ahorrando dinero.

Una forma de interpretar el "confort" es a través del control de la temperatura y la humedad (que desempeña un papel en la forma en que los seres humanos sienten la temperatura y contribuye al confort y la salud generales de un ambiente interior). Utilicemos ambas mediciones como posibles desencadenantes de actuación y denominemos a dos de las tareas del actuador HvacEmulatorTask y HumidifierEmulatorTask. El HVAC se utilizará para controlar la temperatura, y el humidificador para ajustar la humedad relativa.

Nota

La convención para nombrar las tareas de los actuadores prescinde de la palabra "Actuador", ya que creo que está bastante claro que estos dispositivos se encenderán o apagarán. Un sistema del mundo real también puede recoger lecturas de los sensores de estos dispositivos. Para simplificar, nuestros dispositivos virtuales se limitarán a aceptar órdenes de actuación.

Como también utilizarás la matriz de LED en el emulador, podría ser útil dedicar un actuador a iluminar la pantalla, por lo que PIOT-CDA-04-002 también especifica un LedDisplayEmulatorTask.

Repasemos las acciones enumeradas en PIOT-CDA-04-002:

  • Crea (o edita) HvacEmulatorTask, HumidifierEmulatorTask, y LedDisplayEmulatorTask.

  • Inicializa la instancia SenseHAT utilizando la biblioteca pisense.

  • Actualiza _handleActuation() para escribir los datos apropiados en la pantalla LED utilizando la instancia SenseHAT.

Considera el último punto. Como el emulador no emula realmente un sistema de climatización o un humidificador, sólo utilizaremos su pantalla LED para notificar al usuario que se está produciendo un evento de actuación, encendiendo o apagando algo. Sin duda puedes ser creativo con la implementación, pero por ahora, simplemente desplazaremos un mensaje por la pantalla.

Aquí tienes un ejemplo de HvacEmulatorTask, incluyendo las importaciones, la declaración de clase y la inicialización del constructor. Se parece mucho a TemperatureSensorEmulatorTask, ¿verdad?

import logging

from time import sleep

import programmingtheiot.common.ConfigConst as ConfigConst

from programmingtheiot.common.ConfigUtil import ConfigUtil
from programmingtheiot.cda.sim.BaseActuatorSimTask import BaseActuatorSimTask

from pisense import SenseHAT

class HvacEmulatorTask(BaseActuatorSimTask):
  def __init__(self):
    super(HvacEmulatorTask, self).__init__( \
      name = ConfigConst.HVAC_ACTUATOR_NAME, \
      typeID = ConfigConst.HVAC_ACTUATOR_TYPE, \
      simpleName = "HVAC")
  
  self.sh = SenseHAT(emulate = True)

Recuerda que la clase base, BaseActuatorSimTask, define dos métodos privados llamados _activateActuator() y _deactivateActuator(). Ambos serán llamados automáticamente en función de la orden que se envíe al método updateActuator() definido en la clase base. De acuerdo, esto no es lo ideal para todas las situaciones, pero se ajustará a nuestras necesidades.

Aquí tienes un ejemplo de aplicación de cada uno de estos métodos:

def _activateActuator(self, \
  val: float = ConfigConst.DEFAULT_VAL, \
  stateData: str = None) -> int:
  
  if self.sh.screen:
    msg = self.getSimpleName() + ' ON: ' + \
    str(val) + 'C'

    self.sh.screen.scroll_text(msg)
    return 0
  else:
    logging.warning('No LED screen instance available.')
    return -1

La implementación se parece bastante a la del actuador simulado que registraba un mensaje en la consola, ¿no?

La funcionalidad de desactivación es similar. Aquí tienes un vistazo a la implementación:

def _deactivateActuator(self, \
  val: float = ConfigConst.DEFAULT_VAL, \
  stateData: str = None) -> int:
  
  if self.sh.screen:
    msg = self.getSimpleName() + ' OFF'
    self.sh.screen.scroll_text(msg)
    
    sleep(5)
    
    # optionally, clear the screen when done scrolling
    self.sh.screen.clear()
    return 0
  else:
    logging.warning('No LED screen instance available.')
    return -1

La gran diferencia aquí es el retardo de espera. Aunque es completamente opcional, lo incluí aquí para dar tiempo al mensaje a desplazarse; sin embargo, interfiere en el tiempo de la llamada, ya que bloqueará y retrasará otros procesos CDA a menos que se ejecute dentro de un hilo.

Consejo

Si decides adoptar un enfoque multihilo como parte de tu diseño, considera la posibilidad de utilizar una cola para almacenar mensajes en caso de que se envíen más rápido de lo que la pantalla puede procesarlos. Esto plantea otro reto interesante: ¿cómo puedes saber la velocidad a la que hay que sondear un sensor emulado (o real), o cuánto tardará un evento de actuación emulado (o real)?

La respuesta breve es que necesitas conocer la especificación de hardware del sensor o actuador, las restricciones de temporización del dispositivo (o emulador), la frecuencia con que se invocará y cómo factorizar estos tiempos en tu diseño CDA. Puede ser un verdadero problema si no se gestiona adecuadamente. Está fuera de nuestro alcance aquí, pero te animo a que lo tengas en cuenta cuando diseñes tu solución de perímetro.

¿Cuál sería entonces el mejor camino a seguir para la funcionalidad de desactivación? Si el mensaje debe tener tiempo para desplazarse y sabes cuánto tardará cada carácter (supongamos que es aproximadamente la mitad de un segundo, o 500 milisegundos), sólo tienes que calcular la longitud de la cadena a mostrar y luego multiplicarla por el número de segundos (o segundos parciales). Si, por ejemplo, la cadena tiene 20 caracteres, y cada carácter tarda 500 milisegundos en mostrarse y desplazarse, tendrías que esperar aproximadamente 10 segundos para que cada carácter se desplace.

Tal vez una opción mejor sea mantener una representación gráfica 8x8 con iconos y/o colores que indiquen el estado y dejarla iluminada mientras esté vigente el estado dado. Ah, sí, eso sería un excelente ejercicio complementario, ¿no? Pero divago. Ahora que conoces el reto y un camino plausible para abordarlo, ¡puedes elegir un enfoque de implementación diferente!

Ya casi has terminado. Sólo hay dos pasos más: integración con SensorAdapterManager e integración con ActuatorAdapterManager.

Conexión de Sensores Emulados con el Gestor de Adaptadores de Sensores

Este ejercicio, definido en PIOT-CDA-04-003, actualiza SensorAdapterManager introduciendo las tareas emuladoras recién creadas dentro de la inicialización del constructor. El enfoque del diseño te permitirá cambiar fácilmente entre tareas de sensor emuladas y tareas de sensor simuladas cambiando la bandera enableEmulator en PiotConfig.props de True a False.

Las acciones clave son las siguientes:

  • Añade soporte para la bandera enableEmulator de PiotConfig.props.

  • Añade las tareas de los sensores del emulador para temperatura, presión y humedad (y cualquier otra que desees incluir).

  • Utiliza la bandera enableEmulator para cambiar entre utilizar tareas de sensor simuladas y utilizar tareas de sensor emuladas.

Como ya conoces el constructor de SensorAdapterManager, aquí tienes el código que sigue inmediatamente después de declarar self.dataMsgListener = None (que debería ser la última línea de código en la inicialización del constructor):

if not self.useEmulator:
  self.dataGenerator = SensorDataGenerator()
  
  tempFloor = \
    configUtil.getFloat( \
      section = ConfigConst.CONSTRAINED_DEVICE, \
      key = ConfigConst.TEMP_SIM_FLOOR_KEY, \
      defaultVal = SensorDataGenerator.LOW_NORMAL_INDOOR_TEMP)

  tempCeiling = \
    configUtil.getFloat( \
      section = ConfigConst.CONSTRAINED_DEVICE, \
      key = ConfigConst.TEMP_SIM_CEILING_KEY, \
      defaultVal = SensorDataGenerator.HI_NORMAL_INDOOR_TEMP)
  
  tempData = \
    self.dataGenerator.generateDailyIndoorTemperatureDataSet( \
      minValue = tempFloor, \
      maxValue = tempCeiling, \
      useSeconds = False)
               
  self.tempAdapter = \
    TemperatureSensorSimTask(dataSet = tempData)
  
  # TODO: add other sensor simulator tasks

else:
  # load the Temperature emulator
  tempModule = \
    import_module( \
      'programmingtheiot.cda.emulated.TemperatureSensorEmulatorTask', \
      'TemperatureSensorEmulatorTask')

  teClazz = \
    getattr(tempModule, 'TemperatureSensorEmulatorTask')

  self.tempAdapter = teClazz()
    
  # TODO: add other sensor emulator tasks

Si la bandera self.useEmulator es False, se utilizará la funcionalidad del sensor simulado. El resto del código de esta sección es el mismo código que implementaste en el Capítulo 3. Pero, ¿qué pasa con la cláusula else? Vamos a desglosarla.

La primera línea después de la cláusula else es la siguiente:

  tempModule = \
    import_module( \
      'programmingtheiot.cda.emulated.TemperatureSensorEmulatorTask', \
      'TemperatureSensorEmulatorTask')

Si llevas tiempo programando en Python, esto te resultará familiar. El código indica al intérprete de Python que cargue dinámicamente el módulo TemperatureSensorEmulatorTask (y la clase del mismo nombre) utilizando import_module. Esto simplemente envuelve el built-in __import__ con una interfaz ligeramente más amigable para realizar la carga del módulo (de nuevo, sólo si self.useEmulator es True).

Consejo

Si prefieres utilizar la función __import__, puedes sustituir la llamada a import_module() por lo siguiente:

tempModule = \
  __import__( \

    'programmingtheiot.cda.emulated.TemperatureSens
orEmulatorTask', \

    fromlist =
['TemperatureSensorEmulatorTask']).

¿Esta capacidad de carga dinámica de módulos y clases es estrictamente necesaria a efectos de este capítulo? No, no lo es. Sin embargo, es una herramienta que puedes utilizar para futuras integraciones. Sirve para dos propósitos, ninguno de los cuales es necesario en este momento:

  1. Establece un patrón para cargar dinámicamente módulos que pueden no existir utilizando un simple indicador del archivo de configuración. Un ejercicio interesante, pero no crítico. Todavía.

  2. Con este patrón, puedes implementar código específico de hardware dentro del paquete existente `programmingtheiot.cda.embedded` python-components. Esto puede ser útil si decides abordar algunos de los ejercicios opcionales del Módulo 04 del Laboratorio.

Puedes seguir este mismo patrón para cargar dinámicamente las tareas de humedad y presión, HumiditySensorEmulatorTask y PressureSensorEmulatorTask, respectivamente. Asegúrate de añadir esos componentes a tu código antes de ejecutar la prueba de integración manual.

Probemos las cosas y asegurémonos de que puedes alternar entre detección simulada y detección emulada. Echa un vistazo a la prueba que hay al final de la tarjeta de requisitos: verás que las instrucciones son un poco más complicadas que en las pruebas anteriores. He aquí un resumen:

  • Asegúrate de que la bandera enableEmulator en PiotConfig.props está establecida en True y, a continuación, inicia el emulador Sense-Emu. Desde la línea de comandos, deberías poder escribir simplemente sense_emu_gui.

  • Ejecuta SensorAdapterManagerTest dentro de la ruta python-componentes ./src/test/python/programaciontheiot/part02/integracion/sistema.

Si estás ejecutando el emulador Sense-Emu en WSL y tu IDE en Windows, puede que descubras que las pruebas de integración manuales para SensorAdapterManagerTest generan un error. Esto se debe probablemente a que tu IDE no es capaz de comunicarse con el servidor X11. La solución fácil (y rápida) es simplemente ejecutar tu prueba desde la línea de comandos. Puedes hacerlo navegando hasta la ruta que contiene SensorAdapterManagerTest y ejecutándola mediante el siguiente comando dentro de tu virtualenv:

python -m unittest SensorAdapterManagerTest
Nota

Si has decidido configurar tu entorno Python sin utilizar un virtualenv, asegúrate de que estás utilizando el ejecutable del intérprete Python adecuado (como Python 3). Además, asegúrate de que tu PYTHONPATH está configurado en tu entorno. Deberá incluir las rutas . /src/main/python y ./src/test/python.

La salida debería incluir varias secuencias de mensajes similares a las siguientes (dependiendo de cuánto tiempo ejecutes tus pruebas):

2021-01-01 15:06:39,776:SensorAdapterManagerTest:INFO:Testing SensorAdapterManager
class...
.
.
2021-01-01 15:06:45,126:SensorAdapterManager:INFO:Generated humidity data:
name=HumiditySensor,typeID=1,timeStamp=2021-01-
01T20:06:45.125842+00:00,statusCode=0,hasError=False,locationID=constraineddevice00
1,elevation=0.0,latitude=0.0,longitude=0.0,value=0.0
2021-01-01 15:06:45,128:SensorAdapterManager:INFO:Generated pressure data:
name=PressureSensor,typeID=2,timeStamp=2021-01-
01T20:06:45.125956+00:00,statusCode=0,hasError=False,locationID=constraineddevice00
1,elevation=0.0,latitude=0.0,longitude=0.0,value=848.703369140625
2021-01-01 15:06:45,129:SensorAdapterManager:INFO:Generated temp data:
name=TempSensor,typeID=3,timeStamp=2021-01-
01T20:06:45.125996+00:00,statusCode=0,hasError=False,locationID=constraineddevice00
1,elevation=0.0,latitude=0.0,longitude=0.0,value=24.984375
.
.
2021-01-01 15:07:20,139:base:INFO:Job "SensorAdapterManager.handleTelemetry
(trigger: interval[0:00:05], next run at: 2021-01-01 15:07:25 EST)" executed
successfully
.
.

Si este resultado del registro coincide razonablemente bien con el tuyo, ¡genial! Es hora de pasar a los emuladores de actuadores.

Conexión de Actuadores Emulados con el Gestor de Adaptadores de Actuadores

El último ejercicio formal de este capítulo, definido en PIOT-CDA-04-004, sigue el mismo patrón que el anterior para ActuatorAdapterManager:

  • Añade soporte para la bandera enableEmulator de PiotConfig.props.

  • Añade las tareas del actuador emulador para el HVAC, el humidificador y la matriz LED.

  • Utiliza la bandera enableEmulator para cambiar entre utilizar tareas de actuador simuladas y utilizar tareas de actuador emuladas.

Ten en cuenta que el patrón de construcción de ActuatorAdapterManager sólo cambiará para soportar el cambio emulador/simulador. Inmediatamente después de declarar self.dataMsgListener = None (que, de nuevo, debería ser la última línea de código en la inicialización del constructor), añade lo siguiente:

if not self.useEmulator:
  # create the humidifier actuator
  self.humidifierActuator = HumidifierActuatorSimTask()

  # create the HVAC actuator
  self.hvacActuator = HvacActuatorSimTask()
else:
  # load the HVAC emulator
  hvacModule = \
    import_module( \
      'programmingtheiot.cda.emulated.HvacEmulatorTask', \
      'HvacEmulatorTask')
  
  hveClazz = \
    getattr(hvacModule, 'HvacEmulatorTask')
  
  self.hvacActuator = hveClazz()
  
  # TODO: add other actuator tasks

Aquí no hay nada nuevo, ¿verdad? Esta vez, estás cargando dinámicamente el HvacEmulatorTask pero utilizando la misma lógica que para el TemperatureSensorEmulatorTask.

Asegúrate de añadir HumidifierEmulatorTask y LedDisplayEmulatorTask antes de pasar a las pruebas de integración manuales. Aquí se aplican las mismas restricciones que para SensorAdapterManager cuando se utiliza el emulador Sense-Emu: asegúrate de que la bandera enable​Emulator es True en PiotConfig.props e inicia el emulador.

Como en el caso de SensorAdapterManagerTest, puedes ejecutar ActuatorAdapterManagerTest desde la ruta de componentes python ./src/test/python/programmingtheiot/part02/integration/system/:

python -m unittest ActuatorAdapterManagerTest

Para esta prueba, asegúrate de monitorizar también la GUI de Sense-Emu y observa la pantalla LED junto con la salida del registro.

La Figura 4-4 muestra una captura de pantalla de ejemplo en la que aparece la primera letra del mensaje LED "ENCENDIDO".

Sending an “ON” message to the Sense-Emu LED matrix
Figura 4-4. Envío de un mensaje "ON" a la matriz de LEDs Sense-Emu

La Figura 4-4 muestra una pequeña parte del mensaje de texto desplazable que indica que el HVAC se está encendiendo "ON" (se muestra la "O"). Un ejercicio a considerar es activar dos eventos de actuación cuando haya que ajustar la temperatura: uno para el emulador HVAC (que técnicamente sólo puede registrar un mensaje) y otro para la matriz de LED, donde puedes proporcionar una visualización más creativa del HVAC y el humidificador dentro de la matriz de 8×8.

En cuanto a los mensajes de registro, tendrán un aspecto similar al patrón expresado aquí:

2021-01-01 16:58:17,797:ActuatorAdapterManagerTest:INFO:Testing ActuatorAdapterManager
class...
.
.
.
2021-01-01 16:58:25,909:ActuatorAdapterManager:INFO:Actuator command received for
location ID constraineddevice001. Processing...
2021-01-01 16:58:25,910:BaseActuatorSimTask:INFO:Deactivating actuator...
2021-01-01 16:58:36,729:DefaultDataMessageListener:INFO:Actuator Command: 0
2021-01-01 16:58:36,736:ActuatorAdapterManager:INFO:Actuator command received for
location ID constraineddevice001. Processing...
2021-01-01 16:58:36,745:BaseActuatorSimTask:INFO:Activating actuator...
2021-01-01 16:58:42,325:DefaultDataMessageListener:INFO:Actuator Command: 1
2021-01-01 16:58:42,326:ActuatorAdapterManager:INFO:Actuator command received for
location ID constraineddevice001. Processing...
2021-01-01 16:58:42,327:BaseActuatorSimTask:INFO:Deactivating actuator...
2021-01-01 16:58:51,120:DefaultDataMessageListener:INFO:Actuator Command: 0
.
.
.

Si tus pruebas indican éxito, es hora de celebrarlo. Ya tienes en funcionamiento no sólo la funcionalidad CDA simulada, ¡sino también la emulación de sensores y actuadores! En el próximo capítulo veremos cómo gestionar los datos que ahora estás generando para que puedan ser recogidos, transmitidos (eventualmente) e interpretados.

Ejercicios adicionales

La interfaz gráfica de usuario del emulador Sense-Emu te permite ajustar los valores mediante los controles deslizantes y el joystick. Ésta puede ser una herramienta especialmente útil para cambiar manualmente los valores dentro de tu entorno de sensores emulados para probar varios eventos de cruce de umbrales.

Gestión de umbrales

Crea otro adaptador emulador que interprete los controles del joystick de la GUI Sense-Emu y ajuste el suelo (botón abajo) y el techo (botón arriba) de tu temperatura o de tu valor de cruce del umbral de humedad.

Con este en su sitio, comprueba si puedes ejercitar la función de gestión de histéresis que implementaste en el ejercicio adicional del Capítulo 3 utilizando estos nuevos cruces de umbral y los valores de datos simulados generados a partir de SensorDataGenerator (o tu propia versión personalizada).

Conclusión

En este capítulo has aprendido sobre la emulación de hardware y cómo integrar el emulador Sense HAT en tu CDA. También conociste algunos de los retos asociados a la activación de eventos de actuación, y cómo la lógica para manejar dichos eventos no siempre es tan sencilla como enviar un comando ON u OFF. En cuanto al diseño, aprendiste más sobre la modularidad del diseño del CDA y cómo puede ayudarte a superar algunos de estos retos.

Ahora estás preparado para sumergirte en el último capítulo de la Parte II -el Capítulo5-, dondeaprenderás sobre la transformación de datos, que te ayudará a integrar finalmente tu CDA y GDA.

1 El Sense HAT es una placa que se puede conectar a varios módulos de ordenador monoplaca Raspberry Pi a través de su interfaz GPIO de 40 patillas. Puedes obtener más información sobre el Sense HAT en el sitio web de Raspberry Pi.

2 Si te interesa utilizar la Raspberry Pi, consulta Raspberry Pi Cookbook, Third Edition, de Simon Monk (O'Reilly).

3 Puedes leer más sobre la placa Sense HAT en Internet. Si te interesan otras funciones del emulador, puedes consultar la documentación en línea y echar un vistazo a las demostraciones integradas que se encuentran en el menú Archivo → "Abrir ejemplo" de la interfaz gráfica de usuario del emulador.

Get Programar el Internet de las Cosas now with the O’Reilly learning platform.

O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.