none
有關Serial Port的多緒應用 RRS feed

  • 問題


  • 各位先進 :

    有關Serial Port的應用請教各位:

    開發環境: OS WIndows7 , VB.Net 2008

    我實做了一個類別處理Serial Port程式如下 :

    Option Explicit On Option Strict On Imports System.IO.Ports Imports System.Text Public Class RSserialPort Private RS_SerialPort As SerialPort Private RS_COM As String Private RS_BaudRate As Integer Private RS_Parity As Parity Private RS_DataBits As Integer Private RS_StopBits As StopBits Private RS_Flow As Handshake Public Sub New(ByVal _com As String, ByVal Rate As Integer, ByVal pari As String, ByVal databit As Integer, ByVal sbit As String, ByVal flw As Integer) Try RS_COM = _com RS_BaudRate = Rate RS_Parity = CType([Enum].Parse(GetType(Parity), pari), Parity) RS_DataBits = databit RS_StopBits = CType([Enum].Parse(GetType(StopBits), sbit), StopBits) RS_Flow = CType(flw, Handshake) Catch ex As Exception End Try End Sub Public Function RSconnect() As Boolean Dim bol As Boolean = False Try Me.RS_SerialPort = New SerialPort(RS_COM, RS_BaudRate, RS_Parity, RS_DataBits, RS_StopBits) Me.RS_SerialPort.Handshake = RS_Flow Me.RS_SerialPort.Open() Me.RS_SerialPort.NewLine = vbCr RS_SerialPort.ReadTimeout = 500 If Me.RS_SerialPort.IsOpen = True Then AddHandler Me.RS_SerialPort.DataReceived, AddressOf RsRecevie OnConnected(New EventArgs) bol = True End If Catch ex As Exception End Try Return bol End Function Private Sub RsRecevie(ByVal sender As Object, ByVal e As SerialDataReceivedEventArgs) Dim str As String = "" Try Dim buffChar As Integer = Nothing If RS_SerialPort IsNot Nothing And RS_SerialPort.IsOpen = True Then Threading.Thread.Sleep(200) Application.DoEvents() str = RS_SerialPort.ReadExisting OnReceivedData(str) End If Catch tmout As TimeoutException Exit Try Catch ex As Exception End Try End Sub Public Function Close() As Boolean Dim bol As Boolean = False Try If (RS_SerialPort IsNot Nothing) Then If RS_SerialPort.IsOpen = True Then RS_SerialPort.Close() End If RS_SerialPort = Nothing OnDisconnected(New EventArgs) bol = True End If Catch ex As Exception End Try Return bol End Function Public Event Connected As EventHandler Protected Overridable Sub OnConnected(ByVal e As EventArgs) RaiseEvent Connected(Me, e) End Sub Public Event Disconnected As EventHandler Protected Overridable Sub OnDisconnected(ByVal e As EventArgs) RaiseEvent Disconnected(Me, e) End Sub Public Sub Send(ByVal cmd As String) Try Dim byteData As Byte() = Encoding.ASCII.GetBytes(cmd & vbCrLf) If RS_SerialPort IsNot Nothing And RS_SerialPort.IsOpen = True Then RS_SerialPort.Write(byteData, 0, byteData.Length) OnSendData(cmd) End If Catch ex As Exception End Try End Sub 'Send

    Public Delegate Sub ReceivedDataEventHandler(ByVal sender As Object, ByVal e As String) Public Event ReceivedData As ReceivedDataEventHandler Protected Overridable Sub OnReceivedData(ByVal e As String) RaiseEvent ReceivedData(Me, e) End Sub Public Delegate Sub SendDataEventHandler(ByVal sender As Object, ByVal e As String) Public Event SendData As SendDataEventHandler Protected Overridable Sub OnSendData(ByVal e As String) RaiseEvent SendData(Me, e) End Sub End Class

    在視窗程式中,以   _serial = New RSserialPort(_COM, _BaudRate, _Parity, _DataBits, _StopBits, _flow)

    完成連線動作, 當連線成功時會建立一個背景執行緒來進行無窮回圈的狀態查詢

             _onLine_Status = New System.ComponentModel.BackgroundWorker
                _onLine_Status.WorkerSupportsCancellation = True
                AddHandler _onLine_Status.DoWork, AddressOf Status_Task
                _onLine_Status.RunWorkerAsync()

    以下是部分的程式段

    Private Sub Status_Task(ByVal sender As Object, ByVal e As System.ComponentModel.DoWorkEventArgs)
            Try
                While (cnt_check)
                        Dim tim As Integer = -1
                        AddHandler _serial.ReceivedData, AddressOf sts_ReceivedData
                        Me._serial.Send(sts_cmd)
                        While (True)
                            'Exit While
                            tim += 1
                            If (res_STS <> "") Then
                                If res_STS = "ERR" Then
                                    res_STS = ""
                                    Exit While
                                End If
                                Me.Invoke(New MethodInvoker(AddressOf Me.OnLine_checkin_Status))
                                Application.DoEvents()
                                Exit While
                            End If
                            If tim > 1000 Then
                                res_STS = ""
                                If InvokeRequired Then
                                    Me.Invoke(New MethodInvoker(AddressOf Me.Sts_Initail))
                                Else
                                    Me.Sts_Initail()
                                End If
                                Exit While
                            End If
                            Threading.Thread.Sleep(1)
                        End While
                    Threading.Thread.Sleep(200)
                End While
            Catch ex As Exception
            Finally
                Me.end_get_sts()
                Me.Sts_Initail()
            End Try
        End Sub
        Private res_STS As String
        Private Sub sts_ReceivedData(ByVal sender As Object, ByVal e As String)
            Try
                        Dim sts_str() As String = Split(e, " ")
                        If sts_str(0).Length = 8 Then
                            res_STS = sts_str(0)
                        Else
                            res_STS = "ERR"
                        End If
                    End If
            Catch ex As Exception
            End Try
        End Sub
       Private Sub btn_sendtext_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btn_sendtext.Click
            Try
                res_Cmd_ACK = ""
                    AddHandler _serial.ReceivedData, AddressOf Cmd_receive
                    _serial.Send(Msg)
                    Threading.Thread.Sleep(200)
                    Dim tm As Integer = -1
                    While (True)
                        tm += 1
                        If tm > 30000 Then
                            MessageBox.Show(Me, "  Controller回應逾時(超過250秒)... " & vbCrLf & "  Move Delay " , "TimeOut", MessageBoxButtons.OK, MessageBoxIcon.Exclamation)
                            Exit While
                        End If
                        If (res_Cmd_ACK <> "") Then
    	      ...................................
                            Exit While
                        End If
                        Application.DoEvents()
                        Threading.Thread.Sleep(1)
                    End While
            Catch ex As Exception
            Finally
                If _serial IsNot Nothing Then
                    RemoveHandler _serial.ReceivedData, AddressOf Cmd_receive
                End If
            End Try
        End Sub
        Private res_Cmd_ACK As String
        Private Sub Cmd_receive(ByVal sender As Object, ByVal e As String)
            Try
                    If e.Contains("NAK") Then
                       res_Cmd_ACK = "Error"
                    Else
                       res_Cmd_ACK = e
                    End If
                    
                    If _serial IsNot Nothing Then
                        RemoveHandler _serial.ReceivedData, AddressOf Cmd_receive
                    End If
                    
            Catch ex As Exception
            Finally
            End Try
        End Sub

    想請教各位的是 , 接收的事件是分別委派的 , 但兩個接收事件偶有接到另一個事件的內容 , 請問有沒有什麼好的建議來處理呢?

    程式中是不是有不正確之處呢 ? 請指教 .謝謝

    (分別傳送的指令,由分別的事件接收......)

    2013年3月11日 上午 09:50

