#! /usr/bin/perl -w

# path to arduino uno
#/dev/serial/by-id/usb-Arduino__www.arduino.cc__0043_D1DDD1DDD1DD51DDD1DD-if00
# (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
##########################################################
#
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

use Device::SerialPort; # the correct one for linux 
#it's different for windows, a lot, and not just the module name
# for windows, you gotta figure out which com port too...registry junk etc.
#sdaq had all that, sorta, but it's so much hassle to get perl and all these 
#modules going in windows, it's easier to get Virtual Box and 
#install linux in it instead.
# trust me.It really is.  It doesn't even waste cycles, just ram space and disk
# which isn't wasted - now you'll have linux 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
use File::Spec::Functions qw/:ALL/; # changes things like ./ to a real path
use File::Basename; # to get path to program for config files and logs
use Graphics::GnuplotIF qw(GnuplotIF); # gnuplot for plots

############################### globals ######################################## 
# yes, I "cheat" since "great mind runs in the same track".
# If a team was working on this, they'd be stepping on each other constantly, 
# but there isn't one - just me.
# It's actually faster this way, since lexical scope doesn't have perl 
# allocating and cleaning up memory constantly.
# (not that speed should matter much in a modern PC for this)
my $filepath;    # hold base path for local files, we'll make up the rest
my $logfilepath; # full path and filename for log file goes in this
my $port; # serial port object
my @lines; # lines from serial grab - should be around 10 of them...per second
my $running = FALSE;    # at least at first


############
my $countscale = 600.0;     # cpm per count per .1 second as float 
my $adscale = 0.001220703;  # volts per a/d count
my $maxwindow;        # tune for what computers can handle plotting

##############33
# gooey stuff
my $gui;
my $ssbut;      # pointer to go/stop button 
my $pmakelog;   # pointer to makelog checkbox
my $makelog = TRUE; # to write a log file 
my $plotc;      # pointer to "plot counts"
my $plotad;     # plot a/d checkbox
my $logwin;     #  big edit box
my $logwinbuf;  # buffer for above
my $pwindow;    # seconds in plot window spin button

# data for gnuplot
my $cplot;  # gnuplot object for counters
my $adplot; # ditto for a/d channels


my @xaxis;	# for gnu plots - in milliseconds (for now)
my @C0;		# counter zero plot data
my @C1;		# counter one
my @C0a;    # an average over some time
my @C1a;    # for C1
my @ad0;	# a/d channel zero
my @ad1;
my @ad2;
my @ad3;	# a/d channel 3
my @ad4;
my @ad5;    # last a/d channel for arduino uno

#try multiple styles in same plot for counts
my %y1 = ( 'y_values' => \@C0, 'style_spec' => "points");
my %y2 = ( 'y_values' => \@C1,'style_spec' => "points" );
my %y3 = ( 'y_values' => \@C0a,'style_spec' => "lines" ); # make averages lines
my %y4 = ( 'y_values' => \@C1a,'style_spec' => "lines" ); # looks better
##############################  subroutines ####################################  
#Easier to forward-declare & define than declare here and then define later
################################################################################
sub findarduino ()
{ # returns the filepath for the first arduino we find under /dev/serial/by-id
 my $apath;
 
 opendir(DIR,'/dev/serial/by-id') or die "No usb serial devices: $!\n";
 while (defined($apath = readdir(DIR)))
 {
  if ($apath =~ m/usb-Arduino/) # match an arduino name-part?
   {
    $apath = '/dev/serial/by-id/' . $apath; #concatenate
#    print "apath:$apath\n"; # debug
    closedir(DIR); # be nice and clean
    return $apath;
   } 
 }
 die "No arduinos found - we can't get data if there isn't any device connected!";
}
#################################################
sub resetcount()
{
 @C0 = (); # clear all the plot data
 @C1 = ();
 @C0a = ();
 @C1a = ();
 @ad0 = ();
 @ad1 = ();
 @ad2 = ();
 @ad3 = ();
 @ad4 = ();
 @ad5 = ();
 @xaxis = (); # 
# clear old plots and other stuff, start new if requested
 $logwinbuf->set_text(''); # wipe any old text from the display window

# restart arudino and purge old serial data
 $port->write('!');                     ### restart the arduino
#$port->write_drain; # flush output (I think) @@@
# sleep 1; # probably needed @@@ not so far...
 $port->purge_rx; #remove any old news
}
#################################################
sub getwidgetpointers()
{
$ssbut = $gui->get_widget('gostop'); # pointer to start button?
$pmakelog = $gui->get_widget('logfileb');
$pmakelog->set_active(TRUE);

$plotc = $gui->get_widget('plotcounts');
$plotc->set_active(TRUE);

$plotad = $gui->get_widget('plotad');  # plot a/d?
$plotad->set_active(TRUE);

$logwin = $gui->get_widget('logwin'); # pointer to text display window
$logwinbuf = $logwin->get_buffer(); # later use $logwinbuf->set_text($some_text);
$logwinbuf->create_tag("font",'font'=>'monospace'); # used to set font on text edit window
$pwindow = $gui->get_widget('windowseconds');
$gui->signal_autoconnect_from_package('main'); # hooks subs to signals if things are named right
}
####################
# to hook this up, in glade use gtk_main_quit as the handler in the 
# mainwin/GtkWidget/destory-event
sub gtk_main_quit  { Gtk2->main_quit; } # fall out of main loop and exit

