Ermittlung von devices für NOTIFYDEV mittels notifyRegexpChanged

Begonnen von fruemmel, 12 Januar 2022, 13:07:41

Vorheriges Thema - Nächstes Thema

Beta-User

Zitat von: Benni am 17 Januar 2022, 19:22:08
Die Regex muss keinen Doppelpunkt enthalten. Da sie nur matchen muss, kann es auch notify geben (gibt es auch!) in der Art:
Nun ja, gemeint hatte ich das (wieder etwas vereinfachend ausgedrückt) so: gibt es einen Doppelpunkt, ist davon auszugehen, dass er Device und Reading trennen soll. Fehlt er, ist davon auszugehen, dass der ganze Ausdruck als Device/devspec für devspec2array() aufgefasst werden soll.

Wer es unbedingt so notieren will, dass NOTIFYDEV nicht erkannt wird, darf das meinetwegen weiterhin so machen...

Ergänzend aber noch zu dem, was ich gestern zu den Unterschieden zwischen NOTIFYDEV, NOTIFYDEV_1 und NOTIFYDEV_2 geschrieben hatte: Oft wird bei den direkten "ich schreib das selbst in den Device-Hash"-Fällen schlicht auf "global" reagiert - es handelt sich also mutmaßlich eigentlich um ein (post-) Initialisierungs-Thema, warum da überhaupt mit NotifyFn() gearbeitet wird.
Da in https://forum.fhem.de/index.php/topic,64231.msg1201433.html#msg1201433 mal wieder jemand über at-Perl gestoplert wird, "nerve" ich mal wieder mit dem Hinweis, dass man das Thema m.E. auch insgesamt überdenken sollte...
Server: HP-elitedesk@Debian 12, 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: u.a MySensors, Weekday-&RandomTimer, Twilight,  div. attrTemplate-files

Beta-User

...nach etwas geteste hat sich rausgestellt, dass das regexen zur Ermittlung des "verbleibenden Rests" wohl keine tolle Idee ist...

substr() scheint das besser zu machen, daher nochmal ein update, das beim ersten Testen auch mit sowas
defmod n_test_sm notify FB_.*:.(Short|Long).1_.*\(to.ccu\).*  set fakeRokudummy_dummy 3 test3 [a:$NAME:alias] $EVENTund sowas klarkommt:defmod n_test3 notify devstrich0:o[nf]+|rC2TestDummy[12]:.* {if ( $EVENT eq 'on' ) {}}

sub
notifyRegexpChanged($$;$)
{
  my ($hash, $re, $disableNotifyFn) = @_;
  notifyRegexpChanged2($hash, $re, $disableNotifyFn);

  %ntfyHash = ();
  if($disableNotifyFn) {
    delete($hash->{NOTIFYDEV});
    delete($hash->{NOTIFYDEV_1});
    $hash->{disableNotifyFn}=1;
    return;
  }
  delete($hash->{disableNotifyFn});
  my @list2 = split(/\|/, $re);
  my @list = grep { m/./ }                                     # Forum #62369
             map  { (m/^\(?([A-Za-z0-9\.\_]+(?:\.[\+\*])?)(?::.*)?\)?$/ &&
                     ($defs{$1} || devspec2array($1) ne $1)) ? $1 : ""} @list2;
  if(@list && int(@list) == int(@list2)) {
    my %h = map { $_ => 1 } @list;
    @list = keys %h; # remove duplicates
    $hash->{NOTIFYDEV} = join(",", @list);
    $hash->{NOTIFYDEV_1} = join(",", @list);
  } else {
    delete($hash->{NOTIFYDEV});
    delete($hash->{NOTIFYDEV_1});
  }
}

