diff --git a/fhem/contrib/DS_Starter/93_DbRep.pm b/fhem/contrib/DS_Starter/93_DbRep.pm index ce5010c9e..bd9def3a8 100644 --- a/fhem/contrib/DS_Starter/93_DbRep.pm +++ b/fhem/contrib/DS_Starter/93_DbRep.pm @@ -1,5 +1,5 @@ ########################################################################################################## -# $Id: 93_DbRep.pm 27031 2023-01-11 23:20:18Z DS_Starter $ +# $Id: 93_DbRep.pm 27047 2023-01-13 20:58:50Z DS_Starter $ ########################################################################################################## # 93_DbRep.pm # @@ -59,6 +59,7 @@ no if $] >= 5.017011, warnings => 'experimental::smartmatch'; # Version History intern my %DbRep_vNotesIntern = ( + "8.51.3" => "19.01.2023 extend DbRep_averval avgTimeWeightMean by alkazaa ", "8.51.2" => "13.01.2023 rewrite sub DbRep_OutputWriteToDB, new averageValue option writeToDBSingleStart ", "8.51.1" => "11.01.2023 write TYPE uppercase with writeToDB option, Commandref edited, fix add SQL Cache History ". "set PRAGMA auto_vacuum = FULL when execute SQLite vacuum command", @@ -3258,7 +3259,7 @@ sub DbRep_averval { my ($gts,$gtsstr) = (0,q{}); # Variablen für Grünlandtemperatursumme GTS - my ($sql,$sth,$selspec,$addon,$gtsreached); + my ($sql,$sth,$selspec,$addon,$gtsreached,$bin_end,$val1); my $bst = [gettimeofday]; # Background-Startzeit @@ -3349,6 +3350,7 @@ sub DbRep_averval { @wsf = split(" ",$runtime_string_first); @wsn = split(" ",$runtime_string_next); + $wrstr .= $runtime_string."#".$line[0]."#".$wsf[0]."_".$wsf[1]."#".$wsn[0]."_".$wsn[1]."|"; # Kombi zum Rückschreiben in die DB } elsif ($acf =~ /avgDailyMeanGWS/x) { @@ -3440,9 +3442,9 @@ sub DbRep_averval { $gtsstr .= $runtime_string."#".$gts."#".$rsf[0]."|"; } - ############################################################################################# } elsif ($acf eq "avgTimeWeightMean") { + ############################################################################################# # zeitgewichteten Mittelwert berechnen # http://massmatics.de/merkzettel/#!837:Gewichteter_Mittelwert # @@ -3454,9 +3456,12 @@ sub DbRep_averval { # ..... # (val1*$dt/$tsum) + (val2*$dt/$tsum) + .... + (valn*$dt/$tsum) # - # gesamte Zeitspanne $tsum zwischen ersten und letzten Datensatz der Zeitscheibe ermitteln - my ($tsum,$tf,$tl,$tn,$to,$dt,$val,$val1); + + my $aval = (DbRep_checktimeaggr($hash))[2]; # alkazaa moved this here because $aval is needed earlier + + my ($tsum,$tf,$tl,$tn,$to,$dt,$val); # alkazaa + my $sum = 0; my $addonf = 'ORDER BY TIMESTAMP ASC LIMIT 1'; my $addonl = 'ORDER BY TIMESTAMP DESC LIMIT 1'; @@ -3472,8 +3477,48 @@ sub DbRep_averval { $dbh->disconnect; return "$name|$err"; }; +# + if ($bin_end) { # das $bin_end des letzten Bin ist der effektive Zeitpunkt des letzten Datenwertes + $tf = $bin_end; # der vorherigen Periode, die in die aktuelle Periode übernommen wird + } + else { # dies ist der erste Mittelungsplatz, und mit einem "Peek-back-in-time" wird versucht, den Wert unmittelbar vor der Startzeit zu ermitteln + my ($year,$month,$day,$hour,$min,$sec) = $runtime_string_first =~ m/(\d+)-(\d+)-(\d+)\s(\d+):(\d+):(\d+)/xs; + my $time = timelocal ($sec,$min,$hour,$day,$month-1,$year); + + if ($aval eq 'hour' || $aval eq 'minute') { + $time -= 3600; # um 1 Stunde zurückblicken + } + elsif ($aval eq 'day') { + $time -= 24 * 3600; # peek back by 1 day + } + elsif ($aval eq 'week') { + $time -= 3 * 24 * 3600; # peek back by 3 days + } + elsif ($aval eq 'month') { + $time -= 7 * 24 * 3600; # peek back by 1 week + } + else { + $time -= 30 * 24 * 3600; # peek back by 1 month + }; + + my $newtime_string = strftime ("%Y-%m-%d %H:%M:%S", localtime ($time)); + $sql = DbRep_createSelectSql($hash, $table, $selspec, $device, $reading, "'$newtime_string'", "'$runtime_string_first'", $addonl); + + ($err, $sth) = DbRep_prepareExecuteQuery ($name, $dbh, $sql); + return "$name|$err" if ($err); + + my @twm_array = map { $_->[0]."_ESC_".$_->[1] } @{$sth->fetchall_arrayref()}; + + for my $twmrow (@twm_array) { + ($tn,$val1) = split("_ESC_",$twmrow); + $val1 = DbRep_numval ($val1); # nichtnumerische Zeichen eliminieren + $bin_end = $runtime_string_first; # the last value before the full timeframe is 'faked to the start of the timeframe + $tf = $runtime_string_first; + } + } - if(!$tf || !$tl) { # kein Start- und/oder Ende Timestamp in Zeitscheibe vorhanden -> keine Werteberechnung möglich +# old: if(!$tf || !$tl) { # kein Start- und/oder Ende Timestamp in Zeitscheibe vorhanden -> keine Werteberechnung möglich + if(!$tf) { # kein Start-Timestamp in Zeitscheibe vorhanden -> keine Werteberechnung möglich # kein Start- und/oder Ende Timestamp in Zeitscheibe vorhanden -> keine Werteberechnung möglich $sum = "insufficient values"; } else { @@ -3482,6 +3527,9 @@ sub DbRep_averval { $tsum = (timelocal($secl, $minl, $hhl, $ddl, $mml-1, $yyyyl-1900))- (timelocal($secf, $minf, $hhf, $ddf, $mmf-1, $yyyyf-1900)); + $tsum = 0; + $sum = 0; +# if ($IsTimeSet || $IsAggrSet) { $sql = DbRep_createSelectSql($hash, $table, $selspec, $device, $reading, "'$runtime_string_first'", "'$runtime_string_next'", $addon); @@ -3493,14 +3541,30 @@ sub DbRep_averval { ($err, $sth) = DbRep_prepareExecuteQuery ($name, $dbh, $sql); return "$name|$err" if ($err); - my @twm_array = map { $_->[0]."_ESC_".$_->[1] } @{$sth->fetchall_arrayref()}; + my @twm_array = map { $_->[0]."_ESC_".$_->[1] } @{$sth->fetchall_arrayref()}; +# + if ($bin_end) { # der letzte Datenwert aus dem vorherigen Bin wird dem aktuellen Bin vorangestellt, + unshift @twm_array, $bin_end.'_ESC_'.$val1; # wobei das vorherige $bin_end als Zeitstempel verwendet wird + Log3 ($name, 4, "DbRep $name - Time: $bin_end, Value: $val1"); + } + + # für jeden Bin word die Endzeit aufgezeichnet, um sie für den nächsten Bin zu verwenden + my ($yyyyf, $mmf, $ddf, $hhf, $minf, $secf) = $runtime_string_next =~ /(\d+)-*(\d+)*-*(\d+)*\s*(\d+)*:*(\d+)*:*(\d+)*/xs; + + $bin_end = $runtime_string_next; + $bin_end .='-01' if (!$mmf); + $bin_end .='-01' if (!$ddf); + $bin_end .=' 00' if (!$hhf); + $bin_end .=':00' if (!$minf); + $bin_end .=':00' if (!$secf); +# <\alkazaa> for my $twmrow (@twm_array) { - ($tn,$val) = split("_ESC_",$twmrow); - $val = DbRep_numval ($val); # nichtnumerische Zeichen eliminieren + ($tn,$val) = split "_ESC_", $twmrow; + $val = DbRep_numval ($val); # nichtnumerische Zeichen eliminieren - my ($yyyyt1, $mmt1, $ddt1, $hht1, $mint1, $sect1) = ($tn =~ /(\d+)-(\d+)-(\d+) (\d+):(\d+):(\d+)/); - $tn = timelocal($sect1, $mint1, $hht1, $ddt1, $mmt1-1, $yyyyt1-1900); + my ($yyyyt1, $mmt1, $ddt1, $hht1, $mint1, $sect1) = $tn =~ /(\d+)-(\d+)-(\d+)\s(\d+):(\d+):(\d+)/xs; + $tn = timelocal ($sect1, $mint1, $hht1, $ddt1, $mmt1-1, $yyyyt1-1900); if(!$to) { $val1 = $val; @@ -3508,17 +3572,24 @@ sub DbRep_averval { next; } - $dt = $tn - $to; - $sum += $val1 * ($dt/$tsum) if($tsum); - $val1 = $val; - $to = $tn; + $dt = $tn - $to; + $tsum += $dt; # alkazaa: Bildung der Zeitsumme für die spätere Division +# alkazza, old: $sum += $val1*$dt/$tsum) if($tsum); + $sum += $val1 * $dt; # alkazaa neu: die Division durch die Gesamtzeit wird am Ende, außerhalb der Schleife durchgeführt + $val1 = $val; + $to = $tn; Log3 ($name, 5, "DbRep $name - data element: $twmrow"); Log3 ($name, 5, "DbRep $name - time sum: $tsum, delta time: $dt, value: $val1, twm: ".($tsum ? $val1*($dt/$tsum) : 0)); } + + $dt = timelocal($secf, $minf, $hhf, $ddf, $mmf-1, $yyyyf-1900) - $to; # alkazaa: die Zeitspanne des letzten Datenwertes in diesem Bin wird für diesen Bin berücksichtigt + # das Ende dieses Bin ist das $to für das nächste Bin + $tsum += $dt; # $dt ist das Zeitgewicht des letzten Wertes in diesem Bin + $sum += $val1 * $dt; + $sum /= $tsum if ($tsum > 0); +# } - - my $aval = (DbRep_checktimeaggr($hash))[2]; if($aval eq "hour") { @rsf = split /[ :]/,$runtime_string_first; @@ -8019,7 +8090,7 @@ sub DbRep_mysql_DumpClientSide { $Jahr += 1900; $Monat += 1; $Jahrestag += 1; - my $CTIME_String = strftime "%Y-%m-%d %T",localtime(time); + my $CTIME_String = strftime "%Y-%m-%d %T", localtime(time); my $time_stamp = $Jahr."_".sprintf("%02d",$Monat)."_".sprintf("%02d",$Monatstag)."_".sprintf("%02d",$Stunden)."_".sprintf("%02d",$Minuten); my $starttime = sprintf("%02d",$Monatstag).".".sprintf("%02d",$Monat).".".$Jahr." ".sprintf("%02d",$Stunden).":".sprintf("%02d",$Minuten); @@ -13535,12 +13606,12 @@ sub DbRep_setVersionInfo { if($modules{$type}{META}{x_prereqs_src} && !$hash->{HELPER}{MODMETAABSENT}) { # META-Daten sind vorhanden $modules{$type}{META}{version} = "v".$v; # Version aus META.json überschreiben, Anzeige mit {Dumper $modules{SMAPortal}{META}} - if($modules{$type}{META}{x_version}) { # {x_version} ( nur gesetzt wenn $Id: 93_DbRep.pm 27031 2023-01-11 23:20:18Z DS_Starter $ im Kopf komplett! vorhanden ) + if($modules{$type}{META}{x_version}) { # {x_version} ( nur gesetzt wenn $Id: 93_DbRep.pm 27047 2023-01-13 20:58:50Z DS_Starter $ im Kopf komplett! vorhanden ) $modules{$type}{META}{x_version} =~ s/1.1.1/$v/g; } else { $modules{$type}{META}{x_version} = $v; } - return $@ unless (FHEM::Meta::SetInternals($hash)); # FVERSION wird gesetzt ( nur gesetzt wenn $Id: 93_DbRep.pm 27031 2023-01-11 23:20:18Z DS_Starter $ im Kopf komplett! vorhanden ) + return $@ unless (FHEM::Meta::SetInternals($hash)); # FVERSION wird gesetzt ( nur gesetzt wenn $Id: 93_DbRep.pm 27047 2023-01-13 20:58:50Z DS_Starter $ im Kopf komplett! vorhanden ) if(__PACKAGE__ eq "FHEM::$type" || __PACKAGE__ eq $type) { # es wird mit Packages gearbeitet -> Perl übliche Modulversion setzen # mit {->VERSION()} im FHEMWEB kann Modulversion abgefragt werden @@ -15412,12 +15483,15 @@ return;
-
- Hinweis:
- When the vacuum command is executed, the PRAGMA auto_vacuum = FULL is automatically applied to SQLite databases. + Note:
+ When the vacuum command is executed, the PRAGMA auto_vacuum = FULL is automatically applied to SQLite databases.
+ The vacuum command requires additional temporary memory. If there is not enough space in the default TMPDIR directory, + SQLite can be assigned a sufficiently large directory by setting the environment variable SQLITE_TMPDIR.
+ (see also: www.sqlite.org/tempfiles)
+

