| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457 |
- # $Id: 98_logProxy.pm 16299 2018-03-01 08:06:55Z justme1968 $
- ##############################################################################
- #
- # This file is part of fhem.
- #
- # Fhem is free software: you can redistribute it and/or modify
- # it under the terms of the GNU General Public License as published by
- # the Free Software Foundation, either version 2 of the License, or
- # (at your option) any later version.
- #
- # Fhem is distributed in the hope that it will be useful,
- # but WITHOUT ANY WARRANTY; without even the implied warranty of
- # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- # GNU General Public License for more details.
- #
- # You should have received a copy of the GNU General Public License
- # along with fhem. If not, see <http://www.gnu.org/licenses/>.
- #
- ##############################################################################
- package main;
- use strict;
- use warnings;
- use SetExtensions;
- sub logProxy_Initialize($)
- {
- my ($hash) = @_;
- $hash->{DefFn} = "logProxy_Define";
- $hash->{UndefFn} = "logProxy_Undefine";
- #$hash->{SetFn} = "logProxy_Set";
- $hash->{GetFn} = "logProxy_Get";
- #$hash->{AttrList} = "disable:1 ";
- $hash->{SVG_sampleDataFn} = "logProxy_sampleDataFn";
- $hash->{SVG_regexpFn} = "logProxy_regexpFn";
- }
- sub logProxy_Define($$)
- {
- my ($hash, $def) = @_;
- my @args = split("[ \t]+", $def);
- my $name = $args[0];
- my $usage = "Usage: define <name> logProxy";
- return $usage if( int(@args) != 2 );
- my $d = $modules{logProxy}{defptr};
- return "logProxy device already defined as $d->{NAME}." if( defined($d) );
- $modules{logProxy}{defptr} = $hash;
- $hash->{STATE} = 'Initialized';
- return undef;
- }
- sub logProxy_Undefine($$)
- {
- my ($hash,$arg) = @_;
- my $name = $hash->{NAME};
- delete $modules{logProxy}{defptr};
- return undef;
- }
- my $logProxy_columns = "ConstX,ConstY,Func,Polar,FileLog,DbLog";
- sub
- logProxy_sampleDataFn($$$$$)
- {
- my ($ldName, $flog, $max, $conf, $wName) = @_;
- my $desc = "Type,Spec";
- my @htmlArr;
- $max = 16 if($max > 16);
- for(my $r=0; $r < $max; $r++) {
- my @f = split(":", ($flog->[$r] ? $flog->[$r] : ":"), 6);
- my $ret = "";
- $ret .= SVG_sel("par_${r}_0", $logProxy_columns, $f[0]);
- $ret .= SVG_txt("par_${r}_1", "", join(":", @f[1..@f-1]), 30);
- push @htmlArr, $ret;
- }
- my @example;
- push @example, 'ConstY:0';
- push @example, 'ConstY:$data{avg1}';
- push @example, 'ConstY:$data{avg2}';
- push @example, 'DbLog:myDB:myReading';
- push @example, 'FileLog:myFileLog:4:myReading';
- push @example, 'FileLog:FileLog_<SPEC1>:4:<SPEC1>.power';
- push @example, 'FileLog:FileLog_<SPEC1>:4:<SPEC1>.consumption';
- push @example, 'Func:logProxy_WeekProfile2Plot("HCB",$from,$to)';
- push @example, 'Func:logProxy_WeekProfile2Plot("myHeatingControl",$from,$to,"(\\d*)\$")';
- push @example, 'ConstX:logProxy_shiftTime($from,60*60*2),$data{min1},$data{max1}';
- #Log 3, Dumper $desc;
- #Log 3, Dumper @htmlArr;
- #Log 3, Dumper $example;
- return ($desc, \@htmlArr, join("<br>", @example));
- }
- sub
- logProxy_regexpFn($$)
- {
- my ($name, $filter) = @_;
- my $ret;
- my @a = split( ' ', $filter );
- for(my $i = 0; $i < int(@a); $i++) {
- my @fld = split(":", $a[$i]);
- if( $a[$i] =~ m/^(FileLog|DbLog):([^:]*):(.*)/ ) {
- my @options = split( ',', $fld[1] );
- my $log_dev = shift(@options);
- my $column_specs = $3;
- $ret .= '|' if( $ret );
- $ret .= CallFn($log_dev, "SVG_regexpFn", $log_dev, $column_specs);
- }
- }
- return $ret;
- }
- sub
- logProxy_Set($@)
- {
- return undef;
- }
- #WeekProfile format: {$wday}{$time}{$value} with 0 = sunday
- sub
- logProxy_Heating_Controll2WeekProfile($)
- {
- my ($d) = @_;
- return undef if( !defined($defs{$d}) );
- return undef if( !defined($defs{$d}->{helper}{SWITCHINGTIME}) );
- return $defs{$d}->{helper}{SWITCHINGTIME};
- }
- sub
- logProxy_HM2WeekProfile($;$)
- {
- my ($d,$list) = @_;
- return undef if( !defined($defs{$d}) );
- # default to 1st list of tc-it
- $list = "P1" if ( !$list );
- # if tc-it
- my @rl = sort( grep /^R_${list}_[0-7]_tempList...$/,keys %{$defs{$d}{READINGS}} );
- # else cc-tc and rt
- @rl = sort( grep /^R_[0-7]_tempList...$/,keys %{$defs{$d}{READINGS}} ) if( !@rl );
- return undef if( !@rl );
- my %profile = ();
- for(my $i=0; $i<7; ++$i) {
- # correct wday
- my $reading = ReadingsVal($d,$rl[($i+1)%7],undef);
- # collect 'until' switching times
- my %tmp = ();
- my @parts = split( ' ', $reading );
- while( @parts ) {
- my $time = shift @parts;
- $tmp{$time} = shift @parts;
- }
- # shift 'until' switching times into 'from' switching times
- # can not be done in one step if times are out of order
- my %st = ();
- my $time = "00:00";
- foreach my $key (sort (keys %tmp)) {
- $st{$time} = $tmp{$key};
- $time = $key;
- }
- $profile{$i} = \%st;
- }
- return undef if (scalar (keys %profile) != 7);
- return \%profile;
- }
- sub
- logProxy_MAX2WeekProfile($)
- {
- my ($d) = @_;
- return undef if( !defined($defs{$d}) );
- my @rl = sort( grep /^weekprofile-.-...-(temp|time)$/,keys %{$defs{$d}{READINGS}} );
- return undef if( !@rl );
- my %profile = ();
- for(my $i=0; $i<7; ++$i) {
- # correct wday
- my $temps = ReadingsVal($d,$rl[(($i+1)%7)*2],undef);
- my $times = ReadingsVal($d,$rl[(($i+1)%7)*2+1],undef);
- my %st = ();
- my @temps = split( '/', $temps );
- my @times = split( '/', $times );
- while( @times ) {
- my $temp = shift @temps;
- $temp =~ s/\s*([\d\.]*).*/$1/;
- my $time = shift @times;
- $time =~ s/\s*(\d\d:\d\d).*/$1/;
- $st{$time} = $temp;
- }
- $profile{$i} = \%st;
- }
- return \%profile;
- }
- # sample implementaion to plot the week profile of a Heating_Control or HM Thermostat device.
- sub
- logProxy_WeekProfile2Plot($$$;$)
- {
- my ($profile, $from, $to, $regex) = @_;
- return undef if( !$profile );
- if( $regex ) {
- eval { "test" =~ m/$regex/ };
- if( $@ ) {
- Log3 undef, 3, "logProxy_WeekProfile2Plot: $regex: $@";
- return undef;
- }
- }
- if( defined($defs{$profile}) ) {
- if( $defs{$profile}{TYPE} eq "Heating_Control" ) {
- $profile = logProxy_Heating_Controll2WeekProfile($profile);
- } elsif( $defs{$profile}{TYPE} eq "WeekdayTimer" ) {
- $profile = logProxy_Heating_Controll2WeekProfile($profile);
- } elsif( $defs{$profile}{TYPE} eq "CUL_HM" ) {
- my ($p,$l) = split( ',', $profile, 2 );
- $profile = logProxy_HM2WeekProfile($p, $l);
- } elsif( $defs{$profile}{TYPE} eq "MAX" ) {
- $profile = logProxy_MAX2WeekProfile($profile);
- } else {
- Log3 undef, 2, "logProxy_WeekProfile2Plot: $profile is not a Heating_Control, WeekdayTimer, CUL_HM or MAX device";
- return undef;
- }
- }
- #Log 3, Dumper $profile;
- if( ref($profile) ne "HASH" ) {
- Log3 undef, 2, "logProxy_WeekProfile2Plot: no profile hash given";
- return undef;
- }
- my $fromsec = SVG_time_to_sec($from);
- my $tosec = SVG_time_to_sec($to);
- my (undef,undef,undef,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($fromsec);
- my $min = 999999;
- my $max = -999999;
- # go back one day to get the start value, TODO: go back multiple days
- $mday -= 1;
- $wday -= 1;
- $wday %= 7;
- my $ret = "";
- my $value;
- my $prev_value;
- my $sec = $fromsec;
- # while not end of plot range reached
- while( $sec < $tosec ) {
- return undef if( !defined($profile->{$wday}) );
- # for all switching times of current day
- foreach my $st (sort (keys %{ $profile->{$wday} })) {
- #remember previous value for start of plot range
- $prev_value = $value;
- my ($h, $m, $s) = split( ':', $st );
- $s = 0 if( !$s );
- $value = $profile->{$wday}{$st};
- if( $regex ) {
- if( $value =~ m/$regex/ ) {
- Log3 undef, 4, "logProxy_WeekProfile2Plot: $value =~ m/$regex/ => $1";
- $value = $1;
- } else {
- Log3 undef, 3, "logProxy_WeekProfile2Plot: $value =~ m/$regex/ => no match";
- }
- }
- # map some specials to values and eco and comfort to temperatures
- $value = 0 if( $value eq "off" );
- $value = 1 if( $value eq "on" );
- $value = 1 if( $value eq "up" );
- $value = 0 if( $value eq "down" );
- $value = 18 if( $value eq "eco" );
- $value = 22 if( $value eq "comfort" );
- # 'dirty' hack that exploits the feature that $mday can be < 0 and > 31.
- # everything should better be based on a real second counter
- my $timestamp = sprintf("%04d-%02d-%02d_%02d:%02d:%02d", 1900+$year, 1+$mon, $mday, $h, $m, $s );
- $sec = SVG_time_to_sec($timestamp);
- # skip all values before start of plot range
- next if( SVG_time_to_sec($timestamp) < $fromsec );
- # add first value at start of plot range
- if( !$ret && defined($prev_value) ) {
- $min = $prev_value if( $prev_value < $min );
- $max = $prev_value if( $prev_value > $max );
- $ret .= "$from $prev_value\n";
- }
- # done if after end of plot range
- last if( SVG_time_to_sec($timestamp) > $tosec );
- $min = $value if( $value < $min );
- $max = $value if( $value > $max );
- # add actual controll point
- $ret .= "$timestamp $value\n";
- }
- # next day
- $mday += 1;
- $wday += 1;
- $wday %= 7;
- }
- # add last value at end of plot range
- $ret .= "$to $prev_value\n";
- return ($ret,$min,$max,$prev_value);
- }
- sub logProxy_hms2sec($){
- my ($h,$m,$s) = split(":", shift);
- $m = 0 if(!$m);
- $s = 0 if(!$s);
- my $t = $s;
- $t += $m * 60;
- $t += $h * 60*60;
- return ($t)
- }
- sub logProxy_isDay($) {
- my ($sec) = @_;
- my $sr = logProxy_hms2sec(sunrise_abs_dat($sec));
- my $ss = logProxy_hms2sec(sunset_abs_dat($sec));
- my ($s,$m,$h) = localtime($sec);
- my $cur = logProxy_hms2sec( "$h:$m:$s" );
- return ($cur > $sr && $cur < $ss)?1:0;
- }
- sub logProxy_hms2dec($){
- my ($h,$m,$s) = split(":", shift);
- $m = 0 if(!$m);
- $s = 0 if(!$s);
- my $t = $m * 60;
- $t += $s;
- $t /= 3600;
- $t += $h;
- return ($t)
- }
- sub logProxy_dec2hms($){
- my ($t) = @_;
- my $h = int($t);
- my $r = ($t - $h)*3600;
- my $m = int($r/60);
- my $s = $r - $m*60;
- return sprintf("%02d:%02d:%02d",$h,$m,$s);
- }
- sub
- logProxy_Range2Zoom($)
- {
- my( $range ) = @_;
- return "year" if( $range > 1+60*60*24*28*6);
- return "month" if( $range > 1+60*60*24*28);
- return "week" if( $range > 1+60*60*24);
- return "day" if( $range > 1+60*60*6);
- return "qday" if( $range > 1+60*60 );
- return "hour";
- }
- my %logProxy_stepDefault = ( year => 60*60*24,
- month => 60*60*24,
- week => 60*60*6,
- day => 60*60,
- qday => 60*15,
- hour => 60, );
- # sample implementaion to plot an arbitrary function
- sub
- logProxy_Func2Plot($$$;$)
- {
- my ($from, $to, $func, $step) = @_;
- my $fromsec = SVG_time_to_sec($from);
- my $tosec = SVG_time_to_sec($to);
- my $secs = $tosec - $fromsec;
- $step = \%logProxy_stepDefault if( !$step );
- $step = eval $step if( $step =~ m/^{.*}$/ );
- $step = $step->{logProxy_Range2Zoom($secs)} if( ref($step) eq "HASH" );
- $step = $logProxy_stepDefault{logProxy_Range2Zoom($secs)} if( !$step );
- my $min = 999999;
- my $max = -999999;
- my $ret = "";
- my $value;
- for(my $sec=$fromsec; $sec<$tosec; $sec+=$step) {
- my ($s,$m,$h,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($sec);
- $value = eval $func;
- if( $@ ) {
- Log3 undef, 1, "logProxy_Func2Plot: $func: $@";
- next;
- }
- my $timestamp = sprintf("%04d-%02d-%02d_%02d:%02d:%02d", 1900+$year, 1+$mon, $mday, $h, $m, $s );
- $min = $value if( $value < $min );
- $max = $value if( $value > $max );
- # add actual controll point
- $ret .= "$timestamp $value\n";
- }
- return ($ret,$min,$max,$value);
- }
- # shift time by offset seconds (or months if offset ends with m)
- sub
- logProxy_shiftTime($$)
- {
- my ($time, $offset) = @_;
- $time =~ s/ /_/;
- if( $offset =~ m/((-)?\d)*m/ ) {
- my @t = split("[-_:]", $time);
- $time = mktime($t[5],$t[4],$t[3],$t[2],$t[1]-1+$1,$t[0]-1900,0,0,-1);;
- } else {
- $time = SVG_time_to_sec($time);
- $time += $offset;
- }
- my @t = localtime($time);
- $time = sprintf("%04d-%02d-%02d_%02d:%02d:%02d", $t[5]+1900, $t[4]+1, $t[3], $t[2], $t[1], $t[0]);
- return $time;
- }
- # shift plot data by offset
- sub
- logProxy_shiftData($$;$$)
- {
- my ($dp, $offset, $from, $to) = @_;
- my ($dpl,$dpoff,$l) = (length($$dp), 0, "");
- while($dpoff < $dpl) { # using split instead is memory hog
- my $ndpoff = index($$dp, "\n", $dpoff);
- if($ndpoff == -1) {
- $l = substr($$dp, $dpoff);
- } else {
- $l = substr($$dp, $dpoff, $ndpoff-$dpoff);
- }
- if($l =~ m/^#/) {
- } else {
- my ($d, $v) = split(" ", $l);
- $d = logProxy_shiftTime($d, $offset);
- substr($$dp, $dpoff, 19, $d);
- }
- $dpoff = $ndpoff+1;
- last if($ndpoff == -1);
- }
- }
- sub
- logProxy_linearInterpolate($$$$$) {
- my ($t1, $v1, $t2, $v2, $t ) = @_;
- my $dt = $t2 - $t1;
- return $v1 if( !$dt );
- my $dv = $v2 - $v1;
- my $v = $v1 + $dv * ( ($t-$t1) / $dt );
- return $v;
- }
- # clip plot data to [$from,$to] range
- sub
- logProxy_clipData($$$$;$)
- {
- my ($dp, $from, $to, $interpolate, $predict) = @_;
- my $ret = "";
- my $min = 999999;
- my $max = -999999;
- my $comment = "";
- my ($dpl,$dpoff,$l) = (length($$dp), 0, "");
- my $prev_value;
- my $prev_timestamp;
- my $next_value;
- my $next_timestamp;
- while($dpoff < $dpl) { # using split instead is memory hog
- my $ndpoff = index($$dp, "\n", $dpoff);
- if($ndpoff == -1) {
- $l = substr($$dp, $dpoff);
- } else {
- $l = substr($$dp, $dpoff, $ndpoff-$dpoff);
- }
- if($l =~ m/^#/) {
- $comment .= "$l\n";
- } else {
- my ($d, $v) = split(" ", $l);
- my $sec = SVG_time_to_sec($d);
- if( $sec < $from ) {
- $prev_timestamp = $d;
- $prev_value = $v;
- } elsif( $sec > $to ) {
- if( !$next_value ) {
- $next_timestamp = $d;
- $next_value = $v;
- }
- } else {
- if( !$ret && $sec > $from && defined($prev_value) ) {
- my $value = $prev_value;
- $value = logProxy_linearInterpolate( SVG_time_to_sec($prev_timestamp), $prev_value, SVG_time_to_sec($d), $v, $from ) if( $interpolate );
- $min = $value if( $value < $min );
- $max = $value if( $value > $max );
- my @t = localtime($from);
- my $timestamp = sprintf("%04d-%02d-%02d_%02d:%02d:%02d", $t[5]+1900, $t[4]+1, $t[3], $t[2], $t[1], $t[0]);
- $ret .= "$timestamp $value\n";
- }
- $min = $v if( $v < $min );
- $max = $v if( $v > $max );
- $ret .= "$l\n";
- $prev_timestamp = $d;
- $prev_value = $v;
- }
- }
- $dpoff = $ndpoff+1;
- last if($ndpoff == -1);
- }
- #if predict is set -> extend bejond last value
- if( defined($predict) && !defined($next_value) ) {
- $next_value = $prev_value;
- my $sec = SVG_time_to_sec($prev_timestamp);
- if( !$ret && $sec < $from && defined($prev_value) ) {
- my @t = localtime($from);
- my $timestamp = sprintf("%04d-%02d-%02d_%02d:%02d:%02d", $t[5]+1900, $t[4]+1, $t[3], $t[2], $t[1], $t[0]);
- $ret .= "$timestamp $prev_value\n";
- }
- #if $predict = 0 -> predict to end of plot
- my $time = $to;
- #else predict by $predict
- $time = SVG_time_to_sec($prev_timestamp) + $predict if( $predict );
- #but not later than now
- my ($now) = gettimeofday();
- $to = minNum( $time, $now );
- my @t = localtime($to);
- $next_timestamp = sprintf("%04d-%02d-%02d_%02d:%02d:%02d", $t[5]+1900, $t[4]+1, $t[3], $t[2], $t[1], $t[0]);
- }
- if( defined($next_value) ) {
- my $value = $prev_value;
- $value = logProxy_linearInterpolate( SVG_time_to_sec($prev_timestamp), $prev_value, SVG_time_to_sec($next_timestamp), $next_value, $to ) if( $interpolate );
- $min = $value if( $value < $min );
- $max = $value if( $value > $max );
- my @t = localtime($to);
- my $timestamp = sprintf("%04d-%02d-%02d_%02d:%02d:%02d", $t[5]+1900, $t[4]+1, $t[3], $t[2], $t[1], $t[0]);
- $ret .= "$timestamp $value\n";
- }
- $ret .= $comment;
- return (\$ret, $min, $max);
- }
- #parse plot data to array
- sub
- logProxy_data2Array($)
- {
- my ($dp) = @_;
- my @ret = ();
- my $comment;
- my ($dpl,$dpoff,$l) = (length($$dp), 0, "");
- while($dpoff < $dpl) { # using split instead is memory hog
- my $ndpoff = index($$dp, "\n", $dpoff);
- if($ndpoff == -1) {
- $l = substr($$dp, $dpoff);
- } else {
- $l = substr($$dp, $dpoff, $ndpoff-$dpoff);
- }
- if($l =~ m/^#/) {
- $comment .= "$l\n";
- } else {
- my ($d, $v) = split(" ", $l);
- my $sec = SVG_time_to_sec($d);
- push( @ret, [$sec, $v, $d] );
- }
- $dpoff = $ndpoff+1;
- last if($ndpoff == -1);
- }
- return (\@ret,$comment);
- }
- #create plot data from array
- sub
- logProxy_array2Data($$)
- {
- my ($array,$comment) = @_;
- my $ret = "";
- my $min = 999999;
- my $max = -999999;
- my $last;
- return ($ret,$min,$max,$last) if( !ref($array) eq "ARRAY" );
- foreach my $point ( @{$array} ) {
- my @t = localtime($point->[0]);
- my $timestamp = sprintf("%04d-%02d-%02d_%02d:%02d:%02d", $t[5]+1900, $t[4]+1, $t[3], $t[2], $t[1], $t[0]);
- my $value = $point->[1];
- $min = $value if( $value < $min );
- $max = $value if( $value > $max );
- $last = $value;
- $ret .= $timestamp . " " . $value ."\n";
- }
- $ret .= $comment;
- return (\$ret,$min,$max,$last);
- }
- #create plot data from xy-array
- sub
- logProxy_xy2Plot($)
- {
- my ($array) = @_;
- my $ret = ";c 0\n";
- my $min = 999999;
- my $max = -999999;
- my $last;
- my $xmin = 999999;
- my $xmax = -999999;
- return ($ret,$min,$max,$last) if( !ref($array) eq "ARRAY" );
- foreach my $point ( @{$array} ) {
- my $x = $point->[0];
- my $value = $point->[1];
- $min = $value if( $value < $min );
- $max = $value if( $value > $max );
- $last = $value;
- $xmin = $x if( $x < $xmin );
- $xmax = $x if( $x > $xmax );
- $ret .= ";p $x $value\n";
- }
- return ($ret,$min,$max,$last,$xmin,$xmax);
- }
- #create plot data from xy-file
- #assume colums separated by whitespace and x,y pairs separated by comma
- sub
- logProxy_xyFile2Plot($$$)
- {
- my ($filename, $column, $regex)= @_;
- my @array;
- $filename =~ s/%L/$attr{global}{logdir}/g
- if($filename =~ m/%L/ && $attr{global}{logdir});
- if (open(F, "<$filename")) {
- while(<F>) {
- chomp;
- if(/$regex/) {
- my @a= split(/\s/,$_);
- my @pp= split(";", $a[$column-1]);
- map { my @p= split(",", $_); push @array, \@p; } @pp;
- }
- }
- close(F);
- }
- return logProxy_xy2Plot(\@array);
- }
- #create plot data from date-y-array
- sub
- logProxy_values2Plot($)
- {
- my ($array) = @_;
- my $ret = ";c 0\n";
- my $min = 999999;
- my $max = -999999;
- my $last;
- return ($ret,$min,$max,$last) if( !ref($array) eq "ARRAY" );
- foreach my $point ( @{$array} ) {
- $ret .= "$point->[0] $point->[1]\n";
- }
- return ($ret,$min,$max,$last);
- }
- sub
- logProxy_Get($@)
- {
- my ($hash, $name, @a) = @_;
- #Log 3, "logProxy_Get";
- #Log 3, Dumper @a;
- my $inf = shift @a;
- my $outf = shift @a;
- my $from = shift @a;
- my $to = shift @a; # Now @a contains the list of column_specs
- my $internal;
- if($outf && $outf eq "INT") {
- $outf = "-";
- $internal = 1;
- }
- my $ret = "";
- my %data;
- for(my $i = 0; $i < int(@a); $i++) {
- my $j = $i+1;
- $data{"min$j"} = undef;
- $data{"max$j"} = undef;
- $data{"avg$j"} = undef;
- $data{"sum$j"} = 0;
- $data{"cnt$j"} = undef;
- $data{"currval$j"} = 0;
- $data{"currdate$j"} = undef;
- $data{"mindate$j"} = undef;
- $data{"maxdate$j"} = undef;
- $data{"xmin$j"} = undef;
- $data{"xmax$j"} = undef;
- my @fld = split(":", $a[$i]);
- if( $a[$i] =~ m/^(FileLog|DbLog):([^:]*):(.*)/ ) {
- my @options = split( ',', $fld[1] );
- my $log_dev = shift(@options);
- my $infile = $fld[0] eq "DbLog" ? "HISTORY" : "CURRENT";
- my $column_specs = $3;
- my $extend;
- my $extend_scale;
- my $offset;
- my $offset_scale;
- my $interpolate;
- my $clip;
- my $predict;
- my $postFn;
- my $scale2reading;
- if( !defined($defs{$log_dev}) ) {
- Log3 $hash->{NAME}, 1, "$hash->{NAME}: $log_dev does not exist";
- $ret .= "#$a[$i]\n";
- next;
- }
- while (@options) {
- my $option = shift(@options);
- while ($option && $option =~ m/=\{/ && $option !~ m/>}/ ) {
- my $next = shift(@options);
- last if( !defined($next) );
- $option .= ",". $next;
- }
- my ($name,$value) = split( '=', $option, 2 );
- if( $value ) {
- $value = eval $value;
- if( $@ ) {
- Log3 $hash->{NAME}, 1, "$hash->{NAME}: $option: $@";
- $ret .= "#$a[$i]\n";
- next;
- }
- }
- if( $name eq "extend" ) {
- $value =~ m/(-?\d*)(m?)/;
- $extend = $1;
- $extend_scale = $2;
- $extend_scale = "" if( !$extend_scale );
- $clip = 1;
- } elsif( $name eq "offset" ) {
- $value =~ m/(-?\d*)(m?)/;
- $offset = $1;
- $offset_scale = $2;
- $offset_scale = "" if( !$offset_scale );
- } elsif( $name eq "interpolate" ) {
- $interpolate = 1;
- } elsif( $name eq "clip" ) {
- $clip = 1;
- } elsif( $name eq "predict" ) {
- $predict = 0;
- $predict = $value if( defined($value) );
- } elsif( $name eq "postFn" ) {
- $postFn = $value;
- } elsif( $name eq "scale2reading" ) {
- $scale2reading = $value if( defined($value) );
- } else {
- Log3 $hash->{NAME}, 2, "$hash->{NAME}: line $i: $fld[0]: unknown option >$option<";
- }
- }
- my $fromsec = SVG_time_to_sec($from);
- my $tosec = SVG_time_to_sec($to);
- my $from = $from;
- my $to = $to;
- # shift $from and $to
- $from = logProxy_shiftTime($from,-$offset.$offset_scale) if( $offset );
- $to = logProxy_shiftTime($to,-$offset.$offset_scale) if( $offset );
- # extend query range
- $from = logProxy_shiftTime($from,-$extend.$extend_scale) if( $extend );
- $to = logProxy_shiftTime($to,$extend.$extend_scale) if( $extend );
- # zoom dependent reading
- if( $scale2reading ) {
- my @fld = split(':', $column_specs, 3);
- my $reading;
- my $zoom = logProxy_Range2Zoom($tosec-$fromsec);
- if( ref($scale2reading) eq "HASH" ) {
- $reading = $scale2reading->{$zoom};
- $reading = $scale2reading->{"$fld[1].$zoom"} if( defined($scale2reading->{"$fld[1].$zoom"}) );
- } elsif($scale2reading =~ m/^{.*}$/) {
- } else {
- no strict "refs";
- $reading = eval {&{$scale2reading}($zoom,$fld[1])};
- use strict "refs";
- if( $@ ) {
- Log3 $hash->{NAME}, 1, "$hash->{NAME}:readingOfScale $a[$i]: $@";
- }
- }
- if( $reading
- && $reading ne $fld[1] ) {
- Log3 $hash->{NAME}, 4, "$hash->{NAME}:scale $zoom: using $reading instead of $fld[1]";
- $fld[1] = $reading;
- $column_specs = join( ':', @fld );
- } else {
- Log3 $hash->{NAME}, 5, "$hash->{NAME}:scale $zoom: keeping $fld[1]";
- }
- }
- $internal_data = "";
- my $cmd = "get $log_dev $infile INT $from $to $column_specs";
- Log3 $hash->{NAME}, 4, "$hash->{NAME}: calling $cmd";
- FW_fC($cmd, 1);
- # shift data and specials back
- logProxy_shiftData($internal_data,$offset.$offset_scale) if( $offset );
- $main::data{"currdate1"} = logProxy_shiftTime($main::data{"currdate1"},$offset.$offset_scale) if( $offset );
- # clip extended query range to plot range
- if( $clip || defined($predict) ) {
- ($internal_data,$main::data{"min1"},$main::data{"max1"}) = logProxy_clipData($internal_data,$fromsec,$tosec,$interpolate,$predict);
- }
- #call postprocessing function
- if( $postFn ) {
- my($data,$comment) = logProxy_data2Array($internal_data);
- $main::data{"avg1"} = undef;
- $main::data{"sum1"} = undef;
- $main::data{"cnt1"} = int(@{$data});
- $main::data{"currdate1"} = undef;
- $main::data{"mindate1"} = undef;
- $main::data{"maxdate1"} = undef;
- no strict "refs";
- my $d = eval {&{$postFn}($a[$i],$data)};
- use strict "refs";
- if( $@ ) {
- Log3 $hash->{NAME}, 1, "$hash->{NAME}: postFn: $a[$i]: $@";
- $ret .= "#$a[$i]\n";
- next;
- }
- $data = $d;
- $comment = "#$a[$i]\n";
- ($internal_data,$main::data{"min1"},$main::data{"max1"},$main::data{"currval1"}) = logProxy_array2Data($data,$comment);
- }
- if( ref($internal_data) eq "SCALAR" && $$internal_data ) {
- $ret .= $$internal_data;
- $data{"min$j"} = $main::data{"min1"};
- $data{"max$j"} = $main::data{"max1"};
- $data{"avg$j"} = $main::data{"avg1"};
- $data{"sum$j"} = $main::data{"sum1"};
- $data{"cnt$j"} = $main::data{"cnt1"};
- $data{"currval$j"} = $main::data{"currval1"};
- $data{"currdate$j"} = $main::data{"currdate1"};
- $data{"mindate$j"} = $main::data{"mindate1"};
- $data{"maxdate$j"} = $main::data{"maxdate1"};
- } else {
- $ret .= "#$a[$i]\n";
- }
- next;
- } elsif( $fld[0] eq "ConstX" && $fld[1] ) {
- $fld[1] = join( ':', @fld[1..@fld-1]);
- my ($t,$y,$y2) = eval $fld[1];
- if( $@ ) {
- Log3 $hash->{NAME}, 1, "$hash->{NAME}: $fld[1]: $@";
- $ret .= "#$a[$i]\n";
- next;
- }
- if( !$t || !defined($y) || $y eq "undef" ) {
- $ret .= "#$a[$i]\n";
- next;
- }
- $t =~ s/ /_/;
- my $from = $t;
- my $to = $t;
- $y2 = $y if( !defined($y2) );
- $data{"min$j"} = $y > $y2 ? $y2 : $y;
- $data{"max$j"} = $y > $y2 ? $y : $y2;
- $data{"avg$j"} = ($y+$y2)/2;
- $data{"cnt$j"} = $y != $y2 ? 2 : 1;
- $data{"curdval$j"} = $y2;
- $data{"curddate$j"} = $to;
- $data{"maxdate$j"} = $to;
- $data{"mindate$j"} = $to;
- $ret .= "$from $y\n";
- $ret .= "$to $y2\n";
- $ret .= "#$a[$i]\n";
- next;
- } elsif( $fld[0] eq "ConstY" && defined($fld[1]) ) {
- $fld[1] = join( ':', @fld[1..@fld-1]);
- my ($y,$f,$t) = eval $fld[1];
- if( $@ ) {
- Log3 $hash->{NAME}, 1, "$hash->{NAME}: $fld[1]: $@";
- $ret .= "#$a[$i]\n";
- next;
- }
- if( !defined($y) || $y eq "undef" ) {
- $ret .= "#$a[$i]\n";
- next;
- }
- $f =~ s/ /_/ if( $f );
- $t =~ s/ /_/ if( $t );
- my $from = $from;
- $from = $f if( $f );
- my $to = $to;
- $to = $t if( $t );
- $data{"min$j"} = $y;
- $data{"max$j"} = $y;
- $data{"avg$j"} = $y;
- $data{"cnt$j"} = 2;
- $data{"currval$j"} = $y;
- $data{"currdate$j"} = $to;
- $data{"maxdate$j"} = $to;
- $data{"mindate$j"} = $to;
- $ret .= "$from $y\n";
- $ret .= "$to $y\n";
- $ret .= "#$a[$i]\n";
- next;
- } elsif( $fld[0] eq "Func" && $fld[1] ) {
- $fld[1] = join( ':', @fld[1..@fld-1]);
- #my $fromsec = SVG_time_to_sec($from);
- #my $tosec = SVG_time_to_sec($to);
- my ($r,$min,$max,$last,$xmin,$xmax) = eval $fld[1];
- if( $@ ) {
- Log3 $hash->{NAME}, 1, "$hash->{NAME}: $fld[1]: $@";
- next;
- }
- $data{"min$j"} = $min;
- $data{"max$j"} = $max;
- $data{"currval$j"} = $last;
- $data{"xmin$j"} = $xmin;
- $data{"xmax$j"} = $xmax;
- $ret .= $r;
- $ret .= "#$a[$i]\n";
- next;
- } elsif( $fld[0] eq "Polar" ) {
- my $axis;
- my $noaxis;
- my $range;
- my $segments;
- my $isolines = "10|20|30";
- my @options = split( ',', $fld[1] );
- foreach my $option ( @options[0..@options-1] ) {
- my ($name,$value) = split( '=', $option, 2 );
- if( $value ) {
- $value = eval $value;
- if( $@ ) {
- Log3 $hash->{NAME}, 1, "$hash->{NAME}: $option: $@";
- $ret .= "#$a[$i]\n";
- next;
- }
- }
- if( $name eq "axis" ) {
- $axis = 1;
- } elsif( $name eq "noaxis" ) {
- $noaxis = 1;
- } elsif( 0 && $name eq "isolines" && defined($value) ) {
- $isolines = $value;
- } elsif( $name eq "segments" && defined($value) ) {
- $segments = $value;
- } elsif( $name eq "range" && defined($value) ) {
- $range = $value;
- } else {
- Log3 $hash->{NAME}, 2, "$hash->{NAME}: line $i: $fld[0]: unknown option >$option<";
- }
- }
- my $values;
- if( defined( $fld[2] ) ) {
- $fld[2] = join( ':', @fld[2..@fld-1]);
- $values = eval $fld[2];
- if( $@ ) {
- Log3 $hash->{NAME}, 1, "$hash->{NAME}: $fld[2]: $@";
- next;
- }
- }
- next if( !$values && !$segments );
- next if( $values && ref($values) ne "ARRAY" );
- $segments = scalar @{$values} if( !$segments );
- next if( !$segments );
- my $isText = $values && @{$values}[0] !~ m/^[.\d+-]*$/;
- $axis = 1 if( $isText );
- $axis = 1 if( !defined($values) && $segments );
- my $f = 3.14159265 / 180;
- if( $segments && defined( $values ) ) {
- my $segment = 0;
- my $first;
- $ret .= ";c 0\n";
- for( my $a = 0; $a < 360; $a += (360/$segments) ) {
- my $value = @{$values}[$segment++];
- next if( !defined($value) );
- my $r;
- if( $isText ) {
- $r = 32;
- $r = 34 if( $a > 90 && $a < 270 );
- } else {
- $r = $value;
- }
- my $x = sin( $a * $f );
- my $y = cos( $a * $f );
- $x *= $r;
- $y *= $r;
- if( $value =~ m/^[.\d+-]*$/ ) {
- $ret .= ";p $x $y\n";
- $first .= ";p $x $y\n" if( !$first );
- } else {
- my $align = "middle";
- #$align = "start" if( $a > 30 && $a < 150 );
- #$align = "end" if( $a > 210 && $a < 330 );
- $align = "start" if( $a > 0 && $a < 180 );
- $align = "end" if( $a > 180 && $a < 360 );
- $ret .= ";t $x $y $align $value\n";
- }
- }
- $ret .= $first if( $first );
- }
- if( $axis && !$noaxis ) {
- my $axis;
- $ret .= ";\n" if( $ret );
- $ret .= ";ls l7\n";
- foreach my $r (split( '\|', $isolines)) {
- $ret .= ";\n"; #FIXME: this is one to many at the end...
- my $first;
- for( my $a = 0; $a < 360; $a += (360/$segments) ) {
- my $x = sin( $a * $f );
- my $y = cos( $a * $f );
- $x *= $r;
- $y *= $r;
- $ret .= ";p $x $y\n";
- $ret .= ";t $x $y start $r\n" if( $a == 0 && ( $r == 10 || $r == 20 ) ) ;
- $first .= ";p $x $y\n" if( !$first );
- if( $r == 30 ) {
- $axis .= ";\n" if( $axis );
- $axis .= ";p 0 0\n";
- $axis .= ";p $x $y\n";
- }
- }
- $ret .= $first;
- }
- $ret .= ";\n";
- $ret .= $axis;
- }
- $ret .= "#$a[0]\n";
- } else {
- Log3 $name, 2, "$name: unknown keyword $fld[0] in column_spec, must be one of $logProxy_columns";
- }
- }
- for(my $i = 0; $i < int(@a); $i++) {
- my $j = $i+1;
- $main::data{"min$j"} = $data{"min$j"};
- $main::data{"max$j"} = $data{"max$j"};
- $main::data{"avg$j"} = $data{"avg$j"};
- $main::data{"sum$j"} = $data{"sum$j"};
- $main::data{"cnt$j"} = $data{"cnt$j"};
- $main::data{"currval$j"} = $data{"currval$j"};
- $main::data{"currdate$j"} = $data{"currdate$j"};
- $main::data{"mindate$j"} = $data{"mindate$j"};
- $main::data{"maxdate$j"} = $data{"maxdate$j"};
- $main::data{"xmin$j"} = $data{"xmin$j"};
- $main::data{"xmax$j"} = $data{"xmax$j"};
- }
- #Log 3, Dumper $ret;
- if( $internal ) {
- $internal_data = \$ret;
- return undef;
- }
- return $ret;
- }
- 1;
- =pod
- =item helper
- =item summary manipulate the date to be plotted in an SVG device
- =item summary_DE manipulation von mit SVG zu plottenden SVG Daten
- =begin html
- <a name="logProxy"></a>
- <h3>logProxy</h3>
- <ul>
- Allows the manipulation of data to be plotted in an SVG device:
- <ul>
- <li>addition of horizontal lines at fixed values</li>
- <li>addition of horizontal lines at dynamic values eg: min, max or average values of another plot </li>
- <li>addition of vertical lines at fixed or dynamic times between two fixed or dynamic y values</li>
- <li>addition of calculated data like week profiles of HeatingControll devices or heating thermostats</li>
- <li>merge plot data from different sources. eg. different FileLog devices</li>
- <li>horizontaly shifting a (merged) plot to align average or statistic data to the correct day,week and month</li>
- </ul>
- <br>
- <a name="logProxy_Define"></a>
- <b>Define</b>
- <ul>
- <code>define <name> logProxy</code><br>
- <br>
- Only one logProxy device can be defined and is needed.<br><br>
- Example:
- <ul>
- <code>define myProxy logProxy</code><br>
- </ul>
- </ul><br>
- <a name="logProxy_Set"></a>
- <b>Set</b>
- <ul>
- </ul><br>
- <a name="logProxy_Get"></a>
- <b>Get</b>
- <ul>
- see <a href="#FileLogget">FileLog</a> and <a href="#DbLogget">DbLog</a>
- </ul><br>
- <a name="logProxy_Attr"></a>
- <b>Attributes</b>
- <ul>
- </ul><br>
- <br>
- <b>#logProxy <column_spec></b><br>
- where <column_spec> can be one or more of the following:
- <ul>
- <li>FileLog:<log device>[,<options>]:<column_spec><br></li>
- <li>DbLog:<log device>[,<options>]:<column_spec><br></li><br>
- options is a comma separated list of zero or more of:<br>
- <ul>
- <li>clip<br>
- clip the plot data to the plot window</li>
- <li>extend=<value><br>
- extend the query range to the log device by <value> seconds (or <value> months if <value> ends in m).
- also activates cliping.</li>
- <li>interpolate<br>
- perform a linear interpolation to the values in the extended range to get the values at the plot boundary. only usefull
- if plotfunction is lines.</li>
- <li>offset=<value><br>
- shift plot by <value> seconds (or <value> months if <value> ends in m).
- allows alignment of values calculated by average or statsitics module to the correct day, week or month. </li>
- <li>predict[=<value>]<br>
- no value -> extend the last plot value to now.<br>
- value -> extend the last plot value by <value> but maximal to now.<br></li>
- <li>postFn='<myPostFn>'<br>
- myPostFn is the name of a postprocessing function that is called after all processing of the data by logProxy
- has been done. it is called with two arguments: the devspec line from the gplot file and a reference to a data
- array containing the points of the plot. each point is an array with three components: the point in time in seconds,
- the value at this point and the point in time in string form. the return value must return a reference to an array
- of the same format. the third component of each point can be omittet and is not evaluated.<br></li>
- <li>scale2reading=<scaleHashRef><br>
- Use zoom step dependent reading names. <br>
- The reading name to be used is the result of a lookup with the current zoom step into <code>scaleHashRef</code>.
- The keys can be from the following list: year, month, week, day, qday, hour<br>
- Example:
- <ul>
- <code>#logProxy DbLog:dbLog,scale2reading={year=>'temperature_avg_day',month=>'temperature_avg_day'}:s300ht_1:temperature::</code><br>
- </ul><br>
- <br></li>
- </ul>
- <li>ConstX:<time>,<y>[,<y2>]<br>
- Will draw a vertical line (or point) at <time> between <y> to <y2>.<br>
- Everything after the : is evaluated as a perl expression that hast to return one time string and one or two y values.<br>
- Examples:
- <ul>
- <code>#logProxy ConstX:$data{currdate1},$data{currval1}</code><br>
- <code>#logProxy ConstX:$data{mindate1},$data{min1},$data{avg1}</code><br>
- <code>#logProxy ConstX:$data{maxdate1},$data{max1},$data{avg1}</code><br>
- <code>#logProxy ConstX:logProxy_shiftTime($from,60*60*2),$data{min1},$data{max1}</code><br>
- </ul></li><br>
- <li>ConstY:<value>[,<from>[,<to>]]<br>
- Will draw a horizontal line at <value>, optional only between the from and to times.<br>
- Everything after the : is evaluated as a perl expression that hast to return one value and optionaly one or two time strings.<br>
- Examples:
- <ul>
- <code>#logProxy ConstY:0</code><br>
- <code>#logProxy ConstY:1234+15+myFunc(123)</code><br>
- <code>#logProxy ConstY:$data{avg1}</code><br>
- <code>#logProxy ConstY:$data{avg2},$from,$to</code><br>
- <code>#logProxy ConstY:$data{avg2},logProxy_shiftTime($from,60*60*12),logProxy_shiftTime($from,-60*60*12)</code>
- </ul></li><br>
- <li>Polar:[<options>]:<values><br>
- Will draw a polar/spiderweb diagram with the given values. <values> has to evaluate to a perl array.<br>
- If <values> contains numbers these values are plottet and the last value will be connected to the first.<br>
- If <values> contains strings these strings are used as labels for the segments.<br>
- The axis are drawn automaticaly if the values are strings or if no values are given but the segments option is set.<br>
- The corrosponding SVG device should have the plotsize attribute set (eg: attr <mySvg> plotsize 340,300) and the used gplot file has to contain xrange and yrange entries and the x- and y-axis labes should be switched off with xtics, ytics and y2tics entries.<br>
- The following example will plot the temperature and desiredTemperature values of all devices named MAX.*:
- <ul>
- <code>set xtics ()</code><br>
- <code>set ytics ()</code><br>
- <code>set y2tics ()</code><br>
- <code>set xrange [-40:40]</code><br>
- <code>set yrange [-40:40]</code><br><br>
- <code>#logProxy Polar::{[map{ReadingsVal($_,"temperature",0)}devspec2array("MAX.*")]}</code><br>
- <code>#logProxy Polar::{[map{ReadingsVal($_,"desiredTemperature",0)}devspec2array("MAX.*")]}</code><br>
- <code>#logProxy Polar::{[map{ReadingsVal($_,"temperature",0)}devspec2array("MAX.*")]}</code><br>
- <code>#logProxy Polar::{[devspec2array("MAX.*")]}</code><br><br>
- <code>plot "<IN>" using 1:2 axes x1y1 title 'Ist' ls l0 lw 1 with lines,\</code><br>
- <code>plot "<IN>" using 1:2 axes x1y1 title 'Soll' ls l1fill lw 1 with lines,\</code><br>
- <code>plot "<IN>" using 1:2 axes x1y1 notitle ls l0 lw 1 with points,\</code><br>
- <code>plot "<IN>" using 1:2 axes x1y1 notitle ls l2 lw 1 with lines,\</code><br>
- </ul><br>
- options is a comma separated list of zero or more of:<br>
- <ul>
- <li>axis<br>
- force to draw the axis</li>
- <li>noaxis<br>
- disable to draw the axis</li>
- <li>range=<value><br>
- the range to use for the radial axis</li>
- <li>segments=<value><br>
- the number of circle/spiderweb segments to use for the plot</li>
- <li>isolines=<value><br>
- a | separated list of values for which an isoline shoud be drawn. defaults to 10|20|30.</li>
- </ul>
- </li><br>
- <li>Func:<perl expression><br>
- Specifies a perl expression that returns the data to be plotted and its min, max and last value. It can not contain
- space or : characters. The data has to be
- one string of newline separated entries of the form: <code>yyyy-mm-dd_hh:mm:ss value</code><br>Example:
- <ul>
- <code>#logProxy Func:logProxy_WeekProfile2Plot("HCB",$from,$to)</code><br>
- <code>#logProxy Func:logProxy_WeekProfile2Plot("myHeatingControll",$from,$to,"(\\d)*\$")</code><br>
- <code>#logProxy Func:logProxy_Func2Plot($from,$to,'{logProxy_hms2dec(sunrise_abs_dat($sec))}')</code><br>
- <code>#logProxy Func:logProxy_Func2Plot($from,$to,'{logProxy_hms2dec(sunset_abs_dat($sec))}')</code><br>
- </ul><br>
- Notes:<ul>
- <li>logProxy_WeekProfile2Plot is a sample implementation of a function that will plot the week profile
- of a Heating_Control, WeekdyTimer, HomeMatic or MAX Thermostat device can be found in the 98_logProxy.pm module file.</li>
- <li>logProxy_Func2Plot($from,$to,$func) is a sample implementation of a function that will evaluate the given
- function (3rd parameter) for a zoom factor dependent number of times. the current time is given in $sec.
- the step width can be given in an optional 4th parameter. either as a number or as an hash with the keys from
- the following list: hour,qday,day,week,month,year and the values representing the step with for the zoom level.</li>
- <li>logProxy_xy2Plot(\@xyArray) is a sample implementation of a function that will accept a ref to an array
- of xy-cordinate pairs as the data to be plotted.</li>
- <li>logProxy_xyFile2Plot($filename,$column,$regex) is a sample implementation of a function that will accept a filename,
- a column number and a regular expression. The requested column in all lines in the file that match the regular expression
- needs to be in the format x,y to indicate the xy-cordinate pairs as the data to be plotted.</li>
- <li>logProxy_values2Plot(\@xyArray) is a sample implementation of a function that will accept a ref to an array
- of date-y-cordinate pairs as the data to be plotted.</li>
- <li>The perl expressions have access to $from and $to for the begining and end of the plot range and also to the
- SVG specials min, max, avg, cnt, sum, currval (last value) and currdate (last date) values of the individual curves
- already plotted are available as $data{<special-n>}.<br>
- <li>logProxy_Range2Zoom($seconds) can be used to get the approximate zoom step for a plot range of $seconds.</li>
- <li>SVG_time_to_sec($timestamp) can be used to convert the timestamp strings to epoch times for calculation.</li>
- </ul>
- </li><br>
- </li><br>
- </ul>
- Please see also the column_spec paragraphs of FileLog, DbLog and SVG.<br>
- <br>
- NOTE: spaces are not allowed inside the colums_specs.<br>
- <br>
- To use any of the logProxy features with an existing plot the associated SVG file hast to be changed to use the logProxy
- device and the .gplot file has to be changed in the following way:<br>
- All existing #FileLog and #Dblog lines have to be changed to #logProxy lines and<br>the column_spec of these line has to
- be prepended by <code>FileLog:<log device>:</code> or <code>DbLog:<log device>:</code> respectively.<br>
- Examples:
- <ul>
- <code>#DbLog <myDevice>:<myReading></code></br>
- <code>#FileLog 4:<SPEC1>:power\x3a::</code><br>
- <code>#FileLog 4:<SPEC1>:consumption\x3a::</code><br><br>
- will become:<br><br>
- <code>#logProxy DbLog:<myDb>:<myDevice>:<myReading></code></br>
- <code>#logProxy FileLog:FileLog_<SPEC1>:4:<SPEC1>.power\x3a::</code><br>
- <code>#logProxy FileLog:FileLog_<SPEC1>:4:<SPEC1>.consumption\x3a::</code><br>
- </ul>
- </ul>
- =end html
- =cut
|