Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Win32/Win64 API (native code) (https://www.delphipraxis.net/17-win32-win64-api-native-code/)
-   -   Datenverlust bei ReadFile auf virtuellen COM-Port (https://www.delphipraxis.net/182580-datenverlust-bei-readfile-auf-virtuellen-com-port.html)

BlackGuest 3. Nov 2014 07:01

Datenverlust bei ReadFile auf virtuellen COM-Port
 
Folgende Konfiguration:

Ich habe ein Gerät, das über USB an den PC angebunden wird. Das Gerät arbeitet als CDC-Device und die Einbindung erfolgt als virtueller serieller Port.
Die Codeschnipsel verwende ich schon seit Jahren mit "richtigen" COM-Ports, ohne bisherige Probleme.

Hier der Code:
Delphi-Quellcode:
// initialisiert die serielle Schnittstelle COM[PCCOMM]
// Rückgabe = 0, wenn OK sonst <0

function TAPMLU.PortInit:Integer;
   var
    cid      : THandle;
    m2        : THandle;
    port : PChar;
    merker1   : longbool;
    comst    : tdcb;
    CommTimeouts: TCommTimeouts;

   begin
    cid:=Comm; {letzte genutzte Schnittstelle}
    if PC_Comm < 10 then
     begin
      port:=pChar('com'+IntToStr(PC_COMM));
      end
    else
     begin
      port:=pChar('\\.\com'+IntToStr(PC_COMM));
     end;

    if cid<>0 then closeHandle(cid);   // letzte genutzte Schnittstelle schließen, falls geöffnet
    comst.flags:=0;
    comst.xonlim:=0;
    comst.xofflim:=0;
    comst.Baudrate:=CBR_256000;
    comst.parity:=NoPARITY;
    comst.Bytesize:=8;
    comst.stopbits:=1;
    comst.DCBlength:=SizeOf(comst);
    m2:=CreateFile(port ,GENERIC_READ or GENERIC_WRITE , 0, nil, OPEN_EXISTING, 0, 0);

   if (m2>0)and(SetupComm(m2,200,200)) then
    begin
      comm:=m2;
      CommTimeouts.ReadIntervalTimeout:=0;
      CommTimeouts.ReadTotalTimeoutMultiplier:=0;
      CommTimeouts.ReadTotalTimeoutConstant:=50;
      CommTimeouts.WriteTotalTimeoutMultiplier:=0;
      CommTimeouts.WriteTotalTimeoutconstant:=50;
      SetCommTimeouts(comm,CommTimeouts);
      merker1:=setcommstate(comm,comst);
      result:=0;
      ReadFail:=0;
      WriteFail:=0;
    end
   else
    begin
      result:=-1;
      Comm:=0;
    end;
  end;

// Daten senden
// SDaten enthalten Befehlsstring
procedure TAPMLU.sendData(SDaten: array of Ansichar; Anzahl: SmallInt);

 var    x: SmallInt;
         bcc : byte;
         test: longbool;
         len: DWord;
         s: String;
begin
 SDaten[0]:=AnsiChar(Anzahl);
 SDaten[Anzahl]:=#0;
 bcc:=ord(SDaten[0]);
 for x:=1 to (anzahl-1) do bcc:=bcc xor ord(SDaten[x]);
 SDaten[anzahl]:=AnsiChar(bcc);
 for x:=0 to anzahl+1 do buff[x]:=SDaten[x];

 Sended:=IntToStr(anzahl+1)+' Byte: ';
 for x:=0 to anzahl do
  Sended:=Sended+DecHex(ord(buff[x]))+' ';

 test:=WriteFile(Comm,buff,Anzahl+1,len,nil);
 if len<>(Anzahl+1) then
  begin
   inc(WriteFail);
   s:='W '+IntToStr(WriteFail)+': '+Sended;
   Writeln(DebugFile,s);
  end;
end;

function TAPMLU.GetData(Anzahl:SmallInt; Time :longint; var Daten: array of AnsiChar):integer;
var      x: longint;
          y: SmallInt;
          anz: DWord;
          Timeout: longint;
          bcc: byte;
          s: String;

begin
 Daten[0]:=#0;
 x:=0; anz:=0;
 ReadFile(Comm,buff,Anzahl,anz,nil);
 buff[anz]:=#0;
 if anz>0 then for y:=1 to anz do daten[x+y-1]:=buff[y-1];
 x:=x+anz;

 result:=0;
 bcc:=0;

 Recived:=IntToStr(x)+' Byte: ';
 for y:=0 to x-1 do
  Recived:=Recived+DecHex(ord(daten[y]))+' ';

 if Anzahl<>Anz then
  begin
   inc(ReadFail);
   s:='W '+IntToStr(ReadFail)+' send: '+Sended+' recived: '+Recived;
   Writeln(DebugFile,s);
  end;


 for y:=0 to x-2 do bcc:=bcc xor ord(daten[y]);
 result:=ord(Daten[1]);
 if x=0 then
  result:=-10;

 if (bcc<>ord(daten[x-1]))and(x>0)then
  result:=-30;
end;
Ich sende eine definierte Befehlssequenz zum Gerät, welches mir in einem binären Protokoll eine Antwort zurückliefert. Deswegen arbeite ich auch syncron, da ich genau weiß, wie viele Byte zurückkommen. Sind es weniger, dann liegt ein Fehler vor und dieser wird ausgewertet, wenn die Function ReadFile über einen Timeout beendet wird.

Funktioniert auf meinem Notebook mit Windows 7 (64-Bit) ausgezeichnet. Unter Windows 8 (kann evtl. auch an der Rechnergeschwindigkeit liegen) gibt es Probleme. Es kommt des öfteren nicht die erwartete Anzahl an Datenbytes an.
Ganz konkret erwarte ich 35 Byte. Mal kommen nur 34, mal nur 33 und auch mal weniger an. Es fehlen meist die letzten Bytes, manchmal aber auch das Erste.
Sieht ganz so aus, als würden die noch irgendwo im Cache rumliegen.?

Habe ich vielleicht irgendwo einen Denkfehler in meinem Programm? Irgend etwas falsch initialisiert?
Am Readtimeout (50ms) liegt es nicht. Auch bei 1000 ist das Problem das Gleiche.

Hatte vielleicht schon mal jemand ähnliche Probleme?

Gruß
BlackGuest

hathor 3. Nov 2014 08:00

AW: Datenverlust bei ReadFile auf virtuellen COM-Port
 
Du hast einen Mischmasch im Code!
Schreibe konsequent im Code, was Du willst:
AnsiString und PAnsiChar, wo das erwartet wird!

Wie sehen die Bytes am Schluss aus?
Kommt da IMMER das gleiche Byte, dann kann man das als Ende der Übertragung auswerten.
Nimm eine Komponente, wo die function GetTriggersOnRxChar: Boolean;
oder etwas Ähnliches vorhanden ist.

BlackGuest 3. Nov 2014 08:24

AW: Datenverlust bei ReadFile auf virtuellen COM-Port
 
Ok, der Mischmasch kommt daher, dass das Ganze nur schnell aus einer älteren Version übernommen wurde.
Hat aber nichts mit dem Problem zu tun.

Nein, das letzte Byte ist nicht gleich aber die Anzahl der Datenbytes.

Es geht darum, das Daten "verloren" gehen.

Gruß
BlackGuest

hathor 3. Nov 2014 08:30

AW: Datenverlust bei ReadFile auf virtuellen COM-Port
 
Schlechte Kabelverbindung ausgeschlossen?
Zu lang, zu alt, "wackelig"...

CBR_256000 ist sehr hoch - geht es auch langsamer?

Ein Computer macht eigentlich nichts zufällig, ausser mit "Random"...
Ist das Device auch als HID vorhanden?

himitsu 3. Nov 2014 08:49

AW: Datenverlust bei ReadFile auf virtuellen COM-Port
 
Ist auch klar, denn du liest nur das aus, was "aktuell" im Puffer ist, das können mehr oder wender sein, als das, was du jetzt auslesen willst.
Bei weniger mußt du entweder auf die fehlenden Daten warten oder die Daten sammeln und zusammensetzen, bis die Anzahl ausreicht.

Ich würde dir aber eh empfehlen, eine Comport-Komponente zu benutzen.


Ist bei sendData Length(SDaten) größer als Anzahl?
-> Buffer-overrun?

Zitat:

Delphi-Quellcode:
port:=pChar('com'+IntToStr(PC_COMM));

Delphi speichert den erzeigten String in einer temporären String-Variable, und bis zur Verwendung von "port" könnte es nun passieren, daß Delphi die Variable löscht/überschreibt, wenn man das erst "viel" später benutzt.

himitsu 3. Nov 2014 08:56

AW: Datenverlust bei ReadFile auf virtuellen COM-Port
 
Zitat:

Zitat von himitsu (Beitrag 1278454)
Ist auch klar, denn du liest nur das aus, was "aktuell" im Puffer ist, das können mehr oder wender sein, als das, was du jetzt auslesen willst.
Bei weniger mußt du entweder auf die fehlenden Daten warten oder die Daten sammeln und zusammensetzen, bis die Anzahl ausreicht.

Ich würde dir aber eh empfehlen, eine Comport-Komponente zu benutzen.


Ist bei sendData Length(SDaten) größer als Anzahl?
-> Buffer-overrun?

Zitat:

Delphi-Quellcode:
port:=pChar('com'+IntToStr(PC_COMM));

Delphi speichert den erzeigten String in einer temporären String-Variable, und bis zur Verwendung von "port" könnte es nun passieren, daß Delphi die Variable löscht/überschreibt, wenn man das erst "viel" später benutzt.



PS:
Daten am Port werden nicht gecachet, solange der Port nicht offen ist, also wenn Daten ankommen, bevor du den Port aufmachst, dann sind die weg.

Das war übrigens auch einer der Gründe, warum ich meinte der Port solle besser offen bleiben.
Wenn man den Port aufmacht, Daten anfragt und dann die Daten ausliest, dann OK,
aber du willst etwas auslesen das "irgendwann" gesendet wird.

BlackGuest 3. Nov 2014 09:40

AW: Datenverlust bei ReadFile auf virtuellen COM-Port
 
Zitat:

Zitat von hathor (Beitrag 1278451)
Schlechte Kabelverbindung ausgeschlossen?
Zu lang, zu alt, "wackelig"...

Ausgeschlossen. Ein Gerät, ein Kabel, zwei Rechner am Ende des Kabels. Ist von daher schon ausgeschlossen, da die Daten über USB schon "gekapselt" übertragen werden. Mit der alten RS232 hat das nichts mehr zu tun. Der Port wird nur fast identisch angesprochen.

Zitat:

Zitat von hathor (Beitrag 1278451)
CBR_256000 ist sehr hoch - geht es auch langsamer?

Die Einstellung ist eigentlich egal. Über USB werden die Daten immer mit Full-Speed (12MBit/s) übertragen.
Evtl. könnte der Wert intern noch für irgendwelche Timings verwendet werde. Mit 9600 ist das Ergebnis aber das Gleiche.

Zitat:

Zitat von hathor (Beitrag 1278451)
Ist das Device auch als HID vorhanden?

Nein, nur als CDC.

Zitat:

Ist auch klar, denn du liest nur das aus, was "aktuell" im Puffer ist, das können mehr oder wender sein, als das, was du jetzt auslesen willst.
Bei weniger mußt du entweder auf die fehlenden Daten warten oder die Daten sammeln und zusammensetzen, bis die Anzahl ausreicht.
Ich arbeite syncron. ReadFile liefert mir erst ein Ergebnis zurück, wenn entweder die Anzahl Datenbyte gelesen werden konnte oder ein Timeout eintritt.
Ein Timeout tritt noch obigem Code nach 50ms ein. Selbst mit 1000ms habe ich das gleiche Problem.
Beim Senden treten keine Fehler auf. Wenn, dann würde mein Device auch nicht antworten bzw. mit einer Fehlermeldung.
Der Port wird beim Programmstart geöffnet und beim Beenden geschlossen. Ist also die ganze Zeit offen.

Mein Device sendet nur Daten, wenn es vorher eine gültige Befehlssequenz empfangen hat.

Ich hoffe ich konnte einige Unklarheiten ausräumen.

Mein Programm läuft auf einigen Windows 7 PCs problemlos, unter Windows 8 kommt es zu dem Datenverlust, wobei ich da zu wenig Hardware hier habe um 100%-ig sagen zu können, dass es am Windows 8 liegt. Kann auch an der "PC"-Geschwindigkeit liegen. Mein Windows 8 Rechner ist ein Tablet.

Gruß und danke für die bisherigen Hinweise.
BlackGuest

hathor 3. Nov 2014 10:08

AW: Datenverlust bei ReadFile auf virtuellen COM-Port
 
Zitat:

Zitat von BlackGuest (Beitrag 1278459)
Die Einstellung ist eigentlich egal.
BlackGuest

Das bezweifle ich.
Was empfiehlt der Hersteller des Gerätes oder des USB-COM-Wandlers?

Da Deine "Selbstbau"-Software NICHT Event-gesteuert ist, hängt es vom Zufall ab, wann WINDOWS die Daten aus dem Buffer holt.

Ich habe hier die Beschreibung eines CDC-Chips:
256 kommt da NICHT vor:
The following baud rates are supported: 1200, 2400,4800, 9600, 19200, 38400, 57600, 115200, 230400, 460800, and 921600 bps.

BlackGuest 3. Nov 2014 10:59

AW: Datenverlust bei ReadFile auf virtuellen COM-Port
 
Zitat:

Zitat von hathor (Beitrag 1278467)
Zitat:

Zitat von BlackGuest (Beitrag 1278459)
Die Einstellung ist eigentlich egal.
BlackGuest

Das bezweifle ich.
Was empfiehlt der Hersteller des Gerätes oder des USB-COM-Wandlers?

Die CDC ist ja eigentlich für USB-Serial Wandler gedacht. Dort spielt der Wert eine Rolle. Bei der reinen Übertragung über USB nicht. Der CDC Treiber läuft direkt auf einem Microcontroller, also nicht USB-Serial Wandler <--> Microcontroller. Wie schon geschrieben, könnte es sein, das der PC-seitige Treiber den Wert für irgend welche Timings verwendet. Nur mit 9600 Baud (Einstellung im MC) kommt es auf das Gleiche raus.

Zitat:

Zitat von hathor (Beitrag 1278467)
Da Deine "Selbstbau"-Software NICHT Event-gesteuert ist, hängt es vom Zufall ab, wann WINDOWS die Daten aus dem Buffer holt.

ReadFile sollte schon den Buffer auslesen. Auch ohne Event.
Sollte, schon möglich das da das Problem liegt. Nur warum sollte ich auf jedes einzelnen Zeichen reagieren, wenn mich nur die Komplette Antwortsequenz interessiert, die nun mal x-Datenbyte lang ist? Wieso funktioniert das Ganze unter Windows 7 anscheinend völlig ohne Probleme?

Gruß
BlackGuest

hathor 3. Nov 2014 13:06

AW: Datenverlust bei ReadFile auf virtuellen COM-Port
 
Testprogramm:
http://www.rosseeld.be/DRO/PIC/TestCDC.zip

BlackGuest 3. Nov 2014 13:14

AW: Datenverlust bei ReadFile auf virtuellen COM-Port
 
Danke für den Link.
Werde ich dann gleich mal anschauen.

Habe inzwischen noch was rausgefunden. ReadFile liest schon alle Daten die ankommen bzw. die im Puffer sind, bevor die Funktion ein Timout auftritt.
Ich habe jetzt einen festen Testdatensatz verwendet. Es fehlen nicht Bytes am Anfang oder am Ende, sondern irgenwo mitten in der Antwortsequenz. Ich vermute fast, dass die CDC Implementation in meinem MC nicht mit der Windows 8 Implementation zusammenpasst und da irgendwo dieses Problem begründet ist.

Gruß
BlackGuest

BlackGuest 3. Nov 2014 17:03

AW: Datenverlust bei ReadFile auf virtuellen COM-Port
 
Danke noch mal für das Beispiel.
Der Code dort entspricht im Prinzip dem aus meinem Startthread.

Ich habe das Ganze ein wenig umgeschrieben, so dass ich in einer Endlosschleife immer die gleiche Sequenz zum MC sende und auch immer den gleichen Datensatz zurück bekommen sollte. Das Problem ist auch hier das Gleiche. Es gehen hin und wieder Datenbyte verloren. Auch unter Windows 7. Das ist nur nicht aufgefallen, da das Problem wesentlich seltener auftritt und ich im Fehlerfall das Kommando ein mal wiederhole. Liegt wohl eher doch an der Rechnergeschwindigkeit.

Gesendet werden 6 Byte und die Antwort sollte 35 Byte lang sein. Auf meinem Windows 7 Rechner fehlt auch ca. alle 2000 Abfragen irgend ein Byte.

Die Fehlerhäufigkeit kann ich dadurch minimieren, in dem ich mit ReadFile nicht den ganzen Block lese, sondern immer nur ein Byte.

Keine Ahnung, was da schief läuft. Ich werde es mal auf Ereignissteuerung umbauen.
So sollte es aber eigentlich auch problemlos funktionieren.??

Wenn jemand noch einen Einfall hat, immer her damit.

Gruß
BlackGuest

Sir Rufo 3. Nov 2014 17:09

AW: Datenverlust bei ReadFile auf virtuellen COM-Port
 
Im Prinzip ist es ja ganz einfach:

Wenn du die empfangenen Daten prüfen kannst (Länge und/oder Prüfziffer) dann sende die Anfrage so lange, bis du eine korrekte Antwort bekommst. Fehler können bei einer Übertragung immer auftauchen, egal über welches Medium auch immer.

hathor 3. Nov 2014 17:32

AW: Datenverlust bei ReadFile auf virtuellen COM-Port
 
Hast Du den Code vom Microcontroller?

Wie lange ist die erzwungene Pause zwischen SENDEN und EMPFANGEN?

BlackGuest 3. Nov 2014 17:53

AW: Datenverlust bei ReadFile auf virtuellen COM-Port
 
Zitat:

Zitat von Sir Rufo (Beitrag 1278565)
Wenn du die empfangenen Daten prüfen kannst (Länge und/oder Prüfziffer) dann sende die Anfrage so lange, bis du eine korrekte Antwort bekommst. Fehler können bei einer Übertragung immer auftauchen, egal über welches Medium auch immer.

Mache ich ja, aber die Häufigkeit des Fehlers ist zu groß. Eine gewissen Timeoutzeit muss ich dem Ganze aber einräumen. Wenn jeder 10. Datensatz falsch ist, dann dauert das Ganze ewig, was nur ein paar sekunden dauert, wenn die Übertragung fehlerfrei abläuft. Es sind auch keine direkten Übertragungsfehler.

Zitat:

Hast Du den Code vom Microcontroller?
Wie lange ist die erzwungene Pause zwischen SENDEN und EMPFANGEN?
Ja, habe ich. Ist allerdings die Standardimplementation, die beim Compiler dabei war. Ich will nicht ausschließen, dass die nicht 100%-ig konform ist und das Problem dadurch auftaucht. Nur bevor ich mich da durchwühle nehme ich Testweise lieber einen anderen Compiler.

Momentan ist keine Pause dazwischen. Also (fast9 direkt nach dem Senden versuche ich mit Readfile Daten zu lesen.

Gruß
BlackGuest

hathor 3. Nov 2014 18:56

AW: Datenverlust bei ReadFile auf virtuellen COM-Port
 
Zitat:

Zitat von BlackGuest (Beitrag 1278582)
Zitat:

Wie lange ist die erzwungene Pause zwischen SENDEN und EMPFANGEN?
Momentan ist keine Pause dazwischen. Also (fast direkt nach dem Senden versuche ich mit Readfile Daten zu lesen.

Gruß
BlackGuest


Mach mal ein SLEEP(50); dazwischen.

BlackGuest 3. Nov 2014 19:12

AW: Datenverlust bei ReadFile auf virtuellen COM-Port
 
Hatte ich auch schon alles versucht. Hat nix gebracht.

Ich glaube ich fresse einen Besen!
Es scheint jetzt zu funktionieren. Egal mit welchem Rechner. Das Problem lag eindeutig am Zusammenspiel der Treiber. Der auf der Mikrocontrollerseite war wahrscheinlich nicht sauber implementiert. Ich habe jetzt eine neuere Version getestet und siehe da es scheint zu gehen.
Da muss man erst mal drauf kommen, wenn es auf dem einen Rechner funktioniert und auf dem anderen nicht. Die Probleme sind hauptsächlich mit PCs aufgetreten, die mit Windows 8 liefen. Liegt aber wahrscheinlicher an der eingebauten USB-Hardware.

OK, falls jemand ähnliche Probleme hat, ich verwende den CCS Compiler. V4.124 verursacht die beschriebenen Probleme. Mit V4.137 funktioniert alles.

Danke für Eure Geduld und Hilfe.

Gruß
BlackGuest


Alle Zeitangaben in WEZ +1. Es ist jetzt 05:37 Uhr.

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz