Modul für DWD Open Data

Begonnen von jensb, 21 Januar 2018, 14:38:48

Vorheriges Thema - Nächstes Thema

mumpitzstuff

#975
Probier das Script einfach mal aus und schau es dir dann an. Kann man ja in den Hintergrund schicken und per Top beobachten...
Das zeilenweise holen der Daten aus dem Zip ist halt seeeeehhrrrr langsam irgendwie. Leider.

jensb

Danke, das schaue ich mir auf jeden Fall an. Wenn es sich bestätigt kommt es in die übernächste Version.
FHEM 6.1 - RPi 4 Raspbian 12 + PiTFT - OPi Zero Armbian 5.35
EnOcean - (W)LAN/Firmata: BMP180, TSL2561, SHT21, Heatronic 3, OBIS - WLAN/ESP8266: Gardena 1251, Zirkulationspumpe - RTL433: Oregon - Bluetooth - MQTT
Contributions: https://svn.fhem.de/trac/browser/trunk/fhem/contrib/jensb

jensb

Zitat von: Prof. Dr. Peter Henning am 03 März 2024, 14:42:54Ich fände es interessant, die DWD-Daten tatsächlich raumbezogen darzustellen.

Das wäre eine schöne Erweiterung für das User-Interface, vielleicht sogar mit Zeitraffer-Player.

Bei der Darstellung kann ich nicht helfen, aber was die Daten dazu betrifft:

  • Die Alerts werden schon jetzt warnzellenübergreifend im RAM gecached und der Cache könnte auch aus anderen Modulen abgerufen werden.
  • Bei den stündlichen Forecasts bräuchte man aber auf jeden Fall ausreichend RAM, wenn man es genauso machen will. Oder man nimmt doch eine Datei als Cache? Oder alternativ?

Grüße,
Jens
FHEM 6.1 - RPi 4 Raspbian 12 + PiTFT - OPi Zero Armbian 5.35
EnOcean - (W)LAN/Firmata: BMP180, TSL2561, SHT21, Heatronic 3, OBIS - WLAN/ESP8266: Gardena 1251, Zirkulationspumpe - RTL433: Oregon - Bluetooth - MQTT
Contributions: https://svn.fhem.de/trac/browser/trunk/fhem/contrib/jensb

jensb

Es gibt eine weitere Alpha-Version 1.17.3 des DWD_OpenData-Moduls in meinem Contrib-Verzeichnis:

Wesentliche Änderungen im Vergleich zu 1.17.2:

  • Große Vorhersage-Dateien werden vor der Verarbeitung statt ins RAM in das TEMP-Verzeichnis dekomprimiert und vorgefiltert statt vollständig eingelesen, um den RAM-Recourcenbedarf zu verringern, so dass nun 2 GB RAM und 2 Cores ausreichen sollten.
  • Maximalwert für Attribut downloadTimeout auf 120 s erhöht.
  • Abhängigkeit vom Perl-Modul LWP::UserAgent durch Aufruf von HttpUtils_BlockingGet ersetzt (Beitrag von DS_Starter).

Hinweise:
  • Diese Version ist nur eine Zwischenschritt und daher eher etwas für Experimentierfreudige.
  • Anwender, deren TEMP-Verzeichnis sich auf einer SD-Karte ohne Overprovisioning befindet, sollten prüfen, ob eine Umstellung des TEMP-Verzeichnisses auf tmpfs möglich ist, damit die Lebensdauer der SD-Karte nicht reduziert wird (siehe weitere Erläuterungen).
  • Die Aufgaben Download/Entpacken/Filtern der Vorhersagedaten sind im Modulcode modularisiert worden, Es gibt aktuell die Implementierungen GetForecastDataDiskless und GetForecastDataUsingFile. Wer RAM-Verbrauch und Verabeitungszeit mittunen will, kann darauf aufbauend eine weitere Variante vorschlagen.
FHEM 6.1 - RPi 4 Raspbian 12 + PiTFT - OPi Zero Armbian 5.35
EnOcean - (W)LAN/Firmata: BMP180, TSL2561, SHT21, Heatronic 3, OBIS - WLAN/ESP8266: Gardena 1251, Zirkulationspumpe - RTL433: Oregon - Bluetooth - MQTT
Contributions: https://svn.fhem.de/trac/browser/trunk/fhem/contrib/jensb

DS_Starter

Hallo Jens,

habe die Version in den Test genommen und berichte...

