/** 
	**************************************************************************
	* @file 	modbus.h
	* @brief 	Заголовочный файл модуля MODBUS.
	* @details 	Данный файл необходимо подключить в rs_message.h. После подключать 
	* rs_message.h к основному проекту.
	* 
	* @defgroup MODBUS
	* @brief 		Modbus stuff
	*
	*************************************************************************/
#ifndef __MODBUS_H_
#define __MODBUS_H_
 
#include "modbus_config.h"
#include "modbus_data.h"
//#include "settings.h"		// for modbus settings

/** 
	* @addtogroup MODBUS_SETTINGS
	* @ingroup 		MODBUS
	* @brief 			Some defines for modbus
	@{
	*/
/////////////////////////////////////////////////////////////////////
//////////////////////////---SETTINGS---/////////////////////////////
// USER SETTINGS FOR MODBUS IN interface_config.h
//////////////////////////---SETTINGS---/////////////////////////////


/////////////////////////////////////////////////////////////////////
/////////////////////---USER MESSAGE DEFINES---//////////////////////
//-------------DEFINES FOR STRUCTURE----------------
/* defines for structure of modbus message */
#define MbAddr_SIZE 						1						///< size of (MbAddr)
#define Func_Code_SIZE					1						///< size of (Func_Code)
#define	Addr_SIZE								2						///< size of (Addr)
#define Qnt_SIZE								2						///< size of (Qnt)
#define ByteCnt_SIZE						1						///< size of (ByteCnt)
#define DATA_SIZE 							125 				///< maximum number of data: DWORD (NOT MESSAGE SIZE)
#define CRC_SIZE 								2 					///< size of (MB_CRC) in bytes

/** @brief Size of whole message */
#define INFO_SIZE_MAX 					(MbAddr_SIZE+Func_Code_SIZE+Addr_SIZE+Qnt_SIZE+ByteCnt_SIZE)

/** @brief Size of first part of message that will be received
first receive info part of message, than defines size of rest message*/
#define RX_FIRST_PART_SIZE			INFO_SIZE_MAX

/** @brief Size of buffer: max size of whole message */
#define MSG_SIZE_MAX 						(INFO_SIZE_MAX + DATA_SIZE*2 + CRC_SIZE) // max possible size of message

/** @brief Structure for modbus exception codes */
typedef enum //MB_ExceptionTypeDef
{
	// reading
	NO_ERRORS                 = 0x00,		///< no errors
	ILLEGAL_FUNCTION          = 0x01,		///< Принятый код функции не может быть обработан
	ILLEGAL_DATA_ADDRESS      = 0x02,		///< Адрес данных, указанный в запросе, недоступен
	ILLEGAL_DATA_VALUE        = 0x03,		///< Значение, содержащееся в поле данных запроса, является недопустимой величиной
	SLAVE_DEVICE_FAILURE      = 0x04,		///< Невосстанавливаемая ошибка имела место, пока ведомое устройство пыталось выполнить затребованное действие
//	ACKNOWLEDGE =						0x05,		///< idk
//	SLAVE_DEVICE_BUSY =			0x06,		///< idk
//	MEMORY_PARITY_ERROR =		0x08,		///< idk
}MB_ExceptionTypeDef;

#define ERR_VALUES_START				0x80U				///< from this value starts error func codes
/** @brief Structure for modbus func codes */
typedef enum //MB_FunctonTypeDef
{
  /* COMMANDS */
	// reading
	MB_R_COILS            = 0x01,			///< Чтение битовых ячеек
	MB_R_DISC_IN          = 0x02,		  ///< Чтение дискретных входов
#ifndef MODBUS_SWITCH_COMMAND_R_IN_REGS_AND_R_HOLD_REGS
	MB_R_HOLD_REGS        = 0x03,			///< Чтение входных регистров
	MB_R_IN_REGS          = 0x04,			///< Чтение регистров хранения
#else
	MB_R_HOLD_REGS        = 0x04,			///< Чтение входных регистров
	MB_R_IN_REGS          = 0x03,			///< Чтение регистров хранения
#endif
	
	// writting
	MB_W_COIL             = 0x05,			///< Запись битовой ячейки
	MB_W_HOLD_REG         = 0x06,			///< Запись одиночного регистра
	MB_W_COILS            = 0x0F,			///< Запись нескольких битовых ячеек
	MB_W_HOLD_REGS        = 0x10,			///< Запись нескольких регистров
  
  MB_R_DEVICE_INFO      = 0x2B,			///< Чтения информации об устройстве
  
  /* ERRORS */
	// error reading
	MB_ERR_R_COILS        = MB_R_COILS + ERR_VALUES_START,			  ///< Ошибка чтения битовых ячеек
	MB_ERR_R_DISC_IN      = MB_R_DISC_IN + ERR_VALUES_START,	    ///< Ошибка чтения дискретных входов
	MB_ERR_R_IN_REGS      = MB_R_IN_REGS + ERR_VALUES_START,			///< Ошибка чтения регистров хранения
	MB_ERR_R_HOLD_REGS    = MB_R_HOLD_REGS + ERR_VALUES_START,		///< Ошибка чтения входных регистров
	
	// error writting
	MB_ERR_W_COIL         = MB_W_COIL + ERR_VALUES_START,			    ///< Ошибка записи битовой ячейки
	MB_ERR_W_HOLD_REG     = MB_W_HOLD_REG + ERR_VALUES_START,			///< Ошибка записи одиночного регистра
	MB_ERR_W_COILS        = MB_W_COILS + ERR_VALUES_START,			  ///< Ошибка записи нескольких битовых ячеек
	MB_ERR_W_HOLD_REGS    = MB_W_HOLD_REGS + ERR_VALUES_START,	  ///< Ошибка записи нескольких регистров
}MB_FunctonTypeDef;

