/*
 * err_hdlr.c
 *
 *  Created on: 03-Aug-2023
 *      Author: Invcuser52
 */


#include "err_handler.h"
#include "err_recovery.h"
#include "exo_osal_thread.h"
#include "exo_osal_timer.h"
#include "nvm.h"
#include "exo_io_al_sos_timer.h"
#include "exo_tctm_ipc.h"
#include "exo_osal_mem_management.h"
#include "logger.h"
#include "tc_tm_mngr_buffer.h"
#include "obc_tc_tm.h"
/*
 * Error handler info
 *
 * 1. Error Group			= 3 bits  (0-7)
 * 2. Error ID				= 5 bits  (0- 31)
 * 3. Error set status  	= 1 bit
 * 4. Error counter     	= 3 bit   (0-7)
 * 5. Fault status			= 1 bit
 * 6. Error reporter name 	= 6 bit   (0-63)
 * 7. Reporting time		= 3 bytes (3 times)
 *
 * Total >> 3 + 5 + 1 + 3 + 1 + 3unused + 6 + 2unused + 24 + 24 + 24 =  96/8 = 12 bytes
 *
 * For history of 30 errors will be 12*30 = 360 bytes
 */

//#define DISABLE_ERR_HANLDER
//#define DISABLE_NVM_ERR_HDLR


#define ERR_RET_STS 0xFF
#define fault_bit_set(x,y) 		(x = x | (1<<y))
#define fault_bit_clear(x,y) 	(x = x & (~(1<<y)))
#define fault_bit_read(x,y) 	((x & (1<<y)) ? (1) : (0))
#define min(X, Y)  				((X) < (Y) ? (X) : (Y))
#define get_fault_byte_size(x)  ((x/8)+1))
#define SFT_RESET_REQUIRED     1
#define SFT_RESET_NOT_REQUIRED 0

/*Error handler health data sending periodicity - 5 minutes*/
#define ERR_HDLR_HEALTH_DATA_TIMER_IN_SEC (1000*60*5)

/*Error handler queue size */
#define ERR_HDLR_HEALTH_MAX_BUFF_SIZE    54 

/**
 * @brief Global variables for timer to send Error handler health data
 */

static os_timer_handle_ptr  err_hdlr_health_data_timer;

#define NVM_ERR_HISTORY_POS_TOTAL (30-1)

#define PERIODIC_FAULT_FUNC_TIMER_IN_SEC  ( 2 *(60*1000))
#define PERIDIC_BIST_TRIGGER_TIMER_6_MIN   6

typedef uint8_t (*recovery_hdl_fp)(uint8_t rcvry_mode);


typedef struct err_hdl_cfg_lut_def
{
	uint8_t err_code;
	uint8_t err_trial_limit;
	recovery_hdl_fp rcvry_hdl;
	uint8_t sft_reset_en;
}err_hdl_cfg_lut;

uint8_t err_mod_init_complete =  0;

#ifndef DISABLE_ERR_HANLDER
static os_timer_handle_ptr  periodic_fault_time;
#endif



const err_hdl_cfg_lut  err_io_cfg_lut[ERR_IO_MAX] =
{
		{.err_code = ERR_IO_I2C_FAIL, 	.err_code = 2, .rcvry_hdl = NULL, .sft_reset_en = SFT_RESET_REQUIRED},
		{.err_code = ERR_IO_SPI_FAIL, 	.err_code = 3, .rcvry_hdl = NULL, .sft_reset_en = SFT_RESET_REQUIRED},
		{.err_code = ERR_IO_CAN_FAIL, 	.err_code = 3, .rcvry_hdl = NULL, .sft_reset_en = SFT_RESET_REQUIRED},
		{.err_code = ERR_IO_UART_FAIL, 	.err_code = 3, .rcvry_hdl = NULL, .sft_reset_en = SFT_RESET_REQUIRED},
		{.err_code = ERR_IO_QSPI_FAIL, 	.err_code = 3, .rcvry_hdl = NULL, .sft_reset_en = SFT_RESET_REQUIRED},
};


#ifndef STM32F769xx

