//----------------------------------------------------------------------------------
// <copyright file="TurnerC3.c" company="LiquidRobotics">
//	Copyright (c) Liquid Robotics Corporation.  All rights reserved.
// </copyright>
//
// <summary>
// 	This is the source file for managing the TurnerC3 Fluorometer in Current Data Mode
// </summary>
//
// <owner>Jim Kirklin</owner>
//----------------------------------------------------------------------------------


#include "includes.h"
#include <avr/io.h>
#include <avr/wdt.h>
#include <avr/power.h>
//#include "app_init.h"
#include "uart.h"
#include "pib.h"
#include "timer.h"
#include "lrstat.h"
#include "spidebug.h"
#include "wdtrig.h"
#include "pib_apps.h"
#include "User_app_init.h"
#include "fltPyldComms.h"
#include "UserData.h"
//#include "FileTransfer.h"
#include "Turner_C3.h"

#include "SerialDebug.h"

#define FLUOROMETER_CLASS	0x000B
#define TURNER_C3			0x0001

//Serial Number stored at offset 0x0010 from PM base -- 16 bytes
#define TURNER_C3_SERNUM_OFFSET		0x0010
//Sample Rate stored at offset 0x0040 from PM base -- 4 bytes
#define TURNER_C3_SAMPLE_OFFSET		0x0040


typedef struct _TURNER_C3_DATA_REPORT_XMIT_
{
	PIB_IRIDIUM_DATA_XMIT	comm;
	C3_DATA_REPORT			reportData;
}TURNER_C3_DATA_REPORT_XMIT, *PTURNER_C3_DATA_REPORT_XMIT;

typedef struct _TURNER_C3_ASCII_REPORT_XMIT_
{
	PIB_IRIDIUM_DATA_XMIT	comm;
	C3_ASCII_REPORT			reportASCII;
}TURNER_C3_ASCII_REPORT_XMIT, *PTURNER_C3_ASCII_REPORT_XMIT;


typedef struct _TURNER_C3_BUFFER_
{
	char 		bufferTx[256];				// 256 bytes
	char 		bufferRx[1024];				// 1024 bytes
	uint32_t 	sampleRate;					// 4 bytes
	BOOL		sampleC3;					// 1 byte
	BOOL		powerOn;					// 1 bytes



	
	C3_CALIBRATION_BLOCK	newCalibration;
	C3_CALIBRATION_BLOCK	currentCalibration;

	C3_CFG_BLOCK			newConfiguration;
	C3_CFG_BLOCK			currentConfiguration;

	BOOL					heartbeatFirstTime;
	BOOL					heartbeatSecondTime;

	uint32_t				heartbeatCount;
	
	uint8_t					sampleCount;
	C3_DATA_REPORT			reportData;
	C3_ASCII_REPORT			reportASCII;

	char 	nameC1[15];
	char 	nameC2[15];
	char 	nameC3[15];
	char	serialNum[8];

	uint8_t majorFW;
	uint8_t minorFW;
	uint8_t	majorComm;
	uint8_t minorComm;
	char	temperature;
	char	pressure;
	char	wiper;
	uint8_t	sensorsInstalled;

	char 					tokenSeparatorList[MAX_TOKEN_SEPARATORS];
	CONSOLE_RESPONSE_INFO 	responseInfo;
	char					consoleResponse[256];

//	char 		fluorometerSample[120];
}TURNER_C3_BUFFER, *PTURNER_C3_BUFFER;

typedef struct _TURNER_C3_TASK_
{
	uint8_t	taskID;
	uint8_t portNum;
	uint16_t eepromAddress;
	PTURNER_C3_BUFFER 	pBuf;
}TURNER_C3_TASK, *PTURNER_C3_TASK;

uint8_t TurnerC3_Init(PTURNER_C3_TASK taskInfo);
uint8_t TurnerC3_Tx(PTURNER_C3_TASK pTaskInfo, uint8_t* pBuffer, uint8_t len);
uint8_t TurnerC3_Rx(PTURNER_C3_TASK pTaskInfo, char* pBuffer, uint16_t* pLen, char* eol, int eolLen);
uint8_t TurnerC3_Rx_Len(PTURNER_C3_TASK pTaskInfo, char* pBuffer, uint16_t* pLen, uint16_t maxLen, uint16_t maxTimeout);
void 	TurnerC3_GetDataSample(PTURNER_C3_TASK pTtaskInfo);
uint8_t TurnerC3_DataReportXmit(PTURNER_C3_TASK pTaskInfo);
uint8_t TurnerC3_ASCIIReportXmit(PTURNER_C3_TASK pTaskInfo, uint8_t len);
uint8_t TurnerC3_ParseFile(PTURNER_C3_TASK pTaskInfo, uint32_t fileID);
uint8_t TurnerC3_QueueFile(PTURNER_C3_TASK pTaskInfo, uint32_t fileID);




uint8_t TurnerC3_HeartBeat(PTURNER_C3_TASK pTaskInfo);
void 	TurnerC3_GetCurrentData(PTURNER_C3_TASK pTtaskInfo);
uint8_t TurnerC3_GetConfiguration(PTURNER_C3_TASK pTaskInfo, BOOL sendResult);
uint8_t TurnerC3_XmitConfiguration(PTURNER_C3_TASK pTaskInfo, PC3_CFG_BLOCK pCfgBlock);
uint8_t TurnerC3_SetConfiguration(PTURNER_C3_TASK pTaskInfo, char* paramlist);
uint8_t TurnerC3_GetCalibration(PTURNER_C3_TASK pTaskInfo, uint8_t channel);
uint8_t TurnerC3_SetCalibration(PTURNER_C3_TASK pTaskInfo);
uint8_t TurnerC3_StartCurrentData(PTURNER_C3_TASK pTaskInfo);
uint8_t TurnerC3_StopCurrentData(PTURNER_C3_TASK pTaskInfo);
uint8_t TurnerC3_GetDateTime(PTURNER_C3_TASK pTaskInfo);
uint8_t TurnerC3_SetDateTime(PTURNER_C3_TASK pTaskInfo);
uint8_t TurnerC3_PowerOn(PTURNER_C3_TASK pTaskInfo);
uint8_t TurnerC3_PowerOff(PTURNER_C3_TASK pTaskInfo);
uint8_t TurnerC3_SetID(PTURNER_C3_TASK pTaskInfo, char* pSerialNum, uint8_t len);
uint8_t TurnerC3_GetID(PTURNER_C3_TASK pTaskInfo);
uint8_t TurnerC3_XmitID(PTURNER_C3_TASK pTaskInfo);

uint8_t TurnerC3_Xmit_DateTime(PTURNER_C3_TASK pTaskInfo, uint32_t c3Tick);
uint8_t TurnerC3_Xmit_ID(PTURNER_C3_TASK pTaskInfo, PC3_ID_BLOCK pID);
uint32_t TurnerC3_ConvertToTick(uint8_t* pBuf);
uint16_t TurnerC3_CheckSum(uint8_t* tempBuf, uint16_t len);
uint16_t TurnerC3_Endian2ByteSwap(uint16_t val);
uint16_t TurnerC3_Endian2ByteSwap(uint16_t val);

void TurnerC3_ParseConsole(PTURNER_C3_TASK pTaskInfo);
uint8_t TurnerC3_ParseCommand(PTURNER_C3_TASK pTaskInfo, char command, char* cmdParams);
void TurnerC3_QueueConsoleResponse(PTURNER_C3_TASK pTaskInfo);



extern char fileIn[NUM_TASK_FILES][MAX_FILE_SIZE_IN] __attribute__((section(".xdata")));
extern char fileOut[NUM_TASK_FILES][MAX_FILE_SIZE_OUT] __attribute__((section(".xdata")));
extern uint16_t fileOutSize[NUM_TASK_FILES];
extern uint16_t fileInSize[NUM_TASK_FILES];
extern BOOL fileAvailable[NUM_TASK_FILES];
extern uint32_t fileAvailableID[NUM_TASK_FILES];
extern BOOL transmitPending[NUM_TASK_FILES];

extern OS_EVENT* pMutexFloatQueue;
extern FLOAT_TX_BUFFER txFloatData[MAX_FLOAT_BUFFERS] __attribute__((section(".xdata")));
extern uint8_t	headFloatTx;
extern uint8_t tailFloatTx;

extern FLOAT_TX_BUFFER txFloatAck[MAX_ACK_BUFFERS] __attribute__((section(".xdata")));
extern uint8_t	headFloatAckTx;
extern uint8_t tailFloatAckTx;

extern uint8_t	DotToPad(char* pad, char* dot);
extern uint16_t mainTaskAddress;

extern int32_t latitude;
extern int32_t longitude;

extern CONSOLE_CMD_FORMAT consoleIn[NUM_TASK_FILES] __attribute__((section(".xdata")));
extern BOOL consoleAvailable[NUM_TASK_FILES];

extern BOOL kill[NUM_TASK_FILES];


#define NUM_C3_COMMANDS		10

extern const prog_char tokenListConsole[];
prog_char c3Commands[NUM_C3_COMMANDS][8] = {"state", "on", "off", "start", "stop", "cfg", "cal", "tim", "wip", "id"};
prog_char c3ParserID[] = "C3";
prog_char c3CmdList[] = "tofsxcamwiTOFSXCAMWI";

#define QUARTER_SECOND	32


//prog_char separator1a[] = {'%', ' ', '.', ':', '/', '\t', '\r', '\n' ,'\0'};
prog_char separator2a[] = {' ', '\t', '\r', '\n', '\0'};

uint8_t TurnerC3_Init(PTURNER_C3_TASK pTaskInfo)
{
	uint8_t retval = SUCCESS;
	uint16_t baseAddr = 0;

	if(pTaskInfo->portNum == USER_PORT_1)
		baseAddr = EEPROM_PM1_START;
	if(pTaskInfo->portNum == USER_PORT_2)
		baseAddr = EEPROM_PM2_START;
	if(pTaskInfo->portNum == USER_PORT_3)
		baseAddr = EEPROM_PM3_START;

	if(pTaskInfo->pBuf != NULL)
		memset(pTaskInfo->pBuf, 0, TASK_BUF_SIZE);

	switch(pTaskInfo->taskID)
	{
		case 1:
			BSET(DDRK, _BV(PK2));
			BSET(PORTK, _BV(PK2));
			break;
		case 2:
			BSET(DDRH, _BV(PH6));
			BSET(PORTH, _BV(PH6));
			break;
		case 3:
			BSET(DDRF, _BV(PF2));
			BSET(PORTF, _BV(PF2));
			break;
	}

	OSuartInitPort(pTaskInfo->portNum, 57600, UART_MODE_EIGHT_DATA_BITS, UART_MODE_ONE_STOP_BIT, UART_MODE_NO_PARITY);

	if(baseAddr != 0)
	{
		uint32_t temp;
		EepromReadBlock((void*)(baseAddr + TURNER_C3_SAMPLE_OFFSET), (void*)&temp, 4);

		if((temp == 0xFFFFFFFF) || (temp == 0x00000000))
		{
			temp = 60;
			EepromWriteBlock((void*)(baseAddr + TURNER_C3_SAMPLE_OFFSET), (void*)&temp, 4);
		}
		pTaskInfo->pBuf->sampleRate = temp;
	}

	OSTimeDlyHMSM(0,0,1,0);

	pTaskInfo->pBuf->sampleC3 = FALSE;
	pTaskInfo->pBuf->powerOn = FALSE;

	return retval;
}