/** @brief Structure for MEI func codes */
typedef enum //MB_FunctonTypeDef
{
  MEI_DEVICE_IDENTIFICATION = 0x0E,
}MB_MEITypeDef;

/** @brief Structure for MEI func codes */
typedef enum //MB_FunctonTypeDef
{
  MB_BASIC_IDENTIFICATION = 0x01,
  MB_REGULAR_IDENTIFICATION = 0x02,
  
  
  /* ERRORS */  
  MB_ERR_BASIC_IDENTIFICATION = MB_BASIC_IDENTIFICATION + ERR_VALUES_START,
  MB_ERR_REGULAR_IDENTIFICATION = MB_REGULAR_IDENTIFICATION + ERR_VALUES_START,
}MB_ConformityTypeDef;

/** @brief Structure for decive identification message type */
typedef struct
{
	MB_MEITypeDef 		        MEI_Type;			  ///< MEI Type assigned number for Device Identification Interface
  MB_ConformityTypeDef      ReadDevId;
  MB_ConformityTypeDef      Conformity;
  uint8_t                   MoreFollows;    ///< in this library always a zero
  uint8_t                   NextObjId;
  uint8_t                   NumbOfObj;  
}MB_DevIdMsgTypeDef;

/** @brief Structure for modbus messsage */
typedef struct 	// RS_MsgTypeDef
{
	uint8_t 							MbAddr;							///< Modbus Slave Address
	MB_FunctonTypeDef 		Func_Code;					///< Modbus Function Code
  MB_DevIdMsgTypeDef    DevId;              ///< Read Device Identification Header struct
	uint16_t 							Addr;								///< Modbus Address of data
	uint16_t 							Qnt;								///< Quantity of modbus data
	uint8_t 							ByteCnt;						///< Quantity of bytes of data in message to transmit/receive
	
	uint16_t 							DATA[DATA_SIZE];		///< Modbus Data
	MB_ExceptionTypeDef		Except_Code;				///< Exception Code for the command
	
	uint16_t 							MB_CRC;							///< Modbus CRC
}RS_MsgTypeDef;
//--------------------------------------------------
extern RS_MsgTypeDef MODBUS_MSG;
/////////////////////---MODBUS USER SETTINGS---//////////////////////

/** MODBUS_SETTINGS
  * @} 
  */


/////////////////////////////////////////////////////////////////////
////////////////////---MODBUS MESSAGE DEFINES---/////////////////////
/** 
	* @addtogroup MODBUS_MESSAGE_DEFINES
	* @ingroup 		MODBUS
	* @brief 			Some defines for modbus
	@{
	*/
/** @brief Structure for coils operation */
typedef enum
{
	SET_COIL,
	RESET_COIL,
	TOOGLE_COIL,
}MB_CoilsOpTypeDef;

//--------------------------------------------------

/** 
	* @brief 	Macros to set pointer to 16-bit array
	* @param 	_arr_ 				- массив регистров (16-бит).
	*/
#define MB_Set_Arr16_Ptr(_arr_)												((uint16_t*)(&(_arr_)))
/** 
	* @brief 	Macros to set pointer to register
	* @param 	_parr_ 				- массив регистров.
	* @param 	_addr_ 				- Номер регистра (его индекс) от начала массива _arr_.
	*/
#define MB_Set_Register_Ptr(_parr_, _addr_)						((uint16_t *)(_parr_)+(_addr_))

