/** 
**************************************************************************
* @file rs_message.c
* @brief Модуль для реализации протоколов по RS/UART.
**************************************************************************\
* @details
* Данный модуль реализует основные функции для приема и передачи сообщений
* по протоколу RS через UART в режиме прерываний. Реализована обработка
* приема и передачи данных, управление состояниями RS, а также функции для
* инициализации и управления периферией.
*
* Реализованы следующие функции:
* - RS_Receive_IT() — запуск приема данных в прерывании по UART.
* - RS_Transmit_IT() — запуск передачи данных в прерывании по UART.
* - RS_Init() — инициализация структуры RS и привязка периферии.
* - RS_ReInit_UART() — переинициализация UART и перезапуск приема данных.
* - RS_Abort() — остановка работы RS/UART с очисткой флагов и структур.
* - RS_Handle_Receive_Start() — обработка старта приема данных по RS.
*
* В модуле также определен буфер RS_Buffer[] для хранения принимаемых/передаваемых данных.
*
* @note
* Для корректной работы модуля предполагается использование соответствующих
* обработчиков прерываний UART и таймера (RS_UART_Handler(), RS_TIM_Handler()),
* которые надо вызывать с обработчиках используемой периферии

@verbatim
//-------------------Функции-------------------//
Functions: users
  -	RS_Parse_Message/RS_Collect_Message	Заполнение структуры сообщения и буфера
  -	RS_Response  									Ответ на сообщение
  - RS_Define_Size_of_RX_Message	Определение размера принимаемых данных

Functions: general
  -	RS_Receive_IT									Ожидание комманды и ответ на неё
  -	RS_Transmit_IT								Отправление комманды и ожидание ответа
  -	RS_Init												Инициализация переферии и структуры для RS
  -	RS_ReInit_UART								Реинициализация UART для RS
  -	RS_Abort											Отмена приема/передачи по ЮАРТ
  -	RS_Init												Инициализация периферии и modbus handler

Functions: callback/handler
  - RS_Handle_Receive_Start				Функция для запуска приема или остановки RS
  - RS_Handle_Transmit_Start			Функция для запуска передачи или остановки RS
  
  -	RS_UART_RxCpltCallback				Коллбек при окончании приема или передачи
    RS_UART_TxCpltCallback		
    
  - RS_UART_Handler								Обработчик прерывания для UART
  - RS_TIM_Handler								Обработчик прерывания для TIM

@endverbatim
*************************************************************************/
#include "rs_message.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 	Start receive IT.
  * @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
	
	// start receiving
	uart_res = HAL_UART_Receive_IT(hRS->huart, hRS->pBufferPtr, RX_FIRST_PART_SIZE); // 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("\n%d: Error RS: Failed to start RS receiving...", uwTick);
	}
	else
  {
		RS_RES = RS_OK;
    printf_rs("\n%d: RS: Start Receiving...", uwTick);
  }
	
	hRS->RS_STATUS = RS_RES;
	return RS_RES; // returns result of receive init	
}

/**
  * @brief 	Start transmit IT.
  * @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();
//    for(int i = 0; i < hRS->sRS_Timeout; i++);
    
		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 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("\n%d: Error RS: Failed to start RS transmitting...", uwTick);
    }
    else
    {
      RS_RES = RS_OK;
      printf_rs("\n%d: RS: Start Transmitting...", uwTick);
    }
  }
	
	
	hRS->RS_STATUS = RS_RES;
	return RS_RES; // returns result of transmit init 	
}

/** 
	* @brief 	Initialize UART and handle RS stucture.
	* @param 	hRS 					- указатель на хендлер RS.
	* @param 	suart 				- указатель на структуру с настройками UART.
	* @param 	stim 					- указатель на структуру с настройками таймера.
	* @param 	pRS_BufferPtr - указатель на буффер для приема-передачи по UART. Если он NULL, то поставиться библиотечный буфер.
	* @return RS_RES				- статус о состоянии RS после инициализации.
	* @note		Инициализация перефирии и структуры для приема-передачи по 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;
	// init uart
  
//	RS_UART_Init();
	hRS->huart = huart;
	
//	RS_TIM_Init();
	hRS->htim = htim;
	
	if (hRS->sRS_RX_Size_Mode == NULL)
		return RS_ERR;
	
	// 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 	ReInitialize UART and RS receive.
	* @param 	hRS 					- указатель на хендлер RS.
	* @param 	suart 				- указатель на структуру с настройками UART.
	* @return RS_RES				- статус о состоянии RS после инициализации.
	* @note		Реинициализация UART и приема по RS.
	*/