const err_hdl_cfg_lut  err_ahw_cfg_lut_set_1[ERR_AHW_ONE_MAX] =
{

		{.err_code = ERR_IMU_BMX160_OBC_1, 				    .err_trial_limit = 3, .rcvry_hdl = NULL,         									.sft_reset_en = SFT_RESET_NOT_REQUIRED},
		{.err_code = ERR_IMU_BMX160_OBC_2, 					.err_trial_limit = 3, .rcvry_hdl = NULL,         									.sft_reset_en = SFT_RESET_NOT_REQUIRED},
		{.err_code = ERR_IMU_BMX160_XXX, 					.err_trial_limit = 3, .rcvry_hdl = NULL,         			                        .sft_reset_en = SFT_RESET_NOT_REQUIRED},
		{.err_code = ERR_DIGITAL_THERMOSTAT_DS620_EDGE, 	.err_trial_limit = 3, .rcvry_hdl = ahw_rcvry_hdlr_ds620_edge,           			.sft_reset_en = SFT_RESET_NOT_REQUIRED},
		{.err_code = ERR_DIGITAL_THERMOSTAT_DS620_PS,   	.err_trial_limit = 3, .rcvry_hdl = ahw_rcvry_hdlr_ds620_ps,             			.sft_reset_en = SFT_RESET_NOT_REQUIRED},
		{.err_code = ERR_HSC_ADM1176_EDGE, 					.err_trial_limit = 3, .rcvry_hdl = NULL,         		                          	.sft_reset_en = SFT_RESET_NOT_REQUIRED},
		{.err_code = ERR_HSC_ADM1176_PS_2, 					.err_trial_limit = 3, .rcvry_hdl = NULL,         			                        .sft_reset_en = SFT_RESET_NOT_REQUIRED},
		{.err_code = ERR_HSC_ADM1176_PS_1, 					.err_trial_limit = 3, .rcvry_hdl = NULL,         		                           	.sft_reset_en = SFT_RESET_NOT_REQUIRED},
		{.err_code = ERR_TEMP_SENSOR_MCP9843_OBC, 	    	.err_trial_limit = 3, .rcvry_hdl = ahw_rcvry_hdlr_mcp9843_obc,          			.sft_reset_en = SFT_RESET_NOT_REQUIRED},
		{.err_code = ERR_TEMP_SENSOR_MCP9843_PS, 	    	.err_trial_limit = 3, .rcvry_hdl = ahw_rcvry_hdlr_mcp9843_ps,           			.sft_reset_en = SFT_RESET_NOT_REQUIRED},
		{.err_code = ERR_TEMP_SENSOR_MCP9843_EDGE, 	    	.err_trial_limit = 3, .rcvry_hdl = ahw_rcvry_hdlr_mcp9843_edge,         			.sft_reset_en = SFT_RESET_NOT_REQUIRED},
		{.err_code = ERR_TEMP_SENSOR_MCP9843_GPS, 	    	.err_trial_limit = 3, .rcvry_hdl = ahw_rcvry_hdlr_mcp9843_gps,          			.sft_reset_en = SFT_RESET_NOT_REQUIRED},
		{.err_code = ERR_TEMP_SENSOR_MCP9843_SPE01, 		.err_trial_limit = 3, .rcvry_hdl = ahw_rcvry_hdlr_mcp9843_SPE01,          			.sft_reset_en = SFT_RESET_NOT_REQUIRED},
		{.err_code = ERR_TEMP_SENSOR_MCP9843_SPE02, 		.err_trial_limit = 3, .rcvry_hdl = ahw_rcvry_hdlr_mcp9843_SPE02,          			.sft_reset_en = SFT_RESET_NOT_REQUIRED},
		{.err_code = ERR_TEMP_SENSOR_MCP9843_SPW01, 		.err_trial_limit = 3, .rcvry_hdl = ahw_rcvry_hdlr_mcp9843_SPW01,          			.sft_reset_en = SFT_RESET_NOT_REQUIRED},
		{.err_code = ERR_TEMP_SENSOR_MCP9843_SPW02, 		.err_trial_limit = 3, .rcvry_hdl = ahw_rcvry_hdlr_mcp9843_SPW02,          			.sft_reset_en = SFT_RESET_NOT_REQUIRED},
		{.err_code = ERR_TEMP_SENSOR_MCP9843_SPB01, 		.err_trial_limit = 3, .rcvry_hdl = ahw_rcvry_hdlr_mcp9843_SPB01,          			.sft_reset_en = SFT_RESET_NOT_REQUIRED},
		{.err_code = ERR_PSM_INA230_OBC, 	            	.err_trial_limit = 3, .rcvry_hdl = ahw_rcvry_hdlr_ina230_obc,           			.sft_reset_en = SFT_RESET_NOT_REQUIRED},
		{.err_code = ERR_PSM_INA230_PS, 	            	.err_trial_limit = 3, .rcvry_hdl = ahw_rcvry_hdlr_ina230_ps,            			.sft_reset_en = SFT_RESET_NOT_REQUIRED},
		{.err_code = ERR_GPIO_EXPANDER_PCAL6408A_PS, 	    .err_trial_limit = 3, .rcvry_hdl = NULL,            			                    .sft_reset_en = SFT_RESET_NOT_REQUIRED},
		{.err_code = ERR_GPIO_EXPANDER_MCP23008_EDGE_1, 	.err_trial_limit = 3, .rcvry_hdl = NULL,            								.sft_reset_en = SFT_RESET_NOT_REQUIRED},
		{.err_code = ERR_GPIO_EXPANDER_MCP23008_EDGE_2, 	.err_trial_limit = 3, .rcvry_hdl = NULL,            								.sft_reset_en = SFT_RESET_NOT_REQUIRED},
		{.err_code = ERR_GPIO_EXPANDER_MCP23008_OBC_1, 	    .err_trial_limit = 3, .rcvry_hdl = NULL,            								.sft_reset_en = SFT_RESET_NOT_REQUIRED},
		{.err_code = ERR_GPIO_EXPANDER_MCP23008_OBC_2, 	    .err_trial_limit = 3, .rcvry_hdl = NULL,            								.sft_reset_en = SFT_RESET_NOT_REQUIRED},
		{.err_code = ERR_GPIO_EXPANDER_MCP23008_GPS, 	    .err_trial_limit = 3, .rcvry_hdl = NULL,            								.sft_reset_en = SFT_RESET_NOT_REQUIRED},
		{.err_code = ERR_THRUSTER_MORPHEUS_OBC, 	        .err_trial_limit = 3, .rcvry_hdl = NULL,            								.sft_reset_en = SFT_RESET_NOT_REQUIRED},
		{.err_code = ERR_VOLTAGE_SEQUENCER_UCD9801_OBC, 	.err_trial_limit = 3, .rcvry_hdl = NULL,											.sft_reset_en = SFT_RESET_NOT_REQUIRED},
		{.err_code = ERR_DATA_LOGGER_ADS7828_PS,			.err_trial_limit = 3, .rcvry_hdl = NULL,											.sft_reset_en = SFT_RESET_NOT_REQUIRED},
		{.err_code = ERR_EXT_MEM_MX25L512_QSPI, 	   	 	.err_trial_limit = 2, .rcvry_hdl = NULL,  			                                .sft_reset_en = SFT_RESET_NOT_REQUIRED},
		{.err_code = ERR_EXT_MEM_MT25QL512_QSPI, 	   	 	.err_trial_limit = 2, .rcvry_hdl = ahw_rcvry_hdlr_norflash_qspi_mt25ql,  			.sft_reset_en = SFT_RESET_REQUIRED},
		{.err_code = ERR_EXT_MEM_S25HL512T_QSPI, 	   	 	.err_trial_limit = 2, .rcvry_hdl = NULL,  			                                .sft_reset_en = SFT_RESET_NOT_REQUIRED},
		{.err_code = ERR_EXT_MEM_MT28EW128_FMC, 	    	.err_trial_limit = 2, .rcvry_hdl = ahw_rcvry_hdlr_norflash_fmc_mt28ew,              .sft_reset_en = SFT_RESET_REQUIRED},

};


const err_hdl_cfg_lut  err_ahw_cfg_lut_set_2[ERR_AHW_TWO_MAX] =
{

		{.err_code = ERR_UART_EXPANDER_MAX14830_OBC, 	    .err_trial_limit = 2,       .rcvry_hdl = NULL,        .sft_reset_en = SFT_RESET_REQUIRED},

};

#else