sub
notifyRegexpChanged2
{
  my ($hash, $re, $disableNotifyFn) = @_;

  #Log3('global',3 , "nRC2 called for $hash->{NAME}");
  #%ntfyHash = ();
  if($disableNotifyFn) {
    delete($hash->{NOTIFYDEV_2});
  #  $hash->{disableNotifyFn}=1;
    return;
  }
  #delete($hash->{disableNotifyFn});
  my $first = 1;
  my $outer = $re =~ m{\A\s*\((.+)\)\s*\z}x; #check if outer brackets are given
  my @list;
  my $numdef = keys %defs;
  while ($re) {
    (my $dev, $re) = split m{:}x, $re, 2; #get the first seperator for device/reading+rest?
    if ( $first && $outer ) {
        $first = 0;
        my $ops = $dev =~ tr/(//;
        my $clos = $dev =~ tr/)//;
        if ( $ops > $clos ) {
            chop($re);
            $dev =~ s{\A.}{}x;
        }
    }
    $dev =~ s{\A\s*\((.+)\)\s*\z}{$1}x; #remove outer brackets if given
    #Log3('global',3 , "re splitted to $dev and $re") if $re;
    return delete $hash->{NOTIFYDEV_2} if $dev eq '.*';

    while ($dev) {
      (my $part, $dev) = splitByPipe($dev);
      #Log3('global',3 , "dev splitted to $part and $dev") if $dev;

      return delete $hash->{NOTIFYDEV_2} if $part eq '.*';
      my @darr = devspec2array($part);
      return delete $hash->{NOTIFYDEV_2} if !@darr || !$defs{$part} && $darr[0] eq $part || $numdef == @darr;
      @list = (@list, @darr);
    }
    (undef, $re) = splitByPipe($re);
    #Log3('global',3 , "re now cleaned to $re");
  }
  return delete($hash->{NOTIFYDEV2}) if !@list;
  my %h = map { $_ => 1 } @list;
  @list = keys %h; # remove duplicates
  $hash->{NOTIFYDEV_2} = join q{,}, @list;
  return;
}

sub splitByPipe {
    my $string = shift // return (undef,undef);
    # String in pipe-getrennte Tokens teilen, wenn die Klammerebene 0 ist
    my $lastChar = q{x};
    my $bracketLevel = 0;
    my $token = q{};
    my @chars = split q{}, $string;
    my $i = 0;
    for my $char ( @chars ) {
        #Log3('global',3,"char is $char, last was $lastChar, level is $bracketLevel");
        if ($char eq '|' && $lastChar ne '\\' && !$bracketLevel) {
            $string = substr $string, length($token) + 1;
            #Log3('global',3 , "pipe detected, exiting with token $token, string $string");
            return ($token, $string);
        }
        $i++;
        if ($char eq q<(> && $lastChar ne '\\') {
            $bracketLevel++;
        }
        elsif ($char eq q<)> && $lastChar ne '\\') {
            $bracketLevel--;
        }
        $token .= $char;
        if ( $i == scalar @chars ) {
          $string = substr $string, length $token;
        }
        $lastChar = $char;
    }
    #Log3('global',3 , "no more pipes detected, exiting with token $token, string $string");
    return ($token, $string);
}
Server: HP-elitedesk@Debian 12, 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: u.a MySensors, Weekday-&RandomTimer, Twilight,  div. attrTemplate-files

fruemmel

@Beta-User: Danke für die Forschungsarbeit!

Ich habe deine letzte Version bei mir jetzt eingebaut und ein paar Tests gemacht. Es werden deutlich mehr regexp erkannt als vorher. Eine bessere Erkennung gibt es bei mir allerdings auch nur bei den Device-Types notify, watchdog und FileLog. Das bestätigt auch deine Aussage, dass viele Modulentwickler das ohne notifyRegexpChanged oder eben gar nicht lösen. Schade finde ich das insb. bei Modulen wie LightScene, die in der notifyFunc auch anscheinend etwas mehr Zeit verbringen und sich dann um viele uninteressante events kümmern.

Was bisher keine Version erkennt, ist der folgende Ausdruck. Der soll für ein FileLog die Messwerte verschiedener Sensoren (LC_.*) sowie den Temperaturwert eines Heizungsmoduls (mykm200...) einsammeln.
(LC_.*(temperature|humidity)|mykm200:/system/sensors/temperatures/outdoor_t1).*
Aber solche Exoten kann man sicher immer konstruieren, und es geht ja eher darum die breite Masse zu erwischen. Außerdem kann man die regexp sicher leicht anpassen, damit sie erkannt wird.

Da man die perfekte Variante nicht oder nur mit großem Aufwand erreichen kann, finde ich es extrem hilfreich, wenn man seine "schwarzen Schafe" mit einem kleinen Diagnosetool selbst identifizieren könnte.
Einerseits wäre es also hilfreich, wenn man die Geräte identifiziert, bei denen die regexp nicht erkannt wird, und andererseits herausfinden kann, welche Geräte in einem bestimmten Zeitraum die meisten events verursachen. In der Richtung existiert aber noch nichts, oder?

