2
0
mirror of https://github.com/fhem/fhem-mirror.git synced 2025-02-25 09:55:38 +00:00

93_DbRep: diffValue can operate positive and negative differences, sqlCmd can execute 'describe' statement

git-svn-id: https://svn.fhem.de/fhem/trunk@27391 2b470e98-0d58-463d-a4d8-8e2adae1ed80
This commit is contained in:
nasseeder1 2023-04-03 20:49:13 +00:00
parent 77e69a946d
commit f62cc80a81
2 changed files with 192 additions and 111 deletions

View File

@ -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: diffValue can operate positive and negative differences,
sqlCmd can execute 'describe' statement
- bugfix: 72_FRITZBOX: Fehlerkorrekturen: u.a. fritzLog
feature: set name wakeUpCall ... / Attr disableHostIPv4check
- change: 93_DBLog: Plot Editor - include functions like delta-h, delta-h

View File

@ -59,6 +59,7 @@ no if $] >= 5.017011, warnings => 'experimental::smartmatch';
# Version History intern
my %DbRep_vNotesIntern = (
"8.52.2" => "28.03.2023 diffValue ",
"8.52.1" => "19.03.2023 fix Perl Warnings ",
"8.52.0" => "17.02.2023 get utf8mb4 info by connect db and set connection collation accordingly, new setter migrateCollation ",
"8.51.6" => "11.02.2023 fix execute DbRep_afterproc after generating readings ".
@ -1792,16 +1793,27 @@ sub DbRep_Attr {
delete($attr{$name}{timeOlderThan});
delete($attr{$name}{timeYearPeriod});
}
if ($aName =~ /ftpTimeout|timeout|diffAccept/) {
if ($aName =~ /ftpTimeout|timeout/) {
unless ($aVal =~ /^[0-9]+$/) {
return " The Value of $aName is not valid. Use only figures 0-9 without decimal places !";
}
}
if ($aName =~ /diffAccept/) {
my ($sign, $daval) = DbRep_ExplodeDiffAcc ($aVal);
if (!$daval) {
return " The Value of $aName is not valid. Use only figures 0-9 without decimal places !";
}
}
if ($aName eq "readingNameMap") {
unless ($aVal =~ m/^[A-Za-z\d_\.-]+$/) {
return " Unsupported character in $aName found. Use only A-Z a-z _ . -";
}
}
if ($aName eq "timeDiffToNow") {
unless ($aVal =~ /^[0-9]+$/ || $aVal =~ /^\s*[ydhms]:([\d]+)\s*/ && $aVal !~ /.*,.*/ ) {
return "The Value of \"$aName\" isn't valid. Set simple seconds like \"86400\" or use form like \"y:1 d:10 h:6 m:12 s:20\". Refer to commandref !";
@ -1810,6 +1822,7 @@ sub DbRep_Attr {
delete($attr{$name}{timestamp_end});
delete($attr{$name}{timeYearPeriod});
}
if ($aName eq "timeOlderThan") {
unless ($aVal =~ /^[0-9]+$/ || $aVal =~ /^\s*[ydhms]:([\d]+)\s*/ && $aVal !~ /.*,.*/ ) {
return "The Value of \"$aName\" isn't valid. Set simple seconds like \"86400\" or use form like \"y:1 d:10 h:6 m:12 s:20\". Refer to commandref !";
@ -1818,6 +1831,7 @@ sub DbRep_Attr {
delete($attr{$name}{timestamp_end});
delete($attr{$name}{timeYearPeriod});
}
if ($aName eq "dumpMemlimit" || $aName eq "dumpSpeed") {
unless ($aVal =~ /^[0-9]+$/) {
return "The Value of $aName is not valid. Use only figures 0-9 without decimal places.";
@ -1836,9 +1850,11 @@ sub DbRep_Attr {
}
}
}
if ($aName eq "ftpUse") {
delete($attr{$name}{ftpUseSSL});
}
if ($aName eq "ftpUseSSL") {
delete($attr{$name}{ftpUse});
}
@ -4512,11 +4528,14 @@ sub DbRep_diffval {
$selspec = "TIMESTAMP,VALUE";
}
my $st = [gettimeofday]; # SQL-Startzeit
my @row_array;
my @array;
my $difflimit = AttrVal ($name, 'diffAccept', 20); # legt fest, bis zu welchem Wert Differenzen akzeptiert werden (Ausreißer eliminieren)
my ($sign, $dlim) = DbRep_ExplodeDiffAcc ($difflimit); # $sign -> Vorzeichen (+-)
my $st = [gettimeofday]; # SQL-Startzeit
for my $row (@ts) { # DB-Abfrage zeilenweise für jeden Array-Eintrag
my @a = split("#", $row);
my $runtime_string = $a[0];
@ -4530,10 +4549,10 @@ sub DbRep_diffval {
}
if ($IsTimeSet || $IsAggrSet) {
$sql = DbRep_createSelectSql($hash,$table,$selspec,$device,$reading,"'$runtime_string_first'","'$runtime_string_next'",'ORDER BY TIMESTAMP');
$sql = DbRep_createSelectSql($hash, $table, $selspec, $device , $reading, "'$runtime_string_first'", "'$runtime_string_next'", 'ORDER BY TIMESTAMP');
}
else {
$sql = DbRep_createSelectSql($hash,$table,$selspec,$device,$reading,undef,undef,'ORDER BY TIMESTAMP');
$sql = DbRep_createSelectSql($hash, $table, $selspec, $device, $reading, undef, undef, 'ORDER BY TIMESTAMP');
}
($err, $sth) = DbRep_prepareExecuteQuery ($name, $dbh, $sql);
@ -4549,7 +4568,7 @@ sub DbRep_diffval {
my @sp;
my $dse = 0;
my $vold;
my @sqlite_array;
my @db_array;
for my $row (@array) {
@sp = split("[ \t][ \t]*", $row, 4);
@ -4563,14 +4582,24 @@ sub DbRep_diffval {
next;
}
$dse = $vold && (($vnew-$vold) > 0) ? ($vnew-$vold) : 0;
if (!defined $vold) {
$vold = $vnew;
}
if ($sign =~ /\+-/xs) { # sowohl positive als auch negative Abweichung auswerten
$dse = $vnew - $vold;
}
else {
$dse = ($vnew - $vold) > 0 ? ($vnew - $vold) : 0; # nur positive Abweichung auswerten
}
@sp = $runtime_string." ".$timestamp." ".$vnew." ".$dse."\n";
$vold = $vnew;
push @sqlite_array, @sp;
push @db_array, @sp;
}
@array = @sqlite_array;
@array = @db_array;
}
}
@ -4599,8 +4628,6 @@ sub DbRep_diffval {
$sth->finish;
$dbh->disconnect;
my $difflimit = AttrVal($name, "diffAccept", "20"); # legt fest, bis zu welchem Wert Differenzen akzeptiert werden (Ausreißer eliminieren)
# Berechnung diffValue aus Selektionshash
my %rh = (); # Ergebnishash, wird alle Ergebniszeilen enthalten
my %ch = (); # counthash, enthält die Anzahl der verarbeiteten Datasets pro runtime_string
@ -4638,7 +4665,7 @@ sub DbRep_diffval {
$diff_current = $timestamp." ".$diff; # String ignorierter Zeilen erzeugen
if($diff > $difflimit) {
if(abs $diff > $dlim) {
$rejectstr .= $diff_before." -> ".$diff_current."\n";
}
@ -4646,37 +4673,45 @@ sub DbRep_diffval {
if ($runtime_string eq $lastruntimestring) { # Ergebnishash erzeugen
if ($i == 1) {
$diff_total = $diff ? $diff : 0 if($diff <= $difflimit);
if(abs $diff <= $dlim) {
$diff_total = $diff;
}
$rh{$runtime_string} = $runtime_string."|".$diff_total."|".$timestamp;
$ch{$runtime_string} = 1 if($value);
$ch{$runtime_string} = 1 if(defined $a[3]);
$lval = $value;
$rslval = $runtime_string;
}
if ($diff) {
if($diff <= $difflimit) {
$diff_total = $diff_total+$diff;
if(abs $diff <= $dlim) {
$diff_total = $diff_total + $diff;
}
$rh{$runtime_string} = $runtime_string."|".$diff_total."|".$timestamp;
$ch{$runtime_string}++ if($value && $i > 1);
}
$lval = $value;
$rslval = $runtime_string;
}
$ch{$runtime_string}++ if(defined $a[3] && $i > 1);
}
else { # neuer Zeitabschnitt beginnt, ersten Value-Wert erfassen und Übertragsdifferenz bilden
$lastruntimestring = $runtime_string;
$i = 1;
$uediff = $value - $lval if($value > $lval);
$uediff = $value - $lval;
$diff = $uediff;
$lval = $value if($value); # Übetrag über Perioden mit value = 0 hinweg !
$rslval = $runtime_string;
$lval = $value if($value); # Übertrag über Perioden mit value = 0 hinweg !
Log3 ($name, 5, "DbRep $name - balance difference of $uediff between $rslval and $runtime_string");
$diff_total = $diff ? $diff : 0 if($diff <= $difflimit);
$rslval = $runtime_string;
if(abs $diff <= $dlim) {
$diff_total = $diff;
}
$rh{$runtime_string} = $runtime_string."|".$diff_total."|".$timestamp;
$ch{$runtime_string} = 1 if($value);
$ch{$runtime_string} = 1 if(defined $a[3]);
$uediff = 0;
}
@ -4705,7 +4740,7 @@ sub DbRep_diffval {
}
# Ergebnishash als Einzeiler zurückgeben
# ignorierte Zeilen ($diff > $difflimit)
# ignorierte Zeilen (abs $diff > $dlim)
my $rowsrej;
$rowsrej = encode_base64 ($rejectstr, "") if($rejectstr);
@ -4745,10 +4780,10 @@ sub DbRep_diffvalDone {
my $ncpslist = $a[6] ? decode_base64($a[7]) : ''; # Hash von Perioden die nicht kalkuliert werden konnten "no calc in period"
my $irowdone = $a[8];
my $ndp = AttrVal($name, "numDecimalPlaces", $dbrep_defdecplaces);
my $difflimit = AttrVal($name, "diffAccept", "20"); # legt fest, bis zu welchem Wert Differenzen akzeptoert werden (Ausreißer eliminieren)AttrVal($name, "diffAccept", "20");
my $hash = $defs{$name};
my $ndp = AttrVal ($name, "numDecimalPlaces", $dbrep_defdecplaces);
my $difflimit = AttrVal ($name, 'diffAccept', 20); # legt fest, bis zu welchem Wert Differenzen akzeptiert werden (Ausreißer eliminieren)
my ($sign, $dlim) = DbRep_ExplodeDiffAcc ($difflimit);
my $reading_runtime_string;
@ -4816,7 +4851,7 @@ sub DbRep_diffvalDone {
}
ReadingsBulkUpdateValue ($hash, "db_lines_processed", $irowdone) if($hash->{LASTCMD} =~ /writeToDB/);
ReadingsBulkUpdateValue ($hash, "diff_overrun_limit_".$difflimit, $rowsrej) if($rowsrej);
ReadingsBulkUpdateValue ($hash, "diff_overrun_limit_".$dlim, $rowsrej) if($rowsrej);
ReadingsBulkUpdateValue ($hash, "less_data_in_period", $ncpstr) if($ncpstr);
ReadingsBulkUpdateValue ($hash, "state", qq{WARNING - see readings 'less_data_in_period' or 'diff_overrun_limit_XX'})
if($ncpstr||$rowsrej);
@ -6878,7 +6913,7 @@ sub DbRep_sqlCmd {
my (@rows,$row,@head);
my $nrows = 0;
if($sql =~ m/^\s*(call|explain|select|pragma|show)/is) {
if($sql =~ m/^\s*(call|explain|select|pragma|show|describe)/is) {
@head = map { uc($sth->{NAME}[$_]) } keys @{$sth->{NAME}}; # https://metacpan.org/pod/DBI#NAME1
if (@head) {
$row = join("$srs", @head);
@ -7026,7 +7061,7 @@ sub DbRep_sqlCmdBlocking {
}
my $nrows = 0;
if($sql =~ m/^\s*(call|explain|select|pragma|show)/is) {
if($sql =~ m/^\s*(call|explain|select|pragma|show|describe)/is) {
while (my @line = $sth->fetchrow_array()) {
Log3 ($name, 4, "DbRep $name - SQL result: @line");
$ret .= "\n" if($nrows); # Forum: #103295
@ -13911,6 +13946,23 @@ sub DbRep_numval {
return $val;
}
################################################################
# Zerlegung des Attributwertes "diffAccept"
################################################################
sub DbRep_ExplodeDiffAcc {
my $val = shift // q{empty};
my $sign = q{};
my $daval = q{};
if ($val =~/^(\+?-?)([0-9]+)$/xs) {
$sign = $1;
$daval = $2;
}
return ($sign, $daval);
}
################################################################
# Prüfung auf numerischen Wert (vorzeichenbehaftet)
################################################################
@ -17787,32 +17839,42 @@ return;
</li>
<li><b> diffValue [display | writeToDB] </b>
- berechnet den Differenzwert des Datenbankfelds "VALUE" in den angegebenen Zeitgrenzen (siehe verschiedenen time*-Attribute).
Es muss das auszuwertende Reading im Attribut <a href="#DbRep-attr-reading">reading</a> angegeben sein. <br>
Diese Funktion ist z.B. zur Auswertung von Daten sinnvoll, deren Werte sich fortlaufend erhöhen und keine Wertdifferenzen wegschreiben. <br>
Es wird immer die Differenz aus den VALUE-Werten der im Aggregationszeitraum (z.B. day) vorhandenen Datensätzen gebildet und aufsummiert,
wobei ein Übertragswert der Vorperiode (<a href="#DbRep-attr-aggregation">aggregation</a>) zur darauf folgenden Aggregationsperiode berücksichtigt wird, sofern diese einen Value-Wert
enhtält. <br>
Dabei wird ein Zählerüberlauf (Neubeginn bei 0) mit berücksichtigt (vergleiche Attribut <a href="#DbRep-attr-diffAccept">diffAccept</a>). <br>
Wird in einer auszuwertenden Zeit- bzw. Aggregationsperiode nur ein Datensatz gefunden, kann die Differenz in Verbindung mit dem
Differenzübertrag der Vorperiode berechnet werden. in diesem Fall kann es zu einer logischen Ungenauigkeit in der Zuordnung der Differenz
zu der Aggregationsperiode kommen. Deswegen wird eine Warnung im "state" und das
Reading "less_data_in_period" mit einer Liste der betroffenen Perioden wird erzeugt. <br><br>
<li><b> diffValue [display | writeToDB] </b> <br><br>
Berechnet den Differenzwert des Datenbankfelds "VALUE" in den angegebenen Zeitgrenzen
(siehe verschiedenen time*-Attribute). <br><br>
Es wird die Differenz aus den VALUE-Werten der im Aggregationszeitraum (z.B. day) vorhandenen Datensätze gebildet und
aufsummiert.
Ein Übertragswert aus der Vorperiode (<a href="#DbRep-attr-aggregation">aggregation</a>) zur darauf folgenden
Aggregationsperiode wird berücksichtigt, sofern diese Periode einen Value-Wert enhtält. <br><br>
In der Standardeinstellung wertet die Funktion nur positive Differenzen aus wie sie z.B. bei einem stetig ansteigenden
Zählerwert auftreten.
Mit dem Attribut <a href="#DbRep-attr-diffAccept">diffAccept</a>) kann sowohl die akzeptierte Differenzschwelle als
auch die Möglichkeit negative Differenzen auszuwerten eingestellt werden. <br><br>
<ul>
<b>Hinweis: </b><br>
Im Auswertungs- bzw. Aggregationszeitraum (Tag, Woche, Monat, etc.) sollten dem Modul pro Periode mindestens ein Datensatz
zu Beginn und ein Datensatz gegen Ende des Aggregationszeitraumes zur Verfügung stehen um eine möglichst genaue Auswertung
der Differenzwerte vornehmen zu können.
Im Auswertungs- bzw. Aggregationszeitraum (Tag, Woche, Monat, etc.) sollten dem Modul pro Periode mindestens ein
Datensatz zu Beginn und ein Datensatz gegen Ende des Aggregationszeitraumes zur Verfügung stehen um eine möglichst
genaue Auswertung der Differenzwerte vornehmen zu können. <br>
Wird in einer auszuwertenden Zeit- bzw. Aggregationsperiode nur ein Datensatz gefunden, kann die Differenz in
Verbindung mit dem Differenzübertrag der Vorperiode berechnet werden. in diesem Fall kann es zu einer logischen
Ungenauigkeit in der Zuordnung der Differenz zu der Aggregationsperiode kommen. In diesem Fall wird eine Warnung
im state ausgegeben und das Reading <b>less_data_in_period</b> mit einer Liste der betroffenen Perioden erzeugt.
<br>
<br>
</ul>
Ist keine oder die Option "display" angegeben, werden die Ergebnisse nur angezeigt. Mit
der Option "writeToDB" werden die Berechnungsergebnisse mit einem neuen Readingnamen
Ist keine oder die Option <b>display</b> angegeben, werden die Ergebnisse nur angezeigt. <br><br>
Mit der Option <b>writeToDB</b> werden die Berechnungsergebnisse mit einem neuen Readingnamen
in der Datenbank gespeichert. <br>
Der neue Readingname wird aus einem Präfix und dem originalen Readingnamen gebildet,
wobei der originale Readingname durch das Attribut <a href="#DbRep-attr-readingNameMap">readingNameMap</a> ersetzt werden kann.
wobei der originale Readingname durch das Attribut <a href="#DbRep-attr-readingNameMap">readingNameMap</a> ersetzt
werden kann. <br>
Der Präfix setzt sich aus der Bildungsfunktion und der Aggregation zusammen. <br>
Der Timestamp der neuen Readings in der Datenbank wird von der eingestellten Aggregationsperiode
abgeleitet, sofern kein eindeutiger Zeitpunkt des Ergebnisses bestimmt werden kann.
@ -17825,7 +17887,7 @@ return;
</ul>
<br>
Zusammengefasst sind die zur Steuerung dieser Funktion relevanten Attribute: <br><br>
Die für die Funktion relevanten Attribute sind: <br><br>
<ul>
<table>
@ -17843,7 +17905,8 @@ return;
</ul>
<br>
</li> <br>
</li>
<br>
<li><b> dumpMySQL [clientSide | serverSide]</b> <br><br>
@ -19372,24 +19435,40 @@ sub dbval {
</li>
<a id="DbRep-attr-diffAccept"></a>
<li><b>diffAccept </b> - gilt für Funktion diffValue. diffAccept legt fest bis zu welchem Schwellenwert eine berechnete positive Werte-Differenz
zwischen zwei unmittelbar aufeinander folgenden Datensätzen akzeptiert werden soll (Standard ist 20). <br>
Damit werden fehlerhafte DB-Einträge mit einem unverhältnismäßig hohen Differenzwert von der Berechnung ausgeschlossen und
verfälschen nicht das Ergebnis. Sollten Schwellenwertüberschreitungen vorkommen, wird das Reading "diff_overrun_limit_&lt;diffLimit&gt;"
erstellt. (&lt;diffLimit&gt; wird dabei durch den aktuellen Attributwert ersetzt)
<li><b>diffAccept [+-]&lt;Schwellenwert&gt; </b> <br><br>
diffAccept legt für die Funktion diffValue fest, bis zu welchem &lt;Schwellenwert&gt; eine
Werte-Differenz zwischen zwei unmittelbar aufeinander folgenden Datensätzen akzeptiert werden. <br>
Wird dem Schwellenwert <b>+-</b> (optional) vorangestellt, werden sowohl positive als auch negative Differenzen
ausgewertet.
<br><br>
(default: 20, nur positive Differenzen zwischen Vorgänger und Nachfolger)
<br><br>
<ul>
<b>Beispiel: </b> <br>
attr <Name> diffAccept +-10000
</ul>
<br>
Bei Schwellenwertüberschreitungen wird das Reading <b>diff_overrun_limit_&lt;Schwellenwert&gt;</b>
erstellt. <br>
Es enthält eine Liste der relevanten Wertepaare. Mit verbose 3 werden diese Datensätze ebenfalls im Logfile protokolliert.
<br><br>
<ul>
Beispiel Ausgabe im Logfile beim Überschreiten von diffAccept=10: <br><br>
<b>Beispiel Ausgabe im Logfile beim Überschreiten von diffAccept=10:</b> <br><br>
DbRep Rep.STP5000.etotal -> data ignored while calc diffValue due to threshold overrun (diffAccept = 10): <br>
2016-04-09 08:50:50 0.0340 -> 2016-04-09 12:42:01 13.3440 <br><br>
# Der erste Datensatz mit einem Wert von 0.0340 ist untypisch gering zum nächsten Wert 13.3440 und führt zu einem zu hohen
Differenzwert. <br>
# Der Differenz zwischen dem ersten Datensatz mit einem Wert von 0.0340 zum nächsten Wert 13.3440 ist untypisch hoch
und führt zu einem zu hohen Differenzwert. <br>
# Es ist zu entscheiden ob der Datensatz gelöscht, ignoriert, oder das Attribut diffAccept angepasst werden sollte.
</ul><br>
</ul>
<br>
</li>
<a id="DbRep-attr-dumpComment"></a>