1040 lines
30 KiB
C
1040 lines
30 KiB
C
/**
|
|
* 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 <stdlib.h>
|
|
#include <assert.h>
|
|
#include <string.h>
|
|
#include <math.h>
|
|
#include "sics.h"
|
|
#include "sicshipadaba.h"
|
|
#include "sicsobj.h"
|
|
#include "motor.h"
|
|
#include "splitter.h"
|
|
|
|
#define ABS(x) (x < 0 ? -(x) : (x))
|
|
|
|
|
|
/*-------------------------------------------------------------------------*/
|
|
static void SecMotorSetError(pMotor self, char *text)
|
|
{
|
|
hdbValue v;
|
|
pHdb node;
|
|
|
|
node = GetHipadabaNode(self->pDescriptor->parNode, "error");
|
|
if(node != NULL){
|
|
v = MakeHdbText(strdup(text));
|
|
UpdateHipadabaPar(node,v,NULL);
|
|
Log(VERBOSE,"par","%s:error:%s", self->name,text);
|
|
ReleaseHdbValue(&v);
|
|
}
|
|
}
|
|
/*--------------------------------------------------------------------------*/
|
|
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);
|
|
Log(VERBOSE,"par","%s:%s:%f", self->name,name,fVal);
|
|
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;
|
|
self->fTarget = fNew;
|
|
|
|
SecMotorSetError(sulf,"None");
|
|
node = GetHipadabaNode(self->pDescriptor->parNode, "status");
|
|
if(node != NULL){
|
|
v = MakeHdbText(strdup("run"));
|
|
UpdateHipadabaPar(node,v,pCon);
|
|
ReleaseHdbValue(&v);
|
|
}
|
|
v = MakeHdbFloat(fNew);
|
|
status = SetHipadabaPar(self->pDescriptor->parNode, v, pCon);
|
|
|
|
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;
|
|
int status;
|
|
|
|
assert(sulf);
|
|
|
|
self = (pMotor) sulf;
|
|
|
|
status = SecMotorCheckBoundary(self, fVal, &fHard, error, iErrLen);
|
|
if(status != 1){
|
|
SecMotorSetError(sulf,error);
|
|
}
|
|
return status;
|
|
}
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
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);
|
|
SecMotorSetError(self,"Aborted positioning after many retries");
|
|
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);
|
|
Log(VERBOSE,"par","%s:hardposition:%f",self->name,v.v.doubleValue);
|
|
self->posCount = 0;
|
|
}
|
|
}
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
static int SecMotorStatus(void *sulf, SConnection * pCon)
|
|
{
|
|
pMotor self = NULL;
|
|
int status;
|
|
pHdb node = NULL;
|
|
hdbValue v;
|
|
float interrupt = 0.;
|
|
char error[132];
|
|
|
|
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) {
|
|
SCPrintf(pCon,eWarning,"WARNING: %s repositioned", self->name);
|
|
status = checkPosition(self, pCon);
|
|
} else if (strstr(v.v.text, "restart") != NULL) {
|
|
SCPrintf(pCon,eLog,"WARNING: restarting motor %s", self->name);
|
|
SecMotorRun(self,pCon,self->fTarget);
|
|
return HWBusy;
|
|
} else if (strstr(v.v.text, "error") != NULL) {
|
|
if(GetHdbProperty(node,"geterror",error,sizeof(error)) == 1){
|
|
SecMotorSetError(sulf,error);
|
|
}
|
|
node = GetHipadabaNode(self->pDescriptor->parNode, "targetposition");
|
|
if(GetHdbProperty(node,"geterror",error,sizeof(error)) == 1){
|
|
SecMotorSetError(sulf,error);
|
|
}
|
|
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, "interruptmode", &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;
|
|
char error[132];
|
|
|
|
|
|
assert(pData);
|
|
status = GetHdbProperty(self->pDescriptor->parNode,"geterror", error,sizeof(error));
|
|
if(status == 1 && strcmp(error,"none") != 0) {
|
|
SCPrintf(pCon,eError,"ERROR: Failed to read %s with %s", self->name, error);
|
|
return -9999999.99;
|
|
}
|
|
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], *pPtr = NULL;
|
|
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;
|
|
self->pDrivInt->iErrorCount = 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;
|
|
child = GetHipadabaNode(self->pDescriptor->parNode, "status");
|
|
UpdateHipadabaPar(child, MakeHdbText("error"), pCon);
|
|
return hdbAbort;
|
|
}
|
|
|
|
self->posFaultCount = 0;
|
|
self->retryCount = 0;
|
|
self->stopped = 0;
|
|
self->posCount = 0;
|
|
child = GetHipadabaNode(self->pDescriptor->parNode, "status");
|
|
UpdateHipadabaPar(child, MakeHdbText("run"), pCon);
|
|
child = GetHipadabaNode(self->pDescriptor->parNode, "targetposition");
|
|
UpdateHipadabaPar(child, MakeHdbFloat(fHard), pCon);
|
|
Log(VERBOSE,"par","%s:targetposition:%f", self->name,fHard);
|
|
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);
|
|
child = GetHipadabaNode(self->pDescriptor->parNode, "hardposition");
|
|
if((pPtr = GetHdbProp(child,"geterror")) != NULL){
|
|
SetHdbProperty(node,"geterror",pPtr);
|
|
} else {
|
|
SetHdbProperty(node,"geterror",NULL);
|
|
}
|
|
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;
|
|
pHdbPropertyChange pm = NULL;
|
|
pMotor self = (pMotor) userData;
|
|
float fVal;
|
|
hdbValue v;
|
|
|
|
assert(self != NULL);
|
|
|
|
mm = GetHdbUpdateMessage(message);
|
|
if (mm != NULL) {
|
|
v = *mm->v;
|
|
if(ABS(v.v.doubleValue - node->value.v.doubleValue) > .01){
|
|
Log(VERBOSE,"par","%s:hardposition:%f", self->name,v.v.doubleValue);
|
|
}
|
|
fVal = hardToSoftPosition(self, (float) v.v.doubleValue);
|
|
v.v.doubleValue = fVal;
|
|
UpdateHipadabaPar(self->pDescriptor->parNode, v, mm->callData);
|
|
return hdbContinue;
|
|
}
|
|
|
|
/*
|
|
forward geterror
|
|
*/
|
|
pm = GetPropertyChangeMessage(message);
|
|
if(pm != NULL){
|
|
if(strstr(pm->key,"geterror") != NULL){
|
|
SetHdbProperty(self->pDescriptor->parNode,pm->key, pm->value);
|
|
}
|
|
}
|
|
|
|
|
|
return hdbContinue;
|
|
}
|
|
/*--------------------------------------------------------------------------*/
|
|
static hdbCallbackReturn StatusUpdateCallback(pHdb node, void *userData,
|
|
pHdbMessage message)
|
|
{
|
|
pHdbDataMessage mm = NULL;
|
|
pHdbPropertyChange pm = NULL;
|
|
pMotor self = (pMotor) userData;
|
|
float fVal;
|
|
hdbValue v;
|
|
|
|
assert(self != NULL);
|
|
|
|
mm = GetHdbUpdateMessage(message);
|
|
if (mm != NULL) {
|
|
v = *mm->v;
|
|
if(strcmp(v.v.text,node->value.v.text) != 0){
|
|
UpdateHipadabaPar(self->pDescriptor->parNode, v, mm->callData);
|
|
Log(VERBOSE,"par","%s:status:%s", self->name,v.v.text);
|
|
}
|
|
return hdbContinue;
|
|
}
|
|
|
|
/*
|
|
forward geterror
|
|
*/
|
|
pm = GetPropertyChangeMessage(message);
|
|
if(pm != NULL){
|
|
if(strstr(pm->key,"geterror") != NULL){
|
|
SetHdbProperty(self->pDescriptor->parNode,pm->key, pm->value);
|
|
}
|
|
}
|
|
|
|
|
|
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;
|
|
}
|
|
|
|
/*--------------------------------------------------------------------------*/
|
|
typedef struct {
|
|
char *pName;
|
|
SConnection *pCon;
|
|
float lastValue;
|
|
} MotInfo, *pMotInfo;
|
|
/*--------------------------------------------------------------------------*/
|
|
static void KillInfo(void *pData)
|
|
{
|
|
pMotInfo self = NULL;
|
|
|
|
assert(pData);
|
|
self = (pMotInfo) pData;
|
|
if (self->pName) {
|
|
free(self->pName);
|
|
}
|
|
if (self->pCon != NULL) {
|
|
SCDeleteConnection(self->pCon);
|
|
}
|
|
free(self);
|
|
}
|
|
/*-------------------------------------------------------------------------*/
|
|
static hdbCallbackReturn InterestCallback(pHdb node, void *userData,
|
|
pHdbMessage message)
|
|
{
|
|
pHdbDataMessage mm = NULL;
|
|
pMotor self = (pMotor) userData;
|
|
float fVal;
|
|
hdbValue v;
|
|
pMotInfo priv = (pMotInfo)userData;
|
|
|
|
|
|
assert(self != NULL);
|
|
|
|
mm = GetHdbUpdateMessage(message);
|
|
if (mm != NULL) {
|
|
v = *mm->v;
|
|
if(!SCisConnected(priv->pCon)){
|
|
return hdbKill;
|
|
}
|
|
if(ABS(v.v.doubleValue - priv->lastValue) > .1) {
|
|
SCPrintf(priv->pCon,eValue,"%s.position = %f",
|
|
priv->pName, v.v.doubleValue);
|
|
priv->lastValue = v.v.doubleValue;
|
|
}
|
|
return hdbContinue;
|
|
}
|
|
return hdbContinue;
|
|
}
|
|
/*---------------------------------------------------------------------------*/
|
|
static int InterestCmd(pSICSOBJ ccmd, SConnection * con,
|
|
Hdb * cmdNode, Hdb * par[], int nPar)
|
|
{
|
|
pMotInfo priv = NULL;
|
|
|
|
priv = malloc(sizeof(MotInfo));
|
|
if(priv == NULL){
|
|
SCWrite(con,"ERROR: out of memory registering interest",eError);
|
|
return 0;
|
|
}
|
|
|
|
if(nPar >= 1 && (strcmp(par[0]->value.v.text,"UNKNOWN") != 0)) {
|
|
priv->pName = strdup(par[0]->value.v.text);
|
|
} else {
|
|
priv->pName = strdup(ccmd->objectNode->name);
|
|
}
|
|
priv->lastValue = .0;
|
|
priv->pCon = SCCopyConnection(con);
|
|
AppendHipadabaCallback(ccmd->objectNode,
|
|
MakeHipadabaCallback(InterestCallback,priv,KillInfo));
|
|
SCSendOK(con);
|
|
return 1;
|
|
|
|
}
|
|
/*---------------------------------------------------------------------------*/
|
|
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");
|
|
SetHdbProperty(child, "priv", "user");
|
|
AddHipadabaChild(node, child, NULL);
|
|
AppendHipadabaCallback(child,
|
|
MakeHipadabaCallback(SecMotorSignCallback, pM,
|
|
NULL));
|
|
UpdateHipadabaPar(child, MakeHdbFloat(1.), NULL);
|
|
|
|
child = MakeHipadabaNode("softzero", HIPFLOAT, 1);
|
|
SetHdbProperty(child, "__save", "true");
|
|
SetHdbProperty(child, "priv", "user");
|
|
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);
|
|
AppendHipadabaCallback(child,
|
|
MakeHipadabaCallback(StatusUpdateCallback, pM,
|
|
NULL));
|
|
AddHipadabaChild(node, child, NULL);
|
|
|
|
child = MakeHipadabaNode("error", HIPTEXT, 1);
|
|
AddHipadabaChild(node, child, NULL);
|
|
|
|
child = AddSICSHdbPar(node,"interest", usUser, MakeSICSFunc(InterestCmd));
|
|
AddSICSHdbPar(child, "name", usUser, MakeHdbText("UNKNOWN"));
|
|
|
|
|
|
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);
|
|
}
|
|
|
|
RemoveHdbNodeFromParent(self->objectNode,NULL);
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
/*--------------------------------------------------------------------------
|
|
everywhere we need to find a motor for a name quite frequently.......
|
|
----------------------------------------------------------------------------*/
|
|
pMotor FindMotor(SicsInterp * pSics, char *name)
|
|
{
|
|
CommandList *pC;
|
|
pMotor pMot;
|
|
|
|
pC = FindCommand(pSics, name);
|
|
if (!pC) {
|
|
return NULL;
|
|
}
|
|
pMot = (pMotor) pC->pData;
|
|
if (!pMot) {
|
|
return NULL;
|
|
}
|
|
if (strcmp(pMot->pDescriptor->name, "Motor") != 0 ) {
|
|
return NULL;
|
|
}
|
|
return pMot;
|
|
}
|