/******************************************************************************
 *
 * Revision: A
 * Author: Mark Watson
 * Created: Nov 2010
 *
 * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 *  FILE: PARSE.C
 * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 *
 * DESCRIPTION:	The routines in this module decode the
 *					RS-232 and ACB inputs into executable
 *					commands.
 *
 * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 *	Copyright (c) 2010 - 2013 SRD
 *   THE INFORMATION CONTAINED IN THIS file IS THE sole intellectual property OF
 *   SNOWY RANGE INSTRUMENTS and protected under copyright law. ANY REPRODUCTION
 *   or dissemination IN PART OR WHOLE WITHOUT THE express WRITTEN PERMISSION OF
 *   SNOWY RANGE INSTRUMENTS IS PROHIBITED.
 * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 */

#include <ctype.h>
#include <string.h>
#include <stdint.h>

#include "sdi_task.h"

#include "SRI_Global.h"
#include "ASCII.h"
#include "CommandHandler.h"
#include "Parse.h"
#include "util.h"

/****** specific to receive interrupt ******/
char rcvr_q_usb[MAX_REC_USB][REC_LEN_USB];

int received_tail_usb = 0; /* index to the next received[] to be parsed.
 controlled in the command processor. */

int received_head_usb = 0; /* index for the current received being built.
 Used in the usb receive interrupt.
 Checked in the usb receive interrupt
 for overflow. */

int next_byte_usb = 0; /* index for the next rec byte to be stored */

/*************** LOCALLY DEFINED GLOBAL VARIABLES ************************/
/* This structure holds the decoded commands for subsequent execution. */
/* See GLOBAL.H for templates. */
struct decoded parsed[MAX_COMMANDS];

int parsed_head = 0; /* Index to command being parsed */

int parsed_tail = 0; /* Index to next command to execute */

/* The commands array is the look-up table for command parsing and checking. */
/* Refer to GLOBAL.H for structure templates.										*/

/* Command and token delimiters for the parser */
char delimiters[] = " ?=,\x0d\xFF";

