Sinópticos II. Solenoide On/Off en vb.net

Continuando con el tema de los sinópticos, haré un pequeño ejemplo de un control que muestra una solenoide conectada /desconectada. No voy a entrar en la realización de los dibujos puesto que sería demasiado extenso. Para realizar este control abrimos un nuevo proyecto de vb.net y añadimos un control de usuario. En el control de usuario añadimos dos PictureBox.

Cambiamos la propiedad Name a pbOn y pbOff.

Pinchando en la flecha podemos acceder al menú para Tareas para PictureBox. Pincha en el hipervínculo “elegir imagen” y ponemos la imagen. En mi caso he realizado un dibujo de una solenoide. Con el programa de renderizado he sacado una foto con unos colores y otra cambiando el color del conector, como muestran las siguientes figuras:

Cambiamos la propiedad de modo de tamaño a StretchImagen.

Una vez hecho esto cambiamos la propiedad Dock de ambos PictureBox a Fill

De esta forma nuestro control se podrá redimensionar manteniendo la imagen completa.

Para terminar nuestro control tendremos que añadir una propiedad Estado que será la manera en que cambiemos la imagen a visualizar.

Cuando cambiamos la propiedad estado del control, se lee el código que hay en la función Set. Primero se pasa el valor a la variable interna “_Estado”. Bien ya tenemos hecho nuestro control, ahora sólo resta probarlo. Para poder utilizar el control deberemos generar la aplicación. En el Menú Generar, generar. Si todo es correcto deberíamos tener creado un nuevo componente listo para usar en la caja de componentes.

Como siempre, lo arrastramos al Form principal y añadimos un botón para hacer las pruebas. Doble click sobre el botón y añadimos este código para cambiar la propiedad estado del control a cada pulsación del botón.

Y este es el resultado:

Aquí os dejo un archivo .rar con el modelo de la solenoide en Scketchup, las dos imágenes en .jpg y el proyecto en vb.net.

Sinópticos I.

Uno de los retos a los que nos enfrentamos es el de aumentar la calidad de la apariencia de las aplicaciones HMI. Hoy pienso hacer una presentación de algunas herramientas para el diseño de gráficos de calidad. Como siempre, intento utilizar aplicaciones source code y esta no sera una excepción. Mas aun me he llevado una grata sorpresa al comprobar las posibilidades de estas herramientas.

En concreto he hecho algunas pruebas con Sketchup y Kerkytea. El primero es un programa de diseño Cad 3D y el segundo una aplicación para renderizar objetos 3D.  Por ahora solo he hecho algunas pruebas pero espero que pronto pueda presentar alguna aplicación en vb.net utilizando las posibilidades que nos presentas estos programas.

Instalación frigorífica realizada con Sketchup.

Y este es el resultado despues de aplicar el renderizado:

Y esto son algunos ejemplos:

Aquí podéis descargar Sketchup, Kerkytea, PlugIn para Sketchup con herramientas interesantes y por ultimo plugin para exportar modelo desde  Sketchup a Kerkytea.

Registro de estado IV. Grafica.

Hoy toca hacer la primera gráfica, como veremos en otra ocasión, vb.net nos proporciona herramientas para poder dibujar de manera que podríamos construir nuestro gráfico con estas herramientas. En esta ocasión toca la ley del mínimo esfuerzo, utilizar el trabajo de otro. Existe un control muy sencillo de utilizar para realizar esta tarea, ZedGraph nos permite realizar gráficos de diferentes tipos con realmente poco esfuerzo.

Aquí podéis ver los detalles de este interesante control. Y aquí podéis descargar todos los archivos. En concreto lo único que necesitamos para este proyecto es la dll. Descargáis el archivo, se descomprime y se guarda en una carpeta de fácil acceso.

En el proyecto del anterior post, añadimos un formulario que llamaremos FormGrafica. Ahora veamos los pasos necesarios para incluir este control en nuestro proyecto:

Herramientas   > elegir elementos del cuadro de herramientas.

Una vez se ha abierto el cuadro de diálogo pinchamos en Examinar, buscamos la carpeta donde guardamos el control

Como se puede ver, yo descargue la librería, manuales y el sourcecode. Abrimos la carpeta zedgraph_dll_v515

Y seleccionamos el archivo ZedGraph. Con esto ya podemos utilizar el control en nuestro formulario

Arrastramos el control al formulario FormGrafica y le cambiamos la propiedad Dock a fill. Para poder ver el gráfico tendremos que poner un botón en el FormAnalizar, y como siempre, pinchamos dos veces, y en el gestor de eventos FormGrafica.Show().

Ahora veamos el código necesario para que el gráfico muestre nuestros datos.

Imports ZedGraph
 
Public Class FormGrafica
 
Private Sub FormGrafica_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
 
'Creamos el grafico
 
CreateGraph(ZG1)
 
End Sub
 
Private Sub CreateGraph(ByVal zgc As ZedGraphControl)
 
Dim myPane As GraphPane = zgc.GraphPane
 
' Asignamos titulos, escalas, etc
 
myPane.Title.Text = "Grafica de datos historicos"
 
myPane.XAxis.Title.Text = "TIEMPO"
 
myPane.YAxis.Title.Text = ""
 
myPane.YAxis.Scale.Min = 0
 
myPane.YAxis.Scale.Max = 16
 
myPane.XAxis.Type = AxisType.Text
 
myPane.XAxis.Scale.TextLabels = FormAnalizar.Momento
 
myPane.XAxis.MajorGrid.IsVisible = True
 
myPane.YAxis.MajorGrid.IsVisible = True
 
myPane.XAxis.Scale.FontSpec.Angle = 90
 
myPane.XAxis.Scale.FontSpec.Size = 8
 
