diff --git a/fhem/contrib/DS_Starter/93_DbLog.pm b/fhem/contrib/DS_Starter/93_DbLog.pm index 279a1bda2..794f67e20 100644 --- a/fhem/contrib/DS_Starter/93_DbLog.pm +++ b/fhem/contrib/DS_Starter/93_DbLog.pm @@ -1,5 +1,5 @@ ############################################################################################################################################ -# $Id: 93_DbLog.pm 22018 2020-05-24 06:33:11Z DS_Starter $ +# $Id: 93_DbLog.pm 22246 2020-06-23 21:12:11Z DS_Starter $ # # 93_DbLog.pm # written by Dr. Boris Neubert 2007-12-30 @@ -8,7 +8,7 @@ # modified and maintained by Tobias Faust since 2012-06-26 until 2016 # e-mail: tobias dot faust at online dot de # -# redesigned and maintained 2016-2020 by DS_Starter with credits by: JoeAllb, DeeSpe +# redesigned and maintained 2016-2021 by DS_Starter with credits by: JoeAllb, DeeSpe # e-mail: heiko dot maaz at t-online dot de # # reduceLog() created by Claudiu Schuster (rapster) @@ -18,7 +18,7 @@ package main; use strict; use warnings; -eval "use DBI;1" or my $DbLogMMDBI = "DBI"; +eval "use DBI;1" or my $DbLogMMDBI = "DBI"; eval "use FHEM::Meta;1" or my $modMetaAbsent = 1; use Data::Dumper; use Blocking; @@ -29,7 +29,9 @@ use HttpUtils; no if $] >= 5.017011, warnings => 'experimental::smartmatch'; # Version History intern by DS_Starter: -our %DbLog_vNotesIntern = ( +my %DbLog_vNotesIntern = ( + "4.11.0" => "20.02.2021 new attr cacheOverflowThreshold, reading CacheOverflowLastnum, remove prototypes ", + "4.10.2" => "23.06.2020 configCheck changed for SQLite again ", "4.10.1" => "22.06.2020 configCheck changed for SQLite ", "4.10.0" => "22.05.2020 improve configCheck, new vars \$LASTTIMESTAMP and \$LASTVALUE in valueFn / DbLogValueFn, Forum:#111423 ", "4.9.13" => "12.05.2020 commandRef changed, AutoInactiveDestroy => 1 for dbh ", @@ -236,6 +238,7 @@ our %DbLog_vNotesIntern = ( "1.7.1" => "15.12.2016 attr procedure of \"disabled\" changed" ); +# Defaultwerte my %columns = ("DEVICE" => 64, "TYPE" => 64, "EVENT" => 512, @@ -243,13 +246,12 @@ my %columns = ("DEVICE" => 64, "VALUE" => 128, "UNIT" => 32 ); - -sub DbLog_dbReadings($@); -sub DbLog_showChildHandles($$$$); + +my $dblog_cachedef = 500; # default Größe cacheLimit bei asynchronen Betrieb ################################################################ -sub DbLog_Initialize($) { - my ($hash) = @_; +sub DbLog_Initialize { + my $hash = shift; $hash->{DefFn} = "DbLog_Define"; $hash->{UndefFn} = "DbLog_Undef"; @@ -265,6 +267,7 @@ sub DbLog_Initialize($) { "commitMode:basic_ta:on,basic_ta:off,ac:on_ta:on,ac:on_ta:off,ac:off_ta:on ". "cacheEvents:2,1,0 ". "cacheLimit ". + "cacheOverflowThreshold ". "colEvent ". "colReading ". "colValue ". @@ -306,10 +309,10 @@ return; } ############################################################### -sub DbLog_Define($@) { +sub DbLog_Define { my ($hash, $def) = @_; my $name = $hash->{NAME}; - my @a = split("[ \t][ \t]*", $def); + my @a = split m{\s+}x, $def; if($DbLogMMDBI) { Log3($name, 1, "DbLog $name - ERROR - Perl module ".$DbLogMMDBI." is missing. DbLog module is not loaded ! On Debian systems you can install it with \"sudo apt-get install libdbi-perl\" "); @@ -361,22 +364,26 @@ sub DbLog_Define($@) { # initial execution of DbLog_execmemcache DbLog_execmemcache($hash); -return undef; +return; } ################################################################ -sub DbLog_Undef($$) { - my ($hash, $name) = @_; - my $dbh= $hash->{DBHP}; +sub DbLog_Undef { + my $hash = shift; + my $name = shift; + my $dbh = $hash->{DBHP}; + BlockingKill($hash->{HELPER}{".RUNNING_PID"}) if($hash->{HELPER}{".RUNNING_PID"}); BlockingKill($hash->{HELPER}{REDUCELOG_PID}) if($hash->{HELPER}{REDUCELOG_PID}); BlockingKill($hash->{HELPER}{COUNT_PID}) if($hash->{HELPER}{COUNT_PID}); BlockingKill($hash->{HELPER}{DELDAYS_PID}) if($hash->{HELPER}{DELDAYS_PID}); + $dbh->disconnect() if(defined($dbh)); + RemoveInternalTimer($hash); delete $data{DbLog}{$name}; -return undef; +return; } ####################################################################################################### @@ -386,8 +393,8 @@ return undef; # Sobald alle nötigen Maßnahmen erledigt sind, muss der Abschluss mit CancelDelayedShutdown($name) an # FHEM zurückgemeldet werden. ####################################################################################################### -sub DbLog_DelayedShutdown($) { - my ($hash) = @_; +sub DbLog_DelayedShutdown { + my $hash = shift; my $name = $hash->{NAME}; my $async = AttrVal($name, "asyncMode", ""); @@ -407,15 +414,15 @@ return 1; # DbLog-Instanz aufgerufen # ################################################################ -sub DbLog_Attr(@) { +sub DbLog_Attr { my($cmd,$name,$aName,$aVal) = @_; - my $hash = $defs{$name}; - my $dbh = $hash->{DBHP}; - my $do = 0; + my $hash = $defs{$name}; + my $dbh = $hash->{DBHP}; + my $do = 0; if($cmd eq "set") { - if ($aName eq "syncInterval" || $aName eq "cacheLimit" || $aName eq "timeout") { - unless ($aVal =~ /^[0-9]+$/) { return " The Value of $aName is not valid. Use only figures 0-9 !";} + if ($aName eq "syncInterval" || $aName eq "cacheLimit" || $aName eq "cacheOverflowThreshold" || $aName eq "timeout") { + if ($aVal !~ /^[0-9]+$/) { return "The Value of $aName is not valid. Use only figures 0-9 !";} } if ($hash->{MODEL} !~ /MYSQL|POSTGRESQL/ && $aName =~ /dbSchema/) { @@ -456,10 +463,13 @@ sub DbLog_Attr(@) { if ($cmd eq "set" && $aVal) { $hash->{MODE} = "asynchronous"; InternalTimer(gettimeofday()+2, "DbLog_execmemcache", $hash, 0); - } else { + } + else { $hash->{MODE} = "synchronous"; delete($defs{$name}{READINGS}{NextSync}); delete($defs{$name}{READINGS}{CacheUsage}); + delete($defs{$name}{READINGS}{CacheOverflowLastnum}); + delete($defs{$name}{READINGS}{background_processing_time}); InternalTimer(gettimeofday()+5, "DbLog_execmemcache", $hash, 0); } } @@ -488,7 +498,8 @@ sub DbLog_Attr(@) { my $regexp = $hash->{REGEXP}; if ($cmd eq "set" && $aVal) { delete($hash->{NOTIFYDEV}); - } else { + } + else { notifyRegexpChanged($hash, $regexp); } } @@ -536,43 +547,46 @@ sub DbLog_Attr(@) { if ($do == 1) { $hash->{HELPER}{TH} = $aVal.".history"; $hash->{HELPER}{TC} = $aVal.".current"; - - } else { + } + else { $hash->{HELPER}{TH} = "history"; $hash->{HELPER}{TC} = "current"; } } -return undef; +return; } ################################################################ -sub DbLog_Set($@) { +sub DbLog_Set { my ($hash, @a) = @_; - my $name = $hash->{NAME}; - my $async = AttrVal($name, "asyncMode", undef); - my $usage = "Unknown argument, choose one of ". - "reduceLog ". - "reduceLogNbl ". - "reopen ". - "rereadcfg:noArg ". - "count:noArg ". - "configCheck:noArg ". - "countNbl:noArg ". - "deleteOldDays ". - "deleteOldDaysNbl ". - "userCommand ". - "clearReadings:noArg ". - "eraseReadings:noArg ". - "addLog " - ; - - $usage .= "listCache:noArg ". - "addCacheLine ". - "purgeCache:noArg ". - "commitCache:noArg ". - "exportCache:nopurge,purgecache " - if (AttrVal($name, "asyncMode", undef)); + my $name = $hash->{NAME}; + my $async = AttrVal($name, "asyncMode", undef); + + my $usage = "Unknown argument, choose one of ". + "reduceLog ". + "reduceLogNbl ". + "reopen ". + "rereadcfg:noArg ". + "count:noArg ". + "configCheck:noArg ". + "countNbl:noArg ". + "deleteOldDays ". + "deleteOldDaysNbl ". + "userCommand ". + "clearReadings:noArg ". + "eraseReadings:noArg ". + "addLog " + ; + + if (AttrVal($name, "asyncMode", undef)) { + $usage .= "listCache:noArg ". + "addCacheLine ". + "purgeCache:noArg ". + "commitCache:noArg ". + "exportCache:nopurge,purgecache " + ; + } my $history = $hash->{HELPER}{TH}; my $current = $hash->{HELPER}{TC}; @@ -594,7 +608,8 @@ sub DbLog_Set($@) { if (@logs) { $usage .= "importCachefile:".$cj." "; - } else { + } + else { $usage .= "importCachefile "; } @@ -612,7 +627,8 @@ sub DbLog_Set($@) { if (defined $a[2] && $a[2] =~ /(^\d+$)|(^\d+:\d+$)/) { $ret = DbLog_reduceLog($hash,@a); InternalTimer(gettimeofday()+5, "DbLog_execmemcache", $hash, 0); - } else { + } + else { Log3($name, 1, "DbLog $name: reduceLog error, no given."); $ret = "reduceLog error, no given."; } @@ -626,7 +642,8 @@ sub DbLog_Set($@) { if (defined $a[2] && $a[2] =~ /(^\d+$)|(^\d+:\d+$)/) { if ($hash->{HELPER}{REDUCELOG_PID} && $hash->{HELPER}{REDUCELOG_PID}{pid} !~ m/DEAD/) { $ret = "reduceLogNbl already in progress. Please wait until the running process is finished."; - } else { + } + else { delete $hash->{HELPER}{REDUCELOG_PID}; my @b = @a; shift(@b); @@ -635,7 +652,8 @@ sub DbLog_Set($@) { $hash->{HELPER}{REDUCELOG_PID} = BlockingCall("DbLog_reduceLogNbl","$name","DbLog_reduceLogNbl_finished"); return; } - } else { + } + else { Log3($name, 1, "DbLog $name: reduceLogNbl syntax error, no [:] given."); $ret = "reduceLogNbl error, no given."; } @@ -686,7 +704,8 @@ sub DbLog_Set($@) { } DbLog_execmemcache($hash) if($async); $ret = "Reopen executed."; - } else { + } + else { unless ($a[2] =~ /^[0-9]+$/) { return " The Value of $a[1]-time is not valid. Use only figures 0-9 !";} # Statusbit "Kein Schreiben in DB erlauben" wenn reopen mit Zeitangabe $hash->{HELPER}{REOPEN_RUNS} = $a[2]; @@ -769,30 +788,31 @@ sub DbLog_Set($@) { elsif ($a[1] eq 'exportCache') { my $cln; my $crows = 0; - my ($out,$outfile,$error); - my $now = strftime('%Y-%m-%d_%H-%M-%S',localtime); + my $now = strftime('%Y-%m-%d_%H-%M-%S',localtime); - # return wenn "reopen" mit Ablaufzeit gestartet ist oder disabled, nicht im asynch-Mode - return if(IsDisabled($name) || $hash->{HELPER}{REOPEN_RUNS}); + my ($out,$outfile,$error); + + return if(IsDisabled($name) || $hash->{HELPER}{REOPEN_RUNS}); # return wenn "reopen" mit Ablaufzeit gestartet ist oder disabled, nicht im asynch-Mode return if(!AttrVal($name, "asyncMode", undef)); - if(@logs && AttrVal($name, "exportCacheAppend", 0)) { - # exportiertes Cachefile existiert und es soll an das neueste angehängt werden + if(@logs && AttrVal($name, "exportCacheAppend", 0)) { # exportiertes Cachefile existiert und es soll an das neueste angehängt werden $outfile = $dir.pop(@logs); $out = ">>$outfile"; - } else { + } + else { $outfile = $dir."cache_".$name."_".$now; $out = ">$outfile"; } if(open(FH, $out)) { binmode (FH); - } else { + } + else { readingsSingleUpdate($hash, "lastCachefile", $outfile." - Error - ".$!, 1); $error = "could not open ".$outfile.": ".$!; } if(!$error) { - foreach my $key (sort(keys %{$data{DbLog}{$name}{cache}{memcache}})) { + for my $key (sort(keys %{$data{DbLog}{$name}{cache}{memcache}})) { $cln = $data{DbLog}{$name}{cache}{memcache}{$key}."\n"; print FH $cln ; $crows++; @@ -806,6 +826,7 @@ sub DbLog_Set($@) { my $state = $error?$error:(IsDisabled($name))?"disabled":"connected"; my $evt = ($state eq $hash->{HELPER}{OLDSTATE})?0:1; readingsSingleUpdate($hash, "state", $state, $evt); + $hash->{HELPER}{OLDSTATE} = $state; Log3($name, 3, "DbLog $name: $crows cache rows exported to $outfile."); @@ -829,15 +850,18 @@ sub DbLog_Set($@) { if (!$a[2]) { return "Wrong function-call. Use set importCachefile without directory (see attr expimpdir)." ; - } else { + } + else { $infile = $dir.$a[2]; } if (open(FH, "$infile")) { binmode (FH); - } else { + } + else { return "could not open ".$infile.": ".$!; } + while () { my $row = $_; $row = DbLog_charfilter($row) if(AttrVal($name, "useCharfilter",0)); @@ -852,7 +876,8 @@ sub DbLog_Set($@) { readingsSingleUpdate($hash, "lastCachefile", $infile." - Error - ".$!, 1); readingsSingleUpdate($hash, "state", $error, 1); Log3 $name, 5, "DbLog $name -> DbLog_Push Returncode: $error"; - } else { + } + else { unless(rename($dir.$a[2], $dir."impdone_".$a[2])) { Log3($name, 2, "DbLog $name: cachefile $infile couldn't be renamed after import !"); } @@ -860,7 +885,8 @@ sub DbLog_Set($@) { readingsSingleUpdate($hash, "state", $crows." cache rows processed from ".$infile, 1); Log3($name, 3, "DbLog $name: $crows cache rows processed from $infile."); } - } else { + } + else { readingsSingleUpdate($hash, "state", "no rows in ".$infile, 1); Log3($name, 3, "DbLog $name: $infile doesn't contain any rows - no imports done."); } @@ -872,7 +898,8 @@ sub DbLog_Set($@) { if(!$dbh) { Log3($name, 1, "DbLog $name: DBLog_Set - count - DB connect not possible"); return; - } else { + } + else { Log3($name, 4, "DbLog $name: Records count requested."); my $c = $dbh->selectrow_array("SELECT count(*) FROM $history"); readingsSingleUpdate($hash, 'countHistory', $c ,1); @@ -886,7 +913,8 @@ sub DbLog_Set($@) { elsif ($a[1] eq 'countNbl') { if ($hash->{HELPER}{COUNT_PID} && $hash->{HELPER}{COUNT_PID}{pid} !~ m/DEAD/){ $ret = "DbLog count already in progress. Please wait until the running process is finished."; - } else { + } + else { delete $hash->{HELPER}{COUNT_PID}; $hash->{HELPER}{COUNT_PID} = BlockingCall("DbLog_countNbl","$name","DbLog_countNbl_finished"); return; @@ -900,13 +928,14 @@ sub DbLog_Set($@) { if(!$dbh) { Log3($name, 1, "DbLog $name: DBLog_Set - deleteOldDays - DB connect not possible"); return; - } else { + } + else { $cmd = "delete from $history where TIMESTAMP < "; if ($hash->{MODEL} eq 'SQLITE') { $cmd .= "datetime('now', '-$a[2] days')"; } elsif ($hash->{MODEL} eq 'MYSQL') { $cmd .= "DATE_SUB(CURDATE(),INTERVAL $a[2] DAY)"; } elsif ($hash->{MODEL} eq 'POSTGRESQL') { $cmd .= "NOW() - INTERVAL '$a[2]' DAY"; } - else { $cmd = undef; $ret = 'Unknown database type. Maybe you can try userCommand anyway.'; } + else { $cmd = undef; $ret = 'Unknown database type. Maybe you can try userCommand anyway.'; } if(defined($cmd)) { $c = $dbh->do($cmd); @@ -931,7 +960,8 @@ sub DbLog_Set($@) { $hash->{HELPER}{DELDAYS_PID} = BlockingCall("DbLog_deldaysNbl","$name","DbLog_deldaysNbl_done"); return; } - } else { + } + else { Log3($name, 1, "DbLog $name: deleteOldDaysNbl error, no given."); $ret = "deleteOldDaysNbl error, no given."; } @@ -941,7 +971,8 @@ sub DbLog_Set($@) { if(!$dbh) { Log3($name, 1, "DbLog $name: DBLog_Set - userCommand - DB connect not possible"); return; - } else { + } + else { Log3($name, 4, "DbLog $name: userCommand execution requested."); my ($c, @cmd, $sql); @cmd = @a; @@ -981,7 +1012,7 @@ return $ret; # Parameter: Quell-Instanz-Name, und alle FileLog-Parameter, die diese Instanz betreffen. # Quelle: http://forum.fhem.de/index.php/topic,40176.msg325200.html#msg325200 ############################################################################################### -sub DbLog_regexpFn($$) { +sub DbLog_regexpFn { my ($name, $filter) = @_; my $ret; @@ -1001,7 +1032,7 @@ return $ret; ################################################################ # Parsefunktion, abhaengig vom Devicetyp ################################################################ -sub DbLog_ParseEvent($$$$) { +sub DbLog_ParseEvent { my ($name,$device, $type, $event)= @_; my (@result,$reading,$value,$unit); @@ -1019,7 +1050,8 @@ sub DbLog_ParseEvent($$$$) { if(@parts == 2) { $value = $parts[0]; $unit = $parts[1]; - } else { + } + else { $value = join(": ", @parts); $unit = ""; } @@ -1032,7 +1064,8 @@ sub DbLog_ParseEvent($$$$) { if($value eq "") { # Default Splitting geändert 04.01.20 Forum: #106992 if($event =~ /^.*:\s$/) { # und 21.01.20 Forum: #106769 $reading = (split(":", $event))[0]; - } else { + } + else { $reading = "state"; $value = $event; } @@ -1055,7 +1088,8 @@ sub DbLog_ParseEvent($$$$) { if(int(@parts) > 1) { $reading = "data"; $value = $event; - } else { + } + else { @parts = split(/\|/, AttrVal($device, $reading."VUnit", "")); $unit = $parts[1] if($parts[1]); if(lc($reading) =~ m/temp/) { @@ -1073,7 +1107,8 @@ sub DbLog_ParseEvent($$$$) { if(int(@parts)>1) { $reading = "data"; $value = $event; - } else { + } + else { @parts = split(/\|/, AttrVal($device, $reading."Unit", "")); $unit = $parts[1] if($parts[1]); } @@ -1151,7 +1186,8 @@ sub DbLog_ParseEvent($$$$) { } elsif($value eq "pair") { $reading = "actuator-pair"; undef $value; - } else { + } + else { $value =~ s/%//; $value = $value*1.; $unit = "%"; } } @@ -1220,7 +1256,8 @@ sub DbLog_ParseEvent($$$$) { } elsif($reading eq "battery") { if ($value =~ m/(\d+)\%/) { $value = $1; - } else { + } + else { $value = ($value eq "ok"); } } @@ -1280,15 +1317,15 @@ sub DbLog_ParseEvent($$$$) { # $hash->{NOTIFYDEV} = "global"; # $hash->{NOTIFYDEV} = "global,Definition_A,Definition_B"; -sub DbLog_Log($$) { +sub DbLog_Log { # $hash is my entry, $dev_hash is the entry of the changed device my ($hash, $dev_hash) = @_; - my $name = $hash->{NAME}; - my $dev_name = $dev_hash->{NAME}; - my $dev_type = uc($dev_hash->{TYPE}); - my $async = AttrVal($name, "asyncMode", undef); - my $clim = AttrVal($name, "cacheLimit", 500); - my $ce = AttrVal($name, "cacheEvents", 0); + my $name = $hash->{NAME}; + my $dev_name = $dev_hash->{NAME}; + my $dev_type = uc($dev_hash->{TYPE}); + my $async = AttrVal($name, "asyncMode", 0 ); + my $clim = AttrVal($name, "cacheLimit", $dblog_cachedef ); + my $ce = AttrVal($name, "cacheEvents", 0 ); my ($net,$force); return if(IsDisabled($name) || !$hash->{HELPER}{COLSET} || $init_done != 1); @@ -1306,7 +1343,8 @@ sub DbLog_Log($$) { my @vb4devs = split(",", AttrVal($name, "verbose4Devs", "")); if (!@vb4devs) { $vb4show = 1; - } else { + } + else { foreach (@vb4devs) { if($dev_name =~ m/$_/i) { $vb4show = 1; @@ -1336,14 +1374,16 @@ sub DbLog_Log($$) { # Funktion aus Device spezifischer DbLogValueFn validieren if( $DbLogValueFn =~ m/^\s*(\{.*\})\s*$/s ) { $DbLogValueFn = $1; - } else { + } + else { $DbLogValueFn = ''; } # Funktion aus Attr valueFn validieren if( $value_fn =~ m/^\s*(\{.*\})\s*$/s ) { $value_fn = $1; - } else { + } + else { $value_fn = ''; } @@ -1389,7 +1429,8 @@ sub DbLog_Log($$) { Log3 $name, 4, "DbLog $name -> Device:Reading \"$dev_name:$reading\" global excluded from logging by attribute \"excludeDevs\" " if($vb4show && !$hash->{HELPER}{".RUNNING_PID"}); $next = 1; } - } else { + } + else { if($dev_name =~ m/^$_$/) { Log3 $name, 4, "DbLog $name -> Device \"$dev_name\" global excluded from logging by attribute \"excludeDevs\" " if($vb4show && !$hash->{HELPER}{".RUNNING_PID"}); $next = 1; @@ -1502,7 +1543,8 @@ sub DbLog_Log($$) { eval { my $epoch_seconds_begin = timelocal($sec, $min, $hh, $dd, $mm-1, $yyyy-1900); }; if (!$@) { $timestamp = $TIMESTAMP; - } else { + } + else { Log3 ($name, 2, "DbLog $name -> TIMESTAMP got from DbLogValueFn in $dev_name is invalid: $TIMESTAMP"); } $reading = $READING if($READING ne ''); @@ -1539,7 +1581,8 @@ sub DbLog_Log($$) { eval { my $epoch_seconds_begin = timelocal($sec, $min, $hh, $dd, $mm-1, $yyyy-1900); }; if (!$@) { $timestamp = $TIMESTAMP; - } else { + } + else { Log3 ($name, 2, "DbLog $name -> Parameter TIMESTAMP got from valueFn is invalid: $TIMESTAMP"); } $dev_name = $DEVICE if($DEVICE ne ''); @@ -1566,7 +1609,8 @@ sub DbLog_Log($$) { my $memcount = $data{DbLog}{$name}{cache}{memcache}?scalar(keys %{$data{DbLog}{$name}{cache}{memcache}}):0; if($ce == 1) { readingsSingleUpdate($hash, "CacheUsage", $memcount, 1); - } else { + } + else { readingsSingleUpdate($hash, 'CacheUsage', $memcount, 0); } # asynchrone Schreibroutine aufrufen wenn Füllstand des Cache erreicht ist @@ -1581,7 +1625,8 @@ sub DbLog_Log($$) { } # Notify-Routine Laufzeit ermitteln $net = tv_interval($nst); - } else { + } + else { # synchoner Mode push(@row_array, $row); } @@ -1627,7 +1672,7 @@ return; # (kein Überschreiben spezifischer Angaben von DbLogExclude / DbLogInclude in den Quelldevices) # ################################################################################################# -sub DbLog_checkDefMinInt ($$$$$){ +sub DbLog_checkDefMinInt { my ($name,$dev_name,$now,$reading,$value) = @_; my $force; my $DoIt = 1; @@ -1689,7 +1734,7 @@ return $DoIt; ################################################################################################# # Schreibroutine Einfügen Werte in DB im Synchronmode ################################################################################################# -sub DbLog_Push(@) { +sub DbLog_Push { my ($hash, $vb4show, @row_array) = @_; my $name = $hash->{NAME}; my $DbLogType = AttrVal($name, "DbLogType", "History"); @@ -1710,7 +1755,8 @@ sub DbLog_Push(@) { if ($nh) { $dbh = DbLog_ConnectNewDBH($hash); return if(!$dbh); - } else { + } + else { $dbh = $hash->{DBHP}; eval { if ( !$dbh || not $dbh->ping ) { @@ -1721,7 +1767,8 @@ sub DbLog_Push(@) { if ($@) { Log3($name, 1, "DbLog $name: DBLog_Push - DB Session dead! - $@"); return $@; - } else { + } + else { $dbh = $hash->{DBHP}; } } @@ -1749,7 +1796,8 @@ sub DbLog_Push(@) { my ($usepkh,$usepkc,$pkh,$pkc); if (!$supk) { ($usepkh,$usepkc,$pkh,$pkc) = DbLog_checkUsePK($hash,$dbh); - } else { + } + else { Log3 $hash->{NAME}, 5, "DbLog $name -> Primary Key usage suppressed by attribute noSupportPK"; } @@ -1788,11 +1836,14 @@ sub DbLog_Push(@) { # insert history mit/ohne primary key if ($usepkh && $hash->{MODEL} eq 'MYSQL') { $sqlins = "INSERT IGNORE INTO $history (TIMESTAMP, DEVICE, TYPE, EVENT, READING, VALUE, UNIT) VALUES "; - } elsif ($usepkh && $hash->{MODEL} eq 'SQLITE') { + } + elsif ($usepkh && $hash->{MODEL} eq 'SQLITE') { $sqlins = "INSERT OR IGNORE INTO $history (TIMESTAMP, DEVICE, TYPE, EVENT, READING, VALUE, UNIT) VALUES "; - } elsif ($usepkh && $hash->{MODEL} eq 'POSTGRESQL') { + } + elsif ($usepkh && $hash->{MODEL} eq 'POSTGRESQL') { $sqlins = "INSERT INTO $history (TIMESTAMP, DEVICE, TYPE, EVENT, READING, VALUE, UNIT) VALUES "; - } else { + } + else { # ohne PK $sqlins = "INSERT INTO $history (TIMESTAMP, DEVICE, TYPE, EVENT, READING, VALUE, UNIT) VALUES "; } @@ -1832,20 +1883,24 @@ sub DbLog_Push(@) { if($ins_hist == $ceti) { Log3 $hash->{NAME}, 4, "DbLog $name -> $ins_hist of $ceti events inserted into table $history".($usepkh?" using PK on columns $pkh":""); - } else { + } + else { if($usepkh) { Log3 $hash->{NAME}, 3, "DbLog $name -> INFO - ".$ins_hist." of $ceti events inserted into table $history due to PK on columns $pkh"; - } else { + } + else { Log3 $hash->{NAME}, 2, "DbLog $name -> WARNING - only ".$ins_hist." of $ceti events inserted into table $history"; } } eval {$dbh->commit() if(!$dbh->{AutoCommit});}; # Data commit if ($@) { Log3($name, 2, "DbLog $name -> Error commit $history - $@"); - } else { + } + else { if(!$dbh->{AutoCommit}) { Log3($name, 4, "DbLog $name -> insert table $history committed"); - } else { + } + else { Log3($name, 4, "DbLog $name -> insert table $history committed by autocommit"); } } @@ -1857,7 +1912,8 @@ sub DbLog_Push(@) { eval {$dbh->rollback() if(!$dbh->{AutoCommit});}; # issue Turning on AutoCommit failed if ($@) { Log3($name, 2, "DbLog $name -> Error rollback $history - $@"); - } else { + } + else { Log3($name, 4, "DbLog $name -> insert $history rolled back"); } } @@ -1870,11 +1926,14 @@ sub DbLog_Push(@) { # die nicht upgedateten Sätze nicht identifiziert werden können if ($usepkc && $hash->{MODEL} eq 'MYSQL') { eval { $sth_ic = $dbh->prepare("INSERT IGNORE INTO $current (TIMESTAMP, DEVICE, TYPE, EVENT, READING, VALUE, UNIT) VALUES (?,?,?,?,?,?,?)"); }; - } elsif ($usepkc && $hash->{MODEL} eq 'SQLITE') { + } + elsif ($usepkc && $hash->{MODEL} eq 'SQLITE') { eval { $sth_ic = $dbh->prepare("INSERT OR IGNORE INTO $current (TIMESTAMP, DEVICE, TYPE, EVENT, READING, VALUE, UNIT) VALUES (?,?,?,?,?,?,?)"); }; - } elsif ($usepkc && $hash->{MODEL} eq 'POSTGRESQL') { + } + elsif ($usepkc && $hash->{MODEL} eq 'POSTGRESQL') { eval { $sth_ic = $dbh->prepare("INSERT INTO $current (TIMESTAMP, DEVICE, TYPE, EVENT, READING, VALUE, UNIT) VALUES (?,?,?,?,?,?,?) ON CONFLICT DO NOTHING"); }; - } else { + } + else { # ohne PK eval { $sth_ic = $dbh->prepare("INSERT INTO $current (TIMESTAMP, DEVICE, TYPE, EVENT, READING, VALUE, UNIT) VALUES (?,?,?,?,?,?,?)"); }; } @@ -1890,7 +1949,8 @@ sub DbLog_Push(@) { $sth_uc = $dbh->prepare("INSERT INTO $current (TIMESTAMP, TYPE, EVENT, VALUE, UNIT, DEVICE, READING) VALUES (?,?,?,?,?,?,?) ON CONFLICT ($pkc) DO UPDATE SET TIMESTAMP=EXCLUDED.TIMESTAMP, DEVICE=EXCLUDED.DEVICE, TYPE=EXCLUDED.TYPE, EVENT=EXCLUDED.EVENT, READING=EXCLUDED.READING, VALUE=EXCLUDED.VALUE, UNIT=EXCLUDED.UNIT"); - } else { + } + else { $sth_uc = $dbh->prepare("UPDATE $current SET TIMESTAMP=?, TYPE=?, EVENT=?, VALUE=?, UNIT=? WHERE (DEVICE=?) AND (READING=?)"); } @@ -1931,7 +1991,8 @@ sub DbLog_Push(@) { } if(!$nupd_cur) { Log3 $hash->{NAME}, 4, "DbLog $name -> $ceti of $ceti events updated in table $current".($usepkc?" using PK on columns $pkc":""); - } else { + } + else { Log3 $hash->{NAME}, 4, "DbLog $name -> $nupd_cur of $ceti events not updated and try to insert into table $current".($usepkc?" using PK on columns $pkc":""); $doins = 1; } @@ -1957,24 +2018,27 @@ sub DbLog_Push(@) { } if(!$nins_cur) { Log3 $hash->{NAME}, 4, "DbLog $name -> ".($#device_cur+1)." of ".($#device_cur+1)." events inserted into table $current ".($usepkc?" using PK on columns $pkc":""); - } else { + } + else { Log3 $hash->{NAME}, 4, "DbLog $name -> ".($#device_cur+1-$nins_cur)." of ".($#device_cur+1)." events inserted into table $current".($usepkc?" using PK on columns $pkc":""); } } eval {$dbh->commit() if(!$dbh->{AutoCommit});}; # issue Turning on AutoCommit failed if ($@) { Log3($name, 2, "DbLog $name -> Error commit table $current - $@"); - } else { + } + else { if(!$dbh->{AutoCommit}) { Log3($name, 4, "DbLog $name -> insert / update table $current committed"); - } else { + } + else { Log3($name, 4, "DbLog $name -> insert / update table $current committed by autocommit"); } } }; - } - - } else { + } + } + else { ####################### # Array-Insert ####################### @@ -1986,11 +2050,14 @@ sub DbLog_Push(@) { # insert history mit/ohne primary key if ($usepkh && $hash->{MODEL} eq 'MYSQL') { eval { $sth_ih = $dbh->prepare("INSERT IGNORE INTO $history (TIMESTAMP, DEVICE, TYPE, EVENT, READING, VALUE, UNIT) VALUES (?,?,?,?,?,?,?)"); }; - } elsif ($usepkh && $hash->{MODEL} eq 'SQLITE') { + } + elsif ($usepkh && $hash->{MODEL} eq 'SQLITE') { eval { $sth_ih = $dbh->prepare("INSERT OR IGNORE INTO $history (TIMESTAMP, DEVICE, TYPE, EVENT, READING, VALUE, UNIT) VALUES (?,?,?,?,?,?,?)"); }; - } elsif ($usepkh && $hash->{MODEL} eq 'POSTGRESQL') { + } + elsif ($usepkh && $hash->{MODEL} eq 'POSTGRESQL') { eval { $sth_ih = $dbh->prepare("INSERT INTO $history (TIMESTAMP, DEVICE, TYPE, EVENT, READING, VALUE, UNIT) VALUES (?,?,?,?,?,?,?) ON CONFLICT DO NOTHING"); }; - } else { + } + else { # ohne PK eval { $sth_ih = $dbh->prepare("INSERT INTO $history (TIMESTAMP, DEVICE, TYPE, EVENT, READING, VALUE, UNIT) VALUES (?,?,?,?,?,?,?)"); }; } @@ -2028,20 +2095,24 @@ sub DbLog_Push(@) { } if(!$nins_hist) { Log3 $hash->{NAME}, 4, "DbLog $name -> $ceti of $ceti events inserted into table $history".($usepkh?" using PK on columns $pkh":""); - } else { + } + else { if($usepkh) { Log3 $hash->{NAME}, 3, "DbLog $name -> INFO - ".($ceti-$nins_hist)." of $ceti events inserted into table $history due to PK on columns $pkh"; - } else { + } + else { Log3 $hash->{NAME}, 2, "DbLog $name -> WARNING - only ".($ceti-$nins_hist)." of $ceti events inserted into table $history"; } } eval {$dbh->commit() if(!$dbh->{AutoCommit});}; # Data commit if ($@) { Log3($name, 2, "DbLog $name -> Error commit $history - $@"); - } else { + } + else { if(!$dbh->{AutoCommit}) { Log3($name, 4, "DbLog $name -> insert table $history committed"); - } else { + } + else { Log3($name, 4, "DbLog $name -> insert table $history committed by autocommit"); } } @@ -2053,7 +2124,8 @@ sub DbLog_Push(@) { eval {$dbh->rollback() if(!$dbh->{AutoCommit});}; # issue Turning on AutoCommit failed if ($@) { Log3($name, 2, "DbLog $name -> Error rollback $history - $@"); - } else { + } + else { Log3($name, 4, "DbLog $name -> insert $history rolled back"); } } @@ -2064,11 +2136,14 @@ sub DbLog_Push(@) { # insert current mit/ohne primary key if ($usepkc && $hash->{MODEL} eq 'MYSQL') { eval { $sth_ic = $dbh->prepare("INSERT IGNORE INTO $current (TIMESTAMP, DEVICE, TYPE, EVENT, READING, VALUE, UNIT) VALUES (?,?,?,?,?,?,?)"); }; - } elsif ($usepkc && $hash->{MODEL} eq 'SQLITE') { + } + elsif ($usepkc && $hash->{MODEL} eq 'SQLITE') { eval { $sth_ic = $dbh->prepare("INSERT OR IGNORE INTO $current (TIMESTAMP, DEVICE, TYPE, EVENT, READING, VALUE, UNIT) VALUES (?,?,?,?,?,?,?)"); }; - } elsif ($usepkc && $hash->{MODEL} eq 'POSTGRESQL') { + } + elsif ($usepkc && $hash->{MODEL} eq 'POSTGRESQL') { eval { $sth_ic = $dbh->prepare("INSERT INTO $current (TIMESTAMP, DEVICE, TYPE, EVENT, READING, VALUE, UNIT) VALUES (?,?,?,?,?,?,?) ON CONFLICT DO NOTHING"); }; - } else { + } + else { # ohne PK eval { $sth_ic = $dbh->prepare("INSERT INTO $current (TIMESTAMP, DEVICE, TYPE, EVENT, READING, VALUE, UNIT) VALUES (?,?,?,?,?,?,?)"); }; } @@ -2078,13 +2153,16 @@ sub DbLog_Push(@) { if ($usepkc && $hash->{MODEL} eq 'MYSQL') { $sth_uc = $dbh->prepare("REPLACE INTO $current (TIMESTAMP, TYPE, EVENT, VALUE, UNIT, DEVICE, READING) VALUES (?,?,?,?,?,?,?)"); - } elsif ($usepkc && $hash->{MODEL} eq 'SQLITE') { + } + elsif ($usepkc && $hash->{MODEL} eq 'SQLITE') { $sth_uc = $dbh->prepare("INSERT OR REPLACE INTO $current (TIMESTAMP, TYPE, EVENT, VALUE, UNIT, DEVICE, READING) VALUES (?,?,?,?,?,?,?)"); - } elsif ($usepkc && $hash->{MODEL} eq 'POSTGRESQL') { + } + elsif ($usepkc && $hash->{MODEL} eq 'POSTGRESQL') { $sth_uc = $dbh->prepare("INSERT INTO $current (TIMESTAMP, TYPE, EVENT, VALUE, UNIT, DEVICE, READING) VALUES (?,?,?,?,?,?,?) ON CONFLICT ($pkc) DO UPDATE SET TIMESTAMP=EXCLUDED.TIMESTAMP, DEVICE=EXCLUDED.DEVICE, TYPE=EXCLUDED.TYPE, EVENT=EXCLUDED.EVENT, READING=EXCLUDED.READING, VALUE=EXCLUDED.VALUE, UNIT=EXCLUDED.UNIT"); - } else { + } + else { $sth_uc = $dbh->prepare("UPDATE $current SET TIMESTAMP=?, TYPE=?, EVENT=?, VALUE=?, UNIT=? WHERE (DEVICE=?) AND (READING=?)"); } @@ -2125,7 +2203,8 @@ sub DbLog_Push(@) { } if(!$nupd_cur) { Log3 $hash->{NAME}, 4, "DbLog $name -> $ceti of $ceti events updated in table $current".($usepkc?" using PK on columns $pkc":""); - } else { + } + else { Log3 $hash->{NAME}, 4, "DbLog $name -> $nupd_cur of $ceti events not updated and try to insert into table $current".($usepkc?" using PK on columns $pkc":""); $doins = 1; } @@ -2151,17 +2230,20 @@ sub DbLog_Push(@) { } if(!$nins_cur) { Log3 $hash->{NAME}, 4, "DbLog $name -> ".($#device_cur+1)." of ".($#device_cur+1)." events inserted into table $current ".($usepkc?" using PK on columns $pkc":""); - } else { + } + else { Log3 $hash->{NAME}, 4, "DbLog $name -> ".($#device_cur+1-$nins_cur)." of ".($#device_cur+1)." events inserted into table $current".($usepkc?" using PK on columns $pkc":""); } } eval {$dbh->commit() if(!$dbh->{AutoCommit});}; # issue Turning on AutoCommit failed if ($@) { Log3($name, 2, "DbLog $name -> Error commit table $current - $@"); - } else { + } + else { if(!$dbh->{AutoCommit}) { Log3($name, 4, "DbLog $name -> insert / update table $current committed"); - } else { + } + else { Log3($name, 4, "DbLog $name -> insert / update table $current committed by autocommit"); } } @@ -2195,25 +2277,27 @@ return Encode::encode_utf8($error); } ################################################################################################# -# # MemCache auswerten und Schreibroutine asynchron und non-blocking aufrufen -# ################################################################################################# -sub DbLog_execmemcache ($) { - my ($hash) = @_; +sub DbLog_execmemcache { + my $hash = shift; my $name = $hash->{NAME}; - my $syncival = AttrVal($name, "syncInterval", 30); - my $clim = AttrVal($name, "cacheLimit", 500); - my $async = AttrVal($name, "asyncMode", undef); - my $ce = AttrVal($name, "cacheEvents", 0); - my $timeout = AttrVal($name, "timeout", 86400); - my $DbLogType = AttrVal($name, "DbLogType", "History"); + my $syncival = AttrVal($name, "syncInterval", 30 ); + my $clim = AttrVal($name, "cacheLimit", $dblog_cachedef ); + my $async = AttrVal($name, "asyncMode", 0 ); + my $ce = AttrVal($name, "cacheEvents", 0 ); + my $timeout = AttrVal($name, "timeout", 86400 ); + my $DbLogType = AttrVal($name, "DbLogType", "History" ); + my $coft = AttrVal($name, "cacheOverflowThreshold", 0 ); # Steuerung exportCache statt schreiben in DB + + $coft = ($coft && $coft < $clim) ? $clim : $coft; # cacheOverflowThreshold auf cacheLimit setzen wenn kleiner als cacheLimit + my $dbconn = $hash->{dbconn}; my $dbuser = $hash->{dbuser}; my $dbpassword = $attr{"sec$name"}{secret}; my $dolog = 1; - my $error = 0; - my (@row_array,$memcount,$dbh); + + my (@row_array,$memcount,$dbh,$error,$overflownum); RemoveInternalTimer($hash, "DbLog_execmemcache"); @@ -2222,13 +2306,11 @@ sub DbLog_execmemcache ($) { return; } - # return wenn "reopen" mit Zeitangabe läuft, oder kein asynchroner Mode oder wenn disabled - if(!$async || IsDisabled($name) || $hash->{HELPER}{REOPEN_RUNS}) { + if(!$async || IsDisabled($name) || $hash->{HELPER}{REOPEN_RUNS}) { # return wenn "reopen" mit Zeitangabe läuft, oder kein asynchroner Mode oder wenn disabled return; } - # tote PID's löschen - if($hash->{HELPER}{".RUNNING_PID"} && $hash->{HELPER}{".RUNNING_PID"}{pid} =~ m/DEAD/) { + if($hash->{HELPER}{".RUNNING_PID"} && $hash->{HELPER}{".RUNNING_PID"}{pid} =~ m/DEAD/) { # tote PID's löschen delete $hash->{HELPER}{".RUNNING_PID"}; } if($hash->{HELPER}{REDUCELOG_PID} && $hash->{HELPER}{REDUCELOG_PID}{pid} =~ m/DEAD/) { @@ -2238,8 +2320,7 @@ sub DbLog_execmemcache ($) { delete $hash->{HELPER}{DELDAYS_PID}; } - # bei SQLite Sperrverwaltung Logging wenn andere schreibende Zugriffe laufen - if($hash->{MODEL} eq "SQLITE") { + if($hash->{MODEL} eq "SQLITE") { # bei SQLite Sperrverwaltung Logging wenn andere schreibende Zugriffe laufen if($hash->{HELPER}{DELDAYS_PID}) { $error = "deleteOldDaysNbl is running - resync at NextSync"; $dolog = 0; @@ -2254,41 +2335,53 @@ sub DbLog_execmemcache ($) { } } - $memcount = $data{DbLog}{$name}{cache}{memcache}?scalar(keys %{$data{DbLog}{$name}{cache}{memcache}}):0; - if($ce == 2) { - readingsSingleUpdate($hash, "CacheUsage", $memcount, 1); - } else { - readingsSingleUpdate($hash, 'CacheUsage', $memcount, 0); - } + $memcount = $data{DbLog}{$name}{cache}{memcache} ? scalar(keys %{$data{DbLog}{$name}{cache}{memcache}}) : 0; + $overflownum = ($coft && $memcount >= $coft) ? $memcount-$coft : $memcount >= $clim ? $memcount-$clim : 0; + my $mce = $ce == 2 ? 1 : 0; + + readingsSingleUpdate($hash, "CacheUsage", $memcount, $mce); if($memcount && $dolog && !$hash->{HELPER}{".RUNNING_PID"}) { - Log3 $name, 4, "DbLog $name -> ################################################################"; - Log3 $name, 4, "DbLog $name -> ### New database processing cycle - asynchronous ###"; - Log3 $name, 4, "DbLog $name -> ################################################################"; - Log3 $name, 4, "DbLog $name -> MemCache contains $memcount entries to process"; - Log3 $name, 4, "DbLog $name -> DbLogType is: $DbLogType"; + Log3 ($name, 4, "DbLog $name -> ################################################################"); + Log3 ($name, 4, "DbLog $name -> ### New database processing cycle - asynchronous ###"); + Log3 ($name, 4, "DbLog $name -> ################################################################"); + Log3 ($name, 4, "DbLog $name -> MemCache contains $memcount entries to process"); + Log3 ($name, 4, "DbLog $name -> DbLogType is: $DbLogType"); + + if($coft && $memcount >= $coft) { + Log3 ($name, 2, "DbLog $name -> WARNING - Cache is exported to file instead of logging it to database"); + CommandSet (undef, qq{$name exportCache purgecache}); + readingsSingleUpdate($hash, "CacheOverflowLastnum", $overflownum, 1); + return; + } - foreach my $key (sort(keys %{$data{DbLog}{$name}{cache}{memcache}})) { - Log3 $hash->{NAME}, 5, "DbLog $name -> MemCache contains: ".$data{DbLog}{$name}{cache}{memcache}{$key}; - push(@row_array, delete($data{DbLog}{$name}{cache}{memcache}{$key})); + for my $key (sort(keys %{$data{DbLog}{$name}{cache}{memcache}})) { + Log3 ($hash->{NAME}, 5, "DbLog $name -> MemCache contains: ".$data{DbLog}{$name}{cache}{memcache}{$key}); + push (@row_array, delete($data{DbLog}{$name}{cache}{memcache}{$key})); } - delete $data{DbLog}{$name}{cache}{memcache}; # sicherheitshalber Memory freigeben: https://perlmaven.com/undef-on-perl-arrays-and-hashes , bzw. https://www.effectiveperlprogramming.com/2018/09/undef-a-scalar-to-release-its-memory/ + + delete $data{DbLog}{$name}{cache}{memcache}; # sicherheitshalber Memory freigeben: https://perlmaven.com/undef-on-perl-arrays-and-hashes , bzw. https://www.effectiveperlprogramming.com/2018/09/undef-a-scalar-to-release-its-memory/ my $rowlist = join('§', @row_array); - $rowlist = encode_base64($rowlist,""); + $rowlist = encode_base64($rowlist,""); + $hash->{HELPER}{".RUNNING_PID"} = BlockingCall ( - "DbLog_PushAsync", - "$name|$rowlist", - "DbLog_PushAsyncDone", - $timeout, - "DbLog_PushAsyncAborted", - $hash ); + "DbLog_PushAsync", + "$name|$rowlist", + "DbLog_PushAsyncDone", + $timeout, + "DbLog_PushAsyncAborted", + $hash + ); + $hash->{HELPER}{".RUNNING_PID"}{loglevel} = 4; - Log3 $hash->{NAME}, 5, "DbLog $name -> DbLog_PushAsync called with timeout: $timeout"; - } else { + Log3 ($hash->{NAME}, 5, "DbLog $name -> DbLog_PushAsync called with timeout: $timeout"); + } + else { if($dolog && $hash->{HELPER}{".RUNNING_PID"}) { $error = "Commit already running - resync at NextSync"; - } else { + } + else { CancelDelayedShutdown($name) if($hash->{HELPER}{SHUTDOWNSEQ}); Log3 ($name, 2, "DbLog $name - no data for last database write cycle") if(delete $hash->{HELPER}{SHUTDOWNSEQ}); } @@ -2296,15 +2389,17 @@ sub DbLog_execmemcache ($) { my $nextsync = gettimeofday()+$syncival; my $nsdt = FmtDateTime($nextsync); - - if(AttrVal($name, "syncEvents", undef)) { - readingsSingleUpdate($hash, "NextSync", $nsdt. " or if CacheUsage ".$clim." reached", 1); - } else { - readingsSingleUpdate($hash, "NextSync", $nsdt. " or if CacheUsage ".$clim." reached", 0); - } + my $se = AttrVal($name, "syncEvents", undef) ? 1 : 0; - my $state = $error?$error:$hash->{HELPER}{OLDSTATE}; - my $evt = ($state eq $hash->{HELPER}{OLDSTATE})?0:1; + readingsSingleUpdate($hash, "NextSync", $nsdt. " or if CacheUsage ".$clim." reached", $se); + + readingsBeginUpdate($hash); + readingsBulkUpdateIfChanged($hash, "CacheOverflowLastnum", $overflownum, 1); + readingsEndUpdate($hash, 1); + + + my $state = $error // $hash->{HELPER}{OLDSTATE}; + my $evt = ($state eq $hash->{HELPER}{OLDSTATE}) ? 0 : 1; readingsSingleUpdate($hash, "state", $state, $evt); $hash->{HELPER}{OLDSTATE} = $state; @@ -2318,7 +2413,7 @@ return; # Schreibroutine Einfügen Werte in DB asynchron non-blocking # ################################################################################################# -sub DbLog_PushAsync(@) { +sub DbLog_PushAsync { my ($string) = @_; my ($name,$rowlist) = split("\\|", $string); my $hash = $defs{$name}; @@ -2349,9 +2444,11 @@ sub DbLog_PushAsync(@) { eval { if(!$useac) { $dbh = DBI->connect("dbi:$dbconn", $dbuser, $dbpassword, { PrintError => 0, RaiseError => 1, AutoCommit => 0, AutoInactiveDestroy => 1, mysql_enable_utf8 => $utf8 }); - } elsif($useac == 1) { + } + elsif($useac == 1) { $dbh = DBI->connect("dbi:$dbconn", $dbuser, $dbpassword, { PrintError => 0, RaiseError => 1, AutoCommit => 1, AutoInactiveDestroy => 1, mysql_enable_utf8 => $utf8 }); - } else { + } + else { # Server default $dbh = DBI->connect("dbi:$dbconn", $dbuser, $dbpassword, { PrintError => 0, RaiseError => 1, AutoInactiveDestroy => 1, mysql_enable_utf8 => $utf8 }); } @@ -2377,7 +2474,8 @@ sub DbLog_PushAsync(@) { my ($usepkh,$usepkc,$pkh,$pkc); if (!$supk) { ($usepkh,$usepkc,$pkh,$pkc) = DbLog_checkUsePK($hash,$dbh); - } else { + } + else { Log3 $hash->{NAME}, 5, "DbLog $name -> Primary Key usage suppressed by attribute noSupportPK"; } @@ -2416,11 +2514,14 @@ sub DbLog_PushAsync(@) { # insert history mit/ohne primary key if ($usepkh && $hash->{MODEL} eq 'MYSQL') { $sqlins = "INSERT IGNORE INTO $history (TIMESTAMP, DEVICE, TYPE, EVENT, READING, VALUE, UNIT) VALUES "; - } elsif ($usepkh && $hash->{MODEL} eq 'SQLITE') { + } + elsif ($usepkh && $hash->{MODEL} eq 'SQLITE') { $sqlins = "INSERT OR IGNORE INTO $history (TIMESTAMP, DEVICE, TYPE, EVENT, READING, VALUE, UNIT) VALUES "; - } elsif ($usepkh && $hash->{MODEL} eq 'POSTGRESQL') { + } + elsif ($usepkh && $hash->{MODEL} eq 'POSTGRESQL') { $sqlins = "INSERT INTO $history (TIMESTAMP, DEVICE, TYPE, EVENT, READING, VALUE, UNIT) VALUES "; - } else { + } + else { # ohne PK $sqlins = "INSERT INTO $history (TIMESTAMP, DEVICE, TYPE, EVENT, READING, VALUE, UNIT) VALUES "; } @@ -2459,20 +2560,24 @@ sub DbLog_PushAsync(@) { if($ins_hist == $ceti) { Log3 $hash->{NAME}, 4, "DbLog $name -> $ins_hist of $ceti events inserted into table $history".($usepkh?" using PK on columns $pkh":""); - } else { + } + else { if($usepkh) { Log3 $hash->{NAME}, 3, "DbLog $name -> INFO - ".$ins_hist." of $ceti events inserted into table $history due to PK on columns $pkh"; - } else { + } + else { Log3 $hash->{NAME}, 2, "DbLog $name -> WARNING - only ".$ins_hist." of $ceti events inserted into table $history"; } } eval {$dbh->commit() if(!$dbh->{AutoCommit});}; # Data commit if ($@) { Log3($name, 2, "DbLog $name -> Error commit $history - $@"); - } else { + } + else { if(!$dbh->{AutoCommit}) { Log3($name, 4, "DbLog $name -> insert table $history committed"); - } else { + } + else { Log3($name, 4, "DbLog $name -> insert table $history committed by autocommit"); } } @@ -2493,11 +2598,14 @@ sub DbLog_PushAsync(@) { # die nicht upgedateten Sätze nicht identifiziert werden können if ($usepkc && $hash->{MODEL} eq 'MYSQL') { eval { $sth_ic = $dbh->prepare("INSERT IGNORE INTO $current (TIMESTAMP, DEVICE, TYPE, EVENT, READING, VALUE, UNIT) VALUES (?,?,?,?,?,?,?)"); }; - } elsif ($usepkc && $hash->{MODEL} eq 'SQLITE') { + } + elsif ($usepkc && $hash->{MODEL} eq 'SQLITE') { eval { $sth_ic = $dbh->prepare("INSERT OR IGNORE INTO $current (TIMESTAMP, DEVICE, TYPE, EVENT, READING, VALUE, UNIT) VALUES (?,?,?,?,?,?,?)"); }; - } elsif ($usepkc && $hash->{MODEL} eq 'POSTGRESQL') { + } + elsif ($usepkc && $hash->{MODEL} eq 'POSTGRESQL') { eval { $sth_ic = $dbh->prepare("INSERT INTO $current (TIMESTAMP, DEVICE, TYPE, EVENT, READING, VALUE, UNIT) VALUES (?,?,?,?,?,?,?) ON CONFLICT DO NOTHING"); }; - } else { + } + else { # ohne PK eval { $sth_ic = $dbh->prepare("INSERT INTO $current (TIMESTAMP, DEVICE, TYPE, EVENT, READING, VALUE, UNIT) VALUES (?,?,?,?,?,?,?)"); }; } @@ -2511,13 +2619,16 @@ sub DbLog_PushAsync(@) { if ($usepkc && $hash->{MODEL} eq 'MYSQL') { $sth_uc = $dbh->prepare("REPLACE INTO $current (TIMESTAMP, TYPE, EVENT, VALUE, UNIT, DEVICE, READING) VALUES (?,?,?,?,?,?,?)"); - } elsif ($usepkc && $hash->{MODEL} eq 'SQLITE') { + } + elsif ($usepkc && $hash->{MODEL} eq 'SQLITE') { $sth_uc = $dbh->prepare("INSERT OR REPLACE INTO $current (TIMESTAMP, TYPE, EVENT, VALUE, UNIT, DEVICE, READING) VALUES (?,?,?,?,?,?,?)"); - } elsif ($usepkc && $hash->{MODEL} eq 'POSTGRESQL') { + } + elsif ($usepkc && $hash->{MODEL} eq 'POSTGRESQL') { $sth_uc = $dbh->prepare("INSERT INTO $current (TIMESTAMP, TYPE, EVENT, VALUE, UNIT, DEVICE, READING) VALUES (?,?,?,?,?,?,?) ON CONFLICT ($pkc) DO UPDATE SET TIMESTAMP=EXCLUDED.TIMESTAMP, DEVICE=EXCLUDED.DEVICE, TYPE=EXCLUDED.TYPE, EVENT=EXCLUDED.EVENT, READING=EXCLUDED.READING, VALUE=EXCLUDED.VALUE, UNIT=EXCLUDED.UNIT"); - } else { + } + else { $sth_uc = $dbh->prepare("UPDATE $current SET TIMESTAMP=?, TYPE=?, EVENT=?, VALUE=?, UNIT=? WHERE (DEVICE=?) AND (READING=?)"); } @@ -2558,7 +2669,8 @@ sub DbLog_PushAsync(@) { } if(!$nupd_cur) { Log3 $hash->{NAME}, 4, "DbLog $name -> $ceti of $ceti events updated in table $current".($usepkc?" using PK on columns $pkc":""); - } else { + } + else { Log3 $hash->{NAME}, 4, "DbLog $name -> $nupd_cur of $ceti events not updated and try to insert into table $current".($usepkc?" using PK on columns $pkc":""); $doins = 1; } @@ -2584,24 +2696,27 @@ sub DbLog_PushAsync(@) { } if(!$nins_cur) { Log3 $hash->{NAME}, 4, "DbLog $name -> ".($#device_cur+1)." of ".($#device_cur+1)." events inserted into table $current ".($usepkc?" using PK on columns $pkc":""); - } else { + } + else { Log3 $hash->{NAME}, 4, "DbLog $name -> ".($#device_cur+1-$nins_cur)." of ".($#device_cur+1)." events inserted into table $current".($usepkc?" using PK on columns $pkc":""); } } eval {$dbh->commit() if(!$dbh->{AutoCommit});}; # issue Turning on AutoCommit failed if ($@) { Log3($name, 2, "DbLog $name -> Error commit table $current - $@"); - } else { + } + else { if(!$dbh->{AutoCommit}) { Log3($name, 4, "DbLog $name -> insert / update table $current committed"); - } else { + } + else { Log3($name, 4, "DbLog $name -> insert / update table $current committed by autocommit"); } } }; - } - - } else { + } + } + else { ####################### # Array-Insert ####################### @@ -2613,11 +2728,14 @@ sub DbLog_PushAsync(@) { # insert history mit/ohne primary key if ($usepkh && $hash->{MODEL} eq 'MYSQL') { eval { $sth_ih = $dbh->prepare("INSERT IGNORE INTO $history (TIMESTAMP, DEVICE, TYPE, EVENT, READING, VALUE, UNIT) VALUES (?,?,?,?,?,?,?)"); }; - } elsif ($usepkh && $hash->{MODEL} eq 'SQLITE') { + } + elsif ($usepkh && $hash->{MODEL} eq 'SQLITE') { eval { $sth_ih = $dbh->prepare("INSERT OR IGNORE INTO $history (TIMESTAMP, DEVICE, TYPE, EVENT, READING, VALUE, UNIT) VALUES (?,?,?,?,?,?,?)"); }; - } elsif ($usepkh && $hash->{MODEL} eq 'POSTGRESQL') { + } + elsif ($usepkh && $hash->{MODEL} eq 'POSTGRESQL') { eval { $sth_ih = $dbh->prepare("INSERT INTO $history (TIMESTAMP, DEVICE, TYPE, EVENT, READING, VALUE, UNIT) VALUES (?,?,?,?,?,?,?) ON CONFLICT DO NOTHING"); }; - } else { + } + else { # ohne PK eval { $sth_ih = $dbh->prepare("INSERT INTO $history (TIMESTAMP, DEVICE, TYPE, EVENT, READING, VALUE, UNIT) VALUES (?,?,?,?,?,?,?)"); }; } @@ -2662,10 +2780,12 @@ sub DbLog_PushAsync(@) { } if(!$nins_hist) { Log3 $hash->{NAME}, 4, "DbLog $name -> $ceti of $ceti events inserted into table $history".($usepkh?" using PK on columns $pkh":""); - } else { + } + else { if($usepkh) { Log3 $hash->{NAME}, 3, "DbLog $name -> INFO - ".($ceti-$nins_hist)." of $ceti events inserted into table history due to PK on columns $pkh"; - } else { + } + else { Log3 $hash->{NAME}, 2, "DbLog $name -> WARNING - only ".($ceti-$nins_hist)." of $ceti events inserted into table $history"; } s/\|/_ESC_/g for @n2hist; # escape Pipe "|" @@ -2675,10 +2795,12 @@ sub DbLog_PushAsync(@) { eval {$dbh->commit() if(!$dbh->{AutoCommit});}; # Data commit if ($@) { Log3($name, 2, "DbLog $name -> Error commit $history - $@"); - } else { + } + else { if(!$dbh->{AutoCommit}) { Log3($name, 4, "DbLog $name -> insert table $history committed"); - } else { + } + else { Log3($name, 4, "DbLog $name -> insert table $history committed by autocommit"); } } @@ -2697,11 +2819,14 @@ sub DbLog_PushAsync(@) { # insert current mit/ohne primary key if ($usepkc && $hash->{MODEL} eq 'MYSQL') { eval { $sth_ic = $dbh->prepare("INSERT IGNORE INTO $current (TIMESTAMP, DEVICE, TYPE, EVENT, READING, VALUE, UNIT) VALUES (?,?,?,?,?,?,?)"); }; - } elsif ($usepkc && $hash->{MODEL} eq 'SQLITE') { + } + elsif ($usepkc && $hash->{MODEL} eq 'SQLITE') { eval { $sth_ic = $dbh->prepare("INSERT OR IGNORE INTO $current (TIMESTAMP, DEVICE, TYPE, EVENT, READING, VALUE, UNIT) VALUES (?,?,?,?,?,?,?)"); }; - } elsif ($usepkc && $hash->{MODEL} eq 'POSTGRESQL') { + } + elsif ($usepkc && $hash->{MODEL} eq 'POSTGRESQL') { eval { $sth_ic = $dbh->prepare("INSERT INTO $current (TIMESTAMP, DEVICE, TYPE, EVENT, READING, VALUE, UNIT) VALUES (?,?,?,?,?,?,?) ON CONFLICT DO NOTHING"); }; - } else { + } + else { # ohne PK eval { $sth_ic = $dbh->prepare("INSERT INTO $current (TIMESTAMP, DEVICE, TYPE, EVENT, READING, VALUE, UNIT) VALUES (?,?,?,?,?,?,?)"); }; } @@ -2722,7 +2847,8 @@ sub DbLog_PushAsync(@) { $sth_uc = $dbh->prepare("INSERT INTO $current (TIMESTAMP, TYPE, EVENT, VALUE, UNIT, DEVICE, READING) VALUES (?,?,?,?,?,?,?) ON CONFLICT ($pkc) DO UPDATE SET TIMESTAMP=EXCLUDED.TIMESTAMP, DEVICE=EXCLUDED.DEVICE, TYPE=EXCLUDED.TYPE, EVENT=EXCLUDED.EVENT, READING=EXCLUDED.READING, VALUE=EXCLUDED.VALUE, UNIT=EXCLUDED.UNIT"); - } else { + } + else { $sth_uc = $dbh->prepare("UPDATE $current SET TIMESTAMP=?, TYPE=?, EVENT=?, VALUE=?, UNIT=? WHERE (DEVICE=?) AND (READING=?)"); } @@ -2763,7 +2889,8 @@ sub DbLog_PushAsync(@) { } if(!$nupd_cur) { Log3 $hash->{NAME}, 4, "DbLog $name -> $ceti of $ceti events updated in table $current".($usepkc?" using PK on columns $pkc":""); - } else { + } + else { Log3 $hash->{NAME}, 4, "DbLog $name -> $nupd_cur of $ceti events not updated and try to insert into table $current".($usepkc?" using PK on columns $pkc":""); $doins = 1; } @@ -2789,17 +2916,20 @@ sub DbLog_PushAsync(@) { } if(!$nins_cur) { Log3 $hash->{NAME}, 4, "DbLog $name -> ".($#device_cur+1)." of ".($#device_cur+1)." events inserted into table $current ".($usepkc?" using PK on columns $pkc":""); - } else { + } + else { Log3 $hash->{NAME}, 4, "DbLog $name -> ".($#device_cur+1-$nins_cur)." of ".($#device_cur+1)." events inserted into table $current".($usepkc?" using PK on columns $pkc":""); } } eval {$dbh->commit() if(!$dbh->{AutoCommit});}; # issue Turning on AutoCommit failed if ($@) { Log3($name, 2, "DbLog $name -> Error commit table $current - $@"); - } else { + } + else { if(!$dbh->{AutoCommit}) { Log3($name, 4, "DbLog $name -> insert / update table $current committed"); - } else { + } + else { Log3($name, 4, "DbLog $name -> insert / update table $current committed by autocommit"); } } @@ -2825,7 +2955,7 @@ return "$name|$error|$rt|$rowlback"; ############################################################################################# # Auswertung non-blocking asynchron DbLog_PushAsync ############################################################################################# -sub DbLog_PushAsyncDone ($) { +sub DbLog_PushAsyncDone { my ($string) = @_; my @a = split("\\|",$string); my $name = $a[0]; @@ -2889,7 +3019,7 @@ return; ############################################################################################# # Abbruchroutine Timeout non-blocking asynchron DbLog_PushAsync ############################################################################################# -sub DbLog_PushAsyncAborted(@) { +sub DbLog_PushAsyncAborted { my ($hash,$cause) = @_; my $name = $hash->{NAME}; $cause = $cause?$cause:"Timeout: process terminated"; @@ -2912,7 +3042,7 @@ return; # uebergebenes SQL-Format: YYYY-MM-DD HH24:MI:SS # ################################################################ -sub DbLog_explode_datetime($%) { +sub DbLog_explode_datetime { my ($t, %def) = @_; my %retv; @@ -2944,7 +3074,7 @@ return $retv; ################################################################################### # Verbindungen zur DB aufbauen ################################################################################### -sub DbLog_readCfg($){ +sub DbLog_readCfg { my ($hash)= @_; my $name = $hash->{NAME}; @@ -2967,13 +3097,17 @@ sub DbLog_readCfg($){ #check the database model if($hash->{dbconn} =~ m/pg:/i) { $hash->{MODEL}="POSTGRESQL"; - } elsif ($hash->{dbconn} =~ m/mysql:/i) { + } + elsif ($hash->{dbconn} =~ m/mysql:/i) { $hash->{MODEL}="MYSQL"; - } elsif ($hash->{dbconn} =~ m/oracle:/i) { + } + elsif ($hash->{dbconn} =~ m/oracle:/i) { $hash->{MODEL}="ORACLE"; - } elsif ($hash->{dbconn} =~ m/sqlite:/i) { + } + elsif ($hash->{dbconn} =~ m/sqlite:/i) { $hash->{MODEL}="SQLITE"; - } else { + } + else { $hash->{MODEL}="unknown"; Log3 $hash->{NAME}, 1, "Unknown database model found in configuration file $configfilename."; Log3 $hash->{NAME}, 1, "Only MySQL/MariaDB, PostgreSQL, Oracle, SQLite are fully supported."; @@ -2987,14 +3121,15 @@ sub DbLog_readCfg($){ return; } -sub DbLog_ConnectPush($;$$) { +sub DbLog_ConnectPush { # own $dbhp for synchronous logging and dblog_get - my ($hash,$get)= @_; - my $name = $hash->{NAME}; - my $dbconn = $hash->{dbconn}; - my $dbuser = $hash->{dbuser}; - my $dbpassword = $attr{"sec$name"}{secret}; - my $utf8 = defined($hash->{UTF8})?$hash->{UTF8}:0; + my ($hash,$get) = @_; + my $name = $hash->{NAME}; + my $dbconn = $hash->{dbconn}; + my $dbuser = $hash->{dbuser}; + my $dbpassword = $attr{"sec$name"}{secret}; + my $utf8 = defined($hash->{UTF8})?$hash->{UTF8}:0; + my ($dbhp,$state,$evt,$err); return 0 if(IsDisabled($name)); @@ -3010,9 +3145,11 @@ sub DbLog_ConnectPush($;$$) { eval { if(!$useac) { $dbhp = DBI->connect("dbi:$dbconn", $dbuser, $dbpassword, { PrintError => 0, RaiseError => 1, AutoCommit => 0, AutoInactiveDestroy => 1, mysql_enable_utf8 => $utf8 }); - } elsif($useac == 1) { + } + elsif($useac == 1) { $dbhp = DBI->connect("dbi:$dbconn", $dbuser, $dbpassword, { PrintError => 0, RaiseError => 1, AutoCommit => 1, AutoInactiveDestroy => 1, mysql_enable_utf8 => $utf8 }); - } else { + } + else { # Server default $dbhp = DBI->connect("dbi:$dbconn", $dbuser, $dbpassword, { PrintError => 0, RaiseError => 1, AutoInactiveDestroy => 1, mysql_enable_utf8 => $utf8 }); } @@ -3063,7 +3200,7 @@ sub DbLog_ConnectPush($;$$) { return 1; } -sub DbLog_ConnectNewDBH($) { +sub DbLog_ConnectNewDBH { # new dbh for common use (except DbLog_Push and get-function) my ($hash) = @_; my $name = $hash->{NAME}; @@ -3077,9 +3214,11 @@ sub DbLog_ConnectNewDBH($) { eval { if(!$useac) { $dbh = DBI->connect("dbi:$dbconn", $dbuser, $dbpassword, { PrintError => 0, RaiseError => 1, AutoCommit => 0, AutoInactiveDestroy => 1, mysql_enable_utf8 => $utf8 }); - } elsif($useac == 1) { + } + elsif($useac == 1) { $dbh = DBI->connect("dbi:$dbconn", $dbuser, $dbpassword, { PrintError => 0, RaiseError => 1, AutoCommit => 1, AutoInactiveDestroy => 1, mysql_enable_utf8 => $utf8 }); - } else { + } + else { # Server default $dbh = DBI->connect("dbi:$dbconn", $dbuser, $dbpassword, { PrintError => 0, RaiseError => 1, AutoInactiveDestroy => 1, mysql_enable_utf8 => $utf8 }); } @@ -3097,7 +3236,8 @@ sub DbLog_ConnectNewDBH($) { $dbh->{RaiseError} = 0; $dbh->{PrintError} = 1; return $dbh; - } else { + } + else { return 0; } } @@ -3110,10 +3250,10 @@ sub DbLog_ConnectNewDBH($) { # param2: SQL-Statement # ########################################################################## -sub DbLog_ExecSQL($$) { - my ($hash,$sql)= @_; - my $name = $hash->{NAME}; - my $dbh = DbLog_ConnectNewDBH($hash); +sub DbLog_ExecSQL { + my ($hash,$sql) = @_; + my $name = $hash->{NAME}; + my $dbh = DbLog_ConnectNewDBH($hash); Log3($name, 4, "DbLog $name - Backdoor executing: $sql"); @@ -3140,7 +3280,7 @@ sub DbLog_ExecSQL($$) { return $sth; } -sub DbLog_ExecSQL1($$$) { +sub DbLog_ExecSQL1 { my ($hash,$dbh,$sql)= @_; my $name = $hash->{NAME}; @@ -3165,12 +3305,12 @@ return $sth; # outfile: [-|ALL|INT|WEBCHART] # ################################################################ -sub DbLog_Get($@) { +sub DbLog_Get { my ($hash, @a) = @_; - my $name = $hash->{NAME}; - my $utf8 = defined($hash->{UTF8})?$hash->{UTF8}:0; - my $history = $hash->{HELPER}{TH}; - my $current = $hash->{HELPER}{TC}; + my $name = $hash->{NAME}; + my $utf8 = defined($hash->{UTF8})?$hash->{UTF8}:0; + my $history = $hash->{HELPER}{TH}; + my $current = $hash->{HELPER}{TC}; my ($dbh,$err); return DbLog_dbReadings($hash,@a) if $a[1] =~ m/^Readings/; @@ -3273,8 +3413,8 @@ sub DbLog_Get($@) { if ($nh || $hash->{PID} != $$) { # 17.04.2019 Forum: https://forum.fhem.de/index.php/topic,99719.0.html $dbh = DbLog_ConnectNewDBH($hash); return "Can't connect to database." if(!$dbh); - - } else { + } + else { $dbh = $hash->{DBHP}; eval { if ( !$dbh || not $dbh->ping ) { @@ -3285,7 +3425,8 @@ sub DbLog_Get($@) { if ($@) { Log3($name, 1, "DbLog $name: DBLog_Push - DB Session dead! - $@"); return $@; - } else { + } + else { $dbh = $hash->{DBHP}; } } @@ -3299,28 +3440,32 @@ sub DbLog_Get($@) { $sqlspec{order_by_hour} = "TO_CHAR(TIMESTAMP, 'YYYY-MM-DD HH24')"; $sqlspec{max_value} = "MAX(VALUE)"; $sqlspec{day_before} = "($sqlspec{from_timestamp} - INTERVAL '1 DAY')"; - } elsif ($hash->{MODEL} eq "ORACLE") { + } + elsif ($hash->{MODEL} eq "ORACLE") { $sqlspec{get_timestamp} = "TO_CHAR(TIMESTAMP, 'YYYY-MM-DD HH24:MI:SS')"; $sqlspec{from_timestamp} = "TO_TIMESTAMP('$from', 'YYYY-MM-DD HH24:MI:SS')"; $sqlspec{to_timestamp} = "TO_TIMESTAMP('$to', 'YYYY-MM-DD HH24:MI:SS')"; $sqlspec{order_by_hour} = "TO_CHAR(TIMESTAMP, 'YYYY-MM-DD HH24')"; $sqlspec{max_value} = "MAX(VALUE)"; $sqlspec{day_before} = "DATE_SUB($sqlspec{from_timestamp},INTERVAL 1 DAY)"; - } elsif ($hash->{MODEL} eq "MYSQL") { + } + elsif ($hash->{MODEL} eq "MYSQL") { $sqlspec{get_timestamp} = "DATE_FORMAT(TIMESTAMP, '%Y-%m-%d %H:%i:%s')"; $sqlspec{from_timestamp} = "STR_TO_DATE('$from', '%Y-%m-%d %H:%i:%s')"; $sqlspec{to_timestamp} = "STR_TO_DATE('$to', '%Y-%m-%d %H:%i:%s')"; $sqlspec{order_by_hour} = "DATE_FORMAT(TIMESTAMP, '%Y-%m-%d %H')"; $sqlspec{max_value} = "MAX(VALUE)"; # 12.04.2019 Forum: https://forum.fhem.de/index.php/topic,99280.0.html $sqlspec{day_before} = "DATE_SUB($sqlspec{from_timestamp},INTERVAL 1 DAY)"; - } elsif ($hash->{MODEL} eq "SQLITE") { + } + elsif ($hash->{MODEL} eq "SQLITE") { $sqlspec{get_timestamp} = "TIMESTAMP"; $sqlspec{from_timestamp} = "'$from'"; $sqlspec{to_timestamp} = "'$to'"; $sqlspec{order_by_hour} = "strftime('%Y-%m-%d %H', TIMESTAMP)"; $sqlspec{max_value} = "MAX(VALUE)"; $sqlspec{day_before} = "date($sqlspec{from_timestamp},'-1 day')"; - } else { + } + else { $sqlspec{get_timestamp} = "TIMESTAMP"; $sqlspec{from_timestamp} = "'$from'"; $sqlspec{to_timestamp} = "'$to'"; @@ -3332,7 +3477,8 @@ sub DbLog_Get($@) { if($outf =~ m/(all|array)/) { $sqlspec{all} = ",TYPE,EVENT,UNIT"; $sqlspec{all_max} = ",MAX(TYPE) AS TYPE,MAX(EVENT) AS EVENT,MAX(UNIT) AS UNIT"; - } else { + } + else { $sqlspec{all} = ""; $sqlspec{all_max} = ""; } @@ -3411,8 +3557,8 @@ sub DbLog_Get($@) { $stm .= "GROUP BY $sqlspec{order_by_hour} " if($deltacalc); $stm .= "ORDER BY TIMESTAMP"; - - } else { + } + else { # kein deltacalc $stm = "SELECT $sqlspec{get_timestamp}, @@ -3444,7 +3590,8 @@ sub DbLog_Get($@) { if($outf =~ m/(all|array)/) { $sth->bind_columns(undef, \$sql_timestamp, \$sql_device, \$sql_reading, \$sql_value, \$type, \$event, \$unit); - } else { + } + else { $sth->bind_columns(undef, \$sql_timestamp, \$sql_device, \$sql_reading, \$sql_value); } @@ -3491,8 +3638,8 @@ sub DbLog_Get($@) { $maxval = $sql_value if($sql_value > $maxval || ($maxval = -(~0 >> 1)) ); # geändert V4.8.0 / 14.10.2019 $lastv[$i] = $sql_value; } - - } else { + } + else { $writeout = 0; $out_value = ""; $out_tstamp = ""; @@ -3528,7 +3675,8 @@ sub DbLog_Get($@) { if(lc($sql_value) =~ m(hide)) { $writeout = 0; - } else { + } + else { $writeout = 1; } @@ -3537,7 +3685,8 @@ sub DbLog_Get($@) { if($lastd[$i] eq "undef") { %lasttstamp = DbLog_explode_datetime($sql_timestamp, ()); $lasttstamp{hour} = "00"; - } else { + } + else { %lasttstamp = DbLog_explode_datetime($lastd[$i], ()); } # 04 01 @@ -3560,8 +3709,8 @@ sub DbLog_Get($@) { } elsif ($outf =~ m/(array)/) { push(@ReturnArray, {"tstamp" => $out_tstamp, "device" => $sql_device, "type" => $type, "event" => $event, "reading" => $sql_reading, "value" => $out_value, "unit" => $unit}); - - } else { + } + else { $out_tstamp =~ s/\ /_/g; #needed by generating plots $retvaldummy .= "$out_tstamp $out_value\n"; } @@ -3575,12 +3724,15 @@ sub DbLog_Get($@) { $hour = '0'.$j if $j<10; $cnt[$i]++; $out_tstamp = DbLog_implode_datetime($tstamp{year}, $tstamp{month}, $tstamp{day}, $hour, "30", "00"); + if ($outf =~ m/(all)/) { # Timestamp: Device, Type, Event, Reading, Value, Unit $retvaldummy .= sprintf("%s: %s, %s, %s, %s, %s, %s\n", $out_tstamp, $sql_device, $type, $event, $sql_reading, $out_value, $unit); - } elsif ($outf =~ m/(array)/) { + } + elsif ($outf =~ m/(array)/) { push(@ReturnArray, {"tstamp" => $out_tstamp, "device" => $sql_device, "type" => $type, "event" => $event, "reading" => $sql_reading, "value" => $out_value, "unit" => $unit}); - } else { + } + else { $out_tstamp =~ s/\ /_/g; # needed by generating plots $retvaldummy .= "$out_tstamp $out_value\n"; } @@ -3601,7 +3753,8 @@ sub DbLog_Get($@) { if($lastd[$i] eq "undef") { %lasttstamp = DbLog_explode_datetime($sql_timestamp, ()); - } else { + } + else { %lasttstamp = DbLog_explode_datetime($lastd[$i], ()); } @@ -3615,8 +3768,8 @@ sub DbLog_Get($@) { $minval = $maxval; Log3 ($name, 5, "$name - Output delta-d -> TS: $tstamp{day}, LASTTS: $lasttstamp{day}, OUTTS: $out_tstamp, OUTVAL: $out_value, WRITEOUT: $writeout"); } - - } else { + } + else { $out_value = $sql_value; $out_tstamp = $sql_timestamp; $writeout = 1; @@ -3634,8 +3787,8 @@ sub DbLog_Get($@) { } elsif ($outf =~ m/(array)/) { push(@ReturnArray, {"tstamp" => $out_tstamp, "device" => $sql_device, "type" => $type, "event" => $event, "reading" => $sql_reading, "value" => $out_value, "unit" => $unit}); - - } else { # generating plots + } + else { # generating plots $out_tstamp =~ s/\ /_/g; # needed by generating plots $retval .= "$out_tstamp $out_value\n"; $retval .= $retvaldummy; @@ -3656,8 +3809,8 @@ sub DbLog_Get($@) { } } $maxval = $sql_value; - - } else { + } + else { if($sql_value < $min[$i]) { $min[$i] = $sql_value; $mind[$i] = $sql_timestamp; @@ -3670,8 +3823,8 @@ sub DbLog_Get($@) { $minval = $sql_value if($sql_value < $minval); $maxval = $sql_value if($sql_value > $maxval); } - - } else { + } + else { $min[$i] = 0; $max[$i] = 0; $sum[$i] = 0; @@ -3682,7 +3835,8 @@ sub DbLog_Get($@) { if(!$deltacalc) { $cnt[$i]++; $lastv[$i] = $sql_value; - } else { + } + else { $lastv[$i] = $out_value if($out_value); } $lastd[$i] = $sql_timestamp; @@ -3696,7 +3850,8 @@ sub DbLog_Get($@) { $out_value = "0"; $out_tstamp = DbLog_implode_datetime($from_datetime{year}, $from_datetime{month}, $from_datetime{day}, $from_datetime{hour}, "30", "00") if($readings[$i]->[3] eq "delta-h"); $out_tstamp = DbLog_implode_datetime($from_datetime{year}, $from_datetime{month}, $from_datetime{day}, "12", "00", "00") if($readings[$i]->[3] eq "delta-d"); - } else { + } + else { %lasttstamp = DbLog_explode_datetime($lastd[$i], ()); $out_value = sprintf("%g", $maxval - $minval); $out_tstamp = DbLog_implode_datetime($lasttstamp{year}, $lasttstamp{month}, $lasttstamp{day}, $lasttstamp{hour}, "30", "00") if($readings[$i]->[3] eq "delta-h"); @@ -3706,11 +3861,11 @@ sub DbLog_Get($@) { $cnt[$i]++; if($outf =~ m/(all)/) { $retval .= sprintf("%s: %s %s %s %s %s %s\n", $out_tstamp, $sql_device, $type, $event, $sql_reading, $out_value, $unit); - - } elsif ($outf =~ m/(array)/) { + } + elsif ($outf =~ m/(array)/) { push(@ReturnArray, {"tstamp" => $out_tstamp, "device" => $sql_device, "type" => $type, "event" => $event, "reading" => $sql_reading, "value" => $out_value, "unit" => $unit}); - - } else { + } + else { $out_tstamp =~ s/\ /_/g; #needed by generating plots $retval .= "$out_tstamp $out_value\n"; } @@ -3757,8 +3912,8 @@ sub DbLog_Get($@) { } elsif($outf =~ m/(array)/) { return @ReturnArray; - - } else { + } + else { $retval = Encode::encode_utf8($retval) if($utf8); # Log3 $name, 5, "DbLog $name -> Result of get:\n$retval"; return $retval; @@ -3770,14 +3925,15 @@ sub DbLog_Get($@) { # Konfigurationscheck DbLog <-> Datenbank # ########################################################################## -sub DbLog_configcheck($) { - my ($hash)= @_; - my $name = $hash->{NAME}; +sub DbLog_configcheck { + my $hash = shift; + my $name = $hash->{NAME}; my $dbmodel = $hash->{MODEL}; my $dbconn = $hash->{dbconn}; my $dbname = (split(/;|=/, $dbconn))[1]; my $history = $hash->{HELPER}{TH}; my $current = $hash->{HELPER}{TC}; + my ($check, $rec,%dbconfig); ### Version check @@ -3802,10 +3958,12 @@ sub DbLog_configcheck($) { if($dbdver < 4.032) { $dbdhint = "Caution: Your DBD version doesn't support UTF8. "; $dbdupd = 1; - } elsif ($dbdver < 4.042) { + } + elsif ($dbdver < 4.042) { $dbdhint = "Caution: Full UTF-8 support exists from DBD version 4.032, but installing DBD version 4.042 is highly suggested. "; $dbdupd = 1; - } else { + } + else { $dbdhint = "Your DBD version fulfills UTF8 support, no need to update DBD."; } } @@ -3823,7 +3981,8 @@ sub DbLog_configcheck($) { if($supd) { $check .= "Used DbLog version: $hash->{HELPER}{VERSION}.
$uptb
"; $check .= "Recommendation: You should update FHEM to get the recent DbLog version from repository ! $dbdhint

