#ifndef MODBUS_H_
#define MODBUS_H_

#include <avr/io.h>
#include <stdlib.h>
#include "Board_IO.h"   //For debugging.  Remove later!

//True or false defines.
#define MB_FALSE                    0   //False.
#define MB_TRUE                     1   //True.

//RTU of ACSII mode.
#define MB_RTU                      0   //RTU mode.
#define MB_ASCII                    1   //ASCII mode

//Array lengths.
#define MB_MAX_RX_RTU_FRAME         256 //Max rx RTU frame size in bytes.
#define MB_MAX_RX_ASCII_FRAME       513 //Max rx ASCII frame size in bytes.
#define MB_MAX_TX_FRAME             256 //Max tx frame size in bytes.
#define MB_MAX_EVENT_LOG            64  //Max size of event log.

//Error codes.
#define MB_NO_ERROR                 0   //General no error code.
#define MB_ERROR                    1   //General error code.
#define MB_ERR_OVERRUN              2   //Frame overrun.
#define MB_ERR_PARITY               3   //Parity error.
#define MB_ERR_SMALL_FRAME          4   //Frame is less than 3 bytes.
#define MB_ERR_CRC                  5   //CRC error.
#define MB_ERR_BRDCAST              6   //Broadcast request not supported.
#define MB_ERR_EXCEPTION            7   //Callback function returned an exception.
#define MB_ERR_UNSUPPORTED_CODE     8   //Unsupported function code.
#define MB_ERR_UNKNOWN_CODE         9   //Unknown function code.
#define MB_ERR_UNKNOWN_SUB          10  //Unknown sub-function code.
#define MB_ERR_INVALID_QUANTITY     11  //Invalid quantity provided with request.

//RTU indexes.
#define MB_RTU_SLAVE_ADDR           0   //Index to slave address.
#define MB_RTU_FUNC_CODE            1   //Index to function code.
#define MB_RTU_ADDR_HI              2   //Index to starting address high byte.
#define MB_RTU_ADDR_LO              3   //Index to starting address low byte.
#define MB_RTU_QTY_HI               4   //Index to quantity of inputs/outputs high byte.
#define MB_RTU_QTY_LO               5   //Index to quantity of inputs/outputs low byte.
#define MB_EVNT_LOG_BYTE_COUNT      2   //Index to get event log byte count.
#define MB_SUB_FUNCTION             2   //Index to 16 bit sub function code.
#define MB_DIAG_DATA                4   //Index diagnostics data, if any.

//ASCII indexes.
#define MB_ASC_SLAVE_ADDR           0   //Index to slave address.
#define MB_ASC_FUNC_CODE            2   //Index to function code.

//Function codes.
#define MB_READ_COILS               0x01
#define MB_READ_DISCRETE_INPUTS     0x02
#define MB_READ_HOLDING_REGISTERS   0x03
#define MB_READ_INPUT_REGISTERS     0x04
#define MB_WRITE_SINGLE_COIL        0x05
#define MB_WRITE_SINGLE_REGISTER    0x06
#define MB_READ_EXCEPTION_STATUS    0x07
#define MB_DIAGNOSTIC               0x08
#define MB_GET_COM_EVENT_COUNTER    0x0B
#define MB_GET_COM_EVENT_LOG        0x0C
//Special programming functions. User defined.
#define MB_PROGRAM_CONTROLLER       0x0D
#define MB_POLL                     0x0E
//End special functions.
#define MB_WRITE_MULTIPLE_COILS     0x0F
#define MB_WRITE_MULTIPLE_REGISTERS 0x10
#define MB_REPORT_SERVER_ID         0x11
#define MB_READ_FILE_RECORD         0x14
#define MB_WRITE_FILE_RECORD        0x15
#define MB_MASK_WRITE_REGISTER      0x16
#define MB_READ_WRITE_MULTI_REGS    0x17
#define MB_READ_FIFO_QUEUE          0x18
#define MB_READ_DEVICE_ID           0x2B

//Extra functions to support later...maybe.
#define MB_ENCAP_INTERFACE_TRANS    0x2B
#define MB_CANOPEN_GEN_REFERENCE    0x2B

//Exception codes.
#define ILLEGAL_FUNCTION            0x01
#define ILLEGAL_DATA_ADDRESS        0x02
#define ILLEGAL_DATA_VALUE          0x03
#define SERVER_DEVICE_FAILURE       0x04
#define ACKNOWLEDGE                 0x05
#define SERVER_DEVICE_BUSY          0x06
#define NEGATIVE_ACKNOWLEDGE        0x07
#define MEMORY_PARITY_ERROR         0x08
#define GATEWAY_PATH_UNAVAIL        0x0A
#define GATWAY_TARGET_NO_RESP       0x0B

