[gelöst] BlockingCall forkt mit PPID1/Anzahl der BlockingCalls begrenzen

Begonnen von abc2006, 13 Oktober 2017, 00:33:28

Vorheriges Thema - Nächstes Thema

abc2006

Hi,
zum Senden und empfangen von UDP-Paketen habe ich mir in der MyUtils ein paar Subroutinen zusammengebaut. (siehe unten)

Diese Routinen werden von verschiedenen PID's aufgerufen, die Mischer regeln (hin und her fahren).

Solange hier nur ein PID arbeitet (max. alle 60 Sekunden ein Aufruf), läuft alles einwandfrei.
Sobald ich den zweiten PID starte, der alle 15 Sekunden die Routine aufruft, kann ich im Prozessmanager zusehen, wie die anzahl der parallel laufenden FHEM-Instanzen ansteigt ( alle mit PPID 1) bis der Rechner nicht mehr kann. Wie kann das kommen? "geforkte" Prozesse haben doch üblicherweise die PPID des Elternprozesses - also die vom FHEM-Prozess selbst. Und selbst wenn die Anfragen nicht erfolgreich sind, müssten diese Prozesse doch einmal aufgerufen werden und spätestens nach dem Timeout von 5 Sekunden sterben?


Um dies zu umgehen, habe ich nach dem Wiki-Vorbild eine Prüfung eingeführt, die durch eine Variable feststellt, ob noch ein BlockingCall läuft.
Würde das so zuverlässig funktionieren?

Dabei habe ich festgestellt, dass bestimmte Anweisungen, die nach "BlockingCall()" kommen, nicht mehr ausgeführt werden.
Meine Log-Anweisung funktioniert nicht, fhem("set remotebot message ...") hingegen schon. Warum?





our $anzahlBlockingCalls = 0;

sub nb_udpsend {
 
my $blockingFn = "udpsend";
my $finishFn = "finishFn";
my $timeout = 5;
my $abortFn = "abortFn";
my $abortArg = "nb_udpsend_errorArg: did not receive acknowledge from|$_[0]|$_[1]|$_[2]";
##     $order        $ip           $port
my $args = $_[0] . "|" . $_[1] . "|" . $_[2];

log_telegram("3",$log_name,"laufende BC: $anzahlBlockingCalls","remotebot");
if ($anzahlBlockingCalls == 0){
BlockingCall($blockingFn,$args,$finishFn,$timeout,$abortFn,$abortArg);
$anzahlBlockingCalls++;
} else {
log_telegram("4",$log_name,"zu viele calls","remotebot"); ## wurde noch nie ausgeführt
                fhem("set remotebot message zu viele Calls");## funktioniert
}
log_telegram("4",$log_name,"_BlockingCall FN: $blockingFn ARG: $args FIN: $finishFn TO:$timeout AFN:$abortFn AARG: $abortArg _","remotebot");## wurde auch nie ausgeführt
return;
}

sub udpsend {

my ($parameters) = @_;
my ($order,$ip,$port) = split("\\|",$parameters);
my $answer;
my $now = strftime "%Y-%m-%d %T",localtime();
my $sock = IO::Socket::INET->new(
        Proto   => 'udp',
        PeerPort => $port,
        PeerAddr => $ip,
) or die "could not create Socket: $!\n";
$sock->send($order) or die "Send error $!\n";
$sock->setsockopt(SOL_SOCKET, SO_RCVTIMEO, pack('l!l!', 2, 0))
    or die "setsockopt: $!";
$sock->recv($answer,1024)
or die "Could not receive an answer: $!";
$sock->close();

$answer =~ s/(.*)\n.*/$1/g;
return $answer . "|" . $parameters;
}


sub abortFn {
my $log_name = "abortFn(S)";

my ($errArg,$errorder,$errip,$errport) = split("\\|",@_);
my $log_name = "nb_ardsend(S)";
my $subloglevel = 3;

log_telegram("3",$log_name,"function aborted: $errArg " . $errip . ":" . $errport,"remotebot");
log_telegram("5",$log_name,"OUTPUT BEGIN","remotebot");
log_telegram("5",$log_name,"$errip","remotebot");
log_telegram("5",$log_name,"$errport","remotebot");
log_telegram("5",$log_name,"$errorder","remotebot");
log_telegram("5",$log_name,"OUTPUT END","remotebot");
log_telegram("3",$log_name,"laufende BC bevor: $anzahlBlockingCalls","remotebot");
$anzahlBlockingCalls--;
log_telegram("3",$log_name,"laufende BC danach: $anzahlBlockingCalls","remotebot");
}
sub finishFn {
my $log_name = "finishFn(S)";
my ($parameters) = @_;
my ($answer,$order,$ip,$port) = split("\\|",$parameters);

if($answer == $order){
log_telegram("3",$log_name,"function finished: $parameters","remotebot");
} else {
log_telegram("1",$log_name,"function finished: $parameters but:\n$answer is not equal to $order","remotebot");
}
log_telegram("3",$log_name,"laufende BC bevor: $anzahlBlockingCalls","remotebot");
$anzahlBlockingCalls--;
log_telegram("3",$log_name,"laufende BC danach: $anzahlBlockingCalls","remotebot");
}

FHEM nightly auf Intel Atom (lubuntu) mit VDSL 50000 ;-)
Nutze zur Zeit OneWire und KNX

CoolTux

Guten Morgen,

Hab Mal heute Morgen beim Zähneputzen drüber nachgedacht ob das überhaupt irgendwie zu überprüfen ist. Ich müsste aber Deinen Code erstmal in mein Testsystem beim laufen anschauen.
Allerdings kannst Du es ja auch genau so machen wie im Wiki. Irgend ein Device wird ja Deine Sub aufrufen, an dieses Device bindest Du den BlockingCall in dem Du den Hash von dem Device mit gibst. Bei mir ist es zum Beispiel ein Notify.



Grüße
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

abc2006

Danke fürs Nachdenken...

meinst du so ?

PID.HV_RLA:actuation:.* {controlRLA_ARD($hash)}

das ergibt folgenden Fehler:
"Global symbol "$hash" requires explicit package name (did you forget to declare "my $hash"?) at (eval 3039) line 1."

Falls ich das richtig verstanden habe, wird doch auch bei dem Hash *nur* die PID in das Array geschrieben und dann geprüft, wenn schon eine vorhanden ist, wird kein zweiter BlockingCall gestartet.


Im Moment hab ich das massive Problem, dass (gefühlt, ich weiss nicht, wie ich das debuggen soll) wenn der BlockingCall abbricht, eine neue Fhem-Instanz mit der PPID 1 gestartet wird. Und ich weiss nicht, warum.

Grüße,
Stephan

FHEM nightly auf Intel Atom (lubuntu) mit VDSL 50000 ;-)
Nutze zur Zeit OneWire und KNX

rudolfkoenig

die() ist keine gute Idee, da BlockingCall auf eine Rueckgabe wartet.
Generell ist die(), exit() oder Vergleichbares keine gute Idee in einem FHEM-Modul.

rudolfkoenig

Um die im Betreff gestellte Frage zu beantworten: attr global blockingCallMax 5

CoolTux

Zitat von: abc2006 am 13 Oktober 2017, 12:00:27
Danke fürs Nachdenken...

meinst du so ?

PID.HV_RLA:actuation:.* {controlRLA_ARD($hash)}

das ergibt folgenden Fehler:
"Global symbol "$hash" requires explicit package name (did you forget to declare "my $hash"?) at (eval 3039) line 1."

Falls ich das richtig verstanden habe, wird doch auch bei dem Hash *nur* die PID in das Array geschrieben und dann geprüft, wenn schon eine vorhanden ist, wird kein zweiter BlockingCall gestartet.


Im Moment hab ich das massive Problem, dass (gefühlt, ich weiss nicht, wie ich das debuggen soll) wenn der BlockingCall abbricht, eine neue Fhem-Instanz mit der PPID 1 gestartet wird. Und ich weiss nicht, warum.

Grüße,
Stephan

Nee. Nun mal ganz langsam.
PID.HV_RLA:actuation:.* {controlRLA_ARD($hash)}
Hast Du denn ein $hash zu dem Zeitpunkt? Nein hast Du nicht.
Notify gibt uns ein $NAME und ein $EVENT und die Einzelteile des Events (aber das ist unwichtig) des auslösenden Devices.

Wenn dann machst Du das am Notify fest,
PID.HV_RLA:actuation:.* {controlRLA_ARD('NAMEVOMNOTIFY')}

Und in Deiner Funktion machst Du dann