const struct commands commands[] =
    { /* open the commands array of structures */
     /*{ open the first element of the array
      "AD_READ",
      " Use: AD_READ. Lists all of the A/D channel current values.",
      {{NONE, "", QUERY, ADread_handler}},
      {{0, 0}, {0, 0}}},
     {"AVERAGES",
      " Use: Averages [1 - 100].  Sets or reports the integration averages.",
      {{NONE, "", QUERY, averaging_handler},
       {NUMBER, "", SET, averaging_handler}},
      {{1, 100}, {0, 0}}},*/
     {"CAL",                                                                                             /*  */
      "", //Use: CAL [START, SET{p1,p2,p3,p4], Peak, Threshold, Test, Peaks]",
      {
/*       {TOKEN, "START", SET, cal_handler},*/
/*       {TOKEN, "SET", CAL, cal_handler},*/
/*       {TOKEN, "PEAK", ALIGN, cal_handler},
       {TOKEN, "PIXELS", REFERENCE, cal_handler},
       {TOKEN, "THRESHOLDS", THRESHOLD, cal_handler},
       {TOKEN, "PEAKS", SAMPLES, cal_handler},
       {TOKEN, "VALIDATE", TOK_DEBUG, cal_handler},
       {TOKEN, "TOLERANCE", TOLERANCE, cal_handler},*/
       {TOKEN, "DATE", DATETIME, cal_handler},
       {TOKEN, "PARAMS", SET, cal_handler},
       {NONE, "", QUERY, cal_handler}
/*       {TOKEN, "RESET", TOK_OFF, cal_handler}*/
      },
      {{0, 0}, {0, 0}}},
      {"YCAL",                                                                                             /*  */
            "", //Use: CAL [START, SET{p1,p2,p3,p4], Peak, Threshold, Test, Peaks]",
            {
             {TOKEN, "DATE", DATETIME, ycal_handler},
             {TOKEN, "PARAMS", SET, ycal_handler},
             {NONE, "", QUERY, ycal_handler}
            },
            {{0, 0}, {0, 0}}},
     {"DATETIME", /*  */
      "",
      {{TOKEN, "SET", SET,
        datetime_handler},
       {NONE, "", QUERY,
        datetime_handler}},
      {{0, 0}, {0, 0}}},
     {"EEPROM", /*  */
      "",
      {{TOKEN, "RESET", RESET, EEPROM_handler},
       {TOKEN, "UPDATE", SET, EEPROM_handler},
       {NONE, "", QUERY, EEPROM_handler}},
      {{0, 0}, {0, 0}}},
      {"GAUSS", /* Set or read the parameters used for Gaussian smoothing. */
        "Use: GAUSS [SET SIGMA THRESHOLD]",
       {{TOKEN, "SET", SET, gauss_handler},
        {NONE, "", QUERY, gauss_handler}},
        {{1, 25}, {1, 10}}},
     {"HAS", "For use with the software to display useful information.",
      {{TOKEN, "ALL", QUERY, has_handler},
       {TOKEN, "SET", SET, has_handler},
       {TOKEN, "CONFIRM", TOK_OFF, has_handler},
       {STRING, "", TOK_ON, has_handler}},
      {{0, 0}, {0, 0}}},
     {"ID",
      "",
      {{NONE, "", QUERY, id_handler},
       {TOKEN, "PIN", READ, id_handler},
       {TOKEN, "PINSET", SET,
        id_handler}},
      {{0, 0}, {0, 0}}},
      {"IMAGER", /* Set Parameters for IMX290 */
       "",
       {{TOKEN, "WINPV", ALIGN, imager_handler},
        {TOKEN, "READ", READ, imager_handler},
        {TOKEN, "GAIN", GAIN, imager_handler},
        {TOKEN, "GAIN_SEL", RANGE, imager_handler},
        {TOKEN, "OFFSET", OFFSET, imager_handler},
        {TOKEN, "BINS", BINS, imager_handler},
        {NONE, "", QUERY, imager_handler}},
       {{0, 940}, {0, 0}}},
     {"INT_TIME",
      "",
      {{NONE, "", QUERY, int_time_handler},
       {NUMBER, "", CHANGE, int_time_handler},
       {TOKEN, "SET", SET, int_time_handler},
       {TOKEN, "AUTO", AUTO, int_time_handler},
       {TOKEN, "MAX", MAX, int_time_handler},
       {TOKEN, "CAL", CAL, int_time_handler}},
      {{1, 30000}, {10, 1000}}},
     {"ITEMP", /* Set or Report CCD Imager Cooling */
      "",
      {{NONE, "", QUERY, itemp_handler},
       {STRING, "", SET, itemp_handler}},
      {{-30.0, 30.0}, {0, 0}}},
     /*  {
         "KEY", 		 SET Factory decryption key
         " Use: KEY [FACTORY | USER] [N].  Sets Factory or Customer Key. ",
         {
         {NONE, "", QUERY, Key_install_handler},
         {TOKEN, "FACTORY", FACTORYKEY, Key_install_handler },
         {TOKEN, "USER", USERKEY, Key_install_handler }
         },
         {{0.0, 0.0}, {0, 0}}
         },*/
     {"LASERPWR",                           // Control power to the laser #1 (783 nm)
         "", //Use: LASERPWR <ON, OFF, CLASSIC, N>.  Sets or reports the laser status.",
         //" For laser settings, type: <laserpwr>. To turn laser on/off, type: <laserpwr on/off>.",
         //" To set laser power or wavelength, type: <laserpwr [set, wavelength] #>.",
     {
         {TOKEN, "ON", TOK_ON, laserpwr_handler},
         {TOKEN, "OFF", TOK_OFF, laserpwr_handler},
         {TOKEN, "SET", SET, laserpwr_handler},
         {NONE, "", QUERY, laserpwr_handler},
         {TOKEN, "CLASS", TYPE, laserpwr_handler},
         {TOKEN, "LEVELS", LEVELS, laserpwr_handler},
         {TOKEN, "SELECT", SELECT, laserpwr_handler}},
     {{0, 1000}, {0, 0}}},
     {"LD#", /* SET Laser diode serial number. */
      "",
      {{NONE, "", QUERY, LDNo_handler},
       {TOKEN, "SET", SET, LDNo_handler}},
      {{0.0, 0.0}, {0, 0}}},
     {"LDTEMP", /* Set or Report Laser Diode temperature */
      "",
      {{NONE, "", QUERY, ldtemp_handler},
       {STRING, "", SET, ldtemp_handler}},
      {{20, 60}, {0, 0}}},
     {"LEVEL", /* SET POWER LEVEL VS. SHUTTER POSITION */
      "",
      {{NONE, "", QUERY, Level_handler},
       /*{TOKEN, "SET", SET, Level_handler},*/
       {TOKEN, "LASER", LASER, Level_handler},
       {NUMBER, "", SAVE, Level_handler}},
      {{0.0, 300.0}, {0, 18}}},
     /*  {
         "PORT", 		 Port read/write
         " Usage: PORT read <A-E>, PORT write|set|clear <A-E> <bitmap in decimal, octal, or hex>>",
         {
         {NONE, "", QUERY, Port_handler},
         {TOKEN, "READ", PORTREAD, Port_handler},
         {TOKEN, "WRITE", PORTWRITE, Port_handler},
         {TOKEN, "SET", PORTSET, Port_handler},
         {TOKEN, "CLEAR", PORTCLEAR, Port_handler},
         {TOKEN, "IIC", COM, Port_handler},
         },
         {{0.0, 0.0}, {0, 0}}
         },*/
     {"POWER", /* SET CONSTANTS FOR POWER LEVELS */
      "",
      {{NONE, "", QUERY, Power_handler},
       {TOKEN, "SELECT", SELECT, Power_handler},
       {TOKEN, "FAN", RESET, Power_handler},
       {TOKEN, "COOLING", SET, Power_handler},
       {TOKEN, "CCD", TOK_ON, Power_handler},
       {TOKEN, "LASER", TOK_DEBUG, Power_handler}},
      {{0.0, 0.0}, {0, 0}}},
     {"RANGE", /* SET CAL CONSTANTS FOR TOTAL READOUT RANGE */
      "",
      {{NONE, "", QUERY, Range_handler},
       {TOKEN, "SET", SET, Range_handler}},
       //{TOKEN, "EXTEND", SELECT, Range_handler}},
      {{0.0, 0.0}, {0, 0}}},
     {"RASTER", /* Set or Report state of the Raster Motor Drive */ // 20
      "",            //Use: Raster <ON, OFF, N]",
                                                                    //" For current raster state, type: <raster>. To turn raster on/off, type: <raster on/off>.",
                                                                    // " To set raster power level, type: <raster set #>.",
      {{TOKEN, "SET", SET, Raster_handler},
       {TOKEN, "ON", TOK_ON, Raster_handler},
       {TOKEN, "OFF", TOK_OFF, Raster_handler},
       {TOKEN, "MON", TEST, Raster_handler},
       {NONE, "", QUERY, Raster_handler}},
      {{0, 100}, {0, 100}}},
     {"READ", /* Read the data array */
      "",
      {{NUMBER, "", SET, read_handler},
       {TOKEN, "CAL", CAL, read_handler},
       {TOKEN, "VERBOSE", VERBOSE, read_handler},
       {TOKEN, "REFERENCE", SET, read_handler},
       {NONE, "", QUERY, read_handler}},
      {{0, 1024}, {0, 0}}},
     {"RESET",
      "",
      {{TOKEN, "COM", COM, reset_handler},
       {TOKEN, "MSD", MSD, reset_handler},
       {TOKEN, "RELOAD", RELOAD, reset_handler},
       {NONE, "", QUERY, reset_handler}},
      {{0, 0}, {0, 0}}},
     {"SERIALNUM", /* SET SERIAL NUMBER */
      "",
      {{NONE, "", QUERY, SerialNo_handler},
       {TOKEN, "SET", SET, SerialNo_handler}},
      {{0.0, 0.0}, {0, 0}}},
     {"SHUTDOWN", "",
      {{NONE, "", QUERY, Shutdown_handler},
       {NUMBER, "", SET, Shutdown_handler},
       {TOKEN, "BUTTON", BUTTON, Shutdown_handler}},
       {{0, 30}, {0, 0}}},
     {"START", /* Begin integration period */
      "",
      {{TOKEN, "AVG", AVERAGE, integrate_handler},
       {TOKEN, "REF", REFERENCE, integrate_handler},
       {TOKEN, "BINS", BINS, integrate_handler},
       {NONE, "", SET, integrate_handler}},
      {{0, 0}, {0, 0}}},
     {"STATUS",
      "",
      {{NONE, "", QUERY, status_handler},
       {NUMBER, "", SET, status_handler},
       {TOKEN, "MAGSW", TOK_ON, status_handler},
       {TOKEN, "FACTORY", TOK_DEBUG, status_handler},
       /*{TOKEN, "THRESHOLD", THRESHOLD, status_handler},
       {TOKEN, "PASSFAIL", VERBOSE, status_handler},*/
      /* {TOKEN, "CLASS1", CLASS, status_handler}*/},
      {{0, 0}, {0, 0}}},
     {"VERBOSE", /* Sets the Response of the controller to set commands */
      "",
      {{TOKEN, "ON", TOK_ON, verbose_handler},
       {TOKEN, "OFF", TOK_OFF, verbose_handler},
       /*{TOKEN, "DEBUG", TOK_DEBUG, verbose_handler},
       {TOKEN, "SET", SET, verbose_handler},*/
       {NONE, "", QUERY, verbose_handler}},
      {{0, 0}, {0, 0}}},
     {
     "SOFTWARE",
     "",
     {
     {NONE, "", QUERY, software_handler},
     {TOKEN, "ON", TOK_ON, software_handler},
     {TOKEN, "OFF", TOK_OFF, software_handler},
    /* {TOKEN, "ALL", VERBOSE, software_handler},
     {TOKEN, "SERIAL", SERIAL, software_handler},
     {TOKEN, "FIRMWARE", FIRMWARE, software_handler},
     {TOKEN, "MODEL", MODEL, software_handler},
     {TOKEN, "AUTO_INT", ID, software_handler},
     {TOKEN, "INT_TIME", INT, software_handler},
     {TOKEN, "DATE_TIME", DATETIME, software_handler},
     //{TOKEN, "INTERLOCK", LOCK, software_handler},
     {TOKEN, "LASER", LASERPWR, software_handler},
     //{TOKEN, "RANGE", RANGE, software_handler},
     {TOKEN, "PIN", SET, software_handler},
     //{TOKEN, "LIB", TOK_DEBUG, software_handler},
     {TOKEN, "OVERRIDE", LIMIT, software_handler},*/
     },
     {{0, 0}, {0, 0}}
     },
     {"", /* NULL COMMAND, prints prompt for simple CR input */
      "",
      {{NONE, "", QUERY, null_handler}},
      {{0, 0}, {0, 0}}}};

