/* *******************************************************************
 * *                                                                 *
 * *  ANTLIB: Functions to Set Control Requests into Shared Memory   *
 * *                                                                 *
 * ******************************************************************* 
 * File: libs/antenna/AntSet.c
 *
 *                    COPYRIGHT (c) 2003, 2005 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 <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>

#define INCLUDE_STDIO_PROTOTYPES 1
#include "sigtypes.h"
#include "event_flags.h"
#include "user_lib.h"
#include "antenna_lib.h"
#include "antenna_int.h"
#include "setup.h"

#include "ant_global.h"

/* #define DEBUG 1 */
/* *****************************************
 * *                                       *
 * *  Text-Based Common Command Interface  *
 * *                                       *
 * *****************************************
 */
/* ------------------------------
 * Common interface function to handle all control requests from the
 * antenna driver API calls.  Having this single interface allows all
 * of the control requests to be logged and exported to other systems.
 */
#define GETTOKEN \
  if( ! (pToken = sigtok( sStr, &iTokenOffset )) ) goto error ;

#define GETVALUE( VAL ) \
  GETTOKEN ; { char *pEnd ; \
  VAL = strtod( pToken, &pEnd ) ; if( pToken == pEnd ) goto error ; }

#define CHECKOP( OPCODE ) \
  ( iIndex++, (0 == strcmp( pToken, OPCODE )) )

