#!/usr/bin/perl
#
# dsreport - report about DS values
# 
# steve rader
# <rader@hep.wisc.edu>
#
# $Id: dsreport.in,v 1.26 2003/06/21 18:07:05 rader Exp $
#

use lib '/usr/lib/perl5';
use RRDs;

%scale_symbols = qw( -18 a -15 f -12 p -9 n -6 u -3 m 
  3 k 6 M 9 G 12 T 15 P 18 E );

#----------------------------------------

while ( $ARGV[0] =~ /^-/ ) {
  switch: {
    if ( $ARGV[0] eq '--autoscale' || $ARGV[0] =~ /^-a/ ) {
      $scale = 1;
      last switch;
    }
    if ( $ARGV[0] eq '--conversion' || $ARGV[0] =~ /^-c/ ) {
      shift @ARGV;
      $conversion = $ARGV[0];
      if ( $conversion !~ /^\d+$|^\d+\.\d+$|^\.\d+$/ ) {
        print "dsreport: bad conversion factor \"$conversion\"\n";
        exit 1;
      }
      last switch;
    }
    if ( $ARGV[0] eq '--label' || $ARGV[0] =~ /^-lab/ ) {
      shift @ARGV;
      $label = $ARGV[0];
      last switch;
    }
    if ( $ARGV[0] eq '--start' || $ARGV[0] =~ /^-s/ ) {
      shift @ARGV;
      $start = $ARGV[0]; 
      last switch;
    }
    if ( $ARGV[0] eq '--end' || $ARGV[0] =~ /^-e/ ) {
      shift @ARGV;
      $end = $ARGV[0]; 
      last switch;
    }
    if ( $ARGV[0] eq '--verbose' || $ARGV[0] =~ /^-v/ ) {
      $verbose = 1;
      last switch;
    }
    if ( $ARGV[0] eq '--debug' || $ARGV[0] =~ /^-d/ ) {
      $debug = 1;
      last switch;
    }
    if ( $ARGV[0] eq '--now' || $ARGV[0] =~ /^-n/ ) {
      $now_kludge = 1;
      last switch;
    }
    if ( $ARGV[0] eq '--last' || $ARGV[0] =~ /^-las/ ) {
      $last = 1;
      last switch;
    }
    print "dsreport: unknown option \"$ARGV[0]\"\n";
    exit 1;
  }
  shift @ARGV;
}

#----------------------------------------

if ( $#ARGV != 0 ) {
  print <<_EOT_;
usage: dsreport [args] some.rrd
  -now        report current values (also --now)
  -last       report values after last update  (also --last)
  -s start    report starting at time "start" (also --start)
  -e end      report ending at time "end" (also --end)
  -a          autoscale DS values (also --autoscale)
  -c num      convert DS values by "num" (also --conversion)
  -l label    label DS values with "label" (also --label)
  -v          print the start and end times (also --verbose)

  The -s and -e options support the traditional "seconds 
  since the Unix epoch" and the AT-STYLE time specification 
  (see man rrdfetch)
_EOT_
  exit 1;
}

if ( ! -f "$ARGV[0]" ) {
  print "dsreport: can't find \"$ARGV[0]\"\n";
  exit 1;
}

#----------------------------------------

MODE_PARSE:  {
  # special case "now"...
  if ( $now_kludge ) {
    $start = 'now';
    last MODE_PARSE;
  }
  # start and no end so spec end...
  if ( $start && ! $end ) {
    if ( $start =~ /^\d+$/ ) {
      $end = $start+1;
    } else {
      $end = "${start}+1sec";
    }
    last MODE_PARSE;
  }
  # end and no start so spec start...
  if ( $end && ! $start ) {
    if ( $end =~ /^\d+$/ ) {
      $start = $end-1;
    } else {
      $start = "${end}-1sec";
    }
    last MODE_PARSE;
  }
  # last update...
  if ( $last ) {
    ($last) = RRDs::last @ARGV[0];
    if ( $error = RRDs::error ) {
      print "dsreport: rrdtool last failed: \"$error\"\n";
      exit 1;
    }
    if ( $debug ) { 
      print "last update was at ", scalar localtime($last), "($last)\n";
    }
    $start = $last;
    $end = $start+1;
    last MODE_PARSE
  }
  # no now, no start, no end, no nothing... report now...
  if ( ! $start && ! $end ) { 
    $start = 'now';
    $now_kludge = 1;
    last MODE_PARSE;
  }
}

