/**
 * @file adcs_csw.c
 *
 * @brief ADCS controller Software
 *
 * This file contains thread and other functions to handle
 * ADCS states and functionalities
 *
 * @copyright Copyright 2023 Antaris, Inc.
 */
/*********************************************************************/
#include "adcs_csw.h"
#include "adcs_va.h"
#include "adcs_ftm.h"
#include "exo_common.h"
#include "sm_api.h"
#include "tc_tm_mngr.h"
#include <stdio.h>
#include "adcs_comm_interface.h"
#include "logger.h"
#include "adcs_vapi_common.h"
#include "exo_tctm_ipc.h"
#include "nvm.h"


/**
 * @brief Global variables for thread and IPC operations
 */
os_itc_msg_handle_t adcs_csw_trans_msg;
os_itc_msg_handle_t adcs_hdlr_trans_msg;
os_itc_msg_handle_t adcs_hdlr_recv_msg;
e_adcs_csw_sts adcs_csw_init = ADCS_IPC_ERR;
uint8 PLACE_IN_EXT_RAM_MEM adcs_va_buf[4 * 1024] = {0};
extern uint8 tc_storage[TC_DEFERRED_STORAGE_LENGTH]PLACE_IN_EXT_RAM_MEM;

/**
 * @brief Global variables for Last communicated Radio Id
 */
uint8 adcs_radio_id = COMMS_S_BAND;
#ifndef ADCS_HW_SIM
// ADCS Interface error counter
extern uint8 adcs_interface_err;
#endif

// ADCS Temperature error counter
uint8 adcs_temp_err_cnt = 0;

// Health Metrics timer periodicity 20s
#define ADCS_PERIODIC_TIMER_SEC       (20 * 1000)
// Health Metrics timer handle
static os_timer_handle_ptr  adcs_priodic_tmr;

// Periodic Temperature error check period
#define ADCS_TEMP_ERR_PERIOD        ((5 * 60 * 1000)/ADCS_PERIODIC_TIMER_SEC)

/**
 * @brief This function handles the reset event based on the response of
 *         reset request
 * @param[in] ipc_recv_msg : Pointer to os_itc_msg_handle_t
 *
 * @return next state to be transitioned
 * @retval ADCS_ERROR_STATE -> Success
 *         Other states -> Error
 */
static uint8 adcs_handle_reset_event(os_itc_msg_handle_t *ipc_recv_msg);

/**
 * @brief This function handles the boot event based on the response of
 *         boot request
 * @param[in] ipc_recv_msg : Pointer to os_itc_msg_handle_t
 *
 * @return next state to be transitioned
 * @retval ADCS_ERROR_STATE -> Success
 *         Other states -> Error
 */
static uint8 adcs_handle_boot_event(os_itc_msg_handle_t *ipc_recv_msg);

/**
 * @brief This function handles the sensor event based on the response of
 *         sensor init request
 * @param[in] ipc_recv_msg : Pointer to os_itc_msg_handle_t
 *
 * @return next state to be transitioned
 * @retval ADCS_ERROR_STATE -> Success
 *         Other states -> Error
 */
static uint8 adcs_handle_sensor_event(os_itc_msg_handle_t *ipc_recv_msg);

/**
 * @brief This function handles the actuator event based on the response of
 *         actuator init request
 * @param[in] ipc_recv_msg : Pointer to os_itc_msg_handle_t
 *
 * @return next state to be transitioned
 * @retval ADCS_ERROR_STATE -> Success
 *         Other states -> Error
 */
static uint8 adcs_handle_actuator_event(os_itc_msg_handle_t *ipc_recv_msg);

/**
 * @brief This function handles the detumble event based on the response of
 *         detumble request
 * @param[in] ipc_recv_msg : Pointer to os_itc_msg_handle_t
 *
 * @return next state to be transitioned
 * @retval ADCS_ERROR_STATE -> Success
 *         Other states -> Error
 */
static uint8 adcs_handle_detumble_event(os_itc_msg_handle_t *ipc_recv_msg);

/**
 * @brief This function handles the coarse event based on the response of
 *         coarse request
 * @param[in] ipc_recv_msg : Pointer to os_itc_msg_handle_t
 *
 * @return next state to be transitioned
 * @retval ADCS_ERROR_STATE -> Success
 *         Other states -> Error
 */
static uint8 adcs_handle_coarse_event(os_itc_msg_handle_t *ipc_recv_msg);

/**
 * @brief This function handles the fine event based on the response of
 *         fine request
 * @param[in] ipc_recv_msg : Pointer to os_itc_msg_handle_t
 *
 * @return next state to be transitioned
 * @retval ADCS_ERROR_STATE -> Success
 *         Other states -> Error
 */
static uint8 adcs_handle_fine_event(os_itc_msg_handle_t *ipc_recv_msg);

/**
 * @brief This function handles the error event
 * @param[in] ipc_recv_msg : Pointer to os_itc_msg_handle_t
 *
 * @return next state to be transitioned
 * @retval ADCS_ERROR_STATE -> Success
 *         Other states -> Error
 */
static uint8 adcs_handle_error_event(os_itc_msg_handle_t *ipc_recv_msg);

/**
 * @brief This function handles the shutdown event
 * @param[in] ipc_recv_msg : Pointer to os_itc_msg_handle_t
 *
 * @return next state to be transitioned
 * @retval ADCS_SHUTDOWN_STATE -> Success
 *         ADCS_ERROR_STATE -> Error
 */
static uint8 adcs_handle_shutdown_event(os_itc_msg_handle_t *ipc_recv_msg);

/**
 * @brief This function handles the test event
 * @param[in] ipc_recv_msg : Pointer to os_itc_msg_handle_t
 *
 * @return next state to be transitioned
 * @retval ADCS_TEST_STATE -> Success
 *         ADCS_ERROR_STATE -> Error
 */
static uint8 adcs_handle_test_event(os_itc_msg_handle_t *ipc_recv_msg);

/**
 * @brief Main thread to handle ADCS IPC messages
 */
