/*---------------------------------------------------------------------------
eve.c

extended environment controller utilities

Markus Zolliker, Sept 2004
----------------------------------------------------------------------------
*/

#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <fortify.h>
#include <ctype.h>
#include "sics.h"
#include "rs232controller.h"
#include "obpar.h"
#include "evcontroller.h"
#include "evcontroller.i"
#include "evdriver.i"
#include "eve.h"
#include "fsm.h"
#include "logger.h"

#define ILLNUM -1456
#define ILLARGC -1457
#define AMBIGUOS -1458
#define ILLPRIV -1460
#define BADLOG -1461
#define BUSY -1462

typedef enum {
  cntAction, iniAction, parAction, listAction,
  logAction, logSwitchAction, saveAction
} EveAction;
typedef enum { noOp, fmtOp, setOp } ArgOp;

struct EveParArg {
  EveAction action;
  int argc;
  char **argv;
  int ret; /* return value */
  SConnection *pCon;
  pEVControl evc;
  time_t now;
  int period;
  int idx;
  FILE *fil;
};

struct EvePar {
  char *name;
  Logger *log;
};

/*--------------------------------------------------------------------------*/
char *EveArg2Text(int argc, char *argv[], char *res, int maxsize) {
  int i, l;
  char *p;
  
  if (res == NULL) {
    maxsize = 0;
    for (i=0; i<argc; i++) {
      maxsize += strlen(argv[i]) + 1;
    }
    res=malloc(maxsize);
  }
  maxsize--;
  *res='\0';
  strncat(res, argv[0], maxsize);
  p = res;
  for (i=1; i<argc; i++) {
    l = strlen(p);
    p += l;
    maxsize -= l;
    if (maxsize == 0) return res;
    *p=' ';
    p++;
    maxsize--;
    *p='\0';
    strncat(p, argv[i], maxsize);
  }
  return res;
}
/*--------------------------------------------------------------------------*/
int EvePrintf(Eve *eve, int iOut, char *fmt, ...) {
  va_list ap;
  char buf[1024];
  SConnection *pCon;
  
  va_start(ap, fmt);
  vsnprintf(buf, sizeof(buf), fmt, ap);
  va_end(ap);
  
  eve->errMsg[0] = '\0';
  if (eve->evc != NULL) {
    pCon = SCLoad(&eve->evc->conn);
    if (pCon != NULL) {
      if (iOut < 0) {
       if (-iOut > eve->verbose) return 0;
       iOut=eStatus;
      }
      return SCWrite(pCon, buf, iOut);
    }
  }
  if (iOut == eError) {
    snprintf(eve->errMsg, sizeof eve->errMsg, "%s", buf);
  }
  return 1;
}
/*--------------------------------------------------------------------------*/
int EveText2Int(char *list[], char *text) {
  int num = 0;
  char *endp;

  while (list[num] != NULL) {
    if (strcasecmp(list[num],text) == 0) {
      return num;
    }
    num++;
  }
  num = strtol(text, &endp, 0);
  if (endp == text) return -1;
  return num;
}
/*--------------------------------------------------------------------------*/
char *EveInt2Text(char *list[], int num) {
  int i;
  static char buf[12];
  
  for (i = 0; i <= num; i++) {
    if (list[i] == NULL) {
      snprintf(buf, sizeof buf, "%d", num);
      return buf;
    }
  }
  return list[num];
}
/*----------------------------------------------------------------------------*/
int EveCheckRights(EveParArg *arg, int access) {
  
  if (SCMatchRights(arg->pCon,access)) {
    return 1;
  } else {
    arg->ret = ILLPRIV;
  }
  return 0;
}
/*--------------------------------------------------------------------------*/
void EveMakePar(Eve *eve) {
  EveParArg arg;
  int i;
  
  if (eve->par == NULL) {
    arg.action = cntAction;
    arg.pCon = NULL;
    arg.evc = eve->evc;
    arg.idx = 0;
    eve->pardef(eve, &arg); /* first time: count */
    eve->npar = arg.idx;
    
    eve->par = calloc(arg.idx, sizeof *eve->par);
    for (i=0; i<arg.idx; i++) {
      eve->par[i].log = NULL;
      eve->par[i].name = NULL;
    }
    
    arg.action = iniAction;
    arg.idx = 0;
    eve->pardef(eve, &arg); /* second time: start default loggers */
   }
}
/*--------------------------------------------------------------------------*/
void EveLog(Eve *eve) {
  EveParArg arg;
    
  if (!eve->par) {
    EveMakePar(eve);
  }
  arg.now = time(NULL);
  if (arg.now >= (eve->logtime / eve->period + 1) * eve->period) {
    arg.action = logAction;
    arg.pCon = NULL;
    arg.evc = eve->evc;
    arg.argc = 0;
    arg.idx = 0;
    arg.period = eve->period;
    eve->pardef(eve, &arg);
    eve->logtime = arg.now;
  }
}
/*----------------------------------------------------------------------------*/
int EveSwitchLog(Eve *eve, EvePar *par, int on) {
  char buf[80];
  
  if (on) {
    if (par->log == NULL) {
      if (par->name[0] == '\0') {
        par->log = LoggerMake(eve->evc->pName, eve->period, 0);
      } else {
        snprintf(buf, sizeof buf, "%s.%s", eve->evc->pName, par->name);
        par->log = LoggerMake(buf, eve->period, 0);
      }
      if (par->log == NULL) {
        return BADLOG;
      }
    }
  } else {
    if (par->log != NULL) {
      LoggerKill(par->log);
      par->log = NULL;
    }
  }
  return 1;
}
/*--------------------------------------------------------------------------*/
EvePar *EveThisPar(EveParArg *arg, char *name, int flags) {
  Eve *eve = arg->evc->pDriv->pPrivate;
  EvePar *par;
  
  if (!eve->par) return NULL;
  assert(arg->idx < eve->npar);
  par = eve->par + arg->idx;
  if (par->name == NULL) {
    par->name = name;
    if (flags & EVE_LOGPAR) { /* first time: default logger */
      EveSwitchLog(eve, par, 1);
    }
  } else {
    assert(par->name == name);
  }
  return par;
}
/*----------------------------------------------------------------------------*/
ArgOp EveOp(EveParArg *arg, char *name, char **fmt, int access, int flags) {
  static char buf[80];
  char *sp;
  int l;
  EvePar *par;
  ArgOp op;
  
  switch (arg->action) {
  case cntAction:
    arg->idx ++;
    return noOp;
  case iniAction:
    EveThisPar(arg, name, flags);
    arg->idx ++;
    return noOp;
  case listAction:
    if (flags & EVE_ACTPAR) {
      return fmtOp;
    }
    return noOp;
  case logAction:
    if (flags & EVE_ACTPAR) {
      op = fmtOp;
      break; /* reduce fmt */
    }
    arg->idx++;
    return noOp;
  case logSwitchAction:
    if (arg->argc > 2) {
      arg->ret = ILLARGC;
      return noOp;
    }
    if (0==strcasecmp(name, arg->argv[1])) {
      if (arg->ret) {
        arg->ret = AMBIGUOS;
        return noOp;
      }
      par = EveThisPar(arg, name, 0);
      EveSwitchLog(arg->evc->pDriv->pPrivate, par, toupper(arg->argv[0][0]) == 'L');
    }
    arg->idx ++;
    return noOp;
  case parAction:
    if (0==strcasecmp(name, arg->argv[0])) {
      if (arg->ret) {
        arg->ret = AMBIGUOS;
        return noOp;
      }
      arg->ret = 1;
      if (arg->argc == 1) {
        op = fmtOp;
        break; /* reduce fmt */
      }
      if (!EveCheckRights(arg, access)) return noOp;
      op = setOp;
      break;
    }
    return noOp;
  case saveAction:
    if (flags & EVE_SAVEPAR) {
      op = fmtOp;
      break; /* reduce fmt */
    }
    return noOp;
  default:
    return noOp;
  }
  
  if (fmt != NULL) {
    /* skip fmt after tab */
    sp = strchr(*fmt, '\t');
    if (sp != NULL) {
      l = sp - *fmt;
      if (l >= sizeof buf) l = sizeof buf - 1;
      strncpy(buf, *fmt, l);
      buf[l]='\0';
      *fmt = buf;
    }
  }
  return op;
}
/*----------------------------------------------------------------------------*/
void EveOut(EveParArg *arg, char *name, char *buf) {
  EvePar *par;
  int l, i, j, m, ln, lp, iret;
  char *p, *q, *tab;
  char buffer[256];
  char *dot;
  
  switch (arg->action) {
  case listAction:
    p = buf;
    while (*p == ' ') p++; /* skip blanks */
    i = 0;
    if (p[i] == '-') i++;
    j = i;
    while (p[i] >= '0' && p[i] <='9') i++;
    tab = strchr(p, '\t');
    if (tab) {
      l = tab - p;
      *tab = ' ';
    } else {
      l = strlen(p);
    }
    ln = strlen(name);
    if (i != j && (buf[i] == '.' || buf[i] <= ' ')) {
      l += 16 - i - ln; /* decimal point or end of number at least 16 chars after object name */
      if (tab) {
        *tab = '\0';
        q = tab + 1;
      } else {
        q = "";
      }
      lp = strlen(p);
      if (l < lp) l = lp;
      m = 22 - l - ln; /* unit/comment at least 22 chars after object name */
      if (m < 1) m = 1;
      m += strlen(q);
    } else {
      l = 16 - ln;
      q = "";
      m = 0;
    }
    if (l <= 0) l = 1;
    SCPrintf(arg->pCon, eStatus, "%s %s %*s%*s", arg->evc->pName, name, l, p, m, q);
    break;
  case parAction:
    SCPrintf(arg->pCon, eValue, "%s %s = %s", arg->evc->pName, name, buf);
    break;
  case logAction:
    par = EveThisPar(arg, name, 0);
    arg->idx ++;
    if (par->log) {
      iret = LoggerWrite(par->log, arg->now, arg->period, buf);
      if (iret) {
        if (*name) {
          dot = ".";
        } else {
          dot = "";
        }
        snprintf(buffer, sizeof buffer, "%s%s%s = %s", arg->evc->pName, dot, name, buf);
        InvokeCallBack(arg->evc->pCall, VALUECHANGE, buffer);
      }
    }
    break;
  case saveAction:
    fprintf(arg->fil, "%s %s %s\n", arg->evc->pName, name, buf);
    break;
  default: break;
  }
}
/*----------------------------------------------------------------------------*/
void EveFloatPar(EveParArg *arg, char *name, float *value, char *fmt,
                 int access, int flags) {
  char *endp;
  float f;
  char buf[132];
  
  switch (EveOp(arg, name, &fmt, access, flags)) {
  case setOp:
    if (arg->argc > 2) {
      arg->ret = ILLARGC;
      return;
    }
    f = strtod(arg->argv[1], &endp);
    if (endp == arg->argv[1]) {
      arg->ret = ILLNUM;
      break;
    }
    *value = f;
    SCparChange(arg->pCon);
    /* fall through */
  case fmtOp:
    snprintf(buf, sizeof buf, fmt, *value);
    EveOut(arg, name, buf);
    break;
  case noOp: break;
  }
}
/*----------------------------------------------------------------------------*/
void EveIntPar(EveParArg *arg, char *name, int *value, int access, int flags) {
  char *endp;
  int i;
  char buf[132];
  
  switch (EveOp(arg, name, NULL, access, flags)) {
  case setOp:
    if (arg->argc > 2) {
      arg->ret = ILLARGC;
      return;
    }
    i = strtol(arg->argv[1], &endp, 0);
    if (endp == arg->argv[1]) {
      arg->ret = ILLNUM;
      break;
    }
    *value = i;
    SCparChange(arg->pCon);
    /* fall through */
  case fmtOp:
    snprintf(buf, sizeof buf, "%d", *value);
    EveOut(arg, name, buf);
    break;
  case noOp: break;
  }
}
/*----------------------------------------------------------------------------*/
void EveStrPar(EveParArg *arg, char *name, char **value, int maxsize, int access, int flags) {
  static char *empty="";

  switch (EveOp(arg, name, NULL, access, flags)) {
  case setOp:
    if (maxsize == 0) { /* dynamic string */
      if (*value != NULL) free(*value);
      *value = EveArg2Text(arg->argc-1, arg->argv+1, NULL, 0);
    } else { /* fixed string */
      EveArg2Text(arg->argc, arg->argv, *value, maxsize);
    }
    SCparChange(arg->pCon);
    /* fall through */
  case fmtOp:
    if (*value == NULL) value = &empty;
    EveOut(arg, name, *value);
    break;
  case noOp: break;
  }
}
/*----------------------------------------------------------------------------*/
void EveObPar(EveParArg *arg, int index, char *fmt, int flags) {
  char *name;
  char *endp;
  char buf[132];
  float f;
  
  name = arg->evc->pParam[index].name;
  switch (EveOp(arg, name, &fmt, arg->evc->pParam[index].iCode, flags)) {
  case setOp:
    if (arg->argc > 2) {
      arg->ret = ILLARGC;
      return;
    }
    f = strtod(arg->argv[1], &endp);
    if (endp == arg->argv[1]) {
      arg->ret = ILLNUM;
    } else {
      arg->evc->pParam[index].fVal = f;
    }
    SCparChange(arg->pCon);
    /* fall through */
  case fmtOp:
    snprintf(buf, sizeof buf, fmt, arg->evc->pParam[index].fVal);
    EveOut(arg, name, buf);
    break;
  case noOp: break;
  }
}
/*----------------------------------------------------------------------------*/
void EveObParEnum(EveParArg *arg, int index, char *list[], int flags) {
  char *name;
  int i;
  char buf[132];
  char *fmt="%d\t(%s)";
  
  name=arg->evc->pParam[index].name;
  switch (EveOp(arg, name, &fmt, arg->evc->pParam[index].iCode, flags)) {
  case setOp:
    if (arg->argc > 2) {
      arg->ret = ILLARGC;
      return;
    }
    i = EveText2Int(list, arg->argv[1]);
    if (i<0) {
      arg->ret = ILLNUM;
    } else {
      arg->evc->pParam[index].fVal = i;
    }
    SCparChange(arg->pCon);
    /* fall through */
  case fmtOp:
    i = (int)ObVal(arg->evc->pParam, index);
    snprintf(buf, sizeof buf, fmt, i, EveInt2Text(list, i));
    EveOut(arg, name, buf);
    break;
  case noOp: break;
  }
}
/*----------------------------------------------------------------------------*/
void EveEnumPar(EveParArg *arg, char *name, int *value, char *list[], int access, int flags) {
  int i;
  char buf[132];
  char *fmt="%d\t(%s)";
    
  switch (EveOp(arg, name, &fmt, access, flags)) {
  case setOp:
    if (arg->argc > 2) {
      arg->ret = ILLARGC;
      return;
    }
    i = EveText2Int(list, arg->argv[1]);
    if (i<0) {
      arg->ret = ILLNUM;
    } else {
      *value = i;
      SCparChange(arg->pCon);
    }
    /* fall through */
  case fmtOp:
    snprintf(buf, sizeof buf, fmt, *value, EveInt2Text(list, *value));
    EveOut(arg, name, buf);
    break;
  case noOp: break;
  }
}
/*----------------------------------------------------------------------------*/
void EveCmd(EveParArg *arg, char *name, EveSubCmd subcmd, int access) {

  if (arg->action == parAction) {
    if (0==strcasecmp(name, arg->argv[0])) {
      if (arg->ret) {
        arg->ret = AMBIGUOS;
        return;
      }
      if (!EveCheckRights(arg, access)) return;
      arg->ret=subcmd(arg->pCon, arg->evc, arg->argc, arg->argv);
    }
  }
}
/*----------------------------------------------------------------------------*/
void EveFloatCmd(EveParArg *arg, char *name, float *value, char *fmt,
                 FsmFunc fsmFunc, int access, int flags) {
  Eve *eve;
  EveFloatPar(arg, name, value, fmt, access, flags);
  if (arg->action == parAction && arg->argc == 2 && 0==strcasecmp(name, arg->argv[0])) {
    eve = (Eve *)arg->evc->pDriv->pPrivate;
    if (eve->todo) {
      arg->ret = BUSY;
    } else {
      eve->todo = fsmFunc;
    }
  }
}
/*----------------------------------------------------------------------------*/
#define A EVE_ACTPAR
#define L EVE_LOGPAR
#define S EVE_SAVEPAR

