/****************************************************************************/
/* Copyright 1995-2016 MBARI												*/
/****************************************************************************/

#include <mbariTypes.h>		/* MBARI type definitions			*/
#include <mbariConst.h>		/* MBARI constants					*/
#include <oasis.h>			/* OASIS controller definitions		*/
#include <dig_io.h>			/* OASIS I/O definitions			*/
#include <file.h>			/* OASIS File I/O Routines			*/
#include <drvr.h>			/* OASIS driver functions			*/
#include <otask.h>			/* OASIS Multitasking definitions	*/
#include <utils.h>			/* OASIS Utility routines			*/
#include <debug.h>
#include <sensors.h>
#include <malloc.h>

#include <stdio.h>			/* Standard I/O functions			*/
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <optrode.h>		/* This module						*/


#define OPT_BUFSIZE		256			/* Orig 64; add for rec number	*/
#define DATA_FILE		"oxy.txt"	/* log file for optrode data	*/
#define ERR_FILE		"oxy.err"	/* file for logging errors		*/


/****************************************************/
/*	Drive Descriptors for Drivers in this module	*/
/****************************************************/
/* Parameters used:								*/
/* PARM0:  Retry count							*/
/* PARM1:  Power-up delay (secs)				*/
/* PARM2:  0=log to file, 1=log to oasis log,	*/
/*		   2 or greater = log to both			*/
/************************************************/

const char *optParmDesc[] = 
	{"Retry count", "Power-up delay", "Log to file and/or oasis",
	 NULL, NULL, NULL, NULL, NULL};

const DrvDesc optrodeDrvDesc =
	{ "AanderaaO2", optrode_drv, nullWakeFunc, lastOptrodeSample, optParmDesc,
	  600, NO_SERIAL, 9600, MODE_N81NF, 0, LOG_ASCII, 0, 5, 1, 0, 0};


/************************************************************************/
/* Function	   : logOptrodeData											*/
/* Purpose	   : log contents of opt_buf to a data file					*/
/* Inputs	   : opt_buf												*/
/* Outputs	   : None													*/
/************************************************************************/
MLocal void logOptrodeData(Driver* dp, O2Sample *sample, char *buf)
{
	struct tm	*tm_struct;
	FIL			fp;

	/* convert the time stamp to a time structure */
	tm_struct = localtime(&sample->time_stamp);

	sprintf(buf, "%s, ", dp->drv_name);

	/* create the time stamp string "mm/dd/yyyy hh:mm:ss" for the log file */
	strftime(buf + strlen(buf), OPT_BUFSIZE, "%m/%d/%Y %H:%M:%S", tm_struct);

	/* grab the semaphore for the data file */
	takeFileSem();

	if (f_open(&fp, DATA_FILE, (BYTE)(FA_WRITE | FA_OPEN_APPEND)) == FR_OK)
	{
		dp->drv_lastlog = f_tell(&fp);

		sprintf(buf + strlen(buf), ", %d, %d, %6.2f, %6.2f, %6.2f\r\n",
				sample->model,
				sample->serial,
				sample->oxygen,
				sample->saturation,
				sample->temp);

		f_puts(buf, &fp);
		f_close(&fp);
	}
	else
		dprintf("Can't open %s\n", DATA_FILE);
	
	giveFileSem();				/* release the file semaphore			*/
}


/************************************************************************/
/* Function	   : lastOptrodeSample										*/
/* Purpose	   : get the last optrode sample							*/
/* Inputs	   : Driver pointer											*/
/* Outputs	   : OK or ERROR											*/
/************************************************************************/
CmdRtn lastOptrodeSample(Driver* dp)
{
	CmdRtn		rtn = OK;
	FIL			fp;
	char		*buf;

	if (dp->drv_lastlog != -1)
	{
		if ((buf = pmalloc(OPT_BUFSIZE)) == NULL)
			return(ERROR);

		memset(buf, 0, OPT_BUFSIZE);
		takeFileSem();

		if (f_open(&fp, DATA_FILE, (BYTE)(FA_READ | FA_OPEN_EXISTING)) == FR_OK)
		{
			f_lseek(&fp, dp->drv_lastlog);
			f_gets(buf, OPT_BUFSIZE, &fp);
			f_close(&fp);
			xprintf("%s (name, time, model, serial, oxygen, saturation, temp)\n", buf);
					
		}
		else
		{
			xprintf("Can't open %s\n", DATA_FILE);
			rtn = ERROR;
		}

		giveFileSem();				/* release the file semaphore			*/
	}
	else
		xprintf("No samples taken\n");
	
	return(rtn);
}