//Indexes into function pointer array.
#define MB_RC                       0       //Read coils.
#define MB_RDI                      1       //Read discrete inputs.
#define MB_RHR                      2       //Read holding registers.
#define MB_RIR                      3       //Read input registers.
#define MB_WSC                      4       //Write single coil.
#define MB_WSR                      5       //Write single register.
#define MB_RES                      6       //Read exception status.
#define MB_D                        7       //Diagnostic.
#define MB_GCEC                     8       //Get com event counter.
#define MB_GCEL                     9       //Get com event log.
#define MB_PC                       10      //Program controller.
#define MB_P                        11      //Poll
#define MB_WMC                      12      //Write multiple coils.
#define MB_WMR                      13      //Write multiple registers.
#define MB_RSID                     14      //Report server ID.
#define MB_RFR                      15      //Read file record.
#define MB_WFR                      16      //Write file record.
#define MB_MWR                      17      //Mask write register.
#define MB_RWMR                     18      //Read write multi regs.
#define MB_RFQ                      19      //Read FIFO queue.
#define MB_RDID                     20      //Read device ID.
#define MB_FUNC_ARRAY_LENGTH        21      //Total elements in function array.

//Event log defines.
#define MB_COMM_RESET               0x00    //Device initiated communication reset.
#define MB_ENTERED_LISTEN_MODE      0x04    //Device entered listen only mode
#define MB_TX_NORMAL_RESPONSE       0x40    //Normal response sent.
#define MB_TX_READ_EXCEPTION        0x41    //Exception codes 1 through 3.
#define MB_TX_ABORT_EXCEPTION       0x42    //Exception code 4.
#define MB_TX_BUSY_EXCEPTION        0x44    //Exception codes 5 and 6.
#define MB_TX_NAK_EXCEPTION         0x48    //Exception code 7.
#define MB_TX_WRITE_TIMEOUT         0x50    //Write timeout error occurred.
#define MB_TX_LISTEN_MODE           0x60    //Device in listen only mode.
#define MB_RX_COM_ERROR             0x82    //Communication error.
#define MB_RX_CHAR_OVERRUN          0x90    //Character overrun.
#define MB_RX_LISTEN_MODE           0xA0    //Device in listen only mode.
#define MB_RX_BROADCAST_RCVD        0xC0    //Broadcast received.

//Diagnostic sub functions.
#define MB_DIAG_RTN_QUERY_DATA      0x00    //Return query data.
#define MB_DIAG_RESTART_COMMS       0x01    //Restart communications option.
#define MB_DIAG_RTN_DIAG_REG        0x02    //Return diagnostics register.
#define MB_DIAG_ASCII_DELIM         0x03    //Change ASCII input delimiter.
#define MB_DIAG_LISTEN_MODE         0x04    //Force listen only mode.
#define MB_DIAG_CLR_CNTR_AND_DIAG   0x0A    //Clear counters and diagnostics register.
#define MB_DIAG_RTN_BUS_MSG_CNT     0x0B    //Return bus message count.
#define MB_DIAG_RTN_COM_ERR_CNT     0x0C    //Return bus communications error count.
#define MB_DIAG_RTN_EXCPTN_CNT      0x0D    //Return bus exception error count.
#define MB_DIAG_RTN_MSG_CNT         0x0E    //Return server message count.
#define MB_DIAG_RTN_NO_RSP_CNT      0x0F    //Return server no response count.
#define MB_DIAG_RTN_NAK_CNT         0x10    //Return server NAK count.
#define MB_DIAG_RTN_BUSY_CNT        0x11    //Return server busy count.
#define MB_DIAG_RTN_CHAR_OVRN_CNT   0x12    //Return bus character overrun count.
#define MB_DIAG_CLR_CHAR_OVRN_CNT   0x14    //Clear overrun counter and flag.

//Struct of diagnostic counters and event log.
typedef struct
{
    uint16_t mb_bus_msg_count;
    uint16_t mb_bus_com_err_count;
    uint16_t mb_slave_exc_err_count;
    uint16_t mb_slave_message_count;
    uint16_t mb_no_response_count;
    uint16_t mb_slave_nak_count;
    uint16_t mb_slave_busy_count;
    uint16_t mb_char_overrun_count;
    uint16_t mb_event_counter;
    
    //Log array and pointers.
    uint8_t mb_event_log[MB_MAX_EVENT_LOG + 1];
    uint8_t mb_log_head;
    uint8_t mb_log_tail;
} Mb_diagnostics;

