/**
*******************************************************************************
* @file rs_message.c
* @brief Реализация протоколов обмена по RS/UART
*******************************************************************************
* @details
Модуль реализует асинхронный обмен сообщениями через UART с использованием:
- Прерываний по приему/передаче
- Детектирования конца фрейма по IDLE линии
- Таймаутов через таймер
- Двухстадийного приема (заголовок + данные)

@section arch Архитектура:
В режиме слейв:
  - Инициализация приема с сообщения с максимальным размером MSG_SIZE_MAX
  - При срабатывании прерывания IDLE - обработка полученного сообщения
В режиме мастер (пока не реализовано):
  - Отправка запроса и переход в режим приема сообщения с максимальным размером MSG_SIZE_MAX
  - При срабатывании прерывания IDLE - обработка полученного ответа
  
@section ithandler Необходимые обработчики:
- RS_UART_Handler() в UARTx_IRQHandler вместо HAL_UART_IRQHandler()
- RS_TIM_Handler() в TIMx_IRQHandler вместо HAL_TIM_IRQHandler()
******************************************************************************/
#include "rs_message.h"
#include "modbus_diag.h"
#include "linkBlink.h"


uint8_t RS_Buffer[MSG_SIZE_MAX]; // uart buffer

extern void RS_UART_Init(void);
extern void RS_UART_DeInit(UART_HandleTypeDef *huart);
extern void RS_TIM_Init(void);
extern void RS_TIM_DeInit(TIM_HandleTypeDef *htim);

//-------------------------------------------------------------------
//-------------------------GENERAL FUNCTIONS-------------------------
/**
  * @brief  Начать прием по прерываниям.
  * @param  hRS     Указатель на хендлер RS.
  * @param  RS_msg  Указатель на структуру сообщения.
  * @return RS_RES  Статус о состоянии RS после инициализации приема.
  */
RS_StatusTypeDef RS_Receive_IT(RS_HandleTypeDef *hRS, RS_MsgTypeDef *RS_msg)
{
  RS_StatusTypeDef RS_RES = 0;
  HAL_StatusTypeDef uart_res = 0;
  
  //-------------CHECK RS LINE----------------
  // check that receive isnt busy
  if( RS_Is_RX_Busy(hRS) ) // if tx busy - return busy status
    return RS_BUSY; 
  
  //-----------INITIALIZE RECEIVE-------------
  // if all OK: start receiving
  RS_EnableReceive();
  RS_Set_Busy(hRS);             // set RS busy  
  RS_Set_RX_Flags(hRS);         // initialize flags for receive
  hRS->pMessagePtr = RS_msg;    // set pointer to message structire for filling it from UARTHandler fucntions
  if(!hRS->f.RX_Continue)       // if not continue receiving
    hRS->RS_Message_Size = 0;   // set ptr to start buffer
  
  // start receiving
  __HAL_UART_ENABLE_IT(hRS->huart, UART_IT_IDLE);
  uart_res = HAL_UART_Receive_IT(hRS->huart, &hRS->pBufferPtr[hRS->RS_Message_Size], MSG_SIZE_MAX); // receive until ByteCnt+1 byte, 
  // then in Callback restart receive for rest bytes  
  
  // if receive isnt started - abort RS
  if(uart_res != HAL_OK)
  {
    RS_RES = RS_Abort(hRS, ABORT_RS);
    printf_rs_err("Failed to start RS receiving...");
    TrackerCnt_Err(hRS->rs_err);
  }
  else
  {
    RS_RES = RS_OK;
    printf_rs("Start Receiving...");
    TrackerCnt_Ok(hRS->rs_err);
  }
  
  hRS->RS_STATUS = RS_RES;
  return RS_RES; // returns result of receive init  
}

/**
  * @brief  Начать передачу по прерываниям.
  * @param  hRS     Указатель на хендлер RS.
  * @param  RS_msg  Указатель на структуру сообщения.
  * @return RS_RES  Статус о состоянии RS после инициализации передачи.
  */
