/****************************************************************************/
/* Copyright 1991 - 2016 MBARI												*/
/****************************************************************************/
/* Summary	: User Interface Commands for OASIS Mooring Controller			*/
/* Filename : usrcmd1.c														*/
/* Author	: Robert Herlien (rah)											*/
/* Project	: OASIS3 Mooring												*/
/* $Revision: 1.1 $															*/
/* Created	: 07/19/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:													*/
/* 04feb2003 rah - OASIS3 version created from OASIS usrcmd1.c				*/
/* 19jul2016 rah - OASIS5 version created from OASIS3 usrcmd1.c				*/
/* $Log$
 */
/****************************************************************************/

#include <mbariTypes.h>					/* MBARI type definitions			*/
#include <mbariConst.h>					/* MBARI constants					*/
#include <oasis.h>						/* OASIS controller definitions		*/
#include <serial.h>						/* OASIS Serial I/O definitions		*/
#include <otask.h>						/* OASIS task dispatcher			*/
#include <malloc.h>						/* OASIS malloc routines			*/
#include <log.h>						/* OASIS data logging definitions	*/
#include <usrcmds.h>					/* User Command function definitions*/
#include <config.h>						/* OASIS Config file parser defns	*/
#include <drvr.h>						/* OASIS driver functions			*/
#include <timer.h>						/* OASIS clock routines				*/
#include <utils.h>						/* OASIS Utility routines			*/
#include <file.h>						/* OASIS file I/O					*/
#include <fatFs/ff.h>					/* FatFs file system defns			*/
#include <debug.h>

#include <FreeRTOS.h>					/* FreeRTOS definitions			*/
#include <task.h>						/* FreeRTOS/include task.h		*/

#include <istdio.h>						/* Standard I/O						*/
#include <stdio.h>						/* Standard I/O						*/
#include <string.h>						/* String library functions			*/
#include <ctype.h>						/* Character library functions		*/

#include <ymodem.h>
#include <time.h>


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

Extern LstHead			drv_list;		/* Driver list header				*/
Extern Parm_t			send_ascii;		/* 0 = uuencode, 1 = ASCII, 2 = binary*/
Extern Parm_t			sync_tmout;		/* Timeout for drvSync		        */
Extern Nat32			pwr_perm;		/* Devices to leave powered on		*/
Extern Parm_t			wakeOnConsoleChar;


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

MLocal LogRecNum		nextUsrLog = 0;

typedef struct
{
	const char	*prompt;
	Nat32		offset;
	Nat32		radix;
} ParmDesc;				


const ParmDesc			drvprompts[] =
{ {"Interval (secs)", OffsetOf(Driver, drv_parms[INTERVAL]), 10},
  {"Serial Port",	  OffsetOf(Driver, drv_parms[SER_PORT]), 10},
  {"Serial Baud Rate",OffsetOf(Driver, drv_parms[SER_BAUD]), 10},
  {"Serial Setup",	  OffsetOf(Driver, drv_parms[SER_SETUP]), 16},
  {"Driver Flags",	  OffsetOf(Driver, drv_parms[FLAGS]), 16},
  {"Sample type",	  OffsetOf(Driver, drv_parms[SAMPLE_TYPE]), 10},
  {"Hours off mask",  OffsetOf(Driver, drv_parms[TOD_MASK]), 16},
  {"Timeout (secs)",  OffsetOf(Driver, drv_parms[TIMEOUT]), 10},
  {"Extra parm 0",	  OffsetOf(Driver, drv_parms[PARM0]), 10},
  {"Extra parm 1",	  OffsetOf(Driver, drv_parms[PARM1]), 10},
  {"Extra parm 2",	  OffsetOf(Driver, drv_parms[PARM2]), 10},
  {"Extra parm 3",	  OffsetOf(Driver, drv_parms[PARM3]), 10},
  {"Extra parm 4",	  OffsetOf(Driver, drv_parms[PARM4]), 10},
  {"Extra parm 5",	  OffsetOf(Driver, drv_parms[PARM5]), 10},
  {"Extra parm 6",	  OffsetOf(Driver, drv_parms[PARM6]), 10},
  {"Extra parm 7",	  OffsetOf(Driver, drv_parms[PARM7]), 10},
  {"Run Flags",		  OffsetOf(Driver, drv_runflags), 16},
  {"Counter",		  OffsetOf(Driver, drv_cnt), 10}
};

#define NUM_DRVPARMS	(sizeof(drvprompts)/sizeof(ParmDesc))

#define YDATA_FILE	"ydata.tmp"
#define YLOGS_FILE	"ylogs.tmp"


