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.

Robotics 2

Continuamos con Robotics, como habíamos comentado en el post anterior, con CCR podemos ejecutar tareas de manera concurrente, esta tareas normalmente van a ser el procesamiento de los valores que el robot nos devuelve a través de la clase Port<T>.

Vimos unos ejemplos de cómo trabajar con CCR, ahora continuamos con algún ejemplo más complejo.

void RunFromIterator()
{
    Dispatcher d = new Dispatcher(0, "Test Pool");

    DispatcherQueue taskQ = new DispatcherQueue("Test Queue", d);

    Console.WriteLine("Before Iterator submitted - thread {0}", Thread.CurrentThread.ManagedThreadId);

    Arbiter.Activate(taskQ,
        
        Arbiter.FromIteratorHandler(IteratorExample),
        Arbiter.FromIteratorHandler(IteratorExample),
        Arbiter.FromIteratorHandler(IteratorExample),
        Arbiter.FromIteratorHandler(IteratorExample)
        );

    Console.WriteLine("After Iterator submitted - thread {0}", Thread.CurrentThread.ManagedThreadId);

}

private static IEnumerator<ITask> IteratorExample()
{
    Port<int> p1 = new Port<int>();
    Port<int> p2 = new Port<int>();

    p1.Post(0);

    bool done = false;

    while (!done)
    {
        yield return Arbiter.Receive(false, p1,
            delegate(int i)
            {
                Console.WriteLine("P1 Thread {0}: {1}", Thread.CurrentThread.ManagedThreadId, i);
                p2.Post(i + 1);
            }
        );

        yield return Arbiter.Receive(false, p2,
            delegate(int i)
            {
                Console.WriteLine("P2 Thread {0}: {1}", Thread.CurrentThread.ManagedThreadId, i);
                if (i >= 10000000000)
                    done = true;
                else
                    p1.Post(i + 1);
            }
        );
    }

    yield break;
}

Si nos fijamos en este ejemplo nuestro Arbiter activa cuatro tareas que las genera a través de un método helper que se llama, Activator.FromIteratorHandler que básicamente es un delegado a un método del tipo:

IEnumerable<ITask> GetMethod();

Si nos fijamos en este método lo que devuelve es un IEnumerable<ITask>, ITask era la interface que teníamos que implementar para hacer una tarea que se ejecutase en el Dispatcher de CCR, así que de alguna manera estamos devolviendo una colección de tareas para que el Dispatcher las ejecute. Si nos fijamos en el código que tenemos en el cuerpo de la función, hacemos cosas como esta.

yield return Arbiter.Receive

Al utilizar la palabra reservada yield, estamos indicando que vamos a devolver un ITask, que lo provee Arbier.Receive, a ese enumerador infinito que tenemos. Y digo enumerado infinito porque en principio no sabemos cuando vamos a terminar de enumerar esta colección. Yield se utiliza cuando estamos haciendo la implementación de un IEnumerable y de base tenemos un array o una lista y devolvemos elemento a elemento. En este caso es algo parecido, solo que no tenemos una colección de base sino que tenemos la flexibilidad de decidir cuando ese enumerado termina. ¿Y cuando termina?, pues cuando se devuelve un yield break;.

¿Por qué se usa yield y IEnumerable<ITask>?, pues tiene que ver de nuevo con la concurrencia. Teniendo en mente lo que anteriormente dije sobre como CCR está implementado, el uso de yield hace que de alguna manera cuando el DispatcherQueue recorra esa lista por nostros, nostros en nuestro código seamos capaces de devolver esa tarea para que se ejecute en el Dispatcher y aunque salimos del cuerpo del método, el uso de yield haga que de alguna manera se acuerde de cuales eran los valores que teníamos antes en nuestro método y que todo eso sea gestionado por el Dispatcher.

Es desde luego una solución muy elegante a un problema de enumeración de tareas que no sean bloqueadas por el llamado y el que llama.

Uno después de ver este codigo se da un poco cuenta de la importancia que tiene ese procesamiento de colas dentro de CCR, y como Robotics hace uso de esto. Extendiendo un poco el tema de las colas, CCR introduce el conceto de PortSet. PortSet, que como su nombre indica, es un grupo de puertos, perite asociar varios tipos de datos para hacer puertos. Podemos hacer un PortSet de 20 tipos en .NET Framework y 8 tipos de CF. Por supuesto esta clase se usa con genericidad.

