/** * This is the implementation of a second generation motor. A second * generation motor differs in various aspects from a first generation * motor though it is functionally equivalent and even adheres to the * same interface. The differing aspects are: * - The second generation motor deos not use the ObPar array for parameter storage * but the Hipadaba parameter system rooted in self->pDes->objectNode. * - The second generation motor does not use the driver. The second generation motor * implements some functionality but is largely a shell. Scripts are supposed to add * more parameters and link to a corresponding ScriptContext object which takes care * of actual talking to the hardware. * - The old callback system is also not implemented: third parties which are interested * to be notified of changes to the motor position shall register a callback. * * This also means that many of the fields in the motor data structure are not used in * this module but are present to support first generation motors. This must be so as we cannot * replace all existing driver with new ones quickly. Here a list of fields which have no use: * - pDriver * - ParArray * * We need three different position parameters to keep this under control: * - the physical value which is the main motor node * - the targetposition which is is the hardware target where the motor is supposed * to be * - The hardposition which is the actual hardware position. This is also the * one which is responsible for talking to the motor. * * Then we need a status parameter which is some text which says what the motor * is up to. * * A less obvious use of a callback is the HardUpdateCallback. It automatically * updates the physical motor position, too. * * copyright: see file COPYRIGHT * * Mark Koennecke, December 2008 */ #include #include #include #include #include "sics.h" #include "sicshipadaba.h" #include "sicsobj.h" #include "motor.h" #include "splitter.h" #define ABS(x) (x < 0 ? -(x) : (x)) /*--------------------------------------------------------------------------*/ static int SecMotorGetPar(pMotor self, char *name, float *fVal){ char pBueffel[512]; hdbValue value;; int status; snprintf(pBueffel,511,"%s ", name); status = SICSHdbGetPar(self,NULL,pBueffel, &value); *fVal = (float)value.v.doubleValue; return status; } /*---------------------------------------------------------------------------*/ static int SecMotorSetPar(pMotor self, SConnection *pCon, char *name, float fVal){ int status; hdbValue value; value = MakeHdbFloat(fVal); status = SICSHdbSetPar(self, pCon,name,value); if(status == 1){ SCparChange(pCon); return 1; } else { return 0; } } /*-------------------------------------------------------------------------*/ static void *MotorGetInterfaceSec(void *pData, int iID){ pMotor self = NULL; self = (pMotor)pData; assert(self); if(iID == DRIVEID){ return self->pDrivInt; } else if(iID == CALLBACKINTERFACE) { return self->pCall; } return NULL; } /*---------------------------------------------------------------------------*/ static long SecMotorRun(void *sulf, SConnection *pCon, float fNew){ pMotor self = (pMotor)sulf; hdbValue v; v = MakeHdbFloat(fNew); return SetHipadabaPar(self->pDescriptor->parNode,v,pCon); } /*--------------------------------------------------------------------------*/ static int SecMotorCheckBoundary(pMotor self, float fVal, float *fTarget, char *pError, int iErrLen){ double fZero, fixed, lowerlim, upperlim, sign; float fHard, hupper, hlower; char pBueffel[512]; hdbValue v; assert(self); assert(SICSHdbGetPar(self,NULL, "fixed",&v) == 1); fixed = v.v.doubleValue; assert(SICSHdbGetPar(self,NULL, "softzero",&v) == 1); fZero = v.v.doubleValue; assert(SICSHdbGetPar(self,NULL, "softlowerlim",&v) == 1); lowerlim = v.v.doubleValue; assert(SICSHdbGetPar(self,NULL, "softupperlim",&v) == 1); upperlim = v.v.doubleValue; assert(SICSHdbGetPar(self,NULL, "sign",&v) == 1); sign = v.v.doubleValue; *fTarget = fVal; /* check for fixed */ if(fixed >= 0){ sprintf(pBueffel,"Motor %s is Fixed",self->name); strncpy(pError,pBueffel,iErrLen); return 0; /* is this an error? */ } /* check against software boundaries */ if(fVal > upperlim){ sprintf(pBueffel,"%f violates upper software limit %f on %s", fVal, upperlim,self->name); strncpy(pError,pBueffel,iErrLen); return 0; } if(fVal < lowerlim){ sprintf(pBueffel,"%f violates lower software limit %f on %s", fVal,lowerlim,self->name ); strncpy(pError,pBueffel,iErrLen); return 0; } /* correct for zero point */ fZero = -fZero; fHard = fVal - fZero; /* apply sign */ fHard = fHard*sign; /* check for hardware limits */ SecMotorGetPar(self,"hardlowerlim",&hlower); SecMotorGetPar(self,"hardupperlim",&hupper); if(fHard > hupper){ sprintf(pBueffel,"%f violates upper hardware limit %f on %s", fVal,hupper,self->name); strncpy(pError,pBueffel,iErrLen); return 0; } if(fHard < hlower){ sprintf(pBueffel,"%f violates lower hardware limit %f on %s", fVal,hlower,self->name); strncpy(pError,pBueffel,iErrLen); return 0; } *fTarget = fHard; return 1; } /*--------------------------------------------------------------------------*/ static int SecMotorLimits(void *sulf, float fVal, char *error, int iErrLen){ float fHard; pMotor self; assert(sulf); self = (pMotor)sulf; return SecMotorCheckBoundary(self,fVal,&fHard,error,iErrLen); } /*-----------------------------------------------------------------------*/ static int checkPosition(pMotor self, SConnection *pCon){ float precision, hard, target, maxretry; pHdb node = NULL; if(SCGetInterrupt(pCon) != eContinue){ return HWFault; } if(self->stopped){ SCPrintf(pCon,eWarning,"WARNING: %s stopped", self->name); return HWFault; } SecMotorGetPar(self,"hardposition",&hard); SecMotorGetPar(self,"targetposition",&target); SecMotorGetPar(self,"precision",&precision); SecMotorGetPar(self,"maxretry",&maxretry); if(ABS(target-hard) > precision){ if(self->retryCount >= (int)maxretry){ SCPrintf(pCon,eError, "ERROR: Aborting %s after %d retries, off position by %f", self->name, (int)maxretry, target - hard); return HWFault; } self->retryCount++; SCPrintf(pCon,eWarning,"WARNING: %s off position by %f, restarting", self->name, target-hard); node = GetHipadabaNode(self->pDescriptor->parNode,"status"); assert(node != NULL); UpdateHipadabaPar(node,MakeHdbText("run"), pCon); node = GetHipadabaNode(self->pDescriptor->parNode,"hardposition"); assert(node != NULL); SetHipadabaPar(node,MakeHdbFloat(target), pCon); return HWBusy; } return HWIdle; } /*-----------------------------------------------------------------------*/ static void handleMoveCallback(pMotor self, SConnection *pCon){ float movecount; pHdb node = NULL; hdbValue v; SecMotorGetPar(self,"movecount",&movecount); self->posCount++; if(self->posCount > (int)movecount){ node = GetHipadabaNode(self->pDescriptor->parNode,"hardposition"); GetHipadabaPar(node,&v,pCon); UpdateHipadabaPar(node,v,pCon); self->posCount = 0; } } /*-----------------------------------------------------------------------*/ static int SecMotorStatus(void *sulf, SConnection *pCon){ pMotor self = NULL; int status; pHdb node = NULL; hdbValue v; float interrupt; assert(sulf); self = (pMotor)sulf; node = GetHipadabaNode(self->pDescriptor->parNode,"status"); assert(node != NULL); status = GetHipadabaPar(node,&v,pCon); if(status != 1){ return HWFault; } if(v.v.text == NULL){ return HWBusy; } if(strstr(v.v.text,"idle") != NULL){ status = checkPosition(self,pCon); } else if(strstr(v.v.text,"run") != NULL){ handleMoveCallback(self,pCon); status = HWBusy; } else if(strstr(v.v.text,"poserror") != NULL){ status = checkPosition(self,pCon); } else if(strstr(v.v.text,"error") != NULL){ status = HWFault; } else { SCPrintf(pCon,eError,"ERROR: unknown motor status %s found", v.v.text); status = HWFault; } /* * when terminating: force an update of the position. */ switch(status){ case HWFault: self->posCount = 10000; handleMoveCallback(self,pCon); SecMotorGetPar(self,"interrupt",&interrupt); if(SCGetInterrupt(pCon) < (int)interrupt){ SCSetInterrupt(pCon,(int)interrupt); } self->errorCount++; break; case HWIdle: self->posCount = 10000; handleMoveCallback(self,pCon); self->errorCount = 0; break; } return status; } /*---------------------------------------------------------------------------*/ static float SecMotorGetValue(void *pData, SConnection *pCon){ int status; pMotor self = (pMotor)pData; hdbValue v; assert(pData); status = GetHipadabaPar(self->pDescriptor->parNode, &v,pCon); if(status != 1){ return -9999999.99; } else { return (float)v.v.doubleValue; } } /*------------------------------------------------------------------------*/ static int SecMotorHalt(void *sulf){ pMotor self; pHdb node = NULL, par[0]; SICSOBJFunc haltFunc = NULL; int status; assert(sulf); self = (pMotor)sulf; node = GetHipadabaNode(self->pDescriptor->parNode,"halt"); assert(node != NULL); haltFunc = (SICSOBJFunc)node->value.v.func; assert(haltFunc != NULL); self->stopped = 1; return haltFunc((pSICSOBJ)self,pServ->dummyCon,node,par,0); } /*--------------------------------------------------------------------------*/ static int SecMotorGetHardPosition(struct __Motor *self, SConnection *pCon, float *fVal){ hdbValue v; int status; pHdb node = NULL; node = GetHipadabaNode(self->pDescriptor->parNode,"hardposition"); assert(node != NULL); status = GetHipadabaPar(node,&v,pCon); *fVal = (float)v.v.doubleValue; return status; } /*---------------------------------------------------------------------------*/ static void AddMotorPar(pHdb node, int priv, char *name){ pHdb child = NULL; child = MakeSICSHdbPar(name,priv,MakeHdbFloat(.0)); if(child != NULL){ SetHdbProperty(child,"__save", "true"); AddHipadabaChild(node,child,NULL); } } /*---------------------------------------------------------------------------*/ static float hardToSoftPosition(pMotor self, float hard){ float sign, zero, fVal; SecMotorGetPar(self,"sign", &sign); SecMotorGetPar(self,"softzero", &zero); fVal = hard; if(sign < 0){ fVal += zero; } else { fVal -= zero; } fVal *= sign; return fVal; } /*---------------------------------------------------------------------------*/ static hdbCallbackReturn SecMotorCallback(pHdb node, void *userData, pHdbMessage message){ SConnection *pCon = NULL; pHdbDataMessage mm = NULL; hdbValue v; pHdb child = NULL; pMotor self = NULL; float fHard, fVal, sign, zero; char pBueffel[512], pError[132]; int status; self = (pMotor)userData; assert(self != NULL); mm = GetHdbSetMessage(message); if(mm != NULL){ pCon = (SConnection *)mm->callData; v = *(mm->v); /* * check permission */ SecMotorGetPar(self,"accesscode",&fVal); if(!SCMatchRights(pCon,(int)fVal)){ sprintf(pBueffel,"ERROR: You are not authorised to move motor %s", self->name); SCWrite(pCon,pBueffel,eError); SCSetInterrupt(pCon,eAbortBatch); return hdbAbort; } /* * check limits */ status = SecMotorCheckBoundary(self,(float)v.v.doubleValue, &fHard,pError,131); if(status != 1){ snprintf(pBueffel,511,"ERROR: %s",pError); SCWrite(pCon,pBueffel,eWarning); SCSetInterrupt(pCon,eAbortOperation); return hdbAbort; } /* * check the motor bad flag */ SecMotorGetPar(self,"ignorefault",&fVal); if((int)fVal > 0){ snprintf(pBueffel,511,"WARNING: motor %s is unreliable", self->name); SCWrite(pCon,pBueffel,eWarning); self->errorCount = 0; } /* * check for alarm condition */ SecMotorGetPar(self,"failafter",&fVal); if(self->errorCount > (int)fVal){ /* big alarm */ ServerWriteGlobal("ERROR: !!! MOTOR ALARM !!! MOTOR ALARM !!!",eError); sprintf(pBueffel, "ERROR: too many position errors counted at motor %s", self->name); ServerWriteGlobal(pBueffel,eError); SCSetInterrupt(pCon,eAbortBatch); self->errorCount = 0; return hdbAbort; } self->posFaultCount = 0; self->retryCount = 0; self->stopped = 0; self->posCount = 0; child = GetHipadabaNode(self->pDescriptor->parNode,"targetposition"); UpdateHipadabaPar(child,MakeHdbFloat(fHard),pCon); child = GetHipadabaNode(self->pDescriptor->parNode,"status"); UpdateHipadabaPar(child,MakeHdbText("run"), pCon); child = GetHipadabaNode(self->pDescriptor->parNode,"hardposition"); SetHipadabaPar(child,MakeHdbFloat(fHard), pCon); return hdbContinue; } mm = GetHdbGetMessage(message); if(mm != NULL){ pCon = (SConnection *)mm->callData; SecMotorGetPar(self,"hardposition", &fVal); fVal = hardToSoftPosition(self,fVal); node->value.v.doubleValue = fVal; mm->v->v.doubleValue = fVal; return hdbContinue; } return hdbContinue; } /*--------------------------------------------------------------------------*/ static hdbCallbackReturn HardUpdateCallback(pHdb node, void *userData, pHdbMessage message){ pHdbDataMessage mm = NULL; pMotor self = (pMotor)userData; float fVal; hdbValue v; assert(self != NULL); mm = GetHdbUpdateMessage(message); if(mm != NULL){ v = *mm->v; fVal = hardToSoftPosition(self,(float)v.v.doubleValue); v.v.doubleValue = fVal; UpdateHipadabaPar(self->pDescriptor->parNode, v, mm->callData); return hdbContinue; } return hdbContinue; } /*--------------------------------------------------------------------------*/ static hdbCallbackReturn SecMotorSignCallback(pHdb node, void *userData, pHdbMessage message){ pMotor self = NULL; SConnection *pCon = NULL; pHdb zero = NULL; double value; pHdbDataMessage mm = NULL; float limit; hdbValue v; self = (pMotor)userData; mm = GetHdbSetMessage(message); if(mm != NULL){ pCon = (SConnection *)mm->callData; if(!SCMatchRights(pCon,usMugger)){ return hdbAbort; } v = *mm->v; if(ABS(v.v.doubleValue) - 1. > .01){ if(pCon!= NULL){ SCWrite(pCon,"ERROR: invalid sign value",eError); return hdbAbort; } } SecMotorGetPar(self,"softlowerlim",&limit); limit *= v.v.doubleValue; SecMotorSetPar(self,pCon,"softlowerlim",limit); SecMotorGetPar(self,"softupperlim",&limit); limit *= v.v.doubleValue; SecMotorSetPar(self,pCon,"softupperlim",limit); SecMotorSetPar(self,pCon,"softzero",.0); UpdateHipadabaPar(node,v,pCon); return hdbContinue; } return hdbContinue; } /*---------------------------------------------------------------------------*/ static hdbCallbackReturn SecMotorZeroCallback(pHdb node, void *userData, pHdbMessage message){ pMotor self = NULL; float limit, oldZero, diff; SConnection *pCon = NULL; pHdbDataMessage mm = NULL; hdbValue v; self = (pMotor)userData; assert(self != NULL); mm = GetHdbSetMessage(message); if(mm != NULL){ pCon = (SConnection *)mm->callData; if(!SCMatchRights(pCon,usUser)){ return hdbAbort; } v = *mm->v; SecMotorGetPar(self,"softzero", &oldZero); diff = v.v.doubleValue - oldZero; SecMotorGetPar(self,"softupperlim",&limit); limit -= diff; SecMotorSetPar(self,pCon,"softupperlim",limit); SecMotorGetPar(self,"softlowerlim",&limit); limit -= diff; SecMotorSetPar(self,pCon,"softlowerlim",limit); UpdateHipadabaPar(node,v,pCon); return hdbContinue; } return hdbContinue; } /*---------------------------------------------------------------------------*/ pMotor SecMotorInit(char *name){ pMotor pM = NULL; pHdb node = NULL, child = NULL; hdbValue v; assert(name); /* get memory */ pM = (pMotor)malloc(sizeof(Motor)); if(!pM){ return NULL; } memset(pM,0,sizeof(Motor)); /* initialise object descriptor */ pM->pDescriptor = CreateDescriptor("Motor"); if(!pM->pDescriptor){ free(pM); return NULL; } pM->pDescriptor->GetInterface = MotorGetInterfaceSec; pM->pDescriptor->SaveStatus = SaveSICSOBJ; pM->pDescriptor->parNode = MakeSICSHdbPar(name,usSpy,MakeHdbFloat(.0)); if(pM->pDescriptor->parNode == NULL){ free(pM); return NULL; } node = pM->pDescriptor->parNode; pM->objectNode = node; AppendHipadabaCallback(pM->pDescriptor->parNode, MakeHipadabaCallback(SecMotorCallback,pM,NULL)); /* copy arguments */ pM->name = strdup(name); /* * install parameters */ child = MakeSICSHdbPar("targetposition",usInternal,MakeHdbFloat(.0)); if(child == NULL){ return(NULL); } SetHdbProperty(child,"__save", "true"); AddHipadabaChild(node,child,NULL); child = MakeHipadabaNode("hardposition",HIPFLOAT,1); if(child == NULL){ return(NULL); } AddHipadabaChild(node,child,NULL); SetHdbProperty(child,"motname", name); AppendHipadabaCallback(child, MakeHipadabaCallback(HardUpdateCallback,pM,NULL)); child = MakeHipadabaNode("sign",HIPFLOAT, 1); SetHdbProperty(child,"__save", "true"); AddHipadabaChild(node,child,NULL); AppendHipadabaCallback(child, MakeHipadabaCallback(SecMotorSignCallback,pM,NULL)); UpdateHipadabaPar(child,MakeHdbFloat(1.),NULL); child = MakeHipadabaNode("softzero",HIPFLOAT, 1); SetHdbProperty(child,"__save", "true"); AddHipadabaChild(node,child,NULL); AppendHipadabaCallback(child, MakeHipadabaCallback(SecMotorZeroCallback,pM,NULL)); child = MakeHipadabaNode("hardlowerlim",HIPFLOAT, 1); AddHipadabaChild(node,child,NULL); child = MakeHipadabaNode("hardupperlim",HIPFLOAT, 1); AddHipadabaChild(node,child,NULL); child = MakeSICSHdbPar("softlowerlim",usUser, MakeHdbFloat(.0)); SetHdbProperty(child,"__save", "true"); AddHipadabaChild(node,child,NULL); child = MakeSICSHdbPar("softupperlim",usUser, MakeHdbFloat(.0)); SetHdbProperty(child,"__save", "true"); AddHipadabaChild(node,child,NULL); child = MakeSICSHdbPar("fixed",usUser, MakeHdbFloat(-1.)); SetHdbProperty(child,"__save", "true"); AddHipadabaChild(node,child,NULL); child = MakeSICSHdbPar("interruptmode",usMugger, MakeHdbFloat(.0)); SetHdbProperty(child,"__save", "true"); AddHipadabaChild(node,child,NULL); child = MakeSICSHdbPar("precision",usMugger, MakeHdbFloat(.1)); SetHdbProperty(child,"__save", "true"); AddHipadabaChild(node,child,NULL); child = MakeSICSHdbPar("accesscode",usMugger, MakeHdbFloat((double)usUser)); SetHdbProperty(child,"__save", "true"); AddHipadabaChild(node,child,NULL); child = MakeSICSHdbPar("failafter",usMugger, MakeHdbFloat((double)3.0)); SetHdbProperty(child,"__save", "true"); AddHipadabaChild(node,child,NULL); child = MakeSICSHdbPar("maxretry",usMugger, MakeHdbFloat((double)3.0)); SetHdbProperty(child,"__save", "true"); AddHipadabaChild(node,child,NULL); child = MakeSICSHdbPar("ignorefault",usMugger, MakeHdbFloat((double).0)); SetHdbProperty(child,"__save", "true"); AddHipadabaChild(node,child,NULL); child = MakeSICSHdbPar("movecount",usMugger, MakeHdbFloat((double)10.0)); SetHdbProperty(child,"__save", "true"); AddHipadabaChild(node,child,NULL); child = MakeHipadabaNode("status",HIPTEXT,1); SetHdbProperty(child,"motname", name); AddHipadabaChild(node,child,NULL); pM->endScriptID = 0; /* initialise Drivable interface */ pM->pDrivInt = CreateDrivableInterface(); if(!pM->pDrivInt){ DeleteDescriptor(pM->pDescriptor); free(pM); return NULL; } pM->pDrivInt->SetValue = SecMotorRun; pM->pDrivInt->CheckLimits = SecMotorLimits; pM->pDrivInt->CheckStatus = SecMotorStatus; pM->pDrivInt->GetValue = SecMotorGetValue; pM->pDrivInt->Halt = SecMotorHalt; /* * initialize motor function pointers */ pM->MotorGetPar = SecMotorGetPar; pM->MotorSetPar = SecMotorSetPar; pM->MotorGetHardPosition = SecMotorGetHardPosition; /* initialise callback interface */ pM->pCall = CreateCallBackInterface(); if(!pM->pCall){ MotorKill(pM); return NULL; } /* done */ return pM; } /*--------------------------------------------------------------------------*/ static void SecMotorKill(void *data){ pMotor self = (pMotor)data; if(self == NULL){ return; } if(self->name) free(self->name); if(self->pDrivInt){ free(self->pDrivInt); } if(self->pCall){ DeleteCallBackInterface(self->pCall); } /* kill Descriptor */ DeleteDescriptor(self->pDescriptor); free(self); } /*--------------------------------------------------------------------------*/ int SecMotorFactory(SConnection *pCon, SicsInterp *pSics, void *pData, int argc, char *argv[]){ pMotor pNew = NULL; if(argc < 2){ SCWrite(pCon,"ERROR: need name for new motor", eError); return 0; } pNew = SecMotorInit(argv[1]); if(pNew == NULL){ SCWrite(pCon,"ERROR: out of memory creating motor", eError); return 0; } return AddCommand(pSics,argv[1],InterInvokeSICSOBJ, SecMotorKill,pNew); }