#!/usr/bin/perl -w
use strict;
use LWP::UserAgent;
use Net::HTTP;
use HTTP::Request::Common qw(POST);
use Mysql;
use Socket;

############# PARAMETERS ##########################################################
my $SAID  = shift || -1;
my $RFportID  = shift || -1;
my $BID    = shift || -1; # if -1 then band don't used

my $Status = 0; # OK
my @alarms = (0, 0, 0);
my @ASA = (0, 0, 0);
my @BSA = (0, 0, 0);
my $realMinFr   = shift || 0;
my $realMaxFr   = shift || 0;
my $stepfr   = shift || 0;
# 100 for WR, 401 for Agilent, 201 for FS300
# Number of diapasons - in automatic measurement
my $N    = shift || 100;

my $cf    = shift || 0;
my $rf    = shift || 0;
my $isInverted  = shift || 0; # 0=true
my $aqFreq  = shift || 0;
my $measureType  = shift || 0; # 0=automatic, 1=manual
my $request  = shift || ""; # other parameters
my $lockID  = shift || 0; # 0 - don't lock
###################################################################################

$request .= "&";

if($measureType == 0) {print "SCAN> ****** Start: SAID = $SAID, CMID = $RFportID ******\n";}

my $AppServIP  = "82.112.0.34"; # default IP
my $AppServPort  = 8080;     # default Port
my $appserv;
my $smonserv  = "82.112.0.37:80";
my $saveJSON = 0;
my $localaddress = "";

my $MAX_ANS_LEN  = 16384;    # 16 Kb

my $opt_user    = "satis";
my $opt_password = "satis";
my $database    = "satis";

my $conf="/usr/local/controller/scripts/scripts.conf";
open(CONF,$conf) or die "Can't open config file: $!";
my $confparam="";
my $confvalue;
while(<CONF>)
{
  chomp();
  ($confparam,$confvalue)=split(/=/);
  if(defined($confvalue)) {
    if($confparam eq "dbuser") {$opt_user=$confvalue;}
    elsif($confparam eq "dbpass") {$opt_password=$confvalue;}
    elsif($confparam eq "db") {$database=$confvalue;}
    elsif($confparam eq "appserv") {$appserv=$confvalue;}
    elsif($confparam eq "smonserv") {$smonserv=$confvalue;}
    elsif($confparam eq "saveJSON") {$saveJSON=$confvalue eq "yes" ? 1 : 0;}
    elsif($confparam eq "localaddress") {$localaddress=$confvalue;}
    else {}
  }
  else
  {
    if($measureType==0)  {print "SCAN> WARNING: split didn't find '=' in line\n";}
  }
}
close(CONF);
($AppServIP,$AppServPort)=split(/:/,$appserv);

my $i;
my $line;
my $st;
my $sth;
my $query;

my $dbh = Mysql->connect("", "$database", "$opt_user", "$opt_password") ||
 die "Can't connect\n";

# register lockID > 0
my $lockName = "locking$lockID";
my $registeredTime = time();
if($lockID > 0) #perform lock operations
{
  $query = "SELECT Value FROM SystemConf WHERE NodeID=$SAID AND Name='$lockName';";
  $st = Query $dbh $query or die "$Mysql::db_errstr";
  if($line = $st->fetchrow_arrayref)
  {
    $query = "UPDATE SystemConf SET Value='$registeredTime' WHERE NodeID=$SAID AND Name='$lockName';";
    Query $dbh $query or die "$Mysql::db_errstr";
  }
  else
  {
    $query = "INSERT INTO SystemConf(NodeID,Name,Value) VALUES($SAID,'$lockName','$registeredTime');";
    Query $dbh $query or die "$Mysql::db_errstr";
  }

  if($measureType==0) {printf "SCAN> lock name inserted";}
}

