/*---------------------------------------------------------------------------
lcdriv.c

Driver for the Oxford Instruments Teslatron lambda controller
  
Markus Zolliker, Sept 2004
----------------------------------------------------------------------------*/
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <sys/time.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 "oicom.h"
#include "fsm.h"

typedef struct {
  Eve eve;
  float t[4];                   /* set t. & 3 temperatures */
  int dig[4];                   /* format for these */
  float gas;
  int remote;
  int hot;
} LcDriv;

static long LcSetGas(long pc, void *obj);

/*----------------------------------------------------------------------------*/
#define A EVE_ACTPAR
#define L EVE_LOGPAR
#define S EVE_SAVEPAR

void LcPars(LcDriv * me, EveParArg * arg)
{
  char fmt[80];

  sprintf(fmt, "%%.%df\tK", me->dig[2]);
  EveFloatPar(arg, "t2", &me->t[2], fmt, usInternal,
              (me->dig[2] >= 0) * A + L);

  sprintf(fmt, "%%.%df\tK", me->dig[3]);
  EveFloatPar(arg, "t3", &me->t[3], fmt, usInternal,
              (me->dig[3] >= 0) * A + L);

  EveFloatCmd(arg, "gas", &me->gas, "%.1f\t%%", LcSetGas, usUser, A + L);

  EveIntPar(arg, "dig1", &me->dig[1], usUser, S);
  EveIntPar(arg, "dig2", &me->dig[2], usUser, S);
  EveIntPar(arg, "dig3", &me->dig[3], usUser, S);

  sprintf(fmt, "%%.%df\tmbar", me->dig[1]);
  EveStdParEnd(arg, fmt, 0);
}

/*----------------------------------------------------------------------------*/
void LcStatus(LcDriv * me)
{
  char *ans;
  int *code;
  Eve *eve = &me->eve;

  if (eve->state != readState)
    return;
  ans = eve->ans;
  code = &eve->errCode;
  if (ans[0] != 'X' || ans[2] != 'A' || ans[4] != 'C' || ans[6] != 'S') {
    EvePrintf(eve, eError, "illegal status response");
    *code = EVE_FAULT;
    return;
  }
  if (ans[6] != '3' && me->remote == 2) {
    EvePrintf(eve, eError, "LC switched to local");
    *code = EVE_FAULT;
    me->remote = 1;
    return;
  }
}

/*----------------------------------------------------------------------------*/
static int LcRead(long pc, LcDriv * me)
{
  Eve *eve = &me->eve;
  char *p;
  int l;

  FSM_BEGIN EveWrite(eve, "X");
  FSM_NEXT LcStatus(me);        /* check for errors */
/*
    if (!me->remote) goto skiprmt;
    EveWrite(eve, "C0");
    me->remote = 0;
  FSM_NEXT
*/
skiprmt:
  if (me->dig[1] < 0)
    goto skip1;
  EveWrite(eve, "R1");          /* read sensor 1 */
  FSM_NEXT me->t[1] = OiGetFlt(eve, me->dig[1], NULL);
  me->eve.value = me->t[1];
skip1:
  if (me->dig[2] < 0)
    goto skip2;
  EveWrite(eve, "R2");          /* read sensor 2 */
  FSM_NEXT me->t[2] = OiGetFlt(eve, me->dig[2], NULL);
skip2:
  if (me->dig[3] < 0)
    goto skip3;
  EveWrite(eve, "R3");          /* read sensor 3 */
  FSM_NEXT me->t[3] = OiGetFlt(eve, me->dig[3], NULL);
skip3:
FSM_END}

/*----------------------------------------------------------------------------*/
static long LcSetGas(long pc, void *obj)
{
  LcDriv *me = obj;
  Eve *eve = &me->eve;
  pEVControl evc = eve->evc;
  float fld;
  float step;
  float ramp;
  char buf[4];
  SConnection *pCon;
  int a;

  FSM_BEGIN if (me->remote)
    goto skipremote;
  EveWrite(eve, "C1");
FSM_NEXT skipremote:
  OiSet(eve, "G", me->gas, 1);  /* cold valve setting */
FSM_NEXT quit:
FSM_END}

/*----------------------------------------------------------------------------*/
static int LcStart(long pc, LcDriv * me)
{
  Eve *eve = &me->eve;

  FSM_BEGIN EveWrite(eve, "V");
  FSM_NEXT if (0 == strncmp(eve->version, "TESLATRON", 9)) {
    me->eve.syntax = 0;
    me->dig[1] = 1;
    me->dig[2] = 2;
    me->dig[3] = 1;
  } else {
    EvePrintf(eve, eError, "unknown lambda controller version: %s",
              eve->version);
    goto quit;
  }
  EvePrintf(eve, eLog, "connected to %s", eve->version);
  FSM_CALL(LcRead);

quit:
FSM_END}

/*----------------------------------------------------------------------------*/
static int LcSetTemp(long pc, LcDriv * me)
{
  Eve *eve = &me->eve;
  pEVControl evc = eve->evc;
  float fld;
  float step;
  float ramp;
  char buf[4];
  SConnection *pCon;

FSM_BEGIN FSM_END}

/*------------------------------------------------------------------------*/
pEVControl LcMakeEVC(SConnection * pCon, int argc, char *argv[])
{
  /* args:
     <objectname> lc <rs232>
     <host> <port>
   */
  Eve *eve;
  pEVControl evc;
  LcDriv *me = NULL;

  evc = MakeEveEVC(argc, argv, calloc(1, sizeof *me), pCon);
  if (!evc)
    return NULL;

  me = evc->pDriv->pPrivate;
  eve = &me->eve;

  eve->run = (FsmFunc) LcSetTemp;
  eve->read = (FsmFunc) LcRead;
  eve->pardef = (EveParDef) LcPars;
  eve->todo = (FsmFunc) LcStart;
  eve->task = FsmStartTask(me, (FsmHandler) OiHandler, (FsmFunc) EveIdle);

  return evc;
}
