Autor Thema: subs aus main im eigenen Package bekannt geben  (Gelesen 687 mal)

Offline Damian

  • Developer
  • Hero Member
  • ****
  • Beiträge: 5754
subs aus main im eigenen Package bekannt geben
« am: 24 September 2018, 07:54:22 »
Hallo,

ich habe im Modul ein eigenes package erstellt, um Anwender-Funktionen (mit eval erzeugt) zu kapseln.

Wie kann ich nun in diesem package die subs aus dem main-package bekannt geben?

Hintergrund: der Anwender soll weiterhin die Standard-Funktionen, wie z. B. ReadingsVal nutzen können, ohne jedes mal :: ReadingsVal angeben zu müssen.

Meine Versuche mit use sind bisher gescheitert.

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

Offline hexenmeister

  • Developer
  • Hero Member
  • ****
  • Beiträge: 4221
    • tech_LogBuch
Antw:subs aus main im eigenen Package bekannt geben
« Antwort #1 am: 24 September 2018, 08:04:36 »
Moin!

Ich benutze dafür die GPUtils (https://svn.fhem.de/trac/browser/trunk/fhem/FHEM/GPUtils.pm).
Beispiel findest Du z.B. in meiner MQTT_GENERIC_BRIDGE (https://svn.fhem.de/trac/browser/trunk/fhem/FHEM/10_MQTT_GENERIC_BRIDGE.pm)

grobes Schema:
...

package XYZ;

use GPUtils qw(:all);
...
BEGIN {
GP_Import(qw(
    AttrVal
    ReadingsVal
    ...
  ))
};
...
In Verwendung: HM, EnOcean, 1wire, Firmata, MySensors, ESPEasy, MQTT*, NodeRED, Alexa, Telegram,..
Maintainer: MQTT_GENERIC_BRIDGE, SYSMON, SMARTMON, systemd_watchdog, MQTT, MQTT_DEVICE, MQTT_BRIDGE
Contrib: dev_proxy
Kaffeekasse: https://www.paypal.me/s6z
Zustimmung Zustimmung x 1 Liste anzeigen

Offline CoolTux

  • Developer
  • Hero Member
  • ****
  • Beiträge: 16677
Antw:subs aus main im eigenen Package bekannt geben
« Antwort #2 am: 24 September 2018, 08:21:38 »
Sollten wir mal anfangen im Wiki fest zu halten. Es sollte so oder so mit packages gearbeitet werden, bei jedem Modul. Ist einfach sauberer und woeit mir bekannt auch von vielen Kerndevelopern gewünscht.
« Letzte Änderung: 24 September 2018, 09:16:36 von CoolTux »
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.me/MOldenburg
Mein GitHub: https://github.com/LeonGaultier
kein Support für cfg Editierer
Zustimmung Zustimmung x 1 Liste anzeigen

Offline Sidey

  • Developer
  • Hero Member
  • ****
  • Beiträge: 2091
Antw:subs aus main im eigenen Package bekannt geben
« Antwort #3 am: 24 September 2018, 09:08:17 »
Hi cooltux,

Finde deine Idee gut .
Kannst Du grob beschreiben, was nötig ist um ein Modul zu ändern?

Wenn ich es richtig verstehe, dann schreibe ich package oben rein (Modulname) und importiere dann die benötigten FHEM Funktionen.

Wie könnte der Übergang aussehen, wenn ein anderes Modul eine meiner Funktionen aufruft?
Das müsste die Funktion ja dann via package::Name aufrufen?

Eventuell lässt sich das halt nicht zeitgleich umstellen.

Grüße Sidesy
Signalduino, HMLan, Raspberry Pi, Mysensors, ArduinoSensor

Offline CoolTux

  • Developer
  • Hero Member
  • ****
  • Beiträge: 16677
Antw:subs aus main im eigenen Package bekannt geben
« Antwort #4 am: 24 September 2018, 09:15:45 »
Hi cooltux,

Finde deine Idee gut .
Kannst Du grob beschreiben, was nötig ist um ein Modul zu ändern?

Wenn ich es richtig verstehe, dann schreibe ich package oben rein (Modulname) und importiere dann die benötigten FHEM Funktionen.

Wie könnte der Übergang aussehen, wenn ein anderes Modul eine meiner Funktionen aufruft?
Das müsste die Funktion ja dann via package::Name aufrufen?

Eventuell lässt sich das halt nicht zeitgleich umstellen.

Grüße Sidesy

Hallo,

Eigentlich sieht es genau so aus wie Du es beschrieben hast. Ich habe damit in meinem neusten Modul angefangen und werde einige ältere wo ich noch aktiv dran arbeite auch umbauen.

## oberer Teil
package main;

use strict;
use warnings;

...


sub ModulName_Initialize($) {
    my ($hash) = @_;

## Da ich mit package arbeite müssen in die Initialize für die jeweiligen hash Fn Funktionen der Funktionsname
#  und davor mit :: getrennt der eigentliche package Name des Modules
    $hash->{SetFn}      = "PackageName::Set";
    $hash->{GetFn}      = "PackageName::Get";
    $hash->{DefFn}      = "PackageName::Define";
    $hash->{NotifyFn}   = "PackageName::Notify";
    $hash->{UndefFn}    = "PackageName::Undef";
    $hash->{AttrFn}     = "PackageName::Attr";
    $hash->{AttrList}   = "disable:0,1 ".
                            "disabledForIntervals ".
                            $readingFnAttributes;
}

## unserer packagename der Funktion
package PackageName;


use strict;
use warnings;
use POSIX;

use GPUtils qw(:all);  # wird für den Import der FHEM Funktionen aus der fhem.pl benötigt

my $missingModul = "";


## Import der FHEM Funktionen
BEGIN {

    GP_Import(qw(
        readingsSingleUpdate
        readingsBulkUpdate
        readingsBulkUpdateIfChanged
        readingsBeginUpdate
        readingsEndUpdate
        Log3
        AttrVal
        ReadingsVal
        Value
        IsDisabled
        deviceEvents
        init_done
    ))
};


sub Define($$) {

    my ( $hash, $def ) = @_;
my @a = split( "[ \t][ \t]*", $def );

...
...
...
Bei dem GP_Import musst man natürlich schauen was man da braucht. Da müssen alle Funktionen aus fhem main rein welche man braucht. Manchmal bestehen auch Abhängigkeiten zum Beispiel bei Attr. Aber das findet man schnell raus, am Anfang wird Dir FHEM oft um die Ohren knallen  ;D

Sinn und Zweck ist eine Kapselung ähnlich wie bei Java oder C++ die Klassen.
Der Aufruf einer Deiner Funktion innerhalb des Packages aus einer externen Anwendung heraus erfolgt dann über PACKAGENAME::FUNKTIONSNAME. Im übrigen wer zum Beispiel mit der InternalTimer Funktion arbeitet und über diese eine seiner Funktionen nach Ablauf aufrufen lassen möchte muss dies genau so machen.
InternalTimer($timestamp, $packageName::functionName, $arg);


Grüße
Leon
« Letzte Änderung: 24 September 2018, 09:27:28 von CoolTux »
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.me/MOldenburg
Mein GitHub: https://github.com/LeonGaultier
kein Support für cfg Editierer

Offline Damian

  • Developer
  • Hero Member
  • ****
  • Beiträge: 5754
Antw:subs aus main im eigenen Package bekannt geben
« Antwort #5 am: 24 September 2018, 11:22:48 »
Ich habe jetzt mal folgendes bei mir eingebaut:

...
package DOIF;

no strict qw/refs/;

BEGIN {
  foreach(qw(
    CommandAttr
    readingsSingleUpdate
    readingsBeginUpdate
    readingsBulkUpdate
    readingsEndUpdate
    Log3
    DoSet
    fhem
    defs
    AttrVal
    ReadingsVal
    ReadingsTimestamp
    ReadingsAge
    deviceEvents
    AssignIoPort
    addToDevAttrList
    delFromDevAttrList
    devspec2array
    gettimeofday
    InternalTimer
    RemoveInternalTimer
    )) {
      *{'DOIF::'.$_} = *{'main::'.$_};
    }
};
...

Finde es aber recht umständlich. Vor allem, wenn man alle Methoden aus main nutzen möchte.
Programmierte FHEM-Module: DOIF mit uiTable, DOIF-Perl, THRESHOLD, FHEM-Befehl: IF

Offline Benni

  • Developer
  • Hero Member
  • ****
  • Beiträge: 1852
  • FHEMinist
Antw:subs aus main im eigenen Package bekannt geben
« Antwort #6 am: 24 September 2018, 11:35:34 »
Ich finde die Idee der Namespace-Kapselung auch sehr gut und hänge mich mitlesenderweise mal hier ran.  ;)

Edit: Zur Übung habe ich jetzt mal damit angefangen, meine persönlichen 99er-Module auf Packages umzustellen. Wird ne ganze Menge Arbeit. Kann aber jetzt schon feststellen, dass das hinterher für mehr Übersicht sorgt, da dann ganz klar ersichtlich ist, welche Funktion in welchem Package und damit in welcher Moduldatei zu finden ist. Somit lässt sich letztendlich auch ersehen, welches Modul ggf. von anderen Modulen abhängig ist.
« Letzte Änderung: 24 September 2018, 13:30:17 von Benni »
FHEM (FL 9.9) (configDB+DbLog) auf Debian Wheezy.
Jede Menge HM mit 2x HMUART (WeMos+esp-link) über VCCU.
UniRoll an CUL868. Sebury F2-2 RFID über ESPEasy
Module: 98_rssFeed und 98_QRCode

Offline dev0

  • Developer
  • Hero Member
  • ****
  • Beiträge: 3430
    • _.:|:._
Antw:subs aus main im eigenen Package bekannt geben
« Antwort #7 am: 24 September 2018, 12:16:34 »
Zitat
Sinn und Zweck ist eine Kapselung ähnlich wie bei Java oder C++ die Klassen.
Mir ist der Mehrwert von Namespaces für FHEM Module nicht klar. Das mag daran liegen, dass ich kein Programmierer bin und mir Perl 'nur so' angeeignet habe...
Kapselung hört sich interessant an, aber auch nach dem Lesen von perldoc/package, sehe ich keine Vorteile für (meine?) FHEM Module.

Mag mich jemand aufklären oder auf einen Link verweisen?

Offline Damian

  • Developer
  • Hero Member
  • ****
  • Beiträge: 5754
Antw:subs aus main im eigenen Package bekannt geben
« Antwort #8 am: 24 September 2018, 13:16:33 »
Mir ist der Mehrwert von Namespaces für FHEM Module nicht klar. Das mag daran liegen, dass ich kein Programmierer bin und mir Perl 'nur so' angeeignet habe...
Kapselung hört sich interessant an, aber auch nach dem Lesen von perldoc/package, sehe ich keine Vorteile für (meine?) FHEM Module.

Mag mich jemand aufklären oder auf einen Link verweisen?

In meinem Modul dürfen Anwender eigene Funktionen definieren - entspricht im wesentlichen dem Konzept von myUtils. Mit der Kapselung kann sich der Anwender jetzt eine Funktion on oder fhem oder ReadingsVal oder sonst was definieren, ohne Gefahr zu laufen, eine wichtige Funktion irgendwo im ganzen FHEM umzudefinieren.

Der bisherige Weg war, möglichst eindeutige Namen (z. B. mit Modulbezeichnung im Namen zu definieren), das kann der Modulentwickler noch hinbekommen, der FHEM-Anwender dagegen ist sich oft der Problematik aber nicht bewusst.
Programmierte FHEM-Module: DOIF mit uiTable, DOIF-Perl, THRESHOLD, FHEM-Befehl: IF

Offline rudolfkoenig

  • Administrator
  • Hero Member
  • *****
  • Beiträge: 19335
Antw:subs aus main im eigenen Package bekannt geben
« Antwort #9 am: 24 September 2018, 13:31:19 »
Die Absicht: mit einem Package soll nicht versehentlich passieren, dass Du in deinem Modul eine Routine mit dem gleichen Namen anlegst, wie sie schon anderswo existiert, und dann je nach Ladereihenfolge die eine oder die andere Implementation aufgerufen wird, weil die zuletzt geladene die Erste ueberschreibt.Z.Zt. werden diese Probleme meist mit dem Voranstellen des Modulnamens geloest.

"Leider" kann man bei den Packages nach "use PackageName" die exportierten(!) Funktionen ohne Prefix ansprechen, was dann je nach geladenes Modul und exportierten Funktionen zu den gleichen Problemen fuehrt, das hatten wir unlaengst mit der "stat" Funktion erfahren.

Offline CoolTux

  • Developer
  • Hero Member
  • ****
  • Beiträge: 16677
Antw:subs aus main im eigenen Package bekannt geben
« Antwort #10 am: 24 September 2018, 13:33:30 »
Ja es ist am Anfang etwas umständlich, weil man immer darauf achten muss was man aus der main wirklich braucht. Aber dafür rechnet sich der Mehrwert. Du musst Dir keine Sorgen mehr machen ob es den Namen der Funktion schon in FHEM, also auch in anderen Modulen, gibt. Du kannst innerhalb Deines Modules FHEM Funktionen überschreiben. Du kannst also auch eine ReadingsVal erstellen wenn Dir danach ist. Darfst die original FHEM ReadingsVal dann aber nicht importieren. Kannst aber die Funktionen verknüpfen in dem Du due FHEM Funktion mittels main:: auf rufst.
Jeder Ansatz von OO Programming arbeitet auf Basis dieses Denkens.
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.me/MOldenburg
Mein GitHub: https://github.com/LeonGaultier
kein Support für cfg Editierer

Offline Damian

  • Developer
  • Hero Member
  • ****
  • Beiträge: 5754
Antw:subs aus main im eigenen Package bekannt geben
« Antwort #11 am: 24 September 2018, 15:12:13 »
Ok.

Ich denke das Importieren (Überschreiben der Methoden mit *{'DOIF::'.$_} = *{'main::'.$_}) hat sich dann für meinen Zweck erledigt. Denn dann würde
eine Definition vom Anwender z. B. sub {fhem{}} selbst im gekapselten Package alles (auch in main) zunichte machen - das wollte ich eigentlich verhindern.

« Letzte Änderung: 24 September 2018, 15:14:43 von Damian »
Programmierte FHEM-Module: DOIF mit uiTable, DOIF-Perl, THRESHOLD, FHEM-Befehl: IF

Offline hexenmeister

  • Developer
  • Hero Member
  • ****
  • Beiträge: 4221
    • tech_LogBuch
Antw:subs aus main im eigenen Package bekannt geben
« Antwort #12 am: 24 September 2018, 15:28:41 »
Ok.

Ich denke das Importieren (Überschreiben der Methoden mit *{'DOIF::'.$_} = *{'main::'.$_}) hat sich dann für meinen Zweck erledigt. Denn dann würde
eine Definition vom Anwender z. B. sub {fhem{}} selbst im gekapselten Package alles (auch in main) zunichte machen - das wollte ich eigentlich verhindern.
Ich meine nicht. Die subs auf 'main' werden in 'DOIF' verlinkt, nicht andersrum.
In Verwendung: HM, EnOcean, 1wire, Firmata, MySensors, ESPEasy, MQTT*, NodeRED, Alexa, Telegram,..
Maintainer: MQTT_GENERIC_BRIDGE, SYSMON, SMARTMON, systemd_watchdog, MQTT, MQTT_DEVICE, MQTT_BRIDGE
Contrib: dev_proxy
Kaffeekasse: https://www.paypal.me/s6z

Offline Sidey

  • Developer
  • Hero Member
  • ****
  • Beiträge: 2091
Antw:subs aus main im eigenen Package bekannt geben
« Antwort #13 am: 24 September 2018, 15:37:35 »
Interessant wird es dann, wenn jemand aus dem DoIf eine Funktion eines Moduls oder der myutils aufruft. Dann muss er den Namen des Packages voran stellen.


Grüße Sidey

Gesendet von meinem XT1650 mit Tapatalk

Signalduino, HMLan, Raspberry Pi, Mysensors, ArduinoSensor

Offline Damian

  • Developer
  • Hero Member
  • ****
  • Beiträge: 5754
Antw:subs aus main im eigenen Package bekannt geben
« Antwort #14 am: 24 September 2018, 16:26:34 »
Ich meine nicht. Die subs auf 'main' werden in 'DOIF' verlinkt, nicht andersrum.
Die Vorgehensweise habe aus deinem Code entnommen:

  no strict qw/refs/; ## no critic
  my $pkg = caller(0);
  foreach (@_) {
  *{$pkg.'::'.$_} = *{'main::'.$_};

Der Aufruf findet im eigenen Package statt.
Programmierte FHEM-Module: DOIF mit uiTable, DOIF-Perl, THRESHOLD, FHEM-Befehl: IF

Offline Damian

  • Developer
  • Hero Member
  • ****
  • Beiträge: 5754
Antw:subs aus main im eigenen Package bekannt geben
« Antwort #15 am: 24 September 2018, 16:30:17 »
Ok.

Ich denke das Importieren (Überschreiben der Methoden mit *{'DOIF::'.$_} = *{'main::'.$_}) hat sich dann für meinen Zweck erledigt. Denn dann würde
eine Definition vom Anwender z. B. sub {fhem{}} selbst im gekapselten Package alles (auch in main) zunichte machen - das wollte ich eigentlich verhindern.

Dann bleibe ich bei meiner Billiglösung, z. B.:

package DOIF;

sub fhem {
  my ($content)=@_;
  ::fhem($content);
}

So kann der User nur fhem im DOIF zerschießen, wenn er fhem umdefiniert, aber nicht in main.
Programmierte FHEM-Module: DOIF mit uiTable, DOIF-Perl, THRESHOLD, FHEM-Befehl: IF

Offline Damian

  • Developer
  • Hero Member
  • ****
  • Beiträge: 5754
Antw:subs aus main im eigenen Package bekannt geben
« Antwort #16 am: 24 September 2018, 16:36:04 »
Interessant wird es dann, wenn jemand aus dem DoIf eine Funktion eines Moduls oder der myutils aufruft. Dann muss er den Namen des Packages voran stellen.


Grüße Sidey

Gesendet von meinem XT1650 mit Tapatalk

Wenn man ohne package arbeitet, dann sind wohl Funktionen in myutils in main, dann muss man logischer Weise im DOIF ::<funktion> angeben.

Wenn man speziell etwas für DOIF definieren möchte, dann kann man davor package DOIF; definieren, dann kann man sie direkte aufrufen.

Das neue Konzept im Perl-Modus sieht aber vor, eigene Perl-Funktionen, die im definierten DOIF-Device genutzt werden,  auch in diesem zu definieren und nicht irgendwo auszulagern.
Programmierte FHEM-Module: DOIF mit uiTable, DOIF-Perl, THRESHOLD, FHEM-Befehl: IF

Offline hexenmeister

  • Developer
  • Hero Member
  • ****
  • Beiträge: 4221
    • tech_LogBuch
Antw:subs aus main im eigenen Package bekannt geben
« Antwort #17 am: 24 September 2018, 17:15:09 »
Die Vorgehensweise habe aus deinem Code entnommen:
Auch wenn es nicht mein Code ist (GP_utils nutze ich nur),.. Ich denke wir sprechen an einander vorbei. Mit diesem Code verlinkst du subs aus main in deinem Paket-Raum. Überschreibt man den Link, hat das keine Auswirkung auf main, nur auf dein Paket.
« Letzte Änderung: 24 September 2018, 17:20:21 von hexenmeister »
In Verwendung: HM, EnOcean, 1wire, Firmata, MySensors, ESPEasy, MQTT*, NodeRED, Alexa, Telegram,..
Maintainer: MQTT_GENERIC_BRIDGE, SYSMON, SMARTMON, systemd_watchdog, MQTT, MQTT_DEVICE, MQTT_BRIDGE
Contrib: dev_proxy
Kaffeekasse: https://www.paypal.me/s6z

Offline Damian

  • Developer
  • Hero Member
  • ****
  • Beiträge: 5754
Antw:subs aus main im eigenen Package bekannt geben
« Antwort #18 am: 24 September 2018, 17:24:19 »
Auch wenn es nicht mein Code ist (GP_utils nutze ich nur),.. Ich denke wir sprechen an einander vorbei. Mit diesem Code verlinkst du subs aus main in deinem Paket-Raum. Überschreibt man den Link, hat das keine Auswirkung auf main, nur auf dein Paket.

Das dachte ich auch, bis ich es ausprobiert hatte.
Programmierte FHEM-Module: DOIF mit uiTable, DOIF-Perl, THRESHOLD, FHEM-Befehl: IF

Offline hexenmeister

  • Developer
  • Hero Member
  • ****
  • Beiträge: 4221
    • tech_LogBuch
Antw:subs aus main im eigenen Package bekannt geben
« Antwort #19 am: 24 September 2018, 21:43:58 »
Das dachte ich auch, bis ich es ausprobiert hatte.
Habe jetzt auch ausprobier. Du hast recht.
Sehr unglücklich >:(

Und macht man aus den subs einen Loop:
sub gettimeofday() {
  return ":P ".main::gettimeofday();
}
Wird man mit "Out of memory!" bestraft ;D
In Verwendung: HM, EnOcean, 1wire, Firmata, MySensors, ESPEasy, MQTT*, NodeRED, Alexa, Telegram,..
Maintainer: MQTT_GENERIC_BRIDGE, SYSMON, SMARTMON, systemd_watchdog, MQTT, MQTT_DEVICE, MQTT_BRIDGE
Contrib: dev_proxy
Kaffeekasse: https://www.paypal.me/s6z