sub nb_udpsend($) {
    my ($name) = @_;
    my $hash = $defs{$name};
  .....






@Rudi
Danke





Grüße
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

Ich gebe Dir mal meine Lösungen. Schau mal ob Du da was für Dich findest


Notify

buttonIsabelFWstatus.(erlaubt|verboten) { systemCommand("notifyFirewall_IsabelHandy","$EVENT") }



99_myUtils

sub systemCommand($$;$) {

    my ($name,$command,$timeOut) = @_;
    my $hash = $defs{$name};
   
    $timeOut = 30 if( !defined($timeOut) );
   
    Log3 $name, 4, "(Sub systemCommand - $name) - Starte Subroutine systemCommand";
   
   
    if( lc $name eq 'notifycontrolwlan0'
    || lc $name eq 'notifyfirewallsshinet'
            || lc $name eq 'notifyfirewallfheminet'
            || lc $name eq 'notifyfirewall_isabelhandy'
            || lc $name eq 'notifyinternet_isabel'
    || lc $command eq 'gtagbattery') {
       
        BlockingKill($hash->{helper}{RUNNING_PID}) if(defined($hash->{helper}{RUNNING_PID}));

        $hash->{helper}{RUNNING_PID} = BlockingCall("systemCommand_Run", $name."|".$command, "systemCommand_Done", $timeOut, "systemCommand_Aborted", $hash) unless(exists($hash->{helper}{RUNNING_PID}));
        Log3 $name, 4, "(Sub systemCommand - $name) - Starte Blocking Call";
    }
}

sub systemCommand_Run($) {

    my ($string) = @_;
    my ( $name, $command ) = split("\\|", $string);
    my $state;
   
    Log3 $name, 4, "(Sub systemCommand - $name) - Führe Systemkommando im nonBlocking Mode aus: $command";
   
   
    #####################################
    ### Wlan0 auf proxy01 #####
    if( $name eq "notifyControlWlan0" ) {
        Log3 $name, 4, "(Sub systemCommand - $name) - Starte $name";
       
        if( $command eq "inet up" ) {
            qx(ssh proxy01 'sudo /sbin/ifup wlan0');
            Log3 $name, 3, "(Sub systemCommand - $name) - wlan0 wird gestartet";
        }
   
        elsif( $command eq "inet down" ) {
            qx(ssh proxy01 'sudo /sbin/ifdown wlan0');
            Log3 $name, 3, "(Sub systemCommand - $name) - wlan0 wird beendet";
        }
   
        elsif( $command eq "inet reconnect" ) {
            qx(ssh proxy01 'sudo /sbin/ifdown wlan0');
            sleep 5;
            qx(ssh proxy01 'sudo /sbin/ifup wlan0');
            Log3 $name, 3, "(Sub systemCommand - $name) - wlan0 wird neu gestartet";
        }
       
        ### Check is wlan0 alive
        $state = qx(ssh proxy01 'cat /sys/class/net/wlan0/carrier');
        Log3 $name, 3, "(Sub systemCommand - $name) - Check is wlan0 alive";
    }
   
    #######################################
    #### Firewall Skripte auf proxy01 ###
    elsif( $name =~ /notifyFirewall/ ) {
       
        Log3 $name, 4, "(Sub systemCommand - $name) - Starte $name";

       
        if ( $command eq "erlaubt" ) {
       
            while ( qx(ps ax | grep -v grep | grep iptables) ) {
                Log3 $name, 4, "(Sub systemCommand - $name) - iptables noch aktiv, wait 0.5s for new check";
                sleep 0.5;
            }
           
            $state = qx(ssh proxy01 'sudo /etc/iptables/rules.scripts/ssh_inbound_inet.sh allow') if( $name eq "notifyFirewallSshInet" );
            $state = qx(ssh proxy01 'sudo /etc/iptables/rules.scripts/fhem_inbound_inet.sh allow') if( $name eq "notifyFirewallFhemInet" );
            $state = qx(ssh proxy01 'sudo /etc/iptables/rules.scripts/isabel_handy_tablet.sh allow') if( $name eq "notifyFirewall_IsabelHandy" );
            Log3 $name, 3, "(Sub systemCommand - $name) - Zugriff erlaubt für $name";
        }
   
        elsif( $command eq "verboten" ) {
           
            while ( qx(ps ax | grep -v grep | grep iptables) ) {
                Log3 $name, 3, "(Sub systemCommand - $name) - iptables noch aktiv, wait 0.5s for new check";
                sleep 0.5;
            }
       
            $state = qx(ssh proxy01 'sudo /etc/iptables/rules.scripts/ssh_inbound_inet.sh deny') if( $name eq "notifyFirewallSshInet" );
            $state = qx(ssh proxy01 'sudo /etc/iptables/rules.scripts/fhem_inbound_inet.sh deny') if( $name eq "notifyFirewallFhemInet" );
            $state = qx(ssh proxy01 'sudo /etc/iptables/rules.scripts/isabel_handy_tablet.sh deny') if( $name eq "notifyFirewall_IsabelHandy" );
            Log3 $name, 3, "(Sub systemCommand - $name) - Zugriff verboten für $name";
        }
    }
   
    #######################################
    #### Isabel Internetfreigabe über SquidGuard auf proxy01 ###
    elsif( $name =~ /notifyInternet_Isabel/ ) {
       
        Log3 $name, 4, "(Sub systemCommand - $name) - Starte $name";
       
       
        if ( $command eq "holidayToNormal" ) {
       
            $state = qx(ssh proxy01 'sudo /usr/local/bin/squidGuard_SetIsabelInternet.sh holidayToNormal');
            Log3 $name, 3, "(Sub systemCommand - $name) - Internetzugriff von SquidGuard für Isabel auf Normal geändert.";
        }
   
        elsif( $command eq "normalToHoliday" ) {
           
            $state = qx(ssh proxy01 'sudo /usr/local/bin/squidGuard_SetIsabelInternet.sh normalToHoliday');
            Log3 $name, 3, "(Sub systemCommand - $name) - Internetzugriff von SquidGuard für Isabel auf Holiday geändert.";
        }
       
        elsif ( $command eq "holidayToBlocked" ) {
       
            $state = qx(ssh proxy01 'sudo /usr/local/bin/squidGuard_SetIsabelInternet.sh holidayToBlocked');
            Log3 $name, 3, "(Sub systemCommand - $name) - Internetzugriff von SquidGuard für Isabel auf Blocked geändert.";
        }
       
        elsif( $command eq "normalToBlocked" ) {
           
            $state = qx(ssh proxy01 'sudo /usr/local/bin/squidGuard_SetIsabelInternet.sh normalToBlocked');
            Log3 $name, 3, "(Sub systemCommand - $name) - Internetzugriff von SquidGuard für Isabel auf Blocked geändert.";
        }
       
         elsif ( $command eq "blockedToHoliday" ) {
       
            $state = qx(ssh proxy01 'sudo /usr/local/bin/squidGuard_SetIsabelInternet.sh blockedToHoliday');
            Log3 $name, 3, "(Sub systemCommand - $name) - Internetzugriff von SquidGuard für Isabel auf Holiday geändert.";
        }
       
        elsif( $command eq "blockedToNormal" ) {
           
            $state = qx(ssh proxy01 'sudo /usr/local/bin/squidGuard_SetIsabelInternet.sh blockedToNormal');
            Log3 $name, 3, "(Sub systemCommand - $name) - Internetzugriff von SquidGuard für Isabel auf Normal geändert.";
        }
    }
   
    ##########################################
    #### G-Tag Batterie Status ############### 
    elsif( $command eq "gtagbattery" ) {
       
        my %btmac = (
            wd_GTagBatterieMarko => '7C:2F:80:98:B8:3D',
            wd_GTagBatterieNadin => '7C:2F:80:98:B7:F2',
            wd_GTagBatterieIsabel => '7C:2F:80:98:B7:F8',
            wd_GTagBatterieSteven => '7C:2F:80:98:B8:61',
            wd_GTagBatterieAnna => '7C:2F:80:AA:5E:28'
        );
       
        while ( qx(ps ax | grep -v grep | grep gatttool) ) {
            Log3 $name, 4, "(Sub systemCommand - $name) - gatttool noch aktiv, wait 0.5s for new check";
            sleep 0.5;
        }
       
        $state = qx(gatttool -b $btmac{$name} --char-read --handle=0x001b);
        Log3 $name, 3, "(Sub systemCommand - $name) - Rufe Batteriestatus vom GTag ab. MAC: $btmac{$name}";
    }
   

   
   
   
    #################################################
    #### Weiterverarbeitung der Rückgabewerte ######
    Log3 $name, 4, "(Sub systemCommand - $name) - Rückgabe an Auswertungsprogramm beginnt Name: $name, Command: $command, State: $state";
    $state = encode_base64($state,"");
    return "$name|$command|$state";
}

sub systemCommand_Done($) {

    my ($string) = @_;
    my @a = split("\\|",$string);
    my $hash = $defs{$a[0]};
    my $name = $hash->{NAME};
    my $command = $a[1];
    my $state = $a[2];
   
   
    delete($hash->{helper}{RUNNING_PID});
   
    Log3 $name, 3, "(Sub systemCommand_Done - $name) - Der Helper ist diabled. Daher wird hier abgebrochen" if($hash->{helper}{DISABLED});
    return if($hash->{helper}{DISABLED});
   
   
    $state = decode_base64($state);
   
    Log3 $name, 4, "(Sub systemCommand_Done - $name) - State gleich: $state und command: $command";
    Log3 $name, 3, "(Sub systemCommand_Done - $name) - Abschluss vom Systemkommandoaufruf im nonBlocking Mode. Auswertungsteil beginnt.";
   
   
   
    ##############################################################
    #########   Auswertungsteil   ############
   
   
    ####################################################
    ##### wlan0 stop, start, restart, status
    if( ($name eq "notifyControlWlan0") and ($state) ) {
   
            fhem "set buttonProxy01Wlan0Status $state";
    }

    ####################################################
    ####### Firewall Skripte auf proxy01 ###########
    elsif( ($name =~ /notifyFirewall/) and (!$state) ) {
   
        fhem "set buttonSshInetFWstatus fehler" if($name eq "notifyFirewallSshInet" );
        fhem "set buttonFhemInetFWstatus fehler" if($name eq "notifyFirewallFhemInet" );
        fhem "set buttonSshInetFWstatus fehler" if( $name eq "notifyFirewall_IsabelHandy" )
    }
   
    ####################################################
    ####### Isabel Internetfreigabe über SquidGuard auf proxy01 ###########
    elsif( ($name =~ /notifyInternet_Isabel/) and (!$state) ) {
   
        fhem "set buttonIsabelInternetStatus fehler";
    }
   
    ####################################################
    #### G-Tag Batterie Status ###############
    elsif( ($name =~ /wd_GTagBatterie/) and ($state) ) {
   
        my %DEVICE = (
            wd_GTagBatterieMarko => 'presenceMarko',
            wd_GTagBatterieNadin => 'presenceNadin',
            wd_GTagBatterieIsabel => 'presenceIsabel',
            wd_GTagBatterieSteven => 'presenceSteven',
            wd_GTagBatterieAnna => 'presenceAnna'
        );
       
        my $dhash = $defs{$DEVICE{$name}};
        my $dname = $dhash->{NAME};
       
        my @blevel = split(": ",$state);
        my $blevel = hex("0x".$blevel[1]);
       
        readingsBeginUpdate($dhash);
        readingsBulkUpdate($dhash,"device_batteryLevel",$blevel);
       
        if( $blevel < 20 ) {
            readingsBulkUpdate($dhash,"device_battery","low");
        } else {
            readingsBulkUpdate($dhash,"device_battery","ok");
        }
       
        readingsEndUpdate($dhash,1);
    }
       
}

sub systemCommand_Aborted($) {

    my ($hash) = @_;
    my $name = $hash->{NAME};

    delete($hash->{helper}{RUNNING_PID});
    Log3 $name, 3, "($name) - The BlockingCall Process terminated unexpectedly. Timedout";

}
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

Zitat von: rudolfkoenig am 13 Oktober 2017, 12:04:35
die() ist keine gute Idee, da BlockingCall auf eine Rueckgabe wartet.
Generell ist die(), exit() oder Vergleichbares keine gute Idee in einem FHEM-Modul.

Ja da wollte ich den Stephan vorsichtig hinführen das er ein bisschen mehr mit Sorgfalt seinen Code schreibt.
Es ist besser wenn Du Dir angewöhnst wenn Du eine Subroutine erstellst das Du auch gleich fest legst wie viele Parameter und welcher Parametertype mit übergeben wird. Ist sauberer und andere können Deinen Code so besser lesen

sub Routinenname($$)

sub Routinenname($@)


und so weiter.
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

abc2006

Zitat von: CoolTux am 13 Oktober 2017, 12:22:19
Ja da wollte ich den Stephan vorsichtig hinführen das er ein bisschen mehr mit Sorgfalt seinen Code schreibt.
Es ist besser wenn Du Dir angewöhnst wenn Du eine Subroutine erstellst das Du auch gleich fest legst wie viele Parameter und welcher Parametertype mit übergeben wird. Ist sauberer und andere können Deinen Code so besser lesen

sub Routinenname($$)

sub Routinenname($@)


und so weiter.

Äh. Hm.
ZitatIst sauberer und andere können Deinen Code so besser lesen
Fand ich auch, deshalb hatte ich die auch drin (und hab sie bei manchen immer noch).
Warum ich diese klammern und dollars extra weg gemacht habe:

http://www.perlmonks.org/?node_id=861966 <- da schwitz ich heut noch ...
https://stackoverflow.com/questions/297034/why-are-perl-5s-function-prototypes-bad
https://www.effectiveperlprogramming.com/2011/10/understand-why-you-probably-dont-need-prototypes/
usw...

kannst du mir sagen, warum ich sie besser *doch* verwenden sollte? (Anscheinend hab ich diese Informationen ja auch glorreich falsch verstanden)


@rudi:
klingt nachvollziehbar, also schreibe ich anstatt die() besser "setze variable auf Fehlerwert" und gebe die Variable als return zurück?

Ich schau mir nachher genau an, was ihr geschrieben habt, und versuche das umzusetzen, jetzt muss ich erstmal auf Arbeit. Danke schonmal für eure Hilfe.

Bei allen Forks und Abbrüchen und Wiederaufrufen ist mir aber leider immer noch nicht klar, warum manche BlockingCalls dann unter der PPID 1 laufen und meinem (Haupt) FHEM konkurrieren...

Bis später,
Stephan
FHEM nightly auf Intel Atom (lubuntu) mit VDSL 50000 ;-)
Nutze zur Zeit OneWire und KNX

