Leer datos desde el puerto serie de una SAI / UPS

Buenas:


Dentro de poco me llega un SAI o UPS que tiene dos puertos, USB y el RS232. El que voy a sar es el RS232 o conocido como puerto serie de toda la vida y el PC nuevo también usa el RS232. Espero no usar el pin 4 DTR para controlar el puerto serie, ajajajaja.





Aquí abajo, muestra los mensajes de aviso y error. En el display de la UPS solo muestra códigos en el cual después para entender lo que me están hablando debo mirar esta tabla de abajo.





Como puedes ver arriba, por ejemplo: Código A64. Como no se que es el código A64 cuando la UPS directamente l muestra con su display, debo mirar esta table y dice:
A64 => Aviso sobrecarga.

Entonces con Arduino y el puerto RS232 debo extraer la información y que me lo muestre en textos en un LCD de Arduino así:

A64: Aviso de sobrecarga.

Sea con un display de 20x4 o otro que me quepa muchos comentarios o descripcioines de los avisos o fallos del UPS.

Según el fabricante, me dice que mire esta parte, que es la que estoy buscando, los Flags.





Antes de tocar Arduino, prefiero hacer una Interfaz con Visual Studio .Net. Directmente con el puerto RS232.

Solo lee los avisos o erroree mostrados desde la UPS hacia el PC. Solo toca interpretar lo que me cuenta este documento en PDF que lo dejo para que lo descarguen.

Si tiene alguna idea como extraer esa información me lo hacen saber y empiezo a programar. La parte que estoy haciendo ahora solo es preparar la interfaz, ya iré mostrando avances poco a poco, por supuesto, luego iré por Arduino al derecho y al revés, ejjejej.

Descarga PDF.


Sabiendo todo esta información.


Mi intención es crear una interfaz con Windows Form de C#. Usar el componente serialPort1 que lo arrastro al formulario y pongo un label.


La pregunta es:


¿Cómo muestro los datos de la UPS indicado en la tercera captura arriba o en el manual en C#?


Esa es la cuestión.


Que tengan buen día. A scar esto adelante.
;)


PD: Me cuesta creer que todavían saquen PC y UPS con comunicación con RS232 directamente. Es de agradecer.
 
Ya que es para probar, porque no usas algun programa para leer el puerto serie tipo consola, como el propio de arduino (no se si reconocerá al puerto fisico), como el "siow" del compilador PICC.
Sino sube lo que tienes y veo en qué puedo ayudarte
 
Configura el puerto serie para que al dar "enter" solo coloque el caracter "<cr>" que vendria a ser lo mismo que "\r" (Carriage Return, Retorno del Carro), o envia desde C#, lo siguiente "X87\r" al puerto serie SIN usar "WriteLine", solo usa "Write"
SerialPort.Write Method (System.IO.Ports)
Y lee lo que recibiras. Luego haz un troceado y con switch-case realizas lo que quieras.

Te recomiendo primeramente uses algo ya hecho, como Putty, o lo que te comenté antes
 
¿Y por qué no simplemente usas los programas que ya existen para eso?. Cada marca provee su propio software, que generalmente es una interfaz web.
 
Buenas:

Me llegó la UPS o SAI.

Haciendo las primeras pruebas.

Captura8.PNG
Escribo X72 en un terminal de RS232.

Captura9.PNG

Haciendo pruebas sobre su respuesta y todavía sin programarlo en el puerto serie. Me va funcionando.
Captura10.PNG

Código fuente de C#:
C#:
using System;

namespace Delimitador_consola_01
{
    class Program
    {
        static void Main(string[] args)
        {
            // Título de la ventana.
            Console.Title = "Probando delimitador";

            // Tamaño de la ventana, x, y.
            Console.SetWindowSize(80, 15);

            // Color de fondo.
            Console.BackgroundColor = ConsoleColor.Gray;

            // Color de las letras.
            Console.ForegroundColor = ConsoleColor.Black;

            // Limpiar pantalla y dejarlo todo en color de fondo.
            Console.Clear();

            // Visible el cursor.
            Console.CursorVisible = true;

            char[] delimitadorCaracter = { ' ', ',', '#' };
            string frase = "#2000,1400,230,45.0,55.0,8.6";
            string[] palabras = frase.Split(delimitadorCaracter);

            foreach (string palabra in palabras)
            {
                Console.WriteLine($"<{palabra}>");
            }

            Console.WriteLine();
            Console.WriteLine("La potencia aparente es {0} VA.", palabras[1]);
            Console.WriteLine("La potencia activa es de {0} Watts.", palabras[2]);
            Console.WriteLine("El voltaje de salida predeterminado de fábrica es de {0} voltios.", palabras[3]);
            Console.WriteLine("El rango de frecuencia nominal está entre {0} y {1} Hz.", palabras[4] , palabras[5]);
            Console.WriteLine("La corriente nominal es de {0} amperios.", palabras[6]);

            // Pulse cualquier tecla para salir.
            Console.ReadKey();
        }
    }
}

