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

	Some code to make EL737 COUNTERS  as used at SINQ available in TCL.
	Just a wrapper around David Maden's COUNTER routines.
	
	You are free to use and modify this software for noncommercial
	usage.
	
	No warranties or liabilities of any kind taken by me or my employer
	
	Mark Koennecke July 1996
----------------------------------------------------------------------------*/
#include "sinq_prototypes.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/*
#include <timers.h>
*/
#include <tcl.h>
#include "el737_def.h"

#define True 1
#define False 0

typedef struct {
  void *pData;                  /* EL737 open struct */
} EL737st;

EXTERN int EL737Action(ClientData pDat, Tcl_Interp * i, int a,
                       char *argv[]);
static void EL737Error2Text(char *pBuffer, int errcode);

/*---------------------------------------------------------------------------
	Tcl has a high niceness level. It deletes a command properly when
	exiting, reinitializing etc. I use this facility to kill off the
	counter initialised in CterEL737.
---------------------------------------------------------------------------*/
EXTERN void EL737Murder(ClientData pData)
{
  EL737st *pTa = (EL737st *) pData;
  EL737_Close(&(pTa->pData));
  free(pData);
}

/*----------------------------------------------------------------------------
	CterEL737 is the main entry point for this stuff. It connects to a counter
	and, on success, creates a new command with the name of the counter.
	Syntax:
		EL737 name host port channel 
---------------------------------------------------------------------------*/

int CterEL737(ClientData clientData, Tcl_Interp * interp,
              int argc, char *argv[])
{
  int iRet;
  EL737st *pEL737 = NULL;
  int iPort, iChannel, iMotor;
  char *pErr = NULL;
  char pBueffel[80];

  /* check arguments */
  if (argc < 5) {
    Tcl_AppendResult(interp,
                     " Insufficient arguments: CterEL737 name host port channel",
                     (char *) NULL);
    return TCL_ERROR;
  }

  /* convert arguments */
  iRet = Tcl_GetInt(interp, argv[3], &iPort);
  if (iRet == TCL_ERROR) {
    Tcl_AppendResult(interp, "Need integer value for port", (char *) NULL);
    return iRet;
  }

  iRet = Tcl_GetInt(interp, argv[4], &iChannel);
  if (iRet == TCL_ERROR) {
    Tcl_AppendResult(interp, "Need integer value for channel",
                     (char *) NULL);
    return iRet;
  }

  /* make a new pointer, initialise EL737st */
  pEL737 = (EL737st *) malloc(sizeof(EL737st));
  if (pEL737 == NULL) {
    Tcl_AppendResult(interp, "No memory in EL734", NULL);
    return TCL_ERROR;
  }

  /* open the rotten Counter, finally */
  iRet = EL737_Open(&(pEL737->pData), argv[2], iPort, iChannel);
  if (iRet) {                   /* success */
    /* handle TCL, create new command: the Counter */
    Tcl_CreateCommand(interp, strdup(argv[1]), EL737Action,
                      (ClientData) pEL737, EL737Murder);
    Tcl_AppendResult(interp, strdup(argv[1]), (char *) NULL);
    return TCL_OK;
  } else {
    EL737_ErrInfo(&pErr, &iPort, &iChannel, &iMotor);
    EL737Error2Text(pBueffel, iPort);
    Tcl_AppendResult(interp, pBueffel, (char *) NULL);
    free(pEL737);
    return TCL_ERROR;
  }
}

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

	EL737 Action is the routine where commands send to the conter will 
	end up.
	
	Syntax:        timer            starts counter with a preset value
	counter wait           val      for counts or time and does
	               monitor          not return before finished
	               
                       timer            starts counter with a preset value
	counter start          val      for counts or time and 
	               monitor          returns immediatly
	counter isDone                  returns True, false depending if
					started run has ended or not
	counter value                   gets counter values as a little list
	                                consisting of:   
	                                { counts monitor time }
	counter Stop                    forces counter to stop  
