/* **********************************************************************
 * *                                                                    *
 * *       Read TDWR tape and Send Data to test receiver                *
 * *                                                                    *
 * **********************************************************************
 * File: utils/tdwr/tdwr_sending_task.C
 *
 *                        COPYRIGHT (c) 2006  BY
 *              VAISALA INC., WESTFORD MASSACHUSETTS, U.S.A.
 * 
 * THIS SOFTWARE IS FURNISHED UNDER A LICENSE AND MAY BE USED AND  COPIED
 * ONLY  IN  ACCORDANCE WITH  THE  TERMS  OF  SUCH  LICENSE  AND WITH THE
 * INCLUSION OF THE ABOVE COPYRIGHT NOTICE.  THIS SOFTWARE  OR  ANY OTHER
 * COPIES  THEREOF MAY NOT BE PROVIDED OR OTHERWISE MADE AVAILABLE TO ANY
 * OTHER PERSON.  NO TITLE TO AND OWNERSHIP OF  THE  SOFTWARE  IS  HEREBY
 * TRANSFERED.
 */

#include <errno.h>
#include <fcntl.h>
#include <netdb.h>
#include <rpc/rpc.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#ifndef LINUX
#include <string.h>
#endif  /* #ifndef LINUX */
#include <sys/socket.h>
#include <unistd.h>

#ifndef LINUX
#include <arpa/nameser.h>  /* Required for BYTE_ORDER, MAXHOSTNAMELEN on HP and SGI, must be late */
#endif  /* #ifndef LINUX */


#include "sigtypes.h"
#include "user_lib.h"

#include "tdwr_interface.h"
#include "client.h"
#include "udp_pac.h"
#include "frag_pac.h"

#define DEBUG_ON (0)

static int process_data(void);
static void sighup_handler (int signal_a);
static void socket_open(void);

/* In file fragment.c */
static int send_Fragments
( int out_socket, unsigned short frag_options, unsigned char *mptr, int mbytes );

static void swap_frag_hdr
( struct frag_hdr *pHdr_a );

static void swap_tdwr_hdr
( struct tdwr_hdr *pHdr_a );

static void swap_buf_hdr
( struct buf_hdr *pHdr_a );

/* Global declarations */
CMD_ARGS      u_args;
UDP_DI        udp_di;

struct sigaction action;
int    sd;
int    answer;
int    iRayCount;
union  tdwr_buf frag_bfr;
unsigned char *msg_bfr;
int    logging = 0;
char   log_str[200];
FILE   *log_file;

int main( int argc, char **argv )
{
  int i, tape_fd;
  struct timeval tv;

  action.sa_handler = sighup_handler;
  sigemptyset( &action.sa_mask );
  action.sa_flags = 0;
  answer = sigaction( SIGHUP, &action, NULL );


  log_file = fopen("/tmp/tdwr_sending_task.log", "w");
  logging = 1;

  msg_bfr = (unsigned char *)&frag_bfr;

  socket_open();
 
#ifdef LINUX
  tape_fd = open("/dev/st1", O_RDONLY);
#else
  tape_fd = open("/dev/rmt/0m", O_RDONLY);
#endif  /* #ifdef LINUX */
  if (tape_fd <= 0) 
    {
      perror("Tape open failed");
      exit(0);
    }
  Lprintf("opened tape.");

  iRayCount=0;
  while (1)
    {
      struct tdwr_hdr TdwrHdr;
      tv.tv_sec = 0;
      tv.tv_usec = 30000;
      select(16, NULL, NULL, NULL, &tv);
      answer = read(tape_fd, &frag_bfr, 1024);
      if (answer <= 0) 
	{
#if DEBUG_ON
	  fprintf( stderr, "Read size %d\n", answer );
#endif  /* #if DEBUG_ON */
	  close(tape_fd);
	  exit(0);
	}
      TdwrHdr = frag_bfr.tdwr_hdr;
      swap_tdwr_hdr( &TdwrHdr );
      if ( (TdwrHdr.msg_id == 0x2B00) || (TdwrHdr.msg_id == 0x2B01) ) 
	{
	  for (i=1; i<=5; i++)
	    {
	      answer = read(tape_fd, msg_bfr+(i*1024), 1024);
	      if (answer <= 0) 
		{
		  close(tape_fd);
		  exit(0);
		}
	    }
	  process_data();
	}
      else
	{
#if DEBUG_ON
	  fprintf( stderr, "%4d: message ID:0x%04X\n", iRayCount, frag_bfr.tdwr_hdr.msg_id );
#endif  /* #if DEBUG_ON */
	}
      iRayCount++;
    }
}


