/** * This is a generic controller for devices in SICS. It is configurable via Tcl * scripts. * * copyright: see file COPYRIGHT * * Mark Koennecke, November 2007 */ #include #include #include #include #include #include #include #include #include /*--------------------------------------------------------------------------*/ 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; }