@@ -16593,7 +16667,7 @@ return; attr Rep.Agent role Agent
attr Rep.Agent room DbLog
attr Rep.Agent showproctime 1
- attr Rep.Agent stateFormat { ReadingsVal("$name","state", undef) eq "running" ? "renaming" : ReadingsVal("$name","state", undef). " »; ProcTime: ".ReadingsVal("$name","sql_processing_time", undef)." sec"}
+ attr Rep.Agent stateFormat { ReadingsVal($name, 'state', '') eq 'running' ? 'renaming' : ReadingsVal($name, 'state', ''). ' »; ProcTime: '.ReadingsVal($name, 'sql_processing_time', '').' sec'}
attr Rep.Agent timeout 86400

@@ -18318,13 +18392,17 @@ return;
-
Hinweis:
Bei der Ausführung des vacuum Kommandos wird bei SQLite Datenbanken automatisch das PRAGMA auto_vacuum = FULL - angewendet. + angewendet.
+ Das vacuum Kommando erfordert zusätzlichen temporären Speicherplatz. Sollte der Platz im Standard TMPDIR Verzeichnis + nicht ausreichen, kann SQLite durch setzen der Umgebungsvariable SQLITE_TMPDIR ein ausreichend großes Verzeichnis + zugewiesen werden.
+ (siehe: www.sqlite.org/tempfiles)
+

@@ -19518,7 +19596,7 @@ return; attr Rep.Agent role Agent
attr Rep.Agent room DbLog
attr Rep.Agent showproctime 1
- attr Rep.Agent stateFormat { ReadingsVal("$name","state", undef) eq "running" ? "renaming" : ReadingsVal("$name","state", undef). " »; ProcTime: ".ReadingsVal("$name","sql_processing_time", undef)." sec"}
+ attr Rep.Agent stateFormat { ReadingsVal($name, 'state', '') eq 'running' ? 'renaming' : ReadingsVal($name, 'state', ''). ' »; ProcTime: '.ReadingsVal($name, 'sql_processing_time', '').' sec'}
attr Rep.Agent timeout 86400