/*--------------------------------------------------------------------------
                     E L 7 5 5 D R I V

 This file contains the implementation for the EL755 magnet controller
 driver.

 Mark Koennecke, November 1999 

 Copyright: see copyright.h
----------------------------------------------------------------------------*/
#include <string.h>
#include <stdlib.h>
#include <time.h>
#include <math.h>
#include <assert.h>
#include <fortify.h>
#include <strlutil.h>

#include <conman.h>
#include <servlog.h>
#include <fortify.h>
#include "evdriver.h"
#include "hardsup/el755_def.h"
#include "hardsup/el755_errcodes.h"
#include "hardsup/sinq_prototypes.h"

#define CAPSIZED -8090

#define ABS(x) (x < 0 ? -(x) : (x))
/*-----------------------------------------------------------------------*/
typedef struct {
  void *pData;
  char *pHost;
  int iPort;
  int iChannel;
  int iIndex;
  int iLastError;
  int iCapCount;
} EL755Driv, *pEL755Driv;

/*---------------------------------------------------------------------------*/
static int GetEL755Pos(pEVDriver self, float *fPos)
{
  pEL755Driv pMe = NULL;
  int iRet;
  float fSoll;

  assert(self);
  pMe = (pEL755Driv) self->pPrivate;
  assert(pMe);

  iRet = EL755_GetCurrents(&(pMe->pData), &fSoll, fPos);
  if (iRet != 1) {
    return 0;
  }
     /**
      * This here is a check for the condition when the current breaks down.
      * This situation is characterized by the fSoll being something and the 
      * fPos being 0.
      */
  if (ABS(*fPos) < .1 && ABS(fSoll) > .1) {
    pMe->iCapCount++;
    if (pMe->iCapCount > 3) {
      pMe->iLastError = CAPSIZED;
      return 0;
    }
  } else {
    pMe->iCapCount = 0;
  }
  return 1;
}

/*----------------------------------------------------------------------------*/
static int EL755Run(pEVDriver self, float fVal)
{
  pEL755Driv pMe = NULL;
  int iRet;

  assert(self);
  pMe = (pEL755Driv) self->pPrivate;
  assert(pMe);

  iRet = EL755_SetCurrent(&(pMe->pData), fVal);
  pMe->iCapCount = 0;
  if (iRet != 1) {
    return 0;
  }
  return 1;
}

/*--------------------------------------------------------------------------*/
static int EL755Error(pEVDriver self, int *iCode, char *error, int iErrLen)
{
  pEL755Driv pMe = NULL;
  char *pPtr = NULL;
  int i1, i2;
  char pBueffel[132];

  assert(self);
  pMe = (pEL755Driv) self->pPrivate;
  assert(pMe);

  /* retrieve error */
  EL755_ErrInfo(&pPtr, iCode, &i1, &i2);
  switch (*iCode) {
  case CAPSIZED:
    strlcpy(error, "Powersupply has capsized, try lower current", iErrLen);
    break;
  case EL755__TURNED_OFF:
    strlcpy(error, "EL755__TURNED_OF", iErrLen);
    break;
  case EL755__TOO_MANY:
    strlcpy(error, "EL755__TO_MANY", iErrLen);
    break;
  case EL755__TOO_LARGE:
    strlcpy(error, "EL755__TOO_LARGE", iErrLen);
    break;
  case EL755__OVFLOW:
    strlcpy(error, "EL755_OVFLOW", iErrLen);
    break;
  case EL755__OUT_OF_RANGE:
    strlcpy(error, "EL755_OUT_OF_RANGE", iErrLen);
    break;
  case EL755__OFFLINE:
    strlcpy(error, "EL755_OFFLINE", iErrLen);
    break;
  case EL755__NO_SOCKET:
    strlcpy(error, "EL755__NO_SOCKET", iErrLen);
    break;
  case EL755__NOT_OPEN:
    strlcpy(error, "EL755__NOT_OPEN", iErrLen);
    break;
  case EL755__FORCED_CLOSED:
    strlcpy(error, "EL755__FORCED_CLOSED", iErrLen);
    break;
  case EL755__BAD_TMO:
    strlcpy(error, "EL755__BAD_TMO", iErrLen);
    break;
  case EL755__BAD_SOCKET:
    strlcpy(error, "EL755__BAD_SOCKET", iErrLen);
    break;
  case EL755__BAD_PAR:
    strlcpy(error, "EL755__BAD_PAR", iErrLen);
    break;
  case EL755__BAD_OFL:
    strlcpy(error, "EL755__BAD_OFL", iErrLen);
    break;
  case EL755__BAD_MALLOC:
    strlcpy(error, "EL755__BAD_MALLOC", iErrLen);
    break;
  case EL755__BAD_ILLG:
    strlcpy(error, "EL755__BAD_ILLG", iErrLen);
    break;
  case EL755__BAD_DEV:
    strlcpy(error, "EL755__BAD_DEV", iErrLen);
    break;
  case EL755__BAD_CMD:
    strlcpy(error, "EL755__BAD_CMD", iErrLen);
    break;
  case EL755__BAD_ASYNSRV:
    strlcpy(error, "EL755__BAD_ASYNSRV", iErrLen);
    break;
  default:
    sprintf(pBueffel, "Unknown error %d found", *iCode);
    strlcpy(error, pBueffel, iErrLen);
    break;
  }

  return 1;
}