----------------------------------------------------------------------------*/
EXTERN int EL737Action(ClientData clientData, Tcl_Interp * interp,
                       int argc, char *argv[])
{
  EL737st *pData = (EL737st *) clientData;
  char pBueffel[132];
  char pNumBuf[20];
  char *pErr = NULL;
  int iC1, iC2, iC3, iC4, iRS, iRet;
  float fTime;
  int iFlag = 0;
  int iMode;
  double dVal;

  /* obviously we need at least a keyword! */
  if (argc < 2) {
    Tcl_AppendResult(interp, "No keyword given", NULL);
    return TCL_ERROR;
  }

  /* get values out */
  if (strcmp(argv[1], "value") == 0) {
    iRet = EL737_GetStatus(&(pData->pData), &iC1, &iC2, &iC3,
                           &iC4, &fTime, &iRS);
    if (!iRet) {
      EL737_ErrInfo(&pErr, &iC1, &iC2, &iC3);
      EL737Error2Text(pBueffel, iC1);
      Tcl_AppendResult(interp, pBueffel, (char *) NULL);
      return TCL_ERROR;
    }
    sprintf(pNumBuf, "%d", iC2);
    Tcl_AppendElement(interp, pNumBuf);
    sprintf(pNumBuf, "%d", iC1);
    Tcl_AppendElement(interp, pNumBuf);
    sprintf(pNumBuf, "%f", fTime);
    Tcl_AppendElement(interp, pNumBuf);
    return TCL_OK;
  }

  /* isDone ? */
  if (strcmp(argv[1], "isDone") == 0) {
    iRet = EL737_GetStatus(&(pData->pData), &iC1, &iC2, &iC3,
                           &iC4, &fTime, &iRS);
    if (!iRet) {
      EL737_ErrInfo(&pErr, &iC1, &iC2, &iC3);
      EL737Error2Text(pBueffel, iC1);
      Tcl_AppendResult(interp, pBueffel, (char *) NULL);
      return TCL_ERROR;
    }
    if (iRS == 0) {             /* done is true */
      sprintf(pNumBuf, "%d", True);
    } else {
      sprintf(pNumBuf, "%d", False);
    }
    Tcl_AppendResult(interp, pNumBuf, (char *) NULL);
    return TCL_OK;
  }

  /* actual counting neutrons in two different modes */
  if (strcmp(argv[1], "wait") == 0) {
    iFlag = 2;
  }
  if (strcmp(argv[1], "start") == 0) {
    iFlag = 1;
  }
  if (iFlag > 0) {              /* we need to count */
    if (argc < 4) {             /* not enough arguments */
      Tcl_AppendResult(interp, "Usage: ", argv[0], argv[1],
                       " timer or monitor val", NULL);
      return TCL_ERROR;
    }

    /* timer or monitor preset ? */
    if (strcmp(argv[2], "timer") == 0) {
      iMode = 1;
    } else if (strcmp(argv[2], "monitor") == 0) {
      iMode = 2;
    } else {
      Tcl_AppendResult(interp, "Usage: ", argv[0], argv[1],
                       " timer or monitor val", NULL);
      return TCL_ERROR;
    }

    /* get the preset value */
    iRet = Tcl_GetDouble(interp, argv[3], &dVal);
    if (iRet == TCL_ERROR) {
      return TCL_ERROR;
    }

    /* actual start collecting neutrons */
    if (iMode == 1) {
      iRet = EL737_StartTime(&(pData->pData), (float) dVal, &iRS);
    } else {
      iRet = EL737_StartCnt(&(pData->pData), (int) dVal, &iRS);
    }
    if (!iRet) {
      EL737_ErrInfo(&pErr, &iC1, &iC2, &iC3);
      EL737Error2Text(pBueffel, iC1);
      Tcl_AppendResult(interp, pBueffel, (char *) NULL);
      return TCL_ERROR;
    }
  }

  /* end of count startup code */
  /* if apropriate: wait */
  if (iFlag == 2) {
    iRet = EL737_WaitIdle(&(pData->pData), &iC1, &iC2, &iC3, &iC4, &fTime);
    if (!iRet) {
      EL737_ErrInfo(&pErr, &iC1, &iC2, &iC3);
      EL737Error2Text(pBueffel, iC1);
      Tcl_AppendResult(interp, pBueffel, (char *) NULL);
      return TCL_ERROR;
    }
    return TCL_OK;
  } else if (iFlag == 1) {
    return TCL_OK;
  }

  /* the stop command */
  if (strcmp(argv[1], "stop") == 0) {
    iRet = EL737_Stop(&(pData->pData), &iC1, &iC2, &iC3,
                      &iC4, &fTime, &iRS);
    if (!iRet) {
      EL737_ErrInfo(&pErr, &iC1, &iC2, &iC3);
      EL737Error2Text(pBueffel, iC1);
      Tcl_AppendResult(interp, pBueffel, (char *) NULL);
      return TCL_ERROR;
    }
    return TCL_OK;
  }

  Tcl_AppendResult(interp, " obscure command: ", argv[1],
                   " not understood by EL737 counter", NULL);
  return TCL_ERROR;
}

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

	EL737Error2Text converts between an EL734 error code to text
