Foro de elhacker.net

Programación => .NET (C#, VB.NET, ASP) => Mensaje iniciado por: Meta en 1 Abril 2021, 19:15 pm



Título: Separar binario por cada byte
Publicado por: Meta en 1 Abril 2021, 19:15 pm
Hola:

Al mostrar una trama de bytes, lo presento en binario y me muestra esto.

001000111100011110010111110000001011000000001101

Hay 6 Bytes que en realidad en hexadecimal es 23 C7 97 C0 B0 0D

Quiero que se me separe así en cada byte o cada 8 bit.

00100011 11000111 10010111 11000000 10110000 00001101

He intentado hacerlo con este código:
Código
  1.            // Pasar a binario.
  2.            foreach (string leer in recibidos.Select(c => Convert.ToString(c, 2)))
  3.  
  4.            {
  5.                richTextBox1.Text += leer.ToString();
  6.            }

Me pasa dos cosas.
Como en el ejemplo de arriba, si los bits empieza por cero y encuentro un uno, por ejemplo. 00000001, en la pantalla me aparece solo el 1 ignorando los sietes primeros 0. Me gusta más que se muestre así 00000001 en vez de tipo ahorrador con solo un 1.

La otra cosa, que por cada 8 bytes en binario se muestre separado como indicado arriba.

¿Es posible hacerlo?

Gracias.


Título: Re: Separar binario por cada byte
Publicado por: WHK en 1 Abril 2021, 22:42 pm
Intenta procesarlo con un arreglo de bytes en ves de un string, mira esto:

https://stackoverflow.com/questions/5664345/string-to-binary-in-c-sharp

Código:
string.Join(" ", data.Select(byt => Convert.ToString(byt, 2).PadLeft(8, '0')));


Título: Re: Separar binario por cada byte
Publicado por: Meta en 2 Abril 2021, 05:33 am
Buenas mi muy distinguido amigo:

El problema que el código y variable recibidos me viene en string.

Lo he intentado poner en Byte y me da más problemas por todas partes. El código es enorme.

Dejo un ejemplo para que lo veas y te hagas una gran idea.


Código
  1. using System;
  2. using System.IO.Ports;
  3. using System.Linq;
  4. using System.Text;
  5. using System.Windows.Forms;
  6.  
  7. namespace Ejemplo
  8. {
  9.    public partial class Form1 : Form
  10.    {
  11.        // Utilizaremos un string como buffer de recepción.
  12.        string recibidos;
  13.  
  14.        public Form1()
  15.        {
  16.            InitializeComponent();
  17.        }
  18.  
  19.  
  20.        private void Form1_Load(object sender, EventArgs e)
  21.        {
  22.            try
  23.            {
  24.                // Codificación.
  25.                //serialPort1.Encoding = Encoding.GetEncoding(437);
  26.                //serialPort1.Encoding = Encoding.GetEncoding(28591); // 28591 es lo mismo que ISO-8859-1.
  27.                serialPort1.Encoding = Encoding.GetEncoding("ISO-8859-1");
  28.  
  29.                // Añado los puertos disponible en el PC con SerialPort.GetPortNames() al comboBox_Puerto.
  30.                comboBox_Puerto.DataSource = SerialPort.GetPortNames();
  31.  
  32.                // Añade puertos disponibles físicos  y virtuales.
  33.                serialPort1.PortName = comboBox_Puerto.Text.ToString();
  34.  
  35.                // Añadir datos recibidos en el evento.
  36.                serialPort1.DataReceived += new SerialDataReceivedEventHandler(serialPort1_DataReceived);
  37.            }
  38.  
  39.            catch (Exception error)
  40.            {
  41.                MessageBox.Show(error.Message, "Aviso:",
  42.                    MessageBoxButtons.OK, MessageBoxIcon.Warning);
  43.            }
  44.        }
  45.  
  46.        // Detecta USB o puerto serie virtual cuando lo conecta y desconecta del cable.
  47.        protected override void WndProc(ref Message USB)
  48.        {
  49.            if (USB.Msg == 0x219)
  50.            {
  51.                comboBox_Puerto.DataSource = SerialPort.GetPortNames();
  52.            }
  53.  
  54.            // Detecta si hay cambios en el usb y si los hay los refleja.
  55.            base.WndProc(ref USB);
  56.        }
  57.  
  58.        private void button_Conectar_Click(object sender, EventArgs e)
  59.        {
  60.            try
  61.            {
  62.                serialPort1.PortName = comboBox_Puerto.Text.ToString(); // Puerto seleccionado previamente.
  63.                serialPort1.BaudRate = Convert.ToInt32(comboBox_Baudios.Text); // Baudios.
  64.                serialPort1.Open(); // Abrir puerto.
  65.                comboBox_Puerto.Enabled = false;
  66.                comboBox_Baudios.Enabled = false;
  67.                button_Conectar.Enabled = false;
  68.                button_Desconectar.Enabled = true;
  69.                groupBox_Control_Zumbador.Enabled = true;
  70.            }
  71.            catch (Exception error)
  72.            {
  73.                MessageBox.Show(error.Message, "Aviso:",
  74.                MessageBoxButtons.OK, MessageBoxIcon.Warning);
  75.            }
  76.        }
  77.  
  78.        private void button_Desconectar_Click(object sender, EventArgs e)
  79.        {
  80.            try
  81.            {
  82.                serialPort1.Close(); // Cerrar puerto.
  83.                comboBox_Puerto.Enabled = true;
  84.                comboBox_Baudios.Enabled = true;
  85.                button_Conectar.Enabled = true;
  86.                button_Desconectar.Enabled = false;
  87.                groupBox_Control_Zumbador.Enabled = false;
  88.            }
  89.  
  90.            catch (Exception error)
  91.            {
  92.                MessageBox.Show(error.Message, "Aviso:",
  93.                MessageBoxButtons.OK, MessageBoxIcon.Warning);
  94.            }
  95.        }
  96.  
  97.        // Al cerrar el formulario, cierra el puerto si está abierto.
  98.        private void Form1_FormClosing(object sender, FormClosingEventArgs e)
  99.        {
  100.            try
  101.            {
  102.                serialPort1.Close(); // Cerrar puerto.
  103.            }
  104.  
  105.            catch (Exception error)
  106.            {
  107.                MessageBox.Show(error.Message, "Aviso:",
  108.                MessageBoxButtons.OK, MessageBoxIcon.Warning);
  109.            }
  110.        }
  111.  
  112.        // Al recibir datos.
  113.        private void serialPort1_DataReceived(object sender, SerialDataReceivedEventArgs e)
  114.        {
  115.            // Acumula los caracteres recibidos a nuestro 'buffer' (string).
  116.            recibidos += serialPort1.ReadExisting();
  117.  
  118.            // Invocar o llamar al proceso de tramas.
  119.            Invoke(new EventHandler(Actualizar));
  120.        }
  121.  
  122.        // Procesar los datos recibidos en el bufer y extraer tramas completas.
  123.        private void Actualizar(object sender, EventArgs e)
  124.        {
  125.  
  126.            // Asignar el valor de la trama al richTextBox.
  127.            richTextBox1.Text += recibidos;
  128.  
  129.            // Pasar a hexadecimal.
  130.            foreach (byte b in recibidos)
  131.            {
  132.                // x = minúscula, X = mayúscula.
  133.                richTextBox1.Text += b.ToString("X2");
  134.            }
  135.  
  136.            // Nueva línea.
  137.            richTextBox1.Text += Environment.NewLine;
  138.  
  139.            // Pasar a binario.
  140.            foreach (string leer in recibidos.Select(c => Convert.ToString(c, 2)))
  141.            {
  142.                richTextBox1.Text += leer.ToString();
  143.            }
  144.  
  145.            // Nueva línea.
  146.            richTextBox1.Text += Environment.NewLine;
  147.            richTextBox1.Text += Environment.NewLine;
  148.  
  149.            // Selecciona la posición final para leer los mensajes entrantes.
  150.            richTextBox1.SelectionStart = richTextBox1.Text.Length;
  151.  
  152.            // Mantiene el scroll en la entrada de cada mensaje.
  153.            richTextBox1.ScrollToCaret();
  154.  
  155.            // Limpiar.
  156.            recibidos = "";
  157.        }
  158.  
  159.        private void button_Activar_Click(object sender, EventArgs e)
  160.        {
  161.            //byte[] mBuffer = Encoding.ASCII.GetBytes("K60:1\r"); // Comando K60:1 activar.
  162.            byte[] mBuffer = Encoding.ASCII.GetBytes("X87\r"); // Comando X87 Flags.
  163.            serialPort1.Write(mBuffer, 0, mBuffer.Length);
  164.        }
  165.  
  166.        private void button_Desactivar_Click(object sender, EventArgs e)
  167.        {
  168.            byte[] mBuffer = Encoding.ASCII.GetBytes("K60:0\r"); // Comando K60:0 desactivar.
  169.            serialPort1.Write(mBuffer, 0, mBuffer.Length);
  170.        }
  171.  
  172.        private void button_Mute_temporal_Click(object sender, EventArgs e)
  173.        {
  174.            byte[] mBuffer = Encoding.ASCII.GetBytes("B\r"); // Comando K60:2 Mute temporal.
  175.            serialPort1.Write(mBuffer, 0, mBuffer.Length);
  176.        }
  177.  
  178.        private void button_Limpiar_Click(object sender, EventArgs e)
  179.        {
  180.            // Limpiar.
  181.            richTextBox1.Clear();
  182.        }
  183.    }
  184. }


Título: Re: Separar binario por cada byte
Publicado por: @XSStringManolo en 2 Abril 2021, 06:46 am
Usa un bucle

Código
  1. string binario = "001000111100011110010111110000001011000000001101";
  2. string resultado = "";
  3. for (int i = 0; i < binario.Length; i += 8) {
  4.  resultado += binario.Substring(i, 8) + " ";
  5. }

Substring pilla desde el index hasta los siguientes 8 caracteres, asique si solo quedan por ejemplo 3, los pillas igual.

Te sobra un espacio al final. Puedes sumarle 8 al iterador para ver si en la siguiebte iteración ya no vas a entrar en el bucle A.K.A no añadas el espacio la última vez que iteras porque ya acabaste de añadir bytes. O reasignas con Substring usando 0, y resultado.Length -1


Título: Re: Separar binario por cada byte
Publicado por: Meta en 2 Abril 2021, 10:33 am
Buenas:

Como veo la base ya tienes una variable con 0 y 1.
Hice el foreach y for que indicaste. Me da problemas. Lo hice así para que lo pase a binario y elfor tuyo para que lo separe.

(https://social.msdn.microsoft.com/Forums/getfile/1659602)
Ver zoom (https://social.msdn.microsoft.com/Forums/getfile/1659602).

Código
  1.            // Pasar a binario.
  2.            string resultado = "";
  3.            foreach (string leer in recibidos.Select(c => Convert.ToString(c, 2)))
  4.            {
  5.                resultado += leer.ToString();
  6.            }    
  7.  
  8.            for (int i = 0; i < resultado.Length; i += 8)
  9.            {
  10.                richTextBox1.Text += resultado.Substring(i, 8) + " ";
  11.            }

Si lo hago solo así que se muestre en richTextBox me da el  mismo error.
Código
  1.            for (int i = 0; i < recibidos.Length; i += 8)
  2.            {
  3.                richTextBox1.Text += recibidos.Substring(i, 8) + " ";
  4.            }


Título: Re: Separar binario por cada byte
Publicado por: @XSStringManolo en 2 Abril 2021, 23:05 pm
Uhh, no sabía que sucedía ese error.

Suma de 1 en 1 en lugar de 8 en 8 y cuando i % 8 == 0 añades un espacio.


Título: Re: Separar binario por cada byte
Publicado por: WHK en 3 Abril 2021, 03:14 am
Eso pasa porque el tipo String en .net tiene pre filtros como la eliminación de padding vacío, hay que procesarlo como un arreglo de bytes, sino pasarás haciendo parches y de una u otra manera tendrás problema con algún valor.


Título: Re: Separar binario por cada byte
Publicado por: Meta en 3 Abril 2021, 09:13 am
Uhh, no sabía que sucedía ese error.

Suma de 1 en 1 en lugar de 8 en 8 y cuando i % 8 == 0 añades un espacio.

Si te refieres a esto.
Código
  1. for (int i = 0; i < recibidos.Length; i += 8)
  2.            {
  3.                richTextBox1.Text += recibidos.Substring(i, 1) + " ";
  4.            }

O a esto ottro.
Código
  1. for (int i = 0; i < recibidos.Length; i += 1)
  2.            {
  3.                richTextBox1.Text += recibidos.Substring(i, 8) + " ";
  4.            }

No funciona.

Eso pasa porque el tipo String en .net tiene pre filtros como la eliminación de padding vacío, hay que procesarlo como un arreglo de bytes, sino pasarás haciendo parches y de una u otra manera tendrás problema con algún valor.

Los ejemplos de Microsoft, tiene la manía de recibir datos tipo sttring aquí y pòr aquí. Es verdad que en el puerto serie se transmite byte por byte. Aquí en SerialPort.ReadExisting  (https://docs.microsoft.com/es-es/dotnet/api/system.io.ports.serialport.readexisting?WT.mc_id=WD-MVP-36772&view=dotnet-plat-ext-5.0)es funciona con string.

Dicen muchos por Internet, que para estas cosas mejor aceptar las tramas de byte (https://docs.microsoft.com/es-es/dotnet/api/system.byte?view=net-5.0&viewFallbackFrom=dotnet-plat-ext-5.0) tal como vienen, en byte. Guardar los datos en Byte y luego procesarlo. Ya se usaría esto otro SerialPort.Read (https://docs.microsoft.com/es-es/dotnet/api/system.io.ports.serialport.read?WT.mc_id=WD-MVP-36772&view=dotnet-plat-ext-5.0).

El problema si usas tipo arreglo con Byte[] tienes que saber el tamaño que te llega en ese arreglo. El programa completo cambiaría por todas partes, no es cambiar de tipo a string a Byte[] así sin más. Ahí está el problema.

Encuentro otro problema con el invoke al recibir datos por el puerto serie el como se comporta.
Código
  1. // Al recibir datos.
  2.        private void serialPort1_DataReceived(object sender, SerialDataReceivedEventArgs e)
  3.        {
  4.            // Acumula los caracteres recibidos a nuestro 'buffer' (string).
  5.            recibidos += serialPort1.ReadExisting();
  6.  
  7.            // Invocar o llamar al proceso de tramas.
  8.            Invoke(new EventHandler(Actualizar));
  9.        }

Si recibo este mensaje en ASCII, o lo que sea, por ejemplo. Debería mostrar el mensaje recibido tal como aparece en el Ejemplo 1. En el Ejemlo 2, a veces se corta, como si alguien le diera Enter en medio de la frase.

Ejemplo 1:
No hay mayor tesoro que un amigo.

Ejemplo 2:
No hay mayor tesoro q
ue un amigo.

No he conseguido ningún programa estable que funcione siempre como el Ejemplo 1.

Saludos.


Título: Re: Separar binario por cada byte
Publicado por: @XSStringManolo en 3 Abril 2021, 13:40 pm
Suma de 1 en 1 en lugar de 8 en 8 y cuando i % 8 == 0 añades un espacio.
Me refiería a esto.
Código
  1. for (int i = 0; i < recibidos.Length; i += 1) {
  2.  richTextBox1.Text += recibidos[i];
  3.  if (i % 8 == 0) {
  4.    richTextBox1.Text += " ";
  5.  }
  6. }


Título: Re: Separar binario por cada byte
Publicado por: Serapis en 3 Abril 2021, 18:25 pm
En efecto cuando conviertes un numero a string y lo presentas, elimina los ceros a la izquierda. Para conservarlos, hay que formatearlo de alguna manera...

...por ejemplo tu mismo puedes crear una función exprofeso... Sí, tratando con bytes.
Código:
// Toma 1 en cada ciclo del bucle y lo concatena.
// NOTA: Va tomando el bit mas alto para concatenarlo a la derecha.
string = Funcion ByteToString(byte x)
   byte k
   entero n=128
   string s

   bucle para k desde 0 a 7
         s += (x and n)
         n /= 2
   siguiente

   devolver s
fin funcion
Tras la devolucion tu vas concatenando la secuencia al lado que interese, con un espacio o el separador que te interese.

Por otro lado, si tienes una 'ristra de bits' en una cadena de texto, ('100011100011100001110000111...'), puedes igualmente contar su longitud (o puede que sepas directamente cuantos bytes son), y seccionar la cadena en un bucle con cada 8 bits...
Código:
string = Funcion SepararBitString(string bits, string separador, buleano LtoR)  ' Left To Rigth
    entero k, j
    string s

    // comenzamos desde la derecha, por que
    // los numeros estan alineados siempre desde la derecha
    // a diferencia de los strings... esto es,
    // si faltara algun bit, seguiremos teniendo bien divididos los bytes.

    // separador se puede omitir. Si siempre sera un espacio, ponlo directamente.
   
    j = (bits.length-1)
    Si LtoR= False
        j -= 8
        s = bits.substring(j, 8)
        bucle para k desde j-8 hasta 0 en pasos de -8
            s = bits.substring(k, 8) + separador + s
        siguiente   

        // si los bits son forman bytes completos, al acabar el bucle k = -8, si no...
        si (k > -8)  // sobran bits a la izquierda que no completan un byte???
            s = bits.left(-k) + separador + s
        fin si
    si no
        bucle para k desde 0 hasta j en pasos de 8
            s += (separador + bits.substring(k, 8))
        siguiente

        // si los bits son forman bytes completos, al acabar el bucle k = j+8, si no...
        si (k < (j+8))  // sobran bits a la izquierda que no completan un byte???
            s += (separador + bits.right(k-j) )
        fin si
    fin si

    devolver s
fin funcion
En este caso tratándose del puerto serie es muy importante que tengas claro el orden en que los bits son recibidos en secuencia si de derecha a izquierda o al revés... para que la funcion opere exactamente como se espera que los datos  aparezcan y no se confundan.
A menudo si hay que hacer mas que un procesado, no importa en primera instancia que los bits queden invertidos en orden dentro del string (pudiera ser así más eficiente), siempre que la funcion final los procese y devuelve correctamente ordenados.
Es cómodo añadir bits a una cadena de texto, a la derecha a medida que se reciben serialmente... por lo que cuando deban procesarse estan al revés. Sería interesante en este caso tener un función que admita un parámetro adicional que indique justamente si deben ordenarse de derecha a izquierda o al revés y así, la misma función puede tratar ambos casos. Esta ultima funcion cumple ese requisito...

Si la secuencia de bits es muy larga, te sugiero utilizar la clase bitarray (localizada en Collections), que permite operar a muy alta velocidad operando con bits, si bien parece que tu caso es operar con cadenas. En ese caso la funcion ByteToString es muy clara y sencilla.

Suele ser más eficiente (cuando la secuencia de bits sea enorme (pongamos millones)), calcular el tamaño de la cadena de salida de antemano y crearla en su tamaño total antes de entrar al bucle, y luego en vez de añadir, simplemente se 'pega' en su ubicación de destino. La concatenacion de cadenas, exige crear para la concatenación una nueva cadena... con cadenas cortas o poco numerosas no se nota perdida de eficiencia, si se notaría si por ejemplo hubiera que procesar un fichero de cierto tamaño... pongamos 20Mb. en tanto que crear el string de una tacada y procesarlo, sería prácticamente inmediato, una concatenación cada pocos bits para 20Mb. podría demorarse algunos o bastantes segundos (depende de la potencia del equipo, obviamente).

Un control ricthtextbox, es un control pesado, no lo utilices si no vas a hacer nada complejo... si es una simple presentacion de datos usa cualquier otro control ligero de texto. Sería como contratar un camión de 4 ejes, para ir al super a hacer la compra semanal... para lo que basta el 'carrito de la compra'...


Título: Re: Separar binario por cada byte
Publicado por: Meta en 6 Abril 2021, 03:38 am
Muy buen explicación.

En mi caso, siguiendo lo que me dicen, más la locura que tengo en la cabeza, el programa que hice es muy chapucero pero...

¡¡¡FUNCIONA!!!

 ;-) ;-) ;-) ;-) ;-) ;-) ;-) ;-)

