/** 
*******************************************************************************
* @file modbus.c
* @brief Модуль для реализации MODBUS.
*******************************************************************************
* @details 
Файл содержит реализацию функций работы с Modbus.

@section mbapi Функции и макросы

### Инициализация:
- MODBUS_FirstInit()        — Инициализация Modbus (подключение UART, TIM)
- MODBUS_Config()           — Конфигурацмя Modbus (ID, Timeout).
- MODBUS_SlaveStart()       — Запуск Modbus как Slave.
- MODBUS_MasterRequest()    — Отправить запрос в MODBUS как Master.

### Функции для работы с RS (UART):
- RS_Parse_Message() / RS_Collect_Message() — Парсинг и сборка сообщения.
- RS_Response()                             — Отправка ответа.
******************************************************************************/
#include "modbus.h"

/* MODBUS HANDLES */
RS_HandleTypeDef hmodbus1;                ///< Default Handle for Modbus
RS_MsgTypeDef MODBUS_MSG;                 ///< Default Message Struct for Modbus

/* DEFINE DATA FOR MODBUS */
MB_DataStructureTypeDef MB_DATA = {0};;   ///< Coils & Registers

static void MB_DefaultCallback(RS_HandleTypeDef *hmodbus, RS_MsgTypeDef *modbus_msg);

//-------------------------------------------------------------------
//-----------------------------FOR USER------------------------------
/** 
  * @brief    Инициализация периферии модбас.
  * @param    hmodbus Указатель на хендлер RS
  * @param    huart   Указатель на хендлер UART
  * @param    htim    Указатель на хендлер TIM
  * @details  Подключает хендлы периферии к hmodbus
  *           Конфигурация выставляется по умолчанию из modbus_config.h
  */
HAL_StatusTypeDef MODBUS_FirstInit(RS_HandleTypeDef *hmodbus, UART_HandleTypeDef *huart, TIM_HandleTypeDef *htim)
{
  if((hmodbus == NULL) || (huart == NULL))
  {
    return HAL_ERROR;
  }
  MB_DeviceInentificationInit();
  //-----------SETUP MODBUS-------------
  // set up modbus: MB_RX_Size_NotConst and Timeout enable
  hmodbus->ID = MODBUS_DEVICE_ID;
  hmodbus->sRS_Timeout = MODBUS_TIMEOUT;
  hmodbus->sRS_Mode = RS_SLAVE_ALWAYS_WAIT;
  
  // INIT
  hmodbus->RS_STATUS = RS_Init(hmodbus, huart, htim, 0);  

  RS_EnableReceive();
  
  if(hmodbus->RS_STATUS == RS_OK)
    return HAL_OK;
  else
    return HAL_ERROR;
}
/** 
  * @brief    Программная конфигурация модбас.
  * @param    hmodbus указатель на хендлер RS
  * @param    Timeout Время тишины между двумя байтами после которых перезапускается прием
  * @param    master  Режим мастер (пока не сделан)
  * @details  Конфигурирует ID, таймаут и режим hmodbus
  */
HAL_StatusTypeDef MODBUS_Config(RS_HandleTypeDef *hmodbus, uint8_t ID, uint16_t Timeout, uint8_t master)
{
  if(hmodbus == NULL)
  {
    return HAL_ERROR;
  }
  if(!master)
  {
    if((ID < 1) || (ID > 247))
    {
      return HAL_ERROR;
    }
    hmodbus->ID = ID;
  }
  else
    hmodbus->ID = 0;
  
  hmodbus->sRS_Timeout = Timeout;
  if(master)
    hmodbus->sRS_Mode = RS_MASTER_REQUEST;
  else
    hmodbus->sRS_Mode = RS_SLAVE_ALWAYS_WAIT;
    
  return HAL_OK;
}

/** 
  * @brief    Запуск слейв модбас.
  * @param    hmodbus     Указатель на хендлер RS.
  * @param    modbus_msg  Указатель на структуру сообщения. 
                          (NULL чтобы использовать дефолтную)
  * @details  Конфигурирует ID, таймаут и режим hmodbus
  */
