/****************************************************************************
**                                                                      	*
**  FILE        :  mvp.c                                                	*
**                                                                      	*
**  DESCRIPTION :  Program to control Buoyancy Engine and logging			*
**                 instruments on MBARI Vertical Profiler               	*
**                                                                      	*
**  COPYRIGHT   :  2000 Monterey Bay Aquarium Research Institute        	*
**                                                                      	*
**	REVISION HISTORY:														*
**		1.0.0	Initial release (prm)										*
**		1.1.0	Added yoyo command (prm)									*
**		1.2.0	Added motor brake (prm)										*
**		1.3.0	Added motor soft-start (prm)								*
**		1.3.1	Improved accuracy of pot position to percent calculation.	*
**				Added second argument to yo-yo command (prm)            	*
**		1.4.0	Added interrupt sampling of pot position					*
**		1.5.0	Moved all motor position checking to interrupt routine	 	*
**		1.6.0	Added deployment mode with low-power sleep					*
**		1.7.0	Added deployment mode with casts only at given times of day	*
**		1.8.0	Added depth checking with SBE39 during deployment			*
**		1.8.1	Fixed depth bug in smd command, fixed bug in				*
**				CloseSerPorts()	routine										*
**		1.9.0	Added MBBP control											*
**		1.9.1	Changed GoToSleep() to use SleepTill() instead of			*
**				LowPowerSleepTill() because of bugs in that routine			*											*
**		1.10.0	SBE39 depth reading now returns last good value if can't	*
**				read instrument, MBBP now shuts down after timeout if MVP	*
**				stuck at a depth											*
**		1.11.0	Added SBE19 control. Run instruments on upcast only.		*
**				Added Test Instrument Logging command.						*
**		1.11.1  Changed delay between SBE39 readings to 14 sec, and cast	*
**				timeout to 360 readings (should be about 1.5 hours).		*
**																			*
****************************************************************************/
#ifndef PRECOMPHDRS 
#include	<TT8.h>			/* Tattletale Model 8 Definitions */
#include	<tat332.h>		/* 68332 Tattletale (7,8) Hardware Definitions */
#include	<sim332.h>		/* 68332 System Integration Module Definitions */
#include	<qsm332.h>		/* 68332 Queued Serial Module Definitions */
#include	<tpu332.h>		/* 68332 Time Processing Unit Definitions */
#include	<dio332.h>		/* 68332 Digital I/O Port Pin Definitions */
#include	<tt8pic.h>		/* Model 8 PIC Parallel Slave Port Definitions */

#include	<tt8lib.h>		/* definitions and prototypes for Model 8 library */
#include	<userio.h>		/* common convenient user I/O routines */

#include	<assert.h>
#include	<ctype.h>
#include	<errno.h>
#include	<float.h>
#include	<limits.h>
#include	<locale.h>
#include	<math.h>
#include	<setjmp.h>
#include	<signal.h>
#include	<stdarg.h>
#include	<stddef.h>
#include	<stdio.h>
#include	<stdlib.h>
#include	<string.h>
#include	<time.h>
#endif 
 
#include	"mvp.h"
#include	"pwm.h"
#include	"newsleep.h"
#include	"instrument.h"


/* Define error conditions for buoyancy engine */
typedef enum {
	kNone,
	kCurrentTrip,
	kOperatorAbort,
	kPotError
} BEFault;

/* Define the routines that do the work for each command */
typedef enum {
	kEmpty,
	kUnknown,
	kSetEngPos,
	kReadEngPos,
	kReadVehDepth,
	kSetModeDeploy,
	kSetModeYoyo,
	kSetModeTrans,
	kSetClkTime,
	kReadClkTime,
	kTestInstrLog,
	kExitToMon,
	kHelp
} CmdNum;

typedef struct {
	char    *theCommand;
	char    *theExplanation;
	CmdNum  theNumber;
} ACommand;

ACommand commands[] = {
	"SEP", "[0..100]                  Set engine position", kSetEngPos,
	"REP", "                          Read engine position", kReadEngPos,
	"RVD", "                          Read vehicle depth", kReadVehDepth,
	"SMD", "                          Set mode deploy", kSetModeDeploy,
	"SMY", "[0..99][1..100][1..1440]  Set mode yo-yo (hit any key to abort)", kSetModeYoyo,
	"SMT", "[0,1,2]                   Set mode transparent", kSetModeTrans,
	"SCT", "                          Set clock time", kSetClkTime,
	"RCT", "                          Read clock time", kReadClkTime,
	"TIL", "                          Test instrument logging", kTestInstrLog,
	"ETM", "                          Exit to TOM8 monitor", kExitToMon,
	"HELP", NULL, kHelp,
	"?", NULL, kHelp,
};
short   cmdPointer, baseCommand;
char    cmdBuffer[CMD_BUF_SIZE+2];
short   cmdArg[MXARGS];

short	gDesPosition;		/* destination position of buoyancy engine */
short	gActPosition;		/* actual position of buoyancy engine */
short	gDelta;				/* difference between actual and desired position */
short	gSign;				/* sign of gDelta */
short	gRunning;			/* TRUE if motor running */
short	gPWMHi;				/* PWM duty cycle */
BEFault	gFault;				/* buoyancy engine fault */
short	gCasts;				/* total number of casts */
short	gInterval;			/* number of minutes between casts */
int 	gTimeDly = 0;