###########################################################
# setup serial port and return false if error, more involved than
# it should have to be, but...that's life.  
sub setupserial()
{
 my $portname = findarduino();
#"/dev/serial/by-id/usb-Arduino__www.arduino.cc__0043_D1DDD1DDD1DD51DDD1DD-if00"; 
# now, init serial port from code defaults or config file
    my $cfgfile = sprintf("%s.cfg", "adaq");
    $cfgfile = catfile($filepath,$cfgfile); # with full path to program dir
    

    if ( -e $cfgfile ) {   # cfgfile exists
      $port = Device::SerialPort->start ($cfgfile) or die "Can't start $cfgfile\n";
	} else {   # cfgfile does not exist (e.g. 1st time for a tty device)
      $port = new Device::SerialPort ($portname, 1) || die "Can't open $portname: $!\n";
	}

    # some of this not needed if config file exists, this is for suspenders (and first times)
    # @@@ put this in else above, so can reset config by deleting the config on disk
      $port->databits(8);
      $port->baudrate(115200);
      $port->parity("none");
      $port->stopbits(1);
      $port->handshake("none");
      $port->stty_icanon(1);    # this is what gets us on line boundaries
      $port->are_match("\r\n"); # possible end strings (windows/arduino)
      $port->lookclear;         # empty the buffer
#   $port->read_const_time(10);       # const time for read (milliseconds)
#  $port->read_char_time(1);          # avg time between read char
      $port->write_settings || undef $port; # die, sort of, if not working
      $port->save($cfgfile) || warn "Can't save $cfgfile: $!\n"; # save for next time
      sleep 1; # let arduino boot
      $port->purge_all(); # kill any initial garbage
}
########################################################################
# Make logfile path.  Assumes $filepath is already a valid path, and 
# $logfilepath variable exists
# we'll just make a name out of the local time of day when called.
# should be unique
# A hassle because we can't use colons for time separator, spaces on some opsys, 
# and slashes to make it look as expected, as some opsys would interpret those 
# things as dir separators or invalid name chars
sub makelogfilepath
{
 my $tmp;
 my @time = localtime(); # get array of time data
 $tmp = join('_',$time[4]+1,$time[3],$time[5]+1900,$time[2],$time[1],$time[0]);
 $tmp .= '.log'; # add file extension
 $logfilepath = catfile($filepath,$tmp); # put together opsys independent
 #print ("logfilepath:$logfilepath\n"); # debug
}
####################
# create, open and insert a header line into our logfile
# assumes $logfilepath is valid when called
sub createlogfile
{
 open (LOG, "> $logfilepath") or die "Can't create $logfilepath: $!";
 print LOG "# name:$logfilepath\n"; # human readable help/audit in file
}

