/*--------------------------------------------------------------------------- ighdriv.c Driver for the Oxford Instruments IGH Intelligent gas handling system (based on ease). Markus Zolliker, May 2005 ----------------------------------------------------------------------------*/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "oxinst.h" #include "fsm.h" #include "initializer.h" #define OLDIGH -8 #define SORBS_FLAG 1 #define MIXP_FLAG 2 #define MAXP_FLAG 3 #define STILL_FLAG 4 #define SORBP_FLAG 5 #define MOT_FLAGS 6 #define VALVE_FLAGS 9 #define MAX_FLAG 32 static char *valves[]={"V9", "V8", "V7", "V11A", "V13A", "V13B", "V11B", "V12B", " He4", "V1", "V5", "V4", "V3", "V14", "V10", "V2", " V2A", " V1A", " V5A", " V4A", " V3A", " Roots", " Aux", "He3", NULL}; /* valves beginning with blank are not shown in list */ typedef enum {V9,V8,V7,V11A,V13A,V13B,V11B,V12B, HE4,V1,V5,V4,V3,V14,V10,V2, V2A,V1A,V5A,V4A,V3A,ROOTS,AUX,HE3,n_VALVES} Valves; static char *motorValves[]={"V6", "V12A", "V1K", NULL}; typedef enum {V6,V12A,V1K,n_MOTOR} MotorValves; static char *motorCommands[]={"G", "H", "N"}; static char *gauges[]={"G1", "G2", "G3", "", "", "", "P1", "P2", NULL}; typedef enum {G1,G2,G3,G4,G5,G6,P1,P2,n_PRESS} PressureGauges; static char *closedOrOpen[]={"closed", "open", NULL}; typedef struct { EaseDriv d; float setT; float sorbS; float mixT; float onekT; float sorbT; float mixP; float stillP; float sorbP; float press[n_PRESS]; float mv[n_MOTOR]; float v6pos; time_t v6time; int pdig; int v[n_VALVES]; int e; /* heater range */ int a; /* actual heater mode */ int o; /* actual still/sorb mode */ int s; /* moving valve state */ int remote; } Igh; static ParClass ighClass = { "IGH", sizeof(Igh) }; static long IghSet(long pc, void *object); /*----------------------------------------------------------------------------*/ static int IghPower2Range(float p) { int e; if (p <= 0) return 0; for (e = 1; e < 5; e++) { if (p < 1.9994) break; p /= 10; } return e; } /*----------------------------------------------------------------------------*/ static float IghRange2Max(int e) { static float elist[]={0,2,20,200,2000,20000}; if (e < 0) { e = 0; } else if (e > 5) { e = 5; } return elist[e]; } /*----------------------------------------------------------------------------*/ static void IghParDef(void *object) { Igh *drv = ParCast(&ighClass, object); EaseBase *eab = object; int i, flag, l, changed; char *vPos, *val; char fmt[8], vList[80]; float maxP; ParName(""); ParTail("K"); ParFmt("%.4f"); ParFloat(&drv->mixT, PAR_NAN); ParName("Tset"); ParTail("K"); ParFmt("%.4f"); if (eab->syntax == OLDIGH) ParList("all"); ParFloat(&drv->setT, PAR_NAN); ParName("TsorbSet"); EaseUpdate(SORBS_FLAG); ParTail("K"); ParFmt("%.1f"); ParFloat(&drv->sorbS, PAR_NAN); ParName("Tmix"); ParTail("K"); ParFmt("%.4f"); ParFloat(&drv->mixT, PAR_NAN); ParName("T1K"); ParTail("K"); ParFmt("%.4f"); ParFloat(&drv->onekT, PAR_NAN); ParName("Tsorb"); ParTail("K"); ParFmt("%.1f"); ParFloat(&drv->sorbT, PAR_NAN); ParName("Pmix"); if (drv->e >= 5 || drv->e < 1) { strcpy(fmt, "%.0f"); } else { snprintf(fmt, sizeof fmt, "%%.%df", 4 - drv->e); } EaseUpdate(MIXP_FLAG); ParTail("uW"); ParFmt(fmt); ParFloat(&drv->mixP, PAR_NAN); ParName("Pmax"); EaseUpdate(MAXP_FLAG); ParTail("uW"); ParFmt("%g"); if ((val = ParGetValueArg())) { drv->e = IghPower2Range(atof(val) * 0.9); } maxP = IghRange2Max(drv->e); ParFloat(&maxP, PAR_NAN); ParName("Pstill"); EaseUpdate(STILL_FLAG); ParTail("mW"); ParFmt("%.3f"); ParFloat(&drv->stillP, PAR_NAN); ParName("Psorb"); EaseUpdate(SORBP_FLAG); ParTail("W"); ParFmt("%.1f"); ParFloat(&drv->sorbP, PAR_NAN); for (i=0; ipdig); ParFmt(fmt); } else if (i == P2) { ParFmt("%.3f"); ParList("all"); } else if (i == G3) { ParFmt("%.1f"); ParList("all"); } else { ParFmt("%.1f"); } ParFloat(&drv->press[i], PAR_NAN); } } flag = MOT_FLAGS; for (i=0; imv[i], PAR_NAN); } ParName("v6pos"); ParFmt("%.1f"); ParTail("%"); ParFloat(&drv->v6pos, PAR_NAN); assert(flag == VALVE_FLAGS); l = 0; for (i=0; iv[i], PAR_LNAN); if (changed) { if (drv->v[i] != 1) drv->v[i] = 0; /* allow only 1 or 0 */ } flag++; if (ParActionIs(PAR_LIST) && valves[i][0] != ' ') { if (drv->v[i] == 1) { vPos = vList + l; l += strlen(valves[i]) + 1; if (l < sizeof vList) { strcpy(vPos, " "); vPos++; strcpy(vPos, valves[i]); vList[l] = '\0'; } else { l = vPos - vList; } } } } assert(MAX_FLAG == flag-1); if (ParActionIs(PAR_LIST)) { vList[l] = '\0'; ParPrintf(NULL, eValue, "open valves:%s", vList); } EaseBasePar(drv); EaseSendPar(drv); ParStdDef(); EaseMsgPar(eab); } /*----------------------------------------------------------------------------*/ void IghStatus(Igh *drv) { char *ans; int *code; long mask, p; int i; if (drv->d.b.state != EASE_read) return; ans=drv->d.b.ans; code=&drv->d.b.errCode; if (ans[0] != 'X' || ans[2] != 'A' || ans[4] != 'C' || ans[6] != 'P' || ans[15] != 'S' || ans[17] != 'O' || ans[19] != 'E') { ParPrintf(drv, eError, "illegal status response: %s", ans); *code = EASE_FAULT; return; } if (sscanf(ans+7, "%lx", &mask) <= 0) { ParPrintf(drv, eError, "illegal valve status response"); *code = EASE_FAULT; return; } p=1; for (i=0; iv[i] = 1; } else { drv->v[i] = 0; } p=p*2; } } drv->a = ans[3] - '0'; drv->s = ans[16] - '0'; drv->o = ans[18] - '0'; if (EaseGetUpdate(drv, MAXP_FLAG) == 0) { if (drv->a == 0) { drv->e = 0; } else { drv->e = ans[20] - '0'; } } if (ans[5] != '3' && drv->remote == 2) { ParPrintf(drv, eError, "IGH switched to local"); *code = EASE_FAULT; drv->remote = 1; return; } } /*----------------------------------------------------------------------------*/ static long IghRead(long pc, void *object) { Igh *drv = ParCast(&ighClass, object); EaseBase *eab = object; char *p; int l; time_t now; float delta; switch (pc) { default: /* FSM BEGIN *******************************/ EaseWrite(eab, "X"); return __LINE__; case __LINE__: /**********************************/ IghStatus(drv); if (!drv->remote) goto skiprmt; EaseWrite(eab, "C0"); drv->remote = 0; return __LINE__; case __LINE__: /**********************************/ skiprmt: if (! EaseNextFullRead(eab)) goto fsm_quit; if (EaseCheckDoit(eab)) goto quit; if (EaseGetUpdate(drv, SORBS_FLAG)) goto skip0; if (drv->o / 2 != 1) { drv->sorbS = 0; goto skip0; } EaseWrite(eab, "R0"); return __LINE__; case __LINE__: /**********************************/ if (EaseGetUpdate(drv, SORBS_FLAG)) goto skip0; drv->sorbS = OxiGet(eab, 1, NULL, drv->sorbS); skip0: if (EaseCheckDoit(eab)) goto quit; if (eab->syntax == OLDIGH) { EaseWrite(eab, "R3"); } else { EaseWrite(eab, "R32"); } return __LINE__; case __LINE__: /**********************************/ if (eab->syntax == OLDIGH) { drv->mixT = OxiGet(eab, 3, NULL, drv->mixT); } else { drv->mixT = OxiGet(eab, 4, NULL, drv->mixT); if (drv->mixT < 0) drv->mixT += 6.5536; /* correct a bug in firmware < 3.03 */ } if (EaseCheckDoit(eab)) goto quit; if (eab->syntax == OLDIGH) goto noSetTemp; EaseWrite(eab, "R33"); return __LINE__; case __LINE__: /**********************************/ drv->setT = OxiGet(eab, 4, NULL, drv->setT); noSetTemp: EaseWrite(eab, "R1"); return __LINE__; case __LINE__: /**********************************/ drv->sorbT = OxiGet(eab, 1, NULL, drv->sorbT); if (EaseCheckDoit(eab)) goto quit; EaseWrite(eab, "R2"); return __LINE__; case __LINE__: /**********************************/ drv->onekT = OxiGet(eab, 3, NULL, drv->onekT); if (EaseCheckDoit(eab)) goto quit; if (drv->e == 0) { drv->mixP = 0; goto skip4; } if (EaseGetUpdate(drv, MIXP_FLAG)) goto skip4; EaseWrite(eab, "R4"); return __LINE__; case __LINE__: /**********************************/ if (EaseGetUpdate(drv, MIXP_FLAG)) goto skip4; drv->mixP = OxiGet(eab, 5 - drv->e, NULL, drv->mixP/10) * 10; skip4: if (EaseCheckDoit(eab)) goto quit; if (EaseGetUpdate(drv, STILL_FLAG)) goto skip5; EaseWrite(eab, "R5"); return __LINE__; case __LINE__: /**********************************/ if (EaseGetUpdate(drv, STILL_FLAG)) goto skip5; drv->stillP = OxiGet(eab, 1, NULL, drv->stillP); skip5: if (EaseCheckDoit(eab)) goto quit; if (EaseGetUpdate(drv, SORBP_FLAG)) goto skip6; EaseWrite(eab, "R6"); return __LINE__; case __LINE__: /**********************************/ if (EaseGetUpdate(drv, SORBP_FLAG)) goto skip6; drv->sorbP = OxiGet(eab, 3, NULL, drv->sorbP); skip6: if (EaseCheckDoit(eab)) goto quit; if (EaseGetUpdate(drv, MOT_FLAGS+V6)) goto skip7; EaseWrite(eab, "R7"); return __LINE__; case __LINE__: /**********************************/ if (EaseGetUpdate(drv, MOT_FLAGS+V6)) goto skip7; drv->mv[V6] = OxiGet(eab, 1, NULL, drv->mv[V6]); time(&now); delta = (now - drv->v6time) / 2.64; /* speed: 1/2.64 %/sec */ drv->v6time = now; if (drv->v6pos > drv->mv[V6] + delta) { drv->v6pos -= delta; } else if (drv->v6pos < drv->mv[V6] - delta) { drv->v6pos += delta; } else { drv->v6pos = drv->mv[V6]; } skip7: if (EaseCheckDoit(eab)) goto quit; if (EaseGetUpdate(drv, MOT_FLAGS+V12A)) goto skip8; EaseWrite(eab, "R8"); return __LINE__; case __LINE__: /**********************************/ if (EaseGetUpdate(drv, MOT_FLAGS+V12A)) goto skip8; drv->mv[V12A] = OxiGet(eab, 1, NULL, drv->mv[V12A]); skip8: if (EaseCheckDoit(eab)) goto quit; if (EaseGetUpdate(drv, MOT_FLAGS+V1K)) goto skip9; EaseWrite(eab, "R9"); return __LINE__; case __LINE__: /**********************************/ if (EaseGetUpdate(drv, MOT_FLAGS+V1K)) goto skip9; drv->mv[V1K] = OxiGet(eab, 1, NULL, drv->mv[V1K]); skip9: if (EaseCheckDoit(eab)) goto quit; EaseWrite(eab, "R14"); return __LINE__; case __LINE__: /**********************************/ drv->press[G1] = OxiGet(eab, 1, NULL, drv->press[G1]); if (EaseCheckDoit(eab)) goto quit; EaseWrite(eab, "R15"); return __LINE__; case __LINE__: /**********************************/ drv->press[G2] = OxiGet(eab, 1, NULL, drv->press[G2]); if (EaseCheckDoit(eab)) goto quit; EaseWrite(eab, "R16"); return __LINE__; case __LINE__: /**********************************/ drv->press[G3] = OxiGet(eab, 1, NULL, drv->press[G3]); if (EaseCheckDoit(eab)) goto quit; EaseWrite(eab, "R20"); return __LINE__; case __LINE__: /**********************************/ drv->press[P1] = OxiGet(eab, drv->pdig, &drv->pdig, drv->press[P1]); if (EaseCheckDoit(eab)) goto quit; EaseWrite(eab, "R21"); return __LINE__; case __LINE__: /**********************************/ drv->press[P2] = OxiGet(eab, 1, NULL, drv->press[P2]); quit: ParLog(drv); fsm_quit: return 0; } /* FSM END **********************************/ } /*----------------------------------------------------------------------------*/ static long IghStart(long pc, void *object) { Igh *drv = ParCast(&ighClass, object); EaseBase *eab = object; switch (pc) { default: /* FSM BEGIN *******************************/ EaseWrite(eab, "V"); return __LINE__; case __LINE__: /**********************************/ if (0 != strncmp(eab->version, "IGH", 3)) { snprintf(eab->msg, sizeof eab->msg, "unknown gas handling system version: %s", eab->version); ParPrintf(drv, eError, "ERROR: %s", eab->msg); EaseStop(eab); goto quit; } if (strstr(eab->version, "2.01") != NULL) { eab->syntax = OLDIGH; /* includes communication error correction */ } else { eab->syntax = 0; } ParPrintf(drv, eStatus, "connected to %s", eab->version); FsmCall(IghRead); return __LINE__; case __LINE__: /**********************************/ quit: return 0; } /* FSM END ********************************************/ } /*----------------------------------------------------------------------------*/ static long IghSet(long pc, void *object) { Igh *drv = ParCast(&ighClass, object); EaseBase *eab = object; char buf[8]; int a; int upd; int i; float mp; switch (pc) { default: /* FSM BEGIN *******************************/ EaseWrite(eab, "C3"); drv->remote=2; loop: return __LINE__; case __LINE__: /**********************************/ upd = EaseNextUpdate(drv); if (upd >= VALVE_FLAGS) goto set_valve; if (upd >= MOT_FLAGS) goto set_mot; if (upd == EASE_RUN) goto set_temp; if (upd == SORBS_FLAG) goto set_sorb_temp; if (upd == MIXP_FLAG) goto set_mix_pow; if (upd == MAXP_FLAG) goto set_max_pow; if (upd == STILL_FLAG) goto set_still_pow; if (upd == SORBP_FLAG) goto set_sorb_pow; goto finish; set_sorb_temp: if (drv->sorbS == 0) { drv->sorbP = 0; goto set_sorb_pow; } OxiSet(eab, "K", drv->sorbS, 1); return __LINE__; case __LINE__: /**********************************/ EaseWrite(eab, "X"); return __LINE__; case __LINE__: /**********************************/ IghStatus(drv); if (drv->o == 2 || drv->o == 3) goto loop; if (drv->o % 2) { EaseWrite(eab, "O3"); } else { EaseWrite(eab, "O2"); } return __LINE__; case __LINE__: /**********************************/ goto loop; set_sorb_pow: EaseWrite(eab, "X"); return __LINE__; case __LINE__: /**********************************/ IghStatus(drv); if (drv->o <= 1) goto skipSetO; if (drv->o % 2) { EaseWrite(eab, "O1"); drv->o = 1; } else { EaseWrite(eab, "O0"); drv->o = 0; } return __LINE__; case __LINE__: /**********************************/ skipSetO: OxiSet(eab, "B", drv->sorbP, 3); return __LINE__; case __LINE__: /**********************************/ if (drv->sorbP == 0.0 || drv->o >= 4) goto loop; if (drv->o % 2) { EaseWrite(eab, "O5"); } else { EaseWrite(eab, "O4"); } return __LINE__; case __LINE__: /**********************************/ goto loop; set_still_pow: OxiSet(eab, "S", drv->stillP, 1); return __LINE__; case __LINE__: /**********************************/ EaseWrite(eab, "X"); return __LINE__; case __LINE__: /**********************************/ IghStatus(drv); snprintf(buf, sizeof buf, "O%d", drv->o | 1); EaseWrite(eab, buf); return __LINE__; case __LINE__: /**********************************/ goto loop; set_mix_pow: EaseWrite(eab, "A0"); return __LINE__; case __LINE__: /**********************************/ if (drv->e <= 1) goto skipe; EaseWrite(eab, "E1"); return __LINE__; case __LINE__: /**********************************/ skipe: if (drv->mixP > 0) { drv->e = IghPower2Range(drv->mixP); mp = drv->mixP / IghRange2Max(drv->e) * 2000; } else { mp = 0; /* range unchanged for external heater signal */ } OxiSet(eab, "M", mp, 0); return __LINE__; case __LINE__: /**********************************/ if (drv->e == 0) goto loop; EaseWrite(eab, "A1"); return __LINE__; case __LINE__: /**********************************/ set_max_pow: if (drv->e == 0) goto seta0; snprintf(buf, sizeof buf, "E%d", drv->e); EaseWrite(eab, buf); return __LINE__; case __LINE__: /**********************************/ goto loop; seta0: EaseWrite(eab, "A0"); return __LINE__; case __LINE__: /**********************************/ goto loop; set_temp: EaseWrite(eab, "A2"); return __LINE__; case __LINE__: /**********************************/ if (drv->d.targetValue < 0) drv->d.targetValue = 0; if (drv->d.targetValue > 1.999) drv->d.targetValue = 1.999; OxiSet(eab, "T", drv->d.targetValue, 4); return __LINE__; case __LINE__: /**********************************/ goto loop; set_valve: i = upd - VALVE_FLAGS; snprintf(buf, sizeof buf, "P%d", i * 2 + 3 - drv->v[i]); EaseWrite(eab, buf); return __LINE__; case __LINE__: /**********************************/ goto loop; set_mot: i = upd - MOT_FLAGS; if (drv->mv[i] > 99.9) { drv->mv[i]=99.9; } else if (drv->mv[i] < 0) { drv->mv[i]=0; } OxiSet(eab, motorCommands[i], drv->mv[i], 1); return __LINE__; case __LINE__: /**********************************/ goto loop; finish: EaseWrite(eab, "C0"); return __LINE__; case __LINE__: /**********************************/ drv->remote = 0; quit: return 0; } /* FSM END ********************************************/ } /*----------------------------------------------------------------------------*/ static int IghInit(SConnection *con, int argc, char *argv[], int dynamic) { /* args: MakeObject objectname igh MakeObject objectname igh */ Igh *drv; drv = EaseMakeDriv(con, &ighClass, argc, argv, dynamic, MAX_FLAG, IghParDef, OxiHandler, IghStart, NULL, IghRead, IghSet); if (drv == NULL) return 0; return 1; } /*----------------------------------------------------------------------------*/ void IghStartup(void) { ParMakeClass(&ighClass, EaseDrivClass()); MakeDriver("IGH", IghInit, 0, "OI Gas Handling System"); }