"; - } else { + } + else { $check .= "Used DbLog version: $hash->{HELPER}{VERSION}.
$uptb
"; $check .= "Recommendation: No update of DbLog is needed. $dbdhint

"; } @@ -3843,7 +4002,8 @@ sub DbLog_configcheck($) { $rec .= "User -> ".$dbconfig{user}.", " if (defined $dbconfig{user}); $rec .= "Password -> could not read " if (!defined $dbconfig{password}); $rec .= "Password -> read o.k. " if (defined $dbconfig{password}); - } else { + } + else { $rec = $err; } $check .= "Connection $rec

"; @@ -3860,13 +4020,15 @@ sub DbLog_configcheck($) { if($chutf8mod eq $chutf8dat) { $rec = "settings o.k."; - } else { + } + else { $rec = "Both encodings should be identical. You can adjust the usage of UTF8 connection by setting the UTF8 parameter in file '$hash->{CONFIGURATION}' to the right value. "; } if(uc($chutf8mod) ne "UTF8" && uc($chutf8dat) ne "UTF8") { $dbdhint = ""; - } else { + } + else { $dbdhint .= " If you want use UTF8 database option, you must update DBD (Database driver) to at least version 4.032. " if($dbdupd); } @@ -3876,9 +4038,11 @@ sub DbLog_configcheck($) { $chutf8mod = @ce ? uc($ce[0]) : "no result"; @se = DbLog_sqlget($hash,"select character_set_name from information_schema.character_sets"); $chutf8dat = @se ? uc($se[0]) : "no result"; + if($chutf8mod eq $chutf8dat) { $rec = "settings o.k."; - } else { + } + else { $rec = "This is only an information. PostgreSQL supports automatic character set conversion between server and client for certain character set combinations. The conversion information is stored in the pg_conversion system catalog. PostgreSQL comes with some predefined conversions."; } } @@ -3920,11 +4084,13 @@ sub DbLog_configcheck($) { my $max = AttrVal("global", "blockingCallMax", 0); if(!$max || $max >= 6) { $rec = "settings o.k."; - } else { + } + else { $rec = "WARNING - you are running asynchronous mode that is recommended, but the value of global device attribute \"blockingCallMax\" is set quite small.
"; $rec .= "This may cause problems in operation. It is recommended to increase the global blockingCallMax attribute."; } - } else { + } + else { $rec = "Switch $name to the asynchronous logmode by setting the 'asyncMode' attribute. The advantage of this mode is to log events non-blocking.
"; $rec .= "There are attributes 'syncInterval' and 'cacheLimit' relevant for this working mode.
"; $rec .= "Please refer to commandref for further information about these attributes."; @@ -3937,7 +4103,8 @@ sub DbLog_configcheck($) { $check .= "Insert mode of DbLog-device $name is: $bi
"; $rec = "Setting attribute \"bulkInsert\" to \"1\" may result a higher write performance in most cases. "; $rec .= "Feel free to try this mode."; - } else { + } + else { $bi = "Bulk"; $check .= "Insert mode of DbLog-device $name is: $bi
"; $rec = "settings o.k."; @@ -3958,7 +4125,8 @@ sub DbLog_configcheck($) { $emb = 0 if($pe =~ /[01]/); if(!$pf || $pe =~ /[01]/) { $wall .= "".$web.": plotfork=".$pf." / plotEmbed=".$pe."
"; - } else { + } + else { $wall .= $web.": plotfork=".$pf." / plotEmbed=".$pe."
"; } } @@ -3968,9 +4136,10 @@ sub DbLog_configcheck($) { $rec = "You should set attribute \"plotfork = 1\" and \"plotEmbed = 2\" in relevant devices. ". "If these attributes are not set, blocking situations may occure when creating plots. ". "Note: Your system must have sufficient memory to handle parallel running Perl processes. See also global attribute \"blockingCallMax\".
" - } else { + } + else { $check .= $wall; - $rec = "settings o.k."; + $rec = "settings o.k."; } $check .= "
"; $check .= "Recommendation: $rec

"; @@ -3999,7 +4168,8 @@ sub DbLog_configcheck($) { @sr_rdg = DbLog_sqlget($hash,"select column_name,character_maximum_length from information_schema.columns where table_name='$h' and table_schema='$sch' and column_name='reading'"); @sr_val = DbLog_sqlget($hash,"select column_name,character_maximum_length from information_schema.columns where table_name='$h' and table_schema='$sch' and column_name='value'"); @sr_unt = DbLog_sqlget($hash,"select column_name,character_maximum_length from information_schema.columns where table_name='$h' and table_schema='$sch' and column_name='unit'"); - } else { + } + else { @sr_dev = DbLog_sqlget($hash,"select column_name,character_maximum_length from information_schema.columns where table_name='$h' and column_name='device'"); @sr_typ = DbLog_sqlget($hash,"select column_name,character_maximum_length from information_schema.columns where table_name='$h' and column_name='type'"); @sr_evt = DbLog_sqlget($hash,"select column_name,character_maximum_length from information_schema.columns where table_name='$h' and column_name='event'"); @@ -4010,9 +4180,9 @@ sub DbLog_configcheck($) { } } if($dbmodel =~ /SQLITE/) { - my $dev = (DbLog_sqlget($hash,"SELECT sql FROM sqlite_master WHERE name = '$history'"))[0]; - $cdat_dev = $dev // "no result"; - $cdat_typ = $cdat_evt = $cdat_rdg = $cdat_val = $cdat_unt = $cdat_dev; + my $dev = (DbLog_sqlget($hash,"SELECT sql FROM sqlite_master WHERE name = '$history'"))[0]; + $cdat_dev = $dev // "no result"; + $cdat_typ = $cdat_evt = $cdat_rdg = $cdat_val = $cdat_unt = $cdat_dev; ($cdat_dev) = $cdat_dev =~ /DEVICE.varchar\(([\d]+)\)/x; ($cdat_typ) = $cdat_typ =~ /TYPE.varchar\(([\d]+)\)/x; ($cdat_evt) = $cdat_evt =~ /EVENT.varchar\(([\d]+)\)/x; @@ -4043,7 +4213,8 @@ sub DbLog_configcheck($) { if($cdat_dev >= $cmod_dev && $cdat_typ >= $cmod_typ && $cdat_evt >= $cmod_evt && $cdat_rdg >= $cmod_rdg && $cdat_val >= $cmod_val && $cdat_unt >= $cmod_unt) { $rec = "settings o.k."; - } else { + } + else { if ($dbmodel !~ /SQLITE/) { $rec = "The relation between column width in table $history and the field width used in device $name don't meet the requirements. "; $rec .= "Please make sure that the width of database field definition is equal or larger than the field width used by the module. Compare the given results.
"; @@ -4057,7 +4228,8 @@ sub DbLog_configcheck($) { $rec .= "You can change the column width in database by a statement like 'alter table $history modify VALUE varchar(128);' (example for changing field 'VALUE'). "; $rec .= "You can do it for example by executing 'sqlCmd' in DbRep or in a SQL-Editor of your choice. (switch $name to asynchron mode for non-blocking).
"; $rec .= "Alternatively the field width used by $name can be adjusted by setting attributes 'colEvent', 'colReading', 'colValue'. (pls. refer to commandref)"; - } else { + } + else { $rec = "WARNING - The relation between column width in table $history and the field width used by device $name should be equal but it differs."; $rec .= "The field width used by $name can be adjusted by setting attributes 'colEvent', 'colReading', 'colValue'. (pls. refer to commandref)"; $rec .= "Because you use SQLite this is only a warning. Normally the database can handle these differences. "; @@ -4090,7 +4262,8 @@ sub DbLog_configcheck($) { @sr_rdg = DbLog_sqlget($hash,"select column_name,character_maximum_length from information_schema.columns where table_name='$c' and table_schema='$sch' and column_name='reading'"); @sr_val = DbLog_sqlget($hash,"select column_name,character_maximum_length from information_schema.columns where table_name='$c' and table_schema='$sch' and column_name='value'"); @sr_unt = DbLog_sqlget($hash,"select column_name,character_maximum_length from information_schema.columns where table_name='$c' and table_schema='$sch' and column_name='unit'"); - } else { + } + else { @sr_dev = DbLog_sqlget($hash,"select column_name,character_maximum_length from information_schema.columns where table_name='$c' and column_name='device'"); @sr_typ = DbLog_sqlget($hash,"select column_name,character_maximum_length from information_schema.columns where table_name='$c' and column_name='type'"); @sr_evt = DbLog_sqlget($hash,"select column_name,character_maximum_length from information_schema.columns where table_name='$c' and column_name='event'"); @@ -4101,15 +4274,15 @@ sub DbLog_configcheck($) { } } if($dbmodel =~ /SQLITE/) { - my $dev = (DbLog_sqlget($hash,"SELECT sql FROM sqlite_master WHERE name = '$current'"))[0]; - $cdat_dev = $dev?$dev:"no result"; - $cdat_typ = $cdat_evt = $cdat_rdg = $cdat_val = $cdat_unt = $cdat_dev; - $cdat_dev =~ s/.*DEVICE.varchar\(([\d]*)\).*/$1/e; - $cdat_typ =~ s/.*TYPE.varchar\(([\d]*)\).*/$1/e; - $cdat_evt =~ s/.*EVENT.varchar\(([\d]*)\).*/$1/e; - $cdat_rdg =~ s/.*READING.varchar\(([\d]*)\).*/$1/e; - $cdat_val =~ s/.*VALUE.varchar\(([\d]*)\).*/$1/e; - $cdat_unt =~ s/.*UNIT.varchar\(([\d]*)\).*/$1/e; + my $dev = (DbLog_sqlget($hash,"SELECT sql FROM sqlite_master WHERE name = '$current'"))[0]; + $cdat_dev = $dev // "no result"; + $cdat_typ = $cdat_evt = $cdat_rdg = $cdat_val = $cdat_unt = $cdat_dev; + ($cdat_dev) = $cdat_dev =~ /DEVICE.varchar\(([\d]+)\)/x; + ($cdat_typ) = $cdat_typ =~ /TYPE.varchar\(([\d]+)\)/x; + ($cdat_evt) = $cdat_evt =~ /EVENT.varchar\(([\d]+)\)/x; + ($cdat_rdg) = $cdat_rdg =~ /READING.varchar\(([\d]+)\)/x; + ($cdat_val) = $cdat_val =~ /VALUE.varchar\(([\d]+)\)/x; + ($cdat_unt) = $cdat_unt =~ /UNIT.varchar\(([\d]+)\)/x; } if ($dbmodel !~ /SQLITE/) { $cdat_dev = @sr_dev?($sr_dev[1]):"no result"; @@ -4134,7 +4307,8 @@ sub DbLog_configcheck($) { if($cdat_dev >= $cmod_dev && $cdat_typ >= $cmod_typ && $cdat_evt >= $cmod_evt && $cdat_rdg >= $cmod_rdg && $cdat_val >= $cmod_val && $cdat_unt >= $cmod_unt) { $rec = "settings o.k."; - } else { + } + else { if ($dbmodel !~ /SQLITE/) { $rec = "The relation between column width in table $current and the field width used in device $name don't meet the requirements. "; $rec .= "Please make sure that the width of database field definition is equal or larger than the field width used by the module. Compare the given results.
"; @@ -4148,7 +4322,8 @@ sub DbLog_configcheck($) { $rec .= "You can change the column width in database by a statement like 'alter table $current modify VALUE varchar(128);' (example for changing field 'VALUE'). "; $rec .= "You can do it for example by executing 'sqlCmd' in DbRep or in a SQL-Editor of your choice. (switch $name to asynchron mode for non-blocking).
"; $rec .= "Alternatively the field width used by $name can be adjusted by setting attributes 'colEvent', 'colReading', 'colValue'. (pls. refer to commandref)"; - } else { + } + else { $rec = "WARNING - The relation between column width in table $current and the field width used by device $name should be equal but it differs. "; $rec .= "The field width used by $name can be adjusted by setting attributes 'colEvent', 'colReading', 'colValue'. (pls. refer to commandref)"; $rec .= "Because you use SQLite this is only a warning. Normally the database can handle these differences. "; @@ -4175,14 +4350,16 @@ sub DbLog_configcheck($) { $rec .= "Depending on your database size this command may running a long time.
"; $rec .= "Please make sure the device '$name' is operating in asynchronous mode to avoid FHEM from blocking when creating the index.
"; $rec .= "Note: If you have just created another index which covers the same fields and order as suggested (e.g. a primary key) you don't need to create the 'Search_Idx' as well !
"; - } else { + } + else { @six_dev = DbLog_sqlget($hash,"SHOW INDEX FROM $history where Key_name='Search_Idx' and Column_name='DEVICE'"); @six_rdg = DbLog_sqlget($hash,"SHOW INDEX FROM $history where Key_name='Search_Idx' and Column_name='READING'"); @six_tsp = DbLog_sqlget($hash,"SHOW INDEX FROM $history where Key_name='Search_Idx' and Column_name='TIMESTAMP'"); if (@six_dev && @six_rdg && @six_tsp) { $check .= "Index 'Search_Idx' exists and contains recommended fields 'DEVICE', 'TIMESTAMP', 'READING'.
"; $rec = "settings o.k."; - } else { + } + else { $check .= "Index 'Search_Idx' exists but doesn't contain recommended field 'DEVICE'.
" if (!@six_dev); $check .= "Index 'Search_Idx' exists but doesn't contain recommended field 'READING'.
" if (!@six_rdg); $check .= "Index 'Search_Idx' exists but doesn't contain recommended field 'TIMESTAMP'.
" if (!@six_tsp); @@ -4201,7 +4378,8 @@ sub DbLog_configcheck($) { $rec .= "Depending on your database size this command may running a long time.
"; $rec .= "Please make sure the device '$name' is operating in asynchronous mode to avoid FHEM from blocking when creating the index.
"; $rec .= "Note: If you have just created another index which covers the same fields and order as suggested (e.g. a primary key) you don't need to create the 'Search_Idx' as well !
"; - } else { + } + else { $idef = $six[4]; $idef_dev = 1 if($idef =~ /device/); $idef_rdg = 1 if($idef =~ /reading/); @@ -4209,7 +4387,8 @@ sub DbLog_configcheck($) { if ($idef_dev && $idef_rdg && $idef_tsp) { $check .= "Index 'Search_Idx' exists and contains recommended fields 'DEVICE', 'READING', 'TIMESTAMP'.
"; $rec = "settings o.k."; - } else { + } + else { $check .= "Index 'Search_Idx' exists but doesn't contain recommended field 'DEVICE'.
" if (!$idef_dev); $check .= "Index 'Search_Idx' exists but doesn't contain recommended field 'READING'.
" if (!$idef_rdg); $check .= "Index 'Search_Idx' exists but doesn't contain recommended field 'TIMESTAMP'.
" if (!$idef_tsp); @@ -4228,7 +4407,8 @@ sub DbLog_configcheck($) { $rec .= "Depending on your database size this command may running a long time.
"; $rec .= "Please make sure the device '$name' is operating in asynchronous mode to avoid FHEM from blocking when creating the index.
"; $rec .= "Note: If you have just created another index which covers the same fields and order as suggested (e.g. a primary key) you don't need to create the 'Search_Idx' as well !
"; - } else { + } + else { $idef = $six[1]; $idef_dev = 1 if(lc($idef) =~ /device/); $idef_rdg = 1 if(lc($idef) =~ /reading/); @@ -4236,7 +4416,8 @@ sub DbLog_configcheck($) { if ($idef_dev && $idef_rdg && $idef_tsp) { $check .= "Index 'Search_Idx' exists and contains recommended fields 'DEVICE', 'READING', 'TIMESTAMP'.
"; $rec = "settings o.k."; - } else { + } + else { $check .= "Index 'Search_Idx' exists but doesn't contain recommended field 'DEVICE'.
" if (!$idef_dev); $check .= "Index 'Search_Idx' exists but doesn't contain recommended field 'READING'.
" if (!$idef_rdg); $check .= "Index 'Search_Idx' exists but doesn't contain recommended field 'TIMESTAMP'.
" if (!$idef_tsp); @@ -4256,7 +4437,7 @@ sub DbLog_configcheck($) { my (@dix,@dix_rdg,@dix_tsp,$irep_rdg,$irep_tsp); my $isused = 0; my @repdvs = devspec2array("TYPE=DbRep"); - $check .= "Result of check 'Report_Idx' availability for DbRep-devices

"; + $check .= "Result of check 'Report_Idx' availability for DbRep-devices

"; foreach (@repdvs) { $dbrp = $_; @@ -4279,14 +4460,16 @@ sub DbLog_configcheck($) { $rec .= "Depending on your database size this command may running a long time.
"; $rec .= "Please make sure the device '$name' is operating in asynchronous mode to avoid FHEM from blocking when creating the index.
"; $rec .= "Note: If you have just created another index which covers the same fields and order as suggested (e.g. a primary key) you don't need to create the 'Report_Idx' as well !
"; - } else { + } + else { @dix_rdg = DbLog_sqlget($hash,"SHOW INDEX FROM $history where Key_name='Report_Idx' and Column_name='READING'"); @dix_tsp = DbLog_sqlget($hash,"SHOW INDEX FROM $history where Key_name='Report_Idx' and Column_name='TIMESTAMP'"); if (@dix_rdg && @dix_tsp) { $check .= "At least one DbRep-device assigned to $name is used. "; $check .= "Index 'Report_Idx' exists and contains recommended fields 'TIMESTAMP', 'READING'.
"; $rec = "settings o.k."; - } else { + } + else { $check .= "You use at least one DbRep-device assigned to $name. "; $check .= "Index 'Report_Idx' exists but doesn't contain recommended field 'READING'.
" if (!@dix_rdg); $check .= "Index 'Report_Idx' exists but doesn't contain recommended field 'TIMESTAMP'.
" if (!@dix_tsp); @@ -4305,14 +4488,16 @@ sub DbLog_configcheck($) { $rec .= "Depending on your database size this command may running a long time.
"; $rec .= "Please make sure the device '$name' is operating in asynchronous mode to avoid FHEM from blocking when creating the index.
"; $rec .= "Note: If you have just created another index which covers the same fields and order as suggested (e.g. a primary key) you don't need to create the 'Report_Idx' as well !
"; - } else { + } + else { $irep = $dix[4]; $irep_rdg = 1 if($irep =~ /reading/); $irep_tsp = 1 if($irep =~ /timestamp/); if ($irep_rdg && $irep_tsp) { $check .= "Index 'Report_Idx' exists and contains recommended fields 'TIMESTAMP', 'READING'.
"; $rec = "settings o.k."; - } else { + } + else { $check .= "Index 'Report_Idx' exists but doesn't contain recommended field 'READING'.
" if (!$irep_rdg); $check .= "Index 'Report_Idx' exists but doesn't contain recommended field 'TIMESTAMP'.
" if (!$irep_tsp); $rec = "The index should contain the fields 'TIMESTAMP', 'READING'. "; @@ -4330,14 +4515,16 @@ sub DbLog_configcheck($) { $rec .= "Depending on your database size this command may running a long time.
"; $rec .= "Please make sure the device '$name' is operating in asynchronous mode to avoid FHEM from blocking when creating the index.
"; $rec .= "Note: If you have just created another index which covers the same fields and order as suggested (e.g. a primary key) you don't need to create the 'Search_Idx' as well !
"; - } else { + } + else { $irep = $dix[1]; $irep_rdg = 1 if(lc($irep) =~ /reading/); $irep_tsp = 1 if(lc($irep) =~ /timestamp/); if ($irep_rdg && $irep_tsp) { $check .= "Index 'Report_Idx' exists and contains recommended fields 'TIMESTAMP', 'READING'.
"; $rec = "settings o.k."; - } else { + } + else { $check .= "Index 'Report_Idx' exists but doesn't contain recommended field 'READING'.
" if (!$irep_rdg); $check .= "Index 'Report_Idx' exists but doesn't contain recommended field 'TIMESTAMP'.
" if (!$irep_tsp); $rec = "The index should contain the fields 'TIMESTAMP', 'READING'. "; @@ -4347,7 +4534,9 @@ sub DbLog_configcheck($) { } } } - } else { + + } + else { $check .= "No DbRep-device assigned to $name is used. Hence an index for DbRep isn't needed.
"; $rec = "settings o.k."; } @@ -4361,9 +4550,9 @@ return $check; ######################################################################################### # check Modul Aktualität fhem.de <-> local ######################################################################################### -sub DbLog_checkModVer($) { - my ($name) = @_; - my $src = "http://fhem.de/fhemupdate/controls_fhem.txt"; +sub DbLog_checkModVer { + my $name = shift; + my $src = "http://fhem.de/fhemupdate/controls_fhem.txt"; if($src !~ m,^(.*)/([^/]*)$,) { Log3 $name, 1, "DbLog $name -> configCheck: Cannot parse $src, probably not a valid http control file"; @@ -4391,7 +4580,8 @@ sub DbLog_checkModVer($) { @locList = map { $_ =~ s/[\r\n]//; $_ } ; close(FD); Log3 $name, 4, "DbLog $name -> configCheck: Got local $ctrlFileName with ".int(@locList)." entries."; - } else { + } + else { Log3 $name, 1, "DbLog $name -> configCheck: can't open $root/FHEM/$ctrlFileName: $!"; return ("check of new DbLog version not possible, see logfile."); } @@ -4406,7 +4596,8 @@ sub DbLog_checkModVer($) { } my $noSzCheck = AttrVal("global", "updateNoFileCheck", configDBUsed()); - foreach my $rem (@remList) { + + for my $rem (@remList) { my @r = split(" ", $rem, 4); next if($r[0] ne "UPD" || $r[3] !~ /93_DbLog/); my $fName = $r[3]; @@ -4430,11 +4621,13 @@ return ("",0,"Your local DbLog module is up to date."); } ################################### -sub DbLog_updGetUrl($$) { +sub DbLog_updGetUrl { my ($name,$url) = @_; + my %upd_connecthash; - $url =~ s/%/%25/g; - $upd_connecthash{url} = $url; + + $url =~ s/%/%25/g; + $upd_connecthash{url} = $url; $upd_connecthash{keepalive} = ($url =~ m/localUpdate/ ? 0 : 1); # Forum #49798 my ($err, $data) = HttpUtils_BlockingGet(\%upd_connecthash); @@ -4454,9 +4647,10 @@ return ($data,""); ######################################################################################### # Einen (einfachen) Datensatz aus DB lesen ######################################################################################### -sub DbLog_sqlget($$) { - my ($hash,$sql)= @_; - my $name = $hash->{NAME}; +sub DbLog_sqlget { + my ($hash,$sql) = @_; + my $name = $hash->{NAME}; + my ($dbh,$sth,@sr); Log3 ($name, 4, "DbLog $name - Executing SQL: $sql"); @@ -4489,12 +4683,13 @@ return @sr; # Addlog - einfügen des Readingwertes eines gegebenen Devices # ######################################################################################### -sub DbLog_AddLog($$$$$) { - my ($hash,$devrdspec,$value,$nce,$cn)= @_; - my $name = $hash->{NAME}; - my $async = AttrVal($name, "asyncMode", undef); - my $value_fn = AttrVal( $name, "valueFn", "" ); - my $ce = AttrVal($name, "cacheEvents", 0); +sub DbLog_AddLog { + my ($hash,$devrdspec,$value,$nce,$cn) = @_; + my $name = $hash->{NAME}; + my $async = AttrVal($name, "asyncMode", undef); + my $value_fn = AttrVal( $name, "valueFn", "" ); + my $ce = AttrVal($name, "cacheEvents", 0); + my ($dev_type,$dev_name,$dev_reading,$read_val,$event,$ut); my @row_array; my $ts; @@ -4504,7 +4699,8 @@ sub DbLog_AddLog($$$$$) { # Funktion aus Attr valueFn validieren if( $value_fn =~ m/^\s*(\{.*\})\s*$/s ) { $value_fn = $1; - } else { + } + else { $value_fn = ''; } @@ -4558,9 +4754,11 @@ sub DbLog_AddLog($$$$$) { if(goodReadingName($rdspec) && defined($value)) { Log3 $name, 3, "DbLog $name -> addLog WARNING - Device: '$dev_name' -> Reading '$rdspec' not found - add it as new reading."; push @exrds,$rdspec; - } elsif (goodReadingName($rdspec) && !defined($value)) { + } + elsif (goodReadingName($rdspec) && !defined($value)) { Log3 $name, 2, "DbLog $name -> addLog WARNING - Device: '$dev_name' -> new Reading '$rdspec' has no value - can't add it !"; - } else { + } + else { Log3 $name, 2, "DbLog $name -> addLog WARNING - Device: '$dev_name' -> Readingname '$rdspec' is no valid or regexp - can't add regexp as new reading !"; } } @@ -4618,7 +4816,8 @@ sub DbLog_AddLog($$$$$) { eval { my $epoch_seconds_begin = timelocal($sec, $min, $hh, $dd, $mm-1, $yyyy-1900); }; if (!$@) { $ts = $TIMESTAMP; - } else { + } + else { Log3 ($name, 2, "DbLog $name -> Parameter TIMESTAMP got from valueFn is invalid: $TIMESTAMP"); } $dev_name = $DEVICE if($DEVICE ne ''); @@ -4649,10 +4848,12 @@ sub DbLog_AddLog($$$$$) { my $memcount = $data{DbLog}{$name}{cache}{memcache}?scalar(keys %{$data{DbLog}{$name}{cache}{memcache}}):0; if($ce == 1) { readingsSingleUpdate($hash, "CacheUsage", $memcount, 1); - } else { + } + else { readingsSingleUpdate($hash, 'CacheUsage', $memcount, 0); } - } else { + } + else { # synchoner Mode push(@row_array, $row); } @@ -4682,7 +4883,7 @@ return; # Subroutine addCacheLine - einen Datensatz zum Cache hinzufügen # ######################################################################################### -sub DbLog_addCacheLine($$$$$$$$) { +sub DbLog_addCacheLine { my ($hash,$i_timestamp,$i_dev,$i_type,$i_evt,$i_reading,$i_val,$i_unit) = @_; my $name = $hash->{NAME}; my $ce = AttrVal($name, "cacheEvents", 0); @@ -4691,7 +4892,8 @@ sub DbLog_addCacheLine($$$$$$$$) { # Funktion aus Attr valueFn validieren if( $value_fn =~ m/^\s*(\{.*\})\s*$/s ) { $value_fn = $1; - } else { + } + else { $value_fn = ''; } @@ -4729,7 +4931,8 @@ sub DbLog_addCacheLine($$$$$$$$) { eval { my $epoch_seconds_begin = timelocal($sec, $min, $hh, $dd, $mm-1, $yyyy-1900); }; if (!$@) { $i_timestamp = $TIMESTAMP; - } else { + } + else { Log3 ($name, 2, "DbLog $name -> Parameter TIMESTAMP got from valueFn is invalid: $TIMESTAMP"); } @@ -4757,7 +4960,8 @@ sub DbLog_addCacheLine($$$$$$$$) { my $memcount = $data{DbLog}{$name}{cache}{memcache}?scalar(keys %{$data{DbLog}{$name}{cache}{memcache}}):0; if($ce == 1) { readingsSingleUpdate($hash, "CacheUsage", $memcount, 1); - } else { + } + else { readingsSingleUpdate($hash, 'CacheUsage', $memcount, 0); } }; @@ -4770,8 +4974,8 @@ return; # Subroutine cutCol - Daten auf maximale Länge beschneiden # ######################################################################################### -sub DbLog_cutCol($$$$$$$) { - my ($hash,$dn,$dt,$evt,$rd,$val,$unit)= @_; +sub DbLog_cutCol { + my ($hash,$dn,$dt,$evt,$rd,$val,$unit) = @_; my $name = $hash->{NAME}; my $colevent = AttrVal($name, 'colEvent', undef); my $colreading = AttrVal($name, 'colReading', undef); @@ -4801,8 +5005,8 @@ return ($dn,$dt,$evt,$rd,$val,$unit); # Autocommit: 0/1/2 = aus/ein/Servereinstellung # Transaktion: 0/1 = aus/ein ############################################################################### -sub DbLog_commitMode ($) { - my ($hash) = @_; +sub DbLog_commitMode { + my $hash = shift; my $name = $hash->{NAME}; my $useac = 2; # default Servereinstellung my $useta = 1; # default Transaktion ein @@ -4818,8 +5022,9 @@ return($useac,$useta); ############################################################################### # Zeichen von Feldevents filtern ############################################################################### -sub DbLog_charfilter ($) { - my ($txt) = @_; +sub DbLog_charfilter { + my $txt = shift; + my ($p,$a); # nur erwünschte Zeichen ASCII %d32-126 und Sonderzeichen @@ -4843,7 +5048,7 @@ return($txt); ######################################################################################### ### DBLog - Historische Werte ausduennen (alte blockiernde Variante) > Forum #41089 ######################################################################################### -sub DbLog_reduceLog($@) { +sub DbLog_reduceLog { my ($hash,@a) = @_; my $history = $hash->{HELPER}{TH}; my $current = $hash->{HELPER}{TC}; @@ -4875,13 +5080,16 @@ sub DbLog_reduceLog($@) { if ($hash->{MODEL} eq 'SQLITE') { $ots = "datetime('now', '-$od days')"; $nts = "datetime('now', '-$nd days')" if($nd); - } elsif ($hash->{MODEL} eq 'MYSQL') { + } + elsif ($hash->{MODEL} eq 'MYSQL') { $ots = "DATE_SUB(CURDATE(),INTERVAL $od DAY)"; $nts = "DATE_SUB(CURDATE(),INTERVAL $nd DAY)" if($nd); - } elsif ($hash->{MODEL} eq 'POSTGRESQL') { + } + elsif ($hash->{MODEL} eq 'POSTGRESQL') { $ots = "NOW() - INTERVAL '$od' DAY"; $nts = "NOW() - INTERVAL '$nd' DAY" if($nd); - } else { + } + else { $ret = 'Unknown database type.'; } @@ -4939,7 +5147,8 @@ sub DbLog_reduceLog($@) { Log3($hash->{NAME}, 3, "DbLog $name: reduceLog ! FAILED ! for day $processingDay"); eval {$dbh->rollback() if(!$dbh->{AutoCommit});}; $ret = 0; - } else { + } + else { eval {$dbh->commit() if(!$dbh->{AutoCommit});}; } $dbh->{RaiseError} = 0; @@ -4986,7 +5195,8 @@ sub DbLog_reduceLog($@) { $k++; } push(@averageUpdD, ["$updDate $updHour:30:00", 'rl_av_h', $average, $hourHash->{$hourKey}->[1], $hourHash->{$hourKey}->[3], $updDate]) if (defined($a[3]) && $a[3] =~ /average=day/i); - } else { + } + else { push(@averageUpdD, [$hourHash->{$hourKey}->[0], $hourHash->{$hourKey}->[2], $hourHash->{$hourKey}->[4]->[0], $hourHash->{$hourKey}->[1], $hourHash->{$hourKey}->[3], $updDate]) if (defined($a[3]) && $a[3] =~ /average=day/i); } } @@ -4998,7 +5208,8 @@ sub DbLog_reduceLog($@) { Log3($hash->{NAME}, 2, "DbLog $name - reduceLogNbl ! FAILED ! for day $processingDay: $err"); eval {$dbh->rollback() if(!$dbh->{AutoCommit});}; @averageUpdD = (); - } else { + } + else { eval {$dbh->commit() if(!$dbh->{AutoCommit});}; } $dbh->{RaiseError} = 0; @@ -5021,7 +5232,8 @@ sub DbLog_reduceLog($@) { for (keys %averageHash) { if(scalar @{$averageHash{$_}->{tedr}} == 1) { delete $averageHash{$_}; - } else { + } + else { $c += (scalar(@{$averageHash{$_}->{tedr}}) - 1); } } @@ -5064,7 +5276,8 @@ sub DbLog_reduceLog($@) { $err = $@; Log3($hash->{NAME}, 2, "DbLog $name - reduceLogNbl ! FAILED ! for day $processingDay: $err"); eval {$dbh->rollback() if(!$dbh->{AutoCommit});}; - } else { + } + else { eval {$dbh->commit() if(!$dbh->{AutoCommit});}; } $dbh->{RaiseError} = 0; @@ -5093,14 +5306,16 @@ sub DbLog_reduceLog($@) { push(@{$hourlyKnown{$row->[1].$row->[3]}->[4]}, $row->[4]); } } - } else { + } + else { $exclude = 0; for (@excludeRegex) { $exclude = 1 if("$row->[1]:$row->[3]" =~ /^$_$/); } if ($exclude) { $excludeCount++ if($day != 00); - } else { + } + 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]; } } @@ -5122,7 +5337,7 @@ sub DbLog_reduceLog($@) { ######################################################################################### ### DBLog - Historische Werte ausduennen non-blocking > Forum #41089 ######################################################################################### -sub DbLog_reduceLogNbl($) { +sub DbLog_reduceLogNbl { my ($name) = @_; my $hash = $defs{$name}; my $dbconn = $hash->{dbconn}; @@ -5142,9 +5357,11 @@ sub DbLog_reduceLogNbl($) { my ($useac,$useta) = DbLog_commitMode($hash); if(!$useac) { eval {$dbh = DBI->connect("dbi:$dbconn", $dbuser, $dbpassword, { PrintError => 0, RaiseError => 1, AutoInactiveDestroy => 1, AutoCommit => 0 });}; - } elsif($useac == 1) { + } + elsif($useac == 1) { eval {$dbh = DBI->connect("dbi:$dbconn", $dbuser, $dbpassword, { PrintError => 0, RaiseError => 1, AutoInactiveDestroy => 1, AutoCommit => 1 });}; - } else { + } + else { # Server default eval {$dbh = DBI->connect("dbi:$dbconn", $dbuser, $dbpassword, { PrintError => 0, RaiseError => 1, AutoInactiveDestroy => 1 });}; } @@ -5175,16 +5392,20 @@ sub DbLog_reduceLogNbl($) { my ($od,$nd) = split(":",$a[2]); # $od - Tage älter als , $nd - Tage neuer als my ($ots,$nts); + if ($hash->{MODEL} eq 'SQLITE') { $ots = "datetime('now', '-$od days')"; $nts = "datetime('now', '-$nd days')" if($nd); - } elsif ($hash->{MODEL} eq 'MYSQL') { + } + elsif ($hash->{MODEL} eq 'MYSQL') { $ots = "DATE_SUB(CURDATE(),INTERVAL $od DAY)"; $nts = "DATE_SUB(CURDATE(),INTERVAL $nd DAY)" if($nd); - } elsif ($hash->{MODEL} eq 'POSTGRESQL') { + } + elsif ($hash->{MODEL} eq 'POSTGRESQL') { $ots = "NOW() - INTERVAL '$od' DAY"; $nts = "NOW() - INTERVAL '$nd' DAY" if($nd); - } else { + } + else { $ret = 'Unknown database type.'; } @@ -5261,7 +5482,8 @@ sub DbLog_reduceLogNbl($) { Log3 ($name, 2, "DbLog $name -> DbLog_reduceLogNbl - $@"); } $ret = 0; - } else { + } + else { eval {$dbh->commit() if(!$dbh->{AutoCommit});}; if ($@) { Log3 ($name, 2, "DbLog $name -> DbLog_reduceLogNbl - $@"); @@ -5314,7 +5536,8 @@ sub DbLog_reduceLogNbl($) { $k++; } push(@averageUpdD, ["$updDate $updHour:30:00", 'rl_av_h', $average, $hourHash->{$hourKey}->[1], $hourHash->{$hourKey}->[3], $updDate]) if (defined($a[3]) && $a[3] =~ /average=day/i); - } else { + } + else { push(@averageUpdD, [$hourHash->{$hourKey}->[0], $hourHash->{$hourKey}->[2], $hourHash->{$hourKey}->[4]->[0], $hourHash->{$hourKey}->[1], $hourHash->{$hourKey}->[3], $updDate]) if (defined($a[3]) && $a[3] =~ /average=day/i); } } @@ -5329,7 +5552,8 @@ sub DbLog_reduceLogNbl($) { Log3 ($name, 2, "DbLog $name -> DbLog_reduceLogNbl - $@"); } @averageUpdD = (); - } else { + } + else { eval {$dbh->commit() if(!$dbh->{AutoCommit});}; if ($@) { Log3 ($name, 2, "DbLog $name -> DbLog_reduceLogNbl - $@"); @@ -5358,7 +5582,8 @@ sub DbLog_reduceLogNbl($) { for (keys %averageHash) { if(scalar @{$averageHash{$_}->{tedr}} == 1) { delete $averageHash{$_}; - } else { + } + else { $c += (scalar(@{$averageHash{$_}->{tedr}}) - 1); } } @@ -5403,7 +5628,8 @@ sub DbLog_reduceLogNbl($) { if ($@) { Log3 ($name, 2, "DbLog $name -> DbLog_reduceLogNbl - $@"); } - } else { + } + else { eval {$dbh->commit() if(!$dbh->{AutoCommit});}; if ($@) { Log3 ($name, 2, "DbLog $name -> DbLog_reduceLogNbl - $@"); @@ -5435,14 +5661,16 @@ sub DbLog_reduceLogNbl($) { push(@{$hourlyKnown{$row->[1].$row->[3]}->[4]}, $row->[4]); } } - } else { + } + else { $exclude = 0; for (@excludeRegex) { $exclude = 1 if("$row->[1]:$row->[3]" =~ /^$_$/); } if ($exclude) { $excludeCount++ if($day != 00); - } else { + } + 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]; } } @@ -5468,23 +5696,24 @@ return "$name|$ret|0"; ######################################################################################### # DBLog - reduceLogNbl non-blocking Rückkehrfunktion ######################################################################################### -sub DbLog_reduceLogNbl_finished($) { - my ($string) = @_; - my @a = split("\\|",$string); - my $name = $a[0]; - my $hash = $defs{$name}; - my $ret = decode_base64($a[1]); - my $err = decode_base64($a[2]) if ($a[2]); +sub DbLog_reduceLogNbl_finished { + my $string = shift; + my @a = split("\\|",$string); + my $name = $a[0]; + my $hash = $defs{$name}; + my $ret = decode_base64($a[1]); + my $err = decode_base64($a[2]) if ($a[2]); readingsSingleUpdate($hash,"reduceLogState",$err?$err:$ret,1); delete $hash->{HELPER}{REDUCELOG_PID}; + return; } ######################################################################################### # DBLog - count non-blocking ######################################################################################### -sub DbLog_countNbl($) { +sub DbLog_countNbl { my ($name) = @_; my $hash = $defs{$name}; my $history = $hash->{HELPER}{TH}; @@ -5498,7 +5727,8 @@ sub DbLog_countNbl($) { if (!$dbh) { my $err = encode_base64("DbLog $name: DBLog_Set - count - DB connect not possible",""); return "$name|0|0|$err|0"; - } else { + } + else { Log3 $name,4,"DbLog $name: Records count requested."; # SQL-Startzeit $st = [gettimeofday]; @@ -5518,7 +5748,7 @@ return "$name|$cc|$hc|0|$rt"; ######################################################################################### # DBLog - count non-blocking Rückkehrfunktion ######################################################################################### -sub DbLog_countNbl_finished($) { +sub DbLog_countNbl_finished { my ($string) = @_; my @a = split("\\|",$string); my $name = $a[0]; @@ -5547,7 +5777,7 @@ return; ######################################################################################### # DBLog - deleteOldDays non-blocking ######################################################################################### -sub DbLog_deldaysNbl($) { +sub DbLog_deldaysNbl { my ($name) = @_; my $hash = $defs{$name}; my $dbconn = $hash->{dbconn}; @@ -5566,9 +5796,11 @@ sub DbLog_deldaysNbl($) { my ($useac,$useta) = DbLog_commitMode($hash); if(!$useac) { eval {$dbh = DBI->connect("dbi:$dbconn", $dbuser, $dbpassword, { PrintError => 0, RaiseError => 1, AutoCommit => 0, AutoInactiveDestroy => 1 });}; - } elsif($useac == 1) { + } + elsif($useac == 1) { eval {$dbh = DBI->connect("dbi:$dbconn", $dbuser, $dbpassword, { PrintError => 0, RaiseError => 1, AutoCommit => 1, AutoInactiveDestroy => 1 });}; - } else { + } + else { # Server default eval {$dbh = DBI->connect("dbi:$dbconn", $dbuser, $dbpassword, { PrintError => 0, RaiseError => 1, AutoInactiveDestroy => 1 });}; } @@ -5586,11 +5818,14 @@ sub DbLog_deldaysNbl($) { $cmd = "delete from $history where TIMESTAMP < "; if ($hash->{MODEL} eq 'SQLITE') { $cmd .= "datetime('now', '-$days days')"; - } elsif ($hash->{MODEL} eq 'MYSQL') { + } + elsif ($hash->{MODEL} eq 'MYSQL') { $cmd .= "DATE_SUB(CURDATE(),INTERVAL $days DAY)"; - } elsif ($hash->{MODEL} eq 'POSTGRESQL') { + } + elsif ($hash->{MODEL} eq 'POSTGRESQL') { $cmd .= "NOW() - INTERVAL '$days' DAY"; - } else { + } + else { $ret = 'Unknown database type. Maybe you can try userCommand anyway.'; $error = encode_base64($ret,""); Log3 ($name, 2, "DbLog $name - Error: $ret"); @@ -5612,7 +5847,8 @@ sub DbLog_deldaysNbl($) { $dbh->disconnect; Log3 ($name, 4, "DbLog $name -> BlockingCall DbLog_deldaysNbl finished"); return "$name|0|0|$error"; - } else { + } + else { $rows = $sth->rows; $dbh->commit() if(!$dbh->{AutoCommit}); $dbh->disconnect; @@ -5632,7 +5868,7 @@ return "$name|$rows|$rt|0"; ######################################################################################### # DBLog - deleteOldDays non-blocking Rückkehrfunktion ######################################################################################### -sub DbLog_deldaysNbl_done($) { +sub DbLog_deldaysNbl_done { my ($string) = @_; my @a = split("\\|",$string); my $name = $a[0]; @@ -5648,7 +5884,8 @@ sub DbLog_deldaysNbl_done($) { delete $hash->{HELPER}{DELDAYS_PID}; Log3 ($name, 5, "DbLog $name -> DbLog_deldaysNbl_done finished"); return; - } else { + } + else { if(AttrVal($name, "showproctime", undef) && $bt) { my ($rt,$brt) = split(",", $bt); readingsBeginUpdate($hash); @@ -5662,14 +5899,15 @@ sub DbLog_deldaysNbl_done($) { Log3 ($name, 3, "DbLog $name -> deleteOldDaysNbl finished. $rows entries of database $db deleted."); delete $hash->{HELPER}{DELDAYS_PID}; Log3 ($name, 5, "DbLog $name -> DbLog_deldaysNbl_done finished"); + return; } ################################################################ # benutzte DB-Feldlängen in Helper und Internals setzen ################################################################ -sub DbLog_setinternalcols ($){ - my ($hash)= @_; +sub DbLog_setinternalcols { + my $hash = shift; my $name = $hash->{NAME}; $hash->{HELPER}{DEVICECOL} = $columns{DEVICE}; @@ -5690,10 +5928,10 @@ return; ################################################################ # reopen DB-Connection nach Ablauf set ... reopen [n] seconds ################################################################ -sub DbLog_reopen ($){ - my ($hash) = @_; - my $name = $hash->{NAME}; - my $async = AttrVal($name, "asyncMode", undef); +sub DbLog_reopen { + my $hash = shift; + my $name = $hash->{NAME}; + my $async = AttrVal($name, "asyncMode", undef); RemoveInternalTimer($hash, "DbLog_reopen"); @@ -5705,7 +5943,8 @@ sub DbLog_reopen ($){ readingsSingleUpdate($hash, "state", "reopened", 1); $hash->{HELPER}{OLDSTATE} = "reopened"; DbLog_execmemcache($hash) if($async); - } else { + } + else { InternalTimer(gettimeofday()+30, "DbLog_reopen", $hash, 0); } @@ -5715,7 +5954,7 @@ return; ################################################################ # check ob primary key genutzt wird ################################################################ -sub DbLog_checkUsePK ($$){ +sub DbLog_checkUsePK { my ($hash,$dbh) = @_; my $name = $hash->{NAME}; my $dbconn = $hash->{dbconn}; @@ -5743,7 +5982,7 @@ return ($upkh,$upkc,$pkh,$pkc); ################################################################ # Routine für FHEMWEB Detailanzeige ################################################################ -sub DbLog_fhemwebFn($$$$) { +sub DbLog_fhemwebFn { my ($FW_wname, $d, $room, $pageHash) = @_; # pageHash is set for summaryFn. my $ret; @@ -5761,15 +6000,17 @@ return $ret; ################################################################ # Dropdown-Menü cuurent-Tabelle SVG-Editor ################################################################ -sub DbLog_sampleDataFn($$$$$) { +sub DbLog_sampleDataFn { my ($dlName, $dlog, $max, $conf, $wName) = @_; my $desc = "Device:Reading"; my $hash = $defs{$dlName}; my $current = $hash->{HELPER}{TC}; + my @htmlArr; my @example; my @colregs; my $counter; + my $currentPresent = AttrVal($dlName,'DbLogType','History'); my $dbhf = DbLog_ConnectNewDBH($defs{$dlName}); @@ -5804,7 +6045,8 @@ sub DbLog_sampleDataFn($$$$$) { push @htmlArr, $ret; } - } else { + } + else { # Table Current not present, so create an empty input field push @example, "No sample data due to missing table '$current'"; @@ -5829,7 +6071,7 @@ return ($desc, \@htmlArr, join("
", @example)); ################################################################ # Error handling, returns a JSON String ################################################################ -sub DbLog_jsonError($) { +sub DbLog_jsonError { my $errormsg = $_[0]; my $json = '{"success": "false", "msg":"'.$errormsg.'"}'; @@ -5840,7 +6082,7 @@ return $json; # Check Zeitformat # Zeitformat: YYYY-MM-DD HH:MI:SS ################################################################ -sub DbLog_checkTimeformat ($) { +sub DbLog_checkTimeformat { my ($t) = @_; my (@datetime, @date, @time); @@ -5861,7 +6103,7 @@ return; ################################################################ # Prepare the SQL String ################################################################ -sub DbLog_prepareSql(@) { +sub DbLog_prepareSql { my ($hash, @a) = @_; my $starttime = $_[5]; $starttime =~ s/_/ /; @@ -5977,50 +6219,66 @@ sub DbLog_prepareSql(@) { $yearstats .= "FROM $history WHERE READING = '$yaxis' AND DEVICE = '$device' "; $yearstats .= "AND TIMESTAMP Between '$starttime' AND '$endtime' GROUP BY strftime('%Y 00:00:00', TIMESTAMP);"; - } else { + } + else { $sql = "errordb"; } if($userquery eq "getreadings") { $sql = "SELECT distinct(reading) FROM $history WHERE device = '".$device."'"; - } elsif($userquery eq "getdevices") { + } + elsif($userquery eq "getdevices") { $sql = "SELECT distinct(device) FROM $history"; - } elsif($userquery eq "timerange") { + } + elsif($userquery eq "timerange") { $sql = "SELECT ".$xaxis.", VALUE FROM $history WHERE READING = '$yaxis' AND DEVICE = '$device' AND TIMESTAMP Between '$starttime' AND '$endtime' ORDER BY TIMESTAMP;"; - } elsif($userquery eq "hourstats") { + } + elsif($userquery eq "hourstats") { $sql = $hourstats; - } elsif($userquery eq "daystats") { + } + elsif($userquery eq "daystats") { $sql = $daystats; - } elsif($userquery eq "weekstats") { + } + elsif($userquery eq "weekstats") { $sql = $weekstats; - } elsif($userquery eq "monthstats") { + } + elsif($userquery eq "monthstats") { $sql = $monthstats; - } elsif($userquery eq "yearstats") { + } + elsif($userquery eq "yearstats") { $sql = $yearstats; - } elsif($userquery eq "savechart") { + } + elsif($userquery eq "savechart") { $sql = "INSERT INTO frontend (TYPE, NAME, VALUE) VALUES ('savedchart', '$savename', '$jsonChartConfig')"; - } elsif($userquery eq "renamechart") { + } + elsif($userquery eq "renamechart") { $sql = "UPDATE frontend SET NAME = '$savename' WHERE ID = '$jsonChartConfig'"; - } elsif($userquery eq "deletechart") { + } + elsif($userquery eq "deletechart") { $sql = "DELETE FROM frontend WHERE TYPE = 'savedchart' AND ID = '".$savename."'"; - } elsif($userquery eq "updatechart") { + } + elsif($userquery eq "updatechart") { $sql = "UPDATE frontend SET VALUE = '$jsonChartConfig' WHERE ID = '".$savename."'"; - } elsif($userquery eq "getcharts") { + } + elsif($userquery eq "getcharts") { $sql = "SELECT * FROM frontend WHERE TYPE = 'savedchart'"; - } elsif($userquery eq "getTableData") { + } + elsif($userquery eq "getTableData") { if ($device ne '""' && $yaxis ne '""') { $sql = "SELECT * FROM $history WHERE READING = '$yaxis' AND DEVICE = '$device' "; $sql .= "AND TIMESTAMP Between '$starttime' AND '$endtime'"; $sql .= " LIMIT '$paginglimit' OFFSET '$pagingstart'"; $countsql = "SELECT count(*) FROM $history WHERE READING = '$yaxis' AND DEVICE = '$device' "; $countsql .= "AND TIMESTAMP Between '$starttime' AND '$endtime'"; - } elsif($device ne '""' && $yaxis eq '""') { + } + elsif($device ne '""' && $yaxis eq '""') { $sql = "SELECT * FROM $history WHERE DEVICE = '$device' "; $sql .= "AND TIMESTAMP Between '$starttime' AND '$endtime'"; $sql .= " LIMIT '$paginglimit' OFFSET '$pagingstart'"; $countsql = "SELECT count(*) FROM $history WHERE DEVICE = '$device' "; $countsql .= "AND TIMESTAMP Between '$starttime' AND '$endtime'"; - } else { + } + else { $sql = "SELECT * FROM $history"; $sql .= " WHERE TIMESTAMP Between '$starttime' AND '$endtime'"; $sql .= " LIMIT '$paginglimit' OFFSET '$pagingstart'"; @@ -6028,11 +6286,12 @@ sub DbLog_prepareSql(@) { $countsql .= " WHERE TIMESTAMP Between '$starttime' AND '$endtime'"; } return ($sql, $countsql); - } else { + } + else { $sql = "error"; } - return $sql; +return $sql; } ################################################################ @@ -6040,7 +6299,7 @@ sub DbLog_prepareSql(@) { # Do the query # ################################################################ -sub DbLog_chartQuery($@) { +sub DbLog_chartQuery { my ($sql, $countsql) = DbLog_prepareSql(@_); @@ -6082,7 +6341,8 @@ sub DbLog_chartQuery($@) { # When columns are empty but execution was successful, we have done a successful INSERT, UPDATE or DELETE if($columns) { $columncnt = scalar @$columns; - } else { + } + else { return '{"success": "true", "msg":"All ok"}'; } @@ -6093,7 +6353,8 @@ sub DbLog_chartQuery($@) { if($i == 0) { $jsonstring .= '{'; - } else { + } + else { $jsonstring .= ',{'; } @@ -6106,10 +6367,12 @@ sub DbLog_chartQuery($@) { my $fragment = substr($data[$i],0,1); if ($fragment eq "{") { $jsonstring .= $data[$i]; - } else { + } + else { $jsonstring .= '"'.$data[$i].'"'; } - } else { + } + else { $jsonstring .= '""' } @@ -6123,9 +6386,11 @@ sub DbLog_chartQuery($@) { $jsonstring .= ']'; if (defined $totalcount && $totalcount ne "") { $jsonstring .= ',"totalCount": '.$totalcount.'}'; - } else { + } + else { $jsonstring .= '}'; } + return $jsonstring; } @@ -6133,7 +6398,7 @@ return $jsonstring; # get ReadingsVal # get ReadingsTimestamp ################################################################ -sub DbLog_dbReadings($@) { +sub DbLog_dbReadings { my($hash,@a) = @_; my $history = $hash->{HELPER}{TH}; my $current = $hash->{HELPER}{TC}; @@ -6146,14 +6411,16 @@ sub DbLog_dbReadings($@) { my $query; if (lc($DbLogType) =~ m(current) ) { $query = "select VALUE,TIMESTAMP from $current where DEVICE= '$a[2]' and READING= '$a[3]'"; - } else { + } + else { $query = "select VALUE,TIMESTAMP from $history where DEVICE= '$a[2]' and READING= '$a[3]' order by TIMESTAMP desc limit 1"; } my ($reading,$timestamp) = $dbhf->selectrow_array($query); $dbhf->disconnect(); - $reading = (defined($reading)) ? $reading : $a[4]; + $reading = (defined($reading)) ? $reading : $a[4]; $timestamp = (defined($timestamp)) ? $timestamp : $a[4]; + return $reading if $a[1] eq 'ReadingsVal'; return $timestamp if $a[1] eq 'ReadingsTimestamp'; return "Syntax error: $a[1]"; @@ -6163,7 +6430,7 @@ sub DbLog_dbReadings($@) { # Versionierungen des Moduls setzen # Die Verwendung von Meta.pm und Packages wird berücksichtigt ################################################################ -sub DbLog_setVersionInfo($) { +sub DbLog_setVersionInfo { my ($hash) = @_; my $name = $hash->{NAME}; @@ -6175,18 +6442,20 @@ sub DbLog_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{DbLog}{META}} - if($modules{$type}{META}{x_version}) { # {x_version} ( nur gesetzt wenn $Id: 93_DbLog.pm 22018 2020-05-24 06:33:11Z DS_Starter $ im Kopf komplett! vorhanden ) + if($modules{$type}{META}{x_version}) { # {x_version} ( nur gesetzt wenn $Id: 93_DbLog.pm 22246 2020-06-23 21:12:11Z DS_Starter $ im Kopf komplett! vorhanden ) $modules{$type}{META}{x_version} =~ s/1.1.1/$v/g; - } else { + } + else { $modules{$type}{META}{x_version} = $v; } - return $@ unless (FHEM::Meta::SetInternals($hash)); # FVERSION wird gesetzt ( nur gesetzt wenn $Id: 93_DbLog.pm 22018 2020-05-24 06:33:11Z DS_Starter $ im Kopf komplett! vorhanden ) + return $@ unless (FHEM::Meta::SetInternals($hash)); # FVERSION wird gesetzt ( nur gesetzt wenn $Id: 93_DbLog.pm 22246 2020-06-23 21:12:11Z DS_Starter $ im Kopf komplett! vorhanden ) if(__PACKAGE__ eq "FHEM::$type" || __PACKAGE__ eq $type) { # es wird mit Packages gearbeitet -> Perl übliche Modulversion setzen # mit {->VERSION()} im FHEMWEB kann Modulversion abgefragt werden use version 0.77; our $VERSION = FHEM::Meta::Get( $hash, 'version' ); } - } else { + } + else { # herkömmliche Modulstruktur $hash->{VERSION} = $v; } @@ -6201,7 +6470,7 @@ return; # drh Driver handle object (rarely seen or used in applications) # h Any of the handle types above ($dbh, $sth, or $drh) ######################################################################### -sub DbLog_startShowChildhandles ($) { +sub DbLog_startShowChildhandles { my ($str) = @_; my ($name,$sub) = split(":",$str); my $hash = $defs{$name}; @@ -6217,7 +6486,7 @@ sub DbLog_startShowChildhandles ($) { return; } -sub DbLog_showChildHandles ($$$$) { +sub DbLog_showChildHandles { my ($name,$h, $level, $key) = @_; my $t = $h->{Type}."h";