const char wday[7][4] = {
	"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
};

const char month[12][4] = {
	"Jan", "Feb", "Mar", "Apr", "May", "Jun",
	"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
};

const short n_mdays[12] = {
	31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
};


/* Main event loop is here */
void main(void)
{
	Init();							/* set up hardware and program variables */

	printf("\n\nMVP Controller version %s\n", VERSION);

	while(TRUE) {                   /* loop forever */
		printf(PROMPT);
		kbflush();                  /* clear command buffer */
		GetLine(cmdBuffer, CMD_BUF_SIZE);	/* get next command */
		DoCommand();            	/* go do it */
	} /* end while */

} /* end main */


/* Initialize hardware and program variables */
void Init(void) {

	InitTT8(NO_WATCHDOG, TT8_TPU);	/* setup Model 8 for running C programs */
	
	SimSetFSys(AWAKEFREQ);			/* set CPU clock to 3MHz */
	SerSetBaud(9600, 0);			/*  and adjust baud rate */

	PConfOutp(E, 1);				/* config direction pin (Port E bit 1) as output */
	PConfOutp(E, 2);				/* config brake pin (Port E bit 2) as output */
	DisDCDrive();					/* disable the motor driver */
	PClear(E, 2);					/* apply motor brake */
	TPUSetPin(MTR_PWM, OFF);		/* config mtr enable pin (TPU bit 15) as output, and turn off */
	TPUSetupPWM(MTR_PWM, 0, PWM_PER, MiddlePrior); /* start pwm output at 0%*/

	InitPeriodicTimerInterrupt();	/* start periodic interrupt timer */ 
	SetPeriodicTimer(PITDLY);		/* set PITR delay value for clock tick */

	SetTickRate(100L);				/* set tick rate used by newsleep commands */
	
	InitSerPorts();					/* init TPU serial ports to talk to instruments */

} /* end function Init() */


/* Get a command line into the command buffer */
void GetLine(char s[], int lim)
{
	char c;
    int i;
    
    i = 0;
    while(--lim > 0 && (c = getchar()) && c != '\n') {
	    if(c == '\x08' || c == '\x10') {	/* if backspace or delete */
	    	if(i > 0) {				/* if not beginning of buffer */
            	i--;				/* throw previous char away */
			} else {
            	putchar('\x20');	/* print a space */
            } /* end if */
	        lim++;
		} else {
	    	s[i++] = c;				/* put char in buffer */
        } /* end if */
    } /* end while */
    s[i] = '\0';					/* mark end of line */
    printf("\n");					/* echo end of line */

} /* end function GetLine() */


/* Read a command from the command buffer and execute it */
void DoCommand(void)
{
	short   cmdNum;
	
	MakeWordUpper(cmdBuffer);
	cmdNum = ParseCommand();
	switch(cmdNum) {
		case kEmpty:
			break;

		case kUnknown:
			printf("Unknown command.");
			break;

		case kHelp:
			Help();
			break;

		case kSetEngPos:
			SetEngPos(cmdArg[0]);
            break;

		case kReadEngPos:
			ReadEngPos();
			break;
			
		case kReadVehDepth:
			ReadVehDepth();
			break;

		case kSetModeDeploy:
			SetModeDeploy();
			break;
			
		case kSetModeYoyo:
			SetModeYoyo(cmdArg[0], cmdArg[1], cmdArg[2]);
			break;

		case kSetModeTrans:
			TransparentSerial(cmdArg[0]);
			break;

		case kSetClkTime:
			SetClkTime();
			break;
			
		case kReadClkTime:
			ReadClkTime();
			break;
			
		case kTestInstrLog:
			TestInstrLog();
			break;
			
		case kExitToMon:
			ResetToMon();			/* Exit program */
			break;

		default:
			printf("Unrecognized command");
			
	} /* end switch */

} /* end function DoCommand */


/* Set the engine position, given a position on a 0-100 percent scale. */
void SetEngPos(short desPosition)
{
	short	pctPosition, curPosition;
	
	if(desPosition >= 0 && desPosition <= 100) {
		
		StartEngine(desPosition);			/* start the engine moving */
		curPosition = gActPosition;
		while(gRunning) {					/* wait until it stops */
			if(gActPosition > curPosition + ONE_PCT || gActPosition < curPosition - ONE_PCT) {
				curPosition = gActPosition;
				pctPosition = Cnt2Pct(gActPosition); /* convert count to percent */
				printf("Engine at %3d%\n", pctPosition);	/* print current position every one percent */
			} /* end if */
		} /* end while */
				
		pctPosition = Cnt2Pct(gActPosition); /* convert count to percent */
		if(gFault == kCurrentTrip) printf("OVERCURRENT TRIP at %d%\n", pctPosition);
		if(gFault == kOperatorAbort) printf("OPERATOR ABORT at %d%\n", pctPosition);
		if(gFault == kPotError) printf("POSITION POT ERROR at %d%\n", pctPosition);
	} else {
		printf("Position must be in range 0 to 100%%\n");
	} /* end if */

} /* end function SetEngPos() */


/* Start the engine moving to the desired set point.  This routine returns
	immediately, before the engine has reached the destination.
	The engine position (actPosition) is read, and the motor is stopped,
	in an interrupt service routine. */