/*-----------------------------------------------------------------------*/
int EL755_Send(void **handle, char *pCom, char *reply, int iLen);
  /*
   * added to el755_utility by M.K.
   */
/*--------------------------------------------------------------------------*/
static int EL755Send(pEVDriver self, char *pCommand, char *pReply,
                     int iLen)
{
  pEL755Driv pMe = NULL;
  char *pPtr = NULL;
  char pBueffel[132];
  int iRet;

  assert(self);
  pMe = (pEL755Driv) self->pPrivate;
  assert(pMe);

  if (strlen(pCommand) > 130)
    return 0;

  /* make sure that we have a \r at the end */
  strcpy(pBueffel, pCommand);
  if (strrchr(pBueffel, (int) '\r') == NULL) {
    strcat(pBueffel, "\r");
  }

  return EL755_Send(&(pMe->pData), pBueffel, pReply, iLen);
}

/*--------------------------------------------------------------------------*/
static int EL755Init(pEVDriver self)
{
  pEL755Driv pMe = NULL;
  int iRet;

  assert(self);
  pMe = (pEL755Driv) self->pPrivate;
  assert(pMe);

  pMe->pData = NULL;
  iRet = EL755_Open(&(pMe->pData), pMe->pHost, pMe->iPort, pMe->iChannel,
                    pMe->iIndex);
  return iRet;
}

/*--------------------------------------------------------------------------*/
static int EL755Close(pEVDriver self)
{
  pEL755Driv pMe = NULL;
  int iRet;

  assert(self);
  pMe = (pEL755Driv) self->pPrivate;
  assert(pMe);

  EL755_Close(&(pMe->pData), 0);
  return 1;
}

/*---------------------------------------------------------------------------*/
static int EL755Fix(pEVDriver self, int iError)
{
  pEL755Driv pMe = NULL;
  int iRet;

  assert(self);
  pMe = (pEL755Driv) self->pPrivate;
  assert(pMe);

  switch (iError) {
  case CAPSIZED:
  case EL755__TURNED_OFF:
  case EL755__TOO_MANY:
  case EL755__TOO_LARGE:
  case EL755__OUT_OF_RANGE:
  case EL755__BAD_PAR:
  case EL755__BAD_SOCKET:
  case EL755__BAD_MALLOC:
  case EL755__BAD_DEV:
  case EL755__BAD_CMD:
  case EL755__BAD_ASYNSRV:
    return DEVFAULT;
    break;
  case EL755__OVFLOW:
  case EL755__BAD_TMO:
  case EL755__BAD_ILLG:
    return DEVREDO;
    break;
  case EL755__OFFLINE:
  case EL755__BAD_OFL:
    EL755_PutOnline(&(pMe->pData), 2);
    return DEVREDO;
    break;
  case EL755__NO_SOCKET:
  case EL755__NOT_OPEN:
  case EL755__FORCED_CLOSED:
    EL755_Open(&(pMe->pData), pMe->pHost, pMe->iPort,
               pMe->iChannel, pMe->iIndex);
    return DEVREDO;
    break;
  default:
    return DEVFAULT;
    break;
  }

}

/*--------------------------------------------------------------------------*/
static int EL755Halt(pEVDriver * self)
{
  assert(self);

  return 1;
}

/*------------------------------------------------------------------------*/
void KillEL755(void *pData)
{
  pEL755Driv pMe = NULL;

  pMe = (pEL755Driv) pData;
  assert(pMe);

  if (pMe->pHost) {
    free(pMe->pHost);
  }
  free(pMe);
}

/*------------------------------------------------------------------------*/
pEVDriver CreateEL755Driv(int argc, char *argv[])
{
  pEVDriver pNew = NULL;
  pEL755Driv pSim = NULL;

  /* check for arguments */
  if (argc < 4) {
    return NULL;
  }

  pNew = CreateEVDriver(argc, argv);
  pSim = (pEL755Driv) malloc(sizeof(EL755Driv));
  memset(pSim, 0, sizeof(EL755Driv));
  if (!pNew || !pSim) {
    return NULL;
  }
  pNew->pPrivate = pSim;
  pNew->KillPrivate = KillEL755;

  pSim->iLastError = 0;
  pSim->pHost = strdup(argv[0]);
  pSim->iPort = atoi(argv[1]);
  pSim->iChannel = atoi(argv[2]);
  pSim->iIndex = atoi(argv[3]);

  /* initialise function pointers */
  pNew->SetValue = EL755Run;
  pNew->GetValue = GetEL755Pos;
  pNew->Send = EL755Send;
  pNew->GetError = EL755Error;
  pNew->TryFixIt = EL755Fix;
  pNew->Init = EL755Init;
  pNew->Close = EL755Close;

  return pNew;
}
