/* Copyright 2015 MBARI							*/
/************************************************************************/
/* Summary  : Debug menu accessible from debug serial port on MV Conv Board*/
/* Filename : mvdebug.c							*/
/* Author   : Robert Herlien (rah)					*/
/* Project  : xFOCE							*/
/* Revision: 1.0							*/
/* Created  : 04/10/2015						*/
/*									    */
/* 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:						*/
/* 10apr2015 rah - created						*/
/************************************************************************/

#include "mvdebug.h"
#include "serial.h"
#include "dig_io.h"
#include "adc.h"
#include "misc.h"
#include "gf.h"

#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <ctype.h>

extern void menuHelp(int port);
extern int getLine(int port, char *line);


typedef struct			/****************************************/
{				/* Struct to hold commands		*/
    char  *name;		/* Command name				*/
    int   (*func)();		/* Function to execute			*/
    int   nparms;		/* Number of parms (0 or 1 only for now)*/
    UINT  parmMin;		/* Max value of parm (0 to parmMax)	*/
    UINT  parmMax;		/* Max value of parm (0 to parmMax)	*/
} CommandStruct;		/****************************************/

typedef struct			/****************************************/
{				/* Analog channel descriptors		*/
    char  *name;		/* Channel name				*/
    char  *units;		/* Name of engineering units		*/
    double mult;		/* Multiplier				*/
    double offset;		/* Offset				*/
} AnalogDesc;			/****************************************/


#define NumberOf(arr)	(sizeof(arr) / sizeof(arr[0]))

/* Return codes from function execution		*/
#define CONTINUE 0
#define EXIT	(-2)

#ifndef OK
#define OK 0
#endif
#ifndef ERROR
#define ERROR (-1)
#endif


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

int	printDebugMenu(int port);
CommandStruct *getCommand(int port, UINT *parmp);
int	vicorOn(int port, UINT vicor);
int	vicorOff(int port, UINT vicor);
int	pwrRelayOn(int port, UINT relay);
int	pwrRelayOff(int port, UINT relay);
int	fanOn(int port, UINT fan);
int	fanOff(int port, UINT fan);
void	printAnalogChan(int port, UINT chan, UINT16 cnt);
int	analogRead(int port, UINT analogPort);
int	analogAllRead(int port);
int	analogParms(int port);
int	analogNumAvg(int port, UINT numAvg);
int	analogAvgMs(int port, UINT avgMs);
int	statusVector(int port);
int	gfChan(int port, UINT chan);
int	gfCtl(int port, UINT ctrl);
int	waterAlarms(int port);
int	exitProg(void);
int	testCmd(int port, UINT parm);


/********************************/
/*	Module Local Data	*/
/********************************/

static char	inputBuf[256];
static char	msgBuf[256];

static int	dbgSerPort = CFG_SERPORT;
static int	debugLevel = 0;

static CommandStruct cmdTbl[] = {
    {"von", vicorOn, 1, 1, 2},
    {"voff", vicorOff, 1, 1, 2},
    {"on", pwrRelayOn, 1, 1, 2},
    {"off", pwrRelayOff, 1, 1, 2},
    {"fon", fanOn, 1, 1, 2},
    {"foff", fanOff, 1, 1, 2},
    {"aa", analogAllRead, 0, 0, 0},
    {"ar", analogRead, 1, 0, ADC_CHANS},
    {"an", analogNumAvg, 1, 1, 99},
    {"am", analogAvgMs, 1, 1, 99},
    {"ap", analogParms, 0, 0, 0},
    {"sv", statusVector, 0, 0, 0}, 
    {"gf", gfChan, 1, 0, 5}, 
    {"gc", gfCtl, 1, 0, 63},
    {"w", waterAlarms, 0, 0, 0}, 
    {"d", setDebugLevel, 1, 0, 9},
    {"h", printDebugMenu, 0, 0, 0},
    {"x", exitProg, 0, 0, 0},
    {"q", exitProg, 0, 0, 0},
    {"test", testCmd, 1, 1, 250}
};


