59_WUup Code Review

Begonnen von RichardCZ, 26 März 2020, 12:50:49

Vorheriges Thema - Nächstes Thema

RichardCZ

@mahowi hat sich in $SUBJ der Prototypen und der HTML Entities entledigt:

https://svn.fhem.de/trac/changeset/21511/trunk/fhem/FHEM/59_WUup.pm?old=21411&old_path=trunk%2Ffhem%2FFHEM%2F59_WUup.pm

Es ist wohl klar, dass ich das begrüße und frohlocke. Dass dadurch gleichzeitig einige Inkonsistenzen ausgeräumt wurden

sub WUup_Define($$$) {
<->
sub WUup_Define {
       my ( $hash, $def ) = @_;

   
ist ein netter Nebeneffekt. Jetzt möchte ich jedoch zeigen, welche neuen Wege sich dadurch in dem Modul öffnen.

sub WUup_Undef {
    my ( $hash, $arg ) = @_;
    RemoveInternalTimer($hash);
    return undef;
}


Da wir nun als Entwickler nicht auf eine sinnolse Zwangsjacke Rücksicht nehmen müssen, können wir uns 100% auf den Code IN der Sub konzentrieren und sehen, dass da ein Argument vollkommen sinnlos übergeben wird.

sub WUup_Undef {
    my $hash = shift;   # shift ist im übrigen schneller als Listen-Zuweisungen und es "konsumiert" @_, was in 99.9% der Fälle gewünscht ist
    RemoveInternalTimer($hash);
    return;
}


Und da $hash in etwa die gleiche Informationsgüte besitzt wie eine Datei die man "Datei" nennt, knicken wir das auch gleich:

sub WUup_Undef {
    RemoveInternalTimer(shift);
    return;
}


Kürzer, effizienter, richtiger (dank dem return - s.u.)

Aber wir fragen uns (also ich frage mich), warum man sich so unnötig einschränken soll, erwartet doch RemoveInternalTimer ein Argument, optional aber u.U. mehr. Jemand, der in FHEM bewanderter als ich ist, könnte jetzt sagen ob man sich dadurch irgendein Problem aufhalst, aber normalerweise würde ich die Generizität von Code an dieser Stelle nicht unnötig beschneiden:

sub WUup_Undef {
    RemoveInternalTimer(@_);
    return;
}



Über die "return undef;" Todsünde werde ich an anderer Stelle reden, da spielen auch noch andere Sachen rein (warum z.B. Rückgabewert von RemoveInternalTimer wegschmeissen und nicht  return RemoveInternalTimer(@_); aber genug der aufgemachten Fässer)


Witty House Infrastructure Processor (WHIP) is a modern and
comprehensive full-stack smart home framework for the 21st century.

RichardCZ

META-Anmerkung zum Forum: https://highlightjs.org/static/demo/ - code=perl tag funktioniert und könnte den Code in Farbe und bunt darstellen

Code (perl) Auswählen
sub WUup_Set {
    my ( $hash, $name, $cmd, @args ) = @_;

    return "\"set $name\" needs at least one argument" unless ( defined($cmd) );

    if ( $cmd eq "update" ) {
        WUup_stateRequestTimer($hash);
    }
    else {
        return "Unknown argument $cmd, choose one of update:noArg";
    }
}


@args wird komplett sinnlos befüllt, hauen wir's doch einfach weg. Perl 5.10 (Jahrgang 2007) dürfen wir jetzt annehmen, also ziehen wir doch die Parametervalidierung gleich glatt:

Code (perl) Auswählen
sub WUup_Set {
    my $hash = shift;
    my $name = shift;
    my $cmd  = shift // return qq{"set $name" needs at least one argument};

    return WUup_stateRequestTimer($hash) if ($cmd eq 'update');
    return "Unknown argument $cmd, choose one of update:noArg";
}


Und schon sieht der Code aus, als hätte ihn ein Mensch geschrieben. Man sieht auch ein wenig was passiert (oder hättet ihr gewusst, dass bei $cmd = 'update' der Rückgabewert von WUup_stateRequestTimer zurückgegeben wird?

Und da der Code so kurz und prägnant ist, haben wir sogar genug kognitive Kapazität frei um mal die Strings zu lesen:

"Unknown argument $cmd, choose one of update:noArg";

Hmm... Diese Meldung kommt aber auch, wenn man für $cmd just jenes 'noArg' eingegeben hat? Feature oder Bug? Ich weiß es nicht. Ich weiß nur, dass mir erst derart entblätterter Code seine Eigenheiten zeigt.
Witty House Infrastructure Processor (WHIP) is a modern and
comprehensive full-stack smart home framework for the 21st century.

CoolTux

Kurze Frage. Ist generell ein

sub test {
  my $a = shift;
  my $b = shift;
  my $c = shift;

}


einem
sub test {
  my ($a, $b, $c) = @_;

}


vor zu ziehen?
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://www.paypal.com/paypalme/MOldenburg
My FHEM Git: https://git.cooltux.net/FHEM/
Das TuxNet Wiki:
https://www.cooltux.net

CoolTux

#3
Zitat
Code (perl) Auswählen
sub WUup_Set {
    my $hash = shift;
    my $name = shift;
    my $cmd  = shift // return qq{"set $name" needs at least one argument};

    return WUup_stateRequestTimer($hash) if ($cmd eq 'update');
    return "Unknown argument $cmd, choose one of update:noArg";
}


Mich interessiert das
my $cmd  = shift // return qq{"set $name" needs at least one argument};
Ist das eine generelle Prüfung auf defined()?
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://www.paypal.com/paypalme/MOldenburg
My FHEM Git: https://git.cooltux.net/FHEM/
Das TuxNet Wiki:
https://www.cooltux.net

mahowi

Ich muß zugeben, daß ich das letzte Mal vor ca. 20 Jahren etwas ernsthafter in Perl programmiert habe. Hier hatte ich ein bestehendes Modul genommen und für meine Bedürfnisse umgebaut.

Wie vieles in FHEM, es ist nicht besonders schön aber funktioniert.  ;)

Ich werde mir aber heute abend gerne mal Deine Verbesserungsvorschläge ansehen und testen. Ich war dank der Diskussionen hier sowieso gerade dabei, den Code mal durchzusehen.
CUBe (MAX): HT, FK | CUBe (SlowRF): ESA2000WZ
JeeLink: LaCrosse | nanoCUL433: Smartwares SHS-51001-EU, EM1000GZ
ZME_UZB1: GreenWave PowerNode, Popp Thermostat | SIGNALDuino: HE877, X10 MS14A, Revolt NC-5462,  IT Steckdosen + PIR
tado° | Milight | HUE, Lightify | SmarterCoffee

rudolfkoenig

ZitatRemoveInternalTimer(@_);
UndefFn wird mit $hash und $name aufgerufen, RemoveInternalTimer mit dem InternalTimer $arg und optional mit $function.
Da $name nur selten $function ist, wuerde ich den zweiter Parameter sparen.

UndefFn kann den Shutdown mit einem wahren Rueckgabewert verzoegern, das Rueckgabewert von RemoveInternalTimer ist undef bzw. nicht spezifiziert.

Ich will mich nicht gegen Optimierungen aussprechen, man sollte sie aber immer testen, und nicht blind uebernehmen.
Weiterhin wird UndefFn eher selten aufgerufen, der Performance-Gewinn haelt sich an dieser Stelle sehr im Grenzen.

RichardCZ

#6
Zitat von: mahowi am 26 März 2020, 13:12:48
Ich muß zugeben, daß ich das letzte Mal vor ca. 20 Jahren etwas ernsthafter in Perl programmiert habe...

Alles gut. Wenn ich ein Modul rauspicke, dann als Vorbild welchen Weg man gehen kann.

Zitat von: CoolTux am 26 März 2020, 13:09:57
Mich interessiert das
my $cmd  = shift // return qq{"set $name" needs at least one argument};
Ist das eine generelle Prüfung auf defined()?

ja. Das ist der defined-OR, oder auch 'ÖÖÖÖR' Operator.  ;)

my $x = $w // $v;   # soviel wie my $x = defined $w ? $w : $v;

Den gibt es seit Perl 5.10, also seit 2007, offensichtlich hat "keiner" mehr was älteres als Perl 5.10 also nutzt das Leute.

Zitat von: CoolTux am 26 März 2020, 13:06:58
Ist generell ein

sub test {
  my $a = shift;
  my $b = shift;
  my $c = shift;

}


einem
sub test {
  my ($a, $b, $c) = @_;

}


vor zu ziehen?

Also wir vergessen ganz schnell, dass da $a und $b vorkamen.

$x = shift; ist generell einem ($x) = @_ vorzuziehen.
bei zwei shifts auch noch, bei drei ... grenzwertig und nicht wenn sie alleine für sich da stehen würden.

Aber prinzipiell ist das vorzuziehen, weil man damit eine erste und elegante (weil lesbare) Verteidigungslinie der Parametervalidierung aufbauen kann.

So richtig vorzuziehen sind "named Arguments" (benamte Parameter?), aber das kommt später.
https://www.perl.com/article/32/2013/7/6/Use-the-logical-or-and-defined-or-operators-to-provide-default-subroutine-variable-behaviour/
Witty House Infrastructure Processor (WHIP) is a modern and
comprehensive full-stack smart home framework for the 21st century.

RichardCZ

Zitat von: rudolfkoenig am 26 März 2020, 13:17:14
Ich will mich nicht gegen Optimierungen aussprechen, man sollte sie aber immer testen, und nicht blind uebernehmen.
Weiterhin wird UndefFn eher selten aufgerufen, der Performance-Gewinn haelt sich an dieser Stelle sehr im Grenzen.

Im obigen Fall ging es nicht um Optimierung, sondern um Generizität. Ich war mir unsicher, ob man durch das Wegschneiden eines Arguments nicht unnötig Funktionalität verliert.

Hinsichtlich Tests: Erstmal müssen wir aus dem Schlammloch kommen: Namespace
Dann können wir hübsche Testsuiten schreiben, dann übernimmt Perl für uns die Unit-Tests und klopft auf die Finger.

Ansonsten, ja: Ohne diesen Utopiezustand sind Änderungen ein Minenfeld. Vorsicht. Wer sich nicht sicher ist, soll nix ändern.

Für einige Änderungen muss ich nix testen, weil

sub blah {
  my ($x) = @_;

  return 'Das sage ich meiner Mamma!' unless (defined($x));
  return:
}


kann ich im Schlaf zu

sub blah {
  my $x = shift // return 'Das sage ich meiner Mamma!';

  return:
}


umwandeln und meine Hand ins Feuer legen, dass es das Gleiche tut.
Witty House Infrastructure Processor (WHIP) is a modern and
comprehensive full-stack smart home framework for the 21st century.

mahowi

So, ich habe jetzt mal etwas aufgeräumt: https://svn.fhem.de/trac/changeset/21521/trunk/fhem/FHEM/59_WUup.pm

Zumindest beschwert sich perlcritic nicht.  8)
Und es funktioniert auch noch alles, soweit ich das getestet habe.
CUBe (MAX): HT, FK | CUBe (SlowRF): ESA2000WZ
JeeLink: LaCrosse | nanoCUL433: Smartwares SHS-51001-EU, EM1000GZ
ZME_UZB1: GreenWave PowerNode, Popp Thermostat | SIGNALDuino: HE877, X10 MS14A, Revolt NC-5462,  IT Steckdosen + PIR
tado° | Milight | HUE, Lightify | SmarterCoffee

