F Psicrómetro

Un psicrómetro es un aparato utilizado en meteorología para medir la humedad o contenido de vapor de agua en el aire, distinto a los higrómetros corrientes. Los psicrómetros constan de un termómetro de bulbo húmedo y un termómetro de bulbo seco. La humedad puede medirse a partir de la diferencia de temperatura entre ambos aparatos. El húmedo medirá una temperatura inferior producida por la evaporación de agua. Es importante para su correcto funcionamiento que el psicrómetro se instale aislado de vientos fuertes y de la luz solar directa.

FUENTE: Wikipedia.

Pues igual que lo hacemos con termómetros de mercurio igual lo podemos hacer con sondas y un PLC. Esto nos sirve de excusa para introducir el uso de FB con tablas de datos. Algunos procesos no pueden ser calculados de forma directa y deben ser obtenidos a través de tablas de datos, como en este caso.

En este caso vamos a usar esta tabla para calcular la humedad en función de la temperatura del bulbo seco y la diferencia con el bulbo húmedo.


Y este es la función:


FUNCTION Psicometro : INT
VAR_INPUT
	TSeco :INT;
	THumedo :INT;
END_VAR
VAR
	Hr:INT;
	Dif_S_H: INT;
END_VAR
 
TSeco := TSeco / 10;
THumedo := THumedo / 10;
 
Dif_S_H := TSeco - THumedo;
 
(*Calculamos humedad en funcion de la parte entera*)
CASE TSeco OF
	5: CASE Dif_S_H OF
		0: Psicometro := 100;			1: Psicometro := 85;			2: Psicometro := 71;			3: Psicometro := 59;			4: Psicometro := 48;			5: Psicometro := 39;
		6: Psicometro := 30;			7: Psicometro := 22;			8: Psicometro := 18;			9: Psicometro := 18;			10: Psicometro := 4;
	   END_CASE ;
	6: CASE Dif_S_H OF
		0: Psicometro := 100;			1: Psicometro := 85;			2: Psicometro := 72;			3: Psicometro := 61;			4: Psicometro := 50;			5: Psicometro := 41;
		6: Psicometro := 33;			7: Psicometro := 25;			8: Psicometro := 21;			9: Psicometro := 20;			10: Psicometro := 7;
	   END_CASE ;
	7: CASE Dif_S_H OF
		0: Psicometro := 100;			1: Psicometro := 86;			2: Psicometro := 73;			3: Psicometro := 62;			4: Psicometro := 52;			5: Psicometro := 43;
		6: Psicometro := 35;			7: Psicometro := 28;			8: Psicometro := 24;			9: Psicometro := 23;			10: Psicometro :=10;
	   END_CASE ;
	8: CASE Dif_S_H OF
		0: Psicometro := 100;			1: Psicometro := 86;			2: Psicometro := 74;			3: Psicometro := 63;			4: Psicometro := 54;			5: Psicometro := 45;
		6: Psicometro := 37;			7: Psicometro := 30;			8: Psicometro := 26;			9: Psicometro := 25;			10: Psicometro := 13;
	   END_CASE ;
	9: CASE Dif_S_H OF
		0: Psicometro := 100;			1: Psicometro := 86;			2: Psicometro := 75;			3: Psicometro := 65;			4: Psicometro := 55;			5: Psicometro := 47;
		6: Psicometro := 39;			7: Psicometro := 32;			8: Psicometro := 28;			9: Psicometro := 27;			10: Psicometro := 16;
	   END_CASE;
	10: CASE Dif_S_H OF
		0: Psicometro := 100;			1: Psicometro := 87;			2: Psicometro := 76;			3: Psicometro := 66;			4: Psicometro := 57;			5: Psicometro := 48;
		6: Psicometro := 41;			7: Psicometro := 34;			8: Psicometro := 30;			9: Psicometro := 29;			10: Psicometro := 18;
	    END_CASE;
	11: CASE Dif_S_H OF
		0: Psicometro := 100;			1: Psicometro := 88;			2: Psicometro := 77;			3: Psicometro := 67;			4: Psicometro := 58;			5: Psicometro := 50;
		6: Psicometro := 43;			7: Psicometro := 36;			8: Psicometro := 32;			9: Psicometro := 31;			10: Psicometro := 20;
	    END_CASE;
	12: CASE Dif_S_H OF
		0: Psicometro := 100;			1: Psicometro := 88;			2: Psicometro := 78;			3: Psicometro := 68;			4: Psicometro := 59;			5: Psicometro := 52;
		6: Psicometro := 44;			7: Psicometro := 38;			8: Psicometro := 34;			9: Psicometro := 33;			10: Psicometro :=22;
	    END_CASE;
	13: CASE Dif_S_H OF
		0: Psicometro := 100;			1: Psicometro := 89;			2: Psicometro := 78;			3: Psicometro := 69;			4: Psicometro := 61;			5: Psicometro := 53;
		6: Psicometro := 46;			7: Psicometro := 40;			8: Psicometro := 36;			9: Psicometro := 34;			10: Psicometro :=25;
	    END_CASE;
	14: CASE Dif_S_H OF
		0: Psicometro := 100;			1: Psicometro := 89;			2: Psicometro := 79;			3: Psicometro := 70;			4: Psicometro := 62;			5: Psicometro := 54;
		6: Psicometro := 47;			7: Psicometro := 41;			8: Psicometro := 37;			9: Psicometro := 36;			10: Psicometro :=26;
	    END_CASE;
	15: CASE Dif_S_H OF
		0: Psicometro := 100;			1: Psicometro := 89;			2: Psicometro := 80;			3: Psicometro := 71;			4: Psicometro := 63;			5: Psicometro := 55;
		6: Psicometro := 49;			7: Psicometro := 43;			8: Psicometro := 39;			9: Psicometro := 37;			10: Psicometro :=28;
	    END_CASE;
	16: CASE Dif_S_H OF
		0: Psicometro := 100;			1: Psicometro := 90;			2: Psicometro := 80;			3: Psicometro := 72;			4: Psicometro := 64;			5: Psicometro := 57;
		6: Psicometro := 50;			7: Psicometro := 44;			8: Psicometro := 40;			9: Psicometro := 39;			10: Psicometro := 30;
	    END_CASE;
	17: CASE Dif_S_H OF
		0: Psicometro := 100;			1: Psicometro := 90;			2: Psicometro := 81;			3: Psicometro := 72;			4: Psicometro := 65;			5: Psicometro := 58;
		6: Psicometro := 52;			7: Psicometro := 46;			8: Psicometro := 42;			9: Psicometro := 40;			10: Psicometro :=31;
	    END_CASE;
	18: CASE Dif_S_H OF
		0: Psicometro := 100;			1: Psicometro := 90;			2: Psicometro := 81;			3: Psicometro := 73;			4: Psicometro := 66;			5: Psicometro := 59;
		6: Psicometro := 53;			7: Psicometro := 47;			8: Psicometro := 43;			9: Psicometro := 41;			10: Psicometro :=33;
	    END_CASE;
	19: CASE Dif_S_H OF
		0: Psicometro := 100;			1: Psicometro := 91;			2: Psicometro := 82;			3: Psicometro := 74;			4: Psicometro := 66;			5: Psicometro := 60;
		6: Psicometro := 54;			7: Psicometro := 48;			8: Psicometro := 44;			9: Psicometro := 42;			10: Psicometro :=34;
	    END_CASE;
	20: CASE Dif_S_H OF
		0: Psicometro := 100;			1: Psicometro := 91;			2: Psicometro := 82;			3: Psicometro := 74;			4: Psicometro := 67;			5: Psicometro := 61;
		6: Psicometro := 55;			7: Psicometro := 49;			8: Psicometro := 46;			9: Psicometro := 43;			10: Psicometro :=36;
	   END_CASE;
 
