#include "protocan.h"

struct device CurrentDevice;
struct device Device_on_the_Network[8][16];
struct controlflags ControlFlags;

CAN_HandleTypeDef *_HCAN = 0;
RTC_HandleTypeDef *_HRTC = 0;
TIM_HandleTypeDef *_HTIM = 0;

uint8_t CurrentStep = 1;
uint8_t LastStep = 0;
struct RXMsg rxMsg[CAN_RX_BUFFER_SIZE];

/**
* @brief Проверяет, является ли год високосным.
* В системе год хранится как смещение от 2000. Например, значение 24 соответствует 2024.
* Эта функция добавляет 2000 к значению и проверяет, високосный ли получившийся год.
* @param year Смещение от 2000, тип uint8_t (от 0 до 255).
* @return _Bool true, если год високосный; false — иначе.
*/
_Bool IsLeapYear(uint8_t year)
{
	year+=2000;
	return (year%400==0)||((year%4==0)&&(year%100!=0));
}

/**
* @brief Возвращает количество доступных сообщений в буфере приёма CAN.
* Используется кольцевой буфер. 
* Возвращаемое значение — количество элементов между указателями LastStep и CurrentStep.
* @return uint16_t Количество доступных сообщений, ожидающих обработки, в буфере.
*/
uint16_t AvailableCanRxMsg(void)
{
	return ((uint16_t)(CAN_RX_BUFFER_SIZE + (LastStep - CurrentStep + 1)))%CAN_RX_BUFFER_SIZE;
}

void PROTOCAN_DEINIT(uint8_t stage)
{
	switch(stage) {
		case 3:
			#if (USE_HAL_TIM_REGISTER_CALLBACKS == 1)
			HAL_TIM_UnRegisterCallback(_HTIM, HAL_TIM_PERIOD_ELAPSED_CB_ID);
			#endif
		case 2:
		case 1:
			#if (USE_HAL_CAN_REGISTER_CALLBACKS == 1)
			HAL_CAN_UnRegisterCallback(_HCAN, HAL_CAN_RX_FIFO0_MSG_PENDING_CB_ID);
			#endif
	}
	return;
}

/**
* @brief Инициализация переферии
* @details Инициализация указателей на HCAN, HRTC, установка фильтров CAN.
* @note Фильтры CAN описаны в разделе PROTOCAN_CAN_FILTERS().
*/
PROTOCAN_INIT_StatusTypeDef PROTOCAN_INIT(CAN_HandleTypeDef *tmp_hcan, RTC_HandleTypeDef *tmp_hrtc, TIM_HandleTypeDef *tmp_tim)
{
	//HAL_CAN_Start(_HCAN);
	//HAL_CAN_ActivateNotification(_HCAN, CAN_IT_RX_FIFO0_MSG_PENDING | CAN_IT_ERROR | CAN_IT_BUSOFF | CAN_IT_LAST_ERROR_CODE);
	//MX_TIM4_Init();
	//MX_RTC_Init();
	unsigned initStage = 0;
	if(tmp_hcan) {
		_HCAN = tmp_hcan;
		#if (USE_HAL_CAN_REGISTER_CALLBACKS == 1)
		HAL_CAN_RegisterCallback(_HCAN, HAL_CAN_RX_FIFO0_MSG_PENDING_CB_ID, ProtoCanRxFifo0MsgPendingCallback); 
		#endif
	}	else {
		PROTOCAN_DEINIT(initStage);
		return PROTOCAN_HCAN_ERROR;
	}
	initStage++;
	if(tmp_hrtc) {
		_HRTC = tmp_hrtc;
	} else {
		PROTOCAN_DEINIT(initStage);
		return PROTOCAN_HRTC_ERROR;
	}
	initStage++;
	if(tmp_tim) {
		_HTIM = tmp_tim;
		#if (USE_HAL_TIM_REGISTER_CALLBACKS == 1)
		HAL_TIM_RegisterCallback(_HTIM, HAL_TIM_PERIOD_ELAPSED_CB_ID, ProtoCanPulseCallback);
		#endif
	} else {
		PROTOCAN_DEINIT(initStage);
		return PROTOCAN_TIM_ERROR;
	}
	initStage++;
	PROTOCAN_FILTERS();
	ControlFlags.IsPulse = 1;
	return PROTOCAN_OK;
}

/**
* @brief Функция с обработкой полученных запросов
* @details В бесконечном цикле функция ожидает приёма сообщения. После этого сообщение распределяется в зависимости от DataType.
* Обработка запроса аналоговых значений - PROTOCAN_AnalogProcessing().
*	Обработка широковещательных запросов - PROTOCAN_BroadcastProcessing().
* Обработка запроса дискретных значений - PROTOCAN_DiscreticProcessing().
* Обработка запроса к общему адресному пространству - PROTOCAN_GeneralAddressSpace_Answer().
* Обработка Modbus - PROTOCAN_ModbusProcessing().
*/
void PROTOCAN_LOOP(void)
{
	//HAL_TIM_Base_Start_IT(&htim4);
	while(1)
	{
		if(AvailableCanRxMsg())
		{
				if(rxMsg[CurrentStep].eID.Fields.MsgType == DATA_TYPE_ANALOG)
				{
					PROTOCAN_AnalogProcessing(rxMsg[CurrentStep]);
				}
				else if(rxMsg[CurrentStep].eID.Fields.MsgType == DATA_TYPE_BROADCAST)
				{
					PROTOCAN_BroadcastProcessing(rxMsg[CurrentStep]);					
				}
				else if(rxMsg[CurrentStep].eID.Fields.MsgType == DATA_TYPE_DISCRETE)
				{
					PROTOCAN_DiscreticProcessing(rxMsg[CurrentStep]);
				}
				else if(rxMsg[CurrentStep].eID.Fields.MsgType == DATA_TYPE_GENERAL_ADDRESS_SPACE)
				{
					PROTOCAN_GeneralAddressSpace_Answer(rxMsg[CurrentStep]);
				}
				else if(rxMsg[CurrentStep].eID.Fields.MsgType == DATA_TYPE_MODBUS_COIL ||
					rxMsg[CurrentStep].eID.Fields.MsgType == DATA_TYPE_MODBUS_DISCRETE ||
					rxMsg[CurrentStep].eID.Fields.MsgType == DATA_TYPE_MODBUS_HOLDING ||
					rxMsg[CurrentStep].eID.Fields.MsgType == DATA_TYPE_MODBUS_INPUT)
				{
						PROTOCAN_ModbusProcessing(rxMsg[CurrentStep]);
				}
				else if(rxMsg[CurrentStep].eID.Fields.MsgType == DATA_TYPE_ERROR)
				{
					CanRequestError(rxMsg[CurrentStep]);
				}
			CurrentStep = (uint16_t)(CurrentStep + 1) % CAN_RX_BUFFER_SIZE;
		}
	}
}

