/************************************************************************/
/* Copyright 2010 MBARI													*/
/************************************************************************/
/* Summary	: User Commands for remote serial and power ports			*/
/* Filename : usrRemoteCmds.c											*/
/* Author	: Robert Herlien (rah)										*/
/* Project	: Benthic/Midwater Respirometers (BRS/MRS)					*/
/* Revision: 1.0														*/
/* Created	: 08/31/2016												*/
/*																			*/
/* 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:												*/
/* 06may2010 rah - split off from remote.c								*/
/* 31aug2016 rah - created Oasis5 version from Oasis4 version			*/
/************************************************************************/

#include <mbariTypes.h>					/* MBARI type definitions		*/
#include <oasis.h>						/* OASIS controller definitions */
#include <custom.h>						/* Custom def's for deployment	*/
#include <dig_io.h>						/* OASIS I/O definitions		*/
#include <olist.h>						/* OASIS Linked List library	*/
#include <sem.h>						/* OASIS Semaphore library		*/
#include <drvr.h>						/* OASIS driver functions		*/
#include <parse.h>						/* Generic parser declarations	*/
#include <variable.h>					/* Variable definitions			*/
#include <utils.h>						/* OASIS Utility routines		*/
#include <modbus.h>						/* Modbus protocol library		*/
#include <modbusio.h>					/* Modbus (serial) I/O			*/
#include <remote.h>						/* Remote I/O library			*/
#include <usrRemoteCmds.h>				/* User funcs for Remote I/O	*/
#include <debug.h>

#include <istdio.h>						/* Our integer stdio library	*/
#include <FreeRTOS.h>					/* FreeRTOS definitions			*/

#include <stdio.h>						/* Standard I/O library			*/

//#include <stdlib.h>
#include <string.h>


/********************************/
/*		External Data			*/
/*		(from remote.c)			*/
/********************************/

Extern RemSerPort		*serPorts[];
Extern RemIOPort		*pwrPorts[];
Extern RemIOPort		*outPorts[];
Extern RemID			remIDs[];


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

MLocal char		*coilErrStr = "ERROR Reading Coil";
MLocal char		*onStr = "ON";
MLocal char		*offStr = "OFF";

MLocal char		*slaveTypes[] =
	{"", "Unknown", "Chamber Ctrlr", "New Chamber", "Sensor Node", "O5 Expander", "O5DB"};


/************************************************************************/
/* Function	   : busTypeStr												*/
/* Purpose	   : Return literal string "SPI" or "SER"					*/
/* Inputs	   : ModBusType												*/
/* Outputs	   : "SPI" or "SER"											*/
/************************************************************************/
const char *busTypeStr(ModBusType busType)
{
	return((busType == MB_SPI) ? "SPI" : "SER");
}


/************************************************************************/
/* Function	   : coilStateStr											*/
/* Purpose	   : Return literal string "ON" or "OFF"					*/
/* Inputs	   : ModBus bus, slave, coil number							*/
/* Outputs	   : "ON", "OFF", "BUS OFF", or "ERROR"						*/
/************************************************************************/
MLocal char *coilStateStr(RemID *idp, Nat16 coil)
{
	ModBusHandle		mh;
	Errno				rtn;
	Byte				state;

	if ((idp->busType == MB_SER) && !modBusIsPowered(idp->bus))
		return("BUS OFF");

	if ((mh = modBusOpen(idp)) == NULL)
		return(coilErrStr);

	rtn = readCoils(mh, idp, coil, 1, &state);
	modBusClose(mh);

	if (rtn != OK)
		return(coilErrStr);

	return((state & 1) ? onStr : offStr);
}


/************************************************************************/
/* Function	   : showRemote												*/
/* Purpose	   : Show status of remote/virtual ports					*/
/* Inputs	   : None													*/
/* Outputs	   : OK														*/
/************************************************************************/
CmdRtn showRemote(void)
{
	Nat32		i, j;
	RemSerPort	*rp;
	RemIOPort	*rip;
	RemID		*ridp;
	ModBusHandle mh;
	Nat16		coil;
	char		buff[16];

	for (i = 0; i < REMOTE_IDS; i++)
	{
		ridp = &remIDs[i];
		if (ridp->busType != MB_UNASSIGNED)
		{
			if ((ridp->busType == MB_SER) && modBusIsPowered(ridp->bus) &&
				(ridp->remType == RemUnknown))		 /* If don't yet know type,*/
				if ((mh = modBusOpen(ridp)) != NULL) /* Open modbus to find out*/
					modBusClose(mh);

			xprintf("%s on %s bus %u Slave ID %u nser %u npwr %u nio %u ",
					slaveTypes[ridp->remType], busTypeStr(ridp->busType),
					ridp->bus, ridp->slaveID, ridp->nSer, ridp->nPwr, ridp->nIO);
						
			if (ridp->busType == MB_SER)
				xprintf("%s", modBusIsPowered(ridp->bus) ? "ON" : "OFF");

			newline();

			for (j = 0; j < VIRT_SER_PORTS; j++)
			{
				rp = serPorts[j];
				if (rp && (rp->id == ridp))
				{
					xprintf("  Serial %u ", j+PHYS_SER_PORTS);
					if (rp->name[0] != '\0')
						xprintf("[%s] ", rp->name);
					if (rp->owner != NULL)
						xprintf("opened by %s ", pcTaskGetName(rp->owner));
					xprintf("on port %u\n", rp->port);
				}
			}

			for (j = 0; j < VIRT_PWR_PORTS; j++)
			{
				rip = pwrPorts[j];
				if (rip && (rip->id == ridp))
				{
					xprintf("  Power bit %2u ", j+PHYS_PWR_PORTS);
					if (rip->name[0] != '\0')
						xprintf("[%s] ", rip->name);
					sprintf(buff, "%.2f", rip->multiplier);
					coil = (rip->id->remType == RemOrigChamber) ? 
						PowerBitChamber(rip->coil) : PowerBit(rip->coil);
					xprintf("on port %u mult %s surge %u ms [%s]\n",
							rip->coil, buff, rip->surgeDelayMs, 
							coilStateStr(rip->id, coil));
				}
			}

			for (j = 0; j < VIRT_OUT_PORTS; j++)
			{
				rip = outPorts[j];
				if (rip && (rip->id == ridp))
				{
					xprintf("  Output bit %2u ", j);
					if (rip->name[0] != '\0')
						xprintf("[%s] ", rip->name);
					sprintf(buff, "%.2f", rip->multiplier);
					xprintf("on port %u mult %s surge %u ms [%s]\n",
							rip->coil, buff, rip->surgeDelayMs,
							coilStateStr(rip->id, OutputBit(rip->coil)));
				}
			}
		}
	}

	return(OK);

} /* showRemote() */


/************************************************************************/
/* Function	   : onOff													*/
/* Purpose	   : Look for "ON", "OFF", "1", or "0"						*/
/* Inputs	   : String													*/
/* Outputs	   : TRUE for ON, FALSE for OFF								*/
/************************************************************************/
MLocal MBool onOff(char *s)
{
	if (strcasecmp(s, "ON") == 0)
		return(TRUE);
	if (atoi(s) == 1)
		return(TRUE);
	return(FALSE);
}


/************************************************************************/
/* Function	   : usrRemPower											*/
/* Purpose	   : User I/F function to turn on/off a (remote) power bit	*/
/* Inputs	   : pmask, Power bit, bool for on/off						*/
/* Outputs	   : OK or ERROR											*/
/************************************************************************/
CmdRtn usrRemPower(ParmMask_t pmask, char *bitStr, char *onOffStr)
{
	Int16		bitNum;
	
	if (((pmask & 3) != 3) || 
		((bitNum = findRemPwrPort(bitStr)) == ERROR))
	{
		xprintf("Usage: remPower <bitNum> <On|Off>\n");
		return(ERROR);
	}

	return(onOff(onOffStr) ? pwrBitOn(bitNum) : pwrBitOff(bitNum));

} /* usrRemPower() */


/************************************************************************/
/* Function	   : usrRemOutput											*/
/* Purpose	   : User I/F function to turn on/off a remote output bit	*/
/* Inputs	   : pmask, Output bit, bool for on/off						*/
/* Outputs	   : OK or ERROR											*/
/************************************************************************/
CmdRtn usrRemOutput(ParmMask_t pmask, char *bitStr, char *onOffStr)
{
	Int16		bitNum;
	
	if (((pmask & 3) != 3) || 
		((bitNum = findRemOutputPort(bitStr)) == ERROR))
	{
		xprintf("Usage: remOutput <bitNum> <On|Off>\n");
		return(ERROR);
	}

	return(remOutput(bitNum, onOff(onOffStr)));

} /* usrRemOutput() */