abc2006

So, jetzt hab ich dreimal angefangen, Fragen zu formulieren, und dann jetzt doch so lange gebastelt, bis es irgendwie funktioniert.

Erstmal mein Code:


controlVL_ARD ist die funktion, die mir die vom PID vorgegebene Fahrzeit in kompatible Syntax wandelt und noch ein paar Checks durchführt, sowie etwas entschwingt:
sub controlVL_ARD($) {
my $log_name = "controlVL_ARD";
my $subloglevel = 3;
my $previous_loglevel = AttrVal("global","verboseTelegram",0);
if ($previous_loglevel != $subloglevel){
fhem("attr global verboseTelegram $subloglevel");
}
log_telegram("5",$log_name,"debug aktiviert","remotebot");

my ($name) = @_;
my $hash = $defs{$name};
my $warmRelais = 1;
my $kaltRelais = 2;
my $tuwas = ReadingsVal("PID.FUBO","actuation","-5");;
my $VL = ReadingsVal("RE_TEMP_VorlaufHK","temperature","100");
my $VLSoll = ReadingsVal("PID.FUBO","desired","0");
my $SpeicherOben = ReadingsVal("RE_TEMP_Speicher_09","temperature","0");
my $KesselVL = ReadingsVal("RE_TEMP_Vorlauf_Kessel","temperature","0");
my $KesselRL = ReadingsVal("RE_TEMP_Ruecklauf_Kessel","temperature","0");
my $literpromin = ReadingsNum("KNX10.I06_Heizungszaehler_main","literpromin",1);
my $pidstate = ReadingsVal("DF_Heizschleifenfunktion","status","error");
my $order = 0;
my $ip = "192.168.0.98";
my $port = "8887";


my $readingsAge = ReadingsAge("KNX10.I06_Heizungszaehler_main","literpromin",0);
if ($readingsAge > 90 && $literpromin !=0) {
log_telegram("5",$log_name,"_literpromin $literpromin ist bereits $readingsAge Sekunden alt, setze Reading auf 0_","remotebot");
log_telegram("3",$log_name,"_literpromin $literpromin ist bereits $readingsAge Sekunden alt, setze Reading auf 0_","alarmbot");
fhem("setreading KNX10.I06_Heizungszaehler_main literpromin 0");
}elsif($literpromin == 0) {
log_telegram("5",$log_name,"_literpromin $literpromin ==0_","remotebot");
}else {
log_telegram("5",$log_name,"_literpromin $literpromin ist erst $readingsAge Sekunden alt_","remotebot");
}

## muss nach reparatur des Durchflussmessers entfernt werden
##my $literpromin = 1;
## muss nach reparatur des Durchflussmessers entfernt werden

my $message = "VL-ist:$VL VL-soll:$VLSoll";
log_telegram("4",$log_name,"N_changeVL: wurde aufgerufen","remotebot");

if ($VL > 40){
$tuwas = -50;
log_telegram("5",$log_name,"N_changeVL: RE_TEMP_VorlaufHK: [RE_TEMP_VorlaufHK:temperature]°C, fahre $tuwas Sek!","remotebot");
} elsif($literpromin == 0 && $tuwas < 0) { # und $VL < 45 {
log_telegram("5",$log_name,"tue nichts, weil literpromin = $literpromin und tuwas = $tuwas","remotebot");
$tuwas = 0;
}
if ( $pidstate eq "uebergang" && ($tuwas > 0.2 || $tuwas < -0.5) )
{

log_telegram("4",$log_name,"_tuwas : $tuwas ; pidstate: $pidstate _","remotebot");
if($tuwas > 0){
$order = $warmRelais . "R" . abs($tuwas)*1000;
} else {
$order = $kaltRelais . "R" . abs($tuwas)*1000;
}
nb_udpsend($name,$order,$ip,$port);
##fhem("setreading ardsend sent $order");
$message .= " :=> $order";
} elsif (($tuwas >= 0.7 && $SpeicherOben > $VLSoll) || $tuwas <= -0.7 )
{   
log_telegram("4",$log_name,"_tuwas : $tuwas _","remotebot");
if($tuwas > 0){
$order = $warmRelais . "R" . abs($tuwas)*1000;
} else {
$order = $kaltRelais . "R" . abs($tuwas)*1000;
}
nb_udpsend($name,$order,$ip,$port);
##fhem("setreading ardsend sent $order");
$message .= " :=> $order";
} else {
$message .= " :=> $tuwas < 0.7\n keine Änderung";
}## end if
log_telegram("3",$log_name,$message,"remotebot");
if ($previous_loglevel != $subloglevel){
fhem("attr global verboseTelegram $previous_loglevel");
}
}## end sub