#--------------------------------
my $break = 0;
while($lockID > 0 && $break == 0)
{
  # get current lockID, by default lockID=0
  my $curLockID = 0;
  $query = "SELECT Value FROM SystemConf WHERE NodeID=$SAID AND Name='LockID';";
  $st = Query $dbh $query or die "$Mysql::db_errstr";
  if($line = $st->fetchrow_arrayref)
  {
    $curLockID = "$$line[0]";
    $query = "SELECT Value FROM SystemConf WHERE NodeID=$SAID AND Name='locking$curLockID';";
    $st = Query $dbh $query or die "$Mysql::db_errstr";
    if($line = $st->fetchrow_arrayref)
    {
      if($curLockID == $lockID)
      {
        # let continue
        $break = 1;
      }
    }
    else
    {
      $break = 0;

      # old lock, set current lockID to 0
      $curLockID = 0;
      $query = "UPDATE SystemConf SET Value='0' WHERE NodeID=$SAID AND Name='LockID';";
      Query $dbh $query or die "$Mysql::db_errstr";
    }
  }
  else
  {
    $curLockID = 0;
    $query = "INSERT INTO SystemConf(NodeID,Name,Value) VALUES($SAID,'LockID','0');";
    Query $dbh $query or die "$Mysql::db_errstr";
  }

  if($break == 0)
  {
    if($curLockID == 0 or $curLockID == $lockID)
    {
      # lock
      $query = "SELECT Value FROM SystemConf WHERE NodeID=$SAID AND Name='LockID';";
      $st = Query $dbh $query or die "$Mysql::db_errstr";
      if($line = $st->fetchrow_arrayref)
      {
        $query = "UPDATE SystemConf SET Value='$lockID' WHERE NodeID=$SAID AND Name='LockID';";
        Query $dbh $query or die "$Mysql::db_errstr";
      }
      else
      {
        $query = "INSERT INTO SystemConf(NodeID,Name,Value) VALUES($SAID,'LockID','$lockID');";
        Query $dbh $query or die "$Mysql::db_errstr";
      }
    }
    else
    {
      # wait unlock
      if(time() - $registeredTime > 60) # 1 min
      {
        # exit on timeout
        $query = "DELETE FROM SystemConf WHERE NodeID=$SAID AND Name='$lockName';";
        Query $dbh $query or die "$Mysql::db_errstr";
        exit(0);
      }

      $query = "SELECT Value FROM SystemConf WHERE NodeID=$SAID AND Name='$lockName';";
      $st = Query $dbh $query or die "$Mysql::db_errstr";
      if($line = $st->fetchrow_arrayref)
      {
      }
      else
      {
        # request has been canceled, exit
        $query = "DELETE FROM SystemConf WHERE NodeID=$SAID AND Name='$lockName';";
        Query $dbh $query or die "$Mysql::db_errstr";
        exit(0);
      }
    }

    sleep(1);
  }
}

#--------------------------------
my $ua= LWP::UserAgent->new;
@LWP::Protocol::http::EXTRA_SOCK_OPTS = (LocalAddr => $localaddress) if $localaddress ne "";
$ua->agent("Spectrum");

#--- Set socket ---
my $SocketPort = "1550"; # Min port value (default)
my $addr = inet_aton("localhost");
my $paddr;

#--- Get Socket Port ---
$query = "SELECT Value FROM SystemConf WHERE NodeID=$SAID AND Name='SocketPort';";
$st = Query $dbh $query or die "$Mysql::db_errstr";
if($line = $st->fetchrow_arrayref)
{
  $SocketPort = "$$line[0]";
  if($measureType==0) {print "SCAN> SocketPort = $SocketPort\n";}
}
else
{
  undef $st;
  undef $dbh;
  printf "ERROR: Server not running!\n";
  sleep(3);
  die "ERROR: Wrong parameters, failed to scan. Exit.\n";
}
$paddr = sockaddr_in($SocketPort, $addr);
undef $st;

#--- Default freq diapason ---
my $minfr = $realMinFr;
my $maxfr = $realMaxFr;

