/****************************************************************************/
/* Summary  : Command-line functions for BEDS Prototype on Persistor CF2    */
/* Filename : bedsCmd.c							    */
/* Author   : Robert Herlien (rah)					    */
/* Project  : BEDS (Benthic Event Detection System)			    */
/* Revision : 1.0							    */
/* Created  : 09/17/2012						    */
/****************************************************************************/
/* Modification History:						    */
/* 17sep2012 rah - created from Persistor "toPico" stationery and	    */
/*		   previous BEDS prototypes				    */
/* $Log$
 */
/****************************************************************************/

#include <mbariTypes.h>		/* MBARI type definitions		*/
#include <mbariConst.h>		/* MBARI constants			*/
#include <cfxpico.h>		// Persistor PicoDOS Definitions
#include <beds.h>		/* BEDS general definitions		*/
#include <bedsCmd.h>		/* BEDS Command line processor		*/
#include <file.h>		/* BEDS file I/O definitions		*/
#include <fileUtils.h>		/* BEDS file utilities definitions	*/
#include <variable.h>		/* Variable function definitions	*/
#include <syslog.h>		/* BEDS System logger			*/
#include <utils.h>		/* BEDS Utility routines		*/
#include <adc.h>		/* BEDS Analog Module			*/
#include <watch.h>		/* For sensorsOn()			*/
#include "ZModem/bedszm.h"	/* ZModem definitions			*/
#include "ekermit/bedskermit.h"	/* Kermit definitions			*/
#include <bedsym.h>		/* YModem definitions                   */

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <stddef.h>
#include <string.h>
#include <time.h>
#include <stdarg.h>
#include <errno.h>
#include <dirent.h>


/****************************************/
/*	External Data			*/
/****************************************/

Extern Int32	sensorWarmup, platformID;
Extern char	*timespecErr;
Extern char	*watchIdxName, *eventIdxName;


/****************************************/
/*	Global Data			*/
/****************************************/

Global char	*oneArg = "1", *twoArgs = "12", *threeArgs="123";
Global char	*fourArgs = "1234", *fiveArgs = "12345", *sixArgs="123456";


/****************************************/
/*	Module Local Data		*/
/****************************************/

MLocal char	*idxFileErr = "Cannot open index file";
MLocal char	*oFileErr   = "Cannot create output file";
MLocal char	*decFileErr = "Failure in splitting or decimating file";
MLocal char	*noFileErr  = "No file name";
MLocal Nat16	oFileIndex = 1;


/****************************************/
/*	Module Local Data		*/
/****************************************/

MLocal const char *exitMsg = "Exiting command parser";

MLocal char	*tmp1File = "INDEX1.TMP", *tmp2File = "INDEX2.TMP";


/************************************************************************/
/* Function    : argIndex						*/
/* Purpose     : Get index of first argv				*/
/* Inputs      : number of args, arg ptrs				*/
/* Outputs     : Index of first argv past all switches			*/
/* Comment	 Must first call CmdExtractCIDosSwitches, then call	*/
/*		 this function with identical parameters		*/
/************************************************************************/
int argIndex(int nargs, ...)
{
    va_list	ap;
    int		i, index = 1;
    DosSwitch	*swp;

    va_start(ap, nargs);

    for (i = 0; i < nargs; i++)
    {
	swp = va_arg(ap, DosSwitch *);
	if (swp->pos >= index)
	    index = swp->pos + 1;
    }

    va_end();
    return(index);
}


/************************************************************************/
/* Function    : QuitCmd						*/
/* Purpose     : Exit command line parser				*/
/* Inputs      : CmdInfoPtr						*/
/* Outputs     : NULL if OK, else error message				*/
/************************************************************************/
char *QuitCmd(CmdInfoPtr cip)
{
    DosSwitch	QMsw = { "/-", '?', 0, 0 };		// help

    CmdExtractCIDosSwitches(cip, oneArg, &QMsw);

// HELP SWITCH SELECTED (common format to many commands)
    if (QMsw.pos)
    {
	printf("\nQUIT\n\n"
		"Exits the command-line interface and goes back to normal processing\n\n");
	return(NULL);
    }
    
    printf("\n");
    sysLog(exitMsg);
    return(CmdStdBreak(cip));

} /* QuitCmd() */