void AntSet
( const char *pStr_a ) 
{
  UINT4 iTokenOffset = 0 ; SINT4 iIndex = -1 ;
  char *pToken, sStr[ ANTSETCMDLEN ] ;

  antGlobal_c->info.iactive += 1 ; /* Make note of activity */

  /* fprintf( stderr, "AntSet: '%s'\r\n", pStr_a ) ; */

  /* Make a writable private copy of the string that we can bust apart
   * for tokens, and dispatch on the opcode.
   */
  strncpy( sStr, pStr_a, sizeof(sStr) ) ; sStr[ ANTSETCMDLEN-1 ] = '\0' ;
  GETTOKEN ;

  /* ---------- Azimuth Motion Command ---------- */
  if( CHECKOP( ANTSET_AZCMD ) ) {
    FLT4 fVal ; GETTOKEN ;
    if( 0 == strcmp( pToken, "VEL" ) ) {
      GETVALUE( fVal ) ; __ant_set_az_vel( fVal ) ;
    } else if( 0 == strcmp( pToken, "POS" ) ) {
      GETVALUE( fVal ) ; __ant_set_az_pos( iBin2FromFDeg(fVal) ) ;
    } else if( 0 == strcmp( pToken, "STOP" ) ) {      
      __ant_stop_az() ;
    } else goto error ;
  }
  /* ---------- Elevation Motion Command ---------- */
  else if( CHECKOP( ANTSET_ELCMD ) ) {
    FLT4 fVal ; GETTOKEN ;
    if( 0 == strcmp( pToken, "VEL" ) ) {
      GETVALUE( fVal ) ; __ant_set_el_vel( fVal ) ;
    } else if( 0 == strcmp( pToken, "POS" ) ) {
      GETVALUE( fVal ) ; __ant_set_el_pos( iBin2FromFDeg(fVal) ) ;
    } else if( 0 == strcmp( pToken, "STOP" ) ) {      
      __ant_stop_el() ;
    } else goto error ;
  }
  /* ---------- AutoRadiate Mode ---------- */
  else if( CHECKOP( ANTSET_AUTORAD ) ) {
    UINT1 lOn ; GETVALUE( lOn ) ; __ant_set_auto( lOn ) ;
  }
  /* ---------- IRIS Antenna Mode ---------- */
  else if( CHECKOP( ANTSET_IRISMODE ) ) {
    UINT1 lOn ; GETVALUE( lOn ) ; __ant_set_iris( lOn ) ;
  }
  /* ---------- Noise Generator On/Off ---------- */
  else if( CHECKOP( ANTSET_NOISEGEN ) ) {
    UINT1 lOn ; GETVALUE( lOn ) ; __ant_set_noise( lOn ) ;
  }
  /* ---------- Toggle Noise Source ---------- */
  else if( CHECKOP( ANTSET_NOISETOG ) ) {
    __ant_toggle_noise() ;
  }
  /* ---------- Network Status Timeout ---------- */
  else if( CHECKOP( ANTSET_NSTOUT ) ) {
    UINT4 iMS ; GETVALUE( iMS ) ; __ant_set_nst_timeout( iMS ) ;
  }
  /* ---------- Network Status Fault Report ---------- */
  else if( CHECKOP( ANTSET_NSTFLT ) ) {
    char *sNstSite3 ; UINT1 lFault ;
    GETTOKEN ; sNstSite3 = pToken ; GETVALUE( lFault ) ;
    __ant_set_nst( sNstSite3, lFault ) ;
  }
  /* ---------- Set RST Mode ---------- */
  else if( CHECKOP( ANTSET_RSTMODE ) ) {
    char *sRstName ; UINT1 lWait ;
    GETTOKEN ; sRstName = pToken ; GETVALUE( lWait ) ;
    __ant_set_rstmode( sRstName, lWait ) ;
  }
  /* ---------- Turn Servo On/Off ---------- */
  else if( CHECKOP( ANTSET_SERVO ) ) {
    UINT1 lOn ; GETVALUE( lOn ) ; __ant_set_servo( lOn ) ;
  }
  /* ---------- Turn Radiate On/Off ---------- */
  else if( CHECKOP( ANTSET_RADIATE ) ) {
    UINT1 lOn ; GETVALUE( lOn ) ; __ant_set_radiate( lOn ) ;
  }
  /* ---------- Toggle Radiate if needed ---------- */
  else if( CHECKOP( ANTSET_TOGGLE ) ) {
    SINT4 iWarmMs ; GETVALUE( iWarmMs ) ; __ant_toggle_radiate( iWarmMs ) ;
  }
  /* ---------- Toggle Radiate if needed ---------- */
  else if( CHECKOP( ANTSET_WAITRAD ) ) {
    SINT4 iWarmMs ; GETVALUE( iWarmMs ) ; __ant_wait_for_radiate( iWarmMs ) ;
  }
  /* ---------- Turn SigGen CW Mode On/Off ---------- */
  else if( CHECKOP( ANTSET_SIGGENCW ) ) {
    UINT1 lOn ; GETVALUE( lOn ) ; __ant_set_sigcw( lOn ) ;
  }
  /* ---------- Turn SigGen Output On/Off ---------- */
  else if( CHECKOP( ANTSET_SIGGENOUT ) ) {
    UINT1 lOn ; GETVALUE( lOn ) ; __ant_set_siggen( lOn ) ;
  }
  /* ---------- Set SigGen Level ---------- */
  else if( CHECKOP( ANTSET_SIGGENVAL ) ) {
    SINT4 iValue ; UINT1 lWait ; GETVALUE( iValue ) ; GETVALUE( lWait ) ;
    __ant_set_siggen_value( iValue, lWait ) ;
  }
  /* ---------- Turn T/R Power On/Off ---------- */
  else if( CHECKOP( ANTSET_TRPOWER ) ) {
    UINT1 lOn ; GETVALUE( lOn ) ; __ant_set_trpower( lOn ) ;
  }
  /* ---------- Reset the Antenna Controller ---------- */
  else if( CHECKOP( ANTSET_RESET ) ) {
    __ant_set_reset() ;
  }
  /* ---------- Set Pulse/Band Width Lines ---------- */
  else if( CHECKOP( ANTSET_PWBW ) ) {
    UINT2 iPwBw ; GETVALUE( iPwBw ) ; __ant_set_pwbw( iPwBw ) ;
  }
  /* ---------- Set Transmit Polarization ---------- */
  else if( CHECKOP( ANTSET_POLAR ) ) {
    UINT2 iPolar ; GETVALUE( iPolar ) ; __ant_set_polar( iPolar ) ;
  }
  /* ---------- Request to Set the Time ---------- */
  else if( CHECKOP( ANTSET_TIME ) ) {
    __ant_set_time() ;
  }
  /* ---------- Send a Chat Character ---------- */
  else if( CHECKOP( ANTSET_CHAT ) ) {
    UINT1 iChar ; GETVALUE( iChar ) ; __ant_put_chat( iChar ) ;
  }
  /* ---------- Set DRCP Mode ---------- */
  else if( CHECKOP( ANTSET_DRCPMODE ) ) {
    UINT2 iMode ; GETVALUE( iMode ) ; __ant_set_drcp_mode( iMode ) ;
  }
  /* ---------- Set DRCP Relinquish Flag ---------- */
  else if( CHECKOP( ANTSET_DRCPRLNQ ) ) {
    UINT1 lRlnq ; GETVALUE( lRlnq ) ; __ant_set_drcp_relinquish( lRlnq ) ;
  }
  /* ---------- Set DRCP WouldUse Flag ---------- */
  else if( CHECKOP( ANTSET_DRCPWUSE ) ) {
    UINT1 lWUse ; GETVALUE( lWUse ) ; __ant_set_drcp_wouldUse( lWUse ) ;
  }
  /* ---------- Set BITE Control Field Button ---------- */
  else if( CHECKOP( ANTSET_BITEBUTN ) ) {
    UINT1 lButtonIn ; SINT4 iField ; GETVALUE( lButtonIn ) ; GETVALUE( iField ) ;
    __bite_set_button( lButtonIn, iField ) ;
  }
  /* ---------- Send a Command to all BITE Units ---------- */
  else if( CHECKOP( ANTSET_BITESCMD ) ) {
    UINT1 iCommand ; GETVALUE( iCommand ) ; __bite_send_command( iCommand ) ;
  }
  /* ---------- Set Command to Specific BITE Unit ---------- */
  else if( CHECKOP( ANTSET_BITEUCMD ) ) {
    UINT1 iCommand, iUnitId ; GETVALUE( iCommand ) ; GETVALUE( iUnitId ) ;
    __bite_send_unit_command( iCommand, iUnitId ) ;
  }
  /* ---------- Unrecognized Command ---------- */
  else goto error ;

  /* Command was executed successfully.  Make a log entry to hold the
   * most recent version of each possible command.
   */
  if( (iIndex < 0) || (iIndex > ANTSET_NUMCOMMANDS) ) {
    sig_signal( iMessageMod_va( ANT_ANTINTERNERR, 1, "Bad index in AntSet" ) ) ;
  } else {
    antMakeHistoryEntry( (const UINT1 *)pStr_a, strlen(pStr_a), 0,
			 &antGlobal_c->vdat.updHist.cmds[ iIndex ] ) ;
#ifdef DEBUG
    if (iIndex==10)
      {
	UINT4 iPid=getpid();
	volatile struct antUpdateItem *pItem = &antGlobal_c->vdat.updHist.cmds[ iIndex ];
	  fprintf( stderr, "%5d: AntSet %s %d %d\n", iPid, pItem->sData,
		   pItem->iPID, pItem->iBtime );
      }
#endif  /* #ifdef DEBUG */
  }
  /* Normal successful return.  Make note that changes were made.
   */
  antGlobal_c->info.ichange_count += 1 ;
  return ;

  /* Error return comes about when we're given a command string that
   * can not be properly parsed.  This indicates some sort of internal
   * error, and is worthy of a signal.
   */
 error:
  sig_signal( iMessageMod_va( ANT_SETSTRING, 1, pStr_a ) ) ;
}

