[forum:2 Discusión abierta].

Arquitectura y Diseño de Wiring

Enlaces relacionados

Para la información relativa a la gestión de este módulo: Gestión del Wiring

Actualización sobre la versión inicial

Se ha llegado al acuerdo de definir tres entidades en el módulo de wiring:

  • Slots: Datos de entrada de gadgets
  • Eventos: Datos de salida producidos por gadgets
  • Datos o Canales: Canal de comunicación del tipo de datos que sea, y que comunica slots y eventos (los anteriores "tipos de dato" en el modelo propuesto)

De esta forma, se modifica el comportamiento inicial planteado:

  • Salvo que en el template se indique lo contrario, los slots no se añaden al módulo de wiring (no se generan Canales asociados).
    • Es posible definir en el template, para un slot, la opcion "autochannel", de forma que al añadirse al entorno operacional, automáticamente se genere un canal del tipo de datos del slot.
    • Del mismo modo, para un Evento, si se define "autochannel", éste evento se conectará al canal de ese tipo de datos
  • Desde la interfaz de usuario será posible administrar canales (crearlos, eliminarlos, ver el valor actual que contienen, ver cuantos gadgets producen/consumen datos, ver si el canal está muerto, etc)
  • Por tanto, la funcionalidad ofrecida cambia:
    • Un gadget podrá escribir en un canal (datos de salida, eventos), y será notificado a los gadgets suscritos a ese canal (datos de entrada, slots)
    • En cualquier momento un gadget podrá leer de un canal.
  • La información persistente cambia también consecuentemente:
    • Se guarda una lista de pares, canal-valor del dato en el mismo
    • Para cada canal, se guarda:
      • El conjunto de gadgets gadgets que publican dato en él (eventos)
      • El conjunto de gadgets suscritos al canal (slots) (hay que determinar si asociado a un slot se define automáticamente un trigger sobre él, o si hay que indicarlo de manera manual)

Introducción

El módulo de wiring ha de encargarse de ofrecer los mecanismos necesarios para proporcionar comunicación local (sin intercambio de datos con el servidor), entre las instancias de gadgets incluidas en el entorno operacional de un usuario. Ha de ofrecer un modelo de comunicación, un modo de definir qué puede enviar un gadget a otros, una API que posibilite esta comunicación y una interfaz de usuario que le permita configurar las comunicaciones deseadas. Todo ello como parte del núcleo de la plataforma. En los siguientes capítulos se describe el diseño inicial propuesto para el módulo.

Arquitectura de comunicación (Wiring)

El módulo de comunicación local entre gadgets o módulo de wiring se alojará en el núcleo de la plataforma y ejecutará en el cliente. Sus principales caracteristicas se describirán a continuación. La comunicación se realizará mediante la utilización de tipos de datos compartidos entre gadgets. Para ello, en la definición del gadget (template), se incluirán por un lado los datos que un gadget puede producir como salida, y los datos que puede recibir de entrada (los tipos de datos ante los que reacciona, así como el nombre de los manejadores implementados para tratar cada una de dichas reacciones). Por tanto, además de las entradas y salidas, se considera

Hay que aclarar que definir en el template un conjunto de datos de entrada y de salida para un gadget no tiene el sentido tradicional de entradas y salidas de una función, ni implica una semántica determinada, más allá de la explicada en este párrafo. Un gadget con algún dato de entrada definido, puede funcionar sin que se le indique un valor para ese tipo de dato (podrá reaccionar ante eventos de usuario, ofrecer una funcionalidad básica hasta que se le indique el dato, etc.), y al introducir un dato de entrada no tiene por qué generarse un dato de salida. Tampoco es necesario que un gadget haya recibido todos los datos de entrada definidos para generar la salida, etc. La única semántica que se garantiza es,

  • Para los datos de entrada: si el gadget recibe una entrada de alguno de los tipos definidos, reaccionará de alguna manera (es decir, el gadget se ha diseñado para reaccionar ante esos datos)
  • Para los datos de salida: en algún instante de ejecución (no se determina cual), el gadget producirá alguno de los datos de salida.

