/****************************************************************************/
/* Copyright 1991 - 2003 MBARI												*/
/****************************************************************************/
/* Summary	: User Interface Routines for OASIS3 Mooring Controller			*/
/* Filename : userif.c														*/
/* Author	: Robert Herlien (rah)											*/
/* Project	: OASIS3 Mooring												*/
/* $Revision: 1.1 $															*/
/* Created	: 02/03/2003 from OASIS userif.c								*/
/*																			*/
/* 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:													*/
/* 03feb2003 rah - created from OASIS userif.c								*/
/* $Log: userif.c,v $
/* Revision 1.1	 2011/10/25 22:39:22  bobh
/* Initial oasis4 revision
/*
/* Revision 1.8	 2011/03/04 20:21:00  bobh
/* Removed showScript() from help()
/*
/* Revision 1.7	 2010/10/25 16:42:50  bobh
/* Changes to support scripting
/*
/* Revision 1.6	 2010/05/17 18:48:45  bobh
/* Added cfgsave, more, ren commands
/*
/* Revision 1.5	 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.4	 2009/04/28 17:16:31  bobh
/* Added CTD Simulator
/*
/* Revision 1.3	 2009/04/27 22:49:43  bobh
/* First working version of remote serial ports
/*
/* 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.8	 2008/07/17 17:38:42  rich
/* Added gpsClock and vaisala drivers
/*
/* Revision 1.7	 2004/08/19 17:20:21  bobh
/* Fix bug to work with DEBUG_SPI
/*
/* Revision 1.6	 2004/08/18 21:08:35  bobh
/* Diagnostic routines added, minor bug fixes, blinky added as parm
/*
/* Revision 1.5	 2003/09/09 00:01:15  bobh
/* Misc tweaks after demo
/*
/* Revision 1.4	 2003/09/04 22:26:26  bobh
/* no message
/*
/* 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>						/* SHUTCAL definition				*/
#include <debug.h>
#include <io.h>							/* OASIS I/O definitions			*/
#include <list.h>						/* OASIS Linked List library		*/
#include <task.h>						/* OASIS Multitasking definitions	*/
#include <serial.h>						/* OASIS Serial I/O definitions		*/
#include <malloc.h>						/* OASIS malloc routines			*/
#include <drvr.h>						/* OASIS driver functions			*/
#include <log.h>						/* OASIS data logging definitions	*/
#include <userif.h>						/* OASIS User Interface routines	*/
#include <usrcmds.h>					/* User Command function definitions*/
#include <parm.h>						/* Parameter table definitions		*/
#include <file.h>						/* usrDir							*/
#include <utils.h>						/* OASIS Utility routines			*/
#include <ymodem.h>						/* Ymodem command send command		*/
#include <diag.h>						/* OASIS diagnostic routines		*/
#include <swdiag.h>						/* Software diagnostic func protos	*/
#include <variable.h>					/* Variable definitions				*/
#include <spi.h>
#include <modbus.h>
#include <usrRemoteCmds.h>				/* User funcs for Remote I/O		*/
#include <cfgsave.h>					/* OASIS Config file save routines	*/
#include <parse.h>						/* Generic parser declarations		*/
#include <script.h>						/* Scripting Language definitions	*/
#include <usrCmdTbl.h>					/* User Command table				*/

#include <ctype.h>						/* Standard ctype.h defs			*/
#include <stdio.h>						/* Standard I/O						*/
#include <string.h>						/* Standard string.h defs			*/

#define TNC_PWRUP_TIME	(2 * TICKS_PER_SECOND)	/* Wait 2 secs after pwr up */
#define TNC_REPLY_FLUSH_TIME (3*TICKS_PER_SECOND/10)
										/* Wait 300 ms to flush TNC replies */

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

Extern TimeOfDay		tod;			/* Current time in TimeOfDay format */
Extern TimeOfDay		radio_time;		/* Last time connected on radio		*/
Extern Parm_t			tncchr;			/* TNC command character			*/
Extern const char * const signon;		/* Signon message					*/

Extern char				*tnc_name;		/* TNC node name					*/
Extern MBool			tnc_connected;	/* TRUE if we're connected to TNC	*/


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

MLocal LstHead			funcList;		/* Function list				*/

MLocal const char		prompt[] = PROMPT;