/******************************************************************************
 *
 * Revision: A
 * Author: Mark Watson
 * Created:Nov 2010
 *
 * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 *  TITLE: store_parsed
 * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 *
 * PURPOSE:	Copies information from the COMMANDS structure to the parsed[]
 *				command queue.
 *
 * INPUTS:	int primary			index to a main command in the commands[] array.
 *			int subcommand		index to a subcommand for this main command.
 *
 * OUTPUTS:	Executable command and parameters on the parsed[] queue.
 *
 * CALLS:		None.
 *
 * CALLED BY:	decode_command()
 *
 * DETAILS:	Accepts an index into the COMMANDS array and an index to
 *				a sub_handler.  It then copies the command function pointer,
 *				the token, and the limits into the parsed[] array queue
 *				for subsequent execution.
 *
 * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 *	Copywrite (c) 2010 SRD
 * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 */
void store_parsed(int primary, int subcommand)
{
    unsigned char lim_index;

    /* Store the function to execute for this command on the execution queue. */
    parsed[parsed_head].command_function =
        commands[primary].sub_cmd[subcommand].command_function;

    /* Store the token for this command/subcommand combination in
     the execution queue. */
    parsed[parsed_head].token = commands[primary].sub_cmd[subcommand].sub_tok;

    /* Copy parameter limit structures into decoded command queue. */
    for (lim_index = 0; lim_index < MAX_LIMITS; lim_index++)
    {
        parsed[parsed_head].limits[lim_index].lower =
            commands[primary].limits[lim_index].lower;
        parsed[parsed_head].limits[lim_index].upper =
            commands[primary].limits[lim_index].upper;
    } /* end for lim_index */

    parsed[parsed_head].string_location = received_tail_usb;

} /* end store parsed command */