void TurnerC3(void* pParam)
{
	TURNER_C3_TASK taskInfo;
	PTURNER_C3_TASK pTaskInit = (PTURNER_C3_TASK)pParam;
	uint8_t retval = FAIL;
	uint8_t hbCount = 0;
	taskInfo.taskID = pTaskInit->taskID;
	taskInfo.portNum = pTaskInit->portNum;
	taskInfo.pBuf = pTaskInit->pBuf;

	retval = TurnerC3_Init(&taskInfo);

//JGK	if(retval == SUCCESS)
	{
		while(1)
		{
			OSTimeDlyHMSM(0,0,0,500);

			sprintf_P(dbgMsg, PSTR("C3 Loop for PM%d"), taskInfo.portNum);
			SEND_MSG;

			if(taskInfo.pBuf->sampleC3 == TRUE)
			{
				TurnerC3_GetCurrentData(&taskInfo);
			}
			else if(taskInfo.pBuf->powerOn == TRUE)
			{
				hbCount++;
				if(hbCount >= 4)
				{
					TurnerC3_HeartBeat(&taskInfo);
					hbCount = 0;
				}
			}

			if(fileAvailable[pTaskInit->portNum] == TRUE)
			{
				TurnerC3_ParseFile(&taskInfo, fileAvailableID[pTaskInit->portNum]);
				fileAvailableID[pTaskInit->portNum] = 0;
				fileAvailable[pTaskInit->portNum] = FALSE;
			}

			if(consoleAvailable[taskInfo.portNum] == TRUE)
			{
				sprintf_P(dbgMsg, PSTR("Got something")); SEND_MSG;
				taskInfo.pBuf->responseInfo.source = consoleIn[taskInfo.taskID].cmdSrcAddress;
				taskInfo.pBuf->responseInfo.status = 0;
				taskInfo.pBuf->responseInfo.length = consoleIn[taskInfo.taskID].cmdSize;
				taskInfo.pBuf->responseInfo.id = consoleIn[taskInfo.taskID].id;

				TurnerC3_ParseConsole(&taskInfo);
				consoleAvailable[taskInfo.portNum] = FALSE;
			}

			// Check to see if we need to end this task and release resources
			if (kill[taskInfo.portNum] == TRUE) 
			{
				sprintf_P(dbgMsg, PSTR("Ending task...."));
				SEND_MSG;

				uint8_t val = 0xFF;
				uint16_t startAddr = 0;

				if(taskInfo.portNum == USER_PORT_1)
					startAddr = EEPROM_PM1_START;
				if(taskInfo.portNum == USER_PORT_2)
					startAddr = EEPROM_PM2_START;
				if(taskInfo.portNum == USER_PORT_3)
					startAddr = EEPROM_PM3_START;

				if (startAddr == 0)
				{
					sprintf_P(dbgMsg, PSTR("No start address for EEPROM"));
					SEND_MSG;
					kill[taskInfo.portNum] = FALSE;
				}
				else
				{
					// Write all F's to EEPROM
					for (uint16_t addr = startAddr; addr < startAddr + EEPROM_PM_SIZE; addr++)
						EepromWriteBlock((void*)addr, (void*)&val, 1);

					memset(taskInfo.pBuf, 0, TASK_BUF_SIZE);

					// Now end task (ie, just sleep for a really long time)
					while (1) OSTimeDlyHMSM(1,0,0,0);
				}
            }

		}
	}
	return;
}

uint8_t TurnerC3_Tx(PTURNER_C3_TASK pTaskInfo, uint8_t* pBuffer, uint8_t len)
{
	uint8_t retval = FAIL;
	uint8_t err = OS_ERR_NONE;
	uint16_t checksum;

	checksum = TurnerC3_CheckSum(pBuffer, len);
	checksum = TurnerC3_Endian2ByteSwap(checksum);
	
	for(int i=0; i<len; i++)
	{
		err = OSuartPutCharWait(pTaskInfo->portNum, *pBuffer, INFINITE);
		pBuffer++;
		if(err != OS_ERR_NONE)
			break;
	}		
	
	pBuffer = (uint8_t*)&checksum;

	if(err == OS_ERR_NONE)
	{
		for(int i=0; i<2; i++)
		{
			err = OSuartPutCharWait(pTaskInfo->portNum, *pBuffer, INFINITE);
			pBuffer++;
			if(err != OS_ERR_NONE)
				break;
		}		
	}

	if(err==OS_ERR_NONE)
		retval = SUCCESS;

	return retval;
}


uint8_t TurnerC3_Rx(PTURNER_C3_TASK pTaskInfo, char* pBuffer, uint16_t* pLen, char* eol, int eolLen)
{
	uint8_t retval = FAIL;
	char* pChar = pBuffer;
	uint16_t count = 0;
	BOOL done = FALSE;
	int compare = 0;
	uint8_t err = 0;
	char c = 0;


	c = (char) OSuartGetCharWait(pTaskInfo->portNum, 64, &err);	//Wait 0.5 seconds for first character
			
	if(err != OS_ERR_NONE)
		{
			*pLen = 0;
			return retval;
		}

	*pChar = c;
	pChar++;
	count++;

	while((done == FALSE) && (count<110))
	{
		c = (char) OSuartGetCharWait(pTaskInfo->portNum, 960, &err);	//Wait up to 7.5 seconds for characters (first line has delay in it)
			
		if(err != OS_ERR_NONE)
		{
			*pLen = count;
			return retval;
		}

		*pChar = c;
		pChar++;
		count++;

		if(count >= 1)
		{
			compare = strncmp((char *)(pChar - 1), eol, eolLen);
			if(compare == 0)
			{
				retval = SUCCESS;
				done = TRUE;
			}
		}
	}
	*pLen = count;

	return retval;
}


uint8_t TurnerC3_Rx_Len(PTURNER_C3_TASK pTaskInfo, char* pBuffer, uint16_t* pLen, uint16_t maxLen, uint16_t maxTimeout)
{
	uint8_t retval = FAIL;
	char* pChar = pBuffer;
	uint16_t count = 0;
	uint8_t err = 0;

	*pChar = (char) OSuartGetCharWait(pTaskInfo->portNum, maxTimeout, &err);	//Wait up to maxtimeout seconds for characters (first line has delay in it)
			
	if(err != OS_ERR_NONE)
	{
		*pLen = count;
		return retval;
	}

	pChar++;
	count++;

	while(count<maxLen)
	{
		*pChar = (char) OSuartGetCharWait(pTaskInfo->portNum, 32, &err);	//Wait up to 0.5 seconds for characters (first line has delay in it)
			
		if(err != OS_ERR_NONE)
		{
			*pLen = count;
			return retval;
		}

		pChar++;
		count++;
	}
	*pLen = count;
	retval = SUCCESS;

	return retval;
}



//void TurnerC3_GetDataSample(PTURNER_C3_TASK pTaskInfo)
//{
//	uint16_t len = 0;
//	char eol = 0x0A;
//	uint8_t boardID = (uint8_t)(mainTaskAddress>>8);
//	uint8_t retval = FAIL;
//
//	memset(pTaskInfo->pBuf->bufferRx, 0, 1024);
//	retval = TurnerC3_Rx(pTaskInfo, pTaskInfo->pBuf->bufferRx, &len, &eol, 1);
//
//	if(retval == SUCCESS)
//	{
//		memset(pTaskInfo->pBuf->fluorometerSample, ' ', 120);
//		sprintf_P(pTaskInfo->pBuf->fluorometerSample, PSTR("Bd: 0x%02x  Tsk: 0x%02x  Lat: %+09ld  Lon: %+010ld  "),
//					boardID, pTaskInfo->taskID, latitude, longitude); 
//		memcpy(&pTaskInfo->pBuf->fluorometerSample[54], pTaskInfo->pBuf->bufferRx, len - 2);
//		TurnerC3_DataReportXmit(pTaskInfo);
//	}
//}

//uint8_t TurnerC3_DataReportXmit(PTURNER_C3_TASK pTaskInfo)
//{
//	uint8_t retval = 0;
//	uint8_t err = 0;
//	PTURNER_C3_REPORT_XMIT pReport = NULL;
//
//	OSMutexPend(pMutexFloatQueue, INFINITE, &err);
//
//	pReport = (PTURNER_C3_REPORT_XMIT)&(txFloatData[headFloatTx]);
//
//	pReport->comm.headers.length  = sizeof(TURNER_C3_REPORT_XMIT) + CRC16_SIZE;
//	pReport->comm.headers.destination = FLOAT_IRIDIUM_INTERFACE;
//
//	pReport->comm.iridiumInfo.structureType = 0x80;
//	pReport->comm.iridiumInfo.size = pReport->comm.headers.length - sizeof(PIB_IRIDIUM_DATA_XMIT) - CRC16_SIZE;
//
//	pReport->payloadID = 0x00000050;
//
//	memcpy(&(pReport->fluorometerSample), &(pTaskInfo->pBuf->fluorometerSample), 120);
//
//
//	(headFloatTx < MAX_FLOAT_BUFFERS - 1) ? (headFloatTx++) : (headFloatTx = 0);
//
//	if(headFloatTx == tailFloatTx)
//	{
//		(tailFloatTx < MAX_FLOAT_BUFFERS - 1) ? (tailFloatTx++) : (tailFloatTx = 0);
//	}
//
//	OSMutexPost(pMutexFloatQueue);
//
//	return retval;
//}

uint8_t TurnerC3_DataReportXmit(PTURNER_C3_TASK pTaskInfo)
{
	uint8_t retval = 0;
	uint8_t err = 0;
	PTURNER_C3_DATA_REPORT_XMIT pReport = NULL;

	OSMutexPend(pMutexFloatQueue, INFINITE, &err);

	pReport = (PTURNER_C3_DATA_REPORT_XMIT)&(txFloatData[headFloatTx]);
	memset(pReport, 0, sizeof(FLOAT_TX_BUFFER));

	pReport->comm.headers.length  = sizeof(TURNER_C3_DATA_REPORT_XMIT) + CRC16_SIZE;
	pReport->comm.headers.destination = FLOAT_IRIDIUM_INTERFACE;

	pReport->comm.iridiumInfo.structureType = 0x91;
	pReport->comm.iridiumInfo.size = pReport->comm.headers.length - sizeof(PIB_IRIDIUM_DATA_XMIT) - CRC16_SIZE;


	memcpy(&(pReport->reportData), &(pTaskInfo->pBuf->reportData), sizeof(C3_DATA_REPORT));


	(headFloatTx < MAX_FLOAT_BUFFERS - 1) ? (headFloatTx++) : (headFloatTx = 0);

	if(headFloatTx == tailFloatTx)
	{
		(tailFloatTx < MAX_FLOAT_BUFFERS - 1) ? (tailFloatTx++) : (tailFloatTx = 0);
	}

	OSMutexPost(pMutexFloatQueue);

	return retval;
}

uint8_t TurnerC3_ASCIIReportXmit(PTURNER_C3_TASK pTaskInfo, uint8_t len)
{
	uint8_t retval = SUCCESS;
	uint8_t err = 0;
	PTURNER_C3_ASCII_REPORT_XMIT pReport = NULL;

	OSMutexPend(pMutexFloatQueue, INFINITE, &err);

	pReport = (PTURNER_C3_ASCII_REPORT_XMIT)&(txFloatData[headFloatTx]);

//	pReport->comm.headers.length  = sizeof(TURNER_C3_ASCII_REPORT_XMIT) + CRC16_SIZE;
	pReport->comm.headers.length  = sizeof(PIB_IRIDIUM_DATA_XMIT) + len + CRC16_SIZE;
	pReport->comm.headers.destination = FLOAT_IRIDIUM_INTERFACE;

	pReport->comm.iridiumInfo.structureType = 0x91;
	pReport->comm.iridiumInfo.size = pReport->comm.headers.length - sizeof(PIB_IRIDIUM_DATA_XMIT) - CRC16_SIZE;


	memcpy(&(pReport->reportASCII), &(pTaskInfo->pBuf->reportASCII), sizeof(C3_ASCII_REPORT));


	(headFloatTx < MAX_FLOAT_BUFFERS - 1) ? (headFloatTx++) : (headFloatTx = 0);

	if(headFloatTx == tailFloatTx)
	{
		(tailFloatTx < MAX_FLOAT_BUFFERS - 1) ? (tailFloatTx++) : (tailFloatTx = 0);
	}

	OSMutexPost(pMutexFloatQueue);

	return retval;
}