/************************************************************************/
/* Function	   : putascii												*/
/* Purpose	   : Send data to user in ASCII								*/
/* Inputs	   : Log Record ptr, length of DATA portion to print,		*/
/*				 Boolean to print header								*/
/* Outputs	   : None													*/
/************************************************************************/
MLocal void putascii(LogRec *logp, Int32 len, MBool putHdr)
{
	Int32	curlen;
	Byte	*p;
	Nat32	i;
	time_t	sampTime;
	MBool	isAllAscii = TRUE;

	if (putHdr)
	{
		xprintf("%-16s %6u %04d ", 
				logp->log_hdr.log_instrumentName,
				swapLong(logp->log_hdr.log_rcd), swapWord(logp->log_hdr.log_len));

		sampTime = swapLong(logp->log_hdr.log_time);
		clkPrintTime(&sampTime);
		xprintf("\n  ");
	}

//	if (logp->log_hdr.log_type != LOG_ASCII)
		for (i = 0, p = logp->log_data; i < len; i++, p++)
			if (!isprint(*p) && !isspace(*p))
				isAllAscii = FALSE;

	p = logp->log_data;

	if (isAllAscii)
	{
		p[len] = '\0';
		xputs(p);
	}
	else while (len > 0)
	{
		curlen = (len > 32) ? 32 : len;
		len -= curlen;
		while ( curlen-- )
			xprintf("%02x", *p++ );
		dispatch();
	}

} /* putascii() */


/************************************************************************/
/* Function	   : uuout													*/
/* Purpose	   : Encode one byte into uuencode format					*/
/* Inputs	   : Byte to encode											*/
/* Outputs	   : None													*/
/************************************************************************/
MLocal void uuout(Int16 c)
{
	xputc( (c) ? (((c) & 0x3f) + ' ') : '`' );

}/* uuout() */


/************************************************************************/
/* Function	   : encode													*/
/* Purpose	   : Encode bytes into uuencode format						*/
/* Inputs	   : Ptr to data, length to encode							*/
/* Outputs	   : None													*/
/************************************************************************/
MLocal void encode(Byte *dp, Int16 len)
{
	Byte	*p;

	uuout( len );

	for (p = dp; p < dp + len; p += 3)
	{
		uuout( *p >> 2 );
		uuout( ((*p << 4) & 0x30) | ((p[1] >> 4) & 0x0f) );
		uuout( ((p[1] << 2) & 0x3c) | ((p[2] >> 6) & 0x03) );
		uuout( p[2] & 0x3f );
	}
	xputc('\n');
	dispatch();

} /* encode() */


/************************************************************************/
/* Function	   : putuu													*/
/* Purpose	   : Send data in uuencode fmt								*/
/* Inputs	   : Log Record ptr											*/
/* Outputs	   : None													*/
/************************************************************************/
MLocal void putuu(Byte *dp, Int32 len)
{
	Int32 curlen;
	Byte	*p;

	for ( p = dp; len > 0; len -= curlen, p += curlen )
	{
		curlen = (len > 45) ? 45 : len;
		encode( p, curlen );
	}

} /* putuu() */


/************************************************************************/
/* Function	   : startdata												*/
/* Purpose	   : Send data header, get semaphore						*/
/* Inputs	   : Log Block number										*/
/* Outputs	   : None													*/
/************************************************************************/
MLocal void startdata(LogBlk blk, TelemType telem)
{
	if (telem != TELEM_ASCII)
		xprintf( "begin 644 oasis.%u\n", blk );

} /* startdata() */


/************************************************************************/
/* Function	   : stopdata												*/
/* Purpose	   : Send data trailer, release semaphore					*/
/* Inputs	   : None													*/
/* Outputs	   : None													*/
/************************************************************************/
MLocal void stopdata(TelemType telem)
{
	if (telem == TELEM_UUENCODE)
		xputs("`\n");
	if (telem != TELEM_ASCII)
		xputs( "end\n" );

} /* stopdata() */


/************************************************************************/
/* Function	   : checkUsrInterrupt										*/
/* Purpose	   : Pause after sending log, and check for user interrupt	*/
/* Inputs	   : None													*/
/* Outputs	   : TRUE if user hit key to interupt, else FALSE			*/
/* Comment	   : This routine pauses after sending a log record to allow*/
/*				 the TNC to catch its breath.  I suspect the TNC has its*/
/*				 XON/XOFF screwed up if it gets too busy				*/
/************************************************************************/
MLocal MBool checkUsrInterrupt(void)
{
	do
	{									/* Let the output buffer drain	*/
		if (xgetc_tmout(0) != ERROR)	/* If user hit key, return TRUE */
			return(TRUE);
		task_delay(TICKS_PER_SEC/20);	/* Else, wait 50 ms to give TNC */

	} while (xTxStatus() > 0);			/* time to catch its breath		 */

	return( FALSE );

} /* checkUsrInterrupt() */