const err_hdl_cfg_lut  err_ahw_cfg_lut_set_1[ERR_AHW_ONE_MAX] =
{

		{.err_code = ERR_IMU_BMX160_OBC_1, 				    .err_trial_limit = 3, .rcvry_hdl = NULL,         									.sft_reset_en = SFT_RESET_NOT_REQUIRED},
		{.err_code = ERR_IMU_BMX160_OBC_2, 					.err_trial_limit = 3, .rcvry_hdl = NULL,         									.sft_reset_en = SFT_RESET_NOT_REQUIRED},
		{.err_code = ERR_IMU_BMX160_XXX, 					.err_trial_limit = 3, .rcvry_hdl = NULL,         			                        .sft_reset_en = SFT_RESET_NOT_REQUIRED},
		{.err_code = ERR_DIGITAL_THERMOSTAT_DS620_EDGE, 	.err_trial_limit = 3, .rcvry_hdl = NULL,           			.sft_reset_en = SFT_RESET_NOT_REQUIRED},
		{.err_code = ERR_DIGITAL_THERMOSTAT_DS620_PS,   	.err_trial_limit = 3, .rcvry_hdl = NULL,             			.sft_reset_en = SFT_RESET_NOT_REQUIRED},
		{.err_code = ERR_HSC_ADM1176_EDGE, 					.err_trial_limit = 3, .rcvry_hdl = NULL,         			.sft_reset_en = SFT_RESET_NOT_REQUIRED},
		{.err_code = ERR_HSC_ADM1176_PS_2, 					.err_trial_limit = 3, .rcvry_hdl = NULL,         			.sft_reset_en = SFT_RESET_NOT_REQUIRED},
		{.err_code = ERR_HSC_ADM1176_PS_1, 					.err_trial_limit = 3, .rcvry_hdl = NULL,         			.sft_reset_en = SFT_RESET_NOT_REQUIRED},
		{.err_code = ERR_TEMP_SENSOR_MCP9843_OBC, 	    	.err_trial_limit = 3, .rcvry_hdl = NULL,          			.sft_reset_en = SFT_RESET_NOT_REQUIRED},
		{.err_code = ERR_TEMP_SENSOR_MCP9843_PS, 	    	.err_trial_limit = 3, .rcvry_hdl = NULL,           			.sft_reset_en = SFT_RESET_NOT_REQUIRED},
		{.err_code = ERR_TEMP_SENSOR_MCP9843_EDGE, 	    	.err_trial_limit = 3, .rcvry_hdl = NULL,         			.sft_reset_en = SFT_RESET_NOT_REQUIRED},
		{.err_code = ERR_TEMP_SENSOR_MCP9843_GPS, 	    	.err_trial_limit = 3, .rcvry_hdl = NULL,          			.sft_reset_en = SFT_RESET_NOT_REQUIRED},
		{.err_code = ERR_TEMP_SENSOR_MCP9843_SPE01, 		.err_trial_limit = 3, .rcvry_hdl = NULL,          			.sft_reset_en = SFT_RESET_NOT_REQUIRED},
		{.err_code = ERR_TEMP_SENSOR_MCP9843_SPE02, 		.err_trial_limit = 3, .rcvry_hdl = NULL,          			.sft_reset_en = SFT_RESET_NOT_REQUIRED},
		{.err_code = ERR_TEMP_SENSOR_MCP9843_SPW01, 		.err_trial_limit = 3, .rcvry_hdl = NULL,          			.sft_reset_en = SFT_RESET_NOT_REQUIRED},
		{.err_code = ERR_TEMP_SENSOR_MCP9843_SPW02, 		.err_trial_limit = 3, .rcvry_hdl = NULL,          			.sft_reset_en = SFT_RESET_NOT_REQUIRED},
		{.err_code = ERR_TEMP_SENSOR_MCP9843_SPB01, 		.err_trial_limit = 3, .rcvry_hdl = NULL,          			.sft_reset_en = SFT_RESET_NOT_REQUIRED},
		{.err_code = ERR_PSM_INA230_OBC, 	            	.err_trial_limit = 3, .rcvry_hdl = NULL,           			.sft_reset_en = SFT_RESET_NOT_REQUIRED},
		{.err_code = ERR_PSM_INA230_PS, 	            	.err_trial_limit = 3, .rcvry_hdl = NULL,            			.sft_reset_en = SFT_RESET_NOT_REQUIRED},
		{.err_code = ERR_GPIO_EXPANDER_PCAL6408A_PS, 	    .err_trial_limit = 3, .rcvry_hdl = NULL,            			                    .sft_reset_en = SFT_RESET_NOT_REQUIRED},
		{.err_code = ERR_GPIO_EXPANDER_MCP23008_EDGE_1, 	.err_trial_limit = 3, .rcvry_hdl = NULL,            	.sft_reset_en = SFT_RESET_NOT_REQUIRED},
		{.err_code = ERR_GPIO_EXPANDER_MCP23008_EDGE_2, 	.err_trial_limit = 3, .rcvry_hdl = NULL,            	.sft_reset_en = SFT_RESET_NOT_REQUIRED},
		{.err_code = ERR_GPIO_EXPANDER_MCP23008_OBC_1, 	    .err_trial_limit = 3, .rcvry_hdl = NULL,            	.sft_reset_en = SFT_RESET_NOT_REQUIRED},
		{.err_code = ERR_GPIO_EXPANDER_MCP23008_OBC_2, 	    .err_trial_limit = 3, .rcvry_hdl = NULL,            	.sft_reset_en = SFT_RESET_NOT_REQUIRED},
		{.err_code = ERR_GPIO_EXPANDER_MCP23008_GPS, 	    .err_trial_limit = 3, .rcvry_hdl = NULL,            		.sft_reset_en = SFT_RESET_NOT_REQUIRED},
		{.err_code = ERR_THRUSTER_MORPHEUS_OBC, 	        .err_trial_limit = 3, .rcvry_hdl = NULL,            .sft_reset_en = SFT_RESET_NOT_REQUIRED},
		{.err_code = ERR_VOLTAGE_SEQUENCER_UCD9801_OBC, 	.err_trial_limit = 3, .rcvry_hdl = NULL,											.sft_reset_en = SFT_RESET_NOT_REQUIRED},
		{.err_code = ERR_DATA_LOGGER_ADS7828_PS,			.err_trial_limit = 3, .rcvry_hdl = NULL,											.sft_reset_en = SFT_RESET_NOT_REQUIRED},
		{.err_code = ERR_EXT_MEM_MX25L512_QSPI, 	   	 	.err_trial_limit = 2, .rcvry_hdl = NULL,  			                                .sft_reset_en = SFT_RESET_NOT_REQUIRED},
		{.err_code = ERR_EXT_MEM_MT25QL512_QSPI, 	   	 	.err_trial_limit = 2, .rcvry_hdl = NULL,  			.sft_reset_en = SFT_RESET_REQUIRED},
		{.err_code = ERR_EXT_MEM_S25HL512T_QSPI, 	   	 	.err_trial_limit = 2, .rcvry_hdl = NULL,  			                                .sft_reset_en = SFT_RESET_NOT_REQUIRED},
		{.err_code = ERR_EXT_MEM_MT28EW128_FMC, 	    	.err_trial_limit = 2, .rcvry_hdl = NULL,              .sft_reset_en = SFT_RESET_REQUIRED},

};


const err_hdl_cfg_lut  err_ahw_cfg_lut_set_2[ERR_AHW_TWO_MAX] =
{

		{.err_code = ERR_UART_EXPANDER_MAX14830_OBC, 	    .err_trial_limit = 2,       .rcvry_hdl = NULL,        .sft_reset_en = SFT_RESET_REQUIRED},

};


#endif

const err_hdl_cfg_lut  err_system_cfg_lut[ERR_SYS_MAX] =
{
		{.err_code = ERR_IRAM_HEAP_FREE_LESSER_THAN_16K, 	.err_trial_limit = 0,      .rcvry_hdl = NULL,       							.sft_reset_en = SFT_RESET_NOT_REQUIRED},
		{.err_code = ERR_IRAM_HEAP_FREE_LESSER_THAN_8K, 	.err_trial_limit = 0,      .rcvry_hdl = err_rcvry_hdlr_always_fail_cblk,        .sft_reset_en = SFT_RESET_REQUIRED},
		{.err_code = ERR_ERAM_HEAP_FREE_LESSER_THAN_16K, 	.err_trial_limit = 0,      .rcvry_hdl = NULL,       							.sft_reset_en = SFT_RESET_NOT_REQUIRED},
		{.err_code = ERR_ERAM_HEAP_FREE_LESSER_THAN_8K, 	.err_trial_limit = 0,      .rcvry_hdl = err_rcvry_hdlr_always_fail_cblk,        .sft_reset_en = SFT_RESET_REQUIRED},
		{.err_code = ERR_CMALLOC_FREE_LESSER_THAN_4K, 		.err_trial_limit = 0,      .rcvry_hdl = NULL,   							    .sft_reset_en = SFT_RESET_NOT_REQUIRED},
		{.err_code = ERR_OBC_FW_UPDATE_FAIL, 				.err_trial_limit = 0,      .rcvry_hdl = NULL,							        .sft_reset_en = SFT_RESET_NOT_REQUIRED},
};


const err_hdl_cfg_lut  err_services_cfg_lut[ERR_SER_MAX] =
{
		{.err_code = ERR_SER_1, 	.err_trial_limit = 3,      .rcvry_hdl = NULL,       .sft_reset_en = SFT_RESET_REQUIRED},
};

