/**
******************************************************************************
* @file modbus_core.h
* @brief Ядро Modbus протокола - определения и структуры
******************************************************************************
@addtogroup MODBUS_INTERNAL Modbus Internal Tools
@ingroup MODBUS
@{
******************************************************************************
* @details
Базовые определения для реализации Modbus RTU устройства:
- Структуры сообщений Modbus
Коды функций и исключений
Константы размеров полей
Вспомогательные макросы

@section  Структура сообщения:
[ADDR][FUNC][DATA...][CRC]
- Адрес: 1 байт
- Функция: 1 байт  
- Данные: переменной длины
- CRC: 2 байта

******************************************************************************/
#ifndef __MODBUS_CORE_H_
#define __MODBUS_CORE_H_
 
#include "modbus_config.h"
#include "modbus_data.h"
#include "__crc_algs.h"

/** 
  * @addtogroup MODBUS_MESSAGE_DEFINES Modbus Message Tools
  * @ingroup    MODBUS
  * @brief      Определения протокола модбас
  @{
  */

/////////////////////////////////////////////////////////////////////
////////////////////---MODBUS 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_DIAGNOSTIC       = 0x08,     ///< Чтение диагностической информации устройства
  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_ERR_R_DIAGNOSTIC   = MB_R_DIAGNOSTIC   + ERR_VALUES_START,   ///< Ошибка чтения диагностической информации устройства
  MB_ERR_R_DEVICE_INFO  = MB_R_DEVICE_INFO  + ERR_VALUES_START,   ///< Ошибка чтения информации об устройстве
}MB_FunctonTypeDef;

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

/** @brief Structure for comformity */
typedef enum //MB_FunctonTypeDef
{
  MB_BASIC_IDENTIFICATIONS         = 0x01,   /*!<  @brief Basic Device Identifications. 
                                                  @details All objects of this category are mandatory: 
                                                  VendorName,Product code, and revision number */
  
  MB_REGULAR_IDENTIFICATIONS       = 0x02,   /*!<  @brief Regular Device Identifications. 
                                                  @details The device provides additional and optional 
                                                  identifications and description data objects */
  
  MB_EXTENDED_IDENTIFICATIONS      = 0x03,   /*!<  @brief Extended Device Identifications. 
                                                  @details The device provides additional and optional 
                                                  identifications and description private data about the physical
                                                  device itself. All of these data are device dependent. */
  
  MB_SPEDIFIC_IDENTIFICATIONS      = 0x04,   /*!<  @brief Specific Device Identifications. 
                                                  @details The device provides one specific identifications object. */
  
  /* ERRORS */  
  MB_ERR_BASIC_IDENTIFICATIONS = MB_BASIC_IDENTIFICATIONS + ERR_VALUES_START,
  MB_ERR_REGULAR_IDENTIFICATIONS = MB_REGULAR_IDENTIFICATIONS + ERR_VALUES_START,
  MB_ERR_EXTENDED_IDENTIFICATIONS  = MB_REGULAR_IDENTIFICATIONS + ERR_VALUES_START,
  MB_ERR_SPEDIFIC_IDENTIFICATIONS  = MB_REGULAR_IDENTIFICATIONS + ERR_VALUES_START,
}MB_ConformityTypeDef;

/** @brief Structure for decive identifications message type */
typedef struct
{
  MB_MEITypeDef             MEI_Type;       ///< MEI Type assigned number for Device Identifications Interface
  MB_ConformityTypeDef      ReadDevId;
  MB_ConformityTypeDef      Conformity;
  uint8_t                   MoreFollows;
  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 Identifications 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 MESSAGE DEFINES---/////////////////////



/////////////////////////////////////////////////////////////////////
////////////////////---MODBUS FUNCTION DEFINES---////////////////////

/** 
  * @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_))

/** GENERAL_MODBUS_STUFF
  * @} 
  */


//------------------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
////////////////////---MODBUS MESSAGE DEFINES---/////////////////////


/////////////////////////////////////////////////////////////////////
/////////////////////////---FUNCTIONS---/////////////////////////////
  
//---------PROCESS MODBUS COMMAND FUNCTIONS---------
/** 
  * @addtogroup MODBUS_CMD_PROCESS_FUNCTIONS
  @{
  */
  
/* Реализация этих функций лежит в modbus_data.c */

/* 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);
/** MODBUS_CMD_PROCESS_FUNCTIONS
  * @} 
  */
/////////////////////////---FUNCTIONS---/////////////////////////////
#endif //__MODBUS_CORE_H_
/** MODBUS_INTERNAL
  * @} 
  */