SCMP77

Zitat von: RichardCZ am 26 März 2020, 12:50:49Und da $hash in etwa die gleiche Informationsgüte besitzt wie eine Datei die man "Datei" nennt, knicken wir das auch gleich:

sub WUup_Undef {
    RemoveInternalTimer(shift);
    return;
}


Kürzer, effizienter, richtiger (dank dem return - s.u.)

Das sehe ich etwas anders. $hash ist das zentrale Element von FHEM. Und über kurz oder lang wird man es in der Methode wieder brauchen. Es mag erstmal wirklich effizienter sein, aber wenn man dann anfängt, die Methode zu erweitern, wird man garantiert das "shift" im Parameter von RemoveInternalTimer übersehen.

Aber Du hast recht, $hash sagt wenig aus und dieser Name wird in Wirklichkeit nicht seiner Bedeutung gerecht.

In Wirklichkeit ist es der Zugriff auf die Datenstruktur der Instanz des Moduls, so gut wie alles hängt daran.

Ich komme von der Java/C++-Seite und es gibt hier gewisse Übereinstimmungen mit dem "this" von Java, mit dem man auf die Attribute der Instanz einer Klasse zugreift.

Ich habe daher "$hash" bei meinen Modulen durch "$this" ersetzt, was der doch zentralen Bedeutung deutlich gerechter wird.

Mein Vorschlag daher:

sub WUup_Undef {
    my $this = shift;
    RemoveInternalTimer($this);
    return;
}


Und der Kode ist dann schon fast selbsterklärend, mit "RemoveInternalTimer($this)" lösche ich sämtliche zu dieser Instanz gehörigenden Timer.

Raspberry Pi 3 Model B mit Rasbian, SolvisMax, AVM DECT 200, Sonoff mit Tasmota geflasht

RichardCZ

Zitat von: SCMP77 am 26 März 2020, 21:52:55
Das sehe ich etwas anders. $hash ist das zentrale Element von FHEM. Und über kurz oder lang wird man es in der Methode wieder brauchen. Es mag erstmal wirklich effizienter sein, aber wenn man dann anfängt, die Methode zu erweitern, wird man garantiert das "shift" im Parameter von RemoveInternalTimer übersehen.

Eine Grundregel bei der Softwareentwicklung (nicht nur Perl - überall) lautet, dass man nicht auf Vorrat programmieren sollte. Also Code, den man eventuell irgendwann braucht (was aber nicht sicher ist). Ich kann konkret "auf Vorrat" programmieren, wenn ich konkret weiß, dass ich in X tagen/Wochen evtl. Monaten die Architektur aufpusten muss.

Bis dahin hält man den Code lean and mean. Das ist sprachunabhängig.

Ansonsten - kommt vermutlich auf die IDE an, bei mir leuchten shifts hellgrün in die Nacht - kaum zu übersehen. Wenn man sich an ein bestimmtes Layout einer Sub hält


Parameterübergabe & Validierung

Programmlogik

Return(-Block)


Und ein paar andere Regeln (die wir noch nicht besprochen haben) beherzigt (maximale Länge/Komplexität einer sub), dann wird das mit dem "Übersehen" eher nicht passieren.


Zitat
Ich habe daher "$hash" bei meinen Modulen durch "$this" ersetzt, was der doch zentralen Bedeutung deutlich gerechter wird.

Mein Vorschlag daher:

sub WUup_Undef {
    my $this = shift;
    RemoveInternalTimer($this);
    return;
}


Hm. Also das "this" nennt sich bei Perl $self, aber nur im OO-Kontext. Hier geht es tatsächlich nur um Argumente.

$param, $arg ... sowas

Aber ich bin diesbezüglich schmerzfrei. Conway sagt ja, der Code soll optimiert sein "aufs Lesen" (= Wartbarkeit). Wenn das für Dich oder andere wartbarer ist, dann nimm eine Variable.

Ich gestehe: Wenn die Subroutine länger ist - also nicht nur <5 Zeilen, dann habe ich vorne IMMER meinen my $xx = shift block und jongliere nicht mit den shifts direkt herum. Bei sowas wie oben mache ich jetzt nicht die Subroutine 30 Prozent länger mit der zusätzlichen Zeile. Direkt ein shift einer Routine "reinschieben" ist also eher ein Sonderfall für diese ultrakurzen Subs.
Witty House Infrastructure Processor (WHIP) is a modern and
comprehensive full-stack smart home framework for the 21st century.

RichardCZ

Zitat von: mahowi am 26 März 2020, 21:40:43
So, ich habe jetzt mal etwas aufgeräumt: https://svn.fhem.de/trac/changeset/21521/trunk/fhem/FHEM/59_WUup.pm

Zumindest beschwert sich perlcritic nicht.  8)
Und es funktioniert auch noch alles, soweit ich das getestet habe.

Nice! (mir fehlt das Daumen hoch Emoji hier)


$attr{$name}{unit_windspeed}      //= "km/h";
$attr{$name}{unit_solarradiation} //= "lux";
$attr{$name}{round}               //= 4;


(vertical alignment, PBP s. 26)

Und jetzt lege man bitte die Hand aufs Herz und vergleiche die 3 zeilen oben mit

    $attr{$name}{unit_windspeed} = "km/h"
      if ( !defined( $attr{$name}{unit_windspeed} ) );
    $attr{$name}{unit_solarradiation} = "lux"
      if ( !defined( $attr{$name}{unit_solarradiation} ) );
    $attr{$name}{round} = 4 if ( !defined( $attr{$name}{round} ) );


und was davon qualifiziert für das Attribut "Kraut & Rüben"? Und genau das ist die Evolution.

Witty House Infrastructure Processor (WHIP) is a modern and
comprehensive full-stack smart home framework for the 21st century.

CoolTux

Zitat von: RichardCZ am 26 März 2020, 23:03:15
Hm. Also das "this" nennt sich bei Perl $self, aber nur im OO-Kontext. Hier geht es tatsächlich nur um Argumente.

Ich glaube nicht nur im OO-Kontext. Ich sehe $self relativ oft auch in normalen Perlscripten ohne OO. Meist ist es der Basis Hash des gesamten Scriptes.
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://www.paypal.com/paypalme/MOldenburg
My FHEM Git: https://git.cooltux.net/FHEM/
Das TuxNet Wiki:
https://www.cooltux.net

SCMP77

#13
Zitat von: RichardCZ am 26 März 2020, 23:03:15
Eine Grundregel bei der Softwareentwicklung (nicht nur Perl - überall) lautet, dass man nicht auf Vorrat programmieren sollte. Also Code, den man eventuell irgendwann braucht (was aber nicht sicher ist). Ich kann konkret "auf Vorrat" programmieren, wenn ich konkret weiß, dass ich in X tagen/Wochen evtl. Monaten die Architektur aufpusten muss.