RS_StatusTypeDef RS_Transmit_IT(RS_HandleTypeDef *hRS, RS_MsgTypeDef *RS_msg)
{ 
  RS_StatusTypeDef RS_RES = 0;
  HAL_StatusTypeDef uart_res = 0;
  
  //-------------CHECK RS LINE----------------
  // check that transmit isnt busy
  if( RS_Is_TX_Busy(hRS) ) // if tx busy - return busy status
    return RS_BUSY;
    // check receive line

  
  //------------COLLECT MESSAGE---------------
  RS_RES = RS_Collect_Message(hRS, RS_msg, hRS->pBufferPtr);
  if (RS_RES != RS_OK) // if message isnt collect - stop RS and return error in RS_RES
  {// need collect message status, so doesnt write abort to RS_RES
    RS_Abort(hRS, ABORT_RS);
    RS_Handle_Receive_Start(hRS, hRS->pMessagePtr); // restart receive
  }
  else // if collect successful
  {
    
    //----------INITIALIZE TRANSMIT-------------
    RS_EnableTransmit();
    
    RS_Set_Busy(hRS);             // set RS busy
    RS_Set_TX_Flags(hRS);         // initialize flags for transmit IT
    hRS->pMessagePtr = RS_msg;    // set pointer for filling given structure from UARTHandler fucntion
    if(hRS->RS_Message_Size <= 0)
    {
      RS_RES = RS_Abort(hRS, ABORT_RS);
      TrackerCnt_Err(hRS->rs_err);
      return RS_ERR;
    }      
    // if all OK: start transmitting
    uart_res = HAL_UART_Transmit_IT(hRS->huart, hRS->pBufferPtr, hRS->RS_Message_Size); 
    // if transmit isnt started - abort RS
    if(uart_res != HAL_OK)
    {
      RS_RES = RS_Abort(hRS, ABORT_RS);
      printf_rs_err("Failed to start RS transmitting...");
      TrackerCnt_Err(hRS->rs_err);
    }
    else
    {
      RS_RES = RS_OK;
      printf_rs("Start Transmitting...");
      TrackerCnt_Ok(hRS->rs_err);
    }
  }
  
  
  hRS->RS_STATUS = RS_RES;
  return RS_RES; // returns result of transmit init   
}

/** 
  * @brief  Инициалазация структуры @ref RS_HandleTypeDef.
  * @param  hRS           Указатель на хендлер RS.
  * @param  suart         Указатель на структуру с настройками UART.
  * @param  stim          Указатель на структуру с настройками таймера.
  * @param  pRS_BufferPtr Указатель на буффер для приема-передачи по UART. Если он NULL, то поставиться библиотечный буфер.
  * @return RS_RES        Статус о состоянии RS после инициализации.
  * @details  Инициализация перефирии и структуры для приема-передачи по RS.
  */
RS_StatusTypeDef RS_Init(RS_HandleTypeDef *hRS, UART_HandleTypeDef *huart, TIM_HandleTypeDef *htim, uint8_t *pRS_BufferPtr)
{
  // check that hRS is defined
  if (hRS == NULL)
    return RS_ERR;
  // check that huart is defined
  if (huart == NULL) 
    return RS_ERR;
  
  hRS->huart = huart;
  
  hRS->htim = htim;
  
  // check that buffer is defined
  if (hRS->pBufferPtr == NULL)
  {
    hRS->pBufferPtr = RS_Buffer;    // if no - set default
  }
  else
    hRS->pBufferPtr = pRS_BufferPtr; // if yes - set by user
    
  return RS_OK;
}


/**
  * @brief  Отменить прием/передачу RS/UART.
  * @param  hRS         Указатель на хендлер RS.
  * @param  AbortMode   Выбор, что надо отменить.
              - ABORT_TX:    Отмена передачи по ЮАРТ, с очищением флагов TX,
              - ABORT_RX:    Отмена приема по ЮАРТ, с очищением флагов RX,
              - ABORT_RX_TX: Отмена приема и передачи по ЮАРТ,
              - ABORT_RS:    Отмена приема-передачи RS, с очищением всей структуры.
  * @return RS_RES      Статус о состоянии RS после аборта.
  * @details  Отмена работы UART в целом или отмена приема/передачи RS. 
              Также очищается хендл hRS.
  */
RS_StatusTypeDef RS_Abort(RS_HandleTypeDef *hRS, RS_AbortTypeDef AbortMode)
{
  HAL_StatusTypeDef uart_res = 0;
  
  RS_Timeout_Stop(hRS);
  
  if((AbortMode&ABORT_RS) == 0x00)
  { 
    if((AbortMode&ABORT_RX) == ABORT_RX)
    {
      uart_res = HAL_UART_AbortReceive(hRS->huart);   // abort receive
      RS_Reset_RX_Flags(hRS);
    }
    
    if((AbortMode&ABORT_TX) == ABORT_TX)
    {
      uart_res = HAL_UART_AbortTransmit(hRS->huart);  // abort transmit
      RS_Reset_TX_Flags(hRS);
    }
  }
  else
  {
    uart_res = HAL_UART_Abort(hRS->huart);
    RS_Clear_All(hRS);
  }
  hRS->RS_STATUS = RS_ABORTED;
  return RS_ABORTED;
}