Grüße,
Heiko
ESXi@NUC+Debian+MariaDB, PV: SMA, Victron MPII+Pylontech+CerboGX
Maintainer: SSCam, SSChatBot, SSCal, SSFile, DbLog/DbRep, Log2Syslog, SolarForecast,Watches, Dashboard, PylonLowVoltage
Kaffeekasse: https://www.paypal.me/HMaaz
Contrib: https://svn.fhem.de/trac/browser/trunk/fhem/contrib/DS_Starter

mumpitzstuff

use strict;
use warnings;
use IO::Uncompress::Unzip qw(unzip $UnzipError);
use Time::HiRes qw(gettimeofday);


# dieser Teil würde durch den Download erledigt werden
open my $fh_zip, '<:raw', 'MOSMIX_S_LATEST_240.kmz' or die "Could not open file for reading: $!";
my $fileContent = do { local $/; <$fh_zip> };
close $fh_zip;

my $t0 = gettimeofday();

my $z = new IO::Uncompress::Unzip(\$fileContent) or die "unzip failed: $UnzipError\n";

my $buffer;
my $offset = 0;
my $startOfLine = 0;
my $endOfLine = 0;
my $line;
my $collect = 0;
my $collectString = '';
# split into chunks of 1MB
READ_CHUNKS:
while ($z->read($buffer, 1000000, $offset) > 0)
{
    $endOfLine = index($buffer, "\n");
   
    while ($endOfLine != -1)
    {
        $line = substr($buffer, $startOfLine, $endOfLine - $startOfLine + 1);
        $startOfLine = $endOfLine + 1;
        $endOfLine = index($buffer, "\n", $endOfLine + 1);
       
        if (index($line, '<dwd:Issuer>') != -1)
        {   
            print $line;
        }
        elsif (index($line, '<dwd:IssueTime>') != -1)
        {
            print $line;
        }
        elsif (index($line, '<dwd:ForecastTimeSteps>') != -1)
        {
           $collect = 1;
        }
        elsif (index($line, '</dwd:ForecastTimeSteps>') != -1)
        {
           $collect = 0;
           print $collectString.$line;
           $collectString = '';
        }
        elsif (index($line, '<dwd:FormatCfg>') != -1)
        {
           $collect = 1;
        }
        elsif (index($line, '</dwd:FormatCfg>') != -1)
        {
           $collect = 0;
           print $collectString.$line;
           $collectString = '';
        }
        elsif (index($line, '<kml:Placemark>') != -1)
        {
           #print 'Collection started: '.$line;
           $collect = 1;
        }
        elsif (($collect == 1) && (index($line, '</kml:Placemark>') != -1))
        {
           $collect = 0;
           print $collectString.$line;
           last READ_CHUNKS;
        }
        elsif (index($line, '<kml:name>') != -1)
        {
            # Beispielstation (relativ weit hinten)
            if (index($line, '<kml:name>06770</kml:name>') == -1)
            {
                print 'Filtered: '.$line;
                $collect = 0;
                $collectString = '';
            }
            else
            {
                print 'Station gefunden!'."\r\n";
            }
        }   
     
        if ($collect == 1)
        {
            $collectString .= $line;
        }
    }

    # find end of last line within chunk
    my $end = rindex($buffer, "\n");
    # copy the last incomplete line to the buffer of the next chunk
    if (($end > 0) && ($end < length($buffer) - 1))
    {
        $buffer = substr($buffer, $end + 1);
        $offset = length($buffer);
    }

    $startOfLine = 0;
}

$z->close();

print "benchmark1: ".(gettimeofday() - $t0)."\n\n";

Hier noch mal eine etwas komplexere Variante der ersten Version, die aber rund doppelt so schnell ist auf meinem Desktop Rechner, dafür aber einen Buffer von 1MB benötigt (was sicherlich lächerlich wenig ist).

jensb

@mumpitzstuff

Bei diesem eindeutigen Ergebnis braucht man das nicht mehr zu testen, dann geht es mehr darum aus der sub GetForecastDataUsingFile von Version 1.17.3 eine neue sub GetForecastDataDisklessFiltered zu machen. Der größte Teil des aktuellen Codes kann meiner Ansicht nach bleiben. Wahrscheinlich reicht es fast das Filehandle von unzip mit getline in der vorhandenen readline-while-Schleife zu verwenden. Werde mir das wahrscheinlich nächstes Wochenende vornehmen.

