2
0
mirror of https://github.com/fhem/fhem-mirror.git synced 2025-03-03 16:56:54 +00:00

93_DbRep: contrib 8.8.0

git-svn-id: https://svn.fhem.de/fhem/trunk@17690 2b470e98-0d58-463d-a4d8-8e2adae1ed80
This commit is contained in:
nasseeder1 2018-11-05 23:34:14 +00:00
parent f6c93a301c
commit 52bd5b9717

View File

@ -57,6 +57,7 @@ no if $] >= 5.017011, warnings => 'experimental::smartmatch';
# Versions History intern # Versions History intern
our %DbRep_vNotesIntern = ( our %DbRep_vNotesIntern = (
"8.8.0" => "05.11.2018 first connect routine switched to DbRep_Main, get COLSET from DBLOG-instance",
"8.7.0" => "04.11.2018 attribute valueFilter applied to functions based on 'SELECT', 'UPDATE', 'DELETE' and 'valueFilter' generally applied to field 'VALUE' ", "8.7.0" => "04.11.2018 attribute valueFilter applied to functions based on 'SELECT', 'UPDATE', 'DELETE' and 'valueFilter' generally applied to field 'VALUE' ",
"8.6.0" => "29.10.2018 reduceLog use attributes device/reading (can be overwritten by set-options) ", "8.6.0" => "29.10.2018 reduceLog use attributes device/reading (can be overwritten by set-options) ",
"8.5.0" => "27.10.2018 versionNotes revised, EXCLUDE of reading/device possible (DbRep_specsForSql changed) ", "8.5.0" => "27.10.2018 versionNotes revised, EXCLUDE of reading/device possible (DbRep_specsForSql changed) ",
@ -64,11 +65,11 @@ our %DbRep_vNotesIntern = (
"versionNotes changed to support en/de, get dbValue as textfield-long ", "versionNotes changed to support en/de, get dbValue as textfield-long ",
"8.3.0" => "17.10.2018 reduceLog from DbLog integrated into DbRep, textField-long as default for sqlCmd, both attributes timeOlderThan and timeDiffToNow can be set at same time", "8.3.0" => "17.10.2018 reduceLog from DbLog integrated into DbRep, textField-long as default for sqlCmd, both attributes timeOlderThan and timeDiffToNow can be set at same time",
"8.2.3" => "07.10.2018 check availability of DbLog-device at definition time of DbRep-device ", "8.2.3" => "07.10.2018 check availability of DbLog-device at definition time of DbRep-device ",
"8.2.2" => "07.10.2018 DbRep_getMinTs changed, fix don't get the real min timestamp in rare cases ", "8.2.2" => "07.10.2018 DbRep_getInitData changed, fix don't get the real min timestamp in rare cases ",
"8.2.1" => "07.10.2018 \$hash->{dbloghash}{HELPER}{REOPEN_RUNS_UNTIL} contains time until DB is closed ", "8.2.1" => "07.10.2018 \$hash->{dbloghash}{HELPER}{REOPEN_RUNS_UNTIL} contains time until DB is closed ",
"8.2.0" => "05.10.2018 direct help for attributes ", "8.2.0" => "05.10.2018 direct help for attributes ",
"8.1.0" => "02.10.2018 new get versionNotes command ", "8.1.0" => "02.10.2018 new get versionNotes command ",
"8.0.1" => "20.09.2018 DbRep_getMinTs improved", "8.0.1" => "20.09.2018 DbRep_getInitData improved",
"8.0.0" => "11.09.2018 get filesize in DbRep_WriteToDumpFile corrected, restoreMySQL for clientSide dumps, minor fixes ", "8.0.0" => "11.09.2018 get filesize in DbRep_WriteToDumpFile corrected, restoreMySQL for clientSide dumps, minor fixes ",
"7.20.0" => "04.09.2018 deviceRename can operate a Device name with blank, e.g. 'current balance' as old device name ", "7.20.0" => "04.09.2018 deviceRename can operate a Device name with blank, e.g. 'current balance' as old device name ",
"7.19.0" => "25.08.2018 attribute 'valueFilter' to filter datasets in fetchrows ", "7.19.0" => "25.08.2018 attribute 'valueFilter' to filter datasets in fetchrows ",
@ -368,8 +369,9 @@ sub DbRep_Define($@) {
$hash->{ROLE} = AttrVal($name, "role", "Client"); $hash->{ROLE} = AttrVal($name, "role", "Client");
$hash->{MODEL} = $hash->{ROLE}; $hash->{MODEL} = $hash->{ROLE};
$hash->{HELPER}{DBLOGDEVICE} = $a[2]; $hash->{HELPER}{DBLOGDEVICE} = $a[2];
$hash->{HELPER}{IDRETRIES} = 3; # Anzahl wie oft versucht wird initiale Daten zu holen
$hash->{VERSION} = (reverse sort(keys %DbRep_vNotesIntern))[0]; $hash->{VERSION} = (reverse sort(keys %DbRep_vNotesIntern))[0];
$hash->{NOTIFYDEV} = "global,".$name; # nur Events dieser Devices an DbRep_Notify weiterleiten $hash->{NOTIFYDEV} = "global,".$name; # nur Events dieser Devices an DbRep_Notify weiterleiten
my $dbconn = $defs{$a[2]}{dbconn}; my $dbconn = $defs{$a[2]}{dbconn};
$hash->{DATABASE} = (split(/;|=/, $dbconn))[1]; $hash->{DATABASE} = (split(/;|=/, $dbconn))[1];
$hash->{UTF8} = defined($defs{$a[2]}{UTF8})?$defs{$a[2]}{UTF8}:0; $hash->{UTF8} = defined($defs{$a[2]}{UTF8})?$defs{$a[2]}{UTF8}:0;
@ -381,7 +383,7 @@ sub DbRep_Define($@) {
} }
RemoveInternalTimer($hash); RemoveInternalTimer($hash);
InternalTimer(gettimeofday()+int(rand(45)), 'DbRep_firstconnect', $hash, 0); InternalTimer(gettimeofday()+int(rand(45)), 'DbRep_firstconnect', "$name|||", 0);
Log3 ($name, 4, "DbRep $name - initialized"); Log3 ($name, 4, "DbRep $name - initialized");
ReadingsSingleUpdateValue ($hash, 'state', 'initialized', 1); ReadingsSingleUpdateValue ($hash, 'state', 'initialized', 1);
@ -862,7 +864,7 @@ sub DbRep_Get($@) {
$hash->{LASTCMD} = $prop?"$opt $prop":"$opt"; $hash->{LASTCMD} = $prop?"$opt $prop":"$opt";
return "The operation \"$opt\" isn't available with database type $dbmodel" if ($dbmodel ne 'MYSQL'); return "The operation \"$opt\" isn't available with database type $dbmodel" if ($dbmodel ne 'MYSQL');
ReadingsSingleUpdateValue ($hash, "state", "running", 1); ReadingsSingleUpdateValue ($hash, "state", "running", 1);
DbRep_delread($hash); # Readings löschen die nicht in der Ausnahmeliste (Attr readingPreventFromDel) stehen DbRep_delread($hash); # Readings löschen die nicht in der Ausnahmeliste (Attr readingPreventFromDel) stehen
$hash->{HELPER}{RUNNING_PID} = BlockingCall("dbmeta_DoParse", "$name|$opt", "dbmeta_ParseDone", $to, "DbRep_ParseAborted", $hash); $hash->{HELPER}{RUNNING_PID} = BlockingCall("dbmeta_DoParse", "$name|$opt", "dbmeta_ParseDone", $to, "DbRep_ParseAborted", $hash);
} elsif ($opt eq "svrinfo") { } elsif ($opt eq "svrinfo") {
@ -884,7 +886,9 @@ sub DbRep_Get($@) {
$hash->{LASTCMD} = $prop?"$opt $prop":"$opt"; $hash->{LASTCMD} = $prop?"$opt $prop":"$opt";
DbRep_delread($hash); DbRep_delread($hash);
ReadingsSingleUpdateValue ($hash, "state", "running", 1); ReadingsSingleUpdateValue ($hash, "state", "running", 1);
DbRep_firstconnect($hash); $hash->{HELPER}{IDRETRIES} = 3; # Anzahl wie oft versucht wird initiale Daten zu holen
$prop = $prop?$prop:'';
DbRep_firstconnect("$name|$opt|$prop|");
} elsif ($opt =~ /dbValue/) { } elsif ($opt =~ /dbValue/) {
return "get \"$opt\" needs at least an argument" if ( @a < 3 ); return "get \"$opt\" needs at least an argument" if ( @a < 3 );
@ -1044,7 +1048,7 @@ sub DbRep_Attr($$$$) {
ReadingsSingleUpdateValue ($hash, "state", $val, 1); ReadingsSingleUpdateValue ($hash, "state", $val, 1);
if ($do == 0) { if ($do == 0) {
RemoveInternalTimer($hash); RemoveInternalTimer($hash);
InternalTimer(time+5, 'DbRep_firstconnect', $hash, 0); InternalTimer(time+5, 'DbRep_firstconnect', "$name|||", 0);
} else { } else {
my $dbh = $hash->{DBH}; my $dbh = $hash->{DBH};
$dbh->disconnect() if($dbh); $dbh->disconnect() if($dbh);
@ -1342,22 +1346,33 @@ return undef;
# Verbindung zur DB aufbauen und den Timestamp des ältesten # Verbindung zur DB aufbauen und den Timestamp des ältesten
# Datensatzes ermitteln # Datensatzes ermitteln
################################################################################### ###################################################################################
sub DbRep_firstconnect($) { sub DbRep_firstconnect(@) {
my ($hash) = @_; my ($string) = @_;
my $name = $hash->{NAME}; my ($name,$opt,$prop,$fret) = split("\\|", $string);
my $to = "120"; my $hash = $defs{$name};
my $dbloghash = $hash->{dbloghash}; my $to = "120";
my $dbconn = $dbloghash->{dbconn}; my $dbloghash = $hash->{dbloghash};
my $dbuser = $dbloghash->{dbuser}; my $dbconn = $dbloghash->{dbconn};
my $dbuser = $dbloghash->{dbuser};
RemoveInternalTimer($hash, "DbRep_firstconnect"); RemoveInternalTimer($hash, "DbRep_firstconnect");
return if(IsDisabled($name)); return if(IsDisabled($name));
if ($init_done == 1) { if ($init_done == 1) {
# DB Struktur aus DbLog Instanz übernehmen
$hash->{HELPER}{DBREPCOL}{COLSET} = $dbloghash->{HELPER}{COLSET};
$hash->{HELPER}{DBREPCOL}{DEVICECOL} = $dbloghash->{HELPER}{DEVICECOL};
$hash->{HELPER}{DBREPCOL}{EVENTCOL} = $dbloghash->{HELPER}{EVENTCOL};
$hash->{HELPER}{DBREPCOL}{READINGCOL} = $dbloghash->{HELPER}{READINGCOL};
$hash->{HELPER}{DBREPCOL}{TYPECOL} = $dbloghash->{HELPER}{TYPECOL};
$hash->{HELPER}{DBREPCOL}{UNITCOL} = $dbloghash->{HELPER}{UNITCOL};
$hash->{HELPER}{DBREPCOL}{VALUECOL} = $dbloghash->{HELPER}{VALUECOL};
# DB Strukturelemente abrufen
Log3 ($name, 3, "DbRep $name - Connectiontest to database $dbconn with user $dbuser") if($hash->{LASTCMD} ne "minTimestamp"); Log3 ($name, 3, "DbRep $name - Connectiontest to database $dbconn with user $dbuser") if($hash->{LASTCMD} ne "minTimestamp");
$hash->{HELPER}{RUNNING_PID} = BlockingCall("DbRep_getMinTs", "$name", "DbRep_getMinTsDone", $to, "DbRep_getMinTsAborted", $hash); $hash->{HELPER}{RUNNING_PID} = BlockingCall("DbRep_getInitData", "$name|$opt|$prop|$fret", "DbRep_getInitDataDone", $to, "DbRep_getInitDataAborted", $hash);
$hash->{HELPER}{RUNNING_PID}{loglevel} = 5 if($hash->{HELPER}{RUNNING_PID}); # Forum #77057 $hash->{HELPER}{RUNNING_PID}{loglevel} = 5 if($hash->{HELPER}{RUNNING_PID}); # Forum #77057
} else { } else {
InternalTimer(time+1, "DbRep_firstconnect", $hash, 0); InternalTimer(time+1, "DbRep_firstconnect", "$name|$opt|$prop|$fret", 0);
} }
return; return;
@ -1366,8 +1381,9 @@ return;
#################################################################################################### ####################################################################################################
# den ältesten Datensatz (Timestamp) in der DB bestimmen # den ältesten Datensatz (Timestamp) in der DB bestimmen
#################################################################################################### ####################################################################################################
sub DbRep_getMinTs($) { sub DbRep_getInitData($) {
my ($name) = @_; my ($string) = @_;
my ($name,$opt,$prop,$fret) = split("\\|", $string);
my $hash = $defs{$name}; my $hash = $defs{$name};
my $dbloghash = $hash->{dbloghash}; my $dbloghash = $hash->{dbloghash};
my $dbconn = $dbloghash->{dbconn}; my $dbconn = $dbloghash->{dbconn};
@ -1391,8 +1407,6 @@ sub DbRep_getMinTs($) {
my $st = [gettimeofday]; my $st = [gettimeofday];
eval { $mints = $dbh->selectrow_array("SELECT min(TIMESTAMP) FROM history;"); }; eval { $mints = $dbh->selectrow_array("SELECT min(TIMESTAMP) FROM history;"); };
# eval { $mints = $dbh->selectrow_array("select TIMESTAMP from history limit 1;"); };
# eval { $mints = $dbh->selectrow_array("select TIMESTAMP from history order by TIMESTAMP limit 1;"); };
$dbh->disconnect; $dbh->disconnect;
@ -1404,58 +1418,63 @@ sub DbRep_getMinTs($) {
# Background-Laufzeit ermitteln # Background-Laufzeit ermitteln
my $brt = tv_interval($bst); my $brt = tv_interval($bst);
$rt = $rt.",".$brt; $rt = $rt.",".$brt;
no warnings 'uninitialized';
return "$name|$mints|$rt|0"; return "$name|$mints|$rt|0|$opt|$prop|$fret";
} }
#################################################################################################### ####################################################################################################
# Auswertungsroutine den ältesten Datensatz (Timestamp) in der DB bestimmen # Auswertungsroutine den ältesten Datensatz (Timestamp) in der DB bestimmen
#################################################################################################### ####################################################################################################
sub DbRep_getMinTsDone($) { sub DbRep_getInitDataDone($) {
my ($string) = @_; my ($string) = @_;
my @a = split("\\|",$string); my @a = split("\\|",$string);
my $hash = $defs{$a[0]}; my $hash = $defs{$a[0]};
my $name = $hash->{NAME}; my $name = $hash->{NAME};
my $mints = decode_base64($a[1]); my $mints = decode_base64($a[1]);
my $bt = $a[2]; my $bt = $a[2];
my ($rt,$brt) = split(",", $bt); my ($rt,$brt) = split(",", $bt);
my $err = $a[3]?decode_base64($a[3]):undef; my $err = $a[3]?decode_base64($a[3]):undef;
my $opt = $a[4];
my $prop = $a[5];
my $fret = \&{$a[6]} if($a[6]);
my $dblogdevice = $hash->{HELPER}{DBLOGDEVICE}; my $dblogdevice = $hash->{HELPER}{DBLOGDEVICE};
$hash->{dbloghash} = $defs{$dblogdevice}; $hash->{dbloghash} = $defs{$dblogdevice};
my $dbconn = $hash->{dbloghash}{dbconn}; my $dbconn = $hash->{dbloghash}{dbconn};
if ($err) { if ($err) {
readingsBeginUpdate($hash); readingsBeginUpdate($hash);
ReadingsBulkUpdateValue ($hash, "errortext", $err); ReadingsBulkUpdateValue ($hash, "errortext", $err);
ReadingsBulkUpdateValue ($hash, "state", "disconnected"); ReadingsBulkUpdateValue ($hash, "state", "disconnected");
readingsEndUpdate($hash, 1); readingsEndUpdate($hash, 1);
delete($hash->{HELPER}{RUNNING_PID}); delete($hash->{HELPER}{RUNNING_PID});
Log3 ($name, 2, "DbRep $name - DB connect failed. Make sure credentials of database $hash->{DATABASE} are valid and database is reachable."); Log3 ($name, 2, "DbRep $name - DB connect failed. Make sure credentials of database $hash->{DATABASE} are valid and database is reachable.");
return;
} else {
my $state = ($hash->{LASTCMD} eq "minTimestamp")?"done":"connected";
$state = "invalid timestamp \"$mints\" found in database - please delete it" if($mints =~ /^0000-00-00.*$/);
readingsBeginUpdate($hash);
ReadingsBulkUpdateValue ($hash, "timestamp_oldest_dataset", $mints) if($hash->{LASTCMD} eq "minTimestamp");
ReadingsBulkUpdateTimeState($hash,$brt,$rt,$state);
readingsEndUpdate($hash, 1);
Log3 ($name, 3, "DbRep $name - Connectiontest to db $dbconn successful") if($hash->{LASTCMD} ne "minTimestamp");
$hash->{HELPER}{MINTS} = $mints;
} }
my $state = ($hash->{LASTCMD} eq "minTimestamp")?"done":"connected";
$state = "invalid timestamp \"$mints\" found in database - please delete it" if($mints =~ /^0000-00-00.*$/);
readingsBeginUpdate($hash);
ReadingsBulkUpdateValue ($hash, "timestamp_oldest_dataset", $mints) if($hash->{LASTCMD} eq "minTimestamp");
ReadingsBulkUpdateTimeState($hash,$brt,$rt,$state);
readingsEndUpdate($hash, 1);
Log3 ($name, 4, "DbRep $name - Connectiontest to db $dbconn successful") if($hash->{LASTCMD} ne "minTimestamp");
$hash->{HELPER}{MINTS} = $mints;
delete($hash->{HELPER}{RUNNING_PID}); delete($hash->{HELPER}{RUNNING_PID});
return; return if(!$fret);
return &$fret($hash,$opt,$prop);
} }
#################################################################################################### ####################################################################################################
# Abbruchroutine den ältesten Datensatz (Timestamp) in der DB bestimmen # Abbruchroutine den ältesten Datensatz (Timestamp) in der DB bestimmen
#################################################################################################### ####################################################################################################
sub DbRep_getMinTsAborted(@) { sub DbRep_getInitDataAborted(@) {
my ($hash,$cause) = @_; my ($hash,$cause) = @_;
my $name = $hash->{NAME}; my $name = $hash->{NAME};
@ -1564,6 +1583,16 @@ sub DbRep_Main($$;$) {
BlockingKill($hash->{HELPER}{RUNNING_PID}); BlockingKill($hash->{HELPER}{RUNNING_PID});
} }
# initiale Datenermittlung wie minimal Timestamp, Datenbankstrukturen, ...
if(!$hash->{HELPER}{MINTS} or !$hash->{HELPER}{DBREPCOL}{COLSET}) {
my $dbname = $hash->{DATABASE};
Log3 ($name, 3, "DbRep $name - get initial structure information of database \"$dbname\", remaining attempts: ".$hash->{HELPER}{IDRETRIES});
$prop = $prop?$prop:'';
DbRep_firstconnect("$name|$opt|$prop|DbRep_Main") if($hash->{HELPER}{IDRETRIES} > 0);
$hash->{HELPER}{IDRETRIES}--;
return;
}
ReadingsSingleUpdateValue ($hash, "state", "running", 1); ReadingsSingleUpdateValue ($hash, "state", "running", 1);
# only for this block because of warnings if details of readings are not set # only for this block because of warnings if details of readings are not set