static AnalogDesc analogDesc[] = {
    {"VICOR1_TEMP", "deg C",  33.33, 0.0},
    {"VICOR2_TEMP", "deg C",  33.33, 0.0},
    {"VICOR3_TEMP", "deg C",  33.33, 0.0},
    {"HEATSINK",   "deg C",   33.33, 0.0},
    {"48V_1_CURR", "amps",     4.17, 0.0},
    {"48V_1_VOLT", "volts",   20.00, 0.0},
    {"48V_2_CURR", "amps",     4.17, 0.0},
    {"48V_2_VOLT", "volts",   20.00, 0.0},
    {"24V_CURR",   "amps",     1.11, 0.0},
    {"24V_VOLT",   "volts",   10.00, 0.0},
    {"3.3V_CURR",  "amps",     1.11, 0.0},
    {"3.3V_VOLT",  "volts",    1.20, 0.0},
    {"AMB_HUM",    "%RH",     49.87, -37.7},
    {"AMB_TEMP",   "deg C",   37.50, -37.7},
    {"GND_FAULT",  "uamps",   667.0, -1000.0}
};

static char *pwrChans[] = {
    "FAN01 ", "FAN02 ", "VICOR01 ", "VICOR02 ", "FOCE01_RELAY ", "FOCE02_RELAY "
};

static char *gfChans[] = {
    "48V_01", "48V_01_RET", "48V_02", "48V_02_RET", "GF_TEST", "GF_TEST_RET"
};


/************************************************************************/
/* Function    : debugMenu						*/
/* Purpose     : Debug menu main entry point				*/
/* Inputs      : Serial port to use					*/
/* Outputs     : None							*/
/************************************************************************/
void debugMenu(int port)
{
    UINT		cmdParm;
    CommandStruct	*cmdp;

    dbgSerPort = port;
    printDebugMenu(port);

    while (TRUE)
    {
	cmdParm = 0;
	if ((cmdp = getCommand(port, &cmdParm)) != NULL)
	{
	    serPutString(port, "\r\n");
	    if ((*cmdp->func)(port, cmdParm) == EXIT)
	    {
		setDebugLevel(port, 0);
		menuHelp(port);
		return;
	    }
	}
    }

} /* debugMenu() */


/************************************************************************/
/* Function    : printDebugMenu						*/
/* Purpose     : Print the menu						*/
/* Inputs      : Serial port						*/
/* Outputs     : None							*/
/************************************************************************/
int printDebugMenu(int port)
{
    serPutString(port, "\r\nDEBUG MENU - Type a command:\r\n\n");
    serPutString(port, "h for this help message\r\n");
    serPutString(port, "x to go back to main menu\r\n\n");

    serPutString(port, "von[1-2] to turn on a vicor\r\n");
    serPutString(port, "voff[1-2] to turn off a vicor\r\n");
    serPutString(port, "on[1-2] to turn on a FOCE power relay\r\n");
    serPutString(port, "off[1-2] to turn off a FOCE power relay\r\n");
    serPutString(port, "fon[1-2] to turn on a fan\r\n");
    serPutString(port, "foff[1-2] to turn off a fan\r\n\n");

    serPutString(port, "ar[0-14] to read an analog port\r\n");
    serPutString(port, "aa to read all analog ports\r\n");
    serPutString(port, "ap to read analog parameters\r\n");
    serPutString(port, "an[1-99] to set analog numAvg (number samples to average)\r\n");
    serPutString(port, "am[1-99] to set analog avgMs (ms between samples)\r\n");
    serPutString(port, "sv to read system status vector (power bits on)\r\n");
    serPutString(port, "gf[0-5] to set ground fault channel\r\n");
    serPutString(port, "gc[0-63] to set GF control register (in decimal, sorry)\r\n");
    serPutString(port, "w to read water alarms\r\n\n");
    serPutString(port, "d[0-9] to set debug output level\r\n");

    return(OK);

} /* printDebugMenu() */


