/************************************************************************/
/* Copyright 2012 MBARI							*/
/************************************************************************/
/* Summary  : Functions to manipulate system variables for BEDS		*/
/* Filename : variable.c						*/
/* Author   : Robert Herlien (rah)					*/
/* Project  : Benthic Event Detection System (BEDS)			*/
/* Revision : 1.0							*/
/* Created  : 09/24/2012						*/
/*									    */
/* MBARI provides this documentation and code "as is", with no warranty,    */
/* express or implied, of its quality or consistency. It is provided without*/
/* support and without obligation on the part of the Monterey Bay Aquarium  */
/* Research Institute to assist in its use, correction, modification, or    */
/* enhancement. This information should not be published or distributed to  */
/* third parties without specific written permission from MBARI.            */
/*									    */
/************************************************************************/
/* Modification History:						*/
/* 24sep2012 rah - created from Oasis4 parm.c				*/
/* $Log$
 */
/************************************************************************/

#include <mbariTypes.h>			/* MBARI type definitions	    */
#include <mbariConst.h>			/* MBARI constants		    */
#include <cfxpico.h>			/* Persistor PicoDOS definitions    */
#include <beds.h>			/* BEDS controller definitions	    */
#include <variable.h>			/* Variable function definitions    */
#include <utils.h>			/* BEDS Utility routines	    */
#include <dmpSetup.h>			/* BEDS DMP setup routines	    */
#include <fileUtils.h>			/* BEDS file utilities definitions  */
#include <syslog.h>                     /* System logger		    */

#include <stdio.h>			/* Standard I/O			    */
#include <stdlib.h>			/* Standard C library		    */
#include <string.h>			/* String library functions	    */
#include <ctype.h>			/* Standard ctype.h defs	    */
#include <stdint.h>			/* Standard integer types	    */


/********************************/
/*	Global Data		*/
/********************************/

Global Int32	platformID = 0;		/* Platform ID (0 = UNKNOWN)	    */
Global Int32	deploymentID = 0;	/* Deployment ID (0 = UNKNOWN)	    */
Global Int32	evtFreq = 50;		/* FIFO rate during event	    */
Global Int32	watchFreq = 10;		/* FIFO rate during watch cycle	    */
Global Int32	evtPressureSecs = 1;	/* Period (secs) to get press in event*/
Global Int32	sysRecSecs = 600;	/* Period (secs) to get sys parms in watch*/
Global Int32	accelRange = 16;	/* Accelerometer range (g)	    */
Global Int32	gyroRange = 2000;	/* Gyro range (deg/sec)		    */
Global Int32	filterBW = 42;		/* Digital filter bandwidth (Hz)    */
Global Int32	debugBaud = 9600;	/* Baud rate on debug port	    */
Global Int32	modemBaud = 9600;	/* Baud rate to acoustic modem	    */
Global Int32	usrTmout = 120;		/* Timeout on user I/F (secs)	    */
Global Int32	modemAddr = 1;		/* Address of acoustic modem	    */
Global Int32	modemPeriod = 3600;	/* Period (secs) between modem-on time*/
Global Int32	modemOffset = 0;	/* Offset from top of modemPeriod   */
Global Int32	modemOnTime = 300;	/* "On" time for modem		    */
Global Int32	modemWarmup = 15;	/* Warmup time for turning on ac. modem*/
Global Int32	modemWait = 5;		/* Wait time for turning off ac. modem*/
Global Int32	modemTxPwr = 6;		/* S6 setting for modem (TxPower)   */
Global Int32	deploymentModemPeriod = 600; /* Modem Period during deployment mode*/
Global Int32	deploymentModeSecs = 86400L; /* Time to stay in deployment mode*/
Global Int32	sensorWarmup = 2;	/* Warmup time for turning on system sensors*/
Global Int32	sensorMinSecs = 5;	/* If sysRecSecs < this, leave sensors on*/
Global Int32	maxEvent = 7200;	/* Maximum time for event (secs)    */
Global Flt32	noMotionThresh = 2.0f;	/* MPU no motion threshold	    */
Global Flt32	noMotionTime = 2.0f;	/* MPU no motion time		    */
Global Flt32	pressFullScale = 100.0f; /* Full scale for external pressure sensor*/
Global Int32	syslogToConsole = 0;	/* Echo syslog to console	    */
Global Int32	kermitTmout = 30;	/* Timeout (seconds) for kermit file xfer*/
Global MBool	initModem = TRUE;	/* Send init strings to modem at startup*/
Global MBool	initPicoClk = TRUE;	/* Initialize PicoDOS clock at startup*/
Global MBool	localEcho = TRUE;	/* Echo command line		     */
Global MBool	zmSetRemote = TRUE;	/* Use remote cmds to set SRegs for ZModem*/