###########################################################
# get pathname to program directory for use in config and log files
# we need this insane dance as the program path in $0 might be ./name
# which is kind of hard to use right.  We want an absolute path where
# the script lives, and this gets it (with two modules needed!)
sub getmypath()
{
 my $path = rel2abs($0); # fix up any relative path we got
# print ("\nmunged path is: $path\n"); # debug
 $path = dirname($path); # now get just the dir without progname on it
#print ("\nfinally, path to program is: $path\n"); # debug 
 return $path;
}
#############################################
sub getlines()
{ # get as many lines as we can without waiting any extra
 my $gotit = "";
 @lines = (); # start clean
 do
 {
  $gotit = $port->streamline;      # see if data ready
  unless ($gotit eq "") {push (@lines,$gotit);} # if they're blank...
  my $match = $port->matchclear;# so we dont keep reading the same line over
 } until ($gotit eq "");
}
####################
# sub to add a line to our text view, then scroll it to the end
# found this trick here: http://www.gtkforums.com/viewtopic.php?t=1307
# and  translated it from C to perl...simpler in perl, actually
sub addtxtline
{ # text edit boxes are a PITA no matter the opsys or GUI toolset, it seems
  # it's not like you could do better -- the full editor is a complex thing
 my $line = shift; # whatever you handed us to insert at end
 my $itere; # a "thing" that marks a place in a textedit buffer,end in this case
 my $iterb; # same for an iter that points to start
 my $mk; # cursor position (post insert), kind of
 
 #$$$ add take lines off beginning in this routine
 while ($logwinbuf->get_line_count() > $maxwindow)
 { # remove a line at the beginning
 $iterb = $logwinbuf->get_start_iter(); # beginning of whole thing
 $itere = $logwinbuf->get_iter_at_line(1); # beginning of second line
  $logwinbuf->delete($iterb,$itere); # delete a line
 }
 
 $itere = $logwinbuf->get_end_iter(); # find the end
 $logwinbuf->insert($itere,$line); # insert at end
 # stuff to get fixed font.  Evidently have to do this for each new line 
 # might only need iters for that one line, but heck, that's harder than this is
 $iterb = $logwinbuf->get_start_iter(); # need to refresh both after buffer mod 
 $itere = $logwinbuf->get_end_iter(); # find the end
 $logwinbuf->apply_tag_by_name ("font", $iterb, $itere); # make all fixed font

 $mk = $logwinbuf->get_mark ("insert"); # mark maintained for non-editable
 $logwin->scroll_to_mark ($mk, 0.0, FALSE, 0.0, 0.0);
# $logwin->scroll_to_iter($itere,0.0,FALSE,0,0); # scroll to make it visible
# didn't work if I put in 10 lines quickly, the uncommented above DOES work
}

