/*---------------------------------------------------------------------------
oxinst.c

Communication routines for Oxford Instruments equipment
  
Markus Zolliker, March 2005
----------------------------------------------------------------------------

there is no error return value, eab->errCode is used. On success, eab->errCode
is not changed, i.e. an existing errCode is not overwritten.
*/

#include <string.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <fortify.h>
#include "sics.h"
#include "oxinst.h"

/*----------------------------------------------------------------------------*/
char *OxiCorrect(char *str)
{
  /* there are sometimes communication errors with the IGH
     as the errors always follow the same pattern, they can
     be corrected, with the following code. The argument
     is corrected in place, the result is NULL or a text
     describing the conversion */
  int i;
  unsigned char chr;
  static char buf[32];
  char *result = NULL;

  if (str[0] == '?')
    return NULL;
  for (i = 0; i <= 24; i++, str++) {
    chr = *str;
    if (chr == 0)
      return result;
    if (chr > 0x60) {
      if (chr > 0xC0) {
        chr -= 0x80;
      } else {
        chr -= 0x40;
      }
      snprintf(buf, sizeof buf, "%2.2x->%2.2x (%c)", (unsigned char) *str,
               chr, chr);
      *str = chr;
      result = buf;
    }
  }
  if (result) {
    strcat(buf, " overflow");
    return result;
  } else {
    return "overflow";
  }
}

/*----------------------------------------------------------------------------*/
int OxiHandler(void *object)
{
  int iret, l;
  EaseBase *eab = EaseBaseCast(object);
  char *corr;

  if (eab->state < EASE_idle)
    goto quit;
  if (availableNetRS232(eab->ser) || availableRS232(eab->ser)) {
    if (strncmp(eab->msg, "ERROR:", 6) != 0) {
      eab->msg[0] = '\0';
    }
    l = sizeof(eab->ans);
    iret = readRS232TillTerm(eab->ser, eab->ans, &l);
    if (eab->state != EASE_expect && eab->state != EASE_lost) {
      if (iret == 1) {
        ParPrintf(eab, eError, "unexpected answer: %s", eab->ans);
      }
      goto quit;
    }
    if (iret == 1) {
      ParPrintf(eab, -2, "ans: %s", eab->ans);
      if (strcmp(eab->ans, "??ck") == 0) {
        if (eab->state == EASE_lost) {
          EaseWrite(eab, "V");
        }
        goto quit;
      } else if (eab->state == EASE_lost) {
        goto quit;
      } else if (eab->cmd[0] == 'V') {
        if (strcmp(eab->ans, eab->version) == 0) {
          /* we are still connected with the same device */
        } else if (*eab->version == '\0') {
          strlcpy(eab->version, eab->ans, sizeof(eab->version));
        } else {                /* version (and therefore device) changed */
          eab->errCode = EASE_DEV_CHANGED;
          eab->state = EASE_idle;
          goto error;
        }
        eab->state = EASE_idle;
        goto quit;
      } else if (eab->cmd[2] == 'k') {  /* ?ck */
      } else {
        eab->tmo = 20; /* long timeout: the user may block the response with the adjust keys */ 
        if (eab->syntax <= -8) { /* OLDIGH */
          corr = OxiCorrect(eab->ans);
          if (corr) {
/*            ParPrintf(eab, eWarning, "corrected bad response from IGH: %s", corr); */
          }
        }
        if (eab->cmd[0] != eab->ans[0] && eab->cmd[0] != '{') {
          if (strcmp(eab->cmd, "H1\r") != 0 || strcmp(eab->ans, "?H1") != 0) {
            iret = EASE_ILL_ANS;
          }
        }
      }
    }
    if (iret != 1) {
      eab->errCode = iret;
      eab->state = EASE_idle;
      goto error;
    }
    eab->state = EASE_read;
  } else if (eab->state == EASE_expect) {
    if (time(NULL) > eab->chktime) {
      writeRS232(eab->ser, "?ck", 3);
      ParPrintf(eab, -2, "cmd: ?ck");
      eab->chktime = time(NULL);
    }
    if (time(NULL) > eab->cmdtime + eab->tmo) {
      eab->state = EASE_lost;
    }
  } else if (eab->state == EASE_lost) {
    if (time(NULL) > eab->cmdtime) {
      EaseWrite(eab, "?ck");
      eab->state = EASE_lost;
    }
  }
  goto quit;
error:
  /* EaseWriteError(eab); */
quit:
  return EaseHandler(eab);
}

double OxiGet(EaseBase * eab, int dig, int *pdig, double old)
{
  char *endp, *p;
  double val;

  if (eab->state != EASE_read) {
    eab->errCode = EASE_ILL_ANS;
    return old;
  }
  p = strchr(eab->ans, '.');
  if (p) {
    if (pdig != NULL) {
      *pdig = strlen(eab->ans) - (p - eab->ans) - 1;
    }
    val = strtod(eab->ans + 1, &endp);
    if (*endp != '\0') {
      eab->errCode = EASE_ILL_ANS;
      return old;
    }
  } else {
    val = strtol(eab->ans + 1, &endp, 10);
    if (*endp != '\0') {
      eab->errCode = EASE_ILL_ANS;
      return old;
    }
    if (eab->syntax <= 0) {     /* old style format */
      for (; dig > 0; dig--)
        val = val * 0.1;
    }
  }
  return val;
}

void OxiSet(EaseBase * eab, char *cmd, double val, int dig)
{
  char buf[64];
  double f;

  if (eab->syntax > 0) {
    f = fabs(val);
    if (f < 1.0)
      f = 1.0;
    dig = 7 - log10(f);
    if (dig < 0)
      dig = 0;
  }
  snprintf(buf, sizeof(buf), "%s%.*f", cmd, dig, val);
  EaseWrite(eab, buf);
}
