/* ======================================================================
**
** Neptune/Mars OBC Timing Serial Interface Program
** Written By: Steven Lerner/Al Bradley- 2/2005
**             Woods Hole Oceanographic Institution
**
** Usage: obc_timing [-p port 9600 N 8 1] [-it | [-c shore-clock] [-C node-clock(s)] [-TLSG] ] [-D]
** Examples:
**    View ShoreClkTime:  obc_timing -c#CKA                       [#CKA?T #CKA?L1]
**    View NodeClkTime:   obc_timing -c#CKA -C#C01A               [#C01A?T #CKA?L1]
**    Set ShoreClock Time: 
**                        obc_timing -S -Tddddhhmmss -c#CKA       [#CKA!T1234 hh mm ss@]
**    Set NodeClk Time from ShoreClk:
**                        obc_timing -S -c#CKA -C#C01A[,#C01B]    [#C01A!T#CKA?T ]
**   [CAN SET MULTIPLE NODE CLKS #C01A!T#C01B!T#CKA?T]
**    Get Latency RTT:    obc_timing -L -c#CKA -C#C01A            [#CKA!EC01A<cr> #CKA?L2]
**    Set Latency Offset: obc_timing -S -o0500 -C#C01A            [#C01A!O0500]
**    Set Output Relay:   obc_timing -S -R                        [#C01A!SR]
**    Global Sync:        obc_timing -G -c#CKA                    [#CKA!GS]
**   Use A AND MONITOR P UNTIL IT PEAKS, THEN USE !Q
**   GLOBAL SYNC ONLY GOOD TO NEAREST 8MS]
**       
**
** Description: Sail serial driver
**
** History:
**       Date       Who    Description
**      ----------  ---    --------------------------------------------
**      02/24/2005  SL/AB  Create from sail.c, updated sail timeout
**                         for timing bd
** ======================================================================
*/

/* standard ansi C header files */
#include <stdio.h>      /* for printf() and fprintf() */
#include <stdlib.h>     /* for atoi()   */
#include <string.h>     /* for memset() */
#include <unistd.h>     /* for close()  */
#include <errno.h>
#include <math.h>
#include <sys/time.h>

#include <sys/ioctl.h>
#include <fcntl.h>

#include <termios.h>
#include <time.h>
#include <unistd.h>
//Following not needed for windows/cygwin
//#include <term.h>
//#include <asm/io.h>
//#include <sys/perm.h>

#include "serial_io.h"
#include "debug.h"

extern int utime2dsltime_str(double time, char *str);

#define VERSION "02-24-05 v0.1"

#define MAX_BUF_SIZE 1024

int DEBUG = 0;
int vlevel = ERR; /* ERR, WNG, MSG, DBG DBG1, DGB2, DBG3 */
                  //  1    2    3    4   5     6     7

static void usage(char *str)
{
 printf ("\n%s\n",VERSION);
 printf("Usage: %s [-p port baud parity nbits sbits] [-it | [-v] [-c ShoreClockAddr] [-C NodeClockAddr] [-LSG]] [-Tdddd hh mm ss] [-D]\n",str);
 printf("Where:\n");
 printf("\t       -p port baud parity nbits sbits [Default: /dev/ttyS0 9600 N 8 1]\n");
 printf("\t       -i interactive mode\n");
 printf("\t       -t transparent mode\n");
 printf("\t       -v verify command only, don't send it\n");
 printf("\t       -c Shore Clock Address[Default: -c#CKA]\n");
 printf("\t       -C Node  Clock Address[Default: -C#C01A]\n");
 printf("\t       -S Set NodeTime, ShoreTime -T, Latency Offset -O, output Relay -R\n");
 printf("\t       -T \"dddd hh mm ss\"  [use with -S]\n");
 printf("\t       -O offset_amt (nnnn - microseconds) [use with -S] \n");
 printf("\t       -R set output relay [use with -S] \n");
 printf("\t       -L View Clock Latency\n");
 printf("\t       -A View P,X,I\n");
 printf("\t       -G Send Global Sync\n");
 printf("\t       -D debug 1-Err, 2-Wng, 3-Msg, 4-7 Dbg\n\n");
 }
 

/* Basic sail interface - sends command and returns reply */
int talk_sail(int fd, char *cmd_str, int cmd_len, char reply_str[], int timeout, int maxchar)
  {int n = 0; 
   int index = 0;
   write_to_port(fd, cmd_str, cmd_len, vlevel);
   /* Read char by char and stop if ETX, maxchar, or error */
   while (--maxchar > 0)
      {n = read(fd,&reply_str[index],1);
       if (n <= 0) {reply_str[index] = '\0'; return(n);}
       index++;
       /* We're done if ETX received */
       if (reply_str[index-1] == '\03') break;
       }
   reply_str[index] = '\0'; /* Null terminate */
   return(index);
   }

