/****************************************************************************/
/* Copyright 2003 MBARI														*/
/****************************************************************************/
/* Summary	: Routines to parse config file for OASIS3						*/
/* Filename : config.c														*/
/* Author	: Robert Herlien (rah)											*/
/* Project	: OASIS Mooring Replacement (OASIS3)							*/
/* Revision : 1.0															*/
/* Created	: 08/14/2003													*/
/*																			*/
/* 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:													*/
/* 14aug2003 rah - created													*/
/*
 * Revision 1.10  2005/10/11 22:29:37  mrisi
 * swapped HYPER_OCR and AADERAA_O2 lables
 *
 * Revision 1.9	 2005/09/27 03:31:33  mrisi
 * added inductive ECO FLNTU driver
 *
 * Revision 1.8	 2005/08/30 20:49:03  mrisi
 * added support for Hyper OCR driver
 *
 * Revision 1.7	 2005/08/23 19:23:58  mrisi
 * moved AANDERAA_O2 before SPECIAL1 and SPECIAL2
 *
 * Revision 1.6	 2005/08/23 01:06:06  mrisi
 * added oasis logging for AANDERAA_O2
 *
 * Revision 1.5	 2004/09/08 17:16:02  headley
 * cosmetic
 *
 * Revision 1.4	 2004/09/02 16:47:09  headley
 * support for oasis3 groundfault, added missing references to support third microcat
 * to config.c
 *
 * Revision 1.3	 2004/03/01 19:04:47  headley
 * added support for second serial microcat
 *
 * Revision 1.2	 2003/09/03 17:48:16  bobh
 * Further mods for config file changes
 *
*/

/****************************************************************************/

#include <mbariTypes.h>			/* MBARI type definitions				*/
#include <mbariConst.h>			/* MBARI constants						*/
#include <oasis.h>				/* OASIS controller definitions			*/
#include <drvr.h>				/* OASIS driver functions				*/
#include <log.h>				/* Log record definitions				*/
#include <serial.h>				/* OASIS serial I/O definitions			*/
#include <parse.h>				/* Generic parser declarations			*/
#include <config.h>				/* OASIS Config file parser definitions */
#include <variable.h>			/* Variable definitions					*/
#include <parm.h>				/* Parameter table definitions			*/
#include <syslog.h>				/* Respirometer system logger			*/
#include <script.h>				/* Scripting Language definitions		*/
#include <task.h>				/* OASIS task dispatcher				*/
#include <utils.h>				/* OASIS utility functions				*/
#include <userif.h>				/* OASIS User Interface functions		*/
#include <drvTbl.h>				/* Default driver headers				*/
#include <io.h>					/* OASIS I/O definitions				*/
#include <remote.h>				/* Remote I/O library					*/
#include <modbus.h>				/* Modbus protocol library				*/
#include <debug.h>

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


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

MLocal char		cfgBuf[512];
MLocal char		cfgCopy[256];

MLocal StrValPair serSetup[] =
  { {"N81", (NO_PTY | BIT8 | STOP1)}, {"XON", FLOW}, {"ECHO", ECHO},
	{"BIGBUF", BIG_TX_BUF}, {"AUTOCR", AUTOCR}, {"NEWCR", NEWCR}, {"NOLF", NOLF}
  };

Global char		  *logTypes[] = 
  {"LOG_EMPTY", "OASIS_STAT", "ATLAS", "OASIS_CAN", "PAR", "CTD", 
   "SPECTRO", "ADCP", "GPS", "MODEM", "PCO2", "CTD2", "CTD3",
   "SPECTRO2", "SPECTRO3", "NO3", "NO32", "SPEC_PRR", "SATLANTIC",
   "GPS_TYPE2", "NRL", "O2", "FLUOR", "TRANSMISS", "Unused", "Unused",
   "AC9", "CO2_PUMP", "H2O_PUMP", "SHUTTER0", "SHUTTER1", "SPEC_PRR_VOLTS",
   "METSYS", "TSTRING", "GNDFAULT", "MICROCAT", "GPS_TYPE3", "HOBI_AB",
   "HOBI_CB", "HOBI_HS2", "HOBI_HR1", "HOBI_HR2", "HOBI_HR3", "HOBI_HR4",
   "HOBI_DAS", "GASHOUND", "ISUS", "EMETER", "SBE47_CT", "SBE43_O2",
   "HOBI_HR11", "HOBI_HR21", "HOBI_HR22", "HOBI_HR31", "HOBI_HR32",
   "HOBI_HR33", "HOBI_HR41", "HOBI_HR42", "HOBI_HR43", "HOBI_HR44",
   "SERIAL_SHUTTER", "ECO_FL", "ECO_BB", "ASIMET_BPR", "ASIMET_HRH",
   "ASIMET_LWR", "ASIMET_PRC", "ASIMET_SST", "ASIMET_SWR",	"ASIMET_WND",
   "MICROCAT2","MICROCAT3","O3GNDFAULT", "AANDERAA_O2", "HYPER_OCR", 
   "ECO_FLNTU","IM_HS2","GPSCLOCK","VAISALA1","IM01","IM02","IM03",
   "IM04","IM05","IM06","IM07","IM08","IM09","IM10","IM11","IM12",
   "IM13","IM14","IM15","IM16","AIRMAR_PB200","EXT_ANALOG1",
   "EXT_ANALOG2", "EXT_ANALOG3", "BINLOGGER", "BATTMON", "SPECIAL1", "SPECIAL2"
  };

Global char		  *extLogTypes[] = 
  {"LOG_EXTCOMMENT", "BINLOGGER16", "OASLAVE", "DURAFET_PH", "ISFET_PH"
  };

Global int numLogTypes = NumberOf(logTypes);
Global int numDrvDescs = NumberOf(drvDescs);

MLocal int numConfigFileErrors = 0;

MLocal Errno parseVirtualResource(char *cmdtail, 
								  Errno (*assignFunc)(char *,Nat32,Nat16,Nat16,Nat16,Flt32,Nat32));


/************************************************************************/
/* Function	   : parseConfigLine										*/
/* Purpose	   : Parse one line of OASIS.CFG							*/
/* Inputs	   : None													*/
/* Outputs	   : OK or ERROR											*/
/************************************************************************/
MLocal Errno parseConfigLine(Void)
{
  char			*keyword, *cmdtail;

#ifdef DEBUG_CONFIG
  printf("parseConfigLine(): \"%s\"\n", cfgBuf);
#endif

  if ((cmdtail = strchr(cfgBuf, '=')) != NULL)
  {
	keyword = strtok(cfgBuf, "	=\n");
	cmdtail++;
	deblank(cmdtail);

	if (*keyword == '$')
		return(parseVariable(keyword, cmdtail));

	if (cmp_ulc(keyword, "array") != NULL)
	  return(parseArray(cmdtail));
	
	if (cmp_ulc(keyword, "driver") != NULL)
	  return(parseDriver(cmdtail));

	if (cmp_ulc(keyword, "modbus") != NULL)
	  return(parseModBus(cmdtail));

	if (cmp_ulc(keyword, "serial") != NULL)
	  return(parseVirtualResource(cmdtail, assignVirtSerial));

	if (cmp_ulc(keyword, "power") != NULL)
	  return(parseVirtualResource(cmdtail, assignVirtPwr));

	if (cmp_ulc(keyword, "output") != NULL)
	  return(parseVirtualResource(cmdtail, assignVirtOutput));

	if (cmp_ulc(keyword, "script") != NULL)
	  return(parseScript(cmdtail));
	
	if (setParm(keyword, cmdtail) == OK)
	  return(OK);
  }

  return(executeCmdLine(cfgBuf));

} /* parseConfigLine() */


/************************************************************************/
/* Function	   : parseConfigFile										*/
/* Purpose	   : Parse the OASIS.CFG file								*/
/* Inputs	   : Name of config file									*/
/* Outputs	   : OK or ERROR											*/
/************************************************************************/
Errno parseConfigFile(char *cfgFileName)
{
  FILE			*cfgFp;
  char			*name = CFG_FILE;
  Errno			rtn;

  numConfigFileErrors = 0;
  initVars();
  initScript();
  if (cfgFileName != NULL)
	  name = cfgFileName;
  
  if ((cfgFp = fopen(name, "r")) == NULL)
  {
	printf("Can't open %s\n", name);
	return(ERROR);
  }

  while (getOneConfigLine(cfgFp, cfgBuf, sizeof(cfgBuf)) > 0)
  {
	  strncpy(cfgCopy, cfgBuf, sizeof(cfgCopy));
	  if ((rtn = parseConfigLine()) != OK)
	  {
		  numConfigFileErrors++;
		  sysLogPrintf("Error parsing \"%s\"\n", cfgCopy);
		  xprintf("Error parsing \"%s\"\n", cfgCopy);
	  }
  }

  if (numConfigFileErrors > 0)			/* If errors in cfg parsing		*/
	  remCreateErrorThread();			/* blink all remote LEDs		*/

  CIOdrain();
  fclose(cfgFp);
  return(OK);

} /* parseConfigFile() */


/************************************************************************/
/* Function	   : parseAddDefaultDrivers									*/
/* Purpose	   : Add the drivers in drv_default onto the driver list	*/
/* Inputs	   : None													*/
/* Outputs	   : OK or ERROR											*/
/************************************************************************/
Void parseAddDefaultDrivers(Void)
{
  Reg Nat16		i;

  for ( i = 0; i < NumberOf(drv_default); i++ )
	drvr_create(&drv_default[i]);

} /* parseAddDefaultDrivers() */


/************************************************************************/
/* Function	   : findDriver												*/
/* Purpose	   : Find driver by name									*/
/* Inputs	   : Driver name											*/
/* Outputs	   : Ptr to default DrvDesc, or NULL if not found			*/
/************************************************************************/
const DrvDesc *findDriver(char *drvName)
{
  Reg const DrvDesc		*ddp;
  Reg Nat16				i;

  for (i = 0, ddp = &drvDescs[0]; i < NumberOf(drvDescs); i++, ddp++)
	if (cmp_ulc(drvName, ddp->dd_name) != NULL)
	  return(ddp);

  return(NULL);

} /* findDriver() */


/************************************************************************/
/* Function	   : matchSerString											*/
/* Purpose	   : Match one of the serial mode strings					*/
/* Inputs	   : Ptr to token to match, ptr to value to return			*/
/* Outputs	   : Ptr to string following matched token					*/
/************************************************************************/
MLocal char *matchSerString(char *s, Nat32 *valp)
{
  Reg Nat16		i, len;
  Reg StrValPair *ssp;

  for (i = 0, ssp = serSetup; i < NumberOf(serSetup); i++, ssp++)
  {
	len = strlen(ssp->sv_string);
	if (strncasecmp(s, ssp->sv_string, len) == 0)
	{
	  *valp |= ssp->sv_val;
	  return(s + len);
	}
  }
  return(s);
  
} /* matchSerString() */


/************************************************************************/
/* Function	   : getLogType												*/
/* Purpose	   : Get Log (Sample) type									*/
/* Inputs	   : Ptr to token to match									*/
/* Outputs	   : Sample type, or -1 if none found						*/
/************************************************************************/
MLocal Int32 getLogType(char *tok)
{
  Reg Nat16		i;
  Reg char		*p;

  for (i = 0; i < NumberOf(logTypes); i++)
  {
	p = logTypes[i];
	if (cmp_ulc(tok, p) != NULL)
	  return(i);
  }

  if (cmp_ulc(tok, "LOG_ERROR") != NULL)
	return(LOG_ERROR);

  if (cmp_ulc(tok, "LOG_COMMENT") != NULL)
	return(LOG_COMMENT);
 
  for (i = 0; i < NumberOf(extLogTypes); i++)
  {
	p = extLogTypes[i];
	if (cmp_ulc(tok, p) != NULL)
	  return(i+256);
  }

  return(-1);
}


/************************************************************************/
/* Function	   : parseSerMode											*/
/* Purpose	   : Parse the serial mode token							*/
/* Inputs	   : Token													*/
/* Outputs	   : Value for SER_MODE										*/
/************************************************************************/
MLocal Nat32 parseSerMode(char *tok)
{
	Nat32		val = 0;
	char		c;

	if (isdigit((int)*tok & 0xff))
	  sscanNum(tok, (Parm_t *)&val);
	else
	{
	  c = tolower((int)*tok & 0xff);
	  tok++;
	  if (c == 'e')
		val |= EVEN_PTY;
	  if (c == 'o')
		val |= ODD_PTY;
	  if (*tok++ == '7')
		val |= BIT7;
	  if (*tok++ == '2')
		val |= STOP2;
	  while (*tok)
		if (*tok++ == '_')
		  tok = matchSerString(tok, &val);
	}

	return(val);

} /* parseSerMode() */

/************************************************************************/
/* Function	   : parseArray												*/
/* Purpose	   : Parse a "array = " line in config file					*/
/* Inputs	   : Ptr to remainder of parse line							*/
/* Outputs	   : OK or ERROR											*/
/************************************************************************/
Errno parseArray(char* cmdtail)
{
  Reg char		*tok;
  Reg int		i;
  Reg Nat16		size;

  Tokenizer		tokenizer;
  char			name[32];

  /* get array name */
  initTokenizer(&tokenizer, cmdtail);
  tok = getNextToken(&tokenizer);
  strncpy(name, tok, 32);

  /* lookup up array in parm array table */
  size = getParmArraySize(name);
  
  if ( size == 0 )
  {
	  printf("Array not found: \"%s\"\n", name);
	  return ERROR;
  }

  /* set array elements */
  for (i = 0; i < size; ++i)
  {
	  tok = getNextToken(&tokenizer);
	  
	  if ( tok == NULL )
		  setParmArray(name, "0", i);
	  else
		  setParmArray(name, tok, i);
  }

  return OK;

} /* parseArray() */

/************************************************************************/
/* Function	   : parseDriver											*/
/* Purpose	   : Parse a "driver = " line in config file				*/
/* Inputs	   : Ptr to remainder of parse line							*/
/* Outputs	   : OK or ERROR											*/
/************************************************************************/
Errno parseDriver(char *cmdtail)
{
  Reg const DrvDesc *ddp;
  Reg char		*tok;
  Tokenizer		tokenizer;
  Nat32			val;
  DrvDesc		newDriver;

  /* First find driver	*/
  initTokenizer(&tokenizer, cmdtail);
  tok = getNextToken(&tokenizer);
  
#ifdef DEBUG_CONFIG
  printf("parseDriver: tok = \"%s\"\n", tok);
#endif

  if ((ddp = findDriver(tok)) == NULL)
  {
	printf("Driver not found:  \"%s\"\n", tok);
	return(ERROR);
  }

  memcpy(&newDriver, ddp, sizeof(DrvDesc));

  tok = getNextToken(&tokenizer);
  if (isaTok(tok))
	newDriver.dd_name = tok;

  getDecToken(&tokenizer, &newDriver.dd_parms[INTERVAL]);

  getDecToken(&tokenizer, &newDriver.dd_parms[SER_PORT]);
  if (newDriver.dd_parms[SER_PORT] >= VIRT_SER_PORTS)
	newDriver.dd_parms[SER_PORT] = NO_SERIAL;

  val = MAX_BAUD+1;
  getDecToken(&tokenizer, &val);
  if (val <= MAX_BAUD)
	newDriver.dd_parms[SER_BAUD] = val;

  tok = getNextToken(&tokenizer);
  if (isaTok(tok))
	newDriver.dd_parms[SER_SETUP] = parseSerMode(tok);

  getDecToken(&tokenizer, &val);
  if (val < VIRT_PWR_PORTS)
	  newDriver.dd_parms[PWR_CTRL] = val;

  tok = getNextToken(&tokenizer);
  if (isaTok(tok))
  {
	  if (isdigit((int)*tok & 0xff))
	  sscanNum(tok, (Parm_t *)&val);
	else
	  val = getLogType(tok);
	if ((val >= 0) && (val < 65535))
	  newDriver.dd_parms[SAMPLE_TYPE] = val;
  }

  getDecToken(&tokenizer, &newDriver.dd_parms[TOD_MASK]);
  getDecToken(&tokenizer, &newDriver.dd_parms[TIMEOUT]);
  getDecToken(&tokenizer, &newDriver.dd_parms[PARM0]);
  getDecToken(&tokenizer, &newDriver.dd_parms[PARM1]);
  getDecToken(&tokenizer, &newDriver.dd_parms[PARM2]);
  getDecToken(&tokenizer, &newDriver.dd_parms[PARM3]);
  getDecToken(&tokenizer, &newDriver.dd_parms[PARM4]);
  getDecToken(&tokenizer, &newDriver.dd_parms[PARM5]);
  getDecToken(&tokenizer, &newDriver.dd_parms[PARM6]);
  getDecToken(&tokenizer, &newDriver.dd_parms[PARM7]);
  drvr_create(&newDriver);

  return(OK);

} /* parseDriver() */


/************************************************************************/
/* Function	   : parseModBus											*/
/* Purpose	   : Parse a "modbus = " line in config file				*/
/* Inputs	   : Ptr to remainder of parse line							*/
/* Outputs	   : OK or ERROR											*/
/************************************************************************/
Errno parseModBus(char *cmdtail)
{
  Reg char		*tok;
  Tokenizer		tokenizer;
  Nat32			serport=0, baud=DEFAULT_BAUD, sermode=DEFAULT_MODE, flags = 0;
  Flt32			surge = DFLT_SURGE/1000.;

#ifdef DEBUG_CONFIG
  printf("parseModBus: \"%s\"\n", cmdtail);
#endif

  initTokenizer(&tokenizer, cmdtail);

  getDecToken(&tokenizer, &serport);
  getDecToken(&tokenizer, &baud);

  tok = getNextToken(&tokenizer);
  if (isaTok(tok))
	sermode = parseSerMode(tok);

  getFltToken(&tokenizer, &surge);
  
  getDecToken(&tokenizer, &flags);

  return(modBusConfigure((Nat16)serport, baud, sermode, (Nat32)(surge*1000.), flags));

} /* parseModBus() */


/************************************************************************/
/* Function	   : parseVirtualResource									*/
/* Purpose	   : Parse a "serial = " or "power = " line in config file	*/
/* Inputs	   : Ptr to remainder of parse line							*/
/* Outputs	   : OK or ERROR											*/
/************************************************************************/
MLocal Errno parseVirtualResource(char *cmdtail, 
								  Errno (*assignFunc)(char *,Nat32,Nat16,Nat16,Nat16,Flt32,Nat32))
{
	Tokenizer	tokenizer;
	char		*name;
	Nat32		virtPort = 0, bus = 0, slave = 0, port = -1;
	Flt32		mult = DFLT_MULT, surge = DFLT_SURGE/1000.;

#ifdef DEBUG_CONFIG
	printf("parseVirtualResource: \"%s\"\n", cmdtail);
#endif

	initTokenizer(&tokenizer, cmdtail);

	name = getNextToken(&tokenizer);
	if (!isaTok(name))
		return(ERROR);

	if (!isdigit((int)*name & 0xff))
		getDecToken(&tokenizer, &virtPort);
	else
	{
		sscanNum(name, (Parm_t *)&virtPort);
		name = NULL;
	}

	getDecToken(&tokenizer, &bus);
	getDecToken(&tokenizer, &slave);
	getDecToken(&tokenizer, &port);
	getFltToken(&tokenizer, &mult);
	getFltToken(&tokenizer, &surge);

	return((*assignFunc)(name, virtPort, bus, slave, port, mult, (Nat32)(surge*1000.)));

} /* parseVirtualResource() */


/************************************************************************/
/* Function	   : getLogString											*/
/* Purpose	   : Convert log_type into a descriptive string				*/
/* Inputs	   : Log type												*/
/* Outputs	   : String describing log type								*/
/************************************************************************/
		char *
getLogString(Byte logType)
{
  if (logType == LOG_ERROR)
	return("LOG_ERROR");

  if (logType == LOG_COMMENT)
	return("LOG_COMMENT");

  if (logType < NumberOf(logTypes))
	return(logTypes[logType]);

  if ((logType >= 256) && (logType < NumberOf(extLogTypes)+256))
	return(extLogTypes[logType-256]);

  return("Unknown");

} /* getLogString() */

