/****************************************************************************/
/* Copyright 2003-2012 MBARI						    */
/****************************************************************************/
/* Summary  : Functions for Acoustic Modem for BEDS on Persistor CF2	    */
/* Filename : modem.c                                                       */
/* Author   : Robert Herlien (rah)					    */
/* Project  : BEDS (Benthic Event Detection System)			    */	
/* Revision : 1.0							    */
/* Created  : 12/07/2012						    */
/*									    */
/* 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:						    */
/* 07dec2012 rah - created						    */
/****************************************************************************/

#include <mbariTypes.h>		/* MBARI type definitions		*/
#include <mbariConst.h>		/* MBARI constants			*/
#include <cfxpico.h>		/* Persistor PicoDOS Definitions	*/
#include <beds.h>		/* BEDS general definitions		*/	
#include <io.h>			/* BEDS I/O definitions			*/
#include <modem.h>		/* BEDS acoustic modem software		*/
#include <serial.h>		/* BEDS serial I/O definitions		*/
#include <adc.h>		/* BEDS Analog Module			*/
#include <watch.h>		/* BEDS watch cycle			*/
#include <syslog.h>		/* System logger			*/
#include <utils.h>		/* BEDS Utility routines		*/
#include <bedsCmd.h>		/* BEDS user functions			*/

#include <stdio.h>
#include <string.h>
#include <time.h>


#define MODEM_CHECK_TICKS	(5 * TICKS_PER_SECOND)


/********************************/
/*	External Data		*/
/********************************/

Extern Int32	modemBaud;		/* Baud rate on SCI port	    */
Extern Int32	modemWarmup;		/* Warmup time for turning on ac. modem*/
Extern Int32	modemAddr;		/* Address of acoustic modem	    */
Extern Int32	modemTxPwr;		/* S6 setting for modem (TxPower)   */
Extern Nat16	modemMinBatt;		/* Minimum batt voltage seen since last SysRec*/
Extern MBool	initModem;		/* Send init strings to modem at startup*/


/********************************/
/*	Module Local Data	*/
/********************************/

MLocal Nat32	modemBattCheckTick = 0;
MLocal MBool	modemOnline = TRUE;

MLocal char	*modemInitStr[] =
{"ATS2=0\r", "ATS4=5\r", "ATS8=10\r", "ATS11=0\r", "ATS13=0\r", "AT&W\r"};

#ifdef RECIPE_PROTO	//Only if debugging using the programmable power supply
MLocal TUPort	*ctrlPort = NULL;
MLocal TUChParams ctrlParms =
    {8, 'N', true, 9600L, tpuAutoPriority, tpuAutoPriority, 128, 128, 256};
#endif

/************************************************************************/
/* Function    : waitForPrompt						*/
/* Purpose     : Wait for modem prompt ('>')				*/
/* Inputs      : None							*/
/* Outputs     : TRUE if got prompt					*/
/* Comment     : Assumes modem is in command mode, leaves it there	*/
/************************************************************************/
MLocal MBool waitForPrompt(Void)
{
    int		i;

    for (i = 0; i < 50; i++)
    {
	if (kbhit())
	    if (cgetc() == '>')
		return(TRUE);
	RTCDelayMicroSeconds(20000);
    }

    return(FALSE);

} /* waitForPrompt() */


/************************************************************************/
/* Function    : modemCommandMode					*/
/* Purpose     : Put modem into command mode				*/
/* Inputs      : None							*/
/* Outputs     : TRUE if got into command mode				*/
/************************************************************************/
MBool modemCommandMode(void)
{
    int		i;

    for (i = 0; i < 3; i++)
    {
	RTCDelayMicroSeconds(1000000);
	modemiFlush();
	CIOputs("+++");
	if (waitForPrompt())
	{
	    modemOnline = FALSE;
	    return(TRUE);
	}
    }

    return(FALSE);

} /* modemCommandMode() */


/************************************************************************/
/* Function    : getPrompt						*/
/* Purpose     : Send CR, wait for prompt ('>')				*/
/* Inputs      : None							*/
/* Outputs     : TRUE if got prompt					*/
/* Comment     : Assumes modem is in command mode, leaves it there	*/
/************************************************************************/
MLocal MBool getPrompt(Void)
{
    int		i;

    for (i = 0; i < 5; i++)
    {
	RTCDelayMicroSeconds(1000000);
	modemiFlush();
	CIOputc('\r');
	if (waitForPrompt())
	    return(TRUE);
    }

    return(FALSE);
}


