'**********************************************************************************************
'* Selector Switch for AdvancedHMI
'*
'* Archie Jacobs
'* Manufacturing Automation, LLC
'* ajacobs@mfgcontrol.com
'* 
'*
'* Copyright 2011 Archie Jacobs
'*
'*
'* Distributed under the GNU General Public License (www.gnu.org)
'*
'* This program is free software; you can redistribute it and/or
'* as published by the Free Software Foundation; either version 2
'* of the License, or (at your option) any later version.
'*
'* This program is distributed in the hope that it will be useful,
'* but WITHOUT ANY WARRANTY; without even the implied warranty of
'* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
'* GNU General Public License for more details.

'* You should have received a copy of the GNU General Public License
'* along with this program; if not, write to the Free Software
'* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
'*
'**********************************************************************************************

Public Class SelectorSwitch
    Inherits MfgControl.AdvancedHMI.Controls.IndustrialSelectorSwitch


#Region "PLC Related Properties"
    '*****************************************************
    '* Property - Component to communicate to PLC through
    '*****************************************************
    Private _CommComponent As ICommComponent
    <System.ComponentModel.Category("PLC Properties")> _
    Public Property CommComponent() As ICommComponent
        Get
            Return _CommComponent
        End Get
        Set(ByVal value As ICommComponent)
            _CommComponent = value
        End Set
    End Property


    '*****************************************
    '* Property - Address in PLC to Link to
    '*****************************************
    Private NotificationIDText As Integer
    Private m_PLCaddressText As String = ""
    <System.ComponentModel.Category("PLC Properties")> _
    Public Property PLCaddressText() As String
        Get
            Return m_PLCaddressText
        End Get
        Set(ByVal value As String)
            If m_PLCaddressText <> value Then
                m_PLCaddressText = value

                '* When address is changed, re-subscribe to new address
                SubscribeToCommDriver()
            End If
        End Set
    End Property

    '*****************************************
    '* Property - Address in PLC to Link to
    '*****************************************
    Private NotificationIDVisibility As Integer
    Private m_PLCaddressVisibility As String = ""
    <System.ComponentModel.Category("PLC Properties")> _
    Public Property PLCaddressVisibility() As String
        Get
            Return m_PLCaddressVisibility
        End Get
        Set(ByVal value As String)
            m_PLCaddressVisibility = value
        End Set
    End Property

    '*****************************************
    '* Property - Address in PLC to Link to
    '*****************************************
    Private NotificationIDValue As Integer
    Private m_PLCaddressValue As String = ""
    <System.ComponentModel.Category("PLC Properties")> _
    Public Property PLCaddressValue() As String
        Get
            Return m_PLCaddressValue
        End Get
        Set(ByVal value As String)
            If m_PLCaddressValue <> value Then
                m_PLCaddressValue = value

                '* When address is changed, re-subscribe to new address
                SubscribeToCommDriver()
            End If
        End Set
    End Property

    '*****************************************
    '* Property - Address in PLC to Link to
    '*****************************************
    Private m_PLCaddressClick As String = "Main.MyVariable"
    <System.ComponentModel.Category("PLC Properties")> _
    Public Property PLCaddressClick() As String
        Get
            Return m_PLCaddressClick
        End Get
        Set(ByVal value As String)
            If m_PLCaddressClick <> value Then
                m_PLCaddressClick = value

                '* When address is changed, re-subscribe to new address
                SubscribeToCommDriver()
            End If
        End Set
    End Property
#End Region