const err_hdl_cfg_lut  err_comms_cfg_lut[ERR_COMMS_MAX] =
{
		        {.err_code = ERR_COMMS_PKT_DROP, 				       .err_trial_limit = 3,      .rcvry_hdl = NULL,							        .sft_reset_en = SFT_RESET_NOT_REQUIRED},
				{.err_code = ERR_COMMS_PKT_LEN_MISMATCH, 		       .err_trial_limit = 3,      .rcvry_hdl = NULL,							        .sft_reset_en = SFT_RESET_NOT_REQUIRED},
				{.err_code = ERR_COMMS_SBAND_BUSY, 				       .err_trial_limit = 3,      .rcvry_hdl = NULL,							        .sft_reset_en = SFT_RESET_NOT_REQUIRED},
				{.err_code = ERR_COMMS_SBAND_TCP_READ_FAIL, 		   .err_trial_limit = 2,      .rcvry_hdl = NULL,							        .sft_reset_en = SFT_RESET_NOT_REQUIRED},
				{.err_code = ERR_COMMS_SBAND_TCP_CONNECTION_FAIL, 	   .err_trial_limit = 2,      .rcvry_hdl = NULL,							        .sft_reset_en = SFT_RESET_NOT_REQUIRED},
				{.err_code = ERR_COMMS_SBAND_OFF_VOLT_TEMP_EXCEED, 	   .err_trial_limit = 1,      .rcvry_hdl = NULL,							        .sft_reset_en = SFT_RESET_NOT_REQUIRED},
				{.err_code = ERR_COMMS_NACK_RCV_UHF, 	               .err_trial_limit = 2,      .rcvry_hdl = NULL,							        .sft_reset_en = SFT_RESET_NOT_REQUIRED},
				{.err_code = ERR_COMMS_RESTART_UHF, 	               .err_trial_limit = 3,      .rcvry_hdl = NULL,							        .sft_reset_en = SFT_RESET_NOT_REQUIRED},
				{.err_code = ERR_COMMS_SBAND_RSP_CMD, 	               .err_trial_limit = 2,      .rcvry_hdl = NULL,							        .sft_reset_en = SFT_RESET_NOT_REQUIRED},
				{.err_code = ERR_COMMS_SBAND_BUSY_TCP_WRITE, 	       .err_trial_limit = 3,      .rcvry_hdl = NULL,							        .sft_reset_en = SFT_RESET_NOT_REQUIRED},
				{.err_code = ERR_COMMS_SBAND_READ_UDP_RX_FAIL, 	       .err_trial_limit = 3,      .rcvry_hdl = NULL,							        .sft_reset_en = SFT_RESET_NOT_REQUIRED},
				{.err_code = ERR_COMMS_SBAND_UDP_TX_CONNECTION_FAIL,   .err_trial_limit = 2,      .rcvry_hdl = NULL,							        .sft_reset_en = SFT_RESET_NOT_REQUIRED},
				{.err_code = ERR_COMMS_SBAND_SEND_UDP_TX_FAIL, 	       .err_trial_limit = 3,      .rcvry_hdl = NULL,							        .sft_reset_en = SFT_RESET_NOT_REQUIRED},
				{.err_code = ERR_COMMS_SBAND_READ_UDP_TX_FAIL, 	   	   .err_trial_limit = 3,      .rcvry_hdl = NULL,							        .sft_reset_en = SFT_RESET_NOT_REQUIRED},
};

const err_hdl_cfg_lut  err_adcs_cgf_lut[ERR_ADCS_MAX] =
{
        {.err_code = ERR_IO_INTERFACE_ERR,  .err_trial_limit = 255,  .rcvry_hdl = NULL,  .sft_reset_en = SFT_RESET_NOT_REQUIRED},
		{.err_code = ERR_OPERATION_FAILURE, .err_trial_limit = 255,  .rcvry_hdl = NULL,  .sft_reset_en = SFT_RESET_NOT_REQUIRED},
		{.err_code = ERR_TC_TM_ERROR,       .err_trial_limit = 255,  .rcvry_hdl = NULL,  .sft_reset_en = SFT_RESET_NOT_REQUIRED},
		{.err_code = ERR_INVLD_PWR_STATE,   .err_trial_limit = 255,  .rcvry_hdl = NULL,  .sft_reset_en = SFT_RESET_NOT_REQUIRED},
};
const err_hdl_cfg_lut  err_eps_cfg_lut[ERR_EPS_MAX] =
{
		{.err_code = ERR_EPS_COM_FAIL, 	        .err_trial_limit = 10,   .rcvry_hdl = eps_rcvry_hdlr_com_fail,          .sft_reset_en = SFT_RESET_REQUIRED},
		{.err_code = ERR_EPS_CRC_FAIL, 	        .err_trial_limit = 0,    .rcvry_hdl = NULL,                             .sft_reset_en = SFT_RESET_NOT_REQUIRED},
		{.err_code = ERR_EPS_RESP_ERR, 	        .err_trial_limit = 0,    .rcvry_hdl = NULL,                             .sft_reset_en = SFT_RESET_NOT_REQUIRED},
		{.err_code = ERR_EPS_CTRL_FW_MISMATCH, 	.err_trial_limit = 0,    .rcvry_hdl = NULL,                             .sft_reset_en = SFT_RESET_NOT_REQUIRED},
};

const err_hdl_cfg_lut  *err_cfg_lut[ERR_GROUP_MAX] =
{
		err_io_cfg_lut,
		err_ahw_cfg_lut_set_1,
		err_ahw_cfg_lut_set_2,
		err_system_cfg_lut,
		err_services_cfg_lut,
		err_comms_cfg_lut,
		err_adcs_cgf_lut,
		err_eps_cfg_lut
};

const uint8_t error_grp_err_codes_cnt[ERROR_GROUP_TOTAL+1] =
{
		0,
		(ERROR_CODE_TOTAL_IO+0),
		(ERROR_CODE_TOTAL_AHW_SET_1+ERROR_CODE_TOTAL_IO),
		(ERROR_CODE_TOTAL_AHW_SET_1+ERROR_CODE_TOTAL_IO+ERROR_CODE_TOTAL_AHW_SET_2),
		(ERROR_CODE_TOTAL_AHW_SET_1+ERROR_CODE_TOTAL_IO+ERROR_CODE_TOTAL_AHW_SET_2+ERROR_CODE_TOTAL_SYSTEM),
		(ERROR_CODE_TOTAL_AHW_SET_1+ERROR_CODE_TOTAL_IO+ERROR_CODE_TOTAL_AHW_SET_2+ERROR_CODE_TOTAL_SYSTEM+ERROR_CODE_TOTAL_SERVICES),
		(ERROR_CODE_TOTAL_AHW_SET_1+ERROR_CODE_TOTAL_IO+ERROR_CODE_TOTAL_AHW_SET_2+ERROR_CODE_TOTAL_SYSTEM+ERROR_CODE_TOTAL_SERVICES+ERROR_CODE_TOTAL_COMMS),
        (ERROR_CODE_TOTAL_AHW_SET_1+ERROR_CODE_TOTAL_IO+ERROR_CODE_TOTAL_AHW_SET_2+ERROR_CODE_TOTAL_SYSTEM+ERROR_CODE_TOTAL_SERVICES+ERROR_CODE_TOTAL_COMMS+ERROR_CODE_TOTAL_ADCS),
        (ERROR_CODE_TOTAL_AHW_SET_1+ERROR_CODE_TOTAL_IO+ERROR_CODE_TOTAL_AHW_SET_2+ERROR_CODE_TOTAL_SYSTEM+ERROR_CODE_TOTAL_SERVICES+ERROR_CODE_TOTAL_COMMS+ERROR_CODE_TOTAL_ADCS+ERROR_CODE_TOTAL_EPS)
		
};