/************************************************************************/
/* Function    : modemOnlineMode					*/
/* Purpose     : Put modem into online mode				*/
/* Inputs      : None							*/
/* Outputs     : None							*/
/************************************************************************/
void modemOnlineMode(void)
{
    int		i;

    if (getPrompt())
    {
	CIOprintf("ATO\r");
	RTCDelayMicroSeconds(500000);
	modemiFlush();
	modemOnline = TRUE;
    }

} /* modemOnlineMode() */


/************************************************************************/
/* Function    : modemInit						*/
/* Purpose     : Initialize modem module				*/
/* Inputs      : None							*/
/* Outputs     : None							*/
/************************************************************************/
Void modemInit(Void)
{
    int		i;
    char	buf[64];

#ifdef RECIPE_PROTO
    altSerEnable(TRUE);
    ctrlPort = TUOpen(10, 11, 9600L, &ctrlParms);
    RTCDelayMicroSeconds(50000);
    tprintf(ctrlPort, "SYST:REM\n\r");
    RTCDelayMicroSeconds(50000);
    tprintf(ctrlPort, "APPL 25.0, 4.0\n\r");
    RTCDelayMicroSeconds(50000);
#endif

    if (initModem)
    {		//Probably don't need to init registers every time
        	//But following was tested and can be enabled any time
	modemOn();
#ifndef MODEM_ON_TPU			//Normal case
	setConsole(SER_CONSOLE, TRUE);
	setConsoleBaud(modemBaud);
#else
	setConsole(SER_TPU, TRUE);
	setTpuBaud(modemBaud);
#endif

	for (i = 0; i < modemWarmup; i++)
	{
	    RTCDelayMicroSeconds(1000000);
#ifdef WATCHDOG
	    TickleSWSR();		/* Keep watchdog alive	*/
#endif
	}

	if (modemCommandMode())
	{
	    sprintf(buf, "ATS18=%u\r", modemAddr);
	    cputs(buf);
	    waitForPrompt();
	    sprintf(buf, "ATS6=%u\r", modemTxPwr);
	    cputs(buf);

	    for (i = 0; i < NumberOf(modemInitStr); i++)
	    {
		waitForPrompt();
		cputs(modemInitStr[i]);
	    }
	}

	setConsole(SER_CONSOLE, TRUE);
    }	//End of register init

    modemOff();		// Leave modem on for now

} /* modemInit() */


/************************************************************************/
/* Function    : isModemPort						*/
/* Purpose     : Return TRUE if we're talking to the modem serial port	*/
/* Inputs      : None							*/
/* Outputs     : TRUE if on modem port, else FALSE			*/
/************************************************************************/
MBool isModemPort(Void)
{
#ifndef MODEM_ON_TPU
    return(!consoleIsSwitched());
#else
    return(consoleIsSwitched());
#endif
}


/************************************************************************/
/* Function    : modemConnectCheck					*/
/* Purpose     : Check for modem connection				*/
/* Inputs      : None							*/
/* Outputs     : TRUE if found modem connection, else FALSE		*/
/************************************************************************/
MBool modemConnectCheck(Void)
{
#ifndef MODEM_ON_TPU
    return(kbhit());
#else
    return(TIOgetq() > 0);
#endif
}


/************************************************************************/
/* Function    : modemOn						*/
/* Purpose     : Turn Modem On						*/
/* Inputs      : None							*/
/* Outputs     : None							*/
/************************************************************************/
Void modemOn(Void)
{
    sysLog("Turning on modem");

#ifndef RECIPE_PROTO
    PIOSet(MODEM_PWR_EN);
#else
    altSerEnable(TRUE);
    tprintf(ctrlPort, "OUTP ON\n\r");
    tdrain(ctrlPort);
#endif

}


/************************************************************************/
/* Function    : modemOff						*/
/* Purpose     : Turn Modem Off						*/
/* Inputs      : None							*/
/* Outputs     : None							*/
/************************************************************************/
Void modemOff(Void)
{
    sysLog("Turning off modem");
#ifndef RECIPE_PROTO
    PIOClear(MODEM_PWR_EN);
#else
    altSerEnable(TRUE);
    tprintf(ctrlPort, "OUTP OFF\n\r");
    tdrain(ctrlPort);
#endif

}


