2
0
mirror of https://github.com/fhem/fhem-mirror.git synced 2025-01-31 18:59:33 +00:00

93_DbRep: contrib 8.31.0

git-svn-id: https://svn.fhem.de/fhem/trunk@21064 2b470e98-0d58-463d-a4d8-8e2adae1ed80
This commit is contained in:
nasseeder1 2020-01-27 12:58:42 +00:00
parent d8402205b6
commit 52ea01828f

View File

@ -1,5 +1,5 @@
##########################################################################################################
# $Id: 93_DbRep.pm 20571 2019-11-24 19:17:33Z DS_Starter $
# $Id: 93_DbRep.pm 21050 2020-01-25 19:45:16Z DS_Starter $
##########################################################################################################
# 93_DbRep.pm
#
@ -58,6 +58,7 @@ no if $] >= 5.017011, warnings => 'experimental::smartmatch';
# Version History intern
our %DbRep_vNotesIntern = (
"8.31.0" => "26.01.2020 new option \"deleteOther\" for maxValue ",
"8.30.8" => "25.01.2020 adjust SQL-Statements in OutputWriteToDB to avoid Duplicate entry errors and other fixes ",
"8.30.7" => "24.01.2020 corrected count of inserted rows into standby database (DbRep_WriteToDB) ",
"8.30.6" => "23.01.2020 delDoublets now are working also for PostgreSQL, calculation of number_fetched_rows corrected ",
@ -548,7 +549,7 @@ sub DbRep_Set($@) {
(($hash->{ROLE} ne "Agent")?"readingRename ":"").
(($hash->{ROLE} ne "Agent")?"exportToFile ":"").
(($hash->{ROLE} ne "Agent")?"importFromFile ":"").
(($hash->{ROLE} ne "Agent")?"maxValue:display,writeToDB ":"").
(($hash->{ROLE} ne "Agent")?"maxValue:display,writeToDB,deleteOther ":"").
(($hash->{ROLE} ne "Agent")?"minValue:display,writeToDB ":"").
(($hash->{ROLE} ne "Agent")?"fetchrows:history,current ":"").
(($hash->{ROLE} ne "Agent")?"diffValue:display,writeToDB ":"").
@ -3269,15 +3270,15 @@ sub maxval_DoParse($) {
return "$name|''|$device|$reading|''|$err|''";
}
my @array= map { $runtime_string." ".$_ -> [0]." ".$_ -> [1]."\n" } @{ $sth->fetchall_arrayref() };
my @array = map { $runtime_string." ".$_->[0]." ".$_->[1]."!_ESC_!".$runtime_string_first."|".$runtime_string_next } @{ $sth->fetchall_arrayref() };
if(!@array) {
if(AttrVal($name, "aggregation", "") eq "hour") {
my @rsf = split(/[ :]/,$runtime_string_first);
@array = ($runtime_string." "."0"." ".$rsf[0]."_".$rsf[1]."\n");
@array = ($runtime_string." "."0"." ".$rsf[0]."_".$rsf[1]."!_ESC_!".$runtime_string_first."|".$runtime_string_next);
} else {
my @rsf = split(" ",$runtime_string_first);
@array = ($runtime_string." "."0"." ".$rsf[0]."\n");
@array = ($runtime_string." "."0"." ".$rsf[0]."!_ESC_!".$runtime_string_first."|".$runtime_string_next);
}
}
push(@row_array, @array);
@ -3297,10 +3298,12 @@ sub maxval_DoParse($) {
my ($lastruntimestring,$row_max_time,$max_value);
foreach my $row (@row_array) {
my @a = split("[ \t][ \t]*", $row);
my ($r,$t) = split("!_ESC_!", $row); # $t enthält $runtime_string_first."|".$runtime_string_next
my @a = split("[ \t][ \t]*", $r);
my $runtime_string = decode_base64($a[0]);
$lastruntimestring = $runtime_string if ($i == 1);
my $value = $a[1];
$a[-1] =~ s/:/-/g if($a[-1]); # substituieren unsupported characters -> siehe fhem.pl
my $timestamp = ($a[-1]&&$a[-2])?$a[-2]."_".$a[-1]:$a[-1];
@ -3318,18 +3321,18 @@ sub maxval_DoParse($) {
if ($runtime_string eq $lastruntimestring) {
if (!defined($max_value) || $value >= $max_value) {
$max_value = $value;
$row_max_time = $timestamp;
$rh{$runtime_string} = $runtime_string."|".$max_value."|".$row_max_time;
$max_value = $value;
$row_max_time = $timestamp;
$rh{$runtime_string} = $runtime_string."|".$max_value."|".$row_max_time."|".$t;
}
} else {
# neuer Zeitabschnitt beginnt, ersten Value-Wert erfassen
$lastruntimestring = $runtime_string;
undef $max_value;
if (!defined($max_value) || $value >= $max_value) {
$max_value = $value;
$row_max_time = $timestamp;
$rh{$runtime_string} = $runtime_string."|".$max_value."|".$row_max_time;
$max_value = $value;
$row_max_time = $timestamp;
$rh{$runtime_string} = $runtime_string."|".$max_value."|".$row_max_time."|".$t;
}
}
$i++;
@ -3356,6 +3359,17 @@ sub maxval_DoParse($) {
$rt = $rt+$wrt;
}
# andere Werte als "MAX" aus Datenbank löschen
if($prop =~ /deleteOther/) {
($wrt,$irowdone,$err) = DbRep_deleteOtherFromDB($name,$device,$reading,$rows);
if ($err) {
Log3 $hash->{NAME}, 2, "DbRep $name - $err";
$err = encode_base64($err,"");
return "$name|''|$device|$reading|''|$err|''";
}
$rt = $rt+$wrt;
}
my $rowlist = encode_base64($rows,"");
$device = encode_base64($device,"");
@ -3413,7 +3427,7 @@ sub maxval_ParseDone($) {
no warnings 'uninitialized';
foreach my $key (sort(keys(%rh))) {
my @k = split("\\|",$rh{$key});
my @k = split("\\|",$rh{$key});
my $rsf = $k[2]."__" if($k[2]);
if (AttrVal($hash->{NAME}, "readingNameMap", "")) {
@ -3428,7 +3442,7 @@ sub maxval_ParseDone($) {
ReadingsBulkUpdateValue ($hash, $reading_runtime_string, defined($rv)?sprintf("%.4f",$rv):"-");
}
ReadingsBulkUpdateValue ($hash, "db_lines_processed", $irowdone) if($hash->{LASTCMD} =~ /writeToDB/);
ReadingsBulkUpdateValue ($hash, "db_lines_processed", $irowdone) if($hash->{LASTCMD} =~ /writeToDB|deleteOther/);
ReadingsBulkUpdateTimeState($hash,$brt,$rt,$state);
readingsEndUpdate($hash, 1);
@ -10729,8 +10743,9 @@ sub DbRep_OutputWriteToDB($$$$$) {
my %rh = split("§", $wrstr);
foreach my $key (sort(keys(%rh))) {
my @k = split("\\|",$rh{$key});
$rsf = $k[2]; # Datum / Zeit für DB-Speicherung
$value = defined($k[1])?sprintf("%.4f",$k[1]):undef;
$rsf = $k[2]; # Datum / Zeit für DB-Speicherung
($date,$time) = split("_",$rsf);
$time =~ s/-/:/g if($time);
@ -10868,6 +10883,104 @@ sub DbRep_OutputWriteToDB($$$$$) {
return ($wrt,$irowdone,$err);
}
#######################################################################################################
# Werte außer dem Extremwert (MAX/$max_value) aus Datenbank im Zeitraum x für Device / Reading löschen
#
# Struktur $rh{$key} ->
# $runtime_string."|".$max_value."|".$row_max_time."|".$runtime_string_first."|".$runtime_string_next
#
#######################################################################################################
sub DbRep_deleteOtherFromDB($$$$) {
my ($name,$device,$reading,$rows) = @_;
my $hash = $defs{$name};
my $dbloghash = $defs{$hash->{HELPER}{DBLOGDEVICE}};
my $dbconn = $dbloghash->{dbconn};
my $dbuser = $dbloghash->{dbuser};
my $dblogname = $dbloghash->{NAME};
my $dbmodel = $dbloghash->{MODEL};
my $dbpassword = $attr{"sec$dblogname"}{secret};
my $wrt = 0;
my $irowdone = 0;
my $table = "history";
my ($dbh,$sth,$err,$timestamp,$value,$addon,$row_max_time,$runtime_string_first,$runtime_string_next,@row_array);
my %rh = split("§", $rows);
foreach my $key (sort(keys(%rh))) {
# Inhalt $rh{$key} -> $runtime_string."|".$max_value."|".$row_max_time."|".$runtime_string_first."|".$runtime_string_next
my @k = split("\\|",$rh{$key});
$value = defined($k[1])?$k[1]:undef;
$row_max_time = $k[2];
$runtime_string_first = $k[3];
$runtime_string_next = $k[4];
if ($value) {
# den MAX-Wert von Device/Reading und die Zeitgrenzen in Array speichern -> alle anderen sollen gelöscht werden
push(@row_array, "$device|$reading|$value|$row_max_time|$runtime_string_first|$runtime_string_next");
}
}
if (@row_array) { # Löschzyklus
eval {$dbh = DBI->connect("dbi:$dbconn", $dbuser, $dbpassword, { PrintError => 0, RaiseError => 1, AutoCommit => 1, AutoInactiveDestroy => 1 });};
if ($@) {
$err = $@;
Log3 ($name, 2, "DbRep $name - $@");
return ($wrt,$irowdone,$err);
}
eval { $dbh->begin_work() if($dbh->{AutoCommit}); };
if ($@) {
Log3($name, 2, "DbRep $name -> Error start transaction for history - $@");
}
# SQL-Startzeit
my $wst = [gettimeofday];
my $dlines = 0;
foreach my $row (@row_array) {
my @a = split("\\|",$row);
$device = $a[0];
$reading = $a[1];
$value = $a[2];
$row_max_time = $a[3];
$runtime_string_first = $a[4];
$runtime_string_next = $a[5];
$addon = "AND (TIMESTAMP,VALUE) != ('$row_max_time','$value')"; # (a, b) <> (x, y)
my $sql = DbRep_createDeleteSql($hash,$table,$device,$reading,$runtime_string_first,$runtime_string_next,$addon);
Log3 ($name, 4, "DbRep $name - SQL execute: $sql");
eval {
$sth = $dbh->prepare($sql);
$sth->execute();
$dlines += $sth->rows;
};
if ($@) {
$err = $@;
Log3 ($name, 2, "DbRep $name - $@");
$dbh->rollback;
$dbh->disconnect;
return ($wrt,0,$err);
}
}
eval {$dbh->commit() if(!$dbh->{AutoCommit});};
$dbh->disconnect;
Log3 $hash->{NAME}, 3, "DbRep $name - number of lines deleted in \"$dblogname\": $dlines";
$irowdone = $dlines;
# SQL-Laufzeit ermitteln
$wrt = tv_interval($wst);
}
return ($wrt,$irowdone,$err);
}
####################################################################################################
# Werte eines Array in DB schreiben
# Übergabe-Array: $date_ESC_$time_ESC_$device_ESC_$type_ESC_$event_ESC_$reading_ESC_$value_ESC_$unit
@ -11092,12 +11205,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 20571 2019-11-24 19:17:33Z DS_Starter $ im Kopf komplett! vorhanden )
if($modules{$type}{META}{x_version}) { # {x_version} ( nur gesetzt wenn $Id: 93_DbRep.pm 21050 2020-01-25 19:45:16Z 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 20571 2019-11-24 19:17:33Z DS_Starter $ im Kopf komplett! vorhanden )
return $@ unless (FHEM::Meta::SetInternals($hash)); # FVERSION wird gesetzt ( nur gesetzt wenn $Id: 93_DbRep.pm 21050 2020-01-25 19:45:16Z DS_Starter $ im Kopf komplett! vorhanden )
if(__PACKAGE__ eq "FHEM::$type" || __PACKAGE__ eq $type) {
# es wird mit Packages gearbeitet -> Perl übliche Modulversion setzen
# mit {<Modul>->VERSION()} im FHEMWEB kann Modulversion abgefragt werden
@ -12400,22 +12513,25 @@ return;
</ul>
<br>
<li><b> maxValue [display | writeToDB]</b>
<li><b> maxValue [display | writeToDB | deleteOther]</b>
- calculates the maximum value of database column "VALUE" between period given by
<a href="#DbRepattr">attributes</a> "timestamp_begin", "timestamp_end" or "timeDiffToNow / timeOlderThan".
The reading to evaluate must be defined using attribute "reading".
<a href="#DbRepattr">attributes</a> "timestamp_begin", "timestamp_end" / "timeDiffToNow / timeOlderThan" and so on.
The reading to evaluate must be defined using attribute <a href="#reading">reading</a>.
The evaluation contains the timestamp of the <b>last</b> appearing of the identified maximum value
within the given period. <br>
within the given period. <br><br>
Is no or the option "display" specified, the results are only displayed. Using
option "writeToDB" the calculated results are stored in the database with a new reading
If no option or the option <b>display</b> is specified, the results are only displayed. Using
option <b>writeToDB</b> the calculated results are stored in the database with a new reading
name. <br>
The new readingname is built of a prefix and the original reading name,
in which the original reading name can be replaced by the value of attribute "readingNameMap".
in which the original reading name can be replaced by the value of attribute <a href="#readingNameMap">readingNameMap</a>.
The prefix is made up of the creation function and the aggregation. <br>
The timestamp of the new stored readings is deviated from aggregation period,
unless no unique point of time of the result can be determined.
The field "EVENT" will be filled with "calculated".<br><br>
The field "EVENT" will be filled with "calculated". <br><br>
With option <b>deleteOther</b> all datasets except the dataset with the maximum value are deleted. <br><br>
<ul>
<b>Example of building a new reading name from the original reading "totalpac":</b> <br>
@ -13424,7 +13540,7 @@ sub bdump {
</li>
<a name="readingNameMap"></a>
<li><b>readingNameMap </b> - the name of the analyzed reading can be overwritten for output </li> <br>
<li><b>readingNameMap </b> - A part of the created reading name will be replaced by the specified string </li> <br>
<a name="role"></a>
<li><b>role </b> - the role of the DbRep-device. Standard role is "Client". <br>
@ -14899,25 +15015,28 @@ sub bdump {
</ul>
<br>
<li><b> maxValue [display | writeToDB] </b>
<li><b> maxValue [display | writeToDB | deleteOther] </b>
- berechnet den Maximalwert des Datenbankfelds "VALUE" in den Zeitgrenzen
(Attribute) "timestamp_begin", "timestamp_end" bzw. "timeDiffToNow / timeOlderThan".
Es muss das auszuwertende Reading über das <a href="#DbRepattr">Attribut</a> "reading"
(Attribute) "timestamp_begin", "timestamp_end" bzw. "timeDiffToNow / timeOlderThan" etc.
Es muss das auszuwertende Reading über das Attribut <a href="#reading">reading</a>
angegeben sein.
Die Auswertung enthält den Zeitstempel des ermittelten Maximumwertes innerhalb der
Aggregation bzw. Zeitgrenzen.
Im Reading wird der Zeitstempel des <b>letzten</b> Auftretens vom Maximalwert ausgegeben
falls dieser Wert im Intervall mehrfach erreicht wird. <br>
Im Reading wird der Zeitstempel des <b>letzten</b> Auftretens vom Maximalwert ausgegeben,
falls dieser Wert im Intervall mehrfach erreicht wird. <br><br>
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. 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 "readingNameMap" ersetzt werden kann.
wobei der originale Readingname durch das Attribut <a href="#readingNameMap">readingNameMap</a> ersetzt werden kann.
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.
Der Timestamp des neuen Readings in der Datenbank wird von der eingestellten Aggregationsperiode
abgeleitet.
Das Feld "EVENT" wird mit "calculated" gefüllt.<br><br>
Wird die Option <b>deleteOther</b> verwendet, werden alle Datensätze außer dem Datensatz mit dem
ermittelten Maximalwert aus der Datenbank innerhalb der definierten Grenzen gelöscht. <br><br>
<ul>
<b>Beispiel neuer Readingname gebildet aus dem Originalreading "totalpac":</b> <br>
@ -15948,7 +16067,7 @@ sub bdump {
</li>
<a name="readingNameMap"></a>
<li><b>readingNameMap </b> - der Name des ausgewerteten Readings wird mit diesem String für die Anzeige überschrieben </li> <br>
<li><b>readingNameMap </b> - ein Teil des erstellten Readingnamens wird mit dem angegebenen String ersetzt. </li> <br>
<a name="readingPreventFromDel"></a>
<li><b>readingPreventFromDel </b> - Komma separierte Liste von Readings die vor einer neuen Operation nicht gelöscht