Namespace Frame
    ''' <summary>
    ''' Classe de base pour les trames Modbus
    ''' </summary>
    ''' <remarks></remarks>
    Public MustInherit Class ClsFrameModbusBase
#Region "Classes d'exception"
        ''' <summary>
        ''' Classe signalant une erreur de longueur de trame
        ''' </summary>
        ''' <remarks></remarks>
        Public NotInheritable Class LenErrorException
            Inherits IncorrectFrameException
            ''' <summary>
            ''' Constructeur de la classe
            ''' </summary>
            ''' <param name="ptByte">Trame erronée</param>
            ''' <remarks></remarks>
            Public Sub New(ByVal ptByte As Byte())
                MyBase.New("Longueur de la trame incorrect", ptByte)
            End Sub
        End Class
        ''' <summary>
        ''' Signal que le format de la trame est incorrect
        ''' </summary>
        ''' <remarks></remarks>
        Public Class IncorrectFrameException
            Inherits Exception
            ''' <summary>
            ''' Constructeur
            ''' </summary>
            ''' <param name="pReason">Raison</param>
            ''' <param name="pFrame">Trame</param>
            ''' <remarks></remarks>
            Public Sub New(ByVal pReason As String, ByVal pFrame As Byte())
                MyBase.New(pReason + " : " + Array2String(pFrame))
            End Sub
        End Class
#End Region

#Region "Enumération de type"
        ''' <summary>
        ''' Type de trame
        ''' </summary>
        ''' <remarks></remarks>
        Public Enum EFrameType
            ''' <summary>
            ''' Inconnu
            ''' </summary>
            ''' <remarks></remarks>
            Unknow = 0
            ''' <summary>
            ''' Requête
            ''' </summary>
            ''' <remarks></remarks>
            Request
            ''' <summary>
            ''' Réponse
            ''' </summary>
            ''' <remarks></remarks>
            Response
            ''' <summary>
            ''' Erreur
            ''' </summary>
            ''' <remarks></remarks>
            [Error]
        End Enum
#End Region

        ''' <summary>
        ''' Retourne la trame correspondante aux données reçues (Nothing si inconnu ou incorrect)
        ''' </summary>
        ''' <param name="pFrame">Trame reçue</param>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public Shared Function GetClassFromFrame(ByVal pFrame As Byte(), ByVal pRequest As Boolean) As ClsFrameModbusBase
            If pFrame.Length < 3 OrElse ((pFrame(1) And &H80) <> 0 And pRequest) Then
                Return Nothing
            End If
            If pRequest Then
                Select Case pFrame(1)
                    Case &H3
                        If pFrame.Length = 6 Then
                            Return New ClsFrameModbus03Req(pFrame, pFrame)
                        End If
                    Case &H4
                        If pFrame.Length = 6 Then
                            Return New ClsFrameModbus04Req(pFrame, pFrame)
                        End If
                    Case &H10
                        If pFrame.Length > 6 AndAlso CInt(pFrame(6)) + 7 = CInt(pFrame.Length) Then
                            Return New ClsFrameModbus16Req(pFrame, pFrame)
                        End If
                End Select

                Return Nothing
            Else
                Select Case pFrame(1)
                    Case &H3
                        If pFrame.Length >= 5 AndAlso pFrame.Length = CInt(pFrame(2)) + 3 Then
                            Return New ClsFrameModbus03Rep(pFrame, pFrame)
                        End If
                    Case &H83
                        Return New ClsFrameModbus03Err(pFrame, pFrame)
                    Case &H4
                        If pFrame.Length >= 5 AndAlso pFrame.Length = CInt(pFrame(2)) + 3 Then
                            Return New ClsFrameModbus04Rep(pFrame, pFrame)
                        End If
                    Case &H84
                        Return New ClsFrameModbus04Err(pFrame, pFrame)
                    Case &H10
                        If pFrame.Length = 6 Then
                            Return New ClsFrameModbus16Rep(pFrame, pFrame)
                        End If
                    Case &H90
                        Return New ClsFrameModbus16Err(pFrame, pFrame)
                End Select

                Return Nothing
            End If
        End Function


        ''' <summary>
        ''' ID de l'esclave
        ''' </summary>
        ''' <remarks></remarks>
        Private mSlaveId As Byte
        ''' <summary>
        ''' Numéro de fonction
        ''' </summary>
        ''' <remarks></remarks>
        Private mFunction As Byte

        ''' <summary>
        ''' Constructeur de la classe
        ''' </summary>
        ''' <param name="pSlaveId">Numéro d'esclave</param>
        ''' <param name="pFunction">Numéro de fonction</param>
        ''' <remarks></remarks>
        Friend Sub New(ByVal pSlaveId As Byte, ByVal pFunction As Byte)
            mSlaveId = pSlaveId
            mFunction = pFunction
        End Sub
        ''' <summary>
        ''' Récupère les données de la trame
        ''' </summary>
        ''' <param name="ptData">Trame à récupérer</param>
        ''' <remarks></remarks>
        Friend Sub New(ByRef ptData As Byte())
            mSlaveId = ptData(0)
            mFunction = ptData(1)
            ptData = GetArrayRange(ptData, 2, ptData.Length - 2)
        End Sub

        ''' <summary>
        ''' Retourne ou définit le numéro d'esclave
        ''' </summary>
        ''' <value></value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public Property SlaveId() As Byte
            Get
                Return mSlaveId
            End Get
            Set(ByVal value As Byte)
                mSlaveId = value
            End Set
        End Property
        ''' <summary>
        ''' Retourne le numéro de fonction
        ''' </summary>
        ''' <value></value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public ReadOnly Property [Function]() As Byte
            Get
                Return mFunction
            End Get
        End Property

        ''' <summary>
        ''' Retourne la trame
        ''' </summary>
        ''' <param name="pData">Données à encapluser</param>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Protected Overridable Function GetFrame(ByVal pData As Byte()) As Byte()
            Dim ltByte As New List(Of Byte)
            With ltByte
                .Add(mSlaveId)
                .Add(mFunction)
                .AddRange(pData)
            End With
            Return ltByte.ToArray
        End Function
        ''' <summary>
        ''' Retourne la trame Modbus
        ''' </summary>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Friend MustOverride Function GetFrame() As Byte()
        ''' <summary>
        ''' Retourne le type de trame
        ''' </summary>
        ''' <value></value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public MustOverride ReadOnly Property FrameType() As EFrameType
    End Class
End Namespace