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/)
-   -   Delphi COM Port Daten auslesen und auf bestimmtes Char reagieren (https://www.delphipraxis.net/188192-com-port-daten-auslesen-und-auf-bestimmtes-char-reagieren.html)

AJ_Oldendorf 8. Feb 2016 12:50

COM Port Daten auslesen und auf bestimmtes Char reagieren
 
Hallo zusammen,
ich habe ein Gerät, welches mir ca. aller 10ms ein Wert über die serielle Schnittstelle liefert.
Dieser String hat immer den gleichen Aufbau und endet mit #13#10.
Das Gerät kann aber auch andere Daten schicken aber es wird immer mit #13#10 abgeschlossen.
In meiner Software habe ich eingestellt, dass ich auf das #10 reagieren möchte und damit das Ende eines Strings erkenne.
Leider wird mir immer der komplette Buffer gefüllt den ich bei ReadFile angegeben habe und nicht nur bis zu dem gesetztem RXFlagChar (#10).

Zum seriellen Gerät:
Baudrate: 19200
Parität: Keine
StopBits: 1

Hier ein Ausschnitt, wie ich die serielle Schnittstelle initialisiere:

Delphi-Quellcode:
  MyHandle := CreateFileA (tmpStr, Generic_Read or Generic_Write, 0, Nil,
                           Open_Existing, FILE_FLAG_OVERLAPPED, 0);
  if MyHandle <> INVALID_HANDLE_VALUE then
  begin
    if SetupComm (MyHandle, 1024, 1024) then  //eigentlich interessiert mich der Buffer nicht da ich immer nur bis #10 erkennen möchte
    begin
      FillChar (DCB, SizeOf(TDCB), 0);
      DCB.DCBlength := SizeOf (TDCB);
      DCB.BaudRate  := 19200;
      DCB.Flags     := $0001;
      DCB.Parity    := 0;
      DCB.ByteSize  := ByteSize;
      DCB.StopBits  := 0;
      DCB.EvtChar   := #10;
      if SetCommState (MyHandle, DCB) then
      begin
        With CommTimeOuts do
        begin                    
          ReadIntervalTimeout        := 10;
          ReadTotalTimeoutMultiplier := 0;
          ReadTotalTimeoutConstant   := 0;
          WriteTotalTimeoutMultiplier := 0;
          WriteTotalTimeoutConstant  := 0;
        end;
        if SetCommTimeouts(SerHandle, CommTimeOuts) then
        begin
          if SetCommMask(SerHandle, EV_RXFLAG) then
....
Die Software läuft soweit und die Kommunikation zum seriellen Gerät ist auch vorhanden nur habe ich wie gesagt das Problem, dass immer der komplette Buffer gefüllt wird, bevor ich ein Event bekomme. Ich hätte gerne ein Event nach jedem #10.

Hier der Ausschnitt vom ReadFile:

Delphi-Quellcode:
rOverlapped.Offset    := 0;
rOverlapped.OffsetHigh := 0;
ReadFile (MyHandle, ReceiveBuffer, 1024, ReceivedBytes, @rOverlapped);
Kann mir da jemand ein Tip geben wo ich weiter suchen könnte?

Viele Grüße

himitsu 8. Feb 2016 13:47

AW: COM Port Daten auslesen und auf bestimmtes Char reagieren
 
Lies mal, was SetCommState zum EvtChar sagt. https://msdn.microsoft.com/en-us/lib.../aa363214.aspx

Das steuert nicht was du aussliest.
Mit ReadFile liest man immer so viel aus, wie man angibt.
Willst du das nicht, mußt du Char-Weise auslesen, oder einen Puffer/Zwischenspeicher verwenden.

MSDN-Library durchsuchenWaitCommEvent

Photoner 8. Feb 2016 13:51

AW: COM Port Daten auslesen und auf bestimmtes Char reagieren
 
Wie wäre es mit dem lesen von jeweils einem Char? (wie himitsu´s Vorschlag)

Delphi-Quellcode:
 DCB.EvtChar := #0;
Beispiel:

Delphi-Quellcode:

function ReadChar(var MyChar : Char) : boolean;
var
  Buffer : array[0..1] of char;
  nReadChars : dword;

begin

  if not ReadFile (   MyHandle,
         Buffer,
         1,
         nReadChars,
         nil) then begin
    result := false;
    exit;
  end;

  if nReadChars = 0 then
    result := false
  else begin
    result := true;
    MyByte := Buffer[0];
  end;

end;

function GetAnswer(const ATimeOutInMS : Cardinal) : String;
var
 i          : Integer;
 TimeStamp  : Cardinal;
 AnswerChars : Array of Char;
 CurrentChar : Char;
begin
 TimeStamp := GetTickCount;
 Result   := '';
 while True do begin
  if ReadChar(CurrentChar) then begin
   // #13#10 beendet gültige Antwort:
   if CurrentChar=#13 then break;
   SetLength(AnswerChars,Length(AnswerChars)+1);
   AnswerChars[High(AnswerChars)] := CurrentChar;
  end;
  if GetTickCount>TimeStamp+ATimeOutInMS then begin
   //Result := 'Timeout beim Lesen';
   exit;
  end;
 end;
 SetLength(Result,Length(AnswerChars));
 for i:= 1 to Length(AnswerChars) do Result[i] := AnswerChars[i];
end

Verknüpft mit einem "ATimeOutInMS" von 9 (ms) sollte das deinen Zwecken genügen.

Evtl. ist noch was falsch im Code. Nur im Browser getippt; er ist nicht getestet.

AJ_Oldendorf 8. Feb 2016 13:57

AW: COM Port Daten auslesen und auf bestimmtes Char reagieren
 
Ok, Zeichenweise auslesen wäre auch noch möglich aber ich dachte das "EvtChar" ist genau dafür da.
Da steht ja:
The value of the character used to signal an event.

Ich bin davon ausgegangen, dass ich ein Event bekomme wenn das entsprechende Char gefunden wurde im Buffer...

Das andere muss ich mir mal genauer angucken von "Photoner"

AJ_Oldendorf 8. Feb 2016 14:18

AW: COM Port Daten auslesen und auf bestimmtes Char reagieren
 
Habe anstatt dem ReadFile folgenden Aufruf gemacht:

Delphi-Quellcode:
TmpMask := EV_RXFLAG;
WaitCommEvent(MyHandle, TmpMask, @rOverlapped);
Darauf wird auf alle Fälle reagiert aber wie komme ich jetzt an die Daten? Nach dem ReadFile stehen die Daten ja in dem übergebenen ReceiveBuffer.

Neumann 8. Feb 2016 14:20

AW: COM Port Daten auslesen und auf bestimmtes Char reagieren
 
Warum die Comport-Geschichte selber bauen wenn es sie schon fertig gibt?

Komponente TComport Dejan Crnila, kann man mit allen Delphi-Versionen einsetzen und läuft bei mir einwandfrei.

Photoner 8. Feb 2016 14:25

AW: COM Port Daten auslesen und auf bestimmtes Char reagieren
 
Zitat:

Zitat von AJ_Oldendorf (Beitrag 1329777)
Habe anstatt dem ReadFile folgenden Aufruf gemacht:

Darauf wird auf alle Fälle reagiert aber wie komme ich jetzt an die Daten? Nach dem ReadFile stehen die Daten ja in dem übergebenen ReceiveBuffer.

Du sagst es doch selber. Mach den ReceiveBuffer im ReadFile groß genug und gehe die einzelnen Chars dann durch bis du auf das #13#10 triffst. Alles davor ergibt dir deinen String. Dein Event signalisiert dir doch jetzt ob #10 im Buffer ist
(Zitat MSDN :
"EV_RXFLAG
0x0002
The event character was received and placed in the input buffer. The event character is specified in the device's DCB structure, which is applied to a serial port by using the SetCommState function."
)

AJ_Oldendorf 8. Feb 2016 14:46

AW: COM Port Daten auslesen und auf bestimmtes Char reagieren
 
Folgendes habe ich vergessen zu sagen:
Ich brauche zu jedem Wert den ich übermittelt bekommen habe sozusagen ein Zeitstempel wann der Wert eingetroffen ist.
Wenn ich jetzt den Buffer sehr groß stelle, dann werden intern "ganz viele" Werte aufgesammelt. Anschließend soll ich die ja einzeln abgrasen und auf das bestimmte Ev_Char prüfen.
Ich will ja keine Verzögerung zwischen dem Eingang des Strings und der Weitergabe habe (also kein Buffer von x Strings).

Und die Länge vor dem #10 ist auch unterschiedlich. Ein String vor dem #10 kann mal 16 Bytes lang sein, mal aber auch nur 4 Byte oder 18 Bytes...

Photoner 8. Feb 2016 15:32

AW: COM Port Daten auslesen und auf bestimmtes Char reagieren
 
Bin beim lesen darauf gestoßen:

Zitat MSDN:
------------------------------------------------------------------------------------------------------------------------------
ReadIntervalTimeout

The maximum time allowed to elapse before the arrival of the next byte on the communications line, in milliseconds. If the interval between the arrival of any two bytes exceeds this amount, the ReadFile operation is completed and any buffered data is returned. A value of zero indicates that interval time-outs are not used.

A value of MAXDWORD, combined with zero values for both the ReadTotalTimeoutConstant and ReadTotalTimeoutMultiplier members, specifies that the read operation is to return immediately with the bytes that have already been received, even if no bytes have been received.

------------------------------------------------------------------------------------------------------------------------------

Setz das und lese als Reaktion auf dein Event.

Dein Event "#10 Empfangen" wird gefeuert --> du hast einen kompletten String im Buffer und einen ziemlich exakten Zeitstempel.

Readfile blockiert mit der oben erwähnten Einstellung nicht (Zeitstempel hier ist also auch ok) und kommt sofort zurück (Wenn du 1024 bytes anforderst bekommst du trotzdem nur das was im Buffer -Stichwort ReceivedBytes- ist)

Zufrieden :)?

Sir Rufo 8. Feb 2016 15:36

AW: COM Port Daten auslesen und auf bestimmtes Char reagieren
 
Also ich würde so etwas immer aufteilen.

Eine Klasse holt Bytefolgen vom ComPort ab und gibt diese mit dem Zeitstempel weiter.

Eine Klasse nimmt die Bytefolge/Zeitstempel Nachricht und baut bei jedem Vorkommen von #13#10 eine Nachricht mit Bytefolge/Zeitstempel und gibt diese weiter.

AJ_Oldendorf 8. Feb 2016 20:19

AW: COM Port Daten auslesen und auf bestimmtes Char reagieren
 
@Photoner:

Danke, das klingt sehr interessant.
Also das würde dann so aussehen, oder?

Delphi-Quellcode:
ReadIntervalTimeout := MaxDWord;
ReadTotalTimeoutConstant := 0;
ReadTotalTimeoutMultiplier := 0;

TmpMask := EV_RXFLAG;
WaitCommEvent(MyHandle, TmpMask, @rOverlapped);

ReadFile (MyHandle, ReceiveBuffer, 1024, ReceivedBytes, @rOverlapped);
Und wenn das Event von WaitCommEvent getriggert wird, rufe ich ReadFile auf oder?
Dann brauch ich doch bei dem ReadFile auch kein Overlapped Event mehr übergeben, da dies ja bereits getriggert wurde, richtig?

Grüße

AJ_Oldendorf 9. Feb 2016 07:20

AW: COM Port Daten auslesen und auf bestimmtes Char reagieren
 
Ich habe das mal so versucht, dass ich die Zeiten entsprechend gesetzt habe und anschließend mit WaitCommEvent auf das #10 gewartet habe. Anschließend reagiere ich auf das Event mit ReadFile:

Delphi-Quellcode:
ReadFile (MyHandle, ReceiveBuffer, 1024, ReceivedBytes, Nil);
Leider ist der ReceiveBuffer an dieser Stelle immer leer und auch die ReceivedBytes sind 0.

Frage ich die ReceivedBytes nach dem ReadFile so ab
Delphi-Quellcode:
ReceivedBytes := rOverlapped.InternalHigh;
dann stehen da 4 Bytes als Anzahl. Aber leider habe ich immer noch nicht die Daten die gelesen wurden, da mit ReadFile anscheind keine Daten gelesen wurden

Photoner 9. Feb 2016 10:05

AW: COM Port Daten auslesen und auf bestimmtes Char reagieren
 
WaitCommEvent

"If hFile was opened with FILE_FLAG_OVERLAPPED and lpOverlapped is not NULL, WaitCommEvent is performed as an overlapped operation. In this case, the OVERLAPPED structure must contain a handle to a manual-reset event object (created by using the CreateEvent function).

If hFile was not opened with FILE_FLAG_OVERLAPPED, WaitCommEvent does not return until one of the specified events or an error occurs."

In dem Overlapped Record muss also ein Handle auf ein Event (das mit CreateEvent erzeugt wird) drin sein. Wie schaut
Delphi-Quellcode:
rOverlapped
aus?

Alternativ:

Lass das FILE_FLAG_OVERLAPPED mal im CreateFile weg und verzichte auch danach darauf. Mir selbst ist nicht ganz klar wie der asynchrone Zugriff denn genau funktioniert. Ich glaube du musst vor dem ReadFile die Funktion WaitForSingleObject mit dem oben erwähnten Handle aufrufen. Synchron tuts hier auch da der Rücksprung aus WaitCommEvent ohne das
Delphi-Quellcode:
rOverlapped
erst kommt wenn dein RX_Char #10 tatsächlich im Buffer ist.

AJ_Oldendorf 9. Feb 2016 10:15

AW: COM Port Daten auslesen und auf bestimmtes Char reagieren
 
hEvent wird gesetzt beim Init des ComPorts:

Delphi-Quellcode:
rOverlapped.hEvent := CreateEventA (NIL, True, False, NullStr);
Dann kommt das WaitCommEvent wie oben schonmal geschrieben und wenn dieses getriggert wird sieht mein Aufruf so aus:

Delphi-Quellcode:
ReadFile (MyHandle, ReceiveBuffer, 1024, ReceivedBytes, Nil); //<- ReceivedBytes ist hier 0
ReceivedBytes := SeriellComp.rOverlapped.InternalHigh; //<- ReceivedBytes ist hier 4
ResetEvent(rOverlapped.hEvent);
So wäre das ja richtig von der Reihenfolge oder?

Grüße

AJ_Oldendorf 9. Feb 2016 10:48

AW: COM Port Daten auslesen und auf bestimmtes Char reagieren
 
@Sir Rufo...
Zitat:

Also ich würde so etwas immer aufteilen.

Eine Klasse holt Bytefolgen vom ComPort ab und gibt diese mit dem Zeitstempel weiter.

Eine Klasse nimmt die Bytefolge/Zeitstempel Nachricht und baut bei jedem Vorkommen von #13#10 eine Nachricht mit Bytefolge/Zeitstempel und gibt diese weiter.
Ich habe jetzt mal Byteweise die COM abgefragt und mir entsprechend beim Auftreten meines #10 den String zusammen gebaut. Das ist ja im Endeffekt ein Pollen der Seriellen Schnittstelle und man merkt dabei "deutlich", dass das mehr Rechenleistung benötigt.

Der bessere Weg ist auf ein bestimmtes Zeichen nur zur Triggern (WaitCommEvent) und dann die Daten auslesen.
Das Problem ist nur, dass dann bei mir nichts im Buffer steht wenn ich ReadFile nach dem WaitCommEvent aufrufe

Photoner 9. Feb 2016 11:02

AW: COM Port Daten auslesen und auf bestimmtes Char reagieren
 
Der Rücksprung aus der Funktion WaitCommEvent erfolgt sofort obwohl das Ereignis RX_Char noch gar nicht eingetreten ist. Das Readfile danach findet dann meist auch nichts.

WaitForSingleObject ist das Stichwort.

Diese Funktion wartet auf ein bestimmtes Ereignis und hat einen Timeout.

Wenn das Event RX_Char auslöst, dann wird das Event im Overlapped signalisiert und ein Rücksprung aus WaitForSingleObject erfolgt.


WaitCommEvent
WaitForSingleObject
Readfile

Photoner 9. Feb 2016 11:05

AW: COM Port Daten auslesen und auf bestimmtes Char reagieren
 
Oder du verzichtest komplett auf das Overlapped (== synchroner Zugriff) und dann erfolgt der Rücksprung aus WaitCommEvent erst wenn das Event auslöst oder ein Fehler passiert. In den MSDN Links steht alles was du brauchst.

AJ_Oldendorf 9. Feb 2016 11:10

AW: COM Port Daten auslesen und auf bestimmtes Char reagieren
 
Das habe ich auch so.

Mein Thread:

Delphi-Quellcode:
while not Terminated do
begin
  HandleBuffer[0] := rOverlapped.hEvent;
  HandleBuffer[1] := wOverlapped.hEvent;
  Return := MsgWaitForMultipleObjects (Handles, HandleBuffer, False,
                                       2000, QS_ALLINPUT);
  if Terminated then exit;
  Case Return of
    WAIT_OBJECT_0  : begin            
                       ReadFile (MyHandle, ReceiveBuffer, 1024, ReceivedBytes, Nil); //<- ReceivedBytes = 0
                       ReceivedBytes := rOverlapped.InternalHigh; //<- ReceivedBytes = 4
                       //ReceiveBuffer ist leer
                       ResetEvent(rOverlapped.hEvent);
                       rOverlapped.Offset := 0;
                       rOverlapped.OffsetHigh := 0;
                       if ReceivedBytes > 0 then
                       begin
...
WAIT_OBJECT_0+1 ist fürs Schreiben.

Abfrage der Schnittstelle:

Delphi-Quellcode:
rOverlapped.Offset    := 0;
rOverlapped.OffsetHigh := 0;

TmpMask := EV_RXFLAG;
WaitCommEvent(MyHandle, TmpMask, @rOverlapped);
Sollte dem von dir entsprechen...
Zeiten sind beim Init entsprechend auf 0 oder MaxDWord

AJ_Oldendorf 9. Feb 2016 11:17

AW: COM Port Daten auslesen und auf bestimmtes Char reagieren
 
Habe bei dem ReadFile anstatt dem Nil als letzten Parameter nochmal die gleiche Overlapped Struktur angegeben.
Dann bekomme ich auch in den ReceivedBytes was zurück. Nämlich die angegebenen 1024. Ich will ja aber keine 1024 lesen sondern immer nur zwischen zwei #10 Characters.

Sir Rufo 9. Feb 2016 11:23

AW: COM Port Daten auslesen und auf bestimmtes Char reagieren
 
Zitat:

Zitat von AJ_Oldendorf (Beitrag 1329845)
@Sir Rufo...
Zitat:

Also ich würde so etwas immer aufteilen.

Eine Klasse holt Bytefolgen vom ComPort ab und gibt diese mit dem Zeitstempel weiter.

Eine Klasse nimmt die Bytefolge/Zeitstempel Nachricht und baut bei jedem Vorkommen von #13#10 eine Nachricht mit Bytefolge/Zeitstempel und gibt diese weiter.
Ich habe jetzt mal Byteweise die COM abgefragt und mir entsprechend beim Auftreten meines #10 den String zusammen gebaut. Das ist ja im Endeffekt ein Pollen der Seriellen Schnittstelle und man merkt dabei "deutlich", dass das mehr Rechenleistung benötigt.

Der bessere Weg ist auf ein bestimmtes Zeichen nur zur Triggern (WaitCommEvent) und dann die Daten auslesen.
Das Problem ist nur, dass dann bei mir nichts im Buffer steht wenn ich ReadFile nach dem WaitCommEvent aufrufe

So sollst du das ja auch nicht machen :stupid:

Hole dir alles was du vom ComPort kriegen kannst. Das ist ja eine Bytefolge. Diese packst du dann in einen Record
Delphi-Quellcode:
TDataBlock = record
  Data: TBytes;
  Timestamp: TDateTime;
end;
und den dann in eine Queue. Diese Queue wird dann von einem anderen Thread abgefragt und verarbeitet. Dort erfolgt dann das Untersuchen und Zerteilen der Nachrichten und wenn eine komplette Nachricht erkannt wurde, dann schickt dieser Thread die Nachricht raus bzw. reicht diese Nachricht an eine weitere Queue, wo dann ein Versender-Thread diese Queue abfragt und die Nachrichten an die Empfänger schickt.

Durch die unterschiedlichen Threads laufen Empfänger, Zerteiler und Versender völlig unabhängig voneinander und stören sich folglich nicht gegenseitig.

Das Konzept selber ist eine DataFlow-Pipeline und eine Implementierung findest du
https://github.com/gabr42/OmniThread...1_Pipeline.pas

Photoner 9. Feb 2016 12:01

AW: COM Port Daten auslesen und auf bestimmtes Char reagieren
 
Readfile liefert in deiner Konstellation falsche Ergebnisse da es

entweder a) mit Overlapped wieder asynchron ist (Rücksprung bevor fertig gelesen)
oder b) mit nil beim Overlapped einfach falsche Ergebnisse liefern kann.

