2012-07-12 13 views
6

estoy usando el siguiente código para leer los valores de un puerto COM:Cómo leer la comunicación del puerto serie en tampón y analizar los mensajes completos

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 

Esto funciona muy bien, pero de vez en cuando es imposible encontrar todos los datos y, a cambio, da como resultado dos cadenas en lugar de solo una.

Un ejemplo sería si el puerto COM enviaba sobre la palabra "HELLO2YOU" era el siguiente aspecto:

HEL 
LO2YOU 

o

HELLO2 
YOU 

cómo puedo poner un amortiguador en allí para que se asegura de que tenga todos los datos leídos antes de mostrarlos?

Gracias!

Respuesta

9

Tiene que pensar en las comunicaciones de puerto serie como datos de transmisión. Cada vez que recibe datos, debe esperar que sea un mensaje completo, solo parcial o múltiple. Todo depende de qué tan rápido ingresen los datos y qué tan rápido puede leer la aplicación desde la cola. Por lo tanto, tienes razón al pensar que necesitas un buffer. Sin embargo, lo que quizás aún no se dé cuenta es que no hay forma de saber, estrictamente a través del puerto serie, dónde comienza y termina cada mensaje. Eso tiene que ser manejado a través de algún protocolo acordado entre el emisor y el receptor. Por ejemplo, muchas personas usan los caracteres estándar de inicio de texto (STX) y fin de texto (ETX) para indicar el comienzo y el final de cada envío de mensaje. De esta forma, cuando reciba los datos, podrá saber cuándo recibió un mensaje completo.

Por ejemplo, si se ha utilizado caracteres STX y ETX, usted podría hacer una clase como esta:

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 

También debería mencionar que, en los protocolos de comunicación, es típico tener un byte de suma de control mediante el cual puede determinar si el mensaje se corrompió durante su transmisión entre el emisor y el receptor.

3

Esto es bastante normal, los puertos serie son dispositivos muy lentos. Con tasas de baudios como 9600 y la máquina no se atasca demasiado, obtendrá solo uno o dos bytes del puerto cuando use ReadExisting(). Debug.Print() genera un terminador de línea para que pueda ver todo lo que se ha dividido en pedazos.

La forma más fácil de solucionarlo es mediante el uso de ReadLine() en su lugar. Esto requiere que los dispositivos envíen un carácter especial al final de la línea, uno que coincida con el valor de la propiedad SerialPort.NewLine. Lo cual es bastante común, un salto de línea es un texto repetitivo.

Si no, entonces necesitará algún otro tipo de esquema de almacenamiento en búfer.

+0

Hans tiene razón en que ReadLine() es una forma fácil de obtener mensajes completos terminados por un valor de "nueva línea". Sin embargo, evitaría usar ReadLine ya que normalmente se implementa como una función de bloqueo y degradará el rendimiento de su GUI y otras tareas. Normalmente almacenaría los caracteres en una matriz hasta que recibí el carácter de terminación, luego llamé al analizador de comandos. – Jeff

+0

No, no cuando lo llamas en el controlador de eventos DataReceived, se ejecuta en una cadena de subprocesos. –

Cuestiones relacionadas