Quedá a decisión del desarrollador del gadget definir la lógica deseada, siguiendo las restricciones anteriores.

Modelo de comunicación

A falta de estudiar otros métodos alternativos, el modelo de comunicación que en principio ofrece unos resultados más adecuados según los requisitos de comunicación detectados es un modelo híbrido entre variables compartidas (modelo de pizarra) y un modelo publicador/subscriptor. Si se entra más en detalle, el funcionamiento propuesto es el siguiente: El módulo guarda un registro de los tipos de dato que puede producir el conjunto de gadgets instanciado en el entorno operacional. Además del conjunto de tipos de dato, se guarda el valor actual de cada uno de ellos (en caso de que este exista); esto es, cuando un gadget produce un dato (publicador) de un tipo concreto (el conjunto de tipos que puede producir está especificado en el template, como ya se explicó), el módulo de wiring actualiza el valor de dicho tipo. Un gadget que haya definido como uno de sus datos de entrada el que se ha actualizado, podrá ser consciente de ese cambio y actualizar su estado en consonancia, siempre que haya decidido conectarse a ese dato (suscriptor). Por lo general, en cualquier momento un gadget podrá conocer el valor de un dato, aunque no se encuentre suscrito (modelo de pizarra o variables compartidas). La forma de suscribirse a un tipo de dato, se explicará más adelante.

Por tanto el modelo de conexiones explicitas que un usuario ha de realizar para garantizar comunicación entre gadgets es el siguiente: el usuario únicamente conectará los tipos de datos de entrada de los datos que desee con datos equivalentes que están definidos en el módulo (y que han sido obtenidos de los datos de salida de los gadgets). No ha de preocuparse de conectar las salidas de los gadgets, ni gadgets entre sí.

Como puede verse, el modelo elegido presenta múltiples ventajas:

  • Los gadgets están muy desacoplados entre sí: un gadget no conoce la existencia de otros, sólo está conectado con ellos a través de los tipos de datos. De esta forma, un desarrollador de un gadget no tiene que preocuparse de con que otros gadgets podrá comunicarse, simplemente definirá un conjunto de datos de entrada y salida.
  • La ausencia de un gadget no merma el funcionamiento de los demás: al no estar comunicados directamente dos gadgets que pueden interactuar, si el usuario elimina uno de ambos el otro no deja de funcionar: si el gadget eliminado era productor de datos, el otro podrá seguir recibiendo actualizaciones del tipo de datos desde otro gadget que lo produzca; en el caso de que sea un gadget consumidor de datos el eliminado, el gadget productor seguirá actualizando el evento, y podrán ser otros (o incluso ninguno), los que sigan necesitando el valor de ese dato. Nota: queda pendiente evaluar como actuará el módulo en caso de que haya un tipo de dato sin lectores o escritores.

Tipos de datos

Obviamente, el problema de la comunicación a nivel de tipo de dato propuesta radica en los propios tipos de datos: ¿Cómo reconocer que el tipo de dato producido por un gadget es el mismo (semánticamente hablando) que el que otro necesita como entrada? A continuación se proponen algunas soluciones.

Tipos de datos no restringidos (decisión del usuario)