/************************************************************************/
/* Function	   : putOneLogRec											*/
/* Purpose	   : Get one data log record, output it to user				*/
/* Inputs	   : LogPtr, ptr to buffer for LogRec						*/
/* Outputs	   : OK, ERROR, or USR_INTERRUPT							*/
/* Comments	   : Loops to get all of the data record, because logGetRec */
/*				 returns at most LOG_CHUNK bytes of data at a time		*/
/************************************************************************/
MLocal Int32 putOneLogRec(LogPtr lp, LogRec *rcdp, TelemType telem)
{
	Int32	rtn;
	Nat16	offset, len;

	/* get chunks until an error or 0 (no more bytes read) */
	for (offset=0; (rtn = logGetRec(lp, rcdp, offset)) >= 0; offset += rtn)
	{
		len = swapWord(rcdp->log_hdr.log_len);

		// if rtn is really big, this could go on and on...
#ifdef DEBUG_LOG
		dprintf("putOneLogRec:rcd=%d type=%d len=%d offset=%d rtn = %d\r\n",
				swapWord(rcdp->log_hdr.log_rcd),rcdp->log_hdr.log_type,
				len, offset, rtn);
#endif
		if (rtn == 0)
			return(OK);					/* No more data in this record	*/

		switch(telem)
		{
		  case TELEM_ASCII:
			  /* Leave off trailing Sync	*/
			  putascii(rcdp, ((rtn >= (len-offset)) ? rtn-1 : rtn), (offset==0));
			  break;

		  case TELEM_BINARY:
			  if (offset == 0)
				  xputn((char *)rcdp, rtn + sizeof(LogRecHdr));
			  else
				  xputn(rcdp->log_data, rtn);
			  break;

		  default:
			  if (offset == 0)
				  putuu((Byte *)(&rcdp) + 1, rtn + sizeof(LogRecHdr) - 1);
			  else
				  putuu(rcdp->log_data, rtn);
			  break;
		}

		if (checkUsrInterrupt())		/* If user hits key, stop		*/
			return(USR_INTERRUPT);

		if (rtn >= (len-offset))		/* Got whole thing?				*/
		{
			if (telem == TELEM_ASCII)
				newline();
			return(OK);
		}
	}

	return(ERROR);						/* If got here, error			*/

} /* putOneLogRec() */


/************************************************************************/
/* Function	   : writeOneLogRec											*/
/* Purpose	   : Get one data log record, and append it to file			*/
/* Inputs	   : LogBlk number, Log record number, FIL ptr				*/
/* Outputs	   : OK, ERROR, or USR_INTERRUPT							*/
/* Comments	   : Loops to get all of the data record, because logGetRec */
/*				 returns at most LOG_CHUNK bytes of data at a time		*/
/************************************************************************/
MLocal Int32 writeOneLogRec(LogPtr lp, LogRec *rcdp, FIL *fp)
{
	Int32	rtn;
	UINT	cnt;
	FRESULT	fres;
	Nat16	offset;

	/* check for null file pointer */
	if ( fp == NULL )
		return ERROR;
	
	/* get chunks until an error or 0 (no more bytes read) */
	for (offset=0; (rtn = logGetRec(lp, rcdp, offset)) >= 0; offset += rtn)
	{
		if (rtn == 0)
			return OK;					/* No more data in this record	*/
		
		if (offset == 0)
			fres = f_write(fp, rcdp, rtn + sizeof(LogRecHdr), &cnt);
		else
			fres = f_write(fp, rcdp, rtn, &cnt);
		
		/* check for bad file writes */
		if ((fres != FR_OK) || (cnt < rtn))
			return(ERROR);
		
		if (rtn >= (swapWord(rcdp->log_hdr.log_len)-offset))
		  return(OK);					/* If got whole thing, return	*/
	}

	return (ERROR);						/* If got here, error				*/

} /* writeOneLogRec() */


/************************************************************************/
/* Function	   : getLogRecs												*/
/* Purpose	   : Get specified data log records							*/
/* Inputs	   : LogPtr for first record, number recs					*/
/* Outputs	   : OK, ERROR, NO_DATA or USR_INTERRUPT					*/
/************************************************************************/
MLocal CmdRtn getLogRecs(LogPtr logptr, Parm_t nmbr)
{
	LogRec		*rcdp;
	CmdRtn		rtn;

#ifdef DEBUG_LOG
	dprintf("getLogRecs: logptr=%u nmbr=%u\r\n", logptr, nmbr);
#endif  
	if((nmbr < 0) || (nmbr>RECS_PER_BLK))
		return(ERROR);

	if (isLogged(logptr) != LOG_OK)
	{
		xprintf("Can't find log record %u\n", logptr);
		return(NO_DATA);
	}

	if ((rcdp = (LogRec *)pmalloc(sizeof(LogRec))) == NULL)
		return(ERROR);					/* Get log buffer, rtn if none	*/

	startdata(BlkNum(logptr), send_ascii);	/* Send uuencode header		*/

	while (nmbr-- && ((rtn = putOneLogRec(logptr, rcdp, send_ascii)) == OK))
		logptr++;

	free(rcdp);							/* Free the buffer				*/
	stopdata(send_ascii);				/* Send uuencode trailer		*/

	if (rtn == NO_DATA)
		return(OK);

	return(rtn);

} /* getLogRecs() */