static char* ThreadSafeStrTok(char** context, char* sep)
{
	if (!context || !*context) return "";

	char* retval = &(*context)[strspn(*context, sep)];
	char* eos = &retval[strcspn(retval, sep)];

	if (*eos) *eos++ = '\0';

	*context = eos;
	return retval;
}

//static char* ThreadSafeStrTokTail(char** context, char* sep)
//{
//	if (!context || !*context) return "";
//
//	char* retval = &(*context)[strspn(*context, sep)];
//	
//	*context = &retval[strlen(retval)];
//	return retval;
//}


#define TURNERC3_INVALID_PARAMETERS		0x10
#define TURNERC3_POWER_OFF				0x20
#define TURNERC3_POWER_ON				0x30
#define TURNERC3_SAMPLING_OFF			0x40
#define TURNERC3_SAMPLING_ON			0x50
#define TURNERC3_CURRENT_STATE			0x60
#define TURNERC3_UNSUPPORTED_CMD		0x70

void TurnerC3_ParseConsole(PTURNER_C3_TASK pTaskInfo)
{
	char* myCommand = consoleIn[pTaskInfo->taskID].cmd; 
	char* result = NULL;
	char command = 0;
	BOOL goodCommand = FALSE;
	uint8_t resultCode = 0;

	sprintf_P(dbgMsg, PSTR("Parsing console: <%s>"), myCommand); SEND_MSG;

	myCommand[MAX_FILE_SIZE_IN - 1] = 0;			//Make sure command string is NULL terminated

	memset(pTaskInfo->pBuf->tokenSeparatorList, 0, MAX_TOKEN_SEPARATORS);
	strcat_P(pTaskInfo->pBuf->tokenSeparatorList, tokenListConsole);

	result = strtok_r(myCommand, pTaskInfo->pBuf->tokenSeparatorList, &myCommand);
	resultCode = 1;				//No Command Received
	
	if(result != NULL)
	{
		resultCode = 2;			//Unknown Parser Code
		if(strncasecmp_P(result, c3ParserID, strlen_P(c3ParserID)) == 0)
		{
			resultCode = 3;		//Unknown command

			command = getCommand(&myCommand, c3Commands[0], 8, NUM_C3_COMMANDS, c3CmdList, pTaskInfo->pBuf->tokenSeparatorList);

			if(command != 0)
				goodCommand = TRUE;
		}
	}

	if(goodCommand == TRUE)
	{
		sprintf_P(dbgMsg, PSTR("good message")); SEND_MSG;
		resultCode = TurnerC3_ParseCommand(pTaskInfo, command, myCommand);
	}
	else
	{
		sprintf_P(dbgMsg, PSTR("bad .. code=%d"), resultCode); SEND_MSG;
		sprintf_P(dbgMsg, PSTR("cmd = <%s><%s>"), myCommand, result); SEND_MSG;
	}

	if(resultCode != SUCCESS)
	{
		switch(resultCode)
		{
			case 1:
				pTaskInfo->pBuf->responseInfo.status = CONSOLE_RESPONSE_EMPTY_CMD;
				break;
			case 2:
				pTaskInfo->pBuf->responseInfo.status = CONSOLE_RESPONSE_WRONG_DEVICE;
				break;
			case 3:
				pTaskInfo->pBuf->responseInfo.status = CONSOLE_RESPONSE_UNKNOWN_CMD;
				break;
			default:
				pTaskInfo->pBuf->responseInfo.status = CONSOLE_RESPONSE_CMD_FAILED;
				break;
		}
	}

	TurnerC3_QueueConsoleResponse(pTaskInfo);

	memset(consoleIn[pTaskInfo->taskID].cmd, 0, MAX_FILE_SIZE_IN);
	consoleIn[pTaskInfo->taskID].cmdSrcAddress = 0;
	consoleIn[pTaskInfo->taskID].functionCode = 0;
	consoleIn[pTaskInfo->taskID].cmdSize = 0;
	consoleIn[pTaskInfo->taskID].id = 0;

	return;
}


void TurnerC3_QueueConsoleResponse(PTURNER_C3_TASK pTaskInfo)
{
	PCONSOLE_ACK_XMIT_2 pReport;
	uint8_t err = 0;
	char* pData;

	OSMutexPend(pMutexFloatQueue, INFINITE, &err);

	pReport = (PCONSOLE_ACK_XMIT_2)&(txFloatAck[headFloatAckTx]);
	pData = (char*)pReport;

	pReport->comm.headers.destination = FLOAT_TASK_FORWARD_MESSAGE;		//pTaskInfo->pBuf->responseInfo.source;

	pReport->comm.fwdInfo.msgFormat = MSG_FORMAT_SHIP_TO_SHORE;
	pReport->comm.fwdInfo.destination = pTaskInfo->pBuf->responseInfo.source;
	pReport->comm.fwdInfo.source = mainTaskAddress | pTaskInfo->taskID;

	if(pTaskInfo->pBuf->responseInfo.status < CONSOLE_RESPONSE_UNKNOWN_CMD)
	{
		pReport->comm.headers.length = sizeof(PIB_DATA_FORWARD_MESSAGE) + sizeof(CONSOLE_ACK) + pTaskInfo->pBuf->responseInfo.length + CRC16_SIZE;

		pReport->comm.iridiumInfo.headerICD.type = ACK_CONSOLE_DATA;

		pReport->consoleAck.len = pTaskInfo->pBuf->responseInfo.length;
		pReport->consoleAck.disposition = pTaskInfo->pBuf->responseInfo.status;
		pReport->consoleAck.format = pTaskInfo->pBuf->responseInfo.format;
		pData = pData + sizeof(PIB_DATA_FORWARD_MESSAGE) + sizeof(CONSOLE_ACK);
		memcpy(pData, pTaskInfo->pBuf->consoleResponse, pTaskInfo->pBuf->responseInfo.length);

		memcpy(dbgMsg, pTaskInfo->pBuf->consoleResponse, pTaskInfo->pBuf->responseInfo.length+1); SEND_MSG;
	}
	else
	{
		pReport->comm.headers.length = sizeof(PIB_DATA_FORWARD_MESSAGE) + sizeof(CONSOLE_NACK) + CRC16_SIZE;
		pReport->comm.iridiumInfo.headerICD.type = NACK_CONSOLE_DATA;
		pReport->consoleNack.numDispositions = 1;
		pReport->consoleNack.disposition[0] = pTaskInfo->pBuf->responseInfo.status;
	}

	pReport->comm.iridiumInfo.headerICD.packetID = pTaskInfo->pBuf->responseInfo.id;
	pReport->comm.iridiumInfo.headerICD.size = pReport->comm.headers.length - sizeof(PIB_DATA_FORWARD_MESSAGE) - CRC16_SIZE;


	(headFloatAckTx < MAX_ACK_BUFFERS - 1) ? (headFloatAckTx++) : (headFloatAckTx = 0);

	if(headFloatAckTx == tailFloatAckTx)
	{
		(tailFloatAckTx < MAX_ACK_BUFFERS - 1) ? (tailFloatAckTx++) : (tailFloatAckTx = 0);
	}

	OSMutexPost(pMutexFloatQueue);

	return;
}

