W&T conecta
Interfaces para TCP/IP, Ethernet, RS-232, RS-485, USB, 20mA, fibra óptica de vidrio y de plástico, http, SNMP, OPC, Modbus TCP, I/O digital, I/O analógico, ISA, PCI

Tutorial al Web-IO digital:

Dirigir y controlar Web-IO digital con Visual C#


Para la creación de aplicaciones Windows, Visual C++ ha sido hasta hace poco una de las plataformas de desarrollo más utilizadas. Entre tanto trabajan cada vez más programadores con el .Net Framework y crean sus aplicaciones en C# (C Sharp).

Dirigir Web-IO con C Sharp

Con el ejemplo siguiente de programa C# puede representar su Web-IO Digital con sus Inputs y Outputs en una aplicación Windows. Además de ello puede conectar los Outputs del Web-IO.


Preparativos


Recopilación de los diferentes elementos de manejo y objetos de visualización en formulario

Formulario

Importar recursos y declaración de variables de miembros

En primer lugar se importan todas las clases necesarias para la conexión de red y la GUI (Graphical User Interface).

							
using System;
using System.Windows.Forms;
using System.Net;
using System.Net.Sockets;
using System.Text;
              
						

A continuación se declaran los componentes de la aplicación y las variables importantes para una conexión TCP como variables de miembros de la clase, teniendo acceso así a los métodos de la clase.

							
  public class mainWindow : System.Windows.Forms.Form
  {
    private CheckBox cb_Output0;
    private CheckBox cb_Output1;
    private CheckBox cb_Input0;
    private CheckBox cb_Input1;
    private CheckBox cb_Polling_Counter;
    private CheckBox cb_Polling_Outputs;
    private CheckBox cb_Polling_Inputs;
    private Button bt_Readall_Outputs;
    private Button bt_Readall_Inputs;
    private Button bt_Clear_Counter0;
    private Button bt_Clear_Counter1;
    private Button bt_Clearall_Counter;
    private Button bt_Read_Counter0;
    private Button bt_Read_Counter1;
    private Button bt_Readall_Counter;
    private Label lb_Counter0;
    private Label lb_Counter1;
    private Label lb_Intervall;
    private TextBox tb_Counter0;
    private TextBox tb_Counter1;
    private TextBox tb_Intervall;
    private Button bt_Connect;
    private Button bt_Disconnect;
    private TextBox tb_Password;
    private TextBox tb_Port;
    private TextBox tb_IP;
    private GroupBox gb_ioControlBox;
    private GroupBox gb_conControlBox;
    private StatusBar statusBar;

    private Socket client;
    private int intervall;
    private byte[] buffer = new byte[256];
    private System.Windows.Forms.Timer counter;
    private System.Windows.Forms.Timer outputs;
    private Label label2;
    private Label label1;
    private Label label3;
    private System.Windows.Forms.Timer inputs;
							
						

Arranque de programa

Instalar los elementos de manejo

El grupo con los elementos de manejo para el Web-IO se bloquea primero para el manejo. Tan pronto como se establezca una conexión, se da línea libre a todos los elementos, que poseen una versión conveniente.

El nombre del elemento respectivo de manejo se deriva del elemento mismo según el contexto. Los dos primeros signos del nombre significan el tipo del elemento (cb -> Checkbox, bt -> Button, gb -> Groupbox y tb-> TextBox).

							
  public mainWindow()
  {
    InitializeComponent();
    gb_ioControlBox.Enabled = false;
    bt_Disconnect.Enabled = false;
    cb_Input0.Enabled = false;
    cb_Input1.Enabled = false;
    tb_Counter0.Enabled = false;
    tb_Counter1.Enabled = false;
  }
							
						

El control de conexión

Introducir la conexión

Entrando la dirección IP del Web-IO en el campo de texto tb_IP y el puerto 42280 en el campo de texto tb_Port se puede establecer una conexión activando el botón bt_Connect. Si no se entra ninguna dirección IP o ningún puerto, se da un mensaje por la aplicación en la barra de estado.

La conexión

Para poder establecer ahora una conexión TCP se crea e inicia un nuevo zócalo. La dirección IP y el número de puerto están compilados en un punto final de IP. Al abrir BeginConnect se transmite el punto final de IP.

Para que el programa pueda trabajar de forma asíncrona no espera a que se presente algún suceso, sino que trabaja con rutinas Callback. Los métodos Callback se inician cuando arranca un proceso y se activan cuando se produce el suceso respectivo, es decir, por ejemplo al establecer una conexión para enviar o recibir.

							
  private void bt_Connect_Click(object sender, System.EventArgs e)
  {
    try
    {
      if((tb_IP.Text != "") && (tb_Port.Text != ""))
      {
        client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
        IPEndPoint ipep = new IPEndPoint(IPAddress.Parse(tb_IP.Text), int.Parse(tb_Port.Text));
        client.BeginConnect(ipep, new AsyncCallback(connectCallback), client);
      }
      else
        statusBar.Text = "IP and Port needed!";
    }
			catch(SocketException)
			{ statusBar.Text = "Connection not possible!"; }
  }
							
						

A continuación se describe la rutina Callback que se activa cuando se establece la conexión. La aplicación comienza de inmediato a prepararse para la recepción. Para ello se inicia la rutina de Callback "receiveCallback", que explicaremos más adelante.

							
  private void connectCallback(IAsyncResult ar)
  {
    try
    {
      client = (Socket) ar.AsyncState;
      client.EndConnect(ar);
      connectupdatedisplay();
      intervall = 1000;
      client.BeginReceive(buffer, 0, 255, SocketFlags.None, new AsyncCallback(receiveCallback), client);
    }
    catch(Exception)
    { disconnect();
    }
  }
							
						

Además, al activar el proceso connectupdatedisplay() se habilitan en la aplicación todos los elementos de mando requeridos y se desactiva el botón Connect.

En la actual versión de C# ya no está permitido acceder desde un thread (aquí la función Callback) a elementos gráficos de otro thread (forma).

Una solución ofrece aquí el uso de delegados y del método Invoke.

A continuación se describe la rutina Callback que se activa cuando se establece la conexión. La aplicación comienza de inmediato a prepararse para la recepción. Para ello se inicia la rutina de Callback "receiveCallback", que explicaremos más adelante.

							
  delegate void delegateSub();

  private void connectupdatedisplay()
  {
    if (InvokeRequired)
    {
      BeginInvoke(new delegateSub(connectupdatedisplay), new object[] { });
      return;
    }
    statusBar.Text = "Connected!";
    gb_ioControlBox.Enabled = true;
    bt_Disconnect.Enabled = true;
    bt_Connect.Enabled = false;
  }
							
						

Separar la conexión

La conexión se mantiene hasta que es finalizada por el usuario con el botón de desconectar o por Web-IO.

							
  private void bt_Disconnect_Click(object sender, System.EventArgs e)
  {
    try
    {
      client.Shutdown(SocketShutdown.Both);
      client.Close();
      disconnectupdatedisplay();
    }
    catch (Exception)
    {
      statusBar.Text = "Not able to disconnect!";
    }
  }
							
						

Aún cuando la conexión sea finalizada por Web-IO, la aplicación debe cerrar el zócalo empleado y todos los elementos deben ser llevados de nuevo a su posición básica. Ahora ya no debe ser posible pulsar el botón de desconectar.

							
  private void disconnect()
  {
    try
    {
      client.Shutdown(SocketShutdown.Both);
      client.Close();
      disconnectupdatedisplay();
    }
    catch (Exception)
    {
       statusBar.Text = "Not able to disconnect!";
    }
  }
							
						

Para un acceso seguro al thread se ha deslocalizado el restablecimiento de los elementos del formulario en el proceso disconnectupdatedisplay().

							
  private void disconnectupdatedisplay()
  {
    if (InvokeRequired)
    {
      BeginInvoke(new delegateSub(disconnectupdatedisplay), new object[] { });
      return;
    }
    statusBar.Text = "Disconnected!";
    gb_ioControlBox.Enabled = false;
    bt_Disconnect.Enabled = false;
    bt_Connect.Enabled = true;
  }
							
						

Ahora se ha finalizado la conexión y la aplicación ha retrocedido a su estado de partida.

Manejo y comunicación por parte del cliente

Tan pronto como se ha establecido la conexión con el Web-IO, el usuario puede enviar comandos al Web-IO manejando los correspondientes elementos de programa.


Enviar comandos

Al enviar una noticia al Web-IO se solicita una rutina Callback igual como al recibirla.

							private void sendCallback(IAsyncResult ar)
  private void sendCallback(IAsyncResult ar)
  {
    try
    {
      Socket tmp_client = (Socket) ar.AsyncState;
      int bytessend = tmp_client.EndSend(ar);
    }
    catch(Exception)
    {
      statusBar.Text = "Error while sending";
    }
  }
							
						

Poner Outputs
Los Outputs del Web-IO pueden conectarse con ayuda de las dos cajas de chequeo cb_Output0 y cb_Output1. La caja de chequeo dispara una acción si se chasquea. Dependiendo de si la caja de chequeo ya está puesta, el Output se pone a ON o a OFF.

							
  private void cb_Output0_CheckedChanged(object sender, System.EventArgs e)
  {
    if(cb_Output0.Checked)
      send("GET /outputaccess0?PW=" + tb_Password.Text + "&State=ON&");
    else
      send("GET /outputaccess0?PW=" + tb_Password.Text + "&State=OFF&");
  }

  private void cb_Output1_CheckedChanged(object sender, System.EventArgs e)
  {
    if(cb_Output1.Checked)
      send("GET /outputaccess1?PW=" + tb_Password.Text + "&State=ON&");
    else
      send("GET /outputaccess1?PW=" + tb_Password.Text + "&State=OFF&");
  }
							
						

Solicitar estado de Output/Input

							
  private void bt_Readall_Outputs_Click(object sender, System.EventArgs e)
  {
    send("GET /output?PW=" + tb_Password.Text + "&");
  }

  private void bt_Readall_Inputs_Click(object sender, System.EventArgs e)
  {
    send("GET /input?PW=" + tb_Password.Text + "&");
  }
							
						
Consultar o borrar contadores

El siguiente método envía una consulta a un contador concreto y demanda una respuesta con el estado actual del contador o borra el estado de un contador.

							
  private void bt_Read_Counter0_Click(object sender, System.EventArgs e)
  {
    send("GET /counter0?PW=" + tb_Password.Text + "&");
  }

  private void bt_Read_Counter1_Click(object sender, System.EventArgs e)
  {
    send("GET /counter1?PW=" + tb_Password.Text + "&");
  }

  private void bt_Clear_Counter0_Click(object sender, System.EventArgs e)
  {
    send("GET /counterclear0?PW=" + tb_Password.Text + "&");
  }

  private void bt_Clear_Counter1_Click(object sender, System.EventArgs e)
  {
    send("GET /counterclear1?PW=" + tb_Password.Text + "&");
  }
							
						

Naturalmente también se puede consultar o borrar todos los estados de los contadores con un solo comando.

							
  private void bt_Readall_Counter_Click(object sender, System.EventArgs e)
  {
    send("GET /counter?PW=" + tb_Password.Text + "&");
  }

  private void bt_Clearall_Counter_Click(object sender, System.EventArgs e)
  {
    send("GET /counterclear?PW=" + tb_Password.Text + "&");
  }
							
						

Recibir datos de Web-IO y evaluar

Estructura de los datos de recepción
  • Todos los comandos y solicitudes al Web-IO se confirman con un String de respuesta. Las respuestas presentan una estructura específica según el tipo:
  • Para los Outputs: output;<valor binario del estado de salida en formato hexadecimal>
  • Para un Output especial: outputx;<ON u OFF>
  • Para los Inputs: input;<valor binario del estado de salida en formato hexadecimal>
  • Para un Input especial: inputx;<ON u OFF>
  • Después hay también el string de respuesta para un contador que tiene el siguiente aspecto.
  • Contadores: counterx;<estado decimal de contador >
  • o counter;<estado decimal de contador 0 >; <estado decimal de contador 0 >; ...... si todos los contadores se deben leer de una sola vez.
  • Todos los Strings de respuesta se finalizan con un Byte 0.
Recepción de datos

En nuestra aplicación, para recibir este tipo de mensajes se abre el método receiveCallback(). Lo especial de este método es la activación por sucesos. Esto se produce en el momento en que Web-IO envía datos a la aplicación.

							
  private void receiveCallback(IAsyncResult ar)
  {
    int bytesRead;
    string rcv = string.Empty;
    try
    { bytesRead = client.EndReceive(ar);
      rcv = Encoding.ASCII.GetString(buffer);
      client.BeginReceive(buffer, 0, 255, SocketFlags.None, new AsyncCallback(receiveCallback), client);
    }
    catch (Exception)
    {
      bytesRead = 0;
    }
    if (bytesRead == 0)
    {
      if (client.Connected)
      {
        disconnect();
      }
    }
    else if (rcv != null)
    {
      IOupdatedisplay(rcv.Substring(0, bytesRead - 1));
    }
	}
							
						
Evaluación de datos recibidos

Se lee la cadena de respuesta y se transmite al proceso de thread IOupdatedisplay seguro.

							
  delegate void delegateIOSub(string rcv);

  private void IOupdatedisplay(string rcv)
  {
    if (InvokeRequired)
    {
      BeginInvoke(new delegateIOSub(IOupdatedisplay), new object[] { rcv });
      return;
    }
    if (rcv[0] == ’o’)
    {
      int i = Int32.Parse(rcv.Substring(7), System.Globalization.NumberStyles.HexNumber);
      if((i & 1) == 1)
        cb_Output0.Checked = true;
      else
        cb_Output0.Checked = false;
      if((i & 2) == 2)
        cb_Output1.Checked = true;
      else
        cb_Output1.Checked = false;
    }
    if(rcv[0] == ’i’)
    {
      int i = Int32.Parse(rcv.Substring(6), System.Globalization.NumberStyles.HexNumber);
      if ((i & 1) == 1)
        cb_Input0.Checked = true;
      else
        cb_Input0.Checked = false;
      if((i & 2) == 2)
        cb_Input1.Checked = true;
      else
        cb_Input1.Checked = false;
    }
    if(rcv[0] == ’c’)
    {
      if(rcv[7] == ’0’)
        tb_Counter0.Text = rcv.Substring(9);
      if(rcv[7] == ’1’)
        tb_Counter1.Text = rcv.Substring(9);
      if(rcv[7] == ’;’)
      readAndClearCounter(rcv.Substring(8));
    }
  }
							
						

Puesto que se puede leer o borrar todos los estados de los contadores con un solo comando, es necesario implementar aún un método que procese la cadena de respuesta del Web-IO, de tal modo que en la aplicación se asigne a cada contador su estado específico.

							
  private void readAndClearCounter(string data)
  {
    int j = 0;
    string[] counter = new string[2];
    for(int i = 0; i < data.Length; i++)
    {
      if((data[i].CompareTo(’;’)) == 0)
        j++;
      else
        counter[j] += data[i].ToString();
    }
    tb_Counter0.Text = counter[0];
    tb_Counter1.Text = counter[1];
  }
							
						

7. Polling

Solicitud cíclica de determinados valores

Es de desear que el estado de un sólo componente se actualice por mí mismo y así la aplicación siempre presente el estado actual. Para ello se utiliza un temporizador en este programa, que envía consultas al Web-IO cíclicamente en un intervalo de tiempo fijado por el usuario.

El intervalo de tiempo puede fijarse en el campo tb_Intervall.

Naturalmente también se captura en el caso de que el usuario realice una entrada absurda, como p. ej. un valor negativo de tiempo.

								
  private void tb_Intervall_TextChanged(object sender, System.EventArgs e)
  {
    try
    {
      if(Convert.ToInt32(tb_Intervall.Text) > 0)
      {
        intervall = Convert.ToInt32(tb_Intervall.Text);
        statusBar.Text = "New range: " + intervall.ToString() + " ms!";
      }
      else
        statusBar.Text = "Only positiv Integer allowed!";
    }
    catch(Exception)
    {
     statusBar.Text = "Only positiv Integer allowed!";
    }
  }
								
							

Para realizar ahora también la solicitud cíclica de los estados del Web-IO, lo que se llama también Polling, existe la elección entre el Polling de los Outputs, de los Inputs o de los contadores.

Se inicializa un temporizador propio por variante de Polling.

Si se activa la caja de chequeo cb_Polling_Outputs se aplica el Polling a los Outputs. Para ello se inicializa el temporizador correspondiente.

								
  private void cb_Polling_Outputs_CheckedChanged(object sender, System.EventArgs e)
  {
    if(cb_Polling_Outputs.Checked)
    {
      outputs = new System.Windows.Forms.Timer();
      outputs.Interval = intervall;
      outputs.Start();
      outputs.Tick += new EventHandler(timer_handler);
    }
    else
      outputs.Stop();
  }
								
							

Lo mismo ocurre con las entradas y los contadores.

								
  private void cb_Polling_Inputs_CheckedChanged(object sender, System.EventArgs e)
  {
    if(cb_Polling_Inputs.Checked)
    {
      inputs = new System.Windows.Forms.Timer();
      inputs.Interval = intervall;
      inputs.Start();
      inputs.Tick += new EventHandler(timer_handler);
    }
    else
      inputs.Stop();
  }

  private void cb_Polling_Counter_CheckedChanged(object sender, System.EventArgs e)
  {
    if(cb_Polling_Counter.Checked)
    {
      counter = new System.Windows.Forms.Timer();
      counter.Interval = intervall;
      counter.Start();
      counter.Tick += new EventHandler(timer_handler);
    }
    else
      counter.Stop();
  }
								
							

En este método se captura el evento respectivo del temporizador, que se está anunciando, y se le asigna una acción determinada.

								
  private void timer_handler(object sender, System.EventArgs e)
  {
    if(sender == counter) bt_Readall_Counter_Click(sender, e);
    if(sender == outputs) bt_Readall_Outputs_Click(sender, e);
    if(sender == inputs) bt_Readall_Inputs_Click(sender, e);
  }
								
							

El programa ejemplo asiste todas las funciones corrientes del Web-IO en el modo String de comando, optimado para el Web-IO 2x entradas digitales, 2x salidas digitales. Para los otros modelos Web-IO tienen que realizarse en caso necesario adaptaciones en el programa. Otros ejemplos de programa para la programación del zócalo los encontrarán en las páginas de herramientas al Web-IO. Una descripción detallada de la interfaz del zócalo de los modelos Web-IO digitales la encontrarán en el manual de referencia.

Descargar el programa ejemplo


Productos