/************************************************************************/
/* Copyright 2010 MBARI													*/
/************************************************************************/
/* Summary	: Functions to log modbus errors							*/
/* Filename : modbuserr.c												*/
/* Author	: Robert Herlien (rah)										*/
/* Project	: Respirometers												*/
/* Revision : 1.0														*/
/* Created	: 10/25/2010 from syslog.c									*/
/*																			*/
/* 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:												*/
/* 25oct2010 rah - created from syslog.c
*/
/************************************************************************/

#include <mbariTypes.h>					/* MBARI type definitions		*/
#include <mbariConst.h>					/* MBARI constants				*/
#include <oasis.h>						/* OASIS controller definitions	*/
#include <fatFs/ff.h>					/* FatFs file system defns		*/
#include <file.h>						/* OASIS File I/O Routines		*/
#include <task.h>						/* OASIS task dispatcher		*/
#include <timer.h>						/* OASIS clock routines			*/
#include <utils.h>						/* OASIS utility functions		*/
#include <modbus.h>						/* Modbus protocol library		*/
#include <modbusio.h>					/* Modbus I/O					*/
#include <istdio.h>						/* Our integer stdio			*/

#include <fatFs/ff.h>					/* FatFs file system defns		*/
#include <time.h>
#include <FreeRTOS.h>
#include <task.h>						/* FreeRTOS/include task.h		*/


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

Extern Nat32			modbusDebug;


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

MLocal MBool			modLogOpened = FALSE;	
MLocal const char		*modLogName = "modbus.txt";
MLocal const char		*modBakName = "modbus.%03u";

MLocal const char *unknownError = "Unknown error code";

MLocal const char *modBusErrors[] =
{
	/* Spec error codes */
	"Unknown error code", "Illegal function code", "Illegal data address",
	"Illegal data value", "Slave device or server failure",
	"Acknowledge", "Slave device or server busy", "Negative acknowledge",
	"Memory parity error", "Unknown error code", "Gateway path unavailable",
	"Target device failed to respond",
	/* Local error codes */
	"Send Failure", "Comm Timeout", "Invalid CRC", "Illegal Response",
	"Invalid Exception Code", "Illegal Port", "Data Size Error", "Port Error",
	"Receive Timeout", "Wrong Slave ID in Response",
	"Wrong Function code in Response", "Remote Serial Port Conflict",
	"Port in use"
};



/************************************************************************/
/* Function	   : openModLog												*/
/* Purpose	   : Open new modbus.txt file								*/
/* Inputs	   : None													*/
/* Outputs	   : OK or ERROR											*/
/* Comment	   : Assumes caller owns fileSem							*/
/************************************************************************/
MLocal Errno openModLog(void)
{
	int			i;
    FRESULT     fRes;
	char		oldName[32], newName[32];
	FIL			logfp;

	for (i = BACKUP_MODLOG_FILES-1; i; i--)
	{
		isprintf(newName, modBakName, i+1);
		isprintf(oldName, modBakName, i);
		f_unlink(newName);
		f_rename(oldName, newName);
	}

	f_rename(modLogName, oldName);
	fRes = f_open(&logfp, modLogName, (FA_WRITE | FA_CREATE_ALWAYS));
	if (fRes != FR_OK)
		return(ERROR);

	f_printf(&logfp, "ModBus log started\n\r");
	f_close(&logfp);
	modLogOpened = TRUE;

	return(OK);
	
} /* openModLog() */


/************************************************************************/
/* Function	   : modBusErrString										*/
/* Purpose	   : Get ASCII string representing modbus error				*/
/* Inputs	   : Error code												*/
/* Outputs	   : Error string											*/
/************************************************************************/
const char *modBusErrString(Errno errno)
{
	errno = -errno;

	if ((errno <= 0) || (errno > NUM_ERR_CODES))
		return(unknownError);
	else
		return(modBusErrors[errno]);
	
} /* modBusErrString() */


/************************************************************************/
/* Function	   : logModResult											*/
/* Purpose	   : Log result of ModBus transaction						*/
/* Inputs	   : ModBusHandle, return code from transaction				*/
/* Outputs	   : OK or ERROR											*/
/************************************************************************/
void logModResult(ModBusHandle mh, RemID *rp, Errno rtnCode, Nat16 sndLen)
{
    FRESULT     fRes;
	time_t		now;
	Nat16		ms, i;
	struct tm	*tmp;
	FIL			fp;

	if ((modbusDebug == 0) || ((rtnCode == OK) && (modbusDebug < 3)))
		return;

	if (rp->errs >= 1000)
		return;

	takeFileSem();

	if (!modLogOpened)
		if (openModLog() != OK)
		{
			giveFileSem();
			return;
		}

	fRes = f_open(&fp, modLogName, (FA_WRITE | FA_OPEN_APPEND));
	if (fRes != FR_OK)
	{
		giveFileSem();
		return;
	}

	clkGetTime(&now, &ms);
	tmp = gmtime(&now);

	f_printf(&fp, "%04u/%02u/%02u %02u:%02u:%02u.%03u %s Bus %u Slave %u %s ",
			 tmp->tm_year+1900, tmp->tm_mon+1, tmp->tm_mday,
			 tmp->tm_hour, tmp->tm_min, tmp->tm_sec, ms,
			 mh->busType == MB_SER ? "Serial" : "SPI",
			 mh->port, (Nat32)(mh->ioBuf->sndBuf[0]), pcTaskGetName(NULL));

	if (rtnCode != OK)
		f_printf(&fp, modBusErrString(rtnCode));

	if (rtnCode == RCV_TIMEOUT)
		f_printf(&fp, " got %u bytes ", (Nat32)mh->ioBuf->rcvLen);

	if ((modbusDebug >= 3) || ((modbusDebug == 2) && (rtnCode != OK)))
	{
		for (i=0; i < sndLen+2; i++)
			f_printf(&fp, " %02X", mh->ioBuf->sndBuf[i]);
		f_printf(&fp, " /");

		for (i=0; i < mh->ioBuf->rcvLen; i++)
			f_printf(&fp, " %02X", mh->ioBuf->rcvBuf[i]);
	}

	f_printf(&fp, "\n\r");
	f_close(&fp);
	giveFileSem();

} /* logModResult() */