Das hat hier nichts mit aufpusten zu tun, außerdem gehen hier die Sichtweisen in der Informatik schon auseinander. Es gibt da nicht DIE richtige Lösung. Es gibt Programmierrichtlinien, die sehen auch diesen Gesichtspunkt und alles unnötige "wegoptimieren" wird mittlerweile auch nicht unbedingt als Optimum angesehen, denn letzteres kann die Lesbarkeit von Code stark beeinträchtigen.

Perl hat eben nunmal den Nachteil, dass es keine klare Parameterdefinition besitzt, wie viele andere Sprachen. Wenn man an dieser Stelle dann noch weiter optimiert, dann ist es um die Lesbarkeit komplett geschehen. Wenn ich dann die Bedeutung des Parameters im vorliegenden Fall mir aus der Definition von RemoveInternalTimer holen muss, dann ist das ein klares Zeichen dafür, das zuviel "optimiert" wurde.

Oder man müsste die Parameterdefinition verpflichtend vor die Methode tun, aber die Realität ist eine andere, Dokumentationen im Code findet man sehr selten, gerade solchen Projekten wie FHEM oder auch anderen.


Und wie gesagt, $hash ist in FHEM eben nicht irgendein Parameter, es entspricht wirklich weitgehend dem $self/this, die man aus dem OO Sprachen kennt. Es wird hier aber eben von "Hand" initialisiert, teilweise durch FHEM selber, teilweise durch Define oder weiteren Routinen. Es ist aber quasi ein Container, in die die meisten Module ihre instanzierten Daten ablegen. Wie gesagt, es ist eben nicht bloß ein Parameter.

Wenn man mehrer Gerätedefinitionen des gleichen Moduls definiert hat, dann gibt es immer ein eigenes "$hash" pro Geräteinstanz, genau wie es ein $self für jede Instanz einer Klasse in einer OO-Sprache gibt. Die Parallelen sind aus meiner Sicht sehr deutlich. In Python übergibt man diese Self-Instanz den Methoden immer als ersten Parameter wie meist in FHEM! Und dort hat sich "self" durchgesetzt.


Die Zuweisung "my $self = shift" ist aus meiner Sicht daher notwendig, weiter sollte man nicht optimieren.
Raspberry Pi 3 Model B mit Rasbian, SolvisMax, AVM DECT 200, Sonoff mit Tasmota geflasht

RichardCZ

#14
Zitat von: SCMP77 am 27 März 2020, 00:04:28
Das hat hier nichts mit aufpusten zu tun, außerdem gehen hier die Sichtweisen in der Informatik schon auseinander. Es gibt da nicht DIE richtige Lösung. Es gibt Programmierrichtlinien, die sehen auch diesen Gesichtspunkt und alles unnötige "wegoptimieren" wird mittlerweile auch nicht unbedingt als Optimum angesehen, denn letzteres kann die Lesbarkeit von Code stark beeinträchtigen.

Klar. Ich sagte ja, ich bin da schmerzfrei. Der Sinn der Übung hier ist ja auch nicht "Perl Golf" zu betreiben (Wettbewerbe um das kürzeste Perl Programm das eine gegebene Funktion erfüllt - natürlich dann unwartbar).

Es gibt ein paar pro/contra Variable, aber wir unterhalten uns hier über Peanuts.
Contra:
* Perl hat keinen JIT compiler (cperl hat wohl ein wenig was) und so ist das durchreichen einer variable ein wenig langsamer als der direkte shift;
* Falls eine Sub < 5 Zeilen ist, findet man sich da immer zurecht
Pro:
* Lesbarkeit, Einheitlichkeit; Wird in längeren Subs so gemacht, sollte in kürzeren auch so sein.

Dann könnte man darüber reden, dass eine Sub, die eigentlich nur aus 2 Zeilen besteht irgendwo fehl am Platz ist, aber ... geschenkt. IMHO: Dieses Detail soll jeder machen wie er lustig ist.

Zitat
Perl hat eben nunmal den Nachteil, dass es keine klare Parameterdefinition besitzt, wie viele andere Sprachen. Wenn man an dieser Stelle dann noch weiter optimiert, dann ist es um die Lesbarkeit komplett geschehen. Wenn ich dann die Bedeutung des Parameters im vorliegenden Fall mir aus der Definition von RemoveInternalTimer holen muss, dann ist das ein klares Zeichen dafür, das zuviel "optimiert" wurde.

Prinzipiell ja. Die Benamung der Variablen ist sehr wichtig. Dem widmet PBP ein ganzes Kapitel -> Seite 36 - 49
Im Endeffekt baut Conway da eine Grammatik auf wie Variablennamen auszusehen haben, damit wollte ich euch jetzt nicht gleich traktieren.

Ob ein loses Typsystem Vorteil oder Nachteil ist, hängt vom Auge des Betrachters ab. Ein loses Typsystem wie bei Perl ist definitiv Teil der Mustang-DNA (Zähne einkicken) und gefährlich wenn man nicht aufpasst, bzw. bestimte Schemata nicht befolgt. Auf der anderen Seite erlaubt es durchaus lesbareren Code zu schreiben, Weil man natürlich mit dem Inhalt einer Variablen umgeht und nicht mit dem Typ.

Denke da nur an C und andere "atoi" Konsorten, die sich ja schon krumm machen müssen um aus "1" -> 1 zu machen. Und bis man sich durch den ganzen conversion und typecast Code gehangelt hat, hat man vergessen worum es inhaltlich ging.  ;)

Zitat
Die Zuweisung "my $self = shift" ist aus meiner Sicht daher notwendig, weiter sollte man nicht optimieren.

Prinzipiell ja. Es gibt genügend andere Baustellen in FHEM wo man wesentlich mehr optimieren kann, da können wir uns ein wenig expliziten Code leisten. Also: bei kurzen Subs < 5 zeilen die hübsch und lesbar sind, würde ich beides akzeptieren, ansonsten: bin dafür ne extra Variable zwecks Lesbarkeit zu haben. Nur mein Vorschlag - nicht in Stein gemeiselt. Ich denke jeder hat verstanden es geht um Lesbarkeit. Ein "@_-shift" in Zeile 30 einer 70 Zeilen langen sub gehört nicht dazu (insbesondere weil dort ja legitime "shift @array" rumlungern können und da stimme ich 100% zu, dann könnte man das leicht übersehen!

Allerdings empfehle ich doch stark das Ding nicht $self zu nennen, sondern

$arg (Skalar)
$args_hr (Falls ein hashref mit Argumenten antanzt)
$args_lr (falls ein listref mit Argumenten antanzt)
@args (wenn ne Liste kommt)
%args (Wenn ein Hash kommt)

(oder jemand ist sich nicht sicher, dann schlägt er hier auf und fragt für seinen konkreten Fall - deswegen haben wir ja die Perl Ecke)

$self würde ich echt für OO code lassen. Ja, man kann in Perl auch objektorientiert schreiben.  ;)
Witty House Infrastructure Processor (WHIP) is a modern and
comprehensive full-stack smart home framework for the 21st century.

CoolTux

Zitat von: RichardCZ am 27 März 2020, 09:11:35
$self würde ich echt für OO code lassen. Ja, man kann in Perl auch objektorientiert schreiben.  ;)

Was AutoShuttersControl in kleinen Teilen macht  :)
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://www.paypal.com/paypalme/MOldenburg
My FHEM Git: https://git.cooltux.net/FHEM/
Das TuxNet Wiki:
https://www.cooltux.net

mahowi

Zu perlcritic -3:
Zitat von: RichardCZ am 27 März 2020, 10:10:14
$ perlcritic -3 59_WUup.pm
Subroutine "WUup_stateRequestTimer" does not end with "return" at line 184, column 1.  See page 197 of PBP.  (Severity: 4)
Reused variable name in lexical scope: $version at line 214, column 5.  Invent unique variable names.  (Severity: 3)
Regular expression without "/x" flag at line 225, column 20.  See page 236 of PBP.  (Severity: 3)
Cascading if-elsif chain at line 243, column 9.  See pages 117,118 of PBP.  (Severity: 3)
Regular expression without "/x" flag at line 243, column 22.  See page 236 of PBP.  (Severity: 3)
Regular expression without "/x" flag at line 246, column 25.  See page 236 of PBP.  (Severity: 3)
Regular expression without "/x" flag at line 262, column 25.  See page 236 of PBP.  (Severity: 3)
Subroutine "WUup_receive" does not end with "return" at line 315, column 1.  See page 197 of PBP.  (Severity: 4)


Und dann lässt man es erstmal gut sein. perlcritic sollte auf Stufe 5 (-5 oder kein Argument) definitiv den Schnabel halten. Wenn man es auf -4 zum Schweigen bringt ist das super, den Rest schauen wir uns später an.
Wenn ich das richtig verstehe, sollte /x bei Regexes genutzt werden, um Whitespaces zu ignorieren. D.h. aber doch auch, daß es überflüssig ist, wenn keine im String vorkommen.

