diff --git a/CMakeLists.txt b/CMakeLists.txt index bfe3da841..75b81a292 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,34 +7,40 @@ set(FSFW_REVISION 0) # Add the cmake folder so the FindSphinx module is found set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake" ${CMAKE_MODULE_PATH}) -set(FSFW_ETL_LIB_MAJOR_VERSION 20 CACHE STRING - "ETL library major version requirement" -) -set(FSFW_ETL_LIB_VERSION ${FSFW_ETL_LIB_MAJOR_VERSION}.27.3 CACHE STRING - "ETL library exact version requirement" -) +set(FSFW_ETL_LIB_NAME etl) set(FSFW_ETL_LINK_TARGET etl::etl) +set(FSFW_ETL_LIB_MAJOR_VERSION 20 CACHE STRING + "ETL library major version requirement" + ) +set(FSFW_ETL_LIB_VERSION ${FSFW_ETL_LIB_MAJOR_VERSION}.27.3 CACHE STRING + "ETL library exact version requirement" + ) set(FSFW_CATCH2_LIB_MAJOR_VERSION 3 CACHE STRING - "Catch2 library major version requirement" -) + "Catch2 library major version requirement" + ) set(FSFW_CATCH2_LIB_VERSION v${FSFW_CATCH2_LIB_MAJOR_VERSION}.0.0-preview5 CACHE STRING - "Catch2 library exact version requirement" -) + "Catch2 library exact version requirement" + ) -set(FSFW_ETL_LIB_NAME etl) +set(FSFW_FMT_LIB_NAME fmt) +set(FSFW_FMT_LINK_TARGET fmt::fmt) +set(FSFW_FMT_LIB_MAJOR_VERSION 8 CACHE STRING "{fmt} library major version requirement") +set(FSFW_FMT_LIB_VERSION v${FSFW_FMT_LIB_MAJOR_VERSION}.1.1 CACHE STRING + "{fmt} library exact version requirement" + ) option(FSFW_GENERATE_SECTIONS - "Generate function and data sections. Required to remove unused code" ON -) + "Generate function and data sections. Required to remove unused code" ON + ) if(FSFW_GENERATE_SECTIONS) - option(FSFW_REMOVE_UNUSED_CODE "Remove unused code" ON) + option(FSFW_REMOVE_UNUSED_CODE "Remove unused code" ON) endif() option(FSFW_BUILD_UNITTESTS "Build unittest binary in addition to static library" OFF) option(FSFW_BUILD_DOCS "Build documentation with Sphinx and Doxygen" OFF) if(FSFW_BUILD_UNITTESTS) - option(FSFW_TESTS_GEN_COV "Generate coverage data for unittests" ON) + option(FSFW_TESTS_GEN_COV "Generate coverage data for unittests" ON) endif() option(FSFW_WARNING_SHADOW_LOCAL_GCC "Enable -Wshadow=local warning in GCC" ON) @@ -60,84 +66,105 @@ set(FSFW_TEST_TGT fsfw-tests) set(FSFW_DUMMY_TGT fsfw-dummy) project(${LIB_FSFW_NAME}) -add_library(${LIB_FSFW_NAME}) +add_library(${LIB_FSFW_NAME} src/fsfw/serviceinterface/fmtWrapper.h src/fsfw/serviceinterface/fmtWrapper.cpp) if(FSFW_BUILD_UNITTESTS) - message(STATUS "Building the FSFW unittests in addition to the static library") - # Check whether the user has already installed Catch2 first - find_package(Catch2 ${FSFW_CATCH2_LIB_MAJOR_VERSION}) - # Not installed, so use FetchContent to download and provide Catch2 - if(NOT Catch2_FOUND) - message(STATUS "Catch2 installation not found. Downloading Catch2 library with FetchContent") - include(FetchContent) + message(STATUS "Building the FSFW unittests in addition to the static library") + # Check whether the user has already installed Catch2 first + find_package(Catch2 ${FSFW_CATCH2_LIB_MAJOR_VERSION}) + # Not installed, so use FetchContent to download and provide Catch2 + if(NOT Catch2_FOUND) + message(STATUS "Catch2 installation not found. Downloading Catch2 library with FetchContent") + include(FetchContent) - FetchContent_Declare( - Catch2 - GIT_REPOSITORY https://github.com/catchorg/Catch2.git - GIT_TAG ${FSFW_CATCH2_LIB_VERSION} - ) + FetchContent_Declare( + Catch2 + GIT_REPOSITORY https://github.com/catchorg/Catch2.git + GIT_TAG ${FSFW_CATCH2_LIB_VERSION} + ) - list(APPEND FSFW_FETCH_CONTENT_TARGETS Catch2) - endif() + list(APPEND FSFW_FETCH_CONTENT_TARGETS Catch2) + endif() - set(FSFW_CONFIG_PATH tests/src/fsfw_tests/unit/testcfg) - configure_file(tests/src/fsfw_tests/unit/testcfg/FSFWConfig.h.in FSFWConfig.h) - configure_file(tests/src/fsfw_tests/unit/testcfg/TestsConfig.h.in tests/TestsConfig.h) + set(FSFW_CONFIG_PATH tests/src/fsfw_tests/unit/testcfg) + configure_file(tests/src/fsfw_tests/unit/testcfg/FSFWConfig.h.in FSFWConfig.h) + configure_file(tests/src/fsfw_tests/unit/testcfg/TestsConfig.h.in tests/TestsConfig.h) - project(${FSFW_TEST_TGT} CXX C) - add_executable(${FSFW_TEST_TGT}) + project(${FSFW_TEST_TGT} CXX C) + add_executable(${FSFW_TEST_TGT}) - if(FSFW_TESTS_GEN_COV) - message(STATUS "Generating coverage data for the library") - message(STATUS "Targets linking against ${LIB_FSFW_NAME} " - "will be compiled with coverage data as well" - ) - include(FetchContent) - FetchContent_Declare( - cmake-modules - GIT_REPOSITORY https://github.com/bilke/cmake-modules.git - ) - FetchContent_MakeAvailable(cmake-modules) - set(CMAKE_BUILD_TYPE "Debug") - list(APPEND CMAKE_MODULE_PATH ${cmake-modules_SOURCE_DIR}) - include(CodeCoverage) - endif() + if(FSFW_TESTS_GEN_COV) + message(STATUS "Generating coverage data for the library") + message(STATUS "Targets linking against ${LIB_FSFW_NAME} " + "will be compiled with coverage data as well" + ) + include(FetchContent) + FetchContent_Declare( + cmake-modules + GIT_REPOSITORY https://github.com/bilke/cmake-modules.git + ) + FetchContent_MakeAvailable(cmake-modules) + set(CMAKE_BUILD_TYPE "Debug") + list(APPEND CMAKE_MODULE_PATH ${cmake-modules_SOURCE_DIR}) + include(CodeCoverage) + endif() endif() -message(STATUS "Finding and/or providing ETL library") +message(STATUS "Finding and/or providing etl (Embedded Template Library)") # Check whether the user has already installed ETL first find_package(${FSFW_ETL_LIB_NAME} ${FSFW_ETL_LIB_MAJOR_VERSION} QUIET) # Not installed, so use FetchContent to download and provide etl if(NOT ${FSFW_ETL_LIB_NAME}_FOUND) - message(STATUS - "No ETL installation was found with find_package. Installing and providing " - "etl with FindPackage" + message(STATUS + "No ETL installation was found with find_package. Installing and providing " + "etl with FindPackage" ) - include(FetchContent) + include(FetchContent) - FetchContent_Declare( - ${FSFW_ETL_LIB_NAME} - GIT_REPOSITORY https://github.com/ETLCPP/etl - GIT_TAG ${FSFW_ETL_LIB_VERSION} + FetchContent_Declare( + ${FSFW_ETL_LIB_NAME} + GIT_REPOSITORY https://github.com/ETLCPP/etl + GIT_TAG ${FSFW_ETL_LIB_VERSION} + ) + + list(APPEND FSFW_FETCH_CONTENT_TARGETS ${FSFW_ETL_LIB_NAME}) +endif() + +message(STATUS "Finding and/or providing {fmt} formatting library") + +# Check whether the user has already installed ETL first +find_package(fmt ${FSFW_FMT_LIB_MAJOR_VERSION} QUIET) +# Not installed, so use FetchContent to download and provide etl +if(NOT ${FSFW_FMT_LIB_NAME}_FOUND) + message(STATUS + "No {fmt} installation was found with find_package. Installing and providing " + "{fmt} with FindPackage" ) + include(FetchContent) - list(APPEND FSFW_FETCH_CONTENT_TARGETS ${FSFW_ETL_LIB_NAME}) + FetchContent_Declare( + ${FSFW_FMT_LIB_NAME} + GIT_REPOSITORY https://github.com/fmtlib/fmt.git + GIT_TAG ${FSFW_FMT_LIB_VERSION} + ) + + list(APPEND FSFW_FETCH_CONTENT_TARGETS ${FSFW_FMT_LIB_NAME}) endif() # The documentation for FetchContent recommends declaring all the dependencies # before making them available. We make all declared dependency available here # after their declaration if(FSFW_FETCH_CONTENT_TARGETS) - FetchContent_MakeAvailable(${FSFW_FETCH_CONTENT_TARGETS}) - if(TARGET ${FSFW_ETL_LIB_NAME}) - add_library(${FSFW_ETL_LINK_TARGET} ALIAS ${FSFW_ETL_LIB_NAME}) - endif() - if(TARGET Catch2) - # Fixes regression -preview4, to be confirmed in later releases - # Related GitHub issue: https://github.com/catchorg/Catch2/issues/2417 - set_target_properties(Catch2 PROPERTIES DEBUG_POSTFIX "") - endif() + FetchContent_MakeAvailable(${FSFW_FETCH_CONTENT_TARGETS}) + if(TARGET ${FSFW_ETL_LIB_NAME}) + add_library(${FSFW_ETL_LINK_TARGET} ALIAS ${FSFW_ETL_LIB_NAME}) + endif() + if(TARGET Catch2) + # Fixes regression -preview4, to be confirmed in later releases + # Related GitHub issue: https://github.com/catchorg/Catch2/issues/2417 + set_target_properties(Catch2 PROPERTIES DEBUG_POSTFIX "") + endif() endif() set(FSFW_CORE_INC_PATH "inc") @@ -146,64 +173,64 @@ set_property(CACHE FSFW_OSAL PROPERTY STRINGS host linux rtems freertos) # For configure files target_include_directories(${LIB_FSFW_NAME} PRIVATE - ${CMAKE_CURRENT_BINARY_DIR} -) + ${CMAKE_CURRENT_BINARY_DIR} + ) target_include_directories(${LIB_FSFW_NAME} INTERFACE - ${CMAKE_CURRENT_BINARY_DIR} -) + ${CMAKE_CURRENT_BINARY_DIR} + ) if(NOT CMAKE_CXX_STANDARD) - set(CMAKE_CXX_STANDARD 11) - set(CMAKE_CXX_STANDARD_REQUIRED True) + set(CMAKE_CXX_STANDARD 11) + set(CMAKE_CXX_STANDARD_REQUIRED True) elseif(${CMAKE_CXX_STANDARD} LESS 11) - message(FATAL_ERROR "Compiling the FSFW requires a minimum of C++11 support") + message(FATAL_ERROR "Compiling the FSFW requires a minimum of C++11 support") endif() # Backwards comptability if(OS_FSFW AND NOT FSFW_OSAL) - message(WARNING "Please pass the FSFW OSAL as FSFW_OSAL instead of OS_FSFW") - set(FSFW_OSAL OS_FSFW) + message(WARNING "Please pass the FSFW OSAL as FSFW_OSAL instead of OS_FSFW") + set(FSFW_OSAL OS_FSFW) endif() if(NOT FSFW_OSAL) - message(STATUS "No OS for FSFW via FSFW_OSAL set. Assuming host OS") - # Assume host OS and autodetermine from OS_FSFW - if(UNIX) - set(FSFW_OSAL "linux" - CACHE STRING - "OS abstraction layer used in the FSFW" - ) - elseif(WIN32) - set(FSFW_OSAL "host" - CACHE STRING "OS abstraction layer used in the FSFW" - ) - endif() + message(STATUS "No OS for FSFW via FSFW_OSAL set. Assuming host OS") + # Assume host OS and autodetermine from OS_FSFW + if(UNIX) + set(FSFW_OSAL "linux" + CACHE STRING + "OS abstraction layer used in the FSFW" + ) + elseif(WIN32) + set(FSFW_OSAL "host" + CACHE STRING "OS abstraction layer used in the FSFW" + ) + endif() endif() set(FSFW_OSAL_DEFINITION FSFW_OSAL_HOST) if(FSFW_OSAL MATCHES host) - set(FSFW_OS_NAME "Host") - set(FSFW_OSAL_HOST ON) + set(FSFW_OS_NAME "Host") + set(FSFW_OSAL_HOST ON) elseif(FSFW_OSAL MATCHES linux) - set(FSFW_OS_NAME "Linux") - set(FSFW_OSAL_LINUX ON) + set(FSFW_OS_NAME "Linux") + set(FSFW_OSAL_LINUX ON) elseif(FSFW_OSAL MATCHES freertos) - set(FSFW_OS_NAME "FreeRTOS") - set(FSFW_OSAL_FREERTOS ON) - target_link_libraries(${LIB_FSFW_NAME} PRIVATE - ${LIB_OS_NAME} + set(FSFW_OS_NAME "FreeRTOS") + set(FSFW_OSAL_FREERTOS ON) + target_link_libraries(${LIB_FSFW_NAME} PRIVATE + ${LIB_OS_NAME} ) elseif(FSFW_OSAL STREQUAL rtems) - set(FSFW_OS_NAME "RTEMS") - set(FSFW_OSAL_RTEMS ON) + set(FSFW_OS_NAME "RTEMS") + set(FSFW_OSAL_RTEMS ON) else() - message(WARNING - "Invalid operating system for FSFW specified! Setting to host.." + message(WARNING + "Invalid operating system for FSFW specified! Setting to host.." ) - set(FSFW_OS_NAME "Host") - set(OS_FSFW "host") + set(FSFW_OS_NAME "Host") + set(OS_FSFW "host") endif() configure_file(src/fsfw/FSFW.h.in fsfw/FSFW.h) @@ -214,206 +241,212 @@ message(STATUS "Compiling FSFW for the ${FSFW_OS_NAME} operating system.") add_subdirectory(src) add_subdirectory(tests) if(FSFW_ADD_HAL) - add_subdirectory(hal) + add_subdirectory(hal) endif() add_subdirectory(contrib) if(FSFW_BUILD_DOCS) - add_subdirectory(docs) + add_subdirectory(docs) endif() if(FSFW_BUILD_UNITTESTS) - if(FSFW_TESTS_GEN_COV) - if(CMAKE_COMPILER_IS_GNUCXX) - include(CodeCoverage) + if(FSFW_TESTS_GEN_COV) + if(CMAKE_COMPILER_IS_GNUCXX) + include(CodeCoverage) - # Remove quotes. - separate_arguments(COVERAGE_COMPILER_FLAGS - NATIVE_COMMAND "${COVERAGE_COMPILER_FLAGS}" - ) + # Remove quotes. + separate_arguments(COVERAGE_COMPILER_FLAGS + NATIVE_COMMAND "${COVERAGE_COMPILER_FLAGS}" + ) - # Add compile options manually, we don't want coverage for Catch2 - target_compile_options(${FSFW_TEST_TGT} PRIVATE - "${COVERAGE_COMPILER_FLAGS}" - ) - target_compile_options(${LIB_FSFW_NAME} PRIVATE - "${COVERAGE_COMPILER_FLAGS}" - ) + # Add compile options manually, we don't want coverage for Catch2 + target_compile_options(${FSFW_TEST_TGT} PRIVATE + "${COVERAGE_COMPILER_FLAGS}" + ) + target_compile_options(${LIB_FSFW_NAME} PRIVATE + "${COVERAGE_COMPILER_FLAGS}" + ) - # Exclude directories here - if(WIN32) - set(GCOVR_ADDITIONAL_ARGS - "--exclude-throw-branches" - "--exclude-unreachable-branches" - ) - set(COVERAGE_EXCLUDES - "/c/msys64/mingw64/*" "*/fsfw_hal/*" - ) - elseif(UNIX) - set(COVERAGE_EXCLUDES - "/usr/include/*" "/usr/bin/*" "Catch2/*" - "/usr/local/include/*" "*/fsfw_tests/*" - "*/catch2-src/*" "*/fsfw_hal/*" - ) - endif() + # Exclude directories here + if(WIN32) + set(GCOVR_ADDITIONAL_ARGS + "--exclude-throw-branches" + "--exclude-unreachable-branches" + ) + set(COVERAGE_EXCLUDES + "/c/msys64/mingw64/*" "*/fsfw_hal/*" + ) + elseif(UNIX) + set(COVERAGE_EXCLUDES + "/usr/include/*" "/usr/bin/*" "Catch2/*" + "/usr/local/include/*" "*/fsfw_tests/*" + "*/catch2-src/*" "*/fsfw_hal/*" + ) + endif() - target_link_options(${FSFW_TEST_TGT} PRIVATE - -fprofile-arcs - -ftest-coverage - ) - target_link_options(${LIB_FSFW_NAME} PRIVATE - -fprofile-arcs - -ftest-coverage - ) - # Need to specify this as an interface, otherwise there will the compile issues - target_link_options(${LIB_FSFW_NAME} INTERFACE - -fprofile-arcs - -ftest-coverage - ) + target_link_options(${FSFW_TEST_TGT} PRIVATE + -fprofile-arcs + -ftest-coverage + ) + target_link_options(${LIB_FSFW_NAME} PRIVATE + -fprofile-arcs + -ftest-coverage + ) + # Need to specify this as an interface, otherwise there will the compile issues + target_link_options(${LIB_FSFW_NAME} INTERFACE + -fprofile-arcs + -ftest-coverage + ) - if(WIN32) - setup_target_for_coverage_gcovr_html( - NAME ${FSFW_TEST_TGT}_coverage - EXECUTABLE ${FSFW_TEST_TGT} - DEPENDENCIES ${FSFW_TEST_TGT} - ) - else() - setup_target_for_coverage_lcov( - NAME ${FSFW_TEST_TGT}_coverage - EXECUTABLE ${FSFW_TEST_TGT} - DEPENDENCIES ${FSFW_TEST_TGT} - ) - endif() - endif() + if(WIN32) + setup_target_for_coverage_gcovr_html( + NAME ${FSFW_TEST_TGT}_coverage + EXECUTABLE ${FSFW_TEST_TGT} + DEPENDENCIES ${FSFW_TEST_TGT} + ) + else() + setup_target_for_coverage_lcov( + NAME ${FSFW_TEST_TGT}_coverage + EXECUTABLE ${FSFW_TEST_TGT} + DEPENDENCIES ${FSFW_TEST_TGT} + ) + endif() endif() - target_link_libraries(${FSFW_TEST_TGT} PRIVATE Catch2::Catch2 ${LIB_FSFW_NAME}) + endif() + target_link_libraries(${FSFW_TEST_TGT} PRIVATE Catch2::Catch2 ${LIB_FSFW_NAME}) endif() # The project CMakeLists file has to set the FSFW_CONFIG_PATH and add it. # If this is not given, we include the default configuration and emit a warning. if(NOT FSFW_CONFIG_PATH) - set(DEF_CONF_PATH misc/defaultcfg/fsfwconfig) - if(NOT FSFW_BUILD_DOCS) - message(WARNING "Flight Software Framework configuration path not set!") - message(WARNING "Setting default configuration from ${DEF_CONF_PATH} ..") - endif() - add_subdirectory(${DEF_CONF_PATH}) - set(FSFW_CONFIG_PATH ${DEF_CONF_PATH}) + set(DEF_CONF_PATH misc/defaultcfg/fsfwconfig) + if(NOT FSFW_BUILD_DOCS) + message(WARNING "Flight Software Framework configuration path not set!") + message(WARNING "Setting default configuration from ${DEF_CONF_PATH} ..") + endif() + add_subdirectory(${DEF_CONF_PATH}) + set(FSFW_CONFIG_PATH ${DEF_CONF_PATH}) endif() -# FSFW might be part of a possibly complicated folder structure, so we +# FSFW might be part of a possibly complicated folder structure, so we # extract the absolute path of the fsfwconfig folder. if(IS_ABSOLUTE ${FSFW_CONFIG_PATH}) - set(FSFW_CONFIG_PATH_ABSOLUTE ${FSFW_CONFIG_PATH}) + set(FSFW_CONFIG_PATH_ABSOLUTE ${FSFW_CONFIG_PATH}) else() - get_filename_component(FSFW_CONFIG_PATH_ABSOLUTE - ${FSFW_CONFIG_PATH} REALPATH BASE_DIR ${CMAKE_SOURCE_DIR} - ) + get_filename_component(FSFW_CONFIG_PATH_ABSOLUTE + ${FSFW_CONFIG_PATH} REALPATH BASE_DIR ${CMAKE_SOURCE_DIR} + ) endif() foreach(INCLUDE_PATH ${FSFW_ADDITIONAL_INC_PATHS}) - if(IS_ABSOLUTE ${INCLUDE_PATH}) - set(CURR_ABS_INC_PATH "${INCLUDE_PATH}") - else() - get_filename_component(CURR_ABS_INC_PATH - ${INCLUDE_PATH} REALPATH BASE_DIR ${CMAKE_SOURCE_DIR}) - endif() + if(IS_ABSOLUTE ${INCLUDE_PATH}) + set(CURR_ABS_INC_PATH "${INCLUDE_PATH}") + else() + get_filename_component(CURR_ABS_INC_PATH + ${INCLUDE_PATH} REALPATH BASE_DIR ${CMAKE_SOURCE_DIR}) + endif() - if(CMAKE_VERBOSE) - message(STATUS "FSFW include path: ${CURR_ABS_INC_PATH}") - endif() + if(CMAKE_VERBOSE) + message(STATUS "FSFW include path: ${CURR_ABS_INC_PATH}") + endif() - list(APPEND FSFW_ADD_INC_PATHS_ABS ${CURR_ABS_INC_PATH}) + list(APPEND FSFW_ADD_INC_PATHS_ABS ${CURR_ABS_INC_PATH}) endforeach() if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") - if(NOT DEFINED FSFW_WARNING_FLAGS) - set(FSFW_WARNING_FLAGS - -Wall - -Wextra - -Wimplicit-fallthrough=1 - -Wno-unused-parameter - -Wno-psabi - -Wduplicated-cond # check for duplicate conditions - -Wduplicated-branches # check for duplicate branches - -Wlogical-op # Search for bitwise operations instead of logical - -Wnull-dereference # Search for NULL dereference - -Wundef # Warn if undefind marcos are used - -Wformat=2 # Format string problem detection - -Wformat-overflow=2 # Formatting issues in printf - -Wformat-truncation=2 # Formatting issues in printf - -Wformat-security # Search for dangerous printf operations - -Wstrict-overflow=3 # Warn if integer overflows might happen - -Warray-bounds=2 # Some array bounds violations will be found - -Wshift-overflow=2 # Search for bit left shift overflows ( sif::PRINT_BUF = {}; +MutexIF* sif::PRINT_MUTEX = nullptr; + +const char* sif::PREFIX_ARR[4]= {DEBUG_PREFIX, INFO_PREFIX, WARNING_PREFIX, ERROR_PREFIX}; + +ReturnValue_t sif::initialize() { + sif::PRINT_MUTEX = MutexFactory::instance()->createMutex(); + if(sif::PRINT_MUTEX == nullptr) { + return HasReturnvaluesIF::RETURN_FAILED; + } + return HasReturnvaluesIF::RETURN_OK; +} + +size_t sif::writeTypePrefix(LogLevel level) { + auto idx = static_cast(level); + const auto result = + fmt::format_to_n(PRINT_BUF.begin(), PRINT_BUF.size() - 1, + fmt::runtime(fmt::format(fg(LOG_COLOR_ARR[idx]), PREFIX_ARR[idx]))); + return result.size; +} \ No newline at end of file diff --git a/src/fsfw/serviceinterface/fmtWrapper.h b/src/fsfw/serviceinterface/fmtWrapper.h new file mode 100644 index 000000000..9b957512f --- /dev/null +++ b/src/fsfw/serviceinterface/fmtWrapper.h @@ -0,0 +1,164 @@ +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include "fsfw/ipc/MutexGuard.h" +#include "fsfw/ipc/MutexIF.h" +#include "fsfw/timemanager/Clock.h" + +// Takes from stackoverflow to display relative paths: +// https://stackoverflow.com/questions/8487986/file-macro-shows-full-path +#ifdef FSFW_SOURCE_PATH_SIZE +#define __FILENAME_REL__ (((const char*)__FILE__ + SOURCE_PATH_SIZE)) +#endif + +#define __FILENAME__ (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__) + +namespace sif { + +extern std::array PRINT_BUF; +extern MutexIF* PRINT_MUTEX; + +static const char INFO_PREFIX[] = "INFO"; +static const char DEBUG_PREFIX[] = "DEBUG"; +static const char WARNING_PREFIX[] = "WARNING"; +static const char ERROR_PREFIX[] = "ERROR"; + +enum class LogLevel : unsigned int { DEBUG = 0, INFO = 1, WARNING = 2, ERROR = 3 }; + +extern const char* PREFIX_ARR[4]; + +static const std::array LOG_COLOR_ARR = { + fmt::color::deep_sky_blue, fmt::color::forest_green, fmt::color::orange_red, fmt::color::red}; + +ReturnValue_t initialize(); + +size_t writeTypePrefix(LogLevel level); + +template +size_t logTraced(LogLevel level, const char* file, unsigned int line, bool timed, + fmt::format_string fmt, T&&... args) noexcept { + if(PRINT_MUTEX == nullptr) { + fmt::print("Please call sif::initialize at program startup\n"); + return 0; + } + try { + MutexGuard mg(PRINT_MUTEX); + size_t bufPos = writeTypePrefix(level); + auto currentIter = PRINT_BUF.begin() + bufPos; + if (timed) { + Clock::TimeOfDay_t logTime; + Clock::getDateAndTime(&logTime); + const auto result = fmt::format_to_n(currentIter, PRINT_BUF.size() - 1 - bufPos, + " | {}[l.{}] | {:02}:{:02}:{:02}.{:03} | {}", file, line, + logTime.hour, logTime.minute, logTime.second, + logTime.usecond / 1000, fmt::format(fmt, args...)); + bufPos += result.size; + } else { + const auto result = fmt::format_to_n(currentIter, PRINT_BUF.size() - 1 - bufPos, + " | {}[l.{}] | {}", file, line, fmt::format(fmt, args...)); + bufPos += result.size; + } + PRINT_BUF[bufPos] = '\0'; + fmt::print(fmt::runtime(PRINT_BUF.data())); + return bufPos; + } catch (const fmt::v8::format_error& e) { + fmt::print("Printing failed with error: {}\n", e.what()); + return 0; + } +} + +template +size_t log(LogLevel level, bool timed, fmt::format_string fmt, T&&... args) noexcept { + if(PRINT_MUTEX == nullptr) { + fmt::print("Please call sif::initialize at program startup\n"); + return 0; + } + try { + MutexGuard mg(PRINT_MUTEX); + size_t bufPos = writeTypePrefix(level); + auto currentIter = PRINT_BUF.begin() + bufPos; + if (timed) { + Clock::TimeOfDay_t logTime; + Clock::getDateAndTime(&logTime); + const auto result = fmt::format_to_n( + currentIter, PRINT_BUF.size() - bufPos, " | {:02}:{:02}:{:02}.{:03} | {}", logTime.hour, + logTime.minute, logTime.second, logTime.usecond / 1000, fmt::format(fmt, args...)); + bufPos += result.size; + } else { + const auto result = fmt::format_to_n( + currentIter, PRINT_BUF.size() - bufPos, " | {}", fmt::format(fmt, args...)); + bufPos += result.size; + } + PRINT_BUF[bufPos] = '\0'; + fmt::print(fmt::runtime(PRINT_BUF.data())); + return bufPos; + } catch (const fmt::v8::format_error& e) { + fmt::print("Printing failed with error: {}\n", e.what()); + return 0; + } +} + +template +void fdebug(const char* file, unsigned int line, fmt::format_string fmt, T&&... args) noexcept { + logTraced(LogLevel::DEBUG, file, line, false, fmt, args...); +} + +template +void fdebug_t(const char* file, unsigned int line, fmt::format_string fmt, T&&... args) noexcept { + logTraced(LogLevel::DEBUG, file, line, true, fmt, args...); +} + +template +void finfo_t(fmt::format_string fmt, T&&... args) { + log(LogLevel::INFO, true, fmt, args...); +} + +template +void finfo(fmt::format_string fmt, T&&... args) { + log(LogLevel::INFO, false, fmt, args...); +} + +template +void fwarning(const char* file, unsigned int line, fmt::format_string fmt, T&&... args) { + logTraced(LogLevel::WARNING, file, line, false, fmt, args...); +} + +template +void fwarning_t(const char* file, unsigned int line, fmt::format_string fmt, T&&... args) { + logTraced(LogLevel::WARNING, file, line, true, fmt, args...); +} + +template +void ferror(const char* file, unsigned int line, fmt::format_string fmt, T&&... args) { + logTraced(LogLevel::ERROR, file, line, false, fmt, args...); +} + +template +void ferror_t(const char* file, unsigned int line, fmt::format_string fmt, T&&... args) { + logTraced(LogLevel::ERROR, file, line, true, fmt, args...); +} + +} // namespace sif + +#define FSFW_LOGI(format, ...) finfo(FMT_STRING(format), __VA_ARGS__) + +#define FSFW_LOGIT(format, ...) finfo_t(FMT_STRING(format), __VA_ARGS__) + +#define FSFW_LOGD(format, ...) sif::fdebug(__FILENAME__, __LINE__, FMT_STRING(format), __VA_ARGS__) + +#define FSFW_LOGDT(format, ...) fdebug_t(__FILENAME__, __LINE__, FMT_STRING(format), __VA_ARGS__) + +#define FSFW_LOGW(format, ...) fdebug(__FILENAME__, __LINE__, FMT_STRING(format), __VA_ARGS__) + +#define FSFW_LOGWT(format, ...) fdebug_t(__FILENAME__, __LINE__, FMT_STRING(format), __VA_ARGS__) + +#define FSFW_LOGE(format, ...) fdebug(__FILENAME__, __LINE__, FMT_STRING(format), __VA_ARGS__) + +#define FSFW_LOGET(format, ...) fdebug_t(__FILENAME__, __LINE__, FMT_STRING(format), __VA_ARGS__)