- reworked states (removed substate)
- leave handler after every character read
This commit is contained in:
94
ascon.c
94
ascon.c
@ -65,15 +65,23 @@ double DoubleTime(void)
|
||||
void AsconError(Ascon *a, char *msg, int errorno)
|
||||
{
|
||||
static char *stateText[]={
|
||||
"state 0", "state 1", "state 2", "notConnected",
|
||||
"connect", "start connect", "connect finished", "connect failed",
|
||||
"write", "start write", "write finished", "write failed",
|
||||
"read", "start read", "read finished", "read failed",
|
||||
"state 16", "state 17", "state 18", "idle"
|
||||
"not connected",
|
||||
"connect start",
|
||||
"connecting",
|
||||
"connect done",
|
||||
"write start",
|
||||
"writing",
|
||||
"write done",
|
||||
"read start",
|
||||
"reading",
|
||||
"read done",
|
||||
"idle",
|
||||
"failed",
|
||||
"timeout"
|
||||
};
|
||||
char *state;
|
||||
|
||||
if (a->state < 0 || a->state > 19) {
|
||||
if (a->state < 0 || a->state >= AsconMaxState) {
|
||||
state = "bad state";
|
||||
} else {
|
||||
state = stateText[a->state];
|
||||
@ -92,7 +100,7 @@ void AsconError(Ascon *a, char *msg, int errorno)
|
||||
DynStringConcat(a->errmsg, msg);
|
||||
DynStringConcat(a->errmsg, ")");
|
||||
}
|
||||
a->state |= AsconFailed;
|
||||
a->state = AsconFailed;
|
||||
}
|
||||
|
||||
static void AsconConnect(Ascon * a)
|
||||
@ -144,7 +152,7 @@ static void AsconConnect(Ascon * a)
|
||||
ret =
|
||||
connect(a->fd, (struct sockaddr *) &adr, sizeof(struct sockaddr_in));
|
||||
if (ret < 0) {
|
||||
switch(errno) {
|
||||
switch (errno) {
|
||||
case EINPROGRESS:
|
||||
case EALREADY:
|
||||
case EISCONN:
|
||||
@ -359,11 +367,7 @@ int AsconStdHandler(Ascon * a)
|
||||
l = GetDynStringLength(a->wrBuffer) - a->wrPos;
|
||||
ret = AsconWriteChars(a->fd, GetCharArray(a->wrBuffer) + a->wrPos, l);
|
||||
if (ret < 0) {
|
||||
AsconError(a, "ASC4", errno);
|
||||
/*
|
||||
* Ooops: which state shall we go to after a write fail?
|
||||
* This seems to retry.
|
||||
*/
|
||||
AsconError(a, "ASC4", errno); /* sets state to AsconFailed */
|
||||
} else {
|
||||
a->wrPos += ret;
|
||||
if (a->wrPos >= GetDynStringLength(a->wrBuffer)) {
|
||||
@ -378,7 +382,7 @@ int AsconStdHandler(Ascon * a)
|
||||
break;
|
||||
case AsconReading:
|
||||
ret = AsconReadChar(a->fd, &chr);
|
||||
while (ret > 0) {
|
||||
if (ret > 0) {
|
||||
a->start = DoubleTime();
|
||||
|
||||
if (a->replyTerminator != NULL) {
|
||||
@ -413,25 +417,22 @@ int AsconStdHandler(Ascon * a)
|
||||
}
|
||||
a->readState = 0;
|
||||
}
|
||||
ret = AsconReadChar(a->fd, &chr);
|
||||
}
|
||||
if (ret < 0) {
|
||||
/* EINTR means we shall retry */
|
||||
if (errno != EINTR && errno != EAGAIN) {
|
||||
AsconError(a, "ASC5", errno);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
if (a->state == AsconReadDone) {
|
||||
DynStringConcatChar(a->rdBuffer, '\0');
|
||||
} else {
|
||||
} else if (ret > 0) {
|
||||
return 0; /* characater read: recycle */
|
||||
}
|
||||
if (a->timeout > 0) {
|
||||
if (DoubleTime() - a->start > a->timeout) {
|
||||
AsconError(a, "no response", 0);
|
||||
a->state = AsconTimeout;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
@ -539,21 +540,22 @@ void AsconDisconnect(Ascon * a)
|
||||
|
||||
AsconStatus AsconTask(Ascon * a)
|
||||
{
|
||||
int result;
|
||||
double now;
|
||||
|
||||
while (a->handler(a)) {
|
||||
while (1) {
|
||||
result = a->handler(a);
|
||||
switch (a->state) {
|
||||
case AsconReading:
|
||||
case AsconWriting:
|
||||
return AsconPending;
|
||||
case AsconNotConnected:
|
||||
return AsconOffline;
|
||||
break;
|
||||
case AsconConnecting:
|
||||
return AsconUnconnected;
|
||||
case AsconConnectDone:
|
||||
a->state = AsconIdle;
|
||||
return AsconReady;
|
||||
case AsconWriteDone:
|
||||
if (a->noResponse) {
|
||||
a->state = AsconIdle;
|
||||
return AsconReady;
|
||||
}
|
||||
a->state = AsconReadStart;
|
||||
@ -562,39 +564,39 @@ AsconStatus AsconTask(Ascon * a)
|
||||
a->state = AsconIdle;
|
||||
a->responseValid = 1;
|
||||
return AsconReady;
|
||||
case AsconConnecting:
|
||||
return AsconUnconnected;
|
||||
default:
|
||||
switch (a->state % 4) {
|
||||
case AsconOnTheWay:
|
||||
case AsconStart:
|
||||
return AsconPending;
|
||||
case AsconFailed:
|
||||
if (a->state == AsconTimeout) {
|
||||
case AsconIdle:
|
||||
return AsconReady;
|
||||
case AsconTimeout:
|
||||
a->state = AsconIdle;
|
||||
} else {
|
||||
return AsconFailure;
|
||||
case AsconFailed:
|
||||
now = DoubleTime();
|
||||
if (a->fd > 0) {
|
||||
close(a->fd);
|
||||
lastClose = DoubleTime();
|
||||
lastClose = now;
|
||||
a->fd = -1;
|
||||
}
|
||||
if (now > a->lastReconnect + a->reconnectInterval) {
|
||||
a->lastReconnect = now;
|
||||
a->state = AsconConnectStart;
|
||||
}
|
||||
|
||||
return AsconFailure;
|
||||
case AsconFinished:
|
||||
if (a->state < AsconConnectFailed) {
|
||||
return AsconUnconnected;
|
||||
default:
|
||||
if (result) {
|
||||
return AsconPending;
|
||||
}
|
||||
return AsconReady;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return AsconIdle;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
DynStringCopy(a->wrBuffer, command);
|
||||
a->noResponse = noResponse;
|
||||
a->state = AsconWriteStart;
|
||||
@ -609,9 +611,9 @@ char *AsconRead(Ascon * a)
|
||||
a->noResponse = 0;
|
||||
return "";
|
||||
}
|
||||
if (a->state % 4 == AsconFailed) {
|
||||
if (a->state != AsconIdle) {
|
||||
a->state = AsconIdle;
|
||||
return "";
|
||||
return "programming error in devser.c/ascon.c";
|
||||
}
|
||||
if (a->responseValid) {
|
||||
a->responseValid = 0;
|
||||
|
43
ascon.i
43
ascon.i
@ -22,28 +22,23 @@
|
||||
*/
|
||||
|
||||
/**
|
||||
* A sub-state of the connection. Only states with sub-state AsconStart may
|
||||
* 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.
|
||||
* The state of the connection.
|
||||
*/
|
||||
typedef enum {
|
||||
AsconNotConnected=0+AsconFinished,
|
||||
AsconConnecting=4+AsconOnTheWay,
|
||||
AsconConnectStart=AsconConnecting+AsconStart,
|
||||
AsconConnectDone=AsconConnecting+AsconFinished,
|
||||
AsconConnectFailed=AsconConnecting+AsconFailed,
|
||||
AsconWriting=8+AsconOnTheWay,
|
||||
AsconWriteStart=AsconWriting+AsconStart,
|
||||
AsconWriteDone=AsconWriting+AsconFinished,
|
||||
AsconReading=12+AsconOnTheWay,
|
||||
AsconReadStart=AsconReading+AsconStart,
|
||||
AsconReadDone=AsconReading+AsconFinished,
|
||||
AsconIdle=16+AsconFinished,
|
||||
AsconTimeout=20 + AsconFailed
|
||||
AsconNotConnected, /**< unconnected, not to be connected automatically */
|
||||
AsconConnectStart, /**< after initialisation or after AsconFailed */
|
||||
AsconConnecting, /**< after AsconConnectStart or AsconConnecting */
|
||||
AsconConnectDone, /**< after AsconConnecting */
|
||||
AsconWriteStart, /**< set by the AsconWrite function */
|
||||
AsconWriting, /**< after AsconWriteStart or AsconWriting */
|
||||
AsconWriteDone, /**< after AsconWriting */
|
||||
AsconReadStart, /**< after AsconWriteDone */
|
||||
AsconReading, /**< after AsconReadStart or AsconReading */
|
||||
AsconReadDone, /**< after AsconReading */
|
||||
AsconIdle, /**< after AsconWriteDone, AsconReadDone, AsconTimeout, AsconIdle */
|
||||
AsconFailed, /**< after any state */
|
||||
AsconTimeout, /**< after AsconReading */
|
||||
AsconMaxState /**< number of states */
|
||||
} AsconState;
|
||||
|
||||
/** \brief the task handler function prototype
|
||||
@ -74,6 +69,7 @@ struct Ascon {
|
||||
int responseValid; /**< a valid response is ready */
|
||||
AsconHandler handler; /**< handler function */
|
||||
double reconnectInterval; /**< reconnect interval */
|
||||
double lastReconnect; /**< last reconnect try */
|
||||
};
|
||||
|
||||
#define ASCON_SELECT_ERROR -1
|
||||
@ -83,7 +79,10 @@ struct Ascon {
|
||||
|
||||
/** \brief the standard handler routine.
|
||||
* \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
|
||||
*/
|
||||
@ -155,7 +154,7 @@ int AsconWriteChars(int fd, char *data, int length);
|
||||
* \param a The asynchronous I/O structure to store the
|
||||
* error with
|
||||
* \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);
|
||||
|
||||
|
Reference in New Issue
Block a user