/************************************************************************/
/* Function    : checkModemBatt						*/
/* Purpose     : Function called periodically from UI to monitor modem batt*/
/* Inputs      : None							*/
/* Outputs     : None							*/
/************************************************************************/
Void checkModemBatt(Void)
{
    Nat16	modemBatt;

    if ((ioGetTick() - modemBattCheckTick) >= MODEM_CHECK_TICKS)
    {
	modemBattCheckTick = ioGetTick();

	if ((modemBatt = adcSample(MODEM_MIN_CHAN)) < modemMinBatt)
	    modemMinBatt = modemBatt;
    }
}


/************************************************************************/
/* Function    : modemiFlush						*/
/* Purpose     : Flush modem input stream				*/
/* Inputs      : None							*/
/* Outputs     : None							*/
/************************************************************************/
Void modemiFlush(Void)
{
    int	i;

    CIOiflush();
    for (i = 0; (SCIRxGetCharWithTimeout(100) >= 0) && (i < 300); i++)
	;
}


/************************************************************************/
/* Function    : auxPortInCheck						*/
/* Purpose     : Check aux serial port for input traffic		*/
/* Inputs      : None							*/
/* Outputs     : TRUE if aux port has char available			*/
/************************************************************************/
MBool auxPortInCheck()
{
#ifndef MODEM_ON_TPU
    return(TIOgetq() > 0);
#else
    return(kbhit());
#endif
}


/************************************************************************/
/* Function    : ModemCmd						*/
/* Purpose     : Command-line function to send command to modem, get reply*/
/* Inputs      : CmdInfoPtr						*/
/* Outputs     : NULL if OK, else error message				*/
/************************************************************************/
char *ModemCmd(CmdInfoPtr cip)
{
    char	*s;
    Nat16	i, len;
    time_t	now, endTime;
    char	buf[512];

    DosSwitch	QMsw = { "/", '?', 0, 0 };		// help
    DosSwitch	osw = { "/-", 'O', 0, 0 };

    CmdExtractCIDosSwitches(cip, "12", &QMsw, &osw);

// HELP SWITCH SELECTED (common format to many commands)
    if (QMsw.pos)
    {
	CIOprintf("\nMODEM [/O] <string>\n\n"
	       "Send <string> to acoustic modem\n"
               "\t[/O]Turn on modem (if executed from debug port)\n\n");
	return(NULL);
    }
    
    if (osw.pos)
    {
        forceModemCycle();
        return(NULL);
    }

    if (cip->argc < 2)
	return("No command found");

    RTCDelayMicroSeconds(1000000);

    if (!modemCommandMode())
	return("Cannot enter modem command mode");

    s = cip->text + strlen(cip->argv[0].str);

    CIOprintf("%s\r", s);

    time(&now);
    endTime = now + 8;
    len = 0;

    while (time(&now) < endTime)
	if (CIOgetq() > 0)
	    buf[len++] = CIOgetc();

    modemOnlineMode();

    printf("\nModem returns:\n");
    for (i = 0; i < len; i++)
	CIOputc(buf[i]);

    printf("\n");

    return(NULL);

} /* ModemCmd() */


/************************************************************************/
/* Function    : ModemConnectCmd					*/
/* Purpose     : Process CONNECT message from modem			*/
/* Inputs      : CmdInfoPtr						*/
/* Outputs     : NULL if OK, else error message				*/
/************************************************************************/
char *ModemConnectCmd(CmdInfoPtr cip)
{
    removeNewline(cip->text);

#ifdef DEBUG_MODEM
    sysLogPrintf("Got \"%s\" from modem\n", cip->text);
#endif

    return(NULL);

} /* ModemConnectCmd() */


/************************************************************************/
/* Function    : ModemDisconnectCmd					*/
/* Purpose     : Process Off or Lowpower command from modem		*/
/* Inputs      : CmdInfoPtr						*/
/* Outputs     : NULL if OK, else error message				*/
/************************************************************************/
char *ModemDisconnectCmd(CmdInfoPtr cip)
{
    removeNewline(cip->text);

    sysLogPrintf("Exiting command parser due to \"%s\" from modem\n", cip->text);
    
    return(CmdStdBreak(cip));

} /* ModemDisconnectCmd() */


