############################################## # $Id$ # Average computing package main; use strict; use warnings; ########################## sub average_Initialize($) { my ($hash) = @_; $hash->{DefFn} = "average_Define"; $hash->{NotifyFn} = "average_Notify"; $hash->{NotifyOrderPrefix} = "10-"; # Want to be called before the rest $hash->{AttrList} = "disable:0,1 " . "disabledForIntervals " . "computeMethod:integral,counter " . "noaverage:0,1 " . "nominmax:0,1 " . "floatformat:%0.1f,%0.2f"; } ########################## sub average_Define($$$) { my ($hash, $def) = @_; my ($name, $type, $re, $rest) = split("[ \t]+", $def, 4); if(!$re || $rest) { my $msg = "wrong syntax: define average device[:event]"; return $msg; } # Checking for misleading regexps eval { "Hallo" =~ m/^$re$/ }; return "Bad regexp: $@" if($@); $hash->{REGEXP} = $re; $hash->{STATE} = "active"; return undef; } sub avg_setValTime($$$$) { my ($r, $rname, $val, $tn) = @_; $r->{$rname}{VAL} = $val; $r->{$rname}{TIME} = $tn; } ########################## sub average_Notify($$) { my ($avg, $dev) = @_; my $myName = $avg->{NAME}; return "" if(IsDisabled($myName)); my $devName = $dev->{NAME}; my $re = $avg->{REGEXP}; my $max = int(@{$dev->{CHANGED}}); my $tn; my $myIdx = $max; my $doCounter = (AttrVal($myName, "computeMethod", "integral") eq "counter"); my $doMMx = (AttrVal($myName, "nominmax", "0") eq "0"); my $doAvg = (AttrVal($myName, "noaverage", "0") eq "0"); my $ffmt = AttrVal($myName, "floatformat", "%0.1f"); my $r = $dev->{READINGS}; for (my $i = 0; $i < $max; $i++) { my $s = $dev->{CHANGED}[$i]; ################ # Filtering next if(!defined($s)); my ($evName, $val) = split(" ", $s, 2); # resets $1 next if($devName !~ m/^$re$/ && "$devName:$s" !~ m/^$re$/ || $s =~ m/(_avg_|_cum_|_min_|_max_|_cnt_)/); if(defined($1)) { my $reArg = $1; if(defined($2)) { $evName = $1; $reArg = $2; } $val = $reArg if(defined($reArg) && $reArg =~ m/^(-?\d+\.?\d*)/); } next if(!defined($val) || $val !~ m/^(-?\d+\.?\d*)/); $val = $1; ################ # Avg computing $evName =~ s/[^A-Za-z_-].*//; $tn = TimeNow() if(!$tn); my @dNow = split("[ :-]", $tn); for(my $idx = 0; $idx <= 1; $idx++) { # 0:day 1:month my $secNow = 3600*$dNow[3] + 60*$dNow[4] + $dNow[5]; $secNow += $dNow[2]*86400 if($idx); my $cumName = "${evName}_cum_" . ($idx ? "month" : "day"); my $avgName = "${evName}_avg_" . ($idx ? "month" : "day"); my $minName = "${evName}_min_" . ($idx ? "month" : "day"); my $maxName = "${evName}_max_" . ($idx ? "month" : "day"); my $cntName = "${evName}_cnt_" . ($idx ? "month" : "day"); if($doCounter && !defined($r->{$cntName})) { avg_setValTime($r, $cntName, 1, $tn); delete $r->{$cumName}; # Reset when switching to counter-mode delete $r->{$avgName}; } if($doMMx && (!defined($r->{$maxName}) || !defined($r->{$minName}))) { avg_setValTime($r, $maxName, $val, $tn); avg_setValTime($r, $minName, $val, $tn); } if(!defined($r->{$cumName}) || ($doAvg && !defined($r->{$avgName}))) { my $cum = ($doCounter ? $val : $secNow*$val); avg_setValTime($r, $cumName, $cum, $tn); avg_setValTime($r, $avgName, $val, $tn) if ($doAvg); next; } my @dLast = split("[ :-]", $r->{$cumName}{TIME}); my $secLast = 3600*$dLast[3] + 60*$dLast[4] + $dLast[5]; $secLast += $dLast[2]*86400 if($idx); if($idx == 0 && ($dLast[2] == $dNow[2]) || $idx == 1 && ($dLast[1] == $dNow[1])) { # same day or month my $cVal = $r->{$cumName}{VAL}; $cVal += ($doCounter ? $val : ($secNow-$secLast) * $val); avg_setValTime($r, $cumName, $cVal, $tn); if($doAvg) { my $div = ($secNow ? $secNow : 1); if($doCounter) { $div = $r->{$cntName}{VAL}+1; avg_setValTime($r, $cntName, $div, $tn); } my $lVal = sprintf($ffmt, $r->{$cumName}{VAL}/$div); avg_setValTime($r, $avgName, $lVal, $tn); } if($doMMx) { avg_setValTime($r, $maxName, sprintf($ffmt,$val), $tn) if($r->{$maxName}{VAL} < $val); avg_setValTime($r, $minName, sprintf($ffmt,$val), $tn) if($r->{$minName}{VAL} > $val); } } else { # day or month changed: create events and reset values if($doAvg) { $dev->{CHANGED}[$myIdx++] = "$avgName: ".$r->{$avgName}{VAL}; avg_setValTime($r, $cumName, $secNow*$val, $tn); avg_setValTime($r, $avgName, $val, $tn); } if($doCounter) { $dev->{CHANGED}[$myIdx++] = "$cumName: ".$r->{$cumName}{VAL}; avg_setValTime($r, $cumName, 0, $tn); avg_setValTime($r, $cntName, 0, $tn) if($doAvg); } else { avg_setValTime($r, $cumName, $secNow*$val, $tn); } if($doMMx) { $dev->{CHANGED}[$myIdx++] = "$maxName: ".$r->{$maxName}{VAL}; $dev->{CHANGED}[$myIdx++] = "$minName: ".$r->{$minName}{VAL}; avg_setValTime($r, $maxName, sprintf($ffmt, $val), $tn); avg_setValTime($r, $minName, sprintf($ffmt, $val), $tn); } } } } return undef; } 1; =pod =item helper =begin html

average

=end html =cut