/****************************************************************************/
/* Copyright 1991 - 2016 MBARI												*/
/****************************************************************************/
/* Summary	: Commands to set user parameters for OASIS5 Mooring Controller */
/* Filename : parm.c														*/
/* Author	: Robert Herlien (rah)											*/
/* Project	: OASIS5 Mooring												*/
/* $Revision: 1.3 $															*/
/* Created	: 07/14/2016 rah												*/
/*																			*/
/* 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:													*/
/* 15aug2003 rah - Oasis3 version created from usrcmds.c					*/
/* 14jul2016 rah - Oasis5 version created from Oasis3 version				*/
/* $Log$
 */
/****************************************************************************/

#include <mbariTypes.h>					/* MBARI type definitions			*/
#include <mbariConst.h>					/* MBARI constants					*/
#include <oasis.h>						/* OASIS controller definitions		*/
#include <custom.h>						/* PWR_PERM definition				*/
#include <otask.h>						/* OASIS task dispatcher			*/
#include <malloc.h>						/* OASIS malloc routines			*/
#include <parm.h>						/* Parameter table definitions		*/
#include <parse.h>						/* Generic parser declarations		*/
#include <userif.h>						/* OASIS User Interface routines	*/
#include <utils.h>						/* OASIS Utility routines			*/
#include <dig_io.h>						/* OASIS digital I/O definitions	*/
#include <remote.h>						/* Remote I/O library				*/
#include <adc.h>						/* OASIS5 Analog definitions		*/
#include <debug.h>


/* Note that ADC is normalized to 16 bits, or 65536 counts*/
#define LOW_VOLT_LIMIT  27300   /* 24824 = 10 volts      */ 
                                /* 27307 = 11 volts      */ 
                                /* 31030 = 12.5 volts    */ 

/********************************/
/*		External Data			*/
/********************************/

#ifdef LICOR
Extern MBool			licorDebug;
#endif
#ifdef SERIAL_AD
Extern MBool			adDebug;
#endif
#ifdef OA_TELEM
Extern MBool			telemDebug;
#endif


/********************************/
/*		Forward Declarations	*/
/********************************/

MLocal void blinkyFunc(MBool *parmp);
MLocal void wdtFunc(MBool *parmp);


/********************************/
/*		Global Data				*/
/********************************/

/***** Traditional Parameters *****/
Global Nat32	pwr_perm = 0;			/* Permanently powered instruments	*/

Global Parm_t	con_dsc = 4;			/* Disconnect character for CONNECT */
Global Parm_t	con_tmout = 180;		/* Timeout during CONNECT			*/
Global Parm_t	brkchr = 2;				/* Character that sends break		*/
#ifdef TNC
Global Parm_t	tncchr = 3;				/* TNC command character			*/
#endif
Global Parm_t	radio_tmout = DFLT_RADIO_TMOUT; /* Timeout for radio reset	*/
Global Parm_t	sync_tmout = DFLT_SYNC_TMOUT;	/* Timeout for drvSync		*/
Global Parm_t	xoff_tmout = DFLT_XOFF_TMOUT;	/* Timeout for xoff			*/
Global Parm_t	adcStartupMs = DFLT_ADC_STARTUP_MS;
Global Parm_t	gmtOffset = LOCAL_OFFSET;		/* Local offset from GMT	*/
Global Parm_t	logStatus = LOG_STATUS;			/* Log on power up/down		*/
Global Parm_t	gpsInitAlt = GPS_ALT;	/* Initial GPS altitude				*/
Global Parm_t	send_ascii = 0;			/* 0 = uuencode, 1 = ASCII, 2 = binary*/
Global Nat32	scriptDebug = 0;
Global Nat32	modbusDebug = 2;
Global Nat32	lowVoltLimit = LOW_VOLT_LIMIT; /* System low volt reset */
Global Parm_t	pwrFailCount = 3;
Global Parm_t	cfgSaves = 5;			/* Number of OASISnnn.CFG files to save*/
Global Parm_t	wakeOnConsoleChar = 0;
Global Parm_t	wdtSecs = O5_WATCHDOG_SECS;

#ifdef TNC
/***** String Parameters, from old stringtbl *****/
Global char		*tnc_name = TNC_NAME;	/* TNC node name					*/
#endif

#ifdef YMODEM
Extern Parm_t	ymodemFileNum;			/* Number part of file name			*/
#endif

