由於未來不會持續寫 VB.NET 技術相關文章,因此分類至 C# 中。
VB.NET 使用 SerialPort
在 VB.NET 使用 SerialPort 是使用 Imports System.IO.Ports 這個類別,其實現 SerialPort 去讀取 COM 資料時,有分別為 GUI(Form Application) 及 Console Application 兩種做法,都不同。
Console 版本
Console 中沒有辦法用 Me.Invoke 實踐非同步訊息機制(詳情見 Reference. 1),所以使用 SerialPort 中的一個方法 (ReadExists) ,用此方法來讀取 buffer 中現有的資料,且也不可以使用 ReadLine() 的方法,因為 ReadLine() 會阻塞整個 Console Application 的執行緒,導致 Application 卡住,故只能用 ReadExists() 來做,且 ReadExists() 需要自行判斷資料的 "\r\n" 換行字元,避免資料長短不一。
Example:
Imports System.IO.Ports Imports System.Net Imports System.Text Module Module1 Sub Main() Console.WriteLine("Serial Port List: ") GetSerialPortNames() Console.WriteLine("Enter the port you want to listen (Ex: COM3):") Try Dim portname As String = Console.ReadLine() Console.WriteLine("Start listen to " + portname) ReceiveSerialData(portname) Catch ex As Exception Console.WriteLine(ex.Message) Console.WriteLine("Please Run Program Again.....") End Try Console.WriteLine("Press any key to exit program.") Console.ReadLine() End Sub Sub GetSerialPortNames() ' Show all available COM ports. For Each sp As String In My.Computer.Ports.SerialPortNames Console.WriteLine(sp.ToString()) Next End Sub Function ReceiveSerialData(portname As String) ' Receive strings from a serial port. Dim returnStr As String = "" Dim DataReceived As String Dim comport As IO.Ports.SerialPort = Nothing comport = New SerialPort() comport.PortName = portname comport.BaudRate = 9600 comport.Parity = Parity.None comport.DataBits = 8 comport.StopBits = StopBits.One comport.Handshake = Handshake.RequestToSend comport.DtrEnable = True comport.ReadTimeout = 500 comport.Open() Console.WriteLine("Waiting for Data to be Received...") Try Do DataReceived = comport.ReadExisting() If DataReceived Is "" Then Continue Do End If Console.WriteLine("Data received -> {0}", DataReceived) Loop Catch Finally comport.Close() ' Close port End Try End Function End Module
範例中沒有自動判斷 ReadExists() 是否遇到 \r\n 去換行。
GUI 版本
GUI 版本中,可以使用事件系統來傳遞值,因此有一個方法叫做 port_DataReceived,用來接收非同步情形下傳入的值事件,接著要在此控制 GUI 的主執行緒,一定要透過 Delegate 的委派機制去呼叫控制,所以使用 updateCharts() 方法來控制 GUI 的主執行緒 XY 圖之值。
XY 線圖是透過 ToolBox 中的 Charts 去新增,然後在屬性控制中,有一個 Series 欄位(型別為一集合類) 是透過 Series 的類別去控制它的線圖,可以從程式控制 (於範例中 34 行) 看到這樣的方式,或是直接在屬性欄位做修正也可以。
Example:
Imports System.IO.Ports Imports System.Text Imports System.Windows.Forms.DataVisualization.Charting Public Class Form1 Private port As New SerialPort("COM4", 9600, Parity.None, 8, StopBits.One) Private ReadOnly _startOfText As String = System.Text.Encoding.ASCII.GetChars(New Byte() {2}) Private ReadOnly _endOfText As String = System.Text.Encoding.ASCII.GetChars(New Byte() {4}) Public Event MessageReceived(ByVal message As String) Public Event DataIgnored(ByVal text As String) Private message As String Private s As New Series Private _buffer As StringBuilder = New StringBuilder Private logFile As String Dim count As Int32 = 1 Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load s.ChartType = SeriesChartType.Line s.Name = "Series Line 1" Chart1.Series.Add(s) End Sub Private Sub Button3_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button3.Click My.Computer.FileSystem.WriteAllText("C:\logFile.csv", logFile, False) MessageBox.Show("Saved") End Sub Private Sub updateText() ListBox1.Items.Add(Me.message) ListBox1.TopIndex = ListBox1.Items.Count - 1 'add in xy Try s.Points.AddXY(count, Int32.Parse(Me.message)) count += 1 logFile += "," + Me.message Catch End Try End Sub Private Sub port_DataReceived(ByVal sender As Object, ByVal e As SerialDataReceivedEventArgs) Try Dim msg As String = port.ReadExisting() message = msg Me.Invoke(New EventHandler(AddressOf updateText)) Catch ex As Exception MessageBox.Show(ex.Message()) End Try End Sub Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click AddHandler port.DataReceived, New SerialDataReceivedEventHandler(AddressOf port_DataReceived) Try port.Open() Catch ex As Exception MessageBox.Show(ex.Message()) End Try End Sub 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 Private Sub Button4_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button4.Click port.Close() End Sub End Class
Reference:
1. https://stackoverflow.com/questions/26689762/how-to-use-invoke-required-and-invoke-in-console-application
https://stackoverflow.com/questions/11458850/how-to-read-serial-port-communication-into-buffer-and-parse-out-complete-message
https://dotblogs.com.tw/billchung/2012/01/20/66860
https://dotblogs.com.tw/chou/2009/03/20/7614
https://social.msdn.microsoft.com/Forums/vstudio/en-US/6e5ee56f-a201-4a27-91f9-1bfbf7ab1f89/problem-in-receiving-data-through-serial-port-when-using-readline?forum=csharpgeneral
沒有留言:
張貼留言