/*----------------------------------------------------------------------
      This is driver for a standard PSI EL737 counter box as used at 
      SINQ

      copyright: see file COPYRIGHT

      extracted from countdriv.c. Mark Koennecke, June 2003
 -----------------------------------------------------------------------*/
#include <stdlib.h>
#include <assert.h>
#include <math.h>
#include <fortify.h>
#include <string.h>
#include <sics.h>
#include <countdriv.h>
#include <splitter.h>
#include "hardsup/sinq_prototypes.h"
#include "hardsup/el737_def.h"
#include "hardsup/el737fix.h"

#define BADTRANGE -117766
/*----------------------------- EL737 ------------------------------------*/
typedef struct {
  char *host;
  int iPort;
  int iChannel;
  void *pData;
  int finishCount;
} EL737st;
/*------------------------------------------------------------------------*/
static int EL737GetStatus(struct __COUNTER *self, float *fControl)
{
  int iRet;
  int iC1, iC2, iC3, iC4, iRS;
  float fTime;
  EL737st *pEL737;

  assert(self);
  pEL737 = (EL737st *) self->pData;
  assert(pEL737);
  iRet =
      EL737_GetStatus(&pEL737->pData, &iC1, &iC2, &iC3, &iC4, &fTime,
                      &iRS);
  if (self->eMode == eTimer) {
    *fControl = fTime;
  } else {
    *fControl = iC1;
  }
  /* store time */
  self->fTime = fTime;

  if (iRet != 1) {
    return HWFault;
  }
  self->lCounts[0] = iC2;
  self->lCounts[1] = iC1;
  self->lCounts[2] = iC3;
  self->lCounts[3] = iC4;

  /* get extra counters for 8-fold counter boxes */
  iRet = EL737_GetStatusExtra(&pEL737->pData, &iC1, &iC2, &iC3, &iC4);
  self->lCounts[4] = iC1;
  self->lCounts[5] = iC2;
  self->lCounts[6] = iC3;
  self->lCounts[7] = iC4;
  if (iRS == 0) {
    pEL737->finishCount++;
    if (pEL737->finishCount >= 2) {
      return HWIdle;
    } else {
      return HWBusy;
    }
  } else if ((iRS == 1) || (iRS == 2)) {
    pEL737->finishCount = 0;
    return HWBusy;
  } else if ((iRS == 5) || (iRS == 6)) {
    pEL737->finishCount = 0;
    return HWNoBeam;
  } else {
    pEL737->finishCount = 0;
    return HWPause;
  }
}

#ifdef NONINTF
extern float nintf(float f);
#endif
/*-------------------------------------------------------------------------*/
static int EL737Start(struct __COUNTER *self)
{
  int iRet, iRS;
  EL737st *pEL737;

  assert(self);
  pEL737 = (EL737st *) self->pData;
  assert(pEL737);


  self->fTime = 0.;

  if (self->eMode == ePreset) {
    iRet =
        EL737_StartCnt(&pEL737->pData, (int) nintf(self->fPreset), &iRS);
    if (iRet == 1) {
      pEL737->finishCount = 0;
      return OKOK;
    } else {
      return HWFault;
    }
  } else if (self->eMode == eTimer) {
    if (self->fPreset < .1 || self->fPreset > 200000) {
      self->iErrorCode = BADTRANGE;
      return HWFault;
    }
    iRet = EL737_StartTime(&pEL737->pData, self->fPreset, &iRS);
    if (iRet == 1) {
      pEL737->finishCount = 0;
      return OKOK;
    } else {
      return HWFault;
    }
  }
  return 0;
}

/*-------------------------------------------------------------------------*/
static int EL737Pause(struct __COUNTER *self)
{
  int iRet, iRS;
  EL737st *pEL737;

  assert(self);
  pEL737 = (EL737st *) self->pData;
  assert(pEL737);


  iRet = EL737_Pause(&pEL737->pData, &iRS);
  if (iRet == 1) {
    return OKOK;
  } else {
    return HWFault;
  }
  return 0;
}