uint8_t TurnerC3_ParseCommand(PTURNER_C3_TASK pTaskInfo, char command, char* myCommand)
{
	uint8_t retval = FAIL;

	sprintf_P(dbgMsg, PSTR("Parsing Command <%c> <%s>"), command, myCommand); SEND_MSG;
	
	switch (command)
	{
		case 't':
		case 'T':
			// State

			sprintf_P(dbgMsg, PSTR("STATE COMMAND")); SEND_MSG;
			retval = TURNERC3_CURRENT_STATE;

			break;

		case 'o':
		case 'O':
			// ON

			sprintf_P(dbgMsg, PSTR("ON")); SEND_MSG;

			if(pTaskInfo->pBuf->powerOn == FALSE)
				retval = TurnerC3_PowerOn(pTaskInfo);
			else
				retval = TURNERC3_POWER_ON;

			break;

		case 'f':
		case 'F':
			// OFF

			sprintf_P(dbgMsg, PSTR("OFF")); SEND_MSG;

			if(pTaskInfo->pBuf->powerOn == TRUE)
				retval = TurnerC3_PowerOff(pTaskInfo);
			else
				retval = TURNERC3_POWER_OFF;

			break;

		case 's':
		case 'S':
			// START

			sprintf_P(dbgMsg, PSTR("START")); SEND_MSG;
			if((pTaskInfo->pBuf->sampleC3 == FALSE) && (pTaskInfo->pBuf->powerOn == TRUE))
			{
				retval = TurnerC3_StartCurrentData(pTaskInfo);
			}
			else
			{
				retval = TURNERC3_SAMPLING_ON;
				if(pTaskInfo->pBuf->powerOn != TRUE)
					retval = TURNERC3_POWER_OFF;
			}

			break;
	
		case 'x':
		case 'X':
			// STOP

			sprintf_P(dbgMsg, PSTR("STOP")); SEND_MSG;

			if((pTaskInfo->pBuf->sampleC3 == TRUE) && (pTaskInfo->pBuf->powerOn == TRUE))
			{
				retval = TurnerC3_StopCurrentData(pTaskInfo);
			}
			else
			{
				retval = TURNERC3_SAMPLING_OFF;
				if(pTaskInfo->pBuf->powerOn != TRUE)
					retval = TURNERC3_POWER_OFF;
			}

			break;

		
		case 'c':
		case 'C':
			// CFG
			
			sprintf_P(dbgMsg, PSTR("CFG")); SEND_MSG;

			if((pTaskInfo->pBuf->sampleC3 == FALSE) && (pTaskInfo->pBuf->powerOn == TRUE))
			{
				memset(pTaskInfo->pBuf->tokenSeparatorList, 0, MAX_TOKEN_SEPARATORS);
				strcat_P(pTaskInfo->pBuf->tokenSeparatorList, tokenListConsole);

				char* subCommand = ThreadSafeStrTok(&myCommand, pTaskInfo->pBuf->tokenSeparatorList);
				if(strncasecmp_P(subCommand, PSTR("SET"), 3) == 0)
				{
					retval = TurnerC3_SetConfiguration(pTaskInfo, myCommand);
				}
				else if(strncasecmp_P(subCommand, PSTR("GET"), 3) == 0)
				{
					retval = TurnerC3_GetConfiguration(pTaskInfo, TRUE);
				}				
			}
			else
			{
				retval = TURNERC3_SAMPLING_ON;
				if(pTaskInfo->pBuf->powerOn != TRUE)
					retval = TURNERC3_POWER_OFF;
			}

			break;

		case 'a':
		case 'A':
			// CAL

			sprintf_P(dbgMsg, PSTR("CAL")); SEND_MSG;

			retval = TURNERC3_UNSUPPORTED_CMD;
//JK				if((pTaskInfo->pBuf->sampleC3 == FALSE) && (pTaskInfo->pBuf->powerOn == TRUE))
//JK				{
//JK
//JK					//Still need to fill this one in
//JK
//JK				}
//JK				else
//JK				{
//JK					retval = TURNERC3_SAMPLING_ON;
//JK					if(pTaskInfo->pBuf->powerOn != TRUE)
//JK						retval = TURNERC3_POWER_OFF;
//JK				}
//JK				

			break;

		case 'm':
		case 'M':
			// TIM

			sprintf_P(dbgMsg, PSTR("TIM")); SEND_MSG;

			if((pTaskInfo->pBuf->sampleC3 == FALSE) && (pTaskInfo->pBuf->powerOn == TRUE))
			{				
				memset(pTaskInfo->pBuf->tokenSeparatorList, 0, MAX_TOKEN_SEPARATORS);
				strcat_P(pTaskInfo->pBuf->tokenSeparatorList, tokenListConsole);

				char* subCommand = ThreadSafeStrTok(&myCommand, pTaskInfo->pBuf->tokenSeparatorList);
				if(strncasecmp_P(subCommand, PSTR("SET"), 3) == 0)
				{
					retval = TurnerC3_SetDateTime(pTaskInfo);
				}
				else if(strncasecmp_P(subCommand, PSTR("GET"), 3) == 0)
				{
					retval = TurnerC3_GetDateTime(pTaskInfo);
				}				
			}
			else
			{
				retval = TURNERC3_SAMPLING_ON;
				if(pTaskInfo->pBuf->powerOn != TRUE)
					retval = TURNERC3_POWER_OFF;
			}

			break;

		case 'w':
		case 'W':
			// WIP

			sprintf_P(dbgMsg, PSTR("WIP")); SEND_MSG;
			

			uint8_t revolutions = 0;

			if((pTaskInfo->pBuf->sampleC3 == FALSE) && (pTaskInfo->pBuf->powerOn == TRUE))
			{
				memset(pTaskInfo->pBuf->tokenSeparatorList, 0, MAX_TOKEN_SEPARATORS);
				strcat_P(pTaskInfo->pBuf->tokenSeparatorList, tokenListConsole);

				char* subCommand = ThreadSafeStrTok(&myCommand, pTaskInfo->pBuf->tokenSeparatorList);
				revolutions = atoi(subCommand);
				if(revolutions > 0)
				{
					retval = TurnerC3_GetConfiguration(pTaskInfo, FALSE);

					memcpy(&(pTaskInfo->pBuf->currentConfiguration), &(pTaskInfo->pBuf->bufferRx[2]), 22);
				
					if(retval == SUCCESS)
					{
						//Set new configuration for wiper
						retval = TurnerC3_StartCurrentData(pTaskInfo);
						if(retval == SUCCESS)
						{
							uint16_t len = 0;
							retval = FAIL;
							while(retval != SUCCESS)	//And we need some time out here too
							{
								retval = TurnerC3_Rx_Len(pTaskInfo, pTaskInfo->pBuf->bufferRx, &len, 24, QUARTER_SECOND);	//1/4 second timeout on getting current data back
							}
							pTaskInfo->pBuf->sampleC3 = FALSE;
						}
						memset(pTaskInfo->pBuf->bufferTx, 0, 30);
						memcpy(pTaskInfo->pBuf->bufferTx, pTaskInfo->pBuf->bufferRx, 22);
						retval = TurnerC3_SetConfiguration(pTaskInfo, (char*)NULL);
					}
				}				
			}
			else
			{
				retval = TURNERC3_SAMPLING_ON;
				if(pTaskInfo->pBuf->powerOn != TRUE)
					retval = TURNERC3_POWER_OFF;
			}

			break;


		case 'i':
		case 'I':
			// ID

			sprintf_P(dbgMsg, PSTR("ID")); SEND_MSG;

			if(pTaskInfo->pBuf->powerOn == TRUE)
			{
				memset(pTaskInfo->pBuf->tokenSeparatorList, 0, MAX_TOKEN_SEPARATORS);
				strcat_P(pTaskInfo->pBuf->tokenSeparatorList, tokenListConsole);

				char* subCommand = ThreadSafeStrTok(&myCommand, pTaskInfo->pBuf->tokenSeparatorList);
				if(strncasecmp_P(subCommand, PSTR("SET"), 3) == 0)
				{
					retval = TURNERC3_UNSUPPORTED_CMD;
				}
				else if(strncasecmp_P(subCommand, PSTR("GET"), 3) == 0)
				{
					retval = TurnerC3_XmitID(pTaskInfo);
					sprintf_P(dbgMsg, PSTR("retval = %d"), retval); SEND_MSG;
				}
			}
			else
				retval = TURNERC3_POWER_OFF;			

			break;

		default:
			return FAIL;
	}


	switch (retval)
	{
		case SUCCESS:
			memcpy_P(pTaskInfo->pBuf->consoleResponse, PSTR("OK"), 2);
			pTaskInfo->pBuf->responseInfo.status = CONSOLE_RESPONSE_OK;
			pTaskInfo->pBuf->responseInfo.length = 2;
			return SUCCESS;
			break;

		case TURNERC3_INVALID_PARAMETERS:
			memcpy_P(pTaskInfo->pBuf->consoleResponse, PSTR("Invalid Parameter List"), 22);
			pTaskInfo->pBuf->responseInfo.status = CONSOLE_RESPONSE_OK;
			pTaskInfo->pBuf->responseInfo.length = 22;
			return SUCCESS;
			break;

		case TURNERC3_POWER_OFF:
			memcpy_P(pTaskInfo->pBuf->consoleResponse, PSTR("C3 Power is Off -- Please turn it first"), 39);
			pTaskInfo->pBuf->responseInfo.status = CONSOLE_RESPONSE_OK;
			pTaskInfo->pBuf->responseInfo.length = 39;
			return SUCCESS;
			break;

		case TURNERC3_POWER_ON:
			memcpy_P(pTaskInfo->pBuf->consoleResponse, PSTR("C3 Power is already ON"), 22);
			pTaskInfo->pBuf->responseInfo.status = CONSOLE_RESPONSE_OK;
			pTaskInfo->pBuf->responseInfo.length = 22;
			return SUCCESS;
			break;

		case TURNERC3_SAMPLING_OFF:
			memcpy_P(pTaskInfo->pBuf->consoleResponse, PSTR("C3 Data Sampling is already Off"), 31);
			pTaskInfo->pBuf->responseInfo.status = CONSOLE_RESPONSE_OK;
			pTaskInfo->pBuf->responseInfo.length = 31;
			return SUCCESS;
			break;

		case TURNERC3_SAMPLING_ON:
			memcpy_P(pTaskInfo->pBuf->consoleResponse, PSTR("C3 Data Sampling is ON -- unable to process other commands"), 58);
			pTaskInfo->pBuf->responseInfo.status = CONSOLE_RESPONSE_OK;
			pTaskInfo->pBuf->responseInfo.length = 58;
			return SUCCESS;
			break;

		case TURNERC3_CURRENT_STATE:
			if((pTaskInfo->pBuf->powerOn == TRUE) && (pTaskInfo->pBuf->sampleC3 == TRUE))
			{
				memcpy_P(pTaskInfo->pBuf->consoleResponse, PSTR("Power is ON, Sampling is ON"), 27);
				pTaskInfo->pBuf->responseInfo.status = CONSOLE_RESPONSE_OK;
				pTaskInfo->pBuf->responseInfo.length = 27;
			}
			else if((pTaskInfo->pBuf->powerOn == TRUE) && (pTaskInfo->pBuf->sampleC3 == FALSE))
			{
				memcpy_P(pTaskInfo->pBuf->consoleResponse, PSTR("Power is ON, Sampling is OFF"), 28);
				pTaskInfo->pBuf->responseInfo.status = CONSOLE_RESPONSE_OK;
				pTaskInfo->pBuf->responseInfo.length = 28;
			}
			else if((pTaskInfo->pBuf->powerOn == FALSE) && (pTaskInfo->pBuf->sampleC3 == TRUE))
			{
				memcpy_P(pTaskInfo->pBuf->consoleResponse, PSTR("Power is OFF, Sampling is ON"), 28);
				pTaskInfo->pBuf->responseInfo.status = CONSOLE_RESPONSE_OK;
				pTaskInfo->pBuf->responseInfo.length = 28;
			}
			else if((pTaskInfo->pBuf->powerOn == FALSE) && (pTaskInfo->pBuf->sampleC3 == FALSE))
			{
				memcpy_P(pTaskInfo->pBuf->consoleResponse, PSTR("Power is OFF, Sampling is OFF"), 29);
				pTaskInfo->pBuf->responseInfo.status = CONSOLE_RESPONSE_OK;
				pTaskInfo->pBuf->responseInfo.length = 29;
			}
			else
			{
				memcpy_P(pTaskInfo->pBuf->consoleResponse, PSTR("System is in an unknown state"), 29);
				pTaskInfo->pBuf->responseInfo.status = CONSOLE_RESPONSE_OK;
				pTaskInfo->pBuf->responseInfo.length = 29;
			}
			return SUCCESS;
			break;

		case TURNERC3_UNSUPPORTED_CMD:
			memcpy_P(pTaskInfo->pBuf->consoleResponse, PSTR("That command is currently not supported"), 39);
			pTaskInfo->pBuf->responseInfo.status = CONSOLE_RESPONSE_UNKNOWN_CMD;
			pTaskInfo->pBuf->responseInfo.length = 39;
			break;

		case FAIL:
		default:
			memcpy_P(pTaskInfo->pBuf->consoleResponse, PSTR("Unrecognized Command"), 20);
			pTaskInfo->pBuf->responseInfo.status = CONSOLE_RESPONSE_UNKNOWN_CMD;
			pTaskInfo->pBuf->responseInfo.length = 20;
			break;


	}

	return retval;

}

