#!/usr/bin/perl
use Modern::Perl;
use IO::Socket::INET;
use IO::Socket::Timeout; # so we don't hang in poll if an issue
use Time::HiRes qw(sleep);
use File::Basename 'dirname'; # for knowing where we are (ease for developer)
use Gtk3 -init; # for our GUI
use Glib  qw(TRUE FALSE); # for periodic event scheduling without sleeps

#GUI stuff (reference,content)
my $mainwin;
my $builder = Gtk3::Builder->new(); # gtkbuilder object, creates a gui from glade's xml
my $socket;
my $msg;  # tasty bits for supply - but you're hungry again right away
#### GUI elements
my ($vlabelr, $vlabelc); # reference and content for gui elements
my ($alabelr, $alabelc); # amp label
my ($vspinr, $vspinc);   # volt spinner
my ($aspinr,$aspinc);    # amp spinner
my ($chooser,$choosec);  # unit chooser
my ($enar,$enac);        # enable button

my $Tekpower1 = "ESP_3D687D_TP1"; # .64
my $Tekpower2 = "ESP_3DC567_TP2"; # .67

####################################################################
sub setupsocket
{
 my ($ESPIPn,$ESPIPa);
 close $socket if defined($socket);
 $choosec = $chooser->get_active_text();
 $ESPIPn = gethostbyname($choosec);
 if (defined($ESPIPn)) { # host is out there, we found it
# say "gethost is:$ESPIPn, defined?:defined($ESPIPn)";
    $ESPIPa = inet_ntoa($ESPIPn);
    $socket = IO::Socket::INET->new (
 	PeerAddr => $ESPIPa,
 	PeerPort => 42042,
 	Proto => "udp") or die "can't do socket:$choosec:$!";
# 2. Enable read and write timeouts on the socket
  IO::Socket::Timeout->enable_timeouts_on($socket);

# 3. Setup the timeouts
  $socket->read_timeout(0.5);
  $socket->write_timeout(0.5);

# sync spinners with actual set values to begin with
    $msg = "VSET1?\\n";
    print $socket $msg; # ask for existing voltage setting
    $socket->recv($msg,10);
    $vspinr->set_value($msg);
    say "setup vset:$msg";

    $msg = "ISET1?\\n";
    print $socket $msg; # ask for existing voltage setting
    $socket->recv($msg,10);
    $aspinr->set_value($msg);


  	$enar->set_sensitive(TRUE); # double use as indicator of good socket
   } else { # we didn't find host and connect
  	$enar->set_sensitive(FALSE); # or bad one, a way to say "it won't work anyway"
    undef ($socket);
   }	
 }
 
####################################################################
sub on_which_changed
{
 setupsocket();	
# $choosec = $chooser->get_active_text();
# say "event happened:$choosec";
}
####################################################################
sub on_voltspin_value_changed
{
 $vspinc = $vspinr->get_value();
 setvolts();
}
####################################################################
sub on_ampspin_value_changed
{
 $aspinc = $aspinr->get_value();
# say "ampspin:$aspinc";
 setamps();
}
####################################################################
sub setvolts
{
 $msg = sprintf("VSET1:%05.2f\\n",$vspinc);
# say "voltmessage:$msg";
 sendmsg();
}
####################################################################
sub setamps
{
 $msg = sprintf("ISET1:%5.3f\\n",$aspinc);
# say "amps:$msg";
 sendmsg();
}
####################################################################
sub on_enable_toggled
{
 $enac = $enar->get_active();
 if ($enac)
 { # we are turing it on
  $enar->set_label("Disable");
 } else { # we are turing it off
  $enar->set_label("Enable");
  $enac = 0; # else seems to be undef
 }
 $msg = sprintf("OUTPUT%u\\n",$enac);
# say "message:$msg";
 sendmsg();
}
####################################################################
sub sendmsg
{
	setupsocket() unless defined ($socket);
	print $socket $msg;
	say "sent:$msg";
}
####################################################################
sub initialize {
# createguifile();
createguilocal();
#get refs
$vlabelr = $builder->get_object('volts');
$alabelr = $builder->get_object('amps');
$vspinr = $builder->get_object('voltspin');
$aspinr = $builder->get_object('ampspin');
$chooser = $builder->get_object('which');
$enar = $builder->get_object('enable');

$chooser->remove_all(); # clean up any mess we left in Glade
$chooser->append_text($Tekpower1);
$chooser->append_text($Tekpower2);
$chooser->set_active(0);

$enac = 0; # this was either 1 or undef.  0 is cleaner.

Glib::Timeout::add($mainwin,2000,\&poll); #!!!! Victory!  Timed polling with sleeps in between.

###
 
}