/******************************************************************************
 *
 * Revision: A
 * Author: Mark Watson
 * Created: Nov 2010
 *
 * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 *  TITLE: strtoks
 * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 *
 * PURPOSE: Parses the in_str according to delimiters.
 *
 * INPUTS:	char* in_str		input command string.
 *				char* delimiters	list of delimiters. See parse.def.
 *
 * OUTPUTS:	char* out_str		output string containing first token.
 *				char* strtoks		pointer to the terminating delimiter.
 *
 * CALLS:		strchr()
 *
 * CALLED BY:	decode_command()
 *
 * DETAILS:	Ignores leading white space and delimiters, then copies
 *				in_str into out_str up to the next delimiter.  if just WS,
 *				delimiters, or returns then it nulls the output string.
 *				Returns a pointer to the terminating delimiter. If token
 *				length exceeds TOK_LEN the token will be split and a
 *				pointer returned to the first char of the second half.
 *
 * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 *	Copywrite (c) 2010 SRD
 * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 */
char *strtoks(char *in_str, char *out_str, char *delimiters)
{
    unsigned char tok_len;

    /* increment past leading whitespace and delimiters */
    while ((*in_str) && (strchr(delimiters, *in_str)))
        in_str++;

    /* Copy in_str to out_str up to next delimiter or until max length. */
    tok_len = 0;
    while ((*in_str) && (!strchr(delimiters, *in_str)) && (tok_len < TOK_LEN))
    {
        *out_str++ = *in_str++; /* copy string character by character */
        ++tok_len;
    }

    *out_str = 0;    /* terminate the output string with a null */
    return (in_str); /* return pointer to the delimiter */

} /* end strtoks */

