#include "setcan.h"

SetCanDevice_t CurrentDevice;
SetCanDevice_t 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[SETCAN_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)(SETCAN_RX_BUFFER_SIZE + (LastStep - CurrentStep + 1))) % SETCAN_RX_BUFFER_SIZE;
}

void SETCAN_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 описаны в разделе SETCAN_CAN_FILTERS().
*/
SETCAN_INIT_StatusTypeDef SETCAN_INIT(CAN_HandleTypeDef *tmp_hcan, RTC_HandleTypeDef *tmp_hrtc, TIM_HandleTypeDef *tmp_tim)
{
	unsigned initStage = 0;
	if(tmp_hcan)
	{
		_HCAN = tmp_hcan;
		#if (USE_HAL_CAN_REGISTER_CALLBACKS == 1)
		HAL_StatusTypeDef CAN_RC_RESULT = HAL_CAN_RegisterCallback(_HCAN, HAL_CAN_RX_FIFO0_MSG_PENDING_CB_ID, SetCanRxFifo0MsgPendingCallback); 
		if(CAN_RC_RESULT != HAL_OK)
		{
			SETCAN_DEINIT(initStage);
			return SETCAN_INIT_HRTC_ERROR;
		}
		#endif
	}	else {
		SETCAN_DEINIT(initStage);
		return SETCAN_INIT_HCAN_ERROR;
	}
	initStage++;
	if(tmp_hrtc)
	{
		_HRTC = tmp_hrtc;
	}
	else
	{
		SETCAN_DEINIT(initStage);
		return SETCAN_INIT_HRTC_ERROR;
	}
	initStage++;
	if(tmp_tim)
	{
		_HTIM = tmp_tim;
		#if (USE_HAL_TIM_REGISTER_CALLBACKS == 1)
		HAL_StatusTypeDef TIM_RC_RESULT = HAL_TIM_RegisterCallback(_HTIM, HAL_TIM_PERIOD_ELAPSED_CB_ID, SetCanPulseCallback);
		if(TIM_RC_RESULT != HAL_OK)
		{
			SETCAN_DEINIT(initStage);
			return SETCAN_INIT_HRTC_ERROR;
		}
		#endif
	}
	else
	{
		SETCAN_DEINIT(initStage);
		return SETCAN_INIT_TIM_ERROR;
	}
	initStage++;
	SETCAN_FILTERS();
	ControlFlags.IsPulse = 1;
	return SETCAN_INIT_OK;
}

/**
 * @brief Бесконечный цикл обработки входящих CAN-сообщений.
 *
 * Эта функция постоянно проверяет наличие доступных сообщений в буфере CAN и
 * вызывает соответствующие обработчики в зависимости от типа полученного сообщения.
 * Обрабатываемые типы сообщений:
 * - Аналоговые данные
 * - Широковещательные сообщения
 * - Дискретные данные
 * - Общее адресное пространство
 * - Модбас сообщения
 * - Ошибки
 *
 * Обработка сообщений осуществляется внутри бесконечного цикла с использованием
 * функции AvailableCanRxMsg() для определения наличия новых сообщений.
 *
 * В конце каждого обработанного сообщения индекс текущего элемента буфера инкрементируется
 * с учетом размера буфера через операцию по модулю.
 */
void SETCAN_LoopProcessRxMsgs(void)
{
    while(1)
    {
        if(AvailableCanRxMsg())
        {
            if(rxMsg[CurrentStep].eID.Fields.MsgType == SETCAN_MSGTYPE_ANALOG)
            {
                SETCAN_AnalogProcessing(rxMsg[CurrentStep]);
            }
            else if(rxMsg[CurrentStep].eID.Fields.MsgType == SETCAN_MSGTYPE_BROADCAST)
            {
                SETCAN_BroadcastProcessing(rxMsg[CurrentStep]);
            }
            else if(rxMsg[CurrentStep].eID.Fields.MsgType == SETCAN_MSGTYPE_DISCRETE)
            {
                SETCAN_DiscreticProcessing(rxMsg[CurrentStep]);
            }
            else if(rxMsg[CurrentStep].eID.Fields.MsgType == SETCAN_MSGTYPE_GENERAL_ADDRESS_SPACE)
            {
                SetCanMsgToGeneralAddressSpace(rxMsg[CurrentStep]);
            }
            else if(rxMsg[CurrentStep].eID.Fields.MsgType == SETCAN_MSGTYPE_MODBUS_COIL ||
                    rxMsg[CurrentStep].eID.Fields.MsgType == SETCAN_MSGTYPE_MODBUS_DISCRETE ||
                    rxMsg[CurrentStep].eID.Fields.MsgType == SETCAN_MSGTYPE_MODBUS_HOLDING ||
                    rxMsg[CurrentStep].eID.Fields.MsgType == SETCAN_MSGTYPE_MODBUS_INPUT)
            {
                SETCAN_ModbusProcessing(rxMsg[CurrentStep]);
            }
            else if(rxMsg[CurrentStep].eID.Fields.MsgType == SETCAN_MSGTYPE_ERROR)
            {
                SETCAN_RequestError(rxMsg[CurrentStep]);
            }
            // Обновление индекса текущего сообщения с циклическим масштабированием
            CurrentStep = (uint16_t)(CurrentStep + 1) % SETCAN_RX_BUFFER_SIZE;
        }
    }
}

/**
 * @brief Обрабатывает все доступные входящие CAN-сообщения.
 *
 * Эта функция последовательно обрабатывает все сообщения, доступные в буфере,
 * вызывая соответствующие функции обработчики в зависимости от типа сообщения.
 * После обработки каждого сообщения индекс текущего элемента буфера обновляется
 * циклически.
 *
 * @return SETCAN_OK, если все сообщения были обработаны успешно.
 *         В противном случае возвращается код ошибки, полученный при обработке сообщения.
 *
 * Обработка происходит до тех пор, пока есть доступные сообщения, полученные функцией
 * AvailableCanRxMsg().
 */