PortSet<string,int,int,double> portSet;

Esto permite aumentar las opciones de las que podemos hacer con una cola. Por ejemplo con Arbiter ahora podemos hacer lo siguiente:

void Choice()
{
    PortSet<bool, int> ps = new PortSet<bool, int>();

    
    Activate(
        Arbiter.Choice<bool, int>(ps,
            delegate(bool b)
            { Console.WriteLine("Choice: " + b.ToString()); },
            delegate(int n)
            { Console.WriteLine("Choice: " + n.ToString()); }
        )
    );

   
    ps.Post(1000000);
    ps.Post(true);
    ps.PostUnknownType(true);
    ps.PostUnknownType("asdad");
}

Si nos fijamos en el ejemplo, tenemos una activación de un PortSet, pero ahora indicamos dos tipos de Handler<T> para cada uno de los tipos que define nuestro PortSet, esto permite discrimiar el consumo de los valores del puerto a través de tu tipo. Al final de ejemplo podemos ver como tenemos para cada tipo una sobrecarga de Post(T value) y un método llamado PostUnknownType(object) que permite que posteemos un mensaje en ese grupo de puertos. En caso de que el tipo no se pueda resolver, en este caso string, se lanzará una excepción.

Tambien podemos hacer Join con los PortSet sentencias lógicas AND, veamos.

void Join()
{
    Port<bool> p1 = new Port<bool>();
    Port<int> p2 = new Port<int>();
    Port<string> p3 = new Port<string>();

    Arbiter.Activate(Environment.TaskQueue,
        Arbiter.JoinedReceive(
            false, p1, p2,
            delegate(bool b, int i)
            {
                Console.WriteLine("Join 1: {0} {1}", b, i);
                p2.Post(i + 1);
            }
        )
    );

     Arbiter.Activate(Environment.TaskQueue,
        Arbiter.JoinedReceive(
            false, p2, p3,
            delegate(int i, string s)
            {
                Console.WriteLine("Join 2: {0} {1}", i, s);
                p2.Post(i - 1);
            }
        )
    );

    p1.Post(true);
    p3.Post("hello");
    p2.Post(99);
}

En el que hasta que no se recivan dos valores en el puerto del tipo especificado no se van a llamar al método que los procese.

Expandiendo este conecpto podemos en vez de esperar un valor único que procesar, esperar una lista de valores de tamaño fijo (un array).

Port<int> p = new Port<int>();

Arbiter.Activate(Environment.TaskQueue,
   Arbiter.MultipleItemReceive(
       true, p, 6,
       delegate(int[] array)
       {
           string s = "";
           for (int i = 0; i < array.Length; i++)
               s += array[i].ToString() + " ";
           Console.WriteLine("{0} Items: {1}", array.Length, s);
       }
   )
);

En este ejemplo hasta que no se reciben 6 elementos no se procesan, y por supuesto se procesan todos a la vez.

También podemos tener multiples procesadores, para varios tipos de datos.

PortSet<int, double> pSet = new PortSet<int, double>();

Activate(
    Arbiter.MultipleItemReceive(
        pSet, 10,
        delegate(ICollection<int> colInts, ICollection<double> colDoubles)
        {
            Console.Write("Ints: ");
            foreach (int i in colInts)
                Console.Write(i + " ");
            Console.WriteLine();
            Console.Write("Doubles: ");
            foreach (double d in colDoubles)
                Console.Write("{0:F2} ", d);
            Console.WriteLine();
        }
    )
);

En el que hasta que no se reciban 10 valores, como mínimo, en los dos puertos no se ejecutará el método que los procesará.

Y ya para terminar esta breve pero intensa introducción a CCR, hablaremos de las casualidades. Las casualidades no son más que una manera que tiene el CCR de llamar a las excecpiones que se produzcan en nuestro código. Hay que tener en cuenta que en cualquier momento se puede lanzar una excepción y que tenemos que ser capaces de procesarla y que no haga que el codigo se rompa.

