/*--------------------------------------------------------------------------

                         D I L U T I L
                       
     A few utility functions for dealing with a Dillution emperature controller
     CC0-510/AVSI
     within the SINQ setup: host -- TCP/IP -- MAC --- RS-232.
     
     Mark Koennecke, October 1997

       Copyright: see copyrigh.h
----------------------------------------------------------------------------*/
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <assert.h>
#include "serialsinq.h"
#include "dillutil.h"

#ifdef FORTIFY
#include "fortify.h"
#endif

/* 
#define debug 1
*/
/*-------------------------------------------------------------------------*/

int DILLU_Open(pDILLU * pData, char *pHost, int iPort, int iChannel,
               int iMode, char *pTransFile)
{
  int iRet;
  char pCommand[80];
  char pReply[132];
  pDILLU self = NULL;
  pSTable pTable = NULL;
  FILE *fd = NULL;

  /* check translation file first */
  fd = fopen(pTransFile, "r");
  if (!fd) {
    return DILLU__FILENOTFOUND;
  }
  fgets(pReply, 131, fd);
  if (strstr(pReply, "DILLUTION") == NULL) {
    fclose(fd);
    return DILLU__NODILLFILE;
  }

  pTable = CreateTable(fd);
  fclose(fd);
  if (!pTable) {
    return DILLU__ERRORTABLE;
  }

  /* allocate a new data structure */
  self = (pDILLU) malloc(sizeof(DILLU));
  if (self == NULL) {
    return DILLU__BADMALLOC;
  }

  *pData = self;
  self->pTranstable = pTable;

  iRet = SerialOpen(&self->pData, pHost, iPort, iChannel);
  if (iRet != 1) {
    return iRet;
  }

  /* set an lengthy timeout for the configuration in order to
     prevent problems.
   */
  iRet = SerialConfig(&self->pData, 100);
  if (iRet != 1) {
    return iRet;
  }

  self->iReadOnly = iMode;
  if (!self->iReadOnly) {
    /* switch to remote operation */
/*         iRet = SerialWriteRead(&self->pData,"C1\r\n",pReply,131);
         if(iRet != 1)
         { 
           return iRet;
         }
*/
  }
  return 1;
}

/*--------------------------------------------------------------------------*/
void DILLU_Close(pDILLU * pData)
{
  char pReply[132];
  int iRet;
  pDILLU self;

  self = *pData;

  if (!self)
    return;

  /* switch to local operation */
  iRet = SerialWriteRead(&self->pData, "C0\r\n", pReply, 131);
  /* ignore errors on this one, the thing may be down */

  /* close connection */
  SerialClose(&self->pData);

  /* free memory */
  free(self);
  *pData = NULL;
}

/*--------------------------------------------------------------------------*/
int DILLU_Config(pDILLU * pData, int iTmo)
{
  int iRet;
  char pReply[132];
  char pCommand[10];
  pDILLU self;

  self = *pData;

  /* first timeout */
  if (iTmo > 0) {
    iRet = SerialConfig(&self->pData, iTmo);
    if (iRet < 0) {
      return iRet;
    }
  }
  return 1;
}

/*--------------------------------------------------------------------------*/
int DILLU_Send(pDILLU * pData, char *pCommand, char *pReply, int iLen)
{
  pDILLU self;

  self = *pData;

  /* make sure, that there is a \r at the end of the command */
  if (strchr(pCommand, (int) '\r') == NULL) {
    strcat(pCommand, "\r\n");
  }
  return SerialWriteRead(&self->pData, pCommand, pReply, iLen);
}