Die von perlcritic "bemängelten" Zeilen sind:
Code (perl) Auswählen
    $datestring =~ s/:/%3A/g;
[...]
        if ( $key =~ /\w+f$/ ) {
            $value = UConv::c2f( $value, $rnd );
        }
        elsif ( $key =~ /\w+mph.*/ ) {
[...]
        elsif ( $key =~ /.*rainin$/ ) {
            $value = UConv::mm2in( $value, $rnd );


Bei keinem der Regexes können Leerzeichen o.ä. vorkommen. Einmal wird ":" durch "%3A" für die URL ersetzt, bei den anderen werden die zu übergebenden Parameter gefiltert, die durch Attribute fest vorgegeben sind. Damit sehe ich keine Möglichkeit, die Regexes wirklich zu verbessern.

Das "if-elsif"-Massaker will ich mir eh mal ansehen. Das ist wirklich nicht gerade übersichtlich und macht es auch nicht einfacher, neue Parameter zum Umrechnen hinzuzufügen.
CUBe (MAX): HT, FK | CUBe (SlowRF): ESA2000WZ
JeeLink: LaCrosse | nanoCUL433: Smartwares SHS-51001-EU, EM1000GZ
ZME_UZB1: GreenWave PowerNode, Popp Thermostat | SIGNALDuino: HE877, X10 MS14A, Revolt NC-5462,  IT Steckdosen + PIR
tado° | Milight | HUE, Lightify | SmarterCoffee

SCMP77

Zitat von: RichardCZ am 27 März 2020, 09:11:35
Es gibt ein paar pro/contra Variable, aber wir unterhalten uns hier über Peanuts.
Contra:
* Perl hat keinen JIT compiler (cperl hat wohl ein wenig was) und so ist das durchreichen einer variable ein wenig langsamer als der direkte shift;
* Falls eine Sub < 5 Zeilen ist, findet man sich da immer zurecht
Pro:
* Lesbarkeit, Einheitlichkeit; Wird in längeren Subs so gemacht, sollte in kürzeren auch so sein.
Dann könnte man darüber reden, dass eine Sub, die eigentlich nur aus 2 Zeilen besteht irgendwo fehl am Platz ist, aber ... geschenkt. IMHO: Dieses Detail soll jeder machen wie er lustig ist.

Mir geht es hier um das Allgemeine. Optimierung ja, aber nur soweit es sinnvoll ist.

Solche Aufrufe sind in FHEM in Wirklichkeit recht selten. Es ist Event-basiert und so häufig (im Vergleich zu der heutigen Leistung der Prozessoren) liefert die Hardware keine Events, so dass diese Art der Optimierung kaum einen Gewinn bringen wird.

FHEM arbeitet die Events serialisiert ab. Und daher kommt es auf jedes einzelne Modul an. Leider gibt es Module, welche länger andauernde Zugriffe nicht in einem BlockingCall oder entsprechend anderem Zugriff verstecken. Das hat dann zur Folge, dass bis zur Antwort das System steht. Das fällt meist nicht auf, weil die Antwort aus dem eigenen Netz stammt. Aber ich habe vor ein paar Tagen gelesen – ich glaube es war das SONOS-Modul, dass FHEM nach einem Firmware-Update ab und zu 3 Minuten blockieren würde. Bei einer richtigen Nutzung der zur Verfügung stehenden FHEM-API, würde das erst gar nicht auftreten. Aber da solche Module unnötig Zeit selbst bei funktionierenden Geräten verbraten, wäre dort die Optimierung viel sinnvoller.

Sicher, wenn in einem Modul die Daten mittels langer Schleifen mit Methodenaufrufen noch umfangreich weiter verarbeitet werden, dann wäre dort Optimierung sinnvoll, aber ich glaube, das betrifft nur wenig Module.

Zitat von: RichardCZ am 27 März 2020, 09:11:35Allerdings empfehle ich doch stark das Ding nicht $self zu nennen, sondern

$arg (Skalar)
$args_hr (Falls ein hashref mit Argumenten antanzt)
$args_lr (falls ein listref mit Argumenten antanzt)
@args (wenn ne Liste kommt)
%args (Wenn ein Hash kommt)


Das wird dem ,,$hash" nicht gerecht. Wie gesagt, $hash ist die zentrale geräteabhängige Struktur, auf die sich viele Module als auch die FHEM-API stützen.

In dieser Struktur lesen und schreiben die Geräteinstanzen und auch die API-Methoden. Es sind  dort ganz bestimmte Daten unter bestimmten Keywörtern zu finden (DeviceName etc.). Dass API-Methoden dort auch reinschreiben, macht mir auch gewisse Bauchschmerzen, denn das kann zu Überschneidungen und unerwartete Reaktionen führen. Nur als Beispiel, so schreibt die DevIO unten den Keywörtern FD (FileDescriptor), CD (Connection) etwas rein. Wenn das aufrufende Modul oder andere Methoden die ebenfalls verwenden würde, würde es schief gehen. Es hat aber auch den Nachteil, dass eineGeräteinstanz nicht mehr als eine Verbindung zum Gerät aufbauen kann.  Das kommt normalerweise glücklicherweise auch nicht vor, aber das sind natürlich Einschränkungen, die nicht sein müssen.

Hier sind aus meiner Sicht die Baustellen, es fehlen einfach klare Definitionen, welche so etwas  verhindert.

Und wie gesagt, der Name $hash ist für diese zentrale Datenstruktur sehr unglücklich gewählt. Und da sollte man schon sich Gedanken machen einen sinnvolleren für diesen zu finden.

Ich schlage $self oder wenn man self wegen dem Verwechseln bzgl. OO nicht verwenden will, ,,$this" Java-entsprechend vor.

Für mich wäre die Definition eines einheitlichen Formats der Parameterübergabe wichtig und dazu würde eben ein fester sprechender Name für $hash gehören.

Viele Grüße
   Stefan
Raspberry Pi 3 Model B mit Rasbian, SolvisMax, AVM DECT 200, Sonoff mit Tasmota geflasht

RichardCZ

Zitat von: SCMP77 am 27 März 2020, 12:16:14
Das wird dem ,,$hash" nicht gerecht. Wie gesagt, $hash ist die zentrale geräteabhängige Struktur, auf die sich viele Module als auch die FHEM-API stützen.

In dieser Struktur lesen und schreiben die Geräteinstanzen und auch die API-Methoden. Es sind  dort ganz bestimmte Daten unter bestimmten Keywörtern zu finden (DeviceName etc.).

Ok - danke für die Ausführungen, das macht es mir schon klarer. Warum nennt man es dann nicht $fhem_data, oder - wenn die Struktur so FHEM-inhärent ist wie Du sagst und jeder Entwickler im Grunde weiß was er da vorfindet bzw. mit einer bestimmten FHEM-spezifischen Struktur rechnet... $F_hash, $FHEM_hr

? sowas in der Art ?

Und ein Neuling sieht dank des upper case auch gleich ein Ausrufezeichen "Aha - hier ist was zentrales"

Zitat
Für mich wäre die Definition eines einheitlichen Formats der Parameterübergabe wichtig und dazu würde eben ein fester sprechender Name für $hash gehören.

Ja, onging process. Wir baden gerade unsere Hände drin.  ;)
Witty House Infrastructure Processor (WHIP) is a modern and
comprehensive full-stack smart home framework for the 21st century.

Beta-User

My 2ct als Hobby-Entwicker:

Gibt es schlagende Gründe, warum man diesen - möglicherweise schillernden - "Namen" "$hash" diskutiert?

Man hätte es irgendwann in der Vergangenheit vielleicht "schöner" benennen können ($this_FHEMdevice_central_reference_point), aber wer auch nur mal ansatzweise in https://wiki.fhem.de/wiki/DevelopmentModuleIntro#Der_Hash_einer_Ger.C3.A4teinstanz gesehen hat: Das taucht da als 3. Ordnungspunkt nach einigen wenigen Zeilen Text auf.

Ich diskutiere gerne und viel mit, aber an dem Punkt fehlt mir so ein kleines bißchen das Verständnis dafür, dass
- irgendwer es heute an irgendeiner Stelle im Code ANDERS macht;
- und dann darüber auch noch diskutiert wird.

Jeder Modulentwickler, der die Einführung gelesen hat (die gab's auch mal auf English, btw.), sollte sich darüber dann nicht mehr wundern, es ist schlicht und ergreifend gelebte Praxis, dass die eigene Modulinstanz (oder wie auch immer man das nennen will) so genannt wird. FHEM-BP, sozusagen...

Und jeder neue, der die Intro nicht lesen will, wird vermutlich schnell eine Antwort erhalten, was es damit auf sich hat und tut im Verhältnis zu allen anderen Mitmaintainern gut daran, sich an genau diesen usus zu halten...

Wie gesagt, nur meine bescheidene Meinung als Hobbyist, und klar, "tim..." gilt auch hier...
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

RichardCZ

Zitat von: mahowi am 27 März 2020, 12:14:01
Zu perlcritic -3:Wenn ich das richtig verstehe, sollte /x bei Regexes genutzt werden, um Whitespaces zu ignorieren. D.h. aber doch auch, daß es überflüssig ist, wenn keine im String vorkommen.

Hehe  ;D -3 habe ich als eine Art "gucken wir mal durchs Fernglas" Ausblick gemacht, weil 59_WUup schon bei -4 eine ganz gute Figur macht.
Ich habe natürlich darauf spekuliert, dass das Neugierde weckt.

Reguläre Ausdrücke ... Riesenthema

/x macht ein wenig mehr als nur Whitespace zu erlauben

$key =~ /.*rainin$/

könnte man (innerhalb eines if eher nicht, aber im code schon) schreiben als

$key =~ /.*         # well well well, what do we have here? Death to DOT STAR!
         rainin     # this is the substring we want to match
         $          # and this is the end of the string. Not multi-line string, that is.
        /x


Nun, das ist zwar für diese kleine Regex übertrieben, aber ich könnte jetzt 100 andere Beispiele aus FHEM fischen, denen ein strukturiertes Aufklabüstern der Regex sehr helfen würde. Und ähnlich der Diskussion shift/variable in kurzen Subs: Man sollte es immer machen (visuell separieren) Also immer /x
Aber es ist Severity 3 - die Hölle friert erstmal nicht zu wenn man das nicht macht.

Unabhängig davon, dass die Regex eigentlich $key =~ /rainin$/ lauten sollte, könnte sie auch

$key =~ m{rainin \z}xms lauten. Das ist Level 9000.

Oder erstmal

$key =~ /rainin $/x

Space als nichtfunktionaler visueller Separator. Angenommen man möchte tatsächlich einen Whitespace matchen (hypothetisch):

$key =~ /\s+        # there shall be at least one leading whitespace
         rainin     # followed by our core match string
         \s         # and exactly one trailing whitespace before
         $/x;       # line end


Und dann fliegt einem die Regex auch nicht um die Ohren, wenn mal jemand statt einem Space ein Tab oder ein non-breaking space oder sonstwas verwendet.

Zitat
Bei keinem der Regexes können Leerzeichen o.ä. vorkommen.

Ja. Siehe oben. Es geht auch nicht darum was im matched string vorkommt, sondern wie Du die Regex schreiben/formattieren kannst, damit sie lesbarer wird.
Witty House Infrastructure Processor (WHIP) is a modern and
comprehensive full-stack smart home framework for the 21st century.

Wzut

Zitat von: SCMP77 am 27 März 2020, 12:16:14
dazu würde eben ein fester sprechender Name für $hash gehören.
egal wie du ihn nennst oder wie auch immer er in Zukunft sich nennen wird : hast du dir mal zweistufige Module angeschaut die miitels Dispatch / Parse arbeiten ?
Da muss man echt aufpassen welcher $hash es denn nun gerade ist, der Modul-Device eigene (dein $self) oder aber der des aufrufendes Moduls :) An dem Punkt muss ich immer höllisch aufpassen das ein schnelles $hash->{lala} = 1 nicht im falschen Device landet ....
Maintainer der Module: MAX, MPD, UbiquitiMP, UbiquitiOut, SIP, BEOK, readingsWatcher

RichardCZ

Zitat von: Beta-User am 27 März 2020, 13:10:23
Man hätte es irgendwann in der Vergangenheit vielleicht "schöner" benennen können ($this_FHEMdevice_central_reference_point), aber wer auch nur mal ansatzweise in https://wiki.fhem.de/wiki/DevelopmentModuleIntro#Der_Hash_einer_Ger.C3.A4teinstanz gesehen hat: Das taucht da als 3. Ordnungspunkt nach einigen wenigen Zeilen Text auf.

Ja, gelesen. Mir ist irgendwie nie in den Sinn gekommen, dass man eine Argumentvariable wortwörtlich übernimmt. Auf der anderen Seite, die Menge an konfigurierten Apache Servern mit "example.com" in der Konfig spricht eine andere Sprache.  :)

