#!/usr/bin/perl # This script will produce graphs of the values recorded by vanprod in the # dailyobs files for the previous 24 hours. This will run as a daemon. =head1 NAME vangraphd - Perl daemon to produce png format graphs of values recorded by vanprod =head1 SYNOPSIS vangraphd [ -c ] =head1 DESCRIPTION B<-c /path/to/vangraphd.conf> This allows you to override the location of the configuration file. Defaults to /etc/vangraphd.conf if not present. =head1 vangraphd.conf file B Which syslog facility should the daemon use for messages B The location where the dailyobs files from vanprod are located. This will also be the location where the daemon will write it's own files (plot.gp, rain.gp) and the png graphs. B The daemon will copy the png graphs to this directory. B The directory where the daemon will write it's pid. B The path to gnuplot. Version 4.0 or greater is required. B The amount of time in seconds between graph updates. =cut use strict; my $directory = ""; # directory where vanprod files are located my $webdir = ""; # directory for webserver my $gnuplot = ""; # path to gnuplot command my $facility = ""; # syslog facility to use my $piddir = ""; my %configs = (); my $var = ""; my $value = ""; use Getopt::Std; my %opts = (); getopts('c:', \%opts); use File::Copy; use POSIX qw(strftime); use Sys::Syslog qw(:DEFAULT setlogsock); use Sys::Hostname; my $date = strftime "%m.%d.%Y", localtime time; my $yesterday = strftime "%m.%d.%Y", localtime time - 86400; my $chour = (localtime)[2]; my $cmin = (localtime)[1]; my $year = strftime "%Y", localtime time; my $year1 = strftime "%Y", localtime time - 86400; my $month = strftime "%m", localtime time; my $month1 = strftime "%m", localtime time - 86400; my $day = strftime "%d", localtime time; my $day1 = strftime "%d", localtime time - 86400; my $line = ""; my @obs = (); my $time_to_die = 0; my $output = ""; my $host = hostname; $ENV{STY} = "$$.$host.1"; # Make gnuplot and libvga behave. my $interval = 120; my $rain = .1; initialize(); # Become a daemon my $pid = fork(); if($pid){ exit; }; if(!defined $pid){ die "Couldn't fork -- $!\n";}; # Disassociate with our controlling terminal and stop being part of our # previous process group. POSIX::setsid() or die "Can't start new daemon session: $!\n"; # If we made it this far, we are now a daemon process # We should do all our reporting via syslog from now on. setlogsock("unix"); # talk to our local running syslogd # Connect up with syslogd, log vangraphd as our process name, along with our pid openlog("vangraphd", "pid", "$facility"); # Record our pid in a file for ease in signaling. open(PIDFILE, ">$piddir/vangraphd.pid") or syslog("err", "Exiting can't write pid file $piddir/vangraphd.pid %m") and die; print PIDFILE $$; close(PIDFILE); sub initialize{ if($opts{c}){ open(CONF, "<$opts{c}") or die "Unable to open $opts{c} $!\n"; }else{ open(CONF, "; $line =~ s/#.*//; # ignore comments $line =~ s/^\s+//; # no leading whitespace $line =~ s/\s+$//; # no trailing whitespace if($line =~ m/^$/){ next; }; # ignore blank lines and go to next line if nothing left chomp($line); # extract the variable -> value pairs. Whitespace before and/or after the = # or no whitespace on either side of the = is allowed. ($var, $value) = split(/\s*=\s*/, $line, 2); $configs{$var} = $value; }; close(CONF); $gnuplot = $configs{gnuplot}; # path to gnuplot command $facility = $configs{facility}; $directory = $configs{directory}; $piddir = $configs{piddir}; $webdir = $configs{webdir}; $interval = $configs{interval}; $SIG{INT} = \&sigterm; $SIG{TERM} = \&sigterm; $SIG{HUP} = \&initialize; }; =pod B B or B will cause the daemon to exit gracefully. B Will cause the daemon to re-read the values from vangraphd.conf. =cut syslog("info", "vangraphd started."); chdir($directory); open(PLOT, ">plot.gp") or syslog("notice", "Cannot create $directory/rain.gp %m"); print PLOT "set xdata time set timefmt \"\%Y \%m \%d \%H \%M\" set format x \"\%H\" set xtics \"$year1 $month1 $day1 00:00\", 3600 set style data line set grid set terminal png small crop size 450,150 set key below set output \"temp_graph.png\" plot \"temp.dat\" using 1:9 title \"heat index\" 4 set output \"temp_graph.png\" replot \"temp.dat\" using 1:8 title \"chill\" 2 set output \"temp_graph.png\" replot \"temp.dat\" using 1:7 title \"dewpoint\" 3 set output \"temp_graph.png\" replot \"temp.dat\" using 1:6 title \"temp\" 1 set title \"barometer\" unset key set output \"bar_graph.png\" plot \"bar.dat\" using 1:6 set title \"humidity\" set output \"hum_graph.png\" plot \"hum.dat\" using 1:6 unset title set key below set output \"wind_graph.png\" plot \"wind.dat\" using 1:7 title \"wind gust\" set output \"wind_graph.png\" replot \"wind.dat\" using 1:6 title \"average speed\" set title \"wind direction\" unset key set yrange [-1:360] set ytics (\"N\" 0, \"E\" 90, \"S\" 180, \"W\" 270, \"N\" 360) set output \"dir_graph.png\" set style data dots plot \"dir.dat\" using 1:6\n"; close(PLOT); until($time_to_die == 1){ # Main loop of the daemon parse_files(); $output = `$gnuplot plot.gp 2>&1`; if($output){ syslog("notice", "Gnuplot error: $output\n"); }; $output = `$gnuplot rain.gp 2>&1`; if($output){ syslog("notice", "Gnuplot error: $output\n"); }; copy("bar_graph.png", $webdir); copy("hum_graph.png", $webdir); copy("temp_graph.png", $webdir); copy("wind_graph.png", $webdir); copy("dir_graph.png", $webdir); copy("rain_graph.png", $webdir); sleep($interval); }; syslog("info", "Caught sigterm."); sub parse_files(){ $date = strftime "%m.%d.%Y", localtime time; $yesterday = strftime "%m.%d.%Y", localtime time - 86400; $chour = (localtime)[2]; $cmin = (localtime)[1]; $year = strftime "%Y", localtime time; $year1 = strftime "%Y", localtime time - 86400; $month = strftime "%m", localtime time; $month1 = strftime "%m", localtime time - 86400; $day = strftime "%d", localtime time; $day1 = strftime "%d", localtime time - 86400; eval{ open(BAR, ">$directory/bar.dat") or die "Cannot write bar.dat file $!\n"; open(HUM, ">$directory/hum.dat") or die "Cannot write hum.dat file $!\n"; open(TEMP, ">$directory/temp.dat") or die "Cannot write temp.dat file $!\n"; open(WIND, ">$directory/wind.dat") or die "Cannot write wind.dat file $!\n"; open(DIR, ">$directory/dir.dat") or die "Cannot write dir.dat file $!\n"; open(RAIN, ">$directory/rain.dat") or die "Cannot write rain.dat file $!\n"; open(OBS, "<$directory/dailyobs.$yesterday") or die "Cannot open daily observation file $directory/dailyobs.$yesterday $!\n"; $rain = .1; until(eof(OBS)){ $line = ; chomp($line); @obs = split /,/, $line; if($obs[0] == $chour){ if($obs[1] >= $cmin){ if($obs[9] eq "--"){ $obs[9] = $obs[3]; }; if($obs[10] eq "--"){ $obs[10] = $obs[3]; }; print BAR "$year1 $month1 $day1 $obs[0] $obs[1] $obs[2]\n"; print TEMP "$year1 $month1 $day1 $obs[0] $obs[1] $obs[3] $obs[8] $obs[9] $obs[10]\n"; print WIND "$year1 $month1 $day1 $obs[0] $obs[1] $obs[4] $obs[5]\n"; print DIR "$year1 $month1 $day1 $obs[0] $obs[1] $obs[6]\n"; print HUM "$year1 $month1 $day1 $obs[0] $obs[1] $obs[7]\n"; print RAIN "$year1 $month1 $day1 $obs[0] $obs[1] $obs[14] $obs[15] $obs[16]\n"; if($obs[16] > $rain){ $rain = $obs[16] + .1; }; }; }; if($obs[0] > $chour){ if($obs[9] eq "--"){ $obs[9] = $obs[3]; }; if($obs[10] eq "--"){ $obs[10] = $obs[3]; }; print BAR "$year1 $month1 $day1 $obs[0] $obs[1] $obs[2]\n"; print TEMP "$year1 $month1 $day1 $obs[0] $obs[1] $obs[3] $obs[8] $obs[9] $obs[10]\n"; print WIND "$year1 $month1 $day1 $obs[0] $obs[1] $obs[4] $obs[5]\n"; print DIR "$year1 $month1 $day1 $obs[0] $obs[1] $obs[6]\n"; print HUM "$year1 $month1 $day1 $obs[0] $obs[1] $obs[7]\n"; print RAIN "$year1 $month1 $day1 $obs[0] $obs[1] $obs[14] $obs[15] $obs[16]\n"; if($obs[16] > $rain){ $rain = $obs[16] + .1; }; }; }; close(OBS); open(OBS, "<$directory/dailyobs.$date") or die "Cannot open daily observation file $directory/dailyobs.$date $!\n"; until(eof(OBS)){ $line = ; chomp($line); @obs = split /,/, $line; if($obs[9] eq "--"){ $obs[9] = $obs[3]; }; if($obs[10] eq "--"){ $obs[10] = $obs[3]; }; print BAR "$year $month $day $obs[0] $obs[1] $obs[2]\n"; print TEMP "$year $month $day $obs[0] $obs[1] $obs[3] $obs[8] $obs[9] $obs[10]\n"; print WIND "$year $month $day $obs[0] $obs[1] $obs[4] $obs[5]\n"; print DIR "$year $month $day $obs[0] $obs[1] $obs[6]\n"; print HUM "$year $month $day $obs[0] $obs[1] $obs[7]\n"; print RAIN "$year $month $day $obs[0] $obs[1] $obs[14] $obs[15] $obs[16]\n"; if($obs[16] > $rain){ $rain = $obs[16] + .1; }; }; }; # end eval if($@ =~ /^Cannot/){ syslog("notice", "File IO error: $@\n"); }; close(OBS); close(BAR); close(TEMP); close(WIND); close(DIR); close(HUM); close(RAIN); open(PLOT, ">rain.gp") or syslog("notice", "Cannot create $directory/rain.gp %m"); print PLOT "set xdata time set timefmt \"\%Y \%m \%d \%H \%M\" set format x \"\%H\" set xtics \"$year1 $month1 $day1 00:00\", 3600 set style data line set grid set terminal png small crop size 450,150 set key below set yrange \[0:$rain\] unset title set key below set output \"rain_graph.png\" plot \"rain.dat\" using 1:6 title \"rain midnight\" set output \"rain_graph.png\" replot \"rain.dat\" using 1:7 title \"past hour\" set output \"rain_graph.png\" replot \"rain.dat\" using 1:8 title \"24 hours\"\n"; close(PLOT); }; sub sigterm{ # Set a flag to gracefully exit $time_to_die = 1; }; =head1 README This daemon is written to generate png formatted graphs derived from values recorded in the dailyobs files produced by vanprod. Example of installation: cp vangraphd-$VERSION /usr/local/sbin/vangraphd cp vangraphd.conf-$VERSION /etc/vangraphd.conf pod2man /usr/local/sbin/vangraphd /usr/local/man/man1/vangraphd.1 Create a script to start the daemon for you at boot. Modify the vangraphd.conf file to suit your preferences and needs. =head1 PREREQUISITES The daemon requires the following at a minimum in order to run: vanprod 2.1 or greater Getopt::Std POSIX File::Copy Sys::Syslog Sys::Hostname Gnuplot 4.0 or greater =pod OSNAMES any Unix / Linux variant or Mac =pod SCRIPT CATEGORIES Networking Web =head1 SEE ALSO perl(1) perlfunc(1) vanprod(1) gnuplot(1) POSIX(3) Getopt::Std(3) Sys::Syslog(3) Sys::Hostname(3) syslog.conf(5) =head1 COPYRIGHT AND LICENSE Copyright 2005 by Stan Sander. stsander@sblan.net You are welcome to send me any requests for new features or enhancements. Vangraphd is free software. You may redistribute it and/or modify it under the same terms as Perl itself. =cut