/** 
	* @brief 	Macros to set pointer to a certain register that contains certain coil
	* @param 	_parr_ 				- массив коилов.
	* @param 	_coil_ 				- Номер коила от начала массива _arr_.
	*	@note		Используется вместе с @ref MB_Set_Coil_Mask
	@verbatim	Пояснение выражений
						(_coil_/16) - get index (address shift) of register that contain certain coil
	Visual explanation: 30th coil in coils registers array
	xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxCx
	|register[0]----|	|register[1]----|
	|skip this------|	|get this-------|
										|shift to 14 bit|
	@endverbatim
	*/
#define MB_Set_Coil_Reg_Ptr(_parr_, _coil_)						((uint16_t *)(_parr_)+((_coil_)/16))
/** 
	* @brief 	Macros to set mask to a certain bit in coils register
	* @param 	_coil_ 				- Номер коила от начала массива _arr_.
	*	@note		Используется вместе с @ref MB_Set_Coil_Reg_Ptr
	@verbatim	Пояснение выражений
						(16*(_coil_/16) - how many coils we need to skip. e.g. (16*30/16) - skip 16 coils from first register
						_coil_-(16*(_coil_/16)) - shift to certain coil in certain register
					e.g. Coil(30) gets in register[1] (30/16 = 1) coil №14 (30 - (16*30/16) = 30 - 16 = 14)
	
	Visual explanation: 30th coil in coils registers array
	xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxCx
	|register[0]----|	|register[1]----|
	|skip this------|	|get this-------|
										|shift to 14 bit|
	@endverbatim
	*/
#define MB_Set_Coil_Mask(_coil_)											(1 << ( _coil_ - (16*((_coil_)/16)) ))
	
/** 
	* @brief 	Read Coil at its local address.
	* @param 	_parr_ 				- массив коилов.
	* @param 	_coil_ 				- Номер коила от начала массива _arr_.
	* @return uint16_t			- Возвращает запрошенный коил на 0м бите.
	*
  * @details 	Позволяет обратиться к коилу по адресу относительно _arr_.
	*/
#define MB_Read_Coil_Local(_parr_, _coil_)					((	*MB_Set_Coil_Reg_Ptr(_parr_, _coil_)	&		MB_Set_Coil_Mask(_coil_) ) >> (_coil_))
/** 
	* @brief 	Set Coil at its local address.
	* @param 	_parr_ 				- указатель на массив коилов.
	* @param 	_coil_ 				- Номер коила от начала массива _arr_.
	*	
  * @details 	Позволяет обратиться к коилу по адресу относительно _arr_.
	*/
#define	MB_Set_Coil_Local(_parr_, _coil_)							*MB_Set_Coil_Reg_Ptr(_parr_, _coil_) 	|= 	MB_Set_Coil_Mask(_coil_)
/** 
	* @brief 	Reset Coil at its local address.
	* @param 	_parr_ 				- указатель на массив коилов.
	* @param 	_coil_ 				- Номер коила от начала массива _arr_.
	*
  * @details 	Позволяет обратиться к коилу по адресу относительно _arr_.
	*/
#define MB_Reset_Coil_Local(_parr_, _coil_)						*MB_Set_Coil_Reg_Ptr(_parr_, _coil_) 	&= ~(MB_Set_Coil_Mask(_coil_))
/** 
	* @brief 	Set Coil at its local address.
	* @param 	_parr_ 				- указатель на массив коилов.
	* @param 	_coil_ 				- Номер коила от начала массива _arr_.
	*	
  * @details 	Позволяет обратиться к коилу по адресу относительно _arr_.
	*/
#define	MB_Toogle_Coil_Local(_parr_, _coil_)					*MB_Set_Coil_Reg_Ptr(_parr_, _coil_) 	^= 	MB_Set_Coil_Mask(_coil_)
//--------------------------------------------------


//------------------OTHER DEFINES-------------------
#define RegisterType_Holding				0
#define RegisterType_Input					1
#define RegisterType_Discrete				2
// create hadnles and settings for uart, tim, rs with _modbus_ name
//--------------------------------------------------

#ifndef Divide_Up
/**
	* @brief	Calc dividing including remainder
	*	@param 	_val_		- делимое.
	*	@param 	_div_		- делитель.
	*	@details 	Если результат деления без остатка: он возвращается как есть
						Если с остатком - округляется вверх
	*/
//#define Divide_Up(_val_, _div_)												(((_val_)%(_div_))? (_val_)/(_div_)+1 : (_val_)/_div_)	/* через тернарный оператор */
#define Divide_Up(_val_, _div_)												((_val_ - 1) / _div_) + 1																	/* через мат выражение */	
#endif

