*** empty log message ***
This commit is contained in:
613
genericcontroller.c
Normal file
613
genericcontroller.c
Normal file
@@ -0,0 +1,613 @@
|
||||
/**
|
||||
* 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 int GenConSetCallback(void *userData, void *callData, pHdb node,
|
||||
hdbValue v){
|
||||
pSICSOBJ self = (pSICSOBJ)userData;
|
||||
SConnection *pCon = (SConnection *)callData;
|
||||
pGenController priv = NULL;
|
||||
char command[1024];
|
||||
char value[80];
|
||||
int status, privilege;
|
||||
pDynString data;
|
||||
|
||||
assert(self != NULL);
|
||||
|
||||
priv = (pGenController)self->pPrivate;
|
||||
|
||||
/*
|
||||
* 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 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 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 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* check status
|
||||
*/
|
||||
memset(value,0,80);
|
||||
GetHdbProperty(node,"status",value,80);
|
||||
if(strstr(value,"idle") == NULL){
|
||||
return 0;
|
||||
}
|
||||
SetHdbProperty(node,"status","setting");
|
||||
data = formatValue(v);
|
||||
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 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
/*--------------------------------------------------------------------------*/
|
||||
static int GenConGetCallback(void *userData, void *callData, pHdb node,
|
||||
hdbValue v){
|
||||
pSICSOBJ self = (pSICSOBJ)userData;
|
||||
SConnection *pCon = (SConnection *)callData;
|
||||
pGenController priv = NULL;
|
||||
char command[1024];
|
||||
char value[256];
|
||||
int status, privilege;
|
||||
|
||||
assert(self != NULL);
|
||||
|
||||
priv = (pGenController)self->pPrivate;
|
||||
|
||||
/*
|
||||
* check status
|
||||
*/
|
||||
memset(value,0,80);
|
||||
GetHdbProperty(node,"status",value,80);
|
||||
if(strstr(value,"idle") == NULL){
|
||||
return 1;
|
||||
}
|
||||
SetHdbProperty(node,"status","getting");
|
||||
|
||||
/*
|
||||
* check readCommand
|
||||
*/
|
||||
memset(value,0,256);
|
||||
GetHdbProperty(node,"readCommand",value,255);
|
||||
if(strlen(value) < 2){
|
||||
return 0;
|
||||
} else {
|
||||
if(priv->enqueueNode != NULL){
|
||||
priv->enqueueNode(self,pCon, node);
|
||||
} else {
|
||||
if(pCon != NULL){
|
||||
SCWrite(pCon,"ERROR: generic controller connection NOT configured",
|
||||
eError);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Upper Level GetHipadabaPar will automtaically return the
|
||||
* node value. Which should have been updated through an update
|
||||
* during the execution of enqueueNode
|
||||
*/
|
||||
|
||||
return 1;
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
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,
|
||||
-1,
|
||||
-1);
|
||||
if(kalle == NULL){
|
||||
return NULL;
|
||||
}
|
||||
AppendHipadabaCallback(result,HCBSET, kalle);
|
||||
|
||||
kalle = MakeHipadabaCallback(GenConGetCallback,
|
||||
self,
|
||||
NULL,
|
||||
-1,
|
||||
-1);
|
||||
if(kalle == NULL){
|
||||
return NULL;
|
||||
}
|
||||
AppendHipadabaCallback(result,HCBREAD, 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 = makeHdbData(HIPFUNC,1,EnqueFunc);
|
||||
node = MakeSICSHdbPar("enqueue",usUser, funcValue);
|
||||
AddSICSHdbPar(node,"node",usUser,textValue);
|
||||
AppendHipadabaCallback(node,HCBSET,MakeSICSFuncCallback(pNew));
|
||||
AddHipadabaChild(pNew->objectNode,node,NULL);
|
||||
|
||||
funcValue = makeHdbData(HIPFUNC,1,EnqueHeadFunc);
|
||||
node = MakeSICSHdbPar("enqueuehead",usUser, funcValue);
|
||||
AddSICSHdbPar(node,"node",usUser,textValue);
|
||||
AppendHipadabaCallback(node,HCBSET,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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user