Por lo general, los datos que se comunican entre gadgets se reducen a tipos básicos (números, cadenas de texto). Para un dato se define su nombre y su tipo (en el módulo se almacenará también su valor). Nótese que dos gadgets que ofrecen/requieren de un dato llamado igual, pueden no estar refiriendose a lo mismo (o referirse a lo mismo con un formato distinto). La estrategia más sencilla consiste en utilizar estos tipos de datos sencillos. Por ello, si el usuario decide conectar la entrada de un gadget al tipo de dato producido por otro, la plataforma sólo comprobará si los tipos sintácticos (cadenas, números) son compatibles entre sí. La plataforma no garantiza que el gadget receptor del dato funcione como se espera al producirse el dato al que ha sido conectado. De esta forma, queda en manos del usuario la decisión de si son equivalentes dos datos de dos o más gadgets distintos. Si los proveedores de datos ofrecen una descripción textual del formato de los datos, así como su semántica, el usuario tendrá la capacidad necesaria para tomar esta decisión. Obviamente, la solución es la más sencilla de implementar, y la más genérica (no obliga a los desarrolladores de gadgets a seguir ningun acuerdo sobre la semántica de los datos producidos o consumidos). Sin embargo, puede provocar múltiples errores y comportamientos no esperados en los gadgets.

Tipos de datos marcados semánticante (conexión automática)

La solución anterior puede mejorarse de múltiples maneras. La más sencilla es comprobar también el nombre del tipo de dato para ver si encaja. Esta solución tampoco garantiza efectividad absoluta (dos datos llamados dirección pueden tener distinto formato, y por tanto no encajar perfectamente, y dos datos llamados de manera distinta pueden referirse al mismo tipo), pero puede mejorar sustancialmente la opción anterior.

Sin embargo, existe una solución más compleja que garantiza resultados óptimos: marcar semánticamente los tipos de datos publicados y subscritos. De esta forma, si dos tipos de datos tienen la misma marca semántica son el mismo tengan el nombre que tengan, y por tanto los gadgets que se refieren a ellos podrán comunicarse. Esta marca semántica puede ser desde un concepto de una ontología (solución demasiado formar y compleja), a un código numérico que marque a un tipo, y que forme parte o constituya su nombre. Este código se puede denominar friend code. Evidentemente, es una solución compleja ya que los desarrolladores de gadgets tienen que acordar cual es la semántica asociada a cada friend code. Esta solución, sin embargo, es muy útil en entornos empresariales, en las que un mismo desarrollador o equipo de desarrollo puede crear “paquetes de gadgets” interrelacionados, y para ello establecer un diccionario de datos en el que se defina la semántica y el formato asociado a cada tipo, estableciendo los friend codes.

Otra posibilidad que en la versión actual de la plataforma y del módulo de wiring no será implementada es que sea la propia plataforma la que lleve el control del diccionario de datos que puede manejar, estando prefijado de antemano la semántica de cada uno de los friend codes. A esta solución, evidentemente, podría aplicarse la colaboración de los usuarios para que lleguen a un consenso en cuanto a la semántica de cada dato, y añadan nuevos datos en función de sus necesidades.

En cualquier caso, como puede verse, si se utilizan friend codes, la interacción con el usuario para que éste configure que gadgets esten interconectados (en realidad, que entradas de datos se asocian a que tipos de datos definidos) puede ser eliminada o reducida. Si el usuario así lo desea, el módulo de wiring puede establecer automáticamente las conexiones que sean posibles (siempre que se utilice algún marcado semántico como los friend codes explicados). A pesar de ello, el usuario siempre tendrá la decisión final acerca de como intercomunicar los gadgets que haya instanciado en su entorno (eliminando conexiones, estableciendo más de las que le propone el módulo de wiring, etc).

Transformación de datos

Además de las opciones anteriores, existe una posibilidad que en esta versión no se implementará y que puede paliar las inconsistencias de tipos de datos. Se trata de los operadores de datos. El módulo podría proveer de un conjunto de transformadores de datos configurables, y que permitieran que dos datos con la misma semántica pero distinto formato de representación fueran compatibles: por ejemplo, puede haber un tipo de datos que considere el nombre de una persona con el formato Apellidos, Nombre y otro que lo considere como Nombre Apellidos; evidentemente con una transformación simple estos datos serían el mismo, y por tanto podrían utilizarse para comunicar dos gadgets.

Persistencia