/* ------------------------------
 * Handy invocations, passing an opcode and numeric arg(s).
 */
void AntSetArg( const char *pOpStr_a, FLT8 fValue_a )
{
  char sSetCmd[ ANTSETCMDLEN ] ;
  sprintf( sSetCmd, "%s %.10G", pOpStr_a, fValue_a ) ;
  AntSet( sSetCmd ) ;
}
void AntSetArg2( const char *pOpStr_a, FLT8 fVal1_a, FLT8 fVal2_a )
{
  char sSetCmd[ ANTSETCMDLEN ] ;
  sprintf( sSetCmd, "%s %.10G %.10G", pOpStr_a, fVal1_a, fVal2_a ) ;
  AntSet( sSetCmd ) ;
}

/* *****************************************
 * *                                       *
 * *  Command and Update History Routines  *
 * *                                       *
 * *****************************************
 */

static SINT4 iAntGetUpdateOne
( char *pStr_a, 
  UINT4 iClientMask_a, 
  UINT1 iGet_a,
  volatile struct antUpdateItem *pItem_a, SINT4 iFormat_a,
  SINT4 iIndex_a );  /* Diagnostic index */
#define GETUPONE_DIRECT    0	/* Copy item directly as-is */
#define GETUPONE_ASCII     1	/* Format as embedded ASCII command */

/* ------------------------------
 * Make an entry of a given string into a history structure
 */
void antMakeHistoryEntry
( const UINT1 *pStr_a, 
  SINT4 iLength_a, 
  UINT4 iClientMask_a,   /* Mask indicating which client we are */
  volatile struct antUpdateItem *pItem_a )
{
  SINT4 iIndex;
  lock_sem( ANTENNA_CMDHISTORY_SEM ) ;

  pItem_a->iClientType = antClient_c.iClientType ;
  pItem_a->iSendMask   = iClientMask_a;  /* Mark as already sent to here */
  pItem_a->iPID        = getpid() ;
  pItem_a->iLength     = MAX( 0, MIN( iLength_a, ANTUPDSTRLEN ) ) ;
  memcpy( (char *)pItem_a->sData, pStr_a, pItem_a->iLength ) ;

  unlock_sem( ANTENNA_CMDHISTORY_SEM ) ;

  /* Wake up one of the AntExport processes
   */
  for( iIndex=0 ; iIndex < ANTENNA_EXPORT_NUM ; iIndex++ ) {
    sig_setef( ANTENNA_EXPORT_EF0 + iIndex ) ;
  }
}

/* ------------------------------
 * Return a string of packets containing the data and command updates
 * that have not yet been send to the specified client.
 * Return value is the length of
 * the data string, which must be free'd when you're done with it.
 * The contents of the string can be exported directly to update a
 * remote antenna receive process.
 */
