/****************************************************************************/
/* Copyright 1992 to 1996 MBARI                                             */
/****************************************************************************/
/* Summary  : Data Manager Item Logging Program                             */
/* Filename : dmLogger.c                                                    */
/* Author   : Andrew Pearce                                                 */
/* Project  : TIburon ROV                                                   */
/* Version  : Version 1.0                                                   */
/* Created  : 10/28/96                                                      */
/* Modified : 10/28/96                                                      */
/* Archived :                                                               */
/****************************************************************************/
/* Modification History:                                                    */
/* $Header: $
 * $Log:    $
 */
/****************************************************************************/

#include <mbari/types.h>
#include <dmUnix.h>
#include <semLib.h>
#include <datamgr.h>
#include <dm_errno.h>
#include <lstLib.h>
#include <semLib.h>

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <signal.h>

#include <datamgr.h>                /* Data Manager declarations            */
#include <dm_errno.h>               /* Data Manager error declarations      */

#include <westernFlyer.h>           /* Western Flyer Vessel Definitions     */
#include <tiburon.h>                /* Tiburon Definitions                  */

#define DEFAULT_CONFIG_FILE  "./dmLogger.cfg"
#define DEFAULT_LOG_FILE     "./dmLogger.data"
#define FIELD_DELIM           " ,\t\n"

#define LINE_LEN         132
#define MAX_FUNCNAME_LEN  32
#define MAX_FORMAT_LEN    32
#define MAX_EOL_LEN        8

#define DM_ITEM_NAME_LEN 128

#define ALIGN(n, t)     ((t>1) ? (((n)+t-1) & ~(t-1)) : (n))

#define CR 0x0d
#define LF 0x0a
#define EQUAL 0
#define FOREVER for (;;)

#define TRIGGER_MODE 0.0

Extern Nat32            type_sizes[], *type_align;

MLocal LIST  logFileItemList;
MLocal MBool logFileListInit = FALSE;

typedef enum
{
    SLAVE,
    TRIGGER
} triggerMode;

typedef struct
{
    NODE        cfgNode;
    char        dmItemName[MAX_NMLEN];
    MBool       arrayDmItem;
    Int32       arrayIndex;
    char        format[MAX_FORMAT_LEN];
    MBool       appendTime;
    MBool       appendEol;
    triggerMode outMode;
    DM_Item     dmItem;
} dmOutEntry;

MLocal SEM_ID  dmUpdateSem;         /* Data Manager Item Change wakeup sem   */
MLocal FILE  *logFile;              /* Data Logging File Handle             */

     MLocal Void
mapNewLine(char *buffer)
{
    char *newLine;

    if (newLine = strstr(buffer, "\\n"))
        strcpy(newLine, "\n");
} /* mapNewLine() */


    MLocal char*
getNextToken(char *errorMsg)
{
    char *token, *ptr, *p;

    if ((token = strtok(NULL, FIELD_DELIM)) == NULL)
    {
        if (strlen(errorMsg)) printf(errorMsg);
        return (NULL);
    } /* if */

    for (ptr = token; ptr < token + strlen(token); ptr++)
        *ptr = toupper(*ptr);

    return (token);
} /* getNextToken() */

    MLocal char*
getNextArgument(char *errorMsg)
{
    char *token;

    if ((token = strtok(NULL, FIELD_DELIM)) == NULL)
    {
        if (strlen(errorMsg)) printf(errorMsg);
        return (NULL);
    } /* if */

    mapNewLine(token);          /* Map \n to New Line                      */

    return (token);
} /* getNextArgument() */

    MLocal char*
getQuotedString(char *buffer)
{
    char *token, *ptr;
                                /* Find first char NOT in Field Delim List  */
    ptr = buffer + strspn(buffer, FIELD_DELIM);

    if (*ptr == '"')            /* If First character is a quote            */
    {                           /* Extract string up to closing quote       */
        ptr = strtok(ptr + 1, "\"");
        mapNewLine(ptr);        /* Map \n to New Line                       */
        return (ptr);
    } /* if */

    return (NULL);
} /* getQuotedString() */

    MLocal DM_Type
