Files
sics/protocol.c

822 lines
22 KiB
C

/*--------------------------------------------------------------------------
ANSTO Protocol Command Object
Paul Hathaway, November, 2004
Copyright: See copyright.txt
----------------------------------------------------------------------------*/
#include <string.h>
#include <stdlib.h>
#include <assert.h>
#include <conman.h>
#include <obdes.h>
#include <sics.h>
#include <conman.h>
#include <fupa.h>
#include <splitter.h>
#include <outcode.c>
#include <dynstring.h>
#include "commandlog.h"
#include "protocol.h"
#include <json/json.h>
#define MAXMSG 1024
#define INIT_STR_SIZE 256
#define STR_RESIZE_LENGTH 256
#define NUMPROS 6
#define PROLISTLEN 7
typedef struct __Protocol {
pObjectDescriptor pDes; /* required as first field */
char *name; /* protocol handler name */
char *version; /* protocol version string */
int iNumPros; /* number of valid protocols? */
writeFunc defaultWriter; /* default write function */
int isDefaultSet;
char *pProList[PROLISTLEN]; /* list of valid protocols? */
} Protocol;
char *pEventType[]={
"VALUECHANGE", /* 0 */
"MOTDRIVE", /* 1 */
"MONITOR", /* 2 */
"ROTSTART", /* 3 */
"ROTMOVE", /* 4 */
"SCANEND", /* 5 */
"SCANSTART", /* 6 */
"SCANPOINT", /* 7 */
"WLCHANGE", /* 8 */
"REFLECTIONDONE", /* 9 */
"COUNTSTART", /* 10 */
"COUNTEND", /* 11 */
"FILELOADED", /* 12 */
"MOTEND", /* 13 */
"BATCHSTART", /* 14 */
"BATCHAREA", /* 15 */
"BATCHEND", /* 16 */
"DRIVSTAT", /* 17 */
"STATUS", /* 18 */
"POSITION" /* 19 Motor position events, ffr */
};
char *pStatus[]={
"UNSET",
"OKOK", /* 1 */
"HWIdle", /* 2 */
"HWBusy", /* 3 */
"HWFault", /* 4 */
"HWPosFault", /* 5 */
"HWCrash", /* 6 */
"NOMEMORY", /* 7 */
"HWNoBeam", /* 8 */
"HWPause", /* 9 */
"HWWarn", /* 10 */
"HWRedo", /* 11 */
};
typedef struct __Protocol *pProtocol;
/* alternate implementation of protocol list if data hiding in
* Protocol struct via CreateProtocol does not work
* static char *pPros[] = {
* "default",
* "outcodes",
* "sycamore"
* NULL };
* static int iNumPros = 3
*/
pProtocol CreateProtocol(void);
static int ProtocolOptions(SConnection* pCon, pProtocol pPro);
static int ProtocolHelp(SConnection* pCon, Protocol* pPro);
static int ProtocolSet(SConnection* pCon, Protocol* pPro, char *pProName);
static int ProtocolList(SConnection* pCon, Protocol* pPro);
int ProtocolAction(SConnection *pCon, SicsInterp *pSics, void *pData,
int argc, char *argv[]);
static int EnumChoice(char *pList[], int iLength, char *pInput);
static int InitDefaultProtocol(SConnection* pCon, Protocol *pPro);
/* Signatures for protocol writers implemented in this file */
int SCWriteSycamore(SConnection *pCon, char *pBuffer, int iOut);
int SCWriteJSON_String(SConnection *pCon, char *pBuffer, int iOut);
/*--------------------------------------------------------------------------*/
pProtocol CreateProtocol(void)
{
int i, iNumPros = NUMPROS;
char *pPros[] = {"default",
"normal",
"withcode",
"sycamore",
"json",
"act",
NULL
};
pProtocol pNew = NULL;
pNew = (pProtocol)malloc(sizeof(Protocol));
if(!pNew)
{
return NULL;
}
pNew->pDes = CreateDescriptor("Protocol");
if(!pNew->pDes)
{
free(pNew);
return NULL;
}
pNew->name = strdup("protocol");
pNew->version = strdup("1.0");
pNew->iNumPros = iNumPros;
// pNew->pProList = (char *)malloc(sizeof(pPros));
for(i=0;i<iNumPros;i++)
{
pNew->pProList[i] = strdup(pPros[i]);
}
pNew->pProList[i] = NULL;
pNew->isDefaultSet = 0;
return pNew;
}
/*-------------------------------------------------------------------------*/
void DeleteProtocol(void *self)
{
int i;
pProtocol pOld = (pProtocol)self;
if(NULL==pOld)
{
return;
}
if(pOld->name)
{
free(pOld->name);
}
if(pOld->pDes)
{
DeleteDescriptor(pOld->pDes);
}
if(pOld->version)
{
free(pOld->version);
}
if(pOld->pProList)
{
i = 0;
while(NULL!=pOld->pProList[i])
{
free(pOld->pProList[i]);
i++;
}
}
free(pOld);
}
/*------------------------------------------------------------------*/
static int ContextDo(SConnection *pCon, SicsInterp *pSics, void *pData,
int argc, char *argv[]){
commandContext comCon;
char buffer[1024];
char *command;
int status;
if(argc < 3){
SCWrite(pCon,"ERROR: insufficient arguments to contextdo",eError);
return 0;
}
status = Tcl_GetInt(pSics->pTcl,argv[1],&comCon.transID);
if(status != TCL_OK){
snprintf(buffer,1023,"ERROR: failed to convert %s to transaction ID", argv[1]);
SCWrite(pCon,buffer,eError);
return 0;
}
strncpy(comCon.deviceID,argv[2],SCDEVIDLEN);
memset(buffer,0,sizeof(buffer));
command = Arg2Tcl(argc-2,&argv[2],buffer,sizeof buffer);
if (!command) {
SCWrite(pCon,"ERROR: no more memory",eError);
return 0;
}
SCPushContext2(pCon,comCon);
status = InterpExecute(pSics,pCon,command);
if (command != buffer) free(command);
SCPopContext(pCon);
return status;
}
/*--------------------------------------------------------------------------*/
int InstallProtocol(SConnection *pCon, SicsInterp *pSics, void *pData,
int argc, char *argv[])
{
pProtocol pNew = NULL;
pNew = CreateProtocol();
if(NULL==pNew)
{
SCWrite(pCon,"No memory to create Protocol",eError);
return 0;
}
AddCommand(pSics,"Protocol",ProtocolAction,DeleteProtocol,pNew);
AddCommand(pSics,"contextdo",ContextDo,NULL,NULL);
SCSendOK(pCon);
return 1;
}
/*------------------------------------------------------------------------*/
void MakeProtocol(SicsInterp *pSics){
pProtocol pNew = NULL;
pNew = CreateProtocol();
if(NULL!= pNew)
{
AddCommand(pSics,"Protocol",ProtocolAction,DeleteProtocol,pNew);
AddCommand(pSics,"contextdo",ContextDo,NULL,NULL);
}
}
/*------------------------------------------------------------------------*/
static int ProtocolOptions(SConnection* pCon, pProtocol pPro)
{
int i;
char pBuffer[80];
for(i=0;i<pPro->iNumPros;i++)
{
sprintf(pBuffer,"Protocol[%d] = %s",i,pPro->pProList[i]);
SCWrite(pCon,pBuffer,eStatus);
}
return 1;
}
/*------------------------------------------------------------------------*/
static int ProtocolHelp(SConnection* pCon, Protocol* pPro)
{
SCWrite(pCon,
"Usage: protocol {help|list|options|reset} | set protocolName",
eStatus);
return 1;
}
/*------------------------------------------------------------------------*/
static int ProtocolSet(SConnection* pCon, Protocol* pPro, char *pProName)
{
int proID;
if(!SCVerifyConnection(pCon))
{
return 0;
}
/* lazy initialisation of defaultWriter since connection is verified */
InitDefaultProtocol(pCon,pPro);
/* Do not die if no data */
if(NULL == pProName)
{
return 0;
}
/* check list of protocols for valid name and assign functions based */
/* on match of pProName */
proID = EnumChoice(pPro->pProList,pPro->iNumPros,pProName);
switch(proID)
{
case -1: /* invalid */
return 0;
break;
case 1: /* normal (connection start default) */
SCSetWriteFunc(pCon,SCNormalWrite);
break;
case 2: /* outcodes */
SCSetWriteFunc(pCon,SCWriteWithOutcode);
break;
case 3: /* sycamore */
SCSetWriteFunc(pCon,SCWriteSycamore);
break;
case 4: /* json */
SCSetWriteFunc(pCon,SCWriteJSON_String);
break;
case 5:
SCSetWriteFunc(pCon,SCACTWrite);
break;
case 0: /* default = psi_sics */
default:
SCSetWriteFunc(pCon,pPro->defaultWriter);
break;
}
pCon->iProtocolID = proID;
SCSendOK(pCon);
return 1;
}
/*------------------------------------------------------------------------*/
int ProtocolGet(SConnection* pCon, void* pData, char *pProName, int len)
{
int Index;
Protocol *pPro = (Protocol *)pData;
if(!SCVerifyConnection(pCon))
{
return 0;
}
if(pData == NULL)
{
pCon->iProtocolID = 0;
return 1;
}
/* lazy initialisation of defaultWriter since connection is verified */
if(0==pPro->isDefaultSet)
{
pPro->defaultWriter = SCGetWriteFunc(pCon);
pPro->isDefaultSet = 1;
pCon->iProtocolID = 0;
}
strncpy(pProName, pPro->pProList[pCon->iProtocolID], len);
return 1;
#if 0
Index = pCon->iProtocolID;
/* check list of protocols for valid name */
switch(Index)
{
case 0: /* default = psi_sics */
case 1: /* normal (connection start default) */
case 2: /* outcodes */
case 3: /* sycamore */
case 4: /* json */
case 5: /* act */
pProName = pPro->pProList[Index];
return 1;
break;
default:
return 0;
break;
}
#endif
}
/*------------------------------------------------------------------------*/
static int ProtocolList(SConnection* pCon, Protocol* pPro)
{
SCWrite(pCon,
"Usage: protocol {help|list|options|reset} | set protocolName",
eStatus);
return 1;
}
/*-------------------------------------------------------------------------*/
int ProtocolAction(SConnection *pCon, SicsInterp *pSics, void *pData,
int argc, char *argv[])
{
int iRet;
char **argx;
FuPaResult PaRes;
pProtocol pPro = NULL;
const int iNumCmds = 5;
FuncTemplate CommandTemplate[] = {
{"help",0,{0,0}},
{"list",0,{0,0}},
{"options",0,{0,0}},
{"set",1,{FUPATEXT}},
{"reset",0,{0,0}},
{NULL}
};
assert(pCon != NULL);
assert(pSics != NULL);
pPro = (pProtocol)pData;
assert(pPro != NULL);
/* You need to have User level access rights to use this facility */
if(!SCMatchRights(pCon,usSpy))
{
return 0;
}
/* parse function args */
argtolower(argc,argv);
argx = &argv[1];
iRet = EvaluateFuPa((pFuncTemplate)&CommandTemplate,iNumCmds,argc-1,argx,&PaRes);
/* if invalid (iRet < 0) then default to "help" command */
switch(iRet)
{
case 1: /* list */
iRet = ProtocolList(pCon,pPro);
break;
case 2: /* options */
iRet = ProtocolOptions(pCon,pPro);
break;
case 3: /* set */
iRet = ProtocolSet(pCon,pPro,PaRes.Arg[0].text);
break;
case 4: /* reset */
iRet = ProtocolSet(pCon,pPro,"default");
break;
case 0: /* help */
default:
iRet = ProtocolHelp(pCon,pPro);
break;
}
return iRet;
}
/*-------------------------------------------------------------------------*/
static int EnumChoice(char *pList[], int iLength, char *pInput)
{
int i;
int iRet = -1;
for(i=0;i<iLength;i++)
{
if(0==strcmp(pInput,pList[i]))
{
iRet = i;
break;
}
}
return iRet;
}
/*----------------------------------*/
static int InitDefaultProtocol(SConnection* pCon, Protocol *pPro)
{
if(NULL==pCon)
{
return 0;
}
/* lazy initialisation of defaultWriter since connection is verified */
if(0==pPro->isDefaultSet)
{
pPro->defaultWriter = SCGetWriteFunc(pCon);
pPro->isDefaultSet = 1;
pCon->iProtocolID = 0;
}
return pPro->isDefaultSet;
}
/*---------------------------------------------------------------------*/
void sycformat(char *tag, OutCode msgFlag, pDynString msgString, pDynString msgOut) {
DynStringConcat(msgOut," ");
switch (msgFlag) {
eEvent:
break;
eFinish:
break;
default:
DynStringConcat(msgOut,tag);
DynStringConcat(msgOut,"={");
DynStringConcat(msgOut,GetCharArray(msgString));
DynStringConcat(msgOut,"}");
break;
}
}
int SCWriteSycamore(SConnection *pCon, char *pBuffer, int iOut)
{
int iRet;
char pBueffel[MAXMSG], *pBufferFrom, *pBufferTo;
long taskID = 0;
/* char pPrefix[40];*/
pDynString pMsg = NULL;
pDynString pMsgString = NULL;
commandContext comCon;
if (strlen(pBuffer) == 0) {
return 0;
}
/* Strip \r and \n */
for (pBufferFrom=pBufferTo=pBuffer; ; pBufferFrom++) {
if (*pBufferFrom == '\r' || *pBufferFrom == '\n')
continue;
*pBufferTo = *pBufferFrom;
if (*pBufferTo == '\0')
break;
pBufferTo++;
}
if(!SCVerifyConnection(pCon))
{
return 0;
}
/* log it for any case */
if(pCon->pSock)
{
iRet = pCon->pSock->sockid;
}
else
{
iRet = 0;
}
sprintf(pBueffel,"Next line intended for socket: %d",iRet);
SICSLogWrite(pBueffel,eInternal);
SICSLogWrite(pBuffer,iOut);
/* write to commandlog if user or manager privilege */
if(SCGetRights(pCon) <= usUser)
{
WriteToCommandLogId(NULL,iRet,pBuffer);
}
/* put it into the interpreter if present */
if(SCinMacro(pCon))
{
InterpWrite(pServ->pSics,pBuffer);
/* print it to client if error message */
/* FIXME should report errors via sycamore
if((iOut== eError) || (iOut == eWarning) )
iRet = SCDoSockWrite(pCon,GetCharArray(pMsgOut));*/
}
else /* not in interpreter, normal logic */
{
comCon = SCGetContext(pCon);
/* Return 0 without dying if no message data */
if(pBuffer == NULL)
{
return 0;
}
taskID = comCon.transID;
pMsg = CreateDynString(INIT_STR_SIZE, STR_RESIZE_LENGTH);
pMsgString = CreateDynString(INIT_STR_SIZE, STR_RESIZE_LENGTH);
pBueffel[0] = '\0';
sprintf(pBueffel,"[con%4.4d:",(int)pCon->ident); /* field 1: connID */
DynStringConcat(pMsg,pBueffel);
sprintf(pBueffel,"t%6.6d:",(int)taskID); /* field 2: taskID */
DynStringConcat(pMsg,pBueffel);
/* deviceID */
DynStringConcat(pMsg,comCon.deviceID);
DynStringConcatChar(pMsg,':');
/* msgFlag */
switch(iOut) {
case 5: /* eValue */
DynStringConcat(pMsg,"out");
break;
default:
DynStringConcat(pMsg,pCode[iOut]);
break;
}
DynStringConcatChar(pMsg,']');
if (iOut == eStart){
DynStringConcat(pMsgString, comCon.deviceID);
}
if (iOut == eEvent) {
DynStringConcat(pMsgString, " type=");
/* Default type to VALUECHANGE if conEventType not set */
if (-1 == pCon->conEventType)
DynStringConcat(pMsgString, pEventType[0]);
else
DynStringConcat(pMsgString, pEventType[pCon->conEventType]);
/* DynStringConcat(pMsgString, " status=");
DynStringConcat(pMsgString, pStatus[pCon->conStatus]);*/
DynStringConcat(pMsgString,",");
}
DynStringConcat(pMsgString," ");
DynStringConcat(pMsgString,pBuffer);
sycformat(comCon.deviceID, iOut, pMsgString, pMsg);
/* is this really to be printed ? */
if(iOut < pCon->iOutput)
{
if (pMsg != NULL)
DeleteDynString(pMsg);
return 0;
}
/* first the socket */
/*strcat(pMsg, pBueffel);*/
iRet = SCDoSockWrite(pCon,GetCharArray(pMsg));
SCWriteToLogFiles(pCon,GetCharArray(pMsg));
}
if (pMsg != NULL)
DeleteDynString(pMsg);
return 1;
}
/* Only work for hipadaba commands, hlist, hset, hget, hnotify
* A multiline string (ie have crnl) will be converted to an array.
* Strings with '=' will be converted to name value pairs
*/
struct json_object *mkJSON_Object(SConnection *pCon, char *pBuffer, int iOut)
{
int linenum = __LINE__;
char pBueffel[MAXMSG], *pBufferFrom, *pBufferTo;
long taskID = 0;
struct json_object *msg_json=NULL, *tmp_json=NULL;
commandContext comCon;
char pError[256];
pError[0]='\0';
if (strlen(pBuffer) == 0) {
return 0;
}
if(!SCVerifyConnection(pCon))
{
return 0;
}
comCon = SCGetContext(pCon);
/* Return 0 without dying if no message data */
if(pBuffer == NULL)
{
return 0;
}
/*
build the Tcl-command to execute for formatting the
data into a sycamore string
*/
taskID = comCon.transID;
pBueffel[0] = '\0';
msg_json = json_object_new_object();
if (is_error(msg_json)) { linenum = __LINE__; goto reporterr; }
/* field 1: connID */
json_object_object_add(msg_json, "con", json_object_new_int(pCon->ident));
/* field 2: taskID */
json_object_object_add(msg_json, "trans", json_object_new_int(taskID));
/* deviceID */
json_object_object_add(msg_json, "object", json_object_new_string(comCon.deviceID));
/* msgFlag */
switch(iOut) {
case 5: /* eValue */
json_object_object_add(msg_json, "flag", json_object_new_string("out"));
break;
default:
json_object_object_add(msg_json, "flag", json_object_new_string(pCode[iOut]));
break;
}
if (iOut == eHdbValue || iOut == eHdbEvent) {
tmp_json = json_tokener_parse(pBuffer);
if (is_error(tmp_json)) { linenum = __LINE__; goto reporterr; }
} else {
/* Strip \r and \n */
for (pBufferFrom=pBufferTo=pBuffer; ; pBufferFrom++) {
if (*pBufferFrom == '\r' || *pBufferFrom == '\n')
continue;
pBufferTo = pBufferFrom;
if (*pBufferTo == '\0')
break;
pBufferTo++;
}
tmp_json = json_object_new_string(pBuffer);
if (is_error(tmp_json)) { linenum = __LINE__; goto reporterr; }
}
json_object_object_add(msg_json, "data", tmp_json);
return msg_json;
reporterr:
SCSetWriteFunc(pCon,SCNormalWrite);
snprintf(pError, 256,"{\"ERROR\": \"%s:%d Error making json object\"}", __FILE__, linenum);
SCWrite(pCon,pError,eError);
SCSetWriteFunc(pCon,SCWriteJSON_String);
cleanup:
if (tmp_json != NULL && !is_error(tmp_json))
json_object_put(tmp_json);
if (msg_json != NULL && !is_error(msg_json))
json_object_put(msg_json);
return NULL;
}
int SCWriteJSON_String(SConnection *pCon, char *pBuffer, int iOut)
{
struct json_object *my_object=NULL, *tmp_json=NULL;
char pBueffel[MAXMSG], errBuff[MAXMSG];
int iRet, errLen = MAXMSG;
if (strlen(pBuffer) == 0)
return 1;
/* log it for any case */
if(pCon->pSock)
{
iRet = pCon->pSock->sockid;
}
else
{
iRet = 0;
}
sprintf(pBueffel,"Next line intended for socket: %d",iRet);
SICSLogWrite(pBueffel,eInternal);
SICSLogWrite(pBuffer,iOut);
/* write to commandlog if user or manager privilege */
if(SCGetRights(pCon) <= usUser)
{
if(pCon->iMacro != 1)
{
WriteToCommandLogId(NULL,iRet,pBuffer);
}
else
{
if(iOut == eError || iOut == eWarning)
{
WriteToCommandLogId(NULL,iRet,pBuffer);
}
}
}
if(SCinMacro(pCon))
{
InterpWrite(pServ->pSics,pBuffer);
/* print it to client if error message */
if((iOut== eError) || (iOut == eWarning) )
{
tmp_json = json_object_new_string(pBuffer);
iRet = SCDoSockWrite(pCon,json_object_to_json_string(tmp_json));
}
} else {
if ((my_object = mkJSON_Object(pCon, pBuffer, iOut)) == NULL) {
snprintf(errBuff, errLen, "failed to make JSON object from, %s", pBuffer);
tmp_json = json_object_new_string(errBuff);
my_object = json_object_new_object();
json_object_object_add(my_object, "ERROR", tmp_json);
SCDoSockWrite(pCon,json_object_to_json_string(my_object));
iRet = 0;
} else {
iRet = SCDoSockWrite(pCon,json_object_to_json_string(my_object));
SCWriteToLogFiles(pCon,pBuffer);
}
}
if (tmp_json != NULL && !is_error(tmp_json))
json_object_put(tmp_json);
if (my_object != NULL && !is_error(my_object))
json_object_put(my_object);
return iRet;
}
/*------------------------------------------------------------------------*/
/* Protocol API */
char * GetProtocolName(SConnection* pCon)
{
pProtocol pPro;
pSicsInterp pSics;
if(!SCVerifyConnection(pCon))
{
return NULL;
}
pSics = GetInterpreter();
if(!pSics) return NULL;
pPro = FindCommandData(pSics,"protocol","Protocol");
if(!pPro) return NULL;
InitDefaultProtocol(pCon,pPro);
/* check list of protocols for valid name */
switch(pCon->iProtocolID)
{
case 0: /* default = psi_sics */
case 1: /* normal (connection start default) */
case 2: /* outcodes */
case 3: /* sycamore */
case 4: /* json */
return strdup(pPro->pProList[pCon->iProtocolID]);
break;
default:
return strdup("invalid");
break;
}
}
/*----------------------------------*/
int GetProtocolID(SConnection* pCon)
{
if(NULL!=pCon)
{
return pCon->iProtocolID;
}
return -1;
}
/*---------------------------------------------------------------------------*/
writeFunc GetProtocolWriteFunc(SConnection *pCon){
if(pCon != NULL){
switch(pCon->iProtocolID){
case 2: /* outcodes */
return SCWriteWithOutcode;
break;
case 3: /* sycamore */
return SCWriteSycamore;
break;
case 4: /* json */
return SCWriteJSON_String;
break;
case 5:
return SCACTWrite;
break;
default:
return SCNormalWrite;
break;
}
}
return SCNormalWrite;
}