uint8_t TurnerC3_ParseFile(PTURNER_C3_TASK pTaskInfo, uint32_t fileID)
{
	uint8_t retval = FAIL;
	uint16_t len = 0;
	char* pBuf;



	memset(&(fileOut[pTaskInfo->portNum][0]), 0, MAX_FILE_SIZE_OUT);

	strupr(&fileIn[pTaskInfo->portNum][0]);
	len = strlen(&fileIn[pTaskInfo->portNum][0]);

	pBuf = &fileIn[pTaskInfo->portNum][0];
	if(len > 4)
	{
		char* context = pBuf;
		char sep[5];		// = { ' ', '\t', '\r', '\n', '\0'};
//		char sep[] = { ' ', '\t', '\r', '\n', '\0'};
		char* cmdTok = ThreadSafeStrTok(&context, sep);

		memcpy_P(sep, separator2a, 5);
		if (strncmp_P(cmdTok, PSTR("CMD"), 3) == 0)
		{
			char* subCommand = ThreadSafeStrTok(&context, sep);

			//We have a Start/Stop command
			if(strncmp_P(subCommand, PSTR("STATE"), 5) == 0)
			{
				retval = TURNERC3_CURRENT_STATE;
			}
//			if(strncmp_P(subCommand, PSTR("ON"), 2) == 0)
			else if(strncmp_P(subCommand, PSTR("ON"), 2) == 0)
			{
				if(pTaskInfo->pBuf->powerOn == FALSE)
					retval = TurnerC3_PowerOn(pTaskInfo);
				else
					retval = TURNERC3_POWER_ON;
			}
			else if(strncmp_P(subCommand, PSTR("OFF"), 3) == 0)
			{
				if(pTaskInfo->pBuf->powerOn == TRUE)
					retval = TurnerC3_PowerOff(pTaskInfo);
				else
					retval = TURNERC3_POWER_OFF;
			}
			else if(strncmp_P(subCommand, PSTR("START"), 5) == 0)
			{
				if((pTaskInfo->pBuf->sampleC3 == FALSE) && (pTaskInfo->pBuf->powerOn == TRUE))
				{
					retval = TurnerC3_StartCurrentData(pTaskInfo);
				}
				else
				{
					retval = TURNERC3_SAMPLING_ON;
					if(pTaskInfo->pBuf->powerOn != TRUE)
						retval = TURNERC3_POWER_OFF;
				}
			}
			else if(strncmp_P(subCommand, PSTR("STOP"), 4) == 0)
			{
				if((pTaskInfo->pBuf->sampleC3 == TRUE) && (pTaskInfo->pBuf->powerOn == TRUE))
				{
					retval = TurnerC3_StopCurrentData(pTaskInfo);
				}
				else
				{
					retval = TURNERC3_SAMPLING_OFF;
					if(pTaskInfo->pBuf->powerOn != TRUE)
						retval = TURNERC3_POWER_OFF;
				}
			}
			else if(strncmp_P(subCommand, PSTR("CFG"), 3) == 0)
			{
				if((pTaskInfo->pBuf->sampleC3 == FALSE) && (pTaskInfo->pBuf->powerOn == TRUE))
				{
					subCommand = ThreadSafeStrTok(&context, sep);
					if(strncmp_P(subCommand, PSTR("SET"), 3) == 0)
					{
						retval = TurnerC3_SetConfiguration(pTaskInfo, context);
					}
					else if(strncmp_P(subCommand, PSTR("GET"), 3) == 0)
					{
						retval = TurnerC3_GetConfiguration(pTaskInfo, TRUE);
					}				
				}
				else
				{
					retval = TURNERC3_SAMPLING_ON;
					if(pTaskInfo->pBuf->powerOn != TRUE)
						retval = TURNERC3_POWER_OFF;
				}
			}
			else if(strncmp_P(subCommand, PSTR("CAL"), 3) == 0)
			{
				retval = TURNERC3_UNSUPPORTED_CMD;
//JK				if((pTaskInfo->pBuf->sampleC3 == FALSE) && (pTaskInfo->pBuf->powerOn == TRUE))
//JK				{
//JK
//JK					//Still need to fill this one in
//JK
//JK				}
//JK				else
//JK				{
//JK					retval = TURNERC3_SAMPLING_ON;
//JK					if(pTaskInfo->pBuf->powerOn != TRUE)
//JK						retval = TURNERC3_POWER_OFF;
//JK				}
//JK				
			}
			else if(strncmp_P(subCommand, PSTR("TIM"), 3) == 0)
			{
				if((pTaskInfo->pBuf->sampleC3 == FALSE) && (pTaskInfo->pBuf->powerOn == TRUE))
				{
					subCommand = ThreadSafeStrTok(&context, sep);
					if(strncmp_P(subCommand, PSTR("SET"), 3) == 0)
					{
						retval = TurnerC3_SetDateTime(pTaskInfo);
					}
					else if(strncmp_P(subCommand, PSTR("GET"), 3) == 0)
					{
						retval = TurnerC3_GetDateTime(pTaskInfo);
					}				
				}
				else
				{
					retval = TURNERC3_SAMPLING_ON;
					if(pTaskInfo->pBuf->powerOn != TRUE)
						retval = TURNERC3_POWER_OFF;
				}
			}
			else if(strncmp_P(subCommand, PSTR("WIP"), 3) == 0)
			{
				uint8_t revolutions = 0;

				if((pTaskInfo->pBuf->sampleC3 == FALSE) && (pTaskInfo->pBuf->powerOn == TRUE))
				{
					subCommand = ThreadSafeStrTok(&context, sep);
					revolutions = atoi(subCommand);
					if(revolutions > 0)
					{
						retval = TurnerC3_GetConfiguration(pTaskInfo, FALSE);

						memcpy(&(pTaskInfo->pBuf->currentConfiguration), &(pTaskInfo->pBuf->bufferRx[2]), 22);
					
						if(retval == SUCCESS)
						{
							//Set new configuration for wiper
							retval = TurnerC3_StartCurrentData(pTaskInfo);
							if(retval == SUCCESS)
							{
								uint16_t len = 0;
								retval = FAIL;
								while(retval != SUCCESS)	//And we need some time out here too
								{
									retval = TurnerC3_Rx_Len(pTaskInfo, pTaskInfo->pBuf->bufferRx, &len, 24, QUARTER_SECOND);	//1/4 second timeout on getting current data back
								}
								pTaskInfo->pBuf->sampleC3 = FALSE;
							}
							memset(pTaskInfo->pBuf->bufferTx, 0, 30);
							memcpy(pTaskInfo->pBuf->bufferTx, pTaskInfo->pBuf->bufferRx, 22);
							retval = TurnerC3_SetConfiguration(pTaskInfo, (char*)NULL);
						}
					}				
				}
				else
				{
					retval = TURNERC3_SAMPLING_ON;
					if(pTaskInfo->pBuf->powerOn != TRUE)
						retval = TURNERC3_POWER_OFF;
				}
			}
			else if(strncmp_P(subCommand, PSTR("ID"), 2) == 0)
			{
				if(pTaskInfo->pBuf->powerOn == TRUE)
				{
					subCommand = ThreadSafeStrTok(&context, sep);
					if(strncmp_P(subCommand, PSTR("SET"), 3) == 0)
					{
						retval = TURNERC3_UNSUPPORTED_CMD;
					}
					else if(strncmp_P(subCommand, PSTR("GET"), 3) == 0)
					{
						retval = TurnerC3_XmitID(pTaskInfo);
					}
				}
				else
					retval = TURNERC3_POWER_OFF;				
			}
		}
	}

	switch (retval)
	{
		case SUCCESS:
			memcpy_P(&fileOut[pTaskInfo->portNum][0], PSTR("OK"), 2);
			fileOutSize[pTaskInfo->portNum] = 2;
			break;
		case TURNERC3_INVALID_PARAMETERS:
			memcpy_P(&fileOut[pTaskInfo->portNum][0], PSTR("Invalid Parameter List"), 22);
			fileOutSize[pTaskInfo->portNum] = 22;
			break;
		case TURNERC3_POWER_OFF:
			memcpy_P(&fileOut[pTaskInfo->portNum][0], PSTR("C3 Power is Off -- Please turn it first"), 39);
			fileOutSize[pTaskInfo->portNum] = 39;
			break;
		case TURNERC3_POWER_ON:
			memcpy_P(&fileOut[pTaskInfo->portNum][0], PSTR("C3 Power is already ON"), 22);
			fileOutSize[pTaskInfo->portNum] = 22;
			break;
		case TURNERC3_SAMPLING_OFF:
			memcpy_P(&fileOut[pTaskInfo->portNum][0], PSTR("C3 Data Sampling is already Off"), 31);
			fileOutSize[pTaskInfo->portNum] = 31;
			break;
		case TURNERC3_SAMPLING_ON:
			memcpy_P(&fileOut[pTaskInfo->portNum][0], PSTR("C3 Data Sampling is ON -- unable to process other commands"), 58);
			fileOutSize[pTaskInfo->portNum] = 58;
			break;
		case TURNERC3_CURRENT_STATE:
			if((pTaskInfo->pBuf->powerOn == TRUE) && (pTaskInfo->pBuf->sampleC3 == TRUE))
			{
				memcpy_P(&fileOut[pTaskInfo->portNum][0], PSTR("Power is ON, Sampling is ON"), 27);
				fileOutSize[pTaskInfo->portNum] = 27;
			}
			else if((pTaskInfo->pBuf->powerOn == TRUE) && (pTaskInfo->pBuf->sampleC3 == FALSE))
			{
				memcpy_P(&fileOut[pTaskInfo->portNum][0], PSTR("Power is ON, Sampling is OFF"), 28);
				fileOutSize[pTaskInfo->portNum] = 28;
			}
			else if((pTaskInfo->pBuf->powerOn == FALSE) && (pTaskInfo->pBuf->sampleC3 == TRUE))
			{
				memcpy_P(&fileOut[pTaskInfo->portNum][0], PSTR("Power is OFF, Sampling is ON"), 28);
				fileOutSize[pTaskInfo->portNum] = 28;
			}
			else if((pTaskInfo->pBuf->powerOn == FALSE) && (pTaskInfo->pBuf->sampleC3 == FALSE))
			{
				memcpy_P(&fileOut[pTaskInfo->portNum][0], PSTR("Power is OFF, Sampling is OFF"), 29);
				fileOutSize[pTaskInfo->portNum] = 29;
			}
			else
			{
				memcpy_P(&fileOut[pTaskInfo->portNum][0], PSTR("System is in an unknown state"), 29);
				fileOutSize[pTaskInfo->portNum] = 29;
			}
			break;
		case TURNERC3_UNSUPPORTED_CMD:
			memcpy_P(&fileOut[pTaskInfo->portNum][0], PSTR("That command is currently not supported"), 39);
			fileOutSize[pTaskInfo->portNum] = 39;
			break;
		case FAIL:
		default:
			memcpy_P(&fileOut[pTaskInfo->portNum][0], PSTR("Unrecognized Command"), 20);
			fileOutSize[pTaskInfo->portNum] = 20;
			break;


	}

	retval = SUCCESS;


	TurnerC3_QueueFile(pTaskInfo, fileID);
	
	return retval;
}



uint8_t TurnerC3_QueueFile(PTURNER_C3_TASK pTaskInfo, uint32_t fileID)
{
	uint8_t retval = FAIL;
	PFS_REPORT pReport;
	uint8_t err = 0;
	tm* dateTime;
	time_t time;

	OSMutexPend(pMutexFloatQueue, INFINITE, &err);

	pReport = (PFS_REPORT)&(txFloatData[headFloatTx]);

	memset(pReport, 0, sizeof(FS_REPORT));

	pReport->headers.length  = sizeof(FS_REPORT) + CRC16_SIZE;
	pReport->headers.destination = FLOAT_FS_INTERFACE;
	pReport->type = NEW_FILE_AVAILABLE;

	time=gettime();

	//Need to fill in these values appropriately
	pReport->newFile.fileSize = fileOutSize[pTaskInfo->portNum];
	dateTime = gmtime(&time);
	pReport->newFile.creationDate.day = dateTime->tm_mday;
	pReport->newFile.creationDate.hour = dateTime->tm_hour;
	pReport->newFile.creationDate.minute = dateTime->tm_min;
	pReport->newFile.creationDate.month = dateTime->tm_mon + 1;
	pReport->newFile.creationDate.second = dateTime->tm_sec;
	pReport->newFile.creationDate.year = (uint8_t)(dateTime->tm_year - 100);
	dateTime = gmtime(&time);
	pReport->newFile.modifiedDate.day = dateTime->tm_mday;
	pReport->newFile.modifiedDate.hour = dateTime->tm_hour;
	pReport->newFile.modifiedDate.minute = dateTime->tm_min;
	pReport->newFile.modifiedDate.month = dateTime->tm_mon + 1;
	pReport->newFile.modifiedDate.second = dateTime->tm_sec;
	pReport->newFile.modifiedDate.year = (uint8_t)(dateTime->tm_year - 100);

	pReport->newFile.fileAttributes = 0;
	memset(&pReport->newFile.calculatedMD5, 0, sizeof(MD5));
	memset(&pReport->newFile.providedMD5, 0, sizeof(MD5));
	pReport->newFile.calculatedCRC = 0;
	pReport->newFile.providedCRC = 0;
	pReport->newFile.storageControllerAddress = mainTaskAddress | pTaskInfo->portNum;

	char fileName[13];
	sprintf(fileName, "%.8lX.txt", fileID);

	DotToPad(pReport->newFile.fileName, fileName);

	(headFloatTx < (MAX_FLOAT_BUFFERS - 1)) ? (headFloatTx++) : (headFloatTx = 0);
	
	if(headFloatTx == tailFloatTx)
	{
		(tailFloatTx < (MAX_FLOAT_BUFFERS - 1)) ? (tailFloatTx++) : (tailFloatTx = 0);
	}

	OSMutexPost(pMutexFloatQueue);

	return retval;
}