/* Following are not available via user interface	*/
Global Int32	clkDriftPeriod = 60;	/* Write clock drift file every 60 secs*/
Global char	*start = "";		/* Start string			    */
Global char	*BEDSLogLevel = "ERROR"; /* Log level for BEDS logger	    */
Global char	*InvLogLevel = "ERROR"; /* Log level for Invense stack	    */

Global char	*memErrStr = "Out of Memory";
Global char	*varNotFound = "Variable not found";


/********************************/
/*	Module Local Data	*/
/********************************/

MLocal Var vartbl[] =
  { {"PlatformID", &platformID, VAR_LONG, NULL, 1, 35, "Platform ID"},
    {"DeploymentID", &deploymentID, VAR_LONG, NULL, 1, 99, "Deployment ID"},
    {"EvtFreq", &evtFreq, VAR_LONG, NULL, 10, 1000, "Sample rate during event (Hz)"},
    {"WatchFreq", &watchFreq, VAR_LONG, NULL, 1, 100, "Sample rate in watch cycle (Hz)"},
    {"EvtPressureSecs", &evtPressureSecs, VAR_LONG, NULL, 1, 600,
	"Sample rate (sec) for pressure sensor during event"},
    {"SysRecSecs", &sysRecSecs, VAR_LONG, NULL, 1, 3600,
     "Sample rate (secs) for getting system parameters during watch cycle"},
    {"AccelRange", &accelRange, VAR_LONG, checkAccelRange, 2, 16,
     "Accelerometer range in g's (2, 4, 8, or 16)"},
    {"GyroRange", &gyroRange, VAR_LONG, checkGyroRange, 250, 2000,
     "Gyro range in degrees/sec (250, 500, 1000, or 2000)"},
    {"DigitalFilterBW", &filterBW, VAR_LONG, checkBW, 5, 256,
     "Bandwidth of MPU digital filter, Hz (5, 10, 20, 42, 98, 188, or 256)"},
    {"debugBaud", &debugBaud, VAR_LONG, NULL, 300, 115200, "Baud rate to use on DEBUG port"},
    {"modemBaud", &modemBaud, VAR_LONG, NULL, 300, 115200, "Baud rate to acoustic modem"},
    {"Timeout", &usrTmout, VAR_LONG, NULL, 60, 3600, "Timeout on user I/F (secs)"},
    {"ModemPeriod", &modemPeriod, VAR_LONG, NULL, 300, 7200,
     "Period (secs) for checking acoustic Modem"},
    {"DeployModemPeriod", &deploymentModemPeriod, VAR_LONG, NULL, 120, 1800,
     "Modem Period during deployment mode"},
    {"DeployModeSecs", &deploymentModeSecs, VAR_LONG, NULL, 3600, 604800,
     "How long (seconds) to stay in deployment mode"},
    {"ModemOffset", &modemOffset, VAR_LONG, NULL, 0, 3600,
     "Offset (secs) from top of ModemPeriod"},
    {"ModemOnTime", &modemOnTime, VAR_LONG, NULL, 60, 3600,
     "Period we leave modem on (secs)"},
    {"ModemWarmup", &modemWarmup, VAR_LONG, NULL, 5, 600,
     "Warmup time for turning on acoustic modem, secs"},
    {"ModemWait", &modemWait, VAR_LONG, NULL, 1, 120,
     "Time before turning off acoustic modem, secs"},
    {"ModemAddr", &modemAddr, VAR_LONG, NULL, 1, 99,
     "Acoustic Modem address"},
    {"ModemPower", &modemTxPwr, VAR_LONG, NULL, 2, 8,
     "Acoustic Modem Transmit power (S6 setting)"},
    {"InitModem", &initModem, VAR_BOOL, NULL, 0, 0xffff,
     "Send init strings to acoustic modem at startup"},
    {"SensorWarmup", &sensorWarmup, VAR_LONG, NULL, 2, 60,
     "Warmup time for turning on system sensors, secs"},
    {"SensorMinSecs", &sensorMinSecs, VAR_LONG, NULL, 2, 60,
	"If SysRecSys < this, leave sensors on"},
    {"MaxEvent", &maxEvent, VAR_LONG, NULL, 1800, 43200,  "Maximum time for an event (secs)"},
    {"NoMotionThresh", &noMotionThresh, VAR_FLOAT, NULL, 1, 360, "No motion threshold, deg/sec"},
    {"NoMotionTime", &noMotionTime, VAR_FLOAT, NULL, 1, 60, "No motion time (secs)"},
    {"PressFullScale", &pressFullScale, VAR_FLOAT, NULL, 10., 1000.,
     "Full scale for pressure sensor (bar)"},
    {"Echo", &localEcho, VAR_BOOL, NULL, 0, 0xffff, "Echo user commands"},
    {"zmSetRemote", &zmSetRemote, VAR_BOOL, NULL, 0, 0xffff,
     "Use remote cmds to set SRegs for ZModem"},
    {"kermitTimeout", &kermitTmout, VAR_LONG, NULL, 10, 300, "Timeout (secs) for kermit protocol"},
    {"InitPicoClock", &initPicoClk, VAR_BOOL, NULL, 0, 0xffff,
	"Initialize PicoDOS clock at startup"},
    {"logToConsole", &syslogToConsole, VAR_LONG, NULL, 0, 3,
	"0=no echo, 1=echo to console, 2=echo to TPU"}
};