Du musst das Rückgabeergebnis von Readfile prüfen und wieder auf das Event warten:

Delphi-Quellcode:
if not ReadFile(MyHandle, ReceiveBuffer, 1024, nil, @rOverlapped) then
  if GetLastError()<>ERROR_IO_PENDING raise Exception.Create('COM Fehler');
case WaitForSingleObject(rOverlapped.hEvent,ATimeOutInMS) of
 ...
Ich muss nochmals auf die Quelle schlechthin, MSDN, hinweisen. Steht alles da.

ReadFile

"If hFile is opened with FILE_FLAG_OVERLAPPED, the lpOverlapped parameter must point to a valid and unique OVERLAPPED structure, otherwise the function can incorrectly report that the read operation is complete."

"lpNumberOfBytesRead [out, optional]
A pointer to the variable that receives the number of bytes read when using a synchronous hFile parameter. ReadFile sets this value to zero before doing any work or error checking. Use NULL for this parameter if this is an asynchronous operation to avoid potentially erroneous results."

Unter Remarks:
"Synchronization and File Position

If hFile is opened with FILE_FLAG_OVERLAPPED, it is an asynchronous file handle; otherwise it is synchronous. The rules for using the OVERLAPPED structure are slightly different for each, as previously noted.
Note If a file or device is opened for asynchronous I/O, subsequent calls to functions such as ReadFile using that handle generally return immediately, but can also behave synchronously with respect to blocked execution. For more information see http://support.microsoft.com/kb/156932.