Este módulo ha de garantizar la persistencia (tanto a lo largo de una sesión como entre sesiones) de las conexiones creadas por un usuario entre tipos de entrada de instancias de gadgets y tipos de datos, además del valor actual de cada uno de los datos que existen en el entorno operacional del usuario.El módulo de wiring hará uso de las herramientas que proporcione este módulo. En el apartado de diseño se especifica los datos que persistirán

Diseño del módulo de wiring

Diseño del módulo

Como se ha comentado al principio del documento, el módulo ejecutará principalmente en el cliente, por lo que se descargará como una librería javascript al acceder a la plataforma. Nada más arrancar la plataforma, se comunicará con el módulo de persistencia (mediante su interfaz REST), y solicitará toda la información necesaria, almacenándose en estructuras de datos locales en javascript. La información necesaria para que el módulo sea operativo son:

  • Lista de tipos de datos de salida de todos los gadgets instanciados en el entorno operacional: En principio, esta lista estará compuesta por el nombre del dato. Si se añade un gadget con un nombre de dato de salida que ya se encuentra en esta lista, no será añadido. Asociado a cada tipo de dato, también se almacena el valor actual de ese dato.Además, se puede plantear incluir un contador de instancias de gadget que producen ese dato, y otro de instancias que lo están consumiendo (útil para control de datos muertos, o gadgets sin posibilidad de recibir datos).
  • Lista de suscripciones: para cada dato, se mantendrá una lista de identificadores de instancias de gadget que estén suscritos a dicho dato.

El módulo se encargará de gestionar nuevas subscripciones, tanto las que indique el usuario como las automáticas (ver Tipos de datos]). Además, se encargará del manejo de eventos, cuando se produzca la actualización de algún dato (se lance un evento en un gadget concreto)

La librería contendrá las clases necesarias para ofrecer una API tanto al desarrollador de gadgets como la integración con el resto de módulos.

En la sección se describe más detalladamente el funcionamiento, por medio de escenarios de uso.

Funcionamiento del módulo según escenarios de uso

Añadir un gadget

  1. Al añadir un gadget desde el repositorio, el entorno comunicará el identificador del mismo al módulo de wiring.
  2. El módulo accederá al template del gadget para obtener sus tipos de dato de entrada y salida.
  3. El módulo comprobará los datos de salida del gadget y los añadirá a la lista de datos disponibles para aquellos que no estuvieran.
    1. Para los datos ya proporcionados por otros gadgets y que también sean proporcionados por el nuevo, se incrementará un contador de gadgets asociados al dato (de momento, esta información no se aprovecha, pero en el futuro será útil
  4. Para los datos de entrada del gadget, el módulo incrementará el contador de gadgets que requieren ese tipo de dato.
    1. En caso de que la suscripción sea automática, ver (Conectar un gadget a un tipo de dato)

Eliminar un gadget

  1. Al eliminar un gadget, el repositorio/entorno, se lo comunicará al módulo de wiring.
  2. El módulo accederá al template del gadget, para obtener los datos de salida del gadget.
  3. El módulo recorrerá la lista de suscripciones y eliminará las suscripciones del gadget a cualquier dato (decrementando el contador correspondiente)
  4. Posteriormente se decrementará el número de gadgets proporcionando los datos de salida del gadget eliminado.

Suscribirse a un dato

Este caso ocurre al añadir un gadget (si la subscripción automática está activada), y cuando el usuario lo indique). Para este caso, al módulo se le proporciona el identificador de gadget y el dato al que quiere suscribirse. Se asume que el gadget tiene definido en su template el dato como valor posible de entrada (se comprobará previamente a llamar al módulo de wiring)

  1. El módulo de wiring, por medio del identificador de instancia de gadget, accede a su template, para obtener el manejador (nombre de función javascript interna al gadget y que se encarga de tratar los eventos de envío de datos) asociado al tipo de datos al que se quiere suscribir.
  2. Ese manejador se registra en una lista suscripciones, que contiene el conjunto de manejadores a ejecutar, asociados al tipo de dato elegido (por supuesto, también se almacena el identificador de gadget)

