/************************************************************************/
/* Copyright 2013 - 2015 MBARI                                          */
/************************************************************************/
/* Summary  : Menu-driven program to test o5eLib functions under Linux or Cygwin*/
/* Filename : oeTest.c                                                  */
/* Author   : Robert Herlien (rah)                                      */
/* Project  : OASIS5                                                    */
/* Revision: 1.0                                                        */
/* Created  : 11/5/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:                                                */
/* 28may2013 rah - created snTest.c                                     */
/* 5nov2015 rah - created oeTest.c from snTest.c                        */
/************************************************************************/

#include <modbus.h>                     /* libmodbus include            */
#include <modbus-rtu.h>                 /* libmodbus include            */
#include <oeTest.h>                     /* defs for this module         */
#include <o5expander.h>                 /* defs for o5eLib 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>


typedef struct                  /****************************************/
{                               /* Struct to hold commands              */
    char  *name;                /* Command name                         */
    int   (*func)();            /* Function to execute                  */
    int   nparms;               /* Number of parms (0 or 1 only for now)*/
    uint  parmMax;              /* Max value of parm (0 to parmMax)     */
} CommandStruct;                /****************************************/

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);
int     menu(void);
CommandStruct *getCommand(int *parmp);
int     readSerial(O5ExpHandle o5eh, int port);
int     writeSerial(O5ExpHandle o5eh, int port);
int     writeTestSerial(O5ExpHandle o5eh, int port);
int     loopbackSerial(O5ExpHandle o5eh, int port);
int     enableSerial(O5ExpHandle o5eh, int port);
int     disableSerial(O5ExpHandle o5eh, int port);
int     iflushSerial(O5ExpHandle o5eh, int port);
int     oflushSerial(O5ExpHandle o5eh, int port);
int     serBreak(O5ExpHandle o5eh, int port);
int     pwrOn(O5ExpHandle o5eh, int port);
int     pwrOff(O5ExpHandle o5eh, int port);
int     pwrStatus(O5ExpHandle o5eh);
int     blinkyCmd(O5ExpHandle, int parm);
int     serNum(O5ExpHandle o5eh);
int     softwareRev(O5ExpHandle o5eh);
int     configReg(O5ExpHandle o5eh);
int     configMode(O5ExpHandle o5eh);
int     controlMode(O5ExpHandle o5eh);
int     exitProg(void);
int     timeoutCmd(O5ExpHandle);
int     testCmd(O5ExpHandle, int parm);


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

static MBool    oeDebug = FALSE;
static int      slaveID = 1, baud = 38400, modDebug = FALSE;
static char     *modPort = "/dev/ttyS0";
static char     modPortBuf[16];
static char     inputBuf[256];
static char     *testString =
    "This is a very, very long test string 0123456789 abcdefghijklmnopqrstuvwxyz\r\n"
    "This is line 2 of a very, very long test string 0123456789 abcdefghijklmnopqrstuvwxyz\r\n"
    "This is line 3 of a very, very long test string 0123456789 abcdefghijklmnopqrstuvwxyz\r\n"
    "This is line 4 of a very, very long test string 0123456789 abcdefghijklmnopqrstuvwxyz\r\n"
    "This is line 5 of a very, very long test string 0123456789 abcdefghijklmnopqrstuvwxyz\r\n"
    "This is line 6 of a very, very long test string 0123456789 abcdefghijklmnopqrstuvwxyz\r\n"
    "This is line 7 of a very, very long test string 0123456789 abcdefghijklmnopqrstuvwxyz\r\n"
    "This is line 8 of a very, very long test string 0123456789 abcdefghijklmnopqrstuvwxyz\r\n";

static RemSerPort       serPorts[O5E_SER_PORTS];

static CommandStruct cmdTbl[] = {
    {"r", readSerial, 1, 5},
    {"w", writeSerial, 1, 5},
    {"wt", writeTestSerial, 1, 5},
    {"l", loopbackSerial, 1, 5},
    {"e", enableSerial, 1, 5},
    {"d", disableSerial, 1, 5},
    {"if", iflushSerial, 1, 5},
    {"of", oflushSerial, 1, 5},
    {"sb", serBreak, 1, 5},
    {"on", pwrOn, 1, 5},
    {"off", pwrOff, 1, 5},
    {"p", pwrStatus, 0, 0},
    {"b", blinkyCmd, 1, 5},
    {"sn", serNum, 0, 0},
    {"sr", softwareRev, 0, 0}, 
    {"cr", configReg, 0, 0}, 
    {"cm", configMode, 0, 0}, 
    {"ct", controlMode, 0, 0}, 
    {"h", menu, 0, 0},
    {"x", exitProg, 0, 0},
    {"q", exitProg, 0, 0},
    {"test", testCmd, 1, 6}
};


