#define	    ident	"1A01"
#ifdef VAXC
#module	    VelSel_Utility	ident
#endif
#ifdef __DECC
#pragma	    module	    VelSel_Utility	ident
#endif
/*
** +--------------------------------------------------------------+
** |                  Paul Scherrer Institute                     |
** |                     Department ASQ				  |
** |                                                              |
** | This software may be used freely by non-profit organizations.|
** | It may be copied provided that the name of P.S.I. and of the |
** | author is included. Neither P.S.I. nor the author assume any |
** | responsibility for the use of this software outside of P.S.I.|
** +--------------------------------------------------------------+
**
** Module Name  . . . . . . . . : [...LIB.SINQ]VelSel_Utility.C
**
** Author   . . . . . . . . . . : D. Maden
** Date of creation . . . . . . : June 1997
**
**  To compile this module, use:

	$ import tasmad
	$ define/group sinq_c_tlb mad_lib:sinq_c.tlb
	$ cc /debug /noopt /obj=[]VelSel_Utility -
			tasmad_disk:[mad.psi.lib.sinq]VelSel_Utility + -
			sinq_c_tlb/lib

**  To include this module in SINQ.OLB, use:

  $ import tasmad
  $ define/group sinq_c_tlb mad_lib:sinq_c.tlb
  $
  $ define/group sinq_olb mad_lib:sinq_dbg.olb
  $ @tasmad_disk:[mad.lib.sinq]sinq_olb VelSel_Utility debug
  $
  $ define/group sinq_olb mad_lib:sinq.olb
  $ @tasmad_disk:[mad.lib.sinq]sinq_olb VelSel_Utility
**
** Updates:
**  1A01 13-Jun-1997 DM.	Initial version.
**============================================================================
** The entry points included in this module are described below. Prototypes
** can be defined via:
**
**    #include <sinq_prototypes.h>
**
**	VelSel_Close	    - Close a connection to a Velocity Selector.
**	VelSel_Config	    - Configure a connection to a Velocity Selector.
**	VelSel_ErrInfo	    - Return detailed status from last operation.
**	VelSel_GetReply	    - Get next reply from a reply buffer.
**	VelSel_GetStatus    - Get "???" response.
**	VelSel_Open	    - Open a connection to a Velocity Selector.
**	VelSel_SendCmnds    - Send commands to RS232C server.
**---------------------------------------------------------------------
**    int VelSel_Close (&handle, int force_flag)
**	  ------------
**  Input Args:
**	int    force_flag - if non-zero, all connections using the same socket
**			    will also be closed (this gets AsynSrv_Close to
**			    actually close the socket and is needed for error
**			    recovery operations).
**  Output Args:
**	none
**  Modified Args:
**	void **handle	- The pointer to the structure returned by VelSel_Open.
**			  On return, the pointer is set to NULL.
**  Return status:
**	True always (error returns from close and free are not checked).
**  Routines called:
**	AsynSrv_Close
**  Description:
**	The routine calls AsynSrv_Close to close the connection to the RS232C
**	server. If 'force_flag' is non-zero, all other connections to the
**	RS232C server which use the same socket will also be closed.
**
**	The 'force_flag' can be useful in error recovery situations. The AsynSrv
**	utility operates by only opening a socket for each separate combination
**	of host/port. Hence, if several connections are open to the server,
**	then calling VelSel_Close doesn't actually close the socket until all
**	connections have been closed. In the situation
**	where an error has been detected, it is often desirable to
**	close and re-open the socket as part of the recovery procedure. Calling
**	VelSel_Close with 'force_flag' non-zero will force the socket to be
**	closed and will mark all connections using this socket so that they
**	will be informed of the event when they next call any AsynSrv
**	dependent routine.
**
**	Note:	The force-close action is effected by the AsynSrv package. A
**		force-close will thus also close any connections to other
**		RS-232-C devices (e.g. EL737 neutron cntr) on the same server.
**-------------------------------------------------------------------------
**   void VelSel_Config (&handle, msec_tmo, eot_str)
**	  -------------
**  Input Args:
**	void **handle	- The pointer to the structure returned by VelSel_Open.
**	int    msec_tmo	- The time-out for responses. Dflt = 10000.
**	char  *eot_str	- A string of up to 3 characters specifying terminating
**			    characters for input.
**  Output Args:
**	none
**  Modified Args:
**	none
**  Return status:
**	none
**  Routines called:
**	none
**  Description:
**	The routine sets values in the VelSel_info data structure.
**-------------------------------------------------------------------------
**    void VelSel_ErrInfo (&entry_txt_ptr, &errcode, &my_errno, &vaxc_errno)
**	   --------------
**  Input Args:
**	None
**  Output Args:
**	char **entry_txt_ptr - Pointer to a text string giving the call stack
**				at the time that the error was detected.
**	int  *errcode	 - An internal error code indicating the detected error.
**	int  *my_errno	 - Saved value of errno.
**	int  *vaxc_errno - Saved value of vaxc$errno (OpenVMS only).
**  Modified Args:
**	none
**  Return status:
**	none
**  Routines called:
**	none
**  Description:
**	Returns detailed status of the last operation. Once an error has been
**	detected, the error status is frozen until this routine has been called.
**-------------------------------------------------------------------------
**    void  *VelSel_GetReply (&handle, last_rply)
**	     ---------------
**  Input Args:
**	void **handle	    - The pntr to the structure returned by VelSel_Open.
**	void  *last_rply    - Address of last reply processed or NULL.
**  Output Args:
**	none
**  Modified Args:
**	none
**  Return status:
**	Address of next reply structure in the buffer or NULL if no more.
**  Routines called:
**	none
**  Description:
**    VelSel_GetReply is a utility routine mainly intended for internal use
**    by the VelSel_Utility package. It unpacks the replies in the response
**    packet from the RS232C server.
**
**    Having received a response from the server to a sequence of commands,
**    VelSel_GetReply is called with last_rply = NULL. The return value is
**    a pointer to the first reply sub-structure in the response. On calling
**    VelSel_GetReply again with last_rply set to this address, one receives
**    the address of the second reply sub-structure and so on, until NULL
**    is returned when all responses have been exhausted. The structure of
**    a reply sub-structure is RS__RplyStruct.
**-------------------------------------------------------------------------
**    int VelSel_GetStatus (&handle, &status_str, status_str_len)
**	  ----------------
**  Input Args:
**	void **handle	- The pointer to the structure returned by VelSel_Open.
**	int    status_str_len	- The length of status_str.
**  Output Args:
**	char  *status_str	- Pointer to a buffer to save the status.
**  Modified Args:
**	none
**  Return status:
**	True if no problems detected, otherwise False and VelSel_ErrInfo
**	can be called to identify the problem. Values of Errcode set by
**	VelSel_GetStatus are (other values may be set by the called routines):
**	    VELSEL__BAD_TMO, _LOC, _CMD, _OFL, _ADR --> see VelSel_Open.
**	    VELSEL__BAD_ILLG = -5 --> one of the responses could probably not be
**				decoded. This could happen if there is noise
**				on the RS232C connection to the Velocity
**				Selector.
**	If an error is detected, ist_posit is set to 0.0 and all other
**	arguments to -1.
**  Routines called:
**	VelSel_SendCmnds
**  Description:
**	The routine issues a "???" command to the Velocity Selector and
**	analyses the result.
**-------------------------------------------------------------------------
**    int VelSel_Open (&handle, host, port, chan, id)
**	  -----------
**  Input Args:
**	char *host	- Name of host offering the TCP/IP service.
**	int   port	- Number of TCP/IP port of TCP/IP server.
**	int   chan	- RS-232-C Channel number on the TCP/IP server.
**	char *id	- The expected ID of the device, normally "????".
**			    If id is NULL, the device ID is not checked.
**  Output Args:
**	void *handle	- A pointer to a structure of type VelSel_info needed
**			   for subsequent calls to VelSel_... routines. Buffer
**			   space for the structure is allocated dynamically.
**			   It gets released via a call to VelSel_Close.
**  Modified Args:
**	none
**  Return status:
**	True if no problems detected, otherwise False. If False, VelSel_ErrInfo
**	can be called to identify the problem. Values of Errcode set by
**	VelSel_Open are (other values may be set by the called routines):
**	  VELSEL__BAD_TMO     =  -1 --> Time-out error ("?TMO" - this gets
**					generated by the RS232C server).
**	  VELSEL__BAD_LOC     =  -2 --> Off-line ("?LOC"). This should not
**					happen on calls to VelSel_Open since it
**					sends an "RMT 1" cmnd.
**	  VELSEL__BAD_CMD     =  -3 --> Command error ("?CMD"). This could be
**					caused by noise in the RS-232-C
**					transmission.
**	  VELSEL__BAD_OFL     =  -4 --> Connection broken ("?OFL").
**					This can get generated by RS232C_SRV
**					if, for example, the connection is via
**					a terminal server and the terminal
**					server loses power.
**	  VELSEL__BAD_ILLG    =  -5 --> Some other unrecognised response. This
**					should never occur, of course!
**	  VELSEL__BAD_HOST    =  -6 --> Call to "gethostbyname" failed to get
**					network addr of host.
**	  VELSEL__BAD_SOCKET  =  -7 --> Call to "socket" failed.
**	  VELSEL__BAD_BIND    =  -8 --> Call to "bind" failed.
**	  VELSEL__BAD_CONNECT =  -9 --> Call to "connect" failed.
**	  VELSEL__BAD_DEV     = -10 --> Bad cmnd response - is device a VelSel?
**	  VELSEL__BAD_MALLOC  = -11 --> Call to "malloc" failed
**  Routines called:
**	AsynSrv_open, the memory alloc routine "malloc" and VelSel_SendCmnds.
**  Description:
**	The routine calls AsynSrv_open to open a TCP/IP connection to a server
**	offering the "RS-232-C" service for a Velocity Selector. "RMT 1"
**	and "ECHO 0" commands are sent to ensure the device is on-line.
**-------------------------------------------------------------------------
**    int   VelSel_SendCmnds (&handle, ...)
**	    ----------------
**  Input Args:
**	void   **handle	- The pntr to the structure returned by VelSel_Open.
**	char	* ...	- A list of commands, terminated by NULL, for
**			    sending to the Velocity Selector. The commands must
**			    have any necessary \r characters included.
**  Output Args:
**	none
**  Modified Args:
**	none
**  Return status:
**	True if no problems detected, otherwise False and errcode (see
**	VelSel_ErrInfo) is set to indicate the nature of the problem.
**	VelSel_errcode may be set as follows:
**	    VELSEL__BAD_SENDLEN = -12 --> Too much to send; either too many
**					    commands or too long. The buffer
**					    is 232 bytes long and each command
**					    has a 2-byte header.
**	  Errors -13 to -16 are related to network errors whilst sending the
**	  message buffer to the server:
**	    VELSEL__BAD_SEND	 = -13 --> Network problem - server has
**					    probably abended.
**	    VELSEL__BAD_SEND_PIPE = -14 --> Network pipe broken - probably same
**					    cause as VELSEL__BAD_SEND.
**	    VELSEL__BAD_SEND_NET  = -15 --> Some other network problem. "errno"
**					    may be helpful.
**	    VELSEL__BAD_SEND_UNKN = -16 --> Some other network problem happened
**					    resulting in the message not
**					    getting sent completely. "errno" is
**					    probably not helpful in this case.
**	    Errors VELSEL__BAD_RECV, VELSEL__BAD_RECV_PIPE, VELSEL__BAD_RECV_NET
**		and VELSEL__BAD_RECV_UNKN (-17 to -20) are related to network
**		errors whilst receiving the 4-byte response header. They are
**		analogous to VELSEL__BAD_SEND to VELSEL__BAD_SEND_UNKN.
**	    VELSEL__BAD_NOT_BCD = -21 --> The 4-byte response header is not an
**					  ASCII coded decimal integer.
**	    VELSEL__BAD_RECVLEN = -22 --> The body of the response would be too
**					  big to fit in the input buffer. The
**					  buffer is 244 bytes long and each
**					  response has a 3-byte header and a
**					  trailing zero-byte. The response
**					  is flushed.
**	    VELSEL__BAD_FLUSH   = -23 --> Some network error was detected
**					  during flushing. This is an "or"
**					  of errors VELSEL__BAD_RECV to
**					  VELSEL__BAD_RECV_UNKN.
**	    VELSEL__FORCED_CLOSED = -32 --> The connection to the Velocity
**					   Selector has been forcefully
**					   closed. See below.
**	    VELSEL__BAD_REPLY   = -34 --> The n_rply field of the response was
**					either non-numeric or <0, indicating
**					that the Terminal Server detected an
**					error. The reply is added to the
**					routine call stack for debug purposes.
**				
**	    Errors VELSEL__BAD_RECV1, VELSEL__BAD_RECV1_PIPE and
**		VELSEL__BAD_RECV1_NET (-24 to -26) are related to network
**		errors whilst receiving the body of the response. They are
**		equivalent to errors VELSEL__BAD_RECV, to VELSEL__BAD_RECV_NET.
**
**	    VELSEL__FORCED_CLOSED occurs if AsynSrv_Close has been called (e.g.
**		via a call to VelSel_Close) for another device on the same
**		server and the 'force_flag' was set (see VelSel_Close). The
**		caller should call VelSel_Close and then VelSel_Open to
**		re-establish a connection to the Velocity Selector.
**  Routines called:
**	Socket library routines send and recv.
**  Description:
**	The list of commands is assembled into a message buffer with appropriate
**	header information and sent off to the server. The response is then
**	awaited and read in when it arrives.
**
**    For any of the following errors:
**		VELSEL__BAD_SEND		(Note: VELSEL__BAD_SENDLEN and 
**		VELSEL__BAD_SEND_PIPE	VELSEL__BAD_RECVLEN do not cause a close
**		VELSEL__BAD_SEND_NET
**		VELSEL__BAD_SEND_UNKN
**		VELSEL__BAD_RECV
**		VELSEL__BAD_RECV_PIPE
**		VELSEL__BAD_RECV_NET
**		VELSEL__BAD_RECV_UNKN
**		VELSEL__BAD_NOT_BCD
**		VELSEL__BAD_FLUSH
**		VELSEL__BAD_RECV1
**		VELSEL__BAD_RECV1_PIPE
**		VELSEL__BAD_RECV1_NET
**    the network link to the server is force-closed via a call to VelSel_Close.
**    Once the error has been corrected, the link can be re-opened via a
**    call to VelSel_Open. As a result of the force-close, other active handles
**    will need to be released via a call to VelSel_Close before VelSel_Open is
**    called.
**
**    Note: neither of the errors VELSEL__BAD_SENDLEN, VELSEL__BAD_RECVLEN
**	    nor VELSEL__BAD_REPLY cause the link to be closed.
**============================================================================*/
/*
**---------------------------------------------------------------------------
**					Global Definitions
*/
#include    <stdlib.h>
#include    <stdio.h>
#include    <stdarg.h>
#include    <errno.h>
#include    <signal.h>
#include    <netdb.h>
#include    <sys/socket.h>
#include    <netinet/in.h>