void adcs_csw_thread(void *param)
{

    // Variable declaration
    int8 status = ral_error;
    uint8 num_states = 0;
    uint8 state;
    e_sm_sts sm_status;
    uint8 *tc_tm_data;
    uint16 rx_size = 0;
    uint16 tx_size = 0;
    tx_size = tx_size;
    uint16 seq_no = 0;
    os_itc_msg_handle_t adcs_csw_recv_msg;
    uint8 *ptr_temp;
    uint8 *adcs_beacon_data;
    uint8 beacon_data_len;
    uint8 adcs_auto_commissioning_flg;
    uint32 adcs_tmr_periodicity = 0;

    // Interface selection
#ifndef LINUX_TEMP_PORT
    // Init ADCS interface
    adcs_intf_init();
#else
    // Init ADCS Socket interface
    adcs_sock_init();
#endif
    // Init IPC
    adcs_csw_trans_msg.src_entity = ADCS_CONTROLLER;
    num_states = 10;
    sm_status = sm_alloc(ADCS_FSM, num_states);
    if(SM_OK == sm_status)
    {
        nvm_parameter_read(NVM_DATA, NVM_ADCS_AUTO_COMMISIONING,
                        (uint8*)&adcs_auto_commissioning_flg);
        if(0 == adcs_auto_commissioning_flg)
        {
            state = ADCS_BYPASS_STATE;
        }
        else
        {
#ifdef ADCS_FLIGHT_READY
            state = ADCS_RESET_STATE;
#else
            state = ADCS_BOOT_INIT_STATE;
#endif
        }

        // Assigning ADCS State
        sm_status = sm_set_state(ADCS_FSM, state);

        if(SM_OK != sm_status)
        {
            //log error
            DEBUG_CPRINT((" \n State machine failure"));
            log_id(LOG_ERROR,LOG_ADCS,STATE_MACHINE_FAILURE);
        }
        else
        {
            /*
             * No need to send IPC if we want to start the ADCS with bypass
             * state
             */
            if(ADCS_BYPASS_STATE != state)
            {
#ifdef ADCS_FLIGHT_READY
                adcs_csw_trans_msg.Msg_id = RESET_REQUEST;
#else
                adcs_csw_trans_msg.Msg_id = BOOT_INIT_REQUEST;
#endif
                status = os_itc_msg_send(&adcs_csw_trans_msg,
                        ADCS_EVENT_THREAD,
                        os_send_itc_wait);
            }
            else
            {
                status = ral_success;
            }
        }
        if(ral_success == status)
        {
            while(1)
            {
                /*
                 * No need to receive IPC in ERROR state wait until recovery
                 */
                if(ADCS_ERROR_STATE != state)
                {
                    status = os_itc_msg_rcv(ADCS_CONTROLLER,
                            &adcs_csw_recv_msg,
                            os_wait_forever);
                }

                /*
                 * If the IPC is from other external entities, transition to
                 * bypass state
                 */
                if(ADCS_EVENT_THREAD != adcs_csw_recv_msg.src_entity)
                {
                    state = ADCS_BYPASS_STATE;
                }
                else
                {
                	state = sm_get_state(ADCS_FSM);
                }

                // Redirecting command to respective state function
                switch(state)
                {
                    case ADCS_RESET_STATE:
                    {
                        state = adcs_handle_reset_event(&adcs_csw_recv_msg);
                        sm_set_state(ADCS_FSM, state);
                    }
                    break;
                    case ADCS_BOOT_INIT_STATE:
                    {
                        state = adcs_handle_boot_event(&adcs_csw_recv_msg);
                        sm_set_state(ADCS_FSM, state);
                    }
                    break;
                    case ADCS_SENSOR_INIT_STATE:
                    {
                        state = adcs_handle_sensor_event(&adcs_csw_recv_msg);
                        sm_set_state(ADCS_FSM, state);
                    }
                    break;
                    case ADCS_ACTUATOR_INIT_STATE:
                    {
                        state = adcs_handle_actuator_event(&adcs_csw_recv_msg);
                        sm_set_state(ADCS_FSM, state);
                    }
                    break;
                    case ADCS_DETUMBLE_STATE:
                    {
                        state = adcs_handle_detumble_event(&adcs_csw_recv_msg);
                        sm_set_state(ADCS_FSM, state);
                    }
                    break;
                    case ADCS_COARSE_MOMENTUM_STATE:
                    {
                        state = adcs_handle_coarse_event(&adcs_csw_recv_msg);
                        sm_set_state(ADCS_FSM, state);
                    }
                    break;
                    case ADCS_FINE_MOMENTUM_STATE:
                    {
                        state = adcs_handle_fine_event(&adcs_csw_recv_msg);
                        sm_set_state(ADCS_FSM, state);
                    }
                    break;
                    case ADCS_ERROR_STATE:
                    {
                        state = adcs_handle_error_event(&adcs_csw_recv_msg);
                        sm_set_state(ADCS_FSM, state);
                    }
                    break;
                    case ADCS_SHUTDOWN_STATE:
                    {
                        state = adcs_handle_shutdown_event(&adcs_csw_recv_msg);
                        sm_set_state(ADCS_FSM, state);
                    }
                    break;
                    case ADCS_TEST_STATE:
                    {
                        state = adcs_handle_test_event(&adcs_csw_recv_msg);
                        sm_set_state(ADCS_FSM, state);
                    }
                    break;
                    case ADCS_BYPASS_STATE:
                    {
                        if (OBC_ADCS_PERIODIC_CB == adcs_csw_recv_msg.Msg_id)
                        {
                            adcs_health_info_event();
#if !defined(ADCS_HW_SIM) && !defined(LINUX_TEMP_PORT)
                            if(ADCS_TEMP_ERR_PERIOD < adcs_temp_err_cnt)
                            {
                                adcs_temperature_error_rcvry();
                                adcs_temp_err_cnt = 0;
                            }
                            if(adcs_interface_err > 5)
                            {
                                adcs_intrface_error_recvry();
                            }
#endif
                            adcs_temp_err_cnt++;
                        }
                        /*
                         * Handle and Send the Beacon Data to SDR APP
                         */
                        else if(SDR_APP_ADCS_MH_REQ == 
                                adcs_csw_recv_msg.Msg_id)
                        {
                            beacon_data_len = 4;
                            adcs_beacon_data = 
                                    (uint8_t*)os_malloc_eram(beacon_data_len);
                            adcs_get_beacon_data(adcs_beacon_data);
                            adcs_csw_trans_msg.Msg_id = ADCS_SDR_APP_MH_RSP;
                            adcs_csw_trans_msg.pld.pld_ptr = adcs_beacon_data;
                            adcs_csw_trans_msg.Msg_len = beacon_data_len;
                            adcs_csw_trans_msg.src_entity = ADCS_CONTROLLER;

                            int i=0;
                            DEBUG_CPRINT(("\nADCS Beacon Data: "));
                            for(i=0;i<beacon_data_len;i++)
                            {
                                DEBUG_CPRINT(("%X ",adcs_beacon_data[i]));
                            }
                            DEBUG_CPRINT(("\n"));
                            os_itc_msg_send(&adcs_csw_trans_msg,\
                                    adcs_csw_recv_msg.src_entity,\
                                    os_send_itc_wait);
                        }
                        else if(adcs_csw_recv_msg.src_entity == OBC_MAIN)
                        {
                            /*
                             * Verifying command as INIT / DEINIT /
                             * HEALTH CHECK since source entity from OBC_MAIN
                             */
                            if(OBC_ADCS_INIT_REQ == adcs_csw_recv_msg.Msg_id)
                            {
                                adcs_csw_trans_msg.Msg_id =ADCS_OBC_INIT_RSP;
                                adcs_csw_trans_msg.pld.pld_ptr = NULL;

                                status =
                                        os_itc_msg_send(&adcs_csw_trans_msg,
                                                OBC_MAIN, os_send_itc_wait);
#if !defined(ADCS_HW_SIM) && !defined(LINUX_TEMP_PORT)
                                // ADCS Initialize sequence
                                adcs_init_seq();
#endif
                            }
                            else if(OBC_ADCS_DEINIT_REQ ==
                                    adcs_csw_recv_msg.Msg_id)
                            {
                                adcs_csw_trans_msg.Msg_id =
                                    ADCS_OBC_DEINIT_RSP;
                                adcs_csw_trans_msg.pld.pld_ptr = NULL;

                                status =
                                    os_itc_msg_send(&adcs_csw_trans_msg,
                                                OBC_MAIN, os_send_itc_wait);
                            }
                            else if(OBC_ADCS_HEALTH_CHECK_REQ ==
                                    adcs_csw_recv_msg.Msg_id)
                            {
                                //adcs_exec_seq(id_seq, 5);
                            }
                            else if(OBC_ADCS_TIME_SYNC_REQ ==
                                    adcs_csw_recv_msg.Msg_id)
                            {
                                //Synchronize the UTC time
                                adcs_sync_time(adcs_csw_recv_msg.pld.pld_ptr);
                            }

                        }
                        else if(adcs_csw_recv_msg.src_entity ==
                                ORBIT_CONOPS_SCHEDULER)
                        {
                            if(ADCS_SCH_FALLBACK == adcs_csw_recv_msg.Msg_id)
                            {
                                // Fall back execution in case of error
                                adcs_exec_flbk();
                            }
                            else
                            {
                                if(NULL != adcs_csw_recv_msg.pld.pld_ptr)
                                {
                                    uint32_t *pld_data =
                                        adcs_csw_recv_msg.pld.pld_ptr;
                                    adcs_csw_recv_msg.Msg_len = pld_data[1];
                                    // Allocate memory for payload data
                                    tc_tm_data = (uint8 *)os_malloc_eram(
                                            adcs_csw_recv_msg.Msg_len);
#ifndef LINUX_TEMP_PORT
                                    /*
                                     *  Copy payload from IPC to locally
                                     *  allocated memory
                                     */
                                    if(TC_DEFERRED_STORAGE_LENGTH >=
                                            (pld_data[0] + pld_data[1]))
                                    {
                                        /*
                                         * Refer Conops Scheduler SW
                                         * Architecture Document for deferred
                                         * storage frame format information
                                         */
                                        // Extract radio Id from deffered storage
                                        adcs_radio_id = tc_storage[pld_data[0]];
                                        // Copy payload data
                                        os_memcpy(tc_tm_data,
                                                &tc_storage[pld_data[0] + 1],
                                                adcs_csw_recv_msg.Msg_len);
                                    }
#else
                                    // File operation variable declerations
                                    //uint16 line_no = 0;
                                    int itr = 0;
                                    FILE *fp;
                                    const unsigned MAX_LENGTH = 512;
                                    char buffer[MAX_LENGTH];
                                    char *token;
                                    uint32 file_offset = 0;

                                    // Open file for both reading and writing
                                    fp = fopen("tc_deffered_strng.txt", "r+");
                                    if(fp != NULL)
                                    {
                                        file_offset = pld_data[0] * 5;
                                        fseek(fp, file_offset, SEEK_SET);
#if 0
                                        //Get selected line data to
                                        //buffer variable
                                        do
                                        {
                                            fgets(buffer, MAX_LENGTH, fp);
                                            line_no++;
                                        }while(line_no < pld_data[0]);
#endif
                                        fgets(buffer, MAX_LENGTH, fp);
                                        // get the first token
                                        token = strtok(buffer, ",");

                                        /*
                                         * Refer Conops Scheduler SW
                                         * Architecture Document for deferred
                                         * storage frame format information
                                         */
                                        // Extract radio Id from deffered storage
                                        adcs_radio_id = tc_tm_data[itr] =
                                                    (uint8_t)strtol(token, \
                                                            NULL, 0);
                                        // Move to next tocken
                                        token = strtok(NULL, ",");
                                        // walk through other tokens
                                        while( (token != NULL) &&
                                            (itr < adcs_csw_recv_msg.Msg_len))
                                        {
                                            tc_tm_data[itr] =
                                                    (uint8_t)strtol(token, \
                                                            NULL, 0);
                                            token = strtok(NULL, ",");
                                            itr++;
                                        }
                                        // Close the file
                                        fclose(fp);
                                    }
                                    else
                                    {
                                        /*
                                         * File open failes stop further
                                         * process for current schedule
                                         */
                                        DEBUG_CPRINT(("File Open failed "
                                                "tc_deffered_strng.txt\n"));
                                        continue;
                                    }
#endif
                                    sm_set_state(ADCS_FSM, state);
                                    tx_size = adcs_csw_recv_msg.Msg_len;

                                    // Passing the copied payload to ADCS

                                    status = adcs_tc_tm_hdlr(tc_tm_data,
                                        tx_size, &adcs_va_buf[4], &rx_size);

                                    // Free allocated memory
                                    os_free(tc_tm_data);
                                    tc_tm_data = NULL;

                                    /*
                                     * Copy the sequence number back to the TM
                                     * buffer
                                     */
                                    os_memcpy(&adcs_va_buf, &seq_no,
                                            sizeof(seq_no));
                                    /*
                                     * Append the TM_CTRL data. 0x70 represents
                                     * enable encryption, authentication and
                                     * message type as response. Refer TC/TM
                                     * manager document for more information
                                     */
                                    adcs_va_buf[2] = 0x70;
                                    // Copy the Radio Id back to the TM buffer
                                    adcs_va_buf[3] = adcs_radio_id;
                                    adcs_csw_trans_msg.Msg_id =
                                            adcs_csw_recv_msg.Msg_id;
                                    adcs_csw_trans_msg.Msg_len = rx_size;

                                    // Allocate memory for response data
                                    ptr_temp = os_malloc_eram(rx_size + 3 + 1);

                                    /*
                                     *  copying response data from global
                                     *  variable to allocated memory
                                     */
                                    os_memcpy(ptr_temp, adcs_va_buf,
                                            rx_size + 3 + 1);
                                    adcs_csw_trans_msg.pld.pld_ptr =
                                            (uint8 *)ptr_temp;

                                    // Send IPC to TM storage to transmit to GS
                                    status = os_itc_msg_send(
                                            &adcs_csw_trans_msg,TM_STORAGE,
                                            os_send_itc_wait);
                                }
                                else
                                {
                                    // TODO Error handling
                                }
                            }
                        }
                        else
                        {
                            if(NULL != adcs_csw_recv_msg.pld.pld_ptr)
                            {
                                // Allocate memory for payload data
                                tc_tm_data = (uint8 *)os_malloc_eram(
                                        (adcs_csw_recv_msg.Msg_len) + 2 + 1);

                                os_memcpy(tc_tm_data,
                                        adcs_csw_recv_msg.pld.pld_ptr,
                                        (adcs_csw_recv_msg.Msg_len) + 2 + 1);
                                sm_set_state(ADCS_FSM, state);
                                // Extract the sequence number
                                os_memcpy(&seq_no, tc_tm_data, sizeof(seq_no));
                                // Extract the Radio Id
                                adcs_radio_id = tc_tm_data[2];
                                tx_size = adcs_csw_recv_msg.Msg_len;

                                // Passing the copied payload to ADCS
                                if(TC_SET_ADCS_STATE_CFG == tc_tm_data[5])
                                {
                                    if(adcs_auto_commissioning_flg != 
                                            tc_tm_data[6])
                                    {
                                        adcs_auto_commissioning_flg = 
                                                tc_tm_data[6];
                                        nvm_parameter_write(NVM_DATA,\
                                                NVM_ADCS_AUTO_COMMISIONING,\
                                                (uint8*)
                                                &adcs_auto_commissioning_flg);
                                        if(1 == adcs_auto_commissioning_flg)
                                        {
                                            sm_set_state(ADCS_FSM, ADCS_RESET_STATE);
                                            adcs_csw_trans_msg.Msg_id = 
                                                    RESET_REQUEST;
                                            os_itc_msg_send(&adcs_csw_trans_msg,
                                                    ADCS_EVENT_THREAD,
                                                    os_send_itc_wait);
                                        }
                                        status = OPERATION_SUCCESS;
                                    }
                                    else
                                    {
                                        status = 
                                                set_adcs_fsm_state(
                                                        tc_tm_data[7]);
                                    }
                                    adcs_va_buf[4] = ADCS_TCTM_SOF1;
                                    adcs_va_buf[5] = ADCS_TCTM_SOF2;
                                    adcs_va_buf[6] = TC_SET_ADCS_STATE_CFG;
                                    adcs_va_buf[7] = status;
                                    adcs_va_buf[8] = ADCS_TCTM_EOF1;
                                    adcs_va_buf[9] = ADCS_TCTM_EOF1;
                                    rx_size = 6;
                                }
                                else if(TC_SET_ADCS_HM_PERIODICITY ==
                                        tc_tm_data[5])
                                {
                                    os_timer_stop(adcs_priodic_tmr);
                                    os_memcpy(&adcs_tmr_periodicity,\
                                            &tc_tm_data[6],\
                                            sizeof(uint16));
                                    if(adcs_tmr_periodicity != 0)
                                    {
                                        // Converting seconds to milliseconds
                                        adcs_tmr_periodicity = 
                                                adcs_tmr_periodicity * 1000;
                                        /*
                                         * Splitting periodicity for cyclic
                                         * HM buffering
                                         */
                                        adcs_tmr_periodicity = 
                                                adcs_tmr_periodicity / 8;
                                        os_timer_start(adcs_priodic_tmr,\
                                                (os_tick_time_t)
                                                adcs_tmr_periodicity);
                                    }
                                    adcs_va_buf[4] = ADCS_TCTM_SOF1;
                                    adcs_va_buf[5] = ADCS_TCTM_SOF2;
                                    adcs_va_buf[6] = TC_SET_ADCS_HM_PERIODICITY;
                                    adcs_va_buf[7] = OPERATION_SUCCESS;
                                    adcs_va_buf[8] = ADCS_TCTM_EOF1;
                                    adcs_va_buf[9] = ADCS_TCTM_EOF1;
                                    rx_size = 6;
                                }
                                else
                                {
                                    status = adcs_tc_tm_hdlr(&tc_tm_data[3],
                                            tx_size, &adcs_va_buf[4],
                                            &rx_size);
                                }

                                // Free allocated memory
                                os_free(tc_tm_data);
                                tc_tm_data = NULL;
                                // Copy the sequence number back to the
                                // TM buffer
                                os_memcpy(&adcs_va_buf, &seq_no, \
                                        sizeof(seq_no));
                                // Copy the Radio Id back to the TM buffer
                                adcs_va_buf[3] = adcs_radio_id;
                                /*
                                 * Append the TM_CTRL data. 0x70 represents
                                 * enable encryption, authentication and
                                 * message type as response.
                                 * Refer TC/TM manager document for more
                                 * information
                                 */
                                adcs_va_buf[2] = 0x70;
                                adcs_csw_trans_msg.Msg_id =
                                        adcs_csw_recv_msg.Msg_id;
                                adcs_csw_trans_msg.Msg_len = rx_size;

                                // Allocate memory for response data
                                ptr_temp = os_malloc_eram(rx_size + 3 + 1);
                                /*
                                 *  copying response data from global variable
                                 *  to allocated memory
                                 */
                                os_memcpy(ptr_temp, adcs_va_buf, \
                                        rx_size + 3 + 1);
                                adcs_csw_trans_msg.pld.pld_ptr =
                                    (uint8 *)ptr_temp;
                                // Send IPC to TM storage to transmit to GS
                                status = os_itc_msg_send(&adcs_csw_trans_msg,
                                        TM_STORAGE,os_send_itc_wait);
                            }
                            else
                            {
                                // TODO - Error handling
                            }
                        }
                    }
                    break;
                }
                // free pointer received in IPC
                if(adcs_csw_recv_msg.pld.pld_ptr)
                {
#ifndef LINUX_TEMP_PORT
                    os_free(adcs_csw_recv_msg.pld.pld_ptr);
#endif
                    adcs_csw_recv_msg.pld.pld_ptr = NULL;
                }
            }
        }
    }
}

