/*-------------------------------------------------------------------------- ANSTO Protocol Command Object Paul Hathaway, November, 2004 Copyright: See copyright.txt ----------------------------------------------------------------------------*/ #include #include #include #include #include #include #include #include #include #include #include #include "commandlog.h" #include "protocol.h" #include #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;ipProList[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;iiNumPros;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;iisDefaultSet) { 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; }