HAL_StatusTypeDef MODBUS_SlaveStart(RS_HandleTypeDef *hmodbus, RS_MsgTypeDef *modbus_msg)
{
  if(hmodbus == NULL)
  {
    return HAL_ERROR;
  }
  
  if(hmodbus->sRS_Mode >= RS_MASTER_MODE_START)
  {
    return HAL_ERROR;
  }
    
  MB_DiagnosticsInit();
    
  if(modbus_msg)
    hmodbus->RS_STATUS = RS_Receive_IT(hmodbus, modbus_msg);
  else
    hmodbus->RS_STATUS = RS_Receive_IT(hmodbus, &MODBUS_MSG);
  
  if(hmodbus->RS_STATUS == RS_OK)
    return HAL_OK;
  else
    return HAL_ERROR;
}

/** 
  * @brief    Реквест мастера модбас.
  * @param    hmodbus     Указатель на хендлер RS.
  * @param    modbus_msg  Указатель на структуру сообщения
  * @details  Конфигурирует ID, таймаут и режим hmodbus
  */
HAL_StatusTypeDef MODBUS_MasterRequest(RS_HandleTypeDef *hmodbus, RS_MsgTypeDef *modbus_msg, void (*pClbk)(RS_HandleTypeDef*, RS_MsgTypeDef*))
{
  if(hmodbus == NULL)
  {
    return HAL_ERROR;
  }
  if(modbus_msg == NULL)
  {
    return HAL_ERROR;
  }  
  if(hmodbus->sRS_Mode < RS_MASTER_MODE_START)
  {
    return HAL_ERROR;
  }
  
  if(hmodbus->f.RS_Busy)
    return HAL_BUSY;
   
  if(pClbk) // если задан используем пользовательский коллбек
    hmodbus->pCallback = (void (*)(void*, void*))(pClbk);
  else // иначе дефолтный
    hmodbus->pCallback =  (void (*)(void*, void*))(&MB_DefaultCallback);
  
  hmodbus->RS_STATUS = RS_Transmit_IT(hmodbus, modbus_msg);
  
  if(hmodbus->RS_STATUS == RS_OK)
    return HAL_OK;
  else
    return HAL_ERROR;
}

//-------------------------------------------------------------------
//-----------------------------INTERNAL------------------------------

/** 
  * @brief    Дефолтный коллбек для мастера.
  * @param    hmodbus     Указатель на хендлер RS
  * @param    modbus_msg  Указатель на структуру сообщения
  * @details  В этот коллбек попадут все запросы, с NULL-коллбеком
  */
static void MB_DefaultCallback(RS_HandleTypeDef *hmodbus, RS_MsgTypeDef *modbus_msg)
{
  __NOP();
  return;
}


/* Реализация функций из rs_message.c для протокола */
RS_StatusTypeDef RS_Response(RS_HandleTypeDef *hmodbus, RS_MsgTypeDef *modbus_msg)
{
  if(hmodbus->sRS_Mode >= RS_MASTER_MODE_START)
  {
    return RS_ERR;
  }
  return MB_Slave_Response(hmodbus, modbus_msg);
}

RS_StatusTypeDef RS_Collect_Message(RS_HandleTypeDef *hmodbus, RS_MsgTypeDef *modbus_msg, uint8_t *modbus_uart_buff)
{
  if(hmodbus->sRS_Mode < RS_MASTER_MODE_START)
  {
    return MB_Slave_Collect_Message(hmodbus, modbus_msg, modbus_uart_buff);
  }
  else
  {
    return MB_Master_Collect_Message(hmodbus, modbus_msg, modbus_uart_buff);
  }
}

RS_StatusTypeDef RS_Parse_Message(RS_HandleTypeDef *hmodbus, RS_MsgTypeDef *modbus_msg, uint8_t *modbus_uart_buff)
{
  if(hmodbus->sRS_Mode < RS_MASTER_MODE_START)
  {
    return MB_Slave_Parse_Message(hmodbus, modbus_msg, modbus_uart_buff);
  }
  else
  {
    return MB_Master_Parse_Message(hmodbus, modbus_msg, modbus_uart_buff);
  }
}