/********************************************************/
/*  Support routines for getCommand()			*/
/********************************************************/

/************************************************************************/
/* Function    : strncasecmp						*/
/* Purpose     : Compare strings, case insensitive			*/
/* Inputs      : 2 string ptrs, length to match				*/
/* Outputs     : An integer less than, equal to, or greater than zero,	*/
/*		 depending on if s1 < s2, s1 == s2, or s1 > s2		*/
/* Comments    : Tried to make this identical to std C strncasecmp(),	*/
/*		 which is missing from the Microchip C RTL		*/
/************************************************************************/
static int strncasecmp(const char *s1, const char *s2, int len)
{
  const	char	*p1 = s1;
  const	char	*p2 = s2;
  char		c1, c2;
  int		n = len;

  if (len <= 0)
    return(0);

  while ((n-- > 0) && ((c1 = toupper(*p1)) == (c2 = toupper(*p2))))
  {
    if (!c1)
      return(0);
    p1++;
    p2++;
  }

  return((unsigned)c1 - (unsigned)c2);

} /* strncasecmp() */


static int getDec(char *s, UINT *num)
{
    UINT  val;
    int	  rtn;

    rtn = val = 0;

    while (isdigit(*s))
    {
	val *= 10;
	val += (*s - '0');
	rtn = 1;
	s++;
    }

    if (rtn)
	*num = val;
    return(rtn);
}

static CommandStruct  *findCommand(UINT *parmp)
{
    int		i, nameSize, rtn;
    UINT	parm;
    CommandStruct *csp;

    for (i = 0, csp = cmdTbl; i < NumberOf(cmdTbl); i++, csp++)
    {
	nameSize = strlen(csp->name);

	if (strncasecmp(inputBuf, csp->name, nameSize) == 0)
	{
	    if (csp->nparms == 0)
		return(csp);

	    if (((rtn = getDec(&inputBuf[nameSize], &parm)) >= 1) && (parmp != NULL))
		*parmp = parm;
	    
	    return(csp);
	}
    }
    return(NULL);
}

static CommandStruct  *checkParm(int port, CommandStruct *csp, UINT *parmp)
{
    if (csp->nparms == 0)
	return(csp);
    if (parmp == NULL)
	return(NULL);
    if ((*parmp >= csp->parmMin) && (*parmp <= csp->parmMax))
	return(csp);

    sprintf(msgBuf, "\r\nBad parameter: %u\r\n", *parmp);
    serPutString(port, msgBuf);
    return(NULL);
}

static UINT decDigits(UINT n)
{
    UINT digits=1;

    while (n >= 10)
    {
	digits++;
	n /= 10;
    }

    return(digits);
}


/************************************************************************/
/* Function    : getCommand						*/
/* Purpose     : Get a command						*/
/* Inputs      : Serial port, ptr to place to put parm			*/
/* Outputs     : Ptr to command to execute				*/
/************************************************************************/
CommandStruct  *getCommand(int port, UINT *parmp)
{
    int		nchars, nameSize, digits;
    char	*p;
    CommandStruct *csp;

    serPutString(port, "Enter a command:  ");
    memset(inputBuf, 0, sizeof(inputBuf));
    nchars = 0;
    p = inputBuf;

    while( nchars < sizeof(inputBuf) - 1 )
	if (serGetByte(port, (unsigned char *)p))
	{
	    serPutByte(port, *p);

	    if ((*p == '\n') || (*p == '\r'))
	    {
		*p = '\0';
		if ((csp = findCommand(parmp)) == NULL)
		    return(NULL);
		return(checkParm(port, csp, parmp));
	    }

	    nchars++;
	    p++;
	    *p = '\0';

	    if ((csp = findCommand(parmp)) != NULL)
	    {
		if (csp->nparms == 0)
		    return(csp);

		nameSize = strlen(csp->name);
		digits = decDigits(csp->parmMax);

		if (nchars >= nameSize + digits)
		    return(checkParm(port, csp, parmp));
		
		if ((csp->parmMax < 20) && (nchars >= nameSize+1) && (*parmp >= 2))
		    return(checkParm(port, csp, parmp));
	    }
	}

    return(NULL);
}

