/*----------------------------------------------------------------------------
**
**	Commands.c 
**	
**  Interpretation of commands received via the radio receiver, and initiation
** of processes based on those commands and the current robot state.
**
**--------------------------------------------------------------------------*/
                                                                               
#include "Hardware.h"
#include "Onboard.h"
#include "Shutdown.h"
                          
// Forward prototypes
void ProcessCommand(void);
                     
 
// This enum is the state machine for command reception
enum
{
    WAITING_FOR_START_1,
    WAITING_FOR_START_2,
    RECEIVING
};                         

// This defines the position of command bytes within the packet
enum
{
    PACKET_START = 0,
    PACKET_SPEED = 2,
    PACKET_DIRECTION = 3,
    PACKET_WEAPONS = 4,
    PACKET_SPECIALS = 6,
    PACKET_CRC = 7
};
                   
 
#define CRC_POLYNOMIAL          0x1021
#define COMMAND_BUFFER_SIZE     10
#define MAX_ERRORS              100

//
// Static variables available to all functions of this module
//
                                    
static int              ErrorCount = 0;
static unsigned int     crc;
static int              ReceiveState = WAITING_FOR_START_1;
static  char            CommandBuffer[COMMAND_BUFFER_SIZE];
 char                   Speed, Direction;

//----------------------------------------------------------------------------
//
//      COMMAND STRUCTURE
//
// Commands come in a continuous stream of packets from the handset. Each
// packet is a fixed length and is of the following format:
//         
// Size:      2       1       1        2       2      2    Total 9 bytes.
//         -----------------------------------------------
// Field: | START | SPEED | DIREC | WEAPON | SPECL | CRC  |
//         -----------------------------------------------
//
// START (16 bits)
// This is a constant 2 byte message 0xA55A.
//
// SPEED (8 bits)
// Required speed. -99 to +99.
//
// DIREC (8 bits)
// Required direction in 2 degree steps. 0 degrees is straight ahead,
// 90 degrees is exact left, 270 exact right, and 180 full reverse. The
// value is half of the angle required.
//
// WEAPON (16 bits)
// A bitfield to indicate required weapon operation:
//  0: Front hammer forwards
//  1: Front hammer reverse
//  2: Rear hammer forwards
//  3: Rear hammer reverse
//  4: Spear forwards
//  5: Spear reverse
//  6: Spear multi-throw
//  7: Reserved (0)
//  8: Flipper operate forwards
//  9: Flipper operate reverse
// 10: Extend flipper forwards
// 11: Extend flipper backwards
// 12: Retract flipper
// 13: Operate flipper
// 14: Spare
// 15: Reserved (0)
//
// SPECL (16 bits)
// Special requests:
//  0: Light sequence b0
//  1: Light sequence b1
//  2: Light sequence b2
//  3: Leg assist
//  4: Emergency
//  5: Brandish
//  6: Speed Control Method (0=Open loop, 1=Closed loop)
//  7: Reserved (0)
//  8: Lights - reset sequences
//  9: Lights - Auto repeat
// 10: Spare
// 11: Spare
// 12: Spare
// 13: Spare
// 14: Spare
// 15: Reserved (0)
//
// CRC (16 bits)
// 16 bit CRC for packet.
//
//----------------------------------------------------------------------------

/*****   ROM LOOKUP TABLE USED IN CRC16()     *****/