void StartEngine(short desPosition)
{
	gDesPosition = desPosition;
	gFault = kNone;						/* clear any existing faults */
	gPWMHi = 0;
	TPUChangePWM(MTR_PWM, gPWMHi, PWM_PER); /* set PWM to start value */
	PSet(E, 2);							/* release brake */
	DelayMilliSecs(50L);				/* wait for brake to release */
	EnaDCDrive();						/* power up the motor driver */
	DelayMilliSecs(1L);					/* wait for motor power transient to die */
	gDesPosition = Pct2Cnt(gDesPosition);	/* convert percent to count */
	gDelta = gDesPosition - gActPosition;	/* diff is desired pos minus actual pos */
	gSign = gDelta / abs(gDelta);
	if(gDelta > 0) {					/* EXTEND buoyancy engine */
		PSet(E, 1);						/* set direction forward */
	} else {							/* RETRACT buoyancy engine */
		PClear(E, 1);					/* set direction reverse */
	} /* end if */

	gRunning = TRUE;					/* start the motor running */

} /* end function StartEngine() */



/* Read the engine position */
void ReadEngPos(void)
{
	short	potPosition;

	potPosition = Cnt2Pct(gActPosition); 	/* convert count to percent */
	printf("Engine position is %d%%\n", potPosition);
	
} /* end function ReadEngPos() */


/* Read the vehicle depth from the SBE 39 */
void ReadVehDepth(void)
{
	float depth;
	
	depth = ReadSBE39Depth();
//	if(depth > -1.0) {
		printf("Vehicle depth is %.3f meters\n", depth);
//	} else {
//		printf("SBE 39 not responding\n");
//	} /* end if */
} /* end function ReadVehDepth */

	
/* Convert engine position count to percent extension
	Engine lower position limit (LOLIM) is 0%, upper limit (UPLIM) is 100% */
short Cnt2Pct(short count)
{
	return(((long)count + HALF_PCT - LOLIM) * 100 / RANGE); /* convert count to percent */

} /* end function Cnt2Pct() */


/* Convert engine percent extension to position count
	Engine lower position limit (LOLIM) is 0%, upper limit (UPLIM) is 100% */
short Pct2Cnt(short percent)
{
	return((long)percent * RANGE / 100 + LOLIM);	/* convert percent to count */

} /* end function Pct2Cnt() */


/* Go into deployment mode.  This requires using the low-power sleep mode between casts.
	The LP sleep mode is sensitive to which interrupts are occurring, so the Periodic Interrupt
	Timer must be disabled before entering LP sleep.  LP sleep also screws up the TPU PWM
	output, so that must be restarted after wakeup */
