scieee AI-readable full text Open interactive document viewer

Diseño y arquitectura de una plataforma para recolección de datos y control de sistemas integrados

Fernández Barrera, Diego

Full text

Proyecto Fin de Carrera Ingeniería de Telecomunicación Formato de Publicación de la Escuela Técnica Superior de Ingeniería Autor: F. Javier Payán Somet Tutor: Juan José Murillo Fuentes Dep. Teoría de la Señal y Comunicaciones Escuela Técnica Superior de Ingeniería Universidad de Sevilla Sevilla, 2013 Trabajo Fin de Grado Ingeniería de Telecomunicación Diseño y arquitectura de una plataforma para recolección de datos y control de sistemas integrados Autor: Diego Fernández Barrera Tutor: Pablo Nebrera Herrera Departamento de Ingeniería Telemática Escuela Técnica Superior de Ingeniería Universidad de Sevilla Sevilla, 2016 Trabajo Fin de Grado Ingeniería de Telecomunicación Diseño y arquitectura de una plataforma para recolección de datos y control de sistemas integrados Autor: Diego Fernández Barrera Tutor: Pablo Nebrera Herrera Profesor asociado Departamento de Ingeniería Telemática Escuela Técnica Superior de Ingeniería Universidad de Sevilla Sevilla, 2016 Trabajo Fin de Grado: Diseño y arquitectura de una plataforma para recolección de datos y control de sistemas integrados Autor: Diego Fernández Barrera Tutor: Pablo Nebrera Herrera El tribunal nombrado para juzgar el trabajo arriba indicado, compuesto por los siguientes profesores: Presidente: Vocal/es: Secretario: acuerdan otorgarle la calificación de: El Secretario del Tribunal Fecha: Índice 1. Introducción 1 1.1. Motivación 1 1.2. Escenario 1 1.2.1. Usuario 2 1.2.2. Dispositivos integrados 2 1.2.3. Lógica de negocio 2 2. Arquitectura de la plataforma 3 2.1. Funcionalidad 3 2.1.1. Bidireccionalidad 3 Bidireccionalidad en la interfaz con los servicios del cliente 3 Bidireccionalidad en la interfaz con los dispositivos 4 2.1.2. Tiempo real 4 2.1.3. Persistencia 4 2.1.4. Tenencia múltiple 5 2.2. Diseño 5 2.3. Componentes 6 2.3.1. API 6 2.3.2. Broker 7 2.3.3. Bases de datos 7 2.3.4. Backend de autorización 7 2.3.5. Procesador de flujos 7 3. Protocolo MQTT, AMQP y STOMP 9 3.1. MQTT 9 3.1.1. El patrón publicador/suscriptor 9 3.1.2. Escalabilidad 10 3.1.3. Filtrado de mensajes 10 3.1.4. Diferencias con un sitema de colas 10 3.2. STOMP 11 I II Índice 3.3. AMQP 11 3.3.1. Brokers 11 3.3.2. El modelo AQMP 11 3.3.3. Exchanges 12 Direct Exchange 12 Fanout Exchange 13 Topic Exchange 14 Header Exchange 14 3.3.4. Colas 14 Colas durables 14 3.3.5. Bindings 14 3.3.6. Mensages 14 3.4. STOMP vs. MQTT vs. AMQP 15 4. Broker y autorización 17 4.1. RabbitMQ 17 4.1.1. Configuración 18 4.2. Backend de autorización 18 5. API 21 5.1. Funcionamiento 21 5.2. Registro de usuario 21 5.3. Obtención de Fleet Key 22 5.4. Registro de dispositivos 23 5.5. Desarrollo 24 6. Bases de datos 29 6.1. MongoDB 29 6.2. RethinkDB 29 7. Dispositivos y Módulos 31 7.1. Dispositivos 31 7.1.1. Flotas 31 7.2. Módulos 31 7.2.1. Caso 1 32 7.2.2. Caso 2 32 7.3. Conexión al broker 32 8. Ejemplo de uso 33 8.1. Dispositivos 33 8.2. Servicios 34 Alertas 34 Lecturas 35 8.3. Envío de comandos 37 Índice III Apéndice A.Sobre el código 39 Índice de Figuras 41 Índice de Códigos 43 Bibliografía 45 6Capítulo 2. Arquitectura de la plataforma Figura 2.2 Estructura interna de la plataforma. Componentes En la Figura 3.2 se pueden ver los elementos de la plataforma, situados entre los dispositivos y la lógica de negocio del usuario. Tenemos los siguientes componentes: API Es el componente que permite a los usuarios comunicarse con la plataforma. Tanto los dispositivos como los servicios propios del usuario usarán la API. Es tarea de la API el registro de los dispositivos, el registro de 2.3 Componentes 7 usuarios y otras tareas de gestión así como permite hacer consultas para obtener datos almacenados en la plataforma. La API es una aplicación que ha sido desarrollada como parte de este trabajo. Se ha utilizado Node.js para su implementación. Broker Es el componente central de la arquitectura. Por él pasan los mensajes enviados por los otros elementos. Permite balanceo de carga entre múltiples instancias, persistencia, sincronización, etc. Como broker se ha usado RabbitMQ ya que es uno de los más extendidos por su capacidad para escalar, su robustez y su extensibilidad. Bases de datos La persistencia del sistema. Múltiples bases de datos se encargan de almacenar los datos recibidos que deben ser persistidos. También será necesaria almacenar información de usarios y dispositivos registrados. Para almacenamiento de credenciales se usará una base de datos MongoDB por ser una de las más populares. Para persitir los datos del usuario que llegan al sistema se usa RethinkDB . La característica más importante de esta base de datos es que, a diferencia de las bases de datos convencionales, las aplicaciones no tienen que consultarla de forma periódica para detectar nuevos datos o modificaciones en éstos, sino que es la propia base de datos la que notifica a las aplicaciones. Backend de autorización Debido a que es necesario controlar el acceso a la plataforma a los dispositivos y a las aplicaciones, se requiere un control de acceso. En este escenario, el control de acceso debe realizarlo el broker, en este caso en particular lo hará RabbitMQ . Gracias a un plugin para éste, es posible delegar la autenticación en una aplicación externa, de forma que el broker realizará una petición HTPP a una aplicación cuando un cliente se intente conectar, siendo esta aplicación quien decida si se permite o no. El backend de autorización es la aplicacion encargada de tomar las decisiones. Tendrá conectividad con la base de datos donde se encuentran las credenciales y verificará que las conexiones son legítimas. Esta aplicación también ha sido desarrollada como parte del trabajo, una vez más usando Node.js como plataforma. Procesador de flujos Este componente obtiene los datos en crudo de la cola de mensajes, los trata y los almacena en la base de datos. Puede ser interesante añadir algunos metadatos a la información recibida antes de almacenarse. Al mismo tiempo, la base de datos notificará a este componente cuando termine de persistir el dato y este componente lo enviará a una cola del broker donde posteriormente podrá leerlo una aplicación que se conecte a la plataforma. También se ha desarrollado como parte del trabajo en Node.js . Protocolo MQTT, AMQP y STOMP MQTT MQTT [ 2 ] es un protocolo cliente/servidor que permite roles de publicador/suscriptor. Es ligero, abierto, simple y diseñado para ser fácil de implementar en el cliente. Estas características lo hacen ideal para su uso en múltiples situaciones, como por ejemplo, entornos donde la memoria y el ancho de banda son limitados, como la comunicación M2M (Machine to machine) o para dispositivos en el Internet de las Cosas. Es un protocolo binario ligero que, en comparación con HTTP , tiene una mínima sobrecarga en cuanto a cabeceras para hacer más eficiente el tampaño de los paquetes. MQTT es muy fácil de implementar en el cliente. Esto encaja perfectamente en sistemas integrados con recursos limitados, de hecho, esto es uno de los objetivos que se buscaban cuando MQTT se creó. El patrón publicador/suscriptor Figura 3.1 Patrón publicador/suscriptor. El patrón publicador/suscriptor es una alternativa al sistema tradicional de cliente/servidor, donde un cliente se comunica directamente con un destino. Sin embargo, el patrón pub/sub desacopla al cliente que están enviando un mensaje (publicador) de otro cliente o más clientes que reciben mensajes (suscriptor). Esto quiere decir que publicador y suscriptor no son conscientes de la existencia del otro. Existe un tercer componente llamado broker que es conocido tanto por el publicador como el suscriptor que es el encargado de filtrar los mensajes que llegan y distribuirlos correctamente. 9 10 Capítulo 3. Protocolo MQTT, AMQP y STOMP As already mentioned the main aspect in pub/sub is the decoupling of publisher and receiver, which can be differentiated in more dimensions: Como ya hemos mencionado, el pricipal aspecto del patrón publicador/suscriptor es el desacople en las siguientes dimensiones: •Espacio desacoplado: Publicador y suscriptor no tiene porqué saber el uno del otro (IP, puerto, etc.). •Tiempo desacoplado: Publicador y suscriptor no tienen porqué estar en ejecución al mismo tiempo. • Sincronización desacoplada: Las operaciones de los dos componentes no se detienen durante envío o recepción. En resumen, el patrón publicador/suscriptor desacopla el publicador o receptor de un mensaje y a través de los filtros para los mensajes se puede elegir qué clientes reciben qué mensajes. Escalabilidad El patrón publicador/suscriptor ofrece mejor escalabilidad que el patrón clásico de cliente/servidor. Esto se debe a que las operaciones en el broker pueden ser altamente paralelizadas. También es común el cacheo de mensajes y el enrutado inteligente. Sin embargo, es un reto el escalar a millones de conexiones, esto puede conseguirse mediante el uso de clústeres de brokers para distribuir la carga entre múltiples servidores. Filtrado de mensajes El filtrado de mensajes es el encargado de que sólamente los mensajes sean recibidos por los clientes que deben. Tenemos varias opciones de filtrado de mensajes: • Filtrado basado en topic : El topic puede ser parte de cada mensaje. Cada cliente recibe sólamente mensajes del topic en el que está interesado. Los topics generalmente son cadenas de texto organizadas de forma jerárquica, por lo que se puede filtrar en función de una expresión. • Filtrado basado en contenido: El filtrado se basa en el contenido específico del mensaje. Una gran desventaja de este método es que el contenido del mensaje debe ser conocido por el broker, cosa que no siempre ocurre, por ejemplo, cuando el contenido va cifrado. • Filtrado por tipo: En lenguajes orientados a objetos es típico filtrar en base a un tipo o clase del mensaje o evento. En este caso el suscriptor podría recibir todos los mensajes de un tipo o subtipo. En el caso de MQTT se utiliza el filtrado por topic así que cada mensaje contiene un topic , el cual es procesado por el broker para entregarlo a los clientes que se han suscrito a él. MQTT tiene múltiples niveles de calidad de servicio (QoS). Se puede conseguir fácilmente que un mensaje sea entregado del cliente al broker o del broker al cliente. Sin embargo, existe la posibilidad de que nadie se suscriba a un topic en particular, en este caso depende el broker cómo se debe manejar la situación. Diferencias con un sitema de colas Existe confusión debido a su nombre en cuanto a sí es un protocolo de colas. Su nombre proviene de MQseries, un producto de IBM y no de Message Queue. Independientemente del nombre, existen diferencias entre MQTT y un sistema de colas: • Un sistema de colas almacena un mensaje hasta que se consume: En un sistema clásico los mensajes se almacenan hasta que son tomados por un cliente (consumidor), esto en MQTT no ocurre ya que puede haber cero clientes suscritos a un topic y el mensaje es descartado. 3.2 STOMP 11 • Un mensaje sólo es consumido por un cliente: Otra gran diferencia es el hecho de que en un sistema tradicional de cola de mensajes, los mensajes son consumidos por un único consumidor así la carga se puede distribuir entre múltiples procesos. En MQTT esto suele ser al contrario, todos los suscriptores reciben el mensaje si se han suscrito al topic . • Las colas tienen nombres y deben crearse de forma explícita: Una cola es menos flexible que un topic, antes de usar una cola debe declarase explícitamente y sólo entonces se pueden consumir los mensajes. En MQTT los topics son extremadamente flexibles y son creados en el momento. STOMP STOMP (Simple Text Orientated Messaging Protocol), a diferencia de MQTT , está orientado a texto, como HTTP . El protocolo permite a clientes conectarse a un broker para que se puedan comunicar fácilmente entre diferentes tipos de aplicaciones y plataformas. En STOMP también se sigue un modelo publicador/suscriptor. Se centra en tener un diseño simple y minimalista y que sea muy fácil de implementar en cualquier lenguaje. Al funcionar en modo texto, hacer un cliente puede ser tan sencillo como abrir una sesión Telnet para loguearse en cualquier broker de STOMP e interaccionar con él. Se dice que es posible crear un cliente de STOMP en un par de horas. AMQP AMQP [ 4 ] (Advanced Message Queuing Protocol) es un protocolo de mensages que permite a aplicaciones de clientes comunicarse con un broker. Brokers Los brokers de mensajes reciben los mensajes de los publicadores (aplicaciones que los publican, también conocidos como productores) y los enrutan hacia los consumidores (aplicaciones que los procesan). Ya que es un protocolo de red, los publicadores, consumidores y el broker pueden residir en diferentes máquinas. El modelo AQMP El modelo de AMQP 0-9-1 tiene la siguiente visón de lo que ocurre: Los mensajes se publican en exchanges, que se podrían ver como un buzón o una oficina postal. Los exchanges, al recibir mensajes, los distribuyen a las colas (queues) siguiendo unas reglas llamadas bindings . A continuación, el broker entrega los mensajes a los consumidores que están suscritos a las colas, o los propios consumidores consultan la cola y leen los mensajes. Cuando se publica un mensaje, se pueden especificar diferentes atributos (metadata). Alguno de estos atributos pueden ser usados por el broker, sin embargo, el cuerpo del mensajes es completamente opaco para el broker y sólamente será usado por la aplicación que recibirá el mensaje. Las redes pueden tener problemas y las aplicaciones puede fallas al procesar los mensajes, por eso mismo, el modelo AMQP hace uso de ACKs . Cuando un mensaje se entrega a un consumidor, éste debe notificar al broker que ha procesado el mensaje, ya sea de forma automática o cuando el desarrollador de la aplicación lo decida. El broker sólamente eliminará el mensaje de la cola cuando éste haya sido confirmado. 12 Capítulo 3. Protocolo MQTT, AMQP y STOMP Figura 3.2 Ejemplo de enrutamiento en AMQP. En algunas situaciones, por ejemplo, cuando un mensaje no puede ser enrutado, el mensaje puede ser devuelto al productor, descartado o, si el broker lo implementa, enviado a una cola especial llamada dead letter queue. Los consumidores pueden elegir cómo manejar situaciones como estas usando algunos parámetros a la hora de publicar los mensajes. Las colas, los exchanges y los bindings son conocidas como entidades de AMQP- Exchanges Los exchanges son entidades de AMQP a donde llegan los mensajes. Éstos al recibir los mensajes los enrutan hacia cero o más colas. El algoritmo de enrutado depende del tipo de exchange y las reglas definidas (bindings). Existen cuatro tipos de exchanges: •Direct exchange: amq.direct •Fanout exchange: amq.fanout •Topic exchange: amq.topic •Headers exchange: amq.match Independientemente del tipo de exchange, éstos son declarados con ciertos atributos, los más importantes son: •Name: Nombre del exchange. •Durability: Indica al broker que debe sobrevivir a reinicios. •Auto-delete: Son eliminados sin no hay ninguna cola asociada. •Arguments: Dependen del broker. Direct Exchange Los exchanges directos entregan los mensajes a las colas basándose en la routing key . Son ideales para enrutamiento unicast, aunque también se pueden usar para multicast. Funcionan de la siguiente manera: •Una cola se conecta (establece un binding) al exchange con la routing key K = R 3.3 AMQP 13 • El exchange directo suele ser usado para distribuir mensajes entre múltiples workers en modo round robin. Es importante ser conciente de que en AMQP se balancea entre consumidores y no entre colas. Se puede ver de forma grágica en la Figura 3.2. Figura 3.3 Exchange directo. Fanout Exchange Este tipo de exchange enruta todos los mensajes a todas las colas con las que está conectado, es decir, la routing key se ignora en este caso. Una copia de cada mensaje es enviado a cada una de las colas. Este tipo de exchange es ideal para el tráfico broadcast. Se puede ver de forma grágica en la Figura 3.4. Figura 3.4 Fanout exchange. 14 Capítulo 3. Protocolo MQTT, AMQP y STOMP Topic Exchange En este caso, los mensajes se envían a una o varias colas, basándose en la routing key con la que una cola está conectada a un exchange. Este patrón se suele usar en modelos de publicador/suscriptor. Header Exchange El header echange está diseñado para enrutar mensajes basándose en múltiples atributos que viajan en las cabeceras de los mensajes en lugar de usar la routing key . Colas Las colas en AMQP son muy similares a las colas en otros sistemas de colas. Almacenan mensajes que pueden se consumidos por aplicaciones. Las colas comparten algunas propiedades con los exchanges, pero también tienen sus propios atributos: •Name: Nombre de la cola. •Durable: Indica al broker que deben sobrevivir a reinicios. •Exclusive: Sólo se permite una conexión a la cola. •Arguments: Dependen del broker. Las colas deben ser declaradas antes de poder ser usadas. La declaración hace que la cola se cree si no existía anteriormente, si ya estuviese declarada, otra declaración no tendrá ningún efecto. Si se vuelve a declarar con otros atributos diferentes, se producirá una excepción. Colas durables Las colas durables se persisten a disco y sobreviven a reinicios del broker, en cambio, las colas que no son durables son llamadas transitorias. No todos los escenarios requieren que una cola sea durable. La durabilidad de una cola no hace que los mensajes que sean enrutados hacia esa cola sean durables. Si el broker se reinicia, las colas durables volverán a declararse durante el inicio de forma automática, sin embargo, sólamente los mensajes persistidos podrán recuperarse. Bindings Los bindings son reglas usadas por los exchanges para enrutar los mensajes recibidos hacia las colas. Para que un exchange enrute un mensaje a una cola, dicha cola debe enlazarse con el exchange. Los bindings pueden tener atributos opcionales como las routing keys . La finalidad de una clave de enrutado es seleccionar ciertos mensajes publicados en un exchage que está enlazado con una cola, es decir, actúan como filtros. Cuando un mensaje no se puede enrutar hacia una cola se descartará o se devolverá al productor, dependiendo de los atributos que el productor haya ajustado. Mensages Los mensajes en AMQP tienen atributos. Algunos de ellos son tan común que la especificación los define de forma que los desarrolladores no tienen que preocuparse por ellos. •Content type •Content encoding •Routing key 3.4 STOMP vs. MQTT vs. AMQP 15 •Delivery mode •Message priority •Message publishing timestamp •Expiration period •Publisher application id Algunos atributos los usa el broker, pero la mayoría de ellos son para los consumidores que lo reciben. Algunos atributos son opcionales. Los atributos se fijan cuando el mensaje se publica. Los mensajes también tienen una carga (los datos) que el broker trata como una ristra de bytes. El broker no inspecciona o modifica los datos. Es bastante común enviar los datos serializados en formatos como JSON , MessagePack , Protocol BUffers , etc. Para comunicar esta información se pueden usar los atributos content-type o content-encoding . Los mensajes pueden ser publicados como persistentes, que hace que el broker los persista a disco. Si el servidor se reinicia, el sistema se asegura que los mensajes persistentes no se pierdan. Sólo por publicar un mensaje en un exchange durable o que la cola a la que el mensaje es enrutado sea durable no hace que el mensaje sea persitido, para ello el mensaje tiene que ser publicado como persistente. Hay que tener en cuenta que publicar mensajes persistentes afecta al rendimiento. STOMP vs. MQTT vs. AMQP Como se puede ver, STOMP y MQTT son protocolos muy semejantes, entonces, ¿Cuándo usaremos cada uno de ellos?. Pues los dispositivos usarán el protocolo MQTT ya que ofrece más funcionalidad y es muy fácil encontrar implementaciones para muchos tipos de dispositivos. Sin embargo, para el lado de las aplicaciones se ha decidido usar STOMP . Esto quiere decir, que las aplicaciones del usuario que deseen conectarse al sistema para obtener los datos que los dispositivos envían mediante MQTT deben hacerlo usando STOMP . Esto es así porque una de las cosas que permite STOMP es que puede usar cualquier capa de transporte. Uno de los requisitos que se busca es que un navegador web pueda conectarse directamente a la plataforma y recibir datos. De esta forma no se requeriría que el servidor de la aplicación web obtenga los datos y los envíe al navegador, es decir, se pueden hacer aplicaciones serverless. Desde un navegador web sólo se permite el tráfico HTTP , por lo que, en principio, no se puede usar MQTT ya que este viaja en TCP . Ahí es donde entra en juego STOMP , que está diseñado para funcionar independientemente de la capa que haya debajo, lo que lo hace ideal para funcionar sobre websockets . Dicho esto el escenario es el siguiente: • Los dispositivos se conectan mediante MQTT sobre TCP a la plataforma. Se suscriben y publican en topics. • Las aplicaciones de la lógica del usuario se conecta a la plataforma usando STOMP bien sobre una sesión TCP o bien sobre websockets en caso de que quien realice la conexión sea un navegador. Se suscriben y publican en topics. • La plataforma, de forma interna, convierte los mensajes MQTT y STOMP a AMQP y trabaja con este sistema de forma interna. Veremos qué es AMQP en el siguiente capítulo. 22 Capítulo 5. API Código 5.2 Petición de login. POST /users/login { "email": "[email protected]", "password": "secreto" } Como respuesta se obtiene el acess_token y user_id : Código 5.3 Respuesta a login. { "id": "<access_token>", "ttl": 1209600, "created": "2013-12-20T21:10:20.377Z", "userId": "<user_id>" } A partir de ahora podemos realizar peticiones autorizadas enviando en la cabezera del mensaje HTTP el access_token el campo Authorization . Obtención de Fleet Key Para crear una flota es necesario haber realizado el proceso de registro y disponer de un user_id y acess_- token . Ser realizará la petición indicada en el código 5.4. Código 5.4 Creación de fleet_key . POST /users/<user_id>/fleets Authorization: <access_token> Si todo ha ido correctamente se obtendrá como respuesta el mensaje de respuesta 5.5 Código 5.5 Obtencion de fleet_key . { "uuid": "<fleet_uuid>" } 5.4 Registro de dispositivos 23 Registro de dispositivos Cuando el dispositivo se inicie por primera vez, deberá realizar un proceso de registro en el que usará la fleet_key para poder obtener un uuid y un secret . Para que un dispositivo pueda registrarse deberá ser capaz de comunicarse mediante HTTP , en caso contrario, será necesario el uso de un dispostivo intermedio capaz de realizar esta labor en nombre del dispositivo. A este dispositivo intermedio lo llamaremos hub que puede ser cualquier dispositivo que sea capaz de enviar mensajes HTTP y MQTT y reenviarlos en el protocolo que sea a los dispositivos reales. Podría ser, por ejemplo, un router o una Raspberry Pi. Un caso podría ser un Arduino que se comunicase mediante un módulo NRF24 con un programa que se ejecute en una Raspberry Pi. Será dicho programa de la Raspberry Pi el responsable de realizar el registro, de forma que el dispositivo no tiene porqué tener conocimiento del proceso ni de la plataforma. Cabe destacar que, en este caso, será la aplicación en cuestión quien debe conocer la fleet_key . Esto puede ser útil en caso de tener dispositivos que no podemos adaptar al sistema, bastaría con crear este intermediario para poder usar el dispositivo con la plataforma. En cualquier caso, el procedimiento será enviar un mensaje HTTP a la API con el contenido de la petición 5.6. Código 5.6 Registro de dispositivo. POST /fleets/<fleet_key>/registerMote Código 5.7 Respuesta de registro de dispositivo. { "uuid": "<Nuevo UUID de dispositivo>", "secret": "<Nuevo secreto>" } Una vez obtenidas las credenciales para poder enviar datos deberían persistirse para futuros usos. 24 Capítulo 5. API Desarrollo Figura 5.1 Explorador de la API. Para el desarrollo de la API se ha usado Node.js y loopback.io [ 1 ] como framework. loopback.io es un framework diseñado específicamente para el desarrollo de APIs REST de forma muy rápida ya que ahorra considerablemente la lógica a desarrollar. Las APIs se construyen mediante archivos de configuración donde se declara cómo debe comportarse y el framework se ocupa de toda la lógica. También abstrae de las bases de datos, mediante el uso de plugins simplemente se configura la conexión y el desarrollador no tiene que preocuparse de nada más. En caso de que se requiera implementar una lógica, el framework lo permite, ya que es totalmente extensible. Este framework es ideal para construir APIs que usen modelos que se creen, actualicen, eliminen, etc. Permite también establecer una jerarquía entre modelos y también implementa un potente sistema de control de acceso a los recursos expuestos por la API. 5.5 Desarrollo 25 Código 5.8 Declaración de una flota en Loopback.io. { "name": "fleet", "plural": "fleets", "base": "PersistedModel", "idInjection": true, "options": { "validateUpsert": true }, "mongodb": { "collection": "fleets" }, "properties": { "uuid": { "type": "string", "id": true, "index": true } }, "validations": [], "relations": { "user": { "type": "belongsTo", "model": "user", "foreignKey": "ownerId" }, "mote": { "type": "hasMany", "model": "mote", "foreignKey": "fleetId" } }, "acls": [], "methods": {} } 26 Capítulo 5. API Código 5.9 Declaración de usuario en Loopback.io. { "name": "user", "plural": "users", "base": "User", "idInjection": true, "options": { "validateUpsert": true }, "mongodb": { "collection": "users" }, "properties": {}, "validations": [], "relations": { "fleet": { "type": "hasMany", "model": "fleet", "foreignKey": "ownerId" } }, "acls": [{ "principalType": "ROLE", "principalId": "$owner", "permission": "ALLOW", "property": "__get__fleet" }, { "principalType": "ROLE", "principalId": "$owner", "permission": "ALLOW", "property": "__create__fleet" }], "methods": {} } 5.5 Desarrollo 27 Código 5.10 Declaración de dispositivo en Loopback.io. { "name": "mote", "plural": "motes", "base": "PersistedModel", "idInjection": true, "options": { "validateUpsert": true }, "mongodb": { "collection": "devices" }, "properties": { "secret": { "type": "string", "required": true, "description": "secret of the device" }, "uuid": { "type": "string", "id": true, "generated": false, "required": true, "index": true, "description": "UUID of the device" } }, "validations": [], "relations": { "fleet": { "type": "belongsTo", "model": "fleet", "foreignKey": "fleetId" } }, "acls": [], "methods": {} } Bases de datos En el proyecto se han utilizado dos bases de datos, una de ellas se utilizará para almacenar datos que usará el sistema de forma interna, como usuarios, permisos, topics. Para almacenar los datos que envían los dispositivos y aplicaciones se utilizará otra base de datos diferente. Lo que se guarda en esta base de datos es opaco para el sistema, se almacena como un buffer y en ningún momento es utilizado por la propia plataforma. MongoDB Es una base de datos de propósito general, se utilizará para almacenar credenciales de usuarios, ids de los dispositivos y, en general, cualquier tipo de datos que deba consultar el sistema para su correcto funcionamiento. Se elige MongoDB por su popularidad ya que es muy fácil de conectar con frameworks ya existentes, como loopback.io y tiene una gran cantidad de librerías para muchos lenguajes. Figura 6.1 MongoDB. RethinkDB RethinkDB [ 6 ] una base de datos moderna con un enfoque diferente al resto de las bases de datos actuales. En el paradigma clásico de las bases de datos, el cliente se conecta a ellas y hace consultas. Al igual que ocurre con el paradigma clásico de la web, es necesario estar lanzando peticiones constántemente por lo que se tienen instantes de tiempo en los que el sistema no está sincronizado. Con RethinkDB los clientes que se conectan a las bases de datos pueden suscribirse a cambios en los datos almacenados, lo cual nos permite tener un sistema más asíncrono, escalable y más adecuado para aplicaciones en tiempo real. Al no tener que hacer constantes peticiones a la base de datos, se alivia la carga y permite al sistema escalar de forma más fácil. 29 30 Capítulo 6. Bases de datos Como se puede aprecer en la Figura 6.2 los datos de usario son tratados por la base de datos como una ristra de bytes. Figura 6.2 Realizando una consulta a RethinkDB. Dispositivos y Módulos Dispositivos Los dispositivos son elementos externos al sistema. El propio sistema está diseñado para proveer servicios a estos dispositivos, por lo que podemos verlos como un especie de usuario de la plataforma. Cada dispositivo debe estar registrado en el sistema para poder interactuar con él, por lo que deberán implementar un proceso en el cual puedan darse de alta en la plataforma. Cada dispositivo estará asociado a un usuario, esto quiere decir que a la hora de registrarse, el dispositivo debe proveer cierta información para que la plataforma pueda determinar a qué usuario quedará asociado. Flotas La forma de asociar un dispositivo a un usuario es mediante las flotas . Las flotas son agrupaciones independientes de dispositivos que puede crear el usuario para gestionarlos. Cada usuario puede tener múltiples flotas. Al crear una flota, se generará de forma automática una fleet_key , que es una clave única que representa a cada flota. Cuando un dispositivo ejecuta el proceso de registro deberá proveer la clave para que el sistema lo reconozca como perteneciente a su flota correspondiente a la hora de registrarlo en la base de datos. Dispositivos de diferentes flotas no pueden comunicarse entre ellos de forma directa, es decir, si en una flota un dispositivo publica en el topic temperature y otro dispositivo de una flota diferente está suscrito a este topic no recibirá el mensaje. Sin embargo, siempre será posible que una aplicación o dispositivo pertenezca a ambas flotas y sea capaz de reenviar los mensajes. Módulos Los módulos son aplicaciones que simulan ser dispositivos. Su objetivo es actuar como intermerdiario entre los dispositivos reales y la plataforma. El dispositivo en el que corren los módulos se denominará hub. Una Raspberry Pi puede ser muy adecuado como hub ya que puede implementar los protocolos necesarios y puede comunicarse con los dispostivos reales usando un adaptador Bluetooth o ZigBee. Existen varios escenarios donde puede ser interesante usar módulos: 31 38 Capítulo 8. Ejemplo de uso Figura 8.5 Un dispositivo conectado a un Relé que controla las luces. Apéndice A Sobre el código Por motivos de mantener la memoria poco sobrecargada se ha decidido no incluir el código desarrollado como anexo. Ya que el código impreso se queda obsoleto pronto y no resulta cómodo de leer, se deja a continuación el enlace a todo el código desarrollado en GitHub: https:// github.com/ kintonxyz 39 Índice de Figuras 2.1. Estructura de un aplicación que use la plataforma 5 2.2. Estructura interna de la plataforma 6 3.1. Patrón publicador/suscriptor 9 3.2. Ejemplo de enrutamiento en AMQP 12 3.3. Exchange directo 13 3.4. Fanout exchange 13 4.1. RabbitMQ 19 5.1. Explorador de la API 24 6.1. MongoDB 29 6.2. Realizando una consulta a RethinkDB 30 8.1. Sistema de riego inteligente y control de luz 33 8.3. Recibiendo mensajes de IFTTT en Pushbullet 34 8.2. Receta de IFTTT para recibir alertas en Pushbullet 35 8.4. Grafana 36 8.5. Un dispositivo conectado a un Relé que controla las luces 38 41 Índice de Códigos 4.1. Configuración de RabbitMQ 18 4.2. Fragmento de código del backend de autorización 19 5.1. Petición para creación de usuario 21 5.2. Petición de login 22 5.3. Respuesta a login 22 5.4. Creación de fleet_key 22 5.5. Obtencion de fleet_key 22 5.6. Registro de dispositivo 23 5.7. Respuesta de registro de dispositivo 23 5.8. Declaración de una flota en Loopback.io 25 5.9. Declaración de usuario en Loopback.io 26 5.10. Declaración de dispositivo en Loopback.io 27 8.1. Fragmento de código del backend de autorización 36 43 Bibliografía [1] StrongLoop an IBM company, Loopback.io,https:// loopback.io/ , 2016. [2] dc-square GmbH, Everything you need to know about mqtt,http:// www.hivemq.com/ mqtt-essentials/ , 2016. [3] ifttt, Ifttt,https:// ifttt.com/ wtf , 2016. [4] Present Pivotal Software Inc, Amqp 0-9-1 model explained,http:// www.rabbitmq.com/ tutorials/ amqpconcepts.html, 2007. [5] Fundación Node.js, Express,http:// expressjs.com/ es/ , 2016. [6] RethinkDB, Rethinkdb,https:// www.rethinkdb.com/ faq/ , 2016. 45