if($cf > 0 && $rf > 0)
{
#--- Convert frequencies from radio to middle diapason ---
  if($isInverted == 1) # Not inverted
  {
    $minfr = $cf+$realMinFr-$rf;
    $maxfr = $cf+$realMaxFr-$rf;
  }
  else
  {
    $minfr = $cf-$realMaxFr+$rf;
    $maxfr = $cf-$realMinFr+$rf;
  }

  if($measureType==0) {printf "SCAN> New freq diapason: ($realMinFr, $realMaxFr)->($minfr, $maxfr)\n";}
}

$minfr = $minfr + $aqFreq;
$maxfr = $maxfr + $aqFreq;

#--- Get SA port settings and it's Type ---
my $saparentid = 0;
my $SAType = "";
$query = "SELECT ParentID, Type FROM Nodes WHERE NodeID=$SAID";
$st = Query $dbh $query or die "$Mysql::db_errstr";
while($line = $st->fetchrow_arrayref)
{
  $saparentid = "$$line[0]";
  $SAType = $$line[1];
  $request .= "satype=$SAType&";
  if($SAType eq "HP")
  {
    $N = 401;
  }
  elsif($SAType eq "FS300")
  {
    $N = 401;
    $query = "SELECT Value FROM SystemConf WHERE NodeID=$SAID AND Name='address';";
    my $st2 = Query $dbh $query or die "$Mysql::db_errstr";
    while($line = $st2->fetchrow_arrayref)
    {
      my $Value  = "$$line[0]";
      $request .= "address=$Value&";
    }
    undef $st2;
  }
  elsif($SAType eq "Decimator")
  {
    $N = 1001;
    $query = "SELECT Name, Value FROM SystemConf WHERE NodeID=$SAID AND Name IN ('IPAddress', 'IPPort');";
    my $st2 = Query $dbh $query or die "$Mysql::db_errstr";
    while($line = $st2->fetchrow_arrayref)
    {
      my $Name  = "$$line[0]";
      my $Value = "$$line[1]";
      $request .= "$Name=$Value&";
    }
    undef $st2;
  }
}

#--- Get Analyzer port settings ---
$query = "SELECT Value, Name FROM SystemConf WHERE NodeID=$saparentid AND Name IN " .
  "('Number', 'Speed', 'Bits', 'Parity', 'StopBit')";
$st = Query $dbh $query or die "$Mysql::db_errstr";
while($line = $st->fetchrow_arrayref)
{
  if($$line[1] eq "Number") {$request .= "saport=$$line[0]&";}
  elsif($$line[1] eq "Speed") {$request .= "saspeed=$$line[0]&";}
  elsif($$line[1] eq "Bits") {$request .= "sabits=$$line[0]&";}
  elsif($$line[1] eq "Parity") {$request .= "saparity=$$line[0]&";}
  elsif($$line[1] eq "StopBit") {$request .= "sastopbit=$$line[0]&";}
}


#--- Get RF-Switch port settings ---
if($RFportID > 0)
{
  #--- Get RF Port value ---
  $query = "SELECT Value FROM SystemConf WHERE NodeID=$RFportID AND Name='port';";
  $st = Query $dbh $query or die "$Mysql::db_errstr";
  if($line = $st->fetchrow_arrayref) {$request .= "rfport=$$line[0]&";}

#  print "SCAN> RF Port number: $$line[0].\n";

  $query = "SELECT ParentID FROM Nodes WHERE NodeID=$RFportID";
  $st = Query $dbh $query or die "$Mysql::db_errstr";
  if($line = $st->fetchrow_arrayref)
  {
#    print "SCAN> Getting RF Switch parameters...\n";
    my $rfparentid;
    $query = "SELECT ParentID FROM Nodes WHERE NodeID=$$line[0]";  #$$line[0] is RF Switch ID.
    $sth = Query $dbh $query or die "$Mysql::db_errstr";
    while($line = $sth->fetchrow_arrayref)
    {
      $rfparentid = "$$line[0]";
    }
    $query = "SELECT Type FROM Nodes WHERE NodeID=$rfparentid";
    $sth = Query $dbh $query or die "$Mysql::db_errstr";
    while($line = $sth->fetchrow_arrayref)
    {
      $request .= "ctype=$$line[0]&";
    }

    $query = "SELECT Value, Name FROM SystemConf WHERE NodeID=$rfparentid AND Name IN " . 
      "('Number', 'Speed', 'Bits', 'Parity', 'StopBit')";
    $sth = Query $dbh $query or die "$Mysql::db_errstr";
    while($line = $sth->fetchrow_arrayref)
    {
      if($$line[1] eq "Number") {$request .= "cport=$$line[0]&";}
      elsif($$line[1] eq "Speed") {$request .= "cspeed=$$line[0]&";}
      elsif($$line[1] eq "Bits") {$request .= "cbits=$$line[0]&";}
      elsif($$line[1] eq "Parity") {$request .= "cparity=$$line[0]&";}
      elsif($$line[1] eq "StopBit") {$request .= "cstopbit=$$line[0]&";}
#      print "SCAN> RF Switch parameters: $$line[1] = $$line[0].\n";
    }
    
    $request .= "cactive=1&";
  }
  else {$request .= "cactive=0&";}
}
else {$request .= "cactive=0&";}