/******************************************************************************
 *
 * Revision: A
 * Author: Mark Watson
 * Created: Nov 2010
 *
 * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 *  TITLE: decode_command
 * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 *
 * PURPOSE:	Accepts a string from the ACB or USB input queue and
 *				decodes it into executable form.
 *
 * INPUTS:	char* received  pointer to a string in one of the input queues.
 *
 * OUTPUTS:	parsed[parsed_head]	decoded commands end up here.
 *
 * CALLS:	Util_SendUart()  isdigit() isxdigit() isgraph()
 *			strcmp()  strtok()  store_parsed()
 *
 * CALLED BY:	check_command()
 *
 * DETAILS:	Accepts the string pointed to by RECEIVED, parses it and
 *				stores the result in the parsed command queue.
 *				char *received is a pointer to an input string from
 *				USB or ACB queues.  If no match is found between the input
 *				and an entry in the COMMANDS structure then the error message
 *				"SYNTAX?" is sent and the input is discarded.
 *
 * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 *	Copywrite (c) 2010 SRD
 * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 */
char decode_command(char *received)
{
    char primary_command[REC_LEN_USB]; /* parsed string of major command */
    char *next_delim_ptr;              /* pointer to next input delimiter */
    char cmd_index = 0;                /* command array index */
    char sub_index = 0;                /* subcommand array index */
    char found = FALSE;                /* flag for subcommand found */

    /* Parse major command string from input string into primary command */
    next_delim_ptr = strtoks(received, primary_command, delimiters);

    /* Look up major commands */
    /* If no match is found, cmd_index = MAX_VALID_COUNT to indicate failure */
    while ((cmd_index < NUMBER_OF_COMMANDS) && (strcmp(primary_command, commands[cmd_index].string)))
        cmd_index++;

    /* Parse out all subcommands. Put them into the parsed command queue */
    if (cmd_index < NUMBER_OF_COMMANDS)
    {
        /* Zero the subcommand strings */
        while (sub_index < MAX_PARM)
            *parsed[parsed_head].string[sub_index++] = 0;

        sub_index = 0;

        /* Copy each input token string into the parsed string array. */
        /* If number of tokens exceeds MAX_PARM then tokens will be lost. */
        /* If length of a token exceeds TOK_LEN it will be split */

        while ((*next_delim_ptr != '\0') && (sub_index < MAX_PARM))
            next_delim_ptr = strtoks(next_delim_ptr,
                                     parsed[parsed_head].string[sub_index++],
                                     delimiters);

        /* Look up subcommand for a syntax check */
        sub_index = 0;

        while ((sub_index < MAX_SUBS) && (found == FALSE) && (commands[cmd_index].sub_cmd[sub_index].string != 0))
        {
            switch (commands[cmd_index].sub_cmd[sub_index].syn_type)
            {
            case NONE:
                if (*parsed[parsed_head].string[0] == 0)
                {
                    store_parsed(cmd_index, sub_index);
                    found = TRUE;
                }
                break;

            case NUMBER:
                if ((isdigit(*parsed[parsed_head].string[0])) || (*parsed[parsed_head].string[0] == 0x2D))
                {
                    store_parsed(cmd_index, sub_index);
                    found = TRUE;
                }
                break;

            case HEX:
                if (isxdigit(*parsed[parsed_head].string[0]))
                {
                    store_parsed(cmd_index, sub_index);
                    found = TRUE;
                }
                break;

            case STRING:
                if (isgraph(*parsed[parsed_head].string[0]))
                {
                    store_parsed(cmd_index, sub_index);
                    found = TRUE;
                }
                break;

            case TOKEN:
                if (strcmp(parsed[parsed_head].string[0],
                           commands[cmd_index].sub_cmd[sub_index].string) == 0)
                {
                    store_parsed(cmd_index, sub_index);
                    found = TRUE;
                }
                break;

            } /* end switch for subcommands */

            sub_index++;

        } /* end while looking up subcommand */

    } /* end if the primary command is found */

    /* If command bad, alert user and do not advance the parsed
     queue head pointer. */
    if (found == FALSE)
    {
        SDITask_sendToUART("\r\nSYNTAX?\r\n\r\n>",14);
    }

    /* Else command OK, advance the decode head pointer to
     next empty location. */
    //else if (++parsed_head >= MAX_COMMANDS)
    parsed_head = 0; /* wrap the queue pointer */
    return found;
} /* end decode_command */