MLocal const char * const tnc_cmds[] =
{ "NO ON", "EC OFF", "AX ON", "AU OFF", "BBS ON", "P 0", "PACT After 3",
  "DW 33", "TXD 33", "FR 5", "MAXF 4", "CP ON", "CR OFF", "M 0", 
  "CONM C", "SE $1A", "TXU OFF", "F OFF", "STR 0", "ADR OFF"
};


/************************************************************************/
/* Function	   : get_parms												*/
/* Purpose	   : Parse parameters from user command tail				*/
/* Inputs	   : Command tail ptr, Ptr to array of parms, ptr to funcEntry*/
/* Outputs	   : Bit vector of parameters found							*/
/************************************************************************/
		MLocal Nat16
get_parms(char *tailp, Parm_t *parmp, FuncNode *fnp)
{
  Tokenizer		tokenizer;
  Reg char		*tok;
  Reg Nat16		i, pmask;

  pmask = 0;
  memset(parmp, 0, NPARMS * sizeof(Parm_t)); /* Init parms to 0 */
  deblank( tailp );
  initTokenizer(&tokenizer, tailp);

  for ( i = 0; i < NPARMS; i++ )
  {
	switch( fnp->fn_parmtype[i] )
	{
	  case STRING:						/* String type. Go til next		*/
		tok = getNextToken(&tokenizer);
		if (isaTok(tok))
		{
			parmp[i] = (Parm_t)getStringVal(tok);
			pmask |= (1 << i);
		}
		break;

	  case ALL:							/* Get all of command tail		*/
		parmp[i] = (Parm_t)(tokenizer.next);
		tokenizer.next = NULL;
		pmask |= (1 << i);
		break;

	  case DEC:							/* Get decimal number			*/
		tok = getNextToken(&tokenizer);
		if (getLongVal(tok, &parmp[i]))
		  pmask |= (1 << i);
		break;

	  case HEX:							/* Get hex number				*/
		tok = getNextToken(&tokenizer);
		if (isaTok(tok) && (sscanf(tok, " %lx", &parmp[i]) >= 1))
		  pmask |= (1 << i);
		break;
	}
  }

  return( pmask );

} /* get_parms() */


/************************************************************************/
/* Function	   : checkVariable											*/
/* Purpose	   : See if command line is a variable assignment			*/
/* Inputs	   : Command Line ptr										*/
/* Outputs	   : OK or ERROR											*/
/************************************************************************/
MLocal Errno checkVariable(char *cmd)
{
	char		*keyword, *tail;

	if ((tail = strchr(cmd, '=')) == NULL)
		return(ERROR);

	keyword = strtok(cmd, "		=\n");
	tail++;
	deblank(tail);

	if (*keyword != '$')
		return(ERROR);

	return(parseVariable(keyword, tail));

} /* checkVariable() */


/************************************************************************/
/* Function	   : executeCmdLIne											*/
/* Purpose	   : Execute the given command line (from user)				*/
/* Inputs	   : Command Line ptr										*/
/* Outputs	   : OK or ERROR											*/
/************************************************************************/
		Int16
executeCmdLine(char *cmdLine)
{
  Reg FuncNode	*fnp;					/* FuncList node ptr			*/
  Reg char		*tail;					/* Ptr to tail of cmd string	*/
  Reg Nat16		pmask;					/* Parameter mask				*/
  Reg Int16		rtn;					/* Function return code			*/
  Parm_t		parms[NPARMS];			/* Function parameters			*/

  if (checkVariable(cmdLine) == OK)
	  return(OK);

  for ( fnp = (FuncNode *)list_first(&funcList); fnp != FN_NULL;
		fnp = (FuncNode *)list_next((Node *)fnp) )
  {												/* Find funcList entry	*/
	if ( (tail = cmp_ulc(cmdLine, fnp->fn_cs)) != NULL )
	{
	  pmask = get_parms(tail, parms, fnp);

	  if ( (rtn = (*fnp->fn_cmd)(pmask, parms[0], parms[1], parms[2],
								 parms[3], cmdLine)) != ERROR )
		return( rtn );
	  break;
	}
  }

  return( ERROR );

} /* executeCmdLine() */


/************************************************************************/
/* Function	   : one_usr_cmd											*/
/* Purpose	   : Do one cycle of user cmd & response					*/
/* Inputs	   : Buffer ptr, len, timeout in seconds					*/
/* Outputs	   : OK, ERROR, or ABORT									*/
/************************************************************************/
		MLocal Int16
