Laden...

WinUsb read data available Funktion

Erstellt von CoLo vor 8 Jahren Letzter Beitrag vor 8 Jahren 2.478 Views
C
CoLo Themenstarter:in
224 Beiträge seit 2009
vor 8 Jahren
WinUsb read data available Funktion

Ich möchte gerne über eine Art ReadDataAvailable Funktion feststellen, ob Daten vorliegen.
Wenn keine vorliegen, soll unverzüglich quasi ohne Delay die Funktion zurückkehren.

Um über USB-Kabel Daten zu transferieren verwende ich folgende Funktion:


            [DllImport("winusb.dll")]
            public static extern bool WinUsb_ReadPipe(
                IntPtr InterfaceHandle, //IN WINUSB_INTERFACE_HANDLE
                byte PipeID,//IN UCHAR  PipeID,
                byte[] Buffer,//IN PUCHAR  Buffer,
                UInt32 BufferLength,//IN ULONG  BufferLength,
                ref UInt32 LengthTransferred,//OUT PULONG  LengthTransferred
                IntPtr overlapped//IN LPOVERLAPPED  Overlapped
            );

Wenn aber keine Daten vorliegen, habe ich ein Delay von gefühlt 100 Millisekunden bis die Funktion zurückkehrt. Dummerweise habe ich in der MSDN-Doku für winusb nichts dergleichen gefunden.

Der Datentransfer funktioniert ansonsten.

16.841 Beiträge seit 2008
vor 8 Jahren

Was genau ist nun die Frage?

C
CoLo Themenstarter:in
224 Beiträge seit 2009
vor 8 Jahren

Ich möchte verzögerungsfrei feststellen, ob Daten da sind. Wie mache ich das?

16.841 Beiträge seit 2008
vor 8 Jahren

Bist Du Dir sicher, dass das 100ms Delay nicht in Wirklichkeit eher 20ms + Messfehler sind und es daher vom Tickcount kommt?
Und "Verzögerungsfrei" bei USB (meist aufgrund der Controller) sind irgendwie kontrahierend 😉

C
CoLo Themenstarter:in
224 Beiträge seit 2009
vor 8 Jahren

            System.Diagnostics.Stopwatch stopwatch = new System.Diagnostics.Stopwatch();
            stopwatch.Start();

            UInt32 bufLen = 0;
            bool fResult = NativeMethods.WinUsb_ReadPipe
            (
                winUsb.context.Dev,
                winUsb.context.BulkInPipe,
                buf,
                maxBuf,
                ref bufLen,
                IntPtr.Zero //overlapped
            );

            stopwatch.Stop();
            System.Diagnostics.Debug.WriteLine(stopwatch.ElapsedMilliseconds + "|" + stopwatch.ElapsedTicks);

Wenn keine Daten da sind:100|354218
99|353121
99|350529
100|353657
99|352027
99|352493

Wenn Daten da sind:0|3080
0|3125
0|3197
0|3388
0|3139
0|3394

Ich denke, dass die 100ms kein Messfehler sind. Wenn ich es ebenfalls zu Testzwecken im GUI Thread laufen lasse, dann hängt mein GUI Thread kurz.

Du meinst es gibt aufgrund der Controller keine Möglichkeit Verzögerungsfrei festzustellen, ob erstmal Daten vorhanden sind?

5.658 Beiträge seit 2006
vor 8 Jahren

Hi CoLo,

was gibt denn die Funktion zurück, wenn (keine) Daten vorhanden sind?

In der Doku zur WinUsb_ReadPipe-Funktion steht:

When no data is available in the endpoint (pipe is empty), WinUsb_ReadPipe does not return until there is data in the pipe.

Wahrscheinlich tritt dann bei dir ein Timeout-Fehler oder etwas ähnliches auf. Das solltest du mit der GetLastError-Funktion überprüfen können.

Ich kenne mich mit dem Ansteuern von USB-Geräten nicht aus, nehme aber an, daß man das Auslesen besser im Hintergrund durchführen und die UI nur benachrichten sollte, wenn auch tatsächlich Daten gelesen wurden.

