/*--------------------------------------------------------------------------
                                 S P S
                                
  Implementation file for the SICS module handling Siemens SPS controllers
  at SinQ. 
  
  copyright: see copyright.h
  I cannot imagine that somebody wants this. It is highly special for SINQ.                                 
  It handles a protocoll tailor made by Siemens for Sinq. I cannot imagine
  that somebody wants something from Siemens anyway.
  
  
  Mark Koennnecke, Juli 1998
  
  Feature added: bipa command, Mark Koennecke, May 2009

  Reworked to use rs232controller rather then the older  serialport code

  Mark Koennecke, November 2009

---------------------------------------------------------------------------*/
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <tcl.h>
#include "sics.h"
#include "rs232controller.h"
#include "sics.h"
#include "lld.h"
#include "bit.h"
#include "sps.i"
#include "sps.h"
#include "commandlog.h"

/*--------------------- some internal defines ----------------------------*/
#define SPSOFFLINE -10
#define COMMDEAD   -11
#define COMMRECONNECT -12
#define COMMTMO    -13
/*-------------------------------------------------------------------------*/
static float SimRandom(void)
{
  float fVal;

  fVal = ((float) rand() / (float) RAND_MAX) * 100.0;
  return fVal;
}

/*--------------------- static functions ---------------------------------*/
static int init(pSPS self)
{
  int iRet;

  assert(self);
  assert(self->pHost);
  prs232 rs232;
  
  /* check if in simulation mode */
  if (self->iMode)
    return 1;

  rs232 = createRS232(self->pHost,self->iPort);
  self->pData = rs232;
  iRet = initRS232(rs232);
  if(iRet != 1){
	  self->iLastError = COMMDEAD;
	  return 0;
  }
  setRS232SendTerminator(rs232,"\r\n");
  setRS232ReplyTerminator(rs232,"\n");
  setRS232Timeout(rs232,20000);
  /* setRS232Debug(rs232,1); */
  
  return 1;
}

/*-------------------------------------------------------------------------*/
static int SPSCommand(pSPS self, char *pCommand, char *pReply,
                      int iReplyLen)
{
  int iRet, i;
  char pError[132];
  prs232 rs232;
  
  assert(self);
  assert(pCommand);
  assert(pReply);

  rs232 = (prs232)self->pData;
  /*
     try at least three times to get the command through before
     giving up
   */
  for (i = 0; i < 3; i++) {
	if(availableRS232(rs232)){
		memset(pReply,0, iReplyLen);
		iRet = iReplyLen;
		readRS232(rs232, pReply, &iRet);
		/* printf("Dirt on the SPS cable: %d bytes, text = %s\n", iRet, pReply); */
	}
	iRet = transactRS232(rs232, pCommand,strlen(pCommand), pReply, iReplyLen);
    if (iRet < 0) {
      switch (iRet) {
      case TIMEOUT:
        self->iLastError = COMMTMO;
        continue;
        break;
      default:
        self->iLastError = iRet;
        getRS232Error(iRet,pError,131);
        WriteToCommandLog("SYS: SPS-TROUBLE:", pError);
        closeRS232(rs232);
        iRet = initRS232(rs232);
        if (iRet == 0) {
          return 0;
        }
        continue;
        break;
      }
    }

    return 1;
  }
  return 0;
}

/*--------------------------------------------------------------------------*/
int SPSSetButton(pSPS self, SConnection * pCon, int iByte, int iBit)
{
  Permission perm;
  char pCommand[30];
  char pBueffel[256];
  int iRet, iTest, i;

  assert(self);
  assert(pCon);


  /* first check permissions */
  iRet = LLDnodePtr2First(self->lPermissions);
  iTest = 0;
  while (iRet != 0) {
    LLDnodeDataTo(self->lPermissions, &perm);
    if (perm.iByte == iByte) {
      if (perm.iBit == iBit) {
        iTest = SCMatchRights(pCon, perm.iPrivilege);
        break;
      }
    }
    iRet = LLDnodePtr2Next(self->lPermissions);
  }
  if (!iTest) {
    sprintf(pBueffel, "ERROR: you have no permission to flip %d.%d",
            iByte, iBit);
    SCWrite(pCon, pBueffel, eError);
    return 0;
  }

  /* if in sim mode, return true */
  if (self->iMode) {
    return 1;
  }

  /* format command and send a command to set a bit through the 
     drain in order to simulate a button press 
   */
  sprintf(pCommand, "S%3.3d%1.1d", iByte, iBit);
  iRet = SPSCommand(self, pCommand, pBueffel, 255);
  if (!iRet) {
    SCWrite(pCon, pBueffel, eError);
    SCWrite(pCon, "ERROR: Sending flip command failed", eError);
    return 0;
  }
  return 1;
}

