Descentralized Software Services (DSS)

En esta tercera entrega sobre Robotics Studio (MSRS) vamos a hablar sobre DSS, o los servicios descentralizados de software. En los dos artículos previos sobre CCR estuvimos hablando sobre la base sobre la que se construirían los servicios de Robotics a través de esas colas asíncronas y concurrentes representadas por la clase Port<T>. Ahora lo que queremos construir son servicios autónomos que representan una funcionalidad concreta dentro de Robotics Studio.

Los servicios son los bloques básico con los que se construyen las aplicaciones de Robotics, y estas pueden representar acceso al hardware y software, como lectura de sensores (bumper, laser, batery, ect) en lo que a hardware se refiere y por la parte de software, podemos utilizar los servicios para UI (User Interface), Storage, Directory Services, ect.

DSS está basado en un arquitectura REST de servicios web que permite que los dispositivos se puedan comunicarse en un entorno distribuido desde el principio. Además esto permite que aplicaciones de terceros puedan interpolar con Robotics studio, terceros como aplicaciones java, servicios web en asp.net ect. DSSP (Descentralized Software Services Protocol) es el protocolo de comunicación a través de REST y está basado en SOAP.

Además DSS es el runtime que se encarga de cargar todos los servicios, crearlos y comunicarlos entre ellos. La sintaxis de algunas partes nos recuerda mucho a WCF, pero hay que decir que este producto está creado desde cero y no ha tenido influencia de ningún producto de Microsoft. La parte del runtime además permite que se puedan cargar servicios en demanda como ensamblados de .net y que podamos hostear en nuestras aplicaciones en runtime de DSS.

Vamos a ver entonces, la anatomía de un servicio de Robotics Studio.

clip_image002

En este gráfico podemos ver un resumen de cómo está definido un servicio de MSRS. A continuación vamos a definir estos conceptos.

  • Service Identifier: Es la url que representa el servicio. Por ejemplo http://schemas.tempuri.org/2007/08/servicetutorial8.html
  • Contract Identifier: Es el identificador del contrato de operaciones que este servicio acepta. Como hemos dicho antes MSRS utiliza REST para comunicarse de una manera descentralizada así que tenemos que de alguna manera definir cuáles son las operaciones o puerto que este servicio define. Además de esto hay que definir cuáles son los tipos de datos de las operaciones, ya sean simples o compuestos.
  • State: Representa el estado del servicio y es la información que se va a obtener a través de la operación GET. Estados como, por ejemplo, el valor de carga de la batería, el último fotograma de la web cam o cualquier otro valor que indique el estado del servicio.
  • Main Port: es el puerto principal de comunicaciones del servicio. Como dijimos antes DSS está basado en CCR, y la clase PortSet que vimos con anterioridad es el lugar en el que la vamos a empezar a usar. Main Port es un PortSet con las operaciones o mejor dicho las DssOperations<TBody,TResponse> que define el servicio. Y ¿qué definimos con esa DssOperation?, pues básicamente el tipo de dato de la petición y de la respuesta a la operación del servicio. DSSP está basado en REST y esta es un tecnología que normalmente usa HTTP como protocolo de transporte, HTTP está basado en peticiones y respuestas (Request, Response), pues básicamente lo que estamos haciendo aquí es decirle en la operación del protocolo DSSP cuál es el tipo de la petición, ósea que valores necesito para mi petición, y cuál es el tipo de respuesta, que son los valores de respuesta de la operación. Cuando me refiero a operación es algo parecido a los verbos de HTTP (GET,POST,UPDATE,DELETE,DEBUG,ect), pero verbos de DSSP.
  • Partners: son la lista de servicios externos que este servicio necesita para funcionar. Si es que tiene algún partner.

Como se traduce toda esta información a clases de .net? Pues veamos.

El identificador del contrato se define así:

/// <summary>
/// Bank Contract class
/// </summary>
public sealed class Contract
{

   /// <summary>
   /// The Dss Service contract
   /// </summary>
   public const String Identifier = "http://schemas.tempuri.org/2008/10/bank.html";
}

Como una constante dentro de una clase Contract, esto se hace así siempre por convención.

/// <summary>
/// The Bank State
/// </summary>
[DataContract()]
public class BankState
{
    private Dictionary<string, Item> dic = new Dictionary<string, Item>();