nb_udpsend ist die funktion, die den BlockingCall vorbereitet, und im nächsten Schritt dann auch von controlRLA_ARD aufgerufen werden soll, um einen zweiten Mischer zu fahren ( und irgendwann dann noch von controlVL_HK, um einen dritten Heizkreis für Heizkörper zu steuern)

sub nb_udpsend($$$$) {
my $log_name = "nb_udpsend()";
my $subloglevel = 3;
my $previous_loglevel = AttrVal("global","verboseTelegram",0);
if ($previous_loglevel != $subloglevel){
fhem("attr global verboseTelegram $subloglevel");
}log_telegram("5",$log_name,"debug aktiviert","remotebot");
 
    my ($name,$order,$ip,$port) = @_;
my $hash = $defs{name};
my $blockingFn = "udpsend";
my $finishFn = "finishFn";
my $timeout = 5;
my $abortFn = "abortFn";
my $abortArg = $hash;
##     $order        $ip           $port
my $args = "$name|$order|$ip|$port";



log_telegram("4",$log_name,"_BlockingCall FN: $blockingFn ARG: $args FIN: $finishFn TO:$timeout AFN:$abortFn AARG: $abortArg _","remotebot");

##if ($anzahlBlockingCalls == 0){
BlockingKill($hash->{helper}{RUNNING_PID}) if(defined($hash->{helper}{RUNNING_PID}));
$hash->{helper}{RUNNING_PID} = BlockingCall($blockingFn,$args,$finishFn,$timeout,$abortFn,$abortArg) unless(exists($hash->{helper}{RUNNING_PID}));
## $anzahlBlockingCalls++;
##} else {
## fhem("set remotebot message zu viele calls: $anzahlBlockingCalls");
##}




if ($previous_loglevel != $subloglevel){
fhem("attr global verboseTelegram $previous_loglevel");
}
return;
}



udpsend ist die Funktion, die blockierend läuft und auf eine Antwort wartet

sub udpsend($) {

my ($parameters) = @_;
my ($name,$order,$ip,$port) = split("\\|",$parameters);
my $answer="";
my $now = strftime "%Y-%m-%d %T",localtime();
my $sock = IO::Socket::INET->new(
        Proto   => 'udp',
        PeerPort => $port,
        PeerAddr => $ip,
) or die "could not create Socket: $!\n";
$sock->send($order) or die "Send error $!\n";
$sock->setsockopt(SOL_SOCKET, SO_RCVTIMEO, pack('l!l!', 2, 0))
    or die "setsockopt: $!";
$sock->recv($answer,1024)
or die "Could not receive an answer: $!";
$sock->close();
##
$answer =~ s/(.*)\n.*/$1/g;
return "$name|$answer|$order|$ip|$port";
}


finishFn und abortFn

sub abortFn($) {
my $log_name = "abortFn(S)";
my ($hash) = @_;
my $name = $hash->{NAME};

delete($hash->{helper}{RUNNING_PID});

log_telegram("3",$log_name,"function aborted: $name","remotebot");

}
sub finishFn($) {
my $log_name = "finishFn(S)";
my ($parameters) = @_;
my ($irgendwasmitname,$answer,$order,$ip,$port) = split("\\|",$parameters);
my $hash = $defs{$irgendwasmitname};
my $name = $hash->{NAME};

delete($hash->{helper}{RUNNING_PID});


my $subloglevel = 5;
my $previous_loglevel = AttrVal("global","verboseTelegram",0);
if ($previous_loglevel != $subloglevel){
fhem("attr global verboseTelegram $subloglevel");
}
log_telegram("5",$log_name,"OUTPUT BEGIN","remotebot");
log_telegram("5",$log_name,"$answer","remotebot");
log_telegram("5",$log_name,"$order","remotebot");
log_telegram("5",$log_name,"$ip","remotebot");
log_telegram("5",$log_name,"$port","remotebot");
log_telegram("5",$log_name,"OUTPUT END","remotebot");

if($answer == $order){
log_telegram("3",$log_name,"function finished: $parameters","remotebot");
} else {
log_telegram("1",$log_name,"function finished: $parameters but:\n$answer is not equal to $order","remotebot");
}

if ($previous_loglevel != $subloglevel){
fhem("attr global verboseTelegram $previous_loglevel");
}


}
FHEM nightly auf Intel Atom (lubuntu) mit VDSL 50000 ;-)
Nutze zur Zeit OneWire und KNX

abc2006

So, hab jetzt blockingCallMax auf 2 gestellt.
Lief auch soweit zufriedenstellen, bis gerade:
Da sehe ich 5 fhem-Prozesse, obwohl es meiner Meinung nach maximal 3 sein dürften...

Grüße,
Stephan
FHEM nightly auf Intel Atom (lubuntu) mit VDSL 50000 ;-)
Nutze zur Zeit OneWire und KNX

CoolTux

Guten Morgen,

Du hast doch den BlockingCall Aufruf an ein Device gebunden!?
Wie kannst Du dann mehr wie einen Aufruf schaffen bei einem

unless(exists($hash->{helper}{RUNNING_PID}));

Mach mal bitte ein list von dem Device an dem der BlockingCall gebunden ist wenn einer läuft.
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

abc2006

Hi,
bitte hilf mir nochmal bei
Zitatvon dem Device an dem der BlockingCall gebunden ist
Ich weiss nämlich nicht genau, welches du meinst. Ist es DF_controlVL?


###############################################################
Ich habe einen PID. Der aktualisiert sein Reading PID.FUBO:actuation.