/*--------------------------------------------------------------------------*/
int SPSGetStatus(pSPS self, int iStatus, int *iSet)
{
  int iRet, i, i2, iVal;
  unsigned char pByte[16];
  char pBueffel[256], pNum[10];
  char *pPtr;
  unsigned char cBit, cMask;

  assert(self);

  if ((iStatus < 0) || (iStatus >= 128)) {
    return -1;
  }

  /* if in simulation mode, return 1 */
  if (self->iMode) {
    *iSet = 1;
    return 1;
  }

  /* send an R command down to the SPS */
  iRet = SPSCommand(self, "R", pBueffel, 255);
  if (!iRet) {
    return 0;
  }

  /* decode the reply into the Byte array */
  pPtr = strchr(pBueffel, 'R');
  if (pPtr == NULL) {
    return -2;
  }
  pPtr++;
  for (i = 0; i < 16; i++) {
    /* skip the whitespace */
    pPtr++;
    pNum[0] = *pPtr;
    pPtr++;
    pNum[1] = *pPtr;
    pPtr++;
    pNum[2] = *pPtr;
    pPtr++;
    pNum[3] = '\0';
    pByte[i] = atoi(pNum);
  }

  /*  test the bit */
  i = iStatus / 8;
  cBit = pByte[i];
  i2 = iStatus % 8;
  cMask = 1 << i2;

  /*
     printf("Byte %d, Value %d, Bit %d, Value: %d\n", i,cBit, i2, (cBit & cMask));
   */

  if ((cBit & cMask) > 0) {
    *iSet = 1;
  } else {
    *iSet = 0;
  }
  return 1;
}
/*------------------------------------------------------------------------*/
static void byteToString(unsigned char byte, char txt[9])
{
	int i;
	for(i = 0; i < 8; i++){
		if(byte & (1 << i) ){
			txt[i] = '1';
		} else {
			txt[i] = '0';
		}
	}
}
/*------------------------------------------------------------------------*/
static int SPSBitPattern(pSPS self, SConnection *pCon)
{
	int iRet, i;
	char pBueffel[256], *pPtr = NULL, pNum[10], pText[9];
	unsigned char byte;
	pDynString result = NULL;
	
	if(self->iMode){
		SCWrite(pCon,"00000 0001000 00001000 00010000", eValue);
		return 1;
	}
	  /* send an R command down to the SPS */
	  iRet = SPSCommand(self, "R", pBueffel, 255);
	  if (!iRet) {
		  SCWrite(pCon,"ERROR: communication error in SPSBitPattern", 
				  eError);
	    return 0;
	  }
	  result = CreateDynString(128,128);
	  if(result == NULL){
		  SCWrite(pCon,"ERROR: out of memory in SPSBitpattern", eError);
		  return 0;
	  }

	  /* decode the reply into the Byte array */
	  pPtr = strchr(pBueffel, 'R');
	  if (pPtr == NULL) {
	    return -2;
	  }
	  pPtr++;
	  for (i = 0; i < 16; i++) {
	    /* skip the whitespace */
	    pPtr++;
	    pNum[0] = *pPtr;
	    pPtr++;
	    pNum[1] = *pPtr;
	    pPtr++;
	    pNum[2] = *pPtr;
	    pPtr++;
	    pNum[3] = '\0';
	    byte = atoi(pNum);
	    memset(pText,0,9*sizeof(char));
	    byteToString(byte, pText);
	    DynStringConcat(result, pText);
	    if(((i+1) % 4) == 0){
	    	DynStringConcatChar(result,'\n');
	    } else {
	    	DynStringConcatChar(result,' ');
	    }
	  }
	  SCWrite(pCon,GetCharArray(result), eValue);
	  DeleteDynString(result);
	  
	return 1;
}
/*-------------------------------------------------------------------------
  This is a special feature for SANS at SINQ. SANS has a collimator and 
  the length of the collimator can only be deduced from the SPS status
  message by checking each of nine segments separately. For efficiency
  this is coded into a special function for SANS. This also needs a special
  function for testing bits.

  Mark Koennecke, April 1999
*/
static int TestBit(unsigned char cByte, int iBit)
{
  unsigned char cMask;

  assert(iBit >= 0);
  assert(iBit < 8);

  cMask = 1 << iBit;
  if ((cMask & cByte) > 0) {
    return 1;
  } else {
    return 0;
  }
}