Grüße,
Jens
FHEM 6.1 - RPi 4 Raspbian 12 + PiTFT - OPi Zero Armbian 5.35
EnOcean - (W)LAN/Firmata: BMP180, TSL2561, SHT21, Heatronic 3, OBIS - WLAN/ESP8266: Gardena 1251, Zirkulationspumpe - RTL433: Oregon - Bluetooth - MQTT
Contributions: https://svn.fhem.de/trac/browser/trunk/fhem/contrib/jensb

DS_Starter

Hinweis zu 1.17.3:

PERL WARNING: "my" variable $station masks earlier declaration in same scope at ./FHEM/55_DWD_OpenData.pm line 1920.

LG
ESXi@NUC+Debian+MariaDB, PV: SMA, Victron MPII+Pylontech+CerboGX
Maintainer: SSCam, SSChatBot, SSCal, SSFile, DbLog/DbRep, Log2Syslog, SolarForecast,Watches, Dashboard, PylonLowVoltage
Kaffeekasse: https://www.paypal.me/HMaaz
Contrib: https://svn.fhem.de/trac/browser/trunk/fhem/contrib/DS_Starter

SparcWolf

Zitat von: mumpitzstuff am 03 März 2024, 20:51:31...
Hier noch mal eine etwas komplexere Variante der ersten Version, die aber rund doppelt so schnell ist auf meinem Desktop Rechner, dafür aber einen Buffer von 1MB benötigt (was sicherlich lächerlich wenig ist).

Der RPi 3 B+ (1GB RAM) braucht für das Skript ca. 24 Sekunden.

mumpitzstuff

Das klingt ja jetzt erst mal nicht so schlecht wenn man bedenkt, das dort ein 33mb zip File zu einem 670mb Textfile entpackt wird und daraus die notwendigen XML Daten extrahiert werden. Auch die aktuell verwendete Version wird einiges an Laufzeit benötigen.

Vielen Dank für deinen Test!

erwin

Noch ein Hinweis zu 1.1.17.3:
2024.03.04 15:33:52.151 1: PERL WARNING: Use of uninitialized value $lastDocTimestamp in subtraction (-) at ./FHEM/55_DWD_OpenData.pm line 1994.
2024.03.04 15:33:52.151 1: PERL WARNING: Use of uninitialized value $dwdDocTimestamp in subtraction (-) at ./FHEM/55_DWD_OpenData.pm line 1994.
...passiert jede Stunde, wenn versucht wird MOSMIX_S upzudaten.
Ich vermute das probl. liegt irgendwo in ParseDateTimeUTC(),
die attr.:
fc_dwdDocSize 38338439 2024-03-04 15:30:57
fc_dwdDocTime 2024-03-04 14:30:44Z
sehen jedenfalls vernüftig aus.
Wichtig: ein manuelles "get DWDxxx update" funktioniert.
l.g.erwin
FHEM aktuell auf RaspberryPI Mdl 1-4
Maintainer: 00_KNXIO.pm 10_KNX.pm
User: CUNO2 (868 SLOWRF) - HMS100xx, FS20, FHT, 1-Wire  - 2401(iButton), 18x20, 2406, 2413 (AVR), 2450,..,MQTT2, KNX, SONOFF, mySENSORS,....
Hardware:  Busware ROT, Weinzierl IP731, 1-Wire GW,...

DS_Starter

Kann die Info von erwin bestätigen.
Habe auch schon gesucht.
Nachdem ich jetzt die Readings fc_dwdDocSize, fc_dwdDocTime gelöscht habe, hat das autom. Update (einmalig) funktioniert.
Ich warte nun ab ob der Fehler damit weg ist oder wiederkommt.

LG

ESXi@NUC+Debian+MariaDB, PV: SMA, Victron MPII+Pylontech+CerboGX
Maintainer: SSCam, SSChatBot, SSCal, SSFile, DbLog/DbRep, Log2Syslog, SolarForecast,Watches, Dashboard, PylonLowVoltage
Kaffeekasse: https://www.paypal.me/HMaaz
Contrib: https://svn.fhem.de/trac/browser/trunk/fhem/contrib/DS_Starter

DS_Starter

Ging nur einmal automatisch. Manuell funktioniert das Update.
ESXi@NUC+Debian+MariaDB, PV: SMA, Victron MPII+Pylontech+CerboGX
Maintainer: SSCam, SSChatBot, SSCal, SSFile, DbLog/DbRep, Log2Syslog, SolarForecast,Watches, Dashboard, PylonLowVoltage
Kaffeekasse: https://www.paypal.me/HMaaz
Contrib: https://svn.fhem.de/trac/browser/trunk/fhem/contrib/DS_Starter

