Creamos un proyecto nuevo con el Visual Basic 2008 Express y vamos a la pestaña de 'Referencias' para añadir el componente de Siemens para acceder al OPC. Pulsamos sobre Agregar... => Referencia

En la ventana de 'Agregar referencia' vamos a la pestaña 'COM' y buscamos la línea que pone 'Siemens OPC DAAutomation 2.0'. La seleccionamos y pulsamos el botón 'Aceptar'.

Nos aseguramos que bajo el epígrafe 'Espacios de nombres importados' esté marcado 'OPCSiemensDAAutomation'.

Y listo, nuestro proyecto ya tiene acceso a las funciones y componentes para realizar una comunicación OPC.
Ahora vamos a la programación propiamente dicha. Al principio todos los ejemplos que encontré estaban basados en Visual Basic 6.0 y al incorporarlos a Visual Basic .NET no funcionaban. Después de buscar un poco (recomiendo el foro de la OPC foundation) encontré que había una incongruencia en los límites de las tablas (arrays) y hay que crearlas forzando que el primer elemento sea el 1 y no el 0. Después se me ocurrió que tal vez hubiese ejemplos de programación en el DVD de Simatic NET y había uno en Visual Basic .NET (sí, debí haber empezado por ahí). Échale un vistazo, está muy clarito y mi ejemplo está basado en él.
Después de las primeras pruebas exitosas decidí meter todas las variables y funciones para acceder al OPC en una clase y así simplificar el código de mi aplicación.
Creé la clase ConexionOPC, cuyo código pongo a continuación. Para usarla debes adaptar la función 'Conectar' para que acceda a los items (variables) de tu proyecto. En mi caso no llego a cien items y el velocidad de acceso es óptima, pero si lo que quieres es hacer un envío masivo de datos no creo que esta sea la mejor solución.
NOTA: Para especificar un ítem se debe hacer refiriéndose a su simbólico, p. ej. SIMATIC.WinLC.OPCCom.VB_PLC00, donde SIMATIC es el nombre del equipo, WinLC el nombre del componente, OPCCom el nombre del bloque de datos y VB_PLC00 el simbólico de la variable. Cuando introduces las variables a transmitir en el servidor OPC puedes ver como direccionarlas (ver la siguiente captura de pantalla sacada de la primera parte).