/* ===============================================================================
 * Once we got a complete ray, if it is a tdwr data ray, and is either a signal dwell
 * or a sector blanked dwell, then inform the raw_create_task via ipc message queue.
 */
static int process_data(void)
{
  struct tdwr_hdr TdwrHdr;

  TdwrHdr = frag_bfr.tdwr_hdr;
  swap_tdwr_hdr( &TdwrHdr );

  if ((TdwrHdr.msg_id == 0x2B00) || (TdwrHdr.msg_id == 0x2B01))
    {
      struct buf_hdr BufHdr;
      
      BufHdr = frag_bfr.tdwr_buf_norm.hdr;
      swap_buf_hdr( &BufHdr );

      unsigned short idwell_id = (BufHdr.iFlags22&HDR_MSK22_DWELL_ID)>>HDR_SHFT22_DWELL_ID;
      if (!(BufHdr.iFlags6 & HDR_FLG6_DUMMY_RECORD) && 
	  ((idwell_id == 0) || (idwell_id == 2)) &&
	  ((BufHdr.scan_strategy == 1) || (BufHdr.scan_strategy == 2)))
	
	{
	  Lprintf("sending dwell.");
	  send_Fragments(sd, 0x0000, msg_bfr, 6144);
	}
    }
  return(0);
}

void Lprintf(const char *log_str_a)
{
  struct full_time_spec Time;
  char sTimeBuf[TIMENAME_SIZE];
  if (!logging) return;

  GetTimeFull( &Time, TIME_FLG_LOCAL );

  shhmmssddmonyyyy_r( &Time.Ymds, sTimeBuf );
  fprintf(log_file, "%s %s: %s\n", sTimeBuf, Time.sRecordTZName, log_str_a);
  fflush(log_file);
  return;
}

static void sighup_handler(int signal_a)
{
  action.sa_handler = sighup_handler;
  sigemptyset( &action.sa_mask );
  action.sa_flags = 0;
  sigaction( SIGHUP, &action, NULL );
}

static void socket_open(void)
{
  char     local_host[MAXHOSTNAMELEN], *lcl_host;
  struct   sockaddr_in addr;
  unsigned char *addr_ptr = (unsigned char *)&addr.sin_addr;
  unsigned short port_number;
  int      optvalue;
  struct   hostent *hp;
  //  struct   *gethostbyname();
  
  /* Get qid between task formulating ingest data and this task. */
  port_number = 10000;

  /* Open a UDP socket and set it up to support broadcast mode. */

  if ((sd = socket(PF_INET, SOCK_DGRAM, 0)) < 0)
  {
    sprintf(log_str, "Error acquiring socket: errno = %d",errno);
    Lprintf(log_str);
    exit(0);
  }
  
  optvalue = 1;
  if (setsockopt(sd, SOL_SOCKET, SO_BROADCAST, &optvalue, sizeof(optvalue)) < 0)
  {
    sprintf(log_str, "Error setting broadcast mode: errno = %d",errno); 
    Lprintf(log_str);
    exit(0);
  }
  
  /* Get the hostname of our machine and from that get the IP address. */
  /* This is done so we can determine the broadcast address for our */
  /* network.  We then will send the UDP data to this broadcast address. */

  if ( (gethostname(local_host, MAXHOSTNAMELEN)) < 0)
  {
    sprintf(log_str, "Error in determining local hostname: errno = %d",errno);
    Lprintf(log_str); 
    exit(0);
  }
  lcl_host = local_host;

  if (!(hp = gethostbyname(lcl_host)))
  {
    sprintf(log_str, "Error converting node name to host address");
    Lprintf(log_str); 
    exit(0);
  }
  else
  {
    memcpy(&addr.sin_addr, hp->h_addr, hp->h_length);
  }

  sprintf(log_str,"System IP address = %8X",addr.sin_addr.s_addr);
  Lprintf(log_str);
  *(addr_ptr + 3) = 0;
  *(addr_ptr + 2) = 76;
  sprintf(log_str,"Broadcast IP address = %8X",addr.sin_addr.s_addr);
  Lprintf(log_str);
  addr.sin_family = AF_INET;
  addr.sin_port = htons(port_number);
  
  /* Call connect to ready this socket for sending.  We will not block */
  /* on this connect call, as we are using a UDP socket, not TCP. */

  if ( (connect(sd, (struct sockaddr*)(void*)&addr, sizeof(addr))) < 0)
  {
    sprintf(log_str, "Error connecting: errno = %d",errno);
    Lprintf(log_str); 
    exit(0);
  }

}
/*
 *   Filename:     fragment.c
 *
 *   Description:  Functions related to fragmenting data.
 *                 Care was taken in this package to place NO restrictions
 *                 on the fragment size limit or the message size limit
 *                 (other than what system memory can fit).  Any halfword
 *                 or fullword restrictions must be implemented and
 *                 enforced by the caller.
 *                 
 *                 Written to compile under K&R ANSI-C
 *
 *   Contents:
 *     send_Fragments           Sends messages as fragments as two vectors
 *     count_Fragments          Returns fragment count for a message
 *
 *
 *====================================================================
 */