/*-----------------------------------------------------------------------*/
int SPSGetSANS(pSPS self, float *fLength)
{
  int iRet, i, i2, iVal;
  unsigned char pByte[16];
  char pBueffel[256], pNum[10];
  char *pPtr;
  unsigned char cBit, cMask;
  float fLang;

  assert(self);

  /* if in simulation mode, return a random number */
  if (self->iMode) {
    *fLength = SimRandom();
    return 1;
  }

  /* send an R command down to the SPS */
  iRet = SPSCommand(self, "R", pBueffel, 255);
  if (!iRet) {
    return 0;
  }

  /* decode the reply into the Byte array */
  pPtr = strchr(pBueffel, 'R');
  if (pPtr == NULL) {
    return -2;
  }
  pPtr++;
  for (i = 0; i < 16; i++) {
    /* skip the whitespace */
    pPtr++;
    pNum[0] = *pPtr;
    pPtr++;
    pNum[1] = *pPtr;
    pPtr++;
    pNum[2] = *pPtr;
    pPtr++;
    pNum[3] = '\0';
    pByte[i] = atoi(pNum);
  }

  fLang = 189.;
  if (TestBit(pByte[0], 1) > 0) {
    fLang = 18.;
  }
  /* coll 1  15m */
  if (TestBit(pByte[0], 3) > 0) {
    fLang = 15.;
  }
  /* coll 2 11 m */
  if (TestBit(pByte[0], 7) > 0) {
    fLang = 11.;
  }
  /* coll 3 8 m */
  if (TestBit(pByte[1], 3) > 0) {
    fLang -= 3.;
  }
  /* coll 4  6m */
  if (TestBit(pByte[1], 7) > 0) {
    fLang -= 2.;
  }
  /* coll 5 4.5 m */
  if (TestBit(pByte[2], 3) > 0) {
    fLang -= 1.5;
  }
  /* coll 6 3 m */
  if (TestBit(pByte[2], 7) > 0) {
    fLang -= 1.5;
  }
  /* coll 7 */
  if (TestBit(pByte[3], 3) > 0) {
    fLang -= 1.;
  }
  /* coll 8 */
  if (TestBit(pByte[3], 6) > 0) {
    fLang -= 1.;
  }
  if (TestBit(pByte[3], 7) > 0) {
    fLang -= 0.6;
  }
  *fLength = fLang;
  return 1;
}

/*--------------------------------------------------------------------------*/
int SPSGetADC(pSPS self, int iWhich, int *iValue)
{
  int iADC[8];
  int i, iRet;
  char *pPtr, pBueffel[256], pNum[10];

  assert(self);

  /* check iWhich */
  if ((iWhich < 0) || (iWhich > 7)) {
    return 0;
  }

  /* give a random number in simulation mode */
  if (self->iMode) {
    *iValue = (int) SimRandom();
    return 1;
  }

  /* send an A command to the box */
  iRet = SPSCommand(self, "A", pBueffel, 255);
  if (!iRet) {
    return 0;
  }

  /* decode the result */
  pPtr = strchr(pBueffel, 'A');
  if (pPtr == NULL) {           /* a silly answer was returned */
    SICSLogWrite("SPS: Silly answer in SPSGetADC", eError);
    return 0;
  }
  pPtr++;
  for (i = 0; i < 8; i++) {
    pPtr++;                     /* skip whitespace */
    strncpy(pNum, pPtr, 5); /* strlcpy probably wrong here */
    pNum[5] = '\0';
    pPtr += 5;
    iADC[i] = atoi(pNum);
  }

  /* now give the value */
  *iValue = iADC[iWhich];
  return 1;
}