/************************************************************************/
/* Function	   : getYLogRecs											*/
/* Purpose	   : Get specified data log records							*/
/* Inputs	   : LogPtr for first record, number recs					*/
/* Outputs	   : OK, ERROR, NO_DATA or USR_INTERRUPT					*/
/* Comment     : See caution in file.c about _FS_TINY					*/
/************************************************************************/
MLocal CmdRtn getYLogRecs(LogPtr logptr, Parm_t nmbr)
{
	LogRec		*rcdp;
	Int32		rtn;
	FIL			file;
  
	if( (nmbr < 0) || (nmbr>RECS_PER_BLK) )
		return(ERROR);

	if (isLogged(logptr) != LOG_OK)
	{
		xprintf("Can't find log record %u\n", logptr);
		return(NO_DATA);
	}

	if ((rcdp = (LogRec *)pmalloc(sizeof(LogRec))) == NULL)
		return(ERROR);					/* Get log buffer, rtn if none	*/

	/* open temp file */
	if (f_open(&file, YLOGS_FILE, (BYTE)(FA_WRITE | FA_CREATE_ALWAYS)) != FR_OK)
	{
		xprintf("Can't open temp file %s\n", YLOGS_FILE);
		return(ERROR);
	}
  
	while (nmbr-- && ((rtn = writeOneLogRec(logptr, rcdp, &file)) == OK))
		logptr++;

	free(rcdp);							/* Free the buffer				*/

	/* close the temp file */
	f_close(&file);

	/* send the logs back via Y-Modem */
	if ( ymdmSend(1, YLOGS_FILE) != OK )
	{
		xprintf("Y-Modem xfer of file %s failed.\n", YLOGS_FILE);
		return(ERROR);
	}
  
	/* delete tempfile */	   
	if (f_unlink(YLOGS_FILE) != FR_OK)
	{
		xprintf("Could not delete temp file %s\n", YLOGS_FILE);
		return(ERROR);
	}

	if (rtn == NO_DATA)
		return(OK);

	return(rtn);

} /* getYLogRecs() */


/************************************************************************/
/* Function	   : getlog													*/
/* Purpose	   : Get specified data log records							*/
/* Inputs	   : Parm Mask, LogBlk number, Log record number, number recs*/
/* Outputs	   : OK														*/
/************************************************************************/
CmdRtn getlog(ParmMask_t pmask, Parm_t start, Parm_t nmbr)
{
	if ((pmask & 1) != 1)
		return(ERROR);

	if( (nmbr < 0) || (nmbr>RECS_PER_BLK) )
		return(ERROR);
		
	if (getLogRecs(start, ((pmask & 2) == 0) ? 1 : nmbr) == ERROR)
		return(ERROR);

	return(OK);

} /* getlog() */

/************************************************************************/
/* Function	   : getylog												*/
/* Purpose	   : Get specified data log records and send back via Y-Modem */
/* Inputs	   : Parm Mask, LogBlk number, Log record number, number recs*/
/* Outputs	   : OK														*/
/************************************************************************/
CmdRtn getylog(ParmMask_t pmask, Parm_t start, Parm_t nmbr)
{
	if ((pmask & 1) != 1)
		return(ERROR);

	if( (nmbr < 0) || (nmbr>RECS_PER_BLK) )
		return(ERROR);
		
	if (getYLogRecs(start, ((pmask & 2) == 0) ? 1 : nmbr) == ERROR)
		return(ERROR);

	return(OK);

} /* getylog() */


/************************************************************************/
/* Function	   : dump													*/
/* Purpose	   : Dump entire log block(s)								*/
/* Inputs	   : Parm Mask, block number, number of blocks				*/
/* Outputs	   : OK														*/
/************************************************************************/
CmdRtn dump(ParmMask_t pmask, Parm_t blk, Parm_t nblks)
{
	LogPtr		logPtr;
	Reg CmdRtn	rtn;

	if ((pmask & 1) == 0)
		return( ERROR );

	logPtr = RecNum(blk);

	while (BlkNum(logPtr) < (blk + ((pmask & 2) ? nblks : 1)))
	{
		if ((rtn = getLogRecs(logPtr, RECS_PER_BLK)) != OK)
			return(OK);
		
		logPtr += RECS_PER_BLK;
	}
	
	return(OK);

} /* dump() */


/************************************************************************/
/* Function	   : checkLogBlk											*/
/* Purpose	   : Read one log block, report how many records we can read*/
/* Inputs	   : Block number											*/
/* Outputs	   : Number records successfully read						*/
/************************************************************************/
MLocal Nat32 checkLogBlk(LogBlk blknum)
{
	LogPtr		logptr;
	LogRec		*logp;

	if ((logp = (LogRec *)pmalloc(sizeof(LogRec))) == NULL)
		return(ERROR);					/* Get log buffer, rtn if none	*/

	logptr = RecNum(blknum);

	for (logptr = RecNum(blknum); BlkNum(logptr) < blknum+1; logptr++)
		if (logGetRec(logptr, logp, 0) < 0)
			break;

	free(logp);							/* Free the buffer				*/
	return(logptr - RecNum(blknum));

} /* checkLogBlk() */


/************************************************************************/
/* Function	   : checkLog												*/
/* Purpose	   : Read entire log block(s), make sure they're OK			*/
/* Inputs	   : Parm Mask, block number, number of blocks				*/
/* Outputs	   : OK														*/
/************************************************************************/
CmdRtn checkLog(ParmMask_t pmask, Parm_t blk, Parm_t nblks)
{
	LogBlk		curblk;

	if ( (pmask & 1) == 0 )
		return( ERROR );

	for ( curblk = blk; (curblk < (blk + ((pmask & 2) ? nblks : 1))); curblk++ )
		xprintf("Log Block %u returns %u records successfully read\n",
				curblk, checkLogBlk(curblk));
	
	return( OK );

} /* checkLog() */