SETCAN_StatusTypeDef SETCAN_ProcessAllRxMsgs(void)
{
	SETCAN_StatusTypeDef result = SETCAN_TIMEOUT;
	while(AvailableCanRxMsg())
	{
		if(rxMsg[CurrentStep].eID.Fields.MsgType == SETCAN_MSGTYPE_ANALOG)
		{
			result = SETCAN_AnalogProcessing(rxMsg[CurrentStep]);
		}
		else if(rxMsg[CurrentStep].eID.Fields.MsgType == SETCAN_MSGTYPE_BROADCAST)
		{
			result = SETCAN_BroadcastProcessing(rxMsg[CurrentStep]);					
		}
		else if(rxMsg[CurrentStep].eID.Fields.MsgType == SETCAN_MSGTYPE_DISCRETE)
		{
			result = SETCAN_DiscreticProcessing(rxMsg[CurrentStep]);
		}
		else if(rxMsg[CurrentStep].eID.Fields.MsgType == SETCAN_MSGTYPE_GENERAL_ADDRESS_SPACE)
		{
			result = SetCanMsgToGeneralAddressSpace(rxMsg[CurrentStep]);
		}
		else if(rxMsg[CurrentStep].eID.Fields.MsgType == SETCAN_MSGTYPE_MODBUS_COIL ||
			rxMsg[CurrentStep].eID.Fields.MsgType == SETCAN_MSGTYPE_MODBUS_DISCRETE ||
			rxMsg[CurrentStep].eID.Fields.MsgType == SETCAN_MSGTYPE_MODBUS_HOLDING ||
			rxMsg[CurrentStep].eID.Fields.MsgType == SETCAN_MSGTYPE_MODBUS_INPUT)
		{
			result = SETCAN_ModbusProcessing(rxMsg[CurrentStep]);
		}
		else if(rxMsg[CurrentStep].eID.Fields.MsgType == SETCAN_MSGTYPE_ERROR)
		{
			result = SETCAN_RequestError(rxMsg[CurrentStep]);
		}
		CurrentStep = (uint16_t)(CurrentStep + 1) % SETCAN_RX_BUFFER_SIZE;
		if(result != SETCAN_OK)
		{
			return result;
		}
	}
	return result;
}

/**
 * @brief Обрабатывает одно доступное CAN-сообщение.
 *
 * Эта функция проверяет наличие доступных сообщений в буфере и, если таковое есть,
 * выполняет обработку текущего сообщения в зависимости от его типа. После обработки
 * индекс текущего сообщения обновляется циклически.
 *
 * @return Прошедший статус обработки:
 * - SETCAN_OK, если сообщение успешно обработано,
 * - SETCAN_TIMEOUT, если сообщений не было,
 * - или код ошибки, возвращенный функцией обработки.
 */
SETCAN_StatusTypeDef SETCAN_ProcessSingleRxMsg(void)
{
	SETCAN_StatusTypeDef result = SETCAN_TIMEOUT;
	if(AvailableCanRxMsg())
	{
		if(rxMsg[CurrentStep].eID.Fields.MsgType == SETCAN_MSGTYPE_ANALOG)
		{
			result = SETCAN_AnalogProcessing(rxMsg[CurrentStep]);
		}
		else if(rxMsg[CurrentStep].eID.Fields.MsgType == SETCAN_MSGTYPE_BROADCAST)
		{
			result = SETCAN_BroadcastProcessing(rxMsg[CurrentStep]);					
		}
		else if(rxMsg[CurrentStep].eID.Fields.MsgType == SETCAN_MSGTYPE_DISCRETE)
		{
			result = SETCAN_DiscreticProcessing(rxMsg[CurrentStep]);
		}
		else if(rxMsg[CurrentStep].eID.Fields.MsgType == SETCAN_MSGTYPE_GENERAL_ADDRESS_SPACE)
		{
			result = SetCanMsgToGeneralAddressSpace(rxMsg[CurrentStep]);
		}
		else if(rxMsg[CurrentStep].eID.Fields.MsgType == SETCAN_MSGTYPE_MODBUS_COIL ||
			rxMsg[CurrentStep].eID.Fields.MsgType == SETCAN_MSGTYPE_MODBUS_DISCRETE ||
			rxMsg[CurrentStep].eID.Fields.MsgType == SETCAN_MSGTYPE_MODBUS_HOLDING ||
			rxMsg[CurrentStep].eID.Fields.MsgType == SETCAN_MSGTYPE_MODBUS_INPUT)
		{
			result = SETCAN_ModbusProcessing(rxMsg[CurrentStep]);
		}
		else if(rxMsg[CurrentStep].eID.Fields.MsgType == SETCAN_MSGTYPE_ERROR)
		{
			result = SETCAN_RequestError(rxMsg[CurrentStep]);
		}
		CurrentStep = (uint16_t)(CurrentStep + 1) % SETCAN_RX_BUFFER_SIZE;
	}
	return result;
}

/**
* @brief Функция обработки широковещательных запросов.
* @param struct RXMsg _rxMsg - структура для полученного сообщения.
* @details Функция, выполняющая команды, переданные в широковещательном формате с головного (master) устройства. Типы команд: Запрос статуса, запрос на включение или выключение, рестарт устройств, установка времени.
* @return HAL_StatusTypeDef Статус выполнения команды CAN (успех или ошибка).
*/
SETCAN_StatusTypeDef SETCAN_BroadcastProcessing(struct RXMsg _rxMsg)
{	
	msgBodyBroadcastType msg;
	msg.Body = _rxMsg.eID.Fields.MsgBody;
	switch(msg.Fields.Type)
	{
		case SETCAN_BROADCAST_STATUS:
		{
			//Обработка запроса статуса устройства
			return SetCanMsgToBroadcastStatus(_rxMsg);
		}
		case SETCAN_BROADCAST_ONOFF:
		{
			//Обработка запроса на вкл/выкл 
			SetCanMsgToBroadcastOnOff(_rxMsg);
			break;
		}
		case SETCAN_BROADCAST_RESTARTDEVICE:
		{
			SetCanMsgToBroadcastRestart(_rxMsg);
			break;
		}
		case SETCAN_BROADCAST_RTCSETUP:
		{
			//Обработка запроса на синхронизацию времени
			//С головным устройством
			SetCanMsgToBroadcastRtcSetup(_rxMsg);
			break;;
		}
		default:
			//RESERVE SENSOR TYPE.
			return SETCAN_ERROR;
			break;
	}
	return SETCAN_OK;
}

