Added Modbus Protocol
This commit is contained in:
@ -1,6 +1,7 @@
|
||||
#include "loader_asyncprotocol.h"
|
||||
#include "huber_asyncprotocol.h"
|
||||
#include "knauer_asyncprotocol.h"
|
||||
#include "modbus_asyncprotocol.h"
|
||||
#include "omron_asyncprotocol.h"
|
||||
#include "sics.h"
|
||||
|
||||
@ -8,5 +9,6 @@ void LOADERInitProtocol(SicsInterp *pSics)
|
||||
{
|
||||
HUBERInitProtocol(pSics);
|
||||
KNAUERInitProtocol(pSics);
|
||||
MODBUSInitProtocol(pSics);
|
||||
OMRONInitProtocol(pSics);
|
||||
}
|
||||
|
@ -38,6 +38,7 @@ HOBJ += aqp_opalstatus.o
|
||||
HOBJ += omron_asyncprotocol.o
|
||||
HOBJ += huber_asyncprotocol.o
|
||||
HOBJ += knauer_asyncprotocol.o
|
||||
HOBJ += modbus_asyncprotocol.o
|
||||
HOBJ += loader_asyncprotocol.o
|
||||
|
||||
libhlib.a: $(HOBJ)
|
||||
|
718
site_ansto/hardsup/modbus_asyncprotocol.c
Normal file
718
site_ansto/hardsup/modbus_asyncprotocol.c
Normal file
@ -0,0 +1,718 @@
|
||||
#include "modbus_asyncprotocol.h"
|
||||
#include "asyncprotocol.h"
|
||||
#include "asyncqueue.h"
|
||||
#include "ascon.i"
|
||||
#include "dynstring.h"
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <math.h>
|
||||
|
||||
#define PROTOCOL_CONTINUE 0
|
||||
#define PROTOCOL_COMPLETE 1
|
||||
#define PROTOCOL_ERROR (-1)
|
||||
|
||||
/* Sample Messages
|
||||
* structure:
|
||||
* STX
|
||||
* data
|
||||
* ETX
|
||||
* BCC
|
||||
*/
|
||||
#define PROTOCOL_NAME "MODBUS_AP"
|
||||
#define PROTOCOL_INIT MODBUSInitProtocol
|
||||
#define ADUSIZE 17
|
||||
|
||||
/*
|
||||
* MODBUS DataTypes
|
||||
*/
|
||||
enum mbDtypes {U16, U32, F32};
|
||||
|
||||
/*
|
||||
* NumVal = number of values to read or write,
|
||||
* ie number of coils, input/outputs or registers.
|
||||
* Number of bytes = NumVal * (number of bytes in Dtype)
|
||||
*/
|
||||
struct cmdPar {
|
||||
unsigned int MBAddr; /**< MODBUS Address */
|
||||
unsigned int Fcode; /**< Function Code */
|
||||
unsigned int SAddr;
|
||||
unsigned int NumVal; /**< Number of Values */
|
||||
unsigned int NumBytes;
|
||||
unsigned int NumRd;
|
||||
unsigned int NumWr;
|
||||
enum mbDtypes Dtype;
|
||||
double MBdata[2000];
|
||||
};
|
||||
|
||||
enum MBFCODES { RdCoil=1, RdInp=2, RdHldReg=3, RdInpReg=4, WrCoil=5, WrReg=6, WrMCoils=15, WrMRegs=16 };
|
||||
|
||||
typedef struct modbus_private_t Private, *pPrivate;
|
||||
struct modbus_private_t {
|
||||
struct cmdPar MBcmd;
|
||||
int BO[4];
|
||||
int aduLen;
|
||||
int RespLen;
|
||||
int DatLen;
|
||||
unsigned char ADU[ADUSIZE]; /* Allows upto 8 bytes if data */
|
||||
pDynString rdBuffer;
|
||||
};
|
||||
|
||||
/*
|
||||
* 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 */
|
||||
Private mb_priv;
|
||||
} 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
|
||||
*/
|
||||
|
||||
/* IEEE code copied from psi/modbus.c */
|
||||
static void double2ieee(double input, unsigned char ieee[4]) {
|
||||
|
||||
/* convert double to IEEE 32 bit floating number
|
||||
* (denormalized numbers are considered as zero)
|
||||
*/
|
||||
|
||||
long mantissa;
|
||||
int exponent;
|
||||
|
||||
if (input == 0) {
|
||||
ieee[0]= 0;
|
||||
ieee[1]= 0;
|
||||
ieee[2]= 0;
|
||||
ieee[3]= 0;
|
||||
} else {
|
||||
/* frexp -> 0.f * 2^23 */
|
||||
/* 2^24 * fract instead of 2^23
|
||||
* because we get 0.(f/2) not 1.f from frexp and exponent = e + 1
|
||||
*/
|
||||
mantissa = 0x1000000 * (frexp(fabs(input), &exponent));
|
||||
exponent = exponent - 1 + 127;
|
||||
if (exponent < 0) {
|
||||
exponent = 0;
|
||||
} else if (exponent > 0xFE) {
|
||||
exponent = 0xFE;
|
||||
}
|
||||
if (input < 0) {
|
||||
ieee[0] = 0x80 | (exponent >> 1);
|
||||
} else {
|
||||
ieee[0] = exponent >> 1;
|
||||
}
|
||||
ieee[1] = (exponent & 1) << 7 | ((mantissa & 0x7F0000) >> 16);
|
||||
ieee[2] = (mantissa & 0xFF00) >> 8;
|
||||
ieee[3] = mantissa & 0xFF;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
static double ieee2double(unsigned char ieee[4]) {
|
||||
|
||||
/* IEEE 32 bit floating number to double
|
||||
* (denormalized numbers are considered as zero)
|
||||
*/
|
||||
|
||||
long mantissa;
|
||||
double output, norm = 1;
|
||||
int exponent, bias = 127;
|
||||
|
||||
mantissa = ((ieee[1] << 16) & 0x7F0000)
|
||||
| ((ieee[2] << 8) & 0xFF00)
|
||||
| ((ieee[3] ) & 0xFF);
|
||||
|
||||
exponent = (ieee[0] & 0x7F) * 2 + ((ieee[1] >> 7) & 1); /* raw exponent */
|
||||
if (exponent == 255) {
|
||||
if (mantissa == 0) {
|
||||
/*TODO INF */
|
||||
} else {
|
||||
/*TODO NAN */
|
||||
}
|
||||
}
|
||||
if (exponent == 0) {
|
||||
if (mantissa == 0) {
|
||||
return 0.0;
|
||||
} else {
|
||||
bias = 126;
|
||||
norm = 0;
|
||||
}
|
||||
}
|
||||
output = ldexp(mantissa, -23) + norm;
|
||||
if (ieee[0] & 0x80) {
|
||||
output = -output;
|
||||
}
|
||||
return output * ldexp(1, exponent - bias);
|
||||
}
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/** @brief Parse a command string of the form "MBAddr:Fcode:SAddr:Dtype:MBdata"
|
||||
Dtype U16, U32, F32 with optional byte order array eg U32[3,2,1,0]
|
||||
*/
|
||||
static void parseCmd(char *cmdLine, struct cmdPar *MBC, int *BO) {
|
||||
int len;
|
||||
unsigned int i;
|
||||
char *nextPar, dataType[4];
|
||||
double dVal;
|
||||
|
||||
MBC->MBAddr = strtoul(cmdLine, &nextPar, 0);
|
||||
cmdLine = nextPar+1;
|
||||
MBC->Fcode = strtoul(cmdLine, &nextPar, 0);
|
||||
cmdLine = nextPar+1;
|
||||
MBC->SAddr = strtoul(cmdLine, &nextPar, 0);
|
||||
cmdLine = nextPar+1;
|
||||
if (MBC->Fcode != WrCoil) {
|
||||
MBC->NumVal = strtod(cmdLine, &nextPar);
|
||||
cmdLine = nextPar + 1;
|
||||
strncpy(dataType,cmdLine,3);
|
||||
dataType[3] = '\0';
|
||||
cmdLine += 3;
|
||||
nextPar = cmdLine;
|
||||
if (strcmp(dataType, "U16") == 0) {
|
||||
MBC->Dtype = U16;
|
||||
} else if (strcmp(dataType, "U32") == 0) {
|
||||
MBC->Dtype = U32;
|
||||
} else if (strcmp(dataType, "F32") == 0) {
|
||||
MBC->Dtype = F32;
|
||||
}
|
||||
if (*cmdLine == '[') {
|
||||
cmdLine++;
|
||||
BO[0] = strtol(cmdLine, &nextPar, 10);
|
||||
cmdLine = nextPar + 1;
|
||||
BO[1] = strtol(cmdLine, &nextPar, 10);
|
||||
cmdLine = nextPar + 1;
|
||||
BO[2] = strtol(cmdLine, &nextPar, 10);
|
||||
cmdLine = nextPar + 1;
|
||||
BO[3] = strtol(cmdLine, &nextPar, 10);
|
||||
}
|
||||
cmdLine = nextPar + 1;
|
||||
}
|
||||
switch (MBC->Fcode) {
|
||||
case RdCoil:
|
||||
/* Coils are numbered from 1 but addressed from 0 */
|
||||
MBC->SAddr--;
|
||||
MBC->NumBytes = MBC->NumVal;
|
||||
MBC->NumRd = MBC->NumVal;
|
||||
break;
|
||||
case WrCoil:
|
||||
MBC->SAddr--;
|
||||
dVal = strtod(cmdLine, &nextPar);
|
||||
if (dVal == 1) {
|
||||
MBC->MBdata[0] = 255;
|
||||
} else if (dVal == 0) {
|
||||
MBC->MBdata[0] = 0;
|
||||
} else {
|
||||
/* TODO ERROR */
|
||||
}
|
||||
break;
|
||||
case RdHldReg:
|
||||
if (MBC->Dtype == F32 || MBC->Dtype == U32) {
|
||||
MBC->NumRd = MBC->NumVal * 2;
|
||||
} else if (MBC->Dtype == U16) {
|
||||
MBC->NumRd = MBC->NumVal;
|
||||
} else {
|
||||
/* TODO ERROR */
|
||||
}
|
||||
break;
|
||||
case WrMRegs:
|
||||
for (i=0; i < MBC->NumVal; i++) {
|
||||
MBC->MBdata[i] = strtod(cmdLine, &nextPar);
|
||||
cmdLine = nextPar + 1;
|
||||
}
|
||||
if (MBC->Dtype == F32 || MBC->Dtype == U32) {
|
||||
MBC->NumWr = MBC->NumVal * 2;
|
||||
MBC->NumBytes = MBC->NumVal * 4;
|
||||
} else if (MBC->Dtype == U16) {
|
||||
MBC->NumWr = MBC->NumVal;
|
||||
MBC->NumBytes = MBC->NumVal * 2;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static int ModbusOutput(pPrivate myPriv, pDynString wrBuffer, pDynString send_buffer) {
|
||||
int ADUlen, PDUlenPlusUID;
|
||||
unsigned int i, j;
|
||||
char *cmdLine;
|
||||
unsigned char ADU[32], ieee[4];
|
||||
|
||||
cmdLine = GetCharArray(wrBuffer);
|
||||
for (i=0; i < 4; i++)
|
||||
myPriv->BO[i] = i;
|
||||
parseCmd(cmdLine, &myPriv->MBcmd, myPriv->BO);
|
||||
|
||||
ADU[0] = 0;
|
||||
ADU[1] = 0;
|
||||
ADU[2] = 0;
|
||||
ADU[3] = 0;
|
||||
ADU[6] = myPriv->MBcmd.MBAddr;
|
||||
ADU[7] = myPriv->MBcmd.Fcode;
|
||||
switch (myPriv->MBcmd.Fcode) {
|
||||
case RdCoil:
|
||||
case RdHldReg:
|
||||
/* TODO Max NumRd = 125 */
|
||||
ADU[8] = (myPriv->MBcmd.SAddr & 0xFF00) >> 8;
|
||||
ADU[9] = myPriv->MBcmd.SAddr & 0xFF;
|
||||
ADU[10] = (myPriv->MBcmd.NumRd & 0xFF00) >> 8;
|
||||
ADU[11] = myPriv->MBcmd.NumRd & 0xFF;
|
||||
ADUlen = 12;
|
||||
break;
|
||||
case WrCoil:
|
||||
ADU[8] = (myPriv->MBcmd.SAddr & 0xFF00) >> 8;
|
||||
ADU[9] = myPriv->MBcmd.SAddr & 0xFF;
|
||||
ADU[10] = myPriv->MBcmd.MBdata[0];
|
||||
ADU[11] = 0;
|
||||
ADUlen = 12;
|
||||
break;
|
||||
case WrMRegs:
|
||||
ADU[8] = (myPriv->MBcmd.SAddr & 0xFF00) >> 8;
|
||||
ADU[9] = myPriv->MBcmd.SAddr & 0xFF;
|
||||
ADU[10] = (myPriv->MBcmd.NumWr & 0xFF00) >> 8;
|
||||
ADU[11] = myPriv->MBcmd.NumWr & 0xFF;
|
||||
ADU[12] = myPriv->MBcmd.NumBytes;
|
||||
switch (myPriv->MBcmd.Dtype) {
|
||||
case U16:
|
||||
for (i=0, j=13; i < myPriv->MBcmd.NumVal; i++, j+=4) {
|
||||
ADU[j + myPriv->BO[0]] = ((unsigned int)myPriv->MBcmd.MBdata[i] & 0xFF00) >> 8;
|
||||
ADU[j + myPriv->BO[1]] = (unsigned int)myPriv->MBcmd.MBdata[i] & 0xFF;
|
||||
}
|
||||
break;
|
||||
case U32:
|
||||
for (i=0, j=13; i < myPriv->MBcmd.NumVal; i++, j+=4) {
|
||||
ADU[j + myPriv->BO[0]] = ((unsigned int)myPriv->MBcmd.MBdata[i] & 0xFF000000) >> 24;
|
||||
ADU[j + myPriv->BO[1]] = ((unsigned int)myPriv->MBcmd.MBdata[i] & 0xFF0000) >> 16;
|
||||
ADU[j + myPriv->BO[2]] = ((unsigned int)myPriv->MBcmd.MBdata[i] & 0xFF00) >> 8;
|
||||
ADU[j + myPriv->BO[3]] = (unsigned int)myPriv->MBcmd.MBdata[i] & 0xFF;
|
||||
}
|
||||
break;
|
||||
case F32:
|
||||
for (i=0, j=13; i < myPriv->MBcmd.NumVal; i++, j+=4) {
|
||||
double2ieee(myPriv->MBcmd.MBdata[i], ieee);
|
||||
ADU[j + myPriv->BO[0]] = ieee[0];
|
||||
ADU[j + myPriv->BO[1]] = ieee[1];
|
||||
ADU[j + myPriv->BO[2]] = ieee[2];
|
||||
ADU[j + myPriv->BO[3]] = ieee[3];
|
||||
}
|
||||
break;
|
||||
}
|
||||
ADUlen = 13 + myPriv->MBcmd.NumBytes;
|
||||
break;
|
||||
default:
|
||||
// Not Implemented
|
||||
break;
|
||||
}
|
||||
/* Length field in MBAP includes byte 7 of the MBAP, so Length = ADU length - 6. */
|
||||
PDUlenPlusUID = ADUlen - 6;
|
||||
ADU[4] = (PDUlenPlusUID & 0xFF00) >> 8;
|
||||
ADU[5] = PDUlenPlusUID & 0xFF;
|
||||
DynStringClear(send_buffer);
|
||||
DynStringConcatBytes(send_buffer, (char *) ADU, ADUlen);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
/** @brief decode modbus response
|
||||
*/
|
||||
/* ADU[4] = MSB of DatLen, ADU[5] = LSB of DatLen */
|
||||
|
||||
/* NOTE: Min aduLen is 9. The header (7 bytes) + an Error code and Exception code */
|
||||
static const int TIDidx=0;
|
||||
static const int PIDidx=2;
|
||||
static const int LENidx=4;
|
||||
static const int UIDidx=6;
|
||||
static const int CODEidx=7;
|
||||
static const int DATLENidx=8;
|
||||
static const int DATAidx=9;
|
||||
|
||||
static void StartIn(pPrivate myPriv) {
|
||||
myPriv->aduLen = 0;
|
||||
myPriv->RespLen = 0;
|
||||
myPriv->DatLen = 0;
|
||||
}
|
||||
|
||||
static int ModbusInput(pPrivate myPriv, unsigned char chr, pDynString rdBuffer) {
|
||||
int i, byteCnt;
|
||||
unsigned char ieee[4];
|
||||
double dVal;
|
||||
long int liVal;
|
||||
|
||||
if (myPriv->aduLen == 0)
|
||||
DynStringClear(rdBuffer);
|
||||
|
||||
myPriv->ADU[myPriv->aduLen++] = chr;
|
||||
if (myPriv->RespLen == 0) {
|
||||
if (myPriv->aduLen >= UIDidx) {
|
||||
myPriv->RespLen = (myPriv->ADU[LENidx] << 8) + myPriv->ADU[LENidx+1];
|
||||
if (myPriv->RespLen <= 0) {
|
||||
DynStringReplace(rdBuffer, "ASCERR: Length field exception", 0);
|
||||
return PROTOCOL_ERROR;
|
||||
}
|
||||
} else {
|
||||
return PROTOCOL_CONTINUE;
|
||||
}
|
||||
}
|
||||
if (myPriv->aduLen < (6 + myPriv->RespLen)) {
|
||||
return PROTOCOL_CONTINUE;
|
||||
}
|
||||
|
||||
if (myPriv->ADU[CODEidx] > 0x80) {
|
||||
/* snprintf(errMsg, ERRLEN, "TCPMODBUS: Function code %d exception %d:", ADU[CODEidx] & 0x0F, ADU[8] &0x0F); */
|
||||
DynStringReplace(rdBuffer, "ASCERR: Function code exception", 0);
|
||||
return PROTOCOL_ERROR;
|
||||
} else if (myPriv->ADU[CODEidx] != myPriv->MBcmd.Fcode) {
|
||||
/* snprintf(errMsg, ERRLEN, "TCPMODBUS: Requested function %d but got response for %d:", MBcmd.Fcode, ADU[CODEidx]); */
|
||||
DynStringReplace(rdBuffer, "ASCERR: Function code response exception", 0);
|
||||
return PROTOCOL_ERROR;
|
||||
} else {
|
||||
char temp[65];
|
||||
DynStringClear(rdBuffer);
|
||||
switch (myPriv->MBcmd.Fcode) {
|
||||
case RdCoil:
|
||||
byteCnt = myPriv->ADU[8];
|
||||
for (i=0; i < byteCnt; i++) {
|
||||
snprintf(temp, 64, "%d", myPriv->ADU[9+i]);
|
||||
DynStringConcat(rdBuffer, temp);
|
||||
DynStringConcatChar(rdBuffer, ' ');
|
||||
}
|
||||
break;
|
||||
case RdHldReg:
|
||||
byteCnt = myPriv->ADU[8];
|
||||
switch (myPriv->MBcmd.Dtype) {
|
||||
case U16:
|
||||
for (i=0; i < byteCnt/2; i++) {
|
||||
ieee[ myPriv->BO[0] ] = myPriv->ADU[9+i*2];
|
||||
ieee[ myPriv->BO[1] ] = myPriv->ADU[10+i*2];
|
||||
liVal = (ieee[0] << 8) | ieee[1];
|
||||
snprintf(temp, 64, "%ld", liVal);
|
||||
DynStringConcat(rdBuffer, temp);
|
||||
DynStringConcatChar(rdBuffer, ' ');
|
||||
}
|
||||
break;
|
||||
case U32:
|
||||
case F32:
|
||||
for (i=0; i < byteCnt/4; i++) {
|
||||
ieee[ myPriv->BO[0] ] = myPriv->ADU[9+i*4];
|
||||
ieee[ myPriv->BO[1] ] = myPriv->ADU[10+i*4];
|
||||
ieee[ myPriv->BO[2] ] = myPriv->ADU[11+i*4];
|
||||
ieee[ myPriv->BO[3] ] = myPriv->ADU[12+i*4];
|
||||
if (myPriv->MBcmd.Dtype == F32) {
|
||||
dVal = ieee2double(ieee);
|
||||
snprintf(temp, 64, "%g", dVal);
|
||||
} else if (myPriv->MBcmd.Dtype == U32) {
|
||||
liVal = (ieee[0] << 24) | (ieee[1] << 16) | (ieee[2] << 8) | ieee[3];
|
||||
snprintf(temp, 64, "%ld", liVal);
|
||||
}
|
||||
DynStringConcat(rdBuffer, temp);
|
||||
DynStringConcatChar(rdBuffer, ' ');
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case WrCoil:
|
||||
if (myPriv->ADU[7] == myPriv->MBcmd.Fcode && (myPriv->ADU[8] * 256 | myPriv->ADU[9]) == myPriv->MBcmd.SAddr &&
|
||||
myPriv->ADU[10] == myPriv->MBcmd.MBdata[0]) {
|
||||
DynStringReplace(rdBuffer, "OK", 0);
|
||||
} else {
|
||||
DynStringReplace(rdBuffer, "ASCERR: Response doesn't match set request", 0);
|
||||
return PROTOCOL_ERROR;
|
||||
}
|
||||
break;
|
||||
case WrMRegs:
|
||||
if (myPriv->ADU[7] == myPriv->MBcmd.Fcode && (myPriv->ADU[8] * 256 | myPriv->ADU[9]) == myPriv->MBcmd.SAddr &&
|
||||
(myPriv->ADU[10] * 256 | myPriv->ADU[11]) == myPriv->MBcmd.NumWr) {
|
||||
DynStringReplace(rdBuffer, "OK", 0);
|
||||
} else {
|
||||
DynStringReplace(rdBuffer, "ASCERR: Response doesn't match set request", 0);
|
||||
return PROTOCOL_ERROR;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
myPriv->aduLen = 0, myPriv->DatLen = 0, myPriv->RespLen = 0;
|
||||
memset(myPriv->ADU, 0, ADUSIZE);
|
||||
return PROTOCOL_COMPLETE;
|
||||
}
|
||||
|
||||
/*
|
||||
* Protocol Prepare Output
|
||||
*/
|
||||
static int Proto_Prepare(ProtoPrivate *priv, pDynString wrBuffer)
|
||||
{
|
||||
ModbusOutput(&priv->mb_priv, wrBuffer, wrBuffer);
|
||||
StartIn(&priv->mb_priv);
|
||||
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, /* BCC */
|
||||
RX_STOP=99
|
||||
};
|
||||
int iRet;
|
||||
rxchar &= 0xFF;
|
||||
iRet = ModbusInput(&priv->mb_priv, rxchar, priv->rxBuffer);
|
||||
if (iRet == PROTOCOL_COMPLETE)
|
||||
return PROTOCOL_COMPLETE;
|
||||
if (iRet == PROTOCOL_CONTINUE)
|
||||
return PROTOCOL_CONTINUE;
|
||||
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, (char *) 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;
|
||||
}
|
||||
}
|
||||
|
5
site_ansto/hardsup/modbus_asyncprotocol.h
Normal file
5
site_ansto/hardsup/modbus_asyncprotocol.h
Normal file
@ -0,0 +1,5 @@
|
||||
#ifndef MODBUS_ASYNCPROTOCOL_H
|
||||
#define MODBUS_ASYNCPROTOCOL_H
|
||||
#include "sics.h"
|
||||
void MODBUSInitProtocol(SicsInterp *pSics);
|
||||
#endif
|
Reference in New Issue
Block a user