'Transformar el dato en puntos
 
Dim Bit(1) As Double
 
ReDim Bit(FormAnalizar.Dato.Length - 1)
 
Dim i As Integer 'Variable para recorrer cada momento
 
Dim j As Integer 'Variable para cada bits
 
For j = 0 To 15
 
For i = 0 To FormAnalizar.Dato.Length - 1
 
If FormAnalizar.Dato(i).Chars(j) = "0" Then Bit(i) = 0 + j Else Bit(i) = 0.5 + j
 
Next
 
Dim myCurve0 As LineItem = myPane.AddCurve("Bit" & j, Nothing, Bit, Color.Black, SymbolType.None)
 
Next
 
' Fill the axis background with a color gradient
 
myPane.Chart.Fill = New Fill(Color.White, Color.LightGoldenrodYellow, 45.0F)
 
' Fill the pane background with a color gradient
 
myPane.Fill = New Fill(Color.White, Color.FromArgb(220, 220, 255), 45.0F)
 
' Calculate the Axis Scale Ranges
 
zgc.AxisChange()
 
End Sub
 
Private Sub Form1_Resize(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Resize
 
'Si cambiamos el tamaño del formulario se lee este código que envía a la función SetSice
 
SetSize()
 
End Sub
 
Private Sub SetSize()
 
'Reconstruye el tamaño en funcion del tamaño del formulario
 
ZG1.Location = New Point(10, 50)
 
' Leave a small margin around the outside of the control
 
ZG1.Size = New Size(ClientRectangle.Width - 20, ClientRectangle.Height - 80)
 
End Sub
 
End Class

Y este es el resultado:

Aquí dejo el proyecto completo.

Registro de estados III. Visualizar en tabla.

En los anteriores post vimos como guardar el estado de las entradas/salidas del PLC en el PC. Hoy vamos a leer estos archivos y generar una tabla que nos muestre de una forma clara como se han producido los cambios. La técnica empleada para esto es leer texto separado por “;”. En el anterior proyecto añadiremos un botón que nos abra un nuevo formulario. A este botón lo llamaremos buAnalizar y en la propiedad texto le pondremos Analizar. Doble click en el botón y nos genera el evento donde pondremos el siguiente código.

También podríamos utilizar el siguiente código para abrir el formulario, este código nos permite abrir múltiples estancias del mismo formulario:

Como podemos ver en el código, en el segundo caso se crea una nueva estancia del formulario cada vez que presionamos el botón.

En el explorador de soluciones, click con el derecho y añadir nuevo elemento:

Y este es el aspecto de nuestro formAnalizar:

He previsto tres posibilidades para elegir el archivo, las dos primera (IN, OUT), son RadioButon y tienen la peculiaridad de que solo podrá estar marcada una. Cuando marcamos IN se desmarca Out y viceversa. La última posibilidad es un CheckBoxy la función es abrir un cuadro de dialogo para elegir el archivo a analizar. Aunque los tres podrían ser RadioButon he considerado poner un Checkbox para que podáis apreciar la diferencia entre los dos primeros y el tercero. Por último ponemos un botón para proceder a la lectura del archivo.

El cuadro blanco situado a la derecha es un ListView. Podríamos configurar las columnas directamente pero yo prefiero hacerlo por código. Hacemos doble click en el formAnalizar y nos genera el gestor de eventos del FormAnalizar.Load. Lo que quiere decir que será el código que primero leerá cuando se abra el formulario. Aprovechamos este evento para configurar la tabla en el listView. Aunque es un poco largo porque hay muchas columnas, es fácil y prácticamente es copiar pegar, puesto que la diferencia entre las columnas de la tabla son los nombres, bit0, bit1, etc

Private Sub FormAnalizar_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) _
 
Handles MyBase.Load
 
ListView1.View = View.Details
 
ListView1.BackColor = Color.GhostWhite
 
ListView1.GridLines = True
 
ListView1.Columns.Add("Fecha / Hora")
 
ListView1.Columns(0).Width = 150
 
ListView1.Columns.Add("bit.0")
 
ListView1.Columns(1).Width = 40
 
ListView1.Columns.Add("bit.1")
 
ListView1.Columns(2).Width = 40
 
ListView1.Columns.Add("bit.2")
 
ListView1.Columns(3).Width = 40
 
ListView1.Columns.Add("bit.3")
 
ListView1.Columns(4).Width = 40
 
ListView1.Columns.Add("bit.4")
 
ListView1.Columns(5).Width = 40
 
ListView1.Columns.Add("bit.5")
 
ListView1.Columns(6).Width = 40
 
ListView1.Columns.Add("bit.6")
 
ListView1.Columns(7).Width = 40
 
ListView1.Columns.Add("bit.7")
 
ListView1.Columns(8).Width = 40
 
ListView1.Columns.Add("bit.8")
 
ListView1.Columns(9).Width = 40
 
ListView1.Columns.Add("bit.9")
 
ListView1.Columns(10).Width = 40
 
ListView1.Columns.Add("bit.10")
 
ListView1.Columns(11).Width = 40
 
ListView1.Columns.Add("bit.11")
 
ListView1.Columns(12).Width = 40
 
ListView1.Columns.Add("bit.12")
 
ListView1.Columns(13).Width = 40
 
ListView1.Columns.Add("bit.13")
 
ListView1.Columns(14).Width = 40
 
ListView1.Columns.Add("bit.14")
 
ListView1.Columns(15).Width = 40
 
ListView1.Columns.Add("bit.15")
 
ListView1.Columns(16).Width = 40
 
End Sub

Bien, tenemos el formulario gráficamente terminado, ahora vamos a leer los archivos y a llenar la tabla con datos. El primer paso es seleccionar el archivo con el que queremos trabajar:

