/****************************************************************************/
/* Copyright 1995-2003 MBARI												*/
/****************************************************************************/

#include <mbariTypes.h>		/* MBARI type definitions			*/
#include <mbariConst.h>		/* MBARI constants					*/
#include <oasis.h>			/* OASIS controller definitions		*/
#include <io.h>				/* OASIS I/O definitions			*/
#include <file.h>			/* OASIS File I/O Routines			*/
#include <drvr.h>			/* OASIS driver functions			*/
#include <task.h>			/* OASIS Multitasking definitions	*/
#include <utils.h>			/* OASIS Utility routines			*/
#include <stdio.h>			/* Standard I/O functions			*/
#include <debug.h>
#include <sensors.h>
#include <cfxpico.h>		/* Persistor PicoDOS definitions	*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <optrode.h>		/* OASIS serial I/O definitions			*/

/************************************************/
/* 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			*/
/************************************************/

#define TOTAL_OPTRODES	32

#define OPT_BUFSIZE		80			/* 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		*/

Extern Nat32 brsLowVoltLimit;	/* BRS low voltage reset */
MLocal O2Sample lastSample[TOTAL_OPTRODES];


/************************************************************************/
/* Function	   : logOptrodeData											*/
/* Purpose	   : log contents of opt_buf to a data file					*/
/* Inputs	   : opt_buf												*/
/* Outputs	   : None													*/
/************************************************************************/
MLocal Void logOptrodeData(Driver* dp, O2Sample* sample)
{
	FILE *fd;
	char time_str[64];
	struct tm *tm_struct;

	/* 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(time_str, 64, "%m/%d/%Y %H:%M:%S", tm_struct);

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

	/* open the data file */
	fd = fopen(DATA_FILE, "a");

	/* check the file pointer */
	if (fd == NULL)
	{
		dprintf("Can't open Data file\n");
	}
	else
	{
		fprintf(fd, "%s, %s, %d, %d, %6.2f, %6.2f, %6.2f\r\n",
				dp->drv_name,
				time_str,
				sample->model,
				sample->serial,
				sample->oxygen,
				sample->saturation,
				sample->temp);
		/* close it !*/
		fclose(fd);
	}

	/* release the file semaphore */
	giveFileSem();
}


/************************************************************************/
/* Function	   : optrodeChan											*/
/* Purpose	   : get the optorde channel pointer						*/
/* Inputs	   : Driver pointer, Boolean to return an empty sample		*/
/* Outputs	   : Pointer to last sample, or NULL						*/
/************************************************************************/
MLocal O2Sample *optrodeChan(Driver* dp, MBool findEmpty)
{
	int chan;

	for (chan = 0; chan < TOTAL_OPTRODES; chan++)
		if (lastSample[chan].driver == dp)
			return(&lastSample[chan]);

	if (findEmpty)
		for (chan = 0; chan < TOTAL_OPTRODES; chan++)
			if (lastSample[chan].driver == NULL)
				return(&lastSample[chan]);

	return(NULL);
}


/************************************************************************/
/* Function	   : lastOptrodeSample										*/
/* Purpose	   : get the last optrode sample							*/
/* Inputs	   : Driver pointer, O2Sample pointer, verbose flag			*/
/* Outputs	   : OK or ERROR												*/
/************************************************************************/
Int16 lastOptrodeSample(Driver* dp, O2Sample* sample, MBool verbose)
{
	char time_str[64];
	struct tm *tm_struct;
	O2Sample *lastChanSample;

	/* determine the optrode channel for this driver */
	lastChanSample = optrodeChan(dp, FALSE);

	/* if you got an optrode channel get the sample for it */
	if ( lastChanSample != NULL)
	{
		/* grab last sample semaphore */
		memcpy(sample, lastChanSample, sizeof(O2Sample));
	}
	else
	{
		memset(sample, 0, sizeof(O2Sample));
	}

	/* print the optrode sample to the console */
	if ( verbose )
	{
		xprintf("model : %-d\n", sample->model);
		xprintf("serial: %-d\n", sample->serial);
		xprintf("O2	   : %-6.2f uM\n", sample->oxygen);
		xprintf("sat   : %-6.2f %\n", sample->saturation);
		xprintf("temp  : %-6.2f C\n", sample->temp);

		/* 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(time_str, 64, "%m/%d/%Y %H:%M:%S", tm_struct);

		xprintf("time  : %s\n", time_str);
	}

	return OK;
}


/************************************************************************/
/* 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;
	O2Sample *lastChanSample;

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

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

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

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

	/* 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]);

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

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

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

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

	/* skip non printable chars */
	i = 0;
	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);


	/* determine the optrode channel for this dirver */
	lastChanSample = optrodeChan(dp, TRUE);

	/* copy sample to last sample buffer */

	if ( lastChanSample != NULL)
		memcpy(lastChanSample, sample, sizeof(O2Sample));

	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;
	FILE		*fd;
	struct tm	*tm_struct;
	char		opt_buf[OPT_BUFSIZE];

	/* grab the serial port for the sensor and power it up */
	drv_ser_port(dp);

	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, sizeof(opt_buf), "%m/%d/%Y %H:%M:%S", tm_struct);

			takeFileSem();

			if ((fd = fopen(ERR_FILE, "a")) != NULL)
			{
				fprintf(fd, "%s: %s, timeout getting data, %s\r\n",
						dp->drv_name, opt_buf,
						(retryCnt > 0) ? "retry" : "retries exceeded");
				fclose(fd);
			}

			giveFileSem();
		}

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

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

	if (dp->drv_parms[PARM2] >= 1)
	{
		/* 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);
	}
}
