/**
*******************************************************************************
* @file rs_message.h
* @brief Библиотека обмена сообщениями по RS-интерфейсу
*******************************************************************************
@defgroup RS_TOOLS RS Tools
@brief    Всякое для работы по UART/RS
@{
*******************************************************************************
* @details
Универсальная библиотека для работы с последовательными протоколами (Modbus, Custom)
через UART в режиме прерываний с поддержкой таймаутов.

@section posibility Основные возможности:
- Прием/передача в прерываниях
- Обработка IDLE линии для определения конца фрейма
- Таймауты приема через TIM
- Гибкая настройка размера сообщений

@section usage Использование:
1. Определить структуру сообщения и размеры буфера
2. Реализовать weak-функции обработки сообщений
3. Добавить вызовы RS_UART_Handler/RS_TIM_Handler в прерывания
4. Инициализировать через RS_Init() и запустить прием RS_Receive_IT()

@section features Особенности:
- Буфер: RS_Buffer[MSG_SIZE_MAX] Общий для приема/передачи
- Состояния: отслеживается через флаги в RS_HandleTypeDef
- Таймауты: контролируют максимальное время ожидания фрейма
******************************************************************************/
#ifndef __RS_LIB_H_
#define __RS_LIB_H_

#include "modbus_core.h"

/////////////////////////////////////////////////////////////////////
////////////////////////////---DEFINES---////////////////////////////
/* Check that all defines required by RS are defined */
#ifndef MSG_SIZE_MAX
#error Define MSG_SIZE_MAX (Maximum size of message). This is necessary to create buffer for UART.
#endif

/**
  * @cond Заглушки и внутренний недокументированный стаф
  */

/* Clear message-uart buffer */
#define RS_Clear_Buff(_buff_)       for(int i=0; i<MSG_SIZE_MAX;i++)  _buff_[i] = NULL

/* Set/Reset flags */
#define RS_Set_Free(_hRS_)          _hRS_->f.RS_Busy = 0
#define RS_Set_Busy(_hRS_)          _hRS_->f.RS_Busy = 1

#define RS_Set_RX_Flags(_hRS_)          _hRS_->f.RX_Busy = 1; _hRS_->f.RX_Done = 0;
#define RS_Set_RX_Active_Flags(_hRS_)   _hRS_->f.RX_Ongoing = 1


#define RS_Set_TX_Flags(_hRS_)          _hRS_->f.TX_Busy = 1; _hRS_->f.TX_Done = 0

#define RS_Reset_RX_Active_Flags(_hRS_) _hRS_->f.RX_Ongoing = 0;  \
																				_hRS_->f.RX_Continue = 0;
#define RS_Reset_RX_Flags(_hRS_)        RS_Reset_RX_Active_Flags(_hRS_); _hRS_->f.RX_Busy = 0; _hRS_->f.RX_Done = 0;
#define RS_Reset_TX_Flags(_hRS_)        _hRS_->f.TX_Busy = 0; _hRS_->f.TX_Done = 0

#define RS_Set_RX_End_Flag(_hRS_)   _hRS_->f.RX_Done = 1;
#define RS_Set_TX_End_Flag(_hRS_)   _hRS_->f.TX_Done = 1

#define RS_Set_RX_End(_hRS_)        RS_Reset_RX_Flags(_hRS_); RS_Set_RX_End_Flag(_hRS_)
#define RS_Set_TX_End(_hRS_)        RS_Reset_TX_Flags(_hRS_); RS_Set_TX_End_Flag(_hRS_)

/* Clear all RS stuff */
#define RS_Clear_All(_hRS_)         RS_Clear_Buff(_hRS_->pBufferPtr); RS_Reset_RX_Flags(_hRS_); RS_Reset_TX_Flags(_hRS_);