one_usr_cmd(char *buffer, Nat16 buflen, Nat16 tmout)
{
  Reg Int16		rtn;					/* Function return code			*/

  xputs( prompt );						/* Print prompt					*/
  if ( (rtn = xgets_tmout(buffer, buflen, tmout)) == ERROR )
  {										/* If timed out, abort			*/
	xputs("Timeout\n");
	return( ABORT );
  }
	
  if (rtn == 0)							/* If no command, return OK*/
	return( OK );

  if ((rtn = executeCmdLine(buffer)) == ERROR)
	xputs("Bad Command\n");

  return( rtn );

} /* one_usr_cmd() */


/************************************************************************/
/* Function	   : usrif													*/
/* Purpose	   : User I/F Main routine.	 Called by usr_drv or radio_drv */
/* Inputs	   : Timeout in seconds										*/
/* Outputs	   : OK, ERROR if got too many bad cmds, ABORT if no memory */
/************************************************************************/
		MLocal Int16
usrif( Nat16 tmout )
{
  Int16			rtn;
  Nat16			badcnt;
  char			*buffer;

  if ( (buffer = malloc(USR_BUFSIZE)) == NULL )
	return( ABORT );					/* If no memory, return ABORT	*/
  
#ifdef FULL_SIGNON
  xprintf("%s\n%s\n", signon, tnc_name); /* TNC Name=Deployment			*/
#endif

  badcnt = 0;							/* Init bad cmd counter			*/

  while ( (rtn = one_usr_cmd(buffer, USR_BUFSIZE, tmout)) != ABORT )
  {										/* Do user interface			*/
	if ( (rtn == ERROR) && (++badcnt > 50) )
	  break;							/* Check for infinite loop		*/
  }
  free( buffer );						/* Free buffer					*/
  return( rtn );

} /* usrif() */


/************************************************************************/
/* Function	   : usr_drv												*/
/* Purpose	   : User I/F Main routine									*/
/* Inputs	   : Driver Pointer											*/
/* Outputs	   : None													*/
/************************************************************************/
		Void
usr_drv( Driver *dp )
{
  drv_ser_port( dp );					/* Setup serial port			*/
  usrif( dp->drv_parms[TIMEOUT] );
  drv_ser_release( dp );				/* Release serial port			*/
  drv_pwroff( dp );						/* Turn off power				*/

} /* usr_drv() */


/************************************************************************/
/* Function	   : tnc_cmd_mode											*/
/* Purpose	   : Put TNC in Command mode								*/
/* Inputs	   : None													*/
/* Outputs	   : None													*/
/************************************************************************/
		MLocal Void
tnc_cmd_mode( Void )
{
  xputc( tncchr );								/* Go to cmd mode		*/
  xflush_ser( TNC_REPLY_FLUSH_TIME );			/* Clear incoming buffer*/

} /* tnc_cmd_mode() */


/************************************************************************/
/* Function	   : tnc_converse											*/
/* Purpose	   : Put TNC in Converse mode								*/
/* Inputs	   : None													*/
/* Outputs	   : None													*/
/************************************************************************/
		Void
tnc_converse( Void )
{
  xputs("K\r");							/* Go to converse mode			*/
  xflush_ser( TNC_REPLY_FLUSH_TIME );	/* Throw away reply				*/

} /* tnc_converse() */


/************************************************************************/
/* Function	   : sendTncCmd												*/
/* Purpose	   : Send a command to the TNC								*/
/* Inputs	   : Command string to send									*/
/* Outputs	   : None													*/
/************************************************************************/
		MLocal Void
sendTncCmd( char *cmd )
{
  tnc_cmd_mode();
  xprintf("%s\r", cmd);
  xflush_ser( TNC_REPLY_FLUSH_TIME );

} /* sendTncCmd() */


/************************************************************************/
/* Function	   : tnc_status												*/
/* Purpose	   : Get STATus message from TNC							*/
/* Inputs	   : None													*/
/* Outputs	   : Status of TNC, or ERROR								*/
/* Comment	   : Leaves TNC in command mode								*/
/************************************************************************/
		MLocal Int16
tnc_status( Void )
{
  char			*p;
  Int16			res, i;
  char			buff[32];

  res = ERROR;									/* Init result			*/
  tnc_cmd_mode();								/* Go to command mode	*/
  xputs("STAT\r");								/* Ask for status		*/

  while ( xgets_tmout(buff, sizeof(buff), 1) != ERROR )
	if ( (p = strchr(buff, '$')) != NULL )
	  if ((i = sscanf(p+1, "%x", &res)) >= 1)
		break;

  xflush_ser( TNC_REPLY_FLUSH_TIME );			/* Throw away extra "cmd:"*/

  return(res);

} /* tnc_status() */


