Как читать сообщение последовательного порта в буфер и анализировать полные сообщения

Я использую следующий код для чтения значений из COM-порта:

Private port As New SerialPort("COM13", 9600, Parity.None, 8, StopBits.One)

Private Sub port_DataReceived(ByVal sender As Object, ByVal e As SerialDataReceivedEventArgs)
    Debug.Print(port.ReadExisting())
End Sub

Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
    AddHandler port.DataReceived, New SerialDataReceivedEventHandler(AddressOf port_DataReceived)
    port.Open()
End Sub

это работает просто отлично, но время от времени он не получает все данные и взамен приводит к двум строкам вместо одного.

пример был бы, если бы com-порт передавал слово "HELLO2YOU", это было похоже на:

HEL
LO2YOU

или

HELLO2
YOU

как я могу разместить буфер там, чтобы убедиться, что он имеет все данные, прочитанные перед отображением это?

спасибо!

2 ответов


вы должны думать о связи последовательного порта как потоковых данных. Каждый раз, когда вы получаете данные, вы должны ожидать, что это может быть полное сообщение, только частичное сообщение или несколько сообщений. Все зависит от того, как быстро поступают данные и как быстро приложение может читать из очереди. Поэтому вы правы, думая, что вам нужен буфер. Однако вы, возможно, еще не осознаете, что нет способа узнать, строго через последовательный порт, где каждое сообщение начинается и заканчивается. Это должно быть обработано через некоторый согласованный протокол между отправителем и получателем. Например, многие люди используют стандартные символы start-of-text (STX) и end-of-text (ETX) для указания начала и окончания каждой отправки сообщения. Таким образом, когда вы получаете данные, вы можете сказать, когда вы получили полное сообщение.

например, если вы использовали символы STX и ETX, вы можете создать такой класс:

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

Я должен также упомяните, что в протоколах связи типично иметь байт контрольной суммы, по которому вы можете определить, было ли сообщение повреждено во время его передачи между отправителем и получателем.


Это вполне нормально, последовательные порты очень медленных устройствах. С как скорость 9600 и машина не слишком сильно увязли, вы получите только один или два байта из порта, когда вы используете ReadExisting(). Отлаживать.Print () выводит Терминатор строки, поэтому вы увидите, что все полученное разбито на части.

самый простой способ исправить это-использовать ReadLine() вместо этого. Это требует, чтобы устройства отправляли специальный символ в конце строки, тот, который соответствует Последовательный порт.Строки значение свойства. Что довольно распространено, линия подачи является шаблоном.

Если нет, то вам понадобится какая-то другая схема буферизации.