Hallo zusammen!
So, nach einer 'etwas längeren' Debug-Session und Codeanalyse mit einem Kollegen sind wir dem Übel auf die Spur gekommen, wobei wir der Ansicht sind, dass es sich um einen Bug in ApdCOMPort (v4.07) handelt. Aber der Reihe nach:
Das Übel beginnt mit dem ApdCOMPort.GetChar in meinem Programm. Nach Aufruf des Befehls wird in die Datei 'AwUser.pas' in Zeile 1570ff verzweigt. Hier wird normalerweise im ELSE-Zweig ab Zeile 1588ff genau ein Zeichen geholt und dieses auch aus dem Empfangspuffer gelöscht. Manchmal aber kommt es vor, dass in "InAvailMessage" TRUE ist. In diesem Fall wird zwar auch ein bzw. das letzte Zeichen aus dem Empfangspuffer (
PeekCharPrim(C, GetCount);) geholt, es wird aber nicht gelöscht. So kann es also vor kommen, dass ein Zeichen doppelt empfangen bzw. ausgelesen wird.
Wann jedoch wird nun "InAvailMessage" TRUE?
Die Antwort dazu findet sich in derselben Datei ab Zeile 2123ff bzw. 2146ff. Das Problem bei dieser ganzen Routine ist unserer Meinung nach: Es wird zwar hier und da mit CriticalSection gearbeitet, doch ausgerechnet die Zeile, in der "InAvialMessage" auf TRUE gehen kann (Zeile 2146), befindet sich ausserhalb einer CriticalSection. Und dies ist u.M.n. der Bug selbst.
Mit anderen Worten: Ich darf via GetChar/GetBlock nicht auf den Empfangspuffer zugreifen, so lange noch Daten eintrudeln (könnten). Dummerweise nur weiß ich leider nicht, wann die Übertragung tatsächlich beendet wurde und nix mehr kommt.
ApdDataPacket ist auch irgendwo nur eine halbherzige Lösung, denn die EndCond könnte ja auch rein zufällig in den Binärdaten vorhanden sein.
Geändert habe ich die Routine für den Kopfblock erstmal wie folgt:
Delphi-Quellcode:
procedure TForm1.GetHeadBlock;
var x : word;
OldBuffInSize : integer;
begin
OldBuffInSize:= -1;
while (OldBuffInSize < 0) or
(OldBuffInSize <> ApdComPort.InBuffUsed) or
(ApdComPort.InBuffUsed = 0) do
begin
Application.ProcessMessages;
Delay(30);
OldBuffInSize:= ApdComPort.InBuffUsed;
if (TimeoutFlag) then
Break;
end;
if (not TimeoutFlag) then
begin
ApdComPort.GetBlock(Head_Block, HEADBLOCKSIZE);
end
else
Memo.Lines.Add('Timeout beim Warten auf Kopfblock');
end;
Hierbei ist allerdings auch nur das Delay verschoben aus der Hauptroutine zum Empfang und zur Auswertung aller empfangenen Daten.
Eure Meinungen?
Gruß, Carsten