Class Window1 
    ' Create an OPC DA Server Management object for each server you will
    ' connect with. We'll only connect to one server at a time, so we'll
    ' just create one and make it global to keep things simple. Make sure
    ' to use the "WithEvents" modifier so we can receive data changed
    ' notifications for our subscribed items.
    Dim WithEvents daServerMgt As New Kepware.ClientAce.OpcDaClient.DaServerMgt

    ' When we create a subscription, the server will give us a handle that
    ' we must use when referencing that subscription in the server. For
    ' example, we would use this handle to modify or cancel the subscription.
    ' In a real world application, we would probably want to have the ability
    ' to create multiple subscriptions. For example, you could create a subscription
    ' with a high update rate for items that are time critical, and a second 
    ' subscription with a lower update rate for the less time critical items.
    ' This design would help you achieve the response you need for the time
    ' critical items with minimum stress on the server, network, and devices.
    ' However, we will be working with a single subscription in this example 
    ' to keep things simple.
    Dim activeClientSubscriptionHandle As Integer

    ' Where the server subscription handle refers to a subscription within the
    ' server, you can specify a client subscription handle to refer to that same
    ' subscription here. This handle will be included in each data change
    ' notification, along with latest values for one or more items enrolled
    ' in that subscription. You should choose your client handle such that
    ' it will be easy to "look up" that subscription and its associated
    ' items. For example, the client handle might be a collection key, where
    ' each object in the collection describes the subscription and the items
    ' associated with it. Again, we will be working with a single subscription
    ' in this example to keep things simple.
    Dim activeServerSubscriptionHandle As Integer

    Private Sub Window1_Closed(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Closed
        'On unload of the form call the disconnection process for the server.
        Trace.WriteLine("Window1_Closed called")
        DisconnectOPCServer()
    End Sub

    Private Sub Window1_Loaded(ByVal sender As Object, ByVal e As System.Windows.RoutedEventArgs) Handles Me.Loaded
        'The URL describes the type of server connection and the server program ID and optional ClsID
        Dim url As String = "opcda:///Kepware.KEPServerEX.V5"
        'The client handle is a unique identifier for each client connection
        Dim clientHandle As Integer = 1

        ' The connectInfo structure defines a number of connection parameters.
        ' We will describe each of them below.
        Dim connectInfo As New Kepware.ClientAce.OpcDaClient.ConnectInfo

        ' The LocalID member allows you to specify possible language options
        ' the server may support. We will specify "en" for english.
        connectInfo.LocalId = "en"

        ' The KeepAliveTime member is the time interval, in ms, in
        ' which the connection to the server is checked by the API.
        connectInfo.KeepAliveTime = 1000

        ' The RetryAfterConnectionError tells the API to automatically
        ' try to reconnect after a connection loss. This is nice, so 
        ' we'll set to True:
        connectInfo.RetryAfterConnectionError = True

        ' The RetryInitialConnection tells the API to continue to try to
        ' establish an initial connection. This is good as long as we
        ' know for sure the server is really present and will likely allow
        ' a connection at some point. If not, we could be here for a while
        ' so best to set to False:
        connectInfo.RetryInitialConnection = False

        ' The connectFailed is set by the API. You should check this value
        ' after the Connect method. We will initialize it to False in case
        ' Connect throws an exception before it can be set.
        Dim connectFailed As Boolean = False

        ' Call the Connect API method:
        Try
            daServerMgt.Connect(url, clientHandle, connectInfo, connectFailed)
            If connectFailed = False Then
                Call Subscribe_Data()
            End If
        Catch ex As Exception
            MsgBox("Handled Connect exception. Reason: " & ex.Message)

            ' Make sure following code knows connection failed:
            connectFailed = True
        End Try

        ' Handle result:
        If connectFailed Then
            ' Tell user connection attempt failed:
            MsgBox("Connect failed")
        End If

    End Sub
    Public Sub Subscribe_Data()
        ' The client subscription handle is described above (see global
        ' activeServerSubscriptionHandle.) We can use an arbitrary value
        ' in this example since we will be dealing with only one subscription.
        ' If we were managing multiple subscriptions, we would want to use
        ' unique and meaningful handles.
        Dim clientSubscriptionHandle As Integer = 1

        ' The active parameter is used to tell the server if we require
        ' data for the subscribed items now or not. The active state can
        ' be changed later with a call to SubscriptionModify. We will
        ' start our subscription with the state specified by user.
        Dim active As Boolean = True

        ' The updateRate parameter is used to tell the server how fast we
        ' would like to see data updates. This translates roughly into how
        ' fast the server should poll the items enrolled in this subscription.
        ' This is a REQUESTED rate. The server may not be able to honor this
        ' request. This number is measured in milliseconds.
        Dim updateRate As Integer = 1000

        ' The deadband parameter specifies the minimum deviation needed
        ' to be considered a change of value. It is expressed as a percentage
        ' (0 - 100). In a real world application, you should validate text
        ' first.
        Dim deadBand As Single = 0

        ' The itemIdentifiers array describes the items we wish to enroll 
        ' in this subscription. We will include 10 items (0 - 9). Each
        ' member of the ItemIdentifier structure will be described below.
        ' We will assume 10 tags are specified in the GUI for simplicity.
        Dim itemIdentifiers(0) As Kepware.ClientAce.OpcDaClient.ItemIdentifier

        itemIdentifiers(0) = New Kepware.ClientAce.OpcDaClient.ItemIdentifier

        ' The itemName parameter is the name of the item defined in the
        ' server. This can also be the address of a dynamic tag in
        ' KepServerEx.
        itemIdentifiers(0).ItemName = "Channel_1.Device_1.R0"

        ' The ClientHandle will be used by the server to reference items in
        ' Data Changed events. This handle should uniquely identify each
        ' item enrolled in the subscription, and provide a way for us to
        ' "look up" that item.
        itemIdentifiers(0).ClientHandle = 0

        ' You could request a specific data type here. We will leave the
        ' DataType member unspecified in this example so ClientAce will
        ' provide values in their canonical data type.
        itemIdentifiers(0).DataType = Nothing  ' Assign types like this: Type.GetType("System.Int16")

        ' The revisedUpdateRate parameter is the actual update rate that the
        ' server will be using.
        Dim revisedUpdateRate As Integer

        ' Call the Subscribe API method:
        Try
            daServerMgt.Subscribe(clientSubscriptionHandle, active, updateRate, revisedUpdateRate, deadBand, itemIdentifiers, activeServerSubscriptionHandle)

            ' Handle result:

            ' Save the active client subscription handle for use in 
            ' DataChanged events:
            activeClientSubscriptionHandle = clientSubscriptionHandle

            ' Show revised update rate:
            'SubscriptionUpdateRate.Text = revisedUpdateRate

            ' Check item result ID:
            If itemIdentifiers(0).ResultID.Succeeded = False Then

                ' Show a message box if an item could not be added to subscription.
                ' You would probably use some other, less annoying, means to alert
                ' the user to failed item enrolments in an actual application. 
                MsgBox("Failed to add item " & itemIdentifiers(0).ItemName & " to subscription")
            End If

        Catch ex As Exception
            MsgBox("Handled Subscribe exception. Reason: " & ex.Message)
        End Try
    End Sub
    Private Sub daServerMgt_DataChanged(ByVal clientSubscription As Integer, ByVal allQualitiesGood As Boolean, ByVal noErrors As Boolean, ByVal itemValues() As Kepware.ClientAce.OpcDaClient.ItemValueCallback) Handles daServerMgt.DataChanged
        ' Loop over values returned. You should not assume that data
        ' for all items enrolled in a subscription will be included
        ' in every data changed event. In actuality, the number of
        ' values will likely vary each time.
        Dim itemValue As Kepware.ClientAce.OpcDaClient.ItemValueCallback

        For Each itemValue In itemValues
            ' Get the item handle. We will use the Items Client handle to update
            ' the specific control it is associated with. For larger projects we
            ' would create a control array and assocate the client handle and the
            ' control array index with each other.

            Select Case itemValue.ClientHandle
                ' Update value control based on Success and Quality:
                Case 0
                    If itemValue.ResultID.Succeeded = True And itemValue.Quality.IsGood = True Then
                        TextBox1.Text = itemValue.Value.ToString()

                        'Set control color to Green for good quality
                        TextBox1.Background = New SolidColorBrush(Colors.Green)
                    Else
                        TextBox1.Text = "Unknown (" & itemValue.Quality.Name & ")"

                        'Set control color to red for bad quality
                        TextBox1.Background = New SolidColorBrush(Colors.Red)
                    End If
            End Select
        Next
    End Sub
    Private Sub Window1_Unloaded(ByVal sender As Object, ByVal e As System.Windows.RoutedEventArgs) Handles Me.Unloaded
        'On unload of the form call the disconnection process for the server.
        Trace.WriteLine("Window1_Unloaded called")

    End Sub
    Private Sub DisconnectOPCServer()
        ' Call Disconnect API method:
        Try
            If daServerMgt.IsConnected Then
                daServerMgt.Disconnect()
            End If

        Catch ex As Exception
            MsgBox("Handled Disconnect exception. Reason: " & ex.Message)
        End Try
    End Sub
End Class