//static err_handler_info error_codes_hdl[ERROR_CODES_TOTAL];
const uint8_t error_grp_offset[ERROR_GROUP_TOTAL+1] =
{
		0,
		ERROR_CODE_TOTAL_IO,
		ERROR_CODE_TOTAL_AHW_SET_1,
		ERROR_CODE_TOTAL_AHW_SET_2,
		ERROR_CODE_TOTAL_SYSTEM,
		ERROR_CODE_TOTAL_SERVICES,
		ERROR_CODE_TOTAL_COMMS,
		ERROR_CODE_TOTAL_ADCS,
		ERROR_CODE_TOTAL_EPS
};

static uint8_t  err_cntr[ERROR_CODES_TOTAL];

static uint32_t  err_io;
static uint32_t  err_ahw_set_1;
static uint32_t  err_ahw_set_2;
static uint32_t  err_system;
static uint32_t  err_services;
static uint32_t  err_comms;
static uint32_t  err_adcs;
static uint32_t  err_eps;


#ifdef ERR_HANDL_TEST_HOOK_ENABLE

static uint32_t  err_io_hook;
static uint32_t  err_ahw_set_1_hook;
static uint32_t  err_ahw_set_2_hook;
static uint32_t  err_system_hook;
static uint32_t  err_services_hook;
static uint32_t  err_comms_hook;
static uint32_t  err_adcs_hook;
static uint32_t  err_eps_hook;


uint32_t*  error_fault_set_hook[ERR_GROUP_MAX] =
{
		&err_io_hook,
		&err_ahw_set_1_hook,
		&err_ahw_set_2_hook,
		&err_system_hook,
		&err_services_hook,
		&err_comms_hook,
		&err_adcs_hook,
		&err_eps_hook
};




#endif

static uint8_t   errhdr_module_activated = 0;

uint32_t*  error_fault_set[ERR_GROUP_MAX] =
{
		&err_io,
		&err_ahw_set_1,
		&err_ahw_set_2,
		&err_system,
		&err_services,
		&err_comms,
		&err_adcs,
		&err_eps
};


err_code_status_info   err_code_info [ERROR_CODES_TOTAL];

#ifndef DISABLE_ERR_HANLDER
static os_itc_msg_handle_t 		error_handler_itc_msg_hdl;              ///< ERR handler itc handle
#endif

static uint8_t is_input_valid(err_group g_id, uint8_t err);
static void save_err_history(err_handler_history *e_his);


static uint8_t is_input_valid(err_group g_id, uint8_t err)
{
	uint8_t array_idx=ERR_RET_STS;
	/* check group id is valid */
	if(g_id < ERR_GROUP_MAX)
	{
		/* check error code is valid for respective group*/
		if(err < error_grp_offset[g_id+1])
		{
			array_idx = error_grp_err_codes_cnt[g_id] +  err;
		}
	}
	return array_idx;
}


void run_bist(void)
{
	uint8_t u8cnt;
	uint8_t u8cnt_2;
	uint8_t ret_sts;

    log_id(LOG_ERROR, LOG_ERROR_HANDLER, BIST_START);
	for(u8cnt = 0; u8cnt < ERR_GROUP_MAX; u8cnt++)
	{
		//DEBUG_CPRINT(("###BIST G:%d\n",u8cnt));
		for(u8cnt_2 = 0; u8cnt_2 < error_grp_offset[u8cnt+1]; u8cnt_2++)
		{
			if(err_cfg_lut[u8cnt][u8cnt_2].rcvry_hdl != NULL)
			{
				//DEBUG_CPRINT(("ERR:%d\n",u8cnt_2));
				ret_sts = err_cfg_lut[u8cnt][u8cnt_2].rcvry_hdl(NO_RCVRY_ONLY_SELF_TEST);
				if(ret_sts == 0)
				{
					fault_bit_clear(*error_fault_set[u8cnt], u8cnt_2);
				}
				else
				{
					fault_bit_set(*error_fault_set[u8cnt], u8cnt_2);
				}
			}
		}
		//DEBUG_CPRINT(("BIST_G%d = 0x%x\n",u8cnt, *error_fault_set[u8cnt]));
	}
	log_id(LOG_ERROR, LOG_ERROR_HANDLER, BIST_COMPLETED);
}


static void save_err_history(err_handler_history *e_his)
{

#ifndef DISABLE_NVM_ERR_HDLR
	// TODO save history in NVM
	/*
	 * 1. read current save cntr from nvm
	 * 2. check cntr is lesser than 20 , else reset cnt
	 * 3. save err history data in nvm index based on index cntr
	 * 4. save cntr value in nvm
	 */
	uint8_t nvm_err_history_save_current_indx_cntrl = 0xff;
	nvm_parameter_read(NVM_DATA,NVM_ERR_HISTORY_SAVE_CURRENT_INDX_CNTR,(uint8_t*)&nvm_err_history_save_current_indx_cntrl);
	if(nvm_err_history_save_current_indx_cntrl < NVM_ERROR_HISTORY_TOTAL_COUNT)
	{

	}
	else
	{
		nvm_err_history_save_current_indx_cntrl = 0;
	}

	nvm_parameter_write(NVM_DATA,NVM_ERR_HIS_0 + nvm_err_history_save_current_indx_cntrl,(uint8_t*)e_his);
	nvm_err_history_save_current_indx_cntrl++;
	nvm_parameter_write(NVM_DATA,NVM_ERR_HISTORY_SAVE_CURRENT_INDX_CNTR,&nvm_err_history_save_current_indx_cntrl);
#endif


}


/*
 * read err history from nvm
 *
 */

void read_err_history(uint8_t *read_history_buffer, uint8_t err_his_count)
{
#ifndef DISABLE_NVM_ERR_HDLR

	uint16_t err;
	uint16_t offset=0;
	for(err=NVM_ERR_HIS_0; err < (NVM_ERR_HIS_0+err_his_count); err++)
	{
		nvm_parameter_read(NVM_DATA,err, &read_history_buffer[offset]);
		offset += 10;  // size of error history structure is 10 bytes
		os_delay(20);
	}
#endif

}



void save_fault(err_group g_id)
{

#ifndef DISABLE_NVM_ERR_HDLR


		switch(g_id)
		{
			case ERR_GROUP_IO:
				nvm_parameter_write(NVM_DATA,NVM_ERR_IO,(uint8_t*)&err_io);
				break;

			case ERR_GROUP_SYSTEM:
				nvm_parameter_write(NVM_DATA,NVM_ERR_SYSTEM,(uint8_t*)&err_system);
				break;

			case ERR_GROUP_AHW_SET_1:
				nvm_parameter_write(NVM_DATA,NVM_ERR_AHW_SET_1,(uint8_t*)&err_ahw_set_1);
				break;

			case ERR_GROUP_AHW_SET_2:
				nvm_parameter_write(NVM_DATA,NVM_ERR_AHW_SET_2,(uint8_t*)&err_ahw_set_2);
				break;


			case ERR_GROUP_SERVICES:
				nvm_parameter_write(NVM_DATA,NVM_ERR_SERVICES,(uint8_t*)&err_services);
				break;

			case ERR_GROUP_COMMS:
				nvm_parameter_write(NVM_DATA,NVM_ERR_COMMS,(uint8_t*)&err_comms);
				break;

		    case ERR_GROUP_ADCS:
		        nvm_parameter_write(NVM_DATA,NVM_ERR_ADCS_MODULE,(uint8_t*)&err_adcs);
			    break;
		
    	    case ERR_GROUP_EPS:
			    nvm_parameter_write(NVM_DATA,NVM_ERR_EPS,(uint8_t*)&err_eps);		
			    break;

			case ERR_GROUP_MAX:
				break;
		}



#endif


}