###################
sub poll ()
{ # called by GTK while running to get a batch of lines, print and plot 
 my $line;
 my @cfields;   # for splitting up line
 my $format;    # attempt at a trick
 my $avindex;   # for making count averages
 my $c0a;       # average of C0
 my $c1a;       #
 my $avdiv = 0;
 # note - a format string for formline() *needs* a \n
 $format="@######### @#### @#### @#### @#### @#### @#### @######### @#########\n";
 
 #print ("polled\n");# debug
 return FALSE unless $running; # quit calling me!
 getlines(); # gets any lines we've been sent into @lines
 foreach $line (@lines)
 {
  if ($makelog) # write *everything* we know to a log file
  {
   print LOG "$line\n";
#   print "log line printed\n"; 
  }
  next if $line =~ m/#/; # but skip comment lines for the edit box stuff (and plotting)
  @cfields = split(/\s+/,$line); # break on whitespace
  
  
# using push instead of an index, now.  This way we can shift off the beginning
# to limit total plot length to current N samples.  While we're at it,
# silently replace any undefined (transmission error?) data with zero.

# average some counts to create two new average series to plot  


  push @C0, defined($cfields[7]) ? $cfields[7] * $countscale : 0; # c0
  push @C1, defined($cfields[8]) ? $cfields[8] * $countscale : 0; # c1
  
  $avindex = $#C0; # last array index
  $c0a = $c1a = $avdiv = 0; # make zero and defined
  while ($avindex > 0 and $avdiv < 10) # 10 for one second
  {
   $c0a += $C0[$avindex];
   $c1a += $C1[$avindex--];
   $avdiv++;
  }
  
  if ($avdiv > 0)
  {
   $c0a /= $avdiv;
   $c1a /= $avdiv;
  }
  unless ($c0a) {$c0a = 1;} #@@@ @@@ force gnuplot to draw at all...
  push @C0a, $c0a; # push no matter what
  push @C1a, $c1a; # so shifts all have the same size to work with
  push @ad0, defined($cfields[1]) ? $cfields[1] * $adscale : 0; # a/d 0 
  push @ad1, defined($cfields[2]) ? $cfields[2] * $adscale : 0; # a/d 1
  push @ad2, defined($cfields[3]) ? $cfields[3] * $adscale : 0; # a/d 2
  push @ad3, defined($cfields[4]) ? $cfields[4] * $adscale : 0; # a/d 3
  push @ad4, defined($cfields[5]) ? $cfields[5] * $adscale : 0; # a/d 4
  push @ad5, defined($cfields[6]) ? $cfields[6] * $adscale : 0; # a/d 5
  push @xaxis, $cfields[0] * 0.001; # convert to seconds 
 
  while ($maxwindow < @xaxis) # shorten the arrays by removing early data
  {
   shift @xaxis;
   shift @C0;
   shift @C1;
   shift @C0a;
   shift @C1a;
   shift @ad0;
   shift @ad1;
   shift @ad2;
   shift @ad3;
   shift @ad4;
   shift @ad5;
  }
 
 
  $^A = ""; # perl magic format text accumulator
  formline($format,@cfields);
  addtxtline("$^A"); # Hmmmm
#  print "acc:$^A";
 } # foreach line
 print "";   
   if ($cplot) 
   {$cplot->gnuplot_plot_xy_style( \@xaxis, \%y1, \%y2, \%y3, \%y4);} # rewrite plot
   if ($adplot) {$adplot->gnuplot_plot_xy(\@xaxis,\@ad0,\@ad1,\@ad2,\@ad3,\@ad4,
  \@ad5);} # rewrite plot
 #@@@ actually found a perl bug in formline...needs a print or it hangs!
#setting STDOUT to autoflush might help, but we're not even using STDOUT for this!
 return TRUE; # keep running
}
#########################################################################################
sub on_gostop_toggled
{
 if ($running)
 { # stop, write any log file
  $running = FALSE; # internal boolean flag - stop timer on next call to poll
  $ssbut->set_label('ReStart'); # relabel button
  print ("\nStopping\n"); # debug
  close (LOG) or die "Couldn't close $logfilepath:$!"; # all done with this file
  $port->write('p'); ### pause input from arduino
# I'm erasing any existing plot when we restart, 
# so you get to hit stop and still study it onscreen until you go again.

 } else # time to start up the world!
 { # setup and start things - get user's desires from GUI controls
  $running = TRUE;
  $ssbut->set_label('Stop');
#  undef ($cplot); # kill any old plots
#  undef ($adplot); # this timing leaves them up as long as possible
  
  if ($plotc->get_active) 
   {
    unless ($cplot) # after first time, we have one already, no more new
     {
       $cplot =Graphics::GnuplotIF->new(title => "Counts",
       style =>"points", xlabel => 'Seconds', ylabel => 'CPM');
     }
    die "Couldn't create counter plot instance:$!\n" unless $cplot;   
    $cplot->gnuplot_cmd ("set grid");    
    $cplot->gnuplot_cmd ("set key left top"); 
    $cplot->gnuplot_set_plot_titles("C0-counts","C1-counts", 
    "C0-average", "C1-average");
    $cplot->gnuplot_cmd ("set log y");
   }
  if ($plotad->get_active) 
   {
    unless ($adplot)
    {
     $adplot = Graphics::GnuplotIF->new(title => "A/D inputs", raise => FALSE,
       style =>"lines", xlabel => 'Seconds', ylabel => 'Volts');
    }
    die "Couldn't create analog plot instance:$!\n" unless $adplot;   

    $adplot->gnuplot_cmd ("set grid");
    $adplot->gnuplot_cmd ("set key left bottom");
    $adplot->gnuplot_set_plot_titles("a/d 0","a/d 1","a/d 2","a/d 3","a/d 4",
    "a/d 5");
   }
  $makelog = $pmakelog->get_active;
  makelogfilepath(); # create a path and name in current program directory
  createlogfile() if $makelog; # start up a log if requested
#  print ("getvalue\n");
  $maxwindow = 10 * $pwindow->get_value;
  if ($maxwindow < 10) {$maxwindow = 10;}
  elsif ($maxwindow > 500000) {$maxwindow = 500000;}
#  print ("maxwindow:$maxwindow\n"); #debug

#  print ("\nReset and startup. Makelog:$makelog Makeplotopt:$makeplotopt,log plots:$logplots\n"); # debug
  resetcount(); # clear plot buffers, reset hardware, clear serial data
  Glib::Timeout->add(1000, \&poll); # start the calls to the poll subroutine
 } # end "if running" else "start case"
} # end on_gostop_toggled