Darauf höre ich ( im moment grade) mit einem DOIF:
Internals:
   DEF        ([PID.HV_RLA:actuation])({
##controlRLA_ARD("DF_controlVL");
})
DOELSEIF
([PID.FUBO:actuation])({
controlVL_ARD("DF_controlVL");
})
   NAME       DF_controlVL
   NR         718
   NTFY_ORDER 50-DF_controlVL
   STATE      cmd_2
   TYPE       DOIF
   READINGS:
     2017-10-14 02:39:56   192.168.0.95    1503883
     2017-10-14 13:37:45   Device          PID.FUBO
     2017-10-14 13:37:45   cmd             2
     2017-10-14 13:37:45   cmd_event       PID.FUBO
     2017-10-14 13:37:45   cmd_nr          2
     2017-10-14 13:37:45   e_PID.FUBO_actuation -1.8
     2017-10-14 13:37:45   state           cmd_2
   condition:
     0          ReadingValDoIf($hash,'PID.HV_RLA','actuation')
     1          ReadingValDoIf($hash,'PID.FUBO','actuation')
   devices:
     0           PID.HV_RLA
     1           PID.FUBO
     all         PID.HV_RLA PID.FUBO
   do:
     0:
       0          {  }
     1:
       0          { controlVL_ARD("DF_controlVL"); }
     2:
   helper:
     event      desired: 24.4,measured: 25.12,p_p: -0.712800000000002,p_d: 0.0142290613320757,p_i: -1.14420000000033,actuation: -1.8,actuationCalc: -1.84277093866825
     globalinit 1
     last_timer 0
     sleeptimer -1
     timerdev   PID.FUBO
     timerevent desired: 24.4,measured: 25.12,p_p: -0.712800000000002,p_d: 0.0142290613320757,p_i: -1.14420000000033,actuation: -1.8,actuationCalc: -1.84277093866825
     triggerDev PID.FUBO
     timerevents:
       desired: 24.4
       measured: 25.12
       p_p: -0.712800000000002
       p_d: 0.0142290613320757
       p_i: -1.14420000000033
       actuation: -1.8
       actuationCalc: -1.84277093866825
     timereventsState:
       desired: 24.4
       measured: 25.12
       p_p: -0.712800000000002
       p_d: 0.0142290613320757
       p_i: -1.14420000000033
       actuation: -1.8
       actuationCalc: -1.84277093866825
     triggerEvents:
       desired: 24.4
       measured: 25.12
       p_p: -0.712800000000002
       p_d: 0.0142290613320757
       p_i: -1.14420000000033
       actuation: -1.8
       actuationCalc: -1.84277093866825
     triggerEventsState:
       desired: 24.4
       measured: 25.12
       p_p: -0.712800000000002
       p_d: 0.0142290613320757
       p_i: -1.14420000000033
       actuation: -1.8
       actuationCalc: -1.84277093866825
   internals:
   itimer:
   readings:
     0           PID.HV_RLA:actuation
     1           PID.FUBO:actuation
     all         PID.HV_RLA:actuation PID.FUBO:actuation
   regexp:
     0:
     1:
     all:
   state:
     STATE:
   trigger:
Attributes:
   do         always
   room       Heizung,HolzVergaser,x_devel
   wait       3


Dieses DOIF ruft die sub controlVL auf (die andere ist ja gerade auskommentiert)
und die ruft nb_udpsend auf (das ist dann die sub, in der der BlockingCall gestartet wird).

Soweit läuft alles prima.

Wenn ich jetzt hingehe, und im DOIF
den Aufruf von controlRLA_ARD("DF_controlVL");  aktiviere, fangen die Probleme mit den Threads an.
Ich *glaube*, dass die Probleme erst auftreten, wenn die abortFn aufgerufen wird. Solange der Aufruf nicht fehlschlägt und die finishFn genutzt wird, ist alles okay.

Vielleicht liegt es an der Funktion controlRLA_ARD(), obwohl ich mir größte Mühe gegeben habe, sie weitestgehend mit controlVL gleich zu halten...

sub controlRLA_ARD($) {
my $log_name = "controlRLA_ARD";
my $subloglevel = 3;
my $previous_loglevel = AttrVal("global","verboseTelegram",0);
if ($previous_loglevel != $subloglevel){
fhem("attr global verboseTelegram $subloglevel");
}
log_telegram("5",$log_name,"debug aktiviert","remotebot");

my ($name) = @_;
my $hash = $defs{$name};
my $warmRelais = 2;
my $kaltRelais = 1;
my $tuwas = ReadingsVal("PID.HV_RLA","actuation","-5");
my $RLA_Soll = ReadingsVal("PID.HV_RLA","desired","0");
my $RLA = ReadingsVal("RE_TEMP_HV_RLA","temperature","0");
my $order;
my $ip = "192.168.0.95";
my $port = "8888";



my $message = "RLA ist: $RLA, soll: $RLA_Soll","remotebot";

if ($tuwas >= 500 || $tuwas <= -500 )
{   
log_telegram("4",$log_name,"_tuwas : $tuwas _","remotebot");
if($tuwas > 0){
$order = $warmRelais . "R" . abs($tuwas);
} else {
$order = $kaltRelais . "R" . abs($tuwas);
}

nb_udpsend($name,$order,$ip,$port);
##nb_ardsendHV($order);
$message .= " :=> $order";
} else {
$message .= " :=> $tuwas < 500\n keine Änderung";
}## end if
log_telegram("3",$log_name,$message,"remotebot");
if ($previous_loglevel != $subloglevel){
fhem("attr global verboseTelegram $previous_loglevel");
}
}## end sub
FHEM nightly auf Intel Atom (lubuntu) mit VDSL 50000 ;-)
Nutze zur Zeit OneWire und KNX

abc2006

jetzt habe ich ein anderes interessantes Phänomen:
Fhem hat einen subprozess, und der forkt ...

FHEM nightly auf Intel Atom (lubuntu) mit VDSL 50000 ;-)
Nutze zur Zeit OneWire und KNX

abc2006

So, ich nochmal. Sorry, manche Gedankengänge dauern bei mir einfach länger.

ich übergebe der funktion den Namen von meinem DOIF (oder notify).
dann mache ich aus dem Namen den Hash und schreibe in helper-RunningPID
Wenn ich jetzt den timeout (deutlich) länger mache, müsste ich im list unter "helper" einen Punkt "runningpid" finden, in dem... irgendwas drin steht(sorry, so weit bin ich noch nicht).
Ah, und hier ist auch der Punkt, warum ich auf ein DOIF umgebaut habe:
ich hätte sonst zwei notifies mit zwei namen, die zwei funktionen aufrufen, und dann bekomm ichs nicht unter einen Hut.

Ich .. versuch jetzt erstmal, diesen runningpid zu finden...


PS: ist dir bewusst, dass es sich bei mir *noch* nicht um ein Modul handelt, sondern um subs in der 99myUtils?
Nicht dass du davon ausgehst, ich würde in einem Modul arbeiten ...

Grüße.
Stephan
FHEM nightly auf Intel Atom (lubuntu) mit VDSL 50000 ;-)
Nutze zur Zeit OneWire und KNX

CoolTux

Hallo Stephan,

Genau das ist es. Jedes Device hat in FHEM einen Device Hash. In diesem Hash stehen diverse Sachen drin. Readings Values von den Readings, Timestamps und und und. Und unter helper stehen eben auch Sachen drin wie Infos zum BlockingCall.
Und ein zweiter BlockingCall dürfte gar nicht erst anlaufen da wir ja eine Abfrage machen.
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

abc2006

Möglicherweise hab ich einen Fehler im Code ...  ::)

2017.10.14 15:37:42.708 1: Timeout for ardsendHV reached, terminated process 20594
Can't use string ("did not receive acknowledge from"...) as a HASH ref while "strict refs" in use at ./FHEM/99_myUtilsHZG.pm line 111.


Allerdings dürfte dann ja kein neuer BlockingCall gestartet werden ... [/s]

erst denken ... Ich hab die alte funktionierende sub aufgerufen, die natürlich nicht mit den Wünschen der neuen AbortFn klarkommt...
Weitersuchen :-)
FHEM nightly auf Intel Atom (lubuntu) mit VDSL 50000 ;-)
Nutze zur Zeit OneWire und KNX

CoolTux

Zum besseren Verständnis Bau dir doch Mal etwas ganz einfaches. Ein Dummy, ein notify welches auf ein set on vom Dummy triggert, das notify startet eine Routine und stellt gleichzeitig den Dummy wieder auf off. Dir Routine startet ein BlockingCall mit timeout 60s und im BlockingCall machst du ein sleep von 20s und gibst irgendwas nach durchlaufen zurück. Bei den Aufruf des BlockingCall ganz einfach auf, wie im Wiki.

Nun machst du ein set on der BlockingCall startet. Paar Sekunden später machst du noch mal Set on, ein weiterer BlockingCall dürfte nun nicht starten.
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

abc2006

Klingt nach nem Plan, danke.

Wird aber erst heute Nacht was, ich melde mich.

Grüße,
Stephan
FHEM nightly auf Intel Atom (lubuntu) mit VDSL 50000 ;-)
Nutze zur Zeit OneWire und KNX

CoolTux

Dein Projekt, ich unterstütze nur. Also ganz entspannt. In erster Linie geht es darum das Du verstehst was passiert. Und vielleicht solltest Du versuchen Deinen Code wenn denn möglich einfacher zu halten, es sei denn Du hast da den Durchblick, dann habe ich nichts gesagt  ;D

