且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

如何将串口通信读入缓冲区并解析出完整的消息

更新时间:2023-11-08 14:11:46

您必须将串行端口通信视为流数据.任何时候您接收数据时,您都必须预料到它可能是一条完整的消息、只是部分消息或多条消息.这完全取决于数据传入的速度以及您的应用程序能够从队列中读取的速度.因此,您认为需要缓冲区是正确的.但是,您可能还没有意识到,严格通过串行端口,无法知道每条消息的开始和结束位置.这必须通过发送方和接收方之间商定的协议来处理.例如,许多人使用标准的文本开始 (STX) 和文本结束 (ETX) 字符来表示每条消息发送的开始和结束.这样,当您收到数据时,您就可以知道何时收到了完整的消息.

You have to think of Serial Port communications as streaming data. Any time you receive data, you have to expect that it may be a complete message, only a partial message, or multiple messages. It all depends how fast the data is coming in and how fast you application is able to read from the queue. Therefore, you are right in thinking you need a buffer. However, what you may not be realizing yet, is that there is no way to know, strictly via, the Serial Port, where each message begins and ends. That has to be handled via some agreed upon protocol between the sender and the receiver. For instance, many people use the standard start-of-text (STX) and end-of-text (ETX) characters to indicate the beginning and ending of each message send. That way, when you receive the data, you can tell when you have received a complete message.

例如,如果您使用 STX 和 ETX 字符,您可以创建这样的类:

For instance, if you used STX and ETX characters, you could make a class like this:

Public Class DataBuffer
    Private ReadOnly _startOfText As String = ASCII.GetChars(New Byte() {2})
    Private ReadOnly _endOfText As String = ASCII.GetChars(New Byte() {4})

    Public Event MessageReceived(ByVal message As String)
    Public Event DataIgnored(ByVal text As String)

    Private _buffer As StringBuilder = New StringBuilder

    Public Sub AppendText(ByVal text As String)
        _buffer.Append(text)
        While processBuffer(_buffer)
        End While
    End Sub

    Private Function processBuffer(ByVal buffer As StringBuilder) As Boolean
        Dim foundSomethingToProcess As Boolean = False
        Dim current As String = buffer.ToString()
        Dim stxPosition As Integer = current.IndexOf(_startOfText)
        Dim etxPosition As Integer = current.IndexOf(_endOfText)
        If (stxPosition >= 0) And (etxPosition >= 0) And (etxPosition > stxPosition) Then
            Dim messageText As String = current.Substring(0, etxPosition + 1)
            buffer.Remove(0, messageText.Length)
            If stxPosition > 0 Then
                RaiseEvent DataIgnored(messageText.Substring(0, stxPosition))
                messageText = messageText.Substring(stxPosition)
            End If
            RaiseEvent MessageReceived(messageText)
            foundSomethingToProcess = True
        ElseIf (stxPosition = -1) And (current.Length <> 0) Then
            buffer.Remove(0, current.Length)
            RaiseEvent DataIgnored(current)
            foundSomethingToProcess = True
        End If
        Return foundSomethingToProcess
    End Function


    Public Sub Flush()
        If _buffer.Length <> 0 Then
            RaiseEvent DataIgnored(_buffer.ToString())
        End If
    End Sub
End Class

我还应该提到,在通信协议中,通常有一个校验和字节,您可以通过它来确定消息在发送方和接收方之间的传输过程中是否被破坏.

I should also mention that, in communication protocols, it is typical to have a checksum byte by which you can determine if the message got corrupted during its transmission between the sender and the receiver.