2

問題

Windowsで仮想シリアルポートを作成するUSB​​デバイスがあります。ポートからの書き込みと読み取りにVB.Netを使用しています。私のデバイスは特定のサイズのバイトセットで応答しますが、SerialPort.Read(byte-array、offset、number-bytes)は完全なnumber-bytesを返さないが、タイムアウトしたり例外を生成したりしないことがわかりました。繰り返される呼び出しは、追加のフラグメントを返します(最大3つの呼び出しが必要です)。この読み取りメソッドがなぜそのように動作するのかわかりませんか?要求されたバイト数は単なる提案だと思いますか?:-)。最初にタイムアウトしない限り、リクエスト全体が実行されるのを待つと思います。

pySerialを使用するPythonコードには、同じ問題はありません。

だから、私はここで何が間違っているのですか?期待しすぎですか?

いくつかのシナリオは次のとおりです。

  1. ポートにコマンドを記述し、応答で4バイトを取得することを期待しています。最初に1バイトを取得し、次の呼び出しで3バイトを取得します。
  2. 私はコマンドを書き、それに応じて21120バイトを期待しています。ポートから読み取るための3回の呼び出しで1、12671、次に8448バイトを取得します。

これが私のコードからの抜粋です:

Private Sub SetupVirtualSerialPort()
  Dim portName As String = "COM" + (m_DeviceContext * -1).ToString
  Const baud As Int32 = 9600    '7680000
  Const parity As Parity = parity.None
  Const databits As Int32 = 8
  Const stopbits As StopBits = stopbits.One
  m_SerialPort = New SerialPort(portName, baud, parity, databits, stopbits)
  m_SerialPort.WriteTimeout = VSPtimeout
  m_SerialPort.ReadTimeout = VSPtimeout
  m_SerialPort.ReadBufferSize = 2 * RETURN_BUFFER_SIZE
  m_SerialPort.WriteBufferSize = 2 * COMMAND_BUFFER_SIZE
  m_SerialPort.Open()

  ' Register event handlers
  AddHandler m_SerialPort.ErrorReceived, AddressOf m_DriverInterface.Handle_VSP_Error
End Sub


Public Function WriteReadVSPort(ByVal commandLength As Int32, ByVal returnLength As Int32)             As Int32

  Const RetryLimit As Int32 = 5
  Dim NumberRetries As Int32 = 0
  Dim Offset As Int32 = 0
  Dim ExceptionOccurred As Boolean = False
  Dim NumberBytes As Int32 = 0

  Try '  Writing
    m_SerialPort.Write(m_CommandBuffer, 0, commandLength)
  Catch exc As InvalidOperationException
    MessageBox.Show("InvalidOperationException", Application.ProductName)
    ExceptionOccurred = True
  Catch exc As TimeoutException
    MessageBox.Show("TimeoutException", Application.ProductName)
    ExceptionOccurred = True
  End Try

  If Not ExceptionOccurred Then

    Try ' Reading

      ' Working around a problem here: reads are returning fewer 
      ' bytes than requested, though not signalling a timeout exception.
      ' Therefore, we retry if we got fewer bytes than expected, up to five times.
      While NumberRetries < RetryLimit And returnLength > Offset

        NumberBytes = m_SerialPort.Read(m_ReturnBytes, Offset, returnLength - Offset)
        Offset += NumberBytes
        NumberRetries += 1
        If returnLength <> NumberBytes Then
          System.Diagnostics.Debug.Print("Number of bytes read (" & NumberBytes &
            ") not what was requested (" & returnLength & "). Accumulated " & Offset)
        End If

      End While

    Catch exc As InvalidOperationException
      MessageBox.Show("InvalidOperationException", Application.ProductName)
      ExceptionOccurred = True
    Catch exc As TimeoutException
      MessageBox.Show("TimeoutException", Application.ProductName)
      ExceptionOccurred = True
    End Try

  If ExceptionOccurred Then
    Return 1
  Else
    Return 0
  End If

End Function

ありがとうございました。

4

2 に答える 2

1

DataReceivedイベントを使用し、コードイベントをループではなく駆動型にするという提案を取り入れました。1回の読み取り操作で21120バイトのメッセージに対して仮想シリアルポートがまだ機能しないことがわかりました。短いメッセージ長は適切に行われます。ただし、シリアルポートデータ受信しきい値を21119バイトに設定し、シリアルポート読み取りバッファーをメッセージサイズの10倍に設定すると、1であることがわかりました。DataReceivedイベントは、使用可能な12672バイトのみでトリガーされます( 21119)と、フルサイズでRead()を実行したときに返される同じ番号。2.バイト数がしきい値と等しくない場合、その時点で読み取りを行わないと、それ以上のDataReceivedイベントはトリガーされません。3。ただし、12672バイトを読み取った場合は、別のDataReceivedイベントが発生します。残りの8448バイト。

なぜこれがそのように振る舞うのか私にはわかりません。さらなるコメントを歓迎します。

ただし、他の人の利益のために現在のコードを共有すると思いました。

いくつかのクラス変数は次のとおりです。

Private m_SerialPort As SerialPort = Nothing
Private Debug As Int16
Private m_CommandBuffer(COMMAND_BUFFER_SIZE) As Byte
Private m_ReturnBytes(RETURN_BUFFER_SIZE) As Byte
Private m_WaitingOnBytes As Int32
Private m_VSP_Offset As Int32 = 0
Private m_waitHandle As New System.Threading.ManualResetEvent(True) ' Initialize to signaled
Private m_waitHandle2 As New System.Threading.ManualResetEvent(False) ' Initialize to UN-signaled

イベントハンドラサブルーチン