/************************************************************************/
/* Function    : main                                                   */
/* Purpose     : Main entry point                                       */
/* Inputs      : argc, argv                                             */
/* Outputs     : Application return code                                */
/************************************************************************/
int     main(int argc, char **argv)
{
    int                 opt, parm;
    O5ExpHandle         o5eh;
    CommandStruct       *cmdp;

    memset(serPorts, 0, sizeof(serPorts));

    while ((opt = getopt(argc, argv, "a:b:d:hp:")) != -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':
              baud = atoi(optarg);
              break;

          case 'd':
              parm = atoi(optarg);
              if (parm & 1)
                  oeDebug = TRUE;
              if (parm & 2)
                  modDebug = TRUE;
              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 'h':
              usage(argv);
              return(0);
        }

    printf("Testing Modbus port %s  baud %u  slave %u\n", modPort, baud, slaveID);

    initO5ExpanderLib();
    o5eSetDebug(oeDebug);

    if ((o5eh = openO5ExpanderDebug(modPort, baud, slaveID, modDebug)) == NULL)
    {
        printf("Failure in openO5Expander: errno %d %s\n", errno,
               modbus_strerror(errno));
        exit(1);
    }

    o5eModbusSetDebug(o5eh, modDebug);
    rawMode();
    menu();

    while (TRUE)
        if ((cmdp = getCommand(&parm)) != NULL)
        {
            printf("\n");
            if ((*cmdp->func)(o5eh, parm) == EXIT)
                return(0);
        }

    return(0);

} /* main() */


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

    tcgetattr(STDIN_FILENO, &tio);

    tio.c_lflag &= ~ICANON;
    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 <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[-b <baud>] Set Modbus baud rate.  Defaults to 115200\n");
    printf("\t[-d <0-3>] Print debug messages\n");
    printf("\t[-h] Print this message\n");

} /* usage() */


/************************************************************************/
/* Function    : menu                                                   */
/* Purpose     : Print the menu                                         */
/* Inputs      : None                                                   */
/* Outputs     : None                                                   */
/************************************************************************/
int menu(void)
{
    printf("\nType a command:\n\n");
    printf("h for this help message\n");
    printf("x to exit this program\n\n");
    printf("w[0-5] to write to a serial port\n");
    printf("r[0-5] to read from a serial port\n");
    printf("wt[0-5] to write a long test string to a serial port\n");
    printf("l[0-5] to perform a loopback test on a serial port\n");
    printf("e[0-5] to enable a serial port\n");
    printf("d[0-5] to disable a serial port\n");
    printf("if[0-5] to flush serial input\n");
    printf("of[0-5] to flush serial output\n");
    printf("sb[0-5] to send a break to a serial port\n\n");

    printf("on[0-5] to turn on a power relay\n");
    printf("off[0-5] to turn off a power relay\n");
    printf("p to read the state of the power relays\n\n");

    printf("sn to get board serial number\n");
    printf("sr to get board software revision number\n");
//    printf("ct[1-3] for board control (1 for config mode, 2 for reboot, 3 for bootloader)\n");
    printf("cr to get board configuration registers\n");
    printf("cm to go back to config mode\n");

    printf("b[0-5] to set blinky LED (0=off, 1=on, 2=debug 24 hrs, 3 = debug, 4 = error, 5 = 50%% duty)\n\n");
    return(OK);

} /* menu() */


