C# GuiUpdateThread: Refrescar el contenido de la ventana desde un subproceso

En ocasiones cuando desarrollamos aplicaciones de ventanas es necesario desarrollar un proceso largo, si lo hacemos de la forma clásica puede suceder que la interfaz se bloquee, lo ideal en estos casos lo ideal es usar subprocesos.

Vamos a emplear una clase Thread para actualizar el contenido de un campo de edición en un formulario. El thread arranca cuando pulsamos un botón y activa un contador de 0 a 100 con un Sleep en cada ejecución del bucle, cada iteración refresca el contenido del campo de edición con el valor del contador.

t1

Controles:

  • btnStartThread: Botón con leyenda “&StartThread” y diseño plano, cuando hacemos clic sobre el desencadena el evento btnStartThread_Click que ejecuta el subproceso txtNumUpdateThread.
  • txtNum: Campo de edición deshabilitado (Enabled=False).

Cuando pulsamos el botón programamos el arranque del evento:

t2

Un hilo es una instancia de la clase Thread, al crear la instancia debemos pasar como parámetro al constructor la instancia de un delegado (ThreadStart), el delegado apunta al método que corre en segunda plano actualizando el contenido del campo de edición txtNum (txtNumUpdateThread).

Ahora creamos el método txtNumUpdateThread, este será el encargado de refrescar el contenido del campo de edición:

t3

Cuando tenemos actualizar un componente del interfaz gráfico desde un método en un hilo separado en vez de escribir simplemente:

txtNum.Text = x.ToString();

Debemos usar el método Invoke del control (la propiedad InvokeRequired del control contiene true si debemos usar un método de invocación).

“El acceso a los controles de Windows Forms no es intrínsecamente seguro para subprocesos. Si tiene dos o más subprocesos que manipulan el estado de un control, es posible que este acabe teniendo un estado incoherente”

Podemos comprobarlo añadiendo algo de código a la función anterior:

t4

 

GuiUpdateThread2: Segundo ejemplo


using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

using System.Threading;

namespace GuiUpdateThread2
{
public partial class Form1 : Form
{
// delegado con la misma firma del método
public delegate void UpdateTextCallback(string msg);

public Form1()
{
InitializeComponent();
}

private void UpdateTxtMsg(string StrMsg)
{
TxtMsg.Text = StrMsg;
}

private void UpdateTxtThread()
{
string StrTxt = "Texto generado desde otro hilo.";
if (TxtMsg.InvokeRequired)
{
TxtMsg.Invoke(new UpdateTextCallback(this.UpdateTxtMsg),
new object[] { StrTxt });
}
else
{
TxtMsg.Text = StrTxt;
}
}

private void button1_Click(object sender, EventArgs e)
{
ThreadStart delegado = new ThreadStart(UpdateTxtThread);
Thread hilo = new Thread(delegado);
hilo.Start();
}
}
}

 

Posts en este blog:

Enlaces externos:

 

Anuncios

C# FletcherChecksum16: Suma de comprobación CRC para tramas de datos

Introducción

El checksum de Fletcher es un algoritmo para implementar una suma de comprobación que permita detectar un error en alguno de los campos de un mensaje entre su emisión y recepción. Es muy común en protocolos de mensajería de bajo nivel, la máquina que genera el mensaje añade al final de la trama que envía otro campo con el CRC calculado con los bytes del cuerpo del mensaje. El receptor a su vez cuando recibe la trama calcula usando el mismo algoritmo el CRC del cuerpo del mensaje y lo compara con el campo que ha añadido el emisor, evidentemente si los resultados no son el mismo con toda probabilidad la trama se ha corrompido en algún momento de la transmisión.

Las NDCAN de Kimaldi son tarjetas de entradas salidas que permiten la interacción con una red sensores conectadas entre si en batería (fotocelulas de presencia como entradas digitales para detectar la presencia de un objeto por ejemplo) sobre un bus CAN industrial. Al final de la red de tarjetas un concentrador de comunicaciones del mismo fabricante recibe / envía los datos a las tarjetas conectado a un PC usando un protocolo sería basado en tramas con un CRC al final.

can1

El algoritmo empleado para generar el CRC de las tramas serie es muy sencillo y el resultado solo ocupa 1 byte. Las tramas son cadenas en representación hexadecimal (un valor de un byte se representa como dos hexadecimales). Mr Robot usa la aplicación Web ASCII to Hex para convertir entre diferentes bases numéricas, nosotros no vamos a ser menos, por el ejemplo el número decimal 255 se representa en base 16 o hexadecimal como 0xAA (la notación “0x” que precede es para indicar que es un número en hexadecimal):

can2

Ahora vamos a lo que realmente importa, ¿como calcula el CRC? Supongamos que recibimos la siguiente trama:

<STX>02015102000A<CRC><ETX>

Los valores <STX> y <ETX> son valores especiales para indicar donde debemos comenzar a leer la trama y donde finaliza (caracteres de control Start/End of transmission) y no forman parte del calculo del CRC (protocolo MAGSTRIPE usado en tarjetas de banda magnética), para calcular el CRC empleamos exclusivamente el cuerpo del mensaje, en este caso “02015102000A” se calcula de la siguiente forma:

 El valor de CRC se calcula sumando los valores Ascii del cuerpo de la trama:

  • El ascii del 0 es 48
  • El ascii del 2 es 50
  • El ascii del 0 es 48
  • El ascii del 1 es 49
  • El ascii del 5 es 53
  • El ascii del 1 es 49
  • El ascii del 0 es 48
  • El ascii del 2 es 50
  • El ascii del 0 es 48
  • El ascii del 0 es 48
  • El ascii del 0 es 48
  • El ascii del A es 65

Que suman: 604. Su equivalente hexadecimal es 0x25C. Tomando los dos últimos dígitos del resultado hexadecimal obtenemos el valor de la suma en módulo 256, es decir 0x5C.
Así púes la trama queda:

<STX>02015102000A5C<ETX>

Aplicación C# para calcular el CRC de una trama serie

Vamos a diseñar una aplicación super sencilla que permita calcular el CRC de una cadena en formato hexadecimal.

can3

Sólo usamos 3 tipos de controles: TextBox para los campos de edición (uno de ellos de solo lectura con el resultado CRC), Labels para las etiquetas de texto y Button para los 3 botones:

  • txtTrmHex: TextBox donde introducimos cadena hexadecimal.
  • btnAceptar: Button para realizar el cálculo y mostrar el resultado.
  • txtCRC: Campo de edición de solo lectura con el resultado.
  • btnReset: Botón con leyenda “Limpiar” para vaciar contenido de los campos de edición.
  • btnQuitApp: Botón para abandonar la aplicación.

Primero ajustamos el diseño de los controles a nuestro gusto (diseño plano, colores de texto, posicionamiento,…).

Lo primero es crear el método dentro de nuestra clase para calcular el CRC, recibe una cadena de caracteres y retorna como salida un byte:

can4

En el constructor del formulario vamos a poner el foco del cursor sobre el campo de edición para comenzar a introducir la cadena inmediatamente:

can5

Ahora vamos a programar el evento de pulsación del botón aceptar:

can6

También queremos realizar el mismo cálculo cuando pulsamos la tecla “Enter” en el cuadro de edición:

can7

El resto de los eventos ya no tienen ningún misterio:

can8

Aquí esta el código completo:


using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace FletcherChecksum16
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
txtTrmHex.Focus();
}

private byte Calcula_CRC(string cmd)
{
ulong suma=0;
int i=0;
for (i = 0; i < cmd.Length; i++)
{
suma+=cmd[i];
}

return (byte)(suma & 0xFF);
}

private void btnAceptar_Click(object sender, EventArgs e)
{
if (txtTrmHex.Text.Equals(""))
{
MessageBox.Show(this, "No puedes dejar el campo vacio", "Error",
MessageBoxButtons.OK, MessageBoxIcon.Error);
}
else
{
txtCRC.Text = String.Format("{0,2:X}", Calcula_CRC(txtTrmHex.Text));
}
}

private void btnReset_Click(object sender, EventArgs e)
{
txtCRC.Text = "";
txtTrmHex.Text = "";

}

private void btnQuitApp_Click(object sender, EventArgs e)
{
this.Close();
}

private void txtTrmHex_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Enter)
{
if (txtTrmHex.Text.Equals(""))
{
MessageBox.Show(this, "No puedes dejar el campo vacio", "Error",
MessageBoxButtons.OK, MessageBoxIcon.Error);
}
else
{
txtCRC.Text = String.Format("{0,2:X}", Calcula_CRC(txtTrmHex.Text));
}

}
}

}
}

 

C# GetKimaldiDllVer: DLL de fabricante para trabajar con tarjetas de E/S

Creamos un nuevo proyecto GetKimaldiDllVer en Microsoft Visual Studio Professional 2012 de tipo Windows Form Application para Visual C#.

Primero copiamos PrKBUSXdll.dll a la carpeta Debug junto al ejecutable de nuestra nueva aplicación.

En el explorador de solución debemos añadir la referencia a la DLL

kimaldi1

Ahora ya deberíamos ver una nueva referencia a PrKBUSXdll.dll:

kimaldi2

 

En el código de nuestra aplicación añadimos la siguiente línea:

kimaldi3

Ahora instanciamos la clase KBUSXdll:

kimaldi4

 

