/****************************************************************************/
/* Copyright 1992 to 1996 MBARI                                             */
/****************************************************************************/
/* Summary  : UNIX alarm logger application                                 */
/* Filename : alarmLogger.c                                                 */
/* Author   : Andrew Pearce                                                 */
/* Project  : Tiburon                                                       */
/* Version  : Version 1.0                                                   */
/* Created  : 06/18/97                                                      */
/* Modified : 04/27/98                                                      */
/* Archived :                                                               */
/****************************************************************************/
/* Modification History:                                                    */
/* $Header: $
 * $Log:    $
 */
/****************************************************************************/

#include <stdio.h>
#include <string.h>
#include <time.h>
#include <signal.h>

#include <types.h>                  /* mbari style guide type declarations  */
#include <dmUnix.h>		    /* UNIX versions of vxWorks constants   */

#include <lstLib.h>		    /* UNIX version of vxWorks lstLib.h     */
#include <semLib.h>                 /* UNIX version of vxWorks semLib.h     */

#include <datamgr.h>                /* Data Manager declarations            */

#define EQUAL 0
#define LINE_LEN 	132
#define FIELD_DELIM	" \t\n"

#define DM_GROUP_SIZE 32

#define DFLT_ALARM_DEF_FILE "/usr/tiburon/unix/gui/tmacs/config/alarmDef.dat"
#define DFLT_ALARM_LOG_FILE "/usr/tiburon/logs/alarm.log"

#define ALIGN(n, t)     ((t>1) ? (((n)+t-1) & ~(t-1)) : (n))

Extern Nat32		type_sizes[], *type_align;

MLocal MBool debugMode = FALSE;
MLocal FILE  *alarmLogFile;	    /* Alarm Log File                        */

typedef struct
{
    NODE        alarmNode;
    char        dmName[MAX_NMLEN];
    DM_Item     dmItem;
    DM_Type     dmType;
} alarmItemEntry;

typedef struct
{
    NODE groupNode;
    DM_Group dmGroup;

    struct 
    {
	DWord changeBit;
	alarmItemEntry *alarmItemPtr;
    } dmItem[DM_GROUP_SIZE];
} dmGroupEntry;

    Void
printSignOn( Void )
{
    time_t timeNow;		    /* Time from real-time clock            */
    char   createTime[30];	    /* Big enough to hold ctime string      */
    
    timeNow = time ( (time_t *) 0); /* Read real-time clock                 */ 
    strcpy(createTime, ctime(&timeNow));
    createTime[24] = '\0';          /* Remove pesky newline from string     */
    
    printf("Tiburon Alarm Logger Started at: %-24s\n", createTime);
    fprintf(alarmLogFile, "Tiburon Alarm Logger Started at: %-24s\n",
	    createTime);
}  /* printSignOn() */


    MLocal STATUS
alarmItemStartConsumer( LIST *dmGroupList, alarmItemEntry *alarmItem,
    SEM_ID updateSem )
{
    MLocal Nat16 groupIdx = 0;
    MLocal dmGroupEntry *groupEntry; 
    
    if ((alarmItem->dmItem = dm_lookup(alarmItem->dmName, 0)) == NO_ITEM)
	return (ERROR);

    if (groupIdx == 0)
    {
	if ((groupEntry = (dmGroupEntry *)
	     malloc(sizeof(dmGroupEntry))) == NULL)
	    return (ERROR);

	bzero(groupEntry, sizeof(groupEntry));
	groupEntry->dmGroup = dm_create_group(); /* Create Data Manager Group*/

	lstAdd (dmGroupList, (NODE *) groupEntry);
    } /* if */

    if (dm_start_consumer(alarmItem->dmItem, DM_ASYNC, updateSem) != SUCCESS)
	return(ERROR);
    
    groupEntry->dmItem[groupIdx].alarmItemPtr = alarmItem;

    dm_group_add_item(groupEntry->dmGroup, alarmItem->dmItem,
        &groupEntry->dmItem[groupIdx].changeBit);

    if (++groupIdx == DM_GROUP_SIZE)
	groupIdx = 0;

    return(OK);
} /*alarmItemStartConsumer() */     


    MLocal STATUS