/*--------------------------------------------------------------------------*/
int DILLU_Read(pDILLU * pData, float *fVal)
{
  char pCommand[10], pReply[132];
  int iRet;
  float fRead = -9999.;
  float fOhm;
  pDILLU self;

  self = *pData;


  /* send D command */
  sprintf(pCommand, "D\r\n");
  iRet = SerialWriteRead(&self->pData, pCommand, pReply, 131);
  if (iRet != 1) {
    return iRet;
  }

  /* read ohms */
  iRet = sscanf(pReply, "%f", &fOhm);
  if (iRet != 1) {
    return DILLU__BADREAD;
  }
  if (fOhm > 9999890.) {
    return DILLU__SILLYANSWER;
  }

  /* convert to K */
  iRet = InterpolateVal2(self->pTranstable, fOhm, &fRead);
  *fVal = fRead;
  return 1;
}

/*-------------------------------------------------------------------------*/
int DILLU_Set(pDILLU * pData, float fVal)
{
  char pCommand[50], pReply[132];
  int iRet, i, iRange, iExec;
  const float fPrecision = 0.0001;
  float fSet, fRead, fOhms, tmax, fTemp;
  pDILLU self;

  self = *pData;

  if (self->iReadOnly) {
    return DILLU__READONLY;
  }

  /* send D command to read current value */
  sprintf(pCommand, "D\r\n");
  iRet = SerialWriteRead(&self->pData, pCommand, pReply, 131);
  if (iRet != 1) {
    return iRet;
  }

  /* read ohms */
  iRet = sscanf(pReply, "%f", &fRead);
  if (iRet != 1) {
    return DILLU__BADREAD;
  }
  if (fRead > 9999890.) {
    return DILLU__SILLYANSWER;
  }


  /* convert new set value to ohms */
  iRet = InterpolateVal1(self->pTranstable, fVal, &fOhms);
  if (!iRet) {
    return DILLU__OUTOFRANGE;
  }

  /* set to remote operation */
#ifdef debug
  printf("C1\n");
#endif
  iRet = SerialWriteRead(&self->pData, "C1\r\n", pReply, 131);
  if (iRet != 1) {
    return iRet;
  }

  /* set heater power */
  strcpy(pCommand, "G3\r");
  if (fOhms > 1125) {
    strcpy(pCommand, "G2\r");
  }
  if (fOhms > 4000)
    strcpy(pCommand, "G1\r");
#ifdef debug
  printf("A9\n");
#endif
  iRet = SerialWriteRead(&self->pData, "A9\r", pReply, 131);
  if (iRet != 1) {
    return iRet;
  }
#ifdef debug
  printf("%s\n", pCommand);
#endif
  iRet = SerialWriteRead(&self->pData, pCommand, pReply, 131);
  if (iRet != 1) {
    return iRet;
  }

  /* Integrator time constant */
  strcpy(pCommand, "G2\r");
  if (fOhms > 200)
    strcpy(pCommand, "G1\r");
  if (fOhms > 2000)
    strcpy(pCommand, "G0\r");
  strcpy(pCommand, "G7\r");
  if (fOhms > 400.) {
    strcpy(pCommand, "G6\r");
  }
#ifdef debug
  printf("A4\n");
#endif
  iRet = SerialWriteRead(&self->pData, "A4\r", pReply, 131);
  if (iRet != 1) {
    return iRet;
  }
#ifdef debug
  printf("%s\n", pCommand);
#endif
  iRet = SerialWriteRead(&self->pData, pCommand, pReply, 131);
  if (iRet != 1) {
    return iRet;
  }

  /* derivator time constant */
  if (fOhms > 1000.) {
    strcpy(pCommand, "G1\r");
  } else {
    strcpy(pCommand, "G2\r");
  }
#ifdef debug
  printf("A5\n");
#endif
  iRet = SerialWriteRead(&self->pData, "A5\r", pReply, 131);
  if (iRet != 1) {
    return iRet;
  }
#ifdef debug
  printf("%s\n", pCommand);
  iRet = 1;
#endif
  iRet = SerialWriteRead(&self->pData, pCommand, pReply, 131);
  if (iRet != 1) {
    return iRet;
  }

  /* proportional gain */
  if (fOhms > 500.) {
    strcpy(pCommand, "G3\r");
  }
  if (fOhms > 1000) {
    strcpy(pCommand, "G2\r");
  }
  if (fOhms > 2000) {
    strcpy(pCommand, "G1\r");
  }
#ifdef debug
  printf("A6\n");
#endif
  iRet = SerialWriteRead(&self->pData, "A6\r", pReply, 131);
  if (iRet != 1) {
    return iRet;
  }
#ifdef debug
  printf("%s\n", pCommand);
#endif
  iRet = SerialWriteRead(&self->pData, pCommand, pReply, 131);
  if (iRet != 1) {
    return iRet;
  }

  /* range calculation a la Elsenhans */
  iRange = 1;
  fTemp = fOhms * 10000.;
  if ((fRead > 1.9) || (fOhms > 1.9)) {
    iRange = 2;
    fTemp = fOhms * 1000.;
  }
  if ((fRead > 19) || (fOhms > 19)) {
    iRange = 3;
    fTemp = fOhms * 100.;
  }
  if ((fRead > 190) || (fOhms > 190)) {
    iRange = 4;
    fTemp = fOhms * 10.;
  }
  if ((fRead > 750) || (fOhms > 750)) {
    iRange = 5;
    fTemp = fOhms;
  }
  if ((fRead > 19000) || (fOhms > 19000)) {
    iRange = 6;
    fTemp = fOhms / 10.;
  }
  if ((fRead > 190000) || (fOhms > 190000)) {
    iRange = 7;
    fTemp = fOhms / 100.;
  }

  sprintf(pCommand, "R%1.1d\r", iRange);
#ifdef debug
  printf("%s\n", pCommand);
#endif
  iRet = SerialWriteRead(&self->pData, pCommand, pReply, 131);
  if (iRet != 1) {
    return iRet;
  }


  /* finally set temperature */
#ifdef debug
  printf("Set Val befor hex: %d\n", (int) fTemp);
#endif
  sprintf(pCommand, "G%4.4X\r", (int) fTemp);
#ifdef debug
  printf("A3\n");
#endif
  iRet = SerialWriteRead(&self->pData, "A3\r", pReply, 131);
  if (iRet != 1) {
    return iRet;
  }
#ifdef debug
  printf("%s\n", pCommand);
#endif
  iRet = SerialWriteRead(&self->pData, pCommand, pReply, 131);
  if (iRet != 1) {
    return iRet;
  }

  /* unset remote operation, so that users may mess everything up
     from the panel 
   */
#ifdef debug
  printf("C1\n");
#endif
  iRet = SerialWriteRead(&self->pData, "C0\r\n", pReply, 131);
  if (iRet != 1) {
    return iRet;
  }

  return 1;
}