/************************************************************************/
/* Function    : getCommand                                             */
/* Purpose     : Get a command                                          */
/* Inputs      : Ptr to place to put parm                               */
/* Outputs     : Ptr to command to execute                              */
/************************************************************************/
CommandStruct  *getCommand(int *parmp)
{
    int         i, nr, nchars, nameSize;
    uint        parm;
    char        *p;
    CommandStruct *csp;

    printf("Enter a command:  ");
    fflush(stdout);
    memset(inputBuf, 0, sizeof(inputBuf));
    nchars = 0;
    p = inputBuf;

    while( nchars < sizeof(inputBuf) - 1 )
        if ((nr = read(STDIN_FILENO, p, 1)) > 0)
        {
            if ((*p == '\n') || (*p == '\r') || (*p == 4))
                return(NULL);

            nchars += nr;
            p += nr;
            *p = '\0';

            for (i = 0, csp = cmdTbl; i < NumberOf(cmdTbl); i++, csp++)
            {
                nameSize = strlen(csp->name);

                if (strncasecmp(inputBuf, csp->name, nameSize) == 0)
                {
                    if (csp->nparms == 0)
                        return(csp);

                    if ((nchars > nameSize) &&
                        (sscanf(&inputBuf[nameSize], "%u", &parm) >= 1))
                    {
                        if (parm <= csp->parmMax)
                        {
                            *parmp = parm;
                            return(csp);
                        }
                        else
                        {
                            printf("\nBad parameter: %u\n", parm);
                            return(NULL);
                        }
                    }
                }
            }
        }
        else
            usleep(10000);

    return(NULL);
}


/************************************************************************/
/* Function    : exitProg                                               */
/* Purpose     : Command function to exit the program                   */
/* Inputs      : None used                                              */
/* Outputs     : EXIT return code                                       */
/************************************************************************/
int     exitProg(void)
{
    return(EXIT);
}


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

    if ((rsh = serPorts[port].rsh) == NULL)
    {
        if ((rsh = o5eOpenSerial(o5eh, port)) == NULL)
        {
            printf("Failed o5eOpenSerial(), errno %d, %s\n",
                   errno, modbus_strerror(errno));
            return(NULL);
        }

    //Prompt for new baud/stop/parity here
    //re-init if user wants a change
    //  printf("9600,N,8,1?  ");

        if (o5eSerInit(rsh, 9600, 'N', 1) == ERROR)
        {
            printf("Failed o5eSerInit(), errno %d, %s\n",
                   errno, modbus_strerror(errno));
            return(NULL);
        }
        serPorts[port].rsh = rsh;
        serPorts[port].baud = 9600;
        serPorts[port].parity = 'N';
        serPorts[port].stop = 1;
    }

    printf("Using serial port %d at %d,%c,8,%d\n",
           port, serPorts[port].baud, serPorts[port].parity,
           serPorts[port].stop);

    return(rsh);
}


/************************************************************************/
/* Function    : readSerial                                             */
/* Purpose     : Command function to read a serial port                 */
/* Inputs      : O5ExpHandle, Port Number                               */
/* Outputs     : Return code                                            */
/************************************************************************/
int     readSerial(O5ExpHandle o5eh, int port)
{
    RemSerHandle rsh;
    uint32_t    nchars, i;
    MBool       oeDebugSave = oeDebug;

    if ((rsh = getSerHandle(o5eh, port)) == NULL)
        return(ERROR);

    printf("Reading serial port %d at 9600,N,8,1\n", port);
    printf("Type ^D on port %d to exit\n\n", port);

    while(TRUE)
    {
        oeDebug = FALSE;
        if ((nchars = o5eSerRead(rsh, inputBuf, READSIZE)) > 0)
        {
            if (oeDebugSave && (nchars > 0))
                printf("Got %lu chars\n", (unsigned long)nchars);

            write(STDOUT_FILENO, inputBuf, nchars);

            for (i = 0; i < nchars; i++)
                if (inputBuf[i] == 4)
                {
                    oeDebug = oeDebugSave;
                    return(OK);
                }
        }

        usleep(100000);    
//      sleep(1);
    }

    oeDebug = oeDebugSave;
    return(OK);
}

/************************************************************************/
/* Function    : getOneLine                                             */
/* Purpose     : Internal getline()-like function                       */
/* Inputs      : buffer ptr, length                                     */
/* Outputs     : Bytes read                                             */
/* Comments    : Stops on CR, LF, or ^D                                 */
/*               Inserts CR-LF at end of line                           */
/************************************************************************/
static int getOneLine(char *buf, size_t len)
{
    size_t      got;
    char        *p;

    for (p = buf, got = 0; got < len-3; )
    {
        if (read(STDIN_FILENO, p, 1) > 0)
        {
            got++;

            if (*p == 4)
                return(got);

            if ((*p == '\r') || (*p == '\n'))
            {
                *p++ = '\r';
                *p++ = '\n';
                *p = '\0';
                return(got+1);
            }
            p++;
        }
        else
            usleep(100000);
    }

    *p++ = '\r';
    *p++ = '\n';
    *p = '\0';
    return(got+2);
}