Considerations for working with asynchronous file handles:

ReadFile may return before the read operation is complete. In this scenario, ReadFile returns FALSE and the GetLastError function returns ERROR_IO_PENDING, which allows the calling process to continue while the system completes the read operation.
The lpOverlapped parameter must not be NULL and should be used with the following facts in mind:
Although the event specified in the OVERLAPPED structure is set and reset automatically by the system, the offset that is specified in the OVERLAPPED structure is not automatically updated.
ReadFile resets the event to a nonsignaled state when it begins the I/O operation.
The event specified in the OVERLAPPED structure is set to a signaled state when the read operation is complete; until that time, the read operation is considered pending.
Because the read operation starts at the offset that is specified in the OVERLAPPED structure, and ReadFile may return before the system-level read operation is complete (read pending), neither the offset nor any other part of the structure should be modified, freed, or reused by the application until the event is signaled (that is, the read completes).
If end-of-file (EOF) is detected during asynchronous operations, the call to GetOverlappedResult for that operation returns FALSE and GetLastError returns ERROR_HANDLE_EOF.
"

himitsu 9. Feb 2016 12:04

AW: COM Port Daten auslesen und auf bestimmtes Char reagieren
 
Ganz im Ernst, egal, ob du einen Zeitstempel brauchst. ReadFile liest das aus, was schon im Cache des Port ist.