/************************************************************************/
/* Function	   : usrRemOutPulse											*/
/* Purpose	   : User I/F function to pulse a remote output bit			*/
/* Inputs	   : pmask, Output bit, time in ms							*/
/* Outputs	   : OK or ERROR											*/
/************************************************************************/
CmdRtn usrRemOutPulse(ParmMask_t pmask, char *bitStr, char *timeStr)
{
	Int16		bitNum;
	Flt32		fltSecs;
	char		buff[16];

	if (((pmask & 3) != 3) || 
		((bitNum = findRemOutputPort(bitStr)) == ERROR) ||
		!getFltVal(timeStr, &fltSecs))
	{
		xprintf("Usage: remPulse <bitNum> <seconds>\n");
		return(ERROR);
	}

	sprintf(buff, "%.2f", fltSecs);
	xprintf("Pulsing remote output bit %d for %s secs\n", bitNum, buff);

	return(remOutPulseOn(bitNum, fltSecs));

} /* usrRemOutPulse() */


/************************************************************************/
/* Function	   : usrAssignSerial										*/
/* Purpose	   : User I/F function to assign a virtual serial port		*/
/* Inputs	   : pmask, virt serport number, bus, slave, port			*/
/* Outputs	   : OK or ERROR											*/
/* Comment     : Only used to assign Serial ModBus ports				*/
/************************************************************************/
CmdRtn usrAssignSerial(ParmMask_t pmask, Parm_t virtPort, Parm_t bus, Parm_t slave, Parm_t port)
{
	if ((pmask & 0xf) != 0xf)
	{
		xprintf("Usage: assignSerial <virtSerNum> <modBusNum> <slave> <port>\n");
		return(ERROR);
	}

	if (assignVirtSerial(NULL, virtPort, MB_SER, bus, slave, port, 0.0, 0) != OK)
	{
		xprintf("Bad port number reference or port already assigned\n");
		return(ERROR);
	}

	xprintf("SER Bus %u slave %u port %u assigned to virtual serial port %u\n",
			bus, slave, port, virtPort);
	return(OK);

} /* usrAssignSerial() */


/************************************************************************/
/* Function	   : usrAssignPower											*/
/* Purpose	   : User I/F function to assign a virtual power port		*/
/* Inputs	   : pmask, virt power number, bus, slave, port				*/
/* Outputs	   : OK or ERROR											*/
/* Comment     : Only used to assign Serial ModBus ports				*/
/************************************************************************/
CmdRtn usrAssignPower(ParmMask_t pmask, Parm_t virtPort, Parm_t bus, Parm_t slave, Parm_t port)
{
	if ((pmask & 0xf) != 0xf)
	{
		xprintf("Usage: assignPower <virtPwrNum> <modBusNum> <slave> <port>\n");
		return(ERROR);
	}

	if (assignVirtPwr(NULL, virtPort, MB_SER, bus, slave, port, 1.0, 0.0) != OK)
	{
		xprintf("Bad port number reference or port already assigned\n");
		return(ERROR);
	}

	xprintf("SER Bus %u slave %u port %u assigned to virtual power port %u\n",
			bus, slave, port, virtPort);
	return(OK);

} /* usrAssignPower() */


/************************************************************************/
/* Function	   : usrAssignOutput										*/
/* Purpose	   : User I/F function to assign a virtual output port		*/
/* Inputs	   : pmask, virt output number, bus, slave, port			*/
/* Outputs	   : OK or ERROR											*/
/* Comment     : Only used to assign Serial ModBus ports				*/
/************************************************************************/
CmdRtn usrAssignOutput(ParmMask_t pmask, Parm_t virtPort, Parm_t bus, Parm_t slave, Parm_t port)
{
	if ((pmask & 0xf) != 0xf)
	{
		xprintf("Usage: assignOutput <virtOutputNum> <modBusNum> <slave> <port>\n");
		return(ERROR);
	}

	if (assignVirtOutput(NULL, virtPort, MB_SER, bus, slave, port, 1.0, 0.0) != OK)
	{
		xprintf("Bad port number reference or port already assigned\n");
		return(ERROR);
	}

	xprintf("SER Bus %u slave %u port %u assigned to virtual output port %u\n",
			bus, slave, port, virtPort);
	return(OK);

} /* usrAssignOutput() */
