Tag Archives: C#

Mis primeras estrellitas

Entre viernes y sábado gané mis primeras estrellitas del Desarrollador Cinco Estrellas.  Pude sacar el tiempo para presentar los siguientes exámenes.

Estrella #0:

Fundamentos de la programación.

Estrella #1:

.NET Framework.

C#.

Estrella #2:

Aplicaciones web con ASP.NET.

Aplicaciones Windows con WinForms.

Mi segunda estrellita.

Esta semana va a estar un poco difícil que vuelva a dedicar un tiempo a estas labores astronómicas, sin embargo voy a hacer mi mejor esfuerzo.

Serialización de objetos en XML con C#.NET

De manera análoga a como en Java es posible realizar la serialización de clases a XML, encontré que también es posible hacerlo en C#.NET.  Para esto es necesario que la clase a ser serializada exponga los atributos que desea publicar a través de propiedades.

Para la demostración creé una clase XMLSerializable de la cual heredarán las otras que requieran de este tipo de serialización.  Esta clase abstracta provee los métodos load y save para realizar automáticamente la serialización de sus hijos.

La serialización de los objetos se realiza con el siguiente código.

        public void save(String filename)
        {
            // Creates the writing stream
            StreamWriter w = new StreamWriter(filename);
            // Creates the XML serializer with its type
            XmlSerializer s = new XmlSerializer(this.GetType());
            // Serializes the object (this self -> son)
            s.Serialize(w, this);
            // Closes the output stream
            w.Close();
        }

La carga de objetos serializados requiere de un par de verificiones adicionales.

        public bool load(String filename)
        {
            bool success = false;
            // Checks if the source file exists, otherwise there is nothing to do
            if (File.Exists(filename))
            {
                // Creates the reading stream
                StreamReader sr = new StreamReader(filename);
                // Creates the text reader from the stream
                XmlTextReader xr = new XmlTextReader(sr);
                // Creates the XML (de)serializer with its type
                XmlSerializer xs = new XmlSerializer(this.GetType());
                // Temporary storage object
                object c;
                // Checks if the received data can be deserialized
                if (xs.CanDeserialize(xr))
                {
                    // Deserialize the incoming data
                    c = xs.Deserialize(xr);
                    // Gets the type (definition) of the object
                    Type t = this.GetType();
                    // Retrieve the exposed attributes by properties
                    PropertyInfo[] properties = t.GetProperties();
                    // Walks thru all properties and loads its values on
                    // the local attributes
                    foreach (PropertyInfo p in properties)
                    {
                        p.SetValue(this, p.GetValue(c, null), null);
                    }
                    success = true;
                }
                // Closes the reader and the stream
                xr.Close();
                sr.Close();
            }
            return success;
        }

Con estas facilidades se crea una clase Empleado con atributos básicos.

    public class Empleado : XMLSerializable
    {
        ////////////////////////////////////////////////////////////////
        private int      codigo;
        private String   nombre;
        private float    salario;
        private DateTime fechaNacimiento;
        ////////////////////////////////////////////////////////////////
        // Resto del codigo
    }

Es necesario para la serialización que el objeto tenga propiedades de los atributos a exponer.

        public int Codigo
        {
            get { return codigo; }
            set { codigo = value; }
        }
        public String Nombre
        {
            get { return nombre; }
            set { nombre = value; }
        }
        public float Salario
        {
            get { return salario; }
            set { salario = value; }
        }
        public DateTime FechaNacimiento
        {
            get { return fechaNacimiento; }
            set { fechaNacimiento = value; }
        }

También es necesario que la clase cuente con un constructor sin parámetros además de los que requiera normalmente.

        public Empleado()
        {
            this.codigo          = -1;
            this.nombre          = "Sin nombre";
            this.salario         = -1;
            this.fechaNacimiento = new DateTime(1990, 01, 01);
        }
        ////////////////////////////////////////////////////////////////
        public Empleado(int codigo, String nombre, float salario, DateTime fechaNacimiento)
        {
            this.codigo          = codigo;
            this.nombre          = nombre;
            this.salario         = salario;
            this.fechaNacimiento = fechaNacimiento;
        }

Por facilidad también se sobreescribió el método ToString() para generar su representación en cadena.

        override public String ToString()
        {
            return String.Format("[Empleado: codigo = {0}, nombre = \"{1}\", salario = ${2}, fechaNacimiento = {3}]",
                                    this.codigo, this.nombre, this.salario, this.fechaNacimiento);
        }