Por ahora en modo consola de C#, luego en Visual C#. También se hará con el LCD 20x4 en Arduino.

Poco a poco voy publicando avances.

Saludos camaradas.
 
Fijate que segun lo que pusiste, si ocurre lgun problema, la bandera es el "-" despues del numeral, osea "#-", y el codigo de error.
Podes filtrar ese caracter en un if, o en un switch-case para saber si lo que te llega es un error, o son los datos.

Parece interesante... 👍
 
Buenas:

Deja ver si me contesta, le puse por e-mail esto.
Buenas:

Voy cogiendo el truco sobre manejar datos del puerto serie con la UPS del SPS 2000 ADV T. Ya hay gente preparada para comprar la misma UPS en foros y mis propios amigos. También porque usa señal senoidal pura. Hay que mirar todo.

Al enviar comandos, me devuelve a veces este tipo de mensajes en el terminal, por ejemplo:
#-0<cr>
#-1<cr>
#-2<cr>
#-10<cr>

Por nombrar algunos.

Si no es molestia. ¿Tienen datos o documento que significa estos errores?

Por intuición, cuando devuelve #-0<cr> comando correcto,cuando llega los demás, es error o otra cosa que no lo se con certeza.

Que sigan incluyendo puertos series en las futuras máquinas. ;)

Espero su respuesta.

Muchas gracias por su atención, que tengan buen día a tod@s.

Vamos haber si me envía algo. Lo del esquema eléctrico de la parte de la placa donde se conecta USB y RS232 no me deja porque dice que es muy confidencial. Lo del protocolo de comunicación para controlar la UPS de milagro me lo han dado. Dejo claro que no siempre responden, se quedan callados.

Como la cosa me está funcionando por ahora, ya haré una pantalla 20x4 como indica abajo.


También con otra más esta vez con GLCD.


Antes de todo esto, haré mi propio interfaz sea normal con gráficos de todo tipo y modo consola. Ya que hacerlo en modo consola, es muy parecido al trabajarlo con un LCD 20x4 de Arduino.

Por supuesto, habrá menús y submenús para estas LCD.

@DJ T3
Exacto, o uso esos errores o comandos de respuesta que todavía no se que significa exactamente y lo haré con if-else o Switch. Por cierto, me di cuenta que devuelve #0<cr> es que la cosa está bien.

Sigo investigando.
 
Última edición:
Siguiendo con pruebas.

Pulso B y luego X72. No se como interpretar que C# incluido Arduino lo identifique como datos independientes entre si.

 

Adjuntos

  • Captura12.PNG
    Captura12.PNG
    20 KB · Visitas: 59
Segun de lo que entendi.
O mandas la consulta y seguidamente te quedas en un loop hasta que llegue la respuesta (pon algun "timeout" y una bandera, porque si hay un problema va a quedar en infinito), o revisas bien los patrones de cada consulta, y decidis a donde se procesan esos datos (funcion). Por ejemplo la que subiste, uno tiene separado por puntos y el otro por comas, aparte de la longitud que es distinta
 
No es eso, para ello se le pone un timer. implemente como sabe el programa los tipos de lecturas que reciben para poder interpretarlos. Es lo que estoy investigando aquí un ejemplo.

C#:
// Entrada con B y X72 con dos comandos diferentes.

using System;