lkupDmType(char *type)
{
    if  (strcmp(type, "EMPTY") == EQUAL)
        return (DM_EMPTY);

    if  (strcmp(type, "CHAR") == EQUAL)
        return (DM_CHAR);

    if  (strcmp(type, "UCHAR") == EQUAL)
        return (DM_UCHAR);

    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, "MBOOL") == EQUAL)
        return (DM_MBOOL);

    if  (strcmp(type, "FLT32") == EQUAL)
        return (DM_FLT32);

    if  (strcmp(type, "FLT64") == EQUAL)
        return (DM_FLT64);

    if  (strcmp(type, "PTR") == EQUAL)
        return (DM_PTR);

    if  (strcmp(type, "ENUM") == EQUAL)
        return (DM_ENUM);

    if  (strcmp(type, "ARRAY") == EQUAL)
        return (DM_ARRAY);

    if  (strcmp(type, "STRUCT") == EQUAL)
        return (DM_STRUCT);

    return (DM_EMPTY);
} /* lkupDmType() */

    MLocal STATUS
writeDmItemTime(char *buffer, DM_Time dmTime)
{
    time_t updateTime;
    char   *updateTimeString;

    updateTime = dmTime.tv_sec;

    updateTimeString = asctime(gmtime(&updateTime));
    strncpy(buffer, updateTimeString + 11, 9);

    return (9);
} /* writeDmItemTime() */

/************************************************************************/
/* Function    : sprintfDmItem                                          */
/* Purpose     : Print DM Item Value to a buffer                        */
/* Inputs      : Data Mgr Item, Format Statement                        */
/* Outputs     : Returns nBytes written to buffer or ERROR              */
/************************************************************************/
    MLocal Int32
sprintfDmItem( char *buffer, char *format, DM_Item dmItem, MBool appendTime )
{
    DmItemSts itemStatus;                /* Status struct               */
    Nat32     nBytes;                    /* Nbytes written to buffer    */
    char      dmData[MAX_ITM_SIZE];      /* Buffer for the item data    */
    DM_Time   itemTime;

    Reg char       *dataPtr;
    char           *cksumPtr;
    Reg DM_Element *ep;
    Reg Nat32      element;
    Reg Nat32      alignAdjust = 0;
    Flt32          f;
    Flt64          g;

    if (dmItem == NO_ITEM)
        return (sprintf(buffer, " #### "));

    if ( dm_item_status(dmItem, &itemStatus) != SUCCESS )
        return( ERROR );

    if ( dm_read(dmItem, dmData, itemStatus.is_size, &itemTime) != SUCCESS)
        return( ERROR );

    nBytes = 0;
    if (appendTime)
        nBytes += writeDmItemTime(buffer, itemTime);

    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:
            break;

          case DM_CHAR:
          case DM_UCHAR:
            nBytes += sprintf(buffer + nBytes, format, (char *) &dataPtr[0]);
            element = ep->elt_num;
            break;

          case DM_INT16:
          case DM_MBOOL:
            nBytes += sprintf(buffer + nBytes, format, *(Int16 *) dataPtr);
            break;

          case DM_NAT16:
            nBytes += sprintf(buffer + nBytes, format, *(Nat16 *) dataPtr);
            break;

          case DM_INT32:
          case DM_ENUM:
            nBytes += sprintf(buffer + nBytes, format, *(Int32 *) dataPtr);
            break;

          case DM_NAT32:
            nBytes += sprintf(buffer + nBytes, format, *(Nat32 *) dataPtr);
            break;

          case DM_FLT32:
            bcopy(dataPtr, (char *) &f, sizeof(Flt32));
            g = f;
            nBytes += sprintf(buffer + nBytes, format, g);
            break;

          case DM_FLT64:
            bcopy(dataPtr, (char *) &g, sizeof(Flt64));
            nBytes += sprintf(buffer + nBytes, format, g);
            break;

          case DM_PTR:
            nBytes += sprintf(buffer + nBytes, format, *(Nat32 *) dataPtr);
            break;

          default:
            break;

        } /* switch */

        alignAdjust += type_sizes[ep->elt_type];
    } /* if */

    dm_item_status_free( &itemStatus );

    return( nBytes );
} /* sprintfDmItem() */

    MLocal Flt64