/**
* @brief Формирует и отправляет широковещательное CAN-сообщение для ответа на запрос статуса.
* Устанавливает идентификатор, собирает текущие параметры времени и даты, и отправляет сообщение.
* @param _rxMsg Структура полученного по CAN запроса.
* @return HAL_StatusTypeDef Статус операции HAL, например HAL_OK при успехе.
*/
__weak SETCAN_StatusTypeDef SetCanMsgToBroadcastStatus(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;
	SetCanId_t tmp_eID;
	tmp_eID.BitAll = _rxMsg.eID.BitAll;
	tmp_eID.Fields.Route = SETCAN_ROUTE_FROM_DEVICE;
	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 (SETCAN_StatusTypeDef)HAL_CAN_AddTxMessage(_HCAN, &TxHeader, data, &TxMailBox);
}

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

/**
* @brief __weak функция для обработки широковещательного сообщения перезапуска устройства.
* Проверяет длину сообщения, извлекает ID из данных и, если она совпадает с текущим устройством,
* вызывает системный перезапуск.
* @param _rxMsg Структура полученного CAN-сообщения с командой перезапуска.
*/
__weak SETCAN_StatusTypeDef SetCanMsgToBroadcastRestart(struct RXMsg _rxMsg)
{
	if(_rxMsg.DLC == 0)
	{
		return SETCAN_ERROR;
	}
	msgBodyBroadcastType msg;
	msg.Body = _rxMsg.eID.Fields.MsgBody;
	if(msg.Fields.Body == (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 SETCAN_OK;
}

/**
* @brief __weak функция для обработки запроса на синхронизацию RTC через широковещательное сообщение.
* Проверяет длину сообщения и корректность данных (время и дата). Если все проверки пройдены,
* вызывает функцию синхронизации RTC.
* @param _rxMsg Структура полученного CAN-сообщения с данными для установки времени и даты.
*/
__weak SETCAN_StatusTypeDef SetCanMsgToBroadcastRtcSetup(struct RXMsg _rxMsg)
{
	if(_rxMsg.DLC > 7)
	{
		return SETCAN_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)
		{
			return SETCAN_ERROR;
		}
		else
		{
			SETCAN_RTC_SYNC(_rxMsg.Data);
		}
	}
	return SETCAN_OK;
}

/**
* @brief Функция обработки дискретных запросов.
* @param struct RXMsg _rxMsg - структура для полученного сообщения.
* @details Функция, формирующая и отправляющая ответ на запросы. Типы запросов: Аварии, Предупреждения, Управляющие сигналы, Флаги, Рестарт устройства, Изменение режима работы устройства, Запрос на устройство.
* @note Запрос на устройство. Головное (master) устройство запрашивает некоторое колличество параметров. В Data - 64 битовых адресса параметров, тип которых задаётся в Sensor ID. Имеется возможность запрашивать непоследовательные параметры.
* @return HAL_StatusTypeDef Статус выполнения команды CAN (успех или ошибка).
*/
SETCAN_StatusTypeDef SETCAN_DiscreticProcessing(struct RXMsg _rxMsg)
{
	msgBodyDiscreteType msg;
	msg.Body = _rxMsg.eID.Fields.MsgBody;
	switch(msg.Fields.Type)
	{
		case SETCAN_DISCRETE_ACCIDENT:
		{
			SetCanMsgToDiscreteAccident(_rxMsg);
			break;
		}
		case SETCAN_DISCRETE_WARNING:
		{
			SetCanMsgToDiscreteWarning(_rxMsg);
			break;
		}
		case SETCAN_DISCRETE_CONTROL_SIGNALS:
		{
			SetCanMsgToDiscreteControlSignals(_rxMsg);
			break;
		}
		case SETCAN_DISCRETE_FLAGS:
		{
			SetCanMsgToDiscreteFlags(_rxMsg);
			break;
		}
		case SETCAN_DISCRETE_RESET:
		{
			SetCanMsgToDiscreteReset(_rxMsg);
			break;
		}
		case SETCAN_DISCRETE_CHANGE_MODE:
		{
			SetCanMsgToDiscreteChangeMode(_rxMsg);
			break;
		}
		case SETCAN_DISCRETE_REQUEST_LIST_OF_PARAMETERS:
		{
			SetCanMsgToDiscreteRequestListOfParameters(_rxMsg);
			break;
		}
		default:
			//RESERVE SENSOR TYPE.
			return SETCAN_ERROR;
			break;
	}
	return SETCAN_OK;
}

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

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

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

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

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

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

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

/**
* @brief Функция обработки аналоговых запросов.
* @param struct RXMsg _rxMsg - структура для полученного сообщения.
* @details Функция, сортирующая запросы по типам и вызывающая соответствующие функции. Типы запросов: Универсальный, Уставки, Напряжение, Ток, Температура.
* @return HAL_StatusTypeDef Статус выполнения команды CAN (успех или ошибка).
*/
SETCAN_StatusTypeDef SETCAN_AnalogProcessing(struct RXMsg _rxMsg)
{
	msgBodyAnalogType msg;
	msg.Body = _rxMsg.eID.Fields.MsgBody;
	switch (msg.Fields.Type)
	{
		case SETCAN_ANALOG_UNIVERSAL:
		{
			return SetCanMsgToAnalogUniversal(_rxMsg);
			break;
		}
		case SETCAN_ANALOG_SETTINGS:
		{
			return SetCanMsgToAnalogSettings(_rxMsg);
			break;
		}
		case SETCAN_ANALOG_U:
		{
			return SetCanMsgToAnalogUSens(_rxMsg);
			break;
		}
		case SETCAN_ANALOG_I:
		{
			return SetCanMsgToAnalogISens(_rxMsg);		
			break;
		}
		case SETCAN_ANALOG_T:
		{
			return SetCanMsgToAnalogTSens(_rxMsg);
			break;
		}
		default:
			//RESERVE SENSOR TYPE
			return SETCAN_ERROR;
			break;
	}
	return SETCAN_ERROR;
}

/**
* @brief Функция ответа на запрос данных со всех датчиков.
* Создает сообщение с определенными данными и отправляет его через CAN-шину.
* @param _rxMsg Структура входящего сообщения, используется для формирования ID.
* @return HAL_StatusTypeDef Статус выполнения операции HAL, например HAL_OK.
*/
__weak SETCAN_StatusTypeDef SetCanMsgToAnalogUniversal(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;
	SetCanId_t tmp_eID;
	tmp_eID.BitAll = _rxMsg.eID.BitAll;
	tmp_eID.Fields.Route = SETCAN_ROUTE_FROM_DEVICE;
	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 (SETCAN_StatusTypeDef)HAL_CAN_AddTxMessage(_HCAN, &TxHeader, data, &TxMailBox);
}

/**
* @brief Функция ответа на запрос уставок по CAN.
* Создает сообщение с определенными данными и отправляет его через CAN-шину.
* @param _rxMsg Структура входящего сообщения, используется только для формирования ID.
* @return HAL_StatusTypeDef Статус выполнения операции HAL, например HAL_OK.
*/
__weak SETCAN_StatusTypeDef SetCanMsgToAnalogSettings(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;
	SetCanId_t tmp_eID;
	tmp_eID.BitAll = _rxMsg.eID.BitAll;
	tmp_eID.Fields.Route = SETCAN_ROUTE_FROM_DEVICE;
	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 (SETCAN_StatusTypeDef)HAL_CAN_AddTxMessage(_HCAN, &TxHeader, data, &TxMailBox);
}

/**
* @brief Функция ответа на запрос данных с датчиков напряжения.
* Создает сообщение с указанными параметрами и отправляет его через CAN-шину.
* @param _rxMsg Структура входящего сообщения, используемая для формирования идентификатора и данных.
* @return HAL_StatusTypeDef Статус выполнения операции HAL, например HAL_OK при успешной отправке.
*/
__weak SETCAN_StatusTypeDef SetCanMsgToAnalogUSens(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;
	SetCanId_t tmp_eID;
	tmp_eID.BitAll = _rxMsg.eID.BitAll;
	tmp_eID.Fields.Route = SETCAN_ROUTE_FROM_DEVICE;
	TxHeader.ExtId = tmp_eID.BitAll;
	TxHeader.DLC = 6;
	msgBodyAnalogType msg;
	msg.Body = _rxMsg.eID.Fields.MsgBody;
	data[0] = 'U';
	data[1] = 'S';
	data[2] = '0' + msg.Fields.SensorID / 1000;
	data[3] = '0' + msg.Fields.SensorID / 100 % 10;
	data[4] = '0' + msg.Fields.SensorID / 10 % 10;
	data[5] = '0' + msg.Fields.SensorID % 10;
	return (SETCAN_StatusTypeDef)HAL_CAN_AddTxMessage(_HCAN, &TxHeader, data, &TxMailBox);
}

/**
* @brief Функция ответа на запрос данных с датчиков тока.
* Создает сообщение с определенными параметрами и отправляет его через CAN-шину.
* @param _rxMsg Структура входящего сообщения, используется для формирования идентификатора и данных.
* @return HAL_StatusTypeDef Статус операции HAL, например HAL_OK при успешной отправке.
*/
__weak SETCAN_StatusTypeDef SetCanMsgToAnalogISens(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;
	SetCanId_t tmp_eID;
	tmp_eID.BitAll = _rxMsg.eID.BitAll;
	tmp_eID.Fields.Route = SETCAN_ROUTE_FROM_DEVICE;
	TxHeader.ExtId = tmp_eID.BitAll;
	TxHeader.DLC = 6;
	msgBodyAnalogType msg;
	msg.Body = _rxMsg.eID.Fields.MsgBody;
	data[0] = 'I';
	data[1] = 'S';
	data[2] = '0' + msg.Fields.SensorID / 1000;
	data[3] = '0' + msg.Fields.SensorID / 100 % 10;
	data[4] = '0' + msg.Fields.SensorID / 10 % 10;
	data[5] = '0' + msg.Fields.SensorID % 10;
	return (SETCAN_StatusTypeDef)HAL_CAN_AddTxMessage(_HCAN, &TxHeader, data, &TxMailBox);
}

/**
* @brief Функция ответа на запрос данных с датчиков температуры.
* Создает сообщение с определенным заголовком, формирует данные по ID сенсора и отправляет его через CAN.
* @param _rxMsg Структура полученного сообщения, которая используется для формирования идентификатора.
* @return HAL_StatusTypeDef Статус выполнения функции HAL, например HAL_OK при успехе.
*/
__weak SETCAN_StatusTypeDef SetCanMsgToAnalogTSens(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;
	SetCanId_t tmp_eID;
	tmp_eID.BitAll = _rxMsg.eID.BitAll;
	tmp_eID.Fields.Route = SETCAN_ROUTE_FROM_DEVICE;
	TxHeader.ExtId = tmp_eID.BitAll;
	TxHeader.DLC = 6;
	msgBodyAnalogType msg;
	msg.Body = _rxMsg.eID.Fields.MsgBody;
	data[0] = 'T';
	data[1] = 'S';
	data[2] = '0' + msg.Fields.SensorID / 1000;
	data[3] = '0' + msg.Fields.SensorID / 100 % 10;
	data[4] = '0' + msg.Fields.SensorID / 10 % 10;
	data[5] = '0' + msg.Fields.SensorID % 10;
	return (SETCAN_StatusTypeDef)HAL_CAN_AddTxMessage(_HCAN, &TxHeader, data, &TxMailBox);
}

/**
 * @brief Отправляет сообщение по CAN-шине в соответствии с типом протокола.
 * Эта функция формирует и отправляет сообщение через CAN-шину, основываясь на типе
 * переданного ID и данных. Поддерживаются типы сообщений: широковещательные, дискретные
 * и аналоговые.
 * @param id Идентификатор сообщения, содержащий тип сообщения и другую информацию.
 * @param coreData Структура данных, включающая тип, тело и данные для передачи.
 * @return SETCAN_StatusTypeDef Статус операции.
 * @details
 * В зависимости от типа сообщения (панель broadcast, discrete или analog),
 * формируется соответствующий пакет данных и отправляется на шину CAN.
 * Для сообщений, превышающих длину 8 байт, передача выполняется пакетами.
 * В случае ошибок при добавлении сообщения в очередь передачи, возвращается ошибка.
 */
SETCAN_StatusTypeDef SETCAN_SEND_CORE(const SetCanId_t id, const struct SetCanCoreData coreData)
{
	SetCanId_t localId = id;
  uint8_t msgType = localId.Fields.MsgType;
  CAN_TxHeaderTypeDef TxHeader;
  TxHeader.IDE = CAN_ID_EXT;
  TxHeader.RTR = CAN_RTR_DATA;
  TxHeader.TransmitGlobalTime = DISABLE;
  uint32_t TxMailBox = 0;
	uint8_t canData[8];
	switch(id.Fields.MsgType)
	{
		case SETCAN_MSGTYPE_BROADCAST:
		{
			msgBodyBroadcastType body;
			body.Fields.Type = coreData.Type;
			body.Fields.Body = coreData.Body;
			localId.Fields.MsgBody = body.Body;
			TxHeader.ExtId = localId.BitAll;
			TxHeader.DLC = 0;
			if(HAL_CAN_AddTxMessage(_HCAN, &TxHeader, canData, &TxMailBox) != HAL_OK)
			{
				return SETCAN_ERROR;
			}
		}
		case SETCAN_MSGTYPE_DISCRETE:
		{
			msgBodyDiscreteType body;
			body.Fields.Type = coreData.Type;
			uint8_t regsRemaining = coreData.DataCount;
			uint8_t currentIndex = 0;
			while(regsRemaining > 0)
			{
				uint8_t regsInPacket = (regsRemaining > 8) ? 8 : regsRemaining;		
				body.Fields.Body = coreData.Body + currentIndex;
				localId.Fields.MsgBody = body.Body;
				TxHeader.ExtId = localId.BitAll;
				TxHeader.DLC = regsInPacket;
				for(int i = 0; i < regsInPacket; i++)
				{
					canData[i] = coreData.Data[coreData.DataCount - regsRemaining];
				}
				HAL_StatusTypeDef status = HAL_CAN_AddTxMessage(_HCAN, &TxHeader, canData, &TxMailBox);
				if(status != HAL_OK)
				{
					return (SETCAN_StatusTypeDef)status;
				}
				regsRemaining -= regsInPacket;
				currentIndex += regsInPacket;
			}
			break;
		}
		case SETCAN_MSGTYPE_ANALOG:
		{
			msgBodyAnalogType body;
			body.Fields.Type = coreData.Type;
			uint8_t regsRemaining = coreData.DataCount;
			uint8_t currentIndex = 0;
			while(regsRemaining > 0)
			{
				uint8_t regsInPacket = (regsRemaining > 8) ? 8 : regsRemaining;		
				body.Fields.SensorID = coreData.Body + currentIndex;
				localId.Fields.MsgBody = body.Body;
				TxHeader.ExtId = localId.BitAll;
				TxHeader.DLC = regsInPacket;
				for(int i = 0; i < regsInPacket; i++)
				{
					canData[i] = coreData.Data[coreData.DataCount - regsRemaining];
				}
				HAL_StatusTypeDef status = HAL_CAN_AddTxMessage(_HCAN, &TxHeader, canData, &TxMailBox);
				if(status != HAL_OK)
				{
					return (SETCAN_StatusTypeDef)status;
				}
				regsRemaining -= regsInPacket;
				currentIndex += regsInPacket;
			}
			break;
		}
		default:
		{
			return SETCAN_ERROR;
		}
	}
  return SETCAN_OK;
}

/**
* @brief Обработка и ответ на запрос общего адресного пространства.
* Формирует и передает сообщение с данными о статусе "GAS-XXXX", где XXXX — значение из сообщения _rxMsg.
* @param _rxMsg Структура входящего сообщения, содержит идентификатор и тело сообщения.
*/
__weak SETCAN_StatusTypeDef SetCanMsgToGeneralAddressSpace(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;
	SetCanId_t tmp_eID;
	tmp_eID.BitAll = _rxMsg.eID.BitAll;
	tmp_eID.Fields.Route = SETCAN_ROUTE_FROM_DEVICE;
	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';
		}
	}
	return (SETCAN_StatusTypeDef)HAL_CAN_AddTxMessage(_HCAN, &TxHeader, data, &TxMailBox);
}