//#define MB_Is_RX_Busy(_hRS_)        ((_hRS_->huart->gState&HAL_USART_STATE_BUSY_RX) == HAL_USART_STATE_BUSY_RX)
//#define MB_Is_TX_Busy(_hRS_)        ((_hRS_->huart->gState&HAL_USART_STATE_BUSY_RX) == HAL_USART_STATE_BUSY_TX)
#define RS_Is_RX_Busy(_hRS_)        (_hRS_->f.RX_Busy == 1)
#define RS_Is_TX_Busy(_hRS_)        (_hRS_->f.TX_Busy == 1)


#ifndef RS_USER_VARS_NUMB
#define RS_USER_VARS_NUMB 0
#endif

#ifndef local_time
#define local_time() uwTick
#endif
/** @endcond */


/**
  * @addtogroup RS_DEBUG Tools for debug RS/UART/TIM
  * @ingroup RS_TOOLS
  * @brief Дефайны для отладки периферии
  * @{
  */

#ifndef RS_USER_VARS_NUMB
#define RS_USER_VARS_NUMB 0 ///< Количество переменных в @ref TrackerTypeDef
#endif

/** 
  * @brief Тип структуры для счетчиков-переменных
  * @param num_user_vars Есть возмоность добавления num_user_vars количества пользовательскиъх переменных
  */
#define TrackerTypeDef(num_user_vars) void *
/** @brief Инкрементировать переменную - успешных событий */
#define TrackerCnt_Ok(_cntstruct_)
/** @brief Инкрементировать переменную - ошибок */
#define TrackerCnt_Err(_cntstruct_)
/** @brief Инкрементировать переменную - предупреждений */
#define TrackerCnt_Warn(_cntstruct_)



#ifndef printf_rs
/** @brief Printf обычных событий RS/UART/TIM */
#define printf_rs(...)
#endif

#ifndef printf_rs_err
/** @brief Printf ошибок RS/UART/TIM */
#define printf_rs_err(...)
#endif


#ifndef RS_TIM_Handler_ENTER
/** @brief Действия при заходе в прерывания таймера */
#define RS_TIM_Handler_ENTER()  
#endif
#ifndef RS_TIM_Handler_EXIT
/** @brief Действия при выходе из прерывания таймера */
#define RS_TIM_Handler_EXIT()  
#endif

#ifndef RS_UART_Handler_ENTER
/** @brief Действия при заходе в прерывания UART */
#define RS_UART_Handler_ENTER()  
#endif
#ifndef RS_UART_Handler_EXIT
/** @brief Действия при выходе из прерывания UART */
#define RS_UART_Handler_EXIT()  
#endif
/** RS_TOOLS
  * @}
  */



// направление передачи rs485
#ifndef RS_EnableReceive
#define RS_EnableReceive()  ///< Функция изменения направления передачи на ПРИЕМ для RS-485
#endif
#ifndef RS_EnableTransmit
#define RS_EnableTransmit() ///< Функция изменения направления передачи на ПЕРЕДАЧУ для RS-485
#endif
////////////////////////////---DEFINES---////////////////////////////



/////////////////////////////////////////////////////////////////////
///////////////////////---STRUCTURES & ENUMS---//////////////////////
//------------------ENUMERATIONS--------------------
/** @brief Enums for respond CMD about RS status */
typedef enum    // RS_StatusTypeDef
{
  /* IN-CODE STATUS (start from 0x01, and goes up)*/
  /*0x01*/  RS_OK = 0x01,
  /*0x02*/  RS_ERR, 
  /*0x03*/  RS_ABORTED, 
  /*0x04*/  RS_BUSY,    
  /*0x05*/  RS_SKIP,  
  /*0x06*/  RS_TIMEOUT,  
  
  /*0x07*/  RS_COLLECT_MSG_ERR,
  /*0x08*/  RS_PARSE_MSG_ERR,
  
  // reserved values
//  /*0x00*/  RS_UNKNOWN_ERR =        0x00, ///< reserved for case, if no one error founded (nothing changed response from zero)
}RS_StatusTypeDef;