/************************************************************************/
/* Function    : writeSerial                                            */
/* Purpose     : Command function to write to a serial port             */
/* Inputs      : O5ExpHandle, Port Number                               */
/* Outputs     : Return code                                            */
/************************************************************************/
int     writeSerial(O5ExpHandle o5eh, int port)
{
    RemSerHandle rsh;
    int         nchars;

    if ((rsh = getSerHandle(o5eh, port)) == NULL)
        return(ERROR);

    printf("Writing serial port %d at 9600,N,8,1, line at a time\n", port);
    printf("^D to exit\n\n");

    memset(inputBuf, 0, sizeof(inputBuf));

    while(TRUE)
    {
        nchars = getOneLine(inputBuf, sizeof(inputBuf));

        if (strchr(inputBuf, 4) != NULL)
            return(OK);

        while (nchars)
            nchars -= o5eSerWrite(rsh, inputBuf, nchars);
    }

    return(OK);
}


/************************************************************************/
/* Function    : writeTestSerial                                        */
/* Purpose     : Command function to write long test string to serial port*/
/* Inputs      : O5ExpHandle, Port Number                               */
/* Outputs     : Return code                                            */
/************************************************************************/
int     writeTestSerial(O5ExpHandle o5eh, int port)
{
    RemSerHandle rsh;
    uint32_t     sent;

    if ((rsh = getSerHandle(o5eh, port)) == NULL)
        return(ERROR);

    printf("Writing test string to serial port %d at 9600,N,8,1\n", port);

    sent = o5eSerWrite(rsh, testString, strlen(testString));

    printf("Successfully wrote %u chars of test string of length %lu\n",
           sent, strlen(testString));

    return(OK);
}


/************************************************************************/
/* Function    : loopbackSerial                                         */
/* Purpose     : Command function to do loopback test on a serial port  */
/* Inputs      : O5ExpHandle, Port Number                               */
/* Outputs     : Return code                                            */
/************************************************************************/
int     loopbackSerial(O5ExpHandle o5eh, int port)
{
    RemSerHandle        rsh;
    uint32_t            nchars;
    fd_set              fds;
    struct timeval      selTmout;
    struct termios      tio, tiosave;

    if ((rsh = getSerHandle(o5eh, port)) == NULL)
        return(ERROR);

    printf("Running loopback test on serial port %d at 9600,N,8,1\n", port);
    printf("^D to exit\n\n");

    tcgetattr(STDIN_FILENO, &tiosave);
    memcpy(&tio, &tiosave, sizeof(tiosave));
    tio.c_lflag &= (~ICANON & ~ECHO);
    tio.c_cc[VMIN] = 0;
    tio.c_cc[VTIME] = 0;
    tcsetattr(STDIN_FILENO, TCSANOW, &tio);

    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)
                {
                    tcsetattr(STDIN_FILENO, TCSANOW, &tiosave);
                    return(OK);
                }

                o5eSerWrite(rsh, inputBuf, nchars);
            }

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

    return(OK);
}

/************************************************************************/
/* Function    : enableSerial                                           */
/* Purpose     : Command function to write to a serial port             */
/* Inputs      : O5ExpHandle, Port Number                               */
/* Outputs     : Return code                                            */
/************************************************************************/
int     enableSerial(O5ExpHandle o5eh, int port)
{
    RemSerHandle        rsh;

    if ((rsh = getSerHandle(o5eh, port)) == NULL)
        return(ERROR);

    if (o5eSerSetTxEnbl(rsh, TRUE) != OK)
    {
        printf("Failure in o5eSerSetTxEnbl(): errno %d %s\n", errno,
               modbus_strerror(errno));
        return(ERROR);
    }

    printf("Enabling serial port %d\n", port);
    return(OK);
}

/************************************************************************/
/* Function    : disableSerial                                          */
/* Purpose     : Command function to write to a serial port             */
/* Inputs      : O5ExpHandle, Port Number                               */
/* Outputs     : Return code                                            */
/************************************************************************/
int     disableSerial(O5ExpHandle o5eh, int port)
{
    RemSerHandle        rsh;

    if ((rsh = getSerHandle(o5eh, port)) == NULL)
        return(ERROR);

    if (o5eSerSetTxEnbl(rsh, FALSE) != OK)
    {
        printf("Failure in o5eSerSetTxEnbl(): errno %d %s\n", errno,
               modbus_strerror(errno));
        return(ERROR);
    }

    printf("Disabling serial port %d\n", port);
    return(OK);
}

