Add TCP socket disconnection discovery and reconnection attempt.

r1317 | dcl | 2006-11-23 13:38:21 +1100 (Thu, 23 Nov 2006) | 2 lines
This commit is contained in:
Douglas Clowes
2006-11-23 13:38:21 +11:00
parent f694c7a91c
commit f84960d4a5

View File

@@ -10,7 +10,10 @@
*
*/
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <assert.h>
#include <errno.h>
#include <math.h>
#include <time.h>
#include <sys/time.h>
@@ -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; i<retries; i++) {
for (i = 0; i <= retries; i++) {
status = readRS232TillTerm(self->controller, 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) {