0

シリアル ポートから 12 バイトの 16 進文字列を読み取ってテキスト ボックスに表示できるアプリケーションを作成しようとしています。ソースは、RS232-USB コンバーターを使用して USB ポートに接続されている RFID モジュールです。

私の最初の問題は、無効なクロススレッド操作が原因で datarecieved イベントのテキスト ボックスにアクセスすることでした。それをグーグルで検索すると、それを機能させるために必要なコードをコピー/貼り付けました。これらはすべてクロススレッド関数です。

OpenPort 関数と Datarecieved イベントは私自身のコードで、残りは Google から取得しました。

その後、アプリケーションは機能しましたが、不完全なコードを取得し、最初のスワイプで 12 桁すべてを取得し、2 回目のスワイプごとに最初のバイトが消えてしまいました。さらに読んで、受信データのサイズ (以下のコードを参照) の配列を作成することにしましたが、クロススレッド エラーが再び発生します。

配列の使用方法に問題があることはわかっています。これは私のVBの知識をはるかに超えており、私はここで頭を悩ませています。専門家の助けを借りることができます。私は電子技術者で、自分のハードウェア用のアプリを作成するためのプログラミングを独学で学びました。

ありがとう。

更新されたコード:

Public Class Form3
    Dim msg As String

    Private Sub Form3_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        For Each s In System.IO.Ports.SerialPort.GetPortNames()
            lstPorts.Items.Add(s)
        Next s

    End Sub

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        If lstPorts.SelectedIndex = -1 Then
            MessageBox.Show("Please select a port")
            Exit Sub
        Else
            SerialPort1.BaudRate = 9600
            SerialPort1.DataBits = 8
            SerialPort1.Parity = IO.Ports.Parity.None
            SerialPort1.StopBits = IO.Ports.StopBits.One
            SerialPort1.PortName = lstPorts.SelectedItem.ToString
            SerialPort1.Open()
        End If
    End Sub


    Private Sub SerialPort1_DataReceived(ByVal sender As System.Object, ByVal e As System.IO.Ports.SerialDataReceivedEventArgs) Handles SerialPort1.DataReceived

        msg = SerialPort1.ReadExisting


        Me.Invoke(New Action(
        Sub()
            txtReceived.Clear()
            txtReceived.Text += msg
        End Sub))

    End Sub



    Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
        SerialPort1.Close()
        txtReceived.Text = ""
    End Sub
End Class
4

1 に答える 1

0

ここで読んだもの:

If SerialPort1.IsOpen Then
   msg = SerialPort1.ReadExisting()         
End If

ここで上書きすると失われます:

msg = enc.GetString(bytesarray) 

通常、シリアルポート(またはTCP / IP接続)から直接文字列を読み取ろうとすることはお勧めしません。配列アプローチのみに固執し、ReadExistingを取り除きます。「メッセージ」全体があることがわかっている場合にのみ、バイトを文字列に変換します。

いくつかの一般的なヒント(これはプロトコルによって異なりますが):

  1. メッセージを読み込むためのバッファーを定義します。このバッファーは、メッセージ全体を保持するのに十分な大きさであり、ローカル変数にしないでください。
  2. そのバッファへのオフセットを追跡します。
  3. 最初に読み取るとき、オフセットは0になり、2バイトを読み取るとしましょう。オフセットは、値1に対して+2になります。
  4. 2回目(および3回目など)の読み取り時に、そのオフセットから挿入して、新しいバイトを効果的に追加します。
  5. 読み取るたびに、バッファを調べて、メッセージ全体を受信したかどうかを確認する必要があります。これは、読み取られた合計量(固定長のメッセージがある場合)、またはバッファー内の情報(長さプレフィックスなど)によって行うことができます。メッセージを読んだら、バッファから削除する必要があります。
  6. 簡単なアプローチから始めて、メッセージを削除した後、オフセットをゼロにリセットしてプロセスを続行することをお勧めします。受信したデータが削除したメッセージよりも長い場合は、それらの余分なバイトを「左」にシフトして、位置0から開始する必要があります(より効率的なアプローチは、循環バッファーを使用することですが、KISSは今)。

DataRecvイベントでは、UIを更新するために次のようなことを行う必要があります。

   Me.Invoke(New Action(
      Sub()
         'this code runs on the GUI thread, update textboxes!
         Me.Text += msg
      End Sub))

私はまだReadExistingのファンではありませんが、デバイスから常にprinatble文字を取得していることが確実な場合は、次のようにしてみてください。

Public Class Form3 

    Private pendingMsg As New StringBuffer()

    Private Sub Form3_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load 
        For Each s In System.IO.Ports.SerialPort.GetPortNames() 
            lstPorts.Items.Add(s) 
        Next s 

    End Sub 

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click 
        If lstPorts.SelectedIndex = -1 Then 
            MessageBox.Show("Please select a port") 
            Exit Sub 
        Else 
            SerialPort1.BaudRate = 9600 
            SerialPort1.DataBits = 8 
            SerialPort1.Parity = IO.Ports.Parity.None 
            SerialPort1.StopBits = IO.Ports.StopBits.One 
            SerialPort1.PortName = lstPorts.SelectedItem.ToString 
            SerialPort1.Open() 
        End If 
    End Sub 


    Private Sub SerialPort1_DataReceived(ByVal sender As System.Object, ByVal e As System.IO.Ports.SerialDataReceivedEventArgs) Handles SerialPort1.DataReceived 

        Dim completedMsg As String
        completedMsg = String.Empty

        pendingMsg.Append(SerialPort1.ReadExisting())

        If pendingMsg.Length >= 24 Then '12 bytes in hex is 24 chars

           completedMsg = pendingMsg.ToString(0, 24) '1st 24 bytes are a msg

           pendingMsg.Remove(0, 24) 'pop off the msg from our buffer

           Me.Invoke(New Action( 
           Sub()                
               txtReceived.Text = completedMsg                               
           End Sub)) 

        End If

    End Sub 


    Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click 
        SerialPort1.Close() 
        txtReceived.Text = "" 
    End Sub 
End Class
于 2012-04-23T12:48:05.720 に答える