2012-07-12 8 views
6

Używam następujący kod do odczytu wartości z portu COM:Jak czytać szeregowy port komunikacyjny do bufora i przetwarza się kompletne wiadomości

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 

Działa to dobrze, ale co jakiś czas robi się wszystkie dane i wyniki zwracają dwa ciągi zamiast jednego.

Przykładem może być, jeśli port com wysyłał nad słowem „HELLO2YOU” było wyglądać następująco:

HEL 
LO2YOU 

lub

HELLO2 
YOU 

Jak mogę umieścić bufor tam tak, że upewnia się, że ma wszystkie dane odczytane przed wyświetleniem?

Dzięki!

Odpowiedz

9

Musisz myśleć o komunikacji portu szeregowego jako danych strumieniowych. Za każdym razem, gdy otrzymujesz dane, możesz oczekiwać, że będzie to kompletna wiadomość, tylko częściowa wiadomość lub wiele wiadomości. Wszystko zależy od tego, jak szybko dane przychodzą i jak szybko aplikacja może odczytać dane z kolejki. Dlatego masz rację myśląc, że potrzebujesz bufora. Jednak nie zdajesz sobie jeszcze sprawy z tego, że nie ma sposobu, by dokładnie wiedzieć, poprzez Serial Port, gdzie każda wiadomość zaczyna się i kończy. Musi to być obsługiwane za pośrednictwem uzgodnionego protokołu między nadawcą a odbiorcą. Na przykład wiele osób używa standardowych znaków początkowego tekstu (STX) i końca tekstu (ETX), aby wskazać początek i koniec każdej wysyłanej wiadomości. W ten sposób, gdy otrzymasz dane, możesz stwierdzić, kiedy otrzymasz kompletną wiadomość.

Na przykład, jeśli użyto STX i ETX znaków, można zrobić klasę tak:

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 

Należy również wspomnieć, że w protokołach komunikacyjnych, jest to typowy mieć bajt sumy kontrolnej przez który możesz określić, czy wiadomość została uszkodzona podczas transmisji między nadawcą a odbiorcą.

3

Jest to całkiem normalne, porty szeregowe są bardzo powolnymi urządzeniami. Przy prędkościach rzędu 9600 i niezaładaniu maszyny, otrzymasz tylko jeden lub dwa bajty z portu, gdy użyjesz ReadExisting(). Debug.Print() wyprowadza terminator linii, dzięki czemu zobaczysz, co zostało odebrane, podzielone na części.

Najprostszym sposobem naprawy jest użycie ReadLine(). Wymaga to, aby urządzenia wysyłały znak specjalny na końcu wiersza, który pasuje do wartości właściwości SerialPort.NewLine. Co jest dość powszechne, linia podawania jest wzorcem.

Jeśli nie, potrzebujesz innego rodzaju schematu buforowania.

+0

Hans ma rację, że ReadLine() to łatwy sposób na uzyskanie kompletnych wiadomości zakończonych wartością "nowej linii". Unikałbym jednak korzystania z ReadLine, ponieważ jest ona zwykle implementowana jako funkcja blokująca i pogarsza wydajność interfejsu GUI i innych zadań. Normalnie buforowałbym znaki w tablicy, dopóki nie otrzymam znaku kończącego, a następnie wywołaję parser poleceń. – Jeff

+0

Nie, nie, gdy wywołasz go w module obsługi zdarzeń DataReceived, działa on w wątku wątku. –

Powiązane problemy