/************************************************************************/
/* Function    : DeployCmd						*/
/* Purpose     : Go to deployment mode					*/
/* Inputs      : CmdInfoPtr						*/
/* Outputs     : NULL if OK, else error message				*/
/************************************************************************/
char	*DeployCmd(CmdInfoPtr cip)
{
    DosSwitch	QMsw = { "/-", '?', 0, 0 };		// help
    DosSwitch	qsw = { "/-", 'Q', 0, 0 };
    DosSwitch	nsw = { "/-", 'N', 0, 0 };

    CmdExtractCIDosSwitches(cip, threeArgs, &QMsw, &nsw, &qsw);

// HELP SWITCH SELECTED (common format to many commands)
    if (QMsw.pos)
    {
	printf("\n%s [/Q] [/N]\tGo to deployment mode\n"
	       "\t[/Q] Just query current mode\n"
	       "\t[/N] Turn off deployment mode\n\n", cip->argv[0].str);
	return(NULL);
    }

    if (nsw.pos)
	setDeploymentMode(FALSE);
    else if (!qsw.pos)
	setDeploymentMode(TRUE);
    
    printf("\nDeployment mode = %s\n", getDeploymentMode() ? "ON" : "OFF");
    return(NULL);
}


/************************************************************************/
/* Function    : resetConfirm						*/
/* Purpose     : Local function for user to confirm reset		*/
/* Inputs      : None                                                   */
/* Outputs     : TRUE if user confirm, else FALSE                       */
/************************************************************************/
MLocal MBool resetConfirm(void)
{
    cprintf("\nThis could cause permanent loss of communication.\n");
    if (!QRconfirm("Are you really sure", FALSE, TRUE))
	return(FALSE);

    printf("\n");
    sysLog("Resetting system by user request\n");
    fileClose();
    execstr("mount /d C:");
    CIOdrain();
    return(TRUE);
}

/************************************************************************/
/* Function    : BEDSResetCmd						*/
/* Purpose     : Reset the system					*/
/* Inputs      : CmdInfoPtr						*/
/* Outputs     : NULL if OK, else error message				*/
/************************************************************************/
char *BEDSResetCmd(CmdInfoPtr cip)
{
    DosSwitch	QMsw = { "/-", '?', 0, 0 };		// help
    DosSwitch	psw = { "/-", 'P', 0, 0 };		// help

    CmdExtractCIDosSwitches(cip, twoArgs, &QMsw, &psw);

// HELP SWITCH SELECTED (common format to many commands)
    if (QMsw.pos)
    {
	printf("\nRESET [/P]\n\n"
	       "Resets the CF2\n"
	       "/P Resets to PicoDOS\n");
	return(NULL);
    }

    if (resetConfirm())
    {
        if (psw.pos || (strncmp(cip->argv[1].str, "PICO", 4) == 0))
            BIOSResetToPicoDOS();
        BIOSReset();
    }

    return(NULL);

} /* BEDSResetCmd() */


/************************************************************************/
/* Function    : BEDSRecoverCmd						*/
/* Purpose     : Recover the system					*/
/* Inputs      : CmdInfoPtr						*/
/* Outputs     : NULL if OK, else error message				*/
/************************************************************************/
char *BEDSRecoverCmd(CmdInfoPtr cip)
{
    DosSwitch	QMsw = { "/-", '?', 0, 0 };		// help

    CmdExtractCIDosSwitches(cip, oneArg, &QMsw);

// HELP SWITCH SELECTED (common format to many commands)
    if (QMsw.pos)
    {
	printf("\nRecover the system.  Equivalent to \"RESET PICO\"\n\n");
	return(NULL);
    }

    if (resetConfirm())
        BIOSResetToPicoDOS();

    return(NULL);

} /* BEDSRecoverCmd() */