#include    <string.h>

#ifdef	__VMS
#include    <unixio.h>
#else
#include    <unistd.h>
#endif
/*-----------------------------------------------------------------*/
#include    <rs232c_def.h>
#include    <asynsrv_def.h>
#include    <VelSel_def.h>
#include    <sinq_prototypes.h>

#define True	1
#define False	0
/*--------------------------------------------------------------------------
**					Global Variables
*/
static int VelSel_call_depth = 0;
static char VelSel_routine[5][64];
static int VelSel_errcode = 0;
static int VelSel_errno, VelSel_vaxc_errno;
/*
**---------------------------------------------------------------------------
**		VelSel_Close:  Close a connection to a Velocity Selector.
*/
int VelSel_Close(
/*	===========
*/ void **handle,
                  int force_flag)
{

  struct VelSel_info *info_ptr;
  char buff[4];

  info_ptr = (struct VelSel_info *) *handle;
  if (info_ptr == NULL)
    return True;

  if (info_ptr->asyn_info.skt != 0) {
    if (info_ptr->asyn_info.skt > 0) {
      AsynSrv_Close(*handle, force_flag);
    }
  }
  free(*handle);
  *handle = NULL;

  return True;
}

/*
**---------------------------------------------------------------------------
**		VelSel_Config:  Configure a connection to a Velocity Selector.
*/
void VelSel_Config(
/*	============
*/ void **handle,
                    int msec_tmo, char *eot_str)
{

  int i;
  struct VelSel_info *info_ptr;

  info_ptr = (struct VelSel_info *) *handle;
  if (info_ptr == NULL)
    return;
                /*-------------------------
		** Set up the time-out
		*/
  if (msec_tmo < 0) {
    info_ptr->tmo = -1;
  } else {
    info_ptr->tmo = (msec_tmo + 99) / 100;      /* Convert to deci-secs */
    if (info_ptr->tmo > 9999)
      info_ptr->tmo = 9999;
  }
                /*---------------------------------
		** Set up the end-of-text string
		*/
  if (eot_str != NULL) {
    for (i = 0; i < sizeof(info_ptr->eot); i++)
      info_ptr->eot[i] = '\0';

    for (i = 0; i < sizeof(info_ptr->eot); i++) {
      if (eot_str[i] == '\0')
        break;
      info_ptr->eot[i + 1] = eot_str[i];
    }
    info_ptr->eot[0] = '0' + i;
  }
  return;
}

