/** * 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; int accesscode, status; pHdb node = NULL; assert(SICSHdbGetPar(self, NULL, "accesscode", &v) == 1); accesscode = (int)v.v.doubleValue; if(!SCMatchRights(pCon, accesscode)){ return 0; } self->stopped = 0; self->stopReported = 0; v = MakeHdbFloat(fNew); status = SetHipadabaPar(self->pDescriptor->parNode, v, pCon); node = GetHipadabaNode(self->pDescriptor->parNode, "status"); if(node != NULL){ v = MakeHdbText(strdup("run")); UpdateHipadabaPar(node,v,pCon); ReleaseHdbValue(&v); } return status; } /*--------------------------------------------------------------------------*/ static int SecMotorCheckBoundary(pMotor self, float fVal, float *fTarget, char *pError, int iErrLen) { double fZero, fixed, lowerlim, upperlim, sign, offset; 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, "staticoffset", &v) == 1); offset = 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) { snprintf(pBueffel,sizeof(pBueffel)-1, "Motor %s is Fixed", self->name); strlcpy(pError, pBueffel, iErrLen); return 0; /* is this an error? */ } /* check against software boundaries */ if (fVal > upperlim) { snprintf(pBueffel,sizeof(pBueffel)-1, "%f violates upper software limit %f on %s", fVal, upperlim, self->name); strlcpy(pError, pBueffel, iErrLen); return 0; } if (fVal < lowerlim) { snprintf(pBueffel,sizeof(pBueffel)-1, "%f violates lower software limit %f on %s", fVal, lowerlim, self->name); strlcpy(pError, pBueffel, iErrLen); return 0; } /* correct for zero point */ fZero += offset; 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) { snprintf(pBueffel,sizeof(pBueffel)-1, "%f violates upper hardware limit %f on %s", fVal, hupper, self->name); strlcpy(pError, pBueffel, iErrLen); return 0; } if (fHard < hlower) { snprintf(pBueffel,sizeof(pBueffel)-1, "%f violates lower hardware limit %f on %s", fVal, hlower, self->name); strlcpy(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 (self->stopped) { if(self->stopReported == 0){ SCPrintf(pCon, eLog, "WARNING: %s stopped", self->name); self->stopReported = 1; } return HWFault; } if (SCGetInterrupt(pCon) != eContinue) { 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, eLogError, "ERROR: Aborting %s after %d retries, off position by %f", self->name, (int) maxretry, target - hard); node = GetHipadabaNode(self->pDescriptor->parNode, "status"); assert(node != NULL); UpdateHipadabaPar(node, MakeHdbText("error"), pCon); return HWFault; } self->retryCount++; SCPrintf(pCon, eLog, "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; } ReleaseHdbValue(&v); /* * 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); } break; case HWIdle: self->posCount = 10000; handleMoveCallback(self, pCon); 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; self->pDrivInt->iErrorCount--; if(self->pDrivInt->iErrorCount < 0){ self->pDrivInt->iErrorCount = 0; } 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, offset; SecMotorGetPar(self, "sign", &sign); SecMotorGetPar(self, "softzero", &zero); SecMotorGetPar(self, "staticoffset", &offset); fVal = hard; if (sign < 0) { fVal += zero + offset; } else { fVal -= zero + offset; } 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)) { snprintf(pBueffel,sizeof(pBueffel)-1, "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->pDrivInt->iErrorCount > (int) fVal) { /* big alarm */ ServerWriteGlobal("ERROR: !!! MOTOR ALARM !!! MOTOR ALARM !!!", eError); snprintf(pBueffel,sizeof(pBueffel)-1, "ERROR: too many position errors counted at motor %s", self->name); ServerWriteGlobal(pBueffel, eError); SCSetInterrupt(pCon, eAbortBatch); self->pDrivInt->iErrorCount = 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, "softupperlim", limit); SecMotorGetPar(self, "softupperlim", &limit); limit *= v.v.doubleValue; SecMotorSetPar(self, pCon, "softlowerlim", 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; SetHdbProperty(node,"type","drivable"); SetHdbProperty(node,"sicsdev", name); 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(.01)); 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 = MakeSICSHdbPar("staticoffset", usMugger, MakeHdbFloat((double) 0.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); }