/************************************************************************/
/* Function    : SyslogCmd						*/
/* Purpose     : Command-line command to write to syslog		*/
/* Inputs      : CmdInfoPtr						*/
/* Outputs     : NULL if OK, else error message				*/
/************************************************************************/
char *SyslogCmd(CmdInfoPtr cip)
{
    DosSwitch	QMsw = { "/", '?', 0, 0 };		// help

    CmdExtractCIDosSwitches(cip, oneArg, &QMsw);

// HELP SWITCH SELECTED (common format to many commands)
    if (QMsw.pos)
    {
	printf("\nSYSLOG <message>\n\n"
	       "Writes <message> to SYSLOG.TXT\n\n");
	return(NULL);
    }
    
    printf("\n");
    sysLog(cip->text + strlen(cip->argv[0].str) + 1);
    return(NULL);

} /* SyslogCmd() */


/************************************************************************/
/* Function    : sendEvent						*/
/* Purpose     : Send a compressed event file via YModem		*/
/* Inputs      : Evt num, split factor, decimate factor, protocol       */
/* Outputs     : Errno							*/
/************************************************************************/
MLocal char *sendEvent(long eventNum, int split, int decimate,
                       SendProtocol protocol)
{
    int		result = 0;
    int		num;
    char	fname[32], suffix[8];
    static      char *protErr = "Protocol Error";

    getDataFileName(eventNum, "EVT", fname);

    if (((decimate > 1) || (split > 1)) &&
	(splitAndDecimate(fname, split, decimate) == OK))
    {
        for (num = 0; num < split; num++)
        {
	    sprintf(suffix, "E%02u", num);
	    getDataFileName(eventNum, suffix, fname);

            switch(protocol)
            {
              case KERMIT:
                result = kermitSendFiles(fname, 0, 0, 0);
                break;

              case YMODEM:
                result = ymSendFiles(fname);
                break;

              default:
                result = zmSendFiles(fname, 0, 1024, 0);
            }

            printf("\n%s: result = %d\n", fname, result);

            if (result == OK)
            {
                printf("Erasing %s\n", fname);
                unlink(fname);
            }
            else
                return(protErr);
        }
    }
    else
    {
        switch(protocol)
        {
          case KERMIT:
            result = kermitSendFiles(fname, 0, 0, 0);
            break;

          case YMODEM:
            result = ymSendFiles(fname);
            break;

          default:
            result = zmSendFiles(fname, 0, 1024, 0);
        }
    }

    return(result==0 ? NULL : protErr);

} /* sendEvent() */


/************************************************************************/
/* Function    : displayIndexEntry					*/
/* Purpose     : Display one index entry				*/
/* Inputs      : IdxRcd pointer						*/
/* Outputs     : None							*/
/************************************************************************/
MLocal void displayIndexEntry(IdxRcd *idxp)
{
    struct tm	*tmp;
    int		fileNum;
    Flt32	fmaxVal;

    tmp = gmtime(&idxp->startTime);
    fmaxVal = (float)(idxp->maxVal) / 65536.0f;
    idxp->fileName[15]='\0';
    if (sscanf(&idxp->fileName[3], "%ld", &fileNum) <= 0)
	fileNum = -1;

    printf("%4d  %04d/%02d/%02d %02d:%02d:%02d.%03hd %4ld.%03lu %7.4f  %s\n",
	   fileNum, tmp->tm_year+1900, tmp->tm_mon+1, tmp->tm_mday,
	   tmp->tm_hour, tmp->tm_min, tmp->tm_sec,
	   idxp->startMs, idxp->duration/1000, idxp->duration%1000,
	   fmaxVal, idxp->fileName);

} /* displayIndexEntry() */


/************************************************************************/
/* Function    : showIndexFunc						*/
/* Purpose     : Function for more() to show one index entry		*/
/* Inputs      : Line number, IndexStruct				*/
/* Outputs     : TRUE if found entry, else FALSE			*/
/************************************************************************/
MLocal MBool showIndexFunc(int lineNo, IndexStruct *ip)
{
    if (lineNo >= ip->entries)
	return(FALSE);

    displayIndexEntry(&ip->index[lineNo]);

    return(TRUE);

} /* showIndexFunc() */


/************************************************************************/
/* Function    : showIndexAltFunc					*/
/* Purpose     : Alternate function for more() to show one index entry	*/
/* Inputs      : Line number, FILE pointer				*/
/* Outputs     : TRUE if found entry, else FALSE			*/
/************************************************************************/
MLocal MBool showIndexAltFunc(int lineNo, FILE *fp)
{
    IdxRcd	idxRec;

    if (!getNextRec(fp, IndexType, &idxRec))
	return(FALSE);

    displayIndexEntry(&idxRec);

    return(TRUE);

} /* showIndexAltFunc() */


