7 de octubre de 2011

Comunicación OPC con un PLC Logix5000 desde Visual Basic

Ya he hablado en el blog de comunicación OPC, en concreto en esta entrada traté el acceso a un WinAC desde Visual Basic. Ahora, aprovechando la configuración que tengo con SoftLogix sobre una máquina virtual, voy a hacer lo mismo.

Mi prueba consistirá en crear un proyecto para SoftLogix en el que definiré un par de tags, OPC_BIT y OPC_DINT, que me servirán para probar la comunicación. En el SoftLogix no voy a hacer nada más, simplemente le transferiré el proyecto, lo pondre en modo Run y monitorizaré el contenido de los tags. Luego generaré un enlace OPC con el RSLinx y desde un programa en Visual Basic leeré y escribiré en los tags del PLC.


Para establecer una comunicación OPC necesitaré un servidor OPC, y Rockwell nos lo proporciona integrado con el RSLinx, pero ojo, deberemos asegurarnos de que no sea la versión lite. Recomiendo echar un vistazo a este documento Cómo obtener resultados con RSLinx Classic (pdf) donde se especifica para qué sirve cada versión de RSLinx.

En el ordenador donde tengo instalado Visual Basic he instalado RSLinx Classic. Con mi SoftLogix funcionando compruebo que lo detecta.


Para obtener acceso al PLC a través de OPC deberemos configurar un Topic, que es la definición de la ruta de acceso al controlador. Para ello vamos al menú de RSLinx y pulsamos sobre DDE/OPC → Topic Configuration... y aparecerá una ventana donde seleccionaremos nuestro controlador y le asignaremos un Topic, en mi caso PRUEBA_OPC.


Aceptamos y ya podemos cerrar el RSLinx. Para comprobar que nuestro servidor OPC funciona, Rockwell suministra la herramienta OPC Test Client (se instala conjuntamente con el RSLinx). La ejecutamos y vamos al menú Server → Connect...


Selecciono RSLinx OPC Server.


Ahora tenemos que añadir un grupo, vamos al menú Group → Add Group...


Le asignamos un nombre cualquiera y aceptamos.


Ya solo falta añadir Items (que serán nuestros tags), vamos al menú Item → Add Item...


En la ventana que aparece, en la parte inferior izquierda navegamos por el árbol del servidor OPC, seleccionamos dentro de nuestro Topic la opción online y a la derecha deberán mostrarse los tags de nuestro proyecto Logix5000. Selecciono el tag que me interese y pulso el botón Add Item.


Y ya podemos ver el contenido de nuestro tag. Si modificamos su valor desde el RSLogix5000 veremos en el OPC Test Client como varía.


Ya hemos comprobado que tenemos acceso por OPC a nuestro controlador. Ahora lo que voy a hacer es la misma operación pero desde un programa en Visual Basic.

Crearemos un proyecto en Visual Basic y, para tener acceso a las funciones de comunicación OPC, deberemos agregar, en mi caso, la referencia OPC DA Automation Wrapper 2.02 (OPCDAAuto.DLL) que estará disponible en mi ordenador al tener instalado el RSLinx. Esta referencia difiere según sea la versión de RSLinx de que dispongamos; según leo en el foro de control.com  deberemos agregar RsLinxOPCAuto.DLL o incluso RsiOPCAuto.DLL si disponemos de versiones antiguas.


Comprobamos que en Espacios de nombres importados esté marcado RsiOPCAuto.


Con esto ya podemos empezar a programar. Voy a adaptar el código de la clase que programé en la entrada sobre OPC y WinAC:
Imports OPCAutomation

Public Class OPC_NdA 'OPC Notas de Automatización
    '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

    Public 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 OPCServer
            ServidorOPC.Connect("RSLinx OPC Server")

            Mensaje = "Añadiendo grupo al servidor OPC..."
            GruposOPC = ServidorOPC.OPCGroups
            GrupoOPC = GruposOPC.Add("Grupo1")

            GrupoOPC.IsActive = True
            GrupoOPC.UpdateRate = 1000
            GrupoOPC.IsSubscribed = True

            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("[PRUEBA_OPC]OPC_DINT", 0)
            ItemOPC(1) = GrupoOPC.OPCItems.AddItem("[PRUEBA_OPC]OPC_BIT", 1)

        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
        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."
            Detalle_Error = "No hay establecida 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
        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."
            Detalle_Error = "No hay establecida 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."
            Detalle_Error = "No hay establecida 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."
            Detalle_Error = "No hay establecida 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 Or Valor = 1 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

Con respecto al código que hice para Siemens simplemente he cambiado en la función Conectar el servidor OPC a RSLinx OPC Server y he adaptado la lista de items.

Para introducir los items deberemos especificar el nombre del Topic que hayamos configurado en el RSLinx entre corchetes seguido del nombre del tag, por ejemplo:

ItemOPC(0) = GrupoOPC.OPCItems.AddItem("[PRUEBA_OPC]OPC_DINT", 0)

Las funciones para leer y escribir booleanos y enteros son las mismas que las programadas en el OPC de Siemens.

Ya solo nos queda crear una interfaz sencilla para probar la conexión:


Pulsando el botón Conectar se establecerá la conexión OPC y si todo va correctamente podremos escribir y leer valores en el tag OPC_DINT y cambiar el estado del booleano OPC_BIT.

El código de este sencillo programa lo tienes en este proyecto de Visual Basic.

Y hasta aquí esta toma de contacto con la tecnología OPC en Rockwell. No es un ejemplo elaborado, pero espero que pueda servir de punto de partida para aplicaciones más complejas.

Como siempre, agradeceré cualquier comentario.

4 comentarios:

  1. WinLC RTX es un controlador en tiempo real basado en el entorno PC, se comunica de forma remota con STEP 7 y SIMATIC Computing a través
    de redes PROFIBUS, MPI o Ethernet.
    WinLC RTX es compatible con las herramientas de automatización de la gama de productos SIMATIC, p.ej. con el software de programación STEP 7 y WinCC de SIEMENS.
    ¿PODRIAS HACER UN EJEMPLO DE WinLC RTX + STEP 7 + WinCC de SIEMENS con Ethernet?
    GRACIAS, JG.

    ResponderSuprimir
  2. Hola JG. Supongo que ya has visto en el blog las entradas sobre WinAC.

    No creo que dedique ninguna entrada a WinCC, ya que es un software que nunca he visto y no lo tengo disponible.

    Lo que sí tengo es un runtime de WinCC Flexible, que tengo pensado intalar en una máquina virtual y en un futuro lo usaré para alguna entrada del blog.

    Un saludo y gracias por el interés.

    ResponderSuprimir
  3. un runtime de WinCC Flexible ???
    GR, puedes detallar mas el concepto?
    GRACIAS, JG.

    ResponderSuprimir
  4. Hola JG.

    El WinCC Flexible Runtime es un software que, instalado en un ordenador normal, te permite usarlo como un panel Simatic, programable con WinCC Flexible.

    Más información aquí: https://www.automation.siemens.com/mcms/human-machine-interface/en/visualization-software/wincc-flexible/wincc-flexible-runtime/Pages/Default.aspx

    Un saludo.

    ResponderSuprimir

Por favor, no pidas copias de programas comerciales, licencias o números de serie.