Con todo esto, la aplicación de prueba crea en el primer paso a un objeto Empleado y le da valores iniciales a sus atributos, después de esto solicita su serialización.

            Empleado empleado = new Empleado(123, "Pepito Pimentón", 123456.789f, DateTime.Now);
            empleado.save("empleado.xml");

Posteriormente se crea un segundo Empleado que almacenará la información recuperada de la serialización.  Si la prueba tiene éxito, la información de los dos Empleados deberá coincidir.

            Empleado empleado2 = new Empleado();
            empleado2.load("empleado.xml");

La información presentada a través de salida estándar al ejecutar la aplicación de demostración deberá ser algo similar a lo siguiente.

Writing Object: [Empleado: codigo = 123, nombre = "Pepito Pimentón", salario = $123456,8, fechaNacimiento = 14/09/2008 11:12:10 p.m.]
Read Object:    [Empleado: codigo = 123, nombre = "Pepito Pimentón", salario = $123456,8, fechaNacimiento = 14/09/2008 11:12:10 p.m.]

El documento XML generado es bastante explícito y fácil de realizar su manipulación manual si se llegara a requerir.



  123
  Pepito Pimentón
  123456.789
  2008-09-14T22:39:52.888-05:00

Enlaces:

Obtener lecturas de un GPS USB desde Java/C#

La tarea del día de hoy era obtener la posición actual desde un GPS utilizando Java o C#.  Para esto contaba con un GPS Garmin eTrex Summit HC que por supuesto cuenta con un puerto USB.

Extrañamente concluyo que no hay un método confiable ni portable para leer información desde los puertos USB para ninguno de los dos lenguajes.  Para Java encontré varias librerías pero aún inmaduras o con soporte para sólo un sistema operativo, prefiero el soporte para Windows y Linux por lo menos.  Para C# igual horizonte, nativamente aún no es soportado como si lo es el SerialPort y la mejor opción parece ser #usblib de los mismos creadores de SharpDevelop, sin embargo no pude encontrar un ejemplo que indicara su viabilidad.

Con este oscuro panorama terminé explorando un estilo de solución que no es de mi mayor agrado: el ejecutar aplicaciones de terceros para obtener y procesar su salida estándar.

Para esto instalé un software muy útil llamado GPSBabel el cual se puede descargar para Windows, Linux y Mac, incluyendo su código fuente escrito en lenguaje C.  La distribución de Linux está basada en RPM los cuales no fueron instalables en mi estación de trabajo basada en Ubuntu, sin embargo se encuentra disponible el paquete para instalarse desde la distribución de paquetes de Ubuntu.

El comando gpsbabel permite obtener desde línea de comando la información contenida en el GPS, incluyendo por supuesto, su ubicación actual.

$ sudo /usr/bin/gpsbabel.exe -i garmin,get_posn -f usb:

El resultado es algo de este estilo: (probado desde Armenia/Qundío)

4.536692N 75.669346W Position/Position

Extrañamente la distribución de Windows incluye además la fecha/hora de la ejecución, hecho que en Linux no sucede.

Wed Sep 10 22:42:46 2008
4.536692N 75.669346W Position/Position

La aplicación intermedia fue desarrollada en C# debido a la afinad de mi compañero de desarrollo con ese lenguaje.  Fue interesante para aprender algunos detalles particulares del lenguaje a los cuales nos vimos enfrentados durante el desarrollo.

Lo que hice fue crear un método estático (AccesoGPSBabel.obtUbicacionActual) que basado en la ubicación del comando gpsbabel retorna un Hashtable con la latitud/longitud actual o null en caso de suceder algún problema.

La ejecución del programa externo se realiza mediante un objeto System.Diagnostics.Process al cual se le indica la ubicación del archivo ejecutable, los argumentos de línea de comando y se le informa que estamos interesados en obtener su salida estándar.  La aplicación (o un enlace a) gpsbabel deberá estar ubicada en el mismo directorio del ejecutable de la aplicación principal, incluyendo el archivo libexpat.dll para la versión de Windows.

            System.Diagnostics.Process proc = new System.Diagnostics.Process();
            proc.EnableRaisingEvents = false;
            proc.StartInfo.FileName = aplicacion;
            proc.StartInfo.Arguments = "-i " + protocolo + ",get_posn -f usb:";
            proc.StartInfo.RedirectStandardOutput = true;
            proc.StartInfo.UseShellExecute = false;
            proc.Start();

Para recibir la información proveniente de la salida estándar se obtiene su StreamReader de la siguiente manera.

            String datos = proc.StandardOutput.ReadToEnd().Trim();