Agregamos un componente, que nos hará el trabajo de abrir un cuadro de diálogo para seleccionar un archivo. OpenFileDialog.

Lo arrastramos hasta el formulario, y automáticamente se pasará a la barra gris situada debajo del formulario.

Le he cambiado la propiedad Name por Ofd1. El siguiente extracto de código muestra la selección del nombre del archivo a leer, en función de los requerimientos del usuario:

'Borramos el listview
 
ListView1.Items.Clear()
 
'Comprobamos si quiere ver In, Out o abrir un cuadro de dialogo
 
Dim Filename As String = ""
 
If ckMostrarDialogo.Checked Then
 
Ofd1.ShowDialog()
 
Filename = Ofd1.FileName
 
Else
 
If rdIn.Checked Then Filename = "In.csv"
 
If rdOut.Checked Then Filename = "Out.csv"
 
End If
 
If Filename = "" Then
 
MessageBox.Show("Error en parametros")
 
Exit Sub
 
End If

Declaramos la variable FileName donde almacenaremos el nombre del archivo a abrir. Si está marcado el Cheked.Box abre el cuadro de diálogo para que seleccionemos un archivo, una vez seleccionado pasa el nombre a la variable. De no ser así, se comprueba cual de los RadioButton está marcado para asignar el nombre del archivo como In.csv u Out.csv.

Como no sabemos a priori cuantas líneas tendrá el archivo que vamos a abrir, lo haremos en dos pasadas, en la primera pasada contamos las líneas, dimensionamos las variables que almacenaran los datos y en la siguiente pasada pasamos los datos a las variables. Realmente no es necesario almacenar los datos, puesto que como veremos más adelante, los datos se irán escribiendo en la tabla a medida que los leemos, pero esto no termina en una tabla, en la próxima entrada mostraremos los datos en un gráfico, por este motivo los guardamos en unas variables, para posteriormente poder visualizarlos en grafico.

¿Y cómo podemos leer un archivo de este tipo? Pues que lo diga el IDE. Pincha con el derecho dentro del evento buLeerDatos, insertar fragmento de código:

En nuestro caso quitamos la primera línea, ya que filename ya está declarada y cambiamos delimiter por “;”. El código completo del gestor de eventos leerdatos es este:

Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) _
 
Handles buLeerDatos.Click
 
'Borramos el listview
 
ListView1.Items.Clear()
 
'Comprobamos si quiere ver In, Out o abrir un cuadro de dialogo
 
Dim Filename As String = ""
 
If ckMostrarDialogo.Checked Then
 
Ofd1.ShowDialog()
 
Filename = Ofd1.FileName
 
Else
 
If rdIn.Checked Then Filename = "In.csv"
 
If rdOut.Checked Then Filename = "Out.csv"
 
End If
 
If Filename = "" Then
 
MessageBox.Show("Error en parametros")
 
Exit Sub
 
End If
 
Dim i As Integer
 
Dim fields As String()
 
Dim delimiter As String = ";"
 
Try
 
'Leemos para dimensionar las variables
 
Using parser As New TextFieldParser(Filename)
 
parser.SetDelimiters(delimiter)
 
While Not parser.EndOfData
 
' Read in the fields for the current line
 
fields = parser.ReadFields()
 
' Add code here to use data in fields variable.
 
i += 1
 
End While
 
End Using
 
Catch ex As Exception
 
MessageBox.Show("Error de parametros")
 
Exit Sub
 
End Try
 
'Dimensionamos las variables
 
ReDim Momento(i - 1)
 
ReDim Dato(i - 1)
 
i = 0
 
'Leemos y asignamos a variables
 
Using parser As New TextFieldParser(Filename)
 
parser.SetDelimiters(delimiter)
 
While Not parser.EndOfData
 
' Read in the fields for the current line
 
fields = parser.ReadFields()
 
' Add code here to use data in fields variable.
 
Momento(i) = fields(0)
 
Dato(i) = DecToBin(fields(1))
 
Trace1(Momento(i), Dato(i), Color.Black)
 
i += 1
 
End While
 
End Using
 
End Sub

Como podemos ver en el código, en la primera pasada sólo incrementamos i de manera que al final del bucle i = total filas. Redimensionamos las variables y volvemos a leer para asignar los valores a las variables, a la vez llamamos a la función Trace1, que es la encargada de rellenar la tabla de una manera especial.

Public Sub Trace1(ByVal Momento As String, ByVal ValorInt16 As String, ByVal ColorMsg As Color)
 
Dim Texto(16) As String
 
Texto(0) = Momento
 
Dim i As Integer
 
For i = 0 To ValorInt16.Length - 1
 
If DatoAnterior.Chars(i) <> ValorInt16.Chars(i) Then
 
If ValorInt16.Chars(i) = "0" Then Texto(i + 1) = "OFF" Else Texto(i + 1) = "ON"
 
End If
 
Next
 
Dim item As New ListViewItem(Texto)
 
item.ForeColor = ColorMsg
 
ListView1.Items.Add(item)
 
DatoAnterior = ValorInt16
 
End Sub

Principalmente se trata de ir comprobando cada carácter de la variable string que contiene el valor binario, si es diferente de la vez anterior. De ser así, escribimos en la tabla. El resultado es este:

Y este es el código completo:

Imports Microsoft.VisualBasic.FileIO
 
Public Class FormAnalizar
 
Dim Dato(1) As String
 
Dim Momento(1) As String
 
Dim DatoAnterior As String = "0000000000000000"
 
Private Sub FormAnalizar_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) _
 
Handles MyBase.Load
 
ListView1.View = View.Details
 