writeDmItemTime(char *buffer, DM_Time dmTime)
{
    char      *updateTimeString;
    struct tm *log_tm;

    log_tm = localtime( (time_t *) &dmTime.tv_sec );

    sprintf( buffer, "%02d/%02d/%02d %02d:%02d:%02d.%01d ",
	     log_tm->tm_year, log_tm->tm_mon + 1, log_tm->tm_mday,
	     log_tm->tm_hour, log_tm->tm_min, log_tm->tm_sec,
	     dmTime.tv_usec/100000 );

    return (strlen(updateTimeString));
} /* writeDmItemTime() */

/************************************************************************/
/* Function    : sprintfDmItem 	           	                        */
/* Purpose     : Print DM Item Value to a buffer                        */
/* Inputs      : Data Mgr Item,                      			*/
/* Outputs     : Returns nBytes written to buffer or ERROR  	        */
/************************************************************************/
    MLocal Int32
sprintfDmItem( char *buffer, char *time, DM_Item dmItem )
{
    DmItemSts itemStatus;		 /* Status struct		*/
    Nat32     nBytes;                    /* Nbytes written to buffer    */
    char      dmData[MAX_ITM_SIZE];	 /* Buffer forthe item data    */
    DM_Time   itemTime;
    
    Reg char	   *dataPtr;
    char           *cksumPtr;     
    Reg DM_Element *ep;
    Reg Nat32	   element;
    Reg Nat32	   alignAdjust;
    Flt32	   f;
    Flt64	   g;

    if ( dm_item_status(dmItem, &itemStatus) != SUCCESS )
	return( ERROR );

    if ( dm_read(dmItem, dmData, itemStatus.is_size, &itemTime) != SUCCESS)
	return( ERROR );     /* THERE IS A MEMORY LEAK HERE */

    nBytes = 0;
    alignAdjust = 0;
    ep = itemStatus.is_compound;

    for ( element = 0; element < ep->elt_num; element++ )
    {
	alignAdjust = ALIGN( alignAdjust, type_align[ep->elt_type] );
	dataPtr = dmData + alignAdjust;

	switch( ep->elt_type )
	{
	  case DM_EMPTY:
	    nBytes += sprintf(buffer, "Event");
	    break;

	  case DM_CHAR:
	  case DM_UCHAR:
	    nBytes += sprintf(buffer, "%s", (char *) &dataPtr[0]);
	    element = ep->elt_num;
	    break;
		
	  case DM_INT16:
	  case DM_MBOOL:
	    nBytes += sprintf(buffer, "%d", *(Int16 *) dataPtr);
	    break;

	  case DM_NAT16:
	    nBytes += sprintf(buffer, "%d", *(Nat16 *) dataPtr);
	    break;

	  case DM_INT32:
	  case DM_ENUM:
	    nBytes += sprintf(buffer, "%d", *(Int32 *) dataPtr);
	    break;

	  case DM_NAT32:
	    nBytes += sprintf(buffer, "%d", *(Nat32 *) dataPtr);
	    break;

	  case DM_FLT32:
	    bcopy(dataPtr, (char *) &f, sizeof(Flt32));
	    g = f;
	    nBytes += sprintf(buffer, "%f", g);
	    break;

	  case DM_FLT64:
	    bcopy(dataPtr, (char *) &g, sizeof(Flt64));
	    nBytes += sprintf(buffer, "%f", g);
	    break;

	  default:
	    break;

	} /* switch */

	alignAdjust += type_sizes[ep->elt_type];
    } /* if */

    dm_item_status_free( &itemStatus );

    writeDmItemTime(time, itemTime);

    return( nBytes );
} /* sprintfDmItem() */


    MLocal STATUS
logAlarm( alarmItemEntry *alarmItem )
{
    char buffer[80];
    char time[20];
    Nat32 nBytes = 0;
    
    nBytes = sprintfDmItem(buffer, time, alarmItem->dmItem);

    if (debugMode)
        printf ("%s %s, Value %s\n", time, alarmItem->dmName, buffer);

    fprintf (alarmLogFile, "%s %s, Value %s\n",
        time, alarmItem->dmName, buffer);

    return(nBytes != ERROR ? OK : ERROR);
} /* logAlarm() */

   MLocal STATUS
