Ankündigung

Einklappen
Keine Ankündigung bisher.

Neues Plugin: Logikprozessor.pl

Einklappen
X
 
  • Filter
  • Zeit
  • Anzeigen
Alles löschen
neue Beiträge

    [WireGate-Plugin] Neues Plugin: Logikprozessor.pl

    Hallo zusammen,
    der "Translator", den ich neulich gepostet habe, ist mittlerweile so verbessert, dass er eigentlich eine einfache Logikmaschine geworden ist. Viele einfache Dinge lassen sich damit per Wiregate-Plugin lösen.

    Man spezifiziert eine beliebige Zahl von Logiken im Konfigurationsfile, und der Logikprozessor (=Wiregate-Plugin) abonniert die passenden GAs und führt bei eingehenden Telegrammen die entsprechenden Logiken aus. Auch mehrere Logiken für die gleiche GA sind kein Problem.

    Hier ein Eindruck der Möglichkeiten:

    1. Alle Werte, die auf einer GA gesendet werden, mit 2 multiplizieren und auf einer anderen GA weitergeben. Das kostet nur eine Zeile im Konfigurationsfile:
    Code:
        mal2 => { receive=>'9/5/201', transmit=>'9/5/202', translate => sub { 2*$input; }, },
    2. Aus einem relativen Dimmwert einen absoluten Dimmwert machen. Die Werte auf der ersten GA werden aufsummiert, das Ergebnis auf der anderen GA gesendet. Wieder nur eine Zeile im Konfigurationsfile:
    Code:
        sum => { receive=>'9/5/207', transmit=>'9/5/208', translate => sub { $state-2*$input; }, },
    $state enthaelt dabei das jeweils letzte Ergebnis, und das Minuszeichen... ist ein KNX-Geheimnis :-) .

    3. Wie oben, aber das Ergebnis auf den Bereich 0-100 limitieren
    Code:
        lsum => { receive=>'1/2/7', transmit=>'1/2/8', translate => sub { limit(0,$state-2*$input,100); }, },
    4. Hier eine "Treppenlichtfunktion". Auf jeden Schreibzugriff auf die receive-Adresse wird 10min spaeter eine 0 an die transmit-Adresse (hier sind beide gleich) geschickt.
    Code:
        stair => { receive=>'1/2/9', transmit=>'1/2/9', delay=>600, translate => 0, },
    Verzoegert wird uebrigens nur das Senden, nicht das Ausfuehren der translate-Routine. Neu ist hier der "delay"-Parameter, ausserdem der Spezialfall, dass translate einfach eine Konstante als Rueckgabewert spezifiziert.

    Bemerkungen:

    * translate darf nur entweder eine Konstante oder ausführbarer Code (sub {...}) sein.

    * Damit im Fall transmit==receive der Translator nicht auf sein eigenes Schreibtelegramm immer wieder antwortet, wird nur dann gesendet,
    wenn Ergebnis!=Input oder Sender des empfangenen Telegramms!=0 (Wiregate).

    Hier noch ein Feature, für das mir momentan kein Beispiel einfällt: analog der Klausel "delay=>600" oben gibt es auch die Spezifikation "transmit_only_on_request=>1". Diese bewirkt, dass das Ergebnis von translate GAR NICHT gesendet wird, sondern dass auf eine spätere Leseanforderung auf der transmit-Adresse mit dem berechneten Ergebnis geantwortet wird.

    5. Eine Logik, die einem Lichtanschalten gleich einen Dimmwert hinterherfeuert, und zwar tags und nachts einen verschiedenen:
    Code:
        dim => { receive=>'2/2/9', transmit=>'2/3/9', translate => sub { return unless $input; $day ? 80 : 5; }, },
    Die Variablen $day_of_week (Mo...So), $weekend (0 oder 1), $time_of_day ("08:34:02"), $hour_of_day ("08"), $day (1 falls zwischen 7 und 23 Uhr, 0 sonst) und $night (entsprechend umgekehrt) sind für diese Logiken vorbesetzt

    6. Memory-Funktion. Für KNX-Geräte, die kein Auslesen ihres Statuswerts zulassen (z.B. MDT DaliControl bei Einzel-EVG-Ansteuerung). Sehr einfach:
    Code:
        memory => { transmit=>'1/2/9' },
    Hier wird folgende Eigenschaft der Logik ausgenutzt: Wenn ein Write-Telegramm auf die Transmit-Adresse kommt, speichert der Logikprozessor den Wert immer automatisch ab. Eine Leseanfrage auf der transmit-GA hingegen wird immer mit dem letzten Wert (hier also dem gespeicherten) beantwortet. Eine receive-Adresse oder translate-Logik werden hier gar nicht gebraucht.

    7. Eine einfache UND-Logik mit zwei Eingaengen. Falls ein Telegramm auf einer der beiden receive-GAs empfangen wird, wird die andere Adresse noch ausgelesen, die Logik ausgeführt und das Ergebnis auf der transmit-GA uebermittelt:
    Code:
        und => { receive=>['1/2/12','1/2/13'], transmit=>'1/2/14', translate => sub { $input->[0] && $input->[1]; }, },
    Neu ist hier die Verwendung eines Arrays für receive. Wenn das geschieht, ist auch $input ein Array, und die Werte sind so angeordnet wie die Gruppenadressen in receive. Arrays für transmit werden übrigens NICHT unterstützt.

    8. Ein komplexerer Fall als Demonstration: hier sind receive und transmit wieder einfache GAs, dafür besteht der Status des Logikprozessors aus mehreren Werten. Es wird immer die Summe aus letztem, vorletztem und aktuellem Wert gesendet, und zwar mit 30s Verzoegerung.
    Code:
        complex => { receive=>'9/5/205',
                     transmit=>'9/5/206',
                     delay=>30,
                     state => {val1=>0, val2=>0},
                     translate => sub { $state->{val2}=$state->{val1}; $state->{val1}=$state->{result};
                                        $state->{val2}+$state->{val1}+$input; },
        },
    Wenn state ein Hash ist, wird der letzte gesendete Wert - also das was sonst im skalaren $state stehen würde - in $state->{result} übergeben.
    (Für $state sind nur Hashes und Skalare erlaubt, keine Arrays).

    9. Schlussendlich wieder mal Werbung fuer meine GA-Kurznamen. Setzt man im Skript Logikprozessor.pl "$use_short_names=1" (ganz oben im Skript) und verwendet GA-Namen mit eindeutigem Kuerzel (Kuerzel=erstes Wort des Namens), so funktioniert auch das folgende:
    Code:
        D_SZ_Decke => { receive=>'LR_SZ_Decke_1', transmit=>'LK_SZ_Decke_1',
                        translate => sub { limit(0,$state-2*$input,100); }, },
    Das ist doch leserlicher als Beispiel 3, oder? Hier wird ein relativer Dimmwert (LR) im Schlafzimmer (SZ) durch Skalierung und Summierung in einen absoluten Wert für die Konstantlichtregelung (LK) umgewandelt.

    Zum Schluss: Have fun!

    Der Logikprozessor ist gerade "frisch aus dem Ofen", macht bei mir im Testbrett (mittlerweile ist das ein Schaltschrank) so einiges, aber "nothing is perfect". Ich freue mich über Verbesserungsvorschläge und Bug reports.

    NACHTRÄGLICHES EDIT: Der Logikprozessor ist inzwischen erheblich erweitert, u.a. um eine Verzögerungsfunktion für die Logik (delay), eine Karenzzeit, um zu häufiges Senden oder Zirkellogiken auszuschließen (cool), und eine komplette Timerfunktion mit umfangreichen Möglichkeiten (z.B. Weckfunktion nur an Arbeitstagen um 7:30). Für mehr Infos bitte diesem Thread folgen und unbedingt den Code aus dem SVN verwenden, nicht den hier geposteten (veraltet). Wiregate PL32 ist mittlerweile ebenfalls "Pflicht", zumindest wenn man alle Features nutzen will.


    VG,
    Fry

    #2
    Hier der Code des Logikprozessors:

    Code:
    #!/usr/bin/perl -w
    ##################
    # Logikprozessor #
    ##################
    # Wiregate-Plugin
    # (c) 2012 Fry under the GNU Public License
    
    #$plugin_info{$plugname.'_cycle'}=0; return 'deaktiviert';
    
    my $use_short_names=0; # 1 fuer GA-Kuerzel (erstes Wort des GA-Namens), 0 fuer die "nackte" Gruppenadresse
    
    # eibgaconf fixen falls nicht komplett indiziert
    if($use_short_names && !exists $eibgaconf{ZV_Uhrzeit})
    {
        for my $ga (grep /^[0-9\/]+$/, keys %eibgaconf)
        {
            $eibgaconf{$ga}{ga}=$ga;
            my $name=$eibgaconf{$ga}{name};
            next unless defined $name;
            $eibgaconf{$name}=$eibgaconf{$ga};
    
            next unless $name=~/^\s*(\S+)/;
            my $short=$1;
            $short='ZV_'.$1 if $eibgaconf{$ga}{name}=~/^Zeitversand.*(Uhrzeit|Datum)/;
    
            $eibgaconf{$ga}{short}=$short;
            $eibgaconf{$short}=$eibgaconf{$ga};
        }
    }
    
    # Tools und vorbesetzte Variablen fue die Logiken
    sub limit { my ($lo,$x,$hi)=@_; return $x<$lo?$lo:($x>$hi?$hi:$x); }
    my $day_of_week=`/bin/date +"%a"`;
    my $weekend=($day_of_week=~/Sa|So/);
    my $time_of_day=`/bin/date +"%X"`;
    my $hour_of_day=substr($time_of_day,0,2);
    my $day=($hour_of_day>7 && $hour_of_day<23);
    my $night=!$day;
    
    # Konfigurationsfile einlesen
    my %logic=();
    my $conf="/etc/wiregate/plugin/generic/conf.d/$plugname"; $conf=~s/\.pl$/.conf/;
    open FILE, "<$conf" || return "no config found";
    $/=undef;
    my $lines = <FILE>;
    $lines =~ s/(translate\s*=>\s*sub\s*\{)/$1 my \(\$state,\$input\)=\@\_;/sg; 
    close FILE;
    eval($lines);
    return "config error: $@" if $@;
    
    # Aufrufgrund ermitteln
    my $event=undef;
    if (!$plugin_initflag) 
    { $event='restart'; } # Restart des daemons / Reboot
    elsif ($plugin_info{$plugname.'_lastsaved'} > $plugin_info{$plugname.'_last'})
    { $event='modified'; } # Plugin modifiziert
    elsif (%msg) { $event='bus'; } # Bustraffic
    elsif ($fh) { $event='socket'; } # Netzwerktraffic
    else { $event='cycle'; } # Zyklus
    
    # Konfigfile seit dem letzten Mal geaendert?
    my $config_modified = (24*60*60*(-M $conf)-time()) > $plugin_info{$plugname.'_configtime'};
    
    # Plugin-Code
    my $retval='';
    
    if($event=~/restart|modified/ || $config_modified) 
    {
        $plugin_info{$plugname.'_configtime'}=(24*60*60*(-M $conf)-time());
    
        # alle Variablen loeschen und neu initialisieren, alle GAs abonnieren
        for my $k (grep /^$plugname\_/, keys %plugin_info)
        {
            delete $plugin_info{$k};
        }
    
        my $count=0;
        my $err=0;
    
        for my $t (keys %logic)
        {
            # Eintrag pruefen
            if(defined $logic{$t}{receive} && ref $logic{$t}{receive} && ref $logic{$t}{receive} ne 'ARRAY')
            {
                plugin_log($plugname, "Config err: \$logic{$t}{receive} ist weder Skalar noch ARRAY-Referenz ([...]).");
                next;
            }
    
            if(defined $logic{$t}{translate} && ref $logic{$t}{translate} && ref $logic{$t}{translate} ne 'CODE')
            {
                plugin_log($plugname, "Config err: \$logic{$t}{translate} ist weder Skalar noch CODE-Referenz (sub {...}).");
                next;
            }
    
            if(defined $logic{$t}{state} && ref $logic{$t}{state} && ref $logic{$t}{state} ne 'HASH')
            {
                plugin_log($plugname, "Config err: \$logic{$t}{state} ist weder Skalar noch HASH-Referenz ({...}).");
                next;
            }
    
            # transmit-Adresse abonnieren
            my $transmit=groupaddress($logic{$t}{transmit});
            $plugin_subscribe{$transmit}{$plugname}=1;
    
            $count++;
    
            # alle receive-Adressen abonnieren (eine oder mehrere)
            my $receive=groupaddress($logic{$t}{receive});
    
            next unless $receive;
            
            unless(ref $receive)
            { 
                $plugin_subscribe{$receive}{$plugname}=1; 
            }
            else
            {
                for my $rec (@{$receive})
                {
                    $plugin_subscribe{$rec}{$plugname}=1;
                }
            }
        }
    
        $plugin_info{$plugname.'_cycle'}=0;
        $retval.=$count." initialisiert";
    }
    
    if($event=~/bus/)
    {
        # Bustraffic bedienen - nur Schreibzugriffe der iButtons interessieren
        return unless $msg{apci}=~/A_GroupValue_(Write|Read)/;  
    
        my $ga=$msg{dst};
        my $in=$msg{value};
        my $keep_subscription=0; # falls am Ende immer noch Null, die GA stornieren
    
        # welche translate-Logik ist aufgerufen?
        for my $t (keys %logic)
        {
            my $transmit=groupaddress($logic{$t}{transmit});
            my $transmit_ga = ($ga eq $transmit);
    
            my $receive=groupaddress($logic{$t}{receive});
            my $receive_ga=0; 
    
            if(defined $receive)
            {
                unless(ref $logic{$t}{receive})
                {
                    $receive_ga=1 if $ga eq $receive;
                }
                else
                {
                    $receive_ga=1 if grep /^$ga$/, @{$receive};
                }
            }
    
            next unless $receive_ga || $transmit_ga; # diese Logik nicht anwendbar
    
            $keep_subscription=1;
    
            # Sonderfall: Read- und Write-Telegramme auf der Transmit-Adresse?
            if($transmit_ga)
            {    
                # Ein Read-Request auf einer Transmit-GA wird mit dem letzten Ergebnis beantwortet
                # Read-Requests auf die receive-Adressen werden gar nicht beantwortet
                if($msg{apci} eq "A_GroupValue_Read")
                {  
                    my $result=$plugin_info{$plugname.'_'.$t.'_result'};
                    if(defined $result)
                    {
                        knx_write($ga, $result);                
                    }
                    next;
                }
                elsif(!$receive_ga) # Receive geht vor!
                {
                    if(defined $in) # Write-Telegramm: das waren moeglicherweise wir selbst, also nicht antworten
                    {
                        $plugin_info{$plugname.'_'.$t.'_result'}=$in; # einfach Input ablegen
                    }
                    else
                    {
                        delete $plugin_info{$plugname.'_'.$t.'_result'};
                    }
                    next;
                }
            }
    
            # Wir wissen ab hier: Es liegt ein Write-Telegramm auf einer der receive-Adressen vor
    
            # Nebenbei berechnen wir noch zwei Flags, die Zirkelkommunikation verhindern sollen
            # (Logik antwortet auf sich selbst in einer Endlosschleife)
    
            # kommt transmit-GA unter den receive-GAs vor? 
            # Wenn transmit_ga gesetzt ist, ist das schon mal der Fall
            my $possible_circle=$transmit_ga; 
    
            # war Wiregate der Sender des Telegramms?
            my $sender_is_wiregate=int($msg{src})==0; 
    
            # Es folgt die eigentliche Logik-Engine - als erstes definiere das Input-Array fuer die Logik
            my $input=$in; # Skalarer Fall (der haeufigste)
    
            # Array-Fall: bereite Input-Array fuer Logik vor
            if(ref $receive)
            {
                $input=();
                for my $rec (@{$receive})
                {
                    my $rec=groupaddress($rec);
                    
                    $possible_circle=1 if $transmit eq $rec;
                    
                    if($ga eq $rec)
                    {
                        push @{$input}, $in;
                    }
                    else
                    {
                        push @{$input}, knx_read($rec, 300);
                    }
                }
            }
    
            # ab hier liegt $input komplett vor, und nun muss die Logik ausgewertet 
            # und das Resultat auf der Transmit-GA uebertragen
            my $result=undef;
            
            unless(ref $logic{$t}{translate}) 
            {
                # Trivialer Fall: translate enthaelt einen fixen Rueckgabewert
                $plugin_info{$plugname.'_'.$t.'_result'}=$result=$logic{$t}{translate};
            }
            elsif(!ref $logic{$t}{state})
            {
                # Einfacher aber haeufiger Fall: skalarer $state
                # $state mit Ergebnis des letzten Aufrufs vorbesetzen
                my $state=$plugin_info{$plugname.'_'.$t.'_result'};
                
                # Funktionsaufruf, das Ergebnis vom letzten Mal steht in $state
                $result=$logic{$t}{translate}($state,$input);
                
                # Ergebnis des letzten Aufrufs zurueckschreiben
                if(defined $result)
                {
                    $plugin_info{$plugname.'_'.$t.'_result'}=$result;
                }
                else
                {
                    delete $plugin_info{$plugname.'_'.$t.'_result'};
                }
            }
            else
            {
                # Komplexer Fall: $state-Hash aus %logic initialisieren
                my $state=$logic{$t}{state};
                my @vars=keys %{$state};
                push @vars, 'result';
                
                # Nun die dynamischen Variablen aus plugin_info hinzufuegen
                for my $v (@vars)
                {
                    $state->{$v}=$plugin_info{$plugname.'_'.$t.'_'.$v} if defined $plugin_info{$plugname.'_'.$t.'_'.$v};
                }
                
                # Funktionsaufruf, das Ergebnis vom letzten Mal steht in $state->{result}
                $result=$state->{result}=$logic{$t}{translate}($state,$input);
                
                # Alle dynamischen Variablen wieder nach plugin_info schreiben
                # Damit plugin_info nicht durch Konfigurationsfehler vollgemuellt wird, 
                # erlauben wir nur Eintraege mit defined-Werten
                for my $v (@vars)
                {
                    if(defined $state->{$v})
                    {
                        $plugin_info{$plugname.'_'.$t.'_'.$v}=$state->{$v};
                    }
                    else
                    {
                        # wenn die Logik den Wert undef in eine state-Variable schreibt, 
                        # wird beim naechsten Aufruf wieder der Startwert aus %logic genommen,
                        delete $plugin_info{$plugname.'_'.$t.'_'.$v};
                    }
                }
            }
            
            # In bestimmten Sonderfaellen nichts schicken
            next unless defined $result; # Resultat undef => nichts senden
            next if $logic{$t}{transmit_only_on_request};
            next if $possible_circle && $sender_is_wiregate && $in eq $result;
            
            # Falls delay spezifiziert, wird ein "Wecker" gestellt, um in einem spaeteren Aufruf den Wert zu senden
            if($logic{$t}{delay})
            {
                $plugin_info{$plugname.'__'.$t.'_timer'}=time()+$logic{$t}{delay};
                set_timer();
            }
            else
            {
                knx_write($transmit, $result);
            }
        }
    
        unless($keep_subscription)
        {
            delete $plugin_subscribe{$ga}{$plugname};
        }
    }
    
    # Evtl. faellige Timer finden
    for my $timer (grep /$plugname\__.*_timer/, keys %plugin_info) # alle Timer
    {
        next if time()<$plugin_info{$timer}; # weiter falls noch nicht faellig
        
        # Timer loeschen
        delete $plugin_info{$timer}; 
        
        # Relevanten Eintrag von %logic ermitteln
        $timer=~/$plugname\__(.*)_timer/;
        my $t=$1; 
        
        # Transmit-GA
        my $transmit=groupaddress($logic{$t}{transmit});
        
        # zu sendendes Resultat
        my $result=$plugin_info{$plugname.'_'.$t.'_result'};
        next unless defined $result;
        
        knx_write($transmit, $result);
    }
    
    # Timer fuer nachste Aktion setzen
    set_timer();   
    
    return unless $retval;
    return $retval;
    
    
    # Zeit bis zum naechsten Aufruf dieses Plugins berechnen
    
    sub set_timer
    {
        # optimalen Wert fuer Aufrufzyklus finden, um beim naechsten Aufruf was zu senden
        my $nexttimer=undef;
        for my $timer (grep /$plugname\__.*_timer/, keys %plugin_info) # alle Timer
        {
            $nexttimer=$plugin_info{$timer} if !defined $nexttimer || $plugin_info{$timer}<$nexttimer;
        }
    
        unless(defined $nexttimer)
        {
            $plugin_info{$plugname."_cycle"}=0;
        }
        else
        {
            my $cycle=$nexttimer-time();
            $cycle=1 if $cycle<1;
            $plugin_info{$plugname."_cycle"}=$cycle;
        }
    }
    
    # Umgang mit GA-Kurznamen und -Adressen
    
    sub groupaddress
    {
        my $short=shift;
    
        return unless defined $short;
    
        if(ref $short)
        {
            my $ga=[];
            for my $sh (@{$short})
            {
                if($sh!~/^[0-9\/]+$/ && defined $eibgaconf{$sh}{ga})
                {
                    push @{$ga}, $eibgaconf{$sh}{ga};
                }
                else
                {
                    push @{$ga}, $sh;
                }
            }
            return $ga;
        }
        else
        {
            my $ga=$short;
    
            if($short!~/^[0-9\/]+$/ && defined $eibgaconf{$short}{ga})
            {
                $ga=$eibgaconf{$short}{ga};
            }
    
            return $ga;
        }
    }
    
    sub shortname
    {
        my $gas=shift;
    
        return unless defined $gas;
        return $gas unless $use_short_names;
    
        if(ref $gas)
        {
            my $sh=[];
            for my $ga (@{$gas})
            {
                if($ga=~/^[0-9\/]+$/ && defined $eibgaconf{$ga}{short})
                {
                    push @{$sh}, $eibgaconf{$ga}{short};
                }
                else
                {
                    push @{$sh}, $ga;
                }
            }
            return $sh;
        }
        else
        {
            my $sh=$gas;
    
            if($gas=~/^[0-9\/]+$/ && defined $eibgaconf{$gas}{short})
            {
                $sh=$eibgaconf{$gas}{short};
            }
    
            return $sh;
        }
    }

    Kommentar


      #3
      Hier eine Beispielkonfiguration:

      Code:
      #!/usr/bin/perl
      #
      # Logikprozessor.pl - Konfiguration
      #
      
      %logic=(
          # 1. Alle Werte, die auf einer GA gesendet werden, werden mit 2 multipliziert auf einer anderen GA weitergegeben
          mal2 => { receive=>'9/5/201', transmit=>'9/5/202', translate => sub { 2*$input; }, },
      
          # 2. Die Werte auf der ersten GA werden aufsummiert, das Ergebis auf der anderen GA gesendet.
          # Damit kann man bspw aus einem relativen Dimmwert einen absoluten Dimmwert machen. 
          sum => { receive=>'9/5/207', transmit=>'9/5/208', translate => sub { $state+$input; }, },
          # $state enthaelt das jeweils letzte Ergebnis.
      
          # 3. Wie oben, aber das Ergebnis limitiert auf den Bereich 0-100
          lsum => { receive=>'1/2/7', transmit=>'1/2/8', translate => sub { limit(0,$state+$input,100); }, },
       
          # 4. Hier eine "Treppenlichtfunktion". Auf jeden Schreibzugriff auf die receive-Adresse wird 10min spaeter eine 0 an 
          # die transmit-Adresse (hier gleich) geschickt. Verzoegert wird uebrigens nur das Senden, nicht das Ausfuehren der 
          # translate-Routine. Neu ist hier der "delay"-Parameter, ausserdem der Spezialfall, dass translate einfach eine Konstante als
          # Rueckgabewert spezifiziert.
          stair => { receive=>'1/2/9', transmit=>'1/2/9', delay=>600, translate => 0, },
          # Damit im Fall transmit != receive der Logikprozessor nicht auf sein eigenes Schreibtelegramm immer wieder antwortet, wird nur dann gesendet,
          # wenn Ergebnis != Input oder Sender des empfangenen Telegramms!=0 (Wiregate).
      
          # 5. Memory-Funktion. Wenn ein Write-Telegramm auf die Transmit-Adresse kommt, speichert der Logikprozessor den Wert ab.
          # Das Flag "transmit_on_request" bewirkt, dass nichts gesendet wird, jedoch wird eine Leseanfrage auf der transmit-GA immer
          # mit dem letzten Wert (hier also dem gespeicherten) beantwortet. Damit laesst sich eine Speicherfunktion realisieren. 
          # Hier speichert Logikprozessor also den Input ab und sendet den errechneten (=gespeicherten) Wert NUR AUF ANFRAGE.
          memory => { transmit=>'1/2/9', transmit_only_on_request=>1 },
          # Eine receive-Adresse oder translate-Logik werden hier gar nicht gebraucht.
      
          # 6. Eine einfache UND-Logik mit zwei Eingaengen. Falls ein Telegramm auf einer der beiden receive-GAs empfangen wird,
          # wird die andere Adresse noch ausgelesen, die Logik angewendet und das Ergebnis auf der transmit-GA uebermittelt
          und => { receive=>['1/2/12','1/2/13'], transmit=>'1/2/14', translate => sub { $input->[0] && $input->[1]; }, },
      
          # 7. Ein komplexerer Fall nur zur Demonstration: hier besteht der Status des Logikprozessors aus mehreren Werten. 
          # Es wird immer die Summe aus letztem, vorletztem und aktuellem Wert gesendet, und zwar mit 30s Verzoegerung.
          complex => { receive=>'9/5/205', 
                       transmit=>'9/5/206', 
                       delay=>30, 
                       state => {val1=>0, val2=>0}, 
                       translate => sub { $state->{val2}=$state->{val1}; $state->{val1}=$state->{result}; 
                                          $state->{val2}+$state->{val1}+$input; }, 
          },
          # Wenn state ein Hash ist, wird der letzte gesendete Wert in $state->{result} gespeichert.
      
          # 8. Schlussendlich wieder mal Werbung fuer die GA-Kurznamen. Setzt man im Skript Logikprozessor.pl $use_short_names=1
          # und verwendet GA-Namen mit eindeutigem Kuerzel (=erstes Wort des Namens), so funktioniert auch das folgende:
          D_SZ_Decke => { receive=>'LR_SZ_Decke_1', transmit=>'LK_SZ_Decke_1', 
                          translate => sub { limit(0,$state+20*$input,100); }, },
          # ist doch leserlicher, oder? Hier wird ein relativer Dimmwert durch Skalierung und Summierung 
          # in einen absoluten Wert umgewandelt   
          );

      Kommentar


        #4
        Der Logikprozessor.pl kommt nach /etc/wiregate/plugin/generic, und das Konfigurationsfile Logikprozessor.conf nach /etc/wiregate/plugin/generic/conf.d.

        Im SVN sind die Files natürlich auch, und falls ich noch Bugs korrigiere oder das Ding erweitere, wird das dort geschehen, nicht hier im Forum.

        VG, Fry

        Kommentar


          #5
          Eine Erweiterung der Logik-Engine ist gerade ins SVN eingecheckt worden, nämlich die Timer-Funktion.

          Als Konfigurationsbeispiel hier eine einfache Zeitschaltuhr, die immer um 8:00 und um 10:00 am jeweils zweiten Dienstag jedes Monats eine 1 auf die GA '10/1/15' sendet. Ist wieder nur ein Einzeiler für den Logikprozessor:

          Code:
          wecker => { transmit=>'10/1/15', timer=>{ time=>['08:00','10:00'], day_of_month=>[(8..14)], day_of_week=>'Di' }, translate => 1 },
          Logiken mit timer-Klausel weichen in mehreren Punkten von den bisherigen Logiken ab:

          * sie ignorieren die delay-Klausel und senden sofort (ausser transmit_only_on_request ist spezifiziert, siehe erstes Posting oben - dann wird der Wert zwar berechnet, aber erst auf explizite Nachfrage (per Lesetelegramm auf der transmit-Adresse) gesendet)

          * jeglicher Bustraffic auf receive-Adressen wird ignoriert. (Falls receive-Adressen spezifiziert sind, werden diese aber beim Timer-Aufruf abgefragt,
          um das input-Array vorzubesetzen).

          Als timer-Eintrag geht entweder ein einzelnes Hash timer=>{...} wie im Beispiel oben oder eine Liste solcher Eintraege: time=>[{...},{...},{...},...].

          Jeder Eintrag MUSS eine Spezifikation time=>'XX:XX' enthalten (auch das darf wieder eine Liste sein, siehe Beispiel) und DARF zusaetzliche, die Geltungstage einschraenkende Klauseln wie year, month, day_of_month,
          day_of_week, calendar_week enthalten.

          Das Ganze ist mit heißer Nadel gestrickt, hat aber erste Testungen bei mir überstanden.

          Über Erfahrungsberichte - auch Bug reports - von Leuten, die dieses Plugin ausprobieren, würde ich mich freuen.

          Have fun!
          Fry

          Kommentar


            #6
            Hallo,

            erstmal bekommst Du meinen vollen Respekt für deine Plugins. Diese machen das Wiregate für mich noch viel nützlicher und auch einfacher konfigurierbar.
            Habe vom Logikprozessor mal die Treppenlichtfunktion testen wollen, allerdings ohne Erfolg. Habe das Plugin sowie die Conf.d über den Webmin eingefügt und meine GA eingefügt. Ich wollte das nach dem Einschalten einer Beleuchtung und dem Ablauf der eingestellet Zeit die Beleuchtung wieder abschaltet. Allerdings funktioniert das nicht. Hat das sonst schon mal jemand getestet?

            Plusch

            Kommentar


              #7
              Auch von mir vollsten Respekt! Freu mich schon aufs Ausprobieren!
              Mit freundlichen Grüßen
              Niko Will

              Logiken und Schnittstelle zu anderen Systemen: smarthome.py - Visualisierung: smartVISU
              - Gira TS3 - iPhone & iPad - Mobotix T24 - ekey - Denon 2313 - Russound C5 (RIO over TCP Plugin) -

              Kommentar


                #8
                Zitat von Plusch Beitrag anzeigen
                Habe vom Logikprozessor mal die Treppenlichtfunktion testen wollen, allerdings ohne Erfolg. Habe das Plugin sowie die Conf.d über den Webmin eingefügt und meine GA eingefügt. Ich wollte das nach dem Einschalten einer Beleuchtung und dem Ablauf der eingestellet Zeit die Beleuchtung wieder abschaltet. Allerdings funktioniert das nicht. Hat das sonst schon mal jemand getestet?
                Hallo Plusch,

                danke fürs Feedback. Ja, "jemand" hat das getestet (ich :-). Gerade eben auf den Taster gedrückt - jawoll, Licht geht auch wieder aus.

                Bitte poste doch etwas mehr Information: dein Konfigfile, hast du das Plugin vom SVN (gut) oder per copy-paste aus dem Forum (schlecht) geholt, was steht im wiregate-Plugin-Log, was im eibd-Log?

                Beste Grüße,
                Fry

                Kommentar


                  #9
                  Zitat von 2ndsky Beitrag anzeigen
                  Auch von mir vollsten Respekt! Freu mich schon aufs Ausprobieren!
                  Danke! Freue mich über Feedback. Mit Sicherheit wird auch nicht alles auf Anhieb klappen, denn mein System hier ist mittlerweile doch etwas non-Standard (modifizierter Wiregate-Daemon). Aber das kriegen wir schon hin...

                  VG, Fry

                  Kommentar


                    #10
                    Hallo,

                    habe das Plugin sowie die Konfiguration habe ich aus dem SVN.

                    Das Plugin habe ich direkt aus dem SVN ohne Änderung kopiert.
                    Meine Konfiguration sieht so aus (habe nur die GA unter Beispiel 4 angepasst):
                    Code:
                    #!/usr/bin/perl
                    #
                    # Logikprozessor.pl - Konfiguration
                    #
                    
                    %logic=(
                        # 1. Alle Werte, die auf einer GA gesendet werden, werden mit 2 multipliziert auf einer anderen GA weitergegeben
                        mal2 => { receive=>'9/5/201', transmit=>'9/5/202', translate => sub { 2*$input; }, },
                        # das "undef" steht da einfach, weil uns der letzte Ergebniswert nicht interessiert
                    
                        # 2. Die Werte auf der ersten GA werden aufsummiert, das Ergebis auf der anderen GA gesendet.
                        # Damit kann man bspw aus einem relativen Dimmwert einen absoluten Dimmwert machen. 
                        sum => { receive=>'9/5/207', transmit=>'9/5/208', translate => sub { $state+$input; }, },
                        # $state enthaelt das jeweils letzte Ergebnis.
                    
                        # 3. Wie oben, aber das Ergebnis limitiert auf den Bereich 0-100
                        lsum => { receive=>'1/2/7', transmit=>'1/2/8', translate => sub { limit(0,$state+$input,100); }, },
                     
                        # 4. Hier eine "Treppenlichtfunktion". Auf jeden Schreibzugriff auf die receive-Adresse wird 10min spaeter eine 0 an 
                        # die transmit-Adresse (hier gleich) geschickt. Verzoegert wird uebrigens nur das Senden, nicht das Ausfuehren der 
                        # translate-Routine. Neu ist hier der "delay"-Parameter, ausserdem der Spezialfall, dass translate einfach eine Konstante als
                        # Rueckgabewert spezifiziert.
                        stair => { receive=>'2/2/0', transmit=>'2/2/0', delay=>5, translate => 0, },
                        # Verzoegert wird uebrigens nur das Senden, nicht das Ausfuehren der translate-Routine. 
                        # Neu ist hier der "delay"-Parameter, ausserdem der Spezialfall, dass translate einfach eine Konstante 
                        # als Rueckgabewert spezifiziert.
                    
                        # Weitere Bemerkungen:
                        # * translate darf nur entweder eine Konstante oder ausf�hrbarer Code (sub {...}) sein.
                        # * Damit im Fall transmit==receive der Translator nicht auf sein eigenes Schreibtelegramm immer wieder antwortet, 
                        # wird nur dann gesendet, wenn Ergebnis!=Input oder Sender des empfangenen Telegramms!=0 (Wiregate).
                        
                        # Damit im Fall transmit != receive der Logikprozessor nicht auf sein eigenes Schreibtelegramm immer wieder antwortet, 
                        # wird nur dann gesendet, wenn Ergebnis != Input oder Sender des empfangenen Telegramms!=0 (Wiregate).
                    
                        # 5. Hier eine Logik, die den Input bei Eintreffen mit 2 multipliziert, das Resultat aber nur speichert und erst 
                        # spaeter auf ein explizites Lesetelegramm hin auf der transmit-Adresse sendet. 
                        req => { receive=>'1/2/9', transmit=>'1/2/9', transmit_on_request=>1, translate => sub { 2*$input; }, },
                    
                        # 6. Eine Logik, die einem Lichtanschalten gleich einen Dimmwert hinterherfeuert, und zwar tags und nachts einen verschiedenen:
                        dim => { receive=>'2/2/9', transmit=>'2/3/9', translate => sub { return unless $input; $day ? 80 : 5; }, },
                        # Die Variablen $day_of_week (Mo...So), $day_of_month (01-31), $month (01-12), $year (2012),
                        # $weekend (0 oder 1), $weekday (= nicht $weekend), $time_of_day ("08:34:02"), 
                        # $hour ("08"), $day (1 falls zwischen 7 und 23 Uhr, 0 sonst) und $night (entsprechend umgekehrt) 
                        # sind f�r diese Logiken vorbesetzt (bitte nicht darauf schreiben, koennte unverhergesehene Auswirkungen 
                        # auf andere Logiken haben).
                    
                        # 7. Memory-Funktion. Fuer KNX-Ger�te, die kein Auslesen ihres Statuswerts zulassen (z.B. MDT DaliControl 
                        # bei Einzel-EVG-Ansteuerung). Sehr einfach:
                        memory => { transmit=>'1/2/9' },
                        # Hier wird folgende Eigenschaft der Logik ausgenutzt: Wenn ein Write-Telegramm auf die Transmit-Adresse kommt, 
                        # speichert der Logikprozessor den Wert immer automatisch ab. Eine Leseanfrage auf der transmit-GA hingegen wird 
                        # immer mit dem letzten Wert (hier also dem gespeicherten) beantwortet. Eine receive-Adresse oder translate-Logik 
                        # werden hier gar nicht gebraucht.
                    
                        # 8. Eine einfache UND-Logik mit zwei Eingaengen. Falls ein Telegramm auf einer der beiden receive-GAs empfangen wird,
                        # wird die andere Adresse noch ausgelesen, die Logik angewendet und das Ergebnis auf der transmit-GA uebermittelt
                        und => { receive=>['1/2/12','1/2/13'], transmit=>'1/2/14', translate => sub { $input->[0] && $input->[1]; }, },
                    
                        # 9. Ein komplexerer Fall nur zur Demonstration: hier besteht der Status des Logikprozessors aus mehreren Werten. 
                        # Es wird immer die Summe aus letztem, vorletztem und aktuellem Wert gesendet, und zwar mit 30s Verzoegerung.
                        complex => { receive=>'9/5/205', 
                                     transmit=>'9/5/206', 
                                     delay=>30, 
                                     state => {val1=>0, val2=>0}, 
                             translate => sub { $state->{val2}=$state->{val1}; $state->{val1}=$state->{result}; 
                                        $state->{val2}+$state->{val1}+$input; }, 
                        },
                        # Wenn state ein Hash ist, wird der letzte gesendete Wert in $state->{result} gespeichert.
                    
                        # 10. Eine Timer-Funktion. Hier eine einfache Zeitschaltuhr, die immer um 8 Uhr und um 10:00 am jeweils zweiten 
                        # Dienstag jedes Monats eine 1 auf Transmit sendet
                        wecker => { transmit=>'10/1/15', timer=>{ time=>['08:00','10:00'], day_of_month=>[(8..14)], day_of_week=>'Di' }, translate => 1 }, 
                        # Logiken mit timer-Klausel weichen in mehreren Punkten von den bisherigen Logiken ab:
                        # * sie ignorieren die delay-Klausel und senden sofort, aber transmit_only_on_request funktioniert
                        # * jeglicher Bustraffic auf receive-Adressen wird ignoriert. (Diese werden aber beim Timer-Aufruf abgefragt, 
                        # um das input-Array vorzubesetzen).
                        # Als timer-Eintrag geht entweder ein einzelnes Hash timer=>{...} wie oben oder eine Liste solcher Eintraege
                        # time=>[{...},{...},{...},...]. Jeder Eintrag MUSS eine Spezifikation time=>'XX:XX' enthalten (auch das darf wieder 
                        # eine Liste sein) und DARF zusaetzliche, die Geltungstage einschraenkende Klauseln wie year, month, day_of_month,
                        # day_of_week, calendar_week enthalten.
                    
                        # 11. Schlussendlich wieder mal Werbung fuer die GA-Kurznamen. Setzt man im Skript Logikprozessor.pl $use_short_names=1
                        # und verwendet GA-Namen mit eindeutigem Kuerzel (=erstes Wort des Namens), so funktioniert auch das folgende:
                        D_SZ_Decke => { receive=>'LR_SZ_Decke_1', transmit=>'LK_SZ_Decke_1', 
                                translate => sub { limit(0,$state+20*$input,100); }, },
                        # ist doch leserlicher, oder? Hier wird ein relativer Dimmwert durch Skalierung und Summierung 
                        # in einen absoluten Wert umgewandelt   
                        );
                    Im wiregate-Plugin-Log steht zu diesem Plugin überhaupt nichts !??

                    Plusch

                    Kommentar


                      #11
                      Dass im wiregate_plugin.log nichts steht, ist normal und deshalb ein gutes Zeichen. (Ich hatte zuerst sowas wie Debug-Infos im Plugin, die haben mich bei vielen Logiken aber sehr schnell ziemlich genervt... vielleicht muss ich das aber wieder einfügen).

                      Dann bitte ich dich, mal in den eibd-Log zu sehen. Wenn da das Wiregate nichts auf 2/2/0 sendet, vermute ich mal ganz banal, dass knx_write schlichtweg nicht arbeitet

                      -> Kann es sein, dass du die GA 2/2/0 nicht mit der korrekten DPTSubID (Datentyp) in deine /etc/wiregate/eibga.conf eingepflegt hast? Ohne DPT/DPTSubId kann knx_write nicht korrekt arbeiten, und der Logikprozessor kann den Datentyp nicht erraten. Vermutlich ist die korrekte DPT sowas wie 1 oder genauer 1.001 (Switch).

                      Ok?

                      Grüße,
                      Fry

                      Kommentar


                        #12
                        ...ich füge noch hinzu, dass auch knx_read (also das Lesen) ohne korrekt gepflegte /etc/wiregate/eibga.conf nicht einfach so arbeitet. Man kann eine DPT (Datentyp) explizit übergeben, im Logik-Plugin ist das allerdings (noch) nicht vorgesehen (wenn es gewünscht wird, kann ich das noch nachreichen - finde die Lösung einer sauber gepflegten eibga.conf allerdings vor. Ist einfach weniger fehleranfällig).
                        VG, Fry

                        Kommentar


                          #13
                          Zitat von Fry Beitrag anzeigen
                          -> Kann es sein, dass du die GA 2/2/0 nicht mit der korrekten DPTSubID (Datentyp) in deine /etc/wiregate/eibga.conf eingepflegt hast? Ohne DPT/DPTSubId kann knx_write nicht korrekt arbeiten, und der Logikprozessor kann den Datentyp nicht erraten. Vermutlich ist die korrekte DPT sowas wie 1 oder genauer 1.001 (Switch).
                          Ja, genau so ist es. Habe dort überhaupt keine GA eingepflegt. Ich wüsste auch auf Anhieb auch gar nicht wie ....
                          Kann das allerdings leider auch heute nicht mehr testen werde es aber morgen tun und dann berichten.
                          Danke schon mal vor ab für die Hilfe.

                          Gruß
                          Plusch

                          Kommentar


                            #14
                            Zitat von Plusch Beitrag anzeigen
                            Ja, genau so ist es. Habe dort überhaupt keine GA eingepflegt.
                            Es gibt dazu verschiedene Wege, u.a. eine Importfunktion der ETS-Daten auf der Weboberfläche. Da ich diesen Weg aber nicht nutze, kann ich dazu keine Hilfe geben.
                            VG, Fry

                            Kommentar


                              #15
                              Tolle Arbeit, ich verfolge das mit sehr großem Interesse, bitte weiter so.

                              Es wäre interessant, wo Du die Grenzen dieses Logikprozessors siehst?

                              glg

                              Stefan
                              Stefan Werner, Geschäftsführer Elaborated Networks GmbH.
                              Bitte keine PNs. Allg. Fragen ins Forum. Eilige od. wichtige an support ät wiregate.de
                              Alle Informationen und Aussagen nach bestem Wissen und Gewissen. IMPRESSUM

                              Kommentar

                              Lädt...
                              X