ListView1.BackColor = Color.GhostWhite
 
ListView1.GridLines = True
 
ListView1.Columns.Add("Fecha / Hora")
 
ListView1.Columns(0).Width = 150
 
ListView1.Columns.Add("bit.0")
 
ListView1.Columns(1).Width = 40
 
ListView1.Columns.Add("bit.1")
 
ListView1.Columns(2).Width = 40
 
ListView1.Columns.Add("bit.2")
 
ListView1.Columns(3).Width = 40
 
ListView1.Columns.Add("bit.3")
 
ListView1.Columns(4).Width = 40
 
ListView1.Columns.Add("bit.4")
 
ListView1.Columns(5).Width = 40
 
ListView1.Columns.Add("bit.5")
 
ListView1.Columns(6).Width = 40
 
ListView1.Columns.Add("bit.6")
 
ListView1.Columns(7).Width = 40
 
ListView1.Columns.Add("bit.7")
 
ListView1.Columns(8).Width = 40
 
ListView1.Columns.Add("bit.8")
 
ListView1.Columns(9).Width = 40
 
ListView1.Columns.Add("bit.9")
 
ListView1.Columns(10).Width = 40
 
ListView1.Columns.Add("bit.10")
 
ListView1.Columns(11).Width = 40
 
ListView1.Columns.Add("bit.11")
 
ListView1.Columns(12).Width = 40
 
ListView1.Columns.Add("bit.12")
 
ListView1.Columns(13).Width = 40
 
ListView1.Columns.Add("bit.13")
 
ListView1.Columns(14).Width = 40
 
ListView1.Columns.Add("bit.14")
 
ListView1.Columns(15).Width = 40
 
ListView1.Columns.Add("bit.15")
 
ListView1.Columns(16).Width = 40
 
End Sub
 
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) _
 
Handles buLeerDatos.Click
 
'Borramos el listview
 
ListView1.Items.Clear()
 
'Comprobamos si quiere ver In, Out o abrir un cuadro de dialogo
 
Dim Filename As String = ""
 
If ckMostrarDialogo.Checked Then
 
Ofd1.ShowDialog()
 
Filename = Ofd1.FileName
 
Else
 
If rdIn.Checked Then Filename = "In.csv"
 
If rdOut.Checked Then Filename = "Out.csv"
 
End If
 
If Filename = "" Then
 
MessageBox.Show("Error en parametros")
 
Exit Sub
 
End If
 
Dim i As Integer
 
Dim fields As String()
 
Dim delimiter As String = ";"
 
Try
 
'Leemos para dimensionar las variables
 
Using parser As New TextFieldParser(Filename)
 
parser.SetDelimiters(delimiter)
 
While Not parser.EndOfData
 
' Read in the fields for the current line
 
fields = parser.ReadFields()
 
' Add code here to use data in fields variable.
 
i += 1
 
End While
 
End Using
 
Catch ex As Exception
 
MessageBox.Show("Error de parametros")
 
Exit Sub
 
End Try
 
'Dimensionamos las variables
 
ReDim Momento(i - 1)
 
ReDim Dato(i - 1)
 
i = 0
 
'Leemos y asignamos a variables
 
Using parser As New TextFieldParser(Filename)
 
parser.SetDelimiters(delimiter)
 
While Not parser.EndOfData
 
' Read in the fields for the current line
 
fields = parser.ReadFields()
 
' Add code here to use data in fields variable.
 
Momento(i) = fields(0)
 
Dato(i) = DecToBin(fields(1))
 
Trace1(Momento(i), Dato(i), Color.Black)
 
i += 1
 
End While
 
End Using
 
End Sub
 
#Region "Conversion binario decimal "
 
Private Function BinToDec(ByVal ValorBin As String) As Decimal
 
Dim Longitud As Integer = ValorBin.Length
 
'Debug.WriteLine("Entrar: " & ValorBin & "Longitud: " & Longitud)
 
Dim i As Integer = 0
 
Dim ValorDec As Decimal = 0
 
For i = 0 To Longitud - 1
 
Dim _Char As Char = ValorBin.Chars(i)
 
Dim ValorChar As Integer
 
If _Char = "0" Then ValorChar = 0 Else ValorChar = 1
 
ValorDec += ValorChar * System.Math.Pow(2, ((Longitud - i) - 1))
 
Next
 
Return (ValorDec)
 
End Function
 
Private Function DecToBin(ByVal ValorDec As Int32) As String
 
'Esta funcion convierte en binario tanto palabras INT como UINT.
 
'En caso de haber valores negativos se asume que es una palabra INT
 
' y hace la conversión con esta formula
 
If ValorDec < 0 Then
 
ValorDec = 65536 + ValorDec
 
End If
 
Dim Cociente As Int32 = ValorDec
 
Dim Resto As Int32 = 0
 
Dim ValorBinMatriz(17) As Char
 
Dim ValorBin As String = ""
 
Dim i As Integer = 0
 
If ValorDec = 1 Then ValorBinMatriz(0) = "1"
 
While Cociente > 1
 
Cociente = System.Math.DivRem(Cociente, 2, Resto)
 
ValorBinMatriz(i) = Resto.ToString
 
If Cociente < 2 Then ValorBinMatriz(i + 1) = Cociente.ToString
 
i += 1
 
End While
 
Dim j As Integer
 
For j = 0 To 15
 
If ValorBinMatriz(j) = "1" Then ValorBin &= "1" Else ValorBin &= "0"
 
Next
 
Return (ValorBin)
 
End Function
 
#End Region
 
Public Sub Trace1(ByVal Momento As String, ByVal ValorInt16 As String, ByVal ColorMsg As Color)
 
Dim Texto(16) As String
 