/***** Parameters added to take place of old #ifdefs in custom.h *****/
Global MBool	syncAtMidnight = FALSE;
Global MBool	deploymentMode = FALSE;
Global MBool	diagnosticMode = FALSE;
Global MBool	blinky = BLINKY_STATE;
Global MBool	wdtOn = TRUE;
Global MBool	allowDrvrConflict = FALSE;
Global char		*ctdCmd = "TS";
Global char		*ctdSync = NULL;

Global Nat32	sampleArray[8];


/********************************/
/*		Module Local Data		*/
/********************************/
const ParamArray parmArrayTbl[] =
{	
	{"sampleArray", sampleArray, 8, 0, pNullFunc, 
		"example\n"}
};

const Param parmtbl[] =
{	{"DISCHR", &con_dsc, PARM_HEX, 0, pNullFunc, "DISCONNECT char"},
	{"BRKCHR", &brkchr, PARM_HEX, 0, pNullFunc, "BREAK char"},
	{"con_tmout", &con_tmout, PARM_DEC, 0, pNullFunc,
		 "CONNECT Timeout (secs)"},
	{"radio_tmout", &radio_tmout, PARM_DEC, 0, pNullFunc,
	 	"Hours of radio silence before reset"},
	{"PwrPerm", &pwr_perm, PARM_HEX, PARM_IMMUTABLE, pNullFunc,
	 	"Devices permanently powered"},
	{"ASCII", &send_ascii, PARM_DEC, 0, pNullFunc, 
		 "0 = uuencode, 1 = ASCII, 2 = binary"},
	{"xoff_tmout", &xoff_tmout, PARM_DEC, 0, pNullFunc, 
		 "Timeout in seconds for XOFF"},
	{"sync_tmout", &sync_tmout, PARM_DEC, 0, pNullFunc,
		 "Seconds to wait for drvr sync"},
	{"Blinky", &blinky, PARM_BOOL, 0, &blinkyFunc,
		 "TRUE to blink chamber LEDs"},
	{"gmtOffset", &gmtOffset, PARM_DEC, 0, pNullFunc,
		 "Offset in hours from GMT"},
	{"modbusDebug", &modbusDebug, PARM_DEC, 0, pNullFunc,
		 "Debug Level for ModBus"},
	{"scriptDebug", &scriptDebug, PARM_DEC, 0, pNullFunc,
		 "Debug Level for scripting"},
	{"adcStartup", &adcStartupMs, PARM_DEC, 0, pNullFunc,
	     "Time (ms) to allow A/D to warm up"},
	{"lowVoltLimit", &lowVoltLimit, PARM_DEC, 0, pNullFunc,
		 "Controller low voltage reset limit"},
	{"powerFailCount", &pwrFailCount, PARM_DEC, 0, pNullFunc,
		 "Ticks to look for power fail on IRQ2"},
	{"cfgSaves", &cfgSaves, PARM_DEC, 0, pNullFunc,
		 "Number of .CFG files to save"},
	{"allowDrvrConflict", &allowDrvrConflict, PARM_BOOL, 0, pNullFunc, NULL},
	{"wakeOnConsoleChar", &wakeOnConsoleChar, PARM_HEX,
	 	PARM_INVISIBLE | PARM_IMMUTABLE, pNullFunc, NULL},
	{"WDT", &wdtOn, PARM_BOOL, 0, &wdtFunc, NULL},
	{"WDTRATE", &wdtSecs, PARM_DEC, 0, pNullFunc, NULL},
#ifdef LICOR
	{"licorDebug", &licorDebug, PARM_BOOL, 0, pNullFunc, NULL},
#endif
#ifdef OA_TELEM
	{"telemDebug", &telemDebug, PARM_BOOL, 0, pNullFunc, NULL},
#endif
#ifdef SERIAL_AD
	{"analogDebug", &adDebug, PARM_BOOL, 0, pNullFunc, NULL},
#endif
#ifdef YMODEM
	{"fileNum", &ymodemFileNum, 0, 0, pNullFunc, ""},
#endif
#ifdef DEBUG_TASK
	{"maxUsedStack", &maxUsedStack, 0, 0, pNullFunc, ""},
#endif

#ifdef TNC
	{"TNCCHR", &tncchr, PARM_HEX, 0, pNullFunc, "TNC command char"},
	{"TNC", &tnc_name, PARM_STRING, 0, pNullFunc, "Name of TNC"},
#endif

/***** Parameters added to take place of old #ifdefs in custom.h *****/
	{"logStatus", &logStatus, PARM_HEX, PARM_INVISIBLE, pNullFunc, NULL},
	{"syncAtMidnight", &syncAtMidnight, PARM_BOOL, PARM_INVISIBLE,
	 	pNullFunc, NULL},
	{"deploymentMode", &deploymentMode, PARM_BOOL, PARM_INVISIBLE,
	 	pNullFunc, NULL},
	{"diagnosticMode", &diagnosticMode, PARM_BOOL, PARM_INVISIBLE,
	 	pNullFunc, NULL},
	{"ctdCmd", &ctdCmd, PARM_STRING, 0, pNullFunc, NULL},
	{"ctdSync", &ctdSync, PARM_STRING, 0, pNullFunc, NULL}
};