uint8_t TurnerC3_HeartBeat(PTURNER_C3_TASK pTaskInfo)
{
	uint8_t cmd[2];
	uint8_t retval = FAIL;
	uint8_t maxLen = 0;
	uint16_t len = 0;

	cmd[0] = 0;
	cmd[1] = 0;

	TurnerC3_Rx_Len(pTaskInfo, pTaskInfo->pBuf->bufferRx, &len, 100, 2 * QUARTER_SECOND);	//1 second timeout on heartbeat
	memset(pTaskInfo->pBuf->bufferRx, 0, 200);

	retval = TurnerC3_Tx(pTaskInfo, cmd, 2);

	if(retval == SUCCESS)
	{
		if(pTaskInfo->pBuf->heartbeatFirstTime == TRUE)
			maxLen = 100;
		else
			maxLen = 4;

		retval = TurnerC3_Rx_Len(pTaskInfo, pTaskInfo->pBuf->bufferRx, &len, maxLen, 4 * QUARTER_SECOND);	//1 second timeout on heartbeat

		if(len != 0)
		{
			//if(pTaskInfo->pBuf->heartbeatFirstTime == TRUE)
			//pTaskInfo->pBuf->heartbeatSecondTime = TRUE;
			pTaskInfo->pBuf->heartbeatCount++;
			pTaskInfo->pBuf->heartbeatFirstTime = FALSE;
		}
		
		switch(pTaskInfo->pBuf->heartbeatCount)
		{
			case 2:
				TurnerC3_GetID(pTaskInfo);
				break;
			case 3:
				TurnerC3_GetCalibration(pTaskInfo, 1);
				break;
			case 4:
				TurnerC3_GetCalibration(pTaskInfo, 2);
				break;
			case 5:
				TurnerC3_GetCalibration(pTaskInfo, 3);
				TurnerC3_XmitID(pTaskInfo);
				break;
			case 6:
				TurnerC3_GetConfiguration(pTaskInfo, TRUE);
				break;
		}

//		if(pTaskInfo->pBuf->heartbeatCount == 2)
//		{
//			retval = TurnerC3_GetID(pTaskInfo);
//		}
//		if(pTaskInfo->pBuf->heartbeatCount == 3)
//		{
//			retval = TurnerC3_GetCalibration(pTaskInfo);
//			retval = TurnerC3_XmitID(pTaskInfo);
//		}		
	}

	return retval;
}



void TurnerC3_GetCurrentData(PTURNER_C3_TASK pTaskInfo)
{
	uint16_t len = 0;
	uint16_t maxLen = 24;
	uint8_t retval = FAIL;
	PC3_CURRENT_DATA_BLOCK pData;
	uint16_t	halfDays;
	uint16_t	seconds;

#define X_COUNT pTaskInfo->pBuf->sampleCount
	memset(pTaskInfo->pBuf->bufferRx, 0, 1024);
	retval = TurnerC3_Rx_Len(pTaskInfo, pTaskInfo->pBuf->bufferRx, &len, maxLen, 2 * QUARTER_SECOND);	//1/2 second timeout on current data -- no need to wait if not available yet

	if(retval == SUCCESS)
	{
		pData = (PC3_CURRENT_DATA_BLOCK)(pTaskInfo->pBuf->bufferRx + 2);

		pTaskInfo->pBuf->reportData.dataBlock[X_COUNT].headerData.latitude = latitude;
		pTaskInfo->pBuf->reportData.dataBlock[X_COUNT].headerData.longitude = longitude;
		halfDays = TurnerC3_Endian2ByteSwap(pData->c3DateTime.halfdays);
		seconds = TurnerC3_Endian2ByteSwap(pData->c3DateTime.seconds);
		pTaskInfo->pBuf->reportData.dataBlock[X_COUNT].headerData.timestamp = halfDays * 43200 + seconds + JAN_1_2000;
		pTaskInfo->pBuf->reportData.dataBlock[X_COUNT].dataC1 = TurnerC3_Endian2ByteSwap(pData->sampleC1);
		pTaskInfo->pBuf->reportData.dataBlock[X_COUNT].dataC2 = TurnerC3_Endian2ByteSwap(pData->sampleC2);
		pTaskInfo->pBuf->reportData.dataBlock[X_COUNT].dataC3 = TurnerC3_Endian2ByteSwap(pData->sampleC3);
		pTaskInfo->pBuf->reportData.dataBlock[X_COUNT].pressure = TurnerC3_Endian2ByteSwap(pData->samplePres);
		pTaskInfo->pBuf->reportData.dataBlock[X_COUNT].temperature = TurnerC3_Endian2ByteSwap(pData->sampleTemp);

		X_COUNT++;

		if(X_COUNT >= MAX_C3_SAMPLES)
		{
			pTaskInfo->pBuf->reportData.headerReport.payloadType = 0x00000060;
			pTaskInfo->pBuf->reportData.headerReport.boardID = mainBoardID;
			pTaskInfo->pBuf->reportData.headerReport.taskID = pTaskInfo->taskID;
			pTaskInfo->pBuf->reportData.headerReport.dataFormat = 8;
			pTaskInfo->pBuf->reportData.headerReport.parser = 0;
			pTaskInfo->pBuf->reportData.headerReport.priority = 2;
			pTaskInfo->pBuf->reportData.headerReport.repeatCount = X_COUNT;
			TurnerC3_DataReportXmit(pTaskInfo);

			memset(&(pTaskInfo->pBuf->reportData), 0, sizeof(C3_DATA_REPORT));
			X_COUNT = 0;
		}
	}
#undef X_COUNT
}



uint16_t TurnerC3_CheckSum(uint8_t* tempBuf, uint16_t len)
{
	uint16_t checksum = 0;
	uint32_t sum = 0;
	uint8_t hiByte;
	uint8_t loByte;

	for(int i = 0; i<len; i++)
	{
		sum += *tempBuf;
		tempBuf++;
	}
	checksum = (uint16_t)(sum & 0x0000FFFF);
	hiByte = HIBYTE(checksum);
	loByte = LOBYTE(checksum);
	checksum = MAKEWORD(hiByte, ((loByte + hiByte)*-1));

	return checksum;
}

uint16_t TurnerC3_Endian2ByteSwap(uint16_t val)
{
	uint16_t result = 0;

	result |= (LOBYTE(val)<<8);
	result |= (HIBYTE(val));

	return result;
}

uint32_t TurnerC3_Endian4ByteSwap(uint32_t val)
{
	uint32_t result = 0;
	uint16_t hiword = 0;
	uint16_t loword = 0;

	hiword = HIWORD(val);
	loword = LOWORD(val);

	result |= ((uint32_t)(LOBYTE(loword))<<24);
	result |= ((uint32_t)(HIBYTE(loword))<<16);
	result |= ((uint32_t)(LOBYTE(hiword))<<8);
	result |= ((uint32_t)(HIBYTE(hiword)));

	return result;
}


uint8_t TurnerC3_GetConfiguration(PTURNER_C3_TASK pTaskInfo, BOOL sendResult)
{
	uint8_t retval = FAIL;
	uint8_t cmd[2] = {0x03, 0x00};
	uint16_t len = 0;
//	uint8_t length = 0;
//	PC3_CFG_BLOCK pCfgBlock = (PC3_CFG_BLOCK)&pTaskInfo->pBuf->bufferRx[2];
//	uint16_t avgNumReadings = 0;
//	uint16_t numSamples = 0;
//	uint16_t samplePeriod = 0;
//	uint16_t powerOnDelay = 0;
	

	retval = TurnerC3_Tx(pTaskInfo, cmd, 2);

	if(retval == SUCCESS)
	{
		retval = TurnerC3_Rx_Len(pTaskInfo, pTaskInfo->pBuf->bufferRx, &len, 26, 20 * QUARTER_SECOND);	//5 second timout

		if((retval != SUCCESS) || (len != 26) || (pTaskInfo->pBuf->bufferRx[0] != 0x03))
		{
			retval = FAIL;
		}
	}

	if((retval == SUCCESS) && (sendResult == TRUE))
	{
		retval = TurnerC3_XmitConfiguration(pTaskInfo, (PC3_CFG_BLOCK)&pTaskInfo->pBuf->bufferRx[2]);
	}
	return retval;
}

prog_char c_P[] = {'N','N','N','N','N','N','N','N'};
prog_char p_P[] = {'O', 'f', 'f'};
prog_char on_P[] = {'O', 'n', ' '};

uint8_t TurnerC3_XmitConfiguration(PTURNER_C3_TASK pTaskInfo, PC3_CFG_BLOCK pCfgBlock)
{
	uint8_t retval = FAIL;
	uint16_t avgNumReadings = 0;
	uint16_t numSamples = 0;
	uint16_t samplePeriod = 0;
	uint16_t powerOnDelay = 0;
	uint8_t length = 0;

	char c[8];	// = {'N','N','N','N','N','N','N','N'};
//	char c[8] = {'N','N','N','N','N','N','N','N'};
	uint8_t g[8];
	char p[3];	// = {'O','f','f'};
//	char p[3]= {'O','f','f'};
	uint16_t mask = 0x00;

	memset(g, 0, 8);
	memcpy_P(c, c_P, 8);
	memcpy_P(p, p_P, 3);


	mask = TurnerC3_Endian2ByteSwap(pCfgBlock->channelMask);
	for(int i=0; i<8; i++)
	{
		if((mask & 0x01) == 1)
			c[i] = 'Y';
		mask = mask>>1;
		switch(pCfgBlock->gainControl[i])
		{
			case 0:
				g[i] = 1;
				break;
			case 1:
				g[i] = 10;
				break;
			case 2:
				g[i] = 100;
				break;
			default:
				g[i] = 0;
				break;
		}
	}
	if(pCfgBlock->idlePower == 1)
	{
		memcpy_P(p, on_P, 3);
		//p[0]='O';
		//p[1]='n';
		//p[2]=' ';
	}
#define X_CFG pTaskInfo->pBuf->reportASCII

	X_CFG.headerReport.payloadType = 0x00000060;
	X_CFG.headerReport.boardID = mainBoardID;
	X_CFG.headerReport.taskID = pTaskInfo->taskID;
	X_CFG.headerReport.dataFormat = 2;
	X_CFG.headerReport.parser = 1;
	X_CFG.headerReport.priority = 2;
	X_CFG.headerReport.repeatCount = 1;

	X_CFG.headerData.latitude = latitude;
	X_CFG.headerData.longitude = longitude;
	X_CFG.headerData.timestamp = gettime();

	memset(pTaskInfo->pBuf->reportASCII.data, 0, 165);

	avgNumReadings = TurnerC3_Endian2ByteSwap(pCfgBlock->numReadingsAvg);
	numSamples = TurnerC3_Endian2ByteSwap(pCfgBlock->numSamples);
	samplePeriod = TurnerC3_Endian2ByteSwap(pCfgBlock->period);
	powerOnDelay = TurnerC3_Endian2ByteSwap(pCfgBlock->powerOnDelay);

	sprintf_P(pTaskInfo->pBuf->reportASCII.data, PSTR("Samples:%d Period:%d On Delay:%d C1:%c C2:%c C3:%c C4:%c C5:%c C6:%c P:%c T:%c G1:%d G2:%d G3:%d G4:%d G5:%d G6:%d G7:%d G8:%d Avg#:%d Pwr:%c%c%c Wipes:%d"), 
				numSamples, samplePeriod, powerOnDelay, 
				c[0], c[1], c[2], c[3], c[4], c[5], c[6], c[7], 
				g[0], g[1], g[2], g[3], g[4], g[5], g[6], g[7], 
				avgNumReadings, p[0], p[1], p[2], pCfgBlock->numWiperRev);
#undef X_CFG

	length = sizeof(TYPE_91_REPORT_HEADER) + sizeof(TYPE_90_91_DATA_HEADER) + strlen(pTaskInfo->pBuf->reportASCII.data);
	retval = TurnerC3_ASCIIReportXmit(pTaskInfo, length);
	memset(&(pTaskInfo->pBuf->reportASCII), 0, sizeof(C3_ASCII_REPORT));


	return retval;
}

