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.
Was genau ist nun die Frage?
- performance is a feature -
Microsoft MVP - @Website - @AzureStuttgart - github.com/BenjaminAbt - Sustainable Code
Ich möchte verzögerungsfrei feststellen, ob Daten da sind. Wie mache ich das?
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 😉
- performance is a feature -
Microsoft MVP - @Website - @AzureStuttgart - github.com/BenjaminAbt - Sustainable Code
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?
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
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?).
- performance is a feature -
Microsoft MVP - @Website - @AzureStuttgart - github.com/BenjaminAbt - Sustainable Code
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.
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
Statt alle 100ms zu Pollen könntest Du auch einen Procuder-Consumer-Pattern via BlockingCollection realisieren.
Würde Dein Gesamtsystem entlasten.
- performance is a feature -
Microsoft MVP - @Website - @AzureStuttgart - github.com/BenjaminAbt - Sustainable Code