HAL_StatusTypeDef RS_ReInit_UART(RS_HandleTypeDef *hRS, UART_HandleTypeDef *huart)
{
	HAL_StatusTypeDef RS_RES;
	hRS->f.ReInit_UART = 0;
	
//	// check is settings are valid
//	if(Check_UART_Init_Struct(suart) != HAL_OK)
//		return HAL_ERROR;
  
	RS_Abort(hRS, ABORT_RS);
  
	RS_UART_DeInit(huart);
  
  RS_UART_Init();
	
	
	RS_Receive_IT(hRS, hRS->pMessagePtr);
	return RS_RES;
}


/**
	* @brief 	Abort RS/UART.
	* @param 	hRS 				- указатель на хендлер RS.
	* @param 	AbortMode 	- выбор, что надо отменить.
							- ABORT_TX:		 Отмена передачи по ЮАРТ, с очищением флагов TX,
							- ABORT_RX:		 Отмена приема по ЮАРТ, с очищением флагов RX,
							- ABORT_RX_TX: Отмена приема и передачи по ЮАРТ,
							- ABORT_RS: 	 Отмена приема-передачи RS, с очищением всей структуры.
	* @return RS_RES			- статус о состоянии RS после аборта.
	* @note		Отмена работы UART в целом или отмена приема/передачи RS. 
						Также очищается хендл hRS.
	*/