Ob du nun mühevoll NUR eine Zeilen ausliest, oder einfach alles und dass dann trennst, ist egal ... DA ist DA.

1 alles in einen Puffer kopieren (Neues hinten anhängen)
2 Zeilenumruch suchen
2.1 bis dahin die erste Zeile ausschneiden
2.2 verarbeiten
2.3 gehe zu 2
3 gehe zu 1

Im eigenen Puffer kann man schneller voraussuchen. Für den internen Puffer gibt es keine Preview.

Dauert das Verarbeiten länger, dann die extrahierten Zeilen mit Zeitstempel an einen anderen Thread übergeben.



Da es hier um TEXT geht, vielleicht besser ein AnsiChar-Array, einen AnsiString oder einen TStringStream vervenden.

PS: AssignFile+ReadLn geht auch bei COM, genauso wie der TStringStream+THandleStream/TFileStream.

AJ_Oldendorf 9. Feb 2016 12:11

AW: COM Port Daten auslesen und auf bestimmtes Char reagieren
 
Nil und @rOverlapped ist sicherlich ein Tippfehler oder?

Delphi-Quellcode:
if not ReadFile(MyHandle, ReceiveBuffer, 1024, nil, @rOverlapped) then

Also sollte ich es so machen?
Delphi-Quellcode:
while not Terminated do
begin
  HandleBuffer[0] := rOverlapped.hEvent;
  HandleBuffer[1] := wOverlapped.hEvent;
  Return := MsgWaitForMultipleObjects (Handles, HandleBuffer, False,
                                       2000, QS_ALLINPUT);
  if Terminated then exit;
  Case Return of
    WAIT_OBJECT_0 : begin
                       if ReadFile (MyHandle, ReceiveBuffer, 1024, ReceivedBytes, @rOverlapped) then
                       begin
                         ResetEvent(rOverlapped.hEvent);
                         rOverlapped.Offset := 0;
                         rOverlapped.OffsetHigh := 0;
                         if ReceivedBytes > 0 then
                         begin
                         end;
                       end;
