#!/usr/bin/env python
usage="""
Send and receive message from/to Septentrio receiver
"""

import socket
import time
import ftplib
import wget
import urllib

BYTE_LEN = 4096 #256
sept_id = '10.1.141.98'
sept_port = 28784
sept_ftp_hostname = 'asterx-u-3010195'
sept_ftp_path = 'DSK1/SSN/SSRC7'

class Septentrio_Socket:
  """\
  Wrapper to help send/recv commands to Septentrio"""
  def __init__(self, IPAddr, port, timeout=10):
    """\
    IPAddr = receiver address, e.g., '10.1.141.98'
    port = receiver port, e.g., 28784
    """
    self.IPAddr = IPAddr
    self.port = port
    self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    self.sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
    self.sock.settimeout(timeout)
    self.sock.connect( (IPAddr,port) )
    self.prompt_descriptor = self.recv_only().decode("utf-8")
      
  # The receiver is case insensitive when interpreting a command line.
  # The maximum length of any ASCII command line is 2000 characters.
  # The comma "," must be used to separate the arguments from each other 
  # and from the command’s name. Any number of spaces can be inserted 
  # before and after the comma.
  def send_recv( self, cmd):
    """Returns data from command."""
    self.sock.sendall(cmd)
    return bytearray(self.sock.recv(BYTE_LEN))

  def recv_only(self, nbytes=BYTE_LEN):
    """Grab any data bytes returned to the port."""
    return bytearray(self.sock.recv(nbytes))

  def close(self,sleep_time=0.5):
    """Close socket connection"""
    if self.sock is not None:
      self.sock.shutdown(socket.SHUT_RDWR)
      self.sock.close()
      time.sleep(sleep_time) # make sure socket has time to close
    self.sock = None

  def __del__(self):
    """Destructor"""
    self.close()

  def _print(self, msg):
    """Print prompt descriptor with message"""
    print(self.prompt_descriptor)
    print(msg)

  def soft_reset(self):
    """Soft reset"""
    cmd = 'exeResetReceiver,Soft,none\n'
    recv = self.send_recv(str.encode(cmd)).decode("utf-8")
    self._print( recv )

  def list_current_user(self):
    """List current username"""
    cmd = 'lstCurrentUser\n'
    recv = self.send_recv(str.encode(cmd)).decode("utf-8")
    return recv

  def list_hostname(self):
    """List IP parameter including hostname for FTP"""
    cmd='lstInternalFile,IPParameters\n'
    recv = self.send_recv(str.encode(cmd)).decode("utf-8")
    return recv

  def list_dir(self):
    """List files under Disk1"""
    cmd = 'lstDiskInfo,DSK1\n'
    recv = self.send_recv(str.encode(cmd)).decode("utf-8")
    return recv

  def set_logging_filename(self, filename='log'):
    """Set log file name"""
    cmd = 'setFileNaming, DSK1, FileName, ' + filename + '\n'
    recv = self.send_recv(str.encode(cmd)).decode("utf-8")
    return recv

  def get_logging_filename(self):
    """Get log file name"""
    cmd = 'getFileNaming, DSK1\n'
    recv = self.send_recv(str.encode(cmd)).decode("utf-8")
    return recv

  def set_data_in_out(self, port_id='IPR1', input_fmt='auto', output_fmt='none'):
    """
    Use these commands to define/inquire the type of data that
    the receiver should accept/send on a given connection descriptor
    eg.
    set_data_in_out('IPR1','auto','none')
    """
    cmd = 'setDataInOut,'+port_id+','+input_fmt+','+output_fmt+'\n'
    recv = self.send_recv(str.encode(cmd)).decode("utf-8")
    return recv

  def enable_logging(self, output_format='+SBF+NMEA'):
    """Turn on logging for SBF/NMEA"""

    # For SBF/NMEA
    # By default, pressing the log button has the effect of executing the commands sdio,DSK1„SBF+NMEA and sdio,DSK1„none in turn: it toggles internal SBF and NMEA logging on
    # Start the logging SBF/NMEA by enabling SBF and NMEA output to the DSK1 connection (it is enabled by default): setDataInOut,DSK1, ,+SBF+NMEA <CR>
    # Stop logging by invoking: setDataInOut,DSK1, ,-SBF-NMEA <CR>
    #
    # For RINEX:
    # cmd "srxl, DSK1, hour24, sec30, GPSL1CA" starts logging RINEX(Receiver Independent Exchange Format), create zero size file eg. DSK1/20276/sept2760.20oA file with 0.0KB
    # cmd "srxl, DSK1, none" will stop logging RINEX, and dump data to eg. DSK1/20276/sept2760.20o file with 1.8KB

    return self.set_data_in_out('DSK1',' ',output_format)

  def configure_output(self):
    """Configure SBF logging output"""
    cmd='setSBFOutput, Stream1, DSK1, MeasEpoch+MeasExtra+EndOfMeas+OutputLink+GPSRawCA+GPSRawL2C+GPSRawL5+GLORawCA+GALRawFNAV+GALRawINAV+GALRawCNAV+GEORawL1+GEORawL5+GPSNav+GPSAlm+GPSIon+GPSUtc+GLONav+GLOAlm+GLOTime+AuxAntPositions+GALNav+GALAlm+GALIon+GALUtc+GALGstGps+GALSARRLM+AttEuler+GEONav+GEOAlm+BaseVectorGeod+PVTGeodetic+PosCovGeodetic+DOP+EndOfPVT+ExtEvent+DiffCorrIn+BaseStation+InputLink+ChannelStatus+ReceiverStatus+ReceiverSetup+Commands+Comment+BDSRaw+LBandTrackerStatus+LBAS1DecoderStatus+IPStatus+QZSRawL1CA+QZSRawL2C+QZSRawL5+PVTSupport+LBandBeams+BDSNav+QualityInd+NTRIPClientStatus+WiFiAPStatus+CellularStatus+BluetoothStatus+RxComponents+DiskStatus+UHFStatus+RFStatus+IRNSSRaw+QZSNav+WiFiClientStatus+RxMessage+DynDNSStatus+SystemInfo+PVTSupportA+BBSamples, msec100\n'
    recv = self.send_recv(str.encode(cmd)).decode("utf-8")
    return recv

  def send_echo_message(self, port='COM1', msg="'A:Hello world!'"):
    """send a message to one of the connections of the receiver"""
    cmd = 'eecm,'+port+','+msg+','+'none'+'\n'
    print('cmd:', cmd)
    recv = self.send_recv(str.encode(cmd)).decode("utf-8")
    return recv

  def get_logging_setting(self):
    """Get logging settings"""
    cmd = 'getDataInOut,DSK1\n'
    recv = self.send_recv(str.encode(cmd)).decode("utf-8")
    return recv

  def disable_logging(self):
    """Turn off logging"""
    cmd = 'setDataInOut,DSK1, ,-SBF-NMEA\n'
    recv = self.send_recv(str.encode(cmd)).decode("utf-8")
    return recv

  def remove_file(self,filename='log.sbf'):
    """Delete log file on Disk"""
    cmd='exeRemoveFile,DSK1,'+filename+'\n'
    recv = self.send_recv(str.encode(cmd)).decode("utf-8")
    return recv

  def list_ftp_files(self, ftp_hostname = 'asterx-u-3010195', path = "DSK1/SSN/SSRC7"):
    """List logged files on receiver through Septentrio receiver's FTP server
    Default file path is DSK1/SSN/SSRC7
    """
    files = []
    try:
      ftp = ftplib.FTP(ftp_hostname)
      ftp.login()
      try:
        files = ftp.nlst(path)
      except:
        raise RuntimeError("Error in reading files from FTP")
      ftp.quit()
    except:
      raise RuntimeError("FTP hostname does not exist.")
    return files

  def download_file_ftp(self, filename = "log.sbf", ftp_hostname = 'asterx-u-3010195', path = "DSK1/SSN/SSRC7"):
    """Download file from Septentrio receiver through its ftp server using wget
    Default file path is DSK1/SSN/SSRC7
    """
    file_loc = 'ftp://'+ftp_hostname+'/'+path+'/'+filename
    print("Downloading", file_loc)
    try:
      wget.download(file_loc)
      return True
    except:
      print("Error in downloading file "+file_loc)
      return False

  def set_notch_filter(self, mode='off'):
    """
    Turn on/off notch filter
    Notch filters are used to cancel narrowband interferences.
    cmd: snf, Notch1, manual, 1227, 30
    Inputs:
      mode = string: auto, off, (manual)
    """
    cmd = 'setNotchFiltering, all, '+mode+'\n'
    recv = self.send_recv(str.encode(cmd)).decode("utf-8")
    return recv
  
  def get_notch_filter(self, notch_n='Notch1'):
    """
    Get notch filter status on/off
    """
    cmd = 'getNotchFiltering, '+notch_n+'\n'
    recv = self.send_recv(str.encode(cmd)).decode("utf-8")
    return recv

  def set_WBI_mitigation(self, mode='off'):
    """
    Enable or disable the mitigation of wideband interferences
    """
    cmd = 'setWBIMitigation, '+mode+'\n'
    recv = self.send_recv(str.encode(cmd)).decode("utf-8")
    return recv
  
  def get_WBI_mitigation(self):
    """
    Get setting
    """
    cmd = 'getWBIMitigation\n'
    recv = self.send_recv(str.encode(cmd)).decode("utf-8")
    return recv

  def set_ip_receive(self,ip_receve='IPR1',port=5017,tcp_addr='10.1.140.124'):
    """
    Set an IPS connection
    Set port=0 to disable an IPS connection
    """
    cmd = 'setIPReceiveSettings,'+ip_receve+','+str(port)+',TCP,'+tcp_addr+'\n'
    recv = self.send_recv(str.encode(cmd)).decode("utf-8")
    return recv

  def get_ip_receive(self,ip_receve='IPR1'):
    cmd = 'getIPReceiveSettings,'+ip_receve+'\n'
    recv = self.send_recv(str.encode(cmd)).decode("utf-8")
    return recv

  def set_pvt_mode(self, mode='Static',rover_mode='RTK',static_pos='auto'):
    cmd = 'setPVTMode,'+mode+','+rover_mode+','+static_pos+',off\n'
    recv = self.send_recv(str.encode(cmd)).decode("utf-8")
    return recv

  def get_pvt_mode(self):
    cmd = 'getPVTMode\n'
    recv = self.send_recv(str.encode(cmd)).decode("utf-8")
    return recv

  def set_BBSampling_mode(self, mode='BeforeIM'):
    cmd = 'setBBSamplingMode,'+mode+'\n'
    recv = self.send_recv(str.encode(cmd)).decode("utf-8")
    return recv

  def get_BBSampling_mode(self):
    cmd = 'getBBSamplingMode\n'
    recv = self.send_recv(str.encode(cmd)).decode("utf-8")
    return recv

