/****************************************************************************/
/* Copyright 2010 MBARI														*/
/****************************************************************************/
/* Summary	: Routines to save new config file for OASIS3					*/
/* Filename : cfgsave.c														*/
/* Author	: Robert Herlien (rah)											*/
/* Project	: Respirometer Upgrade											*/
/* Revision : $Revision: 1.1 $												*/
/* Created	: 05/14/2010													*/
/*																			*/
/* 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:													*/
/* 14may2010 rah - created													*/
/*
/* $Log: cfgsave.c,v $
/* Revision 1.1	 2011/10/25 22:39:21  bobh
/* Initial oasis4 revision
/*
/* Revision 1.1	 2010/05/17 18:48:45  bobh
/* Added cfgsave, more, ren commands
/*
*/
/****************************************************************************/

#include <mbariTypes.h>			/* MBARI type definitions				*/
#include <mbariConst.h>			/* MBARI constants						*/
#include <oasis.h>				/* OASIS controller definitions			*/
#include <list.h>				/* OASIS Linked List library			*/
#include <sem.h>				/* OASIS Semaphore library				*/
#include <task.h>				/* OASIS task dispatcher				*/
#include <serial.h>				/* OASIS Serial I/O						*/		
#include <drvr.h>				/* OASIS driver functions				*/
#include <config.h>				/* OASIS Config file parser definitions */
#include <cfgsave.h>			/* OASIS Config file save routines		*/
#include <parm.h>				/* Parameter table definitions			*/
#include <file.h>				/* OASIS File I/O Routines				*/
#include <utils.h>				/* OASIS utility functions				*/
#include <io.h>					/* OASIS I/O definitions				*/
#include <modbus.h>				/* Modbus protocol library				*/
#include <remote.h>				/* Remote I/O library					*/
#include <debug.h>

#include <cfxpico.h>			/* Persistor PicoDOS definitions		*/
#include <stdio.h>				/* Standard I/O library					*/
#include <stdlib.h>				/* Standard C library					*/
#include <stdarg.h>
#include <string.h>				/* Standard string library				*/
#include <ctype.h>				/* Standard ctypes						*/
#include <time.h>


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

Extern Parm_t			cfgSaves;		/* Number of OASISnnn.CFG files to save */
Extern const DrvDesc	drvDescs[];		/* Driver descriptors					*/
Extern int				numDrvDescs;	/* Size of drvDescs array				*/
Extern LstHead			drv_list;		/* Driver list							*/
Extern char				*logTypes[];	/* Log type identifiers					*/
Extern int				numLogTypes;	/* Size of the log type array			*/
Extern ModBusStruct		modBus[];		/* ModBus configuration					*/
Extern RemSerPort		serPorts[];		/* Remote serial ports					*/
Extern RemIOPort		pwrPorts[];		/* Remote power ports					*/
Extern RemIOPort		outPorts[];		/* Remote output ports					*/
Extern const Param		parmtbl[];		/* Parameter table						*/
Extern Nat32			numParmTbl;		/* Size of paramter table				*/
Extern const ParamArray parmArrayTbl[]; /* Parameter Array table				*/
Extern Nat32			numParmArrayTbl; /* Size of Paramter Array table		*/


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

MLocal const char * const cfgFileName = "OASIS.CFG";
MLocal const char * const cfgTmpName = "OASISCFG.TMP";
MLocal const char * const cfgBackName = "OASIS%03ld.CFG";
MLocal const char * const comment =
	"################################################################\r\n";


/************************************************************************/
/* Function	   : cserror												*/
/* Purpose	   : Error return function for this module					*/
/* Inputs	   : printf format and args									*/
/* Outputs	   : ERROR													*/
/************************************************************************/
MLocal Int16 cserror( const char *format, ... )
{
	va_list		ap;
	char		pbuf[PRINTBUF_SIZE];

	va_start(ap, format);
	giveFileSem();
	vsnprintf(pbuf, sizeof(pbuf), format, ap);
	xputs(pbuf);
	va_end(ap);
	return(ERROR);

} /* cserror() */


/************************************************************************/
/* Function	   : csfprintf												*/
/* Purpose	   : Perform fprintf to file, print error if failed			*/
/* Inputs	   : FILE ptr, printf format and args						*/
/* Outputs	   : OK or ERROR											*/
/************************************************************************/
MLocal Int16 csfprintf( FILE *fp, const char *format, ... )
{
	va_list		ap;
	int			rtn;

	va_start(ap, format);

	if ((rtn = vfprintf(fp, format, ap)) < 0)
	{
		fclose(fp);
		return(cserror("Error in vfprintf, rtn code = %d\n", rtn));
	}

	va_end(ap);
	return(OK);

} /* csfprintf() */