/**
 * @brief Event handler thread to handle ADCS IPC messages
 */
void adcs_event_hdlr_thread(void *param)
{
    os_status_t status;
    // Init IPC
    adcs_hdlr_trans_msg.src_entity = ADCS_EVENT_THREAD;
    while(1)
    {

        /*
         * Receive IPC from state handler for processing event
         */
        status = os_itc_msg_rcv(ADCS_EVENT_THREAD, &adcs_hdlr_recv_msg,
                                os_wait_forever);

        if(ral_success == status)
        {
            /*
             * Execute selected event
             */
            switch(adcs_hdlr_recv_msg.Msg_id)
            {
                case RESET_REQUEST:
                {
                    DEBUG_CPRINT(("\n Received RESET_REQUEST Command "));
                    log_id(LOG_INFO_L1,LOG_ADCS,
                            Received_RESET_REQUEST_Command);
                    status = adcs_reset();
                    DEBUG_CPRINT(("\n status of adcs reset is %d", status));
                    log_id_with_one_arg(LOG_INFO_L1,LOG_ADCS,
                            STATUS_OF_ADCS_RESET__u8_,status);
                    adcs_hdlr_trans_msg.pld.pld_data = status;
                    status = os_itc_msg_send(&adcs_hdlr_trans_msg,
                                            ADCS_CONTROLLER, os_send_itc_wait);
                }
                break;
                case BOOT_INIT_REQUEST:
                {
                    DEBUG_CPRINT((" Received BOOT_INIT_REQUEST Command \n"));
                    log_id(LOG_INFO_L1,LOG_ADCS,
                            Received_BOOT_INIT_REQUEST_Command);
                    status = adcs_boot();
                    DEBUG_CPRINT((" status of adcs boot is %d \n", status));
                    log_id_with_one_arg(LOG_INFO_L1,LOG_ADCS,
                            STATUS_OF_ADCS_BOOT__u8_,status);
                    adcs_hdlr_trans_msg.pld.pld_data = status;
                    status = os_itc_msg_send(&adcs_hdlr_trans_msg,
                            ADCS_CONTROLLER, os_send_itc_wait);
                }
                break;
                case SENSOR_INIT_REQUEST:
                {
                    DEBUG_CPRINT((" Received SENSOR_INIT_REQUEST Command \n"));
                    log_id(LOG_INFO_L1,LOG_ADCS,
                            Received_SENSOR_INIT_REQUEST_Command);
                    status = adcs_sensor_init();
                    DEBUG_CPRINT((" status of adcs sensor is %d \n", status));
                    log_id_with_one_arg(LOG_INFO_L1,LOG_ADCS,
                            STATUS_OF_ADCS_SENSOR__u8_,status);
                    adcs_hdlr_trans_msg.pld.pld_data = status;
                    status = os_itc_msg_send(&adcs_hdlr_trans_msg,
                            ADCS_CONTROLLER, os_send_itc_wait);
                }
                break;
                case ACTUATOR_INIT_REQUEST:
                {
                    DEBUG_CPRINT(("Received ACTUATOR_INIT_REQUEST Command\n"));
                    log_id(LOG_INFO_L1,LOG_ADCS,
                            Received_ACTUATOR_INIT_REQUEST_Command);
                    status = adcs_actuator_init();
                    DEBUG_CPRINT((" status of adcs actuator is %d\n", status));
                    log_id_with_one_arg(LOG_INFO_L1,LOG_ADCS,
                            STATUS_OF_ADCS_ACTUATOR__u8_,status);
                    adcs_hdlr_trans_msg.pld.pld_data = status;
                    status = os_itc_msg_send(&adcs_hdlr_trans_msg,
                            ADCS_CONTROLLER, os_send_itc_wait);
                }
                break;
                case DETUMBLE_REQUEST:
                {
                    DEBUG_CPRINT((" Received DETUMBLE_REQUEST Command \n"));
                    log_id(LOG_INFO_L1,LOG_ADCS,
                            Received_DETUMBLE_REQUEST_Command);
                    status = adcs_detumble();
                    DEBUG_CPRINT((" status of adcs adcs_detumble is %d \n",
                                status));
                    log_id_with_one_arg(LOG_INFO_L1,LOG_ADCS,
                            STATUS_OF_ADCS_DETUMBLE__u8_,status);
                    adcs_hdlr_trans_msg.pld.pld_data = status;
                    status = os_itc_msg_send(&adcs_hdlr_trans_msg,
                            ADCS_CONTROLLER, os_send_itc_wait);
                }
                break;
                case COARSE_MOMENTUM_REQUEST:
                {
                    DEBUG_CPRINT((" Received COARSE_MOMENTUM_REQUEST"
                                " Command \n"));
                    log_id(LOG_INFO_L1,LOG_ADCS,
                            Received_COARSE_MOMENTUM_REQUEST_Command);
                    status = adcs_coarse_momentum();
                    DEBUG_CPRINT(("status of adcs_coarse_momentum is %d\n",
                            status));
                    log_id_with_one_arg(LOG_INFO_L1,LOG_ADCS,
                            STATUS_OF_ADCS_COARSE_MOMENTUM__u8_,status);
                    adcs_hdlr_trans_msg.pld.pld_data = status;
                    status = os_itc_msg_send(&adcs_hdlr_trans_msg,
                            ADCS_CONTROLLER, os_send_itc_wait);
                }
                break;
                case FINE_MOMENTUM_REQUEST:
                {
                    DEBUG_CPRINT((" Received FINE_MOMENTUM_REQUEST"
                                " Command \n"));
                    log_id(LOG_INFO_L1,LOG_ADCS,
                            Received_FINE_MOMENTUM_REQUEST_Command);
                    status = adcs_fine_momentum();
                    DEBUG_CPRINT(("status of adcs_fine_momentum is %d \n",
                            status));
                    log_id_with_one_arg(LOG_INFO_L1,LOG_ADCS,
                            STATUS_OF_ADCS_FINE_MOMENTUM__u8_,status);
                    adcs_hdlr_trans_msg.pld.pld_data = status;
                    status = os_itc_msg_send(&adcs_hdlr_trans_msg,
                            ADCS_CONTROLLER, os_send_itc_wait);
                }
                break;
                case ADCS_SHUTDOWN_RQST:
                {
                    DEBUG_CPRINT((" Received SHUTDOWN_RQST Command \n"));
                    log_id(LOG_INFO_L1,LOG_ADCS,
                            Received_SHUTDOWN_RQST_Command);
                    status = adcs_shutdown_rqst();
                    DEBUG_CPRINT((" status of adcs shutdown is %d\n", status));
                    log_id_with_one_arg(LOG_INFO_L1,LOG_ADCS,
                            STATUS_OF_ADCS_SHUTDOWN__u8_,status);
                    adcs_hdlr_trans_msg.pld.pld_data = status;
                    status = os_itc_msg_send(&adcs_hdlr_trans_msg,
                            ADCS_CONTROLLER, os_send_itc_wait);
                }
                break;
                case ADCS_TEST_RQST:
                {
                    DEBUG_CPRINT((" Received TEST_RQST Command \n"));
                    log_id(LOG_INFO_L1,LOG_ADCS,
                            Received_TEST_RQST_Command);
                    status = adcs_test_mode();
                    DEBUG_CPRINT((" status of adcs test is %d \n", status));
                    log_id_with_one_arg(LOG_INFO_L1,LOG_ADCS,
                            STATUS_OF_ADCS_TEST__u8_,status);
                    adcs_hdlr_trans_msg.pld.pld_data = status;
                    status = os_itc_msg_send(&adcs_hdlr_trans_msg,
                            ADCS_CONTROLLER, os_send_itc_wait);
                }
                break;
            }
        }
    }
}

