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:
parent
d8402205b6
commit
52ea01828f
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user