/****************************************************************************/
/* Copyright 1991 - 2003 MBARI												*/
/****************************************************************************/
/* Summary	: User Interface Commands for OASIS Mooring Controller			*/
/* Filename : usrcmd1.c														*/
/* Author	: Robert Herlien (rah)											*/
/* Project	: OASIS3 Mooring												*/
/* $Revision: 1.1 $															*/
/* Created	: 02/04/2003 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 - created from OASIS usrcmd1.c								*/
/* $Log: usrcmd1.c,v $
/* Revision 1.1	 2011/10/25 22:39:22  bobh
/* Initial oasis4 revision
/*
/* Revision 1.9	 2011/02/18 18:13:44  bobh
/* Added serialAtoD driver and misc changes for OA Moorings
/*
/* Revision 1.8	 2011/02/15 18:41:58  bobh
/* Changes to support 8 or 16 bit download protocols
/*
/* Revision 1.7	 2010/05/25 21:47:49  bobh
/* Mike's fix for drvparms
/*
/* Revision 1.6	 2010/05/06 22:25:46  bobh
/* Added user functions to manipulate remote I/O, changed function parser to parse and pass 4 parameters
/*
/* Revision 1.5	 2009/11/19 19:17:32  bobh
/* Changes to support con to remote ports and fixes to file I/O due to testing
/*
/* Revision 1.4	 2009/11/04 18:20:43  mrisi
/* added sample and lsample commands
/*
/* Revision 1.3	 2009/04/28 20:53:06  bobh
/* Fixes to ctdSim
/*
/* Revision 1.2	 2009/04/17 18:05:04  bobh
/* Changes leading up to CDR
/*
/* Revision 1.1	 2009/03/25 20:34:41  bobh
/* Initial checkin
/*
/* Revision 1.9	 2005/03/01 00:57:31  mrisi
/* added getydata() and getylog() funcs to send back mooring data using ymodem
/*
/* Revision 1.8	 2004/08/20 16:56:32  bobh
/* Add delay time to DP
/*
/* Revision 1.7	 2004/08/18 21:08:35  bobh
/* Diagnostic routines added, minor bug fixes, blinky added as parm
/*
/* Revision 1.6	 2004/02/16 19:05:26  headley
/* added "Pending" and "Delay" state indicators for the dp command.
/*
/* Revision 1.5	 2003/10/24 20:35:28  headley
/* added comment to mark possible 'bug' in initUsrLog
/*
/* Revision 1.4	 2003/10/03 23:50:34  headley
/* - added comments and debug code (commented out)
/* - changed nmbr from Nat16 to Parm_t (long int) in getLogRecs and getlog()
/* - changed NAT16_MAX to RECS_PER_BLK in dump
/*
/* Revision 1.3	 2003/09/03 17:48:16  bobh
/* Further mods for config file changes
/*
 */
/****************************************************************************/

#include <mbariTypes.h>					/* MBARI type definitions			*/
#include <mbariConst.h>					/* MBARI constants					*/
#include <oasis.h>						/* OASIS controller definitions		*/
#include <custom.h>						/* PWR_PERM definition				*/
#include <io.h>							/* OASIS I/O definitions			*/
#include <spi.h>						/* OASIS SPI I/O definitions		*/
#include <serial.h>						/* OASIS Serial I/O definitions		*/
#include <task.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 <clock.h>						/* OASIS clock routines				*/
#include <utils.h>						/* OASIS Utility routines			*/
#include <file.h>						/* OASIS file I/O					*/
#include <debug.h>

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

#include <ymodem.h>
#include <unistd.h>

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

Extern Nat16			serPort;		/* Dflt ser port, saved by dispatch */
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					*/


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

MLocal LogRecNum		nextUsrLog = 0;