SINT4 iAntGetUpdatePackets
( UINT4 iClientMask_a, /* Mask of which client we are */
  UINT1 iGet_a,        /* Type of get to do */
  char **pData_a )     /* Data returned here */
{
  char *pData ; SINT4 iLength = 0 ;

  lock_sem( ANTENNA_CMDHISTORY_SEM ) ;  

  /* Malloc the worst case string length, so that we can write into it
   * without any worries.
   */
  if( NULL != (pData = malloc( ANTUPDSTRLEN * ANTUPDMAXITEMS )) ) {
    SINT4 ii ; volatile struct antUpdateItem *pItem ;

    /* First pullout the antenna and BITE records, keeping the data
     * exactly as we find it.
     */
    pItem = &antGlobal_c->vdat.updHist.ant ;
    iLength += iAntGetUpdateOne( &pData[iLength], iClientMask_a, iGet_a, pItem,
				 GETUPONE_DIRECT, -1 ) ;

    for( ii=0 ; ii < BITE_MAX_UNITS ; ii++ ) {
      pItem = &antGlobal_c->vdat.updHist.bite[ ii ] ;
      iLength += iAntGetUpdateOne( &pData[iLength], iClientMask_a, iGet_a, pItem,
				   GETUPONE_DIRECT, -1 ) ;
    }
    /* Then extract the ASCII command records, adding the SYNC header
     * and trailer characters to convert them into suitably formatted
     * packets.
     */
    for( ii=0 ; ii < ANTSET_NUMCOMMANDS ; ii++ ) {
      pItem = &antGlobal_c->vdat.updHist.cmds[ ii ] ;
      iLength += iAntGetUpdateOne( &pData[iLength], iClientMask_a, iGet_a, pItem,
				   GETUPONE_ASCII, ii ) ;
    }
    /* If we produced no data at all, then free the memory now and
     * return a NULL data pointer.
     */
    if( iLength == 0 ) { free( pData ) ; pData = NULL ; }
  }

  unlock_sem( ANTENNA_CMDHISTORY_SEM ) ;
  *pData_a = pData ; return( iLength ) ;
}

/* ------------------------------
 * Support routine to optionally extract a packet from the indicated
 * history item, but only if all the criteria for using it are met.
 * Item time must be .GE. first value, and .LE. second value.  Note 
 * that data is often set at the same time, so it will be .EQ. the
 * second value, which will already be checked here.  That is why
 * we cannot includ the second value.
 */
static SINT4 iAntGetUpdateOne
( char *pStr_a, 
  UINT4 iClientMask_a, 
  UINT1 iGet_a,
  volatile struct antUpdateItem *pItem_a, 
  SINT4 iFormat_a,
  SINT4 iIndex_a )  /* Diagnostic index */
{
  UINT1 lNotSent = (0 == (pItem_a->iSendMask & iClientMask_a));
  UINT1 lUseItem ; char *pStr = pStr_a ; SINT4 iLength = 0 ;
  UINT4 iPid = getpid();

  /* No item there */
  if( pItem_a->iLength == 0 ) return 0;

  /* First figure out if we want to use this item.  
   * Likewise, return to the server everything but
   * what the server originally gave to us (via ant_rcv).
   */
  switch( iGet_a ) {
  default:         lUseItem = FALSE ; break ;
  case ANTGET_ALL: lUseItem = TRUE  ; break ;
  case ANTGET_EXPORT:
    lUseItem = lNotSent && (pItem_a->iPID != iPid);  break ;
    /* This may not be needed, the above test may work for the RCV process */
  case ANTGET_RETURN:
    lUseItem = lNotSent &&
      (pItem_a->iClientType != ANT_IAM_ANT_RCV0) ; break ;
  }

  /* If we're using it, then transfer the contents according to the
   * requested format, which may involve adding leading and trailing
   * SYNC characters.
   */
  if( lUseItem ) 
    {
      pItem_a->iSendMask |= iClientMask_a;  /* Store away the sent bit */

      if( iFormat_a == GETUPONE_ASCII ) {
	*pStr++ = ANT_SYNC_ASCMD ; *pStr++ = pItem_a->iLength ; iLength += 2 ;
      }
      memcpy( pStr, (char *)pItem_a->sData, pItem_a->iLength ) ;
      pStr += pItem_a->iLength ; iLength += pItem_a->iLength ;
      
      if( iFormat_a == GETUPONE_ASCII ) { *pStr++ = ANT_SYNC_ENDREC ; iLength++ ; }
#ifdef DEBUG
      if (iIndex_a>=0) fprintf( stderr, "%5d: %2d: %s %d\n", iPid, iIndex_a, pItem_a->sData,
				pItem_a->iPID );
#endif  /* #ifdef DEBUG */
    }
  return( iLength ) ;
}