//Tx struct.
typedef struct
{
    uint8_t mb_tx_frame[MB_MAX_TX_FRAME];       //Array containing raw tx frame.
    uint16_t mb_tx_index;                       //Index into rx frame.
    
    uint8_t mb_tx_exception;                    //an exception code, if any, is stored here.
} Mb_tx_struct;

//Rx struct.
typedef struct
{   
    uint8_t mb_rx_frame[MB_MAX_RX_ASCII_FRAME]; //Array containing raw rx frame.
    uint16_t mb_rx_index;                       //Index into rx frame.
    
    uint8_t mb_rx_char_overrun;                 //Character overrun flag.
    uint8_t mb_rx_parity_error;                 //Parity error flag.
    
    uint8_t mb_rx_crc_lo;                       //Low byte of received CRC value.
    uint8_t mb_rx_crc_hi;                       //High byte of received CRC value.
    uint8_t mb_calc_crc_lo;                     //Low byte of calculated CRC value.
    uint8_t mb_calc_crc_hi;                     //High byte of calculated CRC value.
    
    uint8_t mb_slave_addr;                      //Slave address of rx frame.
    
    uint8_t mb_function_code;                   //Function code.
    uint16_t mb_sub_function;                   //Sub function code.
    
    uint16_t mb_address;                        //Start address of range of requested values.
    uint16_t mb_values;                         //Range of values requested.
    
    uint16_t mb_data_index;                     //Index into data field(if present).
    uint16_t mb_data_length;                    //Length of data field.
} Mb_rx_struct;

//The define MB_DYNAMIC can be used to assign the pointers
//below to dynamically.  If MB_DYNAMIC is not defined, the
//pointers will be assigned to static memory.  Processors
//with limited resources should not use the dynamic memory.
Mb_tx_struct* mb_tx_struct;                     //Pointer to modbus tx struct.
Mb_rx_struct* mb_rx_struct;                     //Pointer to modbus rx struct.
Mb_diagnostics* mb_diagnostics;                 //Pointer to modbus diagnostics struct.

//Setup variables for this device.
uint8_t mb_mode;                                //0 = RTU. 1 = ASCII.
uint8_t mb_listen_only;                         //0 = Normal operation, 1 = Listen only mode.
uint8_t mb_is_programming;                      //User controlled. Indicates if controller is currently being programmed.
uint8_t mb_ascii_delimiter;                     //ASCII end of frame delimiter.  Default is 0x0A (line feed).
uint8_t mb_this_address;                        //The address of this slave device.
uint16_t mb_diagnostics_reg;                    //General purpose user defined diagnostics register.
uint16_t mb_max_frame_size;                     //Max RX frame size (RTU or ASCII).

//Array of callback function pointers.
void (*(*mb_callback_funcs))(Mb_rx_struct*, Mb_tx_struct*); //Pointer to array of modbus callback functions.
void (*mb_callback_tx)(uint8_t);                            //Pointer to modbus transmit callback function.

//Initialize modbus variables.
void mb_init(void (*(*mb_cb_funcs))(Mb_rx_struct*, Mb_tx_struct*),  void (*mb_cb_tx)(uint8_t), uint8_t, uint8_t);
void mb_close();                                            //Clear dynamic memory.

void mb_rx_byte(uint8_t rx_byte, uint8_t parity_error);     //Fill modbus rx frame buffer.
void mb_tx_frame();                                         //Transmits modbus frame.
void mb_rx_tx_reset();                                      //Resets variables in rx and tx structs for next frame.

//RTU functions.
uint16_t mb_crc_16(uint8_t* data_frame, uint16_t data_len); //The function returns the CRC by means of calculation.
uint16_t mb_rtu_get_address();                              //Return 16-bit starting address from rx frame.
uint16_t mb_rtu_get_quantity();                             //Return 16-bit quantity of inputs/outputs from rx frame.
void mb_rtu_exception(uint8_t mb_func, uint8_t error_code); //Create tx frame for exception.
void mb_update_event_log(uint8_t event_type);               //This function adds an event to the modbus event log.
void mb_clear_event_log();                                  //Clears the event log and places a com reset in the log.
void mb_get_event_log();                                    //Copies the event log to the tx buffer.
uint8_t  mb_rtu_rx_parse();                                 //Parse the modbus rx array into its sub-components(RTU only).

//ASCII functions.


#endif /* MODBUS_H_ */