/******************************************************************************
 *
 * Revision: A
 * Author: Mark Watson
 * Created: Nov 2009
 *
 * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 *  TITLE: check_command
 * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 *
 * PURPOSE:	Checks the input queues for new command inputs and decodes them.
 *
 * INPUTS:	rcvr_q_USB[received_tail_USB]  raw commands are read from here.
 *
 * OUTPUTS:	parsed[parsed_head]	decoded commands end up here.
 *
 * CALLS:	decode_command()
 *
 * CALLED BY:	main()
 *
 * DETAILS:	The function checks the  USB input
 *				queue to insure that newly received commands are decoded.
 *				It returns TRUE if a command is decoded, False if the queue
 *				it checks on a given pass is empty.
 *
 * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 *	Copywrite (c) 2010 SRD
 * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 */
static uint8_t check_command(void)
{
    uint8_t cmd_dcd;

    cmd_dcd = FALSE;

    //if (received_tail_usb != received_head_usb)
    //{
        /* Parse and store the oldest USB command. */
        if(decode_command(rcvr_q_usb[received_tail_usb]) == TRUE)
        {
            cmd_dcd = TRUE;
        }
        else
        {
            //memset(&rcvr_q_usb[0][0] , 0, REC_LEN);
        }

        /* Increment to next command. */
        //if (++received_tail_usb >= MAX_REC_USB)
        received_tail_usb = 0;

    //} /* end if USB input queue not empty */

    /* If there is a new parsed command, execute it */
    //if (parsed_head != parsed_tail)
    //{
        if(cmd_dcd == TRUE)
        {
            parsed[parsed_tail].command_function();
        }


        if (status.SpiCommand == FALSE && cmd_dcd == TRUE)
        {
            /* send prompt for next command */
            SDITask_sendToUART("\r\n\r\n>",5);
        }

        /* increment or wrap the parsed command queue tail pointer */
        //if (++parsed_tail >= MAX_COMMANDS)
        //{
            parsed_tail = 0;
        //}

    //} /* end new command execution */
    return (cmd_dcd);

} /* end check command */