/************************************************************************/
/* Function	   : getdata												*/
/* Purpose	   : Get data since last data dump							*/
/* Inputs	   : Parm mask, max number of records						*/
/* Outputs	   : OK														*/
/************************************************************************/
CmdRtn getdata(ParmMask_t pmask, Parm_t maxRecs)
{
	LogRec		*rcdp;					/* LogRec buffer				*/
	LogRecNum	usrLog;					/* Copy of nextUsrLog			*/
	LogPtr		freeLogP;				/* Ptr to how much data collected*/
	CmdRtn		rtn;
	Int32		nRecs;

	if ((rcdp = (LogRec *)pmalloc(sizeof(LogRec))) == NULL)
		return(ERROR);					/* Get log buffer, rtn if none	*/

	if ((pmask & 1) == 0)
		maxRecs = MAX_RECS;

	/* Make copy of nextUsrLog, so multiple access doesn't bomb			*/
	usrLog = nextUsrLog;
	logGetCurrentLogPtrs(&freeLogP, NULL);

	for (rtn = OK, nRecs = 0; (nRecs < maxRecs) && (usrLog < freeLogP) && (rtn == OK); )
	{
		if ((nRecs == 0) || (RecOffset(usrLog) == 0)) /* If new block,		*/
			startdata(BlkNum(usrLog), send_ascii);	  /* Send uuencode header*/
		
		rtn = putOneLogRec(usrLog, rcdp, send_ascii);
		usrLog++;
		nRecs++;

		if ((RecOffset(usrLog) == 0) || (rtn != OK))
			stopdata(send_ascii);		/* Send uuencode trailer		*/
	}

	free(rcdp);							/* Free buffer					*/
	nextUsrLog = usrLog;				/* Copy back to nextUsrLog		*/

	if (usrLog >= freeLogP)
		xprintf("done\n");

	return( OK );

} /* getdata() */


/************************************************************************/
/* Function	   : getydata												*/
/* Purpose	   : Get data since last data dump							*/
/* Inputs	   : Parm mask, max number of records						*/
/* Outputs	   : OK														*/
/* Comment     : See caution in file.c about _FS_TINY					*/
/************************************************************************/
CmdRtn getydata(ParmMask_t pmask, Parm_t maxRecs)
{
	LogRec		*rcdp;					/* LogRec buffer				*/
	LogPtr		usrLog;					/* Copy of nextUsrLog			*/
	LogPtr		freeLogP;				/* Ptr to how much data collected*/
	Int32		rtn;
	Int32		nRecs;
	FRESULT		fres;
	FIL			file;

	if ((rcdp = (LogRec *)pmalloc(sizeof(LogRec))) == NULL)
		return(ERROR);					/* Get log buffer, rtn if none	*/

	if ((pmask & 1) == 0)
		maxRecs = MAX_RECS;

	/* Make copy of nextUsrLog, so multiple access doesn't bomb			*/
	usrLog = nextUsrLog;
	logGetCurrentLogPtrs(&freeLogP, NULL);

	/* open temp file */
	if (f_open(&file, YDATA_FILE, (BYTE)(FA_WRITE | FA_CREATE_ALWAYS)) != FR_OK)
	{
		xprintf("Can't open temp file %s\n", YDATA_FILE);
		return(ERROR);
	}
  
	for (rtn = OK, nRecs = 0; (nRecs < maxRecs) && (usrLog < freeLogP) && (rtn == OK); )
	{
		rtn = writeOneLogRec(usrLog, rcdp, &file);
		usrLog++;
		nRecs++;
	}

	/* close the temp file */
	f_close(&file);
  
	free(rcdp);							/* Free buffer					*/
	nextUsrLog = usrLog;				/* Copy back to nextUsrLog		*/
  
/* What should the policy be here?	delete on success or every time ?*/

	/* send the logs back via Y-Modem */
	if (ymdmSend(1, YDATA_FILE) == OK)
	{
		if (f_unlink(YDATA_FILE) != FR_OK)
		{
			xprintf("Could not delete temp file %s\n", YDATA_FILE);
			return(ERROR);
		}
	}
	else
	{
		xprintf("Y-Modem xfer of file %s failed.\n", YDATA_FILE);
		return(ERROR);
	}

	return(OK);

} /* getydata() */


/************************************************************************/
/* Function	   : getlognmbrs											*/
/* Purpose	   : Get Log Record numbers									*/
/* Inputs	   : None													*/
/* Outputs	   : OK														*/
/************************************************************************/
CmdRtn getlognmbrs(void)
{
	LogPtr		newest, oldest;

	logGetCurrentLogPtrs(&newest, &oldest);
	if (newest == 0)
		xputs("No records logged\n");
	else
		xprintf("Records logged: %u to %u\n", oldest, newest-1);
	xprintf("Next download is record %u\n", nextUsrLog);
	return(OK);

} /* getlognmbrs() */