/************************************************************************/
/* Function	   : writeHeader											*/
/* Purpose	   : Write the header (comments) to the new oasis.cfg file	*/
/* Inputs	   : File ptr												*/
/* Outputs	   : OK or ERROR											*/
/************************************************************************/
MLocal Int16 writeHeader(FILE *fp)
{
	time_t		now;

	if (csfprintf(fp, "#\r\n## Respiromter Configuration File\r\n") != OK)
		return(ERROR);

	if (csfprintf(fp, "##\r\n## Automatically generated due to User request\r\n## ") != OK)
		return(ERROR);

	time(&now);
	if (csfprintf(fp, asctime(localtime(&now))) != OK)
		return(ERROR);

	if (csfprintf(fp, "#\r\n\r\n") != OK)
		return(ERROR);

	return(OK);

} /* writeHeader() */


/************************************************************************/
/* Function	   : findDrvrDesc											*/
/* Purpose	   : Find the DrvDesc matching an existing Driver			*/
/* Inputs	   : Driver Pointer											*/
/* Outputs	   : DrvDesc ptr or NULL									*/
/************************************************************************/
MLocal const DrvDesc *findDrvrDesc(Driver *dp)
{
	int			i;
	const DrvDesc *ddp;

	for (i = 0, ddp = drvDescs ; i < numDrvDescs; i++, ddp++)
		if (dp->drv_task == ddp->dd_task)
			return(ddp);

	return(NULL);
}

/************************************************************************/
/* Function	   : writeDrivers											*/
/* Purpose	   : Write the drivers to the new oasis.cfg file			*/
/* Inputs	   : File ptr												*/
/* Outputs	   : OK or ERROR											*/
/************************************************************************/
MLocal Int16 writeDrivers(FILE *fp)
{
	Driver				*dp;
	const DrvDesc		*ddp;
	char				*logType;
	char				logTypeBuf[32];

	if (csfprintf(fp, "%s#	Drivers\r\n%s", comment, comment) != OK)
		return(ERROR);

	for (dp = (Driver *)drv_list.lst_head; dp != DRV_NULL; dp = dp->drv_next)
		if ((ddp = findDrvrDesc(dp)) != NULL)
		{
			if (dp->drv_parms[SAMPLE_TYPE] < numLogTypes)
				logType = logTypes[dp->drv_parms[SAMPLE_TYPE]];
			else
			{			
				sprintf(logTypeBuf, "%ld", dp->drv_parms[SAMPLE_TYPE]);
				logType = logTypeBuf;
			}
			if (csfprintf(fp, "driver = %s,%s,%ld,%ld,%ld,%#lx,%ld,%s,%#lx,%ld,%ld,%ld,%ld\r\n\r\n",
						  ddp->dd_name, dp->drv_name, dp->drv_parms[INTERVAL],
						  dp->drv_parms[SER_PORT], dp->drv_parms[SER_BAUD], 
						  dp->drv_parms[SER_SETUP], dp->drv_parms[PWR_CTRL],
						  logType, dp->drv_parms[TOD_MASK],
						  dp->drv_parms[TIMEOUT], dp->drv_parms[PARM0],
						  dp->drv_parms[PARM1], dp->drv_parms[PARM1]) != OK)
				return(ERROR);
		}

	csfprintf(fp, "\r\n");
	return(OK);

} /* writeDrivers() */


/************************************************************************/
/* Function	   : writeModbus											*/
/* Purpose	   : Write the ModBus configuration to the new oasis.cfg file*/
/* Inputs	   : File ptr												*/
/* Outputs	   : OK or ERROR											*/
/************************************************************************/
MLocal Int16 writeModbus(FILE *fp)
{
	Int16		i;
	ModBusStruct *mbp;

	if (csfprintf(fp, "%s#	ModBus\r\n%s", comment, comment) != OK)
		return(ERROR);

	for (i = 1, mbp = &modBus[1]; i < PHYS_SER_PORTS; i++, mbp++)
		if (mbp->ident == IDENT)
			if (csfprintf(fp, "modbus = %d,%lu,%#lx\r\n",
						  i, mbp->baud, mbp->sermode) != OK)
				return(ERROR);
	
	csfprintf(fp, "\r\n\r\n");
	return(OK);

} /* writeModBus() */


/************************************************************************/
/* Function	   : writeRemote											*/
/* Purpose	   : Write the Remote configuration to the new oasis.cfg file*/
/* Inputs	   : File ptr												*/
/* Outputs	   : OK or ERROR											*/
/************************************************************************/
MLocal Int16 writeRemote(FILE *fp)
{
	Int16		i;
	RemSerPort	*rsp;
	RemIOPort	*rp;

	if (csfprintf(fp, "%s#	Virtual I/O Ports\r\n%s", comment, comment) != OK)
		return(ERROR);

	for (i = 0, rsp = serPorts; i < VIRT_SER_PORTS; i++, rsp++)
		if (rsp->assigned)
			if (csfprintf(fp, "serial = %u,%u,%u,%u\r\n",
						  i+PHYS_SER_PORTS, rsp->bus, rsp->slave, rsp->port) != OK)
				return(ERROR);
	csfprintf(fp, "\r\n");

	for (i = 0, rp = pwrPorts; i < VIRT_PWR_PORTS; i++, rp++)
		if (rp->assigned)
			if (csfprintf(fp, "power = %u,%u,%u,%u\r\n",
						  i+PHYS_PWR_PORTS, rp->bus, rp->slave, rp->coil) != OK)
				return(ERROR);
	csfprintf(fp, "\r\n");

	for (i = 0, rp = outPorts; i < VIRT_OUT_PORTS; i++, rp++)
		if (rp->assigned)
			if (csfprintf(fp, "output = %u,%u,%u,%u\r\n",
						  i, rp->bus, rp->slave, rp->coil) != OK)
				return(ERROR);

	csfprintf(fp, "\r\n\r\n");
	return(OK);

} /* writeRemote() */