/********************************************************/
/* Debug get/set/print routines for general use		*/
/********************************************************/

/************************************************************************/
/* Function    : dbgPrint						*/
/* Purpose     : Print formatted output to default serial port		*/
/* Inputs      : Format string, parameters				*/
/* Outputs     : Return value from snprintf				*/
/************************************************************************/
int	dbgPrint(const char *format, ...)
{
  va_list	ap;
  int		rtn;

  if (debugLevel <= 0)
      return(0);

  va_start(ap, format);
  rtn = vsnprintf(msgBuf, sizeof(msgBuf), format, ap);
  serPutString(dbgSerPort, msgBuf);
  va_end(ap);
  return(rtn);

} /* dbgPrint() */


int	setDebugLevel(int port, UINT level)
{
    debugLevel = level;
    return(OK);
}

UINT	getDebugLevel(void)
{
    return(debugLevel);
}


/********************************************************/
/* Functions to implement menu commands			*/
/********************************************************/

/************************************************************************/
/* Function    : exitProg						*/
/* Purpose     : Command function to exit the program			*/
/* Inputs      : None used						*/
/* Outputs     : EXIT return code					*/
/************************************************************************/
int	exitProg(void)
{
    return(EXIT);
}

/************************************************************************/
/* Function    : VicorOn						*/
/* Purpose     : Command function to turn on a Vicor converter		*/
/* Inputs      : Serial port, Vicor Number				*/
/* Outputs     : Return code						*/
/************************************************************************/
int     vicorOn(int port, UINT vicorPort)
{
    sprintf(msgBuf, "Turning on Vicor %u\r\n", vicorPort);
    serPutString(port, msgBuf);
    digSetVicor(vicorPort, 1);
    return(OK);
}


/************************************************************************/
/* Function    : VicorOff						*/
/* Purpose     : Command function to turn off a Vicor converter		*/
/* Inputs      : Serial port, Vicor Number				*/
/* Outputs     : Return code						*/
/************************************************************************/
int	vicorOff(int port, UINT vicorPort)
{
    sprintf(msgBuf, "Turning off Vicor %u\r\n", vicorPort);
    serPutString(port, msgBuf);
    digSetVicor(vicorPort, 0);
    return(OK);
}


/************************************************************************/
/* Function    : pwrRelayOn						*/
/* Purpose     : Command function to turn on a FOCE power relay		*/
/* Inputs      : Serial port, Relay Number				*/
/* Outputs     : Return code						*/
/************************************************************************/
int	pwrRelayOn(int port, UINT relay)
{
    sprintf(msgBuf, "Turning on FOCE relay %u\r\n", relay);
    serPutString(port, msgBuf);
    digSetRelay(relay, 1);
    return(OK);
}


/************************************************************************/
/* Function    : pwrRelayOff						*/
/* Purpose     : Command function to turn off a FOCE power relay	*/
/* Inputs      : Serial port, Relay Number				*/
/* Outputs     : Return code						*/
/************************************************************************/
int	pwrRelayOff(int port, UINT relay)
{
    sprintf(msgBuf, "Turning off FOCE relay %u\r\n", relay);
    serPutString(port, msgBuf);
    digSetRelay(relay, 0);
    return(OK);
}


/************************************************************************/
/* Function    : fanOn							*/
/* Purpose     : Command function to turn on a Fan			*/
/* Inputs      : Serial port, Fan Number				*/
/* Outputs     : Return code						*/
/************************************************************************/
int     fanOn(int port, UINT fan)
{
    sprintf(msgBuf, "Turning on Fan %u\r\n", fan);
    serPutString(port, msgBuf);
    digSetFan(fan, 1);
    return(OK);
}