void clear_fault(void)
{
   uint8_t u8cnt;
	for(u8cnt = 0; u8cnt < ERR_GROUP_MAX; u8cnt++)
	{
		*error_fault_set[u8cnt] = 0;
	}
}

void read_fault(void)
{

#ifndef DISABLE_NVM_ERR_HDLR
	nvm_parameter_read(NVM_DATA,NVM_ERR_IO,(uint8_t*)&err_io);
	nvm_parameter_read(NVM_DATA,NVM_ERR_SYSTEM,(uint8_t*)&err_system);
	nvm_parameter_read(NVM_DATA,NVM_ERR_AHW_SET_1,(uint8_t*)&err_ahw_set_1);
	nvm_parameter_read(NVM_DATA,NVM_ERR_AHW_SET_2,(uint8_t*)&err_ahw_set_2);
	nvm_parameter_read(NVM_DATA,NVM_ERR_SERVICES,(uint8_t*)&err_services);
	nvm_parameter_read(NVM_DATA,NVM_ERR_COMMS,(uint8_t*)&err_comms);
	nvm_parameter_read(NVM_DATA,NVM_ERR_ADCS_MODULE,(uint8_t*)&err_adcs);
	nvm_parameter_read(NVM_DATA,NVM_ERR_EPS,(uint8_t*)&err_eps);
#else
	err_io = 0;
	err_system = 0;
	err_ahw_set_1 = 0;
	err_ahw_set_2 = 0;
	err_services = 0;
	err_comms = 0;
	err_adcs = 0;
	err_eps = 0;
#endif


}



void err_detected(err_group g_id, uint8_t err)
{
#ifndef DISABLE_ERR_HANLDER
	os_itc_msg_handle_t err_msg;

	if(errhdr_module_activated != 0)
	{
		err_msg.src_entity	= ERR_HANDLER;
		err_msg.Msg_id 	    = ERR_HANDLER_ITC_MSG_ERROR;
		err_msg.pld_info 	= g_id;
		err_msg.Msg_len 	= err;
		err_msg.pld.pld_ptr = NULL;
		os_itc_msg_send(&err_msg, ERR_HANDLER, os_send_itc_wait);
	}
#endif

}

void err_cleared(err_group g_id, uint8_t err)
{
	os_itc_msg_handle_t err_msg;
	if(errhdr_module_activated != 0)
	{
		err_msg.src_entity	= ERR_HANDLER;
		err_msg.Msg_id 	    = ERR_HANDLER_ITC_MSG_ERRCLR;
		err_msg.pld_info 	= g_id;
		err_msg.Msg_len 	= err;
		err_msg.pld.pld_ptr = NULL;
		os_itc_msg_send(&err_msg, ERR_HANDLER, os_send_itc_wait);
	}
}



void err_clear_errors(err_group g_id, uint8_t err)
{
	uint8_t array_idx;


	/* check group id is valid */
	array_idx = is_input_valid(g_id, err);
	if(array_idx != ERR_RET_STS)
	{
		log_id_with_two_arg(LOG_ERROR, LOG_ERROR_HANDLER, ERROR_CLEARED_GROUP_ID__u8__ERROR_ID__u8_, g_id,err);
		fault_bit_clear(*error_fault_set[g_id], err);
		save_fault(g_id);
		err_cntr[array_idx] = 0;
	}
	else
	{
		DEBUG_CPRINT(("invalid input\n"));
		// do nothing
	}
}

void err_periodic_fault_recovery()
{
#ifndef DISABLE_ERR_HANLDER

	uint8_t array_idx;
	uint8_t ret_sts;
	uint8_t g_id,err;

#ifndef LINUX_TEMP_PORT


	size_t heap_free_bytes;
	size_t eram_heap_free_bytes;


	int32_t free_bytes;
#endif

    log_id(LOG_ERROR, LOG_ERROR_HANDLER, PERIODIC_ERROR_RECOVERY_START);


	static uint8_t periodic_bist_timer_cntr = 0;
	static uint8_t run_bist_at_2nd_min = 0;

	periodic_bist_timer_cntr++;

	/* bist will run for every 6th min
	 * bist will execute once at 2nd minute after POR , it will clear fault bit if faults was set before
	 * software reset*/
	if((periodic_bist_timer_cntr > PERIDIC_BIST_TRIGGER_TIMER_6_MIN) || (run_bist_at_2nd_min == 0))
	{
		periodic_bist_timer_cntr = 0;
		run_bist_at_2nd_min = 1;

		for(g_id=0; g_id<ERROR_GROUP_TOTAL; g_id++)
		{
			for(err=0; err < error_grp_offset[g_id+1] ; err++)
			{
				array_idx = is_input_valid(g_id, err);
				if(array_idx != ERR_RET_STS)
				{
					if(fault_bit_read(*error_fault_set[g_id], err) == 1)
					{
						if(err_cfg_lut[g_id][err].rcvry_hdl != NULL)
						{
							ret_sts = err_cfg_lut[g_id][err].rcvry_hdl(RCVRY_AND_SELF_TEST);
							{
								if(ret_sts == 0)
								{
									err_code_info[array_idx].recovery_success_cntr++;
									err_clear_errors(g_id, err);
									err_cntr[array_idx] = 0;
								}
								else
								{
									err_code_info[array_idx].recovery_failure_cntr++;
								}

							}

						}
					}
				}

			}

		}
		log_id(LOG_ERROR, LOG_ERROR_HANDLER, PERIODIC_ERROR_RECOVERY_COMPLETED);

#ifndef LINUX_TEMP_PORT

		/*
		 * heap availability check and reset system if in case free bytes is lesser than 8kilo bytes
		 */
		heap_free_bytes = xPortGetFreeHeapSize();
		if(heap_free_bytes < (16*1024))
		{
			err_detected(ERR_GROUP_SYSTEM, ERR_IRAM_HEAP_FREE_LESSER_THAN_16K);
			if(heap_free_bytes < (8*1024))
			{
				err_detected(ERR_GROUP_SYSTEM, ERR_IRAM_HEAP_FREE_LESSER_THAN_8K);
			}
		}

		eram_heap_free_bytes = xPortGetFreeHeapSize_eram();
		if(eram_heap_free_bytes < (16*1024))
		{
			err_detected(ERR_GROUP_SYSTEM, ERR_ERAM_HEAP_FREE_LESSER_THAN_16K);
			if(eram_heap_free_bytes < (8*1024))
			{
				err_detected(ERR_GROUP_SYSTEM, ERR_ERAM_HEAP_FREE_LESSER_THAN_8K);
			}
		}


		free_bytes = get_available_cmalloc_free();
		if(free_bytes < (4*1024))
		{
			err_detected(ERR_GROUP_SYSTEM, ERR_CMALLOC_FREE_LESSER_THAN_4K);
		}
        if((heap_free_bytes < (16*1024)) || (eram_heap_free_bytes < (16*1024)) || (free_bytes < (4*1024)))
        {
            gen_alert_tm_mem_exd_thrs();
        }
#endif

	}


#endif

}