private void Causality()
{
    using (Dispatcher d = new Dispatcher())
    {
        using (DispatcherQueue taskQ = new DispatcherQueue("Causality Queue", d))
        {
            Port<Exception> ep = new Port<Exception>();
            Port<int> p = new Port<int>();

            Dispatcher.AddCausality(new Causality("Test", ep));

            Console.WriteLine("Main thread: {0}", Thread.CurrentThread.ManagedThreadId);

            Arbiter.Activate(taskQ, Arbiter.Receive(false, p, CausalityExample));

            p.Post(2);
            Wait(500);

            Exception e;
            while (ep.Test(out e))
                Console.WriteLine("Exception: " + e.Message);
        }
    }
}

Básicamente las casualidades son un Port<T> de excepciones en los que se van posteando automáticamente todas las excepciones que se producen en el código para después procesarlos.

Este último ejemplo también muestra un método Test que nos permite comprobar si hay un elemento en la cola.

Espero que os haya gustado esta introducción a CCR, es los dos próximos post explicare como esto es usado desde DSS que es el corazón de Robotics Studio.

Luis.

Robotics Studio

Recientemente he tenido que impartir un curso sobre Robotics Studio en el Innovation Center de Mondragón, centro de innovación dedicado a tecnologías embebidas. Robotics Studio MSRS, es un herramienta estupenda para que profesionales y entusiastas empiecen a programar aplicaciones robóticas.

Sin entrar a valorar mucho lo que es una aplicación robótica, diré que, básicamente es lo mismo que cualquier software que construimos en nuestro pc. Entrada de datos, que se procesan de alguna manera y se devuelve de otra manera. Como siempre se nos ha dicho el desarrollo de software es como una caja negra que procesa datos. Pues en las aplicaciones robóticas, ocurre lo mismo, salvo que los datos vienen de los diferentes sensores que el robot incorpora. Así que básicamente nos dedicamos a trabajar con esos datos provenientes del robot y a hacer algo con ellos.

Si bien es cierto que el desarrollo de software ha cambiado mucho desde sus inicios, podemos decir que Robotics Studio, representa esa evolución que el desarrollo de software tuvo con respecto al desarrollo de aplicaciones. Decir que Robotics Studio está pensado para orquestar todos esos datos provenientes del robot desde el PC, aunque también se puede “orquestar” desde un móvil. MSRS está diseñado con .NET así que podemos usar cualquiera de nuestro lenguaje favorito de .NET para programar aplicaciones robóticas.

Robotics Studio está compuesto básicamente de dos componentes principales, CCR (Concurrency and Coordinator runtime) y DSS (Descentrailized Software Services)

Como se empieza con el SDK?

CCR

Pues bien, hay que tener varios conceptos claros a la hora de programar aplicaciones para Robotics Studio, como se ha dicho antes una aplicación robótica recibe los datos del robot y los procesa. Ante esta sencilla frase se encuentra el porqué del modelo de desarrollo de MSRS. Un problema que nos podemos encontrar a la hora de programar estas aplicaciones es que los datos de los sensores pueden venir en cualquier momento, y todos a la vez con lo que tenemos que ser capaces de poder lidiar con esta concurrencia desde el primer momento. Pero eso no es lo único porque además de que los sensores nos pueden dar muchos valores a la vez, podemos trabajar con UI y sincronizar todos esos datos con un formulario de Windows Forms, así que, aunque la cosa parece sencilla, en breve se complica.

Para solucionar este problema de base, la concurrencia, MSRS tiene una librería que nos va a permitir programar estás aplicaciones robóticas sin tener que preocuparnos, mucho, de la concurrencia. Si lo pensamos por un momento, tenemos una cola de datos en la que el robot va insertando los datos provenientes de su sensor, y una aplicación que va sacando esos datos de la cola. Esta cola, por supuesto, ha de ser segura frente a subprocesos y concurrente. Para ello disponemos, en MSRS, de una clase muy especial, Port<T>, que nos permite crear ese puerto de comunicación entre un dispositivo y la aplicación. Esta clase Port<T> que es genérica, permite que podamos definir el tipo de datos proveniente de nuestro sensor de manera personalizada.