/*-------------------------------------------------------------------------*/
static int EL737Continue(struct __COUNTER *self)
{
  int iRet, iRS;
  EL737st *pEL737;

  assert(self);
  pEL737 = (EL737st *) self->pData;
  assert(pEL737);


  iRet = EL737_Continue(&pEL737->pData, &iRS);
  if (iRet == 1) {
    return OKOK;
  } else {
    return HWFault;
  }
  return 0;
}

/*--------------------------------------------------------------------------*/
static int EL737Halt(struct __COUNTER *self)
{
  int iRet, iC1, iC2, iC3, iC4, iRS;
  float fPreset;
  EL737st *pEL737;

  assert(self);
  pEL737 = (EL737st *) self->pData;
  assert(pEL737);



  iRet =
      EL737_Stop(&pEL737->pData, &iC1, &iC2, &iC3, &iC4, &fPreset, &iRS);
  if (iRet == 1) {
    self->lCounts[0] = iC2;
    self->lCounts[1] = iC1;
    self->lCounts[2] = iC3;
    self->lCounts[3] = iC4;
    return OKOK;
  }
  return HWFault;
}

/*--------------------------------------------------------------------------*/
static int EL737ReadValues(struct __COUNTER *self)
{
  int iRet;
  int iC1, iC2, iC3, iC4, iRS;
  float fTime;
  EL737st *pEL737;

  assert(self);
  pEL737 = (EL737st *) self->pData;
  assert(pEL737);


  iRet =
      EL737_GetStatus(&pEL737->pData, &iC1, &iC2, &iC3, &iC4, &fTime,
                      &iRS);
  if (iRet != 1) {
    return HWFault;
  }
  self->fTime = fTime;

  self->lCounts[0] = iC2;
  self->lCounts[1] = iC1;
  self->lCounts[2] = iC3;
  self->lCounts[3] = iC4;
  /* get extra counters for 8-fold counter boxes */
  iRet = EL737_GetStatusExtra(&pEL737->pData, &iC1, &iC2, &iC3, &iC4);
  self->lCounts[4] = iC1;
  self->lCounts[5] = iC2;
  self->lCounts[6] = iC3;
  self->lCounts[7] = iC4;

  return OKOK;
}

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

	EL737Error2Text converts between an EL734 error code to text
