#ifndef _protocan
#define _protocan
#include "main.h"
#include "can.h"

#define _DEMO

#define ONLINE 1;
#define OFFLINE 0;

#define ID_MAIN_DEVICE 0x000
#define CURRENT_TYPE_DEVICE 0b001
#define CURRENT_ID_DEVICE 0b0010

#define PROTOCAN_RX_BUFFER_SIZE 128

#define SensorToModbusRegister(SensorType, SensorID) SensorType << 11 | SensorID
#define HighByteOfWord(WORD) (WORD>>8)&0xFF
#define LowByteOfWord(WORD) WORD&0xFF

/**
 * @brief Статус инициализации протокола CAN.
 *
 * Это перечисление описывает возможные статусы после попытки инициализации.
 */
typedef enum
{
  PROTOCAN_INIT_OK = 0x00U,            /**< Инициализация прошла успешно. */
  PROTOCAN_INIT_HCAN_ERROR = 0x01U,    /**< Ошибка при инициализации модуля CAN. */
  PROTOCAN_INIT_HRTC_ERROR = 0x02U,    /**< Ошибка при инициализации модуля RTC. */
  PROTOCAN_INIT_TIM_ERROR = 0x04U      /**< Ошибка при инициализации таймера. */
} PROTOCAN_INIT_StatusTypeDef;

/**
 * @brief Статус выполнения операции протокола CAN.
 *
 * Это перечисление описывает возможные результаты выполнения функций протокола.
 */
typedef enum
{
  PROTOCAN_OK       = 0x00U, /**< Операция прошла успешно. */
  PROTOCAN_ERROR    = 0x01U, /**< Обнаружена ошибка во время выполнения. */
  PROTOCAN_BUSY     = 0x02U, /**< Канал занят, выполнение невозможно в данный момент. */
  PROTOCAN_TIMEOUT  = 0x03U  /**< Истек таймаут ожидания. */
} PROTOCAN_StatusTypeDef;

/**
 * @brief Уровень приоритета CAN-сообщения.
 *
 * Это перечисление определяет важность и приоритет обработки сообщения.
 */
typedef enum {
	PROTOCAN_PRIORITY_CRITICAL = 0,   /**< Критический приоритет, рекомендуется обработка без задержек. */
	PROTOCAN_PRIORITY_STANDARD = 1    /**< Стандартный приоритет, менее важный. */
} ProtoCanPriorityType;

/**
 * @brief Маршрут CAN-сообщения.
 *
 * Это перечисление задает направление маршрута отправки сообщения.
 */
typedef enum {
	PROTOCAN_ROUTE_FROM_PM = 0,      /**< Сообщение отправляется от управляющего модуля (PM). */
	PROTOCAN_ROUTE_FROM_DEVICE = 1   /**< Сообщение отправляется от устройства (Device). */
} ProtoCanRouteType;

/**
 * @brief Типы сообщений CAN.
 *
 * Перечисление определяет различные типы сообщений, используемые в протоколе.
 */
typedef enum {
	PROTOCAN_MSGTYPE_BROADCAST = 0b0000,             /**< Широковещательное сообщение. */
	PROTOCAN_MSGTYPE_DISCRETE = 0b0001,              /**< Дискретное цифровое сообщение. */
	PROTOCAN_MSGTYPE_ANALOG = 0b0010,                /**< Аналоговое сообщение. */
	PROTOCAN_MSGTYPE_GENERAL_ADDRESS_SPACE = 0b0011, /**< Сообщение общего адресного пространства. */
	PROTOCAN_MSGTYPE_MODBUS_COIL = 0b0100,           /**< Сообщение Modbus - Coil. */
	PROTOCAN_MSGTYPE_MODBUS_DISCRETE = 0b0101,       /**< Сообщение Modbus - Discrete. */
	PROTOCAN_MSGTYPE_MODBUS_HOLDING = 0b0110,        /**< Сообщение Modbus - Holding. */
	PROTOCAN_MSGTYPE_MODBUS_INPUT = 0b0111,          /**< Сообщение Modbus - Input. */
	PROTOCAN_MSGTYPE_ERROR = 0b1000,                 /**< Сообщение об ошибке. */
	PROTOCAN_MSGTYPE_PULSE = 0b1111,                 /**< Импульсное сообщение. */
}ProtoCanMsgType;

