/*-----------------------------------------------------------------------
  This is another driver for the EL737 counter box used at SINQ. The hp in
  the name is for high performance, though this only a promise and not yet
  proven. This version connects directly to the RS232 port of the counter
  box on the terminal server rather then working with David Maden's 
  SerPortServer program. Other tweaks will be implemented as well, such as
  reading the monitors only now and then and not on all calls. 

  Mark Koennecke, July 2003
  -------------------------------------------------------------------------*/
#include <stdlib.h>
#include <assert.h>
#include <math.h>
#include <fortify.h>
#include <string.h>
#include <sics.h>
#include <rs232controller.h>
#include <countdriv.h>
#include <splitter.h>

#define STATSEND 0
#define STATRECEIVE 2
/*-----------------------------------------------------------------------
  our own data struture
  ------------------------------------------------------------------------*/
typedef struct {
  prs232 controller;
  int monitorCount; /* read monitors if this is above MONTHRESH */
  float cachedControl;
  int errorCode;
  char savedStarMessage[20];
  int haveSavedStarMessage;
  int lastStatus;
  int monitorThreshold;
} EL737hp, *pEL737hp;
/*--------------------- ERROR CODES -------------------------------------*/
#define OFFLINE -1
#define BADRANGE -3
#define BADCOMMAND -4
#define BADPARAM -5
#define NOPARAM  -7
#define TOMANYCOUNTS -8
#define SYSERROR  -9
#define BADREPLY  -10
#define SELECTFAIL -11
/*-----------------------------------------------------------------------
  search errors in a reply from the EL737. Returns 1 on success or a
  negative error code in case of trouble. 
  ------------------------------------------------------------------------*/