################################################################################
############################## main ############################################
################################################################################

# print "Found all the required modules!\n";
# print "found: " . findarduino() . "\n";
$filepath = getmypath(); # find out where we are despite opsys munging - base dir for all other files
setupserial() or die "serial setup failed:$!\n";
# use this once you've put the glade XML in here after _END_
{ # create block so I can diddle record terminator without affecting the rest
 local $/; # remove normal record terminator
 my $guidata = <main::DATA>; # slurp the glade XML at EOF into a variable
 $gui = Gtk2::GladeXML->new_from_buffer($guidata); # and here it is, cool.
}  # end of block restores record separator to whatever it was

# get pointers to widgets and so forth
getwidgetpointers();

# Enter the event loop
Gtk2->main;

 # debug junk
 my $i;
 my $numlines;
 my $line;

while (0)
{
 $i++;
# print ("calling getline\n");
# printf ("$i:%s\n",getline());
  getlines();
  $numlines = @lines;
  print ("batchsize:$numlines\n");
  foreach $line (@lines)
  {
   printf("$i:%s\n",$line);
  } 
  
 sleep 1;
 if ($i>=5) {exit 0;}
}



__END__  # we've put the glade xml file data here - so if you change the glade..
<?xml version="1.0" encoding="UTF-8"?>
<glade-interface>
  <!-- interface-requires gtk+ 2.16 -->
  <!-- interface-naming-policy project-wide -->
  <widget class="GtkWindow" id="mainwin">
    <property name="width_request">768</property>
    <property name="visible">True</property>
    <property name="can_focus">False</property>
    <property name="tooltip" translatable="yes">Arduino data aquisition</property>
    <property name="title" translatable="yes">Standard Data Aquisition</property>
    <property name="default_width">768</property>
    <property name="default_height">768</property>
    <signal name="destroy" handler="gtk_main_quit" swapped="no"/>
    <signal name="destroy_event" handler="gtk_main_quit" swapped="no"/>
    <child>
      <widget class="GtkVPaned" id="vpaned1">
        <property name="visible">True</property>
        <property name="can_focus">True</property>
        <child>
          <widget class="GtkLabel" id="label1">
            <property name="visible">True</property>
            <property name="can_focus">False</property>
            <property name="xalign">0</property>
            <property name="label" translatable="yes">     Parameters                   Runtime-mS          AD0        AD1      AD2       AD3	 AD4        AD5                         C0                       C1    </property>
          </widget>
          <packing>
            <property name="resize">False</property>
            <property name="shrink">False</property>
          </packing>
        </child>
        <child>
          <widget class="GtkHPaned" id="hpaned1">
            <property name="visible">True</property>
            <property name="can_focus">True</property>
            <child>
              <widget class="GtkVButtonBox" id="vbuttonbox1">
                <property name="visible">True</property>
                <property name="can_focus">False</property>
                <child>
                  <widget class="GtkCheckButton" id="logfileb">
                    <property name="label" translatable="yes">Write Log File?</property>
                    <property name="visible">True</property>
                    <property name="can_focus">True</property>
                    <property name="receives_default">False</property>
                    <property name="tooltip" translatable="yes">Write a log file of this run</property>
                    <property name="use_action_appearance">False</property>
                    <property name="xalign">0.44999998807907104</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="GtkHSeparator" id="hseparator1">
                    <property name="visible">True</property>
                    <property name="can_focus">False</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="can_focus">False</property>
                    <property name="tooltip" translatable="yes">Define what to plot</property>
                    <property name="label" translatable="yes">Plot Selections</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="plotcounts">
                    <property name="label" translatable="yes">Plot counters?</property>
                    <property name="visible">True</property>
                    <property name="can_focus">True</property>
                    <property name="receives_default">False</property>
                    <property name="tooltip" translatable="yes">Make plot of counts?</property>
                    <property name="use_action_appearance">False</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>
                <child>
                  <widget class="GtkCheckButton" id="plotad">
                    <property name="label" translatable="yes">Plot a/d?</property>
                    <property name="visible">True</property>
                    <property name="can_focus">True</property>
                    <property name="receives_default">False</property>
                    <property name="has_tooltip">True</property>
                    <property name="tooltip" translatable="yes">Plot a/d data?</property>
                    <property name="use_action_appearance">False</property>
                    <property name="draw_indicator">True</property>
                  </widget>
                  <packing>
                    <property name="expand">False</property>
                    <property name="fill">False</property>
                    <property name="position">4</property>
                  </packing>
                </child>
                <child>
                  <widget class="GtkHSeparator" id="hseparator2">
                    <property name="visible">True</property>
                    <property name="can_focus">False</property>
                  </widget>
                  <packing>
                    <property name="expand">False</property>
                    <property name="fill">False</property>
                    <property name="position">5</property>
                  </packing>
                </child>
                <child>
                  <widget class="GtkHSeparator" id="hseparator16">
                    <property name="visible">True</property>
                    <property name="can_focus">False</property>
                  </widget>
                  <packing>
                    <property name="expand">False</property>
                    <property name="fill">False</property>
                    <property name="position">6</property>
                  </packing>
                </child>
                <child>
                  <widget class="GtkLabel" id="label3">
                    <property name="visible">True</property>
                    <property name="can_focus">False</property>
                    <property name="label" translatable="yes">Plot window,