/**
* @brief Функция обработки аналоговых запросов.
* @param struct RXMsg _rxMsg - структура для полученного сообщения.
* @details Функция, сортирующая запросы по типам и вызывающая соответствующие функции. Типы запросов: Универсальный, Уставки, Напряжение, Ток, Температура.
* @return HAL_StatusTypeDef Статус выполнения команды CAN (успех или ошибка).
*/
HAL_StatusTypeDef PROTOCAN_AnalogProcessing(struct RXMsg _rxMsg)
{
	msgAnalogType msg;
	msg.Body = _rxMsg.eID.Fields.MsgBody;
	switch (msg.Fields.SensorType)
	{
		case SENSOR_TYPE_ANALOG_UNIVERSAL:
		{
			return ProtoCanMsgToAnalogUniversal(_rxMsg);
			break;
		}
		case SENSOR_TYPE_ANALOG_USTAVKI:
		{
			return ProtoCanMsgToAnalogUSTAVKI(_rxMsg);
			break;
		}
		case SENSOR_TYPE_ANALOG_U:
		{
			return ProtoCanMsgToAnalogUSens(_rxMsg);
			break;
		}
		case SENSOR_TYPE_ANALOG_I:
		{
			return ProtoCanMsgToAnalogISens(_rxMsg);		
			break;
		}
		case SENSOR_TYPE_ANALOG_T:
		{
			return ProtoCanMsgToAnalogTSens(_rxMsg);
			break;
		}
		default:
			//RESERVE SENSOR TYPE
			return HAL_ERROR;
			break;
	}
	return HAL_ERROR;
}

/**
* @brief Функция ответа на запрос данных со всех датчиков.
* Создает сообщение с определенными данными и отправляет его через CAN-шину.
* @param _rxMsg Структура входящего сообщения, используется для формирования ID.
* @return HAL_StatusTypeDef Статус выполнения операции HAL, например HAL_OK.
*/
__weak HAL_StatusTypeDef ProtoCanMsgToAnalogUniversal(struct RXMsg _rxMsg)
{
	CAN_TxHeaderTypeDef TxHeader;
	uint32_t TxMailBox = 0;
	uint8_t data[8];
	TxHeader.IDE = CAN_ID_EXT;
	TxHeader.TransmitGlobalTime = DISABLE;
	TxHeader.RTR = CAN_RTR_DATA;
	extID tmp_eID;
	tmp_eID.BitAll = _rxMsg.eID.BitAll;
	tmp_eID.Fields.Route = ROUTE_SLAVE;
	TxHeader.ExtId = tmp_eID.BitAll;
	TxHeader.DLC = 6;
	data[0] = 'U';
	data[1] = 'N';
	data[2] = 'I';
	data[3] = 'V';
	data[4] = 'E';
	data[5] = 'R';
	return HAL_CAN_AddTxMessage(_HCAN, &TxHeader, data, &TxMailBox);
}

/**
* @brief Функция ответа на запрос уставок по CAN.
* Создает сообщение с определенными данными и отправляет его через CAN-шину.
* @param _rxMsg Структура входящего сообщения, используется только для формирования ID.
* @return HAL_StatusTypeDef Статус выполнения операции HAL, например HAL_OK.
*/
__weak HAL_StatusTypeDef ProtoCanMsgToAnalogUSTAVKI(struct RXMsg _rxMsg)
{
	CAN_TxHeaderTypeDef TxHeader;
	uint32_t TxMailBox = 0;
	uint8_t data[8];
	TxHeader.IDE = CAN_ID_EXT;
	TxHeader.TransmitGlobalTime = DISABLE;
	TxHeader.RTR = CAN_RTR_DATA;
	extID tmp_eID;
	tmp_eID.BitAll = _rxMsg.eID.BitAll;
	tmp_eID.Fields.Route = ROUTE_SLAVE;
	TxHeader.ExtId = tmp_eID.BitAll;
	TxHeader.DLC = 7;
	data[0] = 'U';
	data[1] = 'S';
	data[2] = 'T';
	data[3] = 'A';
	data[4] = 'V';
	data[5] = 'K';
	data[6] = 'I';
	return HAL_CAN_AddTxMessage(_HCAN, &TxHeader, data, &TxMailBox);
}