...
Muss ich mal probieren aber steht dann in den ReceivedBytes nicht wieder 1024 drinne?

AJ_Oldendorf 9. Feb 2016 12:15

AW: COM Port Daten auslesen und auf bestimmtes Char reagieren
 
@Himitsu:
Zitat:

Ganz im Ernst, egal, ob du einen Zeitstempel brauchst. ReadFile liest das aus, was schon im Cache des Port ist.
Mir geht es aber nicht um einen Zeitstempel wann ich mir den String zusammen gebaut habe sondern wann dieser sozusagen an der Schnittstelle an lag.

Aller 9ms steht sozusagen ein neuer Wert am COM an. Den brauche ich mir dem genauen Zeitstempel wann er kam.
Der nächste Wert ist dann eben 9ms später. Das ist aber abhängig davon, wie schnell das Gerät sendet.
Kann auch mal 12ms oder ähnliches sein...

himitsu 9. Feb 2016 12:21

AW: COM Port Daten auslesen und auf bestimmtes Char reagieren
 
Und den bekommst du Niemals raus.
Windows, der Treiber und vorallem dein Programm sind kein Echtzeitsystem.

Du kannst den Puffer nur schnell leeren und verarbeiten, womit du Zeitlich am Nächsten an die Empfangszeit ran kommst.
Alles unter 20 (sicherheitshalber unter 50) Millisekunden kannst du praktisch vergessen.