/*
** -------------------------------------------------------------------------
**		VelSel_ErrInfo:  Return detailed status from last operation.
*/
void VelSel_ErrInfo(
/*	=============
*/ char **entry_txt,
                     int *errcode, int *my_errno, int *vaxc_errno)
{

  int i;
  char buff[80];
  int asyn_errcode, asyn_errno, asyn_vaxerrno;
  char *asyn_errtxt;

  if (VelSel_call_depth <= 0) {
    strcpy(VelSel_routine[0], "VelSel_no_error_detected");
    *errcode = 0;
    *my_errno = 0;
    *vaxc_errno = 0;
  } else {
    if (VelSel_call_depth > 1) {        /* Concatenate the names */
      for (i = 1; i < VelSel_call_depth; i++) {
        strcat(VelSel_routine[0], "/");
        StrJoin(VelSel_routine[0], sizeof(VelSel_routine),
                VelSel_routine[0], VelSel_routine[i]);
      }
    }
    *errcode = VelSel_errcode;
    *my_errno = VelSel_errno;
    *vaxc_errno = VelSel_vaxc_errno;
    switch (VelSel_errcode) {
    case VELSEL__BAD_TMO:
      strcpy(buff, "/VELSEL__BAD_TMO");
      break;
    case VELSEL__BAD_CMD:
      strcpy(buff, "/VELSEL__BAD_CMD");
      break;
    case VELSEL__BAD_OFL:
      strcpy(buff, "/VELSEL__BAD_OFL");
      break;
    case VELSEL__BAD_ILLG:
      strcpy(buff, "/VELSEL__BAD_ILLG");
      break;
    case VELSEL__BAD_HOST:
      strcpy(buff, "/VELSEL__BAD_HOST");
      break;
    case VELSEL__BAD_SOCKET:
      strcpy(buff, "/VELSEL__BAD_SOCKET");
      break;
    case VELSEL__BAD_BIND:
      strcpy(buff, "/VELSEL__BAD_BIND");
      break;
    case VELSEL__BAD_CONNECT:
      strcpy(buff, "/VELSEL__BAD_CONNECT");
      break;
    case VELSEL__BAD_DEV:
      strcpy(buff, "/VELSEL__BAD_DEV");
      break;
    case VELSEL__BAD_MALLOC:
      strcpy(buff, "/VELSEL__BAD_MALLOC");
      break;
    case VELSEL__BAD_SENDLEN:
      strcpy(buff, "/VELSEL__BAD_SENDLEN");
      break;
    case VELSEL__BAD_SEND:
      strcpy(buff, "/VELSEL__BAD_SEND");
      break;
    case VELSEL__BAD_SEND_PIPE:
      strcpy(buff, "/VELSEL__BAD_SEND_PIPE");
      break;
    case VELSEL__BAD_SEND_NET:
      strcpy(buff, "/VELSEL__BAD_SEND_NET");
      break;
    case VELSEL__BAD_SEND_UNKN:
      strcpy(buff, "/VELSEL__BAD_SEND_UNKN");
      break;
    case VELSEL__BAD_RECV:
      strcpy(buff, "/VELSEL__BAD_RECV");
      break;
    case VELSEL__BAD_RECV_PIPE:
      strcpy(buff, "/VELSEL__BAD_RECV_PIPE");
      break;
    case VELSEL__BAD_RECV_NET:
      strcpy(buff, "/VELSEL__BAD_RECV_NET");
      break;
    case VELSEL__BAD_RECV_UNKN:
      strcpy(buff, "/VELSEL__BAD_RECV_UNKN");
      break;
    case VELSEL__BAD_NOT_BCD:
      strcpy(buff, "/VELSEL__BAD_NOT_BCD");
      break;
    case VELSEL__BAD_RECVLEN:
      strcpy(buff, "/VELSEL__BAD_RECVLEN");
      break;
    case VELSEL__BAD_FLUSH:
      strcpy(buff, "/VELSEL__BAD_FLUSH");
      break;
    case VELSEL__BAD_RECV1:
      strcpy(buff, "/VELSEL__BAD_RECV1");
      break;
    case VELSEL__BAD_RECV1_PIPE:
      strcpy(buff, "/VELSEL__BAD_RECV1_PIPE");
      break;
    case VELSEL__BAD_RECV1_NET:
      strcpy(buff, "/VELSEL__BAD_RECV1_NET");
      break;
    case VELSEL__BAD_PAR:
      strcpy(buff, "/VELSEL__BAD_PAR");
      break;
    case VELSEL__BAD_BSY:
      strcpy(buff, "/VELSEL__BAD_BSY");
      break;
    case VELSEL__BAD_OPEN:
      strcpy(buff, "/VELSEL__BAD_OPEN");
      break;
    case VELSEL__FORCED_CLOSED:
      strcpy(buff, "/VELSEL__FORCED_CLOSED");
      break;
    case VELSEL__BAD_STP:
      strcpy(buff, "/VELSEL__BAD_STP");
      break;
    case VELSEL__BAD_REPLY:
      strcpy(buff, "/VELSEL__BAD_REPLY");
      break;
    default:
      sprintf(buff, "/VELSEL__unknown_err_code: %d", VelSel_errcode);
    }
    StrJoin(VelSel_routine[0], sizeof(VelSel_routine), VelSel_routine[0],
            buff);
  }
  AsynSrv_ErrInfo(&asyn_errtxt, &asyn_errcode, &asyn_errno,
                  &asyn_vaxerrno);
  if (asyn_errcode != 0) {
    strcat(VelSel_routine[0], "/");
    StrJoin(VelSel_routine[0], sizeof(VelSel_routine),
            VelSel_routine[0], asyn_errtxt);
  }
  *entry_txt = VelSel_routine[0];
  VelSel_call_depth = 0;
  VelSel_errcode = 0;
}