/************************************************************************/
/* Function	   : wait_connect											*/
/* Purpose	   : Wait for (dis)connect status							*/
/* Inputs	   : Boolean (TRUE to look for connect, FALSE to look for	*/
/*				  disconnect), timeout in seconds						*/
/* Outputs	   : MBool, TRUE if found appropriate status				*/
/************************************************************************/
		MLocal MBool
wait_connect( MBool connect, Nat16 tmout )
{
  Nat32 startTick;

  for ( startTick = ioGetTick(); 
		(ioGetTick() - startTick) < ((Nat32)tmout * TICKS_PER_SECOND); )
  {
	task_delay( TICKS_PER_SECOND/2 );
	if ( connect )
	{
	  if ( (tnc_status() & 0xfffd) == 4 )
		return( TRUE );
	}
	else
	{
	  if ( tnc_status() == 0 )
		return( TRUE );
	}
  }

  return( FALSE );

} /* wait_connect() */


/************************************************************************/
/* Function	   : tnc_init												*/
/* Purpose	   : Initialize TNC parameters								*/
/* Inputs	   : None													*/
/* Outputs	   : None													*/
/************************************************************************/
		MLocal Void
tnc_init( Void )
{
  Nat16 i;

  tnc_cmd_mode();						/* Go to command mode			*/
  xputs( "RESET\r" );					/* Go to command mode, Reset TNC*/
  xflush_ser( TICKS_PER_SECOND/2 );		/* Clear incoming buffer		*/
	
  for ( i = 0; i < NumberOf(tnc_cmds); i++ )
  {
	xprintf("%s\r", tnc_cmds[i]);		/* Send command					*/
	xflush_ser( TNC_REPLY_FLUSH_TIME ); /* Throw away reply				*/
  }
  xprintf("MY %s\r", tnc_name);			/* Set up TNC name string		*/
  xflush_ser( TNC_REPLY_FLUSH_TIME );	/* Throw away reply				*/

} /* tnc_init() */


/************************************************************************/
/* Function	   : tnc_drv												*/
/* Purpose	   : TNC Packet Radio Driver								*/
/* Inputs	   : Driver Pointer											*/
/* Outputs	   : None													*/
/************************************************************************/
		Void
tnc_drv( Driver *dp )
{
  drv_ser_port( dp );					/* Setup serial port			*/
  task_delay( TNC_PWRUP_TIME );			/* Wait for TNC to come alive	*/
  xflush_ser( TNC_REPLY_FLUSH_TIME );	/* Throw away signon message	*/

  //  if ( (dp->drv_flags & DO_INIT) || (--dp->drv_cnt <= 0) )
  if (dp->drv_flags & DO_INIT)
  {										/* Initialize TNC if needed		*/
	tnc_init();							/* Init TNC						*/
	dp->drv_flags &= ~DO_INIT;			/* Turn off init flag			*/
	dp->drv_cnt = dp->drv_parms[PARM2];
  }

  if ( wait_connect(TRUE, dp->drv_parms[PARM0]) )
  {										/* Wait for connection			*/
	tnc_connected = TRUE;				/* Show we're connected			*/
	radio_time = tod;					/* Remember when connected		*/
	tnc_converse();						/* Go to converse mode			*/

	if ( usrif(dp->drv_parms[TIMEOUT]) == ERROR )
										/* Do the user interface		*/
	  dp->drv_flags |= DO_INIT;			/* If lots of bad cmds, re-init */

	while ( dp->drv_wakeup <= tod )		/* Reset wakeup					*/
	  dp->drv_wakeup += dp->drv_parms[INTERVAL];

	if ( tnc_connected )				/* If still connected, disconnect*/
	  sendTncCmd( "DISC" );				/* Request disconnect			*/

	wait_connect(FALSE, dp->drv_parms[PARM1]);
	task_delay( 3 * TICKS_PER_SECOND );
  }

  drv_ser_release( dp );				/* Release serial port			*/
  drv_pwroff( dp );						/* Turn off radio power			*/

} /* tnc_drv() */


/************************************************************************/
/* Function	   : freewave_drv											*/
/* Purpose	   : Freewave Radio Driver									*/
/* Inputs	   : Driver Pointer											*/
/* Outputs	   : None													*/
/************************************************************************/
		Void