unsigned int ccitt_16[256] =
{
    0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7,
    0x8108, 0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF,
    0x1231, 0x0210, 0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6,
    0x9339, 0x8318, 0xB37B, 0xA35A, 0xD3BD, 0xC39C, 0xF3FF, 0xE3DE,
    0x2462, 0x3443, 0x0420, 0x1401, 0x64E6, 0x74C7, 0x44A4, 0x5485,
    0xA56A, 0xB54B, 0x8528, 0x9509, 0xE5EE, 0xF5CF, 0xC5AC, 0xD58D,
    0x3653, 0x2672, 0x1611, 0x0630, 0x76D7, 0x66F6, 0x5695, 0x46B4,
    0xB75B, 0xA77A, 0x9719, 0x8738, 0xF7DF, 0xE7FE, 0xD79D, 0xC7BC,
    0x48C4, 0x58E5, 0x6886, 0x78A7, 0x0840, 0x1861, 0x2802, 0x3823,
    0xC9CC, 0xD9ED, 0xE98E, 0xF9AF, 0x8948, 0x9969, 0xA90A, 0xB92B,
    0x5AF5, 0x4AD4, 0x7AB7, 0x6A96, 0x1A71, 0x0A50, 0x3A33, 0x2A12,
    0xDBFD, 0xCBDC, 0xFBBF, 0xEB9E, 0x9B79, 0x8B58, 0xBB3B, 0xAB1A,
    0x6CA6, 0x7C87, 0x4CE4, 0x5CC5, 0x2C22, 0x3C03, 0x0C60, 0x1C41,
    0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD, 0xAD2A, 0xBD0B, 0x8D68, 0x9D49,
    0x7E97, 0x6EB6, 0x5ED5, 0x4EF4, 0x3E13, 0x2E32, 0x1E51, 0x0E70,
    0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC, 0xBF1B, 0xAF3A, 0x9F59, 0x8F78,
    0x9188, 0x81A9, 0xB1CA, 0xA1EB, 0xD10C, 0xC12D, 0xF14E, 0xE16F,
    0x1080, 0x00A1, 0x30C2, 0x20E3, 0x5004, 0x4025, 0x7046, 0x6067,
    0x83B9, 0x9398, 0xA3FB, 0xB3DA, 0xC33D, 0xD31C, 0xE37F, 0xF35E,
    0x02B1, 0x1290, 0x22F3, 0x32D2, 0x4235, 0x5214, 0x6277, 0x7256,
    0xB5EA, 0xA5CB, 0x95A8, 0x8589, 0xF56E, 0xE54F, 0xD52C, 0xC50D,
    0x34E2, 0x24C3, 0x14A0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405,
    0xA7DB, 0xB7FA, 0x8799, 0x97B8, 0xE75F, 0xF77E, 0xC71D, 0xD73C,
    0x26D3, 0x36F2, 0x0691, 0x16B0, 0x6657, 0x7676, 0x4615, 0x5634,
    0xD94C, 0xC96D, 0xF90E, 0xE92F, 0x99C8, 0x89E9, 0xB98A, 0xA9AB,
    0x5844, 0x4865, 0x7806, 0x6827, 0x18C0, 0x08E1, 0x3882, 0x28A3,
    0xCB7D, 0xDB5C, 0xEB3F, 0xFB1E, 0x8BF9, 0x9BD8, 0xABBB, 0xBB9A,
    0x4A75, 0x5A54, 0x6A37, 0x7A16, 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92,
    0xFD2E, 0xED0F, 0xDD6C, 0xCD4D, 0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9,
    0x7C26, 0x6C07, 0x5C64, 0x4C45, 0x3CA2, 0x2C83, 0x1CE0, 0x0CC1,
    0xEF1F, 0xFF3E, 0xCF5D, 0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8,
    0x6E17, 0x7E36, 0x4E55, 0x5E74, 0x2E93, 0x3EB2, 0x0ED1, 0x1EF0
};

/*----------------------------------------------------------------------------
//
//  crc16
//      
//  Perform a 16-bit CRC calculation on a buffer of data using the CCITT_16
//  polynomial (0x1021). The table lookup method is used for speed.
//  
// Returns:
//  CRC.
//   
// Modification Record:
//  31-May-00   Paul Hills      First version from previous job!
-----------------------------------------------------------------------------*/
unsigned int crc16(char *Ptr, int Count)
{                            
    // Perform CRC for required number of counts.
    crc = CRC_POLYNOMIAL;
    while (Count--)                         
        crc = (crc << 8) ^ ccitt_16[(unsigned char)(crc >> 8) ^ 
                                            (unsigned char)(*Ptr++)];
    
    return crc;
}
    
/*----------------------------------------------------------------------------
//
//  crc8
//      
//  Perform a 16-bit CRC calculation on a buffer of data using the CCITT_16
//  polynomial (0x1021), then XOR the upper and lower bytes of the CRC.
//  
// Returns:
//  Packed crc byte.
//
// Modification Record:
//  31-May-00   Paul Hills      First version translation from NEC78 assembler.
-----------------------------------------------------------------------------*/
unsigned int crc8(char *Ptr, int Count)
{                            
    crc16(Ptr, Count);
    return ((crc >> 8)  ^  (crc & 0x00FF));
}
                                

/*----------------------------------------------------------------------------
//
//  Check message CRC
//      
//  Check that the received CRC agrees with that calculated.
//  
// Returns:
//  TRUE or FALSE
//
// Modification Record:
//  24-Jul-00   Paul Hills      First version
-----------------------------------------------------------------------------*/
char CheckMessageCrc(void)
{
    if ( crc16(&(CommandBuffer[PACKET_SPEED]), PACKET_CRC-PACKET_SPEED+1) == 
                 CommandBuffer[PACKET_CRC] << 8  + CommandBuffer[PACKET_CRC+1])
        return TRUE;
    else
        return FALSE;
}