#define RS_MASTER_MODE_START   0x3 ///< Начало режимов мастера (до него - режим слейв)
/** @brief Enums for RS Modes */
typedef enum  // RS_ModeTypeDef
{
  RS_SLAVE_ALWAYS_WAIT    = 0x01,   ///< Слейв в постоянном ожидании
  RS_RESERVED             = 0x02,   ///< резерв
  RS_MASTER_REQUEST       = 0x03,   ///< Мастер с ручным запросом
  //RS_MASTER_POLLING       = 0x04,   ///< Мастер с опросом в фоновом режиме
}RS_ModeTypeDef;

/** @brief Enums for Abort modes */
typedef enum  // RS_AbortTypeDef
{
  ABORT_TX =      0x01,   ///< Отменить передачу
  ABORT_RX =      0x02,   ///< Отменить прием
  ABORT_RX_TX =   0x03,   ///< Отменить прием и передачу
  ABORT_RS =      0x04,   ///< Отменить любую работу UART в целом
}RS_AbortTypeDef;

//-----------STRUCTURE FOR HANDLE RS------------
/** @brief Struct for flags RS */
typedef struct
{
  unsigned                  RS_Busy:1;            ///< 1 - RS занят, 0 - RS свободен
  unsigned                  RX_Ongoing:1;         ///< 1 - Прием данных в активном состоянии,  0 - Ожидаем начало приема данных
  
  unsigned                  RX_Busy:1;            ///< 1 - Режим приема активен,    0 - Прием не активен
  unsigned                  TX_Busy:1;            ///< 1 - Режим передачи активен,  0 - Прием не активен       
  
  unsigned                  RX_Done:1;            ///< 1 - Прием закончен,          0 - Прием еще в процессе или не инициализирован
  unsigned                  TX_Done:1;            ///< 1 - Передача закончена,      0 - Передача еще в процессе или не инициализирована 
    
  // Выставление следующие флагов определяет пользователь
  unsigned                  RX_Continue:1;        ///< 0 - Продолжить принимать,        0 - Начать прием сначала
  unsigned                  MessageHandled:1;     ///< 1 - Обработка запроса успешна,   0 - Обработка запроса в процессе или ошибка
  unsigned                  EchoResponse:1;       ///< 1 - Ответить эхом,               0 - Ответить своим сообщением
  unsigned                  DeferredResponse:1;   ///< 1 - Не начинать передачу в IT,   0 - Ответить в прерывании
  unsigned                  DataUpdated:1;        ///< 1 - Данные были обновлены
	unsigned                  blink:1;							///< 1 - flag для использования при реализации linkBlink
}RS_FlagsTypeDef;



/** 
  * @brief  Handle for RS communication.
  * @note   Prefixes: h - handle, s - settings, f - flag
  */
typedef struct  // RS_HandleTypeDef
{   
  /* MESSAGE */
  uint8_t                   ID;                   ///< ID хендла
  RS_MsgTypeDef             *pMessagePtr;         ///< Указатель на структуру протокола
  uint8_t                   *pBufferPtr;          ///< Указатеь на буфер UART
  int32_t                   RS_Message_Size;      ///< size of whole message, not only data
  
  /* HANDLERS and SETTINGS */
  UART_HandleTypeDef        *huart;               ///< Хендл UART
  TIM_HandleTypeDef         *htim;                ///< Хендл TIM
  RS_ModeTypeDef            sRS_Mode;             ///< Настройка: слейв/мастер @ref RS_ModeTypeDef
  uint16_t                  sRS_Timeout;          ///< Настройка: Таймаут в тиках таймера
  void                      (*pCallback)(void*, void*);   ///< Указатель на коллбек: принят ответ в режиме мастер
  
  /* FLAGS */   
  RS_FlagsTypeDef           f;                    ///< Флаги для контроля приема/передачи
  
  /* RS STATUS */
  uint32_t                  lastPacketTick;       ///< Время последнего принятого пакета
  RS_StatusTypeDef          RS_STATUS;            ///< Статус RS
  
  TrackerTypeDef(RS_USER_VARS_NUMB) rs_err;
}RS_HandleTypeDef;
extern RS_HandleTypeDef hmodbus1;