namespace Delimitador_consola_02
{
    class Program
    {
        static void Main(string[] args)
        {
            // Título de la ventana.
            Console.Title = "Probando delimitador 2";

            // Tamaño de la ventana, x, y.
            Console.SetWindowSize(80, 50);

            // Color de fondo.
            Console.BackgroundColor = ConsoleColor.Gray;

            // Color de las letras.
            Console.ForegroundColor = ConsoleColor.Black;

            // Limpiar pantalla y dejarlo todo en color de fondo.
            Console.Clear();

            // Visible el cursor.
            Console.CursorVisible = true;

            char[] delimitadorCaracterB = { '#', ',', 'O', 'I', 'L', 'B', 'V', 'F', 'H', 'R', 'S' };
            char[] delimitadorCaracterX72 = { '#', ',' };
            string fraseB = "#I222.2O222.8L000B100V27.5F50.2H50.2R0120S„Ѐ„À"; // Comando B.
            string fraseX72 = "#2000,1400,230,45.0,55.0,8.6"; // Comando X72.
            string[] palabrasB = fraseB.Split(delimitadorCaracterB);
            string[] palabrasX72 = fraseX72.Split(delimitadorCaracterX72);

            Console.WriteLine();
            Console.WriteLine("Comando B:");
            foreach (string palabraB in palabrasB)
            {
                Console.WriteLine($"<{palabraB}>");
            }
            Console.WriteLine();
            Console.WriteLine("El voltaje de la utilidad es de {0} voltios. ", palabrasB[2]); // I.
            Console.WriteLine("El voltaje de salida del UPS es de {0} voltios. ", palabrasB[3]); // O.
            Console.WriteLine("La carga actual de UPS es del {0} por ciento. ", palabrasB[4]); // L.
            Console.WriteLine("La capacidad de la batería es del {0} por ciento. ", palabrasB[5]); // B.
            Console.WriteLine("El voltaje de la batería es de {0} voltios. ", palabrasB[6]); // V.
            //Console.WriteLine("La temperatura del gabinete del UPS es de {0} grados centígrados. ", palabrasB[7]); // T. No hay T. en mi versión.
            Console.WriteLine("La frecuencia de salida del SAI es de {0} Hz. ", palabrasB[7]); // F.
            Console.WriteLine("La frecuencia de salida del SAI es de {0} Hz. ", palabrasB[8]); // H.
            Console.WriteLine("El tiempo de funcionamiento restante de la batería es de {0} minutos. ", palabrasB[9]);
            Console.WriteLine("S: ", palabrasB[10]);

            Console.WriteLine();
            Console.WriteLine("Comando X72:");
            foreach (string palabraX72 in palabrasX72)
            {
                Console.WriteLine($"<{palabraX72}>");
            }
            Console.WriteLine();
            Console.WriteLine("La potencia aparente es {0} VA. (Voltio Amperio.)", palabrasX72[1]);
            Console.WriteLine("La potencia activa es de {0} W. (Watios).", palabrasX72[2]);
            Console.WriteLine("El voltaje de salida predeterminado de fábrica es de {0} V. (Voltios).", palabrasX72[3]);
            Console.WriteLine("El rango de frecuencia nominal está entre {0} y {1} Hz.", palabrasX72[4], palabrasX72[5]);
            Console.WriteLine("La corriente nominal es de {0} A. (Amperios).", palabrasX72[6]);

            // Pulse cualquier tecla para salir.
            Console.ReadKey();
        }
    }
}
 
Mmmm.... Pero ahi no clasificas nada, y si llegas a recibir menos o mas datos de los que tenes ya en los arrays, vas a recibir una excepcion (un error), ya que te vas a ir de indice, en el caso de que tengas menos datos, y en el caso contrario quizas no corresponda a los datos.
 
Captura14.PNG

Siguiendo con prototipos de programas, antes de empezar con Arduino. Estoy con el comando K60 para activar y desactivar el zumbador que por fin me funciona y se muestra en el LDC del SAI o UPS.

Los comandos son estos.
C#:
            byte[] mBuffer = Encoding.ASCII.GetBytes("K60:1\r"); // Comando K60:1 activar.
            serialPort1.Write(mBuffer, 0, mBuffer.Length);

Por poner un ejemplo.

Como dice el amigo. se envía así: K60:1\r"

No olvidar el \r o no funciona nada.

Ahora estoy con la página 15 / 21 que muestro un pequeño avance pero no funciona porque tengo que aprender como se manejas bien los cambios de los bits en byte en C#. En asm de los PIC todo bien, pero en C#, casi nunca lo he hecho, así que a experimentar y aprender, jejejejejje.

