/*-------------------------------------------------------------------------- ANSTO Protocol Command Object Paul Hathaway, November, 2004 Copyright: See copyright.txt ----------------------------------------------------------------------------*/ #include #include #include #include #include #include #include #include #include #include #include #include "protocol.h" #include #define MAXMSG 1024 #define INIT_STR_SIZE 256 #define STR_RESIZE_LENGTH 256 #define NUMPROS 6 #define PROLISTLEN 8 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; /*================================================================================================ WARNING: These two char arrays may replicate things defined elsewhere. They may be out of sync with the rest of SIS. Keep in mind..... ==================================================================================================*/ 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; 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 SCWriteJSON_String(SConnection * pCon, char *pBuffer, int iOut); /* Signatures for protocols from conman.c*/ extern int SCAllWrite(SConnection * self, char *buffer, int iOut); /*--------------------------------------------------------------------------*/ pProtocol CreateProtocol(void) { int i, iNumPros = NUMPROS; char *pPros[] = { "default", "normal", "withcode", "json", "act", "all", 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); } 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[]) { SConnection *comCon = NULL; char buffer[1024]; char *command; int status; if (argc < 3) { SCWrite(pCon, "ERROR: insufficient arguments to contextdo", eError); return 0; } comCon = SCCopyConnection(pCon); if (comCon == NULL) { SCWrite(pCon, "ERROR: out of memory in 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; } strlcpy(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; } if(comCon->transID > 100000) { SCPrintf(comCon,eLog,"COMSTART %d", comCon->transID); } status = InterpExecute(pSics, comCon, command); if(comCon->transID > 100000) { SCPrintf(comCon,eLog,"COMEND %d", comCon->transID); } if (command != buffer) free(command); SCDeleteConnection(comCon); 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(void) { pProtocol pNew = NULL; pNew = CreateProtocol(); if (NULL != pNew) { AddCommand(pServ->pSics, "Protocol", ProtocolAction, DeleteProtocol, pNew); AddCommand(pServ->pSics, "contextdo", ContextDo, NULL, NULL); } } /*------------------------------------------------------------------------*/ static int ProtocolOptions(SConnection * pCon, pProtocol pPro) { int i; char pBuffer[80]; for (i = 0; i < pPro->iNumPros; i++) { snprintf(pBuffer,sizeof(pBuffer)-1, "Protocol[%d] = %s", i, pPro->pProList[i]); SCWrite(pCon, pBuffer, eValue); } return 1; } /*------------------------------------------------------------------------*/ static int ProtocolHelp(SConnection * pCon, Protocol * pPro) { SCWrite(pCon, "Usage: protocol {help|list|options|reset} | set protocolName", eValue); return 1; } /*------------------------------------------------------------------------*/ static int ProtocolSet(SConnection * pCon, Protocol * pPro, char *pProName) { SConnection *pMaster = NULL; int proID; if (!SCVerifyConnection(pCon)) { return 0; } pMaster = SCfindMaster(pCon); assert(pMaster != NULL); /* 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 PROTNORM: /* normal (connection start default) */ SCSetWriteFunc(pMaster, SCNormalWrite); SCSetWriteFunc(pCon, SCNormalWrite); break; case PROTCODE: /* outcodes */ SCSetWriteFunc(pMaster, SCWriteWithOutcode); SCSetWriteFunc(pCon, SCWriteWithOutcode); break; case PROTJSON: /* json */ SCSetWriteFunc(pCon, SCWriteJSON_String); SCSetWriteFunc(pMaster, SCWriteJSON_String); break; case PROTACT: /* ACT */ SCSetWriteFunc(pMaster, SCACTWrite); SCSetWriteFunc(pCon, SCACTWrite); break; case PROTALL: SCSetWriteFunc(pMaster, SCAllWrite); SCSetWriteFunc(pCon, SCAllWrite); break; case PROTSICS: /* default = psi_sics */ default: SCSetWriteFunc(pMaster, pPro->defaultWriter); SCSetWriteFunc(pCon, pPro->defaultWriter); break; } SCSetProtocolID(pCon,proID); SCSetProtocolID(pMaster,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) { SCSetProtocolID(pCon,0); return 1; } /* lazy initialisation of defaultWriter since connection is verified */ if (0 == pPro->isDefaultSet) { pPro->defaultWriter = SCGetWriteFunc(pCon); pPro->isDefaultSet = 1; SCSetProtocolID(pCon,0); } strlcpy(pProName, pPro->pProList[SCGetProtocolID(pCon)], len); return 1; #if 0 Index = SCGetProtocolID(pCon); /* check list of protocols for valid name */ switch (Index) { case PROTSICS: /* default = psi_sics */ case PROTNORM: /* normal (connection start default) */ case PROTCODE: /* outcodes */ case PROTJSON: /* json */ case PROTACT: /* 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", eValue); 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; SCSetProtocolID(pCon,PROTSICS); } return pPro->isDefaultSet; } /*--------------------------------------------------------------------------*/ /* 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 (msg_json == NULL) { linenum = __LINE__; goto reporterr; } /* field 1: connID */ json_object_object_add(msg_json, "con", json_object_new_int(SCGetIdent(pCon))); /* 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 (strcmp(pBuffer, "null") != 0) { if (tmp_json == NULL) { tmp_json = json_object_new_string(pBuffer); } if (tmp_json == NULL) { 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 (tmp_json == NULL) { 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) json_object_put(tmp_json); if (msg_json != NULL) 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 */ iRet = SCGetSockHandle(pCon); /* write to commandlog if user or manager privilege */ if (SCGetRights(pCon) <= usUser && !SCinMacro(pCon)) { Log(INFO,"com","sock%03.3d:%s",iRet, pBuffer); } else { Log(DEBUG,"com","sock%03.3d:%s", 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, (char *)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, (char *)json_object_to_json_string(my_object)); iRet = 0; } else { iRet = SCDoSockWrite(pCon, (char *)json_object_to_json_string(my_object)); } } if (tmp_json != NULL ) json_object_put(tmp_json); if (my_object != NULL ) 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 (SCGetProtocolID(pCon)) { case PROTSICS: /* default = psi_sics */ case PROTNORM: /* normal (connection start default) */ case PROTCODE: /* outcodes */ case PROTJSON: /* json */ case PROTACT: /* act */ return strdup(pPro->pProList[SCGetProtocolID(pCon)]); break; default: return strdup("invalid"); break; } } /*----------------------------------*/ int GetProtocolID(SConnection * pCon) { return SCGetProtocolID(pCon); } /*---------------------------------------------------------------------------*/ writeFunc GetProtocolWriteFunc(SConnection * pCon) { if (pCon != NULL) { switch (SCGetProtocolID(pCon)) { case PROTCODE: /* outcodes */ return SCWriteWithOutcode; break; case PROTJSON: /* json */ return SCWriteJSON_String; break; case PROTACT: return SCACTWrite; break; default: return SCNormalWrite; break; } } return SCNormalWrite; }