Eliminar una suscripcion

Contrario al caso anterior, ocurre al eliminar un gadget o por decisión del usuario, utilizando la interfaz gráfica.

  1. El módulo recorre la lista de suscripciones para el dato a eliminar, y elimina la entrada adecuada, decrementando los contadores

Envío de un evento(actualizar el valor de un dato)

El módulo de Wiring ofrecerá una API para los desarrolladores de gadgets, incluyendo un método para enviar el valor de un dato (hay que recordar que los datos que un gadget puede producir están definidos en el template). En el método simplemente habrá que indicar el dato sobre el que se produce el evento, y el valor que toma el dato. Este es el caso de uso que provoca la comunicación entre gadgets

  1. El módulo de wiring recibe una petición por parte de un gadget para actualizar un dato.
  2. El módulo almacena el nuevo valor de ese dato.
  3. Se recorre la lista de suscriptores a ese dato, e invoca secuencialmente todos los métodos manejadores definidos para esa suscripción, indicando el dato que se está actualizando, y el valor de dicho dato. Nótese que el desarrollador de gadgets habrá tenido que implementar una función manejadora para cada uno de los datos de entrada que defina en el template, y ésta es la que será invocada desde el módulo de wiring.

Además de estos casos de uso que se consideran esenciales, el módulo también ofrecerá funcionalidad adicional:

  • Dado que el valor de los tipos de datos es persistente, el usuario desde la interfaz tendrá la posibilidad de reiniciar (o eliminar) el valor de un dato, o incluso de todos los datos presentes.
  • La API del módulo ofrecerá también al desarrollador de gadgets una llamada para obtener el valor de un dato de manera asíncrona, así como reiniciar el valor de los datos que produce.

Comunicación con otros módulos

A lo largo de los casos de uso se han identificado comunicación e interacción con otros módulos. Esta sección trata de resumirla. En un futuro se definirán los métodos que exportará el módulo de wiring y que necesiten otros módulos del sistema.

  • Deberá comunicarse con el módulo de persistencia, y necesitará las dos operaciones básicas: obtener datos y almacenarlos. La información a salvar/cargar se define en [Persistencia]
  • Ha de comunicarse con el módulo que maneje el template. Necesita un método obtener información proporcionado por dicho módulo que, dado un identificador de gadget (nótese que es necesario tener en cuenta la noción de identificador de gadget, para cada uno de los gadgets que estén instanciados en el entorno operacional, y una lista de relaciones entre identificador y tipo de gadget), devuelva toda la información del template (quizá una decisión de diseño sería que devolviera un objeto información de gadget, con métodos para acceder a cada uno de los campos)
  • El módulo ha de incluir dos operaciones, que serán llamadas desde el entorno operacional: gadget añadido y gadget eliminado y que son necesarias para el correcto funcionamiento del módulo.
  • El módulo ha de comunicarse con la interfaz de wiring, necesitando operaciones de suscribir y dessuscribir, además de los métodos de reiniciar un dato y reiniciar todos
  • Ha de utilizar alguna función del tipo obtener todos los gadgets que de el repositorio/entorno operacional? y que devuelva los identificadores de todos los gadgets incluidos en el entorno, para construir la interfaz de usuario
  • El módulo ha de ofrecer una API al desarrollador de gadgets (probablemente, la plataforma en general le ofrecerá una API más compleja con otros métodos, y será en esta API donde se incluyan los métodos asociados al wiring). Las operaciones se describen a continuación

API del módulo para el desarrollo de gadgets

Como ya se ha comentado antes, la API que ofrece el módulo para el desarrollo de gadgets consta de un método esencial:

    set_value(data_type,value)

que actualiza el valor de un tipo de dato. Además, se ofrecerán otros métodos secundarios:

    value=get_value (data_type)