Global Nat32 numVarTbl = NumberOf(vartbl);


/************************************************************************/
/* Function    : findVar						*/
/* Purpose     : Find system variable					*/
/* Inputs      : Variable name						*/
/* Outputs     : Var ptr, or NULL if not found				*/
/************************************************************************/
Var *findVar(char *name)
{
    Nat16	i;
    Var		*varp;

    for (i = NumberOf(vartbl), varp = vartbl; i--; varp++)
	if (strcasecmp(name, varp->vname) == 0)
	    return(varp);

    return(NULL);

} /* findVar() */


/************************************************************************/
/* Function    : checkVar						*/
/* Purpose     : Check one variable within limits			*/
/* Inputs      : Var ptr                                                */
/* Outputs     : OK, or ERROR if out of range and adjusted              */
/************************************************************************/
MLocal Errno    checkVar(Var *varp)
{
    Int32       oldval;

    if (varp->vtype == VAR_LONG)                    /* Check only longs*/
    {
        oldval = *(Int32 *)(varp->vval);

        if (oldval < varp->valMin)
            *(Int32 *)(varp->vval) = varp->valMin;
        else if (oldval > varp->valMax)
            *(Int32 *)(varp->vval) = varp->valMax;

        if ((oldval < varp->valMin) || (oldval > varp->valMax))
        {
            sysLogPrintf("Variable %s changed from %ld to %ld",
                         varp->vname, oldval, *(Int32 *)(varp->vval));
            return(ERROR);
        }
    }

    return(OK);
}


/************************************************************************/
/* Function    : checkVars						*/
/* Purpose     : Check variables within limits				*/
/* Inputs      : None                                                   */
/* Outputs     : TRUE if OK, FALSE if out of range and adjusted         */
/************************************************************************/
void    checkVars(Void)
{
    Nat16	i;
    Var		*varp;

    for (i = NumberOf(vartbl), varp = vartbl; i--; varp++)\
        checkVar(varp);
}


/************************************************************************/
/* Function    : showVar						*/
/* Purpose     : Show one system variable				*/
/* Inputs      : Var ptr						*/
/* Outputs     : None							*/
/************************************************************************/
MLocal Void showVar(const Var *varp, MBool showHelp)
{
    switch(varp->vtype)
    {
      case VAR_LONG:
	  printf("%-16s = %8ld   ",  varp->vname, *(Int32 *)(varp->vval));
	    break;

      case VAR_FLOAT:
	  printf("%-16s = %8.2f   ", varp->vname, *(Flt32 *)(varp->vval));
	  break;

      case VAR_BOOL:
	  printf("%-16s = %8s   ", varp->vname, 
		 *(MBool *)(varp->vval) ? "TRUE" : "FALSE");
	  break;

      case VAR_STRING:
      case VAR_STRINGA:
	  printf("%-16s = %-10s ", varp->vname, *(char **)(varp->vval));
	  break;
    }

    if (showHelp && varp->vhelp)
	printf("%s\n", varp->vhelp);
    else
	printf("\n");

} /* showVar() */


/************************************************************************/
/* Function    : showVarMoref						*/
/* Purpose     : Function to pass to 'more' for showing variables	*/
/* Inputs      : Variable number					*/
/* Outputs     : TRUE if found, FALSE if not				*/
/************************************************************************/
MLocal MBool showVarMoref(int varNo)
{
    if (varNo < NumberOf(vartbl))
    {
	showVar(&vartbl[varNo], TRUE);
	return(TRUE);
    }

    return(FALSE);
}