Wenn das gelebte Praxis ist, dann ist das ein gewichtiges Argument es "erstmal so zu lassen", weil man ansonsten nur unnötig Staub aufwirbelt.
Auf der anderen Seite, wenn wir es irgendwann schaffen sollten ein neues schönes Mustermodul zu entwerfen, mit superduper Namespace handling etc. dann würde ich mir schon wünschen, dass in diesem Mustermodul sowas nicht mehr vorkommt.

Da werden eh mehr alte Zöpfe fallen, da kommt es auf diesen nicht mehr an.

Witty House Infrastructure Processor (WHIP) is a modern and
comprehensive full-stack smart home framework for the 21st century.

Beta-User

Zitat von: RichardCZ am 27 März 2020, 13:28:02
Wenn das gelebte Praxis ist, dann ist das ein gewichtiges Argument es "erstmal so zu lassen", weil man ansonsten nur unnötig Staub aufwirbelt.
Auf der anderen Seite, wenn wir es irgendwann schaffen sollten ein neues schönes Mustermodul zu entwerfen, mit superduper Namespace handling etc. dann würde ich mir schon wünschen, dass in diesem Mustermodul sowas nicht mehr vorkommt.

Da werden eh mehr alte Zöpfe fallen, da kommt es auf diesen nicht mehr an.
...we will see...

Zitat von: Wzut am 27 März 2020, 13:26:10
egal wie du ihn nennst oder wie auch immer er in Zukunft sich nennen wird : hast du dir mal zweistufige Module angeschaut die miitels Dispatch / Parse arbeiten ?
Da muss man echt aufpassen welcher $hash es denn nun gerade ist, der Modul-Device eigene (dein $self) oder aber der des aufrufendes Moduls :) An dem Punkt muss ich immer höllisch aufpassen das ein schnelles $hash->{lala} = 1 nicht im falschen Device landet ....
An zweistufigen Modulen kenne ich nur MySensors näher, aber da hat irgendwer in der Vergangenheit das so gelöst, dass er sinnigerweise die hashes, die nicht "$hash" (des IO-Devices) sind, dann "$client" genannt hat...

Übrigens, falls jemand ein anderes zweistufiges Mustermodul für "packages" sucht, aber meinen "Künsten" bei MySensors nicht traut: ntruchsess hatte wohl nicht nur da auch (weitestgehend) im eigenen Namespace gearbeitet, mWn. ticken die MQTT-"classic" (und "FIRMATA"-) Module ganz genauso, die stammen aus derselben Quelle...
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

SCMP77

Zitat von: Wzut am 27 März 2020, 13:26:10
egal wie du ihn nennst oder wie auch immer er in Zukunft sich nennen wird : hast du dir mal zweistufige Module angeschaut die miitels Dispatch / Parse arbeiten ?
Da muss man echt aufpassen welcher $hash es denn nun gerade ist, der Modul-Device eigene (dein $self) oder aber der des aufrufendes Moduls :) An dem Punkt muss ich immer höllisch aufpassen das ein schnelles $hash->{lala} = 1 nicht im falschen Device landet ....

$self - oder wie auch immer es nennt, gilt eben immer nur für das eigene "Objekt". Das ist eben so wie bei der OOP, mit $self oder this oder was auch immer greift man immer nur auf die eigenen Daten zu. Daher halte ich $self oder $this nach wie vor für die sinnvollste Definition, weil damit deutlich wird, es sind genau die Daten des Moduls, in dem ich gerade bin. Wenn man auf Daten fremder Objekte zugreift, darf man es natürlich nicht mehr $self nennen, es muss dann einen anderen modulspezifischen Namen erhalten, welcher kommt auf die Anwendung an. Natürlich würde man bei echter OOP nie einem anderen Objekt die kompletten Daten zur Manipulation freigeben, aber wird sind hier leider nicht bei einem OO Projekt und damit existieren hier diese Grenzen leider nicht. OOP gibt es eben nicht ganz ohne Grund, einer der wichtigen Gründe dafür ist eben die Kapselung, dass es eben nicht versehentlich passiert, dass man die Daten eines fremden Objekts manipuliert, die einem gar nicht gehören und wenn, dann immer nur unter der Kontrolle des anderen Objekts.
Raspberry Pi 3 Model B mit Rasbian, SolvisMax, AVM DECT 200, Sonoff mit Tasmota geflasht

KernSani

Ich würde gerne nochmal diese Zeilen hier augreifen:


$attr{$name}{unit_windspeed}      //= "km/h";
$attr{$name}{unit_solarradiation} //= "lux";
$attr{$name}{round}               //= 4;

Das sieht schön aus und ist sicherlich Perl-technisch super. Ich gebe allerdinmgs zwei Punkte zu bedenken:
Für einen Perl-Noob (die wir ja zu großen Teilen hier sind) ist das völlig unverständliches Zeug, ein "if" kann dagegen jeder lesen, der ein bisschen programmieren kann. Ich werde das mal an mir persönlich ausprobieren, wenn ich das irgendwo in meinem Code verwende und in 4 Wochen nochmal drauf schaue ;-) Das ist aber garnicht mein eigentlicher Kritikpunkt, was auch bedacht werden sollte:
Wenn schon aufgeräumt wird, dann könnte man auch gleich FHEM-technisch aufräumen (das kann Richard sicher noch nicht leisten) und dazu würde m.E. gehören, dass $attr nicht direkt manipuliert wird, sondern CommandAttr verwendet wird.