/*-----------------------------------------------------------------------*/
void SPSAddPermission(pSPS self, int iByte, int iBit, int iRights)
{
  Permission perm;

  assert(self);

  perm.iByte = iByte;
  perm.iBit = iBit;
  perm.iPrivilege = iRights;

  LLDnodeAppendFrom(self->lPermissions, &perm);
}

/*-------------------------------------------------------------------------*/
void RemoveSPS(void *pData)
{
  pSPS self = NULL;
  prs232 rs232 = NULL;

  
  self = (pSPS) pData;

  if (!self)
    return;

  rs232 = (prs232)self->pData;
  if (self->pHost) {
    free(self->pHost);
  }

  if(rs232 != NULL){
	  closeRS232(rs232);
	  KillRS232(rs232);
  }
  
  if (self->pDes) {
    DeleteDescriptor(self->pDes);
  }
  LLDdelete(self->lPermissions);
  free(self);
}

/*---------------------------------------------------------------------------*/
int SPSFactory(SConnection * pCon, SicsInterp * pSics, void *pData,
               int argc, char *argv[])
{
  int iRet, iVal;
  pSPS pNew = NULL;
  char pBueffel[256];

  assert(pCon);
  assert(pSics);

  /* we need at least two extra arguments: name and either host or sim */
  if (argc < 3) {
    SCWrite(pCon,
            "ERROR: Insufficient number of arguments for installing SPS",
            eError);
    return 0;
  }

  /* allocate a new SPS */
  pNew = (pSPS) malloc(sizeof(SPS));
  if (!pNew) {
    SCWrite(pCon, "ERROR: no memory to allocate SPS", eError);
    return 0;
  }
  memset(pNew, 0, sizeof(SPS));
  pNew->pDes = CreateDescriptor("SPS");
  pNew->lPermissions = LLDcreate(sizeof(Permission));
  if ((!pNew->pDes) || (pNew->lPermissions < 0)) {
    SCWrite(pCon, "ERROR: no memory to allocate SPS", eError);
    return 0;
  }
  pNew->pHost = strdup(argv[2]);

  /* check if we go for sim */
  strtolower(argv[2]);
  if (strcmp(argv[2], "sim") == 0) {
    pNew->iMode = 1;
  } else {                      /* we are installing a real one and need tons more arguments */

    if (argc < 5) {
      SCWrite(pCon,
              "ERROR: Insufficient number of arguments for installing SPS",
              eError);
      RemoveSPS(pNew);
      return 0;
    }
    /* get port number */
    iRet = Tcl_GetInt(pSics->pTcl, argv[3], &iVal);
    if (iRet != TCL_OK) {
      snprintf(pBueffel,255,
              "ERROR: expected integer argument for port, got %s",
              argv[3]);
      SCWrite(pCon, pBueffel, eError);
      RemoveSPS(pNew);
      return 0;
    }
    pNew->iPort = iVal;

    /* get channel number */
    iRet = Tcl_GetInt(pSics->pTcl, argv[4], &iVal);
    if (iRet != TCL_OK) {
      snprintf(pBueffel,255,
              "ERROR: expected integer argument for channel, got %s",
              argv[4]);
      SCWrite(pCon, pBueffel, eError);
      RemoveSPS(pNew);
      return 0;
    }
    pNew->iChannel = iVal;
  }

  /* initialise */
  iRet = init(pNew);
  if (!iRet) {
    SCWrite(pCon, "ERROR: Failure to initialise SPS controller", eError);
    RemoveSPS(pNew);
    return 0;
  }

  /* install command */
  iRet = AddCommand(pSics, argv[1], SPSAction, RemoveSPS, pNew);
  if (!iRet) {
    snprintf(pBueffel,255, "ERROR: duplicate SPS command %s NOT created",
            argv[1]);
    SCWrite(pCon, pBueffel, eError);
    RemoveSPS(pNew);
  }
  return 1;
}