int     iflushSerial(O5ExpHandle o5eh, int port)
{
     RemSerHandle       rsh;

    if ((rsh = getSerHandle(o5eh, port)) == NULL)
        return(ERROR);

    if (o5eSerIFlush(rsh) != OK)
    {
        printf("Failure in o5eSerIFlush(): errno %d %s\n", errno,
               modbus_strerror(errno));
        return(ERROR);
    }

    printf("Flush serial input, port %d\n", port);
    return(OK);
}

int     oflushSerial(O5ExpHandle o5eh, int port)
{
     RemSerHandle       rsh;

    if ((rsh = getSerHandle(o5eh, port)) == NULL)
        return(ERROR);

    if (o5eSerOFlush(rsh) != OK)
    {
        printf("Failure in o5eSerOFlush(): errno %d %s\n", errno,
               modbus_strerror(errno));
        return(ERROR);
    }

    printf("Flush serial output, port %d\n", port);
    return(OK);
}


/************************************************************************/
/* Function    : serBreak                                               */
/* Purpose     : Command function to send a serial break                */
/* Inputs      : O5ExpHandle, Port Number                               */
/* Outputs     : Return code                                            */
/************************************************************************/
int     serBreak(O5ExpHandle o5eh, int port)
{
     RemSerHandle       rsh;

    if ((rsh = getSerHandle(o5eh, port)) == NULL)
        return(ERROR);

    printf("Sending 200ms serial break, port %d\n", port);

    if (o5eSerBreak(rsh, 200) != OK)
    {
        printf("Failure in o5eSerBreak(): errno %d %s\n", errno,
               modbus_strerror(errno));
        return(ERROR);
    }

    return(OK);
}


/************************************************************************/
/* Function    : pwrOn                                                  */
/* Purpose     : Command function to turn on a power port               */
/* Inputs      : O5ExpHandle, Port Number                               */
/* Outputs     : Return code                                            */
/************************************************************************/
int     pwrOn(O5ExpHandle o5eh, int port)
{
    printf("Powering on port %d\n", port);

    if (o5ePower(o5eh, port, TRUE) != OK)
    {
        printf("Failure in o5ePower(): errno %d %s\n", errno,
               modbus_strerror(errno));
        return(ERROR);
    }

    return(OK);
}


/************************************************************************/
/* Function    : pwrOff                                                 */
/* Purpose     : Command function to turn on a power port               */
/* Inputs      : O5ExpHandle, Port Number                               */
/* Outputs     : Return code                                            */
/************************************************************************/
int     pwrOff(O5ExpHandle o5eh, int port)
{
    printf("Powering off port %d\n", port);

    if (o5ePower(o5eh, port, FALSE) != OK)
    {
        printf("Failure in o5ePower(): errno %d %s\n", errno,
               modbus_strerror(errno));
        return(ERROR);
    }

    return(OK);
}


/************************************************************************/
/* Function    : pwrStatus                                              */
/* Purpose     : Command function to return power bit status            */
/* Inputs      : O5ExpHandle                                            */
/* Outputs     : Return code                                            */
/************************************************************************/
int     pwrStatus(O5ExpHandle o5eh)
{
    uint16_t    pwrVec;

    if (o5eGetPowerVector(o5eh, &pwrVec) != OK)
    {
        printf("Failure in o5eGetPowerVector(): errno %d %s\n", errno,
               modbus_strerror(errno));
        return(ERROR);
    }

    printf("Power vector = 0x%x\n", (uint)pwrVec);
    return(OK);
}


/************************************************************************/
/* Function    : serNum                                                 */
/* Purpose     : Command function to get board serial number            */
/* Inputs      : O5ExpHandle                                            */
/* Outputs     : OK                                                     */
/************************************************************************/
int     serNum(O5ExpHandle o5eh)
{
    uint16_t    serNum;

    if (o5eGetSerialNum(o5eh, &serNum) != OK)
    {
        printf("Failure in o5eGetSerialNum(): errno %d %s\n", errno,
               modbus_strerror(errno));
        return(ERROR);
    }
    printf("Serial Number = %u\n", (uint)serNum);
    return(OK);
}


