#! /usr/bin/perl -w


##########################################################
#
#  Display data from Standard Counter Log files
#
# (C) 2011 Doug Coulter, GPL v2 license -- share and enjoy,
# but if you improve it, be sure and share it back, to
# clab@swva.net, or www.coultersmithing.com/forums
#
#
# See use section below for any modules you'll need to install first
##########################################################
# It's probably obvious I'm coding this like I was a C programmer
# (because I am), and squishing fancy expressions into single liners
# would just make the inevitable changes this is going to need harder
# to figure out later -- for a C programmer (me).
#
# Anything I thought was dodgy is marked with an @@@ for searching
#
# If you're a perl purist or stylist, sorry 'bout that.  
# TMTOWTDI and, TANSTAAFL for good measure.
# lots of steal-me code here for beginning perl people
##########################################################
#
# Note, I'm using a goodly number of globals here to avoid tons of typing
# parameters into subroutines.  Most assume you'll call them in the right
# order and they're valid.  This only works for well disciplined programmers
# and not even for them if things get too big and confusing. It's not stylin,
# don't do this at home.  It does make things faster to run and to write if
# you keep it all in your head well enough. Flame me if you want.  This works.

use strict; # make perl tell me about typos and how dumb I am - built in

# Bring in a pile of library modules, this code is already written and tested by 
# someone else (mostly).  Most of these aren't standard, but have to be gotten from
# CPAN and installed via the standard way for your platform
# if you're running ubuntu the software center can search for these.
# run this from a terminal and use it to get anything mentioned in the printed
# errors -- you'll need the gnuplot program of course too.

use Gtk2 -init; # sets up GUI basics
use Glib qw(TRUE FALSE);
use Gtk2::GladeXML; # lets GUI come from a glade file
use Graphics::GnuplotIF qw(GnuplotIF); # gnuplot for plots

##################################################################
######################  Global Variables #########################
##################################################################

# gui pointers
my $gui; # main window
my $filechooser; # button to look for a file
my $status; # status lable used to tell user stuff
my $spinstart; # where to begin plot
my $spinstop; # where to end the plot
my $logplot; # do we use log plot?
my $points; # radio buttons for plot styles
my $lines;
my $both;
my $doplot; # "go" button
my $GPM_1;	# "what to plot"
my $GPM_10;
my $NPM_1;
my $NPM_10;

# data variables

my @xaxis;	# for gnu plots
my @gpm1;	# y axis data for geigers 1 sec intervals
my @gpm10;	# y axis data for geigers, 10 sec averages
my @npm1;	# y axis data for neutrons, 1 sec intervals
my @npm10;	# y axis data for neutrons, 10 sec averages
my $curindex;	# insertion index for next set of data

my $filepath; # file path for log file from dialog 
my $gplot;	# gnuplot instance pointer

##################################################################
############################# Subroutines  #######################
##################################################################
sub resetdata # clear data arrays etc so a new file starts clean
{
 $curindex = 0;
 @xaxis = @gpm1 = @gpm10 = @npm1 = @npm10 = (); # clear plot arrays
 $NPM_1->set_active(FALSE);
 $NPM_10->set_active(FALSE);
 $doplot->set_sensitive(FALSE); # this is how you enable/disable things derived from GtkWidget
}


