Ereignis, das zu einer bestimmten Uhrzeit auslöst



  • ich möchte ein Event schreiben, das wie der TTimer zu einer bestimmten Uhrzeit auslöst.
    Dazu würd ich eine Klasse schreiben, die als Eigenschaft die Uhrzeit hat, wann das Ereignis auslösen soll

    Edit: 'daß' vs. 'das' korrigiert, wg. Augenkrebs 😉



  • Was hindert dich?

    Bau eine Klasse mit einem TTimer, lasse diesen sekündlich ticken und löse ein Event aus, wenn die entsprechende Zeit erreicht ist.



  • wie definiert man ein Event bzw löst es dann aus?



  • Hallo

    Ein Event ist nichts weiter als ein Funktionsaufruf. Alles was du brauchst ist ein Timer mit einem Intervall von einer Sekunde oder gar mehr und eine Membervariable von Typ TDateTime, die den gewählten Zeitpunkt speichert.
    Im Timer-Event wird nun jedesmal die Membervariable mit der aktuellen Zeit verglichen, bei Überschreitung deine eigentliche Payload-Funktion aufrufen.

    bis bald
    akari



  • ok habs mal versucht:

    class ClockTimer {
       private:
          TTime zeitpunkt;
       public:
          ClockTimer();
          void ClockTimerEvent();
    };
    
    ClockTimer::ClockTimer()
    {
       TTimer* tim = new TTimer(Application);
       tim->Enabled = true;
       tim->Interval = 1000;
       if (TimeToStr(Time()) == zeitpunkt)
          ClockTimerEvent();
    }
    //---------------------------------------------------------------------------
    void ClockTimer::ClockTimerEvent()
    {
    
    }
    

    Bei der Erzeugung des Timers im Konstruktor bin ich mir bei dem Parameter nicht sicher ob das stimmt. Ich will nun meinen Code der zu einer bestimmten Uhrzeit ausgeführt werden soll in den Clocktimer schreiben. Ich will ihn also verwenden wie den TTimer also:

    ClockTimer test;
    test.zeitpunkt = "16:00:00";
    void TForm1::myClockClockTimer(TObject *Sender)
    {
       //mein Coder der 16.00 Uhr ausgeführt werden soll
    }
    

    Evtl will ich dann auch eine Komponente daraus erstellen.



  • Hallo

    Naja, da ist aber noch einiges im Argen. Eine ungeteste Korrektur

    class ClockTimer {
       private :
          TTimer* timer;
       public:
          TDateTime zeitpunkt;
          TNotifyEvent clock_event;
          ClockTimer();
          ~ClockTimer();
          void __fastcall ClockTimerEvent(TObject* Sender);
    };
    
    ClockTimer::ClockTimer(TDateTime zp, TNotifyEvent ce = 0) :
      zeitpunkt(zp), clock_event(ce)
    {
       timer = new TTimer(0);
       timer->Enabled = (zeitpunkt > Now());
       timer->Interval = 1000;
    }
    
    ClockTimer::~ClockTimer()
    { 
      delete timer;
    }
    
    ClockTimer::ClockTimerEvent()
    {
       if (zeitpunkt <= Now())
       {
         zeitpunkt = TDateTime();
         timer->Enabled = false;
         if (clock_event)
           clock_event(0);
       }
    }
    
    // Verwendung
    void __fastcall TForm1::MeinEvent(TObject* Sender)
    {
      // Alarm!
    }
    
    ClockTimer(TDateTime(...), MeinEvent);
    

    Das ist natürlich nur das Minimale. Getter und Setter sauber einbauen, Properties, von TComponent ableiten... läßt sich noch viel verbessern

    bis bald
    akari



  • danke erst mal. Habe die Klasse geschrieben und wollt mal die Verwendung probieren:

    class TForm1 : public TForm
    {
    __published:	// Von der IDE verwaltete Komponenten
    private:	// Anwender-Deklarationen
       ClockTimer* clk;
    public:		// Anwender-Deklarationen
       __fastcall TForm1(TComponent* Owner);
       void __fastcall MeinEvent(TObject* Sender);
    };
    
    //---------------------------------------------------------------------------
    __fastcall TForm1::TForm1(TComponent* Owner)
       : TForm(Owner)
    {
       ClockTimer* clk = new ClockTimer(Time(), MeinEvent); //hier gibts eine Fehlermeldung
    }
    //---------------------------------------------------------------------------
    // Verwendung
    void __fastcall TForm1::MeinEvent(TObject* Sender)
    {
      //mach was
    }
    

    In der kommentierten Zeile gibts folgenden Fehler:

    [C++ Fehler] unitMain.cpp(18): E2285 Keine Übereinstimmung für 'ClockTimer::ClockTimer(TDateTime,void)' gefunden
    [C++ Warnung] unitMain.cpp(19): W8004 'clk' wurde ein Wert zugewiesen, der nie verwendet wird.
    

    Edit: Es wurde ja auch ein Timer definiert. Mit fehlt auch hier der Code, der im Timer abläuft.



  • akari schrieb:

    .. läßt sich noch viel verbessern

    Ich würde den Timer nicht jede Sekunde ticken lassen sondern (wenn wir mal bei deinem Vorschlag bleiben) im Konstruktor die aktuelle Systemzeit holen, anhand derer die Zeit berechnen, die vergehen muss bis die gewünschte Uhrzeit eintritt und den Timer dann genau auf die Zeit einstellen.



  • Ich würde den Timer nicht jede Sekunde ticken lassen sondern (wenn wir mal bei deinem Vorschlag bleiben) im Konstruktor die aktuelle Systemzeit holen, anhand derer die Zeit berechnen, die vergehen muss bis die gewünschte Uhrzeit eintritt und den Timer dann genau auf die Zeit einstellen.

    ok. Das dürft ich hinbekommen. Leider funktioniert das Ereignis nicht. Wie gesagt, da stimmt was an dem Code nicht.
    Mir ist da noch so einiges unklar.

    timer = new TTimer(0);
    

    Hier wird das Timerobjekt erzeugt. Welchen Parameter hat der Timer? In der Hilfe steht leider nix dazu. Wo kann ich nachschauen welche Parameter hier im Konstruktor stehen? (Im Kaiser-Buch steht leider auch nix dazu)

    timer->Enabled = (zeitpunkt > Now());
    

    Ich denke hier wird der Timer einfach nur eingeschaltet wenn die Zeit in der Zukunft liegt. Kann ich den Timer nicht einfach immer laufen lassen? Die eingegebene Zeit liegt doch immer in der Zukunft da ich ja das Ereignis täglich zu einer vom Benutzer vorgegebenen Uhrzeit auslösen will.

    if (zeitpunkt <= Now())
    

    Müsste der Code nicht im Timer stehen? Hab das mal ohne Ereignis getestet und den Code einfach in einen Timer geschrieben. Wenn die Zeit dann erreicht ist wird der Code ja immer wieder ausgeführt. Er soll aber nur einmal zu genau dieser Zeit ausgelöst werden.

    zeitpunkt = TDateTime();
    

    Diese Zeile versteh ich nicht. Hier wird der Zeit ein Typ zugewiesen. Hier fehlt doch noch die variable?

    if (clock_event)
        clock_event(0);
    

    Was passiert hier? Was bedeutet der Parameter 0?

    Hintergrund ist folgender: Ich habe ein Programm bei dem der Benutzer über einen TDatePicker eine Uhrzeit (hh:mm) eingeben kann. Zu dieser Zeit soll eine Aktion ausgeführt werden (in diesem Fall soll eine Pumpe eingeschaltet werden). Da zu anderen Zeiten noch weitere Aktionen stattdfinden sollen, wollte ich das über ein Ereignis machen.



  • Hallo

    rudpower schrieb:

    timer = new TTimer(0);
    

    Hier wird das Timerobjekt erzeugt. Welchen Parameter hat der Timer? In der Hilfe steht leider nix dazu. Wo kann ich nachschauen welche Parameter hier im Konstruktor stehen? (Im Kaiser-Buch steht leider auch nix dazu)

    TTimer ist eine normale VCL-Komponente, wie z.B. bei TEdit kannst du einen Owner an den Konstruktor übergeben. Notwendig ist es aber nicht, wenn du selber das korrekte Löschen der Timer-Instanz garantierst (mit dem Destruktor)

    timer->Enabled = (zeitpunkt > Now());
    

    Ich denke hier wird der Timer einfach nur eingeschaltet wenn die Zeit in der Zukunft liegt. Kann ich den Timer nicht einfach immer laufen lassen? Die eingegebene Zeit liegt doch immer in der Zukunft da ich ja das Ereignis täglich zu einer vom Benutzer vorgegebenen Uhrzeit auslösen will.

    Das ist eine einfache Sicherung gegen "falsche" Werte. Kannst du auch rausnehmen.
    Der Timer darf aber nicht ständig laufen. Sobald der Zeitpunkt erreicht ist, muß der Timer abgeschaltet werden.

    if (zeitpunkt <= Now())
    

    Müsste der Code nicht im Timer stehen? Hab das mal ohne Ereignis getestet und den Code einfach in einen Timer geschrieben. Wenn die Zeit dann erreicht ist wird der Code ja immer wieder ausgeführt. Er soll aber nur einmal zu genau dieser Zeit ausgelöst werden.

    Genau. Wenn der gewählte Zeitpunkt einmal erreicht ist, muß der Timer abgeschaltet werden. Damit kommen wir gleich zur nächsten Frage...

    zeitpunkt = TDateTime();
    

    Diese Zeile versteh ich nicht. Hier wird der Zeit ein Typ zugewiesen. Hier fehlt doch noch die variable?

    Diese Zeile ruft den Konstruktor von TDateTime auf, erstellt eine einfache initialisierte TDateTime-Instanz und kopiert diesen Wert in die Variable zeitpunkt. Der Effekt ist, das nun später abgefragt werden kann, ob in TDateTime ein sinnvoller vom Benutzer gewählter Zeitpunkt steht.

    if (clock_event)
        clock_event(0);
    

    Was passiert hier? Was bedeutet der Parameter 0?

    clock_event ist ein Funktionszeiger, ähnlich einem VCL-Event. Du übergibst mit dieser Variable der ClockTimer-Klasse eine aufzurufende Funktion. Die beiden Zeilen prüfen zuerst, ob eine Funktions zugewiesen wurde (einfach ein Vergleich ob der Zeiger clock_event ungleich 0 ist), und wenn ja dann wird diese Funktion aufgerufen. Die 0 ist hier einfach der Parameter Sender, der nach VCL-Konvention im TNotifyEvent definiert ist.

    Zu deiner Fehlermeldung : Die Deklaration des Konstruktors ist nicht korrekt, passt nicht zur korrekten Implementation. Das hättest du aber auch selber erkennen können 🙄

    class ClockTimer {
      ...
          ClockTimer::ClockTimer(TDateTime zp, TNotifyEvent ce = 0); // Mit Parametern!
      ...
    };
    
    ClockTimer::ClockTimer(TDateTime zp, TNotifyEvent ce) : // Ohne Defaultvalue!
    

    /Edit
    Ich merke gerade das noch ein paar Details fehlen

    {
       timer = new TTimer(0);
       timer->Enabled = (zeitpunkt > Now());
       timer->Interval = 1000;
       timer->OnTimer = ClockTimerEvent; // Eventfunktion muß zugewiesen werden!
    }
    ...
    ClockTimer::ClockTimerEvent(TObject* Sender) // Parameter muß hinzugefügt werden
    

    bis bald
    akari



  • vielen Dank. Habs korrigiert. Stimmt das hätt ich selbst sehen müssen 🙂
    Nu gibts immer noch den Fehler

    [C++ Fehler] unitMain.cpp(18): E2285 Keine Übereinstimmung für 'ClockTimer::ClockTimer(TDateTime,void)' gefunden
    

    in der selben Zeile. ich denke, dass ich als Parameter nicht einfach so das "MeinEvent" reinschreiben kann. Hier mein Code:

    class ClockTimer {
       private :
          TTimer* timer;
       public:
          TDateTime zeitpunkt;
          TNotifyEvent clock_event;
          ClockTimer(TDateTime zp, TNotifyEvent ce);
          ~ClockTimer();
          void __fastcall ClockTimerEvent(TObject* Sender);
    };
    //---------------------------------------------------------------------------
    ClockTimer::ClockTimer(TDateTime zp, TNotifyEvent ce = 0)
       : zeitpunkt(zp), clock_event(ce)
    {
       timer = new TTimer(0);
       timer->Enabled = (zeitpunkt > Now());
       timer->Interval = 1000;
       timer->OnTimer = ClockTimerEvent;
    }
    //---------------------------------------------------------------------------
    ClockTimer::~ClockTimer()
    {
      delete timer;
    }
    //---------------------------------------------------------------------------
    void __fastcall ClockTimer::ClockTimerEvent()
    {
       if (zeitpunkt <= Now()) {
          zeitpunkt = TDateTime();
          timer->Enabled = false;
          if (clock_event)
             clock_event(0);
       }
    }
    

    Form:

    class TForm1 : public TForm
    {
    __published:	// Von der IDE verwaltete Komponenten
    
    private:	// Anwender-Deklarationen
       ClockTimer* clk;
    public:		// Anwender-Deklarationen
       __fastcall TForm1(TComponent* Owner);
       void __fastcall MeinEvent(TObject* Sender);
    };
    //---------------------------------------------------------------------------
    __fastcall TForm1::TForm1(TComponent* Owner)
       : TForm(Owner)
    {
       TDateTime myTime = StrToTime("12:00:00");
       clk = new ClockTimer(myTime, MeinEvent);
    }
    //---------------------------------------------------------------------------
    void __fastcall TForm1::MeinEvent(TObject* Sender)
    {
      ShowMessage("Event ausgelöst");
    }
    


  • Hallo

    Du must die Adresse der Funktion übergeben...

    clk = new ClockTimer(myTime, &MeinEvent);
    

    bis bald
    akari



  • ok, er hat es fehlerfrei kompiliert. Nun hab ich mal eine Zeit eingegeben. Leider kommt die Message, die im Event steht nicht. Da scheint noch irgendwo was nicht zu stimmen.



  • Hallo

    Dann benutz bitte den Debugger und versuch mal, den Quellcode zu verstehen.
    Vor allem ein Breakpoint auf die Bedingung in ClockTimerEvent() dürfte aufschlußreich sein, wenn du mal dir anschaust was für Werte in zeitpunkt und Now() verglichen werden.

    bis bald
    akari



  • habs mal versucht. Setz ich den Breakpoint in das Event und kompiliere öffnet sich das Fenster und nix passiert. Auch zu dem gesetzten Zeitpunkt nicht. Also wurde das Ereignis nicht ausgelöst. Hab dann mal den Breakpoint in die Zeile

    timer->Enabled = zeitpunkt > Now();
    

    gesetzt und gesehen, dass die Eigenschaft Enabled des Timers auf false bleibt. Hab dann einfach mal mit

    timer->Enabled = true;
    

    auf true geändert und nochmal den Breakpoint in das Event gesetzt. Dann wurde es ausgelöst und ich konnte debuggen. Da man mit Alt+F5 nicht auf Now() setzen kann hab ich mir noch eine Variable aktZeit angelegt und Now() zugewiesen. Dabei ist mir aufgefallen, dass der Wert 40695.558313 beträgt. Meine vorgegebene Zeit aber ein 0.57....-Wert ist. Soweit ich das in Erinnerung hab sind das ja doubles und die Zahl vor dem Komma sind die Anzahl der Tage. Die brauch ich ja nicht. Deshalb wird Enabled des Timers immer false. Dann kann es ja nicht gehen :)Ich hoff ich lieg damit richtig.
    Kann ich die aktuelle Zeit auch ohne Datum speichern, also mit 0 vor dem Komma?

    Edit: grad gesehen das Time() nur die Zeit nimmt.



  • ich kann es kaum glauben aber es funktioniert 🙂 Nun versuch ich mal die Zeit als property zu deklarieren. Am schönsten wär natürlich wenn ich daraus dann eine Komponente mach, da ich mehrere Zeiten hab an der ein Ereignis auslösen soll.



  • Ich würde den Timer nicht jede Sekunde ticken lassen sondern (wenn wir mal bei deinem Vorschlag bleiben) im Konstruktor die aktuelle Systemzeit holen, anhand derer die Zeit berechnen, die vergehen muss bis die gewünschte Uhrzeit eintritt und den Timer dann genau auf die Zeit einstellen.

    habe das mal so umgesetzt. Kann ich das so machen? Außerdem hab ich mal für die Zeit eine property erstellt. Da die Zeit im Konstruktor festgelegt wird kann ich ja zur Laufzeit die Zeit gar nicht ändern. Ich möchte ja, dass der Benutzer die Zeit zur Laufzeit ändern kann. Ich stell mir das so vor, dass nach einem Buttonklick die über ein Editfeld eingegebene Zeit verwendet wird. Wie mach ich das am besten?

    class ClockTimer {
       private :
          TTimer* timer;
          TDateTime zeitpunkt;
          TNotifyEvent clock_event;
          void SetZeit(TDateTime zeitpunkt) {this->zeitpunkt = zeitpunkt;}
       public:
          ClockTimer(TDateTime zeitpunkt, TNotifyEvent ce);
          ~ClockTimer();
          void __fastcall ClockTimerEvent(TObject* Sender);
          __property TDateTime zeit = {read=zeitpunkt, write=SetZeit};
    };
    //---------------------------------------------------------------------------
    
    ClockTimer::ClockTimer(TDateTime zp, TNotifyEvent ce=0) :
       timer(new TTimer(0)),
       zeitpunkt(zp),
       clock_event(ce)
    {
       unsigned short usH, usM, usS, usMS;
       TDateTime aktZeit = Time();
       timer->Enabled = zeitpunkt > aktZeit;
       TDateTime diff =  zeitpunkt - aktZeit;
       diff.DecodeTime(&usH, &usM, &usS, &usMS);
       timer->Interval = 1000 * (usS + usM * 60.0 + usH * 3600.0 + int(diff) * 86400.0 + usMS/1000.0);
       timer->OnTimer = ClockTimerEvent;
    }
    //---------------------------------------------------------------------------
    ClockTimer::~ClockTimer()
    {
      delete timer;
    }
    //---------------------------------------------------------------------------
    void __fastcall ClockTimer::ClockTimerEvent(TObject* Sender)
    {
       TDateTime aktZeit = Time();
       if (zeitpunkt <= aktZeit) {
          zeitpunkt = TDateTime();
          timer->Enabled = false;
          if (clock_event)
             clock_event(0);
       }
    }
    

    Der Button müsste ja die Eigenschaft "zeit" auf den gewünschten Zeitpunkt setzen. Da wurde aber der Konstruktor ClockTimer im Konstruktor der Form schon aufgerufen. Oder muss ich für das Event noch eine property schreiben?

    Edit: Man könnte ja auch wie beim TTimer eine Eigenschaft Enabled anlegen. Wenn Enabled wahr ist könnte der ClockTimer laufen. Wäre das eine Möglichkeit? Wie setz ich das am besten um?



  • wenn ich also im Konstruktor der Form das ClockTimer-Objekt mit dem Zeitpunkt im Konstruktor erzeuge wird das Event wie gewünscht zu dem Zeitpunkt ausgelöst. Wenn ich den Konstruktor ohne zeit-Parameter aufrufe (wie im Code unten) und die Eigenschaft zeit setze funktioniert es nicht.

    __fastcall TForm1::TForm1(TComponent* Owner)
       : TForm(Owner)
    {
       TDateTime myTime = StrToTime("12:00:00");
       clk = new ClockTimer(MeinEvent);
       clk->zeit = myTime;
    }
    //---------------------------------------------------------------------------
    void __fastcall TForm1::MeinEvent(TObject* Sender)
    {
      ShowMessage("Event ausgelöst");
    }
    


  • Setzt du im Konstruktor deiner Klasse den Timer auf Enabled = true? Oder steht da immernoch die Variante mit "zeitpunkt > aktZeit" drin? Denn das wäre immer false und führt dazu das nix geht.

    Du könntest alles was im Konstruktor steht in eine Funktion machen und die dann nach dem Setzen der Zeit aufrufen.



  • Oder steht da immernoch die Variante mit "zeitpunkt > aktZeit" drin?

    Stimmt, da stand das immer noch drin. Da brauch im mich nicht wundern. Hab nun eine Methode StartClockTimer geschrieben und nu funktionierts:

    __fastcall TForm1::TForm1(TComponent* Owner)
       : TForm(Owner)
    {
       clk = new ClockTimer(&MeinEvent);
    }
    //---------------------------------------------------------------------------
    void __fastcall TForm1::MeinEvent(TObject* Sender)
    {
      ShowMessage("Event ausgelöst: " + TimeToStr(Time()));
    }
    //---------------------------------------------------------------------------
    void __fastcall TForm1::LMDButton1Click(TObject *Sender)
    {
       TDateTime myTime = StrToTime(txtEingabeZeit->Text);
       clk->Zeit = myTime; // hier über property
       clk->StartClockTimer();
    }
    

    Kann man das so machen oder gibts Verbesserungsbedarf?


Anmelden zum Antworten