diff --git a/fhem/CHANGED b/fhem/CHANGED
index a2ec683a9..57cd99d2b 100644
--- a/fhem/CHANGED
+++ b/fhem/CHANGED
@@ -1,5 +1,7 @@
# Add changes at the top of the list. Keep it in ASCII, and 80-char wide.
# Do not insert empty lines here, update check depends on it.
+ - feature: 93_DbRep: reduceLog - add max, max=day, min, min=day, sum, sum=day
+ some fixes of reduceLog routine
- feature: 50_Signalbot: support to link 2 FHEM instance under the same number
- feature: 93_DbLog: new Attr convertTimezone -> logging in UTC time
- bugfix: 93_DbRep: fix set ... deleteOther for SQLite
diff --git a/fhem/FHEM/93_DbRep.pm b/fhem/FHEM/93_DbRep.pm
index baeadd130..4b00fd259 100644
--- a/fhem/FHEM/93_DbRep.pm
+++ b/fhem/FHEM/93_DbRep.pm
@@ -57,6 +57,7 @@ no if $] >= 5.017011, warnings => 'experimental::smartmatch';
# Version History intern
my %DbRep_vNotesIntern = (
+ "8.50.0" => "20.08.2022 rework of DbRep_reduceLog - add max, max=day, min, min=day, sum, sum=day ",
"8.49.1" => "03.08.2022 fix DbRep_deleteOtherFromDB, Forum: https://forum.fhem.de/index.php/topic,128605.0.html ".
"some code changes and bug fixes ",
"8.49.0" => "16.05.2022 allow optionally set device / reading in the insert command ",
@@ -2925,12 +2926,11 @@ sub _DbRep_collaggstr {
$runtime_orig = $runtime;
# Hilfsrechnungen
- my $rm = strftime "%m", localtime($runtime); # Monat des aktuell laufenden Startdatums d. SQL-Select
- my $cy = strftime "%Y", localtime($runtime); # Jahr des aktuell laufenden Startdatums d. SQL-Select
- # my $icly = DbRep_IsLeapYear($name,$cy);
- # my $inly = DbRep_IsLeapYear($name,$cy+1); # ist kommendes Jahr ein Schaltjahr ?
+ my $rm = strftime "%m", localtime($runtime); # Monat des aktuell laufenden Startdatums d. SQL-Select
+ my $cy = strftime "%Y", localtime($runtime); # Jahr des aktuell laufenden Startdatums d. SQL-Select
my $yf = 365;
- $yf = 366 if(DbRep_IsLeapYear($name,$cy)); # ist aktuelles Jahr ein Schaltjahr ?
+ $yf = 366 if(DbRep_IsLeapYear($name,$cy)); # ist aktuelles Jahr ein Schaltjahr ?
+
Log3 ($name, 5, "DbRep $name - current year: $cy, endyear: $yestr");
$aggsec = $yf * 86400;
@@ -2948,13 +2948,14 @@ sub _DbRep_collaggstr {
$runtime_string_first = strftime "%Y-01-01", localtime($runtime) if($i>1);
$runtime_string_next = strftime "%Y-%m-%d %H:%M:%S", localtime($epoch_seconds_end);
$ll=1;
-
- } else {
+ }
+ else {
if(($runtime) > $epoch_seconds_end) {
$runtime_string_first = strftime "%Y-01-01", localtime($runtime);
$runtime_string_next = strftime "%Y-%m-%d %H:%M:%S", localtime($epoch_seconds_end);
$ll=1;
- } else {
+ }
+ else {
$runtime_string_first = strftime "%Y-01-01", localtime($runtime) if($i>1);
$runtime_string_next = strftime "%Y-01-01", localtime($runtime+($yf * 86400));
}
@@ -2988,15 +2989,16 @@ sub _DbRep_collaggstr {
if ($ysstr == $yestr && $msstr == $mestr || $ry == $yestr && $rm == $mestr) {
$runtime_string_first = strftime "%Y-%m-01", localtime($runtime) if($i>1);
$runtime_string_next = strftime "%Y-%m-%d %H:%M:%S", localtime($epoch_seconds_end);
- $ll=1;
-
- } else {
+ $ll=1;
+ }
+ else {
if(($runtime) > $epoch_seconds_end) {
#$runtime_string_first = strftime "%Y-%m-01", localtime($runtime) if($i>11); # ausgebaut 24.02.2018
$runtime_string_first = strftime "%Y-%m-01", localtime($runtime);
$runtime_string_next = strftime "%Y-%m-%d %H:%M:%S", localtime($epoch_seconds_end);
$ll=1;
- } else {
+ }
+ else {
$runtime_string_first = strftime "%Y-%m-01", localtime($runtime) if($i>1);
$runtime_string_next = strftime "%Y-%m-01", localtime($runtime+($dim*86400));
}
@@ -3034,16 +3036,19 @@ sub _DbRep_collaggstr {
if((strftime "%V", localtime($epoch_seconds_end)) eq ($w) && ($ms+$me != 13)) {
$runtime_string_next = strftime "%Y-%m-%d %H:%M:%S", localtime($epoch_seconds_end);
$ll=1;
- } else {
+ }
+ else {
$runtime_string_next = strftime "%Y-%m-%d", localtime($runtime);
}
- } else {
+ }
+ else {
# weitere Durchläufe
if(($runtime+$aggsec) > $epoch_seconds_end) {
$runtime_string_first = strftime "%Y-%m-%d", localtime($runtime_orig);
$runtime_string_next = strftime "%Y-%m-%d %H:%M:%S", localtime($epoch_seconds_end);
$ll=1;
- } else {
+ }
+ else {
$runtime_string_first = strftime "%Y-%m-%d", localtime($runtime_orig) ;
$runtime_string_next = strftime "%Y-%m-%d", localtime($runtime+$aggsec);
}
@@ -3065,7 +3070,8 @@ sub _DbRep_collaggstr {
$runtime_string_first = strftime "%Y-%m-%d %H:%M:%S", localtime($runtime) if( $dsstr eq $destr);
$runtime_string_next = strftime "%Y-%m-%d %H:%M:%S", localtime($epoch_seconds_end);
$ll=1;
- } else {
+ }
+ else {
$runtime_string_next = strftime "%Y-%m-%d", localtime($runtime+$aggsec);
}
@@ -3094,7 +3100,8 @@ sub _DbRep_collaggstr {
$runtime_string_first = strftime "%Y-%m-%d %H:%M:%S", localtime($runtime) if( $dsstr eq $destr && $hs eq $he);
$runtime_string_next = strftime "%Y-%m-%d %H:%M:%S", localtime($epoch_seconds_end);
$ll=1;
- } else {
+ }
+ else {
$runtime_string_next = strftime "%Y-%m-%d %H", localtime($runtime+$aggsec);
}
@@ -3121,7 +3128,8 @@ sub _DbRep_collaggstr {
# $runtime_string_first = strftime "%Y-%m-%d %H:%M", localtime($runtime) if( $dsstr eq $destr && $ms eq $me);
$runtime_string_next = strftime "%Y-%m-%d %H:%M:%S", localtime($epoch_seconds_end);
$ll=1;
- } else {
+ }
+ else {
$runtime_string_next = strftime "%Y-%m-%d %H:%M", localtime($runtime+$aggsec);
}
@@ -8946,11 +8954,6 @@ sub DbRep_reduceLog {
}
Log3 ($name, 5, "DbRep $name -> Start DbLog_reduceLog");
-
- my ($dbh,$brt,$ret,$row,$filter,$exclude,$c,$day,$hour,$lastHour,$updDate,$updHour);
- my ($dbmodel,$average,$processingDay,$lastUpdH);
- my (%hourlyKnown,%averageHash,@excludeRegex,@dayRows,@averageUpd,@averageUpdD);
- my ($startTime,$currentHour,$currentDay,$deletedCount,$updateCount,$sum,$rowCount,$excludeCount) = (time(),99,0,0,0,0,0,0);
BlockingInformParent("DbRep_delHashValFromBlocking", [$name, "HELPER","REDUCELOG"], 1);
@@ -8961,28 +8964,36 @@ sub DbRep_reduceLog {
next if($w =~ /\b(\d+(:\d+)?)\b/);
push @b, $w;
}
+
@a = @b;
my ($pa,$ph) = parseParams(join ' ', @a);
- my $avgstring = (@$pa[1] && @$pa[1] =~ /average/i) ? 'AVERAGE=HOUR' :
- ($ph->{average} && $ph->{average} eq "day") ? 'AVERAGE=DAY' :
- q{};
-
- # Korrektur des Select-Zeitraums + eine Stunde
- # (Forum: https://forum.fhem.de/index.php/topic,53584.msg1177799.html#msg1177799)
- my ($yyyy, $mm, $dd, $hh, $min, $sec) = $ots =~ /(\d+)-(\d+)-(\d+)\s(\d+):(\d+):(\d+)/x;
- my $epoche = timelocal($sec, $min, $hh, $dd, $mm-1, $yyyy-1900);
- my $splus = $avgstring =~ /AVERAGE/ ? 3600 : 0;
- $ots = strftime "%Y-%m-%d %H:%M:%S", localtime($epoche+$splus);
+ my $mode = (@$pa[1] && @$pa[1] =~ /average/i) ? 'average' :
+ ($ph->{average} && $ph->{average} eq "day") ? 'average=day' :
+ (@$pa[1] && @$pa[1] =~ /max/i) ? 'max' :
+ ($ph->{max} && $ph->{max} eq "day") ? 'max=day' :
+ (@$pa[1] && @$pa[1] =~ /min/i) ? 'min' :
+ ($ph->{min} && $ph->{min} eq "day") ? 'min=day' :
+ (@$pa[1] && @$pa[1] =~ /sum/i) ? 'sum' :
+ ($ph->{sum} && $ph->{sum} eq "day") ? 'sum=day' :
+ q{};
+
+ my $mstr = $mode =~ /average/i ? 'average' :
+ $mode =~ /max/i ? 'max' :
+ $mode =~ /min/i ? 'min' :
+ $mode =~ /sum/i ? 'sum' :
+ q{};
my $excludes = $ph->{EXCLUDE} // q{};
my $includes = $ph->{INCLUDE} // q{};
+ my @excludeRegex;
if ($excludes) {
- @excludeRegex = split(',',$excludes);
+ @excludeRegex = split ',', $excludes;
}
-
+
+ my ($dbh, $dbmodel);
($err,$dbh,$dbmodel) = DbRep_dbConnect($name, 0);
return "$name|$err" if ($err);
@@ -8992,7 +9003,6 @@ sub DbRep_reduceLog {
Log3 ($name, 5, "DbRep $name - IsTimeSet: $IsTimeSet, IsAggrSet: $IsAggrSet");
- my $sql;
my $selspec = "SELECT TIMESTAMP,DEVICE,'',READING,VALUE FROM $table where ";
my $addon = "ORDER BY TIMESTAMP ASC";
@@ -9008,6 +9018,8 @@ sub DbRep_reduceLog {
addon => $addon
};
+ my $sql;
+
if($includes) { # Option EX/INCLUDE wurde angegeben
$sql = "SELECT TIMESTAMP,DEVICE,'',READING,VALUE FROM $table WHERE "
.($includes =~ /^(.+):(.+)$/i ? "DEVICE like '$1' AND READING like '$2' AND " : '')
@@ -9018,19 +9030,16 @@ sub DbRep_reduceLog {
elsif ($IsTimeSet || $IsAggrSet) {
$specs->{rsf} = $nts;
$specs->{rsn} = $ots;
- $sql = DbRep_createCommonSql ($specs);
+ $sql = DbRep_createCommonSql ($specs);
}
else {
$sql = DbRep_createCommonSql ($specs);
}
-
- Log3 ($name, 3, "DbRep $name - reduce data older than: $ots (logical corrected), newer than: $nts");
-
- Log3 ($name, 4, "DbRep $name - SQL execute: $sql");
+ Log3 ($name, 3, "DbRep $name - reduce data older than: $ots, newer than: $nts");
Log3 ($name, 3, "DbRep $name - reduceLog requested with options: "
- .($avgstring ? "\n".$avgstring : '')
+ .($mode ? "\n".$mode : '')
.($includes ? "\nINCLUDE -> $includes " :
((($idanz || $idevswc || $iranz || $irdswc) ? "\nINCLUDE -> " : '')
. (($idanz || $idevswc) ? "Devs: ".($idevs ? $idevs : '').($idevswc ? $idevswc : '').' ' : '').(($iranz || $irdswc) ? "Readings: ".($ireading ? $ireading : '').($irdswc ? $irdswc : '') : '')
@@ -9043,377 +9052,793 @@ sub DbRep_reduceLog {
my ($sth_del, $sth_upd, $sth_delD, $sth_updD, $sth_get);
- eval { $sth_del = $dbh->prepare_cached("DELETE FROM $table WHERE (DEVICE=?) AND (READING=?) AND (TIMESTAMP=?) AND (VALUE=?)");
- $sth_upd = $dbh->prepare_cached("UPDATE $table SET TIMESTAMP=?, EVENT=?, VALUE=? WHERE (DEVICE=?) AND (READING=?) AND (TIMESTAMP=?) AND (VALUE=?)");
- $sth_delD = $dbh->prepare_cached("DELETE FROM $table WHERE (DEVICE=?) AND (READING=?) AND (TIMESTAMP=?)");
- $sth_updD = $dbh->prepare_cached("UPDATE $table SET TIMESTAMP=?, EVENT=?, VALUE=? WHERE (DEVICE=?) AND (READING=?) AND (TIMESTAMP=?)");
- $sth_get = $dbh->prepare($sql);
- };
- if ($@) {
- $err = encode_base64($@, "");
- Log3 ($name, 2, "DbRep $name - DbRep_reduceLog - $@");
- return "$name|$err";
- }
+ ($err, $sth_del) = DbRep_prepareCachedOnly ($name, $dbh, "DELETE FROM $table WHERE (DEVICE=?) AND (READING=?) AND (TIMESTAMP=?) AND (VALUE=?)");
+ return "$name|$err" if ($err);
- eval { $sth_get->execute(); };
- if ($@) {
- $err = encode_base64($@, "");
- Log3 ($name, 2, "DbRep $name - DbRep_reduceLog - $@");
- return "$name|$err";
- }
+ ($err, $sth_upd) = DbRep_prepareCachedOnly ($name, $dbh, "UPDATE $table SET TIMESTAMP=?, EVENT=?, VALUE=? WHERE (DEVICE=?) AND (READING=?) AND (TIMESTAMP=?) AND (VALUE=?)");
+ return "$name|$err" if ($err);
- ######################################### Start
+ ($err, $sth_delD) = DbRep_prepareCachedOnly ($name, $dbh, "DELETE FROM $table WHERE (DEVICE=?) AND (READING=?) AND (TIMESTAMP=?)");
+ return "$name|$err" if ($err);
- # Ergebnsis von $sth_get->fetchrow_arrayref
+ ($err, $sth_updD) = DbRep_prepareCachedOnly ($name, $dbh, "UPDATE $table SET TIMESTAMP=?, EVENT=?, VALUE=? WHERE (DEVICE=?) AND (READING=?) AND (TIMESTAMP=?)");
+ return "$name|$err" if ($err);
+
+ ($err, $sth_get) = DbRep_prepareExecuteQuery ($name, $dbh, $sql);
+ return "$name|$err" if ($err);
+
+
+ ## Start
+ ############################################
+
+ # Ergebnis von $sth_get->fetchrow_arrayref:
# $row->[0] = Datum (YYYY-MM-DD hh:mm:ss)
# $row->[1] = Device
# $row->[2] = leer
# $row->[3] = Reading
# $row->[4] = Value
+ my $ndp = AttrVal($name, "numDecimalPlaces", $dbrep_defdecplaces);
+
+ my ($day, $hour, $processingDay, $params);
+ my (%hourlyKnown,@dayRows,@updateHour,@updateDay);
+ my ($startTime,$currentHour,$currentDay,$deletedCount,$updateCount,$rowCount,$excludeCount) = (time(),99,0,0,0,0,0);
+
do {
- $row = $sth_get->fetchrow_arrayref || ['0000-00-00 00:00:00','D','','R','V']; # || execute last-day dummy
- $ret = 1;
- ($day,$hour) = $row->[0] =~ /-(\d{2})\s(\d{2}):/;
+ my $row = $sth_get->fetchrow_arrayref || ['0000-00-00 00:00:00','D','','R','V']; # || execute last-day dummy
+ my $ts = $row->[0];
+ my $device = $row->[1];
+ my $reading = $row->[3];
+ my $value = $row->[4];
+ ($day,$hour) = $ts =~ /-(\d{2})\s(\d{2}):/;
$rowCount++ if($day != 00);
+ ## verarbeiten der unten vorbereiteten Arrays und Hashes
+ #########################################################
+
if ($day != $currentDay) {
- if ($currentDay) { # nicht am ersten ausgeführten Tag
- if (scalar @dayRows) {
- ($lastHour) = $dayRows[-1]->[0] =~ /(.*\d+\s\d{2}):/;
- $c = 0;
+ if ($currentDay) { # nicht am ersten ausgeführten Tag
+ if (scalar @dayRows) { # alle Tageseinträge löschen
- for my $delRow (@dayRows) {
- $c++ if($day != 00 || $delRow->[0] !~ /$lastHour/);
- }
+ $params = {
+ name => $name,
+ dbh => $dbh,
+ sth_del => $sth_del,
+ table => $table,
+ dayRowsref => \@dayRows,
+ deletedCountref => \$deletedCount,
+ processingDay => $processingDay,
+ ndp => $ndp
+ };
+
+ $err = _DbRep_rl_deleteDayRows ($params);
+ return "$name|$err" if ($err);
- if($c) {
- $deletedCount += $c;
-
- Log3 ($name, 3, "DbRep $name - reduceLog deleting $c records of day: $processingDay");
-
- $dbh->{RaiseError} = 1;
- $dbh->{PrintError} = 0;
-
- eval {$dbh->begin_work() if($dbh->{AutoCommit});};
- if ($@) {
- $err = encode_base64($@, "");
- Log3 ($name, 2, "DbRep $name - DbRep_reduceLog - $@");
- }
-
- eval {
- my $i = 0;
- my $k = 1;
- my $th = ($#dayRows <= 2000) ? 100 :
- ($#dayRows <= 30000) ? 1000 :
- 10000;
-
- for my $delRow (@dayRows) {
- if($day != 00 || $delRow->[0] !~ /$lastHour/) {
-
- Log3 ($name, 5, "DbRep $name - DELETE FROM $table WHERE (DEVICE=$delRow->[1]) AND (READING=$delRow->[3]) AND (TIMESTAMP=$delRow->[0]) AND (VALUE=$delRow->[4])");
-
- $sth_del->execute(($delRow->[1], $delRow->[3], $delRow->[0], $delRow->[4]));
- $i++;
-
- if($i == $th) {
- my $prog = $k * $i;
-
- Log3 ($name, 3, "DbRep $name - reduceLog deletion progress of day: $processingDay is: $prog");
-
- $i = 0;
- $k++;
- }
- }
- }
- };
-
- if ($@) {
- $err = encode_base64($@, "");
-
- Log3 ($name, 2, "DbRep $name - reduceLog ! FAILED ! for day $processingDay: $@");
-
- eval {$dbh->rollback() if(!$dbh->{AutoCommit});};
- if ($@) {
- $err = encode_base64($@, "");
- Log3 ($name, 2, "DbRep $name - DbRep_reduceLog - $@");
- }
- $ret = 0;
- }
- else {
- eval {$dbh->commit() if(!$dbh->{AutoCommit});};
- if ($@) {
- $err = encode_base64($@, "");
- Log3 ($name, 2, "DbRep $name - DbRep_reduceLog - $@");
- }
- }
- $dbh->{RaiseError} = 0;
- $dbh->{PrintError} = 1;
- }
@dayRows = ();
}
- if ($ret && $avgstring =~ /average/i) {
- $dbh->{RaiseError} = 1;
- $dbh->{PrintError} = 0;
-
- eval {$dbh->begin_work() if($dbh->{AutoCommit});};
- if ($@) {
- $err = encode_base64($@, "");
- Log3 ($name, 2, "DbRep $name - DbRep_reduceLog - $@");
- }
-
- eval {
- push(@averageUpd, {%hourlyKnown}) if($day != 00);
-
- $c = 0;
- for my $hourHash (@averageUpd) { # Only count for logging...
- for my $hourKey (keys %$hourHash) {
- $c++ if ($hourHash->{$hourKey}->[0] && scalar(@{$hourHash->{$hourKey}->[4]}) > 1);
- }
- }
-
- $updateCount += $c;
-
- Log3 ($name, 3, "DbRep $name - reduceLog (hourly-average) updating $c records of day: $processingDay") if($c); # else only push to @averageUpdD
-
- my $i = 0;
- my $k = 1;
- my $th = $c <= 2000 ? 100 :
- $c <= 30000 ? 1000 :
- 10000;
-
- for my $hourHash (@averageUpd) {
- for my $hourKey (keys %$hourHash) {
- if ($hourHash->{$hourKey}->[0]) { # true if reading is a number
- ($updDate,$updHour) = $hourHash->{$hourKey}->[0] =~ /(.*\d+)\s(\d{2}):/;
-
- if (scalar(@{$hourHash->{$hourKey}->[4]}) > 1) { # true if reading has multiple records this hour
-
- for (@{$hourHash->{$hourKey}->[4]}) {
- $sum += $_;
- }
-
- $average = sprintf('%.3f', $sum/scalar(@{$hourHash->{$hourKey}->[4]}) );
- $sum = 0;
-
- Log3 ($name, 4, "DbRep $name - UPDATE $table SET TIMESTAMP=$updDate $updHour:30:00, EVENT='rl_av_h', VALUE=$average WHERE DEVICE=$hourHash->{$hourKey}->[1] AND READING=$hourHash->{$hourKey}->[3] AND TIMESTAMP=$hourHash->{$hourKey}->[0] AND VALUE=$hourHash->{$hourKey}->[4]->[0]");
-
- $sth_upd->execute(("$updDate $updHour:30:00", 'rl_av_h', $average, $hourHash->{$hourKey}->[1], $hourHash->{$hourKey}->[3], $hourHash->{$hourKey}->[0], $hourHash->{$hourKey}->[4]->[0]));
-
- $i++;
-
- if($i == $th) {
- my $prog = $k * $i;
- Log3 ($name, 3, "DbRep $name - reduceLog (hourly-average) updating progress of day: $processingDay is: $prog");
- $i = 0;
- $k++;
- }
-
- if ($avgstring =~ /average=day/i) {
- push(@averageUpdD, ["$updDate $updHour:30:00", 'rl_av_h', $average, $hourHash->{$hourKey}->[1], $hourHash->{$hourKey}->[3], $updDate]);
- }
- }
- else {
- if ($avgstring =~ /average=day/i) {
- push(@averageUpdD, [$hourHash->{$hourKey}->[0], $hourHash->{$hourKey}->[2], $hourHash->{$hourKey}->[4]->[0], $hourHash->{$hourKey}->[1], $hourHash->{$hourKey}->[3], $updDate]);
- }
- }
- }
- }
- }
+ if ($mode =~ /average|max|min|sum/i) {
+
+ $params = {
+ name => $name,
+ dbh => $dbh,
+ sth_upd => $sth_upd,
+ mode => $mode,
+ mstr => $mstr,
+ table => $table,
+ hourlyKnownref => \%hourlyKnown,
+ updateHourref => \@updateHour,
+ updateDayref => \@updateDay,
+ updateCountref => \$updateCount,
+ processingDay => $processingDay,
+ ndp => $ndp
};
+
+ $err = _DbRep_rl_updateHour ($params);
+ return "$name|$err" if ($err);
- if ($@) {
- $err = $@;
-
- Log3 ($name, 2, "DbRep $name - reduceLog average=hour ! FAILED ! for day $processingDay: $err");
-
- eval {$dbh->rollback() if(!$dbh->{AutoCommit});};
- if ($@) {
- $err = encode_base64($@, "");
- Log3 ($name, 2, "DbRep $name - DbRep_reduceLog - $@");
- }
- @averageUpdD = ();
- }
- else {
- eval {$dbh->commit() if(!$dbh->{AutoCommit});};
- if ($@) {
- $err = encode_base64($@, "");
- Log3 ($name, 2, "DbRep $name - DbRep_reduceLog - $@");
- }
- }
-
- $dbh->{RaiseError} = 0;
- $dbh->{PrintError} = 1;
- @averageUpd = ();
+ @updateHour = ();
}
- if ($avgstring =~ /average=day/i && scalar(@averageUpdD) && $day != 00) {
- $dbh->{RaiseError} = 1;
- $dbh->{PrintError} = 0;
-
- eval {$dbh->begin_work() if($dbh->{AutoCommit});};
- if ($@) {
- $err = encode_base64($@, "");
- Log3 ($name, 2, "DbRep $name - DbRep_reduceLog - $@");
- }
-
- eval {
- for (@averageUpdD) {
- push(@{$averageHash{$_->[3].$_->[4]}->{tedr}}, [$_->[0], $_->[1], $_->[3], $_->[4]]);
- $averageHash{$_->[3].$_->[4]}->{sum} += $_->[2];
- $averageHash{$_->[3].$_->[4]}->{date} = $_->[5];
- }
-
- $c = 0;
- for (keys %averageHash) {
-
- if(scalar @{$averageHash{$_}->{tedr}} == 1) {
- delete $averageHash{$_};
- }
- else {
- $c += (scalar(@{$averageHash{$_}->{tedr}}) - 1);
- }
- }
- $deletedCount += $c;
- $updateCount += keys(%averageHash);
-
- my ($id,$iu) = (0,0);
- my ($kd,$ku) = (1,1);
- my $thd = ($c <= 2000) ? 100 :
- ($c <= 30000) ? 1000 :
- 10000;
- my $thu = ((keys %averageHash) <= 2000) ? 100 :
- ((keys %averageHash) <= 30000) ? 1000 :
- 10000;
-
- Log3 ($name, 3, "DbRep $name - reduceLog (daily-average) updating ".(keys %averageHash).", deleting $c records of day: $processingDay") if(keys %averageHash);
-
- for my $reading (keys %averageHash) {
- $average = sprintf('%.3f', $averageHash{$reading}->{sum}/scalar(@{$averageHash{$reading}->{tedr}}));
- $lastUpdH = pop @{$averageHash{$reading}->{tedr}};
-
- for (@{$averageHash{$reading}->{tedr}}) {
- Log3 ($name, 5, "DbRep $name - DELETE FROM $table WHERE DEVICE='$_->[2]' AND READING='$_->[3]' AND TIMESTAMP='$_->[0]'");
-
- $sth_delD->execute(($_->[2], $_->[3], $_->[0]));
-
- $id++;
- if($id == $thd) {
- my $prog = $kd * $id;
- Log3 ($name, 3, "DbRep $name - reduceLog (daily-average) deleting progress of day: $processingDay is: $prog");
- $id = 0;
- $kd++;
- }
- }
-
- Log3 ($name, 4, "DbRep $name - UPDATE $table SET TIMESTAMP=$averageHash{$reading}->{date} 12:00:00, EVENT='rl_av_d', VALUE=$average WHERE (DEVICE=$lastUpdH->[2]) AND (READING=$lastUpdH->[3]) AND (TIMESTAMP=$lastUpdH->[0])");
-
- $sth_updD->execute(($averageHash{$reading}->{date}." 12:00:00", 'rl_av_d', $average, $lastUpdH->[2], $lastUpdH->[3], $lastUpdH->[0]));
-
- $iu++;
- if($iu == $thu) {
- my $prog = $ku * $id;
- Log3 ($name, 3, "DbRep $name - reduceLog (daily-average) updating progress of day: $processingDay is: $prog");
- $iu = 0;
- $ku++;
- }
- }
+ if ($mode =~ /=day/i && scalar @updateDay) {
+
+ $params = {
+ name => $name,
+ dbh => $dbh,
+ sth_delD => $sth_delD,
+ sth_updD => $sth_updD,
+ mode => $mode,
+ mstr => $mstr,
+ table => $table,
+ updateDayref => \@updateDay,
+ deletedCountref => \$deletedCount,
+ updateCountref => \$updateCount,
+ processingDay => $processingDay,
+ ndp => $ndp
};
-
- if ($@) {
- Log3 ($name, 3, "DbRep $name - reduceLog average=day ! FAILED ! for day $processingDay");
- eval {$dbh->rollback() if(!$dbh->{AutoCommit});};
- if ($@) {
- $err = encode_base64($@, "");
- Log3 ($name, 2, "DbRep $name - DbRep_reduceLog - $@");
- }
- }
- else {
- eval {$dbh->commit() if(!$dbh->{AutoCommit});};
- if ($@) {
- $err = encode_base64($@, "");
- Log3 ($name, 2, "DbRep $name - DbRep_reduceLog - $@");
- }
- }
-
- $dbh->{RaiseError} = 0;
- $dbh->{PrintError} = 1;
+
+ $err = _DbRep_rl_updateDay ($params);
+ return "$name|$err" if ($err);
+
}
-
- %averageHash = ();
+
%hourlyKnown = ();
- @averageUpd = ();
- @averageUpdD = ();
+ @updateHour = ();
+ @updateDay = ();
$currentHour = 99;
}
$currentDay = $day;
}
+ ## Füllen Arrays und Hashes
+ ############################
+
if ($hour != $currentHour) { # forget records from last hour, but remember these for average
- if ($avgstring =~ /average/i && keys(%hourlyKnown)) {
- push(@averageUpd, {%hourlyKnown});
+ if ($mode =~ /average|max|min|sum/i && keys(%hourlyKnown)) {
+ push(@updateHour, {%hourlyKnown});
}
%hourlyKnown = ();
$currentHour = $hour;
}
-
- if (defined $hourlyKnown{$row->[1].$row->[3]}) { # remember first readings for device per h, other can be deleted
+
+ if (defined $hourlyKnown{$device.$reading}) { # das erste reading pro device und Stunde wird nicht in @dayRows (zum Löschen) gespeichert, die anderen können gelöscht werden
push(@dayRows, [@$row]);
- if ($avgstring =~ /average/i &&
- defined($row->[4]) &&
- $row->[4] =~ /^-?(?:\d+(?:\.\d*)?|\.\d+)$/ &&
- $hourlyKnown{$row->[1].$row->[3]}->[0]) {
+ if ($mode =~ /average|max|min|sum/i &&
+ defined($value) &&
+ DbRep_IsNumeric ($value) &&
+ $hourlyKnown{$device.$reading}->[0]) {
- if ($hourlyKnown{$row->[1].$row->[3]}->[0]) {
- push(@{$hourlyKnown{$row->[1].$row->[3]}->[4]}, $row->[4]);
+ if ($hourlyKnown{$device.$reading}->[0]) {
+ push(@{$hourlyKnown{$device.$reading}->[4]}, $value);
}
}
}
else {
- $exclude = 0;
+ my $exclude = 0;
- for (@excludeRegex) {
- $exclude = 1 if("$row->[1]:$row->[3]" =~ /^$_$/);
+ for my $exreg (@excludeRegex) {
+ $exclude = 1 if("$device:$reading" =~ /^$exreg$/);
}
if ($exclude) {
$excludeCount++ if($day != 00);
}
else {
- $hourlyKnown{$row->[1].$row->[3]} = (defined($row->[4]) && $row->[4] =~ /^-?(?:\d+(?:\.\d*)?|\.\d+)$/) ? [$row->[0],$row->[1],$row->[2],$row->[3],[$row->[4]]] : [0];
+ $hourlyKnown{$device.$reading} = DbRep_IsNumeric ($value) ?
+ [$ts,$device,$row->[2],$reading,[$value]] :
+ [0];
}
}
- $processingDay = (split(' ',$row->[0]))[0];
+
+ $processingDay = (split ' ', $ts)[0]; # $ts = Datum (YYYY-MM-DD hh:mm:ss)
- } while ($day != 00); # die do...while-Anweisung überprüft die Bedingung am Ende jeder Iteration.
+ } while ($day != 00); # die do...while-Anweisung überprüft die Bedingung am Ende jeder Iteration.
######################################### Ende
- $brt = time() - $startTime;
+ my $brt = time() - $startTime;
my $result = "Rows processed: $rowCount, deleted: $deletedCount"
- .($avgstring =~ /average/i ? ", updated: $updateCount" : '')
- .($excludeCount ? ", excluded: $excludeCount" : '');
+ .($mode =~ /average|max|min|sum/i ? ", updated: $updateCount" : '')
+ .($excludeCount ? ", excluded: $excludeCount" : '');
Log3 ($name, 3, "DbRep $name - reduceLog finished. $result");
- $ret = "reduceLog finished. $result";
-
$dbh->disconnect();
- $ret = encode_base64($ret, "");
+ my $ret = encode_base64("reduceLog finished. $result", "");
Log3 ($name, 5, "DbRep $name -> DbRep_reduceLogNbl finished");
return "$name|$err|$ret|$brt";
}
+####################################################################################################
+# reduceLog alle im @dayRows Array enthaltene DB Einträge löschen
+####################################################################################################
+sub _DbRep_rl_deleteDayRows {
+ my $paref = shift;
+ my $name = $paref->{name};
+ my $dbh = $paref->{dbh};
+ my $sth_del = $paref->{sth_del};
+ my $table = $paref->{table};
+ my $dayRowsref = $paref->{dayRowsref};
+ my $deletedCountref = $paref->{deletedCountref};
+ my $processingDay = $paref->{processingDay};
+
+ my $err = q{};
+
+ my @dayRows = @{$dayRowsref};
+
+ #Log3 ($name, 3, "DbRep $name - content dayRows Array:\n".Dumper @dayRows);
+
+ my $c = 0;
+
+ for my $delRow (@dayRows) {
+ $c++;
+ }
+
+ if($c) {
+ ${$deletedCountref} += $c;
+
+ Log3 ($name, 3, "DbRep $name - reduceLog deleting $c records of day: $processingDay");
+
+ $err = DbRep_beginDatabaseTransaction ($name, $dbh);
+ return $err if ($err);
+
+ eval {
+ my $i = 0;
+ my $k = 1;
+ my $th = _DbRep_rl_logThreshold ($#dayRows);
+
+ for my $delRow (@dayRows) {
+ Log3 ($name, 5, "DbRep $name - DELETE FROM $table WHERE (DEVICE=$delRow->[1]) AND (READING=$delRow->[3]) AND (TIMESTAMP=$delRow->[0]) AND (VALUE=$delRow->[4])");
+
+ $sth_del->execute(($delRow->[1], $delRow->[3], $delRow->[0], $delRow->[4]));
+ $i++;
+
+ my $params = {
+ name => $name,
+ logtxt => "deletion",
+ iref => \$i,
+ kref => \$k,
+ th => $th,
+ processingDay => $processingDay
+ };
+
+ _DbRep_rl_logProgress ($params);
+ }
+ 1;
+ }
+ or do {
+ $err = encode_base64($@, "");
+
+ Log3 ($name, 2, "DbRep $name - ERROR - reduceLog failed for day $processingDay: $@");
+
+ DbRep_rollbackOnly ($name, $dbh);
+ return $err;
+ };
+
+ $err = DbRep_commitOnly ($name, $dbh);
+ return $err if ($err);
+ }
+
+return $err;
+}
+
+####################################################################################################
+# reduceLog
+# Stundenupdates vornehmen und @updateDay füllen bei
+# $mode = *=day
+####################################################################################################
+sub _DbRep_rl_updateHour {
+ my $paref = shift;
+ my $name = $paref->{name};
+ my $dbh = $paref->{dbh};
+ my $sth_upd = $paref->{sth_upd};
+ my $mode = $paref->{mode};
+ my $mstr = $paref->{mstr};
+ my $table = $paref->{table};
+ my $hourlyKnownref = $paref->{hourlyKnownref};
+ my $updateHourref = $paref->{updateHourref};
+ my $updateDayref = $paref->{updateDayref};
+ my $updateCountref = $paref->{updateCountref};
+ my $processingDay = $paref->{processingDay};
+ my $ndp = $paref->{ndp};
+
+ my $err = q{};
+
+ #Log3 ($name, 3, "DbRep $name - content hourlyKnown Hash:\n".Dumper %$hourlyKnownref);
+
+ push(@$updateHourref, {%$hourlyKnownref});
+
+ my $c = 0;
+
+ for my $hourHash (@$updateHourref) { # Only count for logging...
+ for my $hourKey (keys %$hourHash) {
+ $c++ if ($hourHash->{$hourKey}->[0] && scalar @{$hourHash->{$hourKey}->[4]} > 1);
+ }
+ }
+
+ ${$updateCountref} += $c;
+
+ if($c) {
+ Log3 ($name, 3, "DbRep $name - reduceLog (hourly-$mstr) updating $c records of day: $processingDay");
+
+ $err = DbRep_beginDatabaseTransaction ($name, $dbh);
+ return $err if ($err);
+ }
+
+ my ($params, $value);
+ my $i = 0;
+ my $k = 1;
+ my $th = _DbRep_rl_logThreshold ($c);
+
+ my $event = $mstr eq 'average' ? 'rl_av_h' :
+ $mstr eq 'max' ? 'rl_max_h' :
+ $mstr eq 'min' ? 'rl_min_h' :
+ $mstr eq 'sum' ? 'rl_sum_h' :
+ 'rl_h';
+
+ my $updminutes = $mstr eq 'average' ? '30:00' :
+ $mstr eq 'max' ? '59:59' :
+ $mstr eq 'min' ? '00:01' :
+ $mstr eq 'sum' ? '00:00' :
+ '00:00';
+
+ #Log3 ($name, 3, "DbRep $name - content updateHour Array:\n".Dumper @$updateHourref);
+
+ $paref->{updminutes} = $updminutes;
+ $paref->{event} = $event;
+ $paref->{th} = $th;
+ $paref->{iref} = \$i;
+ $paref->{kref} = \$k;
+
+ for my $hourHash (@$updateHourref) {
+
+ for my $hourKey (keys %$hourHash) {
+
+ next if (!$hourHash->{$hourKey}->[0]);
+ my ($updDate,$updHour) = $hourHash->{$hourKey}->[0] =~ /(.*\d+)\s(\d{2}):/;
+
+ $paref->{updDate} = $updDate;
+ $paref->{updHour} = $updHour;
+ $paref->{timestamp} = $hourHash->{$hourKey}->[0];
+ $paref->{device} = $hourHash->{$hourKey}->[1];
+ $paref->{reading} = $hourHash->{$hourKey}->[3];
+ $paref->{oldvalue} = $hourHash->{$hourKey}->[4]->[0];
+
+ if (scalar @{$hourHash->{$hourKey}->[4]} > 1) { # wahr wenn reading hat mehrere Datensätze diese Stunde
+
+ $i++;
+
+ $paref->{hourHashKeyRef} = $hourHash->{$hourKey}->[4];
+
+ if ($mstr eq 'average') { # Berechnung Average
+ $value = __DbRep_rl_calcAverageHourly ($paref);
+ }
+ elsif ($mstr eq 'max') { # Berechnung Max
+ $value = __DbRep_rl_calcMaxHourly ($paref);
+ }
+ elsif ($mstr eq 'min') { # Berechnung Min
+ $value = __DbRep_rl_calcMinHourly ($paref);
+ }
+ elsif ($mstr eq 'sum') { # Berechnung Summary
+ $value = __DbRep_rl_calcSumHourly ($paref);
+ }
+
+ $paref->{logtxt} = "(hourly-$mstr) updating";
+ $paref->{newvalue} = $value;
+
+ $err = __DbRep_rl_updateHourDatabase ($paref);
+
+ if ($err) {
+ Log3 ($name, 2, "DbRep $name - ERROR - reduceLog $mstr failed for day $processingDay: $err");
+ $err = encode_base64($err, "");
+
+ DbRep_rollbackOnly ($name, $dbh);
+ return $err;
+ }
+ }
+ else {
+ __DbRep_rl_onlyFillDayArray ($paref);
+ }
+ }
+ }
+
+ if($c) {
+ $err = DbRep_commitOnly ($name, $dbh);
+ return $err if ($err);
+ }
+
+return $err;
+}
+
+####################################################################################################
+# reduceLog stündlichen average Wert berechnen
+####################################################################################################
+sub __DbRep_rl_calcAverageHourly {
+ my $paref = shift;
+ my $name = $paref->{name};
+ my $hourHashKeyRef = $paref->{hourHashKeyRef};
+ my $ndp = $paref->{ndp};
+
+ my $sum = 0;
+
+ for my $val (@{$hourHashKeyRef}) {
+ $sum += $val;
+ }
+
+ my $value = sprintf "%.${ndp}f", $sum / scalar @{$hourHashKeyRef};
+
+return $value;
+}
+
+####################################################################################################
+# reduceLog stündlichen Max Wert berechnen
+####################################################################################################
+sub __DbRep_rl_calcMaxHourly {
+ my $paref = shift;
+ my $name = $paref->{name};
+ my $hourHashKeyRef = $paref->{hourHashKeyRef};
+ my $ndp = $paref->{ndp};
+
+ my $max;
+
+ for my $val (@{$hourHashKeyRef}) {
+ if (!defined $max) {
+ $max = $val;
+ }
+ else {
+ $max = $val if ($val > $max);
+ }
+ }
+
+ my $value = sprintf "%.${ndp}f", $max;
+
+return $value;
+}
+
+####################################################################################################
+# reduceLog stündlichen Min Wert berechnen
+####################################################################################################
+sub __DbRep_rl_calcMinHourly {
+ my $paref = shift;
+ my $name = $paref->{name};
+ my $hourHashKeyRef = $paref->{hourHashKeyRef};
+ my $ndp = $paref->{ndp};
+
+ my $min;
+
+ for my $val (@{$hourHashKeyRef}) {
+ if (!defined $min) {
+ $min = $val;
+ }
+ else {
+ $min = $val if ($val < $min);
+ }
+ }
+
+ my $value = sprintf "%.${ndp}f", $min;
+
+return $value;
+}
+
+####################################################################################################
+# reduceLog stündlichen summary Wert berechnen
+####################################################################################################
+sub __DbRep_rl_calcSumHourly {
+ my $paref = shift;
+ my $name = $paref->{name};
+ my $hourHashKeyRef = $paref->{hourHashKeyRef};
+ my $ndp = $paref->{ndp};
+
+ my $sum = 0;
+
+ for my $val (@{$hourHashKeyRef}) {
+ $sum += $val;
+ }
+
+ my $value = sprintf "%.${ndp}f", $sum;
+
+return $value;
+}
+
+################################################################
+# reduceLog Stundenupdate Datenbank und
+# füllen Tages Update Array
+################################################################
+sub __DbRep_rl_updateHourDatabase {
+ my $paref = shift;
+ my $name = $paref->{name};
+ my $mode = $paref->{mode};
+ my $table = $paref->{table};
+ my $sth_upd = $paref->{sth_upd};
+ my $updateDayref = $paref->{updateDayref};
+ my $updDate = $paref->{updDate};
+ my $updHour = $paref->{updHour};
+ my $updminutes = $paref->{updminutes};
+ my $event = $paref->{event};
+ my $newvalue = $paref->{newvalue};
+ my $device = $paref->{device};
+ my $reading = $paref->{reading};
+ my $timestamp = $paref->{timestamp};
+ my $oldvalue = $paref->{oldvalue};
+
+ Log3 ($name, 4, "DbRep $name - UPDATE $table SET TIMESTAMP=$updDate $updHour:$updminutes, EVENT=$event, VALUE=$newvalue WHERE DEVICE=$device AND READING=$reading AND TIMESTAMP=$timestamp AND VALUE=$oldvalue");
+
+ eval { $sth_upd->execute("$updDate $updHour:$updminutes", $event, $newvalue, $device, $reading, $timestamp, $oldvalue);
+ }
+ or do { return $@;
+ };
+
+ _DbRep_rl_logProgress ($paref);
+
+ if ($mode =~ /=day/i) {
+ push(@$updateDayref, ["$updDate $updHour:$updminutes", $event, $newvalue, $device, $reading, $updDate]);
+ }
+
+return;
+}
+
+################################################################
+# reduceLog Tages Array füllen
+################################################################
+sub __DbRep_rl_onlyFillDayArray {
+ my $paref = shift;
+ my $mode = $paref->{mode};
+ my $updateDayref = $paref->{updateDayref};
+ my $timestamp = $paref->{timestamp};
+ my $event = $paref->{event};
+ my $oldvalue = $paref->{oldvalue};
+ my $device = $paref->{device};
+ my $reading = $paref->{reading};
+ my $updDate = $paref->{updDate};
+
+ if ($mode =~ /=day/i) {
+ push(@$updateDayref, [$timestamp, $event, $oldvalue, $device, $reading, $updDate]);
+ }
+
+return;
+}
+
+####################################################################################################
+# reduceLog Tagesupdates vornehmen
+####################################################################################################
+sub _DbRep_rl_updateDay {
+ my $paref = shift;
+ my $name = $paref->{name};
+ my $dbh = $paref->{dbh};
+ my $sth_delD = $paref->{sth_delD};
+ my $sth_updD = $paref->{sth_updD};
+ my $mode = $paref->{mode};
+ my $mstr = $paref->{mstr};
+ my $table = $paref->{table};
+ my $updateDayref = $paref->{updateDayref};
+ my $deletedCountref = $paref->{deletedCountref};
+ my $updateCountref = $paref->{updateCountref};
+ my $processingDay = $paref->{processingDay};
+ my $ndp = $paref->{ndp};
+
+ my $err = q{};
+
+ #Log3 ($name, 3, "DbRep $name - content updateDay Array:\n".Dumper @$updateDayref);
+
+ my %updateHash;
+
+ for my $row (@$updateDayref) {
+ $updateHash{$row->[3].$row->[4]}->{date} = $row->[5];
+ push @{$updateHash{$row->[3].$row->[4]}->{tedr}}, [$row->[0], $row->[1], $row->[3], $row->[4]]; # tedr -> time, event, device, reading
+
+ if ($mstr eq 'average') { # Day Average
+ $updateHash{$row->[3].$row->[4]}->{sum} += $row->[2]; # Summe aller Werte
+ }
+ elsif ($mstr eq 'max') { # Day Max
+ if (!defined $updateHash{$row->[3].$row->[4]}->{max}) {
+ $updateHash{$row->[3].$row->[4]}->{max} = $row->[2];
+ }
+ else {
+ $updateHash{$row->[3].$row->[4]}->{max} = $row->[2] if ($row->[2] > $updateHash{$row->[3].$row->[4]}->{max});
+ }
+ }
+ elsif ($mstr eq 'min') { # Day Min
+ if (!defined $updateHash{$row->[3].$row->[4]}->{min}) {
+ $updateHash{$row->[3].$row->[4]}->{min} = $row->[2];
+ }
+ else {
+ $updateHash{$row->[3].$row->[4]}->{min} = $row->[2] if ($row->[2] < $updateHash{$row->[3].$row->[4]}->{min});
+ }
+ }
+ elsif ($mstr eq 'sum') { # Day Summary
+ $updateHash{$row->[3].$row->[4]}->{sum} += $row->[2]; # Summe aller Werte
+ }
+ }
+
+ my $c = 0;
+
+ for my $key (keys %updateHash) {
+ if(scalar @{$updateHash{$key}->{tedr}} == 1) {
+ delete $updateHash{$key};
+ }
+ else {
+ $c += (scalar @{$updateHash{$key}->{tedr}} - 1);
+ }
+ }
+
+ ${$deletedCountref} += $c;
+ ${$updateCountref} += keys %updateHash;
+
+ my ($params, $value);
+ my ($id,$iu) = (0,0);
+ my ($kd,$ku) = (1,1);
+ my $thd = _DbRep_rl_logThreshold ($c);
+ my $thu = _DbRep_rl_logThreshold (scalar keys %updateHash);
+
+ my $event = $mstr eq 'average' ? 'rl_av_d' :
+ $mstr eq 'max' ? 'rl_max_d' :
+ $mstr eq 'min' ? 'rl_min_d' :
+ $mstr eq 'sum' ? 'rl_sum_d' :
+ 'rl_d';
+
+ my $time = $mstr eq 'average' ? '12:00:00' :
+ $mstr eq 'max' ? '23:59:59' :
+ $mstr eq 'min' ? '00:00:01' :
+ $mstr eq 'sum' ? '12:00:00' :
+ '00:00:00';
+
+ $paref->{time} = $time;
+ $paref->{event} = $event;
+
+ if(keys %updateHash) {
+ Log3 ($name, 3, "DbRep $name - reduceLog (daily-$mstr) updating ".(keys %updateHash).", deleting $c records of day: $processingDay");
+
+ $err = DbRep_beginDatabaseTransaction ($name, $dbh);
+ return $err if ($err);
+ }
+
+ for my $uhk (keys %updateHash) {
+
+ if ($mstr eq 'average') { # Day Average
+ $value = sprintf "%.${ndp}f", $updateHash{$uhk}->{sum} / scalar @{$updateHash{$uhk}->{tedr}};
+ }
+ elsif ($mstr eq 'max') { # Day Max
+ $value = sprintf "%.${ndp}f", $updateHash{$uhk}->{max};
+ }
+ elsif ($mstr eq 'min') { # Day Min
+ $value = sprintf "%.${ndp}f", $updateHash{$uhk}->{min};
+ }
+ elsif ($mstr eq 'sum') { # Day Summary
+ $value = sprintf "%.${ndp}f", $updateHash{$uhk}->{sum};
+ }
+
+ my $lastUpdH = pop @{$updateHash{$uhk}->{tedr}};
+
+ for my $tedr (@{$updateHash{$uhk}->{tedr}}) {
+ $id++;
+
+ $paref->{logtxt} = "(daily-$mstr) deleting";
+ $paref->{iref} = \$id;
+ $paref->{kref} = \$kd;
+ $paref->{th} = $thd;
+ $paref->{timestamp} = $tedr->[0];
+ $paref->{device} = $tedr->[2];
+ $paref->{reading} = $tedr->[3];
+
+ $err = __DbRep_rl_deleteDayDatabase ($paref);
+
+ if ($err) {
+ Log3 ($name, 3, "DbRep $name - ERROR - reduceLog $mstr=day failed for day $processingDay: $err");
+ $err = encode_base64($err, "");
+
+ DbRep_rollbackOnly ($name, $dbh);
+ return $err;
+ }
+ }
+
+ $iu++;
+
+ $paref->{logtxt} = "(daily-$mstr) updating";
+ $paref->{iref} = \$iu;
+ $paref->{kref} = \$ku;
+ $paref->{th} = $thu;
+ $paref->{date} = $updateHash{$uhk}->{date};
+ $paref->{timestamp} = $lastUpdH->[0];
+ $paref->{device} = $lastUpdH->[2];
+ $paref->{reading} = $lastUpdH->[3];
+ $paref->{value} = $value;
+
+ $err = __DbRep_rl_updateDayDatabase ($paref);
+
+ if ($err) {
+ Log3 ($name, 3, "DbRep $name - ERROR - reduceLog $mstr=day failed for day $processingDay: $err");
+ $err = encode_base64($err, "");
+
+ DbRep_rollbackOnly ($name, $dbh);
+ return $err;
+ }
+ }
+
+ if(keys %updateHash) {
+ $err = DbRep_commitOnly ($name, $dbh);
+ return $err if ($err);
+ }
+
+return $err;
+}
+
+################################################################
+# reduceLog Tageswerte löschen
+################################################################
+sub __DbRep_rl_deleteDayDatabase {
+ my $paref = shift;
+ my $name = $paref->{name};
+ my $table = $paref->{table};
+ my $sth_delD = $paref->{sth_delD};
+ my $device = $paref->{device};
+ my $reading = $paref->{reading};
+ my $timestamp = $paref->{timestamp};
+
+ Log3 ($name, 4, "DbRep $name - DELETE FROM $table WHERE DEVICE='$device' AND READING='$reading' AND TIMESTAMP='$timestamp'");
+
+ eval { $sth_delD->execute($device, $reading, $timestamp);
+ }
+ or do { return $@;
+ };
+
+ _DbRep_rl_logProgress ($paref);
+
+return;
+}
+
+################################################################
+# reduceLog Tageswerte updaten
+################################################################
+sub __DbRep_rl_updateDayDatabase {
+ my $paref = shift;
+ my $name = $paref->{name};
+ my $table = $paref->{table};
+ my $sth_updD = $paref->{sth_updD};
+ my $event = $paref->{event};
+ my $device = $paref->{device};
+ my $reading = $paref->{reading};
+ my $value = $paref->{value};
+ my $date = $paref->{date};
+ my $time = $paref->{time};
+ my $timestamp = $paref->{timestamp};
+
+ Log3 ($name, 4, "DbRep $name - UPDATE $table SET TIMESTAMP=$date $time, EVENT=$event, VALUE=$value WHERE (DEVICE=$device) AND (READING=$reading) AND (TIMESTAMP=$timestamp)");
+
+ eval { $sth_updD->execute("$date $time", $event, $value, $device, $reading, $timestamp);
+ }
+ or do { return $@;
+ };
+
+ _DbRep_rl_logProgress ($paref);
+
+return;
+}
+
+####################################################################################################
+# reduceLog Grenzen für Logausgabe abhängig von der Zeilenanzahl
+####################################################################################################
+sub _DbRep_rl_logThreshold {
+ my $rn = shift;
+
+ my $th = ($rn <= 2000) ? 100 :
+ ($rn <= 30000) ? 1000 :
+ 10000;
+
+return $th;
+}
+
+################################################################
+# reduceLog Logausgabe Fortschritt
+################################################################
+sub _DbRep_rl_logProgress {
+ my $paref = shift;
+ my $name = $paref->{name};
+ my $logtxt = $paref->{logtxt};
+ my $iref = $paref->{iref};
+ my $kref = $paref->{kref};
+ my $th = $paref->{th};
+ my $processingDay = $paref->{processingDay};
+
+ if(${$iref} == $th) {
+ my $prog = ${$kref} * ${$iref};
+
+ Log3 ($name, 3, "DbRep $name - reduceLog $logtxt progress of day: $processingDay is: $prog");
+
+ ${$iref} = 0;
+ ${$kref}++;
+ }
+
+return;
+}
+
####################################################################################################
# reduceLog non-blocking Rückkehrfunktion
####################################################################################################
@@ -10323,6 +10748,32 @@ sub DbRep_prepareOnly {
return ($err, $sth);
}
+####################################################################################################
+# nur SQL prepare Cached
+# return $sth bei Erfolg
+####################################################################################################
+sub DbRep_prepareCachedOnly {
+ my $name = shift;
+ my $dbh = shift;
+ my $sql = shift;
+ my $info = shift // "SQL prepare cached: $sql";
+
+ my $err = q{};
+
+ my $sth;
+
+ Log3 ($name, 4, "DbRep $name - $info");
+
+ eval{ $sth = $dbh->prepare_cached($sql);
+ }
+ or do { $err = encode_base64($@,"");
+ Log3 ($name, 2, "DbRep $name - ERROR - $@");
+ $dbh->disconnect;
+ };
+
+return ($err, $sth);
+}
+
####################################################################################################
# SQL Query evaluieren und Return-String (bei Error in Verarbeitung) und $sth-String
# bei Erfolg
@@ -10431,6 +10882,34 @@ sub DbRep_commitOnly {
return $err;
}
+####################################################################################################
+# nur Datenbank "rollback"
+####################################################################################################
+sub DbRep_rollbackOnly {
+ my $name = shift;
+ my $dbh = shift;
+ my $info = shift // "transaction rollback";
+
+ my $err = q{};
+
+ eval{ if(!$dbh->{AutoCommit}) {
+ $dbh->rollback();
+ Log3 ($name, 4, "DbRep $name - $info");
+ 1;
+ }
+ else {
+ Log3 ($name, 4, "DbRep $name - data auto rollback");
+ 1;
+ }
+ }
+ or do { $err = encode_base64($@,"");
+ Log3 ($name, 2, "DbRep $name - ERROR - $@");
+ $dbh->disconnect;
+ };
+
+return $err;
+}
+
####################################################################################################
# Whitespace am Anfang / Ende eines Strings entfernen
####################################################################################################
@@ -10812,7 +11291,7 @@ sub DbRep_normRelTime {
$fdopt = ($aval =~ /FullDay/x && $toth >= 86400) ? 1 : 0; # ist FullDay Option gesetzt UND Zeitdiff >= 1 Tag ?
}
- $fdopt = 1 if($hash->{LASTCMD} =~ /reduceLog/x); # reduceLog -> FullDay Option damit der ganze Tag berücksichtigt wird
+ $fdopt = 1 if($hash->{LASTCMD} =~ /reduceLog.*=day/x); # reduceLog -> FullDay Option damit der ganze Tag berücksichtigt wird
Log3($name, 4, "DbRep $name - FullDay option: $fdopt");
@@ -10832,13 +11311,11 @@ sub DbRep_corrRelTime {
my ($dsec,$dmin,$dhour,$dmday,$dmon,$dyear,$dwday,$dyday,$disdst,$fyear,$cyear);
(undef,undef,undef,undef,undef,$cyear,undef,undef,$isdst) = localtime(time); # aktuelles Jahr, Sommer/Winterzeit
- if($tdtn) {
- # timeDiffToNow
+ if($tdtn) { # timeDiffToNow
($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,undef) = localtime(time); # Istzeit
($dsec,$dmin,$dhour,$dmday,$dmon,$dyear,$dwday,$dyday,$disdst) = localtime(time-$tim); # Istzeit abzgl. Differenzzeit = Selektionsbeginnzeit
}
- else {
- # timeOlderThan
+ else { # timeOlderThan
($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$disdst) = localtime(time-$tim); # Berechnung Selektionsendezeit
my $mints = $hash->{HELPER}{MINTS}?$hash->{HELPER}{MINTS}:"1970-01-01 01:00:00"; # Selektionsstartzeit
my ($yyyy1, $mm1, $dd1, $hh1, $min1, $sec1) = ($mints =~ /(\d+)-(\d+)-(\d+) (\d+):(\d+):(\d+)/);
@@ -12466,6 +12943,21 @@ sub DbRep_numval {
return $val;
}
+################################################################
+# Prüfung auf numerischen Wert (vorzeichenbehaftet)
+################################################################
+sub DbRep_IsNumeric {
+ my $val = shift // q{empty};
+
+ my $ret = 0;
+
+ if($val =~ /^-?(?:\d+(?:\.\d*)?|\.\d+)$/xs) {
+ $ret = 1;
+ }
+
+return $ret;
+}
+
################################################################
# entfernt führende Mullen einer Zahl
################################################################
@@ -14118,23 +14610,37 @@ return;
-
reduceLog [<no>[:<nn>]] [average[=day]] [EXCLUDE=device1:reading1,device2:reading2,...] [INCLUDE=device:reading]
+ reduceLog [<no>[:<nn>]] [mode] [EXCLUDE=device1:reading1,device2:reading2,...] [INCLUDE=device:reading]
Reduces historical data sets.
- Method without option specification
+ Operation without specifying command line operators
- The data within the time limits defined by the time.* attributes will be
- reduced to one entry (the first) per hour per device & reading.
+ The data is cleaned within the time limits defined by the time.*-attributes.
At least one of the time.* attributes must be set (see table below).
- The FullDay option (full days are always selected) is used implicitly.
- The respective missing time delimitation is calculated by the module in this case.
+ The respective missing time accrual is determined by the module in this case.
+ The working mode is determined by the optional specification of mode:
- By optionally specifying average, not only the database will be cleaned up, but
- all numeric values of an hour are reduced to a single average value.
- With the option average=day, all numeric values of a day are reduced to a single
- average value (implies 'average').
-
+
+
+
+ without specification of mode | : the data is reduced to the first entry per hour per device & reading |
+ average | : numerical values are reduced to an average value per hour per device & reading, otherwise as without mode |
+ average=day | : numeric values are reduced to one mean value per day per device & reading, otherwise as without mode |
+ | The FullDay option (full days are always selected) is used implicitly. |
+ max | : numeric values are reduced to the maximum value per hour per device & reading, otherwise as without mode |
+ max=day | : numeric values are reduced to the maximum value per day per device & reading, otherwise as without mode |
+ | The FullDay option (full days are always selected) is used implicitly. |
+ min | : numeric values are reduced to the minimum value per hour per device & reading, otherwise as without mode |
+ min=day | : numeric values are reduced to the minimum value per day per device & reading, otherwise as without mode |
+ | The FullDay option (full days are always selected) is used implicitly. |
+ sum | : numeric values are reduced to the sum per hour per Device & Reading, otherwise as without mode |
+ sum=day | : numeric values are reduced to the sum per day per Device & Reading, otherwise as without mode |
+ | The FullDay option (full days are always selected) is used implicitly. |
+
+
+
+
With the attributes device and reading the data records to be considered can be included
or be excluded. Both restrictions reduce the selected data and reduce the
@@ -14146,14 +14652,15 @@ return;
- executeBeforeProc | : execution of FHEM command (or Perl-routine) before reducelog |
- executeAfterProc | : execution of FHEM command (or Perl-routine) after reducelog |
- device | : include or exclude <device> for selection |
- reading | : include or exclude <reading> for selection |
- timeOlderThan | : records older than this attribute will be reduced |
- timestamp_end | : records older than this attribute will be reduced |
- timeDiffToNow | : records newer than this attribute will be reduced |
- timestamp_begin | : records newer than this attribute will be reduced |
+ executeBeforeProc | : execution of FHEM command (or Perl-routine) before reducelog |
+ executeAfterProc | : execution of FHEM command (or Perl-routine) after reducelog |
+ device | : include or exclude <device> for selection |
+ reading | : include or exclude <reading> for selection |
+ numDecimalPlaces | : defines the number of decimal places for numeric result values |
+ timeOlderThan | : records older than this attribute will be reduced |
+ timestamp_end | : records older than this attribute will be reduced |
+ timeDiffToNow | : records newer than this attribute will be reduced |
+ timestamp_begin | : records newer than this attribute will be reduced |
valueFilter | : an additional REGEXP to control the record selection. The REGEXP is applied to the database field 'VALUE'. |
@@ -14190,14 +14697,14 @@ return;
- Method with option specification
+ Operation with specification of command line operators
- Records older than <no> days and (optionally) newer than
- <nn> days are considered.
- Specifying average not only cleans up the database, but also
- all numerical values of an hour are reduced to a single mean value.
- With the option average=day all numerical values of a day are reduced to a single
- Average value reduced (implies 'average').
+ Es werden Datensätze berücksichtigt die älter sind als <no> Tage und (optional) neuer sind als
+ <nn> Tage.
+ Records are considered that are older than <no> days and (optionally) newer than
+ <nn> days.
+ The working mode is determined by the optional specification of mode as described above.
+
The additions "EXCLUDE" or "INCLUDE" can be added to exclude or include device/reading combinations in reduceLog
and override the "device" and "reading" attributes, which are ignored in this case.
@@ -14214,10 +14721,10 @@ return;
Note:
- Although the function itself is designed non-blocking, the assigned DbLog device should
- operated in asynchronous mode to avoid blocking of FHEMWEB (table lock).
- It is also strongly recommended to use the standard INDEX 'Search_Idx' in the table 'history'.
- to put on !
+ Although the function itself is designed non-blocking, the assigned DbLog device should be operated in
+ asynchronous mode to avoid blocking FHEMWEB (table lock).
+ Furthermore it is strongly recommended to create the standard INDEX 'Search_Idx' in the table
+ 'history' !
The processing of this command may take an extremely long time (without INDEX).
@@ -16936,22 +17443,36 @@ return;
- reduceLog [<no>[:<nn>]] [average[=day]] [EXCLUDE=device1:reading1,device2:reading2,...] [INCLUDE=device:reading]
+ reduceLog [<no>[:<nn>]] [mode] [EXCLUDE=device1:reading1,device2:reading2,...] [INCLUDE=device:reading]
Reduziert historische Datensätze.
- Arbeitsweise ohne Optionsangabe
+ Arbeitsweise ohne Angabe von Befehlszeilenoperatoren
- Es werden die Daten innerhalb der durch die time.*-Attribute bestimmten
- Zeitgrenzen auf einen Eintrag (den ersten) pro Stunde je Device & Reading reduziert.
- Es muss mindestens eines der time.*-Attribute gesetzt sein (siehe Tabelle unten).
- Die FullDay-Option (es werden immer volle Tage selektiert) wird impliziert verwendet.
- Die jeweils fehlende Zeitabgrenzung wird in diesem Fall durch das Modul errechnet.
+ Es werden die Daten innerhalb der durch die time.*-Attribute bestimmten Zeitgrenzen bereinigt.
+ Es muss mindestens eines der time.*-Attribute gesetzt sein (siehe Tabelle unten).
+ Die jeweils fehlende Zeitabgrenzung wird in diesem Fall durch das Modul ermittelt.
+ Der Arbeitsmodus wird durch die optionale Angabe von mode bestimmt:
- Durch die optionale Angabe von average wird nicht nur die Datenbank bereinigt, sondern
- alle numerischen Werte einer Stunde werden auf einen einzigen Mittelwert reduziert.
- Mit der Option average=day werden alle numerischen Werte eines Tages auf einen einzigen
- Mittelwert reduziert (impliziert 'average').
+
+
+
+ ohne Angabe von mode | : die Daten werden auf den ersten Eintrag pro Stunde je Device & Reading reduziert |
+ average | : numerische Werte werden auf einen Mittelwert pro Stunde je Device & Reading reduziert, sonst wie ohne mode |
+ average=day | : numerische Werte werden auf einen Mittelwert pro Tag je Device & Reading reduziert, sonst wie ohne mode |
+ | Die FullDay-Option (es werden immer volle Tage selektiert) wird impliziert verwendet. |
+ max | : numerische Werte werden auf den Maximalwert pro Stunde je Device & Reading reduziert, sonst wie ohne mode |
+ max=day | : numerische Werte werden auf den Maximalwert pro Tag je Device & Reading reduziert, sonst wie ohne mode |
+ | Die FullDay-Option (es werden immer volle Tage selektiert) wird impliziert verwendet. |
+ min | : numerische Werte werden auf den Minimalwert pro Stunde je Device & Reading reduziert, sonst wie ohne mode |
+ min=day | : numerische Werte werden auf den Minimalwert pro Tag je Device & Reading reduziert, sonst wie ohne mode |
+ | Die FullDay-Option (es werden immer volle Tage selektiert) wird impliziert verwendet. |
+ sum | : numerische Werte werden auf die Summe pro Stunde je Device & Reading reduziert, sonst wie ohne mode |
+ sum=day | : numerische Werte werden auf die Summe pro Tag je Device & Reading reduziert, sonst wie ohne mode |
+ | Die FullDay-Option (es werden immer volle Tage selektiert) wird impliziert verwendet. |
+
+
+
Mit den Attributen device und reading können die zu berücksichtigenden Datensätze eingeschlossen
bzw. ausgeschlossen werden. Beide Eingrenzungen reduzieren die selektierten Daten und verringern den
@@ -16963,14 +17484,15 @@ return;
- executeBeforeProc | : FHEM Kommando (oder Perl-Routine) vor dem Export ausführen |
- executeAfterProc | : FHEM Kommando (oder Perl-Routine) nach dem Export ausführen |
- device | : einschließen oder ausschließen von Datensätzen die <device> enthalten |
- reading | : einschließen oder ausschließen von Datensätzen die <reading> enthalten |
- timeOlderThan | : es werden Datenbankeinträge älter als dieses Attribut reduziert |
- timestamp_end | : es werden Datenbankeinträge älter als dieses Attribut reduziert |
- timeDiffToNow | : es werden Datenbankeinträge neuer als dieses Attribut reduziert |
- timestamp_begin | : es werden Datenbankeinträge neuer als dieses Attribut reduziert |
+ executeBeforeProc | : FHEM Kommando (oder Perl-Routine) vor dem Export ausführen |
+ executeAfterProc | : FHEM Kommando (oder Perl-Routine) nach dem Export ausführen |
+ device | : einschließen oder ausschließen von Datensätzen die <device> enthalten |
+ reading | : einschließen oder ausschließen von Datensätzen die <reading> enthalten |
+ numDecimalPlaces | : legt die Anzahl der Nachkommastellen bei numerischen Ergebniswerten fest |
+ timeOlderThan | : es werden Datenbankeinträge älter als dieses Attribut reduziert |
+ timestamp_end | : es werden Datenbankeinträge älter als dieses Attribut reduziert |
+ timeDiffToNow | : es werden Datenbankeinträge neuer als dieses Attribut reduziert |
+ timestamp_begin | : es werden Datenbankeinträge neuer als dieses Attribut reduziert |
valueFilter | : ein zusätzliches REGEXP um die Datenselektion zu steuern. Der REGEXP wird auf das Datenbankfeld 'VALUE' angewendet. |
@@ -17009,14 +17531,12 @@ return;
- Arbeitsweise mit Optionsangabe
+ Arbeitsweise mit Angabe von Befehlszeilenoperatoren
Es werden Datensätze berücksichtigt die älter sind als <no> Tage und (optional) neuer sind als
<nn> Tage.
- Durch die Angabe von average wird nicht nur die Datenbank bereinigt, sondern
- alle numerischen Werte einer Stunde werden auf einen einzigen Mittelwert reduziert.
- Mit der Option average=day werden alle numerischen Werte eines Tages auf einen einzigen
- Mittelwert reduziert (impliziert 'average').
+ Der Arbeitsmodus wird durch die optionale Angabe von mode wie oben beschrieben bestimmt.
+
Die Zusätze "EXCLUDE" bzw. "INCLUDE" können ergänzt werden um device/reading Kombinationen in reduceLog auszuschließen
bzw. einzuschließen und überschreiben die Einstellung der Attribute "device" und "reading", die in diesem Fall