Files
sics/genericcontroller.c
2008-05-14 14:23:16 +00:00

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;
}