///////////////////////---STRUCTURES & ENUMS---//////////////////////


/////////////////////////////////////////////////////////////////////
///////////////////////////---FUNCTIONS---///////////////////////////
//----------------FUNCTIONS FOR PROCESSING MESSAGE-------------------
/*--------------------Defined by users purposes--------------------*/
/* Пользовательская функция для ответа на запрос по UART */
RS_StatusTypeDef RS_Response(RS_HandleTypeDef *hRS, RS_MsgTypeDef *RS_msg);

/* Пользовательская функция для обработки принятого ответа по UART */
__weak RS_StatusTypeDef RS_Response_Callback(RS_HandleTypeDef *hRS, RS_MsgTypeDef *RS_msg);

/* Пользовательская функция для сбора сообщения в буфер UART */
RS_StatusTypeDef RS_Collect_Message(RS_HandleTypeDef *hRS, RS_MsgTypeDef *RS_msg, uint8_t *msg_uart_buff);

/* Пользовательская функция для парса сообщения из буфера UART */
RS_StatusTypeDef RS_Parse_Message(RS_HandleTypeDef *hRS, RS_MsgTypeDef *RS_msg, uint8_t *msg_uart_buff);


//-------------------------GENERAL FUNCTIONS-------------------------
/*-----------------Should be called from main code-----------------*/
/* Начать прием по прерываниям */
RS_StatusTypeDef RS_Receive_IT(RS_HandleTypeDef *hRS, RS_MsgTypeDef *RS_msg);

/* Начать передачу по прерываниям */
RS_StatusTypeDef RS_Transmit_IT(RS_HandleTypeDef *hRS, RS_MsgTypeDef *RS_msg);

/* Инициалазация структуры @ref RS_HandleTypeDef */
RS_StatusTypeDef RS_Init(RS_HandleTypeDef *hRS, UART_HandleTypeDef *huart, TIM_HandleTypeDef *htim, uint8_t *pRS_BufferPtr);

/* Отменить прием/передачу RS/UART */
RS_StatusTypeDef RS_Abort(RS_HandleTypeDef *hRS, RS_AbortTypeDef AbortMode);
//-------------------------GENERAL FUNCTIONS-------------------------


//-------------------------------------------------------------------
//--------------------CALLBACK/HANDLER FUNCTIONS---------------------
/* Обработчик для начала приема */
RS_StatusTypeDef RS_Handle_Receive_Start(RS_HandleTypeDef *hRS, RS_MsgTypeDef *RS_msg);
/* Обработчик для начала передачи */
RS_StatusTypeDef RS_Handle_Transmit_Start(RS_HandleTypeDef *hRS, RS_MsgTypeDef *RS_msg);
/* UART TX Callback: коллбек после окончания передачи */
RS_StatusTypeDef RS_UART_TxCpltCallback(RS_HandleTypeDef *hRS);
/* Обработчик прерывания UART */
void RS_UART_Handler(RS_HandleTypeDef *hRS);
/* Обработчик прерывания TIM */
void RS_TIM_Handler(RS_HandleTypeDef *hRS);
/* Запуск таймаута приема. */
RS_StatusTypeDef RS_Timeout_Start(RS_HandleTypeDef *hRS);
/* Остановка таймаута приема. */
RS_StatusTypeDef RS_Timeout_Stop(RS_HandleTypeDef *hRS);
/* Обновление (сброс) таймаута приема. */
RS_StatusTypeDef RS_Timeout_Update(RS_HandleTypeDef *hRS);
//--------------------CALLBACK/HANDLER FUNCTIONS---------------------
///////////////////////////---FUNCTIONS---///////////////////////////

/** RS_TOOLS
  * @}
  */

#endif // __RS_LIB_H_