Captura15.PNG

Gracias por seguir... ;)
 
Creo que el USB es un puerto serie virtual, asi que si tienes un shield o similar para el PIC o Atmega, o el microcontrolador que vayas a usar, ya tienes una idea.

Conectalo al PC y verifica en los dispositivos, a ver cómo lo toma
 
Lo tendré en cuenta. En estos momentos peleándome como obtener de los 6 Byte que me llega, mostrar cuando un bit se ha modificado.

Para que te hagas una idea.

Haciendo un programa hecho con Windows Form bajo .Net FrameWotk 4.7. Si envío un comando por el puerto serie que se llama X82, recibo un mensaje que en ASCII no se entiende porque usa caracteres raros.

Lo paso a hexadecimal y en binario.

Envío un comando, ocurre algún evento y en Windoes Form me aparece cógios en HEX y en BIN. Se puede cambiar varios bit en un único Byte.

Por ejemplo, si me llega este dato.

Hexadecimal: 23C797C0B00D

Binario: 00100011 11000111 10010111 11000000 10110000 00001101

Un ejemplo, es poner muchos labels en cada bits y una tabla de cada Byte recibido. Simpre son la misma cantidad de bytes ya que en realdiad hace de Flags.

Centrándonos en el binario, ya que hay 6 Bytes.

C#:
00100011 11000111 10010111 11000000 10110000 00001101
-------- -------- -------- -------- -------- --------
 Byte I  Byte a    Byte b   Byte c   Byte d   Byte F

En el Byte c que corresponde al 11000000 quiero leer el bit 3 que corresponde al "Extractor" indicado en la tabla de abajo. Cada Byte tiene su tabla, ahora nos centramos en un Byte y ver los estados de los bits.

bit:c Dato Función.
7 = 1 Motor A.
6 = 1 Motor B.
5 = 0 Luz A.
4 = 0 Luz B.
3 = 0 Extractor.
2 = 0 Alarma.
1 = 0 Persiana.
0 = 0 Ventilador

El Byte c que ahora contiene estos bits que son 11000000, me llega una nueva trama de Bytes y precisamente este, cambia de 11000000 a 11001000. Solo ha cambiado un bit que es el 3 en el Byte c.

Cada bit tiene su label para mostrarlo en el formulario de Windows. La tabla de abajo se actualiza.

bit:c Dato Función.
7 = 1 Motor A.
6 = 1 Motor B.
5 = 0 Luz A.
4 = 0 Luz B.
3 = 1 Extractor.
2 = 0 Alarma.
1 = 0 Persiana.
0 = 0 Ventilador

Antes el Byte c del bit 3 que es el Extractor estaba a 0, ahora es 1.

En resumen. Quiero saber como se leen los bits que me llegan del puerto serie.

Saludos.

PD: En esta clase de programas que no suelo usar, es como las aves, fáciles de ver, difíciles de alcanzar.
 
Hola:

Parece ser que ya acabé el código y me funciona todo menos la codificación que lo dejé un poco de lado.

La programación está al estilo chapuza y hay que mejorarlo. Tardé más en traducir y escribir los textos que codear. Xddddd.

Dejo un ejemplo del comando B<cr>