/**
 * @brief Типы широковещательных сообщений CAN.
 *
 * Перечисление задает различные типы широковещательных команд, используемых в протоколе.
 */
typedef enum {
	PROTOCAN_BROADCAST_STATUS = 0b000000000000,       /**< Статус устройства. */
	PROTOCAN_BROADCAST_ONOFF = 0b000000000001,        /**< Включение/выключение устройства. */
	PROTOCAN_BROADCAST_RESTARTDEVICE = 0b000000000010, /**< Перезагрузка устройства. */
	PROTOCAN_BROADCAST_RTCSETUP = 0b000000000011,      /**< Настройка RTC (часы реального времени). */
	PROTOCAN_BROADCAST_END = 0b111111111111          /**< Конец диапазона широковещательных сообщений. */
}ProtoCanBroadcastType;

/**
 * @brief Структура и представление широковещательного сообщения CAN.
 *
 * Объединение позволяет обращаться к данным сообщения либо как к отдельным полям, либо как к целому 16-битному значению.
 */
typedef union{
    struct msgBody_Broadcast{
        unsigned Body:4; /**< Поле для дополнительных данных или кода задачи (4 бита). */
        ProtoCanBroadcastType Type:12; /**< Тип широковещательного сообщения (12 бит). */
    } Fields; /**< Структура с разбивкой по полям. */
    unsigned Body; /**< Общее 16-битное представление сообщения. */
} msgBodyBroadcastType;

/**
 * @brief Типы дискретных сообщений CAN.
 *
 * Перечисление задает различные виды дискретных команд или событий.
 */
typedef enum {
	PROTOCAN_DISCRETE_ACCIDENT = 0b0000,                       /**< Аварийное состояние. */
	PROTOCAN_DISCRETE_WARNING = 0b0001,                        /**< Предупредительный сигнал. */
	PROTOCAN_DISCRETE_CONTROL_SIGNALS = 0b0010,                /**< Управляющие сигналы. */
	PROTOCAN_DISCRETE_FLAGS = 0b0011,                          /**< Флаги состояния. */
	PROTOCAN_DISCRETE_RESET = 0b0100,                          /**< Сброс устройства. */
	PROTOCAN_DISCRETE_CHANGE_MODE = 0b0101,                    /**< Смена режима работы. */
	PROTOCAN_DISCRETE_REQUEST_LIST_OF_PARAMETERS = 0b0110,   /**< Запрос списка параметров. */
	PROTOCAN_DISCRETE_END = 0b1111                              /**< Конец диапазона дискретных сообщений. */
}ProtoCanDiscreteType;

/**
 * @brief Структура и представление дискретного сообщения CAN.
 *
 * Объединение позволяет обращаться к данным сообщения как к отдельным полям или как к целому 16-битному значению.
 */
typedef union{
    struct msgBody_Discrete{
        unsigned Body:12; /**< Поле данных или кода (12 бит). */
        ProtoCanDiscreteType Type:4; /**< Тип дискретного сообщения (4 бита). */
    } Fields; /**< Структура с разбивкой по полям. */
    unsigned Body; /**< Общее 16-битное представление сообщения. */
} msgBodyDiscreteType;

/**
 * @brief Типы аналоговых сообщений CAN.
 *
 * Перечисление определяет различные типы аналоговых данных или команд.
 */
typedef enum {
	PROTOCAN_ANALOG_UNIVERSAL = 0b0000,    /**< Универсальный тип. */
	PROTOCAN_ANALOG_SETTINGS = 0b0001,     /**< Настройки. */
	PROTOCAN_ANALOG_U = 0b0010,            /**< Напряжение (U). */
	PROTOCAN_ANALOG_I = 0b0011,            /**< Ток (I). */
	PROTOCAN_ANALOG_T = 0b0100,            /**< Температура (T). */
	PROTOCAN_ANALOG_END = 0b1111           /**< Конец диапазона типов. */
}ProtoCanAnalogType;

/**
 * @brief Структура и представление аналогового сообщения CAN.
 *
 * Объединение позволяет обращаться к данным сообщения как к отдельным полям или как к целому 16-битному значению.
 */
typedef union{
    struct msgBody_Analog{
        unsigned SensorID:12; /**< Идентификатор датчика (12 бит). */
        ProtoCanAnalogType Type:4; /**< Тип аналоговых данных (4 бита). */
    } Fields; /**< Структура с разбивкой по полям. */
    unsigned Body; /**< Общее 16-битное представление сообщения. */
}msgBodyAnalogType;