/**
 * @brief Отправляет произвольный диапазон регистров в пакетах по, максимум, 4 регистра за раз через CAN.
 * Функция разбивает передаваемый массив регистров на пакеты по 4 или меньше и отправляет их по отдельности.
 * Для каждого пакета формируется уникальный MsgBody, равный адресу первого регистра в пакете.
 * @param priority Приоритет сообщения CAN. SETCAN_Priority_TypeDef
 * @param regStartAdr Адрес первого регистра из всего массива.
 * @param data Указатель на массив регистров для отправки.
 * @param regCount Общее количество регистров для отправки.
 * @return Возвращает статус отправки: HAL_OK при успехе, или код ошибки HAL_StatusTypeDef при сбое.
 */
SETCAN_StatusTypeDef SETCAN_SEND_GENERAL_ADDRESS_SPACE(SetCanId_t id, uint16_t regStartAdr, uint16_t *data, uint8_t regCount)
{
	CAN_TxHeaderTypeDef TxHeader;
	uint32_t TxMailBox = 0;
	uint8_t canData[8];
	
	TxHeader.IDE = CAN_ID_EXT;
	TxHeader.TransmitGlobalTime = DISABLE;
	TxHeader.RTR = CAN_RTR_DATA;
	
	SetCanId_t tmp_eID;
	tmp_eID.BitAll = id.BitAll;
	
	uint8_t regsRemaining = regCount;
  uint8_t currentIndex = 0;
	
	while(regsRemaining > 0)
	{
		uint8_t regsInPacket = (regsRemaining > 4) ? 4 : regsRemaining;
		
		tmp_eID.Fields.MsgBody = regStartAdr + currentIndex;
		TxHeader.ExtId = tmp_eID.BitAll;
		TxHeader.DLC = regsInPacket*2;
		
		for(int i = 0; i < regsInPacket; i++)
		{
			canData[(i * 2)] = LowByteOfWord(data[currentIndex + i]);
			canData[(i * 2) + 1] = HighByteOfWord(data[currentIndex + i]);
		}
		
		HAL_StatusTypeDef status = HAL_CAN_AddTxMessage(_HCAN, &TxHeader, canData, &TxMailBox);
		
		if(status != HAL_OK)
		{
			return (SETCAN_StatusTypeDef)status;
		}
		
		regsRemaining -= regsInPacket;
    currentIndex += regsInPacket;
	}
	return (SETCAN_StatusTypeDef)HAL_OK;
}