que obtiene el valor de un tipo de dato

    clear_value (data_type)

que reinicia/elimina el valor de un tipo de datos (sólo funcionará con los tipos de datos que sean salida del gadget que utilice esta funcionalidad)

    clear_all ()

que elimina el valor de todos los tipos de dato de salida del gadget.

Interfaz de usuario

La interfaz de usuario del módulo de wiring seguirá un desarrollo incremental.Como mínimo, debe:

  • Mostrar todas las instancias de gadgets que el usuario haya añadido al entorno operacional.
  • Para cada gadget, mostrar los datos de entrada que se hayan definido.
  • Mostrar todos los datos de salida (es decir, todos los datos susceptibles de ser leídos) disponibles.
  • Mostrar las suscripciones ya realizadas, mostrando una unión entre un tipo de dato y la entrada correspondiente del gadget que participa en la suscripción.

Además, deberá satisfacer la siguiente funcionalidad de cara al usuario:

  • Añadir nuevas suscripciones, indicando el usuario:
    • Dato que se transmitirá
    • Gadget con el que quiere comunicar el dato, indicando la entrada del gadget correspondiente
  • Eliminar suscripciones, indicando que suscripción (pareja gadget-dato) hay que eliminar
  • Borrar el valor de un dato
  • Borrar el valor de todos los datos

El objetivo es tener desarrollada una interfaz en html/javascript que satisfaga estas funcionalidades lo antes posible. Para ello, se empezarán definiendo controles simples (enlaces, entradas de texto, listas desplegables, etc.).

La presentación deberá evolucionar utilizando librerias javascript como prototype/scriptaculous, para que en una versión posterior permita eventos complejos:

  • cada gadget tendrá una representación gráfica,
  • en ella podrán observarse sus datos de entrada y el usuario, por medio de clicks de ratón y drag&drop podrá conectarlos con datos (que también tendrán representación gráfica).
  • las suscripciones ya creadas tendrán también una representación gráfica (un arco o línea que una el dato de entrada del gadget con el dato al que está conectado)
  • Controles complejos:
    • Al pinchar en un gadget, deberán mostrarse los datos disponibles para ser conectados
    • Al pinchar en un dato, deberán mostrarse los gadgets que pueden ser conectados a ese dato

Persistencia

La persistencia es necesaria (como se ha indicado antes), para almacenar la siguiente información:

  • Lista de tipos de datos de salida de todos los gadgets instanciados en el entorno operacional: En principio, esta lista estará compuesta por el nombre del dato más el valor actual de ese dato.Además, se puede plantear incluir un contador de instancias de gadget que producen ese dato, y otro de instancias que lo están consumiendo (útil para control de datos muertos, o gadgets sin posibilidad de recibir datos).
  • Lista de suscripciones: para cada dato, se mantendrá una lista de identificadores de instancias de gadget que estén suscritos a dicho dato.

Queda pendiente definir el formato final de estas listas. El modelo de persistencia es el mismo que el definido para el resto de módulos (Persistencia local en DOM, Persistencia remota con REST-Base de datos)

Implementación del módulo

El módulo como ya se ha comentado, será una librería javascript que se descargará al acceder a la plataforma. El diseño del módulo será orientado a objetos, y ofrecerá las funciones ya descritas en el diseño. Además hará uso de la librería javascript prototype para que ésta le proporcione funcionalidad adicional. Se utilizará la librería scriptaculous para generar la interfaz de usuario, en conjunción con html.

A medida que el código vaya siendo generado, en esta sección se irán indicando las características fundamentales.

Relación con otros módulos

Este módulo requiere de la definición del template de un gadget para acceder a los tipos de datos definidos en él. Además, necesita persistencia, por lo que hace uso de dicho módulo. Por último, la interfaz de wiring ha de estar incluida como parte del entorno operacional, siendo fácilmente accesible desde este, y compartiendo el diseño y ubicación de los gadgets