alarmLogger( LIST *alarmList )
{
    SEM_ID updateSem;
    LIST  dmGroupList;
    dmGroupEntry   *groupEntry;
    alarmItemEntry *alarmItem;

    Int32 item;
    DWord changeBits;

    lstInit(&dmGroupList);

    if ((updateSem = semBCreate(SEM_Q_FIFO, SEM_EMPTY)) == NULL)
    {
	printf("alarmLogger: Error creating update semaphore");
	fprintf(alarmLogFile, "alarmLogger: Error creating update semaphore");
	return(ERROR);
    } /* if */

    for (alarmItem  = (alarmItemEntry *) lstFirst(alarmList); 
	 alarmItem != (alarmItemEntry *) NULL;
	 alarmItem  = (alarmItemEntry *) lstNext((NODE*) alarmItem))
	alarmItemStartConsumer(&dmGroupList, alarmItem, updateSem);

    while (TRUE)
    {
	semTake(updateSem, WAIT_FOREVER);
	
	for (groupEntry  = (dmGroupEntry *) lstFirst(&dmGroupList); 
	     groupEntry != (dmGroupEntry *) NULL;
	     groupEntry  = (dmGroupEntry *) lstNext((NODE*) groupEntry))
	{
	    if ((changeBits = dm_get_group_changes(groupEntry->dmGroup)) != 0)
	    {
		for (item = 0; item < DM_GROUP_SIZE; item++)
		    if (changeBits & groupEntry->dmItem[item].changeBit)
			logAlarm(groupEntry->dmItem[item].alarmItemPtr); 
	    } /* for */
	} /* for */

	fflush(alarmLogFile);
    } /* while */
} /* alarmLogger() */

    MLocal DM_Type
lkupDmAlarmType(char *type)
{
    if  (strcmp(type, "EMPTY") == EQUAL)
	return (DM_EMPTY);

    if  (strcmp(type, "EVENT") == EQUAL)
	return (DM_EMPTY);

    if  (strcmp(type, "BOOLEAN") == EQUAL)
	return (DM_MBOOL);

    if  (strcmp(type, "INT16") == EQUAL)
	return (DM_INT16);

    if 	(strcmp(type, "NAT16") == EQUAL)
	return (DM_NAT16);

    if  (strcmp(type, "INT32") == EQUAL)
	return (DM_INT32);

    if  (strcmp(type, "NAT32") == EQUAL)
	return (DM_NAT32);

    if  (strcmp(type, "MICRO") == EQUAL)
	return (DM_ENUM);

    if  (strcmp(type, "GF") == EQUAL)
	return (DM_ENUM);

    if  (strcmp(type, "GFTEST") == EQUAL)
	return (DM_ENUM);

    if  (strcmp(type, "GF5VTEST") == EQUAL)
	return (DM_ENUM);

    if  (strcmp(type, "MOTOR") == EQUAL)
	return (DM_ENUM);

    if  (strcmp(type, "FLT32") == EQUAL)
	return (DM_FLT32);

    if  (strcmp(type, "FLT64") == EQUAL)
	return (DM_FLT64);

    if  (strcmp(type, "ENUM") == EQUAL)
	return (DM_ENUM);

    return (DM_ENDT);
} /* lkupAlarmDmType() */


    MLocal STATUS
addAlarmDmItem(LIST *alarmList, char *alarmDmName, char* alarmDmType)
{
    alarmItemEntry *alarmItem;

    if ((alarmItem = (alarmItemEntry *)
	 malloc(sizeof(alarmItemEntry))) == NULL)
        return (ERROR);

    alarmItem->dmItem = NO_ITEM;
    if ((alarmItem->dmType = lkupDmAlarmType(alarmDmType)) == DM_ENDT)
    {
	printf ("Unsupported Alarm Type: %s\n", alarmDmType);
	fprintf(alarmLogFile, "Unsupported Alarm Type: %s\n", alarmDmType);
	return (ERROR);
    } /* if */

    strcpy(alarmItem->dmName,   alarmDmName);
    lstAdd (alarmList, (NODE *) alarmItem);

    return(OK);
} /* addAlarmDmItem() */

    STATUS
