/****************************************************************************/
/* Copyright 2003-2018 MBARI												*/
/****************************************************************************/
/* Summary	: Functions for Acoustic Modem for BEDS2 on PIC32MX				*/
/* 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													*/
/* 28feb2018 rah - Ported to BEDS2 on PIC32MX470							*/
/****************************************************************************/

#include <mbariTypes.h>			/* MBARI type definitions				*/
#include <beds.h>				/* BEDS general definitions				*/		
#include <dig_io.h>				/* BEDS digital I/O definitions			*/
#include <modem.h>				/* BEDS acoustic modem software			*/
#include <clock.h>				/* BEDS Clock module					*/
#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 <stdio.h>
#include <string.h>
#include <time.h>

#define MODEM_CHECK_TICKS		(5 * TICKS_PER_SECOND)


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

Extern Int32	modemPort;				/* Serial port for  acoustic modem	*/
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*/
Extern MBool	wdtOn;					/* Use watchdog timer			*/


/********************************/
/*		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"};


/************************************************************************/
/* 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 (getchar() == '>')
				return(TRUE);
		clkDelayUs(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++)
	{
		clkDelayUs(1000000);
		serRxFlush(getConsole());
		printf("+++");
		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++)
	{
		clkDelayUs(1000000);
		serRxFlush(getConsole());
		putchar('\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())
	{
		printf("ATO\r");
		clkDelayUs(500000);
		serRxFlush(getConsole());
		modemOnline = TRUE;
	}

} /* modemOnlineMode() */


/************************************************************************/
/* Function	   : modemInit												*/
/* Purpose	   : Initialize modem module								*/
/* Inputs	   : None													*/
/* Outputs	   : None													*/
/************************************************************************/
Void modemInit(Void)
{
	int			i;
	SerPort		consoleSave;

	if (initModem)
	{			//Probably don't need to init registers every time
				//But following was tested and can be enabled any time
		modemOn();
		consoleSave = getConsole();
		setConsole(modemPort);
		setSerialBaud(modemPort, modemBaud);

		for (i = 0; i < modemWarmup; i++)
		{
			clkDelayUs(1000000);
			if (wdtOn)
				wdtPing();					/* Keep watchdog running */
		}

		if (modemCommandMode())
		{
			printf("ATS18=%u\r", modemAddr);
			waitForPrompt();
			printf("ATS6=%u\r", modemTxPwr);

			for (i = 0; i < NumberOf(modemInitStr); i++)
			{
				waitForPrompt();
				printf(modemInitStr[i]);
			}
		}

		setConsole(consoleSave);
	}	//End of register init

	modemOff();			// Leave modem on for now

} /* modemInit() */


/************************************************************************/
/* Function	   : modemConnectCheck										*/
/* Purpose	   : Check for modem connection								*/
/* Inputs	   : None													*/
/* Outputs	   : TRUE if found modem connection, else FALSE				*/
/************************************************************************/
MBool modemConnectCheck(Void)
{
	return(serRxCount(modemPort) ? TRUE : FALSE);
}


/************************************************************************/
/* Function	   : modemOn												*/
/* Purpose	   : Turn Modem On											*/
/* Inputs	   : None													*/
/* Outputs	   : None													*/
/************************************************************************/
void modemOn(void)
{
	sysLog("Turning on modem");
	digModemEnbl();
}


/************************************************************************/
/* Function	   : modemOff												*/
/* Purpose	   : Turn Modem Off											*/
/* Inputs	   : None													*/
/* Outputs	   : None													*/
/************************************************************************/
void modemOff(void)
{
	sysLog("Turning off modem");
	digModemDisbl();
}


/************************************************************************/
/* Function	   : checkModemBatt											*/
/* Purpose	   : Function called periodically from UI to monitor modem batt*/
/* Inputs	   : None													*/
/* Outputs	   : None													*/
/************************************************************************/
Void checkModemBatt(Void)
{
	Nat16		modemBatt;

	if ((clkGetTick() - modemBattCheckTick) >= MODEM_CHECK_TICKS)
	{
		modemBattCheckTick = clkGetTick();

		if ((modemBatt = adcSample(MODEM_MIN_CHAN)) < modemMinBatt)
			modemMinBatt = modemBatt;
	}
}


