2012-07-12 17 views
6

Tôi đang sử dụng đoạn mã sau để đọc các giá trị từ một cổng com:Làm thế nào để đọc cổng giao tiếp nối tiếp vào bộ đệm và phân tích ra thông điệp hoàn chỉnh

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 

này hoạt động tốt, nhưng tất cả bây giờ và sau đó nó không có được tất cả dữ liệu và kết quả trả về trong hai chuỗi thay vì chỉ một.

Một ví dụ sẽ là nếu cổng com đã được gửi qua từ "HELLO2YOU" người ta trông giống như:

HEL 
LO2YOU 

hoặc

HELLO2 
YOU 

Làm thế nào tôi có thể đặt một bộ đệm trong đó để nó chắc chắn rằng nó có tất cả các dữ liệu đọc trước khi hiển thị nó?

Cảm ơn!

Trả lời

9

Bạn phải nghĩ đến giao tiếp Cổng nối tiếp làm dữ liệu phát trực tuyến. Bất cứ lúc nào bạn nhận được dữ liệu, bạn phải mong đợi rằng nó có thể là một tin nhắn hoàn chỉnh, chỉ một phần tin nhắn, hoặc nhiều tin nhắn. Tất cả phụ thuộc vào tốc độ của dữ liệu và tốc độ mà ứng dụng của bạn có thể đọc được từ hàng đợi. Vì vậy, bạn có quyền suy nghĩ bạn cần một bộ đệm. Tuy nhiên, những gì bạn có thể chưa nhận ra, là không có cách nào để biết, thông qua Cổng nối tiếp, nơi mỗi thông điệp bắt đầu và kết thúc. Điều đó phải được xử lý thông qua một số giao thức thỏa thuận giữa người gửi và người nhận. Ví dụ: nhiều người sử dụng ký tự bắt đầu văn bản (STX) và cuối văn bản (ETX) chuẩn để cho biết phần bắt đầu và kết thúc của mỗi thư được gửi. Bằng cách đó, khi bạn nhận được dữ liệu, bạn có thể biết khi nào bạn đã nhận được một tin nhắn hoàn chỉnh.

Ví dụ, nếu bạn sử dụng STX và ETX ký tự, bạn có thể làm cho một lớp học như thế này:

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 

Tôi cũng nên đề cập rằng, trong các giao thức truyền thông, nó là điển hình để có một byte checksum mà bạn có thể xác định xem thông báo có bị hỏng trong quá trình truyền giữa người gửi và người nhận không.

3

Điều này là khá bình thường, cổng nối tiếp là thiết bị rất chậm. Với baudrates như 9600 và máy không bị sa lầy quá nhiều, bạn sẽ chỉ nhận được một hoặc hai byte từ cổng khi bạn sử dụng ReadExisting(). Debug.Print() xuất ra một trình kết thúc dòng, do đó bạn sẽ thấy bất kỳ thứ gì được nhận thành từng mảnh.

Cách dễ nhất để khắc phục bằng cách sử dụng ReadLine() thay thế. Điều đó yêu cầu các thiết bị gửi một ký tự đặc biệt ở cuối dòng, một ký tự khớp với giá trị thuộc tính SerialPort.NewLine. Đó là khá phổ biến, một dòng nguồn cấp dữ liệu là boilerplate.

Nếu không, bạn sẽ cần một số loại lược đồ đệm khác.

+0

Hans đúng là ReadLine() là một cách dễ dàng để nhận được thông báo hoàn chỉnh được chấm dứt bằng giá trị "dòng mới". Tuy nhiên, tôi sẽ tránh sử dụng ReadLine vì nó thường được thực hiện như một chức năng chặn và sẽ làm suy giảm hiệu suất của GUI của bạn và các tác vụ khác. Thông thường tôi sẽ đệm các ký tự vào một mảng cho đến khi tôi nhận được ký tự kết thúc, sau đó gọi trình phân tích cú pháp lệnh. – Jeff

+0

Không, không phải khi bạn gọi nó trong trình xử lý sự kiện DataReceived, nó chạy trên một thread threadpool. –