void SetModeDeploy(void)
{
	short numCasts = 1,							/* number of casts completed in this set */
		  setCasts;								/* number of casts to perform in each set */
	short numSets = 1,							/* number of sets completed */
		  totalSets;							/* number of sets to perform */
	short castDly;								/* delay between casts in hours */
	int paramsOK = FALSE,						/* parameters valid flag */
		abort = FALSE,							/* abort flag */
		castCount;								/* counter to track how long BP has been running */
	float depth = 1000,							/* vehicle depth in meters */
		  loDepthLim = 30,						/* lower depth limit in meters */
		  upDepthLim = 10;						/* upper depth limit in meters */
	char buffer[64];							/* buffer for entered parameters */
	struct tm startTime;						/* date and time to begin casts */
	time_tt wakeTime = {0, 0};					/* date and time for next wakeup from sleep */


	// Get the deployment parameters
	while(paramsOK == FALSE) {
		if(!MyQueryDateTime("\nEnter date and time to begin casts (MM/DD/YY HH:MM:SS) ", &startTime)) {
			abort = TRUE;
			break;
		} else {
			wakeTime.secs = mktime(&startTime);		/* set first wakeup time */
		} /* end if */

		printf("Enter number of CASTS to perform in each set: ");
		fflush (stdout);
		TimedGetLine(buffer, (int) (sizeof(buffer)));
		sscanf(buffer, "%d", &setCasts);
				
		printf("Enter INTERVAL between start of casts in hours: ");
		fflush (stdout);
		TimedGetLine(buffer, (int) (sizeof(buffer)));
		sscanf(buffer, "%d", &castDly);
		
		printf("Enter number of SETS to perform in deployment: ");
		fflush (stdout);
		TimedGetLine(buffer, (int) (sizeof(buffer)));
		sscanf(buffer, "%d", &totalSets);
		
		printf("Enter LOWER depth limit in meters: ");
		fflush (stdout);
		TimedGetLine(buffer, (int) (sizeof(buffer)));
		sscanf(buffer, "%f", &loDepthLim);
		
		printf("Enter UPPER depth limit in meters: ");
		fflush (stdout);
		TimedGetLine(buffer, (int) (sizeof(buffer)));
		sscanf(buffer, "%f", &upDepthLim);
		

		printf("\n\nThe current time is ");
		ReadClkTime();
		printf("The first cast will begin on ");
		PrintTime(&startTime);
		printf("There will be %d casts in each set\n", setCasts);
		printf("There will be %d hours between the start of each cast\n", castDly);
		printf("There will be %d sets in the deployment\n", totalSets);
		printf("There will be a total of %d casts in the deployment\n", setCasts * totalSets);
		printf("The lower depth limit is %.0f meters\n", loDepthLim);
		printf("The upper depth limit is %.0f meters\n", upDepthLim);
		
		if(!QueryYesNo("Do you want to change any of these parameters", FALSE)) {
			paramsOK = TRUE;
		} /* end if */
	} /* end while */

	// Begin the deployment
	if(!abort && QueryYesNo("\nDo you want to deploy now ", FALSE)) {

		printf("\n\nDeployment started ");
		ReadClkTime();

#if 0	// Include this code to wait a few minutes for ballast adjustment
		printf("Setting engine to 50%% and pausing for %d minutes\n", DEPLOYDLY);
		StartEngine(50);
		while(gRunning);							/* wait until engine stops */
		DelayMilliSecs(DEPLOYDLY * 60 * 1000L);	// wait for a few minutes for ballast adjust, etc.
#endif

		printf("Setting engine to 0%% and waiting until time of first cast\n");
		StartEngine(0);
		while(gRunning);							/* wait until engine stops */

		while(numSets <= totalSets) {
			GoToSleep(wakeTime);
			printf("Starting set %d, cast %d\n", numSets, numCasts);
			printf("Setting engine to 100%%\n");
			StartEngine(100);
			castCount = 1;

			// Rise until lower depth achieved
			depth = ReadSBE39Depth();				/* read the depth sensor */
			while(depth > loDepthLim) {
				DelayMilliSecs(DEPTHDLY * 1000L);	/* pause until next depth reading */
				depth = ReadSBE39Depth();			/* keep reading the depth sensor */
				if(castCount > CASTTIMEOUT) {		/* check how long this cast is taking */
					break;							/* abort the cast */
				} else {
					castCount++;
				} /* end if */
			} /* end while */
			// Start the instruments on the way up
			printf("Rising and starting instruments at depth of %.3f meters\n", depth);
			StartMBBP();
			StartSBE19();

			// Continue rising until at upper depth
			while(depth > upDepthLim) {
				DelayMilliSecs(DEPTHDLY * 1000L);	/* pause until next depth reading */
				depth = ReadSBE39Depth();			/* read the depth sensor */
				if(castCount > CASTTIMEOUT) {		/* check how long this cast is taking */
					break;							/* abort the cast */
				} else {
					castCount++;
				} /* end if */
			} /* end while */
			if(castCount <= CASTTIMEOUT) {
				printf("Reached upper depth of %.3f meters\n", depth);
			} else {
				printf("Stuck at depth of %.3f meters\n", depth);
			} // end if

#if UPONLY
			// Stop the instruments here if we're only recording on upcasts
			printf("Stopping instruments at depth of %.3f meters\n", depth);
			StopMBBP();
			StopSBE19();
#endif

			printf("Setting engine to 0%%\n");
			StartEngine(0);

#if !UPONLY	// Include the following code only if instruments also run on downcast

			// Sink until below upper depth
			while(depth < upDepthLim) {
				DelayMilliSecs(DEPTHDLY * 1000L);	/* pause until next depth reading */
				depth = ReadSBE39Depth();			/* read the depth sensor */
				if(castCount > CASTTIMEOUT) {		/* check how long this cast is taking */
					break;							/* abort the cast */
				} else {
					castCount++;
				} /* end if */
			} /* end while */
			printf("Sinking at surface depth of %.3f meters\n", depth);

			// Continue sinking until below lower depth
			while(depth < loDepthLim) {
				DelayMilliSecs(DEPTHDLY * 1000L);	/* pause until next depth reading */
				depth = ReadSBE39Depth();			/* read the depth sensor */
				if(castCount > CASTTIMEOUT) {		/* check how long this cast is taking */
					break;							/* abort the cast */
				} else {
					castCount++;
				} /* end if */
			} /* end while */
			// Stop the instruments
			printf("Sinking and stopping instruments at depth of %.3f meters\n", depth);
			StopMBBP();
			StopSBE19();
#endif

			while(gRunning);						/* wait until engine stops */
			DelayMilliSecs(1000L);					/* DEBUG add delay to avoid crash after sleep */

			// Calculate next wakeup time
			if(numCasts++ < setCasts) {				/* we are still in this set */
				wakeTime.secs += (castDly * 3600);	/* wake up on the next cast start */
			} else {								/* set up for next set tomorrow */
				numCasts = 1;
				wakeTime.secs = mktime(&startTime) + numSets * SETDLY; /* wake up next day */
				numSets++;
			} /* end if */
			
		} /* end while */

		printf("Deployment finished, setting engine to 100%%\n");
		StartEngine(100);
		ReadClkTime();
	} else {
		printf("\n\nDeployment cancelled\n");
	} /* end if */
	
} /* end function SetModeDeploy() */