/**
* @brief Обработка запроса на основе типа Modbus.
* В зависимости от типа сообщения, вызывает соответствующую функцию
* для отправки Modbus-запроса по CAN-шине.
* @param _rxMsg Структура входящего сообщения RXMsg.
* @return HAL_StatusTypeDef Статус выполнения команды CAN (успех или ошибка).
*/
SETCAN_StatusTypeDef SETCAN_ModbusProcessing(struct RXMsg _rxMsg)
{
	switch(_rxMsg.eID.Fields.MsgType)
	{
		case SETCAN_MSGTYPE_MODBUS_COIL:
		{
			return SetCanMsgToModbusCoil(_rxMsg);
			break;
		}
		case SETCAN_MSGTYPE_MODBUS_DISCRETE:
		{
			return SetCanMsgToModbusDiscrete(_rxMsg);
			break;
		}
		case SETCAN_MSGTYPE_MODBUS_HOLDING:
		{	
			return SetCanMsgToModbusHolding(_rxMsg);
			break;
		}
		case SETCAN_MSGTYPE_MODBUS_INPUT:
		{
			return SetCanMsgToModbusInput(_rxMsg);
			break;
		}
		default:
			//ERROR
			return SETCAN_ERROR;
			break;
	}
	return SETCAN_ERROR;
}