void EveStdPar(EveParArg *arg) {
  static char *errList[]={"noop", "pause", "interrupt", "safeValue", "script", NULL };
  static char *intList[]={"continue", "abortOp", "abortScan", "abortBatch", NULL };
  float value;
  int eh;
  
  EveObPar(arg, UPLIMIT, "%.5g\tK", A + S);
  EveObPar(arg, LOWLIMIT, "%.5g\tK", A + S);
  EveObPar(arg, TOLERANCE, "%.5g\tK", A + S);
  EveObParEnum(arg, ERRORHANDLER, errList, A + S);
  eh = (int)ObVal(arg->evc->pParam, ERRORHANDLER);
  EveObParEnum(arg, INTERRUPT, intList, (eh == 2) * A + S);
  EveObPar(arg, SAFEVALUE, "%.5g\tK", (eh == 3) * A + S);
  EveStrPar(arg, "errorScript", &arg->evc->errorScript, 0, usUser, (eh == 4) * A + S);
  EveObPar(arg, MAXWAIT, "%.0f\tsec", A + S);
  EveObPar(arg, SETTLE, "%.0f\tsec", A + S);
}
/*----------------------------------------------------------------------------*/
void EveStdParEnd(EveParArg *arg, char *fmt, int targetFlag) {
  static char *accessList[]={"internal", "manager", "user", "spy", NULL };
  float value;
  pEVControl evc = arg->evc;
  Eve *eve = evc->pDriv->pPrivate;
    
  EveIntPar(arg, "verbose", &eve->verbose, usUser, S);
  EveIntPar(arg, "period", &eve->period, usUser, S);
  EveObParEnum(arg, ACCESS, accessList, A+S);
  EveStrPar(arg, "driver", &evc->driverName, 0, usInternal, A);
  EveFloatPar(arg, "targetValue", &evc->fTarget, fmt, usInternal, targetFlag);
  EveFloatPar(arg, "", &eve->value, fmt, usInternal, A+L);
}
/*----------------------------------------------------------------------------*/
SConnection *EveArgConn(EveParArg *arg) {
  return arg->pCon;
}
/*----------------------------------------------------------------------------*/
int EveUserAction(EveParArg *arg) {
  return (arg->action == parAction || arg->action == listAction);
}
/*----------------------------------------------------------------------------*/
int EveWrapper(SConnection *pCon, SicsInterp *pSics, void *pData,
               int argc, char *argv[]) {
  pEVControl evc = pData;
  EveParArg arg;
  Eve *eve = evc->pDriv->pPrivate;
  
  if (eve->state == lostState) {
    SCPrintf(pCon, eError, "%s does not respond", evc->pDes->name);
    return -1;
  }
  arg.pCon = pCon;
  arg.evc = evc;
  arg.ret = 0;
  if (argc == 2 && 0==strcasecmp(argv[1], "list")) {
    arg.action = listAction;
    eve->pardef(evc->pDriv->pPrivate, &arg);
    return 1;
  }
  if (argc == 3 && (0 == strcasecmp(argv[1], "log")
                 || 0 == strcasecmp(argv[1], "unlog"))) {
    if (!eve->par) EveMakePar(eve);
    arg.action = logSwitchAction;
    arg.argc = argc - 1;
    arg.argv = argv + 1;
    arg.idx = 0;
    eve->pardef(eve, &arg);
  } else {
    arg.action = parAction;
    arg.argc = argc - 1;
    arg.argv = argv + 1;
    if (argc > 1) eve->pardef(eve, &arg);
    if (arg.ret == 0) { /* forward to standard EVController */
      return EVControlWrapper(pCon,pSics,pData,argc,argv);
    }
  }
error:
  switch (arg.ret) {
  case AMBIGUOS:
    SCPrintf(pCon, eError, "ERROR: doubly defined parameter %s.%s", evc->pName, argv[1]);
    break;
  case ILLPRIV:
    SCPrintf(pCon, eError, "ERROR: Insufficient privilege to change %s.%s", evc->pName, argv[1]);
    break;
  case ILLNUM:
    SCPrintf(pCon, eError, "ERROR: illegal value", evc->pName, argv[2]);
    break;
  case ILLARGC:
    SCPrintf(pCon, eError, "ERROR: illegal number of arguments for %s %s", evc->pName, argv[1]);
    break;
  case BADLOG:
    SCPrintf(pCon, eError, "ERROR: can not create log directory for %s %s", evc->pName, argv[1]);
    break;
  case BUSY:
    SCPrintf(pCon, eError, "ERROR: %s busy", evc->pName);
    break;
  default:
    return arg.ret;
  }
  return -1;
}
/*----------------------------------------------------------------------------*/
void EveWriteError(Eve *eve) {
  switch (eve->errCode) {
  case NOTCONNECTED:
    EvePrintf(eve, eError, "terminalserver for %s not connected", eve->evc->pName);
    break;
  case FAILEDCONNECT:
    EvePrintf(eve, eError, "can not connect to %s:%d", eve->ser->pHost, eve->ser->iPort);
    break;
  case TIMEOUT:
    EvePrintf(eve, eError, "timeout on %s", eve->evc->pName);
    break;
  case INCOMPLETE:
    EvePrintf(eve, eError, "incomplete answer %s from %s", eve->ans, eve->evc->pName);
    break;
  case EVE_ILL_ANS:
    EvePrintf(eve, eError, "illegal answer %s from %s (cmd: %*s)",
      eve->ans, eve->evc->pName, strlen(eve->cmd)-1, eve->cmd);
    break;
  case EVE_DEV_CHANGED:
    EvePrintf(eve, eError, "controller was exchanged on %s", eve->evc->pName);
    break;
  case EVE_FAULT:
    EvePrintf(eve, eError, "error on %s", eve->evc->pName);
    break;
  default:
    EvePrintf(eve, eError, "error code %d on %s", eve->errCode, eve->evc->pName);
    break;
  }
}
/*----------------------------------------------------------------------------*/
void EveWrite(Eve *eve, char *cmd) {
  int iRet;
  char trash[64];
  int l, iret;
  
  if (eve->errCode || eve->state == expectState) return;
  while (availableRS232(eve->ser) == 1) {
    l=sizeof(trash);
    iret = readRS232TillTerm(eve->ser, trash, &l);
    EvePrintf(eve, -2, "trash: %s\n", trash);
  }
  snprintf(eve->cmd, sizeof(eve->cmd), "%s\n", cmd);
  iRet = writeRS232(eve->ser, eve->cmd, strlen(eve->cmd));
  EvePrintf(eve, -2, "cmd: %s", cmd);
  eve->state = expectState;
  eve->cmdtime = time(NULL);
}
/*----------------------------------------------------------------------------*/
void EveWaitRead(Eve *eve) {
  int cnt=0;
  FsmPause(eve->task, 1);
  while (eve->state == expectState) {
    TaskYield(pServ->pTasker); /* wait for handling the last message */
    cnt++;
  }
  FsmPause(eve->task, 0);
  /* if (cnt>1) printf("TaskYield %d\n", cnt); */
}
/*----------------------------------------------------------------------------*/
int EveHandler(Eve *eve) {
    
  if (eve->state == expectState) return 0;
  if (eve->errCode) {
    eve->hwstate = HWFault;
    EveWriteError(eve);
    eve->errCode = 0;
    FsmStop(eve->task, eve->run);
    return 0;
  }
  if (eve->state == lostState) {
    if (FsmStop(eve->task, eve->run)) {
      EvePrintf(eve, -1, "stop %s caused by timeout", eve->evc->pName);
    }
    return 0;
  }
  if (eve->evc->iStop && eve->hwstate == HWBusy) {
    if (FsmStop(eve->task, eve->run)) {
      EvePrintf(eve, -1, "%s stopped", eve->evc->pName);
      return 0;
    }
  }
  return 1;
}
/*----------------------------------------------------------------------------*/
int EveIdle(long pc, Eve *eve) {
  FsmFunc todo;
  struct timeval tm;
  FSM_BEGIN
  
  idle:
    if (!eve->todo) {
      if (eve->hwstate == HWBusy) {
        eve->hwstate = HWIdle;
      }
      goto rd;
    }
  doit:
    todo = eve->todo;
    eve->todo = NULL;
  FSM_CALL(todo);

  rd:
  FSM_CALL(eve->read);
    EveLog(eve); /* just for the case EveLog was not included in the read function */
    if (eve->logtime == 0) eve->logtime = 1;
    if (eve->todo) goto doit;
    /*
    gettimeofday(&tm, NULL);
    printf("stop  %s %f\n", eve->evc->pName, tm.tv_usec / 1e6);
    */
  FSM_WAIT(1)
    /*
    gettimeofday(&tm, NULL);
    printf("start %s %f\n", eve->evc->pName, tm.tv_usec / 1e6);
    */
    goto idle;
  
  /* never return */
  FSM_END
}
/*----------------------------------------------------------------------------*/
static int EveGet(pEVDriver driver, float *fPos) {
  Eve *eve = driver->pPrivate;
   
  assert(driver);
  assert(eve);   

  *fPos=eve->value;
  return 1;
} 
/*----------------------------------------------------------------------------*/
int EveRun(pEVDriver driver, float fVal) {
  Eve *eve = driver->pPrivate;
  
  assert(driver && eve);

  if (! eve->run) {
    EvePrintf(eve, -1, "can not run %s", eve->evc->pName);
    return 0;
  }
  eve->todo = eve->run;
  if (FsmStop(eve->task, eve->run)) {
    EvePrintf(eve, -1, "running %s cancelled", eve->evc->pName);
  }
  eve->hwstate = HWBusy;
  return 1;
}
/*--------------------------------------------------------------------------*/
int EveSend(pEVDriver driver, char *pCommand, char *pReply, int iLen) {
  Eve *eve = driver->pPrivate;
  int iRet;
    
  assert(driver && eve);
  
  EveWaitRead(eve);
  if (eve->errCode) {
    EveWriteError(eve);
    return 0;
  }
  EvePrintf(eve, -2, "cmd: %s", pCommand);
  iRet  = transactRS232(eve->ser, pCommand, strlen(pCommand), pReply, iLen);
  EvePrintf(eve, -2, "ans: %s", pReply);
  if(iRet < 0)
  {
    eve->errCode = iRet;
    EveWriteError(eve);
    return 0;
  }
  return 1;
}
/*-------------------------------------------------------------------------*/
int EveCheckStatus(void *evcVoid, SConnection *pCon) {
  pEVControl evc=evcVoid;
  pEVDriver driver;
  Eve *eve;
  SConnection *con;
  
  assert(evc);
  driver = evc->pDriv;
  assert(driver);
  eve = driver->pPrivate;

  con = SCLoad(&evc->conn);
  if (con != pCon) { /* not sure if this is the case in any situation */
    if (con) {
      SCWrite(con, "no more infos", eWarning);
    }
    SCWrite(pCon, "infos now here", eWarning);
    SCSave(&evc->conn, pCon);
  }
  return eve->hwstate;
}
/*-------------------------------------------------------------------------*/
EVMode EveGetMode(void *data) {
  /* EveGetMode is called in the SICS task loop as long as evc lives
     and is not been explicitely unregistered */
  pEVControl evc = data;
  pEVDriver driver;
  Eve *eve = evc->pDriv->pPrivate;
  
  if (eve->task != NULL) {
    FsmTaskHandler(eve->task);
  }
  return evc->eMode;
}
/*-------------------------------------------------------------------------*/
int EveClose(pEVDriver driver) {
  /* empty routine */
  return 1;
}
/*-------------------------------------------------------------------------*/
int EveAlwaysOk(void *data) {
  return 1;
}
/*-------------------------------------------------------------------------*/
int EveDontFix(pEVDriver driver, int iError) {
  return(DEVFAULT);      /* severe */
}
/*-------------------------------------------------------------------------*/
int EveSavePars(pEVDriver driver, FILE *fil)
{
  Eve *eve = driver->pPrivate;
  EveParArg arg;
  
  arg.action = saveAction;
  arg.pCon = NULL;
  arg.evc = eve->evc;
  arg.fil = fil;
  eve->pardef(eve, &arg);
  return 2; /* no (duplicate) save of standard parameters ! */
}
/*-------------------------------------------------------------------------*/
void EveKill(void *pData) {
  Eve *eve = pData;
  EvePar *p;
  Logger *log;
  int i;
  
  assert(eve);
  KillRS232(eve->ser);
  for (i=0; i<eve->npar; i++) {
    if (eve->par[i].log) {
      LoggerKill(eve->par[i].log);
    }
    eve->par[i].log = NULL;
  }
  free(eve);
}
/*--------------------------------------------------------------------------*/
int EveGetError(pEVDriver driver, int *iCode, char *error, int iErrLen) {
  Eve *eve = driver->pPrivate;

  *iCode = 1; /* severe */
  strncpy(error, eve->errMsg, iErrLen);
  error[iErrLen-1]='\0';
  eve->errMsg[0]='\0';
  return 1;
}
/*----------------------------------------------------------------------------*/
int EveInit(pEVDriver driver) {
  Eve *eve;
  int iRet;
  char *res, buf[128];
  pExeList exe;
  
  assert(driver);
  eve = driver->pPrivate;
  eve->errCode = 0;
  eve->state = idleState;
  eve->version[0] = '\0';
  eve->errMsg[0]='\0';
  
  iRet = initRS232(eve->ser);
  if (iRet != 1) {
    eve->errCode = iRet;
    EveWriteError(eve);
    return -1;
  }
  setRS232ReplyTerminator(eve->ser,"\r");
  setRS232SendTerminator(eve->ser,"\r");
  setRS232Timeout(eve->ser,1000); /* milliseconds */
  setRS232Debug(eve->ser,0);

  return 1;
}
/*----------------------------------------------------------------------------*/
pEVControl MakeEveEVC(int argc, char *argv[], Eve *eve, SConnection *pCon) {

  /* args:
     <objectname> <driver> <rs232>
                           <host> <port>
   */ 
  pEVDriver driver = NULL;
  int port;
  rs232 *ser;
  pEVControl evc;
  
  if (argc < 3 || argc > 4) {
    SCWrite(pCon, "illegal number of arguments", eError);
    goto error;
  }
  driver = CreateEVDriver(0,NULL); /* arguments obsolete */
  if(!driver || !eve) {
    SCWrite(pCon, "ERROR: out of memory", eError);
    goto error;
  }
  
  driver->pPrivate = eve;
  driver->KillPrivate = EveKill;
      
  /* initalise IpsDriver */
  eve->evc = NULL;
  eve->hwstate = HWIdle;
  eve->task = NULL;
  eve->run = NULL;
  eve->logtime = 0;
  eve->period = 5;
  eve->par = NULL;
  
  if (argc == 4) {
    port=atoi(argv[3]);
    if (port==0) {
      SCWrite(pCon, "ERROR: illegal port number", eError);
      goto error;
    }
    ser = createRS232(argv[2], port);
    if (ser == NULL) {
      SCWrite(pCon, "out of memory", eError);
      goto error;
    }
  } else {
    ser = FindCommandData(pServ->pSics, argv[2], "RS232 Controller");
    if (ser == NULL) {
      SCWrite(pCon, "ERROR: rs232 not found", eError);
      goto error;
    }
  }
  eve->ser = ser;
  
  /* initialise function pointers */
  driver->GetValue  = EveGet;
  driver->SetValue  = EveRun;
  driver->Send      = EveSend;
  driver->GetError  = EveGetError;
  driver->TryFixIt  = EveDontFix;
  driver->SavePars  = EveSavePars;
  driver->Init      = EveInit;
  driver->Close     = EveClose;

  evc = MakeEVController(driver, pCon, EveWrapper, argc, argv);
  if (!evc) return NULL;
  
  eve->evc=evc;
  eve->hwstate = HWIdle;
  eve->verbose = 1;
  SCSave(&eve->evc->conn, pCon);
  eve->checkStatus = evc->pDrivInt->CheckStatus;
  evc->pDrivInt->CheckStatus = EveCheckStatus;
  evc->pEnvir->GetMode = EveGetMode;
  return evc;

error:
  if (driver) {
    free(driver);
    driver = NULL;
  }
  /* write out error message */
  return MakeEVController(driver, pCon, EveWrapper, argc, argv);
}
/*----------------------------------------------------------------------------*/