//-------------------------GENERAL FUNCTIONS-------------------------
//-------------------------------------------------------------------



//-------------------------------------------------------------------
//--------------------CALLBACK/HANDLER FUNCTIONS---------------------
/**
  * @brief  Обработчик для начала приема.
  * @param  hRS     Указатель на хендлер RS.
  * @param  RS_msg  Указатель на структуру сообщения.
  * @return RS_RES  Статус о состоянии RS после инициализации приема или окончания общения.
  * @details  Определяет начинать прием команды/ответа или нет.
  */
RS_StatusTypeDef RS_Handle_Receive_Start(RS_HandleTypeDef *hRS, RS_MsgTypeDef *RS_msg)
{ 
  RS_StatusTypeDef RS_RES = 0;
  
  switch(hRS->sRS_Mode)
  {
    // В режиме мастер
    case RS_MASTER_REQUEST:                       
      RS_Timeout_Start(hRS); // сразу запускаем таймаут и начинаем прием
      // В режиме слейв
    case RS_SLAVE_ALWAYS_WAIT:
      RS_RES = RS_Receive_IT(hRS, RS_msg); // Просто запускаем фоновый прием
      break;   

    case RS_RESERVED:
      RS_Set_Free(hRS);  RS_RES = RS_OK; break;   // end RS communication (set RS unbusy)
  }
  
  if(RS_RES != RS_OK)
  {
    TrackerCnt_Err(hRS->rs_err);
  }
  
  return RS_RES;
}
/**  
  * @brief  Обработчик для начала передачи.
  * @param  hRS     Указатель на хендлер RS.
  * @param  RS_msg  Указатель на структуру сообщения.
  * @return RS_RES  Статус о состоянии RS после инициализации передачи.
  * @details  Определяет отвечать ли на команду или нет.
  */
RS_StatusTypeDef RS_Handle_Transmit_Start(RS_HandleTypeDef *hRS, RS_MsgTypeDef *RS_msg)
{ 
  RS_StatusTypeDef RS_RES = 0;
  
  switch(hRS->sRS_Mode)
  {
    case RS_SLAVE_ALWAYS_WAIT:   // in slave mode always response
    case RS_RESERVED:  // transmit response
    case RS_MASTER_REQUEST:  // transmit response
      RS_RES = RS_Transmit_IT(hRS, RS_msg); break; 
  }
  
  if(RS_RES != RS_OK)
  {
    if(hRS->sRS_Mode < RS_MASTER_MODE_START)
    {
      RS_Handle_Receive_Start(hRS, RS_msg);
    }
    
    TrackerCnt_Err(hRS->rs_err);
  }
  
  return RS_RES;
}

/**  
  * @brief  UART TX Callback: коллбек после окончания передачи.
  * @param  hRS     Указатель на хендлер RS.
  * @return RS_RES  Статус о состоянии RS после обработки приема.
  * @details  Определяет поведение RS после передачи сообщения.
  */
RS_StatusTypeDef RS_UART_TxCpltCallback(RS_HandleTypeDef *hRS)
{
  RS_StatusTypeDef RS_RES = RS_OK;
  HAL_StatusTypeDef uart_res = 0;
  
  //--------------ENDING TRANSMITTING-------------
  RS_Set_TX_End(hRS);
  
  //-----------START RECEIVING or END RS----------
  RS_RES = RS_Handle_Receive_Start(hRS, hRS->pMessagePtr);
  
  return RS_RES;
}

/**  
* @brief  Обработчик прерывания UART.
  * @param  hRS     Указатель на хендлер RS.
  * @details  Обрабатывает ошибки если есть и вызывает RS Коллбеки.
  *           Добавить вызов этой функции в UARTx_IRQHandler() ВМЕСТО HAL_UART_IRQHandler().
  */