updateRateConfig(char *buffer)
{
    char   *token;
    Flt64  updateRate;

    if (strcmp( (token = getNextToken("")), "TRIGGER") == EQUAL)
        updateRate = TRIGGER_MODE;
    else
        updateRate = atof(token);

    return (updateRate);
} /* updateRateConfig */

    MLocal STATUS
logFileConfig(char *buffer, char *logFileName)
{
    char   *token;
    token = getNextArgument("");

    strcpy(logFileName, token);
} /* logFileConfig */


    MLocal STATUS
logItemConfig(char *buffer)
{
    dmOutEntry  *dmOutItem;

    char   *dmItemName;
    char   *format;
    char   *token;
    MBool  arrayDmItem;
    Int32  arrayIndex;
    MBool  appendTime;
    triggerMode outMode;

    if (logFileListInit == FALSE)
    {
        lstInit(&logFileItemList);
        logFileListInit = TRUE;
    } /* if */

    if ((dmItemName =
        getNextArgument("Log: Data Manager Item not specified\n"))
        == NULL) return (ERROR);

    arrayDmItem = FALSE;
    arrayIndex = 0;
    if ((token = strchr(dmItemName, '[')) != NULL)
    {
        arrayDmItem = TRUE;
        arrayIndex = atoi(token + 1);
        *token = '\0';
    } /* if */

    token = dmItemName;
    if ((format = getQuotedString(token + strlen(token) + 1)) == NULL)
    {
        if ((format =
             getNextArgument("Log: Output Format not specified\n"))
            == NULL) return (ERROR);
    } /* if */

    appendTime = ((strcmp(getNextToken(""), "YES") == EQUAL) ? TRUE : FALSE);

    outMode = SLAVE;
    if (strcmp( (token = getNextToken("")), "TRIGGER") == EQUAL)
        outMode = TRIGGER;

    if ((dmOutItem = (dmOutEntry *) malloc(sizeof(dmOutEntry))) ==
        (dmOutEntry *) NULL)
        return (ERROR);

    strcpy(dmOutItem->dmItemName, dmItemName);
    strcpy(dmOutItem->format,     format);

    dmOutItem->arrayDmItem = arrayDmItem;
    dmOutItem->arrayIndex  = arrayIndex;
    dmOutItem->appendTime  = appendTime;
    dmOutItem->outMode     = outMode;

    lstAdd (&logFileItemList, (NODE *) dmOutItem);
    return (OK);
} /* logItemConfig */

/****************************************************************************/
/* Function    : signalHandler                                              */
/* Purpose     : Deletes semaphore resource used by logDmItems              */
/* Inputs      : None                                                       */
/* Outputs     : None                                                       */
/****************************************************************************/
    void
signalHandler( int sig )
{
    semDelete(dmUpdateSem);
    fclose (logFile);
    exit(0);
} /* signalHandler() */

    STATUS