####################################################################
sub createguifile # create the gui from xml in a .glade file 
{ # use this for developer convienience.  Final product should put glade xml
  # after the __END__ tag and use createguilocal instead
 $builder->add_from_file(dirname($0) . '/pwrctl.glade'); #@@@ hardcoded filename
 $mainwin = $builder->get_object('mainwin'); #@@@ assumes main window is called mainwin
 $builder->connect_signals(undef);
 $mainwin->set_screen( $mainwin->get_screen() ); #??? from an example.  Seems redundant?
 $mainwin->signal_connect(destroy => sub {Gtk3->main_quit});
 $mainwin->show_all();
# not so program-specific you couldn't use it again
}
####################################################################

####################################################################
sub createguilocal
{
 local $/;
 my $data = <DATA>; # slurp whole "file"
 $builder->add_from_string($data,length($data));
 $mainwin = $builder->get_object('mainwin'); #@@@ assumes main window is called mainwin
 $builder->connect_signals(undef);
 $mainwin->set_screen( $mainwin->get_screen() ); #??? from an example.  Seems redundant?
 $mainwin->signal_connect(destroy => sub {Gtk3->main_quit});
 $mainwin->show_all();
# not so program-specific you couldn't use it again
}

####################################################################
sub poll { # update gui
	# if socket, ask for volts and amps and update gui with info
 say "poll, enac:$enac";
 if ($socket && $enac) # it's connected and enabled
 {
  $msg = "VOUT1?\\n";
  print $socket $msg; # ask for volts output
  sleep .1;
  $socket->recv($msg,10);
  $msg .= " volts";
  $vlabelr->set_text($msg);
  $msg = "IOUT1?\\n"; 
  print $socket $msg;
  $socket->recv($msg,10);
  $msg .= " amps";
  $alabelr->set_text($msg);
 } else { #disconnected or disabled
  $vlabelr->set_text ("00.00 volts");
  $alabelr->set_text ("0.000 amps");
 }

 return 1; # true return to keep running	
}
####################################################################


####################################################################
########################## Main ####################################
####################################################################
initialize();

say "made it";
 Gtk3->main; # GUI event loop - when we quit, we fall out and exit