/* Go into yo-yo mode, cyling back and forth from given lower limit to upper limit */
void SetModeYoyo(short llim, short ulim, short pause)
{
	short	cycles = 0;							/* number of cycles completed */
	char buffer[64];							/* buffer for entered parameters */
	
	// check to see if user entered parameters and if not, get them
	if(llim == NO_VAL || ulim == NO_VAL || pause == NO_VAL) {
		printf("Enter LOWER engine position in percent: ");
		fflush (stdout);
		TimedGetLine(buffer, (int) (sizeof(buffer)));
		sscanf(buffer, "%d", &llim);

		printf("Enter UPPER engine position in percent: ");
		fflush (stdout);
		TimedGetLine(buffer, (int) (sizeof(buffer)));
		sscanf(buffer, "%d", &ulim);
		
		printf("Enter time to PAUSE in seconds: ");
		fflush (stdout);
		TimedGetLine(buffer, (int) (sizeof(buffer)));
		sscanf(buffer, "%d", &pause);
	} /* end if */

	if(llim >= 0 && llim <= 100 && ulim >= 1 && ulim <= 100 && pause >= 1 && pause <= 1440) {
		while(TRUE)	{							/* do until operator keyboard abort */
			SetEngPos(llim);					/* set engine to lower limit */
			if(gFault != kNone) break;
			DelayMilliSecs(pause * 1000L);		/* pause for given number of seconds */
			SetEngPos(ulim);					/* set engine to upper limit */
			if(gFault != kNone) break;
			printf("%d yo-yo cycles completed\n", ++cycles);
			DelayMilliSecs(pause * 1000L);		/* pause for given time */
		} /* end while */
	} else {
		printf("Lower limit must be in range 0 to 99%%\n");
		printf("Upper limit must be in range 1 to 100%%\n");
		printf("Pause must be in range 1 to 1440 seconds\n");
	} /* end if */
	
} /* end function SetModeYoyo() */


/* Talk transparently through the command port to one of the instruments until Control-x is entered */
void TransparentSerial(short instr)
{

	char inchar, outchar;
	short done = FALSE;

	if(instr < 0 || instr > 2) {
		printf("Instrument port must be\n\t0 for SBE 39\n\t1 for SBE 19\n\t2 for MBBP\n");
		done = TRUE;
	} else {
		printf("\n\nEntering transparent mode (use Control-x to exit)\n\n");
		PConfOutp(E,3);	PClear(E,3);		/* power up instrument RS232 drivers */
		DelayMilliSecs(50L);				/* wait for power supplies to come up */
	} /* end if */

#if 0										// for DEBUG only
	printf("> ");
	GetLine(string, 16);
	TSerInFlush(SBE19_IN);
	SndInstr(SBE19_OUT, string);
	RcvInstr(SBE19_IN, string, 5);
#endif

	while(!done) {
		if(SerByteAvail()) {
			outchar = SerGetByte();			/* get char from MVP console port */
			if(outchar == '\x18') {			/* if Control-x was entered, we're done */
				break;
			} /* end if */
			TSerPutByte((instr * 2) + 1, outchar); /* send char to instrument */
		} /* end if */
		
		if(TSerByteAvail(instr*2)) {
			inchar = TSerGetByte(instr * 2);	/* get char from instrument */
			SerPutByte(inchar);				/* send char to MVP port */
		} /* end if */
		
	} /* end while */

	PSet(E,3);								/* shut down instrument RS232 drivers */
	printf("\n\nExiting transparent mode\n\n");

} /* end function TransparentSerial() */


/* Set the real-time clock */
void SetClkTime(void)
{
	struct	tm dateTime;			/* date and time */
	
	if(!MyQueryDateTime("Enter date and time (MM/DD/YY HH:MM:SS) ", &dateTime)) {
		printf("Clock not set\n");
		return;
	} /* end if */
	SetTimeTM(&dateTime, NULL);
	printf("Clock set to ");
	PrintTime(&dateTime);

} /* end function SetClkTime() */


/* Read the real-time clock */
void ReadClkTime(void)
{
	struct tm* now;
	time_tt cTime;        

	cTime = ttmnow();				/* get the current time */
	now = localtime(&(cTime.secs)); /* convert it to a time structure */
	PrintTime(now);
		
} /* end function ReadClkTime() */


/* Print a time to the screen */
void PrintTime(struct tm* aTime)
{
	printf("%s %s %2d %d @ %02d:%02d:%02d\n",
		wday[aTime->tm_wday],
		month[aTime->tm_mon],
		aTime->tm_mday,
		aTime->tm_year + 1900,
		aTime->tm_hour,
		aTime->tm_min,
		aTime->tm_sec);
	fflush(stdout);
} /* end function PrintTime() */



/* Query the date and time from the user.
	Returns TRUE if date and time are okay, FALSE otherwise */