########################
### Message handlers ###
########################
sub on_doplot_clicked
{ # grab all the settings, create a gnuplot, set it up, and do it
 my $bg1 = $GPM_1->get_active() ? TRUE:FALSE;
 my $bg10 = $GPM_10->get_active() ? TRUE:FALSE;
 my $bn1 = $NPM_1->get_active() ? TRUE:FALSE;
 my $bn10 = $NPM_10->get_active() ? TRUE:FALSE;
 my $lp = $logplot->get_active() ? TRUE:FALSE;
 my $startindex = $spinstart->get_value() - 1; # fudge to array indexes
 my $stopindex = $spinstop->get_value() - 1;
 
# my $stxt;
 
 if ($startindex >= $stopindex)
 {
#  $stxt = "Start:".$startindex+1 . "past stop:" . $stopindex+1 ."?? Can't do that!";
  $status->set_text(sprintf("Start:%d, past stop:%d, Can't do that!",$startindex+1,$stopindex+1)); # show in status bar
  return; # really now, skip the rest till you put in a reasonable request
 }
 else
 {
# $stxt ="Startindex:" . $startindex+1 . " stopindex:". $stopindex+1;
 $status->set_text(sprintf("Start:%d\tStop:%d",$startindex+1,$stopindex+1));
 }
 

 
 my @xslice = @xaxis[$startindex .. $stopindex]; # kewl to the max, slice variably
 my @g1slice = @gpm1[$startindex .. $stopindex];
 my @g10slice = @gpm10[$startindex .. $stopindex];
 my @n1slice = @npm1[$startindex .. $stopindex];
 my @n10slice = @npm10[$startindex .. $stopindex];
 
# print ("start:$startindex, stop:$stopindex\n");
 
 my @titles; # we stuff this with the appropriate text(s) depending on UI inputs, cool!
 my @plotArefs; # an array of array references to plot data, one X and multiple Y's as requested
 
 my $style;  # we're really stylin here
 
 if ($points->get_active){$style = "points";}
 if ($lines->get_active){$style = "lines";}
 if ($both->get_active){$style = "linespoints";}
 
# print "style:$style\n";
# print "got to doplot\n";
 
 unless ($gplot)
 { # make a new one if we didn't have one already
  $gplot =Graphics::GnuplotIF->new(title => "Standard Counter results", raise => FALSE,
          xlabel => 'Seconds', ylabel =>'Counts per minute');
 } # end ensure there's a gnuplot instance
 
 $gplot->gnuplot_cmd ("set grid"); # always want this
 
 # set styles based on UI
 
 $gplot->gnuplot_set_style($style);
 
 if ($lp) {$gplot->gnuplot_cmd ("set log y");} # make it log
 else { $gplot->gnuplot_cmd ("unset log y");} # make it linear

 push (@plotArefs,\@xslice); # sliced - fwiw, that takes care of the rest too

 if ($bg1) # check UI and do the right thing(s)
 {
  push(@titles,"GPM-1");
  push(@plotArefs,\@g1slice);
  }
 if ($bg10)
 {
  push(@titles,"GPM-10");
  push(@plotArefs,\@g10slice);
 }
 if ($bn1)
 {
  push(@titles,"NPM-1");
  push(@plotArefs,\@n1slice);
 }
 if ($bn10)
 {
  push(@titles,"NPM-10");
  push(@plotArefs,\@n10slice);
 }

 $gplot->gnuplot_set_plot_titles(@titles); # set titles based on what we'll send
 $gplot->gnuplot_plot_xy(@plotArefs); # and send and plot the data

}
####################

# to hook this up, in glade use gtk_main_quit as the handler in the mainwin/GtkWidget/destory-event
# also needed Gtkobject destroy event and check "after"
sub gtk_main_quit  { Gtk2->main_quit; } # fall out of main loop and exit

