/****************************************************************************/
/* Copyright 2003-2016 MBARI												*/
/****************************************************************************/
/* Summary	: Main Routine for OASIS5 on PIC32MX470F512L					*/
/* Filename : oasis.c														*/
/* Author	: Robert Herlien (rah)											*/
/* Project	: OASIS Mooring Replacement (OASIS5)							*/
/* Revision : 1.0															*/
/* Created	: 18jul2016														*/
/*																			*/
/* 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:													*/
/* 18jul2016 rah - Oasis5 version created from Oasis4 oasis.c				*/
/* 23dec2008 rah - Oasis4 version created from Oasis3 oasis.c				*/
/* 10oct2002 rah - Oasis3 version created from OASIS oasis.c				*/
/* $Log$
 */
/****************************************************************************/

#include <mbariTypes.h>					/* MBARI type definitions			*/
#include <mbariConst.h>					/* MBARI constants					*/
#include <oasis.h>						/* OASIS controller definitions		*/
#include <picConfig.h>					/* PIC32 peripheral routines		*/
#include <ConfigPerformance.h>			/* FreeRTOS PIC32 configuration		*/
#include <custom.h>						/* Custom def's for deployment		*/
#include <dig_io.h>						/* OASIS digital I/O definitions	*/
#include <olist.h>						/* OASIS Linked List library		*/
#include <sem.h>						/* OASIS Semaphore library			*/
#include <otask.h>						/* OASIS task dispatcher			*/
#include <malloc.h>						/* OASIS malloc routines			*/
#include <log.h>						/* OASIS data logging definitions	*/
#include <syslog.h>						/* Respirometer system logger		*/
#include <file.h>						/* OASIS File I/O Routines			*/
#include <drvr.h>						/* OASIS driver functions			*/
#include <timer.h>						/* OASIS clock routines				*/
#include <userif.h>						/* OASIS User Interface routines	*/
#include <usrcmds.h>					/* User Command function definitions*/
#include <utils.h>						/* OASIS Utility routines			*/
#include <serial.h>						/* OASIS serial I/O definitions		*/
#include <modbus.h>						/* Modbus protocol library			*/
#include <parm.h>						/* Parameter table definitions		*/
#include <remote.h>						/* Remote I/O library				*/
#include <o5debug.h>					/* OASIS diagnostic routines		*/
#include <debug.h>

#include <istdio.h>						/* Standard I/O library				*/
#include <string.h>						/* Standard string library			*/
#include <time.h>

#include <FreeRTOS.h>					/* FreeRTOS definitions				*/
#include <task.h>						/* FreeRTOS/include task.h			*/
#include <semphr.h>						/* FreeRTOS semaphore functions		*/


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

Extern Parm_t			radio_tmout;	/* Timeout for radio reset			*/
Extern Parm_t			logStatus;		/* Log on power up/down				*/
Extern MBool			diagnosticMode; /* Go into diagnostic mode			*/
Extern MBool			syncAtMidnight;
Extern Nat32			pwr_perm;		/* Devices to leave powered on		*/
Extern Nat32			wakeOnConsoleChar;
Extern Nat32			serPortsToWake;
Extern Nat32			serPortsWoke;
Extern MBool			blinky;			/* Blink LED?						*/


/********************************/
/*		Global Data				*/
/********************************/

Global time_t			radio_time;		/* Last time user connected on radio*/
Global TaskHandle_t		oasisTask = NULL;

const char * const		signon = "OASIS5 $Revision$";
const char * const		cvs_tag = "CVS tag $Name$";


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

MLocal MBool	multiTasking = FALSE;

MLocal SerDesc	schedSerDesc =
	{SER_CONSOLE, (NO_PTY | BIT8 | STOP1 | FLOW | ECHO | AUTOCR), 0};

#ifdef WATCHDOG
#endif


/************************************************************************/
/* Function	   : rtosIsRunning											*/
/* Purpose	   : Return TRUE if FreeRTOS scheduler has been started		*/
/* Inputs	   : None													*/
/* Outputs	   : TRUE if scheduler started, else FALSE					*/
/************************************************************************/
MBool	rtosIsRunning(void)
{
	return(multiTasking);
}


/************************************************************************/
/* Function	   : setRtosRunMode											*/
/* Purpose	   : Set RTOS run indicator									*/
/* Inputs	   : Boolean for run indicator								*/
/* Outputs	   : None													*/
/************************************************************************/
void	setRtosRunMode(MBool runMode)
{
	multiTasking = runMode;
}


/************************************************************************/
/* Function	   : getOasisTask											*/
/* Purpose	   : Return task handle of Oasis Scheduler Task				*/
/* Inputs	   : TaskHandle_t for Oasis scheduler						*/
/* Outputs	   : None													*/
/************************************************************************/
TaskHandle_t getOasisTask(void)
{
	return(oasisTask);
}