/************************************************************************/
/* Function    : showIndexEntries					*/
/* Purpose     : Display IndexRcd entries for .IDX file			*/
/* Inputs      : Index file name, compare func for ordering, num lines for more*/
/* Outputs     : NULL or error string					*/
/************************************************************************/
MLocal char *showIndexEntries(char *fname, MBool (*compareFunc)(), int numLines)
{
    IndexStruct *ip;
    FILE	*fp;

    if ((ip = createSortedIndex(fname, compareFunc)) == NULL)
    {
	if ((fp = fopen(fname, "r")) == NULL)
	    return(idxFileErr);
	printf("\nProblem opening or sorting index file.  Printing in unsorted time order.\n");
    }

    printf("\nEvent        Start Time        Duration  Max G  File Name\n");

    if (ip == NULL)
    {
	more(numLines, showIndexAltFunc, fp);
	fclose(fp);
    }
    else
    {
	more(numLines, showIndexFunc, ip);
	free(ip);
    }

    return(NULL);

} /* showIndexEntries() */


/************************************************************************/
/* Function    : EventCmd						*/
/* Purpose     : Command-line command to display and send events	*/
/* Inputs      : CmdInfoPtr						*/
/* Outputs     : NULL if OK, else error message				*/
/************************************************************************/
char *EventCmd(CmdInfoPtr cip)
{
    long	i, sends = 0;
    char	*result;
    int		n = 10, dec = 1, split = 1;
    SendProtocol protocol = ZMODEM;

    DosSwitch	qsw = { "/", '?', 0, 0 };		// help
    DosSwitch	nsw = { "/-", 'N', 0, "%ld", false };
    DosSwitch	dsw = { "/-", 'D', 0, "%ld", false };
    DosSwitch	ssw = { "/-", 'S', 0, "%ld", false };
    DosSwitch	tsw = { "/-", 'T', 0, 0 };
    DosSwitch	ksw = { "/-", 'K', 0, 0 };
    DosSwitch	ysw = { "/-", 'Y', 0, 0 };

    CmdExtractCIDosSwitches(cip, "1234567", &qsw, &nsw, &dsw, &ssw, &tsw, &ksw, &ysw);

    printf("\n");

// HELP SWITCH SELECTED (common format to many commands)
    if (qsw.pos)
    {
	printf("EVENT [/T] [/N<n>] to display events\n"
	       "\t[/T] display in reverse time order (defaults to magnitude order)\n"
	       "\t[/N<n>] display <n> lines at a time (default 10)\n"
	       "EVENT [/D<dec>] [/S<sp>] [n] to send event [n] via ZModem or Kermit\n"
	       "\t[/D<dec>] decimate value by factor <dec>\n"
	       "\t[/S<sp>]  split file <sp> ways (2-99)\n"
	       "\t[/K] to send via Kermit (dflt is ZModem)\n"
	       "\t[/Y] to send via YModem (dflt is ZModem)\n\n");
	return(NULL);
    }

    if (nsw.pos && nsw.hasv)
	n = (int)nsw.lv;

    if (dsw.pos && dsw.hasv)
	dec = (int)dsw.lv;

    if (ssw.pos && ssw.hasv)
    {
	split = (int)ssw.lv;
        if (split > 99)
            split = 99;
        if (split < 1)
            split = 1;
    }

    if (ksw.pos)
        protocol = KERMIT;
    else if (ysw.pos)
        protocol = YMODEM;

    for (i = 1; i < cip->argc; i++)
	if (cip->argv[i].isval)
	{
	    if ((result = sendEvent(cip->argv[i].value, split, dec, protocol)) != NULL)
		return(result);
	    sends++;
	}

    if (sends == 0)
	return(showIndexEntries(eventIdxName,
				tsw.pos ? trueFunc : magnitudeFunc, n));

    return(NULL);

} /* EventCmd() */