/*
**---------------------------------------------------------------------------
**		VelSel_GetReply    - Get next reply from a reply buffer.
*/
void *VelSel_GetReply(
                                /*	 ==============
*/ void **handle,               /* Pointer to structure containing
                                 ** message to pull apart */
                       void *last_rply)
{                               /* Starting point */

  int rply_len;
  struct RS__RplyStruct *ptr;
  struct VelSel_info *my_info_ptr;
  struct RS__RplyStruct *my_last_rply;

  ptr = NULL;
  my_info_ptr = (struct VelSel_info *) *handle;
  my_last_rply = (struct RS__RplyStruct *) last_rply;

  if (my_last_rply == NULL) {   /* Start with first reply? */
    /* Yes */
    if (sscanf(my_info_ptr->from_host.n_rply, "%4d",
               &my_info_ptr->max_replies) != 1)
      my_info_ptr->max_replies = 0;
    if (my_info_ptr->max_replies > 0)
      ptr = (struct RS__RplyStruct *) my_info_ptr->from_host.u.rplys;
    my_info_ptr->n_replies = 1;
  } else {
    my_info_ptr->n_replies++;
    if (my_info_ptr->n_replies <= my_info_ptr->max_replies) {
      if (sscanf(my_last_rply->rply_len, "%2d", &rply_len) == 1) {
        ptr =
            (struct RS__RplyStruct *) ((char *) my_last_rply + rply_len +
                                       2);
      }
    }
  }
  return (void *) ptr;
}