/************************************************************************/
/* Function	   : writeParms												*/
/* Purpose	   : Write the global parameters to the new oasis.cfg file	*/
/* Inputs	   : File ptr												*/
/* Outputs	   : OK or ERROR											*/
/************************************************************************/
MLocal Int16 writeParms(FILE *fp)
{
	Int16		i, j, rtn;
	Parm_t		*p;
	const Param *paramP;
	const ParamArray	*paramAP;

	if (csfprintf(fp, "%s#	Global Parameters\r\n%s", comment, comment) != OK)
		return(ERROR);

	for (i = 0, paramP = parmtbl; i < numParmTbl; i++, paramP++)
	{
		switch(paramP->pt_type)
		{
		  case PARM_DEC:
			  rtn = csfprintf(fp, "%s = %ld\r\n", paramP->pt_name,
							  *(Parm_t *)(paramP->pt_parm));
			  break;

		  case PARM_HEX:
			  rtn = csfprintf(fp, "%s = %#lx\r\n", paramP->pt_name,
							  *(Parm_t *)(paramP->pt_parm));
			  break;

		  case PARM_STRING:
			  if ((*(char **)(paramP->pt_parm)) != NULL)
				  rtn = csfprintf(fp, "%s = %s\r\n",  paramP->pt_name, 
								  *(char **)(paramP->pt_parm));
			  break;

		  case PARM_BOOL:
			  rtn = csfprintf(fp, "%s = %s\r\n",  paramP->pt_name,
							  (*(MBool *)(paramP->pt_parm)) ? "TRUE" : "FALSE");
			  break;
		}
		if (rtn != OK)
			return(ERROR);
	}
	csfprintf(fp, "\r\n");

	for (i = 0, paramAP = parmArrayTbl; i < numParmArrayTbl; i++, paramAP++)
	{
		csfprintf(fp, "array = %s", paramAP->pt_name);
		p = (Parm_t *)(paramAP->pt_array);
		for (j = 0; j < paramAP->pt_size; j++)
			csfprintf(fp, ",%ld", *p++);
		csfprintf(fp, "\r\n");
	}

	csfprintf(fp, "\r\n\r\n");
	return(OK);

} /* writeParms() */


/************************************************************************/
/* Function	   : renameFiles											*/
/* Purpose	   : Rename backup files									*/
/* Inputs	   : None													*/
/* Outputs	   : OK or ERROR											*/
/************************************************************************/
MLocal Int16 renameFiles(Void)
{
	char		nameBuf[64], oldName[64];
	Int32		i;

	sprintf(nameBuf, cfgBackName, cfgSaves);
	remove(nameBuf);

	for (i = cfgSaves-1; i > 0; i--)
	{
		sprintf(oldName, cfgBackName, i);
		sprintf(nameBuf, cfgBackName, i+1);
		remove(nameBuf);
		rename(oldName, nameBuf);
	}

	sprintf(nameBuf, cfgBackName, 1L);
	if (rename(cfgFileName, nameBuf) != 0)
		return(cserror("Could not rename %s\n", cfgFileName));

	if (rename(cfgTmpName, cfgFileName) != 0)
		return(cserror("Could not rename %s\n", cfgTmpName));

	return(OK);

} /* renameFiles() */


/************************************************************************/
/* Function	   : usrSaveCfg												*/
/* Purpose	   : User command to save the config file					*/
/* Inputs	   : None													*/
/* Outputs	   : OK or ERROR											*/
/************************************************************************/
Int16 usrSaveCfg(Void)
{
	FILE		*cfgFp;
	Int16		rtn;

	takeFileSem();

	if ((cfgFp = fopen(cfgTmpName, "w")) == NULL)
		return(cserror("Cannot create %s\n", cfgTmpName));

	if (writeHeader(cfgFp) != OK)
		return(ERROR);

	writeModbus(cfgFp);
	writeRemote(cfgFp);
	writeDrivers(cfgFp);
	writeParms(cfgFp);

	if (fclose(cfgFp) != 0)
		return(cserror("Error on file close\n"));

	rtn = renameFiles();
	giveFileSem();
	return(rtn);

} /* usrSaveCfg() */
