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
334 lines
7.7 KiB
C
334 lines
7.7 KiB
C
#include "huber_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
|
|
structure:
|
|
7B - LEFT BRACE
|
|
xx - text
|
|
0D 0A - CR LF
|
|
*/
|
|
#define PROTOCOL_NAME "HUBER_AP"
|
|
#define PROTOCOL_INIT HUBERInitProtocol
|
|
#define LBRACE '{'
|
|
#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)
|
|
{
|
|
DynStringInsert(wrBuffer, "{", 0);
|
|
DynStringConcatChar(wrBuffer, CR);
|
|
DynStringConcatChar(wrBuffer, LF);
|
|
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_LAST=2, /* LF */
|
|
RX_STOP=99
|
|
};
|
|
rxchar &= 0xFF;
|
|
switch (priv->state) {
|
|
case RX_START:
|
|
if (rxchar != LBRACE) {
|
|
priv->state = RX_STOP;
|
|
return PROTOCOL_ERROR;
|
|
}
|
|
priv->state = RX_TEXT;
|
|
return PROTOCOL_CONTINUE;
|
|
case RX_TEXT:
|
|
if (rxchar == CR) {
|
|
priv->state = RX_LAST;
|
|
return PROTOCOL_CONTINUE;
|
|
}
|
|
DynStringConcatChar(priv->rxBuffer, rxchar);
|
|
return PROTOCOL_CONTINUE;
|
|
case RX_LAST:
|
|
if (rxchar == LF) {
|
|
priv->state = RX_STOP;
|
|
return PROTOCOL_COMPLETE;
|
|
} else {
|
|
priv->state = RX_STOP;
|
|
return PROTOCOL_ERROR;
|
|
}
|
|
|
|
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;
|
|
}
|
|
}
|
|
|