mumpitzstuff

Zitat von: SparcWolf am 04 März 2024, 11:19:39
Zitat von: mumpitzstuff am 03 März 2024, 20:51:31...
Hier noch mal eine etwas komplexere Variante der ersten Version, die aber rund doppelt so schnell ist auf meinem Desktop Rechner, dafür aber einen Buffer von 1MB benötigt (was sicherlich lächerlich wenig ist).

Der RPi 3 B+ (1GB RAM) braucht für das Skript ca. 24 Sekunden.

@SparcWolf Könntest du bitte noch mal die folgende Version prüfen?

use strict;
use warnings;
use IO::Uncompress::Unzip qw(unzip $UnzipError);
use Time::HiRes qw(gettimeofday);


# dieser Teil würde durch den Download erledigt werden
open my $fh_zip, '<:raw', 'MOSMIX_S_LATEST_240.kmz' or die "Could not open file for reading: $!";
my $fileContent = do { local $/; <$fh_zip> };
close $fh_zip;

my $t0 = gettimeofday();

my $z = new IO::Uncompress::Unzip(\$fileContent) or die "unzip failed: $UnzipError\n";

my $buffer;
my $offset = 0;
my $startOfLine = 0;
my $endOfLine = 0;
my $line;
my $collect = 0;
my $collectString = '';
my $headerParsed = 0;

# split into chunks of 1MB
READ_CHUNKS:
while ($z->read($buffer, 1000000, $offset) > 0)
{
    $endOfLine = index($buffer, "\n");
   
    while ($endOfLine != -1)
    {
        $line = substr($buffer, $startOfLine, $endOfLine - $startOfLine + 1);
        $startOfLine = $endOfLine + 1;
        $endOfLine = index($buffer, "\n", $endOfLine + 1);
       
        if ($headerParsed == 0)
        {
            if (index($line, '<dwd:Issuer>') != -1)
            {   
                print $line;
            }
            elsif (index($line, '<dwd:IssueTime>') != -1)
            {
                print $line;
            }
            elsif (index($line, '<dwd:ForecastTimeSteps>') != -1)
            {
               $collect = 1;
            }
            elsif (index($line, '</dwd:ForecastTimeSteps>') != -1)
            {
               $collect = 0;
               print $collectString.$line;
               $collectString = '';
            }
            elsif (index($line, '<dwd:FormatCfg>') != -1)
            {
               $collect = 1;
            }
            elsif (index($line, '</dwd:FormatCfg>') != -1)
            {
               $collect = 0;
               print $collectString.$line;
               $collectString = '';
            }
        }
       
        if (index($line, '<kml:Placemark>') != -1)
        {
           #print 'Collection started: '.$line;
           # parsing the header data is not needed anymore
           $headerParsed = 1;
           $collect = 1;
        }
        elsif (($collect == 1) && (index($line, '<kml:name>') != -1))
        {
            # Beispielstation (relativ weit hinten)
            if (index($line, '06770</kml:name>', 10) == -1)
            {
                #print 'Filtered: '.$line;
                $collect = 0;
                $collectString = '';
            }
            else
            {
                print 'Station gefunden!'."\r\n";
            }
        }
        elsif (($collect == 1) && (index($line, '</kml:Placemark>') != -1))
        {
           $collect = 0;
           print $collectString.$line;
           last READ_CHUNKS;
        }
             
        if ($collect == 1)
        {
            $collectString .= $line;
        }
    }

    # find end of last line within chunk
    my $end = rindex($buffer, "\n");
    # copy the last incomplete line to the buffer of the next chunk
    if (($end > 0) && ($end < length($buffer) - 1))
    {
        $buffer = substr($buffer, $end + 1);
        $offset = length($buffer);
    }

    $startOfLine = 0;
}

$z->close();

print "benchmark1: ".(gettimeofday() - $t0)."\n\n";

Ich habe noch ein wenig Feintuning gemacht und auf meinem Rechner die Zeit von 5s auf 3.4s runter gebracht.

SparcWolf

Hallo @mumpitzstuff,

Sehr schön. Die Mühe hat sich gelohnt.

benchmark1: 15.2965559959412
benchmark1: 14.649405002594
benchmark1: 14.5959498882294
benchmark1: 14.8466770648956

Grüße,
  Guido.