Rudi schlägt bestimmt schon bisschen die Hände über den Kopf (nett gemeint). Er hat mal gesagt Tray and Error kostet beim Programmieren nur Nerven. Er hat Recht, aber wenn das Teil dann mal rennt und Du verstanden hast wieso und wie genau reparieren sich die Nerven auf Grund von Glückmomenthormonen  ;D



Grüße
Leon
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

abc2006

Hi Leon,

Zitatich unterstütze nur

Ich weiss. Aber ich weiss das sehr zu schätzen. Ich bastel hier irgendwelchen verquirlten Kram, und du wendest deine Zeit auf, um mir zu helfen, das bedeutet mir viel!
Und ich versuche deine Erklärungen zu verstehen und nachzuvollziehen, auch wenn es bedeutet, dass ich anders arbeiten muss als bisher.
Ich versteh diese Sache mit dem Hash und dem -> halt (noch) nicht wirklich.

Zum Thema:

es gibt einen dummy D_dummy
es gibt ein notify N_notify:
   
D_dummy:on {nb_test("N_notify")}


Es gibt eine Routine test (die mit dem sleep) und eine Routine nb_test

sub nb_test($){
my ($name) = @_;
my $hash = $defs{NAME};
$hash->{helper}{RUNNING_PID} = BlockingCall("test","$hash") unless(exists($hash->{helper}{RUNNING_PID}));
}


sub test($){
Log3 "test", 1, "gestartet";
sleep 30;
Log3 "test", 1, "beendet";
}



der sleep funktioniert, und wird auch im Log dargestellt, aber ich kann so viele Blocking starten, wie ich will:
2017.10.15 02:00:14.569 3: N_notify return value: HASH(0x30488f8)
2017.10.15 02:00:14.572 1: gestartet
2017.10.15 02:00:18.664 3: N_notify return value: HASH(0x30492a0)
2017.10.15 02:00:18.667 1: gestartet
2017.10.15 02:00:44.574 1: beendet
2017.10.15 02:00:48.669 1: beendet


"list D_dummy" gibt (erwartungsgemäß) nichts von RUNNING_PID aus, "list N_notify" aber auch nicht.

Ich habe den Eindruck, dass das mit dem "an den Hash binden" nicht so ganz funktioniert. Kann es sein, dass für jeden Aufruf ein neuer $hash gebildet wird?  Jetzt am Minimal-Beispiel: Was muss ich tun?

Danke und Grüße,
Stephan
FHEM nightly auf Intel Atom (lubuntu) mit VDSL 50000 ;-)
Nutze zur Zeit OneWire und KNX

CoolTux

Hallo Stephan,

Nur kurz. Du hast einen Fehler drin.

my $hash = $defs{$name};

Schau mal was Du geschrieben hast.



Grüße
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

abc2006

Zitatmy $hash = $defs{$name};
werde ich gleich angehen.
Ich habe auch schon ein bisschen weitergebaut:

sub nb_test($){
my $blockingFn = "test";
my $finishFn = "finish";
my $timeout = 40;
my $abortFn = "abort";
my $abortArg = "test";
my $arg = "test";
my $runningpid = ReadingsVal("D_dummy","blockingpid",1);
if($runningpid < 1){
my $BChash = BlockingCall($blockingFn,$arg,$finishFn,$timeout,$abortFn,$abortArg);
fhem("setreading D_dummy blockingpid $BChash->{pid}");
} else {
Log3 "test", 1, "es läuft noch ein BlockingCall";
}
}


sub test($){
Log3 "test", 1, "gestartet";
sleep 30;
Log3 "test", 1, "beendet";
}

sub finish(){
Log3 "test", 1, "finish";
fhem("setreading D_dummy blockingpid 0");
}
sub abort($){
Log3 "test", 1, "abort";
fhem("setreading D_dummy blockingpid -1");
}



Läuft einwandfrei (habs halt jetzt über ein Reading realisiert, schaue mir den $hash trotzdem gleich nochmal an)

Aber: Damit habe ich noch zwei Punkte:

1.)  Rudi schlug das Attribut blockingCallMax vor. Das soll die BlockingCall Aufrufe ja beschränken und in eine queue stellen. Hab ich probiert, tut es. Ist auch genau das (ein Teil davon) was ich mir vorstellen würde. Ich müsste also nicht selbst eine queue implementieren, sondern könnte die globale nutzen.

2.) die BlockingCalls, die ich mit dem o.g. Code erzeuge, haben alle die PPID des originalen FHEM-Prozesses. Ich hab es nicht geschafft, einen weiteren FHEM-Prozess mit der PPID 1 zu erzeugen. Hier hänge ich noch fest...
FHEM nightly auf Intel Atom (lubuntu) mit VDSL 50000 ;-)
Nutze zur Zeit OneWire und KNX

abc2006

Zitat von: CoolTux am 15 Oktober 2017, 02:24:58
Hallo Stephan,

Nur kurz. Du hast einen Fehler drin.

my $hash = $defs{$name};

Schau mal was Du geschrieben hast.



Grüße


Jaaaaa geil, kaum macht mans richtig, schon gehts. Klasse!
dann bau ich jetzt mal an den großen funktionen rum.

Hast du noch irgendwo ein Beispiel (oder nen Tipp), durch was ich das

die() am besten ersetze?
Vielleicht löst sich ja dadurch auch schon mein Problem...

Grüße,
Stephan

edit: und jetzt ist mir das Ding mit den Hashes auf einmal viel klarer geworden... glaub ich!
FHEM nightly auf Intel Atom (lubuntu) mit VDSL 50000 ;-)
Nutze zur Zeit OneWire und KNX

abc2006

So,
hab die BlockingFn wie folgt abgeändert. Das Problem war anscheinend wirklich das die() (falls du die Lösung so als *gut* erachtest ;-)

Grüße,
Stephan

sub udpsend($) {

my ($parameters) = @_;
my ($name,$order,$ip,$port) = split("\\|",$parameters);
my $answer="";
my $now = strftime "%Y-%m-%d %T",localtime();
my $sock = IO::Socket::INET->new(
        Proto   => 'udp',
        PeerPort => $port,
        PeerAddr => $ip,
) or die "could not create Socket: $!\n";
$sock->send($order) or die "Send error $!\n";
$sock->setsockopt(SOL_SOCKET, SO_RCVTIMEO, pack('l!l!', 2, 0))
    or die "setsockopt: $!";
$sock->recv($answer,1024)
or die "Could not receive an answer: $!";
$sock->close();
##
$answer =~ s/(.*)\n.*/$1/g;
my $return = "$name|$answer|$order|$ip|$port";
my $return = encode_base64($return,"");
return $return;
}
FHEM nightly auf Intel Atom (lubuntu) mit VDSL 50000 ;-)
Nutze zur Zeit OneWire und KNX

CoolTux

Sieht gut aus was ich so sehen kann. Läuft denn Dein Code nun wie gewünscht?
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

abc2006