/**
 * @brief Структура и представление сообщения Modbus CAN.
 *
 * Объединение позволяет обращаться к данным как к отдельным полям или как к целому 16-битному значению.
 */
typedef union{
    struct msgBody_Modbus{
        unsigned RegCount:4; /**< Количество регистров (4 бита). */
        unsigned StrAdr:12;  /**< Адрес строки или регистра (12 бит). */
    } Fields; /**< Поля структуры сообщения Modbus. */
    unsigned Body; /**< Общее 16-битное представление сообщения. */
} msgBodyModbusType;

/**
 * @brief Структура и представление сообщения об ошибке CAN.
 *
 * Объединение позволяет обращаться к данным как к отдельным полям или как к целому 16-битному значению.
 */
typedef union{
    struct msgBody_Error{
        unsigned ErrorCode:8; /**< Код ошибки (8 бит). */
        unsigned Info:8;      /**< Дополнительная информация (8 бит). */
    } Fields; /**< Поля ошибки. */
    unsigned Body; /**< Общее 16-битное представление сообщения об ошибке. */
} msgBodyErrorType;

struct controlflags{
	unsigned IsPulse:1;
	unsigned IsRtrMode:1;
};

/**
 * @brief Поля идентификатора протокола CAN.
 *
 * Объединение позволяет обращаться к различным полям идентификатора, а также как к 29-битному целому числу.
 */
typedef union{
	struct protoCanIdFields{
		unsigned MsgBody:16;                 /**< Поле сообщения (16 бит). */
		ProtoCanMsgType MsgType:4;           /**< Тип сообщения (4 бита). */
		unsigned DeviceID:4;                 /**< Идентификатор устройства (4 бита). */
		unsigned DeviceType:3;               /**< Тип устройства (3 бита). */
		ProtoCanRouteType Route:1;           /**< Маршрут (1 бит). */
		ProtoCanPriorityType Priority:1;     /**< Приоритет (1 бит). */
	} Fields; /**< Структура с разбивкой по полям. */
  unsigned int BitAll:29; /**< Общее представление 29-битного идентификатора. */
} ProtoCanId_t;

/**
 * @brief Структура для общего адресного пространства CAN.
 *
 * Используется для хранения данных о регистре и его содержимом.
 */
typedef struct{
	struct ProtoCanCoreData{
		uint16_t Type;
		uint16_t Body;
		uint8_t *Data;
		unsigned DataCount;
	} CoreData;
	struct ProtoCanGeneralAddressSpaceData{
		uint16_t RegStartAdr; /**< Начальный адрес регистров. */
		uint16_t *Data; /**< Указатель на массив данных. */
		unsigned RegCount; /**< Количество регистров. */
	} GeneralAddressSpaceData;
	struct ProtoCanModbusData{
		uint16_t StrAdr;
		uint16_t *Data;
		unsigned RegCount:4;
	} ModbusData;
	struct ProtoCanErrorData{
		uint16_t Info;
		uint16_t Code;
	} ErrorData;
} ProtoCanData_t;

/**
 * @brief Структура данных устройства CAN.
 *
 * Включает статус, идентификатор, таймер последнего импульса и шаг последнего импульса.
 */
typedef struct{
	unsigned Status:1; /**< Статус устройства (флаг). */
	ProtoCanId_t Id; /**< Идентификатор CAN. */
	unsigned TimeFromLastPulse; /**< Время с последнего импульса (мс). */
	unsigned LastPulseStep; /**< Шаг последнего импульса. */
} ProtoCanDevice_t;

/**
 * @brief Структура для хранения сообщения RXCAN.
 */
struct RXMsg {
    /**
     * @brief Структура, содержащая флаги информации.
     */
    struct INFO {
        unsigned EXT : 1; /**< Бит расширенного идентификатора (EXT). */
        unsigned RTR : 1; /**< Бит запроса на передачу (RTR). */
    } info; /**< Информационные флаги сообщения. */
    ProtoCanId_t eID; /**< Идентификатор CAN-сообщения. */
    uint16_t DLC; /**< Длина данных (Data Length Code). */
    uint8_t Data[8]; /**< Массив данных (до 8 байт). */
};