Una vez que ya tenemos el puerto definido en cuanto al tipo de dato que va a procesar tenemos que ser capaces de procesar de alguna manera y de forma individual los valores que se van introduciendo en el puerto. Como he dicho anteriormente, MSRS tiene en mente constantemente el desarrollo de aplicaciones concurrentes, así que para poder procesar esas tareas tenemos que tener nuestro propio ThreadPool.

Dentro de MSRS, tenemos dos clases especiales, Dispatcher y DispatcherQueue, que nos permite crear un dispatcher para generar Threads trabajadores que proceses las tareas provenientes de los Port<T> y DispatcherQueue que nos va a permitir que podamos encolar las tareas de procesamiento de los puertos, pudiendo así procesar desde varios puertos en la misma cola. Cada tarea individual se procesa en un Thread diferente para que así la aplicación sea concurrente. Estas dos clases son las clases que MSRS nos provee para que nosotros podamos escribir el procesamiento de las tareas. Podemos configurar el número de Threads que queremos por Dispatcher y puede haber más de un Dispatcher por proceso y dominio de aplicación.

Pues bien una vez que tenemos definido como se van a procesar las tareas, tenemos que sacar los elementos de los Port<T> y especificar el delegado que será el que se encargue de procesar los elementos de ese puerto. Para esta tarea tenemos los Arbiters, que es una clase estática que permite crear un proceso automático para extraer los valores del Port<T> y procesarlos. Aquí tenemos un ejemplo completo.

public void SimplePort()
{
    Port<string> puerto = new Port<string>();

    Dispatcher d = new Dispatcher(0,
        ThreadPriority.Normal,
        DispatcherOptions.UseBackgroundThreads,
        ApartmentState.STA, "ejemplo");

    DispatcherQueue q = new DispatcherQueue("cola", d);

    Arbiter.Activate(q,
        Arbiter.Receive<string>(true, puerto, 
        delegate(string item)
            {
                Console.WriteLine(item);
            }
    ));

    for (int x = 0; x < 1000; x++)
    {
        puerto.Post(DateTime.Now.ToString());
    }
}

Si nos fijamos con la clase Arbiter, activamos el procesamiento en el DispatcherQueue (q), los elementos que se van recibiendo del Port<T> puerto. Además de especificar con un delegado anónimo el método que procesará los mensajes recibidos en el Port<T>.

Este es el modo sencillo y automático de procesar las tareas restantes del puerto, sin nos fijamos un poco más abajo podemos encontrar como se postean los mensajes en el Port<T> y esos mensajes se van procesando de manera asíncrona.

Podemos tener varios DispatcherQueue en un Dispatcher, y el motivo es que podemos querer diferentes tipos de retención de mensajes en nuestra cola. Imaginemos por un momento que estamos procesando imágenes procedentes de la cámara del robot, pero por algún motivo tenemos 50 imágenes restantes por procesar, por motivos de rendimiento solo nos puede interesar procesar las últimas 25 imágenes recibidas y que las que se quedaron atrás en el tiempo simplemente que se descarten.

Al final la clase Arbiter a lo que se dedica es a procesar Tareas en los Dispatcher, tareas que están definidas en la interfaz ITask. Está interfaz que nosotros podemos implementar de alguna manera para poder definir nuestras propias tareas, es la clase Arbiter la que se encarga de exponer esa funcionalidad de Tareas de una manera más comida para el programador sin tener que estár constantemente implementando la interfaz.

De hecho podemos utilizar los Dispatcher sin necesidad de Port<T> simplemente como un procesador de tareas. En este ejemplo de código lo podemos ver.

void RunFromHandler()
{
    Dispatcher d = new Dispatcher(2, "Test Pool");

    DispatcherQueue q = new DispatcherQueue("Test Queue", d);
    
    ITask[] t = new ITask[100000];
    for (int x = 0; x < t.Length; x++)
    {
        t[x] = Arbiter.FromHandler(SimpleHandler);
    }
    Arbiter.Activate(q, t);
}


void SimpleHandler()
{
    
    Console.WriteLine("Start Simple Handler Thread {0}", Thread.CurrentThread.ManagedThreadId);
    for (int i = 0; i < 10; i++)
    {
        Wait(10);
        Console.Write(i + " ");
    }

    Console.WriteLine("Finished Simple Handler Thread {0}", Thread.CurrentThread.ManagedThreadId);
}

