/**
 * 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
 */
 #include <sics.h>
 #include <sicsobj.h>
 #include <sicshipadaba.h>
 #include <network.h>
 #include <nwatch.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 {
    int state;
    char host[256];
    int port;
    mkChannel *pSock;
    pNWContext watchContext;
    char lineBuffer[BUFLEN];
 } LMD200, *pLMD200;
 /*----------------------------------------------------------------------------*/
 static void killLMD200(void *data){
    pLMD200 priv = (pLMD200)data;
    if(priv == NULL){
       return;
    }
    if(priv->watchContext != NULL){
        NetWatchRemoveCallback(priv->watchContext);
    } 
    if(priv->pSock != NULL){
        NETClosePort(priv->pSock);
        free(priv->pSock);
    }
    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);
    ReleaseHdbValue(&alarmVal);
}
/*--------------------------------------------------------------------------*/
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){
            node->value.v.floatArray[start] = -9999.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);
    strcpy(myBuffer, lineBuffer);
    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;

    switch(priv->state){
        case IDLE:
        	if(strstr(priv->lineBuffer, "Alarm") != NULL){
        		doAlarm(self, priv->lineBuffer);
        		return;
        	} else if(strstr(priv->lineBuffer,"Pre") != NULL){
        		setAlarm(self,priv->lineBuffer);
            } else  {
              priv->state = WAITDATA1;
              setAlarm(self,"I do not feel very alarmed");
              return;
            }
            break;
        case WAITDATA1:
        	if(strstr(priv->lineBuffer,"...0") == NULL){
        		/* this data is out of order, recover...*/
        		priv->state == IDLE;
        		return;
        	}
            storeData(self, 0, priv->lineBuffer);
            priv->state = WAITDATA2;
            break;   
        case WAITDATA2:
        	if(strstr(priv->lineBuffer,"...0") == NULL){
        		/* this data is out of order, recover...*/
        		priv->state == IDLE;
        		return;
        	}
            storeData(self, 8, priv->lineBuffer);
            priv->state = IDLE;
	    node = GetHipadabaNode(self->objectNode,"data");
	    assert(node != NULL);
	    NotifyHipadabaPar(node,NULL);
            break;   
    }
}
/*---------------------------------------------------------------------------
 * This handles the terminator discovery and causes the evaluation of complete
 * lines.
 * --------------------------------------------------------------------------*/
static int LMD200Callback(void *context, int mode){
    pSICSOBJ self = (pSICSOBJ)context;
    pLMD200 priv = NULL;
    char buffer[512], *pPtr = NULL, line[132];    

    assert(self != NULL);
    priv = (pLMD200)self->pPrivate;
    if(mode == nwatch_read){
        memset(buffer,0,512);
        memset(line,0,132);
        NETRead(priv->pSock, buffer, 512, 0);
	pPtr = stptok(buffer,line,132,"\r");
        while(pPtr != NULL){
	  if(strlen(line) > 3){
            strncpy(priv->lineBuffer,line,BUFLEN);
            interpretLine(self, priv);
            priv->lineBuffer[0] = '\0';
	  }
          pPtr = stptok(pPtr,line,132,"\r");
        }      
    }
    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)); 
        strncpy(priv->host,argv[2], 256);
        priv->port = atoi(argv[3]);
        priv->pSock = NETConnect(priv->host,priv->port);
        if(priv->pSock == NULL){
            SCWrite(pCon,"ERROR: failed to connect to LMD200",eError);
            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,16);
        AddSICSHdbPar(self->objectNode,"alarm", usInternal,textValue); 
        AddSICSHdbPar(self->objectNode,"data", usInternal,dataValue); 
        self->pPrivate = priv;
        self->KillPrivate = killLMD200;
        ReleaseHdbValue(&dataValue);
        ReleaseHdbValue(&textValue);
        NetWatchRegisterCallback(&priv->watchContext, priv->pSock->sockid, 
            LMD200Callback, self);

        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;
} 
