Reworked AsyncQueue sendCommand processing
Squashed commit of the following: commit 42fb7d3cde591d40060cc740ccbc47f1ae7a5a50 Author: Douglas Clowes <dcl@ansto.gov.au> Date: Tue Aug 26 13:31:11 2014 +1000 Get the MODBUS_AP working commit da785c1434a04c4186d4174eb2dfbaefc850c8e7 Author: Douglas Clowes <dcl@ansto.gov.au> Date: Mon Aug 25 18:01:50 2014 +1000 Bring Modbus protocol closer to Huber, Knauer and Omron commit ef06ed7b6911cb49b35c19fe73e55f7c57cfd049 Author: Douglas Clowes <dcl@ansto.gov.au> Date: Mon Aug 25 18:01:18 2014 +1000 Make Huber, Knauer and Omron protocols more aligned (diffable) commit 3ef1bb06b3f865502ad7dffc4bf5dba4814d9334 Author: Douglas Clowes <dcl@ansto.gov.au> Date: Fri Aug 22 17:47:50 2014 +1000 Get the Huber and Knauer protocols to be more alike commit 2c9932e83f6735e894278648afdcadece654d43b Author: Douglas Clowes <dcl@ansto.gov.au> Date: Fri Aug 22 17:12:31 2014 +1000 Clean up the Knauer dual-mode protocol and refactor commit 333300b19b0e61916e261300ac6ae2b6bab5df09 Author: Douglas Clowes <dcl@ansto.gov.au> Date: Thu Aug 21 15:38:39 2014 +1000 Get the Knauer dual-mode protocol working(-ish) commit b1f9d82f1b9eb8a1ff54694adc3482984b0d3d72 Author: Douglas Clowes <dcl@ansto.gov.au> Date: Thu Aug 21 15:37:44 2014 +1000 Make private functions static (and not duplicated) commit 0b077414eef9e4351956a2b971d7751cced0d3cd Author: Douglas Clowes <dcl@ansto.gov.au> Date: Thu Aug 21 12:46:10 2014 +1000 Knauer moving toward dual protocol commit 13199bea38a1595ce06923e83474b738b10db94d Author: Douglas Clowes <dcl@ansto.gov.au> Date: Thu Aug 21 12:42:48 2014 +1000 Restructure default sendCommand processing in asyncqueue commit 99a8ea3174ca0636503b0ce0cdb6016790315558 Author: Douglas Clowes <dcl@ansto.gov.au> Date: Thu Aug 21 09:48:50 2014 +1000 Add a Modbus Protocol handler derived from sct_tcpmodbus commit 3adf49fb7c8402c8260a0bb20729d551ac88537b Author: Douglas Clowes <dcl@ansto.gov.au> Date: Thu Aug 21 09:43:54 2014 +1000 Leave the free of private data to the asyncqueue mechanism
This commit is contained in:
@@ -1,105 +1,175 @@
|
||||
#include "knauer_asyncprotocol.h"
|
||||
#include "asyncprotocol.h"
|
||||
#include "asyncqueue.h"
|
||||
#include "ascon.i"
|
||||
#include "dynstring.h"
|
||||
#include <errno.h>
|
||||
|
||||
#define SYN1 0x55
|
||||
#define SYN2 0xAA
|
||||
#define CR '\r'
|
||||
#define LF '\n'
|
||||
#define PROTOCOL_CONTINUE 0
|
||||
#define PROTOCOL_COMPLETE 1
|
||||
#define PROTOCOL_ERROR (-1)
|
||||
|
||||
/* Sample Messages
|
||||
55 aa 00 08 52 45 4d 4f 54 45 3a 30
|
||||
55 aa 00 16 45 52 52 4f 52 3a 34 38 2c 4e 6f 74 20 73 75 70 70 6f 72 74 65 64
|
||||
55 aa 00 18 45 52 52 4f 52 3a 31 36 2c 49 6e 76 61 6c 69 64 20 63 6f 6d 6d 61 6e 64
|
||||
structure:
|
||||
55 aa - sync bytes
|
||||
00 xx - length of following bytes
|
||||
yy .. zz - count(00 xx) message bytes with no terminator
|
||||
*/
|
||||
typedef struct knauer_private_t Private, *pPrivate;
|
||||
struct knauer_private_t {
|
||||
int len;
|
||||
};
|
||||
#define PROTOCOL_NAME "KNAUER_AP"
|
||||
#define PROTOCOL_INIT KNAUERInitProtocol
|
||||
#define SYN1 0x55
|
||||
#define SYN2 0xAA
|
||||
#define CR '\r'
|
||||
#define LF '\n'
|
||||
|
||||
/*
|
||||
* Protocol transmit function
|
||||
* Called by AsyncQueue to transmit a line
|
||||
* Protocol Private data block
|
||||
*/
|
||||
static int KNAUER_Tx(pAsyncProtocol p, pAsyncTxn pTxn) {
|
||||
int iRet = 1;
|
||||
typedef struct proto_private_t {
|
||||
int state; /**< protocol state machine */
|
||||
int len; /**< length from the protocol */
|
||||
pDynString wrBuffer; /**< transmitted message */
|
||||
pDynString rxBuffer; /**< received message */
|
||||
} ProtoPrivate;
|
||||
|
||||
if (pTxn) {
|
||||
pTxn->txn_status = ATX_ACTIVE;
|
||||
iRet = AsyncUnitWrite(pTxn->unit, pTxn->out_buf, pTxn->out_len);
|
||||
/* TODO handle errors */
|
||||
if (iRet < 0) { /* TODO: EOF */
|
||||
/*
|
||||
iRet = AsyncUnitReconnect(pTxn->unit);
|
||||
if (iRet == 0)
|
||||
*/
|
||||
return 0;
|
||||
static ProtoPrivate *makeProtoPrivate()
|
||||
{
|
||||
ProtoPrivate *priv = calloc(sizeof(ProtoPrivate), 1);
|
||||
priv->wrBuffer = CreateDynString(100, 100);
|
||||
priv->rxBuffer = CreateDynString(100, 100);
|
||||
return priv;
|
||||
}
|
||||
|
||||
static ProtoPrivate *Proto_KillPrivate(ProtoPrivate *priv)
|
||||
{
|
||||
if (priv) {
|
||||
if (priv->rxBuffer) {
|
||||
DeleteDynString(priv->rxBuffer);
|
||||
priv->rxBuffer = NULL;
|
||||
}
|
||||
free(priv);
|
||||
}
|
||||
return 1;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Protocol Specific Support Functions
|
||||
*/
|
||||
/*
|
||||
* Protocol Prepare Output
|
||||
*/
|
||||
static int Proto_Prepare(ProtoPrivate *priv, pDynString wrBuffer)
|
||||
{
|
||||
DynStringConcatChar(wrBuffer, CR);
|
||||
return PROTOCOL_COMPLETE;
|
||||
}
|
||||
|
||||
/*
|
||||
* Protocol receive character - characater by character
|
||||
*/
|
||||
static int KNAUER_Rx(pAsyncProtocol p, pAsyncTxn pTxn, int rxchar) {
|
||||
int iRet = 1;
|
||||
pPrivate myPriv = (pPrivate) pTxn->proto_private;
|
||||
|
||||
static int Proto_RxChar(ProtoPrivate *priv, int rxchar)
|
||||
{
|
||||
enum RX_STATE {
|
||||
RX_START=0,
|
||||
RX_TEXT=1,
|
||||
RX_SYN2=2,
|
||||
RX_LEN1=3,
|
||||
RX_LEN2=4,
|
||||
RX_STOP=99
|
||||
};
|
||||
rxchar &= 0xFF;
|
||||
switch (pTxn->txn_state) {
|
||||
case 0: /* first SYN byte */
|
||||
switch (priv->state) {
|
||||
case RX_START:
|
||||
if (rxchar != SYN1) {
|
||||
/* TODO: error */
|
||||
pTxn->txn_state = 99;
|
||||
pTxn->txn_status = ATX_COMPLETE;
|
||||
break;
|
||||
priv->state = RX_STOP;
|
||||
return PROTOCOL_ERROR;
|
||||
}
|
||||
pTxn->txn_state = 1;
|
||||
break;
|
||||
case 1: /* second SYN byte */
|
||||
priv->state = RX_SYN2;
|
||||
return PROTOCOL_CONTINUE;
|
||||
case RX_SYN2:
|
||||
if (rxchar != SYN2) {
|
||||
/* TODO: error */
|
||||
pTxn->txn_state = 99;
|
||||
pTxn->txn_status = ATX_COMPLETE;
|
||||
break;
|
||||
priv->state = RX_STOP;
|
||||
return PROTOCOL_ERROR;
|
||||
}
|
||||
pTxn->txn_state = 2;
|
||||
break;
|
||||
case 2: /* MS length byte */
|
||||
myPriv->len = 256 * rxchar;
|
||||
pTxn->txn_state = 3;
|
||||
break;
|
||||
case 3: /* LS length byte */
|
||||
myPriv->len += rxchar;
|
||||
pTxn->txn_state = 4;
|
||||
break;
|
||||
case 4: /* receiving text */
|
||||
if (pTxn->inp_idx < pTxn->inp_len)
|
||||
pTxn->inp_buf[pTxn->inp_idx++] = rxchar;
|
||||
if (pTxn->inp_idx >= myPriv->len) {
|
||||
pTxn->txn_state = 99;
|
||||
pTxn->txn_status = ATX_COMPLETE;
|
||||
priv->state = RX_LEN1;
|
||||
return PROTOCOL_CONTINUE;
|
||||
case RX_LEN1:
|
||||
priv->len = 256 * rxchar;
|
||||
priv->state = RX_LEN2;
|
||||
return PROTOCOL_CONTINUE;
|
||||
case RX_LEN2:
|
||||
priv->len += rxchar;
|
||||
priv->state = RX_TEXT;
|
||||
return PROTOCOL_CONTINUE;
|
||||
case RX_TEXT:
|
||||
DynStringConcatChar(priv->rxBuffer, rxchar);
|
||||
if (GetDynStringLength(priv->rxBuffer) >= priv->len) {
|
||||
priv->state = RX_STOP;
|
||||
return PROTOCOL_COMPLETE;
|
||||
} else {
|
||||
return PROTOCOL_CONTINUE;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
return PROTOCOL_ERROR;
|
||||
}
|
||||
if (pTxn->txn_state == 99) {
|
||||
iRet = 0;
|
||||
return PROTOCOL_ERROR;
|
||||
}
|
||||
|
||||
/*
|
||||
* AsyncProtocol handling
|
||||
* ======================
|
||||
*/
|
||||
|
||||
static void Async_KillPrivate(pAsyncTxn pTxn)
|
||||
{
|
||||
Proto_KillPrivate((ProtoPrivate *) pTxn->proto_private);
|
||||
pTxn->proto_private = NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* AsyncProtocol Receive Character
|
||||
*/
|
||||
static int Async_Rx(pAsyncProtocol p, pAsyncTxn pTxn, int rxchar) {
|
||||
int iRet, str_len;
|
||||
ProtoPrivate *priv = (ProtoPrivate *) pTxn->proto_private;
|
||||
|
||||
iRet = Proto_RxChar(priv, rxchar);
|
||||
/*
|
||||
* Keep inp_buf and inp_idx up-to-date after each character
|
||||
*/
|
||||
str_len = GetDynStringLength(priv->rxBuffer);
|
||||
if (str_len > pTxn->inp_idx && pTxn->inp_idx < pTxn->inp_len) {
|
||||
int xfr_len;
|
||||
char *tgt = &pTxn->inp_buf[pTxn->inp_idx];
|
||||
char *loc = &GetCharArray(priv->rxBuffer)[pTxn->inp_idx];
|
||||
if (str_len > pTxn->inp_len)
|
||||
xfr_len = pTxn->inp_len - pTxn->inp_idx;
|
||||
else
|
||||
xfr_len = str_len - pTxn->inp_idx;
|
||||
memcpy(tgt, loc, xfr_len);
|
||||
pTxn->inp_idx += xfr_len;
|
||||
}
|
||||
if (iRet == 0) { /* end of command */
|
||||
free(pTxn->proto_private);
|
||||
pTxn->proto_private = NULL;
|
||||
return AQU_POP_CMD;
|
||||
|
||||
if (iRet == PROTOCOL_CONTINUE) {
|
||||
return 1; /* Keep Going */
|
||||
}
|
||||
return iRet;
|
||||
|
||||
if (iRet == PROTOCOL_ERROR) {
|
||||
return -1; /* Error condition */
|
||||
}
|
||||
|
||||
/* Message Complete */
|
||||
return AQU_POP_CMD;
|
||||
}
|
||||
|
||||
/*
|
||||
* AsyncProtocol Event callback
|
||||
*/
|
||||
static int KNAUER_Ev(pAsyncProtocol p, pAsyncTxn pTxn, int event) {
|
||||
free(pTxn->proto_private);
|
||||
pTxn->proto_private = NULL;
|
||||
static int Async_Ev(pAsyncProtocol p, pAsyncTxn pTxn, int event) {
|
||||
if (event == AQU_TIMEOUT) {
|
||||
/* handle command timeout */
|
||||
pTxn->txn_status = ATX_TIMEOUT;
|
||||
@@ -108,41 +178,168 @@ static int KNAUER_Ev(pAsyncProtocol p, pAsyncTxn pTxn, int event) {
|
||||
return AQU_POP_CMD;
|
||||
}
|
||||
|
||||
static int KNAUER_PrepareTxn(pAsyncProtocol p, pAsyncTxn pTxn, const char* cmd, int cmd_len, int rsp_len) {
|
||||
pPrivate myPriv = (pPrivate) malloc(sizeof(Private));
|
||||
if (myPriv == NULL) {
|
||||
SICSLogWrite("ERROR: Out of memory in KNAUER_PrepareTxn", eError);
|
||||
/*
|
||||
* AsyncProtocol Prepare Transaction
|
||||
*/
|
||||
static int Async_PrepareTxn(pAsyncProtocol p, pAsyncTxn pTxn, const char* cmd, int cmd_len, int rsp_len) {
|
||||
ProtoPrivate *priv;
|
||||
priv = makeProtoPrivate();
|
||||
if (priv == NULL) {
|
||||
SICSLogWrite("ERROR: Out of memory in Async_PrepareTxn", eError);
|
||||
return 0;
|
||||
}
|
||||
myPriv->len = 0;
|
||||
pTxn->out_buf = (char*) malloc(cmd_len + 1);
|
||||
priv->state = 0;
|
||||
priv->len = 0;
|
||||
DynStringConcatBytes(priv->wrBuffer, cmd, cmd_len);
|
||||
Proto_Prepare(priv, priv->wrBuffer);
|
||||
pTxn->out_len = GetDynStringLength(priv->wrBuffer);
|
||||
pTxn->out_buf = (char*) malloc(pTxn->out_len);
|
||||
if (pTxn->out_buf == NULL) {
|
||||
SICSLogWrite("ERROR: Out of memory in KNAUER_PrepareTxn", eError);
|
||||
free(myPriv);
|
||||
SICSLogPrintf(eError, "ERROR: Out of memory in %s:%s", __FILE__, __FUNCTION__);
|
||||
Proto_KillPrivate(priv);
|
||||
return 0;
|
||||
}
|
||||
pTxn->proto_private = myPriv;
|
||||
memcpy(pTxn->out_buf, cmd, cmd_len);
|
||||
pTxn->out_buf[cmd_len] = CR;
|
||||
pTxn->out_len = cmd_len + 1;
|
||||
pTxn->proto_private = priv;
|
||||
pTxn->kill_private = Async_KillPrivate;
|
||||
memcpy(pTxn->out_buf, GetCharArray(priv->wrBuffer), pTxn->out_len);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static pAsyncProtocol KNAUER_Protocol = NULL;
|
||||
/*
|
||||
* Ascon Protocol handling
|
||||
* =======================
|
||||
*/
|
||||
|
||||
static void Ascon_KillPrivate(void *priv)
|
||||
{
|
||||
Proto_KillPrivate((ProtoPrivate *) priv);
|
||||
}
|
||||
|
||||
/*
|
||||
* Ascon Protocol WriteStart
|
||||
*/
|
||||
static int Ascon_Prepare(Ascon *a) {
|
||||
ProtoPrivate *priv = (ProtoPrivate *) a->private;
|
||||
Proto_Prepare(priv, a->wrBuffer);
|
||||
a->wrPos = 0;
|
||||
a->state = AsconWriting;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Ascon Protocol Read Poll
|
||||
*/
|
||||
static int Ascon_Rx(Ascon *a) {
|
||||
int ret, status;
|
||||
char chr = '\0';
|
||||
ProtoPrivate *priv = (ProtoPrivate *) a->private;
|
||||
|
||||
ret = AsconReadChar(a->fd, &chr);
|
||||
while (ret > 0) {
|
||||
a->start = DoubleTime();
|
||||
status = Proto_RxChar(priv, chr);
|
||||
if (GetDynStringLength(priv->rxBuffer) > GetDynStringLength(a->rdBuffer)) {
|
||||
int len_rd = GetDynStringLength(a->rdBuffer);
|
||||
int len_rx = GetDynStringLength(priv->rxBuffer);
|
||||
char *loc = &GetCharArray(priv->rxBuffer)[len_rd];
|
||||
DynStringConcatBytes(a->rdBuffer, loc, len_rx - len_rd);
|
||||
}
|
||||
if (status > 0) { /* Complete */
|
||||
a->state = AsconReadDone;
|
||||
return 1;
|
||||
}
|
||||
else if (status < 0) { /* Error */
|
||||
AsconError(a, "Protocol Input Error:", status);
|
||||
/*TODO This hack stops ascon.c:AsconTask() from needlessly closing the connection. Remove this when it's no longer needed */
|
||||
a->lastReconnect = DoubleTime();
|
||||
return 1;
|
||||
}
|
||||
ret = AsconReadChar(a->fd, &chr);
|
||||
}
|
||||
if (ret < 0) {
|
||||
AsconError(a, "AsconReadChar failed:", errno);
|
||||
return 1;
|
||||
}
|
||||
if (a->state != AsconReadDone) {
|
||||
if (a->timeout > 0) {
|
||||
if (DoubleTime() - a->start > a->timeout) {
|
||||
AsconError(a, "read timeout", 0);
|
||||
a->state = AsconTimeout;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Ascon Protocol Poll Loop
|
||||
*/
|
||||
static int AsconProtHandler(Ascon *a) {
|
||||
ProtoPrivate *priv = (ProtoPrivate *) a->private;
|
||||
int ret;
|
||||
|
||||
switch(a->state){
|
||||
case AsconWriteStart:
|
||||
ret = Ascon_Prepare(a);
|
||||
return ret;
|
||||
case AsconReadStart:
|
||||
DynStringClear(priv->rxBuffer);
|
||||
a->start = DoubleTime();
|
||||
priv->state = 0;
|
||||
priv->len = 0;
|
||||
ret = AsconStdHandler(a);
|
||||
return ret;
|
||||
case AsconReading:
|
||||
ret = Ascon_Rx(a);
|
||||
return ret;
|
||||
default:
|
||||
ret = AsconStdHandler(a);
|
||||
return ret;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Ascon Protocol Connection Init
|
||||
*/
|
||||
static int AsconInit(Ascon *a, SConnection *con, int argc, char *argv[])
|
||||
{
|
||||
int iRet;
|
||||
ProtoPrivate *priv;
|
||||
|
||||
iRet = AsconStdInit(a, con, argc, argv);
|
||||
priv = makeProtoPrivate();
|
||||
a->private = priv;
|
||||
a->killPrivate = Ascon_KillPrivate;
|
||||
return iRet;
|
||||
}
|
||||
|
||||
static AsyncProtocol *My_Async_Protocol = NULL;
|
||||
static AsconProtocol *My_Ascon_Protocol = NULL;
|
||||
|
||||
/*
|
||||
* Protocol Initialisation
|
||||
*/
|
||||
void KNAUERInitProtocol(SicsInterp *pSics) {
|
||||
if (KNAUER_Protocol == NULL) {
|
||||
KNAUER_Protocol = AsyncProtocolCreate(pSics, "KNAUER_AP", NULL, NULL);
|
||||
KNAUER_Protocol->sendCommand = KNAUER_Tx;
|
||||
KNAUER_Protocol->handleInput = KNAUER_Rx;
|
||||
KNAUER_Protocol->handleEvent = KNAUER_Ev;
|
||||
KNAUER_Protocol->prepareTxn = KNAUER_PrepareTxn;
|
||||
KNAUER_Protocol->killPrivate = NULL;
|
||||
void PROTOCOL_INIT(SicsInterp *pSics) {
|
||||
if (My_Async_Protocol == NULL) {
|
||||
AsyncProtocol *prot;
|
||||
prot = AsyncProtocolCreate(pSics, PROTOCOL_NAME, NULL, NULL);
|
||||
prot->sendCommand = NULL;
|
||||
prot->handleInput = Async_Rx;
|
||||
prot->handleEvent = Async_Ev;
|
||||
prot->prepareTxn = Async_PrepareTxn;
|
||||
My_Async_Protocol = prot;
|
||||
}
|
||||
|
||||
if (My_Ascon_Protocol == NULL) {
|
||||
AsconProtocol *prot;
|
||||
prot = calloc(sizeof(AsconProtocol), 1);
|
||||
prot->name = strdup(PROTOCOL_NAME);
|
||||
prot->init = AsconInit;
|
||||
prot->handler = AsconProtHandler;
|
||||
AsconInsertProtocol(prot);
|
||||
My_Ascon_Protocol = prot;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user