Ankündigung

Einklappen
Keine Ankündigung bisher.

Zählerabfrage als Wiregate Plugin

Einklappen
Dieses Thema ist geschlossen.
X
X
 
  • Filter
  • Zeit
  • Anzeigen
Alles löschen
neue Beiträge

  • NetFritz
    antwortet
    Hallo
    @lio123
    Du meinst, dass es ein Problem ist, dass beide Skripte laufen?
    Aber warum eigentlich? Die sind doch ziemlich unabhängig voneinander?!?
    Nein
    Ich meine das ein Script mehrmals läuft, das es nicht richtig beendet wurde.

    Ein
    "ps ax | grep .pl" zeigt Dir alle laufende Perl-Scripte an
    dann
    "kill -9 ProzessID" killt Dir garantiert das Script.

    Gruß NetFritz

    Einen Kommentar schreiben:


  • coolrunnings
    antwortet
    So, inzwischen zeichnen auch die rrds auf. Da war noch ein Fehler im Skript.
    Jetzt habe ich allerdings ein Problem die Daten richtig darzustellen (weiß nicht, ob das hierhin gehört oder in die CometVisu Abteilung).

    Im Anhang ist ein Screenshot von Verbrauch und Leistung der letzten 6h.

    Der rote Graph (Leistung) sieht auch gut aus: 6:40 Haare gefönt passt

    Der grüne Graph passt aber meiner Meinung nach nicht. Als Source nehme ich den counter mit 60 Minuten. Die zeitliche Zuordnung sieht aber komisch aus (1h versetzt) und die Werte sind dann doch etwas hoch.

    Die Funktion ist aus dem original SML Skript von jumi.

    Code:
    sub rrd_counter
    {
        if ($debug==1){print ("COUNTER","\n")};
        foreach (@countermodes)
        {
            my $obisname = $_[0];
            if ($debug==1){print $obisname." obisname \n";}
            my $value = $_[1];
            if ($debug==1){print $value." value \n";}
            my $rrdname = $obisname."_".$_."_c\.rrd";
            if ($debug==1){print ($rrdname,"\n")};
            my $rrdfile = $rrdpath."\/".$rrdname;
            unless (-e $rrdfile)
            {
                RRDs::create ($rrdfile,"DS:value:COUNTER:".(($_*60)+600).":0:10000000000",
    			"RRA:AVERAGE:0.5:1:366",	
    			"RRA:AVERAGE:0.5:7:1308",
    			"-s ".($_*60));				# step 
            }
            my $countervalue = int($value*$_*60);
    		if ($debug==1){print $countervalue." countervalue \n";}
            RRDs::update("$rrdfile", "N:$countervalue");
        }
    }
    Bei der Ausführung erhalte ich folgende Ausgabe (nicht passend zum Screenshot)

    Code:
    OBIS ID:     1.8.0
    OBIS search: 77070100010800FF
    found: 650000018201621E52FF590000000000723D5101
    check sml status:          [386]
    check sml value time:      [empty]
    check sml unit:            [30]
    check sml scaler:          [-1]
    check sml value:           [7486801]
    check sml value signature: [empty]
    Unit config: [Wh][cg]
    Final value: [748680.1 Wh]
    COUNTER
    zaehler_verbrauch obisname
    748680.1 value
    zaehler_verbrauch_5_c.rrd
    224604030 countervalue
    zaehler_verbrauch obisname
    748680.1 value
    zaehler_verbrauch_15_c.rrd
    673812090 countervalue
    zaehler_verbrauch obisname
    748680.1 value
    zaehler_verbrauch_60_c.rrd
    2695248360 countervalue
    zaehler_verbrauch obisname
    748680.1 value
    zaehler_verbrauch_1440_c.rrd
    64685960640 countervalue
    GAUGE
    zaehler_verbrauch obisname
    748680.1 value
    zaehler_verbrauch_g.rrd
    GA:9/0/0 value:748680.1 DPT:14
    Hat irgendjemand ne Idee, woran das liegen kann?
    Angehängte Dateien

    Einen Kommentar schreiben:


  • ctr
    antwortet
    Die meisten Linux-Dateisysteme sind case-sensitive
    D.h. "smlwp.pl" ist nicht das Gleiche wie "smlWP.pl"

    Im Screenshot heißt es smlWP.pl aber mit killall und manuellem Aufruf hatten Du smlwp.pl probiert (und die korrekte Fehlermeldung bekommen, dass es die Datei nicht gibt)

    Wenn Du es richtig schreibst, passierst sicher auch etwas...

    Einen Kommentar schreiben:


  • lio123
    antwortet
    Hallo Netfritz,

    Du meinst, dass es ein Problem ist, dass beide Skripte laufen?
    Aber warum eigentlich? Die sind doch ziemlich unabhängig voneinander?!?

    Für mich seltsam ist, dass
    1) bei Killall die Meldung kommt, dass kein Prozess beendet wurde.
    2) die absolute Pfadangabe nicht gefunden wird:
    Code:
    root@wiregate534:~# killall smlwp.pl
    smlwp.pl: Kein Prozess abgebrochen
    root@wiregate534:~# perl /home/user smlwp.pl
    root@wiregate534:~# perl /home/user
    root@wiregate534:~# perl /home/user/smlwp.pl
    Can't open perl script "/home/user/smlwp.pl": Datei oder Verzeichnis nicht gefunden
    root@wiregate534:~# perl /home/user/smlwp.pl
    Can't open perl script "/home/user/smlwp.pl": Datei oder Verzeichnis nicht gefunden
    Ich habe nun mal alle Rechte an das Skript gegeben, was aber auch nichts aus macht und das laufende Skript ja auch mit eingeschränkten Rechten läuft.


    Könnte es auch an Cron liegen? warum tauchen ausgerechnet ausschließlich bei diesen beiden jobs Pfeile auf?

    Aber selbst wenn ich das funktionierende Skript bei Cron deaktiviere kann ich das neue Skript (smlWP.pl) nicht starten

    Angehängte Dateien

    Einen Kommentar schreiben:


  • NetFritz
    antwortet
    Hallo
    Ich auch immer wieder Probleme mit dem Zähler-Script hatte, das mehre liefen.
    Jetzt starte ich mit Cron ein Bash-Schript, das überprüft ob das Script noch läuft, wenn ja wird es mit Kill beendet.
    Code:
       # sml_zaehler.pl starten
       if [ "$(pidof sml_zaehler.pl)" ]
          then
          echo "sml_zaehler.pl laeuft noch"
          pkill -9 sml_zaehler.pl
          echo "sml_zaehler.pl gekillt"
       fi
         /var/www/myhouse/cron/sml_zaehler.pl
    Gruß NetFritz

    Einen Kommentar schreiben:


  • lio123
    antwortet
    ach habe gerade gesehn, dass ich das Problem mit dem vorherigen skript auch mal im Post 331 hatte.

    also, ich hab nun auch Kill all smlWP.pl gemacht, da er mir sagte, dass das Plugin noch sonstwo läuft.
    Antwort war, dass da kein Prozess abgebrochen wurde.


    Danach habe ich Cronjob deaktiviert und versucht nochmals über Kommandozeile das Plugin zu starten.

    Danach sagt er mir, dass es noch woanderst läuft????

    DAnke und Grüße,
    Lio

    Einen Kommentar schreiben:


  • coolrunnings
    antwortet
    Zitat von lio123 Beitrag anzeigen
    Kann ich irgendwo in einem Log Laufzeitfehler oder dergleichen erkennen?
    Du könntest einfach mal das Skript direkt ausführen mit
    Code:
    perl /pfadzumskript/skriptname
    Dann müsstest du eine Ausgabe bekommen ob alles funktioniert hat oder ob Perl irgendwelche Fehler im Code gefunden hat.

    Einen Kommentar schreiben:


  • lio123
    antwortet
    Hallo,

    habe nun endlich mal das Jumi normalo Plugin für meinen Haushaltszähler nun endlich mal für den Wärmepumpen-Zähler mit 2 Tarifen aufgepimpt.

    Hilfestellungen gab mir Jumi damals schon.
    Wie kann ich prüfen, ob das Plugin läuft. Crontab habe ich analog eingerichtet. Ich sehe aber im Diagrammgenerator die neuen rrds nicht.
    Kann ich irgendwo in einem Log Laufzeitfehler oder dergleichen erkennen?

    Hier mal das Plugin:
    Code:
    #!/usr/bin/perl
    
    use warnings;
    use strict;
    use Device::SerialPort;
    use feature "switch";
    use EIBConnection;
    use RRDs;
    use Fcntl qw(:DEFAULT :flock);
    
    
    #0701000F0700FF = 7.0       FIXME !!! [0F]0700
    #070100010801FF = 1.8.1
    #070100010800FF = 1.8.1
    
    # Lock setzen damit Skript nur einmal startet
    
    
      open  *{0}
            or die "What!? $0:$!";
      flock *{0}, LOCK_EX|LOCK_NB
        or die "$0 is already running somewhere!\n";
    
    
    my $eib_url = "local:/tmp/eib";     #for local eibd "local:/tmp/eib" for eibd in LAN: "ip:192.168.2.220:6720"
    my $device = "/dev/usbserial-FTFUDSF1";
    my $repeat = 20;   # Wiederholung alle x Sekunden
    my $rrdpath = "/var/www/rrd";
    my $debug = 1;
    
    my @obis;
    
    
    push @obis,{obis=>"1.8.0",  fact=>10000, ga =>"14/7/53", dpt => 14, rrd_name => "WP_Zaehler_Verbrauch", rrd => "c"   }; #rrd: c=counter ; g=gauge
    push @obis,{obis=>"7.0",    fact=>10,    ga =>"14/7/52", dpt => 9 , rrd_name => "WP_Zaehler_Leistung_Ges",  rrd => "g" };
    push @obis,{obis=>"1.8.1",    fact=>10,    ga =>"14/7/54", dpt => 14 , rrd_name => "WP_Zaehler_Leistung_Tarif1",  rrd => "c" };
    push @obis,{obis=>"1.8.2",    fact=>10,    ga =>"14/8/55", dpt => 14 , rrd_name => "WP_Zaehler_Leistung_Tarif2",  rrd => "c" };
    
    my @countermodes = (5,15,60,1440);    #Aufloesungen fuer COUNTER RRDs in Minuten (1440 = Tagesverbrauch)
    
    
    my $port = Device::SerialPort->new($device) || die $!;
    
    $port->databits(8) || die "failed setting databits";
    $port->baudrate(9600) || die "failed setting baudrate";
    $port->parity("none") || die "failed setting parity";
    $port->stopbits(1);
    $port->handshake("none");
    $port->dtr_active(0);
    $port->purge_all();
    $port->read_char_time(0);     # don't wait for each character
    $port->read_const_time(4000); # 1 second per unfulfilled "read" call
    $port->write_settings || die "cannot write settings";
    
    
    # Loop zum Datenempfang
    
    my $error=0;
    
    while ($error != 1)
    {
    
       my ($x,$data) = 0 ;
       my $sml = 0;
       my $start = 0;
       my $value = 0;
       my $dec_value = 0;
       my $count = 0;
       my $saw = 0;
    
       if ($debug == 1) {print "Step 1 - Daten holen \n";}
    
       while ($start < 2)  # wait for second 1B1B1B1B01010101
         {
          ($count,$saw)=$port->read(512);   # will read 512 chars
          if ($count == 512)       # wurden 512 chars gelesen ?
            {
             $x = uc(unpack('H*',$saw)); # nach hex wandeln
             $data .= $x;
             if ($data =~ /1B1B1B1B01010101/)  {$start ++};
            } # if
         }  # while
    
       if ($debug==1) {print "Step 2 - Reg Exp 1 Datensatz zusammensetzen \n";}
       $data =~ m/1B1B1B1B01010101(.*?)B1B1B1/;
       $sml = $1;
    
       print "Zaehler Haushalt: \n";
    
       if ($debug==1) {print "Step 3 - Datensatz auswerten \n";}
    
                        
    
       foreach my $obiscnt (@obis)
        {
         # Umwandeln OBIS in HEx
    my $hc ="";
    foreach my $c (split(/\./,$obiscnt->{obis})) {
    $hc .= sprintf("%02X", $c);
    }
    my $obissearch = $hc;
         # FF fuer Suche an  Hex-Wert anhaegne
         $obissearch .="FF";
         if ($debug==1) { print $obissearch."     Obis\n";}
         $sml =~ m/$obissearch(.*?)0177/;
         #$sml =~ m/$obiscnt->{obis}(.*?)0177/;
         my $sml_val = $1;
    
                            
         if ($debug==1)  {  print $1." contains hex \n";}
    
         #extract value
         $sml_val =~ s/^.*52FF//;
         $sml_val = substr($sml_val,2);
         if ($debug == 1) {  print $sml_val." hex \n";}
         $value = $sml_val;
    
         $dec_value = sprintf("%d", hex($value));
         $dec_value /= $obiscnt->{fact};
         print $dec_value."<<<<---- Wert\n";
    
         #  RRD-Graphen erstellen  - bei Bedarf wieder aktiv setzen
         #if ($obis->{rrd} eq "c")
         #   {
         #   &rrd_counter ($obis->{rrd_name},$dec_value)
         #   }
         #if ($obis->{rrd} eq "g")
         #   {
         #   &rrd_gauge ($obis->{rrd_name},$dec_value)
         #   }
    
         &knx_write ($obiscnt->{ga},$dec_value,$obiscnt->{dpt});
         if ($debug == 1) {print "GA:".$obiscnt->{ga}." Wert:".$dec_value." DPT:".$obiscnt->{dpt}."\n";}
    
        } # foreach
    
        sleep $repeat;
    
    } # while
    
    $port->close() || warn "Serial port did not close proper!\n";
    undef $port;
    
    ### SUBS ###
    
    
    sub rrd_counter
    {
        if ($debug==1){print ("COUNTER","\n")};
        foreach (@countermodes)
        {
            my $obisname = $_[0];
            if ($debug==1){print $obisname." obisname \n";}
            my $value = $_[1];
            if ($debug==1){print $value." value \n";}
            my $rrdname = $obisname."_".$_."\.rrd";
            if ($debug==1){print ($rrdname,"\n")};
            my $rrdfile = $rrdpath."\/".$rrdname;
            unless (-e $rrdfile)
            {
                RRDs::create ($rrdfile,"DS:value:COUNTER:".(($_*60)+600).":0:10000000000","RRA:AVERAGE:0.5:1:365","RRA:AVERAGE:0.5:7:300","-s ".($_*60));
            }
            my $countervalue = int($value*$_*60);
            RRDs::update("$rrdfile", "N:$countervalue");
        }
    }
    
    sub rrd_gauge
    {
        if ($debug==1){print ("GAUGE","\n")};
        my $obisname = $_[0];
        if ($debug==1){print $obisname." obisname \n";}
        my $value = $_[1];
        if ($debug==1){print $value." value \n";}
        my $rrdname = $obisname."\.rrd";
        if ($debug==1){print ($rrdname,"\n")};
        my $rrdfile = $rrdpath."\/".$rrdname;
        unless (-e $rrdfile)
        {
            RRDs::create ($rrdfile,"DS:value:GAUGE:900:0:10000000000","RRA:AVERAGE:0.5:1:2160","RRA:AVERAGE:0.5:5:2016","RRA:AVERAGE:0.5:15:2880","RRA:AVERAGE:0.5:60:8760");
        }
        RRDs::update("$rrdfile", "N:$value");
    }
    
    
    sub knx_write {
        my ($dst,$value,$dpt,$response,$dbgmsg) = @_;
        my $bytes;
        my $apci = ($response) ? 0x40 : 0x80; # 0x40=response, 0x80=write
        #     DPT 1 (1 bit) = EIS 1/7 (move=DPT 1.8, step=DPT 1.7)
        #     DPT 2 (1 bit controlled) = EIS 8
        #     DPT 3 (3 bit controlled) = EIS 2
        #     DPT 4 (Character) = EIS 13
        #     DPT 5 (8 bit unsigned value) = EIS 6 (DPT 5.1) oder EIS 14.001 (DPT 5.10)
        #     DPT 6 (8 bit signed value) = EIS 14.000
        #     DPT 7 (2 byte unsigned value) = EIS 10.000
        #     DPT 8 (2 byte signed value) = EIS 10.001
        #     DPT 9 (2 byte float value) = EIS 5
        #     DPT 10 (Time) = EIS 3
        #     DPT 11 (Date) = EIS 4
        #     DPT 12 (4 byte unsigned value) = EIS 11.000
        #     DPT 13 (4 byte signed value) = EIS 11.001
        #     DPT 14 (4 byte float value) = EIS 9
        #     DPT 15 (Entrance access) = EIS 12
        #     DPT 16 (Character string) = EIS 15
        # $dpt = $eibgaconf{$dst}{'DPTSubId'} unless $dpt; # read dpt from eibgaconf if existing
        given ($dpt) {
            when (/^12/)             { $bytes = pack ("CCL>", 0, $apci, $value); }  #EIS11.000/DPT12 (4 byte unsigned)
            when (/^13/)             { $bytes = pack ("CCl>", 0, $apci, $value); }
            when (/^14/)             { $bytes = pack ("CCf>", 0, $apci, $value); }
            when (/^16/)             { $bytes = pack ("CCa14", 0, $apci, $value); }
            when (/^17/)             { $bytes = pack ("CCC", 0, $apci, $value & 0x3F); }
            when (/^20/)             { $bytes = pack ("CCC", 0, $apci, $value); }
            when (/^\d\d/)           { return; } # other DPT XX 15 are unhandled
            when (/^[1,2,3]/)        { $bytes = pack ("CC", 0, $apci | ($value & 0x3f)); } #send 6bit small
            when (/^4/)              { $bytes = pack ("CCc", 0, $apci, ord($value)); }
            when ([5,5.001])         { $bytes = pack ("CCC", 0, $apci, encode_dpt5($value)); } #EIS 6/DPT5.001 1byte
            when ([5.004,5.005,5.010]) { $bytes = pack ("CCC", 0, $apci, $value); }
            when (/^5/)              { $bytes = pack ("CCC", 0, $apci, $value); }
            when (/^6/)              { $bytes = pack ("CCc", 0, $apci, $value); }
            when (/^7/)              { $bytes = pack ("CCS>", 0, $apci, $value); }
            when (/^8/)              { $bytes = pack ("CCs>", 0, $apci, $value); }
            when (/^9/)              { $bytes = pack ("CCCC", 0, $apci, encode_dpt9($value)); } #EIS5/DPT9 2byte float
            default                  { LOGGER('WARN',"None or unsupported DPT: $dpt sent to $dst value $value"); return; }
        }
        my $leibcon = EIBConnection->EIBSocketURL($eib_url) or return("Error opening con: $!");
        if ($leibcon->EIBOpenT_Group(str2addr($dst),1) == -1) { return("Error opening group: $!"); }
        my $res=$leibcon->EIBSendAPDU($bytes);
        $leibcon->EIBClose();
        return $res;
        
        # str2addr: Convert an EIB address string in the form "1/2/3" or "1.2.3" to an integer
        sub str2addr {
            my $str = $_[0];
            if ($str =~ /(\d+)\/(\d+)\/(\d+)/) { # logical address
                return ($1 << 11) | ($2 << 8) | $3;
            } elsif ($str =~ /(\d+)\.(\d+)\.(\d+)/) { # physical address
                return ($1 << 12) | ($2 << 8) | $3;
            } else {
                #bad
                return;
            }
        }
        
    }
    
    sub encode_dpt9
    {
        # 2byte signed float
        my $state = shift;
        my $data;
        my $sign = ($state <0 ? 0x8000 : 0);
        my $exp  = 0;
        my $mant = 0;
        $mant = int($state * 100.0);
        while (abs($mant) > 2047)
        {
            $mant /= 2;
            $exp++;
        }
        $data = $sign | ($exp << 11) | ($mant & 0x07ff);
        return $data >> 8, $data & 0xff;
    }
    Danke und GRüße,
    Lio

    Einen Kommentar schreiben:


  • JuMi2006
    antwortet
    Das ganze ist kein Plugin im eigentlichen Sinne sondern sollte per cron aufgerufen werden, funktioniert so also auch auf jedem anderen Linux-System und ist nicht WireGate abhängig, eben wegen der Timing Probleme.
    Man kann das natürlich so umbauen dass es als Plugin läuft, macht m.M.n. aber wenige Sinn.


    Zu den RRDs, versuche erstmal die Gauge-RRDs anzuzeigen, das sollte klappen. Aber dazu muss eben mindestens alle 5 Minuten das script laufen -> crontab.
    Die Counter RRDs zeigen erst was nach 2x$countermodes Minuten an. Also die Tagesverbräuche sieht man erst nach 48 Stunden, Stundenverbrauch erst nach 2 Stunden usw..

    Einen Kommentar schreiben:


  • coolrunnings
    antwortet
    Ich dachte ich hätte mal was gelesen, dass es nicht "so geschickt" ist, wenn PlugIns "warten".

    Kannst du mir das mit der rrd Geschichte noch was erklären. Ich probier nämlich gerade die Geschichte in der Cometvisu anzuzeigen, aber es wird kein Graph angezeigt.

    Einen Kommentar schreiben:


  • JuMi2006
    antwortet
    Sieht doch sehr gut aus!

    1. Wozu socat?
    2. Wenn Du jetzt die Einheit (W/Wh) auswertest kann man auch auf gauge/counter in der config verzichten, siehe hier: https://sourceforge.net/p/openautoma...-meter.pl#l104

    Einen Kommentar schreiben:


  • coolrunnings
    antwortet
    So. Feierabend für heute.
    Ich habe das sml_meter Skript soweit angepasst, dass die Daten automatisch interpretiert werden.
    Bei mir getestet und für gut befunden
    Es wäre super, wenn das sonst wer mit nem SML eHz auch noch testen könnte.

    Was ich noch nicht getestet habe sind die rrd Dateien. Das habe ich aber 1 zu 1 vom Original so übernommen.

    Wenn ich in den nächsten Tagen noch Zeit habe, werde ich mich mal mit socat befassen...

    Code:
    use warnings;
    use strict;
    use Device::SerialPort;
    use feature "switch";
    use EIBConnection;
    use RRDs;
    use Switch;
    use Scalar::Util qw(looks_like_number);
    
    #####################################################################
    # define everything here
    my $eib_url = "local:/tmp/eib";     #for local eibd "local:/tmp/eib" for eibd in LAN: "ip:192.168.2.220:6720"
    my $device = "/dev/ttyUSB0";
    my $rrdpath = "/var/www/rrd";
    
    my @obis;
    push @obis,{obis=>"1.8.0",  fact=>"0.001", ga =>"9/0/0", dpt =>  9, rrd_name => "zaehler_verbrauch", rrd => "c" }; #rrd: c=counter ; g=gauge
    push @obis,{obis=>"16.7.0", fact=>"",      ga =>"9/0/1", dpt =>  9, rrd_name => "zaehler_leistung",  rrd => "g" };
    push @obis,{obis=>"36.7.0", fact=>"",      ga =>"9/0/2", dpt =>  9, rrd_name => "zaehler_leistung_L1",  rrd => "g" };
    push @obis,{obis=>"56.7.0", fact=>"",      ga =>"9/0/3", dpt =>  9, rrd_name => "zaehler_leistung_L2",  rrd => "g" };
    push @obis,{obis=>"76.7.0", fact=>"",      ga =>"9/0/4", dpt =>  9, rrd_name => "zaehler_leistung_L3",  rrd => "g" };
    my @countermodes = (5,15,60,1440);    #Aufloesungen fuer COUNTER RRDs in Minuten (1440 = Tagesverbrauch)
    
    my $debug = "1";
    my $port = Device::SerialPort->new($device) || die $!;
    $port->databits(8);
    $port->baudrate(9600);
    $port->parity("none");
    $port->stopbits(1);
    $port->handshake("none");
    $port->write_settings;
    $port->dtr_active(1);
    $port->purge_all();
    $port->read_char_time(0);     # don't wait for each character
    $port->read_const_time(2000); # 1 second per unfulfilled "read" call
    
    #####################################################################
    # app starting here
    
    my ($x,$rawdata) = 0 ;
    my $count = 0;
    my $saw = 0;
    my $start = 0;
    
    if ($debug == 1) {print "Step 1 - Collect data \n";}
    
    while ($start < 2) {	# wait for second 1B1B1B1B01010101
       
    	($count,$saw)=$port->read(512);  # will read 512 chars
    	if ($count == 512) {       		# 512 chars read?
    		$x = uc(unpack('H*',$saw)); # nach hex wandeln
    		$rawdata .= $x;
    		if ($rawdata =~ /1B1B1B1B01010101/)  {$start ++};
    	} # if
    }  # while
    
    if ($debug==1) {print "Step 2 - Reg Exp 1 build dataset \n";}
    
    $rawdata =~ m/1B1B1B1B01010101(.*?)B1B1B1/;
    #$ra = $1;
    #print $sml."\n";
    
    if ($debug==1) {print "Step 3 - Analyze data \n";}
    
    # find OBIS values in raw data
    foreach my $obiscnt (@obis) {
    	if ( $debug == 1 ) {print "\n";}
    	my $obissearch = &obis2search($obiscnt->{obis});
    	
    	$rawdata =~ m/$obissearch(.*?)017707/;
    	my $obisdata = $1."01";
    	
    	print "found: ".$obisdata."\n";
    	
    	my $smlStatus  = "";
    	my $smlValTime = "";
    	my $smlUnit    = "";
    	my $smlScaler  = "";
    	my $smlValue   = "";
    	my $smlValueS  = "";
    	
    	## check status
    	if ( $debug == 1 ) {print "check sml status:          ";}
    	$smlStatus = &parseOBIS(\$obisdata);
    	
    	## check value time
    	if ( $debug == 1 ) {print "check sml value time:      ";}
    	$smlValTime = &parseOBIS(\$obisdata);
    	
    	## check unit
    	if ( $debug == 1 ) {print "check sml unit:            ";}
    	$smlUnit = &parseOBIS(\$obisdata);
    	
    	## check scaler
    	if ( $debug == 1 ) {print "check sml scaler:          ";}
    	$smlScaler = &parseOBIS(\$obisdata);
    	
    	## check value
    	if ( $debug == 1 ) {print "check sml value:           ";}
    	$smlValue = &parseOBIS(\$obisdata);
    	
    	## check value signature
    	if ( $debug == 1 ) {print "check sml value signature: ";}
    	$smlValueS = &parseOBIS(\$obisdata);
    
    	#print "done: ".$obisdata."\n";
    	#print  $smlStatus;
    	
    	
    	
    	# Calculate value
    	if ( $smlValue ne "" ) {
    		my $calcvalue  = ($smlValue * (10**$smlScaler));
    		my $smlUnitStr = &getSMLUnitStr($smlUnit);
    		if ( $debug == 1 ) {print "Calculated value: [".$calcvalue." ".$smlUnitStr."]\n";}
    		
    		my $dec_value = $calcvalue;
    		
    		# scale value
    		if ( looks_like_number($obiscnt->{fact}) ) {
    			$dec_value = ($calcvalue * $obiscnt->{fact});
    			if ( $debug == 1 ) {print "Final value: [".$dec_value."]\n";}
    		}
    		
    		# write rrd
    		if ($obiscnt->{rrd} eq "c") {
    			&rrd_counter ($obiscnt->{rrd_name},$dec_value)
    		}
    		
    		if ($obiscnt->{rrd} eq "g") {
    			&rrd_gauge ($obiscnt->{rrd_name},$dec_value)
    		}
    
    		# send value to bus
    		&knx_write ($obiscnt->{ga},$dec_value,$obiscnt->{dpt});
    		if ($debug == 1) {print "GA:".$obiscnt->{ga}." value:".$dec_value." DPT:".$obiscnt->{dpt}."\n";}
    	}	
    }
    
    $port->close() || warn "Serial port did not close proper!\n";
    undef $port;
    
    ## subs ##
    
    #####################################################################
    # convert OBIS to search parameter
    sub obis2search {
    
    	my ($obisid) = @_;
    	my $res = "77070100";
    	
    	if ($debug==1) { print "OBIS ID:     ".$obisid."\n";}
    		
    	foreach my $c ( split(/\./,$obisid) ) {
    		$res .= sprintf("%02X",$c);
    	}
    	
    	$res .= "FF";
    	
    	if ($debug==1) { print "OBIS search: ".$res."\n";}
    	
    	return $res;
    }
    
    #####################################################################
    # parse OBIS data
    sub parseOBIS {
    	#my ($obisdata) = @_;
    	my $r_obisdata = shift;
    	my $res = "";
    	
    	if ( length($$r_obisdata) < 2 ) {
    		print "error --> data too short\n";
    		return $res;
    	}
    	
    	my $smlDataTypeID = substr($$r_obisdata,0,2); 
    	
    	if ( $smlDataTypeID == "01" ) {
    		## no data
    		if ( $debug == 1 ) {print "[empty]\n";}
    		$$r_obisdata =~ s/^..//;	# remove first 2 characters
    		return $res;
    	}
    	else {
    		#if ( $debug == 1 ) {print "found something ";}
    		
    		my $smlDataType    = substr($smlDataTypeID,0,1);
    		my $smlIntBitCode = substr($smlDataTypeID,1,1);
    		
    		# only signed and unsigned int possible
    		if ( $smlDataType != "5" && $smlDataType != "6") {
    			if ( $debug == 1 ) {print "Invalid datatype [".$smlDataType."]\n"; };
    			return $res;
    		}
    		
    		$$r_obisdata =~ s/^..//;	# remove first 2 characters
    		
    		
    		my $smlIntBitCnt = 0;
    		
    		switch ( $smlIntBitCode ) {
    			case 2	{ $smlIntBitCnt =  8 }
    			case 3	{ $smlIntBitCnt = 16 }
    			case 5	{ $smlIntBitCnt = 32 }
    			case 9	{ $smlIntBitCnt = 64 }
    			else  	{ if ( $debug == 1 ) {print "Invalid integer bit count [".$smlIntBitCode."]\n"; };
    					  return $res; 
    					}
    		}
    		
    		my $smlIntCharCnt = ($smlIntBitCnt/4);
    		
    		# check that the rest of the string is long enought for the bit count
    		if ( (length($$r_obisdata)) < $smlIntCharCnt ) {
    			if ( $debug == 1 ) {print "String not long enough for for detected bit count [".$smlIntCharCnt."]\n"; };
    			return $res;
    		}
    		
    		# get the hex values
    		my $hexval = substr($$r_obisdata,0,$smlIntCharCnt);
    		$$r_obisdata = substr($$r_obisdata,$smlIntCharCnt);
    		
    		# if signed, get 2' complement
    		if ( $smlDataType == 5 ) {
    			my $hexbin = sprintf("%0${smlIntBitCnt}b",hex($hexval));
    		
    			if ( substr($hexbin,0,1) == 1 ) {
    				#if ( $debug == 1 ) { print "2's complement \n"; }
    				my $hexinv = sprintf("%08x",~hex($hexval));
    				$res = (substr($hexinv,6,2)+1)*(-1);
    			
    			}
    			else {
    				$res = hex($hexval);
    			}
    		}
    		else {
    			$res = hex($hexval);
    		}
    		
    		if ( $debug == 1 ) { print "[".$res."]\n";}
    		
    		return $res;
    	}
    	
    	return $res;
    }
    
    #####################################################################
    # get SML unit string depending on value
    sub getSMLUnitStr {
    
    	my ($smlUnit) = @_;
    	my $res = "";
    	
    	switch ($smlUnit) {	
    		case 30 { $res = "Wh" }
    		case 27 { $res = "W"  }
    	}
    	
    	return $res;
    }
    
    #####################################################################
    
    sub knx_write {
        my ($dst,$value,$dpt,$response,$dbgmsg) = @_;
        my $bytes;
        my $apci = ($response) ? 0x40 : 0x80; # 0x40=response, 0x80=write
        #     DPT 1 (1 bit) = EIS 1/7 (move=DPT 1.8, step=DPT 1.7)
        #     DPT 2 (1 bit controlled) = EIS 8
        #     DPT 3 (3 bit controlled) = EIS 2
        #     DPT 4 (Character) = EIS 13
        #     DPT 5 (8 bit unsigned value) = EIS 6 (DPT 5.1) oder EIS 14.001 (DPT 5.10)
        #     DPT 6 (8 bit signed value) = EIS 14.000
        #     DPT 7 (2 byte unsigned value) = EIS 10.000
        #     DPT 8 (2 byte signed value) = EIS 10.001
        #     DPT 9 (2 byte float value) = EIS 5
        #     DPT 10 (Time) = EIS 3
        #     DPT 11 (Date) = EIS 4
        #     DPT 12 (4 byte unsigned value) = EIS 11.000
        #     DPT 13 (4 byte signed value) = EIS 11.001
        #     DPT 14 (4 byte float value) = EIS 9
        #     DPT 15 (Entrance access) = EIS 12
        #     DPT 16 (Character string) = EIS 15
        # $dpt = $eibgaconf{$dst}{'DPTSubId'} unless $dpt; # read dpt from eibgaconf if existing
        given ($dpt) {
            when (/^12/)             { $bytes = pack ("CCL>", 0, $apci, $value); }  #EIS11.000/DPT12 (4 byte unsigned)
            when (/^13/)             { $bytes = pack ("CCl>", 0, $apci, $value); }
            when (/^14/)             { $bytes = pack ("CCf>", 0, $apci, $value); }
            when (/^16/)             { $bytes = pack ("CCa14", 0, $apci, $value); }
            when (/^17/)             { $bytes = pack ("CCC", 0, $apci, $value & 0x3F); }
            when (/^20/)             { $bytes = pack ("CCC", 0, $apci, $value); }
            when (/^\d\d/)           { return; } # other DPT XX 15 are unhandled
            when (/^[1,2,3]/)        { $bytes = pack ("CC", 0, $apci | ($value & 0x3f)); } #send 6bit small
            when (/^4/)              { $bytes = pack ("CCc", 0, $apci, ord($value)); }
            when ([5,5.001])         { $bytes = pack ("CCC", 0, $apci, encode_dpt5($value)); } #EIS 6/DPT5.001 1byte
            when ([5.004,5.005,5.010]) { $bytes = pack ("CCC", 0, $apci, $value); }
            when (/^5/)              { $bytes = pack ("CCC", 0, $apci, $value); }
            when (/^6/)              { $bytes = pack ("CCc", 0, $apci, $value); }
            when (/^7/)              { $bytes = pack ("CCS>", 0, $apci, $value); }
            when (/^8/)              { $bytes = pack ("CCs>", 0, $apci, $value); }
            when (/^9/)              { $bytes = pack ("CCCC", 0, $apci, encode_dpt9($value)); } #EIS5/DPT9 2byte float
            default                  { LOGGER('WARN',"None or unsupported DPT: $dpt sent to $dst value $value"); return; }
        }
        my $leibcon = EIBConnection->EIBSocketURL($eib_url) or return("Error opening con: $!");
        if ($leibcon->EIBOpenT_Group(str2addr($dst),1) == -1) { return("Error opening group: $!"); }
        my $res=$leibcon->EIBSendAPDU($bytes);
        $leibcon->EIBClose();
        return $res;
        
        # str2addr: Convert an EIB address string in the form "1/2/3" or "1.2.3" to an integer
        sub str2addr {
            my $str = $_[0];
            if ($str =~ /(\d+)\/(\d+)\/(\d+)/) { # logical address
                return ($1 << 11) | ($2 << 8) | $3;
            } elsif ($str =~ /(\d+)\.(\d+)\.(\d+)/) { # physical address
                return ($1 << 12) | ($2 << 8) | $3;
            } else {
                #bad
                return;
            }
        }
        
    }
    
    #####################################################################
    
    sub encode_dpt9
    {
        # 2byte signed float
        my $state = shift;
        my $data;
        my $sign = ($state <0 ? 0x8000 : 0);
        my $exp  = 0;
        my $mant = 0;
        $mant = int($state * 100.0);
        while (abs($mant) > 2047)
        {
            $mant /= 2;
            $exp++;
        }
        $data = $sign | ($exp << 11) | ($mant & 0x07ff);
        return $data >> 8, $data & 0xff;
    }
    
    #####################################################################
    
    sub rrd_counter
    {
        if ($debug==1){print ("COUNTER","\n")};
        foreach (@countermodes)
        {
            my $obisname = $_[0];
            if ($debug==1){print $obisname." obisname \n";}
            my $value = $_[1];
            if ($debug==1){print $value." value \n";}
            my $rrdname = $obisname."_".$_."\.rrd";
            if ($debug==1){print ($rrdname,"\n")};
            my $rrdfile = $rrdpath."\/".$rrdname;
            unless (-e $rrdfile)
            {
                RRDs::create ($rrdfile,"DS:value:COUNTER:".(($_*60)+600).":0:10000000000","RRA:AVERAGE:0.5:1:365","RRA:AVERAGE:0.5:7:300","-s ".($_*60));
            }
            my $countervalue = int($value*$_*60);
            RRDs::update("$rrdfile", "N:$countervalue");
        }
    }
    
    #####################################################################
    
    sub rrd_gauge
    {
        if ($debug==1){print ("GAUGE","\n")};
        my $obisname = $_[0];
        if ($debug==1){print $obisname." obisname \n";}
        my $value = $_[1];
        if ($debug==1){print $value." value \n";}
        my $rrdname = $obisname."\.rrd";
        if ($debug==1){print ($rrdname,"\n")};
        my $rrdfile = $rrdpath."\/".$rrdname;
        unless (-e $rrdfile)
        {
            RRDs::create ($rrdfile,"DS:value:GAUGE:900:0:10000000000","RRA:AVERAGE:0.5:1:2160","RRA:AVERAGE:0.5:5:2016","RRA:AVERAGE:0.5:15:2880","RRA:AVERAGE:0.5:60:8760");
        }
        RRDs::update("$rrdfile", "N:$value");
    }

    Einen Kommentar schreiben:


  • JuMi2006
    antwortet
    Genau das wollte ich am Ende damit errreichen!

    Ich hab ja selbst keinen SML-Zähler, daher ist es besser wenn es jemand macht der das vor Ort testen kann.
    Bitte auch das Ergebnis posten oder am besten gleich im SVN updaten.

    Einen Kommentar schreiben:


  • coolrunnings
    antwortet
    Ich bin gerade dabei, dass Skript so anzupassen, dass die OBIS Daten automatisch geparst werden.
    Dafür habe ich mir mal die SML Definition angeschaut (VDE)
    Die Values kann ich schon umrechnen.
    Dann kann man sich auch den Faktor sparen.

    Die Aufteilung eines OBIS Strings ist folgendermaßen:

    Code:
    LSOBIS      UNIT/SCALER/?     VALUE        TRENNER
    77                                  List start mit 7 nodes
      07 01 00 01 08 01 FF    OBIS ID 1-0:1.8.1
      01                          status (leer)
      01                          valtime (leer)
      62 1E                      unit (1E => Wh)
      52 FF                      scaler (-1)
      59 00 00 00 00 00 6E B7 D6  value (7256022 => 725,6022 kWh)
      01
    77 ....
    010802FF 0101621E52FF59 0000000000000000 0177
    100700FF 0101621B520055 000000EE         0177
    240700FF 0101621B520055 00000030         0177
    380700FF 0101621B520055 0000007A         0177
    4C0700FF 0101621B520055 00000043         0177
    Gruß

    Einen Kommentar schreiben:


  • JuMi2006
    antwortet
    Ich will eigentlich noch diese Zeile sparen:

    $sml_val = substr($sml_val,2);

    dann hätten wir folgendes:

    Code:
    [B]OBIS[/B]      [B]UNIT/SCALER/?[/B]     [B]VALUE[/B]        [B]TRENNER[/B]
    010801FF 0101621E52FF59 00000000006EB7D6 0177
    010802FF 0101621E52FF59 0000000000000000 0177
    100700FF 0101621B520055 000000EE         0177
    240700FF 0101621B520055 00000030         0177
    380700FF 0101621B520055 0000007A         0177
    4C0700FF 0101621B520055 00000043         0177

    Einen Kommentar schreiben:

Lädt...
X