uint8_t TurnerC3_SetConfiguration(PTURNER_C3_TASK pTaskInfo, char* paramlist)
{
	uint8_t retval = TURNERC3_INVALID_PARAMETERS;
	char* param;
	char sep[5];	// = { ',', '\t', '\r', '\n', '\0'};
//	char sep[] = { ',', '\t', '\r', '\n', '\0'};
	int i = 0;
	BOOL done = FALSE;
	uint16_t tempWord = 0;
	uint8_t  tempByte = 0;
	BOOL sendResult = TRUE;
	uint16_t len = 0;

	memcpy_P(sep, separator2a, 5);

	if(paramlist == (char*)NULL)
	{
		memcpy(&pTaskInfo->pBuf->newConfiguration, &pTaskInfo->pBuf->currentConfiguration, sizeof(C3_CFG_BLOCK));
		retval = SUCCESS;
		sendResult = FALSE;
	}
	else
	{
#define X_CFG pTaskInfo->pBuf->newConfiguration
		while((i<15) && (done == FALSE))
		{
			param = ThreadSafeStrTok(&paramlist, sep);
			if(param == NULL)
			{
				done = TRUE;
			}
			else
			{
				switch(i)
				{
					case 0:
						tempWord = atoi(param);
						X_CFG.numSamples = TurnerC3_Endian2ByteSwap(tempWord);
						break;
					case 1:
						tempWord = atoi(param);
						X_CFG.period = TurnerC3_Endian2ByteSwap(tempWord);
						break;
					case 2:
						tempWord = atoi(param);
						X_CFG.powerOnDelay = TurnerC3_Endian2ByteSwap(tempWord);
						break;
					case 3:
						tempWord = atoi(param);
						X_CFG.channelMask = TurnerC3_Endian2ByteSwap(tempWord);
						break;
					case 4:
					case 5:
					case 6:
					case 7:
					case 8:
					case 9:
					case 10:
					case 11:
						if((*param < '0') || (*param > '9'))
						{
							X_CFG.gainControl[i-4] = 3;
						}
						else
						{
							tempByte = atoi(param);
							switch(tempByte)
							{
								case 1:
									X_CFG.gainControl[i-4] = 0;
									break;
								case 10:
									X_CFG.gainControl[i-4] = 1;
									break;
								case 100:
									X_CFG.gainControl[i-4] = 2;
									break;
								default:
									X_CFG.gainControl[i-4] = 3;
							}
						}
						break;
					case 12:
						tempWord = atoi(param);
						X_CFG.numReadingsAvg = TurnerC3_Endian2ByteSwap(tempWord);
						break;
					case 13:
						tempByte = atoi(param);
						X_CFG.idlePower = tempByte;
						break;
					case 14:
						tempByte = atoi(param);
						X_CFG.numWiperRev = tempByte;
						tempWord = TurnerC3_CheckSum((uint8_t*)(&X_CFG), sizeof(C3_CFG_BLOCK) - sizeof(uint16_t));
						X_CFG.cfgChecksum = TurnerC3_Endian2ByteSwap(tempWord);
						done = TRUE;
						sendResult = TRUE;
						retval = SUCCESS;
						break;
				}
			}
			i++;
		}
#undef X_CONFIG
	}

	if(retval == SUCCESS)
	{
		memset(pTaskInfo->pBuf->bufferTx, 0, 30);
		pTaskInfo->pBuf->bufferTx[0] = 3;
		pTaskInfo->pBuf->bufferTx[1] = 22;
		memcpy(&(pTaskInfo->pBuf->bufferTx[2]), &pTaskInfo->pBuf->newConfiguration, sizeof(C3_CFG_BLOCK));


		retval = TurnerC3_Tx(pTaskInfo, (uint8_t*)pTaskInfo->pBuf->bufferTx, sizeof(C3_CFG_BLOCK) + 2);

		if(retval == SUCCESS)
		{
			retval = TurnerC3_Rx_Len(pTaskInfo, pTaskInfo->pBuf->bufferRx, &len, 26, 20 * QUARTER_SECOND);	//5 second timout

			if((retval != SUCCESS) || (len != 26) || (pTaskInfo->pBuf->bufferRx[0] != 0x03))
			{
				retval = FAIL;
			}
			else
			{
				if(sendResult == TRUE)
					retval = TurnerC3_XmitConfiguration(pTaskInfo, (PC3_CFG_BLOCK)&pTaskInfo->pBuf->bufferRx[2]);
			}
		}
	}
	return retval;
}



uint8_t TurnerC3_GetCalibration(PTURNER_C3_TASK pTaskInfo, uint8_t channel)
{
	uint8_t 	retval = FAIL;
	uint8_t		cmd[3] = {0x05, 0x01, 0x00};
	uint16_t 	len = 0;
	char		*pSensorName;

//	for(int i=1; i<4; i++)
//	{
	cmd[2] = channel;	//i;

	OSTimeDlyHMSM(0,0,5,0);

	retval = TurnerC3_Rx_Len(pTaskInfo, pTaskInfo->pBuf->bufferRx, &len, 100, 2 * QUARTER_SECOND);	//2.5 second timeout

	memset(pTaskInfo->pBuf->bufferRx, 0, 200);

	retval = TurnerC3_Tx(pTaskInfo, cmd, 3);

	if(retval == SUCCESS)
	{
		retval = TurnerC3_Rx_Len(pTaskInfo, pTaskInfo->pBuf->bufferRx, &len, 57, 10 * QUARTER_SECOND);	//2.5 second timeout

		if((retval == SUCCESS) && (len == 57) && (pTaskInfo->pBuf->bufferRx[0] == 0x05))
		{
			switch(channel)
			{
				case 1:
					pSensorName = pTaskInfo->pBuf->nameC1;
					break;
				case 2:
					pSensorName = pTaskInfo->pBuf->nameC2;
					break;
				case 3:
					pSensorName = pTaskInfo->pBuf->nameC3;
					break;
				default:
					pSensorName = NULL;
			}
			if(pSensorName != NULL)
			{
				uint8_t mask = 1;

				mask = mask << (channel-1);
				memset(pSensorName, 0, 15);
				if((pTaskInfo->pBuf->sensorsInstalled & mask) != 0)
					memcpy(pSensorName, &pTaskInfo->pBuf->bufferRx[21], 15);
				else
					memcpy_P(pSensorName, PSTR("No Fluorometer "), 15);

				for(int j=0; j<15; j++)
				{
					if(pSensorName[j] == 0)
						pSensorName[j] = ' ';
				}
			}
		}
	}
//	}
	return retval;
}

uint8_t TurnerC3_SetCalibration(PTURNER_C3_TASK pTaskInfo)
{
	uint8_t retval = FAIL;

	return retval;
}


uint8_t TurnerC3_StartCurrentData(PTURNER_C3_TASK pTaskInfo)
{
	uint8_t retval = FAIL;
	uint8_t cmd[3] = {0x04, 0x01, 0x01};

	retval = TurnerC3_Tx(pTaskInfo, cmd, 3);

	pTaskInfo->pBuf->sampleC3 = TRUE;

	return retval;
}

uint8_t TurnerC3_StopCurrentData(PTURNER_C3_TASK pTaskInfo)
{
	uint8_t retval = FAIL;
	uint8_t cmd[3] = {0x04, 0x01, 0x00};

	retval = TurnerC3_Tx(pTaskInfo, cmd, 3);

	pTaskInfo->pBuf->sampleC3 = FALSE;

	return retval;
}


uint8_t TurnerC3_PowerOn(PTURNER_C3_TASK pTaskInfo)
{
	uint8_t retval = FAIL;
	uint8_t err = OS_ERR_NONE;
	char c = 0;

	switch(pTaskInfo->taskID)
	{
		case 1:
			BSET(PORTK, _BV(PK2));
			break;
		case 2:
			BSET(PORTH, _BV(PH6));
			break;
		case 3:
			BSET(PORTF, _BV(PF2));
			break;
	}

	EnablePIBModule(pTaskInfo->portNum, TRUE, FALSE);
	OSTimeDlyHMSM(0,0,0,500);


	switch(pTaskInfo->taskID)
	{
		case 1:
			BCLR(PORTL, _BV(PK2));
			break;
		case 2:
			BCLR(PORTH, _BV(PH6));
			break;
		case 3:
			BCLR(PORTF, _BV(PF2));
			break;
	}
	memset(pTaskInfo->pBuf->bufferRx, 0, 1024);

	err = OS_ERR_NONE;		
	while(err == OS_ERR_NONE)
	{
		c = (char) OSuartGetCharWait(pTaskInfo->portNum, 64, &err);		//wait .5 seconds -- basically clearing buffer
	}

	retval = SUCCESS;

	pTaskInfo->pBuf->powerOn = TRUE;
	pTaskInfo->pBuf->heartbeatFirstTime = TRUE;
//	pTaskInfo->pBuf->heartbeatSecondTime = FALSE;
	pTaskInfo->pBuf->heartbeatCount = 0;

	return retval;
}

uint8_t TurnerC3_PowerOff(PTURNER_C3_TASK pTaskInfo)
{
	EnablePIBModule(pTaskInfo->portNum, FALSE, FALSE);
	pTaskInfo->pBuf->powerOn = FALSE;

	return SUCCESS;
}



uint8_t TurnerC3_GetDateTime(PTURNER_C3_TASK pTaskInfo)
{
	uint8_t retval = FAIL;
	uint8_t	cmd[2] = {0x02, 0x00};
	uint16_t len = 0;
	uint32_t	c3Tick = 0;


	retval = TurnerC3_Tx(pTaskInfo, cmd, 2);

	if(retval == SUCCESS)
	{
		retval = TurnerC3_Rx_Len(pTaskInfo, pTaskInfo->pBuf->bufferRx, &len, 8, 8 * QUARTER_SECOND);	//2 second timeout

		if((retval == SUCCESS) && (len == 8) && (pTaskInfo->pBuf->bufferRx[0] == 0x02))
		{
			c3Tick = TurnerC3_ConvertToTick((uint8_t*)&pTaskInfo->pBuf->bufferRx[2]);
			retval = TurnerC3_Xmit_DateTime(pTaskInfo, c3Tick);
		}
	}
	return retval;
}


