vb.net 如何将串口通信读入缓冲区并解析出完整的消息
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/11458850/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to read serial port communication into buffer and parse out complete messages
提问by StealthRT
I am using the following code to read values from a com port:
我正在使用以下代码从 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
This works just fine, but every now and then it doesnt get all the data and in return results in two strings instead of just one.
这工作得很好,但它时不时地获取所有数据,并返回两个字符串而不是一个字符串。
An example would be if the com port was sending over the word "HELLO2YOU" it was look like:
一个例子是,如果 com 端口通过“HELLO2YOU”这个词发送,它看起来像:
HEL
LO2YOU
or
或者
HELLO2
YOU
How can i place a buffer in there so that it makes sure it has all the data read before displaying it?
我如何在那里放置一个缓冲区,以确保它在显示之前读取了所有数据?
Thanks!
谢谢!
回答by Steven Doggart
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:
例如,如果你使用 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
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.
我还应该提到,在通信协议中,通常有一个校验和字节,您可以通过它来确定消息在发送方和接收方之间的传输过程中是否被破坏。
回答by Hans Passant
This is quite normal, serial ports are very slow devices. With baudrates like 9600 and the machine not bogged down too much, you'll get only one or two bytes from the port when you use ReadExisting(). Debug.Print() outputs a line terminator so you'll see whatever is received broken up in pieces.
这是很正常的,串口是非常慢的设备。使用 9600 等波特率并且机器不会陷入太多困境,当您使用 ReadExisting() 时,您只能从端口获得一两个字节。Debug.Print() 输出一个行终止符,因此您会看到收到的任何内容都被分成几部分。
The easiest way to fix it is by using ReadLine() instead. That requires that the devices sends a special character at the end of the line, one that matches the SerialPort.NewLine property value. Which is quite common, a line feed is boilerplate.
修复它的最简单方法是改用 ReadLine()。这要求设备在行尾发送一个特殊字符,该字符与 SerialPort.NewLine 属性值匹配。这是很常见的,换行符是样板文件。
If not then you'll need some other kind of buffering scheme.
如果没有,那么您将需要一些其他类型的缓冲方案。