/*--------------------------------------------------------------------------- I P S D R I V . C Driver for the Oxford Instruments IPS/PS magnet power supply. Markus Zolliker, June 2004 ----------------------------------------------------------------------------*/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include typedef enum { IPS_INIT, IPS_IDLE, IPS_START, IPS_WAIT, IPS_UP, IPS_STAB1, IPS_START_RAMP, IPS_RAMP, IPS_STAB2, IPS_DOWN } rampState; typedef struct { rs232 *ser; /* serial connection */ int errCode; int isPS; /* is an old PS-120 instead of an IPS-120 */ rampState state; float target; /* target field */ float field; /* actual field */ float ramp; /* actual ramp rate */ int persmode; /* 0: leave switch on, 1: go to persistant mode */ int perswitch; /* state of switch */ int force; /* flag: put heater switch even when stored field not matched */ float forcedfield; /* field user thinks is in magnet */ int verbose; /* verbosity 0..2 */ int settle; /* settling time/4 */ int stabilize; /* do we need to stabilize ? */ time_t tim; /* time when last switching the heater */ float voltage[4]; /* voltage history */ char xstat[32]; /* status response */ char msg[2048]; /* a message to return */ } IpsDriv; #define OX_ILL_ANS -3000 #define OX_ILL_FLD -3001 /*----------------------------------------------------------------------------*/ void IpsSay(IpsDriv *me, int verbosity, char *fmt, ...) { va_list ap; int iRet, l; if (verbosity <= me->verbose) { l=strlen(me->msg); if (l > 0 && l < sizeof(me->msg)-1) { strcat(me->msg, "\n"); l++; } va_start(ap, fmt); vsnprintf(me->msg+l, sizeof(me->msg)-l, fmt, ap); va_end(ap); } } /*----------------------------------------------------------------------------*/ static void IpsWriteError(IpsDriv *me) { switch (me->errCode) { case NOTCONNECTED: IpsSay(me, 0, "terminalserver for IPS not connected"); break; case FAILEDCONNECT: IpsSay(me, 0, "can not connect to IPS"); break; case TIMEOUT: IpsSay(me, 0, "IPS not properly connected"); break; case INCOMPLETE: IpsSay(me, 0, "incomplete answer from IPS"); break; case OX_ILL_ANS: IpsSay(me, 0, "illegal answer from IPS"); break; default: IpsSay(me, 0, "error code %d", me->errCode); break; } } /*----------------------------------------------------------------------------*/ static char *IpsCmd(IpsDriv *me, char *cmd, int *ret) { int iret; static char buf[32]; *ret=transactRS232(me->ser, cmd, strlen(cmd), buf, sizeof(buf)); IpsSay(me, 2, "command: %s, answer: %s", cmd, buf); if (*ret != 1) { if (*ret == 0) { *ret == 1; IpsSay(me, 0, "IPS: unknown RS232 error"); } else { IpsWriteError(me); } return NULL; } if (buf[0] != cmd[0]) { IpsSay(me, 0, "command: %s, answer: %s", cmd, buf); *ret=OX_ILL_ANS; IpsWriteError(me); } else { *ret=0; } return buf; } /*----------------------------------------------------------------------------*/ static float IpsRead(IpsDriv *me, char *cmd, int fmt, int *ret) { char *buf; float val; buf=IpsCmd(me, cmd, ret); if (*ret) return 0.0; if (me->isPS) { /* no decimal point expected */ val=atoi(buf+1); for (; fmt>0; fmt--) val=val*0.1; } else { val=atof(buf+1); } return val; } /*----------------------------------------------------------------------------*/ static char *IpsStatus(IpsDriv *me, int *ret) { char *ans; ans=IpsCmd(me, "X", ret); if (*ret) return me->xstat; strncpy(me->xstat, ans, sizeof(me->xstat)); me->xstat[sizeof(me->xstat)-1]='\0'; return me->xstat; } /*----------------------------------------------------------------------------*/ static int IpsCheckField(IpsDriv *me, char *name, int *ret) { float fld; if (strstr(me->xstat, "H1")==NULL) { me->perswitch=0; fld=IpsRead(me, "R18", 3, ret); /* read persistant field */ if (fabs(fld - me->field) >= 1e-5) { IpsSay(me, 0, "\nit is not sure which field is in the magnet\n" "value stored in power supply: %f\n" " in sics: %f\n" "use command\n \n %s confirm ...\n \n" "to specify the persistent field\n \n" , fld, me->field, name); return -1; } return 0; } me->perswitch=1; fld=IpsRead(me, "R7", 3, ret); /* read actual field */ me->field=fld; return 1; } /*----------------------------------------------------------------------------*/ static void IpsSet(IpsDriv *me, char *cmd, float val, int fmt, int *ret) { char buf[64]; if (me->isPS) { for (;fmt>0;fmt--) val=val*10; snprintf(buf, sizeof(buf), "%s%05d", cmd, val); } else { snprintf(buf, sizeof(buf), "%s%f", cmd, val); } IpsCmd(me, buf, ret); } /*----------------------------------------------------------------------------*/ static int IpsIsStable(IpsDriv *me) { /* returns 1 when 4 subsequent readings within 0.01 Volts. the timeinterval for the readings is specified with me->settle */ time_t now; float v, vmin, vmax; int i; int *ret; assert(me); ret=&me->errCode; time(&now); now = now / me->settle; v = IpsRead(me, "R1", 2, ret); /* voltage */ if (*ret) return 0; i = now % 4; me->voltage[i] = v; vmin=me->voltage[0]; vmax=vmin; for (i=1; i<4; i++) { if (me->voltage[i] < vmin) vmin = me->voltage[i]; if (me->voltage[i] > vmax) vmax = me->voltage[i]; } return (fabs(vmax-vmin) < 0.015); } /*----------------------------------------------------------------------------*/ static int IpsWait(IpsDriv *me) { time_t now; time(&now); if (now < me->tim + 30) { /* we have to wait for stabilizing the switch heater */ IpsSay(me, 2, "waiting %d sec", me->tim + 30 - now); if (strstr(me->xstat, "H5")!=NULL && now > me->tim+5) { IpsSay(me, 0, "IPS: switch heater is not connected"); me->state=IPS_IDLE; return HWFault; } if (now < me->tim+30) return HWBusy; } return 0; } /*----------------------------------------------------------------------------*/ static int IpsStepper(void *evcV, SConnection *pCon) { pEVControl evc; pEVDriver self; IpsDriv *me; int *ret; float fld; float step; float ramp; int i; int swi; int hwstate; char *ans; time_t now; evc=evcV; assert(evc); self=evc->pDriv; assert(self); me = (IpsDriv *)self->pPrivate; assert(me); me->msg[0]='\0'; me->errCode=0; ret=&me->errCode; /* check status */ if (me->state == IPS_IDLE) { hwstate = HWIdle; } else { hwstate = HWBusy; ans = IpsStatus(me, ret); if (*ret) goto error; if (strstr(ans, "X00") != ans || strchr(ans, 'A') == NULL || strchr(ans, 'C') == NULL || strchr(ans, 'H') == NULL || strchr(ans, 'M') == NULL || strchr(ans, 'P') == NULL) { IpsSay(me, 0, "IPS illegal status: %s", ans); goto error; } if (strstr(ans, "C3") == NULL && me->state > IPS_START) { IpsSay(me, 0, "IPS switched to local - stopped"); IpsCmd(me, "C0", ret); goto error; } if (evc->iStop) { IpsSay(me, 0, "IPS stopped"); IpsCmd(me, "C0", ret); me->state = IPS_IDLE; hwstate = HWIdle; } } switch (me->state) { case IPS_START: swi=IpsCheckField(me, evc->pName, ret); if (*ret) goto error; if (swi < 0) { *ret=1; goto error; } if (fabs(evc->fTarget - me->field) < 1e-5 && me->perswitch != me->persmode) { IpsSay(me, 1, "IPS: we are already at field %f", me->field); me->state = IPS_IDLE; hwstate = HWIdle; break; } IpsCmd(me, "C3", ret); if (*ret) goto error; IpsCmd(me, "F7", ret); /* show current in Tesla */ if (*ret) goto error; IpsCmd(me, "A0", ret); /* hold */ if (*ret) goto error; if (swi < 0) { *ret=OX_ILL_FLD; goto error; } me->stabilize=0; if (!me->perswitch) { fld=IpsRead(me, "R7", 3, ret); /* read actual current (in Tesla) */ if (*ret) goto error; if (fabs(fld - me->field) >= 1e-5) { me->stabilize=1; time(&now); if (now < me->tim+30) { IpsSay(me, 1, "IPS: wait until heat switch is open"); } } } me->state=IPS_WAIT; /* fall through */ case IPS_WAIT: if (me->stabilize) { i=IpsWait(me); if (i) { hwstate=i; break; } IpsSet(me, "J", me->field, 3, ret); /* set point */ if (*ret) goto error; IpsSay(me, 1, "IPS: ramp to current for %f Tesla", me->field); IpsCmd(me, "A1", ret); /* go to setpoint */ if (*ret) goto error; for (i=0; i<4; i++) { /* initialize voltage history */ me->voltage[i] = 10*i; /* just some dirty values */ } } me->state = IPS_UP; /* fall through */ case IPS_UP: fld=IpsRead(me, "R7", 3, ret); /* read actual field */ if (fabs(fld - me->field) > 1e-5) break; me->state = IPS_STAB1; if (me->stabilize) { IpsSay(me, 1, "IPS: stabilize"); } case IPS_STAB1: if (me->stabilize && ! IpsIsStable(me)) break; me->state=IPS_START_RAMP; if (strstr(me->xstat, "H1")==NULL) { if (me->force==2) { IpsCmd(me, "H2", ret); } else { IpsCmd(me, "H1", ret); } if (*ret) goto error; me->perswitch = 1; me->force = 0; time(&me->tim); IpsSay(me, 1, "IPS: wait until heat switch is open"); break; } me->perswitch = 1; me->force=0; /* fall through */ case IPS_START_RAMP: i=IpsWait(me); if (i) { hwstate=i; break; } IpsSet(me, "T", me->ramp, 3, ret); if (*ret) goto error; IpsSay(me, 1, "IPS: ramp to %f Tesla", evc->fTarget); IpsCmd(me, "A1", ret); if (*ret) goto error; me->state=IPS_RAMP; /* fall through */ case IPS_RAMP: ramp=IpsRead(me, "R9", 3, ret); /* read ramp rate */ if (*ret) goto error; me->field=IpsRead(me, "R7", 3, ret); /* read "current" in Tesla */ if (*ret) goto error; step=ramp/20; /* step = ramp * 3sec */ if (step < 0.001) step=0.001; if (evc->fTarget > me->field + step) { fld=me->field + step; } else if (evc->fTarget < me->field - step) { fld=me->field - step; } else { fld=evc->fTarget; if (fabs(me->field - evc->fTarget) < 1e-5) { IpsCmd(me, "A0", ret); if (!me->persmode) { IpsCmd(me, "C0", ret); if (*ret) goto error; me->state = IPS_IDLE; hwstate = HWIdle; break; } for (i=0; i<4; i++) { /* initialize voltage history */ me->voltage[i]=10*i; /* just some dirty values */ } me->state = IPS_STAB2; IpsSay(me, 1, "IPS: stabilize"); } } IpsSet(me, "J", fld, 3, ret); if (*ret) goto error; break; case IPS_STAB2: evc->eMode = EVMonitor; if (!me->persmode || IpsIsStable(me)) { IpsSay(me, 1, "IPS: wait until heat switch is closed"); IpsCmd(me, "H0", ret); if (*ret) goto error; me->perswitch = 0; time(&me->tim); me->state = IPS_DOWN; } hwstate = HWIdle; break; case IPS_DOWN: hwstate = HWIdle; i=IpsWait(me); if (i) { if (i == HWFault) { hwstate = HWFault; } } else { IpsSay(me, 1, "IPS: ramp current down"); IpsCmd(me, "A2", ret); if (*ret) goto error; IpsCmd(me, "C0", ret); if (*ret) goto error; me->state = IPS_IDLE; } break; } error: if (*ret) { me->state = IPS_IDLE; hwstate=HWFault; } if (me->msg[0] != '\0') { if (pCon != NULL) { SCWrite(pCon, me->msg, eWarning); } else { ServerWriteGlobal(me->msg, eWarning); } me->msg[0]='\0'; } return hwstate; } /*-------------------------------------------------------------------------*/ int IpsWrapper(SConnection *pCon, SicsInterp *pSics, void *pData, int argc, char *argv[]) { pEVControl self = NULL; char cmd[256], result[256], *res; int iRet, *ret; pEVDriver pD; IpsDriv *me; float fVal, fVal2, pf; self = (pEVControl)pData; assert(self); assert(pCon); assert(pSics); if (argc < 2) { return EVControlWrapper(pCon,pSics,pData,argc,argv); } pD=self->pDriv; assert(pD); me=pD->pPrivate; assert(me); me->msg[0]='\0'; ret=&me->errCode; if (strlen(argv[1]) >= sizeof(cmd)) { return EVControlWrapper(pCon,pSics,pData,argc,argv); } strcpy(cmd, argv[1]); strtolower(cmd); if (0==strcmp(cmd, "ramp")) { if (argc > 2) { me->ramp=atof(argv[2]); } sprintf(result, "%f", me->ramp); } else if (0==strcmp(cmd, "limit")) { if (argc == 2) { fVal=ObVal(self->pParam, UPLIMIT); fVal2=ObVal(self->pParam, LOWLIMIT); if (-fVal2 > fVal) fVal=fVal2; } else { fVal=atof(argv[2]); iRet = EVCSetPar(self, "upperlimit", fabs(fVal),pCon); if (!iRet) { *ret=1; goto error; } iRet = EVCSetPar(self, "lowerlimit", fVal,pCon); if (!iRet) { *ret=1; goto error; } } sprintf(result, "%f", fVal); } else if (0==strcmp(cmd, "persmode")) { if (argc > 2) { me->persmode = atoi(argv[2]); } sprintf(result, "%d", me->persmode); } else if (0==strcmp(cmd, "verbose")) { if (argc > 2) { me->verbose = atoi(argv[2]); } sprintf(result, "%d", me->verbose); } else if (0==strcmp(cmd, "list")) { IpsStatus(me, ret); if (*ret) goto error; IpsCheckField(me, self->pName, ret); if (*ret) goto error; IpsSay(me, 0, "%s.%s = %.0f", self->pName, "access", ObVal(self->pParam, ACCESS)); IpsSay(me, 0, "%s.%s = %.0f", self->pName, "ErrorHandler", ObVal(self->pParam, ERRORHANDLER)); IpsSay(me, 0, "%s.%s = %.0f", self->pName, "interrupt", ObVal(self->pParam, INTERRUPT)); IpsSay(me, 0, "%s.%s = %d", self->pName, "persmode", me->persmode); IpsSay(me, 0, "%s.%s = %d", self->pName, "perswitch", me->perswitch); IpsSay(me, 0, "%s.%s = %d", self->pName, "verbose", me->verbose); fVal=ObVal(self->pParam, UPLIMIT); fVal2=ObVal(self->pParam, LOWLIMIT); if (-fVal2 > fVal) fVal=fVal2; IpsSay(me, 0, "%s.%s = %f", self->pName, "limit", fVal); IpsSay(me, 0, "%s.%s = %f", self->pName, "ramp", me->ramp); IpsSay(me, 0, "%s.%s = %f", self->pName, "CurrentValue", me->field); IpsSay(me, 0, "%s.%s = %f", self->pName, "TargetValue", self->fTarget); IpsSay(me, 0, "%s.driver = %s", self->pName, self->driverName); if (self->errorScript != NULL) { IpsSay(me, 0, "%s.errorScript = %s", self->pName, self->errorScript); } else { IpsSay(me, 0, "%s.errorScript = UNDEFINED", self->pName); } result[0]='\0'; } else if (0==strcmp(cmd, "confirm")) { if (argc > 2) { if (argc > 3) { IpsSay(me, 0, "Too many arguments"); goto error; } fVal=atof(argv[2]); if (fVal < ObVal(self->pParam, LOWLIMIT) || fVal > ObVal(self->pParam, UPLIMIT)) { IpsSay(me, 0, "Field outside limit"); goto error; } res=IpsStatus(me, ret); if (*ret) goto error; if (NULL!=strstr(res, "H1")) { pf=IpsRead(me, "R7", 3, ret); /* read actual field */ if (*ret) goto error; IpsSay(me, 0, "switch heater is on - field is %f", pf); goto error; } pf=IpsRead(me, "R18", 3, ret); /* read persistant field */ if (*ret) goto error; if (fabs(fVal - me->forcedfield) > 1e-5) { me->force=0; } if (me->force==0) { if (fabs(fVal - pf) > 1e-5 && fabs(fVal - me->field) > 1e-5) { IpsSay(me, 0, "Be aware that this does neither match the stored" " field in sics\nnor the field stored in power supply."); } IpsSay(me, 0, "Please repeat this command, to confirm again" " the persistent field of\n %f Tesla.", fVal); me->force=1; result[0]='\0'; } else if (me->force=1) { me->force=2; me->field=fVal; sprintf(result, "%f", me->field); } me->forcedfield = fVal; } else { IpsStatus(me, ret); if (*ret) goto error; IpsCheckField(me, self->pName, ret); if (*ret) goto error; sprintf(result, "%f", me->field); } } else { return EVControlWrapper(pCon,pSics,pData,argc,argv); } if (result[0] != '\0') { IpsSay(me, 0, "%s.%s = %s", self->pName, argv[1], result); } error: if (me->msg[0] != '\0') { SCWrite(pCon, me->msg, eWarning); me->msg[0]='\0'; } if (*ret) { return 0; } else { return 1; } } /*----------------------------------------------------------------------------*/ static int IpsGet(pEVDriver self, float *fPos) { IpsDriv *me = NULL; int *ret; time_t now; float fld; assert(self); me = (IpsDriv *)self->pPrivate; assert(me); ret=&me->errCode; IpsStatus(me, ret); if (!*ret) { IpsCheckField(me, "magfield", ret); } *fPos=me->field; if(*ret < 0) { return 0; } return 1; } /*----------------------------------------------------------------------------*/ static int IpsRun(pEVDriver self, float fVal) { IpsDriv *me; int *ret; assert(self); me = (IpsDriv *)self->pPrivate; assert(me); ret=&me->errCode; IpsStatus(me, ret); if (*ret) return 0; me->state=IPS_START; return 1; } /*--------------------------------------------------------------------------*/ int IpsError(pEVDriver self, int *iCode, char *error, int iErrLen) { IpsDriv *me = NULL; assert(self); me = (IpsDriv *)self->pPrivate; assert(me); *iCode = 1; /* severe */ if (me->msg[0]=='\0') { strncpy(error, "undefined error", iErrLen); } else { strncpy(error, me->msg, iErrLen); } error[iErrLen-1]='\0'; return 1; } /*---------------------------------------------------------------------------*/ static int IpsFix(pEVDriver self, int iError) { IpsDriv *me = NULL; int iRet; assert(self); me = (IpsDriv *)self->pPrivate; assert(me); return(DEVFAULT); /* severe */ /* never used: return(DEVREDO); */ } /*--------------------------------------------------------------------------*/ static int IpsSend(pEVDriver self, char *pCommand, char *pReply, int iLen) { IpsDriv *me = NULL; int iRet; assert(self); me = (IpsDriv *)self->pPrivate; assert(me); iRet = transactRS232(me->ser, pCommand, strlen(pCommand), pReply, iLen); if(iRet < 0) { me->errCode = iRet; IpsWriteError(me); return 0; } return 1; } /*--------------------------------------------------------------------------*/ static int IpsInit(pEVDriver self) { IpsDriv *me = NULL; int iRet; char *res, buf[128]; pExeList exe; SConnection *con; assert(self); me = (IpsDriv *)self->pPrivate; assert(me); iRet = initRS232(me->ser); if (iRet != 1) { me->errCode = iRet; IpsWriteError(me); return 0; } setRS232ReplyTerminator(me->ser,"\r"); setRS232SendTerminator(me->ser,"\r"); setRS232Timeout(me->ser,1000); /* microseconds ? milliseconds ? */ setRS232Debug(me->ser,0); iRet=transactRS232(me->ser, "V", 1, buf, sizeof(buf)); if (iRet != 1) { if (iRet == 0) { me->errCode = 1; IpsSay(me, 0, "IPS: unknown RS232 error"); } else { me->errCode = iRet; IpsWriteError(me); } return 0; } if (0==strncmp(buf, "IPS120", 6)) { me->isPS=0; } else if (0==strncmp(buf, "PS120", 5)) { me->isPS=1; } else { IpsSay(me, 0, "unknown power supply version: %s", buf); return 0; } IpsStatus(me, &iRet); me->msg[0]='\0'; IpsCheckField(me, "magfield", &iRet); me->state = IPS_IDLE; return 1; } /*--------------------------------------------------------------------------*/ static int IpsClose(pEVDriver self) { /* seems not to be needed IpsDriv *me = NULL; int iRet; assert(self); me = (IpsDriv *)self->pPrivate; assert(me); me->pData=NULL; */ return 1; } /*--------------------------------------------------------------------------*/ static int IpsHalt(pEVDriver *self) { assert(self); /* to be implemented */ return 1; } /*------------------------------------------------------------------------*/ void IpsKill(void *pData) { IpsDriv *me = NULL; me = (IpsDriv *)pData; assert(me); KillRS232(me->ser); free(me); } /*------------------------------------------------------------------------*/ pEVDriver CreateIpsDriver(int argc, char *argv[]) { pEVDriver new = NULL; IpsDriv *me = NULL; int port; if (argc < 2) { return NULL; } new = CreateEVDriver(argc,argv); me = (IpsDriv *)malloc(sizeof(IpsDriv)); memset(me,0,sizeof(IpsDriv)); if(!new || !me) return NULL; new->pPrivate = me; new->KillPrivate = IpsKill; /* initalise IpsDriver */ /* me->lastError = NULL; */ me->verbose = 0; me->field = 0; me->persmode = 1; me->perswitch = 0; me->force = 0; me->tim = 0; me->settle = 1; me->state=IPS_INIT; me->ramp=0.1; me->isPS=0; port=atoi(argv[1]); if (port==0) return NULL; me->ser=createRS232(argv[0], port); if (me->ser == NULL) return NULL; /* initialise function pointers */ new->SetValue = IpsRun; new->GetValue = IpsGet; new->Send = IpsSend; new->GetError = IpsError; new->TryFixIt = IpsFix; new->Init = IpsInit; new->Close = IpsClose; return new; } /*------------------------------------------------------------------------*/ static int IpsWatcher(void *data) { pEVControl self; pExeList exe; SConnection *con; int iRet; assert(data); self= (pEVControl)data; exe = GetExecutor(); con = GetExeOwner(exe); iRet = IpsStepper(self, con); return 1; } /*------------------------------------------------------------------------*/ void IpsCustomize(SConnection *pCon, pEVControl pEvc) { /* customize tecs driver */ IpsDriv *me; me=pEvc->pDriv->pPrivate; assert(me); pEvc->pDrivInt->CheckStatus=IpsStepper; pEvc->pEnvir->IsInTolerance=IpsWatcher; EVCSetPar(pEvc,"upperlimit",14.9,pCon); EVCSetPar(pEvc,"lowerlimit",-14.9,pCon); if (me->msg[0]!='\0') { /* write message from IpsInit */ SCWrite(pCon, me->msg, eWarning); } }