Ahora diseñamos el formulario con un botón que al pulsarlo mostrará la versión de la Dll en un campo de edición de texto, añadimos 2 controles:

  • btnGetDllVer: Botón con estilo plano (FlatStyle) y cambiamos color del texto con la propiedad ForeColor.
  • txtDllVer: TextBox de sólo lectura (ReadOnly a True) y con el mismo ForColor.

El resultado final es este:

kimaldi5

Ahora creamos un evento en respuesta a la pulsación del botón que obtenga la versión de la Dll llamando a su función GetVersion().

kimaldi6

Resultado final cuando pulsamos el botón:

kimaldi7

 

 

 

C# MysqlConnector: Conexión a la base de datos (MySqlConnection)

El objetivo del siguiente ejercicio es crear una ventana donde definir los parámetros de conexión a una BD MySQL y pulsando un botón establezca la conexión.

csharp8

En mi caso como estoy tratando de conectarme a una BD remota en Hostgator lo primero que debemos hacer es habilitar mi dirección IP pública (para averiguarla https://www.hostgator.com/ip) en el hosting para que me permita conectarme.

Básicamente la solo vamos a usar la clase  MySqlConnection pasando la cadena de conexión como parámetro al constructor de la clase y los métodos Open y Close para abrir y cerrar la conexión.

Referencias:

 using System;
 using System.Collections.Generic;
 using System.Drawing;
 using System.Windows.Forms;
 using MySql.Data.MySqlClient;
 
 namespace MysqlConnector
 {
     public partial class MainForm : Form
     {
         public MainForm()
         {            
             InitializeComponent();            
         }
         
         void BtnConnectClick(object sender, EventArgs e)
         {
             MySqlConnection connection;
             string connectionString;
             string ServerName;
             string Database;
             string User;
             string Password;
             
             ServerName=txtServer.Text;
             Database=txtDatabaseName.Text;            
             
             User=txtUsr.Text;
             //MessageBox.Show(User);
             
             Password=txtPwd.Text;
             //MessageBox.Show(Password);
         
             connectionString = "SERVER=" + ServerName + ";" + "DATABASE=" +
                 Database + ";" + "UID=" + User + ";" + "PASSWORD=" + Password + ";";
                 
             //MessageBox.Show(connectionString);
             
             connection = new MySqlConnection(connectionString);
             
             try
             {
                 connection.Open();
                 MessageBox.Show("Connection ok!");
             }
             catch (MySqlException ex)
             {
                 MessageBox.Show("Connection Error string '"+connectionString+"' ["+ex.Number+"]: "+ex.Message);
             }
             
             
             connection.Close();
     
         }
     }
 } 

C# TimerCtrl: Control timer para ejecutar rutinas periódicas

Con el control timer se pueden desencadenar eventos periódicos a intervalos de tiempo definidos en las propiedades del objeto.

csharp5El intervalo es definido con una precisión de mili-segundos. Para habilitar su ejecución debemos establecer la propiedad Enabled a True. Definimos el intervalo de ejecución a 1000 milisegundos (1 segundo), cada vez que transcurre ese intervalo se ejecuta la función void Timer1Tick(object sender, EventArgs e).

Añadimos una etiqueta de texto a nuestro formulario que mostrará la fecha-hora actual en forma de cadena.

Cada vez que se ejecute la función del Timer actualiza la etiqueta de texto de forma que vemos como se actualiza cada segundo, este objeto puede ser muy útil por ejemplo para actualizar el contenido de una ventana.


void Timer1Tick(object sender, EventArgs e)
{
label1.Text = DateTime.Now.ToString();
}

Podemos ejecutar o detener el Timer usando los métodos Start o Stop.


 /*
  * Created by SharpDevelop.
  * User: i.landajuela
  * Date: 10/09/2016
  * Time: 18:32
  * 
  * To change this template use Tools | Options | Coding | Edit Standard Headers.
  */
 using System;
 using System.Collections.Generic;
 using System.Drawing;
 using System.Windows.Forms;
 
 namespace TimerCtrl
 {
     /// <summary>
     /// Description of MainForm.
     /// </summary>
     public partial class MainForm : Form
     {
         int second = 0;
         
         public MainForm()
         {
             //
             // The InitializeComponent() call is required for Windows Forms designer support.
             //
             InitializeComponent();
             
             //
             // TODO: Add constructor code after the InitializeComponent() call.
             //
         }
         
         void MainFormLoad(object sender, EventArgs e)
         {
             //timer1.Interval=1000;
             timer1.Start();
     
         }
         
         void Timer1Tick(object sender, EventArgs e)
         {
             label1.Text = DateTime.Now.ToString();
             second=second+1;
             if (second>=10)
             {
                 timer1.Stop();
                 MessageBox.Show("Exiting timer...");
             }
         }
         
     }
 }