void RS_UART_Handler(RS_HandleTypeDef *hRS)
{


  if(hRS->huart == NULL)
  {
    return;
  }  
  RS_UART_Handler_ENTER();
	hRS->f.blink=1;

  //-------------CHECK IDLE FLAG FIRST------------- 
  /* Проверяем флаг IDLE в первую очередь - это гарантирует обработку только после idle */
  if(__HAL_UART_GET_FLAG(hRS->huart, UART_FLAG_IDLE) && __HAL_UART_GET_IT_SOURCE(hRS->huart, UART_IT_IDLE))
  {
		
    __HAL_UART_CLEAR_IDLEFLAG(hRS->huart); // Важно: очистить флаг IDLE
    //-------------STANDARD UART HANDLING-------------
    HAL_UART_IRQHandler(hRS->huart);
    hRS->f.RX_Continue = 0;
    
    // Если прием активен и мы получили IDLE - это конец фрейма
    if(RS_Is_RX_Busy(hRS) && hRS->f.RX_Ongoing)
    {
      // Получаем количество фактически принятых байтов
      hRS->RS_Message_Size += hRS->huart->RxXferSize - hRS->huart->RxXferCount;
      
      
      if(hRS->RS_Message_Size > 0)
      {        
        // Принудительно завершаем прием (получили сообщение)
        HAL_UART_AbortReceive(hRS->huart);   // abort receive      
        
        // Завершаем прием в нашей структуре
        RS_Set_RX_End(hRS);
        
        // Парсим наше сообщение
        hRS->RS_STATUS = RS_Parse_Message(hRS, hRS->pMessagePtr, hRS->pBufferPtr);
        
        // Если сообещине принято корректно
        if(hRS->RS_STATUS == RS_OK) 
        {
          RS_Timeout_Stop(hRS);
          
          hRS->lastPacketTick = local_time();
          if(hRS->sRS_Mode < RS_MASTER_MODE_START)
          {
            RS_Response(hRS, hRS->pMessagePtr); // отвечаем на запрос
          }
          else
          {
            RS_Set_Free(hRS); // освобожднаем RS
            if(hRS->pCallback)
            {
              hRS->pCallback(hRS, hRS->pMessagePtr); // обрабатываем ответ
            }
          }
        }
        else
        {
          RS_Handle_Receive_Start(hRS, hRS->pMessagePtr); // если сообщение пришло не полностью - продолжаем прием до таймаута
					
        }
      }
    }
    return; // Выходим после обработки IDLE
  }
  else
  {
    //-------------STANDARD UART HANDLING-------------
    HAL_UART_IRQHandler(hRS->huart);
  }
  
  
  //-------------CALL RS CALLBACKS------------  
  /* IF NO ERROR OCCURS */
  if(hRS->huart->ErrorCode == 0) 
  {
    
    // if first byte is received and receive is active
    if((hRS->huart->RxXferCount+1 == hRS->huart->RxXferSize) && RS_Is_RX_Busy(hRS)) 
    {
      RS_Timeout_Start(hRS);
    }
    RS_Timeout_Update(hRS);
    
    /* RX Callback - теперь НЕ вызываем здесь, ждем IDLE */
    
    /* TX Callback */
    if ((hRS->huart->TxXferCount == 0U) && RS_Is_TX_Busy(hRS) &&  // if all bytes are transmited and transmit is active
          hRS->huart->gState != HAL_UART_STATE_BUSY_TX)           // also check that receive "REALLY" isnt busy
      RS_UART_TxCpltCallback(hRS);
      
    /* NOTE: RX Callback больше не вызывается здесь - ждем IDLE для гарантии конца фрейма */
  }
  //----------------ERRORS HANDLER----------------
  else
  {
    if (hRS->huart->ErrorCode & HAL_UART_ERROR_ORE)
    {
        MB_Diagnostics_CharacterOverrunCnt();  // <-- Обнаружено переполнение
    }
    //TrackerCnt_Err(hRS->rs_err);
    /* de-init uart transfer */
    RS_Abort(hRS, ABORT_RS);
    RS_Handle_Receive_Start(hRS, hRS->pMessagePtr);
        
    // later, maybe, will be added specific handlers for err  
  }
  RS_UART_Handler_EXIT();
}


/**
  * @brief  Обработчик прерывания TIM.
  * @param  hRS     Указатель на хендлер RS.
  * @details  Попадание сюда = таймаут и перезапуск RS приема
  *           Добавить вызов этой функции в TIMx_IRQHandler() ВМЕСТО HAL_TIM_IRQHandler().
  */