Su procesamiento no se aleja de la carpintería normal del manejo de cadenas con String.Split junto con un detalle adicional, la información aparece con el posfijo N/S, W/E; yo prefería que fuera un solo valor numérico siendo positivo o negativo según su ubicación con respecto a la referencia, tal y como lo maneja GoogleMaps y otros paquetes similares.

Para esto, al procesar las líneas recibidas de la salida estándar recorté la subcadena de manera que no tomara el último carácter de las coordenadas.

            latitud     = secciones[0].Trim().Substring(0, secciones[0].Length - 1);
            latitudDir  = secciones[0].Trim().Substring(secciones[0].Length - 1, 1);
            longitud    = secciones[1].Trim().Substring(0, secciones[1].Length - 1);
            longitudDir = secciones[1].Trim().Substring(secciones[1].Length - 1, 1);

Y después las multipliqué por su inverso aditivo en los casos en que correspondiera.

            Double Latitud = Double.Parse(latitud) * ((latitudDir.Equals("S")) ? -1 : 1);
            Double Longitud = Double.Parse(longitud) * ((longitudDir.Equals("W")) ? -1 : 1);

Sucedió un problema.  El método Double.Parse estaba convirtiendo erróneamente los valores porque el GPS los enviaba con el punto como separador decimal y la Configuración Regional de la máquina Windows que estabamos utilizando usaba la coma.

Como la intención era que la aplicación no sólo funcionara entre diferentes configuraciones regionales sino que lo hiciera también entre diferentes sistemas operativos (Linux con Mono) me dediqué a buscar una solución portable.

Para esto se obtiene al separador decimal basado en la configuración regional del sistema operativo.

            String separadorDecimal = System.Globalization.CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator;

Se realiza la corrección de separadores (de punto al utilizado por la configuración regional local) antes de manipular los datos con el Double.parse.

            latitud = secciones[0].Trim().Substring(0, secciones[0].Length - 1).Replace('.', separadorDecimal[0]);
            longitud = secciones[1].Trim().Substring(0, secciones[1].Length - 1).Replace('.', separadorDecimal[0]);

Con esto se solucionó el problema.  Para terminar, se utilizó un Hashtable para almacenar la información y retornársela al objeto que la solicitó.

            resultado.Add("latitud", Latitud);
            resultado.Add("latitudDireccion", latitudDir);
            resultado.Add("longitud", Longitud);
            resultado.Add("longitudDireccion", longitudDir);

Esta no es la solución que pensaba darle al problema, sin embargo la implementación fue interesante y gracias a la flexibilidad que provee gpsbabel, espero que sea funcional con una amplia gama de dispositivos, no solamente con los de la marca Garmin.

De todas formas me queda pendiente la tarea de buscar la forma de acceder directamente al puerto USB para leer su informacion en forma de cadenas NMEA, la cual me parece la solución menos dependiente posible de su entorno.

Enlace: Distribución con las fuentes del proyecto (C# basado en MonoDevelop).

Microsoft Kid's Corner

Buscando información en internet para el próximo capítulo de la serie de documentos J2ME encontré algo interesante del otro lado de la de las plataformas de desarrollo.

Microsoft Kid's Corner es una sección de documentación para enseñarle a programar a niños pequeños utilizando, obviamente, las herramientas de .NET que el mismo Microsoft desarrolla.

Sin profundizar en estos temas por ahora, se encuentran ya disponibles dos libros electrónicos acerca de la programación con C# y VB, así como varios cursos didácticos relacionados con estas tecnologías y haciendo énfasis en el desarrollo de aplicaciones de escritorio y aplicaciones web.

Para algo podrán servir en el futuro estos documentos, todo lo relacionado con educación es siempre bienvenido.

Enlace: Microsoft Kid's Corner.

Servicio web entre C#.NET y Java

Aproveché este fin de semana para en un par de horas desarrollar un ejemplo de servicio web escrito en C#.NET para ser consumido por un cliente desarrollado en Java, utilizando las herramientas gratuitas Microsoft Visual Web Developer 2008 Express Edition y NetBeans 6.0.1 respectivamente.

El servicio como tal es muy simple, envía correo a través de un servidor SMTP, sin embargo es un ejercicio académico interesante. La documentación del desarrollo la escribí a manera de guía así que podrá ser de utilidad para quienes esten interesados en aprender a desarrollar este tipo de aplicaciones.

Enlaces:

  1. Guía de desarrollo
  2. Código fuente.