/******************************************************************************
 *
 * Revision: A
 * Author: Mark Watson
 * Created: Nov 2009
 *
 * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 *  TITLE: end_input_usb
 * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 *
 * PURPOSE:	Controls the USB received message queue.
 *
 * INPUTS:	char rcvr_q_usb[][]  	An array of strings for received messages.
 *				int received_head_usb	The string pointer for rcvr_q_usb[n][]
 *				int next_byte_usb The character pointer for rcvr_q_usb[][n]*
 * OUTPUTS:	None.
 *
 * CALLS:	send_now().
 *
 * CALLED BY:	usb_intr().
 *
 * DETAILS:	Terminates the current input string with a null, advances
 *				the queue pointer to the next string and resets the character
 *				pointer.  Also checks for space in the queue.
 *
 * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 *	Copywrite (c) 2010 SRD
 * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 */
static void end_input_usb(void)
{
    /* add NULL to form a string */
    rcvr_q_usb[received_head_usb][++next_byte_usb] = 0;

    /* Increment the queue pointer to the next empty input string. */
    if (++received_head_usb >= MAX_REC_USB)
        received_head_usb = 0; /* wrap around if needed */

    next_byte_usb = 0; /* reset char pointer for next input string */

    check_command();

} /* end end_input_string */

/******************************************************************************
 *
 * Revision: A
 * Author: Zachary Gray
 * Created: April 2012
 *
 * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 *  TITLE: process_incoming_character
 * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 *
 * PURPOSE:	Processes single character recieved on USB.
 *
 * INPUTS:	  c - character to process
 *
 * OUTPUTS:	None.
 *
 * CALLS:
 *
 * CALLED BY:	virtual_com.c
 *
 * DETAILS:	Terminates the current input string with a null, advances
 *				the queue pointer to the next string and resets the character
 *				pointer.  Also checks for space in the queue.
 *
 * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 *	Copywrite (c) 2012 SNRI
 * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 */
void process_incoming_character(uint8_t rec_byte_usb)
{
    // DisableInterrupts;

    if (rec_byte_usb == ASCII_BS || rec_byte_usb == ASCII_DEL) /* if backspace is received */
    {
        /* if the string is not empty, null last entry */
        if (next_byte_usb > 0)
        {
            next_byte_usb--;
            rcvr_q_usb[received_head_usb][next_byte_usb] = 0;
        } /* end if string not empty */
    }     /* end if new byte is back space */
    else if (rec_byte_usb == ASCII_EXCLAMATION)
    {
        if (status.state == ACQUIRING)
        {
            status.state = ABORT;
        }
    }    /* end if new byte is an exclamation point */
    else /* Store regular characters in the receive buffer */
    {
        /* Convert all received characters to upper case. */
        rec_byte_usb = toupper(rec_byte_usb);

        /* Convert line feeds to returns. */
        if (rec_byte_usb == ASCII_LF)
            rec_byte_usb = ASCII_CR;

        /* Stuff into current receive string. */
        rcvr_q_usb[received_head_usb][next_byte_usb] = rec_byte_usb;

        /* Check for end of this command string due to carriage return or line feed. */
        if ((rec_byte_usb == ASCII_CR) || (rec_byte_usb == ASCII_LF))
        {
            end_input_usb();
        }
        else /* Check for end of this command string due to buffer length. */
        {
            if (++next_byte_usb >= (REC_LEN_USB - 2))
            {
                rcvr_q_usb[received_head_usb][next_byte_usb] = ASCII_CR;
                end_input_usb();
            }
        } /* end else not carriage return */
    }     /* end else received not a special character */
    //EnableInterrupts;
} /* end process_incoming_character */