/************************************************************************/
/* Function	   : initUsrLog												*/
/* Purpose	   : Set nextUsrLog Block Number							*/
/*				 Called once at init to set block that we're starting with*/
/* Inputs	   : None													*/
/* Outputs	   : OK														*/
/************************************************************************/
void initUsrLog(LogRecNum firstRcd)
{
  nextUsrLog = firstRcd;

} /* initUsrLog() */


/************************************************************************/
/* Function	   : showDrivers											*/
/* Purpose	   : Tell user what drivers are in the system				*/
/* Inputs	   : None													*/
/* Outputs	   : None													*/
/************************************************************************/
MLocal void showDrivers(void)
{
	Reg Driver		*dp;
	time_t			tod;

	tod = clkTime();
	lockList(&drv_list);
	for ( dp = (Driver *)(drv_list.lst_head); dp != DRV_NULL; 
		  dp = dp->drv_next )
	{
		xprintf("%-20s ", dp->drv_name );
		if (dp->drv_td != NULLTID)
			printTaskState(dp->drv_td);

		else if (dp->drv_parms[INTERVAL] == 0)
			xputs("Off\n");
		else
			xprintf("%u secs\n", dp->drv_wakeup - tod);
	}

	unlockList(&drv_list);

} /* showDrivers() */


/************************************************************************/
/* Function	   : drvUsrFunc												*/
/* Purpose	   : Perform driver user function							*/
/* Inputs	   : Drvr name, Time til func, parameter, function flag		*/
/* Outputs	   : OK														*/
/************************************************************************/
MLocal CmdRtn drvUsrFunc(char *name, Parm_t functime, Parm_t usrparm, DWord funcflag)
{
	Reg Driver	*dp;

	if ( (dp = drvr_find(name)) == DRV_NULL )
		return( ERROR );
	
	dp->drv_usrparm = usrparm;
	dp->drv_wakeup = clkTime() + functime;
	dp->drv_runflags |= funcflag;

	return( OK );

} /* drvUsrFunc() */


/************************************************************************/
/* Function	   : aux													*/
/* Purpose	   : Perform Sensor auxiliary function						*/
/* Inputs	   : Parm Mask, Driver name, Time til do function			*/
/* Outputs	   : OK														*/
/************************************************************************/
CmdRtn aux(ParmMask_t pmask, char *name, Parm_t auxtime, Parm_t parm)
{
	return( drvUsrFunc(name, auxtime, parm, DO_AUX) );

} /* aux() */


/************************************************************************/
/* Function	   : init													*/
/* Purpose	   : Initialize driver										*/
/* Inputs	   : Parm Mask, Driver name, Time til init, parameter		*/
/* Outputs	   : OK														*/
/************************************************************************/
CmdRtn init(ParmMask_t pmask, char *name, Parm_t inittime, Parm_t parm)
{
	return( drvUsrFunc(name, inittime, parm, DO_INIT) );

} /* init() */


/************************************************************************/
/* Function	   : sync													*/
/* Purpose	   : Sync sensor											*/
/* Inputs	   : Parm Mask, Driver to sync (dflt ADCP), Time til do sync*/
/* Outputs	   : OK														*/
/************************************************************************/
CmdRtn sync(ParmMask_t pmask, char *name, Parm_t synctime, Parm_t parm)
{
  return( drvUsrFunc(name, synctime, parm, DO_SYNC) );

} /* sync() */


/************************************************************************/
/* Function	   : getprompt												*/
/* Purpose	   : Get prompt for driver parameter						*/
/* Inputs	   : Driver ptr, Parameter number							*/
/* Outputs	   : None													*/
/************************************************************************/
MLocal const char *getprompt(Driver *dp, Nat16 parm)
{
	if ((parm >= PARM0) && (parm < DRV_PARMS) &&
		(dp->drv_parmNames) && (dp->drv_parmNames[parm - PARM0]))
		return(dp->drv_parmNames[parm - PARM0]);
	else
		return(drvprompts[parm].prompt);

} /* getprompt() */


/************************************************************************/
/* Function	   : promptparm												*/
/* Purpose	   : Display parameter										*/
/* Inputs	   : Driver ptr, Parameter number							*/
/* Outputs	   : None													*/
/************************************************************************/
MLocal void promptparm(Driver *dp, Nat16 parm)
{
	const ParmDesc	*pp;
	
	pp = &drvprompts[parm];

	xprintf("%-20s ", getprompt(dp, parm));

	if ( pp->radix == 16 )
		xprintf( "hex  %x	", *(Nat32 *)((Byte *)dp + pp->offset) );
	else
		xprintf( "dec  %d	", *(Nat32 *)((Byte *)dp + pp->offset) );

} /* promptparm() */


/************************************************************************/
/* Function	   : getparm												*/
/* Purpose	   : Get parameter value from user							*/
/* Inputs	   : Ptr to place for answer, radix							*/
/* Outputs	   : TRUE if got valid number								*/
/* Comments	   : Uses user command buffer, size is USR_BUFSIZE (256)	*/
/************************************************************************/
MBool getparm(Nat32 *result, Nat32 radix, Byte *buffer)
{
	if ( xgets_tmout((char *)buffer, USR_BUFSIZE, 30) > 0 )
	{
		xflush_ser(TICKS_PER_SEC/5);
		return( (sscanf(buffer, (radix == 10) ? " %d" : " %x", result) > 0) ?
				TRUE : FALSE);
	}

	return( FALSE );

} /* getparm() */


