- reworked states (removed substate)

- leave handler after every character read
This commit is contained in:
zolliker
2009-02-25 14:44:26 +00:00
parent 2006a2cf7d
commit 2fd07071d9
2 changed files with 81 additions and 80 deletions

94
ascon.c
View File

@ -65,15 +65,23 @@ double DoubleTime(void)
void AsconError(Ascon *a, char *msg, int errorno) void AsconError(Ascon *a, char *msg, int errorno)
{ {
static char *stateText[]={ static char *stateText[]={
"state 0", "state 1", "state 2", "notConnected", "not connected",
"connect", "start connect", "connect finished", "connect failed", "connect start",
"write", "start write", "write finished", "write failed", "connecting",
"read", "start read", "read finished", "read failed", "connect done",
"state 16", "state 17", "state 18", "idle" "write start",
"writing",
"write done",
"read start",
"reading",
"read done",
"idle",
"failed",
"timeout"
}; };
char *state; char *state;
if (a->state < 0 || a->state > 19) { if (a->state < 0 || a->state >= AsconMaxState) {
state = "bad state"; state = "bad state";
} else { } else {
state = stateText[a->state]; state = stateText[a->state];
@ -92,7 +100,7 @@ void AsconError(Ascon *a, char *msg, int errorno)
DynStringConcat(a->errmsg, msg); DynStringConcat(a->errmsg, msg);
DynStringConcat(a->errmsg, ")"); DynStringConcat(a->errmsg, ")");
} }
a->state |= AsconFailed; a->state = AsconFailed;
} }
static void AsconConnect(Ascon * a) static void AsconConnect(Ascon * a)
@ -144,7 +152,7 @@ static void AsconConnect(Ascon * a)
ret = ret =
connect(a->fd, (struct sockaddr *) &adr, sizeof(struct sockaddr_in)); connect(a->fd, (struct sockaddr *) &adr, sizeof(struct sockaddr_in));
if (ret < 0) { if (ret < 0) {
switch(errno) { switch (errno) {
case EINPROGRESS: case EINPROGRESS:
case EALREADY: case EALREADY:
case EISCONN: case EISCONN:
@ -359,11 +367,7 @@ int AsconStdHandler(Ascon * a)
l = GetDynStringLength(a->wrBuffer) - a->wrPos; l = GetDynStringLength(a->wrBuffer) - a->wrPos;
ret = AsconWriteChars(a->fd, GetCharArray(a->wrBuffer) + a->wrPos, l); ret = AsconWriteChars(a->fd, GetCharArray(a->wrBuffer) + a->wrPos, l);
if (ret < 0) { if (ret < 0) {
AsconError(a, "ASC4", errno); AsconError(a, "ASC4", errno); /* sets state to AsconFailed */
/*
* Ooops: which state shall we go to after a write fail?
* This seems to retry.
*/
} else { } else {
a->wrPos += ret; a->wrPos += ret;
if (a->wrPos >= GetDynStringLength(a->wrBuffer)) { if (a->wrPos >= GetDynStringLength(a->wrBuffer)) {
@ -378,7 +382,7 @@ int AsconStdHandler(Ascon * a)
break; break;
case AsconReading: case AsconReading:
ret = AsconReadChar(a->fd, &chr); ret = AsconReadChar(a->fd, &chr);
while (ret > 0) { if (ret > 0) {
a->start = DoubleTime(); a->start = DoubleTime();
if (a->replyTerminator != NULL) { if (a->replyTerminator != NULL) {
@ -413,25 +417,22 @@ int AsconStdHandler(Ascon * a)
} }
a->readState = 0; a->readState = 0;
} }
ret = AsconReadChar(a->fd, &chr);
} }
if (ret < 0) { if (ret < 0) {
/* EINTR means we shall retry */
if (errno != EINTR && errno != EAGAIN) {
AsconError(a, "ASC5", errno); AsconError(a, "ASC5", errno);
}
return 1; return 1;
} }
if (a->state == AsconReadDone) { if (a->state == AsconReadDone) {
DynStringConcatChar(a->rdBuffer, '\0'); DynStringConcatChar(a->rdBuffer, '\0');
} else { } else if (ret > 0) {
return 0; /* characater read: recycle */
}
if (a->timeout > 0) { if (a->timeout > 0) {
if (DoubleTime() - a->start > a->timeout) { if (DoubleTime() - a->start > a->timeout) {
AsconError(a, "no response", 0); AsconError(a, "no response", 0);
a->state = AsconTimeout; a->state = AsconTimeout;
} }
} }
}
break; break;
default: default:
break; break;
@ -539,21 +540,22 @@ void AsconDisconnect(Ascon * a)
AsconStatus AsconTask(Ascon * a) AsconStatus AsconTask(Ascon * a)
{ {
int result;
double now; double now;
while (a->handler(a)) { while (1) {
result = a->handler(a);
switch (a->state) { switch (a->state) {
case AsconReading:
case AsconWriting:
return AsconPending;
case AsconNotConnected: case AsconNotConnected:
return AsconOffline; return AsconOffline;
break; case AsconConnecting:
return AsconUnconnected;
case AsconConnectDone: case AsconConnectDone:
a->state = AsconIdle; a->state = AsconIdle;
return AsconReady; return AsconReady;
case AsconWriteDone: case AsconWriteDone:
if (a->noResponse) { if (a->noResponse) {
a->state = AsconIdle;
return AsconReady; return AsconReady;
} }
a->state = AsconReadStart; a->state = AsconReadStart;
@ -562,39 +564,39 @@ AsconStatus AsconTask(Ascon * a)
a->state = AsconIdle; a->state = AsconIdle;
a->responseValid = 1; a->responseValid = 1;
return AsconReady; return AsconReady;
case AsconConnecting: case AsconIdle:
return AsconUnconnected; return AsconReady;
default: case AsconTimeout:
switch (a->state % 4) {
case AsconOnTheWay:
case AsconStart:
return AsconPending;
case AsconFailed:
if (a->state == AsconTimeout) {
a->state = AsconIdle; a->state = AsconIdle;
} else { return AsconFailure;
case AsconFailed:
now = DoubleTime();
if (a->fd > 0) {
close(a->fd); close(a->fd);
lastClose = DoubleTime(); lastClose = now;
a->fd = -1; a->fd = -1;
}
if (now > a->lastReconnect + a->reconnectInterval) {
a->lastReconnect = now;
a->state = AsconConnectStart; a->state = AsconConnectStart;
} }
return AsconFailure; return AsconFailure;
case AsconFinished: default:
if (a->state < AsconConnectFailed) { if (result) {
return AsconUnconnected; return AsconPending;
} }
return AsconReady; break;
} }
} }
}
return AsconIdle;
} }
int AsconWrite(Ascon * a, char *command, int noResponse) int AsconWrite(Ascon * a, char *command, int noResponse)
{ {
if (a->state <= AsconConnectFailed || a->state % 4 < AsconFinished) if (a->state != AsconIdle) {
/* this might happen if a script is sending after an error */
return 0; return 0;
}
DynStringCopy(a->wrBuffer, command); DynStringCopy(a->wrBuffer, command);
a->noResponse = noResponse; a->noResponse = noResponse;
a->state = AsconWriteStart; a->state = AsconWriteStart;
@ -609,9 +611,9 @@ char *AsconRead(Ascon * a)
a->noResponse = 0; a->noResponse = 0;
return ""; return "";
} }
if (a->state % 4 == AsconFailed) { if (a->state != AsconIdle) {
a->state = AsconIdle; a->state = AsconIdle;
return ""; return "programming error in devser.c/ascon.c";
} }
if (a->responseValid) { if (a->responseValid) {
a->responseValid = 0; a->responseValid = 0;

43
ascon.i
View File

@ -22,28 +22,23 @@
*/ */
/** /**
* A sub-state of the connection. Only states with sub-state AsconStart may * The state of the connection.
* be set by the caller, and only when the sub-state is not AsconOnTheWay
*/
typedef enum { AsconOnTheWay=0, AsconStart=1, AsconFinished=2, AsconFailed=3 } AsconMode;
/**
* The state of the connection. The sub-state is state % 4.
*/ */
typedef enum { typedef enum {
AsconNotConnected=0+AsconFinished, AsconNotConnected, /**< unconnected, not to be connected automatically */
AsconConnecting=4+AsconOnTheWay, AsconConnectStart, /**< after initialisation or after AsconFailed */
AsconConnectStart=AsconConnecting+AsconStart, AsconConnecting, /**< after AsconConnectStart or AsconConnecting */
AsconConnectDone=AsconConnecting+AsconFinished, AsconConnectDone, /**< after AsconConnecting */
AsconConnectFailed=AsconConnecting+AsconFailed, AsconWriteStart, /**< set by the AsconWrite function */
AsconWriting=8+AsconOnTheWay, AsconWriting, /**< after AsconWriteStart or AsconWriting */
AsconWriteStart=AsconWriting+AsconStart, AsconWriteDone, /**< after AsconWriting */
AsconWriteDone=AsconWriting+AsconFinished, AsconReadStart, /**< after AsconWriteDone */
AsconReading=12+AsconOnTheWay, AsconReading, /**< after AsconReadStart or AsconReading */
AsconReadStart=AsconReading+AsconStart, AsconReadDone, /**< after AsconReading */
AsconReadDone=AsconReading+AsconFinished, AsconIdle, /**< after AsconWriteDone, AsconReadDone, AsconTimeout, AsconIdle */
AsconIdle=16+AsconFinished, AsconFailed, /**< after any state */
AsconTimeout=20 + AsconFailed AsconTimeout, /**< after AsconReading */
AsconMaxState /**< number of states */
} AsconState; } AsconState;
/** \brief the task handler function prototype /** \brief the task handler function prototype
@ -74,6 +69,7 @@ struct Ascon {
int responseValid; /**< a valid response is ready */ int responseValid; /**< a valid response is ready */
AsconHandler handler; /**< handler function */ AsconHandler handler; /**< handler function */
double reconnectInterval; /**< reconnect interval */ double reconnectInterval; /**< reconnect interval */
double lastReconnect; /**< last reconnect try */
}; };
#define ASCON_SELECT_ERROR -1 #define ASCON_SELECT_ERROR -1
@ -83,7 +79,10 @@ struct Ascon {
/** \brief the standard handler routine. /** \brief the standard handler routine.
* \param a the connection * \param a the connection
* \return 0 when task has finished (connection to be closed), 1 when active * \return 0 when it makes sense to call the handler again immediately
* (for example while reading, after a character was read, as
* there may be more chars in the buffer)
* 1 else
* *
* In most cases a custom handler may be a wrapper around AsconStdHandler * In most cases a custom handler may be a wrapper around AsconStdHandler
*/ */
@ -155,7 +154,7 @@ int AsconWriteChars(int fd, char *data, int length);
* \param a The asynchronous I/O structure to store the * \param a The asynchronous I/O structure to store the
* error with * error with
* \param msg The error message * \param msg The error message
* \param errorno The error number * \param errorno The error number or 0 for a custom error
*/ */
void AsconError(Ascon *a, char *msg, int errorno); void AsconError(Ascon *a, char *msg, int errorno);