/* * Experience has shown that integrating existing SICS objects into the * Hierarchical Parameter Database (Hdb) is a difficult task without reworking * the complete SICS object model. Rather, it seems easier to adapt some * critical objects to the Hdb with some glue code. Following the facade or * adapter design pattern. This is the purpose of this module. For the moment * the external interface is only an interpreter function which will be used to * install suitable SICS objects into the Hdb tree and generates the necessary * adapters internally. This code can be used to adapt to: * - motors * - the data segment of histogram memories * * copyright: see file COPYRIGHT * * Mark Koennecke, November 2006 */ #include #include #include #include "stptok.h" #include "motor.h" #include "HistMem.h" #include "sicsvar.h" #include "counter.h" #include "lld.h" #include "sicshipadaba.h" #include "sicshdbadapter.h" #include "sicsdata.h" #define PRIVNAM "priv" /*==================== support code ====================================*/ static void AddPrivProperty(pHdb node, int priv){ char pPriv[80]; switch(priv){ case usInternal: strcpy(pPriv,"internal"); break; case usMugger: strcpy(pPriv,"manager"); break; case usUser: strcpy(pPriv,"user"); break; case usSpy: strcpy(pPriv,"spy"); break; default: assert(0); break; } SetHdbProperty(node,PRIVNAM,pPriv); } /*=================== motor code =======================================*/ static int MoveCallback(int iEvent, void *eventData, void *userData, commandContext cc){ MotCallback *motData = (MotCallback *)eventData; pHdb motor = (pHdb)userData; pHdb pos = NULL; if(iEvent == MOTDRIVE && motData != NULL && motor != NULL){ UpdateHipadabaPar(motor,MakeHdbFloat((double)motData->fVal) ,NULL); pos = GetHipadabaNode(motor,"position"); if(pos != NULL){ UpdateHipadabaPar(pos,MakeHdbFloat((double)motData->fVal) ,NULL); } } return 1; } /*---------------------------------------------------------------------*/ static int MotorValueCallback(int iEvent, void *eventData, void *userData, commandContext cc){ pHdb motor = (pHdb)userData; pMotor pMot = (pMotor)eventData; pHdb current = NULL; float fVal; /* * as setting some motor parameters might cause other motor * parametes to change too, I opt for the cheap solution to check * them all. */ if(iEvent == HDBVAL && motor != NULL && pMot != NULL){ current = motor->child; while(current != NULL){ MotorGetPar(pMot,current->name,&fVal); if(fVal != current->value.v.doubleValue) { UpdateHipadabaPar(current,MakeHdbFloat((double)fVal),NULL); } current = current->next; } } return 1; } /*---------------------------------------------------------------------*/ static hdbCallbackReturn MotorParSetCallback(pHdb currentNode, void *userData, pHdbMessage message){ pMotor pMot = (pMotor)userData; SConnection *pCon = NULL; int status; pHdbDataMessage mm = NULL; if((mm = GetHdbSetMessage(message)) == NULL){ return hdbContinue; } pCon = mm->callData; assert(pMot != NULL && pCon != NULL); status = MotorSetPar(pMot,pCon,currentNode->name, (float)mm->v->v.doubleValue); if(status == 1){ return hdbContinue; } else { return hdbAbort; } } /*----------------------------------------------------------------------*/ static hdbCallbackReturn MotorParGetCallback(pHdb currentNode, void *userData, pHdbMessage message){ pMotor pMot = (pMotor)userData; float fVal; int status; pHdbDataMessage mm = NULL; if((mm = GetHdbGetMessage(message)) == NULL){ return hdbContinue; } assert(pMot != NULL); status = MotorGetPar(pMot,currentNode->name,&fVal); currentNode->value.v.doubleValue = fVal; if(status == 1){ return hdbContinue; } else { return hdbAbort; } } /*---------------------------------------------------------------------*/ static pHdb MakeMotParNode(char *name, pMotor pMot){ pHdb node = NULL; pHdbCallback pCall = NULL; char command[1024]; node = MakeHipadabaNode(name, HIPFLOAT, 1); if(node != NULL) { pCall = MakeHipadabaCallback(MotorParSetCallback,pMot,NULL); if(pCall == NULL){ return NULL; } AppendHipadabaCallback(node,pCall); pCall = MakeHipadabaCallback(MotorParGetCallback,pMot,NULL); if(pCall == NULL){ return NULL; } AppendHipadabaCallback(node,pCall); snprintf(command,1023,"%s %s ", pMot->name, name); SetHdbProperty(node,"sicscommand", command); } return node; } /*---------------------------------------------------------------------*/ static int AddStdMotorPar(pHdb motorNode, pMotor pMot){ int i; pHdb parNode = NULL; char *addPar[] = {"target", "hardlowerlim", "hardupperlim", NULL}; i = 0; while(addPar[i] != NULL){ parNode = MakeMotParNode(addPar[i],pMot); SetHdbProperty(parNode,PRIVNAM,"internal"); if(parNode == NULL){ return 0; } AddHipadabaChild(motorNode,parNode, NULL); i++; } /* * Add the parameters in the obpar array */ for(i = 0; i < MOTOBPARLENGTH; i++){ parNode = MakeMotParNode(pMot->ParArray[i].name,pMot); if(parNode == NULL){ return 0; } AddHipadabaChild(motorNode,parNode, NULL); AddPrivProperty(parNode,pMot->ParArray[i].iCode); } return 1; } /*--------------------------------------------------------------------------*/ static char *getDriverParList(MotorDriver *pDriv){ SConnection *pCon = NULL; pDynString list = NULL; char *listData = NULL; if(pDriv->ListDriverPar != NULL){ pCon = SCCreateDummyConnection(pServ->pSics); if(pCon == NULL){ return NULL; } SCStartBuffering(pCon); pDriv->ListDriverPar(pDriv,"test.", pCon); list = SCEndBuffering(pCon); if(list != NULL){ listData = strdup(GetCharArray(list)); SCDeleteConnection(pCon); } else { listData = NULL; } return listData; } return NULL; } /*--------------------------------------------------------------------------*/ extern char *trim(char *str); /*--------------------------------------------------------------------------*/ static char *extractName(char *line){ char *name = NULL, *pEnd = NULL; name = strchr(line,'.'); assert(name != NULL); while(*name == '.'){ name++; } pEnd = strchr(name,'='); assert(pEnd != NULL); *pEnd = '\0'; return trim(name); } /*------------------------------------------------------------------------*/ static int CreateDriverParameters(pMotor pM, pHdb parent){ char *listPtr = NULL, line[80], *pPtr, *name; pHdb node = NULL; listPtr = getDriverParList(pM->pDriver); if(listPtr == NULL){ /* * no driver parameters */ return 1; } pPtr = listPtr; while((pPtr = stptok(pPtr,line,79,"\n")) != NULL){ name = extractName(line); node = MakeMotParNode(name,pM); SetHdbProperty(node,PRIVNAM,"manager"); if(node != NULL){ AddHipadabaChild(parent,node,NULL); } } free(listPtr); return 1; } /*----------------------------------------------------------------------*/ static pHdb CreateMotorAdapter(char *name, pMotor pMot){ pHdb result = NULL; commandContext comCom; float access; assert(pMot != NULL); result = MakeSICSHdbDriv(name,usUser,pMot,HIPFLOAT); if(result == NULL){ return NULL; } MotorGetPar(pMot,"accesscode",&access); AddPrivProperty(result,(int)access); SetHdbProperty(result,"type","drivable"); SetHdbProperty(result,"sicsdev",pMot->name); /* * We want to be notified when this motor drives around. Or * its parameters change. */ strncpy(comCom.deviceID,name,255); comCom.transID = -77; RegisterCallback(pMot->pCall,comCom, MOTDRIVE, MoveCallback, result,NULL); RegisterCallback(pMot->pCall,comCom, HDBVAL, MotorValueCallback, result,NULL); if(!AddStdMotorPar(result,pMot)){ DeleteHipadabaNode(result,NULL); return NULL; } if(!CreateDriverParameters(pMot,result)){ DeleteHipadabaNode(result,NULL); return NULL; } result->protected = 1; return result; } /*============== histogram memory ======================================*/ static long totalSum(int *data, int length){ long result = 0l; int i; if(data == NULL){ return 0; } for(i = 0; i < length; i++){ result += data[i]; } return result; } /*----------------------------------------------------------------------*/ typedef struct { pHistMem pHM; long oldSum; } HMAdapter, *pHMAdapter; /*-------------------------------------------------------------------------*/ static hdbCallbackReturn HMDataGetCallback(pHdb currentNode, void *userData, pHdbMessage message){ pHMAdapter pHMA = (pHMAdapter)userData; SConnection *pCon = NULL; long sum1; pHdbDataMessage mm = NULL; if((mm = GetHdbGetMessage(message)) == NULL){ return hdbContinue; } pCon = mm->callData; assert(pHMA != NULL && pHMA->pHM != NULL); if(pCon == NULL){ return hdbAbort; } currentNode->value.arrayLength = GetHistLength(pHMA->pHM); currentNode->value.v.intArray = (int *)GetHistogramPointer(pHMA->pHM,pCon); sum1 = totalSum(currentNode->value.v.intArray, currentNode->value.arrayLength); if(sum1 != pHMA->oldSum){ UpdateHipadabaPar(currentNode,currentNode->value,NULL); pHMA->oldSum = sum1; } return hdbContinue; } /*----------------------------------------------------------------------*/ static pHdb MakeHMDataNode(pHistMem pHM, char *name){ pHdb node = NULL; pHdbCallback pCall = NULL; pHMAdapter pHMA = NULL; node = MakeHipadabaNode(name,HIPINTVARAR,2); pHMA = malloc(sizeof(HMAdapter)); if(node == NULL || pHMA == NULL){ return NULL; } pHMA->pHM = pHM; pHMA->oldSum = 0; node->value.doNotFree = 1; pCall = MakeHipadabaCallback(HMDataGetCallback,pHMA,free); if(pCall == NULL){ return NULL; } AppendHipadabaCallback(node,pCall); AppendHipadabaCallback(node,MakeReadOnlyCallback()); return node; } /*================ SICS Variable ======================================*/ static hdbCallbackReturn SicsVarSetCallback(pHdb currentNode, void *userData, pHdbMessage message){ pSicsVariable pVar = (pSicsVariable)userData; SConnection *pCon = NULL; int userRights = usMugger; pHdbDataMessage mm = NULL; if((mm = GetHdbSetMessage(message)) == NULL){ return hdbContinue; } pCon = mm->callData; assert(pVar != NULL); if(pCon != NULL){ userRights = SCGetRights(pCon); } switch(currentNode->value.dataType){ case HIPINT: VarSetInt(pVar, mm->v->v.intValue, userRights); break; case HIPFLOAT: VarSetFloat(pVar, (float)mm->v->v.doubleValue, userRights); break; case HIPTEXT: VarSetText(pVar, mm->v->v.text, userRights); break; } return hdbContinue; } /*----------------------------------------------------------------------*/ static int ValueCallback(int iEvent, void *eventData, void *userData, commandContext cc){ pSicsVariable pVar = (pSicsVariable)eventData; pHdb node = (pHdb)userData; hdbValue v; if(iEvent == VALUECHANGE && pVar != NULL && node != NULL){ switch(pVar->eType){ case veInt: v = MakeHdbInt(pVar->iVal); break; case veFloat: v = MakeHdbFloat((double)pVar->fVal); break; case veText: v = MakeHdbText(pVar->text); break; } UpdateHipadabaPar(node,v,NULL); } return 1; } /*----------------------------------------------------------------------*/ static pHdb MakeSicsVarNode(pSicsVariable pVar, char *name){ pHdb node = NULL; pHdbCallback pCall = NULL; commandContext comCom; int type; char command[1024]; switch(pVar->eType){ case veInt: type = HIPINT; break; case veFloat: type = HIPFLOAT; break; case veText: type = HIPTEXT; break; } node = MakeHipadabaNode(name,type,1); if(node == NULL){ return NULL; } if(pVar->iLock == 1) { AddPrivProperty(node,usInternal); } else { AddPrivProperty(node,pVar->iAccessCode); } pCall = MakeHipadabaCallback(SicsVarSetCallback,pVar,NULL); if(pCall == NULL){ return NULL; } strncpy(comCom.deviceID,name,255); comCom.transID = -77; AppendHipadabaCallback(node,pCall); RegisterCallback(pVar->pCall,comCom, VALUECHANGE, ValueCallback, node,NULL); snprintf(command,1023,"%s ", pVar->name); SetHdbProperty(node,"sicscommand",command); node->protected = 1; return node; } /*================ counter =============================================*/ typedef struct { pHdb node; int monitor; /* -1 == time */ pCounter counter; } CountEntry; static int countList = -10; /*---------------------------------------------------------------------*/ static void updateCountList(){ int status; hdbValue v; CountEntry hugo; long monitor; float time; SConnection *pDummy = NULL; if(countList < 0){ return; } pDummy = SCCreateDummyConnection(pServ->pSics); if(pDummy == NULL){ return; } status = LLDnodePtr2First(countList); while(status != 0){ LLDnodeDataTo(countList,&hugo); if(hugo.monitor < 0){ time = GetCountTime(hugo.counter,pDummy); v = MakeHdbFloat((double)time); UpdateHipadabaPar(hugo.node,v, NULL); } else { monitor = GetMonitor(hugo.counter, hugo.monitor, pDummy); v = MakeHdbInt((int)monitor); UpdateHipadabaPar(hugo.node,v, NULL); } status = LLDnodePtr2Next(countList); } SCDeleteConnection(pDummy); } /*---------------------------------------------------------------------------*/ static int CounterCallback(int iEvent, void *eventData, void *userData, commandContext cc){ if(iEvent == MONITOR || iEvent == COUNTEND){ updateCountList(); } return 1; }/*=================== SICSData ========================================*/ static void copyIntSicsData(pHdb node, pSICSData data){ if(node->value.arrayLength != data->dataUsed){ if(node->value.v.intArray != NULL){ free(node->value.v.intArray); node->value.arrayLength = data->dataUsed; node->value.v.intArray = malloc(data->dataUsed*sizeof(int)); if(node->value.v.intArray == NULL){ node->value.arrayLength = 0; return; } memcpy(node->value.v.intArray, data->data, data->dataUsed*sizeof(int)); } } } /*-----------------------------------------------------------------------*/ static void copyFloatSicsData(pHdb node, pSICSData data){ int i; float val; if(node->value.arrayLength != data->dataUsed){ if(node->value.v.floatArray != NULL){ free(node->value.v.floatArray); node->value.arrayLength = data->dataUsed; node->value.v.floatArray = malloc(data->dataUsed*sizeof(double)); if(node->value.v.floatArray == NULL){ node->value.arrayLength = 0; return; } for(i = 0; i < data->dataUsed; i++){ getSICSDataFloat(data,i,&val); node->value.v.floatArray[i] = val; } } } } /*----------------------------------------------------------------------*/ static hdbCallbackReturn SICSDataCallback(pHdb node, void *userData, pHdbMessage message){ pSICSData self = (pSICSData)userData; pHdbDataMessage mm = NULL; int i, status; char script[256], error[1024]; assert(self != NULL); /* * I have to make copies because the floats in SICSData * are floats but doubles in Hipdaba. Siiiigggghhhh! * But it is cleaner in some way anyway. */ if((mm = GetHdbGetMessage(message)) != NULL){ memset(script,0,256); if(GetHdbProperty(node,"readscript", script,256) == 1){ status = Tcl_Eval(InterpGetTcl(pServ->pSics),script); if(status != TCL_OK){ snprintf(error,1023,"ERROR: Tcl returned error: %s", Tcl_GetStringResult(InterpGetTcl(pServ->pSics))); if(mm->callData != NULL){ SCWrite((SConnection *)mm->callData, error, eError); return hdbAbort; } } } if(node->value.dataType == HIPINTVARAR){ copyIntSicsData(node, self); } else if(node->value.dataType == HIPFLOATVARAR){ copyFloatSicsData(node, self); } return hdbContinue; } if((mm = GetHdbSetMessage(message)) != NULL){ if(node->value.dataType == HIPINTVARAR){ for(i = 0; i < mm->v->arrayLength; i++){ setSICSDataInt(self,i,mm->v->v.intArray[i]); } } else if(node->value.dataType == HIPFLOATVARAR){ for(i = 0; i < mm->v->arrayLength; i++){ setSICSDataFloat(self,i,(float)mm->v->v.floatArray[i]); } } memset(script,0,256); if(GetHdbProperty(node,"writescript", script,256) == 1){ status = Tcl_Eval(InterpGetTcl(pServ->pSics),script); if(status != TCL_OK){ snprintf(error,1023,"ERROR: Tcl returned error: %s", Tcl_GetStringResult(InterpGetTcl(pServ->pSics))); if(mm->callData != NULL){ SCWrite((SConnection *)mm->callData, error, eError); return hdbAbort; } } } return hdbContinue; } return hdbContinue; } /*============== interpreter function ==================================*/ int SICSHdbAdapter(SConnection *pCon, SicsInterp *pSics, void *pData, int argc, char *argv[]){ pHdb root = NULL; pHdb path = NULL; pHdb node = NULL; pMotor pMot = NULL; pHistMem pHM = NULL; CommandList *pCom = NULL; pIDrivable pDriv = NULL; pSicsVariable pVar = NULL; char buffer[512]; pCounter pCount = NULL; CountEntry hugo; pSICSData data = NULL; int type; pHdbCallback pCall = NULL; root = GetHipadabaRoot(); assert(root != NULL); if(!SCMatchRights(pCon,usMugger)){ return 0; } if(argc < 4) { SCWrite(pCon,"ERROR: Insufficient number of arguments",eError); return 0; } path = GetHipadabaNode(root,argv[1]); if(path == NULL){ SCWrite(pCon,"ERROR: path to attach object too not found",eError); return 0; } /* * look for motors */ pMot = (pMotor)FindCommandData(pSics,argv[2],"Motor"); if(pMot != NULL){ node = CreateMotorAdapter(argv[3],pMot); if(node == NULL){ SCWrite(pCon,"ERROR: out of memory creating motor node",eError); return 0; } AddHipadabaChild(path,node,pCon); SCSendOK(pCon); return 1; } /* * look for drivables */ pDriv = FindDrivable(pSics,argv[2]); pCom = FindCommand(pSics,argv[2]); if(pDriv != NULL && pCom != NULL && pCom->pData != NULL){ node = MakeSICSHdbDriv(argv[3],usUser,pCom->pData,HIPFLOAT); if(node == NULL){ SCWrite(pCon,"ERROR: out of memory creating drivable node",eError); return 0; } SetHdbProperty(node,PRIVNAM,"user"); SetHdbProperty(node,"type","drivable"); SetHdbProperty(node,"sicsdev",argv[2]); AddHipadabaChild(path,node,pCon); SCSendOK(pCon); return 1; } /** * look for SICS Variables */ pVar = (pSicsVariable)FindCommandData(pSics,argv[2],"SicsVariable"); if(pVar != NULL){ node = MakeSicsVarNode(pVar,argv[3]); if(node == NULL){ SCWrite(pCon,"ERROR: out of memory creating SICS variable node", eError); return 0; } AddHipadabaChild(path,node,pCon); SCSendOK(pCon); return 1; } /* * look for histogram memories */ pHM = (pHistMem)FindCommandData(pSics,argv[2],"HistMem"); if(pHM != NULL){ node = MakeHMDataNode(pHM,argv[3]); if(node == NULL){ SCWrite(pCon,"ERROR: out of memory creating HM node",eError); return 0; } AddHipadabaChild(path,node,pCon); SCSendOK(pCon); return 1; } /** * look for counters */ pCount = (pCounter)FindCommandData(pSics,argv[2],"SingleCounter"); if(pCount != NULL){ hugo.monitor = atoi(argv[3]); hugo.counter = pCount; hugo.node = path; if(countList < 0){ countList = LLDcreate(sizeof(CountEntry)); RegisterCallback(pCount->pCall, SCGetContext(pCon), COUNTSTART, CounterCallback, NULL, NULL); RegisterCallback(pCount->pCall, SCGetContext(pCon), COUNTEND, CounterCallback, NULL, NULL); RegisterCallback(pCount->pCall, SCGetContext(pCon), MONITOR, CounterCallback, NULL, NULL); } LLDnodeAppendFrom(countList,&hugo); SCSendOK(pCon); return 1; } /** * look for SICSData */ data = (pSICSData)FindCommandData(pSics,argv[2],"SICSData"); if(data != NULL){ if(argc < 5){ SCWrite(pCon,"ERROR: need type and name to create SICSData adapter", eError); return 0; } type = convertHdbType(argv[3]); if(type != HIPINTVARAR && type != HIPFLOATVARAR ){ SCWrite(pCon, "ERROR: need intvarar or floatvarar type for SICSData adapter", eError); return 0; } node = MakeHipadabaNode(argv[4],type,0); if(node == NULL){ SCWrite(pCon,"ERROR: out of memory in SICSHdbAdapter", eError); return 0; } pCall = MakeHipadabaCallback(SICSDataCallback,data,NULL); if(pCall == NULL){ SCWrite(pCon,"ERROR: out of memory in SICSHdbAdapter", eError); return 0; } AppendHipadabaCallback(node,pCall); AddHipadabaChild(path,node,pCon); SCSendOK(pCon); return 1; } snprintf(buffer,511, "ERROR: attaching this type of object: %s at %s not implemented", argv[2], argv[1]); SCWrite(pCon,buffer,eError); return 0; }