/************************************************************************/
/* Function	   : showDrvParms											*/
/* Purpose	   : Utility routine for drvparms(), drvadd()				*/
/* Inputs	   : Driver pointer											*/
/* Outputs	   : None													*/
/************************************************************************/
MLocal void showDrvParms(Driver *dp)
{
	Nat16		i;

	xprintf("Num	Parameter		   Radix Value\n");

	for ( i = 0; i < NUM_DRVPARMS; i++ )
	{
		xprintf(" %2d ", i);
		promptparm(dp, i);
		xputc( '\n' );
	}

} /* showDrvParms() */


/************************************************************************/
/* Function	   : drvgetparms											*/
/* Purpose	   : Utility routine for drvparms(), drvadd()				*/
/* Inputs	   : Driver pointer, user I/F buffer						*/
/* Outputs	   : None													*/
/************************************************************************/
MLocal void drvgetparms(Driver *dp, Byte *buffer)
{
	Nat32		radix;
	Nat32		parmIndex, *parmp;
	Nat32		oldSer, newSer, oldPwrPerm, newPwrPerm;

	showDrvParms(dp);

	oldSer = dp->drv_parms[SER_PORT];
	if ((dp->drv_parms[FLAGS] & PWRPERM) && (oldSer != NO_SERIAL))
		oldPwrPerm = (1L << oldSer);
	else
		oldPwrPerm = 0;

	while(TRUE)
	{
		xprintf("Select parm number (0 - %u)  ", (NUM_DRVPARMS - 1));
		if (!getparm(&parmIndex, 10, buffer))
			break;

		if (parmIndex >= NUM_DRVPARMS)
		{
			showDrvParms(dp);
			continue;
		}

		promptparm(dp, (Nat16)parmIndex);
		parmp = (Nat32 *)((Byte *)dp + drvprompts[parmIndex].offset);
		radix = drvprompts[parmIndex].radix;
		if (getparm(parmp, radix, buffer))
		{
			xprintf("%s set to ", getprompt(dp, parmIndex));
			xprintf((radix == 10) ? "%d\n" : "%x hex\n", *parmp);
		}
	}

	/* If ser port or flags changed, manipulate pwr_perm	*/
	newSer = dp->drv_parms[SER_PORT];
	if ((dp->drv_parms[FLAGS] & PWRPERM) && (newSer != NO_SERIAL))
		newPwrPerm = (1L << newSer);
	else
		newPwrPerm = 0;

	if (newPwrPerm != oldPwrPerm)
	{
		if (oldPwrPerm)							/* If it was pwrpermed	*/
		{										/* turn off pwrperm bit	*/
			pwr_perm &= ~oldPwrPerm;
			if (dp->drv_td == NULL)				/* If driver not running*/
				pwrBitOff(oldSer);				/*  turn off power now	*/
		}

		if (newPwrPerm)							/* If we're now pwrpermed*/
		{
			pwr_perm |= newPwrPerm;				/* turn on pwrperm bit	*/
			pwrBitOn(dp->drv_parms[SER_PORT]);	/* and turn on power	*/
		}
	}

	if (dp->drv_parms[FLAGS] & CONSOLE_MODE)
		wakeOnConsoleChar |= newPwrPerm;		/* Set wakeOnConsoleChar */
	else										/*  bit appropriately	 */
		wakeOnConsoleChar &= ~newPwrPerm;

} /* drvgetparms() */


/************************************************************************/
/* Function	   : drvparms												*/
/* Purpose	   : Set Driver parameters									*/
/* Inputs	   : Parm mask, ptr to driver name							*/
/* Outputs	   : OK														*/
/************************************************************************/
CmdRtn drvparms(ParmMask_t pmask, char *name, Parm_t unused1,
				Parm_t unused2, Parm_t unused3, Byte *buffer)
{
	Reg Driver	*dp;
	
	if ( (pmask & 1) && ((dp = drvr_find(name)) != DRV_NULL) )
		drvgetparms( dp, buffer );
	else
		showDrivers();

	return( OK );

} /* drvparms() */


/************************************************************************/
/* Function	   : drvadd													*/
/* Purpose	   : Add a driver to driver list							*/
/* Inputs	   : Parm mask, driver name									*/
/* Outputs	   : OK or ERROR											*/
/************************************************************************/
CmdRtn drvadd(ParmMask_t pmask, char *drvType, char *drvName,
			  Parm_t unused1, Parm_t unused2, Byte *buffer)
{
	Driver			*dp;
	const DrvDesc	*drvDescP;
	DrvDesc			drvdesc;

	if ( ((pmask & 1) != 1) || ((drvDescP = findDriver(drvType)) == NULL) )
		return(ERROR);

	memcpy(&drvdesc, drvDescP, sizeof(DrvDesc));

	if (pmask & 2)						/* Insert name					*/
		drvdesc.dd_name = drvName;

	xprintf("Default sample interval is %u.\n", drvdesc.dd_parms[INTERVAL]);
	xputs("We set it to 0 to keep driver from running until parms are set.\n");
	xputs("Set the interval LAST, after other parms are correct\n\n");

	drvdesc.dd_parms[INTERVAL] = 0;		/* Keep it from sampling til done*/

	if ( (dp = drvr_create(&drvdesc)) != DRV_NULL )
		drvgetparms(dp, buffer);

	checkDrvrConflicts();
	return(OK);
	
} /* drvadd() */


