Program Listing for File error.c

Return to documentation for file (mainboard/Src/error/error.c)

#include "error/error.h"

#include "error/error_list_ref.h"
#include "mainboard_config.h"
#include "tim.h"

#include <stdlib.h>
#ifndef max
#define max(a, b) ((a) > (b) ? (a) : (b))
#endif
#ifndef min
#define min(a, b) ((a) < (b) ? (a) : (b))
#endif

const error_timeout error_timeouts[ERROR_NUM_ERRORS] = {
    [ERROR_CELL_LOW_VOLTAGE]      = SOFT,
    [ERROR_CELL_UNDER_VOLTAGE]    = 500,
    [ERROR_CELL_OVER_VOLTAGE]     = 500,
    [ERROR_CELL_HIGH_TEMPERATURE] = SOFT,
    [ERROR_CELL_OVER_TEMPERATURE] = 1000,
    [ERROR_OVER_CURRENT]          = 500,
    [ERROR_CAN]                   = SOFT,
    [ERROR_ADC_INIT]              = SOFT,
    [ERROR_ADC_TIMEOUT]           = SOFT,
    [ERROR_INT_VOLTAGE_MISMATCH]  = SOFT,
    [ERROR_CELLBOARD_COMM]        = 500,
    [ERROR_CELLBOARD_INTERNAL]    = 500,
    [ERROR_FEEDBACK]              = SOFT,
    [ERROR_EEPROM_COMM]           = SOFT,
    [ERROR_EEPROM_WRITE]          = SOFT};

llist er_list = NULL;

uint32_t get_timeout_delta(error_t *error) {
    uint32_t delta = error->timestamp + error_timeouts[error->id] - HAL_GetTick();
    return delta <= error_timeouts[error->id] ? delta : 0;
}

bool error_equals(llist_node a, llist_node b) {
    return ((error_t *)a)->id == ((error_t *)b)->id && ((error_t *)a)->offset == ((error_t *)b)->offset;
}

int8_t error_compare(llist_node a, llist_node b) {
    // TODO: check if error is soft
    if (get_timeout_delta((error_t *)a) < get_timeout_delta((error_t *)b))
        return 1;
    if (get_timeout_delta((error_t *)a) == get_timeout_delta((error_t *)b))
        return 0;
    return -1;
}

bool error_set_timer(error_t *error) {
    HAL_TIM_Base_Stop_IT(&HTIM_ERR);
    HAL_TIM_OC_Stop_IT(&HTIM_ERR, TIM_CHANNEL_1);
    HAL_GPIO_WritePin(GPIO1_GPIO_Port, GPIO1_Pin, GPIO_PIN_RESET);

    if (error != NULL && error->state == STATE_WARNING && error_timeouts[error->id] < SOFT) {
        // Set counter period register to the delta
        HAL_TIM_Base_Start_IT(&HTIM_ERR);
        HAL_TIM_OC_Start_IT(&HTIM_ERR, TIM_CHANNEL_1);

        volatile uint16_t delta = (get_timeout_delta(error) * 10U);
        uint16_t pulse          = HAL_TIM_ReadCapturedValue(&HTIM_ERR, TIM_CHANNEL_1);
        __HAL_TIM_SET_COMPARE(&HTIM_ERR, TIM_CHANNEL_1, pulse + delta);
        HAL_GPIO_WritePin(GPIO1_GPIO_Port, GPIO1_Pin, GPIO_PIN_SET);

        return true;
    } else {
    }

    return false;
}

void error_init() {
    er_list = llist_init(error_compare, error_equals);
    HAL_TIM_Base_Stop_IT(&HTIM_ERR);
}

void error_init_error(error_t *error, error_id id, uint8_t offset, uint32_t timestamp) {
    error->id        = id;
    error->offset    = offset;
    error->state     = STATE_WARNING;
    error->timestamp = timestamp;
}

bool error_set(error_id id, uint8_t offset, uint32_t timestamp) {
    // Check if error exists
    if (*error_list_ref_array_element(id, offset) == NULL) {
        error_t *error = malloc(sizeof(error_t));

        if (error == NULL) {
            return false;
        }

        error_init_error(error, id, offset, timestamp);

        if (llist_insert_priority(er_list, (llist_node)error) != LLIST_SUCCESS) {
            return false;
        }

        (*error_list_ref_array_element(id, offset)) = (llist_node)error;

        // Re-set timer if first in list
        if (error_equals(llist_get_head(er_list), error)) {
            error_set_timer(error);
        }
    }

    return true;
}

error_t *error_get_top() {
    return (error_t *)llist_get_head(er_list);
}
bool error_set_fatal(error_t *error) {
    if (error != NULL && error->state != STATE_FATAL) {
        error->state = STATE_FATAL;
        return true;
    }
    return false;
}

bool error_reset(error_id id, uint8_t offset) {
    if (*error_list_ref_array_element(id, offset) != NULL) {
        error_t *error = (error_t *)(*error_list_ref_array_element(id, offset));

        // Check if error is fatal; in that case do not remove it
        if (error->state == STATE_FATAL) {
            //error->state = ERROR_NOT_ACTIVE;
            return true;
        }

        // If we are removing the first error, re-set the timer to the second error
        if (error_equals(llist_get_head(er_list), (llist_node)error)) {
            error_t *tmp = NULL;

            // No need to check llist_get output because passing NULL to error_set_timer is legal
            llist_get(er_list, 1, (llist_node *)&tmp);
            error_set_timer(tmp);
        }

        if (llist_remove_by_node(er_list, (llist_node)error) != LLIST_SUCCESS) {
            return false;
        }

        //ERROR_GET_REF(id, offset) = NULL;
        (*error_list_ref_array_element(id, offset)) = NULL;
        //error_list_ref_array[id][offset] = NULL;

        return true;
    }

    return false;
}

size_t error_get_fatal() {
    size_t count = error_count();
    error_t errors[count];
    error_dump(errors);

    size_t fatal = 0;
    for (size_t i = 0; i < count; i++) {
        if (errors[i].state == STATE_FATAL) {
            fatal++;
        }
    }

    return fatal;
}

size_t error_count() {
    return llist_size(er_list);
}

void error_dump(error_t errors[]) {
    llist_export(er_list, (void *)errors, sizeof(error_t));
}