/*
**---------------------------------------------------------------------------
**		VelSel_GetStatus:  Get "???" response from Vel Selector
*/
int VelSel_GetStatus(
/*	===============
*/ void **handle,
                      char *status_str, int status_str_len)
{

  int status;
  struct VelSel_info *info_ptr;
  struct RS__RplyStruct *rply_ptr;
  struct RS__RplyStruct *rply_ptr0;
                    /*----------------------------------------------
		    */
  status_str[0] = '\0';
                    /*----------------------------------------------
		    ** Pre-set the routine name (in case of error)
		    */
  if (VelSel_errcode == 0 && VelSel_call_depth < 5) {
    strcpy(VelSel_routine[VelSel_call_depth], "VelSel_GetStatus");
    VelSel_call_depth++;
  }
                    /*----------------------------------------------
		    ** Do nothing if no connection - the connection gets
		    ** closed if an error is detected.
		    */
  info_ptr = (struct VelSel_info *) *handle;
  if (info_ptr == NULL) {
    return False;
  }
  if (info_ptr->asyn_info.skt <= 0) {
    memset(info_ptr->from_host.msg_size,
           '0', sizeof(info_ptr->from_host.msg_size));
    if ((VelSel_errcode == 0) && (info_ptr->asyn_info.skt < 0)) {
      VelSel_errcode = VELSEL__FORCED_CLOSED;
    }
    return False;
  }
                    /*----------------------------------------------
		    ** Send "???" command to Velocity Selector
		    */
  status = VelSel_SendCmnds(handle, "???", NULL);
  if (!status) {
    /* Error in VelSel_SendCmnds */
    return False;
  } else {
    rply_ptr0 = VelSel_GetReply(handle, NULL);
    if (rply_ptr0 == NULL)
      rply_ptr0 = (struct RS__RplyStruct *) "06\rNULL";
    StrJoin(status_str, status_str_len, rply_ptr0->rply, "");
  }
  VelSel_call_depth--;
  return True;
}