-----------------------------------------------------------------------------*/
static void EL737Error2Text(char *pBuffer, int iErr)
{
  switch (iErr) {
  case EL737__BAD_ADR:
    strcpy(pBuffer, "EL737__BAD_ADR");
    break;
  case EL737__BAD_OVFL:
    strcpy(pBuffer, "EL737__BAD_OVFL");
    break;
  case EL737__BAD_BSY:
    strcpy(pBuffer, "EL737__BAD_BSY");
    break;
  case EL737__BAD_SNTX:
    strcpy(pBuffer, "EL737__BAD_SNTX");
    break;
  case EL737__BAD_CONNECT:
    strcpy(pBuffer, "EL737__BAD_CONNECT");
    break;
  case EL737__BAD_FLUSH:
    strcpy(pBuffer, "EL737__BAD_FLUSH");
    break;
  case EL737__BAD_DEV:
    strcpy(pBuffer, "EL734__BAD_DEV");
    break;
  case EL737__BAD_ID:
    strcpy(pBuffer, "EL737__BAD_ID");
    break;
  case EL737__BAD_ILLG:
    strcpy(pBuffer, "EL737__BAD_ILLG");
    break;
  case EL737__BAD_LOC:
    strcpy(pBuffer, "EL737__BAD_LOC");
    break;
  case EL737__BAD_MALLOC:
    strcpy(pBuffer, "EL737__BAD_MALLOC");
    break;
  case EL737__BAD_NOT_BCD:
    strcpy(pBuffer, "EL737__BAD_NOT_BCD");
    break;
  case EL737__BAD_OFL:
    strcpy(pBuffer, "EL737__BAD_OFL");
    break;
  case EL737__BAD_PAR:
    strcpy(pBuffer, "EL737__BAD_PAR");
    break;

  case EL737__BAD_RECV:
    strcpy(pBuffer, "EL737__BAD_RECV");
    break;
  case EL737__BAD_RECV_NET:
    strcpy(pBuffer, "EL737__BAD_RECV_NET");
    break;
  case EL737__BAD_RECV_PIPE:
    strcpy(pBuffer, "EL737__BAD_RECV_PIPE");
    break;
  case EL737__BAD_RECV_UNKN:
    strcpy(pBuffer, "EL737__BAD_RECV_UNKN");
    break;
  case EL737__BAD_RECVLEN:
    strcpy(pBuffer, "EL737__BAD_RECVLEN");
    break;
  case EL737__BAD_RECV1:
    strcpy(pBuffer, "EL737__BAD_RECV1");
    break;
  case EL737__BAD_RECV1_NET:
    strcpy(pBuffer, "EL737__BAD_RECV1_NET");
    break;
  case EL737__BAD_RECV1_PIPE:
    strcpy(pBuffer, "EL737__BAD_RECV1_PIPE");
    break;
  case EL737__BAD_RNG:
    strcpy(pBuffer, "EL737__BAD_RNG");
    break;
  case EL737__BAD_SEND:
    strcpy(pBuffer, "EL737__BAD_SEND");
    break;
  case EL737__BAD_SEND_PIPE:
    strcpy(pBuffer, "EL737__BAD_SEND_PIPE");
    break;
  case EL737__BAD_SEND_NET:
    strcpy(pBuffer, "EL737__BAD_SEND_NET");
    break;
  case EL737__BAD_SEND_UNKN:
    strcpy(pBuffer, "EL737__BAD_SEND_UNKN");
    break;
  case EL737__BAD_SENDLEN:
    strcpy(pBuffer, "EL737__BAD_SENDLEN");
    break;
  case EL737__BAD_SOCKET:
    strcpy(pBuffer, "EL737__BAD_SOCKET");
    break;
  case EL737__BAD_TMO:
    strcpy(pBuffer, "EL737__BAD_TMO");
    break;
  case EL737__FORCED_CLOSED:
    strcpy(pBuffer, "EL737__FORCED_CLOSED");
    break;
  case EL737__BAD_ASYNSRV:
    strcpy(pBuffer, "EL737__BAD_ASYNSRV");
    break;
  default:
    sprintf(pBuffer, "Unknown EL737 error %d", iErr);
    break;
  }
}

/*--------------------------------------------------------------------------*/
static int EL737GetError(struct __COUNTER *self, int *iCode,
                         char *error, int iErrLen)
{
  char *pErr = NULL;
  int iC1, iC2, iC3;
  char pBueffel[256];

  if (self->iErrorCode == UNKNOWNPAR) {
    strlcpy(error, "unknown internal parameter code", iErrLen);
    *iCode = self->iErrorCode;
    self->iErrorCode = 0;
    return 1;
  } else if (self->iErrorCode == BADCOUNTER) {
    strlcpy(error, "monitor cannot be selected", iErrLen);
    *iCode = self->iErrorCode;
    self->iErrorCode = 0;
    return 1;
  } else if (self->iErrorCode == BADTRANGE) {
    strlcpy(error, "preset time out of range", iErrLen);
    *iCode = self->iErrorCode;
    self->iErrorCode = 0;
    return 1;
  }


  EL737_ErrInfo(&pErr, &iC1, &iC2, &iC3);
  EL737Error2Text(pBueffel, iC1);

  strlcpy(error, pBueffel, iErrLen);
  *iCode = iC1;
  return 1;
}