if __name__ == "__main__":

  import argparse

  parser = argparse.ArgumentParser(formatter_class=argparse.RawTextHelpFormatter,
                                    description=usage)
  parser.add_argument('-t','--test',
                      help='If run routine receiver reset for Anti Jam Test',
                      action="store_true")
  parser.add_argument('-i','--info',
                      help='Show receiver info',
                      action="store_true")
  parser.add_argument('-l','--test_logging',
                      help='If test logging on receiver',
                      action="store_true")
  parser.add_argument('-d','--test_download',
                      help='If test downloading on receiver',
                      action="store_true")
  parser.add_argument('-r','--test_rtk',
                      help='If run rtk mode on receiver',
                      action="store_true")
  
  args = parser.parse_args()
  


  test_logging = False
  test_download = False
  test_rtk = False

  if args.info:
    Sept = Septentrio_Socket(sept_id, sept_port)

    print('-----------------------------------------')
    rt = Sept.list_dir()
    print(rt)
    time.sleep(1)

    print('-----------------------------------------')
    rt = Sept.list_ftp_files()
    print(rt)

    print('-----------------------------------------')
    rt = Sept.get_logging_filename()
    print(rt)

    print('-----------------------------------------')
    rt = Sept.get_logging_setting()
    print(rt)

    print('-----------------------------------------')
    rt = Sept.get_pvt_mode()
    print(rt)

    print('-----------------------------------------')
    rt = Sept.get_BBSampling_mode()
    print(rt)

    del(Sept)

  if args.test:

    Sept = Septentrio_Socket(sept_id, sept_port)

    print('-----------------------------------------')
    rt = Sept.list_hostname()
    print(rt)

    print('-----------------------------------------')
    rt = Sept.list_dir()
    print(rt)

    print('-----------------------------------------')
    rt = Sept.list_ftp_files()
    print(type(rt))
    print(rt)

    print('-----------------------------------------')
    rt = Sept.get_logging_filename()
    print(rt)

    print('-----------------------------------------')
    rt = Sept.get_logging_setting()
    print(rt)


    print('-----------------------------------------')
    print('-----------------------------------------')
    rt = Sept.set_notch_filter('auto')
    print("Set filter type", rt)

    print('-----------------------------------------')
    rt = Sept.get_notch_filter()
    print("Get filter type", rt)

    print('-----------------------------------------')
    rt = Sept.set_WBI_mitigation('on')
    print("set_WBI_mitigation:", rt)

    print('-----------------------------------------')
    rt = Sept.get_WBI_mitigation()
    print("get_WBI_mitigation ", rt)


    print('-----------------------------------------')
    print('-----------------------------------------')
    rt = Sept.get_ip_receive(ip_receve='IPR1')
    print( rt )
    rt = Sept.set_ip_receive(ip_receve='IPR1',port=5017,tcp_addr='10.1.140.124')
    print( rt )
    rt = Sept.get_ip_receive(ip_receve='IPR1')
    print( rt )

    print('-----------------------------------------')
    rt = Sept.get_ip_receive(ip_receve='IPR1')
    print( rt )
    rt = Sept.set_pvt_mode(mode='Static',rover_mode='RTK',static_pos='auto')
    print( rt )

    del(Sept)


  if args.test_rtk:

    Sept = Septentrio_Socket(sept_id, sept_port)
    
    print('-----------------------------------------')
    print('-----------------------------------------')
    rt = Sept.get_ip_receive(ip_receve='IPR1')
    print( rt )

    print('-----------------------------------------')
    rt = Sept.set_pvt_mode(mode='Static',rover_mode='RTK',static_pos='auto')
    print( rt )
    
    print('-----------------------------------------')
    rtk_source_port = 5017
    rtk_source_ip_address = '10.1.140.124'
    rt = Sept.set_ip_receive(ip_receve='IPR1', port=rtk_source_port, tcp_addr=rtk_source_ip_address)  
    print( rt )
    rt = Sept.set_pvt_mode(mode='Rover',rover_mode='RTK',static_pos='auto')
    print( rt )
    
    print("Sleep 5 sec")
    time.sleep(5)
    rt = Sept.set_data_in_out('IPR1','none','none')
    print( rt )

    print("Sleep 5 sec")
    time.sleep(5)
    rt = Sept.set_data_in_out('IPR1','auto','none')
    print( rt )

    del(Sept)

  if args.test_logging:

    print('-----------------------------------------')
    print('-----------------------------------------')
    Sept = Septentrio_Socket(sept_id, sept_port)
    
    print('-----------------------------------------')
    rt = Sept.set_logging_filename('test')
    print(rt)

    print('-----------------------------------------')
    rt = Sept.enable_logging()
    print(rt)

    print('-----------------------------------------')
    rt = Sept.get_logging_setting()
    print(rt)

    print('-----------------------------------------')
    print('Sleep 15 sec ...')
    time.sleep(15)
    print('Sleep 15 sec. Done')

    print('-----------------------------------------')
    rt = Sept.disable_logging()
    print(rt)

    print('-----------------------------------------')
    rt = Sept.get_logging_setting()
    print(rt)  

    del(Sept)
  
  if args.test_download:

    Sept = Septentrio_Socket(sept_id, sept_port)
    print("Turn off logging. %s" % sept_id)
    Sept.disable_logging()

    rt = Sept.list_ftp_files()
    if len(rt) > 0:
      sbf_filename = rt[0] # "log.sbf"
      print("Download file from ftp. %s %s" % (sept_ftp_hostname, sept_ftp_path))
      rt = Sept.download_file_ftp(filename = sbf_filename, ftp_hostname = sept_ftp_hostname, path = sept_ftp_path)
      if rt == True:
        print("\nDownloading files succeed.")
      else:
        print("\nDownloading files failed.")
    else:
      print("No file to download.")

    del Sept

    # convert sbf to T04
    # sbf2t0x can be built from the t01 checkout. Only the range measurements and position records are currently converted.
    # >>/home/rxu/repo/repo_/t01/build64/sbf2t0x/sbf2t0x log.sbf log.T04
    # Use ‘viewdat -d35:19 -mb <T04> > <Matlab file>’ to output the range measurements to be loadable into Matlab.




