/**
 * This is a module to manage the LMD-400 device used to monitor HRPT detector
 * electronics temperatures and pressures. It is read only. The device sends 
 * alarm messages or temperature readings any now and then asynchronously. The 
 * format of the messages is:
 * - Alarm message: date time device type of alarm
 * - Data message: 3 lines
 * --- date time 
 * --- 001..008 val val val ....
 * --- 009..016 val val ....
 * Please note that the values use a komma as the decimal separator and not a . 
 * 
 * copyright: see file COPYRIGHT
 * 
 * Mark Koennecke, October 2007
 * 
 * modifed to use asynnet and to cope with even more data. 
 * Mark Koennecke, June 2009
 */
#include <sics.h>
#include <sicsobj.h>
#include <sicshipadaba.h>
#include <asynnet.h> 
#include <commandlog.h>
#include <stptok.h>
/*
 * possible states 
 */
#define IDLE      0
#define WAITDATA1 1
#define WAITDATA2 2
 /*
  * private data structure 
  */
#define BUFLEN 1024
typedef struct {
  char host[256];
  int port;
  int asChannel;
  char lineBuffer[BUFLEN];
} LMD200, *pLMD200;
 /*----------------------------------------------------------------------------*/
static void killLMD200(void *data)
{
  pLMD200 priv = (pLMD200) data;
  if (priv == NULL) {
    return;
  }
  ANETclose(priv->asChannel);
  free(priv);
}
/*-------------------------------------------------------------------------*/
static void setAlarm(pSICSOBJ self, char *text)
{
  hdbValue alarmVal;
  pHdb node = NULL;

  alarmVal = MakeHdbText(text);
  node = GetHipadabaNode(self->objectNode, "alarm");
  assert(node != NULL);
  UpdateHipadabaPar(node, alarmVal, NULL);
}

/*--------------------------------------------------------------------------*/
static void doAlarm(pSICSOBJ self, char *lineBuffer)
{

  setAlarm(self, lineBuffer);
  WriteToCommandLog("CERCA>> ", lineBuffer);
  ServerWriteGlobal(lineBuffer, eError);
}

/*--------------------------------------------------------------------------*/
extern char *trim(char *);

static void storeData(pSICSOBJ self, int start, char *lineBuffer)
{
  pHdb node = NULL;
  char number[80], *pPtr = NULL, *pKomma;

  node = GetHipadabaNode(self->objectNode, "data");
  assert(node != NULL);
  /*
   * throw first away
   */
  pPtr = stptok(lineBuffer, number, 80, " ");
  pPtr = trim(pPtr);
  pPtr = stptok(pPtr, number, 80, " ");
  while (pPtr != NULL) {
    if (strstr(number, "noinp") != NULL || strstr(number,"----") != NULL) {
      node->value.v.floatArray[start] = -99.99;
      start++;
    } else {
      pKomma = strchr(number, ',');
      if (pKomma != NULL) {
        *pKomma = '.';
      }
      pKomma = strchr(number, '(');
      if (pKomma != NULL) {
        *pKomma = ' ';
      }
      pKomma = strchr(number, ')');
      if (pKomma != NULL) {
        *pKomma = ' ';
      }
      node->value.v.floatArray[start] = atof(trim(number));
      start++;
    }
    pPtr = trim(pPtr);
    pPtr = stptok(pPtr, number, 80, " ");
  }
}

/*--------------------------------------------------------------------------*/
static int countTokens(char *lineBuffer)
{
  int count = 0;
  char myBuffer[BUFLEN], token[60];
  char *pPtr = NULL;


  memset(myBuffer, 0, BUFLEN);
  strlcpy(myBuffer, lineBuffer,BUFLEN);
  pPtr = myBuffer;
  pPtr = stptok(pPtr, token, 60, " \r\n");
  while (pPtr != NULL) {
    count++;
    pPtr = trim(pPtr);
    pPtr = stptok(pPtr, token, 60, " \r\n");
  }
  return count;
}