/*--------------------------------------------------------------------------*/
static int EL737TryAndFixIt(struct __COUNTER *self, int iCode)
{
  EL737st *pEL737;
  int iRet;
  char pCommand[50], pReply[50];

  assert(self);
  pEL737 = (EL737st *) self->pData;
  assert(pEL737);

  switch (iCode) {
  case EL737__BAD_ILLG:
  case EL737__BAD_ADR:
  case EL737__BAD_PAR:
  case EL737__BAD_TMO:
  case EL737__BAD_REPLY:
  case EL737__BAD_SNTX:
  case EL737__BAD_OVFL:
    return COREDO;
    break;
  case EL737__BAD_BSY:
    strcpy(pCommand, "S \r");
    iRet = EL737_SendCmnd(&pEL737->pData, pCommand, pReply, 49);
    if (iRet < 0) {
      return COTERM;
    } else {
      return COREDO;
    }
    break;
  case EL737__BAD_LOC:
    strcpy(pCommand, "rmt 1\r");
    iRet = EL737_SendCmnd(&pEL737->pData, pCommand, pReply, 49);
    if (iRet < 0) {
      return COTERM;
    }
    strcpy(pCommand, "echo 2\r");
    iRet = EL737_SendCmnd(&pEL737->pData, pCommand, pReply, 49);
    if (iRet < 0) {
      return COTERM;
    }
    strcpy(pCommand, "ra\r");
    iRet = EL737_SendCmnd(&pEL737->pData, pCommand, pReply, 49);
    if (iRet < 0) {
      return COTERM;
    }
    return COREDO;
    break;
  case EL737__BAD_DEV:
  case EL737__BAD_ID:
  case EL737__BAD_NOT_BCD:
  case UNKNOWNPAR:
  case BADCOUNTER:
  case BADTRANGE:
    return COTERM;
    break;
  case EL737__FORCED_CLOSED:
    iRet = EL737_Open(&pEL737->pData, pEL737->host, pEL737->iPort,
                      pEL737->iChannel);
    if (iRet == 1) {
      return COREDO;
    } else {
      return COTERM;
    }
    break;
  case EL737__BAD_OFL:
    EL737_Close(&pEL737->pData, 0);
    iRet = EL737_Open(&pEL737->pData, pEL737->host, pEL737->iPort,
                      pEL737->iChannel);
    if (iRet == 1) {
      return COREDO;
    } else {
      return COTERM;
    }
    break;
/*     case EL737__BAD_ASYNSRV:
           EL737_Close(&pEL737->pData,1);
             return COREDO;
*/ default:
    /* try to reopen connection */

    EL737_Close(&pEL737->pData, 1);
    iRet = EL737_Open(&pEL737->pData, pEL737->host, pEL737->iPort,
                      pEL737->iChannel);
    if (iRet == 1) {
      return COREDO;
    } else {
      return COTERM;
    }
    break;
  }
  return COTERM;
}

/*-------------------------------------------------------------------------*/
static int EL737Set(struct __COUNTER *self, char *name, int iCter,
                    float fVal)
{
  int iRet;
  EL737st *pEL737;
  char pCommand[80], pReply[80];

  assert(self);
  pEL737 = (EL737st *) self->pData;
  assert(pEL737);

  if (strcmp(name, "threshold") == 0) {
    sprintf(pCommand, "DL %1.1d %6.2f\r", iCter, fVal);
    iRet = EL737_SendCmnd(&pEL737->pData, pCommand, pReply, 79);
    if (iRet == 1) {
      if (pReply[0] == '?') {
        self->iErrorCode = BADCOUNTER;
        return HWFault;
      }
    } else {
      return HWFault;
    }
    sprintf(pCommand, "DR %1.1d \r", iCter);
    iRet = EL737_SendCmnd(&pEL737->pData, pCommand, pReply, 79);
    if (iRet == 1) {
      if (pCommand[0] == '?') {
        self->iErrorCode = BADCOUNTER;
        return HWFault;
      }
      return OKOK;
    } else {
      return HWFault;
    }
  } else {
    self->iErrorCode = UNKNOWNPAR;
    return HWFault;
  }
}

/*-------------------------------------------------------------------------*/
static int EL737Get(struct __COUNTER *self, char *name, int iCter,
                    float *fVal)
{
  int iRet;
  EL737st *pEL737;
  char pCommand[80], pReply[80];

  assert(self);
  pEL737 = (EL737st *) self->pData;
  assert(pEL737);

  if (strcmp(name, "threshold") == 0) {
    sprintf(pCommand, "DL %1.1d\r", iCter);
    iRet = EL737_SendCmnd(&pEL737->pData, pCommand, pReply, 79);
    if (iRet == 1) {
      if (pReply[0] == '?') {
        self->iErrorCode = BADCOUNTER;
        return HWFault;
      }
      sscanf(pReply, "%f", fVal);
      return OKOK;
    } else {
      return HWFault;
    }
  } else {
    self->iErrorCode = UNKNOWNPAR;
    return HWFault;
  }
}