/* Fragmentation declarations */
static int frag_bytes = DEF_FRAGMENTATION_BYTES;
static int frag_data_bytes = DEF_FRAGMENTATION_BYTES - SIZEOF_FRAG_HDR;
extern FILE *enet_file, *error_file;

static int count_Fragments( int frag_b, int msg_b );

/*
 *
 *   Function    : send_Fragments
 *
 *   Description : Sends a message as vectors, to a socket
 *
 *   Imports     : out_socket, frag_options, mptr, mbytes
 *
 *   Exports     : 
 *
 *   Returns     : <0 error; otherwise, number of bytes sent
 *
 */
static int send_Fragments
( int out_socket, unsigned short frag_options, unsigned char *mptr, int mbytes )
{

  static struct frag_hdr fhdr = { 0L, 0, 0, 0, 0, 0, NULL_OPTION };
  static struct iovec iov[] = { { (caddr_t)&fhdr, SIZEOF_FRAG_HDR }, { 0, 0} };
  int msg_bytes_sent = 0;
  int bytes_remaining = mbytes;
  int data_bytes;
  size_t writev_bytes;
  
#define IOV_CNT ( sizeof(iov)/sizeof(struct iovec) )
  /* Fill the initial fragmentation header, these will not change among fragments */
  if( (fhdr.frag_count=count_Fragments( frag_bytes, mbytes)) <= 0 )
    {
#ifdef PRINT_ERRORS
      fprintf(stderr,"(send_Fragments) Bad argument(s), f_bytes=%d, m_bytes=%d\n", frag_bytes, mbytes);
      fflush( stderr );
#endif
      return( -1 );
    }
  fhdr.msg_bytes  = mbytes;
  fhdr.frag_nmbr  = 0;
  fhdr.options    = frag_options;
  
  /* Loop through the fragments */
  while( bytes_remaining > 0 )
    {
      /* Compute the pointer and length of the next fragment */
      data_bytes = fhdr.data_bytes = iov[1].iov_len =
	bytes_remaining>frag_data_bytes ? frag_data_bytes : bytes_remaining;
      
      /* Update the fragment header members */
      fhdr.frag_seqn++;
      fhdr.frag_nmbr++;
      fhdr.frag_offset = msg_bytes_sent;
      
      /* Define the data vector pointer */
      iov[1].iov_base = (caddr_t)&mptr[msg_bytes_sent];
      
      /* Send the fragment header and data fragment as vectors.
       * Fragment header sent as big endian.  Data from tape is
       * already big-endian. */
      swap_frag_hdr( &fhdr );
      writev_bytes=writev( out_socket, iov, IOV_CNT );
      swap_frag_hdr( &fhdr );
      if( ((signed int)writev_bytes) < 0 )  /* Make sure its signed */
	{
	  /* Be tolerant of nonexistent clients giving them time to startup and bind */
	  if( errno!=ECONNREFUSED )
	    {
#ifdef PRINT_ERRORS
	      fprintf(stderr,"(send_Fragments) Bad status sending fragment %d (%d+%d bytes), errno=%d\n",
		      fhdr.frag_nmbr, iov[0].iov_len, iov[1].iov_len, errno);
	      fflush( stderr );
#endif
	      return( -2 );
	    }
	}
      else if( writev_bytes != (iov[0].iov_len+iov[1].iov_len))
	{
#ifdef PRINT_ERRORS
	  fprintf(stderr,"(send_Fragments) Tried sending fragment %d (%d+%d bytes), only sent %d\n",
		  fhdr.frag_nmbr, iov[0].iov_len, iov[1].iov_len, writev_bytes);
	  fflush( stderr );
#endif
	  msg_bytes_sent = writev_bytes>iov[0].iov_len ? msg_bytes_sent+writev_bytes-iov[0].iov_len : msg_bytes_sent;
	  break;
	}
      /* Update the running counters */
      msg_bytes_sent += data_bytes;
      bytes_remaining -= data_bytes;
    }

#undef IOV_CNT
  
  return( msg_bytes_sent );
}