#Get saved Analyzer and Band Status
$query = "SELECT status, id FROM Alarms WHERE (id = $SAID OR id = $BID) AND raised = 1 AND time = 0";
$sth = Query $dbh $query or die "$Mysql::db_errstr";
while($line = $sth->fetchrow_arrayref)
{
  if($$line[1] eq $SAID) {$ASA[$$line[0] + 2] = 1;}
  if($$line[1] eq $BID) {$BSA[$$line[0] + 2] = 1;}
}

#--- Scan Freq Diapason ---
my $AnswerLength = 0;
my $Np = $N;
my $NPoints = 0;
if($stepfr > 0)
{
  $NPoints = int(($maxfr - $minfr) / $stepfr);
}
my $deltaN = $NPoints % $N;
my $Nc = 0;
if($Np > 0)
{
  $Nc = ($NPoints - $deltaN) / $Np + 1; # (($NPoints<$N)?1:0);
}
my $dsp = 0;
if($NPoints>0) 
{
  $dsp = ($maxfr - $minfr) / $NPoints; # new correct StepFreq value
}
my $sp = $dsp * $Np;

if($measureType==0) {print "SCAN> Num of cycles = $Nc, Np=$Np, NPoints=$NPoints, deltaN=$deltaN, dsp=$dsp, sp=$sp\n";}