/*
**---------------------------------------------------------------------------
**		VelSel_Open:  Open a connection to a Velocity Selector.
*/
int VelSel_Open(
/*	==========
*/ void **handle,
                 char *host, int port, int chan)
{

  int status;
  struct VelSel_info *my_handle;
  struct RS__RplyStruct *rply_ptr;
  struct RS__RplyStruct *rply_ptr0;
  struct RS__RplyStruct *rply_ptr1;
  struct RS__RplyStruct *rply_ptr2;
  struct RS__RplyStruct *rply_ptr3;
/*--------------------------------------------------------
**			Initialise the error info stack and pre-set the
**			routine name (in case of error).
*/
  VelSel_errcode = VelSel_errno = VelSel_vaxc_errno = 0;
  strcpy(VelSel_routine[0], "VelSel_Open");
  VelSel_call_depth = 1;
/*--------------------------------------------------------
**				    Assume trouble
*/
  *handle = NULL;
/*--------------------------------------------------------
**			    Reserve space for the data we need to store.
*/
  my_handle = (struct VelSel_info *) malloc(sizeof(*my_handle));
  if (my_handle == NULL) {
    VelSel_errcode = VELSEL__BAD_MALLOC;        /* malloc failed!! */
    return False;
  }
/*--------------------------------------------------------
**				    Set up the connection
*/
  StrJoin(my_handle->asyn_info.host, sizeof(my_handle->asyn_info.host),
          host, "");
  my_handle->asyn_info.port = port;
  my_handle->asyn_info.chan = chan;
  status = AsynSrv_Open(&my_handle->asyn_info);
  if (!status) {
    VelSel_errcode = VELSEL__BAD_SOCKET;
    GetErrno(&VelSel_errno, &VelSel_vaxc_errno);        /* Save errno info */
    fprintf(stderr, "\nVelSel_Open/AsynSrv_Open: "
            "Failed to make connection.\n");
    free(my_handle);
    return False;
  }

  my_handle->tmo = 25;          /* Set a short time-out initially since
                                 ** there should be no reason for the REM
                                 ** command to take very long
                                 */
  strcpy(my_handle->eot, "1\n\0\0");
  my_handle->msg_id = 0;
  /*
   ** Now ensure the VelSel is on-line. The first "REM" command can
   ** fail due to pending characters in the VelSel input buffer causing
   ** the "REM" to be corrupted. The response of the VelSel to this
   ** command is ignored for this reason (but the VelSel_SendCmnds
   ** status must be OK otherwise it indicates a network problem).
   */
  status = VelSel_SendCmnds((void *) &my_handle, "REM", NULL);
  if (status) {
    status = VelSel_SendCmnds((void *) &my_handle, "REM", NULL);
  }
  if (!status) {
    /* Some error occurred in VelSel_SendCmnds - Errcode will
     ** have been set up there.
     */
    AsynSrv_Close(&my_handle->asyn_info, False);
    free(my_handle);
    return False;
  }
  /*
   ** Check the responses carefully.
   */
  rply_ptr0 = VelSel_GetReply((void *) &my_handle, NULL);

  if (rply_ptr0 == NULL)
    rply_ptr0 = (struct RS__RplyStruct *) "06\rNULL";
  if (rply_ptr0->rply[0] == '?') {
    VelSel_errcode = VELSEL__BAD_DEV;   /* Error response - not a VelSel? */
    AsynSrv_Close(&my_handle->asyn_info, False);
    free(my_handle);
    return False;
  }
  /*
   ** The connection is complete. Pass the data structure
   ** back to the caller as a handle.
   */
  my_handle->tmo = 100;         /* Default time-out is 10 secs */
  *handle = my_handle;
  VelSel_call_depth--;
  return True;
}