/**
* @brief __weak Функция отправки ответа на запрос Coil Modbus по CAN.
* Формирует CAN ответ на запрос Coil Modbus и отправляет его.
* @param _rxMsg Структура входящего сообщения RXMsg.
* @return HAL_StatusTypeDef Статус выполнения команды CAN (успех или ошибка).
*/
__weak SETCAN_StatusTypeDef SetCanMsgToModbusCoil(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;
	SetCanId_t tmp_eID;
	tmp_eID.BitAll = _rxMsg.eID.BitAll;
	tmp_eID.Fields.Route = SETCAN_ROUTE_FROM_DEVICE;
	TxHeader.ExtId = tmp_eID.BitAll;
	TxHeader.DLC = 8;
	msgBodyModbusType 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 (SETCAN_StatusTypeDef)HAL_CAN_AddTxMessage(_HCAN, &TxHeader, data, &TxMailBox);
}

/**
* @brief __weak Функция отправки ответа на запрос Discrete Modbus по CAN.
* Формирует CAN ответ на запрос Discrete Modbus и отправляет его.
* @param _rxMsg Структура входящего сообщения RXMsg.
* @return HAL_StatusTypeDef Статус выполнения команды CAN (успех или ошибка).
*/
__weak SETCAN_StatusTypeDef SetCanMsgToModbusDiscrete(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;
	SetCanId_t tmp_eID;
	tmp_eID.BitAll = _rxMsg.eID.BitAll;
	tmp_eID.Fields.Route = SETCAN_ROUTE_FROM_DEVICE;
	TxHeader.ExtId = tmp_eID.BitAll;
	TxHeader.DLC = 8;
	msgBodyModbusType 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 (SETCAN_StatusTypeDef)HAL_CAN_AddTxMessage(_HCAN, &TxHeader, data, &TxMailBox);
}

/**
* @brief __weak Функция отправки ответа на запрос Holding Modbus по CAN.
* Формирует CAN ответ на запрос Holding Modbus и отправляет его.
* @param _rxMsg Структура входящего сообщения RXMsg.
* @return HAL_StatusTypeDef Статус выполнения команды CAN (успех или ошибка).
*/
__weak SETCAN_StatusTypeDef SetCanMsgToModbusHolding(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;
	SetCanId_t tmp_eID;
	tmp_eID.BitAll = _rxMsg.eID.BitAll;
	tmp_eID.Fields.Route = SETCAN_ROUTE_FROM_DEVICE;
	TxHeader.ExtId = tmp_eID.BitAll;
	TxHeader.DLC = 8;
	msgBodyModbusType 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 (SETCAN_StatusTypeDef)HAL_CAN_AddTxMessage(_HCAN, &TxHeader, data, &TxMailBox);
}

/**
* @brief __weak Функция отправки ответа на запрос Input Modbus по CAN.
* Формирует CAN ответ на запрос Input Modbus и отправляет его.
* @param _rxMsg Структура входящего сообщения RXMsg.
* @return HAL_StatusTypeDef Статус выполнения команды CAN (успех или ошибка).
*/
__weak SETCAN_StatusTypeDef SetCanMsgToModbusInput(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;
	SetCanId_t tmp_eID;
	tmp_eID.BitAll = _rxMsg.eID.BitAll;
	tmp_eID.Fields.Route = SETCAN_ROUTE_FROM_DEVICE;
	TxHeader.ExtId = tmp_eID.BitAll;
	TxHeader.DLC = 8;
	msgBodyModbusType 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 (SETCAN_StatusTypeDef)HAL_CAN_AddTxMessage(_HCAN, &TxHeader, data, &TxMailBox);
}