/************************************************************************/
/* Function    : WatchCmd						*/
/* Purpose     : Command-line command to display watch cycles		*/
/* Inputs      : CmdInfoPtr						*/
/* Outputs     : NULL if OK, else error message				*/
/************************************************************************/
char *WatchCmd(CmdInfoPtr cip)
{
    int		n = 10;

    DosSwitch	qsw = { "/", '?', 0, 0 };		// help
    DosSwitch	nsw = { "/-", 'N', 0, "%ld", false };

    CmdExtractCIDosSwitches(cip, twoArgs, &qsw, &nsw);

    if (qsw.pos)
    {
	printf("\nWATCH [/N<n>] to display watch cycles\n"
	       "\t[/N<n>] display <n> lines at a time (default 10)\n\n");
	return(NULL);
    }

    if (nsw.pos && nsw.hasv)
	n = nsw.lv;

    return(showIndexEntries(watchIdxName, trueFunc, n));

} /* WatchCmd() */


/************************************************************************/
/* Function    : DecimateCmd						*/
/* Purpose     : Command-line Decimate and split files			*/
/* Inputs      : CmdInfoPtr						*/
/* Outputs     : NULL if OK, else error message				*/
/************************************************************************/
char *DecimateCmd(CmdInfoPtr cip)
{
    long	dec=1, split=1;
    int		index;
    char	*rtn;

    DosSwitch	qsw = { "/", '?', 0, 0 };		// help
    DosSwitch	dsw = { "/-", 'D', 0, "%ld", false };
    DosSwitch	ssw = { "/-", 'S', 0, "%ld", false };

    CmdExtractCIDosSwitches(cip, threeArgs, &qsw, &dsw, &ssw);

    if (qsw.pos)
    {
	printf("\n%s [/D<dn>] [/S<sn>] <filename> Decimate and/or split file\n"
	       "\t[/D<dn>] to decimate file by factor of <dn>.\n"
	       "\t\tE.g., if <dn>=10, decimate 50Hz event to 5Hz\n"
	       "\t[/S<sn>] to split the file into <sn> parts\n"
	       "\t<filename> File to decimate or split.\n",
	       cip->argv[0].str);
	return(NULL);
    }

    if (dsw.pos && dsw.hasv)
	dec = dsw.lv;

    if (ssw.pos && ssw.hasv)
	split = ssw.lv;

    index = argIndex(3, &qsw, &dsw, &ssw);

    if (index >= cip->argc)
	return(noFileErr);

    printf("\nDecimate/split file %s, dec=%d, split=%d\n", cip->argv[index].str, dec, split);
    RTCDelayMicroSeconds(1000000);

    rtn = splitAndDecimate(cip->argv[index].str, split, dec);

    printf("done\n");
    return(rtn);

} /* DecimateCmd() */