Christian

Weeks of programming can save you hours of planning

16.841 Beiträge seit 2008
vor 8 Jahren

Nein, aber ich weiß, dass USB Controller oft Eigenheiten entwickeln und man nicht nur gegen einen Controller testen sollte (wobei das in diesem Fall wahrscheinlich eher nicht der Fall ist).

Dummerweise habe ich in der MSDN-Doku für winusb nichts dergleichen gefunden.

In der Beschreibung von WinUsb_ReadPipe steht, dass das Ding blockiert, bis Daten vorhanden sind oder der Timeout erreicht wird.
Ergo sind deine 100ms wohl Dein Timeout.

Wahrscheinlich ist Dein Result sogar false (hast Das überhaupt überprüft?), sodass auch der Win32Error entsprechend gesetzt ist (hast das geprüft?).

C
CoLo Themenstarter:in
224 Beiträge seit 2009
vor 8 Jahren

Oh man. Deklarationsfehler:

[DllImport("winusb.dll"] muss
[DllImport("winusb.dll", SetLastError = true)] lauten, sonst bleibt GetLastWin32Error "0".

Ok, wenn nun keine Daten vorhanden sind:

"NativeMethods.WinUsb_ReadPipe" gibt "false" zurück.
"Marshal.GetLastWin32Error()" gibt "121" bzw. "ERROR_SEM_TIMEOUT 121 (0x79): The semaphore timeout period has expired." zurück.

C
CoLo Themenstarter:in
224 Beiträge seit 2009
vor 8 Jahren

Ich wollte mich nochmal kurz melden.
Ein verzögerungsfreies feststellen, ob Daten da sind habe ich nicht gefunden. Aber zur Zeit benötige ich es auch nicht mehr. Das Thema könnte somit theoretisch geschlossen werden, aber vielleicht findet ja noch jemand heraus wie man verzögerungsfrei feststellt, ob Daten da sind.

Die 100ms kamen daher zu Stande, da ich den Timeout-Wert bei der "WinUsb_SetPipePolicy" Funktion 100ms gesetzt hatte.

Es gibt 3 Objekte die miteinander agieren:
*Benutzeroberfläche *USB-Schnittstelle *Prozessor = Protokoll/Logik der Datenverarbeitung

Verschiedene Ansätze habe ich ausprobiert.*1 Thread für alle *3 Threads bzw. pro Objekt 1 Thread *wieder 1 Thread für alle, danach Post im Forum *1 Thread für Benutzeroberfläche und 1 Thread für Prozessor mit USB-Schnittstelle

Hier die Lösung:

Zur Zeit verwende ich 1 Thread für Benutzeroberfläche und 1 Thread für Prozessor mit USB-Schnittstelle. Der Endlosschleife des Threads des Prozessors sieht ganz grob nun wie folgt aus:*Schleifenverarbeitung stoppen? ja/nein *Erwartete Daten per USB lesen (blockiert nun bis hin zu 3 Sekunden) *Lese-Ergebnis für Benutzeroberfläche in Queue wegschreiben *Daten verarbeiten *Daten per USB schreiben *Schreib-Ergebnis für Benutzeroberfläche in Queue wegschreiben

Die Benutzeroberfläche sieht ganz grob nun wie folgt aus:*Benutzer startet Prozessor-Thread *per Timer ca. alle 100 Millisekunden die Ergebnisse aus der Queue holen und bereinigen *Benutzer stoppt Prozessor-Thread

(Effizienter wäre, wenn ich statt des Timers+Queue echte Events verwende. Aber dann muss ich mich um die Deadlock-Problematik von Events in unterschiedlichen Threads kümmern und so geht es auch.)
Danke Euch beiden. LG CoLo

16.841 Beiträge seit 2008
vor 8 Jahren

Statt alle 100ms zu Pollen könntest Du auch einen Procuder-Consumer-Pattern via BlockingCollection realisieren.
Würde Dein Gesamtsystem entlasten.