####################
# we get here when a file has been chosen to open
sub on_filechooserbutton_file_set
{ # slurp up the file, parse it into plot arrays
 my @cfields; # place to hold a split line 
 my $h; # for breaking up time
 my $m;
 my $s;
 my $cn1; # rest of the numbers on a line so we can reassemble them
 my $cn10;
 my $cnt;
 my $cg1;
 my $cg10;
# my $cgt; 
 my $csec;
 my $dtime;
 my $isneuts = 0;
# there's probably a more compact way to do the above

 resetdata(); # clear out data arrays from any previous read
 $filepath = $filechooser->get_filename();
# print "file chosen:$filepath\n";
 open (LOG, "< $filepath") or die "couldn't open datafile:$filepath: $!"; # debug
 $curindex = 0; # start at array beginnings
 
 while (<LOG>) # for every line in log file
 {
  if ($_ =~ m/#/)
  {
   #print ("comment:$_");
   $status->set_text("in-file comment:$_"); # show in status bar
   next;
  } # end if comment line
 # now, assume valid line and do our thing on it
 @cfields = split(/\s+/); # break on whitespace

 # figure out total seconds
 ($h,$m,$s) = split(/:/,$cfields[0]); # break up time and calculate total seconds
 $csec = $s + 60 * $m + 3600 * $h;
 $dtime = $cfields[0]; # save for later display

 @cfields = @cfields[2,4,6,8,10,12]; # strip off all labels via array slice
 push (@cfields," "); # @@@ loop below needs one past end
 
 $cg1 = $cfields[0]; # geiger counts in second
 $cg10 = $cfields[1]; # geiger counts in last 10 seconds
# $cgt = $cfields[2]; # total geiger counts

 if (defined $cfields[3]) # eg if we're putting out neutron counts too
 {
  $isneuts = 1;
  $cn1 = $cfields[3];
  $cn10 = $cfields[4];
#  $cnt = $cfields[5];  # would anyone want this plotted?
 } else
 {
  $cn1 = $cn10 = $cnt = 0; # make them defined, but zero
 }

 $gpm1[$curindex] = $cg1;  # for gnuplot
 $gpm10[$curindex] = $cg10; # plot data
 
 # neutrons here
 $npm1[$curindex] = $cn1; # neutron plot data
 $npm10[$curindex] = $cn10;

 $xaxis[$curindex++] = $csec; # x axis for all plots
  
 } # end for every line in file
 print ("TotalSec:$csec\n");  # use to set spin control ranges
 $spinstart->set_range(1,$csec);
 $spinstop->set_range(1,$csec); # we'll have to detect negative net range later
 $spinstop->set_value($csec);
 if ($isneuts)
 {
  $NPM_1->set_active(TRUE);
  $NPM_10->set_active(TRUE);
 }
 
$doplot->set_sensitive(TRUE); # this is how you enable/disable things derived from GtkWidget
}


##################################################################
#################################  Main Code  ####################
##################################################################
my $guidata; # test to see if we can pack the xml internally and get it back

#print ("\nprogram running\n"); # debug
undef $/; # remove normal record terminator
$guidata = <main::DATA>;
#print $guidata; # debug
$/ = "\n"; # put record terminator back
$gui = Gtk2::GladeXML->new_from_buffer($guidata); # and here it is, cool.

# get pointers to ui elements for later uses
$filechooser = $gui->get_widget('filechooserbutton'); # get a pointer to widget
$status = $gui->get_widget('status');
$spinstart = $gui->get_widget('spinstart');
$spinstop = $gui->get_widget('spinstop');
$logplot = $gui->get_widget('logplot');
$points = $gui->get_widget ('rad1');
$lines = $gui->get_widget ('rad2');
$both = $gui->get_widget ('rad3');
$GPM_1 = $gui->get_widget ('GPM1but');					# "what to plot"
$GPM_10 = $gui->get_widget ('GPM10but');
$NPM_1 = $gui->get_widget ('NPM1but');
$NPM_10 = $gui->get_widget ('NPM10but');


$doplot = $gui->get_widget('doplot');
$doplot->set_sensitive(FALSE); # this is how you enable/disable things derived from GtkWidget

$logplot->set_active(TRUE); # defaults
$both->set_active(TRUE);
$GPM_1->set_active(TRUE);
$GPM_10->set_active(TRUE);
# we'll set the neutron buttons active if the stuff is in the file we open

$gui->signal_autoconnect_from_package('main'); # hooks subs to signals if things are named right


# Enter the event loop
Gtk2->main;
# we die when the above exits

__END__  # we've put the glade xml file data here and read it from within - so if you change the glade...
<?xml version="1.0"?>
<glade-interface>
  <!-- interface-requires gtk+ 2.16 -->
  <!-- interface-naming-policy project-wide -->
  <widget class="GtkWindow" id="Mainwin">
    <property name="visible">True</property>
    <property name="has_tooltip">True</property>
    <property name="tooltip" translatable="yes">Uses gnuplot to plot standard counter
data from log files</property>
    <property name="border_width">3</property>
    <property name="title" translatable="yes">Standard Counter log file plotter</property>
    <property name="destroy_with_parent">True</property>
    <signal name="destroy" handler="gtk_main_quit" after="yes"/>
    <signal name="destroy_event" handler="gtk_main_quit" after="yes"/>
    <child>
      <widget class="GtkVBox" id="vbox1">
        <property name="visible">True</property>
        <child>
          <widget class="GtkHButtonBox" id="hbuttonbox1">
            <property name="visible">True</property>
            <property name="tooltip" translatable="yes">Press button and select a log file to plot</property>
            <child>
              <widget class="GtkFileChooserButton" id="filechooserbutton">
                <property name="visible">True</property>
                <property name="tooltip" translatable="yes">Choose a file to open</property>
                <property name="create_folders">False</property>
                <property name="width_chars">50</property>
                <signal name="file_set" handler="on_filechooserbutton_file_set" after="yes"/>
              </widget>
              <packing>
                <property name="expand">False</property>
                <property name="fill">False</property>
                <property name="position">0</property>
                <property name="secondary">True</property>
              </packing>
            </child>
          </widget>
          <packing>
            <property name="position">0</property>
          </packing>
        </child>
        <child>
          <widget class="GtkHButtonBox" id="hbuttonbox2">
            <property name="visible">True</property>
            <property name="tooltip" translatable="yes">Select what to plot</property>
            <child>
              <widget class="GtkCheckButton" id="GPM1but">
                <property name="label" translatable="yes">GPM-1</property>
                <property name="visible">True</property>
                <property name="can_focus">True</property>
                <property name="receives_default">False</property>
                <property name="tooltip" translatable="yes">Geigers per minute, one second average</property>
                <property name="draw_indicator">True</property>
              </widget>
              <packing>
                <property name="expand">False</property>
                <property name="fill">False</property>
                <property name="position">0</property>
              </packing>
            </child>
            <child>
              <widget class="GtkCheckButton" id="GPM10but">
                <property name="label" translatable="yes">GPM-10</property>
                <property name="visible">True</property>
                <property name="can_focus">True</property>
                <property name="receives_default">False</property>
                <property name="tooltip" translatable="yes">Geigers per minute, 10 second average</property>
                <property name="draw_indicator">True</property>
              </widget>
              <packing>
                <property name="expand">False</property>
                <property name="fill">False</property>
                <property name="position">1</property>
              </packing>
            </child>
            <child>
              <widget class="GtkCheckButton" id="NPM1but">
                <property name="label" translatable="yes">NPM-1</property>
                <property name="visible">True</property>
                <property name="can_focus">True</property>
                <property name="receives_default">False</property>
                <property name="tooltip" translatable="yes">Neutrons/minute, one second</property>
                <property name="draw_indicator">True</property>
              </widget>
              <packing>
                <property name="expand">False</property>
                <property name="fill">False</property>
                <property name="position">2</property>
              </packing>
            </child>
            <child>
              <widget class="GtkCheckButton" id="NPM10but">
                <property name="label" translatable="yes">NPM-10</property>
                <property name="visible">True</property>
                <property name="can_focus">True</property>
                <property name="receives_default">False</property>
                <property name="tooltip" translatable="yes">Neutrons/minute 10 sec average</property>
                <property name="draw_indicator">True</property>
              </widget>
              <packing>
                <property name="expand">False</property>
                <property name="fill">False</property>
                <property name="position">3</property>
              </packing>
            </child>
          </widget>
          <packing>
            <property name="position">1</property>
          </packing>
        </child>
        <child>
          <widget class="GtkHButtonBox" id="hbuttonbox3">
            <property name="visible">True</property>
            <child>
              <widget class="GtkCheckButton" id="logplot">
                <property name="label" translatable="yes">Log plot</property>
                <property name="visible">True</property>
                <property name="can_focus">True</property>
                <property name="receives_default">False</property>
                <property name="tooltip" translatable="yes">Use logarithmic Y axis if checked</property>
                <property name="draw_indicator">True</property>
              </widget>
              <packing>
                <property name="expand">False</property>
                <property name="fill">False</property>
                <property name="position">0</property>
              </packing>
            </child>
            <child>
              <widget class="GtkRadioButton" id="rad1">
                <property name="label" translatable="yes">points</property>
                <property name="visible">True</property>
                <property name="can_focus">True</property>
                <property name="receives_default">False</property>
                <property name="tooltip" translatable="yes">points plot</property>
                <property name="active">True</property>
                <property name="draw_indicator">True</property>
              </widget>
              <packing>
                <property name="expand">False</property>
                <property name="fill">False</property>
                <property name="position">1</property>
              </packing>
            </child>
            <child>
              <widget class="GtkRadioButton" id="rad2">
                <property name="label" translatable="yes">lines</property>
                <property name="visible">True</property>
                <property name="can_focus">True</property>
                <property name="receives_default">False</property>
                <property name="tooltip" translatable="yes">line plot</property>
                <property name="active">True</property>
                <property name="draw_indicator">True</property>
                <property name="group">rad1</property>
              </widget>
              <packing>
                <property name="expand">False</property>
                <property name="fill">False</property>
                <property name="position">2</property>
              </packing>
            </child>
            <child>
              <widget class="GtkRadioButton" id="rad3">
                <property name="label" translatable="yes">both</property>
                <property name="visible">True</property>
                <property name="can_focus">True</property>
                <property name="receives_default">False</property>
                <property name="tooltip" translatable="yes">points AND lines</property>
                <property name="active">True</property>
                <property name="draw_indicator">True</property>
                <property name="group">rad1</property>
              </widget>
              <packing>
                <property name="expand">False</property>
                <property name="fill">False</property>
                <property name="position">3</property>
              </packing>
            </child>
            <child>
              <widget class="GtkButton" id="doplot">
                <property name="label" translatable="yes">DoPlot</property>
                <property name="visible">True</property>
                <property name="can_focus">True</property>
                <property name="receives_default">True</property>
                <property name="tooltip" translatable="yes">(Re)Create the plot</property>
                <signal name="clicked" handler="on_doplot_clicked" after="yes"/>
              </widget>
              <packing>
                <property name="expand">False</property>
                <property name="fill">False</property>
                <property name="position">4</property>
              </packing>
            </child>
          </widget>
          <packing>
            <property name="position">2</property>
          </packing>
        </child>
        <child>
          <widget class="GtkHButtonBox" id="hbuttonbox5">
            <property name="visible">True</property>
            <child>
              <widget class="GtkLabel" id="label1">
                <property name="visible">True</property>
                <property name="label" translatable="yes">Plot start seconds:</property>
              </widget>
              <packing>
                <property name="expand">False</property>
                <property name="fill">False</property>
                <property name="position">0</property>
              </packing>
            </child>
            <child>
              <widget class="GtkSpinButton" id="spinstart">
                <property name="visible">True</property>
                <property name="can_focus">True</property>
                <property name="tooltip" translatable="yes">Plot start time in seconds</property>
                <property name="invisible_char">&#x25CF;</property>
                <property name="adjustment">1 1 100 1 10 0</property>
                <property name="snap_to_ticks">True</property>
                <property name="numeric">True</property>
              </widget>
              <packing>
                <property name="expand">False</property>
                <property name="fill">False</property>
                <property name="position">1</property>
              </packing>
            </child>
            <child>
              <widget class="GtkLabel" id="label2">
                <property name="visible">True</property>
                <property name="label" translatable="yes">Plot end seconds:</property>
              </widget>
              <packing>
                <property name="expand">False</property>
                <property name="fill">False</property>
                <property name="position">2</property>
              </packing>
            </child>
            <child>
              <widget class="GtkSpinButton" id="spinstop">
                <property name="visible">True</property>
                <property name="can_focus">True</property>
                <property name="tooltip" translatable="yes">Plot end time in seconds</property>
                <property name="invisible_char">&#x25CF;</property>
                <property name="adjustment">1 1 100 1 10 0</property>
                <property name="snap_to_ticks">True</property>
                <property name="numeric">True</property>
              </widget>
              <packing>
                <property name="expand">False</property>
                <property name="fill">False</property>
                <property name="position">3</property>
              </packing>
            </child>
          </widget>
          <packing>
            <property name="position">3</property>
          </packing>
        </child>
        <child>
          <widget class="GtkLabel" id="status">
            <property name="visible">True</property>
            <property name="has_tooltip">True</property>
            <property name="tooltip" translatable="yes">Status/help info</property>
            <property name="label" translatable="yes">Choose a file, select what to plot,
and plotting options, then press plot button</property>
          </widget>
          <packing>
            <property name="position">4</property>
          </packing>
        </child>
      </widget>
    </child>
  </widget>
</glade-interface>
