From 707196580a0bd9beb1acd2593f103e76348e0aa0 Mon Sep 17 00:00:00 2001 From: Dima Kogan Date: Fri, 14 Jun 2013 23:13:54 -0700 Subject: [PATCH 01/19] I now always 'exit -1' on error --- bin/feedgnuplot | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/bin/feedgnuplot b/bin/feedgnuplot index 7c0c4e0..091be88 100755 --- a/bin/feedgnuplot +++ b/bin/feedgnuplot @@ -173,7 +173,7 @@ sub interpretCommandline else { print STDERR "--stream can only take in values >=0 or 'trigger'\n"; - exit 1; + exit -1; } } @@ -184,7 +184,7 @@ sub interpretCommandline elsif ( $options->{stream} <= 0) { print STDERR "--stream can only take in values >=0 or 'trigger'\n"; - exit 1; + exit -1; } } @@ -655,7 +655,7 @@ sub getCurve { print STDERR "Tried to exceed the --maxcurves setting.\n"; print STDERR "Invoke with a higher --maxcurves limit if you really want to do this.\n"; - exit; + exit -1; } my ($id) = @_; From 3146dbdfe7c93db3567dcde17145181e3dbcb7db Mon Sep 17 00:00:00 2001 From: Dima Kogan Date: Fri, 14 Jun 2013 23:20:17 -0700 Subject: [PATCH 02/19] cleared up --stream option logic - minor refactoring - --hardcopy and --stream together now generate a warning and turn off --stream --- bin/feedgnuplot | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/bin/feedgnuplot b/bin/feedgnuplot index 091be88..dd13c41 100755 --- a/bin/feedgnuplot +++ b/bin/feedgnuplot @@ -34,18 +34,14 @@ my $dataQueue; my $latestX; my $streamingFinished : shared = undef; + if($options{stream}) { - if( $options{hardcopy}) - { - $options{stream} = undef; - } - $dataQueue = Thread::Queue->new(); my $addThr = threads->create(\&mainThread); # spawn the plot updating thread. If I'm replotting from a data trigger, I don't need this - my $plotThr = threads->create(\&plotUpdateThread) unless $options{stream} < 0; + my $plotThr = threads->create(\&plotUpdateThread) if $options{stream} > 0; while(<>) { @@ -155,14 +151,21 @@ sub interpretCommandline } } - # parse stream option. Allowed only numbers >= 0 or 'trigger' + if ( defined $options->{hardcopy} && defined $options->{stream} ) + { + print STDERR "Warning: since we're making a hardcopy, I'm disabling streaming\n"; + delete $options->{stream}; + } + + # parse stream option. Allowed only numbers >= 0 or 'trigger'. After this code + # $options->{stream} is + # -1 for triggered replotting + # >0 for timed replotting + # undef if not streaming if(defined $options->{stream}) { - if ( $options->{stream} eq '') - { - # if no streaming period is given, default to 1Hz. - $options->{stream} = 1; - } + # if no streaming period is given, default to 1Hz. + $options->{stream} = 1 if $options->{stream} eq ''; if( !looks_like_number $options->{stream} ) { @@ -969,7 +972,7 @@ As an example, if line 3 of the input is "0 9 1 20" --stream [period] Plot the data as it comes in, in realtime. If period is given, replot every period seconds. If no period is given, replot at 1Hz. If the period is given as 0 or 'trigger', replot ONLY when - the incoming data dictates this . See the "Real-time streaming + the incoming data dictates this. See the "Real-time streaming data" section of the man page. --[no]lines Do [not] draw lines to connect consecutive points From 70b7c98124506fb7c5aa8330f5c171c92dce229b Mon Sep 17 00:00:00 2001 From: Dima Kogan Date: Sat, 15 Jun 2013 01:00:42 -0700 Subject: [PATCH 03/19] better handling of non-timer-based replots Previously I was ALWAYS replotting when triggered, ALWAYS replotting on a timer and NEVER replotting on domain rollover with --monotonic. I now have logic to do the right thing in all these cases. I now replot on domain rollover, unless it's happening too quickly. --- bin/feedgnuplot | 109 ++++++++++++++++++++++++++++++++++++------------ 1 file changed, 83 insertions(+), 26 deletions(-) diff --git a/bin/feedgnuplot b/bin/feedgnuplot index dd13c41..f89c180 100755 --- a/bin/feedgnuplot +++ b/bin/feedgnuplot @@ -2,7 +2,7 @@ use strict; use warnings; use Getopt::Long; -use Time::HiRes qw( usleep ); +use Time::HiRes qw( usleep gettimeofday tv_interval ); use IO::Handle; use List::Util qw( first ); use Scalar::Util qw( looks_like_number ); @@ -33,6 +33,18 @@ my $dataQueue; # latest domain variable present in our data my $latestX; +# The domain of the current point +my @domain; + +# Whether any new data has arrived since the last replot +my $haveNewData; + +# when the last replot happened +my $last_replot_time = [gettimeofday]; + +# whether the previous replot was timer based +my $last_replot_is_from_timer = 1; + my $streamingFinished : shared = undef; if($options{stream}) @@ -283,11 +295,12 @@ sub plotUpdateThread while(! $streamingFinished) { usleep( $options{stream} * 1e6 ); - $dataQueue->enqueue('replot'); + + # indicate that the timer was the replot source + $dataQueue->enqueue('replot timertick'); } $dataQueue->enqueue(undef); - } sub mainThread @@ -470,18 +483,20 @@ sub mainThread $pointRE .= '(' . join('\s+', ($numRE) x $valuesPerPoint) . ')'; $pointRE = qr/$pointRE/; - my @domain; - my $haveNewData; - # I should be using the // operator, but I'd like to be compatible with perl 5.8 while( $_ = (defined $dataQueue ? $dataQueue->dequeue() : <>)) { next if /^#/o; - if( $options{stream} && /^clear/o ) + if( $options{stream} && /^clear/o ) { clearCurves(); } - if(! /^replot/o) + elsif( $options{stream} && /^replot/o ) + { + # /timertick/ determines if the timer was the source of the replot + replot( /timertick/ ); + } + elsif(! /^replot/o) { # parse the incoming data lines. The format is # x id0 dat0 id1 dat1 .... @@ -507,7 +522,9 @@ sub mainThread if( defined $latestX && $domain[0] < $latestX ) { # the x-coordinate of the new point is in the past, so I wipe out - # all the data and start anew + # all the data and start anew. Before I wipe the old data, I + # replot the old data + replot(); clearCurves(); $latestX = undef; } @@ -537,26 +554,10 @@ sub mainThread if($1 ne '') {$id = $1;} else {$id++; } - $haveNewData = 1; pushPoint(getCurve($id), [@domain, split( /\s+/, $2)]); } } - - elsif($options{stream}) - { - # we get here if we need to replot AND if we're streaming - next unless $haveNewData; - $haveNewData = undef; - - if( $options{xlen} ) - { - pruneOldData($domain[0] - $options{xlen}); - plotStoredData($domain[0] - $options{xlen}, $domain[0]); - } - else - { plotStoredData(); } - } } # finished reading in all. Plot what we have @@ -708,11 +709,66 @@ sub clearCurves { splice( @$curve, 1 ); } } +sub replot +{ + return unless $haveNewData; + $haveNewData = undef; + + return if !$options{stream}; + + + # The logic involving domain rollover replotting due to --monotonic is a bit + # tricky. I want this: + + # if( domain rolls over slowly ) + # { + # should update on a timer; + # when the domain rolls over, --monotonic should force a replot + # } + # if( domain rolls over quickly ) + # { + # should update when the domain rolls over, + # at most as quickly as the timer indicates + # } + + + my ($replot_is_from_timer) = @_; + + my $now = [gettimeofday]; + + if( # If there is no replot timer at all, replot at any indication + $options{stream} < 0 || + + # if the last replot was timer-based, but this one isn't, force a replot. + # This makes sure that a replot happens for a domain rollover shortly + # after a timer replot + !$replot_is_from_timer && $last_replot_is_from_timer || + + # if enough time has elapsed since the last replot, it's ok to replot + tv_interval ( $last_replot_time, $now ) > 0.8*$options{stream} ) + { + # tests passed; do replot + if ( $options{xlen} ) + { + pruneOldData($domain[0] - $options{xlen}); + plotStoredData($domain[0] - $options{xlen}, $domain[0]); + } + else + { plotStoredData(); } + + + # update replot state + $last_replot_time = $now; + $last_replot_is_from_timer = $replot_is_from_timer; + } +} + # function to add a point to the plot. Assumes that the curve indexed by $idx already exists sub pushPoint { my ($curve, $xy) = @_; push @$curve, $xy; + $haveNewData = 1; } @@ -1072,7 +1128,8 @@ As an example, if line 3 of the input is "0 9 1 20" If a given x-variable is in the past, all data currently cached for this curve is purged. Without --monotonic, all data is kept. Does not make sense with 3d plots. - No --monotonic by default. + No --monotonic by default. The data is replotted before being + purged --extraValuesPerPoint xxx How many extra values are given for each data point. Normally this From ef2bff2af698a81255fee802bbab2f02fe28bdcb Mon Sep 17 00:00:00 2001 From: Dima Kogan Date: Thu, 27 Jun 2013 13:15:49 -0700 Subject: [PATCH 04/19] minor POD fixes, added some recipes --- bin/feedgnuplot | 47 +++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 45 insertions(+), 2 deletions(-) diff --git a/bin/feedgnuplot b/bin/feedgnuplot index f89c180..cce151b 100755 --- a/bin/feedgnuplot +++ b/bin/feedgnuplot @@ -774,11 +774,11 @@ sub pushPoint =head1 NAME -feedgnuplot - Pipe-oriented frontend to Gnuplot +feedgnuplot - General purpose pipe-oriented plotting tool =head1 SYNOPSIS -Simple plotting of stored data: +Simple plotting of piped data: $ seq 5 | awk '{print 2*$1, $1*$1}' 2 1 @@ -1147,6 +1147,49 @@ As an example, if line 3 of the input is "0 9 1 20" --version Print the version and exit +=head1 RECIPES + +=head2 Basic plotting of piped data + + $ seq 5 | awk '{print 2*$1, $1*$1}' + 2 1 + 4 4 + 6 9 + 8 16 + 10 25 + + $ seq 5 | awk '{print 2*$1, $1*$1}' | + feedgnuplot --lines --points --legend 0 "data 0" --title "Test plot" --y2 1 + +=head2 Realtime plot of network throughput + +Looks at wlan0 on Linux. + + $ while true; do sleep 1; cat /proc/net/dev; done | + gawk '/wlan0/ {if(b) {print $2-b; fflush()} b=$2}' | + feedgnuplot --lines --stream --xlen 10 --ylabel 'Bytes/sec' --xlabel seconds + +=head2 Realtime plot of battery charge + +Uses the result of the C command. + + $ while true; do acpi; sleep 15; done | + perl -nE 'BEGIN{ $| = 1; } /([0-9]*)%/; say join(" ", $./4, $1);' | + feedgnuplot --stream --ymin 0 --ymax 100 --domain --xlabel 'Time (seconds)' --ylabel "Battery charge (%)" + +=head2 Realtime plot of temperatures in an IBM Thinkpad + +Uses C, which reports temperatures at various locations +in a Thinkpad. + + $ while true; do cat /proc/acpi/ibm/thermal | awk '{$1=""; print}' ; sleep 1; done | + feedgnuplot --stream --xlen 100 --lines --autolegend --ymax 100 --ymin 20 --ylabel 'Temperature (deg C)' + +=head2 Plotting a histogram of file sizes in a directory + + $ ls -l | awk '{print $5/1e6}' | + feedgnuplot --histogram 0 --curvestyleall 'with boxes' --ymin 0 --xlabel 'File size (MB)' --ylabel Frequency + =head1 ACKNOWLEDGEMENT This program is originally based on the driveGnuPlots.pl script from From 08d2f79fba1ecc2d33beb2d39e391563db13d06e Mon Sep 17 00:00:00 2001 From: Eric Schulte Date: Mon, 12 Aug 2013 16:33:47 -0600 Subject: [PATCH 05/19] added --exit option allowing feedgnuplot to return --- bin/feedgnuplot | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/bin/feedgnuplot b/bin/feedgnuplot index cce151b..f380bab 100755 --- a/bin/feedgnuplot +++ b/bin/feedgnuplot @@ -118,7 +118,7 @@ sub interpretCommandline 'square!', 'square_xy!', 'hardcopy=s', 'maxcurves=i', 'monotonic!', 'histogram=s@', 'binwidth=f', 'histstyle=s', 'terminal=s', - 'extraValuesPerPoint=i', 'help', 'dump', 'version', + 'extraValuesPerPoint=i', 'help', 'dump', 'exit', 'version', 'geometry=s') or pod2usage( -exitval => 1, -verbose => 1, # synopsis and args -output => \*STDERR ); @@ -578,7 +578,7 @@ sub mainThread # we persist gnuplot, so we shouldn't need this sleep. However, once # gnuplot exits, but the persistent window sticks around, you can no # longer interactively zoom the plot. So we still sleep - sleep(100000) unless $options{dump}; + sleep(100000) unless $options{dump} || $options{exit}; } sub pruneOldData @@ -1143,6 +1143,10 @@ As an example, if line 3 of the input is "0 9 1 20" debugging. It is possible to send the output produced this way to gnuplot directly. + --exit Exit the feedgnuplot process after passing data to gnuplot. With + gnuplot versions >= 4.3 the window will persist but will not be + interactive. + --geometry If using X11, specifies the size, position of the plot window --version Print the version and exit From 2f5e9d95ca4f11c2914e3993e70c03aab6bd6e07 Mon Sep 17 00:00:00 2001 From: Dima Kogan Date: Mon, 12 Aug 2013 17:22:22 -0700 Subject: [PATCH 06/19] adusted help message for --exit --- bin/feedgnuplot | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/bin/feedgnuplot b/bin/feedgnuplot index f380bab..28baf8a 100755 --- a/bin/feedgnuplot +++ b/bin/feedgnuplot @@ -1143,9 +1143,12 @@ As an example, if line 3 of the input is "0 9 1 20" debugging. It is possible to send the output produced this way to gnuplot directly. - --exit Exit the feedgnuplot process after passing data to gnuplot. With - gnuplot versions >= 4.3 the window will persist but will not be - interactive. + --exit Terminate the feedgnuplot process after passing data to + gnuplot. The window will persist but will not be + interactive. Without this option feedgnuplot keeps running + and must be killed by the user. Note that this option works + only with later versions of gnuplot and only with some + gnuplot terminals. --geometry If using X11, specifies the size, position of the plot window From b43c9b985f64bca7aa3284918130e53db22a22f6 Mon Sep 17 00:00:00 2001 From: Dima Kogan Date: Tue, 10 Sep 2013 20:40:35 -0700 Subject: [PATCH 07/19] added preliminary support for timefmt --- bin/feedgnuplot | 49 ++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 46 insertions(+), 3 deletions(-) diff --git a/bin/feedgnuplot b/bin/feedgnuplot index 28baf8a..2deac09 100755 --- a/bin/feedgnuplot +++ b/bin/feedgnuplot @@ -115,7 +115,7 @@ sub interpretCommandline 'circles', 'legend=s{2}', 'autolegend!', 'xlabel=s', 'ylabel=s', 'y2label=s', 'zlabel=s', 'title=s', 'xlen=f', 'ymin=f', 'ymax=f', 'xmin=f', 'xmax=f', 'y2min=f', 'y2max=f', 'zmin=f', 'zmax=f', 'y2=s@', 'curvestyle=s{2}', 'curvestyleall=s', 'extracmds=s@', - 'square!', 'square_xy!', 'hardcopy=s', 'maxcurves=i', 'monotonic!', + 'square!', 'square_xy!', 'hardcopy=s', 'maxcurves=i', 'monotonic!', 'timefmt=s', 'histogram=s@', 'binwidth=f', 'histstyle=s', 'terminal=s', 'extraValuesPerPoint=i', 'help', 'dump', 'exit', 'version', @@ -218,6 +218,12 @@ sub interpretCommandline exit -1; } + if ( $options->{timefmt} ) + { + print STDERR "--3d makes no sense with --timefmt\n"; + exit -1; + } + if ( defined $options->{y2min} || defined $options->{y2max} || defined $options->{y2} ) { print STDERR "--3d does not make sense with --y2...\n"; @@ -244,6 +250,12 @@ sub interpretCommandline } else { + if ( $options->{timefmt} && !$options->{domain} ) + { + print STDERR "--timefmt makes sense only with --domain\n"; + exit -1; + } + if(!$options->{colormap}) { if ( defined $options->{zmin} || defined $options->{zmax} || defined $options->{zlabel} ) @@ -274,6 +286,22 @@ sub interpretCommandline print STDERR "unknown histstyle. Allowed are 'freq...', 'cum...', 'uniq...', 'cnorm...'\n"; exit -1; } + + # deal with timefmt + if ( $options->{timefmt} ) + { + # I need to compute a regex to match the time field and I need to count how + # many whilespace-separated fields there are. + + # strip leading and trailing whitespace + $options->{timefmt} =~ s/^\s*//; + $options->{timefmt} =~ s/\s*$//; + + my $Nfields = scalar split( ' ', $options->{timefmt}); + $options->{timefmt_Ncols} = $Nfields; + my $regex_str = join( '\s+', ('\S+') x $Nfields ); + $options->{timefmt_regex} = qr/$regex_str/; + } } sub getGnuplotVersion @@ -456,6 +484,13 @@ sub mainThread addCurveOption($_, 'axes x1y2 linewidth 3'); } +# timefmt + if( $options{timefmt} ) + { + print(PIPE "set timefmt '$options{timefmt}'\n"); + print(PIPE "set xdata time\n"); + } + # add the extra global options if($options{extracmds}) { @@ -477,6 +512,8 @@ sub mainThread # regexp for a possibly floating point, possibly scientific notation number my $numRE = '-?\d*\.?\d+(?:[Ee][-+]?\d+)?'; + my $domainRE = $options{timefmt_regex} || $numRE; + # a point may be preceded by an id my $pointRE = $options{dataid} ? '(\S+)\s+' : '()'; @@ -510,7 +547,7 @@ sub mainThread if($options{domain}) { - /($numRE)/go or next; + /($domainRE)/go or next; $domain[0] = $1; if($options{'3d'}) { @@ -647,7 +684,13 @@ sub updateCurveOptions my $histoptions = $curveoptions->{histoptions} || ''; - $curveoptions->{options} = "$histoptions $titleoption $curveoptions->{extraoptions} $curvestyleall"; + my $usingoptions = ''; + if( $options{timefmt} ) + { + $usingoptions = "using 1:" . ($options{timefmt_Ncols}+1); + } + + $curveoptions->{options} = "$histoptions $usingoptions $titleoption $curveoptions->{extraoptions} $curvestyleall"; } sub getCurve From 756d934058d01cb7fda13c91f75f0dbf6e040b3c Mon Sep 17 00:00:00 2001 From: Dima Kogan Date: Thu, 19 Sep 2013 15:42:29 -0700 Subject: [PATCH 08/19] Simplified use of %options. I now use the global instead of passing it down --- bin/feedgnuplot | 72 ++++++++++++++++++++++++------------------------- 1 file changed, 35 insertions(+), 37 deletions(-) diff --git a/bin/feedgnuplot b/bin/feedgnuplot index 2deac09..905af79 100755 --- a/bin/feedgnuplot +++ b/bin/feedgnuplot @@ -15,7 +15,7 @@ use Pod::Usage; my $VERSION = 1.24; my %options; -interpretCommandline(\%options); +interpretCommandline(); my $gnuplotVersion = getGnuplotVersion(); @@ -93,8 +93,6 @@ sub interpretCommandline unshift @ARGV, shellwords shift @ARGV; } - my $options = shift; - # everything off by default: # do not stream in the data by default # point plotting by default. @@ -111,7 +109,7 @@ sub interpretCommandline $options{legend} = []; $options{curvestyle} = []; $options{histogram} = []; - GetOptions($options, 'stream:s', 'domain!', 'dataid!', '3d!', 'colormap!', 'lines!', 'points!', + GetOptions(\%options, 'stream:s', 'domain!', 'dataid!', '3d!', 'colormap!', 'lines!', 'points!', 'circles', 'legend=s{2}', 'autolegend!', 'xlabel=s', 'ylabel=s', 'y2label=s', 'zlabel=s', 'title=s', 'xlen=f', 'ymin=f', 'ymax=f', 'xmin=f', 'xmax=f', 'y2min=f', 'y2max=f', 'zmin=f', 'zmax=f', 'y2=s@', 'curvestyle=s{2}', 'curvestyleall=s', 'extracmds=s@', @@ -125,21 +123,21 @@ sub interpretCommandline # handle various cmdline-option errors - if ( $options->{help} ) + if ( $options{help} ) { pod2usage( -exitval => 0, -verbose => 1, # synopsis and args -output => \*STDOUT ); } - if( $options->{version} ) + if( $options{version} ) { print "feedgnuplot version $VERSION\n"; exit 0; } # no global style if one isn't given - $options->{curvestyleall} = '' unless defined $options->{curvestyleall}; + $options{curvestyleall} = '' unless defined $options{curvestyleall}; # expand options that are given as comma-separated lists for my $listkey (qw(histogram y2)) @@ -163,27 +161,27 @@ sub interpretCommandline } } - if ( defined $options->{hardcopy} && defined $options->{stream} ) + if ( defined $options{hardcopy} && defined $options{stream} ) { print STDERR "Warning: since we're making a hardcopy, I'm disabling streaming\n"; - delete $options->{stream}; + delete $options{stream}; } # parse stream option. Allowed only numbers >= 0 or 'trigger'. After this code - # $options->{stream} is + # $options{stream} is # -1 for triggered replotting # >0 for timed replotting # undef if not streaming - if(defined $options->{stream}) + if(defined $options{stream}) { # if no streaming period is given, default to 1Hz. - $options->{stream} = 1 if $options->{stream} eq ''; + $options{stream} = 1 if $options{stream} eq ''; - if( !looks_like_number $options->{stream} ) + if( !looks_like_number $options{stream} ) { - if($options->{stream} eq 'trigger') + if($options{stream} eq 'trigger') { - $options->{stream} = 0; + $options{stream} = 0; } else { @@ -192,57 +190,57 @@ sub interpretCommandline } } - if ( $options->{stream} == 0 ) + if ( $options{stream} == 0 ) { - $options->{stream} = -1; + $options{stream} = -1; } - elsif ( $options->{stream} <= 0) + elsif ( $options{stream} <= 0) { print STDERR "--stream can only take in values >=0 or 'trigger'\n"; exit -1; } } - if ($options->{colormap}) + if ($options{colormap}) { # colormap styles all curves with palette. Seems like there should be a way to do this with a # global setting, but I can't get that to work - $options->{curvestyleall} .= ' palette'; + $options{curvestyleall} .= ' palette'; } - if ( $options->{'3d'} ) + if ( $options{'3d'} ) { - if ( !$options->{domain} ) + if ( !$options{domain} ) { print STDERR "--3d only makes sense with --domain\n"; exit -1; } - if ( $options->{timefmt} ) + if ( $options{timefmt} ) { print STDERR "--3d makes no sense with --timefmt\n"; exit -1; } - if ( defined $options->{y2min} || defined $options->{y2max} || defined $options->{y2} ) + if ( defined $options{y2min} || defined $options{y2max} || defined $options{y2} ) { print STDERR "--3d does not make sense with --y2...\n"; exit -1; } - if ( defined $options->{xlen} ) + if ( defined $options{xlen} ) { print STDERR "--3d does not make sense with --xlen\n"; exit -1; } - if ( defined $options->{monotonic} ) + if ( defined $options{monotonic} ) { print STDERR "--3d does not make sense with --monotonic\n"; exit -1; } - if ( defined $options->{binwidth} || @{$options->{histogram}} ) + if ( defined $options{binwidth} || @{$options{histogram}} ) { print STDERR "--3d does not make sense with histograms\n"; exit -1; @@ -250,22 +248,22 @@ sub interpretCommandline } else { - if ( $options->{timefmt} && !$options->{domain} ) + if ( $options{timefmt} && !$options{domain} ) { print STDERR "--timefmt makes sense only with --domain\n"; exit -1; } - if(!$options->{colormap}) + if(!$options{colormap}) { - if ( defined $options->{zmin} || defined $options->{zmax} || defined $options->{zlabel} ) + if ( defined $options{zmin} || defined $options{zmax} || defined $options{zlabel} ) { print STDERR "--zmin/zmax/zlabel only makes sense with --3d or --colormap\n"; exit -1; } } - if ( defined $options->{square_xy} ) + if ( defined $options{square_xy} ) { print STDERR "--square_xy only makes sense with --3d\n"; exit -1; @@ -288,19 +286,19 @@ sub interpretCommandline } # deal with timefmt - if ( $options->{timefmt} ) + if ( $options{timefmt} ) { # I need to compute a regex to match the time field and I need to count how # many whilespace-separated fields there are. # strip leading and trailing whitespace - $options->{timefmt} =~ s/^\s*//; - $options->{timefmt} =~ s/\s*$//; + $options{timefmt} =~ s/^\s*//; + $options{timefmt} =~ s/\s*$//; - my $Nfields = scalar split( ' ', $options->{timefmt}); - $options->{timefmt_Ncols} = $Nfields; + my $Nfields = scalar split( ' ', $options{timefmt}); + $options{timefmt_Ncols} = $Nfields; my $regex_str = join( '\s+', ('\S+') x $Nfields ); - $options->{timefmt_regex} = qr/$regex_str/; + $options{timefmt_regex} = qr/$regex_str/; } } From 3badf920b6fee9a17ea3d1e007c5b4a27a45cda4 Mon Sep 17 00:00:00 2001 From: Dima Kogan Date: Thu, 19 Sep 2013 15:49:30 -0700 Subject: [PATCH 09/19] explicitly fail if we try --stream --xlen --xmin --- bin/feedgnuplot | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/bin/feedgnuplot b/bin/feedgnuplot index 905af79..cb9a411 100755 --- a/bin/feedgnuplot +++ b/bin/feedgnuplot @@ -276,6 +276,13 @@ sub interpretCommandline exit -1; } + if($options{stream} && $options{xlen} && + ( defined $options{xmin} || defined $options{xmax})) + { + print STDERR "With --stream and --xlen the X bounds are set, so neither --xmin nor --xmax make sense\n"; + exit -1; + } + # --xlen implies an order to the data, so I force monotonicity $options{monotonic} = 1 if defined $options{xlen}; From 4e823e7de5881e7381bbe11ed4d8eb6f70e4dc49 Mon Sep 17 00:00:00 2001 From: Dima Kogan Date: Fri, 20 Sep 2013 00:31:25 -0700 Subject: [PATCH 10/19] I no longer grab the gnuplot version if I don't need it --- bin/feedgnuplot | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/bin/feedgnuplot b/bin/feedgnuplot index cb9a411..022fd53 100755 --- a/bin/feedgnuplot +++ b/bin/feedgnuplot @@ -17,8 +17,6 @@ my $VERSION = 1.24; my %options; interpretCommandline(); -my $gnuplotVersion = getGnuplotVersion(); - # list containing the plot data. Each element is a reference to a list, representing the data for # one curve. The first 'point' is a hash describing various curve parameters. The rest are all # references to lists of (x,y) tuples @@ -346,9 +344,9 @@ sub mainThread local *PIPE; my $dopersist = ''; - if($gnuplotVersion >= 4.3) + if( !$options{stream} && getGnuplotVersion() >= 4.3) { - $dopersist = '--persist' if(!$options{stream}); + $dopersist = '--persist'; } if(exists $options{dump}) From 720c332dd92e135cf1520a35dcd827ef7c71daa2 Mon Sep 17 00:00:00 2001 From: Dima Kogan Date: Fri, 20 Sep 2013 02:41:26 -0700 Subject: [PATCH 11/19] Simplified data storage I'm no longer storing the options as the first data point. That was silly, and things are now clearer --- bin/feedgnuplot | 65 +++++++++++++++++++++++++++++-------------------- 1 file changed, 38 insertions(+), 27 deletions(-) diff --git a/bin/feedgnuplot b/bin/feedgnuplot index 022fd53..36617ee 100755 --- a/bin/feedgnuplot +++ b/bin/feedgnuplot @@ -17,9 +17,9 @@ my $VERSION = 1.24; my %options; interpretCommandline(); -# list containing the plot data. Each element is a reference to a list, representing the data for -# one curve. The first 'point' is a hash describing various curve parameters. The rest are all -# references to lists of (x,y) tuples +# list containing the plot data. Each element is a hashref of parameters. +# $curve->{data} is a list of the points. Each point is a listref representing +# the tuple my @curves = (); # list mapping curve names to their indices in the @curves list @@ -627,12 +627,23 @@ sub pruneOldData foreach my $curve (@curves) { - if( @$curve > 1 ) + # get the data listref. Each reference is a listref representing the tuple + my $data = $curve->{data}; + if( @$data ) { - if( my $firstInWindow = first {$curve->[$_][0] >= $oldestx} 1..$#$curve ) - { splice( @$curve, 1, $firstInWindow-1 ); } - else - { splice( @$curve, 1); } + my $firstInWindow = first {$data->[$_][0] >= $oldestx} 0..$#$data; + if( !defined $firstInWindow ) + { + # everything is too old. Clear out all the data + $curve->{data} = []; + } + elsif( $firstInWindow >= 2 ) + { + # clear out everything that's too old, except for one point. This point + # will be off the plot, but if we're plotting lines there will be a + # connecting line to it. Some of the line will be visible + splice( @$data, 0, $firstInWindow-1 ); + } } } } @@ -643,18 +654,18 @@ sub plotStoredData print PIPE "set xrange [$xmin:$xmax]\n" if defined $xmin; # get the options for those curves that have any data - my @nonemptyCurves = grep {@$_ > 1} @curves; - my @extraopts = map {$_->[0]{options}} @nonemptyCurves; + my @nonemptyCurves = grep { @{$_->{data}} } @curves; + my @extraopts = map {$_->{options}} @nonemptyCurves; my $body = join(', ' , map({ "'-' $_" } @extraopts) ); if($options{'3d'}) { print PIPE "splot $body\n"; } else { print PIPE "plot $body\n"; } - foreach my $buf (@nonemptyCurves) + foreach my $curve (@nonemptyCurves) { # send each point to gnuplot. Ignore the first "point" since it's the # curve options - for my $elem (@{$buf}[1..$#$buf]) + for my $elem (@{$curve->{data}}) { print PIPE "@$elem\n"; } @@ -669,13 +680,13 @@ sub updateCurveOptions # case. When no title is specified, gnuplot will still add a legend entry with an unhelpful '-' # label. Thus I explicitly do 'notitle' for that case - my ($curveoptions, $id) = @_; + my ($curve, $id) = @_; # use the given title, unless we're generating a legend automatically. Given titles # override autolegend my $title; - if(defined $curveoptions->{title}) - { $title = $curveoptions->{title}; } + if(defined $curve->{title}) + { $title = $curve->{title}; } elsif( $options{autolegend} ) { $title = $id; } @@ -685,7 +696,7 @@ sub updateCurveOptions $curvestyleall = $options{curvestyleall} if defined $options{curvestyleall} && !defined $options{curvestyle_hash}{$id}; - my $histoptions = $curveoptions->{histoptions} || ''; + my $histoptions = $curve->{histoptions} || ''; my $usingoptions = ''; if( $options{timefmt} ) @@ -693,7 +704,7 @@ sub updateCurveOptions $usingoptions = "using 1:" . ($options{timefmt_Ncols}+1); } - $curveoptions->{options} = "$histoptions $usingoptions $titleoption $curveoptions->{extraoptions} $curvestyleall"; + $curve->{options} = "$histoptions $usingoptions $titleoption $curve->{extraoptions} $curvestyleall"; } sub getCurve @@ -712,10 +723,10 @@ sub getCurve if( !exists $curveIndices{$id} ) { - push @curves, [{extraoptions => ' '}]; # push a curve with no data and no options + push @curves, {extraoptions => ' ', data => []}; # push a curve with no data and no options $curveIndices{$id} = $#curves; - updateCurveOptions($curves[$#curves][0], $id); + updateCurveOptions($curves[$#curves], $id); } return $curves[$curveIndices{$id}]; } @@ -725,8 +736,8 @@ sub addCurveOption my ($id, $str) = @_; my $curve = getCurve($id); - $curve->[0]{extraoptions} .= "$str "; - updateCurveOptions($curve->[0], $id); + $curve->{extraoptions} .= "$str "; + updateCurveOptions($curve, $id); } sub setCurveLabel @@ -734,8 +745,8 @@ sub setCurveLabel my ($id, $str) = @_; my $curve = getCurve($id); - $curve->[0]{title} = $str; - updateCurveOptions($curve->[0], $id); + $curve->{title} = $str; + updateCurveOptions($curve, $id); } sub setCurveAsHistogram @@ -743,16 +754,16 @@ sub setCurveAsHistogram my ($id, $str) = @_; my $curve = getCurve($id); - $curve->[0]{histoptions} = 'using (histbin($2)):(1.0) smooth ' . $options{histstyle}; + $curve->{histoptions} = 'using (histbin($2)):(1.0) smooth ' . $options{histstyle}; - updateCurveOptions($curve->[0], $id); + updateCurveOptions($curve, $id); } # remove all the curve data sub clearCurves { foreach my $curve(@curves) - { splice( @$curve, 1 ); } + { $curve->{data} = []; } } sub replot @@ -813,7 +824,7 @@ sub replot sub pushPoint { my ($curve, $xy) = @_; - push @$curve, $xy; + push @{$curve->{data}}, $xy; $haveNewData = 1; } From 386c5f6d413db9938e251659c1c0c92f5eb606e0 Mon Sep 17 00:00:00 2001 From: Dima Kogan Date: Fri, 20 Sep 2013 03:36:15 -0700 Subject: [PATCH 12/19] slightly simpler raw data parsing. Hopefully a bit quicker. --- bin/feedgnuplot | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/feedgnuplot b/bin/feedgnuplot index 36617ee..6acbb50 100755 --- a/bin/feedgnuplot +++ b/bin/feedgnuplot @@ -595,7 +595,7 @@ sub mainThread else {$id++; } pushPoint(getCurve($id), - [@domain, split( /\s+/, $2)]); + [@domain, $2]); } } } From d76f163be4186860304bc6f9a3f0d52a01208d20 Mon Sep 17 00:00:00 2001 From: Dima Kogan Date: Fri, 20 Sep 2013 19:29:45 -0700 Subject: [PATCH 13/19] even simpler data storage The data for each curve is now stored as one big string that has ALL the data; this string is easily sent to gnuplot at once. There's also a bit of attached meta-data to allow streaming --xlen culling to work --- bin/feedgnuplot | 165 +++++++++++++++++++++++++++++------------------- 1 file changed, 99 insertions(+), 66 deletions(-) diff --git a/bin/feedgnuplot b/bin/feedgnuplot index 6acbb50..867aaac 100755 --- a/bin/feedgnuplot +++ b/bin/feedgnuplot @@ -18,8 +18,15 @@ my %options; interpretCommandline(); # list containing the plot data. Each element is a hashref of parameters. -# $curve->{data} is a list of the points. Each point is a listref representing -# the tuple +# $curve->{datastring} is a string of all the data in this curve that can be +# sent directly to gnuplot. $curve->{datastring_meta} is a hashref {domain => +# ..., offset_start => ...}. offset_start represents a position in the +# datastring where this particular data element begins. As the data is culled +# with --xlen, the offsets are preserved by using $curve->{datastring_offset} to +# represent the offset IN THE ORIGINAL STRING of the current start of the +# datastring + + my @curves = (); # list mapping curve names to their indices in the @curves list @@ -28,12 +35,6 @@ my %curveIndices = (); # now start the data acquisition and plotting threads my $dataQueue; -# latest domain variable present in our data -my $latestX; - -# The domain of the current point -my @domain; - # Whether any new data has arrived since the last replot my $haveNewData; @@ -109,7 +110,7 @@ sub interpretCommandline $options{histogram} = []; GetOptions(\%options, 'stream:s', 'domain!', 'dataid!', '3d!', 'colormap!', 'lines!', 'points!', 'circles', 'legend=s{2}', 'autolegend!', 'xlabel=s', 'ylabel=s', 'y2label=s', 'zlabel=s', - 'title=s', 'xlen=f', 'ymin=f', 'ymax=f', 'xmin=f', 'xmax=f', 'y2min=f', 'y2max=f', + 'title=s', 'xlen=f', 'ymin=f', 'ymax=f', 'xmin=s', 'xmax=s', 'y2min=f', 'y2max=f', 'zmin=f', 'zmax=f', 'y2=s@', 'curvestyle=s{2}', 'curvestyleall=s', 'extracmds=s@', 'square!', 'square_xy!', 'hardcopy=s', 'maxcurves=i', 'monotonic!', 'timefmt=s', 'histogram=s@', 'binwidth=f', 'histstyle=s', @@ -274,7 +275,7 @@ sub interpretCommandline exit -1; } - if($options{stream} && $options{xlen} && + if($options{stream} && defined $options{xlen} && ( defined $options{xmin} || defined $options{xmax})) { print STDERR "With --stream and --xlen the X bounds are set, so neither --xmin nor --xmax make sense\n"; @@ -334,6 +335,26 @@ sub plotUpdateThread $dataQueue->enqueue(undef); } +sub sendRangeCommand +{ + my ($name, $min, $max) = @_; + + return unless defined $min || defined $max; + + if( defined $min ) + { $min = "\"$min\""; } + else + { $min = ''; } + + if( defined $max ) + { $max = "\"$max\""; } + else + { $max = ''; } + + my $cmd = "set $name [$min:$max]\n"; + print PIPE $cmd; +} + sub mainThread { my $valuesPerPoint = 1; @@ -388,22 +409,13 @@ sub mainThread # If a bound isn't given I want to set it to the empty string, so I can communicate it simply to # gnuplot - $options{xmin} = '' unless defined $options{xmin}; - $options{xmax} = '' unless defined $options{xmax}; - $options{ymin} = '' unless defined $options{ymin}; - $options{ymax} = '' unless defined $options{ymax}; - $options{y2min} = '' unless defined $options{y2min}; - $options{y2max} = '' unless defined $options{y2max}; - $options{zmin} = '' unless defined $options{zmin}; - $options{zmax} = '' unless defined $options{zmax}; - print PIPE "set xtics\n"; if($options{y2}) { print PIPE "set ytics nomirror\n"; print PIPE "set y2tics\n"; # if any of the ranges are given, set the range - print PIPE "set y2range [$options{y2min}:$options{y2max}]\n" if length( $options{y2min} . $options{y2max} ); + sendRangeCommand( "y2range", $options{y2min}, $options{y2max} ); } # set up plotting style @@ -416,9 +428,10 @@ sub mainThread } # if any of the ranges are given, set the range - print PIPE "set xrange [$options{xmin}:$options{xmax}]\n" if length( $options{xmin} . $options{xmax} ); - print PIPE "set yrange [$options{ymin}:$options{ymax}]\n" if length( $options{ymin} . $options{ymax} ); - print PIPE "set zrange [$options{zmin}:$options{zmax}]\n" if length( $options{zmin} . $options{zmax} ); + sendRangeCommand( "xrange", $options{xmin}, $options{xmax} ); + sendRangeCommand( "yrange", $options{ymin}, $options{ymax} ); + sendRangeCommand( "zrange", $options{zmin}, $options{zmax} ); + print PIPE "set style data $style\n" if $style; print PIPE "set grid\n"; @@ -448,7 +461,7 @@ sub mainThread if($options{colormap}) { - print PIPE "set cbrange [$options{zmin}:$options{zmax}]\n" if length( $options{zmin} . $options{zmax} ); + sendRangeCommand( "cbrange", $options{zmin}, $options{zmax} ); } # For the specified values, set the legend entries to 'title "blah blah"' @@ -523,6 +536,15 @@ sub mainThread $pointRE .= '(' . join('\s+', ($numRE) x $valuesPerPoint) . ')'; $pointRE = qr/$pointRE/; + + + # latest domain variable present in our data + my $latestX; + + # The domain of the current point + my @domain; + + # I should be using the // operator, but I'd like to be compatible with perl 5.8 while( $_ = (defined $dataQueue ? $dataQueue->dequeue() : <>)) { @@ -534,7 +556,7 @@ sub mainThread elsif( $options{stream} && /^replot/o ) { # /timertick/ determines if the timer was the source of the replot - replot( /timertick/ ); + replot( $domain[0], /timertick/ ); } elsif(! /^replot/o) { @@ -564,7 +586,7 @@ sub mainThread # the x-coordinate of the new point is in the past, so I wipe out # all the data and start anew. Before I wipe the old data, I # replot the old data - replot(); + replot( $domain[0] ); clearCurves(); $latestX = undef; } @@ -595,7 +617,7 @@ sub mainThread else {$id++; } pushPoint(getCurve($id), - [@domain, $2]); + "@domain $2\n", $domain[0]); } } } @@ -623,38 +645,40 @@ sub mainThread sub pruneOldData { - my ($oldestx) = @_; + my ($x, $xlen) = @_; + my $oldestx = $x - $xlen; foreach my $curve (@curves) { - # get the data listref. Each reference is a listref representing the tuple - my $data = $curve->{data}; - if( @$data ) + next unless $curve->{datastring}; + + my $meta = $curve->{datastring_meta}; + + my $firstInWindow = first {$meta->[$_]{domain} >= $oldestx} 0..$#$meta; + if ( !defined $firstInWindow ) { - my $firstInWindow = first {$data->[$_][0] >= $oldestx} 0..$#$data; - if( !defined $firstInWindow ) - { - # everything is too old. Clear out all the data - $curve->{data} = []; - } - elsif( $firstInWindow >= 2 ) - { - # clear out everything that's too old, except for one point. This point - # will be off the plot, but if we're plotting lines there will be a - # connecting line to it. Some of the line will be visible - splice( @$data, 0, $firstInWindow-1 ); - } + # everything is too old. Clear out all the data + $curve->{datastring} = ''; + $curve->{datastring_meta} = []; + $curve->{datastring_offset} = 0; + } + elsif ( $firstInWindow >= 2 ) + { + # clear out everything that's too old, except for one point. This point + # will be off the plot, but if we're plotting lines there will be a + # connecting line to it. Some of the line will be visible + substr( $curve->{datastring}, 0, + $meta->[$firstInWindow-1]{offset_start} - $curve->{datastring_offset}, + '' ); + $curve->{datastring_offset} = $meta->[$firstInWindow-1]{offset_start}; } } } sub plotStoredData { - my ($xmin, $xmax) = @_; - print PIPE "set xrange [$xmin:$xmax]\n" if defined $xmin; - - # get the options for those curves that have any data - my @nonemptyCurves = grep { @{$_->{data}} } @curves; + # get the options for those curves that havse any data + my @nonemptyCurves = grep { $_->{datastring} } @curves; my @extraopts = map {$_->{options}} @nonemptyCurves; my $body = join(', ' , map({ "'-' $_" } @extraopts) ); @@ -663,12 +687,7 @@ sub plotStoredData foreach my $curve (@nonemptyCurves) { - # send each point to gnuplot. Ignore the first "point" since it's the - # curve options - for my $elem (@{$curve->{data}}) - { - print PIPE "@$elem\n"; - } + print PIPE $curve->{datastring}; print PIPE "e\n"; } } @@ -723,7 +742,10 @@ sub getCurve if( !exists $curveIndices{$id} ) { - push @curves, {extraoptions => ' ', data => []}; # push a curve with no data and no options + push @curves, {extraoptions => ' ', + datastring => '', + datastring_meta => [], + datastring_offset => 0}; # push a curve with no data and no options $curveIndices{$id} = $#curves; updateCurveOptions($curves[$#curves], $id); @@ -763,7 +785,11 @@ sub setCurveAsHistogram sub clearCurves { foreach my $curve(@curves) - { $curve->{data} = []; } + { + $curve->{datastring} = ''; + $curve->{datastring_meta} = []; + $curve->{datastring_offset} = 0; + } } sub replot @@ -789,7 +815,7 @@ sub replot # } - my ($replot_is_from_timer) = @_; + my ($domain0, $replot_is_from_timer) = @_; my $now = [gettimeofday]; @@ -804,14 +830,17 @@ sub replot # if enough time has elapsed since the last replot, it's ok to replot tv_interval ( $last_replot_time, $now ) > 0.8*$options{stream} ) { - # tests passed; do replot - if ( $options{xlen} ) + # ok, then. We really need to replot + if ( defined $options{xlen} ) { - pruneOldData($domain[0] - $options{xlen}); - plotStoredData($domain[0] - $options{xlen}, $domain[0]); + # we have an --xlen, so we need to clean out the old data + pruneOldData( $domain0, $options{xlen} ); + + my ($xmin, $xmax) = ($domain0 - $options{xlen}, $domain0); + sendRangeCommand( "xrange", $xmin, $xmax ); } - else - { plotStoredData(); } + + plotStoredData(); # update replot state @@ -823,8 +852,12 @@ sub replot # function to add a point to the plot. Assumes that the curve indexed by $idx already exists sub pushPoint { - my ($curve, $xy) = @_; - push @{$curve->{data}}, $xy; + my ($curve, $datastring, $domain0) = @_; + + push @{$curve->{datastring_meta}}, { offset_start => length( $curve->{datastring} ) + $curve->{datastring_offset}, + domain => $domain0 }; + $curve->{datastring} .= $datastring; + $haveNewData = 1; } From c169330fca8dff61b238a8526f5c4a0af087339c Mon Sep 17 00:00:00 2001 From: Dima Kogan Date: Fri, 20 Sep 2013 22:06:18 -0700 Subject: [PATCH 14/19] timefmt plots now work with streaming and with --xlen --- bin/feedgnuplot | 122 ++++++++++++++++++++++++++++++------------------ 1 file changed, 77 insertions(+), 45 deletions(-) diff --git a/bin/feedgnuplot b/bin/feedgnuplot index 867aaac..b3c8d0f 100755 --- a/bin/feedgnuplot +++ b/bin/feedgnuplot @@ -11,6 +11,7 @@ use threads; use threads::shared; use Thread::Queue; use Pod::Usage; +use Time::Piece; my $VERSION = 1.24; @@ -355,6 +356,21 @@ sub sendRangeCommand print PIPE $cmd; } +sub makeDomainNumeric +{ + my ($domain0) = @_; + + if ( $options{timefmt} ) + { + my $timepiece = Time::Piece->strptime( $domain0, $options{timefmt} ) + or die "Couldn't parse time format. String '$domain0' doesn't fit format '$options{timefmt}'"; + + return $timepiece->epoch(); + } + + return $domain0; +} + sub mainThread { my $valuesPerPoint = 1; @@ -406,18 +422,6 @@ sub mainThread print PIPE "set terminal $options{terminal}\n" if $options{terminal}; print PIPE "set output \"$outputfile\"\n" if $outputfile; - - # If a bound isn't given I want to set it to the empty string, so I can communicate it simply to - # gnuplot - print PIPE "set xtics\n"; - if($options{y2}) - { - print PIPE "set ytics nomirror\n"; - print PIPE "set y2tics\n"; - # if any of the ranges are given, set the range - sendRangeCommand( "y2range", $options{y2min}, $options{y2max} ); - } - # set up plotting style my $style = ''; if($options{lines}) { $style .= 'lines';} @@ -427,11 +431,6 @@ sub mainThread $options{curvestyleall} = "with circles $options{curvestyleall}"; } - # if any of the ranges are given, set the range - sendRangeCommand( "xrange", $options{xmin}, $options{xmax} ); - sendRangeCommand( "yrange", $options{ymin}, $options{ymax} ); - sendRangeCommand( "zrange", $options{zmin}, $options{zmax} ); - print PIPE "set style data $style\n" if $style; print PIPE "set grid\n"; @@ -459,13 +458,8 @@ sub mainThread print(PIPE "set view equal xy\n"); } - if($options{colormap}) - { - sendRangeCommand( "cbrange", $options{zmin}, $options{zmax} ); - } - # For the specified values, set the legend entries to 'title "blah blah"' - if(@{$options{legend}}) + if(defined $options{legend} && @{$options{legend}}) { # @{$options{legend}} is a list where consecutive pairs are (curveID, # legend). I use $options{legend} here instead of $options{legend_hash} @@ -480,7 +474,7 @@ sub mainThread } # add the extra curve options - if(@{$options{curvestyle}}) + if(defined $options{curvestyle} && @{$options{curvestyle}}) { # @{$options{curvestyle}} is a list where consecutive pairs are (curveID, # style). I use $options{curvestyle} here instead of @@ -495,9 +489,12 @@ sub mainThread } # For the values requested to be printed on the y2 axis, set that - foreach (@{$options{y2}}) + if( defined $options{y2} ) { - addCurveOption($_, 'axes x1y2 linewidth 3'); + foreach (@{$options{y2}}) + { + addCurveOption($_, 'axes x1y2 linewidth 3'); + } } # timefmt @@ -508,7 +505,7 @@ sub mainThread } # add the extra global options - if($options{extracmds}) + if(defined $options{extracmds}) { foreach (@{$options{extracmds}}) { @@ -517,13 +514,16 @@ sub mainThread } # set up histograms - $options{binwidth} ||= 1; # if no binwidth given, set it to 1 - print PIPE - "set boxwidth $options{binwidth}\n" . - "histbin(x) = $options{binwidth} * floor(0.5 + x/$options{binwidth})\n"; - foreach (@{$options{histogram}}) + if( defined $options{histogram} ) { - setCurveAsHistogram( $_ ); + $options{binwidth} ||= 1; # if no binwidth given, set it to 1 + print PIPE + "set boxwidth $options{binwidth}\n" . + "histbin(x) = $options{binwidth} * floor(0.5 + x/$options{binwidth})\n"; + foreach (@{$options{histogram}}) + { + setCurveAsHistogram( $_ ); + } } # regexp for a possibly floating point, possibly scientific notation number @@ -536,6 +536,26 @@ sub mainThread $pointRE .= '(' . join('\s+', ($numRE) x $valuesPerPoint) . ')'; $pointRE = qr/$pointRE/; +# set all the axis ranges + # If a bound isn't given I want to set it to the empty string, so I can communicate it simply to + # gnuplot + print PIPE "set xtics\n"; + + if($options{y2}) + { + print PIPE "set ytics nomirror\n"; + print PIPE "set y2tics\n"; + # if any of the ranges are given, set the range + sendRangeCommand( "y2range", $options{y2min}, $options{y2max} ); + } + + # if any of the ranges are given, set the range + sendRangeCommand( "xrange", $options{xmin}, $options{xmax} ); + sendRangeCommand( "yrange", $options{ymin}, $options{ymax} ); + sendRangeCommand( "zrange", $options{zmin}, $options{zmax} ); + sendRangeCommand( "cbrange", $options{zmin}, $options{zmax} ) if($options{colormap}); + + # latest domain variable present in our data @@ -544,6 +564,10 @@ sub mainThread # The domain of the current point my @domain; + # The x-axis domain represented as a number. This is exactly the same as + # $domain[0] unless the x-axis domain uses a timefmt. Then this is the + # number of seconds since the UNIX epoch. + my $domain0_numeric; # I should be using the // operator, but I'd like to be compatible with perl 5.8 while( $_ = (defined $dataQueue ? $dataQueue->dequeue() : <>)) @@ -556,7 +580,7 @@ sub mainThread elsif( $options{stream} && /^replot/o ) { # /timertick/ determines if the timer was the source of the replot - replot( $domain[0], /timertick/ ); + replot( $domain0_numeric, /timertick/ ); } elsif(! /^replot/o) { @@ -574,6 +598,8 @@ sub mainThread { /($domainRE)/go or next; $domain[0] = $1; + $domain0_numeric = makeDomainNumeric( $domain[0] ); + if($options{'3d'}) { /($numRE)/go or next; @@ -581,17 +607,17 @@ sub mainThread } elsif( $options{monotonic} ) { - if( defined $latestX && $domain[0] < $latestX ) + if( defined $latestX && $domain0_numeric < $latestX ) { # the x-coordinate of the new point is in the past, so I wipe out # all the data and start anew. Before I wipe the old data, I # replot the old data - replot( $domain[0] ); + replot( $domain0_numeric ); clearCurves(); $latestX = undef; } else - { $latestX = $domain[0]; } + { $latestX = $domain0_numeric; } } } @@ -608,6 +634,7 @@ sub mainThread { $domain[0] = $.; } + $domain0_numeric = makeDomainNumeric( $domain[0] ); } my $id = -1; @@ -617,7 +644,7 @@ sub mainThread else {$id++; } pushPoint(getCurve($id), - "@domain $2\n", $domain[0]); + "@domain $2\n", $domain0_numeric); } } } @@ -645,8 +672,7 @@ sub mainThread sub pruneOldData { - my ($x, $xlen) = @_; - my $oldestx = $x - $xlen; + my ($oldestx) = @_; foreach my $curve (@curves) { @@ -815,7 +841,7 @@ sub replot # } - my ($domain0, $replot_is_from_timer) = @_; + my ($domain0_numeric, $replot_is_from_timer) = @_; my $now = [gettimeofday]; @@ -834,9 +860,15 @@ sub replot if ( defined $options{xlen} ) { # we have an --xlen, so we need to clean out the old data - pruneOldData( $domain0, $options{xlen} ); + pruneOldData( $domain0_numeric - $options{xlen} ); - my ($xmin, $xmax) = ($domain0 - $options{xlen}, $domain0); + my ($xmin, $xmax) = ($domain0_numeric - $options{xlen}, $domain0_numeric); + if ( defined $options{timefmt} ) + { + # if we're using a timefmt, I need to convert my xmin range from + # seconds-since-the-epoch BACK to the timefmt. Sheesh + ($xmin, $xmax) = map {Time::Piece->strptime( $_, '%s' )->strftime( $options{timefmt} ) } ($xmin, $xmax); + } sendRangeCommand( "xrange", $xmin, $xmax ); } @@ -852,10 +884,10 @@ sub replot # function to add a point to the plot. Assumes that the curve indexed by $idx already exists sub pushPoint { - my ($curve, $datastring, $domain0) = @_; + my ($curve, $datastring, $domain0_numeric) = @_; push @{$curve->{datastring_meta}}, { offset_start => length( $curve->{datastring} ) + $curve->{datastring_offset}, - domain => $domain0 }; + domain => $domain0_numeric }; $curve->{datastring} .= $datastring; $haveNewData = 1; From c615c8d65e8c7e26476f0e73dbaed29d01522e35 Mon Sep 17 00:00:00 2001 From: Dima Kogan Date: Fri, 20 Sep 2013 22:10:38 -0700 Subject: [PATCH 15/19] reordered bash completion entries in alphabetical order. removed duplicate --zlabel --- completions/bash/feedgnuplot | 85 ++++++++++++++++++------------------ 1 file changed, 42 insertions(+), 43 deletions(-) diff --git a/completions/bash/feedgnuplot b/completions/bash/feedgnuplot index b571d63..fd65ee5 100644 --- a/completions/bash/feedgnuplot +++ b/completions/bash/feedgnuplot @@ -1,43 +1,42 @@ -complete -W \ -' \ - --domain \ - --dataid \ - --3d \ - --colormap \ - --stream \ - --lines \ - --points \ - --circles \ - --xlabel \ - --ylabel \ - --y2label \ - --zlabel \ - --zlabel \ - --title \ - --autolegend \ - --xlen \ - --xmin \ - --xmax \ - --ymin \ - --ymax \ - --y2min \ - --y2max \ - --zmin \ - --zmax \ - --y2 \ - --curvestyleall \ - --extracmds \ - --square \ - --square_xy \ - --hardcopy \ - --maxcurves \ - --monotonic \ - --extraValuesPerPoint \ - --dump \ - --geometry \ - --curvestyle \ - --histogram \ - --binwidth \ - --histstyle \ - --terminal \ - --legend' feedgnuplot +complete -W \ +' \ + --3d \ + --autolegend \ + --binwidth \ + --circles \ + --colormap \ + --curvestyle \ + --curvestyleall \ + --dataid \ + --domain \ + --dump \ + --extraValuesPerPoint \ + --extracmds \ + --geometry \ + --hardcopy \ + --histogram \ + --histstyle \ + --legend \ + --lines \ + --maxcurves \ + --monotonic \ + --points \ + --square \ + --square_xy \ + --stream \ + --terminal \ + --title \ + --xlabel \ + --xlen \ + --xmax \ + --xmin \ + --y2 \ + --y2label \ + --y2max \ + --y2min \ + --ylabel \ + --ymax \ + --ymin \ + --zlabel \ + --zmax \ + --zmin' feedgnuplot From 3c0743c1922246bf405c1be366a6a4a2f744ecdb Mon Sep 17 00:00:00 2001 From: Dima Kogan Date: Fri, 20 Sep 2013 22:31:15 -0700 Subject: [PATCH 16/19] Updated bash, zsh completions with new commands. zsh completion has better prompts --- completions/bash/feedgnuplot | 4 ++ completions/zsh/_feedgnuplot | 88 +++++++++++++++++++----------------- 2 files changed, 50 insertions(+), 42 deletions(-) diff --git a/completions/bash/feedgnuplot b/completions/bash/feedgnuplot index fd65ee5..c0614f1 100644 --- a/completions/bash/feedgnuplot +++ b/completions/bash/feedgnuplot @@ -10,10 +10,12 @@ complete -W \ --dataid \ --domain \ --dump \ + --exit \ --extraValuesPerPoint \ --extracmds \ --geometry \ --hardcopy \ + --help \ --histogram \ --histstyle \ --legend \ @@ -25,7 +27,9 @@ complete -W \ --square_xy \ --stream \ --terminal \ + --timefmt \ --title \ + --version \ --xlabel \ --xlen \ --xmax \ diff --git a/completions/zsh/_feedgnuplot b/completions/zsh/_feedgnuplot index 1da88c9..fbc1a42 100644 --- a/completions/zsh/_feedgnuplot +++ b/completions/zsh/_feedgnuplot @@ -1,44 +1,48 @@ #compdef feedgnuplot -_arguments -S \ - '()--domain[first element of each line is the domain variable]' \ - '--dataid[each data point is preceded by the corresponding ID]' \ - '(--monotonic --xlen --histogram)--3d' \ - '--colormap[show a colormapped xy plot]' \ - '--stream[Plot the data in realtime]::period in s:' \ - '--lines' \ - '--points' \ - '--circles' \ - '--xlabel:X-axis label:' \ - '--ylabel:Y-axis label:' \ - '--y2label:Y2-axis label:' \ - '--zlabel:Z-axis label:' \ - '--zlabel:Z-axis label:' \ - '--title:Plot title:' \ - '--autolegend[Label each plot with its data ID]' \ - '(--3d)--xlen[the size of the x-window to plot]::window size:' \ - '--xmin:min X:' \ - '--xmax:max X:' \ - '--ymin:min Y:' \ - '--ymax:max Y:' \ - '--y2min:min Y2:' \ - '--y2max:max Y2:' \ - '--zmin:min Z:' \ - '--zmax:max Z:' \ - '*--y2:plot to place on the Y2 axis:' \ - '--curvestyleall[Additional styles for ALL curves]:' \ - '*--extracmds[Additional gnuplot commands]:' \ - '--square[Plot data with square aspect ratio]' \ - '--square_xy[For 3D plots, set square aspect ratio for ONLY the x,y axes]' \ - '--hardcopy[Plot to a file]:' \ - '--maxcurves[The maximum allowed number of curves]:' \ - '(--3d)--monotonic[Resets plot if an X in the past is seen]' \ - '--extraValuesPerPoint[How many extra values are given for each data point]:' \ - '--dump[Instead of printing to gnuplot, print to STDOUT]' \ - '--geometry[The X11 geometry string]:geometry string:' \ - '*--curvestyle[Additional styles for a curve]:curve id: :style:' \ - '(--3d)*--histogram:plot to treat as a histogram:' \ - '--binwidth:Histogram bin width:' \ - '--histstyle:Style of histogram:(frequency unique cumulative cnormal)' \ - '--terminal:Terminal options to set with "set terminal":' \ - '*--legend[Legend for a curve]:curve id: :legend:' +_arguments -S \ + '()--domain[first element of each line is the domain variable]' \ + '--dataid[each data point is preceded by the corresponding ID]' \ + '(--monotonic --xlen --histogram)--3d' \ + '--colormap[show a colormapped xy plot]' \ + '--stream[Plot the data in realtime]::period in s:' \ + '--lines' \ + '--points' \ + '--circles' \ + '--xlabel:X-axis label:' \ + '--ylabel:Y-axis label:' \ + '--y2label:Y2-axis label:' \ + '--zlabel:Z-axis label:' \ + '--zlabel:Z-axis label:' \ + '--title:Plot title:' \ + '--autolegend[Label each plot with its data ID]' \ + '(--3d)--xlen[the size of the x-window to plot]::window size:' \ + '(--xlen)--xmin:min X:' \ + '(--xlen)--xmax:max X:' \ + '--ymin:min Y:' \ + '--ymax:max Y:' \ + '--y2min:min Y2:' \ + '--y2max:max Y2:' \ + '--zmin:min Z:' \ + '--zmax:max Z:' \ + '*--y2:plot to place on the Y2 axis:' \ + '--curvestyleall[Additional styles for ALL curves]:style' \ + '*--extracmds[Additional gnuplot commands]:command' \ + '--square[Plot data with square aspect ratio]' \ + '--square_xy[For 3D plots, set square aspect ratio for ONLY the x,y axes]' \ + '--hardcopy[Plot to a file]:filename' \ + '--maxcurves[The maximum allowed number of curves]:number of curves' \ + '(--3d)--monotonic[Resets plot if an X in the past is seen]' \ + '--extraValuesPerPoint[How many extra values are given for each data point]:N'\ + '--dump[Instead of printing to gnuplot, print to STDOUT]' \ + '--geometry[The X11 geometry string]:geometry string:' \ + '*--curvestyle[Additional styles for a curve]:curve id: :style:' \ + '(--3d)*--histogram:plot to treat as a histogram:' \ + '--binwidth:Histogram bin width:' \ + '--histstyle:Style of histogram:(frequency unique cumulative cnormal)' \ + '--terminal:Terminal options to set with "set terminal":' \ + '*--legend[Legend for a curve]:curve id: :legend:' \ + '--exit[Exit gnuplot after making the plot]' \ + '--version' \ + '--help' \ + '--timefmt[Format for time/date data]:time format' From f0225903440271bda1a9d6454d1a1c3f8947ee81 Mon Sep 17 00:00:00 2001 From: Dima Kogan Date: Fri, 20 Sep 2013 22:45:53 -0700 Subject: [PATCH 17/19] added timefmt documentation --- bin/feedgnuplot | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/bin/feedgnuplot b/bin/feedgnuplot index b3c8d0f..45bb4c1 100755 --- a/bin/feedgnuplot +++ b/bin/feedgnuplot @@ -1016,6 +1016,29 @@ C and we're plotting in realtime with C<--stream>, the plot will be refreshed immediately. If a line of data begins with C, the plot is cleared, to be re-filled with any data following the C. +=head3 Time/date data + +If the input data domain is a time/date, this can be interpreted with +C<--timefmt>. This option takes a single argument: the format to use to parse +the data. The format is documented in 'set timefmt' in gnuplot, although the +common flags that L understands are generally supported. The backslash +sequences in the format are I supported, so if you want a tab, put in a tab +instead of \t. When this flag is given, some other options act a little bit +differently: + +=over + +=item + +C<--xlen> is in seconds + +=item + +C<--xmin> and C<--xmax> I use the format passed in to C<--timefmt> + +=back + + =head2 Real-time streaming data To plot real-time data, pass in the C<--stream [refreshperiod]> option. Data @@ -1030,7 +1053,8 @@ To plot only the most recent data (instead of I the data), C<--xlen windowsize> can be given. This will create an constantly-updating, scrolling view of the recent past. C should be replaced by the desired length of the domain window to plot, in domain units (passed-in values if C<--domain> -or line numbers otherwise). +or line numbers otherwise). If the domain is a time/date via C<--timefmt>, then +C is in seconds. =head2 Hardcopy output @@ -1143,6 +1167,9 @@ As an example, if line 3 of the input is "0 9 1 20" --[no]3d Do [not] plot in 3D. This only makes sense with --domain. Each domain here is an (x,y) tuple + --timefmt [format] Interpret the X data as a time/date, parsed with the given + format + --colormap Show a colormapped xy plot. Requires extra data for the color. zmin/zmax can be used to set the extents of the colors. Automatically increments extraValuesPerPoint From 809442e2dccecf97c5558552cc3ec79bb775b585 Mon Sep 17 00:00:00 2001 From: Dima Kogan Date: Fri, 20 Sep 2013 22:55:04 -0700 Subject: [PATCH 18/19] reformatted docs. no content changes --- bin/feedgnuplot | 442 ++++++++++++++++++++++++++++++++++-------------- 1 file changed, 311 insertions(+), 131 deletions(-) diff --git a/bin/feedgnuplot b/bin/feedgnuplot index 45bb4c1..cf854b1 100755 --- a/bin/feedgnuplot +++ b/bin/feedgnuplot @@ -1140,168 +1140,348 @@ it to the plotter. =head1 ARGUMENTS - --[no]domain If enabled, the first element of each line is the - domain variable. If not, the point index is used +=over - --[no]dataid If enabled, each data point is preceded by the ID - of the data set that point corresponds to. This ID is - interpreted as a string, NOT as just a number. If not - enabled, the order of the point is used. +=item -As an example, if line 3 of the input is "0 9 1 20" - '--nodomain --nodataid' would parse the 4 numbers as points in 4 - different curves at x=3 +--[no]domain - '--domain --nodataid' would parse the 4 numbers as points in 3 different - curves at x=0. Here, 0 is the x-variable and 9,1,20 are the data values +If enabled, the first element of each line is the domain variable. If not, the +point index is used - '--nodomain --dataid' would parse the 4 numbers as points in 2 different - curves at x=3. Here 0 and 1 are the data IDs and 9 and 20 are the - data values +=item - '--domain --dataid' would parse the 4 numbers as a single point at - x=0. Here 9 is the data ID and 1 is the data value. 20 is an extra - value, so it is ignored. If another value followed 20, we'd get another - point in curve ID 20 +--[no]dataid - --[no]3d Do [not] plot in 3D. This only makes sense with --domain. - Each domain here is an (x,y) tuple +If enabled, each data point is preceded by the ID of the data set that point +corresponds to. This ID is interpreted as a string, NOT as just a number. If not +enabled, the order of the point is used. - --timefmt [format] Interpret the X data as a time/date, parsed with the given - format +As an example, if line 3 of the input is "0 9 1 20" then - --colormap Show a colormapped xy plot. Requires extra data for the color. - zmin/zmax can be used to set the extents of the colors. - Automatically increments extraValuesPerPoint +=over - --stream [period] Plot the data as it comes in, in realtime. If period is given, - replot every period seconds. If no period is given, replot at - 1Hz. If the period is given as 0 or 'trigger', replot ONLY when - the incoming data dictates this. See the "Real-time streaming - data" section of the man page. +=item - --[no]lines Do [not] draw lines to connect consecutive points - --[no]points Do [not] draw points - --circles Plot with circles. This requires a radius be specified for - each point. Automatically increments extraValuesPerPoint +'--nodomain --nodataid' would parse the 4 numbers as points in 4 different +curves at x=3 - --xlabel xxx Set x-axis label - --ylabel xxx Set y-axis label - --y2label xxx Set y2-axis label. Does not apply to 3d plots - --zlabel xxx Set y-axis label. Only applies to 3d plots +=item - --title xxx Set the title of the plot +'--domain --nodataid' would parse the 4 numbers as points in 3 different +curves at x=0. Here, 0 is the x-variable and 9,1,20 are the data values - --legend curveID legend - Set the label for a curve plot. Use this option multiple times - for multiple curves. With --dataid, curveID is the ID. Otherwise, - it's the index of the curve, starting at 0 +=item - --autolegend Use the curve IDs for the legend. Titles given with --legend - override these +'--nodomain --dataid' would parse the 4 numbers as points in 2 different +curves at x=3. Here 0 and 1 are the data IDs and 9 and 20 are the +data values - --xlen xxx When using --stream, sets the size of the x-window to plot. - Omit this or set it to 0 to plot ALL the data. Does not - make sense with 3d plots. Implies --monotonic +=item - --xmin xxx Set the range for the x axis. These are ignored in a - streaming plot - --xmax xxx Set the range for the x axis. These are ignored in a - streaming plot - --ymin xxx Set the range for the y axis. - --ymax xxx Set the range for the y axis. - --y2min xxx Set the range for the y2 axis. Does not apply to 3d plots. - --y2max xxx Set the range for the y2 axis. Does not apply to 3d plots. - --zmin xxx Set the range for the z axis. Only applies to 3d plots or colormaps. - --zmax xxx Set the range for the z axis. Only applies to 3d plots or colormaps. +'--domain --dataid' would parse the 4 numbers as a single point at +x=0. Here 9 is the data ID and 1 is the data value. 20 is an extra +value, so it is ignored. If another value followed 20, we'd get another +point in curve ID 20 - --y2 xxx Plot the data specified by this curve ID on the y2 axis. - Without --dataid, the ID is just an ordered 0-based index. - Does not apply to 3d plots. Can be passed multiple times, or passed a - comma-separated list +=back - --histogram curveID - Set up a this specific curve to plot a histogram. The bin - width is given with the --binwidth option (assumed 1.0 if - omitted). --histogram does NOT touch the drawing style. - It is often desired to plot these with boxes, and this - MUST be explicitly requested with --curvestyleall 'with - boxes'. This works with --domain and/or --stream, but in - those cases the x-value is used ONLY to cull old data - because of --xlen or --monotonic. I.e. the x-values are - NOT drawn in any way. Can be passed multiple times, or passed a comma- - separated list - --binwidth width The width of bins when making histograms. This setting applies to ALL - histograms in the plot. Defaults to 1.0 if not given. - --histstyle style Normally, histograms are generated with the 'smooth freq' - gnuplot style. --histstyle can be used to select - different 'smooth' settings. Allowed are 'unique', - 'cumulative' and 'cnormal'. 'unique' indicates whether a - bin has at least one item in it: instead of counting the - items, it'll always report 0 or 1. 'cumulative' is the - integral of the "normal" histogram. 'cnormal' is like - 'cumulative', but rescaled to end up at 1.0. +=item - --curvestyle curveID style - Additional styles per curve. With --dataid, curveID is - the ID. Otherwise, it's the index of the curve, starting - at 0. Use this option multiple times for multiple curves. - --curvestylall does NOT apply to curves that have a - --curvestyle +--[no]3d - --curvestyleall xxx Additional styles for all curves that have no --curvestyle +Do [not] plot in 3D. This only makes sense with --domain. Each domain here is an +(x,y) tuple - --extracmds xxx Additional commands. These could contain extra global styles - for instance. Can be passed multiple times. +=item - --square Plot data with aspect ratio 1. For 3D plots, this controls the - aspect ratio for all 3 axes +--timefmt [format] - --square_xy For 3D plots, set square aspect ratio for ONLY the x,y axes +Interpret the X data as a time/date, parsed with the given format - --hardcopy xxx If not streaming, output to a file specified here. Format - inferred from filename, unless specified by --terminal - --terminal xxx String passed to 'set terminal'. No attempts are made to - validate this. --hardcopy sets this to some sensible - defaults if --hardcopy is given .png, .pdf, .ps, .eps or - .svg. If any other file type is desired, use both - --hardcopy and --terminal +=item - --maxcurves xxx The maximum allowed number of curves. This is 100 by default, - but can be reset with this option. This exists purely to - prevent perl from allocating all of the system's memory when - reading bogus data +--colormap - --monotonic If --domain is given, checks to make sure that the x- - coordinate in the input data is monotonically increasing. - If a given x-variable is in the past, all data currently - cached for this curve is purged. Without --monotonic, all - data is kept. Does not make sense with 3d plots. - No --monotonic by default. The data is replotted before being - purged +Show a colormapped xy plot. Requires extra data for the color. zmin/zmax can be +used to set the extents of the colors. Automatically increments +extraValuesPerPoint - --extraValuesPerPoint xxx - How many extra values are given for each data point. Normally this - is 0, and does not need to be specified, but sometimes we want - extra data, like for colors or point sizes or error bars, etc. - feedgnuplot options that require this (colormap, circles) - automatically set it. This option is ONLY needed if unknown styles are - used, with --curvestyleall for instance +=item - --dump Instead of printing to gnuplot, print to STDOUT. Very useful for - debugging. It is possible to send the output produced this way to - gnuplot directly. +--stream [period] - --exit Terminate the feedgnuplot process after passing data to - gnuplot. The window will persist but will not be - interactive. Without this option feedgnuplot keeps running - and must be killed by the user. Note that this option works - only with later versions of gnuplot and only with some - gnuplot terminals. +Plot the data as it comes in, in realtime. If period is given, replot every +period seconds. If no period is given, replot at 1Hz. If the period is given as +0 or 'trigger', replot ONLY when the incoming data dictates this. See the +"Real-time streaming data" section of the man page. - --geometry If using X11, specifies the size, position of the plot window +=item - --version Print the version and exit +--[no]lines + +Do [not] draw lines to connect consecutive points + +=item + +--[no]points + +Do [not] draw points + +=item + +--circles + +Plot with circles. This requires a radius be specified for each point. +Automatically increments extraValuesPerPoint + +=item + +--xlabel xxx + +Set x-axis label + +=item + +--ylabel xxx + +Set y-axis label + +=item + +--y2label xxx + +Set y2-axis label. Does not apply to 3d plots + +=item + +--zlabel xxx + +Set y-axis label. Only applies to 3d plots + +=item + +--title xxx + +Set the title of the plot + +=item + +--legend curveID lege + +nd Set the label for a curve plot. Use this option multiple times for multiple +curves. With --dataid, curveID is the ID. Otherwise, it's the index of the +curve, starting at 0 + +=item + +--autolegend + +Use the curve IDs for the legend. Titles given with --legend override these + +=item + +--xlen xxx + +When using --stream, sets the size of the x-window to plot. Omit this or set it +to 0 to plot ALL the data. Does not make sense with 3d plots. Implies +--monotonic + +=item + +--xmin xxx + +Set the range for the x axis. These are ignored in a streaming plot + +=item + +--xmax xxx + +Set the range for the x axis. These are ignored in a streaming plot + +=item + +--ymin xxx + +Set the range for the y axis. + +=item + +--ymax xxx + +Set the range for the y axis. + +=item + +--y2min xxx + +Set the range for the y2 axis. Does not apply to 3d plots. + +=item + +--y2max xxx + +Set the range for the y2 axis. Does not apply to 3d plots. + +=item + +--zmin xxx + +Set the range for the z axis. Only applies to 3d plots or colormaps. + +=item + +--zmax xxx + +Set the range for the z axis. Only applies to 3d plots or colormaps. + +=item + +--y2 xxx + +Plot the data specified by this curve ID on the y2 axis. Without --dataid, the +ID is just an ordered 0-based index. Does not apply to 3d plots. Can be passed +multiple times, or passed a comma-separated list + +=item + +--histogram curveID + + +Set up a this specific curve to plot a histogram. The bin width is given with +the --binwidth option (assumed 1.0 if omitted). --histogram does NOT touch the +drawing style. It is often desired to plot these with boxes, and this MUST be +explicitly requested with --curvestyleall 'with boxes'. This works with --domain +and/or --stream, but in those cases the x-value is used ONLY to cull old data +because of --xlen or --monotonic. I.e. the x-values are NOT drawn in any way. +Can be passed multiple times, or passed a comma- separated list + +=item + +--binwidth width + +The width of bins when making histograms. This setting applies to ALL histograms +in the plot. Defaults to 1.0 if not given. + +=item + +--histstyle style + +Normally, histograms are generated with the 'smooth freq' gnuplot style. +--histstyle can be used to select different 'smooth' settings. Allowed are +'unique', 'cumulative' and 'cnormal'. 'unique' indicates whether a bin has at +least one item in it: instead of counting the items, it'll always report 0 or 1. +'cumulative' is the integral of the "normal" histogram. 'cnormal' is like +'cumulative', but rescaled to end up at 1.0. + +=item + +--curvestyle curveID + +style Additional styles per curve. With --dataid, curveID is the ID. Otherwise, +it's the index of the curve, starting at 0. Use this option multiple times for +multiple curves. --curvestylall does NOT apply to curves that have a +--curvestyle + +=item + +--curvestyleall xxx + +Additional styles for all curves that have no --curvestyle + +=item + +--extracmds xxx + +Additional commands. These could contain extra global styles for instance. Can +be passed multiple times. + +=item + +--square + +Plot data with aspect ratio 1. For 3D plots, this controls the aspect ratio for +all 3 axes + +=item + +--square_xy + +For 3D plots, set square aspect ratio for ONLY the x,y axes + +=item + +--hardcopy xxx + +If not streaming, output to a file specified here. Format inferred from +filename, unless specified by --terminal + +=item + +--terminal xxx + +String passed to 'set terminal'. No attempts are made to validate this. +--hardcopy sets this to some sensible defaults if --hardcopy is given .png, +.pdf, .ps, .eps or .svg. If any other file type is desired, use both --hardcopy +and --terminal + +=item + +--maxcurves xxx + +The maximum allowed number of curves. This is 100 by default, but can be reset +with this option. This exists purely to prevent perl from allocating all of the +system's memory when reading bogus data + +=item + +--monotonic + +If --domain is given, checks to make sure that the x- coordinate in the input +data is monotonically increasing. If a given x-variable is in the past, all data +currently cached for this curve is purged. Without --monotonic, all data is +kept. Does not make sense with 3d plots. No --monotonic by default. The data is +replotted before being purged + +=item + +--extraValuesPerPoint + +xxx How many extra values are given for each data point. Normally this is 0, and +does not need to be specified, but sometimes we want extra data, like for colors +or point sizes or error bars, etc. feedgnuplot options that require this +(colormap, circles) automatically set it. This option is ONLY needed if unknown +styles are used, with --curvestyleall for instance + +=item + +--dump + +Instead of printing to gnuplot, print to STDOUT. Very useful for debugging. It +is possible to send the output produced this way to gnuplot directly. + +=item + +--exit + +Terminate the feedgnuplot process after passing data to gnuplot. The window will +persist but will not be interactive. Without this option feedgnuplot keeps +running and must be killed by the user. Note that this option works only with +later versions of gnuplot and only with some gnuplot terminals. + +=item + +--geometry + +If using X11, specifies the size, position of the plot window + +=item + +--version + +Print the version and exit + +=back =head1 RECIPES From fad78b60b0de51afdb7eccc55e8c467dda52e1b9 Mon Sep 17 00:00:00 2001 From: Dima Kogan Date: Fri, 20 Sep 2013 23:13:55 -0700 Subject: [PATCH 19/19] slightly improved --timefmt documentation --- bin/feedgnuplot | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/bin/feedgnuplot b/bin/feedgnuplot index cf854b1..f7c8dee 100755 --- a/bin/feedgnuplot +++ b/bin/feedgnuplot @@ -1023,8 +1023,8 @@ C<--timefmt>. This option takes a single argument: the format to use to parse the data. The format is documented in 'set timefmt' in gnuplot, although the common flags that L understands are generally supported. The backslash sequences in the format are I supported, so if you want a tab, put in a tab -instead of \t. When this flag is given, some other options act a little bit -differently: +instead of \t. Whitespace in the format I supported. When this flag is +given, some other options act a little bit differently: =over @@ -1038,6 +1038,19 @@ C<--xmin> and C<--xmax> I use the format passed in to C<--timefmt> =back +Using this option changes both the way the input is parsed I the way the +x-axis tics are labelled. Gnuplot tries to be intelligent in this labelling, but +it doesn't always to what the user wants. The labelling can be controlled with +the gnuplot C command, which takes the same type of format string as +C<--timefmt>. Example: + + $ sar 1 -1 | + awk '$1 ~ /..:..:../ && $8 ~/^[0-9\.]*$/ {print $1,$8; fflush()}' | + feedgnuplot --stream --domain + --lines --timefmt '%H:%M:%S' + --extracmds 'set format x "%H:%M:%S"' + +This plots the 'idle' CPU consumption against time. =head2 Real-time streaming data