Files
sics/site_ansto/hardsup/knauer_asyncprotocol.c
Douglas Clowes a43461b200 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
2014-08-27 16:52:13 +10:00

346 lines
8.2 KiB
C

#include "knauer_asyncprotocol.h"
#include "asyncprotocol.h"
#include "asyncqueue.h"
#include "ascon.i"
#include "dynstring.h"
#include <errno.h>
#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
*/
#define PROTOCOL_NAME "KNAUER_AP"
#define PROTOCOL_INIT KNAUERInitProtocol
#define SYN1 0x55
#define SYN2 0xAA
#define CR '\r'
#define LF '\n'
/*
* Protocol Private data block
*/
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;
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 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 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 (priv->state) {
case RX_START:
if (rxchar != SYN1) {
priv->state = RX_STOP;
return PROTOCOL_ERROR;
}
priv->state = RX_SYN2;
return PROTOCOL_CONTINUE;
case RX_SYN2:
if (rxchar != SYN2) {
priv->state = RX_STOP;
return PROTOCOL_ERROR;
}
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;
}
default:
return PROTOCOL_ERROR;
}
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 == PROTOCOL_CONTINUE) {
return 1; /* Keep Going */
}
if (iRet == PROTOCOL_ERROR) {
return -1; /* Error condition */
}
/* Message Complete */
return AQU_POP_CMD;
}
/*
* AsyncProtocol Event callback
*/
static int Async_Ev(pAsyncProtocol p, pAsyncTxn pTxn, int event) {
if (event == AQU_TIMEOUT) {
/* handle command timeout */
pTxn->txn_status = ATX_TIMEOUT;
return AQU_POP_CMD;
}
return AQU_POP_CMD;
}
/*
* 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;
}
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) {
SICSLogPrintf(eError, "ERROR: Out of memory in %s:%s", __FILE__, __FUNCTION__);
Proto_KillPrivate(priv);
return 0;
}
pTxn->proto_private = priv;
pTxn->kill_private = Async_KillPrivate;
memcpy(pTxn->out_buf, GetCharArray(priv->wrBuffer), pTxn->out_len);
return 1;
}
/*
* 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 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;
}
}