#Region "Events"
    '****************************************************************
    '* UserControl overrides dispose to clean up the component list.
    '* Release all subscriptions
    '****************************************************************
    <System.Diagnostics.DebuggerNonUserCode()> _
    Protected Overrides Sub Dispose(ByVal disposing As Boolean)
        Try
            If disposing And _CommComponent IsNot Nothing Then
                _CommComponent.UnSubscribe(NotificationIDText)
                _CommComponent.UnSubscribe(NotificationIDValue)
                _CommComponent.UnSubscribe(NotificationIDVisibility)
            End If
        Finally
            MyBase.Dispose(disposing)
        End Try
    End Sub

    '* This is part of the transparent background code and it stops flicker
    'Protected Overrides Sub OnPaintBackground(ByVal e As System.Windows.Forms.PaintEventArgs)
    'MyBase.OnPaintBackground(e)
    'End Sub

    Private WithEvents tmrError As New System.Windows.Forms.Timer

    '****************************
    '* Event - Mouse Down
    '****************************
    Private Sub MomentaryButton_MouseDown(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles Me.MouseDown

        If m_PLCaddressClick <> "" And Enabled Then
            Try
                Select Case OutputType
                    'Case MfgControl.AdvancedHMI.Controls.OutputType.MomentarySet : _CommComponent.WriteData(m_PLCaddressClick, 1)
                    Case MfgControl.AdvancedHMI.Controls.OutputType.MomentarySet : _CommComponent.WriteData(m_PLCaddressClick, 1, "Bit", 0)
                    Case MfgControl.AdvancedHMI.Controls.OutputType.MomentaryReset : _CommComponent.WriteData(m_PLCaddressClick, 0, "Bit", 0)
                        'Case MfgControl.AdvancedHMI.Controls.OutputType.MomentaryReset : _CommComponent.WriteData(m_PLCaddressClick, 0)
                    Case MfgControl.AdvancedHMI.Controls.OutputType.SetTrue : _CommComponent.WriteData(m_PLCaddressClick, 1, "Bit", 0)
                        'Case MfgControl.AdvancedHMI.Controls.OutputType.SetTrue : _CommComponent.WriteData(m_PLCaddressClick, 1)

                        'Case MfgControl.AdvancedHMI.Controls.OutputType.SetFalse : _CommComponent.WriteData(m_PLCaddressClick, 0)
                    Case MfgControl.AdvancedHMI.Controls.OutputType.SetFalse : _CommComponent.WriteData(m_PLCaddressClick, 0, "Bit", 0)
                    Case MfgControl.AdvancedHMI.Controls.OutputType.Toggle
                        Dim CurrentValue As Boolean
                        CurrentValue = _CommComponent.ReadAny(m_PLCaddressClick)
                        If CurrentValue Then
                            _CommComponent.WriteData(m_PLCaddressClick, 0, "Bit", 0)
                            '_CommComponent.WriteData(m_PLCaddressClick, 0)
                        Else
                            _CommComponent.WriteData(m_PLCaddressClick, 1, "Bit", 0)
                            '_CommComponent.WriteData(m_PLCaddressClick, 1)
                        End If
                    Case Else

                End Select

                If tmrError.Enabled Then
                    tmrError.Enabled = False
                End If
            Catch ex As Exception
                DisplayError("WRITE FAILED!" & ex.Message)
            End Try
        End If
        Me.Invalidate()
    End Sub



    '****************************
    '* Event - Mouse Up
    '****************************
    Private Sub MomentaryButton_MouseUp(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles Me.MouseUp
        If m_PLCaddressClick <> "" And Enabled Then
            Try
                Select Case OutputType
                    'Case MfgControl.AdvancedHMI.Controls.OutputType.MomentarySet : _CommComponent.WriteData(m_PLCaddressClick, 0)
                    Case MfgControl.AdvancedHMI.Controls.OutputType.MomentarySet : _CommComponent.WriteData(m_PLCaddressClick, 0, "Bit", 0)
                        'Case MfgControl.AdvancedHMI.Controls.OutputType.MomentaryReset : _CommComponent.WriteData(m_PLCaddressClick, 1)
                    Case MfgControl.AdvancedHMI.Controls.OutputType.MomentaryReset : _CommComponent.WriteData(m_PLCaddressClick, 1, "Bit", 0)
                End Select
            Catch ex As Exception
                DisplayError("WRITE FAILED!" & ex.Message)
            End Try
        End If

        Me.Invalidate()
    End Sub


    '********************************************************************
    '* When an instance is added to the form, set the comm component
    '* property. If a comm component does not exist, add one to the form
    '********************************************************************
    Protected Overrides Sub OnCreateControl()
        MyBase.OnCreateControl()

        If Me.DesignMode Then
            '********************************************************
            '* Search for ICommComponent component in parent form
            '* If one exists, set the client of this component to it
            '********************************************************
            Dim i = 0
            Dim j As Integer = Me.Parent.Site.Container.Components.Count - 1
            While _CommComponent Is Nothing And i < j
                If Me.Parent.Site.Container.Components(i).GetType.GetInterface("ICommComponent") IsNot Nothing Then _CommComponent = Me.Parent.Site.Container.Components(i)
                i += 1
            End While

            '************************************************
            '* If no comm component was found, then add one and
            '* point the CommComponent property to it
            '*********************************************
            If _CommComponent Is Nothing And PLCaddressClick <> "" Then
                Me.Parent.Site.Container.Add(New TwinCATComm)
                _CommComponent = Me.Parent.Site.Container.Components(Me.Parent.Site.Container.Components.Count - 1)
            End If
        Else
            SubscribeToCommDriver()
        End If
    End Sub



#End Region

#Region "Subscribing and PLC data receiving"
    '**************************************************
    '* Subscribe to addresses in the Comm(PLC) Driver
    '**************************************************
    Private SubscribedPLCAddressText As String
    Private SubscribedPLCAddressVisibility As String
    Private SubscribedPLCAddressValue As String
    Private Sub SubscribeToCommDriver()
        If Not DesignMode And IsHandleCreated Then
            '*************************
            '* Visbility Subscription
            '*************************
            If SubscribedPLCAddressText <> m_PLCaddressText Then
                '* If already subscribed, but address is changed, then unsubscribe first
                If NotificationIDText > 0 Then
                    _CommComponent.UnSubscribe(NotificationIDText)
                End If
                '* Is there an address to subscribe to?
                If m_PLCaddressText <> "" Then
                    Try
                        'NotificationIDText = _CommComponent.Subscribe(m_PLCaddressText, 250, AddressOf PolledDataReturnedText)
                        NotificationIDText = _CommComponent.Subscribe(m_PLCaddressText, 250, AddressOf PolledDataReturnedText, "Bit", 0)

                        '* If subscription succeedded, save the address and dispose of the retry timer
                        SubscribedPLCAddressText = m_PLCaddressText
                        If SubscribeRetryTimer IsNot Nothing Then
                            SubscribeRetryTimer.Enabled = False
                            SubscribeRetryTimer.Dispose()
                        End If
                    Catch ex As MfgControl.AdvancedHMI.Drivers.PLCDriverException
                        Text = ex.Message

                        '* If it is not a Symbol Not Found error, then retry
                        'If ex.ErrorCode <> 1808 Then
                        If SubscribeRetryTimer Is Nothing Then
                            SubscribeRetryTimer = New Windows.Forms.Timer
                            SubscribeRetryTimer.Interval = 10000
                            AddHandler SubscribeRetryTimer.Tick, AddressOf Retry_Tick
                        End If

                        SubscribeRetryTimer.Enabled = True
                        'Exit Sub
                        'End If
                    End Try
                End If
            End If


            '*************************
            '* Visbility Subscription
            '*************************
            If SubscribedPLCAddressVisibility <> m_PLCaddressVisibility Then
                '* If already subscribed, but address is changed, then unsubscribe first
                If NotificationIDVisibility > 0 Then
                    _CommComponent.UnSubscribe(NotificationIDVisibility)
                End If
                '* Is there an address to subscribe to?
                If m_PLCaddressVisibility <> "" Then
                    Try
                        'NotificationIDVisibility = _CommComponent.Subscribe(m_PLCaddressVisibility, 250, AddressOf PolledDataReturnedVisibility)
                        NotificationIDVisibility = _CommComponent.Subscribe(m_PLCaddressVisibility, 250, AddressOf PolledDataReturnedVisibility, "Bit", 0)

                        SubscribedPLCAddressVisibility = m_PLCaddressVisibility
                        If SubscribeRetryTimer IsNot Nothing Then
                            SubscribeRetryTimer.Enabled = False
                            SubscribeRetryTimer.Dispose()
                        End If
                    Catch ex As MfgControl.AdvancedHMI.Drivers.PLCDriverException
                        If ex.ErrorCode = 1808 Then
                            DisplayError("""" & m_PLCaddressVisibility & """ PLC Address not found")
                        Else
                            DisplayError(ex.Message)
                        End If

                        '* If it is not a Symbol Not Found error, then retry
                        'If ex.ErrorCode <> 1808 Then
                        If SubscribeRetryTimer Is Nothing Then
                            SubscribeRetryTimer = New Windows.Forms.Timer
                            SubscribeRetryTimer.Interval = 10000
                            AddHandler SubscribeRetryTimer.Tick, AddressOf Retry_Tick
                        End If

                        SubscribeRetryTimer.Enabled = True
                        'Exit Sub
                        'End If
                    End Try
                End If
            End If

            '*************************
            '* Value Subscription
            '*************************
            If SubscribedPLCAddressValue <> m_PLCaddressValue Then
                '* If already subscribed, but address is changed, then unsubscribe first
                If NotificationIDValue > 0 Then
                    _CommComponent.UnSubscribe(NotificationIDValue)
                End If
                '* Is there an address to subscribe to?
                If m_PLCaddressValue <> "" Then
                    Try
                        'NotificationIDValue = _CommComponent.Subscribe(m_PLCaddressValue, 250, AddressOf PolledDataReturnedValue)
                        NotificationIDValue = _CommComponent.Subscribe(m_PLCaddressValue, 250, AddressOf PolledDataReturnedValue, "Bit", 0)
                        SubscribedPLCAddressValue = m_PLCaddressValue

                        If SubscribeRetryTimer IsNot Nothing Then
                            SubscribeRetryTimer.Enabled = False
                            SubscribeRetryTimer.Dispose()
                        End If
                    Catch ex As MfgControl.AdvancedHMI.Drivers.PLCDriverException
                        If ex.ErrorCode = 1808 Then
                            DisplayError("""" & m_PLCaddressValue & """ PLC Address not found")
                        Else
                            DisplayError(ex.Message)
                        End If

                        '* If it is not a Symbol Not Found error, then retry
                        'If ex.ErrorCode <> 1808 Then
                        If SubscribeRetryTimer Is Nothing Then
                            SubscribeRetryTimer = New Windows.Forms.Timer
                            SubscribeRetryTimer.Interval = 10000
                            AddHandler SubscribeRetryTimer.Tick, AddressOf Retry_Tick
                        End If

                        SubscribeRetryTimer.Enabled = True
                    End Try
                End If
            End If
        End If
    End Sub

    '********************************************
    '* Keep retrying to subscribe if it failed
    '********************************************
    Private SubscribeRetryTimer As Windows.Forms.Timer
    Private Sub Retry_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs)
        SubscribeToCommDriver()
    End Sub

    '***************************************
    '* Call backs for returned data
    '***************************************
    Private OriginalText As String
    Private Sub PolledDataReturnedText(ByVal Values() As String)
        Try
            Text = Values(0)
        Catch
            DisplayError(Values(0) & "-INVALID Text VALUE RETURNED!")
        End Try
    End Sub


    Private Sub PolledDataReturnedVisibility(ByVal Values() As String)
        Try
            MyBase.Visible = Values(0)
        Catch
            DisplayError(Values(0) & "-INVALID Visibilty VALUE RETURNED!")
        End Try
    End Sub


    Private Sub PolledDataReturnedValue(ByVal Values() As String)
        Try
            SwitchState = Values(0)
        Catch
            DisplayError(Values(0) & "-INVALID Value RETURNED!")
        End Try
    End Sub
#End Region

#Region "Error Display"
    '********************************************************
    '* Show an error via the text property for a short time
    '********************************************************
    Private WithEvents ErrorDisplayTime As System.Windows.Forms.Timer
    Private Sub DisplayError(ByVal ErrorMessage As String)
        If ErrorDisplayTime Is Nothing Then
            ErrorDisplayTime = New System.Windows.Forms.Timer
            AddHandler ErrorDisplayTime.Tick, AddressOf ErrorDisplay_Tick
            ErrorDisplayTime.Interval = 5000
        End If

        '* Save the text to return to
        If Not ErrorDisplayTime.Enabled Then
            OriginalText = Me.Text
        End If

        ErrorDisplayTime.Enabled = True

        Me.Text = ErrorMessage
    End Sub


    '**************************************************************************************
    '* Return the text back to its original after displaying the error for a few seconds.
    '**************************************************************************************
    Private Sub ErrorDisplay_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles ErrorDisplayTime.Tick
        Text = OriginalText

        If ErrorDisplayTime IsNot Nothing Then
            ErrorDisplayTime.Enabled = False
            ErrorDisplayTime.Dispose()
            ErrorDisplayTime = Nothing
        End If
    End Sub
#End Region

End Class