/**
* @brief This API for ADCS Health info timer callback
*/
void adcs_periodic_cb()
{
    os_itc_msg_handle_t adcs_ipc_msg;
    adcs_ipc_msg.Msg_id = OBC_ADCS_PERIODIC_CB;
    adcs_ipc_msg.Msg_len = 0;
    adcs_ipc_msg.pld.pld_data = 0;
    adcs_ipc_msg.pld.pld_ptr = NULL;
    adcs_ipc_msg.pld_info = 0;
    adcs_ipc_msg.src_entity = ADCS_CONTROLLER;
    os_itc_msg_send(&adcs_ipc_msg, ADCS_CONTROLLER, 100);
}

/**
* @brief This API for ADCS Health info timer creation
*/
void adcs_health_metrics_tmr_init()
{
    os_timer_create(&adcs_priodic_tmr, "adcs_periodic_tmr", PERIODIC,\
            adcs_periodic_cb, NULL);
    os_timer_start(adcs_priodic_tmr,\
            (os_tick_time_t)ADCS_PERIODIC_TIMER_SEC);
}

/**
 * @brief This API invoke a thread to handle the ADCS IPC
 * messages
 */
int32 adcs_csw_main(void)
{
    os_status_t status;

    // ADCS State handler thread
    status = os_thread_create(ADCS_CONTROLLER,
                              T_ADCS_CSW_STACK_SIZE,
                              P_ADCS_CONTROLLER,
                              "adcs_csw_thread",
                              adcs_csw_thread,
                              NULL,
                              NULL,
                              NULL);

    // ADCS Event handler thread
    status &= os_thread_create(ADCS_EVENT_THREAD,
                               T_ADCS_EVNT_HNDLR_STACK_SIZE,
                               P_ADCS_EVENT_HDLR,
                               "adcs_event_hdlr_thread",
                               adcs_event_hdlr_thread,
                               NULL,
                               NULL,
                               NULL);

    // FTM Initialization
    adcs_fw_init();

    // ADCS Health Metrics buffer initialization
    adcs_hm_buffer_init();

    // ADCS Health Metrics timer initialization
    adcs_health_metrics_tmr_init();

    if(ral_success == status)
    {
        adcs_csw_init = ADCS_IPC_OK;
    }
    else
    {
        adcs_csw_init = ADCS_IPC_ERR;
    }

    return status;
}