/**
* @brief Функция ответа на запрос данных с датчиков напряжения.
* Создает сообщение с указанными параметрами и отправляет его через CAN-шину.
* @param _rxMsg Структура входящего сообщения, используемая для формирования идентификатора и данных.
* @return HAL_StatusTypeDef Статус выполнения операции HAL, например HAL_OK при успешной отправке.
*/
__weak HAL_StatusTypeDef ProtoCanMsgToAnalogUSens(struct RXMsg _rxMsg)
{
	CAN_TxHeaderTypeDef TxHeader;
	uint32_t TxMailBox = 0;
	uint8_t data[8];
	TxHeader.IDE = CAN_ID_EXT;
	TxHeader.TransmitGlobalTime = DISABLE;
	TxHeader.RTR = CAN_RTR_DATA;
	extID tmp_eID;
	tmp_eID.BitAll = _rxMsg.eID.BitAll;
	tmp_eID.Fields.Route = ROUTE_SLAVE;
	TxHeader.ExtId = tmp_eID.BitAll;
	TxHeader.DLC = 6;
	msgAnalogType msg;
	msg.Body = _rxMsg.eID.Fields.MsgBody;
	data[0] = 'U';
	data[1] = 'S';
	data[2] = 47 + msg.Fields.SensorID / 1000;
	data[3] = 47 + msg.Fields.SensorID / 100 % 10;
	data[4] = 47 + msg.Fields.SensorID / 10 % 10;
	data[5] = 47 + msg.Fields.SensorID % 10;
	return HAL_CAN_AddTxMessage(_HCAN, &TxHeader, data, &TxMailBox);
}

/**
* @brief Функция ответа на запрос данных с датчиков тока.
* Создает сообщение с определенными параметрами и отправляет его через CAN-шину.
* @param _rxMsg Структура входящего сообщения, используется для формирования идентификатора и данных.
* @return HAL_StatusTypeDef Статус операции HAL, например HAL_OK при успешной отправке.
*/
__weak HAL_StatusTypeDef ProtoCanMsgToAnalogISens(struct RXMsg _rxMsg)
{
	CAN_TxHeaderTypeDef TxHeader;
	uint32_t TxMailBox = 0;
	uint8_t data[8];
	TxHeader.IDE = CAN_ID_EXT;
	TxHeader.TransmitGlobalTime = DISABLE;
	TxHeader.RTR = CAN_RTR_DATA;
	extID tmp_eID;
	tmp_eID.BitAll = _rxMsg.eID.BitAll;
	tmp_eID.Fields.Route = ROUTE_SLAVE;
	TxHeader.ExtId = tmp_eID.BitAll;
	TxHeader.DLC = 6;
	msgAnalogType msg;
	msg.Body = _rxMsg.eID.Fields.MsgBody;
	data[0] = 'I';
	data[1] = 'S';
	data[2] = 47 + msg.Fields.SensorID / 1000;
	data[3] = 47 + msg.Fields.SensorID / 100 % 10;
	data[4] = 47 + msg.Fields.SensorID / 10 % 10;
	data[5] = 47 + msg.Fields.SensorID % 10;
	return HAL_CAN_AddTxMessage(_HCAN, &TxHeader, data, &TxMailBox);
}

/**
* @brief Функция ответа на запрос данных с датчиков температуры.
* Создает сообщение с определенным заголовком, формирует данные по ID сенсора и отправляет его через CAN.
* @param _rxMsg Структура полученного сообщения, которая используется для формирования идентификатора.
* @return HAL_StatusTypeDef Статус выполнения функции HAL, например HAL_OK при успехе.
*/
__weak HAL_StatusTypeDef ProtoCanMsgToAnalogTSens(struct RXMsg _rxMsg)
{
	CAN_TxHeaderTypeDef TxHeader;
	uint32_t TxMailBox = 0;
	uint8_t data[8];
	TxHeader.IDE = CAN_ID_EXT;
	TxHeader.TransmitGlobalTime = DISABLE;
	TxHeader.RTR = CAN_RTR_DATA;
	extID tmp_eID;
	tmp_eID.BitAll = _rxMsg.eID.BitAll;
	tmp_eID.Fields.Route = ROUTE_SLAVE;
	TxHeader.ExtId = tmp_eID.BitAll;
	TxHeader.DLC = 6;
	msgAnalogType msg;
	msg.Body = _rxMsg.eID.Fields.MsgBody;
	data[0] = 'T';
	data[1] = 'S';
	data[2] = 47 + msg.Fields.SensorID / 1000;
	data[3] = 47 + msg.Fields.SensorID / 100 % 10;
	data[4] = 47 + msg.Fields.SensorID / 10 % 10;
	data[5] = 47 + msg.Fields.SensorID % 10;
	return HAL_CAN_AddTxMessage(_HCAN, &TxHeader, data, &TxMailBox);
}

/**
* @brief Функция обработки широковещательных запросов.
* @param struct RXMsg _rxMsg - структура для полученного сообщения.
* @details Функция, выполняющая команды, переданные в широковещательном формате с головного (master) устройства. Типы команд: Запрос статуса, запрос на включение или выключение, рестарт устройств, установка времени.
* @return HAL_StatusTypeDef Статус выполнения команды CAN (успех или ошибка).
*/
HAL_StatusTypeDef PROTOCAN_BroadcastProcessing(struct RXMsg _rxMsg)
{	
	msgBroadcastType msg;
	msg.Body = _rxMsg.eID.Fields.MsgBody;
	switch(msg.Fields.BroadcastType)
	{
		case SENSOR_TYPE_BROADCAST_STATUS:
		{
			//Обработка запроса статуса устройства
			return ProtoCanMsgToBroadcastStatus(_rxMsg);
		}
		case SENSOR_TYPE_BROADCAST_ONOFF:
		{
			//Обработка запроса на вкл/выкл 
			ProtoCanMsgToBroadcastOnOff(_rxMsg);
			break;
		}
		case SENSOR_TYPE_BROADCAST_RESTARTDEVICE:
		{
			ProtoCanMsgToBroadcastRestart(_rxMsg);
			break;
		}
		case SENSOR_TYPE_BROADCAST_RTCSETUP:
		{
			//Обработка запроса на синхронизацию времени
			//С головным устройством
			ProtoCanMsgToBroadcastRtcSetup(_rxMsg);
			break;;
		}
		default:
			//RESERVE SENSOR TYPE.
			return HAL_ERROR;
			break;
	}
	return HAL_OK;
}