uint8_t err_run_recovery_particular_error(uint8_t g_id, uint8_t err)
{
//	uint8_t ret;

	uint8_t ret_sts = 1;
	uint8_t array_idx;
	/* check group id is valid */
	array_idx = is_input_valid(g_id, err);
	if(array_idx != ERR_RET_STS)
	{
		if(err_cfg_lut[g_id][err].rcvry_hdl != NULL)
			{
				ret_sts = err_cfg_lut[g_id][err].rcvry_hdl(RCVRY_AND_SELF_TEST);
				if(ret_sts == 0)
				{
					err_code_info[array_idx].recovery_success_cntr++;
					fault_bit_clear(*error_fault_set[g_id], err);
					err_cntr[array_idx] = 0;
					ret_sts = 0;
				}
				else
				{
					err_code_info[array_idx].recovery_failure_cntr++;
					ret_sts = 1;
				}

				save_fault(g_id);
			}
	}
	else
	{
		ret_sts = 1;
	}


	return ret_sts;
}

void err_process_errors(err_group g_id, uint8_t err)
{
	uint8_t array_idx;
	uint8_t ret_sts = 0;
	uint8_t sft_reset = 0;
	err_handler_history    err_history;

	/* check group id is valid */
	array_idx = is_input_valid(g_id, err);
	if(array_idx != ERR_RET_STS)
	{
		log_id_with_two_arg(LOG_ERROR, LOG_ERROR_HANDLER, ERROR_DETECTED_GROUP_ID__u8__ERROR_ID__u8_, g_id, err);
		if(fault_bit_read(*error_fault_set[g_id], err) != 1)
		{
			err_cntr[array_idx]++;
			err_code_info[array_idx].err_apperance_cntr++;

			if(err_cntr[array_idx] > err_cfg_lut[g_id][err].err_trial_limit)
			{
				fault_bit_set(*error_fault_set[g_id], err);

				if(err_cfg_lut[g_id][err].rcvry_hdl != NULL)
				{
					ret_sts = err_cfg_lut[g_id][err].rcvry_hdl(RCVRY_AND_SELF_TEST);
					if(ret_sts == 0)
					{
						log_id_with_two_arg(LOG_ERROR, LOG_ERROR_HANDLER, RECOVERY_PASS_GROUP_ID__u8__ERROR_ID__u8_, g_id, err);
						err_code_info[array_idx].recovery_success_cntr++;
						fault_bit_clear(*error_fault_set[g_id], err);
						err_cntr[array_idx] = 0;
					}
					else
					{
						log_id_with_two_arg(LOG_ERROR, LOG_ERROR_HANDLER, RECOVERY_FAIL_cm__FAULT_BIT_SET_GROUP_ID__u8__ERROR_ID__u8_, g_id, err);
						err_code_info[array_idx].recovery_failure_cntr++;
						if(err_cfg_lut[g_id][err].sft_reset_en == SFT_RESET_REQUIRED)
						{
							DEBUG_CPRINT(("trigger software reset\n"));
							 sft_reset = 1;
								log_id_with_two_arg(LOG_ERROR, LOG_ERROR_HANDLER, SOFTWARE_RESET_BY_ERROR_HANDLER_GROUP_ID__u8__ERROR_ID__u8_, g_id, err);
						}
					}



				}
				else
				{
					log_id_with_two_arg(LOG_ERROR, LOG_ERROR_HANDLER, FAULT_BIT_SET_GROUP_ID__u8__ERROR_ID__u8_, g_id, err);
				}

				// TODO need to get reported module id.
				err_history.reported_module_id = 1;

				err_history.error_code = err;
				err_history.group_id = g_id;
				err_history.fault_set = ret_sts;
				err_history.reported_time = get_rtc_tm_epoch();

				save_err_history(&err_history);
				save_fault(g_id);

				if(sft_reset != 0)
				{
					os_delay(200);
#ifndef LINUX_TEMP_PORT
					 EXO_ASSERT(SW_TEST_RESET);
#endif
				}


			}
			else
			{
				DEBUG_CPRINT(("Err log GID %d, EID %d\n",g_id, err));
			}
		}
		else
		{
			// fault already present
			err_code_info[array_idx].err_apperance_cntr++;
			DEBUG_CPRINT(("fault already present %d, %d\n",g_id,err));
		}



	}
	else
	{
		DEBUG_CPRINT(("invalid input\n"));
		// do nothing
	}
}



err_fault_status err_get_fault_status(err_group g_id, uint8_t err)
{

#ifndef DISABLE_ERR_HANLDER
	uint8_t array_idx;

	/* check group id is valid */
	array_idx = is_input_valid(g_id, err);
	if(array_idx != ERR_RET_STS)
	{
		if(fault_bit_read(*error_fault_set[g_id], err) != 1)
		{
			return E_MODULE_OK;
		}
		else
		{
			return E_MODULE_FAULT;
		}

	}
	else
	{
		DEBUG_CPRINT(("invalid input\n"));
		return E_MODULE_FAULT;
	}
#else
	return E_MODULE_OK;
#endif
}


#ifdef ERR_HANDL_TEST_HOOK_ENABLE


err_fault_status err_handler_test_hook(err_group g_id, uint8_t err)
{

#ifndef DISABLE_ERR_HANLDER
	uint8_t array_idx;

	/* check group id is valid */
	array_idx = is_input_valid(g_id, err);
	if(array_idx != ERR_RET_STS)
	{
		if(fault_bit_read(*error_fault_set_hook[g_id], err) != 1)
		{
			return E_MODULE_OK;
		}
		else
		{
			return E_MODULE_FAULT;
		}

	}
	else
	{
		DEBUG_CPRINT(("invalid input\n"));
		return E_MODULE_FAULT;
	}
#else
	return E_MODULE_OK;
#endif
}

#endif

/**
 * @brief This API manages err handler process
 *
 */

