PMbwmon bandwidth monitor: can specify which interfaces to watch, also supporting...
[hband-tools.git] / tabdata / td-gnuplot
blobc1355388ac72e15187bca4b91fe8ba4a5c71252c
1 #!/usr/bin/env perl
3 =pod
5 =head1 NAME
7 td-gnuplot - Graph tabular data using gnuplot(1)
9 =head1 USAGE
11 td-gnuplot [I<OPTIONS>]
13 =head1 DESCRIPTION
15 Invoke gnuplot(1) to graph the data represented in Tabular data format on STDIN.
16 The first column is the X axis, the rest of the columns are data lines.
18 Default is to output an ascii-art chart to the terminal ("dumb" output in gnuplot).
20 td-gnuplot guesses the data format from the column names.
21 If the 0th column matches to "date" or "time" (case insensitively) then the X axis will be a time axis.
22 If the 0th column matches to "time", then unix epoch timetamp is assumed.
23 Otherwise specify what date/time format is used by eg. B<--timefmt=%Y-%m-%d> option.
25 Plot data read from STDIN is buffered in a temp file
26 (provided by C<< File::Temp->new(TMPDIR=>1) >> and immediately unlinked so no waste product left around),
27 because gnuplot(1) need to seek in it when plotting more than 1 data series.
29 =head1 OPTIONS
31 =over 4
33 =item -i
35 Output an image (PNG) to the STDOUT,
36 instead of drawing to the terminal.
38 =item -d
40 Let gnuplot(1) decide the output medium,
41 instead of drawing to the terminal.
43 =item --I<SETTING>
45 =item --I<SETTING>=I<VALUE>
47 Set any gnuplot setting, optionally set its value to I<VALUE>.
48 I<SETTING> is a setting name used in C<set ...> gnuplot commands, except spaces replaced with dasshes.
49 I<VALUE> is always passed to gnuplot enclosed in double quotes.
50 Examples:
52 --format-x="%Y %b"
53 --xtics-rotate-by=-90
54 --style-data-lines
56 Gnuplot equivalent command:
58 set format x "%Y %b"
59 set xtics rotate by "-90"
60 set style data lines
62 =item -e I<COMMAND>
64 Pass arbitrary gnuplot commands to gnuplot.
65 This option may be repeated.
66 This is passed to gnuplot(1) in command line (B<-e> option)
67 after td-grnuplot(1)'s own sequence of gnuplot setup commands
68 and after the B<< --I<SETTING> >> settings are applied,
69 so you can override them.
71 =back
73 =cut
75 use constant { OUTPUT_TERM=>1, OUTPUT_IMAGE=>2, OUTPUT_DEFAULT=>3, };
76 use Data::Dumper;
77 use Term::Size;
78 use Fcntl qw/F_GETFL F_SETFL F_GETFD F_SETFD FD_CLOEXEC/;
79 use File::Temp;
80 no if ($] >= 5.018), 'warnings' => 'experimental::smartmatch';
83 $OptOutput = OUTPUT_TERM;
84 @gnuplot_commands = ();
86 %OptionDefs = (
87 'i' => sub { $OptOutput = OUTPUT_IMAGE; },
88 'd' => sub { $OptOutput = OUTPUT_DEFAULT; },
89 'e=s@' => \@gnuplot_commands,
92 do '/usr/lib/tool/perl5/tabdata/common.pl' or die "$@";
94 pipe $conf_script_rfh, $conf_script_wfh or die "$0: pipe: $!\n";
95 pipe $plot_script_rfh, $plot_script_wfh or die "$0: pipe: $!\n";
96 fcntl($conf_script_rfh, F_SETFD, fcntl($conf_script_rfh, F_GETFD, 0) & ~FD_CLOEXEC);
97 fcntl($plot_script_rfh, F_SETFD, fcntl($plot_script_rfh, F_GETFD, 0) & ~FD_CLOEXEC);
98 $conf_script_rfileno = fileno $conf_script_rfh;
99 $plot_script_rfileno = fileno $plot_script_rfh;
101 process_header(sys_read_line());
103 $data_tmp_fh = File::Temp->new( SUFFIX => '.dat', TMPDIR => 1, );
104 # shovel all of STDIN to a temp file to let gnuplot seek in it
105 print {$data_tmp_fh} <STDIN>;
106 $data_tmp_path = '/dev/fd/'.fileno($data_tmp_fh);
107 fcntl($data_tmp_fh, F_SETFD, fcntl($data_tmp_fh, F_GETFD, 0) & ~FD_CLOEXEC);
108 unlink $data_tmp_fh->filename;
111 select $conf_script_wfh;
113 print <<"EOT"
114 set xlabel "$Header[0]"
115 set autoscale
116 #set key on outside
117 set key on bmargin left horizontal
118 set xtics out nomirror
119 set x2tics out nomirror
120 set ytics out nomirror
121 set y2tics out nomirror
122 set yrange [0:]
123 set format y "%.2f"
124 set format y2 "%.2f"
125 set style data linespoints
126 set datafile separator "\t"
130 if($Header[0] =~ /date|time/i)
132 print "set xdata time;";
133 #print "set x2data time;";
134 print "set format x \"%Y-%m-%d %H:%M\";";
135 #print "set format x2 \"%Y-%m-%d %H:%M\";";
136 # set xtics rotate by -90
137 # set x2tics rotate by 90
138 if($Header[0] =~ /time/i)
140 # assuming unix epoch timestamp
141 print "set timefmt \"%s\";";
145 if($OptOutput eq OUTPUT_TERM)
147 my ($term_cols, $term_rows) = Term::Size::chars *STDOUT{IO};
148 if(not $term_cols) { ($term_cols, $term_rows) = Term::Size::chars *STDERR{IO}; }
150 print <<"EOT"
151 unset grid
152 set terminal dumb size $term_cols,$term_rows ansi256
156 elsif($OptOutput eq OUTPUT_IMAGE)
158 print <<"EOT"
159 set grid
160 set terminal png
161 set output "/dev/stdout"
166 while(my $arg = shift @ARGV)
168 if($arg =~ /^--([^=]+)(?:=(.*)|)$/)
170 my ($setting, $value) = ($1, $2);
171 $setting =~ s/-/ /g;
172 if(defined $value)
174 print "set $setting \"$value\";";
176 else
178 print "set $setting;";
183 close $conf_script_wfh;
184 select $plot_script_wfh;
186 print "plot ";
188 $sep = '';
189 $input = $data_tmp_path;
190 for my $colnum (2..$#Header+1)
192 my $col = $Header[$colnum-1];
193 print "$sep'$input' using 1:$colnum title \"$col\" axes x1y1";
194 $sep = ', ';
195 $input = '';
197 print "\n";
199 close $plot_script_wfh;
202 exec 'gnuplot', '--persist', map {('-e', $_)} "load \"/dev/fd/$conf_script_rfileno\"", @gnuplot_commands, "load \"/dev/fd/$plot_script_rfileno\"", "pause mouse close";
203 ($errno, $errstr) = (int $!, $!);
204 warn "gnuplot: $errstr\n";
205 exit 125+$errno;