/*----------------------------------------------------------------------------
//
//  IncrementErrors
//      
//  Increment the error counter, and if it is up to a threshold, perform an
// emergency shutdown.
//  
// Modification Record:
//  24-Jul-00   Paul Hills      First version.
-----------------------------------------------------------------------------*/
void IncrementErrors(void)
{

    if (++ErrorCount == MAX_ERRORS)
        EmergencyShutdown();
}


/*----------------------------------------------------------------------------
//
//  CommandByteRx
//      
//  ISR for reception of a command byte from the radio. This is a state 
// machine with 3 states. First it must receive the A5 5A packet start
// sequence which is the first two states, then it receives the data bytes
// and CRC using the third state.
//  
// Modification Record:
//  24-Jul-00   Paul Hills      First version.
-----------------------------------------------------------------------------*/
interrupt [SCI_RXI2] void CommandByteRx(void)
{
    static int CommandBufferIndex = 0;
    static int ByteCount = 0;
    
    //
    // On error, clear error and reset state machine. If received a set
    // number of consecutive errors, shut down.
    //
    
    if (RADIO_COMMS_ERRORS)
    {                
        CLEAR_RADIO_COMMS_ERRORS;
        IncrementErrors();
        ReceiveState = WAITING_FOR_START_1;
    }    
    else
        ErrorCount = 0;     
                
    //
    // Received another byte without error.
    //
    
    ByteCount++;
    RADIO_RECEIVE_FLAG = 0;

    //
    // Command reception state machine
    //
    
    switch (ReceiveState)
    {       
        // Stay in this state until received an 0xA5 byte 
        case WAITING_FOR_START_1:
            if (RADIO_RECEIVE_REGISTER == 0xA5)
            {
                ReceiveState = WAITING_FOR_START_2;
                ByteCount = 1;
            }
            break;
        
        // Stay in this state until received a 0x5A byte 
        case WAITING_FOR_START_2:
            if (RADIO_RECEIVE_REGISTER == 0x5A)
                ReceiveState = RECEIVING;
            break;

        // Now receiving data bytes & CRC
        case RECEIVING:
            // Get byte into receive buffer
            CommandBuffer[CommandBufferIndex++] = RADIO_RECEIVE_REGISTER;
            if (ByteCount == PACKET_CRC+1)  // crc is 2 bytes
            {
                // If CRC is OK, process the command.
                if (CheckMessageCrc() == TRUE)
                {                       
                    // Now got a good packet, process packet and clear 
                    // error counter.
                    ProcessCommand();
                    ErrorCount = 0;     
                }
                else
                {                     
                    // Received a dodgy packet, so increment error counter and
                    // shutdown if necessary.
                    IncrementErrors();
                }
                                            
                CommandBufferIndex = 0;
                ReceiveState = WAITING_FOR_START_1;
            }
            break;
            
        // Should never get to this state. Recover by jump to first state.
        default:                 
            CommandBufferIndex = 0;
            ReceiveState = WAITING_FOR_START_1;
            break;
    }             
}


/*----------------------------------------------------------------------------
//
//  FillFlagArray
//      
// Sets the flag array values based on bits in the command buffer.
//  
// Modification Record:
//  24-Jul-00   Paul Hills      First version
-----------------------------------------------------------------------------*/
void FillFlagArray(char *ArrayPtr, int Values, int Length)
{                             
    int i;
    
    for (i=0 ; i<Length ; i++)
    {                                         
        if (Values & 0x0001 == 0)
            *(ArrayPtr + i) = 0;
        else
            *(ArrayPtr + i) = 1;
        
        ArrayPtr++;
        Values >>= 1;
    }
}

/*----------------------------------------------------------------------------
//
//  ProcessCommand
//      
// Process the commands stored in the receive buffer. This function is called
// after the CRC has been received and checked by the CommandByteRx() ISR.
//  
// Modification Record:
//  24-Jul-00   Paul Hills      First version
-----------------------------------------------------------------------------*/
void ProcessCommand(void)
{   
    //
    // Set globals now a validated packet has been received.
    //

    FillFlagArray(SpeedRequest, CommandBuffer[PACKET_SPEED], 8);
    FillFlagArray(DirectionRequest, CommandBuffer[PACKET_DIRECTION], 8);
    FillFlagArray(WeaponsRequest, CommandBuffer[PACKET_WEAPONS], 16); 
    FillFlagArray(SpecialsRequest, CommandBuffer[PACKET_SPECIALS], 16);
    
    Speed = CommandBuffer[PACKET_SPEED];
    Direction = CommandBuffer[PACKET_DIRECTION];
}  