/*
**---------------------------------------------------------------------------
**		VelSel_SendCmnds - Send commands to RS232C server.
*/
int VelSel_SendCmnds(
/*	================
*/ void **handle,
                      ...)
{                               /* Now we have list of commands -
                                 **   char *txt   = pntr to cmnd strng
                                 ** Terminate list with *txt = NULL.
                                 */
  struct VelSel_info *info_ptr;
  int i, status, c_len, size, max_size, ncmnds;
  int bytes_to_come, bytes_left;
  char *nxt_byte_ptr;
  char err_text[80];
  char text[20];
  va_list ap;                   /* Pointer to variable args */
  char *txt_ptr;
  char *cmnd_lst_ptr;
                    /*----------------------------------------------
		    ** Pre-set the routine name (in case of error)
		    */
  if (VelSel_errcode == 0 && VelSel_call_depth < 5) {
    strcpy(VelSel_routine[VelSel_call_depth], "VelSel_SendCmnds");
    VelSel_call_depth++;
  }
                    /*----------------------------------------------
		    ** Do nothing if no connection - the connection gets
		    ** closed if an error is detected. The connection may
		    ** also be marked to have been forcefully closed.
		    */
  info_ptr = (struct VelSel_info *) *handle;
  if (info_ptr == NULL) {
    return False;
  }
  if (info_ptr->asyn_info.skt <= 0) {
    memset(info_ptr->from_host.msg_size,
           '0', sizeof(info_ptr->from_host.msg_size));
    if ((VelSel_errcode == 0) && (info_ptr->asyn_info.skt < 0)) {
      VelSel_errcode = VELSEL__FORCED_CLOSED;
    }
    return False;
  }
                    /*----------------------------------------------
		    ** Build message for Vel Selector from the list of commands.
		    */
  info_ptr->n_replies = info_ptr->max_replies = 0;

  info_ptr->msg_id++;           /* Set up an incrementing message id */
  if (info_ptr->msg_id > 9999)
    info_ptr->msg_id = 1;
  sprintf(info_ptr->to_host.msg_id, "%04.4d", info_ptr->msg_id);

  memcpy(info_ptr->to_host.c_pcol_lvl, RS__PROTOCOL_ID,
         sizeof(info_ptr->to_host.c_pcol_lvl));
  sprintf(info_ptr->to_host.serial_port, "%04.4d",
          info_ptr->asyn_info.chan);
  sprintf(info_ptr->to_host.tmo, "%04.4d", info_ptr->tmo);

  memcpy(info_ptr->to_host.terms, info_ptr->eot,
         sizeof(info_ptr->to_host.terms));
  memcpy(info_ptr->to_host.n_cmnds, "0000",
         sizeof(info_ptr->to_host.n_cmnds));

  va_start(ap, handle);         /* Set up var arg machinery */

  txt_ptr = va_arg(ap, char *); /* Get pntr to next cmnd string */
  ncmnds = 0;
  cmnd_lst_ptr = &info_ptr->to_host.cmnds[0];
  bytes_left = sizeof(info_ptr->to_host) -
      OffsetOf(struct RS__MsgStruct, cmnds[0]);

  while (txt_ptr != NULL) {
    size = 2 + strlen(txt_ptr);
    if (size > bytes_left) {
      VelSel_errcode = VELSEL__BAD_SENDLEN;     /* Too much to send */
      fprintf(stderr, "\nVelSel_SendCmnds/send: too much to send"
              " - request ignored.\n");
      memset(info_ptr->from_host.msg_size,
             '0', sizeof(info_ptr->from_host.msg_size));
      return False;
    } else {
      strcpy(cmnd_lst_ptr + 2, txt_ptr);
      c_len = strlen(txt_ptr);
      sprintf(text, "%02.2d", c_len);
      memcpy(cmnd_lst_ptr, text, 2);
      cmnd_lst_ptr = cmnd_lst_ptr + c_len + 2;
      ncmnds++;
      bytes_left = bytes_left - size;
      txt_ptr = va_arg(ap, char *);
    }
  }
  sprintf(text, "%04.4d", ncmnds);
  memcpy(info_ptr->to_host.n_cmnds,
         text, sizeof(info_ptr->to_host.n_cmnds));

  size = cmnd_lst_ptr - info_ptr->to_host.msg_id;
  size = (size + 3) & (~3);     /* Round up to multiple of 4 */
  sprintf(text, "%04.4d", size);
  memcpy(info_ptr->to_host.msg_size, text, 4);

  status = send(info_ptr->asyn_info.skt,
                (char *) &info_ptr->to_host, size + 4, 0);
  if (status != (size + 4)) {
    GetErrno(&VelSel_errno, &VelSel_vaxc_errno);
    if (status == 0) {
      VelSel_errcode = VELSEL__BAD_SEND;        /* Server exited (probably) */
      fprintf(stderr, "\nVelSel_SendCmnds/send: probable network problem");
    } else if (status == -1) {
      if (VelSel_errno == EPIPE) {
        VelSel_errcode = VELSEL__BAD_SEND_PIPE; /* Server exited (probably) */
        fprintf(stderr, "\nVelSel_SendCmnds/send: broken network pipe");
      } else {
        VelSel_errcode = VELSEL__BAD_SEND_NET;  /* It's some other net problem */
        perror("VelSel_SendCmnds/send");
      }
    } else {
      VelSel_errcode = VELSEL__BAD_SEND_UNKN;   /* TCP/IP problems */
      fprintf(stderr, "\nVelSel_SendCmnds/send: probable TCP/IP problem");
    }
    VelSel_Close(handle, True); /* Force close TCP/IP connection */
    fprintf(stderr, " - link to server force-closed.\n");
    return False;
  }

  size = sizeof(info_ptr->from_host.msg_size);
  status = recv(info_ptr->asyn_info.skt,
                info_ptr->from_host.msg_size, size, 0);
  if (status != size) {
    GetErrno(&VelSel_errno, &VelSel_vaxc_errno);
    if (status == 0) {
      VelSel_errcode = VELSEL__BAD_RECV;        /* Server exited (probably) */
      fprintf(stderr, "\nVelSel_SendCmnds/recv: probable network problem");
    } else if (status == -1) {
      if (VelSel_errno == EPIPE) {
        VelSel_errcode = VELSEL__BAD_RECV_PIPE; /* Server exited (probably) */
        fprintf(stderr, "\nVelSel_SendCmnds/recv: broken network pipe");
      } else {
        VelSel_errcode = VELSEL__BAD_RECV_NET;  /* It's some other net problem */
        perror("VelSel_SendCmnds/recv");
      }
    } else {
      VelSel_errcode = VELSEL__BAD_RECV_UNKN;   /* TCP/IP problems */
      fprintf(stderr, "\nVelSel_SendCmnds/recv: probable TCP/IP problem");
    }
    VelSel_Close(handle, True); /* Force close TCP/IP connection */
    fprintf(stderr, " - link to server force-closed.\n");
    return False;
  }
  if (sscanf(info_ptr->from_host.msg_size, "%4d", &bytes_to_come) != 1) {
    VelSel_errcode = VELSEL__BAD_NOT_BCD;       /* Header not an ASCII BCD integer */
    VelSel_Close(handle, True); /* Force close TCP/IP connection */
    fprintf(stderr, "\nVelSel_SendCmnds/recv: non-BCD byte count"
            " - link to server force-closed.\n");
    return False;
  }
  max_size = sizeof(info_ptr->from_host) -
      sizeof(info_ptr->from_host.msg_size);
  if (bytes_to_come > max_size) {
    VelSel_errcode = VELSEL__BAD_RECVLEN;
    fprintf(stderr,
            "\nVelSel_SendCmnds/recv: pending message length too big"
            " - flushing ...\n");
    nxt_byte_ptr = &info_ptr->from_host.msg_size[size];
    while (bytes_to_come > 0) { /* Flush out the incoming message */
      bytes_left = bytes_to_come;
      if (bytes_left > max_size)
        bytes_left = max_size;
      status = recv(info_ptr->asyn_info.skt, nxt_byte_ptr, bytes_left, 0);
      if (status <= 0) {
        VelSel_errcode = VELSEL__BAD_FLUSH;     /* TCP/IP problem whilst flushing */
        GetErrno(&VelSel_errno, &VelSel_vaxc_errno);
        VelSel_Close(handle, True);     /* Force close TCP/IP connection */
        fprintf(stderr, "\nVelSel_SendCmnds/recv: network problem during"
                " flush.\nLink to server force-closed.\n");
        return False;
      }
      bytes_to_come = bytes_to_come - status;
    }
    fprintf(stderr, "\n flushed OK.\n");
    memset(info_ptr->from_host.msg_size,
           '0', sizeof(info_ptr->from_host.msg_size));
    return False;
  } else {
    nxt_byte_ptr = &info_ptr->from_host.msg_size[size];
    bytes_left = bytes_to_come;
    while (bytes_left > 0) {    /* Read the rest of the response */
      status = recv(info_ptr->asyn_info.skt, nxt_byte_ptr, bytes_left, 0);
      if (status <= 0) {
        GetErrno(&VelSel_errno, &VelSel_vaxc_errno);
        if (status == 0) {
          VelSel_errcode = VELSEL__BAD_RECV1;   /* Server exited (probably) */
          fprintf(stderr, "\nVelSel_SendCmnds/recv/1: probable network "
                  "problem");
        } else {
          if (VelSel_errno == EPIPE) {
            VelSel_errcode = VELSEL__BAD_RECV1_PIPE;    /* Server exited (probably) */
            fprintf(stderr,
                    "\nVelSel_SendCmnds/recv/1: broken network pipe");
          } else {
            VelSel_errcode = VELSEL__BAD_RECV1_NET;     /* It's some other net fault */
            perror("VelSel_SendCmnds/recv/1");
          }
        }
        VelSel_Close(handle, True);     /* Force close TCP/IP connection */
        fprintf(stderr, " - link to server force-closed.\n");
        return False;
      }
      bytes_left = bytes_left - status;
      nxt_byte_ptr = nxt_byte_ptr + status;
    }
    if ((sscanf(info_ptr->from_host.n_rply, "%4d",
                &info_ptr->max_replies) != 1) ||
        (info_ptr->max_replies < 0)) {
      VelSel_errcode = VELSEL__BAD_REPLY;       /* Reply is bad */
      if (VelSel_call_depth < 5) {      /* Add reply to routine stack */
        bytes_to_come = bytes_to_come + 4;
        if (bytes_to_come >= sizeof(VelSel_routine[0]))
          bytes_to_come = sizeof(VelSel_routine[0]) - 1;
        for (i = 0; i < bytes_to_come; i++) {
          if (info_ptr->from_host.msg_size[i] == '\0')
            info_ptr->from_host.msg_size[i] = '.';
        }
        info_ptr->from_host.msg_size[bytes_to_come] = '\0';
        strcpy(VelSel_routine[VelSel_call_depth],
               info_ptr->from_host.msg_size);
        VelSel_call_depth++;
      }
      return False;
    }
  }
  VelSel_call_depth--;
  return True;
}

/*-------------------------------------------- End of VelSel_Utility.C =======*/