(https://social.msdn.microsoft.com/Forums/getfile/1659779)
Ver imagen (https://social.msdn.microsoft.com/Forums/getfile/1659779).

Código C#:
Código
  1.  
  2. using System;
  3. using System.Collections.Generic;
  4. using System.Linq;
  5. using System.Text;
  6. using System.Threading.Tasks;
  7.  
  8. namespace Delimitador_consola_03
  9. {
  10.    class Program
  11.    {
  12.        static void Main(string[] args)
  13.        {
  14.            #region Configuración ventana.
  15.            // Título de la ventana.
  16.            Console.Title = "Probando manipulación de cadena";
  17.  
  18.            // Tamaño de la ventana, x, y.
  19.            Console.SetWindowSize(100, 35);
  20.  
  21.            // Color de fondo.
  22.            Console.BackgroundColor = ConsoleColor.DarkBlue;
  23.  
  24.            // Color de las letras.
  25.            Console.ForegroundColor = ConsoleColor.Yellow;
  26.  
  27.            // Limpiar pantalla y dejarlo todo en color de fondo.
  28.            Console.Clear();
  29.  
  30.            // Visible el cursor.
  31.            Console.CursorVisible = true;
  32.            #endregion
  33.  
  34.            // Cree una codificación ISO-8859-1.
  35.            Encoding ISO_8859_1 = Encoding.GetEncoding("ISO-8859-1");
  36.  
  37.            #region Variables.
  38.            // Partir a trocitos estos caracteres.
  39.            char[] delimitadorComandoB = { '#', ',', 'O', 'I', 'L', 'B', 'V', 'F', 'H', 'R', 'S', };
  40.  
  41.            // Es la cadena de caracteres que me llegó desde el puerto serie.
  42.            // En este ejemplo lo dejo en la varible directamente.
  43.            // Una cadena completa empieza con # y termina en <cr>, o lo que es lo mismo, /r.
  44.            string respuestaB = "#I223.3O224.0L000B100V26.4F50.2H50.2R0080S„€€„À"; // Comando B.
  45.  
  46.            // Se guarga en este array tipo string los datos ya partidos a tozos.
  47.            string[] palabrasB = respuestaB.Split(delimitadorComandoB);
  48.  
  49.            // Tabla S1 descripción.
  50.            string[] DESCRIPCION_S1 =
  51.            {
  52.                "Indica que este byte está disponible y es válido. [Siempre 1]",             // Posición [0].
  53.                "Indica falla de energía de la red pública; ver detalles en S2.0 y S2.1.",
  54.                "Indica que la capacidad de la batería es menor que el umbral de apagado.",
  55.                "Indica que el zumbador está en estado de pitido.",
  56.                "Indica que la prueba de batería se está procesando.",
  57.                "Indica que el apagado programado está pendiente.",
  58.                "Indica que la restauración programada está pendiente.",
  59.                "Indica falla de hardware; ver detalles sobre el comando X71."               // Posición [7].
  60.            };
  61.  
  62.            string[] DESCRIPCION_S2 =
  63.            {
  64.                "Para indicar que este byte está disponible y es válido. [Siempre 1].",
  65.                "--",
  66.                "XX",
  67.                "Indica sobretemperatura del inversor.",
  68.                "Indica que el inversor tiene una falla.",
  69.                "Indica que el inversor está apagado.",
  70.                "Indica que la frecuencia de la utilidad está fuera de rango.",
  71.                "Indica que el voltaje de la red pública está fuera de rango."
  72.            };
  73.  
  74.            string[] DESCRIPCION_S3 =
  75.            {
  76.                "Para indicar que este byte está disponible y es válido. [Siempre 1].",
  77.                "Indica que la batería está completamente cargada.",
  78.                "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.",
  79.                "Indica que la batería se está cargando.",
  80.                "Indica que la batería se está descargando.",
  81.                "Indica que la capacidad de la batería es inferior al 80 por ciento.",
  82.                "Reservado, debe ser 0.",
  83.                "Indica que la batería no está presente."
  84.            };
  85.  
  86.            string[] DESCRIPCION_S4 =
  87. {
  88.                "Para indicar que este byte está disponible y es válido. [Siempre 1].",
  89.                "Indica que el bypass es una sobrecarga.",
  90.                "Indica que la derivación está activa.",
  91.                "Indica que la salida sufre un cortocircuito.",
  92.                "Indica que la salida tiene carga.",
  93.                "Indica que la salida está sobrecargada.",
  94.                "Indica que la frecuencia de salida está fuera de rango en bypass.",
  95.                "Indica que el voltaje de salida está fuera de rango en derivación."
  96.            };
  97.  
  98.            string[] DESCRIPCION_S5 =
  99. {
  100.                "Para indicar que este byte está disponible y es válido. [Siempre 1].",
  101.                "Indica que no hay salida.",
  102.                "Para indicar que el tiempo de ejecución restante es inferior al umbral.",
  103.                "Para indicar que el zumbador está silenciado (no permanente) en este momento.",
  104.                "Para indicar falla de cableado.",
  105.                "Para indicar SAI en modo ECO.",
  106.                "Para indicar UPS en Bypass manual.",
  107.                "Arreglar 0."
  108.            };
  109.  
  110.            string[] DESCRIPCION_S6 =
  111. {
  112.                "Para indicar que este byte está disponible y es válido. [Siempre 1].",
  113.                "Indica UPS encendido.",
  114.                "Reservado, debe ser 0.",
  115.                "Reservado, debe ser 0.",
  116.                "Reservado, debe ser 0.",
  117.                "Reservado, debe ser 0.",
  118.                "Reservado, debe ser 0.",
  119.                "Reservado, debe ser 0."
  120.            };
  121.  
  122.            bool boolS1 = true;
  123.            bool boolS2 = false;
  124.            bool boolS3 = false;
  125.            bool boolS4 = false;
  126.            bool boolS5 = false;
  127.            bool boolS6 = false;
  128.            bool boolContador = true;
  129.            bool boolContador2 = false;
  130.            #endregion
  131.  
  132.            // Muestra los resultados en pantalla.
  133.            Console.WriteLine();
  134.            Console.WriteLine("El voltaje de la utilidad es de {0} voltios. ", palabrasB[2]); // I.
  135.            Console.WriteLine("El voltaje de salida del UPS es de {0} voltios. ", palabrasB[3]); // O.
  136.            Console.WriteLine("La carga actual de UPS es del {0} por ciento. ", palabrasB[4]); // L.
  137.            Console.WriteLine("La capacidad de la batería es del {0} por ciento. ", palabrasB[5]); // B.
  138.            Console.WriteLine("El voltaje de la batería es de {0} voltios. ", palabrasB[6]); // V.
  139.            //Console.WriteLine("La temperatura del gabinete del UPS es de {0} grados centígrados. ", palabrasB[7]); // T. No hay T. en mi versión.
  140.            Console.WriteLine("La frecuencia de salida del SAI es de {0} Hz. ", palabrasB[7]); // F.
  141.            Console.WriteLine("La frecuencia de salida del SAI es de {0} Hz. ", palabrasB[8]); // H.
  142.            Console.WriteLine("El tiempo de funcionamiento restante de la batería es de {0} minutos. ", palabrasB[9]);
  143.            //Console.WriteLine(@"Estos son los bits de estados que no se entiende.S:
  144.            //Aquí hay que leer cada bits como cuando se leyeron cada Byte arriba: ", (char)palabrasB[10]);
  145.  
  146.            char[] bits = palabrasB[10].ToCharArray();
  147.  
  148.            Tabla();
  149.            int contador = 0;
  150.            for (int b = 0; b < bits.Length; b++)
  151.            {
  152.                for (int a = 7; a >= 0; a--)
  153.                {
  154.  
  155.                    Console.Write((((byte)bits[b]) & (1 << a)) >> a);
  156.                    Console.Write(" | ");
  157.  
  158.                    if ((contador <= 7) && (boolS1 == true))
  159.                    {
  160.                        Console.WriteLine(DESCRIPCION_S1[contador]);
  161.  
  162.                        if (contador == 7)
  163.                        {
  164.                            boolS1 = false;
  165.                            boolS2 = true;
  166.                            boolContador2 = true;
  167.                            contador = 0;
  168.                        }
  169.                    }
  170.                    else if ((contador <= 7) && (boolS2 == true))
  171.                    {
  172.                        Console.WriteLine(DESCRIPCION_S2[contador]);
  173.                        boolContador2 = false;
  174.                        if (contador == 7)
  175.                        {
  176.                            boolS2 = false;
  177.                            boolS3 = true;
  178.                            boolContador2 = true;
  179.                            contador = 0;
  180.                        }
  181.                    }
  182.                    else if ((contador <= 7) && (boolS3 == true))
  183.                    {
  184.                        Console.WriteLine(DESCRIPCION_S3[contador]);
  185.                        boolContador2 = false;
  186.                        if (contador == 7)
  187.                        {
  188.                            boolS3 = false;
  189.                            boolS4 = true;
  190.                            boolContador2 = true;
  191.                            contador = 0;
  192.                        }
  193.                    }
  194.                    else if ((contador <= 7) && (boolS4 == true))
  195.                    {
  196.                        Console.WriteLine(DESCRIPCION_S4[contador]);
  197.                        boolContador2 = false;
  198.                        if (contador == 7)
  199.                        {
  200.                            boolS4 = false;
  201.                            boolS5 = true;
  202.                            boolContador2 = true;
  203.                            contador = 0;
  204.                        }
  205.                    }
  206.                    else if ((contador <= 7) && (boolS5 == true))
  207.                    {
  208.                        Console.WriteLine(DESCRIPCION_S5[contador]);
  209.                        boolContador2 = false;
  210.                        if (contador == 7)
  211.                        {
  212.                            boolS5 = false;
  213.                            boolS6 = true;
  214.                            boolContador2 = true;
  215.                            contador = 0;
  216.                        }
  217.                    }
  218.                    else if ((contador <= 7) && (boolS6 == true))
  219.                    {
  220.                        Console.WriteLine(DESCRIPCION_S6[contador]);
  221.                        boolContador2 = false;
  222.                        if (contador == 7)
  223.                        {
  224.                            boolS6 = true;
  225.                            boolContador = true;
  226.                            boolContador2 = true;
  227.                            contador = 0;
  228.                        }
  229.                    }
  230.  
  231.                    if (boolContador == true)
  232.                    {
  233.                        contador++;
  234.  
  235.                        if (boolContador2 == true)
  236.                        {
  237.                            contador = 0;
  238.                        }
  239.                    }
  240.                    //Console.WriteLine();
  241.                }
  242.                Console.WriteLine();
  243.            }
  244.  
  245.  
  246.            #region Tabla.
  247.            void Tabla()
  248.            {
  249.                Console.WriteLine("Byte | Bit | Estado | Descripción");
  250.                Console.WriteLine("-----+-----+-----------------------------------------------------------------------");
  251.            }
  252.            #endregion
  253.  
  254.            // Pulse cualquier tecla para salir.
  255.            Console.ReadKey();
  256.        }
  257.    }
  258. }
  259.  
  260.  

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.

Gracias a todos.


Título: Re: Separar binario por cada byte
Publicado por: Serapis en 6 Abril 2021, 04:53 am
No estoy seguro de entenderte, pero quizás si...

Tu lo que creo que quieres ahora es decodificar el byte que te llega...Si es así, es suficiente pcoo código, bastan unas constantes, y un par de funciones...

Déjame que parta de un ejemplo con suposiciones (abstracción, no necesito empaparme del asunto concreto que trates).
Código:
// Constantes para la bateria: (un supuesto comando, que te devuelve 1 byte y cada bit tiene un significado propio)
Enumeracion ConstantesDeBateria
    BATERIAPRESENTE = 1   // 0000.0001
    BATERIAAGOTADA = 2   //  0000.0010
    BATERIACARGANDO = 4 // 0000.0100
    BATERIAAL80OMAS = 8 // 0000.1000
    BATERIAW = 16 // 0001.0000
    BATERIAX = 32  // 0010.0000
    BATERIAY = 64 // 0100.0000
    BATERIAZ= 128 // 1000.0000
Fin enumeracion

enumeracion ConstantesDeComandos
    COMANDO_BATERIA = 136  // POR EJEMPLO
    ...otros comandos (aunque algunas veces admiten string en vez de numeros, no creo que sea el caso presente).
fin enumeracion

array de string mensajeBateriaOK(0 a 7)  //mensaje generico para cada bit cuando es '1'.
array de string mensajeBateriaNO(0 a 7)  // mensaje generico para cada bit cuando es '0'.


// el comando típicamente podría quedar aparte del valor (no ser recibido, solo enviado), o podria venir conjunto, precediendo o sucediendo uno al otro, depende de qué hardware se trate...
funcion RecibirComando(ConstantesDeComandos comando, byte valor)
    selecccionar caso comando
        caso COMANDO_BATERIA
            llamada a ProcesarBateria(valor)
       caso X
       caso Y
       ...mas casos...
    fin casos
fin funcion

funcion ProcesarBateria(byte valor)
    entero k
    string mensaje
    ConstantesDeBateria cb

    cb= 1
    bucle para k desde 0 hasta 7
        si TestBit(valor, cb)
            escribirmensaje = mensajeBateriaOK(k) + ...quizás algun valor
        sino
           escribirmensaje = mensajeBateriaNO(k) + ...quizás algun valor
        fin si
        cb = (cb * 2)  ' pasará por los valores: 1,2,4,8,16...128
    siguiente
fin funcion

buleano = funcion TestBit(byte valor, byte Bit)
   devolver (valor and Bit)
fin funcion

Eso de '+ ...quizás algun valor', es para recordarte la respuesta de otro día que trataba sobre concatenar cadenas complejas con valores parametrizables en medio (¿recuerdas?)...

Es solo pasarlo a código y 'ampliar' la oferta de los comandos...
Si es otra cosa lo que pides, me temos que tendrás que expresarte con más claridad.


Título: Re: Separar binario por cada byte
Publicado por: Meta en 6 Abril 2021, 11:20 am
Buenas:

Me gustó el ejemplo que usaste máscaras a la hora de tratar bits en un Byte. Lo he hecho en Windows Form como ejemplo.
(https://social.msdn.microsoft.com/Forums/getfile/1659681)
Ver zoom (https://social.msdn.microsoft.com/Forums/getfile/1659681).

Código
  1. using System;
  2. using System.Windows.Forms;
  3.  
  4. namespace Leer_bits_de_un_Byte_01
  5. {
  6.    public partial class Form1 : Form
  7.    {
  8.        public Form1()
  9.        {
  10.            InitializeComponent();
  11.        }
  12.  
  13.        int ascii;
  14.        int resultado;
  15.        readonly int[] mascara = new int[8]
  16.        {
  17.            0b_0000_0001, // Posición [0].
  18.            0b_0000_0010,
  19.            0b_0000_0100,
  20.            0b_0000_1000,
  21.            0b_0001_0000,
  22.            0b_0010_0000,
  23.            0b_0100_0000,
  24.            0b_1000_0000  // Posición [7].
  25.        };
  26.  
  27.        private void textBox_ASCII_TextChanged(object sender, EventArgs e)
  28.        {
  29.            try
  30.            {
  31.                ascii = int.Parse(textBox_ASCII.Text);
  32.                resultado = ascii & mascara[0];
  33.                if (resultado == 0) { label_c0.Text = "0"; } else { label_c0.Text = "1"; }
  34.                resultado = ascii & mascara[1];
  35.                if (resultado == 0) { label_c1.Text = "0"; } else { label_c1.Text = "1"; }
  36.                resultado = ascii & mascara[2];
  37.                if (resultado == 0) { label_c2.Text = "0"; } else { label_c2.Text = "1"; }
  38.                resultado = ascii & mascara[3];
  39.                if (resultado == 0) { label_c3.Text = "0"; } else { label_c3.Text = "1"; }
  40.                resultado = ascii & mascara[4];
  41.                if (resultado == 0) { label_c4.Text = "0"; } else { label_c4.Text = "1"; }
  42.                resultado = ascii & mascara[5];
  43.                if (resultado == 0) { label_c5.Text = "0"; } else { label_c5.Text = "1"; }
  44.                resultado = ascii & mascara[6];
  45.                if (resultado == 0) { label_c6.Text = "0"; } else { label_c6.Text = "1"; }
  46.                resultado = ascii & mascara[7];
  47.                if (resultado == 0) { label_c7.Text = "0"; } else { label_c7.Text = "1"; }
  48.            }
  49.            catch(Exception error)
  50.            {
  51.                MessageBox.Show(error.Message, "Aviso:",
  52.                MessageBoxButtons.OK, MessageBoxIcon.Warning);
  53.            }
  54.        }
  55.    }
  56. }

En cuanto a lo que quiero decir desde el principio, lo comento de otra manera para que se entienda mejor.

El progrma de ahora mismo está hecho en consola, solo está diseñado para recibir una respuesta de un comando.

Por ejemplo el comando B<cr>

Si envío el comando B<cr> recibo una trama de Bytes, que cada respuesta recibida, siempre empieza la cadena en # como inicio y en <cr> como fin. <cr> es Retorno de Carro. En código es así, /r.

¿Qué ocurre cuando envío otro comando diferente a un dispositivo?

Que me devuelve otro resultado diferente y este programa en C# no lo procesa como debería.

Dejo un ejemplo de un Terminal indicado abajo.
Envío comando B<cr> me da una respuesta larga. Luego envío otro comando llamado X87<cr> y me llega otra cadena más corta y no tiene nada que ver con la B, así con todos los comandos.

(https://social.msdn.microsoft.com/Forums/getfile/1659887)
Ver zoom (https://social.msdn.microsoft.com/Forums/getfile/1659887).

Aquí abajo dejo un PDF que me dio el fabricante de un SAI. Se muestra el protocolo de comunicación, los comandos y que respuesta recibes.

Descargar (https://forum.arduino.cc/index.php?action=dlattach;topic=731422.0;attach=407930).

En resumen.
Quiero hacer un programa, que al enviar un comando, el que sea, me procese la cadena de caracteres recibidos correspondiente.

Espero que esta vez se entienda.

Muchas gracias.  :D

PD: Con tu ejemplo de las máscaras, voy hacer un ejemplo con enum (https://docs.microsoft.com/es-es/dotnet/csharp/language-reference/builtin-types/enum) como hicistes en un programa a parte para cotillear y aprender mejor.


Título: Re: Separar binario por cada byte
Publicado por: Serapis en 7 Abril 2021, 20:34 pm
Me obligas a leerme el pdf, y es precisamente lo que uno trata de evitar, porque ir al detalle fino, requiere mucho más tiempo en responder y sale un mensaje más largo.

Es definitiva es como te señalaba en mi mensaje anterior...
En ese incluí el ejemplo para decodificar un supuesto comando de la bateria (que existe se llama B (Battery), pero con sus valores específicos).

El comando X27, viene explicado también en el pdf, recibe '#' con 6 bytes, un cóoodigo de error viene en la forma: "#- loque sea".

Lo más sencillo al caso una vez leído por encima en pdf, es hacer un parsing genérico y luego especifico para cada comando... es más o menos el pseudocódigo previo que puse en mi mensaje anterior... la parte del preparsing, sería algo como:

Código:
enumeracion PartesResultado
   COMANDO_INICIO = 35  // el ASCII de '#'
   COMANDO_FIN = 13  // el ASCII de 'retorno de carro
   //COMANDO_MEDIO = 1   // referencia al medio, luego se explica mejor.
fin enumeracion

Ahora una funcion que preprocesa el comando recibido:
Código:
funcion ParsingComando(string comando, string valor)
    entero inicio, cantidad

    si ((valor comienza con COMANDO_INICIO) y (termina con COMANDO_FIN))
        
        Si (valor.charat(1) = "-")          // parece ser un código de error. "#- loquesea"
             llamada a InterpetarCodigoError(substring(valor, 2,1))   //enviamos el carácter 3 de los 4 que tiene
               // (ignoro si tienen más de 1 byte-char como codigo de error.
        Sino  // el resultado tiene valores interpretables:
            // enviamos ya 'recortdo' el string, sin los caracteres inicial y final
            inicio = 1
            cantidad =  valor.length -2
            llamada a ProcesarComandos(comando, valor.Substring(inicio, cantidad))
          
        fin si
    sino
        mensaje = "Error, el valor del comando recibido, tiene un formato desconocido..."
    fin si
fin funcion

Como decía conviene tener una enumeracion que represente cada comando:
Hay todos estos comandos:  "The I, O, L, B, V, T, F, H, R, C, Q, S, W, X are single uppercase
Código:
enumeracion ComandosSAI
    SAI_COMANDO_BATERIA = 66    // ASCII de 'B'
    SAI_COMANDO_UTILITY_VOLT = 73  // ASCII de 'I' (una 'i').
    //
    //
    ...
    SAI_COMANDO_STATUS_FLAG = 83  //ASCII de 'S'.
    SAI_COMANDO_W...     = 87   //   idem de 'W'
    SAI_COMANDO_X...     = 88   // " 'X'
character"


La funcion procesar comando, tendrá en selector de casos, con un caso para cada comando que te interese y un 'else', para el resto...
Esto viene a ser equivalente al código de más arriba, solo que allí se recibía 1 solo byte y se ejemplificaba solo el comando 'B' de batería.
Algunos comandos  como el X, tienen 'subcomandos' en la forma Xn, donde 'n' es un numero...
Estos comandos dento de su 'caso', examina precisamente el 'subcomando', y lo gestiona a su vez con otro 'selector de casos'.
Como los comandos al final tienen varos bytes, interesa más hacer una llamada en esta funcion para dedicar una funcion exclusiva para procesar cada comando o subcomando, incluso aunque haya partes comunes entre ellos, será m
ás fácil de implementar y de mantener aunque sea a costa de más código:

Código:
funcion ProcesarComandos(string comando, string valor) //valor ya viene filtrado de # y 'cr'
   ComandosSAI SaiComando = comando.left(1).ToByte
  

    seleccionar caso para SaiComando
        caso SAI_COMANDO_BATERIA
            llamada a ProcesarBateria(valor)
        caso SAI_COMANDO_UTILITY_VOLT
            llamada a ProcesarUtilityVolt(valor)
           ....
        caso SAI_COMANDO_STATUS_FLAG
            llamada a ProcesarStatusFlag(valor)
        caso SAI_COMANDO_W...
            llamada a ProcesarW(valor)
        caso SAI_COMANDO_X...
             comando = comando.substring(1, comando.length-1)
             llamada a ProcesarX(comando, valor)   // <-------- ejemplo de un comando que tiee subcomandos
        otros casos
                mensaje "Actualmente no procesamos el comando: " + comando + nuevalinea + valor
    fin casos
fin funcion

funcion ProcesarX(string comando, string valor)
    byte servico = comando.Tobyte

    seleccionar caso para servicio
        caso 5    
        caso 15  
          ...
        caso 27   // tiene 6 chars
            llamada a ProcesarX27(valor)
        caso 28
          ...
        otros casos
            mensaje "Actualmente noe staos procesando el subcomando '" servicio.Tostring + "' del comando 'X', con valor: " + valor
    fin seleccionar casos
fin funcion

// Según el pdf: #output_voltage,high_transfer_voltage,low_transfer_voltage, battery_threshold,runtime_threshold<cr>
funcion ProcesarX27(string valor)
    llamada a procesarOutputVoltage(valor.charat(0).tobyte)
    llamada a procesarHighTransferV(valor.charat(1).tobyte)    
    llamada a procesarLowTransferV(valor.charat(2).tobyte)
    llamada a procesarBatteryThreshold(valor.charat(3).tobyte)
    llamada a procesarThreshold(valor.substring(4,2))      // este parametro ocupa creo que 2 bytes.
fin funcion

Y bueno, este es el esqueleto para decodificar todo... fíjate que es un simple parsing, donde primero se verifica si el resultado es un codigo de erro o no... sino, luego se verifica que comando se trata, luego cada comando si tiene subcomandos, deriva a otra funcion que identifique de qué subcomando se trata, al final cada comando final, tiene que ser procesado individualmente, porque los bits de cada uno tendran un significado unico y específico...
El caso de error, igualmente en su llamada debe derivar descendeidno e comandos y subcomandos, segun sea el caso, para que al final procesar el error. Ignoro si cuando es un código de error, se compone de un unico byte o más... si es solo uno, una unica funcion (a nivel de comandos y subcomandos) podría ser suficiente, en cambio si tiene varios bytes, y el codigo puede ser excesivo (generaría una función muy larga), convendría en ese caso, tener una etapa más donde se procese cada byte (si cada bit tiene un significado propio, si cada byte tiene un significado propio es lo mismo que recibir 1 solo byte con significado propio a nivel de bit (o más de 1).

En fin, el trabajo no es complicado, solo largo y algo tedioso, porque son más de una docena de comandos y algunos tienen subcomandos, y luego cada caso se procesa a nivel de bit, por lo que necesitas constantes a mogollon...

pd.: Olvidaba adjuntar una captura que hice, para que veas que viene explicado, tienes que leerlo primero entero, para tener una idea global y luego ocn más detenimiento en el apartado que más te interese, si te interesan todos, pués paciencia, hoy uno y mañana otro.
(https://i.imgur.com/ro3tsUR.png)


Título: Re: Separar binario por cada byte
Publicado por: Meta en 8 Abril 2021, 00:36 am
Muy bueno:
 ;-) ;-) ;-) ;-) ;-) ;-) ;-)

Sí, es largo y tedioso.

Los comandos de error he recibido estos.
#-0
#-1
#-3
#-10

Tendría que probocar algo para que me dieran más respuestas erróneas. El fabricante no me hace caso sobre qué significa estos errores. Lo del PDF lo tengo más que leído, es más, este documento que me dieron que es del 2018, mi versión de la UPS aunque lo compré hace semanas, es la 2.020. El firmware que tiene una versión y no se si se puede actualizar.

Si te fijas bien, hay comando que no vienen como indica en el PDF.
Citar
Hay todos estos comandos:  "The I, O, L, B, V, T, F, H, R, C, Q, S, W, X are single uppercase

Su respuesta es esta.
Citar
#I223.3O224.0L000B100V26.4F50.2H50.2R0080S„€€„À

Otra cosa.
La interfaz oficial (https://d7rh5s3nxmpy4.cloudfront.net/CMP1313/files/pmasterp104-setup-windows.zip) es por Web (https://www.salicru.com/sps-2000-adv-t.html).

Pruebo los comandos del en el Hyper Terminal de Windows o otro similar y me funciona. He hecho un poco mi propio programa en Windows Form y controlado el puerto serie en el cual puedo con botones apagar y encender el zumbador o pitido del SAI o UPS.

El programa que incluye es por Web local. La ruta para acceder es con un archivo que se llama agent.cmd y en su interior tiene este código.

start http://localhost:3052/local/

(https://social.msdn.microsoft.com/Forums/getfile/1660156)
Ver imagen (https://social.msdn.microsoft.com/Forums/getfile/1660156).

Lo curioso que la forma de controlar el puerto serie el programa oficial, es con un programa modo servicio, que lo puedo detener o arrancar como dice abajo.
(https://social.msdn.microsoft.com/Forums/getfile/1660157)
Ver imagen (https://social.msdn.microsoft.com/Forums/getfile/1660157).

Este detalle lo intentaré hacerlo en otro proyecto mucho más adelante. ;)
No controlar la UPS, sino otros aparatos como Arduino por puerto serie de esta manera, por vía Web.

Antes lo hacía de esta manera por capaz.
Actuadores <-----> Arduino <-----> PC <-----> Base de datos <-----> Interfaz <-----> Conexión a Internet.

  •     Un ejemplo. Uso Arduino que tiene un display LCD 20x4 en el cual me muestra en todo momento los estados de entradas y salidas. Arduino controla una habitación, ventilador, extractor de aire, ventana motorizada, deteción de gases, alarma, temperatura, humedad, detección si hay apagón de luz y incluye su UPS, etc...
        Ese Arduino lo tengo directamente conectado a su USB al PC.
        En el PC tengo una interfaz que controla el puerto serie/USB y una base de datos.
        Tengo conexión en el PC a Internet.
        La base de datos instalada tengo una buena tabla hecha sobre control de relés y estados de avisos o alarmas.
        Si cambio un valor en una base de datos, por ejemplo, un 1, se me enciende un Led, si pongo un 0, se apaga un Led.
        La interfaz detecta un cambio en la base de datos los datos, lo envía al puerto serie ese valor y le llega a Arduino.
        En la propia Interfaz de un programa de escritorio o Web, puede cambiar valores en la base de datos que también envía datos a Arduino.

Es otra historia.

Sigo interpretado tu código, ya que me ha aportado muchas ideas. Sobre todo de diferenciar códigos de respuestas según código enviado.

Ahora estoy en plan chamuzqueándome la cabeza. A ver si me sale todo esto de alguna vez por todas.

Y sí, es muyyyyyyyyyyyyyyyyyyyy, laaaaaaaaaarrrrrrrrrrrrrrrrrrrrrrrrrrgo y tedioso de la leche.  ;D

Se hará poco a poco, mientras entiendas lo básico, todo lo demás saldrá poco a poco.

Saludos.