Texto(0) = Momento
 
Dim i As Integer
 
For i = 0 To ValorInt16.Length - 1
 
If DatoAnterior.Chars(i) <> ValorInt16.Chars(i) Then
 
If ValorInt16.Chars(i) = "0" Then Texto(i + 1) = "OFF" Else Texto(i + 1) = "ON"
 
End If
 
Next
 
Dim item As New ListViewItem(Texto)
 
item.ForeColor = ColorMsg
 
ListView1.Items.Add(item)
 
DatoAnterior = ValorInt16
 
End Sub
 
End Class

Registro de estados I. Introducción.

Cuando tienes averías raras de las que desconoces la causa, interrogas a los usuarios de las instalaciones he intentas reconstruir lo que pasó. En otras ocasiones te gustaría saber cómo funciona la instalación para poder saber cómo mejorarla. Para no tener que volver a hacer interrogatorios podemos crear nuestro espía que grabará en el PC cada cambio de estado para más tarde poder reconstruir el pasado.

En esta entrada y siguientes construiremos una pequeña aplicación que guarde el estado de las entradas/salidas del PLC en el PC, para más tarde poder ver gráficamente lo que pasó con nuestra instalación mientras estábamos fuera. Para compactar la información guardaremos el estado de las entradas/salidas en palabras INT.

Tenemos que realizar los siguientes pasos en el PLC:

  • Convertir el estado de 16 entradas en una palabra INT.
  • Convertir el estado de 16 salidas en una palabra INT.

Estos en el programa del PC (vb.net) para guardar:

  • Guardar en un archivo las palabras cuando se produzca un cambio.

Y éstos en el programa del PC (vb.net) para reconstruir el pasado:

  • Leer los registros en un intervalo de tiempo.
  • Decodificarlos a binario para poder ver cada I/O por separado.
  • Mostrar en una tabla los cambios y el momento.
  • Mostrar gráficamente estos estados.

Programa de PLC.

Lógicamente tendremos que declarar las entradas/salidas y dos variables que guardarán el estado de las I/0 en formato INT.

El resto del programa es asignar las entradas a la variable EstadoIn y las salidas a la variable EstadoOut. Para extraer un bit de una palabra basta con poner un “.” Y el número de bit. Por ejemplo Var.2 es el bit 2 de la variable Var.

Y esta es la función que convierte valores INT ó UINT a binario. Mediante esta función podremos obtener el estado de un bit, guardar grupos de valores binarios en un valor INT, etc.

La figura muestra el cálculo necesario. Consiste en dividir entre dos mientras el resto sea mayor de uno. Los bit serán los cociente.

Y esta es la función:

A esta función le entregamos un valor INT o UINT y nos devuelve un texto con el valor en binario 16 bit. Más tarde haremos otra función que usando esta nos devuelva el valor (TRUE/FALSE) de un bit.

INT vs UINT.

Los valores INT van desde -32767 hasta + 32767. O lo que es lo mismo, un valor de 15 bit (2 ^ 15) y un bit para el signo. Los valores UINT van desde 0 hasta 65536 y no existen valores negativos (2^16). Cuando leemos una variable del PLC desde vb.net u otro programa, no sabemos cómo se declaró en el PLC , leemos los bits y lo reconstruimos a INT o UINT. Por lo tanto podemos tener valores en el plc declarados como INT y en el PC reconstruidos como UINT. También podemos convertir valores UINT a INT.

Bien, en la primera parte de la función comprobamos si el valor es negativo, en ese caso sabemos con seguridad que es INT y lo convertimos a UINT para descomponerlo en bit. En los casos que sea positivo, no hay diferencias entre los dos tipos a la hora de convertirlos a bits.

Una vez hecho esto lo que hacemos es dividir entre dos el valor entregado en un bucle While End While. Este bucle se repite hasta que se cumple la condición de que el cociente sea < 1.

Mientras hacemos el bucle se almacenan los valores en una matriz de caracteres que contendrá 1 ó 0. Una vez terminado el bucle se convierte en una cadena de texto. Al pasarlo a cadena de texto rellenamos los valores vacíos con 0 hasta completar los 16 bits.

Esta misma función la utilizaremos para realizar otras tareas, como saber el estado de un bit (TRUE/FALSE).

En la próxima entrada realizaré el programa completo de registrar en el PC, cuando se produzca un cambio.

Registro de estados II. Guardar en PC.

En este proyecto añadiremos una mejora. Vamos a crear un control de usuario con el botón conectar. En otras palabras, construimos un botón con funcionalidad añadida. En nuestro caso el código necesario para conectarse al PLC. Si la conexión se realiza con éxito pondrá a true una variable pública de manera que podamos consultar el estado de la conexión. Si estamos conectados al PLC, el botón pasara a hacer la función de desconexión.

En este caso, dejaré el proyecto para descargar y me centro en explicar las funciones.

Una de las ventajas que debemos de aprovechar de vb.net es la llamada POO (programación orientada a objetos) aunque en un principio la utilizaremos de una forma muy rudimentaria, puesto que esto tiene muchas posibilidades y por lo tanto puede llegar a ser compleja. Empezaremos por crear pequeños controles reutilizables que nos ahorren algo de trabajo. Nuestro primer control será un botón que haga la función de conectar al PLC.

Vamos a agregar un control de usuarios, como muestra la siguiente imagen.

El control le pondremos ucBuConectar y agregar.

Dentro del pequeño formulario introducimos un botón y le cambiamos la propiedad Dock a Fill (El botón del centro) y el Text a Conectar. Reducimos el tamaño del formulario al ancho y largo del Boton, y pinchamos con el derecho ver código.

En el código crearemos unas propiedades que vendrían a ser como variables IN/OUT de los FB en programación del PLC. Para crear este código podemos utilizar una función del IDE que nos ahorra trabajo y nos ayuda con la memoria.

Seleccionamos Insertar fragmento de código del menú contextual que aparece al pinchar con el derecho el área de código.

Una vez seleccionado el código que queremos utilizar los escribirá por nosotros. Las áreas resaltadas en verde tienen ciertas características especiales. Las propiedades tienen dos variables, una que se utiliza dentro del control y otra pública que será accesible desde fuera del control. Podemos llamar a ambas variables con el mismo nombre al que añadimos algo para modificarlo, en mi caso la variable interna tendrá un carácter de subrayado ” _Var” y la pública, no ” Var”.

Los pasos son: modificar la variable interna, pulsamos tabulador y modificamos el tipo, en este caso boolean:

Y ahora modificamos la variable pública y tabulador. Automáticamente se modifica el resto de código y ya tenemos la propiedad lista para usarla.

Este control utilizará el evento del botón para conectarse al PLC. Hacemos doble click en el botón y escribimos el siguiente código:

El proyecto en detalle:

Cuando se inicia la aplicación se cargan los datos de la conexión en el control ucBuConectar.

Bien, cuando el usuario pulsa el botón, éste se conectará al PLC y activa un temporizador, como podemos ver en el código del ucBuConectar, en esta línea:

Form1.TimerRuntime.Enable = TRUE

Trascurrido el tiempo definido en las propiedades de el control Timer el programa se dirige hasta el siguiente evento:

De esta manera emulamos el funcionamiento de un PLC en el PC, creando una tarea repetitiva que actualiza el valor de las variables del PLC y demás tareas repetitivas que necesite nuestra aplicación. Cuando pasa por la función LeerUINT se dirige a esta función, que leerá las variables para almacenarlas en la variable global Valor. Cuando queramos consultar el valor de estas variables simplemente valor = Valor(numero de marca).

En el primer paso por el código, se inicializa las variables MemoIn y MemoOut con los valores de las variables del plc, y ponemos ConfInicial a True para que no vuelva a entrar. Lo siguiente es comprobar si ha cambiado el valor de las entradas o salidas, de ser así lo muestra en la texbox, que hemos llamado tbTrace y llama a la función Grabar.

Y tan fácil como el siguiente código para grabar un log.

Y éste es el resultado:

Naturalmente esto no tiene sentido para nosotros, y necesitaremos otra aplicación para convertir estos datos en una tabla, y un gráfico que nos ayude a visualizar de una forma comprensible esta información.

Desde aquí podéis descargar el proyecto PLC y PC. Os recuerdo que el este archivo se encuentra en el área de descarga del foro de Infoplc. Por lo tanto será necesario estar registrado para poder descargarlo. De todas formas, si alguien  no quiere registrarse solo tiene que escribir un comentario con su dirección correcta y os lo enviaré.

En la próxima entrada veremos como reconstruir estos datos para mostrarlos en una tabla y en un gráfico. Bueno, no sé si en la próxima o en las próximas.

Capacidad de refresco de variables vb.net Beckhoff

Cuando hago pruebas con sistemas que no he utilizado antes, me gusta saber sus límites, así que he preparado un pequeño proyecto para poner a prueba vb.net y beckhoff. La idea es leer variables y medir el tiempo empleado. Como queremos poner el sistema al límite lo haremos con bucles. Resumiendo, leeremos un número de variables un determinado número de veces. En el programa de PLC declaramos una variable matriz y hacemos bucles igualmente para generar mucho trabajo con poco código.

Pasamos a la acción. Hoy no dejaré colgado el proyecto, si alguien lo quiere que lo pida. La idea es dejaros los pasos para hacerlo todo desde el principio.

Abrimos Visual Studio y pinchamos en nuevo.

Seleccionamos aplicación Windows form y ponemos el nombre barbaridades, por ejemplo.

En el Form1 ponemos los controles como en la siguiente figura:

El control GroupBox nos sirve para ordenar controles relacionados. Lo ponemos, lo dimensionamos y metemos dentro los dos label y los dos textbox. Cambiamos las siguientes propiedades:

Textbox1.Name à tbNRegistros

Textbox1.Text à 1000

Textbox1.TextAling à Right

Textbox1.Name à tbNVeces

Textbox1.Text à 10

Textbox1.TextAling à Right

El siguiente paso será importar la referencia a la librería para la comunicación con Beckhoff. En la pestaña explorador de soluciones click derecho sobre barbaridades y en el menú contextual agregar referencia.

Seleccionamos la pestaña examinar y buscamos C:\TwinCat\ADS Api\.NET\V2.0.XXXX\TwinCat.Ads. Doble click y ya tenemos la referencia a esta librería.

Hacemos click con el derecho en el form y seleccionamos ver código. Ya tenemos una referencia a la librería de beckhoff por lo que ya podríamos utilizarla llamando a las funciones con su ruta completa TwinCat.Ads.LaFuncionQueSea. Para evitar tener que poner todo esto añadimos al principio de nuestro código Imports TwinCat.Ads. He declarado una variable Dim Valor(200000) As Integer. Esta es una variable de matriz con 200.000 elementos de tipo integer. En esta variable almacenaremos las lecturas de las variables del PLC. Una variable conectado donde almacenamos el estado de conexión con el PLC.

Evento Form Load.

Si hacéis doble click sobre el form en la vista diseño se genera el gestor del evento Form1_Load. Este evento se ejecuta al iniciarse la aplicación. En este caso lo que he puesto es para ocultar el botón prueba. Más tarde, cuando se conecte al plc se hará visible.

Evento Button_Conectar.Click.