int translate_back(char *str, int len, char *out_str)
{  unsigned char ch;
   unsigned char next_ch;
   int new_len = 0;
   
   /* translate characters back: &P->#, &S->/, &C->[cr],&E->[etx], &A->& */
   while (--len >= 0)
      {ch = *str++; next_ch = *str;
       //Translate chars for sail - 
       if (ch == '&')
         {if (next_ch == 'P')      {*out_str++ = '#';}
          else if (next_ch == 'S') {*out_str++ = '/';}
	  else if (next_ch == 'E') {*out_str++ = '\03';}
	  else if (next_ch == 'A') {*out_str++ = '&';}
	  else if (next_ch == 'C') {*out_str++ = '\r';}
	  //Skip next char
	  str++; len--;
	  }
       else {*out_str++ = ch;}
       new_len++;
       }
   *out_str++ = '\0'; /* Null terminate */
   return(new_len);
} /* end translate_back */


void dump_sail_serial_buffer(int io_fd, char *sail_addr, int timeout, int maxchar)
{  char cmd_str[MAX_BUF_SIZE];
   char temp_str[MAX_BUF_SIZE];
   char reply_str[MAX_BUF_SIZE];
   char timestr[80];
   int cmd_len, ret;

   //Query sail device and output everything from buffer (translate back chars too)
   //Continue if any chars in buffer - ie; [cr][lf]+[etx] instead of [cr][lf]:[etx]
   //printf("Checking sail slave serial buffer...\n");
   sprintf(cmd_str,"%s.",sail_addr); cmd_len = strlen(cmd_str);
   ret = talk_sail(io_fd,cmd_str,cmd_len,temp_str,timeout,maxchar);
   ret = translate_back(temp_str,ret,reply_str);
   if (ret <= 0)
        {utime2dsltime_str(-1,timestr);
	 printf("%s Sail turned on? No chars from serial dev, cmdlen=%d[%s],ret=%d\n",timestr,strlen(cmd_str),cmd_str,ret);
	 }
   else if (ret <= cmd_len+4 && reply_str[ret-2] == ':')
        {utime2dsltime_str(-1,timestr);
	 if (DEBUG>=DBG1) printf("%s No chars returned slave serial device - reply=[%s] cmdlen=%d,ret=%d\n",timestr,reply_str,strlen(cmd_str),ret);
	 }
   else {if (ret > cmd_len+4) reply_str[ret-4] = '\0'; 
         printf("%s",reply_str+cmd_len); fflush(stdout);
	 }
   while (ret >= 2 && reply_str[ret-2] == '+')
      {ret = talk_sail(io_fd,cmd_str,cmd_len,temp_str,timeout,maxchar);
       ret = translate_back(temp_str,ret,reply_str);
       if (DEBUG>=DBG) {printf("reply=[%s], sail prompt is [%c]\n",reply_str,reply_str[ret-2]);}
       if (ret > cmd_len+4) {reply_str[ret-4] = '\0';}
                       else {reply_str[cmd_len] = '\0';}
       printf("%s",reply_str+cmd_len);  fflush(stdout);
       }

} /* end dump_sail_serial_buffer */