/**
* @brief Формирует и отправляет широковещательное CAN-сообщение для ответа на запрос статуса.
* Устанавливает идентификатор, собирает текущие параметры времени и даты, и отправляет сообщение.
* @param _rxMsg Структура полученного по CAN запроса.
* @return HAL_StatusTypeDef Статус операции HAL, например HAL_OK при успехе.
*/
__weak HAL_StatusTypeDef ProtoCanMsgToBroadcastStatus(struct RXMsg _rxMsg)
{
	CAN_TxHeaderTypeDef TxHeader;
	uint32_t TxMailBox = 0;
	uint8_t data[8];	
	TxHeader.IDE = CAN_ID_EXT;
	TxHeader.DLC = 7;
	TxHeader.TransmitGlobalTime = DISABLE;
	TxHeader.RTR = CAN_RTR_DATA;
	extID tmp_eID;
	tmp_eID.BitAll = _rxMsg.eID.BitAll;
	tmp_eID.Fields.Route = ROUTE_SLAVE;
	tmp_eID.Fields.DeviceType = CURRENT_TYPE_DEVICE;
	tmp_eID.Fields.DeviceID = CURRENT_ID_DEVICE;
	TxHeader.ExtId = tmp_eID.BitAll;
	
	RTC_TimeTypeDef sTime = {0};
	HAL_RTC_GetTime(_HRTC, &sTime, RTC_FORMAT_BIN);
	data[0] = sTime.Hours;
	data[1] = sTime.Minutes;
	data[2] = sTime.Seconds;
	
	RTC_DateTypeDef DateToUpdate = {0};
	HAL_RTC_GetDate(_HRTC, &DateToUpdate, RTC_FORMAT_BIN);
	data[3] = DateToUpdate.Year;
	data[4] = DateToUpdate.Month;
	data[5] = DateToUpdate.Date;
	data[6] = DateToUpdate.WeekDay;
	
	return HAL_CAN_AddTxMessage(_HCAN, &TxHeader, data, &TxMailBox);
}

/**
* @brief Виртуальная слабая функция для переключения режима пульсации ON/OFF.
* Меняет состояние флага IsPulse в структуре ControlFlags.
* В данном случае — просто инвертирует его логическое значение.
* @param _rxMsg Структура полученного CAN-сообщения (Пока не используется в функции).
*/
__weak void ProtoCanMsgToBroadcastOnOff(struct RXMsg _rxMsg)
{
	ControlFlags.IsPulse = !ControlFlags.IsPulse;
}

/**
* @brief __weak функция для обработки широковещательного сообщения перезапуска устройства.
* Проверяет длину сообщения, извлекает ID из данных и, если она совпадает с текущим устройством,
* вызывает системный перезапуск.
* @param _rxMsg Структура полученного CAN-сообщения с командой перезапуска.
*/
__weak void ProtoCanMsgToBroadcastRestart(struct RXMsg _rxMsg)
{
	if(_rxMsg.DLC == 0)
	{
		return;
	}
	msgBroadcastType msg;
	msg.Body = _rxMsg.eID.Fields.MsgBody;
	if(msg.Fields.Page == (CURRENT_ID_DEVICE / (_rxMsg.DLC*8)))
	{
		uint64_t page = 0;
		for(int i = 0; i < _rxMsg.DLC; i++)
		{
			page+=(_rxMsg.Data[i]<<(i*8));
		}
		if((page>>CURRENT_ID_DEVICE)&0b1)
		{
			NVIC_SystemReset();
		}
	}
	return;
}

/**
* @brief __weak функция для обработки запроса на синхронизацию RTC через широковещательное сообщение.
* Проверяет длину сообщения и корректность данных (время и дата). Если все проверки пройдены,
* вызывает функцию синхронизации RTC.
* @param _rxMsg Структура полученного CAN-сообщения с данными для установки времени и даты.
*/
__weak void ProtoCanMsgToBroadcastRtcSetup(struct RXMsg _rxMsg)
{
	if(_rxMsg.DLC > 7) {
			//ERROR
		} else {
			int DaysCount_Normal[2][12] = {{31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}, 
																			{31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}};
			if( _rxMsg.Data[0] > 23 ||
					_rxMsg.Data[1] > 59 ||
					_rxMsg.Data[2] > 59 ||
					_rxMsg.Data[3] > 99 ||
					_rxMsg.Data[4] > 12 ||
					_rxMsg.Data[5] > DaysCount_Normal[IsLeapYear(_rxMsg.Data[3])][_rxMsg.Data[4]] ||
					_rxMsg.Data[6] > 6) {
				//ERROR
			} else {
				PROTOCAN_RTC_SYNC(_rxMsg.Data);
			}
		}
}