ELSE
		Psicometro := 0;
END_CASE;

Mediante las dos primeras instrucciones dejamos la parte entera. El Fb esta hecho para temperatura en ºC x 10, por lo tanto el ultimo dígito es el decimal que quitaremos puesto que la tabla solo contempla valores enteros.  La variable Dif_S_H almacena la diferencia entre los dos termómetros.

Lo siguiente es la primera instrucción Case mediante la cual seleccionamos la temperatura de bulbo seco, de aquí pasara a la siguiente función Case donde asignara un valor de humedad en función de la diferencia (Dif_S_H).

No se si esto puede tener una aplicación practica. Quizás sea mas interesante hacerlo en Vb.net con un par de sondas de temperatura 1 wire y un pequeño ventilador. (Me lo apunto para hacerlo un día que tenga tiempo).


Break Point en Plc Control.

En el anterior post, comprobando registros, hice un pequeño programa que controlaba una gran cantidad de registros. Poco código para mucho trabajo. Pero todo tiene sus inconvenientes. Uno de los problemas, sin duda es la depuración, puesto que el tiempo para comprobar un registro es el del ciclo de scan. Para mí, esto de la programación de PLC navega hacia la programación informática, o dicho de otra forma, cada vez se parece más. Bien pues uno de los recursos de depuración al que habremos de acostumbrarnos son los Break Point. Esto no es nada más que detener el programa en un punto determinado para, por ejemplo, comprobar el estado de las variables.