uint8_t TurnerC3_SetDateTime(PTURNER_C3_TASK pTaskInfo)
{
	uint8_t retval = FAIL;
	uint8_t cmd[6] = {0x02, 0x04, 0x00, 0x00, 0x00, 0x00};

	uint32_t myTick = 0;
	uint32_t c3Tick = 0;
	uint32_t temp1 = 0;
	uint16_t halfDays = 0;
	uint16_t seconds = 0;

	uint16_t len;

	myTick = (uint32_t)gettime();
	c3Tick = myTick - JAN_1_2000;

	temp1 = (c3Tick/43200);
	halfDays = (uint16_t)temp1;		//43200 seconds = 12 hours
	seconds = (uint16_t)(c3Tick - (temp1 * 43200));

	cmd[2] = HIBYTE(halfDays);
	cmd[3] = LOBYTE(halfDays);
	cmd[4] = HIBYTE(seconds);
	cmd[5] = LOBYTE(seconds);

	retval = TurnerC3_Tx(pTaskInfo, cmd, 6);

	if(retval == SUCCESS)
	{
		retval = TurnerC3_Rx_Len(pTaskInfo, pTaskInfo->pBuf->bufferRx, &len, 8, 8 * QUARTER_SECOND);	//2 second timeout

		if((retval == SUCCESS) && (len == 8) && (pTaskInfo->pBuf->bufferRx[0] == 0x02))
		{
			c3Tick = TurnerC3_ConvertToTick((uint8_t*)&pTaskInfo->pBuf->bufferRx[2]);
			retval = TurnerC3_Xmit_DateTime(pTaskInfo, c3Tick);
			if(c3Tick != myTick)
				retval = FAIL;
		}
	}

	return retval;
}

uint32_t TurnerC3_ConvertToTick(uint8_t* pBuf)
{
	uint32_t halfDays = 0;
	uint32_t seconds = 0;
	uint32_t myTick = 0;

	uint8_t val1;
	uint8_t val2;

	val1 = *pBuf++;
	val2 = *pBuf++;
//	halfDays = (uint32_t)MAKEWORD(val1, val2);
	halfDays = (uint32_t)MAKEWORD(val2, val1);		//Params are Lo Byte, Hi Byte


	val1 = *pBuf++;
	val2 = *pBuf++;
//	seconds = (uint32_t)MAKEWORD(val1, val2);
	seconds = (uint32_t)MAKEWORD(val2, val1);		//Params are Lo Byte, Hi Byte


	myTick = (halfDays * 43200) + seconds + JAN_1_2000;

	return myTick;
}


uint8_t TurnerC3_SetID(PTURNER_C3_TASK pTaskInfo, char* pSerialNum, uint8_t len)
{
	uint8_t retval = FAIL;
	uint8_t cmd[10] = {0x02, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
	uint16_t rxLen = 0;

	if(len>8)
		len = 8;
	for(int i=0; i<len; i++)
	{
		cmd[i+2] = *pSerialNum;
		pSerialNum++;
	}

	retval = TurnerC3_Tx(pTaskInfo, cmd, 10);

	if(retval == SUCCESS)
	{
		retval = TurnerC3_Rx_Len(pTaskInfo, pTaskInfo->pBuf->bufferRx, &rxLen, 23, 8*QUARTER_SECOND);	//2 Second timeout

		if((retval == SUCCESS) && (rxLen == 23) && (pTaskInfo->pBuf->bufferRx[0] == 0x01))
		{
			retval = TurnerC3_Xmit_ID(pTaskInfo, (PC3_ID_BLOCK)&pTaskInfo->pBuf->bufferRx[2]);
		}
	}

	return retval;
}

uint8_t TurnerC3_GetID(PTURNER_C3_TASK pTaskInfo)
{
	uint8_t retval = FAIL;
	uint8_t	cmd[2] = {0x01, 0x00};
	uint16_t len = 0;


	OSTimeDlyHMSM(0,0,1,0);

	retval = TurnerC3_Rx_Len(pTaskInfo, pTaskInfo->pBuf->bufferRx, &len, 100, 2 * QUARTER_SECOND);	//2.5 second timeout
	memset(pTaskInfo->pBuf->bufferRx, 0, 200);

	retval = TurnerC3_Tx(pTaskInfo, cmd, 3);

	if(retval == SUCCESS)
	{
		retval = TurnerC3_Rx_Len(pTaskInfo, pTaskInfo->pBuf->bufferRx, &len, 23, 20*QUARTER_SECOND);		//5 second timeout

		if((retval == SUCCESS) && (len == 23) && (pTaskInfo->pBuf->bufferRx[0] == 0x01))
		{
			memset(pTaskInfo->pBuf->serialNum, 0, 8);
			memcpy(pTaskInfo->pBuf->serialNum, &pTaskInfo->pBuf->bufferRx[2], 8);
			for(uint8_t i=0; i<8; i++)
			{
				if(pTaskInfo->pBuf->serialNum[i] == 0)
					pTaskInfo->pBuf->serialNum[i] = ' ';
			}
			pTaskInfo->pBuf->majorFW = pTaskInfo->pBuf->bufferRx[10];
			pTaskInfo->pBuf->minorFW = pTaskInfo->pBuf->bufferRx[11];
			pTaskInfo->pBuf->majorComm = pTaskInfo->pBuf->bufferRx[19];
			pTaskInfo->pBuf->minorComm = pTaskInfo->pBuf->bufferRx[20];

			pTaskInfo->pBuf->sensorsInstalled = pTaskInfo->pBuf->bufferRx[13] & 0xC7;
			pTaskInfo->pBuf->wiper = 'N';
			pTaskInfo->pBuf->temperature = 'N';
			pTaskInfo->pBuf->pressure = 'N';
			if((pTaskInfo->pBuf->bufferRx[12] & 0x01) != 0)
				pTaskInfo->pBuf->wiper = 'Y';
			if((pTaskInfo->pBuf->sensorsInstalled & 0x80) != 0)
				pTaskInfo->pBuf->temperature = 'Y';
			if((pTaskInfo->pBuf->sensorsInstalled & 0x40) != 0)
				pTaskInfo->pBuf->pressure = 'Y';
			
			//retval = TurnerC3_Xmit_ID(pTaskInfo, (PC3_ID_BLOCK)&pTaskInfo->pBuf->bufferRx[2]);
		}
	}
	return retval;
}

uint8_t TurnerC3_Xmit_ID(PTURNER_C3_TASK pTaskInfo, PC3_ID_BLOCK pID)
{
	uint8_t retval = FAIL;
	uint16_t length = 0;
	char sn[8] = {0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20};
	char c[8] = {'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N'};
	char enabled = 'N';
	uint16_t sensors = TurnerC3_Endian2ByteSwap(pID->sensorsInstalled);

	for(int i = 0; i<8; i++)
	{
		if((sensors & 0x0001) == 1)
			c[i] = 'Y';
		sensors = sensors >> 1;
		if(pID->serialNum[i] != 0)
			sn[i] = pID->serialNum[i];
	}

	if(pID->datalogEnabled != 0)
		enabled = 'Y';

#define X_ID pTaskInfo->pBuf->reportASCII

	X_ID.headerReport.payloadType = 0x00000060;
	X_ID.headerReport.boardID = mainBoardID;
	X_ID.headerReport.taskID = pTaskInfo->taskID;
	X_ID.headerReport.dataFormat = 0;
	X_ID.headerReport.parser = 1;
	X_ID.headerReport.priority = 2;
	X_ID.headerReport.repeatCount = 1;

	X_ID.headerData.latitude = latitude;
	X_ID.headerData.longitude = longitude;
	X_ID.headerData.timestamp = gettime();

	memset(X_ID.data, 0, 165);

	sprintf_P(X_ID.data, PSTR("SN:%c%c%c%c%c%c%c%c fw:%d.%d, C1:%c C2:%c C3:%c C4:%c C5:%c C6:%c P:%c T:%c Log:%c, Size:%d Free:%d Comm:%d.%d"), 
				sn[0], sn[1], sn[2], sn[3], sn[4], sn[5], sn[6], sn[7], pID->fwMajor, pID->fwMinor, c[0], c[1], c[2], c[3], c[4], 
				c[5], c[6], c[7], enabled, TurnerC3_Endian2ByteSwap(pID->datalogSize), TurnerC3_Endian2ByteSwap(pID->datalogFree), 
				pID->commProtocolMajor, pID->commProtocolMinor);

	length = sizeof(TYPE_91_REPORT_HEADER) + sizeof(TYPE_90_91_DATA_HEADER) + strlen(X_ID.data);
	retval = TurnerC3_ASCIIReportXmit(pTaskInfo, length);
	memset(&(X_ID), 0, sizeof(C3_ASCII_REPORT));
#undef X_ID

	return retval;
}

uint8_t TurnerC3_Xmit_DateTime(PTURNER_C3_TASK pTaskInfo, uint32_t	c3Tick)
{
	uint8_t retval = FAIL;
	uint16_t length = 0;
	tm*	dateTime;

	time_t time = (time_t)c3Tick;

	dateTime = gmtime(&time);

#define X_DT pTaskInfo->pBuf->reportASCII

	X_DT.headerReport.payloadType = 0x00000060;
	X_DT.headerReport.boardID = mainBoardID;
	X_DT.headerReport.taskID = pTaskInfo->taskID;
	X_DT.headerReport.dataFormat = 1;
	X_DT.headerReport.parser = 1;
	X_DT.headerReport.priority = 2;
	X_DT.headerReport.repeatCount = 1;

	X_DT.headerData.latitude = latitude;
	X_DT.headerData.longitude = longitude;
	X_DT.headerData.timestamp = gettime();

	memset(X_DT.data, 0, 165);

	sprintf_P(X_DT.data, PSTR("%02d/%02d/%04d  %02d:%02d:%02d"),dateTime->tm_mon + 1, dateTime->tm_mday, (dateTime->tm_year + YEAR0), 
													dateTime->tm_hour, dateTime->tm_min, dateTime->tm_sec); 

	length = sizeof(TYPE_91_REPORT_HEADER) + sizeof(TYPE_90_91_DATA_HEADER) + strlen(X_DT.data);
	retval = TurnerC3_ASCIIReportXmit(pTaskInfo, length);
	memset(&(X_DT), 0, sizeof(C3_ASCII_REPORT));
#undef X_DT

	return retval;
}


uint8_t TurnerC3_XmitID(PTURNER_C3_TASK pTaskInfo)
{
	uint8_t retval = FAIL;
	uint16_t length = 0;

#define X_ID2 pTaskInfo->pBuf->reportASCII

	X_ID2.headerReport.payloadType = 0x00000060;
	X_ID2.headerReport.boardID = mainBoardID;
	X_ID2.headerReport.taskID = pTaskInfo->taskID;
	X_ID2.headerReport.dataFormat = 0;
	X_ID2.headerReport.parser = 1;
	X_ID2.headerReport.priority = 2;
	X_ID2.headerReport.repeatCount = 1;

	X_ID2.headerData.latitude = latitude;
	X_ID2.headerData.longitude = longitude;
	X_ID2.headerData.timestamp = gettime();

	memset(X_ID2.data, 0, 165);

	sprintf_P(X_ID2.data, PSTR("SN:00000000 fw:%02d.%02d Comm:%02d.%02d C1:............... C2:............... C3:............... P:%c T:%c W:%c"), 
				pTaskInfo->pBuf->majorFW, pTaskInfo->pBuf->minorFW, pTaskInfo->pBuf->majorComm, pTaskInfo->pBuf->minorComm, 
				pTaskInfo->pBuf->pressure, pTaskInfo->pBuf->temperature, pTaskInfo->pBuf->wiper);

	memcpy(&X_ID2.data[3], pTaskInfo->pBuf->serialNum, 8);

	memcpy(&X_ID2.data[35], pTaskInfo->pBuf->nameC1, 15);
	memcpy(&X_ID2.data[54], pTaskInfo->pBuf->nameC2, 15);
	memcpy(&X_ID2.data[73], pTaskInfo->pBuf->nameC3, 15);

	length = sizeof(TYPE_91_REPORT_HEADER) + sizeof(TYPE_90_91_DATA_HEADER) + strlen(X_ID2.data);
	retval = TurnerC3_ASCIIReportXmit(pTaskInfo, length);
	memset(&(X_ID2), 0, sizeof(C3_ASCII_REPORT));
#undef X_ID

	return retval;
}