/**
* @brief Функция обработки дискретных запросов.
* @param struct RXMsg _rxMsg - структура для полученного сообщения.
* @details Функция, формирующая и отправляющая ответ на запросы. Типы запросов: Аварии, Предупреждения, Управляющие сигналы, Флаги, Рестарт устройства, Изменение режима работы устройства, Запрос на устройство.
* @note Запрос на устройство. Головное (master) устройство запрашивает некоторое колличество параметров. В Data - 64 битовых адресса параметров, тип которых задаётся в Sensor ID. Имеется возможность запрашивать непоследовательные параметры.
* @return HAL_StatusTypeDef Статус выполнения команды CAN (успех или ошибка).
*/
HAL_StatusTypeDef PROTOCAN_DiscreticProcessing(struct RXMsg _rxMsg)
{
	msgDiscreteType msg;
	msg.Body = _rxMsg.eID.Fields.MsgBody;
	switch(msg.Fields.Type){
		case SENSOR_TYPE_DISCRETE_ACCIDENT:
		{
			ProtoCanMsgToDiscreteAccident(_rxMsg);
			break;
		}
		case SENSOR_TYPE_DISCRETE_WARNING:
		{
			ProtoCanMsgToDiscreteWarning(_rxMsg);
			break;
		}
		case SENSOR_TYPE_DISCRETE_CONTROL_SIGNALS:
		{
			ProtoCanMsgToDiscreteControlSignals(_rxMsg);
			break;
		}
		case SENSOR_TYPE_DISCRETE_FLAGS:
		{
			ProtoCanMsgToDiscreteFlags(_rxMsg);
			break;
		}
		case SENSOR_TYPE_DISCRETE_RESET:
		{
			ProtoCanMsgToDiscreteReset(_rxMsg);
			break;
		}
		case SENSOR_TYPE_DISCRETE_CHANGE_MODE:
		{
			ProtoCanMsgToDiscreteChangeMode(_rxMsg);
			break;
		}
		case SENSOR_TYPE_DISCRETE_REQUEST_LIST_OF_PARAMETERS:
		{
			ProtoCanMsgToDiscreteRequestListOfParameters(_rxMsg);
			break;
		}
		default:
			//RESERVE SENSOR TYPE.
			return HAL_ERROR;
			break;
	}
	return HAL_OK;
}

/**
* @brief Обработка запроса аварийных ситуаций.
* В текущей реализации — заглушка, ничего не выполняет.
* Можно реализовать отправку или обработку аварийных данных.
* @param _rxMsg Входящее сообщение с данными запроса.
*/
__weak void ProtoCanMsgToDiscreteAccident(struct RXMsg _rxMsg)
{
	return;
}

/**
* @brief Обработка запроса предупреждений.
* В текущей реализации — заглушка.
* Можно реализовать обработку или ответ на предупреждения.
* @param _rxMsg Входящее сообщение.
*/
__weak void ProtoCanMsgToDiscreteWarning(struct RXMsg _rxMsg)
{
	return;
}

/**
* @brief Обработка запроса управляющих сигналов.
* В текущей реализации — заглушка.
* Можно реализовать управление сигналами.
* @param _rxMsg Входящее сообщение.
*/
__weak void ProtoCanMsgToDiscreteControlSignals(struct RXMsg _rxMsg)
{
	return;
}

/**
* @brief Обработка запроса флагов.
* В текущей реализации — заглушка.
* Можно реализовать работу с флагами.
* @param _rxMsg Входящее сообщение.
*/
__weak void ProtoCanMsgToDiscreteFlags(struct RXMsg _rxMsg)
{
	return;
}

/**
* @brief Обработка рестарта системы по CAN-запросу.
* Выполняет программный рестарт системы.
* @param _rxMsg Входящее сообщение.
*/
__weak void ProtoCanMsgToDiscreteReset(struct RXMsg _rxMsg)
{
	NVIC_SystemReset();
}

/**
* @brief Обработка запроса на изменение режима.
* В текущей реализации — заглушка.
* Можно реализовать изменение режима работы.
* @param _rxMsg Входящее сообщение.
*/
__weak void ProtoCanMsgToDiscreteChangeMode(struct RXMsg _rxMsg)
{
	return;
}

/**
* @brief Обработка запроса на получение списка параметров.
* В текущей реализации — заглушка.
* Можно реализовать отправку списка параметров.
* @param _rxMsg Входящее сообщение.
*/
__weak void ProtoCanMsgToDiscreteRequestListOfParameters(struct RXMsg _rxMsg)
{
	return;
}

/**
* @brief Обработка и ответ на запрос общего адресного пространства.
* Формирует и передает сообщение с данными о статусе "GAS-XXXX", где XXXX — значение из сообщения _rxMsg.
* @param _rxMsg Структура входящего сообщения, содержит идентификатор и тело сообщения.
*/
void PROTOCAN_GeneralAddressSpace_Answer(struct RXMsg _rxMsg)
{
	CAN_TxHeaderTypeDef TxHeader;
	uint32_t TxMailBox = 0;
	uint8_t data[8];
	TxHeader.IDE = CAN_ID_EXT;
	TxHeader.TransmitGlobalTime = DISABLE;
	TxHeader.RTR = CAN_RTR_DATA;
	extID tmp_eID;
	tmp_eID.BitAll = _rxMsg.eID.BitAll;
	tmp_eID.Fields.Route = ROUTE_SLAVE;
	TxHeader.ExtId = tmp_eID.BitAll;
	TxHeader.DLC = 8;
	data[0] = 'G';
	data[1] = 'A';
	data[2] = 'S';
	data[3] = '-';
	for(int i = 0; i < 4; i++) {
		unsigned sym = (_rxMsg.eID.Fields.MsgBody>>(12-(i*4)))&0xF;
		if(sym >= 10)
			data[4+i] = sym%10+'A';
		else
			data[4+i] = sym+'0';
	}
	HAL_CAN_AddTxMessage(_HCAN, &TxHeader, data, &TxMailBox);
}

/**
* @brief Обработка запроса на основе типа Modbus.
* В зависимости от типа сообщения, вызывает соответствующую функцию
* для отправки Modbus-запроса по CAN-шине.
* @param _rxMsg Структура входящего сообщения RXMsg.
* @return HAL_StatusTypeDef Статус выполнения команды CAN (успех или ошибка).
*/
HAL_StatusTypeDef PROTOCAN_ModbusProcessing(struct RXMsg _rxMsg)
{
	switch(_rxMsg.eID.Fields.MsgType)
	{
		case DATA_TYPE_MODBUS_COIL:
		{
			return ProtoCanMsgToModbusCoil(_rxMsg);
			break;
		}
		case DATA_TYPE_MODBUS_DISCRETE:
		{
			return ProtoCanMsgToModbusDiscrete(_rxMsg);
			break;
		}
		case DATA_TYPE_MODBUS_HOLDING:
		{	
			return ProtoCanMsgToModbusHolding(_rxMsg);
			break;
		}
		case DATA_TYPE_MODBUS_INPUT:
		{
			return ProtoCanMsgToModbusInput(_rxMsg);
			break;
		}
		default:
			//ERROR
			return HAL_ERROR;
			break;
	}
	return HAL_ERROR;
}