/*-------------------------------------------------------------------------*/
static int EL737Send(struct __COUNTER *self, char *pText, char *pReply,
                     int iReplyLen)
{
  EL737st *pEL737;
  char pBuffer[256];

  assert(self);
  pEL737 = (EL737st *) self->pData;
  assert(pEL737);

  /* ensure a \r at the end of the text */
  if (strlen(pText) > 254) {
    strlcpy(pReply, "Command to long", iReplyLen);
    return 1;
  }
  strcpy(pBuffer, pText);
  if (strchr(pBuffer, (int) '\r') == NULL) {
    strcat(pBuffer, "\r");
  }

  return EL737_SendCmnd(&pEL737->pData, pBuffer, pReply, iReplyLen);
}

/*-------------------------------------------------------------------------*/
pCounterDriver CreateEL737Counter(SConnection * pCon, char *name,
                                  int argc, char *argv[])
{
  char *pHost;
  int iPort, iChannel;

  if (argc < 3) {
    SCWrite(pCon,
            "ERROR: insuficient number of arguments to create EL737 counter",
            eError);
    return NULL;
  }
  if (!isNumeric(argv[1]) || !isNumeric(argv[2])) {
    SCWrite(pCon, "ERROR: expected numeric arguments for port and channel",
            eError);
    return NULL;
  }
  iPort = atoi(argv[1]);
  iChannel = atoi(argv[2]);
  return NewEL737Counter(name, argv[0], iPort, iChannel);
}

/*--------------------------------------------------------------------------*/
pCounterDriver NewEL737Counter(char *name, char *host, int iPort,
                               int iChannel)
{
  pCounterDriver pRes = NULL;
  EL737st *pData = NULL;
  int iRet;
  int iC1, iC2, iC3;
  char *pErr;
  char pBueffel[132];

  pRes = CreateCounterDriver(name, "EL737");
  if (!pRes) {
    return NULL;
  }

  /* open connection to counter */
  pData = (EL737st *) malloc(sizeof(EL737st));
  if (!pData) {
    DeleteCounterDriver(pRes);
    return NULL;
  }
  pData->host = strdup(host);
  pData->iPort = iPort;
  pData->iChannel = iChannel;
  pData->pData = NULL;
  iRet = EL737_Open(&(pData->pData), host, iPort, iChannel);
  if (iRet != 1) {
    EL737_ErrInfo(&pErr, &iC1, &iC2, &iC3);
    DeleteCounterDriver(pRes);
    if (pData->host) {
      free(pData->host);
    }
    return NULL;
  }
  pRes->pData = (void *) pData;

  /* assign functions */
  pRes->GetStatus = EL737GetStatus;
  pRes->Start = EL737Start;
  pRes->Halt = EL737Halt;
  pRes->ReadValues = EL737ReadValues;
  pRes->GetError = EL737GetError;
  pRes->TryAndFixIt = EL737TryAndFixIt;
  pRes->Pause = EL737Pause;
  pRes->Continue = EL737Continue;
  pRes->Set = EL737Set;
  pRes->Get = EL737Get;
  pRes->Send = EL737Send;
  pRes->KillPrivate = KillEL737Counter;
  pRes->iNoOfMonitors = 7;
  pRes->fTime = 0.;

  return pRes;
}

/*--------------------------------------------------------------------------*/
void KillEL737Counter(pCounterDriver self)
{
  EL737st *pEL737 = NULL;

  assert(self);
  pEL737 = (EL737st *) self->pData;
  assert(pEL737);

  EL737_Close(&pEL737->pData, 0);
  if (pEL737->host) {
    free(pEL737->host);
  }
}