Photoner 9. Feb 2016 12:24

AW: COM Port Daten auslesen und auf bestimmtes Char reagieren
 
Zitat:

Zitat von AJ_Oldendorf (Beitrag 1329862)
Nil und @rOverlapped ist sicherlich ein Tippfehler oder?

Nein:

"lpNumberOfBytesRead [out, optional]
A pointer to the variable that receives the number of bytes read when using a synchronous hFile parameter. ReadFile sets this value to zero before doing any work or error checking. Use NULL for this parameter if this is an asynchronous operation to avoid potentially erroneous results."

Wenn du mit Overlapped arbeitest, steht nichts sinnvolles in diesem Parameter.

AJ_Oldendorf 9. Feb 2016 12:31

AW: COM Port Daten auslesen und auf bestimmtes Char reagieren
 
Das lässt sich aber nicht compilieren:

Delphi-Quellcode:
if ReadFile (MyHandle, ReceiveBuffer, 1024, Nil, @rOverlapped) then
Kannst du mir mal bitte in meinem Beispiel Source von oben zeigen, wie du das genau meinst?


@himitsu
Das Windows kein Echtzeitsystem ist weiß ich aber wenn ich mal eine Zeitmessung in einem while not Terminated Thread mache, dann komme ich laut Messung auf 10ms