Para conectar al plc hacemos uso de la función conectar. Esta función intentará conectarse al PLC y nos devuelve un valor integer en función del resultado. Los posibles valores son:

1 Conexión realizada con éxito

-1 Error de conexión.

-2 Se conectó pero el PLC está en STOP.

¿Qué hace el código del evento? Lo primero que hacemos es comprobar si ya estamos conectados, en cuyo caso entendemos que el usuario quiere desconectarse. Se llama a la función desconectar, se pone la variable conectado en false, se oculta el botón probar, cambiamos el botón conectar por el texto conectar y salimos del evento.

En el caso de estar conectados la sentencia Exit Sub nos saca del gestor de eventos por lo tanto no llegará a leer la parte de código donde se realiza la conexión. De no ser así, y estar desconectado al PLC se lee Dim Resultado = Conectar(“192.168.0.2.1.1″), 801). Mediante esta línea de código llamamos a la función Conectar y recibimos el resultado en la variable de mismo nombre.

Bien, intentamos conectarnos y analizamos el resultado. Si es = 1 mostramos un cuadro de diálogo con el mensaje Conectado con éxito, ponemos la variable conectado = True, cambiamos el texto del botón por Desconectar y mostramos el botón probar. Si el resultado fuera -2 u otro mostramos el mensaje correspondiente.

Nótese que al llamar a la función Conectar tenemos que proporcionar dos parámetros, dirección y puerto que serán los correspondientes en cada caso.

La siguiente ilustración muestra las funciones que utilizamos para comunicar con el PLC. En este caso y a diferencia con el anterior post vamos a utilizar las funciones en la misma clase.

Lo primero que nos encontramos en la palabra clave Región que nos permite estructurar el código para que sea más fácil la lectura. Simplemente es para hacer separaciones pero no afecta en nada. Se trata de encerrar el código que queremos separar de esta manera:

#Región “Un nombre representativo”

‘El código que proceda

#End Region

Tenemos tres funciones, una para conectar al PLC, otra para leer marcas internas globales y la última para desconectar.

La función conectar:

Crea un cliente, se conecta y configura el TimeOut en 5000 ms, posteriormente comprueba si el estado del PLC está en RUN y devuelve un valor en función de esto. Si se produce una excepción (un error) nos devolverá -2.

La función leerM:

Nos pide una variable inicial y una cantidad de variables. La lectura de variables se realiza por flujos de datos. Este es un aspecto muy importante de esta comunicación. Supongamos una cadena donde hay eslabones negros y blancos. Un valor integer sería una cadena con 16 eslabones. Dos, una cadena con 32 eslabones y asi sucesivamente. Ahora cogemos la cadena y la metemos en una caja y dejamos la punta asomando. La caja es superdura y no la podemos abrir. Pues así de fácil, le pedimos al PLC cuatro variables de tipo integer y nos devuelve una caja con una cadena de 64 eslabones. Nosotros recepcionamos la caja, tiramos de la cadena y contamos eslabones, cuando contamos 16 miramos los colores y calculamos el valor y así seguiremos contando y calculando hasta completar la cadena. Con esta analogía el código quedaría así:

Prepara para recibir una cadena AdsStream

Prepara una caja para la cadena Read.

La cadena tiene un tamaño de Cantidad * 2 (Como lee Byte y nosotros queremos INT lo multiplicamos por 2, INT = 2 Byte)

PLC, por favor, prepárame una cadena empezando con el byte 16416 + VarInicio de longitud igual a Cantidad *2

Más tarde mediante un bucle For Next iremos leyendo la cadena y volcando los datos en una variable global llamada Valor.

Resumiendo, mediante esta función leemos una cantidad determinada de variables y las volcamos en una variable global en Vb.net. De esta manera para saber el valor de una variable en el PLC sólo tenemos que ver el Valor(i) donde i = M de PLC. Como leemos INT y en el PLC está en BYTE solo la i par tendrán valor.

Bueno pues en vb.net ya solo nos falta leer las variables:

Mediante la palabra clave Now vb.net nos devuelve el momento actual. Lo que hacemos en este evento es guardar el momento actual justo antes de leer las variables del PLC y justo después, leemos las variables del PLC tantas veces como le dijimos en el form, calculamos la diferencia de tiempos y lo mostramos en un cuadro de diálogo.

Esta es la variable global del PLC:

Y este es el programa:

Y este es el resultado:

No está nada mal 10.000 registros INT leídos 100 veces consecutivas en 205 milisegundos. Estos datos variarán en función del PC.

Como comunicar vb.net con Beckhoff

Esto es un pequeño tutorial para conectar ambas aplicaciones de una forma sencilla y contando que están en el mismo PC. Hay otras formas de hacerlo pero considero que esta es la mejor forma para hacer las primeras pruebas.

Lo primero que haremos es cambiar la dirección AMS. Pinchamos en el icono de TwinCat en la barra de tareas del PC y seleccionamos la última opción   del menú contextual.  Pinchamos en la ventana AMS router y cambiamos (si es necesario) la dirección AMS net Id de local computer  por la dirección ip de nuestro PC a la que añadimos .1.1 al final. Como muestra la figura.


Te pedirá que reinicies y listo. Se puede dar la situación que al reiniciar, TwinCat detecte un problema, en este caso habrá que modificar la dirección manualmente. Si se os da el caso me ponéis un comentario y os hago llegar la solución.

Ya tenemos las dos casas (TwinCat y vb.net) en el mismo edificio. Ahora añadimos el proyecto de PLC en la configuración de system manager. Click en el icono de la barra de tareas y selecciona system manager. Click con el derecho en PLC-Configuración y seleccionas Agregar a proyecto PLC…

