Files
sics/protocol.c
zolliker 61341b52f4 various improvements
- use dig for resolving host names
- ascon.c: fix terminator parsing
- property callback: change property before callback
- logger.c:default for logger period must be the old value instead of 1
- add frappy type history writing
- increase max. logreader line length
- HIPNONE returns "null" with json protocol
- encode strings properly in formatNameValue
- fix memory leak in json2tcl
- scriptcontext: do not show debug messages when script starts with underscore or when the "send" property is empty
- scriptcontext: remove args for action timestamp
- scriptcontext: "que" function will replace an already queued action, e.g. for 'halt
- introduced updatestatus script
2021-09-16 12:26:18 +02:00

675 lines
19 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.h>
#include <dynstring.h>
#include "protocol.h"
#include <json-c/json.h>
#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;
}