/* **********************************************************************
 * *	                                                                *
 * *                Real Time Display Transmitter                       *
 * *                                                                    *
 * **********************************************************************
 * File: libs/rtq/RtdSupport.C
 *
 *  COPYRIGHT (c) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003 BY
 *         SIGMET INCORPORATED, 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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>  /* place after netinet/in.h on some platforma */
#include <unistd.h>

#include "sigtypes.h"
#include "setup.h"
#include "sig_rtdisp.h"

#include "user_lib.h"
#include "rtq_lib.h"

#include "RtdSupport_h.h"

//#define DEBUG_ON 1
/* ======================================================
 * Runs the Real-Time display transmitt process as long as it
 * remains on.
 */
MESSAGE RtdXmtOn
( volatile struct rtdisp_control *pRtqGlobal_a,
  SINT4 (*ReadPacketProc_a)( UINT1 **pBuffer, int iChannel ),
  int iChannel_a )  /* Channel number, origin 0 */
{
  MESSAGE  istatus;
  int      ierror;
  UINT1    *pBuffer;
  UINT4    iCurrentBTime, iRestartBTime;
  int      itx_bytes;
  volatile const struct rtdisp_setup *pRSetup = &(pRtqGlobal_a->RSetup);
  char     sMessage[80];

  struct ChannelInfo
  {
    UINT1 lWorking;     /* Channel is currently working */
    int   sd;           /* Socket descriptor */
    UINT2 iPortNum;
    char  sAddress[40]; /* Broadcast Address */
  };

  struct ChannelInfo Channel;

  if ((iChannel_a<0)||(iChannel_a>=RTD_CHANNEL_COUNT)) return RTQLIB_BAD_CHANNEL;

  istatus = SS_NORMAL;
  /* Initialize the channel info */
  memset( &Channel, 0, sizeof(struct ChannelInfo) );
  Channel.iPortNum = pRSetup->Channel[iChannel_a].iPortNum;
  strncpy( Channel.sAddress, 
	   (char *)pRSetup->Channel[iChannel_a].sBroadcastAddress, 40-1 );

  /* Open a UDP socket and set it up to support broadcast mode. */
  /* Once socket is opened now, we must close before returning */
  {
    UINT4 iNetAddr;
    int optvalue;
    struct   sockaddr_in BcastAddr;
    
    if ((Channel.sd = socket(PF_INET, SOCK_DGRAM, 0)) < 0)
      {
	istatus = errno_to_message_va( errno, 1, "socket() in RtdXmtOn()" ) ;
	goto CLOSE;
      }
    
    optvalue = 1;
    if (setsockopt(Channel.sd, SOL_SOCKET, SO_BROADCAST, &optvalue,
		     sizeof(optvalue)) < 0)
      {
	istatus = errno_to_message_va( errno, 1, "setsockopt() in RtdXmtOn()" ) ;
	goto CLOSE;
      }
    iNetAddr = inet_addr( Channel.sAddress );
    if ( iNetAddr != (UINT4)-1 )
      {
	BcastAddr.sin_addr.s_addr = iNetAddr;
	BcastAddr.sin_family = AF_INET;
	BcastAddr.sin_port = htons(Channel.iPortNum);
	
	/* 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(Channel.sd, (struct sockaddr *)(void*)&BcastAddr,
		      sizeof(BcastAddr))) < 0)
	  {
	    istatus = errno_to_message_va( errno, 1, "connect() in RtdXmtOn()" ) ;
	    goto CLOSE;
	  }
	else
	  {
	    Channel.lWorking = TRUE;
	  }
      }  /* end if (iNetAddr... */
    else
      {
	sig_signal_va( RTQLIB_SYNTAX, 1, Channel.sAddress );
	istatus = USER_ERROR_SIG;
	goto CLOSE;
      }
  }
  
  /* Main loop here.  Exit when the real time display is turned off */
  getbtime( &iRestartBTime );
  while( RtqWriterStatus(pRtqGlobal_a) )
    {
      /* Return zero if there was no data available, in that case block.
       * Return negative if there was an error. */
      itx_bytes = ReadPacketProc_a( &pBuffer, iChannel_a );
      if (itx_bytes>0)
	{
	  UINT1 lRestart;
	  /* Flag to restart once every 60 seconds */
	  getbtime( &iCurrentBTime );
	  if (btimeGrace( iCurrentBTime-iRestartBTime ) >= 60000)
	    {
	      iRestartBTime=iCurrentBTime;
	      lRestart = TRUE;
	    }
	  else
	    {
	      lRestart = FALSE;
	    }
	  /* Attempt to restart broken channels every */
	  if (Channel.lWorking || lRestart)
	    {
	      int icount;
	    RETRY:
	      icount = write( Channel.sd, pBuffer, itx_bytes );
	      if (icount == -1)
		{
		  /* Supress all error messages when the channel is not working */
		  if (Channel.lWorking)
		    {
		      /* SGI has intermittent errors, we need to repeat the data 
		       * for connection refused.
		       */
		      if (errno == ECONNREFUSED) goto RETRY;
		      if (errno == ENETUNREACH)
			{
			  sig_signal_va( RTQLIB_NOT_REACHABLE, 1, Channel.sAddress );
			}
		      else
			{
			  sig_signal( errno_to_message_va
				      ( errno, 1, Channel.sAddress ) ) ;
			}
		      Channel.lWorking = FALSE;
		    }
		}
	      else if (icount != itx_bytes )
		{
		  sprintf( sMessage, "Sending %d bytes, got %d bytes\n", itx_bytes, icount );
		  Lprintf( sMessage );
		}
	      else
		{
		  Channel.lWorking = TRUE;
		  /*  printf( "Wrote %d bytes\n", itx_bytes ); */
		}
	    }
	}
      else if (itx_bytes==0)
	{
	  sig_microSleep( 100000 );
	}
    }
#ifdef DEBUG_ON
  Lprintf( "RtdXmtOn loop ended.\n" );
#endif  /* #ifdef DEBUG_ON */
  
  /* Close socket before returning */
 CLOSE:
  if (Channel.sd)
    {
      ierror = close( Channel.sd );
      if (ierror == -1) sig_signal( errno_to_message_va
				    ( errno, 1, "close() in RtdXmtOn()" ) ) ;
    }

  return istatus;
}

/* ======================================================================
 * Function to print signaled errors to the loging file or terminal.
 */
MESSAGE RtdSignalHandler
(MESSAGE iStatus_a,
 UINT4 iCount_a,
 const UINT4 iSigArgs_a[] )
{
  char sMessage[170];

  strncpy( sMessage, message_to_text( iStatus_a, iCount_a, iSigArgs_a ), 165 );
  sMessage[165]=0;
  strcat( sMessage, "\n" );

  Lprintf( sMessage );
  return(SS_NORMAL);
}
/*
 * =====================================================================
 * This subroutine figures out which channel we are based on the command
 * line argument.  If there is a problem, it will set the channel to 0.
 * Return value is origin 0, even though the command string is origin 1.
 */
int iGetChannel
( int iArgc_a, 
  char *sArgv_a[] )
{

  char *sPtr;
  int iChannel, iLength;

  /* ------------------------------------------------------------------- */

  if (iArgc_a<2) return 0;

  /* Get the process name from the second command argument */
  iLength = strlen(sArgv_a[1]);
  if (iLength<1) return 0;

  iChannel = strtol( &(sArgv_a[1][iLength-1]), &sPtr, 10 );
  if (iChannel <=0) return 0;

  return iChannel-1;
}