/************************************************************************/
/* Function    : ModemOfflineCmd					*/
/* Purpose     : Dispatched when userif sees "USER:", indicating modem is offline*/
/* Inputs      : CmdInfoPtr						*/
/* Outputs     : NULL							*/
/************************************************************************/
char *ModemOfflineCmd(CmdInfoPtr cip)
{
    removeNewline(cip->text);

    sysLogPrintf("Got \"%s\" from modem, sending ATO\n", cip->text);

    modemOnlineMode();

    return(NULL);

} /* ModemOfflineCmd() */


/************************************************************************/
/* Function    : getSReg						*/
/* Purpose     : Get the value of an S Register from acoustic modem	*/
/* Inputs      : S Register number					*/
/* Outputs     : Numeric value, or -1 if not found			*/
/* Comments    : Assumes we're already in command mode.			*/
/*		 Leaves modem in command mode, at prompt		*/
/************************************************************************/
int getSReg(int reg)
{
    int		val;
    short	c;
    char	*p;
    char	buf[128];

    CIOprintf("ATS%lu?\r", (long)reg);		//CIOprintf expects 2 byte ints

    for (p = buf, c = 1; c >= 0; p++)
        *p = c = SCIRxGetCharWithTimeout(500);

    *p = '\0';

    if (sscanf(buf, " %d", &val) < 1)
	return(-1);

    return(val);

} /* getSReg() */


/************************************************************************/
/* Function    : getRemoteSReg						*/
/* Purpose     : Get the value of a remote S register			*/
/* Inputs      : Remote modem number, S Register number			*/
/* Outputs     : Numeric value, or -1 if not found			*/
/* Comments    : Assumes we're already in command mode.			*/
/*		 Leaves modem in command mode, at prompt		*/
/************************************************************************/
int getRemoteSReg(int modem, int reg)
{
    int		val;
    short	c;
    char	*p;
    char	buf[128];

    CIOprintf("ATEC%lu:ATS%lu?\r", (long)modem, (long)reg);
    RTCDelayMicroSeconds(5000000);

    for (p = buf, c = 1; c >= 0; p++)
        *p = c = SCIRxGetCharWithTimeout(500);

    *p = '\0';

    if (sscanf(buf, " %d", &val) < 1)
    {
	sysLogPrintf("Failure to get remote S%lu from modem %lu\n",
		     (long)reg, (long)modem);
	return(-1);
    }

    sysLogPrintf("Remote S%lu = %lu\n", (long)reg, (long)val);
    return(val);

} /* getRemoteSReg() */


/************************************************************************/
/* Function    : setRemoteSReg						*/
/* Purpose     : Set the value of a remote S register			*/
/* Inputs      : Remote modem number, S Register number, value to set	*/
/* Outputs     : None							*/
/* Comments    : Assumes we're already in command mode.			*/
/*		 Leaves modem in command mode, at prompt		*/
/************************************************************************/
void setRemoteSReg(int modem, int reg, int val)
{
    CIOprintf("ATEC%lu:ATS%lu=%lu\r", (long)modem, (long)reg, (long)val);
    getPrompt();

} /* setRemoteSReg() */


/************************************************************************/
/* Function    : SRegCmd						*/
/* Purpose     : Command-line command to turn on/off modem		*/
/* Inputs      : CmdInfoPtr						*/
/* Outputs     : NULL if OK, else error message				*/
/************************************************************************/
char	*SRegCmd(CmdInfoPtr cip)
{
    int	        modem, reg, val, index;

    DosSwitch	rsw = { "/-", 'R', 0, "%ld", false };

    CmdExtractCIDosSwitches(cip, "1", &rsw);
    index = argIndex(1, &rsw);

    cprintf("\n");

    reg = cip->argv[index].value;

    RTCDelayMicroSeconds(500000);
    if (!modemCommandMode())
	return("Can't get modem attention");

    if (rsw.pos && rsw.hasv)
	val = getRemoteSReg(rsw.lv, reg);
    else
	val = getSReg(reg);

    modemOnlineMode();

    if (val != -1)
    {
	printf("S Register %u = %d\n", reg, val);
	return(NULL);
    }

    return("Can't get S-Register value\n");

} /* SRegCmd() */

