- 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)
|
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
43
ascon.i
@ -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);
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user