/**
 * @brief Отправляет данные Modbus через CAN-шину в соответствии с типом сообщения.
 * Эта функция формирует и отправляет Modbus-данные по CAN-шине, поддерживая
 * различные типы сообщений: Coil, Discrete, Holding Register и Input Register.
 * Для сообщений, превышающих 4 регистров, данные отправляются пакетами.
 * @param id Идентификатор сообщения, содержащий тип сообщения и другую информацию.
 * @param modbusData Структура с данными Modbus: адрес, количество регистров и сами данные.
 * @return SETCAN_StatusTypeDef Статус операции.
 * @details
 * В зависимости от типа Modbus (coil/discrete или holding/input), формируется
 * соответствующий CAN-пакет. Для coil/discrete отправляется один пакет, для holding/input —
 * несколько, по 4 регистра в пакете. В случае ошибок при добавлении сообщения API HAL возвращается ошибка.
 */
SETCAN_StatusTypeDef SETCAN_SEND_MODBUS(const SetCanId_t id, const struct SetCanModbusData modbusData)
{
	SetCanId_t localId = id;
  uint8_t msgType = localId.Fields.MsgType;
	// Структура для отправки
  CAN_TxHeaderTypeDef TxHeader;
  uint32_t TxMailBox = 0;
	uint8_t canData[8];
  if(msgType == SETCAN_MSGTYPE_MODBUS_COIL || msgType == SETCAN_MSGTYPE_MODBUS_DISCRETE)
	{
    // Формируем body
    msgBodyModbusType body;
    body.Fields.StrAdr = modbusData.StrAdr;
    body.Fields.RegCount = modbusData.RegCount;
    // Устанавливаем коллизию ExtId с body.Body
    localId.Fields.MsgBody = (body.Body & 0xFFFF);
    // Обновляем ExtId
    TxHeader.ExtId = localId.BitAll;
    // Остальные настройки
    TxHeader.IDE = CAN_ID_EXT;
    TxHeader.RTR = CAN_RTR_DATA;
    TxHeader.TransmitGlobalTime = DISABLE;
		TxHeader.DLC = modbusData.RegCount % 8 + 1;
    // Формируем CAN данные
		canData[0] = LowByteOfWord(modbusData.Data[0]);
    canData[1] = HighByteOfWord(modbusData.Data[0]);
    if (HAL_CAN_AddTxMessage(_HCAN, &TxHeader, canData, &TxMailBox) != HAL_OK)
		{
			return SETCAN_ERROR;
    }
  }
	else if(msgType == SETCAN_MSGTYPE_MODBUS_HOLDING || msgType == SETCAN_MSGTYPE_MODBUS_INPUT)
	{
    uint16_t* dataPtr = modbusData.Data;
    unsigned totalRegs = modbusData.RegCount;
    unsigned regsProcessed = 0;
    unsigned startAddress = modbusData.StrAdr;
    while (regsProcessed < totalRegs)
		{
			uint8_t regsInPacket = (totalRegs - regsProcessed) > 4 ? 4 : (totalRegs - regsProcessed);
      // Формируем body
      msgBodyModbusType body;
      body.Fields.StrAdr = startAddress;
      body.Fields.RegCount = regsInPacket;
      // Обновляем ExtId
      localId.Fields.MsgBody = (body.Body & 0xFFFF);
      TxHeader.ExtId = localId.BitAll;
      TxHeader.IDE = CAN_ID_EXT;
      TxHeader.RTR = CAN_RTR_DATA;
      TxHeader.TransmitGlobalTime = DISABLE;
			TxHeader.DLC = regsInPacket * 2;
      // Добавляем регистры
      for (int i = 0; i < regsInPacket; i++)
			{
				canData[i * 2] = LowByteOfWord(dataPtr[regsProcessed + i]);
				canData[i * 2 + 1] = HighByteOfWord(dataPtr[regsProcessed + i]);
      }
      // Отправляем сообщение
      if(HAL_CAN_AddTxMessage(_HCAN, &TxHeader, canData, &TxMailBox) != HAL_OK)
			{
				return SETCAN_ERROR;
      }
      regsProcessed += regsInPacket;
      startAddress += regsInPacket;
		}
  }
	else
	{
		return SETCAN_ERROR; // Неверный тип сообщения
  }
  return SETCAN_OK;
}

/**
* @brief __weak Функция отправки сообщения об ошибке.
* Посылает CAN сообщение с кодом ошибки. Используется, когда необходимо оповестить о неуспешной операции.
* @param _rxMsg Структура входящего сообщения RXMsg, данными которого формируется ответ-ошибка.
* @return HAL_StatusTypeDef Статус выполнения команды CAN.
*/
__weak SETCAN_StatusTypeDef SETCAN_RequestError(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;
	SetCanId_t tmp_eID;
	tmp_eID.BitAll = _rxMsg.eID.BitAll;
	tmp_eID.Fields.Route = SETCAN_ROUTE_FROM_DEVICE;
	tmp_eID.Fields.MsgType = SETCAN_MSGTYPE_ERROR;
	msgBodyErrorType msg;
	msg.Fields.ErrorCode = 0xFF;
	msg.Fields.Info = 0;
	tmp_eID.Fields.MsgBody = msg.Body;
	TxHeader.ExtId = tmp_eID.BitAll;
	TxHeader.DLC = 0;
	return (SETCAN_StatusTypeDef)HAL_CAN_AddTxMessage(_HCAN, &TxHeader, data, &TxMailBox);
}

/**
 * @brief Отправляет сообщение об ошибке по CAN-шине.
 * Эта функция формирует и отправляет сообщение об ошибке с кодом и информацией,
 * объединёнными в поле MsgBody идентификатора сообщения, и передаёт его через CAN.
 * @param id Идентификатор сообщения, содержащий базовую информацию.
 * @param errorData Структура с данными ошибки: код и дополнительная информация.
 * @return SETCAN_StatusTypeDef Статус операции.
 * @details
 * В качестве данных передаётся 2 байта: старшие биты — информация об ошибке, младшие — код ошибки.
 * Размер данных в CANFrames устанавливается нулевым, так как все данные инкапсулированы в MsgBody.
 */