Grüße,

Oli
   
RasPi: RFXTRX, HM, zigbee2mqtt, mySensors, JeeLink, miLight, squeezbox, Alexa, Siri, ...

Wzut

Zitat von: KernSani am 27 März 2020, 16:23:37
dass $attr nicht direkt manipuliert wird, sondern CommandAttr verwendet wird.
IMHO hatten wir die Diskussion schon vor ner Weile hier, nicht unbedingt nur auf $attr bezogen sondern alles andere auch wofür fhem.pl fertige Funktionen anbietet.
So hatte ich zum Beispiel in 14_CUL_MAX auch das schöne Beipiel gefunden : 
$dhash->{READINGS}{msgcnt}{VAL} &= 0xFF;
$dhash->{READINGS}{msgcnt}{TIME} = TimeNow();
Maintainer der Module: MAX, MPD, UbiquitiMP, UbiquitiOut, SIP, BEOK, readingsWatcher

KernSani

Zitat von: Wzut am 27 März 2020, 17:04:48
IMHO hatten wir die Diskussion schon vor ner Weile hier, nicht unbedingt nur auf $attr bezogen sondern alles andere auch wofür fhem.pl fertige Funktionen anbietet.
Jep, gilt natürlich nicht nur für $attr.
Wollte nur im konkreten Fall darauf hinweisen, dass ein schöner perlcritics Score nicht das Einzige ist worauf man achten sollte, wenn man ohnehin schon alles anfasst.


Gesendet von iPhone mit Tapatalk
RasPi: RFXTRX, HM, zigbee2mqtt, mySensors, JeeLink, miLight, squeezbox, Alexa, Siri, ...

RichardCZ

Zitat von: KernSani am 27 März 2020, 16:23:37
(thema //= )
Das sieht schön aus und ist sicherlich Perl-technisch super. Ich gebe allerdinmgs zwei Punkte zu bedenken:
Für einen Perl-Noob (die wir ja zu großen Teilen hier sind) ist das völlig unverständliches Zeug, ein "if" kann dagegen jeder lesen, der ein bisschen programmieren kann.

Da stimme ich zu und schicke gleich vorab, dass ich das immer im HInterkopf habe. Ich habe einige (jüngere) Kollegen, die jagen jedem neuen Feature von Perl hinterher.

Da wird der if .. else code schon mal zu given ... when nur um dann später wieder zu fliegen, wenn es mit dem Smartmatch-Operator (~~) doch nicht geklappt hat.

Wer jetzt nicht weiß, wovon die Rede ist: Ihr habt nix verpasst.

Ich betrachte es als selbstverständlichen Teil meiner Aufgabe hier den Entwicklern gewissermaßen ein "Extrakt" eines Modernen Perl zu liefern ohne die Irrungen und Wirrungen der letzten 15 Jahre. Nein, wir werden keine 200 PBP Regeln durchexerzieren, nein wir werden nicht jedes hippe Feature für Perl-Insider propagieren, wo man dann erstmal ne halbe Stunde bei Google hängt um herauszufinden was der UFO, der SATURN und der YADDA YADDA Operator macht.

Bitte keine Sorge, ich bin kein unbedachter junger Hüpfer.  ;)

So. Und jetzt kommen wir zu dem eigentlich wichtigen:

Zitat
Wenn schon aufgeräumt wird, dann könnte man auch gleich FHEM-technisch aufräumen (das kann Richard sicher noch nicht leisten)

B.I.N.G.O.

Ich muss noch wesentlich mehr über FHEM auf der funktionalen Ebene wissen um das zu können. Da biin ich dabei das auszuloten. Ongoing Process...

Witty House Infrastructure Processor (WHIP) is a modern and
comprehensive full-stack smart home framework for the 21st century.

mahowi

Zitat von: KernSani am 27 März 2020, 16:23:37
Wenn schon aufgeräumt wird, dann könnte man auch gleich FHEM-technisch aufräumen (das kann Richard sicher noch nicht leisten) und dazu würde m.E. gehören, dass $attr nicht direkt manipuliert wird, sondern CommandAttr verwendet wird.
Wie schon gesagt, ich hatte hier ein bereits vorhandenes Modul genommen und für meine Zwecke umgebaut. Zumindest in https://wiki.fhem.de/wiki/DevelopmentModuleAPI finde ich leider nichts zu CommandAttr. Wo finde ich denn eine Erklärung dazu?
CUBe (MAX): HT, FK | CUBe (SlowRF): ESA2000WZ
JeeLink: LaCrosse | nanoCUL433: Smartwares SHS-51001-EU, EM1000GZ
ZME_UZB1: GreenWave PowerNode, Popp Thermostat | SIGNALDuino: HE877, X10 MS14A, Revolt NC-5462,  IT Steckdosen + PIR
tado° | Milight | HUE, Lightify | SmarterCoffee

CoolTux

Zitat von: mahowi am 27 März 2020, 21:02:20
Wie schon gesagt, ich hatte hier ein bereits vorhandenes Modul genommen und für meine Zwecke umgebaut. Zumindest in https://wiki.fhem.de/wiki/DevelopmentModuleAPI finde ich leider nichts zu CommandAttr. Wo finde ich denn eine Erklärung dazu?

Das ist eine ewig offene Baustelle und die alten Hasen würden Dir sagen schaue in den Code von fhem.pl
Im Grunde gibt es mittlerweile für alles eine Funktion in der fhem.pl. Also das Setzen von Attributen, Readings. Auslesen von Attributen und Readings und so weiter.
Es sollte vermieden werden Hash selbst von Hand zu befüllen wo FHEM interne Dinge gesetzt werden. Readings, Timestamp, Attribute und so weiter. Beispiel $attr
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://www.paypal.com/paypalme/MOldenburg
My FHEM Git: https://git.cooltux.net/FHEM/
Das TuxNet Wiki:
https://www.cooltux.net

mahowi

Schade, daß die Doku leider etwas vernachlässigt wird. Dann werde ich mir wohl mal den Code zu Gemüte führen müssen.
CUBe (MAX): HT, FK | CUBe (SlowRF): ESA2000WZ
JeeLink: LaCrosse | nanoCUL433: Smartwares SHS-51001-EU, EM1000GZ
ZME_UZB1: GreenWave PowerNode, Popp Thermostat | SIGNALDuino: HE877, X10 MS14A, Revolt NC-5462,  IT Steckdosen + PIR
tado° | Milight | HUE, Lightify | SmarterCoffee

CoolTux

Das mit der Doku aka Developer Guide ist ein bekanntes Problem in fast allen Projekten. Markus hat das komplette Developer Guide fast alleine aufgesetzt. Das ist schon Wahnsinn. Aber so war es bis jetzt halt. Funktionen hielten Einzug in fhem.pl und wurden dort und vielleicht im Forum kurz dokumentiert. Das war es. Erst in letzter Zeit, so zwei drei Jahre, kommen Dokumentationen in die Developer Guides von neuen Funktionen.
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://www.paypal.com/paypalme/MOldenburg
My FHEM Git: https://git.cooltux.net/FHEM/
Das TuxNet Wiki:
https://www.cooltux.net

KernSani

Zitat von: mahowi am 27 März 2020, 21:17:26
Schade, daß die Doku leider etwas vernachlässigt wird. Dann werde ich mir wohl mal den Code zu Gemüte führen müssen.
Du kannst auch z.B. mal in 44_ROLLO schauen, da nutze ich CommandAttr um im define default-Werte zu setzen.


Gesendet von iPhone mit Tapatalk
RasPi: RFXTRX, HM, zigbee2mqtt, mySensors, JeeLink, miLight, squeezbox, Alexa, Siri, ...

RichardCZ

11:39 - und ich überlege stark ob ich zum Vodka greifen soll. Ihr macht mich noch zum Alkoholiker.

96           $attr{$name}{room}                //= "Weather";
97           $attr{$name}{unit_windspeed}      //= "km/h";
98           $attr{$name}{unit_solarradiation} //= "lux";
99           $attr{$name}{round}               //= 4;
   96       #$attr{$name}{room}                //= "Weather";
   97       CommandAttr( undef, "$name room Weather" )
   98           if ( AttrVal( $name, "room", "" ) eq q{} );
   99   
   100       #$attr{$name}{unit_windspeed}      //= "km/h";
   101       CommandAttr( undef, "$name unit_windspeed km/h" )
   102           if ( AttrVal( $name, "unit_windspeed", "" ) eq q{} );
   103   
   104       #$attr{$name}{unit_solarradiation} //= "lux";
   105       CommandAttr( undef, "$name unit_solarradiation lux" )
   106           if ( AttrVal( $name, "unit_solarradiation", "" ) eq q{} );
   107   
   108       #$attr{$name}{round}               //= 4;
   109       CommandAttr( undef, "$name round 4" )
   110           if ( AttrVal( $name, "round", "" ) eq q{} );
Witty House Infrastructure Processor (WHIP) is a modern and
comprehensive full-stack smart home framework for the 21st century.

mahowi

Wein und Whiskey hätte ich im Angebot.  ;)

