#!/usr/bin/perl
#
# get_power_data_sail.pl - Steven Lerner 6/2005
#                          Woods Hole Oceanographic Institution
#
#       Description: Quick script that gets data via mars power
#                    controller via obc xbar7/sail/NPC serial port and logs
#                    raw and space-separated records to Data dir
#                    which can be plotted via gen_daily_plots.pl script.
#
#       Assumptions: Reguires that the serial connection to power ctrl
#                    is already logged-in (ie; open minicom session,
#                    log-in, and then quit w/o reset via ctl-a q)
#                    
#       Usage: get_power_data_sail.pl [-l]
#
#       Output: Generates timestamped power ctlr data for currents,
#               voltages, and temperatures.
#
# History:
#       Date       Who    Description
#       ----       ---    ---------------------------------------------------
#      06/05/2005  SL     Create
#      06/12/2005  SL     Added gettine switch states and set sleep_amt to 4sec
#      08/20/2005  SL     Ported for sail interface. Added -l, -d options
#      08/27/2005  SL     Updated for new sensor names and order. Only write 
#                         header if file is new
#      09/04/2005  SL     Updated parsing of return strings to use attr val/name,
#                         decouples order of incoming data and outputs full line
#      09/20/2005  SL     Update for new sensor names/mapping eg; edfa->cisco
#      12/20/2005  SL     Updated handling terminator char for att/val pairs
#                         Added socket interface for external command interface
#                         and added npc_login upon starrtup
#################################################################################
require "getopts.pl";
require "flush.pl";
require "ctime.pl";
require "timelocal.pl";

$VERSION = "v1.0";

&Getopts('dl');
if (! defined ($opt_l)) {$loop_once = 0;}
                   else {$loop_once = 1;}
if (! defined ($opt_d)) {$DEBUG = 0;}
                   else {$DEBUG = 1;}

$sail = "/home/stevel/sail_driver/sail";
$ddir = "/home/stevel/power_obc/Data";

#Make sure we are logged-in
print "Trying to log into NPC\n" if $DEBUG;
$ret = `./npc_login -b`;
print "ret=[$ret]\n" if $DEBUG;
if ($ret !~ /^1/) {print "Can't log into NPC, try again later...\n";
                   exit;
                   }
sleep 1;


use FileHandle;
use IPC::Open3;

my $in  = new FileHandle;
my $out = new FileHandle;
my $err = new FileHandle;

open3 ($out,$in,$err,"$sail -s#S01A -x7") || die "Unable to open sail driver $!\n";

use IO::Socket::INET;
$UDP_port = 5000; 
$cmd_sock = IO::Socket::INET->new(Proto => 'udp',
                                  LocalPort => "$UDP_port",
	  		          ReuseAddr => 1);
if (!defined($cmd_sock))
      {warn "***unable to create command socket (port=$port)... \n $!";
       }


$sleep_amt = 4;
$prev_datafile = "";

