/* 
 * File:   modbus.h
 * Author: chang
 *
 * Created on July 23, 2024, 5:50 PM
 */

#ifndef MODBUS_H
#define	MODBUS_H

#ifdef	__cplusplus
extern "C" {
#endif
    #include <stdint.h>
    extern volatile uint16_t tick; 
    
 
    

#include <stdbool.h>

#ifndef	NULL
    #define NULL ((void *)0)
#endif	/* NULL */

typedef struct{
	unsigned char *data;		//Data item buffer
	unsigned int index;			//Buffer data index
	unsigned int max_buff_size;	//Maximum buffer size
}LinearBuf_type;    
   
 /*******************************************************************************
  Description: MODBUS Defines
*******************************************************************************/
#define TX_DATA						((unsigned char)0)
#define RX_DATA						((unsigned char)1)
//Function codes
#define MODBUS_FC_READ_HOLDING_REG		0x03
#define MODBUS_FC_READ_INPUT_REG		0x04
#define MODBUS_FC_WRITE_SINGLE_REG		0x06
#define MODBUS_FC_WRITE_MULTIPLE_REG		0x10
#define MODBUS_FC_MASK_WRITE_REGISTER		0x16
#define MODBUS_FC_ERROR					0x80
    
#define MODBUS_FC_READ_COIL_REG 0x01
#define MODBUS_FC_READ_DISCRETE_REG 0x02 
#define MODBUS_FC_WRITE_SINGLE_COIL 0x05 
#define MODBUS_FC_WRITE_MULTIPLE_COIL		0x0f
 

//Exception codes
#define MODBUS_EC_ILLEGAL_FUNCTION			0x01
#define MODBUS_EC_ILLEGAL_DATA_ADDRESS			0x02
#define MODBUS_EC_ILLEGAL_DATA_VALUE			0x03
#define MODBUS_EC_SLAVE_DEVICE_FAILURE			0x04
#define MODBUS_EC_ACKNOWLEDGE				0x05
#define MODBUS_EC_SLAVE_DEVICE_BUSY			0x06
#define MODBUS_EC_MEMORY_PARITY_ERROR			0x08
#define MODBUS_EC_GATEWAY_PATH_UNAVAILABLE		0x0A

//Internal error codes
#define MODBUS_ERR_OK					0x00
#define MODBUS_ERR_RESPONSE_TIMEOUT			0x01
#define MODBUS_ERR_UNEXPECTED_ID			0x02
#define MODBUS_ERR_CHECKSUM_FAILED			0x03
#define MODBUS_ERR_UNEXPECTED_FC			0x04
#define MODBUS_ERR_EXCEPTION_CODE			0x05
#define MODBUS_ERR_UNIDENTIFIED_FC			0x06
#define MODBUS_ERR_ILLEGAL_ADDRESS_RANGE	       	0x07
//Dec 21 2015 for Master modbus add--------Start
#define MODBUS_ERR_UNEXPECTED_BYTE_COUNT	   	0x08	//slave only
#define MODBUS_ERR_BROADCAST_REQUEST		0x09
#define MODBUS_ERR_WAITING			0x0A	//master only
//Dec 21 2015 for Master modbus add--------end
//MODBUS Master Definition

//MODBUS Slave Definition

//MODBUS Machine States - shared for master and slave
#define MODBUS_STATE_INITIAL				0x00
#define MODBUS_STATE_INTERFRAME_DELAY			0x01
#define MODBUS_STATE_IDLE				0x02
#define MODBUS_STATE_RECEPTION				0x03
#define	MODBUS_STATE_PARSE				0x04
#define MODBUS_STATE_TRANSMIT				0x05
#define MODBUS_STATE_END_TRANSMIT			0x06
#define MODBUS_STATE_WAIT_REPLY				0x07

#define MODBUS_BROADCAST_ADDRESS			0x00

//Transmit status
#define MODBUS_TRANSMIT_DONE				0x00
#define MODBUS_TRANSMIT_REQUEST				0x01

//Bit positions in packets of received data
#define MODBUS_REQ_BITPOS_ADDRESS			0x00
#define MODBUS_REQ_BITPOS_FUNCTION			0x01
#define MODBUS_REQ_BITPOS_STARTADDRESS			0x02	//For FC=0x03, FC=0x10
#define MODBUS_REQ_BITPOS_REGISTERCNT			0x04	//For FC=0x03, FC=0x10
#define MODBUS_REQ_BITPOS_REGADDR			0x02	//For FC=0x06
#define MODBUS_REQ_BITPOS_REGVALUE			0x04	//For FC=0x06
#define MODBUS_REQ_BITPOS_BYTECNT			0x06	//For FC=0x10
#define MODBUS_REQ_BITPOS_REGVALUE_BASE			0x07	//For FC=0x10
#define MODBUS_REQ_BITPOS_REFADDRESS			0x02	//For FC=0x16
#define MODBUS_REQ_BITPOS_ANDMASK			0x04	//For FC=0x16
#define MODBUS_REQ_BITPOS_ORMASK			0x06	//For FC=0x16

#define MODBUS_RSP_BITPOS_EXCEPTION_CODE		0x02
#define MODBUS_RSP_BITPOS_BYTECNT			0x02
#define MODBUS_RSP_BITPOS_REGSVALUE			0x03   
    /*******************************************************************************
  Description: MODBUS Data Structures
*******************************************************************************/
typedef struct{
	unsigned char state;				//State variable of slave state machine
	uint16_t *memory_rw;			//Pointer to address 0 of read-write memory
	uint16_t memory_rw_size;		//Size of read-write memory
    uint16_t memory_rw_MinAddr;   //Address of memory_rw[0]: 0-based
	uint16_t *memory_r;				//Pointer to address 0 of read-only memory
	uint16_t memory_r_size;			//Size of read-only memory
    uint16_t memory_r_MinAddr;    //Address of memory_r[0]: 0-based
    uint16_t *memory_coil;	
    uint16_t memory_coil_size;	
    uint16_t memory_coil_MinAddr;    //Address of memory_coil[0]: 0-based
    
    uint16_t *memory_discrete;	
    uint16_t memory_discrete_size;	
    uint16_t memory_discrete_MinAddr;    //Address of memory_discrete[0]: 0-based
    
    uint16_t *memory_rw_user1;			//Pointer to address 0 of read-write memory
	uint16_t memory_rw_user1_size;		//Size of read-write memory
    uint16_t memory_rw_user1_MinAddr;    //Address of memory_rw_user1[0]: 0-based
    uint16_t *memory_rw_user2;			//Pointer to address 0 of read-write memory
	uint16_t memory_rw_user2_size;		//Size of read-write memory
    uint16_t memory_rw_user2_MinAddr;    //Address of memory_rw_user1[0]: 0-based
    uint16_t *memory_r_user;
    uint16_t memory_r_user_size;
    uint16_t memory_r_user_MinAddr;    //Address of memory_r_user[0]: 0-based
    //actually baud rate decided by MCC,  this is only for 3.5 Character calculation
    //unsigned char Baudrate_index;  //0 to 7   0: 4800, 1:9600, 2:19200, 3:28800, 4:38400, 5:43000, 6:57600, 7: 115200
    uint16_t TickFor3p5;    //calculated according to Baudrate_, but it may longer than normal Because forceLongerSpace
    uint16_t TmrCountFor3p5; //calculated according to Baudrate_ and Timer1 PR1 and timer,but it may longer than normal
    
    uint16_t TickFor3p5_Normal;  
    uint16_t TmrCountFor3p5_Normal;
    
    volatile uint16_t * tick4rxd;  //remember Packed time (tick))
    volatile uint16_t * tmr4rxd;    //remember Packed time (Timer1 counter))
    
	unsigned char address;				//Slave address

	unsigned char tx_request;			//Transmit request flag for state machine

    /*  use UART Number to replace
	RingBuf_type *rx_buff;				//Receive buffer data
	
	LinearBuf_type *tx_buff;			//Transmit buffer
    */
    LinearBuf_type *tx_buff;			//Transmit buffer
    
    unsigned char UartNo; // 1 based
	//External function to send buffer out UART - MUST BE INITIALIZED!    
	void (*TxBuffCall)(unsigned char*,uint16_t);
    //TXD flow control function
    void (* EnableRS485Receive)(unsigned char enable); 
    
    //private function
    bool (* UART_IsRxReady)(void);
    void (* UART_ClearAll) (void);
    uint8_t (* UART_Read) (void);
    bool (* TransmitDone)(void);   //just for judgement one-time done, no need to consider linear buffer 
    
    //followings are previous version of global variables :  Private
    unsigned char workBuffer[256]; 
    volatile uint16_t CheckSum_S,CksPos_Slv,ComSLV_Err,SLV_seq;
    volatile uint16_t /*end_frame_tmr,*/ broadcast,Tx_seq,Rsp_type,RspRegisters,RspAddress,RspPos;
    volatile uint16_t startAddress,numRegisters,/*Single write  Value, or MaskAND V*/registerValue, /*Mask OR Value*/MaskORValue;
    volatile uint16_t refAddress,Com_SLV_done;
    volatile unsigned char exceptionCode;
    uint8_t FC_user_read;   //0 : means not support
    uint8_t FC_user1_readHolding;
    uint8_t FC_user2_readHolding;
    uint8_t FC_user1_write;
    uint8_t FC_user2_write;       
}ModbusSlave_t;


typedef struct{
	unsigned char state;				//State variable of slave state machine

	uint16_t *memory_r;				//just for storing reading result.Pointer to input memory for read operations, it is changed as MODBUS_Req_Read_Input_Register parameter : uint16_t *memory_read
	uint16_t memory_r_size;			//Size of memory for read operations
    
    uint16_t *memory_rw;				//just for storing reading result.Pointer to holding memory for read operations, it is changed as MODBUS_Req_Read_Holding_Registers parameter : uint16_t *memory_read
	uint16_t memory_rw_size;			//Size of memory for read operations
    
    uint16_t *memory_coil;	      //just for storing reading result.
    uint16_t memory_coil_size;	  //word size, not bit or byte size
    
    uint16_t *memory_discrete;	  //just for storing reading result.
    uint16_t memory_discrete_size;	  //word size, not bit or byte size
    
    uint16_t *memory_rw_user1;			//just for storing reading result.Pointer to address 0 of user 1 read-write memory
	uint16_t memory_rw_user1_size;		//Size of read-write memory
    
    uint16_t *memory_rw_user2;			//just for storing reading result.Pointer to address 0 of user2 read-write memory
	uint16_t memory_rw_user2_size;		//Size of read-write memory
    
    uint16_t *memory_r_user;
    uint16_t memory_r_user_size;
    
	unsigned char dest_address;			//Destination slave address
	unsigned char function_code;		//Last function code requested
	uint16_t error;					//Return error from incoming packet
	unsigned char exception_code;		//Returned exception code

	unsigned char tx_request;			//Transmit request flag for state machine
    uint16_t ModbusTimeout;
    //actually baud rate decided by MCC,  this is only for 3.5 Character calculation
    //unsigned char Baudrate_index;  //0 to 7   0: 4800, 1:9600, 2:19200, 3:28800, 4:38400, 5:43000, 6:57600, 7: 115200
    uint16_t TickFor3p5;    //calculated according to Baudrate_, but it may longer than normal 
    uint16_t TmrCountFor3p5; //calculated according to Baudrate_ and Timer1 PR1 and timer,but it may longer than normal 
        
    
    volatile uint16_t * tick4rxd;  //remember Packed time (tick))
    volatile uint16_t * tmr4rxd;    //remember Packed time (Timer1 counter))
    
    /*  use UART Number to replace
	RingBuf_type *rx_buff;				//Receive buffer data	
	LinearBuf_type *tx_buff;			//Transmit buffer
     */
    LinearBuf_type *tx_buff;			//Transmit buffer
    unsigned char UartNo; // 1 based
	//External function to send buffer out UART - MUST BE INITIALIZED!
	void (*TxBuffCall)(unsigned char*,uint16_t);

     //we don't need RspFunctionCall, we directly do its function when we need to call modbus->RspFunctionCall
	//void (*RspFunctionCall)(unsigned char function_code,unsigned char error);  //only call once when finish transaction 
    //TXD flow control function
    void (* EnableRS485Receive)(unsigned char enable); 
    
    //private function
    bool (* UART_IsRxReady)(void);
    void (* UART_ClearAll) (void);
    uint8_t (* UART_Read) (void);
    bool (* TransmitDone)(void);   //just for judgement one-time done, no need to consider linear buffer 
    
    //followings are previous version of global variables
    volatile unsigned char RxBuffer[256];
    volatile uint16_t CheckSum_M,CksPos_Mstr,ComMstr_Err,Rx_seq;
    volatile uint16_t Loading_flg,BitReadQty;
    // we remove ReadDataAvallable , the reasons are below:
    //  ReadDataAvallable is for reading operation of Modbus, to tell outside, reading data is available.
    // However, we use MasterStateMachine (external state machine: MB_MasterSM.c, not modbus state machine). Before we sending access modbus command, we set sendReqFrmMaster=1.
    //  When modbus finish operation , modbus call RspFunctionCall , and inside RspFunctionCall, it will clear sendReqFrmMaster=0
    // So MasterStateMachine will know finish operation and take out data.
    // ReadDataAvallable is set before calling RspFunctionCall if Modbus reading aOK.  So no need ReadDataAvallable.
    //  However, If you want to know Communication Error easily from reading operation, you can keep ReadDataAvallable.
    // In MasterStateMachine, if finish reading (sendReqFrmMaster==0), and find out ReadDataAvallable=false, You will
    // know Communication Error for this node reading.
    // But we have ReadOK in MasterStateMachine, so we don't need
    //bool ReadDataAvallable;
    uint8_t FC_user_read;  //0 : means not support
    uint8_t FC_user1_readHolding;
    uint8_t FC_user2_readHolding;
    uint8_t FC_user1_write;
    uint8_t FC_user2_write; 
    
    //For Main state machine: access multiple slaves
    uint8_t State4Master;     //Main state machine
    uint8_t sendReqFrmMaster;  //When I send cmd, I set it to 1, and Modbus will clear it to 0 when finish in modbus ->RspFunctionCall())
    uint8_t modbus_handler_error; //set value in modbus->RspFunctionCall()  (From error when finish transaction)
    uint8_t CMD4writeQTY;  //it is write operation QTY in main state machine (in cmd_LogicalRuller, from bit 0 to 14 , how many bits can be 1).  avoid user to put wrong cmd.)
    uint8_t CMD4readQTY;  //it is read operation QTY in main state machine (in cmd_LogicalRuller, from bit 15 to 30 , how many bits can be 1).  avoid user to put wrong cmd.)
}ModbusMaster_t;

//This structure is for outside write data interface (Modbus Master)   
typedef struct WriteCMD_tag {
  uint8_t * ServerID;
  uint8_t ServerID_QTY;
  uint8_t CommandType;
  uint16_t StartAddress;
  uint16_t WriteRegSize;
  uint16_t * WriteWordValue;  
} WriteCMD; 

//This structure is for outside write data interface (Modbus Master)      
typedef struct ReadCMD_tag {
  uint8_t ServerID;
  uint8_t CommandType;
  uint16_t StartAddress;
  uint16_t ReadRegSize;
  uint16_t * ReadWordValue;  
} ReadCMD; 

//Change to local 
//extern void set3p5TimeSeparate4Master(ModbusMaster_t master, uint32_t Baudrate);  //call once after master setup
//extern void set3p5TimeSeparate4Slave(ModbusSlave_t slave, uint32_t Baudrate);  //call once after slave setup

//State machine for slave/master operation - called by application
void MODBUS_Slave_Poll(ModbusSlave_t *modbus);
//MODBUS_Master_Poll_Local(ModbusMaster_t *modbus);


bool MODBUS_Slave_init(ModbusSlave_t *modbus, uint8_t UartNo, uint32_t Baudrate, bool ForceLongSpace, uint8_t address, uint16_t *memory_rw, uint16_t memory_rw_size, uint16_t memory_rw_minAddr,
                       uint16_t *memory_r, uint16_t memory_r_size,uint16_t memory_r_minAddr, uint16_t *memory_coil, uint16_t memory_coil_size, uint16_t memory_coil_minAddr,
                       uint16_t *memory_discrete, uint16_t memory_discrete_size,uint16_t memory_discrete_minAddr);

bool MODBUS_Master_init(ModbusMaster_t *modbus, uint8_t UartNo, uint32_t Baudrate,uint16_t TimeOut,bool ForceLongSpace, WriteCMD *, uint8_t writeCMDQty,ReadCMD *, uint8_t readCMDQty);

//change to void , otherwise, user can uses it as test key and get correct key in loop call
//bool giveLicense(uint8_t * key);
void giveLicense(uint8_t * key);








#ifdef	__cplusplus
}
#endif

#endif	/* MODBUS_H */

