/** * 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; 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; } genCon->priv->replyCallback(genCon->obj, genCon->pCon, genCon->node, genCon->replyCommand, reply, strlen(reply)); if (genCon->pCon != NULL) { SCDeleteConnection(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 = SCCopyConnection(pCon); priv->comError = GCOK; 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; }