parseAlarmFile(char *alarmFileName, LIST *alarmList)
{
    FILE   *alarmFile;	 	    /* TMACS alarm file Handle               */
    char   buffer[LINE_LEN];	    /* Scratch buffer for freads             */
    char   *token, *ptr;

    char   alarmDmName[LINE_LEN];
    char   alarmDmType[LINE_LEN];

    MBool  bracesOpen = FALSE;

    if ( (alarmFile = fopen(alarmFileName, "r")) == NULL )
	return(ERROR);

    				    /* Start reading TMACS alarm file        */
    while(fgets(buffer, LINE_LEN, alarmFile) != NULL)
    {
	token = (strtok(buffer, FIELD_DELIM));
	if (token[0] != '#')    /* Ignore for comment lines              */
	{
	    if (!bracesOpen)
	    {
		for (ptr = token; ptr < token + strlen(token); ptr++)
		    *ptr = toupper(*ptr);	/* Convert to upper case */
		strcpy (alarmDmType, token);

		if ((token = strtok(NULL, FIELD_DELIM)) != NULL)
		    strcpy(alarmDmName, token);
		    
		if ((token = strtok(NULL, FIELD_DELIM)) != NULL)
		    if (strchr(token, '{'))
			bracesOpen = TRUE;
	    } /* if */
	    else
	    {
		if (strchr(token, '}'))
		{
		    bracesOpen = FALSE;
		    addAlarmDmItem(alarmList, alarmDmName, alarmDmType);
		} /* if */
	    } /* else */
	} /* if */
    } /* while */
    
    fclose (alarmFile);
    return(OK);
} /* parseAlarmFile() */


     Void
terminate( int ignored )
{
    time_t timeNow;		    /* Time from real-time clock            */
    char   createTime[30];	    /* Big enough to hold ctime string      */
    
    timeNow = time ( (time_t *) 0); /* Read real-time clock                 */ 
    strcpy(createTime, ctime(&timeNow));
    createTime[24] = '\0';          /* Remove pesky newline from string     */
    
    printf("\nTiburon Alarm Logger Terminated at: %-24s\n", createTime);
    fprintf(alarmLogFile, "\nTiburon Alarm Logger Terminated at: %-24s\n",
	    createTime);

    fprintf(alarmLogFile, "#################################################################\n\n");

    fflush(alarmLogFile);
    fclose(alarmLogFile);

    exit(0);
} /* terminate() */



   int
main( int argc, char *argv[] )
{
    LIST  alarmItemList;
    char  alarmDefFileName[80];	    /* File name for alarm definition file   */
    char  alarmLogFileName[80];	    /* File name for alarm logging file      */

    pid_t child;
    
    if (argc > 3)
    {
        printf("Usage: alarmLogger alarm.log alarm.def \n");
        exit(1);
    } /* if */

    if (argc == 3)
	strcpy(alarmDefFileName, argv[2]);	  /* User specified def file */
    else
	strcpy(alarmDefFileName, DFLT_ALARM_DEF_FILE);

    if (argc >= 2)
	strcpy(alarmLogFileName, argv[1]);	  /* User specified log file */
    else
	strcpy(alarmLogFileName, DFLT_ALARM_LOG_FILE);

    if ( (alarmLogFile = fopen(alarmLogFileName, "a")) == NULL )
    {
	printf("Error opening alarm log file %s\n",alarmLogFileName); 
	exit(-1);
    } /* if */

/* Set signal handlers if user trys to quit				*/
    signal( SIGINT,  terminate );
    signal( SIGQUIT, terminate );
    signal( SIGTERM, terminate );

    printSignOn();		    /* Print Alarm Logger Start Time         */
    lstInit(&alarmItemList);	    /* Initialize alarm item list            */

    printf("Reading alarm definitions from: %s\n\n", alarmDefFileName);
    fprintf(alarmLogFile, "Reading alarm definitions from: %s\n\n",
	alarmDefFileName);

                                    /* Read TMACS alarm definition file      */
    if (parseAlarmFile(alarmDefFileName, &alarmItemList) != OK)
    {
	printf("Error reading alarm file\n\n");
	fprintf(alarmLogFile,"Error reading alarm file: %s\n\n",
	   alarmDefFileName);
	fclose(alarmLogFile);
	exit(-1);
    } /* if */

    if (!debugMode)
    {
	if ((child = fork()) < 0)
	{
	    printf("Unable to fork alarmLogger\n");
	    exit(-1);
	} /* if */

	else if (child > 0)
	    exit(0);		    /* parent exit. Lets child to do the work*/
    } /* if */
                                    /* Initialize Data Manager services      */
    if (dm_init( (DM_Define *) NULL) != SUCCESS)
    {
	printf("Data Manager Not Running\n");
	fprintf(alarmLogFile, "Data Manager Not Running\n");
	fclose(alarmLogFile);
	exit(-1);
    } /* if */

    alarmLogger(&alarmItemList);
    
    terminate(0); 		    /* Program exited without errors         */
} /* main */		