void RS_TIM_Handler(RS_HandleTypeDef *hRS)
{
  if(hRS->htim == NULL)
  {
    return;
  }
  RS_TIM_Handler_ENTER();

  HAL_TIM_IRQHandler(hRS->htim);
  
  RS_Abort(hRS, ABORT_RS);
  
  hRS->RS_STATUS = RS_TIMEOUT;

  if(hRS->sRS_Mode < RS_MASTER_MODE_START)
    if(hRS->pMessagePtr->MbAddr == hRS->ID) // ошибка если таймаут по нашему сообщению
      TrackerCnt_Err(hRS->rs_err);
  
  if(hRS->sRS_Mode >= RS_MASTER_MODE_START) 
  { // Мастер: коллбек и освобождение для нового запроса
    RS_Set_Free(hRS);
    if(hRS->pCallback)
    {
      hRS->pCallback(hRS, hRS->pMessagePtr); // обрабатываем ответ
    }
  } else {
    // Слейв: перезапускаем прием
    RS_Handle_Receive_Start(hRS, hRS->pMessagePtr);
  }
  RS_TIM_Handler_EXIT();
}

/**
  * @brief  Запуск таймаута приема.
  * @param  hRS     Указатель на хендлер RS.
  * @return RS_RES  Статус операции.
  * @details  Запускает таймер для отсчета времени ожидания следующего байта.
  */
RS_StatusTypeDef RS_Timeout_Start(RS_HandleTypeDef *hRS)
{
  if(hRS->htim)
  {
    hRS->htim->Instance->CNT = 0; // reset cnt;
    if(hRS->sRS_Timeout)                // if timeout setted
    {
      hRS->htim->Instance->ARR = hRS->sRS_Timeout;
      HAL_TIM_Base_Start_IT(hRS->htim);
      RS_Set_RX_Active_Flags(hRS);
    }
  }
  return RS_OK;
}
/**
  * @brief  Остановка таймаута приема.
  * @param  hRS     Указатель на хендлер RS.
  * @return RS_RES  Статус операции.
  * @details  Останавливает таймер ожидания.
  */
RS_StatusTypeDef RS_Timeout_Stop(RS_HandleTypeDef *hRS)
{
  if(hRS->htim)
  {
    // Останавливаем таймаут
    if(hRS->sRS_Timeout)                
      HAL_TIM_Base_Stop_IT(hRS->htim);
    hRS->htim->Instance->CNT = 0;
    __HAL_TIM_CLEAR_IT(hRS->htim, TIM_IT_UPDATE);
  }
  return RS_OK;
}
/**
  * @brief  Обновление (сброс) таймаута приема.
  * @param  hRS     Указатель на хендлер RS.
  * @return RS_RES  Статус операции.
  * @details  Сбрасывает счетчик таймера в 0.
  */
RS_StatusTypeDef RS_Timeout_Update(RS_HandleTypeDef *hRS)
{
  if(hRS->htim)
  {
    hRS->htim->Instance->CNT = 0; // reset cnt;
  }
  return RS_OK;
}

//--------------------CALLBACK/HANDLER FUNCTIONS---------------------
//-------------------------------------------------------------------



//-------------------------------------------------------------------
//--------------WEAK PROTOTYPES FOR PROCESSING MESSAGE---------------
/** 
  * @brief  Пользовательская функция для ответа на запрос по UART.
  * @param  hRS     Указатель на хендлер RS.
  * @param  RS_msg  Указатель на структуру сообщения.
  * @return RS_RES  Статус о результате ответа на комманду.
  */
__weak RS_StatusTypeDef RS_Response(RS_HandleTypeDef *hRS, RS_MsgTypeDef *RS_msg)
{
  /* Redefine function for user purposes */
  return RS_ERR;
}

/** 
  * @brief  Пользовательская функция для сбора сообщения в буфер UART.
  * @param  hRS           Указатель на хендлер RS.
  * @param  RS_msg        Указатель на структуру сообщения.
  * @param  msg_uart_buff Указатель на буффер UART.
  * @return RS_RES        Статус о результате заполнения буфера.
  */
__weak RS_StatusTypeDef RS_Collect_Message(RS_HandleTypeDef *hRS, RS_MsgTypeDef *RS_msg, uint8_t *msg_uart_buff)
{
  /* Redefine function for user purposes */
  return RS_ERR;
}

/** 
  * @brief  Пользовательская функция для парса сообщения из буфера UART.
  * @param  hRS           Указатель на хендлер RS.
  * @param  RS_msg        Указатель на структуру сообщения.
  * @param  msg_uart_buff Указатель на буффер UART.
  * @return RS_RES        Статус о результате заполнения структуры.
  */
__weak RS_StatusTypeDef RS_Parse_Message(RS_HandleTypeDef *hRS, RS_MsgTypeDef *RS_msg, uint8_t *msg_uart_buff)
{
  /* Redefine function for user purposes */
  return RS_ERR;
}
//--------------WEAK PROTOTYPES FOR PROCESSING MESSAGE---------------
//-------------------------------------------------------------------