/*-------------------------------------------------------------------------*/
void DILLU_Error2Text(pDILLU * pData, int iCode, char *pError, int iLen)
{
  char pBueffel[512];
  pDILLU self;

  self = *pData;

  switch (iCode) {
  case DILLU__FILENOTFOUND:
    strncpy(pError, "Translation Table file not found", iLen);
    return;
    break;
  case DILLU__NODILLFILE:
    strncpy(pError, "Translation Table file is not DILLU", iLen);
    return;
    break;
  case DILLU__ERRORTABLE:
    strncpy(pError, "Translation Table could not be created", iLen);
    return;
    break;
  case DILLU__BADREAD:
    strncpy(pError, "Message corrupted", iLen);
    return;
    break;
  case DILLU__SILLYANSWER:
    strncpy(pError, "Message corrupted", iLen);
    return;
    break;
  case DILLU__BADMALLOC:
    strncpy(pError, "Out of memory in Open_DILLU", iLen);
    return;
    break;
  case DILLU__READONLY:
    strncpy(pError, "DILLU is read-only", iLen);
    return;
    break;
  case DILLU__OUTOFRANGE:
    strncpy(pError, "Requested value is out of range", iLen);
    return;
    break;
  default:
    SerialError(iCode, pError, iLen);
    break;
  }
}
