Zyklisches Ereignis in einem Modul anlegen

Hallo,

ich komme grad nicht weiter. Ich möchte innerhalb eines Moduls ein zyklisches Ereignis anlegen das jede Nacht um 0 Uhr eine Variable zurücksetzt.
Mein bisheriger Code dazu sieht wie folgt aus:

   
$eventID = IPS_CreateEvent(1);
IPS_SetParent($eventID, $this->GetIDForIdent('RAIN_COUNTER_TODAY'));
IPS_SetEventCyclicTimeFrom($eventID, 0, 0, 0);
IPS_SetEventActive($eventID, true); 

Das Ereignis wird damit auch angelegt, aber wie und wo gebe ich dem Ereignis jetzt mit das eine Variable zurückgesetzt werden soll? Über die WebConsole kann ich das ja unter „Aktion“ einstellen. Muss ich bei dem Ereignis jetzt mit IPS_SetEventScript eine Funktion innerhalb des Moduls rufen die die Variable zurücksetzt oder gibt es noch einen anderen Weg?

Völlig falscher Ansatz, da Module keine zyklischen Ereignisse nutzen, sondern Timer.
Und bitte im richtigen Bereich schreiben.
Dort wurde diese Frage auch schon häufiger gestellt.
Ich verschiebe das Thema.
Michael

Magst Du mir vielleicht nochmal einen kleinen Schubs in die richtige Richtung geben? Evtl. den passenden Thread den Du meinst? Ich habe bislang den Thread hier gefunden: https://www.symcon.de/forum/threads/33607-RegisterTimer-und-t%C3%A4gliches-Update . Dort wird auch IPS_SetEventCyclicTimeFrom erwähnt. Wenn ich nun RegisterTimer nutze dann wird dieser doch mit dem angegebenen Intervall permanent ausgeführt oder nicht? Ich brauche ja nur eine Ausführung am Tag (um 0 Uhr), quasi wie ein Cronjob.

RegisterTimer

Schau Dir mal z.B. den [i]zyklischen Timer im Astronomiemodul[/i] an.

Hier erklärt:
Ereignis in Modul
Michael

Ja das ist doch der Sinn der Sache das bei einem zyklischen Timer dieser in einem vorgegebenen Intervall erneut ausgeführt wird oder nicht? Du willst doch schließlich jeden Tag was machen, also ist das doch ein festes Intervall. Um Null würde ich das nicht unbedingt ausführen, kann sein das da auch noch andere Prozesse laufen, wenn nicht unbedingt notwendig, dann eher ein paar Sekunden nach 0 Uhr, das nicht zufällig Prozesse, die um 0 Uhr greifen, alle zeitgleich ausgeführt werden.

Nicht verwirren lassen.
Es gab zum Beginn der PHP-Module kein RegisterTimer, darum wurden dort wirklich noch normale zyklische Ereignisse benutzt.
Ist jetzt aber obsulent und somit bitte RegisterTimer nutzen.
Einfach im Applychanges und in der eigentlichen Methode welche durch den Timer startet, das Intervall zur Zielzeit ausrechnen und mit SetTimerInterval bei setzen.
Michael

@Nall-chan, Fonzo
Danke für den Links.

Also quasi so umsetzen wie hier aus dem Astro Modul?


protected function SetCyclicTimerInterval()
{
  $Now = new DateTime();
  $Target = new DateTime();
  $Target->modify('+1 day');
  $Target->setTime(0, 0, 5);
  $Diff = $Target->getTimestamp() - $Now->getTimestamp();
  $Interval = $Diff * 1000;
  $this->SetTimerInterval("AstroTimerUpdate", $Interval);
}

In dem Fall wird das um 5 Minuten nach 0 Uhr ausgeführt, ansonsten musst Du das anpassen.

Außerdem must Du initial den Timer im Create auf 0 setzten siehe Astronomietimer und dann in [i]ApplyChanges das eigentliche Intervall zuweisen[/i] bzw. die Methode aufrufen, die das Intervall setzt…

Ok alles klar. Wie es jetzt umgesetzt werden muss weiß ich dann. Grundsätzlich nochmal zum Verständnis, der Timer sollte genutzt werden weil er intern pro Instanz läuft und von außen nicht verändert werden kann richtig? Ein Ereignis würde ja in der Konsole angezeigt werden und wäre somit editierbar bzw. ließe sich löschen.

