/************************************************************************/
/* 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 <file.h>						/* OASIS File I/O Routines			*/
#include <task.h>						/* OASIS task dispatcher			*/
#include <clock.h>						/* OASIS clock routines				*/
#include <utils.h>						/* OASIS utility functions			*/
#include <modbus.h>						/* Modbus protocol library		*/
#include <debug.h>

#include <cfxpico.h>					/* Persistor PicoDOS definitions	*/
#include <stdio.h>						/* Standard I/O library				*/
#include <unistd.h>
#include <time.h>
#include <string.h>						/* String library functions			*/
#include <stdarg.h>
#include <errno.h>


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

Extern Nat32			modbusDebug;


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

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

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

MLocal const char *modBusErrors[] =
{
	/* Spec error codes */
	&unknownError, "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", &unknownError, "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)
{
	Int16		i;
	FILE		*logfp;
	char		oldName[32], newName[32];

	for (i = BACKUP_MODLOG_FILES-1; i; i--)
	{
		sprintf(newName, modBakName, i+1);
		sprintf(oldName, modBakName, i);
		unlink(newName);
		rename(oldName, newName);
	}

	rename(modLogName, oldName);
	if ((logfp = fopen(modLogName, "w+")) == NULL)
		return(ERROR);

	fprintf(logfp,"ModBus log started\n\r");
	fclose(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, Errno rtnCode, Nat16 sndLen)
{
	FILE		*fp;
	time_t		now;
	Nat16		pticks, i;
	struct tm	*tmp;
	SlaveCntr	*slavep;

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

	slavep = findSlaveCntr(mh, (Nat16)(mh->sndBuf[0]));
	if (slavep->errs >= 1000)
		return;

	takeFileSem();

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

	if ((fp = fopen(modLogName, "a+")) == NULL)
	{
		giveFileSem();
		return;
	}

	RTCGetTime(&now, &pticks);
	pticks /= (CFX_CLOCKS_PER_SEC/1000);		/* Convert Pers ticks to ms */
	tmp = gmtime(&now);

	fprintf(fp, "%04d/%02d/%02d %02d:%02d:%02d.%03hd Bus %hd Slave %hd %s ",
			tmp->tm_year+1900, tmp->tm_mon+1, tmp->tm_mday,
			tmp->tm_hour, tmp->tm_min, tmp->tm_sec, pticks,
			mh->serport, (Nat16)(mh->sndBuf[0]), task_get()->td_name);

	if (rtnCode != OK)
		fprintf(fp, modBusErrString(rtnCode));

	if (rtnCode == RCV_TIMEOUT)
		fprintf(fp, " got %hu bytes ", mh->rcvLen);

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

		for (i=0; i < mh->rcvLen; i++)
			fprintf(fp, " %.2X", mh->rcvBuf[i]);
	}

	fprintf(fp, "\n\r");
	fclose(fp);
	giveFileSem();

} /* logModResult() */