uint16_t AvailableCanRxMsg(void);

PROTOCAN_INIT_StatusTypeDef PROTOCAN_INIT(CAN_HandleTypeDef *tmp_hcan,
																					RTC_HandleTypeDef *tmp_hrtc,
																					TIM_HandleTypeDef *tmp_tim);
void PROTOCAN_DEINIT(uint8_t stage);
void PROTOCAN_FILTERS(void);
void PROTOCAN_LoopProcessRxMsgs(void);
PROTOCAN_StatusTypeDef PROTOCAN_ProcessAllRxMsgs(void);
PROTOCAN_StatusTypeDef PROTOCAN_ProcessSingleRxMsg(void);
PROTOCAN_StatusTypeDef PROTOCAN_SEND(ProtoCanId_t id, ProtoCanData_t data);

void ProtoCanPulseCallback(TIM_HandleTypeDef *htim);
void ProtoCanRxFifo0MsgPendingCallback(CAN_HandleTypeDef *hcan);

PROTOCAN_StatusTypeDef PROTOCAN_BroadcastProcessing(struct RXMsg _rxMsg);
PROTOCAN_StatusTypeDef ProtoCanMsgToBroadcastStatus(struct RXMsg _rxMsg);
PROTOCAN_StatusTypeDef ProtoCanMsgToBroadcastOnOff(struct RXMsg _rxMsg);
PROTOCAN_StatusTypeDef ProtoCanMsgToBroadcastRestart(struct RXMsg _rxMsg);
PROTOCAN_StatusTypeDef ProtoCanMsgToBroadcastRtcSetup(struct RXMsg _rxMsg);

PROTOCAN_StatusTypeDef PROTOCAN_DiscreticProcessing(struct RXMsg _rxMsg);
PROTOCAN_StatusTypeDef ProtoCanMsgToDiscreteAccident(struct RXMsg _rxMsg);
PROTOCAN_StatusTypeDef ProtoCanMsgToDiscreteWarning(struct RXMsg _rxMsg);
PROTOCAN_StatusTypeDef ProtoCanMsgToDiscreteControlSignals(struct RXMsg _rxMsg);
PROTOCAN_StatusTypeDef ProtoCanMsgToDiscreteFlags(struct RXMsg _rxMsg);
PROTOCAN_StatusTypeDef ProtoCanMsgToDiscreteReset(struct RXMsg _rxMsg);
PROTOCAN_StatusTypeDef ProtoCanMsgToDiscreteChangeMode(struct RXMsg _rxMsg);
PROTOCAN_StatusTypeDef ProtoCanMsgToDiscreteRequestListOfParameters(struct RXMsg _rxMsg);

PROTOCAN_StatusTypeDef PROTOCAN_AnalogProcessing(struct RXMsg _rxMsg);
PROTOCAN_StatusTypeDef ProtoCanMsgToAnalogUniversal(struct RXMsg _rxMsg);
PROTOCAN_StatusTypeDef ProtoCanMsgToAnalogSettings(struct RXMsg _rxMsg);
PROTOCAN_StatusTypeDef ProtoCanMsgToAnalogUSens(struct RXMsg _rxMsg);
PROTOCAN_StatusTypeDef ProtoCanMsgToAnalogISens(struct RXMsg _rxMsg);
PROTOCAN_StatusTypeDef ProtoCanMsgToAnalogTSens(struct RXMsg _rxMsg);

PROTOCAN_StatusTypeDef ProtoCanMsgToGeneralAddressSpace(struct RXMsg _rxMsg);

PROTOCAN_StatusTypeDef PROTOCAN_RequestError(struct RXMsg _rxMsg);

PROTOCAN_StatusTypeDef PROTOCAN_ModbusProcessing(struct RXMsg _rxMsg);
PROTOCAN_StatusTypeDef ProtoCanMsgToModbusCoil(struct RXMsg _rxMsg);
PROTOCAN_StatusTypeDef ProtoCanMsgToModbusDiscrete(struct RXMsg _rxMsg);
PROTOCAN_StatusTypeDef ProtoCanMsgToModbusHolding(struct RXMsg _rxMsg);
PROTOCAN_StatusTypeDef ProtoCanMsgToModbusInput(struct RXMsg _rxMsg);

void PROTOCAN_RTC_SYNC(uint8_t *data);

#endif