int MyQueryDateTime(char *prompt, struct tm* tm)
{
	int n, illegal;
	char buffer[64];
	
	printf("%s", prompt);
	fflush (stdout);
	
	TimedGetLine(buffer, (int) (sizeof(buffer)));
	
	//  we reached this point either because of a timeout or
	//  because the user hit a carriage return
	n = sscanf (buffer, "%d/%d/%d %d:%d:%d", &tm->tm_mon, &tm->tm_mday, &tm->tm_year,
												&tm->tm_hour, &tm->tm_min, &tm->tm_sec);
	if(n != 6) {
		printf ("Date not accepted - entry format is month/day/year hour:min:sec\n");
		return FALSE;
	} // end if
	//  correct month for zero-based reference used by computer
	tm->tm_mon--;

	//  allow multiple formats for entry of the year, but the force the stored year
	//  to be from 70 to 206 (corresponding to the years 1970 to 2106)
	if(tm->tm_year > 1970)
		tm->tm_year -= 1900;
	if((tm->tm_year < 70) && (tm->tm_year >= 0))
		tm->tm_year += 100;
		
	// verify that the entered date and time are legal
	illegal = FALSE;
	if((tm->tm_mon < 0) || (tm->tm_mon >= 12))
		illegal = TRUE;
	if((tm->tm_mday <= 0) || (tm->tm_mday > n_mdays[tm->tm_mon]))  {
		if(((tm->tm_year%4) != 0) || (tm->tm_mon != 1) || (tm->tm_mday != 29))
			illegal = TRUE;
	}       
	if((tm->tm_year < 70) || (tm->tm_year > 206))
		illegal = TRUE;
	if((tm->tm_hour < 0) || (tm->tm_hour > 23))
		illegal = TRUE;
	if((tm->tm_min < 0) || (tm->tm_min > 59))
		illegal = TRUE;
	if((tm->tm_sec < 0) || (tm->tm_sec > 59))
		illegal = TRUE;
	if(illegal) {
		printf ("Date not accepted - illegal date entered\n");
		return FALSE;
	}
	return TRUE;
} /* end function MyQueryDateTime() */

/* Query the date from the user.
	Returns TRUE if date is okay, FALSE otherwise */
int QueryDate(char* prompt, struct tm* tm)
{
	int n, illegal;
	char buffer[64];
	
	printf("%s (HH:MM) ", prompt);
	fflush (stdout);
	
	TimedGetLine(buffer, (int) (sizeof(buffer)));
	
	//  we reached this point either because of a timeout or
	//  because the user hit a carriage return
	n = sscanf (buffer, "%d/%d/%d", &tm->tm_mon, &tm->tm_mday, &tm->tm_year);
	if(n != 6) {
		printf ("Date not accepted - entry format is month/day/year\n");
		return FALSE;
	} // end if
	//  correct month for zero-based reference used by computer
	tm->tm_mon--;

	//  allow multiple formats for the year
	if(tm->tm_year > 1970)
		tm->tm_year -= 1900;
	if((tm->tm_year < 70) && (tm->tm_year >= 0))
		tm->tm_year += 100;
		
	// verify that the entered date is legal
	illegal = FALSE;
	if((tm->tm_mon < 0) || (tm->tm_mon >= 12))
		illegal = TRUE;
	if((tm->tm_mday <= 0) || (tm->tm_mday > n_mdays[tm->tm_mon]))  {
		if(((tm->tm_year%4) != 0) || (tm->tm_mon != 1) || (tm->tm_mday != 29))
			illegal = TRUE;
	}       
	if((tm->tm_year < 0) || (tm->tm_year > 206))
		illegal = TRUE;
	if(illegal) {
		printf ("Date not accepted - illegal date entered\n");
		return FALSE;
	}
	return TRUE;
} /* end function QueryDate() */


/* Get a time from the user and convert it to seconds into the day
	(e.g. 12:00 = 43200) */
int QueryTime(char* prompt, long* timeOfDay)
{

	int hours, mins;
	int n, illegal;
	char buffer[64];
	
	printf("%s (MM/DD/YY) ", prompt);
	fflush (stdout);
	
	TimedGetLine(buffer, (int) (sizeof(buffer)));
	
	//  we reached this point either because of a timeout or
	//  because the user hit a carriage return
	n = sscanf (buffer, "%d:%d", &hours, &mins);
	if(n != 2) {
		printf ("Time not accepted - entry format is hour:min\n");
		return FALSE;
	} // end if
		
	// verify that the entered time is legal
	illegal = FALSE;
	if((hours < 0) || (hours > 23))
		illegal = TRUE;
	if((mins < 0) || (mins > 59))
		illegal = TRUE;
	if(illegal) {
		printf ("Time not accepted - illegal time entered\n");
		return FALSE;
	}
	*timeOfDay = (hours * 3600) + (mins * 60);
	return TRUE;
} /* end function QueryTime() */
	

/* Test instrument logging by sending a start command to all instruments,
	waiting fifteen seconds, then sending a stop command. */
void TestInstrLog(void) {

	printf("Starting all instruments on ");
	ReadClkTime();

	StartMBBP();
	StartSBE19();
	
	DelayMilliSecs(15 * 1000L);	// wait for fifteen seconds
	
	StopMBBP();
	StopSBE19();

	printf("Stopping all instruments on ");
	ReadClkTime();

} /* end function BegInstrLog() */


/* Get a line from the user, time out after 30 seconds of inactivity */
int TimedGetLine(char* buffer, int length)
{

	int n;

	//  wait no more than 30 seconds between input characters before
	//  timing out
	gTimeDly = 300;							// set time delay to 30 secs
	n = 0;
	while(gTimeDly)  {
		if(SerByteAvail()) {				// if char available
			gTimeDly = 300;					// reset timer
			buffer[n] = SerGetByte();		// put it in buffer
			if(buffer[n] != BS)  {			// if not backspace
				putchar(buffer[n]);			//	put echo char in screen buffer
				if(buffer[n] == '\r')		// if carriage return
					break;					//	we're done
				if(n < (length - 1))		// if buffer isn't full
					n++;					//	point to next buffer position
				else
					break;  
			} else {						// char is a backspace
				if(n > 0) {					// if buffer not empty
					putchar (buffer[n]);	//	echo backspace
					buffer[n] = 0;			//	and delete last char
					n--;
				} /* end if */
			} /* end if */
			fflush (stdout);				// print echo char to screen 
		} /* end if */
	} /* end while */

	if(!gTimeDly) {
		printf("\nTimed out due to inactivity.\n");
		return FALSE;
	} /* end if */      

	buffer[n] = 0;							// terminate string
	putchar('\n');							// put carriage return in screen buffer
	fflush(stdout);							//	and print it
	return TRUE;
} /* end function TimedGetLine() */