Schöner ist definitv der erste Ansatz. Wie ich aber jetzt auch gelernt habe, soll man die Atrribute nicht direkt manipulieren sondern das FHEM-eigene CommandAttr nutzen:
Zitat von: KernSani am 27 März 2020, 16:23:37
Ich würde gerne nochmal diese Zeilen hier augreifen:


$attr{$name}{unit_windspeed}      //= "km/h";
$attr{$name}{unit_solarradiation} //= "lux";
$attr{$name}{round}               //= 4;

Das sieht schön aus und ist sicherlich Perl-technisch super. Ich gebe allerdinmgs zwei Punkte zu bedenken:
Für einen Perl-Noob (die wir ja zu großen Teilen hier sind) ist das völlig unverständliches Zeug, ein "if" kann dagegen jeder lesen, der ein bisschen programmieren kann. Ich werde das mal an mir persönlich ausprobieren, wenn ich das irgendwo in meinem Code verwende und in 4 Wochen nochmal drauf schaue ;-) Das ist aber garnicht mein eigentlicher Kritikpunkt, was auch bedacht werden sollte:
Wenn schon aufgeräumt wird, dann könnte man auch gleich FHEM-technisch aufräumen (das kann Richard sicher noch nicht leisten) und dazu würde m.E. gehören, dass $attr nicht direkt manipuliert wird, sondern CommandAttr verwendet wird.

Grüße,

Oli
   

Ich würde das auch gerne wieder kürzer schreiben, sehe da aber jetzt keinen Ansatz.
CUBe (MAX): HT, FK | CUBe (SlowRF): ESA2000WZ
JeeLink: LaCrosse | nanoCUL433: Smartwares SHS-51001-EU, EM1000GZ
ZME_UZB1: GreenWave PowerNode, Popp Thermostat | SIGNALDuino: HE877, X10 MS14A, Revolt NC-5462,  IT Steckdosen + PIR
tado° | Milight | HUE, Lightify | SmarterCoffee

Beta-User

#36
Ungetestet:
AttrVal($name,"unit_windspeed",)//CommandAttr(undef,"$name unit_windspeed km/h");
Der Trick ist dabei der, dass bei AttrVal ausdrücklich kein default angegeben wird, sondern gerade darauf "gelauscht" wird, dass ggf. undef zurückkommt...

(Na ja, ist eigentlich nur ein Teil der Lösung, denn wenn man sicherstellen will, dass es den Attributwert gibt, muß man sowas ähnliches z.B. in Initialize() packen , oder dann eben mit Variablen arbeiten, die nur intern gültig sind. Für mich sieht das nach "Mischmasch" aus.)
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

RichardCZ

Zitat von: mahowi am 30 März 2020, 11:57:35
Wein und Whiskey hätte ich im Angebot.  ;)

Schöner ist definitv der erste Ansatz. Wie ich aber jetzt auch gelernt habe, soll man die Atrribute nicht direkt manipulieren sondern das FHEM-eigene CommandAttr nutzen:
Ich würde das auch gerne wieder kürzer schreiben, sehe da aber jetzt keinen Ansatz.

Also - da ich nicht weiß wohin zuerst - es fehlt z.B. noch ein Thread zum Thema:

'', 0, undef - ist doch alles dasselbe oder?

möchte ich zu diesem Diff nur folgende Stichpunkte loswerden:

* Verwendung "dafür vorgesehener Methoden" ist natürlich immer gut. Aber diese Methoden sollten auch gut sein.
* Nach Sichtung halte ich CommandAttr und AttrVal für schlecht designed und schlecht programmiert (ersteres ist das größere Übel)
* Hier (diff) wird leichtfertig mit "not true" und "is undef" umgegangen als sei es das Selbe. dasselbe. Das Gleiche. Scheinbar anscheinend.

Richtig richtig wäre es gewesen, wenn $Attr ein Objekt gewesen wäre und man eben nur mittels Methoden darauf zugreift (Getter), oder was ändert (Setter), oder gleich bei der Definition eines Attributs implizit vom Objekt Default-Werte setzen lässt.

Das ist für FHEM auf absehbare Zukunft aber leider noch Sci-Fi.

Von diesem Sci-Fi ist sowohl //= wie auch CommandAttr() if AttrVal weit entfernt. Mit dem Unterschied, dass letzteres ca. 200x langsamer und 23.5x unleserlicher ist als ersteres.
Witty House Infrastructure Processor (WHIP) is a modern and
comprehensive full-stack smart home framework for the 21st century.

Beta-User

#38
Ansichten eines DAM:

Attribute sind in FHEM das Mittel, um Usereingaben statischer Art zu speichern. Defaults machen (als Attribut!) wenig Sinn. Der Modulautor sollte aber intern natürlich einen default festlegen. Aber eben als Variable innerhalb des Moduls, die einmal initialisiert wird bzw. erneuert, wenn der User tatsächlich eine Eingabe tätigt...

An sich macht die Konstruktion unten daher keinen Sinn, es ist schlicht nicht nötig, Defaults irgendwo außerhalb des Moduls fest abzulegen.
Die Schreib- oder Lesegeschwindigkeit dieser beiden Funktionen ist daher auch reichlich sekundär, da man diese eben nicht ständig brauchen sollte.

Edit
Verwendung als Variable:
my $unit_windspeed = AttrVal($name,"unit_windspeed","km/h");
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

herrmannj

Zitat von: mahowi am 27 März 2020, 21:17:26
Schade, daß die Doku leider etwas vernachlässigt wird. Dann werde ich mir wohl mal den Code zu Gemüte führen müssen.
Kannst Du maßgeblich mit beeinflussen. Das ist ein community Projekt!

herrmannj

Zitat von: Beta-User am 30 März 2020, 12:38:17

my $unit_windspeed = AttrVal($name,"unit_windspeed","km/h");
Ja, so ist die das korrekt.

mahowi

Zitat von: herrmannj am 30 März 2020, 12:44:26
Kannst Du maßgeblich mit beeinflussen. Das ist ein community Projekt!
Natürlich. Wo ich kann, arbeite ich auch gerne im Wiki mit. Aber Funktionen, die ich nicht kenne, kann ich nicht dokumentieren.  ;)
CUBe (MAX): HT, FK | CUBe (SlowRF): ESA2000WZ
JeeLink: LaCrosse | nanoCUL433: Smartwares SHS-51001-EU, EM1000GZ
ZME_UZB1: GreenWave PowerNode, Popp Thermostat | SIGNALDuino: HE877, X10 MS14A, Revolt NC-5462,  IT Steckdosen + PIR
tado° | Milight | HUE, Lightify | SmarterCoffee

RichardCZ

Zitat von: Beta-User am 30 März 2020, 12:38:17
Verwendung als Variable:
my $unit_windspeed = AttrVal($name,"unit_windspeed","km/h");

Und das sieht auch schon wesentlich besser aus. Jetzt noch

my $unit_windspeed = AttrVal($name, 'unit_windspeed', 'km/h');

und ich muss noch nicht zur Flasche greifen.
Witty House Infrastructure Processor (WHIP) is a modern and
comprehensive full-stack smart home framework for the 21st century.

mahowi

Okay, ich habe jetzt die Defaults für Attribute aus dem Define rausgeworfen. Jetzt habe ich nur noch
    my $unit_windspeed      = AttrVal( $name, 'unit_windspeed', 'km/h' );
    my $unit_solarradiation = AttrVal( $name, 'unit_solarradiation', 'lux' );
    my $rnd                 = AttrVal( $name, 'round', 4 );

für benötigte Default-Werte drin.

Wo finde ich denn Näheres zur Verwendung von " und '?
CUBe (MAX): HT, FK | CUBe (SlowRF): ESA2000WZ
JeeLink: LaCrosse | nanoCUL433: Smartwares SHS-51001-EU, EM1000GZ
ZME_UZB1: GreenWave PowerNode, Popp Thermostat | SIGNALDuino: HE877, X10 MS14A, Revolt NC-5462,  IT Steckdosen + PIR
tado° | Milight | HUE, Lightify | SmarterCoffee

RichardCZ

#44
Zitat von: mahowi am 30 März 2020, 14:10:20
Okay, ich habe jetzt die Defaults für Attribute aus dem Define rausgeworfen. Jetzt habe ich nur noch
    my $unit_windspeed      = AttrVal( $name, 'unit_windspeed',      'km/h' );
    my $unit_solarradiation = AttrVal( $name, 'unit_solarradiation', 'lux' );
    my $rnd                 = AttrVal( $name, 'round',                4 );

für benötigte Default-Werte drin.

Wo finde ich denn Näheres zur Verwendung von " und '?

https://forum.fhem.de/index.php/topic,109616.0.html
und PBP 51-53
Witty House Infrastructure Processor (WHIP) is a modern and
comprehensive full-stack smart home framework for the 21st century.

Beta-User

Zitat von: mahowi am 30 März 2020, 12:59:51
Natürlich. Wo ich kann, arbeite ich auch gerne im Wiki mit. Aber Funktionen, die ich nicht kenne, kann ich nicht dokumentieren.  ;)