freewave_drv( Driver *dp )
{
  drv_ser_port( dp );					/* Setup serial port			*/
 
 
 /* Wait for radio to come alive		*/
 
 /* task_delay( TNC_PWRUP_TIME );*/
 task_delay( dp->drv_parms[PARM1] * TICKS_PER_SECOND);
 
 
  xflush_ser( TNC_REPLY_FLUSH_TIME );	/* Throw away signon message	*/

  if ( xgetc_tmout(dp->drv_parms[PARM0]) != ERROR )
  {										/* Wait for connection			*/
	radio_time = tod;					/* Remember when connected		*/

	usrif(dp->drv_parms[TIMEOUT]);		/* Do the user interface		*/

	while ( dp->drv_wakeup <= tod )		/* Reset wakeup					*/
	  dp->drv_wakeup += dp->drv_parms[INTERVAL];
  }

  drv_ser_release( dp );				/* Release serial port			*/
  drv_pwroff( dp );						/* Turn off radio power			*/

} /* freewave_drv() */


/************************************************************************/
/* Function	   : user_init												*/
/* Purpose	   : Reset userif module due to cold (full) init			*/
/* Inputs	   : None													*/
/* Outputs	   : None													*/
/************************************************************************/
		Void
user_init( Void )
{
  Reg FuncNode			*fnp;
  Reg const FuncEntry	*fep;
  Reg Nat16				i;

  list_init( &funcList );

  if ( (fnp = (FuncNode *)malloc(NumberOf(functbl) * sizeof(FuncNode)))
	   != FN_NULL )
	for ( i = 0, fep = functbl; i < NumberOf(functbl); i++, fnp++, fep++ )
	{
	  memcpy( &fnp->fn_cs, fep, sizeof(FuncEntry) );
	  list_add( &funcList, (Node *)fnp );
	}

} /* user_init() */


/************************************************************************/
/* Function	   : help													*/
/* Purpose	   : User Help Function										*/
/* Inputs	   : None													*/
/* Outputs	   : OK														*/
/* Comments	   : Placed here, rather than in usrcmds.c, because it needs*/
/*				 access to the user function table						*/
/************************************************************************/
		Int16
help( Void )
{
  FuncNode				*fnp;

  for ( fnp = (FuncNode *)list_first(&funcList); fnp != FN_NULL;
		fnp = (FuncNode *)list_next((Node *)fnp) )
  {
	if ( fnp->fn_info != NULL )
	  xprintf("%-12s %s\n", fnp->fn_cs, fnp->fn_info);
  }

  return( OK );

} /* help() */


/************************************************************************/
/* Function	   : cmdAdd													*/
/* Purpose	   : Add User Function										*/
/* Inputs	   : None													*/
/* Outputs	   : OK														*/
/* Comments	   : Placed here, rather than in usrcmds.c, because it needs*/
/*				 access to the user function table						*/
/************************************************************************/
		Int16
cmdAdd( Nat16 pmask, char *name, Int16 (*func)(), char *helpstr,
		Parm_t unused1, Byte *buffer )
{
#pragma unused(unused1)
  Reg FuncNode			*fnp;
  Reg Nat16				namelen, helplen;
  Nat16					i;
  Nat32					result;
  FuncNode				*fnpsave;

  if ( (pmask & 3) != 3 )
	return( ERROR );

  namelen = strlen(name) + 1;
  helplen = strlen(helpstr) + 1;

  if ( (fnp = (FuncNode *)malloc(sizeof(FuncNode) + namelen + helplen))
	   == FN_NULL )
	return( ERROR );

  fnpsave = fnp;
  fnp->fn_cmd = func;
  fnp->fn_cs = (char *)(fnp + 1);
  memcpy( fnp->fn_cs, name, namelen );

  if ( helplen == 0 )
	fnp->fn_info = NULL;
  else
  {
	fnp->fn_info = fnp->fn_cs + namelen;
	memcpy( fnp->fn_info, helpstr, helplen );
  }

  for ( i = 0; i < NPARMS; i++ )
  {
	xprintf("Type of parameter %d: 0=NONE, 1=STRING, 2=ALL, 3=DEC, 4=HEX\n", i);
	if ( !getparm(&result, 10, buffer) )
	{
	  free( (char *)fnp );
	  return( ERROR );
	}

	fnpsave->fn_parmtype[i] = (Nat16)result;
  }

  list_add( &funcList, (Node *)fnpsave );

  return( OK );

} /* cmdAdd() */