/* Enable (power on) the DC Drive module */
void EnaDCDrive(void)
{

	PConfOutp(E, 0);				/* config Port E run/sense pin as output */
	PSet(E, 0);						/* set run/sense hi */
	DelayMilliSecs(2L);				/* delay for 2 ms to latch run/sense */
	PConfInp(E, 0);					/* config run/sense pin as input */

} /* end function EnaDCDrive() */


/* Disable (power off) the DC Drive module */
void DisDCDrive(void)
{

	PConfOutp(E, 0);				/* config Port E run/sense pin as output */
	PClear(E, 0);					/* set run/sense low */

} /* end function DisDCDrive() */


/* Soft-start the motor by ramping up the PWM signal each 10 ms until it reaches 100% */
void SoftStart(short *pwmHi)
{

	ulong now;
	static ulong then;

	now = TensMilliSecs();			/* get current time */
	if(now > then) {				/* have 10 ms passed? */
		*pwmHi += PWM_STEP;			/* increase PWM duty cycle */
		if(*pwmHi > PWM_PER) *pwmHi = PWM_PER; /* limit max value */
		TPUChangePWM(MTR_PWM, *pwmHi, PWM_PER); /* set new value */
		then = now;					/* save current time */
	} /* end if */

} /* end function SoftStart() */


/* Print the help message */
void Help(void)
{
	short	i;
	
	printf("Commands are:\n");
	for(i = 0; i < NUMCOMMANDS; i++) {
		printf(commands[i].theCommand);
		if(commands[i].theExplanation) {
			printf(commands[i].theExplanation);
		} /* end if */
		printf("\n");
	} /* end for */
	
} /* end function Help() */


/* Put the system to sleep in low-power mode */
void GoToSleep(time_tt wakeTime)
{
	short result;					/* reason for waking up */
	long clkFreq;					/* saved CPU clock freq */

	printf("Entering sleep mode on ");
	ReadClkTime();
	CloseSerPorts();				// turn off TPU serial ports
	PrepPinsLowPower();
	SetPeriodicTimer(0);			// stop PIT interrupts
	SerShutDown(TRUE, TRUE);		// turn off RS232 charge pump
	clkFreq = SimGetFSys();			// save current CPU clock freq
	SimSetFSys(SLEEPFREQ);			// slow down CPU clock
	result = SleepTill(wakeTime); 	// sleep until specified time
	// we are sleeping now...

	// we just woke up from sleep
	SimSetFSys(clkFreq);			// speed up CPU clock
	SerSetBaud(9600L, 0);			//  and restore baud rate
	SetPeriodicTimer(PITDLY);		// restart PIT interrupts
	TPUSetupPWM(MTR_PWM, 0, PWM_PER, MiddlePrior); /* restart pwm output at 0%*/
	InitSerPorts();					// open TPU serial ports to talk to instruments
	printf("wakeup result = %d\n", result);
	printf("Waking from sleep mode on ");
	ReadClkTime();

} /* end function GoToSleep() */


/* Read the A/D chan numSamps times and average the results */
short AtoDReadAvg(short chan, short numSamps)
{
	unsigned short i, sum = 0;

	if(numSamps > 16) numSamps = 16;		/* limit max samples to prevent math overflow */
	for(i = 0; i < numSamps; i++) {
		sum += AtoDReadWord(chan) / 8;		/* right-justify 12-bit result, and sum */
	} /* end for */
	return sum / numSamps;

} /* end function AtoDReadAvg() */


/* Parse the string in the command buffer */
short ParseCommand(void)
{
	short   i, j, cmdLen;
	char    *p;
	short   returnValue;
	
	returnValue = kUnknown;
	for(i = 0; i < MXARGS; i++) {
		cmdArg[i] = NO_VAL;				/* set to invalid value */
    } /* end for */

	if(!cmdBuffer[0]) {
		returnValue = kEmpty;            /* Empty buffer */
	} else {
		for(i = j = 0; i < NUMCOMMANDS; i++) {
			cmdLen = strlen(commands[i].theCommand);
			if(strncmp(cmdBuffer, commands[i].theCommand, cmdLen) == 0) {
				returnValue = commands[i].theNumber; /* read the command */
				p = cmdBuffer + cmdLen;
				while(*p && j < MXARGS) {
					while(*p && isspace(*p)) p++; /* skip spaces before arg */
					sscanf(p, "%d", &cmdArg[j++]); /* read the argument */
					while(*p && !isspace(*p)) p++; /* skip to next arg */
                } /* end while */
				break;
			} /* end if */
		} /* end for */
	} /* end if */

	return returnValue;

} /* end function ParseCommand */


/* Convert a string to upper case */
void MakeWordUpper(char *p)
{
	for( ; *p && !isspace(*p); p++) *p = toupper(*p);
} /* end function MakeWordUpper() */