#ifndef ByteSwap16
/** 
	* @brief 	Swap between Little Endian and Big Endian
	* @param 	v 			- Переменная для свапа.
  * @return v (new)	- Свапнутая переменная.
  * @details 	Переключения между двумя типами хранения слова: HI-LO байты и LO-HI байты.
	*/
#define ByteSwap16(v)																	(((v&0xFF00) >> (8)) | ((v&0x00FF) << (8)))
#endif
/** GENERAL_MODBUS_STUFF
  * @} 
  */
////////////////////---MODBUS MESSAGE DEFINES---/////////////////////



/////////////////////////////////////////////////////////////////////
/////////////////////////---FUNCTIONS---/////////////////////////////
/** 
	* @addtogroup MODBUS_FUNCTIONS
	* @ingroup 		MODBUS
	* @brief 			Function for controling modbus communication
	*/
	
//----------------FUNCTIONS FOR USER----------------
/** 
	* @addtogroup MODBUS_DATA_ACCESS_FUNCTIONS
	* @ingroup 		MODBUS_FUNCTIONS
	* @brief 			Function for user use
	@{
	*/
/* First set up of MODBUS */
void MODBUS_FirstInit(void);
/* Set or Reset Coil at its global address */
MB_ExceptionTypeDef MB_Write_Coil_Global(uint16_t Addr, MB_CoilsOpTypeDef WriteVal);
/* Read Coil at its global address */
uint16_t MB_Read_Coil_Global(uint16_t Addr, MB_ExceptionTypeDef *Exception);

/** MODBUS_DATA_ACCESS_FUNCTIONS
  * @} 
  */
	
//---------PROCESS MODBUS COMMAND FUNCTIONS---------
/** 
	* @addtogroup MODBUS_CMD_PROCESS_FUNCTIONS
	* @ingroup 		MODBUS_FUNCTIONS
	* @brief 			Function process commands
	@{
	*/
/* Check is address valid for certain array */
MB_ExceptionTypeDef MB_Check_Address_For_Arr(uint16_t Addr, uint16_t Qnt, uint16_t R_ARR_ADDR, uint16_t R_ARR_NUMB);
/* Define Address Origin for Input/Holding Registers */
MB_ExceptionTypeDef MB_DefineRegistersAddress(uint16_t **pRegs, uint16_t Addr, uint16_t Qnt, uint8_t RegisterType);
/* Define Address Origin for coils */
MB_ExceptionTypeDef MB_DefineCoilsAddress(uint16_t **pCoils, uint16_t Addr, uint16_t Qnt, uint16_t *start_shift, uint8_t WriteFlag);
/* Proccess command Read Coils (01 - 0x01) */
uint8_t MB_Read_Coils(RS_MsgTypeDef *modbus_msg);
/* Proccess command Read Holding Registers (03 - 0x03) */
uint8_t MB_Read_Hold_Regs(RS_MsgTypeDef *modbus_msg);
/* Proccess command Read Input Registers (04 - 0x04) */
uint8_t MB_Read_Input_Regs(RS_MsgTypeDef *modbus_msg);
/* 	Proccess command Write Single Coils (05 - 0x05) */
uint8_t MB_Write_Single_Coil(RS_MsgTypeDef *modbus_msg);
/* Proccess command Write Multiple Coils (15 - 0x0F) */
uint8_t MB_Write_Miltuple_Coils(RS_MsgTypeDef *modbus_msg);
/* Proccess command Write Multiple Register (16 - 0x10) */
uint8_t MB_Write_Miltuple_Regs(RS_MsgTypeDef *modbus_msg);

/** MODBUS_DATA_ACCESS_FUNCTIONS
  * @} 
  */
/////////////////////////---FUNCTIONS---/////////////////////////////
	
	
	
/////////////////////////////////////////////////////////////////////
/////////////////////////---CALC DEFINES---//////////////////////////


// TRACES DEFINES
#ifndef Trace_MB_UART_Enter
#define Trace_MB_UART_Enter()
#endif //Trace_MB_UART_Enter
	
#ifndef Trace_MB_UART_Exit
#define Trace_MB_UART_Exit()
#endif //Trace_MB_UART_Exit

#ifndef Trace_MB_TIM_Enter
#define Trace_MB_TIM_Enter()
#endif //Trace_MB_TIM_Enter
	
#ifndef Trace_MB_TIM_Exit
#define Trace_MB_TIM_Exit()
#endif //Trace_MB_TIM_Exit

#endif //__MODBUS_H_
