#!/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 <path/to/vangraphd.conf> ]

=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<facility = local0>  Which syslog facility should the daemon use for messages

B<directory = /some/path/to/weather>  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<webdir = /path/to/my/web/images>  The daemon will copy the png graphs to this directory.

B<piddir = /var/run>  The directory where the daemon will write it's pid.

B<gnuplot = /usr/bin/gnuplot>  The path to gnuplot.  Version 4.0 or greater is required.

B<interval = 120>  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, "</etc/vangraphd.conf") or die "Unable to open /etc/vangraphd.conf $!\n";
};
until(eof(CONF)){
  $line = <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<Signals>

B<INT> or B<TERM> will cause the daemon to exit gracefully.

B<HUP> 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 = <OBS>;
  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 = <OBS>;
  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

