From f84960d4a530ddc94767c93c4f3e9ebe68b9b914 Mon Sep 17 00:00:00 2001 From: Douglas Clowes Date: Thu, 23 Nov 2006 13:38:21 +1100 Subject: [PATCH] Add TCP socket disconnection discovery and reconnection attempt. r1317 | dcl | 2006-11-23 13:38:21 +1100 (Thu, 23 Nov 2006) | 2 lines --- site_ansto/counterdriv.c | 251 ++++++++++++++++++++++++++++++++++----- 1 file changed, 218 insertions(+), 33 deletions(-) diff --git a/site_ansto/counterdriv.c b/site_ansto/counterdriv.c index dea76d98..884e0617 100644 --- a/site_ansto/counterdriv.c +++ b/site_ansto/counterdriv.c @@ -10,7 +10,10 @@ * */ #include +#include +#include #include +#include #include #include #include @@ -66,27 +69,170 @@ typedef struct { * Logs text to a debug file. The file is opened and closed for each call. * * \param flag character used to distinguish calls - * \param pText pointer to text to be logged + * \param format printf style format string + * \param ... printf style variable argument list */ -static void flog(char flag, char* pText) { +static void flog(char flag, char* format, ...) { + va_list ap; FILE* file; + int iRet; + char buffer[2048]; + file = fopen("flog.txt", "a"); if (file) { struct timeval tv; (void) gettimeofday(&tv, NULL); - fprintf(file, "%02d:%02d:%02d:%06d::%c::%s", + fprintf(file, "%02d:%02d:%02d:%06d::%c::", tv.tv_sec % (24 * 3600) / 3600, tv.tv_sec % (3600) / 60, tv.tv_sec % 60, tv.tv_usec, - flag, - pText); - if (strchr(pText, '\n') == NULL) + flag); + va_start(ap, format); + iRet = vsnprintf(buffer, sizeof(buffer), format, ap); + va_end(ap); + fputs(buffer, file); + if (buffer[iRet - 1] != '\n') (void) fputc('\n', file); (void) fclose(file); } } +/** \brief Check connection for remote disconnection + * + * \param self provides access to a monitor's data + * \return SUCCESS or FAILURE + */ +static int checkConnection(pBeamMon self) +{ + int status; + char buf[1]; + + /* + * Make sure that it the structure looks reasonable + */ + if (self && self->controller && self->controller->pSock) { + int sock; + /* + * Make sure that the socket looks reasonable + */ + sock = self->controller->pSock->sockid; + if (sock < 0 || sock >= FD_SETSIZE) { + self->errorCode = NOTCONNECTED; + return FAILURE; + } + /* + * Q: How do you tell that a connection is broken? + * A: Read it or Write it and see what happens! + */ + if (availableRS232(self->controller) != 0) { + status = recv(sock, buf, 1, MSG_PEEK); + if (status < 0) { + if (errno == EAGAIN) { + /* no data to read, otherwise OK */ + return SUCCESS; + } + flog('#', "counter recv = %d, errno = %d:%s\n", status, errno, strerror(errno)); + flog('#', "Channel Error!"); + self->errorCode = BADREAD; + return FAILURE; + } else if (status == 0) { + flog('#', "Channel Disconnected!"); + self->errorCode = NOTCONNECTED; + return FAILURE; + } + } else { + status = send(sock, NULL, 0, 0); + if (status < 0) { + if (errno == EAGAIN) { + flog('#', "Unexpected EAGAIN\n"); + return SUCCESS; + } else if (errno == EPIPE) { + flog('#', "Unexpected EPIPE\n"); + self->errorCode = NOTCONNECTED; + return FAILURE; + } else { + flog('#', "Unexpected error %d: %s\n", errno, strerror(errno)); + self->errorCode = NOTCONNECTED; + return FAILURE; + } + } + } + /* otherwise OK */ + return SUCCESS; + } + /* something is not connected */ + self->errorCode = NOTCONNECTED; + return FAILURE; +} + +/** \brief try to fix connection for remote disconnection + * + * \param self provides access to a monitor's data + * \return SUCCESS or FAILURE + */ +static int fixConnection(pBeamMon self) +{ + int status; + + flog('_', "fixConnection"); + if (self && self->controller) { + if (self->errorCode != NOTCONNECTED) { + flog('!', "unbroken"); + return FAILURE; + } + status = initRS232(self->controller); + if (status == SUCCESS) { + fd_set rmask, wmask; + struct timeval tmo = {1,0}; + int iret; + int sock = self->controller->pSock->sockid; + if (sock < 0 || sock > FD_SETSIZE) { + flog('!', "bad socket"); + return FAILURE; + } + FD_ZERO(&wmask); + FD_ZERO(&rmask); + FD_SET(sock, &wmask); + FD_SET(sock, &rmask); + iret = select(sock+1, &rmask, &wmask, NULL, &tmo); + if (iret < 0) { + flog('!', "%s", strerror(errno)); + return FAILURE; /* some sort of error */ + } else if (iret == 0) { + flog('!', "timeout"); + return FAILURE; /* timed out */ + } else { + if (FD_ISSET(sock, &wmask)) { /* should always be true */ + if (FD_ISSET(sock, &rmask)) { /* there may already be data for read */ + char buff[1]; + iret = recv(sock, buff, 1, MSG_PEEK); /* just peek */ + if (iret == 0 || (iret < 0 && errno != EAGAIN)) { + flog('!', "disconnected"); + return FAILURE; /* disconnected */ + } + } + iret = send(sock, NULL, 0, 0); /* zero length, check only return value */ + if (iret < 0) { + flog('!', "%s", strerror(errno)); + return FAILURE; /* some sort of error */ + } + } + flog('!', "success"); + return SUCCESS; + } + } else { + flog('!', "initRS232 failed"); + return FAILURE; + } + } else { + flog('!', "no initRS232 controller"); + return FAILURE; + } + flog('!', "fallthrough"); + return FAILURE; +} + /** \brief Writes a line to the Monitor * used for sending commands. * @@ -100,11 +246,15 @@ static int MonWrite(pBeamMon self, char* text) { len = strlen(text); if (len > 0) - flog('>', text); + flog('>', "%s", text); status = writeRS232(self->controller, text, (int) len); if (status != 1) { + char str[80]; + flog('#', "MonWrite status = %d, errno = %d\n", status, errno); self->errorCode = status; + if (status == BADSEND && errno == EPIPE) + self->errorCode = NOTCONNECTED; return FAILURE; } return SUCCESS; @@ -119,18 +269,29 @@ static int MonWrite(pBeamMon self, char* text) { * \return SUCCESS or FAILURE */ static int MonRead(pBeamMon self, /*@out@*/ char* text, int *pLen) { - int i, status, retries=20; + int i, status, retries=2; + char str[80]; + char buf[1]; *text = '\0'; - for (i=0; icontroller, text, pLen); switch (status) { case 1: if (pLen > 0) - flog('<', text); + flog('<', "%s", text); return SUCCESS; case TIMEOUT: - flog('<', "*TIMEOUT*"); + /* + * Disconnection of the socket is treated as a timeout. + * + * Determine if this is really a socket diconnection. + */ + if (checkConnection(self) == FAILURE) { + return FAILURE; + } + + flog('<', "*TIMEOUT* %d", self->controller->timeout); self->errorCode = status; continue; default: @@ -139,7 +300,7 @@ static int MonRead(pBeamMon self, /*@out@*/ char* text, int *pLen) { return FAILURE; } } - flog('<', "***TIMEOUT***"); + flog('<', "***TIMEOUT*** %d", self->controller->timeout); return FAILURE; } @@ -176,7 +337,16 @@ static int MonSend(CounterDriver *cntrData, char *pText, char *pReply, int iRepl self = (BeamMon *) cntrData->pData; MonDrainInput(cntrData); + if (self->errorCode == NOTCONNECTED) { + status = fixConnection(self); + } status = MonWrite(self, pText); + /* try to recover the connection if it is broken */ + if (status != SUCCESS && self->errorCode == NOTCONNECTED) { + status = fixConnection(self); + if (status == SUCCESS) + status = MonWrite(self, pText); + } if (status != SUCCESS) return status; if ((status = MonRead(self, pReply, &iReplyLen)) == SUCCESS) @@ -244,13 +414,12 @@ static void HandleReport(CounterDriver *cntrData, BUFFER* bp) self->state = HWNoBeam; self->counter_value = strtoull(cp, &ep, 10); cntrData->fLastCurrent = (float) self->counter_value; - (void) snprintf(str, sizeof(str), "READ %s, %f, %f, %f\n", + flog('.', "READ %s, %f, %f, %f\n", cntrData->eMode == eTimer ? "eTimer" : cntrData->eMode == ePreset ? "ePreset" : "Unknown", cntrData->fPreset, cntrData->fLastCurrent, cntrData->fTime); - flog('.', str); } /** \brief handles an input line received on the RS232 channel @@ -269,8 +438,7 @@ static void MonHandleInput(CounterDriver *cntrData, BUFFER* bp) { const char rpt[] = "READ"; self = (BeamMon *) cntrData->pData; - /* TODO handle the line */ - flog('=', bp->body); + flog('=', "%s", bp->body); if (strncasecmp(bp->body, "EVENT", 5) == 0) { char* cp = bp->body + 5; while (*cp && isspace(*cp)) // skip space @@ -361,8 +529,9 @@ static int MonGetStatus(CounterDriver *cntrData, float *fControl) { MonLookForInput(cntrData, 0); status = MonSendBuffer(cntrData, "SICS READ", &self->buffer); if (status == SUCCESS) { - /* TODO */ MonHandleInput(cntrData, &self->buffer); + } else { + /* TODO */ } if (cntrData->eMode == eTimer) *fControl = cntrData->fTime; @@ -397,13 +566,12 @@ static int MonStart(CounterDriver *cntrData) { BeamMon *self = NULL; int status; char str[100]; - (void) snprintf(str, sizeof(str), "START %s, %f, %f, %f\n", + flog('.', "START %s, %f, %f, %f\n", cntrData->eMode == eTimer ? "eTimer" : cntrData->eMode == ePreset ? "ePreset" : "Unknown", cntrData->fPreset, cntrData->fLastCurrent, cntrData->fTime); - flog('.', str); self = (BeamMon *) cntrData->pData; MonDrainInput(cntrData); if (cntrData->eMode == eTimer) { @@ -531,26 +699,42 @@ static int MonReadValues(CounterDriver *cntrData) { */ static int MonGetError(CounterDriver *cntrData, int *iCode, char *error, int iErrLen) { BeamMon *self = NULL; + char str[40]; - flog('.', "MonGetError"); self = (BeamMon *) cntrData->pData; /* Allocate iErrLen bytes for error messages */ - if (self->errorMsg == NULL) { - self->errorMsg = (char *) malloc((size_t) iErrLen); - if (self->errorMsg == NULL) { - return FAILURE; - } + if (self->errorMsg != NULL) { + free(self->errorMsg); + self->errorMsg = NULL; } - error = self->errorMsg; *iCode = self->errorCode; + flog('.', "MonGetError: %d", self->errorCode); switch (*iCode) { case 0: + if (checkConnection(self) == FAILURE) { + if (self->errorCode == NOTCONNECTED) { + strncpy(error, + "DISCONNECT ERROR: counterdriv channel disconnected.", + (size_t) iErrLen); + } else { + strncpy(error, + "SOCKET ERROR: counterdriv socket in error.", + (size_t) iErrLen); + } + *iCode = self->errorCode; + break; + } strncpy(error, "UNKNOWN ERROR: counterdriv did not set an errorcode.", (size_t) iErrLen); break; case NOTCONNECTED: + strncpy(error, + "DISCONNECT ERROR: counterdriv channel not connected.", + (size_t) iErrLen); + break; case TIMEOUT: + case BADREAD: case BADSEND: case BADMEMORY: case INCOMPLETE: @@ -560,7 +744,11 @@ static int MonGetError(CounterDriver *cntrData, int *iCode, char *error, int iEr (void) snprintf(error, (size_t) iErrLen, "Unknown error code: %d, returned from counterdriv", *iCode); + break; } + flog('#', "MonGetError: %d", *iCode); + flog('#', "%s", error); + self->errorMsg = strdup(error); self->errorCode = 0; return SUCCESS; } @@ -587,6 +775,7 @@ static int MonTryAndFixIt(CounterDriver *cntrData, int iCode) { if (initRS232(self->controller) == 1) return COREDO; return COTERM; + case BADREAD: case BADSEND: case TIMEOUT: case BADMEMORY: /* Won't happen if MonConnect sets the send terminator */ @@ -604,9 +793,7 @@ static int MonTryAndFixIt(CounterDriver *cntrData, int iCode) { */ static int MonSet(CounterDriver *cntrData, char *name, int iCter, float fVal) { BeamMon *self = NULL; - char str[100]; - (void) snprintf(str, 100, "MonSet(%s, %d, %f)", name, iCter, fVal); - flog('.', str); + flog('.', "MonSet(%s, %d, %f)", name, iCter, fVal); self = (BeamMon *) cntrData->pData; if(strcmp(name,"threshold") == 0){ @@ -619,7 +806,6 @@ static int MonSet(CounterDriver *cntrData, char *name, int iCter, float fVal) { static int MonGet(CounterDriver *cntrData, char *name, int iCter, float *fVal) { BeamMon *self = NULL; - char str[100]; self = (BeamMon *) cntrData->pData; if(strcasecmp(name,"threshold") == 0){ @@ -628,8 +814,7 @@ static int MonGet(CounterDriver *cntrData, char *name, int iCter, float *fVal) { } /* TODO*/ - (void) snprintf(str, 100, "MonGet(%s, %d, %f)", name, iCter, *fVal); - flog('.', str); + flog('.', "MonGet(%s, %d, %f)", name, iCter, *fVal); return SUCCESS; } @@ -663,7 +848,7 @@ static void KillMon(/*@null@*/ pCounterDriver cntrData) { /*@null@*/ /*@only@*/ static prs232 MonConnect(/*@dependent@*/SConnection *pCon, char *host, int port) { prs232 controller=NULL; char pError[ERRLEN]; - int msecTimeout = 5000; /* 5000msec timeout */ + int msecTimeout = 1000; /* milliseconds timeout */ controller=createRS232(host,port); if (controller==NULL) {