/*-------------------------------------------------------------------------*/
int SPSAction(SConnection * pCon, SicsInterp * pSics, void *pData,
              int argc, char *argv[])
{
  pSPS self = NULL;
  char pBueffel[256];
  int iByte, iBit, iSet;
  int iRet, i;
  float fLang;

  self = (pSPS) pData;
  assert(self);

  /* we need at least 3 arguments */
  if (argc < 2) {
    snprintf(pBueffel,255, "ERROR: need at least two arguments to %s", argv[0]);
    SCWrite(pCon, pBueffel, eError);
    return 0;
  }

  /* do something according to key word */
  strtolower(argv[1]);
  if (strcmp(argv[1], "push") == 0) {   /* operate a button */
    /* four arguments needed */
    if (argc < 4) {
      snprintf(pBueffel,255, "ERROR: need at least two arguments to %s push",
              argv[0]);
      SCWrite(pCon, pBueffel, eError);
      return 0;
    }
    /* convert arguments */
    iRet = Tcl_GetInt(pSics->pTcl, argv[2], &iByte);
    if (iRet != TCL_OK) {
      snprintf(pBueffel,255,
              "ERROR: expected integer argument for byte, got %s",
              argv[2]);
      SCWrite(pCon, pBueffel, eError);
      return 0;
    }
    iRet = Tcl_GetInt(pSics->pTcl, argv[3], &iBit);
    if (iRet != TCL_OK) {
      snprintf(pBueffel,255, "ERROR: expected integer argument for bit, got %s",
              argv[3]);
      SCWrite(pCon, pBueffel, eError);
      return 0;
    }
    /* try it */
    iRet = SPSSetButton(self, pCon, iByte, iBit);
    if (!iRet) {
      SCWrite(pCon, "ERROR: Pushing button failed", eError);
      return 0;
    } else {
      SCSendOK(pCon);
      return 1;
    }
  } else if (strcmp(argv[1], "status") == 0) {  /* status bits */
    /* which bit ? */
    if (argc < 3) {
      snprintf(pBueffel,255, "ERROR: need at least two arguments to %s push",
              argv[0]);
      SCWrite(pCon, pBueffel, eError);
      return 0;
    }
    iRet = Tcl_GetInt(pSics->pTcl, argv[2], &iByte);
    if (iRet != TCL_OK) {
      snprintf(pBueffel, 255, "ERROR: expected integer argument for bit, got %s",
              argv[2]);
      SCWrite(pCon, pBueffel, eError);
      return 0;
    }
    iRet = SPSGetStatus(self, iByte, &iSet);
    if (iRet <= 0) {
      snprintf(pBueffel,255, "ERROR: failed to read status bit %d", iByte);
      SCWrite(pCon, pBueffel, eError);
      return 0;
    }
    sprintf(pBueffel, "%s.status.%3.3d = %d", argv[0], iByte, iSet);
    SCWrite(pCon, pBueffel, eValue);
    return 1;
  } else if (strcmp(argv[1], "stat2") == 0) {   /* status bits */
    if (argc < 4) {
      snprintf(pBueffel,255, "ERROR: need at least two arguments to %s stat2",
              argv[0]);
      SCWrite(pCon, pBueffel, eError);
      return 0;
    }
    /* which bit ? */
    iRet = Tcl_GetInt(pSics->pTcl, argv[2], &iByte);
    if (iRet != TCL_OK) {
      snprintf(pBueffel,255, "ERROR: expected integer argument for bit, got %s",
              argv[2]);
      SCWrite(pCon, pBueffel, eError);
      return 0;
    }
    iRet = Tcl_GetInt(pSics->pTcl, argv[3], &iBit);
    if (iRet != TCL_OK) {
      snprintf(pBueffel, 255, "ERROR: expected integer argument for bit, got %s",
              argv[3]);
      SCWrite(pCon, pBueffel, eError);
      return 0;
    }
    if ((iByte <= 0) || (iBit < 0) || (iBit > 7)) {
      SCWrite(pCon, "ERROR: arguments out of range for stat2", eError);
      return 0;
    }
    iRet = SPSGetStatus(self, ((iByte - 1) * 8 + iBit), &iSet);
    if (iRet <= 0) {
      sprintf(pBueffel, "ERROR: failed to read status bit %d", iByte);
      SCWrite(pCon, pBueffel, eError);
      return 0;
    }
    sprintf(pBueffel, "%s.status.%3.3d.%1.1d = %d", argv[0], iByte, iBit,
            iSet);
    SCWrite(pCon, pBueffel, eValue);
    return 1;
  } else if (strcmp(argv[1], "adc") == 0) {     /* ADC values */
    /* which ADC ? */
    iRet = Tcl_GetInt(pSics->pTcl, argv[2], &iByte);
    if (iRet != TCL_OK) {
      snprintf(pBueffel,255, "ERROR: expected integer argument for ADC, got %s",
              argv[2]);
      SCWrite(pCon, pBueffel, eError);
      return 0;
    }
    iRet = SPSGetADC(self, iByte, &iSet);
    if (!iRet) {
      sprintf(pBueffel, "ERROR: failed to read ADC %d", iByte);
      SCWrite(pCon, pBueffel, eError);
      return 0;
    }
    sprintf(pBueffel, "%s.ADC.%1.1d = %d", argv[0], iByte, iSet);
    SCWrite(pCon, pBueffel, eValue);
    return 1;
  } else if (strcmp(argv[1], "perm") == 0) {
    /* only managers may set permissions */
    if (!SCMatchRights(pCon, usMugger)) {
      SCWrite(pCon,
              "ERROR: Security: Only mangagers may configure SPS buttons",
              eError);
      return 0;
    }

    /* we need lots of parameters */
    if (argc < 5) {
      snprintf(pBueffel,255, "ERROR: need at least three arguments to %s perm",
              argv[0]);
      SCWrite(pCon, pBueffel, eError);
      return 0;
    }
    /* convert arguments */
    iRet = Tcl_GetInt(pSics->pTcl, argv[2], &iByte);
    if (iRet != TCL_OK) {
      snprintf(pBueffel,255,
              "ERROR: expected integer argument for byte, got %s",
              argv[2]);
      SCWrite(pCon, pBueffel, eError);
      return 0;
    }
    iRet = Tcl_GetInt(pSics->pTcl, argv[3], &iBit);
    if (iRet != TCL_OK) {
      snprintf(pBueffel,255, "ERROR: expected integer argument for bit, got %s",
              argv[3]);
      SCWrite(pCon, pBueffel, eError);
      return 0;
    }
    /* try to convert user code */
    strtolower(argv[4]);
    iSet = 0;
    iSet = decodeSICSPriv(argv[4]);
    if (iSet < 0) {
      sprintf(pBueffel, "ERROR: %s is not valid user code", argv[4]);
      SCWrite(pCon, pBueffel, eError);
      return 0;
    }
    /* do it, finally */
    SPSAddPermission(self, iByte, iBit, iSet);
    SCSendOK(pCon);
    return 1;
  }
/* -------- sans collimator */
  else if (strcmp(argv[1], "colli") == 0) {
    iRet = SPSGetSANS(self, &fLang);
    if (!iRet) {
      SCWrite(pCon, "ERROR: cannot read SANS collimator", eError);
      return 0;
    } else {
      sprintf(pBueffel, "SANS.collimator = %f", fLang);
      SCWrite(pCon, pBueffel, eValue);
      return 1;
    }
  }
  /* -------- bit pattern*/
    else if (strcmp(argv[1], "bipa") == 0) {
    	return SPSBitPattern(self,pCon);
    }
  sprintf(pBueffel, "ERROR: %s does not not understand subcommand %s",
          argv[0], argv[1]);
  SCWrite(pCon, pBueffel, eError);
  return 0;
}