Selecciona el proyecto. Y te mostrara esto:

Activa la configuración (flecha roja).

El siguiente paso es enviar el proyecto desde Plc Control. Click en el icono de TwinCat y selecciona Plc Control.

Abre el proyecto. Ahora le diremos con que plc queremos conectar:

Click en Choose Run-Time System. Y seleccionamos el Run-Time de la dirección correcta:

Por último debemos enviar el proyecto al PLC. Al hacer click en Login, nos avisa que no hay programa en el PLC. ¿Quieres enviarlo?. Le decimos que sí, y una vez enviado click en Run.

Si todo ha ido bien, tenemos que ver el estado del PLC en Run en la barra de estado.

Pues ya sólo falta abrir la aplicación vb.net ir al código

Cambiamos la dirección AMS

Le damos al play y listo. ¿Listo? Eso espero.

Cuando la aplicación se inicia, intenta conectar con TwinCat, la clase ClassBx, se conecta con TwinCat y le pide el estado. Si la respuesta es RUN, da por buena la comunicación y muestra Conexión realizada con éxito, de no ser así, mostrará error en la comunicación. En el blog de Notas de automatización hay un ejemplo para conectar vb.net con una CX, muy recomendable, por cierto.

Todos estos ejemplos son solo para hacer pruebas y en ningún caso se deberían utilizar para aplicaciones reales.

Dirección ip y mascara de subred

Creo que esto nos puede dar algún dolor de cabeza con Beckhoff. Así que lo vemos un poquito y seguimos.

El concepto.

Supongamos que tenemos que identificar una serie de pisos que forman edificios. Por ejemplo tenemos 4 edificios con 4 casas cada uno. Yo que soy un poco raro digo: pues los identificaremos poniendo un número a cada edificio y otro a cada casa. Las casas estarán identificadas como NumeroEdificio.NumeroCasa. Como se muestra en la figura.

Bien, tenemos las casas identificadas de manera que no hay posibilidad de error. Pero la secretaria que tenemos es muy rara y solo sabe contar en binario y con ocho bit. Total que tenemos (2^8) = 256. La secretaria además de ser rara, es muy lista y  amablemente nos pregunta: ¿has pensado que pasará si el pueblo sigue creciendo?

Con estas limitaciones y previendo lo que pase en el futuro, pensamos lo siguiente:

Utilizaremos 4 cifras separadas por puntos XXX.XXX.XXX.XXX por lo que pueda pasar en un futuro. En principio los tres primeros números determinan el edificio y el último la casa. Si continuamos, con el ejemplo de la figura, las casas del edificio 1 irían desde la casa 1.1.1.1 hasta la casa 1.1.1.4. Seguimos pensando y nos preguntamos ¿Qué pasará cuando en un edificio existan más de 255 casas? La secretaria no sabe contar más de esta cifra. Es fácil, reducimos la dirección del edificio a dos cifras, por tanto las primeras dos cifras serán el edificio y las dos últimas las casas. En esas, se nos enciende la bombilla y decidimos utilizar una cosa que llamaremos máscara de subred y cuya utilidad será distinguir el tipo de numeración a utilizar.

Ahora toca pensar en binario, como nuestro edificio tiene 4 casas, por el momento será suficiente con poder contar hasta 4 (2^2), necesitamos solo dos bits para identificar las casas. Podemos utilizar la siguiente mascara de subred (11111111.11111111.11111111.11111100). De esta manera le decimos a la secretaria que solo sabe binario que todo lo que sea uno es la dirección del edificio y los 0 las casas.

Pues todo este rollo es para decir que para que dos ordenadores se vean en una red Tcp/ip las cifras de ip cuya cifra de mascara sea 255 deben ser iguales. En nuestro ejemplo las casas de un edificio. Para que se vean casas de distintos edificios necesitamos unos equipos especiales (router, etc).

También seria válido una máscara de sub red 11111111.11111111.11111100.00000000, que corresponde en números decimales a 255.255.252.0. Lo que nos permite 1024 ordenadores. (2^10).

Resumiendo, con una máscara de subred 255.255.255.0, solo se verán los PC que tienen las tres primeras cifras iguales. Y para que podamos acceder desde VB.net a las variables de TwinCat tendremos que formar parte del mismo edificio. Creo que lo mas fácil para hacer las pruebas es que cambieis la dirección AMS de TwinCat a la de vuestro PC, de esta manera:

Pinchamos en el icono de TwinCat en la barra de tareas del PC y seleccionamos la ultima opción   del menu contectual.  Pinchamos en la ventana AMS router y cambiamos (si es necesario) la dirección AMS net Id de local computer  por la dirección ip de nuestro PC a la que añadimos .1.1 al final. Como muestra la figura.

Te pedirá que reinicies y listo. Se puede dar la situación que al reiniciar, TwinCat detecte un problema, en este caso habrá que modificar la dirección manualmente. Si se os da el caso me ponéis un comentarios y os hago llegar la solución.


Vb.Net Conectando con TwinCat

Bueno, creo que lo mejor es pasar pronto a la acción y probar a conectar con el plc. He preparado un programita sencillo para el PLC y otro para vb.net de manera que podamos hacer la primera conexión. Por ahora no entraremos en como se establece la conexión y utilizaremos una clase que he creado para que nos haga el trabajo.

El programa de plc es este:

Y éstas las variables globales:

En este caso y creo que por última vez utilizaremos una aplicación de consola.

Lo primero que tenemos que hacer es poner la dirección ams y puerto de nuestro SoftPlc en nuestra aplicación vb.net

Añadimos el proyecto a System Manger y activamos la configuración.

Dale al play.

Aquí podéis descargar el ejemplo y en el próximo post analizamos el programita.