SETCAN_StatusTypeDef SETCAN_SEND_ERROR(const SetCanId_t id, const struct SetCanErrorData errorData)
{
	SetCanId_t localId = id;
  uint8_t msgType = localId.Fields.MsgType;
	// Структура для отправки
  CAN_TxHeaderTypeDef TxHeader;
  uint32_t TxMailBox = 0;
	uint8_t canData[8];
	localId.Fields.MsgBody = ((errorData.Info << 8) | errorData.Code);
	// Обновляем ExtId
  TxHeader.ExtId = localId.BitAll;
  // Остальные настройки
  TxHeader.IDE = CAN_ID_EXT;
  TxHeader.RTR = CAN_RTR_DATA;
  TxHeader.TransmitGlobalTime = DISABLE;
	TxHeader.DLC = 0;
	if(HAL_CAN_AddTxMessage(_HCAN, &TxHeader, canData, &TxMailBox) != HAL_OK)
	{
		return SETCAN_ERROR;
  }
  return SETCAN_OK;
}

/**
* @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(SetCanId_t 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 SetCanRxFifo0MsgPendingCallback(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(!((SETCAN_RX_BUFFER_SIZE + LastStep - (CurrentStep - 1)) & SETCAN_RX_BUFFER_SIZE))
			{
				//Буффер переполнен
				return;
			}
			uint16_t tmp_LastStep = (uint16_t)(LastStep + 1) % SETCAN_RX_BUFFER_SIZE;
			SetCanId_t ExtID_Of_RX_MSG;
			ExtID_Of_RX_MSG.BitAll = RxHeader.ExtId;
			//Полученное сообщение - пульс устройств в сети
			if(ExtID_Of_RX_MSG.Fields.MsgType == SETCAN_MSGTYPE_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 SetCanPulseCallback(TIM_HandleTypeDef *htim)
{
	if(ControlFlags.IsPulse)
	{
		static unsigned PulseStage = 0;
		CAN_TxHeaderTypeDef TxHeader;
		uint32_t TxMailBox = 0;
		SetCanId_t currentID;
		currentID.BitAll = 0;
		currentID.Fields.Priority = SETCAN_PRIORITY_STANDARD;
		currentID.Fields.Route = SETCAN_ROUTE_FROM_DEVICE;
		currentID.Fields.DeviceType = CURRENT_TYPE_DEVICE;
		currentID.Fields.DeviceID = CURRENT_ID_DEVICE;
		currentID.Fields.MsgType = SETCAN_MSGTYPE_PULSE;
		currentID.Fields.MsgBody = 0;
		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 SETCAN_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 SETCAN_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;
	#define CAN_IDE_32 0b00000100            // Для 32-х битного масштаба
  // Разбиваем 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();
  }
}

/**
* @brief Конфигурирует три фильтра CAN для различных типов сообщений.
*/
void SETCAN_FILTERS()
{
	#define BIT27 (1UL << 27)
	#define BITS_20_23_MASK (0xFUL << 20)       // 4 бита (20..23)
	#define BITS_24_26_MASK (0x7UL << 24)       // 3 бита (24..26)
	
	#if ISPM == 0
	uint32_t filter1_id = 0 | (CURRENT_TYPE_DEVICE << 24) | (CURRENT_ID_DEVICE << 20);
	uint32_t filter1_mask = BIT27 | BITS_24_26_MASK | BITS_20_23_MASK; // все проверяемые биты
	#else
	uint32_t filter1_id = BIT27;
	uint32_t filter1_mask = BIT27; // все проверяемые биты
	#endif
	#define BITS_16_19_MASK (0xFUL << 16)

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

	uint32_t filter3_id = SETCAN_MSGTYPE_PULSE << 16;
	uint32_t filter3_mask = BITS_16_19_MASK;
	
  // Первый фильтр: проверяем 20-27 биты, где 27-й всегда 1
  SETCAN_CONFIG_FILTER(0, filter1_id, filter1_mask);

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

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

/**
 * @brief Отправляет CAN-сообщение в зависимости от типа сообщения.
 * Эта функция анализирует тип сообщения в идентификаторе и вызывает 
 * соответствующую функцию отправки. В текущей реализации поддерживается только
 * тип SETCAN_MSGTYPE_GENERAL_ADDRESS_SPACE.
 * @param id   Идентификатор CAN-сообщения, содержащий информацию о типе.
 * @param data Структура с данными, которые необходимо отправить. Зависит от типа сообщения.
 * @return Статус выполнения операции.
 */
SETCAN_StatusTypeDef SETCAN_SEND(SetCanId_t id, SetCanData_t data)
{
	switch(id.Fields.MsgType) 
	{
		case SETCAN_MSGTYPE_BROADCAST:
		case SETCAN_MSGTYPE_DISCRETE:
		case SETCAN_MSGTYPE_ANALOG:
		{
			return SETCAN_SEND_CORE(id, data.CoreData);
			break;
		}
		case SETCAN_MSGTYPE_GENERAL_ADDRESS_SPACE:
		{
			/* Если есть регистры для отправки, указатель data должен быть валиден. */      
			if((data.GeneralAddressSpaceData.RegCount > 0U) && data.GeneralAddressSpaceData.Data == 0)
			{
				return SETCAN_ERROR;
      }
			return SETCAN_SEND_GENERAL_ADDRESS_SPACE(id, data.GeneralAddressSpaceData.RegStartAdr,
																										 data.GeneralAddressSpaceData.Data,
																										 data.GeneralAddressSpaceData.RegCount);
    }
		case SETCAN_MSGTYPE_MODBUS_COIL:
		case SETCAN_MSGTYPE_MODBUS_DISCRETE:
		case SETCAN_MSGTYPE_MODBUS_HOLDING:
		case SETCAN_MSGTYPE_MODBUS_INPUT:
		{
			return SETCAN_SEND_MODBUS(id, data.ModbusData);
			break;
		}
		case SETCAN_MSGTYPE_ERROR:
		{
			return SETCAN_SEND_ERROR(id, data.ErrorData);
			break;
		}
    default:
			return SETCAN_ERROR;
  }
	return SETCAN_ERROR;
}