解答



  • 我看到你用 多緒當標題, 以為你的程式都是跑多緒.

    整個 serialport 的概念, 一時三刻不易講清楚.

    基本上就是, 如果你是 發送回應模型, 最好不要用 datareceived event

    整個概念, 詳見 serial port 系列文

    從一篇一路讀完, 會有比較完整的觀念.


    在現實生活中,你和誰在一起的確很重要,甚至能改變你的成長軌跡,決定你的人生成敗。 和什麼樣的人在一起,就會有什麼樣的人生。 和勤奮的人在一起,你不會懶惰; 和積極的人在一起,你不會消沈; 與智者同行,你會不同凡響; 與高人為伍,你能登上巔峰。


    2013年3月12日 上午 03:51
    版主

所有回覆

  • (1) "接收的事件是分別委派的 , 但兩個接收事件偶有接到另一個事件的內容" <--- 其實我不太懂這話的意思, 你同時有兩個 serialport  嗎 ?

    (2)  不要用 Application.DoEvents () , 這玩意會改變事件迴圈本來應該執行的委派順序.


    在現實生活中,你和誰在一起的確很重要,甚至能改變你的成長軌跡,決定你的人生成敗。 和什麼樣的人在一起,就會有什麼樣的人生。 和勤奮的人在一起,你不會懶惰; 和積極的人在一起,你不會消沈; 與智者同行,你會不同凡響; 與高人為伍,你能登上巔峰。

    • 已標示為解答 Angusruby 2013年3月13日 上午 01:26
    • 已取消標示為解答 Angusruby 2013年3月13日 上午 06:07
    2013年3月11日 上午 11:29
    版主
  • Bill 版大 : 謝謝回應

    1.

    serialport只有一個 ,連線時只實作一個_serial As RSserialPort ,

    因為背景需要不斷的詢問HOST的狀態 , 所以一但連線後會建立一個背景程式進行無窮回圈來發送狀態查詢指令

     _onLine_Status = New System.ComponentModel.BackgroundWorker
    _onLine_Status.WorkerSupportsCancellation = True
    AddHandler _onLine_Status.DoWork, AddressOf Status_Task
    _onLine_Status.RunWorkerAsync()

    在實際視窗操作時則是使用按鈕事件進行一般指令發送 ,

    按鈕事件的_serial.send, 委派Cmd_receive來接收 , 狀態詢息則委派sts_ReceivedData來接收

    '==============以下為操作的概要描述

    a. 連線後委派接收只有sts_ReceivedData有收到 -- > 沒有問題

    b . 當按鈕事件開時進行一般指令發送時 , 兩個委派接收事件就會有互相接收到信息的狀況

        會造成程式無窮回圈內的Timeout .

    '============

    2 . 不要用 Application.DoEvents () .

         這一點我會試著不用看看 , 其不同之處在哪兒 ==> 若使用Me.update() 呢 , 是否一樣會造成回圈本來應該執行的委派順序改變呢?

        會使用 Applicayion.DoEvents() ,是因為視窗上有文字的顯示 , 會因接收的訊息來改變 , 不使用,則文字不會即時的改變.

    謝謝回應


    • 已編輯 Angusruby 2013年3月12日 上午 01:03
    2013年3月12日 上午 01:01
  • 試了Bill版大的建議 , 不使用 Applciation.DoEvents() , 接收的問題並沒有改善

    反而會使程式因While Loop 而'累格' 

    或許要試一下全部指令都使用背景去 發及收

    各位有沒有其它的建議呢

    2013年3月12日 上午 02:13


  • 我看到你用 多緒當標題, 以為你的程式都是跑多緒.

    整個 serialport 的概念, 一時三刻不易講清楚.

    基本上就是, 如果你是 發送回應模型, 最好不要用 datareceived event

    整個概念, 詳見 serial port 系列文

    從一篇一路讀完, 會有比較完整的觀念.


    在現實生活中,你和誰在一起的確很重要,甚至能改變你的成長軌跡,決定你的人生成敗。 和什麼樣的人在一起,就會有什麼樣的人生。 和勤奮的人在一起,你不會懶惰; 和積極的人在一起,你不會消沈; 與智者同行,你會不同凡響; 與高人為伍,你能登上巔峰。


    2013年3月12日 上午 03:51
    版主
  • Bill版大 :

    謝謝哦 , 之前的基本篇我有讀完 , 受益良多 ..

    我剛才花了一些時間也看了發送回應模型篇 , 內容簡潔明瞭是讀者的福音

    我再重新架構SerialPort類別來試一下

    2013年3月12日 上午 06:27
  • Bill版大 :

    使用發送回應模型,已可解決問題 , 受益良多 , 謝謝你

    另外外我要標示你為解答 ,但標示不了 , 所以我標示了更上一篇作為解答

    2013年3月13日 上午 01:29