Photoner 9. Feb 2016 12:33

AW: COM Port Daten auslesen und auf bestimmtes Char reagieren
 
Bei Asynchronem Zugriff liefert dir die Funktion

GetOverlappedResult

den gesuchten Wert.

Von der Reihenfolge dann:
WaitCommEvent
WaitForSingleObject
ReadFile
WaitForSingleObject
GetOverlappedResult


Ich habe den Zugriff auf die seriell Schnittstelle bisher nur synchron erledigt. Das mit dem Overlapped macht es sehr viel komplizierter wie man sieht.

Photoner 9. Feb 2016 12:44

AW: COM Port Daten auslesen und auf bestimmtes Char reagieren
 
Zitat:

Zitat von AJ_Oldendorf (Beitrag 1329867)
Das lässt sich aber nicht compilieren:

Delphi-Quellcode:
if ReadFile (MyHandle, ReceiveBuffer, 1024, Nil, @rOverlapped) then
Kannst du mir mal bitte in meinem Beispiel Source von oben zeigen, wie du das genau meinst?

Hast recht, das lässt sich nicht kompilieren. Man muss eine Variable angeben. In dieser steht aber kein sinnvoller Wert drinnen wenn ReadFile ein False zurück liefert weil der Zugriff noch nicht komplett ist.

Jumpy 9. Feb 2016 13:19

AW: COM Port Daten auslesen und auf bestimmtes Char reagieren
 
Zitat:

Zitat von AJ_Oldendorf (Beitrag 1329863)
@Himitsu:
Zitat:

Ganz im Ernst, egal, ob du einen Zeitstempel brauchst. ReadFile liest das aus, was schon im Cache des Port ist.
Mir geht es aber nicht um einen Zeitstempel wann ich mir den String zusammen gebaut habe sondern wann dieser sozusagen an der Schnittstelle an lag.

Dann mach doch genau das, was Sir Rufo geschrieben hat. Merk dir den momentanen Buffer samt Zeitstempel in einem Record und schieb das in einen Queue.

Ein anderer Thread arbeit nun den Queue ab. Untersucht den Record. Ist #10 drin den Part bis #10 samt dem ebenfalls im Record befindlichen Zeitstempel wegschreiben. Den Rest auch noch untersuchen. Immer noch ne #10 drin, das ganze wieder. Wenn nicht den Rest merken.
Nächsten Record (wenn denn schon einer im Queue ist) vornehmen. An den gemerkten Rest anhängen. Wieder nach #10 suchen. Falls gefunden mit dem neuen Zeitstempel aus dem neuen Record wegschreiben.
usw.usw.

AJ_Oldendorf 9. Feb 2016 13:30

AW: COM Port Daten auslesen und auf bestimmtes Char reagieren
 
Das mit dem puffern und selbstständig abarbeiten würde wahrscheinlich funktionieren.
Dachte nur ich komme mit den Windows API Funktionen soweit zurecht.

AJ_Oldendorf 10. Feb 2016 08:01

AW: COM Port Daten auslesen und auf bestimmtes Char reagieren
 
Hallo,
mein Ablauf sieht jetzt so aus:


ComPortInit
Daten vorhanden an Schnittstelle?

Delphi-Quellcode:
TmpMask := EV_RXFLAG;
WaitCommEvent(SerHandle, TmpMask, @rOverlapped);
Thread prüft Overlapped Event auf WAIT_OBJECT_0 im Execute

Delphi-Quellcode:
if ReadFile (MyHandle, ReceiveBuffer, 1024, ReceivedBytes, @rOverlapped) then
begin
  //Tue irgendwas mit den Daten
  //Now als Zeitstempeleingang nutzen
  //Protokolliere Daten
end;
Wieder auf Daten vorhanden prüfen an Schnittstelle

Delphi-Quellcode:
TmpMask := EV_RXFLAG;
WaitCommEvent(SerHandle, TmpMask, @rOverlapped);
Daten vorhanden? Dann wird Thread wieder angetriggert usw. usw. usw.