/************************************************************************/
/* Function    : SysRecCmd						*/
/* Purpose     : Command-line command to display system records		*/
/* Inputs      : CmdInfoPtr						*/
/* Outputs     : NULL if OK, else error message				*/
/************************************************************************/
char	*SysRecCmd(CmdInfoPtr cip)
{
    time_t	startTime, now, nextSysRec;
    Int32	duration, interval;
    FILE	*ifp, *dfp, *ofp = NULL;
    int		recType;
    IdxRcd	idxRec;
    SystemRcd	sysRec;
    FileHdr	hdr;
    struct tm	*tmp;
    short	result;
    char	b[32], fname[32];

    DosSwitch	qsw = { "/-", '?', 0, 0 };		// help
    DosSwitch	ssw = { "/-", 'S', 0, "%s", false };
    DosSwitch	dsw = { "/-", 'D', 0, "%s", false };
    DosSwitch	tsw = { "/-", 'T', 0, "%s", false };
    DosSwitch	ysw = { "/-", 'Y', 0, 0, 0};

    CmdExtractCIDosSwitches(cip, fiveArgs, &qsw, &ssw, &dsw, &tsw, &ysw);

    printf("\n");
    if (qsw.pos)
    {
	printf("\n%s\tGet System Records\n"
	       "%s [/S <timespec>] [/D <n[s|m|h|d]>] [/Y]\n"
	       "\t [/S <timespec>] Starting time\n"
	       "\t [/D <n[s|m|h|d]>] Duration in seconds|minutes|hours|days\n"
	       "\t [/T <n[s|m|h|d]>] Display one sysrec every n seconds|minutes|hours|days\n"
	       "\t [/Y] Send as file via Ymodem\n",
	       cip->argv[0].str, cip->argv[0].str);
	return(NULL);
    }

    time(&now);

    if (!dsw.pos || ((duration = getDuration(dsw.sp)) <= 0))
	duration = 14400;

    if (!ssw.pos || ((startTime = getTimespec(ssw.sp)) <= 0))
	startTime = now - duration;

    nextSysRec = startTime;

    if (!tsw.pos || ((interval = getDuration(tsw.sp)) <= 0))
	interval = 1;

    if ((ifp = fopen(watchIdxName, "r")) == NULL)
	return(idxFileErr);

    if (ysw.pos)
    {
	sprintf(fname, "SR%04d.DAT", oFileIndex++);
	if ((ofp = fopen(fname, "w")) == NULL)
	    return(oFileErr);
	hdr.recType = FileHdrType;
	hdr.rsvd = 0;
	hdr.fmtVersion = FMT_VERSION;
	hdr.platformID = (Nat16)(platformID & 0xffff);
	hdr.dataRate = 0;
	hdr.startTime = startTime;
	hdr.startMs = 0;
	hdr.duration = duration;
	fwrite(&hdr, sizeof(hdr), 1, ofp);
    }
    else
    {
	tmp = gmtime(&startTime);
	printf("Returning sys recs starting %04d/%02d/%02d %02d:%02d:%02d duration %ld interval %ld\n",
	       tmp->tm_year+1900, tmp->tm_mon+1, tmp->tm_mday,
	       tmp->tm_hour, tmp->tm_min, tmp->tm_sec, duration, interval);
    }

    while (getNextRec(ifp, IndexType, &idxRec))
    {
	if ((idxRec.startTime <= (startTime + duration)) &&
	    ((idxRec.startTime + idxRec.duration/1000) >= nextSysRec))
	{				/* Got a watch file in correct time */
	    idxRec.fileName[15]='\0';
	    if ((dfp = fopen(idxRec.fileName, "r")) != NULL)
	    {
		while (getNextRec(dfp, SysRecType, &sysRec))
		{
		    if (sysRec.rcdTime > (startTime + duration))
			break;
		    if (sysRec.rcdTime >= nextSysRec)
		    {
			if (ysw.pos)
			    fwrite(&sysRec, sizeof(sysRec), 1, ofp);
			else
			{
			    tmp = gmtime(&sysRec.rcdTime);
			    printf("%02d/%02d/%04d %02d:%02d:%02d Batt %5.2fV "
				   "Vmodem %5.2fV ExtPress %6.3f bar IntPress %5.2f psia "
				   "Temp %4.1f degC Hum %4.1f%%\n",
				   tmp->tm_mon+1, tmp->tm_mday, tmp->tm_year+1900,
				   tmp->tm_hour, tmp->tm_min, tmp->tm_sec,
				   RAW_TO_BATTVOLTS(sysRec.battVoltage),
				   RAW_TO_BATTVOLTS(sysRec.minModemBatt),
				   adcEngValue(EXT_PRESS_CHAN, sysRec.extPressure, NULL),
				   adcEngValue(INT_PRESS_CHAN, sysRec.intPressure, NULL),
				   adcEngValue(TEMP_CHAN, sysRec.intTemp, NULL),
				   adcEngValue(HUMIDITY_CHAN, sysRec.intHumidity, NULL));
			}

			nextSysRec = sysRec.rcdTime + interval;
		    }
		    if (kbhit())
			break;
		}
		fclose(dfp);
	    }
	}

	if (kbhit())
	    break;
    }

    fclose(ifp);

    if (ysw.pos)
    {
	fclose(ofp);
	sprintf(b, "YS %s", fname);
	printf("\nSet your terminal program to receive YModem\n");
	CIOdrain();
	result = execstr(b);
	sysLogPrintf("Sent %s via YModem, result = %hd", fname, result);
	unlink(fname);
    }

    return(NULL);

} /* SysRecCmd() */