static int checkEL737Error(pEL737hp self, char *pReply){
  /*
    all error start with a ?, if no ? in reply, answer is OK! 
  */
  if(strstr(pReply,"?") == NULL){
    return 1;
  }

  /*
    Now there is an error and we have to identify it
  */
  if(strstr(pReply,"?OF") != NULL){
    return OFFLINE;
  } else if(strstr(pReply,"?OV") != NULL){
    return OVERFLOW;
  } else if(strstr(pReply,"?1") != NULL){
    return BADRANGE;
  } else if(strstr(pReply,"?2") != NULL){
    return BADCOMMAND;
  } else if(strstr(pReply,"?3") != NULL){
    return BADPARAM;
  } else if(strstr(pReply,"?4") != NULL){
    return BADCOUNTER;
  } else if(strstr(pReply,"?5") != NULL){
    return NOPARAM;
  } else if(strstr(pReply,"?6") != NULL){
    return TOMANYCOUNTS;
  } else { 
    return SYSERROR;
  }
}
/*---------------------------------------------------------------*/
static int EL737Command(pEL737hp pPriv, char *pCommand,
			char *pReply, int replylen){
  int status, savedLen;

  savedLen = replylen;
  status = transactRS232(pPriv->controller,pCommand,strlen(pCommand),
			 pReply,replylen);
  if(status < 0){
    pPriv->errorCode = status;
    return 0;
  }

  /*
    save any * message which may have arrrived, and if so: read again
  */
  if(strstr(pReply,"*") != NULL){
    memset(pPriv->savedStarMessage,0,20);
    strncpy(pPriv->savedStarMessage,pReply,19);
    pPriv->haveSavedStarMessage = 1;
    status = readRS232TillTerm(pPriv->controller, pReply, &savedLen);
    if(status < 0){
      pPriv->errorCode = status;
      return 0;
    }
  }

  status = checkEL737Error(pPriv, pReply);
  if(status < 0){
    pPriv->errorCode = status;
    return 0;
  }
  return 1;
} 
/*-----------------------------------------------------------------*/
static int updateMonitors(struct __COUNTER *self){
  int status;
  int m1,m2,m3,m4,m5,m6,m7,m8;
  float fTime;
  pEL737hp pPriv = NULL;
  char reply[132];

  pPriv = (pEL737hp)self->pData;

  if(!EL737Command(pPriv,"RA\r",reply,131)){
    return 0;
  }

  /*
    There are two forms of RA replys: new form with 8 monitors
  */
  status = sscanf(reply,"%f %d %d %d %d %d %d %d %d",
		  &fTime,&m1,&m2,&m3,&m4,&m5,&m6,&m7,&m8);
  if(status != 9){
    /*
      old form with 4 monitors
    */
    status = sscanf(reply,"%d %d %d %d %f",&m1,&m2,&m3,&m4,&fTime);
    if(status != 5){
      printf("Bad reply: %s\n", reply);
      pPriv->errorCode = BADREPLY;
      return 0;
    }
  }
  self->lCounts[0] = m2;
  self->lCounts[1] = m1;
  self->lCounts[2] = m3;
  self->lCounts[3] = m4;
  self->lCounts[4] = m5;
  self->lCounts[5] = m6;
  self->lCounts[6] = m7;
  self->lCounts[7] = m8;
  self->fTime = fTime;

  if(self->eMode == eTimer){
    pPriv->cachedControl = fTime;
  } else {
    pPriv->cachedControl = m2;
  }
  return 1;
}
/*-----------------------------------------------------------------*/
static void decodeStarMessage(pEL737hp self, char *message){
  strtolower(message);
  if(strstr(message,"*cf") != NULL){
    self->lastStatus = HWIdle;
  } else if(strstr(message,"*lr") != NULL){
    self->lastStatus = HWNoBeam;
  } else if(strstr(message,"*hr") != NULL){
    self->lastStatus = HWBusy;
  } else {
    printf("Bad star mesage: %s\n", message);
  }
}
/*------------------------------------------------------------------*/
static int EL737Status(struct __COUNTER *self, float *fControl){
  int status, returnValue, len;
  pEL737hp pPriv = NULL;
  char starMessage[10];

  assert(self);
  pPriv = (pEL737hp)self->pData;


  /*
    now we are dealing with STATRECEIVE mode. 
    Check for data availability first. 
  */
  status = availableNetRS232(pPriv->controller);
  if(status == 0){
    *fControl = pPriv->cachedControl;
    returnValue = pPriv->lastStatus;
  } else if(status < 0) {
    *fControl = pPriv->cachedControl;
    pPriv->errorCode = SELECTFAIL;
    return HWFault;
  } else {
    /*
      we have data: read and decode the * message we are waiting fore
    */
    len = 9;
    status = readRS232TillTerm(pPriv->controller,starMessage,&len);
    if(status < 0){
      pPriv->errorCode = status;
      return HWFault;
    }
    decodeStarMessage(pPriv,starMessage);
    returnValue = pPriv->lastStatus;
  }

  /*
    check if we update the monitors and do it 
  */
  pPriv->monitorCount++;
  if(pPriv->monitorCount > pPriv->monitorThreshold){
    status = updateMonitors(self);
    pPriv->monitorCount = 0;
    if(!status){
      return HWFault;
    }
  }

  /*
    check for a saved starMessage
  */
  if(pPriv->haveSavedStarMessage != 0){
    decodeStarMessage(pPriv,pPriv->savedStarMessage);
    pPriv->haveSavedStarMessage = 0;
    returnValue = pPriv->lastStatus;
  }

  *fControl = pPriv->cachedControl;
  return returnValue;
} 
/*-------------------------------------------------------------------*/
static int EL737Start(struct __COUNTER *self){
  pEL737hp pPriv = NULL;
  int status;
  char pCommand[50], pReply[30];

  assert(self);
  pPriv = (pEL737hp)self->pData;

  if(self->eMode == ePreset){
    snprintf(pCommand,49,"MP %d\r",(int)self->fPreset);
  } else {
    snprintf(pCommand,49,"TP %.2f\r", self->fPreset);
  }
  if(EL737Command(pPriv,pCommand,pReply,29) != 1){
    return 0;
  }
  pPriv->lastStatus = HWBusy;
  return 1;
}
/* --------------------------------------------------------------------*/
static int EL737Pause(struct __COUNTER *self){
  pEL737hp pPriv = NULL;
  int status;
  char pCommand[50], pReply[30];

  assert(self);
  pPriv = (pEL737hp)self->pData;

  status = EL737Command(pPriv,"PS\r",pReply,29);
  if(status > 0){
    pPriv->lastStatus= HWPause;
  }
  return status;
}
/*----------------------------------------------------------------------*/
static int EL737Continue(struct __COUNTER *self){
  pEL737hp pPriv = NULL;
  int status;
  char pCommand[50], pReply[30];

  assert(self);
  pPriv = (pEL737hp)self->pData;

  status = EL737Command(pPriv,"CO\r",pReply,29);
  if(status > 0){
    pPriv->lastStatus = HWBusy;
  }
  return status;
}
/*---------------------------------------------------------------------*/
static int EL737Halt(struct __COUNTER *self){
  pEL737hp pPriv = NULL;
  int status;
  char pCommand[50], pReply[30];

  assert(self);
  pPriv = (pEL737hp)self->pData;

  return EL737Command(pPriv,"S\r",pReply,29);
}
/*-------------------------------------------------------------------*/
static int EL737Transfer(struct __COUNTER *self){
  pEL737hp pPriv = NULL;
  assert(self);
  pPriv = (pEL737hp)self->pData;

  return updateMonitors(self);
}
/*--------------------------------------------------------------------*/
static int EL737GetError(struct __COUNTER *self, int *iCode, 
			 char *pError, int errLen){
  pEL737hp pPriv = NULL;
  assert(self);
  pPriv = (pEL737hp)self->pData;

  *iCode = pPriv->errorCode;
  switch(pPriv->errorCode){
  case OFFLINE:
    strncpy(pError,"EL737 is offline",errLen);
    break;
  case BADRANGE:
    strncpy(pError,"EL737 parameter is out of range",errLen);
    break;
  case BADCOMMAND:
    strncpy(pError,"EL737 received unknown command or is busy",errLen);
    break;
  case BADPARAM:
    strncpy(pError,"EL737 parameter is awful",errLen);
    break;
  case NOPARAM:
    strncpy(pError,"EL737 parameter missing",errLen);
    break;
  case TOMANYCOUNTS:
    strncpy(pError,"EL737 counters overflowed",errLen);
    break;
  case SYSERROR:
    strncpy(pError,"EL737 has an internal system error",errLen);
    break;
  case BADREPLY:
    strncpy(pError,"EL737 sent an unexpected reply",errLen);
    break;
  case SELECTFAIL:
    strncpy(pError,"select system call failed, network trouble",errLen);
  default:
    getRS232Error(pPriv->errorCode,pError,errLen);
  }
  return 1;
}
/*--------------------------------------------------------------------*/
static int EL737FixIt(struct __COUNTER *self, int iCode){
  pEL737hp pPriv = NULL;
  int status;
  char pReply[50];

  assert(self);
  pPriv = (pEL737hp)self->pData;

  switch(iCode){
  case TIMEOUT:
  case BADREPLY:
  case BADPARAM:
  case NOPARAM:
  case BADRANGE:
    return COREDO;
    break;
  case OFFLINE:
    EL737Command(pPriv,"RMT 1\r",pReply,49);
    EL737Command(pPriv,"echo 0\r",pReply,49);
    return COREDO;
    break;
  case BADCOMMAND: /* can be busy, stop it and try again */
    EL737Command(pPriv,"S\r",pReply,49);
    return COREDO;
    break;
  case TOMANYCOUNTS:
  case SYSERROR:
    return COTERM;
    break;
  default:
    /*
      network problem; try to reopen
    */
    status = initRS232(pPriv->controller);
    if(status != 1){
      return COTERM;
    } else {
      return COREDO;
    }
  }
}
/*------------------------------------------------------------------------*/
static int EL737Set(struct __COUNTER *self, char *name, int iCter, 
		     float fVal){
  pEL737hp pPriv = NULL;
  int status;
  char pCommand[80], pReply[50];

  assert(self);
  pPriv = (pEL737hp)self->pData;

  if(strcmp(name,"threshold") == 0){
    sprintf(pCommand,"DL %1.1d %f\r",iCter, fVal);
    if(!EL737Command(pPriv,pCommand,pReply,49)){
      return 0;
    }
    sprintf(pCommand,"DR %1.1d\r",iCter);
    if(!EL737Command(pPriv,pCommand,pReply,49)){
      return 0;
    }
    return 1;
  } else if(strcmp(name,"monitorupdate") != 0){
    pPriv->monitorThreshold = (int)fVal;
    return 1;
  }else {
    self->iErrorCode = UNKNOWNPAR;
    return 0;
  }
}
/*----------------------------------------------------------------------*/
static int EL737Get(struct __COUNTER *self, char *name, int iCter, 
		    float *fVal){
  pEL737hp pPriv = NULL;
  int status;
  char pCommand[80], pReply[50];

  assert(self);
  pPriv = (pEL737hp)self->pData;

  if(strcmp(name,"threshold") == 0){
    sprintf(pCommand,"DL %1.1d\r",iCter);
    if(!EL737Command(pPriv,pCommand,pReply,49)){
      return 0;
    }
    sscanf(pReply,"%f", fVal);
    return 1;
  } else if(strcmp(name,"monitorupdate") == 0){
    *fVal = (float)pPriv->monitorThreshold;
    return 1;
  } else {
    self->iErrorCode = UNKNOWNPAR;
    return 0;
  }
}
/*--------------------------------------------------------------------*/
static int EL737Send(struct __COUNTER *self, char *pText, char *pReply,
		     int iReplyLen){
  pEL737hp pPriv = NULL;

  assert(self);
  pPriv = (pEL737hp)self->pData;

  return EL737Command(pPriv,pText,pReply,iReplyLen);
}
/*---------------------------------------------------------------------*/
static void KillHP(pCounterDriver self){
  pEL737hp pPriv = NULL;

  assert(self);
  pPriv = (pEL737hp)self->pData;

  if(!pPriv){
    return;
  }
  if(pPriv->controller != NULL){
    KillRS232(pPriv->controller);
  }
  free(pPriv);
}
/*-------------------------------------------------------------------*/
pCounterDriver MakeEL737HPV2(SConnection *pCon, char *name, 
			   int argc, char *argv[]){
  pCounterDriver pNew = NULL;
  pEL737hp pPriv = NULL;
  char pHost[132];
  int port, status;

  /*
    check arguments
  */
  if(argc < 2) {
    SCWrite(pCon,"ERROR: insufficient no af arguments to create EL737HP",
	    eError);
    return NULL;
  }
  if(!isNumeric(argv[1])){
    SCWrite(pCon,"ERROR: expected numeric argument for port number",
	    eError);
    return NULL;
  }
  port = atoi(argv[1]);
  strncpy(pHost,argv[0],131);

  /*
    allocate a bank worth of memory ...........
  */
  pNew = CreateCounterDriver(name,"EL737HP");
  pPriv = (pEL737hp)malloc(sizeof(EL737hp));
  if(!pNew || !pPriv){
    return NULL;
  }
  memset(pPriv,0,sizeof(EL737hp));
  pPriv->controller = createRS232(pHost,port);
  if(!pPriv->controller){
    DeleteCounterDriver(pNew);
    return NULL;
  }

  /* assign functions */
  pNew->GetStatus   = EL737Status;
  pNew->Start       = EL737Start;
  pNew->Halt        = EL737Halt;
  pNew->ReadValues  = EL737Transfer;
  pNew->GetError    = EL737GetError;
  pNew->TryAndFixIt = EL737FixIt;
  pNew->Pause       = EL737Pause;
  pNew->Continue    = EL737Continue;
  pNew->Set         = EL737Set;
  pNew->Get         = EL737Get;
  pNew->Send        = EL737Send;
  pNew->KillPrivate = KillHP;
  pNew->iNoOfMonitors = 7;
  pNew->fTime = 0.;
  pNew->pData = pPriv;

  /*
    initialize connection
  */
  setRS232Debug(pPriv->controller,0);
  setRS232ReplyTerminator(pPriv->controller,"\r");
  status = initRS232(pPriv->controller);
  status = EL737Command(pPriv,"RMT 1\r",pHost,131);
  status = EL737Command(pPriv,"RMT 1\r",pHost,131);
  status = EL737Command(pPriv,"ECHO 0\r",pHost,131);

  return pNew;
}