    [DataMember]
    public Dictionary<string, Item> Clientes
    {
        get { return dic; }
        set { dic = value; }
    }
}
[DataContract]
public class Item
{
    private string cliente;
    private int dinero;

    [DataMember]
    public string Cliente
    {
        get { return cliente; }
        set { cliente = value; }
    }

    [DataMember]
    public int Dinero
    {
        get { return dinero; }
        set { dinero = value; }
    }
    public Item(string cliente, int dinero)
    {
        this.cliente = cliente;
        this.dinero = dinero;
    }
    public Item() { }
}

El estado del servicio es una clase decorada con los atributos DataContract y DataMember, como en WCF.

/// <summary>
/// Bank Main Operations Port
/// </summary>
[ServicePort()]
public class BankOperations : PortSet<DsspDefaultLookup, DsspDefaultDrop, Get, Transferir, Conectar>
{
}

Así es como se define el puerto principal. Un PortSet con las operaciones del servicio, que en este ejemplo el servicio acepta 4.

  • DsspDefaultLookup, es una manera de anunciar su defunción de servicio, es lo que usa el runtime de DSS para saber las operaciones del servicio.
  • DsspDefaultDrop es la operación de eliminar servicio.
  • Get, esta operación se puede usar para muchas cosas pero en principio es para obtener el estado actual del servicio, ósea obtener el estado.
  • Transferir y Conectar son operaciones personalizadas.

Si nos fijamos en el código anterior, se han definido dos servicios más además de los que MSRS define por nosotros, ahora bien, ¿Cómo se definen operaciones?

Como se ha señalado anteriormente estamos es un entorno web en que usamos REST + SOAP para comunicarnos. A todo esto lo llamamos DSSP. Como bien sabréis la web se basa en peticiones y respuesta, pues nosotros vamos a definir una operación de la misma manera, una petición y una respuesta. Siguiendo con el concepto de puertos introducido en los dos artículos anteriores, esas peticiones y esas respuestas van a ser dos puertos de CCR. Ahora bien, Port<T> es la clase usada para definir el puerto y tenemos que suministrarle un tipo con el que crear el puerto. Ese es el motivo de porque nosotros tenemos que definir dos tipos de datos personalizados que van a ser los parámetros de la petición.

En vez de hacer un método que acepte una serie de parámetros, que sería lo normal, vamos a encapsular todos esos parámetros en una clase nueva, y por supuesto vamos a decorar esa clase con el atributo Contract. Así que básicamente una operación es una petición de un tipo de dato personalizado por nosotros en la definición del servicio y una respuesta que también es un tipo de dato personalizado con los valores de respuesta de la operación.

public class Transferir : Get<TranferirRequestType, PortSet<TransferirResponseType, Fault>>{ }

Esta es la definición de la operación Transferir, en la que especificamos que el tipo de petición es TransferirRequestType y el de respuesta es TransferirResponseType. La clase Get<TRequest,TResponse> hereda de la clase DsspOperation<TBody, TResponse> que es la clase base de todas las operaciones del protocolo DSSP. TResponse en este caso está definido a su vez como un PortSet<TransferirResponseType,Fault>, un grupo de puertos de dos tipos. Fault es una clase definida en el namespace W3C.Soap y define un tipo de dato para las excepciones que se produzcan cuando se realicen las peticiones al servicio. Así que la cosa se queda así, para la respuesta tenemos un PortSet para que cuando consumamos la operación de este servicio, si algo ocurre más seamos notificados a través del puerto de tipo Fault y si todo ha ido correcto seremos notificados al puerto TransferirResponseType.

Aquí las definiciones de esos tipos.

[DataContract]
public class TranferirRequestType
{
    private int dinero;
    private string clienteDestino;
    private string clienteOrigen;

    [DataMember]
    public int Dinero
    {
        get { return dinero; }
        set { dinero = value; }
    }

    [DataMember]
    public string ClienteOrigen
    {
        get { return clienteOrigen; }
        set { clienteOrigen = value; }
    }

    [DataMember]
    public string ClienteDestino
    {
        get { return clienteDestino; }
        set { clienteDestino = value; }
    }
}
[DataContract]
public class TransferirResponseType
{
    private string resultado;

    [DataMember]
    public string Resultado
    {
        get { return resultado; }
        set { resultado = value; }
    }
}

Por ahora solo hemos definido el cuerpo del servicio, en el siguiente articulo terminaré de especificar el servicio y sube el código fuente del ejemplo completo.

Saludos. Luis.

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *