/************************************************************************/
/* Copyright 2013-2015 MBARI						*/
/************************************************************************/
/* Summary  : Simple terminal emulator for Sensor Node			*/
/* Filename : snTerm.c							*/
/* Author   : Robert Herlien (rah)					*/
/* Project  : xFOCE							*/
/* Revision: 1.0							*/
/* Created  : 03/23/2015 from snTest.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:						*/
/* 23mar2015 rah - created from snTest.c				*/
/************************************************************************/

#include <modbus.h>			/* libmodbus include		*/
#include <modbus-rtu.h>			/* libmodbus include		*/
#include <sensorNode.h>			/* defs for SensorNode library	*/

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <ctype.h>
#include <getopt.h>
#include <termios.h>
#include <sys/select.h>


typedef struct			/****************************************/
{				/* Struct to keep RemSerHandle		*/
    RemSerHandle rsh;		/* The serial handle (NULL if unopened)	*/
    int   baud;			/* Baud rate it's opened at		*/
    int   stop;			/* Stop bits				*/
    char  parity;		/* 'N', 'E', or 'O'			*/
} RemSerPort;			/****************************************/


#define READSIZE	20

#define NumberOf(arr)	(sizeof(arr) / sizeof(arr[0]))

/* Return codes from function execution		*/
#define CONTINUE 0
#define EXIT	(-2)


/********************************/
/*	Forward Declarations	*/
/********************************/

void		usage(char **argv);
void		rawMode(void);
RemSerHandle	getSerHandle(SensorNodeHandle snh, int port, int baud);


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

static MBool	snDebug = FALSE;
static int	slaveID = 1, modBaud = 115200, targetPort = 0,
    		targetBaud = 9600, modDebug = FALSE;
static char	*modPort = "/dev/ttyS0";
static char	modPortBuf[16];
static char	inputBuf[256];
static struct termios	tioSave;


/************************************************************************/
/* Function    : main							*/
/* Purpose     : Main entry point					*/
/* Inputs      : argc, argv						*/
/* Outputs     : Application return code				*/
/************************************************************************/
int	main(int argc, char **argv)
{
    int			opt, parm, nchars;
    SensorNodeHandle	snh;
    RemSerHandle	rsh;
    fd_set		fds;
    struct timeval	selTmout;

    while ((opt = getopt(argc, argv, "a:b:d:hm:p:t:")) != -1)
	switch(opt)
	{
	  case 'a':
	      slaveID = atoi(optarg);
	      if ((slaveID < 1) || (slaveID > 247))
	      {
		  printf("Invalid slave ID: %d.  Setting to 1\n", slaveID);
		  slaveID = 1;
	      }
	      break;

	  case 'b':
	      targetBaud = atoi(optarg);
	      break;

	  case 'd':
	      parm = atoi(optarg);
	      if (parm & 1)
		  snDebug = TRUE;
	      if (parm & 2)
		  modDebug = TRUE;
	      break;

	  case 'm':
	      modBaud = atoi(optarg);
	      break;

	  case 'p':
	      modPort = optarg;

	      /* Cygwin no longer likes COMn.  Translate to /dev/ttyS{n-1}	*/
	      if (strncasecmp(modPort, "COM", 3) == 0)
		  if (sscanf(modPort+3, "%d", &opt) > 0)
		  {
		      sprintf(modPortBuf, "/dev/ttyS%u", opt-1);
		      modPort = modPortBuf;
		  }
	      break;

	  case 't':
	      parm = atoi(optarg);
	      if ((parm < 0) || (parm > 3))
	      {
		  printf("Invalid target serial port: %d\n", parm);
		  usage(argv);
		  return(0);
	      }
	      targetPort = parm;
	      break;

	  case 'h':
	      usage(argv);
	      return(0);
	}

    printf("Terminal for Modbus port %s  baud %u  slave %u target serial port %d\n",
	   modPort, modBaud, slaveID, targetPort);

    initSensorNodeLib();
    snSetDebug(snDebug);

    if ((snh = openSensorNode(modPort, modBaud, slaveID)) == NULL)
    {
	printf("Failure in openSensorNode: errno %d %s\n", errno,
	       modbus_strerror(errno));
	exit(1);
    }

    snModbusSetDebug(snh, modDebug);
    rawMode();

    if ((rsh = getSerHandle(snh, targetPort, targetBaud)) == NULL)
	exit(1);

    FD_ZERO(&fds);
    selTmout.tv_sec = 0;
    selTmout.tv_usec = 20000;

    while(TRUE)
    {
	FD_SET(STDIN_FILENO, &fds);

	if (select(1, &fds, NULL, NULL, &selTmout) > 0)
	    if ((nchars = read(STDIN_FILENO, inputBuf, sizeof(inputBuf))) > 0)
	    {
		if (inputBuf[0] == 4)
		{
		    printf("Normal exit\n");
		    tcsetattr(STDIN_FILENO, TCSANOW, &tioSave);
		    exit(0);
		}

		snSerWrite(rsh, inputBuf, nchars);
	    }

	if ((nchars = snSerRead(rsh, inputBuf, READSIZE)) > 0)
	    write(STDOUT_FILENO, inputBuf, nchars);
    }

    return(0);

} /* main() */