/************************************************************************/
/* Function    : AnalogCmd						*/
/* Purpose     : Command to read A/D channel(s)				*/
/* Inputs      : CmdInfoPtr						*/
/* Outputs     : NULL if OK, else error message				*/
/************************************************************************/
char *AnalogCmd(CmdInfoPtr cip)
{
    Nat16	index, i, val;
    Nat16	chan=0, nchans=1, navg=1;
    Flt32	engValue;
    char	*units;

    DosSwitch	qsw = { "/-", '?', 0, 0 };		// help
    DosSwitch	asw = { "/-", 'A', 0, "%ld", false };
    DosSwitch	nsw = { "/-", 'N', 0, "%ld", false };
    DosSwitch	psw = { "/-", 'P', 0, "%ld", false };

    CmdExtractCIDosSwitches(cip, fourArgs, &qsw, &asw, &nsw, &psw);
    index = argIndex(4, &qsw, &asw, &nsw, &psw);

    printf("\n");

// HELP SWITCH SELECTED (common format to many commands)
    if (qsw.pos)
    {
	printf("\nANALOG\tRead analog channel(s)\n"
	       "ANALOG [/A<n>] [/N<n>] [/P[n]] <chan>\n"
	       "\t [/A<n>] Average <n> samples\n"
	       "\t [/N<n>] Read <n> channels\n"
	       "\t [/P[n]] Wait <n> seconds after powering sensors\n");
	return(NULL);
    }

//    if ((index >= cip->argc) || !cip->argv[index].isval)
//	return("No analog channel specified");

    if ((index < cip->argc) && cip->argv[index].isval)
	chan = cip->argv[index].value;
    else
	nchans = 6;

    if (nsw.pos && nsw.hasv)
	nchans = nsw.lv;

    if (asw.pos && asw.hasv)
	if ((navg = asw.lv) > 1000)
	    navg = 1000;

    if ((chan + nchans) > NUM_ADC_CHANS)
	return("Analog channel(s) out of range");

    sensorsOn(SENSORS_SAMPLING);
    RTCDelayMicroSeconds(1000000* (psw.hasv ? psw.lv : sensorWarmup));

    for (i = chan; i < chan+nchans; i++)
    {
	val = adcSampleAvg(i, navg);
	engValue = adcEngValue(i, val, &units);
	printf("Channel %d = %hu counts %5.2f volts %6.3f %s\n",
	       i, val, RAW_TO_VOLTS(val), engValue, units);
    }

    sensorsOff(SENSORS_SAMPLING);

    return(NULL);

} /* AnalogCmd() */


/************************************************************************/
/* Function    : ZSCmd							*/
/* Purpose     : ZModem Send with Wildcards Command			*/
/* Inputs      : CmdInfoPtr						*/
/* Outputs     : NULL if OK, else error message				*/
/************************************************************************/
char *ZSCmd(CmdInfoPtr cip)
{
    int		index, rtn, verbose=0;
    unsigned	window=1024, framelen=0;

    DosSwitch	qsw = { "/-", '?', 0, 0 };
    DosSwitch	vsw = { "/-", 'V', 0, "%ld", false };
    DosSwitch	wsw = { "/-", 'W', 0, "%lu", false };
    DosSwitch	lsw = { "/-", 'L', 0, "%lu", false };

    CmdExtractCIDosSwitches(cip, fourArgs, &qsw, &vsw, &wsw, &lsw);

    printf("\n");

// HELP SWITCH SELECTED (common format to many commands)
    if (qsw.pos)
    {
	printf("%s [/V<n>] [/W<n>] [/L<n>] - Send file via ZModem protocol\n"
	       "\t [/V<n>] Verbosity level (0-8)\n"
	       "\t [/W<n>] Window size (>= 256)\n"
	       "\t [/L<n>] Frame length (32<=n<=1024)\n",
	       cip->argv[0].str);
	return(NULL);
    }

    if (cip->argc < 2)
	return("No file specified");

    if (vsw.pos && vsw.hasv)
	verbose = vsw.lv;

    if (wsw.pos && wsw.hasv)
	window = wsw.lv;

    if (lsw.pos && lsw.hasv)
	framelen = lsw.lv;

    for(index = argIndex(4, &qsw, &vsw, &wsw, &lsw); index < cip->argc; index++)
	if ((rtn = zmSendFiles(cip->argv[index].str, verbose, window, framelen)) != OK)
	    break;

    return((rtn == OK) ? NULL : "ZModem error");
			      
} /* ZSCmd() */