/**
 * @brief This function deletes the ADCS queue and release its memory
 */
os_status_t adcs_module_suspend(void)
{
    os_status_t status;

    status = os_thread_suspend(ADCS_CONTROLLER);

    return status;
}

/**
 * @brief This functionalities resumes the ADCS thread
 */
os_status_t adcs_module_resume(void)
{
    os_status_t status;

    status = os_thread_resume(ADCS_CONTROLLER);

    return status;
}

/**
 * @brief This function handles the reset event based on the response of
 *         reset request
 */
static uint8 adcs_handle_reset_event(os_itc_msg_handle_t *ipc_recv_msg)
{
    os_status_t status = ral_error;
    uint32 event_response = -1;
    uint8 state;

    event_response = ipc_recv_msg->pld.pld_data;
    DEBUG_CPRINT(("received response for ADCS_RESET : %d \n", event_response));
    switch(event_response)
    {
        case RESET_DONE:
        {
            DEBUG_CPRINT(("In RESET_DONE state \n "));
            log_id(LOG_INFO_L1,LOG_ADCS,RESET_DONE_STATE);
            state = ADCS_BOOT_INIT_STATE;
            sm_set_state(ADCS_FSM, state);
            adcs_csw_trans_msg.Msg_id = BOOT_INIT_REQUEST;
            DEBUG_CPRINT(("sending boot init request \n"));
            log_id(LOG_INFO_L1,LOG_ADCS,Sending_boot_init_request);
            status = os_itc_msg_send(&adcs_csw_trans_msg, ADCS_EVENT_THREAD,
                                     os_send_itc_wait);
            if(ral_success != status)
            {
                state = ADCS_ERROR_STATE;
                sm_set_state(ADCS_FSM, state);
            }
        }
        break;
        case RESET_FAIL:
        {
            DEBUG_CPRINT(("RESET_FAIL event \n"));
            log_id(LOG_ERROR,LOG_ADCS,RESET_FAIL_EVENT);
            state = ADCS_ERROR_STATE;
        }
        break;
        default:
        {
            state = ADCS_ERROR_STATE;
        }
        break;
    }
    return state;
}