/************************************************************************/
/* Function    : rawMode						*/
/* Purpose     : Put input into raw mode				*/
/* Inputs      : None							*/
/* Outputs     : None							*/
/************************************************************************/
void rawMode(void)
{
    struct termios	tio;

    tcgetattr(STDIN_FILENO, &tioSave);
    memcpy(&tio, &tioSave, sizeof(tio));

    tio.c_lflag &= (~ICANON & ~ECHO);
    tio.c_iflag &= (~ICRNL & ~INLCR);
    tio.c_cc[VMIN] = 0;
    tio.c_cc[VTIME] = 0;

    tcsetattr(STDIN_FILENO, TCSANOW, &tio);

} /* rawMode() */


/************************************************************************/
/* Function    : usage							*/
/* Purpose     : Print usage message					*/
/* Inputs      : argv							*/
/* Outputs     : None							*/
/************************************************************************/
void usage(char **argv)
{
    printf("%s [-a <slaveAddr>] [-b <baud>] [-d] [-p <modbus port>] [-t <target port] [-h]\n\n",
	    argv[0]);
    printf("\t[-p <port>] Use serial port <port> to communicate with Modbus device\n");
    printf("\t\tDefaults to COM1\n");
    printf("\t[-a <slaveAddr>] Communicate with Modbus slave <slaveAddr>, 1-247\n");
    printf("\t\tDefaults to 1\n");
    printf("\t[-m <modbus baud>] Set Modbus baud rate.  Defaults to 115200\n");
    printf("\t[-t <target port>] Send/get serial comms to target port <port> (0-3)\n");
    printf("\t\tDefaults to port 0\n");
    printf("\t[-b <target baud>] Baud rate for target (virtual) serial port\n");
    printf("\t\tDefaults to 9600\n");
    printf("\t[-d <0-3>] Print debug messages\n");
    printf("\t[-h] Print this message\n");

} /* usage() */


/************************************************************************/
/* Function    : getSerHandle						*/
/* Purpose     : Get handle for remote serial port			*/
/* Inputs      : SensorNodeHandle, Port Number, baud rate		*/
/* Outputs     : RemSerHandle, or NULL if error				*/
/************************************************************************/
RemSerHandle getSerHandle(SensorNodeHandle snh, int port, int baud)
{
    RemSerHandle rsh;

    if ((rsh = snOpenSerial(snh, port)) == NULL)
    {
	printf("Failed snOpenSerial(), errno %d, %s\n",
	       errno, modbus_strerror(errno));
	return(NULL);
    }

    if (snSerInit(rsh, baud, 'N', 1) == ERROR)
    {
	printf("Failed snSerInit(), errno %d, %s\n",
	       errno, modbus_strerror(errno));
	return(NULL);
    }

    printf("Using serial port %d at %d,N81\n", port, baud);

    return(rsh);
}