/************************************************************************/
/* Function	   : ModemCmd												*/
/* Purpose	   : Command-line function to send command to modem, get reply*/
/* Inputs	   : argc, argv												*/
/* Outputs	   : Return code											*/
/************************************************************************/
CmdRtn ModemCmd(int argc, char **argv)
{
#if 0
	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);

	now = clkTime();
	endTime = now + 8;
	len = 0;

	while (clkTime() < endTime)
		if (CIOgetq() > 0)
			buf[len++] = CIOgetc();

	modemOnlineMode();

	printf("\nModem returns:\n");
	for (i = 0; i < len; i++)
		CIOputc(buf[i]);

	printf("\n");
#endif

	return(OK);

} /* ModemCmd() */


/************************************************************************/
/* Function	   : ModemConnectCmd										*/
/* Purpose	   : Process CONNECT message from modem						*/
/* Inputs	   : argc, argv												*/
/* Outputs	   : Return code											*/
/************************************************************************/
CmdRtn ModemConnectCmd(int argc, char **argv)
{
	removeNewline(argv[0]);

#ifdef DEBUG_MODEM
	sysLogPrintf("Got \"%s\" from modem\n", argv[0]); 
#endif

	return(OK);

} /* ModemConnectCmd() */


/************************************************************************/
/* Function	   : ModemDisconnectCmd										*/
/* Purpose	   : Process Off or Lowpower command from modem				*/
/* Inputs	   : argc, argv												*/
/* Outputs	   : Return code											*/
/************************************************************************/
CmdRtn ModemDisconnectCmd(int argc, char **argv, char *cmdline)
{
	removeNewline(argv[0]);

	sysLogPrintf("Exiting command parser due to \"%s\" from modem\n", argv[0]);
	
	return(ERROR);

} /* ModemDisconnectCmd() */


/************************************************************************/
/* Function	   : ModemOfflineCmd										*/
/* Purpose	   : Dispatched when userif sees "USER:", indicating modem is offline*/
/* Inputs	   : argc, argv												*/
/* Outputs	   : Return code											*/
/************************************************************************/
CmdRtn ModemOfflineCmd(int argc, char **argv)
{
	removeNewline(argv[0]);

	sysLogPrintf("Got \"%s\" from modem, sending ATO\n", argv[0]);

	modemOnlineMode();

	return(OK);

} /* 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, c;
	char		*p;
	char		buf[128];

	printf("ATS%u?\r", reg);

	p = buf;
	while ((c = ser_getc_tmout(modemPort, TICKS_PER_SEC/2)) != ERROR)
		*p++ = (char)c;
	
	*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, c;
	char		*p;
	char		buf[128];

	printf("ATEC%u:ATS%u?\r", modem, reg);
	clkDelayUs(5000000);

	p = buf;
	while ((c = ser_getc_tmout(modemPort, TICKS_PER_SEC/2)) != ERROR)
		*p++ = (char)c;
	
	*p = '\0';

	if (sscanf(buf, " %d", &val) < 1)
	{
		sysLogPrintf("Failure to get remote S%u from modem %u\n",
					 reg, modem);
		return(-1);
	}

	sysLogPrintf("Remote S%u = %u\n", reg, 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)
{
	printf("ATEC%u:ATS%u=%u\r", modem, reg, val);
	getPrompt();

} /* setRemoteSReg() */


/************************************************************************/
/* Function	   : SRegCmd												*/
/* Purpose	   : Command-line command to turn on/off modem				*/
/* Inputs	   : argc, argv												*/
/* Outputs	   : Return code											*/
/************************************************************************/
CmdRtn	SRegCmd(int argc, char **argv)
{
#if 0
	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");
#endif
	return(OK);

} /* SRegCmd() */