/**
* @brief __weak Функция отправки ответа на запрос Coil Modbus по CAN.
* Формирует CAN ответ на запрос Coil Modbus и отправляет его.
* @param _rxMsg Структура входящего сообщения RXMsg.
* @return HAL_StatusTypeDef Статус выполнения команды CAN (успех или ошибка).
*/
__weak HAL_StatusTypeDef ProtoCanMsgToModbusCoil(struct RXMsg _rxMsg)
{
	CAN_TxHeaderTypeDef TxHeader;
	uint32_t TxMailBox = 0;
	uint8_t data[8];
	TxHeader.IDE = CAN_ID_EXT;
	TxHeader.TransmitGlobalTime = DISABLE;
	TxHeader.RTR = CAN_RTR_DATA;
	extID tmp_eID;
	tmp_eID.BitAll = _rxMsg.eID.BitAll;
	tmp_eID.Fields.Route = ROUTE_SLAVE;
	TxHeader.ExtId = tmp_eID.BitAll;
	TxHeader.DLC = 8;
	msgModbusType msg;
	msg.Body = _rxMsg.eID.Fields.MsgBody;
	data[0] = 'M';
	data[1] = 'C';
	data[2] = ' ';
	data[3] = 'S';
	data[4] = msg.Fields.StrAdr;
	data[5] = ' ';
	data[6] = 'C';
	data[7] = msg.Fields.RegCount;
	return HAL_CAN_AddTxMessage(_HCAN, &TxHeader, data, &TxMailBox);
}

/**
* @brief __weak Функция отправки ответа на запрос Discrete Modbus по CAN.
* Формирует CAN ответ на запрос Discrete Modbus и отправляет его.
* @param _rxMsg Структура входящего сообщения RXMsg.
* @return HAL_StatusTypeDef Статус выполнения команды CAN (успех или ошибка).
*/
__weak HAL_StatusTypeDef ProtoCanMsgToModbusDiscrete(struct RXMsg _rxMsg)
{
	CAN_TxHeaderTypeDef TxHeader;
	uint32_t TxMailBox = 0;
	uint8_t data[8];
	TxHeader.IDE = CAN_ID_EXT;
	TxHeader.TransmitGlobalTime = DISABLE;
	TxHeader.RTR = CAN_RTR_DATA;
	extID tmp_eID;
	tmp_eID.BitAll = _rxMsg.eID.BitAll;
	tmp_eID.Fields.Route = ROUTE_SLAVE;
	TxHeader.ExtId = tmp_eID.BitAll;
	TxHeader.DLC = 8;
	msgModbusType msg;
	msg.Body = _rxMsg.eID.Fields.MsgBody;
	data[0] = 'M';
	data[1] = 'D';
	data[2] = ' ';
	data[3] = 'S';
	data[4] = msg.Fields.StrAdr;
	data[5] = ' ';
	data[6] = 'C';
	data[7] = msg.Fields.RegCount;
	return HAL_CAN_AddTxMessage(_HCAN, &TxHeader, data, &TxMailBox);
}

/**
* @brief __weak Функция отправки ответа на запрос Holding Modbus по CAN.
* Формирует CAN ответ на запрос Holding Modbus и отправляет его.
* @param _rxMsg Структура входящего сообщения RXMsg.
* @return HAL_StatusTypeDef Статус выполнения команды CAN (успех или ошибка).
*/
__weak HAL_StatusTypeDef ProtoCanMsgToModbusHolding(struct RXMsg _rxMsg)
{
	CAN_TxHeaderTypeDef TxHeader;
	uint32_t TxMailBox = 0;
	uint8_t data[8];
	TxHeader.IDE = CAN_ID_EXT;
	TxHeader.TransmitGlobalTime = DISABLE;
	TxHeader.RTR = CAN_RTR_DATA;
	extID tmp_eID;
	tmp_eID.BitAll = _rxMsg.eID.BitAll;
	tmp_eID.Fields.Route = ROUTE_SLAVE;
	TxHeader.ExtId = tmp_eID.BitAll;
	TxHeader.DLC = 8;
	msgModbusType msg;
	msg.Body = _rxMsg.eID.Fields.MsgBody;
	data[0] = 'M';
	data[1] = 'H';
	data[2] = ' ';
	data[3] = 'S';
	data[4] = msg.Fields.StrAdr;
	data[5] = ' ';
	data[6] = 'C';
	data[7] = msg.Fields.RegCount;
	return HAL_CAN_AddTxMessage(_HCAN, &TxHeader, data, &TxMailBox);
}

/**
* @brief __weak Функция отправки ответа на запрос Input Modbus по CAN.
* Формирует CAN ответ на запрос Input Modbus и отправляет его.
* @param _rxMsg Структура входящего сообщения RXMsg.
* @return HAL_StatusTypeDef Статус выполнения команды CAN (успех или ошибка).
*/
__weak HAL_StatusTypeDef ProtoCanMsgToModbusInput(struct RXMsg _rxMsg)
{
	CAN_TxHeaderTypeDef TxHeader;
	uint32_t TxMailBox = 0;
	uint8_t data[8];
	TxHeader.IDE = CAN_ID_EXT;
	TxHeader.TransmitGlobalTime = DISABLE;
	TxHeader.RTR = CAN_RTR_DATA;
	extID tmp_eID;
	tmp_eID.BitAll = _rxMsg.eID.BitAll;
	tmp_eID.Fields.Route = ROUTE_SLAVE;
	TxHeader.ExtId = tmp_eID.BitAll;
	TxHeader.DLC = 8;
	msgModbusType msg;
	msg.Body = _rxMsg.eID.Fields.MsgBody;
	data[0] = 'M';
	data[1] = 'I';
	data[2] = ' ';
	data[3] = 'S';
	data[4] = msg.Fields.StrAdr;
	data[5] = ' ';
	data[6] = 'C';
	data[7] = msg.Fields.RegCount;
	return HAL_CAN_AddTxMessage(_HCAN, &TxHeader, data, &TxMailBox);
}