#----------------------------------------

push @fetch, $ARGV[0];
if ( $start ) {
  push @fetch, '-s', $start; 
} 
if ( $end ) {
  push @fetch, '-e', $end; 
} 
push @fetch, 'AVERAGE';

($start,$step,$names,$data) = RRDs::fetch @fetch;
if ( $error = RRDs::error ) {
  print "dsreport: rrdtool fetch failed: \"$error\"\n";
  exit 1;
}

#----------------------------------------

if ( $now_kludge ) {  
  $end = time();
  $last = RRDs::last $ARGV[0];
  if ( $error = RRDs::error ) {
    print "dsreport: rrdtool last failed: \"$error\"\n";
    exit 1;
  }
  if ( $last < $start ) {
    $start = $start - $step;
    $end = time() - $step;
  } 
  if ( (time() % $step) == 0 ) {
    $start = $start - $step;
  }
  undef @fetch;
  push @fetch, $ARGV[0], '-s', $start, '-e', $end, 'AVERAGE';
  ($start,$step,$names,$data) = RRDs::fetch @fetch;
  if ( $error = RRDs::error ) {
    print "dsreport: rrdtool fetch failed: \"$error\"\n";
    exit 1;
  }
}
  

#----------------------------------------

if ( $debug ) {
  $now = time();
  print '-' x 66, "\n";
  print 'rrdtool fetch ', join(' ',@fetch), "\n";
  $d_start = $start;
  print "  Now:         ", scalar localtime($now), " ($now)\n";
  print "  Last update: ", scalar localtime($last), " ($last)\n";
  print "  Start:       ", scalar localtime($d_start), " ($d_start)\n";
  print "  End:         ", scalar localtime($end), " ($end)\n";
  print "  Step size:   $step seconds\n";
  print "  Data points: ", $#$data + 1, "\n";
  print "  Data:                                  ";
  foreach $name (@$names) { printf "%12s ", $name; }
  print "\n";
  foreach $line (@$data) {
    print "    ", scalar localtime($d_start), " ($d_start) ";
    $d_start += $step;
    foreach $val (@$line) {
      if ( ! defined $val ) {
        printf "%12s ", 'NaN';
      } else {
        printf "%12.2f ", $val;
      }
    }
    print "\n";
  }
  print '-' x 66, "\n";
}

#----------------------------------------

for $t ( 0 .. $#{$data} - 1 ) {
  if ( $verbose ) {
    print scalar localtime($start), ' through ', 
      scalar localtime($start+$step), ' average';
  } else {
    print scalar localtime($start);
  }
  $start += $step;
  for $i (0 .. $#$names) {
    if ( ! defined $data->[$t][$i] ) { 
      printf "  %12s %s", 'NaN', $$names[$i];
      next;
    }
    if ( $conversion ) {
      $data->[$t][$i] = $data->[$t][$i] * $conversion;
    }
    if ( $scale ) {
      ($val, $units) = autoscale($data->[$t][$i]);
    } else {
      $val = $data->[$t][$i];
    }
    printf "  %12.2f$units$label %s", $val, $$names[$i];
  }
  print "\n";
}

exit 0;

#==============================================================================

sub autoscale {
  local($value) = @_;
  local($floor, $mag, $index, $symbol, $new_value);

  if ( $value =~ /^\s*[0]+\s*$/ || 
       $value =~ /^\s*[0]+.[0]+\s*$/ || 
       $value =~ /^\s*NaN\s*$/ ) {
    return $value, ' ';
  }

  $floor = &floor($value);
  $mag = int($floor/3);
  $index = $mag * 3;
  $symbol = $scale_symbols{$index};
  $new_value = $value / (10 ** $index);
  return $new_value, " $symbol";
}

#------------------------------------------------------------------

sub floor {
  local($value) = @_;
  local($i) = 0;

  if ( $value > 1.0 ) {
    # scale downward...
    while ( $value > 10.0 ) {
      $i++;
      $value /= 10.0;
    }
  } else {
    while ( $value < 10.0 ) {
      $i--;
      $value *= 10.0;
    }
  }
  return $i;
}