/**
 * @brief This function handles the boot event based on the response of
 *         boot request
 */
static uint8 adcs_handle_boot_event(os_itc_msg_handle_t *ipc_recv_msg)
{
    os_status_t status = ral_error;
    uint32 event_response = -1;
    uint8 state;

    event_response = ipc_recv_msg->pld.pld_data;
    DEBUG_CPRINT(("received response for ADCS_BOOT : %d \n", event_response));
    switch(event_response)
    {
        case BOOT_INIT_DONE:
        {
            DEBUG_CPRINT(("In BOOT_INIT_DONE state \n "));
            log_id(LOG_INFO_L1,LOG_ADCS,BOOT_INIT_DONE_state);
            state = ADCS_SENSOR_INIT_STATE;
            sm_set_state(ADCS_FSM, state);
            adcs_csw_trans_msg.Msg_id = SENSOR_INIT_REQUEST;
            DEBUG_CPRINT(("sending SENSOR_INIT_REQUEST request \n"));
            log_id(LOG_INFO_L1,LOG_ADCS,Sending_sensor_init_request);
            status = os_itc_msg_send(&adcs_csw_trans_msg, ADCS_EVENT_THREAD,
                                     os_send_itc_wait);
            if(ral_success != status)
            {
                state = ADCS_ERROR_STATE;
                sm_set_state(ADCS_FSM, state);
            }
        }
        break;
        case BOOT_INIT_FAIL:
        {
            DEBUG_CPRINT(("BOOT_INIT_FAIL event \n"));
            log_id(LOG_ERROR,LOG_ADCS,BOOT_INIT_FAIL_EVENT);
            state = ADCS_ERROR_STATE;
        }
        break;
        default:
        {
            state = ADCS_ERROR_STATE;
        }
        break;
    }
    return state;
}
/**
 * @brief This function handles the sensor event based on the response of
 *         sensor init request
 */
static uint8 adcs_handle_sensor_event(os_itc_msg_handle_t *ipc_recv_msg)
{
    os_status_t status = ral_error;
    uint32 event_response = -1;
    uint8 state;

    event_response = ipc_recv_msg->pld.pld_data;
    DEBUG_CPRINT(("received response for ADCS_SENSOR: %d\n", event_response));
    switch(event_response)
    {
        case SENSOR_INIT_DONE:
        {
            DEBUG_CPRINT(("In SENSOR_INIT_DONE state \n "));
            log_id(LOG_INFO_L1,LOG_ADCS,SENSOR_INIT_DONE_state);
            state = ADCS_ACTUATOR_INIT_STATE;
            sm_set_state(ADCS_FSM, state);
            adcs_csw_trans_msg.Msg_id = ACTUATOR_INIT_REQUEST;
            DEBUG_CPRINT(("sending ACTUATOR_INIT_REQUEST request \n"));
            log_id(LOG_INFO_L1,LOG_ADCS,sending_ACTUATOR_INIT_REQUEST);
            status = os_itc_msg_send(&adcs_csw_trans_msg, ADCS_EVENT_THREAD,
                                     os_send_itc_wait);
            if(ral_success != status)
            {
                state = ADCS_ERROR_STATE;
                sm_set_state(ADCS_FSM, state);
            }
        }
        break;
        case SENSOR_INIT_FAIL:
        {
            DEBUG_CPRINT(("SENSOR_INIT_FAIL event \n"));
            log_id(LOG_ERROR,LOG_ADCS,SENSOR_INIT_FAIL_event);
            state = ADCS_ERROR_STATE;
        }
        break;
        default:
        {
            state = ADCS_ERROR_STATE;
        }
        break;
    }
    return state;
}