/**
* @brief __weak Функция отправки сообщения об ошибке.
* Посылает CAN сообщение с кодом ошибки. Используется, когда необходимо оповестить о неуспешной операции.
* @param _rxMsg Структура входящего сообщения RXMsg, данными которого формируется ответ-ошибка.
* @return HAL_StatusTypeDef Статус выполнения команды CAN.
*/
__weak HAL_StatusTypeDef CanRequestError(struct RXMsg _rxMsg)
{
	CAN_TxHeaderTypeDef TxHeader;
	uint32_t TxMailBox = 0;
	uint8_t data[8];
	TxHeader.IDE = CAN_ID_EXT;
	TxHeader.TransmitGlobalTime = DISABLE;
	TxHeader.RTR = CAN_RTR_DATA;
	extID tmp_eID;
	tmp_eID.BitAll = _rxMsg.eID.BitAll;
	tmp_eID.Fields.Route = ROUTE_SLAVE;
	tmp_eID.Fields.MsgType = DATA_TYPE_ERROR;
	msgErrorType msg;
	msg.Fields.ErrorCode = 0xFF;
	msg.Fields.Info = 0;
	tmp_eID.Fields.MsgBody = msg.Body;
	TxHeader.ExtId = tmp_eID.BitAll;
	TxHeader.DLC = 0;
	return HAL_CAN_AddTxMessage(_HCAN, &TxHeader, data, &TxMailBox);
}

/**
* @brief Записывает полученное сообщение CAN в буфер rxMsg.
* Копирует параметры расширенного ID, типы, длину данных и сам данные в указанный элемент буфера.
* @param tmp_eID Расширенный ID сообщения.
* @param tmp_IDE Тип идентификатора (например, CAN_ID_EXT).
* @param tmp_RTR Тип запроса (например, CAN_RTR_DATA).
* @param tmp_DLC Длина данных в сообщении.
* @param tmp_DATA Указатель на массив данных, полученных из CAN.
* @param tmp_LastStep Индекс позиции в буфере, куда записывать сообщение.
*/
void TakeRxMsgToBuffer(extID tmp_eID, uint32_t tmp_IDE, uint32_t tmp_RTR, uint32_t tmp_DLC, uint8_t *tmp_DATA, uint16_t tmp_LastStep)
{
	rxMsg[tmp_LastStep].eID.BitAll = tmp_eID.BitAll;
	rxMsg[tmp_LastStep].info.EXT = tmp_IDE;
	rxMsg[tmp_LastStep].info.RTR = tmp_RTR;
	rxMsg[tmp_LastStep].DLC = tmp_DLC;
	for(int i = 0; i < tmp_DLC; i++)
	{
		rxMsg[tmp_LastStep].Data[i] = tmp_DATA[i];
	}
	LastStep = tmp_LastStep;
}

/**
* @brief Обработка сообщений, ожидающих в FIFO, по прерыванию HAL_CAN_RxFifo0MsgPendingCallback.
* Читает все сообщения из FIFO, проверяет их тип, обновляет статус устройств сети или сохраняет сообщение в буфер.
* @param hcan Указатель на структуру управления CAN-Hardware.
*/
void ProtoCanRxFifo0MsgPendingCallback(CAN_HandleTypeDef *hcan)
{
	//Обработка всех сообщений в FIFO
	CAN_RxHeaderTypeDef RxHeader;
	uint8_t RCAN_Data[8];
  while(HAL_CAN_GetRxMessage(hcan, CAN_RX_FIFO0, &RxHeader, RCAN_Data) == HAL_OK)
  {	
		//Расширенный ID
		if(RxHeader.IDE == CAN_ID_EXT)
		{
			if(!((CAN_RX_BUFFER_SIZE + LastStep - (CurrentStep-1))&CAN_RX_BUFFER_SIZE))
			{
				//Буффер переполнен
				return;
			}
			
			uint16_t tmp_LastStep = (uint16_t)(LastStep + 1) % CAN_RX_BUFFER_SIZE;
			
			extID ExtID_Of_RX_MSG;
			ExtID_Of_RX_MSG.BitAll = RxHeader.ExtId;
			
			//Полученное сообщение - пульс устройств в сети
			if(ExtID_Of_RX_MSG.Fields.MsgType == DATA_TYPE_PULSE)
			{
				Device_on_the_Network[ExtID_Of_RX_MSG.Fields.DeviceType][ExtID_Of_RX_MSG.Fields.DeviceID].Status = ONLINE;
				Device_on_the_Network[ExtID_Of_RX_MSG.Fields.DeviceType][ExtID_Of_RX_MSG.Fields.DeviceID].TimeFromLastPulse = 0;
				return;
			}
			
			TakeRxMsgToBuffer(ExtID_Of_RX_MSG, RxHeader.IDE, RxHeader.RTR, RxHeader.DLC, RCAN_Data, tmp_LastStep);
		}
	}
}