/************************************************************************/
/* Function    : fanOff							*/
/* Purpose     : Command function to turn off a Fan			*/
/* Inputs      : Serial port, Fan Number				*/
/* Outputs     : Return code						*/
/************************************************************************/
int	fanOff(int port, UINT fan)
{
    sprintf(msgBuf, "Turning off Fan %u\r\n", fan);
    serPutString(port, msgBuf);
    digSetFan(fan, 0);
    return(OK);
}

/************************************************************************/
/* Function    : printAnalogChan					*/
/* Purpose     : Print one analog channel				*/
/* Inputs      : Serial port, analog channel number, A/D counts		*/
/* Outputs     : None							*/
/************************************************************************/
void	printAnalogChan(int port, UINT chan, UINT16 cnt)
{
    AnalogDesc	*adp = &analogDesc[chan];
    double	volt = ((double)cnt * 3.0 / 65536.0);

    if (chan == 13)
    {
	volt *= 1.665;
	sprintf(msgBuf, "Ch %2d  %-12s %5u cnts %6.3f ADC volts %6.3f %s\r\n",
		chan, adp->name, cnt, volt,
		-2.43*volt*volt*volt + 20.0*volt*volt - 74.19*volt + 123.32,
	        adp->units);
    }
    else
	sprintf(msgBuf, "Ch %2d  %-12s %5u cnts %6.3f ADC volts %6.3f %s\r\n",
		chan, adp->name, cnt, volt,
		volt * adp->mult + adp->offset, adp->units);

    serPutString(port, msgBuf);
}

/************************************************************************/
/* Function    : analogRead						*/
/* Purpose     : Command function to read analog channel		*/
/* Inputs      : Serial port, analog channel number			*/
/* Outputs     : Return code						*/
/************************************************************************/
int	analogRead(int port, UINT chan)
{
    UINT16	cnt;

    if (chan >= ADC_CHANS)
	serPutString(port, "Bad channel number\r\n");

    else
    {
	adcConvert(ADC_CONVERT_START + chan, 1);
	cnt = adcReadReg(ADC_CONVERT_START + chan);
	printAnalogChan(port, chan, cnt);
    }

    return(OK);
}

/************************************************************************/
/* Function    : analogAllRead						*/
/* Purpose     : Command function to read all 8 analog channels		*/
/* Inputs      : Serial port						*/
/* Outputs     : Return code						*/
/************************************************************************/
int	analogAllRead(int port)
{
    UINT	i;

    adcConvert(ADC_CONVERT_START, ADC_CHANS);

    for (i = 0; i < ADC_CHANS; i++)
	printAnalogChan(port, i, adcReadReg(ADC_CONVERT_START + i));
    
    return(OK);
}

/************************************************************************/
/* Function    : analogParms						*/
/* Purpose     : Command function to print analog parms			*/
/* Inputs      : Serial port						*/
/* Outputs     : Return code						*/
/************************************************************************/
int	analogParms(int port)
{
    UINT16	numAvg, avgMs;

    numAvg = adcReadReg(ADC_NUM_AVG);
    avgMs = adcReadReg(ADC_AVG_MS);

    sprintf(msgBuf, "numAvg = %u, avgMs = %u\r\n", numAvg, avgMs);
    serPutString(port, msgBuf);

    return(OK);
}

/************************************************************************/
/* Function    : writeOneAdcReg						*/
/* Purpose     : Interface function to writeAdcRegs()			*/
/* Inputs      : Register value, register address			*/
/* Outputs     : Return code						*/
/************************************************************************/
int writeOneAdcReg(USHORT val, USHORT addr)
{
    UCHAR	cval[2];

    cval[0] = (val >> 8) & 0xff;
    cval[1] = val & 0xff;
    return(adcWriteRegs(cval, addr, 1));
}