if($sp > 0)
{
  my $ans = "";
  my $j = 0;
  for(; $j < $Nc and $Status == 0; $j++)
  {
    my $startTime = time();
    if($lockID == 0) # never exit
    {
      $break = 0;
      while($break == 0)
      {
        $query = "SELECT Value FROM SystemConf WHERE NodeID=$SAID AND Name='LockID';";
        $st = Query $dbh $query or die "$Mysql::db_errstr";
        if($line = $st->fetchrow_arrayref)
        {
          my $lock = "$$line[0]";
          if($lock == 0) {$break = 1;} #unlocked
          else
          {
            $query = "SELECT Value FROM SystemConf WHERE NodeID=$SAID AND Name='locking$lock';";
            $st = Query $dbh $query or die "$Mysql::db_errstr";
            if($line = $st->fetchrow_arrayref)
            {
              # timeout
              my $lastMeasureTime = "$$line[0]";
              if(time() - $lastMeasureTime > 60)
              {
                $query = "DELETE FROM SystemConf WHERE NodeID=$SAID AND Name='locking$lock';";
                 Query $dbh $query or die "$Mysql::db_errstr";
  
                 $query = "UPDATE SystemConf SET Value='0' WHERE NodeID=$SAID AND Name='LockID';";
                Query $dbh $query or die "$Mysql::db_errstr";
  
                $break = 1;
              }
            }
            else
            {
              #last request has been canceled
              $query = "UPDATE SystemConf SET Value='0' WHERE NodeID=$SAID AND Name='LockID';";
              Query $dbh $query or die "$Mysql::db_errstr";

              $break = 1;
            }
          }

          if($break == 0)
          {
            # analizer is still locked, wait 1 sec
            #print "SCAN> locked";
            sleep(1);
          }
        }
        else
        {
          # don't locked yet
          $query = "INSERT INTO SystemConf(NodeID,Name,Value) VALUES($SAID,'LockID','0');";
          Query $dbh $query or die "$Mysql::db_errstr";
          $break = 1;
        }
      }
    }
    else
    {
      $query = "SELECT Value FROM SystemConf WHERE NodeID=$SAID AND Name='$lockName';";
      $st = Query $dbh $query or die "$Mysql::db_errstr";
      if($line = $st->fetchrow_arrayref)
      {
        # update measure time
        my $measureTime = time();
        $query = "UPDATE SystemConf SET Value='$measureTime' WHERE NodeID=$SAID AND Name='$lockName';";
        Query $dbh $query or die "$Mysql::db_errstr";
      }
      else
      {
        # measure request has been canceled
        $query = "DELETE FROM SystemConf WHERE NodeID=$SAID AND Name='$lockName';";
        Query $dbh $query or die "$Mysql::db_errstr";
        exit(0);
      }
    }
  
    #--- measure ---
    my $InterAns = "";
    my $fa = $minfr + $j * $sp;
    my $fb;
    if($j == $Nc - 1)
    {
      $fb = $maxfr;
      $Np = int(($fb - $fa) / $dsp) + 1; # 1 + $NPoints - $j * $N;
    }
    else {$fb = $fa + $sp - $dsp;} # $minfr - $dsp + ($j + 1) * $sp;
  
    if($Np > 0)
    {
      my $isGoodMeasure = 0;
      my $try = 0;
  
     # try measure spectrum
      while($isGoodMeasure == 0 && $try < 3)
      {
        $try++;
        $fa = int$fa;
        $fb = int$fb;
        my $parameters = "&minfr=$fa&maxfr=$fb&numpoints=$Np&monitoring=$measureType&" . "$request";
  
        if($measureType == 0) {print "SCAN> CYCLE $j: fa=$fa, fb=$fb, Np=$Np\n";}
  
        #Sockets: get answer from spectrum analyzer
        socket(SOCK,PF_INET,SOCK_STREAM,getprotobyname('tcp'))
           or die "SCAN> Couldn't create socket! Error message:!$\n";
        connect(SOCK, $paddr) or die "SCAN> Can't connect: $!\n";
          send(SOCK, $parameters, 0);
  
        if($measureType == 0) {print "SCAN> Socket: request has been sent\n";}
  
        if(!defined(recv(SOCK, $InterAns, $MAX_ANS_LEN, 0))) {$InterAns = "ERROR";}
  
        if($measureType == 0) {print "SCAN> Socket: answer has been received\n";}
  
        close(SOCK);
  
        if(rindex($InterAns, "ERROR") >= 0)
        {
          if($measureType==0)  {print "SCAN> $InterAns\n";}
  
          if($try == 3)
           {
            $ans = $InterAns;
            $j = $Nc;
            #Detect error type
            if(rindex($InterAns, "error 2") >= 0) {
              $Status = -1;                  #No Answer
              print "SCAN> Error: no answer.\n";
            }
            else {
              $Status = -2;                  #Communication Error
              print "SCAN> Error: communication error.\n";
            }
            $alarms[$Status + 2] = 1;
          }
#          sleep(10);
        }
        else
        {
          # Split answers
          if($isInverted==1) {$ans = "$ans" . "$InterAns";}# not inverted
           else {$ans = "$InterAns" . "$ans";}
          $isGoodMeasure = 1;
        }
      } # End of trying
    } # End of number of points check
  } # End of Spectrum scaning
  
  if($measureType == 0)
  {
    my $time = time();
    for($i = 0; $i < 2; $i++) {
      if($alarms[$i] != $ASA[$i]){
        $query = "INSERT INTO Alarms(id, time, status, raised, sync) VALUES($SAID, $time*1000, $i - 2, $alarms[$i], -2)";
        Query $dbh $query or die "$Mysql::db_errstr";
        $query = "INSERT INTO Alarms(id, time, status, raised, sync) VALUES($SAID, 0, $i - 2, $alarms[$i], -1)" . 
          " ON DUPLICATE KEY UPDATE raised = $alarms[$i]";
        Query $dbh $query or die "$Mysql::db_errstr";
      }
      if($alarms[$i] != $BSA[$i]){
        $query = "INSERT INTO Alarms(id, time, status, raised, sync) VALUES($BID, $time*1000, $i - 2, $alarms[$i], -2)";
        Query $dbh $query or die "$Mysql::db_errstr";
        $query = "INSERT INTO Alarms(id, time, status, raised, sync) VALUES($BID, 0, $i - 2, $alarms[$i], -1)" . 
          " ON DUPLICATE KEY UPDATE raised = $alarms[$i]";
        Query $dbh $query or die "$Mysql::db_errstr";
      }
    }
    if($Status == 0)
    {
        #Send answer to the application server
      my @Spec = map(ord, split("", $ans));
    
      my $c;
      my $shortAns = "";
      my $checkSum = 0;
      for($c = 0; $c < 4*$NPoints; $c += 4) #$c points to current byte position, 4 bytes per point.
      {
        use integer;
        my $signal = 0;
        for($i = 0; $i < 4; $i++) {$signal += $Spec[$c + $i] << 8*$i;}
          $signal /= 100;
        $checkSum = $checkSum*31 + $signal;
        $checkSum %= 0xFFFF;
        $shortAns .= chr($signal & 0xFF) . chr(($signal >> 8) & 0xFF);
      }
    
      my $txt = "bandid=$BID&minf=$realMinFr&maxf=$realMaxFr&time=$time&type=$SAType&points=$NPoints&bits=16&checkSum=$checkSum";
      my $req = HTTP::Request->new("POST", "http://$AppServIP:$AppServPort/SpectrumServlet/spectrumservlet?$txt");
      $req->content_type('application/octet-stream');
      $req->content($shortAns);
      my $res = $ua->request($req);

      if($saveJSON)
      {
        print "SCAN> sending to $smonserv\n";
        my $req2 = HTTP::Request->new("POST", "http://$smonserv/SpectrumServlet/spectrumservlet?$txt");
        $req2->content_type('application/octet-stream');
        $req2->content($shortAns);
        my $res2 = $ua->request($req2);
      }

      if($res->is_success) {print "BAND> HTTP REQUEST:\n", $res->content, "\n";}
      else {print "BAND> HTTP REQUEST ERROR:\n", $res->error_as_HTML, "\n";}
  
      # Save spectrum to logical Data Base
      $query = "DELETE FROM lastspectrum WHERE bandid=$BID";
       Query $dbh $query or die "$Mysql::db_errstr";
   
      my $koef = ($realMaxFr - $realMinFr) / ($NPoints - 1);
      for($c = 0; $c < 4*$NPoints; $c += 4) #$c points to current byte position, 4 bytes per point.
      {
        #warning! negative signals are unsupported at the moment!
        my $freq = $realMinFr + $koef*($c/4);
        my $signal = 0;
        for($i = 0; $i < 4; $i++) {$signal += $Spec[$c + $i] << 8*$i;}
        $signal /= 1000;
      
        $query = "INSERT INTO lastspectrum(bandid,freq,sign) VALUES($BID,$freq,$signal)";
        Query $dbh $query or die "$Mysql::db_errstr";
      }
    } # Status is OK
  }
  else {print $ans;}  #Send answer for manual scan
}
else {print "SCAN> ERROR: Can't measure spectrum as minfr >= maxfr or npoints = 0.\n";}

undef $st;
undef $sth;
undef $dbh;

if($measureType==0) {print "SCAN> ************** END ********************\n";}