Public Sub Handle_VSP_DataReceived_Dev(ByVal sender As System.Object, ByVal e As System.EventArgs)
  Dim NumberBytes As Int32

  If m_SerialPort.BytesToRead > 0 And m_SerialPort.BytesToRead >= m_WaitingOnBytes Then
    ' This handles the case where the event was triggered, there was data and its length matched
    ' or exceeded the requested amount.
    System.Diagnostics.Debug.Print("DR-Dev: Bytes to read: " & m_SerialPort.BytesToRead & ", waiting for: " & m_WaitingOnBytes)
    NumberBytes = m_SerialPort.Read(m_ReturnBytes, m_VSP_Offset, m_WaitingOnBytes)
    System.Diagnostics.Debug.Print("DR-Dev: got " & NumberBytes & " bytes, released wait handle")
    m_WaitingOnBytes = 0
    m_waitHandle.Set() ' Release the wait handle so the thread running WriteReadVSPort can proceed
  ElseIf m_SerialPort.BytesToRead > 0 And m_WaitingOnBytes > 0 Then
    ' Handle the case where the full request is not delivered. Note: 
    ' This should not need to be done, but it seems that the 
    ' Serial Port is sending the event before all the data is 
    ' received and the threshold is crossed and then not 
    ' sending another event until the buffer is read.
    ' So, here we do a partial read, if we are waiting on a
    ' read operation and adjust the offset and remaining bytes
    ' we are waiting for.
    System.Diagnostics.Debug.Print("DR-Dev: Bytes to read: " & m_SerialPort.BytesToRead & ", waiting for: " & m_WaitingOnBytes)
    NumberBytes = m_SerialPort.Read(m_ReturnBytes, m_VSP_Offset, m_WaitingOnBytes)
    If NumberBytes = m_WaitingOnBytes Then
      ' We actually got all the data, though the serial port did not report it had it ready. Fine, 
      ' proceed as above
      System.Diagnostics.Debug.Print("DR-Dev: got " & m_WaitingOnBytes & " bytes, released wait handle")
      m_WaitingOnBytes = 0
      m_waitHandle.Set() ' Release the wait handle so the thread running WriteReadVSPort can proceed
    Else ' Mark this as a partial read
      System.Diagnostics.Debug.Print("DR-Dev: got partial " & NumberBytes & " while waiting for: " &
        m_WaitingOnBytes & " bytes, continue to hold WriteReadVSPort")
      m_WaitingOnBytes -= NumberBytes
      m_VSP_Offset += NumberBytes
    End If
  End If

End Sub

書き込みコマンド/読み取り応答アクティビティを実行する機能

Public Function WriteReadVSPort(ByVal commandLength As Int32, ByVal returnLength As Int32) As Int32

  Dim ExceptionOccurred As Boolean = False
  Dim NumberBytes As Int32 = 0
  Dim RetriesRemaining As Int32 = 4
  Dim Finished As Boolean = False

  ' Important to set up for reading response before the command is written
  ' because another thread will handle the DataReceived event and process
  ' the received data without intervention from the thread executing
  ' this(subroutine.
  m_VSP_Offset = 0
  m_WaitingOnBytes = returnLength
  ' Set the DataReceived event threshold
  m_SerialPort.ReceivedBytesThreshold = m_WaitingOnBytes - 1
  ' Set waitHandle so it will block the thread executing this routine until the data is received
  m_waitHandle.Reset()

  Try '  Writing
    m_SerialPort.Write(m_CommandBuffer, 0, commandLength)
  Catch exc As InvalidOperationException
    MessageBox.Show("InvalidOperationException when writing to Serial Port COM" & -1 * DeviceContext, Application.ProductName)
    ExceptionOccurred = True
  Catch exc As TimeoutException
    MessageBox.Show("TimeoutException when writing to Serial Port COM" & -1 * DeviceContext, Application.ProductName)
    ExceptionOccurred = True
  End Try

  If Not ExceptionOccurred Then

    Try ' Reading all done by Event Handler

      ' wait for event handler to complete its job, running in another thread
      System.Diagnostics.Debug.Print("WR_VSP: waiting on waitHandle, bytes avail: " &
              m_SerialPort.BytesToRead & ", want bytes: " & m_WaitingOnBytes)

      If m_waitHandle.WaitOne(VSPtimeout) Then
        ' The WaitOne call returned True, meaning that Handle_VSP_DataReceived_Dev was able to receive all the requested data
        System.Diagnostics.Debug.Print("WR_VSP: proceeding")
      Else
        ' The WaitOne call timed out. Give it some retries before throwing an exception
        While Not Finished And RetriesRemaining > 0
          System.Threading.Thread.Sleep(VSPtimeout)
          If m_SerialPort.BytesToRead > 0 And m_SerialPort.BytesToRead >= m_WaitingOnBytes Then
            NumberBytes = m_SerialPort.Read(m_ReturnBytes, m_VSP_Offset, m_WaitingOnBytes)
            System.Diagnostics.Debug.Print("WR_VSP: timeout mode, got " & m_WaitingOnBytes & " bytes")
            Finished = True
          Else
            RetriesRemaining -= 1
          End If
        End While
        If Not Finished Then
          Throw New TimeoutException("Device failed to send the requested number of bytes.")
        End If
      End If

    Catch exc As InvalidOperationException
      MessageBox.Show("InvalidOperationException when reading from Serial Port COM" & -1 * DeviceContext, Application.ProductName)
      ExceptionOccurred = True
    Catch exc As TimeoutException
      MessageBox.Show("TimeoutException when reading from Serial Port COM" & -1 * DeviceContext, Application.ProductName)
      ExceptionOccurred = True
    End Try

  End If

  If ExceptionOccurred Then
    Return 1
  Else
    Return 0
  End If

End Function
于 2011-10-19T16:44:14.103 に答える