/************************************************************************/
/* Function    : KSCmd							*/
/* Purpose     : Kermit Send with Wildcards Command			*/
/* Inputs      : CmdInfoPtr						*/
/* Outputs     : NULL if OK, else error message				*/
/************************************************************************/
char *KSCmd(CmdInfoPtr cip)
{
    int		index, rtn;
    int		xfermode=0, errorrate=0, dbg=0;

    DosSwitch	qsw = { "/-", '?', 0, 0 };
    DosSwitch	bsw = { "/-", 'B', 0, 0, 0};
    DosSwitch	tsw = { "/-", 'T', 0, 0, 0};
    DosSwitch	esw = { "/-", 'E', 0, "%ld", false };
    DosSwitch	dsw = { "/-", 'D', 0, 0, 0};

    CmdExtractCIDosSwitches(cip, fiveArgs, &qsw, &bsw, &tsw, &esw, &dsw);

    printf("\n");

// HELP SWITCH SELECTED (common format to many commands)
    if (qsw.pos)
    {
	printf("%s [/B] [/T] [/D] [/E<n>] - Send file via Kermit protocol\n"
	       "\t [/B] Force binary transfer mode\n"
	       "\t [/T] Force text (ASCII) transfer mode\n"
	       "\t [/E<n>] Simulated error rate (0-100)\n"
	       "\t [/D] Turn on debugging\n",
	       cip->argv[0].str);
	return(NULL);
    }

    if (cip->argc < 2)
	return("No file specified");

    if (bsw.pos)
	xfermode = 1;
    
    if (tsw.pos)
	xfermode = 2;
    
    if (dsw.pos)
	dbg = 1;
    
    if (esw.pos && esw.hasv)
	errorrate = esw.lv;
    
    for(index = argIndex(4, &qsw, &bsw, &tsw, &esw, &dsw); index < cip->argc; index++)
	if ((rtn = kermitSendFiles(cip->argv[index].str, xfermode, errorrate, dbg)) != OK)
	    break;

    return((rtn == OK) ? NULL : "Kermit error");
			      
} /* KSCmd() */

#ifdef DEBUG_TIME

/************************************************************************/
/* Function    : TimeTestCmd						*/
/* Purpose     : Command-line command to test time parsing		*/
/* Inputs      : CmdInfoPtr						*/
/* Outputs     : NULL if OK, else error message				*/
/************************************************************************/
char *TimeTestCmd(CmdInfoPtr cip)
{
    time_t	t;
    struct tm	*tmp;
    char	*s;

    if (cip->argc < 2)
	printf("\nTest by passing strings of the form mm/dd/yyyyThh:mm:ss\n");
    else
    {
	s = cip->text + strlen(cip->argv[0].str);
	printf("\ngetTimespec(\"%s\") returns ", s);
	if ((t = getTimespec(s)) == 0)
	    printf("%s\n", timespecErr);
	else if ((tmp = gmtime(&t)) == NULL)
	    printf("%ld, but gmtime() returns ERROR\n", t);
	else
	    printf("%ld = %ld/%ld/%ld %02ld:%02ld:%02ld\n",
		   t, tmp->tm_mon + 1, tmp->tm_mday, tmp->tm_year+1900,
		   tmp->tm_hour, tmp->tm_min, tmp->tm_sec);
    }

    return(NULL);

} /* TimeTestCmd() */


/************************************************************************/
/* Function    : DurTestCmd						*/
/* Purpose     : Command-line command to test time parsing		*/
/* Inputs      : CmdInfoPtr						*/
/* Outputs     : NULL if OK, else error message				*/
/************************************************************************/
char *DurTestCmd(CmdInfoPtr cip)
{
    char	*s;

    if (cip->argc < 2)
	printf("\nusage: DurTest <n>\n");
    {
	s = cip->text + strlen(cip->argv[0].str);
	printf("\n\ngetDuration(\"%s\") returns %ld\n",
	       s, getDuration(s));
    }

    return(NULL);

} /* DurTestCmd() */

#endif /* DEBUG_TIME */
