- added is_running to driveable ease objects - ighdriv.c: try <maxtry> times when status reply failes - ipsdriv.c: fix trainedTo parameter - reduced SEA version of SICS
1030 lines
29 KiB
C
1030 lines
29 KiB
C
/*---------------------------------------------------------------------------
|
|
ips2driv.c
|
|
|
|
Driver for the Oxford Instruments IPS/PS magnet power supply.
|
|
Version 2 (based on ease).
|
|
|
|
Markus Zolliker, May 2005
|
|
----------------------------------------------------------------------------*/
|
|
#include <string.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <assert.h>
|
|
#include <sys/time.h>
|
|
#include <math.h>
|
|
#include <tcl.h>
|
|
#include <fortify.h>
|
|
#include <sics.h>
|
|
#include <splitter.h>
|
|
#include <obpar.h>
|
|
#include <devexec.h>
|
|
#include <nserver.h>
|
|
#include <interrupt.h>
|
|
#include <emon.h>
|
|
#include <evcontroller.h>
|
|
#include <evcontroller.i>
|
|
#include <sicsvar.h>
|
|
#include <evdriver.i>
|
|
#include <rs232controller.h>
|
|
#include "oxinst.h"
|
|
#include "fsm.h"
|
|
#include "initializer.h"
|
|
|
|
typedef enum {NOTHING, SWITCH_OFF, RAMP_DOWN,
|
|
RAMP_ALMOST, RAMP_ZERO, RAMPED_DOWN} DoThis;
|
|
|
|
typedef struct {
|
|
EaseDriv d;
|
|
float current; /* current (in Tesla) */
|
|
float persfield; /* persistent field from IPS (in Tesla) */
|
|
float lastfield; /* persistent field from last drive */
|
|
float confirmfield; /* field confirmed in 1st step */
|
|
float ramp; /* actual ramp rate (Tesla/min) */
|
|
float maxlimit; /* hard field limit */
|
|
float perscurrent; /* current for persistent mode workaround (Tesla) */
|
|
float perslimit; /* field limit for activating persistent mode workaround (Tesla) */
|
|
float voltage; /* voltage */
|
|
float measured; /* measured current */
|
|
float inductance; /* induction (read only on startup) */
|
|
float ampRamp; /* ramp in amps (read only on startup) */
|
|
float trainedTo; /* use training mode when the field is higher than this value (slow ramp limits) */
|
|
char *startScript; /* script to be called on startup */
|
|
int persmode; /* 0: delayed persistant mode, 1: go to persistant mode, 2: leave switch on */
|
|
int persdelay; /* wait time for delayed persistant mode */
|
|
int perswitch; /* state of switch */
|
|
int remote; /* 0: local, 1: remote, do not check, 2: remote, check */
|
|
int nowait; /* 0: normal, 1: drive finishes immediately, ramp in background */
|
|
int perswait; /* wait time for persistent mode workaround (sec) */
|
|
int heaterFault;
|
|
int trainMode; /* 0/1 read back for train mode, 2/3 command for trainMode */
|
|
int external; /* 0: normal,
|
|
1: go to external (script controlled) mode
|
|
2: external mode
|
|
3: go back to internal mode */
|
|
DoThis dothis;
|
|
char *fmt; /* fmt for field */
|
|
int force; /* force = 2: put heater switch even when stored field does not match */
|
|
time_t swtim; /* time when last switching the heater */
|
|
time_t tim; /* outside IpsChangeField: time of last Field Change */
|
|
} Ips;
|
|
|
|
static ParClass ipsClass = { "IPS", sizeof(Ips) };
|
|
static char *onOff[] = { "off", "on", NULL };
|
|
static char *persMode[] = { "auto", "on", "off", NULL };
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
static int IpsOk(Ips * drv)
|
|
{
|
|
float dif;
|
|
|
|
if (drv->d.b.msg[0] != '\0')
|
|
return 1; /* connection not yet confirmed */
|
|
if (drv->perswitch)
|
|
return 1;
|
|
if (drv->lastfield == PAR_NAN) {
|
|
drv->lastfield = drv->persfield;
|
|
}
|
|
if (fabs(drv->persfield - drv->lastfield) < 1e-2) {
|
|
drv->lastfield = drv->persfield;
|
|
return 1;
|
|
}
|
|
if (drv->force != 0)
|
|
return 1;
|
|
ParPrintf(drv, eWarning,
|
|
"\nit is not sure which field is in the magnet\n"
|
|
"value stored in power supply: %f\n"
|
|
" in software: %f\n"
|
|
"use command\n \n %s confirm ...\n \n"
|
|
"to specify the persistent field\n \n", drv->persfield,
|
|
drv->lastfield, drv->d.b.p.name);
|
|
drv->force = 0;
|
|
return 0;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
static int IpsConfirm(void *object, void *userarg, int argc, char *argv[])
|
|
{
|
|
Ips *drv = ParCast(&ipsClass, object);
|
|
float fld;
|
|
|
|
assert(drv);
|
|
if (argc > 0) {
|
|
if (argc > 1) {
|
|
ParPrintf(object, eError, "Too many arguments");
|
|
return 0;
|
|
}
|
|
fld = atof(argv[0]);
|
|
if (fld > drv->d.upperLimit || fld < drv->d.lowerLimit) {
|
|
ParPrintf(object, eError, "Field outside limit");
|
|
return 0;
|
|
}
|
|
if (drv->perswitch) {
|
|
ParPrintf(object, eWarning, "switch heater is on - field is %f",
|
|
drv->current);
|
|
return 0;
|
|
}
|
|
if (fabs(fld - drv->persfield) > 1e-5
|
|
&& fabs(fld - drv->lastfield) > 1e-5) {
|
|
ParPrintf(object, eWarning,
|
|
"Be aware that this does neither match the field"
|
|
" stored in software\nnor the field stored in power supply.");
|
|
}
|
|
if (drv->force && fld != drv->confirmfield)
|
|
drv->force = 0;
|
|
if (drv->force == 0) {
|
|
ParPrintf(object, eWarning,
|
|
"Please repeat this command, to confirm again"
|
|
" the persistent field of\n %f Tesla.", fld);
|
|
drv->confirmfield = fld;
|
|
drv->force = 1;
|
|
} else {
|
|
drv->force = 2;
|
|
drv->lastfield = fld;
|
|
EaseParHasChanged();
|
|
ParPrintf(object, eValue, "%s confirm = %f", drv->d.b.p.name, fld);
|
|
}
|
|
} else {
|
|
ParPrintf(object, eValue, "%s lastfield = %f", drv->d.b.p.name,
|
|
drv->lastfield);
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
void IpsParDef(void *object)
|
|
{
|
|
Ips *drv = ParCast(&ipsClass, object);
|
|
|
|
ParName("");
|
|
ParFmt(drv->fmt);
|
|
ParTail("Tesla");
|
|
ParFloat(&drv->persfield, PAR_NAN);
|
|
|
|
ParName("persmode");
|
|
ParAccess(usUser);
|
|
ParEnum(persMode);
|
|
ParList(0);
|
|
ParInt(&drv->persmode, 1);
|
|
|
|
ParName("perswitch");
|
|
ParEnum(onOff);
|
|
ParList(0);
|
|
ParInt(&drv->perswitch, PAR_NAN);
|
|
|
|
ParName("nowait");
|
|
ParAccess(usUser);
|
|
ParEnum(onOff);
|
|
ParList(0);
|
|
ParInt(&drv->nowait, 0);
|
|
|
|
ParName("maxlimit");
|
|
ParSave(1);
|
|
ParFloat(&drv->maxlimit, 0.0);
|
|
|
|
ParName("limit");
|
|
ParAccess(usUser);
|
|
ParFmt(drv->fmt);
|
|
ParTail("Tesla");
|
|
ParFloat(&drv->d.upperLimit, 0.0);
|
|
if (ParActionIs(PAR_SET) > 0) {
|
|
if (drv->maxlimit == 0) { /* first time: set maxlimit */
|
|
drv->maxlimit = drv->d.upperLimit;
|
|
} else if (drv->d.upperLimit > drv->maxlimit) {
|
|
drv->d.upperLimit = drv->maxlimit;
|
|
ParPrintf(drv, eWarning, "limit is too high, set back to %.5g\n",
|
|
drv->maxlimit);
|
|
}
|
|
if (drv->d.b.syntax) { /* IPS */
|
|
drv->d.lowerLimit = -drv->d.upperLimit;
|
|
} else { /* PS */
|
|
drv->d.lowerLimit = 0;
|
|
}
|
|
}
|
|
|
|
ParName("ramp");
|
|
ParAccess(usUser);
|
|
ParFmt(drv->fmt);
|
|
ParTail("Tesla/min");
|
|
ParFloat(&drv->ramp, 1.0);
|
|
|
|
ParName("perscurrent");
|
|
ParAccess(usUser);
|
|
ParFmt(drv->fmt);
|
|
ParTail("Tesla");
|
|
ParFloat(&drv->perscurrent, 0.0);
|
|
|
|
ParName("perslimit");
|
|
ParAccess(usUser);
|
|
ParFmt(drv->fmt);
|
|
ParTail("Tesla");
|
|
ParFloat(&drv->perslimit, 10.0);
|
|
|
|
ParName("perswait");
|
|
ParAccess(usUser);
|
|
ParTail("sec");
|
|
ParInt(&drv->perswait, 1800);
|
|
|
|
ParName("persdelay");
|
|
ParAccess(usUser);
|
|
ParTail("sec");
|
|
ParInt(&drv->persdelay, 1800);
|
|
|
|
ParName("current");
|
|
ParFmt(drv->fmt);
|
|
ParTail("Tesla equivalent");
|
|
ParFloat(&drv->current, PAR_NAN);
|
|
|
|
ParName("measured");
|
|
ParFmt(drv->fmt);
|
|
ParTail("Amps");
|
|
ParFloat(&drv->measured, PAR_NAN);
|
|
|
|
ParName("voltage");
|
|
ParFmt(drv->fmt);
|
|
ParTail("Volt");
|
|
ParFloat(&drv->voltage, PAR_NAN);
|
|
|
|
ParName("lastfield");
|
|
ParSave(1);
|
|
ParFloat(&drv->lastfield, PAR_NAN);
|
|
|
|
ParName("ampRamp");
|
|
ParFmt("%g");
|
|
ParTail("A");
|
|
ParList("");
|
|
ParFloat(&drv->ampRamp, 0.0);
|
|
|
|
ParName("inductance");
|
|
ParFmt("%g");
|
|
ParTail("Henry");
|
|
ParList("");
|
|
ParFloat(&drv->inductance, 0.0);
|
|
|
|
ParName("trainedTo");
|
|
ParAccess(usUser);
|
|
ParFmt(drv->fmt);
|
|
ParTail("Tesla");
|
|
ParFloat(&drv->trainedTo, 0.0);
|
|
|
|
ParName("trainMode");
|
|
ParEnum(onOff);
|
|
ParList(0);
|
|
ParInt(&drv->trainMode, PAR_NAN);
|
|
|
|
ParName("external");
|
|
ParAccess(usUser);
|
|
ParEnum(onOff);
|
|
ParList(0);
|
|
ParInt(&drv->external, 0);
|
|
|
|
ParName("startScript");
|
|
ParAccess(usUser);
|
|
ParList("");
|
|
ParSave(1);
|
|
ParStr(&drv->startScript, "0");
|
|
|
|
ParName("confirm");
|
|
ParCmd(IpsConfirm, NULL);
|
|
|
|
EaseBasePar(drv);
|
|
EaseRunPar(drv);
|
|
EaseSendPar(drv);
|
|
ParStdDef();
|
|
|
|
ParName("targetValue");
|
|
ParFmt(drv->fmt);
|
|
ParTail("Tesla");
|
|
ParFloat(&drv->d.targetValue, PAR_NAN);
|
|
|
|
if (ParActionIs(PAR_LIST) || ParActionIs(PAR_SET)
|
|
|| ParActionIs(PAR_SHOW)) {
|
|
IpsOk(drv);
|
|
}
|
|
if (ParActionIs(PAR_KILL)) {
|
|
EaseKillDriv(&drv->d);
|
|
}
|
|
EaseMsgPar(drv);
|
|
}
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
static void IpsStatus(Ips * drv)
|
|
{
|
|
char *ans;
|
|
int *code;
|
|
int swi;
|
|
char *errmsg;
|
|
|
|
if (drv->d.b.state != EASE_read)
|
|
return;
|
|
ans = drv->d.b.ans;
|
|
code = &drv->d.b.errCode;
|
|
if (ans[0] != 'X' ||
|
|
ans[3] != 'A' || ans[5] != 'C' || ans[7] != 'H' || ans[9] != 'M') {
|
|
ParPrintf(drv, eError, "illegal status response");
|
|
*code = EASE_FAULT;
|
|
return;
|
|
}
|
|
switch (ans[1]) {
|
|
case '0':
|
|
if (ans[6] > '3') {
|
|
errmsg = "ERROR: auto-run-down (He level is/was low - press SILENCE on ILM after filling)";
|
|
} else {
|
|
errmsg = "";
|
|
}
|
|
break;
|
|
case '1':
|
|
errmsg = "ERROR: magnet quenched";
|
|
drv->lastfield = PAR_NAN;
|
|
break;
|
|
case '2':
|
|
errmsg = "ERROR: IPS overheated";
|
|
break;
|
|
case '4':
|
|
errmsg = "ERROR: IPS warming up";
|
|
break;
|
|
case '8':
|
|
errmsg = "ERROR: IPS fault";
|
|
*code = EASE_FAULT;
|
|
break;
|
|
default:
|
|
errmsg = "ERROR: illegal status response";
|
|
}
|
|
if (errmsg[0] != '\0') {
|
|
if (strcmp(errmsg, drv->d.b.msg) != 0) {
|
|
snprintf(drv->d.b.msg, sizeof drv->d.b.msg, "%s", errmsg);
|
|
ParPrintf(drv, eError, "%s", errmsg);
|
|
}
|
|
*code = EASE_FAULT;
|
|
return;
|
|
} else {
|
|
drv->d.b.msg[0]='\0'; /* clear status */
|
|
}
|
|
if (ans[6] != '3') {
|
|
if (drv->remote == 2) { /* remote state monitoring local switch */
|
|
ParPrintf(drv, eError, "IPS switched to local");
|
|
*code = EASE_FAULT;
|
|
drv->remote = 3; /* signal to switch back to C0 local locked */
|
|
return;
|
|
}
|
|
if (ans[6] == '1') {
|
|
drv->remote = 3; /* signal to switch back to C0 local locked */
|
|
}
|
|
if (drv->d.hwstate == HWBusy && drv->d.b.doit == NULL) {
|
|
EaseUpdateHwstate(drv, HWIdle);
|
|
drv->d.stopped = 0;
|
|
}
|
|
}
|
|
if (ans[8] == '5') {
|
|
drv->heaterFault = 1;
|
|
return;
|
|
}
|
|
drv->trainMode = (ans[10] >= '4');
|
|
drv->heaterFault = 0;
|
|
if (ans[8] == '1') {
|
|
swi = 1;
|
|
} else {
|
|
swi = 0;
|
|
}
|
|
if (swi != drv->perswitch || drv->swtim == 0) {
|
|
drv->swtim = time(NULL);
|
|
drv->perswitch = swi;
|
|
if (drv->perswitch && drv->dothis == RAMP_ALMOST) {
|
|
drv->dothis = NOTHING;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
static void IpsSetField(Ips * drv, float val)
|
|
{
|
|
ParLog(drv);
|
|
if (drv->perswitch) {
|
|
drv->current = val;
|
|
drv->lastfield = val;
|
|
drv->persfield = val;
|
|
EaseParHasChanged();
|
|
} else {
|
|
drv->persfield = val;
|
|
}
|
|
}
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
static long IpsRead(long pc, void *object)
|
|
{
|
|
Ips *drv = ParCast(&ipsClass, object);
|
|
EaseBase *eab = object;
|
|
time_t expire, now;
|
|
|
|
switch (pc) {
|
|
default: /* FSM BEGIN ****************************** */
|
|
if (eab->state == EASE_abort) {
|
|
eab->state = EASE_idle;
|
|
}
|
|
EasePchk(drv);
|
|
if (drv->external == 2) return 0; /* external mode */
|
|
if (drv->external == 3) { /* switch back to internal */
|
|
drv->external = 0;
|
|
goto localLocked;
|
|
}
|
|
if (drv->external != 1) goto getstatus;
|
|
EaseWrite(eab, "C3");
|
|
return __LINE__;
|
|
case __LINE__: /**********************************/
|
|
drv->external = 2;
|
|
return 0;
|
|
|
|
getstatus:
|
|
EaseWrite(eab, "X");
|
|
return __LINE__;
|
|
case __LINE__: /**********************************/
|
|
IpsStatus(drv);
|
|
|
|
EaseWrite(eab, "R7"); /* read current (in Tesla) */
|
|
return __LINE__;
|
|
case __LINE__: /**********************************/
|
|
drv->current = OxiGet(eab, 3, NULL, drv->current);
|
|
|
|
EaseWrite(eab, "R2"); /* read measured current (in amps) */
|
|
return __LINE__;
|
|
case __LINE__: /**********************************/
|
|
drv->measured = OxiGet(eab, 3, NULL, drv->measured);
|
|
|
|
EaseWrite(eab, "R1"); /* read measured voltage */
|
|
return __LINE__;
|
|
case __LINE__: /**********************************/
|
|
drv->voltage = OxiGet(eab, 3, NULL, drv->voltage);
|
|
|
|
if (drv->perswitch) {
|
|
IpsSetField(drv, drv->current);
|
|
goto checktodo;
|
|
}
|
|
EaseWrite(eab, "R18"); /* read persistant field (in Tesla) */
|
|
return __LINE__;
|
|
case __LINE__: /**********************************/
|
|
IpsSetField(drv, OxiGet(eab, 3, NULL, drv->persfield));
|
|
|
|
checktodo:
|
|
if (eab->msg[0] != '\0') goto quit; /* no action when in error */
|
|
now = time(NULL);
|
|
if (drv->persmode >= 2) { /* persistent mode off */
|
|
drv->dothis = NOTHING;
|
|
goto nothing;
|
|
} else {
|
|
if (drv->perswitch) {
|
|
if ((drv->persmode == 1 && now > drv->tim + 10) || now > drv->tim + drv->persdelay) {
|
|
drv->dothis = SWITCH_OFF;
|
|
} else {
|
|
drv->dothis = NOTHING;
|
|
goto nothing;
|
|
}
|
|
} else if (drv->dothis == RAMP_ALMOST) {
|
|
if (now > drv->tim + drv->perswait) {
|
|
drv->dothis = RAMP_ZERO;
|
|
} else {
|
|
goto nothing;
|
|
}
|
|
} else if (drv->dothis == SWITCH_OFF) {
|
|
if (now > drv->swtim + 30) {
|
|
drv->dothis = RAMP_DOWN;
|
|
} else {
|
|
goto nothing;
|
|
}
|
|
} else {
|
|
goto nothing;
|
|
}
|
|
}
|
|
|
|
EaseWrite(eab, "C3");
|
|
drv->remote = 1; /* remote state */
|
|
return __LINE__;
|
|
case __LINE__: /**********************************/
|
|
|
|
switch (drv->dothis) {
|
|
case SWITCH_OFF: goto switch_off;
|
|
case RAMP_DOWN: goto ramp_down;
|
|
case RAMP_ZERO: goto ramp_zero;
|
|
}
|
|
/* we should never get here */
|
|
goto quit;
|
|
|
|
switch_off:
|
|
EaseWrite(eab, "A0");
|
|
return __LINE__;
|
|
case __LINE__: /**********************************/
|
|
EaseWrite(eab, "H0");
|
|
drv->perswitch = 0;
|
|
drv->swtim = time(NULL);
|
|
drv->lastfield = drv->current;
|
|
EaseParHasChanged();
|
|
return __LINE__;
|
|
case __LINE__: /**********************************/
|
|
ParPrintf(drv, -1, "IPS: go to persistent mode, wait 30 sec for switch");
|
|
goto localLocked;
|
|
|
|
ramp_down:
|
|
if (drv->current == 0)
|
|
goto localLocked;
|
|
if (drv->perscurrent == 0 || drv->persfield < drv->perslimit) {
|
|
drv->dothis = RAMP_ZERO;
|
|
goto ramp_zero;
|
|
}
|
|
|
|
OxiSet(eab, "J", drv->perscurrent, 3); /* put set point to a low value */
|
|
return __LINE__;
|
|
case __LINE__:
|
|
EaseWrite(eab, "A1"); /* goto set (almost zero) */
|
|
ParPrintf(drv, -1, "IPS: ramp current to %f (almost zero)", drv->perscurrent);
|
|
return __LINE__;
|
|
case __LINE__: /**********************************/
|
|
drv->tim = time(NULL);
|
|
drv->dothis = RAMP_ALMOST;
|
|
goto localLocked;
|
|
|
|
ramp_zero:
|
|
EaseWrite(eab, "A2"); /* goto zero */
|
|
ParPrintf(drv, -1, "IPS: ramp current to 0");
|
|
drv->dothis = RAMPED_DOWN;
|
|
return __LINE__;
|
|
case __LINE__: /**********************************/
|
|
goto localLocked;
|
|
|
|
nothing:
|
|
if (drv->remote != 3) /* NOT signal to switch back to local locked */
|
|
goto quit;
|
|
|
|
localLocked:
|
|
EaseWrite(eab, "C0");
|
|
drv->remote = 0; /* local state */
|
|
return __LINE__;
|
|
case __LINE__: /**********************************/
|
|
|
|
quit:
|
|
ParLog(drv);
|
|
return 0;
|
|
} /* FSM END ******************************************* */
|
|
|
|
}
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
static long IpsMeas(long pc, void *object)
|
|
{
|
|
Ips *drv = ParCast(&ipsClass, object);
|
|
EaseBase *eab = object;
|
|
|
|
switch (pc) {
|
|
default: /* FSM BEGIN ****************************** */
|
|
EasePchk(drv);
|
|
EaseWrite(eab, "X");
|
|
return __LINE__;
|
|
case __LINE__: /**********************************/
|
|
IpsStatus(drv);
|
|
|
|
EaseWrite(eab, "R2"); /* read measured current (in amps) */
|
|
return __LINE__;
|
|
case __LINE__: /**********************************/
|
|
drv->measured = OxiGet(eab, 3, NULL, drv->measured);
|
|
|
|
EaseWrite(eab, "R1"); /* read measured voltage */
|
|
return __LINE__;
|
|
case __LINE__: /**********************************/
|
|
drv->voltage = OxiGet(eab, 3, NULL, drv->voltage);
|
|
|
|
quit:
|
|
ParLog(drv);
|
|
return 0;
|
|
} /* FSM END ******************************************* */
|
|
|
|
}
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
static long IpsStart(long pc, void *object)
|
|
{
|
|
Ips *drv = ParCast(&ipsClass, object);
|
|
EaseBase *eab = object;
|
|
float value;
|
|
Tcl_Interp *pTcl = NULL;
|
|
int iRet;
|
|
char msg[256];
|
|
char *script;
|
|
|
|
switch (pc) {
|
|
default: /* FSM BEGIN ****************************** */
|
|
EaseWrite(eab, "V\rQ4"); /* get version and switch to ext. resolution */
|
|
return __LINE__;
|
|
case __LINE__: /**********************************/
|
|
if (0 == strncmp(eab->version, "IPS120", 6)) {
|
|
eab->syntax = 1;
|
|
drv->d.lowerLimit = -drv->d.upperLimit;
|
|
} else if (0 == strncmp(eab->version, "PS", 2)) {
|
|
eab->syntax = 0;
|
|
drv->d.lowerLimit = 0;
|
|
} else {
|
|
snprintf(msg, sizeof msg,
|
|
"unknown power supply version: %s", eab->version);
|
|
EaseStop(eab, msg);
|
|
goto quit;
|
|
}
|
|
ParPrintf(drv, eLog, "connected to %s", eab->version);
|
|
if (eab->syntax) {
|
|
drv->fmt = "%.4f";
|
|
} else {
|
|
drv->fmt = "%.3f";
|
|
}
|
|
drv->tim = time(NULL);
|
|
FsmCall(IpsRead);
|
|
return __LINE__;
|
|
case __LINE__: /**********************************/
|
|
drv->d.targetValue = drv->persfield;
|
|
|
|
if (eab->syntax == 0) goto quit;
|
|
|
|
EaseWrite(eab, "R24");
|
|
return __LINE__;
|
|
case __LINE__:
|
|
drv->inductance = OxiGet(eab, 1, NULL, 0.0);
|
|
|
|
if (drv->ramp == 0) {
|
|
goto ramp_read;
|
|
}
|
|
|
|
EaseWrite(eab, "R9");
|
|
return __LINE__;
|
|
case __LINE__:
|
|
value = OxiGet(eab, 3, NULL, drv->ramp);
|
|
if (fabs(value - drv->ramp) == 0) goto ramp_ok;
|
|
|
|
EaseWrite(eab, "C3");
|
|
drv->remote = 1; /* remote state */
|
|
return __LINE__;
|
|
case __LINE__: /**********************************/
|
|
|
|
OxiSet(eab, "T", drv->ramp, 3);
|
|
return __LINE__;
|
|
case __LINE__: /**********************************/
|
|
|
|
EaseWrite(eab, "C0");
|
|
drv->remote = 0; /* local state */
|
|
return __LINE__;
|
|
case __LINE__: /**********************************/
|
|
|
|
ramp_read:
|
|
EaseWrite(eab, "R9");
|
|
return __LINE__;
|
|
case __LINE__:
|
|
drv->ramp = OxiGet(eab, 3, NULL, drv->ramp);
|
|
|
|
ramp_ok:
|
|
|
|
EaseWrite(eab, "R6");
|
|
return __LINE__;
|
|
case __LINE__:
|
|
drv->ampRamp = OxiGet(eab, 1, NULL, 0.0);
|
|
|
|
if (drv->startScript && drv->startScript[0] != '\0'
|
|
&& 0 != strcmp(drv->startScript, "0")) {
|
|
pTcl = InterpGetTcl(pServ->pSics);
|
|
|
|
script = strdup(drv->startScript);
|
|
iRet = Tcl_Eval(pTcl, script);
|
|
free(script);
|
|
if (iRet != TCL_OK) {
|
|
snprintf(msg, sizeof msg, "%s", pTcl->result);
|
|
EaseStop(eab, msg);
|
|
goto quit;
|
|
}
|
|
eab->msg[0]='\0';
|
|
}
|
|
|
|
quit:
|
|
return 0;
|
|
} /* FSM END ******************************************* */
|
|
|
|
}
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
static long IpsChangeField(long pc, void *object)
|
|
{
|
|
Ips *drv = ParCast(&ipsClass, object);
|
|
EaseBase *eab = object;
|
|
float fld;
|
|
float step;
|
|
float ramp;
|
|
float eps;
|
|
time_t delay;
|
|
|
|
switch (pc) {
|
|
default: /* FSM BEGIN ****************************** */
|
|
|
|
if (eab->syntax == 0) {
|
|
EaseWrite(eab, "C3"); /* set remote */
|
|
} else {
|
|
EaseWrite(eab, "C3\rQ4"); /* set remote and repeat extended resolution (in case of power failure) */
|
|
}
|
|
drv->remote = 1; /* remote state */
|
|
return __LINE__;
|
|
case __LINE__: /**********************************/
|
|
if (!EaseGetUpdate(drv, EASE_RUN)) {
|
|
/* an other flag than EASE_RUN was set -> should not be the case */
|
|
return 0;
|
|
}
|
|
EaseSetUpdate(eab, EASE_RUN, 0);
|
|
if (drv->nowait) {
|
|
EaseUpdateHwstate(drv, HWIdle);
|
|
drv->d.eMode = EVMonitor; /* finish drive, continue in background */
|
|
}
|
|
EaseWrite(eab, "F7"); /* switch to tesla on display */
|
|
return __LINE__;
|
|
case __LINE__: /**********************************/
|
|
|
|
EaseWrite(eab, "A0"); /* hold */
|
|
return __LINE__;
|
|
case __LINE__: /**********************************/
|
|
|
|
EaseWrite(eab, "R7"); /* read current (in Tesla) */
|
|
return __LINE__;
|
|
case __LINE__: /**********************************/
|
|
drv->current = OxiGet(eab, 3, NULL, drv->current);
|
|
|
|
if (drv->perswitch) {
|
|
IpsSetField(drv, drv->current);
|
|
goto skipR18;
|
|
}
|
|
|
|
EaseWrite(eab, "R18"); /* read persistant field (in Tesla) */
|
|
return __LINE__;
|
|
case __LINE__: /**********************************/
|
|
fld = OxiGet(eab, 3, NULL, drv->persfield);
|
|
IpsSetField(drv, fld);
|
|
|
|
skipR18:
|
|
FsmCall(IpsMeas);
|
|
return __LINE__;
|
|
case __LINE__: /**********************************/
|
|
drv->remote = 2; /* remote state monitoring local switch */
|
|
if (!IpsOk(drv))
|
|
goto finish;
|
|
if (drv->lastfield == PAR_NAN) {
|
|
drv->lastfield = drv->persfield;
|
|
}
|
|
if (fabs(drv->d.targetValue - drv->lastfield) < 0.5e-5) {
|
|
ParPrintf(drv, -1, "IPS: we are already at field %f",
|
|
drv->lastfield);
|
|
if (drv->persmode == 1) {
|
|
if (!drv->perswitch)
|
|
goto finish;
|
|
goto target_reached;
|
|
} else {
|
|
if (drv->perswitch)
|
|
goto finish;
|
|
}
|
|
}
|
|
if (fabs(drv->current - drv->lastfield) >= 1.0e-5) {
|
|
goto ramp2setp;
|
|
}
|
|
|
|
if (drv->perswitch)
|
|
goto wait_open;
|
|
if (drv->force == 2) {
|
|
EaseWrite(eab, "H2");
|
|
} else {
|
|
EaseWrite(eab, "H1");
|
|
}
|
|
drv->force = 0;
|
|
return __LINE__;
|
|
case __LINE__: /**********************************/
|
|
if (strcmp(drv->d.b.ans, "H") == 0) {
|
|
goto switched_on;
|
|
}
|
|
ParPrintf(drv, -1, "IPS: switching heater on failed, try again");
|
|
|
|
ramp2setp:
|
|
OxiSet(eab, "J", drv->lastfield, 3); /* set point */
|
|
return __LINE__;
|
|
case __LINE__: /**********************************/
|
|
EaseWrite(eab, "A1");
|
|
ParPrintf(drv, -1, "IPS: ramp to current for %f Tesla",
|
|
drv->lastfield);
|
|
return __LINE__;
|
|
case __LINE__: /**********************************/
|
|
drv->tim = time(NULL);
|
|
|
|
stab1:
|
|
EaseWrite(eab, "R7"); /* read current (in Tesla) */
|
|
return __LINE__;
|
|
case __LINE__: /**********************************/
|
|
drv->current = OxiGet(eab, 3, NULL, drv->current);
|
|
|
|
FsmCall(IpsMeas);
|
|
return __LINE__;
|
|
case __LINE__: /**********************************/
|
|
|
|
if (fabs(drv->current - drv->lastfield) >= 1e-4)
|
|
goto stab1;
|
|
|
|
stab2:
|
|
FsmWait(1);
|
|
return __LINE__;
|
|
case __LINE__: /**********************************/
|
|
|
|
FsmCall(IpsMeas);
|
|
return __LINE__;
|
|
case __LINE__: /**********************************/
|
|
|
|
if (time(NULL) < drv->tim + 10)
|
|
goto stab2; /* stabilize */
|
|
|
|
switch_on:
|
|
if (drv->perswitch)
|
|
goto wait_open;
|
|
if (drv->force == 2) {
|
|
EaseWrite(eab, "H2");
|
|
} else {
|
|
EaseWrite(eab, "H1");
|
|
}
|
|
drv->force = 0;
|
|
return __LINE__;
|
|
case __LINE__: /**********************************/
|
|
if (strcmp(drv->d.b.ans, "?H1") == 0) {
|
|
ParPrintf(drv, eError, "IPS: switching heater on failed");
|
|
eab->errCode = EASE_FAULT;
|
|
goto off_finish;
|
|
}
|
|
switched_on:
|
|
drv->perswitch = 1;
|
|
drv->dothis = NOTHING;
|
|
drv->swtim = time(NULL);
|
|
|
|
wait_open:
|
|
delay = drv->swtim + 15 - time(NULL);
|
|
if (delay > 0)
|
|
ParPrintf(drv, -1, "IPS: wait %d sec to open switch", delay);
|
|
|
|
ParLog(drv);
|
|
start_ramp:
|
|
FsmWait(1);
|
|
drv->tim = time(NULL);
|
|
return __LINE__;
|
|
case __LINE__: /**********************************/
|
|
|
|
FsmCall(IpsMeas);
|
|
return __LINE__;
|
|
case __LINE__: /**********************************/
|
|
|
|
if (drv->heaterFault) {
|
|
if (time(NULL) > drv->swtim + 3) {
|
|
ParPrintf(drv, eError, "IPS: switch heater not connected");
|
|
eab->errCode = EASE_FAULT;
|
|
goto off_finish;
|
|
}
|
|
}
|
|
|
|
if (time(NULL) < drv->swtim + 15)
|
|
goto start_ramp; /* wait */
|
|
OxiSet(eab, "T", drv->ramp, 3);
|
|
return __LINE__;
|
|
case __LINE__: /**********************************/
|
|
OxiSet(eab, "J", drv->current, 3); /* put set point to actual value */
|
|
return __LINE__;
|
|
case __LINE__: /**********************************/
|
|
EaseWrite(eab, "A1"); /* go to setpoint (do not yet run) */
|
|
return __LINE__;
|
|
case __LINE__: /**********************************/
|
|
ParPrintf(drv, -1, "IPS: ramp to %f Tesla", drv->d.targetValue);
|
|
|
|
ramping:
|
|
ParLog(drv);
|
|
FsmWait(1);
|
|
return __LINE__;
|
|
case __LINE__: /**********************************/
|
|
EaseWrite(eab, "R7"); /* read "current" in Tesla */
|
|
return __LINE__;
|
|
case __LINE__: /**********************************/
|
|
IpsSetField(drv, OxiGet(eab, 3, NULL, drv->current)); /* set drv->current and callback */
|
|
|
|
FsmCall(IpsMeas);
|
|
return __LINE__;
|
|
case __LINE__: /**********************************/
|
|
|
|
EaseWrite(eab, "R9"); /* read back ramp rate (may be sweep limited) */
|
|
return __LINE__;
|
|
case __LINE__: /**********************************/
|
|
ramp = OxiGet(eab, 3, NULL, drv->ramp);
|
|
step = fabs(ramp / 3) + 0.001; /* step = ramp * 20 sec */
|
|
if (drv->d.targetValue > drv->current + step) {
|
|
fld = drv->current + step;
|
|
} else if (drv->d.targetValue < drv->current - step) {
|
|
fld = drv->current - step;
|
|
} else {
|
|
fld = drv->d.targetValue;
|
|
if (eab->syntax) {
|
|
eps = 7.0e-5; /* IPS has 4 digits after decimal point */
|
|
} else {
|
|
eps = 7.0e-4; /* PS has 3 digits after decimal point */
|
|
}
|
|
if (fabs(drv->current - drv->d.targetValue) < eps)
|
|
goto target_reached;
|
|
}
|
|
if (fabs(fld) >= drv->trainedTo) {
|
|
if (drv->trainMode == 0) {
|
|
drv->trainMode = 3; /* remember that we must go to train mode */
|
|
}
|
|
drv->trainedTo = fabs(drv->current);
|
|
} else {
|
|
if (drv->trainMode == 1) {
|
|
drv->trainMode = 2; /* remember that we must go to fast mode */
|
|
}
|
|
}
|
|
OxiSet(eab, "J", fld, 3);
|
|
return __LINE__;
|
|
case __LINE__: /**********************************/
|
|
if (drv->trainMode < 2) goto ramping;
|
|
if (drv->trainMode == 2) {
|
|
ParPrintf(drv, -1, "IPS: switch off train mode");
|
|
EaseWrite(eab, "M1");
|
|
} else {
|
|
ParPrintf(drv, -1, "IPS: switch to train mode (slow ramp)");
|
|
EaseWrite(eab, "M5");
|
|
}
|
|
return __LINE__;
|
|
case __LINE__: /**********************************/
|
|
goto ramping;
|
|
|
|
target_reached:
|
|
EaseUpdateHwstate(drv, HWIdle);
|
|
drv->d.eMode = EVMonitor; /* we are at field, drive has finished */
|
|
if (drv->persmode != 1)
|
|
goto hold_finish;
|
|
/* but we continue in the background */
|
|
drv->tim = time(NULL);
|
|
stab3:
|
|
FsmWait(1);
|
|
return __LINE__;
|
|
case __LINE__: /**********************************/
|
|
|
|
FsmCall(IpsMeas);
|
|
return __LINE__;
|
|
case __LINE__: /**********************************/
|
|
|
|
if (time(NULL) < drv->tim + 10)
|
|
goto stab3; /* stabilize */
|
|
|
|
EaseWrite(eab, "A0"); /* hold */
|
|
return __LINE__;
|
|
case __LINE__: /**********************************/
|
|
goto finish;
|
|
|
|
hold_finish:
|
|
EaseWrite(eab, "A0");
|
|
return __LINE__;
|
|
case __LINE__: /**********************************/
|
|
goto finish;
|
|
|
|
off_finish:
|
|
if (drv->perswitch) {
|
|
drv->lastfield = drv->current;
|
|
drv->swtim = time(NULL);
|
|
}
|
|
EaseWrite(eab, "H0");
|
|
return __LINE__;
|
|
case __LINE__: /**********************************/
|
|
|
|
finish:
|
|
EaseWrite(eab, "C0");
|
|
drv->remote = 0; /* local state */
|
|
EaseUpdateHwstate(drv, HWIdle);
|
|
drv->tim = time(NULL); /* time of last field change */
|
|
return __LINE__;
|
|
case __LINE__: /**********************************/
|
|
|
|
return 0;
|
|
} /* FSM END ******************************************* */
|
|
|
|
}
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
static int IpsInit(SConnection * con, int argc, char *argv[], int dynamic)
|
|
{
|
|
/* args:
|
|
MakeObject objectname ips <rs232>
|
|
<host> <port>
|
|
*/
|
|
Ips *drv;
|
|
|
|
drv = EaseMakeDriv(con, &ipsClass, argc, argv, dynamic, 7,
|
|
IpsParDef, OxiHandler, IpsStart, NULL, IpsRead,
|
|
IpsChangeField);
|
|
if (drv == NULL)
|
|
return 0;
|
|
drv->d.maxwait = 999999;
|
|
drv->d.tolerance = 0.00011;
|
|
drv->dothis = NOTHING;
|
|
return 1;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
void IpsStartup(void)
|
|
{
|
|
ParMakeClass(&ipsClass, EaseDrivClass());
|
|
MakeDriver("IPS", IpsInit, 0, "OI Power Supply");
|
|
}
|