623 lines
18 KiB
C
623 lines
18 KiB
C
/**
|
|
* This is a generic controller for devices in SICS. It is configurable via Tcl
|
|
* scripts.
|
|
*
|
|
* copyright: see file COPYRIGHT
|
|
*
|
|
* Mark Koennecke, November 2007
|
|
*/
|
|
#include <stdlib.h>
|
|
#include <assert.h>
|
|
#include <tcl.h>
|
|
#include <sics.h>
|
|
#include <macro.h>
|
|
#include <sicshipadaba.h>
|
|
#include <sicsobj.h>
|
|
#include <dynstring.h>
|
|
#include <genericcontroller.h>
|
|
/*--------------------------------------------------------------------------*/
|
|
static hdbCallbackReturn GenConSetCallback(pHdb node, void *userData,
|
|
pHdbMessage message){
|
|
pSICSOBJ self = (pSICSOBJ)userData;
|
|
SConnection *pCon = NULL;
|
|
pGenController priv = NULL;
|
|
char command[1024];
|
|
char value[80];
|
|
int status, privilege;
|
|
pDynString data;
|
|
pHdbDataMessage mm = NULL;
|
|
|
|
assert(self != NULL);
|
|
|
|
if((mm = GetHdbSetMessage(message)) == NULL){
|
|
return hdbContinue;
|
|
}
|
|
|
|
priv = (pGenController)self->pPrivate;
|
|
pCon = mm->callData;
|
|
|
|
/*
|
|
* check rights
|
|
*/
|
|
memset(value,0,80);
|
|
if(GetHdbProperty(node,"priv",value,80) && pCon != NULL){
|
|
privilege = usInternal;
|
|
if(strcmp(value,"manager") == 0){
|
|
privilege = usMugger;
|
|
}
|
|
if(strcmp(value,"user") == 0){
|
|
privilege = usUser;
|
|
}
|
|
if(!SCMatchRights(pCon,privilege)){
|
|
return hdbAbort;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* check writeCommand
|
|
*/
|
|
memset(value,0,80);
|
|
GetHdbProperty(node,"writeCommand",value,80);
|
|
if(strlen(value) < 2){
|
|
if(pCon != NULL){
|
|
SCWrite(pCon,"ERROR: parameter is read-only",eError);
|
|
return hdbAbort;
|
|
}
|
|
return hdbAbort;
|
|
}
|
|
|
|
/*
|
|
* check status
|
|
*/
|
|
memset(value,0,80);
|
|
GetHdbProperty(node,"status",value,80);
|
|
if(strstr(value,"idle") == NULL){
|
|
return hdbAbort;
|
|
}
|
|
SetHdbProperty(node,"status","setting");
|
|
data = formatValue(*(mm->v), node);
|
|
if(data != NULL){
|
|
SetHdbProperty(node,"target",GetCharArray(data));
|
|
DeleteDynString(data);
|
|
}
|
|
|
|
/*
|
|
* issue command
|
|
*/
|
|
if(priv->enqueueNodeHead != NULL){
|
|
priv->enqueueNodeHead(self,pCon,node);
|
|
} else {
|
|
if(pCon != NULL){
|
|
SCWrite(pCon,"ERROR: generic controller NOT configured",
|
|
eError);
|
|
}
|
|
return hdbAbort;
|
|
}
|
|
|
|
return hdbContinue;
|
|
}
|
|
/*--------------------------------------------------------------------------*/
|
|
static hdbCallbackReturn GenConGetCallback(pHdb node, void *userData,
|
|
pHdbMessage message){
|
|
pSICSOBJ self = (pSICSOBJ)userData;
|
|
SConnection *pCon = NULL;
|
|
pGenController priv = NULL;
|
|
char command[1024];
|
|
char value[256];
|
|
int status, privilege;
|
|
pHdbDataMessage mm = NULL;
|
|
|
|
assert(self != NULL);
|
|
|
|
if((mm = GetHdbGetMessage(message)) == NULL){
|
|
return hdbContinue;
|
|
}
|
|
pCon = mm->callData;
|
|
|
|
priv = (pGenController)self->pPrivate;
|
|
|
|
/*
|
|
* check status
|
|
*/
|
|
memset(value,0,80);
|
|
GetHdbProperty(node,"status",value,80);
|
|
if(strstr(value,"idle") == NULL){
|
|
return hdbContinue;
|
|
}
|
|
SetHdbProperty(node,"status","getting");
|
|
|
|
/*
|
|
* check readCommand
|
|
*/
|
|
memset(value,0,256);
|
|
GetHdbProperty(node,"readCommand",value,255);
|
|
if(strlen(value) < 2){
|
|
return hdbAbort;
|
|
} else {
|
|
if(priv->enqueueNode != NULL){
|
|
priv->enqueueNode(self,pCon, node);
|
|
} else {
|
|
if(pCon != NULL){
|
|
SCWrite(pCon,"ERROR: generic controller connection NOT configured",
|
|
eError);
|
|
}
|
|
return hdbAbort;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Upper Level GetHipadabaPar will automatically return the
|
|
* node value. Which should have been updated through an update
|
|
* during the execution of enqueueNode
|
|
*/
|
|
|
|
return hdbContinue;
|
|
}
|
|
/*---------------------------------------------------------------------------*/
|
|
static pHdb MakeGenConPar(pSICSOBJ self, char *name, int type, int length){
|
|
pHdb result = NULL;
|
|
pHdbCallback kalle = NULL;
|
|
|
|
result = MakeHipadabaNode(name,type,length);
|
|
if(result == NULL){
|
|
return NULL;
|
|
}
|
|
|
|
kalle = MakeHipadabaCallback(GenConSetCallback,
|
|
self,
|
|
NULL);
|
|
if(kalle == NULL){
|
|
return NULL;
|
|
}
|
|
AppendHipadabaCallback(result,kalle);
|
|
|
|
kalle = MakeHipadabaCallback(GenConGetCallback,
|
|
self,
|
|
NULL);
|
|
|
|
if(kalle == NULL){
|
|
return NULL;
|
|
}
|
|
AppendHipadabaCallback(result,kalle);
|
|
|
|
SetHdbProperty(result,"priv","manager");
|
|
SetHdbProperty(result,"readCommand","");
|
|
SetHdbProperty(result,"writeCommand","");
|
|
SetHdbProperty(result,"replyCommand","");
|
|
SetHdbProperty(result,"status","idle");
|
|
|
|
return result;
|
|
}
|
|
/*---------------------------------------------------------------------------*/
|
|
static int MakeGenPar(pSICSOBJ self, SConnection *pCon,
|
|
char *argv[], int argc){
|
|
char buffer[2048];
|
|
int type, length = 1;
|
|
pHdb node = NULL , parent;
|
|
char *pPtr = NULL;
|
|
|
|
if(argc < 5){
|
|
snprintf(buffer,2048,"ERROR: insufficient arguments to %s makepar",
|
|
argv[0]);
|
|
SCWrite(pCon,buffer, eError);
|
|
return 0;
|
|
}
|
|
type = convertHdbType(argv[4]);
|
|
if(argc > 5){
|
|
length = atoi(argv[5]);
|
|
}
|
|
strncpy(buffer,argv[3],2047);
|
|
pPtr = strrchr(buffer,'/');
|
|
if(pPtr == NULL){
|
|
node = MakeGenConPar(self, argv[3], type, length);
|
|
parent = self->objectNode;
|
|
} else {
|
|
*pPtr = '\0';
|
|
pPtr++;
|
|
node = MakeGenConPar(self, pPtr, type, length);
|
|
parent = GetHipadabaNode(self->objectNode,buffer);
|
|
}
|
|
if(node == NULL || parent == NULL){
|
|
SCWrite(pCon,"ERROR: failed to create node or parent not found",
|
|
eError);
|
|
return 0;
|
|
}
|
|
AddHipadabaChild(parent, node, pCon);
|
|
SCSendOK(pCon);
|
|
return 1;
|
|
}
|
|
/*=============================== ==========================================
|
|
* This stuff is for the Tcl - AsynQueue implementation of GenericController
|
|
* ==========================================================================*/
|
|
typedef struct {
|
|
pHdb node;
|
|
pSICSOBJ obj;
|
|
SConnection *pCon;
|
|
pGenController priv;
|
|
pAsyncUnit assi;
|
|
pAsyncTxn trans;
|
|
commandContext comCon;
|
|
char replyCommand[2048];
|
|
} GenContext, *pGenContext;
|
|
/*--------------------------------------------------------------------------
|
|
* This is called by AsyncQueue when a reply has been received.
|
|
* -------------------------------------------------------------------------*/
|
|
static int GenConTxnHandler(pAsyncTxn pTxn){
|
|
pGenContext genCon = NULL;
|
|
char reply[10240];
|
|
|
|
genCon = (pGenContext)pTxn->cntx;
|
|
assert(genCon != NULL);
|
|
|
|
memset(reply,0,10240*sizeof(char));
|
|
switch(pTxn->txn_state){
|
|
case ATX_NULL:
|
|
case ATX_ACTIVE:
|
|
return 1;
|
|
break;
|
|
case ATX_TIMEOUT:
|
|
strcpy(reply,"TIMEOUT");
|
|
break;
|
|
case ATX_DISCO:
|
|
strcpy(reply,"DISCONNECTED");
|
|
break;
|
|
case ATX_COMPLETE:
|
|
strncpy(reply,pTxn->inp_buf,10240);
|
|
break;
|
|
}
|
|
|
|
if(genCon->pCon != NULL){
|
|
SCPushContext2(genCon->pCon, genCon->comCon);
|
|
}
|
|
genCon->priv->replyCallback(genCon->obj, genCon->pCon,
|
|
genCon->node, genCon->replyCommand, reply, strlen(reply));
|
|
if(genCon->pCon != NULL){
|
|
SCPopContext(genCon->pCon);
|
|
}
|
|
free(genCon);
|
|
|
|
return 1;
|
|
}
|
|
/*--------------------------------------------------------------------------*/
|
|
static char *formatCommand(pHdb node, SConnection *pCon){
|
|
pDynString com = NULL;
|
|
char value[512];
|
|
Tcl_Interp *pTcl = NULL;
|
|
int status;
|
|
|
|
com = CreateDynString(256,128);
|
|
if(com == NULL){
|
|
return NULL;
|
|
}
|
|
|
|
memset(value,0,512);
|
|
GetHdbProperty(node,"status",value,512);
|
|
if(strstr(value,"set") != NULL){
|
|
memset(value,0,512);
|
|
GetHdbProperty(node,"writeCommand",value,512);
|
|
DynStringConcat(com,value);
|
|
DynStringConcatChar(com,' ');
|
|
memset(value,0,512);
|
|
GetHdbProperty(node,"target",value,512);
|
|
DynStringConcat(com,value);
|
|
} else {
|
|
memset(value,0,512);
|
|
GetHdbProperty(node,"readCommand",value,512);
|
|
DynStringConcat(com,value);
|
|
}
|
|
pTcl = InterpGetTcl(pServ->pSics);
|
|
if(pCon != NULL){
|
|
MacroPush(pCon);
|
|
}
|
|
status = Tcl_Eval(pTcl, GetCharArray(com));
|
|
if(pCon != NULL){
|
|
MacroPop();
|
|
}
|
|
DeleteDynString(com);
|
|
if(status != TCL_OK){
|
|
SetHdbProperty(node,"result", (char *)Tcl_GetStringResult(pTcl));
|
|
return NULL;
|
|
}
|
|
return strdup(Tcl_GetStringResult(pTcl));
|
|
}
|
|
/*--------------------------------------------------------------------------*/
|
|
static pGenContext PrepareToEnque(pSICSOBJ self, SConnection *pCon, pHdb node){
|
|
pGenContext result = NULL;
|
|
char *command = NULL;
|
|
pGenController priv = NULL;
|
|
|
|
priv = (pGenController)self->pPrivate;
|
|
assert(priv != NULL);
|
|
|
|
command = formatCommand(node, pCon);
|
|
if(command == NULL){
|
|
return NULL;
|
|
}
|
|
|
|
result = malloc(sizeof(GenContext));
|
|
if(result == NULL){
|
|
return NULL;
|
|
}
|
|
memset(result,0,sizeof(GenContext));
|
|
if(!GetHdbProperty(node,"replyCommand",result->replyCommand,2048)){
|
|
if(pCon != NULL){
|
|
SCWrite(pCon,"ERROR: no replyCommand found",eError);
|
|
}
|
|
free(result);
|
|
return NULL;
|
|
}
|
|
|
|
result->assi = AsyncUnitFromQueue((pAsyncQueue)priv->comContext);
|
|
if(result->assi == NULL){
|
|
return NULL;
|
|
}
|
|
result->trans = AsyncUnitPrepareTxn(result->assi,
|
|
command,strlen(command),
|
|
GenConTxnHandler,
|
|
result,
|
|
2048);
|
|
if(result->trans == NULL){
|
|
return NULL;
|
|
}
|
|
result->node = node;
|
|
result->priv = priv;
|
|
result->obj = self;
|
|
result->pCon = pCon;
|
|
priv->comError = GCOK;
|
|
result->comCon = SCGetContext(pCon);
|
|
free(command);
|
|
|
|
return result;
|
|
}
|
|
/*---------------------------------------------------------------------------*/
|
|
static int AsyncEnqueueNode(pSICSOBJ self, SConnection *pCon, pHdb node){
|
|
pGenContext genCon = NULL;
|
|
|
|
genCon = PrepareToEnque(self, pCon, node);
|
|
if(genCon == NULL){
|
|
return 0;
|
|
}
|
|
return AsyncUnitEnqueueTxn(genCon->assi, genCon->trans);
|
|
}
|
|
/*---------------------------------------------------------------------------*/
|
|
static int AsyncEnqueueNodeHead(pSICSOBJ self, SConnection *pCon, pHdb node){
|
|
pGenContext genCon = NULL;
|
|
|
|
genCon = PrepareToEnque(self, pCon, node);
|
|
if(genCon == NULL){
|
|
return 0;
|
|
}
|
|
return AsyncUnitEnqueueHead(genCon->assi, genCon->trans);
|
|
}
|
|
/*---------------------------------------------------------------------------*/
|
|
static int AsyncReply(pSICSOBJ self, SConnection *pCon, pHdb node,
|
|
char *replyCommand, char *reply, int replylen){
|
|
pDynString com = NULL;
|
|
Tcl_Interp *pTcl;
|
|
int status;
|
|
|
|
SetHdbProperty(node,"result",reply);
|
|
|
|
com = CreateDynString(256,128);
|
|
if(com == NULL){
|
|
return 0;
|
|
}
|
|
DynStringConcat(com, replyCommand);
|
|
DynStringConcat(com," \"");
|
|
DynStringConcat(com, reply);
|
|
DynStringConcat(com,"\"\0");
|
|
if(pCon != NULL){
|
|
MacroPush(pCon);
|
|
}
|
|
pTcl = InterpGetTcl(pServ->pSics);
|
|
status = Tcl_Eval(pTcl,GetCharArray(com));
|
|
if(pCon != NULL){
|
|
MacroPop();
|
|
}
|
|
DeleteDynString(com);
|
|
if(status != TCL_OK){
|
|
SetHdbProperty(node,"lastError",(char *)Tcl_GetStringResult(pTcl));
|
|
}
|
|
return status;
|
|
}
|
|
/*============= GenController Object Functions ==============================*/
|
|
static int ConnectAsync(pSICSOBJ self, SConnection *pCon,
|
|
char *argv[], int argc){
|
|
pGenController priv = NULL;
|
|
pAsyncQueue assi = NULL;
|
|
char buffer[2048];
|
|
pAsyncUnit uni = NULL;
|
|
|
|
priv = (pGenController)self->pPrivate;
|
|
assert(priv != NULL);
|
|
|
|
if(argc < 4){
|
|
snprintf(buffer,2048,"ERROR: insufficient arguments to %s asynconnect",
|
|
argv[0]);
|
|
SCWrite(pCon,buffer, eError);
|
|
return 0;
|
|
}
|
|
|
|
assi = (pAsyncQueue)FindCommandData(pServ->pSics, argv[3],"AsyncQueue");
|
|
if(assi == NULL){
|
|
snprintf(buffer,2048,"ERROR: %s not found or no AsyncQueue", argv[3]);
|
|
SCWrite(pCon,buffer,eError);
|
|
return 0;
|
|
}
|
|
|
|
priv->comContext = assi;
|
|
priv->killComContext = NULL; /* not ours, cleaned up by AsyncQueue module */
|
|
priv->enqueueNode = AsyncEnqueueNode;
|
|
priv->enqueueNodeHead = AsyncEnqueueNodeHead;
|
|
priv->replyCallback = AsyncReply;
|
|
priv->comError = GCOK;
|
|
|
|
/*
|
|
* This unit is solely for the purpose of receiving event notifications
|
|
*/
|
|
uni = AsyncUnitFromQueue(assi);
|
|
|
|
SCSendOK(pCon);
|
|
return 1;
|
|
}
|
|
/*---------------------------------------------------------------------------*/
|
|
int GenControllerConfigure(SConnection *pCon, SicsInterp *pSics,
|
|
void *pData, int argc, char *argv[]){
|
|
pSICSOBJ controller = NULL;
|
|
pGenController self = NULL;
|
|
char buffer[2048];
|
|
|
|
if(argc < 3){
|
|
snprintf(buffer,2048,"ERROR: insufficient arguments to %s", argv[0]);
|
|
SCWrite(pCon,buffer,eError);
|
|
return 0;
|
|
}
|
|
|
|
controller = (pSICSOBJ)FindCommandData(pSics,argv[2], "GenericController");
|
|
if(controller == NULL){
|
|
snprintf(buffer,2048,"ERROR: controller %s not found", argv[2]);
|
|
SCWrite(pCon,buffer,eError);
|
|
return 0;
|
|
}
|
|
|
|
strtolower(argv[1]);
|
|
if(strcmp(argv[1],"makepar") == 0){
|
|
return MakeGenPar(controller,pCon,argv,argc);
|
|
} else if(strcmp(argv[1],"asynconnect") == 0){
|
|
return ConnectAsync(controller, pCon, argv, argc);
|
|
} else {
|
|
SCWrite(pCon,"ERROR: argument to GenControllerConfigure not found",
|
|
eError);
|
|
return 0;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
/*---------------------------------------------------------------------------*/
|
|
static void killGeneric(void *data){
|
|
pGenController self = (pGenController)data;
|
|
|
|
if(self == NULL){
|
|
return;
|
|
}
|
|
if(self->comContext != NULL && self->killComContext != NULL){
|
|
self->killComContext(self->comContext);
|
|
}
|
|
free(self);
|
|
}
|
|
/*---------------------------------------------------------------------------*/
|
|
static int EnqueFunc(pSICSOBJ self, SConnection *pCon, pHdb commandNode,
|
|
pHdb par[], int nPar){
|
|
pGenController priv = NULL;
|
|
pHdb node = NULL;
|
|
char buffer[512];
|
|
|
|
priv = (pGenController)self->pPrivate;
|
|
assert(priv != NULL);
|
|
assert(nPar >= 1);
|
|
|
|
node = GetHipadabaNode(self->objectNode,par[0]->value.v.text);
|
|
if(node == NULL){
|
|
snprintf(buffer,511,"ERROR: node %s to enqueue not found",
|
|
par[0]->value.v.text);
|
|
SCWrite(pCon,buffer,eError);
|
|
return 0;
|
|
}
|
|
|
|
if(priv->enqueueNode != NULL){
|
|
priv->enqueueNode(self, pCon, node);
|
|
} else {
|
|
SCWrite(pCon,"ERROR: GenController NOT configured",eError);
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
/*---------------------------------------------------------------------------*/
|
|
static int EnqueHeadFunc(pSICSOBJ self, SConnection *pCon, Hdb commandNode,
|
|
pHdb par[], int nPar){
|
|
pGenController priv = NULL;
|
|
pHdb node = NULL;
|
|
char buffer[512];
|
|
|
|
priv = (pGenController)self->pPrivate;
|
|
assert(priv != NULL);
|
|
assert(nPar >= 1);
|
|
|
|
node = GetHipadabaNode(self->objectNode,par[0]->value.v.text);
|
|
if(node == NULL){
|
|
snprintf(buffer,511,"ERROR: node %s to enqueue not found",
|
|
par[0]->value.v.text);
|
|
SCWrite(pCon,buffer,eError);
|
|
return 0;
|
|
}
|
|
|
|
if(priv->enqueueNodeHead != NULL){
|
|
priv->enqueueNodeHead(self, pCon, node);
|
|
} else {
|
|
SCWrite(pCon,"ERROR: GenController NOT configured",eError);
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
/*---------------------------------------------------------------------------*/
|
|
int GenControllerFactory(SConnection *pCon, SicsInterp *pSics,
|
|
void *pData, int argc, char *argv[]){
|
|
pSICSOBJ pNew = NULL;
|
|
pGenController priv = NULL;
|
|
hdbValue funcValue, textValue;
|
|
pHdb node = NULL;
|
|
char line[132];
|
|
int status;
|
|
|
|
if(argc < 2){
|
|
SCWrite(pCon,
|
|
"ERROR: insufficient number of arguments to GenControllerFactrory",
|
|
eError);
|
|
return 0;
|
|
}
|
|
priv = malloc(sizeof(GenController));
|
|
if(priv == NULL){
|
|
SCWrite(pCon,"ERROR: out of memory in GenControllerFactory",
|
|
eError);
|
|
return 0;
|
|
}
|
|
memset(priv,0,sizeof(GenController));
|
|
|
|
pNew = MakeSICSOBJ(argv[1],"GenericController");
|
|
if(pNew == NULL){
|
|
SCWrite(pCon,"ERROR: out of memory in GenControllerFactory",
|
|
eError);
|
|
return 0;
|
|
}
|
|
pNew->pPrivate = priv;
|
|
pNew->KillPrivate = killGeneric;
|
|
|
|
textValue = MakeHdbText("Undefined");
|
|
funcValue = MakeHdbFunc((voidFunc *)EnqueFunc);
|
|
node = MakeSICSHdbPar("enqueue",usUser, funcValue);
|
|
AddSICSHdbPar(node,"node",usUser,textValue);
|
|
AppendHipadabaCallback(node,MakeSICSFuncCallback(pNew));
|
|
AddHipadabaChild(pNew->objectNode,node,NULL);
|
|
|
|
funcValue = MakeHdbFunc((voidFunc *)EnqueHeadFunc);
|
|
node = MakeSICSHdbPar("enqueuehead",usUser, funcValue);
|
|
AddSICSHdbPar(node,"node",usUser,textValue);
|
|
AppendHipadabaCallback(node,MakeSICSFuncCallback(pNew));
|
|
AddHipadabaChild(pNew->objectNode,node,NULL);
|
|
|
|
status = AddCommand(pSics,
|
|
argv[1],
|
|
InvokeSICSOBJ,
|
|
KillSICSOBJ,
|
|
pNew);
|
|
if(status != 1){
|
|
KillSICSOBJ(pNew);
|
|
SCPrintf(pCon,eError,"ERROR: failed create duplicate command %s", argv[1]);
|
|
return 0;
|
|
}
|
|
SCSendOK(pCon);
|
|
|
|
return 1;
|
|
}
|
|
|