Dazu vielleicht noch zwei DAM-Anmerkungen:
- Diese Art Funktionen sind für vergleichbare Funktionen alle praktisch gleich aufgebaut. Kennt man eine (in jede Richtung), kennt man alle ;) .
- WAS es gibt, steht in einer PBP-unkonformen Weise ziemlich am Anfang von fhem.pl. Sehr übersichtlich, sehr hilfreich für DAM wie mich :P .
(Und wenn man den Prototype mit in die Suche innerhalb der Seite einbaut, findet sich auch ruck-zuck der betreffende Quellcode ::) ).

Zu den Quotes noch: wer noch nicht erfolgreich nach einem pdf gesucht hat, findet auch hier was: https://www.perlmonks.org/?node_id=401006.
Da steht auch was zu "q" &Co.
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

RichardCZ

Witty House Infrastructure Processor (WHIP) is a modern and
comprehensive full-stack smart home framework for the 21st century.

mahowi

Nachdem perlcritic -3 ja jetzt still ist, wollte ich mir das Ganze mal mit -2 ansehen. Das meiste verstehe ich ja, aber was soll mir

4 is not one of the allowed literal values (0, 1, 2). Use the Readonly or Const::Fast module or the "constant" pragma instead at line 108, column 29.  Unnamed numeric literals make code less maintainable.  (Severity: 2)
300 is not one of the allowed literal values (0, 1, 2). Use the Readonly or Const::Fast module or the "constant" pragma instead at line 113, column 25.  Unnamed numeric literals make code less maintainable.  (Severity: 2)
3 is not one of the allowed literal values (0, 1, 2). Use the Readonly or Const::Fast module or the "constant" pragma instead at line 116, column 44.  Unnamed numeric literals make code less maintainable.  (Severity: 2)
3 is not one of the allowed literal values (0, 1, 2). Use the Readonly or Const::Fast module or the "constant" pragma instead at line 131, column 18.  Unnamed numeric literals make code less maintainable.  (Severity: 2)
3 is not one of the allowed literal values (0, 1, 2). Use the Readonly or Const::Fast module or the "constant" pragma instead at line 161, column 26.  Unnamed numeric literals make code less maintainable.  (Severity: 2)
3 is not one of the allowed literal values (0, 1, 2). Use the Readonly or Const::Fast module or the "constant" pragma instead at line 165, column 26.  Unnamed numeric literals make code less maintainable.  (Severity: 2)
3 is not one of the allowed literal values (0, 1, 2). Use the Readonly or Const::Fast module or the "constant" pragma instead at line 172, column 26.  Unnamed numeric literals make code less maintainable.  (Severity: 2)
3 is not one of the allowed literal values (0, 1, 2). Use the Readonly or Const::Fast module or the "constant" pragma instead at line 176, column 26.  Unnamed numeric literals make code less maintainable.  (Severity: 2)
3 is not one of the allowed literal values (0, 1, 2). Use the Readonly or Const::Fast module or the "constant" pragma instead at line 182, column 29.  Unnamed numeric literals make code less maintainable.  (Severity: 2)
4 is not one of the allowed literal values (0, 1, 2). Use the Readonly or Const::Fast module or the "constant" pragma instead at line 191, column 30.  Unnamed numeric literals make code less maintainable.  (Severity: 2)

sagen?

Das sind z.B.:
108:         if ( int(@param) != 4 );
113:    $hash->{INTERVAL} = 300;
116:    $hash->{helper}{password}     = $param[3];

Also vollkommen unterschiedliche Anwendungen. Ich sehe da keinen "Fehler" drin.
CUBe (MAX): HT, FK | CUBe (SlowRF): ESA2000WZ
JeeLink: LaCrosse | nanoCUL433: Smartwares SHS-51001-EU, EM1000GZ
ZME_UZB1: GreenWave PowerNode, Popp Thermostat | SIGNALDuino: HE877, X10 MS14A, Revolt NC-5462,  IT Steckdosen + PIR
tado° | Milight | HUE, Lightify | SmarterCoffee

SCMP77

Hallo,

wirkliche Fehler sind es natürlich nicht, aber die Stellen sind vielleicht zu verbessern.

die Meldungen machen schon teilweise Sinn.

113:    $hash->{INTERVAL} = 300;

Hier kann man etwas erkennen, dass man hier eine Zeit reinschreibt, wozu diese dient, ist aber nicht erkennbar, auch die Einheit ist nicht ersichtlich. Wenn der Wert von 300 an anderen Stellen im Modul auch auftauchen würde und jeweils das gleiche bewirken soll und man an diesem Wert drehen will, müsste man an mehreren Stellen etwas ändern.

Sinn macht daher Am Anfang des Moduls eine Konstante zu definieren, die die Wartezeit näher beschreibt, evtl. mit Einheit.

Beispiel:
use constant SEND_INTERVAL_S => 300;

Die Zeile 113 würde dann zu:

    $hash->{INTERVAL} = SEND_INTERVAL_S;

Dadurch wird die Dokumentation des Codes schon besser, zum anderen ermöglicht es an zentraler Stelle schnell mal den Wert zu ändern.

Auch bzgl. param wäre es vielleicht besser folgendes zu schreiben:


    my ($name, $module, $stationid, $password) = @param ;
    $hash->{helper}{stationid}    = $stationid;
    $hash->{helper}{password}  = $password;


Das würde alles besser dokumentieren, auch wenn es etwas langsamer ist. Aber es ist nur im Define-Befehl und da kommt es nicht drauf an.

Die 4 könnte man wohl nicht sinnvoll ersetzen.

Viele Grüße
     SCMP77
Raspberry Pi 3 Model B mit Rasbian, SolvisMax, AVM DECT 200, Sonoff mit Tasmota geflasht

RichardCZ

Zitat von: mahowi am 16 April 2020, 11:04:51
Nachdem perlcritic -3 ja jetzt still ist, wollte ich mir das Ganze mal mit -2 ansehen.

Mutig.  :)

SCMP77 hat es im Wesentlichen bereits gesagt, hier füge ich noch hinzu:

PBP sagt (wie üblich: zu recht) man soll magische Konstanten vermeiden.

Also wenn mitten im Code eben steht

function_call($name, 1337, 34.5);

dann sind diese Konstanten ohne weitere Erklärung "magisch". Gottgegeben. 3.14 wissen noch die meisten von uns, aber 26.9815386 als molare Masse von Aluminium... nuja.

In FHEM ist das auch lustig, fhem.pl definiert ja

use constant {
  DAYSECONDS    => 86400,
  HOURSECONDS   =>  3600,
  MINUTESECONDS =>    60
};


aber keine Sau nutzt das. Den grep+tamtam spare ich mir jetzt, sonst bekommen noch Einige hier Ausschlag.  ;)




PBP - schicke ich gleich hinterher - sagt aber im fast gleichen Atemzug man soll eben nicht "use constant" verwenden, sondern Readonly.
Witty House Infrastructure Processor (WHIP) is a modern and
comprehensive full-stack smart home framework for the 21st century.

Wzut

Zitat von: RichardCZ am 17 April 2020, 09:31:10
aber keine Sau nutzt das.
vllt. weil es sehr sehr spät eingeführt wurde :) und btw. mir gehen die magischen 3600 auch flotter und einfacher von der Hand als ein MONSTERSTRING dessen  Namen ich erst einmal mühsam suchen müsste ....   
Maintainer der Module: MAX, MPD, UbiquitiMP, UbiquitiOut, SIP, BEOK, readingsWatcher

herrmannj

dito, kenne auch alle drei auswendig & im Schlaf.

RichardCZ

#52
Zitat von: Wzut am 17 April 2020, 19:00:57
vllt. weil es sehr sehr spät eingeführt wurde :) und btw. mir gehen die magischen 3600 auch flotter und einfacher von der Hand als ein MONSTERSTRING dessen  Namen ich erst einmal mühsam suchen müsste ....   

Dann erinnere ich nochmal daran, dass man Programme schreiben sollte die sich gut lesen und nicht die sich gut schreiben.

Das eine muss das andere nicht ausschliessen (tut es aber häufig).

edit:

Ich würde z.B. gerne wissen was in fhemTzOffset die zwei 60er bedeuten. Also ist eine Sekunden in Minute und eine Minuten in Stunde oder beides gleich oder?

    return 60*(($l[2] - $g[2] +
                    ((($l[5] << 9)|$l[7]) <=> (($g[5] << 9)|$g[7])) * 24)*60 +
                    $l[1] - $g[1]);


Ich finde das schon irgendwann raus, aber sowas kostet halt unnötig Zeit.
Witty House Infrastructure Processor (WHIP) is a modern and
comprehensive full-stack smart home framework for the 21st century.

RichardCZ

# POD ERRORS

Hey! **The above document had some coding errors, which are explained below:**

- Around line 352:

    '=item' outside of any '=over'

    =over without closing =back


Das betrifft so praktisch jedes FHEM Modul. Abhilfe ist easy:

=pod

=over

=item ....

=begin html
...

=back

=cut
Witty House Infrastructure Processor (WHIP) is a modern and
comprehensive full-stack smart home framework for the 21st century.