Das läuft soweit auch ganz gut auf meinem Rechner.
Trotzdem passiert es ab und zu, dass ich 2 oder 3 Strings rein bekomme (bzw. in meinem Protokoll sehe), die genau den gleichen Zeitstempel haben (Now wird genutzt nach dem ReadFile für den Zeitstempel).

Jetzt habe ich das auf einem anderen Rechner probiert und da sieht das Protokoll noch "schlechter" aus.

Der String den ich bekomme hat immer 14 Byte + #13#10

Inhaltsbeispiel:
abcdefghijklmn#13#10

Auf meinem Rechner sieht es erstmal ganz gut aus, bis auf dem Problem mit den 2-3 Werten, die alle den gleichen Zeitstempel haben.

Auf dem anderen Rechner sieht das Protokoll so aus (jede Zeile ist ein Wert):

abcdefghijklmn#13#10
abcdefghijklmn#13#10
abcdefghijklmn#13#10
abcdefghi
jklmn#13#10
abcdefghijklmn#13#10
abcdefghijklmn#13#10
abcdefghijklmn#13#10
abcdefghijklmn#13#10
abcde
fghijklmn#13#10
abcdefghijklmn#13#10
abcdefghijklmn#13#10
abcdefghijklmn#13#10
abcdefghijkl
mn#13#10
abcdefghijklmn#13#10
abcdefghijklmn#13#10
abcdefghijklmn#13#10
abcdefghijklmn#13#10

Das ist nur ein Beispiel...
Wie man aber sieht, sind die Werte nicht immer am #10 beendet wurden sondern auch irgendwo dazwischen.
Das sieht für mich so aus, als wenn das WaitCommEvent kommt (#10 erkannt) und ich dann mit ReadFile auslese aber noch nicht alle Daten vollständig im Cache sind. Kann das sein?
Ansonsten würde ja das ReadFile nicht angetriggert werden wenn das WaitCommEvent nicht kommt.
Habt ihr dazu noch eine Idee?

Neumann 10. Feb 2016 08:22

AW: COM Port Daten auslesen und auf bestimmtes Char reagieren
 
Das ist wohl immer so bei Comports. Man braucht einen Puffer, in dem man die Teile der Übertragung sammelt und dann weiter verarbeitet. Das kann z.B. einfach eine globale String-Variable sein.

AJ_Oldendorf 10. Feb 2016 08:37

AW: COM Port Daten auslesen und auf bestimmtes Char reagieren
 
@Neumann:
Wieso das denn? Ich dachte mit dem WaitCommEvent wird mir signalisiert, dass an der Schnittstelle mein gesetztes Zeichen angekommen ist (=#10). Dann müssen die Daten die sich davor befinden (können ja nur davor stehen) auch irgendwo schon im Cache liegen oder wo sind die sonst?

Rollo62 10. Feb 2016 11:31

AW: COM Port Daten auslesen und auf bestimmtes Char reagieren
 
Warum nimmst du nicht sowas wie AsyncPro, da sind schon alle LowLevel Routinen gut abgefangen ?

Rollo

Neumann 10. Feb 2016 11:55

AW: COM Port Daten auslesen und auf bestimmtes Char reagieren
 
Rollo, hatte ich auch schon so ähnlich vorgeschlagen. Vielleicht gibt es Gründe warum alles selbst gebaut werden soll.

Noch mal zu den zerteilten Strings: Muss man wohl einfach mit leben.

AJ_Oldendorf 10. Feb 2016 13:41

AW: COM Port Daten auslesen und auf bestimmtes Char reagieren
 
@Rollo62: Ja ich muss leider alles selber bauen.

@Neumann:
Zitat:

Muss man wohl einfach mit leben.
Aber gibt es dafür auch ein Grund? Wenn ein bestimmtes Zeichen vorher an der Schnittstelle erkannt wird und sich x Zeichen davor befinden, dann muss ich die doch auch irgendwo sehen können bzw. warum sollte das Event mitten in dem String kommen, wo das Zeichen eigentlich gar nicht vorkommt?

Photoner 11. Feb 2016 11:32

AW: COM Port Daten auslesen und auf bestimmtes Char reagieren
 
Noch mal:

Das mit dem Asynchron Lesen ist deutlich schwieriger als synchron. Probier doch mal die zweite Variante aus oder gib mal eine Rückmeldung dazu, falls du das schon hast. CreateFileA ohne FILE_FLAG_OVERLAPPED aufrufen und auf das ganze Overlapped Gedöns :) verzichten. Dann kommt der Rücksprung aus WaitCommEvent auch wirklich nur wenn dein Char im Buffer ist und die ganzen Waitfor... Aufrufe erledigen sich auch.


Alle Zeitangaben in WEZ +1. Es ist jetzt 08:39 Uhr.

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