Seconds </property>
                  </widget>
                  <packing>
                    <property name="expand">False</property>
                    <property name="fill">False</property>
                    <property name="position">7</property>
                  </packing>
                </child>
                <child>
                  <widget class="GtkSpinButton" id="windowseconds">
                    <property name="visible">True</property>
                    <property name="can_focus">True</property>
                    <property name="invisible_char">•</property>
                    <property name="primary_icon_activatable">False</property>
                    <property name="secondary_icon_activatable">False</property>
                    <property name="primary_icon_sensitive">True</property>
                    <property name="secondary_icon_sensitive">True</property>
                    <property name="adjustment">360 1 1000 1 10 0</property>
                  </widget>
                  <packing>
                    <property name="expand">False</property>
                    <property name="fill">False</property>
                    <property name="position">8</property>
                  </packing>
                </child>
                <child>
                  <widget class="GtkHSeparator" id="hseparator4">
                    <property name="visible">True</property>
                    <property name="can_focus">False</property>
                  </widget>
                  <packing>
                    <property name="expand">False</property>
                    <property name="fill">False</property>
                    <property name="position">9</property>
                  </packing>
                </child>
                <child>
                  <widget class="GtkHSeparator" id="hseparator17">
                    <property name="visible">True</property>
                    <property name="can_focus">False</property>
                  </widget>
                  <packing>
                    <property name="expand">False</property>
                    <property name="fill">False</property>
                    <property name="position">10</property>
                  </packing>
                </child>
                <child>
                  <widget class="GtkHSeparator" id="hseparator6">
                    <property name="visible">True</property>
                    <property name="can_focus">False</property>
                  </widget>
                  <packing>
                    <property name="expand">False</property>
                    <property name="fill">False</property>
                    <property name="position">11</property>
                  </packing>
                </child>
                <child>
                  <widget class="GtkHSeparator" id="hseparator7">
                    <property name="visible">True</property>
                    <property name="can_focus">False</property>
                  </widget>
                  <packing>
                    <property name="expand">False</property>
                    <property name="fill">False</property>
                    <property name="position">12</property>
                  </packing>
                </child>
                <child>
                  <widget class="GtkHSeparator" id="hseparator8">
                    <property name="visible">True</property>
                    <property name="can_focus">False</property>
                  </widget>
                  <packing>
                    <property name="expand">False</property>
                    <property name="fill">False</property>
                    <property name="position">13</property>
                  </packing>
                </child>
                <child>
                  <widget class="GtkHSeparator" id="hseparator9">
                    <property name="visible">True</property>
                    <property name="can_focus">False</property>
                  </widget>
                  <packing>
                    <property name="expand">False</property>
                    <property name="fill">False</property>
                    <property name="position">14</property>
                  </packing>
                </child>
                <child>
                  <widget class="GtkHSeparator" id="hseparator10">
                    <property name="visible">True</property>
                    <property name="can_focus">False</property>
                  </widget>
                  <packing>
                    <property name="expand">False</property>
                    <property name="fill">False</property>
                    <property name="position">15</property>
                  </packing>
                </child>
                <child>
                  <widget class="GtkHSeparator" id="hseparator11">
                    <property name="visible">True</property>
                    <property name="can_focus">False</property>
                  </widget>
                  <packing>
                    <property name="expand">False</property>
                    <property name="fill">False</property>
                    <property name="position">16</property>
                  </packing>
                </child>
                <child>
                  <widget class="GtkHSeparator" id="hseparator12">
                    <property name="visible">True</property>
                    <property name="can_focus">False</property>
                  </widget>
                  <packing>
                    <property name="expand">False</property>
                    <property name="fill">False</property>
                    <property name="position">17</property>
                  </packing>
                </child>
                <child>
                  <widget class="GtkHSeparator" id="hseparator13">
                    <property name="visible">True</property>
                    <property name="can_focus">False</property>
                  </widget>
                  <packing>
                    <property name="expand">False</property>
                    <property name="fill">False</property>
                    <property name="position">18</property>
                  </packing>
                </child>
                <child>
                  <widget class="GtkHSeparator" id="hseparator14">
                    <property name="visible">True</property>
                    <property name="can_focus">False</property>
                  </widget>
                  <packing>
                    <property name="expand">False</property>
                    <property name="fill">False</property>
                    <property name="position">19</property>
                  </packing>
                </child>
                <child>
                  <widget class="GtkHSeparator" id="hseparator15">
                    <property name="visible">True</property>
                    <property name="can_focus">False</property>
                  </widget>
                  <packing>
                    <property name="expand">False</property>
                    <property name="fill">False</property>
                    <property name="position">20</property>
                  </packing>
                </child>
                <child>
                  <widget class="GtkToggleButton" id="gostop">
                    <property name="label" translatable="yes">Go</property>
                    <property name="visible">True</property>
                    <property name="can_focus">True</property>
                    <property name="receives_default">True</property>
                    <property name="tooltip" translatable="yes">Go or stop</property>
                    <property name="use_action_appearance">False</property>
                    <property name="draw_indicator">True</property>
                    <signal name="toggled" handler="on_gostop_toggled" after="yes" swapped="no"/>
                  </widget>
                  <packing>
                    <property name="expand">False</property>
                    <property name="fill">False</property>
                    <property name="position">21</property>
                  </packing>
                </child>
              </widget>
              <packing>
                <property name="resize">False</property>
                <property name="shrink">True</property>
              </packing>
            </child>
            <child>
              <widget class="GtkScrolledWindow" id="txtscroll">
                <property name="visible">True</property>
                <property name="can_focus">True</property>
                <property name="hscrollbar_policy">never</property>
                <child>
                  <widget class="GtkTextView" id="logwin">
                    <property name="visible">True</property>
                    <property name="can_focus">True</property>
                    <property name="editable">False</property>
                    <property name="left_margin">10</property>
                    <property name="accepts_tab">False</property>
                  </widget>
                </child>
              </widget>
              <packing>
                <property name="resize">True</property>
                <property name="shrink">False</property>
              </packing>
            </child>
          </widget>
          <packing>
            <property name="resize">True</property>
            <property name="shrink">True</property>
          </packing>
        </child>
      </widget>
    </child>
  </widget>
</glade-interface>
