From 2fd07071d955e398b5678932474988fb1db6f3e2 Mon Sep 17 00:00:00 2001 From: zolliker Date: Wed, 25 Feb 2009 14:44:26 +0000 Subject: [PATCH] - reworked states (removed substate) - leave handler after every character read --- ascon.c | 118 ++++++++++++++++++++++++++++---------------------------- ascon.i | 43 ++++++++++----------- 2 files changed, 81 insertions(+), 80 deletions(-) diff --git a/ascon.c b/ascon.c index 8516ad43..bd74575a 100644 --- a/ascon.c +++ b/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,23 +417,20 @@ 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); - } + AsconError(a, "ASC5", errno); return 1; } if (a->state == AsconReadDone) { DynStringConcatChar(a->rdBuffer, '\0'); - } else { - if (a->timeout > 0) { - if (DoubleTime() - a->start > a->timeout) { - AsconError(a, "no response", 0); - a->state = AsconTimeout; - } + } 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; @@ -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) { - a->state = AsconIdle; - } else { - close(a->fd); - lastClose = DoubleTime(); - a->fd = -1; - a->state = AsconConnectStart; - } - - return AsconFailure; - case AsconFinished: - if (a->state < AsconConnectFailed) { - return AsconUnconnected; - } - return AsconReady; + case AsconIdle: + return AsconReady; + case AsconTimeout: + a->state = AsconIdle; + return AsconFailure; + case AsconFailed: + now = DoubleTime(); + if (a->fd > 0) { + close(a->fd); + lastClose = now; + a->fd = -1; } + if (now > a->lastReconnect + a->reconnectInterval) { + a->lastReconnect = now; + a->state = AsconConnectStart; + } + return AsconFailure; + default: + if (result) { + return AsconPending; + } + 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; diff --git a/ascon.i b/ascon.i index cfc9a9ab..18a8fb16 100644 --- a/ascon.i +++ b/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);