/************************************************************************/
/* Function    : analogNumAvg						*/
/* Purpose     : Command function to set numAvg				*/
/* Inputs      : MVConvHandle, parm					*/
/* Outputs     : OK							*/
/************************************************************************/
int	analogNumAvg(int port, UINT numAvg)
{
    writeOneAdcReg((USHORT)numAvg, ADC_NUM_AVG);

    sprintf(msgBuf, "Set numAvg to %u\r\n", numAvg);
    serPutString(port, msgBuf);

    return(OK);
}

/************************************************************************/
/* Function    : analogAvgMs						*/
/* Purpose     : Command function to set avgMs				*/
/* Inputs      : MVConvHandle, parm					*/
/* Outputs     : OK							*/
/************************************************************************/
int	analogAvgMs(int port, UINT avgMs)
{
    writeOneAdcReg((USHORT)avgMs, ADC_AVG_MS);

    sprintf(msgBuf, "Set avgMs to %u\r\n", avgMs);
    serPutString(port, msgBuf);

    return(OK);
}

/************************************************************************/
/* Function    : statusVector						*/
/* Purpose     : Command function to get system status vector		*/
/* Inputs      : Serial port						*/
/* Outputs     : OK							*/
/************************************************************************/
int	statusVector(int port)
{
    UINT	sysStat, i;

    sysStat = digGetSysStatus();
    
    sprintf(msgBuf, "Status Vector = 0x%x [ ", sysStat);
    serPutString(port, msgBuf);

    if (sysStat == 0)
	serPutString(port, "NONE ");
    else 
	for (i = 0; i < NumberOf(pwrChans); i++)
	    if (sysStat & (1<<i))
		serPutString(port, pwrChans[i]);

    serPutString(port, "]\r\n");
    return(OK);
}

/************************************************************************/
/* Function    : waterAlarms						*/
/* Purpose     : Command function to get water alarms			*/
/* Inputs      : MVConvHandle						*/
/* Outputs     : OK							*/
/************************************************************************/
int	waterAlarms(int port)
{
    UINT	alarms;

    alarms = miscRegRead(H2O_SENSE);
    
    sprintf(msgBuf, "Water Alarm Vector = 0x%x\r\n", alarms);
    serPutString(port, msgBuf);

    return(OK);
}

/************************************************************************/
/* Function    : gfChan							*/
/* Purpose     : Set GF Channel to monitor				*/
/* Inputs      : Serial port, GF chan					*/
/* Outputs     : OK							*/
/************************************************************************/
int	gfChan(int port, UINT chan)
{
    if (chan >= NumberOf(gfChans))
    {
	serPutString(port, "Bad GF Channel number\r\n");
	return(ERROR);
    }

    setGfChan(chan);

    sprintf(msgBuf, "GF Channel set to to %u (%s)\r\n", chan, gfChans[chan]);
    serPutString(port, msgBuf);

    return(OK);
}

/************************************************************************/
/* Function    : gfCtl							*/
/* Purpose     : Set GF Control Reg					*/
/* Inputs      : Serial port, control bits				*/
/* Outputs     : OK							*/
/************************************************************************/
int	gfCtl(int port, UINT ctrl)
{
    setGfCtrl(ctrl & 0x3f);
    sprintf(msgBuf, "GF Control reg set to 0x%x\r\n", ctrl & 0x3f);
    serPutString(port, msgBuf);
    return(OK);
}


/************************************************************************/
/* Function    : testCmd						*/
/* Purpose     : Command function to test parser			*/
/* Inputs      : MVConvHandle, parm					*/
/* Outputs     : OK							*/
/************************************************************************/
int	testCmd(int port, UINT parm)
{
    sprintf(msgBuf, "Test Command, parm = %u, digits = %u\r\n", parm, decDigits(parm));
    serPutString(port, msgBuf);
    return(OK);
}
