/*---------------------------------------------------------------------------
ighdriv.c

Driver for the Oxford Instruments IGH Intelligent gas handling system
(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 <servlog.h>
#include <sicsvar.h>
#include <evdriver.i>
#include <rs232controller.h>
#include "oxinst.h"
#include "fsm.h"
#include "initializer.h"
#include "syncedprot.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;

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;
  long hrSync;
} 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.9994e-3)
      break;
    p /= 10;
  }
  return e;
}

/*----------------------------------------------------------------------------*/
static float IghRange2Max(int e)
{
  static float elist[] = { 0, 0.002, 0.02, 0.2, 2.0, 20.0 };
  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];
  static char *na = NULL;
  float maxP;
  static char *heaterList[] =
      { "off", "2uW", "20uW", "200uW", "2mW", "20mW", NULL };
  static char *closedOrOpen[] = { "closed", "open", NULL };

  ParName("");
  ParTail("K");
  ParFmt("%.4f");
  ParFloat(&drv->mixT, PAR_NAN);

  ParName("Tset");
  ParTail("K");
  ParFmt("%.4f");
  if (eab->syntax == OLDIGH) {
    ParList("all");
    drv->setT = -1;
    ParFloat(&drv->setT, -1);
  } else {
    if (drv->setT < 0)
      drv->setT = 0;
    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");
  EaseUpdate(MIXP_FLAG);
  ParTail("mW");
  ParFmt("%.5g");
  ParFloat(&drv->mixP, PAR_NAN);

  ParName("Pmax");
  ParTail("mW");
  ParFmt("%.5g");
  maxP = IghRange2Max(drv->e);
  ParFloat(&maxP, PAR_NAN);

  ParName("HtrRange");
  EaseUpdate(MAXP_FLAG);
  ParEnum(heaterList);
  ParList(NULL);
  ParInt(&drv->e, PAR_NAN);
  if (ParActionIs(PAR_SET) > 0) {
    drv->mixP = 0;
    if (drv->hrSync == 0) {
      drv->hrSync=SyncedIncr(0);
    }
  }

  ParName("Pstill");
  EaseUpdate(STILL_FLAG);
  ParTail("mW");
  ParFmt("%.3f");
  ParFloat(&drv->stillP, PAR_NAN);

  ParName("Psorb");
  EaseUpdate(SORBP_FLAG);
  ParTail("W");
  ParFmt("%.3f");
  ParFloat(&drv->sorbP, PAR_NAN);

  for (i = 0; i < n_PRESS; i++) {
    if (gauges[i][0] != '\0') {
      ParName(gauges[i]);
      ParTail("mbar");
      if (i == P1) {
        snprintf(fmt, sizeof fmt, "%%.%df", drv->pdig);
        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; i < n_MOTOR; i++) {
    ParName(motorValves[i]);
    ParFmt("%.1f");
    ParTail("%");
    if (i == V1K)
      ParList("all");
    EaseUpdate(flag);
    flag++;
    ParFloat(&drv->mv[i], PAR_NAN);
  }

  ParName("v6pos");
  ParFmt("%.1f");
  ParTail("%");
  ParFloat(&drv->v6pos, PAR_NAN);

  assert(flag == VALVE_FLAGS);
  l = 0;
  for (i = 0; i < n_VALVES; i++) {
    if (valves[i][0] == ' ') {
      ParName(valves[i] + 1);
      changed = 0;
    } else {
      ParName(valves[i]);
      changed = EaseUpdate(flag);
    }
    ParEnum(closedOrOpen);
    ParInt(&drv->v[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);
  if (eab->syntax != OLDIGH) {
    EaseDrivPar(drv, "%.5g", "K");
  }
  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; i < n_VALVES; i++) {
    if (!EaseGetUpdate(drv, VALVE_FLAGS + i)) {
      if (mask & p) {
        drv->v[i] = 1;
      } else {
        drv->v[i] = 0;
      }
    }
    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 ****************************** */
    EasePchk(drv);
    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) || EaseGetUpdate(drv, MAXP_FLAG))
      goto skip4;
    EaseWrite(eab, "R4");
    return __LINE__;
  case __LINE__:                 /**********************************/
    if (EaseGetUpdate(drv, MIXP_FLAG) || EaseGetUpdate(drv, MAXP_FLAG))
      goto skip4;
    drv->mixP = OxiGet(eab, 5 - drv->e, NULL, drv->mixP * 100) * 0.01;
  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;
  char msg[128];
  
  switch (pc) {
  default:                     /* FSM BEGIN ****************************** */
    EasePchk(drv);
    EaseWrite(eab, "V");
    return __LINE__;
  case __LINE__:                 /**********************************/
    if (0 != strncmp(eab->version, "IGH", 3)) {
      snprintf(msg, sizeof msg,
               "unknown gas handling system version: %s", eab->version);
      EaseStop(eab, msg);
      goto quit;
    }
    if (strstr(eab->version, "2.01") != NULL) {
      eab->syntax = OLDIGH;     /* includes communication error correction */
    } else {
      eab->syntax = 0;
    }
    ParPrintf(drv, eLog, "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 ****************************** */
    EasePchk(drv);
    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;
      if (mp < 2001 && mp > 1999) {
        mp = 1999;
      }
    } else {
      mp = 0;                   /* range unchanged for external heater signal */
    }
    OxiSet(eab, "M", mp, 0);
    return __LINE__;
  case __LINE__:                 /**********************************/
    if (drv->e == 0)
      goto seta0;
    snprintf(buf, sizeof buf, "E%d", drv->e);
    EaseWrite(eab, buf);
    return __LINE__;
  case __LINE__:                 /**********************************/
    EaseWrite(eab, "A1");
    return __LINE__;
  case __LINE__:                 /**********************************/
    goto loop;

  set_max_pow:
    if (drv->e == 0)
      goto seta0;
    snprintf(buf, sizeof buf, "E%d", drv->e);
    EaseWrite(eab, buf);
    return __LINE__;
  case __LINE__:                 /**********************************/
    if (drv->hrSync > 0) {
      SyncedDecr(drv->hrSync);
      drv->hrSync = 0;
    }
    if (drv->a == 2) goto loop;
    EaseWrite(eab, "A1");
    return __LINE__;
  case __LINE__:                 /**********************************/
    EaseWrite(eab, "M0");
    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;
    drv->setT = drv->d.targetValue;
    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 <rs232>
     MakeObject objectname igh <host> <port>
   */
  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");
}