/************************************************************************/
/* Function    : setVar							*/
/* Purpose     : Set system variable					*/
/* Inputs      : Variable name, Variable value in ASCII, TRUE to display*/
/* Outputs     : NULL if OK, else error string				*/
/************************************************************************/
char *setVar(char *name, char *value, MBool show)
{
    Var		*varp;
    char	*p, *rtn;
    Int32	vsave, lval;
    Nat16	len;
    MBool	ishex = FALSE;

    if ((varp = findVar(name)) == NULL)
	return(varNotFound);

    vsave = *(Int32 *)varp->vval;
    rtn = NULL;

    switch(varp->vtype)
    {
      case VAR_LONG:
	  p = value + strlen(value) - 1;

	  if ((strncmp(value, "0x", 2) == 0) || (strncmp(value, "0X", 2) == 0))
	      ishex = TRUE;
	  else if ((*p == 'h') || (*p == 'H'))
	  {
	      ishex = TRUE;
	      *p = '\0';
	  }

	  if (ishex)
	      sscanf(value, " %lx", &lval);
	  else
	      sscanf(value, " %ld", &lval);
          
          *(Int32 *)varp->vval = lval;

          if ((checkVar(varp) != OK) && show)
              rtn = "Variable out of range; adjusted";

	  break;

      case VAR_FLOAT:
	  sscanf(value, " %f", (Flt32 *)varp->vval);
	  break;

      case VAR_BOOL:
	  *(MBool *)(varp->vval) = (strncasecmp(value, "TRUE", 4) == 0);
	  break;

      case VAR_STRINGA:
	  /* String has been malloc'd.  Free it	*/
	  free(*(char **)(varp->vval));
	  /**** NOTE FALL_THROUGH!!!   *****/

      case VAR_STRING:
	  len = strlen(value) + 1;
	  if ((p = malloc(len)) == NULL)
	      return(memErrStr);
	  memcpy(p, value, len);		/* Copy string		*/
	  *(char **)(varp->vval) = p;		/* Save str ptr		*/
	  varp->vtype = VAR_STRINGA;		/* Mark as malloc'd	*/
	  break;

      default:
	  return("Bad variable name");
    }

    if (varp->vfunc != NULL)
	if ((rtn = (*varp->vfunc)(varp)) != NULL)
	{
	    *(Int32 *)varp->vval = vsave;
	    return(rtn);
	}

    if (show)
    {
	cprintf("\n");
	showVar(varp, FALSE);
    }

//    printf("setVar() returns %s\r\n", rtn ? rtn : "NULL");
    return(rtn);

} /* setVar() */


/************************************************************************/
/* Function    : varCmd							*/
/* Purpose     : User interface command for "VAR"			*/
/* Inputs      : CmdInfoPtr						*/
/* Outputs     : NULL if OK, else error string				*/
/************************************************************************/
char	*varCmd(CmdInfoPtr cip)
{
    Var		*varp;
    char	*p;
    DosSwitch	QMsw = { "/", '?', 0, 0 };		// help
    DosSwitch	nsw = { "/-", 'N', 0, "%ld", false };
    static char *noVar = "Variable not found";

    CmdExtractCIDosSwitches(cip, "?N", &QMsw, &nsw);

    // HELP SWITCH SELECTED (common format to many commands)
    if (QMsw.pos)
    {
	cprintf("\nUsage:\n\n"
		"%s <name> = <value>\n"
		"%s <name> <value>\n"
		"%s <name>\n"
		"%s [/N<n>]\n\n"
		"First two forms assign <value> to variable named <name>\n\n"
		"Next form displays value of <name> variable\n\n"
		"Last form displays all variables with their values\n"
		"\t[/N<n>] n lines at a time in 'more' format (default 10)\n\n",
		cip->argv[0].str, cip->argv[0].str, cip->argv[0].str, cip->argv[0].str);

	return(NULL);
    }
    
    cprintf("\n");

    if (cip->argc <= 1)
    {
	more((nsw.pos && nsw.hasv) ? nsw.lv : 10, showVarMoref, NULL);
	return(NULL);
    }
    else
    {
	if (cip->argc == 2)
	{
	    if ((p = strchr(cip->argv[1].str, '=')) != NULL)
	    {
		*p++ = '\0';
		return(setVar(cip->argv[1].str, p, TRUE));
	    }

	    if ((varp = findVar(cip->argv[1].str)) != NULL)
	    {
		showVar(varp, TRUE);
		return(NULL);
	    }
	}
	else if ((cip->argc > 3) && (cip->argv[2].str[0] == '='))
	    return(setVar(cip->argv[1].str, cip->argv[3].str, TRUE));
	else
	    return(setVar(cip->argv[1].str, cip->argv[2].str, TRUE));
    }

    return(varNotFound);

} /* VarCmd() */