Código C#:
C#:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Delimitador_consola_03
{
    class Program
    {
        static void Main(string[] args)
        {
            #region Configuración ventana.
            // Título de la ventana.
            Console.Title = "Probando manipulación de cadena";

            // Tamaño de la ventana, x, y.
            Console.SetWindowSize(100, 35);

            // Color de fondo.
            Console.BackgroundColor = ConsoleColor.DarkBlue;

            // Color de las letras.
            Console.ForegroundColor = ConsoleColor.Yellow;

            // Limpiar pantalla y dejarlo todo en color de fondo.
            Console.Clear();

            // Visible el cursor.
            Console.CursorVisible = true;
            #endregion

            // Cree una codificación ISO-8859-1.
            Encoding ISO_8859_1 = Encoding.GetEncoding("ISO-8859-1");

            #region Variables.
            // Partir a trocitos estos caracteres.
            char[] delimitadorComandoB = { '#', ',', 'O', 'I', 'L', 'B', 'V', 'F', 'H', 'R', 'S', };

            // Es la cadena de caracteres que me llegó desde el puerto serie.
            // En este ejemplo lo dejo en la varible directamente.
            // Una cadena completa empieza con # y termina en <cr>, o lo que es lo mismo, /r.
            string respuestaB = "#I223.3O224.0L000B100V26.4F50.2H50.2R0080S„€€„À"; // Comando B.

            // Se guarga en este array tipo string los datos ya partidos a tozos.
            string[] palabrasB = respuestaB.Split(delimitadorComandoB);

            // Tabla S1 descripción.
            string[] DESCRIPCION_S1 =
            {
                "Indica que este byte está disponible y es válido. [Siempre 1]",             // Posición [0].
                "Indica falla de energía de la red pública; ver detalles en S2.0 y S2.1.",
                "Indica que la capacidad de la batería es menor que el umbral de apagado.",
                "Indica que el zumbador está en estado de pitido.",
                "Indica que la prueba de batería se está procesando.",
                "Indica que el apagado programado está pendiente.",
                "Indica que la restauración programada está pendiente.",
                "Indica falla de hardware; ver detalles sobre el comando X71."               // Posición [7].
            };

            string[] DESCRIPCION_S2 =
            {
                "Para indicar que este byte está disponible y es válido. [Siempre 1].",
                "--",
                "XX",
                "Indica sobretemperatura del inversor.",
                "Indica que el inversor tiene una falla.",
                "Indica que el inversor está apagado.",
                "Indica que la frecuencia de la utilidad está fuera de rango.",
                "Indica que el voltaje de la red pública está fuera de rango."
            };

            string[] DESCRIPCION_S3 =
            {
                "Para indicar que este byte está disponible y es válido. [Siempre 1].",
                "Indica que la batería está completamente cargada.",
                "Indica que la capacidad de la batería aún es menor que el umbral restaurado del UPS cuando la energía de la red pública restaurado.",
                "Indica que la batería se está cargando.",
                "Indica que la batería se está descargando.",
                "Indica que la capacidad de la batería es inferior al 80 por ciento.",
                "Reservado, debe ser 0.",
                "Indica que la batería no está presente."
            };

            string[] DESCRIPCION_S4 =
{
                "Para indicar que este byte está disponible y es válido. [Siempre 1].",
                "Indica que el bypass es una sobrecarga.",
                "Indica que la derivación está activa.",
                "Indica que la salida sufre un cortocircuito.",
                "Indica que la salida tiene carga.",
                "Indica que la salida está sobrecargada.",
                "Indica que la frecuencia de salida está fuera de rango en bypass.",
                "Indica que el voltaje de salida está fuera de rango en derivación."
            };

            string[] DESCRIPCION_S5 =
{
                "Para indicar que este byte está disponible y es válido. [Siempre 1].",
                "Indica que no hay salida.",
                "Para indicar que el tiempo de ejecución restante es inferior al umbral.",
                "Para indicar que el zumbador está silenciado (no permanente) en este momento.",
                "Para indicar falla de cableado.",
                "Para indicar SAI en modo ECO.",
                "Para indicar UPS en Bypass manual.",
                "Arreglar 0."
            };

            string[] DESCRIPCION_S6 =
{
                "Para indicar que este byte está disponible y es válido. [Siempre 1].",
                "Indica UPS encendido.",
                "Reservado, debe ser 0.",
                "Reservado, debe ser 0.",
                "Reservado, debe ser 0.",
                "Reservado, debe ser 0.",
                "Reservado, debe ser 0.",
                "Reservado, debe ser 0."
            };

            bool boolS1 = true;
            bool boolS2 = false;
            bool boolS3 = false;
            bool boolS4 = false;
            bool boolS5 = false;
            bool boolS6 = false;
            bool boolContador = true;
            bool boolContador2 = false;
            #endregion

            // Muestra los resultados en pantalla.
            Console.WriteLine();
            Console.WriteLine("El voltaje de la utilidad es de {0} voltios. ", palabrasB[2]); // I.
            Console.WriteLine("El voltaje de salida del UPS es de {0} voltios. ", palabrasB[3]); // O.
            Console.WriteLine("La carga actual de UPS es del {0} por ciento. ", palabrasB[4]); // L.
            Console.WriteLine("La capacidad de la batería es del {0} por ciento. ", palabrasB[5]); // B.
            Console.WriteLine("El voltaje de la batería es de {0} voltios. ", palabrasB[6]); // V.
            //Console.WriteLine("La temperatura del gabinete del UPS es de {0} grados centígrados. ", palabrasB[7]); // T. No hay T. en mi versión.
            Console.WriteLine("La frecuencia de salida del SAI es de {0} Hz. ", palabrasB[7]); // F.
            Console.WriteLine("La frecuencia de salida del SAI es de {0} Hz. ", palabrasB[8]); // H.
            Console.WriteLine("El tiempo de funcionamiento restante de la batería es de {0} minutos. ", palabrasB[9]);
            //Console.WriteLine(@"Estos son los bits de estados que no se entiende.S:
            //Aquí hay que leer cada bits como cuando se leyeron cada Byte arriba: ", (char)palabrasB[10]);

            char[] bits = palabrasB[10].ToCharArray();

            Tabla();
            int contador = 0;
            for (int b = 0; b < bits.Length; b++)
            {
                for (int a = 7; a >= 0; a--)
                {
                   
                    Console.Write((((byte)bits[b]) & (1 << a)) >> a);
                    Console.Write(" | ");

                    if ((contador <= 7) && (boolS1 == true))
                    {
                        Console.WriteLine(DESCRIPCION_S1[contador]);

                        if (contador == 7)
                        {
                            boolS1 = false;
                            boolS2 = true;
                            boolContador2 = true;
                            contador = 0;
                        }
                    }
                    else if ((contador <= 7) && (boolS2 == true))
                    {
                        Console.WriteLine(DESCRIPCION_S2[contador]);
                        boolContador2 = false;
                        if (contador == 7)
                        {
                            boolS2 = false;
                            boolS3 = true;
                            boolContador2 = true;
                            contador = 0;
                        }
                    }
                    else if ((contador <= 7) && (boolS3 == true))
                    {
                        Console.WriteLine(DESCRIPCION_S3[contador]);
                        boolContador2 = false;
                        if (contador == 7)
                        {
                            boolS3 = false;
                            boolS4 = true;
                            boolContador2 = true;
                            contador = 0;
                        }
                    }
                    else if ((contador <= 7) && (boolS4 == true))
                    {
                        Console.WriteLine(DESCRIPCION_S4[contador]);
                        boolContador2 = false;
                        if (contador == 7)
                        {
                            boolS4 = false;
                            boolS5 = true;
                            boolContador2 = true;
                            contador = 0;
                        }
                    }
                    else if ((contador <= 7) && (boolS5 == true))
                    {
                        Console.WriteLine(DESCRIPCION_S5[contador]);
                        boolContador2 = false;
                        if (contador == 7)
                        {
                            boolS5 = false;
                            boolS6 = true;
                            boolContador2 = true;
                            contador = 0;
                        }
                    }
                    else if ((contador <= 7) && (boolS6 == true))
                    {
                        Console.WriteLine(DESCRIPCION_S6[contador]);
                        boolContador2 = false;
                        if (contador == 7)
                        {
                            boolS6 = true;
                            boolContador = true;
                            boolContador2 = true;
                            contador = 0;
                        }
                    }

                    if (boolContador == true)
                    {
                        contador++;

                        if (boolContador2 == true)
                        {
                            contador = 0;
                        }
                    }
                    //Console.WriteLine();
                }
                Console.WriteLine();
            }
           

            #region Tabla.
            void Tabla()
            {
                Console.WriteLine("Byte | Bit | Estado | Descripción");
                Console.WriteLine("-----+-----+-----------------------------------------------------------------------");
            }
            #endregion

            // Pulse cualquier tecla para salir.
            Console.ReadKey();
        }
    }
}

Código chapuza pero funciona.

Ahora la pregunta del millón.

Cada vez que envío un comando me da un resultado diferente.

¿Cómo procesaría cada resultado correspondiente a cada comando?

Está claro que el comando B<cr>, la cadena al recibir no tiene nada que ver con otros comandos.

¿Se entiende lo qué quiero decir?

Si no entiendes, lo pregunto de otra manera.

Gracias.

PD: Por supuesto, se hará el programa en Windows Form, y en LCD de Arduino cuando aprenda bien como se maneja todo esto.
 
Arriba