/**
 * @brief This function handles the actuator event based on the response of
 *         actuator init request
 */
static uint8 adcs_handle_actuator_event(os_itc_msg_handle_t *ipc_recv_msg)
{
    os_status_t status = ral_error;
    uint32 event_response = -1;
    uint8 state;

    event_response = ipc_recv_msg->pld.pld_data;
    DEBUG_CPRINT(("received response for ADCS_ACTUATOR : %d \n",
                event_response));
    switch(event_response)
    {
        case ACTUATOR_INIT_DONE:
        {
            DEBUG_CPRINT(("In ACTUATOR_INIT_DONE state \n "));
            log_id(LOG_INFO_L1,LOG_ADCS,ACTUATOR_INIT_DONE_state);
            state = ADCS_DETUMBLE_STATE;
            sm_set_state(ADCS_FSM, state);
            adcs_csw_trans_msg.Msg_id = DETUMBLE_REQUEST;
            DEBUG_CPRINT(("sending DETUMBLE_REQUEST request \n"));
            log_id(LOG_INFO_L1,LOG_ADCS,sending_DETUMBLE_REQUEST);
            status = os_itc_msg_send(&adcs_csw_trans_msg, ADCS_EVENT_THREAD,
                                     os_send_itc_wait);
            if(ral_success != status)
            {
                state = ADCS_ERROR_STATE;
                sm_set_state(ADCS_FSM, state);
            }
        }
        break;
        case ACTUATOR_INIT_FAIL:
        {
            DEBUG_CPRINT(("ACTUATOR_INIT_FAIL event \n"));
            log_id(LOG_ERROR,LOG_ADCS,ACTUATOR_INIT_FAIL_event);
            state = ADCS_ERROR_STATE;
        }
        break;
        default:
        {
            state = ADCS_ERROR_STATE;
        }
        break;
    }
    return state;
}

/**
 * @brief This function handles the detumble event based on the response of
 *         detumble request
 */
static uint8 adcs_handle_detumble_event(os_itc_msg_handle_t *ipc_recv_msg)
{
    os_status_t status = ral_error;
    uint32 event_response = -1;
    uint8 state;

    event_response = ipc_recv_msg->pld.pld_data;
    DEBUG_CPRINT(("received response for ADCS_DETUMBLE : %d \n",
                event_response));
    switch(event_response)
    {
        case DETUMBLE_DONE:
        {
            DEBUG_CPRINT(("In DETUMBLE_DONE state \n "));
            log_id(LOG_INFO_L1,LOG_ADCS,DETUMBLE_DONE_state);
            state = ADCS_COARSE_MOMENTUM_STATE;
            sm_set_state(ADCS_FSM, state);
            adcs_csw_trans_msg.Msg_id = COARSE_MOMENTUM_REQUEST;
            DEBUG_CPRINT(("sending COARSE_MOMENTUM_REQUEST request \n"));
            log_id(LOG_INFO_L1,LOG_ADCS,sending_COARSE_MOMENTUM_REQUEST);
            status = os_itc_msg_send(&adcs_csw_trans_msg, ADCS_EVENT_THREAD,
                                     os_send_itc_wait);
            if(ral_success != status)
            {
                state = ADCS_ERROR_STATE;
                sm_set_state(ADCS_FSM, state);
            }
        }
        break;
        case DETUMBLE_FAIL:
        {
            DEBUG_CPRINT(("DETUMBLE_FAIL event \n"));
            log_id(LOG_ERROR,LOG_ADCS,DETUMBLE_FAIL_event);
            state = ADCS_ERROR_STATE;
        }
        break;
        default:
        {
            state = ADCS_ERROR_STATE;
        }
        break;
    }
    return state;
}

/**
 * @brief This function handles the coarse event based on the response of
 *         coarse request
 */
static uint8 adcs_handle_coarse_event(os_itc_msg_handle_t *ipc_recv_msg)
{
    os_status_t status = ral_error;
    uint32 event_response = -1;
    uint8 state;

    event_response = ipc_recv_msg->pld.pld_data;
    DEBUG_CPRINT(("received response for COARSE_MOMENTUM : %d \n",
                event_response));
    switch(event_response)
    {
        case COARSE_MOMENTUM_DONE:
        {
            DEBUG_CPRINT(("In COARSE_MOMENTUM_DONE state \n "));
            log_id(LOG_INFO_L1,LOG_ADCS,COARSE_MOMENTUM_DONE_state);
            state = ADCS_FINE_MOMENTUM_STATE;
            sm_set_state(ADCS_FSM, state);
            adcs_csw_trans_msg.Msg_id = FINE_MOMENTUM_REQUEST;
            DEBUG_CPRINT(("sending FINE_MOMENTUM_REQUEST request \n"));
            log_id(LOG_INFO_L1,LOG_ADCS,sending_FINE_MOMENTUM_REQUEST);
            status = os_itc_msg_send(&adcs_csw_trans_msg, ADCS_EVENT_THREAD,
                                     os_send_itc_wait);
            if(ral_success != status)
            {
                state = ADCS_ERROR_STATE;
                sm_set_state(ADCS_FSM, state);
            }
        }
        break;
        case COARSE_MOMENTUM_FAIL:
        {
            DEBUG_CPRINT(("COARSE_MOMENTUM_FAIL event \n"));
            log_id(LOG_ERROR,LOG_ADCS,COARSE_MOMENTUM_FAIL_event);
            state = ADCS_ERROR_STATE;
        }
        break;
        default:
        {
            state = ADCS_ERROR_STATE;
        }
        break;
    }
    return state;
}

/**
 * @brief This function handles the fine event based on the response of
 *         fine request
 */
static uint8 adcs_handle_fine_event(os_itc_msg_handle_t *ipc_recv_msg)
{
    uint32 event_response = -1;
    uint8 state;

    event_response = ipc_recv_msg->pld.pld_data;
    DEBUG_CPRINT(("received response for FINE_MOMENTUM : %d \n",
                event_response));
    switch(event_response)
    {
        case FINE_MOMENTUM_DONE:
        {
            DEBUG_CPRINT(("In FINE_MOMENTUM_DONE state \n "));
            /*
             * Satellite is stable now. Wait for pointing or other request
             * external entities like ground station, scheduler etc.
             */
            log_id(LOG_INFO_L1,LOG_ADCS,FINE_MOMENTUM_DONE_event);
            state = ADCS_BYPASS_STATE;
        }
        break;
        case FINE_MOMENTUM_FAIL:
        {
            DEBUG_CPRINT(("FINE_MOMENTUM_FAIL event \n"));
            log_id(LOG_ERROR,LOG_ADCS,FINE_MOMENTUM_FAIL_event);
            state = ADCS_ERROR_STATE;
        }
        break;
        default:
        {
            state = ADCS_ERROR_STATE;
        }
        break;
    }
    return state;
}

/**
 * @brief This function handles the error event based on the response of
 *         error occured
 */