Esto es una breve introducción del CCR que seguiré expandiendo en otros artículos, mostrado como se pueden hacer composiciones de tareas más complejas y como este CCR se puede usar en aplicaciones que no sean robóticas ni dentro del MSRS en donde la concurrencia es un problema desde el inicio del proyecto.

[Evento] Depuración y Optimización Avanzada de Aplicaciones (recursos)

Hola a todos!!. Primero dar gracias a todos por haber venido a verme a la charla, y gracias también por los mails de agradecimiento!!.

La presentación de power point de la charla.

evento-depuracion

El sitio web de ejemplo aquí.

TinyGet (Herramienta para hacer las peticiones a la web) está disponible como IIS 6 Resource ToolKit http://support.microsoft.com/kb/840671

Libros:

CLR Via C#

clip_image001

Este libro es uno de los mejores. Explica el funcionamiento del CLR (Common Language Runtime) expuesto desde C#.

http://www.amazon.com/CLR-via-Second-Pro-Developer/dp/0735621632%3FSubscriptionId%3D15HRV3AZSMPK0GXTY102%26tag%3Die8suggestion-20%26linkCode%3Dxm2%26camp%3D2025%26creative%3D165953%26creativeASIN%3D0735621632

Professional .NET Framework 2.0

clip_image002

http://www.amazon.com/Professional-NET-Framework-2-0-Programmer/dp/0764571354%3FSubscriptionId%3D15HRV3AZSMPK0GXTY102%26tag%3Die8suggestion-20%26linkCode%3Dxm2%26camp%3D2025%26creative%3D165953%26creativeASIN%3D0764571354

 

Windows Internals

Windows® Internals: Including Windows Server 2008 and Windows Vista, Fifth Edition (PRO-Developer)

Windows Internals es el mejor libro de referencia de Windows, esta 5º edición incluye Windows Vista y Server 2008. Es un libro fundamental para conocer la plataforma Windows.

http://www.amazon.com/Windows%C2%AE-Internals-Including-Windows-PRO-Developer/dp/0735625301%3FSubscriptionId%3D15HRV3AZSMPK0GXTY102%26tag%3Die8suggestion-20%26linkCode%3Dxm2%26camp%3D2025%26creative%3D165953%26creativeASIN%3D0735625301

Blogs:

http://blogs.technet.com/markrussinovich/ (Mark Russinovich) Creador de la suite sysinternals

http://www.wintellect.com/cs/blogs/jrobbins/

http://blogs.msdn.com/tess/

Herramientas:

En este blog en post anteriores podeís encontrar varios post de como se configura el entorno de depuración con símbolos y demás. Además de eso varias herramientas útiles.

Process explorer, sin duda la herramienta definitiva para extraer información de que está haciendo Windows. Nos muestra información sobre rendimiento, consumo de memoria, threads, manejadores y demás.

Process Monitor, igual que process explorer solo que es un registro de la actividad de Windows, en tres categorías Threads, Registro y IO

Notas:

Muchos de vosotros me habeís enviado mails preguntándome varias cosas respondo a todo el mundo desde aquí porque son preguntas interesantes.

Q: Yo vivia muy feliz antes de este evento, ahora siento miedo ¿Porqué?

A: Bueno, las razones son varias, pero creo que al exponer lo complicado que puede ser el desarrollo de aplicaciones uno siente miedo ante esto. Teniendo en cuenta que durante el evento se ha hablado de .net que es un lenguaje de alto nivel, en el que hay muchas cosas que ayudan a programar y hacer las cosas más fáciles, resulta más chocante aún. El caso es que la informática es una de las disciplinas más complicadas que existen por ahí afuera. Y si no me creeis leeros este ensayo de Edsget W. Dijkstra desde aquí.

Q: Todos los ejemplos han sido de una aplicación ASP.NET atacando directamente al proceso w3wp.exe, ¿Es posible hacer esto con otro tipo de aplicaciones?

A: Desde luego las demos estaban preparadas así pero se pueden generar dumps de aplicaciones de escritorio ya sea Windows Forms, WPF o lo que sea. De hecho este tipo de depuración es especial porque está pensada para cuando no se está delante de la maquina que tiene el problema.