Delphi-PRAXiS
Seite 1 von 2  1 2      

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   GUI-Design mit VCL / FireMonkey / Common Controls (https://www.delphipraxis.net/18-gui-design-mit-vcl-firemonkey-common-controls/)
-   -   Delphi Tserial abschiessen (https://www.delphipraxis.net/75298-tserial-abschiessen.html)

mymuschy 17. Aug 2006 11:04


Tserial abschiessen
 
Hallo,

ich kommuniziere per Tserial mit einem externen Gerät das über die Serielle Schnittstelle ständig nur am Senden ist. Empfangen wird dieses Dauerfeuer per:
Delphi-Quellcode:
procedure TSS.tC_SerialRxData(Sender: TObject);
  Var C : Char;
begin
  While tC_Serial.ReadChar(C)>0 do Begin
    Case C of
      #2 : HS:='';
      #3 : Begin
             tC_String:=HS;
           End;
      Else If Length(HS)<100 Then HS:=HS+C;
    End;
  End;
end;
Jetzt hab ich das Problem das das Beenden des Programms ehwig dauert, obwohl ich die Schnittstelle mit:
Delphi-Quellcode:
tC_Serial.Active:=false;
schliesse.

Stecke ich mein externes Gerät aus schliesst das Programm sofort.

Gibt es eine Möglichkeit die TSerial Komponente sofort zu schliessen?

Klaus01 17. Aug 2006 11:06

Re: Tserial abschiessen
 
Das Problem ist, dass Dein Programm in der Schleife steckt
und keine Systemmeldungen verarbeitet.

Delphi-Quellcode:
procedure TSS.tC_SerialRxData(Sender: TObject);
  Var C : Char;
begin
  While tC_Serial.ReadChar(C)>0 do Begin
    Application.ProcessMessages; // <----
    Case C of
      #2 : HS:='';
      #3 : Begin
             tC_String:=HS;
           End;
      Else If Length(HS)<100 Then HS:=HS+C;
    End;
  End;
end;
Könnte vielleicht etwas Abhilfe bringen.

Grüße
Klaus

mymuschy 17. Aug 2006 11:30

Re: Tserial abschiessen
 
Hallo Klaus01,

bringt leider auch nichts! Ich hab auch schon versucht in die Schleifenbedingung eine zweite Bedingung einzubauen.
Delphi-Quellcode:
procedure TSS.tC_SerialRxData(Sender: TObject);
  Var C : Char;
begin
  While (tC_Serial.ReadChar(C)>0) and not sofort_beenden do Begin
    Case C of
      #2 : HS:='';
      #3 : Begin
             tC_String:=HS;
           End;
      Else If Length(HS)<100 Then HS:=HS+C;
    End;
  End;
end;
Die boolsche Variable "sofort_beenden" wird beim schliesen im OnClose Ereignis auf true gesetzt.

Aber leider auch ohne Erfolg...

Ist es vielleicht möglich den Empfang komplett zu unterbinden?

Klaus01 17. Aug 2006 11:53

Re: Tserial abschiessen
 
Kannst Du mal einen Breakpoint in Deinem OnClose Ereignis setzen.
Und dann mal schauen ob direkt nach der Beendigung des Programms
da hinein gesprungen wird.
Wenn es da auch schon länger dauert, dann hängt Dein Programm
vielleicht noch woanders in der Verarbeitung fest.

Wenn Du die Zeichen von der Schnittstelle gelesen hast,
mußt Du dann nich das Puffer der Schnittstelle leeren?

Das würde mit ZapRXQueue gehen.

Grüße
Klaus

mymuschy 17. Aug 2006 12:11

Re: Tserial abschiessen
 
Er spring beim schliessen sofort "rein" und erst nach ca. 10sek. "raus"

Delphi-Quellcode:
procedure TSS.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  showmessage('rein');
  tC_Serial.Active:=false;
  showmessage('raus');
end;
Zitat:

Wenn Du die Zeichen von der Schnittstelle gelesen hast,
mußt Du dann nich das Puffer der Schnittstelle leeren?
Ich bin immer davon ausgegangen das
Delphi-Quellcode:
readChar
das Zeichen aus dem Puffer schmeisst. Weil der nächste Aufruf von readChar holt sich ja auch das nächste Zeichen.


Ich habs mal so versucht:
Delphi-Quellcode:
procedure TSS.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  showmessage('rein');
  tc_serial.ZapRxQueue;
  tC_Serial.Active:=false;
  showmessage('raus');
end;
Aber leider immer noch das gleiche Spiel...

Klaus01 17. Aug 2006 12:14

Re: Tserial abschiessen
 
Delphi-Quellcode:
procedure TSS.tC_SerialRxData(Sender: TObject);
  Var C : Char;
begin
  While (tC_Serial.ReadChar(C)>0) and not sofort_beenden do Begin
    tc_serial.ZapRxQueue;  // wenn dann hier den Puffer löschen

    Case C of
      #2 : HS:='';
      #3 : Begin
             tC_String:=HS;
           End;
      Else If Length(HS)<100 Then HS:=HS+C;
    End;
  End;
end;
Nach dem Einlesen, werden die Date da noch verarbeitet?

Grüße
Klaus

Der_Unwissende 17. Aug 2006 12:19

Re: Tserial abschiessen
 
Hi,
ich bin nicht ganz sicher, ob dein Design so sauber ist.
Ich geh hier einfach mal davon aus, dass tC_SerialRxData eine Ereignisbehandlung ist, die aufgerufen wird, sobald Daten ankommen. Hier liegt dann schon eines deiner möglichen Probleme. Die Kommunikation dürfte wohl asynchron sein, dass heißt natürlich, dass du gar nicht weißt wann welches Datum ankommt. Beendest du also die Ereignisbehandlung und es kommen neue Daten an, so wird die Methode sofort wieder aufgerufen. An sich solltest du auch keine Schleife in einer solchen Behandlung setzen. Imho müsstest du hier vielmehr eine eigene Methode verwenden, in der die Schleife läuft.
Was dein Problem mit dem Schließen angeht, versuch einfach mal die Ereignisbehandlung im onClose abzuschalten (tC_SerialRxData := nil) und dann noch die Schleife abbzubrechen (mit einem Flag).


Gruß Der Unwissende

mymuschy 17. Aug 2006 12:36

Re: Tserial abschiessen
 
@Klaus01
habs mit
Delphi-Quellcode:
tc_serial.ZapRxQueue;
versucht.
Ja es wird noch korrekt empfangen. Aber das Problem ist nicht weg.

@unwissender
Des mit der eigenen Funnktion versuch ich mal.
Wobei mir nicht klar ist warum ein Flag in einer eigenen Funktion, in der eine Schleife läuft, anders ist als ein Flag in der Schleife selbst.

Das mit der
Zitat:

Ereignisbehandlung im onClose abzuschalten (tC_SerialRxData := nil)
hab ich leider nicht verstanden. Wie geht denn das?

DGL-luke 17. Aug 2006 12:52

Re: Tserial abschiessen
 
Deine Methode tC_SerialRxData wird immer dann aufgerufen, wenn Daten da sind. Wenn du also innerhalb dieser Methode sagst "stopp, will keine daten mehr", dann hat das überhaupt keine bedeutung, weil das event ja gleich wieder ausgelöst wird. du musst also das event komplett abschalten. und das geht so, wie Der_Unwissende es gesagt hat.

Der_Unwissende 17. Aug 2006 12:52

Re: Tserial abschiessen
 
Zitat:

Zitat von mymuschy
@unwissender
Des mit der eigenen Funnktion versuch ich mal.
Wobei mir nicht klar ist warum ein Flag in einer eigenen Funktion, in der eine Schleife läuft, anders ist als ein Flag in der Schleife selbst.

Oh, dann hab ich mich unklar ausgedrückt, ein Flag läuft hier genau wie in deiner Methode, es ist nur absolut unsauber die Behandlungsroutine mit einer Schleife zu verwenden!
Stell dir einfach mal vor, du bekommst 200 Byte gesendet. Da die Kommunikation asynchron ist, kommen die Bytes zufällig an. Nehmen wir mal den Fall an, es kommen erst 100 Byte, die lösen dein Event aus, es wird also einmal die Schleife gestartet und in Ruhe abgearbeitet. Währenddessen kommen nun auch irgendwann die nächsten 100 Byte an, jetzt wird wieder das Ereignis ausgelöst und hier läuft dann wieder das gleiche ab. Du reagierst also zweimal auf das Eintreffen von Daten in den Puffer.
Natürlich kannst du bei asynchroner Kommunikation nie sagen wann wieviele Daten im Puffer landen, also ist es garantiert nicht das von dir erwünschte Verhalten!
Wenn du jetzt aber eine eigene Methode verwendest, dann kannst du diese z.B. in einen Thread auslagern. Läuft dieser schon, dann liest der noch den aktuellen Puffer (und auch neu hinzukommende Zeichen) ein. Läuft der noch nicht, schmeißt du ihn an. Du kannst hier leicht abfangen, dass für jedes eintreffen von Daten ein eigener Thread gestartet wird.
Beendest du dann dein Programm, wird der Thread auch einfach nicht mehr neu gestartet (nur ein Beispiel einer möglichen Lösung). An den Flags ändert das natürlich nichts!

Zitat:

Zitat von mymuschy
Das mit der
Zitat:

Ereignisbehandlung im onClose abzuschalten (tC_SerialRxData := nil)
hab ich leider nicht verstanden. Wie geht denn das?

Nun ja, Delphi verwendet um das Eintreten eines Ereingis zu signalisieren einen Methodenzeiger. Hier kannst du die Adresse einer Methode eintragen, die der erwarteten entspricht (also die richtigen Argumente hat). Tritt das Ereignis auf, wird geschaut ob die Adresse der Methode ungleich nil ist und wenn dies der Fall ist, wird die Methode mit der gespeicherten Adresse aufgerufen.
Wenn du im Objektinspektor doppelt auf ein Ereignis klickst, so wird für dich eine fertige Methode angelegt und der Editor springt in den Körper rein.
Hinter den Kulissen wird dabei einfach die Adresse dieser Methode als Methodenzeiger für das Ereignis gespeichert. Deswegen steht im Objektinspektor dann der Name der Methode.
Wenn du nun im Programm im OnCloseQuery einfach schreibst:
Delphi-Quellcode:
tc_serial.OnRxChar := nil;
dann wird die Ereignisbehandlung abgeschaltet. Kommt jetzt ein neues Zeichen im Puffer an, gibt es keinen der benachrichtigt wird.

Sauberer und mehr OO wäre es, auf der Ereignisbehandlung einfach ein Observer-Pattern (bzw. eine Implementierung dieses Pattern) aufzusetzen. Aber das ist dann noch eine andere Sache.


Alle Zeitangaben in WEZ +1. Es ist jetzt 20:57 Uhr.
Seite 1 von 2  1 2      

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