El anterior post lo he editado, la razón es que al revisar los manuales me di cuenta que las estructuras tienen la limitación de que no pueden ser direccionadas (AT %MW). Me sorprende que te permita hacerlo y no de error al compilar, pero en fin esto es lo que hay. Lo que está claro es que si dice que no se puede es mejor no hacerlo puesto que esto al final tendrá sus consecuencias. En un próximo post pondré el programa modificado.

Online in security mode.

Una de las cosas que recomiendo al que quiera trabajar con Plc Control es trabajar en modo seguro en línea. Para esto debemos ir a Project —- > Option — > Desktop y marcar la casilla Online in Security mode. De esta manera siempre nos pedirá una confirmación de la orden. Si marcamos un BreakPoint nos preguntara si realmente queremos hacerlo, al forzar un valor, etc.

Break Point.

Para detener un programa en un punto determinado solo tendremos que pinchar en la línea de programa deseado:

Una vez puesto el BreakPoint, se marca la línea en azul, el programa no se detendrá hasta que llegue a ese punto. Si esta dentro de una condición como IF, el programa continuara hasta que se cumpla la condición. Esto también nos permite saber si se cumple esta condición. Cuando el programa se detiene la línea se marca en rojo.

En la barra de estados nos aparece BP marcado en rojo. Esto es importante. Si esta marcado, cuando reiniciemos el programa, se volverá a parar y si esta desmarcado continuara normalmente. Para reiniciar el programa simplemente pulsamos en Run.

Es importante tener presente que cuando el programa se detiene en un Break Point, no se refrescan las entradas salidas, de esta manera, si una salida estaba activa, continuara activa mientras el programa este parado. Como se entenderá esto puede provocar daños considerables si se utiliza mal.

Step over.

Pasó a paso por instrucción. Una vez detenido el programa en un Break Point podemos continuar pulsando en Run o podemos hacerlos paso a paso por instrucción (el icono con dos zapatos). Esto nos permite avanzar línea a línea comprobando el programa.

Control de registros I.

Bueno, aquí estamos de nuevo, he tenido que aplicarme en terminar algunas cosas que tenía pendiente y que ya no podían esperar más. Como comprenderéis uno tiene que seguir estudiando, probando, etc. Y en ocasiones le faltan horas al día. Pensé en que me llevaría poco tiempo pero no ha sido asi. En fin vamos al grano.

He preparado una pequeña aplicación para controlar registros, en mi caso, control de temperaturas, pero igual valdría para presiones, humedad, co2, etc. La idea es comprobar que estos registros se mantengan dentro de unos márgenes, si se salen se activará una prealarma y si superan un tiempo preestablecido se activa una salida, que por ejemplo podría enviar un mail, un sms o encender una sirena.

El proyecto consta de dos partes, una aplicación para PLC y otra para PC, donde monitorizamos estos registros. Aunque parece una aplicación sencilla, he querido mostrarla en el blog ya que con apenas unas líneas de código podremos controlar una gran cantidad de registros. Además será muy sencillo variar el número de registros.

Para empezar creamos una estructura que contendrá las variables de cada registro.

Y el paso siguiente es crear una matriz con esta estructura en variables globales:

La he direccionado con %MW3000 para posteriormente leer estos registros desde vb.net. Esto no es necesario ya que existe la posibilidad de leer las variables por nombre desde vb.net. Pero como esto es un “laboratorio de pruebas”, pues pruebas.

El siguiente paso es el programa claro:

En esta primera parte del programa he creado una pequeña rutina para rellenar las variables con valores inciales:

Y este es el programa propiamente dicho:

Cada ciclo de scan revisamos un registro, si el selector esta a TRUE, comprobamos que el .Valor este dentro de rango. Esto es mayor que .Mínimo y menor que .Máximo. En caso de estar fuera de rango restamos uno al valor de la variable .Retardo. Si está a TRUE la variable Restar. Si el selector esta a FALSE forzamos el valor de las variables. Prealarma y .Alarma a FALSE y .Retardo a SpRetardo (Set Point retardo o Punto de Ajuste retardo).

Como decíamos antes, este programa está diseñado para enviar SMS. Por este motivo las alarmas se activan en secuencia. De esta manera, hasta que la variable Out no esté a FALSE no se activará la siguiente alarma. Si se producen dos alarmas consecutivas debemos enviar el primer SMS y cuando termine el siguiente.

Para modificar el número de registros a comprobar, sólo tendremos que modificar la constante NReg en el programa MAIN y el tamaño de la Matriz Reg1 en variables globales.

En las próximas entradas veremos la aplicación en vb.net y una simulación para hacer las pruebas.

EDITADO 7.08.10.

Pues hay que leer los manuales. A pesar de que el programa se compila sin error en el manual dice esto:

Así que haremos caso del manual y modificare el programa para solucionar este inconveniente.

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.