/*--------------------------------------------------------------------------*/
static void interpretLine(pSICSOBJ self, pLMD200 priv)
{
  pHdb node = NULL, stamp = NULL;
  hdbValue timeVal;
  
  node = GetHipadabaNode(self->objectNode, "data");
  assert(node != NULL);

  /* printf("Interpreting line: %s\n", priv->lineBuffer); */
  if (strstr(priv->lineBuffer, "Alarm") != NULL) {
	  doAlarm(self, priv->lineBuffer);
	  return;
  } else if (strstr(priv->lineBuffer, "Pre") != NULL) {
	  setAlarm(self, priv->lineBuffer);
  } else if (strstr(priv->lineBuffer, "001...008") != NULL) {
	  storeData(self, 0, priv->lineBuffer);
	  NotifyHipadabaPar(node, NULL);
      return;
  } else if(strstr(priv->lineBuffer,"009...016") != NULL) {
    storeData(self, 8, priv->lineBuffer);
    NotifyHipadabaPar(node, NULL);
  } else if(strstr(priv->lineBuffer,"017...019") != NULL) {
	  storeData(self, 16, priv->lineBuffer);
	  NotifyHipadabaPar(node, NULL);
  } else {
	  if(strlen(priv->lineBuffer) > 5){
		  timeVal = MakeHdbText(priv->lineBuffer);
		  stamp = GetHipadabaNode(self->objectNode, "timestamp");
		  if(stamp != NULL){
			  UpdateHipadabaPar(stamp, timeVal, NULL);
		  }
	  }
  }
}
/*----------------------------------------------------------------------------*/
static int LMD200Callback(int handle, void *userData)
{
	pSICSOBJ self = (pSICSOBJ)userData;
	pLMD200 priv = (pLMD200)self->pPrivate;
	char *pPtr = NULL, *pEnd = NULL;
	int length;
	
	pPtr = ANETreadPtr(priv->asChannel,&length);
	pEnd = strchr(pPtr,(int)'\r');
	while(pEnd != NULL){
		*pEnd = '\0';
		strlcpy(priv->lineBuffer,pPtr,BUFLEN);
		interpretLine(self,priv);
		ANETreadConsume(priv->asChannel,(pEnd - pPtr)+1);
		pPtr = ANETreadPtr(priv->asChannel,&length);
		if(length > 0){
			pEnd = strchr(pPtr,(int)'\r');
		} else {
			pEnd = NULL;
		}
	}
	
	return 1;
}
/*---------------------------------------------------------------------------*/
static int ClearCmd(pSICSOBJ ccmd, SConnection * con,
                      Hdb * cmdNode, Hdb * par[], int nPar)
{
	setAlarm(ccmd,"I do not feel very alarmed...");
	SCSendOK(con);
	return 1;
}
 /*---------------------------------------------------------------------------*/
int MakeLMD200(SConnection * pCon, SicsInterp * pSics, void *pData,
               int argc, char *argv[])
{
  pSICSOBJ self = NULL;
  pLMD200 priv = NULL;
  hdbValue dataValue, textValue;
  int status;

  if (argc < 4) {
    SCWrite(pCon,
            "ERROR: require name  host and port parameters for initialization",
            eError);
    return 0;
  }
  priv = (pLMD200) malloc(sizeof(LMD200));
  memset(priv, 0, sizeof(LMD200));
  strlcpy(priv->host, argv[2], 256);
  priv->port = atoi(argv[3]);
  priv->asChannel = ANETconnect(priv->host, priv->port);
  if (priv->asChannel  < 0) {
    SCWrite(pCon, "ERROR: failed to connect to LMD200", eLogError);
    killLMD200(priv);
    return 0;
  }
  
  self = MakeSICSOBJv(argv[1], "LMD400", HIPNONE, 0);
  if (self == NULL || priv == NULL) {
    return 0;
  }
  textValue = MakeHdbText("I do not feel very alarmed");
  dataValue = makeHdbValue(HIPFLOATAR, 32);
  AddSICSHdbPar(self->objectNode, "alarm", usInternal, textValue);
  AddSICSHdbPar(self->objectNode, "timestamp", usInternal, textValue);
  AddSICSHdbPar(self->objectNode, "data", usInternal, dataValue);
  self->pPrivate = priv;
  self->KillPrivate = killLMD200;
  ReleaseHdbValue(&dataValue);
  ANETsetReadCallback(priv->asChannel,LMD200Callback,self, NULL);

  AddSICSHdbPar(self->objectNode,
                      "clearalarm", usUser, MakeSICSFunc(ClearCmd));
  
  status = AddCommand(pSics,
                      argv[1], InterInvokeSICSOBJ, KillSICSOBJ, self);
  if (status != 1) {
    KillSICSOBJ(self);
    SCPrintf(pCon, eError, "ERROR: failed create duplicate command %s",
             argv[1]);
    return 0;
  }
  return 1;
}
