#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 ** ** 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 #include #include #include #include #include #include #include #include #ifdef __VMS #include #else #include #endif /*-----------------------------------------------------------------*/ #include #include #include #include #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 =======*/