/************************************************************************/
/* Function    : drvdel													*/
/* Purpose     : Delete a driver from driver list						*/
/* Inputs      : Parm mask, ptr to driver name							*/
/* Outputs     : OK														*/
/************************************************************************/
CmdRtn drvdel(ParmMask_t pmask, char *name)
{
	Driver	*dp;

	if (((pmask & 1) == 0) || ((dp = drvr_find(name)) == DRV_NULL))
		showDrivers();
	else
	{
		if (dp->drv_td != NULLTID)			/* If running, kill the task*/
			taskKill(dp->drv_td);

		list_get(&drv_list, (Node *)dp);	/* Remove driver from list	*/
		free((char *)dp);
	}

	return( OK );

} /* drvdel() */


/************************************************************************/
/* Function	   : usrKill												*/
/* Purpose	   : Kill a task											*/
/* Inputs	   : Parm mask, ptr to driver name							*/
/* Outputs	   : OK														*/
/************************************************************************/
CmdRtn	usrKill(ParmMask_t pmask, char *name)
{
	TaskHandle_t	td;
	Driver			*dp;

	if ((td = xTaskGetHandle(name)) == NULL)
		xputs("Task not found\n");
	else
	{
		xprintf("Killing task 0x%x\n", td);
		taskKill(td);
		if ((dp = drvr_find(name)) != DRV_NULL)
			dp->drv_td = NULLTID;
	}

	return(OK);

} /* usrKill() */


/************************************************************************/
/* Function	   : checkStack												*/
/* Purpose	   : Check the stack usage for currently running tasks		*/
/* Inputs	   : None													*/
/* Outputs	   : OK														*/
/************************************************************************/
CmdRtn checkStack(void)
{
	Nat32			nTasks, i;
	TaskStatus_t	*stsp;

	nTasks = uxTaskGetNumberOfTasks();

	if ((stsp = pmalloc(nTasks * sizeof(TaskStatus_t))) == NULL)
		return(ERROR);

	nTasks = uxTaskGetSystemState(stsp, nTasks, NULL);

	for (i = 0; i < nTasks; i++)
	{
		xprintf("%-12s addr %x  taskNum %u  prio %u  ", 
				stsp[i].pcTaskName, stsp[i].xHandle,
				stsp[i].xTaskNumber, stsp[i].uxCurrentPriority);
		printTaskState(stsp[i].xHandle);

		xprintf("             Stack Base %x  Stack High Water %u\n",
				stsp[i].pxStackBase, (Nat32)stsp[i].usStackHighWaterMark);
	}

	free(stsp);
	return(OK);

} /* checkStack() */


/************************************************************************/
/* Function    : sample													*/
/* Purpose     : test function to sample drivers						*/
/* Inputs      : Parm mask, ptr to driver name							*/
/* Outputs     : OK														*/
/************************************************************************/
CmdRtn sample(ParmMask_t pmask, char *name)
{
    Driver *dp;
    int timeout = 0;

    if ( ((pmask & 1) == 0) || ((dp = drvr_find(name)) == DRV_NULL) )
    {
        printf("Can't find the '%s' driver\r\r\n", name);
        showDrivers();
    }
    else
    {
        drvSync(name);						        /* sync the driver */

        /* delay for a second to allow optrode task to be dispatched */
        task_delay(TICKS_PER_SEC);

        /* wait for the sample to complete */
        while ( (dp->drv_td != NULLTID) && (++timeout < sync_tmout) )
            task_delay(TICKS_PER_SEC);

		return(lsample(pmask, name));
    }
    
    return(OK);
}


/************************************************************************/
/* Function	   : lsample												*/
/* Purpose	   : show last sample of a driver							*/
/* Inputs	   : Parm mask, ptr to driver name							*/
/* Outputs	   : OK														*/
/************************************************************************/
CmdRtn lsample(ParmMask_t pmask, char *name)
{
	Driver	*dp;
	LogRec	*rcdp;

	if ( ((pmask & 1) == 0) || ((dp = drvr_find(name)) == DRV_NULL) )
	{
		xprintf("Can't find the '%s' driver.  Drivers in system:\r\n\n", name);
		showDrivers();
	}
	else if (dp->drv_lastSample)
		return((*dp->drv_lastSample)(dp));
	else
	{
		if ((rcdp = (LogRec *)pmalloc(sizeof(LogRec))) == NULL)
			return(ERROR);					/* Get log buffer, rtn if none	*/

		if (dp->drv_lastlog == -1)
			xprintf("No samples logged\n");
		else
			putOneLogRec((LogPtr)dp->drv_lastlog, rcdp, TELEM_ASCII);

		free(rcdp);
	}

	return(OK);
}