/************************************************************************/
/* Function	   : sampleOptrode											*/
/* Purpose	   : Make one attempt to get an optrode sample				*/
/* Inputs	   : Driver pointer, O2Sample pointer						*/
/* Outputs	   : Number of converted fields received from Optrode, or ERROR*/
/************************************************************************/
MLocal Int16 sampleOptrode(Driver* dp, char *opt_buf, O2Sample* sample)
{
	char		*opt_ptr;
	int			num_bytes, ret_val, i;

	memset(sample, 0, sizeof(O2Sample));		/* zero out O2Sample 	*/
	sample->driver = dp;
	
	drv_pwron(dp);								/* power up sensor		*/

	delay_secs(dp->drv_parms[PARM1]);			/* wait power-up delay	*/

	xflush_ser(0);								/* flush the serial port */

	xputs("do sample\n");						/* send the sample command*/

	/* the sensor spits out one sample on power up, so wait for it */
	num_bytes = xgets_tmout(opt_buf, OPT_BUFSIZE, (Nat16)dp->drv_parms[TIMEOUT]);

	sample->time_stamp = clkTime();				/* time stamp the sample */

	drv_pwroff(dp);								/* remove power from sensor */
	task_delay(TICKS_PER_SECOND);  			/* wait for sensor to power down */

	/* if you did'nt get any data from the sensor clear out the
	   sample and return an ERROR */
	if ( num_bytes <= 0)
		return ERROR;

	opt_ptr = opt_buf;					/* convert the data from the O2 sensor */

	i = 0;								/* skip non printable chars */
	while ( !isalnum(*opt_ptr) && (++i < num_bytes) )
		++opt_ptr;

	ret_val = sscanf(opt_ptr,"%d%d%f%f%f", &sample->model,
					 &sample->serial, &sample->oxygen,
					 &sample->saturation, &sample->temp);

	return(ret_val);
}

/************************************************************************/
/* Function	   : optrode_drv											*/
/* Purpose	   : Driver for Andera Optrode								*/
/* Inputs	   : Driver Pointer											*/
/* Outputs	   : None													*/
/************************************************************************/
Void optrode_drv(Driver *dp)
{
	Int16		rtn, retryCnt;
	O2Sample	sample;
	char		*p;
	FIL			fp;
	struct tm	*tm_struct;
	char		*opt_buf;

	/* grab the serial port for the sensor and power it up */
	if ((opt_buf = drvSerPortAndMalloc(dp, OPT_BUFSIZE)) == NULL)
		return;

	sample.driver = dp;
	retryCnt = dp->drv_parms[PARM0];
	if (retryCnt < 1)
		retryCnt = 1;

	while (retryCnt-- > 0)
		if ((rtn = sampleOptrode(dp, opt_buf, &sample)) >= 5)
			break;
		else if (++dp->drv_cnt < 1000)
		{
			/* convert the time stamp to a time structure */
			tm_struct = localtime(&sample.time_stamp);

			/* create the time stamp string "mm/dd/yyyy hh:mm:ss" for the log file */
			strftime(opt_buf, OPT_BUFSIZE, "%m/%d/%Y %H:%M:%S", tm_struct);

			takeFileSem();

			if (f_open(&fp, ERR_FILE, (BYTE)(FA_WRITE | FA_OPEN_APPEND)) == FR_OK)
			{
				f_printf(&fp, "%s: %s, timeout getting data, %s\r\n",
						 dp->drv_name, opt_buf,
						 (retryCnt > 0) ? "retry" : "retries exceeded");
				f_close(&fp);
			}

			giveFileSem();
		}

	drv_ser_release(dp);				/* release the serial port */

	if (dp->drv_parms[PARM2] >= 1)		/* Log to oasis first so we can	*/
	{									/* reuse opt_buf				*/
		/* Remove leading non-printable chars	*/
		for (p = opt_buf; (rtn > 0) && *p && !isalnum(*p); p++ )
			rtn--;

		if (rtn > 0)
			drvLog(dp, (Byte *)p, rtn);
		else
			drvLogError(dp,NO_DATA);
	}

	if (dp->drv_parms[PARM2] != 1)
		logOptrodeData(dp, &sample, opt_buf);
	
	free(opt_buf);
}