So, nachdem ich mir heute durch das speichern einer gecachten Seite alles wieder zerschossen habe, läufts jetzt wieder  >:(

Ja, mein Code macht (im moment) das was er soll. Zur Vollständigkeit nochmal unten anbei.
Um in der Abortfunction feststellen zu können, was der Fehler war, bin ich auf die Internen Variablen umgestiegen.
Oder habe ich eine Möglichkeit, der abortFn ein dynamisches Argument mitzugeben, übersehen?

Womit ich extra gewartet habe, bis das erste Problem im Griff ist: Die Sub ist ja nur deswegen Blocking, weil sie auf eine Antwort wartet. Dazu hatte ich folgendes gefunden:
Zitatwenn es nur darum geht beim lesen nicht zu blockieren kannst du das aber viel einfacher in dem du deinen filedescriptor per selectlist in die fhem event loop einhängst. du wirst dann automatisch aufgerufen wenn daten für dich da sind. ein beispiel dafür ist das telnet modul in fhem.

Zugegebenermaßen, in das Telnet-Modul habe ich bisher nicht reingeschaut. Würde ich heute Abend machen. Denkst du, es macht Sinn, meine Sub auf *selectlist* umzubauen?
Dazu bräuchte ich vermutlich nochmal hilfe.

Danke,
Stephan



sub nb_udpsend($$$$) {
my $log_name = "nb_udpsend()";
my $subloglevel = 5;
my $previous_loglevel = AttrVal("global","verboseTelegram",0);
if ($previous_loglevel != $subloglevel){
fhem("attr global verboseTelegram $subloglevel");
}
log_telegram("5",$log_name,"debug aktiviert","remotebot");
 
    my ($name,$order,$ip,$port) = @_;
my $hash = $defs{$name};
my $blockingFn = "udpsend";
my $finishFn = "finishFn";
my $timeout = 2;
my $abortFn = "abortFn";
my $abortArg = $name;
##     $order        $ip           $port
my $args = "$name|$order|$ip|$port";
##delete($hash->{helper}{RUNNING_PID});

##log_telegram("4",$log_name,"_BlockingCall FN: $blockingFn ARG: $args FIN: $finishFn TO:$timeout AFN:$abortFn AARG: $abortArg _","remotebot");

##BlockingKill($hash->{helper}{RUNNING_PID}) if(defined($hash->{helper}{RUNNING_PID}));
if(!exists($hash->{helper}{RUNNING_PID})){
$hash->{helper}{RUNNING_PID} = BlockingCall($blockingFn,$args,$finishFn,$timeout,$abortFn,$abortArg);
log_telegram("4",$log_name,"_starte BlockingCall FN: $blockingFn ARG: $args FIN: $finishFn TO:$timeout AFN:$abortFn AARG: $abortArg _","remotebot");
} else {
log_telegram("2",$log_name,"BlockingCall $hash->{helper}{RUNNING_PID}{pid} is already running","remotebot");
if ($hash->{helper}{RUNNING_PID}{pid} =~ "DEAD"){
log_telegram("2",$log_name,"töte BlockingCall","remotebot");
BlockingKill($hash->{helper}{RUNNING_PID});
delete($hash->{helper}{RUNNING_PID});
}
}
$hash->{helper}{RUNNING_PID}{OWN_PORT} = $port;
$hash->{helper}{RUNNING_PID}{OWN_IP} = $ip;
$hash->{helper}{RUNNING_PID}{OWN_ORDER} = $order;

if ($previous_loglevel != $subloglevel){
fhem("attr global verboseTelegram $previous_loglevel");
}
return;
}

sub udpsend($) {

my ($parameters) = @_;
my ($name,$order,$ip,$port) = split("\\|",$parameters);
my $hash = $defs{$name};
my $answer="";
my $now = strftime "%Y-%m-%d %T",localtime();
my $sock = IO::Socket::INET->new(
        Proto   => 'udp',
        PeerPort => $port,
        PeerAddr => $ip,
);
my $status .=$!;
##if(defined($!)){return "could not create Socket: $!"};
$sock->send($order);
my $status .=$!;
##if(defined($!)){return "Send error $!"};
$sock->setsockopt(SOL_SOCKET, SO_RCVTIMEO, pack('l!l!', 2, 0));
my $status .=$!;
##if(defined($!)){return "setsockopt: $!"};
$sock->recv($answer,1024);
my $status .=$!;
##if(defined($!)){return "Could not receive an answer: $!"};
$sock->close();
##
$answer =~ s/(.*)\n.*/$1/g;
my $return = "$name|$answer|$order|$ip|$port|$status";
my $ret = encode_base64($return,"");
$hash->{helper}{RUNNING_PID}{OWN_ANSWER} = $answer;
$hash->{helper}{RUNNING_PID}{OWN_STATUS} = $status;

return $ret;
##return $order;


}


sub abortFn($) {
my $log_name = "abortFn(S)";
my ($name) = @_;
my $hash= $defs{$name};
my $subloglevel = 5;
my $previous_loglevel = AttrVal("global","verboseTelegram",0);
if ($previous_loglevel != $subloglevel){
fhem("attr global verboseTelegram $subloglevel");
}

my $answer = $hash->{helper}{RUNNING_PID}{OWN_ANSWER};
my $port = $hash->{helper}{RUNNING_PID}{OWN_PORT};
my $ip = $hash->{helper}{RUNNING_PID}{OWN_IP};
my $order = $hash->{helper}{RUNNING_PID}{OWN_ORDER};
my $status = $hash->{helper}{RUNNING_PID}{OWN_STATUS};


delete($hash->{helper}{RUNNING_PID});

log_telegram("3",$log_name,"function aborted: $name","remotebot");
log_telegram("5",$log_name,"OUTPUT BEGIN","remotebot");
log_telegram("5",$log_name,"ANSWER: $answer","remotebot");
log_telegram("5",$log_name,"ORDER: $order","remotebot");
log_telegram("5",$log_name,"IP: $ip","remotebot");
log_telegram("5",$log_name,"PORT: $port","remotebot");
log_telegram("5",$log_name,"STATUS: $status","remotebot");
log_telegram("5",$log_name,"OUTPUT END","remotebot");

if ($previous_loglevel != $subloglevel){
fhem("attr global verboseTelegram $previous_loglevel");
}

}
sub finishFn($) {
my $log_name = "finishFn(S)";

my ($input) = @_;
my $parameters = decode_base64($input);
my ($name,$answer,$order,$ip,$port,$status) = split("\\|",$parameters);
my $hash = $defs{$name};

delete($hash->{helper}{RUNNING_PID});


my $subloglevel = 5;
my $previous_loglevel = AttrVal("global","verboseTelegram",0);
if ($previous_loglevel != $subloglevel){
fhem("attr global verboseTelegram $subloglevel");
}
log_telegram("5",$log_name,"OUTPUT BEGIN","remotebot");
log_telegram("5",$log_name,"ANSWER: $answer","remotebot");
log_telegram("5",$log_name,"ORDER: $order","remotebot");
log_telegram("5",$log_name,"IP: $ip","remotebot");
log_telegram("5",$log_name,"PORT: $port","remotebot");
log_telegram("5",$log_name,"STATUS: $status","remotebot");
log_telegram("5",$log_name,"OUTPUT END","remotebot");

if($answer == $order){
log_telegram("3",$log_name,"function finished: $parameters","remotebot");
} else {
log_telegram("1",$log_name,"function finished: $parameters but:\n$answer is not equal to $order","remotebot");
}

if ($previous_loglevel != $subloglevel){
fhem("attr global verboseTelegram $previous_loglevel");
}


}

####################################################################################
sub controlVL_ARD($) {
my $log_name = "controlVL_ARD";
my $subloglevel = 3;
my $previous_loglevel = AttrVal("global","verboseTelegram",0);
if ($previous_loglevel != $subloglevel){
fhem("attr global verboseTelegram $subloglevel");
}
log_telegram("5",$log_name,"debug aktiviert","remotebot");

my ($name) = @_;
my $hash = $defs{$name};
my $warmRelais = 1;
my $kaltRelais = 2;
my $tuwas = ReadingsVal("PID.FUBO","actuation","-5");;
my $VL = ReadingsVal("RE_TEMP_VorlaufHK","temperature","100");
my $VLSoll = ReadingsVal("PID.FUBO","desired","0");
my $SpeicherOben = ReadingsVal("RE_TEMP_Speicher_09","temperature","0");
my $KesselVL = ReadingsVal("RE_TEMP_Vorlauf_Kessel","temperature","0");
my $KesselRL = ReadingsVal("RE_TEMP_Ruecklauf_Kessel","temperature","0");
my $literpromin = ReadingsNum("KNX10.I06_Heizungszaehler_main","literpromin",1);
my $pidstate = ReadingsVal("DF_Heizschleifenfunktion","status","error");
my $order = 0;
my $ip = "192.168.0.98";
my $port = "8887";


my $readingsAge = ReadingsAge("KNX10.I06_Heizungszaehler_main","literpromin",0);
if ($readingsAge > 90 && $literpromin !=0) {
log_telegram("5",$log_name,"_literpromin $literpromin ist bereits $readingsAge Sekunden alt, setze Reading auf 0_","remotebot");
log_telegram("3",$log_name,"_literpromin $literpromin ist bereits $readingsAge Sekunden alt, setze Reading auf 0_","alarmbot");
fhem("setreading KNX10.I06_Heizungszaehler_main literpromin 0");
}elsif($literpromin == 0) {
log_telegram("5",$log_name,"_literpromin $literpromin ==0_","remotebot");
}else {
log_telegram("5",$log_name,"_literpromin $literpromin ist erst $readingsAge Sekunden alt_","remotebot");
}

## muss nach reparatur des Durchflussmessers entfernt werden
##my $literpromin = 1;
## muss nach reparatur des Durchflussmessers entfernt werden

my $message = "VL-ist:$VL VL-soll:$VLSoll";
log_telegram("4",$log_name,"N_changeVL: wurde aufgerufen","remotebot");

if ($VL > 40){
$tuwas = -50;
log_telegram("5",$log_name,"N_changeVL: RE_TEMP_VorlaufHK: [RE_TEMP_VorlaufHK:temperature]°C, fahre $tuwas Sek!","remotebot");
} elsif($literpromin == 0 && $tuwas < 0) { # und $VL < 45 {
log_telegram("5",$log_name,"tue nichts, weil literpromin = $literpromin und tuwas = $tuwas","remotebot");
$tuwas = 0;
}
if ( $pidstate eq "uebergang" && ($tuwas > 0.2 || $tuwas < -0.5) )
{

log_telegram("4",$log_name,"_tuwas : $tuwas ; pidstate: $pidstate _","remotebot");
if($tuwas > 0){
$order = $warmRelais . "R" . abs($tuwas)*1000;
} else {
$order = $kaltRelais . "R" . abs($tuwas)*1000;
}
nb_udpsend($name,$order,$ip,$port);
##fhem("setreading ardsend sent $order");
$message .= " :=> $order";
} elsif (($tuwas >= 0.7 && $SpeicherOben > $VLSoll) || $tuwas <= -0.7 )
{   
log_telegram("4",$log_name,"_tuwas : $tuwas _","remotebot");
if($tuwas > 0){
$order = $warmRelais . "R" . abs($tuwas)*1000;
} else {
$order = $kaltRelais . "R" . abs($tuwas)*1000;
}
nb_udpsend($name,$order,$ip,$port);
##fhem("setreading ardsend sent $order");
$message .= " :=> $order";
} else {
$message .= " :=> $tuwas < 0.7\n keine Änderung";
}## end if
log_telegram("3",$log_name,$message,"remotebot");
if ($previous_loglevel != $subloglevel){
fhem("attr global verboseTelegram $previous_loglevel");
}
}## end sub



#########################################################################################################################
sub controlRLA_ARD($) {
my $log_name = "controlRLA_ARD";
my $subloglevel = 3;
my $previous_loglevel = AttrVal("global","verboseTelegram",0);
if ($previous_loglevel != $subloglevel){
fhem("attr global verboseTelegram $subloglevel");
}
log_telegram("5",$log_name,"debug aktiviert","remotebot");

my ($name) = @_;
my $hash = $defs{$name};
my $warmRelais = 2;
my $kaltRelais = 1;
my $tuwas = ReadingsVal("PID.HV_RLA","actuation","-5");
my $RLA_Soll = ReadingsVal("PID.HV_RLA","desired","0");
my $RLA = ReadingsVal("RE_TEMP_HV_RLA","temperature","0");
my $order;
my $ip = "192.168.0.95";
my $port = "8888";



my $message = "RLA ist: $RLA, soll: $RLA_Soll","remotebot";

if ($tuwas >= 500 || $tuwas <= -500 )
{   
log_telegram("4",$log_name,"_tuwas : $tuwas _","remotebot");
if($tuwas > 0){
$order = $warmRelais . "R" . abs($tuwas);
} else {
$order = $kaltRelais . "R" . abs($tuwas);
}

nb_udpsend($name,$order,$ip,$port);
##nb_ardsendHV($order);
$message .= " :=> $order";
} else {
$message .= " :=> $tuwas < 500\n keine Änderung";
}## end if
log_telegram("3",$log_name,$message,"remotebot");
if ($previous_loglevel != $subloglevel){
fhem("attr global verboseTelegram $previous_loglevel");
}
my $return = "dies ist die veraltete funktion|dies ist die veraltete funktion|dies ist die veraltete funktion|dies ist die veraltete funktion|dies ist die veraltete funktion";
my $return = encode_base64($return,"");
return $return;
}## end sub
FHEM nightly auf Intel Atom (lubuntu) mit VDSL 50000 ;-)
Nutze zur Zeit OneWire und KNX

