Files
sics/motor.c
koennecke a5c2da6acf - Switched motor to hdb
- Changes to Hipadaba
- Added project to histogram memory code
- Started regression testing code
- Added hill climbing as optimization method to optimise
2006-08-16 14:13:05 +00:00

1352 lines
41 KiB
C

/*------------------------------------------------------------------------
M O T O R S
This file implements the SICS motor handling. This is the logical
level, the nitty gritty hardware interface is in the driver.
This is a totally revamped version which uses the Hipadaba for
data storage.
copyright: see file COPYRIGHT
Mark Koennecke, July 2006
-----------------------------------------------------------------------------*/
#include <stdlib.h>
#include <assert.h>
#include <string.h>
#include <math.h>
#include <tcl.h>
#include "sics.h"
#include "devexec.h"
#include "motor.h"
#include "splitter.h"
#include "status.h"
#include "servlog.h"
#include "tclmotdriv.h"
#include "site.h"
#include "sicshipadaba.h"
#include "stptok.h"
/*-------------------------------------------------------------------------
some lokal defines
*/
#define ZEROINACTIVE 0
#define INTCONT 0.
#define INTSCAN 1.
#define INTBATCH 2.
#define INTHALT 3.
#define ABS(x) (x < 0 ? -(x) : (x))
/*------------------------------------------------------------------------
a tiny structure used in CallBack work
*/
typedef struct {
float fVal;
char *pName;
} MotCallback;
/*-------------------------------------------------------------------------*/
static void *MotorGetInterface(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 int MotorHalt(void *sulf){
pMotor self;
assert(sulf);
self = (pMotor)sulf;
/* reduce the error count by 1. This is because the driver is
expected to return an error when the motor had been stopped.
However, a stop is usually a consequence of a user intervention
or program logic. This prevents to a false motor alarm when the
motor was repeatedly stopped for other reasons.
*/
self->pDrivInt->iErrorCount--;
if(self->pDrivInt->iErrorCount < 0)
self->pDrivInt->iErrorCount = 0;
self->stopped = 1;
return self->pDriver->Halt((void *)self->pDriver);
}
/*--------------------------------------------------------------------------*/
static int MotorLimits(void *sulf, float fVal, char *error, int iErrLen){
float fHard;
pMotor self;
assert(sulf);
self = (pMotor)sulf;
return MotorCheckBoundary(self,fVal,&fHard,error,iErrLen);
}
/*-------------------------------------------------------------------------*/
/*---------------------------------------------------------------------------*/
static float MotorGetValue(void *pData, SConnection *pCon){
int iRet;
float fVal = 0.;
assert(pData);
iRet = MotorGetHardPosition((pMotor)pData,pCon,&fVal);
if(iRet != OKOK){
fVal = -9999999.99;
}
return fVal;
}
/*------------------------------------------------------------------------*/
static int MotorSaveStatus(void *pData, char *name, FILE *fd){
pMotor self = NULL;
char pBueffel[512];
assert(pData);
assert(fd);
self= (pMotor)pData;
fprintf(fd,"# Motor %s\n",name);
strncpy(pBueffel,name,511);
strncat(pBueffel," ",511);
SaveSICSHipadaba(fd,self->pDescriptor->parNode,pBueffel);
return 1;
}
/*-------------------------------------------------------------------------*/
static void MotorInterrupt(SConnection *pCon, int iVal){
if(SCGetInterrupt(pCon) < iVal){
SCSetInterrupt(pCon,iVal);
}
}
/*---------------------------------------------------------------------*/
static int statusRunTo(pMotor self, SConnection *pCon)
{
char pBueffel[256];
float posCount;
assert(SICSHdbGetFloat(self->pDescriptor->parNode,pCon,"maxretry",
&posCount) == 1);
if(self->retryCount >= (int)posCount)
{
snprintf(pBueffel,255,"ERROR: aborting motor %s after %d retries",
self->name, self->retryCount);
SCWrite(pCon,pBueffel,eError);
return HWFault;
}
if(SCGetInterrupt(pCon) != eContinue){
return HWFault;
}
self->retryCount++;
snprintf(pBueffel,255,"WARNING: restarting %s, %d time",
self->name,self->retryCount);
SCWrite(pCon,pBueffel,eWarning);
self->pDriver->RunTo(self->pDriver,self->fTarget);
return HWBusy;
}
/*--------------------------------------------------------------------*/
static int checkPosition(pMotor self, SConnection *pCon)
{
float fHard, precision;
char pBueffel[132];
int status;
assert(SICSHdbGetFloat(self->pDescriptor->parNode,pCon,
"precision",&precision) == 1);
MotorGetHardPosition(self,pCon,&fHard);
self->fPosition = fHard;
if(ABS(fHard - self->fTarget) > precision){
if(SCGetInterrupt(pCon) != eContinue){
return HWFault;
}
if(self->stopped){
snprintf(pBueffel,131,"WARNING: %s stopped", self->name);
SCWrite(pCon,pBueffel, eWarning);
return HWFault;
}
snprintf(pBueffel,131,"WARNING: %s off position by %f",
self->name, ABS(fHard - self->fTarget));
SCWrite(pCon,pBueffel, eWarning);
status = statusRunTo(self,pCon);
return status;
}
return HWIdle;
}
/*--------------------------------------------------------------------*/
static void finishDriving(pMotor self, SConnection *pCon){
MotCallback sCall;
MotorGetSoftPosition(self,pCon,&sCall.fVal);
sCall.pName = self->name;
InvokeCallBack(self->pCall, MOTDRIVE, &sCall); /* send also very last position */
InvokeCallBack(self->pCall, MOTEND, &sCall);
UpdateHipadabaPar(self->pDescriptor->parNode,
MakeHdbFloat((double)sCall.fVal),pCon);
}
/*--------------------------------------------------------------------*/
static int reportAndFixError(pMotor self, SConnection *pCon){
char pBueffel[256], pError[131];
int iCode, iRet, newStatus;
self->pDriver->GetError(self->pDriver,&iCode, pError,131);
iRet = self->pDriver->TryAndFixIt(self->pDriver,iCode, self->fTarget);
switch(iRet){
case MOTFAIL:
snprintf(pBueffel,255,"ERROR: %s on %s",pError,self->name);
SCWrite(pCon,pBueffel,eError);
newStatus = HWFault;
break;
case MOTREDO:
snprintf(pBueffel,255,"WARNING: %s on %s",pError,self->name);
SCWrite(pCon,pBueffel,eWarning);
newStatus = statusRunTo(self,pCon);
break;
case MOTOK:
snprintf(pBueffel,255,"WARNING: %s on %s",pError,self->name);
SCWrite(pCon,pBueffel,eWarning);
newStatus = HWIdle;
break;
default:
SCWrite(pCon,"WARNING: bad status code in motor.c:reportAndFixError",
eWarning);
SCWrite(pCon,"You may continue, but show this to a SICS programmer",
eWarning);
newStatus = HWIdle;
break;
}
return newStatus;
}
/*---------------------------------------------------------------------*/
static int evaluateStatus(pMotor self, SConnection *pCon){
int iRet, iCode, newStatus;
MotCallback sCall;
char pBueffel[256], pError[132];
float fHard, ignorefault, interrupt;
assert(SICSHdbGetFloat(self->pDescriptor->parNode,pCon,
"ignorefault",&ignorefault) == 1);
assert(SICSHdbGetFloat(self->pDescriptor->parNode,pCon,
"interruptmode",&interrupt) == 1);
iRet = self->pDriver->GetStatus(self->pDriver);
newStatus = iRet;
switch(iRet){
case OKOK:
case HWIdle:
newStatus = checkPosition(self,pCon);
if(newStatus != HWBusy){
finishDriving(self,pCon);
}
break;
case HWFault:
newStatus = reportAndFixError(self,pCon);
break;
case HWPosFault:
newStatus = reportAndFixError(self,pCon);
if(newStatus == HWFault && ignorefault < 1){
newStatus = HWPosFault;
}
if(newStatus == HWIdle || newStatus == OKOK){
newStatus = checkPosition(self,pCon);
if(newStatus != HWBusy){
finishDriving(self,pCon);
}
}
break;
case HWBusy:
newStatus = HWBusy;
break;
case HWWarn:
self->pDriver->GetError(self->pDriver,&iCode, pError,131);
snprintf(pBueffel,255,"WARNING: motor reported: %s", pError);
SCWrite(pCon,pBueffel,eWarning);
newStatus = HWIdle;
break;
default:
SCWrite(pCon,"WARNING: Bad status in motor.c:evaluatStatus",eWarning);
SCWrite(pCon,"You may continue, but show this to a SICS programmer",
eWarning);
break;
}
if(newStatus == HWFault){
MotorInterrupt(pCon,(int)interrupt);
self->retryCount = 0;
}
return newStatus;
}
/*---------------------------------------------------------------------*/
static void handleMoveCallback(pMotor self, SConnection *pCon){
MotCallback sCall;
float movecount;
assert(SICSHdbGetFloat(self->pDescriptor->parNode,pCon,
"movecount",&movecount) == 1);
self->posCount++;
if(self->posCount >= (int)movecount){
MotorGetSoftPosition(self,pCon,&sCall.fVal);
sCall.pName = self->name;
InvokeCallBack(self->pCall, MOTDRIVE, &sCall);
self->posCount = 0;
UpdateHipadabaPar(self->pDescriptor->parNode,
MakeHdbFloat((double)sCall.fVal),pCon);
}
}
/*-----------------------------------------------------------------------*/
static int MotorStatus(void *sulf, SConnection *pCon){
pMotor self = NULL;
int status;
assert(sulf);
self = (pMotor)sulf;
status = evaluateStatus(self,pCon);
if (self->pDrivInt->drivableStatus!=status) {
((SConnection *)pCon)->conEventType=STATUS;
((SConnection *)pCon)->conStatus=status;
SCWrite(pCon, "", eEvent);
self->pDrivInt->drivableStatus=status;
}
if(status == HWBusy){
handleMoveCallback(self,pCon);
}
return status;
}
/*---------------------------------------------------------------------------*/
static int MotorZeroCallback(void *userData, void *callData, pHdb node,
hdbValue v){
pMotor self = NULL;
float limit, oldZero, diff;
SConnection *pCon = NULL;
self = (pMotor)userData;
assert(self != NULL);
pCon = (SConnection *)callData;
assert(SICSHdbGetFloat(self->pDescriptor->parNode,pCon,
"softzero",&oldZero) == 1);
diff = v.v.doubleValue - oldZero;
assert(SICSHdbGetFloat(self->pDescriptor->parNode,pCon,
"softupperlim",&limit) == 1);
limit -= diff;
SICSHdbSetFloat(self->pDescriptor->parNode,(SConnection *)callData,
"softupperlim",limit);
assert(SICSHdbGetFloat(self->pDescriptor->parNode,pCon,
"softlowerlim",&limit) == 1);
limit -= diff;
SICSHdbSetFloat(self->pDescriptor->parNode,(SConnection *)callData,
"softlowerlim",limit);
UpdateHipadabaPar(node,v,callData);
return 1;
}
/*--------------------------------------------------------------------------*/
static int MotorSignCallback(void *userData, void *callData, pHdb node,
hdbValue v){
pMotor self = NULL;
SConnection *pCon = NULL;
pHdb zero = NULL;
self = (pMotor)userData;
pCon = (SConnection *)callData;
if(ABS(v.v.doubleValue - 1.) > .01){
if(pCon!= NULL){
SCWrite(pCon,"ERROR: invalid sign value",eError);
return 0;
}
}
SICSHdbSetFloat(self->pDescriptor->parNode,pCon,
"softlowerlim",self->pDriver->fLower*v.v.doubleValue);
SICSHdbSetFloat(self->pDescriptor->parNode,pCon,
"softupperlim",self->pDriver->fUpper*v.v.doubleValue);
zero = GetHipadabaNode(self->pDescriptor->parNode,"softzero");
if(zero != NULL){
UpdateHipadabaPar(zero,MakeHdbFloat(.0),pCon);
}
UpdateHipadabaPar(node,v,callData);
return 1;
}
/*--------------------------------------------------------------------------*/
static int MotorDriverReadCallback(void *userData, void *callData, pHdb node,
hdbValue v){
pMotor self = NULL;
SConnection *pCon = NULL;
int status;
float value;
self = (pMotor)userData;
assert(self);
pCon = (SConnection *)callData;
if(self->pDriver->GetDriverPar != NULL){
status = self->pDriver->GetDriverPar(self->pDriver,node->name,
&value);
v.dataType = HIPFLOAT;
v.v.doubleValue = (double)value;
node->value.v.doubleValue = (double)value;
return status;
}
return 1;
}
/*--------------------------------------------------------------------------*/
static int MotorDriverSetCallback(void *userData, void *callData, pHdb node,
hdbValue v){
pMotor self = NULL;
SConnection *pCon = NULL;
int status;
float value;
self = (pMotor)userData;
assert(self);
pCon = (SConnection *)callData;
if(pCon != NULL && self->pDriver->SetDriverPar != NULL){
status = self->pDriver->SetDriverPar(self->pDriver,pCon, node->name,
(float)v.v.doubleValue);
if(status == 1){
UpdateHipadabaPar(node,v,callData);
}
return status;
}
return 1;
}
/*--------------------------------------------------------------------------*/
static pHdb MakeMotorDriverNode(pMotor pM, char *name){
pHdb node = NULL;
node = MakeHipadabaNode(name,HIPFLOAT,1);
if(node == NULL){
return NULL;
}
AppendHipadabaCallback(node,HCBSET,
MakeHipadabaCallback(MotorDriverSetCallback,pM,NULL,-1,-1));
AppendHipadabaCallback(node,HCBREAD,
MakeHipadabaCallback(MotorDriverReadCallback,pM,NULL,-1,-1));
return node;
}
/*--------------------------------------------------------------------------*/
static int MotorHardCallback(void *userData, void *callData, pHdb node,
hdbValue v){
int status;
pMotor self = NULL;
float value;
SConnection *pCon = NULL;
self = (pMotor)userData;
pCon = (SConnection *)callData;
assert(self != NULL);
assert(pCon != NULL);
status = MotorGetHardPosition(self,pCon,&value);
node->value.v.doubleValue = (double)value;
v.v.doubleValue = (double)value;
return status;
}
/*--------------------------------------------------------------------------*/
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);
}
/*---------------------------------------------------------------------------
* This currently uses the ListDriverPar function of the driver. In
* a later stage this should be modified to test for and use a new
* driver function, CreateDriverNodes. But this scheme allows to use
* existing drivers without a change, at the expense of some complicated
* code.
* --------------------------------------------------------------------------*/
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 = MakeMotorDriverNode(pM, name);
if(node != NULL){
AddHipadabaChild(parent,node);
}
}
free(listPtr);
return 1;
}
/*---------------------------------------------------------------------------*/
pMotor MotorInit(char *drivername, char *name, MotorDriver *pDriv){
pMotor pM = NULL;
pHdb node = NULL, child = NULL;
assert(drivername);
assert(pDriv);
assert(name);
/* get memory */
pM = (pMotor)malloc(sizeof(Motor));
if(!pM){
return NULL;
}
/* initialise object descriptor */
pM->pDescriptor = CreateDescriptor("Motor");
if(!pM->pDescriptor){
free(pM);
return NULL;
}
pM->pDescriptor->GetInterface = MotorGetInterface;
pM->pDescriptor->SaveStatus = MotorSaveStatus;
/* copy arguments */
pM->pDriver = pDriv;
pM->drivername = strdup(drivername);
pM->name = strdup(name);
/* create and initialize parameters */
node = MakeSICSHdbDriv(name,usUser,pM,HIPFLOAT);
if(node == NULL){
free(pM);
return NULL;
}
pM->pDescriptor->parNode = node;
child = MakeSICSROPar("targetposition",MakeHdbFloat(pM->fPosition));
AppendHipadabaCallback(child,HCBREAD,
MakeMemReadCallback(&pM->fTarget));
AddHipadabaChild(node,child);
child = MakeSICSROPar("hardposition",MakeHdbFloat(pM->fPosition));
AppendHipadabaCallback(child,HCBREAD,
MakeHipadabaCallback(MotorHardCallback,pM,NULL,-1,-1));
AddHipadabaChild(node,child);
child = MakeHipadabaNode("sign",HIPFLOAT, 1);
AppendHipadabaCallback(child,HCBSET, MakeCheckPermissionCallback(usUser));
AppendHipadabaCallback(child,HCBSET,
MakeHipadabaCallback(MotorSignCallback,pM,NULL,-1,-1));
SetHipadabaPar(child,MakeHdbFloat(1.),NULL);
AddHipadabaChild(node,child);
child = MakeHipadabaNode("hardlowerlim",HIPFLOAT, 1);
AppendHipadabaCallback(child,HCBREAD,
MakeMemReadCallback(&pM->pDriver->fLower));
AppendHipadabaCallback(child,HCBSET,
MakeHipadabaCallback(MotorDriverSetCallback,pM,NULL,-1,-1));
AddHipadabaChild(node,child);
child = MakeHipadabaNode("hardupperlim",HIPFLOAT, 1);
AppendHipadabaCallback(child,HCBREAD,
MakeMemReadCallback(&pM->pDriver->fUpper));
AppendHipadabaCallback(child,HCBSET,
MakeHipadabaCallback(MotorDriverSetCallback,pM,NULL,-1,-1));
AddHipadabaChild(node,child);
AddHipadabaChild(node,MakeSICSHdbPar("softlowerlim",usUser,
MakeHdbFloat(pDriv->fLower)));
AddHipadabaChild(node,MakeSICSHdbPar("softupperlim",usUser,
MakeHdbFloat(pDriv->fUpper)));
child = MakeHipadabaNode("softzero",HIPFLOAT, 1);
AppendHipadabaCallback(child,HCBSET, MakeCheckPermissionCallback(usUser));
AppendHipadabaCallback(child,HCBSET,
MakeHipadabaCallback(MotorZeroCallback,pM,NULL,-1,-1));
AddHipadabaChild(node,child);
AddHipadabaChild(node,MakeSICSHdbPar("fixed",usUser,
MakeHdbFloat(-1.)));
AddHipadabaChild(node,MakeSICSHdbPar("interruptmode",usMugger,
MakeHdbFloat(.0)));
AddHipadabaChild(node,MakeSICSHdbPar("precision",usMugger,
MakeHdbFloat(.1)));
AddHipadabaChild(node,MakeSICSHdbPar("accesscode",usMugger,
MakeHdbFloat((double)usUser)));
AddHipadabaChild(node,MakeSICSHdbPar("failafter",usMugger,
MakeHdbFloat((double)3.0)));
AddHipadabaChild(node,MakeSICSHdbPar("maxretry",usMugger,
MakeHdbFloat((double)3.0)));
AddHipadabaChild(node,MakeSICSHdbPar("ignorefault",usMugger,
MakeHdbFloat((double).0)));
AddHipadabaChild(node,MakeSICSHdbPar("movecount",usMugger,
MakeHdbFloat((double)10.0)));
pDriv->GetPosition(pDriv,&(pM->fPosition));
child = MakeSICSROPar("position",MakeHdbFloat(pM->fPosition));
AppendHipadabaCallback(child,HCBREAD,
MakeMemReadCallback(&pM->fPosition));
AddHipadabaChild(node,child);
pM->fTarget = pM->fPosition;
pM->endScriptID = 0;
CreateDriverParameters(pM,pM->pDescriptor->parNode);
/* initialise Drivable interface */
pM->pDrivInt = CreateDrivableInterface();
if(!pM->pDrivInt){
DeleteDescriptor(pM->pDescriptor);
free(pM);
return NULL;
}
pM->pDrivInt->SetValue = MotorRun;
pM->pDrivInt->CheckLimits = MotorLimits;
pM->pDrivInt->CheckStatus = MotorStatus;
pM->pDrivInt->GetValue = MotorGetValue;
pM->pDrivInt->Halt = MotorHalt;
/* initialise callback interface */
pM->pCall = CreateCallBackInterface();
if(!pM->pCall){
MotorKill(pM);
return NULL;
}
/* done */
return pM;
}
/*--------------------------------------------------------------------------*/
extern void KillPiPiezo(void *pData);
void MotorKill(void *self){
pMotor pM;
assert(self);
pM = (pMotor)self;
/* MotorHalt(pM); */
if(pM->name)
free(pM->name);
if(pM->pDrivInt){
free(pM->pDrivInt);
}
if(pM->pCall){
DeleteCallBackInterface(pM->pCall);
}
/* kill driver */
if(pM->drivername){
if(pM->pDriver->KillPrivate != NULL){
pM->pDriver->KillPrivate(pM->pDriver);
}
if(pM->pDriver->name != NULL){
free(pM->pDriver->name);
}
free(pM->pDriver);
free(pM->drivername);
}
/* kill Descriptor */
DeleteDescriptor(pM->pDescriptor);
free(pM);
}
/*--------------------------------------------------------------------------*/
int MotorGetPar(pMotor self, char *name, float *fVal){
char pBueffel[512];
snprintf(pBueffel,511,"%s ", name);
return SICSHdbGetFloat(self->pDescriptor->parNode,NULL,
pBueffel, fVal);
}
/*---------------------------------------------------------------------------*/
int MotorSetPar(pMotor self, SConnection *pCon, char *name, float fVal){
int status;
status = SICSHdbSetFloat(self->pDescriptor->parNode,
pCon,name,fVal);
if(status == 1){
SCparChange(pCon);
return 1;
} else {
return 0;
}
}
/*---------------------------------------------------------------------------
MotorCheckBoundary checks for violation of boundary conditions and
transforms from SoftCoordinates to hard coordinates.
*/
int MotorCheckBoundary(pMotor self, float fVal, float *fNew,
char *pError, int iErrLen)
{
float fHard;
float fZero, fixed, lowerlim, upperlim, sign;
char pBueffel[512];
assert(self);
assert(SICSHdbGetFloat(self->pDescriptor->parNode,NULL,
"fixed",&fixed) == 1);
assert(SICSHdbGetFloat(self->pDescriptor->parNode,NULL,
"softzero",&fZero) == 1);
assert(SICSHdbGetFloat(self->pDescriptor->parNode,NULL,
"softlowerlim",&lowerlim) == 1);
assert(SICSHdbGetFloat(self->pDescriptor->parNode,NULL,
"softupperlim",&upperlim) == 1);
assert(SICSHdbGetFloat(self->pDescriptor->parNode,NULL,
"sign",&sign) == 1);
/* 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 */
if(fHard > self->pDriver->fUpper){
sprintf(pBueffel,"%f violates upper hardware limit %f on %s",
fVal,self->pDriver->fUpper,self->name);
strncpy(pError,pBueffel,iErrLen);
return 0;
}
if(fHard < self->pDriver->fLower){
sprintf(pBueffel,"%f violates lower hardware limit %f on %s",
fVal,self->pDriver->fLower,self->name);
strncpy(pError,pBueffel,iErrLen);
return 0;
}
*fNew = fHard;
return 1;
}
/*---------------------------------------------------------------------------*/
long MotorRun(void *sulf, SConnection *pCon, float fNew){
float fHard, rights, ignore, maxerr, interrupt;
int i, iRet, iCode;
char pBueffel[512];
char pError[132];
pMotor self;
long lTime;
float fDelta;
self = (pMotor)(sulf);
assert(self);
assert(pCon);
assert(SICSHdbGetFloat(self->pDescriptor->parNode,NULL,
"accesscode",&rights) == 1);
assert(SICSHdbGetFloat(self->pDescriptor->parNode,NULL,
"ignorefault",&ignore) == 1);
assert(SICSHdbGetFloat(self->pDescriptor->parNode,NULL,
"failafter",&maxerr) == 1);
assert(SICSHdbGetFloat(self->pDescriptor->parNode,NULL,
"interruptmode",&interrupt) == 1);
/* check if I'am allowed to move this motor */
if(!SCMatchRights(pCon,(int)rights)){
sprintf(pBueffel,"ERROR: You are not authorised to move motor %s",
self->name);
SCWrite(pCon,pBueffel,eError);
SCSetInterrupt(pCon,eAbortBatch);
return 0;
}
/* check boundaries first */
iRet = MotorCheckBoundary(self,fNew,&fHard,pBueffel,511);
if(!iRet){
SCWrite(pCon,pBueffel,eStatus);
SCSetInterrupt(pCon,eAbortOperation);
return 0;
}
/* check if the bad motor flag is set */
if((int)ignore > 0){
snprintf(pBueffel,511,"WARNING: motor %s is unreliable",
self->name);
SCWrite(pCon,pBueffel,eWarning);
self->pDrivInt->iErrorCount = 0;
}
/* check our error count and interrupt if to much */
if(self->pDrivInt->iErrorCount > (int)maxerr){
/* 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->pDrivInt->iErrorCount = 0;
return 0;
}
/* Boundaries OK, send command */
self->posFaultCount = 0;
self->retryCount = 0;
self->stopped = 0;
self->fTarget = fHard;
self->posCount = 0;
iRet = self->pDriver->RunTo(self->pDriver,fHard);
if(iRet != OKOK){
/* try three times to fix it */
for(i = 0; (i < 3) && (iRet != OKOK); i++){
self->pDriver->GetError(self->pDriver,&iCode, pError,131);
sprintf(pBueffel,"WARNING: Trying to fix: %s",pError);
SCWrite(pCon,pBueffel,eWarning);
iRet = self->pDriver->TryAndFixIt(self->pDriver,iCode, fHard);
switch(iRet){
case MOTFAIL:
SCWrite(pCon,pError,eError);
SCWrite(pCon,"\n",eError);
SCSetInterrupt(pCon,(int)interrupt);
return HWFault;
case MOTREDO:
iRet = self->pDriver->RunTo(self->pDriver,fHard);
if(iRet == OKOK){
return OKOK;
}
break;
case MOTOK:
return OKOK;
break;
}
}
/* tried three times, refuses to work */
SCWrite(pCon,pError,eError);
SCWrite(pCon,"\n",eError);
SCSetInterrupt(pCon,(int)interrupt);
return HWFault;
}
return OKOK;
}
/*------------------------------------------------------------------------*/
int MotorGetHardPosition(pMotor self,SConnection *pCon, float *fHard){
int iRet, iCode;
float fVal, interrupt, sign;
char pBueffel[512],pError[256];
assert(self);
assert(pCon);
assert(SICSHdbGetFloat(self->pDescriptor->parNode,NULL,
"interruptmode",&interrupt) == 1);
assert(SICSHdbGetFloat(self->pDescriptor->parNode,NULL,
"sign",&sign) == 1);
iRet = self->pDriver->GetPosition(self->pDriver,&fVal);
if(iRet == OKOK) {
*fHard = fVal;
return 1;
} else {
/* no point in trying this three times */
self->pDriver->GetError(self->pDriver,&iCode, pError,255);
iRet = self->pDriver->TryAndFixIt(self->pDriver,iCode, fVal);
sprintf(pBueffel,"WARNING: Trying to fix %s",pError);
SCWrite(pCon,pBueffel,eWarning);
switch(iRet){
case MOTFAIL:
sprintf(pBueffel,"ERROR: cannot fix motor %s",
self->name);
SCWrite(pCon,pBueffel,eError);
SCSetInterrupt(pCon,(int)interrupt);
*fHard = fVal;
return 0;
case MOTOK:
case MOTREDO:
iRet = self->pDriver->GetPosition(self->pDriver,&fVal);
if(iRet){
*fHard = fVal*sign;
return 1;
} else{
sprintf(pBueffel,"ERROR: cannot fix motor %s",
self->name);
SCSetInterrupt(pCon,(int)interrupt);
SCWrite(pCon,pBueffel,eError);
*fHard = fVal;
return 0;
}
}
}
*fHard = fVal*sign;
return 0;
}
/*------------------------------------------------------------------------*/
float MotorHardToSoftPosition(pMotor self, float fValue){
float sign, zero;
assert(SICSHdbGetFloat(self->pDescriptor->parNode,NULL,
"sign",&sign) == 1);
assert(SICSHdbGetFloat(self->pDescriptor->parNode,NULL,
"softzero",&zero) == 1);
/* apply zeropoint */
if(sign < 0.){
fValue += zero;
}else {
fValue -= zero;
}
/* apply sign */
return fValue*sign;
}
/* ------------------------------------------------------------------------*/
int MotorGetSoftPosition(pMotor self, SConnection *pCon, float *fVal){
int iRet;
float fValue;
assert(self);
assert(pCon);
/* get the hard position */
iRet = MotorGetHardPosition(self,pCon,&fValue);
if(!iRet){
*fVal = fValue;
return 0;
}
*fVal = MotorHardToSoftPosition(self,fValue);
return 1;
}
/*---------------------------------------------------------------------------*/
int MotorCheckPosition(void *sulf, SConnection *pCon){
float fHard, precision;
int i, iRet, iCode;
char pBueffel[512];
pMotor self;
self = (pMotor)sulf;
assert(self);
assert(pCon);
assert(SICSHdbGetFloat(self->pDescriptor->parNode,NULL,
"precision",&precision) == 1);
/* try to find current position */
iRet = MotorGetHardPosition(self,pCon,&fHard);
if(iRet) {
self->fPosition = fHard;
if(ABS(fHard - self->fTarget) < precision){
return 1;
} else {
/* Oooopppsss error */
return 0;
}
} else {
/* error getting hold of position, MotorGetHard already tried to
solve the problem and FAILED, client already knows...*/
return -1;
}
}
/* --------------------------------------------------------------------------
The Factory function for creating a motor. Usage:
MotorCreate name DriverName.
Drivernames currently understood are:
EL734 SINQ EL734 stepper motor
EL734DC SINQ EL734 DC motor
PiPiezo Physik-Instrumente E-255 Piezo Controller
SIM simulated motor
*/
extern MotorDriver *MakePiPiezo(Tcl_Interp *pTcl, char *pArray);
int MotorCreate(SConnection *pCon, SicsInterp *pSics, void *pData,
int argc, char *argv[])
{
pMotor pNew = NULL;
MotorDriver *pDriver = NULL;
char pBueffel[512];
int iD, iRet;
Tcl_Interp *pTcl = (Tcl_Interp *)pSics->pTcl;
pSite site = NULL;
assert(pCon);
assert(pSics);
/* a first check */
if(argc < 3)
{
SCWrite(pCon,"Insufficient arguments for motor creation",eError);
return 0;
}
/* create the driver */
strtolower(argv[2]);
strtolower(argv[1]);
if (strcmp(argv[2],"sim") == 0)
{
iD = argc - 3;
pDriver = CreateSIM(pCon,iD,&argv[3]);
if(!pDriver)
{
return 0;
}
/* create the motor */
pNew = MotorInit("SIM",argv[1],pDriver);
if(!pNew)
{
sprintf(pBueffel,"Failure to create motor %s",argv[1]);
SCWrite(pCon,pBueffel,eError);
return 0;
}
}else if (strcmp(argv[2],"tclmot") == 0)
{
pDriver = CreateTclMotDriv(pCon,argc,argv);
if(!pDriver)
{
return 0;
}
/* create the motor */
pNew = MotorInit("TCLMOT",argv[1],pDriver);
if(!pNew)
{
sprintf(pBueffel,"Failure to create motor %s",argv[1]);
SCWrite(pCon,pBueffel,eError);
return 0;
}
} else if(strcmp(argv[2],"regress") == 0)
{
pDriver = RGMakeMotorDriver();
if(!pDriver)
{
return 0;
}
/* create the motor */
pNew = MotorInit("regress",argv[1],pDriver);
if(!pNew)
{
sprintf(pBueffel,"Failure to create motor %s",argv[1]);
SCWrite(pCon,pBueffel,eError);
return 0;
}
}
else
{
site = getSite();
if(site != NULL)
{
pNew = site->CreateMotor(pCon,argc-1,&argv[1]);
}
if(pNew == NULL)
{
sprintf(pBueffel,"Motor Type %s not recognized for motor %s",
argv[2],argv[1]);
SCWrite(pCon,pBueffel,eError);
return 0;
}
}
/* create the interpreter command */
iRet = AddCommand(pSics,argv[1],MotorAction,MotorKill,pNew);
if(!iRet)
{
sprintf(pBueffel,"ERROR: duplicate command %s not created",argv[1]);
SCWrite(pCon,pBueffel,eError);
return 0;
}
return 1;
}
/* ----------------- some private functions used in MotorAction -------------*/
void MotorListLL(pMotor self, SConnection *pCon)
{
char pBueffel[256];
snprintf(pBueffel,255,"Parameter Listing for motor %s",self->name);
SCWrite(pCon,pBueffel,eValue);
snprintf(pBueffel,255,"%s ",self->name);
PrintSICSParList(self->pDescriptor->parNode,pCon,pBueffel);
}
/*--------------------------------------------------------------------------*/
void MotorReset(pMotor pM)
{
SICSHdbSetFloat(pM->pDescriptor->parNode,NULL,"softlowerlim",
pM->pDriver->fLower);
SICSHdbSetFloat(pM->pDescriptor->parNode,NULL,"softupperlim",
pM->pDriver->fUpper);
SICSHdbSetFloat(pM->pDescriptor->parNode,NULL,"softzero",
.0);
SICSHdbSetFloat(pM->pDescriptor->parNode,NULL,"fixed",
-1.);
}
/*------------------------------------------------------------------------*/
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);
}
free(self);
}
/*------------------- The CallBack function for interest ------------------*/
static int InterestCallback(int iEvent, void *pEvent, void *pUser,
commandContext cc)
{
pMotInfo pInfo = NULL;
char pBueffel[80];
MotCallback *psCall;
assert(pEvent);
assert(pUser);
psCall = (MotCallback *)pEvent;
pInfo = (MotInfo *)pUser;
if (pInfo->lastValue != psCall->fVal) {
pInfo->lastValue = psCall->fVal;
(pInfo->pCon)->conEventType=POSITION;
sprintf(pBueffel,"%s.position = %f ", pInfo->pName, pInfo->lastValue);
SCWriteInContext(pInfo->pCon,pBueffel,eEvent,cc);
}
return 1;
}
/*------------------------------------------------------------------------*/
static void KillScript(void *pData)
{
if(pData != NULL)
{
free(pData);
}
}
/*------------------------ The endscript callback function ----------------*/
static int EndScriptCallback(int iEvent, void *pEvent, void *pUser,
commandContext cc)
{
char *pScript = NULL;
MotCallback *psCall;
char pCommand[1024];
int iRet;
assert(pEvent);
assert(pUser);
psCall = (MotCallback *)pEvent;
pScript = (char *)pUser;
sprintf(pCommand,"%s %f",pScript,psCall->fVal);
iRet = Tcl_Eval(pServ->pSics->pTcl,pCommand);
return iRet;
}
/*----------------------------------------------------------------------------
The wrapper function for a motor. Commands currently supported are:
motorname parametername newval : sets a parameter
motorname parametername : gets the value for a par
motorname list : lists all parameters
motorname reset : puts softlimits to default
motorname interest : starts sending automatic
notifications when driving
motorname endscript : script to call when motor
finishes driving
-----------------------------------------------------------------------------*/
int MotorAction(SConnection *pCon, SicsInterp *pSics, void *pData,
int argc, char *argv[])
{
char pBueffel[512];
TokenList *pList = NULL;
TokenList *pCurrent;
TokenList *pName;
int iRet;
pMotor self;
float fValue;
long lID;
pMotInfo pMoti = NULL;
assert(pCon);
assert(pSics);
assert(pData);
self = (pMotor)pData;
/* create Tokenlist */
argtolower(argc,argv);
pList = SplitArguments(argc,argv);
if(!pList)
{
SCWrite(pCon,"Error parsing argument list in MotorAction",eError);
return 0;
}
/* first argument can be one of list, reset or parameter name */
pCurrent = pList->pNext;
if(!pCurrent) /* no argument, print value */
{
iRet = MotorGetSoftPosition(self,pCon,&fValue);
if(!iRet)
{
sprintf(pBueffel,"Error obtaining position for %s",argv[0]);
SCWrite(pCon,pBueffel,eError);
DeleteTokenList(pList);
return 0;
}
sprintf(pBueffel,"%s = %f",argv[0],fValue);
SCWrite(pCon,pBueffel,eValue);
DeleteTokenList(pList);
return 1;
}
/* check for list */
if(strcmp(pCurrent->text,"list") == 0)
{
MotorListLL(self,pCon);
DeleteTokenList(pList);
return 1;
} /* check for reset */
else if(strcmp(pCurrent->text,"reset") == 0)
{
if(!SCMatchRights(pCon,usUser))
{
sprintf(pBueffel,"Insufficient privilege to reset %s",
argv[0]);
SCWrite(pCon,pBueffel,eError);
DeleteTokenList(pList);
return 0;
}
MotorReset(self);
DeleteTokenList(pList);
SCSendOK(pCon);
return 1;
}
else if(strcmp(pCurrent->text,"interest") == 0) /* interest */
{
pMoti = (pMotInfo)malloc(sizeof(MotInfo));
if(!pMoti)
{
SCWrite(pCon,"ERROR: out of memory in motor.c",eError);
return 0;
}
pMoti->pName = strdup(argv[0]);
pMoti->pCon = pCon;
iRet = MotorGetSoftPosition(self,pCon,&fValue);
if(!iRet)
{
sprintf(pBueffel,"Failed to register interest, Reason:Error obtaining current position for %s",argv[0]);
SCWrite(pCon,pBueffel,eError);
DeleteTokenList(pList);
return 0;
}
pMoti->lastValue = fValue;
lID = RegisterCallback(self->pCall, SCGetContext(pCon),MOTDRIVE, InterestCallback,
pMoti, KillInfo);
SCRegister(pCon,pSics, self->pCall,lID);
DeleteTokenList(pList);
SCSendOK(pCon);
return 1;
}
else if(strcmp(pCurrent->text,"uninterest") == 0)
{
RemoveCallback2(self->pCall,pCon);
SCSendOK(pCon);
DeleteTokenList(pList);
return 1;
}
else if(strcmp(pCurrent->text,"endscript") == 0) /* endscript */
{
if(!SCMatchRights(pCon,usMugger))
{
return 0;
}
if(self->endScriptID != 0)
{
RemoveCallback(self->pCall, self->endScriptID);
self->endScriptID = 0;
}
pCurrent = pCurrent->pNext;
if(!pCurrent)
{
SCWrite(pCon,"ERROR: scriptname argument missing",eError);
return 0;
}
self->endScriptID =
RegisterCallback(self->pCall, SCGetContext(pCon),MOTEND, EndScriptCallback,
strdup(pCurrent->text), KillScript);
SCRegister(pCon,pSics, self->pCall,self->endScriptID);
DeleteTokenList(pList);
SCSendOK(pCon);
return 1;
}
else /* one of the parameter commands or error left now */
{
snprintf(pBueffel,511,"%s ", argv[0]);
iRet = ProcessSICSHdbPar(self->pDescriptor->parNode,
pCon, pBueffel, argc-1, &argv[1]);
if(iRet == -1) {
SCWrite(pCon,"ERROR: subcommand to motor not understood",
eError);
return 0;
} else if(iRet < -1) {
return 0;
}
return iRet;
}
return 0;
}