/** * This is a base class for any drivable object which uses the new * scriptcontext to control the actual driving operation. This can also * serve as a drivable adapter to nodes in other objects. * * copyright: see file COPYRIGHT * * Mark Koennecke, June-July 2008 */ #include #include #include #include #include typedef struct { pIDrivable pDriv; SctController *c; int doNotKillNode; } DrivObjPriv, *pDrivObjPriv; /*---------------------------------------------------------------*/ static void *SCTDRIVGetInterface(void *data, int iD){ pSICSOBJ self = NULL; pDrivObjPriv pPriv; self = (pSICSOBJ)data; pPriv = (pDrivObjPriv)self->pPrivate; if(self != NULL && pPriv != NULL && iD == DRIVEID){ if (self->objectNode == NULL) return NULL; return pPriv->pDriv; } else { return NULL; } return NULL; } /*---------------------------------------------------------------- This routine can return either OKOK or HWFault when thing go wrong. However, the return value of Halt is usually ignored! ------------------------------------------------------------------*/ static int SCTDRIVHalt(void *data) { pSICSOBJ self = NULL; pDrivObjPriv pPriv; char dummy[16]; self = (pSICSOBJ)data; pPriv = (pDrivObjPriv)self->pPrivate; if (GetHdbProperty(self->objectNode,"halt", dummy, sizeof dummy)) { SctQueueNode(pPriv->c, self->objectNode, HaltPRIO, "halt", NULL); } else if (GetHdbProperty(self->objectNode, "status", dummy, sizeof dummy)) { SetHdbProperty(self->objectNode, "status", "idle"); } return OKOK; } /*---------------------------------------------------------------- This routine can return either 1 or 0. 1 means the position can be reached, 0 NOT If 0, error shall contain up to errlen characters of information about which limit was violated ------------------------------------------------------------------*/ static int SCTDRIVCheckLimits(void *data, float val, char *error, int errlen){ pSICSOBJ self = NULL; pDrivObjPriv pPriv; char script[1024]; int status; Tcl_Interp *pTcl = NULL; char *result; self = (pSICSOBJ)data; pPriv = (pDrivObjPriv)self->pPrivate; snprintf(script,1024,"%f", val); SetHdbProperty(self->objectNode,"target", script); if(GetHdbProperty(self->objectNode,"checklimits",script,1024)){ status = SctCallInContext(pServ->dummyCon, script, self->objectNode, pPriv->c, &result); if(SctVerbose(pPriv->c)){ SCPrintf(pServ->dummyCon, eWarning, "script %s called with result %s\n ", script, result); } if(status == 0){ strncpy(error,result,errlen); return 0; } } return 1; } /*---------------------------------------------------------------- This routine can return 0 when a limit problem occurred OKOK when the motor was successfully started HWFault when a problem occured starting the device Possible errors shall be printed to pCon For real motors, this is supposed to try at least three times to start the motor in question val is the value to drive the motor too ------------------------------------------------------------------*/ static long SCTDRIVSetValue(void *data, SConnection *pCon, float val){ pSICSOBJ self = NULL; pDrivObjPriv pPriv; int status; hdbValue v; self = (pSICSOBJ)data; pPriv = (pDrivObjPriv)self->pPrivate; v.dataType = HIPFLOAT; v.v.doubleValue = (double)val; SetHdbProperty(self->objectNode,"writestatus", "start"); status = SetHipadabaPar(self->objectNode, v, pCon); if(status == 1){ return OKOK; } else { return HWFault; } } /*---------------------------------------------------------------- Checks the status of a running motor. Possible return values HWBusy The motor is still running OKOK or HWIdle when the motor finished driving HWFault when a hardware problem ocurred HWPosFault when the hardware cannot reach a position Errors are duly to be printed to pCon For real motors CheckStatus again shall try hard to fix any issues with the motor ------------------------------------------------------------------*/ static int SCTDRIVCheckStatus(void *data, SConnection *pCon){ pSICSOBJ self = NULL; pDrivObjPriv pPriv; char script[1024]; int status; Tcl_Interp *pTcl = NULL; char *result; SConnection *con; self = (pSICSOBJ)data; pPriv = (pDrivObjPriv)self->pPrivate; /* * check if the write command has gone through */ if(GetHdbProperty(self->objectNode,"writestatus", script,1024)){ if(strcmp(script,"start") == 0){ return HWBusy; } } /* * run the checkstatus script */ if(!GetHdbProperty(self->objectNode,"checkstatus",script,1024)){ if (!GetHdbProperty(self->objectNode,"status",script,1024)){ SCWrite(pCon, "ERROR: configuration problem: no checkstatus script!", eError); return HWFault; } result = script; } else { status = SctCallInContext(pCon,script, self->objectNode, pPriv->c, &result); if (status == 0) { SCPrintf(pCon,eError," script %s returned %s", script, result); return HWFault; } if(SctVerbose(pPriv->c)){ SCPrintf(pCon,eError," script %s returned %s", script, result); } } if(strstr(result,"busy") != NULL){ return HWBusy; } else if(strstr(result,"posfault") != NULL){ return HWPosFault; } else if(strstr(result,"fault") != NULL){ return HWFault; } else if(strstr(result,"idle") != NULL){ return HWIdle; } else { SCPrintf(pCon,eError, "ERROR: invalid status code %s returned from checkstatus script", result); return HWFault; } return HWFault; } /*---------------------------------------------------------------- GetValue is supposed to read a motor position On errors, -99999999.99 is returned and messages printed to pCon ------------------------------------------------------------------*/ static float SCTDRIVGetValue(void *data, SConnection *pCon){ pSICSOBJ self = NULL; pDrivObjPriv pPriv; float val = -99999999.99; int status; char error[256]; hdbValue v; self = (pSICSOBJ)data; pPriv = (pDrivObjPriv)self->pPrivate; if(GetHdbProperty(self->objectNode,"geterror", error, 256)){ SCWrite(pCon,error, eError); return val; } return (float)self->objectNode->value.v.doubleValue; } /*-----------------------------------------------------------------*/ void AssignSctDrive(pIDrivable pDriv){ pDriv->Halt = SCTDRIVHalt; pDriv->CheckLimits = SCTDRIVCheckLimits; pDriv->SetValue = SCTDRIVSetValue; pDriv->CheckStatus = SCTDRIVCheckStatus; pDriv->GetValue = SCTDRIVGetValue; } /*---------------------------------------------------------------------------*/ static void KillDriveOBJ(void *data){ pSICSOBJ self = (pSICSOBJ)data; pDrivObjPriv pPriv; if(self == NULL){ return; } pPriv = (pDrivObjPriv)self->pPrivate; if(pPriv->doNotKillNode && self->pDes != NULL){ if(self->pDes->name) free(self->pDes->name); if(self->pDes->pKeys) IFDeleteOptions(self->pDes->pKeys); free(self->pDes); } else { DeleteDescriptor(self->pDes); /* kill descriptor including node */ } if(self->KillPrivate != NULL && self->pPrivate != NULL){ self->KillPrivate(self->pPrivate); } RemoveHdbNodeFromParent(self->objectNode, pServ->dummyCon); free(self); } /*-----------------------------------------------------------------------------*/ pSICSOBJ MakeSctDriveObj(pHdb node, char *class, SctController *c){ pSICSOBJ pNew = NULL; pDrivObjPriv pPriv = NULL; hdbValue val; pNew = (pSICSOBJ)malloc(sizeof(SICSOBJ)); pPriv = (pDrivObjPriv)malloc(sizeof(DrivObjPriv)); if(pNew == NULL || pPriv == NULL){ return NULL; } memset(pNew,0,sizeof(SICSOBJ)); memset(pPriv,0,sizeof(DrivObjPriv)); pNew->pDes = CreateDescriptor(class); pPriv->pDriv = CreateDrivableInterface(); if(pNew->pDes == NULL || pPriv->pDriv == NULL){ free(pNew); free(pPriv); return(NULL); } pNew->objectNode = node; AssignSctDrive(pPriv->pDriv); pPriv->c = c; pNew->pDes->parNode = pNew->objectNode; pNew->pDes->GetInterface = SCTDRIVGetInterface; pNew->pPrivate = pPriv; pNew->KillPrivate = DefaultKill; return pNew; } /*-------------------------------------------------------------------------- * This actually has two syntaxes: * makesctdrive name path-to-existing-node class SctController * makesctdrive name type priv class SctController *--------------------------------------------------------------------------*/ int SctMakeDriveObject(SConnection *pCon, SicsInterp *pSics, void *object, int argc, char *argv[]) { pHdb node = NULL; pDrivObjPriv pPriv = NULL; pSICSOBJ pNew = NULL; pSICSOBJ pSct = NULL; SctController *c = NULL; int priv, type, status; hdbValue val; if(argc < 5){ SCWrite(pCon,"ERROR: not enough arguments to SctMakeDriveObject", eError); return 0; } node = FindHdbNode(NULL,argv[2], pCon); if(node != NULL){ pSct = FindCommandData(pSics,argv[4], "SctController"); if(pSct == NULL){ SCPrintf(pCon,eError, "ERROR: SctController %s not found", argv[4]); return 0; } c = (SctController *)pSct->pPrivate; pNew = MakeSctDriveObj(node,argv[3],c); pPriv = (pDrivObjPriv)pNew->pPrivate; pPriv->doNotKillNode = 1; /* the node is owned by someone else*/ } else { /* convert privilege */ priv = decodeSICSPriv(argv[3]); /* convert datatype */ strtolower(argv[4]); type = convertHdbType(argv[2]); if (type == HIPNONE) { node = MakeHipadabaNode(argv[1], HIPNONE, 1); } else { val = makeHdbValue(type,0); node = MakeSICSHdbPar(argv[1], priv, val); ReleaseHdbValue(&val); } if(node == NULL){ SCWrite(pCon,"ERROR: node creation failed", eError); return 0; } c = FindCommandData(pSics,argv[5], "SctController"); if(c == NULL){ SCPrintf(pCon,eError, "ERROR: SctController %s not found", argv[4]); return 0; } pNew = MakeSctDriveObj(node,argv[3],c); } if(pNew == NULL){ SCWrite(pCon,"ERROR: failed to create drive object", eError); return 0; } status = AddCommand(pSics, argv[1], InterInvokeSICSOBJ, KillDriveOBJ, pNew); if(status != 1){ KillSICSOBJ(pNew); SCPrintf(pCon,eError,"ERROR: failed create duplicate command %s", argv[1]); return 0; } return 1; }