CoolTux


sub myUtils_BlaBla_open($hash) {

     my $hash    = shift;
.......
my $sock = IO::Socket::INET->new(
        Proto   => 'udp',
        PeerPort => $port,
        PeerAddr => $ip,
)
or return Log3 $name, 3, "Couldn't connect to $host:$port";


$hash->{FD}    = $socket->fileno();
$hash->{CD}    = $socket;
$selectlist{$name} = $hash;


.....


Sicherlich war es so gemeint. Du brauchst nun noch die passende Read Sub dazu


sub 99myUtils_BlaBla_Read($) {

    my $hash = shift;
    my $name = $hash->{NAME};
   
    my $len;
    my $buf;
   
   
    Log3 $name, 4, "ReadFn started";

    $len = sysread($hash->{CD},$buf,10240);


........


In $buf stecken dann Deine Daten.

Allerdings empfehle ich Dir das alles in eine eigene 99_myUtils_BlaBla.pm zu stecken mit einer passenden Initialize ganz oben

sub myUtils_BlaBla_Initialize($) {

    my ($hash) = @_;

    $hash->{ReadFn}     = "myUtils_BlaBla_Read";
    $hash->{WriteFn}    = "myUtils_BlaBla_Write";

........

Die Write falls Du auch mal Daten schreiben willst.

Und dann eben noch eine Sub zum verarbeiten der erhaltenen Daten.


Zum Thema abortFn


sub AbortedFn($) {

    my ($hash)  = @_;
    my $name    = $hash->{NAME};

    delete($hash->{helper}{RUNNING_PID});


    Log3 $name, 4, " ($name) - ExecGatttool_Aborted: The BlockingCall Process terminated unexpectedly. Timedout";
}


Das ist schon alles. Seit neustem gibt es noch einen zweiten Parameter der an die AbortFn übergeben wird, den kann man mit auswerten wenn man will. Schau Dir am besten mal den Code von Blocking.pm an
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

Huch fast vergessen


sub 99myUtils_BlaBla_Write($@) {

    my ($hash,$string)  = @_;
    my $name            = $hash->{NAME};


   syswrite($hash->{CD}, $string);



}


Nun hast Du aber immer noch das Problem das Du das ganze an ein Device binden musst damit Du immer zugriff auf den Hash hast. Mein persönliches vorgehen wäre innerhalb dieser myUtils irgendwie ein Device anlegen zu lassen. Aber ganz ehrlich, wenn Du das alles so machst, dann kannst auch gleich ein eigenes Modul schreiben. Nur so für Dich.
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

abc2006

Ja, war auch schon meine Überlegung.
Ich hab auch ein (eigenes) Modul (schon in Betrieb), was Daten von einem "volkszähler-USB-Modul" liest.
Das würde ich dann auch mit in Angriff nehmen.


Danke für die Tipps, bin ab morgen erstmal ne Woche im Urlaub. Melde mich dann, falls ich noch Fragen hab.

Grüße,
Stephan
FHEM nightly auf Intel Atom (lubuntu) mit VDSL 50000 ;-)
Nutze zur Zeit OneWire und KNX

abc2006

ZitatSeit neustem gibt es noch einen zweiten Parameter der an die AbortFn übergeben wird, den kann man mit auswerten wenn man will. Schau Dir am besten mal den Code von Blocking.pm

Vielleicht bin ich blind, aber ich habe keinen weiteren Parameter gefunden (den ich aus dem child an die AbortFn übergeben könnte ...

Grüße,
Stephan
FHEM nightly auf Intel Atom (lubuntu) mit VDSL 50000 ;-)
Nutze zur Zeit OneWire und KNX

CoolTux

Du musst da nichts übergeben. Das macht Blocking.pm von sich aus.

sub Test_Abort($$} {

my ($hash,$msg) = @_;


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

rudolfkoenig

Oder auch: Im zweiten (optionalen) Parameter steht der Grund des Abbruchts (Timeout, oder Kind verstorben).
Wenn du selbst mehrere Parameter uebergeben willst, dann muss man die weiterhin alle ins $hash stecken, da hat sich nichts geaendert.

CoolTux

ups dann ist meine Version falsch


sub Test_Abort($$} {

my ($hash,$msg) = @_;


Muss dann wohl zu

sub Test_Abort($;$} {

my ($hash,$msg) = @_;
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

abc2006

jaaa, das habe ich gesehen.. das problem ist, dass $msg nicht von der "blockigen" kommt, sondern bereits im blockingCall übergeben werden muss.. (im Gegensatz zur FinishFn, die sehr wohl den return-Wert erhält). Das heisst, ich kann in der AbortFn *nicht* auf Fehler in der blockigen reagieren, sondern *ausschließlich* auf Timeouts und Todesfälle.  Oder?

grade die Antwort von Rudi gelesen - alles klar. Hätte es praktisch gefunden, zwei Funktionen zu haben, eine für "hat geklappt" und eine für "hat nicht geklappt", jeweils mit dem return-Wert aufgerufen...

Aber:
Ist nicht schlimm, ich habs mittlerweile alles in die finishFn gebaut und bin jetzt dabei, ein Modul zu schreiben ...  ( und, dank euch, viel gelernt!)

Mal sehen, ob das was wird  ::)

Grüße,
Stephan
FHEM nightly auf Intel Atom (lubuntu) mit VDSL 50000 ;-)
Nutze zur Zeit OneWire und KNX