typedef struct
{
	const char	*prompt;
	Nat16		offset;
	Nat16		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},
  {"Power ctrl",	  OffsetOf(Driver, drv_parms[PWR_CTRL]), 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},
  {"Flags",			  OffsetOf(Driver, drv_flags), 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;
  MBool isAllAscii = TRUE;

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

	clk_printtime(swapLong(logp->log_hdr.log_time));
	xprintf("\n	   ");
  }

  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 )
{
	if (send_ascii != 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( Void )
{
	if (send_ascii == TELEM_UUENCODE)
		xputs("`\n");
	if (send_ascii != 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 FALSE*/
	  return( TRUE );
	task_delay(TICKS_PER_SECOND/20);	/* Else, wait 50 ms to give TNC */
  } while ( ser_sndsts(serPort) > 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 Int16
putOneLogRec(LogPtr lp, LogRec *rcdp)
{
  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...
	//	  dprintf("putOneLogRec:rcd=%d type=%d len=%d offset=%d rtn = %ld\n",
	//		swapWord(rcdp->log_hdr.log_rcd),rcdp->log_hdr.log_type,
	//		len, offset, rtn);
	if (rtn == 0)
	  return(OK);						/* No more data in this record	*/

	switch(send_ascii)
	{
	  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(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 (send_ascii == TELEM_ASCII)
		  newline();
	  return(OK);
	}
  }

  return((Int16)rtn);					/* If got here, error			*/

} /* putOneLogRec() */


/************************************************************************/
/* Function	   : writeOneLogRec											*/
/* Purpose	   : Get one data log record, and append it to file			*/
/* Inputs	   : LogBlk number, Log record number, FILE 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 Int16
writeOneLogRec(LogPtr lp, LogRec *rcdp, FILE* file)
{
	Int32 rtn;
	Nat16 offset;
	size_t cnt;

	/* check for null file pointer */
	if ( file == 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)
			cnt = fwrite(rcdp, 1, rtn + sizeof(LogRecHdr), file);
		else
			cnt = fwrite(rcdp->log_data, 1, rtn, file);

		/* check for bad file writes */
		if (cnt < rtn)
			return(ERROR);
		
		if (rtn >= (swapWord(rcdp->log_hdr.log_len)-offset))
		  return(OK);					/* If got whole thing, return	*/
	}

	return ((Int16)rtn);	/* 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 Int16
getLogRecs(LogPtr logptr, Parm_t nmbr)
{
  LogRec		*rcdp;
  Int16			rtn;

  dprintf("getLogRecs: logptr=%lu nmbr=%lu\n",logptr, nmbr);
  
  if( (nmbr < 0) || (nmbr>RECS_PER_BLK) )
		return(ERROR);

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

  if ((rcdp = (LogRec *)malloc(sizeof(LogRec))) == NULL)
  {										/* Get log buffer, rtn if none	*/
	xprintf("Out of memory\n");
	return(ERROR);
  }

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

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

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

  stopdata();							/* 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					*/
/************************************************************************/
		MLocal Int16
getYLogRecs(LogPtr logptr, Parm_t nmbr)
{
  LogRec		*rcdp;
  Int16			rtn;
  FILE			*file = NULL;
  
  //xprintf("getLogRecs: nmbr(Parm_t)=%ld nmbr(Nat16)=%d\n",nmbr,(Nat16)nmbr);
  
  if( (nmbr < 0) || (nmbr>RECS_PER_BLK) )
		return(ERROR);


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

  if ((rcdp = (LogRec *)malloc(sizeof(LogRec))) == NULL)
  {										/* Get log buffer, rtn if none	*/
	xprintf("Out of memory\n");
	return(ERROR);
  }
  
  /* open temp file */
  file = fopen(YLOGS_FILE, "w+b");
  
  if ( file == NULL )
  {
	  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 */
  fclose(file);

  /* send the logs back via Y-Modem */
  if ( ymdmSend(0, YLOGS_FILE) != OK )
  {
	  xprintf("Y-Modem xfer of file %s failed.\n", YLOGS_FILE);
	  return(ERROR);
  }
  
  /* delete tempfile */	   
  if ( unlink(YLOGS_FILE) )
  {
	  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														*/
/************************************************************************/
		Int16
getlog(Nat16 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														*/
/************************************************************************/
		Int16
getylog(Nat16 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														*/
/************************************************************************/
		Int16
dump(Nat16 pmask, Parm_t blk, Parm_t nblks)
{
  LogPtr		logPtr;
  Reg Int16		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( (rtn == ERROR) ? ERROR : 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 *)malloc(sizeof(LogRec))) == NULL)
  {										/* Get log buffer, rtn if none	*/
	xprintf("Out of memory\n");
	return(ERROR);
  }

  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														*/
/************************************************************************/
		Int16
checkLog(Nat16 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 %lu returns %lu 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														*/
/************************************************************************/
		Int16
getdata(Nat16 pmask, Parm_t maxRecs)
{
  LogRec		*rcdp;					/* LogRec buffer				*/
  LogRecNum		usrLog;					/* Copy of nextUsrLog			*/
  LogPtr		freeLogP;				/* Ptr to how much data collected*/
  Int16			rtn;
  Int32			nRecs;

  if ((rcdp = (LogRec *)malloc(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 uuencode header*/

	rtn = putOneLogRec(usrLog, rcdp);
	usrLog++;
	nRecs++;

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

  }

  free(rcdp);							/* Free buffer					*/
										/* Copy back to nextUsrLog		*/
  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														*/
/************************************************************************/
		Int16
getydata(Nat16 pmask, Parm_t maxRecs)
{
  LogRec		*rcdp;					/* LogRec buffer				*/
  LogPtr		usrLog;					/* Copy of nextUsrLog			*/
  LogPtr		freeLogP;				/* Ptr to how much data collected*/
  Int16			rtn;
  Int32			nRecs;
  FILE			*file = NULL;

  if ((rcdp = (LogRec *)malloc(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 */
  file = fopen(YDATA_FILE, "a+b");
  
  if ( file == NULL )
  {
	  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 */
  fclose(file);
  
  free(rcdp);							/* Free buffer					*/
  nextUsrLog = usrLog;					/* Copy back to nextUsrLog		*/
  
/* What should the policy be here?	delete on success or every time ?*/
/* Should the temp filed be opened as "w+b" or "a+b" ? */

  /* send the logs back via Y-Modem */
  if ( ymdmSend(0, YDATA_FILE) == OK )
  {
	  if ( unlink(YDATA_FILE) )
	  {
		  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														*/
/************************************************************************/
		Int16
getlognmbrs(Void)
{
  LogPtr		newest, oldest;

  logGetCurrentLogPtrs(&newest, &oldest);
  xprintf("Records logged: %lu to %lu\n", oldest, newest);
  xprintf("Next download is record %lu\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;
  TimeOfDay		tod;
  TaskDesc		*td;

  tod = clk_read();
  for ( dp = (Driver *)(drv_list.lst_head); dp != DRV_NULL; 
		dp = dp->drv_next )
  {
	xprintf("%-20s ", dp->drv_name );
	if ( dp->drv_td != NULLTID )
	{
	  td=dp->drv_td;
	  if(td->td_state==PENDING)
		xputs("Pending\n");
	  else if(td->td_state==DELAY)
		xprintf("Delay %ld.%02d secs\n", td->td_delay/TICKS_PER_SECOND,
				(Nat16)(td->td_delay % 100));
	  else if(td->td_state==ZOMBIE)
		xputs("Zombie\n");
	  else
		xputs("Sampling\n");
	}
	else
	  if ( dp->drv_parms[INTERVAL] == 0 )
		xputs("Off\n");
	  else
		xprintf("%lu secs\n", dp->drv_wakeup - tod);
  }

} /* showDrivers() */


/************************************************************************/
/* Function	   : drvUsrFunc												*/
/* Purpose	   : Perform driver user function							*/
/* Inputs	   : Drvr name, Time til func, parameter, function flag		*/
/* Outputs	   : OK														*/
/************************************************************************/
		MLocal Int16
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 = clk_read() + functime;
  dp->drv_flags |= funcflag;

  return( OK );

} /* drvUsrFunc() */


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

} /* aux() */


/************************************************************************/
/* Function	   : init													*/
/* Purpose	   : Initialize driver										*/
/* Inputs	   : Parm Mask, Driver name, Time til init, parameter		*/
/* Outputs	   : OK														*/
/************************************************************************/
		Int16
init(Nat16 pmask, char *name, Parm_t inittime, Parm_t parm )
{
#pragma unused(pmask)
  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														*/
/************************************************************************/
		Int16
sync( Nat16 pmask, char *name, Parm_t synctime, Parm_t parm )
{
#pragma unused(pmask)
  return( drvUsrFunc(name, synctime, parm, DO_SYNC) );

} /* sync() */


/************************************************************************/
/* 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 ", pp->prompt );

  if ( pp->radix == 16 )
	xprintf( "hex  %lx	", *(Nat32 *)((Byte *)dp + pp->offset) );
  else
	xprintf( "dec  %ld	", *(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, Nat16 radix, Byte *buffer )
{
  if ( xgets_tmout((char *)buffer, USR_BUFSIZE, 30) > 0 )
  {
	xflush_ser(TICKS_PER_SECOND/5);
	return( (sscanf(buffer, (radix == 10) ? " %ld" : " %lx", 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 )
{
  Nat16			radix;
  Nat32			val, *parmp;

  showDrvParms(dp);

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

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

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

} /* drvgetparms() */


/************************************************************************/
/* Function	   : drvparms												*/
/* Purpose	   : Set Driver parameters									*/
/* Inputs	   : Parm mask, ptr to driver name							*/
/* Outputs	   : OK														*/
/************************************************************************/
		Int16
drvparms(Nat16 pmask, char *name, Parm_t unused1, Parm_t unused2, Parm_t unused3, Byte *buffer)
{
#pragma unused(unused1, unused2, unused3)
  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											*/
/************************************************************************/
		Int16
drvadd(Nat16 pmask, char *drvType, char *drvName, Parm_t unused1, Parm_t unused2,
	   Byte *buffer)
{
#pragma unused(unused1, unused2)
  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 %lu.\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														*/
/************************************************************************/
		Int16
drvdel(Nat16 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*/
	  task_kill(dp->drv_td);

	list_get( &drv_list, (Node *)dp );
	free( (char *)dp );
  }

  return( OK );

} /* drvdel() */


/************************************************************************/
/* Function	   : usrKill												*/
/* Purpose	   : Kill a task											*/
/* Inputs	   : Parm mask, ptr to driver name							*/
/* Outputs	   : OK														*/
/************************************************************************/
Int16	usrKill(Nat16 pmask, char *name)
{
#pragma unused(pmask)
  TaskDesc		*td;
  Driver		*dp;

  if ((td = task_find(name)) == DRV_NULL)
	xputs("Task not found\n");
  else
  {
	xprintf("Killing task %#lx\n", td);
	task_kill(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														*/
/************************************************************************/
Int16	checkStack(Void)
{
  Node			*np;
  TaskDesc		*td;
  char			*highwater;
  Nat16			*p;

  for (np = list_first(getTaskList()); np != NULL; np = list_next(np))
  {
	  td = ((TaskNode *)np)->tn_td;

	  for(p = (Nat16 *)((char *)td->td_stack - STKSIZE + 2);
		  p < td->td_stack; p++)
		if (*p != 0x4e70)
		{
		  highwater = (char *)p;
		  break;
		}

	  xprintf("Task %s\n", td->td_name);
	  xprintf("	 Stack Base %#lx\n", td->td_stack);
	  xprintf("	 Current SP %#lx\n", td->td_sp);
	  xprintf("	 High Water %#lx\n", highwater);
	  xprintf("	 %d used, %d Free\n\n",
			  (Nat16)((char *)td->td_stack - highwater),
			  STKSIZE - (Nat16)((char *)td->td_stack - highwater));
	}

  return(OK);

} /* checkStack() */


/************************************************************************/
/* Function	   : sample													*/
/* Purpose	   : test function to sample drivers						*/
/* Inputs	   : Parm mask, ptr to driver name							*/
/* Outputs	   : OK														*/
/************************************************************************/
Int16 sample(Nat16 pmask, char *name)
{
	Driver *dp;
	O2Sample o2_sample;
	EnvSample env_sample;
	int timeout = 0;

	if ( ((pmask & 1) == 0) || ((dp = drvr_find(name)) == DRV_NULL) )
	{
		printf("can't find the '%s' driver\r\n", dp->drv_name);
		showDrivers();
	}
	else
	{
		
		/* make sure you can sample the driver */
		if ( (dp->drv_task != optrode_drv) && ( dp->drv_task != envDrv ) )
		{
			printf("can't sample the '%s' driver\r\n", dp->drv_name);
			return OK;
		}
		
		/* sync the driver */
		drvSync(name);

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

		/* wait for the sample to complete */
		while ( (dp->drv_td != NULLTID) && (++timeout < sync_tmout) )
			task_delay(TICKS_PER_SECOND);
		
		if ( dp->drv_task == optrode_drv )
			lastOptrodeSample(dp, &o2_sample, TRUE);
		else if ( dp->drv_task == envDrv )
			lastEnvSample(dp, &env_sample, TRUE);
	}
	
	return OK;
}

/************************************************************************/
/* Function	   : lsample												*/
/* Purpose	   : show last sample of a driver							*/
/* Inputs	   : Parm mask, ptr to driver name							*/
/* Outputs	   : OK														*/
/************************************************************************/
Int16 lsample(Nat16 pmask, char *name)
{
	Driver *dp;
	O2Sample o2_sample;
	EnvSample env_sample;
	int timeout = 0;

	if ( ((pmask & 1) == 0) || ((dp = drvr_find(name)) == DRV_NULL) )
	{
		printf("can't find the '%s' driver\r\n", dp->drv_name);
		showDrivers();
	}
	else
	{
		
		/* make sure you can sample the driver */
		if ( (dp->drv_task != optrode_drv) && ( dp->drv_task != envDrv ) )
		{
			printf("can't get the last sample from the '%s' driver\r\n", 
				   dp->drv_name);
			return OK;
		}
		
		if ( dp->drv_task == optrode_drv )
			lastOptrodeSample(dp, &o2_sample, TRUE);
		else if ( dp->drv_task == envDrv )
			lastEnvSample(dp, &env_sample, TRUE);
	}
	
	return OK;
}
