Autor Thema: Timerverwaltung - lib für parallele Funktionsaufrufe  (Gelesen 3509 mal)

Offline Beta-User

  • Developer
  • Hero Member
  • ****
  • Beiträge: 15320
  • "Developer"?!? Meistens doch eher "User"
Hallo zusammen,

bin jüngst mal wieder über das "Problem" gestolpert, sowas wie eine Timerverwaltung in ein Modul einbauen zu "müssen" bzw. zu wollen.
Konkret geht es um RHASSPY (https://forum.fhem.de/index.php/topic,119447.msg1153802.html#msg1153802) und allgemein gesprochen sieht das Problem so aus: Es kann von verschiedenen externen Stellen eine Art "Dialog" mit dem Modul aufgemacht werden. Nach einer gewissen Zeit sollen (sollen=eigene Festlegung) diese Dialoge dann aber auch wieder geschlossen werden.
Ergo muss für jeden möglichen Dialog bzw. Dialogpartner ein separater Timer angelegt werden, der  bei Fortführung des Dialogs dann auch gezielt wieder gelöscht oder verlängert werden kann, und es "muss" möglich sein, immer dieselbe  zentrale Timeout-Routine anzusprechen, die den betreffenden Timer dann auch zielgerichtet wieder löscht, wenn er nicht (mehr) gebraucht wird. Das Problem dabei ist, dass man nicht wissen kann, wie viele der Timer ggf. am Ende parallel laufen können - immer nur einer wäre einfach...

Um sowas zu realisieren, steht an sich InternalTimer zur Verfügung, ausweichen könnte man auch auf (temporäre) at, beide Varianten muss man aber entweder im Modul verwalten (um die Timer v.a. bei Löschen der Device-Instanz "abschießen" zu können)  oder vom Aufruf her so gestalten, dass sie FHEM nicht abschießen, wenn es die aufgerufene Funktion nicht mehr gibt (also in eine Art eval/AnalyzePerlCommand einpacken).

Im Moment habe ich das nach der "Twilight-" (bzw. "WeekdayTimer"-) "Methode" gelöst und , der Aufruf der Funktion erfolgt über InternalTimer, aber statt wie sonst bei InternalTimer üblich "$hash" zu übergeben, wird ein Hash übergeben, der ua. dann auch $hash enthält, um überhaupt wieder festzustellen, wer die Funktion eigentlich aufgerufen hat und einen "Identifier", über den sich feststellen läßt, zu welchem Dialogpartner der Timer gehört; der letzte Teil wird im Hash der Modul-Instanz verwaltet, solange das relevant ist. In beiden Modulen sind dafür wrapper enthalten, die dann intern InternalTimer bzw. RemoveInternalTimer aufrufen und die Verwaltungsdaten in den Device-Hash schreiben.

Da mir das Thema jetzt zum wiederholten Mal über den Weg gelaufen ist, und ich insbesondere bisher auch keinen "Dreh" gefunden habe, um die "Timer"-lib von Sidey für diese Art Problemstellung :
- übersehe ich mal wieder eine bereits vorhandene Funktionalität?
- gibt es allgemeineren Bedarf dafür? Im Moment ist diese Logik in Twilight und WeekdayTimer so drin - ggf. könnte es also Sinn machen, den Code auszulagern und es entweder in die "Timer"-lib einzubauen oder gesondert. Da die Verwaltung in der Timer-lib aber irgendwie anders abläuft, klingt das für mich eher nach gesondert, aber vermutlich übersehe ich da was...

Für zielführende Hinweise wäre ich euch verbunden, ansonsten bleibt das jetzt erst mal so, wie es ist...
« Letzte Änderung: 21 Mai 2021, 10:03:41 von Beta-User »
Server: HP-T620@Debian 10, aktuelles FHEM + ConfigDB | CUL_HM (VCCU) | MQTT2: MiLight@ESP-GW, BT@OpenMQTTGw | MySensors: seriell, v.a. 2.3.1@RS485 | ZWave | ZigBee@deCONZ | SIGNALduino | MapleCUN | RHASSPY
svn:MySensors, Weekday-&RandomTimer, Twilight,  AttrTemplate {u.a. mqtt2, mysensors, zwave}

Offline rudolfkoenig

  • Administrator
  • Hero Member
  • *****
  • Beiträge: 24396
Ich verstehe noch nicht das Problem, was hier geloest werden soll.

Offline CoolTux

  • Developer
  • Hero Member
  • ****
  • Beiträge: 27000
Schau Dir mal die Utils.pm von HTTPMOD an

https://svn.fhem.de/trac/browser/trunk/fhem/lib/FHEM/HTTPMOD/Utils.pm

Da gibt es einige Hilfsfunktionen bezüglich Timer.
Du musst nicht wissen wie es geht! Du musst nur wissen wo es steht, wie es geht.
Support me to buy new test hardware for development: https://paypal.me/pools/c/8gULisr9BT
My FHEM Git: https://git.cooltux.net/FHEM/
Mein Dokuwiki:
https://www.cooltux.net

Offline Beta-User

  • Developer
  • Hero Member
  • ****
  • Beiträge: 15320
  • "Developer"?!? Meistens doch eher "User"
Schau Dir mal die Utils.pm von HTTPMOD an

https://svn.fhem.de/trac/browser/trunk/fhem/lib/FHEM/HTTPMOD/Utils.pm

Da gibt es einige Hilfsfunktionen bezüglich Timer.
Das geht schon mal in die richtige Richtung, das "Problem" ist da nur, dass die Namen der Timer starr sind ("update"). Würde statt dieses starren Namens eine Option bestehen, einen eigenen Namen zu übergeben, könnte man das auch für RHASSPY wohl mit diesen Funktionen lösen. (Für WeekdayTimer und Twilight ginge es evtl. auch so schon).

(OT: Fehlt in HTTPMOD nicht eine RenameFn? MAn. müsste es Probleme geben, den $hash der ursprünglichen Instanz zu ermitteln, wenn GetUpdate nach Namensänderung der zugehörigen Instanz aufgerufen wird. Ich habe das aber nicht vertieft, vermutlich übersehe ich was.
Die Routinen in RHASSPY umgehen das Problem, indem gleich in das übergebene Argument auch der Instanz-Hash mit übergeben wird.)

Ich verstehe noch nicht das Problem, was hier geloest werden soll.
Bitte entschuldige, wenn ich das Problem bisher nicht hinreichend klar beschrieben habe.

Folgende Situation:
Wir haben uU. (mind.) zwei "Mikrofone", die je unabhängig voneinander einen Dialog zu einem externen Dienst aufmachen. Das kann mehr oder weniger gleichzeitig geschehen. Der Dienst vergibt dann jeweils eine eigene "SessionId", analysiert die jeweiligen Daten und schickt dann die aufbereiteten Daten in einem JSON-Blob an einen MQTT-Server, auf den dann wieder andere Dienste lauschen, u.A. eben FHEM mit Hilfe des Moduls RHASSPY. Die SessionId gilt nur für den Zeitraum zwischen Start eines Dialogs und dessen Beendigung, der Dialog selbst kann aber von allen Teilnehmern am MQTT-Verkehr (eine zeitlang) offen gehalten werden.
Ergo kann es in unserem Beispiel mit den zwei "Mikrofonen" dazu kommen, dass eine RHASSPY-Instanz parallel mit zwei SessionId's umgehen muss, um z.B. von beiden Mikrofonen zu erfahren, welche genaue Anweisung geben werden soll (weil mehrere passen, aber mit einiger Wahrscheinlichkeit nur eine ausgeführt werden soll, oder eine Anweisung z.B. nur ausgeführt werden soll, wenn das ausdrücklich bestätigt wird).

Kommt dann aber weder eine Bestätigung noch eine Auswahl, muss "jemand" irgendwann den Dialog wieder zu machen (und ggf. die Grundeinstellungen für Dialoge mit diesem "Mikrofon" wieder herstellen). Im Moment wird das dadurch erledigt, dass eben für jeden Dialog ein eigener InternalTimer mit eigener "Kennung" angelegt wird.

Die Alternative, auf die ich jetzt wärend des Nachdenkens über das Thema gekommen bin, wäre ggf. die, im Modul ein Array mit den SessionId's (und den zugehörigen Ablaufdaten) vorzuhalten, und dann eben anhand der Ablaufdaten beim Aufruf der timeout-Funktion zu checken, welche SessionId's bereits abgelaufen sind bzw. wann dann der nächste Aufruf erfolgen soll (ähnlich structure_asyncQueue), falls noch was aussteht.

Hätte den Vorteil, dass das zentrale Timer-Array kleiner gehalten wird, aber den Nachteil, dass man beim Setzen eines neuen Timers im Modul dann aufpassen muss, dass man keinen bereits laufenden (mit früherem Endezeitpunkt) löscht. Hat halt alles seine Vor- und Nachteile...

Hier jedenfalls mal der runtergestrippte Code von RHASSPY, vielleicht trägt das zur weiteren Klärung bei (der ist für sich genommen so nicht lauffähig).

# Device löschen
sub Undefine {
    my $hash = shift // return;

    deleteAllRegisteredInternalTimer($hash);
    RemoveInternalTimer($hash);

    return;
}

sub RHASSPY_DialogTimeout {
    my $fnHash = shift // return;
    my $hash = $fnHash->{HASH} // $fnHash;
    return if (!defined($hash));

    my $identiy = $fnHash->{MODIFIER};

    my $data     = shift // $hash->{helper}{'.delayed'}->{$identiy};
    delete $hash->{helper}{'.delayed'}{$identiy};
    deleteSingleRegisteredInternalTimer($identiy, $hash);

    my $siteId = $data->{siteId};
    my $toDisable = defined $data{'.ENABLED'} ? $data->{'.ENABLED'} : [qw(ConfirmAction CancelAction)];

    my $response = $hash->{helper}{lng}->{responses}->{DefaultConfirmationTimeout};
    respond ($hash, $data->{requestType}, $data->{sessionId}, $siteId, $response);
    configure_DialogManager($hash, $siteId, $toDisable, 'false');

    return;
}

sub setDialogTimeout {
    my $hash     = shift // return;
    my $data     = shift // return; # $hash->{helper}{'.delayed'};
    my $timeout  = shift;
    my $response = shift;
    my $toEnable = shift // [qw(ConfirmAction CancelAction)];

    my $siteId = $data->{siteId};
    $data->{'.ENABLED'} = $toEnable;
    my $identiy = qq($data->{sessionId});

    $response = $hash->{helper}{lng}->{responses}->{DefaultConfirmationReceived} if $response eq 'default';
    $hash->{helper}{'.delayed'}{$identiy} = $data;

    resetRegisteredInternalTimer( $identiy, time + $timeout, \&RHASSPY_DialogTimeout, $hash, 0);
    #InternalTimer(time + $timeout, \&RHASSPY_DialogTimeout, $hash, 0);

#[...]
    return; # $toTrigger;
}


# Eingehende "GetNumeric" Intents bearbeiten
sub handleIntentGetNumeric {
    my $hash = shift // return;
    my $data = shift // return;
    my $value;

    Log3($hash->{NAME}, 5, "handleIntentGetNumeric called");

    # Mindestens Type oder Device muss existieren
    return respond ($hash, $data->{requestType}, $data->{sessionId}, $data->{siteId}, getResponse($hash, 'DefaultError')) if !exists $data->{Type} && !exists $data->{Device};

    my $type = $data->{Type};
    my $subType = $data->{subType} // $type;
    my $room = getRoomName($hash, $data);

    # Get suitable device
    my $device = exists $data->{Device}
        ? getDeviceByName($hash, $room, $data->{Device})
        : getDeviceByIntentAndType($hash, $room, 'GetNumeric', $type)
        // return respond($hash, $data->{requestType}, $data->{sessionId}, $data->{siteId}, getResponse($hash, 'NoDeviceFound'));

    #more than one device
    if (ref $device eq 'ARRAY') {
        #until now: only extended test code
        my $first = $device->[0];
        my $response = $device->[1];
        my $all = $device->[2];
        my $choice = $device->[3];
        my $toActivate = $choice eq 'RequestChoiceDevice' ? [qw(ChoiceDevice CancelAction)] : [qw(ChoiceRoom CancelAction)];
        $device = $first;
        Log3($hash->{NAME}, 5, "More than one device possible, response is $response, first is $first, all are $all, type is $choice");
        return setDialogTimeout($hash, $data, 20, $response, $toActivate);
    }

#[...]

    # Antwort senden
    return; # respond ($hash, $data->{requestType}, $data->{sessionId}, $data->{siteId}, $response);
}


sub handleIntentCancelAction {
    my $hash = shift // return;
    my $data = shift // return;

    Log3($hash->{NAME}, 5, 'handleIntentCancelAction called');

    my $toDisable = defined $data->{customData} && defined $data->{customData}->{'.ENABLED'} ? $data->{customData}->{'.ENABLED'} : [qw(ConfirmAction CancelAction)];
   
    my $response = $hash->{helper}{lng}->{responses}->{ 'SilentCancelConfirmation' };

    return respond ($hash, $data->{requestType}, $data->{sessionId}, $data->{siteId}, $response) if !defined $data->{customData};

    #might lead to problems, if there's more than one timeout running...
    #RemoveInternalTimer( $hash, \&RHASSPY_DialogTimeout );
    my $identiy = qq($data->{sessionId});
    deleteSingleRegisteredInternalTimer($identiy, $hash);
    $response = $hash->{helper}{lng}->{responses}->{ 'DefaultCancelConfirmation' };
    configure_DialogManager($hash, $data->{siteId}, $toDisable, 'false');

    return $hash->{NAME};
}


sub handleIntentConfirmAction {
    my $hash = shift // return;
    my $data = shift // return;

    Log3($hash->{NAME}, 5, 'handleIntentConfirmAction called');

    #cancellation case
    #return RHASSPY_DialogTimeout($hash, 1, $data) if $data->{Mode} ne 'OK';
    return handleIntentCancelAction($hash, $data) if $data->{Mode} ne 'OK';
       
    #confirmed case
    my $identiy = qq($data->{sessionId});
    my $data_saved = $hash->{helper}{'.delayed'}->{$identiy};
    delete $hash->{helper}{'.delayed'}{$identiy};
    deleteSingleRegisteredInternalTimer($identiy, $hash);
   
    my $data_old = $data_saved;

    my $toDisable = defined $data_old && defined $data_old->{'.ENABLED'} ? $data_old->{'.ENABLED'} : [qw(ConfirmAction CancelAction)];
    configure_DialogManager($hash, $data->{siteId}, $toDisable, 'false');

    return respond( $hash, $data->{requestType}, $data->{sessionId}, $data->{siteId}, getResponse( $hash, 'DefaultConfirmationNoOutstanding' ) ) if ! defined $data_old;

    $data_old->{siteId} = $data->{siteId};
    $data_old->{sessionId} = $data->{sessionId};
    $data_old->{requestType} = $data->{requestType};
    $data_old->{Confirmation} = 1;

    my $intent = $data_old->{intent};
    my $device = $hash->{NAME};

    # Passenden Intent-Handler aufrufen
    if (ref $dispatchFns->{$intent} eq 'CODE') {
        $device = $dispatchFns->{$intent}->($hash, $data_old);
    }

    return $device;
}


sub handleIntentChoiceDevice {
    my $hash = shift // return;
    my $data = shift // return;

    Log3($hash->{NAME}, 5, 'handleIntentChoiceDevice called');

    #my $data_old = $data->{customData};
    my $identiy = qq($data->{sessionId});
    my $data_old = $hash->{helper}{'.delayed'}->{$identiy};
    delete $hash->{helper}{'.delayed'}{$identiy};
    deleteSingleRegisteredInternalTimer($identiy, $hash);

    return respond( $hash, $data->{requestType}, $data->{sessionId}, $data->{siteId}, getResponse( $hash, 'DefaultChoiceNoOutstanding' ) ) if ! defined $data_old;

    $data_old->{siteId} = $data->{siteId};
    $data_old->{sessionId} = $data->{sessionId};
    $data_old->{requestType} = $data->{requestType};
    $data_old->{Device} = $data->{Device};

    my $intent = $data_old->{intent};
    my $device = $hash->{NAME};

    # Passenden Intent-Handler aufrufen
    if (ref $dispatchFns->{$intent} eq 'CODE') {
        $device = $dispatchFns->{$intent}->($hash, $data_old);
    }

    return $device;
}




# borrowed from WeekdayTimer
################################################################################
sub resetRegisteredInternalTimer {
    my ( $modifier, $tim, $callback, $hash, $initFlag ) = @_;
    deleteSingleRegisteredInternalTimer( $modifier, $hash, $callback );
    return setRegisteredInternalTimer ( $modifier, $tim, $callback, $hash, $initFlag );
}

################################################################################
sub setRegisteredInternalTimer {
    my $modifier = shift // return;
    my $tim      = shift // return;
    my $callback = shift // return;
    my $hash     = shift // return;
    my $initFlag = shift // 0;

    my $timerName = "$hash->{NAME}_$modifier";
    my $fnHash     = {
        HASH     => $hash,
        NAME     => $timerName,
        MODIFIER => $modifier
    };
    if ( defined( $hash->{TIMER}{$timerName} ) ) {
        Log3( $hash, 1, "[$hash->{NAME}] possible overwriting of timer $timerName - please delete it first" );
        stacktrace();
    }
    else {
        $hash->{TIMER}{$timerName} = $fnHash;
    }

    Log3( $hash, 5, "[$hash->{NAME}] setting  Timer: $timerName " . FmtDateTime($tim) );
    InternalTimer( $tim, $callback, $fnHash, $initFlag );
    return $fnHash;
}

################################################################################
sub deleteSingleRegisteredInternalTimer {
    my $modifier = shift;
    my $hash     = shift // return;
    my $callback = shift;

    my $timerName = "$hash->{NAME}_$modifier";
    my $fnHash    = $hash->{TIMER}{$timerName};
    if ( defined($fnHash) ) {
        Log3( $hash, 5, "[$hash->{NAME}] removing Timer: $timerName" );
        RemoveInternalTimer($fnHash);
        delete $hash->{TIMER}{$timerName};
    }
    return;
}

################################################################################
sub deleteAllRegisteredInternalTimer {
    my $hash = shift // return;
       
    for my $key ( keys %{ $hash->{TIMER} } ) {
        deleteSingleRegisteredInternalTimer( $hash->{TIMER}{$key}{MODIFIER}, $hash );
    }
    return;
}


1;

__END__

Server: HP-T620@Debian 10, aktuelles FHEM + ConfigDB | CUL_HM (VCCU) | MQTT2: MiLight@ESP-GW, BT@OpenMQTTGw | MySensors: seriell, v.a. 2.3.1@RS485 | ZWave | ZigBee@deCONZ | SIGNALduino | MapleCUN | RHASSPY
svn:MySensors, Weekday-&RandomTimer, Twilight,  AttrTemplate {u.a. mqtt2, mysensors, zwave}

Offline CoolTux

  • Developer
  • Hero Member
  • ****
  • Beiträge: 27000
Das geht schon mal in die richtige Richtung, das "Problem" ist da nur, dass die Namen der Timer starr sind ("update"). Würde statt dieses starren Namens eine Option bestehen, einen eigenen Namen zu übergeben, könnte man das auch für RHASSPY wohl mit diesen Funktionen lösen. (Für WeekdayTimer und Twilight ginge es evtl. auch so schon).

Ich habe den Stefan mal zu dieser Diskussion eingeladen. Mal schauen ob er kommt.
Du musst nicht wissen wie es geht! Du musst nur wissen wo es steht, wie es geht.
Support me to buy new test hardware for development: https://paypal.me/pools/c/8gULisr9BT
My FHEM Git: https://git.cooltux.net/FHEM/
Mein Dokuwiki:
https://www.cooltux.net

Offline rudolfkoenig

  • Administrator
  • Hero Member
  • *****
  • Beiträge: 24396
@Beta-User: ich verstehe immer noch nicht das Problem.
Ist deine Befuerchtung, dass viele InternalTimer zu "teuer" sind? Ich meine nicht, jedenfalls seit noansi darauf gedraengt hat, sie zu optimieren. Wenn doch, dann bin ich dran.
Oder ist der Aufruf zu kompliziert?
Vermutlich uebersehe ich wieder was.

Offline Beta-User

  • Developer
  • Hero Member
  • ****
  • Beiträge: 15320
  • "Developer"?!? Meistens doch eher "User"
@Beta-User: ich verstehe immer noch nicht das Problem.
Das Problem ist eher die "beschränke" Anzahl an Parametern, die InternalTimer akzeptiert. "Eigentlich" hat man _immer_ $hash, weil man (von einer Modulinstanz aus gesprochen) diesen als Minumum braucht, um überhaupt wissen zu können, um "was es geht". Das ist aber in manchen Fällen zu wenig, also muss man die weitere Info irgendwie "einpacken". Aus "historischen Gründen" erfolgt das bei den "Twilight"-Derivaten über einen Hash, aber evtl. ist ja das Problem, dass ich bisher nicht auf den Gedanken gekommen war, dafür ein Array zu verwenden und das als Argument zu übergeben (wobei man das Array (?) dann auch irgendwo in der Modul-Instanz zwischenspeichern muss, um erforderlichenfalls genau diesen Timer löschen zu können (spätestens in der DeleteFn); ich unterstelle dabei, dass man die ursprünglichen $arg dabei passgenau übergeben muss).

Zitat
Ist deine Befuerchtung, dass viele InternalTimer zu "teuer" sind?
Na ja, wenn es richtig viele wären: vielleicht. Hier ist die Zahl ziemlich begrenzt, und selbst "ausufernde" WeekdayTimer werden kaum über 20 Schaltpunkte am Tag kommen...
Ergo: Die paar ggf. überflüssige Timer sind nicht mein Schmerz.

Zitat
Oder ist der Aufruf zu kompliziert?
Eher im Gegenteil, s.o..
Was ggf. etwas umständlich ist, ist ein "renew", also ein erneutes Setzen mit neuer Zeit, das braucht afaik erst ein Remove und dann ein Setzen - mehr oder weniger mit identischen Parametern. Dafür hätte ich die Diskussion aber nicht angefangen...

Zitat
Vermutlich uebersehe ich wieder was.
Kann ich nicht beurteilen.
Ich meine, in manchen (seltenen!) Fällen fehlt was, aber zum einen gibt's workarounds, und zum anderen sollte man im (vermeintlichen) Bedarfsfall kritisch prüfen, ob man sowas wirklich braucht. Im RandomTimer war nämlich auch diese Art der Timer-Verwaltung aus Twilight auch drin, dort aber völlig unnötig... (es gab noch ein paar "community"-Module, die wegen Umbenennens von "myInternalTimer" auf die Nase gefallen waren; da würde ich auch die Wahrscheinlichkeit auf >90% tippen, dass es dort unnötig kompliziert gelöst war). Das ist mir aber auch erst nach etwas längerer Diskussion mit Sidey über das Thema aufgegangen.

Hier in diesem Thread ging es darum rauszufinden, ob
- ich irgendwas übersehe, und man den gefundenen workaround gar nicht braucht, oder, falls ja, ob er
- ein "akzeptabler" ist oder ggf. sogar zum "Nachbauen" taugt (wenn man wirklich (!) sowas braucht).

(@CoolTux und ggf. StefanStrobel:
Vermutlich bin ich mit der Aussage zu kurz gesprungen, dass das aus der Utils.pm ggf. genutzt werden kann; das "Problem" ist, dass die HTTPMOD-"Familie" bestimmte "Kenner" im Modul-Instanz-Hash abgelegt erwartet, die so bei WDT oder Twilight keinen Sinn ergeben; es geht da nicht um periodisch wiederkehrende Abfragen, sondern um im Voraus berechnete Zeitpunkte, die sich auf den einzelnen Tag beziehen. Die Logik in der Ermittlung des nächsten Zeitpunkts ist also ziemlich anders.)
Server: HP-T620@Debian 10, aktuelles FHEM + ConfigDB | CUL_HM (VCCU) | MQTT2: MiLight@ESP-GW, BT@OpenMQTTGw | MySensors: seriell, v.a. 2.3.1@RS485 | ZWave | ZigBee@deCONZ | SIGNALduino | MapleCUN | RHASSPY
svn:MySensors, Weekday-&RandomTimer, Twilight,  AttrTemplate {u.a. mqtt2, mysensors, zwave}

Offline herrmannj

  • Global Moderator
  • Hero Member
  • ****
  • Beiträge: 5984
glaub das ist zu kompliziert gedacht. Dies geht doch beliebig (erzeugt beliebig viele an der $id unterscheidbare Timer, $id zb global um das Konzept zu illustrieren):

my $p = {
  'instance' => $hash,
  'foo' => 'bar',
  'id' => $id++,
...
InternalTimer($ts, \&_doTimer, $p);
}


sub _doTimer {
  my ($p) = @_;
  my $hash = $p->{'instance'};
  my $foo = $p->{'foo'};
  my $id = $p->{'id'};
  ...
}
smartVisu mit fronthem, einiges an HM, RFXTRX, Oregon, CUL, Homeeasy, ganz viele LED + Diverse

Offline Beta-User

  • Developer
  • Hero Member
  • ****
  • Beiträge: 15320
  • "Developer"?!? Meistens doch eher "User"
glaub das ist zu kompliziert gedacht. Dies geht doch beliebig (erzeugt beliebig viele an der $id unterscheidbare Timer, $id zb global um das Konzept zu illustrieren):
Das Erzeugen ist weniger das Problem, meine Sorgen betreffen eher das "Abräumen" (wenn der Timer nicht mehr benötigt wird).

Die "Twilight"-Methode ist genau die, die Timer so anzulegen, nur dass eben $p noch unter $hash->{TIMER}{$id} gepuffert wird, um ihn wieder löschen zu können.
Oder ist meine Sorge wegen des sauberen Wiederabräumens unbegründet?
Server: HP-T620@Debian 10, aktuelles FHEM + ConfigDB | CUL_HM (VCCU) | MQTT2: MiLight@ESP-GW, BT@OpenMQTTGw | MySensors: seriell, v.a. 2.3.1@RS485 | ZWave | ZigBee@deCONZ | SIGNALduino | MapleCUN | RHASSPY
svn:MySensors, Weekday-&RandomTimer, Twilight,  AttrTemplate {u.a. mqtt2, mysensors, zwave}

Offline Thorsten Pferdekaemper

  • Developer
  • Hero Member
  • ****
  • Beiträge: 6245
  • Finger weg von der fhem.cfg
Hi,
ich glaube, dass es ganz nett wäre, wenn wir Timer hätten, die in etwa so wie setTimeout / clearTimeout in JavaScript funktionieren.
Also in etwa so:
my $timerHandle = setTimer($timeStamp, \&myFunc, $arg1, $arg2, $arg3,...);

clearTimer($timerHandle);
Das kann man sich natürlich auch selbst basteln...
Gruß,
   Thorsten
FUIP

Offline CoolTux

  • Developer
  • Hero Member
  • ****
  • Beiträge: 27000
Antw:Timerverwaltung bei vorab unbekannter Timerzahl- best practice?
« Antwort #10 am: 03 Mai 2021, 14:04:32 »
glaub das ist zu kompliziert gedacht. Dies geht doch beliebig (erzeugt beliebig viele an der $id unterscheidbare Timer, $id zb global um das Konzept zu illustrieren):

my $p = {
  'instance' => $hash,
  'foo' => 'bar',
  'id' => $id++,
...
InternalTimer($ts, \&_doTimer, $p);
}


sub _doTimer {
  my ($p) = @_;
  my $hash = $p->{'instance'};
  my $foo = $p->{'foo'};
  my $id = $p->{'id'};
  ...
}

bei ASC habe ich das erkennen an den funcHash gebunden. Sowohl für Sunrise und Sunset Fahrten als auch die SelfDefense Fahrten habe ich eigene TimerHashs welche entsprechend an das Rolloobject gebunden werden.
Du musst nicht wissen wie es geht! Du musst nur wissen wo es steht, wie es geht.
Support me to buy new test hardware for development: https://paypal.me/pools/c/8gULisr9BT
My FHEM Git: https://git.cooltux.net/FHEM/
Mein Dokuwiki:
https://www.cooltux.net

Offline herrmannj

  • Global Moderator
  • Hero Member
  • ****
  • Beiträge: 5984
Antw:Timerverwaltung bei vorab unbekannter Timerzahl- best practice?
« Antwort #11 am: 03 Mai 2021, 14:14:14 »
Das Erzeugen ist weniger das Problem, meine Sorgen betreffen eher das "Abräumen" (wenn der Timer nicht mehr benötigt wird).

Die "Twilight"-Methode ist genau die, die Timer so anzulegen, nur dass eben $p noch unter $hash->{TIMER}{$id} gepuffert wird, um ihn wieder löschen zu können.
Oder ist meine Sorge wegen des sauberen Wiederabräumens unbegründet?
passt doch. Wenn er nicht mehr benötigt wird musst Du ihn halt "abräumen".

Pass nur auf: klassisches mem leak:
$hash->{TIMER}->{$id}->{'hash'}->{TIMER}->{$id}->{'hash'}->{TIMER}->{$id}->{'hash'}->{TIMER}->{$id}->{'hash'} ...
smartVisu mit fronthem, einiges an HM, RFXTRX, Oregon, CUL, Homeeasy, ganz viele LED + Diverse

Offline herrmannj

  • Global Moderator
  • Hero Member
  • ****
  • Beiträge: 5984
Antw:Timerverwaltung bei vorab unbekannter Timerzahl- best practice?
« Antwort #12 am: 03 Mai 2021, 14:22:28 »
bei ASC habe ich das erkennen an den funcHash gebunden. Sowohl für Sunrise und Sunset Fahrten als auch die SelfDefense Fahrten habe ich eigene TimerHashs welche entsprechend an das Rolloobject gebunden werden.
ja, mach ich auch manchmal mit einer closure (lexical sub). Muss man beim abräumen aber aufpassen

sub foo {
  my ($hash) = @_;
 
  $p = $id;
  my sub _doTimer {
    my ($id) = @_;
    # alle vars schon da
  }
 
  InternalTimer($ts, _doTimer, $p);
}
smartVisu mit fronthem, einiges an HM, RFXTRX, Oregon, CUL, Homeeasy, ganz viele LED + Diverse

Offline rudolfkoenig

  • Administrator
  • Hero Member
  • *****
  • Beiträge: 24396
Antw:Timerverwaltung bei vorab unbekannter Timerzahl- best practice?
« Antwort #13 am: 03 Mai 2021, 14:36:44 »
Zitat
passt doch. Wenn er nicht mehr benötigt wird musst Du ihn halt "abräumen".
Oder abwarten dass die Funktion aufgerufen wird, und dann nichts machen.
Ob was zu machen ist oder nicht, kann man im Hash ablegen.

Zitat
my $timerHandle = setTimer($timeStamp, \&myFunc, $arg1, $arg2, $arg3,...);
clearTimer($timerHandle);

Z.Zt. muss man das so schreiben:
my $timerHandle = [$arg1, $arg2, $arg3,...];
InternalTimer($timesSamp, \&myFunc, $timerHandle);
RemoveInternalTimer($timerHandle);

Offline Thorsten Pferdekaemper

  • Developer
  • Hero Member
  • ****
  • Beiträge: 6245
  • Finger weg von der fhem.cfg
Antw:Timerverwaltung bei vorab unbekannter Timerzahl- best practice?
« Antwort #14 am: 03 Mai 2021, 14:42:13 »
Z.Zt. muss man das so schreiben:
my $timerHandle = [$arg1, $arg2, $arg3,...];
InternalTimer($timesSamp, \&myFunc, $timerHandle);
RemoveInternalTimer($timerHandle);
Das ist nicht ganz dasselbe. Es funktioniert nur, wenn die Argumente immer unterschiedlich sind. Es kann aber gut sein, dass man dieselbe Funktion mit denselben Argumenten einmal in 10s aufrufen will und dann nochmal in 20s.
Natürlich kann man auch das wieder "reparieren"...
Gruß,
   Thorsten 
FUIP