diff --git a/CMakeLists.txt b/CMakeLists.txt index 3380b957..4e2769e7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -19,6 +19,7 @@ elseif(CMAKE_BUILD_TYPE MATCHES FW) main.c sys/console_io/console.c sys/i2c/i2c_io.c + sys/logging/logging.c services/controller/controller.c lm75bd/lm75bd.c @@ -33,6 +34,7 @@ elseif(CMAKE_BUILD_TYPE MATCHES FW) sys sys/i2c sys/console_io + sys/logging lm75bd services/controller services/thermal_mgr diff --git a/lm75bd/lm75bd.c b/lm75bd/lm75bd.c index a666aa44..7d63da66 100644 --- a/lm75bd/lm75bd.c +++ b/lm75bd/lm75bd.c @@ -1,6 +1,7 @@ #include "lm75bd.h" #include "i2c_io.h" #include "errors.h" +#include "logging.h" #include #include @@ -14,10 +15,8 @@ error_code_t lm75bdInit(lm75bd_config_t *config) { if (config == NULL) return ERR_CODE_INVALID_ARG; - errCode = writeConfigLM75BD(config->devAddr, config->osFaultQueueSize, config->osPolarity, - config->osOperationMode, config->devOperationMode); - - if (errCode != ERR_CODE_SUCCESS) return errCode; + RETURN_IF_ERROR_CODE(writeConfigLM75BD(config->devAddr, config->osFaultQueueSize, config->osPolarity, + config->osOperationMode, config->devOperationMode)); // Assume that the overtemperature and hysteresis thresholds are already set // Hysteresis: 75 degrees Celsius diff --git a/services/controller/controller.c b/services/controller/controller.c index ecdc5e0d..248b392e 100644 --- a/services/controller/controller.c +++ b/services/controller/controller.c @@ -4,6 +4,7 @@ #include "lm75bd.h" #include "errors.h" #include "i2c_io.h" +#include "logging.h" #include #include @@ -39,6 +40,7 @@ static void controller(void *pvParameters) { // Initialize the mutex that protects the console output initConsole(); initI2C(); + initLogger(); static lm75bd_config_t config = {0}; config.devAddr = LM75BD_OBC_I2C_ADDR; diff --git a/sys/errors.h b/sys/errors.h index f4805615..975f34e9 100644 --- a/sys/errors.h +++ b/sys/errors.h @@ -16,4 +16,8 @@ typedef enum { /* Driver errors */ ERR_CODE_I2C_TRANSFER_TIMEOUT = 200, + + /* Logging errors */ + ERR_CODE_BUFF_TOO_SMALL = 300, + ERR_CODE_LOG_MSG_SILENCED = 301, } error_code_t; diff --git a/sys/logging/logging.c b/sys/logging/logging.c new file mode 100644 index 00000000..dcadc6d5 --- /dev/null +++ b/sys/logging/logging.c @@ -0,0 +1,57 @@ +#include "logging.h" +#include "errors.h" +#include "console.h" + +#include +#include +#include + +#define MAX_MSG_SIZE 128U +#define MAX_FNAME_LINENUM_SIZE 128U +// Extra 10 for the small extra pieces in "%s - %s\r\n" +#define MAX_LOG_SIZE (MAX_MSG_SIZE + MAX_FNAME_LINENUM_SIZE + 10U) + +#define UART_MUTEX_BLOCK_TIME portMAX_DELAY + +static const char *LEVEL_STRINGS[] = {"TRACE", "DEBUG", "INFO", "WARN", "ERROR", "FATAL"}; + +static log_level_t logLevel; + +void initLogger(void) { + logLevel = LOG_DEFAULT_LEVEL; +} + +void logSetLevel(log_level_t newLogLevel) { logLevel = newLogLevel; } + +error_code_t logLog(log_level_t msgLevel, const char *file, uint32_t line, const char *s, ...) { + if (msgLevel < logLevel) return ERR_CODE_LOG_MSG_SILENCED; + + if (file == NULL || s == NULL) return ERR_CODE_INVALID_ARG; + + int ret = 0; + + // Message + char msgbuf[MAX_MSG_SIZE] = {0}; + va_list args; + va_start(args, s); + ret = vsnprintf(msgbuf, MAX_MSG_SIZE, s, args); + va_end(args); + if (ret < 0) return ERR_CODE_INVALID_ARG; + if ((uint32_t)ret >= MAX_MSG_SIZE) return ERR_CODE_BUFF_TOO_SMALL; + + // File & line number + char infobuf[MAX_FNAME_LINENUM_SIZE] = {0}; + ret = snprintf(infobuf, MAX_FNAME_LINENUM_SIZE, "%-5s -> %s:%u", LEVEL_STRINGS[msgLevel], file, line); + if (ret < 0) return ERR_CODE_INVALID_ARG; + if ((uint32_t)ret >= MAX_FNAME_LINENUM_SIZE) return ERR_CODE_BUFF_TOO_SMALL; + + // Prepare entire output + char buf[MAX_LOG_SIZE] = {0}; + ret = snprintf(buf, MAX_LOG_SIZE, "%s - %s\r\n", infobuf, msgbuf); + if (ret < 0) return ERR_CODE_INVALID_ARG; + if ((uint32_t)ret >= MAX_LOG_SIZE) return ERR_CODE_BUFF_TOO_SMALL; + + printConsole((unsigned char *)buf); + + return ERR_CODE_SUCCESS; +} diff --git a/sys/logging/logging.h b/sys/logging/logging.h new file mode 100644 index 00000000..a7d5298d --- /dev/null +++ b/sys/logging/logging.h @@ -0,0 +1,77 @@ +#pragma once + +#include "errors.h" + +#include +#include + +/** + * @enum log_level_t + * @brief Log levels enum. + * + * Enum containing all log levels. + */ +typedef enum { LOG_TRACE, LOG_DEBUG, LOG_INFO, LOG_WARN, LOG_ERROR, LOG_FATAL, LOG_OFF } log_level_t; + +#ifndef LOG_DEFAULT_LEVEL +#define LOG_DEFAULT_LEVEL LOG_TRACE +#endif + +#define LOG_TRACE(...) logLog(LOG_TRACE, __FILE__, __LINE__, __VA_ARGS__) +#define LOG_DEBUG(...) logLog(LOG_DEBUG, __FILE__, __LINE__, __VA_ARGS__) +#define LOG_INFO(...) logLog(LOG_INFO, __FILE__, __LINE__, __VA_ARGS__) +#define LOG_WARN(...) logLog(LOG_WARN, __FILE__, __LINE__, __VA_ARGS__) +#define LOG_ERROR(...) logLog(LOG_ERROR, __FILE__, __LINE__, __VA_ARGS__) +#define LOG_FATAL(...) logLog(LOG_FATAL, __FILE__, __LINE__, __VA_ARGS__) + +#define LOG_ERROR_CODE(errCode) LOG_ERROR("Error code: %lu", (uint32_t)errCode) + +#define RETURN_IF_ERROR_CODE(_ret) \ + do { \ + errCode = _ret; \ + if (errCode != ERR_CODE_SUCCESS) { \ + LOG_ERROR_CODE(errCode); \ + return errCode; \ + } \ + } while (0) + +#define LOG_IF_ERROR_CODE(_ret) \ + do { \ + errCode = _ret; \ + if (errCode != ERR_CODE_SUCCESS) { \ + LOG_ERROR_CODE(errCode); \ + } \ + } while (0) + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Initialize the logger + */ +void initLogger(void); + +/** + * @brief Set the logging level + * + * @param newLogLevel The new logging level + */ +void logSetLevel(log_level_t newLogLevel); + +/** + * @brief Log a message + * + * @param msgLevel Level of the message + * @param file File of message + * @param line Line of message + * @param s Message to log + * @param ... Additional arguments for the message + * @return error_code_t + * + */ +error_code_t logLog(log_level_t msgLevel, const char *file, uint32_t line, const char *s, ...); + +#ifdef __cplusplus +} +#endif diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 4f40cdc7..4bdcd3bf 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -7,6 +7,7 @@ set(TEST_DEPENDENCIES set(TEST_SOURCES ${CMAKE_SOURCE_DIR}/test/main.cpp ${CMAKE_SOURCE_DIR}/test/test_driver.cpp + ${CMAKE_SOURCE_DIR}/test/mock_logging.c ) set(TEST_SOURCES ${TEST_SOURCES} ${TEST_DEPENDENCIES}) @@ -19,7 +20,8 @@ target_include_directories(${TEST_BINARY} ${CMAKE_SOURCE_DIR}/lm75bd ${CMAKE_SOURCE_DIR}/sys ${CMAKE_SOURCE_DIR}/sys/i2c - ${CMAKE_SOURCE_DIR}/sys/console + ${CMAKE_SOURCE_DIR}/sys/console_io + ${CMAKE_SOURCE_DIR}/sys/logging ) target_link_libraries(${TEST_BINARY} PRIVATE diff --git a/test/mock_logging.c b/test/mock_logging.c new file mode 100644 index 00000000..809e0cc9 --- /dev/null +++ b/test/mock_logging.c @@ -0,0 +1,12 @@ +#include "logging.h" +#include "errors.h" + +#include +#include +#include + +void logSetLevel(log_level_t newLogLevel) { /* do nothing */ } + +error_code_t logLog(log_level_t msgLevel, const char *file, uint32_t line, const char *s, ...) { + return ERR_CODE_SUCCESS; +}