/**
* @brief Функция отправки пульса устройства.
* @details Пульс устройства. Есть возможность отключить пульс по запросу.
*/
void ProtoCanPulseCallback(TIM_HandleTypeDef *htim)
{
	if(ControlFlags.IsPulse)
	{
		static unsigned PulseStage = 0;
		CAN_TxHeaderTypeDef TxHeader;
		uint32_t TxMailBox = 0;
		extID currentID;
		currentID.BitAll = 0;
		currentID.Fields.MsgBody = 0;
		currentID.Fields.DeviceID = CURRENT_ID_DEVICE;
		currentID.Fields.DeviceType = CURRENT_TYPE_DEVICE;
		currentID.Fields.MsgType = DATA_TYPE_PULSE;
		currentID.Fields.Route = ROUTE_SLAVE;
		currentID.Fields.Priority = PRIORITY_STANDARD;
		TxHeader.ExtId = currentID.BitAll;
		uint8_t data[8];
		TxHeader.IDE = CAN_ID_EXT;
		TxHeader.TransmitGlobalTime = DISABLE;
		TxHeader.RTR = CAN_RTR_DATA;
		TxHeader.DLC = 1;
		PulseStage++;
		unsigned int debugID;
		debugID = currentID.BitAll;
		if(PulseStage > 0xFF){
			PulseStage = 0;
		}
		data[0] = PulseStage;
		HAL_CAN_AddTxMessage(_HCAN, &TxHeader, data, &TxMailBox);
	}
}

/**
* @brief Функция установки в RTC полученной даты/время из запроса.
* @param uint8_t *data - Байтовый массив, 7 элементов. [0] - Часы. [1] - Минуты. [2] - Секунды. [3] - Год. [4] - Месяц. [5] - Дата. [6] - День недели.
*/
void PROTOCAN_RTC_SYNC(uint8_t *data)
{
	__HAL_RTC_WRITEPROTECTION_DISABLE(_HRTC);
	RTC_TimeTypeDef sTime = {0};
  RTC_DateTypeDef DateToUpdate = {0};
	
	sTime.Hours = data[0];
  sTime.Minutes = data[1];
  sTime.Seconds = data[2];
	
  if(HAL_RTC_SetTime(_HRTC, &sTime, RTC_FORMAT_BIN) != HAL_OK)
  {
    Error_Handler();
  }

  DateToUpdate.Year = data[3];
  DateToUpdate.Month = data[4];
  DateToUpdate.Date = data[5];
  DateToUpdate.WeekDay = data[6];
  if(HAL_RTC_SetDate(_HRTC, &DateToUpdate, RTC_FORMAT_BIN) != HAL_OK)
  {
    Error_Handler();
  }
	__HAL_RTC_WRITEPROTECTION_ENABLE(_HRTC);
}

/**
* @brief Настройка CAN-Фильтра.
* На основе переданных ID и маски настраивает фильтр CAN для приема только определённых сообщений.
* @param filterBank Номер фильтра (обычно 0..27).
* @param idFilter 32-битный идентификатор, используемый для фильтрации.
* @param idMask 32-битная маска, определяющая биты, проверяемые фильтром.
*/
void PROTOCAN_CONFIG_FILTER(uint8_t filterBank, uint32_t idFilter, uint32_t idMask)
{
    CAN_FilterTypeDef canFilterConfig;
    canFilterConfig.FilterBank = filterBank;
    canFilterConfig.FilterMode = CAN_FILTERMODE_IDMASK;
    canFilterConfig.FilterScale = CAN_FILTERSCALE_32BIT;
    canFilterConfig.FilterFIFOAssignment = CAN_RX_FIFO0;
    canFilterConfig.FilterActivation = ENABLE;
    canFilterConfig.SlaveStartFilterBank = 14;

    // Разбиваем 32-битный ID и маску для фильтрации на High и Low 16 бит
    canFilterConfig.FilterIdHigh = (uint16_t)( ((idFilter) >> 13) );       // верхние 16 бит
    canFilterConfig.FilterIdLow  = (uint16_t)( (((idFilter) << 3) ) | CAN_IDE_32 );  // низкие 16 бит,
    canFilterConfig.FilterMaskIdHigh = (uint16_t)(((idMask) >> 13) );
    canFilterConfig.FilterMaskIdLow  = (uint16_t)( (((idMask) << 3) ) | CAN_IDE_32 );

    if(HAL_CAN_ConfigFilter(_HCAN, &canFilterConfig) != HAL_OK)
    {
        Error_Handler();
    }
}

#define BIT27 (1UL << 27)
#define BITS_20_23_MASK (0xFUL << 20)       // 4 бита (20..23)
#define BITS_24_26_MASK (0x7UL << 24)       // 3 бита (24..26)

uint32_t filter1_id = BIT27 | (CURRENT_TYPE_DEVICE << 24) | (CURRENT_ID_DEVICE << 20);
uint32_t filter1_mask = BIT27 | BITS_24_26_MASK | BITS_20_23_MASK; // все проверяемые биты

#define BITS_16_19_MASK (0xFUL << 16)

uint32_t filter2_id = DATA_TYPE_BROADCAST << 16;
uint32_t filter2_mask = BITS_16_19_MASK;

uint32_t filter3_id = DATA_TYPE_PULSE << 16;
uint32_t filter3_mask = BITS_16_19_MASK;

/**
* @brief Конфигурирует три фильтра CAN для различных типов сообщений.
*/
void PROTOCAN_FILTERS()
{
    // Первый фильтр: проверяем 20-27 биты, где 27-й всегда 1
    PROTOCAN_CONFIG_FILTER(0, filter1_id, filter1_mask);

    // Второй фильтр: проверяем 16-19 биты, равны DATA_TYPE_BROADCASTE
    PROTOCAN_CONFIG_FILTER(1, filter2_id, filter2_mask);

    // Третий фильтр: проверяем 16-19 биты, равны DATA_TYPE_PULSE
    PROTOCAN_CONFIG_FILTER(2, filter3_id, filter3_mask);
}