/*
 *
 *   Function    : count_Fragments
 *
 *   Description : Returns the fragment count associated with a given
 *                 fragment size and message data byte count.
 *
 *   Imports     : frag_b, msg_b
 *
 *   Exports     : 
 *
 *   Returns     : computed fragment count, consider 0 to be an error
 *
 */

static int count_Fragments( int frag_b, int msg_b )
{
  int fragments = 0;
  
  /* Check fragment and data length limits (bytes) */
  if( msg_b > 0 && msg_b <= MAX_MSG_BYTES &&
      (frag_b > (int)SIZEOF_FRAG_HDR) && (frag_b <= (int)MAX_FRAG_BYTES) )
    {
      /* Also reject too many fragments */
      if( (fragments=(msg_b+frag_b-SIZEOF_FRAG_HDR-1) / (frag_b-SIZEOF_FRAG_HDR)) > MAX_FRAGMENTS)
	fragments = 0;
    }
  
  return( fragments );
}

/* ============================================================
 * Swap the frag header if we are not big_endian
 */
static void swap_frag_hdr
( struct frag_hdr *pHdr_a )
{
  /* Only swap if required */
  if (BYTE_ORDER==BIG_ENDIAN) return;

  SwapU4( &pHdr_a->frag_seqn  );
  SwapU2( &pHdr_a->msg_bytes  );
  SwapU2( &pHdr_a->frag_count );
  SwapU2( &pHdr_a->frag_nmbr  );
  SwapU2( &pHdr_a->frag_offset);
  SwapU2( &pHdr_a->data_bytes );
  SwapU2( &pHdr_a->options    );

  return;

}

/* ============================================================
 * Swap the tdwr header if we are not big_endian.
 */
static void swap_tdwr_hdr
( struct tdwr_hdr *pHdr_a )
{
  /* Only swap if required */
  if (BYTE_ORDER==BIG_ENDIAN) return;

  SwapU2( &pHdr_a->msg_id  );
  SwapU2( &pHdr_a->msg_len );
  return;
}

/* ============================================================
 * Swap the buf header if not big-endian
 */
static void swap_buf_hdr
( struct buf_hdr *pHdr_a )
{
  /* Only swap if required */
  if (BYTE_ORDER==BIG_ENDIAN) return;

  SwapU2( &pHdr_a->volume_scan_count );
  // swap1( &pHdr_a->iFlags2        );  /* Single byte data does not need swapping */
  // swap1( &pHdr_a->scan_strategy  );
  SwapU2( &pHdr_a->peak_power        );
  SwapU2( &pHdr_a->iFlags6           );
  SwapU4( &pHdr_a->iFlags8           );

  SwapF4( &pHdr_a->current_elevation );
  SwapF4( &pHdr_a->angular_scan_rate );

  SwapU2( &pHdr_a->pri               );
  SwapU2( &pHdr_a->iFlags22          );
  SwapU2( &pHdr_a->final_range_sample);
  SwapU2( &pHdr_a->range_samples_per_dwell);

  SwapF4( &pHdr_a->current_azimuth   );
  SwapF4( &pHdr_a->total_noise_power );

  SwapS4( &pHdr_a->timestamp         );
  SwapU2( &pHdr_a->base_data_type    );
  SwapU2( &pHdr_a->iFlag42           );
  SwapU2( &pHdr_a->integer_azimuth   );
  SwapU2( &pHdr_a->load_short_final_sample);

  return;
}