RS_StatusTypeDef RS_Abort(RS_HandleTypeDef *hRS, RS_AbortTypeDef AbortMode)
{
	HAL_StatusTypeDef uart_res = 0;
	hRS->htim->Instance->CNT = 0;
	__HAL_TIM_CLEAR_IT(hRS->htim, TIM_IT_UPDATE);
	
	if(hRS->sRS_Timeout)								// if timeout setted
		HAL_TIM_Base_Stop_IT(hRS->htim);	// stop timeout
	
	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 	Handle for starting receive.
	* @param 	hRS 		- указатель на хендлер RS.
	* @param 	RS_msg 	- указатель на структуру сообщения.
	* @return RS_RES	- статус о состоянии RS после инициализации приема или окончания общения.
	* @note		Определяет начинать прием команды/ответа или нет.
	*/
RS_StatusTypeDef RS_Handle_Receive_Start(RS_HandleTypeDef *hRS, RS_MsgTypeDef *RS_msg)
{	
	RS_StatusTypeDef RS_RES = 0;
	
	switch(hRS->sRS_Mode)
	{
		case SLAVE_ALWAYS_WAIT: 											// in slave mode with permanent waiting
			RS_RES = RS_Receive_IT(hRS, RS_msg); break; // start receiving again
		case SLAVE_TIMEOUT_WAIT:											// in slave mode with timeout waiting (start receiving cmd by request)
			RS_Set_Free(hRS);	 RS_RES = RS_OK; break;		// end RS communication (set RS unbusy)
	}
  
  if(RS_RES != RS_OK)
  {
  }
	
	return RS_RES;
}
/**  
	* @brief 	Handle for starting transmit.
	* @param 	hRS 		- указатель на хендлер RS.
	* @param 	RS_msg 	- указатель на структуру сообщения.
	* @return RS_RES	- статус о состоянии RS после инициализации передачи.
	* @note		Определяет отвечать ли на команду или нет.
	*/
RS_StatusTypeDef RS_Handle_Transmit_Start(RS_HandleTypeDef *hRS, RS_MsgTypeDef *RS_msg)
{	
	RS_StatusTypeDef RS_RES = 0;
	
	switch(hRS->sRS_Mode)
	{
		case SLAVE_ALWAYS_WAIT: 	// in slave mode always response
		case SLAVE_TIMEOUT_WAIT:	// transmit response
			RS_RES = RS_Transmit_IT(hRS, RS_msg); break; 
	}
  if(RS_RES != RS_OK)
  {
  }
  
	return RS_RES;
}

/**  
	* @brief 	UART RX Callback: define behaviour after receiving parts of message.
	* @param 	hRS 		- указатель на хендлер RS.
	* @return RS_RES	- статус о состоянии RS после обработки приема.
	* @note		Контролирует прием сообщения: определяет размер принимаемой посылки и обрабатывает его.
	*/
RS_StatusTypeDef RS_UART_RxCpltCallback(RS_HandleTypeDef *hRS)
{
	RS_StatusTypeDef RS_RES = 0;
	HAL_StatusTypeDef uart_res = 0;
	
	// if we had received bytes before ByteCnt
	if((hRS->sRS_RX_Size_Mode == RS_RX_Size_NotConst) && (hRS->f.RX_Half == 0)) // if data size isnt constant and its first half, and
	{	// First receive part of message, then define size of rest of message, and start receive it
		hRS->f.RX_Half = 1;
		//---------------FIND DATA SIZE-----------------
		uint32_t NuRS_of_Rest_Bytes = 0xFFFF;
		RS_RES = RS_Define_Size_of_RX_Message(hRS, &NuRS_of_Rest_Bytes);
		
		
    // if we need to skip this message - restart receive
		if(RS_RES == RS_SKIP || NuRS_of_Rest_Bytes == 0xFFFF)
		{
			RS_Abort(hRS, ABORT_RX);
			RS_RES = RS_Handle_Receive_Start(hRS, hRS->pMessagePtr);
			return RS_RES;
		}
    
		// if there is no bytes to receive
    if(NuRS_of_Rest_Bytes == 0)
    {
      hRS->f.RX_Half = 0;

      //---------PROCESS DATA & ENDING RECEIVING--------
      RS_Set_RX_End(hRS);
      
      if(hRS->sRS_Timeout)								// if timeout setted
        HAL_TIM_Base_Stop_IT(hRS->htim);	// stop timeout
      
      // parse received data
      RS_RES = RS_Parse_Message(hRS, hRS->pMessagePtr, hRS->pBufferPtr); // parse message
      
      // RESPONSE
      RS_RES = RS_Response(hRS, hRS->pMessagePtr);	
      return RS_RES;
    }
    
		
		//-------------START UART RECEIVE---------------
		uart_res = HAL_UART_Receive_IT(hRS->huart, (hRS->pBufferPtr + RX_FIRST_PART_SIZE), NuRS_of_Rest_Bytes); 
		
		if(uart_res != HAL_OK)
		{// need uart status, so doesnt write abort to RS_RES
			RS_RES = RS_Abort(hRS, ABORT_RS);
		}
		else
			RS_RES = RS_OK;
	}	 
	else // if we had received whole message
	{
		hRS->f.RX_Half = 0;

		//---------PROCESS DATA & ENDING RECEIVING--------
		RS_Set_RX_End(hRS);
		
		if(hRS->sRS_Timeout)								// if timeout setted
			HAL_TIM_Base_Stop_IT(hRS->htim);	// stop timeout
		
		// parse received data
		RS_RES = RS_Parse_Message(hRS, hRS->pMessagePtr, hRS->pBufferPtr); // parse message
    
		// RESPONSE
		RS_RES = RS_Response(hRS, hRS->pMessagePtr);	
	}
	
	return RS_RES;	
}


/**  
	* @brief 	UART TX Callback: define behaviour after transmiting message.
	* @param 	hRS 		- указатель на хендлер RS.
	* @return RS_RES	- статус о состоянии RS после обработки приема.
	* @note		Определяет поведение 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);
	RS_EnableReceive();
//  for(int i = 0; i < hRS->sRS_Timeout; i++);
	
	//-----------START RECEIVING or END RS----------
	RS_RES = RS_Handle_Receive_Start(hRS, hRS->pMessagePtr);
	
	return RS_RES;
}

/**  
	* @brief 	Handler for UART.
	* @param 	hRS 		- указатель на хендлер RS.
	* @note		Обрабатывает ошибки если есть и вызывает RS Коллбеки.
	*					Добавить вызов этой функции в UARTx_IRQHandler() после HAL_UART_IRQHandler().
	*/
void RS_UART_Handler(RS_HandleTypeDef *hRS)
{
	//-------------CALL RS CALLBACKS------------	
	/* IF NO ERROR OCCURS */
	if(hRS->huart->ErrorCode == 0) 
	{
		hRS->htim->Instance->CNT = 0; // reset cnt;
		/* Start timeout */
		if(hRS->sRS_Timeout)								// if timeout setted
			if((hRS->huart->RxXferCount+1 == hRS->huart->RxXferSize) && RS_Is_RX_Busy(hRS)) // if first byte is received and receive is active
      {
        hRS->htim->Instance->ARR = hRS->sRS_Timeout; // reset cnt;
				HAL_TIM_Base_Start_IT(hRS->htim);
        RS_Set_RX_Active_Flags(hRS);
      }
		
		/* RX Callback */
		if ((	hRS->huart->RxXferCount == 0U) && RS_Is_RX_Busy(hRS) && 	// if all bytes are received and receive is active
					hRS->huart->RxState != HAL_UART_STATE_BUSY_RX)						// also check that receive "REALLY" isnt busy
			RS_UART_RxCpltCallback(hRS);
		
		/* 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);
	}
	//----------------ERRORS HANDLER----------------
	else
	{
		/* 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	
	}
}


/**
	* @brief 	Handler for TIM.
	* @param 	hRS 		- указатель на хендлер RS.
	* @note		Попадание сюда = таймаут и перезапуск RS приема
	*					Добавить вызов этой функции в TIMx_IRQHandler() после HAL_TIM_IRQHandler().
	*/
void RS_TIM_Handler(RS_HandleTypeDef *hRS)
{
	HAL_TIM_Base_Stop_IT(hRS->htim);
	RS_Abort(hRS, ABORT_RS);	
	
	RS_Handle_Receive_Start(hRS, hRS->pMessagePtr);
}
//--------------------CALLBACK/HANDLER FUNCTIONS---------------------
//-------------------------------------------------------------------



//-------------------------------------------------------------------
//--------------WEAK PROTOTYPES FOR PROCESSING MESSAGE---------------
/** 
	* @brief 	Respond accord to received message.
	* @param 	hRS 		- указатель на хендлер RS.
	* @param 	RS_msg 	- указатель на структуру сообщения.
	* @return RS_RES	- статус о результате ответа на комманду.
	* @note		Обработка принятой комманды и ответ на неё.
	*/
__weak RS_StatusTypeDef RS_Response(RS_HandleTypeDef *hRS, RS_MsgTypeDef *RS_msg)
{
	/* Redefine function for user purposes */
	return RS_ERR;
}

/** 
	* @brief 	Collect message in buffer to transmit it.
	* @param 	hRS 					- указатель на хендлер RS.
	* @param 	RS_msg 				- указатель на структуру сообщения.
	* @param 	msg_uart_buff	- указатель на буффер UART.
	* @return RS_RES				- статус о результате заполнения буфера.
	* @note		Заполнение буффера UART из структуры сообщения.
	*/
__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 	Parse message from buffer to process it.
	* @param 	hRS 					- указатель на хендлер RS.
	* @param 	RS_msg 				- указатель на структуру сообщения.
	* @param 	msg_uart_buff	- указатель на буффер UART.
	* @return RS_RES				- статус о результате заполнения структуры.
	* @note		Заполнение структуры сообщения из буффера UART.
	*/
__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;
}

/** 
	* @brief 	Define size of RX Message that need to be received.
	* @param 	hRS 					- указатель на хендлер RS.
	* @param 	rx_data_size 	- указатель на переменную для записи кол-ва байт для принятия.
	* @return RS_RES				- статус о корректности рассчета кол-ва байт для принятия.
	* @note		Определение сколько байтов надо принять по протоколу.
	*/
__weak RS_StatusTypeDef RS_Define_Size_of_RX_Message(RS_HandleTypeDef *hRS, uint32_t *rx_data_size)
{
	/* Redefine function for user purposes */
	return RS_ERR;
}
//--------------WEAK PROTOTYPES FOR PROCESSING MESSAGE---------------
//-------------------------------------------------------------------