/************************************************************************/
/* Function	   : oasisSchedulerTask										*/
/* Purpose	   : OASIS5 Driver Scheduler Task							*/
/* Inputs	   : Return code from first phase of initialization			*/
/* Outputs	   : None, Never exits										*/
/************************************************************************/
MLocal void oasisSchedulerTask(void *parm)
{
	Nat32		next_alarm;				/* Seconds 'til next alarm		*/
	Errno		initRtn;				/* Return code from initialization*/
	WhatWokeUs	wakeup = WakeNone;		/* How we woke up				*/
	time_t		tod;					/* Current time					*/
	Nat16		loopCntr;				/* Loop counter					*/

	vTaskSetThreadLocalStoragePointer(NULL, ConsoleP, &schedSerDesc);
	vTaskSetThreadLocalStoragePointer(NULL, DrvrP, NULL);
	vTaskSetThreadLocalStoragePointer(NULL, State, (void *)READY);
	vTaskSetThreadLocalStoragePointer(NULL, StateParm, NULL);
	vTaskSetThreadLocalStoragePointer(NULL, Malloc1, NULL);
	vTaskSetThreadLocalStoragePointer(NULL, Malloc2, NULL);

	multiTasking = TRUE;
	initRtn = (Errno)parm;
    digBlinkyOff();
    digTx0En();
    tmrDelayMs(10);                     /* Wait for charge pumps        */

	serPortInit(SER_CONSOLE, CONSOLE_BAUD, NO_PTY | BIT8 | STOP1);

	/* Identify the progam and build	*/
    xprintf("\r\nOASIS5 Software Built on %s at %s\r\n", __DATE__, __TIME__);

    if (clkInit() != 0)
        xputs("RTC needs to be set!!!\r\n");

	if ((file_init() != OK) ||			/* Initialize file system		*/
		(log_init() != OK))				/* Initialize log memory		*/
		initRtn = ERROR;
	
	sysLogInit();						/* Initialize system logger		*/
	sysLogPrintf("RCON = 0x%x", picResetType());
	pwrFailInit();						/* Init power fail interrupt	*/
	initRemoteLib();					/* Init remote serial/IO lib	*/
	initModBus();						/* Init ModBus library			*/

	user_init();						/* Initialize user I/F			*/
	drvr_init();						/* Init drvr module				*/
	radio_time = clkTime();				/* Init read RTC, init radio tmout*/

	if (initRtn != OK)					/* If init was bad, abort		   */
	{
		xputs("Error in OASIS Initialization!	Going to diagnostic mode.\n\n");
		diagnosticMode = TRUE;
	}

	if (diagnosticMode)
		o5Diagnostic(0xffff);			/* If diagnostic mode set, just run*/

    drvPwrVecSet(pwr_perm);				/* Turn off all relays except pwr_perm*/

	while(TRUE)
	{
		tod = clkTime();

		if (logStatus & LOG_PWRUP)		/* Log power up event if enabled   */
			logOasisStatus(0x100 | wakeup);

		if (syncAtMidnight)
			drvMidnightSync();			/* If need to sync @ midnight, check*/

		/* Tickle Oasis3 hardware watchdog first time thru */
//		ioPingWatchdog();
		/* and then init the loop counter */				
		loopCntr = 0;
	
#ifdef DEBUG_DRVR_TIMES
		clkPrintTime(&tod);
		dprintf("OASIS awake, wake type = %d\r\n", wakeup);
#endif
		
		do									/* Main loop - until drivers done  */
		{									/* Loop executes at 10 Hz		   */
			loopCntr++;
			if ((loopCntr % 5) == 0)
			{
				tod = clkTime();			/* Get current time twice per second*/
				   
				if (blinky)
					digToggleBlinky();
			}

			serCheckXoffStatus();			/* See if XOFFs have timed out	   */

			if ( radio_tmout && (tod > (radio_time + (radio_tmout * 3600L))) )
				picSoftReset();				/* If no radio for 24 hrs, reset   */

#ifdef WATCHDOG
			if (loopCntr >= O5_WATCHDOG_RATE)
			{
//				ioPingWatchdog();				/* Tickle O5 hardware watchdog	   */
				loopCntr = 0;
			}
#endif

			task_delay(TICKS_PER_SEC/10);	/* Wait to see if drvrs running   */

			next_alarm = drvr_sample();		/* Start any drvr tasks not running*/
			serPortsWoke = 0;				/* Only force drvr from wakeup once*/

		} while (next_alarm < MIN_SLEEP_TIME); /* Repeat until all drivers done*/


		if ( logStatus & LOG_PWRDOWN )		/* Log power down event if enabled */
			logOasisStatus(0);

#ifdef DEBUG_DRVR_TIMES
		clkPrintTime(&tod);
		dprintf("OASIS sleeping, wakeup secs = %u\r\n", next_alarm);
		serWaitTxComplete(SER_CONSOLE);
#endif
		digBlinkyOff();

		if (next_alarm > MAX_SLEEP_TIME)
			next_alarm = MAX_SLEEP_TIME;

		serPortsToWake = wakeOnConsoleChar;
		serPortsWoke = 0;
		xTaskNotifyWait(~0, ~0, NULL, next_alarm * TICKS_PER_SEC);
		serPortsToWake = 0;
	}

} /* oasisSchedulerTask() */


/************************************************************************/
/* Function	   : main													*/
/* Purpose	   : Main entry point										*/
/* Inputs	   : argc, argv												*/
/* Outputs	   : None													*/
/* Comments	   : Never returns											*/
/************************************************************************/
int main(int argc, char *argv[])
{
	Errno	rtn;

    /* Call initializers from FreeRTOS PIC32 support files      */
    vHardwareConfigurePerformance();
    vHardwareUseMultiVectoredInterrupts();

	multiTasking = FALSE;

    digInit();
    picPeriphInit();
	semLibInit();
    rtn = serInit();
    rtcInit();
    if (adcInit() != OK)
		rtn = ERROR;
	
	oasisTask = task_create(oasisSchedulerTask, (void *)rtn, "OasisScheduler",
							SCHED_STACK_WORDS, SCHED_PRIO);
    
    vTaskStartScheduler();

    // Should never arrive here!!
	picSoftReset();

} /* main() */