/************************************************************************/
/* Function    : softwareRev                                            */
/* Purpose     : Command function to get board firmware revision        */
/* Inputs      : O5ExpHandle                                            */
/* Outputs     : OK                                                     */
/************************************************************************/
int     softwareRev(O5ExpHandle o5eh)
{
    uint16_t    rev;

    if (o5eGetSoftwareRev(o5eh, &rev) != OK)
    {
        printf("Failure in o5eGetSoftwareRev(): errno %d %s\n", errno,
               modbus_strerror(errno));
        return(ERROR);
    }
    printf("Software Rev = 0x%x\n", (uint)rev);
    return(OK);
}


/************************************************************************/
/* Function    : configReg                                              */
/* Purpose     : Command function to get board configuration            */
/* Inputs      : O5ExpHandle                                            */
/* Outputs     : OK                                                     */
/************************************************************************/
int     configReg(O5ExpHandle o5eh)
{
    BoardConfig    cfg;

    if (o5eGetBoardConfig(o5eh, &cfg) != OK)
    {
        printf("Failure in o5eGetBoardConfig(): errno %d %s\n", errno,
               modbus_strerror(errno));
        return(ERROR);
    }

    printf("Board Type = %d\n", cfg.boardType);
    printf("Num Serial Ports = %d\n", cfg.numSerPorts);
    printf("Num Power Ports  = %d\n", cfg.numRelays);
    printf("Num DIO Bits     = %d\n", cfg.numDIO);
    printf("Num Analog Ports = %d\n", cfg.numAnalog);
    printf("Serial is %sswitched with power\n", cfg.serSwitch ? "" : "not ");

    return(OK);
}


/************************************************************************/
/* Function    : configMode                                             */
/* Purpose     : Command function to set board to config mode           */
/* Inputs      : O5ExpHandle                                            */
/* Outputs     : OK                                                     */
/************************************************************************/
int   configMode(O5ExpHandle o5eh)
{
    if (o5eResetConfigMode(o5eh) != OK)
    {
        printf("Failure in o5eResetConfigMode(): errno %d %s\n", errno,
               modbus_strerror(errno));
        return(ERROR);
    }
    printf("Board now in config mode.  Try talking to serial port 0\n");
    return(OK);
}


/************************************************************************/
/* Function    : controlMode                                            */
/* Purpose     : Command function to set board to config mode           */
/* Inputs      : O5ExpHandle                                            */
/* Outputs     : OK                                                     */
/************************************************************************/
int     controlMode(O5ExpHandle o5eh)
{
    printf("Not yet implemented\n");
    return(OK);
}


/************************************************************************/
/* Function    : blinkyCmd                                              */
/* Purpose     : Command function to set blinky LED                     */
/* Inputs      : O5ExpHandle, parm                                      */
/* Outputs     : OK                                                     */
/************************************************************************/
int     blinkyCmd(O5ExpHandle o5eh, int parm)
{
    if (o5eSetBlinky(o5eh, (BlinkyState)parm) != OK)
    {
        printf("Failure in o5eSetBlinky(): errno %d %s\n", errno,
               modbus_strerror(errno));
        return(ERROR);
    }

    return(OK);
}


/************************************************************************/
/* Function    : timeoutCmd                                             */
/* Purpose     : Command function to set O5E timeout                    */
/* Inputs      : O5ExpHandle                                            */
/* Outputs     : OK                                                     */
/************************************************************************/
int     timeoutCmd(O5ExpHandle o5eh)
{
    uint16_t    tmout;

    printf("Enter timeout in number of 50us increments:  ");
    fflush(stdin);

    if (scanf("%hu", &tmout) < 1)
        return(ERROR);

    if (tmout < 20)
    {
        printf("Must be >= 20\n");
        return(ERROR);
    }

    if (setO5ExpModbusTimeout(o5eh, tmout) != OK)
    {
        perror("Failure in setO5ExpModbusTimeout()");
        return(ERROR);
    }

    printf("O5Expander Modbus Timeout set to %.2f ms\n",
           (double)tmout / 20.0);

    return(OK);
}


/************************************************************************/
/* Function    : testCmd                                                */
/* Purpose     : Command function to test parser                        */
/* Inputs      : O5ExpHandle, parm                                      */
/* Outputs     : OK                                                     */
/************************************************************************/
int     testCmd(O5ExpHandle o5eh, int parm)
{
    printf("Test Command, parm = %d\n", parm);
    return(OK);
}