Ja es sollte in einem Modul immer ein interner Timer benutzt werden um etwas auszuführen bzw. die MessageSink, ein Ereigniss ist für einen User, aber nicht für eine Modulinstanz.

Genau man will ja nicht das etwas gelöscht wird, das schließt ja aber nicht aus, dass der User das editieren kann. Wenn Du willst das dies vom User verändert werden kann, must Du halt eine Property für das Intervall setzten, dass vom User verändert werden kann und das Du dann im Modul abfragst. Wenn es an dem Intervall nichts zu verstellen gibt, dann brauchst Du auch keine Property.

OK, aber wäre so etwas nicht über einen Cronjob einfacher zu realisieren bzw. warum geht Symcon hier den Weg über Timer? Ein Cronjob wäre ein Einzeiler, bei einem Timer muss ich jedesmal erst den passenden Intervall berechnen.

Chronjob sind eher Jobs für einen Cron-Daemon der ja idr auf OS Ebene läuft.
Du bewegst dich hier aber ausschließlich innerhalb des SDK von Symcon.
Und das hat seine eigene interne Timer/Job Verwaltung und agiert OS unabhängig.
Michael

Eine kurze Rückmeldung von mir, der Timer läuft und arbeitet „unsichtbar“ im Modul. Geht dies mit Ereignissen (z.B bei einer Variablenaktualisierung) auch? Die betreffen in meinem Fall auch nur Abläufe innerhalb des Moduls und müssten in der Webconsole eigentlich nicht angezeigt werden weil sie dadurch ja vom Benutzer wieder veränderbar / löschbar wären. Mit IPS_SetHidden kann ich die zwar von der Visualisierung ausschließen aber sie werden eben noch immer in der Console angezeigt.

Ja, das nennt sich MessagSink als Methode zum verarbeiten der Nachricht.
Und auf die Nachricht registrierte das Modul sich auf ein Update der Variable (VM_UPDATE) mit RegisterMessage.
Michael

Also ich muss jetzt nochmal nachfragen, denn so wie erhofft funktioniert der Timer nicht.

Ich registriere einen neuen Timer im Create:


public function Create() {
  parent::Create();
  $this->RegisterTimer('ResetTimer', 0, 'WSE_ResetValues($_IPS["TARGET"]);');            
}

Im ApplyChanges setzte ich den Timer Intervall:


public function ApplyChanges() {
  parent::ApplyChanges();
  $this->SetResetTimerInterval();             
}

public function SetResetTimerInterval() {
  $now = new DateTime();
  $target = new DateTime();
  $target->modify('+1 day');
  $target->setTime(0, 1, 0);
  $diff = $target->getTimestamp() - $now->getTimestamp();
  $interval = $diff * 1000;
  $this->SetTimerInterval('ResetTimer', $interval);
}

Der Timer wird wie erwartet um 00:01 Uhr aufgerufen, aber danach in viel kürzeren Abständen.
Hier mal der Auszug aus IPS_GetTimer()


array(11) {
  ["TimerID"]=>
  int(75)
  ["InstanceID"]=>
  int(18916)
  ["Name"]=>
  string(10) "ResetTimer"
  ["Interval"]=>
  int(19682000)
  ["LastRun"]=>
  int(1549274224)
  ["NextRun"]=>
  int(1549293906)
  ["Running"]=>
  bool(false)
  ["ProgressMin"]=>
  int(0)
  ["ProgressMax"]=>
  int(0)
  ["ProgressPosition"]=>
  int(0)
  ["RunOnce"]=>
  bool(false)
}

Der Intervall und NextRun passen dort schon garnicht mehr. Was mache ich falsch?

Der Timer gibt ein Zeitintervall an. So wie das aussieht, nimmst du die Dauer bis zum nächsten Mal 0:01 Uhr. Danach wird wieder das gleiche Intervall genommen, nicht unbedingt das nächste mal um 0:01. Wenn du beispielsweise ApplyChanges um 23:01 ausführst, ist das Intervall eine Stunde lang und würde nach 0:01 das nächste mal um 1:01 auslösen. Wenn du immer um 0:01 auslösen möchtest, dann müsstest du beim Auslösen des Timers die Dauer bis zum nächsten Mal 0:01 berechnen und das Intervall neu setzen. In deinem Fall könntest du also in ResetValues einfach nochmal die Funktion SetResetTimerInterval aufrufen.