__END__
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.18.3 -->
<interface>
  <requires lib="gtk+" version="3.10"/>
  <object class="GtkAdjustment" id="aadj">
    <property name="upper">5</property>
    <property name="step_increment">0.0099999997764825821</property>
    <property name="page_increment">0.10000000000000001</property>
  </object>
  <object class="GtkAdjustment" id="vadj">
    <property name="upper">30</property>
    <property name="step_increment">0.01</property>
    <property name="page_increment">1</property>
  </object>
  <object class="GtkApplicationWindow" id="mainwin">
    <property name="can_focus">False</property>
    <child>
      <object class="GtkBox" id="box1">
        <property name="visible">True</property>
        <property name="can_focus">False</property>
        <property name="orientation">vertical</property>
        <child>
          <object class="GtkBox" id="box3">
            <property name="visible">True</property>
            <property name="can_focus">False</property>
            <property name="homogeneous">True</property>
            <child>
              <object class="GtkLabel" id="volts">
                <property name="width_request">-1</property>
                <property name="visible">True</property>
                <property name="can_focus">False</property>
                <property name="valign">end</property>
                <property name="hexpand">True</property>
                <property name="label" translatable="yes">10.00 Volts</property>
                <property name="ellipsize">end</property>
                <property name="single_line_mode">True</property>
                <property name="lines">1</property>
                <attributes>
                  <attribute name="font-desc" value="&lt;Enter Value&gt; 22"/>
                  <attribute name="foreground" value="#8a8ae2e23434"/>
                  <attribute name="background" value="#000000000000"/>
                </attributes>
              </object>
              <packing>
                <property name="expand">False</property>
                <property name="fill">True</property>
                <property name="position">0</property>
              </packing>
            </child>
            <child>
              <object class="GtkLabel" id="amps">
                <property name="visible">True</property>
                <property name="can_focus">False</property>
                <property name="label" translatable="yes">0.000 amps</property>
                <attributes>
                  <attribute name="font-desc" value="&lt;Enter Value&gt; 22"/>
                  <attribute name="foreground" value="#efef29292929"/>
                  <attribute name="background" value="#000000000000"/>
                </attributes>
              </object>
              <packing>
                <property name="expand">False</property>
                <property name="fill">True</property>
                <property name="position">1</property>
              </packing>
            </child>
          </object>
          <packing>
            <property name="expand">False</property>
            <property name="fill">True</property>
            <property name="position">0</property>
          </packing>
        </child>
        <child>
          <object class="GtkBox" id="box2">
            <property name="visible">True</property>
            <property name="can_focus">False</property>
            <property name="homogeneous">True</property>
            <child>
              <object class="GtkSpinButton" id="voltspin">
                <property name="visible">True</property>
                <property name="can_focus">True</property>
                <property name="max_width_chars">5</property>
                <property name="input_purpose">number</property>
                <property name="orientation">vertical</property>
                <property name="adjustment">vadj</property>
                <property name="climb_rate">0.01</property>
                <property name="digits">2</property>
                <property name="numeric">True</property>
                <property name="update_policy">if-valid</property>
                <signal name="value-changed" handler="on_voltspin_value_changed" after="yes" swapped="no"/>
              </object>
              <packing>
                <property name="expand">False</property>
                <property name="fill">True</property>
                <property name="position">0</property>
              </packing>
            </child>
            <child>
              <object class="GtkSpinButton" id="ampspin">
                <property name="visible">True</property>
                <property name="can_focus">True</property>
                <property name="input_purpose">number</property>
                <property name="orientation">vertical</property>
                <property name="adjustment">aadj</property>
                <property name="climb_rate">0.01</property>
                <property name="digits">3</property>
                <property name="numeric">True</property>
                <signal name="value-changed" handler="on_ampspin_value_changed" after="yes" swapped="no"/>
              </object>
              <packing>
                <property name="expand">False</property>
                <property name="fill">True</property>
                <property name="position">1</property>
              </packing>
            </child>
          </object>
          <packing>
            <property name="expand">False</property>
            <property name="fill">True</property>
            <property name="position">1</property>
          </packing>
        </child>
        <child>
          <object class="GtkBox" id="box4">
            <property name="visible">True</property>
            <property name="can_focus">False</property>
            <child>
              <placeholder/>
            </child>
            <child>
              <placeholder/>
            </child>
            <child>
              <placeholder/>
            </child>
          </object>
          <packing>
            <property name="expand">False</property>
            <property name="fill">True</property>
            <property name="position">2</property>
          </packing>
        </child>
        <child>
          <object class="GtkBox" id="box5">
            <property name="visible">True</property>
            <property name="can_focus">False</property>
            <property name="homogeneous">True</property>
            <child>
              <object class="GtkComboBoxText" id="which">
                <property name="visible">True</property>
                <property name="can_focus">False</property>
                <property name="opacity">0.97999999999999998</property>
                <property name="active">0</property>
                <property name="button_sensitivity">on</property>
                <property name="has_entry">True</property>
                <items>
                  <item translatable="yes"/>
                </items>
                <signal name="changed" handler="on_which_changed" after="yes" swapped="no"/>
                <child internal-child="entry">
                  <object class="GtkEntry" id="comboboxtext-entry">
                    <property name="can_focus">False</property>
                    <property name="placeholder_text" translatable="yes">select a unit</property>
                  </object>
                </child>
              </object>
              <packing>
                <property name="expand">False</property>
                <property name="fill">True</property>
                <property name="position">0</property>
              </packing>
            </child>
            <child>
              <object class="GtkToggleButton" id="enable">
                <property name="label" translatable="yes">Enable</property>
                <property name="visible">True</property>
                <property name="can_focus">True</property>
                <property name="receives_default">True</property>
                <signal name="toggled" handler="on_enable_toggled" after="yes" swapped="no"/>
              </object>
              <packing>
                <property name="expand">False</property>
                <property name="fill">True</property>
                <property name="position">1</property>
              </packing>
            </child>
          </object>
          <packing>
            <property name="expand">False</property>
            <property name="fill">True</property>
            <property name="position">3</property>
          </packing>
        </child>
      </object>
    </child>
  </object>
</interface>