# Loop-forever polling currents and voltages and temps (sensors)
while (1)
  {$tag = gmtimestamp_file();
   #$datafile = "Data/$tag";
   $datafile = "$ddir/$tag";
   if ($datafile ne "$prev_datafile")
     {#Open daily raw data files
      open(CURR_RAW,">>$datafile.cur.raw") || die "Unable to open data file: $datafile.cur.raw\n";
      open(STATE_RAW,">>$datafile.state.raw") || die "Unable to open data file: $datafile.state.raw\n";
      open(VOLT_RAW,">>$datafile.vlt.raw") || die "Unable to open data file: $datafile.vlt.raw\n";
      open(SEN_RAW,">>$datafile.sen.raw") || die "Unable to open data file: $datafile.sen.raw\n";

      #Open daily dat files - if it exists, just append to it, otherewise
      #add header
      if (-f "$datafile.cur.dat")
        {open(CURR_DAT,">>$datafile.cur.dat") || die "Unable to open data file: $datafile.cur.dat\n";}
      else
        {open(CURR_DAT,">$datafile.cur.dat") || die "Unable to open data file: $datafile.cur.dat\n";
         print CURR_DAT "#fields: date,time,load01,load02,load03,load04,load05,load06,load07,load08,";
         print CURR_DAT "load09,load10,load11,load12,load13,load14,load15,load16\n";
         flush(CURR_DAT);
         }

      if (-f "$datafile.state.dat")
        {open(STATE_DAT,">>$datafile.state.dat") || die "Unable to open data file: $datafile.state.dat\n";}
      else
        {open(STATE_DAT,">$datafile.state.dat") || die "Unable to open data file: $datafile.state.dat\n";
         print STATE_DAT "#fields: date,time,load01,load02,load03,load04,load05,load06,load07,load08,";
         print STATE_DAT "load09,load10,load11,load12,load13,load14,load15,load16\n";
         flush(STATE_DAT);
         }

      if (-f "$datafile.vlt.dat")
        {open(VOLT_DAT,">>$datafile.vlt.dat") || die "Unable to open data file: $datafile.vlt.dat\n";}
      else
        {open(VOLT_DAT,">>$datafile.vlt.dat") || die "Unable to open data file: $datafile.vlt.dat\n";
         print VOLT_DAT "#fields: date,time,v12_mV,v48_mV,v-12_mV,v5_mV\n";
         flush(VOLT_DAT);
         }


      if (-f "$datafile.sen.dat")
        {open(SEN_DAT,">>$datafile.sen.dat") || die "Unable to open data file: $datafile.sen.dat\n";}
      else
        {open(SEN_DAT,">>$datafile.sen.dat") || die "Unable to open data file: $datafile.sen.dat\n";
         #print SEN_DAT "#fields: date,time,mv1,mv2,mv3,mv4,mv5,mv6,edfaE,mv8,xlca,edfaW,";
        #print SEN_DAT "bottom,top,ilc,pres_lv,laserE,bm,xlcb,pres_fluid,mv,humid,";
         #print SEN_DAT "laserW,tbd1,commsa,idc,pres_mv,comsb\n";
         #Sensor order updated 8/27/2005
         #print SEN_DAT "#fields: date,time,humid,pres_fluid,pres_lv,pres_mv,bm,bottom,edfaE,edfaW,";
         #print SEN_DAT "idc,ilc,mv1,mv2,mv3,mv4,mv5,mv6,mv7,mv8,neE75,neW75,oeE75,oeW75,";
         #print SEN_DAT "tbd1,top,xlca,xlcb\n";
         #Sensor name updated from edfaE/W to cisco01/02 - 9/20/2005
         print SEN_DAT "#fields: date,time,humid,pres_fluid,pres_lv,pres_mv,bm,bottom,cisco01,cisco02,";
         print SEN_DAT "idc,ilc,mv1,mv2,mv3,mv4,mv5,mv6,mv7,mv8,neE75,neW75,oeE75,oeW75,";
         print SEN_DAT "tbd1,top,xlca,xlcb\n";
         }

      }
   $prev_datafile = "$datafile";


   #Get Current values
   $cmd = "getc 0\n";
   print "Sending $cmd to $port\n" if $DEBUG;
   print $out "$cmd"; #flush(COM);
   $cnt = 0;
   while ($line = <$in>)
     {if ($DEBUG) {print "$line"; flush(STDOUT);}
      if ($cnt++ == 1) {$timestamp = gmtimestamp(1);
                        print "time=$timestamp\n" if ($DEBUG);
                        $Curr{'date_time'} = "$timestamp";
                        print CURR_RAW "time=$timestamp\n";
                        flush(CURR_RAW);
                        }
      if ($line =~ /load/) {$line =~ s/\> //;
                            print CURR_RAW "$line"; flush(CURR_RAW);
                            ($attr,$val) = split('=',$line,2);
                            #chop($val); chop($val);
                            chomp($val); $val =~ s/\r//;
                            $Curr{"$attr"} = $val;
                            }
      #last if ($line =~ /^>/);
      last if ($line =~ /load16/);
      }
   print CURR_DAT "$Curr{'date_time'} $Curr{'load01'}  $Curr{'load02'} $Curr{'load03'} $Curr{'load04'} $Curr{'load05'} $Curr{'load06'} $Curr{'load07'} $Curr{'load08'} $Curr{'load09'} $Curr{'load10'} $Curr{'load11'} $Curr{'load12'} $Curr{'load13'} $Curr{'load14'} $Curr{'load15'} $Curr{'load16'}\n"; 
   &flush(CURR_DAT);



   #Get State values
   $cmd = "gets 0\n";
   print "Sending $cmd to $port\n" if $DEBUG;
   print $out "$cmd"; #flush(COM);
   $cnt = 0; undef %State;
   while ($line = <$in>)
     {if ($DEBUG) {print "$line"; flush(STDOUT);}
      if ($cnt++ == 1) {$timestamp = gmtimestamp(1);
                        $State{'date_time'} = "$timestamp";
                        print "time=$timestamp\n" if ($DEBUG);
                        print STATE_RAW "time=$timestamp\n";
                        flush(STATE_RAW);
                        }
      if ($line =~ /load/) {$line =~ s/\> //;
                            print STATE_RAW "$line"; flush(STATE_RAW);
                            ($attr,$val) = split('=',$line,2);
                            #chop($val); chop($val);
                            chomp($val); $val =~ s/\r//;
                            $State{"$attr"} = $val;
                            }
      #last if ($line =~ /^>/);
      last if ($line =~ /load16/);
      }
   print STATE_DAT "$State{'date_time'} $State{'load01'} $State{'load02'} $State{'load03'} $State{'load04'} $State{'load05'} $State{'load06'} $State{'load07'} $State{'load08'} $State{'load09'} $State{'load10'} $State{'load11'} $State{'load12'} $State{'load13'} $State{'load14'} $State{'load15'} $State{'load16'}\n"; 
   &flush(STATE_DAT);



   #Get Bus Voltage values
   $cmd = "getv\n";
   print "Sending $cmd to $port\n" if $DEBUG;
   print $out "$cmd"; #flush(COM);
   $cntv = 0; undef %Bus;
   while ($line = <$in>)
     {if ($DEBUG) {print "$line"; flush(STDOUT);}
      if ($cntv++ == 1) {$timestamp = gmtimestamp(1);
                         $Bus{'date_time'} = "$timestamp";
                         print "time=$timestamp\n" if ($DEBUG);
                         print VOLT_RAW "time=$timestamp\n";
                         flush(VOLT_RAW);
                         }
      if ($line =~ /mV/) {$line =~ s/\> //;
                          print VOLT_RAW "$line"; flush(VOLT_RAW);
                          ($attr,$val_str) = split('=',$line,2);
                          #chop($val); chop($val);
                          chomp($val); $val =~ s/\r//;
                          ($val,$units) = split(' ',$val_str);
                          $Bus{"$attr"} = $val;
                          }
      #last if ($line =~ /^>/);
      last if ($line =~ /v5/);
      }
   print VOLT_DAT "$Bus{'date_time'} $Bus{'v12'} $Bus{'v48'} $Bus{'v-12'} $Bus{'v5'}\n";
   &flush(VOLT_DAT);


   #Get sensors - temperature, pressure, humidity
   $cmd = "getsens\n";
   print "Sending $cmd to $port\n" if $DEBUG;
   print $out "$cmd"; #flush(COM);
   $cnt2 = 0; undef %Sens;
   while ($line = <$in>)
     {if ($DEBUG) {print "$line"; flush(STDOUT);}
      if ($cnt2++ == 1) {$timestamp = gmtimestamp(1);
                         print "time=$timestamp\n" if ($DEBUG);
                         $Sens{'date_time'} = "$timestamp";
                         print SEN_RAW "time=$timestamp\n";
                         flush(SEN_RAW);
                         }
      if ($line =~ /temp|pressure|humidity/) 
                           {$line =~ s/\> //;
                            print SEN_RAW "$line"; flush(SEN_RAW);
                            ($attr,$val_str) = split('=',$line,2);
                            #chop($val); chop($val);
                            chomp($val); $val =~ s/\r//;
                            ($val,$units) = split(' ',$val_str,2);
                            $Sens{"$attr"} = $val;
                            }
      #last if ($line =~ /^>/);
      #last if ($line =~ /temp\/comms\/b/);
      last if ($line =~ /temp\/xlc\/b/);
      }

    ### TEMP - 9/20/2005 - pressure/lv is no longer being returned via getsense. Temp stub-out
    if (!defined $Sens{'pressure/lv'}) {$Sens{'pressure/lv'} = "---";}

   print SEN_DAT "$Sens{'date_time'} $Sens{'humidity'} $Sens{'pressure/fluid'} $Sens{'pressure/lv'} $Sens{'pressure/mv'} $Sens{'temp/bm'} $Sens{'temp/bottom'} $Sens{'temp/cisco/01'} $Sens{'temp/cisco/02'} $Sens{'temp/idc'} $Sens{'temp/ilc'} $Sens{'temp/mv/1'} $Sens{'temp/mv/2'} $Sens{'temp/mv/3'} $Sens{'temp/mv/4'} $Sens{'temp/mv/5'} $Sens{'temp/mv/6'} $Sens{'temp/mv/7'} $Sens{'temp/mv/8'} $Sens{'temp/ne/switch/e75'} $Sens{'temp/ne/switch/w75'} $Sens{'temp/oe/e75'} $Sens{'temp/oe/w75'} $Sens{'temp/tbd1'} $Sens{'temp/top'} $Sens{'temp/xlc/a'} $Sens{'temp/xlc/b'}\n";

   &flush(SEN_DAT);

   if ($loop_once) {exit;}
   
   print "Sleeping $sleep_amt...\n" if ($DEBUG);

   #sleep $sleep_amt;

   #See if any UDP command available, if so, send it
   for ($i=0; $i<$sleep_amt; $i++)
     {$cmd = &ReadUDPCommand();
      print "UDP CMD Received [$cmd]\n" if $DEBUG;
      if ($cmd =~ /set|cas/) 
        {print "Sending $cmd to $port\n" if $DEBUG;
         print $out "$cmd"; #flush(COM);
         }
      sleep 1;
      }
   }