//---------------------------------------------------------------------------
// Main
//---------------------------------------------------------------------------
int main (int argc, char *argv[])
{
  //externs for getopt
  extern char *optarg;
  extern int optind, opterr, optopt;
  int c;
  int ret;

  io_cfg_t io;

  char *cmd_ptr;
  char cmd_str[MAX_BUF_SIZE];
  char reply_str[MAX_BUF_SIZE];
  int cmd_len;
  int done = 0;

  char *port_options = "/dev/ttyS0 9600 N 8 1";
  int timeout = 10; //Tenths of seconds
  int maxchar = MAX_BUF_SIZE;

  char timestr[80];
  unsigned char ch;
  
  int interactive_mode  = 0;
  int transparent_mode  = 0;
  int command_mode      = 1;
  int set_flag          = 0;
  int global_sync_flag  = 0;
  int latency_flag      = 0;
  int offset_flag       = 0;
  int set_relay_flag    = 0;
  int view_pxi_flag     = 0;
  int set_time_flag     = 0;
  int verify_only_flag  = 0;
  char *shore_clock_addr = "#CKA";
  char *node_clock_addr  = "#C01A";
  char *date_time_str    = "0000 00 00 00";
  char *offset_str       = "0000";
    
  //Get args and check usage
  if (argc <= 1) {usage(argv[0]); exit(1);}
  while((c = getopt(argc,argv,"itvLSGRAc:C:T:p:O:D:")) != -1)
    {
    switch (c)
       {
       case 'p': port_options = strdup(optarg); 
                 break;
       case 'i': interactive_mode = 1; command_mode = 0;
                 break;
       case 't': transparent_mode = 1; command_mode = 0;
                 break;
       case 'v': verify_only_flag = 1;
                 break;
       case 'c': shore_clock_addr = strdup(optarg); 
                 break;
       case 'C': node_clock_addr = strdup(optarg); 
                 break;
       case 'T': date_time_str = strdup(optarg); 
                 set_time_flag = 1;
                 break;
       case 'L': latency_flag = 1;
                 break;
       case 'O': offset_flag = 1;
                 offset_str = strdup(optarg);
                 break;
       case 'R': set_relay_flag = 1;
                 break;
       case 'A': view_pxi_flag = 1;
                 break;
       case 'G': global_sync_flag = 1;
                 break;
       case 'S': set_flag = 1;
                 break;
       case 'D': vlevel = atoi(optarg);
                 if (vlevel >= MSG) DEBUG = vlevel;
                 break;
	    default:  usage(argv[0]);
	          exit(-1);
	    }
    }

  //initialize io structure and set to specified options
  init_io_cfg(&io);
  //port options: port baud parity nbits sbits
  sscanf(port_options,"%s %d %c %d %d",io.port_str,&io.baud,&io.parity,&io.nbits,&io.sbits);
  io.port = strdup(io.port_str);

  /*---------open and configure serial port for Sail --------------*/
  io.block     = 1;       /* 0 non-blocking, 1-blocking (w/timeout val) */
  io.canonical = 0;       /* 0 non-canoncial */
  // Set char timeout - valid only in non-canonical mode
  io.vtime     = timeout; /* time to wait - tenths of a second */
  io.vmin      = 0;       /* blocking read until at least n chars rcvd */
  io.vlevel    = vlevel;  /* ERR, WNG, MSG, DBG1, DGB2, DBG3 */
  
  if (!open_io_port(&io))
    {fprintf(stderr, "***Unable to open IO port [%s] -- exiting\n",io.port_str);
     return 0;
     }

  DPRINTF(MSG,vlevel,fprintf(stderr,"\tOpened Sail Port: %s %d %c %d %d\n",
	                     io.port, io.baud, io.parity, io.nbits, io.sbits););

  //------------------------------------------------------------------------
  //INTERACTIVE MODE
  //------------------------------------------------------------------------
  //Loop forever and talk sail interactive mode - send a line to sail
  if (interactive_mode)
    {int stdin_fd;
     stdin_fd = fileno(stdin);
     printf("INTERACTIVE MODE\n");
     while (!done)
      {//Read command_str from stdin
       cmd_ptr = &cmd_str[0]; ch = '\0';
       while (ch != '\n')
         {read(stdin_fd,&ch,1);
          if (ch != '\n') *cmd_ptr++ = ch;
	  }
       *cmd_ptr = '\0'; //null-terminate cmd str
       cmd_len = strlen(cmd_str);
       if (DEBUG) {utime2dsltime_str(-1,timestr);
                   printf("%s SENT: %s\n",timestr,cmd_str);
		   }
       ret = talk_sail(io.port_fd,cmd_str,cmd_len,reply_str,timeout,maxchar);
       if (DEBUG)
          {utime2dsltime_str(-1,timestr);
	   if (ret > 0) {printf("%s REPLY: %s\n",timestr,reply_str);}
                   else {printf("%s ***Sail error, return=%d\n",timestr,ret);}
	   }
       else {printf("%s\n",reply_str);}
      }
    } //end-if interactive mode
  //------------------------------------------------------------------------


  //------------------------------------------------------------------------
  //TRANSPARENT MODE
  //------------------------------------------------------------------------
  //Loop forever and talk sail transparently - ie; read char, send it to sail
  if (transparent_mode)
    {int stdin_fd;
     struct termios tio;
     printf("TRANSPARENT MODE\n");
     stdin_fd = fileno(stdin);
     //Get current options for the port and make non-buffering
     tcgetattr(stdin_fd, &tio);
     //Set char timeout - valid only in non-canonical mode
     tio.c_cc[VTIME] = 1;  /* inter-character timer */
     tio.c_cc[VMIN]  = 0;  /* blocking read until at least n chars rcvd */
     tio.c_lflag &= ~(ICANON | ECHO | ECHOE);
     //May want to have no echos, ignore signals...
     //tio.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
     tcsetattr(stdin_fd, TCSANOW, &tio); //Set the attributes
     fcntl(stdin_fd,F_SETFL,FNDELAY);    //Set non-blocking
     while (!done)
      {//Read a char and send it to sail
       if (read(stdin_fd,&ch,1) > 0) {write_to_port(io.port_fd, &ch, 1, vlevel);}
      /* Read from sail */
      if (read(io.port_fd,&ch,1) > 0) {printf("%c",ch); fflush(stdout);}
      }
    } //end-if transparent mode
  //------------------------------------------------------------------------



  //------------------------------------------------------------------------
  //COMMAND MODE
  //------------------------------------------------------------------------
  /*
  **  View ShoreClkTime:  obc_timing -c#CKA                       [#CKA?T #CKA?L1]
  **  View NodeClkTime:   obc_timing -c#CKA -C#C01A               [#C01A?T #CKA?L1]
  **  Set ShoreClock Time: 
  **                      obc_timing -S -T"dddd hh mm ss" -c#CKA  [#CKA!T1234 hh mm ss@]
  **  Set NodeClk Time from ShoreClk:
  **                      obc_timing -S -c#CKA -C#C01A[,#C01B]    [#C01A!T#CKA?T ]
  ** [CAN SET MULTIPLE NODE CLKS #C01A!T#C01B!T#CKA?T]
  **  Get Latency RTT:    obc_timing -L -c#CKA -C#C01A            [#CKA!EC01A<cr> #CKA?L2]
  **  Set Latency Offset: obc_timing -S -O0500 -C#C01A            [#C01A!O0500]
  **  Set Output Relay:   obc_timing -S -R                        [#C01A!SR]
  **  Global Sync:        obc_timing -G -c#CKA                    [#CKA!GS]
  */
  //Build OBC Timing cmd and send via sail
  if (command_mode)
    {int stdin_fd;
     stdin_fd = fileno(stdin);
     if (DEBUG) printf("COMMAND MODE\n");
     //If set flag set
     if (set_flag)
       {if (set_time_flag)       sprintf(cmd_str,"%s!T%s@",shore_clock_addr,date_time_str);
        else if (offset_flag)    sprintf(cmd_str,"%s!O%s\r",node_clock_addr,offset_str);
        else if (set_relay_flag) sprintf(cmd_str,"%s!SR",node_clock_addr);
        else sprintf(cmd_str,"%s!T%s?T",node_clock_addr,shore_clock_addr);
        }
     else 
       {if (global_sync_flag)      sprintf(cmd_str,"%s!GS",shore_clock_addr);
        //else if (latency_flag)     sprintf(cmd_str,"%s!E%s\r%s?L2",shore_clock_addr,&node_clock_addr[1],shore_clock_addr);
        else if (latency_flag)     
              {//break command into 2 sequences - !E followed by ?L2
               sprintf(cmd_str,"%s!E%s\r",shore_clock_addr,&node_clock_addr[1]);
               if (DEBUG) {utime2dsltime_str(-1,timestr);
                           printf("%s SENT: %s\n",timestr,cmd_str);
	                   }
               ret = talk_sail(io.port_fd,cmd_str,strlen(cmd_str),reply_str,timeout,maxchar);
               usleep(1000); //wait a smidge
               sprintf(cmd_str,"%s?L2",shore_clock_addr);
               }
        else if (view_pxi_flag)  sprintf(cmd_str,"%sA",node_clock_addr);
        else if (offset_flag)    sprintf(cmd_str,"%s?O",node_clock_addr);
        else {//break command into 2 sequences - ?T followed by ?L1
              sprintf(cmd_str,"%s?T",node_clock_addr);
              if (DEBUG) {utime2dsltime_str(-1,timestr);
                           printf("%s SENT: %s\n",timestr,cmd_str);
	                   }
              ret = talk_sail(io.port_fd,cmd_str,strlen(cmd_str),reply_str,timeout,maxchar);
              printf("%s\n",reply_str);
              usleep(1000); //wait a smidge
              sprintf(cmd_str,"%s?L1",shore_clock_addr);
              }
        }
     cmd_len = strlen(cmd_str);
     if (verify_only_flag) {printf("Verify Cmd: %s\n",cmd_str); exit(1);}
     if (DEBUG) {utime2dsltime_str(-1,timestr);
                 printf("%s SENT: %s\n",timestr,cmd_str);
	         }
     ret = talk_sail(io.port_fd,cmd_str,cmd_len,reply_str,timeout,maxchar);
     if (DEBUG)
          {utime2dsltime_str(-1,timestr);
	   if (ret > 0) {printf("%s REPLY: %s\n",timestr,reply_str);}
                   else {printf("%s ***Sail error, return=%d\n",timestr,ret);}
	   }
     else {printf("%s\n",reply_str);}
    } //end-if command mode



  close_io_port(&io);
  DPRINTF(MSG,vlevel,printf("%s: done - exiting...\n",argv[0]));

  return 0;

} /* End Main() - Sail */



 