logDMItems(const char *logFileName, Flt64 updateRate)
{
    dmOutEntry  *dmOutItem;

    Int32   nBytes;
    Int32   semTimeout;
    char    buffer[LINE_LEN];

    time_t timeNow;                 /* Time from real-time clock            */
    struct tm *logTime;             /* Item Update time                     */

    if (logFileListInit == FALSE)
        return(OK);
                                    /* Check for DM Output for log file      */
    if (lstCount(&logFileItemList) == 0)
        return(ERROR);

    timeNow = time ( (time_t *) 0); /* Read real-time clock to print time   */
    logTime = localtime (&timeNow);

    if ( (logFile = fopen(logFileName, "a")) != (FILE *) NULL )
    {
        printf("Opened log file %s\n", logFileName);
        fprintf(logFile, "Logging started at: %s\n\n", asctime(logTime));
    } /* if */

    if ((dmUpdateSem = semBCreate(SEM_Q_FIFO, SEM_EMPTY)) == (SEM_ID) NULL)
    {
        printf("Log: Error creating semaphore for log file out task");
        return(ERROR);
    } /* if */

#if UNIX
    signal(SIGCHLD, signalHandler);     /* Add handlers to destroy semaphore */
    signal(SIGTERM, signalHandler);
    signal(SIGINT,  signalHandler);
    signal(SIGQUIT, signalHandler);
#endif

    for (dmOutItem  = (dmOutEntry *) lstFirst(&logFileItemList);
         dmOutItem != (dmOutEntry *) NULL;
         dmOutItem  = (dmOutEntry *) lstNext((NODE*) dmOutItem))
    {
        if ( (dmOutItem->dmItem =
            dm_lookup(dmOutItem->dmItemName, dmOutItem->arrayIndex)) == NO_ITEM)
        {
            if (dmOutItem->arrayDmItem)
                printf("Log: Error looking up %s[%d]\n",
                       dmOutItem->dmItemName, dmOutItem->arrayIndex);
            else
                printf("Log: Error looking up %s\n", dmOutItem->dmItemName);
#if 0
            semDelete(dmUpdateSem);
            return (ERROR);
#endif
        } /* if */

        if ((updateRate == TRIGGER_MODE) && (dmOutItem->outMode == TRIGGER))
            dm_start_consumer(dmOutItem->dmItem, DM_STATIC, dmUpdateSem);
        else
            dm_start_consumer(dmOutItem->dmItem, DM_ASYNC,  SEM_NULL);
    } /* for */

    if (updateRate == TRIGGER_MODE)
        semTimeout = WAIT_FOREVER;
    else
        semTimeout = (Int32) ((Flt64) sysClkRateGet() * updateRate);

    FOREVER
    {
        nBytes = 0;

        for (dmOutItem  = (dmOutEntry *) lstFirst(&logFileItemList);
            (dmOutItem != (dmOutEntry *) NULL) && (nBytes < LINE_LEN);
             dmOutItem  = (dmOutEntry *) lstNext((NODE*) dmOutItem))
        {
            sprintfDmItem( buffer, dmOutItem->format, dmOutItem->dmItem,
                dmOutItem->appendTime);

            if (fputs(buffer, logFile) == EOF)
            {
                semDelete(dmUpdateSem);
                return(ERROR);
            } /* if */
        } /* for */

        fprintf(logFile, "\n");
        fflush(logFile);
                               /* Wait for any data items to change     */
        semTake(dmUpdateSem, semTimeout);
    }  /* FOREVER */

    return(OK);
} /* logDmItems() */

/****************************************************************************/
/* Function    : main                                                       */
/* Purpose     : Logs Data Manager Item values to a file                    */
/* Inputs      : None                                                       */
/* Outputs     : None                                                       */
/****************************************************************************/
   int
main( int argc, char *argv[] )
{
    char  logFileName[LINE_LEN];
    char  configFileName[LINE_LEN];
    FILE  *configFile;              /* Configuration File Handle            */
    Flt64 updateRate;               /* Data Logging Update Rate             */

    char  buffer[LINE_LEN];         /* Scratch buffer for freads            */
    char  *token, *ptr;

    printf ("Data Manager Item Logger %s\n", "Revision 1.0");

#if UNIX
    if (dm_init(0) == EDM_NO_DAEMON)/* Initialize Data Manage Services       */
    {
        printf("Data Manager Not Running\n");
        return(ERROR);
    } /* if */
#endif

    updateRate = TRIGGER_MODE;
    strcpy(logFileName, DEFAULT_LOG_FILE);

    strcpy(configFileName, DEFAULT_CONFIG_FILE);
    if (argc > 1)
        strcpy(configFileName, argv[1]);

    printf("Opening configuration file %s\n", configFileName);

    if ( (configFile = fopen(configFileName, "r")) != (FILE *) NULL )
    {                               /* Start reading configuration file      */
        while(fgets(buffer, LINE_LEN, configFile) != NULL)
        {
            token = strtok(buffer, FIELD_DELIM);
            if (token[0] != '#')    /* Ignore for comment lines              */
            {
                for (ptr = token; ptr < token + strlen(token); ptr++)
                    *ptr = toupper(*ptr);

                if (strcmp(token, "LOG") == EQUAL)
                    logItemConfig(buffer);

                else if (strcmp(token, "UPDATE") == EQUAL)
                    updateRate = updateRateConfig(buffer);

                else if (strcmp(token, "LOGFILE") == EQUAL)
                    logFileConfig(buffer, logFileName);
            } /* if */
        } /* while */

        fclose(configFile);

        logDMItems(logFileName, updateRate);
    } /* if */

    return(OK);
} /* main() */