#ifndef DISABLE_ERR_HANLDER
static void err_handler_thread(void* params)
{
    uint8_t* err_hdlr_enqueue_health_data;
    /*API to start timer for sending err handler health data*/
    err_hdlr_health_data_send_tmr_strt();


    /** TM buffering FW queue request for error handler health data */
    /* one instance = 1Byte (To indicate total error fault status variable+
     *                (4Byte+1Byte)*8 = 40 Byte
     *                (Fault status variable + Bit set info) * Number of fault status
     */
     
    tc_err_hdlr_buff_fw_req_queue(41, ERR_HDLR_HEALTH_MAX_BUFF_SIZE, ERR_HDLR_DATA_QUEUE_ID);
 /*
 *  Wait for trace and store in bank1 memory
 *
 */
	uint8_t ret;
	read_fault();

	/* clear all memory overflow error after POR, it would have set as fault before system reset */
	err_clear_errors(ERR_GROUP_SYSTEM, ERR_IRAM_HEAP_FREE_LESSER_THAN_16K);
	err_clear_errors(ERR_GROUP_SYSTEM, ERR_IRAM_HEAP_FREE_LESSER_THAN_8K);
	err_clear_errors(ERR_GROUP_SYSTEM, ERR_ERAM_HEAP_FREE_LESSER_THAN_16K);
	err_clear_errors(ERR_GROUP_SYSTEM, ERR_ERAM_HEAP_FREE_LESSER_THAN_8K);
	err_clear_errors(ERR_GROUP_SYSTEM, ERR_CMALLOC_FREE_LESSER_THAN_4K);


#if 0
	err_handler_history   err_history_;
	memset(&err_history_, 0, sizeof(err_handler_history));
	nvm_parameter_write(NVM_DATA,NVM_ERR_HISTORY_SAVE_CURRENT_INDX_CNTR,&test);

	for(test = 0; test < 20; test++)
	{
		save_err_history(&err_history_);
	}
#endif

    while(1)
    {
    	/*
    	 * wait for error codes
    	 */
        ret = os_itc_msg_rcv(ERR_HANDLER, &error_handler_itc_msg_hdl, 1000);



        if(ret == ral_success)
        {

            if(error_handler_itc_msg_hdl.Msg_id == ERR_HANDLER_ITC_MSG_ERROR)
             {
             	/*
             	 * Msg_id will hold group id and Msg_len hold err id
             	 */
                 err_process_errors(error_handler_itc_msg_hdl.pld_info, error_handler_itc_msg_hdl.Msg_len);
             }
             else if(error_handler_itc_msg_hdl.Msg_id == ERR_HANDLER_ITC_MSG_ERRCLR)
             {
             	/*
             	 * Msg_id will hold group id and Msg_len hold err id
             	 */
             	err_clear_errors(error_handler_itc_msg_hdl.pld_info, error_handler_itc_msg_hdl.Msg_len);
             }
             else if(error_handler_itc_msg_hdl.Msg_id == ERR_HANDLER_ITC_MSG_PERIODIC_FAULT)
             {
             	/*
             	 * Msg_id will hold group id and Msg_len hold err id
             	 */
            	 err_periodic_fault_recovery();
             }
             else if(error_handler_itc_msg_hdl.Msg_id == ERR_HDLR_TMR_EXPRY_EVNT)
             {
                 

                 uint8_t offset = 0;
                 uint8_t cnt = 0;
                 /*Enqueuing eight unint32 variables
                  * Total size = (4bytes+1Byte(Fault set info))  * 8 = 40Bytes+1Byte(ERR_GROUP_MAX)
                  */
                 err_hdlr_enqueue_health_data = os_malloc((ERR_GROUP_MAX * 5) + 1);
                 err_hdlr_enqueue_health_data[0] = ERR_GROUP_MAX;

                 for (cnt = 0; cnt < ERR_GROUP_MAX; cnt++)
                 {
                     err_hdlr_enqueue_health_data[1+offset] = error_grp_offset[cnt+1];
                     offset=offset+1;
                     os_memcpy(&err_hdlr_enqueue_health_data[1+offset],error_fault_set[cnt],4);
                     offset = offset+4;
                 }
                 
                 /* This IPC recieved on expiry of timer - Every 5minutes*/
            	 tc_err_hdlr_buff_fw_enqueue_req(err_hdlr_enqueue_health_data, ERR_HDLR_DATA_QUEUE_ID);
             }
             else
             {
             	/* do nothing */
             }
        }
        else
        {
        }


    }
}

void periodic_fault_recovery_func_callback()
{

	os_itc_msg_handle_t err_msg;

	err_msg.src_entity	= ERR_HANDLER;
	err_msg.Msg_id 	    = ERR_HANDLER_ITC_MSG_PERIODIC_FAULT;
	err_msg.pld_info 	= 0;
	err_msg.Msg_len 	= 0;
	err_msg.pld.pld_ptr = NULL;
	os_itc_msg_send(&err_msg, ERR_HANDLER, os_send_itc_wait);


}

#endif


void err_init(void)
{
#ifndef DISABLE_ERR_HANLDER

    uint8_t u8cnt;
	for(u8cnt = 0; u8cnt < ERROR_CODES_TOTAL; u8cnt++)
	{
		err_cntr[u8cnt] = 0;
	}

	clear_fault();
	// TODO read fault status from NVM
	read_fault();
	//run_bist();


    os_thread_create(ERR_HANDLER, T_ERR_HANLDER_STACK_SIZE, P_ERR_HANDLER, "ERR_HANDLER", err_handler_thread, NULL, NULL, NULL);

	 os_timer_create(&periodic_fault_time,"periodic_failt_timer",PERIODIC,periodic_fault_recovery_func_callback,NULL);
	 os_delay(100);
	 os_timer_start(periodic_fault_time,(os_tick_time_t)PERIODIC_FAULT_FUNC_TIMER_IN_SEC);

	 errhdr_module_activated = 1;

#endif

}

void err_clear_err_cntr(err_group g_id, uint8_t err)
{
	uint8_t array_idx;;
	array_idx = is_input_valid(g_id, err);
	if(array_idx != ERR_RET_STS)
	{
		err_cntr[array_idx] = 0;
	}
}

/*This API is used to enqueue error handler health data*/

void tc_err_hdlr_buff_fw_enqueue_req(uint8* data, uint8 queue_id)
{
    os_itc_msg_handle_t err_hdlr_req_ipc;
    err_hdlr_req_ipc.pld.pld_data = queue_id;
    err_hdlr_req_ipc.pld.pld_ptr = data;
    err_hdlr_req_ipc.src_entity = ERR_HANDLER;
    err_hdlr_req_ipc.Msg_id = OBC_TC_TM_ENQUEUE_REQ;
    os_itc_msg_send(&err_hdlr_req_ipc, HM_MANAGER_IPC_RECEIVE, os_send_itc_wait);
}

/*This API is used to send queue request to HM framework with 
 * necessary instance size and number of instance*/

void tc_err_hdlr_buff_fw_req_queue(uint32 instance_size, uint32 num_of_instance, uint8 queue_id)
{
    os_itc_msg_handle_t err_hdlr_req_ipc;
    s_tc_tm_buffer_req *buff_req = os_malloc(sizeof(s_tc_tm_buffer_req));
    if(buff_req)
    {
        os_memset(buff_req, 0, sizeof(s_tc_tm_buffer_req));
        buff_req->queue_id = queue_id;
        buff_req->instance_size = instance_size;
        buff_req->num_instance = num_of_instance;
        buff_req->wrap_dsb = 0;
        buff_req->priority = 0;
        buff_req->strg_flag = 0;
        buff_req->thresh = 0;
        err_hdlr_req_ipc.pld.pld_ptr = (void*)buff_req;
        err_hdlr_req_ipc.src_entity = ERR_HANDLER;
        err_hdlr_req_ipc.Msg_id = OBC_TC_TM_BUFFER_REQ;
        os_itc_msg_send(&err_hdlr_req_ipc, HM_MANAGER_IPC_RECEIVE, os_send_itc_wait);
    }
}

/*This API is clled upon the expiry of the timer function 
 * set with determined periodicity to send err handler 
 * health data*/

void err_hdlr_health_buff_timer_exp_cb()
{
    /*Upon every timer expire ipc message will be sent to error handler main */
    os_itc_msg_handle_t err_hdlr_timer_cb_ipc;
    err_hdlr_timer_cb_ipc.src_entity = ERR_HANDLER;
    err_hdlr_timer_cb_ipc.pld.pld_ptr=NULL;
    err_hdlr_timer_cb_ipc.Msg_id=ERR_HDLR_TMR_EXPRY_EVNT;
    os_itc_msg_send(&err_hdlr_timer_cb_ipc,ERR_HANDLER, os_send_itc_wait);
    DEBUG_CPRINT(("[ERR_HDLR][HM_FW] - ERR_HDLR_HEALTH_SEND_TIMER_EXPIRES\n"));

}

/*API to start timer for sending err handler health data*/

void err_hdlr_health_data_send_tmr_strt()
{
    /*Creating timer for enqueing error handler health data*/
    os_timer_create(&err_hdlr_health_data_timer,"send_err_hdlr_health_data",PERIODIC,err_hdlr_health_buff_timer_exp_cb,NULL);
    DEBUG_CPRINT(("[ERR_HDLR][HM_FW] - ERR_HDLR_HEALTH_SEND_TIMER_CREATED\n"));
    /*Timer starts with period of 5 minute*/
    os_timer_start(err_hdlr_health_data_timer,(os_tick_time_t)ERR_HDLR_HEALTH_DATA_TIMER_IN_SEC);
}