Global Nat32 numParmTbl = NumberOf(parmtbl);
Global Nat32 numParmArrayTbl = NumberOf(parmArrayTbl);


/************************************************************************/
/* Function	   : pNullFunc												*/
/* Purpose	   : Null Function called after most parm calls				*/
/* Inputs	   : None													*/
/* Outputs	   : OK														*/
/************************************************************************/
void pNullFunc(void)
{

}/* pNullFunc() */


/************************************************************************/
/* Function	   : params													*/
/* Purpose	   : Show or set user parameters							*/
/* Inputs	   : Parm name, Parm value in ASCII							*/
/* Outputs	   : OK if found & parm set, ERROR if parm not found		*/
/************************************************************************/
Int16 setParm(char *name, char *value)
{
	Reg Nat16			i, len;
	Reg const Param		*parmp;
	Reg char			*p;

	for (i = NumberOf(parmtbl), parmp = parmtbl; i--; parmp++)
	{
		if (cmp_ulc(name, parmp->pt_name) != NULL)
		{
			if (parmp->pt_flags & PARM_IMMUTABLE)
				return(OK);
			
			switch(parmp->pt_type)
			{
			  case PARM_HEX:
				  sscanf(value, " %lx", (Parm_t *)parmp->pt_parm);
				  break;
				  
			  case PARM_BOOL:
				  if ((value[0] == '1') || cmp_ulc(value, "TRUE") || cmp_ulc(value, "ON"))
					  *(MBool *)(parmp->pt_parm) = TRUE;
				  else
					  *(MBool *)(parmp->pt_parm) = FALSE;
				  break;

			  case PARM_STRING:
				  len = strlen(value) + 1;
				  if ((p = malloc(len)) == NULL)
					  return( ERROR );
				  memcpy(p, value, len);
				  *(char **)(parmp->pt_parm) = p;
				  break;

			  default:
				  /* Look for decimal number, but use hex for 0x<num> or <num>H */
				  strtok(value, "		;.");
				  sscanNum(value, (Parm_t *)parmp->pt_parm);
				  break;
			}

			if (parmp->pt_func != NULL)
				(*parmp->pt_func)(parmp->pt_parm);

			return(OK);
		}
	}

	return(ERROR);

} /* setParm() */


/************************************************************************/
/* Function	   : getParmArraySize										*/
/* Purpose	   : get the number of elements in the parameter array		*/
/* Inputs	   : Parm array name										*/
/* Outputs	   : number of elements or zero if array does not exist		*/
/************************************************************************/
Int16 getParmArraySize(char* name)
{
	Reg Nat16			i;
	Reg const ParamArray* parmp;

	for (i = NumberOf(parmArrayTbl), parmp = parmArrayTbl; i--; parmp++)
		if (cmp_ulc(name, parmp->pt_name) != NULL)
			return parmp->pt_size;

	return(0);
}


/************************************************************************/
/* Function	   : setParmArray											*/
/* Purpose	   : Set an element of a parameter array					*/
/* Inputs	   : Parm name, Parm value in ASCII, element index			*/
/* Outputs	   : OK if found & parm elemet set, ERROR if not			*/
/************************************************************************/
Int16 setParmArray(char* name, char* value, Nat16 element)
{
	Reg Nat16			i;
	Reg const ParamArray* parmp;
	Parm_t*				val_ptr;

	for (i = NumberOf(parmArrayTbl), parmp = parmArrayTbl; i--; parmp++)
	{
		if (cmp_ulc(name, parmp->pt_name) != NULL)
		{
			/* make sure the element is in range */
			if ( element > (parmp->pt_size - 1) )
				return ERROR;

			/* Look for decimal number, but use hex for 0x<num> or <num>H */
			val_ptr = (Parm_t*)(parmp->pt_array) + element;
			sscanNum(value, val_ptr);
		  
			/* execute function if it exists */
			if (parmp->pt_func != NULL)
				(*parmp->pt_func)();
		  
			return OK;
		}
	}

	return(ERROR);

} /* setParmArray() */