exit;

#
#Subroutines Follow
#
sub gmtimestamp {local($no_gmtlabel) = $_[0];
        local($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = gmtime;
        # zero fill hour,min,sec and month
        if ($sec < 10) {$sec = "0$sec";}
        if ($min < 10) {$min = "0$min";}
        if ($hour < 10) {$hour = "0$hour";}
        $mon += 1; #from gmtime defined 0-11, make it 1-12
        if ($mon < 10) {$mon = "0$mon";}
        if ($mday < 10) {$mday = "0$mday";}
        #make year 4-digits - Note: Year is years since 1900
        $year += 1900;
        if ($no_gmtlabel)
             {return "$year/$mon/$mday $hour:$min:$sec";}
        else {return "$year/$mon/$mday $hour:$min:$sec GMT";}
}




sub gmtimestamp_file {
        local($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = gmtime;
        # zero fill hour,min,sec and month
        if ($sec < 10) {$sec = "0$sec";}
        if ($min < 10) {$min = "0$min";}
        if ($hour < 10) {$hour = "0$hour";}
        $mon += 1; #from gmtime defined 0-11, make it 1-12
        if ($mon < 10) {$mon = "0$mon";}
        if ($mday < 10) {$mday = "0$mday";}
        #make year 4-digits - Note: Year is years since 1900
        $year += 1900;
        return "$year$mon$mday";
}


sub ReadUDPCommand()
{
    my($msg)="";
    my($rbits) = "";
    if (!defined($cmd_sock)) {return;}
    vec($rbits, fileno($cmd_sock), 1) = 1;
    my($nfound) = select($rbits, undef, undef, .25); # Wait for packet
    if ($nfound)                     # Got a packet from somewhere
        {$cmd_sock->recv($msg,1024);
         #print "Got: $msg";
         }
    return($msg);
}


####Sample output from Node power controller (8/27/2005)
#help
#The following commands are recognized:
#
#sets N 0|1   set switch for Load N (1-16) on (1) or off (0)
#gets N       get the state of Load N
#getc N       read current for Load N
#getv         read voltage on all internal buses
#getsens      read all engineering sensors
#quit         exit the program
#
#gets 0
#load01=1
#load-2=0
#...
#
#getc 0
#load01=-10
#load02=27
#...
#
#getv
#v12=61 mV
#v48=488 mV
#v-12=242 mV
#v5=41 mV
#
##Sample getsens response 8/27/2005
#getsens
#humidity=-25.205 %rh
#pressure/fluid=-15.419 psi
#pressure/lv=35.703 psi
#pressure/mv=-46.791 psi
#temp/bm=-13.641 degrees C
#temp/bottom=-182.72 degrees C
#temp/edfa/east=-186.78 degrees C
#temp/edfa/west=-185.23 degrees C
#temp/idc=-0.10681 degrees C
#temp/ilc=-21.835 degrees C
#temp/mv/1=-185.53 degrees C
#temp/mv/2=-185.55 degrees C
#temp/mv/3=-185.53 degrees C
#temp/mv/4=-185.47 degrees C
#temp/mv/5=-185.5 degrees C
#temp/mv/6=-185.59 degrees C
#temp/mv/7=-185.52 degrees C
#temp/mv/8=-185.47 degrees C
#temp/ne/switch/e75=-1.4038 degrees C
#temp/ne/switch/w75=-4.3945 degrees C
#temp/oe/e75=-1.2054 degrees C
#temp/oe/w75=-1.2207 degrees C
#temp/tbd1=-182.51 degrees C
#temp/top=-189.01 degrees C
#temp/xlc/a=0.83923 degrees C
#temp/xlc/b=-47.15 degrees C

#Sample getsens response as of 9/27/2005
#> getsens
#humidity=37.066 %rh
#pressure/fluid=14.868 psi
#pressure/mv=14.502 psi
#temp/bm=18.784 degrees C
#temp/bottom=24.979 degrees C
#temp/cisco/01=21.515 degrees C
#temp/cisco/02=19.012 degrees C
#temp/idc=24.536 degrees C
#temp/ilc=29.8 degrees C
#temp/mv/1=89.951 degrees C
#temp/mv/2=87.418 degrees C
#temp/mv/3=85.968 degrees C
#temp/mv/4=87.906 degrees C
#temp/mv/5=86.06 degrees C
#temp/mv/6=84.076 degrees C
#temp/mv/7=129.62 degrees C
#temp/mv/8=127.03 degrees C
#temp/ne/switch/e75=26.443 degrees C
#temp/ne/switch/w75=40.161 degrees C
#temp/oe/e75=28.03 degrees C
#temp/oe/w75=30.807 degrees C
#temp/tbd1=24.338 degrees C
#temp/top=19.791 degrees C
#temp/xlc/a=21.194 degrees C
#temp/xlc/b=22.568 degrees C