-----------------------------------------------------------------------------*/
void EL737Error2Text(char *pBuffer, int iErr)
{
  switch (iErr) {
  case -28:
    strcpy(pBuffer, "EL737__BAD_ADR");
    break;
  case -8:
    strcpy(pBuffer, "EL737__BAD_OVFL");
    break;
  case -30:
    strcpy(pBuffer, "EL737__BAD_BSY");
    break;
  case -3:
    strcpy(pBuffer, "EL737__BAD_SNTX");
    break;
  case -9:
    strcpy(pBuffer, "EL737__BAD_CONNECT");
    break;
  case -23:
    strcpy(pBuffer, "EL737__BAD_FLUSH");
    break;
  case -6:
    strcpy(pBuffer, "EL734__BAD_DEV");
    break;
  case -10:
    strcpy(pBuffer, "EL737__BAD_ID");
    break;
  case -5:
    strcpy(pBuffer, "EL737__BAD_ILLG");
    break;
  case -2:
    strcpy(pBuffer, "EL737__BAD_LOC");
    break;
  case -11:
    strcpy(pBuffer, "EL737__BAD_MALLOC");
    break;
  case -21:
    strcpy(pBuffer, "EL737__BAD_NOT_BCD");
    break;
  case -4:
    strcpy(pBuffer, "EL737__BAD_OFL");
    break;
  case -29:
    strcpy(pBuffer, "EL737__BAD_PAR");
    break;

  case -17:
    strcpy(pBuffer, "EL737__BAD_RECV");
    break;
  case -19:
    strcpy(pBuffer, "EL737__BAD_RECV_NET");
    break;
  case -18:
    strcpy(pBuffer, "EL737__BAD_RECV_PIPE");
    break;
  case -20:
    strcpy(pBuffer, "EL737__BAD_RECV_UNKN");
    break;
  case -22:
    strcpy(pBuffer, "EL737__BAD_RECVLEN");
    break;
  case -24:
    strcpy(pBuffer, "EL737__BAD_RECV1");
    break;
  case -26:
    strcpy(pBuffer, "EL737__BAD_RECV1_NET");
    break;
  case -25:
    strcpy(pBuffer, "EL737__BAD_RECV1_PIPE");
    break;
  case -27:
    strcpy(pBuffer, "EL737__BAD_RNG");
    break;
  case -13:
    strcpy(pBuffer, "EL737__BAD_SEND");
    break;
  case -14:
    strcpy(pBuffer, "EL737__BAD_SEND_PIPE");
    break;
  case -15:
    strcpy(pBuffer, "EL737__BAD_SEND_NET");
    break;
  case -16:
    strcpy(pBuffer, "EL737__BAD_SEND_UNKN");
    break;
  case -12:
    strcpy(pBuffer, "EL737__BAD_SENDLEN");
    break;
  case -7:
    strcpy(pBuffer, "EL737__BAD_SOCKET");
    break;
  case -1:
    strcpy(pBuffer, "EL737__BAD_TMO");
    break;
  default:
    strcpy(pBuffer, "Unknown EL737 error");
    break;
  }
}