/* Prepare TT8 pins for low-power operation.  This is copied from the newsleep.c module */
void PrepPinsLowPower(void)
{
/*
**	68332 PORT F (IRQ's OR I/O)
*/
	// the following two pins are used by interrupts in this program
	PConfInp(F,3);						/* irq3/led (has pullup) */
	PConfInp(F,1); 						/* rxd1 /IRQ1            */

	PConfOutp(F,7);	PClear(F,7);		/* A-D shutdown */
	PConfInp(F,6);						/* connected to, and pulled by TP15 */
	PConfInp(F,5);						/* picirq (PIC asserts)  */
	PInpToOut(F,4);						/* nc                    */
	PInpToOut(F,2);						/* nc                    */
	PConfInp(F,0);						/* sel5vpic (has pullup) */

/*
**	68332 PORT E (BUS SIGNALS OR I/O)
*/
	PInpToOut(E,7);						/* nc */
	PInpToOut(E,6);						/* nc */
	PInpToOut(E,5);						/* nc */
	PInpToOut(E,4);						/* nc */
	PConfOutp(E,3);	PSet(E,3);			/* shut down instrument RS232 drivers */
	PConfOutp(E,2);	PClear(E,2);		/* disable motor brake */
	PInpToOut(E,1);						/* nc */
	PConfOutp(E,0);	PClear(E,0);		/* disable DC Drive power */

/*
**	68332 PORT D (QSM OR I/O)
*/
	PConfBus(D,7);						/* txd RS-232 */
	PConfOutp(D,6);	PSet(D,6);			/* pcs3 a-d cs active low */
	PInpToOut(D,5);						/* pcs2/nc */
	PInpToOut(D,4);						/* pcs1/nc */
	PInpToOut(D,3);						/* pcs0/nc */
	PConfOutp(D,2);	PClear(D,2);		/* sck */
	PConfOutp(D,1);	PClear(D,2);		/* mosi */
	PInpToOut(D,0);						/* miso */

/*
**	68332 TPU PINS
*/
	TPUSetPin(0, 0);					/* nc */
	TPUSetPin(1, 0);					/* nc */
	TPUSetPin(2, 0);					/* nc */
	TPUSetPin(3, 0);					/* nc */
	TPUSetPin(4, 0);					/* nc */
	TPUSetPin(5, 0);					/* nc */
	TPUSetPin(6, 0);					/* nc */
	TPUSetPin(7, 0);					/* nc */
	TPUSetPin(8, 0);					/* nc */
	TPUSetPin(9, 0);					/* nc */
	TPUSetPin(10, 0);					/* nc */
	TPUSetPin(11, 0);					/* nc */
	TPUSetPin(12, 0);					/* nc */
	TPUSetPin(13, 1);					/* txd2 */
	TPUGetPin(14);						/* rxd2 (active in shutdown) */
	TPUSetPin(15, 0);					/* connected to F,6 */

} /* end function PrepPinsLowPower() */


/* Set up the Periodic Timer interrupt */
void InitPeriodicTimerInterrupt(void)
{ 
	static ExcCFrame	esf;

	*PITR = 0;	/* set count to zero - disable timer */
	InstallHandler(ServicePeriodicTimerInterrupt, PIT_INT_VECTOR, &esf);
} /* end function InitPeriodicTimerInterrupt() */


/* Set the Periodic Interrupt Timer delay
	delay is in range 0-255
	period = (delay * 512 * 4) / 40,000 for TT8
	min is 51.2 msec, max is 13.056 sec
	if delay = 0 then stopped
	bit 8 turns on divide-by-512 prescaler */
void SetPeriodicTimer(int delay)
{
	*PITR = delay | 0x0100;	/* set count and enable interrupt */
} /* end function SetPeriodicTimer() */


/*	Service the Periodic Timer interrupt*/
void ServicePeriodicTimerInterrupt(void)
{ 
	if(gTimeDly > 0) gTimeDly--;
	gActPosition = AtoDReadAvg(POT_POS, NUMSAMPS);	/* read pot position */

	if(gRunning) {						/* if motor is running */
		if(gPWMHi < PWM_PER) {			/* if not at full speed */
			gPWMHi += PWM_STEP;			/*  increase PWM duty cycle */
			if(gPWMHi > PWM_PER) gPWMHi = PWM_PER; /* limit max value */
			TPUChangePWM(MTR_PWM, gPWMHi, PWM_PER); /* set new value */
		} /* end if */
		
		if(!Pin(E, 0)) gFault = kCurrentTrip;	/* check for overcurrent trip */
		if(kbhit()) gFault = kOperatorAbort;	/* check for operator abort */
		if(gActPosition > UPLIM + FIVE_PCT || gActPosition < LOLIM - FIVE_PCT) {
			gFault = kPotError;			/* check for position pot error */
		} /* end if */

		if(gDelta * gSign < 0 || gFault) {	/* if destination reached or fault occurred */
			DisDCDrive();				/* power down the motor driver */
			PClear(E, 2);				/* apply brake */
			gRunning = FALSE;
		} else {
			gDelta = gDesPosition - gActPosition;	/* update position */
		} /* end if */
	} /* end if */

} /* end function ServicePeriodicTimerInterrupt() */