/************************************************************************/
/* Function	   : showParms												*/
/* Purpose	   : Show user parameters									*/
/* Inputs	   : Parm Mask, Parm name, Boolean TRUE to show help string */
/* Outputs	   : None													*/
/************************************************************************/
MLocal void showParms(Nat16 pmask, char *name, MBool showHelp)
{
	Reg Nat16			i;
	Reg const Param		*parmp;

	for (i = NumberOf(parmtbl), parmp = parmtbl; i--; parmp++)
		if (((parmp->pt_flags & PARM_INVISIBLE) == 0) || (pmask & 1))
			if (((pmask & 1) == 0) || (cmp_ulc(name, parmp->pt_name) != NULL))
			{
				switch(parmp->pt_type)
				{
				  case PARM_HEX:
					  xprintf("%-14s = %8lx Hex	   ", 
							  parmp->pt_name, *(Parm_t *)(parmp->pt_parm));
					  break;

				  case PARM_BOOL:
					  xprintf("%-14s = %8s Boolean  ", parmp->pt_name, 
							  *(MBool *)(parmp->pt_parm) ? "TRUE" : "FALSE");
					  break;

				  case PARM_STRING:
					  xprintf("%-14s = %-17s ", 
							  parmp->pt_name, *(char **)(parmp->pt_parm));
					  break;

				  default:
					  xprintf("%-14s = %8ld Decimal  ", 
							  parmp->pt_name, *(Parm_t *)(parmp->pt_parm));
					  break;
				}

				if (showHelp && parmp->pt_help)
					xputs(parmp->pt_help);
				newline();
			}

} /* showParms() */

/************************************************************************/
/* Function	   : showParmArrays											*/
/* Purpose	   : Show user parameter arrays								*/
/* Inputs	   : Parm name												*/
/* Outputs	   : None													*/
/************************************************************************/
MLocal void showParmArrays(Nat16 pmask, char* name)
{
	Reg Nat16			  i;
	Reg const ParamArray* parmp;
	Parm_t*				  val_ptr;

	/* show a list of parameters */
	if ( pmask == 0 )
	{
		for (i = NumberOf(parmArrayTbl), parmp = parmArrayTbl; i--; parmp++)
			xprintf("%s \t %s\n", parmp->pt_name, parmp->pt_help);

		return;
	}

	/* show values of a specific array */
	for (i = NumberOf(parmArrayTbl), parmp = parmArrayTbl; i--; parmp++)
	{
		if ( cmp_ulc(name, parmp->pt_name) != NULL )
		{
			val_ptr = (Parm_t*)(parmp->pt_array);

			for (i = 0; i < parmp->pt_size; ++i)
				xprintf("%s[%d] = %ld\n", parmp->pt_name, i, val_ptr[i]);

			return;
		}
	}

	xprintf("Array \"%s\" not found.\n", name);
	return;

} /* showParmArrays() */

/************************************************************************/
/* Function	   : params													*/
/* Purpose	   : Show or set user parameters							*/
/* Inputs	   : Parm Mask, Parm name, Parm value in ASCII				*/
/* Outputs	   : None													*/
/************************************************************************/
Int16 params( Nat16 pmask, char *name, char *value )
{
	if ((pmask & 3) == 3)
	{
		if (setParm(name, value) == OK)
			showParms(1, name, FALSE);
		else
			xprintf("Parameter \"%s\" not found\n", name);
	}
	else
		showParms(pmask, name, TRUE);

  return(OK);

} /* params() */


/************************************************************************/
/* Function	   : param_arrays											*/
/* Purpose	   : Show user parameter arrays								*/
/* Inputs	   : Parm Mask, Parm name, Parm value in ASCII				*/
/* Outputs	   : None													*/
/************************************************************************/
Int16 param_arrays(Nat16 pmask, char *name, char *value)
{
	showParmArrays(pmask, name);

	return OK;

} /* param_arrays() */


/************************************************************************/
/* Function    : blinkyFunc												*/
/* Purpose     : Function called when blinky is changed					*/
/* Inputs      : None													*/
/* Outputs     : None													*/
/************************************************************************/
MLocal void blinkyFunc(MBool *parmp)
{
	if (!*parmp)
		digBlinkyOff();

} /* blinkyFunc() */


/************************************************************************/
/* Function    : wdtFunc												*/
/* Purpose     : Function called when wdtOn is changed					*/
/* Inputs      : None													*/
/* Outputs     : None													*/
/************************************************************************/
MLocal void wdtFunc(MBool *parmp)
{
	if (*parmp)
		wdtEnable();
	else
		wdtDisable();

} /* wdtFunc() */