Vamos con el código:
Public Class ConexionOPC
'Objectos para establecer la comunicación OPC
Private WithEvents ServidorOPC As OPCServer
Private WithEvents GrupoOPC As OPCGroup
Private WithEvents GruposOPC As OPCGroups
Private ItemOPC() As OPCItem
Private Conectado As Boolean 'Para saber si la conexión está activada
'Si hay algún error se indica en estas variables
Public Mensaje As String
Public Detalle_Error As String
'Constructor
Public Sub New()
'Al crear el objeto, no estamos conectados
Conectado = False
End Sub
'Función para activar la conexión OPC
Public Function Conectar() As Boolean
'Si ya estoy conectado aviso y salgo.
If Conectado Then
Mensaje = "Error conexión OPC."
Detalle_Error = "Se ha intentado crear una conexión OPC cuando ya hay una creada."
Conectar = False
Exit Function
End If
Try
Mensaje = "Conectando con el servidor OPC..."
ServidorOPC = New OPCSiemensDAAutomation.OPCServer
ServidorOPC.Connect("OPC.SimaticNET")
Mensaje = "Añadiendo grupo al servidor OPC..."
GruposOPC = ServidorOPC.OPCGroups
GrupoOPC = GruposOPC.Add("GrupoG")
Mensaje = "Añadiendo Items al grupo..."
ReDim ItemOPC(100) 'Dimensionar según las necesidades
'Introducir un ítem por cada variable del PLC en la que queramos leer o escribir
'A cada ítem le asignamos un número, que debemos recordar para referirnos a él en el programa
ItemOPC(0) = GrupoOPC.OPCItems.AddItem("SIMATIC.WinLC.OPCCom.VB_PLC00", 0)
' Añadir más items...
'...
Catch ex As Exception
Detalle_Error = "Error: " & ex.ToString
Conectado = False
Conectar = False
Exit Function
End Try
Mensaje = "Conexión OPC realizada correctamente."
Detalle_Error = ""
Conectado = True
Conectar = True
End Function
'Función para deshacer la conexión OPC
Public Function Desconectar() As Boolean
Try
Mensaje = "Desconectando..."
ItemOPC = Nothing
If Not IsNothing(ServidorOPC) Then
ServidorOPC.OPCGroups.RemoveAll()
ServidorOPC.Disconnect()
ServidorOPC = Nothing
End If
GrupoOPC = Nothing
GruposOPC = Nothing
Catch ex As Exception
Detalle_Error = "Error: " & ex.ToString
Desconectar = False
Exit Function
End Try
Mensaje = "Desconexión realizada correctamente."
Detalle_Error = ""
Conectado = False
Desconectar = True
End Function
'Función para escribir en un ítem que representa una variable entera
'Se le pasa el índice del ítem y el valor que vamos a escribir
'Si todo va bien devuelve True
Public Function EscribirItemInt(ByVal Indice As Integer, ByVal Entero As Integer) As Boolean
' The typelibrary OPCSiemensDAAutomation was imported with /sysarray by default,
' thus we have to create arrays with lower bound = 1
Dim Dims() As Integer = New Integer() {1}
Dim Bounds() As Integer = New Integer() {1}
Dim Serverhandles As Array = Array.CreateInstance(GetType(Integer), Dims, Bounds)
Dim Errores As Array = Array.CreateInstance(GetType(Integer), Dims, Bounds)
Dim Valores As Array = Array.CreateInstance(GetType(Object), Dims, Bounds)
If Not Conectado Then
Mensaje = "Error, conexión OPC ausente."
Detalle_Error = "Se ha intentado acceder a un Ítem sin haber establecido una conexión OPC."
EscribirItemInt = False
Exit Function
End If
Try
Serverhandles.SetValue(ItemOPC(Indice).ServerHandle, 1)
Errores.SetValue(0, 1)
Valores.SetValue(Entero, 1)
GrupoOPC.SyncWrite(1, Serverhandles, Valores, Errores)
Catch ex As Exception
Detalle_Error = ex.ToString
Mensaje = "¡Error al escribir Item! [Int, Índice " & Indice & "]"
EscribirItemInt = False
Exit Function
End Try
Mensaje = ""
Detalle_Error = ""
EscribirItemInt = True
End Function
'Función para escribir en un ítem que representa una variable booleana
'Se le pasa el índice del ítem y el valor que vamos a escribir
'Si todo va bien devuelve True
Public Function EscribirItemBool(ByVal indice As Integer, ByVal Bit As Boolean) As Boolean
' The typelibrary OPCSiemensDAAutomation was imported with /sysarray by default,
' thus we have to create arrays with lower bound = 1
Dim Dims() As Integer = New Integer() {1}
Dim Bounds() As Integer = New Integer() {1}
Dim Serverhandles As Array = Array.CreateInstance(GetType(Integer), Dims, Bounds)
Dim Errores As Array = Array.CreateInstance(GetType(Integer), Dims, Bounds)
Dim Valores As Array = Array.CreateInstance(GetType(Object), Dims, Bounds)
If Not Conectado Then
Mensaje = "Error, conexión OPC ausente."
Detalle_Error = "Se ha intentado acceder a un Ítem sin haber establecido una conexión OPC."
EscribirItemBool = False
Exit Function
End If
Try
Serverhandles.SetValue(ItemOPC(indice).ServerHandle, 1)
Errores.SetValue(0, 1)
Valores.SetValue(Bit, 1)
GrupoOPC.SyncWrite(1, Serverhandles, Valores, Errores)
Catch ex As Exception
Detalle_Error = ex.ToString
Mensaje = "¡Error al escribir Item! [Bool, Índice " & indice & "]"
EscribirItemBool = False
Exit Function
End Try
Mensaje = ""
Detalle_Error = ""
EscribirItemBool = True
End Function
'Función para leer un ítem que representa una variable entera
'Se le pasa el índice del ítem que vamos a leer
'Si todo va bien devuelve el valor de la variable
Public Function LeerItemInt(ByVal Indice) As Integer
Dim Valor As Object = Nothing
Dim Calidad As Object = Nothing
Dim TimeStamp As Object = Nothing
If Not Conectado Then
Mensaje = "Error, conexión OPC ausente."
Detalle_Error = "Se ha intentado acceder a un Ítem sin haber establecido una conexión OPC."
LeerItemInt = 0
Exit Function
End If
Try
ItemOPC(Indice).Read(OPCDataSource.OPCDevice, Valor, Calidad, TimeStamp)
LeerItemInt = CInt(Valor.ToString)
Catch ex As Exception
Detalle_Error = ex.ToString
Mensaje = "¡Error al leer Item! [Int, Índice " & Indice & "]"
LeerItemInt = 0
Exit Function
End Try
Mensaje = ""
Detalle_Error = ""
End Function
'Función para leer un ítem que representa una variable booleana
'Se le pasa el índice del ítem que vamos a leer
'Si todo va bien devuelve el valor de la variable
Public Function LeerItemBool(ByRef Indice) As Boolean
Dim Valor As Object = Nothing
Dim Calidad As Object = Nothing
Dim TimeStamp As Object = Nothing
If Not Conectado Then
Mensaje = "Error, conexión OPC ausente."
Detalle_Error = "Se ha intentado acceder a un Ítem sin haber establecido una conexión OPC."
LeerItemBool = 0
Exit Function
End If
Try
ItemOPC(Indice).Read(OPCDataSource.OPCDevice, Valor, Calidad, TimeStamp)
If StrComp(Valor.ToString, "True") = 0 Then
LeerItemBool = True
Else
LeerItemBool = False
End If
Catch ex As Exception
Detalle_Error = ex.ToString
Mensaje = "¡Error al leer Item! [Bool, Índice " & Indice & "]"
LeerItemBool = False
Exit Function
End Try
Mensaje = ""
Detalle_Error = ""
End Function
End Class
Para usar esta clase en un programa primero debemos declarar un objeto:
'Objecto para establecer la comunicación OPCy luego llamamos a la función que realiza la conexión:
Public OPC As ConexionOPC
OPC = New ConexionOPC
OPC.Conectar()Viendo lo que nos devuelve la función y el contenido de las cadenas de texto OPC.Mensaje y OPC.Detalle_Error podemos saber si todo ha ido bien.
Para escribir variables hacia el PLC lo que he hecho, por ejemplo, es poner un botón y con los eventos MouseDown y MouseUp escribir un uno o un cero con la función OPC.EscribirItemBool (análogo a lo que se haría en el WinCC Flexible con las funciones ActivarBit y DesactivarBit en los eventos Pulsar y Soltar). Ejemplo:
Private Sub ButtonRearme_MouseDown(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles ButtonRearme.MouseDownLa lectura de variables es distinta ya que no tengo la manera de detectar cuando una variable cambia para leerla, así que lo que he hecho es introducir en mi proyecto el componente Timer, que lanza la función Tick cada periodo de tiempo que especifiquemos. Lo que hago en esta función es actualizar los valores de todas las variables que leo del PLC. Temía que hubiese ralentizaciones pero el servidor OPC responde muy rápido y leyendo cada 200 ms funciona perfectamente. Ejemplo:
OPC.EscribirItemBool(1, True)
End Sub
Private Sub ButtonRearme_MouseUp(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles ButtonRearme.MouseUp
OPC.EscribirItemBool(1, False)
End Sub
'Función que se ejecuta periodicamente para actualizar el estado de las variables del programa PLCHasta aquí esta guía de comunicación OPC. Si a alguien le sirve le agradecería algún comentario. Si haces cambios o mejoras estaría interesado en verlos.
Private Sub Temporizador_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Temporizador.Tick
PosicionServomotor = OPC.LeerItemInt(64)
Servomotor_OK = OPC.LeerItemBool(47)
End Sub
Voy a intentar algo parecido a esto con Omron, desde luego VB.net y un Opc que en principio sera de Matrikon o de Omron. Te enviaré cosas segun vaya haciendolo. Yo antes lo hacia con Vfoxpro y protocolo Host link, va cañon aunque ahora ya me aburre y creo que debo cambiarlo.
ResponderEliminarHola, muchas gracias. Me ha servido de ayuda, soy estudiante y necesito hacer un trabajo de como instalar y configurar un servidor opc para trabajar con un S7-300.
ResponderEliminarAunque yo el tema de visual basic no lo llevo muy bien, intentaré aprender para comprender lo que has hecho.
Muchas gracias de nuevo
Miguel, si necesitas que te aclare algo, no tienes más que decirlo.
ResponderEliminarGracias por vuestro interés.
¿Y esto hacerlo con PHP? como utilizo la misma librería es lo mismo que en visual? entiendo que siguiendo la manera de declarar de PHP
ResponderEliminarander, siento no poder ayudarte, nunca he trabajado con PHP.
ResponderEliminar¿Crees que esto serviria para un OPC de telemecanique?
ResponderEliminarSi tienes los driver de comunicaciones para Schneider te servirá, piensa que en el ejemplo usan los drivers para Siemens, si usas omron usaras las "rutinas LITE", .....
ResponderEliminarHe copiado el codigo de tu ejemplo en Visual Basic 2005 y seleccionado el servidor opc de telemecanique y no me da ningun error. Alegria!!
ResponderEliminarY justo despues primera duda: en la linea ServidorOPC.Connect("OPC.SimaticNET") ¿SimaticNET es el nombre del servidor?
Obviamente en lugar de Simatic.Net (nombre del Driver de Siemens) tendrs que utilizar el nombre del Driver que has instalado, en tu caso el de Schneider.
ResponderEliminarGracias C., parece que estás más atento que yo a los comentarios del blog ;)
ResponderEliminarExcelente mi estimado...más claro ni el agua...
ResponderEliminarHola, tengo un problema. Estoy creando un programa en VB.Net y me estoy guiando con tu información. El problema es que me sale un ERROR que dice: "No se puede convertir un objeto de tipo 'OPCAutomation.OPCServerClass' al tipo 'OPCAutomation.IOPCGroups'.
ResponderEliminarLa parte de código importante para el problema es:
Public WithEvents g_Server As OPCServer
Public WithEvents g_Group As OPCGroup
(mas adelante)
g_Group = g_Server.OPCGroups.Add("hola") -->aqui mes salta el error
Si te sirve de mas ayuda, este programa ya me funcionaba en VB6 pero al pasar a .Net me da este problema.
Espero tu respuesta. MUCHAS GRACIASSSS¡¡
Miguel
Hola. Muy buena la información. Solo que me da un ERROR creo de librerias pero no se de donde sale. Trabajo en VB.Net y declaro ServidorOPC y GrupoOPC igual que tu. Pero despues me sale el error:
ResponderEliminar"No se puede convertir un objeto de tipo 'OPCAutomation.OPCServerClass' al tipo 'OPCAutomation.IOPCGroups'. Que pasa???
Gracias por tu ayuda. Miquel
Estos dos ultimos mensajes son mios. Pensaba que no habia colgado el primer comentario.
ResponderEliminarMiquel. Graciass:-)
Hola Miquel, tiene pinta de que tienes algún conflicto de bibliotecas. ¿usas las mismas bibliotecas en .NET que en VB6?
ResponderEliminarYo tuve problemas con alguna versión del Simatic NET, y tuve que volver a versiones anteriores, aunque la última vez que hice algo con OPC,la última versión de Simatic NET no me dio problemas.
Un saludo y suerte.
Hola,
ResponderEliminarPrimero de todo felicitarte por tus aportes!
Hace poco realize una comunicacion con VBA de excel por OPC con Simatic NET y un S7-1200 en los que leia variables del PLC y las guardaba en un archivo de texto.
Ahora hace poco me he puesto con VB 2008 express y siguiendo tu ejemplo no he tenido problemas para comunicarme con el mismo PLC.
Mi pregunta es: como saco por pantalla los mensajes de la variable "Mensaje" a medida que se vayan produciendo? (lo he exo con un MsBox pero me gustaría que fuese como tipo histórico, sin que se abriesen ventanitas... no se si me explico.
Ante todo muxas gracias y felicitarte de nuevo!!!
Hola anónimo,
ResponderEliminarme alegro que mi ejemplo te haya sido útil.
Yo lo que suelo hacer es un ListBox donde voy añadiendo los textos de la variable Mensaje.
Un saludo.
Muchas gracias, está muy interesante tu aporte, sé que con esta información voy a poder establecer ese tipo de comunicación, pues llevo algun tiempo buscando.
ResponderEliminarcomo funcionaria con el plc es controllogix 1756-l71,
ResponderEliminares para todos igual?
Excelente!! Justo lo q necesitaba! Se agradece demasiado
ResponderEliminar