Beta-User

Danke für die Rückmeldung. Hier nochmal eine zur Vorgehensweise passende Alternativ-Fassung zu der bereits von Rudi genannten Hilfsfunktion notifyRegexpCheck() - da ist intern nur die Hilfsfunktion "splitByPipe" in "notifyRegexpChangedsplitByPipe" umbenannt.

Ist gleich noch erweitert um die Erkennung der "Umschließenden Klammer samt Ende-.*":
sub
notifyRegexpCheck2
{
    my $re = shift // return 'No Expression to check provided!';
    my @list;
    my $consume = $re =~ s{\A\s*(\(.+\))\.\*\z}{$1}x;
    my $outer = $re =~ m{\A\s*\((.+)\)\s*\z}x; #check if outer brackets are given
    my $numdef = keys %defs;
    my $first = 1;
    while ($re) {
        (my $dev, $re) = split m{:}x, $re, 2; #get the first seperator for device/reading+rest?
        if ( $first && $outer ) {
            $first = 0;
            my $ops = $dev =~ tr/(//;
            my $clos = $dev =~ tr/)//;
            if ( $ops > $clos ) {
                chop($re);
                $dev =~ s{\A.}{}x;
            }
        }
        $dev =~ s{\A\s*\((.+)\)\s*\z}{$1}x; #remove outer brackets if given
        push @list, q{.*: matches all (ignored)} if $dev eq '.*';

        while ($dev) {
          (my $part, $dev) = notifyRegexpChangedsplitByPipe($dev);
          #Log3('global',3 , "dev splitted to $part and $dev") if $dev;

          push @list, q{.*: matches all (ignored)} if $part eq '.*';
          if ($defs{$part}) {
              push @list, qq{$part: device $part (OK)};
          } else {
            my @ds = devspec2array($part);
            if($ds[0] ne $part) {
                push @list, "$part: devspec ".join q{,}, @ds." (OK)";
            } elsif ($numdef == @ds) {
                push @list, "$part: matches all devices (ignored)";
            } else {
                push @list, "$part: unknown (ignored)";
            }
          }
        }
        (my $other, $re) = notifyRegexpChangedsplitByPipe($re);
        push @list, "$other: irrelevant part (OK)";
    }
    push @list, ".*: irrelevant part (OK)" if $consume;
    return join "\n", @list;
}


Damit kann man feststellen, woran es hapert, und Rudi kann ggf. besser überlegen, ob so eine Umstellung wirklich die Nebenwirkungen hätte, die er fürchtet :) .

Geräte mit "guten NOTIFYDEV" zählen kann man mit "count".

DOIFtools haben afaik auch Möglichkeiten zu erfassen, wie oft Events bei einzelnen Geräten geworfen werden.

Was in jedem Fall "unzeitgemäß" ist, ist der Reading-Name, das scheint was "uraltes" zu sein, das so ein Reading generiert...
Server: HP-elitedesk@Debian 12, 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: u.a MySensors, Weekday-&RandomTimer, Twilight,  div. attrTemplate-files

Damian

#19
Ich würde da nicht zu viel Energie investieren. Es ist und bleibt ein heißes Eisen. Sobald irgendeine Definition danach in FHEM nicht funktioniert, kann man nachbessern und verärgert nur die Anwender.

Da viele Programme den Filter ohnehin nicht nutzen, deckt es nur einen Teil ab.

Ein nicht unerheblicher Teil der Events aller FHEM-Systeme läuft über diesen Code:

sub
DOIF_Notify($$)
{
  my ($hash, $dev) = @_;
  my $pn = $hash->{NAME};
  return "" if($attr{$pn} && $attr{$pn}{disable});
  return "" if (!$dev->{NAME});
  my $device;
  my $reading;
  my $internal;
  my $ret;
  my $err;
  my $eventa;
  my $eventas;
 

  if (!defined($hash->{helper}{DEVFILTER})) {
    return "";
  } elsif ($dev->{NAME} !~ /$hash->{helper}{DEVFILTER}/) {
    return "";
  }
...


Man kann jetzt messen wieviel Millisekunden ein Funktionsaufruf der notify-Funktion dauert bei einem nicht gesetzten NOTIFYDEV-Filter bis zum Ausstieg durch den eigenen Filter, bei Falschalarm - es werden nicht viele sein.
Programmierte FHEM-Module: DOIF-FHEM, DOIF-Perl, DOIF-uiTable, THRESHOLD, FHEM-Befehl: IF

Damian

Ich habe mir mal den Spaß gemacht und eine Stoppuhr laufen lassen:

my %hash;
$hash{NAME}="Test";
my %dev;
$dev{NAME}="bla";
my %attr;
$hash{helper}{DEVFILTER}="keiner";

sub
DOIF_Notify($$)
{
  my ($hash, $dev) = @_;
  my $pn = $hash->{NAME};
  return "1" if($attr{$pn} && $attr{$pn}{disable});
  return "2" if (!$dev->{NAME});
  my $device;
  my $reading;
  my $internal;
  my $ret;
  my $err;
  my $eventa;
  my $eventas;


  if (!defined($hash->{helper}{DEVFILTER})) {
    return "3";
  } elsif ($dev->{NAME} !~ /$hash->{helper}{DEVFILTER}/) {
    return "kein Treffer";
  }
  return("Ende");
}
for(my $i=0;$i<10000000;$i++){
DOIF_Notify(\%hash,\%dev);
}


Ergebnis pro Aufruf bis zum return "kein Treffer" auf einem raspi 4: 0.002 Millisekunden

Wie viele Events gibt es wohl in einem durchschnittlichen FHEM-System pro Sekunde - es lohnt nicht darüber nachzudenken.

Programmierte FHEM-Module: DOIF-FHEM, DOIF-Perl, DOIF-uiTable, THRESHOLD, FHEM-Befehl: IF

fruemmel

ZitatErgebnis pro Aufruf bis zum return "kein Treffer" auf einem raspi 4: 0.002 Millisekunden

Wie viele Events gibt es wohl in einem durchschnittlichen FHEM-System pro Sekunde - es lohnt nicht darüber nachzudenken.

Ich habe bei mir mal ein paar Werte ermittelt. Ich habe ca. 8.000 Events pro Stunde. Dabei ist schon einiges über event-on-change-reading etc. wegoptimiert. Mit der aktuellen (freigegebenen) regexp-Erkennung habe ich ca. 150 devices, die kein NOTIFYDEV setzen, aber eine NotifyFunc hinterlegt haben. Wenn ich diese Anzahl der Geräte mit der Anzahl der Events multipliziere, komme ich auf insgesamt ca. 1.200.000 Aufrufe der Notify-Functions pro Stunde (also gut 300/Sekunde), um die sich ein Event-Handler theoretisch kümmert. 
Wenn man deine 0.002 Millisekunden als Maßstab nimmt, ist das immer noch wenig Rechenzeit, zumal ja mehrere gleichzeitige Events eines Gerätes performanter behandelt werden können. Andererseits liegt es ja in den Händen des Moduls, wie schnell Events verworfen werden, die das Modul nicht interessieren (falls kein NOTIFYDEV).

Leider entstehen die Events aber ja nicht gleichverteilt, sondern teilweise gehäuft. Ich nutze FTUI recht intensiv, und es gibt immer wieder Momente, wo das System ziemlich verschlafen reagiert. Daher kam der Verdacht auf, dass das Zusammenspiel von Events und NOTIFYDEV auch einen Einfluss haben könnte.

Mir ist aber durch die Diskussion und  Beschäftigung mit dem Thema  auch klar geworden, dass ich noch viel Potential habe, um meine Events zu reduzieren und auch die regexp zu optimieren. Mal sehen, ob das am Ende spürbar hilft.

rudolfkoenig

ZitatWie viele Events gibt es wohl in einem durchschnittlichen FHEM-System pro Sekunde - es lohnt nicht darüber nachzudenken.
Ein durchschnittliches FHEM-System ist in diesem Fall irrelevant.

Die Optimierung wurde 2014 eingebaut, ausgeloest durch ein System mit 300+ Notify/FileLog/etc "NotifyFn" Instanzen und eine Eventrate von 100+/sec auf einem relativ schwachen Rechner (RPi 1?).
Auf einem RPi4 benoetigt diese Installation ca 300*100*0.002ms=60ms => 6% CPU fuer die "sinnlose" Pruefung. Ein RPi4 ist pro Core laut Internet 4 bis 6-mal schneller als ein RPi, da wuerde es also 24%-36% ausmachen. Wenn die restlichen Aktivitaeten das System schon zu 80% ausreizen, kann eine Optimierung einen wesentlichen Unterschied machen.
Und ich habe seither auch "schlimmere" Installationen gesehen.

Diese Optimierung ist sicher nur einer unter Vielen und ich erweitere es gerne, der Code muss aber wartbar (d.h.leicht verstaendlich) bleiben.


Beta-User

Hmm, ob das "wartbarer Code" ist, sei mal dahingestellt, hier jedenfalls mal ein "aktualisierter Satz" für weitere (Ergebnisvergleichs-) Tests für die, die es interessiert.

Auf dem Testsystem sah das auch mit diversen "typischen User-Definitionen" auf den ersten Blick ganz ok aus.

sub
notifyRegexpCheck2
{
    my $re = shift // return 'No Expression to check provided!';
    my $numdef = keys %defs;
    my @fm = devspec2array($re);
    if (@fm  && $fm[0] ne $re ) {
        return "$re: matches all devices (ignored)" if $numdef == @fm;
        my $lst = join q{,}, @fm;
        return "$re: devspec for devices only - $lst (OK)";
    }
    my $consume = $re =~ s{\A\s*(\(.+\))\.\*\z}{$1}x;
    my $outer = $re =~ m{\A\s*\((.+)\)\s*\z}x; #check if outer brackets are given
    my $first = 1;
    my @list;
    while ($re) {
        (my $dev, $re) = split m{:}x, $re, 2; #get the first seperator for device/reading+rest?
        if ( $first && $outer ) {
            $first = 0;
            my $ops = $dev =~ tr/(//;
            my $clos = $dev =~ tr/)//;
            if ( $ops > $clos ) {
                chop($re);
                $dev =~ s{\A.}{}x;
            }
        }
        $dev =~ s{\A\s*\((.+)\)\s*\z}{$1}x; #remove outer brackets if given
        push @list, q{.*: matches all (ignored)} if $dev eq '.*';

        while ($dev) {
          (my $part, $dev) = notifyRegexpChangedsplitByPipe($dev);
          #Log3('global',3 , "dev splitted to $part and $dev") if $dev;

          push @list, q{.*: matches all (ignored)} if $part eq '.*';
          if ($defs{$part}) {
              push @list, qq{$part: device $part (OK)};
          } else {
            my @ds = devspec2array($part);
            if($ds[0] ne $part) {
                my $lst = join q{,}, @ds;
                push @list, "$part: devspec $lst (OK)";
            } elsif ($numdef == @ds) {
                push @list, "$part: matches all devices (ignored)";
            } else {
                push @list, "$part: unknown (ignored)";
            }
          }
        }
        (my $other, $re) = notifyRegexpChangedsplitByPipe($re);
        push @list, "$other: irrelevant part (OK)";
    }
    push @list, ".*: irrelevant part (OK)" if $consume;
    return join "\n", @list;
}

sub
notifyRegexpChanged($$;$)
{
  my ($hash, $re, $disableNotifyFn) = @_;
  notifyRegexpChanged2($hash, $re, $disableNotifyFn);

  %ntfyHash = ();
  if($disableNotifyFn) {
    delete($hash->{NOTIFYDEV});
    delete($hash->{NOTIFYDEV_1});
    $hash->{disableNotifyFn}=1;
    return;
  }
  delete($hash->{disableNotifyFn});
  my @list2 = split(/\|/, $re);
  my @list = grep { m/./ }                                     # Forum #62369
             map  { (m/^\(?([A-Za-z0-9\.\_]+(?:\.[\+\*])?)(?::.*)?\)?$/ &&
                     ($defs{$1} || devspec2array($1) ne $1)) ? $1 : ""} @list2;
  if(@list && int(@list) == int(@list2)) {
    my %h = map { $_ => 1 } @list;
    @list = keys %h; # remove duplicates
    $hash->{NOTIFYDEV} = join(",", @list);
    $hash->{NOTIFYDEV_1} = join(",", @list);
  } else {
    delete($hash->{NOTIFYDEV});
    delete($hash->{NOTIFYDEV_1});
  }
}

sub
notifyRegexpChanged2
{
  my ($hash, $re, $disableNotifyFn) = @_;
  #Log3('global',3 , "nRC2 called for $hash->{NAME}");
  #%ntfyHash = ();
  if($disableNotifyFn) {
    delete($hash->{NOTIFYDEV_2});
  #  $hash->{disableNotifyFn}=1;
    return;
  }
  #delete($hash->{disableNotifyFn});
  my $numdef = keys %defs;
  my @fm = devspec2array($re);
  if (@fm && $fm[0] ne $re) {
    return delete $hash->{NOTIFYDEV_2} if $numdef == @fm ;
    my $lst = join q{,}, @fm;
    $hash->{NOTIFYDEV_2} = $lst;
    return;
  }

  $re =~ s{\A\s*(\(.+\))\.\*\z}{$1}x;
  my $first = 1;
  my $outer = $re =~ m{\A\s*\(.+\)\s*\z}x; #check if outer brackets are given
  my @list;
  while ($re) {
    (my $dev, $re) = split m{:}x, $re, 2; #get the first seperator for device/reading+rest?
    if ( $first && $outer ) {
        $first = 0;
        my $ops = $dev =~ tr/(//;
        my $clos = $dev =~ tr/)//;
        if ( $ops > $clos ) {
            chop($re);
            $dev =~ s{\A.}{}x;
        }
    }
    $dev =~ s{\A\s*\((.+)\)\s*\z}{$1}x if $outer; #remove outer brackets if given
    #Log3('global',3 , "re splitted to $dev and $re") if $re;
    return delete $hash->{NOTIFYDEV_2} if $dev eq '.*';

    while ($dev) {
      (my $part, $dev) = notifyRegexpChangedsplitByPipe($dev);
      #Log3('global',3 , "dev splitted to $part and $dev") if $dev;

      return delete $hash->{NOTIFYDEV_2} if $part eq '.*';
      my @darr = devspec2array($part);
      return delete $hash->{NOTIFYDEV_2} if !@darr || !$defs{$part} && $darr[0] eq $part || $numdef == @darr;
      @list = (@list, @darr);
    }
    (undef, $re) = notifyRegexpChangedsplitByPipe($re);
    #Log3('global',3 , "re now cleaned to $re");
  }
  return delete($hash->{NOTIFYDEV2}) if !@list;
  my %h = map { $_ => 1 } @list;
  @list = keys %h; # remove duplicates
  $hash->{NOTIFYDEV_2} = join q{,}, @list;
  return;
}

sub notifyRegexpChangedsplitByPipe {
    my $string = shift // return (undef,undef);
    # String in pipe-getrennte Tokens teilen, wenn die Klammerebene 0 ist
    my $lastChar = q{x};
    my $bracketLevel = 0;
    my $token = q{};
    my @chars = split q{}, $string;
    my $i = 0;
    for my $char ( @chars ) {
        #Log3('global',3,"char is $char, last was $lastChar, level is $bracketLevel");
        if ($char eq '|' && $lastChar ne '\\' && !$bracketLevel) {
            $string = substr $string, length($token) + 1;
            #Log3('global',3 , "pipe detected, exiting with token $token, string $string");
            return ($token, $string);
        }
        $i++;
        if ($char eq q<(> && $lastChar ne '\\') {
            $bracketLevel++;
        }
        elsif ($char eq q<)> && $lastChar ne '\\') {
            $bracketLevel--;
        }
        $token .= $char;
        if ( $i == scalar @chars ) {
          $string = substr $string, length $token;
        }
        $lastChar = $char;
    }
    #Log3('global',3 , "no more pipes detected, exiting with token $token, string $string");
    return ($token, $string);
}
Server: HP-elitedesk@Debian 12, 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: u.a MySensors, Weekday-&RandomTimer, Twilight,  div. attrTemplate-files

Damian

Zitat von: rudolfkoenig am 19 Januar 2022, 09:54:59
Diese Optimierung ist sicher nur einer unter Vielen und ich erweitere es gerne, der Code muss aber wartbar (d.h.leicht verstaendlich) bleiben.

Meine Rechnung bezog sich nur auf mein Modul, was ja diesbzgl. auch schon optimiert wurde. Die meisten Modulentwickler werden sich mit der Materie nicht so intensiv auseinandergesetzt haben, da wird mit Sicherheit mehr Zeit verbraten, bis man feststellt, dass man für ein bestimmtes Event nicht zuständig ist, also ist eine vernünftige Filtermöglichkeit von außen durch sinnvoll.

Wie auch immer, am Ende musst du Anpassungen in der fhem.pl verantworten.
Programmierte FHEM-Module: DOIF-FHEM, DOIF-Perl, DOIF-uiTable, THRESHOLD, FHEM-Befehl: IF

Damian

Wir reden hier die ganze Zeit von einem Device-Filter und nicht von einem Event-Filter.

Der Modulentwickler könnte, unabhängig von der jetzigen Vorgehensweise, die Option bekommen einen reinen Device-Filter als Regex der "Zentrale" mitzuteilen?

Dann könnte man in fhem.pl bevor man unnötig die jeweilige notify-Funktion aufruft das Device $dev->{NAME} einfach gegen den Filter prüfen.

Damit würde ich immerhin in meinem Modul 0.002 Millisekunden pro unnötiges Event sparen, andere wahrscheinlich viel mehr, wenn sie den Filter setzen könnten.
Programmierte FHEM-Module: DOIF-FHEM, DOIF-Perl, DOIF-uiTable, THRESHOLD, FHEM-Befehl: IF

Beta-User

Zitat von: Damian am 19 Januar 2022, 23:43:50
Wir reden hier die ganze Zeit von einem Device-Filter und nicht von einem Event-Filter.
Korrekt, der Name der Funktion verkürzt das m.E. etwas...

Zitat
Der Modulentwickler könnte, unabhängig von der jetzigen Vorgehensweise, die Option bekommen einen reinen Device-Filter als Regex der "Zentrale" mitzuteilen?
Das ist schon jetzt möglich. Einige Module setzen direkt eine devspec (wie für list zulässig) in NOTIFYDEV, z.B. msgDialog oder msgConfig (über ersteres bin ich neulich erst wg. RHASSPY gestolpert), der Weg klappt nur nicht über die bisherige Funktion.

ZitatDann könnte man in fhem.pl bevor man unnötig die jeweilige notify-Funktion aufruft das Device $dev->{NAME} einfach gegen den Filter prüfen. [...]
Genau so ist es mAn. bereits heute. In dem Moment, in dem NOTIFYDEV gesetzt wird, ist es vermutlich nicht mehr erforderlich (oder sinnvoll), nochmal eigene Optimierungen vorzunehmen.



In dem Zusammenhang ist mir aufgefallen, dass es in meinem Vorschlag auch sinnvoller ist, die regex und nicht die Device-Liste in NOTIFYDEV zu schreiben, also die innere Schleife so zu notieren:
    while ($dev) {
      (my $part, $dev) = notifyRegexpChangedsplitByPipe($dev);
      #Log3('global',3 , "dev splitted to $part and $dev") if $dev;

      return delete $hash->{NOTIFYDEV_2} if $part eq '.*';
      my @darr = devspec2array($part);
      return delete $hash->{NOTIFYDEV_2} if !@darr || !$defs{$part} && $darr[0] eq $part || $numdef == @darr;
      #@list = (@list, @darr); #alte Variante
      push @list, $part; #neue Variante
    }

Das macht es etwas unempfindlicher gegen zwischenzeitliche Umbenennungen der überwachten Devices.

@Rudi:
Würde es Sinn machen, hier einen featurelevel-abhängigen Vorschlag zu machen? Dann könnten ggf. willige Tester einfach den anpassen...?
Oder findest du den ganzen Code so nicht gut?
Server: HP-elitedesk@Debian 12, 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: u.a MySensors, Weekday-&RandomTimer, Twilight,  div. attrTemplate-files

rudolfkoenig

Wie geschrieben, der Code uebersteigt z.Zt. mein Verstaendnis, und bis ich alle Nebeneffekte begreife, wird es auch dauern.
Ich wuerde lieber einen anderen Weg gehen.

Zum Klarstellen:
- mit direktes Setzen von NOTIFYDEV kann jedes Modul die Optimierung selbst aktivieren, ein Aufruf von notifyRegexpChanged ist nicht erforderlich.
- notifyRegexpChanged ist fuer Module wie notify und FileLog, deren Regexp Geraet und Event zusammen bearbeitet. Es ist angepasst fuer von autocreate angelegten FileLogs, von den "EventMonitor create" Knopf angelegten Geraete bzw. von den "Wizards" erweiterten Regexps.
- das notifyRegexpChanged Problem ist nur bei grossen und schwer belasteten Installationen relevant, wo der Benutzer selbst "falsch" optimiert hat.

Ich habe vor in der notify und FileLog FHEMWEB Detailansicht eine Hilfe einzubauen, damit das Optimieren fuer den Benutzer einfacher faellt. Vmtl. ist meine urspruengliche Idee eines notifydev Attributs auch hilfreich.

Beta-User

Zitat von: rudolfkoenig am 20 Januar 2022, 09:20:42
Ich wuerde lieber einen anderen Weg gehen.
OK, dann stelle ich meine Bemühungen an der Stelle ein.

ZitatIch habe vor in der notify und FileLog FHEMWEB Detailansicht eine Hilfe einzubauen, damit das Optimieren fuer den Benutzer einfacher faellt. Vmtl. ist meine urspruengliche Idee eines notifydev Attributs auch hilfreich.
Vermutlich ist der Aufwand, den wizzard zu überarbeiten (den man m.e. sinnvollerweise auch für meine Variante anpassen müßte, was aber kein allzugroßes Hexenwerk sein dürfte), deutlich höher wie die Analyse dessen, was mein Code-Vorschlag so in etwa macht. Im Ergebnis muss man doch nur checken, ob NOTIFYDEV "gut" gefüllt ist, ab da geht es 1:1 weiter "as is". Das wäre also nur ein Übergangsthema, oder?

Was ich schwierig finde, ist die "Doppelpflege" von DEF und Attribut. Da sehe ich die größere Gefahr für _dauerhaften_ Supportaufwand wie wenn man das Problem "an der Wurzel" löst. (vermutlich ist das vergleichbar mit dem Dauerbrenner "addStateEvent").

Just my2ct.
Server: HP-elitedesk@Debian 12, 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: u.a MySensors, Weekday-&RandomTimer, Twilight,  div. attrTemplate-files

Damian

#29
Zitat von: rudolfkoenig am 20 Januar 2022, 09:20:42
Zum Klarstellen:
- mit direktes Setzen von NOTIFYDEV kann jedes Modul die Optimierung selbst aktivieren, ein Aufruf von notifyRegexpChanged ist nicht erforderlich.
- notifyRegexpChanged ist fuer Module wie notify und FileLog, deren Regexp Geraet und Event zusammen bearbeitet. Es ist angepasst fuer von autocreate angelegten FileLogs, von den

Also, irgendwie widersprechen sich die Aussagen und ich finde keine verbindliche Doku zum Setzen von NOTIFYDEV:

unter anderem finde ich z. B.

aus: https://forum.fhem.de/index.php/topic,64382.msg556060.html#msg556060

ZitatDie Funktion notifyRegexpChanged prueft sowas, und berechnet aus einem Regexp NOTIFYDEV bzw. loescht sie, falls NOTIFYDEV kontraproduktiv ist. Modulautoren sollten NOTIFYDEV nur ueber diese Funktion setzen, und nie direkt. Damit ist auch eine Aenderung/Optimierung eher moeglich.

Diese Frage : https://forum.fhem.de/index.php/topic,64449.msg557606.html#msg557606

wurde ebenfalls nicht eindeutig beantwortet.

Wo finde ich denn jetzt eine verbindliche Aussage, was ich als Modulautor (hier z. B. beim DOIF) tun kann? notifyRegexpChanged benutze ich laut Empfehlung, obwohl es nicht besonders sinnvoll für mein Modul ist, da bei mir Device-Regex und Event-Regex ohnehin getrennt behandelt wird, daher habe ich gar nicht das "notify"-Problem.



Programmierte FHEM-Module: DOIF-FHEM, DOIF-Perl, DOIF-uiTable, THRESHOLD, FHEM-Befehl: IF