static uint8 adcs_handle_error_event(os_itc_msg_handle_t *ipc_recv_msg)
{
    uint8 state;

    /*
     * As of now the error state doesn't have any intelligence to handle
     * the error events. So transition to bypass state so that the error
     * can be handled from the ground station
     */
    state = ADCS_BYPASS_STATE;

    return state;
}

/**
 * @brief This function handles the shutdown event based on the response of
 *         shutdown request
 */
static uint8 adcs_handle_shutdown_event(os_itc_msg_handle_t *ipc_recv_msg)
{
    os_status_t status = ral_error;
    uint8 state;

    state = ADCS_SHUTDOWN_STATE;
    sm_set_state(ADCS_FSM, state);
    adcs_csw_trans_msg.Msg_id = ADCS_SHUTDOWN_RQST;
    DEBUG_CPRINT(("sending SHUTDOWN request \n"));
    log_id(LOG_INFO_L1,LOG_ADCS,sending_SHUTDOWN_request);
    status = os_itc_msg_send(&adcs_csw_trans_msg, ADCS_EVENT_THREAD,
                             os_send_itc_wait);
    if(ral_success != status)
    {
        state = ADCS_ERROR_STATE;
        sm_set_state(ADCS_FSM, state);
    }
    return state;
}

/**
 * @brief This function handles the test event based on the response of
 *         test request
 */
static uint8 adcs_handle_test_event(os_itc_msg_handle_t *ipc_recv_msg)
{
    os_status_t status = ral_error;
    uint8 state;

    state = ADCS_TEST_STATE;
    sm_set_state(ADCS_FSM, state);
    adcs_csw_trans_msg.Msg_id = ADCS_TEST_RQST;
    DEBUG_CPRINT(("sending TEST request \n"));
    log_id(LOG_INFO_L1,LOG_ADCS,sending_TEST_request);
    status = os_itc_msg_send(&adcs_csw_trans_msg, ADCS_EVENT_THREAD,
                             os_send_itc_wait);
    if(ral_success != status)
    {
        state = ADCS_ERROR_STATE;
        sm_set_state(ADCS_FSM, state);
    }
    return state;
}

/**
 * @brief This function configure ADCS FSM state based on previous state.
 */
int8 set_adcs_fsm_state(uint8 adcs_new_state)
{
    int8 status = OPERATION_FAILURE;
    uint8 adcs_state = 0;
    adcs_state = sm_get_state(ADCS_FSM);

    switch(adcs_new_state)
    {
    case ADCS_RESET_STATE:
    {
        sm_set_state(ADCS_FSM, ADCS_RESET_STATE);
        adcs_csw_trans_msg.Msg_id = RESET_REQUEST;
        os_itc_msg_send(&adcs_csw_trans_msg,
                ADCS_EVENT_THREAD,
                os_send_itc_wait);
        status = OPERATION_SUCCESS;
    }
    break;
    case ADCS_BOOT_INIT_STATE:
    {
        if(adcs_state == ADCS_RESET_STATE)
        {
            sm_set_state(ADCS_FSM, ADCS_BOOT_INIT_STATE);
            adcs_csw_trans_msg.Msg_id = BOOT_INIT_REQUEST;
            os_itc_msg_send(&adcs_csw_trans_msg,
                    ADCS_EVENT_THREAD,
                    os_send_itc_wait);
            status = OPERATION_SUCCESS;
        }
        else
        {
            // No Action operation failure
        }
    }
    break;
    case ADCS_SENSOR_INIT_STATE:
    {
        if(adcs_state == ADCS_BOOT_INIT_STATE)
        {
            sm_set_state(ADCS_FSM, ADCS_SENSOR_INIT_STATE);
            adcs_csw_trans_msg.Msg_id = SENSOR_INIT_REQUEST;
            os_itc_msg_send(&adcs_csw_trans_msg,
                    ADCS_EVENT_THREAD,
                    os_send_itc_wait);
            status = OPERATION_SUCCESS;
        }
        else
        {
            // No Action operation failure
        }
    }
    break;
    case ADCS_ACTUATOR_INIT_STATE:
    {
        if(adcs_state == ADCS_SENSOR_INIT_STATE)
        {
            sm_set_state(ADCS_FSM, ADCS_ACTUATOR_INIT_STATE);
            adcs_csw_trans_msg.Msg_id = ACTUATOR_INIT_REQUEST;
            status = os_itc_msg_send(&adcs_csw_trans_msg,
                    ADCS_EVENT_THREAD,
                    os_send_itc_wait);
            status = OPERATION_SUCCESS;
        }
        else
        {
            // No Action operation failure
        }
    }
    break;
    case ADCS_DETUMBLE_STATE:
    {
        if(adcs_state == ADCS_ACTUATOR_INIT_STATE)
        {
            sm_set_state(ADCS_FSM, ADCS_DETUMBLE_STATE);
            adcs_csw_trans_msg.Msg_id = DETUMBLE_REQUEST;
            status = os_itc_msg_send(&adcs_csw_trans_msg,
                    ADCS_EVENT_THREAD,
                    os_send_itc_wait);
            status = OPERATION_SUCCESS;
        }
        else
        {
            // No Action operation failure
        }
    }
    break;
    case ADCS_COARSE_MOMENTUM_STATE:
    {
        if(adcs_state == ADCS_DETUMBLE_STATE)
        {
            sm_set_state(ADCS_FSM, ADCS_COARSE_MOMENTUM_STATE);
            adcs_csw_trans_msg.Msg_id = COARSE_MOMENTUM_REQUEST;
            status = os_itc_msg_send(&adcs_csw_trans_msg,
                    ADCS_EVENT_THREAD,
                    os_send_itc_wait);
            status = OPERATION_SUCCESS;
        }
        else
        {
            // No Action operation failure
        }
    }
    break;
    case ADCS_FINE_MOMENTUM_STATE:
    {
        if(adcs_state == ADCS_COARSE_MOMENTUM_STATE)
        {
            sm_set_state(ADCS_FSM, ADCS_FINE_MOMENTUM_STATE);
            adcs_csw_trans_msg.Msg_id = FINE_MOMENTUM_REQUEST;
            status = os_itc_msg_send(&adcs_csw_trans_msg,
                    ADCS_EVENT_THREAD,
                    os_send_itc_wait);
            status = OPERATION_SUCCESS;
        }
        else
        {
            // No Action operation failure
        }
    }
    break;
    case ADCS_SHUTDOWN_STATE:
    {
        sm_set_state(ADCS_FSM, ADCS_SHUTDOWN_STATE);
        adcs_csw_trans_msg.Msg_id = ADCS_SHUTDOWN_RQST;
        status = os_itc_msg_send(&adcs_csw_trans_msg,
                ADCS_EVENT_THREAD,
                os_send_itc_wait);
        status = OPERATION_SUCCESS;
    }
    break;
    case ADCS_BYPASS_STATE:
    {
        sm_set_state(ADCS_FSM, ADCS_BYPASS_STATE);
        status = OPERATION_SUCCESS;
    }
    break;
    }
    return status;
}
